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