Screenshot.cpp 3.1 KB

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