Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: chrome/ext/background.js

Issue 6346177440120832: Added abstraction for frames, to fix domain-based rules, whitelisting and ad counter on Safari (Closed)
Patch Set: Rebased and addressed comments Created Jan. 19, 2014, 10:40 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « background.js ('k') | chrome/ext/common.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <http://adblockplus.org/>, 2 * This file is part of Adblock Plus <http://adblockplus.org/>,
3 * Copyright (C) 2006-2013 Eyeo GmbH 3 * Copyright (C) 2006-2013 Eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
92 }, 92 },
93 _removeTab: function(tab) 93 _removeTab: function(tab)
94 { 94 {
95 delete this._tabs[tab._id]; 95 delete this._tabs[tab._id];
96 96
97 if (Object.keys(this._tabs).length == 0) 97 if (Object.keys(this._tabs).length == 0)
98 this.removeListener(this._sharedListener); 98 this.removeListener(this._sharedListener);
99 } 99 }
100 }; 100 };
101 101
102 var BeforeNavigateTabEventTarget = function()
103 {
104 TabEventTarget.call(this, chrome.webNavigation.onBeforeNavigate);
105 };
106 BeforeNavigateTabEventTarget.prototype = {
107 __proto__: TabEventTarget.prototype,
108 _wrapListener: function(listener)
109 {
110 return function(details)
111 {
112 if (details.frameId == 0)
113 listener(new Tab({id: details.tabId, url: details.url}));
114 };
115 }
116 };
117
102 var LoadingTabEventTarget = function() 118 var LoadingTabEventTarget = function()
103 { 119 {
104 TabEventTarget.call(this, chrome.tabs.onUpdated); 120 TabEventTarget.call(this, chrome.tabs.onUpdated);
105 }; 121 };
106 LoadingTabEventTarget.prototype = { 122 LoadingTabEventTarget.prototype = {
107 __proto__: TabEventTarget.prototype, 123 __proto__: TabEventTarget.prototype,
108 _wrapListener: function(listener) 124 _wrapListener: function(listener)
109 { 125 {
110 return function(id, info, tab) 126 return function(id, info, tab)
111 { 127 {
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
154 TabEventTarget.call(this, chrome.tabs.onRemoved); 170 TabEventTarget.call(this, chrome.tabs.onRemoved);
155 }; 171 };
156 RemovedTabEventTarget.prototype = { 172 RemovedTabEventTarget.prototype = {
157 __proto__: TabEventTarget.prototype, 173 __proto__: TabEventTarget.prototype,
158 _wrapListener: function(listener) 174 _wrapListener: function(listener)
159 { 175 {
160 return function(id) { listener(new Tab({id: id})); }; 176 return function(id) { listener(new Tab({id: id})); };
161 } 177 }
162 }; 178 };
163 179
164 var BeforeRequestEventTarget = function() 180 var BackgroundMessageEventTarget = function()
165 { 181 {
166 WrappedEventTarget.call(this, chrome.webRequest.onBeforeRequest); 182 MessageEventTarget.call(this);
167 }; 183 }
168 BeforeRequestEventTarget.prototype = { 184 BackgroundMessageEventTarget.prototype = {
169 __proto__: WrappedEventTarget.prototype, 185 __proto__: MessageEventTarget.prototype,
170 _wrapListener: function(listener) 186 _wrapSender: function(sender)
171 { 187 {
172 return function(details) 188 var tab = new Tab(sender.tab);
173 { 189 return {tab: tab, frame: new Frame({url: sender.url, tab: tab})};
174 var tab = null;
175
176 if (details.tabId != -1)
177 tab = new Tab({id: details.tabId});
178
179 return {cancel: listener(
180 details.url,
181 details.type,
182 tab,
183 details.frameId,
184 details.parentFrameId
185 ) === false};
186 };
187 },
188 _prepareExtraArguments: function(urls)
189 {
190 return [urls ? {urls: urls} : {}, ["blocking"]];
191 } 190 }
192 }; 191 };
193 192
194 193
195 /* Tabs */ 194 /* Tabs */
196 195
197 var sendMessage = chrome.tabs.sendMessage || chrome.tabs.sendRequest; 196 var sendMessage = chrome.tabs.sendMessage || chrome.tabs.sendRequest;
198 197
199 var BrowserAction = function(tabId) 198 var BrowserAction = function(tabId)
200 { 199 {
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
234 tabId: this._tabId, 233 tabId: this._tabId,
235 text: badge.number.toString() 234 text: badge.number.toString()
236 }); 235 });
237 } 236 }
238 } 237 }
239 }; 238 };
240 239
241 Tab = function(tab) 240 Tab = function(tab)
242 { 241 {
243 this._id = tab.id; 242 this._id = tab.id;
243 this._url = tab.url;
244 244
245 this.url = tab.url;
246 this.browserAction = new BrowserAction(tab.id); 245 this.browserAction = new BrowserAction(tab.id);
247 246
247 this.onBeforeNavigate = ext.tabs.onBeforeNavigate._bindToTab(this);
248 this.onLoading = ext.tabs.onLoading._bindToTab(this); 248 this.onLoading = ext.tabs.onLoading._bindToTab(this);
249 this.onCompleted = ext.tabs.onCompleted._bindToTab(this); 249 this.onCompleted = ext.tabs.onCompleted._bindToTab(this);
250 this.onActivated = ext.tabs.onActivated._bindToTab(this); 250 this.onActivated = ext.tabs.onActivated._bindToTab(this);
251 this.onRemoved = ext.tabs.onRemoved._bindToTab(this); 251 this.onRemoved = ext.tabs.onRemoved._bindToTab(this);
252 }; 252 };
253 Tab.prototype = { 253 Tab.prototype = {
254 get url()
255 {
256 // usually our Tab objects are created from chrome Tab objects, which
257 // provide the url. So we can return the url given in the constructor.
258 if (this._url != null)
259 return this._url;
260
261 // but sometimes we only have the id when we create a Tab object.
262 // In that case we get the url from top frame of the tab, recorded by
263 // the onBeforeRequest handler.
264 var frames = framesOfTabs.get(this);
265 if (frames)
266 {
267 var frame = frames[0];
268 if (frame)
269 return frame.url;
270 }
271 },
254 close: function() 272 close: function()
255 { 273 {
256 chrome.tabs.remove(this._id); 274 chrome.tabs.remove(this._id);
257 }, 275 },
258 activate: function() 276 activate: function()
259 { 277 {
260 chrome.tabs.update(this._id, {selected: true}); 278 chrome.tabs.update(this._id, {selected: true});
261 }, 279 },
262 sendMessage: function(message, responseCallback) 280 sendMessage: function(message, responseCallback)
263 { 281 {
264 sendMessage(this._id, message, responseCallback); 282 sendMessage(this._id, message, responseCallback);
265 } 283 }
266 }; 284 };
267 285
268 TabMap = function() 286 TabMap = function(deleteTabOnBeforeNavigate)
269 { 287 {
270 this._map = {}; 288 this._map = {};
271 this.delete = this.delete.bind(this); 289
290 this._delete = this._delete.bind(this);
291 this._deleteTabOnBeforeNavigate = deleteTabOnBeforeNavigate;
272 }; 292 };
273 TabMap.prototype = { 293 TabMap.prototype = {
274 get: function(tab) 294 get: function(tab)
275 { 295 {
276 return (this._map[tab._id] || {}).value; 296 return (this._map[tab._id] || {}).value;
277 }, 297 },
278 set: function(tab, value) 298 set: function(tab, value)
279 { 299 {
280 if (!(tab._id in this._map)) 300 if (!(tab._id in this._map))
281 tab.onRemoved.addListener(this.delete); 301 {
302 tab.onRemoved.addListener(this._delete);
303 if (this._deleteTabOnBeforeNavigate)
304 tab.onBeforeNavigate.addListener(this._delete);
305 }
282 306
283 this._map[tab._id] = {tab: tab, value: value}; 307 this._map[tab._id] = {tab: tab, value: value};
284 }, 308 },
285 has: function(tab) 309 has: function(tab)
286 { 310 {
287 return tab._id in this._map; 311 return tab._id in this._map;
288 }, 312 },
289 clear: function() 313 clear: function()
290 { 314 {
291 for (var id in this._map) 315 for (var id in this._map)
292 this.delete(this._map[id].tab); 316 this.delete(this._map[id].tab);
317 },
318 _delete: function(tab)
319 {
320 // delay so that other event handlers can still lookup this tab
321 setTimeout(this.delete.bind(this, tab), 0);
293 } 322 }
294 }; 323 };
295 TabMap.prototype["delete"] = function(tab) 324 TabMap.prototype["delete"] = function(tab)
296 { 325 {
297 delete this._map[tab._id]; 326 delete this._map[tab._id];
298 tab.onRemoved.removeListener(this.delete); 327
328 tab.onRemoved.removeListener(this._delete);
329 tab.onBeforeNavigate.removeListener(this._delete);
299 }; 330 };
300 331
301 332
302 /* Windows */ 333 /* Windows */
303 334
304 Window = function(win) 335 Window = function(win)
305 { 336 {
306 this._id = win.id; 337 this._id = win.id;
307 this.visible = win.status != "minimized"; 338 this.visible = win.status != "minimized";
308 }; 339 };
(...skipping 20 matching lines...) Expand all
329 chrome.tabs.create(props); 360 chrome.tabs.create(props);
330 else 361 else
331 chrome.tabs.create(props, function(tab) 362 chrome.tabs.create(props, function(tab)
332 { 363 {
333 callback(new Tab(tab)); 364 callback(new Tab(tab));
334 }); 365 });
335 } 366 }
336 }; 367 };
337 368
338 369
370 /* Frames */
371
372 var framesOfTabs = new TabMap();
373
374 Frame = function(params)
375 {
376 this._tab = params.tab;
377 this._id = params.id;
378 this._url = params.url;
379 };
380 Frame.prototype = {
381 get url()
382 {
383 if (this._url != null)
384 return this._url;
385
386 var frames = framesOfTabs.get(this._tab);
387 if (frames)
388 {
389 var frame = frames[this._id];
390 if (frame)
391 return frame.url;
392 }
393 },
394 get parent()
395 {
396 var frames = framesOfTabs.get(this._tab);
397 if (frames)
398 {
399 var frame;
400 if (this._id != null)
401 frame = frames[this._id];
402 else
403 {
404 // the frame ID wasn't available when we created
405 // the Frame object (e.g. for the onMessage event),
406 // so we have to find the frame details by their URL.
407 for (var frameId in frames)
408 {
409 if (frames[frameId].url == this._url)
410 {
411 frame = frames[frameId];
412 break;
413 }
414 }
415 }
416
417 if (frame && frame.parent != -1)
418 return new Frame({id: frame.parent, tab: this._tab});
419 else
Felix Dahlke 2014/01/20 00:02:15 No else before return, as described in the Mozilla
420 return null;
421 }
422 }
423 };
424
425
426 /* Web request blocking */
427
428 chrome.webRequest.onBeforeRequest.addListener(function(details)
429 {
430 // the high-level code isn't interested in requests that aren't related
431 // to a tab and since those can only be handled in Chrome, we ignore
432 // them here instead of in the browser independent high-level code.
433 if (details.tabId == -1)
434 return;
435
436 var tab = new Tab({id: details.tabId});
437 var frames = framesOfTabs.get(tab);
438
439 if (!frames)
440 {
441 frames = [];
442 framesOfTabs.set(tab, frames);
443
444 // assume that the first request belongs to the top frame. Chrome
445 // may give the top frame the type "object" instead of "main_frame".
446 // https://code.google.com/p/chromium/issues/detail?id=281711
447 if (frameId == 0)
448 details.type = "main_frame";
449 }
450
451 var frameId;
452 if (details.type == "main_frame" || details.type == "sub_frame")
453 {
454 frameId = details.parentFrameId;
455 frames[details.frameId] = {url: details.url, parent: frameId};
456
457 // the high-level code isn't interested in top frame requests and
458 // since those can only be handled in Chrome, we ignore them here
459 // instead of in the browser independent high-level code.
460 if (details.type == "main_frame")
461 return;
462 }
463 else
464 frameId = details.frameId;
465
466 // the high-level code relies on the frame. However in case the frame can't
467 // be controlled by the extension or the extension was (re)loaded after the
468 // frame was loaded, the frame is unknown and we have to ignore the request.
469 if (!(frameId in frames))
470 return;
471
472 var frame = new Frame({id: frameId, tab: tab});
473
474 for (var i = 0; i < ext.webRequest.onBeforeRequest._listeners.length; i++)
475 {
476 if (ext.webRequest.onBeforeRequest._listeners[i](details.url, details.type , tab, frame) === false)
477 return {cancel: true};
478 }
479 }, {urls: ["<all_urls>"]}, ["blocking"]);
480
481
339 /* API */ 482 /* API */
340 483
341 ext.windows = { 484 ext.windows = {
342 getAll: function(callback) 485 getAll: function(callback)
343 { 486 {
344 chrome.windows.getAll(function(windows) 487 chrome.windows.getAll(function(windows)
345 { 488 {
346 callback(windows.map(function(win) 489 callback(windows.map(function(win)
347 { 490 {
348 return new Window(win); 491 return new Window(win);
349 })); 492 }));
350 }); 493 });
351 }, 494 },
352 getLastFocused: function(callback) 495 getLastFocused: function(callback)
353 { 496 {
354 chrome.windows.getLastFocused(function(win) 497 chrome.windows.getLastFocused(function(win)
355 { 498 {
356 callback(new Window(win)); 499 callback(new Window(win));
357 }); 500 });
358 } 501 }
359 }; 502 };
360 503
361 ext.tabs = { 504 ext.tabs = {
505 onBeforeNavigate: new BeforeNavigateTabEventTarget(),
362 onLoading: new LoadingTabEventTarget(), 506 onLoading: new LoadingTabEventTarget(),
363 onCompleted: new CompletedTabEventTarget(), 507 onCompleted: new CompletedTabEventTarget(),
364 onActivated: new ActivatedTabEventTarget(), 508 onActivated: new ActivatedTabEventTarget(),
365 onRemoved: new RemovedTabEventTarget() 509 onRemoved: new RemovedTabEventTarget()
366 }; 510 };
367 511
368 ext.webRequest = { 512 ext.webRequest = {
369 onBeforeRequest: new BeforeRequestEventTarget(), 513 onBeforeRequest: new SimpleEventTarget(),
370 handlerBehaviorChanged: chrome.webRequest.handlerBehaviorChanged 514 handlerBehaviorChanged: chrome.webRequest.handlerBehaviorChanged
371 }; 515 };
372 516
373 var contextMenu = []; 517 var contextMenu = [];
374 ext.contextMenus = { 518 ext.contextMenus = {
375 addMenuItem: function(title, contexts, onclick) 519 addMenuItem: function(title, contexts, onclick)
376 { 520 {
377 contextMenu.push({ 521 contextMenu.push({
378 title: title, 522 title: title,
379 contexts: contexts, 523 contexts: contexts,
(...skipping 20 matching lines...) Expand all
400 onclick: item.onclick 544 onclick: item.onclick
401 }); 545 });
402 } 546 }
403 }); 547 });
404 }, 548 },
405 hideMenu: function() 549 hideMenu: function()
406 { 550 {
407 chrome.contextMenus.removeAll(); 551 chrome.contextMenus.removeAll();
408 } 552 }
409 }; 553 };
554
555 ext.onMessage = new BackgroundMessageEventTarget();
410 })(); 556 })();
OLDNEW
« no previous file with comments | « background.js ('k') | chrome/ext/common.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld