blob: 9374bf30a7385f0f9fb712740729e207938ef45b [file] [log] [blame]
// Copyright (c) 2012 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 CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_MAC_H_
#define CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_MAC_H_
#import <Cocoa/Cocoa.h>
#include <IOSurface/IOSurface.h>
#include <stddef.h>
#include <stdint.h>
#include <list>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/mac/scoped_nsobject.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "components/viz/common/surfaces/surface_id.h"
#include "content/browser/renderer_host/browser_compositor_view_mac.h"
#include "content/browser/renderer_host/input/mouse_wheel_phase_handler.h"
#include "content/browser/renderer_host/input/mouse_wheel_rails_filter_mac.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/browser/renderer_host/text_input_manager.h"
#include "content/common/content_export.h"
#include "content/common/cursors/webcursor.h"
#include "content/common/edit_command.h"
#include "ipc/ipc_sender.h"
#include "ui/accelerated_widget_mac/accelerated_widget_mac.h"
#include "ui/accelerated_widget_mac/display_link_mac.h"
#import "ui/base/cocoa/command_dispatcher.h"
#include "ui/base/cocoa/remote_layer_api.h"
#import "ui/base/cocoa/tool_tip_base_view.h"
#include "ui/base/ime/composition_underline.h"
#include "ui/display/display_observer.h"
namespace content {
class CursorManager;
class RenderWidgetHost;
class RenderWidgetHostImpl;
class RenderWidgetHostViewMac;
class RenderWidgetHostViewMacEditCommandHelper;
class WebContents;
struct TextInputState;
}
@class FullscreenWindowManager;
@protocol RenderWidgetHostViewMacDelegate;
@protocol RenderWidgetHostViewMacOwner
- (content::RenderWidgetHostViewMac*)renderWidgetHostViewMac;
@end
// This is the view that lives in the Cocoa view hierarchy. In Windows-land,
// RenderWidgetHostViewWin is both the view and the delegate. We split the roles
// but that means that the view needs to own the delegate and will dispose of it
// when it's removed from the view system.
@interface RenderWidgetHostViewCocoa
: ToolTipBaseView<CommandDispatcherTarget,
RenderWidgetHostViewMacOwner,
NSTextInputClient> {
@private
std::unique_ptr<content::RenderWidgetHostViewMac> renderWidgetHostView_;
// This ivar is the cocoa delegate of the NSResponder.
base::scoped_nsobject<NSObject<RenderWidgetHostViewMacDelegate>>
responderDelegate_;
BOOL canBeKeyView_;
BOOL closeOnDeactivate_;
std::unique_ptr<content::RenderWidgetHostViewMacEditCommandHelper>
editCommand_helper_;
// Is YES if there was a mouse-down as yet unbalanced with a mouse-up.
BOOL hasOpenMouseDown_;
// The cursor for the page. This is passed up from the renderer.
base::scoped_nsobject<NSCursor> currentCursor_;
// Is YES if the cursor is hidden by key events.
BOOL cursorHidden_;
// Variables used by our implementaion of the NSTextInput protocol.
// An input method of Mac calls the methods of this protocol not only to
// notify an application of its status, but also to retrieve the status of
// the application. That is, an application cannot control an input method
// directly.
// This object keeps the status of a composition of the renderer and returns
// it when an input method asks for it.
// We need to implement Objective-C methods for the NSTextInput protocol. On
// the other hand, we need to implement a C++ method for an IPC-message
// handler which receives input-method events from the renderer.
// Represents the input-method attributes supported by this object.
base::scoped_nsobject<NSArray> validAttributesForMarkedText_;
// Indicates if we are currently handling a key down event.
BOOL handlingKeyDown_;
// Indicates if there is any marked text.
BOOL hasMarkedText_;
// Indicates if unmarkText is called or not when handling a keyboard
// event.
BOOL unmarkTextCalled_;
// The range of current marked text inside the whole content of the DOM node
// being edited.
// TODO(suzhe): This is currently a fake value, as we do not support accessing
// the whole content yet.
NSRange markedRange_;
// The selected range, cached from a message sent by the renderer.
NSRange selectedRange_;
// Text to be inserted which was generated by handling a key down event.
base::string16 textToBeInserted_;
// Marked text which was generated by handling a key down event.
base::string16 markedText_;
// Selected range of |markedText_|.
NSRange markedTextSelectedRange_;
// Underline information of the |markedText_|.
std::vector<ui::CompositionUnderline> underlines_;
// Replacement range information received from |setMarkedText:|.
gfx::Range setMarkedTextReplacementRange_;
// Indicates if doCommandBySelector method receives any edit command when
// handling a key down event.
BOOL hasEditCommands_;
// Contains edit commands received by the -doCommandBySelector: method when
// handling a key down event, not including inserting commands, eg. insertTab,
// etc.
content::EditCommands editCommands_;
// Whether the previous mouse event was ignored due to hitTest check.
BOOL mouseEventWasIgnored_;
// Event monitor for scroll wheel end event.
id endWheelMonitor_;
// When a gesture starts, the system does not inform the view of which type
// of gesture is happening (magnify, rotate, etc), rather, it just informs
// the view that some as-yet-undefined gesture is starting. Capture the
// information about the gesture's beginning event here. It will be used to
// create a specific gesture begin event later.
std::unique_ptr<blink::WebGestureEvent> gestureBeginEvent_;
// To avoid accidental pinches, require that a certain zoom threshold be
// reached before forwarding it to the browser. Use |pinchUnusedAmount_| to
// hold this value. If the user reaches this value, don't re-require the
// threshold be reached until the page has been zoomed back to page scale of
// one.
bool pinchHasReachedZoomThreshold_;
float pinchUnusedAmount_;
NSTimeInterval pinchLastGestureTimestamp_;
// This is set if a GesturePinchBegin event has been sent in the lifetime of
// |gestureBeginEvent_|. If set, a GesturePinchEnd will be sent when the
// gesture ends.
BOOL gestureBeginPinchSent_;
// If true then escape key down events are suppressed until the first escape
// key up event. (The up event is suppressed as well). This is used by the
// flash fullscreen code to avoid sending a key up event without a matching
// key down event.
BOOL suppressNextEscapeKeyUp_;
// This is used to indicate if a stylus is currently in the proximity of the
// tablet.
bool isStylusEnteringProximity_;
blink::WebPointerProperties::PointerType pointerType_;
// The set of key codes from key down events that we haven't seen the matching
// key up events yet.
// Used for filtering out non-matching NSKeyUp events.
std::set<unsigned short> keyDownCodes_;
// The filter used to guide touch events towards a horizontal or vertical
// orientation.
content::MouseWheelRailsFilterMac mouseWheelFilter_;
}
@property(nonatomic, readonly) NSRange selectedRange;
@property(nonatomic, readonly) BOOL suppressNextEscapeKeyUp;
// Common code path for handling begin gesture events. This helper method is
// called via different codepaths based on OS version and SDK:
// - On 10.11 and later, when linking with the 10.11 SDK, it is called from
// |magnifyWithEvent:| when the given event's phase is NSEventPhaseBegin.
// - On 10.10 and earlier, or when linking with an earlier SDK, it is called
// by |beginGestureWithEvent:| when a gesture begins.
- (void)handleBeginGestureWithEvent:(NSEvent*)event;
// Common code path for handling end gesture events. This helper method is
// called via different codepaths based on OS version and SDK:
// - On 10.11 and later, when linking with the 10.11 SDK, it is called from
// |magnifyWithEvent:| when the given event's phase is NSEventPhaseEnded.
// - On 10.10 and earlier, or when linking with an earlier SDK, it is called
// by |endGestureWithEvent:| when a gesture ends.
- (void)handleEndGestureWithEvent:(NSEvent*)event;
- (void)setCanBeKeyView:(BOOL)can;
- (void)setCloseOnDeactivate:(BOOL)b;
// True for always-on-top special windows (e.g. Balloons and Panels).
- (BOOL)acceptsMouseEventsWhenInactive;
// Cancel ongoing composition (abandon the marked text).
- (void)cancelComposition;
// Confirm ongoing composition.
- (void)finishComposingText;
- (void)updateCursor:(NSCursor*)cursor;
- (NSRect)firstViewRectForCharacterRange:(NSRange)theRange
actualRange:(NSRangePointer)actualRange;
- (void)tabletEvent:(NSEvent*)theEvent;
- (void)quickLookWithEvent:(NSEvent*)event;
- (void)showLookUpDictionaryOverlayAtPoint:(NSPoint)point;
- (void)showLookUpDictionaryOverlayFromRange:(NSRange)range
targetView:(NSView*)targetView;
@end
namespace content {
///////////////////////////////////////////////////////////////////////////////
// RenderWidgetHostViewMac
//
// An object representing the "View" of a rendered web page. This object is
// responsible for displaying the content of the web page, and integrating with
// the Cocoa view system. It is the implementation of the RenderWidgetHostView
// that the cross-platform RenderWidgetHost object uses
// to display the data.
//
// Comment excerpted from render_widget_host.h:
//
// "The lifetime of the RenderWidgetHost* is tied to the render process.
// If the render process dies, the RenderWidgetHost* goes away and all
// references to it must become NULL."
//
// RenderWidgetHostView class hierarchy described in render_widget_host_view.h.
class CONTENT_EXPORT RenderWidgetHostViewMac
: public RenderWidgetHostViewBase,
public BrowserCompositorMacClient,
public TextInputManager::Observer,
public ui::AcceleratedWidgetMacNSView,
public IPC::Sender,
public display::DisplayObserver {
public:
// The view will associate itself with the given widget. The native view must
// be hooked up immediately to the view hierarchy, or else when it is
// deleted it will delete this out from under the caller.
//
// When |is_guest_view_hack| is true, this view isn't really the view for
// the |widget|, a RenderWidgetHostViewGuest is.
// TODO(lazyboy): Remove |is_guest_view_hack| once BrowserPlugin has migrated
// to use RWHVChildFrame (http://crbug.com/330264).
RenderWidgetHostViewMac(RenderWidgetHost* widget, bool is_guest_view_hack);
~RenderWidgetHostViewMac() override;
RenderWidgetHostViewCocoa* cocoa_view() const { return cocoa_view_; }
// |delegate| is used to separate out the logic from the NSResponder delegate.
// |delegate| is retained by this class.
// |delegate| should be set at most once.
CONTENT_EXPORT void SetDelegate(
NSObject<RenderWidgetHostViewMacDelegate>* delegate);
void SetAllowPauseForResizeOrRepaint(bool allow);
// RenderWidgetHostView implementation.
bool OnMessageReceived(const IPC::Message& msg) override;
void InitAsChild(gfx::NativeView parent_view) override;
RenderWidgetHost* GetRenderWidgetHost() const override;
void SetSize(const gfx::Size& size) override;
void SetBounds(const gfx::Rect& rect) override;
gfx::Vector2dF GetLastScrollOffset() const override;
gfx::NativeView GetNativeView() const override;
gfx::NativeViewAccessible GetNativeViewAccessible() override;
bool HasFocus() const override;
void Show() override;
void Hide() override;
bool IsShowing() override;
void WasUnOccluded() override;
void WasOccluded() override;
gfx::Rect GetViewBounds() const override;
void SetActive(bool active) override;
void ShowDefinitionForSelection() override;
bool SupportsSpeech() const override;
void SpeakSelection() override;
bool IsSpeaking() const override;
void StopSpeaking() override;
void SetBackgroundColor(SkColor color) override;
SkColor background_color() const override;
void SetNeedsBeginFrames(bool needs_begin_frames) override;
// Implementation of RenderWidgetHostViewBase.
void InitAsPopup(RenderWidgetHostView* parent_host_view,
const gfx::Rect& pos) override;
void InitAsFullscreen(RenderWidgetHostView* reference_host_view) override;
void Focus() override;
void UpdateCursor(const WebCursor& cursor) override;
void DisplayCursor(const WebCursor& cursor) override;
CursorManager* GetCursorManager() override;
void SetIsLoading(bool is_loading) override;
void RenderProcessGone(base::TerminationStatus status,
int error_code) override;
void Destroy() override;
void SetTooltipText(const base::string16& tooltip_text) override;
bool IsSurfaceAvailableForCopy() const override;
void CopyFromSurface(const gfx::Rect& src_rect,
const gfx::Size& output_size,
const ReadbackRequestCallback& callback,
const SkColorType color_type) override;
void CopyFromSurfaceToVideoFrame(
const gfx::Rect& src_rect,
scoped_refptr<media::VideoFrame> target,
const base::Callback<void(const gfx::Rect&, bool)>& callback) override;
void BeginFrameSubscription(
std::unique_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) override;
void EndFrameSubscription() override;
ui::AcceleratedWidgetMac* GetAcceleratedWidgetMac() const override;
void FocusedNodeChanged(bool is_editable_node,
const gfx::Rect& node_bounds_in_screen) override;
void DidCreateNewRendererCompositorFrameSink(
viz::mojom::CompositorFrameSinkClient* renderer_compositor_frame_sink)
override;
void SubmitCompositorFrame(const viz::LocalSurfaceId& local_surface_id,
cc::CompositorFrame frame) override;
void OnDidNotProduceFrame(const viz::BeginFrameAck& ack) override;
void ClearCompositorFrame() override;
BrowserAccessibilityManager* CreateBrowserAccessibilityManager(
BrowserAccessibilityDelegate* delegate, bool for_root_frame) override;
gfx::Point AccessibilityOriginInScreen(const gfx::Rect& bounds) override;
gfx::AcceleratedWidget AccessibilityGetAcceleratedWidget() override;
bool HasAcceleratedSurface(const gfx::Size& desired_size) override;
gfx::Rect GetBoundsInRootWindow() override;
bool LockMouse() override;
void UnlockMouse() override;
void GestureEventAck(const blink::WebGestureEvent& event,
InputEventAckState ack_result) override;
std::unique_ptr<SyntheticGestureTarget> CreateSyntheticGestureTarget()
override;
viz::FrameSinkId GetFrameSinkId() override;
viz::FrameSinkId FrameSinkIdAtPoint(viz::SurfaceHittestDelegate* delegate,
const gfx::Point& point,
gfx::Point* transformed_point) override;
// Returns true when we can do SurfaceHitTesting for the event type.
bool ShouldRouteEvent(const blink::WebInputEvent& event) const;
void ProcessMouseEvent(const blink::WebMouseEvent& event,
const ui::LatencyInfo& latency) override;
void ProcessMouseWheelEvent(const blink::WebMouseWheelEvent& event,
const ui::LatencyInfo& latency) override;
void ProcessTouchEvent(const blink::WebTouchEvent& event,
const ui::LatencyInfo& latency) override;
void ProcessGestureEvent(const blink::WebGestureEvent& event,
const ui::LatencyInfo& latency) override;
bool TransformPointToLocalCoordSpace(const gfx::Point& point,
const viz::SurfaceId& original_surface,
gfx::Point* transformed_point) override;
bool TransformPointToCoordSpaceForView(
const gfx::Point& point,
RenderWidgetHostViewBase* target_view,
gfx::Point* transformed_point) override;
// TextInputManager::Observer implementation.
void OnUpdateTextInputStateCalled(TextInputManager* text_input_manager,
RenderWidgetHostViewBase* updated_view,
bool did_update_state) override;
void OnImeCancelComposition(TextInputManager* text_input_manager,
RenderWidgetHostViewBase* updated_view) override;
void OnImeCompositionRangeChanged(
TextInputManager* text_input_manager,
RenderWidgetHostViewBase* updated_view) override;
void OnSelectionBoundsChanged(
TextInputManager* text_input_manager,
RenderWidgetHostViewBase* updated_view) override;
void OnTextSelectionChanged(TextInputManager* text_input_manager,
RenderWidgetHostViewBase* updated_view) override;
// IPC::Sender implementation.
bool Send(IPC::Message* message) override;
// display::DisplayObserver implementation.
void OnDisplayAdded(const display::Display& new_display) override;
void OnDisplayRemoved(const display::Display& old_display) override;
void OnDisplayMetricsChanged(const display::Display& display,
uint32_t metrics) override;
// Forwards the mouse event to the renderer.
void ForwardMouseEvent(const blink::WebMouseEvent& event);
void KillSelf();
void SetTextInputActive(bool active);
// Returns true and stores first rectangle for character range if the
// requested |range| is already cached, otherwise returns false.
// Exposed for testing.
CONTENT_EXPORT bool GetCachedFirstRectForCharacterRange(
NSRange range, NSRect* rect, NSRange* actual_range);
// Returns true if there is line break in |range| and stores line breaking
// point to |line_breaking_point|. The |line_break_point| is valid only if
// this function returns true.
bool GetLineBreakIndex(const std::vector<gfx::Rect>& bounds,
const gfx::Range& range,
size_t* line_break_point);
// Returns composition character boundary rectangle. The |range| is
// composition based range. Also stores |actual_range| which is corresponding
// to actually used range for returned rectangle.
gfx::Rect GetFirstRectForCompositionRange(const gfx::Range& range,
gfx::Range* actual_range);
// Converts from given whole character range to composition oriented range. If
// the conversion failed, return gfx::Range::InvalidRange.
gfx::Range ConvertCharacterRangeToCompositionRange(
const gfx::Range& request_range);
WebContents* GetWebContents();
// Set the color of the background CALayer shown when no content is ready to
// see.
void SetBackgroundLayerColor(SkColor color);
bool HasPendingWheelEndEventForTesting() {
return mouse_wheel_phase_handler_.HasPendingWheelEndEvent();
}
// These member variables should be private, but the associated ObjC class
// needs access to them and can't be made a friend.
// The associated Model. Can be NULL if Destroy() is called when
// someone (other than superview) has retained |cocoa_view_|.
RenderWidgetHostImpl* render_widget_host_;
// The background CoreAnimation layer which is hosted by |cocoa_view_|.
base::scoped_nsobject<CALayer> background_layer_;
// Delegated frame management and compositor interface.
std::unique_ptr<BrowserCompositorMac> browser_compositor_;
BrowserCompositorMac* BrowserCompositorForTesting() const {
return browser_compositor_.get();
}
// Set when the currently-displayed frame is the minimum scale. Used to
// determine if pinch gestures need to be thresholded.
bool page_at_minimum_scale_;
MouseWheelPhaseHandler mouse_wheel_phase_handler_;
NSWindow* pepper_fullscreen_window() const {
return pepper_fullscreen_window_;
}
CONTENT_EXPORT void release_pepper_fullscreen_window_for_testing();
RenderWidgetHostViewMac* fullscreen_parent_host_view() const {
return fullscreen_parent_host_view_;
}
int window_number() const;
// Update properties, such as the scale factor for the backing store
// and for any CALayers, and the screen color profile.
void UpdateBackingStoreProperties();
// Ensure that the display link is associated with the correct display.
void UpdateDisplayLink();
void PauseForPendingResizeOrRepaintsAndDraw();
// BrowserCompositorMacClient implementation.
NSView* BrowserCompositorMacGetNSView() const override;
SkColor BrowserCompositorMacGetGutterColor(SkColor color) const override;
void BrowserCompositorMacOnBeginFrame() override;
// AcceleratedWidgetMacNSView implementation.
NSView* AcceleratedWidgetGetNSView() const override;
void AcceleratedWidgetGetVSyncParameters(
base::TimeTicks* timebase, base::TimeDelta* interval) const override;
void AcceleratedWidgetSwapCompleted() override;
// Exposed for testing.
viz::SurfaceId SurfaceIdForTesting() const override;
void SetShowingContextMenu(bool showing) override;
// Helper method to obtain ui::TextInputType for the active widget from the
// TextInputManager.
ui::TextInputType GetTextInputType();
// Helper method to obtain the currently active widget from TextInputManager.
// An active widget is a RenderWidget which is currently focused and has a
// |TextInputState.type| which is not ui::TEXT_INPUT_TYPE_NONE.
RenderWidgetHostImpl* GetActiveWidget();
// Returns the composition range information for the active RenderWidgetHost
// (accepting IME and keyboard input).
const TextInputManager::CompositionRangeInfo* GetCompositionRangeInfo();
// Returns the TextSelection information for the active widget. If
// |is_guest_view_hack_| is true, then it will return the TextSelection
// information for this RenderWidgetHostViewMac (which is serving as a
// platform view for a guest).
const TextInputManager::TextSelection* GetTextSelection();
// Get the focused view that should be used for retrieving the text selection.
RenderWidgetHostViewBase* GetFocusedViewForTextSelection();
// Returns the RenderWidgetHostDelegate corresponding to the currently focused
// RenderWidgetHost. It is different from |render_widget_host_->delegate()|
// when there are focused inner WebContentses on the page. Also, this method
// can return nullptr; for instance when |render_widget_host_| becomes nullptr
// in the destruction path of the WebContentsImpl.
RenderWidgetHostDelegate* GetFocusedRenderWidgetHostDelegate();
private:
friend class RenderWidgetHostViewMacTest;
// Returns whether this render view is a popup (autocomplete window).
bool IsPopup() const;
// Shuts down the render_widget_host_. This is a separate function so we can
// invoke it from the message loop.
void ShutdownHost();
// IPC message handlers.
void OnGetRenderedTextCompleted(const std::string& text);
// Send updated vsync parameters to the top level display.
void UpdateDisplayVSyncParameters();
// Dispatches a TTS session.
void SpeakText(const std::string& text);
// Adds/Removes frame observer based on state.
void UpdateNeedsBeginFramesInternal();
void SendSyntheticWheelEventWithPhaseEnded(
blink::WebMouseWheelEvent wheel_event,
bool should_route_event);
// The associated view. This is weak and is inserted into the view hierarchy
// to own this RenderWidgetHostViewMac object. Set to nil at the start of the
// destructor.
RenderWidgetHostViewCocoa* cocoa_view_;
// Indicates if the page is loading.
bool is_loading_;
// Whether it's allowed to pause waiting for a new frame.
bool allow_pause_for_resize_or_repaint_;
// The last scroll offset of the view.
gfx::Vector2dF last_scroll_offset_;
// The text to be shown in the tooltip, supplied by the renderer.
base::string16 tooltip_text_;
// True when this view acts as a platform view hack for a
// RenderWidgetHostViewGuest.
bool is_guest_view_hack_;
// The window used for popup widgets.
base::scoped_nsobject<NSWindow> popup_window_;
// The fullscreen window used for pepper flash.
base::scoped_nsobject<NSWindow> pepper_fullscreen_window_;
base::scoped_nsobject<FullscreenWindowManager> fullscreen_window_manager_;
// Our parent host view, if this is fullscreen. NULL otherwise.
RenderWidgetHostViewMac* fullscreen_parent_host_view_;
// Display link for getting vsync info.
scoped_refptr<ui::DisplayLinkMac> display_link_;
// The current VSync timebase and interval. This is zero until the first call
// to SendVSyncParametersToRenderer(), and refreshed regularly thereafter.
base::TimeTicks vsync_timebase_;
base::TimeDelta vsync_interval_;
// Whether a request for begin frames has been issued.
bool needs_begin_frames_;
// Whether or not the background is opaque as determined by calls to
// SetBackgroundColor. The default value is opaque.
bool background_is_opaque_ = true;
// The color of the background CALayer, stored as a SkColor for efficient
// comparison. Initially transparent so that the embedding NSView shows
// through.
SkColor background_layer_color_ = SK_ColorTRANSPARENT;
// The background color of the last frame that was swapped. This is not
// applied until the swap completes (see comments in
// AcceleratedWidgetSwapCompleted).
SkColor last_frame_root_background_color_ = SK_ColorTRANSPARENT;
std::unique_ptr<CursorManager> cursor_manager_;
// Factory used to safely scope delayed calls to ShutdownHost().
base::WeakPtrFactory<RenderWidgetHostViewMac> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewMac);
};
} // namespace content
#endif // CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_MAC_H_