From ead0c2ecb1d1d7624065422b658a1b61079d3fa5 Mon Sep 17 00:00:00 2001 From: evgeny Date: Sun, 3 Jul 2016 04:19:31 +0300 Subject: initial --- README.md | 9 + bootstrap.js | 413 +++++++++++++++++++++++++++++++++++++++++ chrome.manifest | 2 + content/skin/overlay-bold.css | 5 + content/skin/overlay-small.css | 8 + content/skin/overlay.css | 16 ++ install.rdf | 19 ++ make_xpi.sh | 2 + options.xul | 16 ++ screenshots/1.png | Bin 0 -> 10562 bytes screenshots/2.png | Bin 0 -> 8301 bytes screenshots/3.png | Bin 0 -> 8478 bytes tablocation.xpi | Bin 0 -> 6201 bytes 13 files changed, 490 insertions(+) create mode 100644 README.md create mode 100644 bootstrap.js create mode 100644 chrome.manifest create mode 100644 content/skin/overlay-bold.css create mode 100644 content/skin/overlay-small.css create mode 100644 content/skin/overlay.css create mode 100644 install.rdf create mode 100755 make_xpi.sh create mode 100644 options.xul create mode 100644 screenshots/1.png create mode 100644 screenshots/2.png create mode 100644 screenshots/3.png create mode 100644 tablocation.xpi diff --git a/README.md b/README.md new file mode 100644 index 0000000..32aa8a4 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# tablocation + +The firefox addon. Tested on Firefox 38 under Linux. + +See the screenshots. + +![](/screenshots/1.png?raw=true) +![](/screenshots/2.png?raw=true) +![](/screenshots/3.png?raw=true) diff --git a/bootstrap.js b/bootstrap.js new file mode 100644 index 0000000..86a8a96 --- /dev/null +++ b/bootstrap.js @@ -0,0 +1,413 @@ +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import('resource://gre/modules/devtools/Console.jsm'); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +var consoleService = Cc["@mozilla.org/consoleservice;1"] + .getService(Ci.nsIConsoleService); +var wm = Cc["@mozilla.org/appshell/window-mediator;1"] + .getService(Ci.nsIWindowMediator); +var prefs = Cc["@mozilla.org/preferences-service;1"] + .getService(Ci.nsIPrefService) + .getBranch("extensions.tablocation."); +var styleSheetService = Cc["@mozilla.org/content/style-sheet-service;1"] + .getService(Ci.nsIStyleSheetService); + +var styleSheets = { + common: ["chrome://tablocation/skin/overlay.css"], + smaller: ["chrome://tablocation/skin/overlay-small.css"], + bold: ["chrome://tablocation/skin/overlay-bold.css"] +}; + +var LocationPreference = 0; +var SmallerPreference = false; +var BoldPreference = false; + +var Controller = { + bindWindowEvents: function(chromeWindow) { + if (chromeWindow._tabLocationBind) { + LOG("chromeWindow._tabLocationBind == true"); + return; + } + + chromeWindow._tabLocationBind = true; + var gBrowser = chromeWindow.gBrowser; + gBrowser.addEventListener('load', Controller.windowLoadListener, true); + //gBrowser.addEventListener('readystatechange', Controller.windowLoadListener, true); + gBrowser.addEventListener('DOMTitleChanged', Controller.tabsTitleChangedListener, true); + gBrowser.addTabsProgressListener(Controller.tabsProgressListener); + + var container = gBrowser.tabContainer; + container.addEventListener("TabOpen", Controller.tabOpenListener, false); + container.addEventListener("TabMove", Controller.tabMoveListener); + container.addEventListener("TabClose", Controller.tabCloseListener); + }, + unbindWindowEvents: function(chromeWindow) { + var gBrowser = chromeWindow.gBrowser; + gBrowser.removeEventListener('load', Controller.windowLoadListener, true); + //gBrowser.removeEventListener('readystatechange', Controller.windowLoadListener, true); + gBrowser.removeEventListener('DOMTitleChanged', Controller.tabsTitleChangedListener, true); + gBrowser.removeTabsProgressListener(Controller.tabsProgressListener); + + var container = gBrowser.tabContainer; + container.removeEventListener("TabOpen", Controller.tabOpenListener); + container.removeEventListener("TabMove", Controller.tabMoveListener); + container.removeEventListener("TabClose", Controller.tabCloseListener); + + delete chromeWindow._tabLocationBind; + }, + + // Listeners + windowLoadListener: function(e) { + Controller.setupWindow(this.ownerGlobal); + }, + tabOpenListener: function(e) { + var chromeWindow = e.target.ownerGlobal; + var browser = chromeWindow.gBrowser.getBrowserForTab(e.target); + var index = chromeWindow.gBrowser.getBrowserIndexForDocument(browser.contentDocument); + if (index != -1) { + Controller.setupTab(chromeWindow, index); + } + }, + tabMoveListener: function(e) { + var chromeWindow = e.target.ownerGlobal; + var browser = chromeWindow.gBrowser.getBrowserForTab(e.target); + var index = chromeWindow.gBrowser.getBrowserIndexForDocument(browser.contentDocument); + if (index != -1) { + Controller.setupTab(chromeWindow, index, true); + } + }, + tabCloseListener: function(e) { + var chromeWindow = e.target.ownerGlobal; + var browser = chromeWindow.gBrowser.getBrowserForTab(e.target); + var index = chromeWindow.gBrowser.getBrowserIndexForDocument(browser.contentDocument); + if (index != -1) { + //LOG("tabCloseListener: tearing tab down"); + Controller.tearDownTab(chromeWindow, index, true); + } + }, + tabsProgressListener: { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]), + onLocationChange: function(aBrowser, aProgress, aRequest, aURI) { + var chromeWindow = aBrowser.ownerGlobal; + var index = chromeWindow.gBrowser.getBrowserIndexForDocument(aBrowser.contentDocument); + if (index == -1) { + //LOG("Controller.tabsProgressListener()->onLocationChange(): index == -1"); + return; + } + Controller.updateLocation(chromeWindow, index); + } + }, + tabsTitleChangedListener: function(e) { + var chromeWindow = this.ownerGlobal; + var index = chromeWindow.gBrowser.getBrowserIndexForDocument(e.target); + var browser = chromeWindow.gBrowser.browsers[index]; + if (index == -1 || !browser._tabLocationPatched || (e.target.title == '' && browser.contentDocument.documentURI == 'about:blank')) { + return; + } + Controller.updateTitle(chromeWindow, index); + }, + + // UI functions + setupWindow: function(chromeWindow) { + var tabsCount = chromeWindow.gBrowser.browsers.length; + for (var i = 0; i < tabsCount; i++) { + Controller.setupTab(chromeWindow, i); + } + }, + setupTab: function(chromeWindow, tabIndex, force) { + var browser = chromeWindow.gBrowser.browsers[tabIndex]; + if (browser._tabLocationPatched && !force) { + //LOG("ignore(already) setupTab "+tabIndex); + return true; + } + + //if (browser.contentDocument.readyState == 'uninitialized') { + //LOG("ignore(notready) setupTab "+tabIndex+",readyState:"+browser.contentDocument.readyState); + // return false; + //} + + //LOG("setupTab:"+tabIndex+",readyState:"+browser.contentDocument.readyState); + var ownerDocument = chromeWindow.gBrowser.ownerDocument; + + // Current location and title + var loc = getLocation(browser.contentDocument.location), title = browser.contentDocument.title; + + // Get some elements + var tab = chromeWindow.gBrowser.tabContainer.childNodes[tabIndex]; + var label = ownerDocument.getAnonymousElementByAttribute(tab, 'anonid', 'tab-label'); + + // DOM manipulations + var vbox = createElement(ownerDocument, 'xul:vbox', { orient: 'vertical', flex: 1, anonid: 'tab-label-vbox' }); + vbox.addEventListener('DOMAttrModified', function(e) { + if (e.target != vbox/* && e.target != vbox.firstChild*/) { + //LOG("e.target("+e.target.className+") != vbox [ "+e.attrName+" => "+e.newValue+" ]"); + return; + } + label.setAttribute(e.attrName, e.newValue); + if (tabIndex == 1) { + LOG("DOMAttrModified set " + e.attrName + "=" + e.newValue); + } + }); + var url = createElement(ownerDocument, 'label', { + 'class': 'tab-text tab-url-label', + 'xbl:inherits': 'value=visibleLabel,crop,accesskey,fadein,pinned,selected', + 'flex': '1', + 'anonid': 'tab-url-label', + 'role': 'presentation', + 'fadein': 'true', + 'selected': 'true', + 'crop': 'end' + }); + url.setAttribute('value', loc); + + label.parentNode.insertBefore(vbox, label); + + vbox.appendChild(label); + vbox.appendChild(url); + + browser._tabLocationPatched = true; + + return true; + }, + updateAllLocations: function() { + var windows = wm.getEnumerator("navigator:browser"); + while (windows.hasMoreElements()) { + var chromeWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow); + var tabsCount = chromeWindow.gBrowser.browsers.length; + for (var i = 0; i < tabsCount; i++) { + Controller.updateLocation(chromeWindow, i); + } + } + }, + updateLocation: function(chromeWindow, tabIndex) { + var ownerDocument = chromeWindow.gBrowser.ownerDocument; + var loc = getLocation(chromeWindow.gBrowser.browsers[tabIndex].contentDocument.location); + var tab = chromeWindow.gBrowser.tabContainer.childNodes[tabIndex]; + var url = ownerDocument.getAnonymousElementByAttribute(tab, 'anonid', 'tab-url-label'); + if (url) { + url.setAttribute('value', loc); + } + }, + updateTitle: function(chromeWindow, tabIndex) { + var ownerDocument = chromeWindow.gBrowser.ownerDocument; + var title = chromeWindow.gBrowser.browsers[tabIndex].contentDocument.title; + var tab = chromeWindow.gBrowser.tabContainer.childNodes[tabIndex]; + var label = ownerDocument.getAnonymousElementByAttribute(tab, 'anonid', 'tab-label'); + if (label) { + label.setAttribute('value', title); + } + }, + tearDownWindow: function(chromeWindow) { + var tabsCount = chromeWindow.gBrowser.browsers.length; + for (var i = 0; i < tabsCount; i++) { + Controller.tearDownTab(chromeWindow, i); + } + }, + tearDownTab: function(chromeWindow, tabIndex) { + var browser = chromeWindow.gBrowser.browsers[tabIndex]; + if (!browser._tabLocationPatched) { + //LOG("Controller.tearDownTab() not set up", chromeWindow, tabIndex); + return; + } + var ownerDocument = chromeWindow.gBrowser.ownerDocument; + + var tab = chromeWindow.gBrowser.tabContainer.childNodes[tabIndex]; + var vbox = ownerDocument.getAnonymousElementByAttribute(tab, 'anonid', 'tab-label-vbox'); + //LOG("tearDownTab(): deleting vbox now", vbox); + vbox.parentNode.insertBefore(vbox.firstChild, vbox); + vbox.parentNode.removeChild(vbox); + + delete browser._tabLocationPatched; + }, + + loadStyles: function(list) { + for (var i = 0, len = list.length; i < len; i++) { + var styleSheetURI = Services.io.newURI(list[i], null, null); + styleSheetService.loadAndRegisterSheet(styleSheetURI, styleSheetService.AUTHOR_SHEET); + } + }, + unloadStyles: function(list) { + for (var i = 0, len = list.length; i < len; i++) { + var styleSheetURI = Services.io.newURI(list[i], null, null); + if (styleSheetService.sheetRegistered(styleSheetURI, styleSheetService.AUTHOR_SHEET)) { + styleSheetService.unregisterSheet(styleSheetURI, styleSheetService.AUTHOR_SHEET); + } + } + } +}; + +var PrefsObserver = { + observe: function(subject, topic, data) { + if (topic != "nsPref:changed") { + return; + } + switch (data) { + case "location": + this.updateLocationPreference(); + break; + case "smaller": + this.updateSmallerPreference(); + break; + case "bold": + this.updateBoldPreference(); + break; + } + }, + updateLocationPreference: function() { + var val = prefs.getIntPref("location"); + if (val != 0 && val != 1) { + val = 0; + } + var prev = LocationPreference; + LocationPreference = val; + if (val != prev) { + Controller.updateAllLocations(); + } + }, + updateSmallerPreference: function() { + var val = prefs.getBoolPref("smaller"); + var prev = SmallerPreference; + SmallerPreference = val; + if (val != prev) { + Controller[val ? 'loadStyles' : 'unloadStyles'](styleSheets.smaller); + } + }, + updateBoldPreference: function() { + var val = prefs.getBoolPref("bold"); + var prev = BoldPreference; + BoldPreference = val; + if (val != prev) { + Controller[val ? 'loadStyles' : 'unloadStyles'](styleSheets.bold); + } + } +}; + +var WindowListener = { + onOpenWindow: function(xulWindow) { + //LOG("WindowListener.onOpenWindow()"); + var chromeWindow = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + chromeWindow.addEventListener("load", function listener() { + chromeWindow.removeEventListener("load", listener, false); + var domDocument = chromeWindow.document.documentElement; + var windowType = domDocument.getAttribute("windowtype"); + if (windowType == "navigator:browser") { + Controller.bindWindowEvents(chromeWindow); + //Controller.setupWindow(chromeWindow); + } + }, false); + }, + onCloseWindow: function(chromeWindow) { + // This is not necessary, window will be destroyed anyway.. + Controller.tearDownWindow(chromeWindow); + Controller.unbindWindowEvents(chromeWindow); + }, + onWindowTitleChange: function(chromeWindow, newTitle) { + //LOG("onWindowTitleChange()", chromeWindow, newTitle); + } +}; + +function startup(data, reason) { + try { + // Load stylesheets + Controller.loadStyles(styleSheets.common); + + // Setup UI + wm.addListener(WindowListener); + if (reason != APP_STARTUP) { + var windows = wm.getEnumerator("navigator:browser"); + while (windows.hasMoreElements()) { + var chromeWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow); + var readyState = chromeWindow.document.readyState; + + Controller.bindWindowEvents(chromeWindow); + if (readyState == 'complete') { + Controller.setupWindow(chromeWindow); + } + } + } + + // Register to receive notifications of preference changes + prefs.QueryInterface(Ci.nsIPrefBranch2); + prefs.addObserver("", PrefsObserver, false); + + PrefsObserver.updateLocationPreference(); + PrefsObserver.updateSmallerPreference(); + PrefsObserver.updateBoldPreference(); + } catch (e) { + LOG("startup() error:", e.message, e.stack); + } +} +function shutdown(data, reason) { + if (reason == APP_SHUTDOWN) { + return; + } + + // Unload stylesheets + Controller.unloadStyles(styleSheets.common); + if (SmallerPreference) Controller.unloadStyles(styleSheets.smaller); + if (BoldPreference) Controller.unloadStyles(styleSheets.bold); + + // UI + var windows = wm.getEnumerator("navigator:browser"); + while (windows.hasMoreElements()) { + var chromeWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow); + Controller.tearDownWindow(chromeWindow); + Controller.unbindWindowEvents(chromeWindow); + } + + // Remove prefs observer + prefs.removeObserver("", PrefsObserver); + + if (reason == ADDON_DISABLE) { + Services.obs.notifyObservers(null, "startupcache-invalidate", null); + Services.obs.notifyObservers(null, "chrome-flush-caches", null); + } +} +function install(data, reason) { + try { + prefs.getIntPref('location'); + } catch (e) { + prefs.setIntPref('location', 0); + prefs.setBoolPref('smaller', false); + prefs.setBoolPref('bold', false); + } +} +function uninstall(data, reason) { + prefs.deleteBranch(""); +} + +function LOG() { + var args = Array.prototype.slice.call(arguments); + args.unshift("tablocation:"); + console.log.apply(console, args); +} +function createElement(doc, name, attr) { + var el = doc.createElement(name); + for (var k in attr) { + if (attr.hasOwnProperty(k)) { + el.setAttribute(k, attr[k]); + } + } + return el; +} +function getLocation(loc) { + switch (LocationPreference) { + case 0: + loc = loc.href; + break; + + case 1: + if (loc.host) { + loc = loc.host; + } else { + loc = loc.href; + } + break; + } + return loc; +} diff --git a/chrome.manifest b/chrome.manifest new file mode 100644 index 0000000..0c12a69 --- /dev/null +++ b/chrome.manifest @@ -0,0 +1,2 @@ +content tablocation content/ +skin tablocation classic/1.0 content/skin/ diff --git a/content/skin/overlay-bold.css b/content/skin/overlay-bold.css new file mode 100644 index 0000000..dbed4da --- /dev/null +++ b/content/skin/overlay-bold.css @@ -0,0 +1,5 @@ +@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); +.tab-label { + font-weight: bold; + color: #222; +} diff --git a/content/skin/overlay-small.css b/content/skin/overlay-small.css new file mode 100644 index 0000000..7a485bd --- /dev/null +++ b/content/skin/overlay-small.css @@ -0,0 +1,8 @@ +@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); +.tab-label { + margin-top: -1px !important; + font-size: 0.8em; +} +.tab-url-label { + margin-top: 1px !important; +} diff --git a/content/skin/overlay.css b/content/skin/overlay.css new file mode 100644 index 0000000..8fc72f1 --- /dev/null +++ b/content/skin/overlay.css @@ -0,0 +1,16 @@ +@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); +.tab-label { + font-size: 0.9em; +} +.tab-url-label { + -moz-margin-end: 0px; + -moz-margin-start: 0px; + font-size: 0.7em; + color: #777; + margin-top: -1px !important; + overflow: hidden; +} +.tab-url-label:not([fadein]), +.tabbrowser-tab[pinned] .tab-url-label { + display: none; +} diff --git a/install.rdf b/install.rdf new file mode 100644 index 0000000..bf30c7f --- /dev/null +++ b/install.rdf @@ -0,0 +1,19 @@ + + + + tablocation@ch1p.com + Tab Location + 0.2.6 + 2 + ch1p + true + + + {ec8030f7-c20a-464f-9b0e-13a3a9e97384} + 20 + 99.* + + + + diff --git a/make_xpi.sh b/make_xpi.sh new file mode 100755 index 0000000..516f65f --- /dev/null +++ b/make_xpi.sh @@ -0,0 +1,2 @@ +#!/bin/sh +zip -r tablocation.xpi * -x make_xpi.sh diff --git a/options.xul b/options.xul new file mode 100644 index 0000000..953cefc --- /dev/null +++ b/options.xul @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/screenshots/1.png b/screenshots/1.png new file mode 100644 index 0000000..09fcb20 Binary files /dev/null and b/screenshots/1.png differ diff --git a/screenshots/2.png b/screenshots/2.png new file mode 100644 index 0000000..1c9ddb1 Binary files /dev/null and b/screenshots/2.png differ diff --git a/screenshots/3.png b/screenshots/3.png new file mode 100644 index 0000000..f16d700 Binary files /dev/null and b/screenshots/3.png differ diff --git a/tablocation.xpi b/tablocation.xpi new file mode 100644 index 0000000..dfb24d9 Binary files /dev/null and b/tablocation.xpi differ -- cgit v1.2.3