- add sources.
[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::ToggleDevToolsWindow(
67             created_view,
68             true /* force_open */,
69             DevToolsToggleAction::ShowConsole());
70
71     registrar_.Add(
72         this,
73         content::NOTIFICATION_LOAD_STOP,
74         content::Source<content::NavigationController>(
75             &devtools_window->web_contents()->GetController()));
76   }
77
78  protected:
79   // content::NotificationObserver:
80   virtual void Observe(int type,
81                        const content::NotificationSource& source,
82                        const content::NotificationDetails& details) OVERRIDE {
83     DCHECK(type == content::NOTIFICATION_LOAD_STOP);
84     delayed_create_function_->SendDelayedResponse();
85     delete this;
86   }
87
88  private:
89   scoped_refptr<AppWindowCreateFunction> delayed_create_function_;
90   content::NotificationRegistrar registrar_;
91 };
92
93 void SetCreateResultFromShellWindow(ShellWindow* window,
94                                     base::DictionaryValue* result) {
95   result->SetBoolean("fullscreen", window->GetBaseWindow()->IsFullscreen());
96   result->SetBoolean("minimized", window->GetBaseWindow()->IsMinimized());
97   result->SetBoolean("maximized", window->GetBaseWindow()->IsMaximized());
98   result->SetBoolean("alwaysOnTop", window->GetBaseWindow()->IsAlwaysOnTop());
99   base::DictionaryValue* boundsValue = new base::DictionaryValue();
100   gfx::Rect bounds = window->GetClientBounds();
101   boundsValue->SetInteger("left", bounds.x());
102   boundsValue->SetInteger("top", bounds.y());
103   boundsValue->SetInteger("width", bounds.width());
104   boundsValue->SetInteger("height", bounds.height());
105   result->Set("bounds", boundsValue);
106
107   const ShellWindow::SizeConstraints& size_constraints =
108       window->size_constraints();
109   gfx::Size min_size = size_constraints.GetMinimumSize();
110   gfx::Size max_size = size_constraints.GetMaximumSize();
111   if (min_size.width() != kUnboundedSize)
112     result->SetInteger("minWidth", min_size.width());
113   if (min_size.height() != kUnboundedSize)
114     result->SetInteger("minHeight", min_size.height());
115   if (max_size.width() != kUnboundedSize)
116     result->SetInteger("maxWidth", max_size.width());
117   if (max_size.height() != kUnboundedSize)
118     result->SetInteger("maxHeight", max_size.height());
119 }
120
121 }  // namespace
122
123 void AppWindowCreateFunction::SendDelayedResponse() {
124   SendResponse(true);
125 }
126
127 bool AppWindowCreateFunction::RunImpl() {
128   // Don't create app window if the system is shutting down.
129   if (g_browser_process->IsShuttingDown())
130     return false;
131
132   scoped_ptr<Create::Params> params(Create::Params::Create(*args_));
133   EXTENSION_FUNCTION_VALIDATE(params.get());
134
135   GURL url = GetExtension()->GetResourceURL(params->url);
136   // Allow absolute URLs for component apps, otherwise prepend the extension
137   // path.
138   if (GetExtension()->location() == extensions::Manifest::COMPONENT) {
139     GURL absolute = GURL(params->url);
140     if (absolute.has_scheme())
141       url = absolute;
142   }
143
144   bool inject_html_titlebar = false;
145
146   // TODO(jeremya): figure out a way to pass the opening WebContents through to
147   // ShellWindow::Create so we can set the opener at create time rather than
148   // with a hack in AppWindowCustomBindings::GetView().
149   ShellWindow::CreateParams create_params;
150   app_window::CreateWindowOptions* options = params->options.get();
151   if (options) {
152     if (options->id.get()) {
153       // TODO(mek): use URL if no id specified?
154       // Limit length of id to 256 characters.
155       if (options->id->length() > 256) {
156         error_ = app_window_constants::kInvalidWindowId;
157         return false;
158       }
159
160       create_params.window_key = *options->id;
161
162       if (!options->singleton || *options->singleton) {
163         ShellWindow* window = apps::ShellWindowRegistry::Get(
164             GetProfile())->GetShellWindowForAppAndKey(extension_id(),
165                                                       create_params.window_key);
166         if (window) {
167           content::RenderViewHost* created_view =
168               window->web_contents()->GetRenderViewHost();
169           int view_id = MSG_ROUTING_NONE;
170           if (render_view_host_->GetProcess()->GetID() ==
171               created_view->GetProcess()->GetID()) {
172             view_id = created_view->GetRoutingID();
173           }
174
175           window->GetBaseWindow()->Show();
176           base::DictionaryValue* result = new base::DictionaryValue;
177           result->Set("viewId", new base::FundamentalValue(view_id));
178           SetCreateResultFromShellWindow(window, result);
179           result->SetBoolean("existingWindow", true);
180           result->SetBoolean("injectTitlebar", false);
181           SetResult(result);
182           SendResponse(true);
183           return true;
184         }
185       }
186     }
187
188     // TODO(jeremya): remove these, since they do the same thing as
189     // left/top/width/height.
190     if (options->default_width.get())
191       create_params.bounds.set_width(*options->default_width.get());
192     if (options->default_height.get())
193       create_params.bounds.set_height(*options->default_height.get());
194     if (options->default_left.get())
195       create_params.bounds.set_x(*options->default_left.get());
196     if (options->default_top.get())
197       create_params.bounds.set_y(*options->default_top.get());
198
199     if (options->width.get())
200       create_params.bounds.set_width(*options->width.get());
201     if (options->height.get())
202       create_params.bounds.set_height(*options->height.get());
203     if (options->left.get())
204       create_params.bounds.set_x(*options->left.get());
205     if (options->top.get())
206       create_params.bounds.set_y(*options->top.get());
207
208     if (options->bounds.get()) {
209       app_window::Bounds* bounds = options->bounds.get();
210       if (bounds->width.get())
211         create_params.bounds.set_width(*bounds->width.get());
212       if (bounds->height.get())
213         create_params.bounds.set_height(*bounds->height.get());
214       if (bounds->left.get())
215         create_params.bounds.set_x(*bounds->left.get());
216       if (bounds->top.get())
217         create_params.bounds.set_y(*bounds->top.get());
218     }
219
220     if (GetCurrentChannel() <= chrome::VersionInfo::CHANNEL_DEV ||
221         GetExtension()->location() == extensions::Manifest::COMPONENT) {
222       if (options->type == extensions::api::app_window::WINDOW_TYPE_PANEL) {
223           create_params.window_type = ShellWindow::WINDOW_TYPE_PANEL;
224       }
225     }
226
227     if (options->frame.get()) {
228       if (*options->frame == kHtmlFrameOption &&
229           (GetExtension()->HasAPIPermission(APIPermission::kExperimental) ||
230            CommandLine::ForCurrentProcess()->HasSwitch(
231                switches::kEnableExperimentalExtensionApis))) {
232         create_params.frame = ShellWindow::FRAME_NONE;
233         inject_html_titlebar = true;
234       } else if (*options->frame == kNoneFrameOption) {
235         create_params.frame = ShellWindow::FRAME_NONE;
236       } else {
237         create_params.frame = ShellWindow::FRAME_CHROME;
238       }
239     }
240
241     if (options->transparent_background.get() &&
242         (GetExtension()->HasAPIPermission(APIPermission::kExperimental) ||
243          CommandLine::ForCurrentProcess()->HasSwitch(
244              switches::kEnableExperimentalExtensionApis))) {
245       create_params.transparent_background = *options->transparent_background;
246     }
247
248     gfx::Size& minimum_size = create_params.minimum_size;
249     if (options->min_width.get())
250       minimum_size.set_width(*options->min_width);
251     if (options->min_height.get())
252       minimum_size.set_height(*options->min_height);
253     gfx::Size& maximum_size = create_params.maximum_size;
254     if (options->max_width.get())
255       maximum_size.set_width(*options->max_width);
256     if (options->max_height.get())
257       maximum_size.set_height(*options->max_height);
258
259     if (options->hidden.get())
260       create_params.hidden = *options->hidden.get();
261
262     if (options->resizable.get())
263       create_params.resizable = *options->resizable.get();
264
265     if (options->always_on_top.get())
266       create_params.always_on_top = *options->always_on_top.get();
267
268     if (options->type != extensions::api::app_window::WINDOW_TYPE_PANEL) {
269       switch (options->state) {
270         case extensions::api::app_window::STATE_NONE:
271         case extensions::api::app_window::STATE_NORMAL:
272           break;
273         case extensions::api::app_window::STATE_FULLSCREEN:
274           create_params.state = ui::SHOW_STATE_FULLSCREEN;
275           break;
276         case extensions::api::app_window::STATE_MAXIMIZED:
277           create_params.state = ui::SHOW_STATE_MAXIMIZED;
278           break;
279         case extensions::api::app_window::STATE_MINIMIZED:
280           create_params.state = ui::SHOW_STATE_MINIMIZED;
281           break;
282       }
283     }
284   }
285
286   create_params.creator_process_id =
287       render_view_host_->GetProcess()->GetID();
288
289   ShellWindow* shell_window = new ShellWindow(
290       GetProfile(), new ChromeShellWindowDelegate(), GetExtension());
291   shell_window->Init(url,
292                      new apps::AppWindowContents(shell_window),
293                      create_params);
294
295   if (chrome::IsRunningInForcedAppMode())
296     shell_window->Fullscreen();
297
298   content::RenderViewHost* created_view =
299       shell_window->web_contents()->GetRenderViewHost();
300   int view_id = MSG_ROUTING_NONE;
301   if (create_params.creator_process_id == created_view->GetProcess()->GetID())
302     view_id = created_view->GetRoutingID();
303
304   base::DictionaryValue* result = new base::DictionaryValue;
305   result->Set("viewId", new base::FundamentalValue(view_id));
306   result->Set("injectTitlebar",
307       new base::FundamentalValue(inject_html_titlebar));
308   result->Set("id", new base::StringValue(shell_window->window_key()));
309   SetCreateResultFromShellWindow(shell_window, result);
310   SetResult(result);
311
312   if (apps::ShellWindowRegistry::Get(GetProfile())
313           ->HadDevToolsAttached(created_view)) {
314     new DevToolsRestorer(this, created_view);
315     return true;
316   }
317
318   SendResponse(true);
319   return true;
320 }
321
322 }  // namespace extensions