Created
November 14, 2013 23:59
-
-
Save Gozala/7476658 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
const { addObserver, notifyObservers } = Cc["@mozilla.org/observer-service;1"]. | |
getService(Ci.nsIObserverService); | |
const { getEnumerator } = Cc["@mozilla.org/appshell/window-mediator;1"]. | |
getService(Ci.nsIWindowMediator); | |
const isBrowser = window => { | |
try { | |
return window.document.documentElement.getAttribute("windowtype") === "navigator:browser"; | |
} | |
catch (e) { | |
return false; | |
} | |
} | |
const isInteractive = window => | |
window.document.readyState === "interactive" || | |
window.document.readyState === "complete"; | |
const onWindowOpened = subject => { | |
const window = subject.QueryInterface(Ci.nsIDOMWindow); | |
if (isBrowser(window)) { | |
window.addEventListener("DOMContentLoaded", onWindowReady, true); | |
} | |
} | |
const onWindowReady = event => { | |
// event.target is document rather than window. | |
const window = event.target.defaultView; | |
// Remove listener. | |
window.removeEventListener("DOMContentLoaded", onWindowReady, true); | |
// There are no open event for inital tabs so we emulate one. | |
onInitialTab(window); | |
window.addEventListener("TabOpen", onTabOpen); | |
window.addEventListener("TabClose", onTabClose); | |
window.addEventListener("TabPinned", onTabPinned); | |
window.addEventListener("TabUnpinned", onTabUnpinned); | |
window.addEventListener("TabMove", onTabMove); | |
window.addEventListener("TabShow", onTabShow); | |
window.addEventListener("TabHide", onTabShow); | |
// And more complicated ones. | |
window.addEventListener("TabAttrModified", onTabAttributeModified); | |
window.addEventListener("TabSelect", onTabSelect); | |
} | |
const makeNotifier = type => event => notifyObservers(event.target, type, null); | |
const onTabClose = makeNotifier("tab-close") | |
const onTabPinned = makeNotifier("tab-pinned") | |
const onTabUnpinned = makeNotifier("tab-unpinned") | |
const onTabMove = makeNotifier("tab-move") | |
const onTabShow = makeNotifier("tab-show") | |
const onTabHide = makeNotifier("tab-hide") | |
// Since we can't know which attribute is modified on tab we'll have | |
// to make guess based on state. There for we have weak map of tab | |
// attribute states | |
const states = new WeakMap(); | |
const readState(tab) => ({ | |
busy: tab.getAttribute("busy"), | |
icon: tab.getAttribute("image"), | |
title: tab.label | |
}); | |
// Since there is no TabUnselect and TabSelect is dispatched post factum | |
// we need to keep track and synchronize state ourselfs. We're going to | |
// use weak map for window to previously selected tab associations. | |
const selectedTabToWindow = new WeakMap(); | |
const onInitialTab = window => { | |
const tab = window.gBrowser.selectedTab; | |
// sync state. | |
selectedTabToWindow.set(window, tab); | |
states.set(tab, readState(tab)); | |
// initially dispatch different notification for initial tabs since | |
// platform clearly considers these special. This would let users decide | |
// weather they handle them differently or same as regular open. | |
notifyObservers(tab, "initial-tab-open", null); | |
} | |
const onTabOpen = event => { | |
const tab = event.target; | |
states.set(tab, readState(tab)); | |
notifyObservers(tab, "tab-open", null); | |
} | |
const onTabSelect = event => { | |
const tab = event.target; | |
const window = tab.ownerWindow; | |
// since we can event is dispatched after selectedTab is modified | |
// we can't really know which tab was unselected without synchronizing | |
// state manually :( | |
let selectedTab = selectedTabToWindow.get(window); | |
if (!selectedTab) | |
Cu.reportError(new Error("Some tab should have being selected")); | |
else | |
notifyObservers(tab, "tab-unselect", null); | |
notifyObservers(tab, "tab-select", null); | |
} | |
const onTabAttributeModified = event => { | |
const tab = event.target; | |
const past = states.get(tab); | |
const current = readState(tab); | |
// Note: Update state before dispatching notification, since observer | |
// may update state in side effect which will end up calling this function | |
// and comparing to wrong state. | |
states.set(tab, current); | |
if (past.busy !== current.busy) { | |
notifyObservers(tab, current.busy ? "tab-busy" : "tab-unbusy", null); | |
} | |
else if (past.icon !== current.icon) { | |
notifyObservers(tab, "tab-icon-change", null); | |
} | |
else if (past.title !== current.title) { | |
notifyObservers(tab, "tab-title-change", null); | |
} | |
} | |
// React to windows that will get opened. | |
addObserver({ observe: onWindowOpened }, "domwindowopened", false); | |
// Also register listeners to windows that are already opened. | |
const opened = getEnumerator("browser:navigator"); | |
while (opened.hasMoreElements()) { | |
let window = opened.getNext().QueryInterface(Ci.nsIDOMWindow); | |
if (isInteractive(window)) | |
onWindowReady(window) | |
else | |
onWindowOpened(window) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment