| LEFT | RIGHT | 
|---|
| 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   /* Tabs */ | 20   /* Tabs */ | 
| 20 | 21 | 
| 21   var TabEventTarget = function() { | 22   var TabEventTarget = function() | 
|  | 23   { | 
| 22     WrappedEventTarget.apply(this, arguments); | 24     WrappedEventTarget.apply(this, arguments); | 
| 23   }; | 25   }; | 
| 24   TabEventTarget.prototype = { | 26   TabEventTarget.prototype = { | 
| 25     __proto__: WrappedEventTarget.prototype, | 27     __proto__: WrappedEventTarget.prototype, | 
| 26     _wrapListener: function(listener) { | 28     _wrapListener: function(listener) | 
| 27       return function(event) { | 29     { | 
| 28         listener(new Tab(event.target)); | 30       return function(event) | 
|  | 31       { | 
|  | 32         if (event.target instanceof SafariBrowserTab) | 
|  | 33           listener(new Tab(event.target)); | 
| 29       }; | 34       }; | 
| 30     } | 35     } | 
| 31   }; | 36   }; | 
| 32 | 37 | 
| 33   Tab = function(tab) { | 38   Tab = function(tab) | 
|  | 39   { | 
| 34     this._tab = tab; | 40     this._tab = tab; | 
| 35 | 41 | 
| 36     this._eventTarget = tab; | 42     this._eventTarget = tab; | 
| 37     this._messageDispatcher = tab.page; | 43     this._messageDispatcher = tab.page; | 
| 38 | 44 | 
| 39     this.url = tab.url; |  | 
| 40 |  | 
| 41     this.onBeforeNavigate = new TabEventTarget(tab, "beforeNavigate", false); | 45     this.onBeforeNavigate = new TabEventTarget(tab, "beforeNavigate", false); | 
| 42     this.onCompleted      = new TabEventTarget(tab, "navigate",       false); | 46     this.onCompleted = new TabEventTarget(tab, "navigate", false); | 
| 43     this.onActivated      = new TabEventTarget(tab, "activate",       false); | 47     this.onActivated = new TabEventTarget(tab, "activate", false); | 
| 44     this.onRemoved        = new TabEventTarget(tab, "close",          false); | 48     this.onRemoved = new TabEventTarget(tab, "close", false); | 
| 45   }; | 49   }; | 
| 46   Tab.prototype = { | 50   Tab.prototype = { | 
| 47     close: function() { | 51     get url() | 
|  | 52     { | 
|  | 53       return this._tab.url; | 
|  | 54     }, | 
|  | 55     close: function() | 
|  | 56     { | 
| 48       this._tab.close(); | 57       this._tab.close(); | 
| 49     }, | 58     }, | 
| 50     activate: function() { | 59     activate: function() | 
|  | 60     { | 
| 51       this._tab.activate(); | 61       this._tab.activate(); | 
| 52     }, | 62     }, | 
| 53     sendMessage: sendMessage, | 63     sendMessage: sendMessage, | 
| 54     pageAction: { | 64     pageAction: { | 
| 55       // there are no page actions in safari, so we use toolbar items instead | 65       // there are no page actions in safari, so we use toolbar items instead | 
| 56       setIcon: function(path) { | 66       setIcon: function(path) | 
|  | 67       { | 
| 57         safari.extension.toolbarItems[0].image = safari.extension.baseURI + path
     ; | 68         safari.extension.toolbarItems[0].image = safari.extension.baseURI + path
     ; | 
| 58       }, | 69       }, | 
| 59       setTitle: function(title) { | 70       setTitle: function(title) | 
|  | 71       { | 
| 60         safari.extension.toolbarItems[0].toolTip = title; | 72         safari.extension.toolbarItems[0].toolTip = title; | 
| 61       }, | 73       }, | 
| 62 | 74 | 
| 63       // toolbar items in safari can"t get hidden | 75       // toolbar items in safari can"t get hidden | 
| 64       hide: function() {}, | 76       hide: function() {}, | 
| 65       show: function() {} | 77       show: function() {} | 
| 66     } | 78     } | 
| 67   }; | 79   }; | 
| 68 | 80 | 
| 69   TabMap = function() { | 81   TabMap = function() | 
|  | 82   { | 
| 70     this._tabs = []; | 83     this._tabs = []; | 
| 71     this._values = []; | 84     this._values = []; | 
| 72 | 85 | 
| 73     this._onClosed = this._onClosed.bind(this); | 86     this._onClosed = this._onClosed.bind(this); | 
| 74   }; | 87   }; | 
| 75   TabMap.prototype = { | 88   TabMap.prototype = | 
|  | 89   { | 
| 76     get: function(tab) { | 90     get: function(tab) { | 
| 77       var idx; | 91       var idx; | 
| 78 | 92 | 
| 79       if (!tab || (idx = this._tabs.indexOf(tab._tab)) == -1) | 93       if (!tab || (idx = this._tabs.indexOf(tab._tab)) == -1) | 
| 80         return null; | 94         return null; | 
| 81 | 95 | 
| 82       return this._values[idx]; | 96       return this._values[idx]; | 
| 83     }, | 97     }, | 
| 84     set: function(tab, value) { | 98     set: function(tab, value) | 
|  | 99     { | 
| 85       var idx = this._tabs.indexOf(tab._tab); | 100       var idx = this._tabs.indexOf(tab._tab); | 
| 86 | 101 | 
| 87       if (idx != -1) | 102       if (idx != -1) | 
| 88         this._values[idx] = value; | 103         this._values[idx] = value; | 
| 89       else { | 104       else | 
|  | 105       { | 
| 90         this._tabs.push(tab._tab); | 106         this._tabs.push(tab._tab); | 
| 91         this._values.push(value); | 107         this._values.push(value); | 
| 92 | 108 | 
| 93         tab._tab.addEventListener("close", this._onClosed, false); | 109         tab._tab.addEventListener("close", this._onClosed, false); | 
| 94       } | 110       } | 
| 95     }, | 111     }, | 
| 96     has: function(tab) { | 112     has: function(tab) | 
|  | 113     { | 
| 97       return this._tabs.indexOf(tab._tab) != -1; | 114       return this._tabs.indexOf(tab._tab) != -1; | 
| 98     }, | 115     }, | 
| 99     clear: function() { | 116     clear: function() | 
|  | 117     { | 
| 100       while (this._tabs.length > 0) | 118       while (this._tabs.length > 0) | 
| 101         this._delete(this._tabs[0]); | 119         this._delete(this._tabs[0]); | 
| 102     }, | 120     }, | 
| 103     _delete: function(tab) { | 121     _delete: function(tab) | 
|  | 122     { | 
| 104       var idx = this._tabs.indexOf(tab); | 123       var idx = this._tabs.indexOf(tab); | 
| 105 | 124 | 
| 106       if (idx != -1) { | 125       if (idx != -1) | 
|  | 126       { | 
| 107         this._tabs.splice(idx, 1); | 127         this._tabs.splice(idx, 1); | 
| 108         this._values.splice(idx, 1); | 128         this._values.splice(idx, 1); | 
| 109 | 129 | 
| 110         tab.removeEventListener("close", this._onClosed, false); | 130         tab.removeEventListener("close", this._onClosed, false); | 
| 111       } | 131       } | 
| 112     }, | 132     }, | 
| 113     _onClosed: function(event) { | 133     _onClosed: function(event) | 
|  | 134     { | 
| 114       this._delete(event.target); | 135       this._delete(event.target); | 
| 115     } | 136     } | 
| 116   }; | 137   }; | 
| 117   TabMap.prototype["delete"] = function(tab) { | 138   TabMap.prototype["delete"] = function(tab) | 
|  | 139   { | 
| 118     this._delete(tab._tab); | 140     this._delete(tab._tab); | 
| 119   }; | 141   }; | 
| 120 | 142 | 
| 121 | 143 | 
| 122   /* Windows */ | 144   /* Windows */ | 
| 123 | 145 | 
| 124   Window = function(win) { | 146   Window = function(win) | 
|  | 147   { | 
| 125     this._win = win; | 148     this._win = win; | 
| 126     this.visible = win.visible; |  | 
| 127   } | 149   } | 
| 128   Window.prototype = { | 150   Window.prototype = { | 
| 129     getAllTabs: function(callback) { | 151     get visible() | 
| 130       callback(this._win.tabs.map(function(tab) { | 152     { | 
| 131         return new Tab(tab); | 153       return this._win.visible; | 
| 132       })); | 154     }, | 
| 133     }, | 155     getAllTabs: function(callback) | 
| 134     getActiveTab: function(callback) { | 156     { | 
|  | 157       callback(this._win.tabs.map(function(tab) { return new Tab(tab); })); | 
|  | 158     }, | 
|  | 159     getActiveTab: function(callback) | 
|  | 160     { | 
| 135       callback(new Tab(this._win.activeTab)); | 161       callback(new Tab(this._win.activeTab)); | 
| 136     }, | 162     }, | 
| 137     openTab: function(url, callback) { | 163     openTab: function(url, callback) | 
|  | 164     { | 
| 138       var tab = this._win.openTab(); | 165       var tab = this._win.openTab(); | 
| 139       tab.url = url; | 166       tab.url = url; | 
| 140 | 167 | 
| 141       if (callback) | 168       if (callback) | 
| 142         callback(new Tab(tab)); | 169         callback(new Tab(tab)); | 
| 143     } | 170     } | 
| 144   }; | 171   }; | 
| 145 | 172 | 
| 146   if (safari.extension.globalPage.contentWindow == window) { | 173   if (safari.extension.globalPage.contentWindow == window) | 
|  | 174   { | 
| 147     /* Background page proxy */ | 175     /* Background page proxy */ | 
| 148 | 176 | 
| 149     var proxy = { | 177     var proxy = { | 
| 150       tabs: [], | 178       tabs: [], | 
| 151       objects: [], | 179       objects: [], | 
| 152 | 180 | 
| 153       registerObject: function(obj, objects) { | 181       registerObject: function(obj, objects) | 
|  | 182       { | 
| 154         var objectId = objects.indexOf(obj); | 183         var objectId = objects.indexOf(obj); | 
| 155 | 184 | 
| 156         if (objectId == -1) | 185         if (objectId == -1) | 
| 157           objectId = objects.push(obj) - 1; | 186           objectId = objects.push(obj) - 1; | 
| 158 | 187 | 
| 159         return objectId; | 188         return objectId; | 
| 160       }, | 189       }, | 
| 161       serializeSequence: function(sequence, objects, memo) { | 190       serializeSequence: function(sequence, objects, memo) | 
|  | 191       { | 
| 162         if (!memo) | 192         if (!memo) | 
| 163           memo = {specs: [], arrays: []}; | 193           memo = {specs: [], arrays: []}; | 
| 164 | 194 | 
| 165         var items = []; | 195         var items = []; | 
| 166         for (var i = 0; i < sequence.length; i++) | 196         for (var i = 0; i < sequence.length; i++) | 
| 167           items.push(this.serialize(sequence[i], objects, memo)); | 197           items.push(this.serialize(sequence[i], objects, memo)); | 
| 168 | 198 | 
| 169         return items; | 199         return items; | 
| 170       }, | 200       }, | 
| 171       serialize: function(obj, objects, memo) { | 201       serialize: function(obj, objects, memo) | 
| 172         if (typeof obj == "function") | 202       { | 
| 173           return {type: "function", objectId: this.registerObject(obj, objects)}
     ; | 203         if (typeof obj == "object" && obj != null || typeof obj == "function") | 
| 174 | 204         { | 
| 175         if (typeof obj == "object" && obj != null) { | 205           if (obj.constructor == Array) | 
| 176           if (obj.constructor == Array) { | 206           { | 
| 177             if (!memo) | 207             if (!memo) | 
| 178               memo = {specs: [], arrays: []}; | 208               memo = {specs: [], arrays: []}; | 
| 179 | 209 | 
| 180             var idx = memo.arrays.indexOf(obj); | 210             var idx = memo.arrays.indexOf(obj); | 
| 181             if (idx != -1) | 211             if (idx != -1) | 
| 182               return memo.specs[idx]; | 212               return memo.specs[idx]; | 
| 183 | 213 | 
| 184             var spec = {type: "array"}; | 214             var spec = {type: "array"}; | 
| 185             memo.specs.push(spec); | 215             memo.specs.push(spec); | 
| 186             memo.arrays.push(obj); | 216             memo.arrays.push(obj); | 
| 187 | 217 | 
| 188             spec.items = this.serializeSequence(obj, objects, memo); | 218             spec.items = this.serializeSequence(obj, objects, memo); | 
| 189             return spec; | 219             return spec; | 
| 190           } | 220           } | 
| 191 | 221 | 
| 192           if (obj.constructor != Date) | 222           if (obj.constructor != Date && obj.constructor != RegExp) | 
| 193           if (obj.constructor != RegExp) |  | 
| 194             return {type: "object", objectId: this.registerObject(obj, objects)}
     ; | 223             return {type: "object", objectId: this.registerObject(obj, objects)}
     ; | 
| 195         } | 224         } | 
| 196 | 225 | 
| 197         return {type: "value", value: obj}; | 226         return {type: "value", value: obj}; | 
| 198       }, | 227       }, | 
| 199       createCallback: function(callbackId, tab) { | 228       createCallback: function(callbackId, tab) | 
|  | 229       { | 
| 200         var proxy = this; | 230         var proxy = this; | 
| 201 | 231 | 
| 202         return function() { | 232         return function() | 
|  | 233         { | 
| 203           var idx = proxy.tabs.indexOf(tab); | 234           var idx = proxy.tabs.indexOf(tab); | 
| 204 | 235 | 
| 205           if (idx != -1) { | 236           if (idx != -1) { | 
| 206             var objects = proxy.objects[idx]; | 237             var objects = proxy.objects[idx]; | 
| 207 | 238 | 
| 208             tab.page.dispatchMessage("proxyCallback", { | 239             tab.page.dispatchMessage("proxyCallback", | 
|  | 240             { | 
| 209               callbackId: callbackId, | 241               callbackId: callbackId, | 
| 210               contextId: proxy.registerObject(this, objects), | 242               contextId: proxy.registerObject(this, objects), | 
| 211               args: proxy.serializeSequence(arguments, objects) | 243               args: proxy.serializeSequence(arguments, objects) | 
| 212             }); | 244             }); | 
| 213           } | 245           } | 
| 214         }; | 246         }; | 
| 215       }, | 247       }, | 
| 216       deserialize: function(spec, objects, tab, memo) { | 248       deserialize: function(spec, objects, tab, memo) | 
| 217         switch (spec.type) { | 249       { | 
|  | 250         switch (spec.type) | 
|  | 251         { | 
| 218           case "value": | 252           case "value": | 
| 219             return spec.value; | 253             return spec.value; | 
| 220           case "hosted": | 254           case "hosted": | 
| 221             return objects[spec.objectId]; | 255             return objects[spec.objectId]; | 
| 222           case "callback": | 256           case "callback": | 
| 223             return this.createCallback(spec.callbackId, tab); | 257             return this.createCallback(spec.callbackId, tab); | 
| 224           case "object": | 258           case "object": | 
| 225           case "array": | 259           case "array": | 
| 226             if (!memo) | 260             if (!memo) | 
| 227               memo = {specs: [], objects: []}; | 261               memo = {specs: [], objects: []}; | 
| (...skipping 14 matching lines...) Expand all  Loading... | 
| 242             if (spec.type == "array") | 276             if (spec.type == "array") | 
| 243               for (var i = 0; i < spec.items.length; i++) | 277               for (var i = 0; i < spec.items.length; i++) | 
| 244                 obj.push(this.deserialize(spec.items[i], objects, tab, memo)); | 278                 obj.push(this.deserialize(spec.items[i], objects, tab, memo)); | 
| 245             else | 279             else | 
| 246               for (var k in spec.properties) | 280               for (var k in spec.properties) | 
| 247                 obj[k] = this.deserialize(spec.properties[k], objects, tab, memo
     ); | 281                 obj[k] = this.deserialize(spec.properties[k], objects, tab, memo
     ); | 
| 248 | 282 | 
| 249             return obj; | 283             return obj; | 
| 250         } | 284         } | 
| 251       }, | 285       }, | 
| 252       createObjectCache: function(tab) { | 286       createObjectCache: function(tab) | 
|  | 287       { | 
| 253         var objects = [window]; | 288         var objects = [window]; | 
| 254 | 289 | 
| 255         this.tabs.push(tab); | 290         this.tabs.push(tab); | 
| 256         this.objects.push(objects); | 291         this.objects.push(objects); | 
| 257 | 292 | 
| 258         tab.addEventListener("close", function() { | 293         tab.addEventListener("close", function() | 
|  | 294         { | 
| 259           var idx = this.tabs.indexOf(tab); | 295           var idx = this.tabs.indexOf(tab); | 
| 260 | 296 | 
| 261           if (idx != -1) { | 297           if (idx != -1) | 
|  | 298           { | 
| 262             this.tabs.splice(idx, 1); | 299             this.tabs.splice(idx, 1); | 
| 263             this.objects.splice(idx, 1); | 300             this.objects.splice(idx, 1); | 
| 264           } | 301           } | 
| 265         }.bind(this)); | 302         }.bind(this)); | 
| 266 | 303 | 
| 267         return objects; | 304         return objects; | 
| 268       }, | 305       }, | 
| 269       getObjectCache: function(tab) { | 306       getObjectCache: function(tab) | 
|  | 307       { | 
| 270         var idx = this.tabs.indexOf(tab); | 308         var idx = this.tabs.indexOf(tab); | 
| 271         var objects; | 309         var objects; | 
| 272 | 310 | 
| 273         if (idx != -1) | 311         if (idx != -1) | 
| 274           objects = this.objects[idx]; | 312           objects = this.objects[idx]; | 
| 275         else | 313         else | 
| 276           objects = this.objects[idx] = this.createObjectCache(tab); | 314           objects = this.objects[idx] = this.createObjectCache(tab); | 
| 277 | 315 | 
| 278         return objects; | 316         return objects; | 
| 279       }, | 317       }, | 
| 280       fail: function(error) { | 318       fail: function(error) | 
|  | 319       { | 
| 281         if (error instanceof Error) | 320         if (error instanceof Error) | 
| 282           error = error.message; | 321           error = error.message; | 
| 283         return {succeed: false, error: error}; | 322         return {succeed: false, error: error}; | 
| 284       }, | 323       }, | 
| 285       _handleMessage: function(message, tab) { | 324       _handleMessage: function(message, tab) | 
|  | 325       { | 
| 286         var objects = this.getObjectCache(tab); | 326         var objects = this.getObjectCache(tab); | 
| 287 | 327 | 
| 288         switch (message.type) { | 328         switch (message.type) | 
|  | 329         { | 
| 289           case "getProperty": | 330           case "getProperty": | 
| 290             var obj = objects[message.objectId]; | 331             var obj = objects[message.objectId]; | 
| 291 | 332 | 
| 292             try { | 333             try | 
|  | 334             { | 
| 293               var value = obj[message.property]; | 335               var value = obj[message.property]; | 
| 294             } catch (e) { | 336             } | 
|  | 337             catch (e) | 
|  | 338             { | 
| 295               return this.fail(e); | 339               return this.fail(e); | 
| 296             } | 340             } | 
| 297 | 341 | 
| 298             return {succeed: true, result: this.serialize(value, objects)}; | 342             return {succeed: true, result: this.serialize(value, objects)}; | 
| 299           case "setProperty": | 343           case "setProperty": | 
| 300             var obj = objects[message.objectId]; | 344             var obj = objects[message.objectId]; | 
| 301             var value = this.deserialize(message.value, objects, tab); | 345             var value = this.deserialize(message.value, objects, tab); | 
| 302 | 346 | 
| 303             try { | 347             try | 
|  | 348             { | 
| 304               obj[message.property] = value; | 349               obj[message.property] = value; | 
| 305             } catch (e) { | 350             } | 
|  | 351             catch (e) | 
|  | 352             { | 
| 306               return this.fail(e); | 353               return this.fail(e); | 
| 307             } | 354             } | 
| 308 | 355 | 
| 309             return {succeed: true}; | 356             return {succeed: true}; | 
| 310           case "callFunction": | 357           case "callFunction": | 
| 311             var func = objects[message.functionId]; | 358             var func = objects[message.functionId]; | 
| 312             var context = objects[message.contextId]; | 359             var context = objects[message.contextId]; | 
| 313 | 360 | 
| 314             var args = []; | 361             var args = []; | 
| 315             for (var i = 0; i < message.args.length; i++) | 362             for (var i = 0; i < message.args.length; i++) | 
| 316               args.push(this.deserialize(message.args[i], objects, tab)); | 363               args.push(this.deserialize(message.args[i], objects, tab)); | 
| 317 | 364 | 
| 318             try { | 365             try | 
|  | 366             { | 
| 319               var result = func.apply(context, args); | 367               var result = func.apply(context, args); | 
| 320             } catch (e) { | 368             } | 
|  | 369             catch (e) | 
|  | 370             { | 
| 321               return this.fail(e); | 371               return this.fail(e); | 
| 322             } | 372             } | 
| 323 | 373 | 
| 324             return {succeed: true, result: this.serialize(result, objects)}; | 374             return {succeed: true, result: this.serialize(result, objects)}; | 
| 325           case "inspectObject": | 375           case "inspectObject": | 
| 326             var obj = objects[message.objectId]; | 376             var obj = objects[message.objectId]; | 
| 327 | 377             var objectInfo = {properties: {}, isFunction: typeof obj == "functio
     n"}; | 
| 328             var prototype = Object.getPrototypeOf(obj); | 378 | 
| 329             var prototypeId; | 379             Object.getOwnPropertyNames(obj).forEach(function(prop) | 
| 330             if (prototype != null) | 380             { | 
| 331               prototypeId = this.registerObject(prototype, objects); | 381               objectInfo.properties[prop] = { | 
| 332             else | 382                 enumerable: Object.prototype.propertyIsEnumerable.call(obj, prop
     ) | 
| 333               prototypeId = null; | 383               }; | 
| 334 |  | 
| 335             var properties = {}; |  | 
| 336             Object.getOwnPropertyNames(obj).forEach(function(prop) { |  | 
| 337               if (obj != Object.prototype || prop == "constructor") |  | 
| 338                 properties[prop] = { |  | 
| 339                   enumerable: Object.getOwnPropertyDescriptor(obj, prop).enumera
     ble |  | 
| 340                 }; |  | 
| 341             }); | 384             }); | 
| 342 | 385 | 
| 343             return {prototypeId: prototypeId, properties: properties}; | 386             if (obj.__proto__) | 
|  | 387               objectInfo.prototypeId = this.registerObject(obj.__proto__, object
     s); | 
|  | 388 | 
|  | 389             if (obj == Object.prototype) | 
|  | 390               objectInfo.prototypeOf = "Object"; | 
|  | 391             if (obj == Function.prototype) | 
|  | 392               objectInfo.prototypeOf = "Function"; | 
|  | 393 | 
|  | 394             return objectInfo; | 
| 344         } | 395         } | 
| 345       } | 396       } | 
| 346     }; | 397     }; | 
| 347 | 398 | 
| 348 | 399 | 
| 349     /* Web request blocking */ | 400     /* Web request blocking */ | 
| 350 | 401 | 
| 351     ext.webRequest = { | 402     ext.webRequest = { | 
| 352       onBeforeRequest: { | 403       onBeforeRequest: { | 
| 353         _listeners: [], | 404         _listeners: [], | 
| 354         _urlPatterns: [], | 405         _urlPatterns: [], | 
| 355 | 406 | 
| 356         _handleMessage: function(message, tab) { | 407         _handleMessage: function(message, tab) | 
|  | 408         { | 
| 357           tab = new Tab(tab); | 409           tab = new Tab(tab); | 
| 358 | 410 | 
| 359           for (var i = 0; i < this._listeners.length; i++) { | 411           for (var i = 0; i < this._listeners.length; i++) | 
|  | 412           { | 
| 360             var regex = this._urlPatterns[i]; | 413             var regex = this._urlPatterns[i]; | 
| 361 | 414 | 
| 362             if (!regex || regex.test(message)) | 415             if ((!regex || regex.test(message.url)) && this._listeners[i](messag
     e.url, message.type, tab, 0, -1) === false) | 
| 363             if (this._listeners[i](message.url, message.type, tab, 0, -1) === fa
     lse) { |  | 
| 364               return false; | 416               return false; | 
| 365             } |  | 
| 366           } | 417           } | 
| 367 | 418 | 
| 368           return true; | 419           return true; | 
| 369         }, | 420         }, | 
| 370         addListener: function(listener, urls) { | 421         addListener: function(listener, urls) | 
|  | 422         { | 
| 371           var regex; | 423           var regex; | 
| 372 | 424 | 
| 373           if (urls) | 425           if (urls) | 
| 374             regex = new RegExp("^(?:" + urls.map(function(url) { | 426             regex = new RegExp("^(?:" + urls.map(function(url) | 
| 375               return url.split("*").map(function(s) { | 427             { | 
|  | 428               return url.split("*").map(function(s) | 
|  | 429               { | 
| 376                 return s.replace(/([.?+^$[\]\\(){}|-])/g, "\\$1"); | 430                 return s.replace(/([.?+^$[\]\\(){}|-])/g, "\\$1"); | 
| 377               }).join(".*"); | 431               }).join(".*"); | 
| 378             }).join("|") + ")($|[?#])"); | 432             }).join("|") + ")($|[?#])"); | 
| 379 | 433 | 
| 380           this._listeners.push(listener); | 434           this._listeners.push(listener); | 
| 381           this._urlPatterns.push(regex); | 435           this._urlPatterns.push(regex); | 
| 382         }, | 436         }, | 
| 383         removeListener: function(listener) { | 437         removeListener: function(listener) | 
|  | 438         { | 
| 384           var idx = this._listeners.indexOf(listener); | 439           var idx = this._listeners.indexOf(listener); | 
| 385 | 440 | 
| 386           if (idx != -1) { | 441           if (idx != -1) | 
|  | 442           { | 
| 387             this._listeners.splice(idx, 1); | 443             this._listeners.splice(idx, 1); | 
| 388             this._urlPatterns.splice(idx, 1); | 444             this._urlPatterns.splice(idx, 1); | 
| 389           } | 445           } | 
| 390         } | 446         } | 
| 391       }, | 447       }, | 
| 392       handlerBehaviorChanged: function() {} | 448       handlerBehaviorChanged: function() {} | 
| 393     }; | 449     }; | 
| 394 | 450 | 
| 395 | 451 | 
| 396     /* Synchronous messaging */ | 452     /* Synchronous messaging */ | 
| 397 | 453 | 
| 398     safari.application.addEventListener("message", function(event) { | 454     safari.application.addEventListener("message", function(event) | 
| 399       if (event.name == "canLoad") { | 455     { | 
|  | 456       if (event.name == "canLoad") | 
|  | 457       { | 
| 400         var handler; | 458         var handler; | 
| 401 | 459 | 
| 402         switch (event.message.type) { | 460         switch (event.message.type) | 
|  | 461         { | 
| 403           case "proxy": | 462           case "proxy": | 
| 404             handler = proxy; | 463             handler = proxy; | 
| 405             break; | 464             break; | 
| 406           case "webRequest": | 465           case "webRequest": | 
| 407             handler = ext.webRequest.onBeforeRequest; | 466             handler = ext.webRequest.onBeforeRequest; | 
| 408             break; | 467             break; | 
| 409         } | 468         } | 
| 410 | 469 | 
| 411         event.message = handler._handleMessage(event.message.payload, event.targ
     et); | 470         event.message = handler._handleMessage(event.message.payload, event.targ
     et); | 
| 412       } | 471       } | 
| 413     }, true); | 472     }, true); | 
| 414   } | 473   } | 
| 415 | 474 | 
| 416 | 475 | 
| 417   /* API */ | 476   /* API */ | 
| 418 | 477 | 
| 419   ext.windows = { | 478   ext.windows = { | 
| 420     getAll: function(callback) { | 479     getAll: function(callback) | 
| 421       callback(safari.application.browserWindows.map(function(win) { | 480     { | 
|  | 481       callback(safari.application.browserWindows.map(function(win) | 
|  | 482       { | 
| 422         return new Window(win); | 483         return new Window(win); | 
| 423       })); | 484       })); | 
| 424     }, | 485     }, | 
| 425     getLastFocused: function(callback) { | 486     getLastFocused: function(callback) | 
|  | 487     { | 
| 426       callback(new Window(safari.application.activeBrowserWindow)); | 488       callback(new Window(safari.application.activeBrowserWindow)); | 
| 427     } | 489     } | 
| 428   }; | 490   }; | 
| 429 | 491 | 
| 430   ext.tabs = { | 492   ext.tabs = { | 
| 431     onBeforeNavigate: new TabEventTarget(safari.application, "beforeNavigate", t
     rue), | 493     onBeforeNavigate: new TabEventTarget(safari.application, "beforeNavigate", t
     rue), | 
| 432     onCompleted:      new TabEventTarget(safari.application, "navigate",       t
     rue), | 494     onCompleted: new TabEventTarget(safari.application, "navigate", true), | 
| 433     onActivated:      new TabEventTarget(safari.application, "activate",       t
     rue), | 495     onActivated: new TabEventTarget(safari.application, "activate", true), | 
| 434     onRemoved:        new TabEventTarget(safari.application, "close",          t
     rue) | 496     onRemoved: new TabEventTarget(safari.application, "close", true) | 
| 435   }; | 497   }; | 
| 436 | 498 | 
| 437   ext.backgroundPage = { | 499   ext.backgroundPage = { | 
| 438     getWindow: function() { | 500     getWindow: function() | 
|  | 501     { | 
| 439       return safari.extension.globalPage.contentWindow; | 502       return safari.extension.globalPage.contentWindow; | 
| 440     } | 503     } | 
| 441   }; | 504   }; | 
| 442 | 505 | 
| 443   ext.onMessage = new MessageEventTarget(safari.application); | 506   ext.onMessage = new MessageEventTarget(safari.application); | 
|  | 507 | 
|  | 508 | 
|  | 509   // Safari will load the bubble once, and then show it everytime the icon is | 
|  | 510   // clicked. While Chrome loads it everytime you click the icon. So in order to | 
|  | 511   // force the same behavior in Safari, we are going to reload the page of the | 
|  | 512   // bubble everytime it is shown. | 
|  | 513   if (safari.extension.globalPage.contentWindow != window) | 
|  | 514     safari.application.addEventListener("popover", function() | 
|  | 515     { | 
|  | 516       document.documentElement.style.display = "none"; | 
|  | 517       document.location.reload(); | 
|  | 518     }, true); | 
| 444 })(); | 519 })(); | 
| LEFT | RIGHT | 
|---|