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

Ensure that external domains can only open one tab at once #8147

Closed
wants to merge 5 commits into from

Conversation

rekmarks
Copy link
Member

@rekmarks rekmarks commented Mar 1, 2020

Summary

  • Ensure that external domains can only cause the creation of a single tab at any one time
    • The only case I could find of this was in the permission controller's requestUserApproval
  • Rename MetamaskTabsIDs to MetamaskTabIds, for great benefit to consistency and style
  • Note: The single instance I've identified where the motivating problem occurs should be eliminated by Prevent external domains from submitting more than one perm request at once #8148, but this may nevertheless be worthwhile.

Details

The requestUserApproval function for permissions requests opens the extension in a new browser tab. Currently, there is no limit to the number of tabs that an external origin can open by submitting permissions requests.

This PR establishes a "tab creation history" to track which origins have (effectively) created an open tab. If an origin already has an open tab and makes a new tab-creating request, no new tab will be created. When the tab created by an origin is closed, the origin is removed from the tab creation history, and can once again create a new tab.

The need for this was exposed when I left a faulty test dapp open in Chrome, and it spawned 10 tabs, like so:
image

Misc.

The behavior of that test dapps still creates, over time, an unbounded number of pending permission requests for the same origin, and we should consider implementing a similar logic for permissions requests (e.g., only one pending request at a time), but that would be a separate PR. For that, see: #8148

@rekmarks rekmarks changed the title Ensure that websites can only open one tab at once Ensure that external domains can only open one tab at once Mar 1, 2020

class ExtensionPlatform {

constructor () {

if (extension.tabs) {
Copy link
Member Author

Choose a reason for hiding this comment

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

I had to add this conditional to get the integration tests to pass.

If anyone can tell me how to mock this value, please let me know, and I'll do so.

@metamaskbot
Copy link
Collaborator

Builds ready [ddca6b1]
Page Load Metrics (587 ± 55 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeNotificationfirstPaint339948199
domContentLoaded33068158511656
load33268358711655
domInteractive33068158511656

@metamaskbot
Copy link
Collaborator

Builds ready [374f97f]
Page Load Metrics (584 ± 42 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeNotificationfirstPaint338240105
domContentLoaded3306955838842
load3326975848842
domInteractive3306955838842

@Gudahtt
Copy link
Member

Gudahtt commented Mar 6, 2020

Hmm.... how would this handle the case where the user used a tab opened by us to navigate away? 🤔 e.g. if I used the connect flow tab to google something. I think that'd just break any subsequent interactions with that dapp.

@Gudahtt
Copy link
Member

Gudahtt commented Mar 6, 2020

Maybe we could use openMetamaskTabIds to keep track of that case 🤔

There is a current bug with openMetamaskTabIds that I've been meaning to investigate though; there are cases where that gets "stuck" too, leading to the popup never opening (you need to click the browser-action button instead to see confirmations). This is probably what's behind #7292 and #7051. Happens all the time in development. So we'd probably want to fix that bug before relying on openMetamaskTabIds any further.

@rekmarks
Copy link
Member Author

rekmarks commented Mar 6, 2020

@Gudahtt

We should definitely resolve the openMetaMaskTabIds problem regardless, but getting this down for future reference:

If the user navigates to a different site in a MetaMask UI tab, that ought to destroy that UI instance, and cause stream teardowns to fire. Could we get the origin on the UI stream teardowns and handle it at that time?

Not sure how it would work if the user goes back to the MetaMask UI in such a tab. Would that be problematic?

@Gudahtt
Copy link
Member

Gudahtt commented Mar 6, 2020

Could we get the origin on the UI stream teardowns and handle it at that time?

Yep! I'm fairly certain we can anyway. The endOfStream callback we use to delete the tab from openMetaMaskTabIds is in-scope of the initial connectRemote function, which has the remotePort parameter. remotePort.sender contains the origin.

Not sure how it would work if the user goes back to the MetaMask UI in such a tab. Would that be problematic?

I think that would be fine - if the user navigates back, we can treat it as a normal MetaMask fullscreen tab. By that time the connection to the dapp will have been severed, and the dapp can still initiate new screens.

@Gudahtt Gudahtt added the DO-NOT-MERGE Pull requests that should not be merged label Mar 13, 2020
@Gudahtt
Copy link
Member

Gudahtt commented Mar 13, 2020

^ I'm just gonna mark this as DONOTMERGE for now because I keep opening this PR to review, forgetting that it has changes pending 🤦‍♂️

@Gudahtt
Copy link
Member

Gudahtt commented Apr 9, 2020

This should be unblocked now that #8314 has been merged

@metamaskbot
Copy link
Collaborator

Builds ready [3c01867]
Page Load Metrics (646 ± 15 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeNotificationfirstPaint34473942
domContentLoaded6027146443115
load6047166463115
domInteractive6027146443115

@Gudahtt Gudahtt removed the DO-NOT-MERGE Pull requests that should not be merged label Apr 10, 2020
Comment on lines +496 to +499

/**
* Set up listeners for managing tab creation.
*/
function setupBrowserListeners () {
extension.tabs.onRemoved.addListener(handleTabRemoved)
}
Copy link
Member Author

Choose a reason for hiding this comment

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

Given the handling in endOfStream above (L390), do we even need this?

@rekmarks
Copy link
Member Author

@Gudahtt rebased and updated per previous discussions on this PR. One open question re: a possible simplification.

@metamaskbot
Copy link
Collaborator

Builds ready [76be4db]
Page Load Metrics (587 ± 56 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeNotificationfirstPaint349541136
domContentLoaded34270058611756
load34670258711656
domInteractive34270058511756

@metamaskbot
Copy link
Collaborator

Builds ready [50ad55f]

@metamaskbot
Copy link
Collaborator

Builds ready [154d500]

@rekmarks rekmarks requested a review from a team as a code owner April 26, 2020 04:20
@metamaskbot
Copy link
Collaborator

Builds ready [d63acc7]

@whymarrh
Copy link
Contributor

whymarrh commented Jul 7, 2020

We can come back to this at some point

@whymarrh whymarrh closed this Jul 7, 2020
@whymarrh whymarrh deleted the only-one-tab branch August 20, 2020 14:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants