Merge branch 'offscreen-rendering' of https://github.com/MaxWhere/electron into merge-offscreen

This commit is contained in:
Cheng Zhao 2016-08-03 10:09:48 +09:00
commit 363fabdcb4
27 changed files with 2105 additions and 17 deletions

4
.gitmodules vendored
View file

@ -1,6 +1,6 @@
[submodule "vendor/brightray"]
path = vendor/brightray
url = https://github.com/electron/brightray.git
url=https://github.com/MaxWhere/brightray
[submodule "vendor/node"]
path = vendor/node
url = https://github.com/electron/node.git
@ -21,4 +21,4 @@
url = https://github.com/kennethreitz/requests
[submodule "vendor/boto"]
path = vendor/boto
url = https://github.com/boto/boto.git
url = https://github.com/boto/boto.git

View file

@ -68,6 +68,10 @@
#include "third_party/WebKit/public/web/WebFindOptions.h"
#include "ui/display/screen.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "atom/browser/osr_web_contents_view.h"
#include "atom/browser/osr_render_widget_host_view.h"
#include "atom/common/node_includes.h"
namespace {
@ -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,21 @@ 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);
paint_callback_ = base::Bind(&WebContents::OnPaint, base::Unretained(this),
isolate);
} else {
content::WebContents::CreateParams params(session->browser_context());
web_contents = content::WebContents::Create(params);
@ -360,7 +384,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 +395,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 +405,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 +422,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 +435,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 +490,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 +612,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()) {
const auto rwhv = web_contents()->GetRenderWidgetHostView();
auto osr_rwhv = static_cast<OffScreenRenderWidgetHostView *>(rwhv);
osr_rwhv->SetPaintCallback(&paint_callback_);
}
Emit("dom-ready");
}
}
void WebContents::DidFinishLoad(content::RenderFrameHost* render_frame_host,
@ -1316,6 +1348,10 @@ bool WebContents::IsGuest() const {
return type_ == WEB_VIEW;
}
bool WebContents::IsOffScreen() const {
return type_ == OFF_SCREEN;
}
v8::Local<v8::Value> WebContents::GetWebPreferences(v8::Isolate* isolate) {
WebContentsPreferences* web_preferences =
WebContentsPreferences::FromWebContents(web_contents());
@ -1358,6 +1394,64 @@ v8::Local<v8::Value> WebContents::Debugger(v8::Isolate* isolate) {
return v8::Local<v8::Value>::New(isolate, debugger_);
}
void WebContents::OnPaint(
v8::Isolate* isolate,
const gfx::Rect& damage_rect,
int bitmap_width,
int bitmap_height,
void* bitmap_pixels) {
v8::MaybeLocal<v8::Object> buffer = node::Buffer::New(isolate,
reinterpret_cast<char *>(bitmap_pixels), sizeof(bitmap_pixels));
const gfx::Size bitmap_size = gfx::Size(bitmap_width, bitmap_height);
Emit("paint", damage_rect, buffer.ToLocalChecked(), bitmap_size);
}
void WebContents::StartPainting() {
if (IsOffScreen()) {
const auto osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
web_contents()->GetRenderWidgetHostView());
osr_rwhv->SetPainting(true);
osr_rwhv->Show();
}
}
void WebContents::StopPainting() {
if (IsOffScreen()) {
const auto osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
web_contents()->GetRenderWidgetHostView());
osr_rwhv->SetPainting(false);
osr_rwhv->Hide();
}
}
bool WebContents::IsPainting() const {
if (IsOffScreen()) {
const auto osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
web_contents()->GetRenderWidgetHostView());
return osr_rwhv->IsPainting();
}
return false;
}
void WebContents::SetFrameRate(int frame_rate) {
if (IsOffScreen()) {
const auto osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
web_contents()->GetRenderWidgetHostView());
osr_rwhv->SetFrameRate(frame_rate);
}
}
int WebContents::GetFrameRate() const {
if (IsOffScreen()) {
const auto osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
web_contents()->GetRenderWidgetHostView());
return osr_rwhv->GetFrameRate();
}
return 0;
}
// static
void WebContents::BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> prototype) {
@ -1433,6 +1527,12 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
.SetMethod("copyImageAt", &WebContents::CopyImageAt)
.SetMethod("capturePage", &WebContents::CapturePage)
.SetMethod("isFocused", &WebContents::IsFocused)
.SetMethod("isOffscreen", &WebContents::IsOffScreen)
.SetMethod("startPainting", &WebContents::StartPainting)
.SetMethod("stopPainting", &WebContents::StopPainting)
.SetMethod("isPainting", &WebContents::IsPainting)
.SetMethod("setFrameRate", &WebContents::SetFrameRate)
.SetMethod("getFrameRate", &WebContents::GetFrameRate)
.SetProperty("id", &WebContents::ID)
.SetProperty("session", &WebContents::Session)
.SetProperty("hostWebContents", &WebContents::HostWebContents)

View file

@ -18,6 +18,8 @@
#include "native_mate/handle.h"
#include "ui/gfx/image/image.h"
#include "atom/browser/osr_output_device.h"
namespace blink {
struct WebDeviceEmulationParams;
}
@ -48,6 +50,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)
@ -155,6 +158,15 @@ class WebContents : public mate::TrackableObject<WebContents>,
void SetSize(const SetSizeParams& params);
bool IsGuest() const;
// Methods for offscreen rendering
void OnPaint(v8::Isolate*, const gfx::Rect&, int, int, void*);
bool IsOffScreen() const;
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,
@ -279,6 +291,8 @@ class WebContents : public mate::TrackableObject<WebContents>,
private:
AtomBrowserContext* GetBrowserContext() const;
atom::OnPaintCallback paint_callback_;
uint32_t GetNextRequestId() {
return ++request_id_;
}

View file

@ -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());

View file

@ -9,6 +9,11 @@
#include "atom/common/node_includes.h"
#include "content/public/browser/render_widget_host.h"
#include "content/browser/compositor/image_transport_factory.h"
#include "cc/surfaces/surface_manager.h"
#include "cc/surfaces/surface.h"
#include "cc/output/copy_output_request.h"
namespace atom {
namespace api {
@ -30,6 +35,7 @@ bool FrameSubscriber::ShouldCaptureFrame(
scoped_refptr<media::VideoFrame>* storage,
DeliverFrameCallback* callback) {
const auto host = view_ ? view_->GetRenderWidgetHost() : nullptr;
if (!view_ || !host)
return false;

View file

@ -14,6 +14,10 @@
#include "ui/gfx/geometry/size.h"
#include "v8/include/v8.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "cc/surfaces/surface_id.h"
#include "cc/output/copy_output_result.h"
namespace atom {
namespace api {
@ -34,6 +38,9 @@ class FrameSubscriber : public content::RenderWidgetHostViewFrameSubscriber {
DeliverFrameCallback* callback) override;
private:
void ReadbackResultAsBitmap(
std::unique_ptr<cc::CopyOutputResult> result);
void OnFrameDelivered(const FrameCaptureCallback& callback,
const gfx::Rect& damage_rect,
const SkBitmap& bitmap,

View file

@ -8,6 +8,8 @@
#include <string>
#include <vector>
#include <iostream>
#include "atom/browser/atom_browser_context.h"
#include "atom/browser/atom_javascript_dialog_manager.h"
#include "atom/browser/atom_security_state_model_client.h"
@ -31,6 +33,10 @@
#include "content/public/browser/security_style_explanations.h"
#include "storage/browser/fileapi/isolated_context.h"
// #include "content/browser/web_contents/web_contents_impl.h"
// #include "atom/browser/osr_web_contents_view.h"
#include "atom/browser/native_window_views.h"
using content::BrowserThread;
using security_state::SecurityStateModel;
@ -194,6 +200,7 @@ void CommonWebContentsDelegate::SetOwnerWindow(NativeWindow* owner_window) {
void CommonWebContentsDelegate::SetOwnerWindow(
content::WebContents* web_contents, NativeWindow* owner_window) {
owner_window_ = owner_window->GetWeakPtr();
NativeWindowRelay* relay = new NativeWindowRelay(owner_window_);
web_contents->SetUserData(relay->key, relay);
}

View file

@ -57,6 +57,13 @@
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
#endif
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/context_factory.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread.h"
#include "ui/gfx/native_widget_types.h"
namespace atom {
namespace {
@ -1230,13 +1237,13 @@ void NativeWindowViews::HandleKeyboardEvent(
}
}
gfx::Size NativeWindowViews::GetMinimumSize() {
/*gfx::Size NativeWindowViews::GetMinimumSize() {
return NativeWindow::GetMinimumSize();
}
gfx::Size NativeWindowViews::GetMaximumSize() {
return NativeWindow::GetMaximumSize();
}
}*/
bool NativeWindowViews::AcceleratorPressed(const ui::Accelerator& accelerator) {
return accelerator_util::TriggerAcceleratorTableCommand(

View file

@ -170,9 +170,9 @@ class NativeWindowViews : public NativeWindow,
content::WebContents*,
const content::NativeWebKeyboardEvent& event) override;
// views::View:
gfx::Size GetMinimumSize() override;
gfx::Size GetMaximumSize() override;
// // views::View:
// gfx::Size GetMinimumSize() const override;
// gfx::Size GetMaximumSize() const override;
bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
// Register accelerators supported by the menu model.

View file

@ -0,0 +1,95 @@
// 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_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(NULL);
bitmap_.reset(new SkBitmap);
bitmap_->allocN32Pixels(viewport_pixel_size_.width(),
viewport_pixel_size_.height(),
!transparent_);
if (bitmap_->drawsNothing()) {
NOTREACHED();
bitmap_.reset(NULL);
return;
}
if (transparent_)
bitmap_->eraseARGB(0, 0, 0, 0);
canvas_.reset(new SkCanvas(*bitmap_.get()));
}
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(0, 0, viewport_pixel_size_.width(),
viewport_pixel_size_.height()));
}
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_.get());
callback_.Run(rect, bitmap_->width(), bitmap_->height(),
bitmap_->getPixels());
}
} // namespace atom

View file

@ -0,0 +1,47 @@
// 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_OUTPUT_DEVICE_H_
#define ATOM_BROWSER_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&, int, int, void*)> OnPaintCallback;
class OffScreenOutputDevice : public cc::SoftwareOutputDevice {
public:
OffScreenOutputDevice(bool transparent, const OnPaintCallback& callback);
~OffScreenOutputDevice();
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_;
const 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_OUTPUT_DEVICE_H_

View file

@ -0,0 +1,886 @@
// 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_render_widget_host_view.h"
#include <vector>
#include "third_party/WebKit/public/platform/WebScreenInfo.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/public/browser/render_widget_host_view_frame_subscriber.h"
#include "ui/events/latency_info.h"
#include "content/common/view_messages.h"
#include "ui/gfx/geometry/dip_util.h"
#include "base/memory/ptr_util.h"
#include "content/public/browser/context_factory.h"
#include "base/single_thread_task_runner.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_type.h"
#include "base/callback_helpers.h"
#include "base/location.h"
#include "base/time/time.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/views/widget/widget.h"
#include "cc/output/copy_output_request.h"
#include "cc/scheduler/delay_based_time_source.h"
#include "content/public/browser/browser_thread.h"
const float kDefaultScaleFactor = 1.0;
const int kFrameRetryLimit = 2;
namespace atom {
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) {
uint8_t* pixels = reinterpret_cast<uint8_t*>(bitmap.getPixels());
view_->OnPaint(damage_rect, bitmap.width(), bitmap.height(), pixels);
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(
const bool transparent, content::RenderWidgetHost* host,
NativeWindow* native_window):
render_widget_host_(content::RenderWidgetHostImpl::From(host)),
native_window_(native_window),
software_output_device_(NULL),
callback_(nullptr),
frame_rate_(60),
frame_rate_threshold_ms_(0),
transparent_(transparent),
scale_factor_(kDefaultScaleFactor),
is_showing_(!render_widget_host_->is_hidden()),
size_(native_window->GetSize()),
painting_(true),
delegated_frame_host_(new content::DelegatedFrameHost(this)),
compositor_widget_(gfx::kNullAcceleratedWidget),
weak_ptr_factory_(this) {
DCHECK(render_widget_host_);
render_widget_host_->SetView(this);
last_time_ = base::Time::Now();
root_layer_.reset(new ui::Layer(ui::LAYER_SOLID_COLOR));
#if defined(OS_MACOSX)
CreatePlatformWidget();
#endif
#if !defined(OS_MACOSX)
compositor_widget_ = native_window_->GetAcceleratedWidget();
compositor_.reset(
new ui::Compositor(content::GetContextFactory(),
base::ThreadTaskRunnerHandle::Get()));
compositor_->SetAcceleratedWidget(compositor_widget_);
#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
if (copy_frame_generator_.get())
copy_frame_generator_.reset(NULL);
delegated_frame_host_.reset(NULL);
compositor_.reset(NULL);
root_layer_.reset(NULL);
}
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);
}
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::SetPaintCallback(
const OnPaintCallback* callback) {
callback_ = callback;
}
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) {
printf("popup, parent: %p\n", parent_host_view);
}
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);
}
bool OffScreenRenderWidgetHostView::InstallTransparency() {
if (transparent_) {
SetBackgroundColor(SkColor());
compositor_->SetHostHasTransparentBackground(true);
return true;
}
return false;
}
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_);
}
void OffScreenRenderWidgetHostView::OnSetNeedsBeginFrames(bool enabled) {
SetupFrameRate(false);
begin_frame_timer_->SetActive(enabled);
if (software_output_device_) {
software_output_device_->SetActive(enabled);
}
}
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::SetBeginFrameSource(
cc::BeginFrameSource* source) {
}
bool OffScreenRenderWidgetHostView::IsAutoResizeEnabled() const {
return false;
}
void OffScreenRenderWidgetHostView::OnPaint(
const gfx::Rect& damage_rect,
int bitmap_width,
int bitmap_height,
void* bitmap_pixels) {
TRACE_EVENT0("electron", "OffScreenRenderWidgetHostView::OnPaint");
if (callback_ != nullptr) {
callback_->Run(damage_rect, bitmap_width, bitmap_height, 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_;
}
} // namespace atom

View file

@ -0,0 +1,266 @@
// 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_RENDER_WIDGET_HOST_VIEW_H_
#define ATOM_BROWSER_OSR_RENDER_WIDGET_HOST_VIEW_H_
#include <string>
#include <vector>
#include "atom/browser/native_window.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/browser/renderer_host/delegated_frame_host.h"
#include "content/browser/renderer_host/resize_lock.h"
#include "third_party/WebKit/public/platform/WebVector.h"
#include "cc/scheduler/begin_frame_source.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "cc/output/compositor_frame.h"
#include "ui/gfx/geometry/point.h"
#include "base/process/kill.h"
#include "base/threading/thread.h"
#include "base/time/time.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 "atom/browser/osr_output_device.h"
#if defined(OS_WIN)
#include <windows.h>
#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;
@class NSTextInputContext;
#else
class CALayer;
class NSWindow;
class NSTextInputContext;
#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(const bool transparent,
content::RenderWidgetHost*, NativeWindow*);
~OffScreenRenderWidgetHostView();
// 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;
bool InstallTransparency();
bool IsAutoResizeEnabled() const;
std::unique_ptr<cc::SoftwareOutputDevice> CreateSoftwareOutputDevice(
ui::Compositor* compositor) override;
void OnSetNeedsBeginFrames(bool enabled);
#if defined(OS_MACOSX)
// AcceleratedWidgetMacNSView implementation.
NSView* AcceleratedWidgetGetNSView() const override;
void AcceleratedWidgetGetVSyncParameters(
base::TimeTicks* timebase, base::TimeDelta* interval) const override;
void AcceleratedWidgetSwapCompleted() override;
#endif // defined(OS_MACOSX)
ui::Compositor* compositor() const { return compositor_.get(); }
content::RenderWidgetHostImpl* render_widget_host() const
{ return render_widget_host_; }
void OnBeginFrameTimerTick();
void SendBeginFrame(base::TimeTicks frame_time,
base::TimeDelta vsync_period);
void CreatePlatformWidget();
void DestroyPlatformWidget();
void SetPaintCallback(const atom::OnPaintCallback*);
void OnPaint(const gfx::Rect& damage_rect,
int bitmap_width,
int bitmap_height,
void* bitmap_pixels);
void SetPainting(bool painting);
bool IsPainting() const;
void SetFrameRate(int frame_rate);
int GetFrameRate() const;
private:
void SetupFrameRate(bool force);
void ResizeRootLayer();
content::RenderWidgetHostImpl* render_widget_host_;
NativeWindow* native_window_;
std::unique_ptr<AtomCopyFrameGenerator> copy_frame_generator_;
std::unique_ptr<AtomBeginFrameTimer> begin_frame_timer_;
OffScreenOutputDevice* software_output_device_;
const atom::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<content::DelegatedFrameHost> delegated_frame_host_;
std::unique_ptr<ui::Compositor> compositor_;
gfx::AcceleratedWidget compositor_widget_;
std::unique_ptr<ui::Layer> root_layer_;
#if defined(OS_MACOSX)
NSWindow* window_;
CALayer* background_layer_;
std::unique_ptr<content::BrowserCompositorMac> browser_compositor_;
NSTextInputContext* text_input_context_osr_mac_;
// Selected text on the renderer.
std::string selected_text_;
// The current composition character range and its bounds.
gfx::Range composition_range_;
std::vector<gfx::Rect> composition_bounds_;
// The current caret bounds.
gfx::Rect caret_rect_;
// The current first selection bounds.
gfx::Rect first_selection_rect_;
#endif
base::WeakPtrFactory<OffScreenRenderWidgetHostView> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(OffScreenRenderWidgetHostView);
};
} // namespace atom
#endif // ATOM_BROWSER_OSR_RENDER_WIDGET_HOST_VIEW_H_

View file

@ -0,0 +1,114 @@
// 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_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_));
}

View file

@ -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_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

View file

@ -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_WEB_CONTENTS_VIEW_H_
#define ATOM_BROWSER_OSR_WEB_CONTENTS_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"
#include "atom/browser/osr_render_widget_host_view.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_;
OffScreenRenderWidgetHostView* view_;
content::WebContents* web_contents_;
};
} // namespace atom
#endif // ATOM_BROWSER_OSR_WEB_CONTENTS_VIEW_H_

View file

@ -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,8 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches(
if (!visible) // Default state is visible.
command_line->AppendSwitch("hidden-page");
}
command_line->AppendSwitch(cc::switches::kEnableBeginFrameScheduling);
}
// static

View file

@ -129,6 +129,14 @@
e.preventDefault();
return false;
};
document.onload = () => {
var a = false
setInterval(function () {
document.body.querySelector('.container').style.background = a ? 'red' : 'blue'
a = !a
}, 200)
}
</script>
<div class="header">

View file

@ -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

View file

@ -175,6 +175,36 @@ logging level for all code in the source files under a `foo/bar` directory.
This switch only works when `--enable-logging` is also passed.
## --disable-gpu
Disables the use of the GPU in the renderer process. If this is set with the
`--disable-gpu-compositing` switch, *offscreen rendering* will use a software
output device, which has much better overall performance but lacks of WebGL
and 3D CSS support.
## --disable-gpu-compositing
Disables the use of the GPU compositing in the renderer process. This should be
set with the `--disable-gpu` switch for *offscreen rendering*.
``` javascript
const {app, BrowserWindow} = require('electron');
app.commandLine.appendSwitch('disable-gpu');
app.commandLine.appendSwitch('disable-gpu-compositing');
let win = new BrowserWindow({
webPreferences: {
offscreen: true
}
});
win.loadURL('http://github.com');
win.webContents.on('paint', (event, dirty, data) => {
updateBitmap(dirty, data);
});
```
[app]: app.md
[append-switch]: app.md#appcommandlineappendswitchswitch-value
[ready]: app.md#event-ready

View file

@ -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`

View file

@ -0,0 +1,58 @@
# 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 like this:
``` javascript
const {app} = require('electron');
app.commandLine.appendSwitch('disable-gpu');
app.commandLine.appendSwitch('disable-gpu-compositing');
```
## Usage
``` javascript
const {app, BrowserWindow} = require('electron');
app.commandLine.appendSwitch('disable-gpu');
app.commandLine.appendSwitch('disable-gpu-compositing');
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);
});
```

View file

@ -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_web_contents_view.cc',
'atom/browser/osr_web_contents_view.h',
'atom/browser/osr_output_device.cc',
'atom/browser/osr_output_device.h',
'atom/browser/osr_render_widget_host_view.cc',
'atom/browser/osr_render_widget_host_view.h',
'atom/browser/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',

View file

@ -1167,4 +1167,156 @@ describe('browser-window module', function () {
w.loadURL(server.url)
})
})
describe('offscreen rendering', function () {
it('creates offscreen window', function (done) {
this.timeout(10000)
let w_ = new BrowserWindow({
show: false,
width: 400,
height: 400,
webPreferences: {
backgroundThrottling: false,
offscreen: true
}
})
w_.loadURL('file://' + fixtures + '/api/offscreen-rendering.html')
w_.webContents.once('paint', function (event, rect, data, size) {
assert.notEqual(data.length, 0)
done()
})
})
describe('window.webContents.isOffscreen', function () {
it('has offscreen type', function () {
let w_ = new BrowserWindow({
show: false,
width: 400,
height: 400,
webPreferences: {
backgroundThrottling: false,
offscreen: true
}
})
w_.loadURL('file://' + fixtures + '/api/offscreen-rendering.html')
assert.equal(w_.webContents.isOffscreen(), true)
})
it('is a regular window', function () {
assert.equal(w.webContents.isOffscreen(), false)
})
})
describe('window.webContents.isPainting', function () {
it('is currently painting', function (done) {
this.timeout(10000)
let w_ = new BrowserWindow({
show: false,
width: 400,
height: 400,
webPreferences: {
backgroundThrottling: false,
offscreen: true
}
})
w_.loadURL('file://' + fixtures + '/api/offscreen-rendering.html')
w_.webContents.once('paint', function (event, rect, data, size) {
assert.equal(w_.webContents.isPainting(), true)
done()
})
})
})
describe('window.webContents.stopPainting', function () {
it('stops painting', function (done) {
this.timeout(10000)
let w_ = new BrowserWindow({
show: false,
width: 400,
height: 400,
webPreferences: {
backgroundThrottling: false,
offscreen: true
}
})
w_.loadURL('file://' + fixtures + '/api/offscreen-rendering.html')
w_.webContents.on('dom-ready', function () {
w_.webContents.stopPainting()
assert.equal(w_.webContents.isPainting(), false)
done()
})
})
})
describe('window.webContents.startPainting', function () {
it('starts painting', function (done) {
this.timeout(10000)
let w_ = new BrowserWindow({
show: false,
width: 400,
height: 400,
webPreferences: {
backgroundThrottling: false,
offscreen: true
}
})
w_.loadURL('file://' + fixtures + '/api/offscreen-rendering.html')
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()
})
})
})
})
describe('window.webContents.getFrameRate', function () {
it('has default frame rate', function (done) {
this.timeout(10000)
let w_ = new BrowserWindow({
show: false,
width: 400,
height: 400,
webPreferences: {
backgroundThrottling: false,
offscreen: true
}
})
w_.loadURL('file://' + fixtures + '/api/offscreen-rendering.html')
w_.webContents.once('paint', function (event, rect, data, size) {
assert.equal(w_.webContents.getFrameRate(), 60)
done()
})
})
})
describe('window.webContents.setFrameRate', function () {
it('has custom frame rate', function (done) {
this.timeout(10000)
let w_ = new BrowserWindow({
show: false,
width: 400,
height: 400,
webPreferences: {
backgroundThrottling: false,
offscreen: true
}
})
w_.loadURL('file://' + fixtures + '/api/offscreen-rendering.html')
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()
})
})
})
})
})
})

View file

@ -38,7 +38,7 @@ describe('webContents module', function () {
w.webContents.on('devtools-opened', function () {
const all = webContents.getAllWebContents().sort(function (a, b) {
return a.getId() - b.getId()
})
}).filter(function (wc) { return wc.getType() !== 'offscreen' })
assert.equal(all.length, 4)
assert.equal(all[0].getType(), 'window')

View file

@ -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>

2
vendor/brightray vendored

@ -1 +1 @@
Subproject commit ba150e6367904e14d102f60aa7e66601e4e2a8ab
Subproject commit f94cd198df0c78263689deae916bc9dcde375a81