Unregister from GCM when the only GCM app is removed
[chromium-blink-merge.git] / extensions / browser / guest_view / web_view / web_view_guest.cc
blob9e60bfddef4d22bf074249c6b43c9b6dcf436630
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.
5 #include "extensions/browser/guest_view/web_view/web_view_guest.h"
7 #include "base/message_loop/message_loop.h"
8 #include "base/strings/stringprintf.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "content/public/browser/browser_context.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "content/public/browser/child_process_security_policy.h"
13 #include "content/public/browser/native_web_keyboard_event.h"
14 #include "content/public/browser/navigation_entry.h"
15 #include "content/public/browser/notification_details.h"
16 #include "content/public/browser/notification_service.h"
17 #include "content/public/browser/notification_source.h"
18 #include "content/public/browser/notification_types.h"
19 #include "content/public/browser/render_process_host.h"
20 #include "content/public/browser/render_view_host.h"
21 #include "content/public/browser/render_widget_host_view.h"
22 #include "content/public/browser/resource_request_details.h"
23 #include "content/public/browser/site_instance.h"
24 #include "content/public/browser/storage_partition.h"
25 #include "content/public/browser/user_metrics.h"
26 #include "content/public/browser/web_contents.h"
27 #include "content/public/browser/web_contents_delegate.h"
28 #include "content/public/common/media_stream_request.h"
29 #include "content/public/common/page_zoom.h"
30 #include "content/public/common/result_codes.h"
31 #include "content/public/common/stop_find_action.h"
32 #include "content/public/common/url_constants.h"
33 #include "extensions/browser/api/declarative/rules_registry_service.h"
34 #include "extensions/browser/api/extensions_api_client.h"
35 #include "extensions/browser/api/web_request/web_request_api.h"
36 #include "extensions/browser/api/web_view/web_view_internal_api.h"
37 #include "extensions/browser/extension_system.h"
38 #include "extensions/browser/guest_view/guest_view_manager.h"
39 #include "extensions/browser/guest_view/web_view/web_view_constants.h"
40 #include "extensions/browser/guest_view/web_view/web_view_permission_helper.h"
41 #include "extensions/browser/guest_view/web_view/web_view_permission_types.h"
42 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
43 #include "extensions/common/constants.h"
44 #include "extensions/common/extension_messages.h"
45 #include "extensions/common/guest_view/guest_view_constants.h"
46 #include "extensions/strings/grit/extensions_strings.h"
47 #include "ipc/ipc_message_macros.h"
48 #include "net/base/escape.h"
49 #include "net/base/net_errors.h"
50 #include "ui/base/models/simple_menu_model.h"
51 #include "url/url_constants.h"
53 using base::UserMetricsAction;
54 using content::RenderFrameHost;
55 using content::ResourceType;
56 using content::WebContents;
58 namespace extensions {
60 namespace {
62 std::string WindowOpenDispositionToString(
63 WindowOpenDisposition window_open_disposition) {
64 switch (window_open_disposition) {
65 case IGNORE_ACTION:
66 return "ignore";
67 case SAVE_TO_DISK:
68 return "save_to_disk";
69 case CURRENT_TAB:
70 return "current_tab";
71 case NEW_BACKGROUND_TAB:
72 return "new_background_tab";
73 case NEW_FOREGROUND_TAB:
74 return "new_foreground_tab";
75 case NEW_WINDOW:
76 return "new_window";
77 case NEW_POPUP:
78 return "new_popup";
79 default:
80 NOTREACHED() << "Unknown Window Open Disposition";
81 return "ignore";
85 static std::string TerminationStatusToString(base::TerminationStatus status) {
86 switch (status) {
87 case base::TERMINATION_STATUS_NORMAL_TERMINATION:
88 return "normal";
89 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
90 case base::TERMINATION_STATUS_STILL_RUNNING:
91 return "abnormal";
92 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
93 return "killed";
94 case base::TERMINATION_STATUS_PROCESS_CRASHED:
95 return "crashed";
96 case base::TERMINATION_STATUS_MAX_ENUM:
97 break;
99 NOTREACHED() << "Unknown Termination Status.";
100 return "unknown";
103 std::string GetStoragePartitionIdFromSiteURL(const GURL& site_url) {
104 const std::string& partition_id = site_url.query();
105 bool persist_storage = site_url.path().find("persist") != std::string::npos;
106 return (persist_storage ? webview::kPersistPrefix : "") + partition_id;
109 void ParsePartitionParam(const base::DictionaryValue& create_params,
110 std::string* storage_partition_id,
111 bool* persist_storage) {
112 std::string partition_str;
113 if (!create_params.GetString(webview::kStoragePartitionId, &partition_str)) {
114 return;
117 // Since the "persist:" prefix is in ASCII, StartsWith will work fine on
118 // UTF-8 encoded |partition_id|. If the prefix is a match, we can safely
119 // remove the prefix without splicing in the middle of a multi-byte codepoint.
120 // We can use the rest of the string as UTF-8 encoded one.
121 if (StartsWithASCII(partition_str, "persist:", true)) {
122 size_t index = partition_str.find(":");
123 CHECK(index != std::string::npos);
124 // It is safe to do index + 1, since we tested for the full prefix above.
125 *storage_partition_id = partition_str.substr(index + 1);
127 if (storage_partition_id->empty()) {
128 // TODO(lazyboy): Better way to deal with this error.
129 return;
131 *persist_storage = true;
132 } else {
133 *storage_partition_id = partition_str;
134 *persist_storage = false;
138 void RemoveWebViewEventListenersOnIOThread(
139 void* profile,
140 const std::string& extension_id,
141 int embedder_process_id,
142 int view_instance_id) {
143 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
144 ExtensionWebRequestEventRouter::GetInstance()->RemoveWebViewEventListeners(
145 profile,
146 extension_id,
147 embedder_process_id,
148 view_instance_id);
151 } // namespace
153 // static
154 GuestViewBase* WebViewGuest::Create(content::WebContents* owner_web_contents) {
155 return new WebViewGuest(owner_web_contents);
158 // static
159 bool WebViewGuest::GetGuestPartitionConfigForSite(
160 const GURL& site,
161 std::string* partition_domain,
162 std::string* partition_name,
163 bool* in_memory) {
164 if (!site.SchemeIs(content::kGuestScheme))
165 return false;
167 // Since guest URLs are only used for packaged apps, there must be an app
168 // id in the URL.
169 CHECK(site.has_host());
170 *partition_domain = site.host();
171 // Since persistence is optional, the path must either be empty or the
172 // literal string.
173 *in_memory = (site.path() != "/persist");
174 // The partition name is user supplied value, which we have encoded when the
175 // URL was created, so it needs to be decoded.
176 *partition_name =
177 net::UnescapeURLComponent(site.query(), net::UnescapeRule::NORMAL);
178 return true;
181 // static
182 const char WebViewGuest::Type[] = "webview";
184 using WebViewKey = std::pair<int, int>;
185 using WebViewKeyToIDMap = std::map<WebViewKey, int>;
186 static base::LazyInstance<WebViewKeyToIDMap> web_view_key_to_id_map =
187 LAZY_INSTANCE_INITIALIZER;
189 // static
190 int WebViewGuest::GetOrGenerateRulesRegistryID(
191 int embedder_process_id,
192 int webview_instance_id) {
193 bool is_web_view = embedder_process_id && webview_instance_id;
194 if (!is_web_view)
195 return RulesRegistryService::kDefaultRulesRegistryID;
197 WebViewKey key = std::make_pair(embedder_process_id, webview_instance_id);
198 auto it = web_view_key_to_id_map.Get().find(key);
199 if (it != web_view_key_to_id_map.Get().end())
200 return it->second;
202 auto rph = content::RenderProcessHost::FromID(embedder_process_id);
203 int rules_registry_id =
204 RulesRegistryService::Get(rph->GetBrowserContext())->
205 GetNextRulesRegistryID();
206 web_view_key_to_id_map.Get()[key] = rules_registry_id;
207 return rules_registry_id;
210 // static
211 int WebViewGuest::GetViewInstanceId(WebContents* contents) {
212 auto guest = FromWebContents(contents);
213 if (!guest)
214 return guestview::kInstanceIDNone;
216 return guest->view_instance_id();
219 bool WebViewGuest::CanRunInDetachedState() const {
220 return true;
223 void WebViewGuest::CreateWebContents(
224 const base::DictionaryValue& create_params,
225 const WebContentsCreatedCallback& callback) {
226 content::RenderProcessHost* owner_render_process_host =
227 owner_web_contents()->GetRenderProcessHost();
228 std::string storage_partition_id;
229 bool persist_storage = false;
230 ParsePartitionParam(create_params, &storage_partition_id, &persist_storage);
231 // Validate that the partition id coming from the renderer is valid UTF-8,
232 // since we depend on this in other parts of the code, such as FilePath
233 // creation. If the validation fails, treat it as a bad message and kill the
234 // renderer process.
235 if (!base::IsStringUTF8(storage_partition_id)) {
236 content::RecordAction(
237 base::UserMetricsAction("BadMessageTerminate_BPGM"));
238 owner_render_process_host->Shutdown(content::RESULT_CODE_KILLED_BAD_MESSAGE,
239 false);
240 callback.Run(nullptr);
241 return;
243 std::string url_encoded_partition = net::EscapeQueryParamValue(
244 storage_partition_id, false);
245 std::string partition_domain = GetOwnerSiteURL().host();
246 GURL guest_site(base::StringPrintf("%s://%s/%s?%s",
247 content::kGuestScheme,
248 partition_domain.c_str(),
249 persist_storage ? "persist" : "",
250 url_encoded_partition.c_str()));
252 // If we already have a webview tag in the same app using the same storage
253 // partition, we should use the same SiteInstance so the existing tag and
254 // the new tag can script each other.
255 auto guest_view_manager = GuestViewManager::FromBrowserContext(
256 owner_render_process_host->GetBrowserContext());
257 content::SiteInstance* guest_site_instance =
258 guest_view_manager->GetGuestSiteInstance(guest_site);
259 if (!guest_site_instance) {
260 // Create the SiteInstance in a new BrowsingInstance, which will ensure
261 // that webview tags are also not allowed to send messages across
262 // different partitions.
263 guest_site_instance = content::SiteInstance::CreateForURL(
264 owner_render_process_host->GetBrowserContext(), guest_site);
266 WebContents::CreateParams params(
267 owner_render_process_host->GetBrowserContext(),
268 guest_site_instance);
269 params.guest_delegate = this;
270 callback.Run(WebContents::Create(params));
273 void WebViewGuest::DidAttachToEmbedder() {
274 ApplyAttributes(*attach_params());
277 void WebViewGuest::DidInitialize(const base::DictionaryValue& create_params) {
278 script_executor_.reset(
279 new ScriptExecutor(web_contents(), &script_observers_));
281 notification_registrar_.Add(this,
282 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
283 content::Source<WebContents>(web_contents()));
285 notification_registrar_.Add(this,
286 content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT,
287 content::Source<WebContents>(web_contents()));
289 if (web_view_guest_delegate_)
290 web_view_guest_delegate_->OnDidInitialize();
291 AttachWebViewHelpers(web_contents());
293 rules_registry_id_ = GetOrGenerateRulesRegistryID(
294 owner_web_contents()->GetRenderProcessHost()->GetID(),
295 view_instance_id());
297 // We must install the mapping from guests to WebViews prior to resuming
298 // suspended resource loads so that the WebRequest API will catch resource
299 // requests.
300 PushWebViewStateToIOThread();
302 ApplyAttributes(create_params);
305 void WebViewGuest::AttachWebViewHelpers(WebContents* contents) {
306 if (web_view_guest_delegate_)
307 web_view_guest_delegate_->OnAttachWebViewHelpers(contents);
308 web_view_permission_helper_.reset(new WebViewPermissionHelper(this));
311 void WebViewGuest::DidStopLoading() {
312 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
313 DispatchEventToView(
314 new GuestViewBase::Event(webview::kEventLoadStop, args.Pass()));
317 void WebViewGuest::EmbedderWillBeDestroyed() {
318 // Clean up rules registries for the webview.
319 RulesRegistryService::Get(browser_context())
320 ->RemoveRulesRegistriesByID(rules_registry_id_);
321 WebViewKey key(owner_web_contents()->GetRenderProcessHost()->GetID(),
322 view_instance_id());
323 web_view_key_to_id_map.Get().erase(key);
325 content::BrowserThread::PostTask(
326 content::BrowserThread::IO,
327 FROM_HERE,
328 base::Bind(
329 &RemoveWebViewEventListenersOnIOThread,
330 browser_context(),
331 owner_extension_id(),
332 owner_web_contents()->GetRenderProcessHost()->GetID(),
333 view_instance_id()));
336 const char* WebViewGuest::GetAPINamespace() const {
337 return webview::kAPINamespace;
340 int WebViewGuest::GetTaskPrefix() const {
341 return IDS_EXTENSION_TASK_MANAGER_WEBVIEW_TAG_PREFIX;
344 void WebViewGuest::GuestDestroyed() {
345 // Clean up custom context menu items for this guest.
346 if (web_view_guest_delegate_)
347 web_view_guest_delegate_->OnGuestDestroyed();
348 RemoveWebViewStateFromIOThread(web_contents());
351 void WebViewGuest::GuestReady() {
352 // The guest RenderView should always live in an isolated guest process.
353 CHECK(web_contents()->GetRenderProcessHost()->IsIsolatedGuest());
354 Send(new ExtensionMsg_SetFrameName(web_contents()->GetRoutingID(), name_));
356 // We don't want to accidentally set the opacity of an interstitial page.
357 // WebContents::GetRenderWidgetHostView will return the RWHV of an
358 // interstitial page if one is showing at this time. We only want opacity
359 // to apply to web pages.
360 if (guest_opaque_) {
361 web_contents()
362 ->GetRenderViewHost()
363 ->GetView()
364 ->SetBackgroundColorToDefault();
365 } else {
366 web_contents()->GetRenderViewHost()->GetView()->SetBackgroundColor(
367 SK_ColorTRANSPARENT);
371 void WebViewGuest::GuestSizeChangedDueToAutoSize(const gfx::Size& old_size,
372 const gfx::Size& new_size) {
373 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
374 args->SetInteger(webview::kOldHeight, old_size.height());
375 args->SetInteger(webview::kOldWidth, old_size.width());
376 args->SetInteger(webview::kNewHeight, new_size.height());
377 args->SetInteger(webview::kNewWidth, new_size.width());
378 DispatchEventToView(
379 new GuestViewBase::Event(webview::kEventSizeChanged, args.Pass()));
382 bool WebViewGuest::IsAutoSizeSupported() const {
383 return true;
386 bool WebViewGuest::IsDragAndDropEnabled() const {
387 return true;
390 void WebViewGuest::WillDestroy() {
391 if (!attached() && GetOpener())
392 GetOpener()->pending_new_windows_.erase(this);
395 bool WebViewGuest::AddMessageToConsole(WebContents* source,
396 int32 level,
397 const base::string16& message,
398 int32 line_no,
399 const base::string16& source_id) {
400 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
401 // Log levels are from base/logging.h: LogSeverity.
402 args->SetInteger(webview::kLevel, level);
403 args->SetString(webview::kMessage, message);
404 args->SetInteger(webview::kLine, line_no);
405 args->SetString(webview::kSourceId, source_id);
406 DispatchEventToView(
407 new GuestViewBase::Event(webview::kEventConsoleMessage, args.Pass()));
408 return true;
411 void WebViewGuest::CloseContents(WebContents* source) {
412 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
413 DispatchEventToView(
414 new GuestViewBase::Event(webview::kEventClose, args.Pass()));
417 void WebViewGuest::FindReply(WebContents* source,
418 int request_id,
419 int number_of_matches,
420 const gfx::Rect& selection_rect,
421 int active_match_ordinal,
422 bool final_update) {
423 find_helper_.FindReply(request_id,
424 number_of_matches,
425 selection_rect,
426 active_match_ordinal,
427 final_update);
430 bool WebViewGuest::HandleContextMenu(
431 const content::ContextMenuParams& params) {
432 if (!web_view_guest_delegate_)
433 return false;
434 return web_view_guest_delegate_->HandleContextMenu(params);
437 void WebViewGuest::HandleKeyboardEvent(
438 WebContents* source,
439 const content::NativeWebKeyboardEvent& event) {
440 if (HandleKeyboardShortcuts(event))
441 return;
443 GuestViewBase::HandleKeyboardEvent(source, event);
446 void WebViewGuest::LoadProgressChanged(content::WebContents* source,
447 double progress) {
448 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
449 args->SetString(guestview::kUrl, web_contents()->GetURL().spec());
450 args->SetDouble(webview::kProgress, progress);
451 DispatchEventToView(
452 new GuestViewBase::Event(webview::kEventLoadProgress, args.Pass()));
455 void WebViewGuest::LoadAbort(bool is_top_level,
456 const GURL& url,
457 const std::string& error_type) {
458 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
459 args->SetBoolean(guestview::kIsTopLevel, is_top_level);
460 args->SetString(guestview::kUrl, url.possibly_invalid_spec());
461 args->SetString(guestview::kReason, error_type);
462 DispatchEventToView(
463 new GuestViewBase::Event(webview::kEventLoadAbort, args.Pass()));
466 void WebViewGuest::OnFrameNameChanged(bool is_top_level,
467 const std::string& name) {
468 if (!is_top_level)
469 return;
471 if (name_ == name)
472 return;
474 ReportFrameNameChange(name);
477 void WebViewGuest::CreateNewGuestWebViewWindow(
478 const content::OpenURLParams& params) {
479 GuestViewManager* guest_manager =
480 GuestViewManager::FromBrowserContext(browser_context());
481 // Set the attach params to use the same partition as the opener.
482 // We pull the partition information from the site's URL, which is of the
483 // form guest://site/{persist}?{partition_name}.
484 const GURL& site_url = web_contents()->GetSiteInstance()->GetSiteURL();
485 const std::string storage_partition_id =
486 GetStoragePartitionIdFromSiteURL(site_url);
487 base::DictionaryValue create_params;
488 create_params.SetString(webview::kStoragePartitionId, storage_partition_id);
490 guest_manager->CreateGuest(WebViewGuest::Type,
491 embedder_web_contents(),
492 create_params,
493 base::Bind(&WebViewGuest::NewGuestWebViewCallback,
494 weak_ptr_factory_.GetWeakPtr(),
495 params));
498 void WebViewGuest::NewGuestWebViewCallback(
499 const content::OpenURLParams& params,
500 content::WebContents* guest_web_contents) {
501 WebViewGuest* new_guest = WebViewGuest::FromWebContents(guest_web_contents);
502 new_guest->SetOpener(this);
504 // Take ownership of |new_guest|.
505 pending_new_windows_.insert(
506 std::make_pair(new_guest, NewWindowInfo(params.url, std::string())));
508 // Request permission to show the new window.
509 RequestNewWindowPermission(params.disposition,
510 gfx::Rect(),
511 params.user_gesture,
512 new_guest->web_contents());
515 // TODO(fsamuel): Find a reliable way to test the 'responsive' and
516 // 'unresponsive' events.
517 void WebViewGuest::RendererResponsive(content::WebContents* source) {
518 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
519 args->SetInteger(webview::kProcessId,
520 web_contents()->GetRenderProcessHost()->GetID());
521 DispatchEventToView(
522 new GuestViewBase::Event(webview::kEventResponsive, args.Pass()));
525 void WebViewGuest::RendererUnresponsive(content::WebContents* source) {
526 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
527 args->SetInteger(webview::kProcessId,
528 web_contents()->GetRenderProcessHost()->GetID());
529 DispatchEventToView(
530 new GuestViewBase::Event(webview::kEventUnresponsive, args.Pass()));
533 void WebViewGuest::Observe(int type,
534 const content::NotificationSource& source,
535 const content::NotificationDetails& details) {
536 switch (type) {
537 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME: {
538 DCHECK_EQ(content::Source<WebContents>(source).ptr(), web_contents());
539 if (content::Source<WebContents>(source).ptr() == web_contents())
540 LoadHandlerCalled();
541 break;
543 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: {
544 DCHECK_EQ(content::Source<WebContents>(source).ptr(), web_contents());
545 content::ResourceRedirectDetails* resource_redirect_details =
546 content::Details<content::ResourceRedirectDetails>(details).ptr();
547 bool is_top_level = resource_redirect_details->resource_type ==
548 content::RESOURCE_TYPE_MAIN_FRAME;
549 LoadRedirect(resource_redirect_details->url,
550 resource_redirect_details->new_url,
551 is_top_level);
552 break;
554 default:
555 NOTREACHED() << "Unexpected notification sent.";
556 break;
560 void WebViewGuest::StartFindInternal(
561 const base::string16& search_text,
562 const blink::WebFindOptions& options,
563 scoped_refptr<WebViewInternalFindFunction> find_function) {
564 find_helper_.Find(web_contents(), search_text, options, find_function);
567 void WebViewGuest::StopFindingInternal(content::StopFindAction action) {
568 find_helper_.CancelAllFindSessions();
569 web_contents()->StopFinding(action);
572 bool WebViewGuest::Go(int relative_index) {
573 content::NavigationController& controller = web_contents()->GetController();
574 if (!controller.CanGoToOffset(relative_index))
575 return false;
577 controller.GoToOffset(relative_index);
578 return true;
581 void WebViewGuest::Reload() {
582 // TODO(fsamuel): Don't check for repost because we don't want to show
583 // Chromium's repost warning. We might want to implement a separate API
584 // for registering a callback if a repost is about to happen.
585 web_contents()->GetController().Reload(false);
588 void WebViewGuest::SetUserAgentOverride(
589 const std::string& user_agent_override) {
590 is_overriding_user_agent_ = !user_agent_override.empty();
591 if (is_overriding_user_agent_) {
592 content::RecordAction(UserMetricsAction("WebView.Guest.OverrideUA"));
594 web_contents()->SetUserAgentOverride(user_agent_override);
597 void WebViewGuest::Stop() {
598 web_contents()->Stop();
601 void WebViewGuest::Terminate() {
602 content::RecordAction(UserMetricsAction("WebView.Guest.Terminate"));
603 base::ProcessHandle process_handle =
604 web_contents()->GetRenderProcessHost()->GetHandle();
605 if (process_handle)
606 web_contents()->GetRenderProcessHost()->Shutdown(
607 content::RESULT_CODE_KILLED, false);
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 web_contents()->GetBrowserContext(),
617 web_contents()->GetSiteInstance());
619 if (!partition)
620 return false;
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;
633 WebViewGuest::WebViewGuest(content::WebContents* owner_web_contents)
634 : GuestView<WebViewGuest>(owner_web_contents),
635 rules_registry_id_(RulesRegistryService::kInvalidRulesRegistryID),
636 find_helper_(this),
637 is_overriding_user_agent_(false),
638 guest_opaque_(true),
639 javascript_dialog_helper_(this),
640 current_zoom_factor_(1.0),
641 weak_ptr_factory_(this) {
642 web_view_guest_delegate_.reset(
643 ExtensionsAPIClient::Get()->CreateWebViewGuestDelegate(this));
646 WebViewGuest::~WebViewGuest() {
649 void WebViewGuest::DidCommitProvisionalLoadForFrame(
650 content::RenderFrameHost* render_frame_host,
651 const GURL& url,
652 ui::PageTransition transition_type) {
653 if (!render_frame_host->GetParent())
654 src_ = url;
655 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
656 args->SetString(guestview::kUrl, url.spec());
657 args->SetBoolean(guestview::kIsTopLevel, !render_frame_host->GetParent());
658 args->SetString(webview::kInternalBaseURLForDataURL,
659 web_contents()
660 ->GetController()
661 .GetLastCommittedEntry()
662 ->GetBaseURLForDataURL()
663 .spec());
664 args->SetInteger(webview::kInternalCurrentEntryIndex,
665 web_contents()->GetController().GetCurrentEntryIndex());
666 args->SetInteger(webview::kInternalEntryCount,
667 web_contents()->GetController().GetEntryCount());
668 args->SetInteger(webview::kInternalProcessId,
669 web_contents()->GetRenderProcessHost()->GetID());
670 DispatchEventToView(
671 new GuestViewBase::Event(webview::kEventLoadCommit, args.Pass()));
673 find_helper_.CancelAllFindSessions();
675 // Update the current zoom factor for the new page.
676 ui_zoom::ZoomController* zoom_controller =
677 ui_zoom::ZoomController::FromWebContents(web_contents());
678 DCHECK(zoom_controller);
679 current_zoom_factor_ =
680 content::ZoomLevelToZoomFactor(zoom_controller->GetZoomLevel());
682 if (web_view_guest_delegate_) {
683 web_view_guest_delegate_->OnDidCommitProvisionalLoadForFrame(
684 !render_frame_host->GetParent());
688 void WebViewGuest::DidFailProvisionalLoad(
689 content::RenderFrameHost* render_frame_host,
690 const GURL& validated_url,
691 int error_code,
692 const base::string16& error_description) {
693 LoadAbort(!render_frame_host->GetParent(), validated_url,
694 net::ErrorToShortString(error_code));
697 void WebViewGuest::DidStartProvisionalLoadForFrame(
698 content::RenderFrameHost* render_frame_host,
699 const GURL& validated_url,
700 bool is_error_page,
701 bool is_iframe_srcdoc) {
702 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
703 args->SetString(guestview::kUrl, validated_url.spec());
704 args->SetBoolean(guestview::kIsTopLevel, !render_frame_host->GetParent());
705 DispatchEventToView(
706 new GuestViewBase::Event(webview::kEventLoadStart, args.Pass()));
709 void WebViewGuest::DocumentLoadedInFrame(
710 content::RenderFrameHost* render_frame_host) {
711 if (web_view_guest_delegate_)
712 web_view_guest_delegate_->OnDocumentLoadedInFrame(render_frame_host);
715 bool WebViewGuest::OnMessageReceived(const IPC::Message& message,
716 RenderFrameHost* render_frame_host) {
717 bool handled = true;
718 IPC_BEGIN_MESSAGE_MAP(WebViewGuest, message)
719 IPC_MESSAGE_HANDLER(ExtensionHostMsg_FrameNameChanged, OnFrameNameChanged)
720 IPC_MESSAGE_UNHANDLED(handled = false)
721 IPC_END_MESSAGE_MAP()
722 return handled;
725 void WebViewGuest::RenderProcessGone(base::TerminationStatus status) {
726 // Cancel all find sessions in progress.
727 find_helper_.CancelAllFindSessions();
729 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
730 args->SetInteger(webview::kProcessId,
731 web_contents()->GetRenderProcessHost()->GetID());
732 args->SetString(webview::kReason, TerminationStatusToString(status));
733 DispatchEventToView(
734 new GuestViewBase::Event(webview::kEventExit, args.Pass()));
737 void WebViewGuest::UserAgentOverrideSet(const std::string& user_agent) {
738 content::NavigationController& controller = web_contents()->GetController();
739 content::NavigationEntry* entry = controller.GetVisibleEntry();
740 if (!entry)
741 return;
742 entry->SetIsOverridingUserAgent(!user_agent.empty());
743 web_contents()->GetController().Reload(false);
746 void WebViewGuest::ReportFrameNameChange(const std::string& name) {
747 name_ = name;
748 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
749 args->SetString(webview::kName, name);
750 DispatchEventToView(
751 new GuestViewBase::Event(webview::kEventFrameNameChanged, args.Pass()));
754 void WebViewGuest::LoadHandlerCalled() {
755 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
756 DispatchEventToView(
757 new GuestViewBase::Event(webview::kEventContentLoad, args.Pass()));
760 void WebViewGuest::LoadRedirect(const GURL& old_url,
761 const GURL& new_url,
762 bool is_top_level) {
763 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
764 args->SetBoolean(guestview::kIsTopLevel, is_top_level);
765 args->SetString(webview::kNewURL, new_url.spec());
766 args->SetString(webview::kOldURL, old_url.spec());
767 DispatchEventToView(
768 new GuestViewBase::Event(webview::kEventLoadRedirect, args.Pass()));
771 void WebViewGuest::PushWebViewStateToIOThread() {
772 const GURL& site_url = web_contents()->GetSiteInstance()->GetSiteURL();
773 std::string partition_domain;
774 std::string partition_id;
775 bool in_memory;
776 if (!GetGuestPartitionConfigForSite(
777 site_url, &partition_domain, &partition_id, &in_memory)) {
778 NOTREACHED();
779 return;
782 WebViewRendererState::WebViewInfo web_view_info;
783 web_view_info.embedder_process_id =
784 owner_web_contents()->GetRenderProcessHost()->GetID();
785 web_view_info.instance_id = view_instance_id();
786 web_view_info.partition_id = partition_id;
787 web_view_info.owner_extension_id = owner_extension_id();
788 web_view_info.rules_registry_id = rules_registry_id_;
790 content::BrowserThread::PostTask(
791 content::BrowserThread::IO,
792 FROM_HERE,
793 base::Bind(&WebViewRendererState::AddGuest,
794 base::Unretained(WebViewRendererState::GetInstance()),
795 web_contents()->GetRenderProcessHost()->GetID(),
796 web_contents()->GetRoutingID(),
797 web_view_info));
800 // static
801 void WebViewGuest::RemoveWebViewStateFromIOThread(
802 WebContents* web_contents) {
803 content::BrowserThread::PostTask(
804 content::BrowserThread::IO, FROM_HERE,
805 base::Bind(
806 &WebViewRendererState::RemoveGuest,
807 base::Unretained(WebViewRendererState::GetInstance()),
808 web_contents->GetRenderProcessHost()->GetID(),
809 web_contents->GetRoutingID()));
812 void WebViewGuest::RequestMediaAccessPermission(
813 content::WebContents* source,
814 const content::MediaStreamRequest& request,
815 const content::MediaResponseCallback& callback) {
816 web_view_permission_helper_->RequestMediaAccessPermission(source,
817 request,
818 callback);
821 bool WebViewGuest::CheckMediaAccessPermission(content::WebContents* source,
822 const GURL& security_origin,
823 content::MediaStreamType type) {
824 return web_view_permission_helper_->CheckMediaAccessPermission(
825 source, security_origin, type);
828 void WebViewGuest::CanDownload(
829 content::RenderViewHost* render_view_host,
830 const GURL& url,
831 const std::string& request_method,
832 const base::Callback<void(bool)>& callback) {
833 web_view_permission_helper_->CanDownload(render_view_host,
834 url,
835 request_method,
836 callback);
839 void WebViewGuest::RequestPointerLockPermission(
840 bool user_gesture,
841 bool last_unlocked_by_target,
842 const base::Callback<void(bool)>& callback) {
843 web_view_permission_helper_->RequestPointerLockPermission(
844 user_gesture,
845 last_unlocked_by_target,
846 callback);
849 void WebViewGuest::WillAttachToEmbedder() {
850 rules_registry_id_ = GetOrGenerateRulesRegistryID(
851 owner_web_contents()->GetRenderProcessHost()->GetID(),
852 view_instance_id());
854 // We must install the mapping from guests to WebViews prior to resuming
855 // suspended resource loads so that the WebRequest API will catch resource
856 // requests.
857 PushWebViewStateToIOThread();
860 content::JavaScriptDialogManager* WebViewGuest::GetJavaScriptDialogManager(
861 WebContents* source) {
862 return &javascript_dialog_helper_;
865 void WebViewGuest::NavigateGuest(const std::string& src,
866 bool force_navigation) {
867 if (src.empty())
868 return;
870 GURL url = ResolveURL(src);
872 // Do not allow navigating a guest to schemes other than known safe schemes.
873 // This will block the embedder trying to load unwanted schemes, e.g.
874 // chrome://settings.
875 bool scheme_is_blocked =
876 (!content::ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
877 url.scheme()) &&
878 !url.SchemeIs(url::kAboutScheme)) ||
879 url.SchemeIs(url::kJavaScriptScheme);
880 if (scheme_is_blocked || !url.is_valid()) {
881 LoadAbort(true /* is_top_level */, url,
882 net::ErrorToShortString(net::ERR_ABORTED));
883 NavigateGuest(url::kAboutBlankURL, true /* force_navigation */);
884 return;
886 if (!force_navigation && (src_ == url))
887 return;
889 GURL validated_url(url);
890 web_contents()->GetRenderProcessHost()->FilterURL(false, &validated_url);
891 // As guests do not swap processes on navigation, only navigations to
892 // normal web URLs are supported. No protocol handlers are installed for
893 // other schemes (e.g., WebUI or extensions), and no permissions or bindings
894 // can be granted to the guest process.
895 LoadURLWithParams(validated_url,
896 content::Referrer(),
897 ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
898 web_contents());
900 src_ = validated_url;
903 bool WebViewGuest::HandleKeyboardShortcuts(
904 const content::NativeWebKeyboardEvent& event) {
905 // <webview> outside of Chrome Apps do not handle keyboard shortcuts.
906 if (!in_extension())
907 return false;
909 if (event.type != blink::WebInputEvent::RawKeyDown)
910 return false;
912 // If the user hits the escape key without any modifiers then unlock the
913 // mouse if necessary.
914 if ((event.windowsKeyCode == ui::VKEY_ESCAPE) &&
915 !(event.modifiers & blink::WebInputEvent::InputModifiers)) {
916 return web_contents()->GotResponseToLockMouseRequest(false);
919 #if defined(OS_MACOSX)
920 if (event.modifiers != blink::WebInputEvent::MetaKey)
921 return false;
923 if (event.windowsKeyCode == ui::VKEY_OEM_4) {
924 Go(-1);
925 return true;
928 if (event.windowsKeyCode == ui::VKEY_OEM_6) {
929 Go(1);
930 return true;
932 #else
933 if (event.windowsKeyCode == ui::VKEY_BROWSER_BACK) {
934 Go(-1);
935 return true;
938 if (event.windowsKeyCode == ui::VKEY_BROWSER_FORWARD) {
939 Go(1);
940 return true;
942 #endif
944 return false;
947 void WebViewGuest::ApplyAttributes(const base::DictionaryValue& params) {
948 std::string name;
949 if (params.GetString(webview::kAttributeName, &name)) {
950 // If the guest window's name is empty, then the WebView tag's name is
951 // assigned. Otherwise, the guest window's name takes precedence over the
952 // WebView tag's name.
953 if (name_.empty())
954 SetName(name);
956 if (attached())
957 ReportFrameNameChange(name_);
959 std::string user_agent_override;
960 params.GetString(webview::kParameterUserAgentOverride, &user_agent_override);
961 SetUserAgentOverride(user_agent_override);
963 bool allow_transparency = false;
964 params.GetBoolean(webview::kAttributeAllowTransparency, &allow_transparency);
965 // We need to set the background opaque flag after navigation to ensure that
966 // there is a RenderWidgetHostView available.
967 SetAllowTransparency(allow_transparency);
969 bool is_pending_new_window = false;
970 if (GetOpener()) {
971 // We need to do a navigation here if the target URL has changed between
972 // the time the WebContents was created and the time it was attached.
973 // We also need to do an initial navigation if a RenderView was never
974 // created for the new window in cases where there is no referrer.
975 auto it = GetOpener()->pending_new_windows_.find(this);
976 if (it != GetOpener()->pending_new_windows_.end()) {
977 const NewWindowInfo& new_window_info = it->second;
978 if (new_window_info.changed || !web_contents()->HasOpener())
979 NavigateGuest(new_window_info.url.spec(), false /* force_navigation */);
981 // Once a new guest is attached to the DOM of the embedder page, then the
982 // lifetime of the new guest is no longer managed by the opener guest.
983 GetOpener()->pending_new_windows_.erase(this);
985 is_pending_new_window = true;
989 // Only read the src attribute if this is not a New Window API flow.
990 if (!is_pending_new_window) {
991 std::string src;
992 params.GetString(webview::kAttributeSrc, &src);
993 NavigateGuest(src, false /* force_navigation */);
998 void WebViewGuest::ShowContextMenu(
999 int request_id,
1000 const WebViewGuestDelegate::MenuItemVector* items) {
1001 if (web_view_guest_delegate_)
1002 web_view_guest_delegate_->OnShowContextMenu(request_id, items);
1005 void WebViewGuest::SetName(const std::string& name) {
1006 if (name_ == name)
1007 return;
1008 name_ = name;
1010 Send(new ExtensionMsg_SetFrameName(routing_id(), name_));
1013 void WebViewGuest::SetZoom(double zoom_factor) {
1014 auto zoom_controller =
1015 ui_zoom::ZoomController::FromWebContents(web_contents());
1016 DCHECK(zoom_controller);
1017 double zoom_level = content::ZoomFactorToZoomLevel(zoom_factor);
1018 zoom_controller->SetZoomLevel(zoom_level);
1020 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
1021 args->SetDouble(webview::kOldZoomFactor, current_zoom_factor_);
1022 args->SetDouble(webview::kNewZoomFactor, zoom_factor);
1023 DispatchEventToView(
1024 new GuestViewBase::Event(webview::kEventZoomChange, args.Pass()));
1025 current_zoom_factor_ = zoom_factor;
1028 void WebViewGuest::SetAllowTransparency(bool allow) {
1029 if (guest_opaque_ != allow)
1030 return;
1032 guest_opaque_ = !allow;
1033 if (!web_contents()->GetRenderViewHost()->GetView())
1034 return;
1036 if (guest_opaque_) {
1037 web_contents()
1038 ->GetRenderViewHost()
1039 ->GetView()
1040 ->SetBackgroundColorToDefault();
1041 } else {
1042 web_contents()->GetRenderViewHost()->GetView()->SetBackgroundColor(
1043 SK_ColorTRANSPARENT);
1047 bool WebViewGuest::LoadDataWithBaseURL(const std::string& data_url,
1048 const std::string& base_url,
1049 const std::string& virtual_url,
1050 std::string* error) {
1051 // Make GURLs from URLs.
1052 const GURL data_gurl = GURL(data_url);
1053 const GURL base_gurl = GURL(base_url);
1054 const GURL virtual_gurl = GURL(virtual_url);
1056 // Check that the provided URLs are valid.
1057 // |data_url| must be a valid data URL.
1058 if (!data_gurl.is_valid() || !data_gurl.SchemeIs(url::kDataScheme)) {
1059 base::SStringPrintf(
1060 error, webview::kAPILoadDataInvalidDataURL, data_url.c_str());
1061 return false;
1063 // |base_url| must be a valid URL.
1064 if (!base_gurl.is_valid()) {
1065 base::SStringPrintf(
1066 error, webview::kAPILoadDataInvalidBaseURL, base_url.c_str());
1067 return false;
1069 // |virtual_url| must be a valid URL.
1070 if (!virtual_gurl.is_valid()) {
1071 base::SStringPrintf(
1072 error, webview::kAPILoadDataInvalidVirtualURL, virtual_url.c_str());
1073 return false;
1076 // Set up the parameters to load |data_url| with the specified |base_url|.
1077 content::NavigationController::LoadURLParams load_params(data_gurl);
1078 load_params.load_type = content::NavigationController::LOAD_TYPE_DATA;
1079 load_params.base_url_for_data_url = base_gurl;
1080 load_params.virtual_url_for_data_url = virtual_gurl;
1081 load_params.override_user_agent =
1082 content::NavigationController::UA_OVERRIDE_INHERIT;
1084 // Navigate to the data URL.
1085 web_contents()->GetController().LoadURLWithParams(load_params);
1087 return true;
1090 void WebViewGuest::AddNewContents(content::WebContents* source,
1091 content::WebContents* new_contents,
1092 WindowOpenDisposition disposition,
1093 const gfx::Rect& initial_rect,
1094 bool user_gesture,
1095 bool* was_blocked) {
1096 if (was_blocked)
1097 *was_blocked = false;
1098 RequestNewWindowPermission(disposition,
1099 initial_rect,
1100 user_gesture,
1101 new_contents);
1104 content::WebContents* WebViewGuest::OpenURLFromTab(
1105 content::WebContents* source,
1106 const content::OpenURLParams& params) {
1107 // If the guest wishes to navigate away prior to attachment then we save the
1108 // navigation to perform upon attachment. Navigation initializes a lot of
1109 // state that assumes an embedder exists, such as RenderWidgetHostViewGuest.
1110 // Navigation also resumes resource loading which we don't want to allow
1111 // until attachment.
1112 if (!attached()) {
1113 WebViewGuest* opener = GetOpener();
1114 auto it = opener->pending_new_windows_.find(this);
1115 if (it == opener->pending_new_windows_.end())
1116 return nullptr;
1117 const NewWindowInfo& info = it->second;
1118 NewWindowInfo new_window_info(params.url, info.name);
1119 new_window_info.changed = new_window_info.url != info.url;
1120 it->second = new_window_info;
1121 return nullptr;
1123 if (params.disposition == CURRENT_TAB) {
1124 // This can happen for cross-site redirects.
1125 LoadURLWithParams(params.url, params.referrer, params.transition, source);
1126 return source;
1129 CreateNewGuestWebViewWindow(params);
1130 return nullptr;
1133 void WebViewGuest::WebContentsCreated(WebContents* source_contents,
1134 int opener_render_frame_id,
1135 const base::string16& frame_name,
1136 const GURL& target_url,
1137 content::WebContents* new_contents) {
1138 auto guest = WebViewGuest::FromWebContents(new_contents);
1139 CHECK(guest);
1140 guest->SetOpener(this);
1141 std::string guest_name = base::UTF16ToUTF8(frame_name);
1142 guest->name_ = guest_name;
1143 pending_new_windows_.insert(
1144 std::make_pair(guest, NewWindowInfo(target_url, guest_name)));
1147 void WebViewGuest::LoadURLWithParams(const GURL& url,
1148 const content::Referrer& referrer,
1149 ui::PageTransition transition_type,
1150 content::WebContents* web_contents) {
1151 content::NavigationController::LoadURLParams load_url_params(url);
1152 load_url_params.referrer = referrer;
1153 load_url_params.transition_type = transition_type;
1154 load_url_params.extra_headers = std::string();
1155 if (is_overriding_user_agent_) {
1156 load_url_params.override_user_agent =
1157 content::NavigationController::UA_OVERRIDE_TRUE;
1159 web_contents->GetController().LoadURLWithParams(load_url_params);
1162 void WebViewGuest::RequestNewWindowPermission(
1163 WindowOpenDisposition disposition,
1164 const gfx::Rect& initial_bounds,
1165 bool user_gesture,
1166 content::WebContents* new_contents) {
1167 auto guest = WebViewGuest::FromWebContents(new_contents);
1168 if (!guest)
1169 return;
1170 auto it = pending_new_windows_.find(guest);
1171 if (it == pending_new_windows_.end())
1172 return;
1173 const NewWindowInfo& new_window_info = it->second;
1175 // Retrieve the opener partition info if we have it.
1176 const GURL& site_url = new_contents->GetSiteInstance()->GetSiteURL();
1177 std::string storage_partition_id = GetStoragePartitionIdFromSiteURL(site_url);
1179 base::DictionaryValue request_info;
1180 request_info.SetInteger(webview::kInitialHeight, initial_bounds.height());
1181 request_info.SetInteger(webview::kInitialWidth, initial_bounds.width());
1182 request_info.Set(webview::kTargetURL,
1183 new base::StringValue(new_window_info.url.spec()));
1184 request_info.Set(webview::kName, new base::StringValue(new_window_info.name));
1185 request_info.SetInteger(webview::kWindowID, guest->guest_instance_id());
1186 // We pass in partition info so that window-s created through newwindow
1187 // API can use it to set their partition attribute.
1188 request_info.Set(webview::kStoragePartitionId,
1189 new base::StringValue(storage_partition_id));
1190 request_info.Set(
1191 webview::kWindowOpenDisposition,
1192 new base::StringValue(WindowOpenDispositionToString(disposition)));
1194 web_view_permission_helper_->
1195 RequestPermission(WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW,
1196 request_info,
1197 base::Bind(&WebViewGuest::OnWebViewNewWindowResponse,
1198 weak_ptr_factory_.GetWeakPtr(),
1199 guest->guest_instance_id()),
1200 false /* allowed_by_default */);
1203 GURL WebViewGuest::ResolveURL(const std::string& src) {
1204 if (!in_extension())
1205 return GURL(src);
1207 GURL default_url(base::StringPrintf("%s://%s/",
1208 kExtensionScheme,
1209 owner_extension_id().c_str()));
1210 return default_url.Resolve(src);
1213 void WebViewGuest::OnWebViewNewWindowResponse(
1214 int new_window_instance_id,
1215 bool allow,
1216 const std::string& user_input) {
1217 auto guest =
1218 WebViewGuest::From(owner_web_contents()->GetRenderProcessHost()->GetID(),
1219 new_window_instance_id);
1220 if (!guest)
1221 return;
1223 if (!allow)
1224 guest->Destroy();
1227 } // namespace extensions