Skip to content

Commit

Permalink
API to open Shields and Rewards dialog UI
Browse files Browse the repository at this point in the history
Introduces `chrome.{ braveRewards, braveShields }.openBrowserActionUI(optional windowId, optional relativePath)`.

Several upcoming features require being able to summon a dialog attached to Shields or Rewards icons from some other user action that is not clicking on the icon buttons (the only current way to open the dialogs). These features include:

• Offering to bypass paywalls (Publisher Access Pass)
• Offering to turn shields off (Webcompat detection)
• Offering to opt-in to Rewards or complete a Rewards captcha

The API allows a custom path to be sent to open a specific HTML page other than the one configured for the active tab, e.g. offer-paywall.html, solve-captcha.html etc

The API will default to the current window for the profile, which only works when called from a WebUI page. When called from a background script, a windowId will need to be provided so that the popup is shown on a compatible active or recently-active window (and not, e.g., a devtools window).

This was prototyped for the Publisher Access Pass concept at #3565 and modified for this PR to use the Observer pattern.

If there is a webui page which has access to chrome.braveShields or chrome.braveRewards API, then simply call `chrome.brave{Shields | Rewards}.openBrowserActionUI()` and `chrome.brave{Shields | Rewards}.openBrowserActionUI('fake-page.html)` to test. A popup should open on the current window

Test Plan:
Open chrome://inspect, switch to the Extensions tab, and inspect either the Rewards or Brave extension. Then use the following API calls, one at a time (uncomment the line you wish to run):
```
chrome.windows.getAll(wins => {
  // test that we can open in a specified window
  // chrome.braveShields.openBrowserActionUI(wins[0].id)
  // test that we can specify a page (should be 404 though!)
  // chrome.braveShields.openBrowserActionUI(wins[0].id, 'another.html)
})
// Test that we can call in the 'active' window (e.g. if the call is made from NTP, which is the intention for rewards). Run this command then switch focus to an actual browser window for the profile.
// setTimeout(() => chrome.braveShields.openBrowserActionUI('popup.html?grant-id=42'), 2000)
// And with the default popup html
// setTimeout(() => chrome.braveShields.openBrowserActionUI(), 2000)
```

Then create or open another profile at the same time, and verify that calling the API from one profile cannot open a popup in a window id from another profile.
Also check that the API works for the other profile.
  • Loading branch information
petemill committed Oct 24, 2019
1 parent 87e70e8 commit 73e30e9
Show file tree
Hide file tree
Showing 17 changed files with 393 additions and 17 deletions.
2 changes: 2 additions & 0 deletions browser/extensions/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ declare_args() {

source_set("extensions") {
sources = [
"api/brave_action_api.cc",
"api/brave_action_api.h",
"api/brave_extensions_api_client.cc",
"api/brave_extensions_api_client.h",
"api/brave_rewards_api.cc",
Expand Down
166 changes: 166 additions & 0 deletions browser/extensions/api/brave_action_api.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// Copyright (c) 2019 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// you can obtain one at http://mozilla.org/MPL/2.0/.


#include "brave/browser/extensions/api/brave_action_api.h"

#include <memory>
#include <string>
#include <utility>

#include "base/no_destructor.h"
#include "components/keyed_service/core/dependency_manager.h"
#include "components/keyed_service/core/keyed_service_factory.h"
#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
#include "chrome/browser/extensions/api/tabs/windows_util.h"
#include "chrome/browser/extensions/chrome_extension_function_details.h"
#include "chrome/browser/extensions/window_controller.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "extensions/browser/extension_function.h"

namespace {

class BraveActionAPIDependencyManager : public DependencyManager {
public:
static BraveActionAPIDependencyManager* GetInstance() {
static base::NoDestructor<BraveActionAPIDependencyManager> factory;
return factory.get();
}
BraveActionAPIDependencyManager() { }
~BraveActionAPIDependencyManager() override;
private:
DISALLOW_COPY_AND_ASSIGN(BraveActionAPIDependencyManager);
};

BraveActionAPIDependencyManager::~BraveActionAPIDependencyManager() { }

class BraveActionAPIFactory : public KeyedServiceFactory {
public:
BraveActionAPIFactory() : KeyedServiceFactory("BraveActionAPI",
BraveActionAPIDependencyManager::GetInstance(), SIMPLE) { }

extensions::BraveActionAPI* GetBraveActionAPI(Browser* context) {
return static_cast<extensions::BraveActionAPI*>(
GetServiceForContext(context, true));
}

private:
// KeyedServiceFactory:
std::unique_ptr<KeyedService> BuildServiceInstanceFor(
void* context) const final {
return base::WrapUnique(new extensions::BraveActionAPI());
}
bool IsOffTheRecord(void* context) const final {
return static_cast<Browser*>(context)
->profile()->IsOffTheRecord();
}
void* GetContextToUse(void* context) const final {
return context;
}
void CreateServiceNow(void* context) final {
KeyedServiceFactory::GetServiceForContext(context, true);
}
DISALLOW_COPY_AND_ASSIGN(BraveActionAPIFactory);
};

static BraveActionAPIFactory* GetFactoryInstance() {
static base::NoDestructor<BraveActionAPIFactory> instance;
return instance.get();
}

} // namespace

namespace extensions {
//
// BraveActionAPI::Observer
//
BraveActionAPI::Observer::Observer() { }

BraveActionAPI::Observer::~Observer() { }

//
// BraveActionAPI
//
// static
BraveActionAPI* BraveActionAPI::Get(Browser* context) {
return GetFactoryInstance()->GetBraveActionAPI(context);
}

// static
bool BraveActionAPI::ShowActionUI(
ExtensionFunction* extension_function,
const std::string& extension_id,
std::unique_ptr<int> window_id_param,
std::unique_ptr<std::string> ui_relative_path_param,
std::string* error) {
// Which browser should we send the action to
Browser* browser = nullptr;
// If the windowId is specified, find it. Otherwise get the active
// window for the profile.
if (!window_id_param.get()) {
browser = ChromeExtensionFunctionDetails(extension_function)
.GetCurrentBrowser();
if (!browser) {
*error = tabs_constants::kNoCurrentWindowError;
return false;
}
} else {
int window_id = *window_id_param;
std::string get_browser_error;
if (!windows_util::GetBrowserFromWindowID(
extension_function,
window_id,
WindowController::GetAllWindowFilter(),
&browser,
&get_browser_error)) {
*error = get_browser_error;
return false;
}
}
return ShowActionUI(browser, extension_id, std::move(ui_relative_path_param),
error);
}

// static
bool BraveActionAPI::ShowActionUI(
Browser* browser,
const std::string& extension_id,
std::unique_ptr<std::string> ui_relative_path_param,
std::string* error) {
bool did_notify = BraveActionAPI::Get(browser)->NotifyObservers(extension_id,
std::move(ui_relative_path_param));
if (!did_notify) {
*error = "No toolbar is registered to observe BraveActionUI "
"calls for this window";
return false;
}
return true;
}

BraveActionAPI::BraveActionAPI() {}

BraveActionAPI::~BraveActionAPI() {
}

void BraveActionAPI::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}

void BraveActionAPI::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}

bool BraveActionAPI::NotifyObservers(const std::string& extension_id,
std::unique_ptr<std::string> ui_relative_path_param) {
bool did_notify = false;
for (auto& observer : observers_) {
observer.OnBraveActionShouldTrigger(extension_id,
std::move(ui_relative_path_param));
did_notify = true;
}
return did_notify;
}
} // namespace extensions
62 changes: 62 additions & 0 deletions browser/extensions/api/brave_action_api.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright (c) 2019 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// you can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef BRAVE_BROWSER_EXTENSIONS_API_BRAVE_ACTION_API_H_
#define BRAVE_BROWSER_EXTENSIONS_API_BRAVE_ACTION_API_H_

#include <memory>
#include <string>

#include "components/keyed_service/core/keyed_service.h"
#include "extensions/browser/extension_function.h"
#include "extensions/common/extension.h"

class Browser;

namespace extensions {
class BraveActionAPI : public KeyedService {
public:
class Observer {
public:
Observer();
virtual void OnBraveActionShouldTrigger(
const std::string& extension_id,
std::unique_ptr<std::string> ui_relative_path) = 0;

protected:
virtual ~Observer();
};

static BraveActionAPI* Get(Browser* context);
static bool ShowActionUI(
ExtensionFunction* extension_function,
const std::string& extension_id,
std::unique_ptr<int> window_id,
std::unique_ptr<std::string> ui_relative_path,
std::string* error);
static bool ShowActionUI(
Browser* browser,
const std::string& extension_id,
std::unique_ptr<std::string> ui_relative_path,
std::string* error);
BraveActionAPI();
~BraveActionAPI() override;

// Add or remove observers.
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);

protected:
bool NotifyObservers(const std::string& extension_id,
std::unique_ptr<std::string> ui_relative_path_param);

private:
base::ObserverList<Observer>::Unchecked observers_;

DISALLOW_COPY_AND_ASSIGN(BraveActionAPI);
};
} // namespace extensions

#endif // BRAVE_BROWSER_EXTENSIONS_API_BRAVE_ACTION_API_H_
22 changes: 22 additions & 0 deletions browser/extensions/api/brave_rewards_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@
#include "base/bind.h"
#include "base/strings/string_number_conversions.h"
#include "brave/browser/brave_rewards/tip_dialog.h"
#include "brave/browser/extensions/api/brave_action_api.h"
#include "brave/common/extensions/api/brave_rewards.h"
#include "brave/common/extensions/extension_constants.h"
#include "brave/components/brave_ads/browser/ads_service.h"
#include "brave/components/brave_ads/browser/ads_service_factory.h"
#include "brave/components/brave_rewards/browser/rewards_service.h"
#include "brave/components/brave_rewards/browser/rewards_service_factory.h"
#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
#include "chrome/browser/extensions/chrome_extension_function_details.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/web_contents.h"
Expand Down Expand Up @@ -53,6 +56,25 @@ ExtensionFunction::ResponseAction BraveRewardsCreateWalletFunction::Run() {
return RespondNow(NoArguments());
}

BraveRewardsOpenBrowserActionUIFunction::
~BraveRewardsOpenBrowserActionUIFunction() {
}

ExtensionFunction::ResponseAction
BraveRewardsOpenBrowserActionUIFunction::Run() {
std::unique_ptr<brave_rewards::OpenBrowserActionUI::Params> params(
brave_rewards::OpenBrowserActionUI::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());
std::string error;
if (!BraveActionAPI::ShowActionUI(this,
brave_rewards_extension_id,
std::move(params->window_id),
std::move(params->relative_path), &error)) {
return RespondNow(Error(error));
}
return RespondNow(NoArguments());
}

BraveRewardsTipSiteFunction::~BraveRewardsTipSiteFunction() {
}

Expand Down
11 changes: 11 additions & 0 deletions browser/extensions/api/brave_rewards_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ class BraveRewardsCreateWalletFunction : public ExtensionFunction {
void OnCreateWallet(int32_t result);
};

class BraveRewardsOpenBrowserActionUIFunction :
public ExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("braveRewards.openBrowserActionUI", UNKNOWN)

protected:
~BraveRewardsOpenBrowserActionUIFunction() override;

ResponseAction Run() override;
};

class BraveRewardsTipSiteFunction : public ExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("braveRewards.tipSite", UNKNOWN)
Expand Down
21 changes: 21 additions & 0 deletions browser/extensions/api/brave_shields_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <utility>

#include "base/strings/string_number_conversions.h"
#include "brave/browser/extensions/api/brave_action_api.h"
#include "brave/common/extensions/api/brave_shields.h"
#include "brave/common/extensions/extension_constants.h"
#include "brave/components/brave_shields/browser/brave_shields_p3a.h"
Expand All @@ -18,6 +19,7 @@
#include "brave/components/brave_shields/common/brave_shield_constants.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
#include "chrome/browser/extensions/chrome_extension_function_details.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/web_contents.h"
Expand Down Expand Up @@ -58,6 +60,25 @@ ExtensionFunction::ResponseAction BraveShieldsAllowScriptsOnceFunction::Run() {
return RespondNow(NoArguments());
}

BraveShieldsOpenBrowserActionUIFunction::
~BraveShieldsOpenBrowserActionUIFunction() {
}

ExtensionFunction::ResponseAction
BraveShieldsOpenBrowserActionUIFunction::Run() {
std::unique_ptr<brave_shields::OpenBrowserActionUI::Params> params(
brave_shields::OpenBrowserActionUI::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());
std::string error;
if (!BraveActionAPI::ShowActionUI(this,
brave_extension_id,
std::move(params->window_id),
std::move(params->relative_path), &error)) {
return RespondNow(Error(error));
}
return RespondNow(NoArguments());
}

ExtensionFunction::ResponseAction
BraveShieldsSetBraveShieldsEnabledFunction::Run() {
std::unique_ptr<brave_shields::SetBraveShieldsEnabled::Params> params(
Expand Down
11 changes: 11 additions & 0 deletions browser/extensions/api/brave_shields_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ class BraveShieldsAllowScriptsOnceFunction : public ExtensionFunction {
ResponseAction Run() override;
};

class BraveShieldsOpenBrowserActionUIFunction :
public ExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("braveShields.openBrowserActionUI", UNKNOWN)

protected:
~BraveShieldsOpenBrowserActionUIFunction() override;

ResponseAction Run() override;
};

class BraveShieldsSetBraveShieldsEnabledFunction : public ExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("braveShields.setBraveShieldsEnabled", UNKNOWN)
Expand Down
1 change: 1 addition & 0 deletions browser/ui/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ source_set("ui") {
"//brave/browser/resources/extensions:resources",
"//brave/components/brave_extension:generated_resources",
"//brave/components/brave_extension:static_resources",
"//brave/browser/extensions",
"//chrome/browser/extensions",
"//extensions/browser",
]
Expand Down
7 changes: 7 additions & 0 deletions browser/ui/brave_actions/brave_action_view_controller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ ui::MenuModel* BraveActionViewController::GetContextMenu() {
return nullptr;
}

bool BraveActionViewController::ExecuteActionUI(
std::string relative_path) {
return TriggerPopupWithUrl(PopupShowAction::SHOW_POPUP,
extension()->GetResourceURL(relative_path),
true);
}

ExtensionActionViewController*
BraveActionViewController::GetPreferredPopupViewController() {
return this;
Expand Down
Loading

0 comments on commit 73e30e9

Please sign in to comment.