2011-02-01 Anders Carlsson <andersca@apple.com>
[WebKit-https.git] / Source / WebKit2 / UIProcess / API / mac / WKView.mm
1 /*
2  * Copyright (C) 2010 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "WKView.h"
28
29 #import "ChunkedUpdateDrawingAreaProxy.h"
30 #import "DataReference.h"
31 #import "DrawingAreaProxyImpl.h"
32 #import "FindIndicator.h"
33 #import "FindIndicatorWindow.h"
34 #import "LayerBackedDrawingAreaProxy.h"
35 #import "LayerTreeContext.h"
36 #import "Logging.h"
37 #import "NativeWebKeyboardEvent.h"
38 #import "PDFViewController.h"
39 #import "PageClientImpl.h"
40 #import "PasteboardTypes.h"
41 #import "Region.h"
42 #import "RunLoop.h"
43 #import "TextChecker.h"
44 #import "TextCheckerState.h"
45 #import "WKAPICast.h"
46 #import "WKPrintingView.h"
47 #import "WKStringCF.h"
48 #import "WKTextInputWindowController.h"
49 #import "WKViewInternal.h"
50 #import "WKViewPrivate.h"
51 #import "WebContext.h"
52 #import "WebEventFactory.h"
53 #import "WebPage.h"
54 #import "WebPageProxy.h"
55 #import "WebProcessManager.h"
56 #import "WebProcessProxy.h"
57 #import "WebSystemInterface.h"
58 #import <QuartzCore/QuartzCore.h>
59 #import <WebCore/ColorMac.h>
60 #import <WebCore/DragController.h>
61 #import <WebCore/DragData.h>
62 #import <WebCore/FloatRect.h>
63 #import <WebCore/IntRect.h>
64 #import <WebCore/KeyboardEvent.h>
65 #import <WebCore/PlatformMouseEvent.h>
66 #import <WebCore/PlatformScreen.h>
67 #import <WebKitSystemInterface.h>
68 #import <wtf/RefPtr.h>
69 #import <wtf/RetainPtr.h>
70
71 // FIXME (WebKit2) <rdar://problem/8728860> WebKit2 needs to be localized
72 #define UI_STRING(__str, __desc) [NSString stringWithUTF8String:__str]
73
74 @interface NSApplication (Details)
75 - (void)speakString:(NSString *)string;
76 @end
77
78 @interface NSWindow (Details)
79 - (NSRect)_growBoxRect;
80 - (BOOL)_updateGrowBoxForWindowFrameChange;
81 @end
82
83 extern "C" {
84     // Need to declare this attribute name because AppKit exports it but does not make it available in API or SPI headers.
85     // <rdar://problem/8631468> tracks the request to make it available. This code should be removed when the bug is closed.
86     extern NSString *NSTextInputReplacementRangeAttributeName;
87 }
88
89 using namespace WebKit;
90 using namespace WebCore;
91
92 namespace WebKit {
93
94 typedef id <NSValidatedUserInterfaceItem> ValidationItem;
95 typedef Vector<RetainPtr<ValidationItem> > ValidationVector;
96 typedef HashMap<String, ValidationVector> ValidationMap;
97
98 }
99
100 @interface WKViewData : NSObject {
101 @public
102     OwnPtr<PageClientImpl> _pageClient;
103     RefPtr<WebPageProxy> _page;
104
105     // For ToolTips.
106     NSToolTipTag _lastToolTipTag;
107     id _trackingRectOwner;
108     void* _trackingRectUserData;
109
110     RetainPtr<NSView> _layerHostingView;
111
112     // FIXME: Remove _oldLayerHostingView.
113 #if USE(ACCELERATED_COMPOSITING)
114     NSView *_oldLayerHostingView;
115 #endif
116
117     RetainPtr<id> _remoteAccessibilityChild;
118     
119     // For asynchronous validation.
120     ValidationMap _validationMap;
121
122     OwnPtr<PDFViewController> _pdfViewController;
123
124     OwnPtr<FindIndicatorWindow> _findIndicatorWindow;
125     // We keep here the event when resending it to
126     // the application to distinguish the case of a new event from one 
127     // that has been already sent to WebCore.
128     NSEvent *_keyDownEventBeingResent;
129     Vector<KeypressCommand> _commandsList;
130
131     // The identifier of the plug-in we want to send complex text input to, or 0 if there is none.
132     uint64_t _pluginComplexTextInputIdentifier;
133
134     Vector<CompositionUnderline> _underlines;
135     unsigned _selectionStart;
136     unsigned _selectionEnd;
137
138     bool _inBecomeFirstResponder;
139     bool _inResignFirstResponder;
140     NSEvent *_mouseDownEvent;
141     BOOL _ignoringMouseDraggedEvents;
142     BOOL _dragHasStarted;
143
144 #if ENABLE(GESTURE_EVENTS)
145     id _endGestureMonitor;
146 #endif
147 }
148 @end
149
150 @implementation WKViewData
151 @end
152
153 @interface NSObject (NSTextInputContextDetails)
154 - (BOOL)wantsToHandleMouseEvents;
155 - (BOOL)handleMouseEvent:(NSEvent *)event;
156 @end
157
158 @implementation WKView
159
160 static bool useNewDrawingArea()
161 {
162     static bool useNewDrawingArea = getenv("USE_NEW_DRAWING_AREA");
163
164     return useNewDrawingArea;
165 }
166
167 - (id)initWithFrame:(NSRect)frame
168 {
169     return [self initWithFrame:frame contextRef:toAPI(WebContext::sharedProcessContext())];
170 }
171
172 - (id)initWithFrame:(NSRect)frame contextRef:(WKContextRef)contextRef
173 {   
174     return [self initWithFrame:frame contextRef:contextRef pageGroupRef:nil];
175 }
176
177 - (void)_registerDraggedTypes
178 {
179     NSMutableSet *types = [[NSMutableSet alloc] initWithArray:PasteboardTypes::forEditing()];
180     [types addObjectsFromArray:PasteboardTypes::forURL()];
181     [self registerForDraggedTypes:[types allObjects]];
182     [types release];
183 }
184
185 - (id)initWithFrame:(NSRect)frame contextRef:(WKContextRef)contextRef pageGroupRef:(WKPageGroupRef)pageGroupRef
186 {
187     self = [super initWithFrame:frame];
188     if (!self)
189         return nil;
190
191     InitWebCoreSystemInterface();
192     RunLoop::initializeMainRunLoop();
193
194     NSTrackingArea *trackingArea = [[NSTrackingArea alloc] initWithRect:frame
195                                                                 options:(NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect)
196                                                                   owner:self
197                                                                userInfo:nil];
198     [self addTrackingArea:trackingArea];
199     [trackingArea release];
200
201     _data = [[WKViewData alloc] init];
202
203     _data->_pageClient = PageClientImpl::create(self);
204     _data->_page = toImpl(contextRef)->createWebPage(_data->_pageClient.get(), toImpl(pageGroupRef));
205     _data->_page->initializeWebPage();
206     _data->_mouseDownEvent = nil;
207     _data->_ignoringMouseDraggedEvents = NO;
208
209     [self _registerDraggedTypes];
210
211     WebContext::statistics().wkViewCount++;
212
213 #if !defined(BUILDING_ON_SNOW_LEOPARD)
214     NSData *remoteToken = (NSData *)WKAXRemoteTokenForElement(self);
215     CoreIPC::DataReference dataToken = CoreIPC::DataReference(reinterpret_cast<const uint8_t*>([remoteToken bytes]), [remoteToken length]);
216     _data->_page->sendAccessibilityPresenterToken(dataToken);
217 #endif
218     
219     return self;
220 }
221
222 - (void)dealloc
223 {
224     _data->_page->close();
225
226     [_data release];
227
228     WebContext::statistics().wkViewCount--;
229
230     [super dealloc];
231 }
232
233 - (WKPageRef)pageRef
234 {
235     return toAPI(_data->_page.get());
236 }
237
238 - (void)setDrawsBackground:(BOOL)drawsBackground
239 {
240     _data->_page->setDrawsBackground(drawsBackground);
241 }
242
243 - (BOOL)drawsBackground
244 {
245     return _data->_page->drawsBackground();
246 }
247
248 - (void)setDrawsTransparentBackground:(BOOL)drawsTransparentBackground
249 {
250     _data->_page->setDrawsTransparentBackground(drawsTransparentBackground);
251 }
252
253 - (BOOL)drawsTransparentBackground
254 {
255     return _data->_page->drawsTransparentBackground();
256 }
257
258 - (BOOL)acceptsFirstResponder
259 {
260     return YES;
261 }
262
263 - (BOOL)becomeFirstResponder
264 {
265     NSSelectionDirection direction = [[self window] keyViewSelectionDirection];
266
267     _data->_inBecomeFirstResponder = true;
268     _data->_page->viewStateDidChange(WebPageProxy::ViewIsFocused);
269     _data->_inBecomeFirstResponder = false;
270
271     if (direction != NSDirectSelection)
272         _data->_page->setInitialFocus(direction == NSSelectingNext);
273
274     return YES;
275 }
276
277 - (BOOL)resignFirstResponder
278 {
279     _data->_inResignFirstResponder = true;
280     _data->_page->viewStateDidChange(WebPageProxy::ViewIsFocused);
281     _data->_inResignFirstResponder = false;
282
283     return YES;
284 }
285
286 - (void)viewWillStartLiveResize
287 {
288     _data->_page->viewWillStartLiveResize();
289 }
290
291 - (void)viewDidEndLiveResize
292 {
293     _data->_page->viewWillEndLiveResize();
294 }
295
296 - (BOOL)isFlipped
297 {
298     return YES;
299 }
300
301 - (void)_setRemoteAccessibilityWindow:(id)window
302 {
303 #if !defined(BUILDING_ON_SNOW_LEOPARD)
304     WKAXInitializeRemoteElementWithWindow(_data->_remoteAccessibilityChild.get(), window);    
305 #endif
306 }
307
308 - (void)setFrameSize:(NSSize)size
309 {
310     [super setFrameSize:size];
311     
312     if (![self frameSizeUpdatesDisabled])
313         [self _setDrawingAreaSize:size];
314 }
315
316 - (void)_updateWindowAndViewFrames
317 {
318     NSWindow *window = [self window];
319     ASSERT(window);
320     
321     NSRect windowFrameInScreenCoordinates = [window frame];
322     NSRect viewFrameInWindowCoordinates = [self convertRect:[self frame] toView:nil];
323     NSPoint accessibilityPosition = [[self accessibilityAttributeValue:NSAccessibilityPositionAttribute] pointValue];
324     
325     _data->_page->windowAndViewFramesChanged(enclosingIntRect(windowFrameInScreenCoordinates), enclosingIntRect(viewFrameInWindowCoordinates), IntPoint(accessibilityPosition));
326 }
327
328 - (void)renewGState
329 {
330     // Hide the find indicator.
331     _data->_findIndicatorWindow = nullptr;
332
333     // Update the view frame.
334     if ([self window])
335         [self _updateWindowAndViewFrames];
336
337     [super renewGState];
338 }
339
340 typedef HashMap<SEL, String> SelectorNameMap;
341
342 // Map selectors into Editor command names.
343 // This is not needed for any selectors that have the same name as the Editor command.
344 static const SelectorNameMap* createSelectorExceptionMap()
345 {
346     SelectorNameMap* map = new HashMap<SEL, String>;
347     
348     map->add(@selector(insertNewlineIgnoringFieldEditor:), "InsertNewline");
349     map->add(@selector(insertParagraphSeparator:), "InsertNewline");
350     map->add(@selector(insertTabIgnoringFieldEditor:), "InsertTab");
351     map->add(@selector(pageDown:), "MovePageDown");
352     map->add(@selector(pageDownAndModifySelection:), "MovePageDownAndModifySelection");
353     map->add(@selector(pageUp:), "MovePageUp");
354     map->add(@selector(pageUpAndModifySelection:), "MovePageUpAndModifySelection");
355     map->add(@selector(scrollPageDown:), "ScrollPageForward");
356     map->add(@selector(scrollPageUp:), "ScrollPageBackward");
357     
358     return map;
359 }
360
361 static String commandNameForSelector(SEL selector)
362 {
363     // Check the exception map first.
364     static const SelectorNameMap* exceptionMap = createSelectorExceptionMap();
365     SelectorNameMap::const_iterator it = exceptionMap->find(selector);
366     if (it != exceptionMap->end())
367         return it->second;
368     
369     // Remove the trailing colon.
370     // No need to capitalize the command name since Editor command names are
371     // not case sensitive.
372     const char* selectorName = sel_getName(selector);
373     size_t selectorNameLength = strlen(selectorName);
374     if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
375         return String();
376     return String(selectorName, selectorNameLength - 1);
377 }
378
379 // Editing commands
380
381 #define WEBCORE_COMMAND(command) - (void)command:(id)sender { _data->_page->executeEditCommand(commandNameForSelector(_cmd)); }
382
383 WEBCORE_COMMAND(alignCenter)
384 WEBCORE_COMMAND(alignJustified)
385 WEBCORE_COMMAND(alignLeft)
386 WEBCORE_COMMAND(alignRight)
387 WEBCORE_COMMAND(copy)
388 WEBCORE_COMMAND(cut)
389 WEBCORE_COMMAND(delete)
390 WEBCORE_COMMAND(deleteBackward)
391 WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter)
392 WEBCORE_COMMAND(deleteForward)
393 WEBCORE_COMMAND(deleteToBeginningOfLine)
394 WEBCORE_COMMAND(deleteToBeginningOfParagraph)
395 WEBCORE_COMMAND(deleteToEndOfLine)
396 WEBCORE_COMMAND(deleteToEndOfParagraph)
397 WEBCORE_COMMAND(deleteToMark)
398 WEBCORE_COMMAND(deleteWordBackward)
399 WEBCORE_COMMAND(deleteWordForward)
400 WEBCORE_COMMAND(ignoreSpelling)
401 WEBCORE_COMMAND(indent)
402 WEBCORE_COMMAND(insertBacktab)
403 WEBCORE_COMMAND(insertLineBreak)
404 WEBCORE_COMMAND(insertNewline)
405 WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor)
406 WEBCORE_COMMAND(insertParagraphSeparator)
407 WEBCORE_COMMAND(insertTab)
408 WEBCORE_COMMAND(insertTabIgnoringFieldEditor)
409 WEBCORE_COMMAND(makeTextWritingDirectionLeftToRight)
410 WEBCORE_COMMAND(makeTextWritingDirectionNatural)
411 WEBCORE_COMMAND(makeTextWritingDirectionRightToLeft)
412 WEBCORE_COMMAND(moveBackward)
413 WEBCORE_COMMAND(moveBackwardAndModifySelection)
414 WEBCORE_COMMAND(moveDown)
415 WEBCORE_COMMAND(moveDownAndModifySelection)
416 WEBCORE_COMMAND(moveForward)
417 WEBCORE_COMMAND(moveForwardAndModifySelection)
418 WEBCORE_COMMAND(moveLeft)
419 WEBCORE_COMMAND(moveLeftAndModifySelection)
420 WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection)
421 WEBCORE_COMMAND(moveParagraphForwardAndModifySelection)
422 WEBCORE_COMMAND(moveRight)
423 WEBCORE_COMMAND(moveRightAndModifySelection)
424 WEBCORE_COMMAND(moveToBeginningOfDocument)
425 WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection)
426 WEBCORE_COMMAND(moveToBeginningOfLine)
427 WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection)
428 WEBCORE_COMMAND(moveToBeginningOfParagraph)
429 WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection)
430 WEBCORE_COMMAND(moveToBeginningOfSentence)
431 WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection)
432 WEBCORE_COMMAND(moveToEndOfDocument)
433 WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection)
434 WEBCORE_COMMAND(moveToEndOfLine)
435 WEBCORE_COMMAND(moveToEndOfLineAndModifySelection)
436 WEBCORE_COMMAND(moveToEndOfParagraph)
437 WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection)
438 WEBCORE_COMMAND(moveToEndOfSentence)
439 WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection)
440 WEBCORE_COMMAND(moveToLeftEndOfLine)
441 WEBCORE_COMMAND(moveToLeftEndOfLineAndModifySelection)
442 WEBCORE_COMMAND(moveToRightEndOfLine)
443 WEBCORE_COMMAND(moveToRightEndOfLineAndModifySelection)
444 WEBCORE_COMMAND(moveUp)
445 WEBCORE_COMMAND(moveUpAndModifySelection)
446 WEBCORE_COMMAND(moveWordBackward)
447 WEBCORE_COMMAND(moveWordBackwardAndModifySelection)
448 WEBCORE_COMMAND(moveWordForward)
449 WEBCORE_COMMAND(moveWordForwardAndModifySelection)
450 WEBCORE_COMMAND(moveWordLeft)
451 WEBCORE_COMMAND(moveWordLeftAndModifySelection)
452 WEBCORE_COMMAND(moveWordRight)
453 WEBCORE_COMMAND(moveWordRightAndModifySelection)
454 WEBCORE_COMMAND(outdent)
455 WEBCORE_COMMAND(pageDown)
456 WEBCORE_COMMAND(pageDownAndModifySelection)
457 WEBCORE_COMMAND(pageUp)
458 WEBCORE_COMMAND(pageUpAndModifySelection)
459 WEBCORE_COMMAND(paste)
460 WEBCORE_COMMAND(pasteAsPlainText)
461 WEBCORE_COMMAND(scrollPageDown)
462 WEBCORE_COMMAND(scrollPageUp)
463 WEBCORE_COMMAND(scrollToBeginningOfDocument)
464 WEBCORE_COMMAND(scrollToEndOfDocument)
465 WEBCORE_COMMAND(selectAll)
466 WEBCORE_COMMAND(selectLine)
467 WEBCORE_COMMAND(selectParagraph)
468 WEBCORE_COMMAND(selectSentence)
469 WEBCORE_COMMAND(selectToMark)
470 WEBCORE_COMMAND(selectWord)
471 WEBCORE_COMMAND(setMark)
472 WEBCORE_COMMAND(subscript)
473 WEBCORE_COMMAND(superscript)
474 WEBCORE_COMMAND(swapWithMark)
475 WEBCORE_COMMAND(takeFindStringFromSelection)
476 WEBCORE_COMMAND(transpose)
477 WEBCORE_COMMAND(underline)
478 WEBCORE_COMMAND(unscript)
479 WEBCORE_COMMAND(yank)
480 WEBCORE_COMMAND(yankAndSelect)
481
482 #undef WEBCORE_COMMAND
483
484 /*
485
486 When possible, editing-related methods should be implemented in WebCore with the
487 EditorCommand mechanism and invoked via WEBCORE_COMMAND, rather than implementing
488 individual methods here with Mac-specific code.
489
490 Editing-related methods still unimplemented that are implemented in WebKit1:
491
492 - (void)capitalizeWord:(id)sender;
493 - (void)centerSelectionInVisibleArea:(id)sender;
494 - (void)changeFont:(id)sender;
495 - (void)complete:(id)sender;
496 - (void)copyFont:(id)sender;
497 - (void)lowercaseWord:(id)sender;
498 - (void)makeBaseWritingDirectionLeftToRight:(id)sender;
499 - (void)makeBaseWritingDirectionNatural:(id)sender;
500 - (void)makeBaseWritingDirectionRightToLeft:(id)sender;
501 - (void)pasteFont:(id)sender;
502 - (void)scrollLineDown:(id)sender;
503 - (void)scrollLineUp:(id)sender;
504 - (void)showGuessPanel:(id)sender;
505 - (void)uppercaseWord:(id)sender;
506
507 Some other editing-related methods still unimplemented:
508
509 - (void)changeCaseOfLetter:(id)sender;
510 - (void)copyRuler:(id)sender;
511 - (void)insertContainerBreak:(id)sender;
512 - (void)insertDoubleQuoteIgnoringSubstitution:(id)sender;
513 - (void)insertSingleQuoteIgnoringSubstitution:(id)sender;
514 - (void)pasteRuler:(id)sender;
515 - (void)toggleRuler:(id)sender;
516 - (void)transposeWords:(id)sender;
517
518 */
519
520 // Menu items validation
521
522 static NSMenuItem *menuItem(id <NSValidatedUserInterfaceItem> item)
523 {
524     if (![(NSObject *)item isKindOfClass:[NSMenuItem class]])
525         return nil;
526     return (NSMenuItem *)item;
527 }
528
529 static NSToolbarItem *toolbarItem(id <NSValidatedUserInterfaceItem> item)
530 {
531     if (![(NSObject *)item isKindOfClass:[NSToolbarItem class]])
532         return nil;
533     return (NSToolbarItem *)item;
534 }
535
536 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
537 {
538     SEL action = [item action];
539
540     if (action == @selector(showGuessPanel:)) {
541         if (NSMenuItem *menuItem = ::menuItem(item)) {
542             BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible];
543             [menuItem setTitle:panelShowing
544                 ? UI_STRING("Hide Spelling and Grammar", "menu item title")
545                 : UI_STRING("Show Spelling and Grammar", "menu item title")];
546         }
547         return _data->_page->selectionState().isContentEditable;
548     }
549
550     if (action == @selector(checkSpelling:) || action == @selector(changeSpelling:))
551         return _data->_page->selectionState().isContentEditable;
552
553     if (action == @selector(toggleContinuousSpellChecking:)) {
554         bool enabled = TextChecker::isContinuousSpellCheckingAllowed();
555         bool checked = enabled && TextChecker::state().isContinuousSpellCheckingEnabled;
556         [menuItem(item) setState:checked ? NSOnState : NSOffState];
557         return enabled;
558     }
559
560     if (action == @selector(toggleGrammarChecking:)) {
561         bool checked = TextChecker::state().isGrammarCheckingEnabled;
562         [menuItem(item) setState:checked ? NSOnState : NSOffState];
563         return YES;
564     }
565
566     if (action == @selector(toggleAutomaticSpellingCorrection:)) {
567         bool checked = TextChecker::state().isAutomaticSpellingCorrectionEnabled;
568         [menuItem(item) setState:checked ? NSOnState : NSOffState];
569         return _data->_page->selectionState().isContentEditable;
570     }
571
572     if (action == @selector(orderFrontSubstitutionsPanel:)) {
573         if (NSMenuItem *menuItem = ::menuItem(item)) {
574             BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible];
575             [menuItem setTitle:panelShowing
576                 ? UI_STRING("Hide Substitutions", "menu item title")
577                 : UI_STRING("Show Substitutions", "menu item title")];
578         }
579         return _data->_page->selectionState().isContentEditable;
580     }
581
582     if (action == @selector(toggleSmartInsertDelete:)) {
583         bool checked = _data->_page->isSmartInsertDeleteEnabled();
584         [menuItem(item) setState:checked ? NSOnState : NSOffState];
585         return _data->_page->selectionState().isContentEditable;
586     }
587
588     if (action == @selector(toggleAutomaticQuoteSubstitution:)) {
589         bool checked = TextChecker::state().isAutomaticQuoteSubstitutionEnabled;
590         [menuItem(item) setState:checked ? NSOnState : NSOffState];
591         return _data->_page->selectionState().isContentEditable;
592     }
593
594     if (action == @selector(toggleAutomaticDashSubstitution:)) {
595         bool checked = TextChecker::state().isAutomaticDashSubstitutionEnabled;
596         [menuItem(item) setState:checked ? NSOnState : NSOffState];
597         return _data->_page->selectionState().isContentEditable;
598     }
599
600     if (action == @selector(toggleAutomaticLinkDetection:)) {
601         bool checked = TextChecker::state().isAutomaticLinkDetectionEnabled;
602         [menuItem(item) setState:checked ? NSOnState : NSOffState];
603         return _data->_page->selectionState().isContentEditable;
604     }
605
606     if (action == @selector(toggleAutomaticTextReplacement:)) {
607         bool checked = TextChecker::state().isAutomaticTextReplacementEnabled;
608         [menuItem(item) setState:checked ? NSOnState : NSOffState];
609         return _data->_page->selectionState().isContentEditable;
610     }
611
612     if (action == @selector(uppercaseWord:) || action == @selector(lowercaseWord:) || action == @selector(capitalizeWord:))
613         return _data->_page->selectionState().selectedRangeLength && _data->_page->selectionState().isContentEditable;
614     
615     if (action == @selector(stopSpeaking:))
616         return [NSApp isSpeaking];
617
618     // Next, handle editor commands. Start by returning YES for anything that is not an editor command.
619     // Returning YES is the default thing to do in an AppKit validate method for any selector that is not recognized.
620     String commandName = commandNameForSelector([item action]);
621     if (!Editor::commandIsSupportedFromMenuOrKeyBinding(commandName))
622         return YES;
623
624     // Add this item to the vector of items for a given command that are awaiting validation.
625     pair<ValidationMap::iterator, bool> addResult = _data->_validationMap.add(commandName, ValidationVector());
626     addResult.first->second.append(item);
627     if (addResult.second) {
628         // If we are not already awaiting validation for this command, start the asynchronous validation process.
629         // FIXME: Theoretically, there is a race here; when we get the answer it might be old, from a previous time
630         // we asked for the same command; there is no guarantee the answer is still valid.
631         // FIXME: The function called here should be renamed validateCommand because it is not specific to menu items.
632         _data->_page->validateMenuItem(commandName);
633     }
634
635     // Treat as enabled until we get the result back from the web process and _setUserInterfaceItemState is called.
636     // FIXME <rdar://problem/8803459>: This means disabled items will flash enabled at first for a moment.
637     // But returning NO here would be worse; that would make keyboard commands such as command-C fail.
638     return YES;
639 }
640
641 static void speakString(WKStringRef string, WKErrorRef error, void*)
642 {
643     if (error)
644         return;
645     if (!string)
646         return;
647
648     NSString *convertedString = toImpl(string)->string();
649     [NSApp speakString:convertedString];
650 }
651
652 - (IBAction)startSpeaking:(id)sender
653 {
654     _data->_page->getSelectionOrContentsAsString(StringCallback::create(0, speakString));
655 }
656
657 - (IBAction)stopSpeaking:(id)sender
658 {
659     [NSApp stopSpeaking:sender];
660 }
661
662 - (IBAction)showGuessPanel:(id)sender
663 {
664     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
665     if (!checker) {
666         LOG_ERROR("No NSSpellChecker");
667         return;
668     }
669     
670     NSPanel *spellingPanel = [checker spellingPanel];
671     if ([spellingPanel isVisible]) {
672         [spellingPanel orderOut:sender];
673         return;
674     }
675     
676     _data->_page->advanceToNextMisspelling(true);
677     [spellingPanel orderFront:sender];
678 }
679
680 - (IBAction)checkSpelling:(id)sender
681 {
682     _data->_page->advanceToNextMisspelling(false);
683 }
684
685 - (void)changeSpelling:(id)sender
686 {
687     NSString *word = [[sender selectedCell] stringValue];
688
689     _data->_page->changeSpellingToWord(word);
690 }
691
692 - (IBAction)toggleContinuousSpellChecking:(id)sender
693 {
694     bool spellCheckingEnabled = !TextChecker::state().isContinuousSpellCheckingEnabled;
695     TextChecker::setContinuousSpellCheckingEnabled(spellCheckingEnabled);
696
697     _data->_page->process()->updateTextCheckerState();
698
699     if (!spellCheckingEnabled)
700         _data->_page->unmarkAllMisspellings();
701 }
702
703 - (IBAction)toggleGrammarChecking:(id)sender
704 {
705     bool grammarCheckingEnabled = !TextChecker::state().isGrammarCheckingEnabled;
706     TextChecker::setGrammarCheckingEnabled(grammarCheckingEnabled);
707
708     _data->_page->process()->updateTextCheckerState();
709
710     if (!grammarCheckingEnabled)
711         _data->_page->unmarkAllBadGrammar();
712 }
713
714 - (IBAction)toggleAutomaticSpellingCorrection:(id)sender
715 {
716     TextChecker::setAutomaticSpellingCorrectionEnabled(!TextChecker::state().isAutomaticSpellingCorrectionEnabled);
717
718     _data->_page->process()->updateTextCheckerState();
719 }
720
721 - (void)orderFrontSubstitutionsPanel:(id)sender
722 {
723     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
724     if (!checker) {
725         LOG_ERROR("No NSSpellChecker");
726         return;
727     }
728     
729     NSPanel *substitutionsPanel = [checker substitutionsPanel];
730     if ([substitutionsPanel isVisible]) {
731         [substitutionsPanel orderOut:sender];
732         return;
733     }
734     [substitutionsPanel orderFront:sender];
735 }
736
737 - (IBAction)toggleSmartInsertDelete:(id)sender
738 {
739     _data->_page->setSmartInsertDeleteEnabled(!_data->_page->isSmartInsertDeleteEnabled());
740 }
741
742 - (BOOL)isAutomaticQuoteSubstitutionEnabled
743 {
744     return TextChecker::state().isAutomaticQuoteSubstitutionEnabled;
745 }
746
747 - (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag
748 {
749     if (static_cast<bool>(flag) == TextChecker::state().isAutomaticQuoteSubstitutionEnabled)
750         return;
751
752     TextChecker::setAutomaticQuoteSubstitutionEnabled(flag);
753     _data->_page->process()->updateTextCheckerState();
754 }
755
756 - (void)toggleAutomaticQuoteSubstitution:(id)sender
757 {
758     TextChecker::setAutomaticQuoteSubstitutionEnabled(!TextChecker::state().isAutomaticQuoteSubstitutionEnabled);
759     _data->_page->process()->updateTextCheckerState();
760 }
761
762 - (BOOL)isAutomaticDashSubstitutionEnabled
763 {
764     return TextChecker::state().isAutomaticDashSubstitutionEnabled;
765 }
766
767 - (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag
768 {
769     if (static_cast<bool>(flag) == TextChecker::state().isAutomaticDashSubstitutionEnabled)
770         return;
771
772     TextChecker::setAutomaticDashSubstitutionEnabled(flag);
773     _data->_page->process()->updateTextCheckerState();
774 }
775
776 - (void)toggleAutomaticDashSubstitution:(id)sender
777 {
778     TextChecker::setAutomaticDashSubstitutionEnabled(!TextChecker::state().isAutomaticDashSubstitutionEnabled);
779     _data->_page->process()->updateTextCheckerState();
780 }
781
782 - (BOOL)isAutomaticLinkDetectionEnabled
783 {
784     return TextChecker::state().isAutomaticLinkDetectionEnabled;
785 }
786
787 - (void)setAutomaticLinkDetectionEnabled:(BOOL)flag
788 {
789     if (static_cast<bool>(flag) == TextChecker::state().isAutomaticLinkDetectionEnabled)
790         return;
791
792     TextChecker::setAutomaticLinkDetectionEnabled(flag);
793     _data->_page->process()->updateTextCheckerState();
794 }
795
796 - (void)toggleAutomaticLinkDetection:(id)sender
797 {
798     TextChecker::setAutomaticLinkDetectionEnabled(!TextChecker::state().isAutomaticLinkDetectionEnabled);
799     _data->_page->process()->updateTextCheckerState();
800 }
801
802 - (BOOL)isAutomaticTextReplacementEnabled
803 {
804     return TextChecker::state().isAutomaticTextReplacementEnabled;
805 }
806
807 - (void)setAutomaticTextReplacementEnabled:(BOOL)flag
808 {
809     if (static_cast<bool>(flag) == TextChecker::state().isAutomaticTextReplacementEnabled)
810         return;
811
812     TextChecker::setAutomaticTextReplacementEnabled(flag);
813     _data->_page->process()->updateTextCheckerState();
814 }
815
816 - (void)toggleAutomaticTextReplacement:(id)sender
817 {
818     TextChecker::setAutomaticTextReplacementEnabled(!TextChecker::state().isAutomaticTextReplacementEnabled);
819     _data->_page->process()->updateTextCheckerState();
820 }
821
822 - (void)uppercaseWord:(id)sender
823 {
824     _data->_page->uppercaseWord();
825 }
826
827 - (void)lowercaseWord:(id)sender
828 {
829     _data->_page->lowercaseWord();
830 }
831
832 - (void)capitalizeWord:(id)sender
833 {
834     _data->_page->capitalizeWord();
835 }
836
837 // Events
838
839 // Override this so that AppKit will send us arrow keys as key down events so we can
840 // support them via the key bindings mechanism.
841 - (BOOL)_wantsKeyDownForEvent:(NSEvent *)event
842 {
843     return YES;
844 }
845
846 - (void)_setMouseDownEvent:(NSEvent *)event
847 {
848     ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown);
849     
850     if (event == _data->_mouseDownEvent)
851         return;
852     
853     [_data->_mouseDownEvent release];
854     _data->_mouseDownEvent = [event retain];
855 }
856
857 #define EVENT_HANDLER(Selector, Type) \
858     - (void)Selector:(NSEvent *)theEvent \
859     { \
860         Web##Type##Event webEvent = WebEventFactory::createWeb##Type##Event(theEvent, self); \
861         _data->_page->handle##Type##Event(webEvent); \
862     }
863
864 EVENT_HANDLER(mouseEntered, Mouse)
865 EVENT_HANDLER(mouseExited, Mouse)
866 EVENT_HANDLER(mouseMoved, Mouse)
867 EVENT_HANDLER(otherMouseDown, Mouse)
868 EVENT_HANDLER(otherMouseDragged, Mouse)
869 EVENT_HANDLER(otherMouseMoved, Mouse)
870 EVENT_HANDLER(otherMouseUp, Mouse)
871 EVENT_HANDLER(rightMouseDown, Mouse)
872 EVENT_HANDLER(rightMouseDragged, Mouse)
873 EVENT_HANDLER(rightMouseMoved, Mouse)
874 EVENT_HANDLER(rightMouseUp, Mouse)
875 EVENT_HANDLER(scrollWheel, Wheel)
876
877 #undef EVENT_HANDLER
878
879 - (void)_mouseHandler:(NSEvent *)event
880 {
881     NSInputManager *currentInputManager = [NSInputManager currentInputManager];
882     if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
883         return;
884     WebMouseEvent webEvent = WebEventFactory::createWebMouseEvent(event, self);
885     _data->_page->handleMouseEvent(webEvent);
886 }
887
888 - (void)mouseDown:(NSEvent *)event
889 {
890     [self _setMouseDownEvent:event];
891     _data->_ignoringMouseDraggedEvents = NO;
892     _data->_dragHasStarted = NO;
893     [self _mouseHandler:event];
894 }
895
896 - (void)mouseUp:(NSEvent *)event
897 {
898     [self _setMouseDownEvent:nil];
899     [self _mouseHandler:event];
900 }
901
902 - (void)mouseDragged:(NSEvent *)event
903 {
904     if (_data->_ignoringMouseDraggedEvents)
905         return;
906     [self _mouseHandler:event];
907 }
908
909 #if ENABLE(GESTURE_EVENTS)
910
911 static const short kIOHIDEventTypeScroll = 6;
912
913 - (void)shortCircuitedEndGestureWithEvent:(NSEvent *)event
914 {
915     if ([event subtype] != kIOHIDEventTypeScroll)
916         return;
917
918     WebGestureEvent webEvent = WebEventFactory::createWebGestureEvent(event, self);
919     _data->_page->handleGestureEvent(webEvent);
920
921     if (_data->_endGestureMonitor) {
922         [NSEvent removeMonitor:_data->_endGestureMonitor];
923         _data->_endGestureMonitor = nil;
924     }
925 }
926
927 - (void)beginGestureWithEvent:(NSEvent *)event
928 {
929     if ([event subtype] != kIOHIDEventTypeScroll)
930         return;
931
932     WebGestureEvent webEvent = WebEventFactory::createWebGestureEvent(event, self);
933     _data->_page->handleGestureEvent(webEvent);
934
935     if (!_data->_endGestureMonitor) {
936         _data->_endGestureMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskEndGesture handler:^(NSEvent *blockEvent) {
937             [self shortCircuitedEndGestureWithEvent:blockEvent];
938             return blockEvent;
939         }];
940     }
941 }
942 #endif
943
944 - (void)doCommandBySelector:(SEL)selector
945 {
946     if (selector != @selector(noop:))
947         _data->_commandsList.append(KeypressCommand(commandNameForSelector(selector)));
948 }
949
950 - (void)insertText:(id)string
951 {
952     BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; // Otherwise, NSString
953     
954     LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string);
955     NSString *text;
956     bool isFromInputMethod = _data->_page->selectionState().hasComposition;
957
958     if (isAttributedString) {
959         text = [string string];
960         // We deal with the NSTextInputReplacementRangeAttributeName attribute from NSAttributedString here
961         // simply because it is used by at least one Input Method -- it corresonds to the kEventParamTextInputSendReplaceRange
962         // event in TSM.  This behaviour matches that of -[WebHTMLView setMarkedText:selectedRange:] when it receives an
963         // NSAttributedString
964         NSString *rangeString = [string attribute:NSTextInputReplacementRangeAttributeName atIndex:0 longestEffectiveRange:NULL inRange:NSMakeRange(0, [text length])];
965         LOG(TextInput, "ReplacementRange: %@", rangeString);
966         if (rangeString)
967             isFromInputMethod = YES;
968     } else
969         text = string;
970     
971     String eventText = text;
972     
973     if (!isFromInputMethod)
974         _data->_commandsList.append(KeypressCommand("insertText", text));
975     else {
976         eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore
977         _data->_commandsList.append(KeypressCommand("insertText", eventText));
978     }
979 }
980
981 - (BOOL)_handleStyleKeyEquivalent:(NSEvent *)event
982 {
983     if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) != NSCommandKeyMask)
984         return NO;
985     
986     // Here we special case cmd+b and cmd+i but not cmd+u, for historic reason.
987     // This should not be changed, since it could break some Mac applications that
988     // rely on this inherent behavior.
989     // See https://bugs.webkit.org/show_bug.cgi?id=24943
990     
991     NSString *string = [event characters];
992     if ([string caseInsensitiveCompare:@"b"] == NSOrderedSame) {
993         _data->_page->executeEditCommand("ToggleBold");
994         return YES;
995     }
996     if ([string caseInsensitiveCompare:@"i"] == NSOrderedSame) {
997         _data->_page->executeEditCommand("ToggleItalic");
998         return YES;
999     }
1000     
1001     return NO;
1002 }
1003
1004 - (BOOL)performKeyEquivalent:(NSEvent *)event
1005 {
1006     // There's a chance that responding to this event will run a nested event loop, and
1007     // fetching a new event might release the old one. Retaining and then autoreleasing
1008     // the current event prevents that from causing a problem inside WebKit or AppKit code.
1009     [[event retain] autorelease];
1010     
1011     BOOL eventWasSentToWebCore = (_data->_keyDownEventBeingResent == event);
1012
1013     // Pass key combos through WebCore if there is a key binding available for
1014     // this event. This lets web pages have a crack at intercepting key-modified keypresses.
1015     // But don't do it if we have already handled the event.
1016     // Pressing Esc results in a fake event being sent - don't pass it to WebCore.
1017     if (!eventWasSentToWebCore && event == [NSApp currentEvent] && self == [[self window] firstResponder]) {
1018         [_data->_keyDownEventBeingResent release];
1019         _data->_keyDownEventBeingResent = nil;
1020         
1021         _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(event, self));
1022         return YES;
1023     }
1024     
1025     return [self _handleStyleKeyEquivalent:event] || [super performKeyEquivalent:event];
1026 }
1027
1028 - (void)keyUp:(NSEvent *)theEvent
1029 {
1030     _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, self));
1031 }
1032
1033 - (void)keyDown:(NSEvent *)theEvent
1034 {
1035     if (_data->_pluginComplexTextInputIdentifier) {
1036         // Try feeding the keyboard event directly to the plug-in.
1037         NSString *string = nil;
1038         if ([[WKTextInputWindowController sharedTextInputWindowController] interpretKeyEvent:theEvent string:&string]) {
1039             if (string)
1040                 _data->_page->sendComplexTextInputToPlugin(_data->_pluginComplexTextInputIdentifier, string);
1041             return;
1042         }
1043     }
1044
1045     _data->_underlines.clear();
1046     _data->_selectionStart = 0;
1047     _data->_selectionEnd = 0;
1048     // We could be receiving a key down from AppKit if we have re-sent an event
1049     // that maps to an action that is currently unavailable (for example a copy when
1050     // there is no range selection).
1051     // If this is the case we should ignore the key down.
1052     if (_data->_keyDownEventBeingResent == theEvent) {
1053         [_data->_keyDownEventBeingResent release];
1054         _data->_keyDownEventBeingResent = nil;
1055         [super keyDown:theEvent];
1056         return;
1057     }
1058     _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, self));
1059 }
1060
1061 - (NSTextInputContext *)inputContext {
1062     if (_data->_pluginComplexTextInputIdentifier)
1063         return [[WKTextInputWindowController sharedTextInputWindowController] inputContext];
1064
1065     return [super inputContext];
1066 }
1067
1068 - (NSRange)selectedRange
1069 {
1070     if (_data->_page->selectionState().isNone || !_data->_page->selectionState().isContentEditable)
1071         return NSMakeRange(NSNotFound, 0);
1072     
1073     LOG(TextInput, "selectedRange -> (%u, %u)", _data->_page->selectionState().selectedRangeStart, _data->_page->selectionState().selectedRangeLength);
1074     return NSMakeRange(_data->_page->selectionState().selectedRangeStart, _data->_page->selectionState().selectedRangeLength);
1075 }
1076
1077 - (BOOL)hasMarkedText
1078 {
1079     LOG(TextInput, "hasMarkedText -> %u", _data->_page->selectionState().hasComposition);
1080     return _data->_page->selectionState().hasComposition;
1081 }
1082
1083 - (void)unmarkText
1084 {
1085     LOG(TextInput, "unmarkText");
1086     
1087     _data->_commandsList.append(KeypressCommand("unmarkText"));
1088 }
1089
1090 - (NSArray *)validAttributesForMarkedText
1091 {
1092     static NSArray *validAttributes;
1093     if (!validAttributes) {
1094         validAttributes = [[NSArray alloc] initWithObjects:
1095                            NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName,
1096                            NSMarkedClauseSegmentAttributeName, NSTextInputReplacementRangeAttributeName, nil];
1097         // NSText also supports the following attributes, but it's
1098         // hard to tell which are really required for text input to
1099         // work well; I have not seen any input method make use of them yet.
1100         //     NSFontAttributeName, NSForegroundColorAttributeName,
1101         //     NSBackgroundColorAttributeName, NSLanguageAttributeName.
1102         CFRetain(validAttributes);
1103     }
1104     LOG(TextInput, "validAttributesForMarkedText -> (...)");
1105     return validAttributes;
1106 }
1107
1108 static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnderline>& result)
1109 {
1110     int length = [[string string] length];
1111     
1112     int i = 0;
1113     while (i < length) {
1114         NSRange range;
1115         NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&range inRange:NSMakeRange(i, length - i)];
1116         
1117         if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
1118             Color color = Color::black;
1119             if (NSColor *colorAttr = [attrs objectForKey:NSUnderlineColorAttributeName])
1120                 color = colorFromNSColor([colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
1121             result.append(CompositionUnderline(range.location, NSMaxRange(range), color, [style intValue] > 1));
1122         }
1123         
1124         i = range.location + range.length;
1125     }
1126 }
1127
1128 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange
1129 {
1130     BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; // Otherwise, NSString
1131     
1132     LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u)", isAttributedString ? [string string] : string, newSelRange.location, newSelRange.length);
1133     
1134     NSString *text = string;
1135     
1136     if (isAttributedString) {
1137         text = [string string];
1138         extractUnderlines(string, _data->_underlines);
1139     }
1140     
1141     _data->_commandsList.append(KeypressCommand("setMarkedText", text));
1142     _data->_selectionStart = newSelRange.location;
1143     _data->_selectionEnd = NSMaxRange(newSelRange);
1144 }
1145
1146 - (NSRange)markedRange
1147 {
1148     uint64_t location;
1149     uint64_t length;
1150
1151     _data->_page->getMarkedRange(location, length);
1152     LOG(TextInput, "markedRange -> (%u, %u)", location, length);
1153     return NSMakeRange(location, length);
1154 }
1155
1156 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)nsRange
1157 {
1158     // This is not implemented for now. Need to figure out how to serialize the attributed string across processes.
1159     LOG(TextInput, "attributedSubstringFromRange");
1160     return nil;
1161 }
1162
1163 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
1164 {
1165     NSWindow *window = [self window];
1166     
1167     if (window)
1168         thePoint = [window convertScreenToBase:thePoint];
1169     thePoint = [self convertPoint:thePoint fromView:nil];  // the point is relative to the main frame 
1170     
1171     uint64_t result = _data->_page->characterIndexForPoint(IntPoint(thePoint));
1172     LOG(TextInput, "characterIndexForPoint:(%f, %f) -> %u", thePoint.x, thePoint.y, result);
1173     return result;
1174 }
1175
1176 - (NSRect)firstRectForCharacterRange:(NSRange)theRange
1177
1178     // Just to match NSTextView's behavior. Regression tests cannot detect this;
1179     // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682
1180     // (type something; try ranges (1, -1) and (2, -1).
1181     if ((theRange.location + theRange.length < theRange.location) && (theRange.location + theRange.length != 0))
1182         theRange.length = 0;
1183     
1184     NSRect resultRect = _data->_page->firstRectForCharacterRange(theRange.location, theRange.length);
1185     resultRect = [self convertRect:resultRect toView:nil];
1186     
1187     NSWindow *window = [self window];
1188     if (window)
1189         resultRect.origin = [window convertBaseToScreen:resultRect.origin];
1190     
1191     LOG(TextInput, "firstRectForCharacterRange:(%u, %u) -> (%f, %f, %f, %f)", theRange.location, theRange.length, resultRect.origin.x, resultRect.origin.y, resultRect.size.width, resultRect.size.height);
1192     return resultRect;
1193 }
1194
1195 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
1196 {
1197     NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint];
1198     NSPoint windowMouseLoc = windowImageLoc;
1199    
1200     // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
1201     _data->_ignoringMouseDraggedEvents = YES;
1202     
1203     _data->_page->dragEnded(IntPoint(windowMouseLoc), globalPoint(windowMouseLoc, [self window]), operation);
1204 }
1205
1206 - (DragApplicationFlags)applicationFlags:(id <NSDraggingInfo>)draggingInfo
1207 {
1208     uint32_t flags = 0;
1209     if ([NSApp modalWindow])
1210         flags = DragApplicationIsModal;
1211     if ([[self window] attachedSheet])
1212         flags |= DragApplicationHasAttachedSheet;
1213     if ([draggingInfo draggingSource] == self)
1214         flags |= DragApplicationIsSource;
1215     if ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)
1216         flags |= DragApplicationIsCopyKeyDown;
1217     return static_cast<DragApplicationFlags>(flags);
1218 }
1219
1220 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)draggingInfo
1221 {
1222     IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]);
1223     IntPoint global(globalPoint([draggingInfo draggingLocation], [self window]));
1224     DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]);
1225
1226     _data->_page->resetDragOperation();
1227     _data->_page->performDragControllerAction(DragControllerActionEntered, &dragData, [[draggingInfo draggingPasteboard] name]);
1228     return NSDragOperationCopy;
1229 }
1230
1231 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)draggingInfo
1232 {
1233     IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]);
1234     IntPoint global(globalPoint([draggingInfo draggingLocation], [self window]));
1235     DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]);
1236     _data->_page->performDragControllerAction(DragControllerActionUpdated, &dragData, [[draggingInfo draggingPasteboard] name]);
1237     return _data->_page->dragOperation();
1238 }
1239
1240 - (void)draggingExited:(id <NSDraggingInfo>)draggingInfo
1241 {
1242     IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]);
1243     IntPoint global(globalPoint([draggingInfo draggingLocation], [self window]));
1244     DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]);
1245     _data->_page->performDragControllerAction(DragControllerActionExited, &dragData, [[draggingInfo draggingPasteboard] name]);
1246     _data->_page->resetDragOperation();
1247 }
1248
1249 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)draggingInfo
1250 {
1251     return YES;
1252 }
1253
1254 - (BOOL)performDragOperation:(id <NSDraggingInfo>)draggingInfo
1255 {
1256     IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]);
1257     IntPoint global(globalPoint([draggingInfo draggingLocation], [self window]));
1258     DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]);
1259     _data->_page->performDragControllerAction(DragControllerActionPerformDrag, &dragData, [[draggingInfo draggingPasteboard] name]);
1260     return YES;
1261 }
1262
1263 - (void)_updateWindowVisibility
1264 {
1265     _data->_page->updateWindowIsVisible(![[self window] isMiniaturized]);
1266 }
1267
1268 - (BOOL)_ownsWindowGrowBox
1269 {
1270     NSWindow* window = [self window];
1271     if (!window)
1272         return NO;
1273
1274     NSView *superview = [self superview];
1275     if (!superview)
1276         return NO;
1277
1278     NSRect growBoxRect = [window _growBoxRect];
1279     if (NSIsEmptyRect(growBoxRect))
1280         return NO;
1281
1282     NSRect visibleRect = [self visibleRect];
1283     if (NSIsEmptyRect(visibleRect))
1284         return NO;
1285
1286     NSRect visibleRectInWindowCoords = [self convertRect:visibleRect toView:nil];
1287     if (!NSIntersectsRect(growBoxRect, visibleRectInWindowCoords))
1288         return NO;
1289
1290     return YES;
1291 }
1292
1293 - (BOOL)_updateGrowBoxForWindowFrameChange
1294 {
1295     // Temporarily enable the resize indicator to make a the _ownsWindowGrowBox calculation work.
1296     BOOL wasShowingIndicator = [[self window] showsResizeIndicator];
1297     [[self window] setShowsResizeIndicator:YES];
1298
1299     BOOL ownsGrowBox = [self _ownsWindowGrowBox];
1300     _data->_page->setWindowResizerSize(ownsGrowBox ? enclosingIntRect([[self window] _growBoxRect]).size() : IntSize());
1301     
1302     // Once WebCore can draw the window resizer, this should read:
1303     // if (wasShowingIndicator)
1304     //     [[self window] setShowsResizeIndicator:!ownsGrowBox];
1305     [[self window] setShowsResizeIndicator:wasShowingIndicator];
1306
1307     return ownsGrowBox;
1308 }
1309
1310 - (void)addWindowObserversForWindow:(NSWindow *)window
1311 {
1312     if (window) {
1313         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidBecomeKey:)
1314                                                      name:NSWindowDidBecomeKeyNotification object:nil];
1315         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidResignKey:)
1316                                                      name:NSWindowDidResignKeyNotification object:nil];
1317         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidMiniaturize:) 
1318                                                      name:NSWindowDidMiniaturizeNotification object:window];
1319         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidDeminiaturize:)
1320                                                      name:NSWindowDidDeminiaturizeNotification object:window];
1321         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowFrameDidChange:)
1322                                                      name:NSWindowDidMoveNotification object:window];
1323         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowFrameDidChange:) 
1324                                                      name:NSWindowDidResizeNotification object:window];
1325     }
1326 }
1327
1328 - (void)removeWindowObservers
1329 {
1330     NSWindow *window = [self window];
1331     if (!window)
1332         return;
1333
1334     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil];
1335     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidResignKeyNotification object:nil];
1336     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidMiniaturizeNotification object:window];
1337     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window];
1338     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidMoveNotification object:window];
1339     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidResizeNotification object:window];
1340 }
1341
1342 - (void)viewWillMoveToWindow:(NSWindow *)window
1343 {
1344     if (window != [self window]) {
1345         [self removeWindowObservers];
1346         [self addWindowObserversForWindow:window];
1347     }
1348 }
1349
1350 - (void)viewDidMoveToWindow
1351 {
1352     // We want to make sure to update the active state while hidden, so if the view is about to become visible, we
1353     // update the active state first and then make it visible. If the view is about to be hidden, we hide it first and then
1354     // update the active state.
1355     if ([self window]) {
1356         _data->_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive);
1357         _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible | WebPageProxy::ViewIsInWindow);
1358         [self _updateWindowVisibility];
1359         [self _updateWindowAndViewFrames];
1360     } else {
1361         _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible);
1362         _data->_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive | WebPageProxy::ViewIsInWindow);
1363
1364 #if ENABLE(GESTURE_EVENTS)
1365         if (_data->_endGestureMonitor) {
1366             [NSEvent removeMonitor:_data->_endGestureMonitor];
1367             _data->_endGestureMonitor = nil;
1368         }
1369 #endif
1370     }
1371     [self _setRemoteAccessibilityWindow:[self window]];
1372 }
1373
1374 - (void)_windowDidBecomeKey:(NSNotification *)notification
1375 {
1376     NSWindow *keyWindow = [notification object];
1377     if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet])
1378         _data->_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive);
1379 }
1380
1381 - (void)_windowDidResignKey:(NSNotification *)notification
1382 {
1383     NSWindow *formerKeyWindow = [notification object];
1384     if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet])
1385         _data->_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive);
1386 }
1387
1388 - (void)_windowDidMiniaturize:(NSNotification *)notification
1389 {
1390     [self _updateWindowVisibility];
1391 }
1392
1393 - (void)_windowDidDeminiaturize:(NSNotification *)notification
1394 {
1395     [self _updateWindowVisibility];
1396 }
1397
1398 - (void)_windowFrameDidChange:(NSNotification *)notification
1399 {
1400     [self _updateWindowAndViewFrames];
1401 }
1402
1403 static void drawPageBackground(CGContextRef context, WebPageProxy* page, const IntRect& rect)
1404 {
1405     if (!page->drawsBackground())
1406         return;
1407
1408     CGContextSaveGState(context);
1409     CGContextSetBlendMode(context, kCGBlendModeCopy);
1410
1411     CGColorRef backgroundColor;
1412     if (page->drawsTransparentBackground())
1413         backgroundColor = CGColorGetConstantColor(kCGColorClear);
1414     else
1415         backgroundColor = CGColorGetConstantColor(kCGColorWhite);
1416
1417     CGContextSetFillColorWithColor(context, backgroundColor);
1418     CGContextFillRect(context, rect);
1419
1420     CGContextRestoreGState(context);
1421 }
1422
1423 - (void)drawRect:(NSRect)rect
1424 {
1425     LOG(View, "drawRect: x:%g, y:%g, width:%g, height:%g", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
1426     _data->_page->endPrinting();
1427     if (useNewDrawingArea()) {
1428         CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]);
1429
1430         if (DrawingAreaProxyImpl* drawingArea = static_cast<DrawingAreaProxyImpl*>(_data->_page->drawingArea())) {
1431             const NSRect *rectsBeingDrawn;
1432             NSInteger numRectsBeingDrawn;
1433             [self getRectsBeingDrawn:&rectsBeingDrawn count:&numRectsBeingDrawn];
1434             for (NSInteger i = 0; i < numRectsBeingDrawn; ++i) {
1435                 Region unpaintedRegion;
1436                 IntRect rect = enclosingIntRect(rectsBeingDrawn[i]);
1437                 drawingArea->paint(context, rect, unpaintedRegion);
1438
1439                 Vector<IntRect> unpaintedRects = unpaintedRegion.rects();
1440                 for (size_t i = 0; i < unpaintedRects.size(); ++i)
1441                     drawPageBackground(context, _data->_page.get(), unpaintedRects[i]);
1442             }
1443         } else 
1444             drawPageBackground(context, _data->_page.get(), enclosingIntRect(rect));
1445
1446         _data->_page->didDraw();
1447         return;
1448     }
1449
1450     if (_data->_page->isValid() && _data->_page->drawingArea()) {
1451         CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]);
1452         _data->_page->drawingArea()->paint(IntRect(rect), context);
1453         _data->_page->didDraw();
1454     } else if (_data->_page->drawsBackground()) {
1455         [_data->_page->drawsTransparentBackground() ? [NSColor clearColor] : [NSColor whiteColor] set];
1456         NSRectFill(rect);
1457     }
1458 }
1459
1460 - (BOOL)isOpaque
1461 {
1462     return _data->_page->drawsBackground();
1463 }
1464
1465 - (void)viewDidHide
1466 {
1467     _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible);
1468 }
1469
1470 - (void)viewDidUnhide
1471 {
1472     _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible);
1473 }
1474
1475 - (BOOL)accessibilityIsIgnored
1476 {
1477     return NO;
1478 }
1479
1480 - (id)accessibilityHitTest:(NSPoint)point
1481 {
1482     return _data->_remoteAccessibilityChild.get();
1483 }
1484
1485 - (id)accessibilityAttributeValue:(NSString*)attribute
1486 {
1487     if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
1488         if (!_data->_remoteAccessibilityChild)
1489             return nil;
1490         return [NSArray arrayWithObject:_data->_remoteAccessibilityChild.get()];
1491     }
1492     if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
1493         return NSAccessibilityGroupRole;
1494     if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute])
1495         return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, nil);
1496     if ([attribute isEqualToString:NSAccessibilityParentAttribute])
1497         return NSAccessibilityUnignoredAncestor([self superview]);
1498     if ([attribute isEqualToString:NSAccessibilityEnabledAttribute])
1499         return [NSNumber numberWithBool:YES];
1500     
1501     return [super accessibilityAttributeValue:attribute];
1502 }
1503
1504 - (NSView *)hitTest:(NSPoint)point
1505 {
1506     NSView *hitView = [super hitTest:point];
1507     if (hitView && _data && hitView == _data->_layerHostingView)
1508         hitView = self;
1509
1510 #if USE(ACCELERATED_COMPOSITING)
1511     if (hitView && _data && hitView == _data->_oldLayerHostingView)
1512         hitView = self;
1513 #endif
1514     return hitView;
1515 }
1516
1517 - (NSInteger)conversationIdentifier
1518 {
1519     return (NSInteger)self;
1520 }
1521
1522
1523 - (BOOL)canChangeFrameLayout:(WKFrameRef)frameRef
1524 {
1525     // PDF documents are already paginated, so we can't change them to add headers and footers.
1526     return !toImpl(frameRef)->isMainFrame() || !_data->_pdfViewController;
1527 }
1528
1529 - (NSPrintOperation *)printOperationWithPrintInfo:(NSPrintInfo *)printInfo forFrame:(WKFrameRef)frameRef
1530 {
1531     LOG(View, "Creating an NSPrintOperation for frame '%s'", toImpl(frameRef)->url().utf8().data());
1532
1533     // Only the top frame can currently contain a PDF view.
1534     if (_data->_pdfViewController) {
1535         if (!toImpl(frameRef)->isMainFrame())
1536             return 0;
1537         return _data->_pdfViewController->makePrintOperation(printInfo);
1538     } else {
1539         RetainPtr<WKPrintingView> printingView(AdoptNS, [[WKPrintingView alloc] initWithFrameProxy:toImpl(frameRef)]);
1540         // NSPrintOperation takes ownership of the view.
1541         NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView:printingView.get()];
1542         [printOperation setCanSpawnSeparateThread:YES];
1543         printingView->_printOperation = printOperation;
1544         return printOperation;
1545     }
1546 }
1547
1548 @end
1549
1550 @implementation WKView (Internal)
1551
1552 - (PassOwnPtr<WebKit::DrawingAreaProxy>)_createDrawingAreaProxy
1553 {
1554     if (useNewDrawingArea())
1555         return DrawingAreaProxyImpl::create(_data->_page.get());
1556
1557     return ChunkedUpdateDrawingAreaProxy::create(self, _data->_page.get());
1558 }
1559
1560 - (BOOL)_isFocused
1561 {
1562     if (_data->_inBecomeFirstResponder)
1563         return YES;
1564     if (_data->_inResignFirstResponder)
1565         return NO;
1566     return [[self window] firstResponder] == self;
1567 }
1568
1569 - (void)_processDidCrash
1570 {
1571     [self setNeedsDisplay:YES];
1572     [self _setRemoteAccessibilityWindow:nil];
1573 }
1574
1575 - (void)_pageClosed
1576 {
1577     // When the page closes, the references the accessibility child has to the window 
1578     // need to be removed, otherwise it can leak.
1579     [self _setRemoteAccessibilityWindow:nil];
1580 }
1581
1582 - (void)_didRelaunchProcess
1583 {
1584     [self setNeedsDisplay:YES];
1585 }
1586
1587 - (void)_takeFocus:(BOOL)forward
1588 {
1589     if (forward)
1590         [[self window] selectKeyViewFollowingView:self];
1591     else
1592         [[self window] selectKeyViewPrecedingView:self];
1593 }
1594
1595 - (void)_setCursor:(NSCursor *)cursor
1596 {
1597     if ([NSCursor currentCursor] == cursor)
1598         return;
1599     [cursor set];
1600 }
1601
1602 - (void)_setUserInterfaceItemState:(NSString *)commandName enabled:(BOOL)isEnabled state:(int)newState
1603 {
1604     ValidationVector items = _data->_validationMap.take(commandName);
1605     size_t size = items.size();
1606     for (size_t i = 0; i < size; ++i) {
1607         ValidationItem item = items[i].get();
1608         [menuItem(item) setState:newState];
1609         [menuItem(item) setEnabled:isEnabled];
1610         [toolbarItem(item) setEnabled:isEnabled];
1611         // FIXME <rdar://problem/8803392>: If the item is neither a menu nor toolbar item, it will be left enabled.
1612     }
1613 }
1614
1615 - (void)_setEventBeingResent:(NSEvent *)event
1616 {
1617     _data->_keyDownEventBeingResent = [event retain];
1618 }
1619
1620 - (Vector<KeypressCommand>&)_interceptKeyEvent:(NSEvent *)theEvent 
1621 {
1622     _data->_commandsList.clear();
1623     // interpretKeyEvents will trigger one or more calls to doCommandBySelector or setText
1624     // that will populate the commandsList vector.
1625     [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
1626     return _data->_commandsList;
1627 }
1628
1629 - (void)_getTextInputState:(unsigned)start selectionEnd:(unsigned)end underlines:(Vector<WebCore::CompositionUnderline>&)lines
1630 {
1631     start = _data->_selectionStart;
1632     end = _data->_selectionEnd;
1633     lines = _data->_underlines;
1634 }
1635
1636 - (NSRect)_convertToDeviceSpace:(NSRect)rect
1637 {
1638     return toDeviceSpace(rect, [self window]);
1639 }
1640
1641 - (NSRect)_convertToUserSpace:(NSRect)rect
1642 {
1643     return toUserSpace(rect, [self window]);
1644 }
1645
1646 // Any non-zero value will do, but using something recognizable might help us debug some day.
1647 #define TRACKING_RECT_TAG 0xBADFACE
1648
1649 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
1650 {
1651     ASSERT(_data->_trackingRectOwner == nil);
1652     _data->_trackingRectOwner = owner;
1653     _data->_trackingRectUserData = data;
1654     return TRACKING_RECT_TAG;
1655 }
1656
1657 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
1658 {
1659     ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
1660     ASSERT(_data->_trackingRectOwner == nil);
1661     _data->_trackingRectOwner = owner;
1662     _data->_trackingRectUserData = data;
1663     return TRACKING_RECT_TAG;
1664 }
1665
1666 - (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count
1667 {
1668     ASSERT(count == 1);
1669     ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
1670     ASSERT(_data->_trackingRectOwner == nil);
1671     _data->_trackingRectOwner = owner;
1672     _data->_trackingRectUserData = userDataList[0];
1673     trackingNums[0] = TRACKING_RECT_TAG;
1674 }
1675
1676 - (void)removeTrackingRect:(NSTrackingRectTag)tag
1677 {
1678     if (tag == 0)
1679         return;
1680     
1681     if (_data && (tag == TRACKING_RECT_TAG)) {
1682         _data->_trackingRectOwner = nil;
1683         return;
1684     }
1685     
1686     if (_data && (tag == _data->_lastToolTipTag)) {
1687         [super removeTrackingRect:tag];
1688         _data->_lastToolTipTag = 0;
1689         return;
1690     }
1691     
1692     // If any other tracking rect is being removed, we don't know how it was created
1693     // and it's possible there's a leak involved (see 3500217)
1694     ASSERT_NOT_REACHED();
1695 }
1696
1697 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count
1698 {
1699     int i;
1700     for (i = 0; i < count; ++i) {
1701         int tag = tags[i];
1702         if (tag == 0)
1703             continue;
1704         ASSERT(tag == TRACKING_RECT_TAG);
1705         if (_data != nil) {
1706             _data->_trackingRectOwner = nil;
1707         }
1708     }
1709 }
1710
1711 - (void)_sendToolTipMouseExited
1712 {
1713     // Nothing matters except window, trackingNumber, and userData.
1714     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
1715         location:NSMakePoint(0, 0)
1716         modifierFlags:0
1717         timestamp:0
1718         windowNumber:[[self window] windowNumber]
1719         context:NULL
1720         eventNumber:0
1721         trackingNumber:TRACKING_RECT_TAG
1722         userData:_data->_trackingRectUserData];
1723     [_data->_trackingRectOwner mouseExited:fakeEvent];
1724 }
1725
1726 - (void)_sendToolTipMouseEntered
1727 {
1728     // Nothing matters except window, trackingNumber, and userData.
1729     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
1730         location:NSMakePoint(0, 0)
1731         modifierFlags:0
1732         timestamp:0
1733         windowNumber:[[self window] windowNumber]
1734         context:NULL
1735         eventNumber:0
1736         trackingNumber:TRACKING_RECT_TAG
1737         userData:_data->_trackingRectUserData];
1738     [_data->_trackingRectOwner mouseEntered:fakeEvent];
1739 }
1740
1741 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
1742 {
1743     return nsStringFromWebCoreString(_data->_page->toolTip());
1744 }
1745
1746 - (void)_toolTipChangedFrom:(NSString *)oldToolTip to:(NSString *)newToolTip
1747 {
1748     if (oldToolTip)
1749         [self _sendToolTipMouseExited];
1750
1751     if (newToolTip && [newToolTip length] > 0) {
1752         // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
1753         [self removeAllToolTips];
1754         NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
1755         _data->_lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL];
1756         [self _sendToolTipMouseEntered];
1757     }
1758 }
1759
1760 - (void)_setFindIndicator:(PassRefPtr<FindIndicator>)findIndicator fadeOut:(BOOL)fadeOut
1761 {
1762     if (!findIndicator) {
1763         _data->_findIndicatorWindow = 0;
1764         return;
1765     }
1766
1767     if (!_data->_findIndicatorWindow)
1768         _data->_findIndicatorWindow = FindIndicatorWindow::create(self);
1769
1770     _data->_findIndicatorWindow->setFindIndicator(findIndicator, fadeOut);
1771 }
1772
1773 #if USE(ACCELERATED_COMPOSITING)
1774 - (void)_startAcceleratedCompositing:(CALayer *)rootLayer
1775 {
1776     if (!_data->_oldLayerHostingView) {
1777         NSView *hostingView = [[NSView alloc] initWithFrame:[self bounds]];
1778 #if !defined(BUILDING_ON_LEOPARD)
1779         [hostingView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
1780 #endif
1781         
1782         [self addSubview:hostingView];
1783         [hostingView release];
1784         _data->_oldLayerHostingView = hostingView;
1785     }
1786
1787     // Make a container layer, which will get sized/positioned by AppKit and CA.
1788     CALayer *viewLayer = [CALayer layer];
1789
1790 #ifndef NDEBUG
1791     [viewLayer setName:@"hosting layer"];
1792 #endif
1793
1794 #if defined(BUILDING_ON_LEOPARD)
1795     // Turn off default animations.
1796     NSNull *nullValue = [NSNull null];
1797     NSDictionary *actions = [NSDictionary dictionaryWithObjectsAndKeys:
1798                              nullValue, @"anchorPoint",
1799                              nullValue, @"bounds",
1800                              nullValue, @"contents",
1801                              nullValue, @"contentsRect",
1802                              nullValue, @"opacity",
1803                              nullValue, @"position",
1804                              nullValue, @"sublayerTransform",
1805                              nullValue, @"sublayers",
1806                              nullValue, @"transform",
1807                              nil];
1808     [viewLayer setStyle:[NSDictionary dictionaryWithObject:actions forKey:@"actions"]];
1809 #endif
1810
1811 #if !defined(BUILDING_ON_LEOPARD)
1812     // If we aren't in the window yet, we'll use the screen's scale factor now, and reset the scale 
1813     // via -viewDidMoveToWindow.
1814     CGFloat scaleFactor = [self window] ? [[self window] userSpaceScaleFactor] : [[NSScreen mainScreen] userSpaceScaleFactor];
1815     [viewLayer setTransform:CATransform3DMakeScale(scaleFactor, scaleFactor, 1)];
1816 #endif
1817
1818     [_data->_oldLayerHostingView setLayer:viewLayer];
1819     [_data->_oldLayerHostingView setWantsLayer:YES];
1820     
1821     // Parent our root layer in the container layer
1822     [viewLayer addSublayer:rootLayer];
1823 }
1824
1825 - (void)_stopAcceleratedCompositing
1826 {
1827     if (_data->_oldLayerHostingView) {
1828         [_data->_oldLayerHostingView setLayer:nil];
1829         [_data->_oldLayerHostingView setWantsLayer:NO];
1830         [_data->_oldLayerHostingView removeFromSuperview];
1831         _data->_oldLayerHostingView = nil;
1832     }
1833 }
1834
1835 - (void)_switchToDrawingAreaTypeIfNecessary:(DrawingAreaInfo::Type)type
1836 {
1837     DrawingAreaInfo::Type existingDrawingAreaType = _data->_page->drawingArea() ? _data->_page->drawingArea()->info().type : DrawingAreaInfo::None;
1838     if (existingDrawingAreaType == type)
1839         return;
1840
1841     OwnPtr<DrawingAreaProxy> newDrawingArea;
1842     switch (type) {
1843         case DrawingAreaInfo::Impl:
1844         case DrawingAreaInfo::None:
1845             break;
1846         case DrawingAreaInfo::ChunkedUpdate: {
1847             newDrawingArea = ChunkedUpdateDrawingAreaProxy::create(self, _data->_page.get());
1848             break;
1849         }
1850         case DrawingAreaInfo::LayerBacked: {
1851             newDrawingArea = LayerBackedDrawingAreaProxy::create(self, _data->_page.get());
1852             break;
1853         }
1854     }
1855
1856     newDrawingArea->setSize(IntSize([self frame].size));
1857
1858     _data->_page->drawingArea()->detachCompositingContext();
1859     _data->_page->setDrawingArea(newDrawingArea.release());
1860 }
1861
1862 - (void)_enterAcceleratedCompositingMode:(const LayerTreeContext&)layerTreeContext
1863 {
1864     ASSERT(!_data->_layerHostingView);
1865
1866     // Create an NSView that will host our layer tree.
1867     _data->_layerHostingView.adoptNS([[NSView alloc] initWithFrame:[self bounds]]);
1868     [_data->_layerHostingView.get() setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
1869     [self addSubview:_data->_layerHostingView.get()];
1870
1871     // Create a root layer that will back the NSView.
1872     CALayer *rootLayer = [CALayer layer];
1873 #ifndef NDEBUG
1874     [rootLayer setName:@"Hosting root layer"];
1875 #endif
1876
1877     [_data->_layerHostingView.get() setLayer:rootLayer];
1878     [_data->_layerHostingView.get() setWantsLayer:YES];
1879 }
1880
1881 - (void)_exitAcceleratedCompositingMode
1882 {
1883     ASSERT(_data->_layerHostingView);
1884
1885     [_data->_layerHostingView.get() setLayer:nil];
1886     [_data->_layerHostingView.get() setWantsLayer:NO];
1887     [_data->_layerHostingView.get() removeFromSuperview];
1888     
1889     _data->_layerHostingView = nullptr;
1890 }
1891
1892 - (void)_pageDidEnterAcceleratedCompositing
1893 {
1894     [self _switchToDrawingAreaTypeIfNecessary:DrawingAreaInfo::LayerBacked];
1895 }
1896
1897 - (void)_pageDidLeaveAcceleratedCompositing
1898 {
1899     // FIXME: we may want to avoid flipping back to the non-layer-backed drawing area until the next page load, to avoid thrashing.
1900     [self _switchToDrawingAreaTypeIfNecessary:DrawingAreaInfo::ChunkedUpdate];
1901 }
1902 #endif // USE(ACCELERATED_COMPOSITING)
1903
1904 - (void)_setAccessibilityChildToken:(NSData *)data
1905 {
1906 #if !defined(BUILDING_ON_SNOW_LEOPARD)
1907     _data->_remoteAccessibilityChild = WKAXRemoteElementForToken((CFDataRef)data);
1908     [self _setRemoteAccessibilityWindow:[self window]];
1909 #endif
1910 }
1911
1912 - (void)_setComplexTextInputEnabled:(BOOL)complexTextInputEnabled pluginComplexTextInputIdentifier:(uint64_t)pluginComplexTextInputIdentifier
1913 {
1914     BOOL inputSourceChanged = _data->_pluginComplexTextInputIdentifier;
1915
1916     if (complexTextInputEnabled) {
1917         // Check if we're already allowing text input for this plug-in.
1918         if (pluginComplexTextInputIdentifier == _data->_pluginComplexTextInputIdentifier)
1919             return;
1920
1921         _data->_pluginComplexTextInputIdentifier = pluginComplexTextInputIdentifier;
1922
1923     } else {
1924         // Check if we got a request to disable complex text input for a plug-in that is not the current plug-in.
1925         if (pluginComplexTextInputIdentifier != _data->_pluginComplexTextInputIdentifier)
1926             return;
1927
1928         _data->_pluginComplexTextInputIdentifier = 0;
1929     }
1930
1931     if (inputSourceChanged) {
1932         // Inform the out of line window that the input source changed.
1933         [[WKTextInputWindowController sharedTextInputWindowController] keyboardInputSourceChanged];
1934     }
1935 }
1936
1937 - (void)_setPageHasCustomRepresentation:(BOOL)pageHasCustomRepresentation
1938 {
1939     _data->_pdfViewController = nullptr;
1940
1941     if (pageHasCustomRepresentation)
1942         _data->_pdfViewController = PDFViewController::create(self);
1943 }
1944
1945 - (void)_didFinishLoadingDataForCustomRepresentation:(const CoreIPC::DataReference&)dataReference
1946 {
1947     ASSERT(_data->_pdfViewController);
1948
1949     _data->_pdfViewController->setPDFDocumentData(_data->_page->mainFrame()->mimeType(), dataReference);
1950 }
1951
1952 - (double)_customRepresentationZoomFactor
1953 {
1954     if (!_data->_pdfViewController)
1955         return 1;
1956
1957     return _data->_pdfViewController->zoomFactor();
1958 }
1959
1960 - (void)_setCustomRepresentationZoomFactor:(double)zoomFactor
1961 {
1962     if (!_data->_pdfViewController)
1963         return;
1964
1965     _data->_pdfViewController->setZoomFactor(zoomFactor);
1966 }
1967
1968 - (void)_setDragImage:(NSImage *)image at:(NSPoint)clientPoint linkDrag:(BOOL)linkDrag
1969 {
1970     // We need to prevent re-entering this call to avoid crashing in AppKit.
1971     // Given the asynchronous nature of WebKit2 this can now happen.
1972     if (_data->_dragHasStarted)
1973         return;
1974     
1975     _data->_dragHasStarted = YES;
1976     [super dragImage:image
1977                   at:clientPoint
1978               offset:NSZeroSize
1979                event:(linkDrag) ? [NSApp currentEvent] :_data->_mouseDownEvent
1980           pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard]
1981               source:self
1982            slideBack:YES];
1983     _data->_dragHasStarted = NO;
1984 }
1985
1986 - (void)_setDrawingAreaSize:(NSSize)size
1987 {
1988     if (!_data->_page->drawingArea())
1989         return;
1990     
1991     _data->_page->drawingArea()->setSize(IntSize(size));
1992 }
1993
1994 @end
1995
1996 @implementation WKView (Private)
1997
1998 - (void)disableFrameSizeUpdates
1999 {
2000     _frameSizeUpdatesDisabledCount++;
2001 }
2002
2003 - (void)enableFrameSizeUpdates
2004 {
2005     if (!_frameSizeUpdatesDisabledCount)
2006         return;
2007     
2008     if (!(--_frameSizeUpdatesDisabledCount))
2009         [self _setDrawingAreaSize:[self frame].size];
2010 }
2011
2012 - (BOOL)frameSizeUpdatesDisabled
2013 {
2014     return _frameSizeUpdatesDisabledCount > 0;
2015 }
2016
2017 @end
2018