OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/guest_view/web_view/web_view_guest.h" | |
6 | |
7 #include "base/message_loop/message_loop.h" | |
8 #include "base/strings/stringprintf.h" | |
9 #include "base/strings/utf_string_conversions.h" | |
10 #include "chrome/browser/extensions/api/web_request/web_request_api.h" | |
11 #include "chrome/browser/extensions/api/web_view/web_view_internal_api.h" | |
12 #include "chrome/browser/guest_view/web_view/chrome_web_view_guest_delegate.h" | |
13 #include "chrome/browser/guest_view/web_view/web_view_constants.h" | |
14 #include "chrome/browser/guest_view/web_view/web_view_permission_helper.h" | |
15 #include "chrome/browser/guest_view/web_view/web_view_permission_types.h" | |
16 #include "chrome/browser/guest_view/web_view/web_view_renderer_state.h" | |
17 #include "content/public/browser/browser_thread.h" | |
18 #include "content/public/browser/child_process_security_policy.h" | |
19 #include "content/public/browser/native_web_keyboard_event.h" | |
20 #include "content/public/browser/navigation_entry.h" | |
21 #include "content/public/browser/notification_details.h" | |
22 #include "content/public/browser/notification_service.h" | |
23 #include "content/public/browser/notification_source.h" | |
24 #include "content/public/browser/notification_types.h" | |
25 #include "content/public/browser/render_process_host.h" | |
26 #include "content/public/browser/render_view_host.h" | |
27 #include "content/public/browser/resource_request_details.h" | |
28 #include "content/public/browser/site_instance.h" | |
29 #include "content/public/browser/storage_partition.h" | |
30 #include "content/public/browser/user_metrics.h" | |
31 #include "content/public/browser/web_contents.h" | |
32 #include "content/public/browser/web_contents_delegate.h" | |
33 #include "content/public/common/media_stream_request.h" | |
34 #include "content/public/common/page_zoom.h" | |
35 #include "content/public/common/result_codes.h" | |
36 #include "content/public/common/stop_find_action.h" | |
37 #include "content/public/common/url_constants.h" | |
38 #include "extensions/browser/extension_system.h" | |
39 #include "extensions/browser/guest_view/guest_view_constants.h" | |
40 #include "extensions/browser/guest_view/guest_view_manager.h" | |
41 #include "extensions/common/constants.h" | |
42 #include "extensions/common/extension_messages.h" | |
43 #include "ipc/ipc_message_macros.h" | |
44 #include "net/base/escape.h" | |
45 #include "net/base/net_errors.h" | |
46 #include "third_party/WebKit/public/web/WebFindOptions.h" | |
47 #include "ui/base/models/simple_menu_model.h" | |
48 | |
49 using base::UserMetricsAction; | |
50 using content::RenderFrameHost; | |
51 using content::ResourceType; | |
52 using content::WebContents; | |
53 | |
54 namespace extensions { | |
55 | |
56 namespace { | |
57 | |
58 std::string WindowOpenDispositionToString( | |
59 WindowOpenDisposition window_open_disposition) { | |
60 switch (window_open_disposition) { | |
61 case IGNORE_ACTION: | |
62 return "ignore"; | |
63 case SAVE_TO_DISK: | |
64 return "save_to_disk"; | |
65 case CURRENT_TAB: | |
66 return "current_tab"; | |
67 case NEW_BACKGROUND_TAB: | |
68 return "new_background_tab"; | |
69 case NEW_FOREGROUND_TAB: | |
70 return "new_foreground_tab"; | |
71 case NEW_WINDOW: | |
72 return "new_window"; | |
73 case NEW_POPUP: | |
74 return "new_popup"; | |
75 default: | |
76 NOTREACHED() << "Unknown Window Open Disposition"; | |
77 return "ignore"; | |
78 } | |
79 } | |
80 | |
81 static std::string TerminationStatusToString(base::TerminationStatus status) { | |
82 switch (status) { | |
83 case base::TERMINATION_STATUS_NORMAL_TERMINATION: | |
84 return "normal"; | |
85 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: | |
86 case base::TERMINATION_STATUS_STILL_RUNNING: | |
87 return "abnormal"; | |
88 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: | |
89 return "killed"; | |
90 case base::TERMINATION_STATUS_PROCESS_CRASHED: | |
91 return "crashed"; | |
92 case base::TERMINATION_STATUS_MAX_ENUM: | |
93 break; | |
94 } | |
95 NOTREACHED() << "Unknown Termination Status."; | |
96 return "unknown"; | |
97 } | |
98 | |
99 std::string GetStoragePartitionIdFromSiteURL(const GURL& site_url) { | |
100 const std::string& partition_id = site_url.query(); | |
101 bool persist_storage = site_url.path().find("persist") != std::string::npos; | |
102 return (persist_storage ? webview::kPersistPrefix : "") + partition_id; | |
103 } | |
104 | |
105 void RemoveWebViewEventListenersOnIOThread( | |
106 void* profile, | |
107 const std::string& extension_id, | |
108 int embedder_process_id, | |
109 int view_instance_id) { | |
110 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
111 ExtensionWebRequestEventRouter::GetInstance()->RemoveWebViewEventListeners( | |
112 profile, | |
113 extension_id, | |
114 embedder_process_id, | |
115 view_instance_id); | |
116 } | |
117 | |
118 void ParsePartitionParam(const base::DictionaryValue& create_params, | |
119 std::string* storage_partition_id, | |
120 bool* persist_storage) { | |
121 std::string partition_str; | |
122 if (!create_params.GetString(webview::kStoragePartitionId, &partition_str)) { | |
123 return; | |
124 } | |
125 | |
126 // Since the "persist:" prefix is in ASCII, StartsWith will work fine on | |
127 // UTF-8 encoded |partition_id|. If the prefix is a match, we can safely | |
128 // remove the prefix without splicing in the middle of a multi-byte codepoint. | |
129 // We can use the rest of the string as UTF-8 encoded one. | |
130 if (StartsWithASCII(partition_str, "persist:", true)) { | |
131 size_t index = partition_str.find(":"); | |
132 CHECK(index != std::string::npos); | |
133 // It is safe to do index + 1, since we tested for the full prefix above. | |
134 *storage_partition_id = partition_str.substr(index + 1); | |
135 | |
136 if (storage_partition_id->empty()) { | |
137 // TODO(lazyboy): Better way to deal with this error. | |
138 return; | |
139 } | |
140 *persist_storage = true; | |
141 } else { | |
142 *storage_partition_id = partition_str; | |
143 *persist_storage = false; | |
144 } | |
145 } | |
146 | |
147 } // namespace | |
148 | |
149 // static | |
150 GuestViewBase* WebViewGuest::Create(content::BrowserContext* browser_context, | |
151 int guest_instance_id) { | |
152 return new WebViewGuest(browser_context, guest_instance_id); | |
153 } | |
154 | |
155 // static | |
156 bool WebViewGuest::GetGuestPartitionConfigForSite( | |
157 const GURL& site, | |
158 std::string* partition_domain, | |
159 std::string* partition_name, | |
160 bool* in_memory) { | |
161 if (!site.SchemeIs(content::kGuestScheme)) | |
162 return false; | |
163 | |
164 // Since guest URLs are only used for packaged apps, there must be an app | |
165 // id in the URL. | |
166 CHECK(site.has_host()); | |
167 *partition_domain = site.host(); | |
168 // Since persistence is optional, the path must either be empty or the | |
169 // literal string. | |
170 *in_memory = (site.path() != "/persist"); | |
171 // The partition name is user supplied value, which we have encoded when the | |
172 // URL was created, so it needs to be decoded. | |
173 *partition_name = | |
174 net::UnescapeURLComponent(site.query(), net::UnescapeRule::NORMAL); | |
175 return true; | |
176 } | |
177 | |
178 // static | |
179 const char WebViewGuest::Type[] = "webview"; | |
180 | |
181 // static | |
182 int WebViewGuest::GetViewInstanceId(WebContents* contents) { | |
183 WebViewGuest* guest = FromWebContents(contents); | |
184 if (!guest) | |
185 return guestview::kInstanceIDNone; | |
186 | |
187 return guest->view_instance_id(); | |
188 } | |
189 | |
190 const char* WebViewGuest::GetAPINamespace() { | |
191 return webview::kAPINamespace; | |
192 } | |
193 | |
194 void WebViewGuest::CreateWebContents( | |
195 const std::string& embedder_extension_id, | |
196 int embedder_render_process_id, | |
197 const base::DictionaryValue& create_params, | |
198 const WebContentsCreatedCallback& callback) { | |
199 content::RenderProcessHost* embedder_render_process_host = | |
200 content::RenderProcessHost::FromID(embedder_render_process_id); | |
201 std::string storage_partition_id; | |
202 bool persist_storage = false; | |
203 std::string storage_partition_string; | |
204 ParsePartitionParam(create_params, &storage_partition_id, &persist_storage); | |
205 // Validate that the partition id coming from the renderer is valid UTF-8, | |
206 // since we depend on this in other parts of the code, such as FilePath | |
207 // creation. If the validation fails, treat it as a bad message and kill the | |
208 // renderer process. | |
209 if (!base::IsStringUTF8(storage_partition_id)) { | |
210 content::RecordAction( | |
211 base::UserMetricsAction("BadMessageTerminate_BPGM")); | |
212 base::KillProcess( | |
213 embedder_render_process_host->GetHandle(), | |
214 content::RESULT_CODE_KILLED_BAD_MESSAGE, false); | |
215 callback.Run(NULL); | |
216 return; | |
217 } | |
218 std::string url_encoded_partition = net::EscapeQueryParamValue( | |
219 storage_partition_id, false); | |
220 // The SiteInstance of a given webview tag is based on the fact that it's | |
221 // a guest process in addition to which platform application the tag | |
222 // belongs to and what storage partition is in use, rather than the URL | |
223 // that the tag is being navigated to. | |
224 GURL guest_site(base::StringPrintf("%s://%s/%s?%s", | |
225 content::kGuestScheme, | |
226 embedder_extension_id.c_str(), | |
227 persist_storage ? "persist" : "", | |
228 url_encoded_partition.c_str())); | |
229 | |
230 // If we already have a webview tag in the same app using the same storage | |
231 // partition, we should use the same SiteInstance so the existing tag and | |
232 // the new tag can script each other. | |
233 GuestViewManager* guest_view_manager = | |
234 GuestViewManager::FromBrowserContext( | |
235 embedder_render_process_host->GetBrowserContext()); | |
236 content::SiteInstance* guest_site_instance = | |
237 guest_view_manager->GetGuestSiteInstance(guest_site); | |
238 if (!guest_site_instance) { | |
239 // Create the SiteInstance in a new BrowsingInstance, which will ensure | |
240 // that webview tags are also not allowed to send messages across | |
241 // different partitions. | |
242 guest_site_instance = content::SiteInstance::CreateForURL( | |
243 embedder_render_process_host->GetBrowserContext(), guest_site); | |
244 } | |
245 WebContents::CreateParams params( | |
246 embedder_render_process_host->GetBrowserContext(), | |
247 guest_site_instance); | |
248 params.guest_delegate = this; | |
249 callback.Run(WebContents::Create(params)); | |
250 } | |
251 | |
252 void WebViewGuest::DidAttachToEmbedder() { | |
253 SetUpAutoSize(); | |
254 | |
255 std::string name; | |
256 if (attach_params()->GetString(webview::kName, &name)) { | |
257 // If the guest window's name is empty, then the WebView tag's name is | |
258 // assigned. Otherwise, the guest window's name takes precedence over the | |
259 // WebView tag's name. | |
260 if (name_.empty()) | |
261 name_ = name; | |
262 } | |
263 ReportFrameNameChange(name_); | |
264 | |
265 std::string user_agent_override; | |
266 if (attach_params()->GetString(webview::kParameterUserAgentOverride, | |
267 &user_agent_override)) { | |
268 SetUserAgentOverride(user_agent_override); | |
269 } else { | |
270 SetUserAgentOverride(""); | |
271 } | |
272 | |
273 std::string src; | |
274 if (attach_params()->GetString("src", &src) && !src.empty()) | |
275 NavigateGuest(src); | |
276 | |
277 if (GetOpener()) { | |
278 // We need to do a navigation here if the target URL has changed between | |
279 // the time the WebContents was created and the time it was attached. | |
280 // We also need to do an initial navigation if a RenderView was never | |
281 // created for the new window in cases where there is no referrer. | |
282 PendingWindowMap::iterator it = | |
283 GetOpener()->pending_new_windows_.find(this); | |
284 if (it != GetOpener()->pending_new_windows_.end()) { | |
285 const NewWindowInfo& new_window_info = it->second; | |
286 if (new_window_info.changed || !guest_web_contents()->HasOpener()) | |
287 NavigateGuest(new_window_info.url.spec()); | |
288 } else { | |
289 NOTREACHED(); | |
290 } | |
291 | |
292 // Once a new guest is attached to the DOM of the embedder page, then the | |
293 // lifetime of the new guest is no longer managed by the opener guest. | |
294 GetOpener()->pending_new_windows_.erase(this); | |
295 } | |
296 } | |
297 | |
298 void WebViewGuest::DidInitialize() { | |
299 script_executor_.reset( | |
300 new ScriptExecutor(guest_web_contents(), &script_observers_)); | |
301 | |
302 notification_registrar_.Add( | |
303 this, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME, | |
304 content::Source<WebContents>(guest_web_contents())); | |
305 | |
306 notification_registrar_.Add( | |
307 this, content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT, | |
308 content::Source<WebContents>(guest_web_contents())); | |
309 | |
310 if (web_view_guest_delegate_) | |
311 web_view_guest_delegate_->OnDidInitialize(); | |
312 AttachWebViewHelpers(guest_web_contents()); | |
313 } | |
314 | |
315 void WebViewGuest::AttachWebViewHelpers(WebContents* contents) { | |
316 if (web_view_guest_delegate_) | |
317 web_view_guest_delegate_->OnAttachWebViewHelpers(contents); | |
318 web_view_permission_helper_.reset(new WebViewPermissionHelper(this)); | |
319 } | |
320 | |
321 void WebViewGuest::DidStopLoading() { | |
322 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
323 DispatchEventToEmbedder( | |
324 new GuestViewBase::Event(webview::kEventLoadStop, args.Pass())); | |
325 } | |
326 | |
327 void WebViewGuest::EmbedderDestroyed() { | |
328 // TODO(fsamuel): WebRequest event listeners for <webview> should survive | |
329 // reparenting of a <webview> within a single embedder. Right now, we keep | |
330 // around the browser state for the listener for the lifetime of the embedder. | |
331 // Ideally, the lifetime of the listeners should match the lifetime of the | |
332 // <webview> DOM node. Once http://crbug.com/156219 is resolved we can move | |
333 // the call to RemoveWebViewEventListenersOnIOThread back to | |
334 // WebViewGuest::WebContentsDestroyed. | |
335 content::BrowserThread::PostTask( | |
336 content::BrowserThread::IO, | |
337 FROM_HERE, | |
338 base::Bind( | |
339 &RemoveWebViewEventListenersOnIOThread, | |
340 browser_context(), embedder_extension_id(), | |
341 embedder_render_process_id(), | |
342 view_instance_id())); | |
343 } | |
344 | |
345 void WebViewGuest::GuestDestroyed() { | |
346 // Clean up custom context menu items for this guest. | |
347 if (web_view_guest_delegate_) | |
348 web_view_guest_delegate_->OnGuestDestroyed(); | |
349 RemoveWebViewStateFromIOThread(web_contents()); | |
350 } | |
351 | |
352 void WebViewGuest::GuestReady() { | |
353 // The guest RenderView should always live in an isolated guest process. | |
354 CHECK(guest_web_contents()->GetRenderProcessHost()->IsIsolatedGuest()); | |
355 Send(new ExtensionMsg_SetFrameName( | |
356 guest_web_contents()->GetRoutingID(), name_)); | |
357 } | |
358 | |
359 void WebViewGuest::GuestSizeChangedDueToAutoSize(const gfx::Size& old_size, | |
360 const gfx::Size& new_size) { | |
361 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
362 args->SetInteger(webview::kOldHeight, old_size.height()); | |
363 args->SetInteger(webview::kOldWidth, old_size.width()); | |
364 args->SetInteger(webview::kNewHeight, new_size.height()); | |
365 args->SetInteger(webview::kNewWidth, new_size.width()); | |
366 DispatchEventToEmbedder( | |
367 new GuestViewBase::Event(webview::kEventSizeChanged, args.Pass())); | |
368 } | |
369 | |
370 bool WebViewGuest::IsAutoSizeSupported() const { | |
371 return true; | |
372 } | |
373 | |
374 bool WebViewGuest::IsDragAndDropEnabled() const { | |
375 return true; | |
376 } | |
377 | |
378 void WebViewGuest::WillDestroy() { | |
379 if (!attached() && GetOpener()) | |
380 GetOpener()->pending_new_windows_.erase(this); | |
381 DestroyUnattachedWindows(); | |
382 } | |
383 | |
384 bool WebViewGuest::AddMessageToConsole(WebContents* source, | |
385 int32 level, | |
386 const base::string16& message, | |
387 int32 line_no, | |
388 const base::string16& source_id) { | |
389 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
390 // Log levels are from base/logging.h: LogSeverity. | |
391 args->SetInteger(webview::kLevel, level); | |
392 args->SetString(webview::kMessage, message); | |
393 args->SetInteger(webview::kLine, line_no); | |
394 args->SetString(webview::kSourceId, source_id); | |
395 DispatchEventToEmbedder( | |
396 new GuestViewBase::Event(webview::kEventConsoleMessage, args.Pass())); | |
397 return true; | |
398 } | |
399 | |
400 void WebViewGuest::CloseContents(WebContents* source) { | |
401 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
402 DispatchEventToEmbedder( | |
403 new GuestViewBase::Event(webview::kEventClose, args.Pass())); | |
404 } | |
405 | |
406 void WebViewGuest::FindReply(WebContents* source, | |
407 int request_id, | |
408 int number_of_matches, | |
409 const gfx::Rect& selection_rect, | |
410 int active_match_ordinal, | |
411 bool final_update) { | |
412 find_helper_.FindReply(request_id, number_of_matches, selection_rect, | |
413 active_match_ordinal, final_update); | |
414 } | |
415 | |
416 bool WebViewGuest::HandleContextMenu( | |
417 const content::ContextMenuParams& params) { | |
418 if (!web_view_guest_delegate_) | |
419 return false; | |
420 return web_view_guest_delegate_->HandleContextMenu(params); | |
421 } | |
422 | |
423 void WebViewGuest::HandleKeyboardEvent( | |
424 WebContents* source, | |
425 const content::NativeWebKeyboardEvent& event) { | |
426 if (!attached()) | |
427 return; | |
428 | |
429 if (HandleKeyboardShortcuts(event)) | |
430 return; | |
431 | |
432 // Send the unhandled keyboard events back to the embedder to reprocess them. | |
433 // TODO(fsamuel): This introduces the possibility of out-of-order keyboard | |
434 // events because the guest may be arbitrarily delayed when responding to | |
435 // keyboard events. In that time, the embedder may have received and processed | |
436 // additional key events. This needs to be fixed as soon as possible. | |
437 // See http://crbug.com/229882. | |
438 embedder_web_contents()->GetDelegate()->HandleKeyboardEvent( | |
439 web_contents(), event); | |
440 } | |
441 | |
442 void WebViewGuest::LoadProgressChanged(content::WebContents* source, | |
443 double progress) { | |
444 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
445 args->SetString(guestview::kUrl, guest_web_contents()->GetURL().spec()); | |
446 args->SetDouble(webview::kProgress, progress); | |
447 DispatchEventToEmbedder( | |
448 new GuestViewBase::Event(webview::kEventLoadProgress, args.Pass())); | |
449 } | |
450 | |
451 void WebViewGuest::LoadAbort(bool is_top_level, | |
452 const GURL& url, | |
453 const std::string& error_type) { | |
454 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
455 args->SetBoolean(guestview::kIsTopLevel, is_top_level); | |
456 args->SetString(guestview::kUrl, url.possibly_invalid_spec()); | |
457 args->SetString(guestview::kReason, error_type); | |
458 DispatchEventToEmbedder( | |
459 new GuestViewBase::Event(webview::kEventLoadAbort, args.Pass())); | |
460 } | |
461 | |
462 void WebViewGuest::OnFrameNameChanged(bool is_top_level, | |
463 const std::string& name) { | |
464 if (!is_top_level) | |
465 return; | |
466 | |
467 if (name_ == name) | |
468 return; | |
469 | |
470 ReportFrameNameChange(name); | |
471 } | |
472 | |
473 void WebViewGuest::CreateNewGuestWebViewWindow( | |
474 const content::OpenURLParams& params) { | |
475 GuestViewManager* guest_manager = | |
476 GuestViewManager::FromBrowserContext(browser_context()); | |
477 // Set the attach params to use the same partition as the opener. | |
478 // We pull the partition information from the site's URL, which is of the | |
479 // form guest://site/{persist}?{partition_name}. | |
480 const GURL& site_url = guest_web_contents()->GetSiteInstance()->GetSiteURL(); | |
481 const std::string storage_partition_id = | |
482 GetStoragePartitionIdFromSiteURL(site_url); | |
483 base::DictionaryValue create_params; | |
484 create_params.SetString(webview::kStoragePartitionId, storage_partition_id); | |
485 | |
486 guest_manager->CreateGuest(WebViewGuest::Type, | |
487 embedder_extension_id(), | |
488 embedder_web_contents(), | |
489 create_params, | |
490 base::Bind(&WebViewGuest::NewGuestWebViewCallback, | |
491 base::Unretained(this), | |
492 params)); | |
493 } | |
494 | |
495 void WebViewGuest::NewGuestWebViewCallback( | |
496 const content::OpenURLParams& params, | |
497 content::WebContents* guest_web_contents) { | |
498 WebViewGuest* new_guest = WebViewGuest::FromWebContents(guest_web_contents); | |
499 new_guest->SetOpener(this); | |
500 | |
501 // Take ownership of |new_guest|. | |
502 pending_new_windows_.insert( | |
503 std::make_pair(new_guest, NewWindowInfo(params.url, std::string()))); | |
504 | |
505 // Request permission to show the new window. | |
506 RequestNewWindowPermission(params.disposition, gfx::Rect(), | |
507 params.user_gesture, | |
508 new_guest->guest_web_contents()); | |
509 } | |
510 | |
511 // TODO(fsamuel): Find a reliable way to test the 'responsive' and | |
512 // 'unresponsive' events. | |
513 void WebViewGuest::RendererResponsive(content::WebContents* source) { | |
514 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
515 args->SetInteger(webview::kProcessId, | |
516 guest_web_contents()->GetRenderProcessHost()->GetID()); | |
517 DispatchEventToEmbedder( | |
518 new GuestViewBase::Event(webview::kEventResponsive, args.Pass())); | |
519 } | |
520 | |
521 void WebViewGuest::RendererUnresponsive(content::WebContents* source) { | |
522 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
523 args->SetInteger(webview::kProcessId, | |
524 guest_web_contents()->GetRenderProcessHost()->GetID()); | |
525 DispatchEventToEmbedder( | |
526 new GuestViewBase::Event(webview::kEventUnresponsive, args.Pass())); | |
527 } | |
528 | |
529 void WebViewGuest::Observe(int type, | |
530 const content::NotificationSource& source, | |
531 const content::NotificationDetails& details) { | |
532 switch (type) { | |
533 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME: { | |
534 DCHECK_EQ(content::Source<WebContents>(source).ptr(), | |
535 guest_web_contents()); | |
536 if (content::Source<WebContents>(source).ptr() == guest_web_contents()) | |
537 LoadHandlerCalled(); | |
538 break; | |
539 } | |
540 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: { | |
541 DCHECK_EQ(content::Source<WebContents>(source).ptr(), | |
542 guest_web_contents()); | |
543 content::ResourceRedirectDetails* resource_redirect_details = | |
544 content::Details<content::ResourceRedirectDetails>(details).ptr(); | |
545 bool is_top_level = resource_redirect_details->resource_type == | |
546 content::RESOURCE_TYPE_MAIN_FRAME; | |
547 LoadRedirect(resource_redirect_details->url, | |
548 resource_redirect_details->new_url, | |
549 is_top_level); | |
550 break; | |
551 } | |
552 default: | |
553 NOTREACHED() << "Unexpected notification sent."; | |
554 break; | |
555 } | |
556 } | |
557 | |
558 double WebViewGuest::GetZoom() { | |
559 if (!web_view_guest_delegate_) | |
560 return 1.0; | |
561 return web_view_guest_delegate_->GetZoom(); | |
562 } | |
563 | |
564 void WebViewGuest::Find( | |
565 const base::string16& search_text, | |
566 const blink::WebFindOptions& options, | |
567 scoped_refptr<WebViewInternalFindFunction> find_function) { | |
568 find_helper_.Find(guest_web_contents(), search_text, options, find_function); | |
569 } | |
570 | |
571 void WebViewGuest::StopFinding(content::StopFindAction action) { | |
572 find_helper_.CancelAllFindSessions(); | |
573 guest_web_contents()->StopFinding(action); | |
574 } | |
575 | |
576 void WebViewGuest::Go(int relative_index) { | |
577 guest_web_contents()->GetController().GoToOffset(relative_index); | |
578 } | |
579 | |
580 void WebViewGuest::Reload() { | |
581 // TODO(fsamuel): Don't check for repost because we don't want to show | |
582 // Chromium's repost warning. We might want to implement a separate API | |
583 // for registering a callback if a repost is about to happen. | |
584 guest_web_contents()->GetController().Reload(false); | |
585 } | |
586 | |
587 void WebViewGuest::SetUserAgentOverride( | |
588 const std::string& user_agent_override) { | |
589 if (!attached()) | |
590 return; | |
591 is_overriding_user_agent_ = !user_agent_override.empty(); | |
592 if (is_overriding_user_agent_) { | |
593 content::RecordAction(UserMetricsAction("WebView.Guest.OverrideUA")); | |
594 } | |
595 guest_web_contents()->SetUserAgentOverride(user_agent_override); | |
596 } | |
597 | |
598 void WebViewGuest::Stop() { | |
599 guest_web_contents()->Stop(); | |
600 } | |
601 | |
602 void WebViewGuest::Terminate() { | |
603 content::RecordAction(UserMetricsAction("WebView.Guest.Terminate")); | |
604 base::ProcessHandle process_handle = | |
605 guest_web_contents()->GetRenderProcessHost()->GetHandle(); | |
606 if (process_handle) | |
607 base::KillProcess(process_handle, content::RESULT_CODE_KILLED, false); | |
608 } | |
609 | |
610 bool WebViewGuest::ClearData(const base::Time remove_since, | |
611 uint32 removal_mask, | |
612 const base::Closure& callback) { | |
613 content::RecordAction(UserMetricsAction("WebView.Guest.ClearData")); | |
614 content::StoragePartition* partition = | |
615 content::BrowserContext::GetStoragePartition( | |
616 guest_web_contents()->GetBrowserContext(), | |
617 guest_web_contents()->GetSiteInstance()); | |
618 | |
619 if (!partition) | |
620 return false; | |
621 | |
622 partition->ClearData( | |
623 removal_mask, | |
624 content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL, | |
625 GURL(), | |
626 content::StoragePartition::OriginMatcherFunction(), | |
627 remove_since, | |
628 base::Time::Now(), | |
629 callback); | |
630 return true; | |
631 } | |
632 | |
633 WebViewGuest::WebViewGuest(content::BrowserContext* browser_context, | |
634 int guest_instance_id) | |
635 : GuestView<WebViewGuest>(browser_context, guest_instance_id), | |
636 is_overriding_user_agent_(false), | |
637 find_helper_(this), | |
638 javascript_dialog_helper_(this) { | |
639 web_view_guest_delegate_.reset(new ChromeWebViewGuestDelegate(this)); | |
640 } | |
641 | |
642 WebViewGuest::~WebViewGuest() { | |
643 } | |
644 | |
645 void WebViewGuest::DidCommitProvisionalLoadForFrame( | |
646 content::RenderFrameHost* render_frame_host, | |
647 const GURL& url, | |
648 content::PageTransition transition_type) { | |
649 find_helper_.CancelAllFindSessions(); | |
650 | |
651 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
652 args->SetString(guestview::kUrl, url.spec()); | |
653 args->SetBoolean(guestview::kIsTopLevel, !render_frame_host->GetParent()); | |
654 args->SetInteger(webview::kInternalCurrentEntryIndex, | |
655 guest_web_contents()->GetController().GetCurrentEntryIndex()); | |
656 args->SetInteger(webview::kInternalEntryCount, | |
657 guest_web_contents()->GetController().GetEntryCount()); | |
658 args->SetInteger(webview::kInternalProcessId, | |
659 guest_web_contents()->GetRenderProcessHost()->GetID()); | |
660 DispatchEventToEmbedder( | |
661 new GuestViewBase::Event(webview::kEventLoadCommit, args.Pass())); | |
662 if (web_view_guest_delegate_) { | |
663 web_view_guest_delegate_->OnDidCommitProvisionalLoadForFrame( | |
664 !render_frame_host->GetParent()); | |
665 } | |
666 } | |
667 | |
668 void WebViewGuest::DidFailProvisionalLoad( | |
669 content::RenderFrameHost* render_frame_host, | |
670 const GURL& validated_url, | |
671 int error_code, | |
672 const base::string16& error_description) { | |
673 LoadAbort(!render_frame_host->GetParent(), validated_url, | |
674 net::ErrorToShortString(error_code)); | |
675 } | |
676 | |
677 void WebViewGuest::DidStartProvisionalLoadForFrame( | |
678 content::RenderFrameHost* render_frame_host, | |
679 const GURL& validated_url, | |
680 bool is_error_page, | |
681 bool is_iframe_srcdoc) { | |
682 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
683 args->SetString(guestview::kUrl, validated_url.spec()); | |
684 args->SetBoolean(guestview::kIsTopLevel, !render_frame_host->GetParent()); | |
685 DispatchEventToEmbedder( | |
686 new GuestViewBase::Event(webview::kEventLoadStart, args.Pass())); | |
687 } | |
688 | |
689 void WebViewGuest::DocumentLoadedInFrame( | |
690 content::RenderFrameHost* render_frame_host) { | |
691 if (web_view_guest_delegate_) | |
692 web_view_guest_delegate_->OnDocumentLoadedInFrame(render_frame_host); | |
693 } | |
694 | |
695 bool WebViewGuest::OnMessageReceived(const IPC::Message& message, | |
696 RenderFrameHost* render_frame_host) { | |
697 bool handled = true; | |
698 IPC_BEGIN_MESSAGE_MAP(WebViewGuest, message) | |
699 IPC_MESSAGE_HANDLER(ExtensionHostMsg_FrameNameChanged, OnFrameNameChanged) | |
700 IPC_MESSAGE_UNHANDLED(handled = false) | |
701 IPC_END_MESSAGE_MAP() | |
702 return handled; | |
703 } | |
704 | |
705 void WebViewGuest::RenderProcessGone(base::TerminationStatus status) { | |
706 // Cancel all find sessions in progress. | |
707 find_helper_.CancelAllFindSessions(); | |
708 | |
709 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
710 args->SetInteger(webview::kProcessId, | |
711 guest_web_contents()->GetRenderProcessHost()->GetID()); | |
712 args->SetString(webview::kReason, TerminationStatusToString(status)); | |
713 DispatchEventToEmbedder( | |
714 new GuestViewBase::Event(webview::kEventExit, args.Pass())); | |
715 } | |
716 | |
717 void WebViewGuest::UserAgentOverrideSet(const std::string& user_agent) { | |
718 if (!attached()) | |
719 return; | |
720 content::NavigationController& controller = | |
721 guest_web_contents()->GetController(); | |
722 content::NavigationEntry* entry = controller.GetVisibleEntry(); | |
723 if (!entry) | |
724 return; | |
725 entry->SetIsOverridingUserAgent(!user_agent.empty()); | |
726 guest_web_contents()->GetController().Reload(false); | |
727 } | |
728 | |
729 void WebViewGuest::ReportFrameNameChange(const std::string& name) { | |
730 name_ = name; | |
731 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
732 args->SetString(webview::kName, name); | |
733 DispatchEventToEmbedder( | |
734 new GuestViewBase::Event(webview::kEventFrameNameChanged, args.Pass())); | |
735 } | |
736 | |
737 void WebViewGuest::LoadHandlerCalled() { | |
738 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
739 DispatchEventToEmbedder( | |
740 new GuestViewBase::Event(webview::kEventContentLoad, args.Pass())); | |
741 } | |
742 | |
743 void WebViewGuest::LoadRedirect(const GURL& old_url, | |
744 const GURL& new_url, | |
745 bool is_top_level) { | |
746 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
747 args->SetBoolean(guestview::kIsTopLevel, is_top_level); | |
748 args->SetString(webview::kNewURL, new_url.spec()); | |
749 args->SetString(webview::kOldURL, old_url.spec()); | |
750 DispatchEventToEmbedder( | |
751 new GuestViewBase::Event(webview::kEventLoadRedirect, args.Pass())); | |
752 } | |
753 | |
754 void WebViewGuest::PushWebViewStateToIOThread() { | |
755 const GURL& site_url = guest_web_contents()->GetSiteInstance()->GetSiteURL(); | |
756 std::string partition_domain; | |
757 std::string partition_id; | |
758 bool in_memory; | |
759 if (!GetGuestPartitionConfigForSite( | |
760 site_url, &partition_domain, &partition_id, &in_memory)) { | |
761 NOTREACHED(); | |
762 return; | |
763 } | |
764 DCHECK(embedder_extension_id() == partition_domain); | |
765 | |
766 WebViewRendererState::WebViewInfo web_view_info; | |
767 web_view_info.embedder_process_id = embedder_render_process_id(); | |
768 web_view_info.instance_id = view_instance_id(); | |
769 web_view_info.partition_id = partition_id; | |
770 web_view_info.embedder_extension_id = embedder_extension_id(); | |
771 | |
772 content::BrowserThread::PostTask( | |
773 content::BrowserThread::IO, | |
774 FROM_HERE, | |
775 base::Bind(&WebViewRendererState::AddGuest, | |
776 base::Unretained(WebViewRendererState::GetInstance()), | |
777 guest_web_contents()->GetRenderProcessHost()->GetID(), | |
778 guest_web_contents()->GetRoutingID(), | |
779 web_view_info)); | |
780 } | |
781 | |
782 // static | |
783 void WebViewGuest::RemoveWebViewStateFromIOThread( | |
784 WebContents* web_contents) { | |
785 content::BrowserThread::PostTask( | |
786 content::BrowserThread::IO, FROM_HERE, | |
787 base::Bind( | |
788 &WebViewRendererState::RemoveGuest, | |
789 base::Unretained(WebViewRendererState::GetInstance()), | |
790 web_contents->GetRenderProcessHost()->GetID(), | |
791 web_contents->GetRoutingID())); | |
792 } | |
793 | |
794 content::WebContents* WebViewGuest::CreateNewGuestWindow( | |
795 const content::WebContents::CreateParams& create_params) { | |
796 GuestViewManager* guest_manager = | |
797 GuestViewManager::FromBrowserContext(browser_context()); | |
798 return guest_manager->CreateGuestWithWebContentsParams( | |
799 WebViewGuest::Type, | |
800 embedder_extension_id(), | |
801 embedder_web_contents()->GetRenderProcessHost()->GetID(), | |
802 create_params); | |
803 } | |
804 | |
805 void WebViewGuest::RequestMediaAccessPermission( | |
806 content::WebContents* source, | |
807 const content::MediaStreamRequest& request, | |
808 const content::MediaResponseCallback& callback) { | |
809 web_view_permission_helper_->RequestMediaAccessPermission(source, | |
810 request, | |
811 callback); | |
812 } | |
813 | |
814 void WebViewGuest::CanDownload( | |
815 content::RenderViewHost* render_view_host, | |
816 const GURL& url, | |
817 const std::string& request_method, | |
818 const base::Callback<void(bool)>& callback) { | |
819 web_view_permission_helper_->CanDownload(render_view_host, | |
820 url, | |
821 request_method, | |
822 callback); | |
823 } | |
824 | |
825 void WebViewGuest::RequestPointerLockPermission( | |
826 bool user_gesture, | |
827 bool last_unlocked_by_target, | |
828 const base::Callback<void(bool)>& callback) { | |
829 web_view_permission_helper_->RequestPointerLockPermission( | |
830 user_gesture, | |
831 last_unlocked_by_target, | |
832 callback); | |
833 } | |
834 | |
835 void WebViewGuest::WillAttachToEmbedder() { | |
836 // We must install the mapping from guests to WebViews prior to resuming | |
837 // suspended resource loads so that the WebRequest API will catch resource | |
838 // requests. | |
839 PushWebViewStateToIOThread(); | |
840 } | |
841 | |
842 content::JavaScriptDialogManager* | |
843 WebViewGuest::GetJavaScriptDialogManager() { | |
844 return &javascript_dialog_helper_; | |
845 } | |
846 | |
847 content::ColorChooser* WebViewGuest::OpenColorChooser( | |
848 WebContents* web_contents, | |
849 SkColor color, | |
850 const std::vector<content::ColorSuggestion>& suggestions) { | |
851 if (!attached() || !embedder_web_contents()->GetDelegate()) | |
852 return NULL; | |
853 return embedder_web_contents()->GetDelegate()->OpenColorChooser( | |
854 web_contents, color, suggestions); | |
855 } | |
856 | |
857 void WebViewGuest::RunFileChooser(WebContents* web_contents, | |
858 const content::FileChooserParams& params) { | |
859 if (!attached() || !embedder_web_contents()->GetDelegate()) | |
860 return; | |
861 | |
862 embedder_web_contents()->GetDelegate()->RunFileChooser(web_contents, params); | |
863 } | |
864 | |
865 void WebViewGuest::NavigateGuest(const std::string& src) { | |
866 GURL url = ResolveURL(src); | |
867 | |
868 // Do not allow navigating a guest to schemes other than known safe schemes. | |
869 // This will block the embedder trying to load unwanted schemes, e.g. | |
870 // chrome://settings. | |
871 bool scheme_is_blocked = | |
872 (!content::ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme( | |
873 url.scheme()) && | |
874 !url.SchemeIs(url::kAboutScheme)) || | |
875 url.SchemeIs(url::kJavaScriptScheme); | |
876 if (scheme_is_blocked || !url.is_valid()) { | |
877 LoadAbort(true /* is_top_level */, url, | |
878 net::ErrorToShortString(net::ERR_ABORTED)); | |
879 return; | |
880 } | |
881 | |
882 GURL validated_url(url); | |
883 guest_web_contents()->GetRenderProcessHost()-> | |
884 FilterURL(false, &validated_url); | |
885 // As guests do not swap processes on navigation, only navigations to | |
886 // normal web URLs are supported. No protocol handlers are installed for | |
887 // other schemes (e.g., WebUI or extensions), and no permissions or bindings | |
888 // can be granted to the guest process. | |
889 LoadURLWithParams(validated_url, | |
890 content::Referrer(), | |
891 content::PAGE_TRANSITION_AUTO_TOPLEVEL, | |
892 guest_web_contents()); | |
893 } | |
894 | |
895 bool WebViewGuest::HandleKeyboardShortcuts( | |
896 const content::NativeWebKeyboardEvent& event) { | |
897 if (event.type != blink::WebInputEvent::RawKeyDown) | |
898 return false; | |
899 | |
900 // If the user hits the escape key without any modifiers then unlock the | |
901 // mouse if necessary. | |
902 if ((event.windowsKeyCode == ui::VKEY_ESCAPE) && | |
903 !(event.modifiers & blink::WebInputEvent::InputModifiers)) { | |
904 return guest_web_contents()->GotResponseToLockMouseRequest(false); | |
905 } | |
906 | |
907 #if defined(OS_MACOSX) | |
908 if (event.modifiers != blink::WebInputEvent::MetaKey) | |
909 return false; | |
910 | |
911 if (event.windowsKeyCode == ui::VKEY_OEM_4) { | |
912 Go(-1); | |
913 return true; | |
914 } | |
915 | |
916 if (event.windowsKeyCode == ui::VKEY_OEM_6) { | |
917 Go(1); | |
918 return true; | |
919 } | |
920 #else | |
921 if (event.windowsKeyCode == ui::VKEY_BROWSER_BACK) { | |
922 Go(-1); | |
923 return true; | |
924 } | |
925 | |
926 if (event.windowsKeyCode == ui::VKEY_BROWSER_FORWARD) { | |
927 Go(1); | |
928 return true; | |
929 } | |
930 #endif | |
931 | |
932 return false; | |
933 } | |
934 | |
935 void WebViewGuest::SetUpAutoSize() { | |
936 // Read the autosize parameters passed in from the embedder. | |
937 bool auto_size_enabled = false; | |
938 attach_params()->GetBoolean(webview::kAttributeAutoSize, &auto_size_enabled); | |
939 | |
940 int max_height = 0; | |
941 int max_width = 0; | |
942 attach_params()->GetInteger(webview::kAttributeMaxHeight, &max_height); | |
943 attach_params()->GetInteger(webview::kAttributeMaxWidth, &max_width); | |
944 | |
945 int min_height = 0; | |
946 int min_width = 0; | |
947 attach_params()->GetInteger(webview::kAttributeMinHeight, &min_height); | |
948 attach_params()->GetInteger(webview::kAttributeMinWidth, &min_width); | |
949 | |
950 // Call SetAutoSize to apply all the appropriate validation and clipping of | |
951 // values. | |
952 SetAutoSize(auto_size_enabled, | |
953 gfx::Size(min_width, min_height), | |
954 gfx::Size(max_width, max_height)); | |
955 } | |
956 | |
957 void WebViewGuest::ShowContextMenu( | |
958 int request_id, | |
959 const WebViewGuestDelegate::MenuItemVector* items) { | |
960 if (web_view_guest_delegate_) | |
961 web_view_guest_delegate_->OnShowContextMenu(request_id, items); | |
962 } | |
963 | |
964 void WebViewGuest::SetName(const std::string& name) { | |
965 if (name_ == name) | |
966 return; | |
967 name_ = name; | |
968 | |
969 Send(new ExtensionMsg_SetFrameName(routing_id(), name_)); | |
970 } | |
971 | |
972 void WebViewGuest::SetZoom(double zoom_factor) { | |
973 if (web_view_guest_delegate_) | |
974 web_view_guest_delegate_->OnSetZoom(zoom_factor); | |
975 } | |
976 | |
977 void WebViewGuest::AddNewContents(content::WebContents* source, | |
978 content::WebContents* new_contents, | |
979 WindowOpenDisposition disposition, | |
980 const gfx::Rect& initial_pos, | |
981 bool user_gesture, | |
982 bool* was_blocked) { | |
983 if (was_blocked) | |
984 *was_blocked = false; | |
985 RequestNewWindowPermission(disposition, | |
986 initial_pos, | |
987 user_gesture, | |
988 new_contents); | |
989 } | |
990 | |
991 content::WebContents* WebViewGuest::OpenURLFromTab( | |
992 content::WebContents* source, | |
993 const content::OpenURLParams& params) { | |
994 // If the guest wishes to navigate away prior to attachment then we save the | |
995 // navigation to perform upon attachment. Navigation initializes a lot of | |
996 // state that assumes an embedder exists, such as RenderWidgetHostViewGuest. | |
997 // Navigation also resumes resource loading which we don't want to allow | |
998 // until attachment. | |
999 if (!attached()) { | |
1000 WebViewGuest* opener = GetOpener(); | |
1001 PendingWindowMap::iterator it = | |
1002 opener->pending_new_windows_.find(this); | |
1003 if (it == opener->pending_new_windows_.end()) | |
1004 return NULL; | |
1005 const NewWindowInfo& info = it->second; | |
1006 NewWindowInfo new_window_info(params.url, info.name); | |
1007 new_window_info.changed = new_window_info.url != info.url; | |
1008 it->second = new_window_info; | |
1009 return NULL; | |
1010 } | |
1011 if (params.disposition == CURRENT_TAB) { | |
1012 // This can happen for cross-site redirects. | |
1013 LoadURLWithParams(params.url, params.referrer, params.transition, source); | |
1014 return source; | |
1015 } | |
1016 | |
1017 CreateNewGuestWebViewWindow(params); | |
1018 return NULL; | |
1019 } | |
1020 | |
1021 void WebViewGuest::WebContentsCreated(WebContents* source_contents, | |
1022 int opener_render_frame_id, | |
1023 const base::string16& frame_name, | |
1024 const GURL& target_url, | |
1025 content::WebContents* new_contents) { | |
1026 WebViewGuest* guest = WebViewGuest::FromWebContents(new_contents); | |
1027 CHECK(guest); | |
1028 guest->SetOpener(this); | |
1029 std::string guest_name = base::UTF16ToUTF8(frame_name); | |
1030 guest->name_ = guest_name; | |
1031 pending_new_windows_.insert( | |
1032 std::make_pair(guest, NewWindowInfo(target_url, guest_name))); | |
1033 } | |
1034 | |
1035 void WebViewGuest::LoadURLWithParams(const GURL& url, | |
1036 const content::Referrer& referrer, | |
1037 content::PageTransition transition_type, | |
1038 content::WebContents* web_contents) { | |
1039 content::NavigationController::LoadURLParams load_url_params(url); | |
1040 load_url_params.referrer = referrer; | |
1041 load_url_params.transition_type = transition_type; | |
1042 load_url_params.extra_headers = std::string(); | |
1043 if (is_overriding_user_agent_) { | |
1044 load_url_params.override_user_agent = | |
1045 content::NavigationController::UA_OVERRIDE_TRUE; | |
1046 } | |
1047 web_contents->GetController().LoadURLWithParams(load_url_params); | |
1048 } | |
1049 | |
1050 void WebViewGuest::RequestNewWindowPermission( | |
1051 WindowOpenDisposition disposition, | |
1052 const gfx::Rect& initial_bounds, | |
1053 bool user_gesture, | |
1054 content::WebContents* new_contents) { | |
1055 WebViewGuest* guest = WebViewGuest::FromWebContents(new_contents); | |
1056 if (!guest) | |
1057 return; | |
1058 PendingWindowMap::iterator it = pending_new_windows_.find(guest); | |
1059 if (it == pending_new_windows_.end()) | |
1060 return; | |
1061 const NewWindowInfo& new_window_info = it->second; | |
1062 | |
1063 // Retrieve the opener partition info if we have it. | |
1064 const GURL& site_url = new_contents->GetSiteInstance()->GetSiteURL(); | |
1065 std::string storage_partition_id = GetStoragePartitionIdFromSiteURL(site_url); | |
1066 | |
1067 base::DictionaryValue request_info; | |
1068 request_info.SetInteger(webview::kInitialHeight, initial_bounds.height()); | |
1069 request_info.SetInteger(webview::kInitialWidth, initial_bounds.width()); | |
1070 request_info.Set(webview::kTargetURL, | |
1071 new base::StringValue(new_window_info.url.spec())); | |
1072 request_info.Set(webview::kName, new base::StringValue(new_window_info.name)); | |
1073 request_info.SetInteger(webview::kWindowID, guest->guest_instance_id()); | |
1074 // We pass in partition info so that window-s created through newwindow | |
1075 // API can use it to set their partition attribute. | |
1076 request_info.Set(webview::kStoragePartitionId, | |
1077 new base::StringValue(storage_partition_id)); | |
1078 request_info.Set( | |
1079 webview::kWindowOpenDisposition, | |
1080 new base::StringValue(WindowOpenDispositionToString(disposition))); | |
1081 | |
1082 web_view_permission_helper_-> | |
1083 RequestPermission(WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW, | |
1084 request_info, | |
1085 base::Bind(&WebViewGuest::OnWebViewNewWindowResponse, | |
1086 base::Unretained(this), | |
1087 guest->guest_instance_id()), | |
1088 false /* allowed_by_default */); | |
1089 } | |
1090 | |
1091 void WebViewGuest::DestroyUnattachedWindows() { | |
1092 // Destroy() reaches in and removes the WebViewGuest from its opener's | |
1093 // pending_new_windows_ set. To avoid mutating the set while iterating, we | |
1094 // create a copy of the pending new windows set and iterate over the copy. | |
1095 PendingWindowMap pending_new_windows(pending_new_windows_); | |
1096 // Clean up unattached new windows opened by this guest. | |
1097 for (PendingWindowMap::const_iterator it = pending_new_windows.begin(); | |
1098 it != pending_new_windows.end(); ++it) { | |
1099 it->first->Destroy(); | |
1100 } | |
1101 // All pending windows should be removed from the set after Destroy() is | |
1102 // called on all of them. | |
1103 DCHECK(pending_new_windows_.empty()); | |
1104 } | |
1105 | |
1106 GURL WebViewGuest::ResolveURL(const std::string& src) { | |
1107 if (!in_extension()) { | |
1108 NOTREACHED(); | |
1109 return GURL(src); | |
1110 } | |
1111 | |
1112 GURL default_url(base::StringPrintf("%s://%s/", | |
1113 kExtensionScheme, | |
1114 embedder_extension_id().c_str())); | |
1115 return default_url.Resolve(src); | |
1116 } | |
1117 | |
1118 void WebViewGuest::OnWebViewNewWindowResponse( | |
1119 int new_window_instance_id, | |
1120 bool allow, | |
1121 const std::string& user_input) { | |
1122 WebViewGuest* guest = | |
1123 WebViewGuest::From(embedder_render_process_id(), new_window_instance_id); | |
1124 if (!guest) | |
1125 return; | |
1126 | |
1127 if (!allow) | |
1128 guest->Destroy(); | |
1129 } | |
1130 | |
1131 } // namespace extensions | |
OLD | NEW |