Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / display_info_provider_chromeos.cc
1 // Copyright 2014 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/display_info_provider_chromeos.h"
6
7 #include "ash/display/display_controller.h"
8 #include "ash/display/display_manager.h"
9 #include "ash/shell.h"
10 #include "base/message_loop/message_loop_proxy.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "extensions/common/api/system_display.h"
13 #include "ui/gfx/display.h"
14 #include "ui/gfx/point.h"
15 #include "ui/gfx/rect.h"
16
17 using ash::DisplayManager;
18
19 namespace extensions {
20
21 using core_api::system_display::Bounds;
22 using core_api::system_display::DisplayUnitInfo;
23 using core_api::system_display::DisplayProperties;
24 using core_api::system_display::Insets;
25
26 namespace {
27
28 // Maximum allowed bounds origin absolute value.
29 const int kMaxBoundsOrigin = 200 * 1000;
30
31 // Checks if the given integer value is valid display rotation in degrees.
32 bool IsValidRotationValue(int rotation) {
33   return rotation == 0 || rotation == 90 || rotation == 180 || rotation == 270;
34 }
35
36 // Converts integer integer value in degrees to Rotation enum value.
37 gfx::Display::Rotation DegreesToRotation(int degrees) {
38   DCHECK(IsValidRotationValue(degrees));
39   switch (degrees) {
40     case 0:
41       return gfx::Display::ROTATE_0;
42     case 90:
43       return gfx::Display::ROTATE_90;
44     case 180:
45       return gfx::Display::ROTATE_180;
46     case 270:
47       return gfx::Display::ROTATE_270;
48     default:
49       return gfx::Display::ROTATE_0;
50   }
51 }
52
53 // Checks if the given point is over the radius vector described by it's end
54 // point |vector|. The point is over a vector if it's on its positive (left)
55 // side. The method sees a point on the same line as the vector as being over
56 // the vector.
57 bool PointIsOverRadiusVector(const gfx::Point& point,
58                              const gfx::Point& vector) {
59   // |point| is left of |vector| if its radius vector's scalar product with a
60   // vector orthogonal (and facing the positive side) to |vector| is positive.
61   //
62   // An orthogonal vector of (a, b) is (b, -a), as the scalar product of these
63   // two is 0.
64   // So, (x, y) is over (a, b) if x * b + y * (-a) >= 0, which is equivalent to
65   // x * b >= y * a.
66   return static_cast<int64>(point.x()) * static_cast<int64>(vector.y()) >=
67          static_cast<int64>(point.y()) * static_cast<int64>(vector.x());
68 }
69
70 // Created ash::DisplayLayout value for |rectangle| compared to the |reference|
71 // rectangle.
72 // The layout consists of two values:
73 //   - position: Whether the rectangle is positioned left, right, over or under
74 //     the reference.
75 //   - offset: The rectangle's offset from the reference origin along the axis
76 //     opposite the position direction (if the rectangle is left or right along
77 //     y-axis, otherwise along x-axis).
78 // The rectangle's position is calculated by dividing the space in areas defined
79 // by the |reference|'s diagonals and finding the area |rectangle|'s center
80 // point belongs. If the |rectangle| in the calculated layout does not share a
81 // part of the bounds with the |reference|, the |rectangle| position in set to
82 // the more suitable neighboring position (e.g. if |rectangle| is completely
83 // over the |reference| top bound, it will be set to TOP) and the layout is
84 // recalculated with the new position. This is to handle case where the
85 // rectangle shares an edge with the reference, but it's center is not in the
86 // same area as the reference's edge, e.g.
87 //
88 // +---------------------+
89 // |                     |
90 // | REFERENCE           |
91 // |                     |
92 // |                     |
93 // +---------------------+
94 //                 +-------------------------------------------------+
95 //                 | RECTANGLE               x                       |
96 //                 +-------------------------------------------------+
97 //
98 // The rectangle shares an egde with the reference's bottom edge, but it's
99 // center point is in the left area.
100 ash::DisplayLayout GetLayoutForRectangles(const gfx::Rect& reference,
101                                           const gfx::Rect& rectangle) {
102   // Translate coordinate system so origin is in the reference's top left point
103   // (so the reference's down-diagonal vector starts in the (0, 0)) and scale it
104   // up by two (to avoid division when calculating the rectangle's center
105   // point).
106   gfx::Point center(2 * (rectangle.x() - reference.x()) + rectangle.width(),
107                     2 * (rectangle.y() - reference.y()) + rectangle.height());
108   gfx::Point down_diag(2 * reference.width(), 2 * reference.height());
109
110   bool is_top_right = PointIsOverRadiusVector(center, down_diag);
111
112   // Translate the coordinating system again, so the bottom right point of the
113   // reference is origin (so the references up-diagonal starts at (0, 0)).
114   // Note that the coordinate system is scaled by 2.
115   center.Offset(0, -2 * reference.height());
116   // Choose the vector orientation so the points on the diagonal are considered
117   // to be left.
118   gfx::Point up_diag(-2 * reference.width(), 2 * reference.height());
119
120   bool is_bottom_right = PointIsOverRadiusVector(center, up_diag);
121
122   ash::DisplayLayout::Position position;
123   if (is_top_right) {
124     position =
125         is_bottom_right ? ash::DisplayLayout::RIGHT : ash::DisplayLayout::TOP;
126   } else {
127     position =
128         is_bottom_right ? ash::DisplayLayout::BOTTOM : ash::DisplayLayout::LEFT;
129   }
130
131   // If the rectangle with the calculated position would not have common side
132   // with the reference, try to position it so it shares another edge with the
133   // reference.
134   if (is_top_right == is_bottom_right) {
135     if (rectangle.y() > reference.y() + reference.height()) {
136       // The rectangle is left or right, but completely under the reference.
137       position = ash::DisplayLayout::BOTTOM;
138     } else if (rectangle.y() + rectangle.height() < reference.y()) {
139       // The rectangle is left or right, but completely over the reference.
140       position = ash::DisplayLayout::TOP;
141     }
142   } else {
143     if (rectangle.x() > reference.x() + reference.width()) {
144       // The rectangle is over or under, but completely right of the reference.
145       position = ash::DisplayLayout::RIGHT;
146     } else if (rectangle.x() + rectangle.width() < reference.x()) {
147       // The rectangle is over or under, but completely left of the reference.
148       position = ash::DisplayLayout::LEFT;
149     }
150   }
151
152   if (position == ash::DisplayLayout::LEFT ||
153       position == ash::DisplayLayout::RIGHT) {
154     return ash::DisplayLayout::FromInts(position, rectangle.y());
155   } else {
156     return ash::DisplayLayout::FromInts(position, rectangle.x());
157   }
158 }
159
160 // Updates the display layout for the target display in reference to the primary
161 // display.
162 void UpdateDisplayLayout(const gfx::Rect& primary_display_bounds,
163                          int primary_display_id,
164                          const gfx::Rect& target_display_bounds,
165                          int target_display_id) {
166   ash::DisplayLayout layout =
167       GetLayoutForRectangles(primary_display_bounds, target_display_bounds);
168   ash::Shell::GetInstance()->display_manager()->SetLayoutForCurrentDisplays(
169       layout);
170 }
171
172 // Validates that parameters passed to the SetInfo function are valid for the
173 // desired display and the current display manager state.
174 // Returns whether the parameters are valid. On failure |error| is set to the
175 // error message.
176 bool ValidateParamsForDisplay(const DisplayProperties& info,
177                               const gfx::Display& display,
178                               DisplayManager* display_manager,
179                               int64 primary_display_id,
180                               std::string* error) {
181   bool is_primary = display.id() == primary_display_id ||
182                     (info.is_primary && *info.is_primary);
183
184   // If mirroring source id is set, a display with the given id should exist,
185   // and if should not be the same as the target display's id.
186   if (info.mirroring_source_id && !info.mirroring_source_id->empty()) {
187     int64 mirroring_id;
188     if (!base::StringToInt64(*info.mirroring_source_id, &mirroring_id) ||
189         display_manager->GetDisplayForId(mirroring_id).id() ==
190             gfx::Display::kInvalidDisplayID) {
191       *error = "Display " + *info.mirroring_source_id + " not found.";
192       return false;
193     }
194
195     if (*info.mirroring_source_id == base::Int64ToString(display.id())) {
196       *error = "Not allowed to mirror self.";
197       return false;
198     }
199   }
200
201   // If mirroring source parameter is specified, no other parameter should be
202   // set as when the mirroring is applied the display list could change.
203   if (info.mirroring_source_id &&
204       (info.is_primary || info.bounds_origin_x || info.bounds_origin_y ||
205        info.rotation || info.overscan)) {
206     *error = "No other parameter should be set alongside mirroringSourceId.";
207     return false;
208   }
209
210   // The bounds cannot be changed for the primary display and should be inside
211   // a reasonable bounds. Note that the display is considered primary if the
212   // info has 'isPrimary' parameter set, as this will be applied before bounds
213   // origin changes.
214   if (info.bounds_origin_x || info.bounds_origin_y) {
215     if (is_primary) {
216       *error = "Bounds origin not allowed for the primary display.";
217       return false;
218     }
219     if (info.bounds_origin_x && (*info.bounds_origin_x > kMaxBoundsOrigin ||
220                                  *info.bounds_origin_x < -kMaxBoundsOrigin)) {
221       *error = "Bounds origin x out of bounds.";
222       return false;
223     }
224     if (info.bounds_origin_y && (*info.bounds_origin_y > kMaxBoundsOrigin ||
225                                  *info.bounds_origin_y < -kMaxBoundsOrigin)) {
226       *error = "Bounds origin y out of bounds.";
227       return false;
228     }
229   }
230
231   // Verify the rotation value is valid.
232   if (info.rotation && !IsValidRotationValue(*info.rotation)) {
233     *error = "Invalid rotation.";
234     return false;
235   }
236
237   // Overscan cannot be changed for the internal display, and should be at most
238   // half of the screen size.
239   if (info.overscan) {
240     if (display.IsInternal()) {
241       *error = "Overscan changes not allowed for the internal monitor.";
242       return false;
243     }
244
245     if (info.overscan->left < 0 || info.overscan->top < 0 ||
246         info.overscan->right < 0 || info.overscan->bottom < 0) {
247       *error = "Negative overscan not allowed.";
248       return false;
249     }
250
251     const gfx::Insets overscan =
252         display_manager->GetOverscanInsets(display.id());
253     int screen_width = display.bounds().width() + overscan.width();
254     int screen_height = display.bounds().height() + overscan.height();
255
256     if ((info.overscan->left + info.overscan->right) * 2 > screen_width) {
257       *error = "Horizontal overscan is more than half of the screen width.";
258       return false;
259     }
260
261     if ((info.overscan->top + info.overscan->bottom) * 2 > screen_height) {
262       *error = "Vertical overscan is more than half of the screen height.";
263       return false;
264     }
265   }
266   return true;
267 }
268
269 // Gets the display with the provided string id.
270 gfx::Display GetTargetDisplay(const std::string& display_id_str,
271                               DisplayManager* manager) {
272   int64 display_id;
273   if (!base::StringToInt64(display_id_str, &display_id)) {
274     // This should return invalid display.
275     return gfx::Display();
276   }
277   return manager->GetDisplayForId(display_id);
278 }
279
280 }  // namespace
281
282 DisplayInfoProviderChromeOS::DisplayInfoProviderChromeOS() {
283 }
284
285 DisplayInfoProviderChromeOS::~DisplayInfoProviderChromeOS() {
286 }
287
288 bool DisplayInfoProviderChromeOS::SetInfo(const std::string& display_id_str,
289                                           const DisplayProperties& info,
290                                           std::string* error) {
291   DisplayManager* display_manager =
292       ash::Shell::GetInstance()->display_manager();
293   DCHECK(display_manager);
294   ash::DisplayController* display_controller =
295       ash::Shell::GetInstance()->display_controller();
296   DCHECK(display_controller);
297
298   const gfx::Display target = GetTargetDisplay(display_id_str, display_manager);
299
300   if (target.id() == gfx::Display::kInvalidDisplayID) {
301     *error = "Display not found.";
302     return false;
303   }
304
305   int64 display_id = target.id();
306   // TODO(scottmg): Native is wrong http://crbug.com/133312
307   const gfx::Display& primary =
308       gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
309
310   if (!ValidateParamsForDisplay(
311           info, target, display_manager, primary.id(), error)) {
312     return false;
313   }
314
315   // Process 'isPrimary' parameter.
316   if (info.is_primary && *info.is_primary && target.id() != primary.id())
317     display_controller->SetPrimaryDisplayId(display_id);
318
319   // Process 'mirroringSourceId' parameter.
320   if (info.mirroring_source_id &&
321       info.mirroring_source_id->empty() == display_manager->IsMirrored()) {
322     display_controller->ToggleMirrorMode();
323   }
324
325   // Process 'overscan' parameter.
326   if (info.overscan) {
327     display_manager->SetOverscanInsets(display_id,
328                                        gfx::Insets(info.overscan->top,
329                                                    info.overscan->left,
330                                                    info.overscan->bottom,
331                                                    info.overscan->right));
332   }
333
334   // Process 'rotation' parameter.
335   if (info.rotation) {
336     display_manager->SetDisplayRotation(display_id,
337                                         DegreesToRotation(*info.rotation));
338   }
339
340   // Process new display origin parameters.
341   gfx::Point new_bounds_origin = target.bounds().origin();
342   if (info.bounds_origin_x)
343     new_bounds_origin.set_x(*info.bounds_origin_x);
344   if (info.bounds_origin_y)
345     new_bounds_origin.set_y(*info.bounds_origin_y);
346
347   if (new_bounds_origin != target.bounds().origin()) {
348     gfx::Rect target_bounds = target.bounds();
349     target_bounds.Offset(new_bounds_origin.x() - target.bounds().x(),
350                          new_bounds_origin.y() - target.bounds().y());
351     UpdateDisplayLayout(
352         primary.bounds(), primary.id(), target_bounds, target.id());
353   }
354
355   return true;
356 }
357
358 void DisplayInfoProviderChromeOS::UpdateDisplayUnitInfoForPlatform(
359     const gfx::Display& display,
360     extensions::core_api::system_display::DisplayUnitInfo* unit) {
361   ash::DisplayManager* display_manager =
362       ash::Shell::GetInstance()->display_manager();
363   unit->name = display_manager->GetDisplayNameForId(display.id());
364   if (display_manager->IsMirrored()) {
365     unit->mirroring_source_id =
366         base::Int64ToString(display_manager->mirrored_display_id());
367   }
368
369   // TODO(hshi): determine the DPI of the screen.
370   const float kDpi96 = 96.0;
371
372   const float dpi = display.device_scale_factor() * kDpi96;
373   unit->dpi_x = dpi;
374   unit->dpi_y = dpi;
375
376   const gfx::Insets overscan_insets =
377       display_manager->GetOverscanInsets(display.id());
378   unit->overscan.left = overscan_insets.left();
379   unit->overscan.top = overscan_insets.top();
380   unit->overscan.right = overscan_insets.right();
381   unit->overscan.bottom = overscan_insets.bottom();
382 }
383
384 gfx::Screen* DisplayInfoProviderChromeOS::GetActiveScreen() {
385   return ash::Shell::GetScreen();
386 }
387
388 // static
389 DisplayInfoProvider* DisplayInfoProvider::Create() {
390   return new DisplayInfoProviderChromeOS();
391 }
392
393 }  // namespace extensions