Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: safari/ext/background.js

Issue 5088751004942336: Issue 370 - Right-clicked element is removed independent of created filter (Closed)
Patch Set: Rebase to rev 3c9cea80c481 Created July 18, 2014, 8:54 a.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « qunit/tests/versionComparator.js ('k') | safari/ext/common.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: safari/ext/background.js
===================================================================
rename from safari/background.js
rename to safari/ext/background.js
--- a/safari/background.js
+++ b/safari/ext/background.js
@@ -1,6 +1,6 @@
/*
* This file is part of Adblock Plus <http://adblockplus.org/>,
- * Copyright (C) 2006-2013 Eyeo GmbH
+ * Copyright (C) 2006-2014 Eyeo GmbH
*
* Adblock Plus is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
@@ -17,526 +17,628 @@
(function()
{
- /* Tabs */
+ /* Pages */
- var TabEventTarget = function()
+ var pages = {__proto__: null};
+ var pageCounter = 0;
+
+ var Page = function(id, tab, url)
{
- WrappedEventTarget.apply(this, arguments);
+ this._id = id;
+ this._tab = tab;
+ this._frames = [{url: url, parent: null}];
+
+ if (tab.page)
+ this._messageProxy = new ext._MessageProxy(tab.page);
+ else
+ // while the new tab page is shown on Safari 7, the 'page' property
+ // of the tab is undefined, and we can't send messages to that page
+ this._messageProxy = {
+ handleRequest: function() {},
+ handleResponse: function() {},
+ sendMessage: function() {}
+ };
+
+ this.browserAction = new BrowserAction(this);
+ this.contextMenus = new ContextMenus(this);
};
- TabEventTarget.prototype = {
- __proto__: WrappedEventTarget.prototype,
- _wrapListener: function(listener)
- {
- return function(event)
- {
- if (event.target instanceof SafariBrowserTab)
- listener(new Tab(event.target));
- };
- }
- };
-
- var LoadingTabEventTarget = function(target)
- {
- WrappedEventTarget.call(this, target, "message", false);
- };
- LoadingTabEventTarget.prototype = {
- __proto__: WrappedEventTarget.prototype,
- _wrapListener: function(listener)
- {
- return function (event)
- {
- if (event.name == "loading" && event.message == event.target.url)
- listener(new Tab(event.target));
- };
- }
- };
-
- Tab = function(tab)
- {
- this._tab = tab;
-
- this._eventTarget = tab;
- this._messageDispatcher = tab.page;
-
- this.onLoading = new LoadingTabEventTarget(tab);
- this.onCompleted = new TabEventTarget(tab, "navigate", false);
- this.onActivated = new TabEventTarget(tab, "activate", false);
- this.onRemoved = new TabEventTarget(tab, "close", false);
- };
- Tab.prototype = {
+ Page.prototype = {
get url()
{
- return this._tab.url;
- },
- close: function()
- {
- this._tab.close();
+ return this._frames[0].url;
},
activate: function()
{
this._tab.activate();
},
- sendMessage: sendMessage,
- browserAction: {
- setIcon: function(path)
+ sendMessage: function(message, responseCallback)
+ {
+ this._messageProxy.sendMessage(message, responseCallback, {pageId: this._id});
+ }
+ };
+
+ var isPageActive = function(page)
+ {
+ var tab = page._tab;
+ return tab == tab.browserWindow.activeTab && page == tab._visiblePage;
+ };
+
+ var forgetPage = function(id)
+ {
+ ext._removeFromAllPageMaps(id);
+
+ delete pages[id]._tab._pages[id];
+ delete pages[id];
+ };
+
+ var replacePage = function(page)
+ {
+ var tab = page._tab;
+ tab._visiblePage = page;
+
+ for (var id in tab._pages)
+ {
+ if (id != page._id)
+ forgetPage(id);
+ }
+
+ if (isPageActive(page))
+ updateToolbarItemForPage(page, tab.browserWindow);
+ };
+
+ ext.pages = {
+ open: function(url, callback)
+ {
+ var tab = safari.application.activeBrowserWindow.openTab();
+ tab.url = url;
+
+ if (callback)
{
- safari.extension.toolbarItems[0].image = safari.extension.baseURI + path;
- },
- setTitle: function(title)
+ var onLoading = function(page)
+ {
+ if (page._tab == tab)
+ {
+ ext.pages.onLoading.removeListener(onLoading);
+ callback(page);
+ }
+ };
+ ext.pages.onLoading.addListener(onLoading);
+ }
+ },
+ query: function(info, callback)
+ {
+ var matchedPages = [];
+
+ for (var id in pages)
{
- safari.extension.toolbarItems[0].toolTip = title;
- },
- setBadge: function(badge)
+ var page = pages[id];
+ var win = page._tab.browserWindow;
+
+ if ("active" in info && info.active != isPageActive(page))
+ continue;
+ if ("lastFocusedWindow" in info && info.lastFocusedWindow != (win == safari.application.activeBrowserWindow))
+ continue;
+
+ matchedPages.push(page);
+ };
+
+ callback(matchedPages);
+ },
+ onLoading: new ext._EventTarget()
+ };
+
+ safari.application.addEventListener("close", function(event)
+ {
+ // this event is dispatched on closing windows and tabs. However when a
+ // window is closed, it is first dispatched on each tab in the window and
+ // then on the window itself. But we are only interested in closed tabs.
+ if (!(event.target instanceof SafariBrowserTab))
+ return;
+
+ // when a tab is closed, forget the previous page associated with that
+ // tab. Note that it wouldn't be sufficient do that when the old page
+ // is unloading, because Safari dispatches window.onunload only when
+ // reloading the page or following links, but not when closing the tab.
+ for (var id in event.target._pages)
+ forgetPage(id);
+ }, true);
+
+
+ /* Browser actions */
+
+ var toolbarItemProperties = {};
+
+ var getToolbarItemForWindow = function(win)
+ {
+ for (var i = 0; i < safari.extension.toolbarItems.length; i++)
+ {
+ var toolbarItem = safari.extension.toolbarItems[i];
+
+ if (toolbarItem.browserWindow == win)
+ return toolbarItem;
+ }
+
+ return null;
+ };
+
+ var updateToolbarItemForPage = function(page, win) {
+ var toolbarItem = getToolbarItemForWindow(win);
+ if (!toolbarItem)
+ return;
+
+ for (var name in toolbarItemProperties)
+ {
+ var property = toolbarItemProperties[name];
+
+ if (page && property.pages.has(page))
+ toolbarItem[name] = property.pages.get(page);
+ else
+ toolbarItem[name] = property.global;
+ }
+ };
+
+ var BrowserAction = function(page)
+ {
+ this._page = page;
+ };
+ BrowserAction.prototype = {
+ _set: function(name, value)
+ {
+ var toolbarItem = getToolbarItemForWindow(this._page._tab.browserWindow);
+ if (!toolbarItem)
+ return;
+
+ var property = toolbarItemProperties[name];
+ if (!property)
+ property = toolbarItemProperties[name] = {
+ pages: new ext.PageMap(),
+ global: toolbarItem[name]
+ };
+
+ property.pages.set(this._page, value);
+
+ if (isPageActive(this._page))
+ toolbarItem[name] = value;
+ },
+ setIcon: function(path)
+ {
+ this._set("image", safari.extension.baseURI + path.replace("$size", "16"));
+ },
+ setBadge: function(badge)
+ {
+ if (!badge)
+ this._set("badge", 0);
+ else if ("number" in badge)
+ this._set("badge", badge.number);
+ }
+ };
+
+ safari.application.addEventListener("activate", function(event)
+ {
+ // this event is also dispatched on windows that got focused. But we
+ // are only interested in tabs, which became active in their window.
+ if (!(event.target instanceof SafariBrowserTab))
+ return;
+
+ // update the toolbar item for the page visible in the tab that just
+ // became active. If we can't find that page (e.g. when a page was
+ // opened in a new tab, and our content script didn't run yet), the
+ // toolbar item of the window, is reset to its intial configuration.
+ updateToolbarItemForPage(event.target._visiblePage, event.target.browserWindow);
+ }, true);
+
+
+ /* Context menus */
+
+ var contextMenuItems = new ext.PageMap();
+
+ var ContextMenus = function(page)
+ {
+ this._page = page;
+ };
+ ContextMenus.prototype = {
+ create: function(item)
+ {
+ var items = contextMenuItems.get(this._page);
+ if (!items)
+ contextMenuItems.set(this._page, items = []);
+
+ items.push(item);
+ },
+ removeAll: function()
+ {
+ contextMenuItems.delete(this._page);
+ }
+ };
+
+ safari.application.addEventListener("contextmenu", function(event)
+ {
+ var pageId = event.userInfo.pageId;
+ if (!pageId)
+ return;
+
+ var page = pages[event.userInfo.pageId];
+ var items = contextMenuItems.get(page);
+ if (!items)
+ return;
+
+ var context = event.userInfo.tagName;
+ if (context == "img")
+ context = "image";
+ if (!event.userInfo.srcUrl)
+ context = null;
+
+ for (var i = 0; i < items.length; i++)
+ {
+ // Supported contexts are: all, audio, image, video
+ var menuItem = items[i];
+ if (menuItem.contexts.indexOf("all") == -1 && menuItem.contexts.indexOf(context) == -1)
+ continue;
+
+ event.contextMenu.appendContextMenuItem(i, menuItem.title);
+ }
+ });
+
+ safari.application.addEventListener("command", function(event)
+ {
+ var page = pages[event.userInfo.pageId];
+ var items = contextMenuItems.get(page);
+
+ items[event.command].onclick(event.userInfo.srcUrl, page);
+ });
+
+
+ /* Web requests */
+
+ ext.webRequest = {
+ onBeforeRequest: new ext._EventTarget(true),
+ handlerBehaviorChanged: function() {}
+ };
+
+
+ /* Background page */
+
+ ext.backgroundPage = {
+ getWindow: function()
+ {
+ return window;
+ }
+ };
+
+
+ /* Background page proxy (for access from content scripts) */
+
+ var backgroundPageProxy = {
+ cache: new ext.PageMap(),
+
+ 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")
{
- if (!badge)
- safari.extension.toolbarItems[0].badge = 0;
- else if ("number" in badge)
- safari.extension.toolbarItems[0].badge = badge.number;
+ if (obj.constructor == Array)
+ {
+ 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;
+ }
+
+ if (obj.constructor != Date && obj.constructor != RegExp)
+ return {type: "object", objectId: this.registerObject(obj, objects)};
+ }
+
+ return {type: "value", value: obj};
+ },
+ createCallback: function(callbackId, pageId, frameId)
+ {
+ var proxy = this;
+
+ return function()
+ {
+ var page = pages[pageId];
+ if (!page)
+ return;
+
+ var objects = proxy.cache.get(page);
+ if (!objects)
+ return;
+
+ page._tab.page.dispatchMessage("proxyCallback",
+ {
+ pageId: pageId,
+ frameId: frameId,
+ callbackId: callbackId,
+ contextId: proxy.registerObject(this, objects),
+ args: proxy.serializeSequence(arguments, objects)
+ });
+ };
+ },
+ deserialize: function(spec, objects, pageId, memo)
+ {
+ switch (spec.type)
+ {
+ case "value":
+ return spec.value;
+ case "hosted":
+ return objects[spec.objectId];
+ case "callback":
+ return this.createCallback(spec.callbackId, pageId, spec.frameId);
+ 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, pageId, memo));
+ else
+ for (var k in spec.properties)
+ obj[k] = this.deserialize(spec.properties[k], objects, pageId, memo);
+
+ return obj;
+ }
+ },
+ getObjectCache: function(page)
+ {
+ var objects = this.cache.get(page);
+ if (!objects)
+ {
+ objects = [window];
+ this.cache.set(page, objects);
+ }
+ return objects;
+ },
+ fail: function(error)
+ {
+ if (error instanceof Error)
+ error = error.message;
+ return {succeed: false, error: error};
+ },
+ handleMessage: function(message)
+ {
+ var objects = this.getObjectCache(pages[message.pageId]);
+
+ 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, message.pageId);
+
+ 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, message.pageId));
+
+ 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;
}
}
};
- TabMap = function()
+
+ /* Message processing */
+
+ safari.application.addEventListener("message", function(event)
{
- this._tabs = [];
- this._values = [];
+ switch (event.name)
+ {
+ case "canLoad":
+ switch (event.message.category)
+ {
+ case "loading":
+ var tab = event.target;
+ var message = event.message;
- this._onClosed = this._onClosed.bind(this);
- };
- TabMap.prototype =
- {
- get: function(tab) {
- var idx;
+ var pageId;
+ var frameId;
- if (!tab || (idx = this._tabs.indexOf(tab._tab)) == -1)
- return null;
+ if (message.isTopLevel)
+ {
+ pageId = ++pageCounter;
+ frameId = 0;
- return this._values[idx];
- },
- set: function(tab, value)
- {
- var idx = this._tabs.indexOf(tab._tab);
+ if (!('_pages' in tab))
+ tab._pages = {__proto__: null};
- if (idx != -1)
- this._values[idx] = value;
- else
- {
- this._tabs.push(tab._tab);
- this._values.push(value);
+ var page = new Page(pageId, tab, message.url);
+ pages[pageId] = tab._pages[pageId] = page;
- tab._tab.addEventListener("close", this._onClosed, false);
- }
- },
- has: function(tab)
- {
- return this._tabs.indexOf(tab._tab) != -1;
- },
- clear: function()
- {
- while (this._tabs.length > 0)
- this._delete(this._tabs[0]);
- },
- _delete: function(tab)
- {
- var idx = this._tabs.indexOf(tab);
+ // when a new page is shown, forget the previous page associated
+ // with its tab, and reset the toolbar item if necessary.
+ // Note that it wouldn't be sufficient to do that when the old
+ // page is unloading, because Safari dispatches window.onunload
+ // only when reloading the page or following links, but not when
+ // you enter a new URL in the address bar.
+ if (!message.isPrerendered)
+ replacePage(page);
- if (idx != -1)
- {
- this._tabs.splice(idx, 1);
- this._values.splice(idx, 1);
+ ext.pages.onLoading._dispatch(page);
+ }
+ else
+ {
+ var page;
+ var parentFrame;
- tab.removeEventListener("close", this._onClosed, false);
- }
- },
- _onClosed: function(event)
- {
- this._delete(event.target);
- }
- };
- TabMap.prototype["delete"] = function(tab)
- {
- this._delete(tab._tab);
- };
+ var lastPageId;
+ var lastPage;
+ var lastPageTopLevelFrame;
+ // find the parent frame and its page for this sub frame,
+ // by matching its referrer with the URL of frames previously
+ // loaded in the same tab. If there is more than one match,
+ // the most recent loaded page and frame is preferred.
+ for (var curPageId in tab._pages)
+ {
+ var curPage = pages[curPageId];
- /* Windows */
+ for (var i = 0; i < curPage._frames.length; i++)
+ {
+ var curFrame = curPage._frames[i];
- Window = function(win)
- {
- this._win = win;
- }
- Window.prototype = {
- get visible()
- {
- return this._win.visible;
- },
- getAllTabs: function(callback)
- {
- callback(this._win.tabs.map(function(tab) { return new Tab(tab); }));
- },
- getActiveTab: function(callback)
- {
- callback(new Tab(this._win.activeTab));
- },
- openTab: function(url, callback)
- {
- var tab = this._win.openTab();
- tab.url = url;
+ if (curFrame.url == message.referrer)
+ {
+ pageId = curPageId;
+ page = curPage;
+ parentFrame = curFrame;
+ }
- if (callback)
- callback(new Tab(tab));
- }
- };
+ if (i == 0)
+ {
+ lastPageId = curPageId;
+ lastPage = curPage;
+ lastPageTopLevelFrame = curFrame;
+ }
+ }
+ }
- if (safari.extension.globalPage.contentWindow == window)
- {
- /* Background page proxy */
+ // if we can't find the parent frame and its page, fall back to
+ // the page most recently loaded in the tab and its top level frame
+ if (!page)
+ {
+ pageId = lastPageId;
+ page = lastPage;
+ parentFrame = lastPageTopLevelFrame;
+ }
- 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")
- {
- if (obj.constructor == Array)
- {
- 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;
- }
-
- 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()
- {
- var idx = proxy.tabs.indexOf(tab);
-
- if (idx != -1) {
- var objects = proxy.objects[idx];
-
- 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)
- {
- 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;
-
- 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);
+ frameId = page._frames.length;
+ page._frames.push({url: message.url, parent: parentFrame});
}
- 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 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;
+ event.message = {pageId: pageId, frameId: frameId};
break;
case "webRequest":
- handler = ext.webRequest.onBeforeRequest;
+ var page = pages[event.message.pageId];
+
+ event.message = ext.webRequest.onBeforeRequest._dispatch(
+ event.message.url,
+ event.message.type,
+ page,
+ page._frames[event.message.frameId]
+ );
+ break;
+ case "proxy":
+ event.message = backgroundPageProxy.handleMessage(event.message);
break;
}
+ break;
+ case "request":
+ var page = pages[event.message.pageId];
+ var sender = {page: page, frame: page._frames[event.message.frameId]};
+ page._messageProxy.handleRequest(event.message, sender);
+ break;
+ case "response":
+ pages[event.message.pageId]._messageProxy.handleResponse(event.message);
+ break;
+ case "replaced":
+ // when a prerendered page is shown, forget the previous page
+ // associated with its tab, and reset the toolbar item if necessary.
+ // Note that it wouldn't be sufficient to do that when the old
+ // page is unloading, because Safari dispatches window.onunload
+ // only when reloading the page or following links, but not when
+ // the current page is replaced with a prerendered page.
+ replacePage(pages[event.message.pageId]);
+ break;
+ }
+ });
- event.message = handler._handleMessage(event.message.payload, event.target);
- }
- }, true);
- }
+ /* Storage */
- /* API */
-
- ext.windows = {
- getAll: function(callback)
- {
- callback(safari.application.browserWindows.map(function(win)
- {
- return new Window(win);
- }));
- },
- getLastFocused: function(callback)
- {
- callback(new Window(safari.application.activeBrowserWindow));
- }
- };
-
- ext.tabs = {
- onLoading: new LoadingTabEventTarget(safari.application),
- onCompleted: new TabEventTarget(safari.application, "navigate", true),
- onActivated: new TabEventTarget(safari.application, "activate", true),
- onRemoved: new TabEventTarget(safari.application, "close", true)
- };
-
- ext.backgroundPage = {
- getWindow: function()
- {
- return safari.extension.globalPage.contentWindow;
- }
- };
-
- ext.onMessage = new MessageEventTarget(safari.application);
-
- // TODO: Implement context menu
- ext.contextMenus = {
- 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);
+ ext.storage = safari.extension.settings;
})();
« no previous file with comments | « qunit/tests/versionComparator.js ('k') | safari/ext/common.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld