Commit cee3dc26 authored by lazyboy's avatar lazyboy Committed by Commit bot

Fix display:none issue for <webview>.

After we load a <webview>, setting its display to "none" removes the
plugin <object> from the render tree. If we set the display back to "",
we need to properly detect the case and allocate new instance id
and call attach to make things work.

The problem is briefly described here:
https://docs.google.com/a/chromium.org/document/d/1ITPOxS97DsG1HkbTAlpH0YcaTXKZNYUs84p1MppaHg0/view

BUG=399060
Test=Load a <webview> in a chrome app. See it render.
Set <webview>.style.display = 'none', see it go away.
Set <webview>.stlye.display = '', see it render properly.

Review URL: https://codereview.chromium.org/440463002

Cr-Commit-Position: refs/heads/master@{#291990}
parent 2414c180
...@@ -98,17 +98,26 @@ class TestGuestViewManager : public extensions::GuestViewManager { ...@@ -98,17 +98,26 @@ class TestGuestViewManager : public extensions::GuestViewManager {
public: public:
explicit TestGuestViewManager(content::BrowserContext* context) : explicit TestGuestViewManager(content::BrowserContext* context) :
GuestViewManager(context), GuestViewManager(context),
seen_guest_removed_(false),
web_contents_(NULL) {} web_contents_(NULL) {}
content::WebContents* WaitForGuestCreated() { content::WebContents* WaitForGuestCreated() {
if (web_contents_) if (web_contents_)
return web_contents_; return web_contents_;
message_loop_runner_ = new content::MessageLoopRunner; created_message_loop_runner_ = new content::MessageLoopRunner;
message_loop_runner_->Run(); created_message_loop_runner_->Run();
return web_contents_; return web_contents_;
} }
void WaitForGuestDeleted() {
if (seen_guest_removed_)
return;
deleted_message_loop_runner_ = new content::MessageLoopRunner;
deleted_message_loop_runner_->Run();
}
private: private:
// GuestViewManager override: // GuestViewManager override:
virtual void AddGuest(int guest_instance_id, virtual void AddGuest(int guest_instance_id,
...@@ -116,13 +125,25 @@ class TestGuestViewManager : public extensions::GuestViewManager { ...@@ -116,13 +125,25 @@ class TestGuestViewManager : public extensions::GuestViewManager {
extensions::GuestViewManager::AddGuest( extensions::GuestViewManager::AddGuest(
guest_instance_id, guest_web_contents); guest_instance_id, guest_web_contents);
web_contents_ = guest_web_contents; web_contents_ = guest_web_contents;
seen_guest_removed_ = false;
if (message_loop_runner_.get()) if (created_message_loop_runner_.get())
message_loop_runner_->Quit(); created_message_loop_runner_->Quit();
}
virtual void RemoveGuest(int guest_instance_id) OVERRIDE {
extensions::GuestViewManager::RemoveGuest(guest_instance_id);
web_contents_ = NULL;
seen_guest_removed_ = true;
if (deleted_message_loop_runner_.get())
deleted_message_loop_runner_->Quit();
} }
bool seen_guest_removed_;
content::WebContents* web_contents_; content::WebContents* web_contents_;
scoped_refptr<content::MessageLoopRunner> message_loop_runner_; scoped_refptr<content::MessageLoopRunner> created_message_loop_runner_;
scoped_refptr<content::MessageLoopRunner> deleted_message_loop_runner_;
}; };
// Test factory for creating test instances of GuestViewManager. // Test factory for creating test instances of GuestViewManager.
...@@ -679,7 +700,6 @@ class WebViewTest : public extensions::PlatformAppBrowserTest { ...@@ -679,7 +700,6 @@ class WebViewTest : public extensions::PlatformAppBrowserTest {
} }
void LoadAppWithGuest(const std::string& app_path) { void LoadAppWithGuest(const std::string& app_path) {
ExtensionTestMessageListener launched_listener("WebViewTest.LAUNCHED", ExtensionTestMessageListener launched_listener("WebViewTest.LAUNCHED",
false); false);
launched_listener.set_failure_message("WebViewTest.FAILURE"); launched_listener.set_failure_message("WebViewTest.FAILURE");
...@@ -842,6 +862,33 @@ IN_PROC_BROWSER_TEST_F(WebViewTest, AutoSize) { ...@@ -842,6 +862,33 @@ IN_PROC_BROWSER_TEST_F(WebViewTest, AutoSize) {
<< message_; << 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 // http://crbug.com/326332
IN_PROC_BROWSER_TEST_F(WebViewTest, DISABLED_Shim_TestAutosizeAfterNavigation) { IN_PROC_BROWSER_TEST_F(WebViewTest, DISABLED_Shim_TestAutosizeAfterNavigation) {
TestHelper("testAutosizeAfterNavigation", "web_view/shim", NO_TEST_SERVER); TestHelper("testAutosizeAfterNavigation", "web_view/shim", NO_TEST_SERVER);
......
...@@ -106,7 +106,7 @@ function WebViewInternal(webviewNode) { ...@@ -106,7 +106,7 @@ function WebViewInternal(webviewNode) {
this.browserPluginNode = this.createBrowserPluginNode(); this.browserPluginNode = this.createBrowserPluginNode();
var shadowRoot = this.webviewNode.createShadowRoot(); var shadowRoot = this.webviewNode.createShadowRoot();
shadowRoot.appendChild(this.browserPluginNode); this.partition = new Partition();
this.setupWebviewNodeAttributes(); this.setupWebviewNodeAttributes();
this.setupFocusPropagation(); this.setupFocusPropagation();
...@@ -114,10 +114,9 @@ function WebViewInternal(webviewNode) { ...@@ -114,10 +114,9 @@ function WebViewInternal(webviewNode) {
this.viewInstanceId = IdGenerator.GetNextId(); this.viewInstanceId = IdGenerator.GetNextId();
this.partition = new Partition();
this.parseAttributes();
new WebViewEvents(this, this.viewInstanceId); new WebViewEvents(this, this.viewInstanceId);
shadowRoot.appendChild(this.browserPluginNode);
} }
/** /**
...@@ -563,8 +562,13 @@ WebViewInternal.prototype.handleBrowserPluginAttributeMutation = ...@@ -563,8 +562,13 @@ WebViewInternal.prototype.handleBrowserPluginAttributeMutation =
if (name == 'internalinstanceid' && !oldValue && !!newValue) { if (name == 'internalinstanceid' && !oldValue && !!newValue) {
this.browserPluginNode.removeAttribute('internalinstanceid'); this.browserPluginNode.removeAttribute('internalinstanceid');
this.internalInstanceId = parseInt(newValue); this.internalInstanceId = parseInt(newValue);
if (this.deferredAttachState && !!this.guestInstanceId &&
this.guestInstanceId != 0) { if (!this.deferredAttachState) {
this.parseAttributes();
return;
}
if (!!this.guestInstanceId && this.guestInstanceId != 0) {
window.setTimeout(function() { window.setTimeout(function() {
var isNewWindow = this.deferredAttachState ? var isNewWindow = this.deferredAttachState ?
this.deferredAttachState.isNewWindow : false; this.deferredAttachState.isNewWindow : false;
...@@ -575,6 +579,7 @@ WebViewInternal.prototype.handleBrowserPluginAttributeMutation = ...@@ -575,6 +579,7 @@ WebViewInternal.prototype.handleBrowserPluginAttributeMutation =
params); params);
}.bind(this), 0); }.bind(this), 0);
} }
return; return;
} }
...@@ -737,6 +742,10 @@ WebViewInternal.prototype.onFrameNameChanged = function(name) { ...@@ -737,6 +742,10 @@ WebViewInternal.prototype.onFrameNameChanged = function(name) {
} }
}; };
WebViewInternal.prototype.onPluginDestroyed = function() {
this.reset();
};
WebViewInternal.prototype.dispatchEvent = function(webViewEvent) { WebViewInternal.prototype.dispatchEvent = function(webViewEvent) {
return this.webviewNode.dispatchEvent(webViewEvent); return this.webviewNode.dispatchEvent(webViewEvent);
}; };
......
...@@ -20,6 +20,7 @@ var CreateEvent = function(name) { ...@@ -20,6 +20,7 @@ var CreateEvent = function(name) {
}; };
var FrameNameChangedEvent = CreateEvent('webViewInternal.onFrameNameChanged'); var FrameNameChangedEvent = CreateEvent('webViewInternal.onFrameNameChanged');
var PluginDestroyedEvent = CreateEvent('webViewInternal.onPluginDestroyed');
var WebRequestMessageEvent = CreateEvent('webViewInternal.onMessage'); var WebRequestMessageEvent = CreateEvent('webViewInternal.onMessage');
// WEB_VIEW_EVENTS is a map of stable <webview> DOM event names to their // WEB_VIEW_EVENTS is a map of stable <webview> DOM event names to their
...@@ -192,6 +193,7 @@ function WebViewEvents(webViewInternal, viewInstanceId) { ...@@ -192,6 +193,7 @@ function WebViewEvents(webViewInternal, viewInstanceId) {
// Sets up events. // Sets up events.
WebViewEvents.prototype.setup = function() { WebViewEvents.prototype.setup = function() {
this.setupFrameNameChangedEvent(); this.setupFrameNameChangedEvent();
this.setupPluginDestroyedEvent();
this.setupWebRequestEvents(); this.setupWebRequestEvents();
this.webViewInternal.setupExperimentalContextMenus(); this.webViewInternal.setupExperimentalContextMenus();
...@@ -202,10 +204,15 @@ WebViewEvents.prototype.setup = function() { ...@@ -202,10 +204,15 @@ WebViewEvents.prototype.setup = function() {
}; };
WebViewEvents.prototype.setupFrameNameChangedEvent = function() { WebViewEvents.prototype.setupFrameNameChangedEvent = function() {
var self = this;
FrameNameChangedEvent.addListener(function(e) { FrameNameChangedEvent.addListener(function(e) {
self.webViewInternal.onFrameNameChanged(e.name); this.webViewInternal.onFrameNameChanged(e.name);
}, {instanceId: self.viewInstanceId}); }.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() { WebViewEvents.prototype.setupWebRequestEvents = function() {
......
<!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>
// 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;
{
"name": "<webview> check display: none toggling.",
"version": "1",
"permissions": [
"webview"
],
"app": {
"background": {
"scripts": ["test.js"]
}
}
}
// 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 () {});
});
...@@ -94,7 +94,8 @@ class GuestViewManager : public content::BrowserPluginGuestManager, ...@@ -94,7 +94,8 @@ class GuestViewManager : public content::BrowserPluginGuestManager,
virtual void AddGuest(int guest_instance_id, virtual void AddGuest(int guest_instance_id,
content::WebContents* guest_web_contents); content::WebContents* guest_web_contents);
void RemoveGuest(int guest_instance_id); // Can be overriden in tests.
virtual void RemoveGuest(int guest_instance_id);
content::WebContents* GetGuestByInstanceID(int guest_instance_id); content::WebContents* GetGuestByInstanceID(int guest_instance_id);
......
...@@ -34,6 +34,7 @@ const char kEventLoadStop[] = "webViewInternal.onLoadStop"; ...@@ -34,6 +34,7 @@ const char kEventLoadStop[] = "webViewInternal.onLoadStop";
const char kEventMessage[] = "webViewInternal.onMessage"; const char kEventMessage[] = "webViewInternal.onMessage";
const char kEventNewWindow[] = "webViewInternal.onNewWindow"; const char kEventNewWindow[] = "webViewInternal.onNewWindow";
const char kEventPermissionRequest[] = "webViewInternal.onPermissionRequest"; const char kEventPermissionRequest[] = "webViewInternal.onPermissionRequest";
const char kEventPluginDestroyed[] = "webViewInternal.onPluginDestroyed";
const char kEventResponsive[] = "webViewInternal.onResponsive"; const char kEventResponsive[] = "webViewInternal.onResponsive";
const char kEventSizeChanged[] = "webViewInternal.onSizeChanged"; const char kEventSizeChanged[] = "webViewInternal.onSizeChanged";
const char kEventUnresponsive[] = "webViewInternal.onUnresponsive"; const char kEventUnresponsive[] = "webViewInternal.onUnresponsive";
......
...@@ -38,6 +38,7 @@ extern const char kEventLoadStop[]; ...@@ -38,6 +38,7 @@ extern const char kEventLoadStop[];
extern const char kEventMessage[]; extern const char kEventMessage[];
extern const char kEventNewWindow[]; extern const char kEventNewWindow[];
extern const char kEventPermissionRequest[]; extern const char kEventPermissionRequest[];
extern const char kEventPluginDestroyed[];
extern const char kEventResponsive[]; extern const char kEventResponsive[];
extern const char kEventSizeChanged[]; extern const char kEventSizeChanged[];
extern const char kEventUnresponsive[]; extern const char kEventUnresponsive[];
......
...@@ -351,6 +351,10 @@ void WebViewGuest::WillDestroy() { ...@@ -351,6 +351,10 @@ void WebViewGuest::WillDestroy() {
if (!attached() && GetOpener()) if (!attached() && GetOpener())
GetOpener()->pending_new_windows_.erase(this); GetOpener()->pending_new_windows_.erase(this);
DestroyUnattachedWindows(); DestroyUnattachedWindows();
scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
DispatchEventToEmbedder(
new GuestViewBase::Event(webview::kEventPluginDestroyed, args.Pass()));
} }
bool WebViewGuest::AddMessageToConsole(WebContents* source, bool WebViewGuest::AddMessageToConsole(WebContents* source,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment