Index: chrome/renderer/resources/extensions/ad_view.js |
diff --git a/chrome/renderer/resources/extensions/ad_view.js b/chrome/renderer/resources/extensions/ad_view.js |
index 84c7008857f99766613f4e24c4db4295e4827322..66dd4a6a8f38bd9f77855485f6b7380709be3cb1 100644 |
--- a/chrome/renderer/resources/extensions/ad_view.js |
+++ b/chrome/renderer/resources/extensions/ad_view.js |
@@ -1,4 +1,4 @@ |
-// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+// Copyright 2013 The Chromium Authors. All rights reserved. |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
@@ -15,99 +15,86 @@ var chrome = requireNative('chrome').GetChrome(); |
var forEach = require('utils').forEach; |
var watchForTag = require('tagWatcher').watchForTag; |
- |
+/** |
+ * Define "allowCustomAdNetworks" function such that it returns "true" if the |
+ * "adViewCustom" module was injected. This is so that the |
+ * "kEnableAdviewSrcAttribute" flag is respected. |
+ */ |
var allowCustomAdNetworks = (function(allow){ |
return function() { return Boolean(allow); } |
})(adViewCustom ? adViewCustom.enabled : false); |
- |
-// List of attribute names to "blindly" sync between <adview> tag and internal |
-// browser plugin. |
+/** |
+ * List of attribute names to "blindly" sync between <adview> tag and internal |
+ * browser plugin. |
+ */ |
var AD_VIEW_ATTRIBUTES = [ |
'name', |
]; |
-// List of custom attributes (and their behavior) |
-// |
-// name: attribute name. |
-// onInit(adview): callback invoked when the <adview> element is created. |
-// onMutate(adview, mutation): callback invoked when attribute is mutated. |
+/** |
+ * List of custom attributes (and their behavior). |
+ * |
+ * name: attribute name. |
+ * onMutation(adview, mutation): callback invoked when attribute is mutated. |
+ * isProperty: True if the attribute should be exposed as a property. |
+ */ |
var AD_VIEW_CUSTOM_ATTRIBUTES = [ |
{ |
- 'name': "ad-network", |
- 'onInit': function(adview) { |
- if (adview.node_.hasAttribute(this.name)) { |
- var value = adview.node_.getAttribute(this.name); |
- var item = getAdNetworkInfo(value); |
- if (item) { |
- adview.objectNode_.setAttribute("src", item.url); |
- } |
- else if (allowCustomAdNetworks()) { |
- console.log('The ad-network \"' + value + '\" is not recognized, ' + |
- 'but custom ad-networks are enabled.'); |
- } |
- else { |
- console.error('The ad-network \"' + value + '\" is not recognized.'); |
- } |
- } |
+ name: 'ad-network', |
+ onMutation: function(adview, mutation) { |
+ adview.handleAdNetworkMutation(mutation); |
+ }, |
+ isProperty: function() { |
+ return true; |
} |
}, |
{ |
- 'name': "src", |
- 'onInit': function(adview) { |
- if (allowCustomAdNetworks()) { |
- if (adview.node_.hasAttribute(this.name)) { |
- var newValue = adview.node_.getAttribute(this.name); |
- adview.objectNode_.setAttribute("src", newValue); |
- } |
- } |
+ name: 'src', |
+ onMutation: function(adview, mutation) { |
+ adview.handleSrcMutation(mutation); |
}, |
- 'onMutation': function(adview, mutation) { |
- if (allowCustomAdNetworks()) { |
- if (adview.node_.hasAttribute(this.name)) { |
- var newValue = adview.node_.getAttribute(this.name); |
- // Note: setAttribute does not work as intended here. |
- //adview.objectNode_.setAttribute(this.name, newValue); |
- adview.objectNode_[this.name] = newValue; |
- } |
- else { |
- // If an attribute is removed from the BrowserPlugin, then remove it |
- // from the <adview> as well. |
- this.objectNode_.removeAttribute(this.name); |
- } |
- } |
+ isProperty: function() { |
+ return allowCustomAdNetworks(); |
} |
} |
]; |
-// List of api methods. These are forwarded to the browser plugin. |
+/** |
+ * List of api methods. These are forwarded to the browser plugin. |
+ */ |
var AD_VIEW_API_METHODS = [ |
// Empty for now. |
]; |
-// List of events to blindly forward from the browser plugin to the <adview>. |
+/** |
+ * List of events to blindly forward from the browser plugin to the <adview>. |
+ */ |
var AD_VIEW_EVENTS = { |
- 'loadcommit' : [], |
+ 'loadabort' : ['url', 'isTopLevel', 'reason'], |
+ 'loadcommit' : ['url', 'isTopLevel'], |
'sizechanged': ['oldHeight', 'oldWidth', 'newHeight', 'newWidth'], |
}; |
-// List of supported ad-networks. |
-// |
-// name: identifier of the ad-network, corresponding to a valid value |
-// of the "ad-network" attribute of an <adview> element. |
-// url: url to navigate to when initially displaying the <adview>. |
-// origin: origin of urls the <adview> is allowed navigate to. |
+/** |
+ * List of supported ad-networks. |
+ * |
+ * name: identifier of the ad-network, corresponding to a valid value |
+ * of the "ad-network" attribute of an <adview> element. |
+ * url: url to navigate to when initially displaying the <adview>. |
+ * origin: origin of urls the <adview> is allowed navigate to. |
+ */ |
var AD_VIEW_AD_NETWORKS_WHITELIST = [ |
{ |
- 'name': 'admob', |
- 'url': 'https://admob-sdk.doubleclick.net/chromeapps', |
- 'origin': 'https://double.net' |
+ name: 'admob', |
+ url: 'https://admob-sdk.doubleclick.net/chromeapps', |
+ origin: 'https://double.net' |
}, |
]; |
-// |
-// Return the whitelisted ad-network entry named |name|. |
-// |
+/** |
+ * Return the whitelisted ad-network entry named |name|. |
+ */ |
function getAdNetworkInfo(name) { |
var result = null; |
forEach(AD_VIEW_AD_NETWORKS_WHITELIST, function(i, item) { |
@@ -120,133 +107,194 @@ function getAdNetworkInfo(name) { |
/** |
* @constructor |
*/ |
-function AdView(node) { |
- this.node_ = node; |
- var shadowRoot = node.webkitCreateShadowRoot(); |
+function AdView(adviewNode) { |
+ this.adviewNode_ = adviewNode; |
+ this.browserPluginNode_ = this.createBrowserPluginNode_(); |
+ var shadowRoot = this.adviewNode_.webkitCreateShadowRoot(); |
+ shadowRoot.appendChild(this.browserPluginNode_); |
- this.objectNode_ = document.createElement('object'); |
- this.objectNode_.type = 'application/browser-plugin'; |
+ this.setupCustomAttributes_(); |
+ this.setupAdviewNodeObservers_(); |
+ this.setupAdviewNodeMethods_(); |
+ this.setupAdviewNodeProperties_(); |
+ this.setupAdviewNodeEvents_(); |
+ this.setupBrowserPluginNodeObservers_(); |
+} |
+ |
+/** |
+ * @private |
+ */ |
+AdView.prototype.createBrowserPluginNode_ = function() { |
+ var browserPluginNode = document.createElement('object'); |
+ browserPluginNode.type = 'application/browser-plugin'; |
// The <object> node fills in the <adview> container. |
- this.objectNode_.style.width = '100%'; |
- this.objectNode_.style.height = '100%'; |
+ browserPluginNode.style.width = '100%'; |
+ browserPluginNode.style.height = '100%'; |
forEach(AD_VIEW_ATTRIBUTES, function(i, attributeName) { |
// Only copy attributes that have been assigned values, rather than copying |
// a series of undefined attributes to BrowserPlugin. |
- if (this.node_.hasAttribute(attributeName)) { |
- this.objectNode_.setAttribute( |
- attributeName, this.node_.getAttribute(attributeName)); |
+ if (this.adviewNode_.hasAttribute(attributeName)) { |
+ browserPluginNode.setAttribute( |
+ attributeName, this.adviewNode_.getAttribute(attributeName)); |
} |
}, this); |
+ return browserPluginNode; |
+} |
+ |
+/** |
+ * @private |
+ */ |
+AdView.prototype.setupCustomAttributes_ = function() { |
forEach(AD_VIEW_CUSTOM_ATTRIBUTES, function(i, attributeInfo) { |
- if (attributeInfo.onInit) { |
- attributeInfo.onInit(this); |
+ if (attributeInfo.onMutation) { |
+ attributeInfo.onMutation(this); |
} |
}, this); |
+} |
- shadowRoot.appendChild(this.objectNode_); |
- |
- // this.objectNode_[apiMethod] are not necessarily defined immediately after |
- // the shadow object is appended to the shadow root. |
+/** |
+ * @private |
+ */ |
+AdView.prototype.setupAdviewNodeMethods_ = function() { |
+ // this.browserPluginNode_[apiMethod] are not necessarily defined immediately |
+ // after the shadow object is appended to the shadow root. |
var self = this; |
forEach(AD_VIEW_API_METHODS, function(i, apiMethod) { |
- node[apiMethod] = function(var_args) { |
- return self.objectNode_[apiMethod].apply(self.objectNode_, arguments); |
+ self.adviewNode_[apiMethod] = function(var_args) { |
+ return self.browserPluginNode_[apiMethod].apply( |
+ self.browserPluginNode_, arguments); |
}; |
}, this); |
+} |
+/** |
+ * @private |
+ */ |
+AdView.prototype.setupAdviewNodeObservers_ = function() { |
// Map attribute modifications on the <adview> tag to property changes in |
// the underlying <object> node. |
var handleMutation = function(i, mutation) { |
- this.handleMutation_(mutation); |
+ this.handleAdviewAttributeMutation_(mutation); |
}.bind(this); |
var observer = new WebKitMutationObserver(function(mutations) { |
forEach(mutations, handleMutation); |
}); |
observer.observe( |
- this.node_, |
+ this.adviewNode_, |
{attributes: true, attributeFilter: AD_VIEW_ATTRIBUTES}); |
- var handleObjectMutation = function(i, mutation) { |
- this.handleObjectMutation_(mutation); |
- }.bind(this); |
- var objectObserver = new WebKitMutationObserver(function(mutations) { |
- forEach(mutations, handleObjectMutation); |
- }); |
- objectObserver.observe( |
- this.objectNode_, |
- {attributes: true, attributeFilter: AD_VIEW_ATTRIBUTES}); |
+ this.setupAdviewNodeCustomObservers_(); |
+} |
- // Map custom attribute modifications on the <adview> tag to property changes |
- // in the underlying <object> node. |
- var handleCustomMutation = function(i, mutation) { |
- this.handleCustomMutation_(mutation); |
+/** |
+ * @private |
+ */ |
+AdView.prototype.setupAdviewNodeCustomObservers_ = function() { |
+ var handleMutation = function(i, mutation) { |
+ this.handleAdviewCustomAttributeMutation_(mutation); |
}.bind(this); |
var observer = new WebKitMutationObserver(function(mutations) { |
- forEach(mutations, handleCustomMutation); |
+ forEach(mutations, handleMutation); |
}); |
var customAttributeNames = |
AD_VIEW_CUSTOM_ATTRIBUTES.map(function(item) { return item.name; }); |
observer.observe( |
- this.node_, |
+ this.adviewNode_, |
{attributes: true, attributeFilter: customAttributeNames}); |
+} |
- var objectNode = this.objectNode_; |
+/** |
+ * @private |
+ */ |
+AdView.prototype.setupBrowserPluginNodeObservers_ = function() { |
+ var handleMutation = function(i, mutation) { |
+ this.handleBrowserPluginAttributeMutation_(mutation); |
+ }.bind(this); |
+ var objectObserver = new WebKitMutationObserver(function(mutations) { |
+ forEach(mutations, handleMutation); |
+ }); |
+ objectObserver.observe( |
+ this.browserPluginNode_, |
+ {attributes: true, attributeFilter: AD_VIEW_ATTRIBUTES}); |
+} |
+ |
+/** |
+ * @private |
+ */ |
+AdView.prototype.setupAdviewNodeProperties_ = function() { |
+ var browserPluginNode = this.browserPluginNode_; |
// Expose getters and setters for the attributes. |
forEach(AD_VIEW_ATTRIBUTES, function(i, attributeName) { |
- Object.defineProperty(this.node_, attributeName, { |
+ Object.defineProperty(this.adviewNode_, attributeName, { |
get: function() { |
- return objectNode[attributeName]; |
+ return browserPluginNode[attributeName]; |
}, |
set: function(value) { |
- objectNode[attributeName] = value; |
+ browserPluginNode[attributeName] = value; |
}, |
enumerable: true |
}); |
}, this); |
+ // Expose getters and setters for the custom attributes. |
+ var adviewNode = this.adviewNode_; |
+ forEach(AD_VIEW_CUSTOM_ATTRIBUTES, function(i, attributeInfo) { |
+ if (attributeInfo.isProperty()) { |
+ var attributeName = attributeInfo.name; |
+ Object.defineProperty(this.adviewNode_, attributeName, { |
+ get: function() { |
+ return adviewNode.getAttribute(attributeName); |
+ }, |
+ set: function(value) { |
+ adviewNode.setAttribute(attributeName, value); |
+ }, |
+ enumerable: true |
+ }); |
+ } |
+ }, this); |
+ |
+ this.setupAdviewContentWindowProperty_(); |
+} |
+ |
+/** |
+ * @private |
+ */ |
+AdView.prototype.setupAdviewContentWindowProperty_ = function() { |
+ var browserPluginNode = this.browserPluginNode_; |
// We cannot use {writable: true} property descriptor because we want dynamic |
// getter value. |
- Object.defineProperty(this.node_, 'contentWindow', { |
+ Object.defineProperty(this.adviewNode_, 'contentWindow', { |
get: function() { |
// TODO(fsamuel): This is a workaround to enable |
// contentWindow.postMessage until http://crbug.com/152006 is fixed. |
- if (objectNode.contentWindow) |
- return objectNode.contentWindow.self; |
+ if (browserPluginNode.contentWindow) |
+ return browserPluginNode.contentWindow.self; |
console.error('contentWindow is not available at this time. ' + |
'It will become available when the page has finished loading.'); |
}, |
// No setter. |
enumerable: true |
}); |
- |
- for (var eventName in AD_VIEW_EVENTS) { |
- this.setupEvent_(eventName, AD_VIEW_EVENTS[eventName]); |
- } |
} |
/** |
* @private |
*/ |
-AdView.prototype.handleMutation_ = function(mutation) { |
+AdView.prototype.handleAdviewAttributeMutation_ = function(mutation) { |
// This observer monitors mutations to attributes of the <adview> and |
// updates the BrowserPlugin properties accordingly. In turn, updating |
// a BrowserPlugin property will update the corresponding BrowserPlugin |
// attribute, if necessary. See BrowserPlugin::UpdateDOMAttribute for more |
// details. |
- this.objectNode_[mutation.attributeName] = |
- this.node_.getAttribute(mutation.attributeName); |
+ this.browserPluginNode_[mutation.attributeName] = |
+ this.adviewNode_.getAttribute(mutation.attributeName); |
}; |
/** |
* @private |
*/ |
-AdView.prototype.handleCustomMutation_ = function(mutation) { |
- // This observer monitors mutations to attributes of the <adview> and |
- // updates the BrowserPlugin properties accordingly. In turn, updating |
- // a BrowserPlugin property will update the corresponding BrowserPlugin |
- // attribute, if necessary. See BrowserPlugin::UpdateDOMAttribute for more |
- // details. |
+AdView.prototype.handleAdviewCustomAttributeMutation_ = function(mutation) { |
forEach(AD_VIEW_CUSTOM_ATTRIBUTES, function(i, item) { |
if (mutation.attributeName.toUpperCase() == item.name.toUpperCase()) { |
if (item.onMutation) { |
@@ -259,13 +307,13 @@ AdView.prototype.handleCustomMutation_ = function(mutation) { |
/** |
* @private |
*/ |
-AdView.prototype.handleObjectMutation_ = function(mutation) { |
+AdView.prototype.handleBrowserPluginAttributeMutation_ = function(mutation) { |
// This observer monitors mutations to attributes of the BrowserPlugin and |
// updates the <adview> attributes accordingly. |
- if (!this.objectNode_.hasAttribute(mutation.attributeName)) { |
+ if (!this.browserPluginNode_.hasAttribute(mutation.attributeName)) { |
// If an attribute is removed from the BrowserPlugin, then remove it |
// from the <adview> as well. |
- this.node_.removeAttribute(mutation.attributeName); |
+ this.adviewNode_.removeAttribute(mutation.attributeName); |
} else { |
// Update the <adview> attribute to match the BrowserPlugin attribute. |
// Note: Calling setAttribute on <adview> will trigger its mutation |
@@ -274,10 +322,10 @@ AdView.prototype.handleObjectMutation_ = function(mutation) { |
// again (such as navigation when crashed), this could end up in an infinite |
// loop. Thus, we avoid this loop by only updating the <adview> attribute |
// if the BrowserPlugin attributes differs from it. |
- var oldValue = this.node_.getAttribute(mutation.attributeName); |
- var newValue = this.objectNode_.getAttribute(mutation.attributeName); |
+ var oldValue = this.adviewNode_.getAttribute(mutation.attributeName); |
+ var newValue = this.browserPluginNode_.getAttribute(mutation.attributeName); |
if (newValue != oldValue) { |
- this.node_.setAttribute(mutation.attributeName, newValue); |
+ this.adviewNode_.setAttribute(mutation.attributeName, newValue); |
} |
} |
}; |
@@ -285,23 +333,141 @@ AdView.prototype.handleObjectMutation_ = function(mutation) { |
/** |
* @private |
*/ |
+AdView.prototype.navigateToUrl_ = function(url) { |
+ var newValue = url; |
+ var oldValue = this.browserPluginNode_.hasAttribute('src') ? |
+ this.browserPluginNode_.getAttribute('src') : |
+ null; |
+ |
+ if (newValue === oldValue) |
+ return; |
+ |
+ if (url != null) { |
+ // Note: setAttribute does not work as intended here. |
+ //this.browserPluginNode_.setAttribute('src', url); |
+ this.browserPluginNode_['src'] = url; |
+ if (allowCustomAdNetworks()) { |
+ this.adviewNode_.setAttribute('src', url); |
+ } |
+ } |
+ else { |
+ this.browserPluginNode_.removeAttribute('src'); |
+ if (allowCustomAdNetworks()) { |
+ this.adviewNode_.removeAttribute('src'); |
+ } |
+ } |
+} |
+ |
+/** |
+ * @public |
+ */ |
+AdView.prototype.handleAdNetworkMutation = function(mutation) { |
+ if (this.adviewNode_.hasAttribute('ad-network')) { |
+ var value = this.adviewNode_.getAttribute('ad-network'); |
+ var item = getAdNetworkInfo(value); |
+ if (item) { |
+ this.navigateToUrl_(item.url); |
+ } |
+ else { |
+ if (allowCustomAdNetworks()) { |
+ console.log('The ad-network "' + value + '" is not recognized, ' + |
+ 'but custom ad-networks are enabled.'); |
+ var newValue = value; |
+ var oldValue = (mutation ? mutation.oldValue : undefined); |
+ if (newValue !== oldValue) { |
+ this.navigateToUrl_(''); |
+ } |
+ } |
+ else { |
+ // Ignore the new attribute value and set it to empty string. |
+ // Avoid infinite loop by checking for empty string as new value. |
+ if (value != '') { |
+ console.error('The ad-network "' + value + '" is not recognized.'); |
+ this.adviewNode_.setAttribute('ad-network', ''); |
+ } |
+ this.navigateToUrl_(''); |
+ } |
+ } |
+ } |
+ else { |
+ this.navigateToUrl_(''); |
+ } |
+} |
+ |
+/** |
+ * @public |
+ */ |
+AdView.prototype.handleSrcMutation = function(mutation) { |
+ if (allowCustomAdNetworks()) { |
+ if (this.adviewNode_.hasAttribute('src')) { |
+ var newValue = this.adviewNode_.getAttribute('src'); |
+ // Note: setAttribute does not work as intended here. |
+ //this.browserPluginNode_.setAttribute('src', newValue); |
Fady Samuel
2013/04/09 21:15:38
Remove comment? Also, why are you switching from a
rpaquay
2013/04/22 17:20:08
Added comment explaining reason.
|
+ this.browserPluginNode_['src'] = newValue; |
+ } |
+ else { |
+ // If an attribute is removed from the <adview>, then remove it |
+ // from the BrowserPlugin as well. |
+ this.browserPluginNode_.removeAttribute('src'); |
+ } |
+ } |
+ else { |
+ if (this.adviewNode_.hasAttribute('src')) { |
+ var value = this.adviewNode_.getAttribute('src'); |
+ // Ignore the new attribute value and set it to empty string. |
+ // Avoid infinite loop by checking for empty string as new value. |
+ if (value != '') { |
+ console.error('Setting the "src" attribute of an <adview> ' + |
+ 'element is not supported. Use the "ad-network" attribute ' + |
+ 'instead.'); |
+ this.adviewNode_.setAttribute('src', ''); |
+ } |
+ } |
+ } |
+} |
+ |
+/** |
+ * @private |
+ */ |
+AdView.prototype.setupAdviewNodeEvents_ = function() { |
+ for (var eventName in AD_VIEW_EVENTS) { |
+ this.setupEvent_(eventName, AD_VIEW_EVENTS[eventName]); |
+ } |
+} |
+ |
+/** |
+ * @private |
+ */ |
AdView.prototype.setupEvent_ = function(eventname, attribs) { |
- var node = this.node_; |
- this.objectNode_.addEventListener('-internal-' + eventname, function(e) { |
+ var adviewNode = this.adviewNode_; |
+ var internalname = '-internal-' + eventname; |
+ this.browserPluginNode_.addEventListener(internalname, function(e) { |
var evt = new Event(eventname, { bubbles: true }); |
var detail = e.detail ? JSON.parse(e.detail) : {}; |
forEach(attribs, function(i, attribName) { |
evt[attribName] = detail[attribName]; |
}); |
- node.dispatchEvent(evt); |
+ adviewNode.dispatchEvent(evt); |
}); |
} |
+/** |
+ * @public |
+ */ |
+AdView.prototype.dispatchEvent = function(eventname, detail) { |
+ // Create event object. |
+ var evt = new Event(eventname, { bubbles: true }); |
+ for(var item in detail) { |
+ evt[item] = detail[item]; |
+ } |
+ |
+ // Dispatch event. |
+ this.adviewNode_.dispatchEvent(evt); |
+} |
+ |
// |
// Hook up <adview> tag creation in DOM. |
// |
-var watchForTag = require("tagWatcher").watchForTag; |
- |
window.addEventListener('DOMContentLoaded', function() { |
watchForTag('ADVIEW', function(addedNode) { new AdView(addedNode); }); |
}); |