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