summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfsamuel <fsamuel@chromium.org>2014-10-01 17:51:33 -0700
committerCommit bot <commit-bot@chromium.org>2014-10-02 00:51:45 +0000
commita8484dd476ef402fc235a3e9c19b1d52ac0d0f6e (patch)
treee426936de34aa3c4a7c0232fd0c296066dab1138
parent7f79ce28848eb40d4ec6d2d335e2996ff02362e5 (diff)
downloadchromium_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}
-rw-r--r--chrome/browser/apps/web_view_browsertest.cc27
-rw-r--r--chrome/renderer/resources/extensions/app_view.js74
-rw-r--r--chrome/renderer/resources/extensions/extension_options.js45
-rw-r--r--chrome/test/data/extensions/platform_apps/app_view/shim/main.js13
-rw-r--r--chrome/test/data/extensions/platform_apps/web_view/display_none_and_back/main.html12
-rw-r--r--chrome/test/data/extensions/platform_apps/web_view/display_none_and_back/main.js49
-rw-r--r--chrome/test/data/extensions/platform_apps/web_view/display_none_and_back/manifest.json12
-rw-r--r--chrome/test/data/extensions/platform_apps/web_view/display_none_and_back/test.js7
-rw-r--r--content/browser/browser_plugin/browser_plugin_guest.cc48
-rw-r--r--content/browser/browser_plugin/browser_plugin_guest.h9
-rw-r--r--content/browser/web_contents/web_contents_view_guest.cc18
-rw-r--r--content/browser/web_contents/web_contents_view_guest.h4
-rw-r--r--content/common/browser_plugin/browser_plugin_messages.h5
-rw-r--r--content/public/browser/browser_plugin_guest_delegate.h4
-rw-r--r--content/renderer/browser_plugin/browser_plugin.cc11
-rw-r--r--content/renderer/browser_plugin/browser_plugin_browsertest.cc32
-rw-r--r--extensions/browser/api/guest_view/guest_view_internal_api.cc21
-rw-r--r--extensions/browser/api/guest_view/guest_view_internal_api.h15
-rw-r--r--extensions/browser/api/web_view/web_view_internal_api.cc2
-rw-r--r--extensions/browser/extension_function_histogram_value.h1
-rw-r--r--extensions/browser/guest_view/app_view/app_view_guest.cc4
-rw-r--r--extensions/browser/guest_view/extension_options/extension_options_guest.cc9
-rw-r--r--extensions/browser/guest_view/extension_options/extension_options_guest.h1
-rw-r--r--extensions/browser/guest_view/guest_view_base.cc55
-rw-r--r--extensions/browser/guest_view/guest_view_base.h8
-rw-r--r--extensions/browser/guest_view/web_view/web_view_constants.cc1
-rw-r--r--extensions/browser/guest_view/web_view/web_view_constants.h1
-rw-r--r--extensions/browser/guest_view/web_view/web_view_guest.cc18
-rw-r--r--extensions/browser/guest_view/web_view/web_view_guest.h7
-rw-r--r--extensions/common/api/guest_view_internal.json10
-rw-r--r--extensions/renderer/resources/web_view.js64
-rw-r--r--extensions/renderer/resources/web_view_events.js7
-rw-r--r--tools/metrics/histograms/histograms.xml1
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, &param))
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">