summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/extensions')
-rw-r--r--chrome/browser/extensions/api/tabs/ash_panel_contents.cc221
-rw-r--r--chrome/browser/extensions/api/tabs/ash_panel_contents.h66
-rw-r--r--chrome/browser/extensions/api/tabs/tabs_api.cc37
-rw-r--r--chrome/browser/extensions/app_window_contents.cc172
-rw-r--r--chrome/browser/extensions/app_window_contents.h73
-rw-r--r--chrome/browser/extensions/browser_extension_window_controller.cc9
-rw-r--r--chrome/browser/extensions/browser_extension_window_controller.h2
-rw-r--r--chrome/browser/extensions/extension_tab_util.cc35
-rw-r--r--chrome/browser/extensions/window_controller.h3
-rw-r--r--chrome/browser/extensions/window_controller_list.cc15
-rw-r--r--chrome/browser/extensions/window_controller_list.h3
-rw-r--r--chrome/browser/extensions/window_open_apitest.cc106
12 files changed, 684 insertions, 58 deletions
diff --git a/chrome/browser/extensions/api/tabs/ash_panel_contents.cc b/chrome/browser/extensions/api/tabs/ash_panel_contents.cc
new file mode 100644
index 0000000..cb21550
--- /dev/null
+++ b/chrome/browser/extensions/api/tabs/ash_panel_contents.cc
@@ -0,0 +1,221 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/extensions/api/tabs/ash_panel_contents.h"
+
+#include "base/values.h"
+#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
+#include "chrome/browser/extensions/api/tabs/tabs_windows_api.h"
+#include "chrome/browser/extensions/api/tabs/windows_event_router.h"
+#include "chrome/browser/extensions/extension_tab_util.h"
+#include "chrome/browser/extensions/window_controller_list.h"
+#include "chrome/browser/ui/extensions/native_app_window.h"
+#include "chrome/browser/ui/extensions/shell_window.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_messages.h"
+#include "content/public/browser/site_instance.h"
+#include "content/public/browser/web_contents.h"
+
+// AshPanelWindowController ----------------------------------------------------
+
+// This class enables a ShellWindow instance to be accessed (to a limited
+// extent) via the chrome.windows and chrome.tabs API. This is a temporary
+// bridge to support instantiating ShellWindows from v1 apps, specifically
+// for creating Panels in Ash. See crbug.com/160645.
+class AshPanelWindowController : public extensions::WindowController {
+ public:
+ AshPanelWindowController(ShellWindow* window, Profile* profile);
+ virtual ~AshPanelWindowController();
+
+ void NativeWindowChanged();
+
+ // Overridden from extensions::WindowController.
+ virtual int GetWindowId() const OVERRIDE;
+ virtual std::string GetWindowTypeText() const OVERRIDE;
+ virtual base::DictionaryValue* CreateWindowValueWithTabs(
+ const extensions::Extension* extension) const OVERRIDE;
+ virtual base::DictionaryValue* CreateTabValue(
+ const extensions::Extension* extension, int tab_index) const OVERRIDE;
+ virtual bool CanClose(Reason* reason) const OVERRIDE;
+ virtual void SetFullscreenMode(bool is_fullscreen,
+ const GURL& extension_url) const OVERRIDE;
+ virtual bool IsVisibleToExtension(
+ const extensions::Extension* extension) const OVERRIDE;
+
+ private:
+ ShellWindow* shell_window_; // Weak pointer; this is owned by shell_window_
+ bool is_active_;
+
+ DISALLOW_COPY_AND_ASSIGN(AshPanelWindowController);
+};
+
+AshPanelWindowController::AshPanelWindowController(
+ ShellWindow* shell_window, Profile* profile)
+ : extensions::WindowController(shell_window->GetBaseWindow(), profile),
+ shell_window_(shell_window),
+ is_active_(shell_window->GetBaseWindow()->IsActive()) {
+ extensions::WindowControllerList::GetInstance()->AddExtensionWindow(this);
+}
+
+AshPanelWindowController::~AshPanelWindowController() {
+ extensions::WindowControllerList::GetInstance()->RemoveExtensionWindow(this);
+}
+
+int AshPanelWindowController::GetWindowId() const {
+ return static_cast<int>(shell_window_->session_id().id());
+}
+
+std::string AshPanelWindowController::GetWindowTypeText() const {
+ return extensions::tabs_constants::kWindowTypeValuePanel;
+}
+
+base::DictionaryValue* AshPanelWindowController::CreateWindowValueWithTabs(
+ const extensions::Extension* extension) const {
+ DCHECK(IsVisibleToExtension(extension));
+ base::DictionaryValue* result = CreateWindowValue();
+ DictionaryValue* tab_value = CreateTabValue(extension, 0);
+ if (tab_value) {
+ base::ListValue* tab_list = new ListValue();
+ tab_list->Append(tab_value);
+ result->Set(extensions::tabs_constants::kTabsKey, tab_list);
+ }
+ return result;
+}
+
+base::DictionaryValue* AshPanelWindowController::CreateTabValue(
+ const extensions::Extension* extension, int tab_index) const {
+ if ((extension && !IsVisibleToExtension(extension)) ||
+ (tab_index > 0)) {
+ return NULL;
+ }
+ content::WebContents* web_contents = shell_window_->web_contents();
+ if (!web_contents)
+ return NULL;
+
+ DictionaryValue* tab_value = new DictionaryValue();
+ tab_value->SetInteger(extensions::tabs_constants::kIdKey,
+ SessionID::IdForTab(web_contents));
+ tab_value->SetInteger(extensions::tabs_constants::kIndexKey, 0);
+ const int window_id = GetWindowId();
+ tab_value->SetInteger(extensions::tabs_constants::kWindowIdKey, window_id);
+ tab_value->SetString(
+ extensions::tabs_constants::kUrlKey, web_contents->GetURL().spec());
+ tab_value->SetString(
+ extensions::tabs_constants::kStatusKey,
+ ExtensionTabUtil::GetTabStatusText(web_contents->IsLoading()));
+ tab_value->SetBoolean(
+ extensions::tabs_constants::kActiveKey,
+ shell_window_->GetBaseWindow()->IsActive());
+ // ShellWindow only ever contains one tab, so that tab is always effectively
+ // selcted and highlighted (for purposes of the chrome.tabs API).
+ tab_value->SetInteger(extensions::tabs_constants::kWindowIdKey, window_id);
+ tab_value->SetInteger(extensions::tabs_constants::kIdKey, window_id);
+ tab_value->SetBoolean(extensions::tabs_constants::kSelectedKey, true);
+ tab_value->SetBoolean(extensions::tabs_constants::kHighlightedKey, true);
+ tab_value->SetBoolean(extensions::tabs_constants::kPinnedKey, false);
+ tab_value->SetString(
+ extensions::tabs_constants::kTitleKey, web_contents->GetTitle());
+ tab_value->SetBoolean(
+ extensions::tabs_constants::kIncognitoKey,
+ web_contents->GetBrowserContext()->IsOffTheRecord());
+ return tab_value;
+}
+
+bool AshPanelWindowController::CanClose(Reason* reason) const {
+ return true;
+}
+
+void AshPanelWindowController::SetFullscreenMode(
+ bool is_fullscreen, const GURL& extension_url) const {
+ // Do nothing. Panels cannot be fullscreen.
+}
+
+bool AshPanelWindowController::IsVisibleToExtension(
+ const extensions::Extension* extension) const {
+ return shell_window_->extension() &&
+ extension->id() == shell_window_->extension()->id();
+}
+
+void AshPanelWindowController::NativeWindowChanged() {
+ bool active = shell_window_->GetBaseWindow()->IsActive();
+ if (active == is_active_)
+ return;
+ is_active_ = active;
+ // Let the extension API know that the active window changed.
+ extensions::TabsWindowsAPI* tabs_windows_api =
+ extensions::TabsWindowsAPI::Get(profile());
+ if (!tabs_windows_api)
+ return;
+ tabs_windows_api->windows_event_router()->OnActiveWindowChanged(
+ active ? this : NULL);
+}
+
+// AshPanelContents -----------------------------------------------------
+
+AshPanelContents::AshPanelContents(ShellWindow* host)
+ : host_(host) {
+}
+
+AshPanelContents::~AshPanelContents() {
+}
+
+void AshPanelContents::Initialize(Profile* profile, const GURL& url) {
+ url_ = url;
+
+ extension_function_dispatcher_.reset(
+ new ExtensionFunctionDispatcher(profile, this));
+
+ web_contents_.reset(content::WebContents::Create(
+ content::WebContents::CreateParams(
+ profile, content::SiteInstance::CreateForURL(profile, url_))));
+
+ content::WebContentsObserver::Observe(web_contents_.get());
+}
+
+void AshPanelContents::LoadContents(int32 creator_process_id) {
+ // This must be created after the native window has been created.
+ window_controller_.reset(
+ new AshPanelWindowController(host_, host_->profile()));
+
+ web_contents_->GetController().LoadURL(
+ url_, content::Referrer(), content::PAGE_TRANSITION_LINK,
+ std::string());
+}
+
+void AshPanelContents::NativeWindowChanged(NativeAppWindow* native_app_window) {
+ if (window_controller_)
+ window_controller_->NativeWindowChanged();
+}
+
+void AshPanelContents::NativeWindowClosed() {
+}
+
+content::WebContents* AshPanelContents::GetWebContents() const {
+ return web_contents_.get();
+}
+
+bool AshPanelContents::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(AshPanelContents, message)
+ IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+extensions::WindowController*
+AshPanelContents::GetExtensionWindowController() const {
+ return window_controller_.get();
+}
+
+content::WebContents* AshPanelContents::GetAssociatedWebContents() const {
+ return web_contents_.get();
+}
+
+void AshPanelContents::OnRequest(
+ const ExtensionHostMsg_Request_Params& params) {
+ extension_function_dispatcher_->Dispatch(
+ params, web_contents_->GetRenderViewHost());
+}
diff --git a/chrome/browser/extensions/api/tabs/ash_panel_contents.h b/chrome/browser/extensions/api/tabs/ash_panel_contents.h
new file mode 100644
index 0000000..871139b
--- /dev/null
+++ b/chrome/browser/extensions/api/tabs/ash_panel_contents.h
@@ -0,0 +1,66 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_TABS_ASH_PANEL_CONTENTS_H_
+#define CHROME_BROWSER_EXTENSIONS_API_TABS_ASH_PANEL_CONTENTS_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/extensions/extension_function_dispatcher.h"
+#include "chrome/browser/ui/extensions/shell_window.h"
+#include "content/public/browser/web_contents_observer.h"
+
+class AshPanelWindowController;
+class GURL;
+
+namespace content {
+class RenderViewHost;
+}
+
+namespace extensions {
+struct DraggableRegion;
+}
+
+// ShellWindowContents class specific to panel windows created by v1
+// extenstions. This class maintains a WebContents instance and observes it for
+// the purpose of passing messages to the extensions system. It also creates
+// an extensions::WindowController instance for interfacing with the v1
+// extensions API.
+class AshPanelContents : public ShellWindowContents,
+ public content::WebContentsObserver,
+ public ExtensionFunctionDispatcher::Delegate {
+ public:
+ explicit AshPanelContents(ShellWindow* host);
+ virtual ~AshPanelContents();
+
+ // ShellWindowContents
+ virtual void Initialize(Profile* profile, const GURL& url) OVERRIDE;
+ virtual void LoadContents(int32 creator_process_id) OVERRIDE;
+ virtual void NativeWindowChanged(NativeAppWindow* native_app_window) OVERRIDE;
+ virtual void NativeWindowClosed() OVERRIDE;
+ virtual content::WebContents* GetWebContents() const OVERRIDE;
+
+ // ExtensionFunctionDispatcher::Delegate
+ virtual extensions::WindowController* GetExtensionWindowController() const
+ OVERRIDE;
+ virtual content::WebContents* GetAssociatedWebContents() const OVERRIDE;
+
+ private:
+ // content::WebContentsObserver
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ void OnRequest(const ExtensionHostMsg_Request_Params& params);
+
+ ShellWindow* host_;
+ GURL url_;
+ scoped_ptr<content::WebContents> web_contents_;
+ scoped_ptr<ExtensionFunctionDispatcher> extension_function_dispatcher_;
+ scoped_ptr<AshPanelWindowController> window_controller_;
+
+ DISALLOW_COPY_AND_ASSIGN(AshPanelContents);
+};
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_TABS_ASH_PANEL_CONTENTS_H_
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index b227e6d..c5879f7 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -88,6 +88,12 @@
#include "win8/util/win8_util.h"
#endif // OS_WIN
+#if defined(USE_ASH)
+#include "ash/ash_switches.h"
+#include "chrome/browser/extensions/api/tabs/ash_panel_contents.h"
+#include "chrome/browser/extensions/shell_window_registry.h"
+#endif
+
namespace Get = extensions::api::windows::Get;
namespace GetAll = extensions::api::windows::GetAll;
namespace GetCurrent = extensions::api::windows::GetCurrent;
@@ -531,15 +537,8 @@ bool WindowsCreateFunction::RunImpl() {
}
// Initialize default window bounds according to window type.
- // In ChromiumOS the default popup bounds is 0x0 which indicates default
- // window sizes in PanelBrowserView. In other OSs use the same default
- // bounds as windows.
-#if !defined(OS_CHROMEOS)
if (Browser::TYPE_TABBED == window_type ||
Browser::TYPE_POPUP == window_type) {
-#else
- if (Browser::TYPE_TABBED == window_type) {
-#endif
// Try to position the new browser relative to its originating
// browser window. The call offsets the bounds by kWindowTilePixels
// (defined in WindowSizer to be 10).
@@ -594,8 +593,28 @@ bool WindowsCreateFunction::RunImpl() {
}
}
-#if !defined(OS_CHROMEOS)
if (window_type == Browser::TYPE_PANEL) {
+#if defined(OS_CHROMEOS)
+ if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnablePanels) &&
+ PanelManager::ShouldUsePanels(extension_id)) {
+ ShellWindow::CreateParams create_params;
+ create_params.window_type = ShellWindow::WINDOW_TYPE_PANEL;
+ create_params.bounds = window_bounds;
+ create_params.minimum_size = window_bounds.size();
+ create_params.maximum_size = window_bounds.size();
+ ShellWindow* shell_window =
+ new ShellWindow(window_profile, GetExtension());
+ AshPanelContents* ash_panel_contents = new AshPanelContents(shell_window);
+ shell_window->Init(urls[0], ash_panel_contents, create_params);
+ SetResult(ash_panel_contents->GetExtensionWindowController()->
+ CreateWindowValueWithTabs(GetExtension()));
+ // Add the panel to the shell window registry so that it shows up in
+ // the launcher and as an active render process.
+ extensions::ShellWindowRegistry::Get(window_profile)->AddShellWindow(
+ shell_window);
+ return true;
+ }
+#else
std::string title =
web_app::GenerateApplicationNameFromExtensionId(extension_id);
// Note: Panels ignore all but the first url provided.
@@ -612,8 +631,8 @@ bool WindowsCreateFunction::RunImpl() {
panel->extension_window_controller()->CreateWindowValueWithTabs(
GetExtension()));
return true;
- }
#endif
+ }
// Create a new BrowserWindow.
chrome::HostDesktopType host_desktop_type = chrome::GetActiveDesktop();
diff --git a/chrome/browser/extensions/app_window_contents.cc b/chrome/browser/extensions/app_window_contents.cc
new file mode 100644
index 0000000..88be3e6
--- /dev/null
+++ b/chrome/browser/extensions/app_window_contents.cc
@@ -0,0 +1,172 @@
+// Copyright 2013 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.
+
+#include "chrome/browser/extensions/app_window_contents.h"
+
+#include "chrome/browser/ui/extensions/native_app_window.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "chrome/common/extensions/api/app_window.h"
+#include "chrome/common/extensions/extension_messages.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/resource_dispatcher_host.h"
+#include "content/public/browser/site_instance.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/renderer_preferences.h"
+
+namespace app_window = extensions::api::app_window;
+
+AppWindowContents::AppWindowContents(ShellWindow* host)
+ : host_(host) {
+}
+
+AppWindowContents::~AppWindowContents() {
+}
+
+void AppWindowContents::Initialize(Profile* profile, const GURL& url) {
+ url_ = url;
+
+ extension_function_dispatcher_.reset(
+ new ExtensionFunctionDispatcher(profile, this));
+
+ web_contents_.reset(content::WebContents::Create(
+ content::WebContents::CreateParams(
+ profile, content::SiteInstance::CreateForURL(profile, url_))));
+
+ content::WebContentsObserver::Observe(web_contents_.get());
+ web_contents_->GetMutableRendererPrefs()->
+ browser_handles_all_top_level_requests = true;
+ web_contents_->GetRenderViewHost()->SyncRendererPrefs();
+}
+
+void AppWindowContents::LoadContents(int32 creator_process_id) {
+ // If the new view is in the same process as the creator, block the created
+ // RVH from loading anything until the background page has had a chance to
+ // do any initialization it wants. If it's a different process, the new RVH
+ // shouldn't communicate with the background page anyway (e.g. sandboxed).
+ if (web_contents_->GetRenderViewHost()->GetProcess()->GetID() ==
+ creator_process_id) {
+ SuspendRenderViewHost(web_contents_->GetRenderViewHost());
+ } else {
+ VLOG(1) << "ShellWindow created in new process ("
+ << web_contents_->GetRenderViewHost()->GetProcess()->GetID()
+ << ") != creator (" << creator_process_id
+ << "). Routing disabled.";
+ }
+
+ // TODO(jeremya): there's a bug where navigating a web contents to an
+ // extension URL causes it to create a new RVH and discard the old
+ // (perfectly usable) one. To work around this, we watch for a RVH_CHANGED
+ // message from the web contents (which will be sent during LoadURL) and
+ // suspend resource requests on the new RVH to ensure that we block the new
+ // RVH from loading anything. It should be okay to remove the
+ // NOTIFICATION_RVH_CHANGED registration once http://crbug.com/123007 is
+ // fixed.
+ registrar_.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
+ content::Source<content::NavigationController>(
+ &web_contents()->GetController()));
+ web_contents_->GetController().LoadURL(
+ url_, content::Referrer(), content::PAGE_TRANSITION_LINK,
+ std::string());
+ registrar_.RemoveAll();
+}
+
+void AppWindowContents::NativeWindowChanged(
+ NativeAppWindow* native_app_window) {
+ ListValue args;
+ DictionaryValue* dictionary = new DictionaryValue();
+ args.Append(dictionary);
+
+ gfx::Rect bounds = native_app_window->GetBounds();
+ bounds.Inset(native_app_window->GetFrameInsets());
+ app_window::Bounds update;
+ update.left.reset(new int(bounds.x()));
+ update.top.reset(new int(bounds.y()));
+ update.width.reset(new int(bounds.width()));
+ update.height.reset(new int(bounds.height()));
+ dictionary->Set("bounds", update.ToValue().release());
+ dictionary->SetBoolean("minimized", native_app_window->IsMinimized());
+ dictionary->SetBoolean("maximized", native_app_window->IsMaximized());
+
+ content::RenderViewHost* rvh = web_contents_->GetRenderViewHost();
+ rvh->Send(new ExtensionMsg_MessageInvoke(rvh->GetRoutingID(),
+ host_->extension()->id(),
+ "updateAppWindowProperties",
+ args,
+ GURL(),
+ false));
+
+}
+
+void AppWindowContents::NativeWindowClosed() {
+ content::RenderViewHost* rvh = web_contents_->GetRenderViewHost();
+ rvh->Send(new ExtensionMsg_AppWindowClosed(rvh->GetRoutingID()));
+}
+
+content::WebContents* AppWindowContents::GetWebContents() const {
+ return web_contents_.get();
+}
+
+void AppWindowContents::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ switch (type) {
+ case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED: {
+ // TODO(jeremya): once http://crbug.com/123007 is fixed, we'll no longer
+ // need to suspend resource requests here (the call in the constructor
+ // should be enough).
+ content::Details<std::pair<content::RenderViewHost*,
+ content::RenderViewHost*> >
+ host_details(details);
+ if (host_details->first)
+ SuspendRenderViewHost(host_details->second);
+ break;
+ }
+ default:
+ NOTREACHED() << "Received unexpected notification";
+ }
+}
+
+bool AppWindowContents::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(AppWindowContents, message)
+ IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest)
+ IPC_MESSAGE_HANDLER(ExtensionHostMsg_UpdateDraggableRegions,
+ UpdateDraggableRegions)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+extensions::WindowController*
+AppWindowContents::GetExtensionWindowController() const {
+ return NULL;
+}
+
+content::WebContents* AppWindowContents::GetAssociatedWebContents() const {
+ return web_contents_.get();
+}
+
+void AppWindowContents::OnRequest(
+ const ExtensionHostMsg_Request_Params& params) {
+ extension_function_dispatcher_->Dispatch(
+ params, web_contents_->GetRenderViewHost());
+}
+
+void AppWindowContents::UpdateDraggableRegions(
+ const std::vector<extensions::DraggableRegion>& regions) {
+ host_->UpdateDraggableRegions(regions);
+}
+
+void AppWindowContents::SuspendRenderViewHost(
+ content::RenderViewHost* rvh) {
+ DCHECK(rvh);
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO, FROM_HERE,
+ base::Bind(&content::ResourceDispatcherHost::BlockRequestsForRoute,
+ base::Unretained(content::ResourceDispatcherHost::Get()),
+ rvh->GetProcess()->GetID(), rvh->GetRoutingID()));
+}
diff --git a/chrome/browser/extensions/app_window_contents.h b/chrome/browser/extensions/app_window_contents.h
new file mode 100644
index 0000000..3cf2a32
--- /dev/null
+++ b/chrome/browser/extensions/app_window_contents.h
@@ -0,0 +1,73 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_APP_WINDOW_CONTENTS_H_
+#define CHROME_BROWSER_EXTENSIONS_APP_WINDOW_CONTENTS_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/extensions/extension_function_dispatcher.h"
+#include "chrome/browser/ui/extensions/shell_window.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/web_contents_observer.h"
+
+class GURL;
+
+namespace content {
+class RenderViewHost;
+}
+
+namespace extensions {
+struct DraggableRegion;
+}
+
+// ShellWindowContents class specific to app windows. It maintains a
+// WebContents instance and observes it for the purpose of passing
+// messages to the extensions system.
+class AppWindowContents : public ShellWindowContents,
+ public content::NotificationObserver,
+ public content::WebContentsObserver,
+ public ExtensionFunctionDispatcher::Delegate {
+ public:
+ explicit AppWindowContents(ShellWindow* host);
+ virtual ~AppWindowContents();
+
+ // ShellWindowContents
+ virtual void Initialize(Profile* profile, const GURL& url) OVERRIDE;
+ virtual void LoadContents(int32 creator_process_id) OVERRIDE;
+ virtual void NativeWindowChanged(NativeAppWindow* native_app_window) OVERRIDE;
+ virtual void NativeWindowClosed() OVERRIDE;
+ virtual content::WebContents* GetWebContents() const OVERRIDE;
+
+ private:
+ // content::NotificationObserver
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE;
+
+ // content::WebContentsObserver
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ // ExtensionFunctionDispatcher::Delegate
+ virtual extensions::WindowController* GetExtensionWindowController() const
+ OVERRIDE;
+ virtual content::WebContents* GetAssociatedWebContents() const OVERRIDE;
+
+ void OnRequest(const ExtensionHostMsg_Request_Params& params);
+ void UpdateDraggableRegions(
+ const std::vector<extensions::DraggableRegion>& regions);
+ void SuspendRenderViewHost(content::RenderViewHost* rvh);
+
+ ShellWindow* host_; // This class is owned by |host_|
+ GURL url_;
+ content::NotificationRegistrar registrar_;
+ scoped_ptr<content::WebContents> web_contents_;
+ scoped_ptr<ExtensionFunctionDispatcher> extension_function_dispatcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppWindowContents);
+};
+
+#endif // CHROME_BROWSER_EXTENSIONS_APP_WINDOW_CONTENTS_H_
diff --git a/chrome/browser/extensions/browser_extension_window_controller.cc b/chrome/browser/extensions/browser_extension_window_controller.cc
index 665c5ad..9e50e50 100644
--- a/chrome/browser/extensions/browser_extension_window_controller.cc
+++ b/chrome/browser/extensions/browser_extension_window_controller.cc
@@ -12,6 +12,7 @@
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/extensions/extension.h"
BrowserExtensionWindowController::BrowserExtensionWindowController(
@@ -58,6 +59,14 @@ BrowserExtensionWindowController::CreateWindowValueWithTabs(
return result;
}
+base::DictionaryValue* BrowserExtensionWindowController::CreateTabValue(
+ const extensions::Extension* extension, int tab_index) const {
+ TabStripModel* tab_strip = browser_->tab_strip_model();
+ DictionaryValue* result = ExtensionTabUtil::CreateTabValue(
+ tab_strip->GetWebContentsAt(tab_index), tab_strip, tab_index);
+ return result;
+}
+
bool BrowserExtensionWindowController::CanClose(Reason* reason) const {
// Don't let an extension remove the window if the user is dragging tabs
// in that window.
diff --git a/chrome/browser/extensions/browser_extension_window_controller.h b/chrome/browser/extensions/browser_extension_window_controller.h
index a1e220a..2dfb3d4 100644
--- a/chrome/browser/extensions/browser_extension_window_controller.h
+++ b/chrome/browser/extensions/browser_extension_window_controller.h
@@ -24,6 +24,8 @@ class BrowserExtensionWindowController : public extensions::WindowController {
virtual base::DictionaryValue* CreateWindowValue() const OVERRIDE;
virtual base::DictionaryValue* CreateWindowValueWithTabs(
const extensions::Extension* extension) const OVERRIDE;
+ virtual base::DictionaryValue* CreateTabValue(
+ const extensions::Extension* extension, int tab_index) const OVERRIDE;
virtual bool CanClose(Reason* reason) const OVERRIDE;
virtual void SetFullscreenMode(bool is_fullscreen,
const GURL& extension_url) const OVERRIDE;
diff --git a/chrome/browser/extensions/extension_tab_util.cc b/chrome/browser/extensions/extension_tab_util.cc
index 2ed787d..5e89316 100644
--- a/chrome/browser/extensions/extension_tab_util.cc
+++ b/chrome/browser/extensions/extension_tab_util.cc
@@ -5,8 +5,10 @@
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
+#include "chrome/browser/extensions/shell_window_registry.h"
#include "chrome/browser/extensions/tab_helper.h"
#include "chrome/browser/extensions/window_controller.h"
+#include "chrome/browser/extensions/window_controller_list.h"
#include "chrome/browser/net/url_fixer_upper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sessions/session_id.h"
@@ -14,6 +16,7 @@
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_iterator.h"
#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/extensions/shell_window.h"
#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/extensions/extension.h"
@@ -33,6 +36,25 @@ using content::WebContents;
using extensions::APIPermission;
using extensions::Extension;
+namespace {
+
+extensions::WindowController* GetShellWindowController(
+ const WebContents* contents) {
+ Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
+ extensions::ShellWindowRegistry* registry =
+ extensions::ShellWindowRegistry::Get(profile);
+ if (!registry)
+ return NULL;
+ ShellWindow* shell_window =
+ registry->GetShellWindowForRenderViewHost(contents->GetRenderViewHost());
+ if (!shell_window)
+ return NULL;
+ return extensions::WindowControllerList::GetInstance()->
+ FindWindowById(shell_window->session_id().id());
+}
+
+} // namespace
+
int ExtensionTabUtil::GetWindowId(const Browser* browser) {
return browser->session_id().id();
}
@@ -63,6 +85,13 @@ DictionaryValue* ExtensionTabUtil::CreateTabValue(
TabStripModel* tab_strip,
int tab_index,
const Extension* extension) {
+ // If we have a matching ShellWindow with a controller, get the tab value
+ // from its controller instead.
+ extensions::WindowController* controller = GetShellWindowController(contents);
+ if (controller &&
+ (!extension || controller->IsVisibleToExtension(extension))) {
+ return controller->CreateTabValue(extension, tab_index);
+ }
DictionaryValue *result = CreateTabValue(contents, tab_strip, tab_index);
ScrubTabValueForExtension(contents, extension, result);
return result;
@@ -87,6 +116,12 @@ DictionaryValue* ExtensionTabUtil::CreateTabValue(
const WebContents* contents,
TabStripModel* tab_strip,
int tab_index) {
+ // If we have a matching ShellWindow with a controller, get the tab value
+ // from its controller instead.
+ extensions::WindowController* controller = GetShellWindowController(contents);
+ if (controller)
+ return controller->CreateTabValue(NULL, tab_index);
+
if (!tab_strip)
ExtensionTabUtil::GetTabStripModel(contents, &tab_strip, &tab_index);
diff --git a/chrome/browser/extensions/window_controller.h b/chrome/browser/extensions/window_controller.h
index ec9f6e2..48a881e 100644
--- a/chrome/browser/extensions/window_controller.h
+++ b/chrome/browser/extensions/window_controller.h
@@ -60,6 +60,9 @@ class WindowController {
virtual base::DictionaryValue* CreateWindowValueWithTabs(
const extensions::Extension* extension) const = 0;
+ virtual base::DictionaryValue* CreateTabValue(
+ const extensions::Extension* extension, int tab_index) const = 0;
+
// Returns false if the window is in a state where closing the window is not
// permitted and sets |reason| if not NULL.
virtual bool CanClose(Reason* reason) const = 0;
diff --git a/chrome/browser/extensions/window_controller_list.cc b/chrome/browser/extensions/window_controller_list.cc
index 0dfdffa..257c680b 100644
--- a/chrome/browser/extensions/window_controller_list.cc
+++ b/chrome/browser/extensions/window_controller_list.cc
@@ -52,17 +52,24 @@ void WindowControllerList::RemoveObserver(
observers_.RemoveObserver(observer);
}
-WindowController* WindowControllerList::FindWindowForFunctionById(
- const UIThreadExtensionFunction* function,
- int id) const {
+WindowController* WindowControllerList::FindWindowById(int id) const {
for (ControllerList::const_iterator iter = windows().begin();
iter != windows().end(); ++iter) {
- if (function->CanOperateOnWindow(*iter) && (*iter)->GetWindowId() == id)
+ if ((*iter)->GetWindowId() == id)
return *iter;
}
return NULL;
}
+WindowController* WindowControllerList::FindWindowForFunctionById(
+ const UIThreadExtensionFunction* function,
+ int id) const {
+ WindowController* controller = FindWindowById(id);
+ if (controller && function->CanOperateOnWindow(controller))
+ return controller;
+ return NULL;
+}
+
WindowController* WindowControllerList::CurrentWindowForFunction(
const UIThreadExtensionFunction* function) const {
WindowController* result = NULL;
diff --git a/chrome/browser/extensions/window_controller_list.h b/chrome/browser/extensions/window_controller_list.h
index a32baf6..4b9b934 100644
--- a/chrome/browser/extensions/window_controller_list.h
+++ b/chrome/browser/extensions/window_controller_list.h
@@ -33,6 +33,9 @@ class WindowControllerList {
void AddObserver(WindowControllerListObserver* observer);
void RemoveObserver(WindowControllerListObserver* observer);
+ // Returns a window matching |id|.
+ WindowController* FindWindowById(int id) const;
+
// Returns a window matching the context the function was invoked in.
WindowController* FindWindowForFunctionById(
const UIThreadExtensionFunction* function,
diff --git a/chrome/browser/extensions/window_open_apitest.cc b/chrome/browser/extensions/window_open_apitest.cc
index cac1d4c..b448aee 100644
--- a/chrome/browser/extensions/window_open_apitest.cc
+++ b/chrome/browser/extensions/window_open_apitest.cc
@@ -26,6 +26,15 @@
#include "net/base/mock_host_resolver.h"
#include "testing/gtest/include/gtest/gtest.h"
+#if defined(USE_ASH)
+#include "chrome/browser/extensions/shell_window_registry.h"
+#endif
+
+#if defined(USE_ASH) && !defined(OS_WIN)
+// TODO(stevenjb): Figure out the correct behavior for Ash + Win
+#define USE_ASH_PANELS
+#endif
+
using content::OpenURLParams;
using content::Referrer;
using content::WebContents;
@@ -41,7 +50,16 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, DISABLED_WindowOpen) {
EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
}
-void WaitForTabsAndPopups(Browser* browser,
+int GetPanelCount(Browser* browser) {
+#if defined(USE_ASH_PANELS)
+ return static_cast<int>(extensions::ShellWindowRegistry::Get(
+ browser->profile())->shell_windows().size());
+#else
+ return PanelManager::GetInstance()->num_panels();
+#endif
+}
+
+bool WaitForTabsAndPopups(Browser* browser,
int num_tabs,
int num_popups,
int num_panels) {
@@ -52,12 +70,12 @@ void WaitForTabsAndPopups(Browser* browser,
++num_tabs;
size_t num_browsers = static_cast<size_t>(num_popups) + 1;
- const base::TimeDelta kWaitTime = base::TimeDelta::FromSeconds(15);
+ const base::TimeDelta kWaitTime = base::TimeDelta::FromSeconds(10);
base::TimeTicks end_time = base::TimeTicks::Now() + kWaitTime;
while (base::TimeTicks::Now() < end_time) {
if (chrome::GetBrowserCount(browser->profile()) == num_browsers &&
browser->tab_strip_model()->count() == num_tabs &&
- PanelManager::GetInstance()->num_panels() == num_panels)
+ GetPanelCount(browser) == num_panels)
break;
content::RunAllPendingInMessageLoop();
@@ -65,7 +83,7 @@ void WaitForTabsAndPopups(Browser* browser,
EXPECT_EQ(num_browsers, chrome::GetBrowserCount(browser->profile()));
EXPECT_EQ(num_tabs, browser->tab_strip_model()->count());
- EXPECT_EQ(num_panels, PanelManager::GetInstance()->num_panels());
+ EXPECT_EQ(num_panels, GetPanelCount(browser));
int num_popups_seen = 0;
for (chrome::BrowserIterator iter; !iter.done(); iter.Next()) {
@@ -73,10 +91,8 @@ void WaitForTabsAndPopups(Browser* browser,
continue;
// Check for TYPE_POPUP.
-// TODO: this ifdef will have to be updated when we have win ash bots running
-// browser_tests.
-#if defined(USE_ASH) && !defined(OS_WIN)
- // On Ash, panel windows open as popup windows.
+#if defined(USE_ASH_PANELS)
+ // On Ash, panel windows may open as popup windows.
EXPECT_TRUE((*iter)->is_type_popup() || (*iter)->is_type_panel());
#else
EXPECT_TRUE((*iter)->is_type_popup());
@@ -84,6 +100,11 @@ void WaitForTabsAndPopups(Browser* browser,
++num_popups_seen;
}
EXPECT_EQ(num_popups, num_popups_seen);
+
+ return ((num_browsers == chrome::GetBrowserCount(browser->profile())) &&
+ (num_tabs == browser->tab_strip_model()->count()) &&
+ (num_panels == GetPanelCount(browser)) &&
+ (num_popups == num_popups_seen));
}
IN_PROC_BROWSER_TEST_F(ExtensionApiTest, BrowserIsApp) {
@@ -92,7 +113,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, BrowserIsApp) {
ASSERT_TRUE(LoadExtension(
test_data_dir_.AppendASCII("window_open").AppendASCII("browser_is_app")));
- WaitForTabsAndPopups(browser(), 0, 2, 0);
+ EXPECT_TRUE(WaitForTabsAndPopups(browser(), 0, 2, 0));
for (chrome::BrowserIterator iter; !iter.done(); iter.Next()) {
if (*iter == browser())
@@ -109,7 +130,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, WindowOpenPopupDefault) {
const int num_tabs = 1;
const int num_popups = 0;
- WaitForTabsAndPopups(browser(), num_tabs, num_popups, 0);
+ EXPECT_TRUE(WaitForTabsAndPopups(browser(), num_tabs, num_popups, 0));
}
IN_PROC_BROWSER_TEST_F(ExtensionApiTest, WindowOpenPopupLarge) {
@@ -120,7 +141,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, WindowOpenPopupLarge) {
// On other systems this should open a new popup window.
const int num_tabs = 0;
const int num_popups = 1;
- WaitForTabsAndPopups(browser(), num_tabs, num_popups, 0);
+ EXPECT_TRUE(WaitForTabsAndPopups(browser(), num_tabs, num_popups, 0));
}
IN_PROC_BROWSER_TEST_F(ExtensionApiTest, WindowOpenPopupSmall) {
@@ -132,7 +153,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, WindowOpenPopupSmall) {
// On other systems this should open a new popup window.
const int num_tabs = 0;
const int num_popups = 1;
- WaitForTabsAndPopups(browser(), num_tabs, num_popups, 0);
+ EXPECT_TRUE(WaitForTabsAndPopups(browser(), num_tabs, num_popups, 0));
}
IN_PROC_BROWSER_TEST_F(ExtensionApiTest, PopupBlockingExtension) {
@@ -143,7 +164,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, PopupBlockingExtension) {
test_data_dir_.AppendASCII("window_open").AppendASCII("popup_blocking")
.AppendASCII("extension")));
- WaitForTabsAndPopups(browser(), 5, 3, 0);
+ EXPECT_TRUE(WaitForTabsAndPopups(browser(), 5, 3, 0));
}
IN_PROC_BROWSER_TEST_F(ExtensionApiTest, PopupBlockingHostedApp) {
@@ -178,7 +199,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, PopupBlockingHostedApp) {
open_popup, Referrer(), NEW_FOREGROUND_TAB,
content::PAGE_TRANSITION_TYPED, false));
- WaitForTabsAndPopups(browser(), 3, 1, 0);
+ EXPECT_TRUE(WaitForTabsAndPopups(browser(), 3, 1, 0));
}
IN_PROC_BROWSER_TEST_F(ExtensionApiTest, WindowArgumentsOverflow) {
@@ -205,9 +226,7 @@ class WindowOpenPanelTest : public ExtensionApiTest {
}
};
-// TODO: this ifdef will have to be updated when we have win ash bots running
-// browser_tests.
-#if defined(USE_ASH) && !defined(OS_WIN)
+#if defined(USE_ASH_PANELS)
// On Ash, this currently fails because we're currently opening new panel
// windows as popup windows instead.
#define MAYBE_WindowOpenPanel DISABLED_WindowOpenPanel
@@ -218,9 +237,7 @@ IN_PROC_BROWSER_TEST_F(WindowOpenPanelTest, MAYBE_WindowOpenPanel) {
ASSERT_TRUE(RunExtensionTest("window_open/panel")) << message_;
}
-// TODO: this ifdef will have to be updated when we have win ash bots running
-// browser_tests.
-#if defined(USE_ASH)
+#if defined(USE_ASH_PANELS)
// On Ash, this currently fails because we're currently opening new panel
// windows as popup windows instead.
#define MAYBE_WindowOpenPanelDetached DISABLED_WindowOpenPanelDetached
@@ -233,12 +250,16 @@ IN_PROC_BROWSER_TEST_F(WindowOpenPanelTest, MAYBE_WindowOpenPanelDetached) {
IN_PROC_BROWSER_TEST_F(WindowOpenPanelTest,
CloseNonExtensionPanelsOnUninstall) {
-// TODO: this ifdef will have to be updated when we have win ash bots running
-// browser_tests.
-#if defined(USE_ASH) && !defined(OS_WIN)
+#if defined(USE_ASH_PANELS)
// On Ash, new panel windows open as popup windows instead.
- int num_popups = 4;
- int num_panels = 0;
+ int num_popups, num_panels;
+ if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnablePanels)) {
+ num_popups = 2;
+ num_panels = 2;
+ } else {
+ num_popups = 4;
+ num_panels = 0;
+ }
#else
int num_popups = 2;
int num_panels = 2;
@@ -266,7 +287,7 @@ IN_PROC_BROWSER_TEST_F(WindowOpenPanelTest,
// Two tabs. One in extension domain and one in non-extension domain.
// Two popups - one in extension domain and one in non-extension domain.
// Two panels - one in extension domain and one in non-extension domain.
- WaitForTabsAndPopups(browser(), 2, num_popups, num_panels);
+ EXPECT_TRUE(WaitForTabsAndPopups(browser(), 2, num_popups, num_panels));
// Wait on test messages to make sure the pages loaded.
for (size_t i = 0; i < listeners.size(); ++i)
@@ -276,16 +297,15 @@ IN_PROC_BROWSER_TEST_F(WindowOpenPanelTest,
// Wait for the tabs and popups in non-extension domain to stay open.
// Expect everything else, including panels, to close.
-// TODO: this ifdef will have to be updated when we have win ash bots running
-// browser_tests.
-#if defined(USE_ASH) && !defined(OS_WIN)
- // On Ash, new panel windows open as popup windows instead, so there are 2
- // extension domain popups that will close (instead of 1 popup on non-Ash).
- num_popups -= 2;
-#else
num_popups -= 1;
+#if defined(USE_ASH_PANELS)
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnablePanels)) {
+ // On Ash, new panel windows open as popup windows instead, so there are 2
+ // extension domain popups that will close (instead of 1 popup on non-Ash).
+ num_popups -= 1;
+ }
#endif
- WaitForTabsAndPopups(browser(), 1, num_popups, 0);
+ EXPECT_TRUE(WaitForTabsAndPopups(browser(), 1, num_popups, 0));
}
#if defined(OS_CHROMEOS)
@@ -297,9 +317,7 @@ IN_PROC_BROWSER_TEST_F(WindowOpenPanelTest,
#define MAYBE_ClosePanelsOnExtensionCrash ClosePanelsOnExtensionCrash
#endif
IN_PROC_BROWSER_TEST_F(WindowOpenPanelTest, MAYBE_ClosePanelsOnExtensionCrash) {
-// TODO: this ifdef will have to be updated when we have win ash bots running
-// browser_tests.
-#if defined(USE_ASH) && !defined(OS_WIN)
+#if defined(USE_ASH_PANELS)
// On Ash, new panel windows open as popup windows instead.
int num_popups = 4;
int num_panels = 0;
@@ -330,7 +348,7 @@ IN_PROC_BROWSER_TEST_F(WindowOpenPanelTest, MAYBE_ClosePanelsOnExtensionCrash) {
// Two tabs. One in extension domain and one in non-extension domain.
// Two popups - one in extension domain and one in non-extension domain.
// Two panels - one in extension domain and one in non-extension domain.
- WaitForTabsAndPopups(browser(), 2, num_popups, num_panels);
+ EXPECT_TRUE(WaitForTabsAndPopups(browser(), 2, num_popups, num_panels));
// Wait on test messages to make sure the pages loaded.
for (size_t i = 0; i < listeners.size(); ++i)
@@ -346,14 +364,12 @@ IN_PROC_BROWSER_TEST_F(WindowOpenPanelTest, MAYBE_ClosePanelsOnExtensionCrash) {
WaitForExtensionCrash(extension->id());
// Only expect panels to close. The rest stay open to show a sad-tab.
- WaitForTabsAndPopups(browser(), 2, num_popups, 0);
+ EXPECT_TRUE(WaitForTabsAndPopups(browser(), 2, num_popups, 0));
}
-// TODO: this ifdef will have to be updated when we have win ash bots running
-// browser_tests.
-#if defined(USE_ASH) && !defined(OS_WIN)
-// This test is not applicable on Ash. Ash opens panel windows as popup
-// windows. The modified window.open behavior only applies to panel windows.
+#if defined(USE_ASH_PANELS)
+// This test is not applicable on Ash. The modified window.open behavior only
+// applies to non-Ash panel windows.
#define MAYBE_WindowOpenFromPanel DISABLED_WindowOpenFromPanel
#else
#define MAYBE_WindowOpenFromPanel WindowOpenFromPanel
@@ -368,7 +384,7 @@ IN_PROC_BROWSER_TEST_F(WindowOpenPanelTest, MAYBE_WindowOpenFromPanel) {
// Expect one panel (opened by extension) and one tab (from the panel calling
// window.open). Panels modify the WindowOpenDisposition in window.open
// to always open in a tab.
- WaitForTabsAndPopups(browser(), 1, 0, 1);
+ EXPECT_TRUE(WaitForTabsAndPopups(browser(), 1, 0, 1));
}
IN_PROC_BROWSER_TEST_F(ExtensionApiTest, DISABLED_WindowOpener) {