1/*
2 * Copyright (C) 2010 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "WebPage.h"
28
29#include "FontSmoothingLevel.h"
30#include "WebEvent.h"
31#include "WebPageProxyMessages.h"
32#include "WebPreferencesStore.h"
33#include <WebCore/FocusController.h>
34#include <WebCore/FontRenderingMode.h>
35#include <WebCore/Frame.h>
36#include <WebCore/FrameView.h>
37#include <WebCore/HitTestRequest.h>
38#include <WebCore/HitTestResult.h>
39#include <WebCore/KeyboardEvent.h>
40#include <WebCore/Page.h>
41#include <WebCore/PlatformKeyboardEvent.h>
42#include <WebCore/RenderLayer.h>
43#include <WebCore/RenderView.h>
44#include <WebCore/ResourceHandle.h>
45#include <WebCore/Settings.h>
46#if USE(CG)
47#include <WebKitSystemInterface/WebKitSystemInterface.h>
48#endif
49#include <WinUser.h>
50
51#if USE(CFNETWORK)
52#include <CFNetwork/CFURLCachePriv.h>
53#include <CFNetwork/CFURLProtocolPriv.h>
54#include <CFNetwork/CFURLRequestPriv.h>
55#endif
56
57using namespace WebCore;
58
59namespace WebKit {
60
61void WebPage::platformInitialize()
62{
63    m_page->settings()->setFontRenderingMode(AlternateRenderingMode);
64}
65
66void WebPage::platformPreferencesDidChange(const WebPreferencesStore& store)
67{
68    FontSmoothingLevel fontSmoothingLevel = static_cast<FontSmoothingLevel>(store.getUInt32ValueForKey(WebPreferencesKey::fontSmoothingLevelKey()));
69
70#if USE(CG)
71    FontSmoothingLevel adjustedLevel = fontSmoothingLevel;
72    if (adjustedLevel == FontSmoothingLevelWindows)
73        adjustedLevel = FontSmoothingLevelMedium;
74    wkSetFontSmoothingLevel(adjustedLevel);
75#endif
76
77    m_page->settings()->setFontRenderingMode(fontSmoothingLevel == FontSmoothingLevelWindows ? AlternateRenderingMode : NormalRenderingMode);
78}
79
80static const unsigned CtrlKey = 1 << 0;
81static const unsigned AltKey = 1 << 1;
82static const unsigned ShiftKey = 1 << 2;
83
84struct KeyDownEntry {
85    unsigned virtualKey;
86    unsigned modifiers;
87    const char* name;
88};
89
90struct KeyPressEntry {
91    unsigned charCode;
92    unsigned modifiers;
93    const char* name;
94};
95
96static const KeyDownEntry keyDownEntries[] = {
97    { VK_LEFT,   0,                  "MoveLeft"                                    },
98    { VK_LEFT,   ShiftKey,           "MoveLeftAndModifySelection"                  },
99    { VK_LEFT,   CtrlKey,            "MoveWordLeft"                                },
100    { VK_LEFT,   CtrlKey | ShiftKey, "MoveWordLeftAndModifySelection"              },
101    { VK_RIGHT,  0,                  "MoveRight"                                   },
102    { VK_RIGHT,  ShiftKey,           "MoveRightAndModifySelection"                 },
103    { VK_RIGHT,  CtrlKey,            "MoveWordRight"                               },
104    { VK_RIGHT,  CtrlKey | ShiftKey, "MoveWordRightAndModifySelection"             },
105    { VK_UP,     0,                  "MoveUp"                                      },
106    { VK_UP,     ShiftKey,           "MoveUpAndModifySelection"                    },
107    { VK_PRIOR,  ShiftKey,           "MovePageUpAndModifySelection"                },
108    { VK_DOWN,   0,                  "MoveDown"                                    },
109    { VK_DOWN,   ShiftKey,           "MoveDownAndModifySelection"                  },
110    { VK_NEXT,   ShiftKey,           "MovePageDownAndModifySelection"              },
111    { VK_PRIOR,  0,                  "MovePageUp"                                  },
112    { VK_NEXT,   0,                  "MovePageDown"                                },
113    { VK_HOME,   0,                  "MoveToBeginningOfLine"                       },
114    { VK_HOME,   ShiftKey,           "MoveToBeginningOfLineAndModifySelection"     },
115    { VK_HOME,   CtrlKey,            "MoveToBeginningOfDocument"                   },
116    { VK_HOME,   CtrlKey | ShiftKey, "MoveToBeginningOfDocumentAndModifySelection" },
117
118    { VK_END,    0,                  "MoveToEndOfLine"                             },
119    { VK_END,    ShiftKey,           "MoveToEndOfLineAndModifySelection"           },
120    { VK_END,    CtrlKey,            "MoveToEndOfDocument"                         },
121    { VK_END,    CtrlKey | ShiftKey, "MoveToEndOfDocumentAndModifySelection"       },
122
123    { VK_BACK,   0,                  "DeleteBackward"                              },
124    { VK_BACK,   ShiftKey,           "DeleteBackward"                              },
125    { VK_DELETE, 0,                  "DeleteForward"                               },
126    { VK_BACK,   CtrlKey,            "DeleteWordBackward"                          },
127    { VK_DELETE, CtrlKey,            "DeleteWordForward"                           },
128
129    { 'B',       CtrlKey,            "ToggleBold"                                  },
130    { 'I',       CtrlKey,            "ToggleItalic"                                },
131
132    { VK_ESCAPE, 0,                  "Cancel"                                      },
133    { VK_OEM_PERIOD, CtrlKey,        "Cancel"                                      },
134    { VK_TAB,    0,                  "InsertTab"                                   },
135    { VK_TAB,    ShiftKey,           "InsertBacktab"                               },
136    { VK_RETURN, 0,                  "InsertNewline"                               },
137    { VK_RETURN, CtrlKey,            "InsertNewline"                               },
138    { VK_RETURN, AltKey,             "InsertNewline"                               },
139    { VK_RETURN, ShiftKey,           "InsertNewline"                               },
140    { VK_RETURN, AltKey | ShiftKey,  "InsertNewline"                               },
141
142    // It's not quite clear whether clipboard shortcuts and Undo/Redo should be handled
143    // in the application or in WebKit. We chose WebKit.
144    { 'C',       CtrlKey,            "Copy"                                        },
145    { 'V',       CtrlKey,            "Paste"                                       },
146    { 'X',       CtrlKey,            "Cut"                                         },
147    { 'A',       CtrlKey,            "SelectAll"                                   },
148    { VK_INSERT, CtrlKey,            "Copy"                                        },
149    { VK_DELETE, ShiftKey,           "Cut"                                         },
150    { VK_INSERT, ShiftKey,           "Paste"                                       },
151    { 'Z',       CtrlKey,            "Undo"                                        },
152    { 'Z',       CtrlKey | ShiftKey, "Redo"                                        },
153};
154
155static const KeyPressEntry keyPressEntries[] = {
156    { '\t',   0,                  "InsertTab"                                   },
157    { '\t',   ShiftKey,           "InsertBacktab"                               },
158    { '\r',   0,                  "InsertNewline"                               },
159    { '\r',   CtrlKey,            "InsertNewline"                               },
160    { '\r',   AltKey,             "InsertNewline"                               },
161    { '\r',   ShiftKey,           "InsertNewline"                               },
162    { '\r',   AltKey | ShiftKey,  "InsertNewline"                               },
163};
164
165const char* WebPage::interpretKeyEvent(const KeyboardEvent* evt)
166{
167    ASSERT(evt->type() == eventNames().keydownEvent || evt->type() == eventNames().keypressEvent);
168
169    static HashMap<int, const char*>* keyDownCommandsMap = 0;
170    static HashMap<int, const char*>* keyPressCommandsMap = 0;
171
172    if (!keyDownCommandsMap) {
173        keyDownCommandsMap = new HashMap<int, const char*>;
174        keyPressCommandsMap = new HashMap<int, const char*>;
175
176        for (size_t i = 0; i < WTF_ARRAY_LENGTH(keyDownEntries); ++i)
177            keyDownCommandsMap->set(keyDownEntries[i].modifiers << 16 | keyDownEntries[i].virtualKey, keyDownEntries[i].name);
178
179        for (size_t i = 0; i < WTF_ARRAY_LENGTH(keyPressEntries); ++i)
180            keyPressCommandsMap->set(keyPressEntries[i].modifiers << 16 | keyPressEntries[i].charCode, keyPressEntries[i].name);
181    }
182
183    unsigned modifiers = 0;
184    if (evt->shiftKey())
185        modifiers |= ShiftKey;
186    if (evt->altKey())
187        modifiers |= AltKey;
188    if (evt->ctrlKey())
189        modifiers |= CtrlKey;
190
191    if (evt->type() == eventNames().keydownEvent) {
192        int mapKey = modifiers << 16 | evt->keyCode();
193        return mapKey ? keyDownCommandsMap->get(mapKey) : 0;
194    }
195
196    int mapKey = modifiers << 16 | evt->charCode();
197    return mapKey ? keyPressCommandsMap->get(mapKey) : 0;
198}
199
200bool WebPage::performDefaultBehaviorForKeyEvent(const WebKeyboardEvent& keyboardEvent)
201{
202    if (keyboardEvent.type() != WebEvent::KeyDown && keyboardEvent.type() != WebEvent::RawKeyDown)
203        return false;
204
205    switch (keyboardEvent.windowsVirtualKeyCode()) {
206    case VK_BACK:
207        if (keyboardEvent.isSystemKey())
208            return false;
209        if (keyboardEvent.shiftKey())
210            m_page->goForward();
211        else
212            m_page->goBack();
213        break;
214    case VK_LEFT:
215        if (keyboardEvent.isSystemKey())
216            m_page->goBack();
217        else
218            scroll(m_page.get(), ScrollLeft, ScrollByLine);
219        break;
220    case VK_RIGHT:
221        if (keyboardEvent.isSystemKey())
222            m_page->goForward();
223        else
224            scroll(m_page.get(), ScrollRight, ScrollByLine);
225        break;
226    case VK_UP:
227        if (keyboardEvent.isSystemKey())
228            return false;
229        scroll(m_page.get(), ScrollUp, ScrollByLine);
230        break;
231    case VK_DOWN:
232        if (keyboardEvent.isSystemKey())
233            return false;
234        scroll(m_page.get(), ScrollDown, ScrollByLine);
235        break;
236    case VK_HOME:
237        if (keyboardEvent.isSystemKey())
238            return false;
239        logicalScroll(m_page.get(), ScrollBlockDirectionBackward, ScrollByDocument);
240        break;
241    case VK_END:
242        if (keyboardEvent.isSystemKey())
243            return false;
244        logicalScroll(m_page.get(), ScrollBlockDirectionForward, ScrollByDocument);
245        break;
246    case VK_PRIOR:
247        if (keyboardEvent.isSystemKey())
248            return false;
249        logicalScroll(m_page.get(), ScrollBlockDirectionBackward, ScrollByPage);
250        break;
251    case VK_NEXT:
252        if (keyboardEvent.isSystemKey())
253            return false;
254        logicalScroll(m_page.get(), ScrollBlockDirectionForward, ScrollByPage);
255        break;
256    default:
257        return false;
258    }
259
260    return true;
261}
262
263bool WebPage::platformHasLocalDataForURL(const WebCore::KURL& url)
264{
265#if USE(CFNETWORK)
266    RetainPtr<CFURLRef> cfURL(AdoptCF, url.createCFURL());
267    RetainPtr<CFMutableURLRequestRef> request(AdoptCF, CFURLRequestCreateMutable(0, cfURL.get(), kCFURLRequestCachePolicyReloadIgnoringCache, 60, 0));
268
269    RetainPtr<CFStringRef> userAgent(AdoptCF, userAgent().createCFString());
270    CFURLRequestSetHTTPHeaderFieldValue(request.get(), CFSTR("User-Agent"), userAgent.get());
271
272    RetainPtr<CFURLCacheRef> cache;
273#if USE(CFURLSTORAGESESSIONS)
274    if (CFURLStorageSessionRef storageSession = ResourceHandle::privateBrowsingStorageSession())
275        cache.adoptCF(wkCopyURLCache(storageSession));
276    else
277#endif
278        cache.adoptCF(CFURLCacheCopySharedURLCache());
279
280    RetainPtr<CFCachedURLResponseRef> response(AdoptCF, CFURLCacheCopyResponseForRequest(cache.get(), request.get()));
281    return response;
282#else
283    return false;
284#endif
285}
286
287String WebPage::cachedResponseMIMETypeForURL(const WebCore::KURL& url)
288{
289#if USE(CFNETWORK)
290    RetainPtr<CFURLRef> cfURL(AdoptCF, url.createCFURL());
291    RetainPtr<CFMutableURLRequestRef> request(AdoptCF, CFURLRequestCreateMutable(0, cfURL.get(), kCFURLRequestCachePolicyReloadIgnoringCache, 60, 0));
292
293    RetainPtr<CFStringRef> userAgent(AdoptCF, userAgent().createCFString());
294    CFURLRequestSetHTTPHeaderFieldValue(request.get(), CFSTR("User-Agent"), userAgent.get());
295
296    RetainPtr<CFURLCacheRef> cache;
297#if USE(CFURLSTORAGESESSIONS)
298    if (CFURLStorageSessionRef storageSession = ResourceHandle::privateBrowsingStorageSession())
299        cache.adoptCF(wkCopyURLCache(storageSession));
300    else
301#endif
302        cache.adoptCF(CFURLCacheCopySharedURLCache());
303
304    RetainPtr<CFCachedURLResponseRef> cachedResponse(AdoptCF, CFURLCacheCopyResponseForRequest(cache.get(), request.get()));
305
306    CFURLResponseRef response = CFCachedURLResponseGetWrappedResponse(cachedResponse.get());
307
308    return response ? CFURLResponseGetMIMEType(response) : String();
309#else
310    return String();
311#endif
312}
313
314bool WebPage::platformCanHandleRequest(const WebCore::ResourceRequest& request)
315{
316#if USE(CFNETWORK)
317    return CFURLProtocolCanHandleRequest(request.cfURLRequest());
318#else
319    return true;
320#endif
321}
322
323void WebPage::confirmComposition(const String& compositionString)
324{
325    Frame* frame = m_page->focusController()->focusedOrMainFrame();
326    if (!frame || !frame->editor()->canEdit())
327        return;
328    frame->editor()->confirmComposition(compositionString);
329}
330
331void WebPage::setComposition(const String& compositionString, const Vector<WebCore::CompositionUnderline>& underlines, uint64_t cursorPosition)
332{
333    Frame* frame = m_page->focusController()->focusedOrMainFrame();
334    if (!frame || !frame->editor()->canEdit())
335        return;
336    frame->editor()->setComposition(compositionString, underlines, cursorPosition, 0);
337}
338
339void WebPage::firstRectForCharacterInSelectedRange(const uint64_t characterPosition, WebCore::IntRect& resultRect)
340{
341    Frame* frame = m_page->focusController()->focusedOrMainFrame();
342    IntRect rect;
343    if (RefPtr<Range> range = frame->editor()->hasComposition() ? frame->editor()->compositionRange() : frame->selection()->selection().toNormalizedRange()) {
344        ExceptionCode ec = 0;
345        RefPtr<Range> tempRange = range->cloneRange(ec);
346        tempRange->setStart(tempRange->startContainer(ec), tempRange->startOffset(ec) + characterPosition, ec);
347        rect = frame->editor()->firstRectForRange(tempRange.get());
348    }
349    resultRect = frame->view()->contentsToWindow(rect);
350}
351
352void WebPage::getSelectedText(String& text)
353{
354    Frame* frame = m_page->focusController()->focusedOrMainFrame();
355    RefPtr<Range> selectedRange = frame->selection()->toNormalizedRange();
356    text = selectedRange->text();
357}
358
359void WebPage::gestureWillBegin(const WebCore::IntPoint& point, bool& canBeginPanning)
360{
361    m_gestureReachedScrollingLimit = false;
362
363    bool hitScrollbar = false;
364
365    HitTestRequest request(HitTestRequest::ReadOnly);
366    for (Frame* childFrame = m_page->mainFrame(); childFrame; childFrame = EventHandler::subframeForTargetNode(m_gestureTargetNode.get())) {
367        ScrollView* scollView = childFrame->view();
368        if (!scollView)
369            break;
370
371        RenderView* renderView = childFrame->document()->renderView();
372        if (!renderView)
373            break;
374
375        RenderLayer* layer = renderView->layer();
376        if (!layer)
377            break;
378
379        HitTestResult result = scollView->windowToContents(point);
380        layer->hitTest(request, result);
381        m_gestureTargetNode = result.innerNode();
382
383        if (!hitScrollbar)
384            hitScrollbar = result.scrollbar();
385    }
386
387    if (hitScrollbar) {
388        canBeginPanning = false;
389        return;
390    }
391
392    if (!m_gestureTargetNode) {
393        canBeginPanning = false;
394        return;
395    }
396
397    for (RenderObject* renderer = m_gestureTargetNode->renderer(); renderer; renderer = renderer->parent()) {
398        if (renderer->isBox() && toRenderBox(renderer)->canBeScrolledAndHasScrollableArea()) {
399            canBeginPanning = true;
400            return;
401        }
402    }
403
404    canBeginPanning = false;
405}
406
407static bool scrollbarAtTopOrBottomOfDocument(Scrollbar* scrollbar)
408{
409    ASSERT_ARG(scrollbar, scrollbar);
410    return !scrollbar->currentPos() || scrollbar->currentPos() >= scrollbar->maximum();
411}
412
413void WebPage::gestureDidScroll(const IntSize& size)
414{
415    ASSERT_ARG(size, !size.isZero());
416
417    if (!m_gestureTargetNode || !m_gestureTargetNode->renderer() || !m_gestureTargetNode->renderer()->enclosingLayer())
418        return;
419
420    Scrollbar* verticalScrollbar = 0;
421    if (Frame* frame = m_page->mainFrame()) {
422        if (ScrollView* view = frame->view())
423            verticalScrollbar = view->verticalScrollbar();
424    }
425
426    m_gestureTargetNode->renderer()->enclosingLayer()->scrollByRecursively(size.width(), size.height());
427    bool gestureReachedScrollingLimit = verticalScrollbar && scrollbarAtTopOrBottomOfDocument(verticalScrollbar);
428
429    // FIXME: We really only want to update this state if the state was updated via scrolling the main frame,
430    // not scrolling something in a main frame when the main frame had already reached its scrolling limit.
431
432    if (gestureReachedScrollingLimit == m_gestureReachedScrollingLimit)
433        return;
434
435    send(Messages::WebPageProxy::SetGestureReachedScrollingLimit(gestureReachedScrollingLimit));
436    m_gestureReachedScrollingLimit = gestureReachedScrollingLimit;
437}
438
439void WebPage::gestureDidEnd()
440{
441    m_gestureTargetNode = nullptr;
442}
443
444} // namespace WebKit
445