| 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-2014 Eyeo GmbH | 3 * Copyright (C) 2006-2014 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 // the safari object is missing in frames created from javascript: URLs. | 20 // the safari object is missing in frames created from javascript: URLs. |
| 21 // So we have to fallback to the safari object from the parent frame. | 21 // So we have to fallback to the safari object from the parent frame. |
| 22 if (!("safari" in window)) | 22 if (!("safari" in window)) |
| 23 window.safari = window.parent.safari; | 23 window.safari = window.parent.safari; |
| 24 | 24 |
| 25 if (window == window.top) | 25 |
| 26 safari.self.tab.dispatchMessage("loading"); | 26 /* Intialization */ |
| 27 |
| 28 var beforeLoadEvent = document.createEvent("Event"); |
| 29 beforeLoadEvent.initEvent("beforeload"); |
| 30 |
| 31 var isTopLevel = window == window.top; |
| 32 var isPrerendered = document.visibilityState == "prerender"; |
| 33 |
| 34 var documentInfo = safari.self.tab.canLoad( |
| 35 beforeLoadEvent, |
| 36 { |
| 37 category: "loading", |
| 38 url: document.location.href, |
| 39 referrer: document.referrer, |
| 40 isTopLevel: isTopLevel, |
| 41 isPrerendered: isPrerendered |
| 42 } |
| 43 ); |
| 44 |
| 45 if (isTopLevel && isPrerendered) |
| 46 { |
| 47 var onVisibilitychange = function() |
| 48 { |
| 49 safari.self.tab.dispatchMessage("replaced", {pageId: documentInfo.pageId})
; |
| 50 document.removeEventListener("visibilitychange", onVisibilitychange); |
| 51 }; |
| 52 document.addEventListener("visibilitychange", onVisibilitychange); |
| 53 } |
| 27 | 54 |
| 28 | 55 |
| 29 /* Events */ | 56 /* Web requests */ |
| 30 | 57 |
| 31 var ContentMessageEventTarget = function() | 58 document.addEventListener("beforeload", function(event) |
| 32 { | 59 { |
| 33 MessageEventTarget.call(this, safari.self); | 60 // we don't block non-HTTP requests anyway, so we can bail out |
| 34 }; | 61 // without asking the background page. This is even necessary |
| 35 ContentMessageEventTarget.prototype = { | 62 // because passing large data (like a photo encoded as data: URL) |
| 36 __proto__: MessageEventTarget.prototype, | 63 // to the background page, freezes Safari. |
| 37 _getResponseDispatcher: function(event) | 64 if (!/^https?:/.test(event.url)) |
| 65 return; |
| 66 |
| 67 var type; |
| 68 switch(event.target.localName) |
| 38 { | 69 { |
| 39 return event.target.tab; | 70 case "frame": |
| 40 }, | 71 case "iframe": |
| 41 _getSenderDetails: function(event) | 72 type = "sub_frame"; |
| 73 break; |
| 74 case "img": |
| 75 type = "image"; |
| 76 break; |
| 77 case "object": |
| 78 case "embed": |
| 79 type = "object"; |
| 80 break; |
| 81 case "script": |
| 82 type = "script"; |
| 83 break; |
| 84 case "link": |
| 85 if (/\bstylesheet\b/i.test(event.target.rel)) |
| 86 { |
| 87 type = "stylesheet"; |
| 88 break; |
| 89 } |
| 90 default: |
| 91 type = "other"; |
| 92 } |
| 93 |
| 94 if (!safari.self.tab.canLoad( |
| 95 event, { |
| 96 category: "webRequest", |
| 97 url: event.url, |
| 98 type: type, |
| 99 pageId: documentInfo.pageId, |
| 100 frameId: documentInfo.frameId |
| 101 } |
| 102 )) |
| 42 { | 103 { |
| 43 return {}; | 104 event.preventDefault(); |
| 105 |
| 106 // Safari doesn't dispatch an "error" event when preventing an element |
| 107 // from loading by cancelling the "beforeload" event. So we have to |
| 108 // dispatch it manually. Otherwise element collapsing wouldn't work. |
| 109 if (type != "sub_frame") |
| 110 { |
| 111 var evt = document.createEvent("Event"); |
| 112 evt.initEvent(type == "error"); |
| 113 event.target.dispatchEvent(evt); |
| 114 } |
| 44 } | 115 } |
| 45 }; | 116 }, true); |
| 46 | 117 |
| 47 | 118 |
| 48 /* Background page proxy */ | 119 /* Context menus */ |
| 49 var proxy = { | 120 |
| 121 document.addEventListener("contextmenu", function(event) |
| 122 { |
| 123 var element = event.srcElement; |
| 124 safari.self.tab.setContextMenuEventUserInfo(event, { |
| 125 pageId: documentInfo.pageId, |
| 126 srcUrl: ("src" in element) ? element.src : null, |
| 127 tagName: element.localName |
| 128 }); |
| 129 }); |
| 130 |
| 131 |
| 132 /* Background page */ |
| 133 |
| 134 var backgroundPageProxy = { |
| 50 objects: [], | 135 objects: [], |
| 51 callbacks: [], | 136 callbacks: [], |
| 52 | 137 |
| 53 send: function(message) | 138 send: function(message) |
| 54 { | 139 { |
| 55 var evt = document.createEvent("Event"); | 140 message.category = "proxy"; |
| 56 evt.initEvent("beforeload"); | 141 message.pageId = documentInfo.pageId; |
| 57 return safari.self.tab.canLoad(evt, {type: "proxy", payload: message}); | 142 |
| 143 return safari.self.tab.canLoad(beforeLoadEvent, message); |
| 58 }, | 144 }, |
| 59 checkResult: function(result) | 145 checkResult: function(result) |
| 60 { | 146 { |
| 61 if (!result.succeed) | 147 if (!result.succeed) |
| 62 throw result.error; | 148 throw result.error; |
| 63 }, | 149 }, |
| 64 deserializeResult: function(result) | 150 deserializeResult: function(result) |
| 65 { | 151 { |
| 66 this.checkResult(result); | 152 this.checkResult(result); |
| 67 return this.deserialize(result.result); | 153 return this.deserialize(result.result); |
| 68 }, | 154 }, |
| 69 serialize: function(obj, memo) | 155 serialize: function(obj, memo) |
| 70 { | 156 { |
| 71 var objectId = this.objects.indexOf(obj); | 157 var objectId = this.objects.indexOf(obj); |
| 72 if (objectId != -1) | 158 if (objectId != -1) |
| 73 return {type: "hosted", objectId: objectId}; | 159 return {type: "hosted", objectId: objectId}; |
| 74 | 160 |
| 75 if (typeof obj == "function") | 161 if (typeof obj == "function") |
| 76 { | 162 { |
| 77 var callbackId = this.callbacks.indexOf(obj); | 163 var callbackId = this.callbacks.indexOf(obj); |
| 78 | |
| 79 if (callbackId == -1) | 164 if (callbackId == -1) |
| 80 { | |
| 81 callbackId = this.callbacks.push(obj) - 1; | 165 callbackId = this.callbacks.push(obj) - 1; |
| 82 | 166 |
| 83 safari.self.addEventListener("message", function(event) | 167 return {type: "callback", callbackId: callbackId, frameId: documentInfo.
frameId}; |
| 84 { | |
| 85 if (event.name == "proxyCallback") | |
| 86 if (event.message.callbackId == callbackId) | |
| 87 obj.apply( | |
| 88 this.getObject(event.message.contextId), | |
| 89 this.deserializeSequence(event.message.args) | |
| 90 ); | |
| 91 }.bind(this)); | |
| 92 } | |
| 93 | |
| 94 return {type: "callback", callbackId: callbackId}; | |
| 95 } | 168 } |
| 96 | 169 |
| 97 if (typeof obj == "object" && | 170 if (typeof obj == "object" && |
| 98 obj != null && | 171 obj != null && |
| 99 obj.constructor != Date && | 172 obj.constructor != Date && |
| 100 obj.constructor != RegExp) | 173 obj.constructor != RegExp) |
| 101 { | 174 { |
| 102 if (!memo) | 175 if (!memo) |
| 103 memo = {specs: [], objects: []}; | 176 memo = {specs: [], objects: []}; |
| 104 | 177 |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 219 functionId: objectId, | 292 functionId: objectId, |
| 220 contextId: proxy.getObjectId(this), | 293 contextId: proxy.getObjectId(this), |
| 221 args: Array.prototype.map.call( | 294 args: Array.prototype.map.call( |
| 222 arguments, | 295 arguments, |
| 223 proxy.serialize.bind(proxy) | 296 proxy.serialize.bind(proxy) |
| 224 ) | 297 ) |
| 225 }) | 298 }) |
| 226 ); | 299 ); |
| 227 }; | 300 }; |
| 228 }, | 301 }, |
| 229 getObject: function(objectId) { | 302 handleCallback: function(message) |
| 303 { |
| 304 this.callbacks[message.callbackId].apply( |
| 305 this.getObject(message.contextId), |
| 306 this.deserializeSequence(message.args) |
| 307 ); |
| 308 }, |
| 309 getObject: function(objectId) |
| 310 { |
| 230 var objectInfo = this.send({ | 311 var objectInfo = this.send({ |
| 231 type: "inspectObject", | 312 type: "inspectObject", |
| 232 objectId: objectId | 313 objectId: objectId |
| 233 }); | 314 }); |
| 234 | 315 |
| 235 var obj = this.objects[objectId]; | 316 var obj = this.objects[objectId]; |
| 236 if (obj) | 317 if (obj) |
| 237 Object.getOwnPropertyNames(obj).forEach(function(prop) { delete obj[prop
]; }); | 318 Object.getOwnPropertyNames(obj).forEach(function(prop) { delete obj[prop
]; }); |
| 238 else | 319 else |
| 239 { | 320 { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 274 property, objectInfo.properties[property].enumerable | 355 property, objectInfo.properties[property].enumerable |
| 275 )); | 356 )); |
| 276 | 357 |
| 277 if (objectInfo.isFunction) | 358 if (objectInfo.isFunction) |
| 278 obj.prototype = this.getProperty(objectId, "prototype"); | 359 obj.prototype = this.getProperty(objectId, "prototype"); |
| 279 | 360 |
| 280 return obj; | 361 return obj; |
| 281 } | 362 } |
| 282 }; | 363 }; |
| 283 | 364 |
| 365 ext.backgroundPage = { |
| 366 sendMessage: function(message, responseCallback) |
| 367 { |
| 368 messageProxy.sendMessage(message, responseCallback, documentInfo); |
| 369 }, |
| 370 getWindow: function() |
| 371 { |
| 372 return backgroundPageProxy.getObject(0); |
| 373 } |
| 374 }; |
| 375 |
| 284 | 376 |
| 285 /* Web request blocking */ | 377 /* Message processing */ |
| 286 | 378 |
| 287 document.addEventListener("beforeload", function(event) | 379 var messageProxy = new ext._MessageProxy(safari.self.tab); |
| 380 |
| 381 safari.self.addEventListener("message", function(event) |
| 288 { | 382 { |
| 289 // we don't block non-HTTP requests anyway, so we can bail out | 383 if (event.message.pageId == documentInfo.pageId) |
| 290 // without asking the background page. This is even necessary | 384 { |
| 291 // because passing large data (like a photo encoded as data: URL) | 385 if (event.name == "request") |
| 292 // to the background page, freezes Safari. | 386 { |
| 293 if (!/^https?:/.test(event.url)) | 387 messageProxy.handleRequest(event.message, {}); |
| 294 return; | 388 return; |
| 389 } |
| 295 | 390 |
| 296 var type; | 391 if (event.message.frameId == documentInfo.frameId) |
| 297 | 392 { |
| 298 switch(event.target.localName) | 393 switch (event.name) |
| 299 { | |
| 300 case "frame": | |
| 301 case "iframe": | |
| 302 type = "sub_frame"; | |
| 303 break; | |
| 304 case "img": | |
| 305 type = "image"; | |
| 306 break; | |
| 307 case "object": | |
| 308 case "embed": | |
| 309 type = "object"; | |
| 310 break; | |
| 311 case "script": | |
| 312 type = "script"; | |
| 313 break; | |
| 314 case "link": | |
| 315 if (/\bstylesheet\b/i.test(event.target.rel)) | |
| 316 { | 394 { |
| 317 type = "stylesheet"; | 395 case "response": |
| 318 break; | 396 messageProxy.handleResponse(event.message); |
| 319 } | 397 break; |
| 320 default: | 398 case "proxyCallback": |
| 321 type = "other"; | 399 backgroundPageProxy.handleCallback(event.message); |
| 322 } | 400 break; |
| 323 | |
| 324 if (!safari.self.tab.canLoad( | |
| 325 event, { | |
| 326 type: "webRequest", | |
| 327 payload: { | |
| 328 url: event.url, | |
| 329 type: type, | |
| 330 documentUrl: document.location.href, | |
| 331 isTopLevel: window == window.top | |
| 332 } | 401 } |
| 333 } | 402 } |
| 334 )) | |
| 335 { | |
| 336 event.preventDefault(); | |
| 337 | |
| 338 // Safari doesn't dispatch an "error" event when preventing an element | |
| 339 // from loading by cancelling the "beforeload" event. So we have to | |
| 340 // dispatch it manually. Otherwise element collapsing wouldn't work. | |
| 341 if (type != "sub_frame") | |
| 342 { | |
| 343 var evt = document.createEvent("Event"); | |
| 344 evt.initEvent("error"); | |
| 345 event.target.dispatchEvent(evt); | |
| 346 } | |
| 347 } | 403 } |
| 348 }, true); | 404 }); |
| 349 | |
| 350 | |
| 351 /* API */ | |
| 352 | |
| 353 ext.backgroundPage = { | |
| 354 sendMessage: function(message, responseCallback) | |
| 355 { | |
| 356 _sendMessage( | |
| 357 message, responseCallback, | |
| 358 safari.self.tab, safari.self, | |
| 359 { | |
| 360 documentUrl: document.location.href, | |
| 361 isTopLevel: window == window.top | |
| 362 } | |
| 363 ); | |
| 364 }, | |
| 365 getWindow: function() | |
| 366 { | |
| 367 return proxy.getObject(0); | |
| 368 } | |
| 369 }; | |
| 370 | |
| 371 ext.onMessage = new ContentMessageEventTarget(); | |
| 372 | |
| 373 | |
| 374 // Safari does not pass the element which the context menu is refering to | |
| 375 // so we need to add it to the event's user info. | |
| 376 document.addEventListener("contextmenu", function(event) | |
| 377 { | |
| 378 var element = event.srcElement; | |
| 379 safari.self.tab.setContextMenuEventUserInfo(event, { | |
| 380 srcUrl: ("src" in element) ? element.src : null, | |
| 381 tagName: element.localName | |
| 382 }); | |
| 383 }, false); | |
| 384 })(); | 405 })(); |
| OLD | NEW |