Merge pull request #6164 from MaxWhere/master

beginFrameSubscription bugfix and improvement
This commit is contained in:
Cheng Zhao 2016-06-26 02:39:59 +00:00 committed by GitHub
commit 3d2ad0080d
7 changed files with 94 additions and 31 deletions

View file

@ -1182,11 +1182,20 @@ void WebContents::SendInputEvent(v8::Isolate* isolate,
} }
void WebContents::BeginFrameSubscription( void WebContents::BeginFrameSubscription(
const FrameSubscriber::FrameCaptureCallback& callback) { mate::Arguments* args) {
FrameSubscriber::FrameCaptureCallback callback;
bool only_dirty = false;
if (!args->GetNext(&callback)) {
args->GetNext(&only_dirty);
if (!args->GetNext(&callback))
args->ThrowTypeError("'callback' must be defined");
}
const auto view = web_contents()->GetRenderWidgetHostView(); const auto view = web_contents()->GetRenderWidgetHostView();
if (view) { if (view) {
std::unique_ptr<FrameSubscriber> frame_subscriber(new FrameSubscriber( std::unique_ptr<FrameSubscriber> frame_subscriber(new FrameSubscriber(
isolate(), view, callback)); isolate(), view, callback, only_dirty));
view->BeginFrameSubscription(std::move(frame_subscriber)); view->BeginFrameSubscription(std::move(frame_subscriber));
} }
} }

View file

@ -139,8 +139,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
void SendInputEvent(v8::Isolate* isolate, v8::Local<v8::Value> input_event); void SendInputEvent(v8::Isolate* isolate, v8::Local<v8::Value> input_event);
// Subscribe to the frame updates. // Subscribe to the frame updates.
void BeginFrameSubscription( void BeginFrameSubscription(mate::Arguments* args);
const FrameSubscriber::FrameCaptureCallback& callback);
void EndFrameSubscription(); void EndFrameSubscription();
// Methods for creating <webview>. // Methods for creating <webview>.

View file

@ -6,6 +6,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "atom/common/node_includes.h" #include "atom/common/node_includes.h"
#include "atom/common/native_mate_converters/gfx_converter.h"
#include "content/public/browser/render_widget_host.h" #include "content/public/browser/render_widget_host.h"
namespace atom { namespace atom {
@ -14,12 +15,14 @@ namespace api {
FrameSubscriber::FrameSubscriber(v8::Isolate* isolate, FrameSubscriber::FrameSubscriber(v8::Isolate* isolate,
content::RenderWidgetHostView* view, content::RenderWidgetHostView* view,
const FrameCaptureCallback& callback) const FrameCaptureCallback& callback,
: isolate_(isolate), view_(view), callback_(callback), weak_factory_(this) { bool only_dirty)
: isolate_(isolate), view_(view), callback_(callback),
only_dirty_(only_dirty), weak_factory_(this) {
} }
bool FrameSubscriber::ShouldCaptureFrame( bool FrameSubscriber::ShouldCaptureFrame(
const gfx::Rect& damage_rect, const gfx::Rect& dirty_rect,
base::TimeTicks present_time, base::TimeTicks present_time,
scoped_refptr<media::VideoFrame>* storage, scoped_refptr<media::VideoFrame>* storage,
DeliverFrameCallback* callback) { DeliverFrameCallback* callback) {
@ -27,21 +30,27 @@ bool FrameSubscriber::ShouldCaptureFrame(
if (!view_ || !host) if (!view_ || !host)
return false; return false;
const auto size = view_->GetVisibleViewportSize(); if (dirty_rect.IsEmpty())
return false;
gfx::Rect rect = gfx::Rect(view_->GetVisibleViewportSize());
if (only_dirty_)
rect = dirty_rect;
host->CopyFromBackingStore( host->CopyFromBackingStore(
gfx::Rect(size), rect,
size, rect.size(),
base::Bind(&FrameSubscriber::OnFrameDelivered, base::Bind(&FrameSubscriber::OnFrameDelivered,
weak_factory_.GetWeakPtr(), callback_), weak_factory_.GetWeakPtr(), callback_, rect),
kBGRA_8888_SkColorType); kBGRA_8888_SkColorType);
return false; return false;
} }
void FrameSubscriber::OnFrameDelivered(const FrameCaptureCallback& callback, void FrameSubscriber::OnFrameDelivered(const FrameCaptureCallback& callback,
const SkBitmap& bitmap, content::ReadbackResponse response) { const gfx::Rect& damage_rect, const SkBitmap& bitmap,
if (bitmap.computeSize64() == 0) content::ReadbackResponse response) {
if (response != content::ReadbackResponse::READBACK_SUCCESS)
return; return;
v8::Locker locker(isolate_); v8::Locker locker(isolate_);
@ -57,7 +66,10 @@ void FrameSubscriber::OnFrameDelivered(const FrameCaptureCallback& callback,
reinterpret_cast<uint8_t*>(node::Buffer::Data(buffer.ToLocalChecked())), reinterpret_cast<uint8_t*>(node::Buffer::Data(buffer.ToLocalChecked())),
rgb_arr_size); rgb_arr_size);
callback_.Run(buffer.ToLocalChecked()); v8::Local<v8::Value> damage =
mate::Converter<gfx::Rect>::ToV8(isolate_, damage_rect);
callback_.Run(buffer.ToLocalChecked(), damage);
} }
} // namespace api } // namespace api

View file

@ -20,11 +20,13 @@ namespace api {
class FrameSubscriber : public content::RenderWidgetHostViewFrameSubscriber { class FrameSubscriber : public content::RenderWidgetHostViewFrameSubscriber {
public: public:
using FrameCaptureCallback = base::Callback<void(v8::Local<v8::Value>)>; using FrameCaptureCallback =
base::Callback<void(v8::Local<v8::Value>, v8::Local<v8::Value>)>;
FrameSubscriber(v8::Isolate* isolate, FrameSubscriber(v8::Isolate* isolate,
content::RenderWidgetHostView* view, content::RenderWidgetHostView* view,
const FrameCaptureCallback& callback); const FrameCaptureCallback& callback,
bool only_dirty);
bool ShouldCaptureFrame(const gfx::Rect& damage_rect, bool ShouldCaptureFrame(const gfx::Rect& damage_rect,
base::TimeTicks present_time, base::TimeTicks present_time,
@ -33,11 +35,13 @@ class FrameSubscriber : public content::RenderWidgetHostViewFrameSubscriber {
private: private:
void OnFrameDelivered(const FrameCaptureCallback& callback, void OnFrameDelivered(const FrameCaptureCallback& callback,
const SkBitmap& bitmap, content::ReadbackResponse response); const gfx::Rect& damage_rect, const SkBitmap& bitmap,
content::ReadbackResponse response);
v8::Isolate* isolate_; v8::Isolate* isolate_;
content::RenderWidgetHostView* view_; content::RenderWidgetHostView* view_;
FrameCaptureCallback callback_; FrameCaptureCallback callback_;
bool only_dirty_;
base::WeakPtrFactory<FrameSubscriber> weak_factory_; base::WeakPtrFactory<FrameSubscriber> weak_factory_;

View file

@ -919,12 +919,14 @@ For the `mouseWheel` event, the `event` object also have following properties:
* `hasPreciseScrollingDeltas` Boolean * `hasPreciseScrollingDeltas` Boolean
* `canScroll` Boolean * `canScroll` Boolean
### `webContents.beginFrameSubscription(callback)` ### `webContents.beginFrameSubscription([onlyDirty ,]callback)`
* `onlyDirty` Boolean
* `callback` Function * `callback` Function
Begin subscribing for presentation events and captured frames, the `callback` Begin subscribing for presentation events and captured frames, the `callback`
will be called with `callback(frameBuffer)` when there is a presentation event. will be called with `callback(frameBuffer, dirtyRect)` when there is a
presentation event.
The `frameBuffer` is a `Buffer` that contains raw pixel data. On most machines, The `frameBuffer` is a `Buffer` that contains raw pixel data. On most machines,
the pixel data is effectively stored in 32bit BGRA format, but the actual the pixel data is effectively stored in 32bit BGRA format, but the actual
@ -932,6 +934,11 @@ representation depends on the endianness of the processor (most modern
processors are little-endian, on machines with big-endian processors the data processors are little-endian, on machines with big-endian processors the data
is in 32bit ARGB format). is in 32bit ARGB format).
The `dirtyRect` is an object with `x, y, width, height` properties that
describes which part of the page was repainted. If `onlyDirty` is set to
`true`, `frameBuffer` will only contain the repainted area. `onlyDirty`
defaults to `false`.
### `webContents.endFrameSubscription()` ### `webContents.endFrameSubscription()`
End subscribing for frame presentation events. End subscribing for frame presentation events.

View file

@ -603,21 +603,42 @@ describe('browser-window module', function () {
}) })
describe('beginFrameSubscription method', function () { describe('beginFrameSubscription method', function () {
this.timeout(20000) it('subscribes to frame updates', function (done) {
this.timeout(20000)
it('subscribes frame updates', function (done) { w.loadURL('file://' + fixtures + '/api/frame-subscriber.html')
let called = false w.webContents.on('dom-ready', function () {
w.loadURL('file://' + fixtures + '/api/blank.html') w.webContents.beginFrameSubscription(function (data) {
w.webContents.beginFrameSubscription(function (data) { assert.notEqual(data.length, 0)
// This callback might be called twice. w.webContents.endFrameSubscription()
if (called) return done()
called = true })
assert.notEqual(data.length, 0)
w.webContents.endFrameSubscription()
done()
}) })
}) })
it('subscribes to frame updates (only dirty rectangle)', function (done) {
this.timeout(20000)
w.loadURL('file://' + fixtures + '/api/frame-subscriber.html')
w.webContents.on('dom-ready', function () {
w.webContents.beginFrameSubscription(true, function (data) {
assert.notEqual(data.length, 0)
w.webContents.endFrameSubscription()
done()
})
})
})
it('throws error when subscriber is not well defined', function (done) {
this.timeout(20000)
w.loadURL('file://' + fixtures + '/api/frame-subscriber.html')
try{
w.webContents.beginFrameSubscription(true, true)
} catch(e) {
done()
}
})
}) })
describe('savePage method', function () { describe('savePage method', function () {

11
spec/fixtures/api/frame-subscriber.html vendored Normal file
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)
}, 100)
</script>
</html>