Skip to content

Commit c76ef73

Browse files
jackhou-chromiumCommit bot
authored andcommitted
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}
1 parent 9f11d3b commit c76ef73

File tree

24 files changed

+164
-0
lines changed

24 files changed

+164
-0
lines changed

apps/ui/views/native_app_window_views.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,4 +401,8 @@ bool NativeAppWindowViews::CanHaveAlphaEnabled() const {
401401
return widget_->IsTranslucentWindowOpacitySupported();
402402
}
403403

404+
void NativeAppWindowViews::SetVisibleOnAllWorkspaces(bool always_visible) {
405+
widget_->SetVisibleOnAllWorkspaces(always_visible);
406+
}
407+
404408
} // namespace apps

apps/ui/views/native_app_window_views.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ class NativeAppWindowViews : public extensions::NativeAppWindow,
161161
virtual void SetContentSizeConstraints(const gfx::Size& min_size,
162162
const gfx::Size& max_size) OVERRIDE;
163163
virtual bool CanHaveAlphaEnabled() const OVERRIDE;
164+
virtual void SetVisibleOnAllWorkspaces(bool always_visible) OVERRIDE;
164165

165166
// web_modal::WebContentsModalDialogHost implementation.
166167
virtual gfx::NativeView GetHostView() const OVERRIDE;

chrome/browser/apps/app_window_browsertest.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,3 +257,9 @@ IN_PROC_BROWSER_TEST_F(AppWindowAPITest, TestFrameColorsInStable) {
257257
ASSERT_TRUE(RunAppWindowAPITest("testFrameColors")) << message_;
258258
}
259259
#endif
260+
261+
IN_PROC_BROWSER_TEST_F(AppWindowAPITest, TestVisibleOnAllWorkspaces) {
262+
ASSERT_TRUE(
263+
RunAppWindowAPITestAndWaitForRoundTrip("testVisibleOnAllWorkspaces"))
264+
<< message_;
265+
}

chrome/browser/extensions/api/app_current_window_internal/app_current_window_internal_api.cc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ namespace SetIcon = app_current_window_internal::SetIcon;
2727
namespace SetBadgeIcon = app_current_window_internal::SetBadgeIcon;
2828
namespace SetShape = app_current_window_internal::SetShape;
2929
namespace SetAlwaysOnTop = app_current_window_internal::SetAlwaysOnTop;
30+
namespace SetVisibleOnAllWorkspaces =
31+
app_current_window_internal::SetVisibleOnAllWorkspaces;
3032

3133
using app_current_window_internal::Bounds;
3234
using app_current_window_internal::Region;
@@ -397,4 +399,18 @@ bool AppCurrentWindowInternalSetAlwaysOnTopFunction::RunWithWindow(
397399
return true;
398400
}
399401

402+
bool AppCurrentWindowInternalSetVisibleOnAllWorkspacesFunction::RunWithWindow(
403+
AppWindow* window) {
404+
if (GetCurrentChannel() > chrome::VersionInfo::CHANNEL_DEV) {
405+
error_ = kDevChannelOnly;
406+
return false;
407+
}
408+
409+
scoped_ptr<SetVisibleOnAllWorkspaces::Params> params(
410+
SetVisibleOnAllWorkspaces::Params::Create(*args_));
411+
CHECK(params.get());
412+
window->GetBaseWindow()->SetVisibleOnAllWorkspaces(params->always_visible);
413+
return true;
414+
}
415+
400416
} // namespace extensions

chrome/browser/extensions/api/app_current_window_internal/app_current_window_internal_api.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,18 @@ class AppCurrentWindowInternalSetAlwaysOnTopFunction
197197
virtual bool RunWithWindow(AppWindow* window) OVERRIDE;
198198
};
199199

200+
class AppCurrentWindowInternalSetVisibleOnAllWorkspacesFunction
201+
: public AppCurrentWindowInternalExtensionFunction {
202+
public:
203+
DECLARE_EXTENSION_FUNCTION(
204+
"app.currentWindowInternal.setVisibleOnAllWorkspaces",
205+
APP_CURRENTWINDOWINTERNAL_SETVISIBLEONALLWORKSPACES)
206+
207+
protected:
208+
virtual ~AppCurrentWindowInternalSetVisibleOnAllWorkspacesFunction() {}
209+
virtual bool RunWithWindow(AppWindow* window) OVERRIDE;
210+
};
211+
200212
} // namespace extensions
201213

202214
#endif // CHROME_BROWSER_EXTENSIONS_API_APP_CURRENT_WINDOW_INTERNAL_APP_CURRENT_WINDOW_INTERNAL_API_H_

chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ class NativeAppWindowCocoa : public extensions::NativeAppWindow,
148148
virtual gfx::Size GetContentMaximumSize() const OVERRIDE;
149149
virtual void SetContentSizeConstraints(const gfx::Size& min_size,
150150
const gfx::Size& max_size) OVERRIDE;
151+
virtual void SetVisibleOnAllWorkspaces(bool always_visible) OVERRIDE;
151152

152153
// WebContentsObserver implementation.
153154
virtual void RenderViewCreated(content::RenderViewHost* rvh) OVERRIDE;

chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ void SetFullScreenCollectionBehavior(NSWindow* window, bool allow_fullscreen) {
6060
[window setCollectionBehavior:behavior];
6161
}
6262

63+
void SetWorkspacesCollectionBehavior(NSWindow* window, bool always_visible) {
64+
NSWindowCollectionBehavior behavior = [window collectionBehavior];
65+
if (always_visible)
66+
behavior |= NSWindowCollectionBehaviorCanJoinAllSpaces;
67+
else
68+
behavior &= ~NSWindowCollectionBehaviorCanJoinAllSpaces;
69+
[window setCollectionBehavior:behavior];
70+
}
71+
6372
void InitCollectionBehavior(NSWindow* window) {
6473
// Since always-on-top windows have a higher window level
6574
// than NSNormalWindowLevel, they will default to
@@ -379,6 +388,8 @@ - (void)setMouseDownCanMoveWindow:(BOOL)can_move;
379388
[window setLevel:AlwaysOnTopWindowLevel()];
380389
InitCollectionBehavior(window);
381390

391+
SetWorkspacesCollectionBehavior(window, params.visible_on_all_workspaces);
392+
382393
window_controller_.reset(
383394
[[NativeAppWindowController alloc] initWithWindow:window.release()]);
384395

@@ -967,6 +978,10 @@ - (void)setMouseDownCanMoveWindow:(BOOL)can_move;
967978
NSNormalWindowLevel)];
968979
}
969980

981+
void NativeAppWindowCocoa::SetVisibleOnAllWorkspaces(bool always_visible) {
982+
SetWorkspacesCollectionBehavior(window(), always_visible);
983+
}
984+
970985
NativeAppWindowCocoa::~NativeAppWindowCocoa() {
971986
}
972987

chrome/browser/ui/views/apps/chrome_native_app_window_views.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ void ChromeNativeAppWindowViews::InitializeDefaultWindow(
215215
if (create_params.alpha_enabled)
216216
init_params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
217217
init_params.keep_on_top = create_params.always_on_top;
218+
init_params.visible_on_all_workspaces =
219+
create_params.visible_on_all_workspaces;
218220

219221
#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
220222
// Set up a custom WM_CLASS for app windows. This allows task switchers in

chrome/common/extensions/api/_api_features.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@
7575
"extension_types": ["platform_app"],
7676
"noparent": true
7777
},
78+
"app.window.canSetVisibleOnAllWorkspaces": {
79+
"channel": "dev"
80+
},
7881
"app.currentWindowInternal": {
7982
"noparent": true,
8083
"internal": true,

chrome/common/extensions/api/app_current_window_internal.idl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
static void clearBadge();
5353
static void setShape(Region region);
5454
static void setAlwaysOnTop(boolean always_on_top);
55+
static void setVisibleOnAllWorkspaces(boolean always_visible);
5556
};
5657

5758
interface Events {

chrome/test/data/extensions/platform_apps/window_api/test.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1352,6 +1352,21 @@ function testFrameColors() {
13521352
]);
13531353
}
13541354

1355+
function testVisibleOnAllWorkspaces() {
1356+
chrome.test.runTests([
1357+
function setAndUnsetVisibleOnAllWorkspaces() {
1358+
chrome.app.window.create('test.html', {
1359+
visibleOnAllWorkspaces: true
1360+
}, callbackPass(function(win) {
1361+
win.setVisibleOnAllWorkspaces(false);
1362+
win.setVisibleOnAllWorkspaces(true);
1363+
chrome.test.sendMessage(
1364+
'WaitForRoundTrip', callbackPass(function(reply) {}));
1365+
}));
1366+
},
1367+
]);
1368+
}
1369+
13551370
chrome.app.runtime.onLaunched.addListener(function() {
13561371
chrome.test.sendMessage('Launched', function(reply) {
13571372
window[reply]();
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2014 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// All these tests are run in Stable channel.
6+
7+
var error = "The visibleOnAllWorkspaces option requires dev channel or newer.";
8+
9+
chrome.app.runtime.onLaunched.addListener(function() {
10+
chrome.test.runTests([
11+
12+
// Check CreateWindowOptions.visibleOnAllWorkspaces().
13+
function testCreateOption() {
14+
chrome.app.window.create(
15+
'index.html', {
16+
visibleOnAllWorkspaces: true,
17+
}, chrome.test.callbackFail(error));
18+
},
19+
20+
// Check chrome.app.window.canSetVisibleOnAllWorkspaces().
21+
function testCanSetVisibleOnAllWorkspaces() {
22+
chrome.test.assertTrue(
23+
chrome.app.window.canSetVisibleOnAllWorkspaces === undefined);
24+
chrome.test.callbackPass(function () {})();
25+
},
26+
27+
]);
28+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<!-- empty -->
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"name": "Windows API - visibleOnAllWorkspaces (in Stable Channel)",
3+
"version": "1",
4+
"manifest_version": 2,
5+
"app": {
6+
"background": {
7+
"scripts": ["background.js"]
8+
}
9+
}
10+
}

extensions/browser/api/app_window/app_window_api.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ const char kAlphaEnabledMissingPermission[] =
5656
"The alphaEnabled option requires app.window.alpha permission.";
5757
const char kAlphaEnabledNeedsFrameNone[] =
5858
"The alphaEnabled option can only be used with \"frame: 'none'\".";
59+
const char kVisibleOnAllWorkspacesWrongChannel[] =
60+
"The visibleOnAllWorkspaces option requires dev channel or newer.";
5961
} // namespace app_window_constants
6062

6163
const char kNoneFrameOption[] = "none";
@@ -259,6 +261,15 @@ bool AppWindowCreateFunction::RunAsync() {
259261
if (options->focused.get())
260262
create_params.focused = *options->focused.get();
261263

264+
if (options->visible_on_all_workspaces.get()) {
265+
if (AppsClient::Get()->IsCurrentChannelOlderThanDev()) {
266+
error_ = app_window_constants::kVisibleOnAllWorkspacesWrongChannel;
267+
return false;
268+
}
269+
create_params.visible_on_all_workspaces =
270+
*options->visible_on_all_workspaces.get();
271+
}
272+
262273
if (options->type != app_window::WINDOW_TYPE_PANEL) {
263274
switch (options->state) {
264275
case app_window::STATE_NONE:

extensions/browser/api/app_window/app_window_apitest.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,12 @@ IN_PROC_BROWSER_TEST_F(PlatformAppBrowserTest,
160160
<< message_;
161161
}
162162

163+
IN_PROC_BROWSER_TEST_F(PlatformAppBrowserTest,
164+
WindowsApiVisibleOnAllWorkspacesInStable) {
165+
extensions::ScopedCurrentChannel channel(chrome::VersionInfo::CHANNEL_STABLE);
166+
EXPECT_TRUE(RunPlatformAppTest(
167+
"platform_apps/windows_api_visible_on_all_workspaces/in_stable"))
168+
<< message_;
169+
}
170+
163171
} // namespace extensions

extensions/browser/app_window/app_window.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ class AppWindow : public content::NotificationObserver,
180180
// configured to be always on top. Defaults to false.
181181
bool always_on_top;
182182

183+
// If true, the window will be visible on all workspaces. Defaults to false.
184+
bool visible_on_all_workspaces;
185+
183186
// The API enables developers to specify content or window bounds. This
184187
// function combines them into a single, constrained window size.
185188
gfx::Rect GetInitialWindowBounds(const gfx::Insets& frame_insets) const;

extensions/browser/app_window/native_app_window.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ class NativeAppWindow : public ui::BaseWindow,
9292
virtual void SetContentSizeConstraints(const gfx::Size& min_size,
9393
const gfx::Size& max_size) = 0;
9494

95+
// Returns whether the window show be visible on all workspaces.
96+
virtual void SetVisibleOnAllWorkspaces(bool always_visible) = 0;
97+
9598
// Returns false if the underlying native window ignores alpha transparency
9699
// when compositing.
97100
virtual bool CanHaveAlphaEnabled() const = 0;

extensions/browser/extension_function_histogram_value.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,7 @@ enum HistogramValue {
948948
MEDIAGALLERIES_GETALLGALLERYWATCH,
949949
MEDIAGALLERIES_REMOVEALLGALLERYWATCH,
950950
MANAGEMENT_GETSELF,
951+
APP_CURRENTWINDOWINTERNAL_SETVISIBLEONALLWORKSPACES,
951952
// Last entry: Add new entries above and ensure to update
952953
// tools/metrics/histograms/histograms.xml.
953954
ENUM_BOUNDARY

extensions/common/api/app_window.idl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,10 @@ namespace app.window {
263263

264264
// If true, the window will be focused when created. Defaults to true.
265265
boolean? focused;
266+
267+
// If true, the window will be visible on all workspaces.
268+
// This is only available on dev channel.
269+
boolean? visibleOnAllWorkspaces;
266270
};
267271

268272
// Called in the creating window (parent) before the load event is called in
@@ -369,6 +373,11 @@ namespace app.window {
369373
// TODO(jackhou): Document this properly before going to stable.
370374
[nodoc] static boolean alphaEnabled();
371375

376+
// For platforms that support multiple workspaces, is this window visible on
377+
// all of them?
378+
// This is only available on dev channel.
379+
static void setVisibleOnAllWorkspaces(boolean alwaysVisible);
380+
372381
// The JavaScript 'window' object for the created child.
373382
[instanceOf=Window] object contentWindow;
374383

@@ -426,6 +435,10 @@ namespace app.window {
426435
// Gets an $(ref:AppWindow) with the given id. If no window with the given id
427436
// exists null is returned. This method is new in Chrome 33.
428437
[nocompile] static AppWindow get(DOMString id);
438+
439+
// Does the current platform support windows being visible on all
440+
// workspaces?
441+
[nocompile] static boolean canSetVisibleOnAllWorkspaces();
429442
};
430443

431444
interface Events {

extensions/renderer/resources/app_window_custom_bindings.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@ appWindow.registerCustomHook(function(bindingsAPI) {
193193
return windows.length > 0 ? windows[0] : null;
194194
});
195195

196+
apiFunctions.setHandleRequest('canSetVisibleOnAllWorkspaces', function() {
197+
return /Mac/.test(navigator.platform) || /Linux/.test(navigator.userAgent);
198+
});
199+
196200
// This is an internal function, but needs to be bound into a closure
197201
// so the correct JS context is used for global variables such as
198202
// currentWindowInternal, appWindowData, etc.

extensions/shell/browser/shell_native_app_window.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,10 @@ void ShellNativeAppWindow::SetContentSizeConstraints(
238238
NOTIMPLEMENTED();
239239
}
240240

241+
void ShellNativeAppWindow::SetVisibleOnAllWorkspaces(bool always_visible) {
242+
NOTIMPLEMENTED();
243+
}
244+
241245
bool ShellNativeAppWindow::CanHaveAlphaEnabled() const {
242246
NOTIMPLEMENTED();
243247
return false;

extensions/shell/browser/shell_native_app_window.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ class ShellNativeAppWindow : public NativeAppWindow {
7575
virtual gfx::Size GetContentMaximumSize() const OVERRIDE;
7676
virtual void SetContentSizeConstraints(const gfx::Size& min_size,
7777
const gfx::Size& max_size) OVERRIDE;
78+
virtual void SetVisibleOnAllWorkspaces(bool always_visible) OVERRIDE;
7879
virtual bool CanHaveAlphaEnabled() const OVERRIDE;
7980

8081
private:

tools/metrics/histograms/histograms.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41368,6 +41368,7 @@ Therefore, the affected-histogram name has to have at least one dot in it.
4136841368
<int value="887" label="MEDIAGALLERIES_GETALLGALLERYWATCH"/>
4136941369
<int value="888" label="MEDIAGALLERIES_REMOVEALLGALLERYWATCH"/>
4137041370
<int value="889" label="MANAGEMENT_GETSELF"/>
41371+
<int value="890" label="APP_CURRENTWINDOWINTERNAL_SETVISIBLEONALLWORKSPACES"/>
4137141372
</enum>
4137241373

4137341374
<enum name="ExtensionInstallCause" type="int">

0 commit comments

Comments
 (0)