Convert ExtensionMsg_ExecuteCode to LocalFrame mojom message

This CL converts ExtensionMsg_ExecuteCode and
ExtensionHostMsg_ExecuteCodeFinished to ExecuteCode()
in extensions::mojom::LocalFrame.

It cleans up extension_messages_param_traits.{cc,h} and the
unit test for the traits.

Bug: 1180858
Change-Id: I7a92bbbf9eaa89995a57c7b31f000d7303f3ffaf
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2752767
Commit-Queue: Julie Kim <[email protected]>
Reviewed-by: Dave Tapuska <[email protected]>
Reviewed-by: Reilly Grant <[email protected]>
Reviewed-by: Sam McNally <[email protected]>
Cr-Commit-Position: refs/heads/master@{#864179}
diff --git a/extensions/browser/script_executor.cc b/extensions/browser/script_executor.cc
index d3b63a1..2615916 100644
--- a/extensions/browser/script_executor.cc
+++ b/extensions/browser/script_executor.cc
@@ -10,13 +10,16 @@
 #include "base/bind.h"
 #include "base/check_op.h"
 #include "base/hash/hash.h"
+#include "base/memory/weak_ptr.h"
 #include "base/pickle.h"
 #include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_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"
 #include "extensions/browser/extension_api_frame_id_map.h"
 #include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_web_contents_observer.h"
 #include "extensions/browser/url_loader_factory_manager.h"
 #include "extensions/common/extension_messages.h"
 #include "extensions/common/mojom/host_id.mojom.h"
@@ -55,14 +58,13 @@
 
   Handler(ScriptsExecutedOnceCallback observer,
           content::WebContents* web_contents,
-          const mojom::ExecuteCodeParams& params,
+          mojom::ExecuteCodeParamsPtr params,
           ScriptExecutor::FrameScope scope,
           const std::vector<int>& frame_ids,
           ScriptExecutor::ScriptFinishedCallback callback)
       : content::WebContentsObserver(web_contents),
         observer_(std::move(observer)),
-        host_id_(params.host_id->type, params.host_id->id),
-        request_id_(params.request_id),
+        host_id_(params->host_id->type, params->host_id->id),
         callback_(std::move(callback)) {
     for (int frame_id : frame_ids) {
       content::RenderFrameHost* frame =
@@ -113,7 +115,7 @@
     }
 
     for (content::RenderFrameHost* frame : pending_render_frames_)
-      SendExecuteCode(params, frame);
+      SendExecuteCode(params.Clone(), frame);
 
     if (pending_render_frames_.empty())
       Finish();
@@ -138,27 +140,6 @@
     Finish();
   }
 
-  bool OnMessageReceived(const IPC::Message& message,
-                         content::RenderFrameHost* render_frame_host) override {
-    // Unpack by hand to check the request_id, since there may be multiple
-    // requests in flight but only one is for this.
-    if (message.type() != ExtensionHostMsg_ExecuteCodeFinished::ID)
-      return false;
-
-    int message_request_id;
-    base::PickleIterator iter(message);
-    CHECK(iter.ReadInt(&message_request_id));
-
-    if (message_request_id != request_id_)
-      return false;
-
-    IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(Handler, message, render_frame_host)
-      IPC_MESSAGE_HANDLER(ExtensionHostMsg_ExecuteCodeFinished,
-                          OnExecuteCodeFinished)
-    IPC_END_MESSAGE_MAP()
-    return true;
-  }
-
   void RenderFrameDeleted(
       content::RenderFrameHost* render_frame_host) override {
     int erased_count = base::Erase(pending_render_frames_, render_frame_host);
@@ -183,21 +164,31 @@
 
   // Sends an ExecuteCode message to the given frame host, and increments
   // the number of pending messages.
-  void SendExecuteCode(const mojom::ExecuteCodeParams& params,
+  void SendExecuteCode(mojom::ExecuteCodeParamsPtr params,
                        content::RenderFrameHost* frame) {
     DCHECK(frame->IsRenderFrameLive());
     DCHECK(base::Contains(pending_render_frames_, frame));
     URLLoaderFactoryManager::WillExecuteCode(frame, host_id_);
-    frame->Send(new ExtensionMsg_ExecuteCode(frame->GetRoutingID(), params));
+    ExtensionWebContentsObserver::GetForWebContents(web_contents())
+        ->GetLocalFrame(frame)
+        ->ExecuteCode(std::move(params),
+                      base::BindOnce(&Handler::OnExecuteCodeFinished,
+                                     weak_ptr_factory_.GetWeakPtr(),
+                                     frame->GetProcess()->GetID(),
+                                     frame->GetRoutingID()));
   }
 
   // Handles the ExecuteCodeFinished message.
-  void OnExecuteCodeFinished(content::RenderFrameHost* render_frame_host,
-                             int request_id,
+  void OnExecuteCodeFinished(int render_process_id,
+                             int render_frame_id,
                              const std::string& error,
                              const GURL& on_url,
-                             const base::Optional<base::Value>& result) {
-    DCHECK_EQ(request_id_, request_id);
+                             base::Optional<base::Value> result) {
+    auto* render_frame_host =
+        content::RenderFrameHost::FromID(render_process_id, render_frame_id);
+    if (!render_frame_host)
+      return;
+
     DCHECK(!pending_render_frames_.empty());
     size_t erased = base::Erase(pending_render_frames_, render_frame_host);
     DCHECK_EQ(1u, erased);
@@ -251,9 +242,6 @@
   // The id of the host (the extension or the webui) doing the injection.
   mojom::HostID host_id_;
 
-  // The request id of the injection.
-  int request_id_ = 0;
-
   // The id of the primary frame of the injection, if only a single frame is
   // explicitly specified.
   base::Optional<int> root_rfh_id_;
@@ -273,6 +261,8 @@
   // The callback to run after all injections complete.
   ScriptExecutor::ScriptFinishedCallback callback_;
 
+  base::WeakPtrFactory<Handler> weak_ptr_factory_{this};
+
   DISALLOW_COPY_AND_ASSIGN(Handler);
 };
 
@@ -316,30 +306,29 @@
     CHECK(process_type == WEB_VIEW_PROCESS);
   }
 
-  mojom::ExecuteCodeParams params;
-  params.request_id = next_request_id_++;
-  params.host_id = mojom::HostID::New(host_id.type, host_id.id);
-  params.action_type = action_type;
-  params.code = code;
-  params.match_about_blank = (about_blank == MATCH_ABOUT_BLANK);
-  params.run_at = run_at;
-  params.is_web_view = (process_type == WEB_VIEW_PROCESS);
-  params.webview_src = webview_src;
-  params.script_url = script_url;
-  params.wants_result = (result_type == JSON_SERIALIZED_RESULT);
-  params.user_gesture = user_gesture;
-  params.css_origin = css_origin;
+  auto params = mojom::ExecuteCodeParams::New();
+  params->host_id = host_id.Clone();
+  params->action_type = action_type;
+  params->code = code;
+  params->match_about_blank = (about_blank == MATCH_ABOUT_BLANK);
+  params->run_at = run_at;
+  params->is_web_view = (process_type == WEB_VIEW_PROCESS);
+  params->webview_src = webview_src;
+  params->script_url = script_url;
+  params->wants_result = (result_type == JSON_SERIALIZED_RESULT);
+  params->user_gesture = user_gesture;
+  params->css_origin = css_origin;
 
   // Generate the unique key that represents this CSS injection or removal
   // from an extension (i.e. tabs.insertCSS or tabs.removeCSS).
   if (host_id.type == mojom::HostID::HostType::kExtensions &&
       (action_type == mojom::ActionType::kAddCss ||
        action_type == mojom::ActionType::kRemoveCss))
-    params.injection_key = GenerateInjectionKey(host_id, script_url, code);
+    params->injection_key = GenerateInjectionKey(host_id, script_url, code);
 
   // Handler handles IPCs and deletes itself on completion.
-  new Handler(observer_, web_contents_, params, frame_scope, frame_ids,
-              std::move(callback));
+  new Handler(observer_, web_contents_, std::move(params), frame_scope,
+              frame_ids, std::move(callback));
 }
 
 }  // namespace extensions
diff --git a/extensions/browser/script_executor.h b/extensions/browser/script_executor.h
index 0ad4f85..2c6cb7ea 100644
--- a/extensions/browser/script_executor.h
+++ b/extensions/browser/script_executor.h
@@ -40,7 +40,7 @@
 
 // Interface for executing extension content scripts (e.g. executeScript) as
 // described by the mojom::ExecuteCodeParams IPC, and notifying the
-// caller when responded with ExtensionHostMsg_ExecuteCodeFinished.
+// caller when responded with ExecuteCodeCallback.
 class ScriptExecutor {
  public:
   explicit ScriptExecutor(content::WebContents* web_contents);
@@ -129,9 +129,6 @@
   }
 
  private:
-  // The next value to use for request_id in mojom::ExecuteCodeParams.
-  int next_request_id_ = 0;
-
   content::WebContents* web_contents_;
 
   ScriptsExecutedNotification observer_;
diff --git a/extensions/browser/url_loader_factory_manager.cc b/extensions/browser/url_loader_factory_manager.cc
index 48fc8e3..1dc22d7 100644
--- a/extensions/browser/url_loader_factory_manager.cc
+++ b/extensions/browser/url_loader_factory_manager.cc
@@ -281,10 +281,11 @@
 
   // When WillExecuteCode runs, the frame already received the initial
   // URLLoaderFactoryBundle - therefore we need to request a separate push
-  // below.  This doesn't race with the ExtensionMsg_ExecuteCode message,
+  // below.  This doesn't race with the ExecuteCode mojo message,
   // because the URLLoaderFactoryBundle is sent to the renderer over
-  // content.mojom.FrameNavigationControl interface which is associated with the
-  // legacy IPC pipe (raciness will be introduced if that ever changes).
+  // content.mojom.Frame interface which is associated with the
+  // extensions.mojom.LocalFrame (raciness will be introduced if that ever
+  // changes).
   constexpr bool kPushToRendererNow = true;
 
   MarkIsolatedWorldsAsRequiringSeparateURLLoaderFactory(
diff --git a/extensions/browser/url_loader_factory_manager.h b/extensions/browser/url_loader_factory_manager.h
index b0eb6fc..828ae4c 100644
--- a/extensions/browser/url_loader_factory_manager.h
+++ b/extensions/browser/url_loader_factory_manager.h
@@ -46,7 +46,7 @@
   // |navigation|.
   static void ReadyToCommitNavigation(content::NavigationHandle* navigation);
 
-  // To be called before ExtensionMsg_ExecuteCode is sent to a renderer process
+  // To be called before ExecuteCode is sent to a renderer process
   // (to ensure that the renderer gets the special URLLoaderFactory before
   // injecting content script requested via chrome.tabs.executeScript).
   //
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn
index 19a664e4..f2cda25 100644
--- a/extensions/common/BUILD.gn
+++ b/extensions/common/BUILD.gn
@@ -174,8 +174,6 @@
       "extension_message_generator.h",
       "extension_messages.cc",
       "extension_messages.h",
-      "extension_messages_param_traits.cc",
-      "extension_messages_param_traits.h",
       "extension_paths.cc",
       "extension_paths.h",
       "extension_resource.cc",
@@ -467,7 +465,6 @@
       "extension_builder_unittest.cc",
       "extension_icon_set_unittest.cc",
       "extension_l10n_util_unittest.cc",
-      "extension_messages_param_traits_unittest.cc",
       "extension_messages_unittest.cc",
       "extension_resource_path_normalizer_unittest.cc",
       "extension_resource_unittest.cc",
diff --git a/extensions/common/extension_messages.h b/extensions/common/extension_messages.h
index 7820f099..6cbe9ff 100644
--- a/extensions/common/extension_messages.h
+++ b/extensions/common/extension_messages.h
@@ -31,7 +31,6 @@
 #include "extensions/common/event_filtering_info.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_guid.h"
-#include "extensions/common/extension_messages_param_traits.h"
 #include "extensions/common/extensions_client.h"
 #include "extensions/common/message_bundle.h"
 #include "extensions/common/mojom/action_type.mojom-shared.h"
@@ -74,9 +73,6 @@
 IPC_ENUM_TRAITS_MAX_VALUE(extensions::MessagingEndpoint::Type,
                           extensions::MessagingEndpoint::Type::kLast)
 
-IPC_ENUM_TRAITS_MAX_VALUE(extensions::mojom::HostID::HostType,
-                          extensions::mojom::HostID::HostType::kMaxValue)
-
 // Parameters structure for ExtensionHostMsg_AddAPIActionToActivityLog and
 // ExtensionHostMsg_AddEventToActivityLog.
 IPC_STRUCT_BEGIN(ExtensionHostMsg_APIActionOrEvent_Params)
@@ -167,55 +163,6 @@
   IPC_STRUCT_MEMBER(extensions::EventFilteringInfo, filtering_info)
 IPC_STRUCT_END()
 
-IPC_STRUCT_TRAITS_BEGIN(extensions::mojom::HostID)
-  IPC_STRUCT_TRAITS_MEMBER(type)
-  IPC_STRUCT_TRAITS_MEMBER(id)
-IPC_STRUCT_TRAITS_END()
-
-// Allows an extension to execute code in a tab.
-IPC_STRUCT_TRAITS_BEGIN(extensions::mojom::ExecuteCodeParams)
-  // The extension API request id, for responding.
-  IPC_STRUCT_TRAITS_MEMBER(request_id)
-
-  // The ID of the requesting injection host.
-  IPC_STRUCT_TRAITS_MEMBER(host_id)
-
-  // Whether the code is JavaScript or CSS.
-  IPC_STRUCT_TRAITS_MEMBER(action_type)
-
-  // String of code to execute.
-  IPC_STRUCT_TRAITS_MEMBER(code)
-
-  // The webview guest source who calls to execute code.
-  IPC_STRUCT_TRAITS_MEMBER(webview_src)
-
-  // Whether to inject into about:blank (sub)frames.
-  IPC_STRUCT_TRAITS_MEMBER(match_about_blank)
-
-  // When to inject the code.
-  IPC_STRUCT_TRAITS_MEMBER(run_at)
-
-  // Whether the request is coming from a <webview>.
-  IPC_STRUCT_TRAITS_MEMBER(is_web_view)
-
-  // Whether the caller is interested in the result value. Manifest-declared
-  // content scripts and executeScript() calls without a response callback
-  // are examples of when this will be false.
-  IPC_STRUCT_TRAITS_MEMBER(wants_result)
-
-  // The URL of the script that was injected, if any.
-  IPC_STRUCT_TRAITS_MEMBER(script_url)
-
-  // Whether the code to be executed should be associated with a user gesture.
-  IPC_STRUCT_TRAITS_MEMBER(user_gesture)
-
-  // The origin of the CSS.
-  IPC_STRUCT_TRAITS_MEMBER(css_origin)
-
-  // The autogenerated key for the CSS injection.
-  IPC_STRUCT_TRAITS_MEMBER(injection_key)
-IPC_STRUCT_TRAITS_END()
-
 // Struct containing information about the sender of connect() calls that
 // originate from a tab.
 IPC_STRUCT_BEGIN(ExtensionMsg_TabConnectionInfo)
@@ -550,10 +497,6 @@
 IPC_MESSAGE_CONTROL1(ExtensionMsg_Loaded,
                      std::vector<ExtensionMsg_Loaded_Params>)
 
-// Notification that renderer should run some JavaScript code.
-IPC_MESSAGE_ROUTED1(ExtensionMsg_ExecuteCode,
-                    extensions::mojom::ExecuteCodeParams)
-
 // Tell the render view which browser window it's being attached to.
 IPC_MESSAGE_ROUTED1(ExtensionMsg_UpdateBrowserWindowId,
                     int /* id of browser window */)
@@ -729,17 +672,6 @@
     std::string /* extension id */,
     extensions::MessageBundle::SubstitutionMap /* message bundle */)
 
-// Sent from the renderer to the browser to return the script running result.
-IPC_MESSAGE_ROUTED4(
-    ExtensionHostMsg_ExecuteCodeFinished,
-    int /* request id */,
-    std::string /* error; empty implies success */,
-    GURL /* URL of the code executed on. May be empty if unsuccessful. */,
-    base::Optional<base::Value> /* result of the script, if any. We use a
-                                   base::Optional<> here to differentiate
-                                   between no result (such as in the case of an
-                                   error) and a null result. */)
-
 // Sent from the renderer to the browser to notify that content scripts are
 // running in the renderer that the IPC originated from.
 IPC_MESSAGE_ROUTED2(ExtensionHostMsg_ContentScriptsExecuting,
diff --git a/extensions/common/extension_messages_param_traits.cc b/extensions/common/extension_messages_param_traits.cc
deleted file mode 100644
index 6b587fc6..0000000
--- a/extensions/common/extension_messages_param_traits.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "extensions/common/extension_messages_param_traits.h"
-
-#include "base/pickle.h"
-#include "extensions/common/extension_messages.h"
-
-namespace IPC {
-
-using extensions::mojom::HostIDPtr;
-
-void ParamTraits<HostIDPtr>::Write(base::Pickle* m, const param_type& p) {
-  WriteParam(m, p->type);
-  m->WriteString(p->id);
-}
-
-bool ParamTraits<HostIDPtr>::Read(const base::Pickle* m,
-                                  base::PickleIterator* iter,
-                                  param_type* p) {
-  bool success = true;
-
-  extensions::mojom::HostID::HostType type;
-  success &= ReadParam(m, iter, &type);
-  std::string id;
-  success &= iter->ReadString(&id);
-
-  if (success)
-    *p = extensions::mojom::HostID::New(type, id);
-  return success;
-}
-
-void ParamTraits<HostIDPtr>::Log(const param_type& p, std::string* l) {}
-
-}  // namespace IPC
diff --git a/extensions/common/extension_messages_param_traits.h b/extensions/common/extension_messages_param_traits.h
deleted file mode 100644
index e62b438d..0000000
--- a/extensions/common/extension_messages_param_traits.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// TODO(crbug.com/1180858): These traits files,
-// extension_messages_param_traits.{cc,h}, are required for sending extension
-// mojom types to legacy IPCs. Once the mojofication of extension is done, these
-// traits should be removed.
-
-#ifndef EXTENSIONS_COMMON_EXTENSION_MESSAGES_PARAM_TRAITS_H_
-#define EXTENSIONS_COMMON_EXTENSION_MESSAGES_PARAM_TRAITS_H_
-
-#include "extensions/common/mojom/host_id.mojom.h"
-
-#include "ipc/ipc_message_utils.h"
-
-namespace base {
-class Pickle;
-class PickleIterator;
-}  // namespace base
-
-namespace IPC {
-
-template <>
-struct ParamTraits<extensions::mojom::HostIDPtr> {
-  typedef extensions::mojom::HostIDPtr param_type;
-  static void Write(base::Pickle* m, const param_type& p);
-  static bool Read(const base::Pickle* m,
-                   base::PickleIterator* iter,
-                   param_type* r);
-  static void Log(const param_type& p, std::string* l);
-};
-
-}  // namespace IPC
-
-#endif  // EXTENSIONS_COMMON_EXTENSION_MESSAGES_PARAM_TRAITS_H_
diff --git a/extensions/common/extension_messages_param_traits_unittest.cc b/extensions/common/extension_messages_param_traits_unittest.cc
deleted file mode 100644
index 4614c8a5..0000000
--- a/extensions/common/extension_messages_param_traits_unittest.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "extensions/common/extension_messages_param_traits.h"
-
-#include "extensions/common/mojom/host_id.mojom.h"
-#include "ipc/ipc_message_macros.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-TEST(ExtensionsParamTraitsTest, HostIDPtrTest) {
-  constexpr char kTestId[] = "aaaaaaaaaaaaa";
-
-  extensions::mojom::HostIDPtr params = extensions::mojom::HostID::New();
-  params->type = extensions::mojom::HostID::HostType::kWebUi;
-  params->id = kTestId;
-
-  IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL);
-  IPC::WriteParam(&msg, params);
-
-  base::PickleIterator iter(msg);
-  extensions::mojom::HostIDPtr output;
-
-  EXPECT_TRUE(IPC::ReadParam(&msg, &iter, &output));
-  EXPECT_EQ(output->type, extensions::mojom::HostID::HostType::kWebUi);
-  EXPECT_EQ(output->id, kTestId);
-}
diff --git a/extensions/common/mojom/frame.mojom b/extensions/common/mojom/frame.mojom
index c6d8e3fd..692a95b 100644
--- a/extensions/common/mojom/frame.mojom
+++ b/extensions/common/mojom/frame.mojom
@@ -14,8 +14,6 @@
 
 // Allows an extension to execute code in a tab.
 struct ExecuteCodeParams {
-  // The extension API request id, for responding.
-  int32 request_id;
   // The ID of the requesting injection host.
   HostID host_id;
   // Whether the code is JavaScript or CSS.
@@ -71,6 +69,15 @@
                 string function_name,
                 mojo_base.mojom.ListValue args);
 
+  // Notifies the renderer that it should run some JavaScript code. The reply
+  // is sent back to the browser to return the script running result. An empty
+  // |error| implies success. |url| is the URL that the code executed on. It may
+  // be empty if it's unsuccessful. We use an optional for |result| to
+  // differentiate between no result (such as in the case of an error) and a
+  // null result.
+  ExecuteCode(ExecuteCodeParams param) =>
+      (string error, url.mojom.Url url, mojo_base.mojom.Value? result);
+
   // Trigger to execute declarative content script under browser control.
   ExecuteDeclarativeScript(int32 tab_id,
                            string extension_id,
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index e667566a1..968645bc 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -721,6 +721,13 @@
       render_frame, tab_id, extension_id, script_id, url);
 }
 
+void Dispatcher::ExecuteCode(mojom::ExecuteCodeParamsPtr param,
+                             mojom::LocalFrame::ExecuteCodeCallback callback,
+                             content::RenderFrame* render_frame) {
+  script_injection_manager_->HandleExecuteCode(
+      std::move(param), std::move(callback), render_frame);
+}
+
 // static
 std::vector<Dispatcher::JsResourceInfo> Dispatcher::GetJsResources() {
   // Libraries.
diff --git a/extensions/renderer/dispatcher.h b/extensions/renderer/dispatcher.h
index 24c31ff..3fc776c 100644
--- a/extensions/renderer/dispatcher.h
+++ b/extensions/renderer/dispatcher.h
@@ -25,6 +25,7 @@
 #include "extensions/common/extensions_client.h"
 #include "extensions/common/features/feature.h"
 #include "extensions/common/mojom/feature_session_type.mojom.h"
+#include "extensions/common/mojom/frame.mojom.h"
 #include "extensions/common/mojom/host_id.mojom-forward.h"
 #include "extensions/common/mojom/renderer.mojom.h"
 #include "extensions/renderer/resource_bundle_source_map.h"
@@ -188,6 +189,11 @@
                                 const std::string& script_id,
                                 const GURL& url);
 
+  // Executes the code described in |param| and calls |callback| if it's done.
+  void ExecuteCode(mojom::ExecuteCodeParamsPtr param,
+                   mojom::LocalFrame::ExecuteCodeCallback callback,
+                   content::RenderFrame* render_frame);
+
   struct JsResourceInfo {
     const char* name = nullptr;
     int id = 0;
diff --git a/extensions/renderer/extension_frame_helper.cc b/extensions/renderer/extension_frame_helper.cc
index f83ba12..a6f5865 100644
--- a/extensions/renderer/extension_frame_helper.cc
+++ b/extensions/renderer/extension_frame_helper.cc
@@ -471,6 +471,12 @@
       base::Value::AsListValue(args));
 }
 
+void ExtensionFrameHelper::ExecuteCode(mojom::ExecuteCodeParamsPtr param,
+                                       ExecuteCodeCallback callback) {
+  extension_dispatcher_->ExecuteCode(std::move(param), std::move(callback),
+                                     render_frame());
+}
+
 void ExtensionFrameHelper::SetFrameName(const std::string& name) {
   render_frame()->GetWebFrame()->SetName(blink::WebString::FromUTF8(name));
 }
diff --git a/extensions/renderer/extension_frame_helper.h b/extensions/renderer/extension_frame_helper.h
index f70c4a5..48b94d5 100644
--- a/extensions/renderer/extension_frame_helper.h
+++ b/extensions/renderer/extension_frame_helper.h
@@ -109,6 +109,10 @@
                      const std::string& module_name,
                      const std::string& function_name,
                      const base::Value args) override;
+
+  void ExecuteCode(mojom::ExecuteCodeParamsPtr param,
+                   ExecuteCodeCallback callback) override;
+
   void ExecuteDeclarativeScript(int32_t tab_id,
                                 const std::string& extension_id,
                                 const std::string& script_id,
diff --git a/extensions/renderer/programmatic_script_injector.cc b/extensions/renderer/programmatic_script_injector.cc
index 932e617..69bd7196 100644
--- a/extensions/renderer/programmatic_script_injector.cc
+++ b/extensions/renderer/programmatic_script_injector.cc
@@ -14,7 +14,6 @@
 #include "extensions/common/extension_messages.h"
 #include "extensions/common/manifest_constants.h"
 #include "extensions/common/mojom/action_type.mojom-shared.h"
-#include "extensions/common/mojom/frame.mojom.h"
 #include "extensions/common/mojom/host_id.mojom.h"
 #include "extensions/common/permissions/api_permission.h"
 #include "extensions/common/permissions/permissions_data.h"
@@ -30,8 +29,9 @@
 namespace extensions {
 
 ProgrammaticScriptInjector::ProgrammaticScriptInjector(
-    mojom::ExecuteCodeParamsPtr params)
-    : params_(std::move(params)), finished_(false) {}
+    mojom::ExecuteCodeParamsPtr params,
+    mojom::LocalFrame::ExecuteCodeCallback callback)
+    : params_(std::move(params)), callback_(std::move(callback)) {}
 
 ProgrammaticScriptInjector::~ProgrammaticScriptInjector() {
 }
@@ -143,18 +143,15 @@
 
 void ProgrammaticScriptInjector::OnInjectionComplete(
     std::unique_ptr<base::Value> execution_result,
-    mojom::RunLocation run_location,
-    content::RenderFrame* render_frame) {
+    mojom::RunLocation run_location) {
   DCHECK(!result_.has_value());
   if (execution_result) {
     result_ = base::Value::FromUniquePtrValue(std::move(execution_result));
   }
-  Finish(std::string(), render_frame);
+  Finish(std::string());
 }
 
-void ProgrammaticScriptInjector::OnWillNotInject(
-    InjectFailureReason reason,
-    content::RenderFrame* render_frame) {
+void ProgrammaticScriptInjector::OnWillNotInject(InjectFailureReason reason) {
   std::string error;
   switch (reason) {
     case NOT_ALLOWED:
@@ -173,7 +170,7 @@
     case WONT_INJECT:
       break;
   }
-  Finish(error, render_frame);
+  Finish(error);
 }
 
 bool ProgrammaticScriptInjector::CanShowUrlInError() const {
@@ -187,19 +184,12 @@
       APIPermission::kTab);
 }
 
-void ProgrammaticScriptInjector::Finish(const std::string& error,
-                                        content::RenderFrame* render_frame) {
+void ProgrammaticScriptInjector::Finish(const std::string& error) {
   DCHECK(!finished_);
   finished_ = true;
 
-  // It's possible that the render frame was destroyed in the course of
-  // injecting scripts. Don't respond if it was (the browser side watches for
-  // frame deletions so nothing is left hanging).
-  if (render_frame) {
-    render_frame->Send(new ExtensionHostMsg_ExecuteCodeFinished(
-        render_frame->GetRoutingID(), params_->request_id, error, url_,
-        result_));
-  }
+  if (callback_)
+    std::move(callback_).Run(error, url_, std::move(result_));
 }
 
 }  // namespace extensions
diff --git a/extensions/renderer/programmatic_script_injector.h b/extensions/renderer/programmatic_script_injector.h
index 6be6317..393661b4 100644
--- a/extensions/renderer/programmatic_script_injector.h
+++ b/extensions/renderer/programmatic_script_injector.h
@@ -11,22 +11,20 @@
 #include "base/optional.h"
 #include "base/values.h"
 #include "extensions/common/mojom/css_origin.mojom-shared.h"
-#include "extensions/common/mojom/frame.mojom-forward.h"
+#include "extensions/common/mojom/frame.mojom.h"
 #include "extensions/common/mojom/injection_type.mojom-shared.h"
 #include "extensions/common/mojom/run_location.mojom-shared.h"
 #include "extensions/renderer/script_injection.h"
 #include "url/gurl.h"
 
-namespace content {
-class RenderFrame;
-}
-
 namespace extensions {
 
 // A ScriptInjector to handle tabs.executeScript().
 class ProgrammaticScriptInjector : public ScriptInjector {
  public:
-  explicit ProgrammaticScriptInjector(mojom::ExecuteCodeParamsPtr params);
+  explicit ProgrammaticScriptInjector(
+      mojom::ExecuteCodeParamsPtr params,
+      mojom::LocalFrame::ExecuteCodeCallback callback);
   ~ProgrammaticScriptInjector() override;
 
  private:
@@ -57,21 +55,22 @@
       std::set<std::string>* injected_stylesheets,
       size_t* num_injected_stylesheets) const override;
   void OnInjectionComplete(std::unique_ptr<base::Value> execution_result,
-                           mojom::RunLocation run_location,
-                           content::RenderFrame* render_frame) override;
-  void OnWillNotInject(InjectFailureReason reason,
-                       content::RenderFrame* render_frame) override;
+                           mojom::RunLocation run_location) override;
+  void OnWillNotInject(InjectFailureReason reason) override;
 
   // Whether it is safe to include information about the URL in error messages.
   bool CanShowUrlInError() const;
 
   // Notify the browser that the script was injected (or never will be), and
   // send along any results or errors.
-  void Finish(const std::string& error, content::RenderFrame* render_frame);
+  void Finish(const std::string& error);
 
   // The parameters for injecting the script.
   mojom::ExecuteCodeParamsPtr params_;
 
+  // The callback to notify that the script has been executed.
+  mojom::LocalFrame::ExecuteCodeCallback callback_;
+
   // The url of the frame into which we are injecting.
   GURL url_;
 
@@ -83,7 +82,7 @@
   base::Optional<base::Value> result_;
 
   // Whether or not this script injection has finished.
-  bool finished_;
+  bool finished_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(ProgrammaticScriptInjector);
 };
diff --git a/extensions/renderer/script_injection.cc b/extensions/renderer/script_injection.cc
index 65f4ffe..6b5c15d 100644
--- a/extensions/renderer/script_injection.cc
+++ b/extensions/renderer/script_injection.cc
@@ -247,7 +247,7 @@
 void ScriptInjection::NotifyWillNotInject(
     ScriptInjector::InjectFailureReason reason) {
   complete_ = true;
-  injector_->OnWillNotInject(reason, render_frame_);
+  injector_->OnWillNotInject(reason);
 }
 
 ScriptInjection::InjectionResult ScriptInjection::Inject(
@@ -279,8 +279,7 @@
   if (complete_) {
     if (host_id().type == mojom::HostID::HostType::kExtensions)
       RecordContentScriptInjection(ukm_source_id_, host_id().id);
-    injector_->OnInjectionComplete(std::move(execution_result_), run_location_,
-                                   render_frame_);
+    injector_->OnInjectionComplete(std::move(execution_result_), run_location_);
   } else {
     ++scripts_run_info->num_blocking_js;
   }
@@ -377,8 +376,7 @@
   // asynchronously, and we should run it.
   if (!async_completion_callback_.is_null()) {
     complete_ = true;
-    injector_->OnInjectionComplete(std::move(execution_result_), run_location_,
-                                   render_frame_);
+    injector_->OnInjectionComplete(std::move(execution_result_), run_location_);
     // Warning: this object can be destroyed after this line!
     std::move(async_completion_callback_).Run(this);
   }
diff --git a/extensions/renderer/script_injection_manager.cc b/extensions/renderer/script_injection_manager.cc
index 7fbc3142..429d3294 100644
--- a/extensions/renderer/script_injection_manager.cc
+++ b/extensions/renderer/script_injection_manager.cc
@@ -21,7 +21,6 @@
 #include "extensions/common/extension_features.h"
 #include "extensions/common/extension_messages.h"
 #include "extensions/common/extension_set.h"
-#include "extensions/common/mojom/frame.mojom.h"
 #include "extensions/common/mojom/host_id.mojom.h"
 #include "extensions/renderer/extension_frame_helper.h"
 #include "extensions/renderer/extension_injection_host.h"
@@ -64,17 +63,6 @@
   NOTREACHED();
 }
 
-// TODO(crbug.com/1180858): Remove once ExtensionMsg_ExecuteCode is converted to
-// mojo as the mojo method will get mojom::ExecuteCodeParamsPtr.
-mojom::ExecuteCodeParamsPtr CreateExecuteCodeParamsPtr(
-    const mojom::ExecuteCodeParams& params) {
-  return mojom::ExecuteCodeParams::New(
-      params.request_id, params.host_id.Clone(), params.action_type,
-      params.code, params.webview_src, params.match_about_blank, params.run_at,
-      params.is_web_view, params.wants_result, params.script_url,
-      params.user_gesture, params.css_origin, params.injection_key);
-}
-
 }  // namespace
 
 class ScriptInjectionManager::RFOHelper : public content::RenderFrameObserver {
@@ -96,7 +84,6 @@
   void OnDestruct() override;
   void OnStop() override;
 
-  virtual void OnExecuteCode(const mojom::ExecuteCodeParams& params);
   virtual void OnPermitScriptInjection(int64_t request_id);
 
   // Tells the ScriptInjectionManager to run tasks associated with
@@ -147,7 +134,6 @@
     const IPC::Message& message) {
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(ScriptInjectionManager::RFOHelper, message)
-    IPC_MESSAGE_HANDLER(ExtensionMsg_ExecuteCode, OnExecuteCode)
     IPC_MESSAGE_HANDLER(ExtensionMsg_PermitScriptInjection,
                         OnPermitScriptInjection)
     IPC_MESSAGE_UNHANDLED(handled = false)
@@ -240,11 +226,6 @@
   DidFailProvisionalLoad();
 }
 
-void ScriptInjectionManager::RFOHelper::OnExecuteCode(
-    const mojom::ExecuteCodeParams& params) {
-  manager_->HandleExecuteCode(params, render_frame());
-}
-
 void ScriptInjectionManager::RFOHelper::OnPermitScriptInjection(
     int64_t request_id) {
   manager_->HandlePermitScriptInjection(request_id);
@@ -465,21 +446,26 @@
 }
 
 void ScriptInjectionManager::HandleExecuteCode(
-    const mojom::ExecuteCodeParams& params,
+    mojom::ExecuteCodeParamsPtr params,
+    mojom::LocalFrame::ExecuteCodeCallback callback,
     content::RenderFrame* render_frame) {
   std::unique_ptr<const InjectionHost> injection_host;
-  if (params.host_id->type == mojom::HostID::HostType::kExtensions) {
-    injection_host = ExtensionInjectionHost::Create(params.host_id->id);
-    if (!injection_host)
+  if (params->host_id->type == mojom::HostID::HostType::kExtensions) {
+    injection_host = ExtensionInjectionHost::Create(params->host_id->id);
+    if (!injection_host) {
+      std::move(callback).Run(base::EmptyString(), GURL::EmptyGURL(),
+                              base::nullopt);
       return;
-  } else if (params.host_id->type == mojom::HostID::HostType::kWebUi) {
-    injection_host.reset(new WebUIInjectionHost(*params.host_id));
+    }
+  } else if (params->host_id->type == mojom::HostID::HostType::kWebUi) {
+    injection_host = std::make_unique<WebUIInjectionHost>(*params->host_id);
   }
 
+  mojom::RunLocation run_at = params->run_at;
   auto injection = std::make_unique<ScriptInjection>(
-      std::make_unique<ProgrammaticScriptInjector>(
-          CreateExecuteCodeParamsPtr(params)),
-      render_frame, std::move(injection_host), params.run_at,
+      std::make_unique<ProgrammaticScriptInjector>(std::move(params),
+                                                   std::move(callback)),
+      render_frame, std::move(injection_host), run_at,
       activity_logging_enabled_);
 
   FrameStatusMap::const_iterator iter = frame_statuses_.find(render_frame);
diff --git a/extensions/renderer/script_injection_manager.h b/extensions/renderer/script_injection_manager.h
index c919b00..0397cc2c8 100644
--- a/extensions/renderer/script_injection_manager.h
+++ b/extensions/renderer/script_injection_manager.h
@@ -15,7 +15,7 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/scoped_observer.h"
-#include "extensions/common/mojom/frame.mojom-forward.h"
+#include "extensions/common/mojom/frame.mojom.h"
 #include "extensions/common/mojom/host_id.mojom-forward.h"
 #include "extensions/common/mojom/run_location.mojom-shared.h"
 #include "extensions/common/user_script.h"
@@ -44,6 +44,11 @@
   // Removes pending injections of the unloaded extension.
   void OnExtensionUnloaded(const std::string& extension_id);
 
+  // Handle the ExecuteCode extension message.
+  void HandleExecuteCode(mojom::ExecuteCodeParamsPtr params,
+                         mojom::LocalFrame::ExecuteCodeCallback callback,
+                         content::RenderFrame* render_frame);
+
   void ExecuteDeclarativeScript(content::RenderFrame* render_frame,
                                 int tab_id,
                                 const ExtensionId& extension_id,
@@ -90,10 +95,6 @@
                    mojom::RunLocation run_location,
                    ScriptsRunInfo* scripts_run_info);
 
-  // Handle the ExecuteCode extension message.
-  void HandleExecuteCode(const mojom::ExecuteCodeParams& params,
-                         content::RenderFrame* render_frame);
-
   // Handle the GrantInjectionPermission extension message.
   void HandlePermitScriptInjection(int64_t request_id);
 
diff --git a/extensions/renderer/script_injector.h b/extensions/renderer/script_injector.h
index 09f43c35..daf7476 100644
--- a/extensions/renderer/script_injector.h
+++ b/extensions/renderer/script_injector.h
@@ -96,18 +96,12 @@
 
   // Notifies the script that injection has completed, with a possibly-populated
   // list of results (depending on whether or not ExpectsResults() was true).
-  // |render_frame| contains the render frame, or null if the frame was
-  // invalidated.
   virtual void OnInjectionComplete(
       std::unique_ptr<base::Value> execution_result,
-      mojom::RunLocation run_location,
-      content::RenderFrame* render_frame) = 0;
+      mojom::RunLocation run_location) = 0;
 
   // Notifies the script that injection will never occur.
-  // |render_frame| contains the render frame, or null if the frame was
-  // invalidated.
-  virtual void OnWillNotInject(InjectFailureReason reason,
-                               content::RenderFrame* render_frame) = 0;
+  virtual void OnWillNotInject(InjectFailureReason reason) = 0;
 };
 
 }  // namespace extensions
diff --git a/extensions/renderer/user_script_injector.cc b/extensions/renderer/user_script_injector.cc
index b6dca1f..63fb543 100644
--- a/extensions/renderer/user_script_injector.cc
+++ b/extensions/renderer/user_script_injector.cc
@@ -280,11 +280,8 @@
 
 void UserScriptInjector::OnInjectionComplete(
     std::unique_ptr<base::Value> execution_result,
-    mojom::RunLocation run_location,
-    content::RenderFrame* render_frame) {}
+    mojom::RunLocation run_location) {}
 
-void UserScriptInjector::OnWillNotInject(InjectFailureReason reason,
-                                         content::RenderFrame* render_frame) {
-}
+void UserScriptInjector::OnWillNotInject(InjectFailureReason reason) {}
 
 }  // namespace extensions
diff --git a/extensions/renderer/user_script_injector.h b/extensions/renderer/user_script_injector.h
index a9c76c4..fc4fb8b 100644
--- a/extensions/renderer/user_script_injector.h
+++ b/extensions/renderer/user_script_injector.h
@@ -67,10 +67,8 @@
       std::set<std::string>* injected_stylesheets,
       size_t* num_injected_stylesheets) const override;
   void OnInjectionComplete(std::unique_ptr<base::Value> execution_result,
-                           mojom::RunLocation run_location,
-                           content::RenderFrame* render_frame) override;
-  void OnWillNotInject(InjectFailureReason reason,
-                       content::RenderFrame* render_frame) override;
+                           mojom::RunLocation run_location) override;
+  void OnWillNotInject(InjectFailureReason reason) override;
 
   // The associated user script. Owned by the UserScriptSet that created this
   // object.