This source file includes following definitions.
- LoadCompleted
- CheckBoundsConflict
- CopyBoundsSpec
- SendDelayedResponse
- RunImpl
- GetBoundsSpec
- GetFrameFromString
- GetFrameOptions
- UpdateFrameOptionsForChannel
#include "chrome/browser/extensions/api/app_window/app_window_api.h"
#include "apps/app_window.h"
#include "apps/app_window_contents.h"
#include "apps/app_window_registry.h"
#include "apps/apps_client.h"
#include "apps/ui/native_app_window.h"
#include "base/command_line.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/devtools/devtools_window.h"
#include "chrome/common/extensions/api/app_window.h"
#include "chrome/common/extensions/features/feature_channel.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/url_constants.h"
#include "extensions/browser/extensions_browser_client.h"
#include "extensions/browser/image_util.h"
#include "extensions/common/switches.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/ui_base_types.h"
#include "ui/gfx/rect.h"
#include "url/gurl.h"
using apps::AppWindow;
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 kInvalidChannelForFrameOptions[] =
"frameOptions is only available in dev channel.";
const char kColorWithFrameNone[] = "Windows with no frame cannot have a color.";
const char kInvalidChannelForBounds[] =
"innerBounds and outerBounds are only available in dev channel.";
const char kConflictingBoundsOptions[] =
"The $1 property cannot be specified for both inner and outer bounds.";
}
const char kNoneFrameOption[] = "none";
const char kHtmlFrameOption[] = "experimental-html";
namespace {
class DevToolsRestorer : public base::RefCounted<DevToolsRestorer> {
public:
DevToolsRestorer(AppWindowCreateFunction* delayed_create_function,
content::RenderViewHost* created_view)
: delayed_create_function_(delayed_create_function) {
AddRef();
DevToolsWindow* devtools_window =
DevToolsWindow::OpenDevToolsWindow(
created_view,
DevToolsToggleAction::ShowConsole());
devtools_window->SetLoadCompletedCallback(
base::Bind(&DevToolsRestorer::LoadCompleted, this));
}
private:
friend class base::RefCounted<DevToolsRestorer>;
~DevToolsRestorer() {}
void LoadCompleted() {
delayed_create_function_->SendDelayedResponse();
Release();
}
scoped_refptr<AppWindowCreateFunction> delayed_create_function_;
};
bool CheckBoundsConflict(const scoped_ptr<int>& inner_property,
const scoped_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 = ReplaceStringPlaceholders(
app_window_constants::kConflictingBoundsOptions, subst, NULL);
return false;
}
return true;
}
void CopyBoundsSpec(
const extensions::api::app_window::BoundsSpecification* input_spec,
apps::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);
}
}
AppWindowCreateFunction::AppWindowCreateFunction()
: inject_html_titlebar_(false) {}
void AppWindowCreateFunction::SendDelayedResponse() {
SendResponse(true);
}
bool AppWindowCreateFunction::RunImpl() {
if (extensions::ExtensionsBrowserClient::Get()->IsShuttingDown())
return false;
scoped_ptr<Create::Params> params(Create::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());
GURL url = GetExtension()->GetResourceURL(params->url);
if (GetExtension()->location() == extensions::Manifest::COMPONENT) {
GURL absolute = GURL(params->url);
if (absolute.has_scheme())
url = absolute;
}
AppWindow::CreateParams create_params;
app_window::CreateWindowOptions* options = params->options.get();
if (options) {
if (options->id.get()) {
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* window = apps::AppWindowRegistry::Get(browser_context())
->GetAppWindowForAppAndKey(
extension_id(), create_params.window_key);
if (window) {
content::RenderViewHost* created_view =
window->web_contents()->GetRenderViewHost();
int view_id = MSG_ROUTING_NONE;
if (render_view_host_->GetProcess()->GetID() ==
created_view->GetProcess()->GetID()) {
view_id = created_view->GetRoutingID();
}
if (options->focused.get() && !*options->focused.get())
window->Show(AppWindow::SHOW_INACTIVE);
else
window->Show(AppWindow::SHOW_ACTIVE);
base::DictionaryValue* result = new base::DictionaryValue;
result->Set("viewId", new base::FundamentalValue(view_id));
window->GetSerializedState(result);
result->SetBoolean("existingWindow", true);
result->SetBoolean("injectTitlebar", false);
SetResult(result);
SendResponse(true);
return true;
}
}
}
if (!GetBoundsSpec(*options, &create_params, &error_))
return false;
if (GetCurrentChannel() <= chrome::VersionInfo::CHANNEL_DEV ||
GetExtension()->location() == extensions::Manifest::COMPONENT) {
if (options->type == extensions::api::app_window::WINDOW_TYPE_PANEL) {
create_params.window_type = AppWindow::WINDOW_TYPE_PANEL;
}
}
if (!GetFrameOptions(*options, &create_params))
return false;
if (options->transparent_background.get() &&
(GetExtension()->HasAPIPermission(APIPermission::kExperimental) ||
CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableExperimentalExtensionApis))) {
create_params.transparent_background = *options->transparent_background;
}
if (options->hidden.get())
create_params.hidden = *options->hidden.get();
if (options->resizable.get())
create_params.resizable = *options->resizable.get();
if (options->always_on_top.get() &&
GetExtension()->HasAPIPermission(APIPermission::kAlwaysOnTopWindows))
create_params.always_on_top = *options->always_on_top.get();
if (options->focused.get())
create_params.focused = *options->focused.get();
if (options->type != extensions::api::app_window::WINDOW_TYPE_PANEL) {
switch (options->state) {
case extensions::api::app_window::STATE_NONE:
case extensions::api::app_window::STATE_NORMAL:
break;
case extensions::api::app_window::STATE_FULLSCREEN:
create_params.state = ui::SHOW_STATE_FULLSCREEN;
break;
case extensions::api::app_window::STATE_MAXIMIZED:
create_params.state = ui::SHOW_STATE_MAXIMIZED;
break;
case extensions::api::app_window::STATE_MINIMIZED:
create_params.state = ui::SHOW_STATE_MINIMIZED;
break;
}
}
}
UpdateFrameOptionsForChannel(&create_params);
create_params.creator_process_id =
render_view_host_->GetProcess()->GetID();
AppWindow* app_window = apps::AppsClient::Get()->CreateAppWindow(
browser_context(), GetExtension());
app_window->Init(
url, new apps::AppWindowContentsImpl(app_window), create_params);
if (ExtensionsBrowserClient::Get()->IsRunningInForcedAppMode())
app_window->ForcedFullscreen();
content::RenderViewHost* created_view =
app_window->web_contents()->GetRenderViewHost();
int view_id = MSG_ROUTING_NONE;
if (create_params.creator_process_id == created_view->GetProcess()->GetID())
view_id = created_view->GetRoutingID();
base::DictionaryValue* result = new base::DictionaryValue;
result->Set("viewId", new base::FundamentalValue(view_id));
result->Set("injectTitlebar",
new base::FundamentalValue(inject_html_titlebar_));
result->Set("id", new base::StringValue(app_window->window_key()));
app_window->GetSerializedState(result);
SetResult(result);
if (apps::AppWindowRegistry::Get(browser_context())
->HadDevToolsAttached(created_view)) {
new DevToolsRestorer(this, created_view);
return true;
}
SendResponse(true);
return true;
}
bool AppWindowCreateFunction::GetBoundsSpec(
const extensions::api::app_window::CreateWindowOptions& options,
apps::AppWindow::CreateParams* params,
std::string* error) {
DCHECK(params);
DCHECK(error);
if (options.inner_bounds.get() || options.outer_bounds.get()) {
if (GetCurrentChannel() > chrome::VersionInfo::CHANNEL_DEV) {
*error = app_window_constants::kInvalidChannelForBounds;
return false;
}
const extensions::api::app_window::BoundsSpecification* inner_bounds =
options.inner_bounds.get();
const extensions::api::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 {
if (options.default_width.get())
params->content_spec.bounds.set_width(*options.default_width.get());
if (options.default_height.get())
params->content_spec.bounds.set_height(*options.default_height.get());
if (options.default_left.get())
params->window_spec.bounds.set_x(*options.default_left.get());
if (options.default_top.get())
params->window_spec.bounds.set_y(*options.default_top.get());
if (options.width.get())
params->content_spec.bounds.set_width(*options.width.get());
if (options.height.get())
params->content_spec.bounds.set_height(*options.height.get());
if (options.left.get())
params->window_spec.bounds.set_x(*options.left.get());
if (options.top.get())
params->window_spec.bounds.set_y(*options.top.get());
if (options.bounds.get()) {
app_window::ContentBounds* bounds = options.bounds.get();
if (bounds->width.get())
params->content_spec.bounds.set_width(*bounds->width.get());
if (bounds->height.get())
params->content_spec.bounds.set_height(*bounds->height.get());
if (bounds->left.get())
params->window_spec.bounds.set_x(*bounds->left.get());
if (bounds->top.get())
params->window_spec.bounds.set_y(*bounds->top.get());
}
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 == kHtmlFrameOption &&
(GetExtension()->HasAPIPermission(APIPermission::kExperimental) ||
CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableExperimentalExtensionApis))) {
inject_html_titlebar_ = true;
return AppWindow::FRAME_NONE;
}
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 (GetCurrentChannel() > chrome::VersionInfo::CHANNEL_DEV) {
error_ = app_window_constants::kInvalidChannelForFrameOptions;
return false;
}
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::ParseCSSColorString(*options.frame->as_frame_options->color,
&create_params->frame_color)) {
create_params->has_frame_color = true;
return true;
}
error_ = app_window_constants::kInvalidColorSpecification;
return false;
}
return true;
}
void AppWindowCreateFunction::UpdateFrameOptionsForChannel(
apps::AppWindow::CreateParams* create_params) {
#if defined(OS_WIN)
if (create_params->frame == AppWindow::FRAME_CHROME &&
GetCurrentChannel() > chrome::VersionInfo::CHANNEL_DEV) {
create_params->has_frame_color = true;
create_params->frame_color = SK_ColorWHITE;
}
#endif
}
}