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