Skip to content
This repository has been archived by the owner on Oct 18, 2023. It is now read-only.

feat(coil-extension): add iframes impl #415

Merged
merged 33 commits into from Mar 5, 2020
Merged

Conversation

sublimator
Copy link
Member

@sublimator sublimator commented Feb 27, 2020

This pull request implements Web Monetization inside <iframe> elements. Only iframes that are direct children of the top frame, with monetization in their allow feature set policy will be monetized.

It does this by changing the 1:1 mapping of WM streams to browser tabs to M:1, with the 1:1 mapping now of WM streams to iframes.

To do so, it takes advantage of:

  1. webNavigation wext apis
    • New BackgroundFrameService
      • Keeps track of frames, including the current readyState/url/parentFrameId etc.
      • use frameChanged event where using chrome.tabs.onUpdated before (which was problematic)
    • Will require extra permissions which may be off-putting to some users
      • This will not actually give us any more private information than we already have. This may not be clear to all users though.
  2. MessageSender['frameId'], MessageSender['tab'] attributes
    Messages sent using chrome.runtime.sendMessage api from content scripts
    will be recieved in the background script onMessage listener with
    tabId/frameId implied (i.e. not inside the message payload).

Whenever a monetization request occurs inside an iframe (that is only one level deep), a request will be sent to the background page to check if that iframe is allowed.

The request handler in the background page consults the BackgroundFramesService to determine the parent frame of the sender (currently always 0) then sends a message to the associated content script via tabs.sendMessage(tabId, msg, {frameId: parentId}). The msg contains the {tabId, frameId} monetization is requested from.

  • Inside the parent frame content script, a WeakMap of iframe elements to Promise<{tabId, frameId}> is maintained as well as a promise queue via a Map of correlationId to promise resolver.
  • A linear search is done over a query of all iframes, consulting the WeakMap, queuing new promises where necessary, awaiting each promise in turn. The promise result frame is checked against the search element frame.
    • All <iframe>s in the page that have no promise queued already are sent a correlationId via frame.contentWindow!.postMessage
    • Concurrent searches initiated via multiple child frames requesting monetization are handled fine using this method
  • Content scripts listen for the correlationId via adding listeners to the window message event
    • This is then reported to the background page, which looks up the parent frame of the sender and reports the correlationId/frame pair and the correlationId associated promise is resolved with the frame.
    • It seems highly unlikely that nefarious actors could listen in on the event, replaying the message to another window and beat the content script listener to use the correlationId.
      • We could take extra measures from the background page to ensure ids are only ever used once but it does not really seem necessary.
        • unknown correlationIds are ignored

Questions:

  • Should we limit the amount of concurrently monetized frames ?

    • If so, how ?
  • Do we need to do anything special client side to adjust bandwidth? Or will the server do it all ?

    • Is there anything needed server side to optimally support this ?
      • Seems to be some (transient) errors when a bunch of streams connect at the same time image
      • it seems the retry logic handles them though
  • Should we allow arbitrarily nested iframes to monetize as long as long as all iframes in the iframe hierarchy have specified allow="monetization" ?

  • What does the text in the popup mean now that 'content' could refer to inner frames as well as the top page ?

  • Should there be some kind of 'in-page' indication that an iframe is being monetized ? (@fruehle offhand suggested maybe some kind of WM logo watermark)

MacOS Chrome 80.0.3987.132 (Official Build) (64-bit)

  • Build for prod with release settings

    • e.g. yarn build-prod chrome -p --run-prod --devtool=none
  • Import unpacked/temporary extension/add-on

    • For Firefox, go to about:debugging
      • Enable add-on debugging
      • Load Temporary Add-on...
    • For Chrome or MS Edge, go to chrome://extensions (or edge://extensions) and Load Unpacked
  • Ensure that you are logged in with a user with valid subscription

    • image
  • example.com should say "This website is not supported"

    • image
  • xrpcommunity.blog should monetize

    • image
  • twitch.tv works

    • image
  • monetized youtube video

    • image
  • Coil welcome and welcome to explore pages

    • Go to coil.com, the browser action popup should show welcome to coil
      • image
    • Should have a link to coil.com/explore page
    • Once on explore page should show Start Exploring with a rocket-ship graphic
      • image
  • Check the monetization animation works properly

    • image
    • Only required on desktop browsers
    • Should animate when monetized and packets received
    • Should stop animation when network disconnected
      • Note that on Firefox/MacOS the popup automatically closes when the
        tab loses focus so can use something like this in terminal:
        • sudo sleep 10 && sudo ifconfig en0 down && sleep 10 && sudo ifconfig en0 up
  • Check monetization works consistently

  • Will route to $coildomain.com/login rather than open popup if logged out

    • Log out from $coildomain.com
    • Check that icon is in 'unavailable' state
      • image
    • Click on browser action
    • Check that routed to login page
      • image
  • Popup icon should show if page is monetized even when logged out

    • Log out from extension
    • Go to a monetized page and check that the icon "monetized" black and in 'unavailable' state
      • image
  • Run the puppeteer tests (look at the circle jobs)

    • export BROWSER_TYPE='chrome' # or 'firefox'
    • logout test currently fails on Firefox due to puppeteer-firefox limitations
  • Go to a youtube video,
    manually skip to near end of video, and when autoplay of a video from
    another channel starts, check that monetization has stopped.

  • Go to xrpcommunity.blog and as page
    is loading very quickly open the popup.
    It should show "This page is Web-Monetized" even before streaming
    starts. Should show 'setting up payment' then 'coil is paying creator'
    #120

  • Open the reloading-every-15s.html file:

    • Use a localhost server so WM works (e.g. with python -m http.server 4000)
    • Open the developer tools console undocked so can view while PAGE IS BACKGROUNDED
      • Note the Reloading page logging
    • Open the extension background page developer tools and look at the stream logging
    • SHOULD NOT initiate a stream or SPSP request
      #144
  • Open the event-logger.html file:

    • Use a localhost server so WM works (e.g. with python -m http.server 4000)

    • Look for unusual timings, check that pending state is emitted nearly
      immediately after page load or meta tag added

      • TESTERS NOTE (resolved):
        • image
        • is this a performance regression? if so, is it acceptable ?
        • Normal time-to-pending on master: image
        • NOTE: this was simply due to having devtools open
    • Issue: #63

    • Fix PR: #69

  • Check started event fires when quickly switching between tabs

    • Open the event-logger.html file
    • Switch to another (non-monetized) tab. The payments stop. Quickly switch back to the first tab.
    • The payments restart. Make sure there is a monetizationstart event
    • Issue: #105
    • Fix PR: #117
  • Check stopped event fires with correct requestId

    • Open the event-logger.html file
    • Induce a stop/start in same js 'tick'
      • TESTERS NOTE
        • image
        • FIXED
    • Check that the stopped event has the correct requestId
    • Issue: #127
    • Fix PR: #128
  • Run a local web server (e.g. with python -m http.server 4000) serving
    the dist folder, then open static/popup.html in a
    normal tab and check the popup rendering in various states.

  • On MacOS Chromium browsers (Chrome/Edge) check that the monetized animation is working
    on non primary monitors.

  • Check that popup closes when another window is focused

    • Open two Browser windows, open the popup in one window

    • Focus on second window

    • Ensure that popup is closed

    • Issue: #313

    • Fix PR: #332

  • Check SPA apps keep streaming when url changed, meta stays

    • Go to e.g. https://www.wevolver.com/

    • Change other pages which uses HTML5 history.pushState

    • Check that streaming is maintained throughout

      • if not, use browser devtools to check if meta exists
        • document.head.querySelector('meta[name="monetization"]')
    • Issue: #507

    • Fix PR: #508

@@ -31,5 +31,5 @@
"run_at": "document_start"
}
],
"permissions": [ "activeTab", "storage", "notifications", "tabs", "<all_urls>" ]
"permissions": [ "webNavigation", "activeTab", "storage", "notifications", "tabs", "<all_urls>" ]
Copy link
Member Author

Choose a reason for hiding this comment

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

This is going to require extra permissions

Copy link
Member Author

Choose a reason for hiding this comment

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

Nothing more revealing (in terms of privacy) than what we already require but it could be off-putting to some users.

@sublimator sublimator force-pushed the nd-iframes-2020-02-27 branch 3 times, most recently from a83c0b2 to 07757e4 Compare February 27, 2020 06:10
this.runtime.sendMessage(message, resolve)
})
if (!allowed) {
console.error(
Copy link
Member Author

Choose a reason for hiding this comment

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

It might be nicer to log the actual element from the parent frame content script

@sublimator sublimator force-pushed the nd-iframes-2020-02-27 branch 2 times, most recently from b9cc3e6 to dc408a9 Compare March 3, 2020 03:16
@@ -4,7 +4,7 @@ jobs:
# Run Linting
lint-all:
docker:
- image: circleci/node:12.14.1-buster-browsers
- image: circleci/node:12.16.1-buster-browsers
Copy link
Member Author

@sublimator sublimator Mar 3, 2020

Choose a reason for hiding this comment

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

Just hail Mary attempts at fixing some tests that fail on CI and not locally on my dev machine.

Copy link

@sharafian sharafian left a comment

Choose a reason for hiding this comment

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

Have tried it out and it looks good! I think we should merge this and roll out a v47 with this and the tipping beta. The one issue I noticed is that mutating the allow attribute of an iframe doesn't get picked up by the extension, but we can always fix that at a later time if we decide that's supported

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

Successfully merging this pull request may close these issues.

None yet

2 participants