summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/extensions/api/DEPS1
-rw-r--r--chrome/browser/extensions/api/sessions/sessions_api.cc4
-rw-r--r--chrome/browser/extensions/api/tabs/tabs_api.cc97
-rw-r--r--chrome/browser/extensions/api/tabs/tabs_constants.cc1
-rw-r--r--chrome/browser/extensions/api/tabs/tabs_constants.h1
-rw-r--r--chrome/browser/extensions/api/tabs/tabs_test.cc547
-rw-r--r--chrome/browser/extensions/api/tabs/windows_event_router.cc18
-rw-r--r--chrome/browser/extensions/api/tabs/windows_util.cc18
-rw-r--r--chrome/browser/extensions/api/tabs/windows_util.h15
-rw-r--r--chrome/browser/extensions/extension_tab_util.cc5
-rw-r--r--chrome/browser/extensions/window_controller.cc34
-rw-r--r--chrome/browser/extensions/window_controller.h19
-rw-r--r--chrome/browser/extensions/window_controller_list.cc38
-rw-r--r--chrome/browser/extensions/window_controller_list.h18
-rw-r--r--chrome/browser/ui/panels/panel.cc2
-rw-r--r--chrome/common/extensions/api/windows.json36
-rw-r--r--chrome/test/data/extensions/api_test/windows/events/background.html6
-rw-r--r--chrome/test/data/extensions/api_test/windows/events/manifest.json9
-rw-r--r--chrome/test/data/extensions/api_test/windows/events/test.js82
-rw-r--r--extensions/common/event_filtering_info.cc15
-rw-r--r--extensions/common/event_filtering_info.h11
-rw-r--r--extensions/common/event_matcher.cc38
-rw-r--r--extensions/common/event_matcher.h3
-rw-r--r--extensions/renderer/event_bindings.cc6
24 files changed, 925 insertions, 99 deletions
diff --git a/chrome/browser/extensions/api/DEPS b/chrome/browser/extensions/api/DEPS
index 03792c6..059fc1c 100644
--- a/chrome/browser/extensions/api/DEPS
+++ b/chrome/browser/extensions/api/DEPS
@@ -9,6 +9,7 @@ include_rules = [
specific_include_rules = {
".*test.*": [
+ "+chrome/browser/ui/views/frame",
"+components/captive_portal",
],
}
diff --git a/chrome/browser/extensions/api/sessions/sessions_api.cc b/chrome/browser/extensions/api/sessions/sessions_api.cc
index 35131e3..c46f0e4 100644
--- a/chrome/browser/extensions/api/sessions/sessions_api.cc
+++ b/chrome/browser/extensions/api/sessions/sessions_api.cc
@@ -424,7 +424,9 @@ void SessionsRestoreFunction::SetResultRestoredTab(
bool SessionsRestoreFunction::SetResultRestoredWindow(int window_id) {
WindowController* controller = NULL;
- if (!windows_util::GetWindowFromWindowID(this, window_id, &controller)) {
+ if (!windows_util::GetWindowFromWindowID(
+ this, window_id, WindowController::GetDefaultWindowFilter(),
+ &controller)) {
// error_ is set by GetWindowFromWindowId function call.
return false;
}
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index a2d5aab..f46760c 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -116,6 +116,29 @@ using api::extension_types::InjectDetails;
namespace {
+template <typename T>
+class ApiParameterExtractor {
+ public:
+ explicit ApiParameterExtractor(T* params) : params_(params) {}
+ ~ApiParameterExtractor() {}
+
+ bool populate_tabs() {
+ if (params_->get_info.get() && params_->get_info->populate.get())
+ return *params_->get_info->populate;
+ return false;
+ }
+
+ WindowController::TypeFilter type_filters() {
+ if (params_->get_info.get() && params_->get_info->window_types.get())
+ return WindowController::GetFilterFromWindowTypes(
+ *params_->get_info->window_types.get());
+ return WindowController::GetDefaultWindowFilter();
+ }
+
+ private:
+ T* params_;
+};
+
bool GetBrowserFromWindowID(ChromeUIThreadExtensionFunction* function,
int window_id,
Browser** browser) {
@@ -265,18 +288,14 @@ bool WindowsGetFunction::RunSync() {
scoped_ptr<windows::Get::Params> params(windows::Get::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());
- bool populate_tabs = false;
- if (params->get_info.get() && params->get_info->populate.get())
- populate_tabs = *params->get_info->populate;
-
+ ApiParameterExtractor<windows::Get::Params> extractor(params.get());
WindowController* controller;
- if (!windows_util::GetWindowFromWindowID(this,
- params->window_id,
- &controller)) {
+ if (!windows_util::GetWindowFromWindowID(
+ this, params->window_id, extractor.type_filters(), &controller)) {
return false;
}
- if (populate_tabs)
+ if (extractor.populate_tabs())
SetResult(controller->CreateWindowValueWithTabs(extension()));
else
SetResult(controller->CreateWindowValue());
@@ -288,17 +307,14 @@ bool WindowsGetCurrentFunction::RunSync() {
windows::GetCurrent::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());
- bool populate_tabs = false;
- if (params->get_info.get() && params->get_info->populate.get())
- populate_tabs = *params->get_info->populate;
-
+ ApiParameterExtractor<windows::GetCurrent::Params> extractor(params.get());
WindowController* controller;
- if (!windows_util::GetWindowFromWindowID(this,
- extension_misc::kCurrentWindowId,
- &controller)) {
+ if (!windows_util::GetWindowFromWindowID(
+ this, extension_misc::kCurrentWindowId, extractor.type_filters(),
+ &controller)) {
return false;
}
- if (populate_tabs)
+ if (extractor.populate_tabs())
SetResult(controller->CreateWindowValueWithTabs(extension()));
else
SetResult(controller->CreateWindowValue());
@@ -310,22 +326,24 @@ bool WindowsGetLastFocusedFunction::RunSync() {
windows::GetLastFocused::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());
- bool populate_tabs = false;
- if (params->get_info.get() && params->get_info->populate.get())
- populate_tabs = *params->get_info->populate;
-
- // Note: currently this returns the last active browser. If we decide to
- // include other window types (e.g. panels), we will need to add logic to
- // WindowControllerList that mirrors the active behavior of BrowserList.
- Browser* browser = chrome::FindAnyBrowser(
- GetProfile(), include_incognito(), chrome::GetActiveDesktop());
- if (!browser || !browser->window()) {
+ ApiParameterExtractor<windows::GetLastFocused::Params> extractor(
+ params.get());
+ // The WindowControllerList should contain a list of application,
+ // browser and devtools windows.
+ WindowController* controller = nullptr;
+ for (auto iter : WindowControllerList::GetInstance()->windows()) {
+ if (windows_util::CanOperateOnWindow(this, iter,
+ extractor.type_filters())) {
+ controller = iter;
+ if (controller->window()->IsActive())
+ break; // Use focused window.
+ }
+ }
+ if (!controller) {
error_ = keys::kNoLastFocusedWindowError;
return false;
}
- WindowController* controller =
- browser->extension_window_controller();
- if (populate_tabs)
+ if (extractor.populate_tabs())
SetResult(controller->CreateWindowValueWithTabs(extension()));
else
SetResult(controller->CreateWindowValue());
@@ -337,19 +355,17 @@ bool WindowsGetAllFunction::RunSync() {
windows::GetAll::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());
- bool populate_tabs = false;
- if (params->get_info.get() && params->get_info->populate.get())
- populate_tabs = *params->get_info->populate;
-
+ ApiParameterExtractor<windows::GetAll::Params> extractor(params.get());
base::ListValue* window_list = new base::ListValue();
const WindowControllerList::ControllerList& windows =
WindowControllerList::GetInstance()->windows();
for (WindowControllerList::ControllerList::const_iterator iter =
windows.begin();
iter != windows.end(); ++iter) {
- if (!windows_util::CanOperateOnWindow(this, *iter))
+ if (!windows_util::CanOperateOnWindow(this, *iter,
+ extractor.type_filters()))
continue;
- if (populate_tabs)
+ if (extractor.populate_tabs())
window_list->Append((*iter)->CreateWindowValueWithTabs(extension()));
else
window_list->Append((*iter)->CreateWindowValue());
@@ -693,9 +709,11 @@ bool WindowsUpdateFunction::RunSync() {
EXTENSION_FUNCTION_VALIDATE(params);
WindowController* controller;
- if (!windows_util::GetWindowFromWindowID(this, params->window_id,
- &controller))
+ if (!windows_util::GetWindowFromWindowID(
+ this, params->window_id, WindowController::GetAllWindowFilter(),
+ &controller)) {
return false;
+ }
ui::WindowShowState show_state =
ConvertToWindowShowState(params->update_info.state);
@@ -795,8 +813,9 @@ bool WindowsRemoveFunction::RunSync() {
EXTENSION_FUNCTION_VALIDATE(params);
WindowController* controller;
- if (!windows_util::GetWindowFromWindowID(this, params->window_id,
- &controller))
+ if (!windows_util::GetWindowFromWindowID(
+ this, params->window_id, WindowController::GetDefaultWindowFilter(),
+ &controller))
return false;
WindowController::Reason reason;
diff --git a/chrome/browser/extensions/api/tabs/tabs_constants.cc b/chrome/browser/extensions/api/tabs/tabs_constants.cc
index 68f5b04..c1994b0 100644
--- a/chrome/browser/extensions/api/tabs/tabs_constants.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_constants.cc
@@ -9,6 +9,7 @@ namespace tabs_constants {
const char kActiveKey[] = "active";
const char kAllFramesKey[] = "allFrames";
+const char kAllWindowTypesKey[] = "allWindowTypes";
const char kAlwaysOnTopKey[] = "alwaysOnTop";
const char kBypassCache[] = "bypassCache";
const char kCodeKey[] = "code";
diff --git a/chrome/browser/extensions/api/tabs/tabs_constants.h b/chrome/browser/extensions/api/tabs/tabs_constants.h
index 02d6c36..44e53da 100644
--- a/chrome/browser/extensions/api/tabs/tabs_constants.h
+++ b/chrome/browser/extensions/api/tabs/tabs_constants.h
@@ -13,6 +13,7 @@ namespace tabs_constants {
// Keys used in serializing tab data & events.
extern const char kActiveKey[];
extern const char kAllFramesKey[];
+extern const char kAllWindowTypesKey[];
extern const char kAlwaysOnTopKey[];
extern const char kBypassCache[];
extern const char kCodeKey[];
diff --git a/chrome/browser/extensions/api/tabs/tabs_test.cc b/chrome/browser/extensions/api/tabs/tabs_test.cc
index 6bb2709..3d86ad1 100644
--- a/chrome/browser/extensions/api/tabs/tabs_test.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_test.cc
@@ -13,6 +13,7 @@
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
+#include "chrome/browser/apps/app_browsertest_util.h"
#include "chrome/browser/devtools/devtools_window_testing.h"
#include "chrome/browser/extensions/api/tabs/tabs_api.h"
#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
@@ -26,6 +27,7 @@
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/zoom/chrome_zoom_level_prefs.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/browser_context.h"
@@ -33,12 +35,17 @@
#include "content/public/common/page_zoom.h"
#include "content/public/common/url_constants.h"
#include "extensions/browser/api_test_utils.h"
+#include "extensions/browser/app_window/app_window.h"
+#include "extensions/browser/app_window/app_window_registry.h"
+#include "extensions/browser/app_window/native_app_window.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/test_util.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/result_catcher.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "ui/gfx/geometry/rect.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_observer.h"
namespace extensions {
@@ -46,7 +53,7 @@ namespace keys = tabs_constants;
namespace utils = extension_function_test_utils;
namespace {
-using ExtensionTabsTest = ExtensionApiTest;
+using ExtensionTabsTest = PlatformAppBrowserTest;
class ExtensionWindowCreateTest : public InProcessBrowserTest {
public:
@@ -84,26 +91,6 @@ int GetWindowId(base::DictionaryValue* window) {
} // namespace
-IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, WindowTypes) {
- Browser* normal_browser = new Browser(Browser::CreateParams(
- browser()->profile(), browser()->host_desktop_type()));
- EXPECT_EQ(keys::kWindowTypeValueNormal,
- normal_browser->extension_window_controller()->GetWindowTypeText());
-
- Browser* popup_browser = new Browser(
- Browser::CreateParams(Browser::TYPE_POPUP, browser()->profile(),
- browser()->host_desktop_type()));
- EXPECT_EQ(keys::kWindowTypeValuePopup,
- popup_browser->extension_window_controller()->GetWindowTypeText());
-
- DevToolsWindow* devtools = DevToolsWindowTesting::OpenDevToolsWindowSync(
- browser()->tab_strip_model()->GetWebContentsAt(0), false /* is_docked */);
- EXPECT_EQ(keys::kWindowTypeValueDevTools, DevToolsWindowTesting::Get(devtools)
- ->browser()
- ->extension_window_controller()
- ->GetWindowTypeText());
-}
-
IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetWindow) {
int window_id = ExtensionTabUtil::GetWindowId(browser());
@@ -203,6 +190,22 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetWindow) {
browser(),
utils::INCLUDE_INCOGNITO)));
EXPECT_TRUE(api_test_utils::GetBoolean(result.get(), "incognito"));
+
+ // DevTools window.
+ DevToolsWindow* devtools = DevToolsWindowTesting::OpenDevToolsWindowSync(
+ browser()->tab_strip_model()->GetWebContentsAt(0), false /* is_docked */);
+
+ function = new WindowsGetFunction();
+ function->set_extension(extension.get());
+ result.reset(utils::ToDictionary(utils::RunFunctionAndReturnSingleResult(
+ function.get(),
+ base::StringPrintf("[%u, {\"windowTypes\": [\"devtools\"]}]",
+ ExtensionTabUtil::GetWindowId(
+ DevToolsWindowTesting::Get(devtools)->browser())),
+ browser(), utils::INCLUDE_INCOGNITO)));
+ EXPECT_EQ("devtools", api_test_utils::GetString(result.get(), "type"));
+
+ DevToolsWindowTesting::CloseDevToolsWindowSync(devtools);
}
IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetCurrentWindow) {
@@ -257,7 +260,12 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetAllWindows) {
window_ids.insert(ExtensionTabUtil::GetWindowId(new_browser));
}
- // Undocked DevTools window should not be accessible.
+ // Application windows should not be accessible, unless allWindowTypes is set
+ // to true.
+ AppWindow* app_window = CreateTestAppWindow("{}");
+
+ // Undocked DevTools window should not be accessible, unless allWindowTypes is
+ // set to true.
DevToolsWindow* devtools = DevToolsWindowTesting::OpenDevToolsWindowSync(
browser()->tab_strip_model()->GetWebContentsAt(0), false /* is_docked */);
@@ -270,8 +278,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetAllWindows) {
browser())));
base::ListValue* windows = result.get();
- EXPECT_EQ(NUM_WINDOWS, windows->GetSize());
- for (size_t i = 0; i < NUM_WINDOWS; ++i) {
+ EXPECT_EQ(window_ids.size(), windows->GetSize());
+ for (size_t i = 0; i < windows->GetSize(); ++i) {
base::DictionaryValue* result_window = nullptr;
EXPECT_TRUE(windows->GetDictionary(i, &result_window));
result_ids.insert(GetWindowId(result_window));
@@ -292,7 +300,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetAllWindows) {
browser())));
windows = result.get();
- EXPECT_EQ(NUM_WINDOWS, windows->GetSize());
+ EXPECT_EQ(window_ids.size(), windows->GetSize());
for (size_t i = 0; i < windows->GetSize(); ++i) {
base::DictionaryValue* result_window = nullptr;
EXPECT_TRUE(windows->GetDictionary(i, &result_window));
@@ -302,10 +310,88 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetAllWindows) {
base::ListValue* tabs = nullptr;
EXPECT_TRUE(result_window->GetList(keys::kTabsKey, &tabs));
}
- // The returned ids should contain all the current browser instance ids.
+ // The returned ids should contain all the current app, browser and
+ // devtools instance ids.
EXPECT_EQ(window_ids, result_ids);
DevToolsWindowTesting::CloseDevToolsWindowSync(devtools);
+
+ CloseAppWindow(app_window);
+}
+
+IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetAllWindowsAllTypes) {
+ const size_t NUM_WINDOWS = 5;
+ std::set<int> window_ids;
+ std::set<int> result_ids;
+ window_ids.insert(ExtensionTabUtil::GetWindowId(browser()));
+
+ for (size_t i = 0; i < NUM_WINDOWS - 1; ++i) {
+ Browser* new_browser = CreateBrowser(browser()->profile());
+ window_ids.insert(ExtensionTabUtil::GetWindowId(new_browser));
+ }
+
+ // Application windows should be accessible.
+ AppWindow* app_window = CreateTestAppWindow("{}");
+ window_ids.insert(app_window->session_id().id());
+
+ // Undocked DevTools window should be accessible too.
+ DevToolsWindow* devtools = DevToolsWindowTesting::OpenDevToolsWindowSync(
+ browser()->tab_strip_model()->GetWebContentsAt(0), false /* is_docked */);
+ window_ids.insert(ExtensionTabUtil::GetWindowId(
+ DevToolsWindowTesting::Get(devtools)->browser()));
+
+ scoped_refptr<WindowsGetAllFunction> function = new WindowsGetAllFunction();
+ scoped_refptr<Extension> extension(test_util::CreateEmptyExtension());
+ function->set_extension(extension.get());
+ scoped_ptr<base::ListValue> result(
+ utils::ToList(utils::RunFunctionAndReturnSingleResult(
+ function.get(),
+ "[{\"windowTypes\": [\"app\", \"devtools\", \"normal\", \"panel\", "
+ "\"popup\"]}]",
+ browser())));
+
+ base::ListValue* windows = result.get();
+ EXPECT_EQ(window_ids.size(), windows->GetSize());
+ for (size_t i = 0; i < windows->GetSize(); ++i) {
+ base::DictionaryValue* result_window = nullptr;
+ EXPECT_TRUE(windows->GetDictionary(i, &result_window));
+ result_ids.insert(GetWindowId(result_window));
+
+ // "populate" was not passed in so tabs are not populated.
+ base::ListValue* tabs = nullptr;
+ EXPECT_FALSE(result_window->GetList(keys::kTabsKey, &tabs));
+ }
+ // The returned ids should contain all the current app, browser and
+ // devtools instance ids.
+ EXPECT_EQ(window_ids, result_ids);
+
+ result_ids.clear();
+ function = new WindowsGetAllFunction();
+ function->set_extension(extension.get());
+ result.reset(utils::ToList(utils::RunFunctionAndReturnSingleResult(
+ function.get(),
+ "[{\"populate\": true, \"windowTypes\": [\"app\", \"devtools\", "
+ "\"normal\", \"panel\", \"popup\"]}]",
+ browser())));
+
+ windows = result.get();
+ EXPECT_EQ(window_ids.size(), windows->GetSize());
+ for (size_t i = 0; i < windows->GetSize(); ++i) {
+ base::DictionaryValue* result_window = nullptr;
+ EXPECT_TRUE(windows->GetDictionary(i, &result_window));
+ result_ids.insert(GetWindowId(result_window));
+
+ // "populate" was enabled so tabs should be populated.
+ base::ListValue* tabs = nullptr;
+ EXPECT_TRUE(result_window->GetList(keys::kTabsKey, &tabs));
+ }
+ // The returned ids should contain all the current app, browser and
+ // devtools instance ids.
+ EXPECT_EQ(window_ids, result_ids);
+
+ DevToolsWindowTesting::CloseDevToolsWindowSync(devtools);
+
+ CloseAppWindow(app_window);
}
IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, UpdateNoPermissions) {
@@ -614,6 +700,354 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, InvalidUpdateWindowState) {
keys::kInvalidWindowStateError));
}
+IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, UpdateAppWindowSizeConstraint) {
+ AppWindow* app_window = CreateTestAppWindow(
+ "{\"outerBounds\": "
+ "{\"width\": 300, \"height\": 300,"
+ " \"minWidth\": 200, \"minHeight\": 200,"
+ " \"maxWidth\": 400, \"maxHeight\": 400}}");
+
+ scoped_refptr<WindowsGetFunction> get_function = new WindowsGetFunction();
+ scoped_refptr<Extension> extension(test_util::CreateEmptyExtension().get());
+ get_function->set_extension(extension.get());
+ scoped_ptr<base::DictionaryValue> result(
+ utils::ToDictionary(utils::RunFunctionAndReturnSingleResult(
+ get_function.get(),
+ base::StringPrintf("[%u, {\"windowTypes\": [\"app\"]}]",
+ app_window->session_id().id()),
+ browser())));
+
+ EXPECT_EQ(300, api_test_utils::GetInteger(result.get(), "width"));
+ EXPECT_EQ(300, api_test_utils::GetInteger(result.get(), "height"));
+
+ // Verify the min width/height of the application window are
+ // respected.
+ scoped_refptr<WindowsUpdateFunction> update_min_function =
+ new WindowsUpdateFunction();
+ result.reset(utils::ToDictionary(utils::RunFunctionAndReturnSingleResult(
+ update_min_function.get(),
+ base::StringPrintf("[%u, {\"width\": 100, \"height\": 100}]",
+ app_window->session_id().id()),
+ browser())));
+
+ EXPECT_EQ(200, api_test_utils::GetInteger(result.get(), "width"));
+ EXPECT_EQ(200, api_test_utils::GetInteger(result.get(), "height"));
+
+ // Verify the max width/height of the application window are
+ // respected.
+ scoped_refptr<WindowsUpdateFunction> update_max_function =
+ new WindowsUpdateFunction();
+ result.reset(utils::ToDictionary(utils::RunFunctionAndReturnSingleResult(
+ update_max_function.get(),
+ base::StringPrintf("[%u, {\"width\": 500, \"height\": 500}]",
+ app_window->session_id().id()),
+ browser())));
+
+ EXPECT_EQ(400, api_test_utils::GetInteger(result.get(), "width"));
+ EXPECT_EQ(400, api_test_utils::GetInteger(result.get(), "height"));
+
+ CloseAppWindow(app_window);
+}
+
+IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, UpdateDevToolsWindow) {
+ DevToolsWindow* devtools = DevToolsWindowTesting::OpenDevToolsWindowSync(
+ browser()->tab_strip_model()->GetWebContentsAt(0), false /* is_docked */);
+
+ scoped_refptr<WindowsGetFunction> get_function = new WindowsGetFunction();
+ scoped_refptr<Extension> extension(test_util::CreateEmptyExtension().get());
+ get_function->set_extension(extension.get());
+ scoped_ptr<base::DictionaryValue> result(
+ utils::ToDictionary(utils::RunFunctionAndReturnSingleResult(
+ get_function.get(),
+ base::StringPrintf(
+ "[%u, {\"windowTypes\": [\"devtools\"]}]",
+ ExtensionTabUtil::GetWindowId(
+ DevToolsWindowTesting::Get(devtools)->browser())),
+ browser())));
+
+ // Verify the updating width/height works.
+ int32_t new_width = api_test_utils::GetInteger(result.get(), "width") - 50;
+ int32_t new_height = api_test_utils::GetInteger(result.get(), "height") - 50;
+
+ scoped_refptr<WindowsUpdateFunction> update_function =
+ new WindowsUpdateFunction();
+ result.reset(utils::ToDictionary(utils::RunFunctionAndReturnSingleResult(
+ update_function.get(),
+ base::StringPrintf("[%u, {\"width\": %d, \"height\": %d}]",
+ ExtensionTabUtil::GetWindowId(
+ DevToolsWindowTesting::Get(devtools)->browser()),
+ new_width, new_height),
+ browser())));
+
+ EXPECT_EQ(new_width, api_test_utils::GetInteger(result.get(), "width"));
+ EXPECT_EQ(new_height, api_test_utils::GetInteger(result.get(), "height"));
+
+ DevToolsWindowTesting::CloseDevToolsWindowSync(devtools);
+}
+
+// TODO(llandwerlin): Activating a browser window and waiting for the
+// action to happen requires views::Widget which is not available on
+// MacOSX. Deactivate for now.
+#if !defined(OS_MACOSX)
+class ExtensionWindowLastFocusedTest : public ExtensionTabsTest {
+ public:
+ void SetUpOnMainThread() override;
+
+ void ActivateAppWindow(AppWindow* app_window);
+
+ void ActivateBrowserWindow(Browser* browser);
+
+ Browser* CreateBrowserWithEmptyTab(bool as_popup);
+
+ int GetTabId(const base::DictionaryValue* value) const;
+
+ base::Value* RunFunction(UIThreadExtensionFunction* function,
+ const std::string& params);
+
+ private:
+ // A helper class to wait for an AppWindow to become activated. On
+ // window system like X11, for a NativeWidget to be activated, we
+ // need to wait for the round trip communication with the X server.
+ class AppWindowActivatedWaiter : public AppWindowRegistry::Observer {
+ public:
+ AppWindowActivatedWaiter(AppWindow* app_window,
+ content::BrowserContext* browser_context)
+ : app_window_(app_window),
+ browser_context_(browser_context),
+ waiting_(false) {
+ AppWindowRegistry::Get(browser_context_)->AddObserver(this);
+ }
+ ~AppWindowActivatedWaiter() override {
+ AppWindowRegistry::Get(browser_context_)->RemoveObserver(this);
+ }
+
+ void ActivateAndWait() {
+ app_window_->GetBaseWindow()->Activate();
+ if (!app_window_->GetBaseWindow()->IsActive()) {
+ waiting_ = true;
+ content::RunMessageLoop();
+ }
+ }
+
+ // AppWindowRegistry::Observer:
+ void OnAppWindowActivated(AppWindow* app_window) override {
+ if (app_window_ == app_window && waiting_) {
+ base::MessageLoopForUI::current()->Quit();
+ waiting_ = false;
+ }
+ }
+
+ private:
+ AppWindow* app_window_;
+ content::BrowserContext* browser_context_;
+ bool waiting_;
+ };
+
+ // A helper class to wait for an views::Widget to become activated.
+ class WidgetActivatedWaiter : public views::WidgetObserver {
+ public:
+ explicit WidgetActivatedWaiter(views::Widget* widget)
+ : widget_(widget), waiting_(false) {
+ widget_->AddObserver(this);
+ }
+ ~WidgetActivatedWaiter() override { widget_->RemoveObserver(this); }
+
+ void ActivateAndWait() {
+ widget_->Activate();
+ if (!widget_->IsActive()) {
+ waiting_ = true;
+ content::RunMessageLoop();
+ }
+ }
+
+ // views::WidgetObserver:
+ void OnWidgetActivationChanged(views::Widget* widget,
+ bool active) override {
+ if (widget_ == widget && waiting_) {
+ base::MessageLoopForUI::current()->Quit();
+ waiting_ = false;
+ }
+ }
+
+ private:
+ views::Widget* widget_;
+ bool waiting_;
+ };
+
+ scoped_refptr<Extension> extension_;
+};
+
+void ExtensionWindowLastFocusedTest::SetUpOnMainThread() {
+ ExtensionTabsTest::SetUpOnMainThread();
+ extension_ = test_util::CreateEmptyExtension();
+}
+
+void ExtensionWindowLastFocusedTest::ActivateAppWindow(AppWindow* app_window) {
+ AppWindowActivatedWaiter waiter(app_window, browser()->profile());
+ waiter.ActivateAndWait();
+}
+
+void ExtensionWindowLastFocusedTest::ActivateBrowserWindow(Browser* browser) {
+ BrowserView* view = BrowserView::GetBrowserViewForBrowser(browser);
+ EXPECT_NE(nullptr, view);
+ views::Widget* widget = view->frame();
+ EXPECT_NE(nullptr, widget);
+ WidgetActivatedWaiter waiter(widget);
+ waiter.ActivateAndWait();
+}
+
+Browser* ExtensionWindowLastFocusedTest::CreateBrowserWithEmptyTab(
+ bool as_popup) {
+ Browser* new_browser;
+ if (as_popup)
+ new_browser = new Browser(
+ Browser::CreateParams(Browser::TYPE_POPUP, browser()->profile(),
+ browser()->host_desktop_type()));
+ else
+ new_browser = new Browser(Browser::CreateParams(
+ browser()->profile(), browser()->host_desktop_type()));
+ AddBlankTabAndShow(new_browser);
+ return new_browser;
+}
+
+int ExtensionWindowLastFocusedTest::GetTabId(
+ const base::DictionaryValue* value) const {
+ const base::ListValue* tabs = NULL;
+ if (!value->GetList(keys::kTabsKey, &tabs))
+ return -2;
+ const base::Value* tab = NULL;
+ if (!tabs->Get(0, &tab))
+ return -2;
+ const base::DictionaryValue* tab_dict = NULL;
+ if (!tab->GetAsDictionary(&tab_dict))
+ return -2;
+ int tab_id = 0;
+ if (!tab_dict->GetInteger(keys::kIdKey, &tab_id))
+ return -2;
+ return tab_id;
+}
+
+base::Value* ExtensionWindowLastFocusedTest::RunFunction(
+ UIThreadExtensionFunction* function,
+ const std::string& params) {
+ function->set_extension(extension_.get());
+ return utils::RunFunctionAndReturnSingleResult(function, params, browser());
+}
+
+IN_PROC_BROWSER_TEST_F(ExtensionWindowLastFocusedTest,
+ NoDevtoolsAndAppWindows) {
+ DevToolsWindow* devtools = DevToolsWindowTesting::OpenDevToolsWindowSync(
+ browser()->tab_strip_model()->GetWebContentsAt(0), false /* is_docked */);
+ {
+ int devtools_window_id = ExtensionTabUtil::GetWindowId(
+ DevToolsWindowTesting::Get(devtools)->browser());
+ ActivateBrowserWindow(DevToolsWindowTesting::Get(devtools)->browser());
+
+ scoped_refptr<WindowsGetLastFocusedFunction> function =
+ new WindowsGetLastFocusedFunction();
+ scoped_ptr<base::DictionaryValue> result(utils::ToDictionary(
+ RunFunction(function.get(), "[{\"populate\": true}]")));
+ EXPECT_NE(devtools_window_id,
+ api_test_utils::GetInteger(result.get(), "id"));
+ }
+
+ AppWindow* app_window = CreateTestAppWindow(
+ "{\"outerBounds\": "
+ "{\"width\": 300, \"height\": 300,"
+ " \"minWidth\": 200, \"minHeight\": 200,"
+ " \"maxWidth\": 400, \"maxHeight\": 400}}");
+ {
+ ActivateAppWindow(app_window);
+
+ scoped_refptr<WindowsGetLastFocusedFunction> get_current_app_function =
+ new WindowsGetLastFocusedFunction();
+ scoped_ptr<base::DictionaryValue> result(utils::ToDictionary(
+ RunFunction(get_current_app_function.get(), "[{\"populate\": true}]")));
+ int app_window_id = app_window->session_id().id();
+ EXPECT_NE(app_window_id, api_test_utils::GetInteger(result.get(), "id"));
+ }
+
+ DevToolsWindowTesting::CloseDevToolsWindowSync(devtools);
+ CloseAppWindow(app_window);
+}
+
+IN_PROC_BROWSER_TEST_F(ExtensionWindowLastFocusedTest,
+ NoTabIdForDevToolsAndAppWindows) {
+ Browser* normal_browser = CreateBrowserWithEmptyTab(false);
+ {
+ ActivateBrowserWindow(normal_browser);
+
+ scoped_refptr<WindowsGetLastFocusedFunction> function =
+ new WindowsGetLastFocusedFunction();
+ scoped_ptr<base::DictionaryValue> result(utils::ToDictionary(
+ RunFunction(function.get(), "[{\"populate\": true}]")));
+ int normal_browser_window_id =
+ ExtensionTabUtil::GetWindowId(normal_browser);
+ EXPECT_EQ(normal_browser_window_id,
+ api_test_utils::GetInteger(result.get(), "id"));
+ EXPECT_NE(-1, GetTabId(result.get()));
+ EXPECT_EQ("normal", api_test_utils::GetString(result.get(), "type"));
+ }
+
+ Browser* popup_browser = CreateBrowserWithEmptyTab(true);
+ {
+ ActivateBrowserWindow(popup_browser);
+
+ scoped_refptr<WindowsGetLastFocusedFunction> function =
+ new WindowsGetLastFocusedFunction();
+ scoped_ptr<base::DictionaryValue> result(utils::ToDictionary(
+ RunFunction(function.get(), "[{\"populate\": true}]")));
+ int popup_browser_window_id = ExtensionTabUtil::GetWindowId(popup_browser);
+ EXPECT_EQ(popup_browser_window_id,
+ api_test_utils::GetInteger(result.get(), "id"));
+ EXPECT_NE(-1, GetTabId(result.get()));
+ EXPECT_EQ("popup", api_test_utils::GetString(result.get(), "type"));
+ }
+
+ DevToolsWindow* devtools = DevToolsWindowTesting::OpenDevToolsWindowSync(
+ browser()->tab_strip_model()->GetWebContentsAt(0), false /* is_docked */);
+ {
+ ActivateBrowserWindow(DevToolsWindowTesting::Get(devtools)->browser());
+
+ scoped_refptr<WindowsGetLastFocusedFunction> function =
+ new WindowsGetLastFocusedFunction();
+ scoped_ptr<base::DictionaryValue> result(utils::ToDictionary(RunFunction(
+ function.get(),
+ "[{\"populate\": true, \"windowTypes\": [ \"devtools\" ]}]")));
+ int devtools_window_id = ExtensionTabUtil::GetWindowId(
+ DevToolsWindowTesting::Get(devtools)->browser());
+ EXPECT_EQ(devtools_window_id,
+ api_test_utils::GetInteger(result.get(), "id"));
+ EXPECT_EQ(-1, GetTabId(result.get()));
+ EXPECT_EQ("devtools", api_test_utils::GetString(result.get(), "type"));
+ }
+
+ AppWindow* app_window = CreateTestAppWindow(
+ "{\"outerBounds\": "
+ "{\"width\": 300, \"height\": 300,"
+ " \"minWidth\": 200, \"minHeight\": 200,"
+ " \"maxWidth\": 400, \"maxHeight\": 400}}");
+ {
+ ActivateAppWindow(app_window);
+
+ scoped_refptr<WindowsGetLastFocusedFunction> get_current_app_function =
+ new WindowsGetLastFocusedFunction();
+ scoped_ptr<base::DictionaryValue> result(utils::ToDictionary(
+ RunFunction(get_current_app_function.get(),
+ "[{\"populate\": true, \"windowTypes\": [ \"app\" ]}]")));
+ int app_window_id = app_window->session_id().id();
+ EXPECT_EQ(app_window_id, api_test_utils::GetInteger(result.get(), "id"));
+ EXPECT_EQ(-1, GetTabId(result.get()));
+ EXPECT_EQ("app", api_test_utils::GetString(result.get(), "type"));
+ }
+
+ chrome::CloseWindow(normal_browser);
+ chrome::CloseWindow(popup_browser);
+ DevToolsWindowTesting::CloseDevToolsWindowSync(devtools);
+ CloseAppWindow(app_window);
+}
+#endif // !defined(OS_MACOSX)
+
IN_PROC_BROWSER_TEST_F(ExtensionWindowCreateTest, AcceptState) {
scoped_refptr<WindowsCreateFunction> function(new WindowsCreateFunction());
scoped_refptr<Extension> extension(test_util::CreateEmptyExtension());
@@ -773,7 +1207,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, DuplicateTabNoPermission) {
IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, NoTabsEventOnDevTools) {
extensions::ResultCatcher catcher;
ExtensionTestMessageListener listener("ready", true);
- ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("tabs/no_events")));
+ ASSERT_TRUE(
+ LoadExtension(test_data_dir_.AppendASCII("api_test/tabs/no_events")));
ASSERT_TRUE(listener.WaitUntilSatisfied());
DevToolsWindow* devtools = DevToolsWindowTesting::OpenDevToolsWindowSync(
@@ -786,6 +1221,64 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, NoTabsEventOnDevTools) {
DevToolsWindowTesting::CloseDevToolsWindowSync(devtools);
}
+IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, NoTabsAppWindow) {
+ extensions::ResultCatcher catcher;
+ ExtensionTestMessageListener listener("ready", true);
+ ASSERT_TRUE(
+ LoadExtension(test_data_dir_.AppendASCII("api_test/tabs/no_events")));
+ ASSERT_TRUE(listener.WaitUntilSatisfied());
+
+ AppWindow* app_window = CreateTestAppWindow(
+ "{\"outerBounds\": "
+ "{\"width\": 300, \"height\": 300,"
+ " \"minWidth\": 200, \"minHeight\": 200,"
+ " \"maxWidth\": 400, \"maxHeight\": 400}}");
+
+ listener.Reply("stop");
+
+ ASSERT_TRUE(catcher.GetNextResult());
+
+ CloseAppWindow(app_window);
+}
+
+IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, FilteredEvents) {
+ extensions::ResultCatcher catcher;
+ ExtensionTestMessageListener listener("ready", true);
+ ASSERT_TRUE(
+ LoadExtension(test_data_dir_.AppendASCII("api_test/windows/events")));
+ ASSERT_TRUE(listener.WaitUntilSatisfied());
+
+ AppWindow* app_window = CreateTestAppWindow(
+ "{\"outerBounds\": "
+ "{\"width\": 300, \"height\": 300,"
+ " \"minWidth\": 200, \"minHeight\": 200,"
+ " \"maxWidth\": 400, \"maxHeight\": 400}}");
+
+ Browser* browser_window = new Browser(Browser::CreateParams(
+ browser()->profile(), browser()->host_desktop_type()));
+ AddBlankTabAndShow(browser_window);
+
+ DevToolsWindow* devtools_window =
+ DevToolsWindowTesting::OpenDevToolsWindowSync(
+ browser()->tab_strip_model()->GetWebContentsAt(0),
+ false /* is_docked */);
+
+ chrome::CloseWindow(browser_window);
+ DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window);
+ CloseAppWindow(app_window);
+
+ // TODO(llandwerlin): It seems creating an app window on MacOSX
+ // won't create an activation event whereas it does on all other
+ // platform. Disable focus event tests for now.
+#if defined(OS_MACOSX)
+ listener.Reply("");
+#else
+ listener.Reply("focus");
+#endif
+
+ ASSERT_TRUE(catcher.GetNextResult());
+}
+
IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, ExecuteScriptOnDevTools) {
scoped_ptr<base::DictionaryValue> test_extension_value(
api_test_utils::ParseDictionary(
diff --git a/chrome/browser/extensions/api/tabs/windows_event_router.cc b/chrome/browser/extensions/api/tabs/windows_event_router.cc
index 6b2eb10..76eeda8 100644
--- a/chrome/browser/extensions/api/tabs/windows_event_router.cc
+++ b/chrome/browser/extensions/api/tabs/windows_event_router.cc
@@ -138,12 +138,16 @@ void WindowsEventRouter::Observe(
}
static bool WillDispatchWindowFocusedEvent(
- BrowserContext* new_active_context,
- int window_id,
+ WindowController* window_controller,
BrowserContext* context,
const Extension* extension,
base::ListValue* event_args,
const base::DictionaryValue* listener_filter) {
+ int window_id = window_controller ? window_controller->GetWindowId()
+ : extension_misc::kUnknownWindowId;
+ Profile* new_active_context =
+ window_controller ? window_controller->profile() : nullptr;
+
// When switching between windows in the default and incognito profiles,
// dispatch WINDOW_ID_NONE to extensions whose profile lost focus that
// can't see the new focused window across the incognito boundary.
@@ -185,9 +189,12 @@ void WindowsEventRouter::OnActiveWindowChanged(
windows::OnFocusChanged::kEventName,
make_scoped_ptr(new base::ListValue())));
event->will_dispatch_callback =
- base::Bind(&WillDispatchWindowFocusedEvent,
- static_cast<BrowserContext*>(window_profile),
- window_id);
+ base::Bind(&WillDispatchWindowFocusedEvent, window_controller);
+ // Set the window type to 'normal' if we don't have a window
+ // controller, so the event is not filtered.
+ event->filter_info.SetWindowType(window_controller
+ ? window_controller->GetWindowTypeText()
+ : keys::kWindowTypeValueNormal);
EventRouter::Get(profile_)->BroadcastEvent(event.Pass());
}
@@ -197,6 +204,7 @@ void WindowsEventRouter::DispatchEvent(events::HistogramValue histogram_value,
scoped_ptr<base::ListValue> args) {
scoped_ptr<Event> event(new Event(histogram_value, event_name, args.Pass()));
event->restrict_to_browser_context = window_controller->profile();
+ event->filter_info.SetWindowType(window_controller->GetWindowTypeText());
EventRouter::Get(profile_)->BroadcastEvent(event.Pass());
}
diff --git a/chrome/browser/extensions/api/tabs/windows_util.cc b/chrome/browser/extensions/api/tabs/windows_util.cc
index 0e2ead5..56830c9 100644
--- a/chrome/browser/extensions/api/tabs/windows_util.cc
+++ b/chrome/browser/extensions/api/tabs/windows_util.cc
@@ -2,12 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <string>
+#include <vector>
+
#include "chrome/browser/extensions/api/tabs/windows_util.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
#include "chrome/browser/extensions/chrome_extension_function.h"
#include "chrome/browser/extensions/chrome_extension_function_details.h"
+#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/extensions/window_controller.h"
#include "chrome/browser/extensions/window_controller_list.h"
#include "chrome/browser/profiles/profile.h"
@@ -15,11 +19,13 @@
#include "extensions/browser/extension_function_dispatcher.h"
#include "extensions/common/constants.h"
#include "extensions/common/error_utils.h"
+#include "extensions/common/extension.h"
namespace windows_util {
bool GetWindowFromWindowID(UIThreadExtensionFunction* function,
int window_id,
+ extensions::WindowController::TypeFilter filter,
extensions::WindowController** controller) {
if (window_id == extension_misc::kCurrentWindowId) {
extensions::WindowController* extension_window_controller =
@@ -37,8 +43,9 @@ bool GetWindowFromWindowID(UIThreadExtensionFunction* function,
return false;
}
} else {
- *controller = extensions::WindowControllerList::GetInstance()
- ->FindWindowForFunctionById(function, window_id);
+ *controller =
+ extensions::WindowControllerList::GetInstance()
+ ->FindWindowForFunctionByIdWithFilter(function, window_id, filter);
if (!(*controller)) {
function->SetError(extensions::ErrorUtils::FormatErrorMessage(
extensions::tabs_constants::kWindowNotFoundError,
@@ -50,11 +57,10 @@ bool GetWindowFromWindowID(UIThreadExtensionFunction* function,
}
bool CanOperateOnWindow(const UIThreadExtensionFunction* function,
- const extensions::WindowController* controller) {
- if (function->extension() != NULL &&
- !controller->IsVisibleToExtension(function->extension())) {
+ const extensions::WindowController* controller,
+ extensions::WindowController::TypeFilter filter) {
+ if (!controller->MatchesFilter(filter))
return false;
- }
if (function->browser_context() == controller->profile())
return true;
diff --git a/chrome/browser/extensions/api/tabs/windows_util.h b/chrome/browser/extensions/api/tabs/windows_util.h
index 0e0a8d2..940d380 100644
--- a/chrome/browser/extensions/api/tabs/windows_util.h
+++ b/chrome/browser/extensions/api/tabs/windows_util.h
@@ -5,9 +5,16 @@
#ifndef CHROME_BROWSER_EXTENSIONS_API_TABS_WINDOWS_UTIL_H__
#define CHROME_BROWSER_EXTENSIONS_API_TABS_WINDOWS_UTIL_H__
+#include "chrome/browser/extensions/window_controller_list.h"
+
class UIThreadExtensionFunction;
+namespace content {
+class BrowserContext;
+}
+
namespace extensions {
+class Extension;
class WindowController;
}
@@ -17,12 +24,18 @@ namespace windows_util {
// returns false and sets UIThreadExtensionFunction error_.
bool GetWindowFromWindowID(UIThreadExtensionFunction* function,
int window_id,
+ extensions::WindowController::TypeFilter filter,
extensions::WindowController** controller);
// Returns true if |function| (and the profile and extension that it was
// invoked from) can operate on the window wrapped by |window_controller|.
+// If |all_window_types| is set this function will return true for any
+// kind of window (including app and devtools), otherwise it will
+// return true only for normal browser windows as well as windows
+// created by the extension.
bool CanOperateOnWindow(const UIThreadExtensionFunction* function,
- const extensions::WindowController* controller);
+ const extensions::WindowController* controller,
+ extensions::WindowController::TypeFilter filter);
} // namespace windows_util
diff --git a/chrome/browser/extensions/extension_tab_util.cc b/chrome/browser/extensions/extension_tab_util.cc
index 6ac0c78..384c94d 100644
--- a/chrome/browser/extensions/extension_tab_util.cc
+++ b/chrome/browser/extensions/extension_tab_util.cc
@@ -432,9 +432,10 @@ void ExtensionTabUtil::ScrubTabValueForExtension(
WebContents* contents,
const Extension* extension,
base::DictionaryValue* tab_info) {
- bool has_permission = extension &&
+ int tab_id = GetTabId(contents);
+ bool has_permission = tab_id >= 0 && extension &&
extension->permissions_data()->HasAPIPermissionForTab(
- GetTabId(contents), APIPermission::kTab);
+ tab_id, APIPermission::kTab);
if (!has_permission) {
tab_info->Remove(keys::kUrlKey, NULL);
diff --git a/chrome/browser/extensions/window_controller.cc b/chrome/browser/extensions/window_controller.cc
index 96f9eac..7c9a961 100644
--- a/chrome/browser/extensions/window_controller.cc
+++ b/chrome/browser/extensions/window_controller.cc
@@ -8,6 +8,7 @@
#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
#include "chrome/browser/extensions/window_controller_list.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/extensions/api/windows.h"
#include "ui/base/base_window.h"
#include "ui/gfx/geometry/rect.h"
@@ -16,6 +17,34 @@ namespace extensions {
///////////////////////////////////////////////////////////////////////////////
// WindowController
+// static
+WindowController::TypeFilter WindowController::GetAllWindowFilter() {
+ // This needs to be updated if there is a change to
+ // extensions::api::windows:WindowType.
+ COMPILE_ASSERT(api::windows::WINDOW_TYPE_LAST == 5,
+ Update_extensions_WindowController_to_match_WindowType);
+ return ((1 << api::windows::WINDOW_TYPE_NORMAL) |
+ (1 << api::windows::WINDOW_TYPE_PANEL) |
+ (1 << api::windows::WINDOW_TYPE_POPUP) |
+ (1 << api::windows::WINDOW_TYPE_APP) |
+ (1 << api::windows::WINDOW_TYPE_DEVTOOLS));
+}
+
+// static
+WindowController::TypeFilter WindowController::GetDefaultWindowFilter() {
+ return ((1 << api::windows::WINDOW_TYPE_NORMAL) |
+ (1 << api::windows::WINDOW_TYPE_PANEL) |
+ (1 << api::windows::WINDOW_TYPE_POPUP));
+}
+
+WindowController::TypeFilter WindowController::GetFilterFromWindowTypes(
+ const std::vector<api::windows::WindowType>& types) {
+ WindowController::TypeFilter filter = 0;
+ for (auto& window_type : types)
+ filter |= 1 << window_type;
+ return filter;
+}
+
WindowController::WindowController(ui::BaseWindow* window, Profile* profile)
: window_(window), profile_(profile) {
}
@@ -63,4 +92,9 @@ base::DictionaryValue* WindowController::CreateWindowValue() const {
return result;
}
+bool WindowController::MatchesFilter(TypeFilter filter) const {
+ TypeFilter type = 1 << api::windows::ParseWindowType(GetWindowTypeText());
+ return (type & filter) != 0;
+}
+
} // namespace extensions
diff --git a/chrome/browser/extensions/window_controller.h b/chrome/browser/extensions/window_controller.h
index f21a0fa..73d3907 100644
--- a/chrome/browser/extensions/window_controller.h
+++ b/chrome/browser/extensions/window_controller.h
@@ -9,6 +9,7 @@
#include "base/basictypes.h"
#include "base/compiler_specific.h"
+#include "chrome/common/extensions/api/windows.h"
class Browser; // TODO(stevenjb) eliminate this dependency.
class GURL;
@@ -41,6 +42,21 @@ class WindowController {
REASON_NOT_EDITABLE,
};
+ // A bitmaks used as filter on window types.
+ using TypeFilter = uint32_t;
+
+ // Returns a filter allowing all window types to be manipulated
+ // through the chrome.windows APIs.
+ static TypeFilter GetAllWindowFilter();
+
+ // Returns the default filter to be used when operating on the windows
+ // from WindowControllerList when using the chrome.windows APIs.
+ static TypeFilter GetDefaultWindowFilter();
+
+ // Builds a filter out of a vector of window types.
+ static TypeFilter GetFilterFromWindowTypes(
+ const std::vector<api::windows::WindowType>& types);
+
WindowController(ui::BaseWindow* window, Profile* profile);
virtual ~WindowController();
@@ -83,6 +99,9 @@ class WindowController {
// need to define this behavior.
virtual bool IsVisibleToExtension(const Extension* extension) const = 0;
+ // Returns true if the window type of the controller matches the |filter|.
+ bool MatchesFilter(TypeFilter filter) const;
+
private:
ui::BaseWindow* window_;
Profile* profile_;
diff --git a/chrome/browser/extensions/window_controller_list.cc b/chrome/browser/extensions/window_controller_list.cc
index cea69d5..c12646b 100644
--- a/chrome/browser/extensions/window_controller_list.cc
+++ b/chrome/browser/extensions/window_controller_list.cc
@@ -9,6 +9,7 @@
#include "chrome/browser/extensions/api/tabs/windows_util.h"
#include "chrome/browser/extensions/chrome_extension_function_details.h"
#include "chrome/browser/extensions/window_controller_list_observer.h"
+#include "chrome/common/extensions/api/windows.h"
#include "components/sessions/session_id.h"
#include "extensions/browser/extension_function.h"
#include "ui/base/base_window.h"
@@ -55,30 +56,53 @@ void WindowControllerList::RemoveObserver(
}
WindowController* WindowControllerList::FindWindowById(int id) const {
+ return FindWindowByIdWithFilter(id,
+ WindowController::GetDefaultWindowFilter());
+}
+
+WindowController* WindowControllerList::FindWindowByIdWithFilter(
+ int id,
+ WindowController::TypeFilter filter) const {
for (ControllerList::const_iterator iter = windows().begin();
iter != windows().end(); ++iter) {
- if ((*iter)->GetWindowId() == id)
+ if ((*iter)->GetWindowId() == id && (*iter)->MatchesFilter(filter))
return *iter;
}
- return NULL;
+ return nullptr;
}
WindowController* WindowControllerList::FindWindowForFunctionById(
const UIThreadExtensionFunction* function,
int id) const {
- WindowController* controller = FindWindowById(id);
- if (controller && windows_util::CanOperateOnWindow(function, controller))
+ return FindWindowForFunctionByIdWithFilter(
+ function, id, WindowController::GetDefaultWindowFilter());
+}
+
+WindowController* WindowControllerList::FindWindowForFunctionByIdWithFilter(
+ const UIThreadExtensionFunction* function,
+ int id,
+ WindowController::TypeFilter filter) const {
+ WindowController* controller = FindWindowByIdWithFilter(id, filter);
+ if (controller &&
+ windows_util::CanOperateOnWindow(function, controller, filter))
return controller;
- return NULL;
+ return nullptr;
}
WindowController* WindowControllerList::CurrentWindowForFunction(
const UIThreadExtensionFunction* function) const {
- WindowController* result = NULL;
+ return CurrentWindowForFunctionWithFilter(
+ function, WindowController::GetDefaultWindowFilter());
+}
+
+WindowController* WindowControllerList::CurrentWindowForFunctionWithFilter(
+ const UIThreadExtensionFunction* function,
+ WindowController::TypeFilter filter) const {
+ WindowController* result = nullptr;
// Returns either the focused window (if any), or the last window in the list.
for (ControllerList::const_iterator iter = windows().begin();
iter != windows().end(); ++iter) {
- if (windows_util::CanOperateOnWindow(function, *iter)) {
+ if (windows_util::CanOperateOnWindow(function, *iter, filter)) {
result = *iter;
if (result->window()->IsActive())
break; // use focused window
diff --git a/chrome/browser/extensions/window_controller_list.h b/chrome/browser/extensions/window_controller_list.h
index c280e07..e593014 100644
--- a/chrome/browser/extensions/window_controller_list.h
+++ b/chrome/browser/extensions/window_controller_list.h
@@ -36,16 +36,34 @@ class WindowControllerList {
// Returns a window matching |id|.
WindowController* FindWindowById(int id) const;
+ // Returns a window matching |id| using |filter|.
+ WindowController* FindWindowByIdWithFilter(
+ int id,
+ WindowController::TypeFilter filter) const;
+
// Returns a window matching the context the function was invoked in.
WindowController* FindWindowForFunctionById(
const UIThreadExtensionFunction* function,
int id) const;
+ // Returns a window matching the context the function was invoked in
+ // using |filter|.
+ WindowController* FindWindowForFunctionByIdWithFilter(
+ const UIThreadExtensionFunction* function,
+ int id,
+ WindowController::TypeFilter filter) const;
+
// Returns the focused or last added window matching the context the function
// was invoked in.
WindowController* CurrentWindowForFunction(
const UIThreadExtensionFunction* function) const;
+ // Returns the focused or last added window matching the context the function
+ // was invoked in using |filter|.
+ WindowController* CurrentWindowForFunctionWithFilter(
+ const UIThreadExtensionFunction* function,
+ WindowController::TypeFilter filter) const;
+
const ControllerList& windows() const { return windows_; }
static WindowControllerList* GetInstance();
diff --git a/chrome/browser/ui/panels/panel.cc b/chrome/browser/ui/panels/panel.cc
index 5f52022..6501af4 100644
--- a/chrome/browser/ui/panels/panel.cc
+++ b/chrome/browser/ui/panels/panel.cc
@@ -96,7 +96,6 @@ PanelExtensionWindowController::CreateWindowValueWithTabs(
const extensions::Extension* extension) const {
base::DictionaryValue* result = CreateWindowValue();
- DCHECK(IsVisibleToExtension(extension));
base::DictionaryValue* tab_value = CreateTabValue(extension, 0);
if (tab_value) {
base::ListValue* tab_list = new base::ListValue();
@@ -115,7 +114,6 @@ base::DictionaryValue* PanelExtensionWindowController::CreateTabValue(
if (!web_contents)
return NULL;
- DCHECK(IsVisibleToExtension(extension));
base::DictionaryValue* tab_value = new base::DictionaryValue();
tab_value->SetInteger(extensions::tabs_constants::kIdKey,
SessionTabHelper::IdForTab(web_contents));
diff --git a/chrome/common/extensions/api/windows.json b/chrome/common/extensions/api/windows.json
index bd79fe8..d63e906 100644
--- a/chrome/common/extensions/api/windows.json
+++ b/chrome/common/extensions/api/windows.json
@@ -78,7 +78,8 @@
"optional": true,
"description": "",
"properties": {
- "populate": {"type": "boolean", "optional": true, "description": "If true, the $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission." }
+ "populate": {"type": "boolean", "optional": true, "description": "If true, the $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission." },
+ "windowTypes": {"type": "array", "items": { "$ref": "WindowType" }, "optional": true, "description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to <code>['normal', 'panel', 'popup']</code>." }
}
},
{
@@ -103,7 +104,8 @@
"optional": true,
"description": "",
"properties": {
- "populate": {"type": "boolean", "optional": true, "description": "If true, the $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission." }
+ "populate": {"type": "boolean", "optional": true, "description": "If true, the $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission." },
+ "windowTypes": {"type": "array", "items": { "$ref": "WindowType" }, "optional": true, "description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to <code>['normal', 'panel', 'popup']</code>." }
}
},
{
@@ -128,7 +130,8 @@
"optional": true,
"description": "",
"properties": {
- "populate": {"type": "boolean", "optional": true, "description": "If true, the $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission." }
+ "populate": {"type": "boolean", "optional": true, "description": "If true, the $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission." },
+ "windowTypes": {"type": "array", "items": { "$ref": "WindowType" }, "optional": true, "description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to <code>['normal', 'panel', 'popup']</code>." }
}
},
{
@@ -153,7 +156,8 @@
"optional": true,
"description": "",
"properties": {
- "populate": {"type": "boolean", "optional": true, "description": "If true, each $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects for that window. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission." }
+ "populate": {"type": "boolean", "optional": true, "description": "If true, each $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects for that window. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission." },
+ "windowTypes": {"type": "array", "items": { "$ref": "WindowType" }, "optional": true, "description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to <code>['normal', 'panel', 'popup']</code>." }
}
},
{
@@ -267,6 +271,14 @@
"name": "onCreated",
"type": "function",
"description": "Fired when a window is created.",
+ "filters": [
+ {
+ "name": "windowTypes",
+ "type": "array",
+ "items": { "$ref": "WindowType" },
+ "description": "Conditions that the window's type being created must satisfy. By default it will satisfy <code>['normal', 'panel', 'popup']</code>."
+ }
+ ],
"parameters": [
{
"$ref": "Window",
@@ -279,6 +291,14 @@
"name": "onRemoved",
"type": "function",
"description": "Fired when a window is removed (closed).",
+ "filters": [
+ {
+ "name": "windowTypes",
+ "type": "array",
+ "items": { "$ref": "WindowType" },
+ "description": "Conditions that the window's type being removed must satisfy. By default it will satisfy <code>['normal', 'panel', 'popup']</code>."
+ }
+ ],
"parameters": [
{"type": "integer", "name": "windowId", "minimum": 0, "description": "ID of the removed window."}
]
@@ -287,6 +307,14 @@
"name": "onFocusChanged",
"type": "function",
"description": "Fired when the currently focused window changes. Will be chrome.windows.WINDOW_ID_NONE if all chrome windows have lost focus. Note: On some Linux window managers, WINDOW_ID_NONE will always be sent immediately preceding a switch from one chrome window to another.",
+ "filters": [
+ {
+ "name": "windowTypes",
+ "type": "array",
+ "items": { "$ref": "WindowType" },
+ "description": "Conditions that the window's type being removed must satisfy. By default it will satisfy <code>['normal', 'panel', 'popup']</code>."
+ }
+ ],
"parameters": [
{"type": "integer", "name": "windowId", "minimum": -1, "description": "ID of the newly focused window."}
]
diff --git a/chrome/test/data/extensions/api_test/windows/events/background.html b/chrome/test/data/extensions/api_test/windows/events/background.html
new file mode 100644
index 0000000..6ada66a
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/windows/events/background.html
@@ -0,0 +1,6 @@
+<!--
+ * Copyright 2015 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.
+-->
+<script src="test.js"></script>
diff --git a/chrome/test/data/extensions/api_test/windows/events/manifest.json b/chrome/test/data/extensions/api_test/windows/events/manifest.json
new file mode 100644
index 0000000..cac457b
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/windows/events/manifest.json
@@ -0,0 +1,9 @@
+{
+ "name": "chrome.tabs test",
+ "version": "0.1",
+ "manifest_version": 2,
+ "description": "end-to-end browser test for chrome.windows events API",
+ "background": {
+ "page": "background.html"
+ }
+}
diff --git a/chrome/test/data/extensions/api_test/windows/events/test.js b/chrome/test/data/extensions/api_test/windows/events/test.js
new file mode 100644
index 0000000..a10aaf72
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/windows/events/test.js
@@ -0,0 +1,82 @@
+// Copyright 2015 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 results = {
+ filtered: {},
+ unfiltered: {}
+};
+
+function recordEvents(category, name, windowId, windowType) {
+ if (!results[category][windowId])
+ results[category][windowId] = { id: windowId, type: windowType };
+ results[category][windowId][name] = true
+}
+
+chrome.windows.onCreated.addListener(function(win) {
+ recordEvents('filtered', 'create', win.id, win.type);
+});
+chrome.windows.onRemoved.addListener(function(id) {
+ recordEvents('filtered', 'remove', id, null);
+});
+chrome.windows.onFocusChanged.addListener(function(id) {
+ recordEvents('filtered', 'focus', id, null);
+});
+
+var noFilter = { windowTypes: ['app', 'devtools', 'normal', 'panel', 'popup'] };
+chrome.windows.onCreated.addListener(function(win) {
+ recordEvents('unfiltered', 'create', win.id, win.type);
+}, noFilter);
+chrome.windows.onRemoved.addListener(function(id) {
+ recordEvents('unfiltered', 'remove', id, null);
+}, noFilter);
+chrome.windows.onFocusChanged.addListener(function(id) {
+ recordEvents('unfiltered', 'focus', id, null);
+}, noFilter);
+
+chrome.test.sendMessage('ready', function (message) {
+ chrome.windows.getCurrent(function(currentWindow) {
+ var filteredCount = 0;
+ for (var i in results.filtered) {
+ var win = results.filtered[i];
+ if (win.id == currentWindow.id || win.id == -1)
+ continue;
+ filteredCount++;
+ chrome.test.assertFalse(win.type == 'app' || win.type == 'devtools',
+ 'Unexpected window type "' +
+ win.type + '" in filtered events');
+ chrome.test.assertTrue(win.create == true,
+ 'Missing create event for ' + win.type);
+ chrome.test.assertTrue(win.remove == true,
+ 'Missing remove event for ' + win.type);
+ chrome.test.assertTrue(win.focus == true,
+ 'Missing focus event for ' + win.type);
+ }
+ chrome.test.assertEq(1, filteredCount);
+
+ var unfilteredCount = 0;
+ var includes_app = false, includes_devtools = false;
+ for (var i in results.unfiltered) {
+ var win = results.unfiltered[i];
+ if (win.id == currentWindow.id || win.id == -1)
+ continue;
+ unfilteredCount++;
+ if (win.type == 'app')
+ includes_app = true;
+ if (win.type == 'devtools')
+ includes_devtools = true;
+ chrome.test.assertTrue(win.create == true,
+ 'Missing create event for ' + win.type);
+ chrome.test.assertTrue(win.remove == true,
+ 'Missing remove event for ' + win.type);
+ if (message == 'focus')
+ chrome.test.assertTrue(win.focus == true,
+ 'Missing focus event for ' + win.type);
+ }
+ chrome.test.assertEq(3, unfilteredCount);
+ chrome.test.assertTrue(includes_app && includes_devtools,
+ 'Could not find app or devtools windows');
+
+ chrome.test.notifyPass();
+ });
+});
diff --git a/extensions/common/event_filtering_info.cc b/extensions/common/event_filtering_info.cc
index 2180984..c585de9 100644
--- a/extensions/common/event_filtering_info.cc
+++ b/extensions/common/event_filtering_info.cc
@@ -12,12 +12,17 @@ namespace extensions {
EventFilteringInfo::EventFilteringInfo()
: has_url_(false),
has_instance_id_(false),
- instance_id_(0) {
-}
+ instance_id_(0),
+ has_window_type_(false) {}
EventFilteringInfo::~EventFilteringInfo() {
}
+void EventFilteringInfo::SetWindowType(const std::string& window_type) {
+ window_type_ = window_type;
+ has_window_type_ = true;
+}
+
void EventFilteringInfo::SetURL(const GURL& url) {
url_ = url;
has_url_ = true;
@@ -42,11 +47,15 @@ scoped_ptr<base::Value> EventFilteringInfo::AsValue() const {
if (!service_type_.empty())
result->SetString("serviceType", service_type_);
+ if (has_window_type_)
+ result->SetString("windowType", window_type_);
+
return result.Pass();
}
bool EventFilteringInfo::IsEmpty() const {
- return !has_url_ && service_type_.empty() && !has_instance_id_;
+ return !has_window_type_ && !has_url_ && service_type_.empty() &&
+ !has_instance_id_;
}
} // namespace extensions
diff --git a/extensions/common/event_filtering_info.h b/extensions/common/event_filtering_info.h
index a358798..f51e3d2 100644
--- a/extensions/common/event_filtering_info.h
+++ b/extensions/common/event_filtering_info.h
@@ -26,12 +26,20 @@ class EventFilteringInfo {
public:
EventFilteringInfo();
~EventFilteringInfo();
+ void SetWindowType(const std::string& window_type);
void SetURL(const GURL& url);
void SetInstanceID(int instance_id);
void SetServiceType(const std::string& service_type) {
service_type_ = service_type;
}
+ // Note: window type is a Chrome concept, so arguably doesn't belong
+ // in the extensions module. If the number of Chrome concept grows,
+ // consider a delegation model with a ChromeEventFilteringInfo
+ // class.
+ bool has_window_type() const { return has_window_type_; }
+ const std::string& window_type() const { return window_type_; }
+
bool has_url() const { return has_url_; }
const GURL& url() const { return url_; }
@@ -52,6 +60,9 @@ class EventFilteringInfo {
bool has_instance_id_;
int instance_id_;
+ bool has_window_type_;
+ std::string window_type_;
+
// Allow implicit copy and assignment.
};
diff --git a/extensions/common/event_matcher.cc b/extensions/common/event_matcher.cc
index 8ae022a..0071111 100644
--- a/extensions/common/event_matcher.cc
+++ b/extensions/common/event_matcher.cc
@@ -2,12 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/callback.h"
+
#include "extensions/common/event_matcher.h"
#include "extensions/common/event_filtering_info.h"
namespace {
const char kUrlFiltersKey[] = "url";
+const char kWindowTypesKey[] = "windowTypes";
+
+const char* const kDefaultWindowTypes[] = {"normal", "panel", "popup"};
}
namespace extensions {
@@ -29,20 +34,30 @@ bool EventMatcher::MatchNonURLCriteria(
return event_info.instance_id() == GetInstanceID();
}
+ if (event_info.has_window_type()) {
+ for (int i = 0; i < GetWindowTypeCount(); i++) {
+ std::string window_type;
+ if (GetWindowType(i, &window_type) &&
+ window_type == event_info.window_type())
+ return true;
+ }
+ return false;
+ }
+
const std::string& service_type_filter = GetServiceTypeFilter();
return service_type_filter.empty() ||
service_type_filter == event_info.service_type();
}
int EventMatcher::GetURLFilterCount() const {
- base::ListValue* url_filters = NULL;
+ base::ListValue* url_filters = nullptr;
if (filter_->GetList(kUrlFiltersKey, &url_filters))
return url_filters->GetSize();
return 0;
}
bool EventMatcher::GetURLFilter(int i, base::DictionaryValue** url_filter_out) {
- base::ListValue* url_filters = NULL;
+ base::ListValue* url_filters = nullptr;
if (filter_->GetList(kUrlFiltersKey, &url_filters)) {
return url_filters->GetDictionary(i, url_filter_out);
}
@@ -65,6 +80,25 @@ int EventMatcher::GetInstanceID() const {
return instance_id;
}
+int EventMatcher::GetWindowTypeCount() const {
+ base::ListValue* window_type_filters = nullptr;
+ if (filter_->GetList(kWindowTypesKey, &window_type_filters))
+ return window_type_filters->GetSize();
+ return arraysize(kDefaultWindowTypes);
+}
+
+bool EventMatcher::GetWindowType(int i, std::string* window_type_out) const {
+ base::ListValue* window_types = nullptr;
+ if (filter_->GetList(kWindowTypesKey, &window_types)) {
+ return window_types->GetString(i, window_type_out);
+ }
+ if (i >= 0 && i < static_cast<int>(arraysize(kDefaultWindowTypes))) {
+ *window_type_out = kDefaultWindowTypes[i];
+ return true;
+ }
+ return false;
+}
+
int EventMatcher::GetRoutingID() const {
return routing_id_;
}
diff --git a/extensions/common/event_matcher.h b/extensions/common/event_matcher.h
index bd0ce7b..0d134e6 100644
--- a/extensions/common/event_matcher.h
+++ b/extensions/common/event_matcher.h
@@ -31,6 +31,9 @@ class EventMatcher {
int GetURLFilterCount() const;
bool GetURLFilter(int i, base::DictionaryValue** url_filter_out);
+ int GetWindowTypeCount() const;
+ bool GetWindowType(int i, std::string* window_type_out) const;
+
std::string GetServiceTypeFilter() const;
int HasURLFilters() const;
diff --git a/extensions/renderer/event_bindings.cc b/extensions/renderer/event_bindings.cc
index f27d123..a468ad9 100644
--- a/extensions/renderer/event_bindings.cc
+++ b/extensions/renderer/event_bindings.cc
@@ -97,6 +97,12 @@ EventFilteringInfo ParseFromObject(v8::Local<v8::Object> object,
v8::Local<v8::Value> service_type_value(object->Get(service_type));
info.SetServiceType(*v8::String::Utf8Value(service_type_value));
}
+ v8::Local<v8::String> window_types(
+ v8::String::NewFromUtf8(isolate, "windowType"));
+ if (object->Has(window_types)) {
+ v8::Local<v8::Value> window_types_value(object->Get(window_types));
+ info.SetWindowType(*v8::String::Utf8Value(window_types_value));
+ }
return info;
}