• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
2// reserved. Use of this source code is governed by a BSD-style license that
3// can be found in the LICENSE file.
4
5#include "tests/cefclient/browser/browser_window_osr_mac.h"
6
7#include <Cocoa/Cocoa.h>
8#include <OpenGL/gl.h>
9#import <objc/runtime.h>
10
11#include "include/base/cef_logging.h"
12#include "include/cef_parser.h"
13#include "include/wrapper/cef_closure_task.h"
14#include "tests/cefclient/browser/bytes_write_handler.h"
15#include "tests/cefclient/browser/main_context.h"
16#include "tests/cefclient/browser/osr_accessibility_helper.h"
17#include "tests/cefclient/browser/osr_accessibility_node.h"
18#include "tests/cefclient/browser/text_input_client_osr_mac.h"
19#include "tests/shared/browser/geometry_util.h"
20#include "tests/shared/browser/main_message_loop.h"
21
22#import <AppKit/NSAccessibility.h>
23
24@interface BrowserOpenGLView
25    : NSOpenGLView <NSDraggingSource, NSDraggingDestination, NSAccessibility> {
26 @private
27  NSTrackingArea* tracking_area_;
28  client::BrowserWindowOsrMac* browser_window_;
29  client::OsrRenderer* renderer_;
30  NSPoint last_mouse_pos_;
31  NSPoint cur_mouse_pos_;
32  bool rotating_;
33
34  bool was_last_mouse_down_on_view_;
35
36  float device_scale_factor_;
37
38  // Drag and drop.
39  CefRefPtr<CefDragData> current_drag_data_;
40  NSDragOperation current_drag_op_;
41  NSDragOperation current_allowed_ops_;
42  NSPasteboard* pasteboard_;
43  NSString* fileUTI_;
44
45  // For intreacting with IME.
46  NSTextInputContext* text_input_context_osr_mac_;
47  CefTextInputClientOSRMac* text_input_client_;
48
49  // Manages Accessibility Tree
50  client::OsrAccessibilityHelper* accessibility_helper_;
51
52  // Event monitor for scroll wheel end event.
53  id endWheelMonitor_;
54}
55
56@end  // @interface BrowserOpenGLView
57
58namespace {
59
60NSString* const kCEFDragDummyPboardType = @"org.CEF.drag-dummy-type";
61NSString* const kNSURLTitlePboardType = @"public.url-name";
62
63class ScopedGLContext {
64 public:
65  ScopedGLContext(BrowserOpenGLView* view, bool swap_buffers)
66      : swap_buffers_(swap_buffers) {
67    context_ = [view openGLContext];
68    [context_ makeCurrentContext];
69  }
70  ~ScopedGLContext() {
71    [NSOpenGLContext clearCurrentContext];
72    if (swap_buffers_)
73      [context_ flushBuffer];
74  }
75
76 private:
77  NSOpenGLContext* context_;
78  const bool swap_buffers_;
79};
80
81NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) {
82  NSRect point_rect = NSMakeRect(point.x, point.y, 0, 0);
83  return [window convertRectToScreen:point_rect].origin;
84}
85
86}  // namespace
87
88@implementation BrowserOpenGLView
89
90- (id)initWithFrame:(NSRect)frame
91    andBrowserWindow:(client::BrowserWindowOsrMac*)browser_window
92         andRenderer:(client::OsrRenderer*)renderer {
93  NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc]
94      initWithAttributes:(NSOpenGLPixelFormatAttribute[]){
95                             NSOpenGLPFADoubleBuffer, NSOpenGLPFADepthSize, 32,
96                             0}];
97#if !__has_feature(objc_arc)
98  [pixelFormat autorelease];
99#endif  // !__has_feature(objc_arc)
100
101  if (self = [super initWithFrame:frame pixelFormat:pixelFormat]) {
102    browser_window_ = browser_window;
103    renderer_ = renderer;
104    rotating_ = false;
105    endWheelMonitor_ = nil;
106    device_scale_factor_ = 1.0f;
107
108    tracking_area_ = [[NSTrackingArea alloc]
109        initWithRect:frame
110             options:NSTrackingMouseMoved | NSTrackingActiveInActiveApp |
111                     NSTrackingInVisibleRect
112               owner:self
113            userInfo:nil];
114    [self addTrackingArea:tracking_area_];
115
116    // enable HiDPI buffer
117    [self setWantsBestResolutionOpenGLSurface:YES];
118
119    [self resetDragDrop];
120
121    NSArray* types = [NSArray
122        arrayWithObjects:kCEFDragDummyPboardType, NSStringPboardType,
123                         NSFilenamesPboardType, NSPasteboardTypeString, nil];
124    [self registerForDraggedTypes:types];
125  }
126
127  return self;
128}
129
130- (void)dealloc {
131  [[NSNotificationCenter defaultCenter]
132      removeObserver:self
133                name:NSWindowDidChangeBackingPropertiesNotification
134              object:nil];
135#if !__has_feature(objc_arc)
136  if (text_input_context_osr_mac_) {
137    [text_input_client_ release];
138    [text_input_context_osr_mac_ release];
139  }
140  [super dealloc];
141#endif  // !__has_feature(objc_arc)
142}
143
144- (void)detach {
145  renderer_ = NULL;
146  browser_window_ = NULL;
147  if (text_input_client_)
148    [text_input_client_ detach];
149}
150
151- (CefRefPtr<CefBrowser>)getBrowser {
152  if (browser_window_)
153    return browser_window_->GetBrowser();
154  return NULL;
155}
156
157- (void)setFrame:(NSRect)frameRect {
158  CefRefPtr<CefBrowser> browser = [self getBrowser];
159  if (!browser.get())
160    return;
161
162  [super setFrame:frameRect];
163  browser->GetHost()->WasResized();
164}
165
166- (void)sendMouseClick:(NSEvent*)event
167                button:(CefBrowserHost::MouseButtonType)type
168                  isUp:(bool)isUp {
169  CefRefPtr<CefBrowser> browser = [self getBrowser];
170  if (!browser.get())
171    return;
172
173  CefMouseEvent mouseEvent;
174  [self getMouseEvent:mouseEvent forEvent:event];
175
176  // |point| is in OS X view coordinates.
177  NSPoint point = [self getClickPointForEvent:event];
178
179  // Convert to device coordinates.
180  point = [self convertPointToBackingInternal:point];
181
182  if (!isUp) {
183    was_last_mouse_down_on_view_ = ![self isOverPopupWidgetX:point.x
184                                                        andY:point.y];
185  } else if (was_last_mouse_down_on_view_ &&
186             [self isOverPopupWidgetX:point.x andY:point.y] &&
187             ([self getPopupXOffset] || [self getPopupYOffset])) {
188    return;
189  }
190
191  browser->GetHost()->SendMouseClickEvent(mouseEvent, type, isUp,
192                                          [event clickCount]);
193}
194
195- (void)mouseDown:(NSEvent*)event {
196  [self sendMouseClick:event button:MBT_LEFT isUp:false];
197}
198
199- (void)rightMouseDown:(NSEvent*)event {
200  if ([event modifierFlags] & NSShiftKeyMask) {
201    // Start rotation effect.
202    last_mouse_pos_ = cur_mouse_pos_ = [self getClickPointForEvent:event];
203    rotating_ = true;
204    return;
205  }
206
207  [self sendMouseClick:event button:MBT_RIGHT isUp:false];
208}
209
210- (void)otherMouseDown:(NSEvent*)event {
211  [self sendMouseClick:event button:MBT_MIDDLE isUp:false];
212}
213
214- (void)mouseUp:(NSEvent*)event {
215  [self sendMouseClick:event button:MBT_LEFT isUp:true];
216}
217
218- (void)rightMouseUp:(NSEvent*)event {
219  if (rotating_) {
220    // End rotation effect.
221    renderer_->SetSpin(0, 0);
222    rotating_ = false;
223    [self setNeedsDisplay:YES];
224    return;
225  }
226  [self sendMouseClick:event button:MBT_RIGHT isUp:true];
227}
228
229- (void)otherMouseUp:(NSEvent*)event {
230  [self sendMouseClick:event button:MBT_MIDDLE isUp:true];
231}
232
233- (void)mouseMoved:(NSEvent*)event {
234  CefRefPtr<CefBrowser> browser = [self getBrowser];
235  if (!browser.get())
236    return;
237
238  if (rotating_) {
239    // Apply rotation effect.
240    cur_mouse_pos_ = [self getClickPointForEvent:event];
241    ;
242    renderer_->IncrementSpin((cur_mouse_pos_.x - last_mouse_pos_.x),
243                             (cur_mouse_pos_.y - last_mouse_pos_.y));
244    last_mouse_pos_ = cur_mouse_pos_;
245    [self setNeedsDisplay:YES];
246    return;
247  }
248
249  CefMouseEvent mouseEvent;
250  [self getMouseEvent:mouseEvent forEvent:event];
251
252  browser->GetHost()->SendMouseMoveEvent(mouseEvent, false);
253}
254
255- (void)mouseDragged:(NSEvent*)event {
256  [self mouseMoved:event];
257}
258
259- (void)rightMouseDragged:(NSEvent*)event {
260  [self mouseMoved:event];
261}
262
263- (void)otherMouseDragged:(NSEvent*)event {
264  [self mouseMoved:event];
265}
266
267- (void)mouseEntered:(NSEvent*)event {
268  [self mouseMoved:event];
269}
270
271- (void)mouseExited:(NSEvent*)event {
272  CefRefPtr<CefBrowser> browser = [self getBrowser];
273  if (!browser.get())
274    return;
275
276  CefMouseEvent mouseEvent;
277  [self getMouseEvent:mouseEvent forEvent:event];
278
279  browser->GetHost()->SendMouseMoveEvent(mouseEvent, true);
280}
281
282- (void)keyDown:(NSEvent*)event {
283  CefRefPtr<CefBrowser> browser = [self getBrowser];
284  if (!browser.get() || !text_input_context_osr_mac_)
285    return;
286
287  if ([event type] != NSFlagsChanged) {
288    if (text_input_client_) {
289      [text_input_client_ HandleKeyEventBeforeTextInputClient:event];
290
291      // The return value of this method seems to always be set to YES, thus we
292      // ignore it and ask the host view whether IME is active or not.
293      [text_input_context_osr_mac_ handleEvent:event];
294
295      CefKeyEvent keyEvent;
296      [self getKeyEvent:keyEvent forEvent:event];
297
298      [text_input_client_ HandleKeyEventAfterTextInputClient:keyEvent];
299    }
300  }
301
302  // Check for Caps lock and Toggle Touch Emulation
303  if (client::MainContext::Get()->TouchEventsEnabled())
304    [self toggleTouchEmulation:event];
305}
306
307// OSX does not have touch screens, so we emulate it by mapping multitouch
308// events on TrackPad to Touch Events on Screen. To ensure it does not
309// interfere with other Trackpad events, this mapping is only enabled if
310// touch-events=enabled commandline is passed and caps lock key is on.
311- (void)toggleTouchEmulation:(NSEvent*)event {
312  if ([event type] == NSFlagsChanged && [event keyCode] == 0x39) {
313    NSUInteger flags = [event modifierFlags];
314    BOOL touch_enabled = flags & NSAlphaShiftKeyMask ? YES : NO;
315    [self setAcceptsTouchEvents:touch_enabled];
316  }
317}
318
319- (cef_touch_event_type_t)getTouchPhase:(NSTouchPhase)phase {
320  cef_touch_event_type_t event_type = CEF_TET_RELEASED;
321  switch (phase) {
322    case NSTouchPhaseBegan:
323      event_type = CEF_TET_PRESSED;
324      break;
325    case NSTouchPhaseMoved:
326      event_type = CEF_TET_MOVED;
327      break;
328    case NSTouchPhaseEnded:
329      event_type = CEF_TET_RELEASED;
330      break;
331    case NSTouchPhaseCancelled:
332      event_type = CEF_TET_CANCELLED;
333      break;
334    default:
335      break;
336  }
337  return event_type;
338}
339
340// Translate NSTouch events to CefTouchEvents and send to browser.
341- (void)sendTouchEvent:(NSEvent*)event touchPhase:(NSTouchPhase)phase {
342  int modifiers = [self getModifiersForEvent:event];
343  CefRefPtr<CefBrowser> browser = [self getBrowser];
344
345  NSSet* touches = [event touchesMatchingPhase:phase inView:self];
346
347  for (NSTouch* touch in touches) {
348    // Convert NSTouch to CefTouchEvent.
349    CefTouchEvent touch_event;
350
351    // NSTouch.identity is unique during the life of the touch
352    touch_event.id = touch.identity.hash;
353    touch_event.type = [self getTouchPhase:phase];
354
355    NSPoint scaled_pos = [touch normalizedPosition];
356    NSSize view_size = [self bounds].size;
357
358    // Map point on Touch Device to View coordinates.
359    NSPoint touch_point = NSMakePoint(scaled_pos.x * view_size.width,
360                                      scaled_pos.y * view_size.height);
361
362    NSPoint contentLocal = [self convertPoint:touch_point fromView:nil];
363    NSPoint point;
364    point.x = contentLocal.x;
365    point.y = [self frame].size.height - contentLocal.y;  // Flip y.
366
367    // Convert to device coordinates.
368    point = [self convertPointToBackingInternal:point];
369
370    int device_x = point.x;
371    int device_y = point.y;
372
373    const float device_scale_factor = [self getDeviceScaleFactor];
374    // Convert to browser view coordinates.
375    touch_event.x = client::DeviceToLogical(device_x, device_scale_factor);
376    touch_event.y = client::DeviceToLogical(device_y, device_scale_factor);
377
378    touch_event.radius_x = 0;
379    touch_event.radius_y = 0;
380
381    touch_event.rotation_angle = 0;
382    touch_event.pressure = 0;
383
384    touch_event.modifiers = modifiers;
385
386    // Notify the browser of touch event.
387    browser->GetHost()->SendTouchEvent(touch_event);
388  }
389}
390
391- (void)touchesBeganWithEvent:(NSEvent*)event {
392  [self sendTouchEvent:event touchPhase:NSTouchPhaseBegan];
393}
394
395- (void)touchesMovedWithEvent:(NSEvent*)event {
396  [self sendTouchEvent:event touchPhase:NSTouchPhaseMoved];
397}
398
399- (void)touchesEndedWithEvent:(NSEvent*)event {
400  [self sendTouchEvent:event touchPhase:NSTouchPhaseEnded];
401}
402
403- (void)touchesCancelledWithEvent:(NSEvent*)event {
404  [self sendTouchEvent:event touchPhase:NSTouchPhaseCancelled];
405}
406
407- (void)keyUp:(NSEvent*)event {
408  CefRefPtr<CefBrowser> browser = [self getBrowser];
409  if (!browser.get())
410    return;
411
412  CefKeyEvent keyEvent;
413  [self getKeyEvent:keyEvent forEvent:event];
414
415  keyEvent.type = KEYEVENT_KEYUP;
416  browser->GetHost()->SendKeyEvent(keyEvent);
417}
418
419- (void)flagsChanged:(NSEvent*)event {
420  if ([self isKeyUpEvent:event])
421    [self keyUp:event];
422  else
423    [self keyDown:event];
424}
425
426- (void)shortCircuitScrollWheelEvent:(NSEvent*)event {
427  if ([event phase] != NSEventPhaseEnded &&
428      [event phase] != NSEventPhaseCancelled)
429    return;
430
431  [self sendScrollWheelEvet:event];
432
433  if (endWheelMonitor_) {
434    [NSEvent removeMonitor:endWheelMonitor_];
435    endWheelMonitor_ = nil;
436  }
437}
438
439- (void)scrollWheel:(NSEvent*)event {
440  // Use an NSEvent monitor to listen for the wheel-end end. This ensures that
441  // the event is received even when the mouse cursor is no longer over the
442  // view when the scrolling ends. Also it avoids sending duplicate scroll
443  // events to the renderer.
444  if ([event phase] == NSEventPhaseBegan && !endWheelMonitor_) {
445    endWheelMonitor_ = [NSEvent
446        addLocalMonitorForEventsMatchingMask:NSScrollWheelMask
447                                     handler:^(NSEvent* blockEvent) {
448                                       [self shortCircuitScrollWheelEvent:
449                                                 blockEvent];
450                                       return blockEvent;
451                                     }];
452  }
453
454  [self sendScrollWheelEvet:event];
455}
456
457- (void)sendScrollWheelEvet:(NSEvent*)event {
458  CefRefPtr<CefBrowser> browser = [self getBrowser];
459  if (!browser.get())
460    return;
461
462  CGEventRef cgEvent = [event CGEvent];
463  DCHECK(cgEvent);
464
465  int deltaX =
466      CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis2);
467  int deltaY =
468      CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis1);
469
470  CefMouseEvent mouseEvent;
471  [self getMouseEvent:mouseEvent forEvent:event];
472
473  browser->GetHost()->SendMouseWheelEvent(mouseEvent, deltaX, deltaY);
474}
475
476- (BOOL)canBecomeKeyView {
477  CefRefPtr<CefBrowser> browser = [self getBrowser];
478  return (browser.get() != NULL);
479}
480
481- (BOOL)acceptsFirstResponder {
482  CefRefPtr<CefBrowser> browser = [self getBrowser];
483  return (browser.get() != NULL);
484}
485
486- (BOOL)becomeFirstResponder {
487  CefRefPtr<CefBrowser> browser = [self getBrowser];
488  if (browser.get()) {
489    browser->GetHost()->SendFocusEvent(true);
490    return [super becomeFirstResponder];
491  }
492
493  return NO;
494}
495
496- (BOOL)resignFirstResponder {
497  CefRefPtr<CefBrowser> browser = [self getBrowser];
498  if (browser.get()) {
499    browser->GetHost()->SendFocusEvent(false);
500    return [super resignFirstResponder];
501  }
502
503  return NO;
504}
505
506- (void)undo:(id)sender {
507  CefRefPtr<CefBrowser> browser = [self getBrowser];
508  if (browser.get())
509    browser->GetFocusedFrame()->Undo();
510}
511
512- (void)redo:(id)sender {
513  CefRefPtr<CefBrowser> browser = [self getBrowser];
514  if (browser.get())
515    browser->GetFocusedFrame()->Redo();
516}
517
518- (void)cut:(id)sender {
519  CefRefPtr<CefBrowser> browser = [self getBrowser];
520  if (browser.get())
521    browser->GetFocusedFrame()->Cut();
522}
523
524- (void)copy:(id)sender {
525  CefRefPtr<CefBrowser> browser = [self getBrowser];
526  if (browser.get())
527    browser->GetFocusedFrame()->Copy();
528}
529
530- (void)paste:(id)sender {
531  CefRefPtr<CefBrowser> browser = [self getBrowser];
532  if (browser.get())
533    browser->GetFocusedFrame()->Paste();
534}
535
536- (void)delete:(id)sender {
537  CefRefPtr<CefBrowser> browser = [self getBrowser];
538  if (browser.get())
539    browser->GetFocusedFrame()->Delete();
540}
541
542- (void)selectAll:(id)sender {
543  CefRefPtr<CefBrowser> browser = [self getBrowser];
544  if (browser.get())
545    browser->GetFocusedFrame()->SelectAll();
546}
547
548- (NSPoint)getClickPointForEvent:(NSEvent*)event {
549  NSPoint windowLocal = [event locationInWindow];
550  NSPoint contentLocal = [self convertPoint:windowLocal fromView:nil];
551
552  NSPoint point;
553  point.x = contentLocal.x;
554  point.y = [self frame].size.height - contentLocal.y;  // Flip y.
555  return point;
556}
557
558- (void)getKeyEvent:(CefKeyEvent&)keyEvent forEvent:(NSEvent*)event {
559  if ([event type] == NSKeyDown || [event type] == NSKeyUp) {
560    NSString* s = [event characters];
561    if ([s length] > 0)
562      keyEvent.character = [s characterAtIndex:0];
563
564    s = [event charactersIgnoringModifiers];
565    if ([s length] > 0)
566      keyEvent.unmodified_character = [s characterAtIndex:0];
567  }
568
569  if ([event type] == NSFlagsChanged) {
570    keyEvent.character = 0;
571    keyEvent.unmodified_character = 0;
572  }
573
574  keyEvent.native_key_code = [event keyCode];
575
576  keyEvent.modifiers = [self getModifiersForEvent:event];
577}
578
579- (NSTextInputContext*)inputContext {
580  if (!text_input_context_osr_mac_) {
581    text_input_client_ =
582        [[CefTextInputClientOSRMac alloc] initWithBrowser:[self getBrowser]];
583    text_input_context_osr_mac_ =
584        [[NSTextInputContext alloc] initWithClient:text_input_client_];
585#if !__has_feature(objc_arc)
586    [text_input_client_ retain];
587    [text_input_context_osr_mac_ retain];
588#endif  // !__has_feature(objc_arc)
589  }
590
591  return text_input_context_osr_mac_;
592}
593
594- (void)getMouseEvent:(CefMouseEvent&)mouseEvent forEvent:(NSEvent*)event {
595  const float device_scale_factor = [self getDeviceScaleFactor];
596
597  // |point| is in OS X view coordinates.
598  NSPoint point = [self getClickPointForEvent:event];
599
600  // Convert to device coordinates.
601  point = [self convertPointToBackingInternal:point];
602
603  int device_x = point.x;
604  int device_y = point.y;
605  if ([self isOverPopupWidgetX:device_x andY:device_y])
606    [self applyPopupOffsetToX:device_x andY:device_y];
607
608  // Convert to browser view coordinates.
609  mouseEvent.x = client::DeviceToLogical(device_x, device_scale_factor);
610  mouseEvent.y = client::DeviceToLogical(device_y, device_scale_factor);
611
612  mouseEvent.modifiers = [self getModifiersForEvent:event];
613}
614
615- (void)getMouseEvent:(CefMouseEvent&)mouseEvent
616          forDragInfo:(id<NSDraggingInfo>)info {
617  const float device_scale_factor = [self getDeviceScaleFactor];
618
619  // |point| is in OS X view coordinates.
620  NSPoint windowPoint = [info draggingLocation];
621  NSPoint point = [self flipWindowPointToView:windowPoint];
622
623  // Convert to device coordinates.
624  point = [self convertPointToBackingInternal:point];
625
626  // Convert to browser view coordinates.
627  mouseEvent.x = client::DeviceToLogical(point.x, device_scale_factor);
628  mouseEvent.y = client::DeviceToLogical(point.y, device_scale_factor);
629
630  mouseEvent.modifiers = [NSEvent modifierFlags];
631}
632
633- (int)getModifiersForEvent:(NSEvent*)event {
634  int modifiers = 0;
635
636  if ([event modifierFlags] & NSControlKeyMask)
637    modifiers |= EVENTFLAG_CONTROL_DOWN;
638  if ([event modifierFlags] & NSShiftKeyMask)
639    modifiers |= EVENTFLAG_SHIFT_DOWN;
640  if ([event modifierFlags] & NSAlternateKeyMask)
641    modifiers |= EVENTFLAG_ALT_DOWN;
642  if ([event modifierFlags] & NSCommandKeyMask)
643    modifiers |= EVENTFLAG_COMMAND_DOWN;
644  if ([event modifierFlags] & NSAlphaShiftKeyMask)
645    modifiers |= EVENTFLAG_CAPS_LOCK_ON;
646
647  if ([event type] == NSKeyUp || [event type] == NSKeyDown ||
648      [event type] == NSFlagsChanged) {
649    // Only perform this check for key events
650    if ([self isKeyPadEvent:event])
651      modifiers |= EVENTFLAG_IS_KEY_PAD;
652  }
653
654  // OS X does not have a modifier for NumLock, so I'm not entirely sure how to
655  // set EVENTFLAG_NUM_LOCK_ON;
656  //
657  // There is no EVENTFLAG for the function key either.
658
659  // Mouse buttons
660  switch ([event type]) {
661    case NSLeftMouseDragged:
662    case NSLeftMouseDown:
663    case NSLeftMouseUp:
664      modifiers |= EVENTFLAG_LEFT_MOUSE_BUTTON;
665      break;
666    case NSRightMouseDragged:
667    case NSRightMouseDown:
668    case NSRightMouseUp:
669      modifiers |= EVENTFLAG_RIGHT_MOUSE_BUTTON;
670      break;
671    case NSOtherMouseDragged:
672    case NSOtherMouseDown:
673    case NSOtherMouseUp:
674      modifiers |= EVENTFLAG_MIDDLE_MOUSE_BUTTON;
675      break;
676    default:
677      break;
678  }
679
680  return modifiers;
681}
682
683- (BOOL)isKeyUpEvent:(NSEvent*)event {
684  if ([event type] != NSFlagsChanged)
685    return [event type] == NSKeyUp;
686
687  // FIXME: This logic fails if the user presses both Shift keys at once, for
688  // example: we treat releasing one of them as keyDown.
689  switch ([event keyCode]) {
690    case 54:  // Right Command
691    case 55:  // Left Command
692      return ([event modifierFlags] & NSCommandKeyMask) == 0;
693
694    case 57:  // Capslock
695      return ([event modifierFlags] & NSAlphaShiftKeyMask) == 0;
696
697    case 56:  // Left Shift
698    case 60:  // Right Shift
699      return ([event modifierFlags] & NSShiftKeyMask) == 0;
700
701    case 58:  // Left Alt
702    case 61:  // Right Alt
703      return ([event modifierFlags] & NSAlternateKeyMask) == 0;
704
705    case 59:  // Left Ctrl
706    case 62:  // Right Ctrl
707      return ([event modifierFlags] & NSControlKeyMask) == 0;
708
709    case 63:  // Function
710      return ([event modifierFlags] & NSFunctionKeyMask) == 0;
711  }
712  return false;
713}
714
715- (BOOL)isKeyPadEvent:(NSEvent*)event {
716  if ([event modifierFlags] & NSNumericPadKeyMask)
717    return true;
718
719  switch ([event keyCode]) {
720    case 71:  // Clear
721    case 81:  // =
722    case 75:  // /
723    case 67:  // *
724    case 78:  // -
725    case 69:  // +
726    case 76:  // Enter
727    case 65:  // .
728    case 82:  // 0
729    case 83:  // 1
730    case 84:  // 2
731    case 85:  // 3
732    case 86:  // 4
733    case 87:  // 5
734    case 88:  // 6
735    case 89:  // 7
736    case 91:  // 8
737    case 92:  // 9
738      return true;
739  }
740
741  return false;
742}
743
744- (void)windowDidChangeBackingProperties:(NSNotification*)notification {
745  // This delegate method is only called on 10.7 and later, so don't worry about
746  // other backing changes calling it on 10.6 or earlier
747  [self resetDeviceScaleFactor];
748}
749
750- (void)drawRect:(NSRect)dirtyRect {
751  CefRefPtr<CefBrowser> browser = [self getBrowser];
752  if ([self inLiveResize] || !browser.get()) {
753    // Fill with the background color.
754    const cef_color_t background_color =
755        client::MainContext::Get()->GetBackgroundColor();
756    NSColor* color = [NSColor
757        colorWithCalibratedRed:float(CefColorGetR(background_color)) / 255.0f
758                         green:float(CefColorGetG(background_color)) / 255.0f
759                          blue:float(CefColorGetB(background_color)) / 255.0f
760                         alpha:1.f];
761    [color setFill];
762    NSRectFill(dirtyRect);
763  }
764
765  // The Invalidate below fixes flicker when resizing.
766  if ([self inLiveResize] && browser.get())
767    browser->GetHost()->Invalidate(PET_VIEW);
768}
769
770// Drag and drop
771
772- (BOOL)startDragging:(CefRefPtr<CefDragData>)drag_data
773           allowedOps:(NSDragOperation)ops
774                point:(NSPoint)position {
775  DCHECK(!pasteboard_);
776  DCHECK(!fileUTI_);
777  DCHECK(!current_drag_data_.get());
778
779  [self resetDragDrop];
780
781  current_allowed_ops_ = ops;
782  current_drag_data_ = drag_data;
783
784  [self fillPasteboard];
785
786  NSEvent* currentEvent = [[NSApplication sharedApplication] currentEvent];
787  NSWindow* window = [self window];
788  NSTimeInterval eventTime = [currentEvent timestamp];
789
790  NSEvent* dragEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
791                                          location:position
792                                     modifierFlags:NSLeftMouseDraggedMask
793                                         timestamp:eventTime
794                                      windowNumber:[window windowNumber]
795                                           context:nil
796                                       eventNumber:0
797                                        clickCount:1
798                                          pressure:1.0];
799
800  // TODO(cef): Pass a non-nil value to dragImage (see issue #1715). For now
801  // work around the "callee requires a non-null argument" error that occurs
802  // when building with the 10.11 SDK.
803  id nilArg = nil;
804  [window dragImage:nilArg
805                 at:position
806             offset:NSZeroSize
807              event:dragEvent
808         pasteboard:pasteboard_
809             source:self
810          slideBack:YES];
811  return YES;
812}
813
814- (void)setCurrentDragOp:(NSDragOperation)op {
815  current_drag_op_ = op;
816}
817
818// NSDraggingSource Protocol
819
820- (NSDragOperation)draggingSession:(NSDraggingSession*)session
821    sourceOperationMaskForDraggingContext:(NSDraggingContext)context {
822  switch (context) {
823    case NSDraggingContextOutsideApplication:
824      return current_allowed_ops_;
825
826    case NSDraggingContextWithinApplication:
827    default:
828      return current_allowed_ops_;
829  }
830}
831
832- (NSArray*)namesOfPromisedFilesDroppedAtDestination:(NSURL*)dropDest {
833  if (![dropDest isFileURL])
834    return nil;
835
836  if (!current_drag_data_)
837    return nil;
838
839  size_t expected_size = current_drag_data_->GetFileContents(NULL);
840  if (expected_size == 0)
841    return nil;
842
843  std::string path = [[dropDest path] UTF8String];
844  path.append("/");
845  path.append(current_drag_data_->GetFileName().ToString());
846
847  CefRefPtr<CefStreamWriter> writer = CefStreamWriter::CreateForFile(path);
848  if (!writer)
849    return nil;
850
851  if (current_drag_data_->GetFileContents(writer) != expected_size)
852    return nil;
853
854  return @[ [NSString stringWithUTF8String:path.c_str()] ];
855}
856
857- (void)draggedImage:(NSImage*)anImage
858             endedAt:(NSPoint)screenPoint
859           operation:(NSDragOperation)operation {
860  CefRefPtr<CefBrowser> browser = [self getBrowser];
861  if (!browser.get())
862    return;
863
864  if (operation == (NSDragOperationMove | NSDragOperationCopy))
865    operation &= ~NSDragOperationMove;
866
867  NSPoint windowPoint = [[self window] convertScreenToBase:screenPoint];
868  NSPoint pt = [self flipWindowPointToView:windowPoint];
869  CefRenderHandler::DragOperation op =
870      static_cast<CefRenderHandler::DragOperation>(operation);
871  browser->GetHost()->DragSourceEndedAt(pt.x, pt.y, op);
872  browser->GetHost()->DragSourceSystemDragEnded();
873  [self resetDragDrop];
874}
875
876// NSDraggingDestination Protocol
877
878- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info {
879  CefRefPtr<CefBrowser> browser = [self getBrowser];
880  if (!browser.get())
881    return NSDragOperationNone;
882
883  CefRefPtr<CefDragData> drag_data;
884  if (!current_drag_data_) {
885    drag_data = CefDragData::Create();
886    [self populateDropData:drag_data fromPasteboard:[info draggingPasteboard]];
887  } else {
888    drag_data = current_drag_data_->Clone();
889    drag_data->ResetFileContents();
890  }
891
892  CefMouseEvent mouseEvent;
893  [self getMouseEvent:mouseEvent forDragInfo:info];
894
895  NSDragOperation mask = [info draggingSourceOperationMask];
896  CefBrowserHost::DragOperationsMask allowed_ops =
897      static_cast<CefBrowserHost::DragOperationsMask>(mask);
898
899  browser->GetHost()->DragTargetDragEnter(drag_data, mouseEvent, allowed_ops);
900  browser->GetHost()->DragTargetDragOver(mouseEvent, allowed_ops);
901
902  current_drag_op_ = NSDragOperationCopy;
903  return current_drag_op_;
904}
905
906- (void)draggingExited:(id<NSDraggingInfo>)sender {
907  CefRefPtr<CefBrowser> browser = [self getBrowser];
908  if (browser.get())
909    browser->GetHost()->DragTargetDragLeave();
910}
911
912- (BOOL)prepareForDragOperation:(id<NSDraggingInfo>)info {
913  return YES;
914}
915
916- (BOOL)performDragOperation:(id<NSDraggingInfo>)info {
917  CefRefPtr<CefBrowser> browser = [self getBrowser];
918  if (!browser.get())
919    return NO;
920
921  CefMouseEvent mouseEvent;
922  [self getMouseEvent:mouseEvent forDragInfo:info];
923
924  browser->GetHost()->DragTargetDrop(mouseEvent);
925
926  return YES;
927}
928
929- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info {
930  CefRefPtr<CefBrowser> browser = [self getBrowser];
931  if (!browser.get())
932    return NSDragOperationNone;
933
934  CefMouseEvent mouseEvent;
935  [self getMouseEvent:mouseEvent forDragInfo:info];
936
937  NSDragOperation mask = [info draggingSourceOperationMask];
938  CefBrowserHost::DragOperationsMask allowed_ops =
939      static_cast<CefBrowserHost::DragOperationsMask>(mask);
940
941  browser->GetHost()->DragTargetDragOver(mouseEvent, allowed_ops);
942
943  return current_drag_op_;
944}
945
946// NSPasteboardOwner Protocol
947
948- (void)pasteboard:(NSPasteboard*)pboard provideDataForType:(NSString*)type {
949  if (!current_drag_data_) {
950    return;
951  }
952
953  // URL.
954  if ([type isEqualToString:NSURLPboardType]) {
955    DCHECK(current_drag_data_->IsLink());
956    NSString* strUrl =
957        [NSString stringWithUTF8String:current_drag_data_->GetLinkURL()
958                                           .ToString()
959                                           .c_str()];
960    NSURL* url = [NSURL URLWithString:strUrl];
961    [url writeToPasteboard:pboard];
962    // URL title.
963  } else if ([type isEqualToString:kNSURLTitlePboardType]) {
964    NSString* strTitle =
965        [NSString stringWithUTF8String:current_drag_data_->GetLinkTitle()
966                                           .ToString()
967                                           .c_str()];
968    [pboard setString:strTitle forType:kNSURLTitlePboardType];
969
970    // File contents.
971  } else if ([type isEqualToString:fileUTI_]) {
972    size_t size = current_drag_data_->GetFileContents(NULL);
973    DCHECK_GT(size, 0U);
974    CefRefPtr<client::BytesWriteHandler> handler =
975        new client::BytesWriteHandler(size);
976    CefRefPtr<CefStreamWriter> writer =
977        CefStreamWriter::CreateForHandler(handler.get());
978    current_drag_data_->GetFileContents(writer);
979    DCHECK_EQ(handler->GetDataSize(), static_cast<int64>(size));
980
981    [pboard setData:[NSData dataWithBytes:handler->GetData()
982                                   length:handler->GetDataSize()]
983            forType:fileUTI_];
984
985    // Plain text.
986  } else if ([type isEqualToString:NSStringPboardType]) {
987    NSString* strTitle =
988        [NSString stringWithUTF8String:current_drag_data_->GetFragmentText()
989                                           .ToString()
990                                           .c_str()];
991    [pboard setString:strTitle forType:NSStringPboardType];
992
993  } else if ([type isEqualToString:kCEFDragDummyPboardType]) {
994    // The dummy type _was_ promised and someone decided to call the bluff.
995    [pboard setData:[NSData data] forType:kCEFDragDummyPboardType];
996  }
997}
998
999// NSAccessibility Protocol implementation.
1000- (BOOL)accessibilityIsIgnored {
1001  if (!accessibility_helper_)
1002    return YES;
1003  else
1004    return NO;
1005}
1006
1007- (id)accessibilityAttributeValue:(NSString*)attribute {
1008  if (!accessibility_helper_)
1009    return [super accessibilityAttributeValue:attribute];
1010  if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
1011    return NSAccessibilityGroupRole;
1012  } else if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) {
1013    client::OsrAXNode* node = accessibility_helper_->GetRootNode();
1014    std::string desc = node ? node->AxDescription() : "";
1015    return [NSString stringWithUTF8String:desc.c_str()];
1016  } else if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
1017    client::OsrAXNode* node = accessibility_helper_->GetRootNode();
1018    std::string desc = node ? node->AxValue() : "";
1019    return [NSString stringWithUTF8String:desc.c_str()];
1020  } else if ([attribute
1021                 isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
1022    return NSAccessibilityRoleDescriptionForUIElement(self);
1023  } else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
1024    client::OsrAXNode* node = accessibility_helper_->GetRootNode();
1025    // Add Root as first Kid
1026    NSMutableArray* kids = [NSMutableArray arrayWithCapacity:1];
1027    NSObject* child = CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(
1028        node->GetNativeAccessibleObject(NULL));
1029    [kids addObject:child];
1030    return NSAccessibilityUnignoredChildren(kids);
1031  } else {
1032    return [super accessibilityAttributeValue:attribute];
1033  }
1034}
1035
1036- (id)accessibilityFocusedUIElement {
1037  if (accessibility_helper_) {
1038    client::OsrAXNode* node = accessibility_helper_->GetFocusedNode();
1039    return node ? CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(
1040                      node->GetNativeAccessibleObject(NULL))
1041                : nil;
1042  }
1043  return nil;
1044}
1045
1046// Utility methods.
1047- (void)resetDragDrop {
1048  current_drag_op_ = NSDragOperationNone;
1049  current_allowed_ops_ = NSDragOperationNone;
1050  current_drag_data_ = NULL;
1051  if (fileUTI_) {
1052#if !__has_feature(objc_arc)
1053    [fileUTI_ release];
1054#endif  // !__has_feature(objc_arc)
1055    fileUTI_ = nil;
1056  }
1057  if (pasteboard_) {
1058#if !__has_feature(objc_arc)
1059    [pasteboard_ release];
1060#endif  // !__has_feature(objc_arc)
1061    pasteboard_ = nil;
1062  }
1063}
1064
1065- (void)fillPasteboard {
1066  DCHECK(!pasteboard_);
1067  pasteboard_ = [NSPasteboard pasteboardWithName:NSDragPboard];
1068#if !__has_feature(objc_arc)
1069  [pasteboard_ retain];
1070#endif  // !__has_feature(objc_arc)
1071
1072  [pasteboard_ declareTypes:@[ kCEFDragDummyPboardType ] owner:self];
1073
1074  // URL (and title).
1075  if (current_drag_data_->IsLink()) {
1076    [pasteboard_ addTypes:@[ NSURLPboardType, kNSURLTitlePboardType ]
1077                    owner:self];
1078  }
1079
1080  // MIME type.
1081  CefString mimeType;
1082  size_t contents_size = current_drag_data_->GetFileContents(NULL);
1083  CefString download_metadata = current_drag_data_->GetLinkMetadata();
1084  CefString file_name = current_drag_data_->GetFileName();
1085
1086  // File.
1087  if (contents_size > 0) {
1088    std::string file_name = current_drag_data_->GetFileName().ToString();
1089    size_t sep = file_name.find_last_of(".");
1090    CefString extension = file_name.substr(sep + 1);
1091
1092    mimeType = CefGetMimeType(extension);
1093
1094    if (!mimeType.empty()) {
1095      CFStringRef mimeTypeCF;
1096      mimeTypeCF = CFStringCreateWithCString(kCFAllocatorDefault,
1097                                             mimeType.ToString().c_str(),
1098                                             kCFStringEncodingUTF8);
1099      fileUTI_ = (__bridge NSString*)UTTypeCreatePreferredIdentifierForTag(
1100          kUTTagClassMIMEType, mimeTypeCF, NULL);
1101      CFRelease(mimeTypeCF);
1102      // File (HFS) promise.
1103      NSArray* fileUTIList = @[ fileUTI_ ];
1104      [pasteboard_ addTypes:@[ NSFilesPromisePboardType ] owner:self];
1105      [pasteboard_ setPropertyList:fileUTIList
1106                           forType:NSFilesPromisePboardType];
1107
1108      [pasteboard_ addTypes:fileUTIList owner:self];
1109    }
1110  }
1111
1112  // Plain text.
1113  if (!current_drag_data_->GetFragmentText().empty()) {
1114    [pasteboard_ addTypes:@[ NSStringPboardType ] owner:self];
1115  }
1116}
1117
1118- (void)populateDropData:(CefRefPtr<CefDragData>)data
1119          fromPasteboard:(NSPasteboard*)pboard {
1120  DCHECK(data);
1121  DCHECK(pboard);
1122  DCHECK(data && !data->IsReadOnly());
1123  NSArray* types = [pboard types];
1124
1125  // Get plain text.
1126  if ([types containsObject:NSStringPboardType]) {
1127    data->SetFragmentText(
1128        [[pboard stringForType:NSStringPboardType] UTF8String]);
1129  }
1130
1131  // Get files.
1132  if ([types containsObject:NSFilenamesPboardType]) {
1133    NSArray* files = [pboard propertyListForType:NSFilenamesPboardType];
1134    if ([files isKindOfClass:[NSArray class]] && [files count]) {
1135      for (NSUInteger i = 0; i < [files count]; i++) {
1136        NSString* filename = [files objectAtIndex:i];
1137        BOOL exists =
1138            [[NSFileManager defaultManager] fileExistsAtPath:filename];
1139        if (exists) {
1140          data->AddFile([filename UTF8String], CefString());
1141        }
1142      }
1143    }
1144  }
1145}
1146
1147- (NSPoint)flipWindowPointToView:(const NSPoint&)windowPoint {
1148  NSPoint viewPoint = [self convertPoint:windowPoint fromView:nil];
1149  NSRect viewFrame = [self frame];
1150  viewPoint.y = viewFrame.size.height - viewPoint.y;
1151  return viewPoint;
1152}
1153
1154- (void)resetDeviceScaleFactor {
1155  float device_scale_factor = 1.0f;
1156  NSWindow* window = [self window];
1157  if (window)
1158    device_scale_factor = [window backingScaleFactor];
1159  [self setDeviceScaleFactor:device_scale_factor];
1160}
1161
1162- (void)setDeviceScaleFactor:(float)device_scale_factor {
1163  if (device_scale_factor == device_scale_factor_)
1164    return;
1165
1166  // Apply some sanity checks.
1167  if (device_scale_factor < 1.0f || device_scale_factor > 4.0f)
1168    return;
1169
1170  device_scale_factor_ = device_scale_factor;
1171
1172  CefRefPtr<CefBrowser> browser = [self getBrowser];
1173  if (browser) {
1174    browser->GetHost()->NotifyScreenInfoChanged();
1175    browser->GetHost()->WasResized();
1176  }
1177}
1178
1179- (float)getDeviceScaleFactor {
1180  return device_scale_factor_;
1181}
1182
1183- (void)viewDidChangeBackingProperties {
1184  const CGFloat device_scale_factor = [self getDeviceScaleFactor];
1185
1186  if (device_scale_factor == device_scale_factor_)
1187    return;
1188
1189  CefRefPtr<CefBrowser> browser = [self getBrowser];
1190  if (browser) {
1191    browser->GetHost()->NotifyScreenInfoChanged();
1192    browser->GetHost()->WasResized();
1193  }
1194}
1195
1196- (bool)isOverPopupWidgetX:(int)x andY:(int)y {
1197  CefRect rc = renderer_->popup_rect();
1198  int popup_right = rc.x + rc.width;
1199  int popup_bottom = rc.y + rc.height;
1200  return (x >= rc.x) && (x < popup_right) && (y >= rc.y) && (y < popup_bottom);
1201}
1202
1203- (int)getPopupXOffset {
1204  return renderer_->original_popup_rect().x - renderer_->popup_rect().x;
1205}
1206
1207- (int)getPopupYOffset {
1208  return renderer_->original_popup_rect().y - renderer_->popup_rect().y;
1209}
1210
1211- (void)applyPopupOffsetToX:(int&)x andY:(int&)y {
1212  if ([self isOverPopupWidgetX:x andY:y]) {
1213    x += [self getPopupXOffset];
1214    y += [self getPopupYOffset];
1215  }
1216}
1217
1218// Convert from scaled coordinates to view coordinates.
1219- (NSPoint)convertPointFromBackingInternal:(NSPoint)aPoint {
1220  return [self convertPointFromBacking:aPoint];
1221}
1222
1223// Convert from view coordinates to scaled coordinates.
1224- (NSPoint)convertPointToBackingInternal:(NSPoint)aPoint {
1225  return [self convertPointToBacking:aPoint];
1226}
1227
1228// Convert from scaled coordinates to view coordinates.
1229- (NSRect)convertRectFromBackingInternal:(NSRect)aRect {
1230  return [self convertRectFromBacking:aRect];
1231}
1232
1233// Convert from view coordinates to scaled coordinates.
1234- (NSRect)convertRectToBackingInternal:(NSRect)aRect {
1235  return [self convertRectToBacking:aRect];
1236}
1237
1238- (void)ChangeCompositionRange:(CefRange)range
1239              character_bounds:(const CefRenderHandler::RectList&)bounds {
1240  if (text_input_client_)
1241    [text_input_client_ ChangeCompositionRange:range character_bounds:bounds];
1242}
1243
1244- (void)UpdateAccessibilityTree:(CefRefPtr<CefValue>)value {
1245  if (!accessibility_helper_) {
1246    accessibility_helper_ =
1247        new client::OsrAccessibilityHelper(value, [self getBrowser]);
1248  } else {
1249    accessibility_helper_->UpdateAccessibilityTree(value);
1250  }
1251
1252  if (accessibility_helper_) {
1253    NSAccessibilityPostNotification(self,
1254                                    NSAccessibilityValueChangedNotification);
1255  }
1256  return;
1257}
1258
1259- (void)UpdateAccessibilityLocation:(CefRefPtr<CefValue>)value {
1260  if (accessibility_helper_) {
1261    accessibility_helper_->UpdateAccessibilityLocation(value);
1262  }
1263
1264  if (accessibility_helper_) {
1265    NSAccessibilityPostNotification(self,
1266                                    NSAccessibilityValueChangedNotification);
1267  }
1268  return;
1269}
1270@end
1271
1272namespace client {
1273
1274class BrowserWindowOsrMacImpl {
1275 public:
1276  BrowserWindowOsrMacImpl(BrowserWindow::Delegate* delegate,
1277                          const std::string& startup_url,
1278                          const OsrRendererSettings& settings,
1279                          BrowserWindowOsrMac& browser_window);
1280  ~BrowserWindowOsrMacImpl();
1281
1282  // BrowserWindow methods.
1283  void CreateBrowser(ClientWindowHandle parent_handle,
1284                     const CefRect& rect,
1285                     const CefBrowserSettings& settings,
1286                     CefRefPtr<CefDictionaryValue> extra_info,
1287                     CefRefPtr<CefRequestContext> request_context);
1288  void GetPopupConfig(CefWindowHandle temp_handle,
1289                      CefWindowInfo& windowInfo,
1290                      CefRefPtr<CefClient>& client,
1291                      CefBrowserSettings& settings);
1292  void ShowPopup(ClientWindowHandle parent_handle,
1293                 int x,
1294                 int y,
1295                 size_t width,
1296                 size_t height);
1297  void Show();
1298  void Hide();
1299  void SetBounds(int x, int y, size_t width, size_t height);
1300  void SetFocus(bool focus);
1301  void SetDeviceScaleFactor(float device_scale_factor);
1302  float GetDeviceScaleFactor() const;
1303  ClientWindowHandle GetWindowHandle() const;
1304
1305  // ClientHandlerOsr::OsrDelegate methods.
1306  void OnAfterCreated(CefRefPtr<CefBrowser> browser);
1307  void OnBeforeClose(CefRefPtr<CefBrowser> browser);
1308  bool GetRootScreenRect(CefRefPtr<CefBrowser> browser, CefRect& rect);
1309  void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect);
1310  bool GetScreenPoint(CefRefPtr<CefBrowser> browser,
1311                      int viewX,
1312                      int viewY,
1313                      int& screenX,
1314                      int& screenY);
1315  bool GetScreenInfo(CefRefPtr<CefBrowser> browser, CefScreenInfo& screen_info);
1316  void OnPopupShow(CefRefPtr<CefBrowser> browser, bool show);
1317  void OnPopupSize(CefRefPtr<CefBrowser> browser, const CefRect& rect);
1318  void OnPaint(CefRefPtr<CefBrowser> browser,
1319               CefRenderHandler::PaintElementType type,
1320               const CefRenderHandler::RectList& dirtyRects,
1321               const void* buffer,
1322               int width,
1323               int height);
1324  void OnCursorChange(CefRefPtr<CefBrowser> browser,
1325                      CefCursorHandle cursor,
1326                      cef_cursor_type_t type,
1327                      const CefCursorInfo& custom_cursor_info);
1328  bool StartDragging(CefRefPtr<CefBrowser> browser,
1329                     CefRefPtr<CefDragData> drag_data,
1330                     CefRenderHandler::DragOperationsMask allowed_ops,
1331                     int x,
1332                     int y);
1333  void UpdateDragCursor(CefRefPtr<CefBrowser> browser,
1334                        CefRenderHandler::DragOperation operation);
1335  void OnImeCompositionRangeChanged(
1336      CefRefPtr<CefBrowser> browser,
1337      const CefRange& selection_range,
1338      const CefRenderHandler::RectList& character_bounds);
1339
1340  void UpdateAccessibilityTree(CefRefPtr<CefValue> value);
1341  void UpdateAccessibilityLocation(CefRefPtr<CefValue> value);
1342
1343 private:
1344  // Create the NSView.
1345  void Create(ClientWindowHandle parent_handle, const CefRect& rect);
1346
1347  BrowserWindowOsrMac& browser_window_;
1348  // The below members will only be accessed on the main thread which should be
1349  // the same as the CEF UI thread.
1350  OsrRenderer renderer_;
1351  BrowserOpenGLView* native_browser_view_;
1352  bool hidden_;
1353  bool painting_popup_;
1354};
1355
1356BrowserWindowOsrMacImpl::BrowserWindowOsrMacImpl(
1357    BrowserWindow::Delegate* delegate,
1358    const std::string& startup_url,
1359    const OsrRendererSettings& settings,
1360    BrowserWindowOsrMac& browser_window)
1361    : browser_window_(browser_window),
1362      renderer_(settings),
1363      native_browser_view_(nil),
1364      hidden_(false),
1365      painting_popup_(false) {}
1366
1367BrowserWindowOsrMacImpl::~BrowserWindowOsrMacImpl() {
1368  if (native_browser_view_) {
1369    // Disassociate the view with |this|.
1370    [native_browser_view_ detach];
1371  }
1372}
1373
1374void BrowserWindowOsrMacImpl::CreateBrowser(
1375    ClientWindowHandle parent_handle,
1376    const CefRect& rect,
1377    const CefBrowserSettings& settings,
1378    CefRefPtr<CefDictionaryValue> extra_info,
1379    CefRefPtr<CefRequestContext> request_context) {
1380  REQUIRE_MAIN_THREAD();
1381
1382  // Create the native NSView.
1383  Create(parent_handle, rect);
1384
1385  CefWindowInfo window_info;
1386  window_info.SetAsWindowless(
1387      CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(native_browser_view_));
1388
1389  // Create the browser asynchronously.
1390  CefBrowserHost::CreateBrowser(window_info, browser_window_.client_handler_,
1391                                browser_window_.client_handler_->startup_url(),
1392                                settings, extra_info, request_context);
1393}
1394
1395void BrowserWindowOsrMacImpl::GetPopupConfig(CefWindowHandle temp_handle,
1396                                             CefWindowInfo& windowInfo,
1397                                             CefRefPtr<CefClient>& client,
1398                                             CefBrowserSettings& settings) {
1399  CEF_REQUIRE_UI_THREAD();
1400
1401  windowInfo.SetAsWindowless(temp_handle);
1402  client = browser_window_.client_handler_;
1403}
1404
1405void BrowserWindowOsrMacImpl::ShowPopup(ClientWindowHandle parent_handle,
1406                                        int x,
1407                                        int y,
1408                                        size_t width,
1409                                        size_t height) {
1410  REQUIRE_MAIN_THREAD();
1411  DCHECK(browser_window_.browser_.get());
1412
1413  // Create the native NSView.
1414  Create(parent_handle,
1415         CefRect(x, y, static_cast<int>(width), static_cast<int>(height)));
1416
1417  // Send resize notification so the compositor is assigned the correct
1418  // viewport size and begins rendering.
1419  browser_window_.browser_->GetHost()->WasResized();
1420
1421  Show();
1422}
1423
1424void BrowserWindowOsrMacImpl::Show() {
1425  REQUIRE_MAIN_THREAD();
1426
1427  if (hidden_) {
1428    // Set the browser as visible.
1429    browser_window_.browser_->GetHost()->WasHidden(false);
1430    hidden_ = false;
1431  }
1432
1433  // Give focus to the browser.
1434  browser_window_.browser_->GetHost()->SendFocusEvent(true);
1435}
1436
1437void BrowserWindowOsrMacImpl::Hide() {
1438  REQUIRE_MAIN_THREAD();
1439
1440  if (!browser_window_.browser_.get())
1441    return;
1442
1443  // Remove focus from the browser.
1444  browser_window_.browser_->GetHost()->SendFocusEvent(false);
1445
1446  if (!hidden_) {
1447    // Set the browser as hidden.
1448    browser_window_.browser_->GetHost()->WasHidden(true);
1449    hidden_ = true;
1450  }
1451}
1452
1453void BrowserWindowOsrMacImpl::SetBounds(int x,
1454                                        int y,
1455                                        size_t width,
1456                                        size_t height) {
1457  REQUIRE_MAIN_THREAD();
1458  // Nothing to do here. GTK will take care of positioning in the container.
1459}
1460
1461void BrowserWindowOsrMacImpl::SetFocus(bool focus) {
1462  REQUIRE_MAIN_THREAD();
1463  if (native_browser_view_)
1464    [native_browser_view_.window makeFirstResponder:native_browser_view_];
1465}
1466
1467void BrowserWindowOsrMacImpl::SetDeviceScaleFactor(float device_scale_factor) {
1468  REQUIRE_MAIN_THREAD();
1469  if (native_browser_view_)
1470    [native_browser_view_ setDeviceScaleFactor:device_scale_factor];
1471}
1472
1473float BrowserWindowOsrMacImpl::GetDeviceScaleFactor() const {
1474  REQUIRE_MAIN_THREAD();
1475  if (native_browser_view_)
1476    return [native_browser_view_ getDeviceScaleFactor];
1477  return 1.0f;
1478}
1479
1480ClientWindowHandle BrowserWindowOsrMacImpl::GetWindowHandle() const {
1481  REQUIRE_MAIN_THREAD();
1482  return CAST_NSVIEW_TO_CEF_WINDOW_HANDLE(native_browser_view_);
1483}
1484
1485void BrowserWindowOsrMacImpl::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
1486  CEF_REQUIRE_UI_THREAD();
1487}
1488
1489void BrowserWindowOsrMacImpl::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
1490  CEF_REQUIRE_UI_THREAD();
1491  REQUIRE_MAIN_THREAD();
1492
1493  // Detach |this| from the ClientHandlerOsr.
1494  static_cast<ClientHandlerOsr*>(browser_window_.client_handler_.get())
1495      ->DetachOsrDelegate();
1496}
1497
1498bool BrowserWindowOsrMacImpl::GetRootScreenRect(CefRefPtr<CefBrowser> browser,
1499                                                CefRect& rect) {
1500  CEF_REQUIRE_UI_THREAD();
1501  return false;
1502}
1503
1504void BrowserWindowOsrMacImpl::GetViewRect(CefRefPtr<CefBrowser> browser,
1505                                          CefRect& rect) {
1506  CEF_REQUIRE_UI_THREAD();
1507  REQUIRE_MAIN_THREAD();
1508
1509  rect.x = rect.y = 0;
1510
1511  if (!native_browser_view_) {
1512    // Never return an empty rectangle.
1513    rect.width = rect.height = 1;
1514    return;
1515  }
1516
1517  const float device_scale_factor = [native_browser_view_ getDeviceScaleFactor];
1518
1519  // |bounds| is in OS X view coordinates.
1520  NSRect bounds = native_browser_view_.bounds;
1521
1522  // Convert to device coordinates.
1523  bounds = [native_browser_view_ convertRectToBackingInternal:bounds];
1524
1525  // Convert to browser view coordinates.
1526  rect.width = DeviceToLogical(bounds.size.width, device_scale_factor);
1527  if (rect.width == 0)
1528    rect.width = 1;
1529  rect.height = DeviceToLogical(bounds.size.height, device_scale_factor);
1530  if (rect.height == 0)
1531    rect.height = 1;
1532}
1533
1534bool BrowserWindowOsrMacImpl::GetScreenPoint(CefRefPtr<CefBrowser> browser,
1535                                             int viewX,
1536                                             int viewY,
1537                                             int& screenX,
1538                                             int& screenY) {
1539  CEF_REQUIRE_UI_THREAD();
1540  REQUIRE_MAIN_THREAD();
1541
1542  if (!native_browser_view_)
1543    return false;
1544
1545  const float device_scale_factor = [native_browser_view_ getDeviceScaleFactor];
1546
1547  // (viewX, viewX) is in browser view coordinates.
1548  // Convert to device coordinates.
1549  NSPoint view_pt = NSMakePoint(LogicalToDevice(viewX, device_scale_factor),
1550                                LogicalToDevice(viewY, device_scale_factor));
1551
1552  // Convert to OS X view coordinates.
1553  view_pt = [native_browser_view_ convertPointFromBackingInternal:view_pt];
1554
1555  // Reverse the Y component.
1556  const NSRect bounds = native_browser_view_.bounds;
1557  view_pt.y = bounds.size.height - view_pt.y;
1558
1559  // Convert to screen coordinates.
1560  NSPoint window_pt = [native_browser_view_ convertPoint:view_pt toView:nil];
1561  NSPoint screen_pt =
1562      ConvertPointFromWindowToScreen(native_browser_view_.window, window_pt);
1563
1564  screenX = screen_pt.x;
1565  screenY = screen_pt.y;
1566  return true;
1567}
1568
1569bool BrowserWindowOsrMacImpl::GetScreenInfo(CefRefPtr<CefBrowser> browser,
1570                                            CefScreenInfo& screen_info) {
1571  CEF_REQUIRE_UI_THREAD();
1572  REQUIRE_MAIN_THREAD();
1573
1574  if (!native_browser_view_)
1575    return false;
1576
1577  CefRect view_rect;
1578  GetViewRect(browser, view_rect);
1579
1580  screen_info.device_scale_factor = [native_browser_view_ getDeviceScaleFactor];
1581
1582  // The screen info rectangles are used by the renderer to create and position
1583  // popups. Keep popups inside the view rectangle.
1584  screen_info.rect = view_rect;
1585  screen_info.available_rect = view_rect;
1586
1587  return true;
1588}
1589
1590void BrowserWindowOsrMacImpl::OnPopupShow(CefRefPtr<CefBrowser> browser,
1591                                          bool show) {
1592  CEF_REQUIRE_UI_THREAD();
1593  REQUIRE_MAIN_THREAD();
1594
1595  if (!native_browser_view_)
1596    return;
1597
1598  if (!show) {
1599    renderer_.ClearPopupRects();
1600    browser->GetHost()->Invalidate(PET_VIEW);
1601  }
1602  renderer_.OnPopupShow(browser, show);
1603}
1604
1605void BrowserWindowOsrMacImpl::OnPopupSize(CefRefPtr<CefBrowser> browser,
1606                                          const CefRect& rect) {
1607  CEF_REQUIRE_UI_THREAD();
1608  REQUIRE_MAIN_THREAD();
1609
1610  if (!native_browser_view_)
1611    return;
1612
1613  const float device_scale_factor = [native_browser_view_ getDeviceScaleFactor];
1614
1615  // |rect| is in browser view coordinates. Convert to device coordinates.
1616  CefRect device_rect = LogicalToDevice(rect, device_scale_factor);
1617
1618  renderer_.OnPopupSize(browser, device_rect);
1619}
1620
1621void BrowserWindowOsrMacImpl::OnPaint(
1622    CefRefPtr<CefBrowser> browser,
1623    CefRenderHandler::PaintElementType type,
1624    const CefRenderHandler::RectList& dirtyRects,
1625    const void* buffer,
1626    int width,
1627    int height) {
1628  CEF_REQUIRE_UI_THREAD();
1629  REQUIRE_MAIN_THREAD();
1630
1631  if (!native_browser_view_)
1632    return;
1633
1634  if (width <= 2 && height <= 2) {
1635    // Ignore really small buffer sizes while the widget is starting up.
1636    return;
1637  }
1638
1639  if (painting_popup_) {
1640    renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height);
1641    return;
1642  }
1643
1644  ScopedGLContext scoped_gl_context(native_browser_view_, true);
1645
1646  renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height);
1647  if (type == PET_VIEW && !renderer_.popup_rect().IsEmpty()) {
1648    painting_popup_ = true;
1649    browser->GetHost()->Invalidate(PET_POPUP);
1650    painting_popup_ = false;
1651  }
1652  renderer_.Render();
1653}
1654
1655void BrowserWindowOsrMacImpl::OnCursorChange(
1656    CefRefPtr<CefBrowser> browser,
1657    CefCursorHandle cursor,
1658    cef_cursor_type_t type,
1659    const CefCursorInfo& custom_cursor_info) {
1660  CEF_REQUIRE_UI_THREAD();
1661  REQUIRE_MAIN_THREAD();
1662
1663  [CAST_CEF_CURSOR_HANDLE_TO_NSCURSOR(cursor) set];
1664}
1665
1666bool BrowserWindowOsrMacImpl::StartDragging(
1667    CefRefPtr<CefBrowser> browser,
1668    CefRefPtr<CefDragData> drag_data,
1669    CefRenderHandler::DragOperationsMask allowed_ops,
1670    int x,
1671    int y) {
1672  CEF_REQUIRE_UI_THREAD();
1673  REQUIRE_MAIN_THREAD();
1674
1675  if (!native_browser_view_)
1676    return false;
1677
1678  static float device_scale_factor =
1679      [native_browser_view_ getDeviceScaleFactor];
1680
1681  // |point| is in browser view coordinates.
1682  NSPoint point = NSMakePoint(x, y);
1683
1684  // Convert to device coordinates.
1685  point.x = LogicalToDevice(point.x, device_scale_factor);
1686  point.y = LogicalToDevice(point.y, device_scale_factor);
1687
1688  // Convert to OS X view coordinates.
1689  point = [native_browser_view_ convertPointFromBackingInternal:point];
1690
1691  return [native_browser_view_
1692      startDragging:drag_data
1693         allowedOps:static_cast<NSDragOperation>(allowed_ops)
1694              point:point];
1695}
1696
1697void BrowserWindowOsrMacImpl::UpdateDragCursor(
1698    CefRefPtr<CefBrowser> browser,
1699    CefRenderHandler::DragOperation operation) {
1700  CEF_REQUIRE_UI_THREAD();
1701  REQUIRE_MAIN_THREAD();
1702
1703  if (native_browser_view_)
1704    [native_browser_view_ setCurrentDragOp:operation];
1705}
1706
1707void BrowserWindowOsrMacImpl::OnImeCompositionRangeChanged(
1708    CefRefPtr<CefBrowser> browser,
1709    const CefRange& selection_range,
1710    const CefRenderHandler::RectList& bounds) {
1711  CEF_REQUIRE_UI_THREAD();
1712
1713  if (native_browser_view_) {
1714    [native_browser_view_ ChangeCompositionRange:selection_range
1715                                character_bounds:bounds];
1716  }
1717}
1718
1719void BrowserWindowOsrMacImpl::UpdateAccessibilityTree(
1720    CefRefPtr<CefValue> value) {
1721  CEF_REQUIRE_UI_THREAD();
1722
1723  if (native_browser_view_) {
1724    [native_browser_view_ UpdateAccessibilityTree:value];
1725  }
1726}
1727
1728void BrowserWindowOsrMacImpl::UpdateAccessibilityLocation(
1729    CefRefPtr<CefValue> value) {
1730  CEF_REQUIRE_UI_THREAD();
1731
1732  if (native_browser_view_) {
1733    [native_browser_view_ UpdateAccessibilityLocation:value];
1734  }
1735}
1736
1737void BrowserWindowOsrMacImpl::Create(ClientWindowHandle parent_handle,
1738                                     const CefRect& rect) {
1739  REQUIRE_MAIN_THREAD();
1740  DCHECK(!native_browser_view_);
1741
1742  NSRect window_rect = NSMakeRect(rect.x, rect.y, rect.width, rect.height);
1743  native_browser_view_ =
1744      [[BrowserOpenGLView alloc] initWithFrame:window_rect
1745                              andBrowserWindow:&browser_window_
1746                                   andRenderer:&renderer_];
1747  native_browser_view_.autoresizingMask =
1748      (NSViewWidthSizable | NSViewHeightSizable);
1749  native_browser_view_.autoresizesSubviews = YES;
1750  [CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(parent_handle)
1751      addSubview:native_browser_view_];
1752
1753  // Determine the default scale factor.
1754  [native_browser_view_ resetDeviceScaleFactor];
1755
1756  [[NSNotificationCenter defaultCenter]
1757      addObserver:native_browser_view_
1758         selector:@selector(windowDidChangeBackingProperties:)
1759             name:NSWindowDidChangeBackingPropertiesNotification
1760           object:native_browser_view_.window];
1761}
1762
1763BrowserWindowOsrMac::BrowserWindowOsrMac(BrowserWindow::Delegate* delegate,
1764                                         const std::string& startup_url,
1765                                         const OsrRendererSettings& settings)
1766    : BrowserWindow(delegate) {
1767  client_handler_ = new ClientHandlerOsr(this, this, startup_url);
1768  impl_.reset(
1769      new BrowserWindowOsrMacImpl(delegate, startup_url, settings, *this));
1770}
1771
1772BrowserWindowOsrMac::~BrowserWindowOsrMac() {}
1773
1774void BrowserWindowOsrMac::CreateBrowser(
1775    ClientWindowHandle parent_handle,
1776    const CefRect& rect,
1777    const CefBrowserSettings& settings,
1778    CefRefPtr<CefDictionaryValue> extra_info,
1779    CefRefPtr<CefRequestContext> request_context) {
1780  impl_->CreateBrowser(parent_handle, rect, settings, extra_info,
1781                       request_context);
1782}
1783
1784void BrowserWindowOsrMac::GetPopupConfig(CefWindowHandle temp_handle,
1785                                         CefWindowInfo& windowInfo,
1786                                         CefRefPtr<CefClient>& client,
1787                                         CefBrowserSettings& settings) {
1788  impl_->GetPopupConfig(temp_handle, windowInfo, client, settings);
1789}
1790
1791void BrowserWindowOsrMac::ShowPopup(ClientWindowHandle parent_handle,
1792                                    int x,
1793                                    int y,
1794                                    size_t width,
1795                                    size_t height) {
1796  impl_->ShowPopup(parent_handle, x, y, width, height);
1797}
1798
1799void BrowserWindowOsrMac::Show() {
1800  impl_->Show();
1801}
1802
1803void BrowserWindowOsrMac::Hide() {
1804  impl_->Hide();
1805}
1806
1807void BrowserWindowOsrMac::SetBounds(int x, int y, size_t width, size_t height) {
1808  impl_->SetBounds(x, y, width, height);
1809}
1810
1811void BrowserWindowOsrMac::SetFocus(bool focus) {
1812  impl_->SetFocus(focus);
1813}
1814
1815void BrowserWindowOsrMac::SetDeviceScaleFactor(float device_scale_factor) {
1816  impl_->SetDeviceScaleFactor(device_scale_factor);
1817}
1818
1819float BrowserWindowOsrMac::GetDeviceScaleFactor() const {
1820  return impl_->GetDeviceScaleFactor();
1821}
1822
1823ClientWindowHandle BrowserWindowOsrMac::GetWindowHandle() const {
1824  return impl_->GetWindowHandle();
1825}
1826
1827void BrowserWindowOsrMac::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
1828  impl_->OnAfterCreated(browser);
1829}
1830
1831void BrowserWindowOsrMac::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
1832  impl_->OnBeforeClose(browser);
1833}
1834
1835bool BrowserWindowOsrMac::GetRootScreenRect(CefRefPtr<CefBrowser> browser,
1836                                            CefRect& rect) {
1837  return impl_->GetRootScreenRect(browser, rect);
1838}
1839
1840void BrowserWindowOsrMac::GetViewRect(CefRefPtr<CefBrowser> browser,
1841                                      CefRect& rect) {
1842  impl_->GetViewRect(browser, rect);
1843}
1844
1845bool BrowserWindowOsrMac::GetScreenPoint(CefRefPtr<CefBrowser> browser,
1846                                         int viewX,
1847                                         int viewY,
1848                                         int& screenX,
1849                                         int& screenY) {
1850  return impl_->GetScreenPoint(browser, viewX, viewY, screenX, screenY);
1851}
1852
1853bool BrowserWindowOsrMac::GetScreenInfo(CefRefPtr<CefBrowser> browser,
1854                                        CefScreenInfo& screen_info) {
1855  return impl_->GetScreenInfo(browser, screen_info);
1856}
1857
1858void BrowserWindowOsrMac::OnPopupShow(CefRefPtr<CefBrowser> browser,
1859                                      bool show) {
1860  impl_->OnPopupShow(browser, show);
1861}
1862
1863void BrowserWindowOsrMac::OnPopupSize(CefRefPtr<CefBrowser> browser,
1864                                      const CefRect& rect) {
1865  impl_->OnPopupSize(browser, rect);
1866}
1867
1868void BrowserWindowOsrMac::OnPaint(CefRefPtr<CefBrowser> browser,
1869                                  CefRenderHandler::PaintElementType type,
1870                                  const CefRenderHandler::RectList& dirtyRects,
1871                                  const void* buffer,
1872                                  int width,
1873                                  int height) {
1874  impl_->OnPaint(browser, type, dirtyRects, buffer, width, height);
1875}
1876
1877void BrowserWindowOsrMac::OnCursorChange(
1878    CefRefPtr<CefBrowser> browser,
1879    CefCursorHandle cursor,
1880    cef_cursor_type_t type,
1881    const CefCursorInfo& custom_cursor_info) {
1882  impl_->OnCursorChange(browser, cursor, type, custom_cursor_info);
1883}
1884
1885bool BrowserWindowOsrMac::StartDragging(
1886    CefRefPtr<CefBrowser> browser,
1887    CefRefPtr<CefDragData> drag_data,
1888    CefRenderHandler::DragOperationsMask allowed_ops,
1889    int x,
1890    int y) {
1891  return impl_->StartDragging(browser, drag_data, allowed_ops, x, y);
1892}
1893
1894void BrowserWindowOsrMac::UpdateDragCursor(
1895    CefRefPtr<CefBrowser> browser,
1896    CefRenderHandler::DragOperation operation) {
1897  impl_->UpdateDragCursor(browser, operation);
1898}
1899
1900void BrowserWindowOsrMac::OnImeCompositionRangeChanged(
1901    CefRefPtr<CefBrowser> browser,
1902    const CefRange& selection_range,
1903    const CefRenderHandler::RectList& character_bounds) {
1904  impl_->OnImeCompositionRangeChanged(browser, selection_range,
1905                                      character_bounds);
1906}
1907
1908void BrowserWindowOsrMac::UpdateAccessibilityTree(CefRefPtr<CefValue> value) {
1909  impl_->UpdateAccessibilityTree(value);
1910}
1911
1912void BrowserWindowOsrMac::UpdateAccessibilityLocation(
1913    CefRefPtr<CefValue> value) {
1914  impl_->UpdateAccessibilityLocation(value);
1915}
1916
1917}  // namespace client
1918