| Index: safari/ext/background.js |
| =================================================================== |
| --- a/safari/ext/background.js |
| +++ b/safari/ext/background.js |
| @@ -188,307 +188,305 @@ |
| } |
| }; |
| - if (safari.extension.globalPage.contentWindow == window) |
| - { |
| - /* Background page proxy */ |
| - var proxy = { |
| - tabs: [], |
| - objects: [], |
| + /* Background page proxy */ |
| - registerObject: function(obj, objects) |
| + var proxy = { |
| + tabs: [], |
| + objects: [], |
| + |
| + registerObject: function(obj, objects) |
| + { |
| + var objectId = objects.indexOf(obj); |
| + |
| + if (objectId == -1) |
| + objectId = objects.push(obj) - 1; |
| + |
| + return objectId; |
| + }, |
| + serializeSequence: function(sequence, objects, memo) |
| + { |
| + if (!memo) |
| + memo = {specs: [], arrays: []}; |
| + |
| + var items = []; |
| + for (var i = 0; i < sequence.length; i++) |
| + items.push(this.serialize(sequence[i], objects, memo)); |
| + |
| + return items; |
| + }, |
| + serialize: function(obj, objects, memo) |
| + { |
| + if (typeof obj == "object" && obj != null || typeof obj == "function") |
| { |
| - var objectId = objects.indexOf(obj); |
| + if (obj.constructor == Array) |
| + { |
| + if (!memo) |
| + memo = {specs: [], arrays: []}; |
| - if (objectId == -1) |
| - objectId = objects.push(obj) - 1; |
| + var idx = memo.arrays.indexOf(obj); |
| + if (idx != -1) |
| + return memo.specs[idx]; |
| - return objectId; |
| - }, |
| - serializeSequence: function(sequence, objects, memo) |
| + var spec = {type: "array"}; |
| + memo.specs.push(spec); |
| + memo.arrays.push(obj); |
| + |
| + spec.items = this.serializeSequence(obj, objects, memo); |
| + return spec; |
| + } |
| + |
| + if (obj.constructor != Date && obj.constructor != RegExp) |
| + return {type: "object", objectId: this.registerObject(obj, objects)}; |
| + } |
| + |
| + return {type: "value", value: obj}; |
| + }, |
| + createCallback: function(callbackId, tab) |
| + { |
| + var proxy = this; |
| + |
| + return function() |
| { |
| - if (!memo) |
| - memo = {specs: [], arrays: []}; |
| + var idx = proxy.tabs.indexOf(tab); |
| - var items = []; |
| - for (var i = 0; i < sequence.length; i++) |
| - items.push(this.serialize(sequence[i], objects, memo)); |
| + if (idx != -1) { |
| + var objects = proxy.objects[idx]; |
| - return items; |
| - }, |
| - serialize: function(obj, objects, memo) |
| + tab.page.dispatchMessage("proxyCallback", |
| + { |
| + callbackId: callbackId, |
| + contextId: proxy.registerObject(this, objects), |
| + args: proxy.serializeSequence(arguments, objects) |
| + }); |
| + } |
| + }; |
| + }, |
| + deserialize: function(spec, objects, tab, memo) |
| + { |
| + switch (spec.type) |
| { |
| - if (typeof obj == "object" && obj != null || typeof obj == "function") |
| + case "value": |
| + return spec.value; |
| + case "hosted": |
| + return objects[spec.objectId]; |
| + case "callback": |
| + return this.createCallback(spec.callbackId, tab); |
| + case "object": |
| + case "array": |
| + if (!memo) |
| + memo = {specs: [], objects: []}; |
| + |
| + var idx = memo.specs.indexOf(spec); |
| + if (idx != -1) |
| + return memo.objects[idx]; |
| + |
| + var obj; |
| + if (spec.type == "array") |
| + obj = []; |
| + else |
| + obj = {}; |
| + |
| + memo.specs.push(spec); |
| + memo.objects.push(obj); |
| + |
| + if (spec.type == "array") |
| + for (var i = 0; i < spec.items.length; i++) |
| + obj.push(this.deserialize(spec.items[i], objects, tab, memo)); |
| + else |
| + for (var k in spec.properties) |
| + obj[k] = this.deserialize(spec.properties[k], objects, tab, memo); |
| + |
| + return obj; |
| + } |
| + }, |
| + createObjectCache: function(tab) |
| + { |
| + var objects = [window]; |
| + |
| + this.tabs.push(tab); |
| + this.objects.push(objects); |
| + |
| + tab.addEventListener("close", function() |
| + { |
| + var idx = this.tabs.indexOf(tab); |
| + |
| + if (idx != -1) |
| { |
| - if (obj.constructor == Array) |
| + this.tabs.splice(idx, 1); |
| + this.objects.splice(idx, 1); |
| + } |
| + }.bind(this)); |
| + |
| + return objects; |
| + }, |
| + getObjectCache: function(tab) |
| + { |
| + var idx = this.tabs.indexOf(tab); |
| + var objects; |
| + |
| + if (idx != -1) |
| + objects = this.objects[idx]; |
| + else |
| + objects = this.objects[idx] = this.createObjectCache(tab); |
| + |
| + return objects; |
| + }, |
| + fail: function(error) |
| + { |
| + if (error instanceof Error) |
| + error = error.message; |
| + return {succeed: false, error: error}; |
| + }, |
| + _handleMessage: function(message, tab) |
| + { |
| + var objects = this.getObjectCache(tab); |
| + |
| + switch (message.type) |
| + { |
| + case "getProperty": |
| + var obj = objects[message.objectId]; |
| + |
| + try |
| { |
| - if (!memo) |
| - memo = {specs: [], arrays: []}; |
| - |
| - var idx = memo.arrays.indexOf(obj); |
| - if (idx != -1) |
| - return memo.specs[idx]; |
| - |
| - var spec = {type: "array"}; |
| - memo.specs.push(spec); |
| - memo.arrays.push(obj); |
| - |
| - spec.items = this.serializeSequence(obj, objects, memo); |
| - return spec; |
| + var value = obj[message.property]; |
| + } |
| + catch (e) |
| + { |
| + return this.fail(e); |
| } |
| - if (obj.constructor != Date && obj.constructor != RegExp) |
| - return {type: "object", objectId: this.registerObject(obj, objects)}; |
| + return {succeed: true, result: this.serialize(value, objects)}; |
| + case "setProperty": |
| + var obj = objects[message.objectId]; |
| + var value = this.deserialize(message.value, objects, tab); |
| + |
| + try |
| + { |
| + obj[message.property] = value; |
| + } |
| + catch (e) |
| + { |
| + return this.fail(e); |
| + } |
| + |
| + return {succeed: true}; |
| + case "callFunction": |
| + var func = objects[message.functionId]; |
| + var context = objects[message.contextId]; |
| + |
| + var args = []; |
| + for (var i = 0; i < message.args.length; i++) |
| + args.push(this.deserialize(message.args[i], objects, tab)); |
| + |
| + try |
| + { |
| + var result = func.apply(context, args); |
| + } |
| + catch (e) |
| + { |
| + return this.fail(e); |
| + } |
| + |
| + return {succeed: true, result: this.serialize(result, objects)}; |
| + case "inspectObject": |
| + var obj = objects[message.objectId]; |
| + var objectInfo = {properties: {}, isFunction: typeof obj == "function"}; |
| + |
| + Object.getOwnPropertyNames(obj).forEach(function(prop) |
| + { |
| + objectInfo.properties[prop] = { |
| + enumerable: Object.prototype.propertyIsEnumerable.call(obj, prop) |
| + }; |
| + }); |
| + |
| + if (obj.__proto__) |
| + objectInfo.prototypeId = this.registerObject(obj.__proto__, objects); |
| + |
| + if (obj == Object.prototype) |
| + objectInfo.prototypeOf = "Object"; |
| + if (obj == Function.prototype) |
| + objectInfo.prototypeOf = "Function"; |
| + |
| + return objectInfo; |
| + } |
| + } |
| + }; |
| + |
| + |
| + /* Web request blocking */ |
| + |
| + ext.webRequest = { |
| + onBeforeRequest: { |
| + _listeners: [], |
| + _urlPatterns: [], |
| + |
| + _handleMessage: function(message, tab) |
| + { |
| + tab = new Tab(tab); |
| + |
| + for (var i = 0; i < this._listeners.length; i++) |
| + { |
| + var regex = this._urlPatterns[i]; |
| + |
| + if ((!regex || regex.test(message.url)) && this._listeners[i](message.url, message.type, tab, 0, -1) === false) |
| + return false; |
| } |
| - return {type: "value", value: obj}; |
| + return true; |
| }, |
| - createCallback: function(callbackId, tab) |
| + addListener: function(listener, urls) |
| { |
| - var proxy = this; |
| + var regex; |
| - return function() |
| - { |
| - var idx = proxy.tabs.indexOf(tab); |
| + if (urls) |
| + regex = new RegExp("^(?:" + urls.map(function(url) |
| + { |
| + return url.split("*").map(function(s) |
| + { |
| + return s.replace(/([.?+^$[\]\\(){}|-])/g, "\\$1"); |
| + }).join(".*"); |
| + }).join("|") + ")($|[?#])"); |
| - if (idx != -1) { |
| - var objects = proxy.objects[idx]; |
| - |
| - tab.page.dispatchMessage("proxyCallback", |
| - { |
| - callbackId: callbackId, |
| - contextId: proxy.registerObject(this, objects), |
| - args: proxy.serializeSequence(arguments, objects) |
| - }); |
| - } |
| - }; |
| + this._listeners.push(listener); |
| + this._urlPatterns.push(regex); |
| }, |
| - deserialize: function(spec, objects, tab, memo) |
| + removeListener: function(listener) |
| { |
| - switch (spec.type) |
| - { |
| - case "value": |
| - return spec.value; |
| - case "hosted": |
| - return objects[spec.objectId]; |
| - case "callback": |
| - return this.createCallback(spec.callbackId, tab); |
| - case "object": |
| - case "array": |
| - if (!memo) |
| - memo = {specs: [], objects: []}; |
| - |
| - var idx = memo.specs.indexOf(spec); |
| - if (idx != -1) |
| - return memo.objects[idx]; |
| - |
| - var obj; |
| - if (spec.type == "array") |
| - obj = []; |
| - else |
| - obj = {}; |
| - |
| - memo.specs.push(spec); |
| - memo.objects.push(obj); |
| - |
| - if (spec.type == "array") |
| - for (var i = 0; i < spec.items.length; i++) |
| - obj.push(this.deserialize(spec.items[i], objects, tab, memo)); |
| - else |
| - for (var k in spec.properties) |
| - obj[k] = this.deserialize(spec.properties[k], objects, tab, memo); |
| - |
| - return obj; |
| - } |
| - }, |
| - createObjectCache: function(tab) |
| - { |
| - var objects = [window]; |
| - |
| - this.tabs.push(tab); |
| - this.objects.push(objects); |
| - |
| - tab.addEventListener("close", function() |
| - { |
| - var idx = this.tabs.indexOf(tab); |
| - |
| - if (idx != -1) |
| - { |
| - this.tabs.splice(idx, 1); |
| - this.objects.splice(idx, 1); |
| - } |
| - }.bind(this)); |
| - |
| - return objects; |
| - }, |
| - getObjectCache: function(tab) |
| - { |
| - var idx = this.tabs.indexOf(tab); |
| - var objects; |
| + var idx = this._listeners.indexOf(listener); |
| if (idx != -1) |
| - objects = this.objects[idx]; |
| - else |
| - objects = this.objects[idx] = this.createObjectCache(tab); |
| - |
| - return objects; |
| - }, |
| - fail: function(error) |
| - { |
| - if (error instanceof Error) |
| - error = error.message; |
| - return {succeed: false, error: error}; |
| - }, |
| - _handleMessage: function(message, tab) |
| - { |
| - var objects = this.getObjectCache(tab); |
| - |
| - switch (message.type) |
| { |
| - case "getProperty": |
| - var obj = objects[message.objectId]; |
| - |
| - try |
| - { |
| - var value = obj[message.property]; |
| - } |
| - catch (e) |
| - { |
| - return this.fail(e); |
| - } |
| - |
| - return {succeed: true, result: this.serialize(value, objects)}; |
| - case "setProperty": |
| - var obj = objects[message.objectId]; |
| - var value = this.deserialize(message.value, objects, tab); |
| - |
| - try |
| - { |
| - obj[message.property] = value; |
| - } |
| - catch (e) |
| - { |
| - return this.fail(e); |
| - } |
| - |
| - return {succeed: true}; |
| - case "callFunction": |
| - var func = objects[message.functionId]; |
| - var context = objects[message.contextId]; |
| - |
| - var args = []; |
| - for (var i = 0; i < message.args.length; i++) |
| - args.push(this.deserialize(message.args[i], objects, tab)); |
| - |
| - try |
| - { |
| - var result = func.apply(context, args); |
| - } |
| - catch (e) |
| - { |
| - return this.fail(e); |
| - } |
| - |
| - return {succeed: true, result: this.serialize(result, objects)}; |
| - case "inspectObject": |
| - var obj = objects[message.objectId]; |
| - var objectInfo = {properties: {}, isFunction: typeof obj == "function"}; |
| - |
| - Object.getOwnPropertyNames(obj).forEach(function(prop) |
| - { |
| - objectInfo.properties[prop] = { |
| - enumerable: Object.prototype.propertyIsEnumerable.call(obj, prop) |
| - }; |
| - }); |
| - |
| - if (obj.__proto__) |
| - objectInfo.prototypeId = this.registerObject(obj.__proto__, objects); |
| - |
| - if (obj == Object.prototype) |
| - objectInfo.prototypeOf = "Object"; |
| - if (obj == Function.prototype) |
| - objectInfo.prototypeOf = "Function"; |
| - |
| - return objectInfo; |
| + this._listeners.splice(idx, 1); |
| + this._urlPatterns.splice(idx, 1); |
| } |
| } |
| - }; |
| + }, |
| + handlerBehaviorChanged: function() {} |
| + }; |
| - /* Web request blocking */ |
| + /* Synchronous messaging */ |
| - ext.webRequest = { |
| - onBeforeRequest: { |
| - _listeners: [], |
| - _urlPatterns: [], |
| + safari.application.addEventListener("message", function(event) |
| + { |
| + if (event.name == "canLoad") |
| + { |
| + var handler; |
| - _handleMessage: function(message, tab) |
| - { |
| - tab = new Tab(tab); |
| + switch (event.message.type) |
| + { |
| + case "proxy": |
| + handler = proxy; |
| + break; |
| + case "webRequest": |
| + handler = ext.webRequest.onBeforeRequest; |
| + break; |
| + } |
| - for (var i = 0; i < this._listeners.length; i++) |
| - { |
| - var regex = this._urlPatterns[i]; |
| - |
| - if ((!regex || regex.test(message.url)) && this._listeners[i](message.url, message.type, tab, 0, -1) === false) |
| - return false; |
| - } |
| - |
| - return true; |
| - }, |
| - addListener: function(listener, urls) |
| - { |
| - var regex; |
| - |
| - if (urls) |
| - regex = new RegExp("^(?:" + urls.map(function(url) |
| - { |
| - return url.split("*").map(function(s) |
| - { |
| - return s.replace(/([.?+^$[\]\\(){}|-])/g, "\\$1"); |
| - }).join(".*"); |
| - }).join("|") + ")($|[?#])"); |
| - |
| - this._listeners.push(listener); |
| - this._urlPatterns.push(regex); |
| - }, |
| - removeListener: function(listener) |
| - { |
| - var idx = this._listeners.indexOf(listener); |
| - |
| - if (idx != -1) |
| - { |
| - this._listeners.splice(idx, 1); |
| - this._urlPatterns.splice(idx, 1); |
| - } |
| - } |
| - }, |
| - handlerBehaviorChanged: function() {} |
| - }; |
| - |
| - |
| - /* Synchronous messaging */ |
| - |
| - safari.application.addEventListener("message", function(event) |
| - { |
| - if (event.name == "canLoad") |
| - { |
| - var handler; |
| - |
| - switch (event.message.type) |
| - { |
| - case "proxy": |
| - handler = proxy; |
| - break; |
| - case "webRequest": |
| - handler = ext.webRequest.onBeforeRequest; |
| - break; |
| - } |
| - |
| - event.message = handler._handleMessage(event.message.payload, event.target); |
| - } |
| - }, true); |
| - } |
| + event.message = handler._handleMessage(event.message.payload, event.target); |
| + } |
| + }, true); |
| /* API */ |
| @@ -528,15 +526,4 @@ |
| create: function(title, contexts, onclick) {}, |
| removeAll: function(callback) {} |
| }; |
| - |
| - // Safari will load the bubble once, and then show it everytime the icon is |
| - // clicked. While Chrome loads it everytime you click the icon. So in order to |
| - // force the same behavior in Safari, we are going to reload the page of the |
| - // bubble everytime it is shown. |
| - if (safari.extension.globalPage.contentWindow != window) |
| - safari.application.addEventListener("popover", function() |
| - { |
| - document.documentElement.style.display = "none"; |
| - document.location.reload(); |
| - }, true); |
| })(); |