[Mac] Rubber-banding on Lion.

BUG=88353
TEST=On Lion, scroll past the edge of the web content area and see textured over-scroll area.

Review URL: http://codereview.chromium.org/7582009

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@96387 0039d316-1c4b-4281-b951-d872f2087c98
parent 84534f2f
......@@ -8,6 +8,7 @@
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/mac/mac_util.h"
#include "base/mac/scoped_cftyperef.h"
#import "base/mac/scoped_nsautorelease_pool.h"
#include "base/metrics/histogram.h"
......@@ -44,6 +45,7 @@
#include "third_party/WebKit/Source/WebKit/chromium/public/mac/WebInputEventFactory.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
#include "ui/gfx/point.h"
#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
#include "ui/gfx/surface/io_surface_support_mac.h"
#include "webkit/glue/webaccessibility.h"
#include "webkit/plugins/npapi/webplugin.h"
......@@ -52,6 +54,7 @@ using WebKit::WebInputEvent;
using WebKit::WebInputEventFactory;
using WebKit::WebMouseEvent;
using WebKit::WebMouseWheelEvent;
using WebKit::WebGestureEvent;
static inline int ToWebKitModifiers(NSUInteger flags) {
int modifiers = 0;
......@@ -75,14 +78,24 @@ static inline int ToWebKitModifiers(NSUInteger flags) {
@end
// This API was published since 10.6. Provide the declaration so it can be
// // called below when building with the 10.5 SDK.
// called below when building with the 10.5 SDK.
#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
@class NSTextInputContext;
@interface NSResponder (AppKitDetails)
- (NSTextInputContext *)inputContext;
- (void)beginGestureWithEvent:(NSEvent*)event;
- (void)endGestureWithEvent:(NSEvent*)event;
@end
#endif
// Undocumented Lion method to get the pattern for the over-scroll area.
@interface NSColor (LionSekretAPI)
+ (NSImage*)_linenPatternImage;
@end
// NSEvent subtype for scroll gestures events.
static const short kIOHIDEventTypeScroll = 6;
namespace {
// Maximum number of characters we allow in a tooltip.
......@@ -1211,6 +1224,32 @@ void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
renderWidgetHostView_->render_widget_host_->ForwardMouseEvent(event);
}
- (void)beginGestureWithEvent:(NSEvent*)event {
if (base::mac::IsOSLionOrLater() &&
[event subtype] == kIOHIDEventTypeScroll &&
renderWidgetHostView_->render_widget_host_) {
WebGestureEvent webEvent = WebInputEventFactory::gestureEvent(event, self);
renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(webEvent);
}
// Forward the gesture event to the next responder so that the browser window
// controller has a chance to act on back/forward gestures.
[[self nextResponder] beginGestureWithEvent:event];
}
- (void)endGestureWithEvent:(NSEvent*)event {
if (base::mac::IsOSLionOrLater() &&
[event subtype] == kIOHIDEventTypeScroll &&
renderWidgetHostView_->render_widget_host_) {
WebGestureEvent webEvent = WebInputEventFactory::gestureEvent(event, self);
renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(webEvent);
}
// Forward the gesture event to the next responder so that the browser window
// controller has a chance to act on back/forward gestures.
[[self nextResponder] endGestureWithEvent:event];
}
- (BOOL)performKeyEquivalent:(NSEvent*)theEvent {
// |performKeyEquivalent:| is sent to all views of a window, not only down the
// responder chain (cf. "Handling Key Equivalents" in
......@@ -1247,7 +1286,7 @@ void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
// to us instead of doing key view loop control, ctrl-left/right get handled
// correctly, etc.
// (However, there are still some keys that Cocoa swallows, e.g. the key
// equivalent that Cocoa uses for toggling the input langauge. In this case,
// equivalent that Cocoa uses for toggling the input language. In this case,
// that's actually a good thing, though -- see http://crbug.com/26115 .)
return YES;
}
......@@ -1566,6 +1605,93 @@ void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
}
}
- (void)fillRect:(NSRect)rect withPattern:(NSImage*)patternImage {
if (NSIsEmptyRect(rect))
return;
NSColor* patternColor = [NSColor colorWithPatternImage:patternImage];
[patternColor set];
NSRectFill(rect);
}
- (void)drawShadow:(NSShadow*)shadow
withRect:(NSRect)rect
clip:(NSRect)clipRect {
gfx::ScopedNSGraphicsContextSaveGState scopedGState;
NSBezierPath* drawingPath = [NSBezierPath bezierPathWithRect:rect];
[shadow set];
[[NSColor blackColor] set];
[[NSBezierPath bezierPathWithRect:clipRect] addClip];
[drawingPath fill];
}
- (NSRect)computeVisibleContentRect {
const RenderWidgetHost* rwh = renderWidgetHostView_->render_widget_host_;
gfx::Point offset = rwh->last_scroll_offset();
gfx::Size size = rwh->contents_size();
NSRect contentRect =
NSMakeRect(-offset.x(), -offset.y(), size.width(), size.height());
NSRect frameRect = [self frame];
frameRect.origin = NSMakePoint(0, 0);
return NSIntersectionRect(frameRect, contentRect);
}
- (void)fillOverScrollAreas:(NSRect)dirtyRect {
if (![NSColor respondsToSelector:@selector(_linenPatternImage)])
return;
NSRect visibleContentRect = [self computeVisibleContentRect];
NSSize frameSize = [self frame].size;
bool hasHorizontalOverflow = (NSWidth(visibleContentRect) < frameSize.width);
bool hasVerticalOverflow = (NSHeight(visibleContentRect) < frameSize.height);
if (!hasHorizontalOverflow && !hasVerticalOverflow)
return;
NSRect xRect = NSMakeRect(0,
0,
frameSize.width - NSWidth(visibleContentRect),
frameSize.height);
NSRect yRect = NSMakeRect(0,
0,
frameSize.width,
frameSize.height - NSHeight(visibleContentRect));
NSImage* background = [NSColor _linenPatternImage];
scoped_nsobject<NSShadow> shadow([[NSShadow alloc] init]);
[shadow.get() setShadowColor:[NSColor blackColor]];
[shadow setShadowBlurRadius:5];
if (hasHorizontalOverflow) {
NSRect shadowRect;
if (visibleContentRect.origin.x > 0) {
shadowRect = xRect;
shadowRect.origin.x += NSWidth(shadowRect);
} else {
xRect.origin.x = NSWidth(visibleContentRect);
shadowRect = xRect;
shadowRect.origin.x -= 1;
}
shadowRect.size.width = 1;
NSRect intersectRect = NSIntersectionRect(dirtyRect, xRect);
[self fillRect:intersectRect withPattern:background];
[self drawShadow:shadow.get() withRect:shadowRect clip:intersectRect];
}
if (hasVerticalOverflow) {
NSRect shadowRect = visibleContentRect;
if (visibleContentRect.origin.y > 0) {
yRect.origin.y = NSHeight(visibleContentRect);
shadowRect.origin.y = yRect.origin.y - 1;
} else {
shadowRect.origin.y = yRect.origin.y + NSHeight(yRect);
}
shadowRect.size.height = 1;
NSRect intersectRect = NSIntersectionRect(dirtyRect, yRect);
[self fillRect:intersectRect withPattern:background];
[self drawShadow:shadow.get() withRect:shadowRect clip:intersectRect];
}
}
- (void)drawRect:(NSRect)dirtyRect {
if (!renderWidgetHostView_->render_widget_host_) {
// TODO(shess): Consider using something more noticable?
......@@ -1641,6 +1767,9 @@ void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
// Fill the remaining portion of the damagedRect with white
[self fillBottomRightRemainderOfRect:bitmapRect dirtyRect:damagedRect];
// Fill the over-scroll areas, if any, with the appropriate pattern.
[self fillOverScrollAreas:dirtyRect];
if (!renderWidgetHostView_->whiteout_start_time_.is_null()) {
base::TimeDelta whiteout_duration = base::TimeTicks::Now() -
renderWidgetHostView_->whiteout_start_time_;
......@@ -1888,7 +2017,7 @@ void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
// Performs a right click copying WebKit's
// accessibilityPerformShowMenuAction.
NSPoint location = [self accessibilityPointInScreen:accessibility];
NSSize size = [[accessibility size] sizeValue];
NSSize size = [[accessibility size] sizeValue];
location = [[self window] convertScreenToBase:location];
location.x += size.width/2;
location.y += size.height/2;
......
......@@ -34,6 +34,7 @@ using base::Time;
using base::TimeDelta;
using base::TimeTicks;
using WebKit::WebGestureEvent;
using WebKit::WebInputEvent;
using WebKit::WebKeyboardEvent;
using WebKit::WebMouseEvent;
......@@ -560,6 +561,15 @@ void RenderWidgetHost::ForwardWheelEvent(
ForwardInputEvent(wheel_event, sizeof(WebMouseWheelEvent), false);
}
void RenderWidgetHost::ForwardGestureEvent(
const WebKit::WebGestureEvent& gesture_event) {
TRACE_EVENT0("renderer_host", "RenderWidgetHost::ForwardWheelEvent");
if (ignore_input_events_ || process_->ignore_input_events())
return;
ForwardInputEvent(gesture_event, sizeof(WebGestureEvent), false);
}
void RenderWidgetHost::ForwardKeyboardEvent(
const NativeWebKeyboardEvent& key_event) {
TRACE_EVENT0("renderer_host", "RenderWidgetHost::ForwardKeyboardEvent");
......@@ -870,6 +880,8 @@ void RenderWidgetHost::OnMsgUpdateRect(
// Update our knowledge of the RenderWidget's size.
current_size_ = params.view_size;
// Update our knowledge of the RenderWidget's contents size.
contents_size_ = params.contents_size;
// Update our knowledge of the RenderWidget's scroll offset.
last_scroll_offset_ = params.scroll_offset;
......
......@@ -260,6 +260,7 @@ class RenderWidgetHost : public IPC::Channel::Listener,
// Called when a mouse click activates the renderer.
virtual void OnMouseActivate();
void ForwardWheelEvent(const WebKit::WebMouseWheelEvent& wheel_event);
void ForwardGestureEvent(const WebKit::WebGestureEvent& gesture_event);
virtual void ForwardKeyboardEvent(const NativeWebKeyboardEvent& key_event);
virtual void ForwardTouchEvent(const WebKit::WebTouchEvent& touch_event);
......@@ -366,6 +367,7 @@ class RenderWidgetHost : public IPC::Channel::Listener,
void ActivateDeferredPluginHandles();
const gfx::Point& last_scroll_offset() const { return last_scroll_offset_; }
const gfx::Size& contents_size() const { return contents_size_; }
// Notification that the user has made some kind of input that could
// perform an action. See OnUserGesture for more details.
......@@ -550,6 +552,13 @@ class RenderWidgetHost : public IPC::Channel::Listener,
// The current size of the RenderWidget.
gfx::Size current_size_;
// The size of the underlying contents, including parts that are not visible
// due to scrolling. It can be used to calculate the view port bounds using
// |last_scroll_offset_|. Note: This size is not always available. In
// particular, it is only available for RenderViews after a paint has taken
// place. Additionally, it only available on the Mac.
gfx::Size contents_size_;
// The current reserved area of the RenderWidget where contents should not be
// rendered to draw the resize corner, sidebar mini tabs etc.
gfx::Rect current_reserved_rect_;
......
......@@ -627,6 +627,9 @@ IPC_STRUCT_BEGIN(ViewHostMsg_UpdateRect_Params)
// progress.
IPC_STRUCT_MEMBER(gfx::Size, view_size)
// The size of the web content area.
IPC_STRUCT_MEMBER(gfx::Size, contents_size)
// The area of the RenderView reserved for resize corner when this message
// was generated. Reported for the same reason as view_size is.
IPC_STRUCT_MEMBER(gfx::Rect, resizer_rect)
......@@ -646,7 +649,7 @@ IPC_STRUCT_BEGIN(ViewHostMsg_UpdateRect_Params)
// ViewHostMsg_UpdateRect_Flags::IS_REPAINT_ACK
// Indicates that this is a response to a ViewMsg_Repaint message.
//
// If flags is zero, then this message corresponds to an unsoliticed paint
// If flags is zero, then this message corresponds to an unsolicited paint
// request by the render view. Any of the above bits may be set in flags,
// which would indicate that this paint message is an ACK for multiple
// request messages.
......
......@@ -3944,6 +3944,11 @@ gfx::Point RenderView::GetScrollOffset() {
return gfx::Point(scroll_offset.width, scroll_offset.height);
}
gfx::Size RenderView::GetContentsSize() {
WebSize bounds = webview()->mainFrame()->contentsSize();
return gfx::Size(bounds.width, bounds.height);
}
void RenderView::OnClearFocusedNode() {
if (webview())
webview()->clearFocusedNode();
......
......@@ -624,6 +624,7 @@ class RenderView : public RenderWidget,
gfx::Rect* location,
gfx::Rect* clip);
virtual gfx::Point GetScrollOffset();
virtual gfx::Size GetContentsSize();
virtual void DidHandleKeyEvent();
virtual void DidHandleMouseEvent(const WebKit::WebMouseEvent& event);
virtual void OnSetFocus(bool enable);
......
......@@ -831,6 +831,7 @@ void RenderWidget::DoDeferredUpdate() {
params.plugin_window_moves.swap(plugin_window_moves_);
params.flags = next_paint_flags_;
params.scroll_offset = GetScrollOffset();
params.contents_size = GetContentsSize();
update_reply_pending_ = true;
Send(new ViewHostMsg_UpdateRect(routing_id_, params));
......@@ -1248,6 +1249,11 @@ gfx::Point RenderWidget::GetScrollOffset() {
return gfx::Point(0, 0);
}
gfx::Size RenderWidget::GetContentsSize() {
// Bare RenderWidgets don't support contents size.
return gfx::Size(0, 0);
}
void RenderWidget::SetHidden(bool hidden) {
if (is_hidden_ == hidden)
return;
......
......@@ -250,6 +250,9 @@ class RenderWidget : public IPC::Channel::Listener,
// scroll offset.
virtual gfx::Point GetScrollOffset();
// Gets the dimensions of the content area.
virtual gfx::Size GetContentsSize();
// Sets the "hidden" state of this widget. All accesses to is_hidden_ should
// use this method so that we can properly inform the RenderThread of our
// state.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment