Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(110)

Side by Side Diff: chrome/renderer/resources/extensions/ad_view.js

Issue 12463015: Enable <adview> tag for packaged apps. (Closed) Base URL: https://git.chromium.org/chromium/src.git@master
Patch Set: Address asargent@ code review feedback. Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Shim that simulates a <adview> tag via Mutation Observers.
6 //
7 // The actual tag is implemented via the browser plugin. The internals of this
8 // are hidden via Shadow DOM.
9
10 // TODO(rpaquay): This file is currently very similar to "web_view.js". Do we
11 // want to refactor to extract common pieces?
12
13 var adViewCustom = require('adViewCustom');
14
15 var allowCustomAdNetworks = (function(allow){
16 return function() { return Boolean(allow); }
17 })(adViewCustom ? adViewCustom.enabled : false);
18
19
20 // List of attribute names to "blindly" sync between <adview> tag and internal
21 // browser plugin.
22 var AD_VIEW_ATTRIBUTES = [
23 'name',
24 ];
25
26 // List of custom attributes (and their behavior)
27 //
28 // name: attribute name.
29 // onInit(adview): callback invoked when the <adview> element is created.
30 var AD_VIEW_CUSTOM_ATTRIBUTES = [
31 {
32 'name': "ad-network",
33 'onInit': function(adview) {
34 if (adview.node_.hasAttribute(this.name)) {
35 var value = adview.node_.getAttribute(this.name);
36 var item = getAdNetworkInfo(value);
37 if (item) {
38 adview.objectNode_.setAttribute("src", item.url);
39 }
40 else if (allowCustomAdNetworks()) {
41 console.log('The ad-network \"' + value + '\" is not recognized, ' +
42 'but custom ad-networks are enabled.');
43 }
44 else {
45 console.error('The ad-network \"' + value + '\" is not recognized.');
46 }
47 }
48 }
49 },
50 {
51 'name': "src",
52 'onInit': function(adview) {
53 if (allowCustomAdNetworks()) {
54 if (adview.node_.hasAttribute(this.name)) {
55 var newValue = adview.node_.getAttribute(this.name);
56 adview.objectNode_.setAttribute("src", newValue);
57 }
58 }
59 },
60 'onMutation': function(adview, mutation) {
61 if (allowCustomAdNetworks()) {
62 if (adview.node_.hasAttribute(this.name)) {
63 var newValue = adview.node_.getAttribute(this.name);
64 //console.log('Setting <adview> "src": ' + newValue);
asargent_no_longer_on_chrome 2013/03/12 20:54:27 nit: remove this debugging line
rpaquay 2013/03/12 23:05:36 Done.
65 // Note: setAttribute does not work as intended here.
66 //adview.objectNode_.setAttribute(this.name, newValue);
67 adview.objectNode_[this.name] = newValue;
68 }
69 else {
70 // If an attribute is removed from the BrowserPlugin, then remove it
71 // from the <adview> as well.
72 this.objectNode_.removeAttribute(this.name);
73 }
74 }
75 }
76 }
77 ];
78
79 // List of api methods. These are forwarded to the browser plugin.
80 var AD_VIEW_API_METHODS = [
81 // Empty for now.
82 ];
83
84 // List of events to blindly forward from the browser plugin to the <adview>.
85 var AD_VIEW_EVENTS = {
86 'loadcommit' : [],
87 'sizechanged': ['oldHeight', 'oldWidth', 'newHeight', 'newWidth'],
88 };
89
90 // List of supported ad-networks.
91 //
92 // name: identifier of the ad-network, corresponding to a valid value
93 // of the "ad-network" attribute of an <adview> element.
94 // url: url to navigate to when initially displaying the <adview>.
95 // origin: origin of urls the <adview> is allowed navigate to.
96 var AD_VIEW_AD_NETWORKS_WHITELIST = [
97 {
98 'name': 'admob',
99 'url': 'https://admob-sdk.doubleclick.net/chromeapps',
100 'origin': 'https://double.net'
101 },
102 ];
103
104 //
105 // Return the whitelisted ad-network entry named |name|.
106 //
107 function getAdNetworkInfo(name) {
108 var result = null;
109 AD_VIEW_AD_NETWORKS_WHITELIST.forEach(function(item) {
110 if (item.name === name)
111 result = item;
112 });
113 return result;
114 }
115
116 /**
117 * @constructor
118 */
119 function AdView(node) {
120 this.node_ = node;
121 var shadowRoot = node.webkitCreateShadowRoot();
122
123 this.objectNode_ = document.createElement('object');
124 this.objectNode_.type = 'application/browser-plugin';
125 // The <object> node fills in the <adview> container.
126 this.objectNode_.style.width = '100%';
127 this.objectNode_.style.height = '100%';
128 AD_VIEW_ATTRIBUTES.forEach(function(attributeName) {
129 // Only copy attributes that have been assigned values, rather than copying
130 // a series of undefined attributes to BrowserPlugin.
131 if (this.node_.hasAttribute(attributeName)) {
132 this.objectNode_.setAttribute(
133 attributeName, this.node_.getAttribute(attributeName));
134 }
135 }, this);
136
137 AD_VIEW_CUSTOM_ATTRIBUTES.forEach(function(attributeInfo) {
138 if (attributeInfo.onInit) {
139 attributeInfo.onInit(this);
140 }
141 }, this);
142
143 shadowRoot.appendChild(this.objectNode_);
144
145 // this.objectNode_[apiMethod] are not necessarily defined immediately after
146 // the shadow object is appended to the shadow root.
147 var self = this;
148 AD_VIEW_API_METHODS.forEach(function(apiMethod) {
asargent_no_longer_on_chrome 2013/03/12 20:54:27 nit: see kalman's recent mail about Array.forEach
rpaquay 2013/03/12 23:05:36 Done.
149 node[apiMethod] = function(var_args) {
150 return self.objectNode_[apiMethod].apply(self.objectNode_, arguments);
151 };
152 }, this);
153
154 // Map attribute modifications on the <adview> tag to property changes in
155 // the underlying <object> node.
156 var handleMutation = this.handleMutation_.bind(this);
157 var observer = new WebKitMutationObserver(function(mutations) {
158 mutations.forEach(handleMutation);
159 });
160 observer.observe(
161 this.node_,
162 {attributes: true, attributeFilter: AD_VIEW_ATTRIBUTES});
163
164 var handleObjectMutation = this.handleObjectMutation_.bind(this);
165 var objectObserver = new WebKitMutationObserver(function(mutations) {
166 mutations.forEach(handleObjectMutation);
167 });
168 objectObserver.observe(
169 this.objectNode_,
170 {attributes: true, attributeFilter: AD_VIEW_ATTRIBUTES});
171
172 // Map custom attribute modifications on the <adview> tag to property changes
173 // in the underlying <object> node.
174 var handleCustomMutation = this.handleCustomMutation_.bind(this);
175 var observer = new WebKitMutationObserver(function(mutations) {
176 mutations.forEach(handleCustomMutation);
177 });
178 var customAttributeNames =
179 AD_VIEW_CUSTOM_ATTRIBUTES.map(function(item) { return item.name; });
180 observer.observe(
181 this.node_,
182 {attributes: true, attributeFilter: customAttributeNames});
183
184 var objectNode = this.objectNode_;
185 // Expose getters and setters for the attributes.
186 AD_VIEW_ATTRIBUTES.forEach(function(attributeName) {
187 Object.defineProperty(this.node_, attributeName, {
188 get: function() {
189 return objectNode[attributeName];
190 },
191 set: function(value) {
192 objectNode[attributeName] = value;
193 },
194 enumerable: true
195 });
196 }, this);
197
198 // We cannot use {writable: true} property descriptor because we want dynamic
199 // getter value.
200 Object.defineProperty(this.node_, 'contentWindow', {
201 get: function() {
202 // TODO(fsamuel): This is a workaround to enable
203 // contentWindow.postMessage until http://crbug.com/152006 is fixed.
204 if (objectNode.contentWindow)
205 return objectNode.contentWindow.self;
206 console.error('contentWindow is not available at this time. ' +
207 'It will become available when the page has finished loading.');
208 },
209 // No setter.
210 enumerable: true
211 });
212
213 for (var eventName in AD_VIEW_EVENTS) {
214 this.setupEvent_(eventName, AD_VIEW_EVENTS[eventName]);
215 }
216 }
217
218 /**
219 * @private
220 */
221 AdView.prototype.handleMutation_ = function(mutation) {
222 // This observer monitors mutations to attributes of the <adview> and
223 // updates the BrowserPlugin properties accordingly. In turn, updating
224 // a BrowserPlugin property will update the corresponding BrowserPlugin
225 // attribute, if necessary. See BrowserPlugin::UpdateDOMAttribute for more
226 // details.
227 this.objectNode_[mutation.attributeName] =
228 this.node_.getAttribute(mutation.attributeName);
229 };
230
231 /**
232 * @private
233 */
234 AdView.prototype.handleCustomMutation_ = function(mutation) {
235 // This observer monitors mutations to attributes of the <adview> and
236 // updates the BrowserPlugin properties accordingly. In turn, updating
237 // a BrowserPlugin property will update the corresponding BrowserPlugin
238 // attribute, if necessary. See BrowserPlugin::UpdateDOMAttribute for more
239 // details.
240 AD_VIEW_CUSTOM_ATTRIBUTES.forEach(function(item) {
241 if (mutation.attributeName.toUpperCase() == item.name.toUpperCase()) {
242 if (item.onMutation) {
243 item.onMutation.bind(item)(this, mutation);
244 }
245 }
246 }, this);
247 };
248
249 /**
250 * @private
251 */
252 AdView.prototype.handleObjectMutation_ = function(mutation) {
253 // This observer monitors mutations to attributes of the BrowserPlugin and
254 // updates the <adview> attributes accordingly.
255 if (!this.objectNode_.hasAttribute(mutation.attributeName)) {
256 // If an attribute is removed from the BrowserPlugin, then remove it
257 // from the <adview> as well.
258 this.node_.removeAttribute(mutation.attributeName);
259 } else {
260 // Update the <adview> attribute to match the BrowserPlugin attribute.
261 // Note: Calling setAttribute on <adview> will trigger its mutation
262 // observer which will then propagate that attribute to BrowserPlugin. In
263 // cases where we permit assigning a BrowserPlugin attribute the same value
264 // again (such as navigation when crashed), this could end up in an infinite
265 // loop. Thus, we avoid this loop by only updating the <adview> attribute
266 // if the BrowserPlugin attributes differs from it.
267 var oldValue = this.node_.getAttribute(mutation.attributeName);
268 var newValue = this.objectNode_.getAttribute(mutation.attributeName);
269 if (newValue != oldValue) {
270 this.node_.setAttribute(mutation.attributeName, newValue);
271 }
272 }
273 };
274
275 /**
276 * @private
277 */
278 AdView.prototype.setupEvent_ = function(eventname, attribs) {
279 var node = this.node_;
280 this.objectNode_.addEventListener('-internal-' + eventname, function(e) {
281 var evt = new Event(eventname, { bubbles: true });
282 var detail = e.detail ? JSON.parse(e.detail) : {};
283 attribs.forEach(function(attribName) {
284 evt[attribName] = detail[attribName];
285 });
286 node.dispatchEvent(evt);
287 });
288 }
289
290 //
291 // Hook up <adview> tag creation in DOM.
292 //
293 var watchForTag = require("tagWatcher").watchForTag;
294
295 window.addEventListener('DOMContentLoaded', function() {
296 watchForTag('ADVIEW', function(addedNode) { new AdView(addedNode); });
297 });
OLDNEW
« no previous file with comments | « chrome/renderer/extensions/dispatcher.cc ('k') | chrome/renderer/resources/extensions/ad_view_custom.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698