Title: [221255] trunk/Source
Revision
221255
Author
[email protected]
Date
2017-08-28 06:55:10 -0700 (Mon, 28 Aug 2017)

Log Message

WebDriver: implement screen capture commands
https://bugs.webkit.org/show_bug.cgi?id=174615

Reviewed by Brian Burg.

Source/WebDriver:

Implement takeScreenshot and takeElementScreenshot commands.

19. Screen Capture.
https://w3c.github.io/webdriver/webdriver-spec.html#screen-capture

* CommandResult.cpp:
(WebDriver::CommandResult::CommandResult): Handle ScreenshotError protocol error.
(WebDriver::CommandResult::httpStatusCode const): Add UnableToCaptureScreen.
(WebDriver::CommandResult::errorString const): Ditto.
* CommandResult.h:
* Session.cpp:
(WebDriver::Session::takeScreenshot):
* Session.h:
* WebDriverService.cpp:
(WebDriver::WebDriverService::takeScreenshot):
(WebDriver::WebDriverService::takeElementScreenshot):
* WebDriverService.h:

Source/WebKit:

Extend takeScreenshot command to optionally take a screenshot of an element. When no element is provided, the
screenshot is taken from the page visible area.

* PlatformGTK.cmake: Add WebAutomationSessionCairo.cpp to compilation.
* PlatformWPE.cmake: Ditto.
* UIProcess/Automation/Automation.json: Add ScreenshotError and several optional parameters to takeScreenshot.
* UIProcess/Automation/WebAutomationSession.cpp:
(WebKit::WebAutomationSession::takeScreenshot): Receive optional frame, node and scrollIntoView that are
checked and passed to the web process.
* UIProcess/Automation/WebAutomationSession.h:
* UIProcess/Automation/cairo/WebAutomationSessionCairo.cpp: Added.
(WebKit::WebAutomationSession::platformGetBase64EncodedPNGData): Cairo implementation.
* UIProcess/Automation/gtk/WebAutomationSessionGtk.cpp:
* WebProcess/Automation/WebAutomationSessionProxy.cpp:
(WebKit::snapshotRectForScreenshot): Helper to get the rectangle to be used for a screenshot.
(WebKit::WebAutomationSessionProxy::takeScreenshot): If a node handle is provided take the snapshot using the
element rectangle, otherwise use the page visible content rectangle.
* WebProcess/Automation/WebAutomationSessionProxy.h:
* WebProcess/Automation/WebAutomationSessionProxy.messages.in: Update TakeSnapshot message.

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebDriver/ChangeLog (221254 => 221255)


--- trunk/Source/WebDriver/ChangeLog	2017-08-28 13:39:11 UTC (rev 221254)
+++ trunk/Source/WebDriver/ChangeLog	2017-08-28 13:55:10 UTC (rev 221255)
@@ -1,5 +1,30 @@
 2017-08-28  Carlos Garcia Campos  <[email protected]>
 
+        WebDriver: implement screen capture commands
+        https://bugs.webkit.org/show_bug.cgi?id=174615
+
+        Reviewed by Brian Burg.
+
+        Implement takeScreenshot and takeElementScreenshot commands.
+
+        19. Screen Capture.
+        https://w3c.github.io/webdriver/webdriver-spec.html#screen-capture
+
+        * CommandResult.cpp:
+        (WebDriver::CommandResult::CommandResult): Handle ScreenshotError protocol error.
+        (WebDriver::CommandResult::httpStatusCode const): Add UnableToCaptureScreen.
+        (WebDriver::CommandResult::errorString const): Ditto.
+        * CommandResult.h:
+        * Session.cpp:
+        (WebDriver::Session::takeScreenshot):
+        * Session.h:
+        * WebDriverService.cpp:
+        (WebDriver::WebDriverService::takeScreenshot):
+        (WebDriver::WebDriverService::takeElementScreenshot):
+        * WebDriverService.h:
+
+2017-08-28  Carlos Garcia Campos  <[email protected]>
+
         WebDriver: implement cookies commands
         https://bugs.webkit.org/show_bug.cgi?id=174613
 

Modified: trunk/Source/WebDriver/CommandResult.cpp (221254 => 221255)


--- trunk/Source/WebDriver/CommandResult.cpp	2017-08-28 13:39:11 UTC (rev 221254)
+++ trunk/Source/WebDriver/CommandResult.cpp	2017-08-28 13:55:10 UTC (rev 221255)
@@ -108,6 +108,8 @@
             m_errorCode = ErrorCode::NoSuchAlert;
         else if (errorName == "ElementNotSelectable")
             m_errorCode = ErrorCode::ElementNotSelectable;
+        else if (errorName == "ScreenshotError")
+            m_errorCode = ErrorCode::UnableToCaptureScreen;
 
         break;
     }
@@ -149,6 +151,7 @@
         return 408;
     case ErrorCode::_javascript_Error:
     case ErrorCode::SessionNotCreated:
+    case ErrorCode::UnableToCaptureScreen:
     case ErrorCode::UnexpectedAlertOpen:
     case ErrorCode::UnknownError:
     case ErrorCode::UnsupportedOperation:
@@ -198,6 +201,8 @@
         return ASCIILiteral("stale element reference");
     case ErrorCode::Timeout:
         return ASCIILiteral("timeout");
+    case ErrorCode::UnableToCaptureScreen:
+        return ASCIILiteral("unable to capture screen");
     case ErrorCode::UnexpectedAlertOpen:
         return ASCIILiteral("unexpected alert open");
     case ErrorCode::UnknownCommand:

Modified: trunk/Source/WebDriver/CommandResult.h (221254 => 221255)


--- trunk/Source/WebDriver/CommandResult.h	2017-08-28 13:39:11 UTC (rev 221254)
+++ trunk/Source/WebDriver/CommandResult.h	2017-08-28 13:55:10 UTC (rev 221255)
@@ -57,6 +57,7 @@
         SessionNotCreated,
         StaleElementReference,
         Timeout,
+        UnableToCaptureScreen,
         UnexpectedAlertOpen,
         UnknownCommand,
         UnknownError,

Modified: trunk/Source/WebDriver/Session.cpp (221254 => 221255)


--- trunk/Source/WebDriver/Session.cpp	2017-08-28 13:39:11 UTC (rev 221254)
+++ trunk/Source/WebDriver/Session.cpp	2017-08-28 13:55:10 UTC (rev 221255)
@@ -1997,4 +1997,39 @@
     });
 }
 
+void Session::takeScreenshot(std::optional<String> elementID, std::optional<bool> scrollIntoView, Function<void (CommandResult&&)>&& completionHandler)
+{
+    if (!m_toplevelBrowsingContext) {
+        completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
+        return;
+    }
+
+    handleUserPrompts([this, elementID, scrollIntoView, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
+        if (result.isError()) {
+            completionHandler(WTFMove(result));
+            return;
+        }
+        RefPtr<InspectorObject> parameters = InspectorObject::create();
+        parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
+        if (m_currentBrowsingContext)
+            parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
+        if (elementID)
+            parameters->setString(ASCIILiteral("nodeHandle"), elementID.value());
+        if (scrollIntoView.value_or(false))
+            parameters->setBoolean(ASCIILiteral("scrollIntoViewIfNeeded"), true);
+        m_host->sendCommandToBackend(ASCIILiteral("takeScreenshot"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
+            if (response.isError || !response.responseObject) {
+                completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
+                return;
+            }
+            String data;
+            if (!response.responseObject->getString(ASCIILiteral("data"), data)) {
+                completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
+                return;
+            }
+            completionHandler(CommandResult::success(InspectorValue::create(data)));
+        });
+    });
+}
+
 } // namespace WebDriver

Modified: trunk/Source/WebDriver/Session.h (221254 => 221255)


--- trunk/Source/WebDriver/Session.h	2017-08-28 13:39:11 UTC (rev 221254)
+++ trunk/Source/WebDriver/Session.h	2017-08-28 13:55:10 UTC (rev 221255)
@@ -113,6 +113,7 @@
     void acceptAlert(Function<void (CommandResult&&)>&&);
     void getAlertText(Function<void (CommandResult&&)>&&);
     void sendAlertText(const String&, Function<void (CommandResult&&)>&&);
+    void takeScreenshot(std::optional<String> elementID, std::optional<bool> scrollIntoView, Function<void (CommandResult&&)>&&);
 
 private:
     Session(std::unique_ptr<SessionHost>&&);

Modified: trunk/Source/WebDriver/WebDriverService.cpp (221254 => 221255)


--- trunk/Source/WebDriver/WebDriverService.cpp	2017-08-28 13:39:11 UTC (rev 221254)
+++ trunk/Source/WebDriver/WebDriverService.cpp	2017-08-28 13:55:10 UTC (rev 221255)
@@ -158,6 +158,10 @@
     { HTTPMethod::Get, "/session/$sessionId/alert/text", &WebDriverService::getAlertText },
     { HTTPMethod::Post, "/session/$sessionId/alert/text", &WebDriverService::sendAlertText },
 
+    { HTTPMethod::Get, "/session/$sessionId/screenshot", &WebDriverService::takeScreenshot },
+    { HTTPMethod::Get, "/session/$sessionId/element/$elementId/screenshot", &WebDriverService::takeElementScreenshot },
+
+
     { HTTPMethod::Get, "/session/$sessionId/element/$elementId/displayed", &WebDriverService::isElementDisplayed },
 };
 
@@ -1507,4 +1511,45 @@
     });
 }
 
+void WebDriverService::takeScreenshot(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
+{
+    // §19.1 Take Screenshot.
+    // https://w3c.github.io/webdriver/webdriver-spec.html#take-screenshot
+    auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
+    if (!session)
+        return;
+
+    session->waitForNavigationToComplete([session, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
+        if (result.isError()) {
+            completionHandler(WTFMove(result));
+            return;
+        }
+        session->takeScreenshot(std::nullopt, std::nullopt, WTFMove(completionHandler));
+    });
+}
+
+void WebDriverService::takeElementScreenshot(RefPtr<InspectorObject>&& parameters, Function<void (CommandResult&&)>&& completionHandler)
+{
+    // §19.2 Take Element Screenshot.
+    // https://w3c.github.io/webdriver/webdriver-spec.html#take-element-screenshot
+    auto session = findSessionOrCompleteWithError(*parameters, completionHandler);
+    if (!session)
+        return;
+
+    auto elementID = findElementOrCompleteWithError(*parameters, completionHandler);
+    if (!elementID)
+        return;
+
+    bool scrollIntoView = true;
+    parameters->getBoolean(ASCIILiteral("scroll"), scrollIntoView);
+
+    session->waitForNavigationToComplete([session, elementID, scrollIntoView, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable {
+        if (result.isError()) {
+            completionHandler(WTFMove(result));
+            return;
+        }
+        session->takeScreenshot(elementID.value(), scrollIntoView, WTFMove(completionHandler));
+    });
+}
+
 } // namespace WebDriver

Modified: trunk/Source/WebDriver/WebDriverService.h (221254 => 221255)


--- trunk/Source/WebDriver/WebDriverService.h	2017-08-28 13:39:11 UTC (rev 221254)
+++ trunk/Source/WebDriver/WebDriverService.h	2017-08-28 13:55:10 UTC (rev 221255)
@@ -109,6 +109,8 @@
     void acceptAlert(RefPtr<Inspector::InspectorObject>&&, Function<void (CommandResult&&)>&&);
     void getAlertText(RefPtr<Inspector::InspectorObject>&&, Function<void (CommandResult&&)>&&);
     void sendAlertText(RefPtr<Inspector::InspectorObject>&&, Function<void (CommandResult&&)>&&);
+    void takeScreenshot(RefPtr<Inspector::InspectorObject>&&, Function<void (CommandResult&&)>&&);
+    void takeElementScreenshot(RefPtr<Inspector::InspectorObject>&&, Function<void (CommandResult&&)>&&);
 
     static Capabilities platformCapabilities();
     RefPtr<Inspector::InspectorObject> processCapabilities(const Inspector::InspectorObject&, Function<void (CommandResult&&)>&) const;

Modified: trunk/Source/WebKit/ChangeLog (221254 => 221255)


--- trunk/Source/WebKit/ChangeLog	2017-08-28 13:39:11 UTC (rev 221254)
+++ trunk/Source/WebKit/ChangeLog	2017-08-28 13:55:10 UTC (rev 221255)
@@ -1,5 +1,32 @@
 2017-08-28  Carlos Garcia Campos  <[email protected]>
 
+        WebDriver: implement screen capture commands
+        https://bugs.webkit.org/show_bug.cgi?id=174615
+
+        Reviewed by Brian Burg.
+
+        Extend takeScreenshot command to optionally take a screenshot of an element. When no element is provided, the
+        screenshot is taken from the page visible area.
+
+        * PlatformGTK.cmake: Add WebAutomationSessionCairo.cpp to compilation.
+        * PlatformWPE.cmake: Ditto.
+        * UIProcess/Automation/Automation.json: Add ScreenshotError and several optional parameters to takeScreenshot.
+        * UIProcess/Automation/WebAutomationSession.cpp:
+        (WebKit::WebAutomationSession::takeScreenshot): Receive optional frame, node and scrollIntoView that are
+        checked and passed to the web process.
+        * UIProcess/Automation/WebAutomationSession.h:
+        * UIProcess/Automation/cairo/WebAutomationSessionCairo.cpp: Added.
+        (WebKit::WebAutomationSession::platformGetBase64EncodedPNGData): Cairo implementation.
+        * UIProcess/Automation/gtk/WebAutomationSessionGtk.cpp:
+        * WebProcess/Automation/WebAutomationSessionProxy.cpp:
+        (WebKit::snapshotRectForScreenshot): Helper to get the rectangle to be used for a screenshot.
+        (WebKit::WebAutomationSessionProxy::takeScreenshot): If a node handle is provided take the snapshot using the
+        element rectangle, otherwise use the page visible content rectangle.
+        * WebProcess/Automation/WebAutomationSessionProxy.h:
+        * WebProcess/Automation/WebAutomationSessionProxy.messages.in: Update TakeSnapshot message.
+
+2017-08-28  Carlos Garcia Campos  <[email protected]>
+
         Automation: takeScreenshot should use the visible content rect not the document rect
         https://bugs.webkit.org/show_bug.cgi?id=175665
 

Modified: trunk/Source/WebKit/PlatformGTK.cmake (221254 => 221255)


--- trunk/Source/WebKit/PlatformGTK.cmake	2017-08-28 13:39:11 UTC (rev 221254)
+++ trunk/Source/WebKit/PlatformGTK.cmake	2017-08-28 13:55:10 UTC (rev 221255)
@@ -191,6 +191,8 @@
     UIProcess/API/gtk/WebKitWebViewBaseAccessible.cpp
     UIProcess/API/gtk/WebKitWebViewGtk.cpp
 
+    UIProcess/Automation/cairo/WebAutomationSessionCairo.cpp
+
     UIProcess/Automation/gtk/WebAutomationSessionGtk.cpp
 
     UIProcess/Launcher/gtk/ProcessLauncherGtk.cpp

Modified: trunk/Source/WebKit/PlatformWPE.cmake (221254 => 221255)


--- trunk/Source/WebKit/PlatformWPE.cmake	2017-08-28 13:39:11 UTC (rev 221254)
+++ trunk/Source/WebKit/PlatformWPE.cmake	2017-08-28 13:55:10 UTC (rev 221255)
@@ -197,6 +197,8 @@
     UIProcess/API/wpe/WebKitWebViewWPE.cpp
     UIProcess/API/wpe/WPEView.cpp
 
+    UIProcess/Automation/cairo/WebAutomationSessionCairo.cpp
+
     UIProcess/Launcher/wpe/ProcessLauncherWPE.cpp
 
     UIProcess/Plugins/unix/PluginInfoStoreUnix.cpp

Modified: trunk/Source/WebKit/UIProcess/Automation/Automation.json (221254 => 221255)


--- trunk/Source/WebKit/UIProcess/Automation/Automation.json	2017-08-28 13:39:11 UTC (rev 221254)
+++ trunk/Source/WebKit/UIProcess/Automation/Automation.json	2017-08-28 13:55:10 UTC (rev 221255)
@@ -60,7 +60,8 @@
                 "InvalidParameter",
                 "InvalidSelector",
                 "ElementNotInteractable",
-                "ElementNotSelectable"
+                "ElementNotSelectable",
+                "ScreenshotError"
             ]
         },
         {
@@ -377,9 +378,12 @@
         },
         {
             "name": "takeScreenshot",
-            "description": "Take a screenshot of the current page in a browsing context.",
+            "description": "Take a screenshot of the current page or given element in a browsing context.",
             "parameters": [
-                { "name": "handle", "$ref": "BrowsingContextHandle", "description": "The handle for the browsing context to take a screenshot of." }
+                { "name": "handle", "$ref": "BrowsingContextHandle", "description": "The handle for the browsing context to take a screenshot of." },
+                { "name": "frameHandle", "$ref": "FrameHandle", "optional": true, "description": "The handle for the frame that contains the element. The main frame is used if this parameter is empty string or excluded." },
+                { "name": "nodeHandle", "$ref": "NodeHandle", "optional": true, "description": "The handle of the element to take a screenshot of. The browsing context document element is used if this parameter is excluded." },
+                { "name": "scrollIntoViewIfNeeded", "type": "boolean", "optional": true, "description": "If the element should be scrolled into view before taking the screenshot." }
             ],
             "returns": [
                 { "name": "data", "type": "string", "description": "Base64-encoded image data (PNG)." }

Modified: trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.cpp (221254 => 221255)


--- trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.cpp	2017-08-28 13:39:11 UTC (rev 221254)
+++ trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.cpp	2017-08-28 13:55:10 UTC (rev 221255)
@@ -1351,16 +1351,23 @@
 #endif // PLATFORM(COCOA)
 }
 
-void WebAutomationSession::takeScreenshot(ErrorString& errorString, const String& handle, Ref<TakeScreenshotCallback>&& callback)
+void WebAutomationSession::takeScreenshot(ErrorString& errorString, const String& handle, const String* optionalFrameHandle, const String* optionalNodeHandle, const bool* optionalScrollIntoViewIfNeeded, Ref<TakeScreenshotCallback>&& callback)
 {
     WebPageProxy* page = webPageProxyForHandle(handle);
     if (!page)
         FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
 
+    std::optional<uint64_t> frameID = webFrameIDForHandle(optionalFrameHandle ? *optionalFrameHandle : emptyString());
+    if (!frameID)
+        FAIL_WITH_PREDEFINED_ERROR(FrameNotFound);
+
+    bool scrollIntoViewIfNeeded = optionalScrollIntoViewIfNeeded ? *optionalScrollIntoViewIfNeeded : false;
+    String nodeHandle = optionalNodeHandle ? *optionalNodeHandle : emptyString();
+
     uint64_t callbackID = m_nextScreenshotCallbackID++;
     m_screenshotCallbacks.set(callbackID, WTFMove(callback));
 
-    page->process().send(Messages::WebAutomationSessionProxy::TakeScreenshot(page->pageID(), callbackID), 0);
+    page->process().send(Messages::WebAutomationSessionProxy::TakeScreenshot(page->pageID(), frameID.value(), nodeHandle, scrollIntoViewIfNeeded, callbackID), 0);
 }
 
 void WebAutomationSession::didTakeScreenshot(uint64_t callbackID, const ShareableBitmap::Handle& imageDataHandle, const String& errorType)
@@ -1389,7 +1396,7 @@
 void WebAutomationSession::platformSimulateMouseInteraction(WebKit::WebPageProxy&, const WebCore::IntPoint&, Inspector::Protocol::Automation::MouseInteraction, Inspector::Protocol::Automation::MouseButton, WebEvent::Modifiers)
 {
 }
-#endif // !PLATFORM(MAC)
+#endif // !PLATFORM(MAC) && !PLATFORM(GTK)
 
 #if !PLATFORM(COCOA) && !PLATFORM(GTK)
 void WebAutomationSession::platformSimulateKeyStroke(WebPageProxy&, Inspector::Protocol::Automation::KeyboardInteractionType, Inspector::Protocol::Automation::VirtualKey)
@@ -1399,11 +1406,13 @@
 void WebAutomationSession::platformSimulateKeySequence(WebPageProxy&, const String&)
 {
 }
+#endif // !PLATFORM(COCOA) && !PLATFORM(GTK)
 
+#if !PLATFORM(COCOA) && !USE(CAIRO)
 std::optional<String> WebAutomationSession::platformGetBase64EncodedPNGData(const ShareableBitmap::Handle&)
 {
-    return String();
+    return std::nullopt;
 }
-#endif // !PLATFORM(COCOA)
+#endif // !PLATFORM(COCOA) && !USE(CAIRO)
 
 } // namespace WebKit

Modified: trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.h (221254 => 221255)


--- trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.h	2017-08-28 13:39:11 UTC (rev 221254)
+++ trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.h	2017-08-28 13:55:10 UTC (rev 221255)
@@ -130,7 +130,7 @@
     void evaluateJavaScriptFunction(Inspector::ErrorString&, const String& browsingContextHandle, const String* optionalFrameHandle, const String& function, const Inspector::InspectorArray& arguments, const bool* optionalExpectsImplicitCallbackArgument, const int* optionalCallbackTimeout, Ref<Inspector::AutomationBackendDispatcherHandler::EvaluateJavaScriptFunctionCallback>&&) override;
     void performMouseInteraction(Inspector::ErrorString&, const String& handle, const Inspector::InspectorObject& requestedPosition, const String& mouseButton, const String& mouseInteraction, const Inspector::InspectorArray& keyModifiers, RefPtr<Inspector::Protocol::Automation::Point>& updatedPosition) override;
     void performKeyboardInteractions(Inspector::ErrorString&, const String& handle, const Inspector::InspectorArray& interactions, Ref<PerformKeyboardInteractionsCallback>&&) override;
-    void takeScreenshot(Inspector::ErrorString&, const String& handle, Ref<Inspector::AutomationBackendDispatcherHandler::TakeScreenshotCallback>&&) override;
+    void takeScreenshot(Inspector::ErrorString&, const String& handle, const String* optionalFrameHandle, const String* optionalNodeHandle, const bool* optionalScrollIntoViewIfNeeded, Ref<TakeScreenshotCallback>&&) override;
     void resolveChildFrameHandle(Inspector::ErrorString&, const String& browsingContextHandle, const String* optionalFrameHandle, const int* optionalOrdinal, const String* optionalName, const String* optionalNodeHandle, Ref<ResolveChildFrameHandleCallback>&&) override;
     void resolveParentFrameHandle(Inspector::ErrorString&, const String& browsingContextHandle, const String& frameHandle, Ref<ResolveParentFrameHandleCallback>&&) override;
     void computeElementLayout(Inspector::ErrorString&, const String& browsingContextHandle, const String& frameHandle, const String& nodeHandle, const bool* optionalScrollIntoViewIfNeeded, const bool* useViewportCoordinates, Ref<Inspector::AutomationBackendDispatcherHandler::ComputeElementLayoutCallback>&&) override;

Added: trunk/Source/WebKit/UIProcess/Automation/cairo/WebAutomationSessionCairo.cpp (0 => 221255)


--- trunk/Source/WebKit/UIProcess/Automation/cairo/WebAutomationSessionCairo.cpp	                        (rev 0)
+++ trunk/Source/WebKit/UIProcess/Automation/cairo/WebAutomationSessionCairo.cpp	2017-08-28 13:55:10 UTC (rev 221255)
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 Igalia S.L.
+ *
+ * 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 "WebAutomationSession.h"
+
+#include <WebCore/RefPtrCairo.h>
+#include <cairo/cairo.h>
+#include <wtf/text/Base64.h>
+
+using namespace WebCore;
+
+namespace WebKit {
+
+std::optional<String> WebAutomationSession::platformGetBase64EncodedPNGData(const ShareableBitmap::Handle& handle)
+{
+    RefPtr<ShareableBitmap> bitmap = ShareableBitmap::create(handle, SharedMemory::Protection::ReadOnly);
+    if (!bitmap)
+        return std::nullopt;
+
+    auto surface = bitmap->createCairoSurface();
+    if (!surface)
+        return std::nullopt;
+
+    Vector<unsigned char> pngData;
+    cairo_surface_write_to_png_stream(surface.get(), [](void* userData, const unsigned char* data, unsigned length) -> cairo_status_t {
+        auto* pngData = static_cast<Vector<unsigned char>*>(userData);
+        pngData->append(data, length);
+        return CAIRO_STATUS_SUCCESS;
+    }, &pngData);
+
+    if (pngData.isEmpty())
+        return std::nullopt;
+
+    return base64Encode(pngData);
+}
+
+} // namespace WebKit
+

Modified: trunk/Source/WebKit/UIProcess/Automation/gtk/WebAutomationSessionGtk.cpp (221254 => 221255)


--- trunk/Source/WebKit/UIProcess/Automation/gtk/WebAutomationSessionGtk.cpp	2017-08-28 13:39:11 UTC (rev 221254)
+++ trunk/Source/WebKit/UIProcess/Automation/gtk/WebAutomationSessionGtk.cpp	2017-08-28 13:55:10 UTC (rev 221255)
@@ -309,11 +309,5 @@
     } while (*p);
 }
 
-std::optional<String> WebAutomationSession::platformGetBase64EncodedPNGData(const ShareableBitmap::Handle&)
-{
-    // FIXME: Implement this, possibly moving it to a WebAutomationSessionCairo.cpp file.
-    return std::nullopt;
-}
-
 } // namespace WebKit
 

Modified: trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.cpp (221254 => 221255)


--- trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.cpp	2017-08-28 13:39:11 UTC (rev 221254)
+++ trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.cpp	2017-08-28 13:55:10 UTC (rev 221255)
@@ -53,6 +53,7 @@
 #include <WebCore/HTMLSelectElement.h>
 #include <WebCore/JSElement.h>
 #include <WebCore/MainFrame.h>
+#include <WebCore/RenderElement.h>
 #include <wtf/UUID.h>
 
 namespace WebKit {
@@ -633,8 +634,24 @@
     WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidSelectOptionElement(callbackID, { }), 0);
 }
 
-void WebAutomationSessionProxy::takeScreenshot(uint64_t pageID, uint64_t callbackID)
+static WebCore::IntRect snapshotRectForScreenshot(WebPage& page, WebCore::Element* element)
 {
+    if (element) {
+        if (!element->renderer())
+            return { };
+
+        WebCore::LayoutRect topLevelRect;
+        return WebCore::snappedIntRect(element->renderer()->paintingRootRect(topLevelRect));
+    }
+
+    if (auto* frameView = page.mainFrameView())
+        return frameView->visibleContentRect();
+
+    return { };
+}
+
+void WebAutomationSessionProxy::takeScreenshot(uint64_t pageID, uint64_t frameID, String nodeHandle, bool scrollIntoViewIfNeeded, uint64_t callbackID)
+{
     ShareableBitmap::Handle handle;
 
     WebPage* page = WebProcess::singleton().webPage(pageID);
@@ -644,23 +661,41 @@
         return;
     }
 
-    WebCore::FrameView* frameView = page->mainFrameView();
-    if (!frameView) {
-        WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidTakeScreenshot(callbackID, handle, String()), 0);
+    WebFrame* frame = frameID ? WebProcess::singleton().webFrame(frameID) : page->mainWebFrame();
+    if (!frame || !frame->coreFrame()) {
+        String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound);
+        WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidTakeScreenshot(callbackID, handle, frameNotFoundErrorType), 0);
         return;
     }
 
-    WebCore::IntRect snapshotRect = frameView->visibleContentRect();
+    WebCore::Element* coreElement = nullptr;
+    if (!nodeHandle.isEmpty()) {
+        coreElement = elementForNodeHandle(*frame, nodeHandle);
+        if (!coreElement) {
+            String nodeNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::NodeNotFound);
+            WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidTakeScreenshot(callbackID, handle, nodeNotFoundErrorType), 0);
+            return;
+        }
+    }
+
+    String screenshotErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::ScreenshotError);
+    WebCore::IntRect snapshotRect = snapshotRectForScreenshot(*page, coreElement);
     if (snapshotRect.isEmpty()) {
-        WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidTakeScreenshot(callbackID, handle, String()), 0);
+        WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidTakeScreenshot(callbackID, handle, screenshotErrorType), 0);
         return;
     }
 
+    if (coreElement && scrollIntoViewIfNeeded)
+        coreElement->scrollIntoViewIfNeeded(false);
+
     RefPtr<WebImage> image = page->scaledSnapshotWithOptions(snapshotRect, 1, SnapshotOptionsShareable);
-    if (image)
-        image->bitmap().createHandle(handle, SharedMemory::Protection::ReadOnly);
+    if (!image) {
+        WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidTakeScreenshot(callbackID, handle, screenshotErrorType), 0);
+        return;
+    }
 
-    WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidTakeScreenshot(callbackID, handle, String()), 0);    
+    image->bitmap().createHandle(handle, SharedMemory::Protection::ReadOnly);
+    WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidTakeScreenshot(callbackID, handle, { }), 0);
 }
 
 void WebAutomationSessionProxy::getCookiesForFrame(uint64_t pageID, uint64_t frameID, uint64_t callbackID)

Modified: trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.h (221254 => 221255)


--- trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.h	2017-08-28 13:39:11 UTC (rev 221254)
+++ trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.h	2017-08-28 13:55:10 UTC (rev 221255)
@@ -66,7 +66,7 @@
     void focusFrame(uint64_t pageID, uint64_t frameID);
     void computeElementLayout(uint64_t pageID, uint64_t frameID, String nodeHandle, bool scrollIntoViewIfNeeded, bool useViewportCoordinates, uint64_t callbackID);
     void selectOptionElement(uint64_t pageID, uint64_t frameID, String nodeHandle, uint64_t callbackID);
-    void takeScreenshot(uint64_t pageID, uint64_t callbackID);
+    void takeScreenshot(uint64_t pageID, uint64_t frameID, String nodeHandle, bool scrollIntoViewIfNeeded, uint64_t callbackID);
     void getCookiesForFrame(uint64_t pageID, uint64_t frameID, uint64_t callbackID);
     void deleteCookie(uint64_t pageID, uint64_t frameID, String cookieName, uint64_t callbackID);
 

Modified: trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.messages.in (221254 => 221255)


--- trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.messages.in	2017-08-28 13:39:11 UTC (rev 221254)
+++ trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.messages.in	2017-08-28 13:55:10 UTC (rev 221255)
@@ -34,7 +34,7 @@
 
     SelectOptionElement(uint64_t pageID, uint64_t frameID, String nodeHandle, uint64_t callbackID)
 
-    TakeScreenshot(uint64_t pageID, uint64_t callbackID)
+    TakeScreenshot(uint64_t pageID, uint64_t frameID, String nodeHandle, bool scrollIntoViewIfNeeded, uint64_t callbackID)
 
     GetCookiesForFrame(uint64_t pageID, uint64_t frameID, uint64_t callbackID)
     DeleteCookie(uint64_t pageID, uint64_t frameID, String cookieName, uint64_t callbackID)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to