Fix crash when a webview tries to load a plugin resource.
[chromium-blink-merge.git] / ash / utility / partial_screenshot_controller.cc
blobf2406d99abd91fee0bc57536e4ca6839928ed050
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.
5 #include "ash/utility/partial_screenshot_controller.h"
7 #include <cmath>
9 #include "ash/screenshot_delegate.h"
10 #include "ash/shell.h"
11 #include "ash/shell_window_ids.h"
12 #include "base/stl_util.h"
13 #include "ui/compositor/paint_recorder.h"
14 #include "ui/events/event_handler.h"
15 #include "ui/gfx/canvas.h"
16 #include "ui/wm/core/cursor_manager.h"
18 namespace ash {
20 namespace {
22 // The size to increase the invalidated area in the layer to repaint. The area
23 // should be slightly bigger than the actual region because the region indicator
24 // rectangles are drawn outside of the selected region.
25 const int kInvalidateRegionAdditionalSize = 3;
27 } // namespace
29 class PartialScreenshotController::PartialScreenshotLayer
30 : public ui::LayerOwner,
31 public ui::LayerDelegate {
32 public:
33 PartialScreenshotLayer(ui::Layer* parent) {
34 SetLayer(new ui::Layer(ui::LAYER_TEXTURED));
35 layer()->SetFillsBoundsOpaquely(false);
36 layer()->SetBounds(parent->bounds());
37 parent->Add(layer());
38 parent->StackAtTop(layer());
39 layer()->SetVisible(true);
40 layer()->set_delegate(this);
42 ~PartialScreenshotLayer() override {}
44 const gfx::Rect& region() const { return region_; }
46 void SetRegion(const gfx::Rect& region) {
47 // Invalidates the region which covers the current and new region.
48 gfx::Rect union_rect(region_);
49 union_rect.Union(region);
50 union_rect.Inset(-kInvalidateRegionAdditionalSize,
51 -kInvalidateRegionAdditionalSize);
52 union_rect.Intersects(layer()->bounds());
54 region_ = region;
55 layer()->SchedulePaint(union_rect);
58 private:
59 // ui::LayerDelegate:
60 void OnPaintLayer(const ui::PaintContext& context) override {
61 if (region_.IsEmpty())
62 return;
64 // Screenshot area representation: black rectangle with white
65 // rectangle inside. To avoid capturing these rectangles when mouse
66 // release, they should be outside of the actual capturing area.
67 ui::PaintRecorder recorder(context);
68 gfx::Rect rect(region_);
69 rect.Inset(-1, -1);
70 recorder.canvas()->DrawRect(rect, SK_ColorWHITE);
71 rect.Inset(-1, -1);
72 recorder.canvas()->DrawRect(rect, SK_ColorBLACK);
75 void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override {}
77 void OnDeviceScaleFactorChanged(float device_scale_factor) override {}
79 base::Closure PrepareForLayerBoundsChange() override {
80 return base::Closure();
83 gfx::Rect region_;
85 DISALLOW_COPY_AND_ASSIGN(PartialScreenshotLayer);
88 class PartialScreenshotController::ScopedCursorSetter {
89 public:
90 ScopedCursorSetter(::wm::CursorManager* cursor_manager,
91 gfx::NativeCursor cursor)
92 : cursor_manager_(nullptr) {
93 if (cursor_manager->IsCursorLocked())
94 return;
95 gfx::NativeCursor original_cursor = cursor_manager->GetCursor();
96 cursor_manager_ = cursor_manager;
97 cursor_manager_->SetCursor(cursor);
98 cursor_manager_->LockCursor();
99 // SetCursor does not make any effects at this point but it sets back to the
100 // original cursor when unlocked.
101 cursor_manager_->SetCursor(original_cursor);
104 ~ScopedCursorSetter() {
105 if (cursor_manager_)
106 cursor_manager_->UnlockCursor();
109 private:
110 ::wm::CursorManager* cursor_manager_;
112 DISALLOW_COPY_AND_ASSIGN(ScopedCursorSetter);
115 PartialScreenshotController::PartialScreenshotController()
116 : root_window_(nullptr), screenshot_delegate_(nullptr) {
117 Shell* shell = Shell::GetInstance();
118 shell->PrependPreTargetHandler(this);
121 PartialScreenshotController::~PartialScreenshotController() {
122 if (screenshot_delegate_)
123 Cancel();
124 Shell::GetInstance()->RemovePreTargetHandler(this);
127 void PartialScreenshotController::StartPartialScreenshotSession(
128 ScreenshotDelegate* screenshot_delegate) {
129 // Already in a screenshot session.
130 if (screenshot_delegate_) {
131 DCHECK_EQ(screenshot_delegate_, screenshot_delegate);
132 return;
135 screenshot_delegate_ = screenshot_delegate;
136 Shell::GetScreen()->AddObserver(this);
137 for (aura::Window* root : Shell::GetAllRootWindows()) {
138 layers_[root] = new PartialScreenshotLayer(
139 Shell::GetContainer(root, kShellWindowId_OverlayContainer)->layer());
142 cursor_setter_.reset(new ScopedCursorSetter(
143 Shell::GetInstance()->cursor_manager(), ui::kCursorCross));
146 void PartialScreenshotController::MaybeStart(const ui::LocatedEvent& event) {
147 aura::Window* current_root =
148 static_cast<aura::Window*>(event.target())->GetRootWindow();
149 if (root_window_) {
150 // It's already started. This can happen when the second finger touches
151 // the screen, or combination of the touch and mouse. We should grab the
152 // partial screenshot instead of restarting.
153 if (current_root == root_window_) {
154 Update(event);
155 Complete();
157 } else {
158 root_window_ = current_root;
159 start_position_ = event.root_location();
163 void PartialScreenshotController::Complete() {
164 const gfx::Rect& region = layers_[root_window_]->region();
165 if (!region.IsEmpty()) {
166 screenshot_delegate_->HandleTakePartialScreenshot(
167 root_window_, gfx::IntersectRects(root_window_->bounds(), region));
169 Cancel();
172 void PartialScreenshotController::Cancel() {
173 root_window_ = nullptr;
174 screenshot_delegate_ = nullptr;
175 Shell::GetScreen()->RemoveObserver(this);
176 STLDeleteValues(&layers_);
177 cursor_setter_.reset();
180 void PartialScreenshotController::Update(const ui::LocatedEvent& event) {
181 // Update may happen without MaybeStart() if the partial screenshot session
182 // starts when dragging.
183 if (!root_window_)
184 MaybeStart(event);
186 DCHECK(layers_.find(root_window_) != layers_.end());
187 layers_[root_window_]->SetRegion(
188 gfx::Rect(std::min(start_position_.x(), event.root_location().x()),
189 std::min(start_position_.y(), event.root_location().y()),
190 ::abs(start_position_.x() - event.root_location().x()),
191 ::abs(start_position_.y() - event.root_location().y())));
194 void PartialScreenshotController::OnKeyEvent(ui::KeyEvent* event) {
195 if (!screenshot_delegate_)
196 return;
197 if (event->type() == ui::ET_KEY_RELEASED &&
198 event->key_code() == ui::VKEY_ESCAPE) {
199 Cancel();
202 // Intercepts all key events.
203 event->StopPropagation();
206 void PartialScreenshotController::OnMouseEvent(ui::MouseEvent* event) {
207 if (!screenshot_delegate_)
208 return;
209 switch (event->type()) {
210 case ui::ET_MOUSE_PRESSED:
211 MaybeStart(*event);
212 break;
213 case ui::ET_MOUSE_DRAGGED:
214 Update(*event);
215 break;
216 case ui::ET_MOUSE_RELEASED:
217 Complete();
218 break;
219 default:
220 // Do nothing.
221 break;
223 event->StopPropagation();
226 void PartialScreenshotController::OnTouchEvent(ui::TouchEvent* event) {
227 if (!screenshot_delegate_)
228 return;
229 switch (event->type()) {
230 case ui::ET_TOUCH_PRESSED:
231 MaybeStart(*event);
232 break;
233 case ui::ET_TOUCH_MOVED:
234 Update(*event);
235 break;
236 case ui::ET_TOUCH_RELEASED:
237 Complete();
238 break;
239 default:
240 // Do nothing.
241 break;
243 event->StopPropagation();
246 void PartialScreenshotController::OnDisplayAdded(
247 const gfx::Display& new_display) {
248 if (!screenshot_delegate_)
249 return;
250 Cancel();
253 void PartialScreenshotController::OnDisplayRemoved(
254 const gfx::Display& old_display) {
255 if (!screenshot_delegate_)
256 return;
257 Cancel();
260 void PartialScreenshotController::OnDisplayMetricsChanged(
261 const gfx::Display& display,
262 uint32_t changed_metrics) {
265 } // namespace ash