Home | History | Annotate | Download | only in app_current_window_internal
      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 
     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.
     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 
     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 
    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 
    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 
    138 bool AppCurrentWindowInternalFocusFunction::RunWithWindow(AppWindow* window) {
    139   window->GetBaseWindow()->Activate();
    140   return true;
    141 }
    142 
    143 bool AppCurrentWindowInternalFullscreenFunction::RunWithWindow(
    144     AppWindow* window) {
    145   window->Fullscreen();
    146   return true;
    147 }
    148 
    149 bool AppCurrentWindowInternalMaximizeFunction::RunWithWindow(
    150     AppWindow* window) {
    151   window->Maximize();
    152   return true;
    153 }
    154 
    155 bool AppCurrentWindowInternalMinimizeFunction::RunWithWindow(
    156     AppWindow* window) {
    157   window->Minimize();
    158   return true;
    159 }
    160 
    161 bool AppCurrentWindowInternalRestoreFunction::RunWithWindow(AppWindow* window) {
    162   window->Restore();
    163   return true;
    164 }
    165 
    166 bool AppCurrentWindowInternalDrawAttentionFunction::RunWithWindow(
    167     AppWindow* window) {
    168   window->GetBaseWindow()->FlashFrame(true);
    169   return true;
    170 }
    171 
    172 bool AppCurrentWindowInternalClearAttentionFunction::RunWithWindow(
    173     AppWindow* window) {
    174   window->GetBaseWindow()->FlashFrame(false);
    175   return true;
    176 }
    177 
    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 
    188 bool AppCurrentWindowInternalHideFunction::RunWithWindow(AppWindow* window) {
    189   window->Hide();
    190   return true;
    191 }
    192 
    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 
    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 
    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 
    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 
    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 
    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 
    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