Commit dcc81485 authored by jackhou's avatar jackhou Committed by Commit bot

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

Committed: https://chromium.googlesource.com/chromium/src/+/97adb18915b7909429b51d829e7c4aeb66c57d64

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

Cr-Commit-Position: refs/heads/master@{#293832}
parent 090e36f0
...@@ -401,4 +401,8 @@ bool NativeAppWindowViews::CanHaveAlphaEnabled() const { ...@@ -401,4 +401,8 @@ bool NativeAppWindowViews::CanHaveAlphaEnabled() const {
return widget_->IsTranslucentWindowOpacitySupported(); return widget_->IsTranslucentWindowOpacitySupported();
} }
void NativeAppWindowViews::SetVisibleOnAllWorkspaces(bool always_visible) {
widget_->SetVisibleOnAllWorkspaces(always_visible);
}
} // namespace apps } // namespace apps
...@@ -161,6 +161,7 @@ class NativeAppWindowViews : public extensions::NativeAppWindow, ...@@ -161,6 +161,7 @@ class NativeAppWindowViews : public extensions::NativeAppWindow,
virtual void SetContentSizeConstraints(const gfx::Size& min_size, virtual void SetContentSizeConstraints(const gfx::Size& min_size,
const gfx::Size& max_size) OVERRIDE; const gfx::Size& max_size) OVERRIDE;
virtual bool CanHaveAlphaEnabled() const OVERRIDE; virtual bool CanHaveAlphaEnabled() const OVERRIDE;
virtual void SetVisibleOnAllWorkspaces(bool always_visible) OVERRIDE;
// web_modal::WebContentsModalDialogHost implementation. // web_modal::WebContentsModalDialogHost implementation.
virtual gfx::NativeView GetHostView() const OVERRIDE; virtual gfx::NativeView GetHostView() const OVERRIDE;
......
...@@ -257,3 +257,9 @@ IN_PROC_BROWSER_TEST_F(AppWindowAPITest, TestFrameColorsInStable) { ...@@ -257,3 +257,9 @@ IN_PROC_BROWSER_TEST_F(AppWindowAPITest, TestFrameColorsInStable) {
ASSERT_TRUE(RunAppWindowAPITest("testFrameColors")) << message_; ASSERT_TRUE(RunAppWindowAPITest("testFrameColors")) << message_;
} }
#endif #endif
IN_PROC_BROWSER_TEST_F(AppWindowAPITest, TestVisibleOnAllWorkspaces) {
ASSERT_TRUE(
RunAppWindowAPITestAndWaitForRoundTrip("testVisibleOnAllWorkspaces"))
<< message_;
}
...@@ -27,6 +27,8 @@ namespace SetIcon = app_current_window_internal::SetIcon; ...@@ -27,6 +27,8 @@ namespace SetIcon = app_current_window_internal::SetIcon;
namespace SetBadgeIcon = app_current_window_internal::SetBadgeIcon; namespace SetBadgeIcon = app_current_window_internal::SetBadgeIcon;
namespace SetShape = app_current_window_internal::SetShape; namespace SetShape = app_current_window_internal::SetShape;
namespace SetAlwaysOnTop = app_current_window_internal::SetAlwaysOnTop; 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::Bounds;
using app_current_window_internal::Region; using app_current_window_internal::Region;
...@@ -397,4 +399,18 @@ bool AppCurrentWindowInternalSetAlwaysOnTopFunction::RunWithWindow( ...@@ -397,4 +399,18 @@ bool AppCurrentWindowInternalSetAlwaysOnTopFunction::RunWithWindow(
return true; 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 } // namespace extensions
...@@ -197,6 +197,18 @@ class AppCurrentWindowInternalSetAlwaysOnTopFunction ...@@ -197,6 +197,18 @@ class AppCurrentWindowInternalSetAlwaysOnTopFunction
virtual bool RunWithWindow(AppWindow* window) OVERRIDE; 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 } // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_APP_CURRENT_WINDOW_INTERNAL_APP_CURRENT_WINDOW_INTERNAL_API_H_ #endif // CHROME_BROWSER_EXTENSIONS_API_APP_CURRENT_WINDOW_INTERNAL_APP_CURRENT_WINDOW_INTERNAL_API_H_
...@@ -148,6 +148,7 @@ class NativeAppWindowCocoa : public extensions::NativeAppWindow, ...@@ -148,6 +148,7 @@ class NativeAppWindowCocoa : public extensions::NativeAppWindow,
virtual gfx::Size GetContentMaximumSize() const OVERRIDE; virtual gfx::Size GetContentMaximumSize() const OVERRIDE;
virtual void SetContentSizeConstraints(const gfx::Size& min_size, virtual void SetContentSizeConstraints(const gfx::Size& min_size,
const gfx::Size& max_size) OVERRIDE; const gfx::Size& max_size) OVERRIDE;
virtual void SetVisibleOnAllWorkspaces(bool always_visible) OVERRIDE;
// WebContentsObserver implementation. // WebContentsObserver implementation.
virtual void RenderViewCreated(content::RenderViewHost* rvh) OVERRIDE; virtual void RenderViewCreated(content::RenderViewHost* rvh) OVERRIDE;
......
...@@ -60,6 +60,15 @@ void SetFullScreenCollectionBehavior(NSWindow* window, bool allow_fullscreen) { ...@@ -60,6 +60,15 @@ void SetFullScreenCollectionBehavior(NSWindow* window, bool allow_fullscreen) {
[window setCollectionBehavior:behavior]; [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) { void InitCollectionBehavior(NSWindow* window) {
// Since always-on-top windows have a higher window level // Since always-on-top windows have a higher window level
// than NSNormalWindowLevel, they will default to // than NSNormalWindowLevel, they will default to
...@@ -379,6 +388,8 @@ NativeAppWindowCocoa::NativeAppWindowCocoa( ...@@ -379,6 +388,8 @@ NativeAppWindowCocoa::NativeAppWindowCocoa(
[window setLevel:AlwaysOnTopWindowLevel()]; [window setLevel:AlwaysOnTopWindowLevel()];
InitCollectionBehavior(window); InitCollectionBehavior(window);
SetWorkspacesCollectionBehavior(window, params.visible_on_all_workspaces);
window_controller_.reset( window_controller_.reset(
[[NativeAppWindowController alloc] initWithWindow:window.release()]); [[NativeAppWindowController alloc] initWithWindow:window.release()]);
...@@ -967,6 +978,10 @@ void NativeAppWindowCocoa::SetAlwaysOnTop(bool always_on_top) { ...@@ -967,6 +978,10 @@ void NativeAppWindowCocoa::SetAlwaysOnTop(bool always_on_top) {
NSNormalWindowLevel)]; NSNormalWindowLevel)];
} }
void NativeAppWindowCocoa::SetVisibleOnAllWorkspaces(bool always_visible) {
SetWorkspacesCollectionBehavior(window(), always_visible);
}
NativeAppWindowCocoa::~NativeAppWindowCocoa() { NativeAppWindowCocoa::~NativeAppWindowCocoa() {
} }
......
...@@ -215,6 +215,8 @@ void ChromeNativeAppWindowViews::InitializeDefaultWindow( ...@@ -215,6 +215,8 @@ void ChromeNativeAppWindowViews::InitializeDefaultWindow(
if (create_params.alpha_enabled) if (create_params.alpha_enabled)
init_params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; init_params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
init_params.keep_on_top = create_params.always_on_top; 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) #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
// Set up a custom WM_CLASS for app windows. This allows task switchers in // Set up a custom WM_CLASS for app windows. This allows task switchers in
......
...@@ -75,6 +75,9 @@ ...@@ -75,6 +75,9 @@
"extension_types": ["platform_app"], "extension_types": ["platform_app"],
"noparent": true "noparent": true
}, },
"app.window.canSetVisibleOnAllWorkspaces": {
"channel": "dev"
},
"app.currentWindowInternal": { "app.currentWindowInternal": {
"noparent": true, "noparent": true,
"internal": true, "internal": true,
......
...@@ -52,6 +52,7 @@ ...@@ -52,6 +52,7 @@
static void clearBadge(); static void clearBadge();
static void setShape(Region region); static void setShape(Region region);
static void setAlwaysOnTop(boolean always_on_top); static void setAlwaysOnTop(boolean always_on_top);
static void setVisibleOnAllWorkspaces(boolean always_visible);
}; };
interface Events { interface Events {
......
...@@ -1352,6 +1352,21 @@ function testFrameColors() { ...@@ -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.app.runtime.onLaunched.addListener(function() {
chrome.test.sendMessage('Launched', function(reply) { chrome.test.sendMessage('Launched', function(reply) {
window[reply](); window[reply]();
......
// 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 () {})();
},
]);
});
{
"name": "Windows API - visibleOnAllWorkspaces (in Stable Channel)",
"version": "1",
"manifest_version": 2,
"app": {
"background": {
"scripts": ["background.js"]
}
}
}
...@@ -56,6 +56,8 @@ const char kAlphaEnabledMissingPermission[] = ...@@ -56,6 +56,8 @@ const char kAlphaEnabledMissingPermission[] =
"The alphaEnabled option requires app.window.alpha permission."; "The alphaEnabled option requires app.window.alpha permission.";
const char kAlphaEnabledNeedsFrameNone[] = const char kAlphaEnabledNeedsFrameNone[] =
"The alphaEnabled option can only be used with \"frame: 'none'\"."; "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 } // namespace app_window_constants
const char kNoneFrameOption[] = "none"; const char kNoneFrameOption[] = "none";
...@@ -259,6 +261,15 @@ bool AppWindowCreateFunction::RunAsync() { ...@@ -259,6 +261,15 @@ bool AppWindowCreateFunction::RunAsync() {
if (options->focused.get()) if (options->focused.get())
create_params.focused = *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) { if (options->type != app_window::WINDOW_TYPE_PANEL) {
switch (options->state) { switch (options->state) {
case app_window::STATE_NONE: case app_window::STATE_NONE:
......
...@@ -160,4 +160,12 @@ IN_PROC_BROWSER_TEST_F(PlatformAppBrowserTest, ...@@ -160,4 +160,12 @@ IN_PROC_BROWSER_TEST_F(PlatformAppBrowserTest,
<< message_; << 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 } // namespace extensions
...@@ -160,7 +160,8 @@ AppWindow::CreateParams::CreateParams() ...@@ -160,7 +160,8 @@ AppWindow::CreateParams::CreateParams()
hidden(false), hidden(false),
resizable(true), resizable(true),
focused(true), focused(true),
always_on_top(false) { always_on_top(false),
visible_on_all_workspaces(false) {
} }
AppWindow::CreateParams::~CreateParams() {} AppWindow::CreateParams::~CreateParams() {}
......
...@@ -180,6 +180,9 @@ class AppWindow : public content::NotificationObserver, ...@@ -180,6 +180,9 @@ class AppWindow : public content::NotificationObserver,
// configured to be always on top. Defaults to false. // configured to be always on top. Defaults to false.
bool always_on_top; 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 // The API enables developers to specify content or window bounds. This
// function combines them into a single, constrained window size. // function combines them into a single, constrained window size.
gfx::Rect GetInitialWindowBounds(const gfx::Insets& frame_insets) const; gfx::Rect GetInitialWindowBounds(const gfx::Insets& frame_insets) const;
......
...@@ -92,6 +92,9 @@ class NativeAppWindow : public ui::BaseWindow, ...@@ -92,6 +92,9 @@ class NativeAppWindow : public ui::BaseWindow,
virtual void SetContentSizeConstraints(const gfx::Size& min_size, virtual void SetContentSizeConstraints(const gfx::Size& min_size,
const gfx::Size& max_size) = 0; 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 // Returns false if the underlying native window ignores alpha transparency
// when compositing. // when compositing.
virtual bool CanHaveAlphaEnabled() const = 0; virtual bool CanHaveAlphaEnabled() const = 0;
......
...@@ -948,6 +948,7 @@ enum HistogramValue { ...@@ -948,6 +948,7 @@ enum HistogramValue {
MEDIAGALLERIES_GETALLGALLERYWATCH, MEDIAGALLERIES_GETALLGALLERYWATCH,
MEDIAGALLERIES_REMOVEALLGALLERYWATCH, MEDIAGALLERIES_REMOVEALLGALLERYWATCH,
MANAGEMENT_GETSELF, MANAGEMENT_GETSELF,
APP_CURRENTWINDOWINTERNAL_SETVISIBLEONALLWORKSPACES,
// Last entry: Add new entries above and ensure to update // Last entry: Add new entries above and ensure to update
// tools/metrics/histograms/histograms.xml. // tools/metrics/histograms/histograms.xml.
ENUM_BOUNDARY ENUM_BOUNDARY
......
...@@ -263,6 +263,10 @@ namespace app.window { ...@@ -263,6 +263,10 @@ namespace app.window {
// If true, the window will be focused when created. Defaults to true. // If true, the window will be focused when created. Defaults to true.
boolean? focused; 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 // Called in the creating window (parent) before the load event is called in
...@@ -369,6 +373,11 @@ namespace app.window { ...@@ -369,6 +373,11 @@ namespace app.window {
// TODO(jackhou): Document this properly before going to stable. // TODO(jackhou): Document this properly before going to stable.
[nodoc] static boolean alphaEnabled(); [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. // The JavaScript 'window' object for the created child.
[instanceOf=Window] object contentWindow; [instanceOf=Window] object contentWindow;
...@@ -426,6 +435,10 @@ namespace app.window { ...@@ -426,6 +435,10 @@ namespace app.window {
// Gets an $(ref:AppWindow) with the given id. If no window with the given id // 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. // exists null is returned. This method is new in Chrome 33.
[nocompile] static AppWindow get(DOMString id); [nocompile] static AppWindow get(DOMString id);
// Does the current platform support windows being visible on all
// workspaces?
[nocompile] static boolean canSetVisibleOnAllWorkspaces();
}; };
interface Events { interface Events {
......
...@@ -193,6 +193,10 @@ appWindow.registerCustomHook(function(bindingsAPI) { ...@@ -193,6 +193,10 @@ appWindow.registerCustomHook(function(bindingsAPI) {
return windows.length > 0 ? windows[0] : null; 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 // 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 // so the correct JS context is used for global variables such as
// currentWindowInternal, appWindowData, etc. // currentWindowInternal, appWindowData, etc.
......
...@@ -238,6 +238,10 @@ void ShellNativeAppWindow::SetContentSizeConstraints( ...@@ -238,6 +238,10 @@ void ShellNativeAppWindow::SetContentSizeConstraints(
NOTIMPLEMENTED(); NOTIMPLEMENTED();
} }
void ShellNativeAppWindow::SetVisibleOnAllWorkspaces(bool always_visible) {
NOTIMPLEMENTED();
}
bool ShellNativeAppWindow::CanHaveAlphaEnabled() const { bool ShellNativeAppWindow::CanHaveAlphaEnabled() const {
NOTIMPLEMENTED(); NOTIMPLEMENTED();
return false; return false;
......
...@@ -75,6 +75,7 @@ class ShellNativeAppWindow : public NativeAppWindow { ...@@ -75,6 +75,7 @@ class ShellNativeAppWindow : public NativeAppWindow {
virtual gfx::Size GetContentMaximumSize() const OVERRIDE; virtual gfx::Size GetContentMaximumSize() const OVERRIDE;
virtual void SetContentSizeConstraints(const gfx::Size& min_size, virtual void SetContentSizeConstraints(const gfx::Size& min_size,
const gfx::Size& max_size) OVERRIDE; const gfx::Size& max_size) OVERRIDE;
virtual void SetVisibleOnAllWorkspaces(bool always_visible) OVERRIDE;
virtual bool CanHaveAlphaEnabled() const OVERRIDE; virtual bool CanHaveAlphaEnabled() const OVERRIDE;
private: private:
......
...@@ -41408,6 +41408,7 @@ Therefore, the affected-histogram name has to have at least one dot in it. ...@@ -41408,6 +41408,7 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<int value="887" label="MEDIAGALLERIES_GETALLGALLERYWATCH"/> <int value="887" label="MEDIAGALLERIES_GETALLGALLERYWATCH"/>
<int value="888" label="MEDIAGALLERIES_REMOVEALLGALLERYWATCH"/> <int value="888" label="MEDIAGALLERIES_REMOVEALLGALLERYWATCH"/>
<int value="889" label="MANAGEMENT_GETSELF"/> <int value="889" label="MANAGEMENT_GETSELF"/>
<int value="890" label="APP_CURRENTWINDOWINTERNAL_SETVISIBLEONALLWORKSPACES"/>
</enum> </enum>
<enum name="ExtensionInstallCause" type="int"> <enum name="ExtensionInstallCause" type="int">
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment