summaryrefslogtreecommitdiffstats
path: root/webkit/glue
diff options
context:
space:
mode:
authorbrettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-10 17:56:53 +0000
committerbrettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-10 17:56:53 +0000
commitdf4bdd0741c7d8bd09f9f1ef7f318935cbb25356 (patch)
tree7d109eb90238d0134a0ba01a4443c853f61b858b /webkit/glue
parent20737699af37ed66f628e5222962c118d6a06175 (diff)
downloadchromium_src-df4bdd0741c7d8bd09f9f1ef7f318935cbb25356.zip
chromium_src-df4bdd0741c7d8bd09f9f1ef7f318935cbb25356.tar.gz
chromium_src-df4bdd0741c7d8bd09f9f1ef7f318935cbb25356.tar.bz2
First pass at implementing real Flush callbacks. This does not currently
include a test since that needs some work on threading and the testing interface. I plan on doing that in a separate pass. TEST=none BUG=none Review URL: http://codereview.chromium.org/2712002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@49415 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/glue')
-rw-r--r--webkit/glue/plugins/pepper_device_context_2d.cc176
-rw-r--r--webkit/glue/plugins/pepper_device_context_2d.h74
-rw-r--r--webkit/glue/plugins/pepper_plugin_instance.cc49
-rw-r--r--webkit/glue/plugins/pepper_plugin_instance.h27
4 files changed, 286 insertions, 40 deletions
diff --git a/webkit/glue/plugins/pepper_device_context_2d.cc b/webkit/glue/plugins/pepper_device_context_2d.cc
index 4a4b374..e549f0e 100644
--- a/webkit/glue/plugins/pepper_device_context_2d.cc
+++ b/webkit/glue/plugins/pepper_device_context_2d.cc
@@ -4,7 +4,11 @@
#include "webkit/glue/plugins/pepper_device_context_2d.h"
+#include <iterator>
+
#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/task.h"
#include "gfx/point.h"
#include "gfx/rect.h"
#include "skia/ext/platform_canvas.h"
@@ -14,6 +18,7 @@
#include "third_party/ppapi/c/ppb_device_context_2d.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "webkit/glue/plugins/pepper_image_data.h"
+#include "webkit/glue/plugins/pepper_plugin_instance.h"
#include "webkit/glue/plugins/pepper_plugin_module.h"
#include "webkit/glue/plugins/pepper_resource_tracker.h"
@@ -70,6 +75,12 @@ PP_Resource Create(PP_Module module_id, int32_t width, int32_t height,
return context->GetResource();
}
+bool IsDeviceContext2D(PP_Resource resource) {
+ scoped_refptr<DeviceContext2D> context(
+ ResourceTracker::Get()->GetAsDeviceContext2D(resource));
+ return !!context.get();
+}
+
bool Describe(PP_Resource device_context,
int32_t* width, int32_t* height, bool* is_always_opaque) {
scoped_refptr<DeviceContext2D> context(
@@ -120,6 +131,7 @@ bool Flush(PP_Resource device_context,
const PPB_DeviceContext2D ppb_devicecontext2d = {
&Create,
+ &IsDeviceContext2D,
&Describe,
&PaintImageData,
&Scroll,
@@ -159,7 +171,21 @@ struct DeviceContext2D::QueuedOperation {
scoped_refptr<ImageData> replace_image;
};
-DeviceContext2D::DeviceContext2D(PluginModule* module) : Resource(module) {
+DeviceContext2D::FlushCallbackData::FlushCallbackData(
+ PPB_DeviceContext2D_FlushCallback c,
+ void* d)
+ : callback_(c),
+ callback_data_(d) {
+}
+
+void DeviceContext2D::FlushCallbackData::Execute(PP_Resource device_context) {
+ callback_(device_context, callback_data_);
+}
+
+DeviceContext2D::DeviceContext2D(PluginModule* module)
+ : Resource(module),
+ bound_instance_(NULL),
+ flushed_any_data_(false) {
}
DeviceContext2D::~DeviceContext2D() {
@@ -266,42 +292,57 @@ bool DeviceContext2D::ReplaceContents(PP_Resource image) {
bool DeviceContext2D::Flush(PPB_DeviceContext2D_FlushCallback callback,
void* callback_data) {
+ // TODO(brettw) check that the current thread is not the main one and
+ // implement blocking flushes in this case.
+ if (!callback)
+ return false;
+
+ if (queued_operations_.empty())
+ return true; // Nothing to do.
+
+ gfx::Rect changed_rect;
for (size_t i = 0; i < queued_operations_.size(); i++) {
QueuedOperation& operation = queued_operations_[i];
+ gfx::Rect op_rect;
switch (operation.type) {
case QueuedOperation::PAINT:
ExecutePaintImageData(operation.paint_image.get(),
operation.paint_x, operation.paint_y,
- operation.paint_src_rect);
+ operation.paint_src_rect,
+ &op_rect);
break;
case QueuedOperation::SCROLL:
ExecuteScroll(operation.scroll_clip_rect,
- operation.scroll_dx, operation.scroll_dy);
+ operation.scroll_dx, operation.scroll_dy,
+ &op_rect);
break;
case QueuedOperation::REPLACE:
- ExecuteReplaceContents(operation.replace_image.get());
+ ExecuteReplaceContents(operation.replace_image.get(), &op_rect);
break;
}
+ changed_rect = changed_rect.Union(op_rect);
}
queued_operations_.clear();
- // TODO(brettw) implement invalidate and callbacks!
-
- // Cause the updated part of the screen to be repainted. This will happen
- // asynchronously.
- /*
- gfx::Rect dest_gfx_rect(src_rect->left, src_rect->top,
- src_rect->right - src_rect->left,
- src_rect->bottom - src_rect->top);
-
- plugin_delegate_->instance()->webplugin()->InvalidateRect(dest_gfx_rect);
-
- // Save the callback to execute later. See |unpainted_flush_callbacks_| in
- // the header file.
- if (callback) {
- unpainted_flush_callbacks_.push_back(
- FlushCallbackData(callback, id, context, user_data));
+ flushed_any_data_ = true;
+
+ // We need the rect to be in terms of the current clip rect of the plugin
+ // since that's what will actually be painted. If we issue an invalidate
+ // for a clipped-out region, WebKit will do nothing and we won't get any
+ // ViewInitiatedPaint/ViewFlushedPaint calls, leaving our callback stranded.
+ gfx::Rect visible_changed_rect;
+ if (bound_instance_ && !changed_rect.IsEmpty())
+ visible_changed_rect = bound_instance_->clip().Intersect(changed_rect);
+
+ if (bound_instance_ && !visible_changed_rect.IsEmpty()) {
+ unpainted_flush_callbacks_.push_back(FlushCallbackData(callback,
+ callback_data));
+ bound_instance_->InvalidateRect(visible_changed_rect);
+ } else {
+ // There's nothing visible to invalidate so just schedule the callback to
+ // execute in the next round of the message loop.
+ ScheduleOffscreenCallback(
+ FlushCallbackData(callback, callback_data));
}
-*/
return true;
}
@@ -345,6 +386,38 @@ bool DeviceContext2D::ReadImageData(PP_Resource image, int32_t x, int32_t y) {
return true;
}
+bool DeviceContext2D::BindToInstance(PluginInstance* new_instance) {
+ if (bound_instance_ == new_instance)
+ return true; // Rebinding the same device, nothing to do.
+ if (bound_instance_ && new_instance)
+ return false; // Can't change a bound device.
+
+ if (!new_instance) {
+ // When the device is detached, we'll not get any more paint callbacks so
+ // we need to clear the list, but we still want to issue any pending
+ // callbacks to the plugin.
+ for (size_t i = 0; i < unpainted_flush_callbacks_.size(); i++)
+ ScheduleOffscreenCallback(unpainted_flush_callbacks_[i]);
+ for (size_t i = 0; i < painted_flush_callbacks_.size(); i++)
+ ScheduleOffscreenCallback(painted_flush_callbacks_[i]);
+ unpainted_flush_callbacks_.clear();
+ painted_flush_callbacks_.clear();
+ } else if (flushed_any_data_) {
+ // Only schedule a paint if this backing store has had any data flushed to
+ // it. This is an optimization. A "normal" plugin will first allocated a
+ // backing store, bind it, and then execute their normal painting and
+ // update loop. If binding a device always invalidated, it would mean we
+ // would get one paint for the bind, and one for the first time the plugin
+ // actually painted something. By not bothering to schedule an invalidate
+ // when an empty device is initially bound, we can save an extra paint for
+ // many plugins during the critical page initialization phase.
+ new_instance->InvalidateRect(gfx::Rect());
+ }
+
+ bound_instance_ = new_instance;
+ return true;
+}
+
void DeviceContext2D::Paint(WebKit::WebCanvas* canvas,
const gfx::Rect& plugin_rect,
const gfx::Rect& paint_rect) {
@@ -389,27 +462,47 @@ void DeviceContext2D::Paint(WebKit::WebCanvas* canvas,
#endif
}
+void DeviceContext2D::ViewInitiatedPaint() {
+ // Move all "unpainted" callbacks to the painted state. See
+ // |unpainted_flush_callbacks_| in the header for more.
+ std::copy(unpainted_flush_callbacks_.begin(),
+ unpainted_flush_callbacks_.end(),
+ std::back_inserter(painted_flush_callbacks_));
+ unpainted_flush_callbacks_.clear();
+}
+
+void DeviceContext2D::ViewFlushedPaint() {
+ // Notify all "painted" callbacks. See |unpainted_flush_callbacks_| in the
+ // header for more.
+ for (size_t i = 0; i < painted_flush_callbacks_.size(); i++)
+ painted_flush_callbacks_[i].Execute(GetResource());
+ painted_flush_callbacks_.clear();
+}
+
void DeviceContext2D::ExecutePaintImageData(ImageData* image,
int x, int y,
- const gfx::Rect& src_rect) {
+ const gfx::Rect& src_rect,
+ gfx::Rect* invalidated_rect) {
+ // Ensure the source image is mapped to read from it.
+ ImageDataAutoMapper auto_mapper(image);
+ if (!auto_mapper.is_valid())
+ return;
+
// Portion within the source image to cut out.
SkIRect src_irect = { src_rect.x(), src_rect.y(),
src_rect.right(), src_rect.bottom() };
// Location within the backing store to copy to.
- SkRect dest_rect = { SkIntToScalar(x + src_rect.x()),
- SkIntToScalar(y + src_rect.y()),
- SkIntToScalar(x + src_rect.right()),
- SkIntToScalar(y + src_rect.bottom()) };
+ *invalidated_rect = src_rect;
+ invalidated_rect->Offset(x, y);
+ SkRect dest_rect = { SkIntToScalar(invalidated_rect->x()),
+ SkIntToScalar(invalidated_rect->y()),
+ SkIntToScalar(invalidated_rect->right()),
+ SkIntToScalar(invalidated_rect->bottom()) };
// We're guaranteed to have a mapped canvas since we mapped it in Init().
skia::PlatformCanvas* backing_canvas = image_data_->mapped_canvas();
- // Ensure the source image is mapped to read from it.
- ImageDataAutoMapper auto_mapper(image);
- if (!auto_mapper.is_valid())
- return;
-
// We want to replace the contents of the bitmap rather than blend.
SkPaint paint;
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
@@ -417,12 +510,29 @@ void DeviceContext2D::ExecutePaintImageData(ImageData* image,
&src_irect, dest_rect, &paint);
}
-void DeviceContext2D::ExecuteScroll(const gfx::Rect& clip, int dx, int dy) {
+void DeviceContext2D::ExecuteScroll(const gfx::Rect& clip, int dx, int dy,
+ gfx::Rect* invalidated_rect) {
// FIXME(brettw)
}
-void DeviceContext2D::ExecuteReplaceContents(ImageData* image) {
+void DeviceContext2D::ExecuteReplaceContents(ImageData* image,
+ gfx::Rect* invalidated_rect) {
image_data_->Swap(image);
+ *invalidated_rect = gfx::Rect(0, 0,
+ image_data_->width(), image_data_->height());
+}
+
+void DeviceContext2D::ScheduleOffscreenCallback(
+ const FlushCallbackData& callback) {
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &DeviceContext2D::ExecuteOffscreenCallback,
+ callback));
+}
+
+void DeviceContext2D::ExecuteOffscreenCallback(FlushCallbackData data) {
+ data.Execute(GetResource());
}
} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_device_context_2d.h b/webkit/glue/plugins/pepper_device_context_2d.h
index 998f7c0..bb488ab 100644
--- a/webkit/glue/plugins/pepper_device_context_2d.h
+++ b/webkit/glue/plugins/pepper_device_context_2d.h
@@ -21,6 +21,7 @@ class Rect;
namespace pepper {
class ImageData;
+class PluginInstance;
class PluginModule;
class DeviceContext2D : public Resource {
@@ -48,26 +49,91 @@ class DeviceContext2D : public Resource {
void* callback_data);
bool ReadImageData(PP_Resource image, int32_t x, int32_t y);
+ // Assciates this device with the given plugin instance. You can pass NULL to
+ // clear the existing device. Returns true on success. In this case, a
+ // repaint of the page will also be scheduled. Failure means that the device
+ // is already bound to a different instance, and nothing will happen.
+ bool BindToInstance(PluginInstance* new_instance);
+
+ // Paints the current backing store to the web page.
void Paint(WebKit::WebCanvas* canvas,
const gfx::Rect& plugin_rect,
const gfx::Rect& paint_rect);
+ // Notifications that the view has rendered the page and that it has been
+ // flushed to the screen. These messages are used to send Flush callbacks to
+ // the plugin. See
+ void ViewInitiatedPaint();
+ void ViewFlushedPaint();
+
private:
+ // Tracks a call to flush that requires a callback.
+ // See unpainted_flush_callbacks_ below.
+ class FlushCallbackData {
+ public:
+ FlushCallbackData(PPB_DeviceContext2D_FlushCallback c, void* d);
+
+ void Execute(PP_Resource device_context);
+
+ private:
+ PPB_DeviceContext2D_FlushCallback callback_;
+ void* callback_data_;
+ };
+ typedef std::vector<FlushCallbackData> FlushCallbackVector;
+
// Called internally to execute the different queued commands. The
- // parameters to these functions will have already been validated.
+ // parameters to these functions will have already been validated. The last
+ // rect argument will be filled by each function with the area affected by
+ // the update that requires invalidation. If there were no pixels changed,
+ // this rect can be untouched.
void ExecutePaintImageData(ImageData* image,
int x, int y,
- const gfx::Rect& src_rect);
- void ExecuteScroll(const gfx::Rect& clip, int dx, int dy);
- void ExecuteReplaceContents(ImageData* image);
+ const gfx::Rect& src_rect,
+ gfx::Rect* invalidated_rect);
+ void ExecuteScroll(const gfx::Rect& clip, int dx, int dy,
+ gfx::Rect* invalidated_rect);
+ void ExecuteReplaceContents(ImageData* image,
+ gfx::Rect* invalidated_rect);
+
+ // Schedules the offscreen callback to be fired at a future time. This
+ // will add the given item to the offscreen_flush_callbacks_ vector.
+ void ScheduleOffscreenCallback(const FlushCallbackData& callback);
+
+ // Function scheduled to execute by ScheduleOffscreenCallback that actually
+ // issues the offscreen callbacks.
+ void ExecuteOffscreenCallback(FlushCallbackData data);
scoped_refptr<ImageData> image_data_;
+ // Non-owning pointer to the plugin instance this device context is currently
+ // bound to, if any. If the device context is currently unbound, this will
+ // be NULL.
+ PluginInstance* bound_instance_;
+
// Keeps track of all drawing commands queued before a Flush call.
struct QueuedOperation;
typedef std::vector<QueuedOperation> OperationQueue;
OperationQueue queued_operations_;
+ // Indicates whether any changes have been flushed to the backing store.
+ // This is initially false and is set to true at the first Flush() call.
+ bool flushed_any_data_;
+
+ // The plugin may be constantly giving us paint messages. "Unpainted" ones
+ // are paint requests which have never been painted. These could have been
+ // done while the RenderView was already waiting for an ACK from a previous
+ // paint, so won't generate a new one yet.
+ //
+ // "Painted" ones are those paints that have been painted by RenderView, but
+ // for which the ACK from the browser has not yet been received.
+ //
+ // When we get updates from a plugin with a callback, it is first added to
+ // the unpainted callbacks. When the renderer has initiated a paint, we'll
+ // move it to the painted callbacks list. When the renderer receives a flush,
+ // we'll execute the callback and remove it from the list.
+ FlushCallbackVector unpainted_flush_callbacks_;
+ FlushCallbackVector painted_flush_callbacks_;
+
DISALLOW_COPY_AND_ASSIGN(DeviceContext2D);
};
diff --git a/webkit/glue/plugins/pepper_plugin_instance.cc b/webkit/glue/plugins/pepper_plugin_instance.cc
index 15bff8c..a2f83b8 100644
--- a/webkit/glue/plugins/pepper_plugin_instance.cc
+++ b/webkit/glue/plugins/pepper_plugin_instance.cc
@@ -19,6 +19,7 @@
#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
#include "third_party/WebKit/WebKit/chromium/public/WebPluginContainer.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebRect.h"
#include "webkit/glue/plugins/pepper_device_context_2d.h"
#include "webkit/glue/plugins/pepper_plugin_module.h"
#include "webkit/glue/plugins/pepper_resource_tracker.h"
@@ -172,6 +173,15 @@ void PluginInstance::Paint(WebKit::WebCanvas* canvas,
device_context_2d_->Paint(canvas, plugin_rect, paint_rect);
}
+void PluginInstance::InvalidateRect(const gfx::Rect& rect) {
+ if (!container_ || position_.IsEmpty())
+ return; // Nothing to do.
+ if (rect.IsEmpty())
+ container_->invalidate();
+ else
+ container_->invalidateRect(rect);
+}
+
PP_Var PluginInstance::GetWindowObject() {
if (!container_)
return PP_MakeVoid();
@@ -191,6 +201,16 @@ PP_Var PluginInstance::GetOwnerElementObject() {
}
bool PluginInstance::BindGraphicsDeviceContext(PP_Resource device_id) {
+ if (!device_id) {
+ // Special-case clearing the current device.
+ if (device_context_2d_) {
+ device_context_2d_->BindToInstance(NULL);
+ device_context_2d_ = NULL;
+ InvalidateRect(gfx::Rect());
+ }
+ return true;
+ }
+
scoped_refptr<Resource> device_resource =
ResourceTracker::Get()->GetResource(device_id);
if (!device_resource.get())
@@ -198,8 +218,10 @@ bool PluginInstance::BindGraphicsDeviceContext(PP_Resource device_id) {
DeviceContext2D* device_2d = device_resource->AsDeviceContext2D();
if (device_2d) {
+ if (!device_2d->BindToInstance(this))
+ return false; // Can't bind to more than one instance.
device_context_2d_ = device_2d;
- // TODO(brettw) repaint the plugin.
+ // BindToInstance will have invalidated the plugin if necessary.
}
return true;
@@ -270,10 +292,31 @@ PP_Var PluginInstance::GetInstanceObject() {
void PluginInstance::ViewChanged(const gfx::Rect& position,
const gfx::Rect& clip) {
+ position_ = position;
+ if (clip.IsEmpty()) {
+ // WebKit can give weird (x,y) positions for empty clip rects (since the
+ // position technically doesn't matter). But we want to make these
+ // consistent since this is given to the plugin, so force everything to 0
+ // in the "everything is clipped" case.
+ clip_ = gfx::Rect();
+ } else {
+ clip_ = clip;
+ }
+
PP_Rect pp_position, pp_clip;
- RectToPPRect(position, &pp_position);
- RectToPPRect(clip, &pp_clip);
+ RectToPPRect(position_, &pp_position);
+ RectToPPRect(clip_, &pp_clip);
instance_interface_->ViewChanged(GetPPInstance(), &pp_position, &pp_clip);
}
+void PluginInstance::ViewInitiatedPaint() {
+ if (device_context_2d_)
+ device_context_2d_->ViewInitiatedPaint();
+}
+
+void PluginInstance::ViewFlushedPaint() {
+ if (device_context_2d_)
+ device_context_2d_->ViewFlushedPaint();
+}
+
} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_plugin_instance.h b/webkit/glue/plugins/pepper_plugin_instance.h
index 3b045d4..aaf9bc0 100644
--- a/webkit/glue/plugins/pepper_plugin_instance.h
+++ b/webkit/glue/plugins/pepper_plugin_instance.h
@@ -10,6 +10,7 @@
#include "base/basictypes.h"
#include "base/ref_counted.h"
+#include "gfx/rect.h"
#include "third_party/ppapi/c/pp_instance.h"
#include "third_party/ppapi/c/pp_resource.h"
#include "third_party/WebKit/WebKit/chromium/public/WebCanvas.h"
@@ -49,12 +50,22 @@ class PluginInstance : public base::RefCounted<PluginInstance> {
PluginDelegate* delegate() const { return delegate_; }
PluginModule* module() const { return module_.get(); }
+ const gfx::Rect& position() const { return position_; }
+ const gfx::Rect& clip() const { return clip_; }
+
PP_Instance GetPPInstance();
+ // Paints the current backing store to the web page.
void Paint(WebKit::WebCanvas* canvas,
const gfx::Rect& plugin_rect,
const gfx::Rect& paint_rect);
+ // Schedules a paint of the page for the given region. The coordinates are
+ // relative to the top-left of the plugin. This does nothing if the plugin
+ // has not yet been positioned. You can supply an empty gfx::Rect() to
+ // invalidate the entire plugin.
+ void InvalidateRect(const gfx::Rect& rect);
+
// PPB_Instance implementation.
PP_Var GetWindowObject();
PP_Var GetOwnerElementObject();
@@ -70,6 +81,12 @@ class PluginInstance : public base::RefCounted<PluginInstance> {
PP_Var GetInstanceObject();
void ViewChanged(const gfx::Rect& position, const gfx::Rect& clip);
+ // Notifications that the view has rendered the page and that it has been
+ // flushed to the screen. These messages are used to send Flush callbacks to
+ // the plugin for DeviceContext2D.
+ void ViewInitiatedPaint();
+ void ViewFlushedPaint();
+
private:
PluginDelegate* delegate_;
scoped_refptr<PluginModule> module_;
@@ -78,6 +95,16 @@ class PluginInstance : public base::RefCounted<PluginInstance> {
// NULL until we have been initialized.
WebKit::WebPluginContainer* container_;
+ // Position in the viewport (which moves as the page is scrolled) of this
+ // plugin. This will be a 0-sized rectangle if the plugin has not yet been
+ // laid out.
+ gfx::Rect position_;
+
+ // Current clip rect. This will be empty if the plugin is not currently
+ // visible. This is in the plugin's coordinate system, so fully visible will
+ // be (0, 0, w, h) regardless of scroll position.
+ gfx::Rect clip_;
+
// The current device context for painting in 2D.
scoped_refptr<DeviceContext2D> device_context_2d_;