diff options
author | joi@chromium.org <joi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-03 00:49:04 +0000 |
---|---|---|
committer | joi@chromium.org <joi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-03 00:49:04 +0000 |
commit | 0d5d44b1e5043239085de5e5ea9ca4d6854bce45 (patch) | |
tree | 6bb5d458366e7975c8820efbc6f21b660d583321 | |
parent | 58b48978267e72777344231c4680e68aa94c6add (diff) | |
download | chromium_src-0d5d44b1e5043239085de5e5ea9ca4d6854bce45.zip chromium_src-0d5d44b1e5043239085de5e5ea9ca4d6854bce45.tar.gz chromium_src-0d5d44b1e5043239085de5e5ea9ca4d6854bce45.tar.bz2 |
Allow experimental extension pop-ups from infobars.
BUG=none
TEST=browser_tests.exe --gtest_filter=*PopupFromInfobar*
Review URL: http://codereview.chromium.org/3127037
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@58442 0039d316-1c4b-4281-b951-d872f2087c98
14 files changed, 566 insertions, 4 deletions
diff --git a/chrome/browser/extensions/extension_popup_api.cc b/chrome/browser/extensions/extension_popup_api.cc index 7fc8f1b..3e4f22a 100644 --- a/chrome/browser/extensions/extension_popup_api.cc +++ b/chrome/browser/extensions/extension_popup_api.cc @@ -356,10 +356,11 @@ void PopupShowFunction::Run() { } bool PopupShowFunction::RunImpl() { - // Popups may only be displayed from TAB_CONTENTS. + // Popups may only be displayed from TAB_CONTENTS and EXTENSION_INFOBAR. ViewType::Type view_type = dispatcher()->render_view_host()->delegate()->GetRenderViewType(); - if (ViewType::TAB_CONTENTS != view_type) { + if (ViewType::TAB_CONTENTS != view_type && + ViewType::EXTENSION_INFOBAR != view_type) { error_ = kPopupsDisallowed; return false; } diff --git a/chrome/browser/extensions/extension_popup_apitest.cc b/chrome/browser/extensions/extension_popup_apitest.cc index 6021f00..83f5717 100644 --- a/chrome/browser/extensions/extension_popup_apitest.cc +++ b/chrome/browser/extensions/extension_popup_apitest.cc @@ -11,5 +11,12 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, FLAKY_Popup) { CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableExperimentalExtensionApis); - ASSERT_TRUE(RunExtensionTest("popup")) << message_; + ASSERT_TRUE(RunExtensionTest("popup/popup_main")) << message_; +} + +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, PopupFromInfobar) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableExperimentalExtensionApis); + + ASSERT_TRUE(RunExtensionTest("popup/popup_from_infobar")) << message_; } diff --git a/chrome/renderer/extensions/extension_process_bindings.cc b/chrome/renderer/extensions/extension_process_bindings.cc index 340502f..ecbdbf2 100644 --- a/chrome/renderer/extensions/extension_process_bindings.cc +++ b/chrome/renderer/extensions/extension_process_bindings.cc @@ -276,6 +276,7 @@ class ExtensionImpl : public ExtensionBase { return v8::Undefined(); if (viewtype_to_find != ViewType::EXTENSION_POPUP && + viewtype_to_find != ViewType::EXTENSION_INFOBAR && viewtype_to_find != ViewType::TAB_CONTENTS) { NOTREACHED() << "Requesting invalid view type."; } @@ -311,7 +312,11 @@ class ExtensionImpl : public ExtensionBase { } static v8::Handle<v8::Value> GetPopupParentWindow(const v8::Arguments& args) { - return PopupViewFinder(args, ViewType::TAB_CONTENTS); + v8::Handle<v8::Value> view = PopupViewFinder(args, ViewType::TAB_CONTENTS); + if (view == v8::Undefined()) { + view = PopupViewFinder(args, ViewType::EXTENSION_INFOBAR); + } + return view; } static v8::Handle<v8::Value> GetExtensionViews(const v8::Arguments& args) { diff --git a/chrome/test/data/extensions/api_test/popup/popup_from_infobar/background.html b/chrome/test/data/extensions/api_test/popup/popup_from_infobar/background.html new file mode 100644 index 0000000..62b4e16 --- /dev/null +++ b/chrome/test/data/extensions/api_test/popup/popup_from_infobar/background.html @@ -0,0 +1,29 @@ +<script> +var pass = chrome.test.callbackPass; + +chrome.test.runTests([ + function testShowsAndCanGetParent() { + chrome.tabs.getSelected(null, function(tab) { + chrome.experimental.infobars.show( + {tabId: tab.id, path:'in-infobar.html'}); + }); + // Flow continues in infobarCallback + } +]); + +function infobarCallback(showPopupFunc) { + showPopupFunc(); + // Flow continues in popupCallback +} + +function popupCallback(popupWindow, getFromParentFunc) { + getFromParentFunc(); + var result = popupWindow.document.getElementById('target').innerText; + if (!result) { + chrome.test.fail('no result'); + } + chrome.test.assertEq(result, '42'); + chrome.test.notifyPass(); +} + +</script> diff --git a/chrome/test/data/extensions/api_test/popup/popup_from_infobar/in-infobar.html b/chrome/test/data/extensions/api_test/popup/popup_from_infobar/in-infobar.html new file mode 100644 index 0000000..8ea0fb8 --- /dev/null +++ b/chrome/test/data/extensions/api_test/popup/popup_from_infobar/in-infobar.html @@ -0,0 +1,25 @@ +<html> +<head> +<script> +window.magic = 42; + +function showPopup() { + var anchor = document.getElementById('anchor'); + if (!anchor) { + chrome.test.fail('no anchor'); + } + chrome.experimental.popup.show( + 'in-popup.html', + {relativeTo: anchor}); +} + +function init() { + chrome.extension.getBackgroundPage().infobarCallback(showPopup); +} + +</script> +</head> +<body onload='init()'> +<div id='anchor'>anchor</div> +</body> +</html> diff --git a/chrome/test/data/extensions/api_test/popup/popup_from_infobar/in-popup.html b/chrome/test/data/extensions/api_test/popup/popup_from_infobar/in-popup.html new file mode 100644 index 0000000..b52ce9e --- /dev/null +++ b/chrome/test/data/extensions/api_test/popup/popup_from_infobar/in-popup.html @@ -0,0 +1,22 @@ +<html> +<head> +<script> + +function getFromParent() { + var target = document.getElementById('target'); + if (!target) { + chrome.test.fail('no target'); + } + target.innerText = chrome.experimental.popup.getParentWindow().magic; +} + +function init() { + chrome.extension.getBackgroundPage().popupCallback(window, getFromParent); +} + +</script> +</head> +<body onload="init()"> +<span id='target'></span> +</body> +</html> diff --git a/chrome/test/data/extensions/api_test/popup/popup_from_infobar/manifest.json b/chrome/test/data/extensions/api_test/popup/popup_from_infobar/manifest.json new file mode 100644 index 0000000..d386cb6 --- /dev/null +++ b/chrome/test/data/extensions/api_test/popup/popup_from_infobar/manifest.json @@ -0,0 +1,8 @@ +{ + "name": "Extension API test for chrome.experimental.popups from infobars.", + "version": "1.0", + "background_page": "background.html", + "permissions": [ + "experimental", "tabs" + ] +} diff --git a/chrome/test/data/extensions/api_test/popup/popup_main/background_page.html b/chrome/test/data/extensions/api_test/popup/popup_main/background_page.html new file mode 100644 index 0000000..aec295a --- /dev/null +++ b/chrome/test/data/extensions/api_test/popup/popup_main/background_page.html @@ -0,0 +1,10 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<script> +window.onload = function() { + // Create a tab in which the popup tests can execute. + chrome.tabs.create({ "url": "dom_ui.html" }); +} +</script> +</head> +</html> diff --git a/chrome/test/data/extensions/api_test/popup/popup_main/dom_ui.html b/chrome/test/data/extensions/api_test/popup/popup_main/dom_ui.html new file mode 100644 index 0000000..cf1e58d --- /dev/null +++ b/chrome/test/data/extensions/api_test/popup/popup_main/dom_ui.html @@ -0,0 +1,252 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<script> +// A token assigned to the global context of this script so that subsequently +// created tabs/views may find this view. +var TESTING_TOKEN = true; + +var globalValue = "I am not 42."; + +// Some helper functions that track the focus state of a form on the toolbar. +var formFocused = false; +function onFormFocused() { + formFocused = true; +} + +function onFormBlurred() { + formFocused = false; +} + +// Callback that validates popup repositioning, and is invoked during execution +// of the following tests: +// popupRectangleSizing and popupChromeSizing. +// |offset| specifies the delta in screen-space by which the browser was moved. +// |initialSize| specfies the rect of the popup before the brower move. +// |movedSize| specifies the rect of the popup after the browser move. +function onWindowMoveCompleted(offset, initialSize, movedSize) { + chrome.test.assertEq(initialSize.width, movedSize.width); + chrome.test.assertEq(initialSize.height, movedSize.height); + chrome.test.assertTrue( + initialSize.top + offset.y == movedSize.top && + initialSize.left + offset.x == movedSize.left, + "Popup repositioned incorrectly after browser move."); +} + +// Assert function used by tests executed in separate extension views. +// Used by the following test: popupTeardownDismissal +// |value| is value upon which to assert. +// |message| is displayed if |value| is false. +function assertTrue(value, message) { + chrome.test.assertTrue(value, message); +} + +// Function used to signal completion of tests run in separate extension views. +// Used by the following test: popupTeardownDismissal +function testCompleted() { + chrome.test.succeed(); +} + +window.onload = function() { + chrome.test.runTests([ + function showNoFocusShift() { + var entryForm = document.getElementById("entryForm").focus(); + chrome.test.assertTrue(formFocused); + + // Validate that displaying a pop-up with the giveFocus parameter assigned + // to false does not touch the focus setting of the input field. + var showDetails = { + "relativeTo": document.getElementById("anchorHere"), + "giveFocus": false + }; + + // The focus should also remain untouched during closing of the popup. + chrome.test.listenOnce(chrome.experimental.popup.onClosed, function() { + chrome.test.assertTrue(formFocused); + }); + + chrome.experimental.popup.show("dom_ui_popup.html", + showDetails, + chrome.test.callbackPass(function() { + chrome.test.assertTrue(formFocused); + chrome.experimental.extension.getPopupView().close(); + })); + }, + function noPopup() { + chrome.test.assertTrue( + undefined === chrome.experimental.extension.getPopupView(), + "Popup view is defined when no popup shown."); + chrome.test.succeed(); + }, + function noParentWindow() { + chrome.test.assertTrue( + undefined === chrome.experimental.popup.getParentWindow(), + "Parent window accessible outside of popup view."); + chrome.test.succeed(); + }, + function show() { + var showDetails = { + "relativeTo": document.getElementById("anchorHere") + }; + chrome.experimental.popup.show("dom_ui_popup.html", + showDetails, + chrome.test.callbackPass(function() { + chrome.test.assertTrue( + chrome.experimental.extension.getPopupView() != undefined); + })); + }, + function accessPopup() { + var popupView = chrome.experimental.extension.getPopupView(); + chrome.test.assertTrue(popupView != undefined, + "Unable to access popup view."); + + chrome.test.assertTrue(popupView.theAnswer != undefined, + "Unable to access popup contents."); + + chrome.test.assertEq(42, popupView.theAnswer()); + chrome.test.succeed(); + }, + function accessHost() { + var popupView = chrome.experimental.extension.getPopupView(); + chrome.test.assertTrue(popupView != undefined, + "Unable to access popup view."); + + chrome.test.assertTrue(popupView.manipulateHost != undefined, + "Unable to access popup contents."); + + popupView.manipulateHost(); + chrome.test.assertEq(42, globalValue); + chrome.test.succeed(); + }, + function closePopup() { + // Ensure that the test waits until the popup is dismissed. + chrome.test.listenOnce(chrome.experimental.popup.onClosed, function() { + // The popup should not be accessible during the onClosed handler. + chrome.test.assertTrue( + chrome.experimental.extension.getPopupView() == undefined); + }); + chrome.experimental.extension.getPopupView().close(); + }, + function popupBlackBorder() { + // Ensure that the test waits until the popup is dismissed. + chrome.test.listenOnce(chrome.experimental.popup.onClosed); + + // Validate that displaying a pop-up with a black border still invokes + // the callback successfully. Note that this test does not validate + // the actual style of the border displayed. + var showDetails = { + "relativeTo": document.getElementById("anchorHere"), + "borderStyle": "rectangle" + }; + chrome.experimental.popup.show("dom_ui_popup.html", + showDetails, + chrome.test.callbackPass(function() { + chrome.experimental.extension.getPopupView().close(); + })); + }, + function disallowMultiplePopups() { + // This test ensures that for a given extension with a popup displayed, + // displaying a subsequent popup will dismiss the first. + var showDetails1 = { + "relativeTo": document.getElementById("anchorHere"), + }; + + var showDetails2 = { + "relativeTo": document.getElementById("anchorHere2"), + "borderStyle": "rectangle" + }; + + // Track the number of popups opened and closed, so that we can signal + // the test as completed when appropriate. + var numberClosed = 0; + var doneListening = chrome.test.listenForever( + chrome.experimental.popup.onClosed, + function() { + // This test expects to open and close two popups, so signify that + // the test has succeeded, after closing the second popup. + if (++numberClosed == 2) { + doneListening(); + } + }); + + chrome.experimental.popup.show("dom_ui_popup_a.html", + showDetails1, + function() { + // Validate that the popup view returned is the one we expect. + chrome.test.assertEq( + 'a', + chrome.experimental.extension.getPopupView().getIdentity()); + + // Ensure that only one popup is open. + chrome.test.assertEq( + 1, + chrome.extension.getViews({type: "popup"}).length); + + chrome.experimental.popup.show("dom_ui_popup_b.html", + showDetails2, + function() { + // Validate that the first popup view is fully closed, and that + // getPopupView returns the most recently opened popup. + chrome.test.assertEq( + 'b', + chrome.experimental.extension.getPopupView().getIdentity()); + + // Ensure that only one popup is open. + chrome.test.assertEq( + 1, + chrome.extension.getViews({type: 'popup'}).length); + + chrome.experimental.extension.getPopupView().close(); + }); + }); + }, + function popupChromeSizing() { + // Ensure that the test waits until the popup is dismissed. + chrome.test.listenOnce(chrome.experimental.popup.onClosed); + + // Ensure that popups with a chrome border are repositioned and sized + // correctly. + var showDetails = { + "relativeTo": document.getElementById("anchorHere") + }; + + chrome.experimental.popup.show("dom_ui_popup_sizing.html", + showDetails); + }, + function popupRectangleSizing() { + // Ensure that the test waits until the popup is dismissed. + chrome.test.listenOnce(chrome.experimental.popup.onClosed); + + // Ensure that popups with a rectangle border are repositioned and sized + // correctly. + var showDetails = { + "relativeTo": document.getElementById("anchorHere"), + "borderStyle": "rectangle" + }; + + chrome.experimental.popup.show("dom_ui_popup_sizing.html", + showDetails); + }, + function popupTeardownDismissal() { + // This test verifies that closing of views that launched active popups + // results in a popup dismissal. + var tabProperties = { + "url": "dom_ui_popup_dismissal.html" + }; + chrome.tabs.create(tabProperties); + } + ]); +} +</script> +</head> +<body> +<div id="anchorHere"> +<span>TEST</span> +</div> +<div id="anchorHere2"> +<span>TESTING 2</span> +</div> +<form> +<input id="entryForm" onfocus="onFormFocused();" onblur="onFormBlurred();"/> +</form> +</body> +</html> diff --git a/chrome/test/data/extensions/api_test/popup/popup_main/dom_ui_popup.html b/chrome/test/data/extensions/api_test/popup/popup_main/dom_ui_popup.html new file mode 100644 index 0000000..67d4cdf --- /dev/null +++ b/chrome/test/data/extensions/api_test/popup/popup_main/dom_ui_popup.html @@ -0,0 +1,19 @@ +<html> +<head> +<script> +function theAnswer() { + return 42; +} + +function manipulateHost() { + var popupHost = chrome.experimental.popup.getParentWindow(); + if (popupHost && popupHost.globalValue) { + popupHost.globalValue = 42; + } +} +</script> +</head> +<body> +Popup-Contents +</body> +</html>
\ No newline at end of file diff --git a/chrome/test/data/extensions/api_test/popup/popup_main/dom_ui_popup_a.html b/chrome/test/data/extensions/api_test/popup/popup_main/dom_ui_popup_a.html new file mode 100644 index 0000000..621fbec --- /dev/null +++ b/chrome/test/data/extensions/api_test/popup/popup_main/dom_ui_popup_a.html @@ -0,0 +1,24 @@ +<html> +<head> +<script> +function getIdentity() { + return 'a'; +} +</script> +</head> +<body> +Popup-A-Contents +</body> +</html> +<html> +<head> +<script> +function getIdentity() { + return 'a'; +} +</script> +</head> +<body> +Popup-A-Contents +</body> +</html>
\ No newline at end of file diff --git a/chrome/test/data/extensions/api_test/popup/popup_main/dom_ui_popup_b.html b/chrome/test/data/extensions/api_test/popup/popup_main/dom_ui_popup_b.html new file mode 100644 index 0000000..5dd9792 --- /dev/null +++ b/chrome/test/data/extensions/api_test/popup/popup_main/dom_ui_popup_b.html @@ -0,0 +1,24 @@ +<html> +<head> +<script> +function getIdentity() { + return 'b'; +} +</script> +</head> +<body> +Popup-B-Contents +</body> +</html> +<html> +<head> +<script> +function getIdentity() { + return 'b'; +} +</script> +</head> +<body> +Popup-B-Contents +</body> +</html>
\ No newline at end of file diff --git a/chrome/test/data/extensions/api_test/popup/popup_main/dom_ui_popup_dismissal.html b/chrome/test/data/extensions/api_test/popup/popup_main/dom_ui_popup_dismissal.html new file mode 100644 index 0000000..5ba2839 --- /dev/null +++ b/chrome/test/data/extensions/api_test/popup/popup_main/dom_ui_popup_dismissal.html @@ -0,0 +1,67 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<script> +var onbeforeunloadInvoked= false; +var onunloadInvoked = false; +var popupDismissed = false; +var testingWindow = locateTestingWindow(); + +// Helper function that locates the main tab that is being used +// to drive the popup tests. +function locateTestingWindow() { + var extensionViews = chrome.extension.getViews(); + for (var i = 0; i < extensionViews.length; ++i) { + // Look for the magic token defined on the testing page. + if (typeof extensionViews[i].TESTING_TOKEN != "undefined" && + !!extensionViews[i].TESTING_TOKEN) { + return extensionViews[i]; + } + } + return null; +} + +// Onload handler that tests the popup dismissal behaviour when closing the +// current tab. A popup is launched and the timing of the onClosed callback +// is tested wrt the onbeforeunload and onunload callbacks. +window.onload = function() { + chrome.experimental.popup.onClosed.addListener(function() { + testingWindow.assertTrue(onbeforeunloadInvoked, + "Popup dismissed before onbeforeunload called."); + testingWindow.assertTrue(!onunloadInvoked, + "Popup dismissed after onunload called."); + popupDismissed = true; + }); + + var showDetails = { + "relativeTo": document.getElementById("popupAnchor") + }; + chrome.experimental.popup.show("dom_ui_popup.html", + showDetails, + function() { + chrome.tabs.getSelected(null, function(tab) { + chrome.tabs.remove(tab.id); + }); + }); +} + +window.onbeforeunload = function() { + onbeforeunloadInvoked = true; +} + +window.onunload = function() { + onunloadInvoked = true; + + // If the popup was not yet dismissed, do not signal that the test has + // completed. Let the test time-out to signal failure. + if (popupDismissed) + testingWindow.testCompleted(); +}; +</script> +</head> +<body> +Testing Popup Sizing +<div id='popupAnchor'> +<span>Anchor Temporary Popup Here</span> +</div> +</body> +</html> diff --git a/chrome/test/data/extensions/api_test/popup/popup_main/dom_ui_popup_sizing.html b/chrome/test/data/extensions/api_test/popup/popup_main/dom_ui_popup_sizing.html new file mode 100644 index 0000000..29fe0f2 --- /dev/null +++ b/chrome/test/data/extensions/api_test/popup/popup_main/dom_ui_popup_sizing.html @@ -0,0 +1,69 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<script> +// Returns the current size of the displayed popup view. +function getCurrentWindowSize() { + return { + "top": window.screenTop, + "left": window.screenLeft, + "width": window.outerWidth, + "height": window.outerHeight + }; +} + +// Utility that captures the size of the popup window before and after a browser +// move and notifies the test's parent view of the respective sizes. +function doSizingValidation() { + var initialSize = getCurrentWindowSize(); + + // Move the browser, and ensure that the popup is repositioned correctly, + // and retains its proper size. + var offset = {'x': 5, 'y': 5}; + chrome.windows.getCurrent(function(browserWindow) { + chrome.windows.update(browserWindow.id, + { + "left": browserWindow.left + offset.x, + "top": browserWindow.top + offset.y + }, + function(UpdatedWindow) { + // Yield so that the window move notification may be processed before + // invoking the callback. This is required because chrome.windows.update + // calls its callback in a race with the windows message that repositions + // the browser. + // TODO: Fix this race condition so that the update callback is invoked + // after all of the update machinery has been invoked. + var updatePoller = setInterval(function() { + var newPosition = getCurrentWindowSize(); + if (newPosition.top != initialSize.top) { + clearInterval(updatePoller); + chrome.experimental.popup.getParentWindow().onWindowMoveCompleted( + offset, + initialSize, + newPosition); + window.close(); + } + }, 50); + }); + }); +} + +window.onload = function() { + // Delay invocation of the sizing test so that layout of the popup may + // complete. On windows, onload is called before layout has been performed, + // so window.screenTop, and the other fields used in getCurrentWindowSize will + // return 0 until the layout has been performed. + // TODO: Fix the order of the onload and layout processing. + var positionPoller = setInterval(function() { + var initialSize = getCurrentWindowSize(); + if (initialSize.width != 0) { + clearInterval(positionPoller); + doSizingValidation(); + } + }, 50); +} +</script> +</head> +<body style='width:128px; height:128px'> +Testing Popup Sizing +</body> +</html> |