diff options
author | jackhou <jackhou@chromium.org> | 2014-09-07 23:47:07 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-09-08 06:49:51 +0000 |
commit | c76ef7326840ea3eeb3fa96fb88f5269b5ec55a7 (patch) | |
tree | 0af1d21c7802ca3d610ace16737589ca5066f0a6 | |
parent | 9f11d3bc7c31cc4c10efa15e4bfddf12705908ed (diff) | |
download | chromium_src-c76ef7326840ea3eeb3fa96fb88f5269b5ec55a7.zip chromium_src-c76ef7326840ea3eeb3fa96fb88f5269b5ec55a7.tar.gz chromium_src-c76ef7326840ea3eeb3fa96fb88f5269b5ec55a7.tar.bz2 |
Add AppWindow.setVisibleOnAllWorkspaces.
For platforms that support multiple workspaces (currently Mac and Linux), this
allows app windows be visible on all workspaces simultaneously.
API proposal:
https://docs.google.com/document/d/1RC3CYwsrVxS_5hXg6nE3zA9y59G98z9Ezmmmq_Gzx9o/edit?usp=sharing
BUG=384644
Review URL: https://codereview.chromium.org/469993003
Cr-Commit-Position: refs/heads/master@{#293676}
24 files changed, 164 insertions, 0 deletions
diff --git a/apps/ui/views/native_app_window_views.cc b/apps/ui/views/native_app_window_views.cc index 4579c73..e185b8f 100644 --- a/apps/ui/views/native_app_window_views.cc +++ b/apps/ui/views/native_app_window_views.cc @@ -401,4 +401,8 @@ bool NativeAppWindowViews::CanHaveAlphaEnabled() const { return widget_->IsTranslucentWindowOpacitySupported(); } +void NativeAppWindowViews::SetVisibleOnAllWorkspaces(bool always_visible) { + widget_->SetVisibleOnAllWorkspaces(always_visible); +} + } // namespace apps diff --git a/apps/ui/views/native_app_window_views.h b/apps/ui/views/native_app_window_views.h index 9988b17..931faba 100644 --- a/apps/ui/views/native_app_window_views.h +++ b/apps/ui/views/native_app_window_views.h @@ -161,6 +161,7 @@ class NativeAppWindowViews : public extensions::NativeAppWindow, virtual void SetContentSizeConstraints(const gfx::Size& min_size, const gfx::Size& max_size) OVERRIDE; virtual bool CanHaveAlphaEnabled() const OVERRIDE; + virtual void SetVisibleOnAllWorkspaces(bool always_visible) OVERRIDE; // web_modal::WebContentsModalDialogHost implementation. virtual gfx::NativeView GetHostView() const OVERRIDE; diff --git a/chrome/browser/apps/app_window_browsertest.cc b/chrome/browser/apps/app_window_browsertest.cc index 95e6bc8..5651e4d 100644 --- a/chrome/browser/apps/app_window_browsertest.cc +++ b/chrome/browser/apps/app_window_browsertest.cc @@ -257,3 +257,9 @@ IN_PROC_BROWSER_TEST_F(AppWindowAPITest, TestFrameColorsInStable) { ASSERT_TRUE(RunAppWindowAPITest("testFrameColors")) << message_; } #endif + +IN_PROC_BROWSER_TEST_F(AppWindowAPITest, TestVisibleOnAllWorkspaces) { + ASSERT_TRUE( + RunAppWindowAPITestAndWaitForRoundTrip("testVisibleOnAllWorkspaces")) + << message_; +} diff --git a/chrome/browser/extensions/api/app_current_window_internal/app_current_window_internal_api.cc b/chrome/browser/extensions/api/app_current_window_internal/app_current_window_internal_api.cc index 28ffe4e..18554f7 100644 --- a/chrome/browser/extensions/api/app_current_window_internal/app_current_window_internal_api.cc +++ b/chrome/browser/extensions/api/app_current_window_internal/app_current_window_internal_api.cc @@ -27,6 +27,8 @@ namespace SetIcon = app_current_window_internal::SetIcon; namespace SetBadgeIcon = app_current_window_internal::SetBadgeIcon; namespace SetShape = app_current_window_internal::SetShape; namespace SetAlwaysOnTop = app_current_window_internal::SetAlwaysOnTop; +namespace SetVisibleOnAllWorkspaces = + app_current_window_internal::SetVisibleOnAllWorkspaces; using app_current_window_internal::Bounds; using app_current_window_internal::Region; @@ -397,4 +399,18 @@ bool AppCurrentWindowInternalSetAlwaysOnTopFunction::RunWithWindow( return true; } +bool AppCurrentWindowInternalSetVisibleOnAllWorkspacesFunction::RunWithWindow( + AppWindow* window) { + if (GetCurrentChannel() > chrome::VersionInfo::CHANNEL_DEV) { + error_ = kDevChannelOnly; + return false; + } + + scoped_ptr<SetVisibleOnAllWorkspaces::Params> params( + SetVisibleOnAllWorkspaces::Params::Create(*args_)); + CHECK(params.get()); + window->GetBaseWindow()->SetVisibleOnAllWorkspaces(params->always_visible); + return true; +} + } // namespace extensions diff --git a/chrome/browser/extensions/api/app_current_window_internal/app_current_window_internal_api.h b/chrome/browser/extensions/api/app_current_window_internal/app_current_window_internal_api.h index 9d4c85f..8b7aa7b 100644 --- a/chrome/browser/extensions/api/app_current_window_internal/app_current_window_internal_api.h +++ b/chrome/browser/extensions/api/app_current_window_internal/app_current_window_internal_api.h @@ -197,6 +197,18 @@ class AppCurrentWindowInternalSetAlwaysOnTopFunction virtual bool RunWithWindow(AppWindow* window) OVERRIDE; }; +class AppCurrentWindowInternalSetVisibleOnAllWorkspacesFunction + : public AppCurrentWindowInternalExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION( + "app.currentWindowInternal.setVisibleOnAllWorkspaces", + APP_CURRENTWINDOWINTERNAL_SETVISIBLEONALLWORKSPACES) + + protected: + virtual ~AppCurrentWindowInternalSetVisibleOnAllWorkspacesFunction() {} + virtual bool RunWithWindow(AppWindow* window) OVERRIDE; +}; + } // namespace extensions #endif // CHROME_BROWSER_EXTENSIONS_API_APP_CURRENT_WINDOW_INTERNAL_APP_CURRENT_WINDOW_INTERNAL_API_H_ diff --git a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h index 4acf8b2..06b3730 100644 --- a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h +++ b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h @@ -148,6 +148,7 @@ class NativeAppWindowCocoa : public extensions::NativeAppWindow, virtual gfx::Size GetContentMaximumSize() const OVERRIDE; virtual void SetContentSizeConstraints(const gfx::Size& min_size, const gfx::Size& max_size) OVERRIDE; + virtual void SetVisibleOnAllWorkspaces(bool always_visible) OVERRIDE; // WebContentsObserver implementation. virtual void RenderViewCreated(content::RenderViewHost* rvh) OVERRIDE; diff --git a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm index 691de9a..757e36c 100644 --- a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm +++ b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm @@ -60,6 +60,15 @@ void SetFullScreenCollectionBehavior(NSWindow* window, bool allow_fullscreen) { [window setCollectionBehavior:behavior]; } +void SetWorkspacesCollectionBehavior(NSWindow* window, bool always_visible) { + NSWindowCollectionBehavior behavior = [window collectionBehavior]; + if (always_visible) + behavior |= NSWindowCollectionBehaviorCanJoinAllSpaces; + else + behavior &= ~NSWindowCollectionBehaviorCanJoinAllSpaces; + [window setCollectionBehavior:behavior]; +} + void InitCollectionBehavior(NSWindow* window) { // Since always-on-top windows have a higher window level // than NSNormalWindowLevel, they will default to @@ -379,6 +388,8 @@ NativeAppWindowCocoa::NativeAppWindowCocoa( [window setLevel:AlwaysOnTopWindowLevel()]; InitCollectionBehavior(window); + SetWorkspacesCollectionBehavior(window, params.visible_on_all_workspaces); + window_controller_.reset( [[NativeAppWindowController alloc] initWithWindow:window.release()]); @@ -967,6 +978,10 @@ void NativeAppWindowCocoa::SetAlwaysOnTop(bool always_on_top) { NSNormalWindowLevel)]; } +void NativeAppWindowCocoa::SetVisibleOnAllWorkspaces(bool always_visible) { + SetWorkspacesCollectionBehavior(window(), always_visible); +} + NativeAppWindowCocoa::~NativeAppWindowCocoa() { } diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc index 18765b7..44b62d1 100644 --- a/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc +++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc @@ -215,6 +215,8 @@ void ChromeNativeAppWindowViews::InitializeDefaultWindow( if (create_params.alpha_enabled) init_params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; init_params.keep_on_top = create_params.always_on_top; + init_params.visible_on_all_workspaces = + create_params.visible_on_all_workspaces; #if defined(OS_LINUX) && !defined(OS_CHROMEOS) // Set up a custom WM_CLASS for app windows. This allows task switchers in diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json index 06cdddc..50ac97d 100644 --- a/chrome/common/extensions/api/_api_features.json +++ b/chrome/common/extensions/api/_api_features.json @@ -75,6 +75,9 @@ "extension_types": ["platform_app"], "noparent": true }, + "app.window.canSetVisibleOnAllWorkspaces": { + "channel": "dev" + }, "app.currentWindowInternal": { "noparent": true, "internal": true, diff --git a/chrome/common/extensions/api/app_current_window_internal.idl b/chrome/common/extensions/api/app_current_window_internal.idl index 890e75b..f6bd26f 100644 --- a/chrome/common/extensions/api/app_current_window_internal.idl +++ b/chrome/common/extensions/api/app_current_window_internal.idl @@ -52,6 +52,7 @@ static void clearBadge(); static void setShape(Region region); static void setAlwaysOnTop(boolean always_on_top); + static void setVisibleOnAllWorkspaces(boolean always_visible); }; interface Events { diff --git a/chrome/test/data/extensions/platform_apps/window_api/test.js b/chrome/test/data/extensions/platform_apps/window_api/test.js index ebcc444..14396a3 100644 --- a/chrome/test/data/extensions/platform_apps/window_api/test.js +++ b/chrome/test/data/extensions/platform_apps/window_api/test.js @@ -1352,6 +1352,21 @@ function testFrameColors() { ]); } +function testVisibleOnAllWorkspaces() { + chrome.test.runTests([ + function setAndUnsetVisibleOnAllWorkspaces() { + chrome.app.window.create('test.html', { + visibleOnAllWorkspaces: true + }, callbackPass(function(win) { + win.setVisibleOnAllWorkspaces(false); + win.setVisibleOnAllWorkspaces(true); + chrome.test.sendMessage( + 'WaitForRoundTrip', callbackPass(function(reply) {})); + })); + }, + ]); +} + chrome.app.runtime.onLaunched.addListener(function() { chrome.test.sendMessage('Launched', function(reply) { window[reply](); diff --git a/chrome/test/data/extensions/platform_apps/windows_api_visible_on_all_workspaces/in_stable/background.js b/chrome/test/data/extensions/platform_apps/windows_api_visible_on_all_workspaces/in_stable/background.js new file mode 100644 index 0000000..b996cf0 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/windows_api_visible_on_all_workspaces/in_stable/background.js @@ -0,0 +1,28 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// All these tests are run in Stable channel. + +var error = "The visibleOnAllWorkspaces option requires dev channel or newer."; + +chrome.app.runtime.onLaunched.addListener(function() { + chrome.test.runTests([ + + // Check CreateWindowOptions.visibleOnAllWorkspaces(). + function testCreateOption() { + chrome.app.window.create( + 'index.html', { + visibleOnAllWorkspaces: true, + }, chrome.test.callbackFail(error)); + }, + + // Check chrome.app.window.canSetVisibleOnAllWorkspaces(). + function testCanSetVisibleOnAllWorkspaces() { + chrome.test.assertTrue( + chrome.app.window.canSetVisibleOnAllWorkspaces === undefined); + chrome.test.callbackPass(function () {})(); + }, + + ]); +}); diff --git a/chrome/test/data/extensions/platform_apps/windows_api_visible_on_all_workspaces/in_stable/index.html b/chrome/test/data/extensions/platform_apps/windows_api_visible_on_all_workspaces/in_stable/index.html new file mode 100644 index 0000000..c341a40 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/windows_api_visible_on_all_workspaces/in_stable/index.html @@ -0,0 +1 @@ +<!-- empty --> diff --git a/chrome/test/data/extensions/platform_apps/windows_api_visible_on_all_workspaces/in_stable/manifest.json b/chrome/test/data/extensions/platform_apps/windows_api_visible_on_all_workspaces/in_stable/manifest.json new file mode 100644 index 0000000..41633cd --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/windows_api_visible_on_all_workspaces/in_stable/manifest.json @@ -0,0 +1,10 @@ +{ + "name": "Windows API - visibleOnAllWorkspaces (in Stable Channel)", + "version": "1", + "manifest_version": 2, + "app": { + "background": { + "scripts": ["background.js"] + } + } +} diff --git a/extensions/browser/api/app_window/app_window_api.cc b/extensions/browser/api/app_window/app_window_api.cc index b6ecd5a..2a80004 100644 --- a/extensions/browser/api/app_window/app_window_api.cc +++ b/extensions/browser/api/app_window/app_window_api.cc @@ -56,6 +56,8 @@ const char kAlphaEnabledMissingPermission[] = "The alphaEnabled option requires app.window.alpha permission."; const char kAlphaEnabledNeedsFrameNone[] = "The alphaEnabled option can only be used with \"frame: 'none'\"."; +const char kVisibleOnAllWorkspacesWrongChannel[] = + "The visibleOnAllWorkspaces option requires dev channel or newer."; } // namespace app_window_constants const char kNoneFrameOption[] = "none"; @@ -259,6 +261,15 @@ bool AppWindowCreateFunction::RunAsync() { if (options->focused.get()) create_params.focused = *options->focused.get(); + if (options->visible_on_all_workspaces.get()) { + if (AppsClient::Get()->IsCurrentChannelOlderThanDev()) { + error_ = app_window_constants::kVisibleOnAllWorkspacesWrongChannel; + return false; + } + create_params.visible_on_all_workspaces = + *options->visible_on_all_workspaces.get(); + } + if (options->type != app_window::WINDOW_TYPE_PANEL) { switch (options->state) { case app_window::STATE_NONE: diff --git a/extensions/browser/api/app_window/app_window_apitest.cc b/extensions/browser/api/app_window/app_window_apitest.cc index 6d63e15..a63def1 100644 --- a/extensions/browser/api/app_window/app_window_apitest.cc +++ b/extensions/browser/api/app_window/app_window_apitest.cc @@ -160,4 +160,12 @@ IN_PROC_BROWSER_TEST_F(PlatformAppBrowserTest, << message_; } +IN_PROC_BROWSER_TEST_F(PlatformAppBrowserTest, + WindowsApiVisibleOnAllWorkspacesInStable) { + extensions::ScopedCurrentChannel channel(chrome::VersionInfo::CHANNEL_STABLE); + EXPECT_TRUE(RunPlatformAppTest( + "platform_apps/windows_api_visible_on_all_workspaces/in_stable")) + << message_; +} + } // namespace extensions diff --git a/extensions/browser/app_window/app_window.h b/extensions/browser/app_window/app_window.h index f3b8e6e..a7378bd7 100644 --- a/extensions/browser/app_window/app_window.h +++ b/extensions/browser/app_window/app_window.h @@ -180,6 +180,9 @@ class AppWindow : public content::NotificationObserver, // configured to be always on top. Defaults to false. bool always_on_top; + // If true, the window will be visible on all workspaces. Defaults to false. + bool visible_on_all_workspaces; + // The API enables developers to specify content or window bounds. This // function combines them into a single, constrained window size. gfx::Rect GetInitialWindowBounds(const gfx::Insets& frame_insets) const; diff --git a/extensions/browser/app_window/native_app_window.h b/extensions/browser/app_window/native_app_window.h index afd65c5..b5007dd 100644 --- a/extensions/browser/app_window/native_app_window.h +++ b/extensions/browser/app_window/native_app_window.h @@ -92,6 +92,9 @@ class NativeAppWindow : public ui::BaseWindow, virtual void SetContentSizeConstraints(const gfx::Size& min_size, const gfx::Size& max_size) = 0; + // Returns whether the window show be visible on all workspaces. + virtual void SetVisibleOnAllWorkspaces(bool always_visible) = 0; + // Returns false if the underlying native window ignores alpha transparency // when compositing. virtual bool CanHaveAlphaEnabled() const = 0; diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h index a2e4679..3442077 100644 --- a/extensions/browser/extension_function_histogram_value.h +++ b/extensions/browser/extension_function_histogram_value.h @@ -948,6 +948,7 @@ enum HistogramValue { MEDIAGALLERIES_GETALLGALLERYWATCH, MEDIAGALLERIES_REMOVEALLGALLERYWATCH, MANAGEMENT_GETSELF, + APP_CURRENTWINDOWINTERNAL_SETVISIBLEONALLWORKSPACES, // Last entry: Add new entries above and ensure to update // tools/metrics/histograms/histograms.xml. ENUM_BOUNDARY diff --git a/extensions/common/api/app_window.idl b/extensions/common/api/app_window.idl index caff663..3a6e9dc 100644 --- a/extensions/common/api/app_window.idl +++ b/extensions/common/api/app_window.idl @@ -263,6 +263,10 @@ namespace app.window { // If true, the window will be focused when created. Defaults to true. boolean? focused; + + // If true, the window will be visible on all workspaces. + // This is only available on dev channel. + boolean? visibleOnAllWorkspaces; }; // Called in the creating window (parent) before the load event is called in @@ -369,6 +373,11 @@ namespace app.window { // TODO(jackhou): Document this properly before going to stable. [nodoc] static boolean alphaEnabled(); + // For platforms that support multiple workspaces, is this window visible on + // all of them? + // This is only available on dev channel. + static void setVisibleOnAllWorkspaces(boolean alwaysVisible); + // The JavaScript 'window' object for the created child. [instanceOf=Window] object contentWindow; @@ -426,6 +435,10 @@ namespace app.window { // Gets an $(ref:AppWindow) with the given id. If no window with the given id // exists null is returned. This method is new in Chrome 33. [nocompile] static AppWindow get(DOMString id); + + // Does the current platform support windows being visible on all + // workspaces? + [nocompile] static boolean canSetVisibleOnAllWorkspaces(); }; interface Events { diff --git a/extensions/renderer/resources/app_window_custom_bindings.js b/extensions/renderer/resources/app_window_custom_bindings.js index 812d3d6..9020fb2 100644 --- a/extensions/renderer/resources/app_window_custom_bindings.js +++ b/extensions/renderer/resources/app_window_custom_bindings.js @@ -193,6 +193,10 @@ appWindow.registerCustomHook(function(bindingsAPI) { return windows.length > 0 ? windows[0] : null; }); + apiFunctions.setHandleRequest('canSetVisibleOnAllWorkspaces', function() { + return /Mac/.test(navigator.platform) || /Linux/.test(navigator.userAgent); + }); + // This is an internal function, but needs to be bound into a closure // so the correct JS context is used for global variables such as // currentWindowInternal, appWindowData, etc. diff --git a/extensions/shell/browser/shell_native_app_window.cc b/extensions/shell/browser/shell_native_app_window.cc index 260307c..c5b339fb 100644 --- a/extensions/shell/browser/shell_native_app_window.cc +++ b/extensions/shell/browser/shell_native_app_window.cc @@ -238,6 +238,10 @@ void ShellNativeAppWindow::SetContentSizeConstraints( NOTIMPLEMENTED(); } +void ShellNativeAppWindow::SetVisibleOnAllWorkspaces(bool always_visible) { + NOTIMPLEMENTED(); +} + bool ShellNativeAppWindow::CanHaveAlphaEnabled() const { NOTIMPLEMENTED(); return false; diff --git a/extensions/shell/browser/shell_native_app_window.h b/extensions/shell/browser/shell_native_app_window.h index 672b70f..8e0e2ef 100644 --- a/extensions/shell/browser/shell_native_app_window.h +++ b/extensions/shell/browser/shell_native_app_window.h @@ -75,6 +75,7 @@ class ShellNativeAppWindow : public NativeAppWindow { virtual gfx::Size GetContentMaximumSize() const OVERRIDE; virtual void SetContentSizeConstraints(const gfx::Size& min_size, const gfx::Size& max_size) OVERRIDE; + virtual void SetVisibleOnAllWorkspaces(bool always_visible) OVERRIDE; virtual bool CanHaveAlphaEnabled() const OVERRIDE; private: diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 26a03b3..586e4db 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml @@ -41368,6 +41368,7 @@ Therefore, the affected-histogram name has to have at least one dot in it. <int value="887" label="MEDIAGALLERIES_GETALLGALLERYWATCH"/> <int value="888" label="MEDIAGALLERIES_REMOVEALLGALLERYWATCH"/> <int value="889" label="MANAGEMENT_GETSELF"/> + <int value="890" label="APP_CURRENTWINDOWINTERNAL_SETVISIBLEONALLWORKSPACES"/> </enum> <enum name="ExtensionInstallCause" type="int"> |