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