Commit bd149bb9 authored by Dave Vandyke's avatar Dave Vandyke Committed by Chromium LUCI CQ

[DNR] Add tabUpdate option to setExtensionActionOptions API

Add the tabUpdate option to the setExtensionActionOptions API which
provides a way for extension developers to manually increase or decrease
the action count for a given tab.

Skipping presubmit since this seems to be hitting crbug.com/956368.

Bug: 1123296
No-Presubmit: True
Change-Id: Ie235f12841029aa45f92ed61b2e300d78d4d0fab
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2489913
Commit-Queue: Karan Bhatia <karandeepb@chromium.org>
Reviewed-by: default avatarKaran Bhatia <karandeepb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#832110}
parent 5c3ba407
......@@ -98,6 +98,7 @@
#include "extensions/common/api/declarative_net_request/test_utils.h"
#include "extensions/common/api/extension_action/action_info.h"
#include "extensions/common/constants.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/extension_features.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/file_util.h"
......@@ -396,17 +397,20 @@ class DeclarativeNetRequestBrowserTest
UnorderedElementsAreArray(expected_ruleset_ids));
}
void SetActionsAsBadgeText(const ExtensionId& extension_id, bool pref) {
const char* pref_string = pref ? "true" : "false";
std::string SetExtensionActionOptions(const ExtensionId& extension_id,
const std::string& options) {
static constexpr char kSetExtensionActionOptionsScript[] = R"(
chrome.declarativeNetRequest.setExtensionActionOptions(
{displayActionCountAsBadgeText: %s});
window.domAutomationController.send("done");
chrome.declarativeNetRequest.setExtensionActionOptions(%s,
() => {
window.domAutomationController.send(chrome.runtime.lastError ?
chrome.runtime.lastError.message : 'success');
}
);
)";
ExecuteScriptInBackgroundPage(
return ExecuteScriptInBackgroundPage(
extension_id,
base::StringPrintf(kSetExtensionActionOptionsScript, pref_string));
base::StringPrintf(kSetExtensionActionOptionsScript, options.c_str()));
}
// Navigates frame with name |frame_name| to |url|.
......@@ -3160,8 +3164,12 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
EXPECT_EQ(default_badge_text,
query_badge_text(extension_2->id(), first_tab_id));
SetActionsAsBadgeText(extension_1->id(), true);
SetActionsAsBadgeText(extension_2->id(), true);
EXPECT_EQ(SetExtensionActionOptions(extension_1->id(),
"{displayActionCountAsBadgeText: true}"),
"success");
EXPECT_EQ(SetExtensionActionOptions(extension_2->id(),
"{displayActionCountAsBadgeText: true}"),
"success");
// 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
......@@ -3215,8 +3223,12 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
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);
EXPECT_EQ(SetExtensionActionOptions(extension_1->id(),
"{displayActionCountAsBadgeText: false}"),
"success");
EXPECT_EQ(SetExtensionActionOptions(extension_2->id(),
"{displayActionCountAsBadgeText: false}"),
"success");
// 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
......@@ -3286,7 +3298,9 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
TestExtensionActionAPIObserver test_api_observer(
profile(), extension_id, {web_contents(), second_browser_contents});
SetActionsAsBadgeText(extension_id, true);
EXPECT_EQ(SetExtensionActionOptions(extension_id,
"{displayActionCountAsBadgeText: true}"),
"success");
// Wait until ExtensionActionAPI::NotifyChange is called, then perform a
// sanity check that one action was matched, and this is reflected in the
......@@ -3594,6 +3608,124 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
}
}
// Test that the setExtensionActionOptions tabUpdate option works correctly.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
ActionsMatchedCountAsBadgeTextTabUpdate) {
// Load the extension with a background script so scripts can be run from its
// generated background page.
set_config_flags(ConfigFlag::kConfig_HasBackgroundScript);
// Set up an extension with a blocking rule.
TestRule rule = CreateGenericRule();
rule.condition->url_filter = std::string("||abc.com");
rule.condition->resource_types = std::vector<std::string>({"sub_frame"});
ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
{rule}, "extension", {URLPattern::kAllUrlsPattern}));
const Extension* extension = last_loaded_extension();
ExtensionAction* action =
ExtensionActionManager::Get(web_contents()->GetBrowserContext())
->GetExtensionAction(*extension);
const std::string default_badge_text = "asdf";
action->SetBadgeText(ExtensionAction::kDefaultTabId, default_badge_text);
// Display action count as badge text for |extension|.
EXPECT_EQ(SetExtensionActionOptions(extension->id(),
"{displayActionCountAsBadgeText: true}"),
"success");
// Navigate to a page with two frames, the same-origin one should be blocked.
const GURL page_url =
embedded_test_server()->GetURL("abc.com", "/page_with_two_frames.html");
ui_test_utils::NavigateToURL(browser(), page_url);
int tab_id = ExtensionTabUtil::GetTabId(web_contents());
// Verify that the initial badge text reflects that the same-origin frame was
// blocked.
EXPECT_EQ("1", action->GetDisplayBadgeText(tab_id));
// Increment the action count.
EXPECT_EQ(SetExtensionActionOptions(
extension->id(),
base::StringPrintf("{tabUpdate: {tabId: %d, increment: 10}}",
tab_id)),
"success");
EXPECT_EQ("11", action->GetDisplayBadgeText(tab_id));
// An increment of 0 is ignored.
EXPECT_EQ(
SetExtensionActionOptions(
extension->id(),
base::StringPrintf("{tabUpdate: {tabId: %d, increment: 0}}", tab_id)),
"success");
EXPECT_EQ("11", action->GetDisplayBadgeText(tab_id));
// If the tab doesn't exist an error should be shown.
EXPECT_EQ(
SetExtensionActionOptions(
extension->id(),
base::StringPrintf("{tabUpdate: {tabId: %d, increment: 10}}", 999)),
ErrorUtils::FormatErrorMessage(declarative_net_request::kTabNotFoundError,
"999"));
EXPECT_EQ("11", action->GetDisplayBadgeText(tab_id));
// The action count should continue to increment when an action is taken.
NavigateFrame("frame1", page_url);
EXPECT_EQ("12", action->GetDisplayBadgeText(tab_id));
// The action count can be decremented.
EXPECT_EQ(SetExtensionActionOptions(
extension->id(),
base::StringPrintf("{tabUpdate: {tabId: %d, increment: -5}}",
tab_id)),
"success");
EXPECT_EQ("7", action->GetDisplayBadgeText(tab_id));
// Check that the action count cannot be decremented below 0. We fallback to
// displaying the default badge text when the action count is 0.
EXPECT_EQ(SetExtensionActionOptions(
extension->id(),
base::StringPrintf("{tabUpdate: {tabId: %d, increment: -10}}",
tab_id)),
"success");
EXPECT_EQ(default_badge_text, action->GetDisplayBadgeText(tab_id));
EXPECT_EQ(SetExtensionActionOptions(
extension->id(),
base::StringPrintf("{tabUpdate: {tabId: %d, increment: -1}}",
tab_id)),
"success");
EXPECT_EQ(default_badge_text, action->GetDisplayBadgeText(tab_id));
EXPECT_EQ(
SetExtensionActionOptions(
extension->id(),
base::StringPrintf("{tabUpdate: {tabId: %d, increment: 3}}", tab_id)),
"success");
EXPECT_EQ("3", action->GetDisplayBadgeText(tab_id));
// The action count cannot be incremented if the display action as badge text
// feature is not enabled.
EXPECT_EQ(
SetExtensionActionOptions(
extension->id(),
base::StringPrintf("{displayActionCountAsBadgeText: false, "
"tabUpdate: {tabId: %d, increment: 10}}",
tab_id)),
declarative_net_request::kIncrementActionCountWithoutUseAsBadgeTextError);
EXPECT_EQ(default_badge_text, action->GetDisplayBadgeText(tab_id));
// Any increment to the action count should not be ignored if we're enabling
// the preference.
EXPECT_EQ(SetExtensionActionOptions(
extension->id(),
base::StringPrintf("{displayActionCountAsBadgeText: true, "
"tabUpdate: {tabId: %d, increment: 5}}",
tab_id)),
"success");
EXPECT_EQ("8", action->GetDisplayBadgeText(tab_id));
}
// Test that the onRuleMatchedDebug event is only available for unpacked
// extensions.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
......
......@@ -35,6 +35,7 @@
#include "chrome/browser/extensions/event_router_forwarder.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_system_factory.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/extensions/menu_manager.h"
#include "chrome/browser/extensions/updater/chrome_update_client_config.h"
......@@ -581,6 +582,13 @@ bool ChromeExtensionsBrowserClient::IsScreenshotRestricted(
return tabs_util::IsScreenshotRestricted(web_contents);
}
bool ChromeExtensionsBrowserClient::IsValidTabId(
content::BrowserContext* context,
int tab_id) const {
return ExtensionTabUtil::GetTabById(
tab_id, context, true /* include_incognito */, nullptr /* contents */);
}
// static
void ChromeExtensionsBrowserClient::SetMediaRouterAccessLoggerForTesting(
MediaRouterExtensionAccessLogger* media_router_access_logger) {
......
......@@ -159,6 +159,8 @@ class ChromeExtensionsBrowserClient : public ExtensionsBrowserClient {
content::BrowserContext* context) override;
bool IsScreenshotRestricted(
content::WebContents* web_contents) const override;
bool IsValidTabId(content::BrowserContext* context,
int tab_id) const override;
static void set_did_chrome_update_for_testing(bool did_update);
......
......@@ -322,6 +322,23 @@ int ActionTracker::GetPendingRuleCountForTest(const ExtensionId& extension_id,
: tracked_info->second.matched_rules.size();
}
void ActionTracker::IncrementActionCountForTab(const ExtensionId& extension_id,
int tab_id,
int increment) {
TrackedInfo& tracked_info = rules_tracked_[{extension_id, tab_id}];
size_t new_action_count =
std::max<int>(tracked_info.action_count + increment, 0);
if (tracked_info.action_count == new_action_count)
return;
DCHECK(ExtensionsAPIClient::Get());
ExtensionsAPIClient::Get()->UpdateActionCount(browser_context_, extension_id,
tab_id, new_action_count,
false /* clear_badge_text */);
tracked_info.action_count = new_action_count;
}
template <typename T>
ActionTracker::TrackedInfoContextKey<T>::TrackedInfoContextKey(
ExtensionId extension_id,
......
......@@ -103,6 +103,13 @@ class ActionTracker {
int GetPendingRuleCountForTest(const ExtensionId& extension_id,
int64_t navigation_id);
// Increments the action count for the given |extension_id| and |tab_id|.
// A negative value for |increment| will decrement the action count, but the
// action count will never be less than 0.
void IncrementActionCountForTab(const ExtensionId& extension_id,
int tab_id,
int increment);
private:
// Template key type used for TrackedInfo, specified by an extension_id and
// another ID.
......
......@@ -106,6 +106,11 @@ const char kEnabledRulesetsRegexRuleCountExceeded[] =
"limit.";
const char kInternalErrorUpdatingEnabledRulesets[] = "Internal error.";
const char kTabNotFoundError[] = "No tab with id: *.";
const char kIncrementActionCountWithoutUseAsBadgeTextError[] =
"Cannot increment action count unless displaying action count as badge "
"text.";
const char kIndexAndPersistRulesTimeHistogram[] =
"Extensions.DeclarativeNetRequest.IndexAndPersistRulesTime";
const char kManifestRulesCountHistogram[] =
......
......@@ -174,6 +174,10 @@ extern const char kEnabledRulesetsRuleCountExceeded[];
extern const char kEnabledRulesetsRegexRuleCountExceeded[];
extern const char kInternalErrorUpdatingEnabledRulesets[];
// setExtensionActionOptions API errors.
extern const char kTabNotFoundError[];
extern const char kIncrementActionCountWithoutUseAsBadgeTextError[];
// Histogram names.
extern const char kIndexAndPersistRulesTimeHistogram[];
extern const char kManifestRulesCountHistogram[];
......
......@@ -30,6 +30,7 @@
#include "extensions/browser/api/extensions_api_client.h"
#include "extensions/browser/extension_file_task_runner.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extensions_browser_client.h"
#include "extensions/browser/quota_service.h"
#include "extensions/common/api/declarative_net_request.h"
#include "extensions/common/api/declarative_net_request/constants.h"
......@@ -392,34 +393,58 @@ DeclarativeNetRequestSetExtensionActionOptionsFunction::Run() {
EXTENSION_FUNCTION_VALIDATE(params);
EXTENSION_FUNCTION_VALIDATE(error.empty());
bool use_action_count_as_badge_text =
params->options.display_action_count_as_badge_text;
declarative_net_request::RulesMonitorService* rules_monitor_service =
declarative_net_request::RulesMonitorService::Get(browser_context());
DCHECK(rules_monitor_service);
ExtensionPrefs* prefs = ExtensionPrefs::Get(browser_context());
if (use_action_count_as_badge_text ==
prefs->GetDNRUseActionCountAsBadgeText(extension_id()))
return RespondNow(NoArguments());
declarative_net_request::ActionTracker& action_tracker =
rules_monitor_service->action_tracker();
prefs->SetDNRUseActionCountAsBadgeText(extension_id(),
use_action_count_as_badge_text);
// If the preference is switched on, update the extension's badge text with
// the number of actions matched for this extension. Otherwise, clear the
// action count for the extension's icon and show the default badge text if
// set.
if (use_action_count_as_badge_text) {
declarative_net_request::RulesMonitorService* rules_monitor_service =
declarative_net_request::RulesMonitorService::Get(browser_context());
DCHECK(rules_monitor_service);
const declarative_net_request::ActionTracker& action_tracker =
rules_monitor_service->action_tracker();
action_tracker.OnPreferenceEnabled(extension_id());
} else {
DCHECK(ExtensionsAPIClient::Get());
ExtensionsAPIClient::Get()->ClearActionCount(browser_context(),
*extension());
bool use_action_count_as_badge_text =
prefs->GetDNRUseActionCountAsBadgeText(extension_id());
if (params->options.display_action_count_as_badge_text &&
*params->options.display_action_count_as_badge_text !=
use_action_count_as_badge_text) {
use_action_count_as_badge_text =
*params->options.display_action_count_as_badge_text;
prefs->SetDNRUseActionCountAsBadgeText(extension_id(),
use_action_count_as_badge_text);
// If the preference is switched on, update the extension's badge text
// with the number of actions matched for this extension. Otherwise, clear
// the action count for the extension's icon and show the default badge
// text if set.
if (use_action_count_as_badge_text)
action_tracker.OnPreferenceEnabled(extension_id());
else {
DCHECK(ExtensionsAPIClient::Get());
ExtensionsAPIClient::Get()->ClearActionCount(browser_context(),
*extension());
}
}
if (params->options.tab_update) {
if (!use_action_count_as_badge_text) {
return RespondNow(
Error(declarative_net_request::
kIncrementActionCountWithoutUseAsBadgeTextError));
}
const auto& update_options = *params->options.tab_update;
int tab_id = update_options.tab_id;
if (!ExtensionsBrowserClient::Get()->IsValidTabId(browser_context(),
tab_id)) {
return RespondNow(Error(ErrorUtils::FormatErrorMessage(
declarative_net_request::kTabNotFoundError,
base::NumberToString(tab_id))));
}
action_tracker.IncrementActionCountForTab(extension_id(), tab_id,
update_options.increment);
}
return RespondNow(NoArguments());
}
......
......@@ -124,4 +124,9 @@ bool ExtensionsBrowserClient::IsScreenshotRestricted(
return false;
}
bool ExtensionsBrowserClient::IsValidTabId(content::BrowserContext* context,
int tab_id) const {
return false;
}
} // namespace extensions
......@@ -370,6 +370,9 @@ class ExtensionsBrowserClient {
// Protection policy.
virtual bool IsScreenshotRestricted(content::WebContents* web_contents) const;
// Returns true if the given |tab_id| exists.
virtual bool IsValidTabId(content::BrowserContext* context, int tab_id) const;
private:
std::vector<std::unique_ptr<ExtensionsBrowserAPIProvider>> providers_;
};
......
......@@ -425,10 +425,20 @@ namespace declarativeNetRequest {
DOMString[]? enableRulesetIds;
};
dictionary TabActionCountUpdate {
// The tab for which to update the action count.
long tabId;
// The amount to increment the tab's action count by. Negative values will
// decrement the count.
long increment;
};
dictionary ExtensionActionOptions {
// Whether to automatically display the action count for a page as the
// extension's badge text. False by default.
boolean displayActionCountAsBadgeText;
// extension's badge text. This preference is persisted across sessions.
boolean? displayActionCountAsBadgeText;
// Details of how the tab's action count should be adjusted.
TabActionCountUpdate? tabUpdate;
};
callback EmptyCallback = void();
......@@ -531,10 +541,12 @@ namespace declarativeNetRequest {
static void getMatchedRules(optional MatchedRulesFilter filter,
GetMatchedRulesCallback callback);
// Configures how matched actions will be displayed on the extension action.
// This preference is persisted across sessions.
// Configures if the action count for tabs should be displayed as the
// extension action's badge text and provides a way for that action count to
// be incremented.
static void setExtensionActionOptions(
ExtensionActionOptions options);
ExtensionActionOptions options,
optional EmptyCallback callback);
// Checks if the given regular expression will be supported as a
// <code>regexFilter</code> rule condition.
......
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