Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API to open Shields and Rewards dialog UI #3664

Merged
merged 1 commit into from
Oct 25, 2019

Conversation

petemill
Copy link
Member

@petemill petemill commented Oct 10, 2019

Any uplifts should also include:

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

Security / Permissions

The use of the target extension ID coming from the ExtensionFunction means that only contexts which are granted access to an extension's overall API will have permission to open that extension's popup from code.

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.

Fix brave/brave-browser#6419

Submitter Checklist:

Reviewer Checklist:

  • New files have MPL-2.0 license header.
  • Request a security/privacy review as needed.
  • Adequate test coverage exists to prevent regressions
  • Verify test plan is specified in PR before merging to source

After-merge Checklist:

  • The associated issue milestone is set to the smallest version that the
    changes has landed on.
  • All relevant documentation has been updated.

@petemill petemill self-assigned this Oct 10, 2019
@petemill petemill force-pushed the trigger-internal-extension-popup branch from 2111317 to 1ad4d45 Compare October 10, 2019 22:08
ryanml
ryanml previously requested changes Oct 11, 2019
Copy link
Contributor

@ryanml ryanml left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check CI for build/browser test errors

@petemill
Copy link
Member Author

I think the problem here is a dependency violation in calling code in brave/browser/ui from brave/browser/extensions.
We should probably turn the dependency around and have an observer or delegate in brave/browser/extensions that the browser/ui/BraveActionViewController calls.

Comment on lines 13 to 16
#include "brave/browser/ui/views/brave_actions/brave_actions_container.h"
#include "brave/browser/ui/views/location_bar/brave_location_bar_view.h"
#include "brave/browser/ui/views/toolbar/brave_toolbar_view.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed in DM, these views header files should not be included here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed, now using an obserer pattern

@ryanml ryanml mentioned this pull request Oct 15, 2019
32 tasks
@petemill petemill mentioned this pull request Oct 17, 2019
32 tasks
@petemill petemill force-pushed the trigger-internal-extension-popup branch from 1ad4d45 to 1fc2597 Compare October 18, 2019 05:08
@petemill
Copy link
Member Author

petemill commented Oct 18, 2019

This is ready for review. Now using the Observer pattern, modeled on ExtensionActionAPI and ExtensionActionAPI::Observer. Interestingly enough there already is an API to open popups from an extension itself (we're not using that here since we want to open the popup from other contexts, specifying which extension to open). The pattern for that API is to store a reference on the BrowserWindow to the UI model which contains the ExtensionActions, and for the ExtensionActionAPI to go through the browser UI layer and call ExecuteAction directly. I much prefer the observer pattern here so that the UI project looks at the Extension project. Plus, it doesn't need a new patch or chromium_src override, which adding a property to BrowserWindow would require.

@petemill petemill force-pushed the trigger-internal-extension-popup branch from 1fc2597 to 9723cda Compare October 18, 2019 05:17
@petemill petemill requested a review from bridiver October 18, 2019 05:17
@petemill
Copy link
Member Author

One question concerning the BrowserContextKeyedAPIFactory, which I am using to get a singleton-per-profile instance. I have exposed one but haven't added a call inside browser_context_keyed_service_factories.cc. Is this neccessary since the service doesn't need to do anything outside of having a member function being explicitly called.

Likewise I haven't called DependsOn( anywhere for the factory instance, and not sure if that's neccessary.

Let me know if you have any thoughts @bridiver @simonhong.

@@ -53,6 +56,26 @@ ExtensionFunction::ResponseAction BraveRewardsCreateWalletFunction::Run() {
return RespondNow(NoArguments());
}

BraveRewardsOpenBrowserActionUIFunction::
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Not sure if this effects lint, but can you format definitions that need to break like: https://github.com/brave/brave-core/blob/9723cdad5bb75b84d6ccdaac55fbc1d137181fd7/browser/extensions/api/brave_rewards_api.cc#L955

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

) {
}

ExtensionFunction::ResponseAction BraveRewardsOpenBrowserActionUIFunction::Run(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

return;
}
if (actions_[extension_id].view_controller_) {
// auto view_controller = static_cast<BraveActionViewController*>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this comment be removed or does it indicate needed code?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the spot - removed

@petemill petemill force-pushed the trigger-internal-extension-popup branch from 9723cda to 31ba47a Compare October 18, 2019 06:04
}
}
// Don't support showing action popups in a popup window.
if (!browser->SupportsWindowFeature(Browser::FEATURE_TOOLBAR)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think using BrowserWindow::IsToolbarVisible() seems more robust.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I think I'll remove this part. It was there from the first implementation, where we called methods directly on the toolbar. Now, the subscriber for that Browser will either be there or it won't. Plus, the subscriber can decide to still show the popup even if the toolbar is hidden, like what happens in full screen windows when permission bubbles appear.

@simonhong
Copy link
Member

simonhong commented Oct 18, 2019

I think we don't need to have BraveActionAPI instance per profile and I can't find any reason why BraveActionAPI should be tied with Profile.
IMO, just one class that manage observer list seems sufficient.
and I think BraveBrowserProcessImpl seems fine as a owner of it because just one instance is fine for whole browser. WDYT? @petemill

return false;
}
// Pass event to correct observer
for (auto& observer : observers_) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about using Browser* and observers map?
I think it should be a little bit efficient than iterating all every time.

@@ -379,6 +379,24 @@
}
],
"functions": [
{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is a utility function that can be used from any internal extension it should go in it's own api config instead of being duplicated in rewards and shields

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as discussed, it's specific to each API (each API opens the UI related to that API)

@petemill
Copy link
Member Author

As discussed with @bridiver, we'll attempt to make a KeyedServiceFactory where the key is a Browser instance. That'll solve both of @simonhong 's valid points.

@petemill petemill force-pushed the trigger-internal-extension-popup branch 2 times, most recently from bdbf9a7 to c8ecffa Compare October 24, 2019 02:40
@petemill
Copy link
Member Author

petemill commented Oct 24, 2019

This is ready for re-review @simonhong @bridiver now that I've converted from BrowserContextKeyedService to a Browser-keyed-KeyedService. Changes are here: https://github.com/brave/brave-core/compare/bdbf9a7e6d6131c2b5ab12f80acb71c67e3dceba..trigger-internal-extension-popup

@petemill petemill force-pushed the trigger-internal-extension-popup branch from c8ecffa to c6921d9 Compare October 24, 2019 02:50
@@ -10,6 +10,7 @@
#include <memory>
#include <string>

#include "brave/browser/extensions/api/brave_action_api.h"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you might be missing a dep for this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@petemill petemill force-pushed the trigger-internal-extension-popup branch 2 times, most recently from 463fb09 to 76d045e Compare October 24, 2019 18:44
@petemill petemill force-pushed the trigger-internal-extension-popup branch 3 times, most recently from 202f3f3 to 194a65f Compare October 24, 2019 20:02
@petemill petemill requested a review from ryanml October 24, 2019 20:21
@petemill petemill dismissed ryanml’s stale review October 24, 2019 20:24

made changes requested

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.
@petemill petemill force-pushed the trigger-internal-extension-popup branch from 194a65f to 73e30e9 Compare October 24, 2019 21:18
Copy link
Contributor

@NejcZdovc NejcZdovc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rewards code looks good

@petemill petemill merged commit cff1e51 into master Oct 25, 2019
@petemill
Copy link
Member Author

CI still shows all tests passed even though ultimate status is the grey circle. Merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Open Shields and Rewards popups from code
5 participants