Roll src/third_party/skia 0f881c6:b436ed6
[chromium-blink-merge.git] / ash / magnifier / magnification_controller.cc
blobf43154f1026c7e90e30943086e894b15b6f71ea6
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.
5 #include "ash/magnifier/magnification_controller.h"
7 #include "ash/accelerators/accelerator_controller.h"
8 #include "ash/accessibility_delegate.h"
9 #include "ash/ash_switches.h"
10 #include "ash/display/root_window_transformers.h"
11 #include "ash/host/ash_window_tree_host.h"
12 #include "ash/host/root_window_transformer.h"
13 #include "ash/root_window_controller.h"
14 #include "ash/shell.h"
15 #include "ash/system/tray/system_tray_delegate.h"
16 #include "base/command_line.h"
17 #include "base/synchronization/waitable_event.h"
18 #include "ui/aura/client/aura_constants.h"
19 #include "ui/aura/client/cursor_client.h"
20 #include "ui/aura/window.h"
21 #include "ui/aura/window_tree_host.h"
22 #include "ui/base/ime/input_method.h"
23 #include "ui/base/ime/input_method_observer.h"
24 #include "ui/base/ime/text_input_client.h"
25 #include "ui/compositor/dip_util.h"
26 #include "ui/compositor/layer.h"
27 #include "ui/compositor/layer_animation_observer.h"
28 #include "ui/compositor/scoped_layer_animation_settings.h"
29 #include "ui/events/event.h"
30 #include "ui/events/event_handler.h"
31 #include "ui/gfx/point3_f.h"
32 #include "ui/gfx/point_conversions.h"
33 #include "ui/gfx/point_f.h"
34 #include "ui/gfx/rect_conversions.h"
35 #include "ui/gfx/screen.h"
36 #include "ui/wm/core/compound_event_filter.h"
37 #include "ui/wm/core/coordinate_conversion.h"
39 namespace {
41 const float kMaxMagnifiedScale = 4.0f;
42 const float kMaxMagnifiedScaleThreshold = 4.0f;
43 const float kMinMagnifiedScaleThreshold = 1.1f;
44 const float kNonMagnifiedScale = 1.0f;
46 const float kInitialMagnifiedScale = 2.0f;
47 const float kScrollScaleChangeFactor = 0.05f;
49 // Threadshold of panning. If the cursor moves to within pixels (in DIP) of
50 // |kPanningMergin| from the edge, the view-port moves.
51 const int kPanningMergin = 100;
53 // Gives a little panning margin for following caret, so that we will move the
54 // view-port before the caret is completely out of sight.
55 const int kCaretPanningMargin = 10;
57 void MoveCursorTo(aura::WindowTreeHost* host, const gfx::Point& root_location) {
58 gfx::Point3F host_location_3f(root_location);
59 host->GetRootTransform().TransformPoint(&host_location_3f);
60 host->MoveCursorToHostLocation(
61 gfx::ToCeiledPoint(host_location_3f.AsPointF()));
64 } // namespace
66 namespace ash {
68 ////////////////////////////////////////////////////////////////////////////////
69 // MagnificationControllerImpl:
71 class MagnificationControllerImpl : virtual public MagnificationController,
72 public ui::EventHandler,
73 public ui::ImplicitAnimationObserver,
74 public aura::WindowObserver,
75 public ui::InputMethodObserver {
76 public:
77 MagnificationControllerImpl();
78 ~MagnificationControllerImpl() override;
80 // MagnificationController overrides:
81 void SetEnabled(bool enabled) override;
82 bool IsEnabled() const override;
83 void SetScale(float scale, bool animate) override;
84 float GetScale() const override { return scale_; }
85 void MoveWindow(int x, int y, bool animate) override;
86 void MoveWindow(const gfx::Point& point, bool animate) override;
87 gfx::Point GetWindowPosition() const override {
88 return gfx::ToFlooredPoint(origin_);
90 void SetScrollDirection(ScrollDirection direction) override;
92 // For test
93 gfx::Point GetPointOfInterestForTesting() override {
94 return point_of_interest_;
97 private:
98 // ui::ImplicitAnimationObserver overrides:
99 void OnImplicitAnimationsCompleted() override;
101 // aura::WindowObserver overrides:
102 void OnWindowDestroying(aura::Window* root_window) override;
103 void OnWindowBoundsChanged(aura::Window* window,
104 const gfx::Rect& old_bounds,
105 const gfx::Rect& new_bounds) override;
107 // Redraws the magnification window with the given origin position and the
108 // given scale. Returns true if the window is changed; otherwise, false.
109 // These methods should be called internally just after the scale and/or
110 // the position are changed to redraw the window.
111 bool Redraw(const gfx::PointF& position, float scale, bool animate);
112 bool RedrawDIP(const gfx::PointF& position, float scale, bool animate);
114 // 1) If the screen is scrolling (i.e. animating) and should scroll further,
115 // it does nothing.
116 // 2) If the screen is scrolling (i.e. animating) and the direction is NONE,
117 // it stops the scrolling animation.
118 // 3) If the direction is set to value other than NONE, it starts the
119 // scrolling/ animation towards that direction.
120 void StartOrStopScrollIfNecessary();
122 // Redraw with the given zoom scale keeping the mouse cursor location. In
123 // other words, zoom (or unzoom) centering around the cursor.
124 void RedrawKeepingMousePosition(float scale, bool animate);
126 void OnMouseMove(const gfx::Point& location);
128 // Move the mouse cursot to the given point. Actual move will be done when
129 // the animation is completed. This should be called after animation is
130 // started.
131 void AfterAnimationMoveCursorTo(const gfx::Point& location);
133 // Switch Magnified RootWindow to |new_root_window|. This does following:
134 // - Unzoom the current root_window.
135 // - Zoom the given new root_window |new_root_window|.
136 // - Switch the target window from current window to |new_root_window|.
137 void SwitchTargetRootWindow(aura::Window* new_root_window,
138 bool redraw_original_root_window);
140 // Returns if the magnification scale is 1.0 or not (larger then 1.0).
141 bool IsMagnified() const;
143 // Returns the rect of the magnification window.
144 gfx::RectF GetWindowRectDIP(float scale) const;
145 // Returns the size of the root window.
146 gfx::Size GetHostSizeDIP() const;
148 // Correct the givin scale value if nessesary.
149 void ValidateScale(float* scale);
151 // ui::EventHandler overrides:
152 void OnMouseEvent(ui::MouseEvent* event) override;
153 void OnScrollEvent(ui::ScrollEvent* event) override;
154 void OnTouchEvent(ui::TouchEvent* event) override;
156 // Moves the view port when |point| is located within
157 // |x_panning_margin| and |y_pannin_margin| to the edge of the visible
158 // window region. The view port will be moved so that the |point| will be
159 // moved to the point where it has |x_target_margin| and |y_target_margin|
160 // to the edge of the visible region.
161 void MoveMagnifierWindow(const gfx::Point& point,
162 int x_panning_margin,
163 int y_panning_margin,
164 int x_target_margin,
165 int y_target_margin);
167 // ui::InputMethodObserver:
168 void OnTextInputTypeChanged(const ui::TextInputClient* client) override {}
169 void OnFocus() override {}
170 void OnBlur() override {}
171 void OnTextInputStateChanged(const ui::TextInputClient* client) override {}
172 void OnInputMethodDestroyed(const ui::InputMethod* input_method) override {}
173 void OnShowImeIfNeeded() override {}
174 void OnCaretBoundsChanged(const ui::TextInputClient* client) override;
176 // Target root window. This must not be NULL.
177 aura::Window* root_window_;
179 // True if the magnified window is currently animating a change. Otherwise,
180 // false.
181 bool is_on_animation_;
183 bool is_enabled_;
185 // True if the cursor needs to move the given position after the animation
186 // will be finished. When using this, set |position_after_animation_| as well.
187 bool move_cursor_after_animation_;
188 // Stores the position of cursor to be moved after animation.
189 gfx::Point position_after_animation_;
191 // Stores the last mouse cursor (or last touched) location. This value is
192 // used on zooming to keep this location visible.
193 gfx::Point point_of_interest_;
195 // Current scale, origin (left-top) position of the magnification window.
196 float scale_;
197 gfx::PointF origin_;
199 ScrollDirection scroll_direction_;
201 ui::InputMethod* input_method_; // Not owned.
203 DISALLOW_COPY_AND_ASSIGN(MagnificationControllerImpl);
206 ////////////////////////////////////////////////////////////////////////////////
207 // MagnificationControllerImpl:
209 MagnificationControllerImpl::MagnificationControllerImpl()
210 : root_window_(Shell::GetPrimaryRootWindow()),
211 is_on_animation_(false),
212 is_enabled_(false),
213 move_cursor_after_animation_(false),
214 scale_(kNonMagnifiedScale),
215 scroll_direction_(SCROLL_NONE),
216 input_method_(NULL) {
217 Shell::GetInstance()->AddPreTargetHandler(this);
218 root_window_->AddObserver(this);
219 point_of_interest_ = root_window_->bounds().CenterPoint();
222 MagnificationControllerImpl::~MagnificationControllerImpl() {
223 if (input_method_)
224 input_method_->RemoveObserver(this);
226 root_window_->RemoveObserver(this);
228 Shell::GetInstance()->RemovePreTargetHandler(this);
231 void MagnificationControllerImpl::RedrawKeepingMousePosition(
232 float scale, bool animate) {
233 gfx::Point mouse_in_root = point_of_interest_;
235 // mouse_in_root is invalid value when the cursor is hidden.
236 if (!root_window_->bounds().Contains(mouse_in_root))
237 mouse_in_root = root_window_->bounds().CenterPoint();
239 const gfx::PointF origin =
240 gfx::PointF(mouse_in_root.x() -
241 (scale_ / scale) * (mouse_in_root.x() - origin_.x()),
242 mouse_in_root.y() -
243 (scale_ / scale) * (mouse_in_root.y() - origin_.y()));
244 bool changed = RedrawDIP(origin, scale, animate);
245 if (changed)
246 AfterAnimationMoveCursorTo(mouse_in_root);
249 bool MagnificationControllerImpl::Redraw(const gfx::PointF& position,
250 float scale,
251 bool animate) {
252 const gfx::PointF position_in_dip =
253 ui::ConvertPointToDIP(root_window_->layer(), position);
254 return RedrawDIP(position_in_dip, scale, animate);
257 bool MagnificationControllerImpl::RedrawDIP(const gfx::PointF& position_in_dip,
258 float scale,
259 bool animate) {
260 DCHECK(root_window_);
262 float x = position_in_dip.x();
263 float y = position_in_dip.y();
265 ValidateScale(&scale);
267 if (x < 0)
268 x = 0;
269 if (y < 0)
270 y = 0;
272 const gfx::Size host_size_in_dip = GetHostSizeDIP();
273 const gfx::SizeF window_size_in_dip = GetWindowRectDIP(scale).size();
274 float max_x = host_size_in_dip.width() - window_size_in_dip.width();
275 float max_y = host_size_in_dip.height() - window_size_in_dip.height();
276 if (x > max_x)
277 x = max_x;
278 if (y > max_y)
279 y = max_y;
281 // Does nothing if both the origin and the scale are not changed.
282 if (origin_.x() == x &&
283 origin_.y() == y &&
284 scale == scale_) {
285 return false;
288 origin_.set_x(x);
289 origin_.set_y(y);
290 scale_ = scale;
292 // Creates transform matrix.
293 gfx::Transform transform;
294 // Flips the signs intentionally to convert them from the position of the
295 // magnification window.
296 transform.Scale(scale_, scale_);
297 transform.Translate(-origin_.x(), -origin_.y());
299 ui::ScopedLayerAnimationSettings settings(
300 root_window_->layer()->GetAnimator());
301 settings.AddObserver(this);
302 settings.SetPreemptionStrategy(
303 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
304 settings.SetTweenType(gfx::Tween::EASE_OUT);
305 settings.SetTransitionDuration(
306 base::TimeDelta::FromMilliseconds(animate ? 100 : 0));
308 gfx::Display display =
309 Shell::GetScreen()->GetDisplayNearestWindow(root_window_);
310 scoped_ptr<RootWindowTransformer> transformer(
311 CreateRootWindowTransformerForDisplay(root_window_, display));
312 GetRootWindowController(root_window_)->ash_host()->SetRootWindowTransformer(
313 transformer.Pass());
315 if (animate)
316 is_on_animation_ = true;
318 return true;
321 void MagnificationControllerImpl::StartOrStopScrollIfNecessary() {
322 // This value controls the scrolling speed.
323 const int kMoveOffset = 40;
324 if (is_on_animation_) {
325 if (scroll_direction_ == SCROLL_NONE)
326 root_window_->layer()->GetAnimator()->StopAnimating();
327 return;
330 gfx::PointF new_origin = origin_;
331 switch (scroll_direction_) {
332 case SCROLL_NONE:
333 // No need to take action.
334 return;
335 case SCROLL_LEFT:
336 new_origin.Offset(-kMoveOffset, 0);
337 break;
338 case SCROLL_RIGHT:
339 new_origin.Offset(kMoveOffset, 0);
340 break;
341 case SCROLL_UP:
342 new_origin.Offset(0, -kMoveOffset);
343 break;
344 case SCROLL_DOWN:
345 new_origin.Offset(0, kMoveOffset);
346 break;
348 RedrawDIP(new_origin, scale_, true);
351 void MagnificationControllerImpl::OnMouseMove(const gfx::Point& location) {
352 DCHECK(root_window_);
354 gfx::Point mouse(location);
355 int margin = kPanningMergin / scale_; // No need to consider DPI.
356 MoveMagnifierWindow(mouse, margin, margin, margin, margin);
359 void MagnificationControllerImpl::AfterAnimationMoveCursorTo(
360 const gfx::Point& location) {
361 DCHECK(root_window_);
363 aura::client::CursorClient* cursor_client =
364 aura::client::GetCursorClient(root_window_);
365 if (cursor_client) {
366 // When cursor is invisible, do not move or show the cursor after the
367 // animation.
368 if (!cursor_client->IsCursorVisible())
369 return;
370 cursor_client->DisableMouseEvents();
372 move_cursor_after_animation_ = true;
373 position_after_animation_ = location;
376 gfx::Size MagnificationControllerImpl::GetHostSizeDIP() const {
377 return root_window_->bounds().size();
380 gfx::RectF MagnificationControllerImpl::GetWindowRectDIP(float scale) const {
381 const gfx::Size size_in_dip = root_window_->bounds().size();
382 const float width = size_in_dip.width() / scale;
383 const float height = size_in_dip.height() / scale;
385 return gfx::RectF(origin_.x(), origin_.y(), width, height);
388 bool MagnificationControllerImpl::IsMagnified() const {
389 return scale_ >= kMinMagnifiedScaleThreshold;
392 void MagnificationControllerImpl::ValidateScale(float* scale) {
393 // Adjust the scale to just |kNonMagnifiedScale| if scale is smaller than
394 // |kMinMagnifiedScaleThreshold|;
395 if (*scale < kMinMagnifiedScaleThreshold)
396 *scale = kNonMagnifiedScale;
398 // Adjust the scale to just |kMinMagnifiedScale| if scale is bigger than
399 // |kMinMagnifiedScaleThreshold|;
400 if (*scale > kMaxMagnifiedScaleThreshold)
401 *scale = kMaxMagnifiedScale;
403 DCHECK(kNonMagnifiedScale <= *scale && *scale <= kMaxMagnifiedScale);
406 void MagnificationControllerImpl::OnImplicitAnimationsCompleted() {
407 if (!is_on_animation_)
408 return;
410 if (move_cursor_after_animation_) {
411 MoveCursorTo(root_window_->GetHost(), position_after_animation_);
412 move_cursor_after_animation_ = false;
414 aura::client::CursorClient* cursor_client =
415 aura::client::GetCursorClient(root_window_);
416 if (cursor_client)
417 cursor_client->EnableMouseEvents();
420 is_on_animation_ = false;
422 StartOrStopScrollIfNecessary();
425 void MagnificationControllerImpl::OnWindowDestroying(
426 aura::Window* root_window) {
427 if (root_window == root_window_) {
428 // There must be at least one root window because this controller is
429 // destroyed before the root windows get destroyed.
430 DCHECK(root_window);
432 aura::Window* target_root_window = Shell::GetTargetRootWindow();
433 CHECK(target_root_window);
435 // The destroyed root window must not be target.
436 CHECK_NE(target_root_window, root_window);
437 // Don't redraw the old root window as it's being destroyed.
438 SwitchTargetRootWindow(target_root_window, false);
439 point_of_interest_ = target_root_window->bounds().CenterPoint();
443 void MagnificationControllerImpl::OnWindowBoundsChanged(
444 aura::Window* window,
445 const gfx::Rect& old_bounds,
446 const gfx::Rect& new_bounds) {
447 // TODO(yoshiki): implement here. crbug.com/230979
450 void MagnificationControllerImpl::SwitchTargetRootWindow(
451 aura::Window* new_root_window,
452 bool redraw_original_root_window) {
453 DCHECK(new_root_window);
455 if (new_root_window == root_window_)
456 return;
458 // Stores the previous scale.
459 float scale = GetScale();
461 // Unmagnify the previous root window.
462 root_window_->RemoveObserver(this);
463 if (redraw_original_root_window)
464 RedrawKeepingMousePosition(1.0f, true);
466 root_window_ = new_root_window;
467 RedrawKeepingMousePosition(scale, true);
468 root_window_->AddObserver(this);
471 ////////////////////////////////////////////////////////////////////////////////
472 // MagnificationControllerImpl: MagnificationController implementation
474 void MagnificationControllerImpl::SetScale(float scale, bool animate) {
475 if (!is_enabled_)
476 return;
478 ValidateScale(&scale);
479 Shell::GetInstance()->accessibility_delegate()->
480 SaveScreenMagnifierScale(scale);
481 RedrawKeepingMousePosition(scale, animate);
484 void MagnificationControllerImpl::MoveWindow(int x, int y, bool animate) {
485 if (!is_enabled_)
486 return;
488 Redraw(gfx::Point(x, y), scale_, animate);
491 void MagnificationControllerImpl::MoveWindow(const gfx::Point& point,
492 bool animate) {
493 if (!is_enabled_)
494 return;
496 Redraw(point, scale_, animate);
499 void MagnificationControllerImpl::SetScrollDirection(
500 ScrollDirection direction) {
501 scroll_direction_ = direction;
502 StartOrStopScrollIfNecessary();
505 void MagnificationControllerImpl::SetEnabled(bool enabled) {
506 Shell* shell = Shell::GetInstance();
507 if (enabled) {
508 if (!input_method_) {
509 input_method_ =
510 root_window_->GetProperty(aura::client::kRootWindowInputMethodKey);
511 if (input_method_)
512 input_method_->AddObserver(this);
515 float scale =
516 Shell::GetInstance()->accessibility_delegate()->
517 GetSavedScreenMagnifierScale();
518 if (scale <= 0.0f)
519 scale = kInitialMagnifiedScale;
520 ValidateScale(&scale);
522 // Do nothing, if already enabled with same scale.
523 if (is_enabled_ && scale == scale_)
524 return;
526 is_enabled_ = enabled;
527 RedrawKeepingMousePosition(scale, true);
528 shell->accessibility_delegate()->SaveScreenMagnifierScale(scale);
529 } else {
530 // Do nothing, if already disabled.
531 if (!is_enabled_)
532 return;
534 if (input_method_) {
535 input_method_->RemoveObserver(this);
536 input_method_ = NULL;
539 RedrawKeepingMousePosition(kNonMagnifiedScale, true);
540 is_enabled_ = enabled;
544 bool MagnificationControllerImpl::IsEnabled() const {
545 return is_enabled_;
548 ////////////////////////////////////////////////////////////////////////////////
549 // MagnificationControllerImpl: aura::EventFilter implementation
551 void MagnificationControllerImpl::OnMouseEvent(ui::MouseEvent* event) {
552 aura::Window* target = static_cast<aura::Window*>(event->target());
553 aura::Window* current_root = target->GetRootWindow();
554 gfx::Rect root_bounds = current_root->bounds();
556 if (root_bounds.Contains(event->root_location())) {
557 // This must be before |SwitchTargetRootWindow()|.
558 if (event->type() != ui::ET_MOUSE_CAPTURE_CHANGED)
559 point_of_interest_ = event->root_location();
561 if (current_root != root_window_) {
562 DCHECK(current_root);
563 SwitchTargetRootWindow(current_root, true);
566 if (IsMagnified() && event->type() == ui::ET_MOUSE_MOVED)
567 OnMouseMove(event->root_location());
571 void MagnificationControllerImpl::OnScrollEvent(ui::ScrollEvent* event) {
572 if (event->IsAltDown() && event->IsControlDown()) {
573 if (event->type() == ui::ET_SCROLL_FLING_START ||
574 event->type() == ui::ET_SCROLL_FLING_CANCEL) {
575 event->StopPropagation();
576 return;
579 if (event->type() == ui::ET_SCROLL) {
580 ui::ScrollEvent* scroll_event = static_cast<ui::ScrollEvent*>(event);
581 float scale = GetScale();
582 scale += scroll_event->y_offset() * kScrollScaleChangeFactor;
583 SetScale(scale, true);
584 event->StopPropagation();
585 return;
590 void MagnificationControllerImpl::OnTouchEvent(ui::TouchEvent* event) {
591 aura::Window* target = static_cast<aura::Window*>(event->target());
592 aura::Window* current_root = target->GetRootWindow();
593 if (current_root == root_window_) {
594 gfx::Rect root_bounds = current_root->bounds();
595 if (root_bounds.Contains(event->root_location()))
596 point_of_interest_ = event->root_location();
600 void MagnificationControllerImpl::MoveMagnifierWindow(const gfx::Point& point,
601 int x_panning_margin,
602 int y_panning_margin,
603 int x_target_margin,
604 int y_target_margin) {
605 DCHECK(root_window_);
606 int x = origin_.x();
607 int y = origin_.y();
608 bool start_zoom = false;
610 const gfx::Rect window_rect = gfx::ToEnclosingRect(GetWindowRectDIP(scale_));
611 const int left = window_rect.x();
612 const int right = window_rect.right();
614 int x_diff = 0;
615 if (point.x() < left + x_panning_margin) {
616 // Panning left.
617 x_diff = point.x() - (left + x_target_margin);
618 start_zoom = true;
619 } else if (right - x_panning_margin < point.x()) {
620 // Panning right.
621 x_diff = point.x() - (right - x_target_margin);
622 start_zoom = true;
624 x = left + x_diff;
626 const int top = window_rect.y();
627 const int bottom = window_rect.bottom();
629 int y_diff = 0;
630 if (point.y() < top + y_panning_margin) {
631 // Panning up.
632 y_diff = point.y() - (top + y_target_margin);
633 start_zoom = true;
634 } else if (bottom - y_panning_margin < point.y()) {
635 // Panning down.
636 y_diff = point.y() - (bottom - y_target_margin);
637 start_zoom = true;
639 y = top + y_diff;
640 if (start_zoom && !is_on_animation_) {
641 // No animation on panning.
642 bool animate = false;
643 bool ret = RedrawDIP(gfx::Point(x, y), scale_, animate);
645 if (ret) {
646 // If the magnified region is moved, hides the mouse cursor and moves it.
647 if (x_diff != 0 || y_diff != 0)
648 MoveCursorTo(root_window_->GetHost(), point);
653 void MagnificationControllerImpl::OnCaretBoundsChanged(
654 const ui::TextInputClient* client) {
655 // caret bounds in screen coordinates.
656 const gfx::Rect caret_bounds = client->GetCaretBounds();
657 // Note: OnCaretBoundsChanged could be fired OnTextInputTypeChanged during
658 // which the caret position is not set a meaning position, and we do not
659 // need to adjust the view port position based on the bogus caret position.
660 // This is only a transition period, the caret position will be fixed upon
661 // focusing right after.
662 if (caret_bounds.width() == 0 && caret_bounds.height() == 0)
663 return;
665 gfx::Point caret_origin = caret_bounds.origin();
666 // caret_origin in |root_window_| coordinates.
667 wm::ConvertPointFromScreen(root_window_, &caret_origin);
669 // Visible window_rect in |root_window_| coordinates.
670 const gfx::Rect visible_window_rect =
671 gfx::ToEnclosingRect(GetWindowRectDIP(scale_));
673 const int panning_margin = kCaretPanningMargin / scale_;
674 MoveMagnifierWindow(caret_origin,
675 panning_margin,
676 panning_margin,
677 visible_window_rect.width() / 2,
678 visible_window_rect.height() / 2);
681 ////////////////////////////////////////////////////////////////////////////////
682 // MagnificationController:
684 // static
685 MagnificationController* MagnificationController::CreateInstance() {
686 return new MagnificationControllerImpl();
689 } // namespace ash