diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2012-01-06 14:44:00 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2012-01-06 14:44:00 +0100 |
commit | 40736c5763bf61337c8c14e16d8587db021a87d4 (patch) | |
tree | b17a9c00042ad89cb1308e2484491799aa14e9f8 /Source/WebKit2/WebProcess/WebPage/WebPage.cpp | |
download | qtwebkit-40736c5763bf61337c8c14e16d8587db021a87d4.tar.gz |
Imported WebKit commit 2ea9d364d0f6efa8fa64acf19f451504c59be0e4 (http://svn.webkit.org/repository/webkit/trunk@104285)
Diffstat (limited to 'Source/WebKit2/WebProcess/WebPage/WebPage.cpp')
-rw-r--r-- | Source/WebKit2/WebProcess/WebPage/WebPage.cpp | 2900 |
1 files changed, 2900 insertions, 0 deletions
diff --git a/Source/WebKit2/WebProcess/WebPage/WebPage.cpp b/Source/WebKit2/WebProcess/WebPage/WebPage.cpp new file mode 100644 index 000000000..660f6f99c --- /dev/null +++ b/Source/WebKit2/WebProcess/WebPage/WebPage.cpp @@ -0,0 +1,2900 @@ +/* + * Copyright (C) 2010, 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WebPage.h" + +#include "Arguments.h" +#include "DataReference.h" +#include "DecoderAdapter.h" +#include "DrawingArea.h" +#include "InjectedBundle.h" +#include "InjectedBundleBackForwardList.h" +#include "LayerTreeHost.h" +#include "MessageID.h" +#include "NetscapePlugin.h" +#include "NotificationPermissionRequestManager.h" +#include "PageOverlay.h" +#include "PluginProxy.h" +#include "PluginView.h" +#include "PrintInfo.h" +#include "RunLoop.h" +#include "SessionState.h" +#include "ShareableBitmap.h" +#include "WebBackForwardList.h" +#include "WebBackForwardListItem.h" +#include "WebBackForwardListProxy.h" +#include "WebChromeClient.h" +#include "WebContextMenu.h" +#include "WebContextMenuClient.h" +#include "WebContextMessages.h" +#include "WebCoreArgumentCoders.h" +#include "WebDragClient.h" +#include "WebEditorClient.h" +#include "WebEvent.h" +#include "WebEventConversion.h" +#include "WebFrame.h" +#include "WebFullScreenManager.h" +#include "WebGeolocationClient.h" +#include "WebGeometry.h" +#include "WebImage.h" +#include "WebInspector.h" +#include "WebInspectorClient.h" +#include "WebNotificationClient.h" +#include "WebOpenPanelResultListener.h" +#include "WebPageCreationParameters.h" +#include "WebPageGroupProxy.h" +#include "WebPageProxyMessages.h" +#include "WebPopupMenu.h" +#include "WebPreferencesStore.h" +#include "WebProcess.h" +#include "WebProcessProxyMessages.h" +#include <JavaScriptCore/APICast.h> +#include <WebCore/AbstractDatabase.h> +#include <WebCore/ArchiveResource.h> +#include <WebCore/Chrome.h> +#include <WebCore/ContextMenuController.h> +#include <WebCore/DocumentFragment.h> +#include <WebCore/DocumentLoader.h> +#include <WebCore/DocumentMarkerController.h> +#include <WebCore/DragController.h> +#include <WebCore/DragData.h> +#include <WebCore/DragSession.h> +#include <WebCore/EventHandler.h> +#include <WebCore/FocusController.h> +#include <WebCore/FormState.h> +#include <WebCore/Frame.h> +#include <WebCore/FrameLoadRequest.h> +#include <WebCore/FrameLoaderTypes.h> +#include <WebCore/FrameView.h> +#include <WebCore/HTMLFormElement.h> +#include <WebCore/HistoryItem.h> +#include <WebCore/KeyboardEvent.h> +#include <WebCore/MouseEvent.h> +#include <WebCore/Page.h> +#include <WebCore/PlatformKeyboardEvent.h> +#include <WebCore/PluginDocument.h> +#include <WebCore/PrintContext.h> +#include <WebCore/RenderArena.h> +#include <WebCore/RenderLayer.h> +#include <WebCore/RenderTreeAsText.h> +#include <WebCore/RenderView.h> +#include <WebCore/ReplaceSelectionCommand.h> +#include <WebCore/ResourceRequest.h> +#include <WebCore/SchemeRegistry.h> +#include <WebCore/ScriptValue.h> +#include <WebCore/SerializedScriptValue.h> +#include <WebCore/Settings.h> +#include <WebCore/SharedBuffer.h> +#include <WebCore/SubstituteData.h> +#include <WebCore/TextIterator.h> +#include <WebCore/markup.h> +#include <runtime/JSLock.h> +#include <runtime/JSValue.h> + +#include <WebCore/Range.h> +#include <WebCore/VisiblePosition.h> + +#if ENABLE(PLUGIN_PROCESS) +#if PLATFORM(MAC) +#include "MachPort.h" +#endif +#endif + +#if PLATFORM(MAC) +#include "BuiltInPDFView.h" +#endif + +#if PLATFORM(QT) +#include "HitTestResult.h" +#include <QMimeData> +#endif + +#if PLATFORM(GTK) +#include "DataObjectGtk.h" +#endif + +#ifndef NDEBUG +#include <wtf/RefCountedLeakCounter.h> +#endif + +using namespace JSC; +using namespace WebCore; +using namespace std; + +namespace WebKit { + +class SendStopResponsivenessTimer { +public: + SendStopResponsivenessTimer(WebPage* page) + : m_page(page) + { + } + + ~SendStopResponsivenessTimer() + { + m_page->send(Messages::WebPageProxy::StopResponsivenessTimer()); + } + +private: + WebPage* m_page; +}; + +DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, webPageCounter, ("WebPage")); + +PassRefPtr<WebPage> WebPage::create(uint64_t pageID, const WebPageCreationParameters& parameters) +{ + RefPtr<WebPage> page = adoptRef(new WebPage(pageID, parameters)); + + if (page->pageGroup()->isVisibleToInjectedBundle() && WebProcess::shared().injectedBundle()) + WebProcess::shared().injectedBundle()->didCreatePage(page.get()); + + return page.release(); +} + +WebPage::WebPage(uint64_t pageID, const WebPageCreationParameters& parameters) + : m_viewSize(parameters.viewSize) + , m_useFixedLayout(false) + , m_drawsBackground(true) + , m_drawsTransparentBackground(false) + , m_isInRedo(false) + , m_isClosed(false) + , m_tabToLinks(false) +#if PLATFORM(MAC) + , m_windowIsVisible(false) + , m_isSmartInsertDeleteEnabled(parameters.isSmartInsertDeleteEnabled) + , m_keyboardEventBeingInterpreted(0) +#elif PLATFORM(WIN) + , m_nativeWindow(parameters.nativeWindow) +#endif + , m_setCanStartMediaTimer(WebProcess::shared().runLoop(), this, &WebPage::setCanStartMediaTimerFired) + , m_findController(this) + , m_geolocationPermissionRequestManager(this) + , m_pageID(pageID) + , m_canRunBeforeUnloadConfirmPanel(parameters.canRunBeforeUnloadConfirmPanel) + , m_canRunModal(parameters.canRunModal) + , m_isRunningModal(false) + , m_cachedMainFrameIsPinnedToLeftSide(false) + , m_cachedMainFrameIsPinnedToRightSide(false) + , m_cachedPageCount(0) + , m_isShowingContextMenu(false) +#if PLATFORM(WIN) + , m_gestureReachedScrollingLimit(false) +#endif +{ + ASSERT(m_pageID); + // FIXME: This is a non-ideal location for this Setting and + // 4ms should be adopted project-wide now, https://bugs.webkit.org/show_bug.cgi?id=61214 + Settings::setDefaultMinDOMTimerInterval(0.004); + + Page::PageClients pageClients; + pageClients.chromeClient = new WebChromeClient(this); + pageClients.contextMenuClient = new WebContextMenuClient(this); + pageClients.editorClient = new WebEditorClient(this); + pageClients.dragClient = new WebDragClient(this); + pageClients.backForwardClient = WebBackForwardListProxy::create(this); +#if ENABLE(CLIENT_BASED_GEOLOCATION) + pageClients.geolocationClient = new WebGeolocationClient(this); +#endif +#if ENABLE(INSPECTOR) + pageClients.inspectorClient = new WebInspectorClient(this); +#endif +#if ENABLE(NOTIFICATIONS) + pageClients.notificationClient = new WebNotificationClient(this); +#endif + + m_page = adoptPtr(new Page(pageClients)); + + // Qt does not yet call setIsInWindow. Until it does, just leave + // this line out so plug-ins and video will work. Eventually all platforms + // should call setIsInWindow and this comment and #if should be removed, + // leaving behind the setCanStartMedia call. +#if !PLATFORM(QT) + m_page->setCanStartMedia(false); +#endif + + updatePreferences(parameters.store); + + m_pageGroup = WebProcess::shared().webPageGroup(parameters.pageGroupData); + m_page->setGroupName(m_pageGroup->identifier()); + + platformInitialize(); + + m_drawingArea = DrawingArea::create(this, parameters); + m_drawingArea->setPaintingEnabled(false); + + m_mainFrame = WebFrame::createMainFrame(this); + + setUseFixedLayout(parameters.useFixedLayout); + + setDrawsBackground(parameters.drawsBackground); + setDrawsTransparentBackground(parameters.drawsTransparentBackground); + + setPaginationMode(parameters.paginationMode); + setPageLength(parameters.pageLength); + setGapBetweenPages(parameters.gapBetweenPages); + + setMemoryCacheMessagesEnabled(parameters.areMemoryCacheClientCallsEnabled); + + setActive(parameters.isActive); + setFocused(parameters.isFocused); + setIsInWindow(parameters.isInWindow); + + m_userAgent = parameters.userAgent; + + WebBackForwardListProxy::setHighestItemIDFromUIProcess(parameters.highestUsedBackForwardItemID); + + if (!parameters.sessionState.isEmpty()) + restoreSession(parameters.sessionState); + + m_drawingArea->setPaintingEnabled(true); + +#ifndef NDEBUG + webPageCounter.increment(); +#endif +} + +WebPage::~WebPage() +{ + if (m_backForwardList) + m_backForwardList->detach(); + + ASSERT(!m_page); + + m_sandboxExtensionTracker.invalidate(); + +#if PLATFORM(MAC) + ASSERT(m_pluginViews.isEmpty()); +#endif + +#ifndef NDEBUG + webPageCounter.decrement(); +#endif +} + +void WebPage::dummy(bool&) +{ +} + +CoreIPC::Connection* WebPage::connection() const +{ + return WebProcess::shared().connection(); +} + +void WebPage::initializeInjectedBundleContextMenuClient(WKBundlePageContextMenuClient* client) +{ + m_contextMenuClient.initialize(client); +} + +void WebPage::initializeInjectedBundleEditorClient(WKBundlePageEditorClient* client) +{ + m_editorClient.initialize(client); +} + +void WebPage::initializeInjectedBundleFormClient(WKBundlePageFormClient* client) +{ + m_formClient.initialize(client); +} + +void WebPage::initializeInjectedBundleLoaderClient(WKBundlePageLoaderClient* client) +{ + m_loaderClient.initialize(client); +} + +void WebPage::initializeInjectedBundlePolicyClient(WKBundlePagePolicyClient* client) +{ + m_policyClient.initialize(client); +} + +void WebPage::initializeInjectedBundleResourceLoadClient(WKBundlePageResourceLoadClient* client) +{ + m_resourceLoadClient.initialize(client); +} + +void WebPage::initializeInjectedBundleUIClient(WKBundlePageUIClient* client) +{ + m_uiClient.initialize(client); +} + +#if ENABLE(FULLSCREEN_API) +void WebPage::initializeInjectedBundleFullScreenClient(WKBundlePageFullScreenClient* client) +{ + m_fullScreenClient.initialize(client); +} +#endif + +PassRefPtr<Plugin> WebPage::createPlugin(WebFrame* frame, const Plugin::Parameters& parameters) +{ + String pluginPath; + + if (!WebProcess::shared().connection()->sendSync( + Messages::WebContext::GetPluginPath(parameters.mimeType, parameters.url.string()), + Messages::WebContext::GetPluginPath::Reply(pluginPath), 0)) { + return 0; + } + + if (pluginPath.isNull()) { +#if PLATFORM(MAC) + if (parameters.mimeType == "application/pdf" + || (parameters.mimeType.isEmpty() && parameters.url.path().lower().endsWith(".pdf"))) + return BuiltInPDFView::create(frame); +#else + UNUSED_PARAM(frame); +#endif + return 0; + } + +#if ENABLE(PLUGIN_PROCESS) + return PluginProxy::create(pluginPath); +#else + NetscapePlugin::setSetExceptionFunction(NPRuntimeObjectMap::setGlobalException); + return NetscapePlugin::create(NetscapePluginModule::getOrCreate(pluginPath)); +#endif +} + +EditorState WebPage::editorState() const +{ + Frame* frame = m_page->focusController()->focusedOrMainFrame(); + ASSERT(frame); + + EditorState result; + result.selectionIsNone = frame->selection()->isNone(); + result.selectionIsRange = frame->selection()->isRange(); + result.isContentEditable = frame->selection()->isContentEditable(); + result.isContentRichlyEditable = frame->selection()->isContentRichlyEditable(); + result.isInPasswordField = frame->selection()->isInPasswordField(); + result.hasComposition = frame->editor()->hasComposition(); + result.shouldIgnoreCompositionSelectionChange = frame->editor()->ignoreCompositionSelectionChange(); + +#if PLATFORM(QT) + size_t location = 0; + size_t length = 0; + + Element* selectionRoot = frame->selection()->rootEditableElement(); + Element* scope = selectionRoot ? selectionRoot : frame->document()->documentElement(); + + if (!scope) + return result; + + RefPtr<Range> range; + if (result.hasComposition && (range = frame->editor()->compositionRange())) { + frame->editor()->getCompositionSelection(result.anchorPosition, result.cursorPosition); + + result.compositionRect = frame->view()->contentsToWindow(range->boundingBox()); + } + + if (!result.hasComposition && !result.selectionIsNone && (range = frame->selection()->selection().firstRange())) { + TextIterator::getLocationAndLengthFromRange(scope, range.get(), location, length); + bool baseIsFirst = frame->selection()->selection().isBaseFirst(); + + result.cursorPosition = (baseIsFirst) ? location + length : location; + result.anchorPosition = (baseIsFirst) ? location : location + length; + result.selectedText = range->text(); + } + + if (range) + result.microFocus = frame->view()->contentsToWindow(frame->editor()->firstRectForRange(range.get())); + + // FIXME: We should only transfer innerText when it changes and do this on the UI side. + if (result.isContentEditable && !result.isInPasswordField) { + result.surroundingText = scope->innerText(); + if (result.hasComposition) { + // The anchor is always the left position when they represent a composition. + result.surroundingText.remove(result.anchorPosition, result.cursorPosition - result.anchorPosition); + } + } +#endif + + return result; +} + +String WebPage::renderTreeExternalRepresentation() const +{ + return externalRepresentation(m_mainFrame->coreFrame(), RenderAsTextBehaviorNormal); +} + +uint64_t WebPage::renderTreeSize() const +{ + if (!m_page) + return 0; + + Frame* mainFrame = m_page->mainFrame(); + if (!mainFrame) + return 0; + + uint64_t size = 0; + for (Frame* coreFrame = mainFrame; coreFrame; coreFrame = coreFrame->tree()->traverseNext()) + size += coreFrame->document()->renderArena()->totalRenderArenaSize(); + + return size; +} + +void WebPage::setTracksRepaints(bool trackRepaints) +{ + if (FrameView* view = mainFrameView()) + view->setTracksRepaints(trackRepaints); +} + +bool WebPage::isTrackingRepaints() const +{ + if (FrameView* view = mainFrameView()) + return view->isTrackingRepaints(); + + return false; +} + +void WebPage::resetTrackedRepaints() +{ + if (FrameView* view = mainFrameView()) + view->resetTrackedRepaints(); +} + +PassRefPtr<ImmutableArray> WebPage::trackedRepaintRects() +{ + FrameView* view = mainFrameView(); + if (!view) + return ImmutableArray::create(); + + const Vector<IntRect>& rects = view->trackedRepaintRects(); + size_t size = rects.size(); + if (!size) + return ImmutableArray::create(); + + Vector<RefPtr<APIObject> > vector; + vector.reserveInitialCapacity(size); + + for (size_t i = 0; i < size; ++i) + vector.uncheckedAppend(WebRect::create(toAPI(rects[i]))); + + return ImmutableArray::adopt(vector); +} + +void WebPage::executeEditingCommand(const String& commandName, const String& argument) +{ + Frame* frame = m_page->focusController()->focusedOrMainFrame(); + if (!frame) + return; + frame->editor()->command(commandName).execute(argument); +} + +bool WebPage::isEditingCommandEnabled(const String& commandName) +{ + Frame* frame = m_page->focusController()->focusedOrMainFrame(); + if (!frame) + return false; + + Editor::Command command = frame->editor()->command(commandName); + return command.isSupported() && command.isEnabled(); +} + +void WebPage::clearMainFrameName() +{ + if (Frame* frame = mainFrame()) + frame->tree()->clearName(); +} + +#if USE(ACCELERATED_COMPOSITING) +void WebPage::enterAcceleratedCompositingMode(GraphicsLayer* layer) +{ + m_drawingArea->setRootCompositingLayer(layer); +} + +void WebPage::exitAcceleratedCompositingMode() +{ + m_drawingArea->setRootCompositingLayer(0); +} +#endif + +void WebPage::close() +{ + if (m_isClosed) + return; + + m_isClosed = true; + + if (pageGroup()->isVisibleToInjectedBundle() && WebProcess::shared().injectedBundle()) + WebProcess::shared().injectedBundle()->willDestroyPage(this); + +#if ENABLE(INSPECTOR) + m_inspector = 0; +#endif +#if ENABLE(FULLSCREEN_API) + m_fullScreenManager = 0; +#endif + + if (m_activePopupMenu) { + m_activePopupMenu->disconnectFromPage(); + m_activePopupMenu = 0; + } + + if (m_activeOpenPanelResultListener) { + m_activeOpenPanelResultListener->disconnectFromPage(); + m_activeOpenPanelResultListener = 0; + } + + m_sandboxExtensionTracker.invalidate(); + + m_underlayPage = nullptr; + m_printContext = nullptr; + m_mainFrame->coreFrame()->loader()->detachFromParent(); + m_page = nullptr; + m_drawingArea = nullptr; + + bool isRunningModal = m_isRunningModal; + m_isRunningModal = false; + + // The WebPage can be destroyed by this call. + WebProcess::shared().removeWebPage(m_pageID); + + if (isRunningModal) + WebProcess::shared().runLoop()->stop(); +} + +void WebPage::tryClose() +{ + SendStopResponsivenessTimer stopper(this); + + if (!m_mainFrame->coreFrame()->loader()->shouldClose()) { + send(Messages::WebPageProxy::StopResponsivenessTimer()); + return; + } + + send(Messages::WebPageProxy::ClosePage(true)); +} + +void WebPage::sendClose() +{ + send(Messages::WebPageProxy::ClosePage(false)); +} + +void WebPage::loadURL(const String& url, const SandboxExtension::Handle& sandboxExtensionHandle) +{ + loadURLRequest(ResourceRequest(KURL(KURL(), url)), sandboxExtensionHandle); +} + +void WebPage::loadURLRequest(const ResourceRequest& request, const SandboxExtension::Handle& sandboxExtensionHandle) +{ + SendStopResponsivenessTimer stopper(this); + + m_sandboxExtensionTracker.beginLoad(m_mainFrame.get(), sandboxExtensionHandle); + m_mainFrame->coreFrame()->loader()->load(request, false); +} + +void WebPage::loadData(PassRefPtr<SharedBuffer> sharedBuffer, const String& MIMEType, const String& encodingName, const KURL& baseURL, const KURL& unreachableURL) +{ + SendStopResponsivenessTimer stopper(this); + + ResourceRequest request(baseURL); + SubstituteData substituteData(sharedBuffer, MIMEType, encodingName, unreachableURL); + m_mainFrame->coreFrame()->loader()->load(request, substituteData, false); +} + +void WebPage::loadHTMLString(const String& htmlString, const String& baseURLString) +{ + RefPtr<SharedBuffer> sharedBuffer = SharedBuffer::create(reinterpret_cast<const char*>(htmlString.characters()), htmlString.length() * sizeof(UChar)); + KURL baseURL = baseURLString.isEmpty() ? blankURL() : KURL(KURL(), baseURLString); + loadData(sharedBuffer, "text/html", "utf-16", baseURL, KURL()); +} + +void WebPage::loadAlternateHTMLString(const String& htmlString, const String& baseURLString, const String& unreachableURLString) +{ + RefPtr<SharedBuffer> sharedBuffer = SharedBuffer::create(reinterpret_cast<const char*>(htmlString.characters()), htmlString.length() * sizeof(UChar)); + KURL baseURL = baseURLString.isEmpty() ? blankURL() : KURL(KURL(), baseURLString); + KURL unreachableURL = unreachableURLString.isEmpty() ? KURL() : KURL(KURL(), unreachableURLString); + loadData(sharedBuffer, "text/html", "utf-16", baseURL, unreachableURL); +} + +void WebPage::loadPlainTextString(const String& string) +{ + RefPtr<SharedBuffer> sharedBuffer = SharedBuffer::create(reinterpret_cast<const char*>(string.characters()), string.length() * sizeof(UChar)); + loadData(sharedBuffer, "text/plain", "utf-16", blankURL(), KURL()); +} + +void WebPage::linkClicked(const String& url, const WebMouseEvent& event) +{ + Frame* frame = m_page->mainFrame(); + if (!frame) + return; + + RefPtr<Event> coreEvent; + if (event.type() != WebEvent::NoType) + coreEvent = MouseEvent::create(eventNames().clickEvent, frame->document()->defaultView(), platform(event), 0, 0); + + frame->loader()->loadFrameRequest(FrameLoadRequest(frame->document()->securityOrigin(), ResourceRequest(url)), + false, false, coreEvent.get(), 0, MaybeSendReferrer); +} + +void WebPage::stopLoadingFrame(uint64_t frameID) +{ + WebFrame* frame = WebProcess::shared().webFrame(frameID); + if (!frame) + return; + + frame->coreFrame()->loader()->stopForUserCancel(); +} + +void WebPage::stopLoading() +{ + SendStopResponsivenessTimer stopper(this); + + m_mainFrame->coreFrame()->loader()->stopForUserCancel(); +} + +void WebPage::setDefersLoading(bool defersLoading) +{ + m_page->setDefersLoading(defersLoading); +} + +void WebPage::reload(bool reloadFromOrigin) +{ + SendStopResponsivenessTimer stopper(this); + + m_mainFrame->coreFrame()->loader()->reload(reloadFromOrigin); +} + +void WebPage::goForward(uint64_t backForwardItemID, const SandboxExtension::Handle& sandboxExtensionHandle) +{ + SendStopResponsivenessTimer stopper(this); + + HistoryItem* item = WebBackForwardListProxy::itemForID(backForwardItemID); + ASSERT(item); + if (!item) + return; + + m_sandboxExtensionTracker.beginLoad(m_mainFrame.get(), sandboxExtensionHandle); + m_page->goToItem(item, FrameLoadTypeForward); +} + +void WebPage::goBack(uint64_t backForwardItemID, const SandboxExtension::Handle& sandboxExtensionHandle) +{ + SendStopResponsivenessTimer stopper(this); + + HistoryItem* item = WebBackForwardListProxy::itemForID(backForwardItemID); + ASSERT(item); + if (!item) + return; + + m_sandboxExtensionTracker.beginLoad(m_mainFrame.get(), sandboxExtensionHandle); + m_page->goToItem(item, FrameLoadTypeBack); +} + +void WebPage::goToBackForwardItem(uint64_t backForwardItemID, const SandboxExtension::Handle& sandboxExtensionHandle) +{ + SendStopResponsivenessTimer stopper(this); + + HistoryItem* item = WebBackForwardListProxy::itemForID(backForwardItemID); + ASSERT(item); + if (!item) + return; + + m_sandboxExtensionTracker.beginLoad(m_mainFrame.get(), sandboxExtensionHandle); + m_page->goToItem(item, FrameLoadTypeIndexedBackForward); +} + +void WebPage::tryRestoreScrollPosition() +{ + m_page->mainFrame()->loader()->history()->restoreScrollPositionAndViewState(); +} + +void WebPage::layoutIfNeeded() +{ + if (m_mainFrame->coreFrame()->view()) + m_mainFrame->coreFrame()->view()->updateLayoutAndStyleIfNeededRecursive(); + + if (m_underlayPage) { + if (FrameView *frameView = m_underlayPage->mainFrameView()) + frameView->updateLayoutAndStyleIfNeededRecursive(); + } +} + +void WebPage::setSize(const WebCore::IntSize& viewSize) +{ + FrameView* view = m_page->mainFrame()->view(); + +#if USE(TILED_BACKING_STORE) + // If we are resizing to content ignore external attempts. + if (view->useFixedLayout()) + return; +#endif + + if (m_viewSize == viewSize) + return; + + view->resize(viewSize); + view->setNeedsLayout(); + m_drawingArea->setNeedsDisplay(IntRect(IntPoint(0, 0), viewSize)); + + m_viewSize = viewSize; +} + +#if USE(TILED_BACKING_STORE) +void WebPage::setFixedVisibleContentRect(const IntRect& rect) +{ + ASSERT(m_useFixedLayout); + + Frame* frame = m_page->mainFrame(); + + frame->view()->setFixedVisibleContentRect(rect); +} + +void WebPage::setResizesToContentsUsingLayoutSize(const IntSize& targetLayoutSize) +{ + ASSERT(m_useFixedLayout); + ASSERT(!targetLayoutSize.isEmpty()); + + FrameView* view = m_page->mainFrame()->view(); + + view->setDelegatesScrolling(true); + view->setUseFixedLayout(true); + view->setPaintsEntireContents(true); + + if (view->fixedLayoutSize() == targetLayoutSize) + return; + + // Always reset even when empty. + view->setFixedLayoutSize(targetLayoutSize); + + // Schedule a layout to use the new target size. + if (!view->layoutPending()) { + view->setNeedsLayout(); + view->scheduleRelayout(); + } +} + +void WebPage::resizeToContentsIfNeeded() +{ + ASSERT(m_useFixedLayout); + + FrameView* view = m_page->mainFrame()->view(); + + if (!view->useFixedLayout()) + return; + + IntSize contentSize = view->contentsSize(); + if (contentSize == m_viewSize) + return; + + m_viewSize = contentSize; + view->resize(m_viewSize); + view->setNeedsLayout(); +} + +void WebPage::setViewportSize(const IntSize& size) +{ + ASSERT(m_useFixedLayout); + + if (m_viewportSize == size) + return; + + m_viewportSize = size; + + // Recalculate the recommended layout size, when the available size (device pixel) changes. + Settings* settings = m_page->settings(); + + int minimumLayoutFallbackWidth = std::max(settings->layoutFallbackWidth(), size.width()); + + IntSize targetLayoutSize = computeViewportAttributes(m_page->viewportArguments(), minimumLayoutFallbackWidth, settings->deviceWidth(), settings->deviceHeight(), settings->deviceDPI(), size).layoutSize; + setResizesToContentsUsingLayoutSize(targetLayoutSize); +} + +#endif + +void WebPage::scrollMainFrameIfNotAtMaxScrollPosition(const IntSize& scrollOffset) +{ + Frame* frame = m_page->mainFrame(); + + IntPoint scrollPosition = frame->view()->scrollPosition(); + IntPoint maximumScrollPosition = frame->view()->maximumScrollPosition(); + + // If the current scroll position in a direction is the max scroll position + // we don't want to scroll at all. + IntSize newScrollOffset; + if (scrollPosition.x() < maximumScrollPosition.x()) + newScrollOffset.setWidth(scrollOffset.width()); + if (scrollPosition.y() < maximumScrollPosition.y()) + newScrollOffset.setHeight(scrollOffset.height()); + + if (newScrollOffset.isZero()) + return; + + frame->view()->setScrollPosition(frame->view()->scrollPosition() + newScrollOffset); +} + +void WebPage::drawRect(GraphicsContext& graphicsContext, const IntRect& rect) +{ + GraphicsContextStateSaver stateSaver(graphicsContext); + graphicsContext.clip(rect); + + if (m_underlayPage) { + m_underlayPage->drawRect(graphicsContext, rect); + + graphicsContext.beginTransparencyLayer(1); + m_mainFrame->coreFrame()->view()->paint(&graphicsContext, rect); + graphicsContext.endTransparencyLayer(); + return; + } + + m_mainFrame->coreFrame()->view()->paint(&graphicsContext, rect); +} + +void WebPage::drawPageOverlay(GraphicsContext& graphicsContext, const IntRect& rect) +{ + ASSERT(m_pageOverlay); + + GraphicsContextStateSaver stateSaver(graphicsContext); + graphicsContext.clip(rect); + m_pageOverlay->drawRect(graphicsContext, rect); +} + +double WebPage::textZoomFactor() const +{ + Frame* frame = m_mainFrame->coreFrame(); + if (!frame) + return 1; + return frame->textZoomFactor(); +} + +void WebPage::setTextZoomFactor(double zoomFactor) +{ + Frame* frame = m_mainFrame->coreFrame(); + if (!frame) + return; + frame->setTextZoomFactor(static_cast<float>(zoomFactor)); +} + +double WebPage::pageZoomFactor() const +{ + Frame* frame = m_mainFrame->coreFrame(); + if (!frame) + return 1; + return frame->pageZoomFactor(); +} + +void WebPage::setPageZoomFactor(double zoomFactor) +{ + Frame* frame = m_mainFrame->coreFrame(); + if (!frame) + return; + frame->setPageZoomFactor(static_cast<float>(zoomFactor)); +} + +void WebPage::setPageAndTextZoomFactors(double pageZoomFactor, double textZoomFactor) +{ + Frame* frame = m_mainFrame->coreFrame(); + if (!frame) + return; + return frame->setPageAndTextZoomFactors(static_cast<float>(pageZoomFactor), static_cast<float>(textZoomFactor)); +} + +void WebPage::windowScreenDidChange(uint64_t displayID) +{ + m_page->windowScreenDidChange(static_cast<PlatformDisplayID>(displayID)); +} + +void WebPage::scalePage(double scale, const IntPoint& origin) +{ + m_page->setPageScaleFactor(scale, origin); + + send(Messages::WebPageProxy::PageScaleFactorDidChange(scale)); +} + +double WebPage::pageScaleFactor() const +{ + return m_page->pageScaleFactor(); +} + +void WebPage::setDeviceScaleFactor(float scaleFactor) +{ + if (scaleFactor == m_page->deviceScaleFactor()) + return; + + m_page->setDeviceScaleFactor(scaleFactor); + + // Tell all our plug-in views that the device scale factor changed. +#if PLATFORM(MAC) + for (HashSet<PluginView*>::const_iterator it = m_pluginViews.begin(), end = m_pluginViews.end(); it != end; ++it) + (*it)->setDeviceScaleFactor(scaleFactor); +#endif + + if (m_findController.isShowingOverlay()) { + // We must have updated layout to get the selection rects right. + layoutIfNeeded(); + m_findController.deviceScaleFactorDidChange(); + } +} + +float WebPage::deviceScaleFactor() const +{ + return m_page->deviceScaleFactor(); +} + +void WebPage::setUseFixedLayout(bool fixed) +{ + m_useFixedLayout = fixed; + + FrameView* view = mainFrameView(); + if (!view) + return; + + view->setUseFixedLayout(fixed); + if (!fixed) + view->setFixedLayoutSize(IntSize()); +} + +void WebPage::setFixedLayoutSize(const IntSize& size) +{ + FrameView* view = mainFrameView(); + if (!view) + return; + + view->setFixedLayoutSize(size); + view->forceLayout(); +} + +void WebPage::setPaginationMode(uint32_t mode) +{ + Page::Pagination pagination = m_page->pagination(); + pagination.mode = static_cast<Page::Pagination::Mode>(mode); + m_page->setPagination(pagination); +} + +void WebPage::setPageLength(double pageLength) +{ + Page::Pagination pagination = m_page->pagination(); + pagination.pageLength = pageLength; + m_page->setPagination(pagination); +} + +void WebPage::setGapBetweenPages(double gap) +{ + Page::Pagination pagination = m_page->pagination(); + pagination.gap = gap; + m_page->setPagination(pagination); +} + +void WebPage::installPageOverlay(PassRefPtr<PageOverlay> pageOverlay) +{ + bool shouldFadeIn = true; + + if (m_pageOverlay) { + m_pageOverlay->setPage(0); + + if (pageOverlay) { + // We're installing a page overlay when a page overlay is already active. + // In this case we don't want to fade in the new overlay. + shouldFadeIn = false; + } + } + + m_pageOverlay = pageOverlay; + m_pageOverlay->setPage(this); + + if (shouldFadeIn) + m_pageOverlay->startFadeInAnimation(); + + m_drawingArea->didInstallPageOverlay(); +#if PLATFORM(WIN) + send(Messages::WebPageProxy::DidInstallOrUninstallPageOverlay(true)); +#endif + + m_pageOverlay->setNeedsDisplay(); +} + +void WebPage::uninstallPageOverlay(PageOverlay* pageOverlay, bool fadeOut) +{ + if (pageOverlay != m_pageOverlay) + return; + + if (fadeOut) { + m_pageOverlay->startFadeOutAnimation(); + return; + } + + m_pageOverlay->setPage(0); + m_pageOverlay = nullptr; + + m_drawingArea->didUninstallPageOverlay(); +#if PLATFORM(WIN) + send(Messages::WebPageProxy::DidInstallOrUninstallPageOverlay(false)); +#endif +} + +PassRefPtr<WebImage> WebPage::snapshotInViewCoordinates(const IntRect& rect, ImageOptions options) +{ + FrameView* frameView = m_mainFrame->coreFrame()->view(); + if (!frameView) + return 0; + + IntSize bitmapSize = rect.size(); + float deviceScaleFactor = corePage()->deviceScaleFactor(); + bitmapSize.scale(deviceScaleFactor); + + RefPtr<WebImage> snapshot = WebImage::create(bitmapSize, options); + if (!snapshot->bitmap()) + return 0; + + OwnPtr<WebCore::GraphicsContext> graphicsContext = snapshot->bitmap()->createGraphicsContext(); + graphicsContext->applyDeviceScaleFactor(deviceScaleFactor); + graphicsContext->translate(-rect.x(), -rect.y()); + + frameView->updateLayoutAndStyleIfNeededRecursive(); + + PaintBehavior oldBehavior = frameView->paintBehavior(); + frameView->setPaintBehavior(oldBehavior | PaintBehaviorFlattenCompositingLayers); + frameView->paint(graphicsContext.get(), rect); + frameView->setPaintBehavior(oldBehavior); + + return snapshot.release(); +} + +PassRefPtr<WebImage> WebPage::scaledSnapshotInDocumentCoordinates(const IntRect& rect, double scaleFactor, ImageOptions options) +{ + FrameView* frameView = m_mainFrame->coreFrame()->view(); + if (!frameView) + return 0; + + float combinedScaleFactor = scaleFactor * corePage()->deviceScaleFactor(); + IntSize size(ceil(rect.width() * combinedScaleFactor), ceil(rect.height() * combinedScaleFactor)); + RefPtr<WebImage> snapshot = WebImage::create(size, options); + if (!snapshot->bitmap()) + return 0; + + OwnPtr<WebCore::GraphicsContext> graphicsContext = snapshot->bitmap()->createGraphicsContext(); + graphicsContext->applyDeviceScaleFactor(combinedScaleFactor); + graphicsContext->translate(-rect.x(), -rect.y()); + + frameView->updateLayoutAndStyleIfNeededRecursive(); + + PaintBehavior oldBehavior = frameView->paintBehavior(); + frameView->setPaintBehavior(oldBehavior | PaintBehaviorFlattenCompositingLayers); + frameView->paintContents(graphicsContext.get(), rect); + frameView->setPaintBehavior(oldBehavior); + + return snapshot.release(); +} + +PassRefPtr<WebImage> WebPage::snapshotInDocumentCoordinates(const IntRect& rect, ImageOptions options) +{ + return scaledSnapshotInDocumentCoordinates(rect, 1, options); +} + +void WebPage::pageDidScroll() +{ + // Hide the find indicator. + m_findController.hideFindIndicator(); + + m_uiClient.pageDidScroll(this); + + send(Messages::WebPageProxy::PageDidScroll()); +} + +#if USE(TILED_BACKING_STORE) +void WebPage::pageDidRequestScroll(const IntPoint& point) +{ + send(Messages::WebPageProxy::PageDidRequestScroll(point)); +} +#endif + +WebContextMenu* WebPage::contextMenu() +{ + if (!m_contextMenu) + m_contextMenu = WebContextMenu::create(this); + return m_contextMenu.get(); +} + +// Events + +static const WebEvent* g_currentEvent = 0; + +// FIXME: WebPage::currentEvent is used by the plug-in code to avoid having to convert from DOM events back to +// WebEvents. When we get the event handling sorted out, this should go away and the Widgets should get the correct +// platform events passed to the event handler code. +const WebEvent* WebPage::currentEvent() +{ + return g_currentEvent; +} + +class CurrentEvent { +public: + explicit CurrentEvent(const WebEvent& event) + : m_previousCurrentEvent(g_currentEvent) + { + g_currentEvent = &event; + } + + ~CurrentEvent() + { + g_currentEvent = m_previousCurrentEvent; + } + +private: + const WebEvent* m_previousCurrentEvent; +}; + +static bool isContextClick(const PlatformMouseEvent& event) +{ + if (event.button() == WebCore::RightButton) + return true; + +#if PLATFORM(MAC) + // FIXME: this really should be about OSX-style UI, not about the Mac port + if (event.button() == WebCore::LeftButton && event.ctrlKey()) + return true; +#endif + + return false; +} + +static bool handleContextMenuEvent(const PlatformMouseEvent& platformMouseEvent, Page* page) +{ + IntPoint point = page->mainFrame()->view()->windowToContents(platformMouseEvent.position()); + HitTestResult result = page->mainFrame()->eventHandler()->hitTestResultAtPoint(point, false); + + Frame* frame = page->mainFrame(); + if (result.innerNonSharedNode()) + frame = result.innerNonSharedNode()->document()->frame(); + + bool handled = frame->eventHandler()->sendContextMenuEvent(platformMouseEvent); + if (handled) + page->chrome()->showContextMenu(); + + return handled; +} + +static bool handleMouseEvent(const WebMouseEvent& mouseEvent, Page* page, bool onlyUpdateScrollbars) +{ + Frame* frame = page->mainFrame(); + if (!frame->view()) + return false; + + PlatformMouseEvent platformMouseEvent = platform(mouseEvent); + + switch (platformMouseEvent.type()) { + case PlatformEvent::MousePressed: { + if (isContextClick(platformMouseEvent)) + page->contextMenuController()->clearContextMenu(); + + bool handled = frame->eventHandler()->handleMousePressEvent(platformMouseEvent); + if (isContextClick(platformMouseEvent)) + handled = handleContextMenuEvent(platformMouseEvent, page); + + return handled; + } + case PlatformEvent::MouseReleased: + return frame->eventHandler()->handleMouseReleaseEvent(platformMouseEvent); + case PlatformEvent::MouseMoved: + return frame->eventHandler()->mouseMoved(platformMouseEvent, onlyUpdateScrollbars); + default: + ASSERT_NOT_REACHED(); + return false; + } +} + +void WebPage::mouseEvent(const WebMouseEvent& mouseEvent) +{ + // Don't try to handle any pending mouse events if a context menu is showing. + if (m_isShowingContextMenu) { + send(Messages::WebPageProxy::DidReceiveEvent(static_cast<uint32_t>(mouseEvent.type()), false)); + return; + } + + bool handled = false; + + if (m_pageOverlay) { + // Let the page overlay handle the event. + handled = m_pageOverlay->mouseEvent(mouseEvent); + } + + if (!handled) { + CurrentEvent currentEvent(mouseEvent); + + // We need to do a full, normal hit test during this mouse event if the page is active or if a mouse + // button is currently pressed. It is possible that neither of those things will be true since on + // Lion when legacy scrollbars are enabled, WebKit receives mouse events all the time. If it is one + // of those cases where the page is not active and the mouse is not pressed, then we can fire a more + // efficient scrollbars-only version of the event. + bool onlyUpdateScrollbars = !(m_page->focusController()->isActive() || (mouseEvent.button() != WebMouseEvent::NoButton)); + handled = handleMouseEvent(mouseEvent, m_page.get(), onlyUpdateScrollbars); + } + + send(Messages::WebPageProxy::DidReceiveEvent(static_cast<uint32_t>(mouseEvent.type()), handled)); +} + +void WebPage::mouseEventSyncForTesting(const WebMouseEvent& mouseEvent, bool& handled) +{ + // Don't try to handle any pending mouse events if a context menu is showing. + if (m_isShowingContextMenu) { + handled = true; + return; + } + + handled = m_pageOverlay && m_pageOverlay->mouseEvent(mouseEvent); + + if (!handled) { + CurrentEvent currentEvent(mouseEvent); + + // We need to do a full, normal hit test during this mouse event if the page is active or if a mouse + // button is currently pressed. It is possible that neither of those things will be true since on + // Lion when legacy scrollbars are enabled, WebKit receives mouse events all the time. If it is one + // of those cases where the page is not active and the mouse is not pressed, then we can fire a more + // efficient scrollbars-only version of the event. + bool onlyUpdateScrollbars = !(m_page->focusController()->isActive() || (mouseEvent.button() != WebMouseEvent::NoButton)); + handled = handleMouseEvent(mouseEvent, m_page.get(), onlyUpdateScrollbars); + } +} + +static bool handleWheelEvent(const WebWheelEvent& wheelEvent, Page* page) +{ + Frame* frame = page->mainFrame(); + if (!frame->view()) + return false; + + PlatformWheelEvent platformWheelEvent = platform(wheelEvent); + return frame->eventHandler()->handleWheelEvent(platformWheelEvent); +} + +void WebPage::wheelEvent(const WebWheelEvent& wheelEvent) +{ + CurrentEvent currentEvent(wheelEvent); + + bool handled = handleWheelEvent(wheelEvent, m_page.get()); + send(Messages::WebPageProxy::DidReceiveEvent(static_cast<uint32_t>(wheelEvent.type()), handled)); +} + +void WebPage::wheelEventSyncForTesting(const WebWheelEvent& wheelEvent, bool& handled) +{ + CurrentEvent currentEvent(wheelEvent); + + handled = handleWheelEvent(wheelEvent, m_page.get()); +} + +static bool handleKeyEvent(const WebKeyboardEvent& keyboardEvent, Page* page) +{ + if (!page->mainFrame()->view()) + return false; + + if (keyboardEvent.type() == WebEvent::Char && keyboardEvent.isSystemKey()) + return page->focusController()->focusedOrMainFrame()->eventHandler()->handleAccessKey(platform(keyboardEvent)); + return page->focusController()->focusedOrMainFrame()->eventHandler()->keyEvent(platform(keyboardEvent)); +} + +void WebPage::keyEvent(const WebKeyboardEvent& keyboardEvent) +{ + CurrentEvent currentEvent(keyboardEvent); + + bool handled = handleKeyEvent(keyboardEvent, m_page.get()); + // FIXME: Platform default behaviors should be performed during normal DOM event dispatch (in most cases, in default keydown event handler). + if (!handled) + handled = performDefaultBehaviorForKeyEvent(keyboardEvent); + + send(Messages::WebPageProxy::DidReceiveEvent(static_cast<uint32_t>(keyboardEvent.type()), handled)); +} + +void WebPage::keyEventSyncForTesting(const WebKeyboardEvent& keyboardEvent, bool& handled) +{ + CurrentEvent currentEvent(keyboardEvent); + + handled = handleKeyEvent(keyboardEvent, m_page.get()); + if (!handled) + handled = performDefaultBehaviorForKeyEvent(keyboardEvent); +} + +#if ENABLE(GESTURE_EVENTS) +static bool handleGestureEvent(const WebGestureEvent& gestureEvent, Page* page) +{ + Frame* frame = page->mainFrame(); + if (!frame->view()) + return false; + + PlatformGestureEvent platformGestureEvent = platform(gestureEvent); + return frame->eventHandler()->handleGestureEvent(platformGestureEvent); +} + +void WebPage::gestureEvent(const WebGestureEvent& gestureEvent) +{ + CurrentEvent currentEvent(gestureEvent); + + bool handled = handleGestureEvent(gestureEvent, m_page.get()); + send(Messages::WebPageProxy::DidReceiveEvent(static_cast<uint32_t>(gestureEvent.type()), handled)); +} +#endif + +void WebPage::validateCommand(const String& commandName, uint64_t callbackID) +{ + bool isEnabled = false; + int32_t state = 0; + Frame* frame = m_page->focusController()->focusedOrMainFrame(); + if (frame) { + Editor::Command command = frame->editor()->command(commandName); + state = command.state(); + isEnabled = command.isSupported() && command.isEnabled(); + } + + send(Messages::WebPageProxy::ValidateCommandCallback(commandName, isEnabled, state, callbackID)); +} + +void WebPage::executeEditCommand(const String& commandName) +{ + executeEditingCommand(commandName, String()); +} + +uint64_t WebPage::restoreSession(const SessionState& sessionState) +{ + const BackForwardListItemVector& list = sessionState.list(); + size_t size = list.size(); + uint64_t currentItemID = 0; + for (size_t i = 0; i < size; ++i) { + WebBackForwardListItem* webItem = list[i].get(); + DecoderAdapter decoder(webItem->backForwardData().data(), webItem->backForwardData().size()); + + RefPtr<HistoryItem> item = HistoryItem::decodeBackForwardTree(webItem->url(), webItem->title(), webItem->originalURL(), decoder); + if (!item) { + LOG_ERROR("Failed to decode a HistoryItem from session state data."); + return 0; + } + + if (i == sessionState.currentIndex()) + currentItemID = webItem->itemID(); + + WebBackForwardListProxy::addItemFromUIProcess(list[i]->itemID(), item.release()); + } + ASSERT(currentItemID); + return currentItemID; +} + +void WebPage::restoreSessionAndNavigateToCurrentItem(const SessionState& sessionState, const SandboxExtension::Handle& sandboxExtensionHandle) +{ + if (uint64_t currentItemID = restoreSession(sessionState)) + goToBackForwardItem(currentItemID, sandboxExtensionHandle); +} + +#if ENABLE(TOUCH_EVENTS) +static bool handleTouchEvent(const WebTouchEvent& touchEvent, Page* page) +{ + Frame* frame = page->mainFrame(); + if (!frame->view()) + return false; + + return frame->eventHandler()->handleTouchEvent(platform(touchEvent)); +} + +void WebPage::touchEvent(const WebTouchEvent& touchEvent) +{ + CurrentEvent currentEvent(touchEvent); + + bool handled = handleTouchEvent(touchEvent, m_page.get()); + + send(Messages::WebPageProxy::DidReceiveEvent(static_cast<uint32_t>(touchEvent.type()), handled)); +} + +void WebPage::touchEventSyncForTesting(const WebTouchEvent& touchEvent, bool& handled) +{ + CurrentEvent currentEvent(touchEvent); + handled = handleTouchEvent(touchEvent, m_page.get()); +} +#endif + +void WebPage::scroll(Page* page, ScrollDirection direction, ScrollGranularity granularity) +{ + page->focusController()->focusedOrMainFrame()->eventHandler()->scrollRecursively(direction, granularity); +} + +void WebPage::logicalScroll(Page* page, ScrollLogicalDirection direction, ScrollGranularity granularity) +{ + page->focusController()->focusedOrMainFrame()->eventHandler()->logicalScrollRecursively(direction, granularity); +} + +void WebPage::scrollBy(uint32_t scrollDirection, uint32_t scrollGranularity) +{ + scroll(m_page.get(), static_cast<ScrollDirection>(scrollDirection), static_cast<ScrollGranularity>(scrollGranularity)); +} + +void WebPage::centerSelectionInVisibleArea() +{ + Frame* frame = m_page->focusController()->focusedOrMainFrame(); + if (!frame) + return; + + frame->selection()->revealSelection(ScrollAlignment::alignCenterAlways); + m_findController.showFindIndicatorInSelection(); +} + +void WebPage::setActive(bool isActive) +{ + m_page->focusController()->setActive(isActive); + +#if PLATFORM(MAC) + // Tell all our plug-in views that the window focus changed. + for (HashSet<PluginView*>::const_iterator it = m_pluginViews.begin(), end = m_pluginViews.end(); it != end; ++it) + (*it)->setWindowIsFocused(isActive); +#endif +} + +void WebPage::setDrawsBackground(bool drawsBackground) +{ + if (m_drawsBackground == drawsBackground) + return; + + m_drawsBackground = drawsBackground; + + for (Frame* coreFrame = m_mainFrame->coreFrame(); coreFrame; coreFrame = coreFrame->tree()->traverseNext()) { + if (FrameView* view = coreFrame->view()) + view->setTransparent(!drawsBackground); + } + + m_drawingArea->pageBackgroundTransparencyChanged(); + m_drawingArea->setNeedsDisplay(IntRect(IntPoint(0, 0), m_viewSize)); +} + +void WebPage::setDrawsTransparentBackground(bool drawsTransparentBackground) +{ + if (m_drawsTransparentBackground == drawsTransparentBackground) + return; + + m_drawsTransparentBackground = drawsTransparentBackground; + + Color backgroundColor = drawsTransparentBackground ? Color::transparent : Color::white; + for (Frame* coreFrame = m_mainFrame->coreFrame(); coreFrame; coreFrame = coreFrame->tree()->traverseNext()) { + if (FrameView* view = coreFrame->view()) + view->setBaseBackgroundColor(backgroundColor); + } + + m_drawingArea->pageBackgroundTransparencyChanged(); + m_drawingArea->setNeedsDisplay(IntRect(IntPoint(0, 0), m_viewSize)); +} + +void WebPage::viewWillStartLiveResize() +{ + if (!m_page) + return; + + // FIXME: This should propagate to all ScrollableAreas. + if (Frame* frame = m_page->focusController()->focusedOrMainFrame()) { + if (FrameView* view = frame->view()) + view->willStartLiveResize(); + } +} + +void WebPage::viewWillEndLiveResize() +{ + if (!m_page) + return; + + // FIXME: This should propagate to all ScrollableAreas. + if (Frame* frame = m_page->focusController()->focusedOrMainFrame()) { + if (FrameView* view = frame->view()) + view->willEndLiveResize(); + } +} + +void WebPage::setFocused(bool isFocused) +{ + m_page->focusController()->setFocused(isFocused); +} + +void WebPage::setInitialFocus(bool forward, bool isKeyboardEventValid, const WebKeyboardEvent& event) +{ + if (!m_page || !m_page->focusController()) + return; + + Frame* frame = m_page->focusController()->focusedOrMainFrame(); + frame->document()->setFocusedNode(0); + + if (isKeyboardEventValid && event.type() == WebEvent::KeyDown) { + PlatformKeyboardEvent platformEvent(platform(event)); + platformEvent.disambiguateKeyDownEvent(PlatformEvent::RawKeyDown); + m_page->focusController()->setInitialFocus(forward ? FocusDirectionForward : FocusDirectionBackward, KeyboardEvent::create(platformEvent, frame->document()->defaultView()).get()); + return; + } + + m_page->focusController()->setInitialFocus(forward ? FocusDirectionForward : FocusDirectionBackward, 0); +} + +void WebPage::setWindowResizerSize(const IntSize& windowResizerSize) +{ + if (m_windowResizerSize == windowResizerSize) + return; + + m_windowResizerSize = windowResizerSize; + + for (Frame* coreFrame = m_mainFrame->coreFrame(); coreFrame; coreFrame = coreFrame->tree()->traverseNext()) { + FrameView* view = coreFrame->view(); + if (view) + view->windowResizerRectChanged(); + } +} + +void WebPage::setCanStartMediaTimerFired() +{ + if (m_page) + m_page->setCanStartMedia(true); +} + +void WebPage::setIsInWindow(bool isInWindow) +{ + if (!isInWindow) { + m_setCanStartMediaTimer.stop(); + m_page->setCanStartMedia(false); + m_page->willMoveOffscreen(); + } else { + // Defer the call to Page::setCanStartMedia() since it ends up sending a syncrhonous messages to the UI process + // in order to get plug-in connections, and the UI process will be waiting for the Web process to update the backing + // store after moving the view into a window, until it times out and paints white. See <rdar://problem/9242771>. + m_setCanStartMediaTimer.startOneShot(0); + m_page->didMoveOnscreen(); + } +} + +void WebPage::didReceivePolicyDecision(uint64_t frameID, uint64_t listenerID, uint32_t policyAction, uint64_t downloadID) +{ + WebFrame* frame = WebProcess::shared().webFrame(frameID); + if (!frame) + return; + frame->didReceivePolicyDecision(listenerID, static_cast<PolicyAction>(policyAction), downloadID); +} + +void WebPage::show() +{ + send(Messages::WebPageProxy::ShowPage()); +} + +void WebPage::setUserAgent(const String& userAgent) +{ + m_userAgent = userAgent; +} + +IntPoint WebPage::screenToWindow(const IntPoint& point) +{ + IntPoint windowPoint; + sendSync(Messages::WebPageProxy::ScreenToWindow(point), Messages::WebPageProxy::ScreenToWindow::Reply(windowPoint)); + return windowPoint; +} + +IntRect WebPage::windowToScreen(const IntRect& rect) +{ + IntRect screenRect; + sendSync(Messages::WebPageProxy::WindowToScreen(rect), Messages::WebPageProxy::WindowToScreen::Reply(screenRect)); + return screenRect; +} + +IntRect WebPage::windowResizerRect() const +{ + if (m_windowResizerSize.isEmpty()) + return IntRect(); + + IntSize frameViewSize; + if (Frame* coreFrame = m_mainFrame->coreFrame()) { + if (FrameView* view = coreFrame->view()) + frameViewSize = view->size(); + } + + return IntRect(frameViewSize.width() - m_windowResizerSize.width(), frameViewSize.height() - m_windowResizerSize.height(), + m_windowResizerSize.width(), m_windowResizerSize.height()); +} + +KeyboardUIMode WebPage::keyboardUIMode() +{ + bool fullKeyboardAccessEnabled = WebProcess::shared().fullKeyboardAccessEnabled(); + return static_cast<KeyboardUIMode>((fullKeyboardAccessEnabled ? KeyboardAccessFull : KeyboardAccessDefault) | (m_tabToLinks ? KeyboardAccessTabsToLinks : 0)); +} + +void WebPage::runJavaScriptInMainFrame(const String& script, uint64_t callbackID) +{ + // NOTE: We need to be careful when running scripts that the objects we depend on don't + // disappear during script execution. + + // Retain the SerializedScriptValue at this level so it (and the internal data) lives + // long enough for the DataReference to be encoded by the sent message. + RefPtr<SerializedScriptValue> serializedResultValue; + CoreIPC::DataReference dataReference; + + JSLock lock(SilenceAssertionsOnly); + if (JSValue resultValue = m_mainFrame->coreFrame()->script()->executeScript(script, true).jsValue()) { + if ((serializedResultValue = SerializedScriptValue::create(m_mainFrame->jsContext(), toRef(m_mainFrame->coreFrame()->script()->globalObject(mainThreadNormalWorld())->globalExec(), resultValue), 0))) + dataReference = serializedResultValue->data(); + } + + send(Messages::WebPageProxy::ScriptValueCallback(dataReference, callbackID)); +} + +void WebPage::getContentsAsString(uint64_t callbackID) +{ + String resultString = m_mainFrame->contentsAsString(); + send(Messages::WebPageProxy::StringCallback(resultString, callbackID)); +} + +void WebPage::getRenderTreeExternalRepresentation(uint64_t callbackID) +{ + String resultString = renderTreeExternalRepresentation(); + send(Messages::WebPageProxy::StringCallback(resultString, callbackID)); +} + +void WebPage::getSelectionOrContentsAsString(uint64_t callbackID) +{ + String resultString = m_mainFrame->selectionAsString(); + if (resultString.isEmpty()) + resultString = m_mainFrame->contentsAsString(); + send(Messages::WebPageProxy::StringCallback(resultString, callbackID)); +} + +void WebPage::getSourceForFrame(uint64_t frameID, uint64_t callbackID) +{ + String resultString; + if (WebFrame* frame = WebProcess::shared().webFrame(frameID)) + resultString = frame->source(); + + send(Messages::WebPageProxy::StringCallback(resultString, callbackID)); +} + +void WebPage::getMainResourceDataOfFrame(uint64_t frameID, uint64_t callbackID) +{ + CoreIPC::DataReference dataReference; + + RefPtr<SharedBuffer> buffer; + if (WebFrame* frame = WebProcess::shared().webFrame(frameID)) { + if (DocumentLoader* loader = frame->coreFrame()->loader()->documentLoader()) { + if ((buffer = loader->mainResourceData())) + dataReference = CoreIPC::DataReference(reinterpret_cast<const uint8_t*>(buffer->data()), buffer->size()); + } + } + + send(Messages::WebPageProxy::DataCallback(dataReference, callbackID)); +} + +static PassRefPtr<SharedBuffer> resourceDataForFrame(Frame* frame, const KURL& resourceURL) +{ + DocumentLoader* loader = frame->loader()->documentLoader(); + if (!loader) + return 0; + + RefPtr<ArchiveResource> subresource = loader->subresource(resourceURL); + if (!subresource) + return 0; + + return subresource->data(); +} + +void WebPage::getResourceDataFromFrame(uint64_t frameID, const String& resourceURLString, uint64_t callbackID) +{ + CoreIPC::DataReference dataReference; + KURL resourceURL(KURL(), resourceURLString); + + RefPtr<SharedBuffer> buffer; + if (WebFrame* frame = WebProcess::shared().webFrame(frameID)) { + buffer = resourceDataForFrame(frame->coreFrame(), resourceURL); + if (!buffer) { + // Try to get the resource data from the cache. + buffer = cachedResponseDataForURL(resourceURL); + } + + if (buffer) + dataReference = CoreIPC::DataReference(reinterpret_cast<const uint8_t*>(buffer->data()), buffer->size()); + } + + send(Messages::WebPageProxy::DataCallback(dataReference, callbackID)); +} + +void WebPage::getWebArchiveOfFrame(uint64_t frameID, uint64_t callbackID) +{ + CoreIPC::DataReference dataReference; + +#if PLATFORM(MAC) || PLATFORM(WIN) + RetainPtr<CFDataRef> data; + if (WebFrame* frame = WebProcess::shared().webFrame(frameID)) { + if ((data = frame->webArchiveData())) + dataReference = CoreIPC::DataReference(CFDataGetBytePtr(data.get()), CFDataGetLength(data.get())); + } +#endif + + send(Messages::WebPageProxy::DataCallback(dataReference, callbackID)); +} + +void WebPage::forceRepaintWithoutCallback() +{ + m_drawingArea->forceRepaint(); +} + +void WebPage::forceRepaint(uint64_t callbackID) +{ + forceRepaintWithoutCallback(); + send(Messages::WebPageProxy::VoidCallback(callbackID)); +} + +void WebPage::preferencesDidChange(const WebPreferencesStore& store) +{ + WebPreferencesStore::removeTestRunnerOverrides(); + updatePreferences(store); +} + +void WebPage::updatePreferences(const WebPreferencesStore& store) +{ + Settings* settings = m_page->settings(); + + m_tabToLinks = store.getBoolValueForKey(WebPreferencesKey::tabsToLinksKey()); + + // FIXME: This should be generated from macro expansion for all preferences, + // but we currently don't match the naming of WebCore exactly so we are + // handrolling the boolean and integer preferences until that is fixed. + +#define INITIALIZE_SETTINGS(KeyUpper, KeyLower, TypeName, Type, DefaultValue) settings->set##KeyUpper(store.get##TypeName##ValueForKey(WebPreferencesKey::KeyLower##Key())); + + FOR_EACH_WEBKIT_STRING_PREFERENCE(INITIALIZE_SETTINGS) + +#undef INITIALIZE_SETTINGS + + settings->setScriptEnabled(store.getBoolValueForKey(WebPreferencesKey::javaScriptEnabledKey())); + settings->setLoadsImagesAutomatically(store.getBoolValueForKey(WebPreferencesKey::loadsImagesAutomaticallyKey())); + settings->setLoadsSiteIconsIgnoringImageLoadingSetting(store.getBoolValueForKey(WebPreferencesKey::loadsSiteIconsIgnoringImageLoadingPreferenceKey())); + settings->setPluginsEnabled(store.getBoolValueForKey(WebPreferencesKey::pluginsEnabledKey())); + settings->setJavaEnabled(store.getBoolValueForKey(WebPreferencesKey::javaEnabledKey())); + settings->setOfflineWebApplicationCacheEnabled(store.getBoolValueForKey(WebPreferencesKey::offlineWebApplicationCacheEnabledKey())); + settings->setLocalStorageEnabled(store.getBoolValueForKey(WebPreferencesKey::localStorageEnabledKey())); + settings->setXSSAuditorEnabled(store.getBoolValueForKey(WebPreferencesKey::xssAuditorEnabledKey())); + settings->setFrameFlatteningEnabled(store.getBoolValueForKey(WebPreferencesKey::frameFlatteningEnabledKey())); + settings->setPrivateBrowsingEnabled(store.getBoolValueForKey(WebPreferencesKey::privateBrowsingEnabledKey())); + settings->setDeveloperExtrasEnabled(store.getBoolValueForKey(WebPreferencesKey::developerExtrasEnabledKey())); + settings->setTextAreasAreResizable(store.getBoolValueForKey(WebPreferencesKey::textAreasAreResizableKey())); + settings->setNeedsSiteSpecificQuirks(store.getBoolValueForKey(WebPreferencesKey::needsSiteSpecificQuirksKey())); + settings->setJavaScriptCanOpenWindowsAutomatically(store.getBoolValueForKey(WebPreferencesKey::javaScriptCanOpenWindowsAutomaticallyKey())); + settings->setForceFTPDirectoryListings(store.getBoolValueForKey(WebPreferencesKey::forceFTPDirectoryListingsKey())); + settings->setDNSPrefetchingEnabled(store.getBoolValueForKey(WebPreferencesKey::dnsPrefetchingEnabledKey())); +#if ENABLE(WEB_ARCHIVE) + settings->setWebArchiveDebugModeEnabled(store.getBoolValueForKey(WebPreferencesKey::webArchiveDebugModeEnabledKey())); +#endif + settings->setLocalFileContentSniffingEnabled(store.getBoolValueForKey(WebPreferencesKey::localFileContentSniffingEnabledKey())); + settings->setUsesPageCache(store.getBoolValueForKey(WebPreferencesKey::usesPageCacheKey())); + settings->setPageCacheSupportsPlugins(store.getBoolValueForKey(WebPreferencesKey::pageCacheSupportsPluginsKey())); + settings->setAuthorAndUserStylesEnabled(store.getBoolValueForKey(WebPreferencesKey::authorAndUserStylesEnabledKey())); + settings->setPaginateDuringLayoutEnabled(store.getBoolValueForKey(WebPreferencesKey::paginateDuringLayoutEnabledKey())); + settings->setDOMPasteAllowed(store.getBoolValueForKey(WebPreferencesKey::domPasteAllowedKey())); + settings->setJavaScriptCanAccessClipboard(store.getBoolValueForKey(WebPreferencesKey::javaScriptCanAccessClipboardKey())); + settings->setShouldPrintBackgrounds(store.getBoolValueForKey(WebPreferencesKey::shouldPrintBackgroundsKey())); + settings->setWebSecurityEnabled(store.getBoolValueForKey(WebPreferencesKey::webSecurityEnabledKey())); + settings->setAllowUniversalAccessFromFileURLs(store.getBoolValueForKey(WebPreferencesKey::allowUniversalAccessFromFileURLsKey())); + settings->setAllowFileAccessFromFileURLs(store.getBoolValueForKey(WebPreferencesKey::allowFileAccessFromFileURLsKey())); + + settings->setMinimumFontSize(store.getUInt32ValueForKey(WebPreferencesKey::minimumFontSizeKey())); + settings->setMinimumLogicalFontSize(store.getUInt32ValueForKey(WebPreferencesKey::minimumLogicalFontSizeKey())); + settings->setDefaultFontSize(store.getUInt32ValueForKey(WebPreferencesKey::defaultFontSizeKey())); + settings->setDefaultFixedFontSize(store.getUInt32ValueForKey(WebPreferencesKey::defaultFixedFontSizeKey())); + settings->setLayoutFallbackWidth(store.getUInt32ValueForKey(WebPreferencesKey::layoutFallbackWidthKey())); + settings->setDeviceDPI(store.getUInt32ValueForKey(WebPreferencesKey::deviceDPIKey())); + settings->setDeviceWidth(store.getUInt32ValueForKey(WebPreferencesKey::deviceWidthKey())); + settings->setDeviceHeight(store.getUInt32ValueForKey(WebPreferencesKey::deviceHeightKey())); + settings->setEditableLinkBehavior(static_cast<WebCore::EditableLinkBehavior>(store.getUInt32ValueForKey(WebPreferencesKey::editableLinkBehaviorKey()))); + settings->setShowsToolTipOverTruncatedText(store.getBoolValueForKey(WebPreferencesKey::showsToolTipOverTruncatedTextKey())); + + settings->setAcceleratedCompositingEnabled(store.getBoolValueForKey(WebPreferencesKey::acceleratedCompositingEnabledKey()) && LayerTreeHost::supportsAcceleratedCompositing()); + settings->setAcceleratedDrawingEnabled(store.getBoolValueForKey(WebPreferencesKey::acceleratedDrawingEnabledKey()) && LayerTreeHost::supportsAcceleratedCompositing()); + settings->setCanvasUsesAcceleratedDrawing(store.getBoolValueForKey(WebPreferencesKey::canvasUsesAcceleratedDrawingKey()) && LayerTreeHost::supportsAcceleratedCompositing()); + settings->setShowDebugBorders(store.getBoolValueForKey(WebPreferencesKey::compositingBordersVisibleKey())); + settings->setShowRepaintCounter(store.getBoolValueForKey(WebPreferencesKey::compositingRepaintCountersVisibleKey())); + settings->setWebGLEnabled(store.getBoolValueForKey(WebPreferencesKey::webGLEnabledKey())); + settings->setMediaPlaybackRequiresUserGesture(store.getBoolValueForKey(WebPreferencesKey::mediaPlaybackRequiresUserGestureKey())); + settings->setMediaPlaybackAllowsInline(store.getBoolValueForKey(WebPreferencesKey::mediaPlaybackAllowsInlineKey())); + settings->setMockScrollbarsEnabled(store.getBoolValueForKey(WebPreferencesKey::mockScrollbarsEnabledKey())); + +#if ENABLE(SQL_DATABASE) + AbstractDatabase::setIsAvailable(store.getBoolValueForKey(WebPreferencesKey::databasesEnabledKey())); +#endif + +#if ENABLE(FULLSCREEN_API) + settings->setFullScreenEnabled(store.getBoolValueForKey(WebPreferencesKey::fullScreenEnabledKey())); +#endif + + settings->setLocalStorageDatabasePath(WebProcess::shared().localStorageDirectory()); + +#if USE(AVFOUNDATION) + settings->setAVFoundationEnabled(store.getBoolValueForKey(WebPreferencesKey::isAVFoundationEnabledKey())); +#endif + +#if ENABLE(WEB_SOCKETS) + settings->setUseHixie76WebSocketProtocol(store.getBoolValueForKey(WebPreferencesKey::hixie76WebSocketProtocolEnabledKey())); +#endif + +#if ENABLE(WEB_AUDIO) + settings->setWebAudioEnabled(store.getBoolValueForKey(WebPreferencesKey::webAudioEnabledKey())); +#endif + + settings->setApplicationChromeMode(store.getBoolValueForKey(WebPreferencesKey::applicationChromeModeKey())); + settings->setSuppressIncrementalRendering(store.getBoolValueForKey(WebPreferencesKey::suppressIncrementalRenderingKey())); + settings->setBackspaceKeyNavigationEnabled(store.getBoolValueForKey(WebPreferencesKey::backspaceKeyNavigationEnabledKey())); + settings->setCaretBrowsingEnabled(store.getBoolValueForKey(WebPreferencesKey::caretBrowsingEnabledKey())); + +#if ENABLE(VIDEO_TRACK) + settings->setShouldDisplaySubtitles(store.getBoolValueForKey(WebPreferencesKey::shouldDisplaySubtitlesKey())); + settings->setShouldDisplayCaptions(store.getBoolValueForKey(WebPreferencesKey::shouldDisplayCaptionsKey())); + settings->setShouldDisplayTextDescriptions(store.getBoolValueForKey(WebPreferencesKey::shouldDisplayTextDescriptionsKey())); +#endif + + platformPreferencesDidChange(store); +} + +#if ENABLE(INSPECTOR) +WebInspector* WebPage::inspector() +{ + if (m_isClosed) + return 0; + if (!m_inspector) + m_inspector = WebInspector::create(this); + return m_inspector.get(); +} +#endif + +#if ENABLE(FULLSCREEN_API) +WebFullScreenManager* WebPage::fullScreenManager() +{ + if (!m_fullScreenManager) + m_fullScreenManager = WebFullScreenManager::create(this); + return m_fullScreenManager.get(); +} +#endif + +NotificationPermissionRequestManager* WebPage::notificationPermissionRequestManager() +{ + if (m_notificationPermissionRequestManager) + return m_notificationPermissionRequestManager.get(); + + m_notificationPermissionRequestManager = NotificationPermissionRequestManager::create(this); + return m_notificationPermissionRequestManager.get(); +} + +#if !PLATFORM(GTK) && !PLATFORM(MAC) +bool WebPage::handleEditingKeyboardEvent(KeyboardEvent* evt) +{ + Node* node = evt->target()->toNode(); + ASSERT(node); + Frame* frame = node->document()->frame(); + ASSERT(frame); + + const PlatformKeyboardEvent* keyEvent = evt->keyEvent(); + if (!keyEvent) + return false; + + Editor::Command command = frame->editor()->command(interpretKeyEvent(evt)); + + if (keyEvent->type() == PlatformEvent::RawKeyDown) { + // WebKit doesn't have enough information about mode to decide how commands that just insert text if executed via Editor should be treated, + // so we leave it upon WebCore to either handle them immediately (e.g. Tab that changes focus) or let a keypress event be generated + // (e.g. Tab that inserts a Tab character, or Enter). + return !command.isTextInsertion() && command.execute(evt); + } + + if (command.execute(evt)) + return true; + + // Don't insert null or control characters as they can result in unexpected behaviour + if (evt->charCode() < ' ') + return false; + + return frame->editor()->insertText(evt->keyEvent()->text(), evt); +} +#endif + +#if PLATFORM(WIN) +void WebPage::performDragControllerAction(uint64_t action, WebCore::IntPoint clientPosition, WebCore::IntPoint globalPosition, uint64_t draggingSourceOperationMask, const WebCore::DragDataMap& dataMap, uint32_t flags) +{ + if (!m_page) { + send(Messages::WebPageProxy::DidPerformDragControllerAction(WebCore::DragSession())); + return; + } + + DragData dragData(dataMap, clientPosition, globalPosition, static_cast<DragOperation>(draggingSourceOperationMask), static_cast<DragApplicationFlags>(flags)); + switch (action) { + case DragControllerActionEntered: + send(Messages::WebPageProxy::DidPerformDragControllerAction(m_page->dragController()->dragEntered(&dragData))); + break; + + case DragControllerActionUpdated: + send(Messages::WebPageProxy::DidPerformDragControllerAction(m_page->dragController()->dragUpdated(&dragData))); + break; + + case DragControllerActionExited: + m_page->dragController()->dragExited(&dragData); + break; + + case DragControllerActionPerformDrag: + m_page->dragController()->performDrag(&dragData); + break; + + default: + ASSERT_NOT_REACHED(); + } +} + +#elif PLATFORM(QT) || PLATFORM(GTK) +void WebPage::performDragControllerAction(uint64_t action, WebCore::DragData dragData) +{ + if (!m_page) { + send(Messages::WebPageProxy::DidPerformDragControllerAction(WebCore::DragSession())); +#if PLATFORM(QT) + QMimeData* data = const_cast<QMimeData*>(dragData.platformData()); +#elif PLATFORM(GTK) + DataObjectGtk* data = const_cast<DataObjectGtk*>(dragData.platformData()); +#endif + delete data; + return; + } + + switch (action) { + case DragControllerActionEntered: + send(Messages::WebPageProxy::DidPerformDragControllerAction(m_page->dragController()->dragEntered(&dragData))); + break; + + case DragControllerActionUpdated: + send(Messages::WebPageProxy::DidPerformDragControllerAction(m_page->dragController()->dragUpdated(&dragData))); + break; + + case DragControllerActionExited: + m_page->dragController()->dragExited(&dragData); + break; + + case DragControllerActionPerformDrag: { + m_page->dragController()->performDrag(&dragData); + break; + } + + default: + ASSERT_NOT_REACHED(); + } + // DragData does not delete its platformData so we need to do that here. +#if PLATFORM(QT) + QMimeData* data = const_cast<QMimeData*>(dragData.platformData()); +#elif PLATFORM(GTK) + DataObjectGtk* data = const_cast<DataObjectGtk*>(dragData.platformData()); +#endif + delete data; +} + +#else +void WebPage::performDragControllerAction(uint64_t action, WebCore::IntPoint clientPosition, WebCore::IntPoint globalPosition, uint64_t draggingSourceOperationMask, const String& dragStorageName, uint32_t flags, const SandboxExtension::Handle& sandboxExtensionHandle) +{ + if (!m_page) { + send(Messages::WebPageProxy::DidPerformDragControllerAction(WebCore::DragSession())); + return; + } + + DragData dragData(dragStorageName, clientPosition, globalPosition, static_cast<DragOperation>(draggingSourceOperationMask), static_cast<DragApplicationFlags>(flags)); + switch (action) { + case DragControllerActionEntered: + send(Messages::WebPageProxy::DidPerformDragControllerAction(m_page->dragController()->dragEntered(&dragData))); + break; + + case DragControllerActionUpdated: + send(Messages::WebPageProxy::DidPerformDragControllerAction(m_page->dragController()->dragUpdated(&dragData))); + break; + + case DragControllerActionExited: + m_page->dragController()->dragExited(&dragData); + break; + + case DragControllerActionPerformDrag: { + ASSERT(!m_pendingDropSandboxExtension); + + m_pendingDropSandboxExtension = SandboxExtension::create(sandboxExtensionHandle); + + m_page->dragController()->performDrag(&dragData); + + // If we started loading a local file, the sandbox extension tracker would have adopted this + // pending drop sandbox extension. If not, we'll play it safe and invalidate it. + if (m_pendingDropSandboxExtension) { + m_pendingDropSandboxExtension->invalidate(); + m_pendingDropSandboxExtension = nullptr; + } + + break; + } + + default: + ASSERT_NOT_REACHED(); + } +} +#endif + +void WebPage::dragEnded(WebCore::IntPoint clientPosition, WebCore::IntPoint globalPosition, uint64_t operation) +{ + IntPoint adjustedClientPosition(clientPosition.x() + m_page->dragController()->dragOffset().x(), clientPosition.y() + m_page->dragController()->dragOffset().y()); + IntPoint adjustedGlobalPosition(globalPosition.x() + m_page->dragController()->dragOffset().x(), globalPosition.y() + m_page->dragController()->dragOffset().y()); + + m_page->dragController()->dragEnded(); + FrameView* view = m_page->mainFrame()->view(); + if (!view) + return; + // FIXME: These are fake modifier keys here, but they should be real ones instead. + PlatformMouseEvent event(adjustedClientPosition, adjustedGlobalPosition, LeftButton, PlatformEvent::MouseMoved, 0, false, false, false, false, currentTime()); + m_page->mainFrame()->eventHandler()->dragSourceEndedAt(event, (DragOperation)operation); +} + +void WebPage::willPerformLoadDragDestinationAction() +{ + m_sandboxExtensionTracker.willPerformLoadDragDestinationAction(m_pendingDropSandboxExtension.release()); +} + +WebUndoStep* WebPage::webUndoStep(uint64_t stepID) +{ + return m_undoStepMap.get(stepID).get(); +} + +void WebPage::addWebUndoStep(uint64_t stepID, WebUndoStep* entry) +{ + m_undoStepMap.set(stepID, entry); +} + +void WebPage::removeWebEditCommand(uint64_t stepID) +{ + m_undoStepMap.remove(stepID); +} + +void WebPage::unapplyEditCommand(uint64_t stepID) +{ + WebUndoStep* step = webUndoStep(stepID); + if (!step) + return; + + step->step()->unapply(); +} + +void WebPage::reapplyEditCommand(uint64_t stepID) +{ + WebUndoStep* step = webUndoStep(stepID); + if (!step) + return; + + m_isInRedo = true; + step->step()->reapply(); + m_isInRedo = false; +} + +void WebPage::didRemoveEditCommand(uint64_t commandID) +{ + removeWebEditCommand(commandID); +} + +void WebPage::setActivePopupMenu(WebPopupMenu* menu) +{ + m_activePopupMenu = menu; +} + +void WebPage::setActiveOpenPanelResultListener(PassRefPtr<WebOpenPanelResultListener> openPanelResultListener) +{ + m_activeOpenPanelResultListener = openPanelResultListener; +} + +bool WebPage::findStringFromInjectedBundle(const String& target, FindOptions options) +{ + return m_page->findString(target, options); +} + +void WebPage::findString(const String& string, uint32_t options, uint32_t maxMatchCount) +{ + m_findController.findString(string, static_cast<FindOptions>(options), maxMatchCount); +} + +void WebPage::hideFindUI() +{ + m_findController.hideFindUI(); +} + +void WebPage::countStringMatches(const String& string, uint32_t options, uint32_t maxMatchCount) +{ + m_findController.countStringMatches(string, static_cast<FindOptions>(options), maxMatchCount); +} + +void WebPage::didChangeSelectedIndexForActivePopupMenu(int32_t newIndex) +{ + if (!m_activePopupMenu) + return; + + m_activePopupMenu->didChangeSelectedIndex(newIndex); + m_activePopupMenu = 0; +} + +void WebPage::didChooseFilesForOpenPanel(const Vector<String>& files) +{ + if (!m_activeOpenPanelResultListener) + return; + + m_activeOpenPanelResultListener->didChooseFiles(files); + m_activeOpenPanelResultListener = 0; +} + +void WebPage::didCancelForOpenPanel() +{ + m_activeOpenPanelResultListener = 0; +} + +#if ENABLE(WEB_PROCESS_SANDBOX) +void WebPage::extendSandboxForFileFromOpenPanel(const SandboxExtension::Handle& handle) +{ + SandboxExtension::create(handle)->consumePermanently(); +} +#endif + +void WebPage::didReceiveGeolocationPermissionDecision(uint64_t geolocationID, bool allowed) +{ + m_geolocationPermissionRequestManager.didReceiveGeolocationPermissionDecision(geolocationID, allowed); +} + +void WebPage::didReceiveNotificationPermissionDecision(uint64_t notificationID, bool allowed) +{ + notificationPermissionRequestManager()->didReceiveNotificationPermissionDecision(notificationID, allowed); +} + +void WebPage::advanceToNextMisspelling(bool startBeforeSelection) +{ + Frame* frame = m_page->focusController()->focusedOrMainFrame(); + frame->editor()->advanceToNextMisspelling(startBeforeSelection); +} + +void WebPage::changeSpellingToWord(const String& word) +{ + replaceSelectionWithText(m_page->focusController()->focusedOrMainFrame(), word); +} + +void WebPage::unmarkAllMisspellings() +{ + for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext()) { + if (Document* document = frame->document()) + document->markers()->removeMarkers(DocumentMarker::Spelling); + } +} + +void WebPage::unmarkAllBadGrammar() +{ + for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext()) { + if (Document* document = frame->document()) + document->markers()->removeMarkers(DocumentMarker::Grammar); + } +} + +#if PLATFORM(MAC) +void WebPage::uppercaseWord() +{ + m_page->focusController()->focusedOrMainFrame()->editor()->uppercaseWord(); +} + +void WebPage::lowercaseWord() +{ + m_page->focusController()->focusedOrMainFrame()->editor()->lowercaseWord(); +} + +void WebPage::capitalizeWord() +{ + m_page->focusController()->focusedOrMainFrame()->editor()->capitalizeWord(); +} +#endif + +void WebPage::setTextForActivePopupMenu(int32_t index) +{ + if (!m_activePopupMenu) + return; + + m_activePopupMenu->setTextForIndex(index); +} + +#if PLATFORM(GTK) +void WebPage::failedToShowPopupMenu() +{ + if (!m_activePopupMenu) + return; + + m_activePopupMenu->client()->popupDidHide(); +} +#endif + +void WebPage::didSelectItemFromActiveContextMenu(const WebContextMenuItemData& item) +{ + if (!m_contextMenu) + return; + + m_contextMenu->itemSelected(item); + m_contextMenu = 0; +} + +void WebPage::replaceSelectionWithText(Frame* frame, const String& text) +{ + bool selectReplacement = true; + bool smartReplace = false; + return frame->editor()->replaceSelectionWithText(text, selectReplacement, smartReplace); +} + +void WebPage::clearSelection() +{ + m_page->focusController()->focusedOrMainFrame()->selection()->clear(); +} + +bool WebPage::mainFrameHasCustomRepresentation() const +{ + if (Frame* frame = mainFrame()) + return static_cast<WebFrameLoaderClient*>(frame->loader()->client())->frameHasCustomRepresentation(); + + return false; +} + +void WebPage::didChangeScrollOffsetForMainFrame() +{ + Frame* frame = m_page->mainFrame(); + IntPoint scrollPosition = frame->view()->scrollPosition(); + IntPoint maximumScrollPosition = frame->view()->maximumScrollPosition(); + IntPoint minimumScrollPosition = frame->view()->minimumScrollPosition(); + + bool isPinnedToLeftSide = (scrollPosition.x() <= minimumScrollPosition.x()); + bool isPinnedToRightSide = (scrollPosition.x() >= maximumScrollPosition.x()); + + if (isPinnedToLeftSide != m_cachedMainFrameIsPinnedToLeftSide || isPinnedToRightSide != m_cachedMainFrameIsPinnedToRightSide) { + send(Messages::WebPageProxy::DidChangeScrollOffsetPinningForMainFrame(isPinnedToLeftSide, isPinnedToRightSide)); + + m_cachedMainFrameIsPinnedToLeftSide = isPinnedToLeftSide; + m_cachedMainFrameIsPinnedToRightSide = isPinnedToRightSide; + } +} + +void WebPage::mainFrameDidLayout() +{ + unsigned pageCount = m_page->pageCount(); + if (pageCount != m_cachedPageCount) { + send(Messages::WebPageProxy::DidChangePageCount(pageCount)); + m_cachedPageCount = pageCount; + } +} + +#if PLATFORM(MAC) + +void WebPage::addPluginView(PluginView* pluginView) +{ + ASSERT(!m_pluginViews.contains(pluginView)); + + m_pluginViews.add(pluginView); +} + +void WebPage::removePluginView(PluginView* pluginView) +{ + ASSERT(m_pluginViews.contains(pluginView)); + + m_pluginViews.remove(pluginView); +} + +void WebPage::setWindowIsVisible(bool windowIsVisible) +{ + m_windowIsVisible = windowIsVisible; + + // Tell all our plug-in views that the window visibility changed. + for (HashSet<PluginView*>::const_iterator it = m_pluginViews.begin(), end = m_pluginViews.end(); it != end; ++it) + (*it)->setWindowIsVisible(windowIsVisible); +} + +void WebPage::windowAndViewFramesChanged(const WebCore::IntRect& windowFrameInScreenCoordinates, const WebCore::IntRect& viewFrameInWindowCoordinates, const WebCore::IntPoint& accessibilityViewCoordinates) +{ + m_windowFrameInScreenCoordinates = windowFrameInScreenCoordinates; + m_viewFrameInWindowCoordinates = viewFrameInWindowCoordinates; + m_accessibilityPosition = accessibilityViewCoordinates; + + // Tell all our plug-in views that the window and view frames have changed. + for (HashSet<PluginView*>::const_iterator it = m_pluginViews.begin(), end = m_pluginViews.end(); it != end; ++it) + (*it)->windowAndViewFramesChanged(windowFrameInScreenCoordinates, viewFrameInWindowCoordinates); +} + +#endif + +bool WebPage::windowIsFocused() const +{ +#if PLATFORM(MAC) + if (!m_windowIsVisible) + return false; +#endif + return m_page->focusController()->isFocused() && m_page->focusController()->isActive(); +} + +void WebPage::didReceiveMessage(CoreIPC::Connection* connection, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments) +{ + if (messageID.is<CoreIPC::MessageClassDrawingArea>()) { + if (m_drawingArea) + m_drawingArea->didReceiveDrawingAreaMessage(connection, messageID, arguments); + return; + } + +#if USE(TILED_BACKING_STORE) && USE(ACCELERATED_COMPOSITING) + if (messageID.is<CoreIPC::MessageClassLayerTreeHost>()) { + if (m_drawingArea) + m_drawingArea->didReceiveLayerTreeHostMessage(connection, messageID, arguments); + return; + } +#endif + +#if ENABLE(INSPECTOR) + if (messageID.is<CoreIPC::MessageClassWebInspector>()) { + if (WebInspector* inspector = this->inspector()) + inspector->didReceiveWebInspectorMessage(connection, messageID, arguments); + return; + } +#endif + +#if ENABLE(FULLSCREEN_API) + if (messageID.is<CoreIPC::MessageClassWebFullScreenManager>()) { + fullScreenManager()->didReceiveMessage(connection, messageID, arguments); + return; + } +#endif + + didReceiveWebPageMessage(connection, messageID, arguments); +} + +void WebPage::didReceiveSyncMessage(CoreIPC::Connection* connection, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments, OwnPtr<CoreIPC::ArgumentEncoder>& reply) +{ + didReceiveSyncWebPageMessage(connection, messageID, arguments, reply); +} + +InjectedBundleBackForwardList* WebPage::backForwardList() +{ + if (!m_backForwardList) + m_backForwardList = InjectedBundleBackForwardList::create(this); + return m_backForwardList.get(); +} + +#if PLATFORM(QT) +void WebPage::findZoomableAreaForPoint(const WebCore::IntPoint& point) +{ + Frame* mainframe = m_mainFrame->coreFrame(); + HitTestResult result = mainframe->eventHandler()->hitTestResultAtPoint(mainframe->view()->windowToContents(point), /*allowShadowContent*/ false, /*ignoreClipping*/ true); + + Node* node = result.innerNode(); + + if (!node) + return; + + IntRect zoomableArea = node->getRect(); + + while (true) { + bool found = !node->isTextNode() && !node->isShadowRoot(); + + // No candidate found, bail out. + if (!found && !node->parentNode()) + return; + + // Candidate found, and it is a better candidate than its parent. + // NB: A parent is considered a better candidate iff the node is + // contained by it and it is the only child. + if (found && (!node->parentNode() || node->parentNode()->childNodeCount() != 1)) + break; + + node = node->parentNode(); + zoomableArea.unite(node->getRect()); + } + + if (node->document() && node->document()->frame() && node->document()->frame()->view()) { + const ScrollView* view = node->document()->frame()->view(); + zoomableArea = view->contentsToWindow(zoomableArea); + } + + send(Messages::WebPageProxy::DidFindZoomableArea(point, zoomableArea)); +} +#endif + +WebPage::SandboxExtensionTracker::~SandboxExtensionTracker() +{ + invalidate(); +} + +void WebPage::SandboxExtensionTracker::invalidate() +{ + if (m_pendingProvisionalSandboxExtension) { + m_pendingProvisionalSandboxExtension->invalidate(); + m_pendingProvisionalSandboxExtension = 0; + } + + if (m_provisionalSandboxExtension) { + m_provisionalSandboxExtension->invalidate(); + m_provisionalSandboxExtension = 0; + } + + if (m_committedSandboxExtension) { + m_committedSandboxExtension->invalidate(); + m_committedSandboxExtension = 0; + } +} + +void WebPage::SandboxExtensionTracker::willPerformLoadDragDestinationAction(PassRefPtr<SandboxExtension> pendingDropSandboxExtension) +{ + setPendingProvisionalSandboxExtension(pendingDropSandboxExtension); +} + +void WebPage::SandboxExtensionTracker::beginLoad(WebFrame* frame, const SandboxExtension::Handle& handle) +{ + ASSERT(frame->isMainFrame()); + + setPendingProvisionalSandboxExtension(SandboxExtension::create(handle)); +} + +void WebPage::SandboxExtensionTracker::setPendingProvisionalSandboxExtension(PassRefPtr<SandboxExtension> pendingProvisionalSandboxExtension) +{ + // If we get two beginLoad calls in succession, without a provisional load starting, then + // m_pendingProvisionalSandboxExtension will be non-null. Invalidate and null out the extension if that is the case. + if (m_pendingProvisionalSandboxExtension) { + m_pendingProvisionalSandboxExtension->invalidate(); + m_pendingProvisionalSandboxExtension = nullptr; + } + + m_pendingProvisionalSandboxExtension = pendingProvisionalSandboxExtension; +} + +static bool shouldReuseCommittedSandboxExtension(WebFrame* frame) +{ + ASSERT(frame->isMainFrame()); + + FrameLoader* frameLoader = frame->coreFrame()->loader(); + FrameLoadType frameLoadType = frameLoader->loadType(); + + // If the page is being reloaded, it should reuse whatever extension is committed. + if (frameLoadType == FrameLoadTypeReload || frameLoadType == FrameLoadTypeReloadFromOrigin) + return true; + + DocumentLoader* documentLoader = frameLoader->documentLoader(); + DocumentLoader* provisionalDocumentLoader = frameLoader->provisionalDocumentLoader(); + if (!documentLoader || !provisionalDocumentLoader) + return false; + + if (documentLoader->url().isLocalFile() && provisionalDocumentLoader->url().isLocalFile()) + return true; + + return false; +} + +void WebPage::SandboxExtensionTracker::didStartProvisionalLoad(WebFrame* frame) +{ + if (!frame->isMainFrame()) + return; + + // We should only reuse the commited sandbox extension if it is not null. It can be + // null if the last load was for an error page. + if (m_committedSandboxExtension && shouldReuseCommittedSandboxExtension(frame)) { + m_pendingProvisionalSandboxExtension = m_committedSandboxExtension.release(); + ASSERT(!m_committedSandboxExtension); + } + + ASSERT(!m_provisionalSandboxExtension); + + m_provisionalSandboxExtension = m_pendingProvisionalSandboxExtension.release(); + if (!m_provisionalSandboxExtension) + return; + + m_provisionalSandboxExtension->consume(); +} + +void WebPage::SandboxExtensionTracker::didCommitProvisionalLoad(WebFrame* frame) +{ + if (!frame->isMainFrame()) + return; + + ASSERT(!m_pendingProvisionalSandboxExtension); + + // The provisional load has been committed. Invalidate the currently committed sandbox + // extension and make the provisional sandbox extension the committed sandbox extension. + if (m_committedSandboxExtension) + m_committedSandboxExtension->invalidate(); + + m_committedSandboxExtension = m_provisionalSandboxExtension.release(); +} + +void WebPage::SandboxExtensionTracker::didFailProvisionalLoad(WebFrame* frame) +{ + if (!frame->isMainFrame()) + return; + + if (!m_provisionalSandboxExtension) + return; + + m_provisionalSandboxExtension->invalidate(); + m_provisionalSandboxExtension = nullptr; +} + +bool WebPage::hasLocalDataForURL(const KURL& url) +{ + if (url.isLocalFile()) + return true; + + FrameLoader* frameLoader = m_page->mainFrame()->loader(); + DocumentLoader* documentLoader = frameLoader ? frameLoader->documentLoader() : 0; + if (documentLoader && documentLoader->subresource(url)) + return true; + + return platformHasLocalDataForURL(url); +} + +void WebPage::setCustomTextEncodingName(const String& encoding) +{ + m_page->mainFrame()->loader()->reloadWithOverrideEncoding(encoding); +} + +void WebPage::didRemoveBackForwardItem(uint64_t itemID) +{ + WebBackForwardListProxy::removeItem(itemID); +} + +#if PLATFORM(MAC) + +bool WebPage::isSpeaking() +{ + bool result; + return sendSync(Messages::WebPageProxy::GetIsSpeaking(), Messages::WebPageProxy::GetIsSpeaking::Reply(result)) && result; +} + +void WebPage::speak(const String& string) +{ + send(Messages::WebPageProxy::Speak(string)); +} + +void WebPage::stopSpeaking() +{ + send(Messages::WebPageProxy::StopSpeaking()); +} + +#endif + +#if USE(CG) +static RetainPtr<CGPDFDocumentRef> pdfDocumentForPrintingFrame(Frame* coreFrame) +{ + Document* document = coreFrame->document(); + if (!document) + return 0; + + if (!document->isPluginDocument()) + return 0; + + PluginView* pluginView = static_cast<PluginView*>(toPluginDocument(document)->pluginWidget()); + if (!pluginView) + return 0; + + return pluginView->pdfDocumentForPrinting(); +} +#endif // USE(CG) + +void WebPage::beginPrinting(uint64_t frameID, const PrintInfo& printInfo) +{ + WebFrame* frame = WebProcess::shared().webFrame(frameID); + if (!frame) + return; + + Frame* coreFrame = frame->coreFrame(); + if (!coreFrame) + return; + +#if USE(CG) + if (pdfDocumentForPrintingFrame(coreFrame)) + return; +#endif // USE(CG) + + if (!m_printContext) + m_printContext = adoptPtr(new PrintContext(coreFrame)); + + m_printContext->begin(printInfo.availablePaperWidth, printInfo.availablePaperHeight); + + float fullPageHeight; + m_printContext->computePageRects(FloatRect(0, 0, printInfo.availablePaperWidth, printInfo.availablePaperHeight), 0, 0, printInfo.pageSetupScaleFactor, fullPageHeight, true); +} + +void WebPage::endPrinting() +{ + m_printContext = nullptr; +} + +void WebPage::computePagesForPrinting(uint64_t frameID, const PrintInfo& printInfo, uint64_t callbackID) +{ + Vector<IntRect> resultPageRects; + double resultTotalScaleFactorForPrinting = 1; + + beginPrinting(frameID, printInfo); + + if (m_printContext) { + resultPageRects = m_printContext->pageRects(); + resultTotalScaleFactorForPrinting = m_printContext->computeAutomaticScaleFactor(FloatSize(printInfo.availablePaperWidth, printInfo.availablePaperHeight)) * printInfo.pageSetupScaleFactor; + } +#if USE(CG) + else { + WebFrame* frame = WebProcess::shared().webFrame(frameID); + Frame* coreFrame = frame ? frame->coreFrame() : 0; + RetainPtr<CGPDFDocumentRef> pdfDocument = coreFrame ? pdfDocumentForPrintingFrame(coreFrame) : 0; + if (pdfDocument && CGPDFDocumentAllowsPrinting(pdfDocument.get())) { + CFIndex pageCount = CGPDFDocumentGetNumberOfPages(pdfDocument.get()); + IntRect pageRect(0, 0, ceilf(printInfo.availablePaperWidth), ceilf(printInfo.availablePaperHeight)); + for (CFIndex i = 1; i <= pageCount; ++i) { + resultPageRects.append(pageRect); + pageRect.move(0, pageRect.height()); + } + } + } +#endif // USE(CG) + + // If we're asked to print, we should actually print at least a blank page. + if (resultPageRects.isEmpty()) + resultPageRects.append(IntRect(0, 0, 1, 1)); + + send(Messages::WebPageProxy::ComputedPagesCallback(resultPageRects, resultTotalScaleFactorForPrinting, callbackID)); +} + +#if USE(CG) +static inline CGFloat roundCGFloat(CGFloat f) +{ + if (sizeof(CGFloat) == sizeof(float)) + return roundf(static_cast<float>(f)); + return static_cast<CGFloat>(round(f)); +} + +static void drawPDFPage(CGPDFDocumentRef pdfDocument, CFIndex pageIndex, CGContextRef context, CGFloat pageSetupScaleFactor, CGSize paperSize) +{ + CGContextSaveGState(context); + + CGContextScaleCTM(context, pageSetupScaleFactor, pageSetupScaleFactor); + + CGPDFPageRef page = CGPDFDocumentGetPage(pdfDocument, pageIndex + 1); + CGRect cropBox = CGPDFPageGetBoxRect(page, kCGPDFCropBox); + if (CGRectIsEmpty(cropBox)) + cropBox = CGRectIntersection(cropBox, CGPDFPageGetBoxRect(page, kCGPDFMediaBox)); + else + cropBox = CGPDFPageGetBoxRect(page, kCGPDFMediaBox); + + bool shouldRotate = (paperSize.width < paperSize.height) != (cropBox.size.width < cropBox.size.height); + if (shouldRotate) + swap(cropBox.size.width, cropBox.size.height); + + // Center. + CGFloat widthDifference = paperSize.width / pageSetupScaleFactor - cropBox.size.width; + CGFloat heightDifference = paperSize.height / pageSetupScaleFactor - cropBox.size.height; + if (widthDifference || heightDifference) + CGContextTranslateCTM(context, roundCGFloat(widthDifference / 2), roundCGFloat(heightDifference / 2)); + + if (shouldRotate) { + CGContextRotateCTM(context, static_cast<CGFloat>(piOverTwoDouble)); + CGContextTranslateCTM(context, 0, -cropBox.size.width); + } + + CGContextDrawPDFPage(context, page); + + CGContextRestoreGState(context); +} +#endif // USE(CG) + +#if PLATFORM(MAC) || PLATFORM(WIN) +void WebPage::drawRectToPDF(uint64_t frameID, const PrintInfo& printInfo, const WebCore::IntRect& rect, uint64_t callbackID) +{ + WebFrame* frame = WebProcess::shared().webFrame(frameID); + Frame* coreFrame = frame ? frame->coreFrame() : 0; + + RetainPtr<CFMutableDataRef> pdfPageData(AdoptCF, CFDataCreateMutable(0, 0)); + + if (coreFrame) { +#if !USE(CG) + UNUSED_PARAM(printInfo); + + ASSERT(coreFrame->document()->printing()); +#else + ASSERT(coreFrame->document()->printing() || pdfDocumentForPrintingFrame(coreFrame)); + + // FIXME: Use CGDataConsumerCreate with callbacks to avoid copying the data. + RetainPtr<CGDataConsumerRef> pdfDataConsumer(AdoptCF, CGDataConsumerCreateWithCFData(pdfPageData.get())); + + CGRect mediaBox = CGRectMake(0, 0, rect.width(), rect.height()); + RetainPtr<CGContextRef> context(AdoptCF, CGPDFContextCreate(pdfDataConsumer.get(), &mediaBox, 0)); + RetainPtr<CFDictionaryRef> pageInfo(AdoptCF, CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + CGPDFContextBeginPage(context.get(), pageInfo.get()); + + if (RetainPtr<CGPDFDocumentRef> pdfDocument = pdfDocumentForPrintingFrame(coreFrame)) { + CFIndex pageCount = CGPDFDocumentGetNumberOfPages(pdfDocument.get()); + IntSize paperSize(ceilf(printInfo.availablePaperWidth), ceilf(printInfo.availablePaperHeight)); + IntRect pageRect(IntPoint(), paperSize); + for (CFIndex i = 0; i < pageCount; ++i) { + if (pageRect.intersects(rect)) { + CGContextSaveGState(context.get()); + + CGContextTranslateCTM(context.get(), pageRect.x() - rect.x(), pageRect.y() - rect.y()); + drawPDFPage(pdfDocument.get(), i, context.get(), printInfo.pageSetupScaleFactor, paperSize); + + CGContextRestoreGState(context.get()); + } + pageRect.move(0, pageRect.height()); + } + } else { + GraphicsContext ctx(context.get()); + ctx.scale(FloatSize(1, -1)); + ctx.translate(0, -rect.height()); + m_printContext->spoolRect(ctx, rect); + } + + CGPDFContextEndPage(context.get()); + CGPDFContextClose(context.get()); +#endif + } + + send(Messages::WebPageProxy::DataCallback(CoreIPC::DataReference(CFDataGetBytePtr(pdfPageData.get()), CFDataGetLength(pdfPageData.get())), callbackID)); +} + +void WebPage::drawPagesToPDF(uint64_t frameID, const PrintInfo& printInfo, uint32_t first, uint32_t count, uint64_t callbackID) +{ + WebFrame* frame = WebProcess::shared().webFrame(frameID); + Frame* coreFrame = frame ? frame->coreFrame() : 0; + + RetainPtr<CFMutableDataRef> pdfPageData(AdoptCF, CFDataCreateMutable(0, 0)); + + if (coreFrame) { + +#if !USE(CG) + ASSERT(coreFrame->document()->printing()); +#else + ASSERT(coreFrame->document()->printing() || pdfDocumentForPrintingFrame(coreFrame)); + + RetainPtr<CGPDFDocumentRef> pdfDocument = pdfDocumentForPrintingFrame(coreFrame); + + // FIXME: Use CGDataConsumerCreate with callbacks to avoid copying the data. + RetainPtr<CGDataConsumerRef> pdfDataConsumer(AdoptCF, CGDataConsumerCreateWithCFData(pdfPageData.get())); + + CGRect mediaBox = m_printContext && m_printContext->pageCount() ? m_printContext->pageRect(0) : CGRectMake(0, 0, printInfo.availablePaperWidth, printInfo.availablePaperHeight); + RetainPtr<CGContextRef> context(AdoptCF, CGPDFContextCreate(pdfDataConsumer.get(), &mediaBox, 0)); + size_t pageCount = m_printContext ? m_printContext->pageCount() : CGPDFDocumentGetNumberOfPages(pdfDocument.get()); + for (uint32_t page = first; page < first + count; ++page) { + if (page >= pageCount) + break; + + RetainPtr<CFDictionaryRef> pageInfo(AdoptCF, CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + CGPDFContextBeginPage(context.get(), pageInfo.get()); + + if (pdfDocument) + drawPDFPage(pdfDocument.get(), page, context.get(), printInfo.pageSetupScaleFactor, CGSizeMake(printInfo.availablePaperWidth, printInfo.availablePaperHeight)); + else { + GraphicsContext ctx(context.get()); + ctx.scale(FloatSize(1, -1)); + ctx.translate(0, -m_printContext->pageRect(page).height()); + m_printContext->spoolPage(ctx, page, m_printContext->pageRect(page).width()); + } + + CGPDFContextEndPage(context.get()); + } + CGPDFContextClose(context.get()); +#endif + } + + send(Messages::WebPageProxy::DataCallback(CoreIPC::DataReference(CFDataGetBytePtr(pdfPageData.get()), CFDataGetLength(pdfPageData.get())), callbackID)); +} +#endif + +void WebPage::runModal() +{ + if (m_isClosed) + return; + if (m_isRunningModal) + return; + + m_isRunningModal = true; + send(Messages::WebPageProxy::RunModal()); + RunLoop::run(); + ASSERT(!m_isRunningModal); +} + +void WebPage::setMemoryCacheMessagesEnabled(bool memoryCacheMessagesEnabled) +{ + m_page->setMemoryCacheClientCallsEnabled(memoryCacheMessagesEnabled); +} + +bool WebPage::canHandleRequest(const WebCore::ResourceRequest& request) +{ + if (SchemeRegistry::shouldLoadURLSchemeAsEmptyDocument(request.url().protocol())) + return true; + return platformCanHandleRequest(request); +} + +#if PLATFORM(MAC) && !defined(BUILDING_ON_SNOW_LEOPARD) +void WebPage::handleCorrectionPanelResult(const String& result) +{ + Frame* frame = m_page->focusController()->focusedOrMainFrame(); + if (!frame) + return; + frame->editor()->handleCorrectionPanelResult(result); +} +#endif + +void WebPage::simulateMouseDown(int button, WebCore::IntPoint position, int clickCount, WKEventModifiers modifiers, double time) +{ + mouseEvent(WebMouseEvent(WebMouseEvent::MouseDown, static_cast<WebMouseEvent::Button>(button), position, position, 0, 0, 0, clickCount, static_cast<WebMouseEvent::Modifiers>(modifiers), time)); +} + +void WebPage::simulateMouseUp(int button, WebCore::IntPoint position, int clickCount, WKEventModifiers modifiers, double time) +{ + mouseEvent(WebMouseEvent(WebMouseEvent::MouseUp, static_cast<WebMouseEvent::Button>(button), position, position, 0, 0, 0, clickCount, static_cast<WebMouseEvent::Modifiers>(modifiers), time)); +} + +void WebPage::simulateMouseMotion(WebCore::IntPoint position, double time) +{ + mouseEvent(WebMouseEvent(WebMouseEvent::MouseMove, WebMouseEvent::NoButton, position, position, 0, 0, 0, 0, WebMouseEvent::Modifiers(), time)); +} + +String WebPage::viewportConfigurationAsText(int deviceDPI, int deviceWidth, int deviceHeight, int availableWidth, int availableHeight) +{ + ViewportArguments arguments = mainFrame()->document()->viewportArguments(); + ViewportAttributes attrs = WebCore::computeViewportAttributes(arguments, /* default layout width for non-mobile pages */ 980, deviceWidth, deviceHeight, deviceDPI, IntSize(availableWidth, availableHeight)); + WebCore::restrictMinimumScaleFactorToViewportSize(attrs, IntSize(availableWidth, availableHeight)); + WebCore::restrictScaleFactorToInitialScaleIfNotUserScalable(attrs); + return String::format("viewport size %dx%d scale %f with limits [%f, %f] and userScalable %f\n", attrs.layoutSize.width(), attrs.layoutSize.height(), attrs.initialScale, attrs.minimumScale, attrs.maximumScale, attrs.userScalable); +} + +void WebPage::setCompositionForTesting(const String& compositionString, uint64_t from, uint64_t length) +{ + Frame* frame = m_page->focusController()->focusedOrMainFrame(); + if (!frame || !frame->editor()->canEdit()) + return; + + Vector<CompositionUnderline> underlines; + underlines.append(CompositionUnderline(0, compositionString.length(), Color(Color::black), false)); + frame->editor()->setComposition(compositionString, underlines, from, from + length); +} + +bool WebPage::hasCompositionForTesting() +{ + Frame* frame = m_page->focusController()->focusedOrMainFrame(); + return frame && frame->editor()->hasComposition(); +} + +void WebPage::confirmCompositionForTesting(const String& compositionString) +{ + Frame* frame = m_page->focusController()->focusedOrMainFrame(); + if (!frame || !frame->editor()->canEdit()) + return; + + if (compositionString.isNull()) + frame->editor()->confirmComposition(); + frame->editor()->confirmComposition(compositionString); +} + +Frame* WebPage::mainFrame() const +{ + return m_page ? m_page->mainFrame() : 0; +} + +FrameView* WebPage::mainFrameView() const +{ + if (Frame* frame = mainFrame()) + return frame->view(); + + return 0; +} + +} // namespace WebKit |