Serenity Operating System
at master 76 lines 3.3 kB view raw
1/* 2 * Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/Optional.h> 8#include <LibGfx/Bitmap.h> 9#include <LibGfx/Rect.h> 10#include <LibWeb/DOM/Document.h> 11#include <LibWeb/DOM/ElementFactory.h> 12#include <LibWeb/HTML/AnimationFrameCallbackDriver.h> 13#include <LibWeb/HTML/BrowsingContext.h> 14#include <LibWeb/HTML/HTMLCanvasElement.h> 15#include <LibWeb/HTML/TagNames.h> 16#include <LibWeb/HTML/Window.h> 17#include <LibWeb/Namespace.h> 18#include <LibWeb/Page/Page.h> 19#include <LibWeb/Platform/EventLoopPlugin.h> 20#include <LibWeb/WebDriver/Error.h> 21#include <LibWeb/WebDriver/Screenshot.h> 22 23namespace Web::WebDriver { 24 25// https://w3c.github.io/webdriver/#dfn-encoding-a-canvas-as-base64 26static Response encode_canvas_element(HTML::HTMLCanvasElement const& canvas) 27{ 28 // FIXME: 1. If the canvas element’s bitmap’s origin-clean flag is set to false, return error with error code unable to capture screen. 29 30 // 2. If the canvas element’s bitmap has no pixels (i.e. either its horizontal dimension or vertical dimension is zero) then return error with error code unable to capture screen. 31 if (canvas.bitmap()->width() == 0 || canvas.bitmap()->height() == 0) 32 return Error::from_code(ErrorCode::UnableToCaptureScreen, "Captured screenshot is empty"sv); 33 34 // 3. Let file be a serialization of the canvas element’s bitmap as a file, using "image/png" as an argument. 35 // 4. Let data url be a data: URL representing file. [RFC2397] 36 auto data_url = canvas.to_data_url("image/png"sv, {}); 37 38 // 5. Let index be the index of "," in data url. 39 auto index = data_url.find(','); 40 VERIFY(index.has_value()); 41 42 // 6. Let encoded string be a substring of data url using (index + 1) as the start argument. 43 auto encoded_string = data_url.substring(*index + 1); 44 45 // 7. Return success with data encoded string. 46 return JsonValue { move(encoded_string) }; 47} 48 49// Common animation callback steps between: 50// https://w3c.github.io/webdriver/#take-screenshot 51// https://w3c.github.io/webdriver/#take-element-screenshot 52Response capture_element_screenshot(Painter const& painter, Page& page, DOM::Element& element, Gfx::IntRect& rect) 53{ 54 Optional<Response> encoded_string_or_error; 55 56 element.document().window().animation_frame_callback_driver().add([&](auto) { 57 auto viewport_rect = page.top_level_browsing_context().viewport_rect(); 58 rect.intersect(page.enclosing_device_rect(viewport_rect).to_type<int>()); 59 60 auto canvas_element = DOM::create_element(element.document(), HTML::TagNames::canvas, Namespace::HTML).release_value_but_fixme_should_propagate_errors(); 61 auto& canvas = verify_cast<HTML::HTMLCanvasElement>(*canvas_element); 62 63 if (!canvas.create_bitmap(rect.width(), rect.height())) { 64 encoded_string_or_error = Error::from_code(ErrorCode::UnableToCaptureScreen, "Unable to create a screenshot bitmap"sv); 65 return; 66 } 67 68 painter(rect, *canvas.bitmap()); 69 encoded_string_or_error = encode_canvas_element(canvas); 70 }); 71 72 Platform::EventLoopPlugin::the().spin_until([&]() { return encoded_string_or_error.has_value(); }); 73 return encoded_string_or_error.release_value(); 74} 75 76}