| Index: safari/ext/background.js |
| =================================================================== |
| --- a/safari/ext/background.js |
| +++ b/safari/ext/background.js |
| @@ -17,133 +17,65 @@ |
| (function() |
| { |
| - /* Pages */ |
| + /* Context menus */ |
| - var pages = Object.create(null); |
| - var pageCounter = 0; |
| + var contextMenuItems = new ext.PageMap(); |
| - var Page = function(id, tab, url) |
| + var ContextMenus = function(page) |
| { |
| - this._id = id; |
| - this._tab = tab; |
| - this._frames = [{url: new URL(url), parent: null}]; |
| + this._page = page; |
| + }; |
| + ContextMenus.prototype = { |
| + create: function(item) |
| + { |
| + var items = contextMenuItems.get(this._page); |
| + if (!items) |
| + contextMenuItems.set(this._page, items = []); |
| - 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); |
| - }; |
| - Page.prototype = { |
| - get url() |
| + items.push(item); |
| + }, |
| + removeAll: function() |
| { |
| - return this._frames[0].url; |
| - }, |
| - sendMessage: function(message, responseCallback) |
| - { |
| - this._messageProxy.sendMessage(message, responseCallback, {pageId: this._id}); |
| + contextMenuItems.delete(this._page); |
| } |
| }; |
| - ext._getPage = function(id) |
| + safari.application.addEventListener("contextmenu", function(event) |
| { |
| - return pages[id]; |
| - }; |
| - |
| - var isPageActive = function(page) |
| - { |
| - var tab = page._tab; |
| - var win = tab.browserWindow; |
| - return win && tab == win.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) |
| - { |
| - 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) |
| - { |
| - 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)) |
| + if (!event.userInfo) |
| 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); |
| + 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"; |
| + |
| + 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(page); |
| + }); |
| /* Browser actions */ |
| @@ -230,66 +162,173 @@ |
| }, true); |
| - /* Context menus */ |
| + /* Pages */ |
| - var contextMenuItems = new ext.PageMap(); |
| + var pages = Object.create(null); |
| + var pageCounter = 0; |
| - var ContextMenus = function(page) |
| + var Page = function(id, tab, url) |
| { |
| - this._page = page; |
| + this._id = id; |
| + this._tab = tab; |
| + this._frames = [{url: new 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); |
| }; |
| - ContextMenus.prototype = { |
| - create: function(item) |
| + Page.prototype = { |
| + get url() |
| { |
| - var items = contextMenuItems.get(this._page); |
| - if (!items) |
| - contextMenuItems.set(this._page, items = []); |
| - |
| - items.push(item); |
| + return this._frames[0].url; |
| }, |
| - removeAll: function() |
| + sendMessage: function(message, responseCallback) |
| { |
| - contextMenuItems.delete(this._page); |
| + this._messageProxy.sendMessage(message, responseCallback, {pageId: this._id}); |
| } |
| }; |
| - safari.application.addEventListener("contextmenu", function(event) |
| + ext._getPage = function(id) |
| { |
| - if (!event.userInfo) |
| + return pages[id]; |
| + }; |
| + |
| + var isPageActive = function(page) |
| + { |
| + var tab = page._tab; |
| + var win = tab.browserWindow; |
| + return win && tab == win.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); |
| + }; |
| + |
| + var addPage = function(tab, url, prerendered) |
| + { |
| + var pageId = ++pageCounter; |
| + |
| + if (!('_pages' in tab)) |
| + tab._pages = Object.create(null); |
| + |
| + var page = new Page(pageId, tab, url); |
| + pages[pageId] = tab._pages[pageId] = page; |
| + |
| + // 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 (!prerendered) |
| + replacePage(page); |
| + |
| + return pageId; |
| + }; |
| + |
| + ext.pages = { |
| + open: function(url, callback) |
| + { |
| + var tab = safari.application.activeBrowserWindow.openTab(); |
| + tab.url = url; |
| + |
| + if (callback) |
| + { |
| + 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) |
| + { |
| + 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; |
| - var pageId = event.userInfo.pageId; |
| - if (!pageId) |
| - 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); |
| - var page = pages[event.userInfo.pageId]; |
| - var items = contextMenuItems.get(page); |
| - if (!items) |
| - return; |
| + // We generally rely on content scripts to report new pages, |
| + // since Safari's extension API doesn't consider pre-rendered |
| + // pages. However, when the extension initializes we have to |
| + // use Safari's extension API to detect existing tabs. |
| + safari.application.browserWindows.forEach(function(win) |
| + { |
| + for (var i = 0; i < win.tabs.length; i++) |
| + { |
| + var tab = win.tabs[i]; |
| + var url = tab.url; |
| - var context = event.userInfo.tagName; |
| - if (context == "img") |
| - context = "image"; |
| - |
| - 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); |
| + // For the new tab page the url property is undefined. |
| + if (url) |
| + addPage(tab, url, false); |
| } |
| }); |
| - safari.application.addEventListener("command", function(event) |
| - { |
| - var page = pages[event.userInfo.pageId]; |
| - var items = contextMenuItems.get(page); |
| - |
| - items[event.command].onclick(page); |
| - }); |
| - |
| /* Web requests */ |
| @@ -525,25 +564,10 @@ |
| if (message.isTopLevel) |
| { |
| - pageId = ++pageCounter; |
| + pageId = addPage(tab, message.url, message.isPrerendered); |
| frameId = 0; |
| - if (!('_pages' in tab)) |
| - tab._pages = Object.create(null); |
| - |
| - var page = new Page(pageId, tab, message.url); |
| - pages[pageId] = tab._pages[pageId] = page; |
| - |
| - // 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); |
| - |
| - ext.pages.onLoading._dispatch(page); |
| + ext.pages.onLoading._dispatch(pages[pageId]); |
| } |
| else |
| { |
| @@ -594,7 +618,6 @@ |
| frameId = page._frames.length; |
| page._frames.push({url: new URL(message.url), parent: parentFrame}); |
| } |
| - |
| event.message = {pageId: pageId, frameId: frameId}; |
| break; |
| case "webRequest": |