Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / app_window / app_window_api.cc
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 "apps/app_window_contents.h"
8 #include "apps/shell_window.h"
9 #include "apps/shell_window_registry.h"
10 #include "apps/ui/native_app_window.h"
11 #include "base/command_line.h"
12 #include "base/time/time.h"
13 #include "base/values.h"
14 #include "chrome/browser/app_mode/app_mode_utils.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/devtools/devtools_window.h"
17 #include "chrome/browser/extensions/window_controller.h"
18 #include "chrome/browser/ui/apps/chrome_shell_window_delegate.h"
19 #include "chrome/common/extensions/api/app_window.h"
20 #include "chrome/common/extensions/features/feature_channel.h"
21 #include "content/public/browser/notification_registrar.h"
22 #include "content/public/browser/notification_types.h"
23 #include "content/public/browser/render_process_host.h"
24 #include "content/public/browser/render_view_host.h"
25 #include "content/public/browser/web_contents.h"
26 #include "content/public/common/url_constants.h"
27 #include "extensions/common/switches.h"
28 #include "ui/base/ui_base_types.h"
29 #include "ui/gfx/rect.h"
30 #include "url/gurl.h"
31
32 #if defined(USE_ASH)
33 #include "ash/shell.h"
34 #include "ui/aura/root_window.h"
35 #include "ui/aura/window.h"
36 #endif
37
38 using apps::ShellWindow;
39
40 namespace app_window = extensions::api::app_window;
41 namespace Create = app_window::Create;
42
43 namespace extensions {
44
45 namespace app_window_constants {
46 const char kInvalidWindowId[] =
47     "The window id can not be more than 256 characters long.";
48 }
49
50 const char kNoneFrameOption[] = "none";
51 const char kHtmlFrameOption[] = "experimental-html";
52
53 namespace {
54
55 const int kUnboundedSize = apps::ShellWindow::SizeConstraints::kUnboundedSize;
56
57 // Opens an inspector window and delays the response to the
58 // AppWindowCreateFunction until the DevToolsWindow has finished loading, and is
59 // ready to stop on breakpoints in the callback.
60 class DevToolsRestorer : public content::NotificationObserver {
61  public:
62   DevToolsRestorer(AppWindowCreateFunction* delayed_create_function,
63                    content::RenderViewHost* created_view)
64       : delayed_create_function_(delayed_create_function) {
65     DevToolsWindow* devtools_window =
66         DevToolsWindow::OpenDevToolsWindow(
67             created_view,
68             DevToolsToggleAction::ShowConsole());
69
70     registrar_.Add(
71         this,
72         content::NOTIFICATION_LOAD_STOP,
73         content::Source<content::NavigationController>(
74             &devtools_window->web_contents()->GetController()));
75   }
76
77  protected:
78   // content::NotificationObserver:
79   virtual void Observe(int type,
80                        const content::NotificationSource& source,
81                        const content::NotificationDetails& details) OVERRIDE {
82     DCHECK(type == content::NOTIFICATION_LOAD_STOP);
83     delayed_create_function_->SendDelayedResponse();
84     delete this;
85   }
86
87  private:
88   scoped_refptr<AppWindowCreateFunction> delayed_create_function_;
89   content::NotificationRegistrar registrar_;
90 };
91
92 void SetCreateResultFromShellWindow(ShellWindow* window,
93                                     base::DictionaryValue* result) {
94   result->SetBoolean("fullscreen", window->GetBaseWindow()->IsFullscreen());
95   result->SetBoolean("minimized", window->GetBaseWindow()->IsMinimized());
96   result->SetBoolean("maximized", window->GetBaseWindow()->IsMaximized());
97   result->SetBoolean("alwaysOnTop", window->IsAlwaysOnTop());
98   base::DictionaryValue* boundsValue = new base::DictionaryValue();
99   gfx::Rect bounds = window->GetClientBounds();
100   boundsValue->SetInteger("left", bounds.x());
101   boundsValue->SetInteger("top", bounds.y());
102   boundsValue->SetInteger("width", bounds.width());
103   boundsValue->SetInteger("height", bounds.height());
104   result->Set("bounds", boundsValue);
105
106   const ShellWindow::SizeConstraints& size_constraints =
107       window->size_constraints();
108   gfx::Size min_size = size_constraints.GetMinimumSize();
109   gfx::Size max_size = size_constraints.GetMaximumSize();
110   if (min_size.width() != kUnboundedSize)
111     result->SetInteger("minWidth", min_size.width());
112   if (min_size.height() != kUnboundedSize)
113     result->SetInteger("minHeight", min_size.height());
114   if (max_size.width() != kUnboundedSize)
115     result->SetInteger("maxWidth", max_size.width());
116   if (max_size.height() != kUnboundedSize)
117     result->SetInteger("maxHeight", max_size.height());
118 }
119
120 }  // namespace
121
122 void AppWindowCreateFunction::SendDelayedResponse() {
123   SendResponse(true);
124 }
125
126 bool AppWindowCreateFunction::RunImpl() {
127   // Don't create app window if the system is shutting down.
128   if (g_browser_process->IsShuttingDown())
129     return false;
130
131   scoped_ptr<Create::Params> params(Create::Params::Create(*args_));
132   EXTENSION_FUNCTION_VALIDATE(params.get());
133
134   GURL url = GetExtension()->GetResourceURL(params->url);
135   // Allow absolute URLs for component apps, otherwise prepend the extension
136   // path.
137   if (GetExtension()->location() == extensions::Manifest::COMPONENT) {
138     GURL absolute = GURL(params->url);
139     if (absolute.has_scheme())
140       url = absolute;
141   }
142
143   bool inject_html_titlebar = false;
144
145   // TODO(jeremya): figure out a way to pass the opening WebContents through to
146   // ShellWindow::Create so we can set the opener at create time rather than
147   // with a hack in AppWindowCustomBindings::GetView().
148   ShellWindow::CreateParams create_params;
149   app_window::CreateWindowOptions* options = params->options.get();
150   if (options) {
151     if (options->id.get()) {
152       // TODO(mek): use URL if no id specified?
153       // Limit length of id to 256 characters.
154       if (options->id->length() > 256) {
155         error_ = app_window_constants::kInvalidWindowId;
156         return false;
157       }
158
159       create_params.window_key = *options->id;
160
161       if (options->singleton && *options->singleton == false) {
162         WriteToConsole(
163           content::CONSOLE_MESSAGE_LEVEL_WARNING,
164           "The 'singleton' option in chrome.apps.window.create() is deprecated!"
165           " Change your code to no longer rely on this.");
166       }
167
168       if (!options->singleton || *options->singleton) {
169         ShellWindow* window = apps::ShellWindowRegistry::Get(
170             GetProfile())->GetShellWindowForAppAndKey(extension_id(),
171                                                       create_params.window_key);
172         if (window) {
173           content::RenderViewHost* created_view =
174               window->web_contents()->GetRenderViewHost();
175           int view_id = MSG_ROUTING_NONE;
176           if (render_view_host_->GetProcess()->GetID() ==
177               created_view->GetProcess()->GetID()) {
178             view_id = created_view->GetRoutingID();
179           }
180
181           if (options->focused.get() && !*options->focused.get())
182             window->Show(ShellWindow::SHOW_INACTIVE);
183           else
184             window->Show(ShellWindow::SHOW_ACTIVE);
185
186           base::DictionaryValue* result = new base::DictionaryValue;
187           result->Set("viewId", new base::FundamentalValue(view_id));
188           SetCreateResultFromShellWindow(window, result);
189           result->SetBoolean("existingWindow", true);
190           result->SetBoolean("injectTitlebar", false);
191           SetResult(result);
192           SendResponse(true);
193           return true;
194         }
195       }
196     }
197
198     // TODO(jeremya): remove these, since they do the same thing as
199     // left/top/width/height.
200     if (options->default_width.get())
201       create_params.bounds.set_width(*options->default_width.get());
202     if (options->default_height.get())
203       create_params.bounds.set_height(*options->default_height.get());
204     if (options->default_left.get())
205       create_params.bounds.set_x(*options->default_left.get());
206     if (options->default_top.get())
207       create_params.bounds.set_y(*options->default_top.get());
208
209     if (options->width.get())
210       create_params.bounds.set_width(*options->width.get());
211     if (options->height.get())
212       create_params.bounds.set_height(*options->height.get());
213     if (options->left.get())
214       create_params.bounds.set_x(*options->left.get());
215     if (options->top.get())
216       create_params.bounds.set_y(*options->top.get());
217
218     if (options->bounds.get()) {
219       app_window::Bounds* bounds = options->bounds.get();
220       if (bounds->width.get())
221         create_params.bounds.set_width(*bounds->width.get());
222       if (bounds->height.get())
223         create_params.bounds.set_height(*bounds->height.get());
224       if (bounds->left.get())
225         create_params.bounds.set_x(*bounds->left.get());
226       if (bounds->top.get())
227         create_params.bounds.set_y(*bounds->top.get());
228     }
229
230     if (GetCurrentChannel() <= chrome::VersionInfo::CHANNEL_DEV ||
231         GetExtension()->location() == extensions::Manifest::COMPONENT) {
232       if (options->type == extensions::api::app_window::WINDOW_TYPE_PANEL) {
233           create_params.window_type = ShellWindow::WINDOW_TYPE_PANEL;
234       }
235     }
236
237     if (options->frame.get()) {
238       if (*options->frame == kHtmlFrameOption &&
239           (GetExtension()->HasAPIPermission(APIPermission::kExperimental) ||
240            CommandLine::ForCurrentProcess()->HasSwitch(
241                switches::kEnableExperimentalExtensionApis))) {
242         create_params.frame = ShellWindow::FRAME_NONE;
243         inject_html_titlebar = true;
244       } else if (*options->frame == kNoneFrameOption) {
245         create_params.frame = ShellWindow::FRAME_NONE;
246       } else {
247         create_params.frame = ShellWindow::FRAME_CHROME;
248       }
249     }
250
251     if (options->transparent_background.get() &&
252         (GetExtension()->HasAPIPermission(APIPermission::kExperimental) ||
253          CommandLine::ForCurrentProcess()->HasSwitch(
254              switches::kEnableExperimentalExtensionApis))) {
255       create_params.transparent_background = *options->transparent_background;
256     }
257
258     gfx::Size& minimum_size = create_params.minimum_size;
259     if (options->min_width.get())
260       minimum_size.set_width(*options->min_width);
261     if (options->min_height.get())
262       minimum_size.set_height(*options->min_height);
263     gfx::Size& maximum_size = create_params.maximum_size;
264     if (options->max_width.get())
265       maximum_size.set_width(*options->max_width);
266     if (options->max_height.get())
267       maximum_size.set_height(*options->max_height);
268
269     if (options->hidden.get())
270       create_params.hidden = *options->hidden.get();
271
272     if (options->resizable.get())
273       create_params.resizable = *options->resizable.get();
274
275     if (options->always_on_top.get() &&
276         GetExtension()->HasAPIPermission(APIPermission::kAlwaysOnTopWindows))
277       create_params.always_on_top = *options->always_on_top.get();
278
279     if (options->focused.get())
280       create_params.focused = *options->focused.get();
281
282     if (options->type != extensions::api::app_window::WINDOW_TYPE_PANEL) {
283       switch (options->state) {
284         case extensions::api::app_window::STATE_NONE:
285         case extensions::api::app_window::STATE_NORMAL:
286           break;
287         case extensions::api::app_window::STATE_FULLSCREEN:
288           create_params.state = ui::SHOW_STATE_FULLSCREEN;
289           break;
290         case extensions::api::app_window::STATE_MAXIMIZED:
291           create_params.state = ui::SHOW_STATE_MAXIMIZED;
292           break;
293         case extensions::api::app_window::STATE_MINIMIZED:
294           create_params.state = ui::SHOW_STATE_MINIMIZED;
295           break;
296       }
297     }
298   }
299
300   create_params.creator_process_id =
301       render_view_host_->GetProcess()->GetID();
302
303   ShellWindow* shell_window = new ShellWindow(
304       GetProfile(), new ChromeShellWindowDelegate(), GetExtension());
305   shell_window->Init(url,
306                      new apps::AppWindowContents(shell_window),
307                      create_params);
308
309   if (chrome::IsRunningInForcedAppMode())
310     shell_window->ForcedFullscreen();
311
312   content::RenderViewHost* created_view =
313       shell_window->web_contents()->GetRenderViewHost();
314   int view_id = MSG_ROUTING_NONE;
315   if (create_params.creator_process_id == created_view->GetProcess()->GetID())
316     view_id = created_view->GetRoutingID();
317
318   base::DictionaryValue* result = new base::DictionaryValue;
319   result->Set("viewId", new base::FundamentalValue(view_id));
320   result->Set("injectTitlebar",
321       new base::FundamentalValue(inject_html_titlebar));
322   result->Set("id", new base::StringValue(shell_window->window_key()));
323   SetCreateResultFromShellWindow(shell_window, result);
324   SetResult(result);
325
326   if (apps::ShellWindowRegistry::Get(GetProfile())
327           ->HadDevToolsAttached(created_view)) {
328     new DevToolsRestorer(this, created_view);
329     return true;
330   }
331
332   SendResponse(true);
333   return true;
334 }
335
336 }  // namespace extensions