[Extensions OOPI] Make programmatic script injection use frame logic

Instead of sending an inject message to a single RenderView, have the
browser send inject messages to each of the relevant render frames, and
eliminate the usage of RenderView.
For now, permissioning is still done on the renderer side, and
content scripts are unchanged.

BUG=455776

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

Cr-Commit-Position: refs/heads/master@{#332471}
diff --git a/chrome/browser/chromeos/accessibility/accessibility_manager.cc b/chrome/browser/chromeos/accessibility/accessibility_manager.cc
index c36c118..c4437ea 100644
--- a/chrome/browser/chromeos/accessibility/accessibility_manager.cc
+++ b/chrome/browser/chromeos/accessibility/accessibility_manager.cc
@@ -36,6 +36,7 @@
 #include "chrome/browser/chromeos/ui/accessibility_focus_ring_controller.h"
 #include "chrome/browser/extensions/component_loader.h"
 #include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/chrome_paths.h"
@@ -59,6 +60,7 @@
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/file_reader.h"
+#include "extensions/browser/script_executor.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_messages.h"
 #include "extensions/common/extension_resource.h"
@@ -97,6 +99,32 @@
   return path;
 }
 
+// Uses the ScriptExecutor associated with the given |render_view_host| to
+// execute the given |code|.
+void ExecuteScriptHelper(
+    content::RenderViewHost* render_view_host,
+    const std::string& code,
+    const std::string& extension_id) {
+  content::WebContents* web_contents =
+      content::WebContents::FromRenderViewHost(render_view_host);
+  if (!web_contents)
+    return;
+  extensions::TabHelper::FromWebContents(web_contents)->script_executor()->
+      ExecuteScript(HostID(HostID::EXTENSIONS, extension_id),
+                    extensions::ScriptExecutor::JAVASCRIPT,
+                    code,
+                    extensions::ScriptExecutor::ALL_FRAMES,
+                    extensions::ScriptExecutor::DONT_MATCH_ABOUT_BLANK,
+                    extensions::UserScript::DOCUMENT_IDLE,
+                    extensions::ScriptExecutor::ISOLATED_WORLD,
+                    extensions::ScriptExecutor::DEFAULT_PROCESS,
+                    GURL(),  // No webview src.
+                    GURL(),  // No file url.
+                    false,  // Not user gesture.
+                    extensions::ScriptExecutor::NO_RESULT,
+                    extensions::ScriptExecutor::ExecuteScriptCallback());
+}
+
 // Helper class that directly loads an extension's content scripts into
 // all of the frames corresponding to a given RenderViewHost.
 class ContentScriptLoader {
@@ -134,22 +162,10 @@
  private:
   void OnFileLoaded(bool success, const std::string& data) {
     if (success) {
-      ExtensionMsg_ExecuteCode_Params params;
-      params.request_id = 0;
-      params.host_id = HostID(HostID::EXTENSIONS, extension_id_);
-      params.is_javascript = true;
-      params.code = data;
-      params.run_at = extensions::UserScript::DOCUMENT_IDLE;
-      params.all_frames = true;
-      params.match_about_blank = false;
-      params.in_main_world = false;
-
       RenderViewHost* render_view_host =
           RenderViewHost::FromID(render_process_id_, render_view_id_);
-      if (render_view_host) {
-        render_view_host->Send(new ExtensionMsg_ExecuteCode(
-            render_view_host->GetRoutingID(), params));
-      }
+      if (render_view_host)
+        ExecuteScriptHelper(render_view_host, data, extension_id_);
     }
     Run();
   }
@@ -203,17 +219,8 @@
 
   // Set a flag to tell ChromeVox that it's just been enabled,
   // so that it won't interrupt our speech feedback enabled message.
-  ExtensionMsg_ExecuteCode_Params params;
-  params.request_id = 0;
-  params.host_id = HostID(HostID::EXTENSIONS, extension->id());
-  params.is_javascript = true;
-  params.code = "window.INJECTED_AFTER_LOAD = true;";
-  params.run_at = extensions::UserScript::DOCUMENT_IDLE;
-  params.all_frames = true;
-  params.match_about_blank = false;
-  params.in_main_world = false;
-  render_view_host->Send(new ExtensionMsg_ExecuteCode(
-      render_view_host->GetRoutingID(), params));
+  ExecuteScriptHelper(render_view_host, "window.INJECTED_AFTER_LOAD = true;",
+                      extension->id());
 
   // Inject ChromeVox' content scripts.
   ContentScriptLoader* loader = new ContentScriptLoader(
diff --git a/chrome/browser/extensions/active_script_controller.cc b/chrome/browser/extensions/active_script_controller.cc
index c3e9956..c999e13e 100644
--- a/chrome/browser/extensions/active_script_controller.cc
+++ b/chrome/browser/extensions/active_script_controller.cc
@@ -264,12 +264,12 @@
 void ActiveScriptController::PermitScriptInjection(int64 request_id) {
   // This only sends the response to the renderer - the process of adding the
   // extension to the list of |permitted_extensions_| is done elsewhere.
-  content::RenderViewHost* render_view_host =
-      web_contents()->GetRenderViewHost();
-  if (render_view_host) {
-    render_view_host->Send(new ExtensionMsg_PermitScriptInjection(
-        render_view_host->GetRoutingID(), request_id));
-  }
+  // TODO(devlin): Instead of sending this to all frames, we should include the
+  // routing_id in the permission request message, and send only to the proper
+  // frame (sending it to all frames doesn't hurt, but isn't as efficient).
+  web_contents()->SendToAllFrames(new ExtensionMsg_PermitScriptInjection(
+      MSG_ROUTING_NONE,  // Routing id is set by the |web_contents|.
+      request_id));
 }
 
 void ActiveScriptController::NotifyChange(const Extension* extension) {
diff --git a/chrome/browser/extensions/api/declarative_content/content_action.cc b/chrome/browser/extensions/api/declarative_content/content_action.cc
index 59d571a..6cff6be 100644
--- a/chrome/browser/extensions/api/declarative_content/content_action.cc
+++ b/chrome/browser/extensions/api/declarative_content/content_action.cc
@@ -17,7 +17,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sessions/session_tab_helper.h"
 #include "content/public/browser/invalidate_type.h"
-#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/browser/declarative_user_script_manager.h"
 #include "extensions/browser/extension_registry.h"
@@ -423,9 +423,9 @@
 void RequestContentScript::InstructRenderProcessToInject(
     content::WebContents* contents,
     const std::string& extension_id) const {
-  content::RenderViewHost* render_view_host = contents->GetRenderViewHost();
-  render_view_host->Send(new ExtensionMsg_ExecuteDeclarativeScript(
-      render_view_host->GetRoutingID(),
+  content::RenderFrameHost* render_frame_host = contents->GetMainFrame();
+  render_frame_host->Send(new ExtensionMsg_ExecuteDeclarativeScript(
+      render_frame_host->GetRoutingID(),
       SessionTabHelper::IdForTab(contents),
       extension_id,
       script_.id(),
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 58ea230..a39a3394 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -535,6 +535,7 @@
 #if defined(ENABLE_EXTENSIONS)
   new extensions::ExtensionsRenderFrameObserver(render_frame);
   new extensions::ExtensionFrameHelper(render_frame, ext_dispatcher);
+  ext_dispatcher->OnRenderFrameCreated(render_frame);
 #endif
 
 #if defined(ENABLE_PLUGINS)
@@ -575,7 +576,6 @@
 
 #if defined(ENABLE_EXTENSIONS)
   new extensions::ExtensionHelper(render_view, extension_dispatcher_.get());
-  extension_dispatcher_->OnRenderViewCreated(render_view);
 #endif
   new PageLoadHistograms(render_view);
 #if defined(ENABLE_PRINTING)
diff --git a/content/public/renderer/render_frame_observer.h b/content/public/renderer/render_frame_observer.h
index 0c787b6..a20bbed0 100644
--- a/content/public/renderer/render_frame_observer.h
+++ b/content/public/renderer/render_frame_observer.h
@@ -49,6 +49,8 @@
   virtual void WidgetWillClose() {}
 
   // These match the Blink API notifications
+  virtual void DidCreateNewDocument() {}
+  virtual void DidCreateDocumentElement() {}
   virtual void DidCommitProvisionalLoad(bool is_new_navigation,
                                         bool is_same_page_navigation) {}
   virtual void DidStartProvisionalLoad() {}
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index cd06b8ab..9b5d4abf 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -2677,6 +2677,7 @@
 void RenderFrameImpl::didCreateNewDocument(blink::WebLocalFrame* frame) {
   DCHECK(!frame_ || frame_ == frame);
 
+  FOR_EACH_OBSERVER(RenderFrameObserver, observers_, DidCreateNewDocument());
   FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(),
                     DidCreateNewDocument(frame));
 }
@@ -2731,6 +2732,8 @@
     }
   }
 
+  FOR_EACH_OBSERVER(RenderFrameObserver, observers_,
+                    DidCreateDocumentElement());
   FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(),
                     DidCreateDocumentElement(frame));
 }
diff --git a/extensions/browser/guest_view/web_view/web_view_apitest.cc b/extensions/browser/guest_view/web_view/web_view_apitest.cc
index 021a80e..369bc73 100644
--- a/extensions/browser/guest_view/web_view/web_view_apitest.cc
+++ b/extensions/browser/guest_view/web_view/web_view_apitest.cc
@@ -156,7 +156,7 @@
 content::WebContents* WebViewAPITest::GetFirstAppWindowWebContents() {
   const AppWindowRegistry::AppWindowList& app_window_list =
       AppWindowRegistry::Get(browser_context_)->app_windows();
-  DCHECK(app_window_list.size() == 1);
+  DCHECK_EQ(1u, app_window_list.size());
   return (*app_window_list.begin())->web_contents();
 }
 
diff --git a/extensions/browser/script_executor.cc b/extensions/browser/script_executor.cc
index bdd831ab..0502d89 100644
--- a/extensions/browser/script_executor.cc
+++ b/extensions/browser/script_executor.cc
@@ -8,6 +8,7 @@
 #include "base/callback.h"
 #include "base/logging.h"
 #include "base/pickle.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -34,19 +35,37 @@
  public:
   Handler(ObserverList<ScriptExecutionObserver>* script_observers,
           content::WebContents* web_contents,
-          const ExtensionMsg_ExecuteCode_Params& params,
+          ExtensionMsg_ExecuteCode_Params* params,
+          ScriptExecutor::FrameScope scope,
+          int* request_id_counter,
           const ScriptExecutor::ExecuteScriptCallback& callback)
       : content::WebContentsObserver(web_contents),
         script_observers_(AsWeakPtr(script_observers)),
-        host_id_(params.host_id),
-        request_id_(params.request_id),
+        host_id_(params->host_id),
+        main_request_id_((*request_id_counter)++),
+        sub_request_id_(scope == ScriptExecutor::ALL_FRAMES ?
+                            (*request_id_counter)++ : -1),
+        num_pending_(0),
         callback_(callback) {
-    content::RenderViewHost* rvh = web_contents->GetRenderViewHost();
-    rvh->Send(new ExtensionMsg_ExecuteCode(rvh->GetRoutingID(), params));
+    content::RenderFrameHost* main_frame = web_contents->GetMainFrame();
+    if (scope == ScriptExecutor::ALL_FRAMES) {
+      web_contents->ForEachFrame(base::Bind(&Handler::SendExecuteCode,
+                                            base::Unretained(this), params,
+                                            main_frame));
+    } else {
+      SendExecuteCode(params, main_frame, main_frame);
+    }
   }
 
+ private:
+  // This class manages its own lifetime.
   ~Handler() override {}
 
+  // content::WebContentsObserver:
+  void WebContentsDestroyed() override {
+    Finish(kRendererDestroyed, GURL(), base::ListValue());
+  }
+
   bool OnMessageReceived(const IPC::Message& message) override {
     // Unpack by hand to check the request_id, since there may be multiple
     // requests in flight but only one is for this.
@@ -57,8 +76,10 @@
     PickleIterator iter(message);
     CHECK(iter.ReadInt(&message_request_id));
 
-    if (message_request_id != request_id_)
+    if (message_request_id != main_request_id_ &&
+        message_request_id != sub_request_id_) {
       return false;
+    }
 
     IPC_BEGIN_MESSAGE_MAP(Handler, message)
       IPC_MESSAGE_HANDLER(ExtensionHostMsg_ExecuteCodeFinished,
@@ -67,17 +88,45 @@
     return true;
   }
 
-  void WebContentsDestroyed() override {
-    base::ListValue val;
-    callback_.Run(kRendererDestroyed, GURL(std::string()), val);
-    delete this;
+  // Sends an ExecuteCode message to the given frame host, and increments
+  // the number of pending messages.
+  void SendExecuteCode(ExtensionMsg_ExecuteCode_Params* params,
+                       content::RenderFrameHost* main_frame,
+                       content::RenderFrameHost* frame) {
+    ++num_pending_;
+    params->request_id =
+        main_frame == frame ? main_request_id_ : sub_request_id_;
+    frame->Send(new ExtensionMsg_ExecuteCode(frame->GetRoutingID(), *params));
   }
 
- private:
+  // Handles the ExecuteCodeFinished message.
   void OnExecuteCodeFinished(int request_id,
                              const std::string& error,
                              const GURL& on_url,
-                             const base::ListValue& script_result) {
+                             const base::ListValue& result_list) {
+    DCHECK_NE(-1, request_id);
+    bool is_main_frame = request_id == main_request_id_;
+
+    // Set the result, if there is one.
+    const base::Value* script_value = nullptr;
+    if (result_list.Get(0u, &script_value)) {
+      // If this is the main result, we put it at index 0. Otherwise, we just
+      // append it at the end.
+      if (is_main_frame && !results_.empty())
+        CHECK(results_.Insert(0u, script_value->DeepCopy()));
+      else
+        results_.Append(script_value->DeepCopy());
+    }
+
+    if (is_main_frame) {  // Only use the main frame's error and url.
+      error_ = error;
+      url_ = on_url;
+    }
+
+    // Wait until the final request finishes before reporting back.
+    if (--num_pending_ > 0)
+      return;
+
     if (script_observers_.get() && error.empty() &&
         host_id_.type() == HostID::EXTENSIONS) {
       ScriptExecutionObserver::ExecutingScriptsMap id_map;
@@ -87,14 +136,46 @@
                         OnScriptsExecuted(web_contents(), id_map, on_url));
     }
 
-    callback_.Run(error, on_url, script_result);
+    Finish(error_, url_, results_);
+  }
+
+  void Finish(const std::string& error,
+              const GURL& url,
+              const base::ListValue& result) {
+    if (!callback_.is_null())
+      callback_.Run(error, url, result);
     delete this;
   }
 
-  base::WeakPtr<ObserverList<ScriptExecutionObserver> > script_observers_;
+  base::WeakPtr<ObserverList<ScriptExecutionObserver>> script_observers_;
+
+  // The id of the host (the extension or the webui) doing the injection.
   HostID host_id_;
-  int request_id_;
+
+  // The request id of the injection into the main frame.
+  int main_request_id_;
+
+  // The request id of the injection into any sub frames. We need a separate id
+  // for these so that we know which frame to use as the first result, and which
+  // error (if any) to use.
+  int sub_request_id_;
+
+  // The number of still-running injections.
+  int num_pending_;
+
+  // The results of the injection.
+  base::ListValue results_;
+
+  // The error from injecting into the main frame.
+  std::string error_;
+
+  // The url of the main frame.
+  GURL url_;
+
+  // The callback to run after all injections complete.
   ScriptExecutor::ExecuteScriptCallback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(Handler);
 };
 
 }  // namespace
@@ -139,11 +220,9 @@
   }
 
   ExtensionMsg_ExecuteCode_Params params;
-  params.request_id = next_request_id_++;
   params.host_id = host_id;
   params.is_javascript = (script_type == JAVASCRIPT);
   params.code = code;
-  params.all_frames = (frame_scope == ALL_FRAMES);
   params.match_about_blank = (about_blank == MATCH_ABOUT_BLANK);
   params.run_at = static_cast<int>(run_at);
   params.in_main_world = (world_type == MAIN_WORLD);
@@ -154,7 +233,8 @@
   params.user_gesture = user_gesture;
 
   // Handler handles IPCs and deletes itself on completion.
-  new Handler(script_observers_, web_contents_, params, callback);
+  new Handler(script_observers_, web_contents_, &params, frame_scope,
+              &next_request_id_, callback);
 }
 
 }  // namespace extensions
diff --git a/extensions/common/extension_messages.h b/extensions/common/extension_messages.h
index e998fba..56855be4 100644
--- a/extensions/common/extension_messages.h
+++ b/extensions/common/extension_messages.h
@@ -121,9 +121,6 @@
   // The webview guest source who calls to execute code.
   IPC_STRUCT_MEMBER(GURL, webview_src)
 
-  // Whether to inject into all frames, or only the root frame.
-  IPC_STRUCT_MEMBER(bool, all_frames)
-
   // Whether to inject into about:blank (sub)frames.
   IPC_STRUCT_MEMBER(bool, match_about_blank)
 
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index f210e27e..0284ee6f 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -215,8 +215,8 @@
 Dispatcher::~Dispatcher() {
 }
 
-void Dispatcher::OnRenderViewCreated(content::RenderView* render_view) {
-  script_injection_manager_->OnRenderViewCreated(render_view);
+void Dispatcher::OnRenderFrameCreated(content::RenderFrame* render_frame) {
+  script_injection_manager_->OnRenderFrameCreated(render_frame);
 }
 
 bool Dispatcher::IsExtensionActive(const std::string& extension_id) const {
diff --git a/extensions/renderer/dispatcher.h b/extensions/renderer/dispatcher.h
index 5ec0c6c..f85c1ad 100644
--- a/extensions/renderer/dispatcher.h
+++ b/extensions/renderer/dispatcher.h
@@ -86,7 +86,7 @@
 
   RequestSender* request_sender() { return request_sender_.get(); }
 
-  void OnRenderViewCreated(content::RenderView* render_view);
+  void OnRenderFrameCreated(content::RenderFrame* render_frame);
 
   bool IsExtensionActive(const std::string& extension_id) const;
 
diff --git a/extensions/renderer/programmatic_script_injector.cc b/extensions/renderer/programmatic_script_injector.cc
index f13fa654..4546d93 100644
--- a/extensions/renderer/programmatic_script_injector.cc
+++ b/extensions/renderer/programmatic_script_injector.cc
@@ -28,7 +28,6 @@
     : params_(new ExtensionMsg_ExecuteCode_Params(params)),
       url_(ScriptContext::GetDataSourceURLForFrame(web_frame)),
       render_view_(content::RenderView::FromWebView(web_frame->view())),
-      results_(new base::ListValue()),
       finished_(false) {
   effective_url_ = ScriptContext::GetEffectiveDocumentURL(
       web_frame, url_, params.match_about_blank);
@@ -42,10 +41,6 @@
   return UserScript::PROGRAMMATIC_SCRIPT;
 }
 
-bool ProgrammaticScriptInjector::ShouldExecuteInChildFrames() const {
-  return params_->all_frames;
-}
-
 bool ProgrammaticScriptInjector::ShouldExecuteInMainWorld() const {
   return params_->in_main_world;
 }
@@ -120,9 +115,11 @@
 }
 
 void ProgrammaticScriptInjector::OnInjectionComplete(
-    scoped_ptr<base::ListValue> execution_results,
+    scoped_ptr<base::Value> execution_result,
     UserScript::RunLocation run_location) {
-  results_ = execution_results.Pass();
+  DCHECK(results_.empty());
+  if (execution_result)
+    results_.Append(execution_result.Pass());
   Finish(std::string());
 }
 
@@ -159,7 +156,7 @@
       params_->request_id,
       error,
       url_,
-      *results_));
+      results_));
 }
 
 }  // namespace extensions
diff --git a/extensions/renderer/programmatic_script_injector.h b/extensions/renderer/programmatic_script_injector.h
index 2ec3c90..6e9aacb 100644
--- a/extensions/renderer/programmatic_script_injector.h
+++ b/extensions/renderer/programmatic_script_injector.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/values.h"
 #include "extensions/renderer/script_injection.h"
 #include "url/gurl.h"
 
@@ -32,7 +33,6 @@
  private:
   // ScriptInjector implementation.
   UserScript::InjectionType script_type() const override;
-  bool ShouldExecuteInChildFrames() const override;
   bool ShouldExecuteInMainWorld() const override;
   bool IsUserGesture() const override;
   bool ExpectsResults() const override;
@@ -49,7 +49,7 @@
       UserScript::RunLocation run_location) const override;
   void GetRunInfo(ScriptsRunInfo* scripts_run_info,
                   UserScript::RunLocation run_location) const override;
-  void OnInjectionComplete(scoped_ptr<base::ListValue> execution_results,
+  void OnInjectionComplete(scoped_ptr<base::Value> execution_result,
                            UserScript::RunLocation run_location) override;
   void OnWillNotInject(InjectFailureReason reason) override;
 
@@ -75,7 +75,7 @@
   content::RenderView* render_view_;
 
   // The results of the script execution.
-  scoped_ptr<base::ListValue> results_;
+  base::ListValue results_;
 
   // Whether or not this script injection has finished.
   bool finished_;
diff --git a/extensions/renderer/script_injection.cc b/extensions/renderer/script_injection.cc
index db11923ee..5ab2ea5 100644
--- a/extensions/renderer/script_injection.cc
+++ b/extensions/renderer/script_injection.cc
@@ -20,7 +20,6 @@
 #include "extensions/renderer/extension_injection_host.h"
 #include "extensions/renderer/extensions_renderer_client.h"
 #include "extensions/renderer/script_injection_callback.h"
-#include "extensions/renderer/script_injection_manager.h"
 #include "extensions/renderer/scripts_run_info.h"
 #include "third_party/WebKit/public/platform/WebString.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
@@ -43,17 +42,6 @@
 // The id of the next pending injection.
 int64 g_next_pending_id = 0;
 
-// Append all the child frames of |parent_frame| to |frames_vector|.
-void AppendAllChildFrames(blink::WebFrame* parent_frame,
-                          std::vector<blink::WebFrame*>* frames_vector) {
-  DCHECK(parent_frame);
-  for (blink::WebFrame* child_frame = parent_frame->firstChild(); child_frame;
-       child_frame = child_frame->nextSibling()) {
-    frames_vector->push_back(child_frame);
-    AppendAllChildFrames(child_frame, frames_vector);
-  }
-}
-
 // Gets the isolated world ID to use for the given |injection_host|
 // in the given |frame|. If no isolated world has been created for that
 // |injection_host| one will be created and initialized.
@@ -121,10 +109,7 @@
       tab_id_(tab_id),
       request_id_(kInvalidRequestId),
       complete_(false),
-      running_frames_(0),
-      execution_results_(new base::ListValue()),
-      all_injections_started_(false),
-      script_injection_manager_(nullptr) {
+      did_inject_js_(false) {
   CHECK(injection_host_.get());
 }
 
@@ -136,7 +121,7 @@
 ScriptInjection::InjectionResult ScriptInjection::TryToInject(
     UserScript::RunLocation current_location,
     ScriptsRunInfo* scripts_run_info,
-    ScriptInjectionManager* manager) {
+    const CompletionCallback& async_completion_callback) {
   if (current_location < run_location_)
     return INJECTION_WAITING;  // Wait for the right location.
 
@@ -157,6 +142,14 @@
       NotifyWillNotInject(ScriptInjector::NOT_ALLOWED);
       return INJECTION_FINISHED;  // We're done.
     case PermissionsData::ACCESS_WITHHELD:
+      // Note: we don't consider ACCESS_WITHHELD for child frames because there
+      // is nowhere to surface a request for a child frame.
+      // TODO(devlin): We should ask for permission somehow. crbug.com/491402.
+      if (web_frame_->parent()) {
+        NotifyWillNotInject(ScriptInjector::NOT_ALLOWED);
+        return INJECTION_FINISHED;
+      }
+
       SendInjectionMessage(true /* request permission */);
       return INJECTION_WAITING;  // Wait around for permission.
     case PermissionsData::ACCESS_ALLOWED:
@@ -164,7 +157,7 @@
       // If the injection is blocked, we need to set the manager so we can
       // notify it upon completion.
       if (result == INJECTION_BLOCKED)
-        script_injection_manager_ = manager;
+        async_completion_callback_ = async_completion_callback;
       return result;
   }
 
@@ -215,52 +208,29 @@
   if (injection_host_->ShouldNotifyBrowserOfInjection())
     SendInjectionMessage(false /* don't request permission */);
 
-  std::vector<blink::WebFrame*> frame_vector;
-  frame_vector.push_back(web_frame_);
-  if (injector_->ShouldExecuteInChildFrames())
-    AppendAllChildFrames(web_frame_, &frame_vector);
+  bool should_inject_js = injector_->ShouldInjectJs(run_location_);
+  bool should_inject_css = injector_->ShouldInjectCss(run_location_);
+  DCHECK(should_inject_js || should_inject_css);
 
-  bool inject_js = injector_->ShouldInjectJs(run_location_);
-  bool inject_css = injector_->ShouldInjectCss(run_location_);
-  DCHECK(inject_js || inject_css);
+  if (should_inject_js)
+    InjectJs(web_frame_);
+  if (should_inject_css)
+    InjectCss(web_frame_);
 
-  GURL top_url = web_frame_->top()->document().url();
-  for (std::vector<blink::WebFrame*>::iterator iter = frame_vector.begin();
-       iter != frame_vector.end();
-       ++iter) {
-    // TODO(dcheng): Unfortunately, the code as written won't work in an OOPI
-    // world. This is just a temporary hack to make things compile.
-    blink::WebLocalFrame* frame = (*iter)->toWebLocalFrame();
+  complete_ = did_inject_js_ || !should_inject_js;
 
-    // We recheck access here in the renderer for extra safety against races
-    // with navigation, but different frames can have different URLs, and the
-    // injection host might only have access to a subset of them.
-    // For child frames, we just skip ones the injection host doesn't have
-    // access to and carry on.
-    // Note: we don't consider ACCESS_WITHHELD because there is nowhere to
-    // surface a request for a child frame.
-    // TODO(rdevlin.cronin): We should ask for permission somehow.
-    if (injector_->CanExecuteOnFrame(
-        injection_host_.get(), frame, tab_id_, top_url) ==
-            PermissionsData::ACCESS_DENIED) {
-      DCHECK(frame->parent());
-      continue;
-    }
-    if (inject_js)
-      InjectJs(frame);
-    if (inject_css)
-      InjectCss(frame);
-  }
-
-  all_injections_started_ = true;
   injector_->GetRunInfo(scripts_run_info, run_location_);
-  scripts_run_info->num_blocking_js = running_frames_;
-  TryToFinish();
+
+  if (complete_)
+    injector_->OnInjectionComplete(execution_result_.Pass(), run_location_);
+  else
+    ++scripts_run_info->num_blocking_js;
+
   return complete_ ? INJECTION_FINISHED : INJECTION_BLOCKED;
 }
 
 void ScriptInjection::InjectJs(blink::WebLocalFrame* frame) {
-  ++running_frames_;
+  DCHECK(!did_inject_js_);
   std::vector<blink::WebScriptSource> sources =
       injector_->GetJsSources(run_location_);
   bool in_main_world = injector_->ShouldExecuteInMainWorld();
@@ -299,12 +269,10 @@
 void ScriptInjection::OnJsInjectionCompleted(
     blink::WebLocalFrame* frame,
     const blink::WebVector<v8::Local<v8::Value> >& results) {
-  DCHECK(running_frames_ > 0);
-  --running_frames_;
+  DCHECK(!did_inject_js_);
 
   bool expects_results = injector_->ExpectsResults();
   if (expects_results) {
-    scoped_ptr<base::Value> result;
     if (!results.isEmpty() && !results[0].IsEmpty()) {
       // Right now, we only support returning single results (per frame).
       scoped_ptr<content::V8ValueConverter> v8_converter(
@@ -314,39 +282,27 @@
       // context scope, and it switches to v8::Object's creation context
       // when encountered.
       v8::Local<v8::Context> context = frame->mainWorldScriptContext();
-      result.reset(v8_converter->FromV8Value(results[0], context));
+      execution_result_.reset(v8_converter->FromV8Value(results[0], context));
     }
-    if (!result.get())
-      result = base::Value::CreateNullValue();
-    // We guarantee that the main frame's result is at the first index, but
-    // any sub frames results do not have guaranteed order.
-    execution_results_->Insert(
-        frame == web_frame_ ? 0 : execution_results_->GetSize(),
-        result.release());
+    if (!execution_result_.get())
+      execution_result_ = base::Value::CreateNullValue();
   }
-  TryToFinish();
-}
+  did_inject_js_ = true;
 
-void ScriptInjection::TryToFinish() {
-  if (all_injections_started_ && running_frames_ == 0) {
-    complete_ = true;
-    injector_->OnInjectionComplete(execution_results_.Pass(),
-                                   run_location_);
-
-    // This object can be destroyed after next line.
-    if (script_injection_manager_)
-      script_injection_manager_->OnInjectionFinished(this);
+  // If |async_completion_callback_| is set, it means the script finished
+  // asynchronously, and we should run it.
+  if (!async_completion_callback_.is_null()) {
+    injector_->OnInjectionComplete(execution_result_.Pass(), run_location_);
+    // Warning: this object can be destroyed after this line!
+    async_completion_callback_.Run(this);
   }
 }
 
 void ScriptInjection::InjectCss(blink::WebLocalFrame* frame) {
   std::vector<std::string> css_sources =
       injector_->GetCssSources(run_location_);
-  for (std::vector<std::string>::const_iterator iter = css_sources.begin();
-       iter != css_sources.end();
-       ++iter) {
-    frame->document().insertStyleSheet(blink::WebString::fromUTF8(*iter));
-  }
+  for (const std::string& css : css_sources)
+    frame->document().insertStyleSheet(blink::WebString::fromUTF8(css));
 }
 
 }  // namespace extensions
diff --git a/extensions/renderer/script_injection.h b/extensions/renderer/script_injection.h
index baffec48..1b161b0c 100644
--- a/extensions/renderer/script_injection.h
+++ b/extensions/renderer/script_injection.h
@@ -6,6 +6,7 @@
 #define EXTENSIONS_RENDERER_SCRIPT_INJECTION_H_
 
 #include "base/basictypes.h"
+#include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/scoped_ptr.h"
 #include "extensions/common/user_script.h"
@@ -38,12 +39,16 @@
     INJECTION_WAITING
   };
 
+  using CompletionCallback = base::Callback<void(ScriptInjection*)>;
+
   // Return the id of the injection host associated with the given world.
   static std::string GetHostIdForIsolatedWorld(int world_id);
 
   // Remove the isolated world associated with the given injection host.
   static void RemoveIsolatedWorld(const std::string& host_id);
 
+  // TODO(devlin): This and its *ScriptInjector brethren, should take take a
+  // RenderFrame, instead of a WebFrame.
   ScriptInjection(scoped_ptr<ScriptInjector> injector,
                   blink::WebLocalFrame* web_frame,
                   scoped_ptr<const InjectionHost> injection_host,
@@ -57,9 +62,12 @@
   // finished yet, returns INJECTION_WAITING if injections is delayed (either
   // for permission purposes or because |current_location| is not the designated
   // |run_location_|).
-  InjectionResult TryToInject(UserScript::RunLocation current_location,
-                              ScriptsRunInfo* scripts_run_info,
-                              ScriptInjectionManager* manager);
+  // If INJECTION_BLOCKED is returned, |async_completion_callback| will be
+  // called upon completion.
+  InjectionResult TryToInject(
+      UserScript::RunLocation current_location,
+      ScriptsRunInfo* scripts_run_info,
+      const CompletionCallback& async_completion_callback);
 
   // Called when permission for the given injection has been granted.
   // Returns INJECTION_FINISHED if injection has injected or will never inject,
@@ -91,9 +99,6 @@
   // Inject any JS scripts into the |frame|.
   void InjectJs(blink::WebLocalFrame* frame);
 
-  // Checks if all scripts have been injected and finished.
-  void TryToFinish();
-
   // Inject any CSS source into the |frame|.
   void InjectCss(blink::WebLocalFrame* frame);
 
@@ -123,18 +128,14 @@
   // or because it will never complete.
   bool complete_;
 
-  // Number of frames in which the injection is running.
-  int running_frames_;
+  // Whether or not the injection successfully injected JS.
+  bool did_inject_js_;
 
   // Results storage.
-  scoped_ptr<base::ListValue> execution_results_;
+  scoped_ptr<base::Value> execution_result_;
 
-  // Flag is true when injections for each frame started.
-  bool all_injections_started_;
-
-  // ScriptInjectionManager::OnInjectionFinished will be called after injection
-  // finished.
-  ScriptInjectionManager* script_injection_manager_;
+  // The callback to run upon completing asynchronously.
+  CompletionCallback async_completion_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(ScriptInjection);
 };
diff --git a/extensions/renderer/script_injection_manager.cc b/extensions/renderer/script_injection_manager.cc
index f758b95..e0a610b 100644
--- a/extensions/renderer/script_injection_manager.cc
+++ b/extensions/renderer/script_injection_manager.cc
@@ -8,9 +8,10 @@
 #include "base/bind.h"
 #include "base/memory/weak_ptr.h"
 #include "base/values.h"
+#include "content/public/renderer/render_frame.h"
+#include "content/public/renderer/render_frame_observer.h"
 #include "content/public/renderer/render_thread.h"
 #include "content/public/renderer/render_view.h"
-#include "content/public/renderer/render_view_observer.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_messages.h"
 #include "extensions/common/extension_set.h"
@@ -56,19 +57,20 @@
 
 }  // namespace
 
-class ScriptInjectionManager::RVOHelper : public content::RenderViewObserver {
+class ScriptInjectionManager::RFOHelper : public content::RenderFrameObserver {
  public:
-  RVOHelper(content::RenderView* render_view, ScriptInjectionManager* manager);
-  ~RVOHelper() override;
+  RFOHelper(content::RenderFrame* render_frame,
+            ScriptInjectionManager* manager);
+  ~RFOHelper() override;
 
  private:
-  // RenderViewObserver implementation.
+  // RenderFrameObserver implementation.
   bool OnMessageReceived(const IPC::Message& message) override;
-  void DidCreateNewDocument(blink::WebLocalFrame* frame) override;
-  void DidCreateDocumentElement(blink::WebLocalFrame* frame) override;
-  void DidFinishDocumentLoad(blink::WebLocalFrame* frame) override;
-  void DidFinishLoad(blink::WebLocalFrame* frame) override;
-  void FrameDetached(blink::WebFrame* frame) override;
+  void DidCreateNewDocument() override;
+  void DidCreateDocumentElement() override;
+  void DidFinishDocumentLoad() override;
+  void DidFinishLoad() override;
+  void FrameDetached() override;
   void OnDestruct() override;
 
   virtual void OnExecuteCode(const ExtensionMsg_ExecuteCode_Params& params);
@@ -80,11 +82,11 @@
 
   // Tells the ScriptInjectionManager to run tasks associated with
   // document_idle.
-  void RunIdle(blink::WebFrame* frame);
+  void RunIdle();
 
-  // Indicate that the given |frame| is no longer valid because it is starting
+  // Indicate that the frame is no longer valid because it is starting
   // a new load or closing.
-  void InvalidateFrame(blink::WebFrame* frame);
+  void InvalidateFrame();
 
   // The owning ScriptInjectionManager.
   ScriptInjectionManager* manager_;
@@ -92,26 +94,26 @@
   // The set of frames that we are about to notify for DOCUMENT_IDLE. We keep
   // a set of those that are valid, so we don't notify that an invalid frame
   // became idle.
-  std::set<blink::WebFrame*> pending_idle_frames_;
+  std::set<content::RenderFrame*> pending_idle_frames_;
 
-  base::WeakPtrFactory<RVOHelper> weak_factory_;
+  base::WeakPtrFactory<RFOHelper> weak_factory_;
 };
 
-ScriptInjectionManager::RVOHelper::RVOHelper(
-    content::RenderView* render_view,
+ScriptInjectionManager::RFOHelper::RFOHelper(
+    content::RenderFrame* render_frame,
     ScriptInjectionManager* manager)
-    : content::RenderViewObserver(render_view),
+    : content::RenderFrameObserver(render_frame),
       manager_(manager),
       weak_factory_(this) {
 }
 
-ScriptInjectionManager::RVOHelper::~RVOHelper() {
+ScriptInjectionManager::RFOHelper::~RFOHelper() {
 }
 
-bool ScriptInjectionManager::RVOHelper::OnMessageReceived(
+bool ScriptInjectionManager::RFOHelper::OnMessageReceived(
     const IPC::Message& message) {
   bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(ScriptInjectionManager::RVOHelper, message)
+  IPC_BEGIN_MESSAGE_MAP(ScriptInjectionManager::RFOHelper, message)
     IPC_MESSAGE_HANDLER(ExtensionMsg_ExecuteCode, OnExecuteCode)
     IPC_MESSAGE_HANDLER(ExtensionMsg_PermitScriptInjection,
                         OnPermitScriptInjection)
@@ -122,26 +124,23 @@
   return handled;
 }
 
-void ScriptInjectionManager::RVOHelper::DidCreateNewDocument(
-    blink::WebLocalFrame* frame) {
+void ScriptInjectionManager::RFOHelper::DidCreateNewDocument() {
   // A new document is going to be shown, so invalidate the old document state.
   // Check that the frame's state is known before invalidating the frame,
   // because it is possible that a script injection was scheduled before the
   // page was loaded, e.g. by navigating to a javascript: URL before the page
   // has loaded.
-  if (manager_->frame_statuses_.find(frame) != manager_->frame_statuses_.end())
-    InvalidateFrame(frame);
+  if (manager_->frame_statuses_.count(render_frame()) != 0)
+    InvalidateFrame();
 }
 
-void ScriptInjectionManager::RVOHelper::DidCreateDocumentElement(
-    blink::WebLocalFrame* frame) {
-  manager_->StartInjectScripts(frame, UserScript::DOCUMENT_START);
+void ScriptInjectionManager::RFOHelper::DidCreateDocumentElement() {
+  manager_->StartInjectScripts(render_frame(), UserScript::DOCUMENT_START);
 }
 
-void ScriptInjectionManager::RVOHelper::DidFinishDocumentLoad(
-    blink::WebLocalFrame* frame) {
-  manager_->StartInjectScripts(frame, UserScript::DOCUMENT_END);
-  pending_idle_frames_.insert(frame);
+void ScriptInjectionManager::RFOHelper::DidFinishDocumentLoad() {
+  manager_->StartInjectScripts(render_frame(), UserScript::DOCUMENT_END);
+  pending_idle_frames_.insert(render_frame());
   // We try to run idle in two places: here and DidFinishLoad.
   // DidFinishDocumentLoad() corresponds to completing the document's load,
   // whereas DidFinishLoad corresponds to completing the document and all
@@ -151,14 +150,12 @@
   // triggered), then there's no reason to keep waiting.
   content::RenderThread::Get()->GetTaskRunner()->PostDelayedTask(
       FROM_HERE,
-      base::Bind(&ScriptInjectionManager::RVOHelper::RunIdle,
-                 weak_factory_.GetWeakPtr(),
-                 frame),
+      base::Bind(&ScriptInjectionManager::RFOHelper::RunIdle,
+                 weak_factory_.GetWeakPtr()),
       base::TimeDelta::FromMilliseconds(kScriptIdleTimeoutInMs));
 }
 
-void ScriptInjectionManager::RVOHelper::DidFinishLoad(
-    blink::WebLocalFrame* frame) {
+void ScriptInjectionManager::RFOHelper::DidFinishLoad() {
   // Ensure that we don't block any UI progress by running scripts.
   // We *don't* add the frame to |pending_idle_frames_| here because
   // DidFinishDocumentLoad should strictly come before DidFinishLoad, so the
@@ -166,38 +163,34 @@
   // don't try to run idle twice.
   content::RenderThread::Get()->GetTaskRunner()->PostTask(
       FROM_HERE,
-      base::Bind(&ScriptInjectionManager::RVOHelper::RunIdle,
-                 weak_factory_.GetWeakPtr(),
-                 frame));
+      base::Bind(&ScriptInjectionManager::RFOHelper::RunIdle,
+                 weak_factory_.GetWeakPtr()));
 }
 
-void ScriptInjectionManager::RVOHelper::FrameDetached(blink::WebFrame* frame) {
+void ScriptInjectionManager::RFOHelper::FrameDetached() {
   // The frame is closing - invalidate.
-  InvalidateFrame(frame);
+  InvalidateFrame();
 }
 
-void ScriptInjectionManager::RVOHelper::OnDestruct() {
+void ScriptInjectionManager::RFOHelper::OnDestruct() {
   manager_->RemoveObserver(this);
 }
 
-void ScriptInjectionManager::RVOHelper::OnExecuteCode(
+void ScriptInjectionManager::RFOHelper::OnExecuteCode(
     const ExtensionMsg_ExecuteCode_Params& params) {
-  manager_->HandleExecuteCode(params, render_view());
+  manager_->HandleExecuteCode(params, render_frame());
 }
 
-void ScriptInjectionManager::RVOHelper::OnExecuteDeclarativeScript(
+void ScriptInjectionManager::RFOHelper::OnExecuteDeclarativeScript(
     int tab_id,
     const ExtensionId& extension_id,
     int script_id,
     const GURL& url) {
-  blink::WebFrame* main_frame = render_view()->GetWebView()->mainFrame();
-  CHECK(main_frame);
-
   // TODO(markdittmer): This would be cleaner if we compared page_ids instead.
-  // Begin script injeciton workflow only if the current URL is identical to
+  // Begin script injection workflow only if the current URL is identical to
   // the one that matched declarative conditions in the browser.
-  if (main_frame->top()->document().url() == url) {
-    manager_->HandleExecuteDeclarativeScript(main_frame,
+  if (render_frame()->GetWebFrame()->top()->document().url() == url) {
+    manager_->HandleExecuteDeclarativeScript(render_frame(),
                                              tab_id,
                                              extension_id,
                                              script_id,
@@ -205,24 +198,23 @@
   }
 }
 
-void ScriptInjectionManager::RVOHelper::OnPermitScriptInjection(
+void ScriptInjectionManager::RFOHelper::OnPermitScriptInjection(
     int64 request_id) {
   manager_->HandlePermitScriptInjection(request_id);
 }
 
-void ScriptInjectionManager::RVOHelper::RunIdle(blink::WebFrame* frame) {
+void ScriptInjectionManager::RFOHelper::RunIdle() {
   // Only notify the manager if the frame hasn't either been removed or already
   // had idle run since the task to RunIdle() was posted.
-  if (pending_idle_frames_.count(frame) > 0) {
-    manager_->StartInjectScripts(frame, UserScript::DOCUMENT_IDLE);
-    pending_idle_frames_.erase(frame);
+  if (pending_idle_frames_.count(render_frame()) > 0) {
+    manager_->StartInjectScripts(render_frame(), UserScript::DOCUMENT_IDLE);
+    pending_idle_frames_.erase(render_frame());
   }
 }
 
-void ScriptInjectionManager::RVOHelper::InvalidateFrame(
-    blink::WebFrame* frame) {
-  pending_idle_frames_.erase(frame);
-  manager_->InvalidateForFrame(frame);
+void ScriptInjectionManager::RFOHelper::InvalidateFrame() {
+  pending_idle_frames_.erase(render_frame());
+  manager_->InvalidateForFrame(render_frame());
 }
 
 ScriptInjectionManager::ScriptInjectionManager(
@@ -237,9 +229,9 @@
 ScriptInjectionManager::~ScriptInjectionManager() {
 }
 
-void ScriptInjectionManager::OnRenderViewCreated(
-    content::RenderView* render_view) {
-  rvo_helpers_.push_back(new RVOHelper(render_view, this));
+void ScriptInjectionManager::OnRenderFrameCreated(
+    content::RenderFrame* render_frame) {
+  rfo_helpers_.push_back(new RFOHelper(render_frame, this));
 }
 
 void ScriptInjectionManager::OnExtensionUnloaded(
@@ -278,22 +270,23 @@
   }
 }
 
-void ScriptInjectionManager::RemoveObserver(RVOHelper* helper) {
-  for (ScopedVector<RVOHelper>::iterator iter = rvo_helpers_.begin();
-       iter != rvo_helpers_.end();
+void ScriptInjectionManager::RemoveObserver(RFOHelper* helper) {
+  for (ScopedVector<RFOHelper>::iterator iter = rfo_helpers_.begin();
+       iter != rfo_helpers_.end();
        ++iter) {
     if (*iter == helper) {
-      rvo_helpers_.erase(iter);
+      rfo_helpers_.erase(iter);
       break;
     }
   }
 }
 
-void ScriptInjectionManager::InvalidateForFrame(blink::WebFrame* frame) {
+void ScriptInjectionManager::InvalidateForFrame(content::RenderFrame* frame) {
+  blink::WebLocalFrame* web_frame = frame->GetWebFrame();
   for (ScopedVector<ScriptInjection>::iterator iter =
            pending_injections_.begin();
        iter != pending_injections_.end();) {
-    if ((*iter)->web_frame() == frame)
+    if ((*iter)->web_frame() == web_frame)
       iter = pending_injections_.erase(iter);
     else
       ++iter;
@@ -303,7 +296,8 @@
 }
 
 void ScriptInjectionManager::StartInjectScripts(
-    blink::WebFrame* frame, UserScript::RunLocation run_location) {
+    content::RenderFrame* frame,
+    UserScript::RunLocation run_location) {
   FrameStatusMap::iterator iter = frame_statuses_.find(frame);
   // We also don't execute if we detect that the run location is somehow out of
   // order. This can happen if:
@@ -337,14 +331,15 @@
 }
 
 void ScriptInjectionManager::InjectScripts(
-    blink::WebFrame* frame,
+    content::RenderFrame* frame,
     UserScript::RunLocation run_location) {
+  blink::WebLocalFrame* web_frame = frame->GetWebFrame();
   // Find any injections that want to run on the given frame.
   ScopedVector<ScriptInjection> frame_injections;
   for (ScopedVector<ScriptInjection>::iterator iter =
            pending_injections_.begin();
        iter != pending_injections_.end();) {
-    if ((*iter)->web_frame() == frame) {
+    if ((*iter)->web_frame() == web_frame) {
       frame_injections.push_back(*iter);
       iter = pending_injections_.weak_erase(iter);
     } else {
@@ -354,9 +349,9 @@
 
   // Add any injections for user scripts.
   int tab_id = ExtensionHelper::Get(content::RenderView::FromWebView(
-                                        frame->top()->view()))->tab_id();
+                                        web_frame->top()->view()))->tab_id();
   user_script_set_manager_->GetAllInjections(
-      &frame_injections, frame, tab_id, run_location);
+      &frame_injections, web_frame, tab_id, run_location);
 
   ScriptsRunInfo scripts_run_info;
   std::vector<ScriptInjection*> released_injections;
@@ -364,7 +359,7 @@
   for (ScriptInjection* injection : released_injections)
     TryToInject(make_scoped_ptr(injection), run_location, &scripts_run_info);
 
-  scripts_run_info.LogRun(frame, run_location);
+  scripts_run_info.LogRun(web_frame, run_location);
 }
 
 void ScriptInjectionManager::TryToInject(
@@ -374,10 +369,13 @@
   // Try to inject the script. If the injection is waiting (i.e., for
   // permission), add it to the list of pending injections. If the injection
   // has blocked, add it to the list of running injections.
+  // The Unretained below is safe because this object owns all the
+  // ScriptInjections, so is guaranteed to outlive them.
   switch (injection->TryToInject(
       run_location,
       scripts_run_info,
-      this)) {
+      base::Bind(&ScriptInjectionManager::OnInjectionFinished,
+                 base::Unretained(this)))) {
     case ScriptInjection::INJECTION_WAITING:
       pending_injections_.push_back(injection.Pass());
       break;
@@ -391,22 +389,7 @@
 
 void ScriptInjectionManager::HandleExecuteCode(
     const ExtensionMsg_ExecuteCode_Params& params,
-    content::RenderView* render_view) {
-  // TODO(dcheng): Not sure how this can happen today. In an OOPI world, it
-  // would indicate a logic error--the browser must direct this request to the
-  // right renderer process to begin with.
-  blink::WebLocalFrame* main_frame =
-      render_view->GetWebView()->mainFrame()->toWebLocalFrame();
-  if (!main_frame) {
-    render_view->Send(
-        new ExtensionHostMsg_ExecuteCodeFinished(render_view->GetRoutingID(),
-                                                 params.request_id,
-                                                 "No main frame",
-                                                 GURL(std::string()),
-                                                 base::ListValue()));
-    return;
-  }
-
+    content::RenderFrame* render_frame) {
   scoped_ptr<const InjectionHost> injection_host;
   if (params.host_id.type() == HostID::EXTENSIONS) {
     injection_host = ExtensionInjectionHost::Create(params.host_id.id(),
@@ -418,16 +401,17 @@
         new WebUIInjectionHost(params.host_id));
   }
 
+  blink::WebLocalFrame* web_frame = render_frame->GetWebFrame();
   scoped_ptr<ScriptInjection> injection(new ScriptInjection(
       scoped_ptr<ScriptInjector>(
-          new ProgrammaticScriptInjector(params, main_frame)),
-      main_frame,
+          new ProgrammaticScriptInjector(params, web_frame)),
+      web_frame,
       injection_host.Pass(),
       static_cast<UserScript::RunLocation>(params.run_at),
-      ExtensionHelper::Get(render_view)->tab_id()));
+      ExtensionHelper::Get(render_frame->GetRenderView())->tab_id()));
 
   ScriptsRunInfo scripts_run_info;
-  FrameStatusMap::const_iterator iter = frame_statuses_.find(main_frame);
+  FrameStatusMap::const_iterator iter = frame_statuses_.find(render_frame);
 
   TryToInject(
       injection.Pass(),
@@ -436,17 +420,16 @@
 }
 
 void ScriptInjectionManager::HandleExecuteDeclarativeScript(
-    blink::WebFrame* web_frame,
+    content::RenderFrame* render_frame,
     int tab_id,
     const ExtensionId& extension_id,
     int script_id,
     const GURL& url) {
-  // TODO(dcheng): This function signature should really be a WebLocalFrame,
-  // rather than trying to coerce it here.
+  blink::WebLocalFrame* web_frame = render_frame->GetWebFrame();
   scoped_ptr<ScriptInjection> injection =
       user_script_set_manager_->GetInjectionForDeclarativeScript(
           script_id,
-          web_frame->toWebLocalFrame(),
+          web_frame,
           tab_id,
           url,
           extension_id);
@@ -475,7 +458,7 @@
 
   // At this point, because the request is present in pending_injections_, we
   // know that this is the same page that issued the request (otherwise,
-  // RVOHelper's DidStartProvisionalLoad callback would have caused it to be
+  // RFOHelper's DidStartProvisionalLoad callback would have caused it to be
   // cleared out).
 
   scoped_ptr<ScriptInjection> injection(*iter);
diff --git a/extensions/renderer/script_injection_manager.h b/extensions/renderer/script_injection_manager.h
index 29ab11e..a0416b3 100644
--- a/extensions/renderer/script_injection_manager.h
+++ b/extensions/renderer/script_injection_manager.h
@@ -19,13 +19,8 @@
 
 struct ExtensionMsg_ExecuteCode_Params;
 
-namespace blink {
-class WebFrame;
-class WebLocalFrame;
-}
-
 namespace content {
-class RenderView;
+class RenderFrame;
 }
 
 namespace extensions {
@@ -43,38 +38,39 @@
   virtual ~ScriptInjectionManager();
 
   // Notifies that a new render view has been created.
-  void OnRenderViewCreated(content::RenderView* render_view);
+  void OnRenderFrameCreated(content::RenderFrame* render_frame);
 
   // Removes pending injections of the unloaded extension.
   void OnExtensionUnloaded(const std::string& extension_id);
 
+ private:
+  // A RenderFrameObserver implementation which watches the various render
+  // frames in order to notify the ScriptInjectionManager of different
+  // document load states and IPCs.
+  class RFOHelper;
+
+  using FrameStatusMap =
+      std::map<content::RenderFrame*, UserScript::RunLocation>;
+
   // Notifies that an injection has been finished.
   void OnInjectionFinished(ScriptInjection* injection);
 
- private:
-  // A RenderViewObserver implementation which watches the various render views
-  // in order to notify the ScriptInjectionManager of different document load
-  // states.
-  class RVOHelper;
-
-  typedef std::map<blink::WebFrame*, UserScript::RunLocation> FrameStatusMap;
-
   // UserScriptSetManager::Observer implementation.
   void OnUserScriptsUpdated(const std::set<HostID>& changed_hosts,
                             const std::vector<UserScript*>& scripts) override;
 
-  // Notifies that an RVOHelper should be removed.
-  void RemoveObserver(RVOHelper* helper);
+  // Notifies that an RFOHelper should be removed.
+  void RemoveObserver(RFOHelper* helper);
 
   // Invalidate any pending tasks associated with |frame|.
-  void InvalidateForFrame(blink::WebFrame* frame);
+  void InvalidateForFrame(content::RenderFrame* frame);
 
   // Starts the process to inject appropriate scripts into |frame|.
-  void StartInjectScripts(blink::WebFrame* frame,
+  void StartInjectScripts(content::RenderFrame* frame,
                           UserScript::RunLocation run_location);
 
   // Actually injects the scripts into |frame|.
-  void InjectScripts(blink::WebFrame* frame,
+  void InjectScripts(content::RenderFrame* frame,
                      UserScript::RunLocation run_location);
 
   // Try to inject and store injection if it has not finished.
@@ -84,10 +80,10 @@
 
   // Handle the ExecuteCode extension message.
   void HandleExecuteCode(const ExtensionMsg_ExecuteCode_Params& params,
-                         content::RenderView* render_view);
+                         content::RenderFrame* render_frame);
 
   // Handle the ExecuteDeclarativeScript extension message.
-  void HandleExecuteDeclarativeScript(blink::WebFrame* web_frame,
+  void HandleExecuteDeclarativeScript(content::RenderFrame* web_frame,
                                       int tab_id,
                                       const ExtensionId& extension_id,
                                       int script_id,
@@ -103,8 +99,8 @@
   // RunLocation of the frame corresponds to the last location that has ran.
   FrameStatusMap frame_statuses_;
 
-  // The collection of RVOHelpers.
-  ScopedVector<RVOHelper> rvo_helpers_;
+  // The collection of RFOHelpers.
+  ScopedVector<RFOHelper> rfo_helpers_;
 
   // The set of UserScripts associated with extensions. Owned by the Dispatcher.
   UserScriptSetManager* user_script_set_manager_;
diff --git a/extensions/renderer/script_injector.h b/extensions/renderer/script_injector.h
index 1c98aa0..b550200 100644
--- a/extensions/renderer/script_injector.h
+++ b/extensions/renderer/script_injector.h
@@ -40,9 +40,6 @@
   // Returns the script type of this particular injection.
   virtual UserScript::InjectionType script_type() const = 0;
 
-  // Returns true if the script should execute in child frames.
-  virtual bool ShouldExecuteInChildFrames() const = 0;
-
   // Returns true if the script should execute in the main world.
   virtual bool ShouldExecuteInMainWorld() const = 0;
 
@@ -84,7 +81,7 @@
   // Notifies the script that injection has completed, with a possibly-populated
   // list of results (depending on whether or not ExpectsResults() was true).
   virtual void OnInjectionComplete(
-      scoped_ptr<base::ListValue> execution_results,
+      scoped_ptr<base::Value> execution_result,
       UserScript::RunLocation run_location) = 0;
 
   // Notifies the script that injection will never occur.
diff --git a/extensions/renderer/user_script_injector.cc b/extensions/renderer/user_script_injector.cc
index 9a5a26d9..53e1263 100644
--- a/extensions/renderer/user_script_injector.cc
+++ b/extensions/renderer/user_script_injector.cc
@@ -125,10 +125,6 @@
   return UserScript::CONTENT_SCRIPT;
 }
 
-bool UserScriptInjector::ShouldExecuteInChildFrames() const {
-  return false;
-}
-
 bool UserScriptInjector::ShouldExecuteInMainWorld() const {
   return false;
 }
@@ -260,7 +256,7 @@
 }
 
 void UserScriptInjector::OnInjectionComplete(
-    scoped_ptr<base::ListValue> execution_results,
+    scoped_ptr<base::Value> execution_result,
     UserScript::RunLocation run_location) {
 }
 
diff --git a/extensions/renderer/user_script_injector.h b/extensions/renderer/user_script_injector.h
index 748fa297..f3a0029cc 100644
--- a/extensions/renderer/user_script_injector.h
+++ b/extensions/renderer/user_script_injector.h
@@ -37,7 +37,6 @@
 
   // ScriptInjector implementation.
   UserScript::InjectionType script_type() const override;
-  bool ShouldExecuteInChildFrames() const override;
   bool ShouldExecuteInMainWorld() const override;
   bool IsUserGesture() const override;
   bool ExpectsResults() const override;
@@ -54,7 +53,7 @@
       UserScript::RunLocation run_location) const override;
   void GetRunInfo(ScriptsRunInfo* scripts_run_info,
                   UserScript::RunLocation run_location) const override;
-  void OnInjectionComplete(scoped_ptr<base::ListValue> execution_results,
+  void OnInjectionComplete(scoped_ptr<base::Value> execution_result,
                            UserScript::RunLocation run_location) override;
   void OnWillNotInject(InjectFailureReason reason) override;
 
diff --git a/extensions/shell/renderer/shell_content_renderer_client.cc b/extensions/shell/renderer/shell_content_renderer_client.cc
index 88df15c..a123363 100644
--- a/extensions/shell/renderer/shell_content_renderer_client.cc
+++ b/extensions/shell/renderer/shell_content_renderer_client.cc
@@ -68,6 +68,7 @@
     content::RenderFrame* render_frame) {
   // ExtensionFrameHelper destroys itself when the RenderFrame is destroyed.
   new ExtensionFrameHelper(render_frame, extension_dispatcher_.get());
+  extension_dispatcher_->OnRenderFrameCreated(render_frame);
 
   // TODO(jamescook): Do we need to add a new PepperHelper(render_frame) here?
   // It doesn't seem necessary for either Pepper or NaCl.
@@ -80,7 +81,6 @@
 void ShellContentRendererClient::RenderViewCreated(
     content::RenderView* render_view) {
   new ExtensionHelper(render_view, extension_dispatcher_.get());
-  extension_dispatcher_->OnRenderViewCreated(render_view);
 }
 
 bool ShellContentRendererClient::OverrideCreatePlugin(