Skip to content

Commit

Permalink
Publisher Ads: inject ads to googletag slots
Browse files Browse the repository at this point in the history
  • Loading branch information
petemill committed Jan 30, 2020
1 parent 744a8ee commit dadcde2
Show file tree
Hide file tree
Showing 11 changed files with 201 additions and 42 deletions.
9 changes: 9 additions & 0 deletions Greaselion.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,14 @@
"scripts": [
"publisher-ads/wsjgroup.bundle.js"
]
},
{
"urls": [
"http://*/*",
"https://*/*"
],
"scripts": [
"publisher-ads/gpt-site.bundle.js"
]
}
]
7 changes: 1 addition & 6 deletions common/contentScript/inject-to-document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,5 @@ export default function injectToDocument (isolatedFn: Function, ...codeVars: any
scriptEl.remove()
})
}

if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', inject)
} else {
inject()
}
inject()
}
5 changes: 4 additions & 1 deletion common/pageLifecycle/run-on-url-change.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// you can obtain one at http://mozilla.org/MPL/2.0/.

import injectToDocument from '../contentScript/inject-to-document'
import runOnPageLoaded from './run-on-loaded'

const customEventName = 'brave-url-changed'

Expand All @@ -29,5 +30,7 @@ export default function runOnUrlChange(fn: Function) {
return prevReplaceState.call(this, ...args)
}
}
injectToDocument(fnPageInjectionCode, customEventName)
runOnPageLoaded(() =>
injectToDocument(fnPageInjectionCode, customEventName)
)
}
6 changes: 4 additions & 2 deletions common/pageLifecycle/wait-for-window-var.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// you can obtain one at http://mozilla.org/MPL/2.0/.

import injectToDocument from '../contentScript/inject-to-document'
import runOnPageLoaded from './run-on-loaded'

type OnValueFunction = (varValue: any) => void
type VarValueCustomEvent = CustomEvent<{ varValue: any}>
Expand Down Expand Up @@ -51,6 +52,7 @@ export default function waitForWindowVar(varName: string, onValue: OnValueFuncti
}
})
}

injectToDocument(fnPageInjectionCode, varName, customEventName)
runOnPageLoaded(() =>
injectToDocument(fnPageInjectionCode, varName, customEventName)
)
}
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"webpack-cli": "^3.3.10"
},
"dependencies": {
"@types/chrome": "0.0.93"
"@types/chrome": "0.0.93",
"@types/doubleclick-gpt": "^2019041801.0.2"
}
}
139 changes: 139 additions & 0 deletions publisher-ads/gpt-site.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// Copyright (c) 2020 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/.

import injectToDocument from '../common/contentScript/inject-to-document'
import BATAd from './bat-ad'
import fetchAdCreatives from './creativeFetch/same-context'
import { AdSize, stringToAdSize } from './'
import runOnPageLoaded from '../common/pageLifecycle/run-on-loaded'

console.log("content script")
type SlotDefinition = {
adUnitPath: string,
size: googletag.GeneralSize
elementId?: string
}

type PageSlotsEventData = { pageSlots: SlotDefinition[] }

const eventName = `brave-gpt-slots-ready`

function googleSizeToAdSize (sizeParam: googletag.GeneralSize): AdSize[] | null {
if (typeof sizeParam === 'string') {
const size = stringToAdSize(sizeParam)
if (!size) {
return []
}
return [ size ]
} else if (Array.isArray(sizeParam)) {
if (sizeParam.every((i: any) => Array.isArray(i))) {
return sizeParam as AdSize[]
}
if (sizeParam.length === 2 && sizeParam.every((i: any) => typeof i === 'number')) {
return [sizeParam as AdSize]
}
if (sizeParam.every((i: any) => typeof i === 'string')) {
const stringSizes: string[] = sizeParam as string[]
const adSizes: AdSize[] = stringSizes
.map(stringToAdSize)
.filter((size: AdSize | null) => size !== null) as AdSize[]
return adSizes
}
}
// TODO(petemill): There seem to be more types that googletag.GeneralSize can be that
// we are not handling here. Implement conversions as we encounter those types.
return null
}

function elementIdFromAdUnitPath (adUnitPath: string): string {
const segments = adUnitPath.split('/')
return segments[segments.length - 1]
}

// Content script receives information about the ads from the Page
window.addEventListener(eventName, function (e: CustomEvent<PageSlotsEventData>) {
console.log('Content script received page slots', e.detail.pageSlots)
const { detail: { pageSlots }} = e
runOnPageLoaded(function () {
for (const slot of pageSlots) {
const sizes = googleSizeToAdSize(slot.size)
if (!sizes) {
console.error('Brave Publisher Ads: could not find sizes for slot', slot)
continue
}
const selector = slot.elementId || elementIdFromAdUnitPath(slot.adUnitPath)
const element = document.querySelector<HTMLElement>(`#${selector}`)
if (!element) {
console.error('Brave Publisher Ads: could not find element for slot', slot)
continue
}
new BATAd(element, fetchAdCreatives).sizes = sizes
}
})
})

// Page script sends information about the ads to the Content Script
injectToDocument(function ($eventName: string) {
console.log('inject')
function observeGoogleTag(googletag: googletag.Googletag) {
// @ts-ignore
googletag.__isObserved = true
console.log('observe')
// Page usually defines googletag before this script runs,
// then the stub adds the api functions, usually after this script runs.
// If we simple define the functions then they will be
// overwritten.
// Instead, Proxy them.
let actualDefineSlots = googletag.defineSlot
let actualEnableServices = googletag.enableServices
const pageSlots: SlotDefinition[] = []

googletag.defineSlot = function (adUnitPath, size, elementId) {
pageSlots.push({ adUnitPath, size, elementId })
return actualDefineSlots(adUnitPath, size)
}

googletag.enableServices = function () {
window.dispatchEvent(new CustomEvent<PageSlotsEventData>($eventName, {
detail: {
pageSlots
},
bubbles: true
}))
actualEnableServices()
}

return new Proxy(googletag, {
set (googletag, propName, value) {
if (propName === 'defineSlot') {
actualDefineSlots = value
} else if (propName === 'enableServices') {
actualEnableServices = value
} else {
googletag[propName] = value
}
return true
}
})
}

if (window['googletag']) {
googletag = observeGoogleTag(googletag)
} else {
console.log('googletag was not defined')
// Maybe the Page didn't define it, so wait in case it gets defined.
let _value: googletag.Googletag
Object.defineProperty(window, 'googletag', {
configurable: true,
set: function (value) {
console.log("googletag = set")
_value = (value.__isObserved ? value : observeGoogleTag(value))
},
get: function () {
return _value
}
})
}
}, eventName)
15 changes: 14 additions & 1 deletion publisher-ads/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
export type AdSize = [number, number]
// Copyright (c) 2020 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/.

export type AdSize = [number, number]

export function stringToAdSize (sizeString: string): AdSize | null {
const sizeData = sizeString.split('x')
if (sizeData.length == 2) {
return [Number(sizeData[0]), Number(sizeData[1])]
}
return null
}
29 changes: 10 additions & 19 deletions publisher-ads/marketwatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,19 @@ import targetAdSlotsBySelector from './slotTargeting/by-selector'
import fetchAdCreatives from './creativeFetch/same-context'
import runOnPageLoaded from '../common/pageLifecycle/run-on-loaded'
import { GetAdSizesFunction } from './slotTargeting/'
import { AdSize } from './'
import { AdSize, stringToAdSize } from './'

function stringToAdSize (sizeString: string): AdSize | null {
const sizeData = sizeString.split('x')
if (sizeData.length == 2) {
return [Number(sizeData[0]), Number(sizeData[1])]
const getSizeForMarketWatchAdSlot: GetAdSizesFunction = (element: HTMLElement) => {
const sizeData = element.getAttribute('data-size')
if (sizeData) {
const sizesByString: AdSize[] = sizeData.split(',')
.map(stringToAdSize)
.filter(item => item) as AdSize[]
return sizesByString
}
return null
return []
}


runOnPageLoaded(function () {
const getMarketWatchAdSizes: GetAdSizesFunction = (element: HTMLElement) => {
const sizeData = element.getAttribute('data-size')
if (sizeData) {
const sizesByString: AdSize[] = sizeData.split(',')
.map(stringToAdSize)
.filter(item => item) as AdSize[]
return sizesByString
}
return []
}

targetAdSlotsBySelector('.ad', getMarketWatchAdSizes, fetchAdCreatives)
targetAdSlotsBySelector('.ad', getSizeForMarketWatchAdSlot, fetchAdCreatives)
})
24 changes: 12 additions & 12 deletions publisher-ads/wsjgroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ import targetAdSlotsBySelector from './slotTargeting/by-selector'
import fetchAdCreatives from './creativeFetch/same-context'
import runOnPageLoaded from '../common/pageLifecycle/run-on-loaded'

runOnPageLoaded(function () {
function getSizeForAdSlot (element: HTMLElement) {
const adOptionsString = element.getAttribute('data-ad-options')
if (!adOptionsString) {
return null
}
const adOptions = JSON.parse(adOptionsString)
if (!adOptions) {
return null
}
return adOptions.adSize
function getSizeForWSJAdSlot (element: HTMLElement) {
const adOptionsString = element.getAttribute('data-ad-options')
if (!adOptionsString) {
return null
}
const adOptions = JSON.parse(adOptionsString)
if (!adOptions) {
return null
}
return adOptions.adSize
}

targetAdSlotsBySelector('[data-ad-options]', getSizeForAdSlot, fetchAdCreatives)
runOnPageLoaded(function () {
targetAdSlotsBySelector('[data-ad-options]', getSizeForWSJAdSlot, fetchAdCreatives)
})
1 change: 1 addition & 0 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module.exports = (env, argv) => ({
['publisher-ads/wsjgroup']: './publisher-ads/wsjgroup',
['publisher-ads/washingtonpost']: './publisher-ads/washingtonpost',
['publisher-ads/marketwatch']: './publisher-ads/marketwatch',
['publisher-ads/gpt-site']: './publisher-ads/gpt-site'
},
plugins: [
new CleanWebpackPlugin(),
Expand Down

0 comments on commit dadcde2

Please sign in to comment.