f52645c66b8a15789bdaaf7e5a833d6389ae9476
[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 "WKView.h"
27
28 #import "ChunkedUpdateDrawingAreaProxy.h"
29 #import "DataReference.h"
30 #import "DrawingAreaProxyImpl.h"
31 #import "FindIndicator.h"
32 #import "FindIndicatorWindow.h"
33 #import "LayerBackedDrawingAreaProxy.h"
34 #import "Logging.h"
35 #import "NativeWebKeyboardEvent.h"
36 #import "PDFViewController.h"
37 #import "PageClientImpl.h"
38 #import "RunLoop.h"
39 #import "TextChecker.h"
40 #import "TextCheckerState.h"
41 #import "WKAPICast.h"
42 #import "WKStringCF.h"
43 #import "WKTextInputWindowController.h"
44 #import "WebContext.h"
45 #import "WebEventFactory.h"
46 #import "WebPage.h"
47 #import "WebPageProxy.h"
48 #import "WebProcessManager.h"
49 #import "WebProcessProxy.h"
50 #import "WebSystemInterface.h"
51 #import <QuartzCore/QuartzCore.h>
52 #import <WebCore/ColorMac.h>
53 #import <WebCore/DragController.h>
54 #import <WebCore/DragData.h>
55 #import <WebCore/FloatRect.h>
56 #import <WebCore/IntRect.h>
57 #import <WebCore/KeyboardEvent.h>
58 #import <WebCore/PlatformMouseEvent.h>
59 #import <WebCore/PlatformScreen.h>
60 #import <WebKitSystemInterface.h>
61 #import <wtf/RefPtr.h>
62 #import <wtf/RetainPtr.h>
63
64 // FIXME (WebKit2) <rdar://problem/8728860> WebKit2 needs to be localized
65 #define UI_STRING(__str, __desc) [NSString stringWithUTF8String:__str]
66
67 @interface NSApplication (Details)
68 - (void)speakString:(NSString *)string;
69 @end
70
71 @interface NSWindow (Details)
72 - (NSRect)_growBoxRect;
73 - (BOOL)_updateGrowBoxForWindowFrameChange;
74 @end
75
76 extern "C" {
77     // Need to declare this attribute name because AppKit exports it but does not make it available in API or SPI headers.
78     // <rdar://problem/8631468> tracks the request to make it available. This code should be removed when the bug is closed.
79     extern NSString *NSTextInputReplacementRangeAttributeName;
80 }
81
82 using namespace WebKit;
83 using namespace WebCore;
84
85 namespace WebKit {
86
87 typedef id <NSValidatedUserInterfaceItem> ValidationItem;
88 typedef Vector<RetainPtr<ValidationItem> > ValidationVector;
89 typedef HashMap<String, ValidationVector> ValidationMap;
90
91 }
92
93 @interface WKViewData : NSObject {
94 @public
95     OwnPtr<PageClientImpl> _pageClient;
96     RefPtr<WebPageProxy> _page;
97
98     // For ToolTips.
99     NSToolTipTag _lastToolTipTag;
100     id _trackingRectOwner;
101     void* _trackingRectUserData;
102
103 #if USE(ACCELERATED_COMPOSITING)
104     NSView *_layerHostingView;
105 #endif
106
107     RetainPtr<id> _remoteAccessibilityChild;
108     
109     // For asynchronous validation.
110     ValidationMap _validationMap;
111
112     OwnPtr<PDFViewController> _pdfViewController;
113
114     OwnPtr<FindIndicatorWindow> _findIndicatorWindow;
115     // We keep here the event when resending it to
116     // the application to distinguish the case of a new event from one 
117     // that has been already sent to WebCore.
118     NSEvent *_keyDownEventBeingResent;
119     Vector<KeypressCommand> _commandsList;
120
121     // The identifier of the plug-in we want to send complex text input to, or 0 if there is none.
122     uint64_t _pluginComplexTextInputIdentifier;
123
124     Vector<CompositionUnderline> _underlines;
125     unsigned _selectionStart;
126     unsigned _selectionEnd;
127
128     bool _inBecomeFirstResponder;
129     bool _inResignFirstResponder;
130 }
131 @end
132
133 @implementation WKViewData
134 @end
135
136 @interface NSObject (NSTextInputContextDetails)
137 - (BOOL)wantsToHandleMouseEvents;
138 - (BOOL)handleMouseEvent:(NSEvent *)event;
139 @end
140
141 @implementation WKView
142
143 static bool useNewDrawingArea()
144 {
145     static bool useNewDrawingArea = getenv("USE_NEW_DRAWING_AREA");
146
147     return useNewDrawingArea;
148 }
149
150 - (id)initWithFrame:(NSRect)frame
151 {
152     return [self initWithFrame:frame contextRef:toAPI(WebContext::sharedProcessContext())];
153 }
154
155 - (id)initWithFrame:(NSRect)frame contextRef:(WKContextRef)contextRef
156 {   
157     return [self initWithFrame:frame contextRef:contextRef pageGroupRef:nil];
158 }
159
160 static NSString * const WebArchivePboardType = @"Apple Web Archive pasteboard type";
161 static NSString * const WebURLsWithTitlesPboardType = @"WebURLsWithTitlesPboardType";
162 static NSString * const WebURLPboardType = @"public.url";
163 static NSString * const WebURLNamePboardType = @"public.url-name";
164
165 - (void)_registerDraggedTypes
166 {
167     NSArray *editableTypes = [NSArray arrayWithObjects:WebArchivePboardType, NSHTMLPboardType, NSFilenamesPboardType, NSTIFFPboardType, NSPDFPboardType,
168 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD)
169         NSPICTPboardType,
170 #endif
171         NSURLPboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, NSColorPboardType, kUTTypePNG, nil];
172     NSArray *URLTypes = [NSArray arrayWithObjects:WebURLsWithTitlesPboardType, NSURLPboardType, WebURLPboardType,  WebURLNamePboardType, NSStringPboardType, NSFilenamesPboardType, nil];
173     NSMutableSet *types = [[NSMutableSet alloc] initWithArray:editableTypes];
174     [types addObjectsFromArray:URLTypes];
175     [self registerForDraggedTypes:[types allObjects]];
176     [types release];
177 }
178
179 - (id)initWithFrame:(NSRect)frame contextRef:(WKContextRef)contextRef pageGroupRef:(WKPageGroupRef)pageGroupRef
180 {
181     self = [super initWithFrame:frame];
182     if (!self)
183         return nil;
184
185     InitWebCoreSystemInterface();
186     RunLoop::initializeMainRunLoop();
187
188     NSTrackingArea *trackingArea = [[NSTrackingArea alloc] initWithRect:frame
189                                                                 options:(NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect)
190                                                                   owner:self
191                                                                userInfo:nil];
192     [self addTrackingArea:trackingArea];
193     [trackingArea release];
194
195     _data = [[WKViewData alloc] init];
196
197     _data->_pageClient = PageClientImpl::create(self);
198     _data->_page = toImpl(contextRef)->createWebPage(_data->_pageClient.get(), toImpl(pageGroupRef));
199     _data->_page->initializeWebPage();
200
201     [self _registerDraggedTypes];
202
203     WebContext::statistics().wkViewCount++;
204
205 #if !defined(BUILDING_ON_SNOW_LEOPARD)
206     NSData *remoteToken = (NSData *)WKAXRemoteTokenForElement(self);
207     CoreIPC::DataReference dataToken = CoreIPC::DataReference(reinterpret_cast<const uint8_t*>([remoteToken bytes]), [remoteToken length]);
208     _data->_page->sendAccessibilityPresenterToken(dataToken);
209 #endif
210     
211     return self;
212 }
213
214 - (void)dealloc
215 {
216     _data->_page->close();
217
218     [_data release];
219
220     WebContext::statistics().wkViewCount--;
221
222     [super dealloc];
223 }
224
225 - (WKPageRef)pageRef
226 {
227     return toAPI(_data->_page.get());
228 }
229
230 - (void)setDrawsBackground:(BOOL)drawsBackground
231 {
232     _data->_page->setDrawsBackground(drawsBackground);
233 }
234
235 - (BOOL)drawsBackground
236 {
237     return _data->_page->drawsBackground();
238 }
239
240 - (void)setDrawsTransparentBackground:(BOOL)drawsTransparentBackground
241 {
242     _data->_page->setDrawsTransparentBackground(drawsTransparentBackground);
243 }
244
245 - (BOOL)drawsTransparentBackground
246 {
247     return _data->_page->drawsTransparentBackground();
248 }
249
250 - (BOOL)acceptsFirstResponder
251 {
252     return YES;
253 }
254
255 - (BOOL)becomeFirstResponder
256 {
257     NSSelectionDirection direction = [[self window] keyViewSelectionDirection];
258
259     _data->_inBecomeFirstResponder = true;
260     _data->_page->viewStateDidChange(WebPageProxy::ViewIsFocused);
261     _data->_inBecomeFirstResponder = false;
262
263     if (direction != NSDirectSelection)
264         _data->_page->setInitialFocus(direction == NSSelectingNext);
265
266     return YES;
267 }
268
269 - (BOOL)resignFirstResponder
270 {
271     _data->_inResignFirstResponder = true;
272     _data->_page->viewStateDidChange(WebPageProxy::ViewIsFocused);
273     _data->_inResignFirstResponder = false;
274
275     return YES;
276 }
277
278 - (BOOL)isFlipped
279 {
280     return YES;
281 }
282
283 - (void)setFrameSize:(NSSize)size
284 {
285     [super setFrameSize:size];
286
287     if (!_data->_page->drawingArea())
288         return;
289     
290     _data->_page->drawingArea()->setSize(IntSize(size));
291 }
292
293 - (void)_updateWindowAndViewFrames
294 {
295     NSWindow *window = [self window];
296     ASSERT(window);
297     
298     NSRect windowFrameInScreenCoordinates = [window frame];
299     NSRect viewFrameInWindowCoordinates = [self convertRect:[self frame] toView:nil];
300     NSPoint accessibilityPosition = [[self accessibilityAttributeValue:NSAccessibilityPositionAttribute] pointValue];
301     
302     _data->_page->windowAndViewFramesChanged(enclosingIntRect(windowFrameInScreenCoordinates), enclosingIntRect(viewFrameInWindowCoordinates), IntPoint(accessibilityPosition));
303 }
304
305 - (void)renewGState
306 {
307     // Hide the find indicator.
308     _data->_findIndicatorWindow = nullptr;
309
310     // Update the view frame.
311     if ([self window])
312         [self _updateWindowAndViewFrames];
313
314     [super renewGState];
315 }
316
317 typedef HashMap<SEL, String> SelectorNameMap;
318
319 // Map selectors into Editor command names.
320 // This is not needed for any selectors that have the same name as the Editor command.
321 static const SelectorNameMap* createSelectorExceptionMap()
322 {
323     SelectorNameMap* map = new HashMap<SEL, String>;
324     
325     map->add(@selector(insertNewlineIgnoringFieldEditor:), "InsertNewline");
326     map->add(@selector(insertParagraphSeparator:), "InsertNewline");
327     map->add(@selector(insertTabIgnoringFieldEditor:), "InsertTab");
328     map->add(@selector(pageDown:), "MovePageDown");
329     map->add(@selector(pageDownAndModifySelection:), "MovePageDownAndModifySelection");
330     map->add(@selector(pageUp:), "MovePageUp");
331     map->add(@selector(pageUpAndModifySelection:), "MovePageUpAndModifySelection");
332     
333     return map;
334 }
335
336 static String commandNameForSelector(SEL selector)
337 {
338     // Check the exception map first.
339     static const SelectorNameMap* exceptionMap = createSelectorExceptionMap();
340     SelectorNameMap::const_iterator it = exceptionMap->find(selector);
341     if (it != exceptionMap->end())
342         return it->second;
343     
344     // Remove the trailing colon.
345     // No need to capitalize the command name since Editor command names are
346     // not case sensitive.
347     const char* selectorName = sel_getName(selector);
348     size_t selectorNameLength = strlen(selectorName);
349     if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
350         return String();
351     return String(selectorName, selectorNameLength - 1);
352 }
353
354 // Editing commands
355
356 #define WEBCORE_COMMAND(command) - (void)command:(id)sender { _data->_page->executeEditCommand(commandNameForSelector(_cmd)); }
357
358 WEBCORE_COMMAND(copy)
359 WEBCORE_COMMAND(cut)
360 WEBCORE_COMMAND(paste)
361 WEBCORE_COMMAND(delete)
362 WEBCORE_COMMAND(pasteAsPlainText)
363 WEBCORE_COMMAND(selectAll)
364 WEBCORE_COMMAND(takeFindStringFromSelection)
365
366 #undef WEBCORE_COMMAND
367
368 // Menu items validation
369
370 static NSMenuItem *menuItem(id <NSValidatedUserInterfaceItem> item)
371 {
372     if (![(NSObject *)item isKindOfClass:[NSMenuItem class]])
373         return nil;
374     return (NSMenuItem *)item;
375 }
376
377 static NSToolbarItem *toolbarItem(id <NSValidatedUserInterfaceItem> item)
378 {
379     if (![(NSObject *)item isKindOfClass:[NSToolbarItem class]])
380         return nil;
381     return (NSToolbarItem *)item;
382 }
383
384 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
385 {
386     SEL action = [item action];
387
388     if (action == @selector(showGuessPanel:)) {
389         if (NSMenuItem *menuItem = ::menuItem(item)) {
390             BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible];
391             [menuItem setTitle:panelShowing
392                 ? UI_STRING("Hide Spelling and Grammar", "menu item title")
393                 : UI_STRING("Show Spelling and Grammar", "menu item title")];
394         }
395         return _data->_page->selectionState().isContentEditable;
396     }
397
398     if (action == @selector(checkSpelling:) || action == @selector(changeSpelling:))
399         return _data->_page->selectionState().isContentEditable;
400
401     if (action == @selector(toggleContinuousSpellChecking:)) {
402         bool enabled = TextChecker::isContinuousSpellCheckingAllowed();
403         bool checked = enabled && TextChecker::state().isContinuousSpellCheckingEnabled;
404         [menuItem(item) setState:checked ? NSOnState : NSOffState];
405         return enabled;
406     }
407
408     if (action == @selector(toggleGrammarChecking:)) {
409         bool checked = TextChecker::state().isGrammarCheckingEnabled;
410         [menuItem(item) setState:checked ? NSOnState : NSOffState];
411         return YES;
412     }
413
414     if (action == @selector(toggleAutomaticSpellingCorrection:)) {
415         bool checked = TextChecker::state().isAutomaticSpellingCorrectionEnabled;
416         [menuItem(item) setState:checked ? NSOnState : NSOffState];
417         return _data->_page->selectionState().isContentEditable;
418     }
419
420     if (action == @selector(orderFrontSubstitutionsPanel:)) {
421         if (NSMenuItem *menuItem = ::menuItem(item)) {
422             BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible];
423             [menuItem setTitle:panelShowing
424                 ? UI_STRING("Hide Substitutions", "menu item title")
425                 : UI_STRING("Show Substitutions", "menu item title")];
426         }
427         return _data->_page->selectionState().isContentEditable;
428     }
429
430     if (action == @selector(toggleSmartInsertDelete:)) {
431         bool checked = _data->_page->isSmartInsertDeleteEnabled();
432         [menuItem(item) setState:checked ? NSOnState : NSOffState];
433         return _data->_page->selectionState().isContentEditable;
434     }
435
436     if (action == @selector(toggleAutomaticQuoteSubstitution:)) {
437         bool checked = TextChecker::state().isAutomaticQuoteSubstitutionEnabled;
438         [menuItem(item) setState:checked ? NSOnState : NSOffState];
439         return _data->_page->selectionState().isContentEditable;
440     }
441
442     if (action == @selector(toggleAutomaticDashSubstitution:)) {
443         bool checked = TextChecker::state().isAutomaticDashSubstitutionEnabled;
444         [menuItem(item) setState:checked ? NSOnState : NSOffState];
445         return _data->_page->selectionState().isContentEditable;
446     }
447
448     if (action == @selector(toggleAutomaticLinkDetection:)) {
449         bool checked = TextChecker::state().isAutomaticLinkDetectionEnabled;
450         [menuItem(item) setState:checked ? NSOnState : NSOffState];
451         return _data->_page->selectionState().isContentEditable;
452     }
453
454     if (action == @selector(toggleAutomaticTextReplacement:)) {
455         bool checked = TextChecker::state().isAutomaticTextReplacementEnabled;
456         [menuItem(item) setState:checked ? NSOnState : NSOffState];
457         return _data->_page->selectionState().isContentEditable;
458     }
459
460     if (action == @selector(uppercaseWord:) || action == @selector(lowercaseWord:) || action == @selector(capitalizeWord:))
461         return _data->_page->selectionState().selectedRangeLength && _data->_page->selectionState().isContentEditable;
462     
463     if (action == @selector(stopSpeaking:))
464         return [NSApp isSpeaking];
465
466     // Next, handle editor commands. Start by returning YES for anything that is not an editor command.
467     // Returning YES is the default thing to do in an AppKit validate method for any selector that is not recognized.
468     String commandName = commandNameForSelector([item action]);
469     if (!Editor::commandIsSupportedFromMenuOrKeyBinding(commandName))
470         return YES;
471
472     // Add this item to the vector of items for a given command that are awaiting validation.
473     pair<ValidationMap::iterator, bool> addResult = _data->_validationMap.add(commandName, ValidationVector());
474     addResult.first->second.append(item);
475     if (addResult.second) {
476         // If we are not already awaiting validation for this command, start the asynchronous validation process.
477         // FIXME: Theoretically, there is a race here; when we get the answer it might be old, from a previous time
478         // we asked for the same command; there is no guarantee the answer is still valid.
479         // FIXME: The function called here should be renamed validateCommand because it is not specific to menu items.
480         _data->_page->validateMenuItem(commandName);
481     }
482
483     // Treat as enabled until we get the result back from the web process and _setUserInterfaceItemState is called.
484     // FIXME <rdar://problem/8803459>: This means disabled items will flash enabled at first for a moment.
485     // But returning NO here would be worse; that would make keyboard commands such as command-C fail.
486     return YES;
487 }
488
489 static void speakString(WKStringRef string, WKErrorRef error, void*)
490 {
491     if (error)
492         return;
493     if (!string)
494         return;
495
496     NSString *convertedString = toImpl(string)->string();
497     [NSApp speakString:convertedString];
498 }
499
500 - (IBAction)startSpeaking:(id)sender
501 {
502     _data->_page->getSelectionOrContentsAsString(StringCallback::create(0, speakString));
503 }
504
505 - (IBAction)stopSpeaking:(id)sender
506 {
507     [NSApp stopSpeaking:sender];
508 }
509
510 - (IBAction)showGuessPanel:(id)sender
511 {
512     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
513     if (!checker) {
514         LOG_ERROR("No NSSpellChecker");
515         return;
516     }
517     
518     NSPanel *spellingPanel = [checker spellingPanel];
519     if ([spellingPanel isVisible]) {
520         [spellingPanel orderOut:sender];
521         return;
522     }
523     
524     _data->_page->advanceToNextMisspelling(true);
525     [spellingPanel orderFront:sender];
526 }
527
528 - (IBAction)checkSpelling:(id)sender
529 {
530     _data->_page->advanceToNextMisspelling(false);
531 }
532
533 - (void)changeSpelling:(id)sender
534 {
535     NSString *word = [[sender selectedCell] stringValue];
536
537     _data->_page->changeSpellingToWord(word);
538 }
539
540 - (IBAction)toggleContinuousSpellChecking:(id)sender
541 {
542     bool spellCheckingEnabled = !TextChecker::state().isContinuousSpellCheckingEnabled;
543     TextChecker::setContinuousSpellCheckingEnabled(spellCheckingEnabled);
544
545     _data->_page->process()->updateTextCheckerState();
546
547     if (!spellCheckingEnabled)
548         _data->_page->unmarkAllMisspellings();
549 }
550
551 - (IBAction)toggleGrammarChecking:(id)sender
552 {
553     bool grammarCheckingEnabled = !TextChecker::state().isGrammarCheckingEnabled;
554     TextChecker::setGrammarCheckingEnabled(grammarCheckingEnabled);
555
556     _data->_page->process()->updateTextCheckerState();
557
558     if (!grammarCheckingEnabled)
559         _data->_page->unmarkAllBadGrammar();
560 }
561
562 - (IBAction)toggleAutomaticSpellingCorrection:(id)sender
563 {
564     TextChecker::setAutomaticSpellingCorrectionEnabled(!TextChecker::state().isAutomaticSpellingCorrectionEnabled);
565
566     _data->_page->process()->updateTextCheckerState();
567 }
568
569 - (void)orderFrontSubstitutionsPanel:(id)sender
570 {
571     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
572     if (!checker) {
573         LOG_ERROR("No NSSpellChecker");
574         return;
575     }
576     
577     NSPanel *substitutionsPanel = [checker substitutionsPanel];
578     if ([substitutionsPanel isVisible]) {
579         [substitutionsPanel orderOut:sender];
580         return;
581     }
582     [substitutionsPanel orderFront:sender];
583 }
584
585 - (IBAction)toggleSmartInsertDelete:(id)sender
586 {
587     _data->_page->setSmartInsertDeleteEnabled(!_data->_page->isSmartInsertDeleteEnabled());
588 }
589
590 - (BOOL)isAutomaticQuoteSubstitutionEnabled
591 {
592     return TextChecker::state().isAutomaticQuoteSubstitutionEnabled;
593 }
594
595 - (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag
596 {
597     if (static_cast<bool>(flag) == TextChecker::state().isAutomaticQuoteSubstitutionEnabled)
598         return;
599
600     TextChecker::setAutomaticQuoteSubstitutionEnabled(flag);
601     _data->_page->process()->updateTextCheckerState();
602 }
603
604 - (void)toggleAutomaticQuoteSubstitution:(id)sender
605 {
606     TextChecker::setAutomaticQuoteSubstitutionEnabled(!TextChecker::state().isAutomaticQuoteSubstitutionEnabled);
607     _data->_page->process()->updateTextCheckerState();
608 }
609
610 - (BOOL)isAutomaticDashSubstitutionEnabled
611 {
612     return TextChecker::state().isAutomaticDashSubstitutionEnabled;
613 }
614
615 - (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag
616 {
617     if (static_cast<bool>(flag) == TextChecker::state().isAutomaticDashSubstitutionEnabled)
618         return;
619
620     TextChecker::setAutomaticDashSubstitutionEnabled(flag);
621     _data->_page->process()->updateTextCheckerState();
622 }
623
624 - (void)toggleAutomaticDashSubstitution:(id)sender
625 {
626     TextChecker::setAutomaticDashSubstitutionEnabled(!TextChecker::state().isAutomaticDashSubstitutionEnabled);
627     _data->_page->process()->updateTextCheckerState();
628 }
629
630 - (BOOL)isAutomaticLinkDetectionEnabled
631 {
632     return TextChecker::state().isAutomaticLinkDetectionEnabled;
633 }
634
635 - (void)setAutomaticLinkDetectionEnabled:(BOOL)flag
636 {
637     if (static_cast<bool>(flag) == TextChecker::state().isAutomaticLinkDetectionEnabled)
638         return;
639
640     TextChecker::setAutomaticLinkDetectionEnabled(flag);
641     _data->_page->process()->updateTextCheckerState();
642 }
643
644 - (void)toggleAutomaticLinkDetection:(id)sender
645 {
646     TextChecker::setAutomaticLinkDetectionEnabled(!TextChecker::state().isAutomaticLinkDetectionEnabled);
647     _data->_page->process()->updateTextCheckerState();
648 }
649
650 - (BOOL)isAutomaticTextReplacementEnabled
651 {
652     return TextChecker::state().isAutomaticTextReplacementEnabled;
653 }
654
655 - (void)setAutomaticTextReplacementEnabled:(BOOL)flag
656 {
657     if (static_cast<bool>(flag) == TextChecker::state().isAutomaticTextReplacementEnabled)
658         return;
659
660     TextChecker::setAutomaticTextReplacementEnabled(flag);
661     _data->_page->process()->updateTextCheckerState();
662 }
663
664 - (void)toggleAutomaticTextReplacement:(id)sender
665 {
666     TextChecker::setAutomaticTextReplacementEnabled(!TextChecker::state().isAutomaticTextReplacementEnabled);
667     _data->_page->process()->updateTextCheckerState();
668 }
669
670 - (void)uppercaseWord:(id)sender
671 {
672     _data->_page->uppercaseWord();
673 }
674
675 - (void)lowercaseWord:(id)sender
676 {
677     _data->_page->lowercaseWord();
678 }
679
680 - (void)capitalizeWord:(id)sender
681 {
682     _data->_page->capitalizeWord();
683 }
684
685 // Events
686
687 // Override this so that AppKit will send us arrow keys as key down events so we can
688 // support them via the key bindings mechanism.
689 - (BOOL)_wantsKeyDownForEvent:(NSEvent *)event
690 {
691     return YES;
692 }
693
694 #define EVENT_HANDLER(Selector, Type) \
695     - (void)Selector:(NSEvent *)theEvent \
696     { \
697         Web##Type##Event webEvent = WebEventFactory::createWeb##Type##Event(theEvent, self); \
698         _data->_page->handle##Type##Event(webEvent); \
699     }
700
701 EVENT_HANDLER(mouseEntered, Mouse)
702 EVENT_HANDLER(mouseExited, Mouse)
703 EVENT_HANDLER(mouseMoved, Mouse)
704 EVENT_HANDLER(otherMouseDown, Mouse)
705 EVENT_HANDLER(otherMouseDragged, Mouse)
706 EVENT_HANDLER(otherMouseMoved, Mouse)
707 EVENT_HANDLER(otherMouseUp, Mouse)
708 EVENT_HANDLER(rightMouseDown, Mouse)
709 EVENT_HANDLER(rightMouseDragged, Mouse)
710 EVENT_HANDLER(rightMouseMoved, Mouse)
711 EVENT_HANDLER(rightMouseUp, Mouse)
712 EVENT_HANDLER(scrollWheel, Wheel)
713
714 #undef EVENT_HANDLER
715
716 #define MOUSE_EVENT_HANDLER(Selector) \
717     - (void)Selector:(NSEvent *)theEvent \
718     { \
719         NSInputManager *currentInputManager = [NSInputManager currentInputManager]; \
720         if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:theEvent]) \
721             return; \
722         WebMouseEvent webEvent = WebEventFactory::createWebMouseEvent(theEvent, self); \
723         _data->_page->handleMouseEvent(webEvent); \
724     }
725
726 MOUSE_EVENT_HANDLER(mouseDown)
727 MOUSE_EVENT_HANDLER(mouseDragged)
728 MOUSE_EVENT_HANDLER(mouseUp)
729
730 #undef MOUSE_EVENT_HANDLER
731
732 - (void)doCommandBySelector:(SEL)selector
733 {
734     if (selector != @selector(noop:))
735         _data->_commandsList.append(KeypressCommand(commandNameForSelector(selector)));
736 }
737
738 - (void)insertText:(id)string
739 {
740     BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; // Otherwise, NSString
741     
742     LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string);
743     NSString *text;
744     bool isFromInputMethod = _data->_page->selectionState().hasComposition;
745
746     if (isAttributedString) {
747         text = [string string];
748         // We deal with the NSTextInputReplacementRangeAttributeName attribute from NSAttributedString here
749         // simply because it is used by at least one Input Method -- it corresonds to the kEventParamTextInputSendReplaceRange
750         // event in TSM.  This behaviour matches that of -[WebHTMLView setMarkedText:selectedRange:] when it receives an
751         // NSAttributedString
752         NSString *rangeString = [string attribute:NSTextInputReplacementRangeAttributeName atIndex:0 longestEffectiveRange:NULL inRange:NSMakeRange(0, [text length])];
753         LOG(TextInput, "ReplacementRange: %@", rangeString);
754         if (rangeString)
755             isFromInputMethod = YES;
756     } else
757         text = string;
758     
759     String eventText = text;
760     
761     if (!isFromInputMethod)
762         _data->_commandsList.append(KeypressCommand("insertText", text));
763     else {
764         eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore
765         _data->_commandsList.append(KeypressCommand("insertText", eventText));
766     }
767 }
768
769 - (BOOL)_handleStyleKeyEquivalent:(NSEvent *)event
770 {
771     if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) != NSCommandKeyMask)
772         return NO;
773     
774     // Here we special case cmd+b and cmd+i but not cmd+u, for historic reason.
775     // This should not be changed, since it could break some Mac applications that
776     // rely on this inherent behavior.
777     // See https://bugs.webkit.org/show_bug.cgi?id=24943
778     
779     NSString *string = [event characters];
780     if ([string caseInsensitiveCompare:@"b"] == NSOrderedSame) {
781         _data->_page->executeEditCommand("ToggleBold");
782         return YES;
783     }
784     if ([string caseInsensitiveCompare:@"i"] == NSOrderedSame) {
785         _data->_page->executeEditCommand("ToggleItalic");
786         return YES;
787     }
788     
789     return NO;
790 }
791
792 - (BOOL)performKeyEquivalent:(NSEvent *)event
793 {
794     // There's a chance that responding to this event will run a nested event loop, and
795     // fetching a new event might release the old one. Retaining and then autoreleasing
796     // the current event prevents that from causing a problem inside WebKit or AppKit code.
797     [[event retain] autorelease];
798     
799     BOOL eventWasSentToWebCore = (_data->_keyDownEventBeingResent == event);
800
801     // Pass key combos through WebCore if there is a key binding available for
802     // this event. This lets web pages have a crack at intercepting key-modified keypresses.
803     // But don't do it if we have already handled the event.
804     // Pressing Esc results in a fake event being sent - don't pass it to WebCore.
805     if (!eventWasSentToWebCore && event == [NSApp currentEvent] && self == [[self window] firstResponder]) {
806         [_data->_keyDownEventBeingResent release];
807         _data->_keyDownEventBeingResent = nil;
808         
809         _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(event, self));
810         return YES;
811     }
812     
813     return [self _handleStyleKeyEquivalent:event] || [super performKeyEquivalent:event];
814 }
815
816 - (void)_setEventBeingResent:(NSEvent *)event
817 {
818     _data->_keyDownEventBeingResent = [event retain];
819 }
820
821 - (Vector<KeypressCommand>&)_interceptKeyEvent:(NSEvent *)theEvent 
822 {
823     _data->_commandsList.clear();
824     // interpretKeyEvents will trigger one or more calls to doCommandBySelector or setText
825     // that will populate the commandsList vector.
826     [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
827     return _data->_commandsList;
828 }
829
830 - (void)_getTextInputState:(unsigned)start selectionEnd:(unsigned)end underlines:(Vector<WebCore::CompositionUnderline>&)lines
831 {
832     start = _data->_selectionStart;
833     end = _data->_selectionEnd;
834     lines = _data->_underlines;
835 }
836
837 - (void)keyUp:(NSEvent *)theEvent
838 {
839     _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, self));
840 }
841
842 - (void)keyDown:(NSEvent *)theEvent
843 {
844     if (_data->_pluginComplexTextInputIdentifier) {
845         // Try feeding the keyboard event directly to the plug-in.
846         NSString *string = nil;
847         if ([[WKTextInputWindowController sharedTextInputWindowController] interpretKeyEvent:theEvent string:&string]) {
848             if (string)
849                 _data->_page->sendComplexTextInputToPlugin(_data->_pluginComplexTextInputIdentifier, string);
850             return;
851         }
852     }
853
854     _data->_underlines.clear();
855     _data->_selectionStart = 0;
856     _data->_selectionEnd = 0;
857     // We could be receiving a key down from AppKit if we have re-sent an event
858     // that maps to an action that is currently unavailable (for example a copy when
859     // there is no range selection).
860     // If this is the case we should ignore the key down.
861     if (_data->_keyDownEventBeingResent == theEvent) {
862         [_data->_keyDownEventBeingResent release];
863         _data->_keyDownEventBeingResent = nil;
864         [super keyDown:theEvent];
865         return;
866     }
867     _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, self));
868 }
869
870 - (NSTextInputContext *)inputContext {
871     if (_data->_pluginComplexTextInputIdentifier)
872         return [[WKTextInputWindowController sharedTextInputWindowController] inputContext];
873
874     return [super inputContext];
875 }
876
877 - (NSRange)selectedRange
878 {
879     if (_data->_page->selectionState().isNone || !_data->_page->selectionState().isContentEditable)
880         return NSMakeRange(NSNotFound, 0);
881     
882     LOG(TextInput, "selectedRange -> (%u, %u)", _data->_page->selectionState().selectedRangeStart, _data->_page->selectionState().selectedRangeLength);
883     return NSMakeRange(_data->_page->selectionState().selectedRangeStart, _data->_page->selectionState().selectedRangeLength);
884 }
885
886 - (BOOL)hasMarkedText
887 {
888     LOG(TextInput, "hasMarkedText -> %u", _data->_page->selectionState().hasComposition);
889     return _data->_page->selectionState().hasComposition;
890 }
891
892 - (void)unmarkText
893 {
894     LOG(TextInput, "unmarkText");
895     
896     _data->_commandsList.append(KeypressCommand("unmarkText"));
897 }
898
899 - (NSArray *)validAttributesForMarkedText
900 {
901     static NSArray *validAttributes;
902     if (!validAttributes) {
903         validAttributes = [[NSArray alloc] initWithObjects:
904                            NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName,
905                            NSMarkedClauseSegmentAttributeName, NSTextInputReplacementRangeAttributeName, nil];
906         // NSText also supports the following attributes, but it's
907         // hard to tell which are really required for text input to
908         // work well; I have not seen any input method make use of them yet.
909         //     NSFontAttributeName, NSForegroundColorAttributeName,
910         //     NSBackgroundColorAttributeName, NSLanguageAttributeName.
911         CFRetain(validAttributes);
912     }
913     LOG(TextInput, "validAttributesForMarkedText -> (...)");
914     return validAttributes;
915 }
916
917 static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnderline>& result)
918 {
919     int length = [[string string] length];
920     
921     int i = 0;
922     while (i < length) {
923         NSRange range;
924         NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&range inRange:NSMakeRange(i, length - i)];
925         
926         if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
927             Color color = Color::black;
928             if (NSColor *colorAttr = [attrs objectForKey:NSUnderlineColorAttributeName])
929                 color = colorFromNSColor([colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
930             result.append(CompositionUnderline(range.location, NSMaxRange(range), color, [style intValue] > 1));
931         }
932         
933         i = range.location + range.length;
934     }
935 }
936
937 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange
938 {
939     BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; // Otherwise, NSString
940     
941     LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u)", isAttributedString ? [string string] : string, newSelRange.location, newSelRange.length);
942     
943     NSString *text = string;
944     
945     if (isAttributedString) {
946         text = [string string];
947         extractUnderlines(string, _data->_underlines);
948     }
949     
950     _data->_commandsList.append(KeypressCommand("setMarkedText", text));
951     _data->_selectionStart = newSelRange.location;
952     _data->_selectionEnd = NSMaxRange(newSelRange);
953 }
954
955 - (NSRange)markedRange
956 {
957     uint64_t location;
958     uint64_t length;
959
960     _data->_page->getMarkedRange(location, length);
961     LOG(TextInput, "markedRange -> (%u, %u)", location, length);
962     return NSMakeRange(location, length);
963 }
964
965 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)nsRange
966 {
967     // This is not implemented for now. Need to figure out how to serialize the attributed string across processes.
968     LOG(TextInput, "attributedSubstringFromRange");
969     return nil;
970 }
971
972 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
973 {
974     NSWindow *window = [self window];
975     
976     if (window)
977         thePoint = [window convertScreenToBase:thePoint];
978     thePoint = [self convertPoint:thePoint fromView:nil];  // the point is relative to the main frame 
979     
980     uint64_t result = _data->_page->characterIndexForPoint(IntPoint(thePoint));
981     LOG(TextInput, "characterIndexForPoint:(%f, %f) -> %u", thePoint.x, thePoint.y, result);
982     return result;
983 }
984
985 - (NSRect)firstRectForCharacterRange:(NSRange)theRange
986
987     // Just to match NSTextView's behavior. Regression tests cannot detect this;
988     // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682
989     // (type something; try ranges (1, -1) and (2, -1).
990     if ((theRange.location + theRange.length < theRange.location) && (theRange.location + theRange.length != 0))
991         theRange.length = 0;
992     
993     NSRect resultRect = _data->_page->firstRectForCharacterRange(theRange.location, theRange.length);
994     resultRect = [self convertRect:resultRect toView:nil];
995     
996     NSWindow *window = [self window];
997     if (window)
998         resultRect.origin = [window convertBaseToScreen:resultRect.origin];
999     
1000     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);
1001     return resultRect;
1002 }
1003
1004 - (DragApplicationFlags)applicationFlags:(id <NSDraggingInfo>)draggingInfo
1005 {
1006     uint32_t flags = 0;
1007     if ([NSApp modalWindow])
1008         flags = DragApplicationIsModal;
1009     if ([[self window] attachedSheet])
1010         flags |= DragApplicationHasAttachedSheet;
1011     if ([draggingInfo draggingSource] == self)
1012         flags |= DragApplicationIsSource;
1013     if ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)
1014         flags |= DragApplicationIsCopyKeyDown;
1015     return static_cast<DragApplicationFlags>(flags);
1016 }
1017
1018 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)draggingInfo
1019 {
1020     IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]);
1021     IntPoint global(globalPoint([draggingInfo draggingLocation], [self window]));
1022     DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]);
1023     
1024     _data->_page->performDragControllerAction(DragControllerActionEntered, &dragData, [[draggingInfo draggingPasteboard] name]);
1025     return NSDragOperationCopy;
1026 }
1027
1028 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)draggingInfo
1029 {
1030     IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]);
1031     IntPoint global(globalPoint([draggingInfo draggingLocation], [self window]));
1032     DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]);
1033     _data->_page->performDragControllerAction(DragControllerActionUpdated, &dragData, [[draggingInfo draggingPasteboard] name]);
1034     return _data->_page->dragOperation();
1035 }
1036
1037 - (void)draggingExited:(id <NSDraggingInfo>)draggingInfo
1038 {
1039     IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]);
1040     IntPoint global(globalPoint([draggingInfo draggingLocation], [self window]));
1041     DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]);
1042     _data->_page->performDragControllerAction(DragControllerActionExited, &dragData, [[draggingInfo draggingPasteboard] name]);
1043     _data->_page->resetDragOperation();
1044 }
1045
1046 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)draggingInfo
1047 {
1048     return YES;
1049 }
1050
1051 - (BOOL)performDragOperation:(id <NSDraggingInfo>)draggingInfo
1052 {
1053     IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]);
1054     IntPoint global(globalPoint([draggingInfo draggingLocation], [self window]));
1055     DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]);
1056     _data->_page->performDragControllerAction(DragControllerActionPerformDrag, &dragData, [[draggingInfo draggingPasteboard] name]);
1057     return YES;
1058 }
1059
1060 - (void)_updateWindowVisibility
1061 {
1062     _data->_page->updateWindowIsVisible(![[self window] isMiniaturized]);
1063 }
1064
1065 - (BOOL)_ownsWindowGrowBox
1066 {
1067     NSWindow* window = [self window];
1068     if (!window)
1069         return NO;
1070
1071     NSView *superview = [self superview];
1072     if (!superview)
1073         return NO;
1074
1075     NSRect growBoxRect = [window _growBoxRect];
1076     if (NSIsEmptyRect(growBoxRect))
1077         return NO;
1078
1079     NSRect visibleRect = [self visibleRect];
1080     if (NSIsEmptyRect(visibleRect))
1081         return NO;
1082
1083     NSRect visibleRectInWindowCoords = [self convertRect:visibleRect toView:nil];
1084     if (!NSIntersectsRect(growBoxRect, visibleRectInWindowCoords))
1085         return NO;
1086
1087     return YES;
1088 }
1089
1090 - (BOOL)_updateGrowBoxForWindowFrameChange
1091 {
1092     // Temporarily enable the resize indicator to make a the _ownsWindowGrowBox calculation work.
1093     BOOL wasShowingIndicator = [[self window] showsResizeIndicator];
1094     [[self window] setShowsResizeIndicator:YES];
1095
1096     BOOL ownsGrowBox = [self _ownsWindowGrowBox];
1097     _data->_page->setWindowResizerSize(ownsGrowBox ? enclosingIntRect([[self window] _growBoxRect]).size() : IntSize());
1098     
1099     // Once WebCore can draw the window resizer, this should read:
1100     // if (wasShowingIndicator)
1101     //     [[self window] setShowsResizeIndicator:!ownsGrowBox];
1102     [[self window] setShowsResizeIndicator:wasShowingIndicator];
1103
1104     return ownsGrowBox;
1105 }
1106
1107 - (void)addWindowObserversForWindow:(NSWindow *)window
1108 {
1109     if (window) {
1110         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidBecomeKey:)
1111                                                      name:NSWindowDidBecomeKeyNotification object:nil];
1112         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidResignKey:)
1113                                                      name:NSWindowDidResignKeyNotification object:nil];
1114         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidMiniaturize:) 
1115                                                      name:NSWindowDidMiniaturizeNotification object:window];
1116         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidDeminiaturize:)
1117                                                      name:NSWindowDidDeminiaturizeNotification object:window];
1118         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowFrameDidChange:)
1119                                                      name:NSWindowDidMoveNotification object:window];
1120         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowFrameDidChange:) 
1121                                                      name:NSWindowDidResizeNotification object:window];
1122     }
1123 }
1124
1125 - (void)removeWindowObservers
1126 {
1127     NSWindow *window = [self window];
1128     if (!window)
1129         return;
1130
1131     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil];
1132     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidResignKeyNotification object:nil];
1133     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidMiniaturizeNotification object:window];
1134     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window];
1135     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidMoveNotification object:window];
1136     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidResizeNotification object:window];
1137 }
1138
1139 - (void)viewWillMoveToWindow:(NSWindow *)window
1140 {
1141     if (window != [self window]) {
1142         [self removeWindowObservers];
1143         [self addWindowObserversForWindow:window];
1144     }
1145 }
1146
1147 - (void)viewDidMoveToWindow
1148 {
1149     // We want to make sure to update the active state while hidden, so if the view is about to become visible, we
1150     // update the active state first and then make it visible. If the view is about to be hidden, we hide it first and then
1151     // update the active state.
1152     if ([self window]) {
1153         _data->_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive);
1154         _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible | WebPageProxy::ViewIsInWindow);
1155         [self _updateWindowVisibility];
1156         [self _updateWindowAndViewFrames];
1157     } else {
1158         _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible);
1159         _data->_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive | WebPageProxy::ViewIsInWindow);
1160     }
1161
1162 }
1163
1164 - (void)_windowDidBecomeKey:(NSNotification *)notification
1165 {
1166     NSWindow *keyWindow = [notification object];
1167     if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet])
1168         _data->_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive);
1169 }
1170
1171 - (void)_windowDidResignKey:(NSNotification *)notification
1172 {
1173     NSWindow *formerKeyWindow = [notification object];
1174     if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet])
1175         _data->_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive);
1176 }
1177
1178 - (void)_windowDidMiniaturize:(NSNotification *)notification
1179 {
1180     [self _updateWindowVisibility];
1181 }
1182
1183 - (void)_windowDidDeminiaturize:(NSNotification *)notification
1184 {
1185     [self _updateWindowVisibility];
1186 }
1187
1188 - (void)_windowFrameDidChange:(NSNotification *)notification
1189 {
1190     [self _updateWindowAndViewFrames];
1191 }
1192
1193 - (void)drawRect:(NSRect)rect
1194 {
1195     if (useNewDrawingArea()) {
1196         if (DrawingAreaProxyImpl* drawingArea = static_cast<DrawingAreaProxyImpl*>(_data->_page->drawingArea())) {
1197             CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]);
1198             drawingArea->paint(context, enclosingIntRect(rect));
1199         } else if (_data->_page->drawsBackground()) {
1200             [_data->_page->drawsTransparentBackground() ? [NSColor clearColor] : [NSColor whiteColor] set];
1201             NSRectFill(rect);
1202         }
1203
1204         _data->_page->didDraw();
1205         return;
1206     }
1207
1208     if (_data->_page->isValid() && _data->_page->drawingArea()) {
1209         CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]);
1210         _data->_page->drawingArea()->paint(IntRect(rect), context);
1211         _data->_page->didDraw();
1212     } else if (_data->_page->drawsBackground()) {
1213         [_data->_page->drawsTransparentBackground() ? [NSColor clearColor] : [NSColor whiteColor] set];
1214         NSRectFill(rect);
1215     }
1216 }
1217
1218 - (BOOL)isOpaque
1219 {
1220     return _data->_page->drawsBackground();
1221 }
1222
1223 - (void)viewDidHide
1224 {
1225     _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible);
1226 }
1227
1228 - (void)viewDidUnhide
1229 {
1230     _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible);
1231 }
1232
1233 - (void)_setAccessibilityChildToken:(NSData *)data
1234 {
1235 #if !defined(BUILDING_ON_SNOW_LEOPARD)
1236     _data->_remoteAccessibilityChild = WKAXRemoteElementForToken((CFDataRef)data);
1237     WKAXInitializeRemoteElementWithWindow(_data->_remoteAccessibilityChild.get(), [self window]);
1238 #endif
1239 }
1240
1241 - (BOOL)accessibilityIsIgnored
1242 {
1243     return NO;
1244 }
1245
1246 - (id)accessibilityHitTest:(NSPoint)point
1247 {
1248     return _data->_remoteAccessibilityChild.get();
1249 }
1250
1251 - (id)accessibilityAttributeValue:(NSString*)attribute
1252 {
1253     if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
1254         if (!_data->_remoteAccessibilityChild)
1255             return nil;
1256         return [NSArray arrayWithObject:_data->_remoteAccessibilityChild.get()];
1257     }
1258     if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
1259         return NSAccessibilityGroupRole;
1260     if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute])
1261         return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, nil);
1262     if ([attribute isEqualToString:NSAccessibilityParentAttribute])
1263         return NSAccessibilityUnignoredAncestor([self superview]);
1264     if ([attribute isEqualToString:NSAccessibilityEnabledAttribute])
1265         return [NSNumber numberWithBool:YES];
1266     
1267     return [super accessibilityAttributeValue:attribute];
1268 }
1269
1270 - (NSView *)hitTest:(NSPoint)point
1271 {
1272     NSView *hitView = [super hitTest:point];
1273 #if USE(ACCELERATED_COMPOSITING)
1274     if (hitView && _data && hitView == _data->_layerHostingView)
1275         hitView = self;
1276 #endif
1277     return hitView;
1278 }
1279
1280 - (NSInteger)conversationIdentifier
1281 {
1282     return (NSInteger)self;
1283 }
1284
1285 - (NSPrintOperation *)printOperationWithPrintInfo:(NSPrintInfo *)printInfo forFrame:(WKFrameRef)frameRef
1286 {
1287     // Only the top frame can currently contain a PDF view.
1288     if (_data->_pdfViewController) {
1289         ASSERT(toImpl(frameRef)->isMainFrame());
1290         return _data->_pdfViewController->makePrintOperation(printInfo);
1291     }
1292     return [NSPrintOperation printOperationWithView:self printInfo:printInfo];
1293 }
1294
1295 - (BOOL)canChangeFrameLayout:(WKFrameRef)frameRef
1296 {
1297     // PDF documents are already paginated, so we can't change them to add headers and footers.
1298     return !toImpl(frameRef)->isMainFrame() || !_data->_pdfViewController;
1299 }
1300
1301 @end
1302
1303 @implementation WKView (Internal)
1304
1305 - (PassOwnPtr<WebKit::DrawingAreaProxy>)_createDrawingAreaProxy
1306 {
1307     if (useNewDrawingArea())
1308         return DrawingAreaProxyImpl::create(_data->_page.get());
1309
1310     return ChunkedUpdateDrawingAreaProxy::create(self, _data->_page.get());
1311 }
1312
1313 - (BOOL)_isFocused
1314 {
1315     if (_data->_inBecomeFirstResponder)
1316         return YES;
1317     if (_data->_inResignFirstResponder)
1318         return NO;
1319     return [[self window] firstResponder] == self;
1320 }
1321
1322 - (void)_processDidCrash
1323 {
1324     [self setNeedsDisplay:YES];
1325 }
1326
1327 - (void)_didRelaunchProcess
1328 {
1329     [self setNeedsDisplay:YES];
1330 }
1331
1332 - (void)_takeFocus:(BOOL)forward
1333 {
1334     if (forward)
1335         [[self window] selectKeyViewFollowingView:self];
1336     else
1337         [[self window] selectKeyViewPrecedingView:self];
1338 }
1339
1340 - (void)_setCursor:(NSCursor *)cursor
1341 {
1342     if ([NSCursor currentCursor] == cursor)
1343         return;
1344     [cursor set];
1345 }
1346
1347 - (void)_setUserInterfaceItemState:(NSString *)commandName enabled:(BOOL)isEnabled state:(int)newState
1348 {
1349     ValidationVector items = _data->_validationMap.take(commandName);
1350     size_t size = items.size();
1351     for (size_t i = 0; i < size; ++i) {
1352         ValidationItem item = items[i].get();
1353         [menuItem(item) setState:newState];
1354         [menuItem(item) setEnabled:isEnabled];
1355         [toolbarItem(item) setEnabled:isEnabled];
1356         // FIXME <rdar://problem/8803392>: If the item is neither a menu nor toolbar item, it will be left enabled.
1357     }
1358 }
1359
1360 - (NSRect)_convertToDeviceSpace:(NSRect)rect
1361 {
1362     return toDeviceSpace(rect, [self window]);
1363 }
1364
1365 - (NSRect)_convertToUserSpace:(NSRect)rect
1366 {
1367     return toUserSpace(rect, [self window]);
1368 }
1369
1370 // Any non-zero value will do, but using something recognizable might help us debug some day.
1371 #define TRACKING_RECT_TAG 0xBADFACE
1372
1373 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
1374 {
1375     ASSERT(_data->_trackingRectOwner == nil);
1376     _data->_trackingRectOwner = owner;
1377     _data->_trackingRectUserData = data;
1378     return TRACKING_RECT_TAG;
1379 }
1380
1381 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
1382 {
1383     ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
1384     ASSERT(_data->_trackingRectOwner == nil);
1385     _data->_trackingRectOwner = owner;
1386     _data->_trackingRectUserData = data;
1387     return TRACKING_RECT_TAG;
1388 }
1389
1390 - (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count
1391 {
1392     ASSERT(count == 1);
1393     ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
1394     ASSERT(_data->_trackingRectOwner == nil);
1395     _data->_trackingRectOwner = owner;
1396     _data->_trackingRectUserData = userDataList[0];
1397     trackingNums[0] = TRACKING_RECT_TAG;
1398 }
1399
1400 - (void)removeTrackingRect:(NSTrackingRectTag)tag
1401 {
1402     if (tag == 0)
1403         return;
1404     
1405     if (_data && (tag == TRACKING_RECT_TAG)) {
1406         _data->_trackingRectOwner = nil;
1407         return;
1408     }
1409     
1410     if (_data && (tag == _data->_lastToolTipTag)) {
1411         [super removeTrackingRect:tag];
1412         _data->_lastToolTipTag = 0;
1413         return;
1414     }
1415     
1416     // If any other tracking rect is being removed, we don't know how it was created
1417     // and it's possible there's a leak involved (see 3500217)
1418     ASSERT_NOT_REACHED();
1419 }
1420
1421 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count
1422 {
1423     int i;
1424     for (i = 0; i < count; ++i) {
1425         int tag = tags[i];
1426         if (tag == 0)
1427             continue;
1428         ASSERT(tag == TRACKING_RECT_TAG);
1429         if (_data != nil) {
1430             _data->_trackingRectOwner = nil;
1431         }
1432     }
1433 }
1434
1435 - (void)_sendToolTipMouseExited
1436 {
1437     // Nothing matters except window, trackingNumber, and userData.
1438     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
1439         location:NSMakePoint(0, 0)
1440         modifierFlags:0
1441         timestamp:0
1442         windowNumber:[[self window] windowNumber]
1443         context:NULL
1444         eventNumber:0
1445         trackingNumber:TRACKING_RECT_TAG
1446         userData:_data->_trackingRectUserData];
1447     [_data->_trackingRectOwner mouseExited:fakeEvent];
1448 }
1449
1450 - (void)_sendToolTipMouseEntered
1451 {
1452     // Nothing matters except window, trackingNumber, and userData.
1453     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
1454         location:NSMakePoint(0, 0)
1455         modifierFlags:0
1456         timestamp:0
1457         windowNumber:[[self window] windowNumber]
1458         context:NULL
1459         eventNumber:0
1460         trackingNumber:TRACKING_RECT_TAG
1461         userData:_data->_trackingRectUserData];
1462     [_data->_trackingRectOwner mouseEntered:fakeEvent];
1463 }
1464
1465 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
1466 {
1467     return nsStringFromWebCoreString(_data->_page->toolTip());
1468 }
1469
1470 - (void)_toolTipChangedFrom:(NSString *)oldToolTip to:(NSString *)newToolTip
1471 {
1472     if (oldToolTip)
1473         [self _sendToolTipMouseExited];
1474
1475     if (newToolTip && [newToolTip length] > 0) {
1476         // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
1477         [self removeAllToolTips];
1478         NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
1479         _data->_lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL];
1480         [self _sendToolTipMouseEntered];
1481     }
1482 }
1483
1484 - (void)_setFindIndicator:(PassRefPtr<FindIndicator>)findIndicator fadeOut:(BOOL)fadeOut
1485 {
1486     if (!findIndicator) {
1487         _data->_findIndicatorWindow = 0;
1488         return;
1489     }
1490
1491     if (!_data->_findIndicatorWindow)
1492         _data->_findIndicatorWindow = FindIndicatorWindow::create(self);
1493
1494     _data->_findIndicatorWindow->setFindIndicator(findIndicator, fadeOut);
1495 }
1496
1497 #if USE(ACCELERATED_COMPOSITING)
1498 - (void)_startAcceleratedCompositing:(CALayer *)rootLayer
1499 {
1500     if (!_data->_layerHostingView) {
1501         NSView *hostingView = [[NSView alloc] initWithFrame:[self bounds]];
1502 #if !defined(BUILDING_ON_LEOPARD)
1503         [hostingView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
1504 #endif
1505         
1506         [self addSubview:hostingView];
1507         [hostingView release];
1508         _data->_layerHostingView = hostingView;
1509     }
1510
1511     // Make a container layer, which will get sized/positioned by AppKit and CA.
1512     CALayer *viewLayer = [CALayer layer];
1513
1514 #ifndef NDEBUG
1515     [viewLayer setName:@"hosting layer"];
1516 #endif
1517
1518 #if defined(BUILDING_ON_LEOPARD)
1519     // Turn off default animations.
1520     NSNull *nullValue = [NSNull null];
1521     NSDictionary *actions = [NSDictionary dictionaryWithObjectsAndKeys:
1522                              nullValue, @"anchorPoint",
1523                              nullValue, @"bounds",
1524                              nullValue, @"contents",
1525                              nullValue, @"contentsRect",
1526                              nullValue, @"opacity",
1527                              nullValue, @"position",
1528                              nullValue, @"sublayerTransform",
1529                              nullValue, @"sublayers",
1530                              nullValue, @"transform",
1531                              nil];
1532     [viewLayer setStyle:[NSDictionary dictionaryWithObject:actions forKey:@"actions"]];
1533 #endif
1534
1535 #if !defined(BUILDING_ON_LEOPARD)
1536     // If we aren't in the window yet, we'll use the screen's scale factor now, and reset the scale 
1537     // via -viewDidMoveToWindow.
1538     CGFloat scaleFactor = [self window] ? [[self window] userSpaceScaleFactor] : [[NSScreen mainScreen] userSpaceScaleFactor];
1539     [viewLayer setTransform:CATransform3DMakeScale(scaleFactor, scaleFactor, 1)];
1540 #endif
1541
1542     [_data->_layerHostingView setLayer:viewLayer];
1543     [_data->_layerHostingView setWantsLayer:YES];
1544     
1545     // Parent our root layer in the container layer
1546     [viewLayer addSublayer:rootLayer];
1547 }
1548
1549 - (void)_stopAcceleratedCompositing
1550 {
1551     if (_data->_layerHostingView) {
1552         [_data->_layerHostingView setLayer:nil];
1553         [_data->_layerHostingView setWantsLayer:NO];
1554         [_data->_layerHostingView removeFromSuperview];
1555         _data->_layerHostingView = nil;
1556     }
1557 }
1558
1559 - (void)_switchToDrawingAreaTypeIfNecessary:(DrawingAreaInfo::Type)type
1560 {
1561     DrawingAreaInfo::Type existingDrawingAreaType = _data->_page->drawingArea() ? _data->_page->drawingArea()->info().type : DrawingAreaInfo::None;
1562     if (existingDrawingAreaType == type)
1563         return;
1564
1565     OwnPtr<DrawingAreaProxy> newDrawingArea;
1566     switch (type) {
1567         case DrawingAreaInfo::Impl:
1568         case DrawingAreaInfo::None:
1569             break;
1570         case DrawingAreaInfo::ChunkedUpdate: {
1571             newDrawingArea = ChunkedUpdateDrawingAreaProxy::create(self, _data->_page.get());
1572             break;
1573         }
1574         case DrawingAreaInfo::LayerBacked: {
1575             newDrawingArea = LayerBackedDrawingAreaProxy::create(self, _data->_page.get());
1576             break;
1577         }
1578     }
1579
1580     newDrawingArea->setSize(IntSize([self frame].size));
1581
1582     _data->_page->drawingArea()->detachCompositingContext();
1583     _data->_page->setDrawingArea(newDrawingArea.release());
1584 }
1585
1586 - (void)_pageDidEnterAcceleratedCompositing
1587 {
1588     [self _switchToDrawingAreaTypeIfNecessary:DrawingAreaInfo::LayerBacked];
1589 }
1590
1591 - (void)_pageDidLeaveAcceleratedCompositing
1592 {
1593     // FIXME: we may want to avoid flipping back to the non-layer-backed drawing area until the next page load, to avoid thrashing.
1594     [self _switchToDrawingAreaTypeIfNecessary:DrawingAreaInfo::ChunkedUpdate];
1595 }
1596 #endif // USE(ACCELERATED_COMPOSITING)
1597
1598 - (void)_setComplexTextInputEnabled:(BOOL)complexTextInputEnabled pluginComplexTextInputIdentifier:(uint64_t)pluginComplexTextInputIdentifier
1599 {
1600     BOOL inputSourceChanged = _data->_pluginComplexTextInputIdentifier;
1601
1602     if (complexTextInputEnabled) {
1603         // Check if we're already allowing text input for this plug-in.
1604         if (pluginComplexTextInputIdentifier == _data->_pluginComplexTextInputIdentifier)
1605             return;
1606
1607         _data->_pluginComplexTextInputIdentifier = pluginComplexTextInputIdentifier;
1608
1609     } else {
1610         // Check if we got a request to disable complex text input for a plug-in that is not the current plug-in.
1611         if (pluginComplexTextInputIdentifier != _data->_pluginComplexTextInputIdentifier)
1612             return;
1613
1614         _data->_pluginComplexTextInputIdentifier = 0;
1615     }
1616
1617     if (inputSourceChanged) {
1618         // Inform the out of line window that the input source changed.
1619         [[WKTextInputWindowController sharedTextInputWindowController] keyboardInputSourceChanged];
1620     }
1621 }
1622
1623 - (void)_setPageHasCustomRepresentation:(BOOL)pageHasCustomRepresentation
1624 {
1625     _data->_pdfViewController = nullptr;
1626
1627     if (pageHasCustomRepresentation)
1628         _data->_pdfViewController = PDFViewController::create(self);
1629 }
1630
1631 - (void)_didFinishLoadingDataForCustomRepresentation:(const CoreIPC::DataReference&)dataReference
1632 {
1633     ASSERT(_data->_pdfViewController);
1634
1635     _data->_pdfViewController->setPDFDocumentData(_data->_page->mainFrame()->mimeType(), dataReference);
1636 }
1637
1638 - (double)_customRepresentationZoomFactor
1639 {
1640     if (!_data->_pdfViewController)
1641         return 1;
1642
1643     return _data->_pdfViewController->zoomFactor();
1644 }
1645
1646 - (void)_setCustomRepresentationZoomFactor:(double)zoomFactor
1647 {
1648     if (!_data->_pdfViewController)
1649         return;
1650
1651     _data->_pdfViewController->setZoomFactor(zoomFactor);
1652 }
1653
1654 @end