diff options
Diffstat (limited to 'WebKit/chromium/src/WebViewImpl.cpp')
-rw-r--r-- | WebKit/chromium/src/WebViewImpl.cpp | 1434 |
1 files changed, 1032 insertions, 402 deletions
diff --git a/WebKit/chromium/src/WebViewImpl.cpp b/WebKit/chromium/src/WebViewImpl.cpp index ce03523..7bec254 100644 --- a/WebKit/chromium/src/WebViewImpl.cpp +++ b/WebKit/chromium/src/WebViewImpl.cpp @@ -32,35 +32,44 @@ #include "WebViewImpl.h" #include "AutoFillPopupMenuClient.h" -#include "AutocompletePopupMenuClient.h" #include "AXObjectCache.h" +#include "BackForwardListImpl.h" #include "Chrome.h" +#include "ChromiumBridge.h" +#include "ColorSpace.h" +#include "CompositionUnderlineVectorBuilder.h" #include "ContextMenu.h" #include "ContextMenuController.h" #include "ContextMenuItem.h" #include "CSSStyleSelector.h" #include "CSSValueKeywords.h" #include "Cursor.h" +#include "DeviceOrientationClientProxy.h" #include "Document.h" #include "DocumentLoader.h" #include "DOMUtilitiesPrivate.h" #include "DragController.h" +#include "DragScrollTimer.h" #include "DragData.h" #include "Editor.h" #include "EventHandler.h" +#include "Extensions3D.h" #include "FocusController.h" #include "FontDescription.h" #include "FrameLoader.h" #include "FrameTree.h" #include "FrameView.h" #include "GraphicsContext.h" -#include "HitTestResult.h" +#include "GraphicsContext3D.h" +#include "GraphicsContext3DInternal.h" #include "HTMLInputElement.h" #include "HTMLMediaElement.h" +#include "HitTestResult.h" #include "HTMLNames.h" #include "Image.h" +#include "ImageBuffer.h" +#include "ImageData.h" #include "InspectorController.h" -#include "IntRect.h" #include "KeyboardCodes.h" #include "KeyboardEvent.h" #include "MIMETypeRegistry.h" @@ -72,8 +81,8 @@ #include "PlatformContextSkia.h" #include "PlatformKeyboardEvent.h" #include "PlatformMouseEvent.h" +#include "PlatformThemeChromiumGtk.h" #include "PlatformWheelEvent.h" -#include "PluginInfoStore.h" #include "PopupMenuChromium.h" #include "PopupMenuClient.h" #include "ProgressTracker.h" @@ -82,31 +91,46 @@ #include "SecurityOrigin.h" #include "SelectionController.h" #include "Settings.h" +#include "SpeechInputClientImpl.h" +#include "Timer.h" #include "TypingCommand.h" +#include "UserGestureIndicator.h" +#include "Vector.h" #include "WebAccessibilityObject.h" #include "WebDevToolsAgentPrivate.h" +#include "WebDevToolsAgentImpl.h" #include "WebDragData.h" #include "WebFrameImpl.h" +#include "WebImage.h" +#include "WebInputElement.h" #include "WebInputEvent.h" #include "WebInputEventConversion.h" +#include "WebKit.h" +#include "WebKitClient.h" #include "WebMediaPlayerAction.h" #include "WebNode.h" +#include "WebPlugin.h" +#include "WebPluginContainerImpl.h" #include "WebPoint.h" #include "WebPopupMenuImpl.h" #include "WebRect.h" +#include "WebRuntimeFeatures.h" #include "WebSettingsImpl.h" #include "WebString.h" #include "WebVector.h" #include "WebViewClient.h" +#include <wtf/RefPtr.h> + +#if PLATFORM(CG) +#include <CoreGraphics/CGContext.h> +#endif #if OS(WINDOWS) -#include "KeyboardCodesWin.h" #include "RenderThemeChromiumWin.h" #else -#if OS(LINUX) +#if OS(LINUX) || OS(FREEBSD) #include "RenderThemeChromiumLinux.h" #endif -#include "KeyboardCodesPosix.h" #include "RenderTheme.h" #endif @@ -116,15 +140,37 @@ using namespace WebCore; +namespace { + +GraphicsContext3D::Attributes getCompositorContextAttributes() +{ + // Explicitly disable antialiasing for the compositor. As of the time of + // this writing, the only platform that supported antialiasing for the + // compositor was Mac OS X, because the on-screen OpenGL context creation + // code paths on Windows and Linux didn't yet have multisampling support. + // Mac OS X essentially always behaves as though it's rendering offscreen. + // Multisampling has a heavy cost especially on devices with relatively low + // fill rate like most notebooks, and the Mac implementation would need to + // be optimized to resolve directly into the IOSurface shared between the + // GPU and browser processes. For these reasons and to avoid platform + // disparities we explicitly disable antialiasing. + GraphicsContext3D::Attributes attributes; + attributes.antialias = false; + return attributes; +} + +} // anonymous namespace + namespace WebKit { // Change the text zoom level by kTextSizeMultiplierRatio each time the user // zooms text in or out (ie., change by 20%). The min and max values limit // text zoom to half and 3x the original text size. These three values match // those in Apple's port in WebKit/WebKit/WebView/WebView.mm -static const double textSizeMultiplierRatio = 1.2; -static const double minTextSizeMultiplier = 0.5; -static const double maxTextSizeMultiplier = 3.0; +const double WebView::textSizeMultiplierRatio = 1.2; +const double WebView::minTextSizeMultiplier = 0.5; +const double WebView::maxTextSizeMultiplier = 3.0; + // The group name identifies a namespace of pages. Page group is used on OSX // for some programs that use HTML views to display things that don't seem like @@ -133,8 +179,8 @@ static const double maxTextSizeMultiplier = 3.0; const char* pageGroupName = "default"; // Used to defer all page activity in cases where the embedder wishes to run -// a nested event loop. -static PageGroupLoadDeferrer* pageGroupLoadDeferrer; +// a nested event loop. Using a stack enables nesting of message loop invocations. +static Vector<PageGroupLoadDeferrer*> pageGroupLoadDeferrerStack; // Ensure that the WebDragOperation enum values stay in sync with the original // DragOperation constants. @@ -149,26 +195,34 @@ COMPILE_ASSERT_MATCHING_ENUM(DragOperationMove); COMPILE_ASSERT_MATCHING_ENUM(DragOperationDelete); COMPILE_ASSERT_MATCHING_ENUM(DragOperationEvery); -// Note that focusOnShow is false so that the suggestions popup is shown not -// activated. We need the page to still have focus so the user can keep typing -// while the popup is showing. -static const PopupContainerSettings suggestionsPopupSettings = { - false, // focusOnShow - false, // setTextOnIndexChange - false, // acceptOnAbandon - true, // loopSelectionNavigation - true, // restrictWidthOfListBox. Same as other browser (Fx, IE, and safari) +static const PopupContainerSettings autoFillPopupSettings = { + false, // setTextOnIndexChange + false, // acceptOnAbandon + true, // loopSelectionNavigation + false, // restrictWidthOfListBox (For security reasons show the entire entry + // so the user doesn't enter information he did not intend to.) // For suggestions, we use the direction of the input field as the direction // of the popup items. The main reason is to keep the display of items in // drop-down the same as the items in the input field. PopupContainerSettings::DOMElementDirection, }; +static bool shouldUseExternalPopupMenus = false; + // WebView ---------------------------------------------------------------- -WebView* WebView::create(WebViewClient* client) +WebView* WebView::create(WebViewClient* client, WebDevToolsAgentClient* devToolsClient) +{ + // Keep runtime flag for device motion turned off until it's implemented. + WebRuntimeFeatures::enableDeviceMotion(false); + + // Pass the WebViewImpl's self-reference to the caller. + return adoptRef(new WebViewImpl(client, devToolsClient)).leakRef(); +} + +void WebView::setUseExternalPopupMenus(bool useExternalPopupMenus) { - return new WebViewImpl(client); + shouldUseExternalPopupMenus = useExternalPopupMenus; } void WebView::updateVisitedLinkState(unsigned long long linkHash) @@ -183,24 +237,23 @@ void WebView::resetVisitedLinkState() void WebView::willEnterModalLoop() { - // It is not valid to nest more than once. - ASSERT(!pageGroupLoadDeferrer); - PageGroup* pageGroup = PageGroup::pageGroup(pageGroupName); ASSERT(pageGroup); - ASSERT(!pageGroup->pages().isEmpty()); - // Pick any page in the page group since we are deferring all pages. - pageGroupLoadDeferrer = new PageGroupLoadDeferrer(*pageGroup->pages().begin(), true); + if (pageGroup->pages().isEmpty()) + pageGroupLoadDeferrerStack.append(static_cast<PageGroupLoadDeferrer*>(0)); + else { + // Pick any page in the page group since we are deferring all pages. + pageGroupLoadDeferrerStack.append(new PageGroupLoadDeferrer(*pageGroup->pages().begin(), true)); + } } void WebView::didExitModalLoop() { - // The embedder must have called willEnterNestedEventLoop. - ASSERT(pageGroupLoadDeferrer); + ASSERT(pageGroupLoadDeferrerStack.size()); - delete pageGroupLoadDeferrer; - pageGroupLoadDeferrer = 0; + delete pageGroupLoadDeferrerStack.last(); + pageGroupLoadDeferrerStack.removeLast(); } void WebViewImpl::initializeMainFrame(WebFrameClient* frameClient) @@ -216,7 +269,7 @@ void WebViewImpl::initializeMainFrame(WebFrameClient* frameClient) SecurityOrigin::setLocalLoadPolicy(SecurityOrigin::AllowLocalLoadsForLocalOnly); } -WebViewImpl::WebViewImpl(WebViewClient* client) +WebViewImpl::WebViewImpl(WebViewClient* client, WebDevToolsAgentClient* devToolsClient) : m_client(client) , m_backForwardListClientImpl(this) , m_chromeClientImpl(this) @@ -229,6 +282,8 @@ WebViewImpl::WebViewImpl(WebViewClient* client) , m_newNavigationLoader(0) #endif , m_zoomLevel(0) + , m_minimumZoomLevel(zoomFactorToZoomLevel(minTextSizeMultiplier)) + , m_maximumZoomLevel(zoomFactorToZoomLevel(maxTextSizeMultiplier)) , m_contextMenuAllowed(false) , m_doingDragAndDrop(false) , m_ignoreInputEvents(false) @@ -240,31 +295,51 @@ WebViewImpl::WebViewImpl(WebViewClient* client) , m_dropEffect(DropEffectDefault) , m_operationsAllowed(WebDragOperationNone) , m_dragOperation(WebDragOperationNone) - , m_suggestionsPopupShowing(false) - , m_suggestionsPopupClient(0) - , m_suggestionsPopup(0) + , m_autoFillPopupShowing(false) + , m_autoFillPopupClient(0) + , m_autoFillPopup(0) , m_isTransparent(false) , m_tabsToLinks(false) + , m_dragScrollTimer(new DragScrollTimer()) +#if USE(ACCELERATED_COMPOSITING) + , m_layerRenderer(0) + , m_isAcceleratedCompositingActive(false) + , m_compositorCreationFailed(false) +#endif +#if ENABLE(INPUT_SPEECH) + , m_speechInputClient(SpeechInputClientImpl::create(client)) +#endif + , m_deviceOrientationClientProxy(new DeviceOrientationClientProxy(client ? client->deviceOrientationClient() : 0)) { // WebKit/win/WebView.cpp does the same thing, except they call the // KJS specific wrapper around this method. We need to have threading // initialized because CollatorICU requires it. WTF::initializeThreading(); + WTF::initializeMainThread(); // set to impossible point so we always get the first mouse pos m_lastMousePosition = WebPoint(-1, -1); - // the page will take ownership of the various clients - m_page.set(new Page(&m_chromeClientImpl, - &m_contextMenuClientImpl, - &m_editorClientImpl, - &m_dragClientImpl, - &m_inspectorClientImpl, - 0, - 0)); + if (devToolsClient) + m_devToolsAgent = new WebDevToolsAgentImpl(this, devToolsClient); + + Page::PageClients pageClients; + pageClients.chromeClient = &m_chromeClientImpl; + pageClients.contextMenuClient = &m_contextMenuClientImpl; + pageClients.editorClient = &m_editorClientImpl; + pageClients.dragClient = &m_dragClientImpl; + pageClients.inspectorClient = &m_inspectorClientImpl; +#if ENABLE(INPUT_SPEECH) + pageClients.speechInputClient = m_speechInputClient.get(); +#endif + pageClients.deviceOrientationClient = m_deviceOrientationClientProxy.get(); + + m_page.set(new Page(pageClients)); - m_page->backForwardList()->setClient(&m_backForwardListClientImpl); + static_cast<BackForwardListImpl*>(m_page->backForwardList())->setClient(&m_backForwardListClientImpl); m_page->setGroupName(pageGroupName); + + m_inspectorSettingsMap.set(new SettingsMap); } WebViewImpl::~WebViewImpl() @@ -326,19 +401,34 @@ void WebViewImpl::mouseDown(const WebMouseEvent& event) if (!mainFrameImpl() || !mainFrameImpl()->frameView()) return; + // If there is a select popup open, close it as the user is clicking on + // the page (outside of the popup). We also save it so we can prevent a + // click on the select element from immediately reopening the popup. + RefPtr<WebCore::PopupContainer> selectPopup; + if (event.button == WebMouseEvent::ButtonLeft) { + selectPopup = m_selectPopup; + hideSelectPopup(); + ASSERT(!m_selectPopup); + } + m_lastMouseDownPoint = WebPoint(event.x, event.y); - // If a text field that has focus is clicked again, we should display the - // suggestions popup. RefPtr<Node> clickedNode; if (event.button == WebMouseEvent::ButtonLeft) { + IntPoint point(event.x, event.y); + point = m_page->mainFrame()->view()->windowToContents(point); + HitTestResult result(m_page->mainFrame()->eventHandler()->hitTestResultAtPoint(point, false)); + Node* hitNode = result.innerNonSharedNode(); + + // Take capture on a mouse down on a plugin so we can send it mouse events. + if (hitNode && hitNode->renderer() && hitNode->renderer()->isEmbeddedObject()) + m_mouseCaptureNode = hitNode; + + // If a text field that has focus is clicked again, we should display the + // AutoFill popup. RefPtr<Node> focusedNode = focusedWebCoreNode(); if (focusedNode.get() && toHTMLInputElement(focusedNode.get())) { - IntPoint point(event.x, event.y); - point = m_page->mainFrame()->view()->windowToContents(point); - HitTestResult result(point); - result = m_page->mainFrame()->eventHandler()->hitTestResultAtPoint(point, false); - if (result.innerNonSharedNode() == focusedNode) { + if (hitNode == focusedNode) { // Already focused text field was clicked, let's remember this. If // focus has not changed after the mouse event is processed, we'll // trigger the autocomplete. @@ -347,14 +437,23 @@ void WebViewImpl::mouseDown(const WebMouseEvent& event) } } + mainFrameImpl()->frame()->loader()->resetMultipleFormSubmissionProtection(); + mainFrameImpl()->frame()->eventHandler()->handleMousePressEvent( PlatformMouseEventBuilder(mainFrameImpl()->frameView(), event)); if (clickedNode.get() && clickedNode == focusedWebCoreNode()) { - // Focus has not changed, show the suggestions popup. + // Focus has not changed, show the AutoFill popup. static_cast<EditorClientImpl*>(m_page->editorClient())-> showFormAutofillForNode(clickedNode.get()); } + if (m_selectPopup && m_selectPopup == selectPopup) { + // That click triggered a select popup which is the same as the one that + // was showing before the click. It means the user clicked the select + // while the popup was showing, and as a result we first closed then + // immediately reopened the select popup. It needs to be closed. + hideSelectPopup(); + } // Dispatch the contextmenu event regardless of if the click was swallowed. // On Windows, we handle it on mouse up, not down. @@ -363,7 +462,7 @@ void WebViewImpl::mouseDown(const WebMouseEvent& event) || (event.button == WebMouseEvent::ButtonLeft && event.modifiers & WebMouseEvent::ControlKey)) mouseContextMenu(event); -#elif OS(LINUX) +#elif OS(LINUX) || OS(FREEBSD) if (event.button == WebMouseEvent::ButtonRight) mouseContextMenu(event); #endif @@ -402,7 +501,7 @@ void WebViewImpl::mouseUp(const WebMouseEvent& event) if (!mainFrameImpl() || !mainFrameImpl()->frameView()) return; -#if OS(LINUX) +#if OS(LINUX) || OS(FREEBSD) // If the event was a middle click, attempt to copy text into the focused // frame. We execute this before we let the page have a go at the event // because the page may change what is focused during in its event handler. @@ -426,7 +525,7 @@ void WebViewImpl::mouseUp(const WebMouseEvent& event) IntPoint contentPoint = view->windowToContents(clickPoint); HitTestResult hitTestResult = focused->eventHandler()->hitTestResultAtPoint(contentPoint, false, false, ShouldHitTestScrollbars); // We don't want to send a paste when middle clicking a scroll bar or a - // link (which will navigate later in the code). The main scrollbars + // link (which will navigate later in the code). The main scrollbars // have to be handled separately. if (!hitTestResult.scrollbar() && !hitTestResult.isLiveLink() && focused && !view->scrollbarAtPoint(clickPoint)) { Editor* editor = focused->editor(); @@ -439,7 +538,6 @@ void WebViewImpl::mouseUp(const WebMouseEvent& event) } #endif - mouseCaptureLost(); mainFrameImpl()->frame()->eventHandler()->handleMouseReleaseEvent( PlatformMouseEventBuilder(mainFrameImpl()->frameView(), event)); @@ -451,10 +549,10 @@ void WebViewImpl::mouseUp(const WebMouseEvent& event) #endif } -void WebViewImpl::mouseWheel(const WebMouseWheelEvent& event) +bool WebViewImpl::mouseWheel(const WebMouseWheelEvent& event) { PlatformWheelEventBuilder platformEvent(mainFrameImpl()->frameView(), event); - mainFrameImpl()->frame()->eventHandler()->handleWheelEvent(platformEvent); + return mainFrameImpl()->frame()->eventHandler()->handleWheelEvent(platformEvent); } bool WebViewImpl::keyEvent(const WebKeyboardEvent& event) @@ -471,6 +569,10 @@ bool WebViewImpl::keyEvent(const WebKeyboardEvent& event) // event. m_suppressNextKeypressEvent = false; + // Give any select popup a chance at consuming the key event. + if (selectPopupHandleKeyEvent(event)) + return true; + // Give Autocomplete a chance to consume the key events it is interested in. if (autocompleteHandleKeyEvent(event)) return true; @@ -483,21 +585,21 @@ bool WebViewImpl::keyEvent(const WebKeyboardEvent& event) if (!handler) return keyEventDefault(event); -#if OS(WINDOWS) || OS(LINUX) +#if OS(WINDOWS) || OS(LINUX) || OS(FREEBSD) const WebInputEvent::Type contextMenuTriggeringEventType = #if OS(WINDOWS) WebInputEvent::KeyUp; -#elif OS(LINUX) +#elif OS(LINUX) || OS(FREEBSD) WebInputEvent::RawKeyDown; #endif - if (((!event.modifiers && (event.windowsKeyCode == VKEY_APPS)) - || ((event.modifiers == WebInputEvent::ShiftKey) && (event.windowsKeyCode == VKEY_F10))) - && event.type == contextMenuTriggeringEventType) { + bool isUnmodifiedMenuKey = !(event.modifiers & WebInputEvent::InputModifiers) && event.windowsKeyCode == VKEY_APPS; + bool isShiftF10 = event.modifiers == WebInputEvent::ShiftKey && event.windowsKeyCode == VKEY_F10; + if ((isUnmodifiedMenuKey || isShiftF10) && event.type == contextMenuTriggeringEventType) { sendContextMenuEvent(event); return true; } -#endif +#endif // OS(WINDOWS) || OS(LINUX) || OS(FREEBSD) // It's not clear if we should continue after detecting a capslock keypress. // I'll err on the side of continuing, which is the pre-existing behaviour. @@ -507,17 +609,30 @@ bool WebViewImpl::keyEvent(const WebKeyboardEvent& event) PlatformKeyboardEventBuilder evt(event); if (handler->keyEvent(evt)) { - if (WebInputEvent::RawKeyDown == event.type) - m_suppressNextKeypressEvent = true; + if (WebInputEvent::RawKeyDown == event.type) { + // Suppress the next keypress event unless the focused node is a plug-in node. + // (Flash needs these keypress events to handle non-US keyboards.) + Node* node = frame->document()->focusedNode(); + if (!node || !node->renderer() || !node->renderer()->isEmbeddedObject()) + m_suppressNextKeypressEvent = true; + } return true; } return keyEventDefault(event); } +bool WebViewImpl::selectPopupHandleKeyEvent(const WebKeyboardEvent& event) +{ + if (!m_selectPopup) + return false; + + return m_selectPopup->handleKeyEvent(PlatformKeyboardEventBuilder(event)); +} + bool WebViewImpl::autocompleteHandleKeyEvent(const WebKeyboardEvent& event) { - if (!m_suggestionsPopupShowing + if (!m_autoFillPopupShowing // Home and End should be left to the text field to process. || event.windowsKeyCode == VKEY_HOME || event.windowsKeyCode == VKEY_END) @@ -525,7 +640,7 @@ bool WebViewImpl::autocompleteHandleKeyEvent(const WebKeyboardEvent& event) // Pressing delete triggers the removal of the selected suggestion from the DB. if (event.windowsKeyCode == VKEY_DELETE - && m_suggestionsPopup->selectedIndex() != -1) { + && m_autoFillPopup->selectedIndex() != -1) { Node* node = focusedWebCoreNode(); if (!node || (node->nodeType() != Node::ELEMENT_NODE)) { ASSERT_NOT_REACHED(); @@ -537,22 +652,25 @@ bool WebViewImpl::autocompleteHandleKeyEvent(const WebKeyboardEvent& event) return false; } - int selectedIndex = m_suggestionsPopup->selectedIndex(); - HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(element); - WebString name = inputElement->name(); - WebString value = m_autocompletePopupClient->itemText(selectedIndex); + int selectedIndex = m_autoFillPopup->selectedIndex(); + + if (!m_autoFillPopupClient->canRemoveSuggestionAtIndex(selectedIndex)) + return false; + + WebString name = WebInputElement(static_cast<HTMLInputElement*>(element)).nameForAutofill(); + WebString value = m_autoFillPopupClient->itemText(selectedIndex); m_client->removeAutofillSuggestions(name, value); // Update the entries in the currently showing popup to reflect the // deletion. - m_autocompletePopupClient->removeSuggestionAtIndex(selectedIndex); - refreshSuggestionsPopup(); + m_autoFillPopupClient->removeSuggestionAtIndex(selectedIndex); + refreshAutoFillPopup(); return false; } - if (!m_suggestionsPopup->isInterestedInEventForKey(event.windowsKeyCode)) + if (!m_autoFillPopup->isInterestedInEventForKey(event.windowsKeyCode)) return false; - if (m_suggestionsPopup->handleKeyEvent(PlatformKeyboardEventBuilder(event))) { + if (m_autoFillPopup->handleKeyEvent(PlatformKeyboardEventBuilder(event))) { // We need to ignore the next Char event after this otherwise pressing // enter when selecting an item in the menu will go to the page. if (WebInputEvent::RawKeyDown == event.type) @@ -604,62 +722,21 @@ bool WebViewImpl::charEvent(const WebKeyboardEvent& event) return true; } -// The WebViewImpl::SendContextMenuEvent function is based on the Webkit -// function -// bool WebView::handleContextMenuEvent(WPARAM wParam, LPARAM lParam) in -// webkit\webkit\win\WebView.cpp. The only significant change in this -// function is the code to convert from a Keyboard event to the Right -// Mouse button up event. -// -// This function is an ugly copy/paste and should be cleaned up when the -// WebKitWin version is cleaned: https://bugs.webkit.org/show_bug.cgi?id=20438 -#if OS(WINDOWS) || OS(LINUX) -// FIXME: implement on Mac -bool WebViewImpl::sendContextMenuEvent(const WebKeyboardEvent& event) +#if ENABLE(TOUCH_EVENTS) +bool WebViewImpl::touchEvent(const WebTouchEvent& event) { - static const int kContextMenuMargin = 1; - Frame* mainFrameImpl = page()->mainFrame(); - FrameView* view = mainFrameImpl->view(); - if (!view) + if (!mainFrameImpl() || !mainFrameImpl()->frameView()) return false; - IntPoint coords(-1, -1); -#if OS(WINDOWS) - int rightAligned = ::GetSystemMetrics(SM_MENUDROPALIGNMENT); -#else - int rightAligned = 0; + PlatformTouchEventBuilder touchEventBuilder(mainFrameImpl()->frameView(), event); + return mainFrameImpl()->frame()->eventHandler()->handleTouchEvent(touchEventBuilder); +} #endif - IntPoint location; - - - Frame* focusedFrame = page()->focusController()->focusedOrMainFrame(); - Node* focusedNode = focusedFrame->document()->focusedNode(); - Position start = mainFrameImpl->selection()->selection().start(); - - if (focusedFrame->editor() && focusedFrame->editor()->canEdit() && start.node()) { - RenderObject* renderer = start.node()->renderer(); - if (!renderer) - return false; - - RefPtr<Range> selection = mainFrameImpl->selection()->toNormalizedRange(); - IntRect firstRect = mainFrameImpl->firstRectForRange(selection.get()); - - int x = rightAligned ? firstRect.right() : firstRect.x(); - location = IntPoint(x, firstRect.bottom()); - } else if (focusedNode) - location = focusedNode->getRect().bottomLeft(); - else { - location = IntPoint( - rightAligned ? view->contentsWidth() - kContextMenuMargin : kContextMenuMargin, - kContextMenuMargin); - } - - location = view->contentsToWindow(location); - // FIXME: The IntSize(0, -1) is a hack to get the hit-testing to result in - // the selected element. Ideally we'd have the position of a context menu - // event be separate from its target node. - coords = location + IntSize(0, -1); +#if OS(WINDOWS) || OS(LINUX) || OS(FREEBSD) +// Mac has no way to open a context menu based on a keyboard event. +bool WebViewImpl::sendContextMenuEvent(const WebKeyboardEvent& event) +{ // The contextMenuController() holds onto the last context menu that was // popped up on the page until a new one is created. We need to clear // this menu before propagating the event through the DOM so that we can @@ -668,17 +745,9 @@ bool WebViewImpl::sendContextMenuEvent(const WebKeyboardEvent& event) // not run. page()->contextMenuController()->clearContextMenu(); - focusedFrame->view()->setCursor(pointerCursor()); - WebMouseEvent mouseEvent; - mouseEvent.button = WebMouseEvent::ButtonRight; - mouseEvent.x = coords.x(); - mouseEvent.y = coords.y(); - mouseEvent.type = WebInputEvent::MouseUp; - - PlatformMouseEventBuilder platformEvent(view, mouseEvent); - m_contextMenuAllowed = true; - bool handled = focusedFrame->eventHandler()->sendContextMenuEvent(platformEvent); + Frame* focusedFrame = page()->focusController()->focusedOrMainFrame(); + bool handled = focusedFrame->eventHandler()->sendContextMenuEventForKey(); m_contextMenuAllowed = false; return handled; } @@ -733,45 +802,59 @@ bool WebViewImpl::scrollViewWithKeyboard(int keyCode, int modifiers) { ScrollDirection scrollDirection; ScrollGranularity scrollGranularity; + if (!mapKeyCodeForScroll(keyCode, &scrollDirection, &scrollGranularity)) + return false; + return propagateScroll(scrollDirection, scrollGranularity); +} +bool WebViewImpl::mapKeyCodeForScroll(int keyCode, + WebCore::ScrollDirection* scrollDirection, + WebCore::ScrollGranularity* scrollGranularity) +{ switch (keyCode) { case VKEY_LEFT: - scrollDirection = ScrollLeft; - scrollGranularity = ScrollByLine; + *scrollDirection = ScrollLeft; + *scrollGranularity = ScrollByLine; break; case VKEY_RIGHT: - scrollDirection = ScrollRight; - scrollGranularity = ScrollByLine; + *scrollDirection = ScrollRight; + *scrollGranularity = ScrollByLine; break; case VKEY_UP: - scrollDirection = ScrollUp; - scrollGranularity = ScrollByLine; + *scrollDirection = ScrollUp; + *scrollGranularity = ScrollByLine; break; case VKEY_DOWN: - scrollDirection = ScrollDown; - scrollGranularity = ScrollByLine; + *scrollDirection = ScrollDown; + *scrollGranularity = ScrollByLine; break; case VKEY_HOME: - scrollDirection = ScrollUp; - scrollGranularity = ScrollByDocument; + *scrollDirection = ScrollUp; + *scrollGranularity = ScrollByDocument; break; case VKEY_END: - scrollDirection = ScrollDown; - scrollGranularity = ScrollByDocument; + *scrollDirection = ScrollDown; + *scrollGranularity = ScrollByDocument; break; case VKEY_PRIOR: // page up - scrollDirection = ScrollUp; - scrollGranularity = ScrollByPage; + *scrollDirection = ScrollUp; + *scrollGranularity = ScrollByPage; break; case VKEY_NEXT: // page down - scrollDirection = ScrollDown; - scrollGranularity = ScrollByPage; + *scrollDirection = ScrollDown; + *scrollGranularity = ScrollByPage; break; default: return false; } - return propagateScroll(scrollDirection, scrollGranularity); + return true; +} + +void WebViewImpl::hideSelectPopup() +{ + if (m_selectPopup.get()) + m_selectPopup->hidePopup(); } bool WebViewImpl::propagateScroll(ScrollDirection scrollDirection, @@ -781,18 +864,39 @@ bool WebViewImpl::propagateScroll(ScrollDirection scrollDirection, if (!frame) return false; - bool scrollHandled = - frame->eventHandler()->scrollOverflow(scrollDirection, - scrollGranularity); + bool scrollHandled = frame->eventHandler()->scrollOverflow(scrollDirection, scrollGranularity); Frame* currentFrame = frame; while (!scrollHandled && currentFrame) { - scrollHandled = currentFrame->view()->scroll(scrollDirection, - scrollGranularity); + scrollHandled = currentFrame->view()->scroll(scrollDirection, scrollGranularity); currentFrame = currentFrame->tree()->parent(); } return scrollHandled; } +void WebViewImpl::popupOpened(WebCore::PopupContainer* popupContainer) +{ + if (popupContainer->popupType() == WebCore::PopupContainer::Select) { + ASSERT(!m_selectPopup); + m_selectPopup = popupContainer; + } +} + +void WebViewImpl::popupClosed(WebCore::PopupContainer* popupContainer) +{ + if (popupContainer->popupType() == WebCore::PopupContainer::Select) { + ASSERT(m_selectPopup.get()); + m_selectPopup = 0; + } +} + +void WebViewImpl::hideAutoFillPopup() +{ + if (m_autoFillPopupShowing) { + m_autoFillPopup->hidePopup(); + m_autoFillPopupShowing = false; + } +} + Frame* WebViewImpl::focusedWebCoreFrame() { return m_page.get() ? m_page->focusController()->focusedOrMainFrame() : 0; @@ -846,8 +950,20 @@ void WebViewImpl::resize(const WebSize& newSize) if (m_client) { WebRect damagedRect(0, 0, m_size.width, m_size.height); - m_client->didInvalidateRect(damagedRect); + if (isAcceleratedCompositingActive()) { +#if USE(ACCELERATED_COMPOSITING) + invalidateRootLayerRect(damagedRect); +#endif + } else + m_client->didInvalidateRect(damagedRect); + } + +#if USE(ACCELERATED_COMPOSITING) + if (m_layerRenderer && isAcceleratedCompositingActive()) { + m_layerRenderer->resizeOnscreenContent(IntSize(std::max(1, m_size.width), + std::max(1, m_size.height))); } +#endif } void WebViewImpl::layout() @@ -871,19 +987,99 @@ void WebViewImpl::layout() } } +#if USE(ACCELERATED_COMPOSITING) +void WebViewImpl::doPixelReadbackToCanvas(WebCanvas* canvas, const IntRect& rect) +{ + ASSERT(rect.right() <= m_layerRenderer->rootLayerTextureSize().width() + && rect.bottom() <= m_layerRenderer->rootLayerTextureSize().height()); + +#if PLATFORM(SKIA) + PlatformContextSkia context(canvas); + + // PlatformGraphicsContext is actually a pointer to PlatformContextSkia + GraphicsContext gc(reinterpret_cast<PlatformGraphicsContext*>(&context)); + int bitmapHeight = canvas->getDevice()->accessBitmap(false).height(); +#elif PLATFORM(CG) + GraphicsContext gc(canvas); + int bitmapHeight = CGBitmapContextGetHeight(reinterpret_cast<CGContextRef>(canvas)); +#else + notImplemented(); +#endif + // Compute rect to sample from inverted GPU buffer. + IntRect invertRect(rect.x(), bitmapHeight - rect.bottom(), rect.width(), rect.height()); + + OwnPtr<ImageBuffer> imageBuffer(ImageBuffer::create(rect.size())); + RefPtr<ImageData> imageData(ImageData::create(rect.width(), rect.height())); + if (imageBuffer.get() && imageData.get()) { + m_layerRenderer->getFramebufferPixels(imageData->data()->data()->data(), invertRect); + imageBuffer->putPremultipliedImageData(imageData.get(), IntRect(IntPoint(), rect.size()), IntPoint()); + gc.save(); + gc.translate(FloatSize(0.0f, bitmapHeight)); + gc.scale(FloatSize(1.0f, -1.0f)); + // Use invertRect in next line, so that transform above inverts it back to + // desired destination rect. + gc.drawImageBuffer(imageBuffer.get(), ColorSpaceDeviceRGB, invertRect.location()); + gc.restore(); + } +} +#endif + void WebViewImpl::paint(WebCanvas* canvas, const WebRect& rect) { - WebFrameImpl* webframe = mainFrameImpl(); - if (webframe) - webframe->paint(canvas, rect); + if (isAcceleratedCompositingActive()) { +#if USE(ACCELERATED_COMPOSITING) + doComposite(); + + // If a canvas was passed in, we use it to grab a copy of the + // freshly-rendered pixels. + if (canvas) { + // Clip rect to the confines of the rootLayerTexture. + IntRect resizeRect(rect); + resizeRect.intersect(IntRect(IntPoint(), m_layerRenderer->rootLayerTextureSize())); + doPixelReadbackToCanvas(canvas, resizeRect); + } +#endif + } else { + WebFrameImpl* webframe = mainFrameImpl(); + if (webframe) + webframe->paint(canvas, rect); + } +} + +void WebViewImpl::themeChanged() +{ + if (!page()) + return; + FrameView* view = page()->mainFrame()->view(); + + WebRect damagedRect(0, 0, m_size.width, m_size.height); + view->invalidateRect(damagedRect); +} + +void WebViewImpl::composite(bool finish) +{ +#if USE(ACCELERATED_COMPOSITING) + doComposite(); + + // Finish if requested. + if (finish) + m_layerRenderer->finish(); + + // Put result onscreen. + m_layerRenderer->present(); + + GraphicsContext3D* context = m_layerRenderer->context(); + if (context->getExtensions()->getGraphicsResetStatusARB() != GraphicsContext3D::NO_ERROR) + reallocateRenderer(); +#endif } -// FIXME: m_currentInputEvent should be removed once ChromeClient::show() can -// get the current-event information from WebCore. const WebInputEvent* WebViewImpl::m_currentInputEvent = 0; bool WebViewImpl::handleInputEvent(const WebInputEvent& inputEvent) { + UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture); + // If we've started a drag and drop operation, ignore input events until // we're done. if (m_doingDragAndDrop) @@ -892,14 +1088,41 @@ bool WebViewImpl::handleInputEvent(const WebInputEvent& inputEvent) if (m_ignoreInputEvents) return true; - // FIXME: Remove m_currentInputEvent. - // This only exists to allow ChromeClient::show() to know which mouse button - // triggered a window.open event. - // Safari must perform a similar hack, ours is in our WebKit glue layer - // theirs is in the application. This should go when WebCore can be fixed - // to pass more event information to ChromeClient::show() m_currentInputEvent = &inputEvent; + if (m_mouseCaptureNode.get() && WebInputEvent::isMouseEventType(inputEvent.type)) { + // Save m_mouseCaptureNode since mouseCaptureLost() will clear it. + RefPtr<Node> node = m_mouseCaptureNode; + + // Not all platforms call mouseCaptureLost() directly. + if (inputEvent.type == WebInputEvent::MouseUp) + mouseCaptureLost(); + + AtomicString eventType; + switch (inputEvent.type) { + case WebInputEvent::MouseMove: + eventType = eventNames().mousemoveEvent; + break; + case WebInputEvent::MouseLeave: + eventType = eventNames().mouseoutEvent; + break; + case WebInputEvent::MouseDown: + eventType = eventNames().mousedownEvent; + break; + case WebInputEvent::MouseUp: + eventType = eventNames().mouseupEvent; + break; + default: + ASSERT_NOT_REACHED(); + } + + node->dispatchMouseEvent( + PlatformMouseEventBuilder(mainFrameImpl()->frameView(), *static_cast<const WebMouseEvent*>(&inputEvent)), + eventType, static_cast<const WebMouseEvent*>(&inputEvent)->clickCount); + m_currentInputEvent = 0; + return true; + } + bool handled = true; // FIXME: WebKit seems to always return false on mouse events processing @@ -915,7 +1138,7 @@ bool WebViewImpl::handleInputEvent(const WebInputEvent& inputEvent) break; case WebInputEvent::MouseWheel: - mouseWheel(*static_cast<const WebMouseWheelEvent*>(&inputEvent)); + handled = mouseWheel(*static_cast<const WebMouseWheelEvent*>(&inputEvent)); break; case WebInputEvent::MouseDown: @@ -936,6 +1159,15 @@ bool WebViewImpl::handleInputEvent(const WebInputEvent& inputEvent) handled = charEvent(*static_cast<const WebKeyboardEvent*>(&inputEvent)); break; +#if ENABLE(TOUCH_EVENTS) + case WebInputEvent::TouchStart: + case WebInputEvent::TouchMove: + case WebInputEvent::TouchEnd: + case WebInputEvent::TouchCancel: + handled = touchEvent(*static_cast<const WebTouchEvent*>(&inputEvent)); + break; +#endif + default: handled = false; } @@ -947,6 +1179,7 @@ bool WebViewImpl::handleInputEvent(const WebInputEvent& inputEvent) void WebViewImpl::mouseCaptureLost() { + m_mouseCaptureNode = 0; } void WebViewImpl::setFocus(bool enable) @@ -981,7 +1214,8 @@ void WebViewImpl::setFocus(bool enable) } m_imeAcceptEvents = true; } else { - hideSuggestionsPopup(); + hideAutoFillPopup(); + hideSelectPopup(); // Clear focus on the currently focused frame if any. if (!m_page.get()) @@ -1002,11 +1236,11 @@ void WebViewImpl::setFocus(bool enable) } } -bool WebViewImpl::handleCompositionEvent(WebCompositionCommand command, - int cursorPosition, - int targetStart, - int targetEnd, - const WebString& imeString) +bool WebViewImpl::setComposition( + const WebString& text, + const WebVector<WebCompositionUnderline>& underlines, + int selectionStart, + int selectionEnd) { Frame* focused = focusedWebCoreFrame(); if (!focused || !m_imeAcceptEvents) @@ -1014,13 +1248,12 @@ bool WebViewImpl::handleCompositionEvent(WebCompositionCommand command, Editor* editor = focused->editor(); if (!editor) return false; - if (!editor->canEdit()) { - // The input focus has been moved to another WebWidget object. - // We should use this |editor| object only to complete the ongoing - // composition. - if (!editor->hasComposition()) - return false; - } + + // The input focus has been moved to another WebWidget object. + // We should use this |editor| object only to complete the ongoing + // composition. + if (!editor->canEdit() && !editor->hasComposition()) + return false; // We should verify the parent node of this IME composition node are // editable because JavaScript may delete a parent node of the composition @@ -1033,7 +1266,9 @@ bool WebViewImpl::handleCompositionEvent(WebCompositionCommand command, return false; } - if (command == WebCompositionCommandDiscard) { + // If we're not going to fire a keypress event, then the keydown event was + // canceled. In that case, cancel any existing composition. + if (text.isEmpty() || m_suppressNextKeypressEvent) { // A browser process sent an IPC message which does not contain a valid // string, which means an ongoing composition has been canceled. // If the ongoing composition has been canceled, replace the ongoing @@ -1041,81 +1276,102 @@ bool WebViewImpl::handleCompositionEvent(WebCompositionCommand command, String emptyString; Vector<CompositionUnderline> emptyUnderlines; editor->setComposition(emptyString, emptyUnderlines, 0, 0); - } else { - // A browser process sent an IPC message which contains a string to be - // displayed in this Editor object. - // To display the given string, set the given string to the - // m_compositionNode member of this Editor object and display it. - if (targetStart < 0) - targetStart = 0; - if (targetEnd < 0) - targetEnd = static_cast<int>(imeString.length()); - String compositionString(imeString); - // Create custom underlines. - // To emphasize the selection, the selected region uses a solid black - // for its underline while other regions uses a pale gray for theirs. - Vector<CompositionUnderline> underlines(3); - underlines[0].startOffset = 0; - underlines[0].endOffset = targetStart; - underlines[0].thick = true; - underlines[0].color.setRGB(0xd3, 0xd3, 0xd3); - underlines[1].startOffset = targetStart; - underlines[1].endOffset = targetEnd; - underlines[1].thick = true; - underlines[1].color.setRGB(0x00, 0x00, 0x00); - underlines[2].startOffset = targetEnd; - underlines[2].endOffset = static_cast<int>(imeString.length()); - underlines[2].thick = true; - underlines[2].color.setRGB(0xd3, 0xd3, 0xd3); - // When we use custom underlines, WebKit ("InlineTextBox.cpp" Line 282) - // prevents from writing a text in between 'selectionStart' and - // 'selectionEnd' somehow. - // Therefore, we use the 'cursorPosition' for these arguments so that - // there are not any characters in the above region. - editor->setComposition(compositionString, underlines, - cursorPosition, cursorPosition); - // The given string is a result string, which means the ongoing - // composition has been completed. I have to call the - // Editor::confirmCompletion() and complete this composition. - if (command == WebCompositionCommandConfirm) - editor->confirmComposition(); + return text.isEmpty(); } + // When the range of composition underlines overlap with the range between + // selectionStart and selectionEnd, WebKit somehow won't paint the selection + // at all (see InlineTextBox::paint() function in InlineTextBox.cpp). + // But the selection range actually takes effect. + editor->setComposition(String(text), + CompositionUnderlineVectorBuilder(underlines), + selectionStart, selectionEnd); + return editor->hasComposition(); } -bool WebViewImpl::queryCompositionStatus(bool* enableIME, WebRect* caretRect) +bool WebViewImpl::confirmComposition() +{ + Frame* focused = focusedWebCoreFrame(); + if (!focused || !m_imeAcceptEvents) + return false; + Editor* editor = focused->editor(); + if (!editor || !editor->hasComposition()) + return false; + + // We should verify the parent node of this IME composition node are + // editable because JavaScript may delete a parent node of the composition + // node. In this case, WebKit crashes while deleting texts from the parent + // node, which doesn't exist any longer. + PassRefPtr<Range> range = editor->compositionRange(); + if (range) { + const Node* node = range->startPosition().node(); + if (!node || !node->isContentEditable()) + return false; + } + + editor->confirmComposition(); + return true; +} + +WebTextInputType WebViewImpl::textInputType() { - // Store whether the selected node needs IME and the caret rectangle. - // This process consists of the following four steps: - // 1. Retrieve the selection controller of the focused frame; - // 2. Retrieve the caret rectangle from the controller; - // 3. Convert the rectangle, which is relative to the parent view, to the - // one relative to the client window, and; - // 4. Store the converted rectangle. + WebTextInputType type = WebTextInputTypeNone; const Frame* focused = focusedWebCoreFrame(); if (!focused) - return false; + return type; const Editor* editor = focused->editor(); if (!editor || !editor->canEdit()) - return false; + return type; SelectionController* controller = focused->selection(); if (!controller) - return false; + return type; const Node* node = controller->start().node(); if (!node) - return false; + return type; + + // FIXME: Support more text input types when necessary, eg. Number, + // Date, Email, URL, etc. + if (controller->isInPasswordField()) + type = WebTextInputTypePassword; + else if (node->shouldUseInputMethod()) + type = WebTextInputTypeText; - *enableIME = node->shouldUseInputMethod() && !controller->isInPasswordField(); - const FrameView* view = node->document()->view(); + return type; +} + +WebRect WebViewImpl::caretOrSelectionBounds() +{ + WebRect rect; + const Frame* focused = focusedWebCoreFrame(); + if (!focused) + return rect; + + SelectionController* controller = focused->selection(); + if (!controller) + return rect; + + const FrameView* view = focused->view(); if (!view) - return false; + return rect; - *caretRect = view->contentsToWindow(controller->absoluteCaretBounds()); - return true; + const Node* node = controller->start().node(); + if (!node || !node->renderer()) + return rect; + + if (controller->isCaret()) + rect = view->contentsToWindow(controller->absoluteCaretBounds()); + else if (controller->isRange()) { + node = controller->end().node(); + if (!node || !node->renderer()) + return rect; + RefPtr<Range> range = controller->toNormalizedRange(); + rect = view->contentsToWindow(focused->editor()->firstRectForRange(range.get())); + } + return rect; } void WebViewImpl::setTextDirection(WebTextDirection direction) @@ -1151,6 +1407,15 @@ void WebViewImpl::setTextDirection(WebTextDirection direction) } } +bool WebViewImpl::isAcceleratedCompositingActive() const +{ +#if USE(ACCELERATED_COMPOSITING) + return m_isAcceleratedCompositingActive; +#else + return false; +#endif +} + // WebView -------------------------------------------------------------------- WebSettings* WebViewImpl::settings() @@ -1166,7 +1431,7 @@ WebString WebViewImpl::pageEncoding() const if (!m_page.get()) return WebString(); - return m_page->mainFrame()->loader()->encoding(); + return m_page->mainFrame()->loader()->writer()->encoding(); } void WebViewImpl::setPageEncoding(const WebString& encodingName) @@ -1191,7 +1456,7 @@ bool WebViewImpl::dispatchBeforeUnloadEvent() if (!frame) return true; - return frame->shouldClose(); + return frame->loader()->shouldClose(); } void WebViewImpl::dispatchUnloadEvent() @@ -1249,6 +1514,10 @@ void WebViewImpl::setInitialFocus(bool reverse) keyboardEvent.windowsKeyCode = 0x09; PlatformKeyboardEventBuilder platformEvent(keyboardEvent); RefPtr<KeyboardEvent> webkitEvent = KeyboardEvent::create(platformEvent, 0); + + Frame* frame = page()->focusController()->focusedOrMainFrame(); + if (Document* document = frame->document()) + document->setFocusedNode(0); page()->focusController()->setInitialFocus( reverse ? FocusDirectionBackward : FocusDirectionForward, webkitEvent.get()); @@ -1288,25 +1557,71 @@ void WebViewImpl::clearFocusedNode() } } -int WebViewImpl::zoomLevel() +void WebViewImpl::scrollFocusedNodeIntoView() +{ + Node* focusedNode = focusedWebCoreNode(); + if (focusedNode && focusedNode->isElementNode()) { + Element* elementNode = static_cast<Element*>(focusedNode); + elementNode->scrollIntoViewIfNeeded(true); + } +} + +double WebViewImpl::zoomLevel() { return m_zoomLevel; } -int WebViewImpl::setZoomLevel(bool textOnly, int zoomLevel) +double WebViewImpl::setZoomLevel(bool textOnly, double zoomLevel) { - float zoomFactor = static_cast<float>( - std::max(std::min(std::pow(textSizeMultiplierRatio, zoomLevel), - maxTextSizeMultiplier), - minTextSizeMultiplier)); - Frame* frame = mainFrameImpl()->frame(); - if (zoomFactor != frame->zoomFactor()) { + if (zoomLevel < m_minimumZoomLevel) + m_zoomLevel = m_minimumZoomLevel; + else if (zoomLevel > m_maximumZoomLevel) + m_zoomLevel = m_maximumZoomLevel; + else m_zoomLevel = zoomLevel; - frame->setZoomFactor(zoomFactor, textOnly); + + Frame* frame = mainFrameImpl()->frame(); + WebPluginContainerImpl* pluginContainer = WebFrameImpl::pluginContainerFromFrame(frame); + if (pluginContainer) + pluginContainer->plugin()->setZoomLevel(m_zoomLevel, textOnly); + else { + double zoomFactor = zoomLevelToZoomFactor(m_zoomLevel); + if (textOnly) + frame->setPageAndTextZoomFactors(1, zoomFactor); + else + frame->setPageAndTextZoomFactors(zoomFactor, 1); } return m_zoomLevel; } +void WebViewImpl::zoomLimitsChanged(double minimumZoomLevel, + double maximumZoomLevel) +{ + m_minimumZoomLevel = minimumZoomLevel; + m_maximumZoomLevel = maximumZoomLevel; + m_client->zoomLimitsChanged(m_minimumZoomLevel, m_maximumZoomLevel); +} + +void WebViewImpl::fullFramePluginZoomLevelChanged(double zoomLevel) +{ + if (zoomLevel == m_zoomLevel) + return; + + m_zoomLevel = std::max(std::min(zoomLevel, m_maximumZoomLevel), m_minimumZoomLevel); + m_client->zoomLevelChanged(); +} + +double WebView::zoomLevelToZoomFactor(double zoomLevel) +{ + return std::pow(textSizeMultiplierRatio, zoomLevel); +} + +double WebView::zoomFactorToZoomLevel(double factor) +{ + // Since factor = 1.2^level, level = log(factor) / log(1.2) + return log(factor) / log(textSizeMultiplierRatio); +} + void WebViewImpl::performMediaPlayerAction(const WebMediaPlayerAction& action, const WebPoint& location) { @@ -1331,6 +1646,9 @@ void WebViewImpl::performMediaPlayerAction(const WebMediaPlayerAction& action, case WebMediaPlayerAction::Loop: mediaElement->setLoop(action.enable); break; + case WebMediaPlayerAction::Controls: + mediaElement->setControls(action.enable); + break; default: ASSERT_NOT_REACHED(); } @@ -1368,6 +1686,15 @@ void WebViewImpl::dragSourceEndedAt( false, 0); m_page->mainFrame()->eventHandler()->dragSourceEndedAt(pme, static_cast<DragOperation>(operation)); + m_dragScrollTimer->stop(); +} + +void WebViewImpl::dragSourceMovedTo( + const WebPoint& clientPoint, + const WebPoint& screenPoint, + WebDragOperation operation) +{ + m_dragScrollTimer->triggerScroll(mainFrameImpl()->frameView(), clientPoint); } void WebViewImpl::dragSourceSystemDragEnded() @@ -1392,26 +1719,22 @@ WebDragOperation WebViewImpl::dragTargetDragEnter( m_dragIdentity = identity; m_operationsAllowed = operationsAllowed; - DragData dragData( - m_currentDragData.get(), - clientPoint, - screenPoint, - static_cast<DragOperation>(operationsAllowed)); + return dragTargetDragEnterOrOver(clientPoint, screenPoint, DragEnter); +} - m_dropEffect = DropEffectDefault; - m_dragTargetDispatch = true; - DragOperation effect = m_page->dragController()->dragEntered(&dragData); - // Mask the operation against the drag source's allowed operations. - if ((effect & dragData.draggingSourceOperationMask()) != effect) - effect = DragOperationNone; - m_dragTargetDispatch = false; +WebDragOperation WebViewImpl::dragTargetDragEnterNew( + int identity, + const WebPoint& clientPoint, + const WebPoint& screenPoint, + WebDragOperationsMask operationsAllowed) +{ + ASSERT(!m_currentDragData.get()); - if (m_dropEffect != DropEffectDefault) { - m_dragOperation = (m_dropEffect != DropEffectNone) ? WebDragOperationCopy - : WebDragOperationNone; - } else - m_dragOperation = static_cast<WebDragOperation>(effect); - return m_dragOperation; + m_currentDragData = ChromiumDataObject::createReadable(Clipboard::DragAndDrop); + m_dragIdentity = identity; + m_operationsAllowed = operationsAllowed; + + return dragTargetDragEnterOrOver(clientPoint, screenPoint, DragEnter); } WebDragOperation WebViewImpl::dragTargetDragOver( @@ -1419,29 +1742,9 @@ WebDragOperation WebViewImpl::dragTargetDragOver( const WebPoint& screenPoint, WebDragOperationsMask operationsAllowed) { - ASSERT(m_currentDragData.get()); - m_operationsAllowed = operationsAllowed; - DragData dragData( - m_currentDragData.get(), - clientPoint, - screenPoint, - static_cast<DragOperation>(operationsAllowed)); - - m_dropEffect = DropEffectDefault; - m_dragTargetDispatch = true; - DragOperation effect = m_page->dragController()->dragUpdated(&dragData); - // Mask the operation against the drag source's allowed operations. - if ((effect & dragData.draggingSourceOperationMask()) != effect) - effect = DragOperationNone; - m_dragTargetDispatch = false; - if (m_dropEffect != DropEffectDefault) { - m_dragOperation = (m_dropEffect != DropEffectNone) ? WebDragOperationCopy - : WebDragOperationNone; - } else - m_dragOperation = static_cast<WebDragOperation>(effect); - return m_dragOperation; + return dragTargetDragEnterOrOver(clientPoint, screenPoint, DragOver); } void WebViewImpl::dragTargetDragLeave() @@ -1495,6 +1798,7 @@ void WebViewImpl::dragTargetDrop(const WebPoint& clientPoint, m_dropEffect = DropEffectDefault; m_dragOperation = WebDragOperationNone; m_dragIdentity = 0; + m_dragScrollTimer->stop(); } int WebViewImpl::dragIdentity() @@ -1504,7 +1808,42 @@ int WebViewImpl::dragIdentity() return 0; } -unsigned long WebViewImpl::createUniqueIdentifierForRequest() { +WebDragOperation WebViewImpl::dragTargetDragEnterOrOver(const WebPoint& clientPoint, const WebPoint& screenPoint, DragAction dragAction) +{ + ASSERT(m_currentDragData.get()); + + DragData dragData( + m_currentDragData.get(), + clientPoint, + screenPoint, + static_cast<DragOperation>(m_operationsAllowed)); + + m_dropEffect = DropEffectDefault; + m_dragTargetDispatch = true; + DragOperation effect = dragAction == DragEnter ? m_page->dragController()->dragEntered(&dragData) + : m_page->dragController()->dragUpdated(&dragData); + // Mask the operation against the drag source's allowed operations. + if (!(effect & dragData.draggingSourceOperationMask())) + effect = DragOperationNone; + m_dragTargetDispatch = false; + + if (m_dropEffect != DropEffectDefault) { + m_dragOperation = (m_dropEffect != DropEffectNone) ? WebDragOperationCopy + : WebDragOperationNone; + } else + m_dragOperation = static_cast<WebDragOperation>(effect); + + if (dragAction == DragOver) + m_dragScrollTimer->triggerScroll(mainFrameImpl()->frameView(), clientPoint); + else + m_dragScrollTimer->stop(); + + + return m_dragOperation; +} + +unsigned long WebViewImpl::createUniqueIdentifierForRequest() +{ if (m_page) return m_page->progress()->createUniqueIdentifier(); return 0; @@ -1537,15 +1876,24 @@ void WebViewImpl::setInspectorSettings(const WebString& settings) m_inspectorSettings = settings; } -WebDevToolsAgent* WebViewImpl::devToolsAgent() +bool WebViewImpl::inspectorSetting(const WebString& key, WebString* value) const { - return m_devToolsAgent.get(); + if (!m_inspectorSettingsMap->contains(key)) + return false; + *value = m_inspectorSettingsMap->get(key); + return true; } -void WebViewImpl::setDevToolsAgent(WebDevToolsAgent* devToolsAgent) +void WebViewImpl::setInspectorSetting(const WebString& key, + const WebString& value) { - ASSERT(!m_devToolsAgent.get()); // May only set once! - m_devToolsAgent.set(static_cast<WebDevToolsAgentPrivate*>(devToolsAgent)); + m_inspectorSettingsMap->set(key, value); + client()->didUpdateInspectorSetting(key, value); +} + +WebDevToolsAgent* WebViewImpl::devToolsAgent() +{ + return m_devToolsAgent.get(); } WebAccessibilityObject WebViewImpl::accessibilityObject() @@ -1558,25 +1906,31 @@ WebAccessibilityObject WebViewImpl::accessibilityObject() document->axObjectCache()->getOrCreate(document->renderer())); } -void WebViewImpl::applyAutofillSuggestions( +void WebViewImpl::applyAutoFillSuggestions( const WebNode& node, - const WebVector<WebString>& suggestions, - int defaultSuggestionIndex) + const WebVector<WebString>& names, + const WebVector<WebString>& labels, + const WebVector<int>& uniqueIDs, + int separatorIndex) { - applyAutocompleteSuggestions(node, suggestions, defaultSuggestionIndex); + WebVector<WebString> icons(names.size()); + applyAutoFillSuggestions(node, names, labels, icons, uniqueIDs, separatorIndex); } void WebViewImpl::applyAutoFillSuggestions( const WebNode& node, const WebVector<WebString>& names, const WebVector<WebString>& labels, - int defaultSuggestionIndex) + const WebVector<WebString>& icons, + const WebVector<int>& uniqueIDs, + int separatorIndex) { ASSERT(names.size() == labels.size()); - ASSERT(defaultSuggestionIndex < static_cast<int>(names.size())); + ASSERT(names.size() == uniqueIDs.size()); + ASSERT(separatorIndex < static_cast<int>(names.size())); if (names.isEmpty()) { - hideSuggestionsPopup(); + hideAutoFillPopup(); return; } @@ -1585,7 +1939,7 @@ void WebViewImpl::applyAutoFillSuggestions( // focused node, then we have nothing to do. FIXME: also check the // caret is at the end and that the text has not changed. if (!focusedNode || focusedNode != PassRefPtr<Node>(node)) { - hideSuggestionsPopup(); + hideAutoFillPopup(); return; } @@ -1597,98 +1951,51 @@ void WebViewImpl::applyAutoFillSuggestions( if (!m_autoFillPopupClient.get()) m_autoFillPopupClient.set(new AutoFillPopupMenuClient); - m_autoFillPopupClient->initialize(inputElem, names, labels, - defaultSuggestionIndex); - - if (m_suggestionsPopupClient != m_autoFillPopupClient.get()) { - hideSuggestionsPopup(); - m_suggestionsPopupClient = m_autoFillPopupClient.get(); - } + m_autoFillPopupClient->initialize( + inputElem, names, labels, icons, uniqueIDs, separatorIndex); if (!m_autoFillPopup.get()) { - m_autoFillPopup = PopupContainer::create(m_suggestionsPopupClient, - suggestionsPopupSettings); + m_autoFillPopup = PopupContainer::create(m_autoFillPopupClient.get(), + PopupContainer::Suggestion, + autoFillPopupSettings); } - if (m_suggestionsPopup != m_autoFillPopup.get()) - m_suggestionsPopup = m_autoFillPopup.get(); - - if (m_suggestionsPopupShowing) { - m_autoFillPopupClient->setSuggestions(names, labels); - refreshSuggestionsPopup(); + if (m_autoFillPopupShowing) { + refreshAutoFillPopup(); } else { - m_suggestionsPopup->show(focusedNode->getRect(), - focusedNode->ownerDocument()->view(), 0); - m_suggestionsPopupShowing = true; + m_autoFillPopup->show(focusedNode->getRect(), focusedNode->ownerDocument()->view(), 0); + m_autoFillPopupShowing = true; } + + // DEPRECATED: This special mode will go away once AutoFill and Autocomplete + // merge is complete. + if (m_autoFillPopupClient) + m_autoFillPopupClient->setAutocompleteMode(false); } +// DEPRECATED: replacing with applyAutoFillSuggestions. void WebViewImpl::applyAutocompleteSuggestions( const WebNode& node, const WebVector<WebString>& suggestions, int defaultSuggestionIndex) { - ASSERT(defaultSuggestionIndex < static_cast<int>(suggestions.size())); + WebVector<WebString> names(suggestions.size()); + WebVector<WebString> labels(suggestions.size()); + WebVector<WebString> icons(suggestions.size()); + WebVector<int> uniqueIDs(suggestions.size()); - if (!m_page.get() || suggestions.isEmpty()) { - hideSuggestionsPopup(); - return; - } + for (size_t i = 0; i < suggestions.size(); ++i) + names[i] = suggestions[i]; - RefPtr<Node> focusedNode = focusedWebCoreNode(); - // If the node for which we queried the Autocomplete suggestions is not the - // focused node, then we have nothing to do. FIXME: also check the - // caret is at the end and that the text has not changed. - if (!focusedNode || focusedNode != PassRefPtr<Node>(node)) { - hideSuggestionsPopup(); - return; - } - - HTMLInputElement* inputElem = - static_cast<HTMLInputElement*>(focusedNode.get()); - - // The first time the Autocomplete is shown we'll create the client and the - // popup. - if (!m_autocompletePopupClient.get()) - m_autocompletePopupClient.set(new AutocompletePopupMenuClient); - - m_autocompletePopupClient->initialize(inputElem, suggestions, - defaultSuggestionIndex); - - if (m_suggestionsPopupClient != m_autocompletePopupClient.get()) { - hideSuggestionsPopup(); - m_suggestionsPopupClient = m_autocompletePopupClient.get(); - } - - if (!m_autocompletePopup.get()) { - m_autocompletePopup = PopupContainer::create(m_suggestionsPopupClient, - suggestionsPopupSettings); - } - - if (m_suggestionsPopup != m_autocompletePopup.get()) - m_suggestionsPopup = m_autocompletePopup.get(); - - if (m_suggestionsPopupShowing) { - m_autocompletePopupClient->setSuggestions(suggestions); - refreshSuggestionsPopup(); - } else { - m_suggestionsPopup->show(focusedNode->getRect(), - focusedNode->ownerDocument()->view(), 0); - m_suggestionsPopupShowing = true; - } + applyAutoFillSuggestions(node, names, labels, icons, uniqueIDs, -1); + if (m_autoFillPopupClient) + m_autoFillPopupClient->setAutocompleteMode(true); } -void WebViewImpl::hideAutofillPopup() +void WebViewImpl::hidePopups() { - hideSuggestionsPopup(); -} - -void WebViewImpl::hideSuggestionsPopup() -{ - if (m_suggestionsPopupShowing) { - m_suggestionsPopup->hidePopup(); - m_suggestionsPopupShowing = false; - } + hideSelectPopup(); + hideAutoFillPopup(); } void WebViewImpl::performCustomContextMenuAction(unsigned action) @@ -1744,11 +2051,16 @@ bool WebViewImpl::isActive() const return (page() && page()->focusController()) ? page()->focusController()->isActive() : false; } +void WebViewImpl::setDomainRelaxationForbidden(bool forbidden, const WebString& scheme) +{ + SecurityOrigin::setDomainRelaxationForbiddenForURLScheme(forbidden, String(scheme)); +} + void WebViewImpl::setScrollbarColors(unsigned inactiveColor, unsigned activeColor, unsigned trackColor) { -#if OS(LINUX) - RenderThemeChromiumLinux::setScrollbarColors(inactiveColor, +#if OS(LINUX) || OS(FREEBSD) + PlatformThemeChromiumGtk::setScrollbarColors(inactiveColor, activeColor, trackColor); #endif @@ -1758,7 +2070,7 @@ void WebViewImpl::setSelectionColors(unsigned activeBackgroundColor, unsigned activeForegroundColor, unsigned inactiveBackgroundColor, unsigned inactiveForegroundColor) { -#if OS(LINUX) +#if OS(LINUX) || OS(FREEBSD) RenderThemeChromiumLinux::setSelectionColors(activeBackgroundColor, activeForegroundColor, inactiveBackgroundColor, @@ -1767,15 +2079,45 @@ void WebViewImpl::setSelectionColors(unsigned activeBackgroundColor, #endif } -void WebViewImpl::addUserScript(const WebString& sourceCode, bool runAtStart) +void WebView::addUserScript(const WebString& sourceCode, + const WebVector<WebString>& patternsIn, + WebView::UserScriptInjectAt injectAt, + WebView::UserContentInjectIn injectIn) +{ + OwnPtr<Vector<String> > patterns(new Vector<String>); + for (size_t i = 0; i < patternsIn.size(); ++i) + patterns->append(patternsIn[i]); + + PageGroup* pageGroup = PageGroup::pageGroup(pageGroupName); + RefPtr<DOMWrapperWorld> world(DOMWrapperWorld::create()); + pageGroup->addUserScriptToWorld(world.get(), sourceCode, WebURL(), patterns.release(), 0, + static_cast<UserScriptInjectionTime>(injectAt), + static_cast<UserContentInjectedFrames>(injectIn)); +} + +void WebView::addUserStyleSheet(const WebString& sourceCode, + const WebVector<WebString>& patternsIn, + WebView::UserContentInjectIn injectIn, + WebView::UserStyleInjectionTime injectionTime) { + OwnPtr<Vector<String> > patterns(new Vector<String>); + for (size_t i = 0; i < patternsIn.size(); ++i) + patterns->append(patternsIn[i]); + PageGroup* pageGroup = PageGroup::pageGroup(pageGroupName); RefPtr<DOMWrapperWorld> world(DOMWrapperWorld::create()); - pageGroup->addUserScriptToWorld(world.get(), sourceCode, WebURL(), 0, 0, - runAtStart ? InjectAtDocumentStart : InjectAtDocumentEnd); + + // FIXME: Current callers always want the level to be "author". It probably makes sense to let + // callers specify this though, since in other cases the caller will probably want "user" level. + // + // FIXME: It would be nice to populate the URL correctly, instead of passing an empty URL. + pageGroup->addUserStyleSheetToWorld(world.get(), sourceCode, WebURL(), patterns.release(), 0, + static_cast<UserContentInjectedFrames>(injectIn), + UserStyleAuthorLevel, + static_cast<WebCore::UserStyleInjectionTime>(injectionTime)); } -void WebViewImpl::removeAllUserContent() +void WebView::removeAllUserContent() { PageGroup* pageGroup = PageGroup::pageGroup(pageGroupName); pageGroup->removeAllUserContent(); @@ -1794,12 +2136,17 @@ void WebViewImpl::didCommitLoad(bool* isNewNavigation) m_observedNewNavigation = false; } +bool WebViewImpl::useExternalPopupMenus() +{ + return shouldUseExternalPopupMenus; +} + bool WebViewImpl::navigationPolicyFromMouseEvent(unsigned short button, bool ctrl, bool shift, bool alt, bool meta, WebNavigationPolicy* policy) { -#if OS(WINDOWS) || OS(LINUX) || OS(FREEBSD) +#if OS(WINDOWS) || OS(LINUX) || OS(FREEBSD) || OS(SOLARIS) const bool newTabModifier = (button == 1) || ctrl; #elif OS(DARWIN) const bool newTabModifier = (button == 1) || meta; @@ -1822,15 +2169,16 @@ bool WebViewImpl::navigationPolicyFromMouseEvent(unsigned short button, return true; } -void WebViewImpl::startDragging(const WebPoint& eventPos, - const WebDragData& dragData, - WebDragOperationsMask mask) +void WebViewImpl::startDragging(const WebDragData& dragData, + WebDragOperationsMask mask, + const WebImage& dragImage, + const WebPoint& dragImageOffset) { if (!m_client) return; ASSERT(!m_doingDragAndDrop); m_doingDragAndDrop = true; - m_client->startDragging(eventPos, dragData, mask); + m_client->startDragging(dragData, mask, dragImage, dragImageOffset); } void WebViewImpl::setCurrentHistoryItem(HistoryItem* item) @@ -1866,24 +2214,25 @@ NotificationPresenterImpl* WebViewImpl::notificationPresenterImpl() } #endif -void WebViewImpl::refreshSuggestionsPopup() +void WebViewImpl::refreshAutoFillPopup() { - ASSERT(m_suggestionsPopupShowing); + ASSERT(m_autoFillPopupShowing); // Hide the popup if it has become empty. - if (!m_autocompletePopupClient->listSize()) { - hideSuggestionsPopup(); + if (!m_autoFillPopupClient->listSize()) { + hideAutoFillPopup(); return; } - IntRect oldBounds = m_suggestionsPopup->boundsRect(); - m_suggestionsPopup->refresh(); - IntRect newBounds = m_suggestionsPopup->boundsRect(); + IntRect oldBounds = m_autoFillPopup->boundsRect(); + m_autoFillPopup->refresh(); + IntRect newBounds = m_autoFillPopup->boundsRect(); // Let's resize the backing window if necessary. if (oldBounds != newBounds) { WebPopupMenuImpl* popupMenu = - static_cast<WebPopupMenuImpl*>(m_suggestionsPopup->client()); - popupMenu->client()->setWindowRect(newBounds); + static_cast<WebPopupMenuImpl*>(m_autoFillPopup->client()); + if (popupMenu) + popupMenu->client()->setWindowRect(newBounds); } } @@ -1916,4 +2265,285 @@ bool WebViewImpl::tabsToLinks() const return m_tabsToLinks; } +#if USE(ACCELERATED_COMPOSITING) +bool WebViewImpl::allowsAcceleratedCompositing() +{ + return !m_compositorCreationFailed; +} + +void WebViewImpl::setRootGraphicsLayer(WebCore::PlatformLayer* layer) +{ + setIsAcceleratedCompositingActive(layer ? true : false); + if (m_layerRenderer) + m_layerRenderer->setRootLayer(layer); + + IntRect damagedRect(0, 0, m_size.width, m_size.height); + if (m_isAcceleratedCompositingActive) + invalidateRootLayerRect(damagedRect); + else + m_client->didInvalidateRect(damagedRect); +} + +void WebViewImpl::setRootLayerNeedsDisplay() +{ + m_client->scheduleComposite(); +} + + +void WebViewImpl::scrollRootLayerRect(const IntSize& scrollDelta, const IntRect& clipRect) +{ + ASSERT(m_layerRenderer); + // Compute the damage rect in viewport space. + WebFrameImpl* webframe = mainFrameImpl(); + if (!webframe) + return; + FrameView* view = webframe->frameView(); + if (!view) + return; + + IntRect contentRect = view->visibleContentRect(false); + IntRect screenRect = view->contentsToWindow(contentRect); + + // We support fast scrolling in one direction at a time. + if (scrollDelta.width() && scrollDelta.height()) { + invalidateRootLayerRect(WebRect(screenRect)); + return; + } + + // Compute the region we will expose by scrolling. We use the + // content rect for invalidation. Using this space for damage + // rects allows us to intermix invalidates with scrolls. + IntRect damagedContentsRect; + if (scrollDelta.width()) { + int dx = scrollDelta.width(); + damagedContentsRect.setY(screenRect.y()); + damagedContentsRect.setHeight(screenRect.height()); + if (dx > 0) { + damagedContentsRect.setX(screenRect.x()); + damagedContentsRect.setWidth(dx); + } else { + damagedContentsRect.setX(screenRect.right() + dx); + damagedContentsRect.setWidth(-dx); + } + } else { + int dy = scrollDelta.height(); + damagedContentsRect.setX(screenRect.x()); + damagedContentsRect.setWidth(screenRect.width()); + if (dy > 0) { + damagedContentsRect.setY(screenRect.y()); + damagedContentsRect.setHeight(dy); + } else { + damagedContentsRect.setY(screenRect.bottom() + dy); + damagedContentsRect.setHeight(-dy); + } + } + + // Move the previous damage + m_rootLayerScrollDamage.move(scrollDelta.width(), scrollDelta.height()); + // Union with the new damage rect. + m_rootLayerScrollDamage.unite(damagedContentsRect); + + // Scroll any existing damage that intersects with clip rect + if (clipRect.intersects(m_rootLayerDirtyRect)) { + // Find the inner damage + IntRect innerDamage(clipRect); + innerDamage.intersect(m_rootLayerDirtyRect); + + // Move the damage + innerDamage.move(scrollDelta.width(), scrollDelta.height()); + + // Merge it back into the damaged rect + m_rootLayerDirtyRect.unite(innerDamage); + } + + setRootLayerNeedsDisplay(); +} + +void WebViewImpl::invalidateRootLayerRect(const IntRect& rect) +{ + ASSERT(m_layerRenderer); + + if (!page()) + return; + + // FIXME: add a smarter damage aggregation logic and/or unify with + // LayerChromium's damage logic + m_rootLayerDirtyRect.unite(rect); + setRootLayerNeedsDisplay(); +} + + +void WebViewImpl::setIsAcceleratedCompositingActive(bool active) +{ + ChromiumBridge::histogramEnumeration("GPU.setIsAcceleratedCompositingActive", active * 2 + m_isAcceleratedCompositingActive, 4); + + if (m_isAcceleratedCompositingActive == active) + return; + + if (!active) { + m_isAcceleratedCompositingActive = false; + if (m_layerRenderer) + m_layerRenderer->finish(); // finish all GL rendering before we hide the window? + m_client->didActivateAcceleratedCompositing(false); + return; + } + + if (m_layerRenderer) { + m_isAcceleratedCompositingActive = true; + m_layerRenderer->resizeOnscreenContent(WebCore::IntSize(std::max(1, m_size.width), + std::max(1, m_size.height))); + + m_client->didActivateAcceleratedCompositing(true); + return; + } + + RefPtr<GraphicsContext3D> context = m_temporaryOnscreenGraphicsContext3D.release(); + if (!context) { + context = GraphicsContext3D::create(getCompositorContextAttributes(), m_page->chrome(), GraphicsContext3D::RenderDirectlyToHostWindow); + if (context) + context->reshape(std::max(1, m_size.width), std::max(1, m_size.height)); + } + m_layerRenderer = LayerRendererChromium::create(context.release()); + if (m_layerRenderer) { + m_client->didActivateAcceleratedCompositing(true); + m_isAcceleratedCompositingActive = true; + m_compositorCreationFailed = false; + } else { + m_isAcceleratedCompositingActive = false; + m_client->didActivateAcceleratedCompositing(false); + m_compositorCreationFailed = true; + } +} + +void WebViewImpl::updateRootLayerContents(const IntRect& rect) +{ + if (!isAcceleratedCompositingActive()) + return; + + WebFrameImpl* webframe = mainFrameImpl(); + if (!webframe) + return; + FrameView* view = webframe->frameView(); + if (!view) + return; + + LayerChromium* rootLayer = m_layerRenderer->rootLayer(); + if (rootLayer) { + IntRect visibleRect = view->visibleContentRect(true); + + m_layerRenderer->setRootLayerCanvasSize(IntSize(rect.width(), rect.height())); + GraphicsContext* rootLayerContext = m_layerRenderer->rootLayerGraphicsContext(); + +#if PLATFORM(SKIA) + PlatformContextSkia* skiaContext = rootLayerContext->platformContext(); + skia::PlatformCanvas* platformCanvas = skiaContext->canvas(); + + platformCanvas->save(); + + // Bring the canvas into the coordinate system of the paint rect. + platformCanvas->translate(static_cast<SkScalar>(-rect.x()), static_cast<SkScalar>(-rect.y())); + + rootLayerContext->save(); + + webframe->paintWithContext(*rootLayerContext, rect); + rootLayerContext->restore(); + + platformCanvas->restore(); +#elif PLATFORM(CG) + CGContextRef cgContext = rootLayerContext->platformContext(); + + CGContextSaveGState(cgContext); + + // Bring the CoreGraphics context into the coordinate system of the paint rect. + CGContextTranslateCTM(cgContext, -rect.x(), -rect.y()); + + rootLayerContext->save(); + + webframe->paintWithContext(*rootLayerContext, rect); + rootLayerContext->restore(); + + CGContextRestoreGState(cgContext); +#else +#error Must port to your platform +#endif + } +} + +void WebViewImpl::doComposite() +{ + ASSERT(isAcceleratedCompositingActive()); + if (!page()) + return; + FrameView* view = page()->mainFrame()->view(); + + // The visibleRect includes scrollbars whereas the contentRect doesn't. + IntRect visibleRect = view->visibleContentRect(true); + IntRect contentRect = view->visibleContentRect(false); + IntRect viewPort = IntRect(0, 0, m_size.width, m_size.height); + + // Give the compositor a chance to setup/resize the root texture handle and perform scrolling. + m_layerRenderer->prepareToDrawLayers(visibleRect, contentRect, IntPoint(view->scrollX(), view->scrollY())); + + // Draw the contents of the root layer. + Vector<IntRect> damageRects; + damageRects.append(m_rootLayerScrollDamage); + damageRects.append(m_rootLayerDirtyRect); + for (size_t i = 0; i < damageRects.size(); ++i) { + IntRect damagedRect = damageRects[i]; + + // Intersect this rectangle with the viewPort. + damagedRect.intersect(viewPort); + + // Now render it. + if (damagedRect.width() && damagedRect.height()) { + updateRootLayerContents(damagedRect); + m_layerRenderer->updateRootLayerTextureRect(damagedRect); + } + } + m_rootLayerDirtyRect = IntRect(); + m_rootLayerScrollDamage = IntRect(); + + // Draw the actual layers... + m_layerRenderer->drawLayers(visibleRect, contentRect); +} + +void WebViewImpl::reallocateRenderer() +{ + GraphicsContext3D* context = m_layerRenderer->context(); + RefPtr<GraphicsContext3D> newContext = GraphicsContext3D::create(context->getContextAttributes(), m_page->chrome(), GraphicsContext3D::RenderDirectlyToHostWindow); + // GraphicsContext3D::create might fail and return 0, in that case LayerRendererChromium::create will also return 0. + RefPtr<LayerRendererChromium> layerRenderer = LayerRendererChromium::create(newContext); + + // Reattach the root layer. Child layers will get reattached as a side effect of updateLayersRecursive. + if (layerRenderer) + m_layerRenderer->transferRootLayer(layerRenderer.get()); + m_layerRenderer = layerRenderer; + + // Enable or disable accelerated compositing and request a refresh. + setRootGraphicsLayer(m_layerRenderer ? m_layerRenderer->rootLayer() : 0); +} +#endif + + +WebGraphicsContext3D* WebViewImpl::graphicsContext3D() +{ +#if USE(ACCELERATED_COMPOSITING) + if (m_page->settings()->acceleratedCompositingEnabled() && allowsAcceleratedCompositing()) { + GraphicsContext3D* context = 0; + if (m_layerRenderer) + context = m_layerRenderer->context(); + else if (m_temporaryOnscreenGraphicsContext3D) + context = m_temporaryOnscreenGraphicsContext3D.get(); + else { + m_temporaryOnscreenGraphicsContext3D = GraphicsContext3D::create(getCompositorContextAttributes(), m_page->chrome(), GraphicsContext3D::RenderDirectlyToHostWindow); + if (m_temporaryOnscreenGraphicsContext3D) + m_temporaryOnscreenGraphicsContext3D->reshape(std::max(1, m_size.width), std::max(1, m_size.height)); + context = m_temporaryOnscreenGraphicsContext3D.get(); + } + return GraphicsContext3DInternal::extractWebGraphicsContext3D(context); + } +#endif + return 0; +} + } // namespace WebKit |