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