diff --git a/cef1/cef.gyp b/cef1/cef.gyp index 1668e0cac..c7b2324d2 100644 --- a/cef1/cef.gyp +++ b/cef1/cef.gyp @@ -812,6 +812,7 @@ 'sources': [ '<@(includes_mac)', 'libcef/browser_impl_mac.mm', + 'libcef/browser_tooltip_mac.h', 'libcef/browser_webview_delegate_mac.mm', 'libcef/browser_webview_mac.h', 'libcef/browser_webview_mac.mm', diff --git a/cef1/libcef/browser_tooltip_mac.h b/cef1/libcef/browser_tooltip_mac.h new file mode 100644 index 000000000..3a7b037d2 --- /dev/null +++ b/cef1/libcef/browser_tooltip_mac.h @@ -0,0 +1,13 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CEF_LIBCEF_BROWSER_TOOLTIP_MAC_H_ +#define CEF_LIBCEF_BROWSER_TOOLTIP_MAC_H_ +#pragma once + +@protocol BrowserTooltip +- (void)setToolTipAtMousePoint:(NSString *)string; +@end + +#endif // CEF_LIBCEF_BROWSER_TOOLTIP_MAC_H_ diff --git a/cef1/libcef/browser_webview_mac.h b/cef1/libcef/browser_webview_mac.h index 557d2e339..c11e05b17 100644 --- a/cef1/libcef/browser_webview_mac.h +++ b/cef1/libcef/browser_webview_mac.h @@ -12,6 +12,7 @@ #include "base/memory/scoped_nsobject.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebCompositionUnderline.h" #include +#import "libcef/browser_tooltip_mac.h" class CefBrowserImpl; @class WebDragSource; @@ -21,7 +22,7 @@ struct WebDropData; // A view to wrap the WebCore view and help it live in a Cocoa world. The // (rough) equivalent of Apple's WebView. -@interface BrowserWebView : NSView { +@interface BrowserWebView : NSView { @private CefBrowserImpl* browser_; // weak NSTrackingArea* trackingArea_; @@ -60,6 +61,12 @@ struct WebDropData; // Underline information of the |markedText_|. std::vector underlines_; + + // These are part of the magic tooltip code from WebKit's WebHTMLView: + id trackingRectOwner_; // (not retained) + void* trackingRectUserData_; + NSTrackingRectTag lastToolTipTag_; + scoped_nsobject toolTip_; } - (void)mouseDown:(NSEvent *)theEvent; diff --git a/cef1/libcef/browser_webview_mac.mm b/cef1/libcef/browser_webview_mac.mm index 35a4f9fa3..10569a336 100644 --- a/cef1/libcef/browser_webview_mac.mm +++ b/cef1/libcef/browser_webview_mac.mm @@ -84,6 +84,7 @@ void ExtractUnderlines( trackingArea_ = [[NSTrackingArea alloc] initWithRect:frame options:NSTrackingMouseMoved | + NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp | NSTrackingInVisibleRect owner:self @@ -656,4 +657,196 @@ extern "C" { hasMarkedText_ = NO; } +/////////////////////////////////////////////////////////////////////////////// +// Tooltip Support +// This code is copied from +// content/browser/renderer_host/render_widget_host_view_mac.mm + +// Below is the nasty tooltip stuff -- copied from WebKit's WebHTMLView.mm +// with minor modifications for code style and commenting. +// +// The 'public' interface is -setToolTipAtMousePoint:. This differs from +// -setToolTip: in that the updated tooltip takes effect immediately, +// without the user's having to move the mouse out of and back into the view. +// +// Unfortunately, doing this requires sending fake mouseEnter/Exit events to +// the view, which in turn requires overriding some internal tracking-rect +// methods (to keep track of its owner & userdata, which need to be filled out +// in the fake events.) --snej 7/6/09 + + +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * (C) 2006, 2007 Graham Dennis (graham.dennis@gmail.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Any non-zero value will do, but using something recognizable might help us +// debug some day. +static const NSTrackingRectTag kTrackingRectTag = 0xBADFACE; + +// Override of a public NSView method, replacing the inherited functionality. +// See above for rationale. +- (NSTrackingRectTag)addTrackingRect:(NSRect)rect + owner:(id)owner + userData:(void *)data + assumeInside:(BOOL)assumeInside { + DCHECK(trackingRectOwner_ == nil); + trackingRectOwner_ = owner; + trackingRectUserData_ = data; + return kTrackingRectTag; +} + +// Override of (apparently) a private NSView method(!) See above for rationale. +- (NSTrackingRectTag)_addTrackingRect:(NSRect)rect + owner:(id)owner + userData:(void *)data + assumeInside:(BOOL)assumeInside + useTrackingNum:(int)tag { + DCHECK(tag == 0 || tag == kTrackingRectTag); + DCHECK(trackingRectOwner_ == nil); + trackingRectOwner_ = owner; + trackingRectUserData_ = data; + return kTrackingRectTag; +} + +// Override of (apparently) a private NSView method(!) See above for rationale. +- (void)_addTrackingRects:(NSRect *)rects + owner:(id)owner + userDataList:(void **)userDataList + assumeInsideList:(BOOL *)assumeInsideList + trackingNums:(NSTrackingRectTag *)trackingNums + count:(int)count { + DCHECK(count == 1); + DCHECK(trackingNums[0] == 0 || trackingNums[0] == kTrackingRectTag); + DCHECK(trackingRectOwner_ == nil); + trackingRectOwner_ = owner; + trackingRectUserData_ = userDataList[0]; + trackingNums[0] = kTrackingRectTag; +} + +// Override of a public NSView method, replacing the inherited functionality. +// See above for rationale. +- (void)removeTrackingRect:(NSTrackingRectTag)tag { + if (tag == 0) + return; + + if (tag == kTrackingRectTag) { + trackingRectOwner_ = nil; + return; + } + + if (tag == lastToolTipTag_) { + [super removeTrackingRect:tag]; + lastToolTipTag_ = 0; + return; + } + + // If any other tracking rect is being removed, we don't know how it was + // created and it's possible there's a leak involved (see Radar 3500217). + NOTREACHED(); +} + +// Override of (apparently) a private NSView method(!) +- (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count { + for (int i = 0; i < count; ++i) { + int tag = tags[i]; + if (tag == 0) + continue; + DCHECK(tag == kTrackingRectTag); + trackingRectOwner_ = nil; + } +} + +// Sends a fake NSMouseExited event to the view for its current tracking rect. +- (void)_sendToolTipMouseExited { + // Nothing matters except window, trackingNumber, and userData. + int windowNumber = [[self window] windowNumber]; + NSEvent* fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited + location:NSMakePoint(0, 0) + modifierFlags:0 + timestamp:0 + windowNumber:windowNumber + context:NULL + eventNumber:0 + trackingNumber:kTrackingRectTag + userData:trackingRectUserData_]; + [trackingRectOwner_ mouseExited:fakeEvent]; +} + +// Sends a fake NSMouseEntered event to the view for its current tracking rect. +- (void)_sendToolTipMouseEntered { + // Nothing matters except window, trackingNumber, and userData. + int windowNumber = [[self window] windowNumber]; + NSEvent* fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered + location:NSMakePoint(0, 0) + modifierFlags:0 + timestamp:0 + windowNumber:windowNumber + context:NULL + eventNumber:0 + trackingNumber:kTrackingRectTag + userData:trackingRectUserData_]; + [trackingRectOwner_ mouseEntered:fakeEvent]; +} + +// Sets the view's current tooltip, to be displayed at the current mouse +// location. (This does not make the tooltip appear -- as usual, it only +// appears after a delay.) Pass null to remove the tooltip. +- (void)setToolTipAtMousePoint:(NSString *)string { + NSString *toolTip = [string length] == 0 ? nil : string; + if ((toolTip && toolTip_ && [toolTip isEqualToString:toolTip_]) || + (!toolTip && !toolTip_)) { + return; + } + + if (toolTip_) { + [self _sendToolTipMouseExited]; + } + + toolTip_.reset([toolTip copy]); + + if (toolTip) { + // See radar 3500217 for why we remove all tooltips + // rather than just the single one we created. + [self removeAllToolTips]; + NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000); + lastToolTipTag_ = [self addToolTipRect:wideOpenRect + owner:self + userData:NULL]; + [self _sendToolTipMouseEntered]; + } +} + +// NSView calls this to get the text when displaying the tooltip. +- (NSString *)view:(NSView *)view + stringForToolTip:(NSToolTipTag)tag + point:(NSPoint)point + userData:(void *)data { + return [[toolTip_ retain] autorelease]; +} + @end diff --git a/cef1/libcef/webwidget_host.h b/cef1/libcef/webwidget_host.h index af7623335..6910c6b98 100644 --- a/cef1/libcef/webwidget_host.h +++ b/cef1/libcef/webwidget_host.h @@ -277,6 +277,7 @@ class WebWidgetHost { #endif // OS_WIN #if defined(OS_MACOSX) + std::string tooltip_text_; int mouse_modifiers_; WebKit::WebMouseEvent::Button mouse_button_down_; #endif diff --git a/cef1/libcef/webwidget_host_mac.mm b/cef1/libcef/webwidget_host_mac.mm index 66d235e90..d5a7ceb42 100644 --- a/cef1/libcef/webwidget_host_mac.mm +++ b/cef1/libcef/webwidget_host_mac.mm @@ -4,6 +4,7 @@ #import +#import "libcef/browser_tooltip_mac.h" #include "base/compiler_specific.h" #include "third_party/WebKit/Source/WebCore/config.h" MSVC_PUSH_WARNING_LEVEL(0); @@ -16,6 +17,7 @@ MSVC_POP_WARNING(); #include "base/bind.h" #import "base/logging.h" +#import "base/sys_string_conversions.h" #import "skia/ext/platform_canvas.h" #import "third_party/WebKit/Source/Platform/chromium/public/WebSize.h" #import "third_party/WebKit/Source/WebKit/chromium/public/mac/WebInputEventFactory.h" @@ -41,6 +43,9 @@ using WebKit::WebWidgetClient; namespace { +// Maximum number of characters we allow in a tooltip. +const size_t kMaxTooltipLength = 1024; + inline SkIRect convertToSkiaRect(const gfx::Rect& r) { return SkIRect::MakeLTRB(r.x(), r.y(), r.right(), r.bottom()); } @@ -297,7 +302,28 @@ void WebWidgetHost::Paint(SkRegion& update_rgn) { } void WebWidgetHost::SetTooltipText(const CefString& tooltip_text) { - // TODO(port): Implement this method as part of tooltip support. + if (tooltip_text.empty() && !tooltip_text_.empty()) { + tooltip_text_.clear(); + if ([view_ respondsToSelector:@selector(setToolTipAtMousePoint:)]) + [(id)view_ setToolTipAtMousePoint:nil]; + } else if (tooltip_text != tooltip_text_ && [[view_ window] isKeyWindow]) { + tooltip_text_ = tooltip_text; + + if ([view_ respondsToSelector:@selector(setToolTipAtMousePoint:)]) { + // Clamp the tooltip length to kMaxTooltipLength. It's a DOS issue on + // Windows; we're just trying to be polite. Don't persist the trimmed + // string, as then the comparison above will always fail and we'll try to + // set it again every single time the mouse moves. + NSString* tooltip_nsstring; + if (tooltip_text_.length() > kMaxTooltipLength) { + tooltip_nsstring = + base::SysUTF8ToNSString(tooltip_text_.substr(0, kMaxTooltipLength)); + } else { + tooltip_nsstring = base::SysUTF8ToNSString(tooltip_text_); + } + [(id)view_ setToolTipAtMousePoint:tooltip_nsstring]; + } + } } WebScreenInfo WebWidgetHost::GetScreenInfo() { @@ -308,6 +334,8 @@ void WebWidgetHost::MouseEvent(NSEvent *event) { const WebMouseEvent& web_event = WebInputEventFactory::mouseEvent( event, view_); webwidget_->handleInputEvent(web_event); + if (web_event.type == WebInputEvent::MouseLeave) + SetTooltipText(CefString()); } void WebWidgetHost::WheelEvent(NSEvent *event) { @@ -342,7 +370,7 @@ void WebWidgetHost::SendKeyEvent(cef_key_type_t type, event.type = WebInputEvent::Char; break; } - event.timeStampSeconds = TickCount(); + event.timeStampSeconds = TickCount(); if (modifiers & KEY_SHIFT) event.modifiers |= WebInputEvent::ShiftKey; @@ -453,7 +481,7 @@ void WebWidgetHost::SendMouseClickEvent(int x, int y, event.type = WebInputEvent::MouseDown; event.clickCount = clickCount; - event.timeStampSeconds = TickCount(); + event.timeStampSeconds = TickCount(); event.x = x; event.y = y; event.windowX = event.x; diff --git a/cef1/tests/cefclient/osrtest_mac.mm b/cef1/tests/cefclient/osrtest_mac.mm index 45f36df6f..9b2af4f7c 100644 --- a/cef1/tests/cefclient/osrtest_mac.mm +++ b/cef1/tests/cefclient/osrtest_mac.mm @@ -1,4 +1,4 @@ - // Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights +// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights // reserved. Use of this source code is governed by a BSD-style license // that can be found in the LICENSE file. @@ -303,6 +303,7 @@ class ClientOSRHandler : public CefClient, tracking_area_ = [[NSTrackingArea alloc] initWithRect:frame options:NSTrackingMouseMoved | + NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp | NSTrackingInVisibleRect owner:self