зеркало из https://github.com/electron/electron.git
Коммит
53831d71e3
|
@ -17,6 +17,9 @@
|
|||
#include "atom/browser/lib/bluetooth_chooser.h"
|
||||
#include "atom/browser/native_window.h"
|
||||
#include "atom/browser/net/atom_network_delegate.h"
|
||||
#include "atom/browser/osr/osr_output_device.h"
|
||||
#include "atom/browser/osr/osr_web_contents_view.h"
|
||||
#include "atom/browser/osr/osr_render_widget_host_view.h"
|
||||
#include "atom/browser/ui/drag_util.h"
|
||||
#include "atom/browser/web_contents_permission_helper.h"
|
||||
#include "atom/browser/web_contents_preferences.h"
|
||||
|
@ -42,6 +45,7 @@
|
|||
#include "chrome/browser/printing/print_view_manager_basic.h"
|
||||
#include "chrome/browser/printing/print_preview_message_handler.h"
|
||||
#include "content/browser/renderer_host/render_widget_host_impl.h"
|
||||
#include "content/browser/web_contents/web_contents_impl.h"
|
||||
#include "content/common/view_messages.h"
|
||||
#include "content/public/browser/favicon_status.h"
|
||||
#include "content/public/browser/native_web_keyboard_event.h"
|
||||
|
@ -190,6 +194,7 @@ struct Converter<atom::api::WebContents::Type> {
|
|||
case Type::BROWSER_WINDOW: type = "window"; break;
|
||||
case Type::REMOTE: type = "remote"; break;
|
||||
case Type::WEB_VIEW: type = "webview"; break;
|
||||
case Type::OFF_SCREEN: type = "offscreen"; break;
|
||||
default: break;
|
||||
}
|
||||
return mate::ConvertToV8(isolate, type);
|
||||
|
@ -205,6 +210,8 @@ struct Converter<atom::api::WebContents::Type> {
|
|||
*out = Type::WEB_VIEW;
|
||||
} else if (type == "backgroundPage") {
|
||||
*out = Type::BACKGROUND_PAGE;
|
||||
} else if (type == "offscreen") {
|
||||
*out = Type::OFF_SCREEN;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -277,6 +284,8 @@ WebContents::WebContents(v8::Isolate* isolate,
|
|||
type_ = WEB_VIEW;
|
||||
else if (options.Get("isBackgroundPage", &b) && b)
|
||||
type_ = BACKGROUND_PAGE;
|
||||
else if (options.Get("offscreen", &b) && b)
|
||||
type_ = OFF_SCREEN;
|
||||
|
||||
// Obtain the session.
|
||||
std::string partition;
|
||||
|
@ -300,6 +309,17 @@ WebContents::WebContents(v8::Isolate* isolate,
|
|||
guest_delegate_.reset(new WebViewGuestDelegate);
|
||||
params.guest_delegate = guest_delegate_.get();
|
||||
web_contents = content::WebContents::Create(params);
|
||||
} else if (IsOffScreen()) {
|
||||
bool transparent = false;
|
||||
options.Get("transparent", &transparent);
|
||||
|
||||
content::WebContents::CreateParams params(session->browser_context());
|
||||
auto* view = new OffScreenWebContentsView(transparent);
|
||||
params.view = view;
|
||||
params.delegate_view = view;
|
||||
|
||||
web_contents = content::WebContents::Create(params);
|
||||
view->SetWebContents(web_contents);
|
||||
} else {
|
||||
content::WebContents::CreateParams params(session->browser_context());
|
||||
web_contents = content::WebContents::Create(params);
|
||||
|
@ -360,7 +380,7 @@ bool WebContents::AddMessageToConsole(content::WebContents* source,
|
|||
const base::string16& message,
|
||||
int32_t line_no,
|
||||
const base::string16& source_id) {
|
||||
if (type_ == BROWSER_WINDOW) {
|
||||
if (type_ == BROWSER_WINDOW || type_ == OFF_SCREEN) {
|
||||
return false;
|
||||
} else {
|
||||
Emit("console-message", level, message, line_no, source_id);
|
||||
|
@ -371,7 +391,7 @@ bool WebContents::AddMessageToConsole(content::WebContents* source,
|
|||
void WebContents::OnCreateWindow(const GURL& target_url,
|
||||
const std::string& frame_name,
|
||||
WindowOpenDisposition disposition) {
|
||||
if (type_ == BROWSER_WINDOW)
|
||||
if (type_ == BROWSER_WINDOW || type_ == OFF_SCREEN)
|
||||
Emit("-new-window", target_url, frame_name, disposition);
|
||||
else
|
||||
Emit("new-window", target_url, frame_name, disposition);
|
||||
|
@ -381,7 +401,7 @@ content::WebContents* WebContents::OpenURLFromTab(
|
|||
content::WebContents* source,
|
||||
const content::OpenURLParams& params) {
|
||||
if (params.disposition != CURRENT_TAB) {
|
||||
if (type_ == BROWSER_WINDOW)
|
||||
if (type_ == BROWSER_WINDOW || type_ == OFF_SCREEN)
|
||||
Emit("-new-window", params.url, "", params.disposition);
|
||||
else
|
||||
Emit("new-window", params.url, "", params.disposition);
|
||||
|
@ -398,7 +418,7 @@ content::WebContents* WebContents::OpenURLFromTab(
|
|||
void WebContents::BeforeUnloadFired(content::WebContents* tab,
|
||||
bool proceed,
|
||||
bool* proceed_to_fire_unload) {
|
||||
if (type_ == BROWSER_WINDOW)
|
||||
if (type_ == BROWSER_WINDOW || type_ == OFF_SCREEN)
|
||||
*proceed_to_fire_unload = proceed;
|
||||
else
|
||||
*proceed_to_fire_unload = true;
|
||||
|
@ -411,7 +431,8 @@ void WebContents::MoveContents(content::WebContents* source,
|
|||
|
||||
void WebContents::CloseContents(content::WebContents* source) {
|
||||
Emit("close");
|
||||
if (type_ == BROWSER_WINDOW && owner_window())
|
||||
|
||||
if ((type_ == BROWSER_WINDOW || type_ == OFF_SCREEN) && owner_window())
|
||||
owner_window()->CloseContents(source);
|
||||
}
|
||||
|
||||
|
@ -465,13 +486,13 @@ void WebContents::ExitFullscreenModeForTab(content::WebContents* source) {
|
|||
|
||||
void WebContents::RendererUnresponsive(content::WebContents* source) {
|
||||
Emit("unresponsive");
|
||||
if (type_ == BROWSER_WINDOW && owner_window())
|
||||
if ((type_ == BROWSER_WINDOW || type_ == OFF_SCREEN) && owner_window())
|
||||
owner_window()->RendererUnresponsive(source);
|
||||
}
|
||||
|
||||
void WebContents::RendererResponsive(content::WebContents* source) {
|
||||
Emit("responsive");
|
||||
if (type_ == BROWSER_WINDOW && owner_window())
|
||||
if ((type_ == BROWSER_WINDOW || type_ == OFF_SCREEN) && owner_window())
|
||||
owner_window()->RendererResponsive(source);
|
||||
}
|
||||
|
||||
|
@ -587,8 +608,15 @@ void WebContents::DidChangeThemeColor(SkColor theme_color) {
|
|||
|
||||
void WebContents::DocumentLoadedInFrame(
|
||||
content::RenderFrameHost* render_frame_host) {
|
||||
if (!render_frame_host->GetParent())
|
||||
if (!render_frame_host->GetParent()) {
|
||||
if (IsOffScreen()) {
|
||||
auto* rwhv = web_contents()->GetRenderWidgetHostView();
|
||||
static_cast<OffScreenRenderWidgetHostView*>(rwhv)->SetPaintCallback(
|
||||
base::Bind(&WebContents::OnPaint, base::Unretained(this)));
|
||||
}
|
||||
|
||||
Emit("dom-ready");
|
||||
}
|
||||
}
|
||||
|
||||
void WebContents::DidFinishLoad(content::RenderFrameHost* render_frame_host,
|
||||
|
@ -782,13 +810,6 @@ WebContents::Type WebContents::GetType() const {
|
|||
return type_;
|
||||
}
|
||||
|
||||
#if !defined(OS_MACOSX)
|
||||
bool WebContents::IsFocused() const {
|
||||
auto view = web_contents()->GetRenderWidgetHostView();
|
||||
return view && view->HasFocus();
|
||||
}
|
||||
#endif
|
||||
|
||||
bool WebContents::Equal(const WebContents* web_contents) const {
|
||||
return GetID() == web_contents->GetID();
|
||||
}
|
||||
|
@ -1152,6 +1173,13 @@ void WebContents::Focus() {
|
|||
web_contents()->Focus();
|
||||
}
|
||||
|
||||
#if !defined(OS_MACOSX)
|
||||
bool WebContents::IsFocused() const {
|
||||
auto view = web_contents()->GetRenderWidgetHostView();
|
||||
return view && view->HasFocus();
|
||||
}
|
||||
#endif
|
||||
|
||||
void WebContents::TabTraverse(bool reverse) {
|
||||
web_contents()->FocusThroughTabTraversal(reverse);
|
||||
}
|
||||
|
@ -1316,6 +1344,72 @@ bool WebContents::IsGuest() const {
|
|||
return type_ == WEB_VIEW;
|
||||
}
|
||||
|
||||
bool WebContents::IsOffScreen() const {
|
||||
return type_ == OFF_SCREEN;
|
||||
}
|
||||
|
||||
void WebContents::OnPaint(const gfx::Rect& dirty_rect,
|
||||
const gfx::Size& bitmap_size,
|
||||
void* bitmap_pixels) {
|
||||
v8::MaybeLocal<v8::Object> buffer = node::Buffer::New(
|
||||
isolate(), reinterpret_cast<char*>(bitmap_pixels), sizeof(bitmap_pixels));
|
||||
if (!buffer.IsEmpty())
|
||||
Emit("paint", dirty_rect, buffer.ToLocalChecked(), bitmap_size);
|
||||
}
|
||||
|
||||
void WebContents::StartPainting() {
|
||||
if (!IsOffScreen())
|
||||
return;
|
||||
|
||||
auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
|
||||
web_contents()->GetRenderWidgetHostView());
|
||||
if (osr_rwhv) {
|
||||
osr_rwhv->SetPainting(true);
|
||||
osr_rwhv->Show();
|
||||
}
|
||||
}
|
||||
|
||||
void WebContents::StopPainting() {
|
||||
if (!IsOffScreen())
|
||||
return;
|
||||
|
||||
auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
|
||||
web_contents()->GetRenderWidgetHostView());
|
||||
if (osr_rwhv) {
|
||||
osr_rwhv->SetPainting(false);
|
||||
osr_rwhv->Hide();
|
||||
}
|
||||
}
|
||||
|
||||
bool WebContents::IsPainting() const {
|
||||
if (!IsOffScreen())
|
||||
return false;
|
||||
|
||||
const auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
|
||||
web_contents()->GetRenderWidgetHostView());
|
||||
return osr_rwhv && osr_rwhv->IsPainting();
|
||||
}
|
||||
|
||||
void WebContents::SetFrameRate(int frame_rate) {
|
||||
if (!IsOffScreen())
|
||||
return;
|
||||
|
||||
auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
|
||||
web_contents()->GetRenderWidgetHostView());
|
||||
if (osr_rwhv)
|
||||
osr_rwhv->SetFrameRate(frame_rate);
|
||||
}
|
||||
|
||||
int WebContents::GetFrameRate() const {
|
||||
if (!IsOffScreen())
|
||||
return 0;
|
||||
|
||||
const auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
|
||||
web_contents()->GetRenderWidgetHostView());
|
||||
return osr_rwhv ? osr_rwhv->GetFrameRate() : 0;
|
||||
}
|
||||
|
||||
|
||||
v8::Local<v8::Value> WebContents::GetWebPreferences(v8::Isolate* isolate) {
|
||||
WebContentsPreferences* web_preferences =
|
||||
WebContentsPreferences::FromWebContents(web_contents());
|
||||
|
@ -1408,6 +1502,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
|
|||
.SetMethod("findInPage", &WebContents::FindInPage)
|
||||
.SetMethod("stopFindInPage", &WebContents::StopFindInPage)
|
||||
.SetMethod("focus", &WebContents::Focus)
|
||||
.SetMethod("isFocused", &WebContents::IsFocused)
|
||||
.SetMethod("tabTraverse", &WebContents::TabTraverse)
|
||||
.SetMethod("_send", &WebContents::SendIPCMessage)
|
||||
.SetMethod("sendInputEvent", &WebContents::SendInputEvent)
|
||||
|
@ -1417,6 +1512,12 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
|
|||
.SetMethod("startDrag", &WebContents::StartDrag)
|
||||
.SetMethod("setSize", &WebContents::SetSize)
|
||||
.SetMethod("isGuest", &WebContents::IsGuest)
|
||||
.SetMethod("isOffscreen", &WebContents::IsOffScreen)
|
||||
.SetMethod("startPainting", &WebContents::StartPainting)
|
||||
.SetMethod("stopPainting", &WebContents::StopPainting)
|
||||
.SetMethod("isPainting", &WebContents::IsPainting)
|
||||
.SetMethod("setFrameRate", &WebContents::SetFrameRate)
|
||||
.SetMethod("getFrameRate", &WebContents::GetFrameRate)
|
||||
.SetMethod("getType", &WebContents::GetType)
|
||||
.SetMethod("getWebPreferences", &WebContents::GetWebPreferences)
|
||||
.SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow)
|
||||
|
@ -1432,7 +1533,6 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
|
|||
&WebContents::ShowDefinitionForSelection)
|
||||
.SetMethod("copyImageAt", &WebContents::CopyImageAt)
|
||||
.SetMethod("capturePage", &WebContents::CapturePage)
|
||||
.SetMethod("isFocused", &WebContents::IsFocused)
|
||||
.SetProperty("id", &WebContents::ID)
|
||||
.SetProperty("session", &WebContents::Session)
|
||||
.SetProperty("hostWebContents", &WebContents::HostWebContents)
|
||||
|
|
|
@ -48,6 +48,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
|||
BROWSER_WINDOW, // Used by BrowserWindow.
|
||||
REMOTE, // Thin wrap around an existing WebContents.
|
||||
WEB_VIEW, // Used by <webview>.
|
||||
OFF_SCREEN, // Used for offscreen rendering
|
||||
};
|
||||
|
||||
// For node.js callback function type: function(error, buffer)
|
||||
|
@ -67,7 +68,6 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
|||
|
||||
int GetID() const;
|
||||
Type GetType() const;
|
||||
bool IsFocused() const;
|
||||
bool Equal(const WebContents* web_contents) const;
|
||||
void LoadURL(const GURL& url, const mate::Dictionary& options);
|
||||
void DownloadURL(const GURL& url);
|
||||
|
@ -130,6 +130,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
|||
|
||||
// Focus.
|
||||
void Focus();
|
||||
bool IsFocused() const;
|
||||
void TabTraverse(bool reverse);
|
||||
|
||||
// Send messages to browser.
|
||||
|
@ -155,6 +156,17 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
|||
void SetSize(const SetSizeParams& params);
|
||||
bool IsGuest() const;
|
||||
|
||||
// Methods for offscreen rendering
|
||||
bool IsOffScreen() const;
|
||||
void OnPaint(const gfx::Rect& dirty_rect,
|
||||
const gfx::Size& bitmap_size,
|
||||
void* bitmap_pixels);
|
||||
void StartPainting();
|
||||
void StopPainting();
|
||||
bool IsPainting() const;
|
||||
void SetFrameRate(int frame_rate);
|
||||
int GetFrameRate() const;
|
||||
|
||||
// Callback triggered on permission response.
|
||||
void OnEnterFullscreenModeForTab(content::WebContents* source,
|
||||
const GURL& origin,
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
#include "atom/common/native_mate_converters/string16_converter.h"
|
||||
#include "atom/common/node_includes.h"
|
||||
#include "atom/common/options_switches.h"
|
||||
#include "base/command_line.h"
|
||||
#include "content/public/browser/render_process_host.h"
|
||||
#include "content/public/common/content_switches.h"
|
||||
#include "native_mate/constructor.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "ui/gfx/geometry/rect.h"
|
||||
|
@ -78,6 +80,10 @@ Window::Window(v8::Isolate* isolate, v8::Local<v8::Object> wrapper,
|
|||
if (options.Get(options::kBackgroundColor, &value))
|
||||
web_preferences.Set(options::kBackgroundColor, value);
|
||||
|
||||
v8::Local<v8::Value> transparent;
|
||||
if (options.Get("transparent", &transparent))
|
||||
web_preferences.Set("transparent", transparent);
|
||||
|
||||
// Creates the WebContents used by BrowserWindow.
|
||||
auto web_contents = WebContents::Create(isolate, web_preferences);
|
||||
web_contents_.Reset(isolate, web_contents.ToV8());
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
// Copyright (c) 2016 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/osr/osr_output_device.h"
|
||||
|
||||
#include "third_party/skia/include/core/SkDevice.h"
|
||||
#include "ui/gfx/skia_util.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
OffScreenOutputDevice::OffScreenOutputDevice(bool transparent,
|
||||
const OnPaintCallback& callback)
|
||||
: transparent_(transparent),
|
||||
callback_(callback),
|
||||
active_(false) {
|
||||
DCHECK(!callback_.is_null());
|
||||
}
|
||||
|
||||
OffScreenOutputDevice::~OffScreenOutputDevice() {
|
||||
}
|
||||
|
||||
void OffScreenOutputDevice::Resize(
|
||||
const gfx::Size& pixel_size, float scale_factor) {
|
||||
scale_factor_ = scale_factor;
|
||||
|
||||
if (viewport_pixel_size_ == pixel_size) return;
|
||||
viewport_pixel_size_ = pixel_size;
|
||||
|
||||
canvas_.reset();
|
||||
bitmap_.reset(new SkBitmap);
|
||||
bitmap_->allocN32Pixels(viewport_pixel_size_.width(),
|
||||
viewport_pixel_size_.height(),
|
||||
!transparent_);
|
||||
if (bitmap_->drawsNothing()) {
|
||||
NOTREACHED();
|
||||
bitmap_.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if (transparent_)
|
||||
bitmap_->eraseARGB(0, 0, 0, 0);
|
||||
|
||||
canvas_.reset(new SkCanvas(*bitmap_));
|
||||
}
|
||||
|
||||
SkCanvas* OffScreenOutputDevice::BeginPaint(const gfx::Rect& damage_rect) {
|
||||
DCHECK(canvas_.get());
|
||||
DCHECK(bitmap_.get());
|
||||
|
||||
damage_rect_ = damage_rect;
|
||||
|
||||
return canvas_.get();
|
||||
}
|
||||
|
||||
void OffScreenOutputDevice::EndPaint() {
|
||||
DCHECK(canvas_.get());
|
||||
DCHECK(bitmap_.get());
|
||||
|
||||
if (!bitmap_.get()) return;
|
||||
|
||||
cc::SoftwareOutputDevice::EndPaint();
|
||||
|
||||
if (active_)
|
||||
OnPaint(damage_rect_);
|
||||
}
|
||||
|
||||
void OffScreenOutputDevice::SetActive(bool active) {
|
||||
if (active == active_)
|
||||
return;
|
||||
active_ = active;
|
||||
|
||||
if (active_)
|
||||
OnPaint(gfx::Rect(viewport_pixel_size_));
|
||||
}
|
||||
|
||||
void OffScreenOutputDevice::OnPaint(const gfx::Rect& damage_rect) {
|
||||
gfx::Rect rect = damage_rect;
|
||||
if (!pending_damage_rect_.IsEmpty()) {
|
||||
rect.Union(pending_damage_rect_);
|
||||
pending_damage_rect_.SetRect(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
rect.Intersect(gfx::Rect(viewport_pixel_size_));
|
||||
if (rect.IsEmpty())
|
||||
return;
|
||||
|
||||
SkAutoLockPixels bitmap_pixels_lock(*bitmap_);
|
||||
callback_.Run(rect, gfx::Size(bitmap_->width(), bitmap_->height()),
|
||||
bitmap_->getPixels());
|
||||
}
|
||||
|
||||
} // namespace atom
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) 2016 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_OSR_OSR_OUTPUT_DEVICE_H_
|
||||
#define ATOM_BROWSER_OSR_OSR_OUTPUT_DEVICE_H_
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "cc/output/software_output_device.h"
|
||||
#include "third_party/skia/include/core/SkBitmap.h"
|
||||
#include "third_party/skia/include/core/SkCanvas.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
typedef base::Callback<void(const gfx::Rect&,
|
||||
const gfx::Size&, void*)> OnPaintCallback;
|
||||
|
||||
class OffScreenOutputDevice : public cc::SoftwareOutputDevice {
|
||||
public:
|
||||
OffScreenOutputDevice(bool transparent, const OnPaintCallback& callback);
|
||||
~OffScreenOutputDevice();
|
||||
|
||||
// cc::SoftwareOutputDevice:
|
||||
void Resize(const gfx::Size& pixel_size, float scale_factor) override;
|
||||
SkCanvas* BeginPaint(const gfx::Rect& damage_rect) override;
|
||||
void EndPaint() override;
|
||||
|
||||
void SetActive(bool active);
|
||||
void OnPaint(const gfx::Rect& damage_rect);
|
||||
|
||||
private:
|
||||
const bool transparent_;
|
||||
OnPaintCallback callback_;
|
||||
|
||||
bool active_;
|
||||
|
||||
std::unique_ptr<SkCanvas> canvas_;
|
||||
std::unique_ptr<SkBitmap> bitmap_;
|
||||
gfx::Rect pending_damage_rect_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(OffScreenOutputDevice);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_OSR_OSR_OUTPUT_DEVICE_H_
|
|
@ -0,0 +1,870 @@
|
|||
// Copyright (c) 2016 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/osr/osr_render_widget_host_view.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/callback_helpers.h"
|
||||
#include "base/location.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/single_thread_task_runner.h"
|
||||
#include "base/time/time.h"
|
||||
#include "cc/output/copy_output_request.h"
|
||||
#include "cc/scheduler/delay_based_time_source.h"
|
||||
#include "components/display_compositor/gl_helper.h"
|
||||
#include "content/browser/renderer_host/render_widget_host_impl.h"
|
||||
#include "content/browser/renderer_host/render_widget_host_delegate.h"
|
||||
#include "content/common/view_messages.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/context_factory.h"
|
||||
#include "content/public/browser/render_widget_host_view_frame_subscriber.h"
|
||||
#include "ui/compositor/layer.h"
|
||||
#include "ui/compositor/layer_type.h"
|
||||
#include "ui/events/latency_info.h"
|
||||
#include "ui/gfx/geometry/dip_util.h"
|
||||
#include "ui/gfx/native_widget_types.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
const float kDefaultScaleFactor = 1.0;
|
||||
const int kFrameRetryLimit = 2;
|
||||
|
||||
} // namespace
|
||||
|
||||
class AtomCopyFrameGenerator {
|
||||
public:
|
||||
AtomCopyFrameGenerator(int frame_rate_threshold_ms,
|
||||
OffScreenRenderWidgetHostView* view)
|
||||
: frame_rate_threshold_ms_(frame_rate_threshold_ms),
|
||||
view_(view),
|
||||
frame_pending_(false),
|
||||
frame_in_progress_(false),
|
||||
frame_retry_count_(0),
|
||||
weak_ptr_factory_(this) {
|
||||
last_time_ = base::Time::Now();
|
||||
}
|
||||
|
||||
void GenerateCopyFrame(
|
||||
bool force_frame,
|
||||
const gfx::Rect& damage_rect) {
|
||||
if (force_frame && !frame_pending_)
|
||||
frame_pending_ = true;
|
||||
|
||||
if (!frame_pending_)
|
||||
return;
|
||||
|
||||
if (!damage_rect.IsEmpty())
|
||||
pending_damage_rect_.Union(damage_rect);
|
||||
|
||||
if (frame_in_progress_)
|
||||
return;
|
||||
|
||||
frame_in_progress_ = true;
|
||||
|
||||
const int64_t frame_rate_delta =
|
||||
(base::TimeTicks::Now() - frame_start_time_).InMilliseconds();
|
||||
if (frame_rate_delta < frame_rate_threshold_ms_) {
|
||||
content::BrowserThread::PostDelayedTask(content::BrowserThread::UI,
|
||||
FROM_HERE,
|
||||
base::Bind(&AtomCopyFrameGenerator::InternalGenerateCopyFrame,
|
||||
weak_ptr_factory_.GetWeakPtr()), base::TimeDelta::FromMilliseconds(
|
||||
frame_rate_threshold_ms_ - frame_rate_delta));
|
||||
return;
|
||||
}
|
||||
|
||||
InternalGenerateCopyFrame();
|
||||
}
|
||||
|
||||
bool frame_pending() const { return frame_pending_; }
|
||||
|
||||
void set_frame_rate_threshold_ms(int frame_rate_threshold_ms) {
|
||||
frame_rate_threshold_ms_ = frame_rate_threshold_ms;
|
||||
}
|
||||
|
||||
private:
|
||||
void InternalGenerateCopyFrame() {
|
||||
frame_pending_ = false;
|
||||
frame_start_time_ = base::TimeTicks::Now();
|
||||
|
||||
if (!view_->render_widget_host())
|
||||
return;
|
||||
|
||||
const gfx::Rect damage_rect = pending_damage_rect_;
|
||||
pending_damage_rect_.SetRect(0, 0, 0, 0);
|
||||
|
||||
std::unique_ptr<cc::CopyOutputRequest> request =
|
||||
cc::CopyOutputRequest::CreateRequest(base::Bind(
|
||||
&AtomCopyFrameGenerator::CopyFromCompositingSurfaceHasResult,
|
||||
weak_ptr_factory_.GetWeakPtr(),
|
||||
damage_rect));
|
||||
|
||||
request->set_area(gfx::Rect(view_->GetPhysicalBackingSize()));
|
||||
|
||||
view_->DelegatedFrameHostGetLayer()->RequestCopyOfOutput(
|
||||
std::move(request));
|
||||
}
|
||||
|
||||
void CopyFromCompositingSurfaceHasResult(
|
||||
const gfx::Rect& damage_rect,
|
||||
std::unique_ptr<cc::CopyOutputResult> result) {
|
||||
if (result->IsEmpty() || result->size().IsEmpty() ||
|
||||
!view_->render_widget_host()) {
|
||||
OnCopyFrameCaptureFailure(damage_rect);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result->HasTexture()) {
|
||||
PrepareTextureCopyOutputResult(damage_rect, std::move(result));
|
||||
return;
|
||||
}
|
||||
|
||||
DCHECK(result->HasBitmap());
|
||||
PrepareBitmapCopyOutputResult(damage_rect, std::move(result));
|
||||
}
|
||||
|
||||
void PrepareTextureCopyOutputResult(
|
||||
const gfx::Rect& damage_rect,
|
||||
std::unique_ptr<cc::CopyOutputResult> result) {
|
||||
DCHECK(result->HasTexture());
|
||||
base::ScopedClosureRunner scoped_callback_runner(
|
||||
base::Bind(&AtomCopyFrameGenerator::OnCopyFrameCaptureFailure,
|
||||
weak_ptr_factory_.GetWeakPtr(),
|
||||
damage_rect));
|
||||
|
||||
const gfx::Size& result_size = result->size();
|
||||
SkIRect bitmap_size;
|
||||
if (bitmap_)
|
||||
bitmap_->getBounds(&bitmap_size);
|
||||
|
||||
if (!bitmap_ ||
|
||||
bitmap_size.width() != result_size.width() ||
|
||||
bitmap_size.height() != result_size.height()) {
|
||||
bitmap_.reset(new SkBitmap);
|
||||
bitmap_->allocN32Pixels(result_size.width(),
|
||||
result_size.height(),
|
||||
true);
|
||||
if (bitmap_->drawsNothing())
|
||||
return;
|
||||
}
|
||||
|
||||
content::ImageTransportFactory* factory =
|
||||
content::ImageTransportFactory::GetInstance();
|
||||
display_compositor::GLHelper* gl_helper = factory->GetGLHelper();
|
||||
if (!gl_helper)
|
||||
return;
|
||||
|
||||
std::unique_ptr<SkAutoLockPixels> bitmap_pixels_lock(
|
||||
new SkAutoLockPixels(*bitmap_));
|
||||
uint8_t* pixels = static_cast<uint8_t*>(bitmap_->getPixels());
|
||||
|
||||
cc::TextureMailbox texture_mailbox;
|
||||
std::unique_ptr<cc::SingleReleaseCallback> release_callback;
|
||||
result->TakeTexture(&texture_mailbox, &release_callback);
|
||||
DCHECK(texture_mailbox.IsTexture());
|
||||
if (!texture_mailbox.IsTexture())
|
||||
return;
|
||||
|
||||
ignore_result(scoped_callback_runner.Release());
|
||||
|
||||
gl_helper->CropScaleReadbackAndCleanMailbox(
|
||||
texture_mailbox.mailbox(),
|
||||
texture_mailbox.sync_token(),
|
||||
result_size,
|
||||
gfx::Rect(result_size),
|
||||
result_size,
|
||||
pixels,
|
||||
kN32_SkColorType,
|
||||
base::Bind(
|
||||
&AtomCopyFrameGenerator::CopyFromCompositingSurfaceFinishedProxy,
|
||||
weak_ptr_factory_.GetWeakPtr(),
|
||||
base::Passed(&release_callback),
|
||||
damage_rect,
|
||||
base::Passed(&bitmap_),
|
||||
base::Passed(&bitmap_pixels_lock)),
|
||||
display_compositor::GLHelper::SCALER_QUALITY_FAST);
|
||||
}
|
||||
|
||||
static void CopyFromCompositingSurfaceFinishedProxy(
|
||||
base::WeakPtr<AtomCopyFrameGenerator> generator,
|
||||
std::unique_ptr<cc::SingleReleaseCallback> release_callback,
|
||||
const gfx::Rect& damage_rect,
|
||||
std::unique_ptr<SkBitmap> bitmap,
|
||||
std::unique_ptr<SkAutoLockPixels> bitmap_pixels_lock,
|
||||
bool result) {
|
||||
gpu::SyncToken sync_token;
|
||||
if (result) {
|
||||
display_compositor::GLHelper* gl_helper =
|
||||
content::ImageTransportFactory::GetInstance()->GetGLHelper();
|
||||
if (gl_helper)
|
||||
gl_helper->GenerateSyncToken(&sync_token);
|
||||
}
|
||||
const bool lost_resource = !sync_token.HasData();
|
||||
release_callback->Run(sync_token, lost_resource);
|
||||
|
||||
if (generator) {
|
||||
generator->CopyFromCompositingSurfaceFinished(
|
||||
damage_rect, std::move(bitmap), std::move(bitmap_pixels_lock),
|
||||
result);
|
||||
} else {
|
||||
bitmap_pixels_lock.reset();
|
||||
bitmap.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void CopyFromCompositingSurfaceFinished(
|
||||
const gfx::Rect& damage_rect,
|
||||
std::unique_ptr<SkBitmap> bitmap,
|
||||
std::unique_ptr<SkAutoLockPixels> bitmap_pixels_lock,
|
||||
bool result) {
|
||||
DCHECK(!bitmap_);
|
||||
bitmap_ = std::move(bitmap);
|
||||
|
||||
if (result) {
|
||||
OnCopyFrameCaptureSuccess(damage_rect, *bitmap_,
|
||||
std::move(bitmap_pixels_lock));
|
||||
} else {
|
||||
bitmap_pixels_lock.reset();
|
||||
OnCopyFrameCaptureFailure(damage_rect);
|
||||
}
|
||||
}
|
||||
|
||||
void PrepareBitmapCopyOutputResult(
|
||||
const gfx::Rect& damage_rect,
|
||||
std::unique_ptr<cc::CopyOutputResult> result) {
|
||||
DCHECK(result->HasBitmap());
|
||||
std::unique_ptr<SkBitmap> source = result->TakeBitmap();
|
||||
DCHECK(source);
|
||||
if (source) {
|
||||
std::unique_ptr<SkAutoLockPixels> bitmap_pixels_lock(
|
||||
new SkAutoLockPixels(*source));
|
||||
OnCopyFrameCaptureSuccess(damage_rect, *source,
|
||||
std::move(bitmap_pixels_lock));
|
||||
} else {
|
||||
OnCopyFrameCaptureFailure(damage_rect);
|
||||
}
|
||||
}
|
||||
|
||||
void OnCopyFrameCaptureFailure(
|
||||
const gfx::Rect& damage_rect) {
|
||||
pending_damage_rect_.Union(damage_rect);
|
||||
|
||||
const bool force_frame = (++frame_retry_count_ <= kFrameRetryLimit);
|
||||
OnCopyFrameCaptureCompletion(force_frame);
|
||||
}
|
||||
|
||||
void OnCopyFrameCaptureSuccess(
|
||||
const gfx::Rect& damage_rect,
|
||||
const SkBitmap& bitmap,
|
||||
std::unique_ptr<SkAutoLockPixels> bitmap_pixels_lock) {
|
||||
view_->OnPaint(damage_rect,
|
||||
gfx::Size(bitmap.width(), bitmap.height()),
|
||||
bitmap.getPixels());
|
||||
|
||||
if (frame_retry_count_ > 0)
|
||||
frame_retry_count_ = 0;
|
||||
|
||||
OnCopyFrameCaptureCompletion(false);
|
||||
}
|
||||
|
||||
void OnCopyFrameCaptureCompletion(bool force_frame) {
|
||||
frame_in_progress_ = false;
|
||||
|
||||
if (frame_pending_) {
|
||||
content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
|
||||
base::Bind(&AtomCopyFrameGenerator::GenerateCopyFrame,
|
||||
weak_ptr_factory_.GetWeakPtr(),
|
||||
force_frame,
|
||||
gfx::Rect()));
|
||||
}
|
||||
}
|
||||
|
||||
int frame_rate_threshold_ms_;
|
||||
OffScreenRenderWidgetHostView* view_;
|
||||
|
||||
base::Time last_time_;
|
||||
|
||||
base::TimeTicks frame_start_time_;
|
||||
bool frame_pending_;
|
||||
bool frame_in_progress_;
|
||||
int frame_retry_count_;
|
||||
std::unique_ptr<SkBitmap> bitmap_;
|
||||
gfx::Rect pending_damage_rect_;
|
||||
|
||||
base::WeakPtrFactory<AtomCopyFrameGenerator> weak_ptr_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AtomCopyFrameGenerator);
|
||||
};
|
||||
|
||||
class AtomBeginFrameTimer : public cc::DelayBasedTimeSourceClient {
|
||||
public:
|
||||
AtomBeginFrameTimer(int frame_rate_threshold_ms,
|
||||
const base::Closure& callback)
|
||||
: callback_(callback) {
|
||||
time_source_ = cc::DelayBasedTimeSource::Create(
|
||||
base::TimeDelta::FromMilliseconds(frame_rate_threshold_ms),
|
||||
content::BrowserThread::GetMessageLoopProxyForThread(
|
||||
content::BrowserThread::UI).get());
|
||||
time_source_->SetClient(this);
|
||||
}
|
||||
|
||||
void SetActive(bool active) {
|
||||
time_source_->SetActive(active);
|
||||
}
|
||||
|
||||
bool IsActive() const {
|
||||
return time_source_->Active();
|
||||
}
|
||||
|
||||
void SetFrameRateThresholdMs(int frame_rate_threshold_ms) {
|
||||
time_source_->SetTimebaseAndInterval(
|
||||
base::TimeTicks::Now(),
|
||||
base::TimeDelta::FromMilliseconds(frame_rate_threshold_ms));
|
||||
}
|
||||
|
||||
private:
|
||||
void OnTimerTick() override {
|
||||
callback_.Run();
|
||||
}
|
||||
|
||||
const base::Closure callback_;
|
||||
std::unique_ptr<cc::DelayBasedTimeSource> time_source_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AtomBeginFrameTimer);
|
||||
};
|
||||
|
||||
OffScreenRenderWidgetHostView::OffScreenRenderWidgetHostView(
|
||||
bool transparent,
|
||||
content::RenderWidgetHost* host,
|
||||
NativeWindow* native_window)
|
||||
: render_widget_host_(content::RenderWidgetHostImpl::From(host)),
|
||||
native_window_(native_window),
|
||||
software_output_device_(nullptr),
|
||||
frame_rate_(60),
|
||||
frame_rate_threshold_ms_(0),
|
||||
last_time_(base::Time::Now()),
|
||||
transparent_(transparent),
|
||||
scale_factor_(kDefaultScaleFactor),
|
||||
is_showing_(!render_widget_host_->is_hidden()),
|
||||
size_(native_window->GetSize()),
|
||||
painting_(true),
|
||||
root_layer_(new ui::Layer(ui::LAYER_SOLID_COLOR)),
|
||||
delegated_frame_host_(new content::DelegatedFrameHost(this)),
|
||||
weak_ptr_factory_(this) {
|
||||
DCHECK(render_widget_host_);
|
||||
render_widget_host_->SetView(this);
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
CreatePlatformWidget();
|
||||
#else
|
||||
compositor_.reset(
|
||||
new ui::Compositor(content::GetContextFactory(),
|
||||
base::ThreadTaskRunnerHandle::Get()));
|
||||
compositor_->SetAcceleratedWidget(native_window_->GetAcceleratedWidget());
|
||||
#endif
|
||||
compositor_->SetDelegate(this);
|
||||
compositor_->SetRootLayer(root_layer_.get());
|
||||
|
||||
ResizeRootLayer();
|
||||
}
|
||||
|
||||
OffScreenRenderWidgetHostView::~OffScreenRenderWidgetHostView() {
|
||||
if (is_showing_)
|
||||
delegated_frame_host_->WasHidden();
|
||||
delegated_frame_host_->ResetCompositor();
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
DestroyPlatformWidget();
|
||||
#endif
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::OnBeginFrameTimerTick() {
|
||||
const base::TimeTicks frame_time = base::TimeTicks::Now();
|
||||
const base::TimeDelta vsync_period =
|
||||
base::TimeDelta::FromMilliseconds(frame_rate_threshold_ms_);
|
||||
SendBeginFrame(frame_time, vsync_period);
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::SendBeginFrame(
|
||||
base::TimeTicks frame_time, base::TimeDelta vsync_period) {
|
||||
base::TimeTicks display_time = frame_time + vsync_period;
|
||||
|
||||
base::TimeDelta estimated_browser_composite_time =
|
||||
base::TimeDelta::FromMicroseconds(
|
||||
(1.0f * base::Time::kMicrosecondsPerSecond) / (3.0f * 60));
|
||||
|
||||
base::TimeTicks deadline = display_time - estimated_browser_composite_time;
|
||||
|
||||
render_widget_host_->Send(new ViewMsg_BeginFrame(
|
||||
render_widget_host_->GetRoutingID(),
|
||||
cc::BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, deadline,
|
||||
vsync_period, cc::BeginFrameArgs::NORMAL)));
|
||||
}
|
||||
|
||||
bool OffScreenRenderWidgetHostView::OnMessageReceived(
|
||||
const IPC::Message& message) {
|
||||
bool handled = true;
|
||||
IPC_BEGIN_MESSAGE_MAP(OffScreenRenderWidgetHostView, message)
|
||||
IPC_MESSAGE_HANDLER(ViewHostMsg_SetNeedsBeginFrames,
|
||||
OnSetNeedsBeginFrames)
|
||||
IPC_MESSAGE_UNHANDLED(handled = false)
|
||||
IPC_END_MESSAGE_MAP()
|
||||
|
||||
if (!handled)
|
||||
return content::RenderWidgetHostViewBase::OnMessageReceived(message);
|
||||
return handled;
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::InitAsChild(gfx::NativeView) {
|
||||
}
|
||||
|
||||
content::RenderWidgetHost* OffScreenRenderWidgetHostView::GetRenderWidgetHost()
|
||||
const {
|
||||
return render_widget_host_;
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::SetSize(const gfx::Size& size) {
|
||||
size_ = size;
|
||||
|
||||
const gfx::Size& size_in_pixels =
|
||||
gfx::ConvertSizeToPixel(scale_factor_, size);
|
||||
|
||||
root_layer_->SetBounds(gfx::Rect(size));
|
||||
compositor_->SetScaleAndSize(scale_factor_, size_in_pixels);
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::SetBounds(const gfx::Rect& new_bounds) {
|
||||
}
|
||||
|
||||
gfx::Vector2dF OffScreenRenderWidgetHostView::GetLastScrollOffset() const {
|
||||
return last_scroll_offset_;
|
||||
}
|
||||
|
||||
gfx::NativeView OffScreenRenderWidgetHostView::GetNativeView() const {
|
||||
return gfx::NativeView();
|
||||
}
|
||||
|
||||
gfx::NativeViewAccessible
|
||||
OffScreenRenderWidgetHostView::GetNativeViewAccessible() {
|
||||
return gfx::NativeViewAccessible();
|
||||
}
|
||||
|
||||
ui::TextInputClient* OffScreenRenderWidgetHostView::GetTextInputClient() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::Focus() {
|
||||
}
|
||||
|
||||
bool OffScreenRenderWidgetHostView::HasFocus() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OffScreenRenderWidgetHostView::IsSurfaceAvailableForCopy() const {
|
||||
return delegated_frame_host_->CanCopyToBitmap();
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::Show() {
|
||||
if (is_showing_)
|
||||
return;
|
||||
|
||||
is_showing_ = true;
|
||||
if (render_widget_host_)
|
||||
render_widget_host_->WasShown(ui::LatencyInfo());
|
||||
delegated_frame_host_->SetCompositor(compositor_.get());
|
||||
delegated_frame_host_->WasShown(ui::LatencyInfo());
|
||||
compositor_->ScheduleFullRedraw();
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::Hide() {
|
||||
if (!is_showing_)
|
||||
return;
|
||||
|
||||
if (render_widget_host_)
|
||||
render_widget_host_->WasHidden();
|
||||
delegated_frame_host_->WasHidden();
|
||||
delegated_frame_host_->ResetCompositor();
|
||||
is_showing_ = false;
|
||||
}
|
||||
|
||||
bool OffScreenRenderWidgetHostView::IsShowing() {
|
||||
return is_showing_;
|
||||
}
|
||||
|
||||
gfx::Rect OffScreenRenderWidgetHostView::GetViewBounds() const {
|
||||
return gfx::Rect(size_);
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::SetBackgroundColor(SkColor color) {
|
||||
if (transparent_)
|
||||
color = SkColorSetARGB(SK_AlphaTRANSPARENT, 0, 0, 0);
|
||||
|
||||
content::RenderWidgetHostViewBase::SetBackgroundColor(color);
|
||||
|
||||
const bool opaque = !transparent_ && GetBackgroundOpaque();
|
||||
if (render_widget_host_)
|
||||
render_widget_host_->SetBackgroundOpaque(opaque);
|
||||
}
|
||||
|
||||
gfx::Size OffScreenRenderWidgetHostView::GetVisibleViewportSize() const {
|
||||
return size_;
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::SetInsets(const gfx::Insets& insets) {
|
||||
}
|
||||
|
||||
bool OffScreenRenderWidgetHostView::LockMouse() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::UnlockMouse() {
|
||||
}
|
||||
|
||||
bool OffScreenRenderWidgetHostView::GetScreenColorProfile(std::vector<char>*) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::OnSwapCompositorFrame(
|
||||
uint32_t output_surface_id,
|
||||
std::unique_ptr<cc::CompositorFrame> frame) {
|
||||
TRACE_EVENT0("electron",
|
||||
"OffScreenRenderWidgetHostView::OnSwapCompositorFrame");
|
||||
|
||||
if (frame->metadata.root_scroll_offset != last_scroll_offset_) {
|
||||
last_scroll_offset_ = frame->metadata.root_scroll_offset;
|
||||
}
|
||||
|
||||
if (frame->delegated_frame_data) {
|
||||
if (software_output_device_) {
|
||||
if (!begin_frame_timer_.get()) {
|
||||
software_output_device_->SetActive(true);
|
||||
}
|
||||
|
||||
delegated_frame_host_->SwapDelegatedFrame(output_surface_id,
|
||||
std::move(frame));
|
||||
} else {
|
||||
if (!copy_frame_generator_.get()) {
|
||||
copy_frame_generator_.reset(
|
||||
new AtomCopyFrameGenerator(frame_rate_threshold_ms_, this));
|
||||
}
|
||||
|
||||
cc::RenderPass* root_pass =
|
||||
frame->delegated_frame_data->render_pass_list.back().get();
|
||||
gfx::Size frame_size = root_pass->output_rect.size();
|
||||
gfx::Rect damage_rect =
|
||||
gfx::ToEnclosingRect(gfx::RectF(root_pass->damage_rect));
|
||||
damage_rect.Intersect(gfx::Rect(frame_size));
|
||||
|
||||
delegated_frame_host_->SwapDelegatedFrame(output_surface_id,
|
||||
std::move(frame));
|
||||
|
||||
if (painting_)
|
||||
copy_frame_generator_->GenerateCopyFrame(true, damage_rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::ClearCompositorFrame() {
|
||||
delegated_frame_host_->ClearDelegatedFrame();
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::InitAsPopup(
|
||||
content::RenderWidgetHostView* parent_host_view, const gfx::Rect& pos) {
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::InitAsFullscreen(
|
||||
content::RenderWidgetHostView *) {
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::UpdateCursor(const content::WebCursor &) {
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::SetIsLoading(bool loading) {
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::TextInputStateChanged(
|
||||
const content::TextInputState& params) {
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::ImeCancelComposition() {
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::RenderProcessGone(base::TerminationStatus,
|
||||
int) {
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::Destroy() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::SetTooltipText(const base::string16 &) {
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::SelectionBoundsChanged(
|
||||
const ViewHostMsg_SelectionBounds_Params &) {
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::CopyFromCompositingSurface(
|
||||
const gfx::Rect& src_subrect,
|
||||
const gfx::Size& dst_size,
|
||||
const content::ReadbackRequestCallback& callback,
|
||||
const SkColorType preferred_color_type) {
|
||||
delegated_frame_host_->CopyFromCompositingSurface(
|
||||
src_subrect, dst_size, callback, preferred_color_type);
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::CopyFromCompositingSurfaceToVideoFrame(
|
||||
const gfx::Rect& src_subrect,
|
||||
const scoped_refptr<media::VideoFrame>& target,
|
||||
const base::Callback<void(const gfx::Rect&, bool)>& callback) {
|
||||
delegated_frame_host_->CopyFromCompositingSurfaceToVideoFrame(
|
||||
src_subrect, target, callback);
|
||||
}
|
||||
|
||||
bool OffScreenRenderWidgetHostView::CanCopyToVideoFrame() const {
|
||||
return delegated_frame_host_->CanCopyToVideoFrame();
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::BeginFrameSubscription(
|
||||
std::unique_ptr<content::RenderWidgetHostViewFrameSubscriber> subscriber) {
|
||||
delegated_frame_host_->BeginFrameSubscription(std::move(subscriber));
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::EndFrameSubscription() {
|
||||
delegated_frame_host_->EndFrameSubscription();
|
||||
}
|
||||
|
||||
bool OffScreenRenderWidgetHostView::HasAcceleratedSurface(const gfx::Size &) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::GetScreenInfo(
|
||||
blink::WebScreenInfo* results) {
|
||||
results->rect = gfx::Rect(size_);
|
||||
results->availableRect = gfx::Rect(size_);
|
||||
results->depth = 24;
|
||||
results->depthPerComponent = 8;
|
||||
results->deviceScaleFactor = scale_factor_;
|
||||
results->orientationAngle = 0;
|
||||
results->orientationType = blink::WebScreenOrientationLandscapePrimary;
|
||||
}
|
||||
|
||||
bool OffScreenRenderWidgetHostView::GetScreenColorProfile(
|
||||
blink::WebVector<char>*) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gfx::Rect OffScreenRenderWidgetHostView::GetBoundsInRootWindow() {
|
||||
return gfx::Rect(size_);
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::LockCompositingSurface() {
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::UnlockCompositingSurface() {
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::ImeCompositionRangeChanged(
|
||||
const gfx::Range &, const std::vector<gfx::Rect>&) {
|
||||
}
|
||||
|
||||
gfx::Size OffScreenRenderWidgetHostView::GetPhysicalBackingSize() const {
|
||||
return size_;
|
||||
}
|
||||
|
||||
gfx::Size OffScreenRenderWidgetHostView::GetRequestedRendererSize() const {
|
||||
return size_;
|
||||
}
|
||||
|
||||
int OffScreenRenderWidgetHostView::
|
||||
DelegatedFrameHostGetGpuMemoryBufferClientId()
|
||||
const {
|
||||
return render_widget_host_->GetProcess()->GetID();
|
||||
}
|
||||
|
||||
ui::Layer* OffScreenRenderWidgetHostView::DelegatedFrameHostGetLayer() const {
|
||||
return const_cast<ui::Layer*>(root_layer_.get());
|
||||
}
|
||||
|
||||
bool OffScreenRenderWidgetHostView::DelegatedFrameHostIsVisible() const {
|
||||
return !render_widget_host_->is_hidden();
|
||||
}
|
||||
|
||||
SkColor OffScreenRenderWidgetHostView::DelegatedFrameHostGetGutterColor(
|
||||
SkColor color) const {
|
||||
return color;
|
||||
}
|
||||
|
||||
gfx::Size OffScreenRenderWidgetHostView::DelegatedFrameHostDesiredSizeInDIP()
|
||||
const {
|
||||
return size_;
|
||||
}
|
||||
|
||||
bool OffScreenRenderWidgetHostView::DelegatedFrameCanCreateResizeLock() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<content::ResizeLock>
|
||||
OffScreenRenderWidgetHostView::DelegatedFrameHostCreateResizeLock(
|
||||
bool defer_compositor_lock) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::DelegatedFrameHostResizeLockWasReleased() {
|
||||
return render_widget_host_->WasResized();
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::DelegatedFrameHostSendCompositorSwapAck(
|
||||
int output_surface_id, const cc::CompositorFrameAck& ack) {
|
||||
render_widget_host_->Send(new ViewMsg_SwapCompositorFrameAck(
|
||||
render_widget_host_->GetRoutingID(),
|
||||
output_surface_id, ack));
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::
|
||||
DelegatedFrameHostSendReclaimCompositorResources(
|
||||
int output_surface_id, const cc::CompositorFrameAck& ack) {
|
||||
render_widget_host_->Send(new ViewMsg_ReclaimCompositorResources(
|
||||
render_widget_host_->GetRoutingID(),
|
||||
output_surface_id, ack));
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::
|
||||
DelegatedFrameHostOnLostCompositorResources() {
|
||||
render_widget_host_->ScheduleComposite();
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::DelegatedFrameHostUpdateVSyncParameters(
|
||||
const base::TimeTicks& timebase, const base::TimeDelta& interval) {
|
||||
render_widget_host_->UpdateVSyncParameters(timebase, interval);
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::SetBeginFrameSource(
|
||||
cc::BeginFrameSource* source) {
|
||||
}
|
||||
|
||||
std::unique_ptr<cc::SoftwareOutputDevice>
|
||||
OffScreenRenderWidgetHostView::CreateSoftwareOutputDevice(
|
||||
ui::Compositor* compositor) {
|
||||
DCHECK_EQ(compositor_.get(), compositor);
|
||||
DCHECK(!copy_frame_generator_);
|
||||
DCHECK(!software_output_device_);
|
||||
|
||||
software_output_device_ = new OffScreenOutputDevice(transparent_,
|
||||
base::Bind(&OffScreenRenderWidgetHostView::OnPaint,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
return base::WrapUnique(software_output_device_);
|
||||
}
|
||||
|
||||
bool OffScreenRenderWidgetHostView::InstallTransparency() {
|
||||
if (transparent_) {
|
||||
SetBackgroundColor(SkColor());
|
||||
compositor_->SetHostHasTransparentBackground(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OffScreenRenderWidgetHostView::IsAutoResizeEnabled() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::OnSetNeedsBeginFrames(bool enabled) {
|
||||
SetupFrameRate(false);
|
||||
|
||||
begin_frame_timer_->SetActive(enabled);
|
||||
|
||||
if (software_output_device_) {
|
||||
software_output_device_->SetActive(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::SetPaintCallback(
|
||||
const OnPaintCallback& callback) {
|
||||
callback_ = callback;
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::OnPaint(
|
||||
const gfx::Rect& damage_rect,
|
||||
const gfx::Size& bitmap_size,
|
||||
void* bitmap_pixels) {
|
||||
TRACE_EVENT0("electron", "OffScreenRenderWidgetHostView::OnPaint");
|
||||
|
||||
if (!callback_.is_null())
|
||||
callback_.Run(damage_rect, bitmap_size, bitmap_pixels);
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::SetPainting(bool painting) {
|
||||
painting_ = painting;
|
||||
|
||||
if (software_output_device_) {
|
||||
software_output_device_->SetActive(painting);
|
||||
}
|
||||
}
|
||||
|
||||
bool OffScreenRenderWidgetHostView::IsPainting() const {
|
||||
return painting_;
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::SetFrameRate(int frame_rate) {
|
||||
if (frame_rate <= 0)
|
||||
frame_rate = 1;
|
||||
if (frame_rate > 60)
|
||||
frame_rate = 60;
|
||||
|
||||
frame_rate_ = frame_rate;
|
||||
|
||||
SetupFrameRate(true);
|
||||
}
|
||||
|
||||
int OffScreenRenderWidgetHostView::GetFrameRate() const {
|
||||
return frame_rate_;
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::SetupFrameRate(bool force) {
|
||||
if (!force && frame_rate_threshold_ms_ != 0)
|
||||
return;
|
||||
|
||||
frame_rate_threshold_ms_ = 1000 / frame_rate_;
|
||||
|
||||
compositor_->vsync_manager()->SetAuthoritativeVSyncInterval(
|
||||
base::TimeDelta::FromMilliseconds(frame_rate_threshold_ms_));
|
||||
|
||||
if (copy_frame_generator_.get()) {
|
||||
copy_frame_generator_->set_frame_rate_threshold_ms(
|
||||
frame_rate_threshold_ms_);
|
||||
}
|
||||
|
||||
if (begin_frame_timer_.get()) {
|
||||
begin_frame_timer_->SetFrameRateThresholdMs(frame_rate_threshold_ms_);
|
||||
} else {
|
||||
begin_frame_timer_.reset(new AtomBeginFrameTimer(
|
||||
frame_rate_threshold_ms_,
|
||||
base::Bind(&OffScreenRenderWidgetHostView::OnBeginFrameTimerTick,
|
||||
weak_ptr_factory_.GetWeakPtr())));
|
||||
}
|
||||
}
|
||||
|
||||
void OffScreenRenderWidgetHostView::ResizeRootLayer() {
|
||||
SetupFrameRate(false);
|
||||
|
||||
const float orgScaleFactor = scale_factor_;
|
||||
const bool scaleFactorDidChange = (orgScaleFactor != scale_factor_);
|
||||
|
||||
gfx::Size size = GetViewBounds().size();
|
||||
|
||||
if (!scaleFactorDidChange && size == root_layer_->bounds().size())
|
||||
return;
|
||||
|
||||
const gfx::Size& size_in_pixels =
|
||||
gfx::ConvertSizeToPixel(scale_factor_, size);
|
||||
|
||||
root_layer_->SetBounds(gfx::Rect(size));
|
||||
compositor_->SetScaleAndSize(scale_factor_, size_in_pixels);
|
||||
}
|
||||
|
||||
} // namespace atom
|
|
@ -0,0 +1,253 @@
|
|||
// Copyright (c) 2016 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_OSR_OSR_RENDER_WIDGET_HOST_VIEW_H_
|
||||
#define ATOM_BROWSER_OSR_OSR_RENDER_WIDGET_HOST_VIEW_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "atom/browser/native_window.h"
|
||||
#include "atom/browser/osr/osr_output_device.h"
|
||||
#include "base/process/kill.h"
|
||||
#include "base/threading/thread.h"
|
||||
#include "base/time/time.h"
|
||||
#include "cc/scheduler/begin_frame_source.h"
|
||||
#include "cc/output/compositor_frame.h"
|
||||
#include "content/browser/renderer_host/delegated_frame_host.h"
|
||||
#include "content/browser/renderer_host/render_widget_host_impl.h"
|
||||
#include "content/browser/renderer_host/render_widget_host_view_base.h"
|
||||
#include "content/browser/renderer_host/resize_lock.h"
|
||||
#include "ui/compositor/compositor.h"
|
||||
#include "ui/compositor/layer_delegate.h"
|
||||
#include "ui/compositor/layer_owner.h"
|
||||
#include "ui/base/ime/text_input_client.h"
|
||||
#include "ui/gfx/geometry/point.h"
|
||||
#include "third_party/WebKit/public/platform/WebVector.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "ui/gfx/win/window_impl.h"
|
||||
#endif
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
#include "content/browser/renderer_host/browser_compositor_view_mac.h"
|
||||
#include "ui/accelerated_widget_mac/accelerated_widget_mac.h"
|
||||
#endif
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
#ifdef __OBJC__
|
||||
@class CALayer;
|
||||
@class NSWindow;
|
||||
#else
|
||||
class CALayer;
|
||||
class NSWindow;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace atom {
|
||||
|
||||
class AtomCopyFrameGenerator;
|
||||
class AtomBeginFrameTimer;
|
||||
|
||||
class OffScreenRenderWidgetHostView
|
||||
: public content::RenderWidgetHostViewBase,
|
||||
#if defined(OS_MACOSX)
|
||||
public ui::AcceleratedWidgetMacNSView,
|
||||
#endif
|
||||
public ui::CompositorDelegate,
|
||||
public content::DelegatedFrameHostClient {
|
||||
public:
|
||||
OffScreenRenderWidgetHostView(bool transparent,
|
||||
content::RenderWidgetHost* render_widget_host,
|
||||
NativeWindow* native_window);
|
||||
~OffScreenRenderWidgetHostView() override;
|
||||
|
||||
// content::RenderWidgetHostView:
|
||||
bool OnMessageReceived(const IPC::Message&) override;
|
||||
void InitAsChild(gfx::NativeView) override;
|
||||
content::RenderWidgetHost* GetRenderWidgetHost(void) const override;
|
||||
void SetSize(const gfx::Size &) override;
|
||||
void SetBounds(const gfx::Rect &) override;
|
||||
gfx::Vector2dF GetLastScrollOffset(void) const override;
|
||||
gfx::NativeView GetNativeView(void) const override;
|
||||
gfx::NativeViewAccessible GetNativeViewAccessible(void) override;
|
||||
ui::TextInputClient* GetTextInputClient() override;
|
||||
void Focus(void) override;
|
||||
bool HasFocus(void) const override;
|
||||
bool IsSurfaceAvailableForCopy(void) const override;
|
||||
void Show(void) override;
|
||||
void Hide(void) override;
|
||||
bool IsShowing(void) override;
|
||||
gfx::Rect GetViewBounds(void) const override;
|
||||
gfx::Size GetVisibleViewportSize() const override;
|
||||
void SetInsets(const gfx::Insets&) override;
|
||||
void SetBackgroundColor(SkColor color) override;
|
||||
bool LockMouse(void) override;
|
||||
void UnlockMouse(void) override;
|
||||
bool GetScreenColorProfile(std::vector<char>*) override;
|
||||
#if defined(OS_MACOSX)
|
||||
ui::AcceleratedWidgetMac* GetAcceleratedWidgetMac() const override;
|
||||
void SetActive(bool active) override;
|
||||
void ShowDefinitionForSelection() override;
|
||||
bool SupportsSpeech() const override;
|
||||
void SpeakSelection() override;
|
||||
bool IsSpeaking() const override;
|
||||
void StopSpeaking() override;
|
||||
#endif // defined(OS_MACOSX)
|
||||
|
||||
// content::RenderWidgetHostViewBase:
|
||||
void OnSwapCompositorFrame(uint32_t, std::unique_ptr<cc::CompositorFrame>)
|
||||
override;
|
||||
void ClearCompositorFrame(void) override;
|
||||
void InitAsPopup(content::RenderWidgetHostView *rwhv, const gfx::Rect& rect)
|
||||
override;
|
||||
void InitAsFullscreen(content::RenderWidgetHostView *) override;
|
||||
void UpdateCursor(const content::WebCursor &) override;
|
||||
void SetIsLoading(bool is_loading) override;
|
||||
void TextInputStateChanged(const content::TextInputState& params) override;
|
||||
void ImeCancelComposition(void) override;
|
||||
void RenderProcessGone(base::TerminationStatus, int) override;
|
||||
void Destroy(void) override;
|
||||
void SetTooltipText(const base::string16 &) override;
|
||||
#if defined(OS_MACOSX)
|
||||
void SelectionChanged(const base::string16& text,
|
||||
size_t offset,
|
||||
const gfx::Range& range) override;
|
||||
#endif
|
||||
void SelectionBoundsChanged(const ViewHostMsg_SelectionBounds_Params &)
|
||||
override;
|
||||
void CopyFromCompositingSurface(const gfx::Rect &,
|
||||
const gfx::Size &,
|
||||
const content::ReadbackRequestCallback &,
|
||||
const SkColorType) override;
|
||||
void CopyFromCompositingSurfaceToVideoFrame(
|
||||
const gfx::Rect &,
|
||||
const scoped_refptr<media::VideoFrame> &,
|
||||
const base::Callback<void(const gfx::Rect &, bool),
|
||||
base::internal::CopyMode::Copyable> &) override;
|
||||
bool CanCopyToVideoFrame(void) const override;
|
||||
void BeginFrameSubscription(
|
||||
std::unique_ptr<content::RenderWidgetHostViewFrameSubscriber>) override;
|
||||
void EndFrameSubscription() override;
|
||||
bool HasAcceleratedSurface(const gfx::Size &) override;
|
||||
void GetScreenInfo(blink::WebScreenInfo *) override;
|
||||
bool GetScreenColorProfile(blink::WebVector<char>*);
|
||||
gfx::Rect GetBoundsInRootWindow(void) override;
|
||||
void LockCompositingSurface(void) override;
|
||||
void UnlockCompositingSurface(void) override;
|
||||
void ImeCompositionRangeChanged(
|
||||
const gfx::Range &, const std::vector<gfx::Rect>&) override;
|
||||
gfx::Size GetPhysicalBackingSize() const override;
|
||||
gfx::Size GetRequestedRendererSize() const override;
|
||||
|
||||
// content::DelegatedFrameHostClient:
|
||||
int DelegatedFrameHostGetGpuMemoryBufferClientId(void) const;
|
||||
ui::Layer *DelegatedFrameHostGetLayer(void) const override;
|
||||
bool DelegatedFrameHostIsVisible(void) const override;
|
||||
SkColor DelegatedFrameHostGetGutterColor(SkColor) const override;
|
||||
gfx::Size DelegatedFrameHostDesiredSizeInDIP(void) const override;
|
||||
bool DelegatedFrameCanCreateResizeLock(void) const override;
|
||||
std::unique_ptr<content::ResizeLock> DelegatedFrameHostCreateResizeLock(
|
||||
bool defer_compositor_lock) override;
|
||||
void DelegatedFrameHostResizeLockWasReleased(void) override;
|
||||
void DelegatedFrameHostSendCompositorSwapAck(
|
||||
int, const cc::CompositorFrameAck &) override;
|
||||
void DelegatedFrameHostSendReclaimCompositorResources(
|
||||
int, const cc::CompositorFrameAck &) override;
|
||||
void DelegatedFrameHostOnLostCompositorResources(void) override;
|
||||
void DelegatedFrameHostUpdateVSyncParameters(
|
||||
const base::TimeTicks &, const base::TimeDelta &) override;
|
||||
void SetBeginFrameSource(cc::BeginFrameSource* source) override;
|
||||
|
||||
// ui::CompositorDelegate:
|
||||
std::unique_ptr<cc::SoftwareOutputDevice> CreateSoftwareOutputDevice(
|
||||
ui::Compositor* compositor) override;
|
||||
|
||||
bool InstallTransparency();
|
||||
bool IsAutoResizeEnabled() const;
|
||||
void OnSetNeedsBeginFrames(bool enabled);
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
// ui::AcceleratedWidgetMacNSView:
|
||||
NSView* AcceleratedWidgetGetNSView() const override;
|
||||
void AcceleratedWidgetGetVSyncParameters(
|
||||
base::TimeTicks* timebase, base::TimeDelta* interval) const override;
|
||||
void AcceleratedWidgetSwapCompleted() override;
|
||||
#endif // defined(OS_MACOSX)
|
||||
|
||||
void OnBeginFrameTimerTick();
|
||||
void SendBeginFrame(base::TimeTicks frame_time,
|
||||
base::TimeDelta vsync_period);
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
void CreatePlatformWidget();
|
||||
void DestroyPlatformWidget();
|
||||
#endif
|
||||
|
||||
void SetPaintCallback(const OnPaintCallback& callback);
|
||||
void OnPaint(const gfx::Rect& damage_rect,
|
||||
const gfx::Size& bitmap_size,
|
||||
void* bitmap_pixels);
|
||||
|
||||
void SetPainting(bool painting);
|
||||
bool IsPainting() const;
|
||||
|
||||
void SetFrameRate(int frame_rate);
|
||||
int GetFrameRate() const;
|
||||
|
||||
ui::Compositor* compositor() const { return compositor_.get(); }
|
||||
content::RenderWidgetHostImpl* render_widget_host() const
|
||||
{ return render_widget_host_; }
|
||||
|
||||
private:
|
||||
void SetupFrameRate(bool force);
|
||||
void ResizeRootLayer();
|
||||
|
||||
// Weak ptrs.
|
||||
content::RenderWidgetHostImpl* render_widget_host_;
|
||||
NativeWindow* native_window_;
|
||||
OffScreenOutputDevice* software_output_device_;
|
||||
|
||||
OnPaintCallback callback_;
|
||||
|
||||
int frame_rate_;
|
||||
int frame_rate_threshold_ms_;
|
||||
|
||||
base::Time last_time_;
|
||||
|
||||
const bool transparent_;
|
||||
float scale_factor_;
|
||||
bool is_showing_;
|
||||
gfx::Vector2dF last_scroll_offset_;
|
||||
gfx::Size size_;
|
||||
bool painting_;
|
||||
|
||||
std::unique_ptr<ui::Layer> root_layer_;
|
||||
std::unique_ptr<ui::Compositor> compositor_;
|
||||
std::unique_ptr<content::DelegatedFrameHost> delegated_frame_host_;
|
||||
|
||||
std::unique_ptr<AtomCopyFrameGenerator> copy_frame_generator_;
|
||||
std::unique_ptr<AtomBeginFrameTimer> begin_frame_timer_;
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
NSWindow* window_;
|
||||
CALayer* background_layer_;
|
||||
std::unique_ptr<content::BrowserCompositorMac> browser_compositor_;
|
||||
|
||||
// Selected text on the renderer.
|
||||
std::string selected_text_;
|
||||
#endif
|
||||
|
||||
base::WeakPtrFactory<OffScreenRenderWidgetHostView> weak_ptr_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(OffScreenRenderWidgetHostView);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_OSR_OSR_RENDER_WIDGET_HOST_VIEW_H_
|
|
@ -0,0 +1,113 @@
|
|||
// Copyright (c) 2016 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/osr/osr_render_widget_host_view.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "ui/accelerated_widget_mac/accelerated_widget_mac.h"
|
||||
|
||||
ui::AcceleratedWidgetMac*
|
||||
atom::OffScreenRenderWidgetHostView::GetAcceleratedWidgetMac() const {
|
||||
if (browser_compositor_)
|
||||
return browser_compositor_->accelerated_widget_mac();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NSView* atom::OffScreenRenderWidgetHostView::AcceleratedWidgetGetNSView()
|
||||
const {
|
||||
return [window_ contentView];
|
||||
}
|
||||
|
||||
void atom::OffScreenRenderWidgetHostView::AcceleratedWidgetGetVSyncParameters(
|
||||
base::TimeTicks* timebase, base::TimeDelta* interval) const {
|
||||
*timebase = base::TimeTicks();
|
||||
*interval = base::TimeDelta();
|
||||
}
|
||||
|
||||
void atom::OffScreenRenderWidgetHostView::AcceleratedWidgetSwapCompleted() {
|
||||
}
|
||||
|
||||
void atom::OffScreenRenderWidgetHostView::SetActive(bool active) {
|
||||
}
|
||||
|
||||
void atom::OffScreenRenderWidgetHostView::ShowDefinitionForSelection() {
|
||||
}
|
||||
|
||||
bool atom::OffScreenRenderWidgetHostView::SupportsSpeech() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void atom::OffScreenRenderWidgetHostView::SpeakSelection() {
|
||||
}
|
||||
|
||||
bool atom::OffScreenRenderWidgetHostView::IsSpeaking() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void atom::OffScreenRenderWidgetHostView::StopSpeaking() {
|
||||
}
|
||||
|
||||
void atom::OffScreenRenderWidgetHostView::SelectionChanged(
|
||||
const base::string16& text,
|
||||
size_t offset,
|
||||
const gfx::Range& range) {
|
||||
if (range.is_empty() || text.empty()) {
|
||||
selected_text_.clear();
|
||||
} else {
|
||||
size_t pos = range.GetMin() - offset;
|
||||
size_t n = range.length();
|
||||
|
||||
DCHECK(pos + n <= text.length()) << "The text can not fully cover range.";
|
||||
if (pos >= text.length()) {
|
||||
DCHECK(false) << "The text can not cover range.";
|
||||
return;
|
||||
}
|
||||
selected_text_ = base::UTF16ToUTF8(text.substr(pos, n));
|
||||
}
|
||||
|
||||
RenderWidgetHostViewBase::SelectionChanged(text, offset, range);
|
||||
}
|
||||
|
||||
void atom::OffScreenRenderWidgetHostView::CreatePlatformWidget() {
|
||||
window_ = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 1, 1)
|
||||
styleMask:NSBorderlessWindowMask
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:NO];
|
||||
|
||||
background_layer_ = [[[CALayer alloc] init] retain];
|
||||
[background_layer_ setBackgroundColor:CGColorGetConstantColor(kCGColorClear)];
|
||||
NSView* content_view = [window_ contentView];
|
||||
[content_view setLayer:background_layer_];
|
||||
[content_view setWantsLayer:YES];
|
||||
|
||||
browser_compositor_ = content::BrowserCompositorMac::Create();
|
||||
|
||||
compositor_.reset(browser_compositor_->compositor());
|
||||
compositor_->SetRootLayer(root_layer_.get());
|
||||
browser_compositor_->accelerated_widget_mac()->SetNSView(this);
|
||||
browser_compositor_->compositor()->SetVisible(true);
|
||||
|
||||
compositor_->SetLocksWillTimeOut(true);
|
||||
browser_compositor_->Unsuspend();
|
||||
}
|
||||
|
||||
void atom::OffScreenRenderWidgetHostView::DestroyPlatformWidget() {
|
||||
DCHECK(window_);
|
||||
|
||||
ui::Compositor* compositor = compositor_.release();
|
||||
ALLOW_UNUSED_LOCAL(compositor);
|
||||
|
||||
[window_ close];
|
||||
window_ = nil;
|
||||
[background_layer_ release];
|
||||
background_layer_ = nil;
|
||||
|
||||
browser_compositor_->accelerated_widget_mac()->ResetNSView();
|
||||
browser_compositor_->compositor()->SetVisible(false);
|
||||
browser_compositor_->compositor()->SetScaleAndSize(1.0, gfx::Size(0, 0));
|
||||
browser_compositor_->compositor()->SetRootLayer(NULL);
|
||||
content::BrowserCompositorMac::Recycle(std::move(browser_compositor_));
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
// Copyright (c) 2016 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/osr/osr_web_contents_view.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
OffScreenWebContentsView::OffScreenWebContentsView(bool transparent)
|
||||
: transparent_(transparent),
|
||||
web_contents_(nullptr) {
|
||||
}
|
||||
|
||||
OffScreenWebContentsView::~OffScreenWebContentsView() {
|
||||
}
|
||||
|
||||
void OffScreenWebContentsView::SetWebContents(
|
||||
content::WebContents* web_contents) {
|
||||
web_contents_ = web_contents;
|
||||
}
|
||||
|
||||
gfx::NativeView OffScreenWebContentsView::GetNativeView() const {
|
||||
return gfx::NativeView();
|
||||
}
|
||||
|
||||
gfx::NativeView OffScreenWebContentsView::GetContentNativeView() const {
|
||||
return gfx::NativeView();
|
||||
}
|
||||
|
||||
gfx::NativeWindow OffScreenWebContentsView::GetTopLevelNativeWindow() const {
|
||||
return gfx::NativeWindow();
|
||||
}
|
||||
|
||||
void OffScreenWebContentsView::GetContainerBounds(gfx::Rect* out) const {
|
||||
*out = GetViewBounds();
|
||||
}
|
||||
|
||||
void OffScreenWebContentsView::SizeContents(const gfx::Size& size) {
|
||||
}
|
||||
|
||||
void OffScreenWebContentsView::Focus() {
|
||||
}
|
||||
|
||||
void OffScreenWebContentsView::SetInitialFocus() {
|
||||
}
|
||||
|
||||
void OffScreenWebContentsView::StoreFocus() {
|
||||
}
|
||||
|
||||
void OffScreenWebContentsView::RestoreFocus() {
|
||||
}
|
||||
|
||||
content::DropData* OffScreenWebContentsView::GetDropData() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gfx::Rect OffScreenWebContentsView::GetViewBounds() const {
|
||||
return view_ ? view_->GetViewBounds() : gfx::Rect();
|
||||
}
|
||||
|
||||
void OffScreenWebContentsView::CreateView(const gfx::Size& initial_size,
|
||||
gfx::NativeView context) {
|
||||
}
|
||||
|
||||
content::RenderWidgetHostViewBase*
|
||||
OffScreenWebContentsView::CreateViewForWidget(
|
||||
content::RenderWidgetHost* render_widget_host, bool is_guest_view_hack) {
|
||||
auto relay = NativeWindowRelay::FromWebContents(web_contents_);
|
||||
view_ = new OffScreenRenderWidgetHostView(transparent_, render_widget_host,
|
||||
relay->window.get());
|
||||
return view_;
|
||||
}
|
||||
|
||||
content::RenderWidgetHostViewBase*
|
||||
OffScreenWebContentsView::CreateViewForPopupWidget(
|
||||
content::RenderWidgetHost* render_widget_host) {
|
||||
auto relay = NativeWindowRelay::FromWebContents(web_contents_);
|
||||
view_ = new OffScreenRenderWidgetHostView(transparent_, render_widget_host,
|
||||
relay->window.get());
|
||||
return view_;
|
||||
}
|
||||
|
||||
void OffScreenWebContentsView::SetPageTitle(const base::string16& title) {
|
||||
}
|
||||
|
||||
void OffScreenWebContentsView::RenderViewCreated(
|
||||
content::RenderViewHost* host) {
|
||||
if (view_)
|
||||
view_->InstallTransparency();
|
||||
}
|
||||
|
||||
void OffScreenWebContentsView::RenderViewSwappedIn(
|
||||
content::RenderViewHost* host) {
|
||||
}
|
||||
|
||||
void OffScreenWebContentsView::SetOverscrollControllerEnabled(bool enabled) {
|
||||
}
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
void OffScreenWebContentsView::SetAllowOtherViews(bool allow) {
|
||||
}
|
||||
|
||||
bool OffScreenWebContentsView::GetAllowOtherViews() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OffScreenWebContentsView::IsEventTracking() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void OffScreenWebContentsView::CloseTabAfterEventTracking() {
|
||||
}
|
||||
#endif // defined(OS_MACOSX)
|
||||
|
||||
void OffScreenWebContentsView::StartDragging(
|
||||
const content::DropData& drop_data,
|
||||
blink::WebDragOperationsMask allowed_ops,
|
||||
const gfx::ImageSkia& image,
|
||||
const gfx::Vector2d& image_offset,
|
||||
const content::DragEventSourceInfo& event_info) {
|
||||
if (web_contents_)
|
||||
web_contents_->SystemDragEnded();
|
||||
}
|
||||
|
||||
void OffScreenWebContentsView::UpdateDragCursor(
|
||||
blink::WebDragOperation operation) {
|
||||
}
|
||||
|
||||
} // namespace atom
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright (c) 2016 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_OSR_OSR_WEB_CONTENTS_VIEW_H_
|
||||
#define ATOM_BROWSER_OSR_OSR_WEB_CONTENTS_VIEW_H_
|
||||
|
||||
#include "atom/browser/osr/osr_render_widget_host_view.h"
|
||||
#include "content/browser/renderer_host/render_view_host_delegate_view.h"
|
||||
#include "content/browser/web_contents/web_contents_view.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class OffScreenWebContentsView : public content::WebContentsView,
|
||||
public content::RenderViewHostDelegateView {
|
||||
public:
|
||||
explicit OffScreenWebContentsView(bool transparent);
|
||||
~OffScreenWebContentsView();
|
||||
|
||||
void SetWebContents(content::WebContents*);
|
||||
|
||||
// content::WebContentsView
|
||||
gfx::NativeView GetNativeView() const override;
|
||||
gfx::NativeView GetContentNativeView() const override;
|
||||
gfx::NativeWindow GetTopLevelNativeWindow() const override;
|
||||
void GetContainerBounds(gfx::Rect* out) const override;
|
||||
void SizeContents(const gfx::Size& size) override;
|
||||
void Focus() override;
|
||||
void SetInitialFocus() override;
|
||||
void StoreFocus() override;
|
||||
void RestoreFocus() override;
|
||||
content::DropData* GetDropData() const override;
|
||||
gfx::Rect GetViewBounds() const override;
|
||||
void CreateView(
|
||||
const gfx::Size& initial_size, gfx::NativeView context) override;
|
||||
content::RenderWidgetHostViewBase* CreateViewForWidget(
|
||||
content::RenderWidgetHost* render_widget_host,
|
||||
bool is_guest_view_hack) override;
|
||||
content::RenderWidgetHostViewBase* CreateViewForPopupWidget(
|
||||
content::RenderWidgetHost* render_widget_host) override;
|
||||
void SetPageTitle(const base::string16& title) override;
|
||||
void RenderViewCreated(content::RenderViewHost* host) override;
|
||||
void RenderViewSwappedIn(content::RenderViewHost* host) override;
|
||||
void SetOverscrollControllerEnabled(bool enabled) override;
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
void SetAllowOtherViews(bool allow) override;
|
||||
bool GetAllowOtherViews() const override;
|
||||
bool IsEventTracking() const override;
|
||||
void CloseTabAfterEventTracking() override;
|
||||
#endif
|
||||
|
||||
// content::RenderViewHostDelegateView
|
||||
void StartDragging(
|
||||
const content::DropData& drop_data,
|
||||
blink::WebDragOperationsMask allowed_ops,
|
||||
const gfx::ImageSkia& image,
|
||||
const gfx::Vector2d& image_offset,
|
||||
const content::DragEventSourceInfo& event_info) override;
|
||||
void UpdateDragCursor(blink::WebDragOperation operation) override;
|
||||
|
||||
private:
|
||||
const bool transparent_;
|
||||
|
||||
// Weak refs.
|
||||
OffScreenRenderWidgetHostView* view_;
|
||||
content::WebContents* web_contents_;
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_OSR_OSR_WEB_CONTENTS_VIEW_H_
|
|
@ -19,6 +19,7 @@
|
|||
#include "content/public/common/web_preferences.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "net/base/filename_util.h"
|
||||
#include "cc/base/switches.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "ui/gfx/switches.h"
|
||||
|
@ -185,6 +186,12 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches(
|
|||
if (!visible) // Default state is visible.
|
||||
command_line->AppendSwitch("hidden-page");
|
||||
}
|
||||
|
||||
// Use frame scheduling for offscreen renderers.
|
||||
// TODO(zcbenz): Remove this after Chrome 54, on which it becomes default.
|
||||
bool offscreen;
|
||||
if (web_preferences.GetBoolean("offscreen", &offscreen) && offscreen)
|
||||
command_line->AppendSwitch(cc::switches::kEnableBeginFrameScheduling);
|
||||
}
|
||||
|
||||
// static
|
||||
|
|
|
@ -30,6 +30,7 @@ an issue:
|
|||
* [Using Pepper Flash Plugin](tutorial/using-pepper-flash-plugin.md)
|
||||
* [Using Widevine CDM Plugin](tutorial/using-widevine-cdm-plugin.md)
|
||||
* [Testing on Headless CI Systems (Travis, Jenkins)](tutorial/testing-on-headless-ci.md)
|
||||
* [Offscreen Rendering](tutorial/offscreen-rendering.md)
|
||||
|
||||
## Tutorials
|
||||
|
||||
|
|
|
@ -283,6 +283,8 @@ The `webPreferences` option is an object that can have the following properties:
|
|||
* `defaultEncoding` String - Defaults to `ISO-8859-1`.
|
||||
* `backgroundThrottling` Boolean - Whether to throttle animations and timers
|
||||
when the page becomes background. Defaults to `true`.
|
||||
* `offscreen` Boolean - Whether to enable offscreen rendering for the browser
|
||||
window. Defaults to `false`.
|
||||
|
||||
### Instance Events
|
||||
|
||||
|
|
|
@ -461,6 +461,41 @@ app.on('ready', () => {
|
|||
|
||||
Emitted when a page's view is repainted.
|
||||
|
||||
#### Event: 'paint'
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `dirtyRect` Object
|
||||
* `x` Number - the x coordinate on the bitmap
|
||||
* `y` Number - the y coordinate on the bitmap
|
||||
* `width` Number - the width of the dirty area
|
||||
* `height` Number - the height of the dirty area
|
||||
* `data` Buffer - the bitmap data of the dirty rect
|
||||
* `bitmapSize` Object
|
||||
* `width` Number - the width of the whole bitmap
|
||||
* `height` Number - the height of the whole bitmap
|
||||
|
||||
Emitted when a new frame is generated. Only the dirty area is passed in the
|
||||
buffer.
|
||||
|
||||
```javascript
|
||||
const {BrowserWindow} = require('electron')
|
||||
|
||||
let win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 1500,
|
||||
webPreferences: {
|
||||
offscreen: true
|
||||
}
|
||||
})
|
||||
win.loadURL('http://github.com')
|
||||
|
||||
win.webContents.on('paint', (event, dirty, data) => {
|
||||
// updateBitmap(dirty, data)
|
||||
})
|
||||
```
|
||||
|
||||
### Instance Methods
|
||||
|
||||
#### `contents.loadURL(url[, options])`
|
||||
|
@ -1084,6 +1119,31 @@ win.webContents.on('did-finish-load', () => {
|
|||
|
||||
Shows pop-up dictionary that searches the selected word on the page.
|
||||
|
||||
#### `contents.isOffscreen()`
|
||||
|
||||
Indicates whether *offscreen rendering* is enabled.
|
||||
|
||||
#### `contents.startPainting()`
|
||||
|
||||
If *offscreen rendering* is enabled and not painting, start painting.
|
||||
|
||||
#### `contents.stopPainting()`
|
||||
|
||||
If *offscreen rendering* is enabled and painting, stop painting.
|
||||
|
||||
#### `contents.isPainting()`
|
||||
|
||||
If *offscreen rendering* is enabled returns whether it is currently painting.
|
||||
|
||||
#### `contents.setFrameRate(fps)`
|
||||
|
||||
If *offscreen rendering* is enabled sets the frame rate to the specified number.
|
||||
Only values between 1 and 60 are accepted.
|
||||
|
||||
#### `contents.getFrameRate()`
|
||||
|
||||
If *offscreen rendering* is enabled returns the current frame rate.
|
||||
|
||||
### Instance Properties
|
||||
|
||||
#### `contents.id`
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
# Offscreen Rendering
|
||||
|
||||
Offscreen rendering lets you obtain the content of a browser window in a bitmap,
|
||||
so it can be rendered anywhere, for example on a texture in a 3D scene. The
|
||||
offscreen rendering in Electron uses a similar approach than the [Chromium
|
||||
Embedded Framework](https://bitbucket.org/chromiumembedded/cef) project.
|
||||
|
||||
Two modes of rendering can be used and only the dirty area is passed in the
|
||||
`'paint'` event to be more efficient. The rendering can be stopped, continued
|
||||
and the frame rate can be set. The specified frame rate is a top limit value,
|
||||
when there is nothing happening on a webpage, no frames are generated. The
|
||||
maximum frame rate is 60, because above that there is no benefit, just
|
||||
performance loss.
|
||||
|
||||
## Two modes of rendering
|
||||
|
||||
### GPU accelerated
|
||||
|
||||
GPU accelerated rendering means that the GPU is used for composition. Because of
|
||||
that the frame has to be copied from the GPU which requires more performance,
|
||||
thus this mode is quite a bit slower than the other one. The benefit of this
|
||||
mode that WebGL and 3D CSS animations are supported.
|
||||
|
||||
### Software output device
|
||||
|
||||
This mode uses a software output device for rendering in the CPU, so the frame
|
||||
generation is much faster, thus this mode is preferred over the GPU accelerated
|
||||
one.
|
||||
|
||||
To enable this mode GPU acceleration has to be disabled by calling the
|
||||
[`app.disableHardwareAcceleration()`][disablehardwareacceleration] API.
|
||||
|
||||
## Usage
|
||||
|
||||
``` javascript
|
||||
const {app, BrowserWindow} = require('electron')
|
||||
|
||||
app.disableHardwareAcceleration()
|
||||
|
||||
let win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 1500,
|
||||
webPreferences: {
|
||||
offscreen: true
|
||||
}
|
||||
})
|
||||
win.loadURL('http://github.com')
|
||||
|
||||
win.webContents.setFrameRate(30)
|
||||
|
||||
win.webContents.on('paint', (event, dirty, data) => {
|
||||
// updateBitmap(dirty, data)
|
||||
})
|
||||
```
|
||||
|
||||
[disablehardwareacceleration]: ../api/app.md#appdisablehardwareacceleration
|
|
@ -206,6 +206,13 @@
|
|||
'atom/browser/native_window_mac.h',
|
||||
'atom/browser/native_window_mac.mm',
|
||||
'atom/browser/native_window_observer.h',
|
||||
'atom/browser/osr/osr_web_contents_view.cc',
|
||||
'atom/browser/osr/osr_web_contents_view.h',
|
||||
'atom/browser/osr/osr_output_device.cc',
|
||||
'atom/browser/osr/osr_output_device.h',
|
||||
'atom/browser/osr/osr_render_widget_host_view.cc',
|
||||
'atom/browser/osr/osr_render_widget_host_view.h',
|
||||
'atom/browser/osr/osr_render_widget_host_view_mac.mm',
|
||||
'atom/browser/net/asar/asar_protocol_handler.cc',
|
||||
'atom/browser/net/asar/asar_protocol_handler.h',
|
||||
'atom/browser/net/asar/url_request_asar_job.cc',
|
||||
|
|
|
@ -8,7 +8,7 @@ import sys
|
|||
|
||||
BASE_URL = os.getenv('LIBCHROMIUMCONTENT_MIRROR') or \
|
||||
'https://s3.amazonaws.com/github-janky-artifacts/libchromiumcontent'
|
||||
LIBCHROMIUMCONTENT_COMMIT = '658edcb9509cfa03eea321977d060769467891e0'
|
||||
LIBCHROMIUMCONTENT_COMMIT = '086d162df0962c12d2db5a9fbe488aa52ad9a327'
|
||||
|
||||
PLATFORM = {
|
||||
'cygwin': 'win32',
|
||||
|
|
|
@ -1167,4 +1167,98 @@ describe('browser-window module', function () {
|
|||
w.loadURL(server.url)
|
||||
})
|
||||
})
|
||||
|
||||
describe('offscreen rendering', function () {
|
||||
this.timeout(10000)
|
||||
|
||||
beforeEach(function () {
|
||||
if (w != null) w.destroy()
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
backgroundThrottling: false,
|
||||
offscreen: true
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('creates offscreen window', function (done) {
|
||||
w.webContents.once('paint', function (event, rect, data, size) {
|
||||
assert.notEqual(data.length, 0)
|
||||
done()
|
||||
})
|
||||
w.loadURL('file://' + fixtures + '/api/offscreen-rendering.html')
|
||||
})
|
||||
|
||||
describe('window.webContents.isOffscreen()', function () {
|
||||
it('is true for offscreen type', function () {
|
||||
w.loadURL('file://' + fixtures + '/api/offscreen-rendering.html')
|
||||
assert.equal(w.webContents.isOffscreen(), true)
|
||||
})
|
||||
|
||||
it('is false for regular window', function () {
|
||||
let c = new BrowserWindow({show: false})
|
||||
assert.equal(c.webContents.isOffscreen(), false)
|
||||
c.destroy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('window.webContents.isPainting()', function () {
|
||||
it('returns whether is currently painting', function (done) {
|
||||
w.webContents.once('paint', function (event, rect, data, size) {
|
||||
assert.equal(w.webContents.isPainting(), true)
|
||||
done()
|
||||
})
|
||||
w.loadURL('file://' + fixtures + '/api/offscreen-rendering.html')
|
||||
})
|
||||
})
|
||||
|
||||
describe('window.webContents.stopPainting()', function () {
|
||||
it('stops painting', function (done) {
|
||||
w.webContents.on('dom-ready', function () {
|
||||
w.webContents.stopPainting()
|
||||
assert.equal(w.webContents.isPainting(), false)
|
||||
done()
|
||||
})
|
||||
w.loadURL('file://' + fixtures + '/api/offscreen-rendering.html')
|
||||
})
|
||||
})
|
||||
|
||||
describe('window.webContents.startPainting()', function () {
|
||||
it('starts painting', function (done) {
|
||||
w.webContents.on('dom-ready', function () {
|
||||
w.webContents.stopPainting()
|
||||
w.webContents.startPainting()
|
||||
w.webContents.once('paint', function (event, rect, data, size) {
|
||||
assert.equal(w.webContents.isPainting(), true)
|
||||
done()
|
||||
})
|
||||
})
|
||||
w.loadURL('file://' + fixtures + '/api/offscreen-rendering.html')
|
||||
})
|
||||
})
|
||||
|
||||
describe('window.webContents.getFrameRate()', function () {
|
||||
it('has default frame rate', function (done) {
|
||||
w.webContents.once('paint', function (event, rect, data, size) {
|
||||
assert.equal(w.webContents.getFrameRate(), 60)
|
||||
done()
|
||||
})
|
||||
w.loadURL('file://' + fixtures + '/api/offscreen-rendering.html')
|
||||
})
|
||||
})
|
||||
|
||||
describe('window.webContents.setFrameRate(frameRate)', function () {
|
||||
it('sets custom frame rate', function (done) {
|
||||
w.webContents.on('dom-ready', function () {
|
||||
w.webContents.setFrameRate(30)
|
||||
w.webContents.once('paint', function (event, rect, data, size) {
|
||||
assert.equal(w.webContents.getFrameRate(), 30)
|
||||
done()
|
||||
})
|
||||
})
|
||||
w.loadURL('file://' + fixtures + '/api/offscreen-rendering.html')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<html>
|
||||
<body>
|
||||
<div style="width: 10px; height: 10px;" id="dirty"></div>
|
||||
</body>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
setInterval(function(){
|
||||
document.getElementById('dirty').style.backgroundColor =
|
||||
'#'+(Math.random()*0xFFFFFF<<0).toString(16)
|
||||
}, 10)
|
||||
</script>
|
||||
</html>
|
|
@ -1 +1 @@
|
|||
Subproject commit ba150e6367904e14d102f60aa7e66601e4e2a8ab
|
||||
Subproject commit 7b5f321cd44f2083c59ab94f2bb23cc3f59a5c2b
|
Загрузка…
Ссылка в новой задаче