diff options
author | fsamuel <fsamuel@chromium.org> | 2014-10-01 17:51:33 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-10-02 00:51:45 +0000 |
commit | a8484dd476ef402fc235a3e9c19b1d52ac0d0f6e (patch) | |
tree | e426936de34aa3c4a7c0232fd0c296066dab1138 | |
parent | 7f79ce28848eb40d4ec6d2d335e2996ff02362e5 (diff) | |
download | chromium_src-a8484dd476ef402fc235a3e9c19b1d52ac0d0f6e.zip chromium_src-a8484dd476ef402fc235a3e9c19b1d52ac0d0f6e.tar.gz chromium_src-a8484dd476ef402fc235a3e9c19b1d52ac0d0f6e.tar.bz2 |
GuestView: Move lifetime management out of content
This CL introduces an internal destroyGuest API, and enables preservation of
guests when display:none is set.
This patch is also a big step towards guest teleportation.
BUG=419020, 330264
TBR=kenrb@chromium.org for browser_plugin_messages.h IPC removal, asvitkine@chromium.org for histograms.xml
Review URL: https://codereview.chromium.org/618823002
Cr-Commit-Position: refs/heads/master@{#297751}
33 files changed, 313 insertions, 282 deletions
diff --git a/chrome/browser/apps/web_view_browsertest.cc b/chrome/browser/apps/web_view_browsertest.cc index 0a39ca9..491a90b 100644 --- a/chrome/browser/apps/web_view_browsertest.cc +++ b/chrome/browser/apps/web_view_browsertest.cc @@ -819,33 +819,6 @@ IN_PROC_BROWSER_TEST_F(WebViewTest, AutoSize) { << message_; } -// Tests that a <webview> that is set to "display: none" after load and then -// setting "display: block" re-renders the plugin properly. -// -// Initially after loading the <webview> and the test sets <webview> to -// "display: none". -// This causes the browser plugin to be destroyed, we then set the -// style.display of the <webview> to block again and check that loadstop -// fires properly. -IN_PROC_BROWSER_TEST_F(WebViewTest, DisplayNoneAndBack) { - LoadAppWithGuest("web_view/display_none_and_back"); - - scoped_refptr<content::MessageLoopRunner> loop_runner( - new content::MessageLoopRunner); - WebContentsHiddenObserver observer(GetGuestWebContents(), - loop_runner->QuitClosure()); - - // Handled in platform_apps/web_view/display_none_and_back/main.js - SendMessageToEmbedder("hide-guest"); - GetGuestViewManager()->WaitForGuestDeleted(); - ExtensionTestMessageListener test_passed_listener("WebViewTest.PASSED", - false); - - SendMessageToEmbedder("show-guest"); - GetGuestViewManager()->WaitForGuestCreated(); - EXPECT_TRUE(test_passed_listener.WaitUntilSatisfied()); -} - // http://crbug.com/326332 IN_PROC_BROWSER_TEST_F(WebViewTest, DISABLED_Shim_TestAutosizeAfterNavigation) { TestHelper("testAutosizeAfterNavigation", "web_view/shim", NO_TEST_SERVER); diff --git a/chrome/renderer/resources/extensions/app_view.js b/chrome/renderer/resources/extensions/app_view.js index 968340b..0b16e65 100644 --- a/chrome/renderer/resources/extensions/app_view.js +++ b/chrome/renderer/resources/extensions/app_view.js @@ -11,6 +11,8 @@ var guestViewInternalNatives = requireNative('guest_view_internal'); function AppViewInternal(appviewNode) { privates(appviewNode).internal = this; this.appviewNode = appviewNode; + this.elementAttached = false; + this.pendingGuestCreation = false; this.browserPluginNode = this.createBrowserPluginNode(); var shadowRoot = this.appviewNode.createShadowRoot(); @@ -41,15 +43,25 @@ AppViewInternal.prototype.createBrowserPluginNode = function() { }; AppViewInternal.prototype.connect = function(app, data, callback) { + if (!this.elementAttached || this.pendingGuestCreation) { + if (callback) { + callback(false); + } + return; + } var createParams = { 'appId': app, 'data': data || {} }; - var self = this; GuestViewInternal.createGuest( 'appview', createParams, function(guestInstanceId) { + this.pendingGuestCreation = false; + if (guestInstanceId && !this.elementAttached) { + GuestViewInternal.destroyGuest(guestInstanceId); + guestInstanceId = 0; + } if (!guestInstanceId) { this.browserPluginNode.style.visibility = 'hidden'; var errorMsg = 'Unable to connect to app "' + app + '".'; @@ -66,20 +78,50 @@ AppViewInternal.prototype.connect = function(app, data, callback) { } }.bind(this) ); + this.pendingGuestCreation = true; }; AppViewInternal.prototype.attachWindow = function(guestInstanceId) { this.guestInstanceId = guestInstanceId; + if (!this.internalInstanceId) { + return; + } var params = { - 'instanceId': this.viewInstanceId, + 'instanceId': this.viewInstanceId }; this.browserPluginNode.style.visibility = 'visible'; return guestViewInternalNatives.AttachGuest( - parseInt(this.browserPluginNode.getAttribute('internalinstanceid')), + this.internalInstanceId, guestInstanceId, params); }; +AppViewInternal.prototype.handleBrowserPluginAttributeMutation = + function(name, oldValue, newValue) { + if (name == 'internalinstanceid' && !oldValue && !!newValue) { + this.browserPluginNode.removeAttribute('internalinstanceid'); + this.internalInstanceId = parseInt(newValue); + + if (!!this.guestInstanceId && this.guestInstanceId != 0) { + var params = { + 'instanceId': this.viewInstanceId + }; + guestViewInternalNatives.AttachGuest( + this.internalInstanceId, + this.guestInstanceId, + params); + } + return; + } +}; + +AppViewInternal.prototype.reset = function() { + if (this.guestInstanceId) { + GuestViewInternal.destroyGuest(this.guestInstanceId); + this.guestInstanceId = undefined; + } +}; + function registerBrowserPluginElement() { var proto = Object.create(HTMLObjectElement.prototype); @@ -94,6 +136,14 @@ function registerBrowserPluginElement() { var unused = this.nonExistentAttribute; }; + proto.attributeChangedCallback = function(name, oldValue, newValue) { + var internal = privates(this).internal; + if (!internal) { + return; + } + internal.handleBrowserPluginAttributeMutation(name, oldValue, newValue); + }; + AppViewInternal.BrowserPlugin = DocumentNatives.RegisterElement('appplugin', {extends: 'object', prototype: proto}); @@ -111,10 +161,28 @@ function registerAppViewElement() { new AppViewInternal(this); }; + proto.attachedCallback = function() { + var internal = privates(this).internal; + if (!internal) { + return; + } + internal.elementAttached = true; + }; + + proto.detachedCallback = function() { + var internal = privates(this).internal; + if (!internal) { + return; + } + internal.elementAttached = false; + internal.reset(); + }; + proto.connect = function() { var internal = privates(this).internal; $Function.apply(internal.connect, internal, arguments); } + window.AppView = DocumentNatives.RegisterElement('appview', {prototype: proto}); diff --git a/chrome/renderer/resources/extensions/extension_options.js b/chrome/renderer/resources/extensions/extension_options.js index 32bee63..38ca845 100644 --- a/chrome/renderer/resources/extensions/extension_options.js +++ b/chrome/renderer/resources/extensions/extension_options.js @@ -24,6 +24,9 @@ function ExtensionOptionsInternal(extensionoptionsNode) { privates(extensionoptionsNode).internal = this; this.extensionoptionsNode = extensionoptionsNode; this.viewInstanceId = IdGenerator.GetNextId(); + this.guestInstanceId = 0; + this.elementAttached = false; + this.pendingGuestCreation = false; this.autosizeDeferred = false; @@ -68,7 +71,14 @@ ExtensionOptionsInternal.prototype.createBrowserPluginNode = function() { return browserPluginNode; }; -ExtensionOptionsInternal.prototype.createGuest = function() { +ExtensionOptionsInternal.prototype.createGuestIfNecessary = function() { + if (!this.elementAttached || this.pendingGuestCreation) { + return; + } + if (this.guestInstanceId != 0) { + this.attachWindow(); + return; + } var params = { 'extensionId': this.extensionId, }; @@ -76,6 +86,11 @@ ExtensionOptionsInternal.prototype.createGuest = function() { 'extensionoptions', params, function(guestInstanceId) { + this.pendingGuestCreation = false; + if (guestInstanceId && !this.elementAttached) { + GuestViewInternal.destroyGuest(guestInstanceId); + guestInstanceId = 0; + } if (guestInstanceId == 0) { // Fire a createfailed event here rather than in ExtensionOptionsGuest // because the guest will not be created, and cannot fire an event. @@ -86,7 +101,9 @@ ExtensionOptionsInternal.prototype.createGuest = function() { this.guestInstanceId = guestInstanceId; this.attachWindow(); } - }.bind(this)); + }.bind(this) + ); + this.pendingGuestCreation = true; }; ExtensionOptionsInternal.prototype.dispatchEvent = @@ -113,7 +130,7 @@ ExtensionOptionsInternal.prototype.handleExtensionOptionsAttributeMutation = // If a guest view does not exist then create one. if (!this.guestInstanceId) { - this.createGuest(); + this.createGuestIfNecessary(); return; } // TODO(ericzeng): Implement navigation to another guest view if we want @@ -142,10 +159,11 @@ ExtensionOptionsInternal.prototype.handleExtensionOptionsAttributeMutation = ExtensionOptionsInternal.prototype.handleBrowserPluginAttributeMutation = function(name, oldValue, newValue) { if (name == 'internalinstanceid' && !oldValue && !!newValue) { + this.elementAttached = true; this.internalInstanceId = parseInt(newValue); this.browserPluginNode.removeAttribute('internalinstanceid'); if (this.extensionId) - this.createGuest(); + this.createGuestIfNecessary(); } }; @@ -293,6 +311,13 @@ ExtensionOptionsInternal.prototype.resumeDeferredAutoSize = function() { } }; +ExtensionOptionsInternal.prototype.reset = function() { + if (this.guestInstanceId) { + GuestViewInternal.destroyGuest(this.guestInstanceId); + this.guestInstanceId = undefined; + } +}; + function registerBrowserPluginElement() { var proto = Object.create(HTMLObjectElement.prototype); @@ -331,10 +356,20 @@ function registerExtensionOptionsElement() { new ExtensionOptionsInternal(this); }; + proto.detachedCallback = function() { + var internal = privates(this).internal; + if (!internal) { + return; + } + internal.elementAttached = false; + internal.reset(); + }; + proto.attributeChangedCallback = function(name, oldValue, newValue) { var internal = privates(this).internal; - if (!internal) + if (!internal) { return; + } internal.handleExtensionOptionsAttributeMutation(name, oldValue, newValue); }; diff --git a/chrome/test/data/extensions/platform_apps/app_view/shim/main.js b/chrome/test/data/extensions/platform_apps/app_view/shim/main.js index f305dca..fe3c2eb 100644 --- a/chrome/test/data/extensions/platform_apps/app_view/shim/main.js +++ b/chrome/test/data/extensions/platform_apps/app_view/shim/main.js @@ -71,11 +71,13 @@ embedder.test.assertFalse = function(condition) { function testAppViewWithUndefinedDataShouldSucceed(appToEmbed) { var appview = new AppView(); LOG('appToEmbed ' + appToEmbed); + document.body.appendChild(appview); // Step 1: Attempt to connect to a non-existant app. LOG('attempting to connect to non-existant app.'); appview.connect('abc123', undefined, function(success) { // Make sure we fail. if (success) { + LOG('UNEXPECTED CONNECTION.'); embedder.test.fail(); return; } @@ -85,44 +87,49 @@ function testAppViewWithUndefinedDataShouldSucceed(appToEmbed) { appview.connect(appToEmbed, undefined, function(success) { // Make sure we don't fail. if (!success) { + LOG('FAILED TO CONNECT.'); embedder.test.fail(); return; } + LOG('CONNECTED.'); embedder.test.succeed(); }); }); - document.body.appendChild(appview); }; function testAppViewRefusedDataShouldFail(appToEmbed) { var appview = new AppView(); LOG('appToEmbed ' + appToEmbed); + document.body.appendChild(appview); LOG('Attempting to connect to app with refused params.'); appview.connect(appToEmbed, { 'foo': 'bar' }, function(success) { // Make sure we fail. if (success) { + LOG('UNEXPECTED CONNECTION.'); embedder.test.fail(); return; } + LOG('FAILED TO CONNECT.'); embedder.test.succeed(); }); - document.body.appendChild(appview); }; function testAppViewGoodDataShouldSucceed(appToEmbed) { var appview = new AppView(); LOG('appToEmbed ' + appToEmbed); + document.body.appendChild(appview); LOG('Attempting to connect to app with good params.'); // Step 2: Attempt to connect to an app with good params. appview.connect(appToEmbed, { 'foo': 'bleep' }, function(success) { // Make sure we don't fail. if (!success) { + LOG('FAILED TO CONNECT.'); embedder.test.fail(); return; } + LOG('CONNECTED.'); embedder.test.succeed(); }); - document.body.appendChild(appview); }; embedder.test.testList = { diff --git a/chrome/test/data/extensions/platform_apps/web_view/display_none_and_back/main.html b/chrome/test/data/extensions/platform_apps/web_view/display_none_and_back/main.html deleted file mode 100644 index d7227a6..0000000 --- a/chrome/test/data/extensions/platform_apps/web_view/display_none_and_back/main.html +++ /dev/null @@ -1,12 +0,0 @@ -<!doctype html> -<!-- - * Copyright 2014 The Chromium Authors. All rights reserved. Use of this - * source code is governed by a BSD-style license that can be found in the - * LICENSE file. ---> -<html> -<body> - <div id="webview-tag-container"></div> - <script src="main.js"></script> -</body> -</html> diff --git a/chrome/test/data/extensions/platform_apps/web_view/display_none_and_back/main.js b/chrome/test/data/extensions/platform_apps/web_view/display_none_and_back/main.js deleted file mode 100644 index 6a54e0f..0000000 --- a/chrome/test/data/extensions/platform_apps/web_view/display_none_and_back/main.js +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -var LOG = function(msg) { - window.console.log(msg); -}; - -var failTest = function() { - chrome.test.sendMessage('WebViewTest.FAILURE'); -}; - -var firstTime = true; - -var startTest = function() { - document.querySelector('#webview-tag-container').innerHTML = - '<webview style="width: 10px; height: 10px; margin: 0; padding: 0;"' + - '></webview>'; - - var webview = document.querySelector('webview'); - var onLoadStop = function(e) { - if (firstTime) { - firstTime = false; - chrome.test.sendMessage('WebViewTest.LAUNCHED'); - } else { - chrome.test.sendMessage('WebViewTest.PASSED'); - } - }; - - webview.addEventListener('loadstop', onLoadStop); - webview.src = 'data:text/html,<body>Guest</body>'; -}; - -window.onAppCommand = function(command) { - LOG('onAppCommand: ' + command); - switch (command) { - case 'hide-guest': - document.querySelector('webview').style.display = 'none'; - break; - case 'show-guest': - document.querySelector('webview').style.display = ''; - break; - default: - failTest(); - break; - } -}; - -window.onload = startTest; diff --git a/chrome/test/data/extensions/platform_apps/web_view/display_none_and_back/manifest.json b/chrome/test/data/extensions/platform_apps/web_view/display_none_and_back/manifest.json deleted file mode 100644 index 134230f..0000000 --- a/chrome/test/data/extensions/platform_apps/web_view/display_none_and_back/manifest.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "<webview> check display: none toggling.", - "version": "1", - "permissions": [ - "webview" - ], - "app": { - "background": { - "scripts": ["test.js"] - } - } -} diff --git a/chrome/test/data/extensions/platform_apps/web_view/display_none_and_back/test.js b/chrome/test/data/extensions/platform_apps/web_view/display_none_and_back/test.js deleted file mode 100644 index a80d274..0000000 --- a/chrome/test/data/extensions/platform_apps/web_view/display_none_and_back/test.js +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -chrome.app.runtime.onLaunched.addListener(function() { - chrome.app.window.create('main.html', {}, function () {}); -}); diff --git a/content/browser/browser_plugin/browser_plugin_guest.cc b/content/browser/browser_plugin/browser_plugin_guest.cc index f2a0f74..1c73384 100644 --- a/content/browser/browser_plugin/browser_plugin_guest.cc +++ b/content/browser/browser_plugin/browser_plugin_guest.cc @@ -87,6 +87,7 @@ BrowserPluginGuest::BrowserPluginGuest(bool has_render_view, last_text_input_type_(ui::TEXT_INPUT_TYPE_NONE), last_input_mode_(ui::TEXT_INPUT_MODE_DEFAULT), last_can_compose_inline_(true), + guest_proxy_routing_id_(MSG_ROUTING_NONE), delegate_(delegate), weak_ptr_factory_(this) { DCHECK(web_contents); @@ -134,10 +135,6 @@ bool BrowserPluginGuest::LockMouse(bool allowed) { return embedder_web_contents()->GotResponseToLockMouseRequest(allowed); } -void BrowserPluginGuest::Destroy() { - delegate_->Destroy(); -} - WebContentsImpl* BrowserPluginGuest::CreateNewGuestWindow( const WebContents::CreateParams& params) { WebContentsImpl* new_contents = @@ -175,7 +172,6 @@ bool BrowserPluginGuest::OnMessageReceivedFromEmbedder( IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_ImeSetComposition, OnImeSetComposition) IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_LockMouse_ACK, OnLockMouseAck) - IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_PluginDestroyed, OnPluginDestroyed) IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_ReclaimCompositorResources, OnReclaimCompositorResources) IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_ResizeGuest, OnResizeGuest) @@ -200,13 +196,15 @@ void BrowserPluginGuest::Initialize( guest_window_rect_ = gfx::Rect(params.origin, params.resize_guest_params.view_size); + WebContentsViewGuest* new_view = + static_cast<WebContentsViewGuest*>(GetWebContents()->GetView()); + if (attached()) + new_view->OnGuestDetached(embedder_web_contents_->GetView()); + // Once a BrowserPluginGuest has an embedder WebContents, it's considered to // be attached. embedder_web_contents_ = embedder_web_contents; - - WebContentsViewGuest* new_view = - static_cast<WebContentsViewGuest*>(GetWebContents()->GetView()); - new_view->OnGuestInitialized(embedder_web_contents->GetView()); + new_view->OnGuestAttached(embedder_web_contents->GetView()); RendererPreferences* renderer_prefs = GetWebContents()->GetMutableRendererPrefs(); @@ -336,7 +334,6 @@ void BrowserPluginGuest::SwapCompositorFrame( guest_params.output_surface_id = output_surface_id; guest_params.producing_route_id = host_routing_id; guest_params.producing_host_id = host_process_id; - SendMessageToEmbedder( new BrowserPluginMsg_CompositorFrameSwapped( browser_plugin_instance_id(), guest_params)); @@ -454,7 +451,6 @@ bool BrowserPluginGuest::ShouldForwardToBrowserPluginGuest( case BrowserPluginHostMsg_ImeConfirmComposition::ID: case BrowserPluginHostMsg_ImeSetComposition::ID: case BrowserPluginHostMsg_LockMouse_ACK::ID: - case BrowserPluginHostMsg_PluginDestroyed::ID: case BrowserPluginHostMsg_ReclaimCompositorResources::ID: case BrowserPluginHostMsg_ResizeGuest::ID: case BrowserPluginHostMsg_SetEditCommandsForNextKeyEvent::ID: @@ -520,20 +516,21 @@ void BrowserPluginGuest::Attach( int browser_plugin_instance_id, WebContentsImpl* embedder_web_contents, const BrowserPluginHostMsg_Attach_Params& params) { - if (attached()) - return; - delegate_->WillAttach(embedder_web_contents, browser_plugin_instance_id); // If a RenderView has already been created for this new window, then we need // to initialize the browser-side state now so that the RenderFrameHostManager // does not create a new RenderView on navigation. if (has_render_view_) { + // This will trigger a callback to RenderViewReady after a round-trip IPC. static_cast<RenderViewHostImpl*>( GetWebContents()->GetRenderViewHost())->Init(); - WebContentsViewGuest* new_view = + WebContentsViewGuest* web_contents_view = static_cast<WebContentsViewGuest*>(GetWebContents()->GetView()); - new_view->CreateViewForWidget(web_contents()->GetRenderViewHost()); + if (!web_contents()->GetRenderViewHost()->GetView()) { + web_contents_view->CreateViewForWidget( + web_contents()->GetRenderViewHost()); + } } Initialize(browser_plugin_instance_id, params, embedder_web_contents); @@ -542,11 +539,18 @@ void BrowserPluginGuest::Attach( // Create a swapped out RenderView for the guest in the embedder render // process, so that the embedder can access the guest's window object. - int guest_routing_id = - GetWebContents()->CreateSwappedOutRenderView( - embedder_web_contents_->GetSiteInstance()); + // On reattachment, we can reuse the same swapped out RenderView because + // the embedder process will always be the same even if the embedder + // WebContents changes. + if (guest_proxy_routing_id_ == MSG_ROUTING_NONE) { + guest_proxy_routing_id_ = + GetWebContents()->CreateSwappedOutRenderView( + embedder_web_contents_->GetSiteInstance()); + } + + delegate_->DidAttach(guest_proxy_routing_id_); - delegate_->DidAttach(guest_routing_id); + has_render_view_ = true; RecordAction(base::UserMetricsAction("BrowserPlugin.Guest.Attached")); } @@ -660,10 +664,6 @@ void BrowserPluginGuest::OnLockMouseAck(int browser_plugin_instance_id, mouse_locked_ = true; } -void BrowserPluginGuest::OnPluginDestroyed(int browser_plugin_instance_id) { - Destroy(); -} - void BrowserPluginGuest::OnResizeGuest( int browser_plugin_instance_id, const BrowserPluginHostMsg_ResizeGuest_Params& params) { diff --git a/content/browser/browser_plugin/browser_plugin_guest.h b/content/browser/browser_plugin/browser_plugin_guest.h index cecca6d..c6b7fb7 100644 --- a/content/browser/browser_plugin/browser_plugin_guest.h +++ b/content/browser/browser_plugin/browser_plugin_guest.h @@ -114,10 +114,6 @@ class CONTENT_EXPORT BrowserPluginGuest : public WebContentsObserver { // Called when the embedder WebContents changes visibility. void EmbedderVisibilityChanged(bool visible); - // Destroys the guest WebContents and all its associated state, including - // this BrowserPluginGuest, and its new unattached windows. - void Destroy(); - // Creates a new guest WebContentsImpl with the provided |params| with |this| // as the |opener|. WebContentsImpl* CreateNewGuestWindow( @@ -253,7 +249,6 @@ class CONTENT_EXPORT BrowserPluginGuest : public WebContentsObserver { bool last_unlocked_by_target, bool privileged); void OnLockMouseAck(int instance_id, bool succeeded); - void OnPluginDestroyed(int instance_id); // Resizes the guest's web contents. void OnResizeGuest( int instance_id, const BrowserPluginHostMsg_ResizeGuest_Params& params); @@ -365,6 +360,10 @@ class CONTENT_EXPORT BrowserPluginGuest : public WebContentsObserver { ui::TextInputMode last_input_mode_; bool last_can_compose_inline_; + // The is the routing ID for a swapped out RenderView for the guest + // WebContents in the embedder's process. + int guest_proxy_routing_id_; + // This is a queue of messages that are destined to be sent to the embedder // once the guest is attached to a particular embedder. std::deque<linked_ptr<IPC::Message> > pending_messages_; diff --git a/content/browser/web_contents/web_contents_view_guest.cc b/content/browser/web_contents/web_contents_view_guest.cc index efa92bd..17a5c484 100644 --- a/content/browser/web_contents/web_contents_view_guest.cc +++ b/content/browser/web_contents/web_contents_view_guest.cc @@ -60,7 +60,7 @@ gfx::NativeWindow WebContentsViewGuest::GetTopLevelNativeWindow() const { return guest_->embedder_web_contents()->GetTopLevelNativeWindow(); } -void WebContentsViewGuest::OnGuestInitialized(WebContentsView* parent_view) { +void WebContentsViewGuest::OnGuestAttached(WebContentsView* parent_view) { #if defined(USE_AURA) // In aura, ScreenPositionClient doesn't work properly if we do // not have the native view associated with this WebContentsViewGuest in the @@ -71,6 +71,13 @@ void WebContentsViewGuest::OnGuestInitialized(WebContentsView* parent_view) { #endif // defined(USE_AURA) } +void WebContentsViewGuest::OnGuestDetached(WebContentsView* old_parent_view) { +#if defined(USE_AURA) + old_parent_view->GetNativeView()->RemoveChild( + platform_view_->GetNativeView()); +#endif // defined(USE_AURA) +} + ContextMenuParams WebContentsViewGuest::ConvertContextMenuParams( const ContextMenuParams& params) const { // We need to add |offset| of the guest from the embedder to position the @@ -143,12 +150,9 @@ RenderWidgetHostViewBase* WebContentsViewGuest::CreateViewForWidget( RenderWidgetHostViewBase* platform_widget = platform_view_->CreateViewForWidget(render_widget_host); - RenderWidgetHostViewBase* view = new RenderWidgetHostViewGuest( - render_widget_host, - guest_, - platform_widget); - - return view; + return new RenderWidgetHostViewGuest(render_widget_host, + guest_, + platform_widget); } RenderWidgetHostViewBase* WebContentsViewGuest::CreateViewForPopupWidget( diff --git a/content/browser/web_contents/web_contents_view_guest.h b/content/browser/web_contents/web_contents_view_guest.h index 9a54375..c8b662a 100644 --- a/content/browser/web_contents/web_contents_view_guest.h +++ b/content/browser/web_contents/web_contents_view_guest.h @@ -35,7 +35,9 @@ class WebContentsViewGuest : public WebContentsView, WebContents* web_contents(); - void OnGuestInitialized(WebContentsView* parent_view); + void OnGuestAttached(WebContentsView* parent_view); + + void OnGuestDetached(WebContentsView* old_parent_view); // Converts the guest specific coordinates in |params| to embedder specific // ones. diff --git a/content/common/browser_plugin/browser_plugin_messages.h b/content/common/browser_plugin/browser_plugin_messages.h index 80c8e23..aaeae27 100644 --- a/content/common/browser_plugin/browser_plugin_messages.h +++ b/content/common/browser_plugin/browser_plugin_messages.h @@ -125,11 +125,6 @@ IPC_MESSAGE_ROUTED2(BrowserPluginHostMsg_ReclaimCompositorResources, int /* browser_plugin_instance_id */, FrameHostMsg_ReclaimCompositorResources_Params /* params */) -// When a BrowserPlugin has been removed from the embedder's DOM, it informs -// the browser process to cleanup the guest. -IPC_MESSAGE_ROUTED1(BrowserPluginHostMsg_PluginDestroyed, - int /* browser_plugin_instance_id */) - // Tells the guest it has been shown or hidden. IPC_MESSAGE_ROUTED2(BrowserPluginHostMsg_SetVisibility, int /* browser_plugin_instance_id */, diff --git a/content/public/browser/browser_plugin_guest_delegate.h b/content/public/browser/browser_plugin_guest_delegate.h index 0b7372f5..21e115a 100644 --- a/content/public/browser/browser_plugin_guest_delegate.h +++ b/content/public/browser/browser_plugin_guest_delegate.h @@ -57,10 +57,6 @@ class CONTENT_EXPORT BrowserPluginGuestDelegate { bool last_unlocked_by_target, const base::Callback<void(bool)>& callback) {} - // Requests that the delegate destroy itself along with its associated - // WebContents. - virtual void Destroy() {} - // Registers a |callback| with the delegate that the delegate would call when // it is about to be destroyed. typedef base::Callback<void()> DestructionCallback; diff --git a/content/renderer/browser_plugin/browser_plugin.cc b/content/renderer/browser_plugin/browser_plugin.cc index b51b452..7e91f85 100644 --- a/content/renderer/browser_plugin/browser_plugin.cc +++ b/content/renderer/browser_plugin/browser_plugin.cc @@ -83,13 +83,6 @@ BrowserPlugin::BrowserPlugin(RenderViewImpl* render_view, BrowserPlugin::~BrowserPlugin() { browser_plugin_manager()->RemoveBrowserPlugin(browser_plugin_instance_id_); - - if (!ready()) - return; - - browser_plugin_manager()->Send( - new BrowserPluginHostMsg_PluginDestroyed(render_view_routing_id_, - browser_plugin_instance_id_)); } bool BrowserPlugin::OnMessageReceived(const IPC::Message& message) { @@ -174,7 +167,6 @@ void BrowserPlugin::OnCompositorFrameSwapped(const IPC::Message& message) { BrowserPluginMsg_CompositorFrameSwapped::Param param; if (!BrowserPluginMsg_CompositorFrameSwapped::Read(&message, ¶m)) return; - // Note that there is no need to send ACK for this message. // If the guest has updated pixels then it is no longer crashed. guest_crashed_ = false; @@ -317,6 +309,8 @@ bool BrowserPlugin::initialize(WebPluginContainer* container) { g_plugin_container_map.Get().insert(std::make_pair(container_, this)); + browser_plugin_manager()->AddBrowserPlugin(browser_plugin_instance_id_, this); + // This is a way to notify observers of our attributes that this plugin is // available in render tree. // TODO(lazyboy): This should be done through the delegate instead. Perhaps @@ -324,7 +318,6 @@ bool BrowserPlugin::initialize(WebPluginContainer* container) { UpdateDOMAttribute("internalinstanceid", base::IntToString(browser_plugin_instance_id_)); - browser_plugin_manager()->AddBrowserPlugin(browser_plugin_instance_id_, this); return true; } diff --git a/content/renderer/browser_plugin/browser_plugin_browsertest.cc b/content/renderer/browser_plugin/browser_plugin_browsertest.cc index 6abede3..bb0719f 100644 --- a/content/renderer/browser_plugin/browser_plugin_browsertest.cc +++ b/content/renderer/browser_plugin/browser_plugin_browsertest.cc @@ -31,9 +31,6 @@ const char kHTMLForBrowserPluginObject[] = " src='foo' type='%s'></object>" "<script>document.querySelector('object').nonExistentAttribute;</script>"; -const char kHTMLForSourcelessPluginObject[] = - "<object id='browserplugin' width='640px' height='480px' type='%s'>"; - std::string GetHTMLForBrowserPluginObject() { return base::StringPrintf(kHTMLForBrowserPluginObject, kBrowserPluginMimeType); @@ -173,33 +170,4 @@ TEST_F(BrowserPluginTest, InitialResize) { ASSERT_TRUE(browser_plugin); } -TEST_F(BrowserPluginTest, RemovePlugin) { - LoadHTML(GetHTMLForBrowserPluginObject().c_str()); - MockBrowserPlugin* browser_plugin = GetCurrentPlugin(); - ASSERT_TRUE(browser_plugin); - - EXPECT_FALSE(browser_plugin_manager()->sink().GetUniqueMessageMatching( - BrowserPluginHostMsg_PluginDestroyed::ID)); - ExecuteJavaScript("x = document.getElementById('browserplugin'); " - "x.parentNode.removeChild(x);"); - ProcessPendingMessages(); - EXPECT_TRUE(browser_plugin_manager()->sink().GetUniqueMessageMatching( - BrowserPluginHostMsg_PluginDestroyed::ID)); -} - -// This test verifies that PluginDestroyed messages do not get sent from a -// BrowserPlugin that has never navigated. -TEST_F(BrowserPluginTest, RemovePluginBeforeNavigation) { - std::string html = base::StringPrintf(kHTMLForSourcelessPluginObject, - kBrowserPluginMimeType); - LoadHTML(html.c_str()); - EXPECT_FALSE(browser_plugin_manager()->sink().GetUniqueMessageMatching( - BrowserPluginHostMsg_PluginDestroyed::ID)); - ExecuteJavaScript("x = document.getElementById('browserplugin'); " - "x.parentNode.removeChild(x);"); - ProcessPendingMessages(); - EXPECT_FALSE(browser_plugin_manager()->sink().GetUniqueMessageMatching( - BrowserPluginHostMsg_PluginDestroyed::ID)); -} - } // namespace content diff --git a/extensions/browser/api/guest_view/guest_view_internal_api.cc b/extensions/browser/api/guest_view/guest_view_internal_api.cc index 37109dc..a397176 100644 --- a/extensions/browser/api/guest_view/guest_view_internal_api.cc +++ b/extensions/browser/api/guest_view/guest_view_internal_api.cc @@ -65,6 +65,27 @@ void GuestViewInternalCreateGuestFunction::CreateGuestCallback( SendResponse(true); } +GuestViewInternalDestroyGuestFunction:: + GuestViewInternalDestroyGuestFunction() { +} + +GuestViewInternalDestroyGuestFunction:: + ~GuestViewInternalDestroyGuestFunction() { +} + +bool GuestViewInternalDestroyGuestFunction::RunAsync() { + scoped_ptr<guest_view_internal::DestroyGuest::Params> params( + guest_view_internal::DestroyGuest::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + GuestViewBase* guest = GuestViewBase::From( + render_view_host()->GetProcess()->GetID(), params->instance_id); + if (!guest) + return false; + guest->Destroy(); + SendResponse(true); + return true; +} + GuestViewInternalSetAutoSizeFunction:: GuestViewInternalSetAutoSizeFunction() { } diff --git a/extensions/browser/api/guest_view/guest_view_internal_api.h b/extensions/browser/api/guest_view/guest_view_internal_api.h index d6d54ce..13d7a42 100644 --- a/extensions/browser/api/guest_view/guest_view_internal_api.h +++ b/extensions/browser/api/guest_view/guest_view_internal_api.h @@ -24,6 +24,21 @@ class GuestViewInternalCreateGuestFunction : public AsyncExtensionFunction { DISALLOW_COPY_AND_ASSIGN(GuestViewInternalCreateGuestFunction); }; +class GuestViewInternalDestroyGuestFunction : public AsyncExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("guestViewInternal.destroyGuest", + GUESTVIEWINTERNAL_DESTROYGUEST); + GuestViewInternalDestroyGuestFunction(); + + protected: + virtual ~GuestViewInternalDestroyGuestFunction(); + virtual bool RunAsync() OVERRIDE FINAL; + + private: + void DestroyGuestCallback(content::WebContents* guest_web_contents); + DISALLOW_COPY_AND_ASSIGN(GuestViewInternalDestroyGuestFunction); +}; + class GuestViewInternalSetAutoSizeFunction : public AsyncExtensionFunction { public: DECLARE_EXTENSION_FUNCTION("guestViewInternal.setAutoSize", diff --git a/extensions/browser/api/web_view/web_view_internal_api.cc b/extensions/browser/api/web_view/web_view_internal_api.cc index c6ed1ce..62b3497 100644 --- a/extensions/browser/api/web_view/web_view_internal_api.cc +++ b/extensions/browser/api/web_view/web_view_internal_api.cc @@ -65,7 +65,7 @@ bool WebViewInternalNavigateFunction::RunAsyncSafe(WebViewGuest* guest) { webview::Navigate::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); std::string src = params->src; - guest->NavigateGuest(src); + guest->NavigateGuest(src, true /* force_navigation */); return true; } diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h index e46694e..a524997 100644 --- a/extensions/browser/extension_function_histogram_value.h +++ b/extensions/browser/extension_function_histogram_value.h @@ -958,6 +958,7 @@ enum HistogramValue { HOTWORDPRIVATE_GETLAUNCHSTATE, HOTWORDPRIVATE_SETHOTWORDALWAYSONSEARCHENABLED, WEBVIEWINTERNAL_LOADDATAWITHBASEURL, + GUESTVIEWINTERNAL_DESTROYGUEST, // Last entry: Add new entries above and ensure to update // tools/metrics/histograms/histograms.xml. ENUM_BOUNDARY diff --git a/extensions/browser/guest_view/app_view/app_view_guest.cc b/extensions/browser/guest_view/app_view/app_view_guest.cc index e608a49..92608ac 100644 --- a/extensions/browser/guest_view/app_view/app_view_guest.cc +++ b/extensions/browser/guest_view/app_view/app_view_guest.cc @@ -203,8 +203,12 @@ void AppViewGuest::DidAttachToEmbedder() { // element. This means that the host element knows how to route input // events to the guest, and the guest knows how to get frames to the // embedder. + if (!url_.is_valid()) + return; + web_contents()->GetController().LoadURL( url_, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string()); + url_ = GURL(); } void AppViewGuest::DidInitialize() { diff --git a/extensions/browser/guest_view/extension_options/extension_options_guest.cc b/extensions/browser/guest_view/extension_options/extension_options_guest.cc index 32b5dce..ef79ee7 100644 --- a/extensions/browser/guest_view/extension_options/extension_options_guest.cc +++ b/extensions/browser/guest_view/extension_options/extension_options_guest.cc @@ -40,7 +40,8 @@ ExtensionOptionsGuest::ExtensionOptionsGuest( : GuestView<ExtensionOptionsGuest>(browser_context, guest_instance_id), extension_options_guest_delegate_( extensions::ExtensionsAPIClient::Get() - ->CreateExtensionOptionsGuestDelegate(this)) { + ->CreateExtensionOptionsGuestDelegate(this)), + has_navigated_(false) { } ExtensionOptionsGuest::~ExtensionOptionsGuest() { @@ -108,10 +109,16 @@ void ExtensionOptionsGuest::CreateWebContents( void ExtensionOptionsGuest::DidAttachToEmbedder() { SetUpAutoSize(); + + // We should not re-navigate on reattachment. + if (has_navigated_) + return; + web_contents()->GetController().LoadURL(options_page_, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string()); + has_navigated_ = true; } void ExtensionOptionsGuest::DidInitialize() { diff --git a/extensions/browser/guest_view/extension_options/extension_options_guest.h b/extensions/browser/guest_view/extension_options/extension_options_guest.h index 6d9f9e5..73b28ef 100644 --- a/extensions/browser/guest_view/extension_options/extension_options_guest.h +++ b/extensions/browser/guest_view/extension_options/extension_options_guest.h @@ -77,6 +77,7 @@ class ExtensionOptionsGuest scoped_ptr<extensions::ExtensionOptionsGuestDelegate> extension_options_guest_delegate_; GURL options_page_; + bool has_navigated_; DISALLOW_COPY_AND_ASSIGN(ExtensionOptionsGuest); }; diff --git a/extensions/browser/guest_view/guest_view_base.cc b/extensions/browser/guest_view/guest_view_base.cc index 9f5a244..6a181a6 100644 --- a/extensions/browser/guest_view/guest_view_base.cc +++ b/extensions/browser/guest_view/guest_view_base.cc @@ -70,16 +70,18 @@ class GuestViewBase::EmbedderWebContentsObserver : public WebContentsObserver { // WebContentsObserver implementation. virtual void WebContentsDestroyed() OVERRIDE { + // If the embedder is destroyed then destroy the guest. Destroy(); } - virtual void RenderViewHostChanged( - content::RenderViewHost* old_host, - content::RenderViewHost* new_host) OVERRIDE { + virtual void AboutToNavigateRenderView( + content::RenderViewHost* render_view_host) OVERRIDE { + // If the embedder navigates then destroy the guest. Destroy(); } virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE { + // If the embedder crashes, then destroy the guest. Destroy(); } @@ -297,24 +299,6 @@ void GuestViewBase::RenderProcessExited(content::RenderProcessHost* host, Destroy(); } -void GuestViewBase::Destroy() { - DCHECK(web_contents()); - content::RenderProcessHost* host = - content::RenderProcessHost::FromID(embedder_render_process_id()); - if (host) - host->RemoveObserver(this); - WillDestroy(); - if (!destruction_callback_.is_null()) - destruction_callback_.Run(); - - webcontents_guestview_map.Get().erase(web_contents()); - GuestViewManager::FromBrowserContext(browser_context_)-> - RemoveGuest(guest_instance_id_); - pending_events_.clear(); - - delete web_contents(); -} - void GuestViewBase::DidAttach(int guest_proxy_routing_id) { // Give the derived class an opportunity to perform some actions. DidAttachToEmbedder(); @@ -341,6 +325,28 @@ void GuestViewBase::GuestSizeChanged(const gfx::Size& old_size, GuestSizeChangedDueToAutoSize(old_size, new_size); } +void GuestViewBase::Destroy() { + DCHECK(web_contents()); + content::RenderProcessHost* host = + content::RenderProcessHost::FromID(embedder_render_process_id()); + if (host) + host->RemoveObserver(this); + + // Give the derived class an opportunity to perform some cleanup. + WillDestroy(); + + // Give the content module an opportunity to perform some cleanup. + if (!destruction_callback_.is_null()) + destruction_callback_.Run(); + + webcontents_guestview_map.Get().erase(web_contents()); + GuestViewManager::FromBrowserContext(browser_context_)-> + RemoveGuest(guest_instance_id_); + pending_events_.clear(); + + delete web_contents(); +} + void GuestViewBase::SetAttachParams(const base::DictionaryValue& params) { attach_params_.reset(params.DeepCopy()); attach_params_->GetInteger(guestview::kParameterInstanceId, @@ -396,7 +402,14 @@ void GuestViewBase::RenderViewReady() { } void GuestViewBase::WebContentsDestroyed() { + // Let the derived class know that its WebContents is in the process of + // being destroyed. web_contents() is still valid at this point. + // TODO(fsamuel): This allows for reentrant code into WebContents during + // destruction. This could potentially lead to bugs. Perhaps we should get rid + // of this? GuestDestroyed(); + + // Self-destruct. delete this; } diff --git a/extensions/browser/guest_view/guest_view_base.h b/extensions/browser/guest_view/guest_view_base.h index f255e1f..7c3bd43 100644 --- a/extensions/browser/guest_view/guest_view_base.h +++ b/extensions/browser/guest_view/guest_view_base.h @@ -102,7 +102,7 @@ class GuestViewBase : public content::BrowserPluginGuestDelegate, virtual void GuestDestroyed() {} // This method is invoked when the guest RenderView is ready, e.g. because we - // recreated it after a crash. + // recreated it after a crash or after reattachment. // // This gives the derived class an opportunity to perform some initialization // work. @@ -224,7 +224,10 @@ class GuestViewBase : public content::BrowserPluginGuestDelegate, return opener_.get(); } - // Sets some additional chrome/ initialization parameters. + // Destroy this guest. + void Destroy(); + + // Saves the attach state of the custom element hosting this GuestView. void SetAttachParams(const base::DictionaryValue& params); void SetOpener(GuestViewBase* opener); @@ -235,7 +238,6 @@ class GuestViewBase : public content::BrowserPluginGuestDelegate, int exit_code) OVERRIDE; // BrowserPluginGuestDelegate implementation. - virtual void Destroy() OVERRIDE FINAL; virtual void DidAttach(int guest_proxy_routing_id) OVERRIDE FINAL; virtual void ElementSizeChanged(const gfx::Size& old_size, const gfx::Size& new_size) OVERRIDE FINAL; diff --git a/extensions/browser/guest_view/web_view/web_view_constants.cc b/extensions/browser/guest_view/web_view/web_view_constants.cc index 9454292..9526d43 100644 --- a/extensions/browser/guest_view/web_view/web_view_constants.cc +++ b/extensions/browser/guest_view/web_view/web_view_constants.cc @@ -42,7 +42,6 @@ const char kEventLoadStop[] = "webViewInternal.onLoadStop"; const char kEventMessage[] = "webViewInternal.onMessage"; const char kEventNewWindow[] = "webViewInternal.onNewWindow"; const char kEventPermissionRequest[] = "webViewInternal.onPermissionRequest"; -const char kEventPluginDestroyed[] = "webViewInternal.onPluginDestroyed"; const char kEventResponsive[] = "webViewInternal.onResponsive"; const char kEventSizeChanged[] = "webViewInternal.onSizeChanged"; const char kEventUnresponsive[] = "webViewInternal.onUnresponsive"; diff --git a/extensions/browser/guest_view/web_view/web_view_constants.h b/extensions/browser/guest_view/web_view/web_view_constants.h index c7ee206..d6d0b50 100644 --- a/extensions/browser/guest_view/web_view/web_view_constants.h +++ b/extensions/browser/guest_view/web_view/web_view_constants.h @@ -46,7 +46,6 @@ extern const char kEventLoadStop[]; extern const char kEventMessage[]; extern const char kEventNewWindow[]; extern const char kEventPermissionRequest[]; -extern const char kEventPluginDestroyed[]; extern const char kEventResponsive[]; extern const char kEventSizeChanged[]; extern const char kEventUnresponsive[]; diff --git a/extensions/browser/guest_view/web_view/web_view_guest.cc b/extensions/browser/guest_view/web_view/web_view_guest.cc index 2e124e0..f00a682 100644 --- a/extensions/browser/guest_view/web_view/web_view_guest.cc +++ b/extensions/browser/guest_view/web_view/web_view_guest.cc @@ -287,7 +287,7 @@ void WebViewGuest::DidAttachToEmbedder() { std::string src; if (attach_params()->GetString(webview::kAttributeSrc, &src) && !src.empty()) - NavigateGuest(src); + NavigateGuest(src, false /* force_navigation */); if (GetOpener()) { // We need to do a navigation here if the target URL has changed between @@ -299,7 +299,7 @@ void WebViewGuest::DidAttachToEmbedder() { if (it != GetOpener()->pending_new_windows_.end()) { const NewWindowInfo& new_window_info = it->second; if (new_window_info.changed || !web_contents()->HasOpener()) - NavigateGuest(new_window_info.url.spec()); + NavigateGuest(new_window_info.url.spec(), false /* force_navigation */); } else { NOTREACHED(); } @@ -404,10 +404,6 @@ void WebViewGuest::WillDestroy() { if (!attached() && GetOpener()) GetOpener()->pending_new_windows_.erase(this); DestroyUnattachedWindows(); - - scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); - DispatchEventToEmbedder( - new GuestViewBase::Event(webview::kEventPluginDestroyed, args.Pass())); } bool WebViewGuest::AddMessageToConsole(WebContents* source, @@ -679,6 +675,8 @@ void WebViewGuest::DidCommitProvisionalLoadForFrame( content::RenderFrameHost* render_frame_host, const GURL& url, ui::PageTransition transition_type) { + if (!render_frame_host->GetParent()) + src_ = url; scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); args->SetString(guestview::kUrl, url.spec()); args->SetBoolean(guestview::kIsTopLevel, !render_frame_host->GetParent()); @@ -898,7 +896,11 @@ content::ColorChooser* WebViewGuest::OpenColorChooser( web_contents, color, suggestions); } -void WebViewGuest::NavigateGuest(const std::string& src) { +void WebViewGuest::NavigateGuest(const std::string& src, + bool force_navigation) { + if (src.empty()) + return; + GURL url = ResolveURL(src); // Do not allow navigating a guest to schemes other than known safe schemes. @@ -914,6 +916,8 @@ void WebViewGuest::NavigateGuest(const std::string& src) { net::ErrorToShortString(net::ERR_ABORTED)); return; } + if (!force_navigation && (src_ == url)) + return; GURL validated_url(url); web_contents()->GetRenderProcessHost()->FilterURL(false, &validated_url); diff --git a/extensions/browser/guest_view/web_view/web_view_guest.h b/extensions/browser/guest_view/web_view/web_view_guest.h index ccf52ed..f1e1e83 100644 --- a/extensions/browser/guest_view/web_view/web_view_guest.h +++ b/extensions/browser/guest_view/web_view/web_view_guest.h @@ -58,7 +58,7 @@ class WebViewGuest : public GuestView<WebViewGuest>, static const char Type[]; // Request navigating the guest to the provided |src| URL. - void NavigateGuest(const std::string& src); + void NavigateGuest(const std::string& src, bool force_navigation); // Shows the context menu for the guest. // |items| acts as a filter. This restricts the current context's default @@ -343,10 +343,13 @@ class WebViewGuest : public GuestView<WebViewGuest>, // Stores whether the contents of the guest can be transparent. bool guest_opaque_; + // Stores the src URL of the WebView. + GURL src_; + // Handles the JavaScript dialog requests. JavaScriptDialogHelper javascript_dialog_helper_; - // Handels permission requests. + // Handles permission requests. scoped_ptr<WebViewPermissionHelper> web_view_permission_helper_; scoped_ptr<WebViewGuestDelegate> web_view_guest_delegate_; diff --git a/extensions/common/api/guest_view_internal.json b/extensions/common/api/guest_view_internal.json index bafb2ed..9a96f0b 100644 --- a/extensions/common/api/guest_view_internal.json +++ b/extensions/common/api/guest_view_internal.json @@ -67,6 +67,16 @@ ] }, { + "name": "destroyGuest", + "type": "function", + "parameters": [ + { + "type": "integer", + "name": "instanceId" + } + ] + }, + { "name": "setAutoSize", "type": "function", "parameters": [ diff --git a/extensions/renderer/resources/web_view.js b/extensions/renderer/resources/web_view.js index 3cf20f9..095d377 100644 --- a/extensions/renderer/resources/web_view.js +++ b/extensions/renderer/resources/web_view.js @@ -87,6 +87,7 @@ function WebViewInternal(webviewNode) { privates(webviewNode).internal = this; this.webviewNode = webviewNode; this.attached = false; + this.pendingGuestCreation = false; this.elementAttached = false; this.beforeFirstNavigation = true; @@ -142,6 +143,7 @@ WebViewInternal.prototype.reset = function() { // heard back from createGuest yet. We will not reset the flag in this case so // that we don't end up allocating a second guest. if (this.guestInstanceId) { + GuestViewInternal.destroyGuest(this.guestInstanceId); this.guestInstanceId = undefined; this.beforeFirstNavigation = true; this.validPartitionId = true; @@ -542,25 +544,18 @@ WebViewInternal.prototype.handleBrowserPluginAttributeMutation = this.browserPluginNode.removeAttribute('internalinstanceid'); this.internalInstanceId = parseInt(newValue); - if (!this.deferredAttachState) { - this.parseAttributes(); - return; - } - if (!!this.guestInstanceId && this.guestInstanceId != 0) { - window.setTimeout(function() { - var isNewWindow = this.deferredAttachState ? - this.deferredAttachState.isNewWindow : false; - var params = this.buildAttachParams(isNewWindow); - guestViewInternalNatives.AttachGuest( - this.internalInstanceId, - this.guestInstanceId, - params, - function(w) { - this.contentWindow = w; - }.bind(this) - ); - }.bind(this), 0); + var isNewWindow = this.deferredAttachState ? + this.deferredAttachState.isNewWindow : false; + var params = this.buildAttachParams(isNewWindow); + guestViewInternalNatives.AttachGuest( + this.internalInstanceId, + this.guestInstanceId, + params, + function(w) { + this.contentWindow = w; + }.bind(this) + ); } return; @@ -641,24 +636,20 @@ WebViewInternal.prototype.hasNavigated = function() { WebViewInternal.prototype.parseSrcAttribute = function(result) { if (!this.partition.validPartitionId) { result.error = ERROR_MSG_INVALID_PARTITION_ATTRIBUTE; - return false; + return; } this.src = this.webviewNode.getAttribute('src'); if (!this.src) { - return true; - } - - if (!this.elementAttached) { - return true; + return; } if (!this.hasGuestInstanceID()) { if (this.beforeFirstNavigation) { this.beforeFirstNavigation = false; - this.allocateInstanceId(); + this.createGuest(); } - return true; + return; } // Navigate to this.src. @@ -668,17 +659,23 @@ WebViewInternal.prototype.parseSrcAttribute = function(result) { /** @return {boolean} */ WebViewInternal.prototype.parseAttributes = function() { + if (!this.elementAttached) { + return; + } var hasNavigated = this.hasNavigated(); var attributeValue = this.webviewNode.getAttribute('partition'); var result = this.partition.fromAttribute(attributeValue, hasNavigated); - return this.parseSrcAttribute(result); + this.parseSrcAttribute(result); }; WebViewInternal.prototype.hasGuestInstanceID = function() { return this.guestInstanceId != undefined; }; -WebViewInternal.prototype.allocateInstanceId = function() { +WebViewInternal.prototype.createGuest = function() { + if (this.pendingGuestCreation) { + return; + } var storagePartitionId = this.webviewNode.getAttribute(WEB_VIEW_ATTRIBUTE_PARTITION) || this.webviewNode[WEB_VIEW_ATTRIBUTE_PARTITION]; @@ -689,9 +686,15 @@ WebViewInternal.prototype.allocateInstanceId = function() { 'webview', params, function(guestInstanceId) { + this.pendingGuestCreation = false; + if (!this.elementAttached) { + GuestViewInternal.destroyGuest(this.guestInstanceId); + return; + } this.attachWindow(guestInstanceId, false); }.bind(this) ); + this.pendingGuestCreation = true; }; WebViewInternal.prototype.onFrameNameChanged = function(name) { @@ -703,10 +706,6 @@ WebViewInternal.prototype.onFrameNameChanged = function(name) { } }; -WebViewInternal.prototype.onPluginDestroyed = function() { - this.reset(); -}; - WebViewInternal.prototype.dispatchEvent = function(webViewEvent) { return this.webviewNode.dispatchEvent(webViewEvent); }; @@ -756,7 +755,6 @@ WebViewInternal.prototype.onAttach = function(storagePartitionId) { this.partition.fromAttribute(storagePartitionId, this.hasNavigated()); }; - /** @private */ WebViewInternal.prototype.getUserAgent = function() { return this.userAgentOverride || navigator.userAgent; diff --git a/extensions/renderer/resources/web_view_events.js b/extensions/renderer/resources/web_view_events.js index f02069b..c73a1f6 100644 --- a/extensions/renderer/resources/web_view_events.js +++ b/extensions/renderer/resources/web_view_events.js @@ -184,7 +184,6 @@ function WebViewEvents(webViewInternal, viewInstanceId) { // Sets up events. WebViewEvents.prototype.setup = function() { this.setupFrameNameChangedEvent(); - this.setupPluginDestroyedEvent(); this.setupWebRequestEvents(); this.webViewInternal.setupExperimentalContextMenus(); @@ -200,12 +199,6 @@ WebViewEvents.prototype.setupFrameNameChangedEvent = function() { }.bind(this), {instanceId: this.viewInstanceId}); }; -WebViewEvents.prototype.setupPluginDestroyedEvent = function() { - PluginDestroyedEvent.addListener(function(e) { - this.webViewInternal.onPluginDestroyed(); - }.bind(this), {instanceId: this.viewInstanceId}); -}; - WebViewEvents.prototype.setupWebRequestEvents = function() { var request = {}; var createWebRequestEvent = function(webRequestEvent) { diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 88857ba..e4053b5 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml @@ -42335,6 +42335,7 @@ Therefore, the affected-histogram name has to have at least one dot in it. <int value="897" label="HOTWORDPRIVATE_GETLAUNCHSTATE"/> <int value="898" label="HOTWORDPRIVATE_SETHOTWORDALWAYSONSEARCHENABLED"/> <int value="899" label="WEBVIEWINTERNAL_LOADDATAWITHBASEURL"/> + <int value="900" label="GUESTVIEWINTERNAL_DESTROYGUEST"/> </enum> <enum name="ExtensionInstallCause" type="int"> |