Commit de569e05 authored by Dave Vandyke's avatar Dave Vandyke Committed by Commit Bot

[DNR] Expose action count for extensions with permissions

When the extension has enabled displayActionCountAsBadgeText, the
action count for a tab is displayed as the badge text. So far, if the
extension then queries the badge text a placeholder is returned. With
this change we instead return the action count if the extension has
the declarativeNetRequestFeedback permission or activeTab access
granted for the tab.

Bug: 1139852
Change-Id: I0500cee99a854390e88a7a7cb6e25ef90972d815
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2502928Reviewed-by: default avatarKelvin Jiang <kelvinjiang@chromium.org>
Reviewed-by: default avatarKaran Bhatia <karandeepb@chromium.org>
Commit-Queue: Karan Bhatia <karandeepb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#828380}
parent 9124aaee
......@@ -3083,11 +3083,12 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, WebRequestPriority) {
}
// Test that the extension cannot retrieve the number of actions matched
// from the badge text by calling chrome.browserAction.getBadgeText.
// from the badge text by calling chrome.browserAction.getBadgeText, unless
// it has the declarativeNetRequestFeedback permission or activeTab is granted
// for the tab.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
GetBadgeTextForActionsMatched) {
auto query_badge_text_from_ext = [this](const ExtensionId& extension_id,
int tab_id) {
auto query_badge_text = [this](const ExtensionId& extension_id, int tab_id) {
static constexpr char kBadgeTextQueryScript[] = R"(
chrome.browserAction.getBadgeText({tabId: %d}, badgeText => {
window.domAutomationController.send(badgeText);
......@@ -3098,69 +3099,142 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
extension_id, base::StringPrintf(kBadgeTextQueryScript, tab_id));
};
// Load the extension with a background script so scripts can be run from its
// generated background page.
set_config_flags(ConfigFlag::kConfig_HasBackgroundScript);
TestRule rule = CreateGenericRule();
rule.condition->url_filter = "abc.com";
rule.condition->url_filter = "def.com";
rule.id = kMinValidID;
rule.condition->resource_types = std::vector<std::string>({"main_frame"});
rule.condition->resource_types = std::vector<std::string>({"sub_frame"});
rule.action->type = "block";
std::vector<TestRule> rules({rule});
set_config_flags(ConfigFlag::kConfig_HasBackgroundScript |
ConfigFlag::kConfig_HasActiveTab);
// Create the first extension.
ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
{rules}, "test_extension", {URLPattern::kAllUrlsPattern}));
{rule}, "test_extension", {URLPattern::kAllUrlsPattern}));
const Extension* extension_1 = last_loaded_extension();
ExtensionAction* action_1 =
ExtensionActionManager::Get(web_contents()->GetBrowserContext())
->GetExtensionAction(*extension_1);
const ExtensionId& extension_id = last_loaded_extension_id();
const Extension* dnr_extension = last_loaded_extension();
TestRule rule_2 = CreateGenericRule();
rule_2.condition->url_filter = "ghi.com";
rule_2.id = kMinValidID;
rule_2.condition->resource_types = std::vector<std::string>({"sub_frame"});
rule_2.action->type = "block";
ExtensionAction* action =
set_config_flags(ConfigFlag::kConfig_HasBackgroundScript |
ConfigFlag::kConfig_HasFeedbackPermission);
// Create the second extension with the feedback permission.
ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
{rule_2}, "test_extension_2", {URLPattern::kAllUrlsPattern}));
const Extension* extension_2 = last_loaded_extension();
ExtensionAction* action_2 =
ExtensionActionManager::Get(web_contents()->GetBrowserContext())
->GetExtensionAction(*dnr_extension);
->GetExtensionAction(*extension_2);
const std::string default_badge_text = "asdf";
action->SetBadgeText(ExtensionAction::kDefaultTabId, default_badge_text);
action_1->SetBadgeText(ExtensionAction::kDefaultTabId, default_badge_text);
action_2->SetBadgeText(ExtensionAction::kDefaultTabId, default_badge_text);
const GURL page_url = embedded_test_server()->GetURL(
"abc.com", "/pages_with_script/index.html");
// Navigate to a page with two frames, no requests should be blocked
// initially.
const GURL page_url =
embedded_test_server()->GetURL("abc.com", "/page_with_two_frames.html");
ui_test_utils::NavigateToURL(browser(), page_url);
TabHelper* tab_helper = TabHelper::FromWebContents(web_contents());
ActiveTabPermissionGranter* active_tab_granter =
tab_helper->active_tab_permission_granter();
ASSERT_TRUE(active_tab_granter);
// The preference is initially turned off. Both the visible badge text and the
// badge text queried by the extension using getBadgeText() should return the
// default badge text.
int first_tab_id = ExtensionTabUtil::GetTabId(web_contents());
EXPECT_EQ(default_badge_text, action->GetDisplayBadgeText(first_tab_id));
EXPECT_EQ(default_badge_text, action_1->GetDisplayBadgeText(first_tab_id));
EXPECT_EQ(default_badge_text, action_2->GetDisplayBadgeText(first_tab_id));
std::string queried_badge_text =
query_badge_text_from_ext(extension_id, first_tab_id);
EXPECT_EQ(default_badge_text, queried_badge_text);
EXPECT_EQ(default_badge_text,
query_badge_text(extension_1->id(), first_tab_id));
EXPECT_EQ(default_badge_text,
query_badge_text(extension_2->id(), first_tab_id));
SetActionsAsBadgeText(extension_1->id(), true);
SetActionsAsBadgeText(extension_2->id(), true);
// After enabling the preference the visible badge text should remain as the
// default initially, as the action count for the tab is still 0 for both
// extensions.
EXPECT_EQ(default_badge_text, action_1->GetDisplayBadgeText(first_tab_id));
EXPECT_EQ(default_badge_text, action_2->GetDisplayBadgeText(first_tab_id));
SetActionsAsBadgeText(extension_id, true);
// Since the preference is on for the current tab, attempting to query the
// badge text from the extension should return the placeholder text instead of
// the matched action count.
queried_badge_text = query_badge_text_from_ext(extension_id, first_tab_id);
// badge text should return the placeholder text instead of the matched action
// count for |extension_1|.
EXPECT_EQ(declarative_net_request::kActionCountPlaceholderBadgeText,
queried_badge_text);
query_badge_text(extension_1->id(), first_tab_id));
// The placeholder should not be returned if the declarativeNetRequestFeedback
// permission is enabled.
EXPECT_EQ(default_badge_text,
query_badge_text(extension_2->id(), first_tab_id));
// |extension_1| should block this frame navigation.
NavigateFrame("frame1", embedded_test_server()->GetURL("def.com", "/"));
// |extension_2| should block this frame navigation.
NavigateFrame("frame2", embedded_test_server()->GetURL("ghi.com", "/"));
// One action was matched, and this should be reflected in the badge text.
EXPECT_EQ("1", action->GetDisplayBadgeText(first_tab_id));
EXPECT_EQ("1", action_1->GetDisplayBadgeText(first_tab_id));
EXPECT_EQ("1", action_2->GetDisplayBadgeText(first_tab_id));
// The placeholder should still be returned for the first extension, but the
// second extension with permission should now return the action count.
EXPECT_EQ(declarative_net_request::kActionCountPlaceholderBadgeText,
query_badge_text(extension_1->id(), first_tab_id));
EXPECT_EQ("1", query_badge_text(extension_2->id(), first_tab_id));
// Tab-specific badge text should take priority over the action count and
// placeholder text.
action_1->SetBadgeText(first_tab_id, "text_1");
action_2->SetBadgeText(first_tab_id, "text_2");
EXPECT_EQ("text_1", query_badge_text(extension_1->id(), first_tab_id));
EXPECT_EQ("text_2", query_badge_text(extension_2->id(), first_tab_id));
action_1->ClearBadgeText(first_tab_id);
action_2->ClearBadgeText(first_tab_id);
// Querying the badge text with the activeTab access for the current tab
// should provide the matched action count.
active_tab_granter->GrantIfRequested(extension_1);
EXPECT_EQ("1", query_badge_text(extension_1->id(), first_tab_id));
// Revoking activeTab should cause the placeholder text to be returned again
// since we don't have the declarativeNetRequestFeedback permission.
active_tab_granter->RevokeForTesting();
EXPECT_EQ(declarative_net_request::kActionCountPlaceholderBadgeText,
query_badge_text(extension_1->id(), first_tab_id));
SetActionsAsBadgeText(extension_1->id(), false);
SetActionsAsBadgeText(extension_2->id(), false);
SetActionsAsBadgeText(extension_id, false);
// Switching the preference off should cause the extension queried badge text
// to be the explicitly set badge text for this tab if it exists. In this
// case, the queried badge text should be the default badge text.
queried_badge_text = query_badge_text_from_ext(extension_id, first_tab_id);
EXPECT_EQ(default_badge_text, queried_badge_text);
EXPECT_EQ(default_badge_text,
query_badge_text(extension_1->id(), first_tab_id));
EXPECT_EQ(default_badge_text,
query_badge_text(extension_2->id(), first_tab_id));
// The displayed badge text should be the default badge text now that the
// preference is off.
EXPECT_EQ(default_badge_text, action->GetDisplayBadgeText(first_tab_id));
EXPECT_EQ(default_badge_text, action_1->GetDisplayBadgeText(first_tab_id));
EXPECT_EQ(default_badge_text, action_2->GetDisplayBadgeText(first_tab_id));
// Verify that turning off the preference deletes the DNR action count within
// the extension action.
EXPECT_FALSE(action->HasDNRActionCount(first_tab_id));
EXPECT_FALSE(action_1->HasDNRActionCount(first_tab_id));
EXPECT_FALSE(action_2->HasDNRActionCount(first_tab_id));
}
// Test that enabling the "displayActionCountAsBadgeText" preference using
......
......@@ -31,6 +31,7 @@
#include "components/sessions/content/session_tab_helper.h"
#include "content/public/browser/notification_service.h"
#include "extensions/browser/api/declarative_net_request/constants.h"
#include "extensions/browser/api/declarative_net_request/utils.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_action_manager.h"
#include "extensions/browser/extension_host.h"
......@@ -513,18 +514,24 @@ ExtensionActionGetPopupFunction::RunExtensionAction() {
ExtensionFunction::ResponseAction
ExtensionActionGetBadgeTextFunction::RunExtensionAction() {
// Return a placeholder value if the extension has enabled using
// declarativeNetRequest action count as badge text and the badge count shown
// for this tab is the number of actions matched.
std::string badge_text =
extension_action_->UseDNRActionCountAsBadgeText(tab_id_)
? declarative_net_request::kActionCountPlaceholderBadgeText
: extension_action_->GetExplicitlySetBadgeText(tab_id_);
// TODO(crbug.com/990224): Document this behavior once
// chrome.declarativeNetRequest.setExtensionActionOptions is promoted to beta
// from trunk.
return RespondNow(OneArgument(base::Value(std::move(badge_text))));
ExtensionPrefs* prefs = ExtensionPrefs::Get(browser_context());
bool is_dnr_action_count_active =
prefs->GetDNRUseActionCountAsBadgeText(extension_id()) &&
!extension_action_->HasBadgeText(tab_id_);
// Ensure that the placeholder string is returned if this extension is
// displaying action counts for the badge labels and the extension doesn't
// have permission to view the action count for this tab. Note that
// tab-specific badge text takes priority over the action count.
if (is_dnr_action_count_active &&
!declarative_net_request::HasDNRFeedbackPermission(extension(),
tab_id_)) {
return RespondNow(OneArgument(base::Value(
std::move(declarative_net_request::kActionCountPlaceholderBadgeText))));
}
return RespondNow(OneArgument(
base::Value(extension_action_->GetDisplayBadgeText(tab_id_))));
}
ExtensionFunction::ResponseAction
......
......@@ -171,7 +171,7 @@
}, {
"name": "getBadgeText",
"type": "function",
"description": "Gets the badge text of the action. If no tab is specified, the non-tab-specific badge text is returned.",
"description": "Gets the badge text of the action. If no tab is specified, the non-tab-specific badge text is returned. If <a href='declarativeNetRequest#setExtensionActionOptions'>displayActionCountAsBadgeText</a> is enabled, a placeholder text will be returned unless the <a href='declare_permissions#declarativeNetRequestFeedback'>declarativeNetRequestFeedback</a> permission is present or tab-specific badge text was provided.",
"parameters": [{
"name": "details",
"$ref": "TabDetails"
......
......@@ -50,16 +50,8 @@ bool CanCallGetMatchedRules(content::BrowserContext* browser_context,
const Extension* extension,
base::Optional<int> tab_id,
std::string* error) {
const PermissionsData* permissions_data = extension->permissions_data();
const auto kFeedbackPermission =
APIPermission::kDeclarativeNetRequestFeedback;
bool can_call = tab_id.has_value()
? permissions_data->HasAPIPermissionForTab(
*tab_id, kFeedbackPermission)
: permissions_data->HasAPIPermission(kFeedbackPermission);
bool can_call =
declarative_net_request::HasDNRFeedbackPermission(extension, tab_id);
if (!can_call)
*error = declarative_net_request::kErrorGetMatchedRulesMissingPermissions;
......
......@@ -27,6 +27,8 @@
#include "extensions/browser/api/web_request/web_request_resource_type.h"
#include "extensions/common/api/declarative_net_request/constants.h"
#include "extensions/common/api/declarative_net_request/dnr_manifest_data.h"
#include "extensions/common/permissions/api_permission.h"
#include "extensions/common/permissions/permissions_data.h"
#include "third_party/flatbuffers/src/include/flatbuffers/flatbuffers.h"
namespace extensions {
......@@ -359,5 +361,15 @@ size_t GetEnabledStaticRuleCount(const CompositeMatcher* composite_matcher) {
return enabled_static_rule_count;
}
bool HasDNRFeedbackPermission(const Extension* extension,
const base::Optional<int>& tab_id) {
const PermissionsData* permissions_data = extension->permissions_data();
return tab_id.has_value()
? permissions_data->HasAPIPermissionForTab(
*tab_id, APIPermission::kDeclarativeNetRequestFeedback)
: permissions_data->HasAPIPermission(
APIPermission::kDeclarativeNetRequestFeedback);
}
} // namespace declarative_net_request
} // namespace extensions
......@@ -143,6 +143,12 @@ T CreateString(const flatbuffers::String& str) {
// |composite_matcher|.
size_t GetEnabledStaticRuleCount(const CompositeMatcher* composite_matcher);
// Returns true if |extension| has the declarativeNetRequestFeedback permission
// for the specified |tab_id|. If |tab_is| is omitted, then non-tab specific
// permissions are checked.
bool HasDNRFeedbackPermission(const Extension* extension,
const base::Optional<int>& tab_id);
} // namespace declarative_net_request
} // namespace extensions
......
......@@ -260,16 +260,13 @@ gfx::Image ExtensionAction::GetPlaceholderIconImage() const {
}
std::string ExtensionAction::GetDisplayBadgeText(int tab_id) const {
return UseDNRActionCountAsBadgeText(tab_id)
? base::NumberToString(GetDNRActionCount(tab_id))
: GetExplicitlySetBadgeText(tab_id);
}
bool ExtensionAction::UseDNRActionCountAsBadgeText(int tab_id) const {
// Tab specific badge text set by an extension overrides the automatically set
// action count. Action count should only be shown if at least one action is
// matched.
return !HasBadgeText(tab_id) && GetDNRActionCount(tab_id) > 0;
bool use_dnr_action_count =
!HasBadgeText(tab_id) && GetDNRActionCount(tab_id) > 0;
return use_dnr_action_count ? base::NumberToString(GetDNRActionCount(tab_id))
: GetExplicitlySetBadgeText(tab_id);
}
bool ExtensionAction::HasPopupUrl(int tab_id) const {
......
......@@ -166,10 +166,6 @@ class ExtensionAction {
// - The default badge text, if set, otherwise: an empty string.
std::string GetDisplayBadgeText(int tab_id) const;
// Returns whether this extension action is using the DNR action count as its
// badge text.
bool UseDNRActionCountAsBadgeText(int tab_id) const;
// Set this action's badge visibility on a specific tab. Returns true if
// the visibility has changed.
bool SetIsVisible(int tab_id, bool value);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment