|  | // Copyright (c) 2012 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. | 
|  |  | 
|  | #include "extensions/browser/api/app_window/app_window_api.h" | 
|  |  | 
|  | #include <memory> | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/command_line.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/strings/string_number_conversions.h" | 
|  | #include "base/strings/string_util.h" | 
|  | #include "base/time/time.h" | 
|  | #include "base/values.h" | 
|  | #include "build/build_config.h" | 
|  | #include "content/public/browser/notification_registrar.h" | 
|  | #include "content/public/browser/notification_types.h" | 
|  | #include "content/public/browser/render_frame_host.h" | 
|  | #include "content/public/browser/render_process_host.h" | 
|  | #include "content/public/browser/web_contents.h" | 
|  | #include "content/public/common/browser_side_navigation_policy.h" | 
|  | #include "content/public/common/url_constants.h" | 
|  | #include "extensions/browser/app_window/app_window.h" | 
|  | #include "extensions/browser/app_window/app_window_client.h" | 
|  | #include "extensions/browser/app_window/app_window_contents.h" | 
|  | #include "extensions/browser/app_window/app_window_registry.h" | 
|  | #include "extensions/browser/app_window/native_app_window.h" | 
|  | #include "extensions/browser/extensions_browser_client.h" | 
|  | #include "extensions/common/api/app_window.h" | 
|  | #include "extensions/common/features/simple_feature.h" | 
|  | #include "extensions/common/image_util.h" | 
|  | #include "extensions/common/manifest.h" | 
|  | #include "extensions/common/permissions/permissions_data.h" | 
|  | #include "extensions/common/switches.h" | 
|  | #include "third_party/skia/include/core/SkColor.h" | 
|  | #include "ui/base/ui_base_types.h" | 
|  | #include "ui/gfx/geometry/rect.h" | 
|  | #include "url/gurl.h" | 
|  |  | 
|  | namespace app_window = extensions::api::app_window; | 
|  | namespace Create = app_window::Create; | 
|  |  | 
|  | namespace extensions { | 
|  |  | 
|  | namespace app_window_constants { | 
|  | const char kInvalidWindowId[] = | 
|  | "The window id can not be more than 256 characters long."; | 
|  | const char kInvalidColorSpecification[] = | 
|  | "The color specification could not be parsed."; | 
|  | const char kColorWithFrameNone[] = "Windows with no frame cannot have a color."; | 
|  | const char kInactiveColorWithoutColor[] = | 
|  | "frame.inactiveColor must be used with frame.color."; | 
|  | const char kConflictingBoundsOptions[] = | 
|  | "The $1 property cannot be specified for both inner and outer bounds."; | 
|  | const char kAlwaysOnTopPermission[] = | 
|  | "The \"app.window.alwaysOnTop\" permission is required."; | 
|  | const char kInvalidUrlParameter[] = | 
|  | "The URL used for window creation must be local for security reasons."; | 
|  | const char kAlphaEnabledWrongChannel[] = | 
|  | "The alphaEnabled option requires dev channel or newer."; | 
|  | 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 kImeWindowMissingPermission[] = | 
|  | "Extensions require the \"app.window.ime\" permission to create windows."; | 
|  | const char kImeOptionIsNotSupported[] = | 
|  | "The \"ime\" option is not supported for platform app."; | 
|  | #if !defined(OS_CHROMEOS) | 
|  | const char kImeWindowUnsupportedPlatform[] = | 
|  | "The \"ime\" option can only be used on ChromeOS."; | 
|  | #else | 
|  | const char kImeWindowMustBeImeWindowOrPanel[] = | 
|  | "IME extensions must create ime window ( with \"ime: true\" and " | 
|  | "\"frame: 'none'\") or panel window (with \"type: panel\")."; | 
|  | #endif | 
|  | const char kShowInShelfWindowKeyNotSet[] = | 
|  | "The \"showInShelf\" option requires the \"id\" option to be set."; | 
|  | }  // namespace app_window_constants | 
|  |  | 
|  | const char kNoneFrameOption[] = "none"; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // If the same property is specified for the inner and outer bounds, raise an | 
|  | // error. | 
|  | bool CheckBoundsConflict(const std::unique_ptr<int>& inner_property, | 
|  | const std::unique_ptr<int>& outer_property, | 
|  | const std::string& property_name, | 
|  | std::string* error) { | 
|  | if (inner_property.get() && outer_property.get()) { | 
|  | std::vector<std::string> subst; | 
|  | subst.push_back(property_name); | 
|  | *error = base::ReplaceStringPlaceholders( | 
|  | app_window_constants::kConflictingBoundsOptions, subst, NULL); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Copy over the bounds specification properties from the API to the | 
|  | // AppWindow::CreateParams. | 
|  | void CopyBoundsSpec(const app_window::BoundsSpecification* input_spec, | 
|  | AppWindow::BoundsSpecification* create_spec) { | 
|  | if (!input_spec) | 
|  | return; | 
|  |  | 
|  | if (input_spec->left.get()) | 
|  | create_spec->bounds.set_x(*input_spec->left); | 
|  | if (input_spec->top.get()) | 
|  | create_spec->bounds.set_y(*input_spec->top); | 
|  | if (input_spec->width.get()) | 
|  | create_spec->bounds.set_width(*input_spec->width); | 
|  | if (input_spec->height.get()) | 
|  | create_spec->bounds.set_height(*input_spec->height); | 
|  | if (input_spec->min_width.get()) | 
|  | create_spec->minimum_size.set_width(*input_spec->min_width); | 
|  | if (input_spec->min_height.get()) | 
|  | create_spec->minimum_size.set_height(*input_spec->min_height); | 
|  | if (input_spec->max_width.get()) | 
|  | create_spec->maximum_size.set_width(*input_spec->max_width); | 
|  | if (input_spec->max_height.get()) | 
|  | create_spec->maximum_size.set_height(*input_spec->max_height); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | AppWindowCreateFunction::AppWindowCreateFunction() {} | 
|  |  | 
|  | bool AppWindowCreateFunction::RunAsync() { | 
|  | // Don't create app window if the system is shutting down. | 
|  | if (ExtensionsBrowserClient::Get()->IsShuttingDown()) | 
|  | return false; | 
|  |  | 
|  | std::unique_ptr<Create::Params> params(Create::Params::Create(*args_)); | 
|  | EXTENSION_FUNCTION_VALIDATE(params.get()); | 
|  |  | 
|  | GURL url = extension()->GetResourceURL(params->url); | 
|  | // Allow absolute URLs for component apps, otherwise prepend the extension | 
|  | // path. | 
|  | // TODO(devlin): Investigate if this is still used. If not, kill it dead! | 
|  | GURL absolute = GURL(params->url); | 
|  | if (absolute.has_scheme()) { | 
|  | if (extension()->location() == Manifest::COMPONENT) { | 
|  | url = absolute; | 
|  | } else { | 
|  | // Show error when url passed isn't local. | 
|  | error_ = app_window_constants::kInvalidUrlParameter; | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | // TODO(jeremya): figure out a way to pass the opening WebContents through to | 
|  | // AppWindow::Create so we can set the opener at create time rather than | 
|  | // with a hack in AppWindowCustomBindings::GetView(). | 
|  | AppWindow::CreateParams create_params; | 
|  | app_window::CreateWindowOptions* options = params->options.get(); | 
|  | if (options) { | 
|  | if (options->id.get()) { | 
|  | // TODO(mek): use URL if no id specified? | 
|  | // Limit length of id to 256 characters. | 
|  | if (options->id->length() > 256) { | 
|  | error_ = app_window_constants::kInvalidWindowId; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | create_params.window_key = *options->id; | 
|  |  | 
|  | if (options->singleton && *options->singleton == false) { | 
|  | WriteToConsole( | 
|  | content::CONSOLE_MESSAGE_LEVEL_WARNING, | 
|  | "The 'singleton' option in chrome.apps.window.create() is deprecated!" | 
|  | " Change your code to no longer rely on this."); | 
|  | } | 
|  |  | 
|  | if (!options->singleton || *options->singleton) { | 
|  | AppWindow* existing_window = | 
|  | AppWindowRegistry::Get(browser_context()) | 
|  | ->GetAppWindowForAppAndKey(extension_id(), | 
|  | create_params.window_key); | 
|  | if (existing_window) { | 
|  | content::RenderFrameHost* existing_frame = | 
|  | existing_window->web_contents()->GetMainFrame(); | 
|  | int frame_id = MSG_ROUTING_NONE; | 
|  | if (render_frame_host()->GetProcess()->GetID() == | 
|  | existing_frame->GetProcess()->GetID()) { | 
|  | frame_id = existing_frame->GetRoutingID(); | 
|  | } | 
|  |  | 
|  | if (!options->hidden.get() || !*options->hidden) { | 
|  | if (options->focused.get() && !*options->focused) | 
|  | existing_window->Show(AppWindow::SHOW_INACTIVE); | 
|  | else | 
|  | existing_window->Show(AppWindow::SHOW_ACTIVE); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<base::DictionaryValue> result( | 
|  | new base::DictionaryValue); | 
|  | result->Set("frameId", new base::FundamentalValue(frame_id)); | 
|  | existing_window->GetSerializedState(result.get()); | 
|  | result->SetBoolean("existingWindow", true); | 
|  | SetResult(std::move(result)); | 
|  | SendResponse(true); | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!GetBoundsSpec(*options, &create_params, &error_)) | 
|  | return false; | 
|  |  | 
|  | if (options->type == app_window::WINDOW_TYPE_PANEL) { | 
|  | #if defined(OS_CHROMEOS) | 
|  | // Panels for v2 apps are only supported on Chrome OS. | 
|  | create_params.window_type = AppWindow::WINDOW_TYPE_PANEL; | 
|  | #else | 
|  | WriteToConsole(content::CONSOLE_MESSAGE_LEVEL_WARNING, | 
|  | "Panels are not supported on this platform"); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | if (!GetFrameOptions(*options, &create_params)) | 
|  | return false; | 
|  |  | 
|  | if (extension()->GetType() == Manifest::TYPE_EXTENSION) { | 
|  | // Whitelisted IME extensions are allowed to use this API to create IME | 
|  | // specific windows to show accented characters or suggestions. | 
|  | if (!extension()->permissions_data()->HasAPIPermission( | 
|  | APIPermission::kImeWindowEnabled)) { | 
|  | error_ = app_window_constants::kImeWindowMissingPermission; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | #if !defined(OS_CHROMEOS) | 
|  | // IME window is only supported on ChromeOS. | 
|  | error_ = app_window_constants::kImeWindowUnsupportedPlatform; | 
|  | return false; | 
|  | #else | 
|  | // IME extensions must create ime window (with "ime: true" and | 
|  | // "frame: none") or panel window (with "type: panel"). | 
|  | if (options->ime.get() && *options->ime.get() && | 
|  | create_params.frame == AppWindow::FRAME_NONE) { | 
|  | create_params.is_ime_window = true; | 
|  | } else if (options->type == app_window::WINDOW_TYPE_PANEL) { | 
|  | create_params.window_type = AppWindow::WINDOW_TYPE_PANEL; | 
|  | } else { | 
|  | error_ = app_window_constants::kImeWindowMustBeImeWindowOrPanel; | 
|  | return false; | 
|  | } | 
|  | #endif  // OS_CHROMEOS | 
|  | } else { | 
|  | if (options->ime.get()) { | 
|  | error_ = app_window_constants::kImeOptionIsNotSupported; | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (options->alpha_enabled.get()) { | 
|  | const char* const kWhitelist[] = { | 
|  | #if defined(OS_CHROMEOS) | 
|  | "B58B99751225318C7EB8CF4688B5434661083E07",  // http://crbug.com/410550 | 
|  | "06BE211D5F014BAB34BC22D9DDA09C63A81D828E",  // http://crbug.com/425539 | 
|  | "F94EE6AB36D6C6588670B2B01EB65212D9C64E33", | 
|  | "B9EF10DDFEA11EF77873CC5009809E5037FC4C7A",  // http://crbug.com/435380 | 
|  | #endif | 
|  | "0F42756099D914A026DADFA182871C015735DD95",  // http://crbug.com/323773 | 
|  | "2D22CDB6583FD0A13758AEBE8B15E45208B4E9A7", | 
|  | "E7E2461CE072DF036CF9592740196159E2D7C089",  // http://crbug.com/356200 | 
|  | "A74A4D44C7CFCD8844830E6140C8D763E12DD8F3", | 
|  | "312745D9BF916161191143F6490085EEA0434997", | 
|  | "53041A2FA309EECED01FFC751E7399186E860B2C", | 
|  | "A07A5B743CD82A1C2579DB77D353C98A23201EEF",  // http://crbug.com/413748 | 
|  | "F16F23C83C5F6DAD9B65A120448B34056DD80691", | 
|  | "0F585FB1D0FDFBEBCE1FEB5E9DFFB6DA476B8C9B" | 
|  | }; | 
|  | if (AppWindowClient::Get()->IsCurrentChannelOlderThanDev() && | 
|  | !SimpleFeature::IsIdInArray( | 
|  | extension_id(), kWhitelist, arraysize(kWhitelist))) { | 
|  | error_ = app_window_constants::kAlphaEnabledWrongChannel; | 
|  | return false; | 
|  | } | 
|  | if (!extension()->permissions_data()->HasAPIPermission( | 
|  | APIPermission::kAlphaEnabled)) { | 
|  | error_ = app_window_constants::kAlphaEnabledMissingPermission; | 
|  | return false; | 
|  | } | 
|  | if (create_params.frame != AppWindow::FRAME_NONE) { | 
|  | error_ = app_window_constants::kAlphaEnabledNeedsFrameNone; | 
|  | return false; | 
|  | } | 
|  | #if defined(USE_AURA) | 
|  | create_params.alpha_enabled = *options->alpha_enabled; | 
|  | #else | 
|  | // Transparency is only supported on Aura. | 
|  | // Fallback to creating an opaque window (by ignoring alphaEnabled). | 
|  | #endif | 
|  | } | 
|  |  | 
|  | if (options->hidden.get()) | 
|  | create_params.hidden = *options->hidden; | 
|  |  | 
|  | if (options->resizable.get()) | 
|  | create_params.resizable = *options->resizable; | 
|  |  | 
|  | if (options->always_on_top.get()) { | 
|  | create_params.always_on_top = *options->always_on_top; | 
|  |  | 
|  | if (create_params.always_on_top && | 
|  | !extension()->permissions_data()->HasAPIPermission( | 
|  | APIPermission::kAlwaysOnTopWindows)) { | 
|  | error_ = app_window_constants::kAlwaysOnTopPermission; | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (options->focused.get()) | 
|  | create_params.focused = *options->focused; | 
|  |  | 
|  | if (options->visible_on_all_workspaces.get()) { | 
|  | create_params.visible_on_all_workspaces = | 
|  | *options->visible_on_all_workspaces; | 
|  | } | 
|  |  | 
|  | if (options->show_in_shelf.get()) { | 
|  | create_params.show_in_shelf = *options->show_in_shelf.get(); | 
|  |  | 
|  | if (create_params.show_in_shelf && create_params.window_key.empty()) { | 
|  | error_ = app_window_constants::kShowInShelfWindowKeyNotSet; | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (options->icon.get()) { | 
|  | // First, check if the window icon URL is a valid global URL. | 
|  | create_params.window_icon_url = GURL(*options->icon.get()); | 
|  |  | 
|  | // If the URL is not global, check for a valid extension local URL. | 
|  | if (!create_params.window_icon_url.is_valid()) { | 
|  | create_params.window_icon_url = | 
|  | extension()->GetResourceURL(*options->icon.get()); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (options->type != app_window::WINDOW_TYPE_PANEL) { | 
|  | switch (options->state) { | 
|  | case app_window::STATE_NONE: | 
|  | case app_window::STATE_NORMAL: | 
|  | break; | 
|  | case app_window::STATE_FULLSCREEN: | 
|  | create_params.state = ui::SHOW_STATE_FULLSCREEN; | 
|  | break; | 
|  | case app_window::STATE_MAXIMIZED: | 
|  | create_params.state = ui::SHOW_STATE_MAXIMIZED; | 
|  | break; | 
|  | case app_window::STATE_MINIMIZED: | 
|  | create_params.state = ui::SHOW_STATE_MINIMIZED; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | create_params.creator_process_id = | 
|  | render_frame_host()->GetProcess()->GetID(); | 
|  |  | 
|  | AppWindow* app_window = | 
|  | AppWindowClient::Get()->CreateAppWindow(browser_context(), extension()); | 
|  | app_window->Init(url, new AppWindowContentsImpl(app_window), | 
|  | render_frame_host(), create_params); | 
|  |  | 
|  | if (ExtensionsBrowserClient::Get()->IsRunningInForcedAppMode() && | 
|  | !app_window->is_ime_window()) { | 
|  | app_window->ForcedFullscreen(); | 
|  | } | 
|  |  | 
|  | content::RenderFrameHost* created_frame = | 
|  | app_window->web_contents()->GetMainFrame(); | 
|  | int frame_id = MSG_ROUTING_NONE; | 
|  | if (create_params.creator_process_id == created_frame->GetProcess()->GetID()) | 
|  | frame_id = created_frame->GetRoutingID(); | 
|  |  | 
|  | std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue); | 
|  | result->Set("frameId", new base::FundamentalValue(frame_id)); | 
|  | result->Set("id", new base::StringValue(app_window->window_key())); | 
|  | app_window->GetSerializedState(result.get()); | 
|  | SetResult(std::move(result)); | 
|  |  | 
|  | if (AppWindowRegistry::Get(browser_context()) | 
|  | ->HadDevToolsAttached(app_window->web_contents())) { | 
|  | AppWindowClient::Get()->OpenDevToolsWindow( | 
|  | app_window->web_contents(), | 
|  | base::Bind(&AppWindowCreateFunction::SendResponse, this, true)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // PlzNavigate: delay sending the response until the newly created window has | 
|  | // been told to navigate, and blink has been correctly initialized in the | 
|  | // renderer. | 
|  | if (content::IsBrowserSideNavigationEnabled()) { | 
|  | app_window->SetOnFirstCommitCallback( | 
|  | base::Bind(&AppWindowCreateFunction::SendResponse, this, true)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | SendResponse(true); | 
|  | app_window->WindowEventsReady(); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool AppWindowCreateFunction::GetBoundsSpec( | 
|  | const app_window::CreateWindowOptions& options, | 
|  | AppWindow::CreateParams* params, | 
|  | std::string* error) { | 
|  | DCHECK(params); | 
|  | DCHECK(error); | 
|  |  | 
|  | if (options.inner_bounds.get() || options.outer_bounds.get()) { | 
|  | // Parse the inner and outer bounds specifications. If developers use the | 
|  | // new API, the deprecated fields will be ignored - do not attempt to merge | 
|  | // them. | 
|  |  | 
|  | const app_window::BoundsSpecification* inner_bounds = | 
|  | options.inner_bounds.get(); | 
|  | const app_window::BoundsSpecification* outer_bounds = | 
|  | options.outer_bounds.get(); | 
|  | if (inner_bounds && outer_bounds) { | 
|  | if (!CheckBoundsConflict( | 
|  | inner_bounds->left, outer_bounds->left, "left", error)) { | 
|  | return false; | 
|  | } | 
|  | if (!CheckBoundsConflict( | 
|  | inner_bounds->top, outer_bounds->top, "top", error)) { | 
|  | return false; | 
|  | } | 
|  | if (!CheckBoundsConflict( | 
|  | inner_bounds->width, outer_bounds->width, "width", error)) { | 
|  | return false; | 
|  | } | 
|  | if (!CheckBoundsConflict( | 
|  | inner_bounds->height, outer_bounds->height, "height", error)) { | 
|  | return false; | 
|  | } | 
|  | if (!CheckBoundsConflict(inner_bounds->min_width, | 
|  | outer_bounds->min_width, | 
|  | "minWidth", | 
|  | error)) { | 
|  | return false; | 
|  | } | 
|  | if (!CheckBoundsConflict(inner_bounds->min_height, | 
|  | outer_bounds->min_height, | 
|  | "minHeight", | 
|  | error)) { | 
|  | return false; | 
|  | } | 
|  | if (!CheckBoundsConflict(inner_bounds->max_width, | 
|  | outer_bounds->max_width, | 
|  | "maxWidth", | 
|  | error)) { | 
|  | return false; | 
|  | } | 
|  | if (!CheckBoundsConflict(inner_bounds->max_height, | 
|  | outer_bounds->max_height, | 
|  | "maxHeight", | 
|  | error)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | CopyBoundsSpec(inner_bounds, &(params->content_spec)); | 
|  | CopyBoundsSpec(outer_bounds, &(params->window_spec)); | 
|  | } else { | 
|  | // Parse deprecated fields. | 
|  | // Due to a bug in NativeAppWindow::GetFrameInsets() on Windows and ChromeOS | 
|  | // the bounds set the position of the window and the size of the content. | 
|  | // This will be preserved as apps may be relying on this behavior. | 
|  |  | 
|  | if (options.default_width.get()) | 
|  | params->content_spec.bounds.set_width(*options.default_width); | 
|  | if (options.default_height.get()) | 
|  | params->content_spec.bounds.set_height(*options.default_height); | 
|  | if (options.default_left.get()) | 
|  | params->window_spec.bounds.set_x(*options.default_left); | 
|  | if (options.default_top.get()) | 
|  | params->window_spec.bounds.set_y(*options.default_top); | 
|  |  | 
|  | if (options.width.get()) | 
|  | params->content_spec.bounds.set_width(*options.width); | 
|  | if (options.height.get()) | 
|  | params->content_spec.bounds.set_height(*options.height); | 
|  | if (options.left.get()) | 
|  | params->window_spec.bounds.set_x(*options.left); | 
|  | if (options.top.get()) | 
|  | params->window_spec.bounds.set_y(*options.top); | 
|  |  | 
|  | if (options.bounds.get()) { | 
|  | app_window::ContentBounds* bounds = options.bounds.get(); | 
|  | if (bounds->width.get()) | 
|  | params->content_spec.bounds.set_width(*bounds->width); | 
|  | if (bounds->height.get()) | 
|  | params->content_spec.bounds.set_height(*bounds->height); | 
|  | if (bounds->left.get()) | 
|  | params->window_spec.bounds.set_x(*bounds->left); | 
|  | if (bounds->top.get()) | 
|  | params->window_spec.bounds.set_y(*bounds->top); | 
|  | } | 
|  |  | 
|  | gfx::Size& minimum_size = params->content_spec.minimum_size; | 
|  | if (options.min_width.get()) | 
|  | minimum_size.set_width(*options.min_width); | 
|  | if (options.min_height.get()) | 
|  | minimum_size.set_height(*options.min_height); | 
|  | gfx::Size& maximum_size = params->content_spec.maximum_size; | 
|  | if (options.max_width.get()) | 
|  | maximum_size.set_width(*options.max_width); | 
|  | if (options.max_height.get()) | 
|  | maximum_size.set_height(*options.max_height); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | AppWindow::Frame AppWindowCreateFunction::GetFrameFromString( | 
|  | const std::string& frame_string) { | 
|  | if (frame_string == kNoneFrameOption) | 
|  | return AppWindow::FRAME_NONE; | 
|  |  | 
|  | return AppWindow::FRAME_CHROME; | 
|  | } | 
|  |  | 
|  | bool AppWindowCreateFunction::GetFrameOptions( | 
|  | const app_window::CreateWindowOptions& options, | 
|  | AppWindow::CreateParams* create_params) { | 
|  | if (!options.frame) | 
|  | return true; | 
|  |  | 
|  | DCHECK(options.frame->as_string || options.frame->as_frame_options); | 
|  | if (options.frame->as_string) { | 
|  | create_params->frame = GetFrameFromString(*options.frame->as_string); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (options.frame->as_frame_options->type) | 
|  | create_params->frame = | 
|  | GetFrameFromString(*options.frame->as_frame_options->type); | 
|  |  | 
|  | if (options.frame->as_frame_options->color.get()) { | 
|  | if (create_params->frame != AppWindow::FRAME_CHROME) { | 
|  | error_ = app_window_constants::kColorWithFrameNone; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (!image_util::ParseHexColorString( | 
|  | *options.frame->as_frame_options->color, | 
|  | &create_params->active_frame_color)) { | 
|  | error_ = app_window_constants::kInvalidColorSpecification; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | create_params->has_frame_color = true; | 
|  | create_params->inactive_frame_color = create_params->active_frame_color; | 
|  |  | 
|  | if (options.frame->as_frame_options->inactive_color.get()) { | 
|  | if (!image_util::ParseHexColorString( | 
|  | *options.frame->as_frame_options->inactive_color, | 
|  | &create_params->inactive_frame_color)) { | 
|  | error_ = app_window_constants::kInvalidColorSpecification; | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (options.frame->as_frame_options->inactive_color.get()) { | 
|  | error_ = app_window_constants::kInactiveColorWithoutColor; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // namespace extensions |