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