OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/renderer/render_widget.h" | 5 #include "chrome/renderer/render_widget.h" |
6 | 6 |
7 #include "base/gfx/point.h" | 7 #include "base/gfx/point.h" |
8 #include "base/gfx/size.h" | 8 #include "base/gfx/size.h" |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/message_loop.h" | 10 #include "base/message_loop.h" |
(...skipping 14 matching lines...) Expand all Loading... |
25 #endif // defined(OS_POSIX) | 25 #endif // defined(OS_POSIX) |
26 | 26 |
27 #include "webkit/glue/webtextdirection.h" | 27 #include "webkit/glue/webtextdirection.h" |
28 #include "webkit/glue/webwidget.h" | 28 #include "webkit/glue/webwidget.h" |
29 | 29 |
30 using WebKit::WebInputEvent; | 30 using WebKit::WebInputEvent; |
31 using WebKit::WebRect; | 31 using WebKit::WebRect; |
32 using WebKit::WebScreenInfo; | 32 using WebKit::WebScreenInfo; |
33 using WebKit::WebSize; | 33 using WebKit::WebSize; |
34 | 34 |
| 35 namespace { |
| 36 |
| 37 // A helper class to manage access to the RenderWidget::paint_rects vector. |
| 38 class PaintRegion { |
| 39 public: |
| 40 explicit PaintRegion(std::vector<gfx::Rect>* rects) |
| 41 : rects_(*rects), |
| 42 coalesce_(false) { |
| 43 } |
| 44 |
| 45 gfx::Rect GetBoundingRect() const { |
| 46 gfx::Rect bounding_rect; |
| 47 for (size_t i = 0; i < rects_.size(); ++i) |
| 48 bounding_rect = bounding_rect.Union(rects_[i]); |
| 49 return bounding_rect; |
| 50 } |
| 51 |
| 52 void Add(gfx::Rect rect) { |
| 53 if (rect.IsEmpty()) |
| 54 return; |
| 55 |
| 56 if (rects_.size() >= kNumRectsThreshold) { |
| 57 coalesce_ = true; |
| 58 for (size_t i = rects_.size() - 1; i > 0; --i) { |
| 59 rects_[0] = rects_[0].Union(rects_[i]); |
| 60 rects_.pop_back(); |
| 61 } |
| 62 } |
| 63 |
| 64 if (coalesce_) { |
| 65 DCHECK(rects_.size() == 1); |
| 66 rects_[0] = rects_[0].Union(rect); |
| 67 } else { |
| 68 // Look for all rects that would intersect with this new |
| 69 // rect, union them and remove them from the list so that we |
| 70 // have only one rect for any pixel in the invalidation. |
| 71 std::vector<gfx::Rect>::iterator it = rects_.begin(); |
| 72 while (it != rects_.end()) { |
| 73 if (rect.Intersects(*it)) { |
| 74 rect = rect.Union(*it); |
| 75 rects_.erase(it); |
| 76 // We must go back to the beginning since some rects we previously |
| 77 // checked might now intersects with the union of the two rects, |
| 78 // even though they didn't intersect with them individually. |
| 79 it = rects_.begin(); |
| 80 } else { |
| 81 ++it; |
| 82 } |
| 83 } |
| 84 rects_.push_back(rect); |
| 85 } |
| 86 } |
| 87 |
| 88 void Clip(const gfx::Rect& clip_rect) { |
| 89 std::vector<gfx::Rect>::iterator it = rects_.begin(); |
| 90 while (it != rects_.end()) { |
| 91 if (!clip_rect.Contains(*it)) |
| 92 *it = clip_rect.Intersect(*it); |
| 93 ++it; |
| 94 } |
| 95 } |
| 96 |
| 97 bool Intersects(const gfx::Rect& rect) const { |
| 98 for (size_t i = 0; i < rects_.size(); ++i) { |
| 99 if (rects_[i].Intersects(rect)) |
| 100 return true; |
| 101 } |
| 102 return false; |
| 103 } |
| 104 |
| 105 private: |
| 106 std::vector<gfx::Rect>& rects_; |
| 107 bool coalesce_; |
| 108 static const size_t kNumRectsThreshold; |
| 109 }; |
| 110 // We use a threshold for the number of rects because in many cases as |
| 111 // described here: |
| 112 // http://my.opera.com/desktopteam/blog/2008/03/28/painting-performance-fixes |
| 113 // we get diminishing returns from using more sub-rectangles, it becomes more |
| 114 // efficient to Union the rects. The nubmer 25 is also used in WebKit for |
| 115 // deferred paints in the FrameView. It was chosen here based on some |
| 116 // manual experiments using the benchmark from the article sited above. |
| 117 const size_t PaintRegion::kNumRectsThreshold = 25; |
| 118 |
| 119 } // namespace |
| 120 |
35 RenderWidget::RenderWidget(RenderThreadBase* render_thread, bool activatable) | 121 RenderWidget::RenderWidget(RenderThreadBase* render_thread, bool activatable) |
36 : routing_id_(MSG_ROUTING_NONE), | 122 : routing_id_(MSG_ROUTING_NONE), |
37 webwidget_(NULL), | 123 webwidget_(NULL), |
38 opener_id_(MSG_ROUTING_NONE), | 124 opener_id_(MSG_ROUTING_NONE), |
39 render_thread_(render_thread), | 125 render_thread_(render_thread), |
40 host_window_(NULL), | 126 host_window_(NULL), |
41 current_paint_buf_(NULL), | 127 current_paint_buf_(NULL), |
42 current_scroll_buf_(NULL), | 128 current_scroll_buf_(NULL), |
43 next_paint_flags_(0), | 129 next_paint_flags_(0), |
44 paint_reply_pending_(false), | 130 paint_reply_pending_(false), |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
193 size_ = new_size; | 279 size_ = new_size; |
194 | 280 |
195 // We should not be sent a Resize message if we have not ACK'd the previous | 281 // We should not be sent a Resize message if we have not ACK'd the previous |
196 DCHECK(!next_paint_is_resize_ack()); | 282 DCHECK(!next_paint_is_resize_ack()); |
197 | 283 |
198 // When resizing, we want to wait to paint before ACK'ing the resize. This | 284 // When resizing, we want to wait to paint before ACK'ing the resize. This |
199 // ensures that we only resize as fast as we can paint. We only need to send | 285 // ensures that we only resize as fast as we can paint. We only need to send |
200 // an ACK if we are resized to a non-empty rect. | 286 // an ACK if we are resized to a non-empty rect. |
201 webwidget_->Resize(new_size); | 287 webwidget_->Resize(new_size); |
202 if (!new_size.IsEmpty()) { | 288 if (!new_size.IsEmpty()) { |
203 DCHECK(!paint_rect_.IsEmpty()); | 289 DCHECK(!paint_rects_.empty()); |
204 | 290 |
205 // This should have caused an invalidation of the entire view. The damaged | 291 // This should have caused an invalidation of the entire view. The damaged |
206 // rect could be larger than new_size if we are being made smaller. | 292 // rect could be larger than new_size if we are being made smaller. |
207 DCHECK_GE(paint_rect_.width(), new_size.width()); | 293 DCHECK_GE(PaintRegion(&paint_rects_).GetBoundingRect().width(), |
208 DCHECK_GE(paint_rect_.height(), new_size.height()); | 294 new_size.width()); |
209 | 295 DCHECK_GE(PaintRegion(&paint_rects_).GetBoundingRect().height(), |
| 296 new_size.height()); |
210 // We will send the Resize_ACK flag once we paint again. | 297 // We will send the Resize_ACK flag once we paint again. |
211 set_next_paint_is_resize_ack(); | 298 set_next_paint_is_resize_ack(); |
212 } | 299 } |
213 } | 300 } |
214 | 301 |
215 void RenderWidget::OnWasHidden() { | 302 void RenderWidget::OnWasHidden() { |
216 // Go into a mode where we stop generating paint and scrolling events. | 303 // Go into a mode where we stop generating paint and scrolling events. |
217 is_hidden_ = true; | 304 is_hidden_ = true; |
218 } | 305 } |
219 | 306 |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
307 } | 394 } |
308 } | 395 } |
309 | 396 |
310 void RenderWidget::ClearFocus() { | 397 void RenderWidget::ClearFocus() { |
311 // We may have got the focus from the browser before this gets processed, in | 398 // We may have got the focus from the browser before this gets processed, in |
312 // which case we do not want to unfocus ourself. | 399 // which case we do not want to unfocus ourself. |
313 if (!has_focus_ && webwidget_) | 400 if (!has_focus_ && webwidget_) |
314 webwidget_->SetFocus(false); | 401 webwidget_->SetFocus(false); |
315 } | 402 } |
316 | 403 |
317 void RenderWidget::PaintRect(const gfx::Rect& rect, | 404 void RenderWidget::PaintThisRect(const gfx::Rect& rect, |
318 skia::PlatformCanvas* canvas) { | 405 skia::PlatformCanvas* canvas) { |
319 | 406 // Make sure we don't erase previous rects in the canvas |
320 // Bring the canvas into the coordinate system of the paint rect. | 407 SkRect clip_rect; |
321 canvas->translate(static_cast<SkScalar>(-rect.x()), | 408 clip_rect.iset(rect.x(), rect.y(), rect.right(), rect.bottom()); |
322 static_cast<SkScalar>(-rect.y())); | 409 canvas->clipRect(clip_rect, SkRegion::kReplace_Op); |
323 | 410 |
324 // If there is a custom background, tile it. | 411 // If there is a custom background, tile it. |
325 if (!background_.empty()) { | 412 if (!background_.empty()) { |
326 SkPaint paint; | 413 SkPaint paint; |
327 SkShader* shader = SkShader::CreateBitmapShader(background_, | 414 SkShader* shader = SkShader::CreateBitmapShader(background_, |
328 SkShader::kRepeat_TileMode, | 415 SkShader::kRepeat_TileMode, |
329 SkShader::kRepeat_TileMode); | 416 SkShader::kRepeat_TileMode); |
330 paint.setShader(shader)->unref(); | 417 paint.setShader(shader)->unref(); |
331 paint.setPorterDuffXfermode(SkPorterDuff::kSrcOver_Mode); | 418 paint.setPorterDuffXfermode(SkPorterDuff::kSrcOver_Mode); |
332 canvas->drawPaint(paint); | 419 canvas->drawPaint(paint); |
333 } | 420 } |
334 | 421 |
335 webwidget_->Paint(canvas, rect); | 422 webwidget_->Paint(canvas, rect); |
| 423 } |
| 424 |
| 425 void RenderWidget::PaintRect(const gfx::Rect& rect, |
| 426 skia::PlatformCanvas* canvas) { |
| 427 // Bring the canvas into the coordinate system of the paint rect. |
| 428 canvas->translate(static_cast<SkScalar>(-rect.x()), |
| 429 static_cast<SkScalar>(-rect.y())); |
| 430 PaintThisRect(rect, canvas); |
336 | 431 |
337 // Flush to underlying bitmap. TODO(darin): is this needed? | 432 // Flush to underlying bitmap. TODO(darin): is this needed? |
338 canvas->getTopPlatformDevice().accessBitmap(false); | 433 canvas->getTopPlatformDevice().accessBitmap(false); |
339 | 434 |
340 // Let the subclass observe this paint operations. | 435 // Let the subclass observe this paint operation. |
| 436 DidPaint(); |
| 437 } |
| 438 |
| 439 void RenderWidget::PaintRects(const std::vector<gfx::Rect>& rects, |
| 440 skia::PlatformCanvas* canvas) { |
| 441 for (size_t i = 0; i < rects.size(); ++i) |
| 442 PaintThisRect(rects[i], canvas); |
| 443 |
| 444 // Flush to underlying bitmap. TODO(darin): is this needed? |
| 445 canvas->getTopPlatformDevice().accessBitmap(false); |
| 446 |
| 447 // Let the subclass observe these paint operations. |
341 DidPaint(); | 448 DidPaint(); |
342 } | 449 } |
343 | 450 |
344 void RenderWidget::DoDeferredPaint() { | 451 void RenderWidget::DoDeferredPaint() { |
345 if (!webwidget_ || paint_reply_pending() || paint_rect_.IsEmpty()) | 452 if (!webwidget_ || paint_reply_pending() || paint_rects_.empty()) |
346 return; | 453 return; |
347 | 454 |
348 // When we are hidden, we want to suppress painting, but we still need to | 455 // When we are hidden, we want to suppress painting, but we still need to |
349 // mark this DoDeferredPaint as complete. | 456 // mark this DoDeferredPaint as complete. |
350 if (is_hidden_ || size_.IsEmpty()) { | 457 if (is_hidden_ || size_.IsEmpty()) { |
351 paint_rect_ = gfx::Rect(); | 458 paint_rects_.clear(); |
352 needs_repainting_on_restore_ = true; | 459 needs_repainting_on_restore_ = true; |
353 return; | 460 return; |
354 } | 461 } |
355 | 462 |
356 // Layout may generate more invalidation... | 463 // Layout may generate more invalidation... |
357 webwidget_->Layout(); | 464 webwidget_->Layout(); |
358 | 465 |
359 // OK, save the current paint_rect to a local since painting may cause more | 466 // OK, save the current paint_rect to a local since painting may cause more |
360 // invalidation. Some WebCore rendering objects only layout when painted. | 467 // invalidation. Some WebCore rendering objects only layout when painted. |
361 gfx::Rect damaged_rect = paint_rect_; | 468 std::vector<gfx::Rect> paint_rects = paint_rects_; |
362 paint_rect_ = gfx::Rect(); | 469 paint_rects_.clear(); |
363 | 470 |
364 // Compute a buffer for painting and cache it. | 471 // Compute a buffer for painting and cache it. |
| 472 DCHECK(!current_paint_buf_); |
| 473 |
| 474 // we use the whole view size as opposed to damaged rect size we have a pool |
| 475 // of paint buffers anyway, and this size has surely been used at least once |
| 476 // to paint the whole view... And it also prevents, somehow, an intermittent |
| 477 // bug we get when painting the sub-rectangles with StretchDIBits on Windows. |
| 478 gfx::Rect bitmap_rect = gfx::Rect(gfx::Point(0, 0), size_); |
365 skia::PlatformCanvas* canvas = | 479 skia::PlatformCanvas* canvas = |
366 RenderProcess::current()->GetDrawingCanvas(¤t_paint_buf_, | 480 RenderProcess::current()->GetDrawingCanvas(¤t_paint_buf_, |
367 damaged_rect); | 481 bitmap_rect); |
368 if (!canvas) { | 482 if (!canvas) { |
369 NOTREACHED(); | 483 NOTREACHED(); |
370 return; | 484 return; |
371 } | 485 } |
372 | 486 |
373 PaintRect(damaged_rect, canvas); | 487 // We must make sure all our paint rects are within the bitmap bounds. |
| 488 PaintRegion(&paint_rects).Clip(bitmap_rect); |
| 489 PaintRects(paint_rects, canvas); |
374 | 490 |
375 ViewHostMsg_PaintRect_Params params; | 491 ViewHostMsg_PaintRect_Params params; |
376 params.bitmap_rect = damaged_rect; | 492 params.bitmap = current_paint_buf_->id(); |
| 493 params.bitmap_rect = bitmap_rect; |
| 494 params.paint_rects = paint_rects; |
377 params.view_size = size_; | 495 params.view_size = size_; |
378 params.plugin_window_moves = plugin_window_moves_; | 496 params.plugin_window_moves = plugin_window_moves_; |
379 params.flags = next_paint_flags_; | 497 params.flags = next_paint_flags_; |
380 params.bitmap = current_paint_buf_->id(); | |
381 | 498 |
382 delete canvas; | 499 delete canvas; |
383 | 500 |
384 plugin_window_moves_.clear(); | 501 plugin_window_moves_.clear(); |
385 | 502 |
386 paint_reply_pending_ = true; | 503 paint_reply_pending_ = true; |
387 Send(new ViewHostMsg_PaintRect(routing_id_, params)); | 504 Send(new ViewHostMsg_PaintRect(routing_id_, params)); |
388 next_paint_flags_ = 0; | 505 next_paint_flags_ = 0; |
389 | 506 |
390 UpdateIME(); | 507 UpdateIME(); |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
473 /////////////////////////////////////////////////////////////////////////////// | 590 /////////////////////////////////////////////////////////////////////////////// |
474 // WebWidgetDelegate | 591 // WebWidgetDelegate |
475 | 592 |
476 gfx::NativeViewId RenderWidget::GetContainingView(WebWidget* webwidget) { | 593 gfx::NativeViewId RenderWidget::GetContainingView(WebWidget* webwidget) { |
477 return host_window_; | 594 return host_window_; |
478 } | 595 } |
479 | 596 |
480 void RenderWidget::DidInvalidateRect(WebWidget* webwidget, | 597 void RenderWidget::DidInvalidateRect(WebWidget* webwidget, |
481 const WebRect& rect) { | 598 const WebRect& rect) { |
482 // We only want one pending DoDeferredPaint call at any time... | 599 // We only want one pending DoDeferredPaint call at any time... |
483 bool paint_pending = !paint_rect_.IsEmpty(); | 600 bool paint_pending = !paint_rects_.empty(); |
484 | 601 |
485 // If this invalidate overlaps with a pending scroll, then we have to | 602 // If this invalidate overlaps with a pending scroll, then we have to |
486 // downgrade to invalidating the scroll rect. | 603 // downgrade to invalidating the scroll rect. |
| 604 PaintRegion rect_vector(&paint_rects_); |
487 if (gfx::Rect(rect).Intersects(scroll_rect_)) { | 605 if (gfx::Rect(rect).Intersects(scroll_rect_)) { |
488 paint_rect_ = paint_rect_.Union(scroll_rect_); | 606 rect_vector.Add(scroll_rect_); |
489 scroll_rect_ = gfx::Rect(); | 607 scroll_rect_ = gfx::Rect(); |
490 } | 608 } |
491 | 609 |
492 gfx::Rect view_rect(0, 0, size_.width(), size_.height()); | 610 // We don't need to intersect with the view rect here since we have to |
493 // TODO(iyengar) Investigate why we have painting issues when | 611 // do it later on in case the view rect changes between invalidations. |
494 // we ignore invalid regions outside the view. | 612 rect_vector.Add(rect); |
495 // Ignore invalidates that occur outside the bounds of the view | |
496 // TODO(darin): maybe this should move into the paint code? | |
497 // paint_rect_ = view_rect.Intersect(paint_rect_.Union(rect)); | |
498 paint_rect_ = paint_rect_.Union(view_rect.Intersect(rect)); | |
499 | 613 |
500 if (paint_rect_.IsEmpty() || paint_reply_pending() || paint_pending) | 614 if (paint_rects_.empty() || paint_reply_pending() || paint_pending) |
501 return; | 615 return; |
502 | 616 |
503 // Perform painting asynchronously. This serves two purposes: | 617 // Perform painting asynchronously. This serves two purposes: |
504 // 1) Ensures that we call WebView::Paint without a bunch of other junk | 618 // 1) Ensures that we call WebView::Paint without a bunch of other junk |
505 // on the call stack. | 619 // on the call stack. |
506 // 2) Allows us to collect more damage rects before painting to help coalesce | 620 // 2) Allows us to collect more damage rects before painting to help coalesce |
507 // the work that we will need to do. | 621 // the work that we will need to do. |
508 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( | 622 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( |
509 this, &RenderWidget::DoDeferredPaint)); | 623 this, &RenderWidget::DoDeferredPaint)); |
510 } | 624 } |
511 | 625 |
512 void RenderWidget::DidScrollRect(WebWidget* webwidget, int dx, int dy, | 626 void RenderWidget::DidScrollRect(WebWidget* webwidget, int dx, int dy, |
513 const WebRect& clip_rect) { | 627 const WebRect& clip_rect) { |
514 if (dx != 0 && dy != 0) { | 628 if (dx != 0 && dy != 0) { |
515 // We only support scrolling along one axis at a time. | 629 // We only support scrolling along one axis at a time. |
516 DidScrollRect(webwidget, 0, dy, clip_rect); | 630 DidScrollRect(webwidget, 0, dy, clip_rect); |
517 dy = 0; | 631 dy = 0; |
518 } | 632 } |
519 | 633 |
520 bool intersects_with_painting = paint_rect_.Intersects(clip_rect); | 634 bool intersects_with_painting = |
| 635 PaintRegion(&paint_rects_).Intersects(clip_rect); |
521 | 636 |
522 // If we already have a pending scroll operation or if this scroll operation | 637 // If we already have a pending scroll operation or if this scroll operation |
523 // intersects the existing paint region, then just failover to invalidating. | 638 // intersects the existing paint region, then just failover to invalidating. |
524 if (!scroll_rect_.IsEmpty() || intersects_with_painting) { | 639 if (!scroll_rect_.IsEmpty() || intersects_with_painting) { |
525 if (!intersects_with_painting && scroll_rect_ == gfx::Rect(clip_rect)) { | 640 if (!intersects_with_painting && scroll_rect_ == gfx::Rect(clip_rect)) { |
526 // OK, we can just update the scroll delta (requires same scrolling axis) | 641 // OK, we can just update the scroll delta (requires same scrolling axis) |
527 if (!dx && !scroll_delta_.x()) { | 642 if (!dx && !scroll_delta_.x()) { |
528 scroll_delta_.set_y(scroll_delta_.y() + dy); | 643 scroll_delta_.set_y(scroll_delta_.y() + dy); |
529 return; | 644 return; |
530 } | 645 } |
(...skipping 310 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
841 if (i == plugin_window_moves_.size()) | 956 if (i == plugin_window_moves_.size()) |
842 plugin_window_moves_.push_back(move); | 957 plugin_window_moves_.push_back(move); |
843 } | 958 } |
844 | 959 |
845 WebScreenInfo RenderWidget::GetScreenInfo(WebWidget* webwidget) { | 960 WebScreenInfo RenderWidget::GetScreenInfo(WebWidget* webwidget) { |
846 WebScreenInfo results; | 961 WebScreenInfo results; |
847 RenderThread::current()->Send( | 962 RenderThread::current()->Send( |
848 new ViewHostMsg_GetScreenInfo(host_window_, &results)); | 963 new ViewHostMsg_GetScreenInfo(host_window_, &results)); |
849 return results; | 964 return results; |
850 } | 965 } |
OLD | NEW |