Make ActiveTabPermissionManager also grant the tabs permission for
that tab; only respected for tabs.executeScript and tabs.insertCSS.

This involved converting tabs.executeScript/insertCSS to use JSON
schema compiler.


BUG=137643
[email protected]

Review URL: https://chromiumcodereview.appspot.com/10815028

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@149631 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/extensions/active_tab_apitest.cc b/chrome/browser/extensions/active_tab_apitest.cc
new file mode 100644
index 0000000..0470aa20
--- /dev/null
+++ b/chrome/browser/extensions/active_tab_apitest.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2012 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 "base/logging.h"
+#include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_system.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/test/base/ui_test_utils.h"
+
+namespace extensions {
+namespace {
+
+IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ActiveTab) {
+  ASSERT_TRUE(StartTestServer());
+
+  const Extension* extension =
+      LoadExtension(test_data_dir_.AppendASCII("active_tab"));
+  ASSERT_TRUE(extension);
+
+  ExtensionService* service =
+      ExtensionSystem::Get(browser()->profile())->extension_service();
+
+  // Shouldn't be initially granted based on activeTab.
+  {
+    ResultCatcher catcher;
+    ui_test_utils::NavigateToURL(browser(), test_server()->GetURL("page.html"));
+    EXPECT_TRUE(catcher.GetNextResult()) << message_;
+  }
+
+  // Granting to the extension should give it access to page.html.
+  {
+    ResultCatcher catcher;
+    service->toolbar_model()->ExecuteBrowserAction(extension, browser(), NULL);
+    EXPECT_TRUE(catcher.GetNextResult()) << message_;
+  }
+
+  // Changing page should go back to it not having access.
+  {
+    ResultCatcher catcher;
+    ui_test_utils::NavigateToURL(browser(),
+                                 test_server()->GetURL("final_page.html"));
+    EXPECT_TRUE(catcher.GetNextResult()) << message_;
+  }
+}
+
+}  // namespace
+}  // namespace extensions
diff --git a/chrome/browser/extensions/active_tab_permission_manager.cc b/chrome/browser/extensions/active_tab_permission_manager.cc
index d591e1f..67d6d58 100644
--- a/chrome/browser/extensions/active_tab_permission_manager.cc
+++ b/chrome/browser/extensions/active_tab_permission_manager.cc
@@ -49,19 +49,18 @@
     return;
   }
 
-  URLPatternSet new_permissions;
-  const URLPatternSet* old_permissions =
-      extension->GetTabSpecificHostPermissions(tab_id_);
-  if (old_permissions)
-    new_permissions.AddPatterns(*old_permissions);
+  APIPermissionSet new_apis;
+  new_apis.insert(APIPermission::kTab);
+  URLPatternSet new_hosts;
+  new_hosts.AddPattern(pattern);
 
-  new_permissions.AddPattern(pattern);
+  extension->UpdateTabSpecificPermissions(
+      tab_id_, new PermissionSet(new_apis, new_hosts, URLPatternSet()));
   granted_extensions_.Insert(extension);
-  extension->SetTabSpecificHostPermissions(tab_id_, new_permissions);
   Send(new ExtensionMsg_UpdateTabSpecificPermissions(GetPageID(),
                                                      tab_id_,
                                                      extension->id(),
-                                                     new_permissions));
+                                                     new_hosts));
 }
 
 bool ActiveTabPermissionManager::IsGranted(const Extension* extension) {
@@ -102,7 +101,7 @@
 
   for (ExtensionSet::const_iterator it = granted_extensions_.begin();
        it != granted_extensions_.end(); ++it) {
-    (*it)->ClearTabSpecificHostPermissions(tab_id_);
+    (*it)->ClearTabSpecificPermissions(tab_id_);
     extension_ids.push_back((*it)->id());
   }
 
diff --git a/chrome/browser/extensions/active_tab_unittest.cc b/chrome/browser/extensions/active_tab_unittest.cc
index 1fbb4b4..35e5e48 100644
--- a/chrome/browser/extensions/active_tab_unittest.cc
+++ b/chrome/browser/extensions/active_tab_unittest.cc
@@ -84,8 +84,9 @@
   bool IsAllowed(const scoped_refptr<const Extension>& extension,
                  const GURL& url,
                  int tab_id) {
-    return (extension->CanExecuteScriptOnPage(url, tab_id, NULL, NULL) &&
-            extension->CanCaptureVisiblePage(url, tab_id, NULL));
+    return extension->CanExecuteScriptOnPage(url, tab_id, NULL, NULL) &&
+           extension->CanCaptureVisiblePage(url, tab_id, NULL) &&
+           HasTabsPermission(extension, tab_id);
   }
 
   bool IsBlocked(const scoped_refptr<const Extension>& extension,
@@ -96,8 +97,18 @@
   bool IsBlocked(const scoped_refptr<const Extension>& extension,
                  const GURL& url,
                  int tab_id) {
-    return (!extension->CanExecuteScriptOnPage(url, tab_id, NULL, NULL) &&
-            !extension->CanCaptureVisiblePage(url, tab_id, NULL));
+    // Note: can't check HasTabsPermission because it isn't URL specific.
+    return !extension->CanExecuteScriptOnPage(url, tab_id, NULL, NULL) &&
+           !extension->CanCaptureVisiblePage(url, tab_id, NULL);
+  }
+
+  bool HasTabsPermission(const scoped_refptr<const Extension>& extension) {
+    return HasTabsPermission(extension, tab_id());
+  }
+
+  bool HasTabsPermission(const scoped_refptr<const Extension>& extension,
+                         int tab_id) {
+    return extension->HasAPIPermissionForTab(tab_id, APIPermission::kTab);
   }
 
   // An extension with the activeTab permission.
@@ -122,6 +133,10 @@
   EXPECT_TRUE(IsBlocked(another_extension, google));
   EXPECT_TRUE(IsBlocked(extension_without_active_tab, google));
 
+  EXPECT_FALSE(HasTabsPermission(extension));
+  EXPECT_FALSE(HasTabsPermission(another_extension));
+  EXPECT_FALSE(HasTabsPermission(extension_without_active_tab));
+
   active_tab_permission_manager()->GrantIfRequested(extension);
   active_tab_permission_manager()->GrantIfRequested(
       extension_without_active_tab);
@@ -135,8 +150,8 @@
   // Other subdomains shouldn't be given access.
   GURL mail_google("http://mail.google.com");
   EXPECT_TRUE(IsBlocked(extension, mail_google));
-  EXPECT_TRUE(IsBlocked(another_extension, google));
-  EXPECT_TRUE(IsBlocked(extension_without_active_tab, google));
+  EXPECT_TRUE(IsBlocked(another_extension, mail_google));
+  EXPECT_TRUE(IsBlocked(extension_without_active_tab, mail_google));
 
   // Reloading the page should clear the active permissions.
   Reload();
@@ -145,6 +160,10 @@
   EXPECT_TRUE(IsBlocked(another_extension, google));
   EXPECT_TRUE(IsBlocked(extension_without_active_tab, google));
 
+  EXPECT_FALSE(HasTabsPermission(extension));
+  EXPECT_FALSE(HasTabsPermission(another_extension));
+  EXPECT_FALSE(HasTabsPermission(extension_without_active_tab));
+
   // But they should still be able to be granted again.
   active_tab_permission_manager()->GrantIfRequested(extension);
 
@@ -179,6 +198,10 @@
   EXPECT_TRUE(IsBlocked(another_extension, chromium));
   EXPECT_TRUE(IsBlocked(extension_without_active_tab, chromium));
 
+  EXPECT_FALSE(HasTabsPermission(extension));
+  EXPECT_FALSE(HasTabsPermission(another_extension));
+  EXPECT_FALSE(HasTabsPermission(extension_without_active_tab));
+
   // Should be able to grant to multiple extensions at the same time (if they
   // have the activeTab permission, of course).
   active_tab_permission_manager()->GrantIfRequested(extension);
@@ -250,6 +273,7 @@
 
   EXPECT_TRUE(IsAllowed(extension, google, tab_id()));
   EXPECT_TRUE(IsBlocked(extension, google, tab_id() + 1));
+  EXPECT_FALSE(HasTabsPermission(extension, tab_id() + 1));
 }
 
 TEST_F(ActiveTabTest, NavigateInPage) {
diff --git a/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.cc b/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.cc
index 699eb903..1fe587ea 100644
--- a/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.cc
+++ b/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tab_contents/tab_contents.h"
+#include "chrome/common/extensions/api/tabs.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/extensions/extension_error_utils.h"
@@ -29,106 +30,64 @@
 #include "content/public/browser/web_contents.h"
 
 using content::BrowserThread;
+using extensions::api::tabs::InjectDetails;
 using extensions::ScriptExecutor;
+using extensions::UserScript;
 
 namespace keys = extensions::tabs_constants;
 
 ExecuteCodeInTabFunction::ExecuteCodeInTabFunction()
-    : execute_tab_id_(-1),
-      all_frames_(false),
-      run_at_(extensions::UserScript::DOCUMENT_IDLE) {
+    : execute_tab_id_(-1) {
 }
 
 ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {}
 
+bool ExecuteCodeInTabFunction::HasPermission() {
+  if (Init() &&
+      extension_->HasAPIPermissionForTab(execute_tab_id_,
+                                         extensions::APIPermission::kTab)) {
+    return true;
+  }
+  return ExtensionFunction::HasPermission();
+}
+
 bool ExecuteCodeInTabFunction::RunImpl() {
-  DictionaryValue* script_info;
-  EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &script_info));
-  size_t number_of_value = script_info->size();
-  if (number_of_value == 0) {
+  EXTENSION_FUNCTION_VALIDATE(Init());
+
+  if (!details_->code.get() && !details_->file.get()) {
     error_ = keys::kNoCodeOrFileToExecuteError;
     return false;
-  } else {
-    bool has_code = script_info->HasKey(keys::kCodeKey);
-    bool has_file = script_info->HasKey(keys::kFileKey);
-    if (has_code && has_file) {
-      error_ = keys::kMoreThanOneValuesError;
-      return false;
-    } else if (!has_code && !has_file) {
-      error_ = keys::kNoCodeOrFileToExecuteError;
-      return false;
-    }
+  }
+  if (details_->code.get() && details_->file.get()) {
+    error_ = keys::kMoreThanOneValuesError;
+    return false;
   }
 
-  execute_tab_id_ = -1;
-  Browser* browser = NULL;
   TabContents* contents = NULL;
 
-  // If |tab_id| is specified, look for it. Otherwise default to selected tab
-  // in the current window.
-  Value* tab_value = NULL;
-  EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &tab_value));
-  if (tab_value->IsType(Value::TYPE_NULL)) {
-    browser = GetCurrentBrowser();
-    if (!browser) {
-      error_ = keys::kNoCurrentWindowError;
-      return false;
-    }
-    if (!ExtensionTabUtil::GetDefaultTab(browser, &contents, &execute_tab_id_))
-      return false;
-  } else {
-    EXTENSION_FUNCTION_VALIDATE(tab_value->GetAsInteger(&execute_tab_id_));
-    if (!ExtensionTabUtil::GetTabById(execute_tab_id_, profile(),
-                                      include_incognito(),
-                                      &browser, NULL, &contents, NULL)) {
-      return false;
-    }
+  // If |tab_id| is specified, look for the tab. Otherwise default to selected
+  // tab in the current window.
+  CHECK_GE(execute_tab_id_, 0);
+  if (!ExtensionTabUtil::GetTabById(execute_tab_id_, profile(),
+                                    include_incognito(),
+                                    NULL, NULL, &contents, NULL)) {
+    return false;
   }
 
   // NOTE: This can give the wrong answer due to race conditions, but it is OK,
   // we check again in the renderer.
-  CHECK(browser);
   CHECK(contents);
   if (!GetExtension()->CanExecuteScriptOnPage(
           contents->web_contents()->GetURL(), execute_tab_id_, NULL, &error_)) {
     return false;
   }
 
-  if (script_info->HasKey(keys::kAllFramesKey)) {
-    if (!script_info->GetBoolean(keys::kAllFramesKey, &all_frames_))
-      return false;
-  }
+  if (details_->code.get())
+    return Execute(*details_->code);
 
-  if (script_info->HasKey(keys::kRunAtKey)) {
-    std::string run_string;
-    EXTENSION_FUNCTION_VALIDATE(script_info->GetString(
-          keys::kRunAtKey, &run_string));
+  CHECK(details_->file.get());
+  resource_ = GetExtension()->GetResource(*details_->file);
 
-    if (run_string == extension_manifest_values::kRunAtDocumentStart)
-      run_at_ = extensions::UserScript::DOCUMENT_START;
-    else if (run_string == extension_manifest_values::kRunAtDocumentEnd)
-      run_at_ = extensions::UserScript::DOCUMENT_END;
-    else if (run_string == extension_manifest_values::kRunAtDocumentIdle)
-      run_at_ = extensions::UserScript::DOCUMENT_IDLE;
-    else
-      EXTENSION_FUNCTION_VALIDATE(false);
-  }
-
-  std::string code_string;
-  if (script_info->HasKey(keys::kCodeKey)) {
-    if (!script_info->GetString(keys::kCodeKey, &code_string))
-      return false;
-  }
-
-  if (!code_string.empty())
-    return Execute(code_string);
-
-  std::string relative_path;
-  if (script_info->HasKey(keys::kFileKey)) {
-    if (!script_info->GetString(keys::kFileKey, &relative_path))
-      return false;
-    resource_ = GetExtension()->GetResource(relative_path);
-  }
   if (resource_.extension_root().empty() || resource_.relative_path().empty()) {
     error_ = keys::kNoCodeOrFileToExecuteError;
     return false;
@@ -163,6 +122,38 @@
                                                   result);
 }
 
+bool ExecuteCodeInTabFunction::Init() {
+  if (details_.get())
+    return true;
+
+  // |tab_id| is optional so it's ok if it's not there.
+  int tab_id = -1;
+  args_->GetInteger(0, &tab_id);
+
+  // |details| are not optional.
+  DictionaryValue* details_value = NULL;
+  if (!args_->GetDictionary(1, &details_value))
+    return false;
+  scoped_ptr<InjectDetails> details(new InjectDetails());
+  if (!InjectDetails::Populate(*details_value, details.get()))
+    return false;
+
+  // If the tab ID is -1 then it needs to be converted to the currently active
+  // tab's ID.
+  if (tab_id == -1) {
+    Browser* browser = GetCurrentBrowser();
+    if (!browser)
+      return false;
+    TabContents* tab_contents = NULL;
+    if (!ExtensionTabUtil::GetDefaultTab(browser, &tab_contents, &tab_id))
+      return false;
+  }
+
+  execute_tab_id_ = tab_id;
+  details_ = details.Pass();
+  return true;
+}
+
 void ExecuteCodeInTabFunction::DidLoadFile(bool success,
                                            const std::string& data) {
   std::string function_name = name();
@@ -252,12 +243,32 @@
     NOTREACHED();
   }
 
+  ScriptExecutor::FrameScope frame_scope =
+      details_->all_frames.get() && *details_->all_frames ?
+          ScriptExecutor::ALL_FRAMES :
+          ScriptExecutor::TOP_FRAME;
+
+  UserScript::RunLocation run_at = UserScript::UNDEFINED;
+  switch (details_->run_at) {
+    case InjectDetails::RUN_AT_NONE:
+    case InjectDetails::RUN_AT_DOCUMENT_IDLE:
+      run_at = UserScript::DOCUMENT_IDLE;
+      break;
+    case InjectDetails::RUN_AT_DOCUMENT_START:
+      run_at = UserScript::DOCUMENT_START;
+      break;
+    case InjectDetails::RUN_AT_DOCUMENT_END:
+      run_at = UserScript::DOCUMENT_END;
+      break;
+  }
+  CHECK_NE(UserScript::UNDEFINED, run_at);
+
   contents->extension_tab_helper()->script_executor()->ExecuteScript(
       extension->id(),
       script_type,
       code_string,
-      all_frames_ ? ScriptExecutor::ALL_FRAMES : ScriptExecutor::TOP_FRAME,
-      run_at_,
+      frame_scope,
+      run_at,
       ScriptExecutor::ISOLATED_WORLD,
       base::Bind(&ExecuteCodeInTabFunction::OnExecuteCodeFinished, this));
   return true;
diff --git a/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.h b/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.h
index 7a1f519..fa500e1 100644
--- a/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.h
+++ b/chrome/browser/extensions/api/tabs/execute_code_in_tab_function.h
@@ -11,6 +11,14 @@
 #include "chrome/common/extensions/extension_resource.h"
 #include "chrome/common/extensions/user_script.h"
 
+namespace extensions {
+namespace api {
+namespace tabs {
+struct InjectDetails;
+}  // namespace tabs
+}  // namespace api
+}  // namespace extensions
+
 // Implement API call tabs.executeScript and tabs.insertCSS.
 class ExecuteCodeInTabFunction : public AsyncExtensionFunction {
  public:
@@ -20,6 +28,7 @@
   virtual ~ExecuteCodeInTabFunction();
 
   // ExtensionFunction:
+  virtual bool HasPermission() OVERRIDE;
   virtual bool RunImpl() OVERRIDE;
 
   // Message handler.
@@ -29,6 +38,10 @@
                                      const ListValue& script_result);
 
  private:
+  // Initialize the |execute_tab_id_| and |details_| if they haven't already
+  // been. Returns whether initialization was successful.
+  bool Init();
+
   // Called when contents from the file whose path is specified in JSON
   // arguments has been loaded.
   void DidLoadFile(bool success, const std::string& data);
@@ -51,16 +64,12 @@
   // Id of tab which executes code.
   int execute_tab_id_;
 
+  // The injection details.
+  scoped_ptr<extensions::api::tabs::InjectDetails> details_;
+
   // Contains extension resource built from path of file which is
   // specified in JSON arguments.
   ExtensionResource resource_;
-
-  // If all_frames_ is true, script or CSS text would be injected
-  // to all frames; Otherwise only injected to top main frame.
-  bool all_frames_;
-
-  // The intended time to run the script.
-  extensions::UserScript::RunLocation run_at_;
 };
 
 class TabsExecuteScriptFunction : public ExecuteCodeInTabFunction {
diff --git a/chrome/browser/extensions/extension_function.cc b/chrome/browser/extensions/extension_function.cc
index 4e53d1b0..e28e353 100644
--- a/chrome/browser/extensions/extension_function.cc
+++ b/chrome/browser/extensions/extension_function.cc
@@ -83,6 +83,10 @@
   return NULL;
 }
 
+bool ExtensionFunction::HasPermission() {
+  return extension_->HasAPIPermission(name_);
+}
+
 void ExtensionFunction::OnQuotaExceeded() {
   error_ = QuotaLimitHeuristic::kGenericOverQuotaError;
   SendResponse(false);
diff --git a/chrome/browser/extensions/extension_function.h b/chrome/browser/extensions/extension_function.h
index 0698d3bcb..adef96a26 100644
--- a/chrome/browser/extensions/extension_function.h
+++ b/chrome/browser/extensions/extension_function.h
@@ -44,12 +44,16 @@
 class WindowController;
 }
 
+#ifdef NDEBUG
 #define EXTENSION_FUNCTION_VALIDATE(test) do { \
     if (!(test)) { \
       bad_message_ = true; \
       return false; \
     } \
   } while (0)
+#else   // NDEBUG
+#define EXTENSION_FUNCTION_VALIDATE(test) CHECK(test)
+#endif  // NDEBUG
 
 #define EXTENSION_FUNCTION_ERROR(error) do { \
     error_ = error; \
@@ -79,6 +83,15 @@
   virtual UIThreadExtensionFunction* AsUIThreadExtensionFunction();
   virtual IOThreadExtensionFunction* AsIOThreadExtensionFunction();
 
+  // Returns true if the function has permission to run.
+  //
+  // The default implementation is to check the Extension's permissions against
+  // what this function requires to run, but some APIs may require finer
+  // grained control, such as tabs.executeScript being allowed for active tabs.
+  //
+  // This will be run after the function has been set up but before Run().
+  virtual bool HasPermission();
+
   // Execute the API. Clients should initialize the ExtensionFunction using
   // SetArgs(), set_request_id(), and the other setters before calling this
   // method. Derived classes should be ready to return GetResultList() and
diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc
index 2e2935d..565c1248 100644
--- a/chrome/browser/extensions/extension_function_dispatcher.cc
+++ b/chrome/browser/extensions/extension_function_dispatcher.cc
@@ -136,7 +136,7 @@
                               extension_info_map->process_map(),
                               g_global_io_data.Get().api.get(),
                               profile,
-                              ipc_sender, routing_id));
+                              ipc_sender, NULL, routing_id));
   if (!function) {
     LogFailure(extension, params.name, kAccessDenied);
     return;
@@ -193,7 +193,7 @@
                               render_view_host->GetProcess()->GetID(),
                               *(service->process_map()),
                               extensions::ExtensionAPI::GetSharedInstance(),
-                              profile(), render_view_host,
+                              profile(), render_view_host, render_view_host,
                               render_view_host->GetRoutingID()));
   if (!function) {
     LogFailure(extension, params.name, kAccessDenied);
@@ -206,7 +206,6 @@
     NOTREACHED();
     return;
   }
-  function_ui->SetRenderViewHost(render_view_host);
   function_ui->set_dispatcher(AsWeakPtr());
   function_ui->set_profile(profile_);
   function->set_include_incognito(service->CanCrossIncognito(extension));
@@ -251,6 +250,7 @@
     extensions::ExtensionAPI* api,
     void* profile,
     IPC::Sender* ipc_sender,
+    RenderViewHost* render_view_host,
     int routing_id) {
   if (!extension) {
     LOG(ERROR) << "Specified extension does not exist.";
@@ -267,13 +267,6 @@
     return NULL;
   }
 
-  if (!extension->HasAPIPermission(params.name)) {
-    LOG(ERROR) << "Extension " << extension->id() << " does not have "
-               << "permission to function: " << params.name;
-    SendAccessDenied(ipc_sender, routing_id, params.request_id);
-    return NULL;
-  }
-
   ExtensionFunction* function =
       ExtensionFunctionRegistry::GetInstance()->NewFunction(params.name);
   function->SetArgs(&params.arguments);
@@ -283,6 +276,20 @@
   function->set_user_gesture(params.user_gesture);
   function->set_extension(extension);
   function->set_profile_id(profile);
+
+  UIThreadExtensionFunction* function_ui =
+      function->AsUIThreadExtensionFunction();
+  if (function_ui) {
+    function_ui->SetRenderViewHost(render_view_host);
+  }
+
+  if (!function->HasPermission()) {
+    LOG(ERROR) << "Extension " << extension->id() << " does not have "
+               << "permission to function: " << params.name;
+    SendAccessDenied(ipc_sender, routing_id, params.request_id);
+    return NULL;
+  }
+
   return function;
 }
 
diff --git a/chrome/browser/extensions/extension_function_dispatcher.h b/chrome/browser/extensions/extension_function_dispatcher.h
index bb719592..af2a060e 100644
--- a/chrome/browser/extensions/extension_function_dispatcher.h
+++ b/chrome/browser/extensions/extension_function_dispatcher.h
@@ -120,6 +120,7 @@
       extensions::ExtensionAPI* api,
       void* profile,
       IPC::Sender* ipc_sender,
+      content::RenderViewHost* render_view_host,
       int routing_id);
 
   // Helper to send an access denied error to the requesting renderer. Can be