Left: | ||
Right: |
OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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) | |
Felix Dahlke
2014/01/18 13:39:19
This would return undefined if frame is falsy. I t
Sebastian Noack
2014/01/19 10:19:40
Done.
| |
418 if (frame.parent != -1) | |
419 return new Frame({id: frame.parent, tab: this._tab}); | |
420 else | |
421 return null; | |
422 } | |
423 } | |
424 }; | |
425 | |
426 | |
427 /* Web request blocking */ | |
428 | |
429 chrome.webRequest.onBeforeRequest.addListener(function(details) | |
430 { | |
431 // the high-level code isn't interested in requests that aren't related | |
432 // to a tab and since those can only be handled in Chrome, we ignore | |
433 // them here instead of in the browser independant high-level code. | |
Felix Dahlke
2014/01/18 13:39:19
s/independant/independent/
Sebastian Noack
2014/01/19 10:19:40
Done.
| |
434 if (details.tabId == -1) | |
435 return; | |
436 | |
437 var tab = new Tab({id: details.tabId}); | |
438 var frames = framesOfTabs.get(tab); | |
439 | |
440 if (!frames) | |
441 { | |
442 frames = []; | |
443 framesOfTabs.set(tab, frames); | |
444 | |
445 // assume that the first request belongs to the top frame. Chrome | |
446 // may give the top frame the type "object" instead of "main_frame". | |
447 // https://code.google.com/p/chromium/issues/detail?id=281711 | |
448 if (frameId == 0) | |
449 details.type = "main_frame"; | |
450 } | |
451 | |
452 var frameId; | |
453 if (details.type == "main_frame" || details.type == "sub_frame") | |
454 { | |
455 frameId = details.parentFrameId; | |
456 frames[details.frameId] = {url: details.url, parent: frameId}; | |
457 | |
458 // the high-level code isn't interested in top frame requests and | |
459 // since those can only be handled in Chrome, we ignore them here | |
460 // instead of in the browser independent high-level code. | |
461 if (details.type == "main_frame") | |
462 return; | |
463 } | |
464 else | |
465 frameId = details.frameId; | |
466 | |
467 // the high-level code relies on the frame. However in case the frame can't | |
468 // be controlled by the extension or the extension was (re)loaded after the | |
469 // frame was loaded, the frame is unkown and we have to ignore the request. | |
Felix Dahlke
2014/01/18 13:39:19
s/unkown/unknown/
Sebastian Noack
2014/01/19 10:19:40
Done.
| |
470 if (!(frameId in frames)) | |
471 return; | |
472 | |
473 var frame = new Frame({id: frameId, tab: tab}); | |
474 | |
475 for (var i = 0; i < ext.webRequest.onBeforeRequest._listeners.length; i++) | |
476 { | |
477 if (ext.webRequest.onBeforeRequest._listeners[i](details.url, details.type , tab, frame) === false) | |
Felix Dahlke
2014/01/18 13:39:19
Why not the following?
if (!ext.webRequest.onBefo
Sebastian Noack
2014/01/19 10:19:40
In order to block the request, the handler should
| |
478 return {cancel: true}; | |
479 } | |
480 }, {urls: ["<all_urls>"]}, ["blocking"]); | |
481 | |
482 | |
339 /* API */ | 483 /* API */ |
340 | 484 |
341 ext.windows = { | 485 ext.windows = { |
342 getAll: function(callback) | 486 getAll: function(callback) |
343 { | 487 { |
344 chrome.windows.getAll(function(windows) | 488 chrome.windows.getAll(function(windows) |
345 { | 489 { |
346 callback(windows.map(function(win) | 490 callback(windows.map(function(win) |
347 { | 491 { |
348 return new Window(win); | 492 return new Window(win); |
349 })); | 493 })); |
350 }); | 494 }); |
351 }, | 495 }, |
352 getLastFocused: function(callback) | 496 getLastFocused: function(callback) |
353 { | 497 { |
354 chrome.windows.getLastFocused(function(win) | 498 chrome.windows.getLastFocused(function(win) |
355 { | 499 { |
356 callback(new Window(win)); | 500 callback(new Window(win)); |
357 }); | 501 }); |
358 } | 502 } |
359 }; | 503 }; |
360 | 504 |
361 ext.tabs = { | 505 ext.tabs = { |
506 onBeforeNavigate: new BeforeNavigateTabEventTarget(), | |
362 onLoading: new LoadingTabEventTarget(), | 507 onLoading: new LoadingTabEventTarget(), |
363 onCompleted: new CompletedTabEventTarget(), | 508 onCompleted: new CompletedTabEventTarget(), |
364 onActivated: new ActivatedTabEventTarget(), | 509 onActivated: new ActivatedTabEventTarget(), |
365 onRemoved: new RemovedTabEventTarget() | 510 onRemoved: new RemovedTabEventTarget() |
366 }; | 511 }; |
367 | 512 |
368 ext.webRequest = { | 513 ext.webRequest = { |
369 onBeforeRequest: new BeforeRequestEventTarget(), | 514 onBeforeRequest: new SimpleEventTarget(), |
370 handlerBehaviorChanged: chrome.webRequest.handlerBehaviorChanged | 515 handlerBehaviorChanged: chrome.webRequest.handlerBehaviorChanged |
371 }; | 516 }; |
372 | 517 |
373 ext.contextMenus = { | 518 ext.contextMenus = { |
374 create: function(title, contexts, onclick) | 519 create: function(title, contexts, onclick) |
375 { | 520 { |
376 chrome.contextMenus.create({ | 521 chrome.contextMenus.create({ |
377 title: title, | 522 title: title, |
378 contexts: contexts, | 523 contexts: contexts, |
379 onclick: function(info, tab) | 524 onclick: function(info, tab) |
380 { | 525 { |
381 onclick(info.srcUrl, new Tab(tab)); | 526 onclick(info.srcUrl, new Tab(tab)); |
382 } | 527 } |
383 }); | 528 }); |
384 }, | 529 }, |
385 removeAll: function(callback) | 530 removeAll: function(callback) |
386 { | 531 { |
387 chrome.contextMenus.removeAll(callback); | 532 chrome.contextMenus.removeAll(callback); |
388 } | 533 } |
389 }; | 534 }; |
535 | |
536 ext.onMessage = new BackgroundMessageEventTarget(); | |
390 })(); | 537 })(); |
OLD | NEW |