Commit 40625a8f authored by jam@chromium.org's avatar jam@chromium.org

Fix windowed NPAPI plugins covering up dialogs on Win Aura.

The original fix stopped working with the new style of dialogs, since they're not parented to the WebContents anymore but instead to its parent. It also turns out we don't need to watch out for transient windows, as they're now top level ones with their own HWND so clipping works through the OS and we don't need to do anything special.

BUG=299224
R=ben@chromium.org

Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=233297

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@233398 0039d316-1c4b-4281-b951-d872f2087c98
parent 3cfdab14
......@@ -3,17 +3,29 @@
// found in the LICENSE file.
#include "base/command_line.h"
#include "base/prefs/pref_service.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/content_settings/host_content_settings_map.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#if defined(OS_WIN) && defined(USE_AURA)
#include "content/public/browser/web_contents_view.h"
#include "ui/aura/root_window.h"
#endif
namespace {
class PrintPreviewTest : public InProcessBrowserTest {
......@@ -65,4 +77,63 @@ IN_PROC_BROWSER_TEST_F(PrintPreviewTest, PrintCommands) {
ASSERT_TRUE(chrome::IsCommandEnabled(browser(), IDC_ADVANCED_PRINT));
}
#if defined(OS_WIN) && defined(USE_AURA)
BOOL CALLBACK EnumerateChildren(HWND hwnd, LPARAM l_param) {
HWND* child = reinterpret_cast<HWND*>(l_param);
*child = hwnd;
// The first child window is the plugin, then its children. So stop
// enumerating after the first callback.
return FALSE;
}
// This test verifies that constrained windows aren't covered by windowed NPAPI
// plugins. The code which fixes this is in WebContentsViewAura::WindowObserver.
IN_PROC_BROWSER_TEST_F(PrintPreviewTest, WindowedNPAPIPluginHidden) {
browser()->profile()->GetPrefs()->SetBoolean(prefs::kPluginsAlwaysAuthorize,
true);
// First load the page and wait for the NPAPI plugin's window to display.
string16 expected_title(ASCIIToUTF16("ready"));
content::WebContents* tab =
browser()->tab_strip_model()->GetActiveWebContents();
content::TitleWatcher title_watcher(tab, expected_title);
GURL url = ui_test_utils::GetTestUrl(
base::FilePath().AppendASCII("printing"),
base::FilePath().AppendASCII("npapi_plugin.html"));
ui_test_utils::NavigateToURL(browser(), url);
EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
// Now get the region of the plugin before and after the print preview is
// shown. They should be different.
HWND hwnd =
tab->GetView()->GetNativeView()->GetDispatcher()->GetAcceleratedWidget();
HWND child = NULL;
EnumChildWindows(hwnd, EnumerateChildren,reinterpret_cast<LPARAM>(&child));
RECT region_before, region_after;
int result = GetWindowRgnBox(child, &region_before);
ASSERT_EQ(result, SIMPLEREGION);
// Now print preview.
Print();
result = GetWindowRgnBox(child, &region_after);
if (result == NULLREGION) {
// Depending on the browser window size, the plugin could be full covered.
return;
}
ASSERT_EQ(result, SIMPLEREGION);
bool rects_equal =
region_before.left == region_after.left &&
region_before.top == region_after.top &&
region_before.right == region_after.right &&
region_before.bottom == region_after.bottom;
ASSERT_FALSE(rects_equal);
}
#endif
} // namespace
<hmtl>
<body>
<embed type="application/vnd.npapi-test"
height="300"
width="300"
name="set_title_in_paint"
id="1"
mode="np_embed"/>
</body>
<script>
function SetTitle() {
window.document.title = "ready";
return;
}
</script>
</hmtl>
......@@ -442,113 +442,6 @@ class RenderWidgetHostViewAura::WindowObserver : public aura::WindowObserver {
DISALLOW_COPY_AND_ASSIGN(WindowObserver);
};
#if defined(OS_WIN)
// On Windows, we need to watch the top level window for changes to transient
// windows because they can cover the view and we need to ensure that they're
// rendered on top of windowed NPAPI plugins.
class RenderWidgetHostViewAura::TransientWindowObserver
: public aura::WindowObserver {
public:
explicit TransientWindowObserver(RenderWidgetHostViewAura* view)
: view_(view), top_level_(NULL) {
view_->window_->AddObserver(this);
}
virtual ~TransientWindowObserver() {
view_->window_->RemoveObserver(this);
StopObserving();
}
// Overridden from aura::WindowObserver:
virtual void OnWindowHierarchyChanged(
const aura::WindowObserver::HierarchyChangeParams& params) OVERRIDE {
aura::Window* top_level = GetToplevelWindow();
if (top_level == top_level_)
return;
StopObserving();
top_level_ = top_level;
if (top_level_ && top_level_ != view_->window_)
top_level_->AddObserver(this);
}
virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
if (window == top_level_)
StopObserving();
}
virtual void OnWindowBoundsChanged(aura::Window* window,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds) OVERRIDE {
if (window->transient_parent())
SendPluginCutoutRects();
}
virtual void OnWindowVisibilityChanged(aura::Window* window,
bool visible) OVERRIDE {
if (window->transient_parent())
SendPluginCutoutRects();
}
virtual void OnAddTransientChild(aura::Window* window,
aura::Window* transient) OVERRIDE {
transient->AddObserver(this);
// Just wait for the OnWindowBoundsChanged of the transient, since the size
// is not known now.
}
virtual void OnRemoveTransientChild(aura::Window* window,
aura::Window* transient) OVERRIDE {
transient->RemoveObserver(this);
SendPluginCutoutRects();
}
aura::Window* GetToplevelWindow() {
aura::Window* root = view_->window_->GetRootWindow();
if (!root)
return NULL;
aura::client::ActivationClient* activation_client =
aura::client::GetActivationClient(root);
if (!activation_client)
return NULL;
return activation_client->GetToplevelWindow(view_->window_);
}
void StopObserving() {
if (!top_level_)
return;
const aura::Window::Windows& transients = top_level_->transient_children();
for (size_t i = 0; i < transients.size(); ++i)
transients[i]->RemoveObserver(this);
if (top_level_ != view_->window_)
top_level_->RemoveObserver(this);
top_level_ = NULL;
}
void SendPluginCutoutRects() {
std::vector<gfx::Rect> cutouts;
if (top_level_) {
const aura::Window::Windows& transients =
top_level_->transient_children();
for (size_t i = 0; i < transients.size(); ++i) {
if (transients[i]->IsVisible())
cutouts.push_back(transients[i]->GetBoundsInRootWindow());
}
}
view_->UpdateTransientRects(cutouts);
}
private:
RenderWidgetHostViewAura* view_;
aura::Window* top_level_;
DISALLOW_COPY_AND_ASSIGN(TransientWindowObserver);
};
#endif
////////////////////////////////////////////////////////////////////////////////
// RenderWidgetHostViewAura, public:
......@@ -584,9 +477,6 @@ RenderWidgetHostViewAura::RenderWidgetHostViewAura(RenderWidgetHost* host)
aura::client::SetActivationChangeObserver(window_, this);
aura::client::SetFocusChangeObserver(window_, this);
gfx::Screen::GetScreenFor(window_)->AddObserver(this);
#if defined(OS_WIN)
transient_observer_.reset(new TransientWindowObserver(this));
#endif
software_frame_manager_.reset(new SoftwareFrameManager(
weak_ptr_factory_.GetWeakPtr()));
}
......@@ -684,7 +574,6 @@ void RenderWidgetHostViewAura::WasShown() {
#if defined(OS_WIN)
LPARAM lparam = reinterpret_cast<LPARAM>(this);
EnumChildWindows(ui::GetHiddenWindow(), ShowWindowsCallback, lparam);
transient_observer_->SendPluginCutoutRects();
#endif
}
......@@ -697,6 +586,7 @@ void RenderWidgetHostViewAura::WasHidden() {
released_front_lock_ = NULL;
#if defined(OS_WIN)
constrained_rects_.clear();
aura::WindowEventDispatcher* dispatcher = window_->GetDispatcher();
if (dispatcher) {
HWND parent = dispatcher->GetAcceleratedWidget();
......@@ -891,14 +781,10 @@ void RenderWidgetHostViewAura::MovePluginWindows(
plugin_window_moves_[moves[i].window] = moves[i];
// transient_rects_ and constrained_rects_ are relative to the root window.
// We want to convert them to be relative to the plugin window.
std::vector<gfx::Rect> cutout_rects;
cutout_rects.assign(transient_rects_.begin(), transient_rects_.end());
cutout_rects.insert(cutout_rects.end(), constrained_rects_.begin(),
constrained_rects_.end());
for (size_t j = 0; j < cutout_rects.size(); ++j) {
gfx::Rect offset_cutout = cutout_rects[j];
// constrained_rects_ are relative to the root window. We want to convert
// them to be relative to the plugin window.
for (size_t j = 0; j < constrained_rects_.size(); ++j) {
gfx::Rect offset_cutout = constrained_rects_[j];
offset_cutout -= moves[i].window_rect.OffsetFromOrigin();
moves[i].cutout_rects.push_back(offset_cutout);
}
......@@ -1362,12 +1248,6 @@ void RenderWidgetHostViewAura::DidReceiveFrameFromRenderer() {
}
#if defined(OS_WIN)
void RenderWidgetHostViewAura::UpdateTransientRects(
const std::vector<gfx::Rect>& rects) {
transient_rects_ = rects;
UpdateCutoutRects();
}
void RenderWidgetHostViewAura::UpdateConstrainedWindowRects(
const std::vector<gfx::Rect>& rects) {
constrained_rects_ = rects;
......@@ -1380,10 +1260,7 @@ void RenderWidgetHostViewAura::UpdateCutoutRects() {
HWND parent = window_->GetDispatcher()->GetAcceleratedWidget();
CutoutRectsParams params;
params.widget = this;
params.cutout_rects.assign(transient_rects_.begin(), transient_rects_.end());
params.cutout_rects.insert(params.cutout_rects.end(),
constrained_rects_.begin(),
constrained_rects_.end());
params.cutout_rects = constrained_rects_;
params.geometry = &plugin_window_moves_;
LPARAM lparam = reinterpret_cast<LPARAM>(&params);
EnumChildWindows(parent, SetCutoutRectsCallback, lparam);
......@@ -3209,9 +3086,6 @@ RenderWidgetHostViewAura::~RenderWidgetHostViewAura() {
factory->RemoveObserver(this);
}
window_observer_.reset();
#if defined(OS_WIN)
transient_observer_.reset();
#endif
if (window_->GetDispatcher())
window_->GetDispatcher()->RemoveRootWindowObserver(this);
UnlockMouse();
......
......@@ -395,10 +395,6 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
class WindowObserver;
friend class WindowObserver;
#if defined(OS_WIN)
class TransientWindowObserver;
friend class TransientWindowObserver;
#endif
// Overridden from ImageTransportFactoryObserver:
virtual void OnLostResources() OVERRIDE;
......@@ -556,11 +552,6 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
BrowserAccessibilityManager* GetOrCreateBrowserAccessibilityManager();
#if defined(OS_WIN)
// Sets the cutout rects from transient windows. These are rectangles that
// windowed NPAPI plugins shouldn't paint in. Overwrites any previous cutout
// rects.
void UpdateTransientRects(const std::vector<gfx::Rect>& rects);
// Updates the total list of cutout rects, which is the union of transient
// windows and constrained windows.
void UpdateCutoutRects();
......@@ -731,11 +722,8 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
PaintObserver* paint_observer_;
#if defined(OS_WIN)
scoped_ptr<TransientWindowObserver> transient_observer_;
// The list of rectangles from transient and constrained windows over this
// view. Windowed NPAPI plugins shouldn't draw over them.
std::vector<gfx::Rect> transient_rects_;
// The list of rectangles from constrained windows over this view. Windowed
// NPAPI plugins shouldn't draw over them.
std::vector<gfx::Rect> constrained_rects_;
typedef std::map<HWND, WebPluginGeometry> PluginWindowMoves;
......
......@@ -663,15 +663,66 @@ class WebContentsViewAura::WindowObserver
view_->window_->GetDispatcher()->RemoveRootWindowObserver(this);
if (parent_)
parent_->RemoveObserver(this);
#if defined(OS_WIN)
if (parent_) {
const aura::Window::Windows& children = parent_->children();
for (size_t i = 0; i < children.size(); ++i)
children[i]->RemoveObserver(this);
}
#endif
}
// Overridden from aura::WindowObserver:
#if defined(OS_WIN)
// Constrained windows are added as children of the parent's parent's view
// which may overlap with windowed NPAPI plugins. In that case, tell the RWHV
// so that it can update the plugins' cutout rects accordingly.
// Note: this is hard coding how Chrome layer adds its dialogs. Since NPAPI is
// going to be deprecated in a year, this is ok for now. The test for this is
// PrintPreviewTest.WindowedNPAPIPluginHidden.
virtual void OnWindowAdded(aura::Window* new_window) OVERRIDE {
if (new_window->parent() != parent_)
return;
new_window->AddObserver(this);
UpdateConstrainedWindows(NULL);
}
virtual void OnWillRemoveWindow(aura::Window* window) OVERRIDE {
if (window->parent() == parent_ && window != view_->window_) {
window->RemoveObserver(this);
UpdateConstrainedWindows(window);
}
}
virtual void OnWindowVisibilityChanged(aura::Window* window,
bool visible) OVERRIDE {
if (window->parent() == parent_ && window != view_->window_)
UpdateConstrainedWindows(NULL);
}
#endif
virtual void OnWindowParentChanged(aura::Window* window,
aura::Window* parent) OVERRIDE {
if (window == parent_)
if (window != view_->window_)
return;
if (parent_)
parent_->RemoveObserver(this);
#if defined(OS_WIN)
if (parent_) {
const aura::Window::Windows& children = parent_->children();
for (size_t i = 0; i < children.size(); ++i)
children[i]->RemoveObserver(this);
RenderWidgetHostViewAura* view = ToRenderWidgetHostViewAura(
view_->web_contents_->GetRenderWidgetHostView());
if (view)
view->UpdateConstrainedWindowRects(std::vector<gfx::Rect>());
}
#endif
parent_ = parent;
if (parent)
parent->AddObserver(this);
......@@ -680,18 +731,24 @@ class WebContentsViewAura::WindowObserver
virtual void OnWindowBoundsChanged(aura::Window* window,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds) OVERRIDE {
if (window == parent_ || window == view_->window_) {
SendScreenRects();
if (view_->touch_editable_)
view_->touch_editable_->UpdateEditingController();
#if defined(OS_WIN)
} else {
UpdateConstrainedWindows(NULL);
#endif
}
}
virtual void OnWindowAddedToRootWindow(aura::Window* window) OVERRIDE {
if (window != parent_)
if (window == view_->window_)
window->GetDispatcher()->AddRootWindowObserver(this);
}
virtual void OnWindowRemovingFromRootWindow(aura::Window* window) OVERRIDE {
if (window != parent_)
if (window == view_->window_)
window->GetDispatcher()->RemoveRootWindowObserver(this);
}
......@@ -712,104 +769,35 @@ class WebContentsViewAura::WindowObserver
SendScreenRects();
}
WebContentsViewAura* view_;
// We cache the old parent so that we can unregister when it's not the parent
// anymore.
aura::Window* parent_;
DISALLOW_COPY_AND_ASSIGN(WindowObserver);
};
#if defined(OS_WIN)
// Constrained windows are added as children of the WebContent's view which may
// overlap with windowed NPAPI plugins. In that case, tell the RWHV so that it
// can update the plugins' cutout rects accordingly.
class WebContentsViewAura::ChildWindowObserver : public aura::WindowObserver,
public WebContentsObserver {
public:
explicit ChildWindowObserver(WebContentsViewAura* view)
: WebContentsObserver(view->web_contents_),
view_(view),
web_contents_destroyed_(false) {
view_->window_->AddObserver(this);
}
virtual ~ChildWindowObserver() {
view_->window_->RemoveObserver(this);
const aura::Window::Windows& children = view_->window_->children();
for (size_t i = 0; i < children.size(); ++i)
children[i]->RemoveObserver(this);
}
// Overridden from aura::WindowObserver:
virtual void OnWindowAdded(aura::Window* new_window) OVERRIDE {
// If new child windows are added to the WebContent's view, tell the RWHV.
// We also start watching them to know when their size is updated. Of
// course, ignore the shadow window that contains the RWHV and child windows
// of the child windows that we are watching.
RenderWidgetHostViewAura* view = ToRenderWidgetHostViewAura(
view_->web_contents_->GetRenderWidgetHostView());
aura::Window* content_window = view ? view->GetNativeView() : NULL;
if (new_window->parent() == view_->window_ &&
new_window != content_window) {
new_window->AddObserver(this);
UpdateConstrainedWindows(NULL);
}
}
virtual void OnWillRemoveWindow(aura::Window* window) OVERRIDE {
RenderWidgetHostViewAura* view = ToRenderWidgetHostViewAura(
view_->web_contents_->GetRenderWidgetHostView());
aura::Window* content_window = view ? view->GetNativeView() : NULL;
if (window->parent() == view_->window_ &&
window != content_window) {
window->RemoveObserver(this);
UpdateConstrainedWindows(window);
}
}
virtual void OnWindowBoundsChanged(aura::Window* window,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds) OVERRIDE {
if (window->parent() == view_->window_ &&
window != view_->GetContentNativeView()) {
UpdateConstrainedWindows(NULL);
}
}
// Overridden from WebContentsObserver:
virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE {
web_contents_destroyed_ = true;
}
private:
void UpdateConstrainedWindows(aura::Window* exclude) {
if (web_contents_destroyed_)
return;
RenderWidgetHostViewAura* view = ToRenderWidgetHostViewAura(
view_->web_contents_->GetRenderWidgetHostView());
if (!view)
return;
std::vector<gfx::Rect> constrained_windows;
const aura::Window::Windows& children = view_->window_->children();
aura::Window* content = view_->GetContentNativeView();
const aura::Window::Windows& children = parent_->children();
for (size_t i = 0; i < children.size(); ++i) {
if (children[i] != content && children[i] != exclude)
if (children[i] != view_->window_ &&
children[i] != exclude &&
children[i]->IsVisible()) {
constrained_windows.push_back(children[i]->GetBoundsInRootWindow());
}
}
view->UpdateConstrainedWindowRects(constrained_windows);
}
#endif
WebContentsViewAura* view_;
bool web_contents_destroyed_;
DISALLOW_COPY_AND_ASSIGN(ChildWindowObserver);
// We cache the old parent so that we can unregister when it's not the parent
// anymore.
aura::Window* parent_;
DISALLOW_COPY_AND_ASSIGN(WindowObserver);
};
#endif
////////////////////////////////////////////////////////////////////////////////
// WebContentsViewAura, public:
......@@ -836,9 +824,7 @@ WebContentsViewAura::~WebContentsViewAura() {
return;
window_observer_.reset();
#if defined(OS_WIN)
child_window_observer_.reset();
#endif
// Window needs a valid delegate during its destructor, so we explicitly
// delete it here.
window_.reset();
......@@ -1169,9 +1155,6 @@ void WebContentsViewAura::CreateView(
window_->SetName("WebContentsViewAura");
window_observer_.reset(new WindowObserver(this));
#if defined(OS_WIN)
child_window_observer_.reset(new ChildWindowObserver(this));
#endif
// delegate_->GetDragDestDelegate() creates a new delegate on every call.
// Hence, we save a reference to it locally. Similar model is used on other
......
......@@ -50,9 +50,6 @@ class CONTENT_EXPORT WebContentsViewAura
private:
class WindowObserver;
#if defined(OS_WIN)
class ChildWindowObserver;
#endif
virtual ~WebContentsViewAura();
......@@ -193,9 +190,6 @@ class CONTENT_EXPORT WebContentsViewAura
scoped_ptr<aura::Window> overscroll_window_;
scoped_ptr<WindowObserver> window_observer_;
#if defined(OS_WIN)
scoped_ptr<ChildWindowObserver> child_window_observer_;
#endif
// The WebContentsImpl whose contents we display.
WebContentsImpl* web_contents_;
......
......@@ -97,7 +97,8 @@ PluginTest* CreatePluginTest(const std::string& test_name,
} else if (test_name == "hidden_plugin" ||
test_name == "create_instance_in_paint" ||
test_name == "alert_in_window_message" ||
test_name == "ensure_scripting_works_in_destroy") {
test_name == "ensure_scripting_works_in_destroy" ||
test_name == "set_title_in_paint") {
new_test = new WindowedPluginTest(instance, host_functions);
#endif
} else if (test_name == "setup") {
......
......@@ -38,7 +38,8 @@ NPError WindowedPluginTest::SetWindow(NPWindow* pNPWindow) {
}
if ((test_name() == "create_instance_in_paint" && test_id() == "1") ||
test_name() == "alert_in_window_message") {
test_name() == "alert_in_window_message" ||
test_name() == "set_title_in_paint") {
static ATOM window_class = 0;
if (!window_class) {
WNDCLASSEX wcex;
......@@ -133,6 +134,10 @@ LRESULT CALLBACK WindowedPluginTest::WindowProc(
// and verify that we don't hang the browser.
CallJSFunction(this_ptr, "CallAlert");
CallJSFunction(this_ptr, "CallAlert");
} else if (this_ptr->test_name() == "set_title_in_paint" &&
message == WM_PAINT) {
this_ptr->done_ = true;
CallJSFunction(this_ptr, "SetTitle");
}
if (this_ptr->done_) {
......
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