-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for monetization in iframes
- Loading branch information
Showing
7 changed files
with
319 additions
and
105 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/*********************************************************** | ||
* content_general.js runs both at the top level page (alongside content_origin.js), and | ||
* in iframes (alongside content_iframe.js); it handles forwarding web monetization events into | ||
* akita events. | ||
***********************************************************/ | ||
|
||
const NEW_PAYMENT_POINTER_CHECK_RATE_MS = 1000; // 1 seconds | ||
const PAYMENT_POINTER_VALIDATION_RATE_MS = 1000 * 60 * 60; // 1 hour | ||
|
||
/** | ||
* Content scripts can only see a "clean version" of the DOM, i.e. a version of the DOM without | ||
* properties which are added by JavaScript, such as document.monetization! | ||
* reference: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts#Content_script_environment | ||
* https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Sharing_objects_with_page_scripts | ||
* So we must inject some code into the JavaScript context of the current tab in order to | ||
* access the document.monetization object. We inject code using a script element: | ||
*/ | ||
const scriptEl = document.createElement('script'); | ||
scriptEl.text = ` | ||
if (document.monetization) { | ||
document.monetization.addEventListener('monetizationstart', (event) => { | ||
document.dispatchEvent(new CustomEvent('akita_monetizationstart', { detail: event.detail })); | ||
}); | ||
document.monetization.addEventListener('monetizationprogress', (event) => { | ||
document.dispatchEvent(new CustomEvent('akita_monetizationprogress', { detail: event.detail })); | ||
}); | ||
document.monetization.addEventListener('monetizationstop', (event) => { | ||
document.dispatchEvent(new CustomEvent('akita_monetizationstop', { detail: event.detail })); | ||
}); | ||
} | ||
`; | ||
document.body.appendChild(scriptEl); | ||
|
||
/** | ||
* Gets the payment pointer as a string from the monetization meta tag on the current page. | ||
* If there is no monetization meta tag on the page, null is returned. | ||
* | ||
* For more info on the monetization meta tag: | ||
* - https://webmonetization.org/docs/getting-started | ||
* - https://webmonetization.org/specification.html#meta-tags-set | ||
* | ||
* @return {string|null} The payment pointer on the current page, or null if no meta tag is present. | ||
*/ | ||
function getPaymentPointerFromPage() { | ||
const monetizationMetaTag = document.querySelector('meta[name="monetization"]'); | ||
|
||
if (monetizationMetaTag) { | ||
return monetizationMetaTag.content; | ||
} else { | ||
return null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/*********************************************************** | ||
* content_iframe.js runs inside of each iframe and tracks and notifies content_origin.js of | ||
* payment pointer changes in the iframe via the `webBrowser.runtime` message channel. Each | ||
* content_iframe.js in each iframe is assigned a uuid from content_origin.js via postMessage so | ||
* that content_origin.js can know which iframe the script is associated with when the script sends | ||
* messages. | ||
***********************************************************/ | ||
|
||
// window.isTopLevel is set to true in ./content_origin.js, which only runs in the top level page; | ||
// so if window.isTopLevel is not set to true, then this script is running in an iframe. All code | ||
// in content_iframe.js should be inside this if block. | ||
if (!window.isTopLevel) { | ||
|
||
let topLevelOrigin = null; | ||
let monetizationPaymentEvents = []; | ||
|
||
document.addEventListener('akita_monetizationprogress', (event) => { | ||
const monetizationPaymentEvent = event.detail; | ||
|
||
// Payment events may be received by the iframe before the top level page communicates the | ||
// topLevelOrigin to the iframe. However, since payment events are stored under the | ||
// topLevelOrigin, the topLevelOrigin needs to be received before payment events are saved. | ||
// Until a topLevelOrigin is received, payment events are put into an array for later storage. | ||
if (topLevelOrigin === null) { | ||
monetizationPaymentEvents.push(monetizationPaymentEvent); | ||
} else { | ||
storeDataIntoAkitaFormat(monetizationPaymentEvent, AKITA_DATA_TYPE.PAYMENT, topLevelOrigin); | ||
} | ||
}); | ||
|
||
|
||
// Listen for a message from the top level page (./content_origin.js). | ||
// The iframe expects to receive a uuid and origin from the top level page soon after the iframe | ||
// is created. | ||
window.addEventListener('message', (event) => { | ||
const data = event.data; | ||
|
||
if (data.uuid && data.topLevelOrigin) { | ||
// Set topLevelOrigin so that iframe monetization events can be stored under this origin | ||
// and store any payment events that occured before receiving the topLevelOrigin. | ||
topLevelOrigin = data.topLevelOrigin; | ||
for (const monetizationPaymentEvent of monetizationPaymentEvents) { | ||
storeDataIntoAkitaFormat(monetizationPaymentEvent, AKITA_DATA_TYPE.PAYMENT, topLevelOrigin); | ||
} | ||
monetizationPaymentEvents = []; | ||
|
||
const iframeUuid = data.uuid; | ||
// Let the top level page know that the iframe has receieved the uuid | ||
webBrowser.runtime.sendMessage({ | ||
iframeReceivedUuid: { | ||
iframeUuid: iframeUuid | ||
} | ||
}); | ||
trackIframePaymentPointer(iframeUuid); | ||
} | ||
}); | ||
|
||
|
||
/** | ||
* Regularly check for payment pointer changes in the iframe. If the payment pointer changes, | ||
* the top level page (./content_origin.js) is notified via ./background_script.js, which forwards | ||
* the `webBrowser.runtime.sendMessage` iframePaymentPointerChange message. | ||
* | ||
* @param {string} iframeUuid the uuid given to the iframe by top level page. | ||
*/ | ||
function trackIframePaymentPointer(iframeUuid) { | ||
let cachedPaymentPointer = null; | ||
|
||
setInterval(async () => { | ||
const paymentPointerInIframe = getPaymentPointerFromPage(); | ||
|
||
// If the payment pointer in iframe changes, send new payment pointer to content_origin.js | ||
if (paymentPointerInIframe !== cachedPaymentPointer) { | ||
webBrowser.runtime.sendMessage({ | ||
iframePaymentPointerChange: { | ||
iframeUuid: iframeUuid, | ||
paymentPointer: paymentPointerInIframe | ||
} | ||
}); | ||
cachedPaymentPointer = paymentPointerInIframe; | ||
} | ||
}, NEW_PAYMENT_POINTER_CHECK_RATE_MS); | ||
} | ||
} |
Oops, something went wrong.