c2f0165375953b53cc3cd1b9eb431db5eceb93ad
[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 "PluginView.h"
34 #import "PrintInfo.h"
35 #import "WKAccessibilityWebPageObject.h"
36 #import "WebCoreArgumentCoders.h"
37 #import "WebEvent.h"
38 #import "WebEventConversion.h"
39 #import "WebFrame.h"
40 #import "WebInspector.h"
41 #import "WebPageProxyMessages.h"
42 #import "WebPreferencesStore.h"
43 #import "WebProcess.h"
44 #import <PDFKit/PDFKit.h>
45 #import <WebCore/AXObjectCache.h>
46 #import <WebCore/FocusController.h>
47 #import <WebCore/Frame.h>
48 #import <WebCore/FrameView.h>
49 #import <WebCore/HitTestResult.h>
50 #import <WebCore/HTMLConverter.h>
51 #import <WebCore/KeyboardEvent.h>
52 #import <WebCore/Page.h>
53 #import <WebCore/PlatformKeyboardEvent.h>
54 #import <WebCore/ResourceHandle.h>
55 #import <WebCore/RenderObject.h>
56 #import <WebCore/RenderStyle.h>
57 #import <WebCore/ScrollView.h>
58 #import <WebCore/StyleInheritedData.h>
59 #import <WebCore/TextIterator.h>
60 #import <WebCore/WindowsKeyboardCodes.h>
61 #import <WebCore/visible_units.h>
62 #import <WebKitSystemInterface.h>
63
64 using namespace WebCore;
65 using namespace std;
66
67 namespace WebKit {
68
69 static PassRefPtr<Range> convertToRange(Frame*, NSRange);
70
71 void WebPage::platformInitialize()
72 {
73 #if USE(CFNETWORK)
74     m_page->addSchedulePair(SchedulePair::create([[NSRunLoop currentRunLoop] getCFRunLoop], kCFRunLoopCommonModes));
75 #else
76     m_page->addSchedulePair(SchedulePair::create([NSRunLoop currentRunLoop], kCFRunLoopCommonModes));
77 #endif
78
79     WKAccessibilityWebPageObject* mockAccessibilityElement = [[[WKAccessibilityWebPageObject alloc] init] autorelease];
80
81     // Get the pid for the starting process.
82     pid_t pid = WebProcess::shared().presenterApplicationPid();    
83     WKAXInitializeElementWithPresenterPid(mockAccessibilityElement, pid);
84     [mockAccessibilityElement setWebPage:this];
85     
86     // send data back over
87     NSData* remoteToken = (NSData *)WKAXRemoteTokenForElement(mockAccessibilityElement); 
88     CoreIPC::DataReference dataToken = CoreIPC::DataReference(reinterpret_cast<const uint8_t*>([remoteToken bytes]), [remoteToken length]);
89     send(Messages::WebPageProxy::RegisterWebProcessAccessibilityToken(dataToken));
90     m_mockAccessibilityElement = mockAccessibilityElement;
91 }
92
93 void WebPage::platformPreferencesDidChange(const WebPreferencesStore& store)
94 {
95     if (WebInspector* inspector = this->inspector())
96         inspector->setInspectorUsesWebKitUserInterface(store.getBoolValueForKey(WebPreferencesKey::inspectorUsesWebKitUserInterfaceKey()));
97 }
98
99 typedef HashMap<String, String> SelectorNameMap;
100
101 // Map selectors into Editor command names.
102 // This is not needed for any selectors that have the same name as the Editor command.
103 static const SelectorNameMap* createSelectorExceptionMap()
104 {
105     SelectorNameMap* map = new HashMap<String, String>;
106
107     map->add("insertNewlineIgnoringFieldEditor:", "InsertNewline");
108     map->add("insertParagraphSeparator:", "InsertNewline");
109     map->add("insertTabIgnoringFieldEditor:", "InsertTab");
110     map->add("pageDown:", "MovePageDown");
111     map->add("pageDownAndModifySelection:", "MovePageDownAndModifySelection");
112     map->add("pageUp:", "MovePageUp");
113     map->add("pageUpAndModifySelection:", "MovePageUpAndModifySelection");
114
115     return map;
116 }
117
118 static String commandNameForSelectorName(const String& selectorName)
119 {
120     // Check the exception map first.
121     static const SelectorNameMap* exceptionMap = createSelectorExceptionMap();
122     SelectorNameMap::const_iterator it = exceptionMap->find(selectorName);
123     if (it != exceptionMap->end())
124         return it->second;
125
126     // Remove the trailing colon.
127     // No need to capitalize the command name since Editor command names are not case sensitive.
128     size_t selectorNameLength = selectorName.length();
129     if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
130         return String();
131     return selectorName.left(selectorNameLength - 1);
132 }
133
134 static Frame* frameForEvent(KeyboardEvent* event)
135 {
136     Node* node = event->target()->toNode();
137     ASSERT(node);
138     Frame* frame = node->document()->frame();
139     ASSERT(frame);
140     return frame;
141 }
142
143 bool WebPage::executeKeypressCommandsInternal(const Vector<WebCore::KeypressCommand>& commands, KeyboardEvent* event)
144 {
145     Frame* frame = frameForEvent(event);
146     ASSERT(frame->page() == corePage());
147
148     bool eventWasHandled = false;
149     for (size_t i = 0; i < commands.size(); ++i) {
150         if (commands[i].commandName == "insertText:") {
151             ASSERT(!frame->editor()->hasComposition());
152
153             if (!frame->editor()->canEdit())
154                 continue;
155
156             // An insertText: might be handled by other responders in the chain if we don't handle it.
157             // One example is space bar that results in scrolling down the page.
158             eventWasHandled |= frame->editor()->insertText(commands[i].text, event);
159         } else {
160             Editor::Command command = frame->editor()->command(commandNameForSelectorName(commands[i].commandName));
161             if (command.isSupported()) {
162                 bool commandExecutedByEditor = command.execute(event);
163                 eventWasHandled |= commandExecutedByEditor;
164                 if (!commandExecutedByEditor) {
165                     bool performedNonEditingBehavior = event->keyEvent()->type() == PlatformEvent::RawKeyDown && performNonEditingBehaviorForSelector(commands[i].commandName);
166                     eventWasHandled |= performedNonEditingBehavior;
167                 }
168             } else {
169                 bool commandWasHandledByUIProcess = false;
170                 WebProcess::shared().connection()->sendSync(Messages::WebPageProxy::ExecuteSavedCommandBySelector(commands[i].commandName), 
171                     Messages::WebPageProxy::ExecuteSavedCommandBySelector::Reply(commandWasHandledByUIProcess), m_pageID);
172                 eventWasHandled |= commandWasHandledByUIProcess;
173             }
174         }
175     }
176     return eventWasHandled;
177 }
178
179 bool WebPage::handleEditingKeyboardEvent(KeyboardEvent* event, bool saveCommands)
180 {
181     ASSERT(!saveCommands || event->keypressCommands().isEmpty()); // Save commands once for each event.
182
183     Frame* frame = frameForEvent(event);
184     
185     const PlatformKeyboardEvent* platformEvent = event->keyEvent();
186     if (!platformEvent)
187         return false;
188     Vector<KeypressCommand>& commands = event->keypressCommands();
189
190     if ([platformEvent->macEvent() type] == NSFlagsChanged)
191         return false;
192
193     bool eventWasHandled = false;
194     
195     if (saveCommands) {
196         KeyboardEvent* oldEvent = m_keyboardEventBeingInterpreted;
197         m_keyboardEventBeingInterpreted = event;
198         bool sendResult = WebProcess::shared().connection()->sendSync(Messages::WebPageProxy::InterpretQueuedKeyEvent(editorState()), 
199             Messages::WebPageProxy::InterpretQueuedKeyEvent::Reply(eventWasHandled, commands), m_pageID);
200         m_keyboardEventBeingInterpreted = oldEvent;
201         if (!sendResult)
202             return false;
203
204         // An input method may make several actions per keypress. For example, pressing Return with Korean IM both confirms it and sends a newline.
205         // IM-like actions are handled immediately (so the return value from UI process is true), but there are saved commands that
206         // should be handled like normal text input after DOM event dispatch.
207         if (!event->keypressCommands().isEmpty())
208             return false;
209     } else {
210         // Are there commands that could just cause text insertion if executed via Editor?
211         // WebKit doesn't have enough information about mode to decide how they should be treated, so we leave it upon WebCore
212         // to either handle them immediately (e.g. Tab that changes focus) or let a keypress event be generated
213         // (e.g. Tab that inserts a Tab character, or Enter).
214         bool haveTextInsertionCommands = false;
215         for (size_t i = 0; i < commands.size(); ++i) {
216             if (frame->editor()->command(commandNameForSelectorName(commands[i].commandName)).isTextInsertion())
217                 haveTextInsertionCommands = true;
218         }
219         // If there are no text insertion commands, default keydown handler is the right time to execute the commands.
220         // Keypress (Char event) handler is the latest opportunity to execute.
221         if (!haveTextInsertionCommands || platformEvent->type() == PlatformEvent::Char) {
222             eventWasHandled = executeKeypressCommandsInternal(event->keypressCommands(), event);
223             event->keypressCommands().clear();
224         }
225     }
226     return eventWasHandled;
227 }
228
229 void WebPage::sendComplexTextInputToPlugin(uint64_t pluginComplexTextInputIdentifier, const String& textInput)
230 {
231     for (HashSet<PluginView*>::const_iterator it = m_pluginViews.begin(), end = m_pluginViews.end(); it != end; ++it) {
232         if ((*it)->sendComplexTextInput(pluginComplexTextInputIdentifier, textInput))
233             break;
234     }
235 }
236
237 void WebPage::setComposition(const String& text, Vector<CompositionUnderline> underlines, uint64_t selectionStart, uint64_t selectionEnd, uint64_t replacementRangeStart, uint64_t replacementRangeEnd, EditorState& newState)
238 {
239     Frame* frame = m_page->focusController()->focusedOrMainFrame();
240
241     if (frame->selection()->isContentEditable()) {
242         RefPtr<Range> replacementRange;
243         if (replacementRangeStart != NSNotFound) {
244             replacementRange = convertToRange(frame, NSMakeRange(replacementRangeStart, replacementRangeEnd - replacementRangeStart));
245             frame->selection()->setSelection(VisibleSelection(replacementRange.get(), SEL_DEFAULT_AFFINITY));
246         }
247
248         frame->editor()->setComposition(text, underlines, selectionStart, selectionEnd);
249     }
250
251     newState = editorState();
252 }
253
254 void WebPage::confirmComposition(EditorState& newState)
255 {
256     Frame* frame = m_page->focusController()->focusedOrMainFrame();
257
258     frame->editor()->confirmComposition();
259
260     newState = editorState();
261 }
262
263 void WebPage::cancelComposition(EditorState& newState)
264 {
265     Frame* frame = m_page->focusController()->focusedOrMainFrame();
266
267     frame->editor()->cancelComposition();
268
269     newState = editorState();
270 }
271
272 void WebPage::insertText(const String& text, uint64_t replacementRangeStart, uint64_t replacementRangeEnd, bool& handled, EditorState& newState)
273 {
274     Frame* frame = m_page->focusController()->focusedOrMainFrame();
275
276     if (replacementRangeStart != NSNotFound) {
277         RefPtr<Range> replacementRange = convertToRange(frame, NSMakeRange(replacementRangeStart, replacementRangeEnd - replacementRangeStart));
278         if (replacementRange)
279             frame->selection()->setSelection(VisibleSelection(replacementRange.get(), SEL_DEFAULT_AFFINITY));
280     }
281
282     if (!frame->editor()->hasComposition()) {
283         // An insertText: might be handled by other responders in the chain if we don't handle it.
284         // One example is space bar that results in scrolling down the page.
285         handled = frame->editor()->insertText(text, m_keyboardEventBeingInterpreted);
286     } else {
287         handled = true;
288         frame->editor()->confirmComposition(text);
289     }
290
291     newState = editorState();
292 }
293
294 void WebPage::insertDictatedText(const String& text, uint64_t replacementRangeStart, uint64_t replacementRangeEnd, const Vector<WebCore::DictationAlternative>& dictationAlternativeLocations, bool& handled, EditorState& newState)
295 {
296     Frame* frame = m_page->focusController()->focusedOrMainFrame();
297
298     if (replacementRangeStart != NSNotFound) {
299         RefPtr<Range> replacementRange = convertToRange(frame, NSMakeRange(replacementRangeStart, replacementRangeEnd - replacementRangeStart));
300         if (replacementRange)
301             frame->selection()->setSelection(VisibleSelection(replacementRange.get(), SEL_DEFAULT_AFFINITY));
302     }
303
304     ASSERT(!frame->editor()->hasComposition());
305     handled = frame->editor()->insertDictatedText(text, dictationAlternativeLocations, m_keyboardEventBeingInterpreted);
306     newState = editorState();
307 }
308
309 void WebPage::getMarkedRange(uint64_t& location, uint64_t& length)
310 {
311     location = NSNotFound;
312     length = 0;
313     Frame* frame = m_page->focusController()->focusedOrMainFrame();
314     if (!frame)
315         return;
316
317     RefPtr<Range> range = frame->editor()->compositionRange();
318     size_t locationSize;
319     size_t lengthSize;
320     if (range && TextIterator::getLocationAndLengthFromRange(frame->selection()->rootEditableElementOrDocumentElement(), range.get(), locationSize, lengthSize)) {
321         location = static_cast<uint64_t>(locationSize);
322         length = static_cast<uint64_t>(lengthSize);
323     }
324 }
325
326 void WebPage::getSelectedRange(uint64_t& location, uint64_t& length)
327 {
328     location = NSNotFound;
329     length = 0;
330     Frame* frame = m_page->focusController()->focusedOrMainFrame();
331     if (!frame)
332         return;
333
334     size_t locationSize;
335     size_t lengthSize;
336     RefPtr<Range> range = frame->selection()->toNormalizedRange();
337     if (range && TextIterator::getLocationAndLengthFromRange(frame->selection()->rootEditableElementOrDocumentElement(), range.get(), locationSize, lengthSize)) {
338         location = static_cast<uint64_t>(locationSize);
339         length = static_cast<uint64_t>(lengthSize);
340     }
341 }
342
343 void WebPage::getAttributedSubstringFromRange(uint64_t location, uint64_t length, AttributedString& result)
344 {
345     NSRange nsRange = NSMakeRange(location, length - location);
346
347     Frame* frame = m_page->focusController()->focusedOrMainFrame();
348     if (!frame)
349         return;
350
351     if (frame->selection()->isNone() || !frame->selection()->isContentEditable() || frame->selection()->isInPasswordField())
352         return;
353
354     RefPtr<Range> range = convertToRange(frame, nsRange);
355     if (!range)
356         return;
357
358     result.string = [WebHTMLConverter editingAttributedStringFromRange:range.get()];
359     NSAttributedString* attributedString = result.string.get();
360     
361     // [WebHTMLConverter editingAttributedStringFromRange:] insists on inserting a trailing 
362     // whitespace at the end of the string which breaks the ATOK input method.  <rdar://problem/5400551>
363     // To work around this we truncate the resultant string to the correct length.
364     if ([attributedString length] > nsRange.length) {
365         ASSERT([attributedString length] == nsRange.length + 1);
366         ASSERT([[attributedString string] characterAtIndex:nsRange.length] == '\n' || [[attributedString string] characterAtIndex:nsRange.length] == ' ');
367         result.string = [attributedString attributedSubstringFromRange:NSMakeRange(0, nsRange.length)];
368     }
369 }
370
371 void WebPage::characterIndexForPoint(IntPoint point, uint64_t& index)
372 {
373     index = NSNotFound;
374     Frame* frame = m_page->mainFrame();
375     if (!frame)
376         return;
377
378     HitTestResult result = frame->eventHandler()->hitTestResultAtPoint(point, false);
379     frame = result.innerNonSharedNode() ? result.innerNonSharedNode()->document()->frame() : m_page->focusController()->focusedOrMainFrame();
380     
381     RefPtr<Range> range = frame->rangeForPoint(result.roundedPoint());
382     if (!range)
383         return;
384
385     size_t location;
386     size_t length;
387     if (TextIterator::getLocationAndLengthFromRange(frame->selection()->rootEditableElementOrDocumentElement(), range.get(), location, length))
388         index = static_cast<uint64_t>(location);
389 }
390
391 PassRefPtr<Range> convertToRange(Frame* frame, NSRange nsrange)
392 {
393     if (nsrange.location > INT_MAX)
394         return 0;
395     if (nsrange.length > INT_MAX || nsrange.location + nsrange.length > INT_MAX)
396         nsrange.length = INT_MAX - nsrange.location;
397         
398     // our critical assumption is that we are only called by input methods that
399     // concentrate on a given area containing the selection
400     // We have to do this because of text fields and textareas. The DOM for those is not
401     // directly in the document DOM, so serialization is problematic. Our solution is
402     // to use the root editable element of the selection start as the positional base.
403     // That fits with AppKit's idea of an input context.
404     return TextIterator::rangeFromLocationAndLength(frame->selection()->rootEditableElementOrDocumentElement(), nsrange.location, nsrange.length);
405 }
406     
407 void WebPage::firstRectForCharacterRange(uint64_t location, uint64_t length, WebCore::IntRect& resultRect)
408 {
409     Frame* frame = m_page->focusController()->focusedOrMainFrame();
410     resultRect.setLocation(IntPoint(0, 0));
411     resultRect.setSize(IntSize(0, 0));
412     
413     RefPtr<Range> range = convertToRange(frame, NSMakeRange(location, length));
414     if (!range)
415         return;
416     
417     ASSERT(range->startContainer());
418     ASSERT(range->endContainer());
419      
420     IntRect rect = frame->editor()->firstRectForRange(range.get());
421     resultRect = frame->view()->contentsToWindow(rect);
422 }
423
424 void WebPage::executeKeypressCommands(const Vector<WebCore::KeypressCommand>& commands, bool& handled, EditorState& newState)
425 {
426     handled = executeKeypressCommandsInternal(commands, m_keyboardEventBeingInterpreted);
427     newState = editorState();
428 }
429
430 static bool isPositionInRange(const VisiblePosition& position, Range* range)
431 {
432     RefPtr<Range> positionRange = makeRange(position, position);
433
434     ExceptionCode ec = 0;
435     range->compareBoundaryPoints(Range::START_TO_START, positionRange.get(), ec);
436     if (ec)
437         return false;
438
439     if (!range->isPointInRange(positionRange->startContainer(), positionRange->startOffset(), ec))
440         return false;
441     if (ec)
442         return false;
443
444     return true;
445 }
446
447 static bool shouldUseSelection(const VisiblePosition& position, const VisibleSelection& selection)
448 {
449     if (!selection.isRange())
450         return false;
451
452     RefPtr<Range> selectedRange = selection.toNormalizedRange();
453     if (!selectedRange)
454         return false;
455
456     return isPositionInRange(position, selectedRange.get());
457 }
458
459 void WebPage::performDictionaryLookupAtLocation(const FloatPoint& floatPoint)
460 {
461     Frame* frame = m_page->mainFrame();
462     if (!frame)
463         return;
464
465     // Find the frame the point is over.
466     IntPoint point = roundedIntPoint(floatPoint);
467     HitTestResult result = frame->eventHandler()->hitTestResultAtPoint(frame->view()->windowToContents(point), false);
468     frame = result.innerNonSharedNode() ? result.innerNonSharedNode()->document()->frame() : m_page->focusController()->focusedOrMainFrame();
469
470     IntPoint translatedPoint = frame->view()->windowToContents(point);
471
472     // Don't do anything if there is no character at the point.
473     if (!frame->rangeForPoint(translatedPoint))
474         return;
475
476     VisiblePosition position = frame->visiblePositionForPoint(translatedPoint);
477     VisibleSelection selection = m_page->focusController()->focusedOrMainFrame()->selection()->selection();
478     if (shouldUseSelection(position, selection)) {
479         performDictionaryLookupForSelection(DictionaryPopupInfo::HotKey, frame, selection);
480         return;
481     }
482
483     NSDictionary *options = nil;
484
485 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
486     // As context, we are going to use the surrounding paragraph of text.
487     VisiblePosition paragraphStart = startOfParagraph(position);
488     VisiblePosition paragraphEnd = endOfParagraph(position);
489
490     NSRange rangeToPass = NSMakeRange(TextIterator::rangeLength(makeRange(paragraphStart, position).get()), 0);
491
492     RefPtr<Range> fullCharacterRange = makeRange(paragraphStart, paragraphEnd);
493     String fullPlainTextString = plainText(fullCharacterRange.get());
494
495     NSRange extractedRange = WKExtractWordDefinitionTokenRangeFromContextualString(fullPlainTextString, rangeToPass, &options);
496
497     RefPtr<Range> finalRange = TextIterator::subrange(fullCharacterRange.get(), extractedRange.location, extractedRange.length);
498     if (!finalRange)
499         return;
500 #else
501     RefPtr<Range> finalRange = makeRange(startOfWord(position), endOfWord(position));
502     if (!finalRange)
503         return;
504 #endif
505
506     performDictionaryLookupForRange(DictionaryPopupInfo::HotKey, frame, finalRange.get(), options);
507 }
508
509 void WebPage::performDictionaryLookupForSelection(DictionaryPopupInfo::Type type, Frame* frame, const VisibleSelection& selection)
510 {
511     RefPtr<Range> selectedRange = selection.toNormalizedRange();
512     if (!selectedRange)
513         return;
514
515     NSDictionary *options = nil;
516
517 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
518     VisiblePosition selectionStart = selection.visibleStart();
519     VisiblePosition selectionEnd = selection.visibleEnd();
520
521     // As context, we are going to use the surrounding paragraphs of text.
522     VisiblePosition paragraphStart = startOfParagraph(selectionStart);
523     VisiblePosition paragraphEnd = endOfParagraph(selectionEnd);
524
525     int lengthToSelectionStart = TextIterator::rangeLength(makeRange(paragraphStart, selectionStart).get());
526     int lengthToSelectionEnd = TextIterator::rangeLength(makeRange(paragraphStart, selectionEnd).get());
527     NSRange rangeToPass = NSMakeRange(lengthToSelectionStart, lengthToSelectionEnd - lengthToSelectionStart);
528
529     String fullPlainTextString = plainText(makeRange(paragraphStart, paragraphEnd).get());
530
531     // Since we already have the range we want, we just need to grab the returned options.
532     WKExtractWordDefinitionTokenRangeFromContextualString(fullPlainTextString, rangeToPass, &options);
533 #endif
534
535     performDictionaryLookupForRange(type, frame, selectedRange.get(), options);
536 }
537
538 void WebPage::performDictionaryLookupForRange(DictionaryPopupInfo::Type type, Frame* frame, Range* range, NSDictionary *options)
539 {
540     String rangeText = range->text();
541     if (rangeText.stripWhiteSpace().isEmpty())
542         return;
543     
544     RenderObject* renderer = range->startContainer()->renderer();
545     RenderStyle* style = renderer->style();
546     NSFont *font = style->font().primaryFont()->getNSFont();
547     
548     // We won't be able to get an NSFont in the case that a Web Font is being used, so use
549     // the default system font at the same size instead.
550     if (!font)
551         font = [NSFont systemFontOfSize:style->font().primaryFont()->platformData().size()];
552
553     CFDictionaryRef fontDescriptorAttributes = (CFDictionaryRef)[[font fontDescriptor] fontAttributes];
554     if (!fontDescriptorAttributes)
555         return;
556
557     Vector<FloatQuad> quads;
558     range->textQuads(quads);
559     if (quads.isEmpty())
560         return;
561     
562     IntRect rangeRect = frame->view()->contentsToWindow(quads[0].enclosingBoundingBox());
563     
564     DictionaryPopupInfo dictionaryPopupInfo;
565     dictionaryPopupInfo.type = type;
566     dictionaryPopupInfo.origin = FloatPoint(rangeRect.x(), rangeRect.y() + (style->fontMetrics().ascent() * pageScaleFactor()));
567     dictionaryPopupInfo.fontInfo.fontAttributeDictionary = fontDescriptorAttributes;
568 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
569     dictionaryPopupInfo.options = (CFDictionaryRef)options;
570 #endif
571
572     send(Messages::WebPageProxy::DidPerformDictionaryLookup(rangeText, dictionaryPopupInfo));
573 }
574
575 bool WebPage::performNonEditingBehaviorForSelector(const String& selector)
576 {
577     // FIXME: All these selectors have corresponding Editor commands, but the commands only work in editable content.
578     // Should such non-editing behaviors be implemented in Editor or EventHandler::defaultArrowEventHandler() perhaps?
579     if (selector == "moveUp:")
580         scroll(m_page.get(), ScrollUp, ScrollByLine);
581     else if (selector == "moveToBeginningOfParagraph:")
582         scroll(m_page.get(), ScrollUp, ScrollByPage);
583     else if (selector == "moveToBeginningOfDocument:") {
584         scroll(m_page.get(), ScrollUp, ScrollByDocument);
585         scroll(m_page.get(), ScrollLeft, ScrollByDocument);
586     } else if (selector == "moveDown:")
587         scroll(m_page.get(), ScrollDown, ScrollByLine);
588     else if (selector == "moveToEndOfParagraph:")
589         scroll(m_page.get(), ScrollDown, ScrollByPage);
590     else if (selector == "moveToEndOfDocument:") {
591         scroll(m_page.get(), ScrollDown, ScrollByDocument);
592         scroll(m_page.get(), ScrollLeft, ScrollByDocument);
593     } else if (selector == "moveLeft:")
594         scroll(m_page.get(), ScrollLeft, ScrollByLine);
595     else if (selector == "moveWordLeft:")
596         scroll(m_page.get(), ScrollLeft, ScrollByPage);
597     else if (selector == "moveToLeftEndOfLine:")
598         m_page->goBack();
599     else if (selector == "moveRight:")
600         scroll(m_page.get(), ScrollRight, ScrollByLine);
601     else if (selector == "moveWordRight:")
602         scroll(m_page.get(), ScrollRight, ScrollByPage);
603     else if (selector == "moveToRightEndOfLine:")
604         m_page->goForward();
605     else
606         return false;
607
608     return true;
609 }
610
611 bool WebPage::performDefaultBehaviorForKeyEvent(const WebKeyboardEvent&)
612 {
613     return false;
614 }
615
616 void WebPage::registerUIProcessAccessibilityTokens(const CoreIPC::DataReference& elementToken, const CoreIPC::DataReference& windowToken)
617 {
618     NSData* elementTokenData = [NSData dataWithBytes:elementToken.data() length:elementToken.size()];
619     NSData* windowTokenData = [NSData dataWithBytes:windowToken.data() length:windowToken.size()];
620     id remoteElement = WKAXRemoteElementForToken(elementTokenData);
621     id remoteWindow = WKAXRemoteElementForToken(windowTokenData);
622     WKAXSetWindowForRemoteElement(remoteWindow, remoteElement);
623     
624     [accessibilityRemoteObject() setRemoteParent:remoteElement];
625 }
626
627 void WebPage::readSelectionFromPasteboard(const String& pasteboardName, bool& result)
628 {
629     Frame* frame = m_page->focusController()->focusedOrMainFrame();
630     if (!frame || frame->selection()->isNone()) {
631         result = false;
632         return;
633     }
634     frame->editor()->readSelectionFromPasteboard(pasteboardName);
635     result = true;
636 }
637
638 void WebPage::getStringSelectionForPasteboard(String& stringValue)
639 {
640     Frame* frame = m_page->focusController()->focusedOrMainFrame();
641     if (!frame || frame->selection()->isNone())
642         return;
643
644     stringValue = frame->editor()->stringSelectionForPasteboard();
645 }
646
647 void WebPage::getDataSelectionForPasteboard(const String pasteboardType, SharedMemory::Handle& handle, uint64_t& size)
648 {
649     Frame* frame = m_page->focusController()->focusedOrMainFrame();
650     if (!frame || frame->selection()->isNone())
651         return;
652
653     RefPtr<SharedBuffer> buffer = frame->editor()->dataSelectionForPasteboard(pasteboardType);
654     if (!buffer) {
655         size = 0;
656         return;
657     }
658     size = buffer->size();
659     RefPtr<SharedMemory> sharedMemoryBuffer = SharedMemory::create(size);
660     memcpy(sharedMemoryBuffer->data(), buffer->data(), size);
661     sharedMemoryBuffer->createHandle(handle, SharedMemory::ReadOnly);
662 }
663
664 WKAccessibilityWebPageObject* WebPage::accessibilityRemoteObject()
665 {
666     return m_mockAccessibilityElement.get();
667 }
668          
669 bool WebPage::platformHasLocalDataForURL(const WebCore::KURL& url)
670 {
671     NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url];
672     [request setValue:(NSString*)userAgent() forHTTPHeaderField:@"User-Agent"];
673     NSCachedURLResponse *cachedResponse;
674 #if USE(CFURLSTORAGESESSIONS)
675     if (CFURLStorageSessionRef storageSession = ResourceHandle::currentStorageSession())
676         cachedResponse = WKCachedResponseForRequest(storageSession, request);
677     else
678 #endif
679         cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
680     [request release];
681     
682     return cachedResponse;
683 }
684
685 static NSCachedURLResponse *cachedResponseForURL(WebPage* webPage, const KURL& url)
686 {
687     RetainPtr<NSMutableURLRequest> request(AdoptNS, [[NSMutableURLRequest alloc] initWithURL:url]);
688     [request.get() setValue:(NSString *)webPage->userAgent() forHTTPHeaderField:@"User-Agent"];
689
690 #if USE(CFURLSTORAGESESSIONS)
691     if (CFURLStorageSessionRef storageSession = ResourceHandle::currentStorageSession())
692         return WKCachedResponseForRequest(storageSession, request.get());
693 #endif
694
695     return [[NSURLCache sharedURLCache] cachedResponseForRequest:request.get()];
696 }
697
698 String WebPage::cachedSuggestedFilenameForURL(const KURL& url)
699 {
700     return [[cachedResponseForURL(this, url) response] suggestedFilename];
701 }
702
703 String WebPage::cachedResponseMIMETypeForURL(const KURL& url)
704 {
705     return [[cachedResponseForURL(this, url) response] MIMEType];
706 }
707
708 PassRefPtr<SharedBuffer> WebPage::cachedResponseDataForURL(const KURL& url)
709 {
710     return SharedBuffer::wrapNSData([cachedResponseForURL(this, url) data]);
711 }
712
713 bool WebPage::platformCanHandleRequest(const WebCore::ResourceRequest& request)
714 {
715     if ([NSURLConnection canHandleRequest:request.nsURLRequest()])
716         return true;
717
718     // FIXME: Return true if this scheme is any one WebKit2 knows how to handle.
719     return request.url().protocolIs("applewebdata");
720 }
721
722 void WebPage::shouldDelayWindowOrderingEvent(const WebKit::WebMouseEvent& event, bool& result)
723 {
724     result = false;
725     Frame* frame = m_page->focusController()->focusedOrMainFrame();
726     if (!frame)
727         return;
728
729 #if ENABLE(DRAG_SUPPORT)
730     HitTestResult hitResult = frame->eventHandler()->hitTestResultAtPoint(frame->view()->windowToContents(event.position()), true);
731     if (hitResult.isSelected())
732         result = frame->eventHandler()->eventMayStartDrag(platform(event));
733 #endif
734 }
735
736 void WebPage::acceptsFirstMouse(int eventNumber, const WebKit::WebMouseEvent& event, bool& result)
737 {
738     result = false;
739     Frame* frame = m_page->focusController()->focusedOrMainFrame();
740     if (!frame)
741         return;
742     
743     HitTestResult hitResult = frame->eventHandler()->hitTestResultAtPoint(frame->view()->windowToContents(event.position()), true);
744     frame->eventHandler()->setActivationEventNumber(eventNumber);
745 #if ENABLE(DRAG_SUPPORT)
746     if (hitResult.isSelected())
747         result = frame->eventHandler()->eventMayStartDrag(platform(event));
748     else
749 #endif
750         result = !!hitResult.scrollbar();
751 }
752
753 void WebPage::setLayerHostingMode(LayerHostingMode layerHostingMode)
754 {
755     m_layerHostingMode = layerHostingMode;
756
757     for (HashSet<PluginView*>::const_iterator it = m_pluginViews.begin(), end = m_pluginViews.end(); it != end; ++it)
758         (*it)->setLayerHostingMode(layerHostingMode);
759 }
760
761 void WebPage::computePagesForPrintingPDFDocument(uint64_t frameID, const PrintInfo& printInfo, Vector<IntRect>& resultPageRects)
762 {
763     ASSERT(resultPageRects.isEmpty());
764     WebFrame* frame = WebProcess::shared().webFrame(frameID);
765     Frame* coreFrame = frame ? frame->coreFrame() : 0;
766     RetainPtr<PDFDocument> pdfDocument = coreFrame ? pdfDocumentForPrintingFrame(coreFrame) : 0;
767     if ([pdfDocument.get() allowsPrinting]) {
768         NSUInteger pageCount = [pdfDocument.get() pageCount];
769         IntRect pageRect(0, 0, ceilf(printInfo.availablePaperWidth), ceilf(printInfo.availablePaperHeight));
770         for (NSUInteger i = 1; i <= pageCount; ++i) {
771             resultPageRects.append(pageRect);
772             pageRect.move(0, pageRect.height());
773         }
774     }
775 }
776
777 static inline CGFloat roundCGFloat(CGFloat f)
778 {
779     if (sizeof(CGFloat) == sizeof(float))
780         return roundf(static_cast<float>(f));
781     return static_cast<CGFloat>(round(f));
782 }
783
784 static void drawPDFPage(PDFDocument *pdfDocument, CFIndex pageIndex, CGContextRef context, CGFloat pageSetupScaleFactor, CGSize paperSize)
785 {
786     CGContextSaveGState(context);
787
788     CGContextScaleCTM(context, pageSetupScaleFactor, pageSetupScaleFactor);
789
790     PDFPage *pdfPage = [pdfDocument pageAtIndex:pageIndex];
791     NSRect cropBox = [pdfPage boundsForBox:kPDFDisplayBoxCropBox];
792     if (NSIsEmptyRect(cropBox))
793         cropBox = [pdfPage boundsForBox:kPDFDisplayBoxMediaBox];
794     else
795         cropBox = NSIntersectionRect(cropBox, [pdfPage boundsForBox:kPDFDisplayBoxMediaBox]);
796
797     bool shouldRotate = (paperSize.width < paperSize.height) != (cropBox.size.width < cropBox.size.height);
798     if (shouldRotate)
799         swap(cropBox.size.width, cropBox.size.height);
800
801     // Center.
802     CGFloat widthDifference = paperSize.width / pageSetupScaleFactor - cropBox.size.width;
803     CGFloat heightDifference = paperSize.height / pageSetupScaleFactor - cropBox.size.height;
804     if (widthDifference || heightDifference)
805         CGContextTranslateCTM(context, roundCGFloat(widthDifference / 2), roundCGFloat(heightDifference / 2));
806
807     if (shouldRotate) {
808         CGContextRotateCTM(context, static_cast<CGFloat>(piOverTwoDouble));
809         CGContextTranslateCTM(context, 0, -cropBox.size.width);
810     }
811
812     [NSGraphicsContext saveGraphicsState];
813     [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO]];
814     [pdfPage drawWithBox:kPDFDisplayBoxCropBox];
815     [NSGraphicsContext restoreGraphicsState];
816
817     CGAffineTransform transform = CGContextGetCTM(context);
818
819     for (PDFAnnotation *annotation in [pdfPage annotations]) {
820         if (![annotation isKindOfClass:pdfAnnotationLinkClass()])
821             continue;
822
823         PDFAnnotationLink *linkAnnotation = (PDFAnnotationLink *)annotation;
824         NSURL *url = [linkAnnotation URL];
825         if (!url)
826             continue;
827
828         CGRect urlRect = NSRectToCGRect([linkAnnotation bounds]);
829         CGRect transformedRect = CGRectApplyAffineTransform(urlRect, transform);
830         CGPDFContextSetURLForRect(context, (CFURLRef)url, transformedRect);
831     }
832
833     CGContextRestoreGState(context);
834 }
835
836 void WebPage::drawRectToPDFFromPDFDocument(CGContextRef context, PDFDocument *pdfDocument, const PrintInfo& printInfo, const WebCore::IntRect& rect)
837 {
838     NSUInteger pageCount = [pdfDocument pageCount];
839     IntSize paperSize(ceilf(printInfo.availablePaperWidth), ceilf(printInfo.availablePaperHeight));
840     IntRect pageRect(IntPoint(), paperSize);
841     for (NSUInteger i = 0; i < pageCount; ++i) {
842         if (pageRect.intersects(rect)) {
843             CGContextSaveGState(context);
844
845             CGContextTranslateCTM(context, pageRect.x() - rect.x(), pageRect.y() - rect.y());
846             drawPDFPage(pdfDocument, i, context, printInfo.pageSetupScaleFactor, paperSize);
847
848             CGContextRestoreGState(context);
849         }
850         pageRect.move(0, pageRect.height());
851     }
852 }
853
854 void WebPage::drawPagesToPDFFromPDFDocument(CGContextRef context, PDFDocument *pdfDocument, const PrintInfo& printInfo, uint32_t first, uint32_t count)
855 {
856     NSUInteger pageCount = [pdfDocument pageCount];
857     for (uint32_t page = first; page < first + count; ++page) {
858         if (page >= pageCount)
859             break;
860
861         RetainPtr<CFDictionaryRef> pageInfo(AdoptCF, CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
862
863         CGPDFContextBeginPage(context, pageInfo.get());
864         drawPDFPage(pdfDocument, page, context, printInfo.pageSetupScaleFactor, CGSizeMake(printInfo.availablePaperWidth, printInfo.availablePaperHeight));
865         CGPDFContextEndPage(context);
866     }
867 }
868
869 } // namespace WebKit