summaryrefslogtreecommitdiffstats
path: root/extensions
diff options
context:
space:
mode:
authorrdevlin.cronin <rdevlin.cronin@chromium.org>2016-02-11 15:25:58 -0800
committerCommit bot <commit-bot@chromium.org>2016-02-11 23:26:59 +0000
commit9a62870fce8cd82a7dc43ea4786313c1b199e674 (patch)
tree5a6c4e2a87ffa349fc99bf5f9e60dc7be1488cc8 /extensions
parent308e3ee0c13d0c31ebdddaeb147d53b7d702026b (diff)
downloadchromium_src-9a62870fce8cd82a7dc43ea4786313c1b199e674.zip
chromium_src-9a62870fce8cd82a7dc43ea4786313c1b199e674.tar.gz
chromium_src-9a62870fce8cd82a7dc43ea4786313c1b199e674.tar.bz2
[Extensions] Hook the webRequest API into tab permissions with click-to-script
With click-to-script turned on, webRequest needs to respect tab-specific permissions so that when the user grants access to a tab, the extension can operate on it. In order to do this, we also need access to the tab id on the IO thread. Add the tab id to the ExtensionApiFrameIdMap, and check for it in the webRequest api. For now, don't do anything if the frame isn't cached - that'll come later. This also doesn't hook webRequest into requesting permissions - that's next. BUG=460306 Review URL: https://codereview.chromium.org/1687913002 Cr-Commit-Position: refs/heads/master@{#375036}
Diffstat (limited to 'extensions')
-rw-r--r--extensions/browser/api/declarative_webrequest/webrequest_action.cc3
-rw-r--r--extensions/browser/api/web_request/web_request_api.cc21
-rw-r--r--extensions/browser/api/web_request/web_request_event_details.cc10
-rw-r--r--extensions/browser/api/web_request/web_request_event_details.h4
-rw-r--r--extensions/browser/api/web_request/web_request_permissions.cc8
-rw-r--r--extensions/browser/api/web_request/web_request_permissions.h1
-rw-r--r--extensions/browser/extension_api_frame_id_map.cc170
-rw-r--r--extensions/browser/extension_api_frame_id_map.h88
-rw-r--r--extensions/browser/extension_api_frame_id_map_unittest.cc111
-rw-r--r--extensions/browser/extension_web_contents_observer.cc4
-rw-r--r--extensions/browser/extensions_browser_client.cc5
-rw-r--r--extensions/browser/extensions_browser_client.h3
12 files changed, 272 insertions, 156 deletions
diff --git a/extensions/browser/api/declarative_webrequest/webrequest_action.cc b/extensions/browser/api/declarative_webrequest/webrequest_action.cc
index e3cf9a8..c5dad854 100644
--- a/extensions/browser/api/declarative_webrequest/webrequest_action.cc
+++ b/extensions/browser/api/declarative_webrequest/webrequest_action.cc
@@ -507,8 +507,9 @@ bool WebRequestAction::HasPermission(const InfoMap* extension_info_map,
permission_check = WebRequestPermissions::REQUIRE_HOST_PERMISSION;
break;
}
+ // TODO(devlin): Pass in the real tab id here.
return WebRequestPermissions::CanExtensionAccessURL(
- extension_info_map, extension_id, request->url(), crosses_incognito,
+ extension_info_map, extension_id, request->url(), -1, crosses_incognito,
permission_check);
}
diff --git a/extensions/browser/api/web_request/web_request_api.cc b/extensions/browser/api/web_request/web_request_api.cc
index 532e43d..ddec87c5 100644
--- a/extensions/browser/api/web_request/web_request_api.cc
+++ b/extensions/browser/api/web_request/web_request_api.cc
@@ -1340,9 +1340,22 @@ void ExtensionWebRequestEventRouter::GetMatchingListenersImpl(
continue;
}
+ int tab_id = -1;
+ int render_process_id = -1;
+ int render_frame_id = -1;
+ ExtensionApiFrameIdMap::FrameData frame_data;
+ // TODO(devlin): Figure out when one/both of these can fail, and if we
+ // need to address it.
+ if (content::ResourceRequestInfo::GetRenderFrameForRequest(
+ request, &render_process_id, &render_frame_id) &&
+ ExtensionApiFrameIdMap::Get()->GetCachedFrameDataOnIO(
+ render_process_id, render_frame_id, &frame_data)) {
+ tab_id = frame_data.tab_id;
+ }
if (!is_web_view_guest &&
!WebRequestPermissions::CanExtensionAccessURL(
- extension_info_map, listener.extension_id, url, crosses_incognito,
+ extension_info_map, listener.extension_id, url, tab_id,
+ crosses_incognito,
WebRequestPermissions::REQUIRE_HOST_PERMISSION)) {
continue;
}
@@ -2051,9 +2064,13 @@ bool WebRequestInternalAddEventListenerFunction::RunSync() {
// while having host permissions for http://www.example.com/foo/* and
// http://www.example.com/bar/*.
// For this reason we do only a coarse check here to warn the extension
- // developer if he does something obviously wrong.
+ // developer if they do something obviously wrong.
if (extension->permissions_data()
->GetEffectiveHostPermissions()
+ .is_empty() &&
+ extension->permissions_data()
+ ->withheld_permissions()
+ .explicit_hosts()
.is_empty()) {
error_ = keys::kHostPermissionsRequired;
return false;
diff --git a/extensions/browser/api/web_request/web_request_event_details.cc b/extensions/browser/api/web_request/web_request_event_details.cc
index 2382dcd..04fa9fb 100644
--- a/extensions/browser/api/web_request/web_request_event_details.cc
+++ b/extensions/browser/api/web_request/web_request_event_details.cc
@@ -13,7 +13,6 @@
#include "extensions/browser/api/web_request/upload_data_presenter.h"
#include "extensions/browser/api/web_request/web_request_api_constants.h"
#include "extensions/browser/api/web_request/web_request_api_helpers.h"
-#include "extensions/browser/extension_api_frame_id_map.h"
#include "ipc/ipc_message.h"
#include "net/base/auth.h"
#include "net/base/upload_data_stream.h"
@@ -162,7 +161,7 @@ void WebRequestEventDetails::DetermineFrameIdOnUI() {
void WebRequestEventDetails::DetermineFrameIdOnIO(
const DeterminedFrameIdCallback& callback) {
scoped_ptr<WebRequestEventDetails> self(this);
- ExtensionApiFrameIdMap::Get()->GetFrameIdOnIO(
+ ExtensionApiFrameIdMap::Get()->GetFrameDataOnIO(
render_process_id_, render_frame_id_,
base::Bind(&WebRequestEventDetails::OnDeterminedFrameId,
base::Unretained(this), base::Passed(&self), callback));
@@ -189,10 +188,9 @@ scoped_ptr<base::DictionaryValue> WebRequestEventDetails::GetAndClearDict() {
void WebRequestEventDetails::OnDeterminedFrameId(
scoped_ptr<WebRequestEventDetails> self,
const DeterminedFrameIdCallback& callback,
- int extension_api_frame_id,
- int extension_api_parent_frame_id) {
- dict_.SetInteger(keys::kFrameIdKey, extension_api_frame_id);
- dict_.SetInteger(keys::kParentFrameIdKey, extension_api_parent_frame_id);
+ const ExtensionApiFrameIdMap::FrameData& frame_data) {
+ dict_.SetInteger(keys::kFrameIdKey, frame_data.frame_id);
+ dict_.SetInteger(keys::kParentFrameIdKey, frame_data.parent_frame_id);
callback.Run(std::move(self));
}
diff --git a/extensions/browser/api/web_request/web_request_event_details.h b/extensions/browser/api/web_request/web_request_event_details.h
index 91e29bf..7bb3644 100644
--- a/extensions/browser/api/web_request/web_request_event_details.h
+++ b/extensions/browser/api/web_request/web_request_event_details.h
@@ -11,6 +11,7 @@
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "base/values.h"
+#include "extensions/browser/extension_api_frame_id_map.h"
namespace net {
class AuthChallengeInfo;
@@ -119,8 +120,7 @@ class WebRequestEventDetails {
private:
void OnDeterminedFrameId(scoped_ptr<WebRequestEventDetails> self,
const DeterminedFrameIdCallback& callback,
- int extension_api_frame_id,
- int extension_api_parent_frame_id);
+ const ExtensionApiFrameIdMap::FrameData& frame_data);
// The details that are always included in a webRequest event object.
base::DictionaryValue dict_;
diff --git a/extensions/browser/api/web_request/web_request_permissions.cc b/extensions/browser/api/web_request/web_request_permissions.cc
index ec0aa17..78ab323 100644
--- a/extensions/browser/api/web_request/web_request_permissions.cc
+++ b/extensions/browser/api/web_request/web_request_permissions.cc
@@ -108,6 +108,7 @@ bool WebRequestPermissions::CanExtensionAccessURL(
const extensions::InfoMap* extension_info_map,
const std::string& extension_id,
const GURL& url,
+ int tab_id,
bool crosses_incognito,
HostPermissionsCheck host_permissions_check) {
// extension_info_map can be NULL in testing.
@@ -130,9 +131,12 @@ bool WebRequestPermissions::CanExtensionAccessURL(
// about: URLs are not covered in host permissions, but are allowed
// anyway.
if (!url.SchemeIs(url::kAboutScheme) &&
- !extension->permissions_data()->HasHostPermission(url) &&
!url::IsSameOriginWith(url, extension->url())) {
- return false;
+ extensions::PermissionsData::AccessType access =
+ extension->permissions_data()->GetPageAccess(extension, url, tab_id,
+ nullptr);
+ if (access != extensions::PermissionsData::ACCESS_ALLOWED)
+ return false;
}
break;
case REQUIRE_ALL_URLS:
diff --git a/extensions/browser/api/web_request/web_request_permissions.h b/extensions/browser/api/web_request/web_request_permissions.h
index a710cee..2153183 100644
--- a/extensions/browser/api/web_request/web_request_permissions.h
+++ b/extensions/browser/api/web_request/web_request_permissions.h
@@ -40,6 +40,7 @@ class WebRequestPermissions {
const extensions::InfoMap* extension_info_map,
const std::string& extension_id,
const GURL& url,
+ int tab_id,
bool crosses_incognito,
HostPermissionsCheck host_permissions_check);
diff --git a/extensions/browser/extension_api_frame_id_map.cc b/extensions/browser/extension_api_frame_id_map.cc
index c2c8a34..b7ae5ea 100644
--- a/extensions/browser/extension_api_frame_id_map.cc
+++ b/extensions/browser/extension_api_frame_id_map.cc
@@ -12,6 +12,7 @@
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/child_process_host.h"
+#include "extensions/browser/extensions_browser_client.h"
namespace extensions {
@@ -22,18 +23,32 @@ namespace {
base::LazyInstance<ExtensionApiFrameIdMap>::Leaky g_map_instance =
LAZY_INSTANCE_INITIALIZER;
+int GetTabId(content::RenderFrameHost* rfh) {
+ if (!rfh)
+ return -1;
+ return ExtensionsBrowserClient::Get()->GetTabIdForWebContents(
+ content::WebContents::FromRenderFrameHost(rfh));
+}
+
+bool IsFrameRoutingIdValid(int frame_routing_id) {
+ // frame_routing_id == -2 = MSG_ROUTING_NONE -> not a RenderFrameHost.
+ // frame_routing_id == -1 -> should be MSG_ROUTING_NONE, but there are
+ // callers that use "-1" for unknown frames.
+ return frame_routing_id > -1;
+}
+
} // namespace
const int ExtensionApiFrameIdMap::kInvalidFrameId = -1;
const int ExtensionApiFrameIdMap::kTopFrameId = 0;
-ExtensionApiFrameIdMap::CachedFrameIdPair::CachedFrameIdPair()
- : frame_id(kInvalidFrameId), parent_frame_id(kInvalidFrameId) {}
+ExtensionApiFrameIdMap::FrameData::FrameData()
+ : frame_id(kInvalidFrameId), parent_frame_id(kInvalidFrameId), tab_id(-1) {}
-ExtensionApiFrameIdMap::CachedFrameIdPair::CachedFrameIdPair(
- int frame_id,
- int parent_frame_id)
- : frame_id(frame_id), parent_frame_id(parent_frame_id) {}
+ExtensionApiFrameIdMap::FrameData::FrameData(int frame_id,
+ int parent_frame_id,
+ int tab_id)
+ : frame_id(frame_id), parent_frame_id(parent_frame_id), tab_id(tab_id) {}
ExtensionApiFrameIdMap::RenderFrameIdKey::RenderFrameIdKey()
: render_process_id(content::ChildProcessHost::kInvalidUniqueID),
@@ -45,10 +60,10 @@ ExtensionApiFrameIdMap::RenderFrameIdKey::RenderFrameIdKey(
: render_process_id(render_process_id),
frame_routing_id(frame_routing_id) {}
-ExtensionApiFrameIdMap::FrameIdCallbacks::FrameIdCallbacks()
+ExtensionApiFrameIdMap::FrameDataCallbacks::FrameDataCallbacks()
: is_iterating(false) {}
-ExtensionApiFrameIdMap::FrameIdCallbacks::~FrameIdCallbacks() {}
+ExtensionApiFrameIdMap::FrameDataCallbacks::~FrameDataCallbacks() {}
bool ExtensionApiFrameIdMap::RenderFrameIdKey::operator<(
const RenderFrameIdKey& other) const {
@@ -125,103 +140,96 @@ content::RenderFrameHost* ExtensionApiFrameIdMap::GetRenderFrameHostById(
return web_contents->FindFrameByFrameTreeNodeId(frame_id);
}
-ExtensionApiFrameIdMap::CachedFrameIdPair ExtensionApiFrameIdMap::KeyToValue(
+ExtensionApiFrameIdMap::FrameData ExtensionApiFrameIdMap::KeyToValue(
const RenderFrameIdKey& key) const {
content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
key.render_process_id, key.frame_routing_id);
- return CachedFrameIdPair(GetFrameId(rfh), GetParentFrameId(rfh));
+ return FrameData(GetFrameId(rfh), GetParentFrameId(rfh), GetTabId(rfh));
}
-ExtensionApiFrameIdMap::CachedFrameIdPair
-ExtensionApiFrameIdMap::LookupFrameIdOnUI(const RenderFrameIdKey& key) {
+ExtensionApiFrameIdMap::FrameData ExtensionApiFrameIdMap::LookupFrameDataOnUI(
+ const RenderFrameIdKey& key) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- FrameIdMap::const_iterator frame_id_iter = frame_id_map_.find(key);
- if (frame_id_iter != frame_id_map_.end())
+ FrameDataMap::const_iterator frame_id_iter = frame_data_map_.find(key);
+ if (frame_id_iter != frame_data_map_.end())
return frame_id_iter->second;
- CachedFrameIdPair cached_frame_id_pair = KeyToValue(key);
+ FrameData cached_frame_data = KeyToValue(key);
// Don't save invalid values in the map.
- if (cached_frame_id_pair.frame_id == kInvalidFrameId)
- return cached_frame_id_pair;
+ if (cached_frame_data.frame_id == kInvalidFrameId)
+ return cached_frame_data;
- auto kvpair = FrameIdMap::value_type(key, cached_frame_id_pair);
- base::AutoLock lock(frame_id_map_lock_);
- return frame_id_map_.insert(kvpair).first->second;
+ auto kvpair = FrameDataMap::value_type(key, cached_frame_data);
+ base::AutoLock lock(frame_data_map_lock_);
+ return frame_data_map_.insert(kvpair).first->second;
}
-void ExtensionApiFrameIdMap::ReceivedFrameIdOnIO(
+void ExtensionApiFrameIdMap::ReceivedFrameDataOnIO(
const RenderFrameIdKey& key,
- const CachedFrameIdPair& cached_frame_id_pair) {
+ const FrameData& cached_frame_data) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
- FrameIdCallbacksMap::iterator map_iter = callbacks_map_.find(key);
+ FrameDataCallbacksMap::iterator map_iter = callbacks_map_.find(key);
if (map_iter == callbacks_map_.end()) {
// Can happen if ReceivedFrameIdOnIO was called after the frame ID was
- // resolved (e.g. via GetFrameIdOnIO), but before PostTaskAndReply replied.
+ // resolved (e.g. via GetFrameDataOnIO), but before PostTaskAndReply
+ // replied.
return;
}
- FrameIdCallbacks& callbacks = map_iter->second;
+ FrameDataCallbacks& callbacks = map_iter->second;
if (callbacks.is_iterating)
return;
callbacks.is_iterating = true;
// Note: Extra items can be appended to |callbacks| during this loop if a
- // callback calls GetFrameIdOnIO().
- for (std::list<FrameIdCallback>::iterator it = callbacks.callbacks.begin();
+ // callback calls GetFrameDataOnIO().
+ for (std::list<FrameDataCallback>::iterator it = callbacks.callbacks.begin();
it != callbacks.callbacks.end(); ++it) {
- it->Run(cached_frame_id_pair.frame_id,
- cached_frame_id_pair.parent_frame_id);
+ it->Run(cached_frame_data);
}
callbacks_map_.erase(key);
}
-void ExtensionApiFrameIdMap::GetFrameIdOnIO(int render_process_id,
- int frame_routing_id,
- const FrameIdCallback& callback) {
+void ExtensionApiFrameIdMap::GetFrameDataOnIO(
+ int render_process_id,
+ int frame_routing_id,
+ const FrameDataCallback& callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+ // TODO(robwu): Enable assertion when all callers have been fixed.
+ // DCHECK_EQ(MSG_ROUTING_NONE, -1);
+ if (!IsFrameRoutingIdValid(frame_routing_id)) {
+ callback.Run(FrameData());
+ return;
+ }
+
if (frame_routing_id <= -1) {
// frame_routing_id == -2 = MSG_ROUTING_NONE -> not a RenderFrameHost.
// frame_routing_id == -1 -> should be MSG_ROUTING_NONE, but there are
// callers that use "-1" for unknown frames.
- // TODO(robwu): Enable assertion when all callers have been fixed.
- // DCHECK_EQ(MSG_ROUTING_NONE, -1);
- callback.Run(kInvalidFrameId, kInvalidFrameId);
+ callback.Run(FrameData());
return;
}
- // A valid routing ID is only meaningful with a valid process ID.
- DCHECK_GE(render_process_id, 0);
- const RenderFrameIdKey key(render_process_id, frame_routing_id);
- CachedFrameIdPair cached_frame_id_pair;
- bool did_find_cached_frame_id_pair = false;
-
- {
- base::AutoLock lock(frame_id_map_lock_);
- FrameIdMap::const_iterator frame_id_iter = frame_id_map_.find(key);
- if (frame_id_iter != frame_id_map_.end()) {
- // This is very likely to happen because CacheFrameId() is called as soon
- // as the frame is created.
- cached_frame_id_pair = frame_id_iter->second;
- did_find_cached_frame_id_pair = true;
- }
- }
+ FrameData cached_frame_data;
+ bool did_find_cached_frame_data = GetCachedFrameDataOnIO(
+ render_process_id, frame_routing_id, &cached_frame_data);
- FrameIdCallbacksMap::iterator map_iter = callbacks_map_.find(key);
+ const RenderFrameIdKey key(render_process_id, frame_routing_id);
+ FrameDataCallbacksMap::iterator map_iter = callbacks_map_.find(key);
- if (did_find_cached_frame_id_pair) {
+ if (did_find_cached_frame_data) {
// Value already cached, thread hopping is not needed.
if (map_iter == callbacks_map_.end()) {
// If the frame ID was cached, then it is likely that there are no pending
// callbacks. So do not unnecessarily copy the callback, but run it.
- callback.Run(cached_frame_id_pair.frame_id,
- cached_frame_id_pair.parent_frame_id);
+ callback.Run(cached_frame_data);
} else {
map_iter->second.callbacks.push_back(callback);
- ReceivedFrameIdOnIO(key, cached_frame_id_pair);
+ ReceivedFrameDataOnIO(key, cached_frame_data);
}
return;
}
@@ -231,37 +239,63 @@ void ExtensionApiFrameIdMap::GetFrameIdOnIO(int render_process_id,
callbacks_map_[key].callbacks.push_back(callback);
content::BrowserThread::PostTaskAndReplyWithResult(
content::BrowserThread::UI, FROM_HERE,
- base::Bind(&ExtensionApiFrameIdMap::LookupFrameIdOnUI,
+ base::Bind(&ExtensionApiFrameIdMap::LookupFrameDataOnUI,
base::Unretained(this), key),
- base::Bind(&ExtensionApiFrameIdMap::ReceivedFrameIdOnIO,
+ base::Bind(&ExtensionApiFrameIdMap::ReceivedFrameDataOnIO,
base::Unretained(this), key));
}
-void ExtensionApiFrameIdMap::CacheFrameId(content::RenderFrameHost* rfh) {
+bool ExtensionApiFrameIdMap::GetCachedFrameDataOnIO(int render_process_id,
+ int frame_routing_id,
+ FrameData* frame_data_out) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+ // TODO(robwu): Enable assertion when all callers have been fixed.
+ // DCHECK_EQ(MSG_ROUTING_NONE, -1);
+ if (!IsFrameRoutingIdValid(frame_routing_id))
+ return false;
+
+ // A valid routing ID is only meaningful with a valid process ID.
+ DCHECK_GE(render_process_id, 0);
+
+ base::AutoLock lock(frame_data_map_lock_);
+ FrameDataMap::const_iterator frame_id_iter = frame_data_map_.find(
+ RenderFrameIdKey(render_process_id, frame_routing_id));
+ if (frame_id_iter != frame_data_map_.end()) {
+ // This is very likely to happen because CacheFrameId() is called as soon
+ // as the frame is created.
+ *frame_data_out = frame_id_iter->second;
+ return true;
+ }
+
+ return false;
+}
+
+void ExtensionApiFrameIdMap::CacheFrameData(content::RenderFrameHost* rfh) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
const RenderFrameIdKey key(rfh->GetProcess()->GetID(), rfh->GetRoutingID());
- CacheFrameId(key);
- DCHECK(frame_id_map_.find(key) != frame_id_map_.end());
+ CacheFrameData(key);
+ DCHECK(frame_data_map_.find(key) != frame_data_map_.end());
}
-void ExtensionApiFrameIdMap::CacheFrameId(const RenderFrameIdKey& key) {
- LookupFrameIdOnUI(key);
+void ExtensionApiFrameIdMap::CacheFrameData(const RenderFrameIdKey& key) {
+ LookupFrameDataOnUI(key);
}
-void ExtensionApiFrameIdMap::RemoveFrameId(content::RenderFrameHost* rfh) {
+void ExtensionApiFrameIdMap::RemoveFrameData(content::RenderFrameHost* rfh) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(rfh);
const RenderFrameIdKey key(rfh->GetProcess()->GetID(), rfh->GetRoutingID());
- RemoveFrameId(key);
+ RemoveFrameData(key);
}
-void ExtensionApiFrameIdMap::RemoveFrameId(const RenderFrameIdKey& key) {
+void ExtensionApiFrameIdMap::RemoveFrameData(const RenderFrameIdKey& key) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- base::AutoLock lock(frame_id_map_lock_);
- frame_id_map_.erase(key);
+ base::AutoLock lock(frame_data_map_lock_);
+ frame_data_map_.erase(key);
}
} // namespace extensions
diff --git a/extensions/browser/extension_api_frame_id_map.h b/extensions/browser/extension_api_frame_id_map.h
index d1fee76..58b1320 100644
--- a/extensions/browser/extension_api_frame_id_map.h
+++ b/extensions/browser/extension_api_frame_id_map.h
@@ -30,9 +30,12 @@ namespace extensions {
// - The ID does not change during the frame's lifetime and is not re-used after
// the frame is removed. The frame may change its current RenderFrameHost over
// time, so multiple RenderFrameHosts may map to the same extension frame ID.
-
+//
// This class provides a mapping from a (render_process_id, frame_routing_id)
-// pair that maps a RenderFrameHost to an extension frame ID.
+// pair to a FrameData struct, which includes the extension's frame id (as
+// described above), the parent frame id, and the tab id (the latter can be
+// invalid if it's not in a tab).
+//
// Unless stated otherwise, the methods can only be called on the UI thread.
//
// The non-static methods of this class use an internal cache. This cache is
@@ -40,11 +43,26 @@ namespace extensions {
// attaching FrameTreeNode IDs to requests is negligible (crbug.com/524228),
// then we can remove all key caching and remove the cache from this class.
// TODO(robwu): Keep an eye on crbug.com/524228 and act upon the outcome.
+// TODO(devlin): Also keep an eye on FrameIOData to see if that could help.
class ExtensionApiFrameIdMap {
public:
- using FrameIdCallback =
- base::Callback<void(int extension_api_frame_id,
- int extension_api_parent_frame_id)>;
+ // The data for a RenderFrame. Every RenderFrameIdKey maps to a FrameData.
+ struct FrameData {
+ FrameData();
+ FrameData(int frame_id, int parent_frame_id, int tab_id);
+
+ // The extension API frame ID of the frame.
+ int frame_id;
+
+ // The extension API frame ID of the parent of the frame.
+ int parent_frame_id;
+
+ // The id of the tab that the frame is in, or -1 if the frame isn't in a
+ // tab.
+ int tab_id;
+ };
+
+ using FrameDataCallback = base::Callback<void(const FrameData&)>;
// An invalid extension API frame ID.
static const int kInvalidFrameId;
@@ -77,20 +95,27 @@ class ExtensionApiFrameIdMap {
// on the UI thread. Thread hopping is minimized if possible. Callbacks for
// the same |render_process_id| and |frame_routing_id| are guaranteed to be
// run in order. The order of other callbacks is undefined.
- void GetFrameIdOnIO(int render_process_id,
- int frame_routing_id,
- const FrameIdCallback& callback);
+ void GetFrameDataOnIO(int render_process_id,
+ int frame_routing_id,
+ const FrameDataCallback& callback);
+
+ // Attempts to populate |frame_data_out| with the FrameData for the specified
+ // frame, but only does so if the data is already cached. Returns true if
+ // cached frame data was found.
+ bool GetCachedFrameDataOnIO(int render_process_id,
+ int frame_routing_id,
+ FrameData* frame_data_out);
// Looks up the frame ID and stores it in the map. This method should be
// called as early as possible, e.g. in a
// WebContentsObserver::RenderFrameCreated notification.
- void CacheFrameId(content::RenderFrameHost* rfh);
+ void CacheFrameData(content::RenderFrameHost* rfh);
// Removes the frame ID mapping for a given frame. This method can be called
// at any time, but it is typically called when a frame is destroyed.
// If this method is not called, the cached mapping for the frame is retained
// forever.
- void RemoveFrameId(content::RenderFrameHost* rfh);
+ void RemoveFrameData(content::RenderFrameHost* rfh);
protected:
friend struct base::DefaultLazyInstanceTraits<ExtensionApiFrameIdMap>;
@@ -110,33 +135,20 @@ class ExtensionApiFrameIdMap {
bool operator==(const RenderFrameIdKey& other) const;
};
- // The cached pair of frame IDs of the frame. Every RenderFrameIdKey
- // maps to a CachedFrameIdPair.
- struct CachedFrameIdPair {
- CachedFrameIdPair();
- CachedFrameIdPair(int frame_id, int parent_frame_id);
-
- // The extension API frame ID of the frame.
- int frame_id;
-
- // The extension API frame ID of the parent of the frame.
- int parent_frame_id;
- };
-
- struct FrameIdCallbacks {
- FrameIdCallbacks();
- ~FrameIdCallbacks();
+ struct FrameDataCallbacks {
+ FrameDataCallbacks();
+ ~FrameDataCallbacks();
// This is a std::list so that iterators are not invalidated when the list
// is modified during an iteration.
- std::list<FrameIdCallback> callbacks;
+ std::list<FrameDataCallback> callbacks;
// To avoid re-entrant processing of callbacks.
bool is_iterating;
};
- using FrameIdMap = std::map<RenderFrameIdKey, CachedFrameIdPair>;
- using FrameIdCallbacksMap = std::map<RenderFrameIdKey, FrameIdCallbacks>;
+ using FrameDataMap = std::map<RenderFrameIdKey, FrameData>;
+ using FrameDataCallbacksMap = std::map<RenderFrameIdKey, FrameDataCallbacks>;
ExtensionApiFrameIdMap();
~ExtensionApiFrameIdMap();
@@ -144,31 +156,31 @@ class ExtensionApiFrameIdMap {
// Determines the value to be stored in |frame_id_map_| for a given key. This
// method is only called when |key| is not in |frame_id_map_|.
// virtual for testing.
- virtual CachedFrameIdPair KeyToValue(const RenderFrameIdKey& key) const;
+ virtual FrameData KeyToValue(const RenderFrameIdKey& key) const;
- CachedFrameIdPair LookupFrameIdOnUI(const RenderFrameIdKey& key);
+ FrameData LookupFrameDataOnUI(const RenderFrameIdKey& key);
// Called as soon as the frame ID is found for the given |key|, and runs all
// queued callbacks with |cached_frame_id_pair|.
- void ReceivedFrameIdOnIO(const RenderFrameIdKey& key,
- const CachedFrameIdPair& cached_frame_id_pair);
+ void ReceivedFrameDataOnIO(const RenderFrameIdKey& key,
+ const FrameData& cached_frame_id_pair);
// Implementation of CacheFrameId(RenderFrameHost), separated for testing.
- void CacheFrameId(const RenderFrameIdKey& key);
+ void CacheFrameData(const RenderFrameIdKey& key);
// Implementation of RemoveFrameId(RenderFrameHost), separated for testing.
- void RemoveFrameId(const RenderFrameIdKey& key);
+ void RemoveFrameData(const RenderFrameIdKey& key);
// Queued callbacks for use on the IO thread.
- FrameIdCallbacksMap callbacks_map_;
+ FrameDataCallbacksMap callbacks_map_;
// This map is only modified on the UI thread and is used to minimize the
// number of thread hops on the IO thread.
- FrameIdMap frame_id_map_;
+ FrameDataMap frame_data_map_;
// This lock protects |frame_id_map_| from being concurrently written on the
// UI thread and read on the IO thread.
- base::Lock frame_id_map_lock_;
+ base::Lock frame_data_map_lock_;
DISALLOW_COPY_AND_ASSIGN(ExtensionApiFrameIdMap);
};
diff --git a/extensions/browser/extension_api_frame_id_map_unittest.cc b/extensions/browser/extension_api_frame_id_map_unittest.cc
index 119c54d6..fcf7ab2 100644
--- a/extensions/browser/extension_api_frame_id_map_unittest.cc
+++ b/extensions/browser/extension_api_frame_id_map_unittest.cc
@@ -9,7 +9,7 @@
#include "ipc/ipc_message.h"
#include "testing/gtest/include/gtest/gtest.h"
-using FrameIdCallback = extensions::ExtensionApiFrameIdMap::FrameIdCallback;
+using FrameDataCallback = extensions::ExtensionApiFrameIdMap::FrameDataCallback;
namespace extensions {
@@ -31,9 +31,17 @@ int ToTestParentFrameId(int render_process_id, int frame_routing_id) {
return render_process_id * 1000 + frame_routing_id * 10 + 7;
}
+int ToTestTabId(int render_process_id, int frame_routing_id) {
+ if (render_process_id < 0 && frame_routing_id < 0)
+ return -1;
+ // Return a deterministic value (yet different from the input) for testing.
+ // To make debugging easier: Ending with 5 = tab ID.
+ return render_process_id * 1000 + frame_routing_id * 10 + 5;
+}
+
class TestExtensionApiFrameIdMap : public ExtensionApiFrameIdMap {
public:
- int GetInternalSize() { return frame_id_map_.size(); }
+ int GetInternalSize() { return frame_data_map_.size(); }
int GetInternalCallbackCount() {
int count = 0;
for (auto& it : callbacks_map_)
@@ -45,21 +53,22 @@ class TestExtensionApiFrameIdMap : public ExtensionApiFrameIdMap {
// fixed IDs in unit tests.
// TODO(robwu): Use content/public/test/test_renderer_host.h to mock
// RenderFrameHosts and update the tests to test against these mocks.
- // After doing that, there is no need for CacheFrameId/RemoveFrameId methods
- // that take a RenderFrameIdKey, so the methods can be merged.
- void SetInternalFrameId(int render_process_id, int frame_routing_id) {
- CacheFrameId(RenderFrameIdKey(render_process_id, frame_routing_id));
+ // After doing that, there is no need for CacheFrameData/RemoveFrameData
+ // methods that take a RenderFrameIdKey, so the methods can be merged.
+ void SetInternalFrameData(int render_process_id, int frame_routing_id) {
+ CacheFrameData(RenderFrameIdKey(render_process_id, frame_routing_id));
}
- void RemoveInternalFrameId(int render_process_id, int frame_routing_id) {
- RemoveFrameId(RenderFrameIdKey(render_process_id, frame_routing_id));
+ void RemoveInternalFrameData(int render_process_id, int frame_routing_id) {
+ RemoveFrameData(RenderFrameIdKey(render_process_id, frame_routing_id));
}
private:
// ExtensionApiFrameIdMap:
- CachedFrameIdPair KeyToValue(const RenderFrameIdKey& key) const override {
- return CachedFrameIdPair(
+ FrameData KeyToValue(const RenderFrameIdKey& key) const override {
+ return FrameData(
ToTestFrameId(key.render_process_id, key.frame_routing_id),
- ToTestParentFrameId(key.render_process_id, key.frame_routing_id));
+ ToTestParentFrameId(key.render_process_id, key.frame_routing_id),
+ ToTestTabId(key.render_process_id, key.frame_routing_id));
}
};
@@ -68,9 +77,10 @@ class ExtensionApiFrameIdMapTest : public testing::Test {
ExtensionApiFrameIdMapTest()
: thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
- FrameIdCallback CreateCallback(int render_process_id,
- int frame_routing_id,
- const std::string& callback_name_for_testing) {
+ FrameDataCallback CreateCallback(
+ int render_process_id,
+ int frame_routing_id,
+ const std::string& callback_name_for_testing) {
return base::Bind(&ExtensionApiFrameIdMapTest::OnCalledCallback,
base::Unretained(this), render_process_id,
frame_routing_id, callback_name_for_testing);
@@ -79,15 +89,16 @@ class ExtensionApiFrameIdMapTest : public testing::Test {
void OnCalledCallback(int render_process_id,
int frame_routing_id,
const std::string& callback_name_for_testing,
- int extension_api_frame_id,
- int extension_api_parent_frame_id) {
+ const ExtensionApiFrameIdMap::FrameData& frame_data) {
results_.push_back(callback_name_for_testing);
// If this fails, then the mapping is completely wrong.
EXPECT_EQ(ToTestFrameId(render_process_id, frame_routing_id),
- extension_api_frame_id);
+ frame_data.frame_id);
EXPECT_EQ(ToTestParentFrameId(render_process_id, frame_routing_id),
- extension_api_parent_frame_id);
+ frame_data.parent_frame_id);
+ EXPECT_EQ(ToTestTabId(render_process_id, frame_routing_id),
+ frame_data.tab_id);
}
const std::vector<std::string>& results() { return results_; }
@@ -103,27 +114,27 @@ class ExtensionApiFrameIdMapTest : public testing::Test {
} // namespace
-TEST_F(ExtensionApiFrameIdMapTest, GetFrameIdOnIO) {
+TEST_F(ExtensionApiFrameIdMapTest, GetFrameDataOnIO) {
TestExtensionApiFrameIdMap map;
EXPECT_EQ(0, map.GetInternalSize());
// Two identical calls, should be processed at the next message loop.
- map.GetFrameIdOnIO(1, 2, CreateCallback(1, 2, "first"));
+ map.GetFrameDataOnIO(1, 2, CreateCallback(1, 2, "first"));
EXPECT_EQ(1, map.GetInternalCallbackCount());
EXPECT_EQ(0, map.GetInternalSize());
- map.GetFrameIdOnIO(1, 2, CreateCallback(1, 2, "first again"));
+ map.GetFrameDataOnIO(1, 2, CreateCallback(1, 2, "first again"));
EXPECT_EQ(2, map.GetInternalCallbackCount());
EXPECT_EQ(0, map.GetInternalSize());
// First get the frame ID on IO (queued on message loop), then set it on UI.
// No callbacks should be invoked because the IO thread cannot know that the
// frame ID was set on the UI thread.
- map.GetFrameIdOnIO(2, 1, CreateCallback(2, 1, "something else"));
+ map.GetFrameDataOnIO(2, 1, CreateCallback(2, 1, "something else"));
EXPECT_EQ(3, map.GetInternalCallbackCount());
EXPECT_EQ(0, map.GetInternalSize());
- map.SetInternalFrameId(2, 1);
+ map.SetInternalFrameData(2, 1);
EXPECT_EQ(1, map.GetInternalSize());
EXPECT_EQ(0U, results().size());
@@ -131,8 +142,8 @@ TEST_F(ExtensionApiFrameIdMapTest, GetFrameIdOnIO) {
{
// Callbacks for invalid IDs should immediately be run because it doesn't
// require a thread hop to determine their invalidity.
- map.GetFrameIdOnIO(-1, MSG_ROUTING_NONE,
- CreateCallback(-1, MSG_ROUTING_NONE, "invalid IDs"));
+ map.GetFrameDataOnIO(-1, MSG_ROUTING_NONE,
+ CreateCallback(-1, MSG_ROUTING_NONE, "invalid IDs"));
EXPECT_EQ(3, map.GetInternalCallbackCount()); // No change.
EXPECT_EQ(1, map.GetInternalSize()); // No change.
ASSERT_EQ(1U, results().size()); // +1
@@ -143,10 +154,10 @@ TEST_F(ExtensionApiFrameIdMapTest, GetFrameIdOnIO) {
{
// First set the frame ID on UI, then get it on IO. Callback should
// immediately be invoked.
- map.SetInternalFrameId(3, 1);
+ map.SetInternalFrameData(3, 1);
EXPECT_EQ(2, map.GetInternalSize());
- map.GetFrameIdOnIO(3, 1, CreateCallback(3, 1, "the only result"));
+ map.GetFrameDataOnIO(3, 1, CreateCallback(3, 1, "the only result"));
EXPECT_EQ(3, map.GetInternalCallbackCount()); // No change.
EXPECT_EQ(2, map.GetInternalSize()); // +1
ASSERT_EQ(1U, results().size()); // +1
@@ -158,13 +169,13 @@ TEST_F(ExtensionApiFrameIdMapTest, GetFrameIdOnIO) {
// Request the frame ID on IO, set the frame ID (in reality, set on the UI),
// and request another frame ID. The last query should cause both callbacks
// to run because the frame ID is known at the time of the call.
- map.GetFrameIdOnIO(7, 2, CreateCallback(7, 2, "queued"));
+ map.GetFrameDataOnIO(7, 2, CreateCallback(7, 2, "queued"));
EXPECT_EQ(4, map.GetInternalCallbackCount()); // +1
- map.SetInternalFrameId(7, 2);
+ map.SetInternalFrameData(7, 2);
EXPECT_EQ(3, map.GetInternalSize()); // +1
- map.GetFrameIdOnIO(7, 2, CreateCallback(7, 2, "not queued"));
+ map.GetFrameDataOnIO(7, 2, CreateCallback(7, 2, "not queued"));
EXPECT_EQ(3, map.GetInternalCallbackCount()); // -1 (first callback ran).
EXPECT_EQ(3, map.GetInternalSize()); // No change.
ASSERT_EQ(2U, results().size()); // +2 (both callbacks ran).
@@ -174,7 +185,7 @@ TEST_F(ExtensionApiFrameIdMapTest, GetFrameIdOnIO) {
}
// A call identical to the very first call.
- map.GetFrameIdOnIO(1, 2, CreateCallback(1, 2, "same as first"));
+ map.GetFrameDataOnIO(1, 2, CreateCallback(1, 2, "same as first"));
EXPECT_EQ(4, map.GetInternalCallbackCount());
EXPECT_EQ(3, map.GetInternalSize());
@@ -198,7 +209,7 @@ TEST_F(ExtensionApiFrameIdMapTest, GetFrameIdOnIO) {
// Request the frame ID for input that was already looked up. Should complete
// synchronously.
- map.GetFrameIdOnIO(1, 2, CreateCallback(1, 2, "first and cached"));
+ map.GetFrameDataOnIO(1, 2, CreateCallback(1, 2, "first and cached"));
EXPECT_EQ(0, map.GetInternalCallbackCount()); // No change.
EXPECT_EQ(4, map.GetInternalSize()); // No change.
ASSERT_EQ(1U, results().size()); // +1 (synchronous callback).
@@ -206,10 +217,10 @@ TEST_F(ExtensionApiFrameIdMapTest, GetFrameIdOnIO) {
ClearResults();
// Trigger frame removal and look up frame ID. The frame ID should no longer
- // be available. and GetFrameIdOnIO() should require a thread hop.
- map.RemoveInternalFrameId(1, 2);
+ // be available. and GetFrameDataOnIO() should require a thread hop.
+ map.RemoveInternalFrameData(1, 2);
EXPECT_EQ(3, map.GetInternalSize()); // -1
- map.GetFrameIdOnIO(1, 2, CreateCallback(1, 2, "first was removed"));
+ map.GetFrameDataOnIO(1, 2, CreateCallback(1, 2, "first was removed"));
EXPECT_EQ(1, map.GetInternalCallbackCount()); // +1
ASSERT_EQ(0U, results().size()); // No change (queued callback).
base::RunLoop().RunUntilIdle();
@@ -219,4 +230,34 @@ TEST_F(ExtensionApiFrameIdMapTest, GetFrameIdOnIO) {
EXPECT_EQ("first was removed", results()[0]);
}
+TEST_F(ExtensionApiFrameIdMapTest, GetCachedFrameDataOnIO) {
+ TestExtensionApiFrameIdMap map;
+ EXPECT_EQ(0, map.GetInternalSize());
+
+ const int kRenderProcessId = 1;
+ const int kFrameRoutingId = 2;
+
+ // Getting cached data on the IO thread should fail if there is no cached
+ // data for the entry...
+ ExtensionApiFrameIdMap::FrameData data;
+ EXPECT_FALSE(
+ map.GetCachedFrameDataOnIO(kRenderProcessId, kFrameRoutingId, &data));
+ // ... should not queue any callbacks...
+ EXPECT_EQ(0, map.GetInternalCallbackCount());
+ base::RunLoop().RunUntilIdle();
+ // ... and should not add any entries to the map.
+ EXPECT_EQ(0, map.GetInternalSize());
+
+ // Getting cached data should succeed if there is a cached entry.
+ map.SetInternalFrameData(kRenderProcessId, kFrameRoutingId);
+ EXPECT_EQ(1, map.GetInternalSize());
+ EXPECT_TRUE(
+ map.GetCachedFrameDataOnIO(kRenderProcessId, kFrameRoutingId, &data));
+ EXPECT_EQ(0, map.GetInternalCallbackCount());
+ EXPECT_EQ(ToTestFrameId(kRenderProcessId, kFrameRoutingId), data.frame_id);
+ EXPECT_EQ(ToTestParentFrameId(kRenderProcessId, kFrameRoutingId),
+ data.parent_frame_id);
+ EXPECT_EQ(ToTestTabId(kRenderProcessId, kFrameRoutingId), data.tab_id);
+}
+
} // namespace extensions
diff --git a/extensions/browser/extension_web_contents_observer.cc b/extensions/browser/extension_web_contents_observer.cc
index ac482df..e00f7f1 100644
--- a/extensions/browser/extension_web_contents_observer.cc
+++ b/extensions/browser/extension_web_contents_observer.cc
@@ -115,14 +115,14 @@ void ExtensionWebContentsObserver::RenderFrameCreated(
// Optimization: Look up the extension API frame ID to force the mapping to be
// cached. This minimizes the number of IO->UI->IO thread hops when the ID is
// looked up again on the IO thread for the webRequest API.
- ExtensionApiFrameIdMap::Get()->CacheFrameId(render_frame_host);
+ ExtensionApiFrameIdMap::Get()->CacheFrameData(render_frame_host);
}
void ExtensionWebContentsObserver::RenderFrameDeleted(
content::RenderFrameHost* render_frame_host) {
ProcessManager::Get(browser_context_)
->UnregisterRenderFrameHost(render_frame_host);
- ExtensionApiFrameIdMap::Get()->RemoveFrameId(render_frame_host);
+ ExtensionApiFrameIdMap::Get()->RemoveFrameData(render_frame_host);
}
void ExtensionWebContentsObserver::DidCommitProvisionalLoadForFrame(
diff --git a/extensions/browser/extensions_browser_client.cc b/extensions/browser/extensions_browser_client.cc
index b6c9c38..cae50ed 100644
--- a/extensions/browser/extensions_browser_client.cc
+++ b/extensions/browser/extensions_browser_client.cc
@@ -22,6 +22,11 @@ ExtensionsBrowserClient::CreateUpdateClient(content::BrowserContext* context) {
return scoped_refptr<update_client::UpdateClient>(nullptr);
}
+int ExtensionsBrowserClient::GetTabIdForWebContents(
+ content::WebContents* web_contents) {
+ return -1;
+}
+
void ExtensionsBrowserClient::ReportError(content::BrowserContext* context,
scoped_ptr<ExtensionError> error) {
LOG(ERROR) << error->GetDebugString();
diff --git a/extensions/browser/extensions_browser_client.h b/extensions/browser/extensions_browser_client.h
index dbd6e10..7c67893 100644
--- a/extensions/browser/extensions_browser_client.h
+++ b/extensions/browser/extensions_browser_client.h
@@ -246,6 +246,9 @@ class ExtensionsBrowserClient {
virtual scoped_refptr<update_client::UpdateClient> CreateUpdateClient(
content::BrowserContext* context);
+ // Returns the tab id for a given |web_contents|.
+ virtual int GetTabIdForWebContents(content::WebContents* web_contents);
+
// Returns the single instance of |this|.
static ExtensionsBrowserClient* Get();