Home | History | Annotate | Download | only in renderer_host
      1 // Copyright (c) 2011 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.
      4 
      5 #include <QuartzCore/QuartzCore.h>
      6 
      7 #include "chrome/browser/renderer_host/render_widget_host_view_mac.h"
      8 
      9 #include "base/logging.h"
     10 #include "base/mac/scoped_cftyperef.h"
     11 #import "base/mac/scoped_nsautorelease_pool.h"
     12 #include "base/metrics/histogram.h"
     13 #import "base/memory/scoped_nsobject.h"
     14 #include "base/string_util.h"
     15 #include "base/sys_info.h"
     16 #include "base/sys_string_conversions.h"
     17 #import "chrome/browser/accessibility/browser_accessibility_cocoa.h"
     18 #include "chrome/browser/accessibility/browser_accessibility_state.h"
     19 #include "chrome/browser/browser_trial.h"
     20 #include "chrome/browser/gpu_process_host_ui_shim.h"
     21 #import "chrome/browser/renderer_host/accelerated_plugin_view_mac.h"
     22 #include "chrome/browser/spellchecker_platform_engine.h"
     23 #import "chrome/browser/ui/cocoa/rwhvm_editcommand_helper.h"
     24 #import "chrome/browser/ui/cocoa/view_id_util.h"
     25 #include "chrome/common/render_messages.h"
     26 #include "content/browser/browser_thread.h"
     27 #include "content/browser/gpu_process_host.h"
     28 #include "content/browser/plugin_process_host.h"
     29 #include "content/browser/renderer_host/backing_store_mac.h"
     30 #include "content/browser/renderer_host/render_process_host.h"
     31 #include "content/browser/renderer_host/render_view_host.h"
     32 #include "content/browser/renderer_host/render_widget_host.h"
     33 #include "content/common/edit_command.h"
     34 #include "content/common/gpu_messages.h"
     35 #include "content/common/native_web_keyboard_event.h"
     36 #include "content/common/plugin_messages.h"
     37 #include "content/common/view_messages.h"
     38 #include "skia/ext/platform_canvas.h"
     39 #import "third_party/mozilla/ComplexTextInputPanel.h"
     40 #include "third_party/skia/include/core/SkColor.h"
     41 #include "third_party/WebKit/Source/WebKit/chromium/public/mac/WebInputEventFactory.h"
     42 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
     43 #include "ui/gfx/point.h"
     44 #include "ui/gfx/surface/io_surface_support_mac.h"
     45 #include "webkit/glue/webaccessibility.h"
     46 #include "webkit/plugins/npapi/webplugin.h"
     47 
     48 using WebKit::WebInputEvent;
     49 using WebKit::WebInputEventFactory;
     50 using WebKit::WebMouseEvent;
     51 using WebKit::WebMouseWheelEvent;
     52 
     53 static inline int ToWebKitModifiers(NSUInteger flags) {
     54   int modifiers = 0;
     55   if (flags & NSControlKeyMask) modifiers |= WebInputEvent::ControlKey;
     56   if (flags & NSShiftKeyMask) modifiers |= WebInputEvent::ShiftKey;
     57   if (flags & NSAlternateKeyMask) modifiers |= WebInputEvent::AltKey;
     58   if (flags & NSCommandKeyMask) modifiers |= WebInputEvent::MetaKey;
     59   return modifiers;
     60 }
     61 
     62 @interface RenderWidgetHostViewCocoa (Private)
     63 + (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event;
     64 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r;
     65 - (void)keyEvent:(NSEvent *)theEvent wasKeyEquivalent:(BOOL)equiv;
     66 - (void)cancelChildPopups;
     67 - (void)checkForPluginImeCancellation;
     68 @end
     69 
     70 // This API was published since 10.6. Provide the declaration so it can be
     71 // // called below when building with the 10.5 SDK.
     72 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
     73 @class NSTextInputContext;
     74 @interface NSResponder (AppKitDetails)
     75 - (NSTextInputContext *)inputContext;
     76 @end
     77 #endif
     78 
     79 namespace {
     80 
     81 // Maximum number of characters we allow in a tooltip.
     82 const size_t kMaxTooltipLength = 1024;
     83 
     84 // TODO(suzhe): Upstream this function.
     85 WebKit::WebColor WebColorFromNSColor(NSColor *color) {
     86   CGFloat r, g, b, a;
     87   [color getRed:&r green:&g blue:&b alpha:&a];
     88 
     89   return
     90       std::max(0, std::min(static_cast<int>(lroundf(255.0f * a)), 255)) << 24 |
     91       std::max(0, std::min(static_cast<int>(lroundf(255.0f * r)), 255)) << 16 |
     92       std::max(0, std::min(static_cast<int>(lroundf(255.0f * g)), 255)) << 8  |
     93       std::max(0, std::min(static_cast<int>(lroundf(255.0f * b)), 255));
     94 }
     95 
     96 // Extract underline information from an attributed string. Mostly copied from
     97 // third_party/WebKit/Source/WebKit/mac/WebView/WebHTMLView.mm
     98 void ExtractUnderlines(
     99     NSAttributedString* string,
    100     std::vector<WebKit::WebCompositionUnderline>* underlines) {
    101   int length = [[string string] length];
    102   int i = 0;
    103   while (i < length) {
    104     NSRange range;
    105     NSDictionary* attrs = [string attributesAtIndex:i
    106                               longestEffectiveRange:&range
    107                                             inRange:NSMakeRange(i, length - i)];
    108     if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
    109       WebKit::WebColor color = SK_ColorBLACK;
    110       if (NSColor *colorAttr =
    111           [attrs objectForKey:NSUnderlineColorAttributeName]) {
    112         color = WebColorFromNSColor(
    113             [colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
    114       }
    115       underlines->push_back(WebKit::WebCompositionUnderline(
    116           range.location, NSMaxRange(range), color, [style intValue] > 1));
    117     }
    118     i = range.location + range.length;
    119   }
    120 }
    121 
    122 // EnablePasswordInput() and DisablePasswordInput() are copied from
    123 // enableSecureTextInput() and disableSecureTextInput() functions in
    124 // third_party/WebKit/WebCore/platform/SecureTextInput.cpp
    125 // But we don't call EnableSecureEventInput() and DisableSecureEventInput()
    126 // here, because they are already called in webkit and they are system wide
    127 // functions.
    128 void EnablePasswordInput() {
    129   CFArrayRef inputSources = TISCreateASCIICapableInputSourceList();
    130   TSMSetDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag,
    131                          sizeof(CFArrayRef), &inputSources);
    132   CFRelease(inputSources);
    133 }
    134 
    135 void DisablePasswordInput() {
    136   TSMRemoveDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag);
    137 }
    138 
    139 // Adjusts an NSRect in Cocoa screen coordinates to have an origin in the upper
    140 // left of the primary screen (Carbon coordinates), and stuffs it into a
    141 // gfx::Rect.
    142 gfx::Rect FlipNSRectToRectScreen(const NSRect& rect) {
    143   gfx::Rect new_rect(NSRectToCGRect(rect));
    144   if ([[NSScreen screens] count] > 0) {
    145     new_rect.set_y([[[NSScreen screens] objectAtIndex:0] frame].size.height -
    146                    new_rect.y() - new_rect.height());
    147   }
    148   return new_rect;
    149 }
    150 
    151 // Returns the window that visually contains the given view. This is different
    152 // from [view window] in the case of tab dragging, where the view's owning
    153 // window is a floating panel attached to the actual browser window that the tab
    154 // is visually part of.
    155 NSWindow* ApparentWindowForView(NSView* view) {
    156   // TODO(shess): In case of !window, the view has been removed from
    157   // the view hierarchy because the tab isn't main.  Could retrieve
    158   // the information from the main tab for our window.
    159   NSWindow* enclosing_window = [view window];
    160 
    161   // See if this is a tab drag window. The width check is to distinguish that
    162   // case from extension popup windows.
    163   NSWindow* ancestor_window = [enclosing_window parentWindow];
    164   if (ancestor_window && (NSWidth([enclosing_window frame]) ==
    165                           NSWidth([ancestor_window frame]))) {
    166     enclosing_window = ancestor_window;
    167   }
    168 
    169   return enclosing_window;
    170 }
    171 
    172 }  // namespace
    173 
    174 // RenderWidgetHostView --------------------------------------------------------
    175 
    176 // static
    177 RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget(
    178     RenderWidgetHost* widget) {
    179   return new RenderWidgetHostViewMac(widget);
    180 }
    181 
    182 // static
    183 RenderWidgetHostView* RenderWidgetHostView::
    184     GetRenderWidgetHostViewFromNativeView(gfx::NativeView native_view) {
    185   // TODO(port)
    186   NOTREACHED() <<
    187       "RenderWidgetHostView::GetRenderWidgetHostViewFromNativeView not"
    188       "implemented";
    189   return NULL;
    190 }
    191 
    192 ///////////////////////////////////////////////////////////////////////////////
    193 // RenderWidgetHostViewMac, public:
    194 
    195 RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget)
    196     : render_widget_host_(widget),
    197       about_to_validate_and_paint_(false),
    198       call_set_needs_display_in_rect_pending_(false),
    199       text_input_type_(WebKit::WebTextInputTypeNone),
    200       is_loading_(false),
    201       is_hidden_(false),
    202       shutdown_factory_(this),
    203       needs_gpu_visibility_update_after_repaint_(false),
    204       compositing_surface_(gfx::kNullPluginWindow) {
    205   // |cocoa_view_| owns us and we will be deleted when |cocoa_view_| goes away.
    206   // Since we autorelease it, our caller must put |native_view()| into the view
    207   // hierarchy right after calling us.
    208   cocoa_view_ = [[[RenderWidgetHostViewCocoa alloc]
    209                   initWithRenderWidgetHostViewMac:this] autorelease];
    210   render_widget_host_->set_view(this);
    211 
    212   // Turn on accessibility only if VoiceOver is running.
    213   if (IsVoiceOverRunning()) {
    214     BrowserAccessibilityState::GetInstance()->OnScreenReaderDetected();
    215     render_widget_host_->EnableRendererAccessibility();
    216   }
    217 }
    218 
    219 RenderWidgetHostViewMac::~RenderWidgetHostViewMac() {
    220 }
    221 
    222 ///////////////////////////////////////////////////////////////////////////////
    223 // RenderWidgetHostViewMac, RenderWidgetHostView implementation:
    224 
    225 void RenderWidgetHostViewMac::InitAsPopup(
    226     RenderWidgetHostView* parent_host_view,
    227     const gfx::Rect& pos) {
    228   bool activatable = popup_type_ == WebKit::WebPopupTypeNone;
    229   [cocoa_view_ setCloseOnDeactivate:YES];
    230   [cocoa_view_ setCanBeKeyView:activatable ? YES : NO];
    231   [parent_host_view->GetNativeView() addSubview:cocoa_view_];
    232 
    233   NSPoint origin_global = NSPointFromCGPoint(pos.origin().ToCGPoint());
    234   if ([[NSScreen screens] count] > 0) {
    235     origin_global.y = [[[NSScreen screens] objectAtIndex:0] frame].size.height -
    236         pos.height() - origin_global.y;
    237   }
    238   NSPoint origin_window =
    239       [[cocoa_view_ window] convertScreenToBase:origin_global];
    240   NSPoint origin_view =
    241       [cocoa_view_ convertPoint:origin_window fromView:nil];
    242   NSRect initial_frame = NSMakeRect(origin_view.x,
    243                                     origin_view.y,
    244                                     pos.width(),
    245                                     pos.height());
    246   [cocoa_view_ setFrame:initial_frame];
    247 }
    248 
    249 void RenderWidgetHostViewMac::InitAsFullscreen() {
    250   NOTIMPLEMENTED() << "Full screen not implemented on Mac";
    251 }
    252 
    253 RenderWidgetHost* RenderWidgetHostViewMac::GetRenderWidgetHost() const {
    254   return render_widget_host_;
    255 }
    256 
    257 void RenderWidgetHostViewMac::DidBecomeSelected() {
    258   if (!is_hidden_)
    259     return;
    260 
    261   if (tab_switch_paint_time_.is_null())
    262     tab_switch_paint_time_ = base::TimeTicks::Now();
    263   is_hidden_ = false;
    264   render_widget_host_->WasRestored();
    265 }
    266 
    267 void RenderWidgetHostViewMac::WasHidden() {
    268   if (is_hidden_)
    269     return;
    270 
    271   // If we receive any more paint messages while we are hidden, we want to
    272   // ignore them so we don't re-allocate the backing store.  We will paint
    273   // everything again when we become selected again.
    274   is_hidden_ = true;
    275 
    276   // If we have a renderer, then inform it that we are being hidden so it can
    277   // reduce its resource utilization.
    278   render_widget_host_->WasHidden();
    279 }
    280 
    281 void RenderWidgetHostViewMac::SetSize(const gfx::Size& size) {
    282   gfx::Rect rect = GetViewBounds();
    283   rect.set_size(size);
    284   SetBounds(rect);
    285 }
    286 
    287 void RenderWidgetHostViewMac::SetBounds(const gfx::Rect& rect) {
    288   // |rect.size()| is view coordinates, |rect.origin| is screen coordinates,
    289   // TODO(thakis): fix, http://crbug.com/73362
    290   if (is_hidden_)
    291     return;
    292 
    293   // During the initial creation of the RenderWidgetHostView in
    294   // TabContents::CreateRenderViewForRenderManager, SetSize is called with an
    295   // empty size. In the Windows code flow, it is not ignored because subsequent
    296   // sizing calls from the OS flow through TCVW::WasSized which calls SetSize()
    297   // again. On Cocoa, we rely on the Cocoa view struture and resizer flags to
    298   // keep things sized properly. On the other hand, if the size is not empty
    299   // then this is a valid request for a pop-up.
    300   if (rect.size().IsEmpty())
    301     return;
    302 
    303   // Ignore the position of |rect| for non-popup rwhvs. This is because
    304   // background tabs do not have a window, but the window is required for the
    305   // coordinate conversions. Popups are always for a visible tab.
    306   if (IsPopup()) {
    307     // The position of |rect| is screen coordinate system and we have to
    308     // consider Cocoa coordinate system is upside-down and also multi-screen.
    309     NSPoint origin_global = NSPointFromCGPoint(rect.origin().ToCGPoint());
    310     if ([[NSScreen screens] count] > 0) {
    311       NSSize size = NSMakeSize(rect.width(), rect.height());
    312       size = [cocoa_view_ convertSize:size toView:nil];
    313       NSScreen* screen =
    314           static_cast<NSScreen*>([[NSScreen screens] objectAtIndex:0]);
    315       origin_global.y =
    316           NSHeight([screen frame]) - size.height - origin_global.y;
    317     }
    318 
    319     // Then |origin_global| is converted to client coordinate system.
    320     DCHECK([cocoa_view_ window]);
    321     NSPoint origin_window =
    322         [[cocoa_view_ window] convertScreenToBase:origin_global];
    323     NSPoint origin_view =
    324         [[cocoa_view_ superview] convertPoint:origin_window fromView:nil];
    325     NSRect frame = NSMakeRect(origin_view.x, origin_view.y,
    326                               rect.width(), rect.height());
    327     [cocoa_view_ setFrame:frame];
    328   } else {
    329     DCHECK([[cocoa_view_ superview] isKindOfClass:[BaseView class]]);
    330     BaseView* superview = static_cast<BaseView*>([cocoa_view_ superview]);
    331     gfx::Rect rect = [superview flipNSRectToRect:[cocoa_view_ frame]];
    332     rect.set_width(rect.width());
    333     rect.set_height(rect.height());
    334     [cocoa_view_ setFrame:[superview flipRectToNSRect:rect]];
    335   }
    336 }
    337 
    338 gfx::NativeView RenderWidgetHostViewMac::GetNativeView() {
    339   return native_view();
    340 }
    341 
    342 void RenderWidgetHostViewMac::MovePluginWindows(
    343     const std::vector<webkit::npapi::WebPluginGeometry>& moves) {
    344   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    345   // Handle movement of accelerated plugins, which are the only "windowed"
    346   // plugins that exist on the Mac.
    347   for (std::vector<webkit::npapi::WebPluginGeometry>::const_iterator iter =
    348            moves.begin();
    349        iter != moves.end();
    350        ++iter) {
    351     webkit::npapi::WebPluginGeometry geom = *iter;
    352 
    353     AcceleratedPluginView* view = ViewForPluginWindowHandle(geom.window);
    354     DCHECK(view);
    355     if (!view)
    356       continue;
    357 
    358     if (geom.rects_valid) {
    359       gfx::Rect rect = geom.window_rect;
    360       if (geom.visible) {
    361         rect.set_x(rect.x() + geom.clip_rect.x());
    362         rect.set_y(rect.y() + geom.clip_rect.y());
    363         rect.set_width(geom.clip_rect.width());
    364         rect.set_height(geom.clip_rect.height());
    365       }
    366       NSRect new_rect([cocoa_view_ flipRectToNSRect:rect]);
    367       [view setFrame:new_rect];
    368       NSMutableArray* cutout_rects =
    369           [NSMutableArray arrayWithCapacity:geom.cutout_rects.size()];
    370       for (unsigned int i = 0; i < geom.cutout_rects.size(); ++i) {
    371         // Convert to NSRect, and flip vertically.
    372         NSRect cutout_rect = NSRectFromCGRect(geom.cutout_rects[i].ToCGRect());
    373         cutout_rect.origin.y = new_rect.size.height - NSMaxY(cutout_rect);
    374         [cutout_rects addObject:[NSValue valueWithRect:cutout_rect]];
    375       }
    376       [view setCutoutRects:cutout_rects];
    377       [view setNeedsDisplay:YES];
    378     }
    379 
    380     plugin_container_manager_.SetPluginContainerGeometry(geom);
    381 
    382     BOOL visible =
    383         plugin_container_manager_.SurfaceShouldBeVisible(geom.window);
    384     [view setHidden:!visible];
    385   }
    386 }
    387 
    388 void RenderWidgetHostViewMac::Focus() {
    389   [[cocoa_view_ window] makeFirstResponder:cocoa_view_];
    390 }
    391 
    392 void RenderWidgetHostViewMac::Blur() {
    393   [[cocoa_view_ window] makeFirstResponder:nil];
    394 }
    395 
    396 bool RenderWidgetHostViewMac::HasFocus() {
    397   return [[cocoa_view_ window] firstResponder] == cocoa_view_;
    398 }
    399 
    400 void RenderWidgetHostViewMac::Show() {
    401   [cocoa_view_ setHidden:NO];
    402 
    403   DidBecomeSelected();
    404 }
    405 
    406 void RenderWidgetHostViewMac::Hide() {
    407   [cocoa_view_ setHidden:YES];
    408 
    409   WasHidden();
    410 }
    411 
    412 bool RenderWidgetHostViewMac::IsShowing() {
    413   return ![cocoa_view_ isHidden];
    414 }
    415 
    416 gfx::Rect RenderWidgetHostViewMac::GetViewBounds() const {
    417   // TODO(shess): In case of !window, the view has been removed from
    418   // the view hierarchy because the tab isn't main.  Could retrieve
    419   // the information from the main tab for our window.
    420   NSWindow* enclosing_window = ApparentWindowForView(cocoa_view_);
    421   if (!enclosing_window)
    422     return gfx::Rect();
    423 
    424   NSRect bounds = [cocoa_view_ bounds];
    425   bounds = [cocoa_view_ convertRect:bounds toView:nil];
    426   bounds.origin = [enclosing_window convertBaseToScreen:bounds.origin];
    427   return FlipNSRectToRectScreen(bounds);
    428 }
    429 
    430 void RenderWidgetHostViewMac::UpdateCursor(const WebCursor& cursor) {
    431   current_cursor_ = cursor;
    432   UpdateCursorIfOverSelf();
    433 }
    434 
    435 void RenderWidgetHostViewMac::UpdateCursorIfOverSelf() {
    436   // Do something special (as Win Chromium does) for arrow cursor while loading
    437   // a page? TODO(avi): decide
    438   // Can we synchronize to the event stream? Switch to -[NSWindow
    439   // mouseLocationOutsideOfEventStream] if we cannot. TODO(avi): test and see
    440   NSEvent* event = [[cocoa_view_ window] currentEvent];
    441   if ([event window] != [cocoa_view_ window])
    442     return;
    443 
    444   NSPoint event_location = [event locationInWindow];
    445   NSPoint local_point = [cocoa_view_ convertPoint:event_location fromView:nil];
    446 
    447   if (!NSPointInRect(local_point, [cocoa_view_ bounds]))
    448     return;
    449 
    450   NSCursor* ns_cursor = current_cursor_.GetCursor();
    451   [ns_cursor set];
    452 }
    453 
    454 void RenderWidgetHostViewMac::SetIsLoading(bool is_loading) {
    455   is_loading_ = is_loading;
    456   UpdateCursorIfOverSelf();
    457 }
    458 
    459 void RenderWidgetHostViewMac::ImeUpdateTextInputState(
    460     WebKit::WebTextInputType type,
    461     const gfx::Rect& caret_rect) {
    462   if (text_input_type_ != type) {
    463     text_input_type_ = type;
    464     if (HasFocus())
    465       SetTextInputActive(true);
    466   }
    467 
    468   // We need to convert the coordinate of the cursor rectangle sent from the
    469   // renderer and save it. Our input method backend uses a coordinate system
    470   // whose origin is the upper-left corner of this view. On the other hand,
    471   // Cocoa uses a coordinate system whose origin is the lower-left corner of
    472   // this view. So, we convert the cursor rectangle and save it.
    473   [cocoa_view_ setCaretRect:[cocoa_view_ flipRectToNSRect:caret_rect]];
    474 }
    475 
    476 void RenderWidgetHostViewMac::ImeCancelComposition() {
    477   [cocoa_view_ cancelComposition];
    478 }
    479 
    480 void RenderWidgetHostViewMac::DidUpdateBackingStore(
    481     const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy,
    482     const std::vector<gfx::Rect>& copy_rects) {
    483   if (!is_hidden_) {
    484     std::vector<gfx::Rect> rects(copy_rects);
    485 
    486     // Because the findbar might be open, we cannot use scrollRect:by: here. For
    487     // now, simply mark all of scroll rect as dirty.
    488     if (!scroll_rect.IsEmpty())
    489       rects.push_back(scroll_rect);
    490 
    491     for (size_t i = 0; i < rects.size(); ++i) {
    492       NSRect ns_rect = [cocoa_view_ flipRectToNSRect:rects[i]];
    493 
    494       if (about_to_validate_and_paint_) {
    495         // As much as we'd like to use -setNeedsDisplayInRect: here, we can't.
    496         // We're in the middle of executing a -drawRect:, and as soon as it
    497         // returns Cocoa will clear its record of what needs display. We
    498         // instead use |performSelector:| to call |setNeedsDisplayInRect:|
    499         // after returning to the main loop, at which point |drawRect:| is no
    500         // longer on the stack.
    501         DCHECK([NSThread isMainThread]);
    502         if (!call_set_needs_display_in_rect_pending_) {
    503           [cocoa_view_ performSelector:@selector(callSetNeedsDisplayInRect)
    504                        withObject:nil
    505                        afterDelay:0];
    506           call_set_needs_display_in_rect_pending_ = true;
    507           invalid_rect_ = ns_rect;
    508         } else {
    509           // The old invalid rect is probably invalid now, since the view has
    510           // most likely been resized, but there's no harm in dirtying the
    511           // union.  In the limit, this becomes equivalent to dirtying the
    512           // whole view.
    513           invalid_rect_ = NSUnionRect(invalid_rect_, ns_rect);
    514         }
    515       } else {
    516         [cocoa_view_ setNeedsDisplayInRect:ns_rect];
    517       }
    518     }
    519 
    520     if (!about_to_validate_and_paint_)
    521       [cocoa_view_ displayIfNeeded];
    522   }
    523 
    524   // If |about_to_validate_and_paint_| is set, then -drawRect: is on the stack
    525   // and it's not allowed to call -setHidden on the accelerated view.  In that
    526   // case, -callSetNeedsDisplayInRect: will hide it later.
    527   // If |about_to_validate_and_paint_| is not set, do it now.
    528   if (!about_to_validate_and_paint_)
    529     HandleDelayedGpuViewHiding();
    530 }
    531 
    532 void RenderWidgetHostViewMac::RenderViewGone(base::TerminationStatus status,
    533                                              int error_code) {
    534   // TODO(darin): keep this around, and draw sad-tab into it.
    535   UpdateCursorIfOverSelf();
    536   Destroy();
    537 }
    538 
    539 void RenderWidgetHostViewMac::Destroy() {
    540   // On Windows, popups are implemented with a popup window style, so that when
    541   // an event comes in that would "cancel" it, it receives the OnCancelMode
    542   // message and can kill itself. Alas, on the Mac, views cannot capture events
    543   // outside of themselves. On Windows, if Destroy is being called on a view,
    544   // then the event causing the destroy had also cancelled any popups by the
    545   // time Destroy() was called. On the Mac we have to destroy all the popups
    546   // ourselves.
    547 
    548   // Depth-first destroy all popups. Use ShutdownHost() to enforce
    549   // deepest-first ordering.
    550   for (NSView* subview in [cocoa_view_ subviews]) {
    551     if ([subview isKindOfClass:[RenderWidgetHostViewCocoa class]]) {
    552       [static_cast<RenderWidgetHostViewCocoa*>(subview)
    553           renderWidgetHostViewMac]->ShutdownHost();
    554     } else if ([subview isKindOfClass:[AcceleratedPluginView class]]) {
    555       [static_cast<AcceleratedPluginView*>(subview)
    556           onRenderWidgetHostViewGone];
    557     }
    558   }
    559 
    560   // We've been told to destroy.
    561   [cocoa_view_ retain];
    562   [cocoa_view_ removeFromSuperview];
    563   [cocoa_view_ autorelease];
    564 
    565   // We get this call just before |render_widget_host_| deletes
    566   // itself.  But we are owned by |cocoa_view_|, which may be retained
    567   // by some other code.  Examples are TabContentsViewMac's
    568   // |latent_focus_view_| and TabWindowController's
    569   // |cachedContentView_|.
    570   render_widget_host_ = NULL;
    571 }
    572 
    573 // Called from the renderer to tell us what the tooltip text should be. It
    574 // calls us frequently so we need to cache the value to prevent doing a lot
    575 // of repeat work.
    576 void RenderWidgetHostViewMac::SetTooltipText(const std::wstring& tooltip_text) {
    577   if (tooltip_text != tooltip_text_ && [[cocoa_view_ window] isKeyWindow]) {
    578     tooltip_text_ = tooltip_text;
    579 
    580     // Clamp the tooltip length to kMaxTooltipLength. It's a DOS issue on
    581     // Windows; we're just trying to be polite. Don't persist the trimmed
    582     // string, as then the comparison above will always fail and we'll try to
    583     // set it again every single time the mouse moves.
    584     std::wstring display_text = tooltip_text_;
    585     if (tooltip_text_.length() > kMaxTooltipLength)
    586       display_text = tooltip_text_.substr(0, kMaxTooltipLength);
    587 
    588     NSString* tooltip_nsstring = base::SysWideToNSString(display_text);
    589     [cocoa_view_ setToolTipAtMousePoint:tooltip_nsstring];
    590   }
    591 }
    592 
    593 //
    594 // RenderWidgetHostViewCocoa uses the stored selection text,
    595 // which implements NSServicesRequests protocol.
    596 //
    597 void RenderWidgetHostViewMac::SelectionChanged(const std::string& text) {
    598   selected_text_ = text;
    599 }
    600 
    601 bool RenderWidgetHostViewMac::IsPopup() const {
    602   return popup_type_ != WebKit::WebPopupTypeNone;
    603 }
    604 
    605 BackingStore* RenderWidgetHostViewMac::AllocBackingStore(
    606     const gfx::Size& size) {
    607   return new BackingStoreMac(render_widget_host_, size);
    608 }
    609 
    610 // Sets whether or not to accept first responder status.
    611 void RenderWidgetHostViewMac::SetTakesFocusOnlyOnMouseDown(bool flag) {
    612   [cocoa_view_ setTakesFocusOnlyOnMouseDown:flag];
    613 }
    614 
    615 void RenderWidgetHostViewMac::KillSelf() {
    616   if (shutdown_factory_.empty()) {
    617     [cocoa_view_ setHidden:YES];
    618     MessageLoop::current()->PostTask(FROM_HERE,
    619         shutdown_factory_.NewRunnableMethod(
    620             &RenderWidgetHostViewMac::ShutdownHost));
    621   }
    622 }
    623 
    624 void RenderWidgetHostViewMac::PluginFocusChanged(bool focused, int plugin_id) {
    625   [cocoa_view_ pluginFocusChanged:(focused ? YES : NO) forPlugin:plugin_id];
    626 }
    627 
    628 void RenderWidgetHostViewMac::StartPluginIme() {
    629   [cocoa_view_ setPluginImeActive:YES];
    630 }
    631 
    632 bool RenderWidgetHostViewMac::PostProcessEventForPluginIme(
    633     const NativeWebKeyboardEvent& event) {
    634   // Check WebInputEvent type since multiple types of events can be sent into
    635   // WebKit for the same OS event (e.g., RawKeyDown and Char), so filtering is
    636   // necessary to avoid double processing.
    637   // Also check the native type, since NSFlagsChanged is considered a key event
    638   // for WebKit purposes, but isn't considered a key event by the OS.
    639   if (event.type == WebInputEvent::RawKeyDown &&
    640       [event.os_event type] == NSKeyDown)
    641     return [cocoa_view_ postProcessEventForPluginIme:event.os_event];
    642   return false;
    643 }
    644 
    645 void RenderWidgetHostViewMac::PluginImeCompositionCompleted(
    646     const string16& text, int plugin_id) {
    647   if (render_widget_host_) {
    648     render_widget_host_->Send(new ViewMsg_PluginImeCompositionCompleted(
    649         render_widget_host_->routing_id(), text, plugin_id));
    650   }
    651 }
    652 
    653 gfx::PluginWindowHandle
    654 RenderWidgetHostViewMac::AllocateFakePluginWindowHandle(bool opaque,
    655                                                         bool root) {
    656   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    657 
    658   // |render_widget_host_| is set to NULL when |RWHVMac::Destroy()| has
    659   // completed. If |AllocateFakePluginWindowHandle()| is called after that,
    660   // we will crash when the AcceleratedPluginView we allocate below is
    661   // destroyed.
    662   DCHECK(render_widget_host_);
    663 
    664   // Create an NSView to host the plugin's/compositor's pixels.
    665   gfx::PluginWindowHandle handle =
    666       plugin_container_manager_.AllocateFakePluginWindowHandle(opaque, root);
    667 
    668   scoped_nsobject<AcceleratedPluginView> plugin_view(
    669       [[AcceleratedPluginView alloc] initWithRenderWidgetHostViewMac:this
    670                                                         pluginHandle:handle]);
    671   [plugin_view setHidden:YES];
    672 
    673   [cocoa_view_ addSubview:plugin_view];
    674   plugin_views_[handle] = plugin_view;
    675 
    676   return handle;
    677 }
    678 
    679 void RenderWidgetHostViewMac::DestroyFakePluginWindowHandle(
    680     gfx::PluginWindowHandle window) {
    681   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    682   PluginViewMap::iterator it = plugin_views_.find(window);
    683   DCHECK(plugin_views_.end() != it);
    684   if (plugin_views_.end() == it) {
    685     return;
    686   }
    687   [it->second removeFromSuperview];
    688   plugin_views_.erase(it);
    689 
    690   // The view's dealloc will call DeallocFakePluginWindowHandle(), which will
    691   // remove the handle from |plugin_container_manager_|. This code path is
    692   // taken if a plugin is removed, but the RWHVMac itself stays alive.
    693 }
    694 
    695 // This is called by AcceleratedPluginView's -dealloc.
    696 void RenderWidgetHostViewMac::DeallocFakePluginWindowHandle(
    697     gfx::PluginWindowHandle window) {
    698   // When a browser window with a GpuScheduler is closed, the render process
    699   // will attempt to finish all GL commands. It will busy-wait on the GPU
    700   // process until the command queue is empty. If a paint is pending, the GPU
    701   // process won't process any GL commands until the browser sends a paint ack,
    702   // but since the browser window is already closed, it will never arrive.
    703   // To resolve this we ask the GPU process to destroy the command buffer
    704   // associated with the given render widget.  Once the command buffer is
    705   // destroyed, all GL commands from the renderer will immediately receive
    706   // channel error.
    707   if (render_widget_host_ &&
    708       plugin_container_manager_.IsRootContainer(window)) {
    709     GpuProcessHost::SendOnIO(
    710         render_widget_host_->process()->id(),
    711         content::CAUSE_FOR_GPU_LAUNCH_NO_LAUNCH,
    712         new GpuMsg_DestroyCommandBuffer(
    713             render_widget_host_->process()->id(),
    714             render_widget_host_->routing_id()));
    715   }
    716 
    717   plugin_container_manager_.DestroyFakePluginWindowHandle(window);
    718 }
    719 
    720 AcceleratedPluginView* RenderWidgetHostViewMac::ViewForPluginWindowHandle(
    721     gfx::PluginWindowHandle window) {
    722   PluginViewMap::iterator it = plugin_views_.find(window);
    723   DCHECK(plugin_views_.end() != it);
    724   if (plugin_views_.end() == it)
    725     return nil;
    726   return it->second;
    727 }
    728 
    729 void RenderWidgetHostViewMac::AcceleratedSurfaceSetIOSurface(
    730     gfx::PluginWindowHandle window,
    731     int32 width,
    732     int32 height,
    733     uint64 io_surface_identifier) {
    734   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    735   plugin_container_manager_.SetSizeAndIOSurface(window,
    736                                                 width,
    737                                                 height,
    738                                                 io_surface_identifier);
    739 
    740   if (plugin_container_manager_.IsRootContainer(window)) {
    741     // Fake up a WebPluginGeometry for the root window to set the
    742     // container's size; we will never get a notification from the
    743     // browser about the root window, only plugins.
    744     webkit::npapi::WebPluginGeometry geom;
    745     gfx::Rect rect(0, 0, width, height);
    746     geom.window = window;
    747     geom.window_rect = rect;
    748     geom.clip_rect = rect;
    749     geom.visible = true;
    750     geom.rects_valid = true;
    751     MovePluginWindows(std::vector<webkit::npapi::WebPluginGeometry>(1, geom));
    752   }
    753 }
    754 
    755 void RenderWidgetHostViewMac::AcceleratedSurfaceSetTransportDIB(
    756     gfx::PluginWindowHandle window,
    757     int32 width,
    758     int32 height,
    759     TransportDIB::Handle transport_dib) {
    760   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    761   plugin_container_manager_.SetSizeAndTransportDIB(window,
    762                                                    width,
    763                                                    height,
    764                                                    transport_dib);
    765 }
    766 
    767 void RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped(
    768     gfx::PluginWindowHandle window,
    769     uint64 surface_id,
    770     int renderer_id,
    771     int32 route_id,
    772     int gpu_host_id,
    773     uint64 swap_buffers_count) {
    774   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    775   AcceleratedPluginView* view = ViewForPluginWindowHandle(window);
    776   DCHECK(view);
    777   if (!view)
    778     return;
    779 
    780   plugin_container_manager_.SetSurfaceWasPaintedTo(window, surface_id);
    781 
    782   // The surface is hidden until its first paint, to not show gargabe.
    783   if (plugin_container_manager_.SurfaceShouldBeVisible(window))
    784     [view setHidden:NO];
    785   [view updateSwapBuffersCount:swap_buffers_count
    786                   fromRenderer:renderer_id
    787                        routeId:route_id
    788                      gpuHostId:gpu_host_id];
    789 }
    790 
    791 void RenderWidgetHostViewMac::UpdateRootGpuViewVisibility(
    792     bool show_gpu_widget) {
    793   // Plugins are destroyed on page navigate. The compositor layer on the other
    794   // hand is created on demand and then stays alive until its renderer process
    795   // dies (usually on cross-domain navigation). Instead, only a flag
    796   // |is_accelerated_compositing_active()| is flipped when the compositor output
    797   // should be shown/hidden.
    798   // Show/hide the view belonging to the compositor here.
    799   plugin_container_manager_.set_gpu_rendering_active(show_gpu_widget);
    800 
    801   gfx::PluginWindowHandle root_handle =
    802       plugin_container_manager_.root_container_handle();
    803   if (root_handle != gfx::kNullPluginWindow) {
    804     AcceleratedPluginView* view = ViewForPluginWindowHandle(root_handle);
    805     DCHECK(view);
    806     bool visible =
    807         plugin_container_manager_.SurfaceShouldBeVisible(root_handle);
    808     [[view window] disableScreenUpdatesUntilFlush];
    809     [view setHidden:!visible];
    810   }
    811 }
    812 
    813 void RenderWidgetHostViewMac::HandleDelayedGpuViewHiding() {
    814   if (needs_gpu_visibility_update_after_repaint_) {
    815     UpdateRootGpuViewVisibility(false);
    816     needs_gpu_visibility_update_after_repaint_ = false;
    817   }
    818 }
    819 
    820 void RenderWidgetHostViewMac::AcknowledgeSwapBuffers(
    821     int renderer_id,
    822     int32 route_id,
    823     int gpu_host_id,
    824     uint64 swap_buffers_count) {
    825   // Called on the display link thread. Hand actual work off to the IO thread,
    826   // because |GpuProcessHost::Get()| can only be called there.
    827   // Currently, this is never called for plugins.
    828   if (render_widget_host_) {
    829     DCHECK_EQ(render_widget_host_->process()->id(), renderer_id);
    830     // |render_widget_host_->routing_id()| and |route_id| are usually not
    831     // equal: The former identifies the channel from the RWH in the browser
    832     // process to the corresponding render widget in the renderer process, while
    833     // the latter identifies the channel from the GpuCommandBufferStub in the
    834     // GPU process to the corresponding command buffer client in the renderer.
    835   }
    836 
    837   // TODO(apatrick): Send the acknowledgement via the UI thread when running in
    838   // single process or in process GPU mode for now. This is bad from a
    839   // performance point of view but the plan is to not use AcceleratedSurface at
    840   // all in these cases.
    841   if (gpu_host_id == 0) {
    842     BrowserThread::PostTask(
    843         BrowserThread::UI,
    844         FROM_HERE,
    845         NewRunnableFunction(&GpuProcessHostUIShim::SendToGpuHost,
    846                             gpu_host_id,
    847                             new GpuMsg_AcceleratedSurfaceBuffersSwappedACK(
    848                                 renderer_id,
    849                                 route_id,
    850                                 swap_buffers_count)));
    851   } else {
    852     GpuProcessHost::SendOnIO(
    853         gpu_host_id,
    854         content::CAUSE_FOR_GPU_LAUNCH_NO_LAUNCH,
    855         new GpuMsg_AcceleratedSurfaceBuffersSwappedACK(
    856             renderer_id, route_id, swap_buffers_count));
    857   }
    858 }
    859 
    860 void RenderWidgetHostViewMac::GpuRenderingStateDidChange() {
    861   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    862   if (GetRenderWidgetHost()->is_accelerated_compositing_active()) {
    863     UpdateRootGpuViewVisibility(
    864         GetRenderWidgetHost()->is_accelerated_compositing_active());
    865   } else {
    866     needs_gpu_visibility_update_after_repaint_ = true;
    867   }
    868 }
    869 
    870 gfx::PluginWindowHandle RenderWidgetHostViewMac::GetCompositingSurface() {
    871   if (compositing_surface_ == gfx::kNullPluginWindow)
    872     compositing_surface_ = AllocateFakePluginWindowHandle(
    873         /*opaque=*/true, /*root=*/true);
    874   return compositing_surface_;
    875 }
    876 
    877 void RenderWidgetHostViewMac::DrawAcceleratedSurfaceInstance(
    878       CGLContextObj context,
    879       gfx::PluginWindowHandle plugin_handle,
    880       NSSize size) {
    881   // Called on the display link thread.
    882   CGLSetCurrentContext(context);
    883 
    884   glMatrixMode(GL_PROJECTION);
    885   glLoadIdentity();
    886   // Note that we place the origin at the upper left corner with +y
    887   // going down
    888   glOrtho(0, size.width, size.height, 0, -1, 1);
    889   glMatrixMode(GL_MODELVIEW);
    890   glLoadIdentity();
    891 
    892   plugin_container_manager_.Draw(context, plugin_handle);
    893 }
    894 
    895 void RenderWidgetHostViewMac::ForceTextureReload() {
    896   plugin_container_manager_.ForceTextureReload();
    897 }
    898 
    899 void RenderWidgetHostViewMac::SetVisuallyDeemphasized(const SkColor* color,
    900                                                       bool animate) {
    901   // This is not used on mac.
    902 }
    903 
    904 void RenderWidgetHostViewMac::ShutdownHost() {
    905   shutdown_factory_.RevokeAll();
    906   render_widget_host_->Shutdown();
    907   // Do not touch any members at this point, |this| has been deleted.
    908 }
    909 
    910 bool RenderWidgetHostViewMac::IsVoiceOverRunning() {
    911   NSUserDefaults* user_defaults = [NSUserDefaults standardUserDefaults];
    912   [user_defaults addSuiteNamed:@"com.apple.universalaccess"];
    913   return 1 == [user_defaults integerForKey:@"voiceOverOnOffKey"];
    914 }
    915 
    916 gfx::Rect RenderWidgetHostViewMac::GetViewCocoaBounds() const {
    917   return gfx::Rect(NSRectToCGRect([cocoa_view_ bounds]));
    918 }
    919 
    920 gfx::Rect RenderWidgetHostViewMac::GetRootWindowRect() {
    921   // TODO(shess): In case of !window, the view has been removed from
    922   // the view hierarchy because the tab isn't main.  Could retrieve
    923   // the information from the main tab for our window.
    924   NSWindow* enclosing_window = ApparentWindowForView(cocoa_view_);
    925   if (!enclosing_window)
    926     return gfx::Rect();
    927 
    928   NSRect bounds = [enclosing_window frame];
    929   return FlipNSRectToRectScreen(bounds);
    930 }
    931 
    932 void RenderWidgetHostViewMac::SetActive(bool active) {
    933   if (render_widget_host_)
    934     render_widget_host_->SetActive(active);
    935   if (HasFocus())
    936     SetTextInputActive(active);
    937   if (!active)
    938     [cocoa_view_ setPluginImeActive:NO];
    939 }
    940 
    941 void RenderWidgetHostViewMac::SetWindowVisibility(bool visible) {
    942   if (render_widget_host_) {
    943     render_widget_host_->Send(new ViewMsg_SetWindowVisibility(
    944         render_widget_host_->routing_id(), visible));
    945   }
    946 }
    947 
    948 void RenderWidgetHostViewMac::WindowFrameChanged() {
    949   if (render_widget_host_) {
    950     render_widget_host_->Send(new ViewMsg_WindowFrameChanged(
    951         render_widget_host_->routing_id(), GetRootWindowRect(),
    952         GetViewBounds()));
    953   }
    954 }
    955 
    956 void RenderWidgetHostViewMac::SetBackground(const SkBitmap& background) {
    957   RenderWidgetHostView::SetBackground(background);
    958   if (render_widget_host_)
    959     render_widget_host_->Send(new ViewMsg_SetBackground(
    960         render_widget_host_->routing_id(), background));
    961 }
    962 
    963 bool RenderWidgetHostViewMac::ContainsNativeView(
    964     gfx::NativeView native_view) const {
    965   // TODO(port)
    966   NOTREACHED() <<
    967     "RenderWidgetHostViewMac::ContainsNativeView not implemented.";
    968   return false;
    969 }
    970 
    971 void RenderWidgetHostViewMac::OnAccessibilityNotifications(
    972     const std::vector<ViewHostMsg_AccessibilityNotification_Params>& params) {
    973   if (!browser_accessibility_manager_.get()) {
    974     // Use empty document to process notifications
    975     webkit_glue::WebAccessibility empty_document;
    976     empty_document.role = WebAccessibility::ROLE_WEB_AREA;
    977     empty_document.state = 0;
    978     browser_accessibility_manager_.reset(
    979         BrowserAccessibilityManager::Create(cocoa_view_, empty_document, NULL));
    980   }
    981   browser_accessibility_manager_->OnAccessibilityNotifications(params);
    982 }
    983 
    984 void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
    985   if (active) {
    986     if (text_input_type_ == WebKit::WebTextInputTypePassword)
    987       EnablePasswordInput();
    988     else
    989       DisablePasswordInput();
    990   } else {
    991     if (text_input_type_ == WebKit::WebTextInputTypePassword)
    992       DisablePasswordInput();
    993   }
    994 }
    995 
    996 // RenderWidgetHostViewCocoa ---------------------------------------------------
    997 
    998 @implementation RenderWidgetHostViewCocoa
    999 
   1000 @synthesize caretRect = caretRect_;
   1001 
   1002 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r {
   1003   self = [super initWithFrame:NSZeroRect];
   1004   if (self) {
   1005     editCommand_helper_.reset(new RWHVMEditCommandHelper);
   1006     editCommand_helper_->AddEditingSelectorsToClass([self class]);
   1007 
   1008     renderWidgetHostView_.reset(r);
   1009     canBeKeyView_ = YES;
   1010     focusedPluginIdentifier_ = -1;
   1011   }
   1012   return self;
   1013 }
   1014 
   1015 - (void)setCanBeKeyView:(BOOL)can {
   1016   canBeKeyView_ = can;
   1017 }
   1018 
   1019 - (void)setTakesFocusOnlyOnMouseDown:(BOOL)b {
   1020   takesFocusOnlyOnMouseDown_ = b;
   1021 }
   1022 
   1023 - (void)setCloseOnDeactivate:(BOOL)b {
   1024   closeOnDeactivate_ = b;
   1025 }
   1026 
   1027 - (BOOL)shouldIgnoreMouseEvent:(NSEvent*)theEvent {
   1028   NSWindow* window = [self window];
   1029   // If this is a background window, don't handle mouse movement events. This
   1030   // is the expected behavior on the Mac as evidenced by other applications.
   1031   // Do this only if the window level is NSNormalWindowLevel, as this
   1032   // does not necessarily apply in other contexts (e.g. balloons).
   1033   if ([theEvent type] == NSMouseMoved &&
   1034       [window level] == NSNormalWindowLevel && ![window isKeyWindow]) {
   1035     return YES;
   1036   }
   1037 
   1038   // Use hitTest to check whether the mouse is over a nonWebContentView - in
   1039   // which case the mouse event should not be handled by the render host.
   1040   const SEL nonWebContentViewSelector = @selector(nonWebContentView);
   1041   NSView* contentView = [window contentView];
   1042   NSView* view = [contentView hitTest:[theEvent locationInWindow]];
   1043   // Traverse the superview hierarchy as the hitTest will return the frontmost
   1044   // view, such as an NSTextView, while nonWebContentView may be specified by
   1045   // its parent view.
   1046   while (view) {
   1047     if ([view respondsToSelector:nonWebContentViewSelector] &&
   1048         [view performSelector:nonWebContentViewSelector]) {
   1049       // The cursor is over a nonWebContentView - ignore this mouse event.
   1050       return YES;
   1051     }
   1052     view = [view superview];
   1053   }
   1054   return NO;
   1055 }
   1056 
   1057 - (void)mouseEvent:(NSEvent*)theEvent {
   1058   if ([self shouldIgnoreMouseEvent:theEvent]) {
   1059     // If this is the first such event, send a mouse exit to the host view.
   1060     if (!mouseEventWasIgnored_ && renderWidgetHostView_->render_widget_host_) {
   1061       WebMouseEvent exitEvent =
   1062           WebInputEventFactory::mouseEvent(theEvent, self);
   1063       exitEvent.type = WebInputEvent::MouseLeave;
   1064       exitEvent.button = WebMouseEvent::ButtonNone;
   1065       renderWidgetHostView_->render_widget_host_->ForwardMouseEvent(exitEvent);
   1066     }
   1067     mouseEventWasIgnored_ = YES;
   1068     return;
   1069   }
   1070 
   1071   if (mouseEventWasIgnored_) {
   1072     // If this is the first mouse event after a previous event that was ignored
   1073     // due to the hitTest, send a mouse enter event to the host view.
   1074     if (renderWidgetHostView_->render_widget_host_) {
   1075       WebMouseEvent enterEvent =
   1076           WebInputEventFactory::mouseEvent(theEvent, self);
   1077       enterEvent.type = WebInputEvent::MouseMove;
   1078       enterEvent.button = WebMouseEvent::ButtonNone;
   1079       renderWidgetHostView_->render_widget_host_->ForwardMouseEvent(enterEvent);
   1080     }
   1081   }
   1082   mouseEventWasIgnored_ = NO;
   1083 
   1084   // TODO(rohitrao): Probably need to handle other mouse down events here.
   1085   if ([theEvent type] == NSLeftMouseDown && takesFocusOnlyOnMouseDown_) {
   1086     if (renderWidgetHostView_->render_widget_host_)
   1087       renderWidgetHostView_->render_widget_host_->OnMouseActivate();
   1088 
   1089     // Manually take focus after the click but before forwarding it to the
   1090     // renderer.
   1091     [[self window] makeFirstResponder:self];
   1092   }
   1093 
   1094   // Don't cancel child popups; killing them on a mouse click would prevent the
   1095   // user from positioning the insertion point in the text field spawning the
   1096   // popup. A click outside the text field would cause the text field to drop
   1097   // the focus, and then EditorClientImpl::textFieldDidEndEditing() would cancel
   1098   // the popup anyway, so we're OK.
   1099 
   1100   NSEventType type = [theEvent type];
   1101   if (type == NSLeftMouseDown)
   1102     hasOpenMouseDown_ = YES;
   1103   else if (type == NSLeftMouseUp)
   1104     hasOpenMouseDown_ = NO;
   1105 
   1106   // TODO(suzhe): We should send mouse events to the input method first if it
   1107   // wants to handle them. But it won't work without implementing method
   1108   // - (NSUInteger)characterIndexForPoint:.
   1109   // See: http://code.google.com/p/chromium/issues/detail?id=47141
   1110   // Instead of sending mouse events to the input method first, we now just
   1111   // simply confirm all ongoing composition here.
   1112   if (type == NSLeftMouseDown || type == NSRightMouseDown ||
   1113       type == NSOtherMouseDown) {
   1114     [self confirmComposition];
   1115   }
   1116 
   1117   const WebMouseEvent& event =
   1118       WebInputEventFactory::mouseEvent(theEvent, self);
   1119 
   1120   if (renderWidgetHostView_->render_widget_host_)
   1121     renderWidgetHostView_->render_widget_host_->ForwardMouseEvent(event);
   1122 }
   1123 
   1124 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent {
   1125   // |performKeyEquivalent:| is sent to all views of a window, not only down the
   1126   // responder chain (cf. "Handling Key Equivalents" in
   1127   // http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/EventOverview/HandlingKeyEvents/HandlingKeyEvents.html
   1128   // ). We only want to handle key equivalents if we're first responder.
   1129   if ([[self window] firstResponder] != self)
   1130     return NO;
   1131 
   1132   // If we return |NO| from this function, cocoa will send the key event to
   1133   // the menu and only if the menu does not process the event to |keyDown:|. We
   1134   // want to send the event to a renderer _before_ sending it to the menu, so
   1135   // we need to return |YES| for all events that might be swallowed by the menu.
   1136   // We do not return |YES| for every keypress because we don't get |keyDown:|
   1137   // events for keys that we handle this way.
   1138   NSUInteger modifierFlags = [theEvent modifierFlags];
   1139   if ((modifierFlags & NSCommandKeyMask) == 0) {
   1140     // Make sure the menu does not contain key equivalents that don't
   1141     // contain cmd.
   1142     DCHECK(![[NSApp mainMenu] performKeyEquivalent:theEvent]);
   1143     return NO;
   1144   }
   1145 
   1146   // Command key combinations are sent via performKeyEquivalent rather than
   1147   // keyDown:. We just forward this on and if WebCore doesn't want to handle
   1148   // it, we let the TabContentsView figure out how to reinject it.
   1149   [self keyEvent:theEvent wasKeyEquivalent:YES];
   1150   return YES;
   1151 }
   1152 
   1153 - (BOOL)_wantsKeyDownForEvent:(NSEvent*)event {
   1154   // This is a SPI that AppKit apparently calls after |performKeyEquivalent:|
   1155   // returned NO. If this function returns |YES|, Cocoa sends the event to
   1156   // |keyDown:| instead of doing other things with it. Ctrl-tab will be sent
   1157   // to us instead of doing key view loop control, ctrl-left/right get handled
   1158   // correctly, etc.
   1159   // (However, there are still some keys that Cocoa swallows, e.g. the key
   1160   // equivalent that Cocoa uses for toggling the input langauge. In this case,
   1161   // that's actually a good thing, though -- see http://crbug.com/26115 .)
   1162   return YES;
   1163 }
   1164 
   1165 - (void)keyEvent:(NSEvent*)theEvent {
   1166   [self keyEvent:theEvent wasKeyEquivalent:NO];
   1167 }
   1168 
   1169 - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv {
   1170   DCHECK([theEvent type] != NSKeyDown ||
   1171          !equiv == !([theEvent modifierFlags] & NSCommandKeyMask));
   1172 
   1173   if ([theEvent type] == NSFlagsChanged) {
   1174     // Ignore NSFlagsChanged events from the NumLock and Fn keys as
   1175     // Safari does in -[WebHTMLView flagsChanged:] (of "WebHTMLView.mm").
   1176     int keyCode = [theEvent keyCode];
   1177     if (!keyCode || keyCode == 10 || keyCode == 63)
   1178       return;
   1179   }
   1180 
   1181   // Don't cancel child popups; the key events are probably what's triggering
   1182   // the popup in the first place.
   1183 
   1184   RenderWidgetHost* widgetHost = renderWidgetHostView_->render_widget_host_;
   1185   DCHECK(widgetHost);
   1186 
   1187   NativeWebKeyboardEvent event(theEvent);
   1188 
   1189   // We only handle key down events and just simply forward other events.
   1190   if ([theEvent type] != NSKeyDown) {
   1191     widgetHost->ForwardKeyboardEvent(event);
   1192 
   1193     // Possibly autohide the cursor.
   1194     if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent])
   1195       [NSCursor setHiddenUntilMouseMoves:YES];
   1196 
   1197     return;
   1198   }
   1199 
   1200   scoped_nsobject<RenderWidgetHostViewCocoa> keepSelfAlive([self retain]);
   1201 
   1202   // Records the current marked text state, so that we can know if the marked
   1203   // text was deleted or not after handling the key down event.
   1204   BOOL oldHasMarkedText = hasMarkedText_;
   1205 
   1206   // This method should not be called recursively.
   1207   DCHECK(!handlingKeyDown_);
   1208 
   1209   // Tells insertText: and doCommandBySelector: that we are handling a key
   1210   // down event.
   1211   handlingKeyDown_ = YES;
   1212 
   1213   // These variables might be set when handling the keyboard event.
   1214   // Clear them here so that we can know whether they have changed afterwards.
   1215   textToBeInserted_.clear();
   1216   markedText_.clear();
   1217   underlines_.clear();
   1218   unmarkTextCalled_ = NO;
   1219   hasEditCommands_ = NO;
   1220   editCommands_.clear();
   1221 
   1222   // Before doing anything with a key down, check to see if plugin IME has been
   1223   // cancelled, since the plugin host needs to be informed of that before
   1224   // receiving the keydown.
   1225   if ([theEvent type] == NSKeyDown)
   1226     [self checkForPluginImeCancellation];
   1227 
   1228   // Sends key down events to input method first, then we can decide what should
   1229   // be done according to input method's feedback.
   1230   // If a plugin is active, bypass this step since events are forwarded directly
   1231   // to the plugin IME.
   1232   if (focusedPluginIdentifier_ == -1)
   1233     [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
   1234 
   1235   handlingKeyDown_ = NO;
   1236 
   1237   // Indicates if we should send the key event and corresponding editor commands
   1238   // after processing the input method result.
   1239   BOOL delayEventUntilAfterImeCompostion = NO;
   1240 
   1241   // To emulate Windows, over-write |event.windowsKeyCode| to VK_PROCESSKEY
   1242   // while an input method is composing or inserting a text.
   1243   // Gmail checks this code in its onkeydown handler to stop auto-completing
   1244   // e-mail addresses while composing a CJK text.
   1245   // If the text to be inserted has only one character, then we don't need this
   1246   // trick, because we'll send the text as a key press event instead.
   1247   if (hasMarkedText_ || oldHasMarkedText || textToBeInserted_.length() > 1) {
   1248     NativeWebKeyboardEvent fakeEvent = event;
   1249     fakeEvent.windowsKeyCode = 0xE5;  // VKEY_PROCESSKEY
   1250     fakeEvent.setKeyIdentifierFromWindowsKeyCode();
   1251     fakeEvent.skip_in_browser = true;
   1252     widgetHost->ForwardKeyboardEvent(fakeEvent);
   1253     // If this key event was handled by the input method, but
   1254     // -doCommandBySelector: (invoked by the call to -interpretKeyEvents: above)
   1255     // enqueued edit commands, then in order to let webkit handle them
   1256     // correctly, we need to send the real key event and corresponding edit
   1257     // commands after processing the input method result.
   1258     // We shouldn't do this if a new marked text was set by the input method,
   1259     // otherwise the new marked text might be cancelled by webkit.
   1260     if (hasEditCommands_ && !hasMarkedText_)
   1261       delayEventUntilAfterImeCompostion = YES;
   1262   } else {
   1263     if (!editCommands_.empty())
   1264       widgetHost->ForwardEditCommandsForNextKeyEvent(editCommands_);
   1265     widgetHost->ForwardKeyboardEvent(event);
   1266   }
   1267 
   1268   // Calling ForwardKeyboardEvent() could have destroyed the widget. When the
   1269   // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will
   1270   // be set to NULL. So we check it here and return immediately if it's NULL.
   1271   if (!renderWidgetHostView_->render_widget_host_)
   1272     return;
   1273 
   1274   // Then send keypress and/or composition related events.
   1275   // If there was a marked text or the text to be inserted is longer than 1
   1276   // character, then we send the text by calling ConfirmComposition().
   1277   // Otherwise, if the text to be inserted only contains 1 character, then we
   1278   // can just send a keypress event which is fabricated by changing the type of
   1279   // the keydown event, so that we can retain all necessary informations, such
   1280   // as unmodifiedText, etc. And we need to set event.skip_in_browser to true to
   1281   // prevent the browser from handling it again.
   1282   // Note that, |textToBeInserted_| is a UTF-16 string, but it's fine to only
   1283   // handle BMP characters here, as we can always insert non-BMP characters as
   1284   // text.
   1285   BOOL textInserted = NO;
   1286   if (textToBeInserted_.length() >
   1287       ((hasMarkedText_ || oldHasMarkedText) ? 0u : 1u)) {
   1288     widgetHost->ImeConfirmComposition(textToBeInserted_);
   1289     textInserted = YES;
   1290   }
   1291 
   1292   // Updates or cancels the composition. If some text has been inserted, then
   1293   // we don't need to cancel the composition explicitly.
   1294   if (hasMarkedText_ && markedText_.length()) {
   1295     // Sends the updated marked text to the renderer so it can update the
   1296     // composition node in WebKit.
   1297     // When marked text is available, |selectedRange_| will be the range being
   1298     // selected inside the marked text.
   1299     widgetHost->ImeSetComposition(markedText_, underlines_,
   1300                                   selectedRange_.location,
   1301                                   NSMaxRange(selectedRange_));
   1302   } else if (oldHasMarkedText && !hasMarkedText_ && !textInserted) {
   1303     if (unmarkTextCalled_)
   1304       widgetHost->ImeConfirmComposition();
   1305     else
   1306       widgetHost->ImeCancelComposition();
   1307   }
   1308 
   1309   // If the key event was handled by the input method but it also generated some
   1310   // edit commands, then we need to send the real key event and corresponding
   1311   // edit commands here. This usually occurs when the input method wants to
   1312   // finish current composition session but still wants the application to
   1313   // handle the key event. See http://crbug.com/48161 for reference.
   1314   if (delayEventUntilAfterImeCompostion) {
   1315     // If |delayEventUntilAfterImeCompostion| is YES, then a fake key down event
   1316     // with windowsKeyCode == 0xE5 has already been sent to webkit.
   1317     // So before sending the real key down event, we need to send a fake key up
   1318     // event to balance it.
   1319     NativeWebKeyboardEvent fakeEvent = event;
   1320     fakeEvent.type = WebKit::WebInputEvent::KeyUp;
   1321     fakeEvent.skip_in_browser = true;
   1322     widgetHost->ForwardKeyboardEvent(fakeEvent);
   1323     // Not checking |renderWidgetHostView_->render_widget_host_| here because
   1324     // a key event with |skip_in_browser| == true won't be handled by browser,
   1325     // thus it won't destroy the widget.
   1326 
   1327     if (!editCommands_.empty())
   1328       widgetHost->ForwardEditCommandsForNextKeyEvent(editCommands_);
   1329     widgetHost->ForwardKeyboardEvent(event);
   1330 
   1331     // Calling ForwardKeyboardEvent() could have destroyed the widget. When the
   1332     // widget was destroyed, |renderWidgetHostView_->render_widget_host_| will
   1333     // be set to NULL. So we check it here and return immediately if it's NULL.
   1334     if (!renderWidgetHostView_->render_widget_host_)
   1335       return;
   1336   }
   1337 
   1338   const NSUInteger kCtrlCmdKeyMask = NSControlKeyMask | NSCommandKeyMask;
   1339   // Only send a corresponding key press event if there is no marked text.
   1340   if (!hasMarkedText_) {
   1341     if (!textInserted && textToBeInserted_.length() == 1) {
   1342       // If a single character was inserted, then we just send it as a keypress
   1343       // event.
   1344       event.type = WebKit::WebInputEvent::Char;
   1345       event.text[0] = textToBeInserted_[0];
   1346       event.text[1] = 0;
   1347       event.skip_in_browser = true;
   1348       widgetHost->ForwardKeyboardEvent(event);
   1349     } else if ((!textInserted || delayEventUntilAfterImeCompostion) &&
   1350                [[theEvent characters] length] > 0 &&
   1351                (([theEvent modifierFlags] & kCtrlCmdKeyMask) ||
   1352                 (hasEditCommands_ && editCommands_.empty()))) {
   1353       // We don't get insertText: calls if ctrl or cmd is down, or the key event
   1354       // generates an insert command. So synthesize a keypress event for these
   1355       // cases, unless the key event generated any other command.
   1356       event.type = WebKit::WebInputEvent::Char;
   1357       event.skip_in_browser = true;
   1358       widgetHost->ForwardKeyboardEvent(event);
   1359     }
   1360   }
   1361 
   1362   // Possibly autohide the cursor.
   1363   if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent])
   1364     [NSCursor setHiddenUntilMouseMoves:YES];
   1365 }
   1366 
   1367 - (void)scrollWheel:(NSEvent *)theEvent {
   1368   [self cancelChildPopups];
   1369 
   1370   const WebMouseWheelEvent& event =
   1371       WebInputEventFactory::mouseWheelEvent(theEvent, self);
   1372   if (renderWidgetHostView_->render_widget_host_)
   1373     renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(event);
   1374 }
   1375 
   1376 // See the comment in RenderWidgetHostViewMac::Destroy() about cancellation
   1377 // events. On the Mac we must kill popups on outside events, thus this lovely
   1378 // case of filicide caused by events on parent views.
   1379 - (void)cancelChildPopups {
   1380   // If this view can be the key view, it is not a popup. Therefore, if it has
   1381   // any children, they are popups that need to be canceled.
   1382   if (canBeKeyView_) {
   1383     for (NSView* subview in [self subviews]) {
   1384       if (![subview isKindOfClass:[RenderWidgetHostViewCocoa class]])
   1385         continue;  // Skip plugin views.
   1386 
   1387       [static_cast<RenderWidgetHostViewCocoa*>(subview)
   1388           renderWidgetHostViewMac]->KillSelf();
   1389     }
   1390   }
   1391 }
   1392 
   1393 - (void)setFrameSize:(NSSize)newSize {
   1394   [super setFrameSize:newSize];
   1395   if (renderWidgetHostView_->render_widget_host_)
   1396     renderWidgetHostView_->render_widget_host_->WasResized();
   1397 }
   1398 
   1399 - (void)setFrame:(NSRect)frameRect {
   1400   [super setFrame:frameRect];
   1401   if (renderWidgetHostView_->render_widget_host_)
   1402     renderWidgetHostView_->render_widget_host_->WasResized();
   1403 }
   1404 
   1405 - (void)setFrameWithDeferredUpdate:(NSRect)frameRect {
   1406   [super setFrame:frameRect];
   1407   [self performSelector:@selector(renderWidgetHostWasResized)
   1408              withObject:nil
   1409              afterDelay:0];
   1410 }
   1411 
   1412 - (void)renderWidgetHostWasResized {
   1413   if (renderWidgetHostView_->render_widget_host_)
   1414     renderWidgetHostView_->render_widget_host_->WasResized();
   1415 }
   1416 
   1417 - (void)callSetNeedsDisplayInRect {
   1418   DCHECK([NSThread isMainThread]);
   1419   DCHECK(renderWidgetHostView_->call_set_needs_display_in_rect_pending_);
   1420   [self setNeedsDisplayInRect:renderWidgetHostView_->invalid_rect_];
   1421   renderWidgetHostView_->call_set_needs_display_in_rect_pending_ = false;
   1422   renderWidgetHostView_->invalid_rect_ = NSZeroRect;
   1423 
   1424   renderWidgetHostView_->HandleDelayedGpuViewHiding();
   1425 }
   1426 
   1427 // Fills with white the parts of the area to the right and bottom for |rect|
   1428 // that intersect |damagedRect|.
   1429 - (void)fillBottomRightRemainderOfRect:(gfx::Rect)rect
   1430                              dirtyRect:(gfx::Rect)damagedRect {
   1431   if (damagedRect.right() > rect.right()) {
   1432     int x = std::max(rect.right(), damagedRect.x());
   1433     int y = std::min(rect.bottom(), damagedRect.bottom());
   1434     int width = damagedRect.right() - x;
   1435     int height = damagedRect.y() - y;
   1436 
   1437     // Extra fun to get around the fact that gfx::Rects can't have
   1438     // negative sizes.
   1439     if (width < 0) {
   1440       x += width;
   1441       width = -width;
   1442     }
   1443     if (height < 0) {
   1444       y += height;
   1445       height = -height;
   1446     }
   1447 
   1448     NSRect r = [self flipRectToNSRect:gfx::Rect(x, y, width, height)];
   1449     [[NSColor whiteColor] set];
   1450     NSRectFill(r);
   1451   }
   1452   if (damagedRect.bottom() > rect.bottom()) {
   1453     int x = damagedRect.x();
   1454     int y = damagedRect.bottom();
   1455     int width = damagedRect.right() - x;
   1456     int height = std::max(rect.bottom(), damagedRect.y()) - y;
   1457 
   1458     // Extra fun to get around the fact that gfx::Rects can't have
   1459     // negative sizes.
   1460     if (width < 0) {
   1461       x += width;
   1462       width = -width;
   1463     }
   1464     if (height < 0) {
   1465       y += height;
   1466       height = -height;
   1467     }
   1468 
   1469     NSRect r = [self flipRectToNSRect:gfx::Rect(x, y, width, height)];
   1470     [[NSColor whiteColor] set];
   1471     NSRectFill(r);
   1472   }
   1473 }
   1474 
   1475 - (void)drawRect:(NSRect)dirtyRect {
   1476   if (!renderWidgetHostView_->render_widget_host_) {
   1477     // TODO(shess): Consider using something more noticable?
   1478     [[NSColor whiteColor] set];
   1479     NSRectFill(dirtyRect);
   1480     return;
   1481   }
   1482 
   1483   const gfx::Rect damagedRect([self flipNSRectToRect:dirtyRect]);
   1484 
   1485   if (renderWidgetHostView_->render_widget_host_->
   1486       is_accelerated_compositing_active()) {
   1487     gfx::Rect gpuRect;
   1488 
   1489     gfx::PluginWindowHandle root_handle =
   1490        renderWidgetHostView_->plugin_container_manager_.root_container_handle();
   1491     if (root_handle != gfx::kNullPluginWindow) {
   1492       AcceleratedPluginView* view =
   1493           renderWidgetHostView_->ViewForPluginWindowHandle(root_handle);
   1494       DCHECK(view);
   1495       if (view && ![view isHidden]) {
   1496         NSRect frame = [view frame];
   1497         frame.size = [view cachedSize];
   1498         gpuRect = [self flipNSRectToRect:frame];
   1499       }
   1500     }
   1501 
   1502     [self fillBottomRightRemainderOfRect:gpuRect dirtyRect:damagedRect];
   1503     return;
   1504   }
   1505 
   1506   DCHECK(!renderWidgetHostView_->about_to_validate_and_paint_);
   1507 
   1508   renderWidgetHostView_->about_to_validate_and_paint_ = true;
   1509   BackingStoreMac* backingStore = static_cast<BackingStoreMac*>(
   1510       renderWidgetHostView_->render_widget_host_->GetBackingStore(true));
   1511   renderWidgetHostView_->about_to_validate_and_paint_ = false;
   1512 
   1513   if (backingStore) {
   1514     gfx::Rect bitmapRect(0, 0,
   1515                          backingStore->size().width(),
   1516                          backingStore->size().height());
   1517 
   1518     // Specify the proper y offset to ensure that the view is rooted to the
   1519     // upper left corner.  This can be negative, if the window was resized
   1520     // smaller and the renderer hasn't yet repainted.
   1521     int yOffset = NSHeight([self bounds]) - backingStore->size().height();
   1522 
   1523     gfx::Rect paintRect = bitmapRect.Intersect(damagedRect);
   1524     if (!paintRect.IsEmpty()) {
   1525       // if we have a CGLayer, draw that into the window
   1526       if (backingStore->cg_layer()) {
   1527         CGContextRef context = static_cast<CGContextRef>(
   1528             [[NSGraphicsContext currentContext] graphicsPort]);
   1529 
   1530         // TODO: add clipping to dirtyRect if it improves drawing performance.
   1531         CGContextDrawLayerAtPoint(context, CGPointMake(0.0, yOffset),
   1532                                   backingStore->cg_layer());
   1533       } else {
   1534         // if we haven't created a layer yet, draw the cached bitmap into
   1535         // the window.  The CGLayer will be created the next time the renderer
   1536         // paints.
   1537         CGContextRef context = static_cast<CGContextRef>(
   1538             [[NSGraphicsContext currentContext] graphicsPort]);
   1539         base::mac::ScopedCFTypeRef<CGImageRef> image(
   1540             CGBitmapContextCreateImage(backingStore->cg_bitmap()));
   1541         CGRect imageRect = bitmapRect.ToCGRect();
   1542         imageRect.origin.y = yOffset;
   1543         CGContextDrawImage(context, imageRect, image);
   1544       }
   1545     }
   1546 
   1547     // Fill the remaining portion of the damagedRect with white
   1548     [self fillBottomRightRemainderOfRect:bitmapRect dirtyRect:damagedRect];
   1549 
   1550     if (!renderWidgetHostView_->whiteout_start_time_.is_null()) {
   1551       base::TimeDelta whiteout_duration = base::TimeTicks::Now() -
   1552           renderWidgetHostView_->whiteout_start_time_;
   1553       UMA_HISTOGRAM_TIMES("MPArch.RWHH_WhiteoutDuration", whiteout_duration);
   1554 
   1555       // Reset the start time to 0 so that we start recording again the next
   1556       // time the backing store is NULL...
   1557       renderWidgetHostView_->whiteout_start_time_ = base::TimeTicks();
   1558     }
   1559     if (!renderWidgetHostView_->tab_switch_paint_time_.is_null()) {
   1560       base::TimeDelta tab_switch_paint_duration = base::TimeTicks::Now() -
   1561           renderWidgetHostView_->tab_switch_paint_time_;
   1562       UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration",
   1563           tab_switch_paint_duration);
   1564       // Reset tab_switch_paint_time_ to 0 so future tab selections are
   1565       // recorded.
   1566       renderWidgetHostView_->tab_switch_paint_time_ = base::TimeTicks();
   1567     }
   1568   } else {
   1569     [[NSColor whiteColor] set];
   1570     NSRectFill(dirtyRect);
   1571     if (renderWidgetHostView_->whiteout_start_time_.is_null())
   1572       renderWidgetHostView_->whiteout_start_time_ = base::TimeTicks::Now();
   1573   }
   1574 }
   1575 
   1576 - (BOOL)canBecomeKeyView {
   1577   if (!renderWidgetHostView_->render_widget_host_)
   1578     return NO;
   1579 
   1580   return canBeKeyView_;
   1581 }
   1582 
   1583 - (BOOL)acceptsFirstResponder {
   1584   if (!renderWidgetHostView_->render_widget_host_)
   1585     return NO;
   1586 
   1587   return canBeKeyView_ && !takesFocusOnlyOnMouseDown_;
   1588 }
   1589 
   1590 - (BOOL)becomeFirstResponder {
   1591   if (!renderWidgetHostView_->render_widget_host_)
   1592     return NO;
   1593 
   1594   renderWidgetHostView_->render_widget_host_->Focus();
   1595   renderWidgetHostView_->render_widget_host_->SetInputMethodActive(true);
   1596   renderWidgetHostView_->SetTextInputActive(true);
   1597 
   1598   // Cancel any onging composition text which was left before we lost focus.
   1599   // TODO(suzhe): We should do it in -resignFirstResponder: method, but
   1600   // somehow that method won't be called when switching among different tabs.
   1601   // See http://crbug.com/47209
   1602   [self cancelComposition];
   1603 
   1604   NSNumber* direction = [NSNumber numberWithUnsignedInteger:
   1605       [[self window] keyViewSelectionDirection]];
   1606   NSDictionary* userInfo =
   1607       [NSDictionary dictionaryWithObject:direction
   1608                                   forKey:kSelectionDirection];
   1609   [[NSNotificationCenter defaultCenter]
   1610       postNotificationName:kViewDidBecomeFirstResponder
   1611                     object:self
   1612                   userInfo:userInfo];
   1613 
   1614   return YES;
   1615 }
   1616 
   1617 - (BOOL)resignFirstResponder {
   1618   renderWidgetHostView_->SetTextInputActive(false);
   1619   if (!renderWidgetHostView_->render_widget_host_)
   1620     return YES;
   1621 
   1622   if (closeOnDeactivate_)
   1623     renderWidgetHostView_->KillSelf();
   1624 
   1625   renderWidgetHostView_->render_widget_host_->SetInputMethodActive(false);
   1626   renderWidgetHostView_->render_widget_host_->Blur();
   1627 
   1628   // We should cancel any onging composition whenever RWH's Blur() method gets
   1629   // called, because in this case, webkit will confirm the ongoing composition
   1630   // internally.
   1631   [self cancelComposition];
   1632 
   1633   return YES;
   1634 }
   1635 
   1636 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
   1637   SEL action = [item action];
   1638 
   1639   // For now, these actions are always enabled for render view,
   1640   // this is sub-optimal.
   1641   // TODO(suzhe): Plumb the "can*" methods up from WebCore.
   1642   if (action == @selector(undo:) ||
   1643       action == @selector(redo:) ||
   1644       action == @selector(cut:) ||
   1645       action == @selector(copy:) ||
   1646       action == @selector(copyToFindPboard:) ||
   1647       action == @selector(paste:) ||
   1648       action == @selector(pasteAsPlainText:) ||
   1649       action == @selector(checkSpelling:)) {
   1650     return renderWidgetHostView_->render_widget_host_->IsRenderView();
   1651   }
   1652 
   1653   if (action == @selector(toggleContinuousSpellChecking:)) {
   1654     RenderViewHost::CommandState state;
   1655     state.is_enabled = false;
   1656     state.checked_state = RENDER_VIEW_COMMAND_CHECKED_STATE_UNCHECKED;
   1657     if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
   1658       state = static_cast<RenderViewHost*>(
   1659           renderWidgetHostView_->render_widget_host_)->
   1660               GetStateForCommand(RENDER_VIEW_COMMAND_TOGGLE_SPELL_CHECK);
   1661     }
   1662     if ([(id)item respondsToSelector:@selector(setState:)]) {
   1663       NSCellStateValue checked_state =
   1664           RENDER_VIEW_COMMAND_CHECKED_STATE_UNCHECKED;
   1665       switch (state.checked_state) {
   1666         case RENDER_VIEW_COMMAND_CHECKED_STATE_UNCHECKED:
   1667           checked_state = NSOffState;
   1668           break;
   1669         case RENDER_VIEW_COMMAND_CHECKED_STATE_CHECKED:
   1670           checked_state = NSOnState;
   1671           break;
   1672         case RENDER_VIEW_COMMAND_CHECKED_STATE_MIXED:
   1673           checked_state = NSMixedState;
   1674           break;
   1675       }
   1676       [(id)item setState:checked_state];
   1677     }
   1678     return state.is_enabled;
   1679   }
   1680 
   1681   return editCommand_helper_->IsMenuItemEnabled(action, self);
   1682 }
   1683 
   1684 - (RenderWidgetHostViewMac*)renderWidgetHostViewMac {
   1685   return renderWidgetHostView_.get();
   1686 }
   1687 
   1688 // Determine whether we should autohide the cursor (i.e., hide it until mouse
   1689 // move) for the given event. Customize here to be more selective about which
   1690 // key presses to autohide on.
   1691 + (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event {
   1692   return ([event type] == NSKeyDown) ? YES : NO;
   1693 }
   1694 
   1695 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute
   1696                                          index:(NSUInteger)index
   1697                                       maxCount:(NSUInteger)maxCount {
   1698   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
   1699   NSUInteger totalLength = [fullArray count];
   1700   if (index >= totalLength)
   1701     return nil;
   1702   NSUInteger length = MIN(totalLength - index, maxCount);
   1703   return [fullArray subarrayWithRange:NSMakeRange(index, length)];
   1704 }
   1705 
   1706 - (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute {
   1707   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
   1708   return [fullArray count];
   1709 }
   1710 
   1711 - (id)accessibilityAttributeValue:(NSString *)attribute {
   1712   BrowserAccessibilityManager* manager =
   1713       renderWidgetHostView_->browser_accessibility_manager_.get();
   1714 
   1715   // Contents specifies document view of RenderWidgetHostViewCocoa provided by
   1716   // BrowserAccessibilityManager. Children includes all subviews in addition to
   1717   // contents. Currently we do not have subviews besides the document view.
   1718   if (([attribute isEqualToString:NSAccessibilityChildrenAttribute] ||
   1719           [attribute isEqualToString:NSAccessibilityContentsAttribute]) &&
   1720       manager) {
   1721     return [NSArray arrayWithObjects:manager->
   1722         GetRoot()->toBrowserAccessibilityCocoa(), nil];
   1723   } else if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
   1724     return NSAccessibilityScrollAreaRole;
   1725   }
   1726   id ret = [super accessibilityAttributeValue:attribute];
   1727   return ret;
   1728 }
   1729 
   1730 - (NSArray*)accessibilityAttributeNames {
   1731   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
   1732   [ret addObject:NSAccessibilityContentsAttribute];
   1733   [ret addObjectsFromArray:[super accessibilityAttributeNames]];
   1734   return ret;
   1735 }
   1736 
   1737 - (id)accessibilityHitTest:(NSPoint)point {
   1738   if (!renderWidgetHostView_->browser_accessibility_manager_.get())
   1739     return self;
   1740   NSPoint pointInWindow = [[self window] convertScreenToBase:point];
   1741   NSPoint localPoint = [self convertPoint:pointInWindow fromView:nil];
   1742   localPoint.y = NSHeight([self bounds]) - localPoint.y;
   1743   BrowserAccessibilityCocoa* root = renderWidgetHostView_->
   1744       browser_accessibility_manager_->
   1745           GetRoot()->toBrowserAccessibilityCocoa();
   1746   id obj = [root accessibilityHitTest:localPoint];
   1747   return obj;
   1748 }
   1749 
   1750 - (BOOL)accessibilityIsIgnored {
   1751   return NO;
   1752 }
   1753 
   1754 - (NSUInteger)accessibilityGetIndexOf:(id)child {
   1755   BrowserAccessibilityManager* manager =
   1756       renderWidgetHostView_->browser_accessibility_manager_.get();
   1757   // Only child is root.
   1758   if (manager &&
   1759       manager->GetRoot()->toBrowserAccessibilityCocoa() == child) {
   1760     return 0;
   1761   } else {
   1762     return NSNotFound;
   1763   }
   1764 }
   1765 
   1766 - (id)accessibilityFocusedUIElement {
   1767   BrowserAccessibilityManager* manager =
   1768       renderWidgetHostView_->browser_accessibility_manager_.get();
   1769   if (manager) {
   1770     BrowserAccessibility* focused_item = manager->GetFocus(NULL);
   1771     DCHECK(focused_item);
   1772     if (focused_item) {
   1773       BrowserAccessibilityCocoa* focused_item_cocoa =
   1774           focused_item->toBrowserAccessibilityCocoa();
   1775       DCHECK(focused_item_cocoa);
   1776       if (focused_item_cocoa)
   1777         return focused_item_cocoa;
   1778     }
   1779   }
   1780   return [super accessibilityFocusedUIElement];
   1781 }
   1782 
   1783 - (void)doDefaultAction:(int32)accessibilityObjectId {
   1784   renderWidgetHostView_->render_widget_host_->
   1785       AccessibilityDoDefaultAction(accessibilityObjectId);
   1786 }
   1787 
   1788 // Convert a web accessibility's location in web coordinates into a cocoa
   1789 // screen coordinate.
   1790 - (NSPoint)accessibilityPointInScreen:
   1791     (BrowserAccessibilityCocoa*)accessibility {
   1792   NSPoint origin = [accessibility origin];
   1793   NSSize size = [accessibility size];
   1794   origin.y = NSHeight([self bounds]) - origin.y;
   1795   NSPoint originInWindow = [self convertPoint:origin toView:nil];
   1796   NSPoint originInScreen = [[self window] convertBaseToScreen:originInWindow];
   1797   originInScreen.y = originInScreen.y - size.height;
   1798   return originInScreen;
   1799 }
   1800 
   1801 - (void)setAccessibilityFocus:(BOOL)focus
   1802               accessibilityId:(int32)accessibilityObjectId {
   1803   if (focus) {
   1804     renderWidgetHostView_->render_widget_host_->
   1805         SetAccessibilityFocus(accessibilityObjectId);
   1806   }
   1807 }
   1808 
   1809 // Spellchecking methods
   1810 // The next three methods are implemented here since this class is the first
   1811 // responder for anything in the browser.
   1812 
   1813 // This message is sent whenever the user specifies that a word should be
   1814 // changed from the spellChecker.
   1815 - (void)changeSpelling:(id)sender {
   1816   // Grab the currently selected word from the spell panel, as this is the word
   1817   // that we want to replace the selected word in the text with.
   1818   NSString* newWord = [[sender selectedCell] stringValue];
   1819   if (newWord != nil) {
   1820     RenderWidgetHostViewMac* thisHostView = [self renderWidgetHostViewMac];
   1821     thisHostView->GetRenderWidgetHost()->Replace(
   1822         base::SysNSStringToUTF16(newWord));
   1823   }
   1824 }
   1825 
   1826 // This message is sent by NSSpellChecker whenever the next word should be
   1827 // advanced to, either after a correction or clicking the "Find Next" button.
   1828 // This isn't documented anywhere useful, like in NSSpellProtocol.h with the
   1829 // other spelling panel methods. This is probably because Apple assumes that the
   1830 // the spelling panel will be used with an NSText, which will automatically
   1831 // catch this and advance to the next word for you. Thanks Apple.
   1832 // This is also called from the Edit -> Spelling -> Check Spelling menu item.
   1833 - (void)checkSpelling:(id)sender {
   1834   RenderWidgetHostViewMac* thisHostView = [self renderWidgetHostViewMac];
   1835   thisHostView->GetRenderWidgetHost()->AdvanceToNextMisspelling();
   1836 }
   1837 
   1838 // This message is sent by the spelling panel whenever a word is ignored.
   1839 - (void)ignoreSpelling:(id)sender {
   1840   // Ideally, we would ask the current RenderView for its tag, but that would
   1841   // mean making a blocking IPC call from the browser. Instead,
   1842   // SpellCheckerPlatform::CheckSpelling remembers the last tag and
   1843   // SpellCheckerPlatform::IgnoreWord assumes that is the correct tag.
   1844   NSString* wordToIgnore = [sender stringValue];
   1845   if (wordToIgnore != nil)
   1846     SpellCheckerPlatform::IgnoreWord(base::SysNSStringToUTF16(wordToIgnore));
   1847 }
   1848 
   1849 - (void)showGuessPanel:(id)sender {
   1850   RenderWidgetHostViewMac* thisHostView = [self renderWidgetHostViewMac];
   1851   thisHostView->GetRenderWidgetHost()->ToggleSpellPanel(
   1852       SpellCheckerPlatform::SpellingPanelVisible());
   1853 }
   1854 
   1855 - (void)toggleContinuousSpellChecking:(id)sender {
   1856   if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
   1857     static_cast<RenderViewHost*>(renderWidgetHostView_->render_widget_host_)->
   1858       ToggleSpellCheck();
   1859   }
   1860 }
   1861 
   1862 // END Spellchecking methods
   1863 
   1864 // Below is the nasty tooltip stuff -- copied from WebKit's WebHTMLView.mm
   1865 // with minor modifications for code style and commenting.
   1866 //
   1867 //  The 'public' interface is -setToolTipAtMousePoint:. This differs from
   1868 // -setToolTip: in that the updated tooltip takes effect immediately,
   1869 //  without the user's having to move the mouse out of and back into the view.
   1870 //
   1871 // Unfortunately, doing this requires sending fake mouseEnter/Exit events to
   1872 // the view, which in turn requires overriding some internal tracking-rect
   1873 // methods (to keep track of its owner & userdata, which need to be filled out
   1874 // in the fake events.) --snej 7/6/09
   1875 
   1876 
   1877 /*
   1878  * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
   1879  *           (C) 2006, 2007 Graham Dennis (graham.dennis (a] gmail.com)
   1880  *
   1881  * Redistribution and use in source and binary forms, with or without
   1882  * modification, are permitted provided that the following conditions
   1883  * are met:
   1884  *
   1885  * 1.  Redistributions of source code must retain the above copyright
   1886  *     notice, this list of conditions and the following disclaimer.
   1887  * 2.  Redistributions in binary form must reproduce the above copyright
   1888  *     notice, this list of conditions and the following disclaimer in the
   1889  *     documentation and/or other materials provided with the distribution.
   1890  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
   1891  *     its contributors may be used to endorse or promote products derived
   1892  *     from this software without specific prior written permission.
   1893  *
   1894  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
   1895  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   1896  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   1897  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
   1898  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   1899  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   1900  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   1901  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   1902  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   1903  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   1904  */
   1905 
   1906 // Any non-zero value will do, but using something recognizable might help us
   1907 // debug some day.
   1908 static const NSTrackingRectTag kTrackingRectTag = 0xBADFACE;
   1909 
   1910 // Override of a public NSView method, replacing the inherited functionality.
   1911 // See above for rationale.
   1912 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect
   1913                                owner:(id)owner
   1914                             userData:(void *)data
   1915                         assumeInside:(BOOL)assumeInside {
   1916   DCHECK(trackingRectOwner_ == nil);
   1917   trackingRectOwner_ = owner;
   1918   trackingRectUserData_ = data;
   1919   return kTrackingRectTag;
   1920 }
   1921 
   1922 // Override of (apparently) a private NSView method(!) See above for rationale.
   1923 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect
   1924                                 owner:(id)owner
   1925                              userData:(void *)data
   1926                          assumeInside:(BOOL)assumeInside
   1927                        useTrackingNum:(int)tag {
   1928   DCHECK(tag == 0 || tag == kTrackingRectTag);
   1929   DCHECK(trackingRectOwner_ == nil);
   1930   trackingRectOwner_ = owner;
   1931   trackingRectUserData_ = data;
   1932   return kTrackingRectTag;
   1933 }
   1934 
   1935 // Override of (apparently) a private NSView method(!) See above for rationale.
   1936 - (void)_addTrackingRects:(NSRect *)rects
   1937                     owner:(id)owner
   1938              userDataList:(void **)userDataList
   1939          assumeInsideList:(BOOL *)assumeInsideList
   1940              trackingNums:(NSTrackingRectTag *)trackingNums
   1941                     count:(int)count {
   1942   DCHECK(count == 1);
   1943   DCHECK(trackingNums[0] == 0 || trackingNums[0] == kTrackingRectTag);
   1944   DCHECK(trackingRectOwner_ == nil);
   1945   trackingRectOwner_ = owner;
   1946   trackingRectUserData_ = userDataList[0];
   1947   trackingNums[0] = kTrackingRectTag;
   1948 }
   1949 
   1950 // Override of a public NSView method, replacing the inherited functionality.
   1951 // See above for rationale.
   1952 - (void)removeTrackingRect:(NSTrackingRectTag)tag {
   1953   if (tag == 0)
   1954     return;
   1955 
   1956   if (tag == kTrackingRectTag) {
   1957     trackingRectOwner_ = nil;
   1958     return;
   1959   }
   1960 
   1961   if (tag == lastToolTipTag_) {
   1962     [super removeTrackingRect:tag];
   1963     lastToolTipTag_ = 0;
   1964     return;
   1965   }
   1966 
   1967   // If any other tracking rect is being removed, we don't know how it was
   1968   // created and it's possible there's a leak involved (see Radar 3500217).
   1969   NOTREACHED();
   1970 }
   1971 
   1972 // Override of (apparently) a private NSView method(!)
   1973 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count {
   1974   for (int i = 0; i < count; ++i) {
   1975     int tag = tags[i];
   1976     if (tag == 0)
   1977       continue;
   1978     DCHECK(tag == kTrackingRectTag);
   1979     trackingRectOwner_ = nil;
   1980   }
   1981 }
   1982 
   1983 // Sends a fake NSMouseExited event to the view for its current tracking rect.
   1984 - (void)_sendToolTipMouseExited {
   1985   // Nothing matters except window, trackingNumber, and userData.
   1986   int windowNumber = [[self window] windowNumber];
   1987   NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
   1988                                               location:NSMakePoint(0, 0)
   1989                                          modifierFlags:0
   1990                                              timestamp:0
   1991                                           windowNumber:windowNumber
   1992                                                context:NULL
   1993                                            eventNumber:0
   1994                                         trackingNumber:kTrackingRectTag
   1995                                               userData:trackingRectUserData_];
   1996   [trackingRectOwner_ mouseExited:fakeEvent];
   1997 }
   1998 
   1999 // Sends a fake NSMouseEntered event to the view for its current tracking rect.
   2000 - (void)_sendToolTipMouseEntered {
   2001   // Nothing matters except window, trackingNumber, and userData.
   2002   int windowNumber = [[self window] windowNumber];
   2003   NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
   2004                                               location:NSMakePoint(0, 0)
   2005                                          modifierFlags:0
   2006                                              timestamp:0
   2007                                           windowNumber:windowNumber
   2008                                                context:NULL
   2009                                            eventNumber:0
   2010                                         trackingNumber:kTrackingRectTag
   2011                                               userData:trackingRectUserData_];
   2012   [trackingRectOwner_ mouseEntered:fakeEvent];
   2013 }
   2014 
   2015 // Sets the view's current tooltip, to be displayed at the current mouse
   2016 // location. (This does not make the tooltip appear -- as usual, it only
   2017 // appears after a delay.) Pass null to remove the tooltip.
   2018 - (void)setToolTipAtMousePoint:(NSString *)string {
   2019   NSString *toolTip = [string length] == 0 ? nil : string;
   2020   if ((toolTip && toolTip_ && [toolTip isEqualToString:toolTip_]) ||
   2021       (!toolTip && !toolTip_)) {
   2022     return;
   2023   }
   2024 
   2025   if (toolTip_) {
   2026     [self _sendToolTipMouseExited];
   2027   }
   2028 
   2029   toolTip_.reset([toolTip copy]);
   2030 
   2031   if (toolTip) {
   2032     // See radar 3500217 for why we remove all tooltips
   2033     // rather than just the single one we created.
   2034     [self removeAllToolTips];
   2035     NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
   2036     lastToolTipTag_ = [self addToolTipRect:wideOpenRect
   2037                                      owner:self
   2038                                   userData:NULL];
   2039     [self _sendToolTipMouseEntered];
   2040   }
   2041 }
   2042 
   2043 // NSView calls this to get the text when displaying the tooltip.
   2044 - (NSString *)view:(NSView *)view
   2045   stringForToolTip:(NSToolTipTag)tag
   2046              point:(NSPoint)point
   2047           userData:(void *)data {
   2048   return [[toolTip_ copy] autorelease];
   2049 }
   2050 
   2051 // Below is our NSTextInput implementation.
   2052 //
   2053 // When WebHTMLView receives a NSKeyDown event, WebHTMLView calls the following
   2054 // functions to process this event.
   2055 //
   2056 // [WebHTMLView keyDown] ->
   2057 //     EventHandler::keyEvent() ->
   2058 //     ...
   2059 //     [WebEditorClient handleKeyboardEvent] ->
   2060 //     [WebHTMLView _interceptEditingKeyEvent] ->
   2061 //     [NSResponder interpretKeyEvents] ->
   2062 //     [WebHTMLView insertText] ->
   2063 //     Editor::insertText()
   2064 //
   2065 // Unfortunately, it is hard for Chromium to use this implementation because
   2066 // it causes key-typing jank.
   2067 // RenderWidgetHostViewMac is running in a browser process. On the other
   2068 // hand, Editor and EventHandler are running in a renderer process.
   2069 // So, if we used this implementation, a NSKeyDown event is dispatched to
   2070 // the following functions of Chromium.
   2071 //
   2072 // [RenderWidgetHostViewMac keyEvent] (browser) ->
   2073 //     |Sync IPC (KeyDown)| (*1) ->
   2074 //     EventHandler::keyEvent() (renderer) ->
   2075 //     ...
   2076 //     EditorClientImpl::handleKeyboardEvent() (renderer) ->
   2077 //     |Sync IPC| (*2) ->
   2078 //     [RenderWidgetHostViewMac _interceptEditingKeyEvent] (browser) ->
   2079 //     [self interpretKeyEvents] ->
   2080 //     [RenderWidgetHostViewMac insertText] (browser) ->
   2081 //     |Async IPC| ->
   2082 //     Editor::insertText() (renderer)
   2083 //
   2084 // (*1) we need to wait until this call finishes since WebHTMLView uses the
   2085 // result of EventHandler::keyEvent().
   2086 // (*2) we need to wait until this call finishes since WebEditorClient uses
   2087 // the result of [WebHTMLView _interceptEditingKeyEvent].
   2088 //
   2089 // This needs many sync IPC messages sent between a browser and a renderer for
   2090 // each key event, which would probably result in key-typing jank.
   2091 // To avoid this problem, this implementation processes key events (and input
   2092 // method events) totally in a browser process and sends asynchronous input
   2093 // events, almost same as KeyboardEvents (and TextEvents) of DOM Level 3, to a
   2094 // renderer process.
   2095 //
   2096 // [RenderWidgetHostViewMac keyEvent] (browser) ->
   2097 //     |Async IPC (RawKeyDown)| ->
   2098 //     [self interpretKeyEvents] ->
   2099 //     [RenderWidgetHostViewMac insertText] (browser) ->
   2100 //     |Async IPC (Char)| ->
   2101 //     Editor::insertText() (renderer)
   2102 //
   2103 // Since this implementation doesn't have to wait any IPC calls, this doesn't
   2104 // make any key-typing jank. --hbono 7/23/09
   2105 //
   2106 extern "C" {
   2107 extern NSString *NSTextInputReplacementRangeAttributeName;
   2108 }
   2109 
   2110 - (NSArray *)validAttributesForMarkedText {
   2111   // This code is just copied from WebKit except renaming variables.
   2112   if (!validAttributesForMarkedText_) {
   2113     validAttributesForMarkedText_.reset([[NSArray alloc] initWithObjects:
   2114         NSUnderlineStyleAttributeName,
   2115         NSUnderlineColorAttributeName,
   2116         NSMarkedClauseSegmentAttributeName,
   2117         NSTextInputReplacementRangeAttributeName,
   2118         nil]);
   2119   }
   2120   return validAttributesForMarkedText_.get();
   2121 }
   2122 
   2123 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint {
   2124   NOTIMPLEMENTED();
   2125   return NSNotFound;
   2126 }
   2127 
   2128 - (NSRect)firstRectForCharacterRange:(NSRange)theRange {
   2129   // An input method requests a cursor rectangle to display its candidate
   2130   // window.
   2131   // Calculate the screen coordinate of the cursor rectangle saved in
   2132   // RenderWidgetHostViewMac::ImeUpdateTextInputState() and send it to the
   2133   // input method.
   2134   // Since this window may be moved since we receive the cursor rectangle last
   2135   // time we sent the cursor rectangle to the input method, so we should map
   2136   // from the view coordinate to the screen coordinate every time when an input
   2137   // method need it.
   2138   NSRect resultRect = [self convertRect:caretRect_ toView:nil];
   2139   NSWindow* window = [self window];
   2140   if (window)
   2141     resultRect.origin = [window convertBaseToScreen:resultRect.origin];
   2142 
   2143   return resultRect;
   2144 }
   2145 
   2146 - (NSRange)selectedRange {
   2147   // Return the selected range saved in the setMarkedText method.
   2148   return hasMarkedText_ ? selectedRange_ : NSMakeRange(NSNotFound, 0);
   2149 }
   2150 
   2151 - (NSRange)markedRange {
   2152   // An input method calls this method to check if an application really has
   2153   // a text being composed when hasMarkedText call returns true.
   2154   // Returns the range saved in the setMarkedText method so the input method
   2155   // calls the setMarkedText method and we can update the composition node
   2156   // there. (When this method returns an empty range, the input method doesn't
   2157   // call the setMarkedText method.)
   2158   return hasMarkedText_ ? markedRange_ : NSMakeRange(NSNotFound, 0);
   2159 }
   2160 
   2161 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)range {
   2162   // TODO(hbono): Even though many input method works without implementing
   2163   // this method, we need to save a copy of the string in the setMarkedText
   2164   // method and create a NSAttributedString with the given range.
   2165   // http://crbug.com/37715
   2166   return nil;
   2167 }
   2168 
   2169 - (NSInteger)conversationIdentifier {
   2170   return reinterpret_cast<NSInteger>(self);
   2171 }
   2172 
   2173 // Each RenderWidgetHostViewCocoa has its own input context, but we return
   2174 // nil when the caret is in non-editable content or password box to avoid
   2175 // making input methods do their work.
   2176 - (NSTextInputContext *)inputContext {
   2177   if (focusedPluginIdentifier_ != -1)
   2178     return [[ComplexTextInputPanel sharedComplexTextInputPanel] inputContext];
   2179 
   2180   switch(renderWidgetHostView_->text_input_type_) {
   2181     case WebKit::WebTextInputTypeNone:
   2182     case WebKit::WebTextInputTypePassword:
   2183       return nil;
   2184     default:
   2185       return [super inputContext];
   2186   }
   2187 }
   2188 
   2189 - (BOOL)hasMarkedText {
   2190   // An input method calls this function to figure out whether or not an
   2191   // application is really composing a text. If it is composing, it calls
   2192   // the markedRange method, and maybe calls the setMarkedText method.
   2193   // It seems an input method usually calls this function when it is about to
   2194   // cancel an ongoing composition. If an application has a non-empty marked
   2195   // range, it calls the setMarkedText method to delete the range.
   2196   return hasMarkedText_;
   2197 }
   2198 
   2199 - (void)unmarkText {
   2200   // Delete the composition node of the renderer and finish an ongoing
   2201   // composition.
   2202   // It seems an input method calls the setMarkedText method and set an empty
   2203   // text when it cancels an ongoing composition, i.e. I have never seen an
   2204   // input method calls this method.
   2205   hasMarkedText_ = NO;
   2206   markedText_.clear();
   2207   underlines_.clear();
   2208 
   2209   // If we are handling a key down event, then ConfirmComposition() will be
   2210   // called in keyEvent: method.
   2211   if (!handlingKeyDown_)
   2212     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition();
   2213   else
   2214     unmarkTextCalled_ = YES;
   2215 }
   2216 
   2217 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange {
   2218   // An input method updates the composition string.
   2219   // We send the given text and range to the renderer so it can update the
   2220   // composition node of WebKit.
   2221   BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
   2222   NSString* im_text = isAttributedString ? [string string] : string;
   2223   int length = [im_text length];
   2224 
   2225   markedRange_ = NSMakeRange(0, length);
   2226   selectedRange_ = newSelRange;
   2227   markedText_ = base::SysNSStringToUTF16(im_text);
   2228   hasMarkedText_ = (length > 0);
   2229 
   2230   underlines_.clear();
   2231   if (isAttributedString) {
   2232     ExtractUnderlines(string, &underlines_);
   2233   } else {
   2234     // Use a thin black underline by default.
   2235     underlines_.push_back(
   2236         WebKit::WebCompositionUnderline(0, length, SK_ColorBLACK, false));
   2237   }
   2238 
   2239   // If we are handling a key down event, then SetComposition() will be
   2240   // called in keyEvent: method.
   2241   // Input methods of Mac use setMarkedText calls with an empty text to cancel
   2242   // an ongoing composition. So, we should check whether or not the given text
   2243   // is empty to update the input method state. (Our input method backend can
   2244   // automatically cancels an ongoing composition when we send an empty text.
   2245   // So, it is OK to send an empty text to the renderer.)
   2246   if (!handlingKeyDown_) {
   2247     renderWidgetHostView_->render_widget_host_->ImeSetComposition(
   2248         markedText_, underlines_,
   2249         newSelRange.location, NSMaxRange(newSelRange));
   2250   }
   2251 }
   2252 
   2253 - (void)doCommandBySelector:(SEL)selector {
   2254   // An input method calls this function to dispatch an editing command to be
   2255   // handled by this view.
   2256   if (selector == @selector(noop:))
   2257     return;
   2258 
   2259   std::string command(
   2260       [RWHVMEditCommandHelper::CommandNameForSelector(selector) UTF8String]);
   2261 
   2262   // If this method is called when handling a key down event, then we need to
   2263   // handle the command in the key event handler. Otherwise we can just handle
   2264   // it here.
   2265   if (handlingKeyDown_) {
   2266     hasEditCommands_ = YES;
   2267     // We ignore commands that insert characters, because this was causing
   2268     // strange behavior (e.g. tab always inserted a tab rather than moving to
   2269     // the next field on the page).
   2270     if (!StartsWithASCII(command, "insert", false))
   2271       editCommands_.push_back(EditCommand(command, ""));
   2272   } else {
   2273     renderWidgetHostView_->render_widget_host_->ForwardEditCommand(command, "");
   2274   }
   2275 }
   2276 
   2277 - (void)insertText:(id)string {
   2278   // An input method has characters to be inserted.
   2279   // Same as Linux, Mac calls this method not only:
   2280   // * when an input method finishs composing text, but also;
   2281   // * when we type an ASCII character (without using input methods).
   2282   // When we aren't using input methods, we should send the given character as
   2283   // a Char event so it is dispatched to an onkeypress() event handler of
   2284   // JavaScript.
   2285   // On the other hand, when we are using input methods, we should send the
   2286   // given characters as an input method event and prevent the characters from
   2287   // being dispatched to onkeypress() event handlers.
   2288   // Text inserting might be initiated by other source instead of keyboard
   2289   // events, such as the Characters dialog. In this case the text should be
   2290   // sent as an input method event as well.
   2291   BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
   2292   NSString* im_text = isAttributedString ? [string string] : string;
   2293   if (handlingKeyDown_) {
   2294     textToBeInserted_.append(base::SysNSStringToUTF16(im_text));
   2295   } else {
   2296     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
   2297         base::SysNSStringToUTF16(im_text));
   2298   }
   2299 
   2300   // Inserting text will delete all marked text automatically.
   2301   hasMarkedText_ = NO;
   2302 }
   2303 
   2304 - (void)viewDidMoveToWindow {
   2305   if (canBeKeyView_) {
   2306     NSWindow* newWindow = [self window];
   2307     // Pointer comparison only, since we don't know if lastWindow_ is still
   2308     // valid.
   2309     if (newWindow) {
   2310       // If we move into a new window, refresh the frame information. We
   2311       // don't need to do it if it was the same window as it used to be in,
   2312       // since that case is covered by DidBecomeSelected. We only want to
   2313       // do this for real browser views, not popups.
   2314       if (newWindow != lastWindow_) {
   2315         lastWindow_ = newWindow;
   2316         renderWidgetHostView_->WindowFrameChanged();
   2317       }
   2318       renderWidgetHostView_->ForceTextureReload();
   2319     }
   2320   }
   2321 
   2322   // If we switch windows (or are removed from the view hierarchy), cancel any
   2323   // open mouse-downs.
   2324   if (hasOpenMouseDown_) {
   2325     WebMouseEvent event;
   2326     event.type = WebInputEvent::MouseUp;
   2327     event.button = WebMouseEvent::ButtonLeft;
   2328     if (renderWidgetHostView_->render_widget_host_)
   2329       renderWidgetHostView_->render_widget_host_->ForwardMouseEvent(event);
   2330 
   2331     hasOpenMouseDown_ = NO;
   2332   }
   2333 }
   2334 
   2335 - (void)undo:(id)sender {
   2336   if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
   2337     static_cast<RenderViewHost*>(renderWidgetHostView_->render_widget_host_)->
   2338       Undo();
   2339   }
   2340 }
   2341 
   2342 - (void)redo:(id)sender {
   2343   if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
   2344     static_cast<RenderViewHost*>(renderWidgetHostView_->render_widget_host_)->
   2345       Redo();
   2346   }
   2347 }
   2348 
   2349 - (void)cut:(id)sender {
   2350   if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
   2351     static_cast<RenderViewHost*>(renderWidgetHostView_->render_widget_host_)->
   2352       Cut();
   2353   }
   2354 }
   2355 
   2356 - (void)copy:(id)sender {
   2357   if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
   2358     static_cast<RenderViewHost*>(renderWidgetHostView_->render_widget_host_)->
   2359       Copy();
   2360   }
   2361 }
   2362 
   2363 - (void)copyToFindPboard:(id)sender {
   2364   if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
   2365     static_cast<RenderViewHost*>(renderWidgetHostView_->render_widget_host_)->
   2366       CopyToFindPboard();
   2367   }
   2368 }
   2369 
   2370 - (void)paste:(id)sender {
   2371   if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
   2372     static_cast<RenderViewHost*>(renderWidgetHostView_->render_widget_host_)->
   2373       Paste();
   2374   }
   2375 }
   2376 
   2377 - (void)pasteAsPlainText:(id)sender {
   2378   if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
   2379     static_cast<RenderViewHost*>(renderWidgetHostView_->render_widget_host_)->
   2380       ForwardEditCommand("PasteAndMatchStyle", "");
   2381   }
   2382 }
   2383 
   2384 - (void)cancelComposition {
   2385   if (!hasMarkedText_)
   2386     return;
   2387 
   2388   // Cancel the ongoing composition. [NSInputManager markedTextAbandoned:]
   2389   // doesn't call any NSTextInput functions, such as setMarkedText or
   2390   // insertText. So, we need to send an IPC message to a renderer so it can
   2391   // delete the composition node.
   2392   NSInputManager *currentInputManager = [NSInputManager currentInputManager];
   2393   [currentInputManager markedTextAbandoned:self];
   2394 
   2395   hasMarkedText_ = NO;
   2396   // Should not call [self unmarkText] here, because it'll send unnecessary
   2397   // cancel composition IPC message to the renderer.
   2398 }
   2399 
   2400 - (void)confirmComposition {
   2401   if (!hasMarkedText_)
   2402     return;
   2403 
   2404   if (renderWidgetHostView_->render_widget_host_)
   2405     renderWidgetHostView_->render_widget_host_->ImeConfirmComposition();
   2406 
   2407   [self cancelComposition];
   2408 }
   2409 
   2410 - (void)setPluginImeActive:(BOOL)active {
   2411   if (active == pluginImeActive_)
   2412     return;
   2413 
   2414   pluginImeActive_ = active;
   2415   if (!active) {
   2416     [[ComplexTextInputPanel sharedComplexTextInputPanel] cancelComposition];
   2417     renderWidgetHostView_->PluginImeCompositionCompleted(
   2418         string16(), focusedPluginIdentifier_);
   2419   }
   2420 }
   2421 
   2422 - (void)pluginFocusChanged:(BOOL)focused forPlugin:(int)pluginId {
   2423   if (focused)
   2424     focusedPluginIdentifier_ = pluginId;
   2425   else if (focusedPluginIdentifier_ == pluginId)
   2426     focusedPluginIdentifier_ = -1;
   2427 
   2428   // Whenever plugin focus changes, plugin IME resets.
   2429   [self setPluginImeActive:NO];
   2430 }
   2431 
   2432 - (BOOL)postProcessEventForPluginIme:(NSEvent*)event {
   2433   if (!pluginImeActive_)
   2434     return false;
   2435 
   2436   ComplexTextInputPanel* inputPanel =
   2437       [ComplexTextInputPanel sharedComplexTextInputPanel];
   2438   NSString* composited_string = nil;
   2439   BOOL handled = [inputPanel interpretKeyEvent:event
   2440                                         string:&composited_string];
   2441   if (composited_string) {
   2442     renderWidgetHostView_->PluginImeCompositionCompleted(
   2443         base::SysNSStringToUTF16(composited_string), focusedPluginIdentifier_);
   2444     pluginImeActive_ = NO;
   2445   }
   2446   return handled;
   2447 }
   2448 
   2449 - (void)checkForPluginImeCancellation {
   2450   if (pluginImeActive_ &&
   2451       ![[ComplexTextInputPanel sharedComplexTextInputPanel] inComposition]) {
   2452     renderWidgetHostView_->PluginImeCompositionCompleted(
   2453         string16(), focusedPluginIdentifier_);
   2454     pluginImeActive_ = NO;
   2455   }
   2456 }
   2457 
   2458 - (ViewID)viewID {
   2459   return VIEW_ID_TAB_CONTAINER_FOCUS_VIEW;
   2460 }
   2461 
   2462 // Overriding a NSResponder method to support application services.
   2463 
   2464 - (id)validRequestorForSendType:(NSString*)sendType
   2465                      returnType:(NSString*)returnType {
   2466   id requestor = nil;
   2467   BOOL sendTypeIsString = [sendType isEqual:NSStringPboardType];
   2468   BOOL returnTypeIsString = [returnType isEqual:NSStringPboardType];
   2469   BOOL hasText = !renderWidgetHostView_->selected_text().empty();
   2470   BOOL takesText =
   2471       renderWidgetHostView_->text_input_type_ != WebKit::WebTextInputTypeNone;
   2472 
   2473   if (sendTypeIsString && hasText && !returnType) {
   2474     requestor = self;
   2475   } else if (!sendType && returnTypeIsString && takesText) {
   2476     requestor = self;
   2477   } else if (sendTypeIsString && returnTypeIsString && hasText && takesText) {
   2478     requestor = self;
   2479   } else {
   2480     requestor = [super validRequestorForSendType:sendType
   2481                                       returnType:returnType];
   2482   }
   2483   return requestor;
   2484 }
   2485 
   2486 @end
   2487 
   2488 //
   2489 // Supporting application services
   2490 //
   2491 @implementation RenderWidgetHostViewCocoa(NSServicesRequests)
   2492 
   2493 - (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard
   2494                              types:(NSArray*)types {
   2495   const std::string& str = renderWidgetHostView_->selected_text();
   2496   if (![types containsObject:NSStringPboardType] || str.empty()) return NO;
   2497 
   2498   scoped_nsobject<NSString> text([[NSString alloc]
   2499                                    initWithUTF8String:str.c_str()]);
   2500   NSArray* toDeclare = [NSArray arrayWithObject:NSStringPboardType];
   2501   [pboard declareTypes:toDeclare owner:nil];
   2502   return [pboard setString:text forType:NSStringPboardType];
   2503 }
   2504 
   2505 - (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard {
   2506   NSString *string = [pboard stringForType:NSStringPboardType];
   2507   if (!string) return NO;
   2508 
   2509   // If the user is currently using an IME, confirm the IME input,
   2510   // and then insert the text from the service, the same as TextEdit and Safari.
   2511   [self confirmComposition];
   2512   [self insertText:string];
   2513   return YES;
   2514 }
   2515 
   2516 @end
   2517