Merge pull request #6703 from electron/merge-offscreen

Merge #6691
This commit is contained in:
Cheng Zhao 2016-08-03 15:58:31 +09:00 коммит произвёл GitHub
Родитель 1fa18aeba2 2e8ccfd8df
Коммит 53831d71e3
20 изменённых файлов: 1952 добавлений и 19 удалений

Просмотреть файл

@ -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,9 +608,16 @@ 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,
const GURL& validated_url) {
@ -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')
})
})
})
})

11
spec/fixtures/api/offscreen-rendering.html поставляемый Normal 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 поставляемый

@ -1 +1 @@
Subproject commit ba150e6367904e14d102f60aa7e66601e4e2a8ab
Subproject commit 7b5f321cd44f2083c59ab94f2bb23cc3f59a5c2b