• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_current_window_internal/app_current_window_internal_api.h"
6 
7 #include "apps/app_window.h"
8 #include "apps/app_window_registry.h"
9 #include "apps/size_constraints.h"
10 #include "apps/ui/native_app_window.h"
11 #include "base/command_line.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/common/extensions/api/app_current_window_internal.h"
14 #include "chrome/common/extensions/api/app_window.h"
15 #include "chrome/common/extensions/features/feature_channel.h"
16 #include "extensions/common/features/simple_feature.h"
17 #include "extensions/common/permissions/permissions_data.h"
18 #include "extensions/common/switches.h"
19 #include "third_party/skia/include/core/SkRegion.h"
20 
21 namespace app_current_window_internal =
22     extensions::api::app_current_window_internal;
23 
24 namespace Show = app_current_window_internal::Show;
25 namespace SetBounds = app_current_window_internal::SetBounds;
26 namespace SetSizeConstraints = app_current_window_internal::SetSizeConstraints;
27 namespace SetIcon = app_current_window_internal::SetIcon;
28 namespace SetBadgeIcon = app_current_window_internal::SetBadgeIcon;
29 namespace SetShape = app_current_window_internal::SetShape;
30 namespace SetAlwaysOnTop = app_current_window_internal::SetAlwaysOnTop;
31 
32 using apps::AppWindow;
33 using app_current_window_internal::Bounds;
34 using app_current_window_internal::Region;
35 using app_current_window_internal::RegionRect;
36 using app_current_window_internal::SizeConstraints;
37 
38 namespace extensions {
39 
40 namespace {
41 
42 const char kNoAssociatedAppWindow[] =
43     "The context from which the function was called did not have an "
44     "associated app window.";
45 
46 const char kDevChannelOnly[] =
47     "This function is currently only available in the Dev channel.";
48 
49 const char kRequiresFramelessWindow[] =
50     "This function requires a frameless window (frame:none).";
51 
52 const char kAlwaysOnTopPermission[] =
53     "The \"app.window.alwaysOnTop\" permission is required.";
54 
55 const char kInvalidParameters[] = "Invalid parameters.";
56 
57 const int kUnboundedSize = apps::SizeConstraints::kUnboundedSize;
58 
GetBoundsFields(const Bounds & bounds_spec,gfx::Rect * bounds)59 void GetBoundsFields(const Bounds& bounds_spec, gfx::Rect* bounds) {
60   if (bounds_spec.left)
61     bounds->set_x(*bounds_spec.left);
62   if (bounds_spec.top)
63     bounds->set_y(*bounds_spec.top);
64   if (bounds_spec.width)
65     bounds->set_width(*bounds_spec.width);
66   if (bounds_spec.height)
67     bounds->set_height(*bounds_spec.height);
68 }
69 
70 // Copy the constraint value from the API to our internal representation of
71 // content size constraints. A value of zero resets the constraints. The insets
72 // are used to transform window constraints to content constraints.
GetConstraintWidth(const scoped_ptr<int> & width,const gfx::Insets & insets,gfx::Size * size)73 void GetConstraintWidth(const scoped_ptr<int>& width,
74                         const gfx::Insets& insets,
75                         gfx::Size* size) {
76   if (!width.get())
77     return;
78 
79   size->set_width(*width > 0 ? std::max(0, *width - insets.width())
80                              : kUnboundedSize);
81 }
82 
GetConstraintHeight(const scoped_ptr<int> & height,const gfx::Insets & insets,gfx::Size * size)83 void GetConstraintHeight(const scoped_ptr<int>& height,
84                          const gfx::Insets& insets,
85                          gfx::Size* size) {
86   if (!height.get())
87     return;
88 
89   size->set_height(*height > 0 ? std::max(0, *height - insets.height())
90                                : kUnboundedSize);
91 }
92 
93 }  // namespace
94 
95 namespace bounds {
96 
97 enum BoundsType {
98   INNER_BOUNDS,
99   OUTER_BOUNDS,
100   DEPRECATED_BOUNDS,
101   INVALID_TYPE
102 };
103 
104 const char kInnerBoundsType[] = "innerBounds";
105 const char kOuterBoundsType[] = "outerBounds";
106 const char kDeprecatedBoundsType[] = "bounds";
107 
GetBoundsType(const std::string & type_as_string)108 BoundsType GetBoundsType(const std::string& type_as_string) {
109   if (type_as_string == kInnerBoundsType)
110     return INNER_BOUNDS;
111   else if (type_as_string == kOuterBoundsType)
112     return OUTER_BOUNDS;
113   else if (type_as_string == kDeprecatedBoundsType)
114     return DEPRECATED_BOUNDS;
115   else
116     return INVALID_TYPE;
117 }
118 
119 }  // namespace bounds
120 
RunSync()121 bool AppCurrentWindowInternalExtensionFunction::RunSync() {
122   apps::AppWindowRegistry* registry =
123       apps::AppWindowRegistry::Get(GetProfile());
124   DCHECK(registry);
125   content::RenderViewHost* rvh = render_view_host();
126   if (!rvh)
127     // No need to set an error, since we won't return to the caller anyway if
128     // there's no RVH.
129     return false;
130   AppWindow* window = registry->GetAppWindowForRenderViewHost(rvh);
131   if (!window) {
132     error_ = kNoAssociatedAppWindow;
133     return false;
134   }
135   return RunWithWindow(window);
136 }
137 
RunWithWindow(AppWindow * window)138 bool AppCurrentWindowInternalFocusFunction::RunWithWindow(AppWindow* window) {
139   window->GetBaseWindow()->Activate();
140   return true;
141 }
142 
RunWithWindow(AppWindow * window)143 bool AppCurrentWindowInternalFullscreenFunction::RunWithWindow(
144     AppWindow* window) {
145   window->Fullscreen();
146   return true;
147 }
148 
RunWithWindow(AppWindow * window)149 bool AppCurrentWindowInternalMaximizeFunction::RunWithWindow(
150     AppWindow* window) {
151   window->Maximize();
152   return true;
153 }
154 
RunWithWindow(AppWindow * window)155 bool AppCurrentWindowInternalMinimizeFunction::RunWithWindow(
156     AppWindow* window) {
157   window->Minimize();
158   return true;
159 }
160 
RunWithWindow(AppWindow * window)161 bool AppCurrentWindowInternalRestoreFunction::RunWithWindow(AppWindow* window) {
162   window->Restore();
163   return true;
164 }
165 
RunWithWindow(AppWindow * window)166 bool AppCurrentWindowInternalDrawAttentionFunction::RunWithWindow(
167     AppWindow* window) {
168   window->GetBaseWindow()->FlashFrame(true);
169   return true;
170 }
171 
RunWithWindow(AppWindow * window)172 bool AppCurrentWindowInternalClearAttentionFunction::RunWithWindow(
173     AppWindow* window) {
174   window->GetBaseWindow()->FlashFrame(false);
175   return true;
176 }
177 
RunWithWindow(AppWindow * window)178 bool AppCurrentWindowInternalShowFunction::RunWithWindow(AppWindow* window) {
179   scoped_ptr<Show::Params> params(Show::Params::Create(*args_));
180   CHECK(params.get());
181   if (params->focused && !*params->focused)
182     window->Show(AppWindow::SHOW_INACTIVE);
183   else
184     window->Show(AppWindow::SHOW_ACTIVE);
185   return true;
186 }
187 
RunWithWindow(AppWindow * window)188 bool AppCurrentWindowInternalHideFunction::RunWithWindow(AppWindow* window) {
189   window->Hide();
190   return true;
191 }
192 
RunWithWindow(AppWindow * window)193 bool AppCurrentWindowInternalSetBoundsFunction::RunWithWindow(
194     AppWindow* window) {
195   scoped_ptr<SetBounds::Params> params(SetBounds::Params::Create(*args_));
196   CHECK(params.get());
197 
198   bounds::BoundsType bounds_type = bounds::GetBoundsType(params->bounds_type);
199   if (bounds_type == bounds::INVALID_TYPE) {
200     NOTREACHED();
201     error_ = kInvalidParameters;
202     return false;
203   }
204 
205   // Start with the current bounds, and change any values that are specified in
206   // the incoming parameters.
207   gfx::Rect original_window_bounds = window->GetBaseWindow()->GetBounds();
208   gfx::Rect window_bounds = original_window_bounds;
209   gfx::Insets frame_insets = window->GetBaseWindow()->GetFrameInsets();
210   const Bounds& bounds_spec = params->bounds;
211 
212   switch (bounds_type) {
213     case bounds::DEPRECATED_BOUNDS: {
214       // We need to maintain backcompatibility with a bug on Windows and
215       // ChromeOS, which sets the position of the window but the size of the
216       // content.
217       if (bounds_spec.left)
218         window_bounds.set_x(*bounds_spec.left);
219       if (bounds_spec.top)
220         window_bounds.set_y(*bounds_spec.top);
221       if (bounds_spec.width)
222         window_bounds.set_width(*bounds_spec.width + frame_insets.width());
223       if (bounds_spec.height)
224         window_bounds.set_height(*bounds_spec.height + frame_insets.height());
225       break;
226     }
227     case bounds::OUTER_BOUNDS: {
228       GetBoundsFields(bounds_spec, &window_bounds);
229       break;
230     }
231     case bounds::INNER_BOUNDS: {
232       window_bounds.Inset(frame_insets);
233       GetBoundsFields(bounds_spec, &window_bounds);
234       window_bounds.Inset(-frame_insets);
235       break;
236     }
237     default:
238       NOTREACHED();
239   }
240 
241   if (original_window_bounds != window_bounds) {
242     if (original_window_bounds.size() != window_bounds.size()) {
243       apps::SizeConstraints constraints(
244           apps::SizeConstraints::AddFrameToConstraints(
245               window->GetBaseWindow()->GetContentMinimumSize(), frame_insets),
246           apps::SizeConstraints::AddFrameToConstraints(
247               window->GetBaseWindow()->GetContentMaximumSize(), frame_insets));
248 
249       window_bounds.set_size(constraints.ClampSize(window_bounds.size()));
250     }
251 
252     window->GetBaseWindow()->SetBounds(window_bounds);
253   }
254 
255   return true;
256 }
257 
RunWithWindow(AppWindow * window)258 bool AppCurrentWindowInternalSetSizeConstraintsFunction::RunWithWindow(
259     AppWindow* window) {
260   scoped_ptr<SetSizeConstraints::Params> params(
261       SetSizeConstraints::Params::Create(*args_));
262   CHECK(params.get());
263 
264   bounds::BoundsType bounds_type = bounds::GetBoundsType(params->bounds_type);
265   if (bounds_type != bounds::INNER_BOUNDS &&
266       bounds_type != bounds::OUTER_BOUNDS) {
267     NOTREACHED();
268     error_ = kInvalidParameters;
269     return false;
270   }
271 
272   gfx::Size original_min_size =
273       window->GetBaseWindow()->GetContentMinimumSize();
274   gfx::Size original_max_size =
275       window->GetBaseWindow()->GetContentMaximumSize();
276   gfx::Size min_size = original_min_size;
277   gfx::Size max_size = original_max_size;
278   const SizeConstraints& constraints = params->constraints;
279 
280   // Use the frame insets to convert window size constraints to content size
281   // constraints.
282   gfx::Insets insets;
283   if (bounds_type == bounds::OUTER_BOUNDS)
284     insets = window->GetBaseWindow()->GetFrameInsets();
285 
286   GetConstraintWidth(constraints.min_width, insets, &min_size);
287   GetConstraintWidth(constraints.max_width, insets, &max_size);
288   GetConstraintHeight(constraints.min_height, insets, &min_size);
289   GetConstraintHeight(constraints.max_height, insets, &max_size);
290 
291   if (min_size != original_min_size || max_size != original_max_size)
292     window->SetContentSizeConstraints(min_size, max_size);
293 
294   return true;
295 }
296 
RunWithWindow(AppWindow * window)297 bool AppCurrentWindowInternalSetIconFunction::RunWithWindow(AppWindow* window) {
298   if (GetCurrentChannel() > chrome::VersionInfo::CHANNEL_DEV &&
299       GetExtension()->location() != extensions::Manifest::COMPONENT) {
300     error_ = kDevChannelOnly;
301     return false;
302   }
303 
304   scoped_ptr<SetIcon::Params> params(SetIcon::Params::Create(*args_));
305   CHECK(params.get());
306   // The |icon_url| parameter may be a blob url (e.g. an image fetched with an
307   // XMLHttpRequest) or a resource url.
308   GURL url(params->icon_url);
309   if (!url.is_valid())
310     url = GetExtension()->GetResourceURL(params->icon_url);
311 
312   window->SetAppIconUrl(url);
313   return true;
314 }
315 
RunWithWindow(AppWindow * window)316 bool AppCurrentWindowInternalSetBadgeIconFunction::RunWithWindow(
317     AppWindow* window) {
318   if (GetCurrentChannel() > chrome::VersionInfo::CHANNEL_DEV) {
319     error_ = kDevChannelOnly;
320     return false;
321   }
322 
323   scoped_ptr<SetBadgeIcon::Params> params(SetBadgeIcon::Params::Create(*args_));
324   CHECK(params.get());
325   // The |icon_url| parameter may be a blob url (e.g. an image fetched with an
326   // XMLHttpRequest) or a resource url.
327   GURL url(params->icon_url);
328   if (!url.is_valid() && !params->icon_url.empty())
329     url = GetExtension()->GetResourceURL(params->icon_url);
330 
331   window->SetBadgeIconUrl(url);
332   return true;
333 }
334 
RunWithWindow(AppWindow * window)335 bool AppCurrentWindowInternalClearBadgeFunction::RunWithWindow(
336     AppWindow* window) {
337   if (GetCurrentChannel() > chrome::VersionInfo::CHANNEL_DEV) {
338     error_ = kDevChannelOnly;
339     return false;
340   }
341 
342   window->ClearBadge();
343   return true;
344 }
345 
RunWithWindow(AppWindow * window)346 bool AppCurrentWindowInternalSetShapeFunction::RunWithWindow(
347     AppWindow* window) {
348 
349   if (!window->GetBaseWindow()->IsFrameless()) {
350     error_ = kRequiresFramelessWindow;
351     return false;
352   }
353 
354   scoped_ptr<SetShape::Params> params(
355       SetShape::Params::Create(*args_));
356   const Region& shape = params->region;
357 
358   // Build a region from the supplied list of rects.
359   // If |rects| is missing, then the input region is removed. This clears the
360   // input region so that the entire window accepts input events.
361   // To specify an empty input region (so the window ignores all input),
362   // |rects| should be an empty list.
363   scoped_ptr<SkRegion> region(new SkRegion);
364   if (shape.rects) {
365     for (std::vector<linked_ptr<RegionRect> >::const_iterator i =
366              shape.rects->begin();
367          i != shape.rects->end();
368          ++i) {
369       const RegionRect& inputRect = **i;
370       int32_t x = inputRect.left;
371       int32_t y = inputRect.top;
372       int32_t width = inputRect.width;
373       int32_t height = inputRect.height;
374 
375       SkIRect rect = SkIRect::MakeXYWH(x, y, width, height);
376       region->op(rect, SkRegion::kUnion_Op);
377     }
378   } else {
379     region.reset(NULL);
380   }
381 
382   window->UpdateShape(region.Pass());
383 
384   return true;
385 }
386 
RunWithWindow(AppWindow * window)387 bool AppCurrentWindowInternalSetAlwaysOnTopFunction::RunWithWindow(
388     AppWindow* window) {
389   if (!GetExtension()->permissions_data()->HasAPIPermission(
390           extensions::APIPermission::kAlwaysOnTopWindows)) {
391     error_ = kAlwaysOnTopPermission;
392     return false;
393   }
394 
395   scoped_ptr<SetAlwaysOnTop::Params> params(
396       SetAlwaysOnTop::Params::Create(*args_));
397   CHECK(params.get());
398   window->SetAlwaysOnTop(params->always_on_top);
399   return true;
400 }
401 
402 }  // namespace extensions
403