mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 20:42:43 +00:00 
			
		
		
		
	 cb68c6eaf1
			
		
	
	
		cb68c6eaf1
		
	
	
	
	
		
			
			The default canvas size is 300x150 pixels. If the element or document we are trying to screenshot for the WebDriver is not at least that size, then we will create a canvas that is wider or taller than the actual element we are painting, resulting in a bunch of transparent pixels falling off the end. This fixes 14 WPT css/CSS2/floats tests that we run in CI, and presumably a ton of other reftests in the WPT test suite.
		
			
				
	
	
		
			81 lines
		
	
	
	
		
			3.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			81 lines
		
	
	
	
		
			3.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | ||
|  * Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
 | ||
|  *
 | ||
|  * SPDX-License-Identifier: BSD-2-Clause
 | ||
|  */
 | ||
| 
 | ||
| #include <AK/Optional.h>
 | ||
| #include <LibGfx/Bitmap.h>
 | ||
| #include <LibGfx/Rect.h>
 | ||
| #include <LibWeb/DOM/Document.h>
 | ||
| #include <LibWeb/DOM/ElementFactory.h>
 | ||
| #include <LibWeb/HTML/AnimationFrameCallbackDriver.h>
 | ||
| #include <LibWeb/HTML/BrowsingContext.h>
 | ||
| #include <LibWeb/HTML/HTMLCanvasElement.h>
 | ||
| #include <LibWeb/HTML/TagNames.h>
 | ||
| #include <LibWeb/HTML/TraversableNavigable.h>
 | ||
| #include <LibWeb/HTML/Window.h>
 | ||
| #include <LibWeb/Namespace.h>
 | ||
| #include <LibWeb/Page/Page.h>
 | ||
| #include <LibWeb/Platform/EventLoopPlugin.h>
 | ||
| #include <LibWeb/WebDriver/Error.h>
 | ||
| #include <LibWeb/WebDriver/Screenshot.h>
 | ||
| 
 | ||
| namespace Web::WebDriver {
 | ||
| 
 | ||
| // https://w3c.github.io/webdriver/#dfn-encoding-a-canvas-as-base64
 | ||
| static Response encode_canvas_element(HTML::HTMLCanvasElement& canvas)
 | ||
| {
 | ||
|     // 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.
 | ||
| 
 | ||
|     // 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.
 | ||
|     if (canvas.bitmap()->width() == 0 || canvas.bitmap()->height() == 0)
 | ||
|         return Error::from_code(ErrorCode::UnableToCaptureScreen, "Captured screenshot is empty"sv);
 | ||
| 
 | ||
|     // 3. Let file be a serialization of the canvas element’s bitmap as a file, using "image/png" as an argument.
 | ||
|     // 4. Let data url be a data: URL representing file. [RFC2397]
 | ||
|     auto data_url = canvas.to_data_url("image/png"sv, {});
 | ||
| 
 | ||
|     // 5. Let index be the index of "," in data url.
 | ||
|     auto index = data_url.find_byte_offset(',');
 | ||
|     VERIFY(index.has_value());
 | ||
| 
 | ||
|     // 6. Let encoded string be a substring of data url using (index + 1) as the start argument.
 | ||
|     auto encoded_string = MUST(data_url.substring_from_byte_offset(*index + 1));
 | ||
| 
 | ||
|     // 7. Return success with data encoded string.
 | ||
|     return JsonValue { move(encoded_string) };
 | ||
| }
 | ||
| 
 | ||
| // Common animation callback steps between:
 | ||
| // https://w3c.github.io/webdriver/#take-screenshot
 | ||
| // https://w3c.github.io/webdriver/#take-element-screenshot
 | ||
| Response capture_element_screenshot(Painter const& painter, Page& page, DOM::Element& element, Gfx::IntRect& rect)
 | ||
| {
 | ||
|     Optional<Response> encoded_string_or_error;
 | ||
| 
 | ||
|     element.document().window().animation_frame_callback_driver().add([&](auto) {
 | ||
|         auto viewport_rect = page.top_level_traversable()->viewport_rect();
 | ||
|         rect.intersect(page.enclosing_device_rect(viewport_rect).to_type<int>());
 | ||
| 
 | ||
|         auto canvas_element = DOM::create_element(element.document(), HTML::TagNames::canvas, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
 | ||
|         auto& canvas = verify_cast<HTML::HTMLCanvasElement>(*canvas_element);
 | ||
| 
 | ||
|         // FIXME: Handle DevicePixelRatio in HiDPI mode.
 | ||
|         MUST(canvas.set_width(rect.width()));
 | ||
|         MUST(canvas.set_height(rect.height()));
 | ||
| 
 | ||
|         if (!canvas.create_bitmap(rect.width(), rect.height())) {
 | ||
|             encoded_string_or_error = Error::from_code(ErrorCode::UnableToCaptureScreen, "Unable to create a screenshot bitmap"sv);
 | ||
|             return;
 | ||
|         }
 | ||
| 
 | ||
|         painter(rect, *canvas.bitmap());
 | ||
|         encoded_string_or_error = encode_canvas_element(canvas);
 | ||
|     });
 | ||
| 
 | ||
|     Platform::EventLoopPlugin::the().spin_until([&]() { return encoded_string_or_error.has_value(); });
 | ||
|     return encoded_string_or_error.release_value();
 | ||
| }
 | ||
| 
 | ||
| }
 |