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