Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(98)

Side by Side Diff: chrome/browser/extensions/api/app_window/app_window_api.cc

Issue 534163002: Move app.window API implementation to extensions (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix GN Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "chrome/browser/extensions/api/app_window/app_window_api.h"
6
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 "chrome/common/extensions/api/app_window.h"
13 #include "content/public/browser/notification_registrar.h"
14 #include "content/public/browser/notification_types.h"
15 #include "content/public/browser/render_process_host.h"
16 #include "content/public/browser/render_view_host.h"
17 #include "content/public/browser/web_contents.h"
18 #include "content/public/common/url_constants.h"
19 #include "extensions/browser/app_window/app_window.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/apps_client.h"
23 #include "extensions/browser/app_window/native_app_window.h"
24 #include "extensions/browser/extensions_browser_client.h"
25 #include "extensions/browser/image_util.h"
26 #include "extensions/common/features/simple_feature.h"
27 #include "extensions/common/permissions/permissions_data.h"
28 #include "extensions/common/switches.h"
29 #include "third_party/skia/include/core/SkColor.h"
30 #include "ui/base/ui_base_types.h"
31 #include "ui/gfx/rect.h"
32 #include "url/gurl.h"
33
34 namespace app_window = extensions::api::app_window;
35 namespace Create = app_window::Create;
36
37 namespace extensions {
38
39 namespace app_window_constants {
40 const char kInvalidWindowId[] =
41 "The window id can not be more than 256 characters long.";
42 const char kInvalidColorSpecification[] =
43 "The color specification could not be parsed.";
44 const char kColorWithFrameNone[] = "Windows with no frame cannot have a color.";
45 const char kInactiveColorWithoutColor[] =
46 "frame.inactiveColor must be used with frame.color.";
47 const char kConflictingBoundsOptions[] =
48 "The $1 property cannot be specified for both inner and outer bounds.";
49 const char kAlwaysOnTopPermission[] =
50 "The \"app.window.alwaysOnTop\" permission is required.";
51 const char kInvalidUrlParameter[] =
52 "The URL used for window creation must be local for security reasons.";
53 const char kAlphaEnabledWrongChannel[] =
54 "The alphaEnabled option requires dev channel or newer.";
55 const char kAlphaEnabledMissingPermission[] =
56 "The alphaEnabled option requires app.window.alpha permission.";
57 const char kAlphaEnabledNeedsFrameNone[] =
58 "The alphaEnabled option can only be used with \"frame: 'none'\".";
59 } // namespace app_window_constants
60
61 const char kNoneFrameOption[] = "none";
62 // TODO(benwells): Remove HTML titlebar injection.
63 const char kHtmlFrameOption[] = "experimental-html";
64
65 namespace {
66
67 // If the same property is specified for the inner and outer bounds, raise an
68 // error.
69 bool CheckBoundsConflict(const scoped_ptr<int>& inner_property,
70 const scoped_ptr<int>& outer_property,
71 const std::string& property_name,
72 std::string* error) {
73 if (inner_property.get() && outer_property.get()) {
74 std::vector<std::string> subst;
75 subst.push_back(property_name);
76 *error = ReplaceStringPlaceholders(
77 app_window_constants::kConflictingBoundsOptions, subst, NULL);
78 return false;
79 }
80
81 return true;
82 }
83
84 // Copy over the bounds specification properties from the API to the
85 // AppWindow::CreateParams.
86 void CopyBoundsSpec(
87 const extensions::api::app_window::BoundsSpecification* input_spec,
88 AppWindow::BoundsSpecification* create_spec) {
89 if (!input_spec)
90 return;
91
92 if (input_spec->left.get())
93 create_spec->bounds.set_x(*input_spec->left);
94 if (input_spec->top.get())
95 create_spec->bounds.set_y(*input_spec->top);
96 if (input_spec->width.get())
97 create_spec->bounds.set_width(*input_spec->width);
98 if (input_spec->height.get())
99 create_spec->bounds.set_height(*input_spec->height);
100 if (input_spec->min_width.get())
101 create_spec->minimum_size.set_width(*input_spec->min_width);
102 if (input_spec->min_height.get())
103 create_spec->minimum_size.set_height(*input_spec->min_height);
104 if (input_spec->max_width.get())
105 create_spec->maximum_size.set_width(*input_spec->max_width);
106 if (input_spec->max_height.get())
107 create_spec->maximum_size.set_height(*input_spec->max_height);
108 }
109
110 } // namespace
111
112 AppWindowCreateFunction::AppWindowCreateFunction()
113 : inject_html_titlebar_(false) {}
114
115 bool AppWindowCreateFunction::RunAsync() {
116 // Don't create app window if the system is shutting down.
117 if (extensions::ExtensionsBrowserClient::Get()->IsShuttingDown())
118 return false;
119
120 scoped_ptr<Create::Params> params(Create::Params::Create(*args_));
121 EXTENSION_FUNCTION_VALIDATE(params.get());
122
123 GURL url = extension()->GetResourceURL(params->url);
124 // Allow absolute URLs for component apps, otherwise prepend the extension
125 // path.
126 GURL absolute = GURL(params->url);
127 if (absolute.has_scheme()) {
128 if (extension()->location() == extensions::Manifest::COMPONENT) {
129 url = absolute;
130 } else {
131 // Show error when url passed isn't local.
132 error_ = app_window_constants::kInvalidUrlParameter;
133 return false;
134 }
135 }
136
137 // TODO(jeremya): figure out a way to pass the opening WebContents through to
138 // AppWindow::Create so we can set the opener at create time rather than
139 // with a hack in AppWindowCustomBindings::GetView().
140 AppWindow::CreateParams create_params;
141 app_window::CreateWindowOptions* options = params->options.get();
142 if (options) {
143 if (options->id.get()) {
144 // TODO(mek): use URL if no id specified?
145 // Limit length of id to 256 characters.
146 if (options->id->length() > 256) {
147 error_ = app_window_constants::kInvalidWindowId;
148 return false;
149 }
150
151 create_params.window_key = *options->id;
152
153 if (options->singleton && *options->singleton == false) {
154 WriteToConsole(
155 content::CONSOLE_MESSAGE_LEVEL_WARNING,
156 "The 'singleton' option in chrome.apps.window.create() is deprecated!"
157 " Change your code to no longer rely on this.");
158 }
159
160 if (!options->singleton || *options->singleton) {
161 AppWindow* window = AppWindowRegistry::Get(browser_context())
162 ->GetAppWindowForAppAndKey(
163 extension_id(), create_params.window_key);
164 if (window) {
165 content::RenderViewHost* created_view =
166 window->web_contents()->GetRenderViewHost();
167 int view_id = MSG_ROUTING_NONE;
168 if (render_view_host_->GetProcess()->GetID() ==
169 created_view->GetProcess()->GetID()) {
170 view_id = created_view->GetRoutingID();
171 }
172
173 if (options->hidden.get() && !*options->hidden.get()) {
174 if (options->focused.get() && !*options->focused.get())
175 window->Show(AppWindow::SHOW_INACTIVE);
176 else
177 window->Show(AppWindow::SHOW_ACTIVE);
178 }
179
180 base::DictionaryValue* result = new base::DictionaryValue;
181 result->Set("viewId", new base::FundamentalValue(view_id));
182 window->GetSerializedState(result);
183 result->SetBoolean("existingWindow", true);
184 // TODO(benwells): Remove HTML titlebar injection.
185 result->SetBoolean("injectTitlebar", false);
186 SetResult(result);
187 SendResponse(true);
188 return true;
189 }
190 }
191 }
192
193 if (!GetBoundsSpec(*options, &create_params, &error_))
194 return false;
195
196 if (!AppsClient::Get()->IsCurrentChannelOlderThanDev() ||
197 extension()->location() == extensions::Manifest::COMPONENT) {
198 if (options->type == extensions::api::app_window::WINDOW_TYPE_PANEL) {
199 create_params.window_type = AppWindow::WINDOW_TYPE_PANEL;
200 }
201 }
202
203 if (!GetFrameOptions(*options, &create_params))
204 return false;
205
206 if (options->alpha_enabled.get()) {
207 const char* whitelist[] = {
208 "0F42756099D914A026DADFA182871C015735DD95", // http://crbug.com/323773
209 "2D22CDB6583FD0A13758AEBE8B15E45208B4E9A7",
210 "E7E2461CE072DF036CF9592740196159E2D7C089", // http://crbug.com/356200
211 "A74A4D44C7CFCD8844830E6140C8D763E12DD8F3",
212 "312745D9BF916161191143F6490085EEA0434997",
213 "53041A2FA309EECED01FFC751E7399186E860B2C"
214 };
215 if (AppsClient::Get()->IsCurrentChannelOlderThanDev() &&
216 !extensions::SimpleFeature::IsIdInList(
217 extension_id(),
218 std::set<std::string>(whitelist,
219 whitelist + arraysize(whitelist)))) {
220 error_ = app_window_constants::kAlphaEnabledWrongChannel;
221 return false;
222 }
223 if (!extension()->permissions_data()->HasAPIPermission(
224 APIPermission::kAlphaEnabled)) {
225 error_ = app_window_constants::kAlphaEnabledMissingPermission;
226 return false;
227 }
228 if (create_params.frame != AppWindow::FRAME_NONE) {
229 error_ = app_window_constants::kAlphaEnabledNeedsFrameNone;
230 return false;
231 }
232 #if defined(USE_AURA)
233 create_params.alpha_enabled = *options->alpha_enabled;
234 #else
235 // Transparency is only supported on Aura.
236 // Fallback to creating an opaque window (by ignoring alphaEnabled).
237 #endif
238 }
239
240 if (options->hidden.get())
241 create_params.hidden = *options->hidden.get();
242
243 if (options->resizable.get())
244 create_params.resizable = *options->resizable.get();
245
246 if (options->always_on_top.get()) {
247 create_params.always_on_top = *options->always_on_top.get();
248
249 if (create_params.always_on_top &&
250 !extension()->permissions_data()->HasAPIPermission(
251 APIPermission::kAlwaysOnTopWindows)) {
252 error_ = app_window_constants::kAlwaysOnTopPermission;
253 return false;
254 }
255 }
256
257 if (options->focused.get())
258 create_params.focused = *options->focused.get();
259
260 if (options->type != extensions::api::app_window::WINDOW_TYPE_PANEL) {
261 switch (options->state) {
262 case extensions::api::app_window::STATE_NONE:
263 case extensions::api::app_window::STATE_NORMAL:
264 break;
265 case extensions::api::app_window::STATE_FULLSCREEN:
266 create_params.state = ui::SHOW_STATE_FULLSCREEN;
267 break;
268 case extensions::api::app_window::STATE_MAXIMIZED:
269 create_params.state = ui::SHOW_STATE_MAXIMIZED;
270 break;
271 case extensions::api::app_window::STATE_MINIMIZED:
272 create_params.state = ui::SHOW_STATE_MINIMIZED;
273 break;
274 }
275 }
276 }
277
278 create_params.creator_process_id =
279 render_view_host_->GetProcess()->GetID();
280
281 AppWindow* app_window =
282 AppsClient::Get()->CreateAppWindow(browser_context(), extension());
283 app_window->Init(url, new AppWindowContentsImpl(app_window), create_params);
284
285 if (ExtensionsBrowserClient::Get()->IsRunningInForcedAppMode())
286 app_window->ForcedFullscreen();
287
288 content::RenderViewHost* created_view =
289 app_window->web_contents()->GetRenderViewHost();
290 int view_id = MSG_ROUTING_NONE;
291 if (create_params.creator_process_id == created_view->GetProcess()->GetID())
292 view_id = created_view->GetRoutingID();
293
294 base::DictionaryValue* result = new base::DictionaryValue;
295 result->Set("viewId", new base::FundamentalValue(view_id));
296 result->Set("injectTitlebar",
297 new base::FundamentalValue(inject_html_titlebar_));
298 result->Set("id", new base::StringValue(app_window->window_key()));
299 app_window->GetSerializedState(result);
300 SetResult(result);
301
302 if (AppWindowRegistry::Get(browser_context())
303 ->HadDevToolsAttached(created_view)) {
304 AppsClient::Get()->OpenDevToolsWindow(
305 app_window->web_contents(),
306 base::Bind(&AppWindowCreateFunction::SendResponse, this, true));
307 return true;
308 }
309
310 SendResponse(true);
311 app_window->WindowEventsReady();
312
313 return true;
314 }
315
316 bool AppWindowCreateFunction::GetBoundsSpec(
317 const extensions::api::app_window::CreateWindowOptions& options,
318 AppWindow::CreateParams* params,
319 std::string* error) {
320 DCHECK(params);
321 DCHECK(error);
322
323 if (options.inner_bounds.get() || options.outer_bounds.get()) {
324 // Parse the inner and outer bounds specifications. If developers use the
325 // new API, the deprecated fields will be ignored - do not attempt to merge
326 // them.
327
328 const extensions::api::app_window::BoundsSpecification* inner_bounds =
329 options.inner_bounds.get();
330 const extensions::api::app_window::BoundsSpecification* outer_bounds =
331 options.outer_bounds.get();
332 if (inner_bounds && outer_bounds) {
333 if (!CheckBoundsConflict(
334 inner_bounds->left, outer_bounds->left, "left", error)) {
335 return false;
336 }
337 if (!CheckBoundsConflict(
338 inner_bounds->top, outer_bounds->top, "top", error)) {
339 return false;
340 }
341 if (!CheckBoundsConflict(
342 inner_bounds->width, outer_bounds->width, "width", error)) {
343 return false;
344 }
345 if (!CheckBoundsConflict(
346 inner_bounds->height, outer_bounds->height, "height", error)) {
347 return false;
348 }
349 if (!CheckBoundsConflict(inner_bounds->min_width,
350 outer_bounds->min_width,
351 "minWidth",
352 error)) {
353 return false;
354 }
355 if (!CheckBoundsConflict(inner_bounds->min_height,
356 outer_bounds->min_height,
357 "minHeight",
358 error)) {
359 return false;
360 }
361 if (!CheckBoundsConflict(inner_bounds->max_width,
362 outer_bounds->max_width,
363 "maxWidth",
364 error)) {
365 return false;
366 }
367 if (!CheckBoundsConflict(inner_bounds->max_height,
368 outer_bounds->max_height,
369 "maxHeight",
370 error)) {
371 return false;
372 }
373 }
374
375 CopyBoundsSpec(inner_bounds, &(params->content_spec));
376 CopyBoundsSpec(outer_bounds, &(params->window_spec));
377 } else {
378 // Parse deprecated fields.
379 // Due to a bug in NativeAppWindow::GetFrameInsets() on Windows and ChromeOS
380 // the bounds set the position of the window and the size of the content.
381 // This will be preserved as apps may be relying on this behavior.
382
383 if (options.default_width.get())
384 params->content_spec.bounds.set_width(*options.default_width.get());
385 if (options.default_height.get())
386 params->content_spec.bounds.set_height(*options.default_height.get());
387 if (options.default_left.get())
388 params->window_spec.bounds.set_x(*options.default_left.get());
389 if (options.default_top.get())
390 params->window_spec.bounds.set_y(*options.default_top.get());
391
392 if (options.width.get())
393 params->content_spec.bounds.set_width(*options.width.get());
394 if (options.height.get())
395 params->content_spec.bounds.set_height(*options.height.get());
396 if (options.left.get())
397 params->window_spec.bounds.set_x(*options.left.get());
398 if (options.top.get())
399 params->window_spec.bounds.set_y(*options.top.get());
400
401 if (options.bounds.get()) {
402 app_window::ContentBounds* bounds = options.bounds.get();
403 if (bounds->width.get())
404 params->content_spec.bounds.set_width(*bounds->width.get());
405 if (bounds->height.get())
406 params->content_spec.bounds.set_height(*bounds->height.get());
407 if (bounds->left.get())
408 params->window_spec.bounds.set_x(*bounds->left.get());
409 if (bounds->top.get())
410 params->window_spec.bounds.set_y(*bounds->top.get());
411 }
412
413 gfx::Size& minimum_size = params->content_spec.minimum_size;
414 if (options.min_width.get())
415 minimum_size.set_width(*options.min_width);
416 if (options.min_height.get())
417 minimum_size.set_height(*options.min_height);
418 gfx::Size& maximum_size = params->content_spec.maximum_size;
419 if (options.max_width.get())
420 maximum_size.set_width(*options.max_width);
421 if (options.max_height.get())
422 maximum_size.set_height(*options.max_height);
423 }
424
425 return true;
426 }
427
428 AppWindow::Frame AppWindowCreateFunction::GetFrameFromString(
429 const std::string& frame_string) {
430 if (frame_string == kHtmlFrameOption &&
431 (extension()->permissions_data()->HasAPIPermission(
432 APIPermission::kExperimental) ||
433 CommandLine::ForCurrentProcess()->HasSwitch(
434 switches::kEnableExperimentalExtensionApis))) {
435 inject_html_titlebar_ = true;
436 return AppWindow::FRAME_NONE;
437 }
438
439 if (frame_string == kNoneFrameOption)
440 return AppWindow::FRAME_NONE;
441
442 return AppWindow::FRAME_CHROME;
443 }
444
445 bool AppWindowCreateFunction::GetFrameOptions(
446 const app_window::CreateWindowOptions& options,
447 AppWindow::CreateParams* create_params) {
448 if (!options.frame)
449 return true;
450
451 DCHECK(options.frame->as_string || options.frame->as_frame_options);
452 if (options.frame->as_string) {
453 create_params->frame = GetFrameFromString(*options.frame->as_string);
454 return true;
455 }
456
457 if (options.frame->as_frame_options->type)
458 create_params->frame =
459 GetFrameFromString(*options.frame->as_frame_options->type);
460
461 if (options.frame->as_frame_options->color.get()) {
462 if (create_params->frame != AppWindow::FRAME_CHROME) {
463 error_ = app_window_constants::kColorWithFrameNone;
464 return false;
465 }
466
467 if (!image_util::ParseCSSColorString(
468 *options.frame->as_frame_options->color,
469 &create_params->active_frame_color)) {
470 error_ = app_window_constants::kInvalidColorSpecification;
471 return false;
472 }
473
474 create_params->has_frame_color = true;
475 create_params->inactive_frame_color = create_params->active_frame_color;
476
477 if (options.frame->as_frame_options->inactive_color.get()) {
478 if (!image_util::ParseCSSColorString(
479 *options.frame->as_frame_options->inactive_color,
480 &create_params->inactive_frame_color)) {
481 error_ = app_window_constants::kInvalidColorSpecification;
482 return false;
483 }
484 }
485
486 return true;
487 }
488
489 if (options.frame->as_frame_options->inactive_color.get()) {
490 error_ = app_window_constants::kInactiveColorWithoutColor;
491 return false;
492 }
493
494 return true;
495 }
496
497 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698