summaryrefslogtreecommitdiffstats
path: root/chrome/renderer
diff options
context:
space:
mode:
authorkalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-24 07:43:06 +0000
committerkalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-24 07:43:06 +0000
commit204d8ee08f3429f45229fc99498846c62c5c7d73 (patch)
tree65c26514e94fa52e0d730705d7789d333f68510e /chrome/renderer
parentfb6278ad7938341acf11f0562dc4a3066e9b28a5 (diff)
downloadchromium_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/renderer')
-rw-r--r--chrome/renderer/extensions/miscellaneous_bindings.cc72
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>());
}
};