Commit 5a15b72a authored by rob's avatar rob Committed by Commit bot

Fix re-entrancy and lifetime issue in RenderFrameObserverNatives::OnDocumentElementCreated

BUG=585268,568130

Review URL: https://codereview.chromium.org/1684953002

Cr-Commit-Position: refs/heads/master@{#374758}
parent 6acd65a6
...@@ -19,43 +19,29 @@ namespace { ...@@ -19,43 +19,29 @@ namespace {
// Deletes itself when done. // Deletes itself when done.
class LoadWatcher : public content::RenderFrameObserver { class LoadWatcher : public content::RenderFrameObserver {
public: public:
LoadWatcher(ScriptContext* context, LoadWatcher(content::RenderFrame* frame,
content::RenderFrame* frame, const base::Callback<void(bool)>& callback)
v8::Local<v8::Function> cb) : content::RenderFrameObserver(frame), callback_(callback) {}
: content::RenderFrameObserver(frame),
context_(context), void DidCreateDocumentElement() override {
callback_(context->isolate(), cb) { // The callback must be run as soon as the root element is available.
if (ExtensionFrameHelper::Get(frame)-> // Running the callback may trigger DidCreateDocumentElement or
did_create_current_document_element()) { // DidFailProvisionalLoad, so delete this before running the callback.
// If the document element is already created, then we can call the base::Callback<void(bool)> callback = callback_;
// callback immediately (though post it to the message loop so as to not delete this;
// call it re-entrantly). callback.Run(true);
// 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));
}
} }
void DidCreateDocumentElement() override { CallbackAndDie(true); }
void DidFailProvisionalLoad(const blink::WebURLError& error) override { void DidFailProvisionalLoad(const blink::WebURLError& error) override {
CallbackAndDie(false); // Use PostTask to avoid running user scripts while handling this
} // DidFailProvisionalLoad notification.
base::MessageLoop::current()->PostTask(FROM_HERE,
private: base::Bind(callback_, false));
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);
delete this; delete this;
} }
ScriptContext* context_; private:
v8::Global<v8::Function> callback_; base::Callback<void(bool)> callback_;
DISALLOW_COPY_AND_ASSIGN(LoadWatcher); DISALLOW_COPY_AND_ASSIGN(LoadWatcher);
}; };
...@@ -63,13 +49,20 @@ class LoadWatcher : public content::RenderFrameObserver { ...@@ -63,13 +49,20 @@ class LoadWatcher : public content::RenderFrameObserver {
} // namespace } // namespace
RenderFrameObserverNatives::RenderFrameObserverNatives(ScriptContext* context) RenderFrameObserverNatives::RenderFrameObserverNatives(ScriptContext* context)
: ObjectBackedNativeHandler(context) { : ObjectBackedNativeHandler(context), weak_ptr_factory_(this) {
RouteFunction( RouteFunction(
"OnDocumentElementCreated", "OnDocumentElementCreated",
base::Bind(&RenderFrameObserverNatives::OnDocumentElementCreated, base::Bind(&RenderFrameObserverNatives::OnDocumentElementCreated,
base::Unretained(this))); base::Unretained(this)));
} }
RenderFrameObserverNatives::~RenderFrameObserverNatives() {}
void RenderFrameObserverNatives::Invalidate() {
weak_ptr_factory_.InvalidateWeakPtrs();
ObjectBackedNativeHandler::Invalidate();
}
void RenderFrameObserverNatives::OnDocumentElementCreated( void RenderFrameObserverNatives::OnDocumentElementCreated(
const v8::FunctionCallbackInfo<v8::Value>& args) { const v8::FunctionCallbackInfo<v8::Value>& args) {
CHECK(args.Length() == 2); CHECK(args.Length() == 2);
...@@ -84,9 +77,32 @@ void RenderFrameObserverNatives::OnDocumentElementCreated( ...@@ -84,9 +77,32 @@ void RenderFrameObserverNatives::OnDocumentElementCreated(
return; return;
} }
new LoadWatcher(context(), frame, args[1].As<v8::Function>()); v8::Global<v8::Function> v8_callback(context()->isolate(),
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); 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 } // namespace extensions
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define EXTENSIONS_RENDERER_RENDER_FRAME_OBSERVER_NATIVES_H_ #define EXTENSIONS_RENDERER_RENDER_FRAME_OBSERVER_NATIVES_H_
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "extensions/renderer/object_backed_native_handler.h" #include "extensions/renderer/object_backed_native_handler.h"
namespace extensions { namespace extensions {
...@@ -15,13 +16,20 @@ class ScriptContext; ...@@ -15,13 +16,20 @@ class ScriptContext;
class RenderFrameObserverNatives : public ObjectBackedNativeHandler { class RenderFrameObserverNatives : public ObjectBackedNativeHandler {
public: public:
explicit RenderFrameObserverNatives(ScriptContext* context); explicit RenderFrameObserverNatives(ScriptContext* context);
~RenderFrameObserverNatives() override;
private: private:
void Invalidate() override;
// Runs a callback upon creation of new document element inside a render frame // Runs a callback upon creation of new document element inside a render frame
// (document.documentElement). // (document.documentElement).
void OnDocumentElementCreated( void OnDocumentElementCreated(
const v8::FunctionCallbackInfo<v8::Value>& args); const v8::FunctionCallbackInfo<v8::Value>& args);
void InvokeCallback(v8::Global<v8::Function> callback, bool succeeded);
base::WeakPtrFactory<RenderFrameObserverNatives> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(RenderFrameObserverNatives); DISALLOW_COPY_AND_ASSIGN(RenderFrameObserverNatives);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment