Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / extensions / browser / api / app_window / app_window_api.cc
blob3901d3476f052ec67d6ce4211769de5f12f8d731
1 // Copyright (c) 2012 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.
5 #include "extensions/browser/api/app_window/app_window_api.h"
7 #include "base/command_line.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/string_util.h"
10 #include "base/time/time.h"
11 #include "base/values.h"
12 #include "content/public/browser/notification_registrar.h"
13 #include "content/public/browser/notification_types.h"
14 #include "content/public/browser/render_process_host.h"
15 #include "content/public/browser/render_view_host.h"
16 #include "content/public/browser/web_contents.h"
17 #include "content/public/common/url_constants.h"
18 #include "extensions/browser/app_window/app_window.h"
19 #include "extensions/browser/app_window/app_window_client.h"
20 #include "extensions/browser/app_window/app_window_contents.h"
21 #include "extensions/browser/app_window/app_window_registry.h"
22 #include "extensions/browser/app_window/native_app_window.h"
23 #include "extensions/browser/extensions_browser_client.h"
24 #include "extensions/common/api/app_window.h"
25 #include "extensions/common/features/simple_feature.h"
26 #include "extensions/common/image_util.h"
27 #include "extensions/common/manifest.h"
28 #include "extensions/common/permissions/permissions_data.h"
29 #include "extensions/common/switches.h"
30 #include "third_party/skia/include/core/SkColor.h"
31 #include "ui/base/ui_base_types.h"
32 #include "ui/gfx/geometry/rect.h"
33 #include "url/gurl.h"
35 namespace app_window = extensions::core_api::app_window;
36 namespace Create = app_window::Create;
38 namespace extensions {
40 namespace app_window_constants {
41 const char kInvalidWindowId[] =
42 "The window id can not be more than 256 characters long.";
43 const char kInvalidColorSpecification[] =
44 "The color specification could not be parsed.";
45 const char kColorWithFrameNone[] = "Windows with no frame cannot have a color.";
46 const char kInactiveColorWithoutColor[] =
47 "frame.inactiveColor must be used with frame.color.";
48 const char kConflictingBoundsOptions[] =
49 "The $1 property cannot be specified for both inner and outer bounds.";
50 const char kAlwaysOnTopPermission[] =
51 "The \"app.window.alwaysOnTop\" permission is required.";
52 const char kInvalidUrlParameter[] =
53 "The URL used for window creation must be local for security reasons.";
54 const char kAlphaEnabledWrongChannel[] =
55 "The alphaEnabled option requires dev channel or newer.";
56 const char kAlphaEnabledMissingPermission[] =
57 "The alphaEnabled option requires app.window.alpha permission.";
58 const char kAlphaEnabledNeedsFrameNone[] =
59 "The alphaEnabled option can only be used with \"frame: 'none'\".";
60 const char kImeWindowMissingPermission[] =
61 "Extensions require the \"app.window.ime\" permission to create windows.";
62 const char kImeOptionIsNotSupported[] =
63 "The \"ime\" option is not supported for platform app.";
64 #if !defined(OS_CHROMEOS)
65 const char kImeWindowUnsupportedPlatform[] =
66 "The \"ime\" option can only be used on ChromeOS.";
67 #else
68 const char kImeOptionMustBeTrueAndNeedsFrameNone[] =
69 "IME extensions must create window with \"ime: true\" and "
70 "\"frame: 'none'\".";
71 #endif
72 } // namespace app_window_constants
74 const char kNoneFrameOption[] = "none";
75 // TODO(benwells): Remove HTML titlebar injection.
76 const char kHtmlFrameOption[] = "experimental-html";
78 namespace {
80 // If the same property is specified for the inner and outer bounds, raise an
81 // error.
82 bool CheckBoundsConflict(const scoped_ptr<int>& inner_property,
83 const scoped_ptr<int>& outer_property,
84 const std::string& property_name,
85 std::string* error) {
86 if (inner_property.get() && outer_property.get()) {
87 std::vector<std::string> subst;
88 subst.push_back(property_name);
89 *error = ReplaceStringPlaceholders(
90 app_window_constants::kConflictingBoundsOptions, subst, NULL);
91 return false;
94 return true;
97 // Copy over the bounds specification properties from the API to the
98 // AppWindow::CreateParams.
99 void CopyBoundsSpec(const app_window::BoundsSpecification* input_spec,
100 AppWindow::BoundsSpecification* create_spec) {
101 if (!input_spec)
102 return;
104 if (input_spec->left.get())
105 create_spec->bounds.set_x(*input_spec->left);
106 if (input_spec->top.get())
107 create_spec->bounds.set_y(*input_spec->top);
108 if (input_spec->width.get())
109 create_spec->bounds.set_width(*input_spec->width);
110 if (input_spec->height.get())
111 create_spec->bounds.set_height(*input_spec->height);
112 if (input_spec->min_width.get())
113 create_spec->minimum_size.set_width(*input_spec->min_width);
114 if (input_spec->min_height.get())
115 create_spec->minimum_size.set_height(*input_spec->min_height);
116 if (input_spec->max_width.get())
117 create_spec->maximum_size.set_width(*input_spec->max_width);
118 if (input_spec->max_height.get())
119 create_spec->maximum_size.set_height(*input_spec->max_height);
122 } // namespace
124 AppWindowCreateFunction::AppWindowCreateFunction()
125 : inject_html_titlebar_(false) {}
127 bool AppWindowCreateFunction::RunAsync() {
128 // Don't create app window if the system is shutting down.
129 if (ExtensionsBrowserClient::Get()->IsShuttingDown())
130 return false;
132 scoped_ptr<Create::Params> params(Create::Params::Create(*args_));
133 EXTENSION_FUNCTION_VALIDATE(params.get());
135 GURL url = extension()->GetResourceURL(params->url);
136 // Allow absolute URLs for component apps, otherwise prepend the extension
137 // path.
138 GURL absolute = GURL(params->url);
139 if (absolute.has_scheme()) {
140 if (extension()->location() == Manifest::COMPONENT) {
141 url = absolute;
142 } else {
143 // Show error when url passed isn't local.
144 error_ = app_window_constants::kInvalidUrlParameter;
145 return false;
149 // TODO(jeremya): figure out a way to pass the opening WebContents through to
150 // AppWindow::Create so we can set the opener at create time rather than
151 // with a hack in AppWindowCustomBindings::GetView().
152 AppWindow::CreateParams create_params;
153 app_window::CreateWindowOptions* options = params->options.get();
154 if (options) {
155 if (options->id.get()) {
156 // TODO(mek): use URL if no id specified?
157 // Limit length of id to 256 characters.
158 if (options->id->length() > 256) {
159 error_ = app_window_constants::kInvalidWindowId;
160 return false;
163 create_params.window_key = *options->id;
165 if (options->singleton && *options->singleton == false) {
166 WriteToConsole(
167 content::CONSOLE_MESSAGE_LEVEL_WARNING,
168 "The 'singleton' option in chrome.apps.window.create() is deprecated!"
169 " Change your code to no longer rely on this.");
172 if (!options->singleton || *options->singleton) {
173 AppWindow* window = AppWindowRegistry::Get(browser_context())
174 ->GetAppWindowForAppAndKey(
175 extension_id(), create_params.window_key);
176 if (window) {
177 content::RenderViewHost* created_view =
178 window->web_contents()->GetRenderViewHost();
179 int view_id = MSG_ROUTING_NONE;
180 if (render_view_host_->GetProcess()->GetID() ==
181 created_view->GetProcess()->GetID()) {
182 view_id = created_view->GetRoutingID();
185 if (!options->hidden.get() || !*options->hidden.get()) {
186 if (options->focused.get() && !*options->focused.get())
187 window->Show(AppWindow::SHOW_INACTIVE);
188 else
189 window->Show(AppWindow::SHOW_ACTIVE);
192 base::DictionaryValue* result = new base::DictionaryValue;
193 result->Set("viewId", new base::FundamentalValue(view_id));
194 window->GetSerializedState(result);
195 result->SetBoolean("existingWindow", true);
196 // TODO(benwells): Remove HTML titlebar injection.
197 result->SetBoolean("injectTitlebar", false);
198 SetResult(result);
199 SendResponse(true);
200 return true;
205 if (!GetBoundsSpec(*options, &create_params, &error_))
206 return false;
208 if (!AppWindowClient::Get()->IsCurrentChannelOlderThanDev() ||
209 extension()->location() == Manifest::COMPONENT) {
210 if (options->type == app_window::WINDOW_TYPE_PANEL) {
211 create_params.window_type = AppWindow::WINDOW_TYPE_PANEL;
215 if (!GetFrameOptions(*options, &create_params))
216 return false;
218 if (extension()->GetType() == Manifest::TYPE_EXTENSION) {
219 // Whitelisted IME extensions are allowed to use this API to create IME
220 // specific windows to show accented characters or suggestions.
221 if (!extension()->permissions_data()->HasAPIPermission(
222 APIPermission::kImeWindowEnabled)) {
223 error_ = app_window_constants::kImeWindowMissingPermission;
224 return false;
227 #if !defined(OS_CHROMEOS)
228 // IME window is only supported on ChromeOS.
229 error_ = app_window_constants::kImeWindowUnsupportedPlatform;
230 return false;
231 #else
232 // IME extensions must create window with "ime: true" and "frame: none".
233 if (!options->ime.get() || !*options->ime.get() ||
234 create_params.frame != AppWindow::FRAME_NONE) {
235 error_ = app_window_constants::kImeOptionMustBeTrueAndNeedsFrameNone;
236 return false;
238 create_params.is_ime_window = true;
239 #endif // OS_CHROMEOS
240 } else {
241 if (options->ime.get()) {
242 error_ = app_window_constants::kImeOptionIsNotSupported;
243 return false;
247 if (options->alpha_enabled.get()) {
248 const char* const kWhitelist[] = {
249 #if defined(OS_CHROMEOS)
250 "B58B99751225318C7EB8CF4688B5434661083E07", // http://crbug.com/410550
251 "06BE211D5F014BAB34BC22D9DDA09C63A81D828E", // http://crbug.com/425539
252 "F94EE6AB36D6C6588670B2B01EB65212D9C64E33",
253 "B9EF10DDFEA11EF77873CC5009809E5037FC4C7A", // http://crbug.com/435380
254 #endif
255 "0F42756099D914A026DADFA182871C015735DD95", // http://crbug.com/323773
256 "2D22CDB6583FD0A13758AEBE8B15E45208B4E9A7",
257 "E7E2461CE072DF036CF9592740196159E2D7C089", // http://crbug.com/356200
258 "A74A4D44C7CFCD8844830E6140C8D763E12DD8F3",
259 "312745D9BF916161191143F6490085EEA0434997",
260 "53041A2FA309EECED01FFC751E7399186E860B2C",
261 "A07A5B743CD82A1C2579DB77D353C98A23201EEF", // http://crbug.com/413748
262 "F16F23C83C5F6DAD9B65A120448B34056DD80691",
263 "0F585FB1D0FDFBEBCE1FEB5E9DFFB6DA476B8C9B"
265 if (AppWindowClient::Get()->IsCurrentChannelOlderThanDev() &&
266 !SimpleFeature::IsIdInArray(
267 extension_id(), kWhitelist, arraysize(kWhitelist))) {
268 error_ = app_window_constants::kAlphaEnabledWrongChannel;
269 return false;
271 if (!extension()->permissions_data()->HasAPIPermission(
272 APIPermission::kAlphaEnabled)) {
273 error_ = app_window_constants::kAlphaEnabledMissingPermission;
274 return false;
276 if (create_params.frame != AppWindow::FRAME_NONE) {
277 error_ = app_window_constants::kAlphaEnabledNeedsFrameNone;
278 return false;
280 #if defined(USE_AURA)
281 create_params.alpha_enabled = *options->alpha_enabled;
282 #else
283 // Transparency is only supported on Aura.
284 // Fallback to creating an opaque window (by ignoring alphaEnabled).
285 #endif
288 if (options->hidden.get())
289 create_params.hidden = *options->hidden.get();
291 if (options->resizable.get())
292 create_params.resizable = *options->resizable.get();
294 if (options->always_on_top.get()) {
295 create_params.always_on_top = *options->always_on_top.get();
297 if (create_params.always_on_top &&
298 !extension()->permissions_data()->HasAPIPermission(
299 APIPermission::kAlwaysOnTopWindows)) {
300 error_ = app_window_constants::kAlwaysOnTopPermission;
301 return false;
305 if (options->focused.get())
306 create_params.focused = *options->focused.get();
308 if (options->visible_on_all_workspaces.get()) {
309 create_params.visible_on_all_workspaces =
310 *options->visible_on_all_workspaces.get();
313 if (options->type != app_window::WINDOW_TYPE_PANEL) {
314 switch (options->state) {
315 case app_window::STATE_NONE:
316 case app_window::STATE_NORMAL:
317 break;
318 case app_window::STATE_FULLSCREEN:
319 create_params.state = ui::SHOW_STATE_FULLSCREEN;
320 break;
321 case app_window::STATE_MAXIMIZED:
322 create_params.state = ui::SHOW_STATE_MAXIMIZED;
323 break;
324 case app_window::STATE_MINIMIZED:
325 create_params.state = ui::SHOW_STATE_MINIMIZED;
326 break;
331 create_params.creator_process_id =
332 render_view_host_->GetProcess()->GetID();
334 AppWindow* app_window =
335 AppWindowClient::Get()->CreateAppWindow(browser_context(), extension());
336 app_window->Init(url, new AppWindowContentsImpl(app_window), create_params);
338 if (ExtensionsBrowserClient::Get()->IsRunningInForcedAppMode() &&
339 !app_window->is_ime_window()) {
340 app_window->ForcedFullscreen();
343 content::RenderViewHost* created_view =
344 app_window->web_contents()->GetRenderViewHost();
345 int view_id = MSG_ROUTING_NONE;
346 if (create_params.creator_process_id == created_view->GetProcess()->GetID())
347 view_id = created_view->GetRoutingID();
349 base::DictionaryValue* result = new base::DictionaryValue;
350 result->Set("viewId", new base::FundamentalValue(view_id));
351 result->Set("injectTitlebar",
352 new base::FundamentalValue(inject_html_titlebar_));
353 result->Set("id", new base::StringValue(app_window->window_key()));
354 app_window->GetSerializedState(result);
355 SetResult(result);
357 if (AppWindowRegistry::Get(browser_context())
358 ->HadDevToolsAttached(created_view)) {
359 AppWindowClient::Get()->OpenDevToolsWindow(
360 app_window->web_contents(),
361 base::Bind(&AppWindowCreateFunction::SendResponse, this, true));
362 return true;
365 SendResponse(true);
366 app_window->WindowEventsReady();
368 return true;
371 bool AppWindowCreateFunction::GetBoundsSpec(
372 const app_window::CreateWindowOptions& options,
373 AppWindow::CreateParams* params,
374 std::string* error) {
375 DCHECK(params);
376 DCHECK(error);
378 if (options.inner_bounds.get() || options.outer_bounds.get()) {
379 // Parse the inner and outer bounds specifications. If developers use the
380 // new API, the deprecated fields will be ignored - do not attempt to merge
381 // them.
383 const app_window::BoundsSpecification* inner_bounds =
384 options.inner_bounds.get();
385 const app_window::BoundsSpecification* outer_bounds =
386 options.outer_bounds.get();
387 if (inner_bounds && outer_bounds) {
388 if (!CheckBoundsConflict(
389 inner_bounds->left, outer_bounds->left, "left", error)) {
390 return false;
392 if (!CheckBoundsConflict(
393 inner_bounds->top, outer_bounds->top, "top", error)) {
394 return false;
396 if (!CheckBoundsConflict(
397 inner_bounds->width, outer_bounds->width, "width", error)) {
398 return false;
400 if (!CheckBoundsConflict(
401 inner_bounds->height, outer_bounds->height, "height", error)) {
402 return false;
404 if (!CheckBoundsConflict(inner_bounds->min_width,
405 outer_bounds->min_width,
406 "minWidth",
407 error)) {
408 return false;
410 if (!CheckBoundsConflict(inner_bounds->min_height,
411 outer_bounds->min_height,
412 "minHeight",
413 error)) {
414 return false;
416 if (!CheckBoundsConflict(inner_bounds->max_width,
417 outer_bounds->max_width,
418 "maxWidth",
419 error)) {
420 return false;
422 if (!CheckBoundsConflict(inner_bounds->max_height,
423 outer_bounds->max_height,
424 "maxHeight",
425 error)) {
426 return false;
430 CopyBoundsSpec(inner_bounds, &(params->content_spec));
431 CopyBoundsSpec(outer_bounds, &(params->window_spec));
432 } else {
433 // Parse deprecated fields.
434 // Due to a bug in NativeAppWindow::GetFrameInsets() on Windows and ChromeOS
435 // the bounds set the position of the window and the size of the content.
436 // This will be preserved as apps may be relying on this behavior.
438 if (options.default_width.get())
439 params->content_spec.bounds.set_width(*options.default_width.get());
440 if (options.default_height.get())
441 params->content_spec.bounds.set_height(*options.default_height.get());
442 if (options.default_left.get())
443 params->window_spec.bounds.set_x(*options.default_left.get());
444 if (options.default_top.get())
445 params->window_spec.bounds.set_y(*options.default_top.get());
447 if (options.width.get())
448 params->content_spec.bounds.set_width(*options.width.get());
449 if (options.height.get())
450 params->content_spec.bounds.set_height(*options.height.get());
451 if (options.left.get())
452 params->window_spec.bounds.set_x(*options.left.get());
453 if (options.top.get())
454 params->window_spec.bounds.set_y(*options.top.get());
456 if (options.bounds.get()) {
457 app_window::ContentBounds* bounds = options.bounds.get();
458 if (bounds->width.get())
459 params->content_spec.bounds.set_width(*bounds->width.get());
460 if (bounds->height.get())
461 params->content_spec.bounds.set_height(*bounds->height.get());
462 if (bounds->left.get())
463 params->window_spec.bounds.set_x(*bounds->left.get());
464 if (bounds->top.get())
465 params->window_spec.bounds.set_y(*bounds->top.get());
468 gfx::Size& minimum_size = params->content_spec.minimum_size;
469 if (options.min_width.get())
470 minimum_size.set_width(*options.min_width);
471 if (options.min_height.get())
472 minimum_size.set_height(*options.min_height);
473 gfx::Size& maximum_size = params->content_spec.maximum_size;
474 if (options.max_width.get())
475 maximum_size.set_width(*options.max_width);
476 if (options.max_height.get())
477 maximum_size.set_height(*options.max_height);
480 return true;
483 AppWindow::Frame AppWindowCreateFunction::GetFrameFromString(
484 const std::string& frame_string) {
485 if (frame_string == kHtmlFrameOption &&
486 (extension()->permissions_data()->HasAPIPermission(
487 APIPermission::kExperimental) ||
488 base::CommandLine::ForCurrentProcess()->HasSwitch(
489 switches::kEnableExperimentalExtensionApis))) {
490 inject_html_titlebar_ = true;
491 return AppWindow::FRAME_NONE;
494 if (frame_string == kNoneFrameOption)
495 return AppWindow::FRAME_NONE;
497 return AppWindow::FRAME_CHROME;
500 bool AppWindowCreateFunction::GetFrameOptions(
501 const app_window::CreateWindowOptions& options,
502 AppWindow::CreateParams* create_params) {
503 if (!options.frame)
504 return true;
506 DCHECK(options.frame->as_string || options.frame->as_frame_options);
507 if (options.frame->as_string) {
508 create_params->frame = GetFrameFromString(*options.frame->as_string);
509 return true;
512 if (options.frame->as_frame_options->type)
513 create_params->frame =
514 GetFrameFromString(*options.frame->as_frame_options->type);
516 if (options.frame->as_frame_options->color.get()) {
517 if (create_params->frame != AppWindow::FRAME_CHROME) {
518 error_ = app_window_constants::kColorWithFrameNone;
519 return false;
522 if (!image_util::ParseCSSColorString(
523 *options.frame->as_frame_options->color,
524 &create_params->active_frame_color)) {
525 error_ = app_window_constants::kInvalidColorSpecification;
526 return false;
529 create_params->has_frame_color = true;
530 create_params->inactive_frame_color = create_params->active_frame_color;
532 if (options.frame->as_frame_options->inactive_color.get()) {
533 if (!image_util::ParseCSSColorString(
534 *options.frame->as_frame_options->inactive_color,
535 &create_params->inactive_frame_color)) {
536 error_ = app_window_constants::kInvalidColorSpecification;
537 return false;
541 return true;
544 if (options.frame->as_frame_options->inactive_color.get()) {
545 error_ = app_window_constants::kInactiveColorWithoutColor;
546 return false;
549 return true;
552 } // namespace extensions