| 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 |