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 |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 * GNU General Public License for more details. | 12 * GNU General Public License for more details. |
13 * | 13 * |
14 * You should have received a copy of the GNU General Public License | 14 * You should have received a copy of the GNU General Public License |
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
16 */ | 16 */ |
17 | 17 |
18 (function() | 18 (function() |
19 { | 19 { |
20 if (window == window.top) | 20 if (window == window.top) |
21 safari.self.tab.dispatchMessage("loading"); | 21 safari.self.tab.dispatchMessage("loading", {isDocumentHidden: document.hidde
n}); |
22 | 22 |
23 | 23 |
24 /* Events */ | 24 /* Events */ |
25 | 25 |
26 var ContentMessageEventTarget = function() | 26 var ContentMessageEventTarget = function() |
27 { | 27 { |
28 MessageEventTarget.call(this, safari.self); | 28 MessageEventTarget.call(this, safari.self); |
29 }; | 29 }; |
30 ContentMessageEventTarget.prototype = { | 30 ContentMessageEventTarget.prototype = { |
31 __proto__: MessageEventTarget.prototype, | 31 __proto__: MessageEventTarget.prototype, |
32 _getResponseDispatcher: function(event) | 32 _getResponseDispatcher: function(event) |
33 { | 33 { |
34 return event.target.tab; | 34 return event.target.tab; |
35 }, | 35 }, |
36 _getSenderDetails: function(event) | 36 _getSenderDetails: function(event) |
37 { | 37 { |
38 return {}; | 38 return {}; |
| 39 }, |
| 40 _ignoreIf: function(event) |
| 41 { |
| 42 // If the current page is a preloaded page in Safari 7.0, we have to |
| 43 // ignore all messages sent from the background page. The high-level code |
| 44 // doesn't know anything about preloaded pages and expects messages to be |
| 45 // handled only by the current visible page. |
| 46 return document.hidden && event.message.isTabVisible; |
39 } | 47 } |
40 }; | 48 }; |
41 | 49 |
42 | 50 |
43 /* Background page proxy */ | 51 /* Background page proxy */ |
44 var proxy = { | 52 var proxy = { |
45 objects: [], | 53 objects: [], |
46 callbacks: [], | 54 callbacks: [], |
47 | 55 |
48 send: function(message) | 56 send: function(message) |
49 { | 57 { |
50 var evt = document.createEvent("Event"); | 58 evt = document.createEvent("Event"); |
51 evt.initEvent("beforeload"); | 59 evt.initEvent("beforeload"); |
52 return safari.self.tab.canLoad(evt, {type: "proxy", payload: message}); | 60 return safari.self.tab.canLoad(evt, {type: "proxy", payload: message}); |
53 }, | 61 }, |
54 checkResult: function(result) | 62 checkResult: function(result) |
55 { | 63 { |
56 if (!result.succeed) | 64 if (!result.succeed) |
57 throw result.error; | 65 throw result.error; |
58 }, | 66 }, |
59 deserializeResult: function(result) | 67 deserializeResult: function(result) |
60 { | 68 { |
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
272 if (objectInfo.isFunction) | 280 if (objectInfo.isFunction) |
273 obj.prototype = this.getProperty(objectId, "prototype"); | 281 obj.prototype = this.getProperty(objectId, "prototype"); |
274 | 282 |
275 return obj; | 283 return obj; |
276 } | 284 } |
277 }; | 285 }; |
278 | 286 |
279 | 287 |
280 /* Web request blocking */ | 288 /* Web request blocking */ |
281 | 289 |
282 document.addEventListener("beforeload", function(event) | 290 var canLoad = function(event) |
283 { | 291 { |
284 var type; | 292 var type; |
285 | 293 switch (event.target.localName) |
286 switch(event.target.localName) | |
287 { | 294 { |
288 case "frame": | 295 case "frame": |
289 case "iframe": | 296 case "iframe": |
290 type = "sub_frame"; | 297 type = "sub_frame"; |
291 break; | 298 break; |
292 case "img": | 299 case "img": |
293 type = "image"; | 300 type = "image"; |
294 break; | 301 break; |
295 case "object": | 302 case "object": |
296 case "embed": | 303 case "embed": |
297 type = "object"; | 304 type = "object"; |
298 break; | 305 break; |
299 case "script": | 306 case "script": |
300 type = "script"; | 307 type = "script"; |
301 break; | 308 break; |
302 case "link": | 309 case "link": |
303 if (/\bstylesheet\b/i.test(event.target.rel)) | 310 if (/\bstylesheet\b/i.test(event.target.rel)) |
304 { | 311 { |
305 type = "stylesheet"; | 312 type = "stylesheet"; |
306 break; | 313 break; |
307 } | 314 } |
308 default: | 315 default: |
309 type = "other"; | 316 type = "other"; |
310 } | 317 } |
311 | 318 |
312 if (!safari.self.tab.canLoad( | 319 return safari.self.tab.canLoad( |
313 event, { | 320 event, { |
314 type: "webRequest", | 321 type: "webRequest", |
315 payload: { | 322 payload: { |
316 url: event.url, | 323 url: event.url, |
317 type: type, | 324 type: type, |
318 documentUrl: document.location.href, | 325 documentUrl: document.location.href, |
319 isTopLevel: window == window.top | 326 isTopLevel: window == window.top, |
| 327 isDocumentHidden: document.hidden |
320 } | 328 } |
321 } | 329 } |
322 )) | 330 ); |
| 331 }; |
| 332 |
| 333 document.addEventListener("beforeload", function(event) |
| 334 { |
| 335 var result; |
| 336 if (event.target._blocked || (result = canLoad(event)) == "blocked") |
323 { | 337 { |
324 event.preventDefault(); | 338 event.preventDefault(); |
325 | 339 |
326 // Safari doesn't dispatch an "error" or "load" event when preventing an | 340 // Safari doesn't dispatch an "error" or "load" event when preventing an |
327 // element from loading by cancelling the "beforeload" event. So we have | 341 // element from loading by cancelling the "beforeload" event. So we have |
328 // to dispatch it manually. Otherwise element collapsing wouldn't work. | 342 // to dispatch it manually. Otherwise element collapsing wouldn't work. |
329 var evt = document.createEvent("Event"); | 343 var evt = document.createEvent("Event"); |
330 evt.initEvent(type == "sub_frame" ? "load" : "error"); | 344 evt.initEvent(/^i?frame$/.test(event.target.localName) ? "load" : "error")
; |
331 event.target.dispatchEvent(evt); | 345 event.target.dispatchEvent(evt); |
332 } | 346 } |
| 347 else if (result == "deferred") |
| 348 deferredBeforeLoadEvents.push(event); |
333 }, true); | 349 }, true); |
334 | 350 |
335 | 351 |
336 /* API */ | 352 /* API */ |
337 | 353 |
338 ext.backgroundPage = { | 354 ext.backgroundPage = { |
339 sendMessage: function(message, responseCallback) | 355 sendMessage: function(message, responseCallback) |
340 { | 356 { |
341 _sendMessage( | 357 _sendMessage( |
342 message, responseCallback, | 358 // message payload |
343 safari.self.tab, safari.self, | 359 message, |
| 360 // response callback |
| 361 function(response) |
| 362 { |
| 363 if (response.deferred) |
| 364 deferredMessages.push([message, responseCallback]); |
| 365 else if (responseCallback) |
| 366 responseCallback(response.payload); |
| 367 }, |
| 368 // message dispatcher |
| 369 safari.self.tab, |
| 370 // response event target |
| 371 safari.self, |
| 372 // extra data |
344 { | 373 { |
345 documentUrl: document.location.href, | 374 documentUrl: document.location.href, |
346 isTopLevel: window == window.top | 375 isTopLevel: window == window.top, |
| 376 isDocumentHidden: document.hidden |
347 } | 377 } |
348 ); | 378 ); |
349 }, | 379 }, |
350 getWindow: function() | 380 getWindow: function() |
351 { | 381 { |
352 return proxy.getObject(0); | 382 return proxy.getObject(0); |
353 } | 383 } |
354 }; | 384 }; |
355 | 385 |
356 ext.onMessage = new ContentMessageEventTarget(); | 386 ext.onMessage = new ContentMessageEventTarget(); |
357 | 387 |
358 | 388 |
| 389 // Starting with Safari 7.0 content scripts also run in preloaded pages. |
| 390 // However our high-level code doesn't know anything about preloaded pages |
| 391 // and expects only one page running in each tab. So we have to defer |
| 392 // all requests to the background page, until the preloaded page is shown. |
| 393 |
| 394 var deferredMessages = []; |
| 395 var deferredBeforeLoadEvents = []; |
| 396 |
| 397 document.addEventListener("visibilitychange", function() |
| 398 { |
| 399 if (document.hidden) |
| 400 return; |
| 401 |
| 402 // notify background page, that it can catch up on onLoading events |
| 403 if (window == window.top) |
| 404 safari.self.tab.dispatchMessage("show"); |
| 405 |
| 406 // catch up on deferred messages |
| 407 for (var i = 0; i < deferredMessages.length; i++) |
| 408 ext.backgroundPage.sendMessage.apply(null, deferredMessages[i]); |
| 409 deferredMessages = []; |
| 410 |
| 411 // catch up on deferred resource blocking |
| 412 for (var i = 0; i < deferredBeforeLoadEvents.length; i++) |
| 413 { |
| 414 var result = canLoad(deferredBeforeLoadEvents[i]); |
| 415 if (result == "blocked") |
| 416 { |
| 417 // if at least one script was supposed to be blocked, |
| 418 // we have to reload the preloaded page. Executed scripts |
| 419 // won't undo themselves by removing them from the DOM |
| 420 var element = deferredBeforeLoadEvents[i].target; |
| 421 if (element.localName == "script") |
| 422 { |
| 423 document.documentElement.style.display = "none"; |
| 424 document.location.reload(); |
| 425 return; |
| 426 } |
| 427 |
| 428 // other resources that were supposed to be blocked are replaced |
| 429 // with an identical clone, in order to trigger "beforeload" again |
| 430 if (element.parentNode) |
| 431 { |
| 432 var clone = element.cloneNode(true); |
| 433 clone._blocked = true; // remember that this should be blocked, |
| 434 // to avoid checking twice |
| 435 element.parentNode.replaceChild(clone, element); |
| 436 } |
| 437 } |
| 438 } |
| 439 deferredBeforeLoadEvents = []; |
| 440 }); |
| 441 |
| 442 |
359 // Safari does not pass the element which the context menu is refering to | 443 // Safari does not pass the element which the context menu is refering to |
360 // so we need to add it to the event's user info. | 444 // so we need to add it to the event's user info. |
361 document.addEventListener("contextmenu", function(event) | 445 document.addEventListener("contextmenu", function(event) |
362 { | 446 { |
363 var element = event.srcElement; | 447 var element = event.srcElement; |
364 safari.self.tab.setContextMenuEventUserInfo(event, { | 448 safari.self.tab.setContextMenuEventUserInfo(event, { |
365 srcUrl: ("src" in element) ? element.src : null, | 449 srcUrl: ("src" in element) ? element.src : null, |
366 tagName: element.localName | 450 tagName: element.localName |
367 }); | 451 }); |
368 }, false); | 452 }, false); |
369 })(); | 453 })(); |
OLD | NEW |