}
#endif
+#if BUILDFLAG(IS_EFL)
+void RenderWidgetHostImpl::RequestContentSnapshot(
+ const gfx::Rect& snapshot_rect,
+ float page_scale_factor,
+ int request_id) {
+ blink_widget_->GetContentSnapshot(
+ snapshot_rect, page_scale_factor,
+ base::BindOnce(&RenderWidgetHostImpl::OnGetContentSnapshot,
+ weak_factory_.GetWeakPtr(), request_id));
+}
+
+void RenderWidgetHostImpl::OnGetContentSnapshot(int request_id,
+ const SkBitmap& bitmap) {
+ if (!view_)
+ return;
+ view_->DidGetContentSnapshot(bitmap, request_id);
+}
+#endif
+
blink::VisualProperties RenderWidgetHostImpl::GetInitialVisualProperties() {
blink::VisualProperties initial_props = GetVisualProperties();
RenderWidgetHost::InputEventObserver* observer) override;
#endif
+#if BUILDFLAG(IS_EFL)
+ void RequestContentSnapshot(const gfx::Rect& src_subrect,
+ float page_scale_factor,
+ int request_id);
+ void OnGetContentSnapshot(int request_id, const SkBitmap& bitmap);
+#endif
+
// Returns true if the RenderWidget is hidden.
bool is_hidden() const { return is_hidden_; }
offscreen_helper_->NotifySwap(texture_id);
}
+void RenderWidgetHostViewAura::DidGetContentSnapshot(const SkBitmap& bitmap,
+ int request_id) {
+ if (offscreen_helper_)
+ offscreen_helper_->DidGetContentSnapshot(bitmap, request_id);
+}
+
void RenderWidgetHostViewAura::DidHandleKeyEvent(
blink::WebInputEvent::Type input_event_type,
bool processed) {
return offscreen_helper_.get();
}
void NotifySwap(const uint32_t texture_id);
+ void DidGetContentSnapshot(const SkBitmap& bitmap, int request_id) override;
void DidHandleKeyEvent(blink::WebInputEvent::Type input_event,
bool processed) override;
void SelectionChanged(const std::u16string& text,
// This method will clear any cached fallback surface. For use in response to
// a CommitPending where there is no content for TakeFallbackContentFrom.
virtual void ClearFallbackSurfaceForCommitPending() {}
+
+#if BUILDFLAG(IS_EFL)
+ virtual void DidHandleKeyEvent(blink::WebInputEvent::Type input_event,
+ bool processed) {}
+ virtual void DidGetContentSnapshot(const SkBitmap& bitmap, int request_id) {}
+#endif
+
// This method will reset the fallback to the first surface after navigation.
virtual void ResetFallbackToFirstNavigationSurface() = 0;
// RenderWidgetHostView.
VisibleTimeRequestTrigger* GetVisibleTimeRequestTrigger();
-#if BUILDFLAG(IS_EFL)
- virtual void DidHandleKeyEvent(blink::WebInputEvent::Type input_event,
- bool processed) {}
-#endif
-
protected:
explicit RenderWidgetHostViewBase(RenderWidgetHost* host);
~RenderWidgetHostViewBase() override;
if (tizen_product_tv) {
enabled_features += [ "is_tizen_tv" ]
}
+ if (use_efl) {
+ enabled_features += [ "is_efl" ]
+ }
shared_cpp_typemaps = [
{
import "third_party/blink/public/mojom/widget/record_content_to_visible_time_request.mojom";
import "ui/base/ime/mojom/text_input_state.mojom";
+[EnableIf=is_efl]
+import "skia/public/mojom/bitmap.mojom";
+
// This interface is bound on the compositor thread.
interface WidgetCompositor {
// Requests that the RenderWidget sends back a response after the next main
gfx.mojom.Rect window_screen_rect) => ();
+ [EnableIf=is_efl]
+ GetContentSnapshot(gfx.mojom.Rect snapshot_rect, float page_scale_factor)
+ => (skia.mojom.BitmapN32? bitmap);
+
// Informs the widget that it was hidden. This allows it to reduce its
// resource utilization, and will cancel any pending
// RecordContentToVisibleTimeRequest that was set with WasShown or
#include "third_party/skia/include/core/SkColor.h"
#include "ui/display/mojom/screen_orientation.mojom-shared.h"
+#if BUILDFLAG(IS_EFL)
+#include "third_party/skia/include/core/SkCanvas.h"
+#endif
+
namespace base {
class TimeDelta;
}
// Returns the number of live WebView instances in this process.
static size_t GetWebViewCount();
+#if BUILDFLAG(IS_EFL)
+ // Paints the rectangular region within the WebWidget
+ // onto the specified canvas, when the page is not having any 3D content.
+ virtual bool PaintSoftBitmap(SkCanvas*, const gfx::Rect&) = 0;
+ virtual bool HasAcceleratedCanvasWithinViewport() const = 0;
+#endif
+
protected:
~WebView() = default;
};
#include "third_party/blink/public/web/win/web_font_rendering.h"
#endif
+#if BUILDFLAG(IS_EFL)
+#include "third_party/blink/renderer/core/dom/container_node.h"
+#include "third_party/blink/renderer/core/dom/static_node_list.h"
+#endif
+
// Get rid of WTF's pow define so we can use std::pow.
#undef pow
#include <cmath> // for std::pow
std::move(remote_main_frame_interfaces->main_frame));
}
+#if BUILDFLAG(IS_EFL)
+bool WebViewImpl::PaintSoftBitmap(SkCanvas* canvas, const gfx::Rect& rect) {
+ if (rect.IsEmpty())
+ return false;
+
+ if (!MainFrameImpl() || !MainFrameImpl()->GetFrameView())
+ return false;
+
+ LocalFrameView* view = MainFrameImpl()->GetFrameView();
+ view->UpdateAllLifecyclePhases(DocumentUpdateReason::kOverlay);
+
+ gfx::PointF offset = MainFrame()->ToWebLocalFrame()->GetScrollOffset();
+ gfx::Rect dirty_rect(rect.x() + offset.x(), rect.y() + offset.y(),
+ rect.width(), rect.height());
+ PaintRecordBuilder builder;
+ {
+ // FIXME: scaleFactor should be ideally pagescalefactor * devicescale
+ // here we are assuming pagescalefactor as 1 always, this needs to
+ // be taken care with a proper fix for capturing zoomed contents.
+ LocalFrame& frame = view->GetFrame();
+ ChromeClient& chrome_client = frame.GetChromeClient();
+ float scale_factor = chrome_client.GetScreenInfo(frame).device_scale_factor;
+
+ GraphicsContext& context = builder.Context();
+ context.SetDeviceScaleFactor(scale_factor);
+
+ AffineTransform transform;
+ transform.Scale(scale_factor);
+ transform.Translate(static_cast<float>(-dirty_rect.x()),
+ static_cast<float>(-dirty_rect.y()));
+
+ // FIXME: Use the page-background color to clear instead for half
+ // loaded pages.
+ SkColor color = SkColorSetARGB(0xff, 0xff, 0xff, 0xff);
+ canvas->clear(color);
+ view->PaintOutsideOfLifecycle(context, PaintFlag::kOmitCompositingInfo,
+ CullRect(dirty_rect));
+ }
+ builder.EndRecording()->Playback(canvas);
+ return true;
+}
+
+bool WebViewImpl::HasAcceleratedCanvasWithinViewport() const {
+ if (!MainFrameImpl() || !MainFrameImpl()->GetFrameView())
+ return false;
+
+ Document* document = MainFrameImpl()->GetFrame()->GetDocument();
+ if (!document)
+ return false;
+
+ StaticElementList* canvas_elements =
+ document->QuerySelectorAll("canvas", ASSERT_NO_EXCEPTION);
+ if (!canvas_elements)
+ return false;
+
+ gfx::Rect visible_content_rect =
+ MainFrameImpl()->GetFrameView()->LayoutViewport()->VisibleContentRect();
+ for (unsigned i = 0; i < canvas_elements->length(); ++i) {
+ if (visible_content_rect.Intersects(
+ canvas_elements->item(i)->BoundsInWidget())) {
+ return true;
+ }
+ }
+ return false;
+}
+#endif
+
} // namespace blink
const SessionStorageNamespaceId& GetSessionStorageNamespaceId() override;
bool IsFencedFrameRoot() const override;
+#if BUILDFLAG(IS_EFL)
+ bool PaintSoftBitmap(SkCanvas*, const gfx::Rect&) override;
+ bool HasAcceleratedCanvasWithinViewport() const override;
+#endif
+
// Functions to add and remove observers for this object.
void AddObserver(WebViewObserver* observer);
void RemoveObserver(WebViewObserver* observer);
#include "ui/gfx/geometry/point.h"
#endif
+#if BUILDFLAG(IS_EFL)
+#include "skia/ext/image_operations.h"
+#endif
+
namespace WTF {
template <>
return true;
}
+#if BUILDFLAG(IS_EFL)
+void WebFrameWidgetImpl::GetContentSnapshot(const gfx::Rect& snapshot_rect,
+ float page_scale_factor,
+ SkBitmap* snapshot) {
+ WebView* view = View();
+ if (!view || !view->MainFrame()) {
+ LOG(ERROR) << "SoftBitmap:Capture Invalid view";
+ return;
+ }
+
+ if (view->HasAcceleratedCanvasWithinViewport()) {
+ LOG(ERROR)
+ << "SoftBitmap:Capture avoided for WebGl/Cavas2D content on viewport";
+ return;
+ }
+
+ if (view->MainFrameWidget()->Size().IsEmpty()) {
+ LOG(ERROR) << "SoftBitmap:Capture Invalid size";
+ return;
+ }
+
+ snapshot->allocPixels(SkImageInfo::MakeN32Premul(snapshot_rect.width(),
+ snapshot_rect.height()));
+ SkCanvas canvas(*snapshot);
+ if (!view->PaintSoftBitmap(&canvas, snapshot_rect)) {
+ LOG(ERROR) << "SoftBitmap:Capture PaintSoftBitmap failed";
+ return;
+ }
+
+ *snapshot = skia::ImageOperations::Resize(
+ *snapshot, skia::ImageOperations::RESIZE_GOOD,
+ page_scale_factor * snapshot->width(),
+ page_scale_factor * snapshot->height());
+}
+#endif
+
void WebFrameWidgetImpl::OrientationChanged() {
local_root_->SendOrientationChangeEvent();
}
const VisualProperties& visual_properties) override;
bool UpdateScreenRects(const gfx::Rect& widget_screen_rect,
const gfx::Rect& window_screen_rect) override;
+#if BUILDFLAG(IS_EFL)
+ void GetContentSnapshot(const gfx::Rect& snapshot_rect,
+ float page_scale_factor,
+ SkBitmap* snapshot) override;
+#endif
void OrientationChanged() override;
void DidUpdateSurfaceAndScreen(
const display::ScreenInfos& previous_original_screen_infos) override;
#include "third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.h"
#endif
+#if BUILDFLAG(IS_EFL)
+#include "third_party/skia/include/core/SkBitmap.h"
+#endif
+
namespace blink {
namespace {
std::move(callback).Run();
}
+#if BUILDFLAG(IS_EFL)
+void WidgetBase::GetContentSnapshot(const gfx::Rect& snapshot_rect,
+ float page_scale_factor,
+ GetContentSnapshotCallback callback) {
+ SkBitmap content_snapshot;
+ client_->GetContentSnapshot(snapshot_rect, page_scale_factor,
+ &content_snapshot);
+ std::move(callback).Run(content_snapshot);
+}
+#endif
+
void WidgetBase::WasHidden() {
// A provisional frame widget will never be hidden since that would require it
// to be shown first. A frame must be attached to the frame tree before
void UpdateScreenRects(const gfx::Rect& widget_screen_rect,
const gfx::Rect& window_screen_rect,
UpdateScreenRectsCallback callback) override;
+#if BUILDFLAG(IS_EFL)
+ void GetContentSnapshot(const gfx::Rect& snapshot_rect,
+ float page_scale_factor,
+ GetContentSnapshotCallback callback) override;
+#endif
void WasHidden() override;
void WasShown(bool was_evicted,
mojom::blink::RecordContentToVisibleTimeRequestPtr
return false;
}
+#if BUILDFLAG(IS_EFL)
+ virtual void GetContentSnapshot(const gfx::Rect& snapshot_rect,
+ float page_scale_factor,
+ SkBitmap* snapshot) {}
+#endif
+
// Convert screen coordinates to device emulated coordinates (scaled
// coordinates when devtools is used). This occurs for popups where their
// window bounds are emulated.
namespace content {
+// If the first frame is not prepared after load is finished,
+// delay getting the snapshot by 100ms.
+static const int kSnapshotProcessDelay = 100;
+
+class ScreenshotCapturedCallback {
+ public:
+ ScreenshotCapturedCallback(Screenshot_Captured_Callback func, void* user_data)
+ : func_(func), user_data_(user_data) {}
+ void Run(Evas_Object* image) {
+ if (func_ != NULL)
+ (func_)(image, user_data_);
+ }
+
+ private:
+ Screenshot_Captured_Callback func_;
+ void* user_data_;
+};
+
class RenderWidgetHostHelperAura : public RenderWidgetHostHelper {
public:
explicit RenderWidgetHostHelperAura(RWHVAuraOffscreenHelperEfl* rwhv_helper)
OnFocusIn);
evas_object_event_callback_del(content_image_, EVAS_CALLBACK_FOCUS_OUT,
OnFocusOut);
+ evas_event_callback_del_full(evas_, EVAS_CALLBACK_RENDER_FLUSH_PRE,
+ OnEvasRenderFlushPre, this);
evas_object_del(content_image_elm_host_);
evas_object_del(content_image_);
device_scale_factor_ =
display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor();
+
+ snapshot_timer_.reset(new base::OneShotTimer);
}
void RWHVAuraOffscreenHelperEfl::SetAuraParentWindow(
evas_gl_api_->glBindTexture(GL_TEXTURE_2D, 0);
evas_gl_make_current(evas_gl_, 0, 0);
+
+ // for snapshot
+ frame_rendered_ = true;
}
void RWHVAuraOffscreenHelperEfl::InitEvasGL() {
}
}
+void RWHVAuraOffscreenHelperEfl::OnEvasRenderFlushPre(void* data,
+ Evas* evas,
+ void* event_info) {
+ RWHVAuraOffscreenHelperEfl* rwhv_helper =
+ static_cast<RWHVAuraOffscreenHelperEfl*>(data);
+ if (rwhv_helper && rwhv_helper->snapshot_task_list_.size() > 0)
+ rwhv_helper->ProcessSnapshotRequest();
+}
+
+void RWHVAuraOffscreenHelperEfl::InvalidateForSnapshot() {
+ evas_object_image_pixels_dirty_set(content_image_, true);
+}
+
+void RWHVAuraOffscreenHelperEfl::ProcessSnapshotRequest() {
+ if (!frame_rendered_)
+ return;
+
+ // Process all snapshot requests pending now.
+ for (auto& task : snapshot_task_list_) {
+ if (!task.is_null())
+ std::move(task).Run();
+ }
+
+ if (snapshot_timer_->IsRunning())
+ snapshot_timer_->Stop();
+ // Stop now and clear task list
+ snapshot_task_list_.clear();
+ // Unregister render post event
+ evas_event_callback_del_full(evas_, EVAS_CALLBACK_RENDER_FLUSH_PRE,
+ OnEvasRenderFlushPre, this);
+}
+
+void RWHVAuraOffscreenHelperEfl::RunGetSnapshotOnMainThread(
+ const gfx::Rect snapshot_area,
+ int request_id,
+ float scale_factor) {
+#if defined(USE_TTRACE)
+ TTRACE(TTRACE_TAG_WEB,
+ "RWHVAuraOffscreenHelperEfl::RunGetSnapshotOnMainThread");
+#endif
+ ScreenshotCapturedCallback* callback =
+ screen_capture_cb_map_.Lookup(request_id);
+
+ if (!callback)
+ return;
+
+ if (rwhv_aura_->IsShowing() && evas_focus_get(evas_))
+ callback->Run(GetSnapshot(snapshot_area, scale_factor));
+ screen_capture_cb_map_.Remove(request_id);
+}
+
+Evas_Object* RWHVAuraOffscreenHelperEfl::GetSnapshot(
+ const gfx::Rect& snapshot_area,
+ float scale_factor,
+ bool is_magnifier) {
+#if defined(USE_TTRACE)
+ TTRACE(TTRACE_TAG_WEB, "RWHVAuraOffscreenHelperEfl::GetSnapshot");
+#endif
+ if (scale_factor == 0.0)
+ return nullptr;
+
+ int rotation =
+ display::Screen::GetScreen()->GetPrimaryDisplay().RotationAsDegree();
+
+ bool is_evasgl_direct_landscape = false;
+ // For EvasGL Direct Rendering in landscape mode
+ if (!is_magnifier) {
+ is_evasgl_direct_landscape = ((rotation == 90 || rotation == 270) &&
+ !evas_gl_rotation_get(evas_gl_));
+ }
+
+ const gfx::Rect window_rect = gfx::ToEnclosingRect(gfx::ConvertRectToPixels(
+ rwhv_aura_->GetBoundsInRootWindow(), device_scale_factor_));
+ // |view_rect| is absolute coordinate of webview.
+ gfx::Rect view_rect = GetViewBoundsInPix();
+
+ // the gl coordinate system is based on screen rect for direct rendering.
+ // if direct rendering is disabled, the coordinate is based on webview.
+ // TODO(is46.kim) : Even though evas direct rendering flag is set, direct
+ // rendering may be disabled in the runtime depend on environment.
+ // There is no way to get current rendering mode. |is_direct_rendering|
+ // flag should be changed by current rendering mode.
+ bool is_direct_rendering = !is_magnifier;
+ if (!is_direct_rendering) {
+ view_rect.set_x(0);
+ view_rect.set_y(0);
+ }
+ const gfx::Size surface_size =
+ (is_direct_rendering) ? window_rect.size() : view_rect.size();
+
+ // |snapshot_area| is relative coordinate in webview.
+ int x = snapshot_area.x();
+ int y = snapshot_area.y();
+ int width = snapshot_area.width();
+ int height = snapshot_area.height();
+
+ // Convert |snapshot_area| to absolute coordinate.
+ x += view_rect.x();
+ y += view_rect.y();
+
+ // Limit snapshot rect by webview size
+ if (x + width > view_rect.right())
+ width = view_rect.right() - x;
+ if (y + height > view_rect.bottom())
+ height = view_rect.bottom() - y;
+
+ // Convert coordinate system for OpenGL.
+ // (0,0) is top left corner in webview coordinate system.
+ if (is_evasgl_direct_landscape) {
+ if (rotation == 270) {
+ x = surface_size.width() - x - width;
+ y = surface_size.height() - y - height;
+ }
+ std::swap(x, y);
+ std::swap(width, height);
+ } else {
+ // (0,0) is bottom left corner in opengl coordinate system.
+ y = surface_size.height() - y - height;
+ }
+
+ int size = width * height * sizeof(GLuint);
+ GLuint* data = (GLuint*)malloc(size);
+ if (!data) {
+ LOG(ERROR) << "Failed to allocate memory for snapshot";
+ return nullptr;
+ }
+
+ {
+#if defined(USE_TTRACE)
+ TTRACE(TTRACE_TAG_WEB,
+ "RWHVAuraOffscreenHelperEfl::GetSnapshot(ReadPixels)");
+#endif
+ if (!MakeCurrent()) {
+ LOG(ERROR) << "MakeCurrent() failed.";
+ free(data);
+ return nullptr;
+ }
+
+ evas_gl_api_->glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE,
+ data);
+ ClearCurrent();
+ }
+
+ if (!is_evasgl_direct_landscape && (rotation == 90 || rotation == 270))
+ std::swap(width, height);
+
+#if _DUMP_SNAPSHOT_
+ static int frame_number = 0;
+ frame_number++;
+ {
+ char filename[150];
+ sprintf(filename, "/opt/share/dump/snapshot%02d-%d_%dx%d_rgba8888.raw",
+ frame_number, rotation, width, height);
+ FILE* fp = fopen(filename, "w");
+ if (fp) {
+ fwrite(data, width * height * 4, 1, fp);
+ fflush(fp);
+ fclose(fp);
+ } else {
+ LOG(ERROR) << "Unable to open file";
+ }
+ }
+#endif
+
+ // flip the Y axis and change color format from RGBA to BGRA
+ for (int j = 0; j < height / 2; j++) {
+ for (int i = 0; i < width; i++) {
+ GLuint& px1 = data[(j * width) + i];
+ GLuint& px2 = data[((height - 1) - j) * width + i];
+ px1 = ((px1 & 0xff) << 16) | ((px1 >> 16) & 0xff) | (px1 & 0xff00ff00) |
+ 0xff000000;
+ px2 = ((px2 & 0xff) << 16) | ((px2 >> 16) & 0xff) | (px2 & 0xff00ff00) |
+ 0xff000000;
+ std::swap(px1, px2);
+ }
+ }
+
+ SkBitmap bitmap;
+ if (scale_factor != 1.0 || rotation) {
+ bitmap.setInfo(SkImageInfo::MakeN32Premul(width, height));
+ bitmap.setPixels(data);
+
+ // scaling bitmap
+ if (scale_factor != 1.0) {
+ width = scale_factor * width;
+ height = scale_factor * height;
+ bitmap = skia::ImageOperations::Resize(
+ bitmap, skia::ImageOperations::RESIZE_GOOD, width, height);
+ }
+
+ if (rotation == 90) {
+ bitmap = SkBitmapOperations::Rotate(bitmap,
+ SkBitmapOperations::ROTATION_90_CW);
+ } else if (rotation == 180) {
+ bitmap = SkBitmapOperations::Rotate(bitmap,
+ SkBitmapOperations::ROTATION_180_CW);
+ } else if (rotation == 270) {
+ bitmap = SkBitmapOperations::Rotate(bitmap,
+ SkBitmapOperations::ROTATION_270_CW);
+ }
+ }
+
+ if (rotation == 90 || rotation == 270)
+ std::swap(width, height);
+
+ Evas_Object* image = evas_object_image_filled_add(evas_);
+ if (image) {
+ evas_object_image_size_set(image, width, height);
+ evas_object_image_alpha_set(image, EINA_TRUE);
+ evas_object_image_data_copy_set(image,
+ bitmap.empty() ? data : bitmap.getPixels());
+ evas_object_resize(image, width, height);
+#if _DUMP_SNAPSHOT_
+ char filename[150];
+ sprintf(filename, "/opt/share/dump/snapshot%02d-%d_%dx%d.png", frame_number,
+ rotation, width, height);
+ evas_object_image_save(image, filename, NULL, "quality=100");
+#endif
+ }
+ free(data);
+
+ return image;
+}
+
+void RWHVAuraOffscreenHelperEfl::RequestSnapshotAsync(
+ const gfx::Rect& snapshot_area,
+ Screenshot_Captured_Callback callback,
+ void* user_data,
+ float scale_factor) {
+#if defined(USE_TTRACE)
+ TTRACE(TTRACE_TAG_WEB, "RWHVAuraOffscreenHelperEfl::RequestSnapshotAsync");
+#endif
+ ScreenshotCapturedCallback* cb =
+ new ScreenshotCapturedCallback(callback, user_data);
+
+ int request_id = screen_capture_cb_map_.Add(base::WrapUnique(cb));
+
+ if (rwhv_aura_->IsShowing() && evas_focus_get(evas_)) {
+ // Create a snapshot task that will be executed after frame
+ // is generated and drawn to surface.
+ snapshot_task_list_.push_back(base::BindOnce(
+ &RWHVAuraOffscreenHelperEfl::RunGetSnapshotOnMainThread,
+ base::Unretained(this), snapshot_area, request_id, scale_factor));
+
+ // Delete OnEvasRenderPost callback to prevent registration twice.
+ evas_event_callback_del_full(evas_, EVAS_CALLBACK_RENDER_FLUSH_PRE,
+ OnEvasRenderFlushPre, this);
+ evas_event_callback_add(evas_, EVAS_CALLBACK_RENDER_FLUSH_PRE,
+ OnEvasRenderFlushPre, this);
+
+ // If there is no EVAS_CALLBACK_RENDER_FLUSH_PRE event sent out in 400ms,
+ // we trigger one by ourself
+ snapshot_timer_->Start(
+ FROM_HERE, base::Milliseconds(4 * kSnapshotProcessDelay),
+ base::BindOnce(&RWHVAuraOffscreenHelperEfl::InvalidateForSnapshot,
+ base::Unretained(this)));
+ } else {
+ // Sends message to renderer to capture the content snapshot of the view.
+ rwhv_aura_->host()->RequestContentSnapshot(snapshot_area, scale_factor,
+ request_id);
+ }
+}
+
+void RWHVAuraOffscreenHelperEfl::DidGetContentSnapshot(const SkBitmap& bitmap,
+ const int request_id) {
+ ScreenshotCapturedCallback* callback =
+ screen_capture_cb_map_.Lookup(request_id);
+ if (!callback)
+ return;
+
+ Evas_Object* image = nullptr;
+ if (!bitmap.empty()) {
+ image = evas_object_image_filled_add(evas_);
+ evas_object_image_size_set(image, bitmap.width(), bitmap.height());
+ evas_object_image_data_copy_set(image, bitmap.getPixels());
+ }
+ callback->Run(image);
+ screen_capture_cb_map_.Remove(request_id);
+}
+
Evas_Object* RWHVAuraOffscreenHelperEfl::ewk_view() const {
auto wci = static_cast<WebContentsImpl*>(web_contents_);
if (!wci)
namespace content {
+typedef void (*Screenshot_Captured_Callback)(Evas_Object* image,
+ void* user_data);
+typedef base::OnceCallback<void()> SnapshotTask;
+
class RenderWidgetHostHelper;
class RenderWidgetHostImpl;
class RenderWidgetHostViewAura;
+class ScreenshotCapturedCallback;
class WebContents;
class WebContentsDelegate;
#endif
);
+ // |snapshot_area| is relative coordinate system based on Webview.
+ // (0,0) is top left corner.
+ Evas_Object* GetSnapshot(const gfx::Rect& snapshot_area,
+ float scale_factor,
+ bool is_magnifier = false);
+ void RequestSnapshotAsync(const gfx::Rect& snapshot_area,
+ Screenshot_Captured_Callback callback,
+ void* user_data,
+ float scale_factor = 1.0);
+ void DidGetContentSnapshot(const SkBitmap& bitmap, int request_id);
+
RenderWidgetHostViewAura* rwhva() { return rwhv_aura_; }
void OnMouseOrTouchEvent(ui::Event* event);
void DidHandleKeyEvent(blink::WebInputEvent::Type input_event_type,
static void OnFocusOut(void* data, Evas*, Evas_Object*, void*);
static void OnHostFocusIn(void* data, Evas_Object*, void*);
static void OnHostFocusOut(void* data, Evas_Object*, void*);
+ static void OnEvasRenderFlushPre(void* data, Evas* evas, void* event_info);
void Initialize();
void InitializeProgram();
bool MakeCurrent();
void ClearBrowserFrame();
+ void InvalidateForSnapshot();
+ void ProcessSnapshotRequest();
+ void RunGetSnapshotOnMainThread(const gfx::Rect snapshot_area,
+ int request_id,
+ float scale_factor);
+
ui::EflEventHandler* GetEventHandler();
ui::IMContextEfl* GetIMContextEfl();
WebContents* web_contents_ = nullptr;
std::unique_ptr<RenderWidgetHostHelper> rwh_helper_;
+
+ bool frame_rendered_ = false;
+ std::list<SnapshotTask> snapshot_task_list_;
+ std::unique_ptr<base::OneShotTimer> snapshot_timer_;
+ base::IDMap<std::unique_ptr<ScreenshotCapturedCallback>>
+ screen_capture_cb_map_;
};
} // namespace content
bool EWebView::GetSnapshotAsync(
Eina_Rectangle rect,
Ewk_Web_App_Screenshot_Captured_Callback callback,
- void* user_data) {
-#if !defined(USE_AURA)
- if (!rwhv())
+ void* user_data,
+ float scale_factor) {
+ if (!rwhva() || !rwhva()->offscreen_helper())
return false;
- return rwhv()->RequestSnapshotAsync(rect, callback, user_data);
-#else
- return false;
-#endif
+ rwhva()->offscreen_helper()->RequestSnapshotAsync(
+ gfx::Rect(rect.x, rect.y, rect.w, rect.h), callback, user_data,
+ scale_factor);
+ return true;
}
-Evas_Object* EWebView::GetSnapshot(Eina_Rectangle rect) {
- Evas_Object* image = NULL;
-#if !defined(USE_AURA)
-#if BUILDFLAG(IS_TIZEN)
- if (!rwhv() || !rwhv()->MakeCurrent())
- return NULL;
-
- int width = rect.w;
- int height = rect.h;
-
- if (width > rwhv()->GetViewBoundsInPix().width() - rect.x)
- width = rwhv()->GetViewBoundsInPix().width() - rect.x;
- if (height > rwhv()->GetViewBoundsInPix().height() - rect.y)
- height = rwhv()->GetViewBoundsInPix().height() - rect.y;
-
- int x = rect.x;
- int y = rwhv()->GetViewBoundsInPix().height() - height + rect.y;
+Evas_Object* EWebView::GetSnapshot(Eina_Rectangle rect, float scale_factor) {
+ if (!rwhva() || !rwhva()->offscreen_helper())
+ return nullptr;
- Evas_GL_API* gl_api = rwhv()->evasGlApi();
- DCHECK(gl_api);
- int size = width * height * sizeof(GLuint);
-
- GLuint* tmp = (GLuint*)malloc(size);
- if (!tmp)
- return NULL;
-
- GLuint* bits = (GLuint*)malloc(size);
- if (!bits) {
- free(tmp);
- return NULL;
- }
-
- gl_api->glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE,
- (GLubyte*)bits);
-
- // flip the Y axis and change color format from RGBA to BGRA
- int i, j, idx1, idx2;
- GLuint d;
- for (j = 0; j < height; j++) {
- for (i = 0; i < width; i++) {
- idx1 = (j * width) + i;
- idx2 = ((height - 1) - j) * width + i;
- d = bits[idx1];
- tmp[idx2] = ((d & 0x000000ff) << 16) + ((d & 0x00ff0000) >> 16) +
- ((d & 0xff00ff00));
- }
- }
-
- image = evas_object_image_filled_add(rwhv()->evas());
- if (image) {
- evas_object_image_size_set(image, width, height);
- evas_object_image_alpha_set(image, EINA_TRUE);
- evas_object_image_data_copy_set(image, tmp);
- evas_object_resize(image, width, height);
- }
-#endif
-#endif
- return image;
+ return rwhva()->offscreen_helper()->GetSnapshot(
+ gfx::Rect(rect.x, rect.y, rect.w, rect.h), scale_factor);
}
void EWebView::BackForwardListClear() {
* Creates a snapshot of given rectangle from EWebView
*
* @param rect rectangle of EWebView which will be taken into snapshot
- *
+ * @param scale_factor scale factor
* @return created snapshot or NULL if error occured.
* @note ownership of snapshot is passed to caller
- */
- Evas_Object* GetSnapshot(Eina_Rectangle rect);
+ */
+ Evas_Object* GetSnapshot(Eina_Rectangle rect, float scale_factor);
bool GetSnapshotAsync(Eina_Rectangle rect,
Ewk_Web_App_Screenshot_Captured_Callback callback,
- void* user_data);
+ void* user_data,
+ float scale_factor);
void InvokePolicyResponseCallback(_Ewk_Policy_Decision* policy_decision,
bool* defer);
void InvokePolicyNavigationCallback(const NavigationPolicyParams& params,
{
EINA_SAFETY_ON_NULL_RETURN_VAL(canvas, NULL);
EWK_VIEW_IMPL_GET_OR_RETURN(view, impl, NULL);
- // Zoom level equality (<=0.001) is sufficient compared to high precision std::numeric_value::epsilon.
- if (!blink::PageZoomValuesEqual(scale_factor, 1.0)) {
- LOG(ERROR) << "We only support scale factor of 1.0."
- << "Scaling option will be supported after hardware acceleration is enabled.";
- return NULL;
- }
- return impl->GetSnapshot(view_area);
+ return impl->GetSnapshot(view_area, scale_factor);
}
Eina_Bool ewk_view_screenshot_contents_get_async(const Evas_Object* view, Eina_Rectangle view_area,
{
EINA_SAFETY_ON_NULL_RETURN_VAL(callback, EINA_FALSE);
EWK_VIEW_IMPL_GET_OR_RETURN(view, impl, EINA_FALSE);
- // Zoom level equality (<=0.001) is sufficient compared to high precision std::numeric_value::epsilon.
- if (!blink::PageZoomValuesEqual(scale_factor, 1.0)) {
- LOG(ERROR) << "We only support scale factor of 1.0."
- << "Scaling option will be supported after hardware acceleration is enabled.";
- return EINA_FALSE;
- }
- return impl->GetSnapshotAsync(view_area, callback, user_data) ? EINA_TRUE : EINA_FALSE;
+ return impl->GetSnapshotAsync(view_area, callback, user_data, scale_factor)
+ ? EINA_TRUE
+ : EINA_FALSE;
}
unsigned int ewk_view_inspector_server_start(Evas_Object* ewkView, unsigned int port)
(sizeof(rots) / sizeof(int)));
}
+void Window::TakeScreenshotAsync() {
+ log_trace("%s", __PRETTY_FUNCTION__);
+ Eina_Rectangle rect;
+ EINA_RECTANGLE_SET(&rect, 0, 0, 400, 400);
+ ewk_view_screenshot_contents_get_async(web_view_, rect, 1.0,
+ evas_object_evas_get(web_view_),
+ &Window::OnScreenshotCaptured, NULL);
+}
+
#if BUILDFLAG(IS_TIZEN_TV)
void Window::On_Video_Playback_Load(void* data,
Evas_Object*,
ewk_view_custom_header_add(web_view_, "accept", "image/webp,*/*;q=0.8");
}
+void Window::OnScreenshotCaptured(Evas_Object* image, void* user_data) {
+ log_trace("%s", __PRETTY_FUNCTION__);
+ static int c = 1;
+ char buf[250];
+ sprintf(buf, "screenshot%d.png", c++);
+ if (evas_object_image_save(image, buf, 0, 0))
+ log_info("Screenshot image saved in %s", buf);
+ else
+ log_info("Screenshot image could not be saved");
+}
+
void Window::Exit() const {
browser_.Exit();
}
Window(Browser&, int width, int height, bool incognito);
~Window();
- Evas_Object* GetEvasObject() const { return window_; };
+ Evas_Object* GetEvasObject() const { return window_; }
Ewk_Settings* GetEwkSettings() const;
void LoadURL(std::string url);
void ShowInspectorURL(const char* url);
void SetGoogleDataProxyHeaders() const;
void SetAutoRotate();
+ void TakeScreenshotAsync();
void Exit() const;
IdType Id() const;
static void OnNewWindowPolicyDecide(void*, Evas_Object*, void*);
static void OnBackForwardListChanged(void*, Evas_Object*, void*);
static void OnQuotaPermissionRequest(Evas_Object*, const Ewk_Quota_Permission_Request*, void*);
+ static void OnScreenshotCaptured(Evas_Object*, void*);
static void OnUserMediaPermissionRequest(void* data, Evas_Object*, void* event_info);
static void OnUserMediaPermissionDecisionTaken(bool decision, void* data);
static void OnEnterFullScreenRequest(void*, Evas_Object*, void*);
elm_ctxpopup_item_append(menu_, "Change page zoom level", NULL,
&WindowUI::OnShowZoomPopup, this);
+ elm_ctxpopup_item_append(menu_, "Take screenshot async", NULL,
+ &WindowUI::OnTakeScreenshotAsync, this);
+
elm_ctxpopup_item_append(menu_, "Quit", NULL, &WindowUI::Exit, this);
return menu_;
thiz->CloseMenu();
}
+void WindowUI::OnTakeScreenshotAsync(void* data, Evas_Object* obj, void*) {
+ log_trace("%s", __PRETTY_FUNCTION__);
+ WindowUI* thiz = static_cast<WindowUI*>(data);
+ thiz->window_.TakeScreenshotAsync();
+ thiz->CloseMenu();
+}
+
void WindowUI::OnZoomChanged(void* data, Evas_Object* obj, void*) {
log_trace("%s", __PRETTY_FUNCTION__);
WindowUI* thiz = static_cast<WindowUI*>(data);
static void ClosePopup(void* data, Evas_Object*, void*);
static void OnShowZoomPopup(void* data, Evas_Object*, void*);
+ static void OnTakeScreenshotAsync(void* data, Evas_Object*, void*);
static void OnZoomChanged(void* data, Evas_Object*, void*);
static void OnRememberFormDataChange(void* data, Evas_Object*, void*);
static void OnRememberPasswordChange(void* data, Evas_Object*, void*);