2be2a537a50a459278842d8e3212de6ae65a8e6a
[WebKit-https.git] / Source / WebKit2 / WebProcess / WebPage / mac / WebPageMac.mm
1 /*
2  * Copyright (C) 2010, 2011, 2012 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "WebPage.h"
28
29 #import "AttributedString.h"
30 #import "DataReference.h"
31 #import "EditorState.h"
32 #import "PDFKitImports.h"
33 #import "PageBanner.h"
34 #import "PluginView.h"
35 #import "PrintInfo.h"
36 #import "WKAccessibilityWebPageObject.h"
37 #import "WebCoreArgumentCoders.h"
38 #import "WebEvent.h"
39 #import "WebEventConversion.h"
40 #import "WebFrame.h"
41 #import "WebImage.h"
42 #import "WebInspector.h"
43 #import "WebPageProxyMessages.h"
44 #import "WebPreferencesStore.h"
45 #import "WebProcess.h"
46 #import <PDFKit/PDFKit.h>
47 #import <QuartzCore/QuartzCore.h>
48 #import <WebCore/AXObjectCache.h>
49 #import <WebCore/EventHandler.h>
50 #import <WebCore/FocusController.h>
51 #import <WebCore/Frame.h>
52 #import <WebCore/FrameLoader.h>
53 #import <WebCore/FrameView.h>
54 #import <WebCore/HTMLConverter.h>
55 #import <WebCore/HitTestResult.h>
56 #import <WebCore/KeyboardEvent.h>
57 #import <WebCore/MIMETypeRegistry.h>
58 #import <WebCore/NetworkingContext.h>
59 #import <WebCore/Page.h>
60 #import <WebCore/PlatformKeyboardEvent.h>
61 #import <WebCore/PluginDocument.h>
62 #import <WebCore/RenderObject.h>
63 #import <WebCore/RenderStyle.h>
64 #import <WebCore/ResourceHandle.h>
65 #import <WebCore/ScrollView.h>
66 #import <WebCore/StyleInheritedData.h>
67 #import <WebCore/TextIterator.h>
68 #import <WebCore/VisibleUnits.h>
69 #import <WebCore/WindowsKeyboardCodes.h>
70 #import <WebKitSystemInterface.h>
71
72 using namespace WebCore;
73
74 namespace WebKit {
75
76 static PassRefPtr<Range> convertToRange(Frame*, NSRange);
77
78 void WebPage::platformInitialize()
79 {
80 #if USE(CFNETWORK)
81     m_page->addSchedulePair(SchedulePair::create([[NSRunLoop currentRunLoop] getCFRunLoop], kCFRunLoopCommonModes));
82 #else
83     m_page->addSchedulePair(SchedulePair::create([NSRunLoop currentRunLoop], kCFRunLoopCommonModes));
84 #endif
85
86     WKAccessibilityWebPageObject* mockAccessibilityElement = [[[WKAccessibilityWebPageObject alloc] init] autorelease];
87
88     // Get the pid for the starting process.
89     pid_t pid = WebProcess::shared().presenterApplicationPid();    
90     WKAXInitializeElementWithPresenterPid(mockAccessibilityElement, pid);
91     [mockAccessibilityElement setWebPage:this];
92     
93     // send data back over
94     NSData* remoteToken = (NSData *)WKAXRemoteTokenForElement(mockAccessibilityElement); 
95     CoreIPC::DataReference dataToken = CoreIPC::DataReference(reinterpret_cast<const uint8_t*>([remoteToken bytes]), [remoteToken length]);
96     send(Messages::WebPageProxy::RegisterWebProcessAccessibilityToken(dataToken));
97     m_mockAccessibilityElement = mockAccessibilityElement;
98 }
99
100 NSObject *WebPage::accessibilityObjectForMainFramePlugin()
101 {
102     Frame* frame = m_page->mainFrame();
103     if (!frame)
104         return 0;
105
106     if (PluginView* pluginView = pluginViewForFrame(frame))
107         return pluginView->accessibilityObject();
108
109     return 0;
110 }
111
112 void WebPage::platformPreferencesDidChange(const WebPreferencesStore& store)
113 {
114     if (WebInspector* inspector = this->inspector())
115         inspector->setInspectorUsesWebKitUserInterface(store.getBoolValueForKey(WebPreferencesKey::inspectorUsesWebKitUserInterfaceKey()));
116 }
117
118 bool WebPage::shouldUsePDFPlugin() const
119 {
120     return pdfPluginEnabled() && classFromPDFKit(@"PDFLayerController");
121 }
122
123 typedef HashMap<String, String> SelectorNameMap;
124
125 // Map selectors into Editor command names.
126 // This is not needed for any selectors that have the same name as the Editor command.
127 static const SelectorNameMap* createSelectorExceptionMap()
128 {
129     SelectorNameMap* map = new HashMap<String, String>;
130
131     map->add("insertNewlineIgnoringFieldEditor:", "InsertNewline");
132     map->add("insertParagraphSeparator:", "InsertNewline");
133     map->add("insertTabIgnoringFieldEditor:", "InsertTab");
134     map->add("pageDown:", "MovePageDown");
135     map->add("pageDownAndModifySelection:", "MovePageDownAndModifySelection");
136     map->add("pageUp:", "MovePageUp");
137     map->add("pageUpAndModifySelection:", "MovePageUpAndModifySelection");
138
139     return map;
140 }
141
142 static String commandNameForSelectorName(const String& selectorName)
143 {
144     // Check the exception map first.
145     static const SelectorNameMap* exceptionMap = createSelectorExceptionMap();
146     SelectorNameMap::const_iterator it = exceptionMap->find(selectorName);
147     if (it != exceptionMap->end())
148         return it->value;
149
150     // Remove the trailing colon.
151     // No need to capitalize the command name since Editor command names are not case sensitive.
152     size_t selectorNameLength = selectorName.length();
153     if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
154         return String();
155     return selectorName.left(selectorNameLength - 1);
156 }
157
158 static Frame* frameForEvent(KeyboardEvent* event)
159 {
160     Node* node = event->target()->toNode();
161     ASSERT(node);
162     Frame* frame = node->document()->frame();
163     ASSERT(frame);
164     return frame;
165 }
166
167 bool WebPage::executeKeypressCommandsInternal(const Vector<WebCore::KeypressCommand>& commands, KeyboardEvent* event)
168 {
169     Frame* frame = frameForEvent(event);
170     ASSERT(frame->page() == corePage());
171
172     bool eventWasHandled = false;
173     for (size_t i = 0; i < commands.size(); ++i) {
174         if (commands[i].commandName == "insertText:") {
175             ASSERT(!frame->editor().hasComposition());
176
177             if (!frame->editor().canEdit())
178                 continue;
179
180             // An insertText: might be handled by other responders in the chain if we don't handle it.
181             // One example is space bar that results in scrolling down the page.
182             eventWasHandled |= frame->editor().insertText(commands[i].text, event);
183         } else {
184             Editor::Command command = frame->editor().command(commandNameForSelectorName(commands[i].commandName));
185             if (command.isSupported()) {
186                 bool commandExecutedByEditor = command.execute(event);
187                 eventWasHandled |= commandExecutedByEditor;
188                 if (!commandExecutedByEditor) {
189                     bool performedNonEditingBehavior = event->keyEvent()->type() == PlatformEvent::RawKeyDown && performNonEditingBehaviorForSelector(commands[i].commandName, event);
190                     eventWasHandled |= performedNonEditingBehavior;
191                 }
192             } else {
193                 bool commandWasHandledByUIProcess = false;
194                 WebProcess::shared().parentProcessConnection()->sendSync(Messages::WebPageProxy::ExecuteSavedCommandBySelector(commands[i].commandName), 
195                     Messages::WebPageProxy::ExecuteSavedCommandBySelector::Reply(commandWasHandledByUIProcess), m_pageID);
196                 eventWasHandled |= commandWasHandledByUIProcess;
197             }
198         }
199     }
200     return eventWasHandled;
201 }
202
203 bool WebPage::handleEditingKeyboardEvent(KeyboardEvent* event, bool saveCommands)
204 {
205     ASSERT(!saveCommands || event->keypressCommands().isEmpty()); // Save commands once for each event.
206
207     Frame* frame = frameForEvent(event);
208     
209     const PlatformKeyboardEvent* platformEvent = event->keyEvent();
210     if (!platformEvent)
211         return false;
212     Vector<KeypressCommand>& commands = event->keypressCommands();
213
214     if ([platformEvent->macEvent() type] == NSFlagsChanged)
215         return false;
216
217     bool eventWasHandled = false;
218     
219     if (saveCommands) {
220         KeyboardEvent* oldEvent = m_keyboardEventBeingInterpreted;
221         m_keyboardEventBeingInterpreted = event;
222         bool sendResult = WebProcess::shared().parentProcessConnection()->sendSync(Messages::WebPageProxy::InterpretQueuedKeyEvent(editorState()), 
223             Messages::WebPageProxy::InterpretQueuedKeyEvent::Reply(eventWasHandled, commands), m_pageID);
224         m_keyboardEventBeingInterpreted = oldEvent;
225         if (!sendResult)
226             return false;
227
228         // An input method may make several actions per keypress. For example, pressing Return with Korean IM both confirms it and sends a newline.
229         // IM-like actions are handled immediately (so the return value from UI process is true), but there are saved commands that
230         // should be handled like normal text input after DOM event dispatch.
231         if (!event->keypressCommands().isEmpty())
232             return false;
233     } else {
234         // Are there commands that could just cause text insertion if executed via Editor?
235         // WebKit doesn't have enough information about mode to decide how they should be treated, so we leave it upon WebCore
236         // to either handle them immediately (e.g. Tab that changes focus) or let a keypress event be generated
237         // (e.g. Tab that inserts a Tab character, or Enter).
238         bool haveTextInsertionCommands = false;
239         for (size_t i = 0; i < commands.size(); ++i) {
240             if (frame->editor().command(commandNameForSelectorName(commands[i].commandName)).isTextInsertion())
241                 haveTextInsertionCommands = true;
242         }
243         // If there are no text insertion commands, default keydown handler is the right time to execute the commands.
244         // Keypress (Char event) handler is the latest opportunity to execute.
245         if (!haveTextInsertionCommands || platformEvent->type() == PlatformEvent::Char) {
246             eventWasHandled = executeKeypressCommandsInternal(event->keypressCommands(), event);
247             event->keypressCommands().clear();
248         }
249     }
250     return eventWasHandled;
251 }
252
253 void WebPage::sendComplexTextInputToPlugin(uint64_t pluginComplexTextInputIdentifier, const String& textInput)
254 {
255     for (HashSet<PluginView*>::const_iterator it = m_pluginViews.begin(), end = m_pluginViews.end(); it != end; ++it) {
256         if ((*it)->sendComplexTextInput(pluginComplexTextInputIdentifier, textInput))
257             break;
258     }
259 }
260
261 void WebPage::setComposition(const String& text, Vector<CompositionUnderline> underlines, uint64_t selectionStart, uint64_t selectionEnd, uint64_t replacementRangeStart, uint64_t replacementRangeEnd, EditorState& newState)
262 {
263     Frame* frame = m_page->focusController()->focusedOrMainFrame();
264
265     if (frame->selection()->isContentEditable()) {
266         RefPtr<Range> replacementRange;
267         if (replacementRangeStart != NSNotFound) {
268             replacementRange = convertToRange(frame, NSMakeRange(replacementRangeStart, replacementRangeEnd - replacementRangeStart));
269             frame->selection()->setSelection(VisibleSelection(replacementRange.get(), SEL_DEFAULT_AFFINITY));
270         }
271
272         frame->editor().setComposition(text, underlines, selectionStart, selectionEnd);
273     }
274
275     newState = editorState();
276 }
277
278 void WebPage::confirmComposition(EditorState& newState)
279 {
280     Frame* frame = m_page->focusController()->focusedOrMainFrame();
281
282     frame->editor().confirmComposition();
283
284     newState = editorState();
285 }
286
287 void WebPage::cancelComposition(EditorState& newState)
288 {
289     Frame* frame = m_page->focusController()->focusedOrMainFrame();
290
291     frame->editor().cancelComposition();
292
293     newState = editorState();
294 }
295
296 void WebPage::insertText(const String& text, uint64_t replacementRangeStart, uint64_t replacementRangeEnd, bool& handled, EditorState& newState)
297 {
298     Frame* frame = m_page->focusController()->focusedOrMainFrame();
299
300     if (replacementRangeStart != NSNotFound) {
301         RefPtr<Range> replacementRange = convertToRange(frame, NSMakeRange(replacementRangeStart, replacementRangeEnd - replacementRangeStart));
302         if (replacementRange)
303             frame->selection()->setSelection(VisibleSelection(replacementRange.get(), SEL_DEFAULT_AFFINITY));
304     }
305
306     if (!frame->editor().hasComposition()) {
307         // An insertText: might be handled by other responders in the chain if we don't handle it.
308         // One example is space bar that results in scrolling down the page.
309         handled = frame->editor().insertText(text, m_keyboardEventBeingInterpreted);
310     } else {
311         handled = true;
312         frame->editor().confirmComposition(text);
313     }
314
315     newState = editorState();
316 }
317
318 void WebPage::insertDictatedText(const String& text, uint64_t replacementRangeStart, uint64_t replacementRangeEnd, const Vector<WebCore::DictationAlternative>& dictationAlternativeLocations, bool& handled, EditorState& newState)
319 {
320     Frame* frame = m_page->focusController()->focusedOrMainFrame();
321
322     if (replacementRangeStart != NSNotFound) {
323         RefPtr<Range> replacementRange = convertToRange(frame, NSMakeRange(replacementRangeStart, replacementRangeEnd - replacementRangeStart));
324         if (replacementRange)
325             frame->selection()->setSelection(VisibleSelection(replacementRange.get(), SEL_DEFAULT_AFFINITY));
326     }
327
328     ASSERT(!frame->editor().hasComposition());
329     handled = frame->editor().insertDictatedText(text, dictationAlternativeLocations, m_keyboardEventBeingInterpreted);
330     newState = editorState();
331 }
332
333 void WebPage::getMarkedRange(uint64_t& location, uint64_t& length)
334 {
335     location = NSNotFound;
336     length = 0;
337     Frame* frame = m_page->focusController()->focusedOrMainFrame();
338     if (!frame)
339         return;
340
341     RefPtr<Range> range = frame->editor().compositionRange();
342     size_t locationSize;
343     size_t lengthSize;
344     if (range && TextIterator::getLocationAndLengthFromRange(frame->selection()->rootEditableElementOrDocumentElement(), range.get(), locationSize, lengthSize)) {
345         location = static_cast<uint64_t>(locationSize);
346         length = static_cast<uint64_t>(lengthSize);
347     }
348 }
349
350 void WebPage::getSelectedRange(uint64_t& location, uint64_t& length)
351 {
352     location = NSNotFound;
353     length = 0;
354     Frame* frame = m_page->focusController()->focusedOrMainFrame();
355     if (!frame)
356         return;
357
358     size_t locationSize;
359     size_t lengthSize;
360     RefPtr<Range> range = frame->selection()->toNormalizedRange();
361     if (range && TextIterator::getLocationAndLengthFromRange(frame->selection()->rootEditableElementOrDocumentElement(), range.get(), locationSize, lengthSize)) {
362         location = static_cast<uint64_t>(locationSize);
363         length = static_cast<uint64_t>(lengthSize);
364     }
365 }
366
367 void WebPage::getAttributedSubstringFromRange(uint64_t location, uint64_t length, AttributedString& result)
368 {
369     NSRange nsRange = NSMakeRange(location, length - location);
370
371     Frame* frame = m_page->focusController()->focusedOrMainFrame();
372     if (!frame)
373         return;
374
375     if (frame->selection()->isNone() || !frame->selection()->isContentEditable() || frame->selection()->isInPasswordField())
376         return;
377
378     RefPtr<Range> range = convertToRange(frame, nsRange);
379     if (!range)
380         return;
381
382     result.string = [WebHTMLConverter editingAttributedStringFromRange:range.get()];
383     NSAttributedString* attributedString = result.string.get();
384     
385     // [WebHTMLConverter editingAttributedStringFromRange:] insists on inserting a trailing 
386     // whitespace at the end of the string which breaks the ATOK input method.  <rdar://problem/5400551>
387     // To work around this we truncate the resultant string to the correct length.
388     if ([attributedString length] > nsRange.length) {
389         ASSERT([attributedString length] == nsRange.length + 1);
390         ASSERT([[attributedString string] characterAtIndex:nsRange.length] == '\n' || [[attributedString string] characterAtIndex:nsRange.length] == ' ');
391         result.string = [attributedString attributedSubstringFromRange:NSMakeRange(0, nsRange.length)];
392     }
393 }
394
395 void WebPage::characterIndexForPoint(IntPoint point, uint64_t& index)
396 {
397     index = NSNotFound;
398     Frame* frame = m_page->mainFrame();
399     if (!frame)
400         return;
401
402     HitTestResult result = frame->eventHandler()->hitTestResultAtPoint(point);
403     frame = result.innerNonSharedNode() ? result.innerNodeFrame() : m_page->focusController()->focusedOrMainFrame();
404     
405     RefPtr<Range> range = frame->rangeForPoint(result.roundedPointInInnerNodeFrame());
406     if (!range)
407         return;
408
409     size_t location;
410     size_t length;
411     if (TextIterator::getLocationAndLengthFromRange(frame->selection()->rootEditableElementOrDocumentElement(), range.get(), location, length))
412         index = static_cast<uint64_t>(location);
413 }
414
415 PassRefPtr<Range> convertToRange(Frame* frame, NSRange nsrange)
416 {
417     if (nsrange.location > INT_MAX)
418         return 0;
419     if (nsrange.length > INT_MAX || nsrange.location + nsrange.length > INT_MAX)
420         nsrange.length = INT_MAX - nsrange.location;
421         
422     // our critical assumption is that we are only called by input methods that
423     // concentrate on a given area containing the selection
424     // We have to do this because of text fields and textareas. The DOM for those is not
425     // directly in the document DOM, so serialization is problematic. Our solution is
426     // to use the root editable element of the selection start as the positional base.
427     // That fits with AppKit's idea of an input context.
428     return TextIterator::rangeFromLocationAndLength(frame->selection()->rootEditableElementOrDocumentElement(), nsrange.location, nsrange.length);
429 }
430     
431 void WebPage::firstRectForCharacterRange(uint64_t location, uint64_t length, WebCore::IntRect& resultRect)
432 {
433     Frame* frame = m_page->focusController()->focusedOrMainFrame();
434     resultRect.setLocation(IntPoint(0, 0));
435     resultRect.setSize(IntSize(0, 0));
436     
437     RefPtr<Range> range = convertToRange(frame, NSMakeRange(location, length));
438     if (!range)
439         return;
440     
441     ASSERT(range->startContainer());
442     ASSERT(range->endContainer());
443      
444     IntRect rect = frame->editor().firstRectForRange(range.get());
445     resultRect = frame->view()->contentsToWindow(rect);
446 }
447
448 void WebPage::executeKeypressCommands(const Vector<WebCore::KeypressCommand>& commands, bool& handled, EditorState& newState)
449 {
450     handled = executeKeypressCommandsInternal(commands, m_keyboardEventBeingInterpreted);
451     newState = editorState();
452 }
453
454 static bool isPositionInRange(const VisiblePosition& position, Range* range)
455 {
456     RefPtr<Range> positionRange = makeRange(position, position);
457
458     ExceptionCode ec = 0;
459     range->compareBoundaryPoints(Range::START_TO_START, positionRange.get(), ec);
460     if (ec)
461         return false;
462
463     if (!range->isPointInRange(positionRange->startContainer(), positionRange->startOffset(), ec))
464         return false;
465     if (ec)
466         return false;
467
468     return true;
469 }
470
471 static bool shouldUseSelection(const VisiblePosition& position, const VisibleSelection& selection)
472 {
473     if (!selection.isRange())
474         return false;
475
476     RefPtr<Range> selectedRange = selection.toNormalizedRange();
477     if (!selectedRange)
478         return false;
479
480     return isPositionInRange(position, selectedRange.get());
481 }
482
483 void WebPage::performDictionaryLookupAtLocation(const FloatPoint& floatPoint)
484 {
485     Frame* frame = m_page->mainFrame();
486     if (!frame)
487         return;
488
489     if (PluginView* pluginView = pluginViewForFrame(frame)) {
490         if (pluginView->performDictionaryLookupAtLocation(floatPoint))
491             return;
492     }
493
494     // Find the frame the point is over.
495     IntPoint point = roundedIntPoint(floatPoint);
496     HitTestResult result = frame->eventHandler()->hitTestResultAtPoint(frame->view()->windowToContents(point));
497     frame = result.innerNonSharedNode() ? result.innerNonSharedNode()->document()->frame() : m_page->focusController()->focusedOrMainFrame();
498
499     IntPoint translatedPoint = frame->view()->windowToContents(point);
500
501     // Don't do anything if there is no character at the point.
502     if (!frame->rangeForPoint(translatedPoint))
503         return;
504
505     VisiblePosition position = frame->visiblePositionForPoint(translatedPoint);
506     VisibleSelection selection = m_page->focusController()->focusedOrMainFrame()->selection()->selection();
507     if (shouldUseSelection(position, selection)) {
508         performDictionaryLookupForSelection(frame, selection);
509         return;
510     }
511
512     NSDictionary *options = nil;
513
514     // As context, we are going to use four lines of text before and after the point. (Dictionary can sometimes look up things that are four lines long)
515     const int numberOfLinesOfContext = 4;
516     VisiblePosition contextStart = position;
517     VisiblePosition contextEnd = position;
518     for (int i = 0; i < numberOfLinesOfContext; i++) {
519         VisiblePosition n = previousLinePosition(contextStart, contextStart.lineDirectionPointForBlockDirectionNavigation());
520         if (n.isNull() || n == contextStart)
521             break;
522         contextStart = n;
523     }
524     for (int i = 0; i < numberOfLinesOfContext; i++) {
525         VisiblePosition n = nextLinePosition(contextEnd, contextEnd.lineDirectionPointForBlockDirectionNavigation());
526         if (n.isNull() || n == contextEnd)
527             break;
528         contextEnd = n;
529     }
530
531     VisiblePosition lineStart = startOfLine(contextStart);
532     if (!lineStart.isNull())
533         contextStart = lineStart;
534
535     VisiblePosition lineEnd = endOfLine(contextEnd);
536     if (!lineEnd.isNull())
537         contextEnd = lineEnd;
538
539     NSRange rangeToPass = NSMakeRange(TextIterator::rangeLength(makeRange(contextStart, position).get()), 0);
540
541     RefPtr<Range> fullCharacterRange = makeRange(contextStart, contextEnd);
542     String fullPlainTextString = plainText(fullCharacterRange.get());
543
544     NSRange extractedRange = WKExtractWordDefinitionTokenRangeFromContextualString(fullPlainTextString, rangeToPass, &options);
545
546     // This function sometimes returns {NSNotFound, 0} if it was unable to determine a good string.
547     if (extractedRange.location == NSNotFound)
548         return;
549
550     RefPtr<Range> finalRange = TextIterator::subrange(fullCharacterRange.get(), extractedRange.location, extractedRange.length);
551     if (!finalRange)
552         return;
553
554     performDictionaryLookupForRange(frame, finalRange.get(), options);
555 }
556
557 void WebPage::performDictionaryLookupForSelection(Frame* frame, const VisibleSelection& selection)
558 {
559     RefPtr<Range> selectedRange = selection.toNormalizedRange();
560     if (!selectedRange)
561         return;
562
563     NSDictionary *options = nil;
564
565     VisiblePosition selectionStart = selection.visibleStart();
566     VisiblePosition selectionEnd = selection.visibleEnd();
567
568     // As context, we are going to use the surrounding paragraphs of text.
569     VisiblePosition paragraphStart = startOfParagraph(selectionStart);
570     VisiblePosition paragraphEnd = endOfParagraph(selectionEnd);
571
572     int lengthToSelectionStart = TextIterator::rangeLength(makeRange(paragraphStart, selectionStart).get());
573     int lengthToSelectionEnd = TextIterator::rangeLength(makeRange(paragraphStart, selectionEnd).get());
574     NSRange rangeToPass = NSMakeRange(lengthToSelectionStart, lengthToSelectionEnd - lengthToSelectionStart);
575
576     String fullPlainTextString = plainText(makeRange(paragraphStart, paragraphEnd).get());
577
578     // Since we already have the range we want, we just need to grab the returned options.
579     WKExtractWordDefinitionTokenRangeFromContextualString(fullPlainTextString, rangeToPass, &options);
580
581     performDictionaryLookupForRange(frame, selectedRange.get(), options);
582 }
583
584 void WebPage::performDictionaryLookupForRange(Frame* frame, Range* range, NSDictionary *options)
585 {
586     if (range->text().stripWhiteSpace().isEmpty())
587         return;
588     
589     RenderObject* renderer = range->startContainer()->renderer();
590     RenderStyle* style = renderer->style();
591
592     Vector<FloatQuad> quads;
593     range->textQuads(quads);
594     if (quads.isEmpty())
595         return;
596
597     IntRect rangeRect = frame->view()->contentsToWindow(quads[0].enclosingBoundingBox());
598     
599     DictionaryPopupInfo dictionaryPopupInfo;
600     dictionaryPopupInfo.origin = FloatPoint(rangeRect.x(), rangeRect.y() + (style->fontMetrics().ascent() * pageScaleFactor()));
601     dictionaryPopupInfo.options = (CFDictionaryRef)options;
602
603     NSAttributedString *nsAttributedString = [WebHTMLConverter editingAttributedStringFromRange:range];
604
605     RetainPtr<NSMutableAttributedString> scaledNSAttributedString = adoptNS([[NSMutableAttributedString alloc] initWithString:[nsAttributedString string]]);
606
607     NSFontManager *fontManager = [NSFontManager sharedFontManager];
608
609     [nsAttributedString enumerateAttributesInRange:NSMakeRange(0, [nsAttributedString length]) options:0 usingBlock:^(NSDictionary *attributes, NSRange range, BOOL *stop) {
610         RetainPtr<NSMutableDictionary> scaledAttributes = adoptNS([attributes mutableCopy]);
611
612         NSFont *font = [scaledAttributes objectForKey:NSFontAttributeName];
613         if (font) {
614             font = [fontManager convertFont:font toSize:[font pointSize] * pageScaleFactor()];
615             [scaledAttributes setObject:font forKey:NSFontAttributeName];
616         }
617
618         [scaledNSAttributedString.get() addAttributes:scaledAttributes.get() range:range];
619     }];
620
621     AttributedString attributedString;
622     attributedString.string = scaledNSAttributedString;
623
624     send(Messages::WebPageProxy::DidPerformDictionaryLookup(attributedString, dictionaryPopupInfo));
625 }
626
627 bool WebPage::performNonEditingBehaviorForSelector(const String& selector, KeyboardEvent* event)
628 {
629     // First give accessibility a chance to handle the event.
630     Frame* frame = frameForEvent(event);
631     frame->eventHandler()->handleKeyboardSelectionMovementForAccessibility(event);
632     if (event->defaultHandled())
633         return true;
634
635     // FIXME: All these selectors have corresponding Editor commands, but the commands only work in editable content.
636     // Should such non-editing behaviors be implemented in Editor or EventHandler::defaultArrowEventHandler() perhaps?
637     
638     bool didPerformAction = false;
639
640     if (selector == "moveUp:")
641         didPerformAction = scroll(m_page.get(), ScrollUp, ScrollByLine);
642     else if (selector == "moveToBeginningOfParagraph:")
643         didPerformAction = scroll(m_page.get(), ScrollUp, ScrollByPage);
644     else if (selector == "moveToBeginningOfDocument:") {
645         didPerformAction = scroll(m_page.get(), ScrollUp, ScrollByDocument);
646         didPerformAction |= scroll(m_page.get(), ScrollLeft, ScrollByDocument);
647     } else if (selector == "moveDown:")
648         didPerformAction = scroll(m_page.get(), ScrollDown, ScrollByLine);
649     else if (selector == "moveToEndOfParagraph:")
650         didPerformAction = scroll(m_page.get(), ScrollDown, ScrollByPage);
651     else if (selector == "moveToEndOfDocument:") {
652         didPerformAction = scroll(m_page.get(), ScrollDown, ScrollByDocument);
653         didPerformAction |= scroll(m_page.get(), ScrollLeft, ScrollByDocument);
654     } else if (selector == "moveLeft:")
655         didPerformAction = scroll(m_page.get(), ScrollLeft, ScrollByLine);
656     else if (selector == "moveWordLeft:")
657         didPerformAction = scroll(m_page.get(), ScrollLeft, ScrollByPage);
658     else if (selector == "moveToLeftEndOfLine:")
659         didPerformAction = m_page->goBack();
660     else if (selector == "moveRight:")
661         didPerformAction = scroll(m_page.get(), ScrollRight, ScrollByLine);
662     else if (selector == "moveWordRight:")
663         didPerformAction = scroll(m_page.get(), ScrollRight, ScrollByPage);
664     else if (selector == "moveToRightEndOfLine:")
665         didPerformAction = m_page->goForward();
666
667     return didPerformAction;
668 }
669
670 bool WebPage::performDefaultBehaviorForKeyEvent(const WebKeyboardEvent&)
671 {
672     return false;
673 }
674
675 void WebPage::registerUIProcessAccessibilityTokens(const CoreIPC::DataReference& elementToken, const CoreIPC::DataReference& windowToken)
676 {
677     NSData* elementTokenData = [NSData dataWithBytes:elementToken.data() length:elementToken.size()];
678     NSData* windowTokenData = [NSData dataWithBytes:windowToken.data() length:windowToken.size()];
679     id remoteElement = WKAXRemoteElementForToken(elementTokenData);
680     id remoteWindow = WKAXRemoteElementForToken(windowTokenData);
681     WKAXSetWindowForRemoteElement(remoteWindow, remoteElement);
682     
683     [accessibilityRemoteObject() setRemoteParent:remoteElement];
684 }
685
686 void WebPage::readSelectionFromPasteboard(const String& pasteboardName, bool& result)
687 {
688     Frame* frame = m_page->focusController()->focusedOrMainFrame();
689     if (!frame || frame->selection()->isNone()) {
690         result = false;
691         return;
692     }
693     frame->editor().readSelectionFromPasteboard(pasteboardName);
694     result = true;
695 }
696
697 void WebPage::getStringSelectionForPasteboard(String& stringValue)
698 {
699     Frame* frame = m_page->focusController()->focusedOrMainFrame();
700
701     if (!frame)
702         return;
703
704     if (PluginView* pluginView = focusedPluginViewForFrame(frame)) {
705         String selection = pluginView->getSelectionString();
706         if (!selection.isNull()) {
707             stringValue = selection;
708             return;
709         }
710     }
711
712     if (frame->selection()->isNone())
713         return;
714
715     stringValue = frame->editor().stringSelectionForPasteboard();
716 }
717
718 void WebPage::getDataSelectionForPasteboard(const String pasteboardType, SharedMemory::Handle& handle, uint64_t& size)
719 {
720     Frame* frame = m_page->focusController()->focusedOrMainFrame();
721     if (!frame || frame->selection()->isNone())
722         return;
723
724     RefPtr<SharedBuffer> buffer = frame->editor().dataSelectionForPasteboard(pasteboardType);
725     if (!buffer) {
726         size = 0;
727         return;
728     }
729     size = buffer->size();
730     RefPtr<SharedMemory> sharedMemoryBuffer = SharedMemory::create(size);
731     memcpy(sharedMemoryBuffer->data(), buffer->data(), size);
732     sharedMemoryBuffer->createHandle(handle, SharedMemory::ReadOnly);
733 }
734
735 WKAccessibilityWebPageObject* WebPage::accessibilityRemoteObject()
736 {
737     return m_mockAccessibilityElement.get();
738 }
739          
740 bool WebPage::platformHasLocalDataForURL(const WebCore::KURL& url)
741 {
742     NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url];
743     [request setValue:(NSString*)userAgent() forHTTPHeaderField:@"User-Agent"];
744     NSCachedURLResponse *cachedResponse;
745     if (CFURLStorageSessionRef storageSession = corePage()->mainFrame()->loader()->networkingContext()->storageSession().platformSession())
746         cachedResponse = WKCachedResponseForRequest(storageSession, request);
747     else
748         cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
749     [request release];
750     
751     return cachedResponse;
752 }
753
754 static NSCachedURLResponse *cachedResponseForURL(WebPage* webPage, const KURL& url)
755 {
756     RetainPtr<NSMutableURLRequest> request = adoptNS([[NSMutableURLRequest alloc] initWithURL:url]);
757     [request.get() setValue:(NSString *)webPage->userAgent() forHTTPHeaderField:@"User-Agent"];
758
759     if (CFURLStorageSessionRef storageSession = webPage->corePage()->mainFrame()->loader()->networkingContext()->storageSession().platformSession())
760         return WKCachedResponseForRequest(storageSession, request.get());
761
762     return [[NSURLCache sharedURLCache] cachedResponseForRequest:request.get()];
763 }
764
765 String WebPage::cachedSuggestedFilenameForURL(const KURL& url)
766 {
767     return [[cachedResponseForURL(this, url) response] suggestedFilename];
768 }
769
770 String WebPage::cachedResponseMIMETypeForURL(const KURL& url)
771 {
772     return [[cachedResponseForURL(this, url) response] MIMEType];
773 }
774
775 PassRefPtr<SharedBuffer> WebPage::cachedResponseDataForURL(const KURL& url)
776 {
777     return SharedBuffer::wrapNSData([cachedResponseForURL(this, url) data]);
778 }
779
780 bool WebPage::platformCanHandleRequest(const WebCore::ResourceRequest& request)
781 {
782     if ([NSURLConnection canHandleRequest:request.nsURLRequest(DoNotUpdateHTTPBody)])
783         return true;
784
785     // FIXME: Return true if this scheme is any one WebKit2 knows how to handle.
786     return request.url().protocolIs("applewebdata");
787 }
788
789 void WebPage::shouldDelayWindowOrderingEvent(const WebKit::WebMouseEvent& event, bool& result)
790 {
791     result = false;
792     Frame* frame = m_page->focusController()->focusedOrMainFrame();
793     if (!frame)
794         return;
795
796 #if ENABLE(DRAG_SUPPORT)
797     HitTestResult hitResult = frame->eventHandler()->hitTestResultAtPoint(frame->view()->windowToContents(event.position()), HitTestRequest::ReadOnly | HitTestRequest::Active);
798     if (hitResult.isSelected())
799         result = frame->eventHandler()->eventMayStartDrag(platform(event));
800 #endif
801 }
802
803 void WebPage::acceptsFirstMouse(int eventNumber, const WebKit::WebMouseEvent& event, bool& result)
804 {
805     result = false;
806     Frame* frame = m_page->focusController()->focusedOrMainFrame();
807     if (!frame)
808         return;
809     
810     HitTestResult hitResult = frame->eventHandler()->hitTestResultAtPoint(frame->view()->windowToContents(event.position()), HitTestRequest::ReadOnly | HitTestRequest::Active);
811     frame->eventHandler()->setActivationEventNumber(eventNumber);
812 #if ENABLE(DRAG_SUPPORT)
813     if (hitResult.isSelected())
814         result = frame->eventHandler()->eventMayStartDrag(platform(event));
815     else
816 #endif
817         result = !!hitResult.scrollbar();
818 }
819
820 void WebPage::setLayerHostingMode(LayerHostingMode layerHostingMode)
821 {
822     m_layerHostingMode = layerHostingMode;
823
824     for (HashSet<PluginView*>::const_iterator it = m_pluginViews.begin(), end = m_pluginViews.end(); it != end; ++it)
825         (*it)->setLayerHostingMode(layerHostingMode);
826 }
827
828 void WebPage::setTopOverhangImage(PassRefPtr<WebImage> image)
829 {
830     FrameView* frameView = m_mainFrame->coreFrame()->view();
831     if (!frameView)
832         return;
833
834     GraphicsLayer* layer = frameView->setWantsLayerForTopOverHangArea(image.get());
835     if (!layer)
836         return;
837
838     layer->setSize(image->size());
839     layer->setPosition(FloatPoint(0, -image->size().height()));
840
841     RetainPtr<CGImageRef> cgImage = image->bitmap()->makeCGImageCopy();
842     layer->platformLayer().contents = (id)cgImage.get();
843 }
844
845 void WebPage::setBottomOverhangImage(PassRefPtr<WebImage> image)
846 {
847     FrameView* frameView = m_mainFrame->coreFrame()->view();
848     if (!frameView)
849         return;
850
851     GraphicsLayer* layer = frameView->setWantsLayerForBottomOverHangArea(image.get());
852     if (!layer)
853         return;
854
855     layer->setSize(image->size());
856     
857     RetainPtr<CGImageRef> cgImage = image->bitmap()->makeCGImageCopy();
858     layer->platformLayer().contents = (id)cgImage.get();
859 }
860
861 void WebPage::updateHeaderAndFooterLayersForDeviceScaleChange(float scaleFactor)
862 {    
863     if (m_headerBanner)
864         m_headerBanner->didChangeDeviceScaleFactor(scaleFactor);
865     if (m_footerBanner)
866         m_footerBanner->didChangeDeviceScaleFactor(scaleFactor);
867 }
868
869 void WebPage::computePagesForPrintingPDFDocument(uint64_t frameID, const PrintInfo& printInfo, Vector<IntRect>& resultPageRects)
870 {
871     ASSERT(resultPageRects.isEmpty());
872     WebFrame* frame = WebProcess::shared().webFrame(frameID);
873     Frame* coreFrame = frame ? frame->coreFrame() : 0;
874     RetainPtr<PDFDocument> pdfDocument = coreFrame ? pdfDocumentForPrintingFrame(coreFrame) : 0;
875     if ([pdfDocument.get() allowsPrinting]) {
876         NSUInteger pageCount = [pdfDocument.get() pageCount];
877         IntRect pageRect(0, 0, ceilf(printInfo.availablePaperWidth), ceilf(printInfo.availablePaperHeight));
878         for (NSUInteger i = 1; i <= pageCount; ++i) {
879             resultPageRects.append(pageRect);
880             pageRect.move(0, pageRect.height());
881         }
882     }
883 }
884
885 static inline CGFloat roundCGFloat(CGFloat f)
886 {
887     if (sizeof(CGFloat) == sizeof(float))
888         return roundf(static_cast<float>(f));
889     return static_cast<CGFloat>(round(f));
890 }
891
892 static void drawPDFPage(PDFDocument *pdfDocument, CFIndex pageIndex, CGContextRef context, CGFloat pageSetupScaleFactor, CGSize paperSize)
893 {
894     CGContextSaveGState(context);
895
896     CGContextScaleCTM(context, pageSetupScaleFactor, pageSetupScaleFactor);
897
898     PDFPage *pdfPage = [pdfDocument pageAtIndex:pageIndex];
899     NSRect cropBox = [pdfPage boundsForBox:kPDFDisplayBoxCropBox];
900     if (NSIsEmptyRect(cropBox))
901         cropBox = [pdfPage boundsForBox:kPDFDisplayBoxMediaBox];
902     else
903         cropBox = NSIntersectionRect(cropBox, [pdfPage boundsForBox:kPDFDisplayBoxMediaBox]);
904
905     // Always auto-rotate PDF content regardless of the paper orientation.
906     NSInteger rotation = [pdfPage rotation];
907     if (rotation == 90 || rotation == 270)
908         std::swap(cropBox.size.width, cropBox.size.height);
909
910     bool shouldRotate = (paperSize.width < paperSize.height) != (cropBox.size.width < cropBox.size.height);
911     if (shouldRotate)
912         std::swap(cropBox.size.width, cropBox.size.height);
913
914     // Center.
915     CGFloat widthDifference = paperSize.width / pageSetupScaleFactor - cropBox.size.width;
916     CGFloat heightDifference = paperSize.height / pageSetupScaleFactor - cropBox.size.height;
917     if (widthDifference || heightDifference)
918         CGContextTranslateCTM(context, roundCGFloat(widthDifference / 2), roundCGFloat(heightDifference / 2));
919
920     if (shouldRotate) {
921         CGContextRotateCTM(context, static_cast<CGFloat>(piOverTwoDouble));
922         CGContextTranslateCTM(context, 0, -cropBox.size.width);
923     }
924
925     [NSGraphicsContext saveGraphicsState];
926     [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO]];
927     [pdfPage drawWithBox:kPDFDisplayBoxCropBox];
928     [NSGraphicsContext restoreGraphicsState];
929
930     CGAffineTransform transform = CGContextGetCTM(context);
931
932     for (PDFAnnotation *annotation in [pdfPage annotations]) {
933         if (![annotation isKindOfClass:pdfAnnotationLinkClass()])
934             continue;
935
936         PDFAnnotationLink *linkAnnotation = (PDFAnnotationLink *)annotation;
937         NSURL *url = [linkAnnotation URL];
938         if (!url)
939             continue;
940
941         CGRect urlRect = NSRectToCGRect([linkAnnotation bounds]);
942         CGRect transformedRect = CGRectApplyAffineTransform(urlRect, transform);
943         CGPDFContextSetURLForRect(context, (CFURLRef)url, transformedRect);
944     }
945
946     CGContextRestoreGState(context);
947 }
948
949 void WebPage::drawPDFDocument(CGContextRef context, PDFDocument *pdfDocument, const PrintInfo& printInfo, const WebCore::IntRect& rect)
950 {
951     NSUInteger pageCount = [pdfDocument pageCount];
952     IntSize paperSize(ceilf(printInfo.availablePaperWidth), ceilf(printInfo.availablePaperHeight));
953     IntRect pageRect(IntPoint(), paperSize);
954     for (NSUInteger i = 0; i < pageCount; ++i) {
955         if (pageRect.intersects(rect)) {
956             CGContextSaveGState(context);
957
958             CGContextTranslateCTM(context, pageRect.x() - rect.x(), pageRect.y() - rect.y());
959             drawPDFPage(pdfDocument, i, context, printInfo.pageSetupScaleFactor, paperSize);
960
961             CGContextRestoreGState(context);
962         }
963         pageRect.move(0, pageRect.height());
964     }
965 }
966
967 void WebPage::drawPagesToPDFFromPDFDocument(CGContextRef context, PDFDocument *pdfDocument, const PrintInfo& printInfo, uint32_t first, uint32_t count)
968 {
969     NSUInteger pageCount = [pdfDocument pageCount];
970     for (uint32_t page = first; page < first + count; ++page) {
971         if (page >= pageCount)
972             break;
973
974         RetainPtr<CFDictionaryRef> pageInfo = adoptCF(CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
975
976         CGPDFContextBeginPage(context, pageInfo.get());
977         drawPDFPage(pdfDocument, page, context, printInfo.pageSetupScaleFactor, CGSizeMake(printInfo.availablePaperWidth, printInfo.availablePaperHeight));
978         CGPDFContextEndPage(context);
979     }
980 }
981
982 void WebPage::didUpdateInWindowStateTimerFired()
983 {
984     [CATransaction flush];
985     send(Messages::WebPageProxy::DidUpdateInWindowState());
986 }
987
988 } // namespace WebKit