diff options
author | kalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-24 07:43:06 +0000 |
---|---|---|
committer | kalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-24 07:43:06 +0000 |
commit | 204d8ee08f3429f45229fc99498846c62c5c7d73 (patch) | |
tree | 65c26514e94fa52e0d730705d7789d333f68510e /chrome | |
parent | fb6278ad7938341acf11f0562dc4a3066e9b28a5 (diff) | |
download | chromium_src-204d8ee08f3429f45229fc99498846c62c5c7d73.zip chromium_src-204d8ee08f3429f45229fc99498846c62c5c7d73.tar.gz chromium_src-204d8ee08f3429f45229fc99498846c62c5c7d73.tar.bz2 |
Run the JS callbacks that execute on a v8 GC via MiscellaneousBindings::BoundToGC
in the current MessageLoop rather than re-entrantly, to avoid executing any JS in an
unexpected state.
BUG=258526
Review URL: https://chromiumcodereview.appspot.com/19670020
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@213372 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/renderer/extensions/miscellaneous_bindings.cc | 72 |
1 files changed, 45 insertions, 27 deletions
diff --git a/chrome/renderer/extensions/miscellaneous_bindings.cc b/chrome/renderer/extensions/miscellaneous_bindings.cc index 8921d77..004aaa3 100644 --- a/chrome/renderer/extensions/miscellaneous_bindings.cc +++ b/chrome/renderer/extensions/miscellaneous_bindings.cc @@ -9,7 +9,9 @@ #include "base/basictypes.h" #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/lazy_instance.h" +#include "base/message_loop/message_loop.h" #include "base/values.h" #include "chrome/common/extensions/extension_messages.h" #include "chrome/common/extensions/message_bundle.h" @@ -160,39 +162,55 @@ class ExtensionImpl : public extensions::ChromeV8Extension { } } - struct GCCallbackArgs { - GCCallbackArgs(v8::Handle<v8::Object> object, - v8::Handle<v8::Function> callback) - : object(object), callback(callback) {} - - extensions::ScopedPersistent<v8::Object> object; - extensions::ScopedPersistent<v8::Function> callback; + // Holds a |callback| to run sometime after |object| is GC'ed. |callback| will + // not be executed re-entrantly to avoid running JS in an unexpected state. + class GCCallback { + public: + static void Bind(v8::Handle<v8::Object> object, + v8::Handle<v8::Function> callback) { + GCCallback* cb = new GCCallback(object, callback); + cb->object_.MakeWeak(cb, NearDeathCallback); + } private: - DISALLOW_COPY_AND_ASSIGN(GCCallbackArgs); - }; + static void NearDeathCallback(v8::Isolate* isolate, + v8::Persistent<v8::Object>* object, + GCCallback* self) { + // v8 says we need to explicitly reset weak handles from their callbacks. + // It's not implicit as one might expect. + self->object_.reset(); + base::MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&GCCallback::RunCallback, base::Owned(self))); + } - static void GCCallback(v8::Isolate* isolate, - v8::Persistent<v8::Object>* object, - GCCallbackArgs* args) { - v8::HandleScope handle_scope; - v8::Handle<v8::Context> context = args->callback->CreationContext(); - v8::Context::Scope context_scope(context); - WebKit::WebScopedMicrotaskSuppression suppression; - // Wrap in try/catch here so that we don't call into any message/exception - // handlers during GC. That is a recipe for pain. - v8::TryCatch trycatch; - args->callback->Call(context->Global(), 0, NULL); - delete args; - } + GCCallback(v8::Handle<v8::Object> object, v8::Handle<v8::Function> callback) + : object_(object), callback_(callback) { + } + + void RunCallback() { + v8::HandleScope handle_scope; + v8::Handle<v8::Context> context = callback_->CreationContext(); + if (context.IsEmpty()) + return; + v8::Context::Scope context_scope(context); + WebKit::WebScopedMicrotaskSuppression suppression; + callback_->Call(context->Global(), 0, NULL); + } + + extensions::ScopedPersistent<v8::Object> object_; + extensions::ScopedPersistent<v8::Function> callback_; + + DISALLOW_COPY_AND_ASSIGN(GCCallback); + }; - // Binds a callback to be invoked when the given object is garbage collected. + // void BindToGC(object, callback) + // + // Binds |callback| to be invoked *sometime after* |object| is garbage + // collected. We don't call the method re-entrantly so as to avoid executing + // JS in some bizarro undefined mid-GC state. void BindToGC(const v8::FunctionCallbackInfo<v8::Value>& args) { CHECK(args.Length() == 2 && args[0]->IsObject() && args[1]->IsFunction()); - GCCallbackArgs* context = new GCCallbackArgs( - v8::Handle<v8::Object>::Cast(args[0]), - v8::Handle<v8::Function>::Cast(args[1])); - context->object.MakeWeak(context, GCCallback); + GCCallback::Bind(args[0].As<v8::Object>(), args[1].As<v8::Function>()); } }; |