Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(66)

Unified Diff: extensions/renderer/render_frame_observer_natives.cc

Issue 1684953002: Fix re-entrancy and lifetime issue in RenderFrameObserverNatives::OnDocumentElementCreated (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Nits + Invalidate weak ptrs upon Invalidate() Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « extensions/renderer/render_frame_observer_natives.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: extensions/renderer/render_frame_observer_natives.cc
diff --git a/extensions/renderer/render_frame_observer_natives.cc b/extensions/renderer/render_frame_observer_natives.cc
index c3a147ddd4b657ec3e31f4cb9807918185a54084..1c1f06e87f3217890d3519e426f51be50ebc4542 100644
--- a/extensions/renderer/render_frame_observer_natives.cc
+++ b/extensions/renderer/render_frame_observer_natives.cc
@@ -19,43 +19,29 @@ namespace {
// Deletes itself when done.
class LoadWatcher : public content::RenderFrameObserver {
public:
- LoadWatcher(ScriptContext* context,
- content::RenderFrame* frame,
- v8::Local<v8::Function> cb)
- : content::RenderFrameObserver(frame),
- context_(context),
- callback_(context->isolate(), cb) {
- if (ExtensionFrameHelper::Get(frame)->
- did_create_current_document_element()) {
- // If the document element is already created, then we can call the
- // callback immediately (though post it to the message loop so as to not
- // call it re-entrantly).
- // The Unretained is safe because this class manages its own lifetime.
- base::MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(&LoadWatcher::CallbackAndDie, base::Unretained(this),
- true));
- }
+ LoadWatcher(content::RenderFrame* frame,
+ const base::Callback<void(bool)>& callback)
+ : content::RenderFrameObserver(frame), callback_(callback) {}
+
+ void DidCreateDocumentElement() override {
+ // The callback must be run as soon as the root element is available.
+ // Running the callback may trigger DidCreateDocumentElement or
+ // DidFailProvisionalLoad, so delete this before running the callback.
+ base::Callback<void(bool)> callback = callback_;
+ delete this;
+ callback.Run(true);
}
- void DidCreateDocumentElement() override { CallbackAndDie(true); }
-
void DidFailProvisionalLoad(const blink::WebURLError& error) override {
- CallbackAndDie(false);
- }
-
- private:
- void CallbackAndDie(bool succeeded) {
- v8::Isolate* isolate = context_->isolate();
- v8::HandleScope handle_scope(isolate);
- v8::Local<v8::Value> args[] = {v8::Boolean::New(isolate, succeeded)};
- context_->CallFunction(v8::Local<v8::Function>::New(isolate, callback_),
- arraysize(args), args);
+ // Use PostTask to avoid running user scripts while handling this
+ // DidFailProvisionalLoad notification.
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(callback_, false));
delete this;
}
- ScriptContext* context_;
- v8::Global<v8::Function> callback_;
+ private:
+ base::Callback<void(bool)> callback_;
DISALLOW_COPY_AND_ASSIGN(LoadWatcher);
};
@@ -63,13 +49,20 @@ class LoadWatcher : public content::RenderFrameObserver {
} // namespace
RenderFrameObserverNatives::RenderFrameObserverNatives(ScriptContext* context)
- : ObjectBackedNativeHandler(context) {
+ : ObjectBackedNativeHandler(context), weak_ptr_factory_(this) {
RouteFunction(
"OnDocumentElementCreated",
base::Bind(&RenderFrameObserverNatives::OnDocumentElementCreated,
base::Unretained(this)));
}
+RenderFrameObserverNatives::~RenderFrameObserverNatives() {}
+
+void RenderFrameObserverNatives::Invalidate() {
+ weak_ptr_factory_.InvalidateWeakPtrs();
+ ObjectBackedNativeHandler::Invalidate();
+}
+
void RenderFrameObserverNatives::OnDocumentElementCreated(
const v8::FunctionCallbackInfo<v8::Value>& args) {
CHECK(args.Length() == 2);
@@ -84,9 +77,32 @@ void RenderFrameObserverNatives::OnDocumentElementCreated(
return;
}
- new LoadWatcher(context(), frame, args[1].As<v8::Function>());
+ v8::Global<v8::Function> v8_callback(context()->isolate(),
Devlin 2016/02/10 20:30:47 Something else I just thought of: v8::Globals are
robwu 2016/02/10 20:45:49 I don't know, I'm a complete noob with V8 (I just
Devlin 2016/02/10 20:59:00 Let's go with this for now - it's certainly better
+ args[1].As<v8::Function>());
+ base::Callback<void(bool)> callback(
+ base::Bind(&RenderFrameObserverNatives::InvokeCallback,
+ weak_ptr_factory_.GetWeakPtr(), base::Passed(&v8_callback)));
+ if (ExtensionFrameHelper::Get(frame)->did_create_current_document_element()) {
+ // If the document element is already created, then we can call the callback
+ // immediately (though use PostTask to ensure that the callback is called
+ // asynchronously).
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(callback, true));
+ } else {
+ new LoadWatcher(frame, callback);
+ }
args.GetReturnValue().Set(true);
}
+void RenderFrameObserverNatives::InvokeCallback(
+ v8::Global<v8::Function> callback,
+ bool succeeded) {
+ v8::Isolate* isolate = context()->isolate();
+ v8::HandleScope handle_scope(isolate);
+ v8::Local<v8::Value> args[] = {v8::Boolean::New(isolate, succeeded)};
+ context()->CallFunction(v8::Local<v8::Function>::New(isolate, callback),
+ arraysize(args), args);
+}
+
} // namespace extensions
« no previous file with comments | « extensions/renderer/render_frame_observer_natives.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698