utils.coffee | |
---|---|
{ WindowTracker, isBrowserWindow } = require 'window-utils'
{ interfaces: Ci, classes: Cc } = Components
HTMLInputElement = Ci.nsIDOMHTMLInputElement
HTMLTextAreaElement = Ci.nsIDOMHTMLTextAreaElement
HTMLSelectElement = Ci.nsIDOMHTMLSelectElement
XULDocument = Ci.nsIDOMXULDocument
XULElement = Ci.nsIDOMXULElement
HTMLDocument = Ci.nsIDOMHTMLDocument
HTMLElement = Ci.nsIDOMHTMLElement
Window = Ci.nsIDOMWindow
ChromeWindow = Ci.nsIDOMChromeWindow
KeyboardEvent = Ci.nsIDOMKeyEvent
_sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService)
_clip = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard)
class Bucket
constructor: (@idFunc, @newFunc) ->
@bucket = {}
get: (obj) ->
id = @idFunc obj
if container = @bucket[id]
return container
else
return @bucket[id] = @newFunc obj
forget: (obj) ->
delete @bucket[id] if id = @idFunc obj
class WindowEventTracker
constructor: (events, eventFilter = null) ->
handlerFilter = (handler) ->
return (event) ->
if !eventFilter or eventFilter event
handler event
addEventListeners = (window) ->
for name, handler of events
window.addEventListener name, handlerFilter(handler), false
removeEventListeners = (window) ->
for name, handler of events
window.removeEventListener name, handlerFilter(handler), false
@windowTracker = new WindowTracker
track: (window) ->
if isBrowserWindow window
addEventListeners window
untrack: (window) ->
if isBrowserWindow window
removeEventListeners window
start: -> @windowTracker.start()
stop: -> @windowTracker.stop()
isRootWindow = (window) ->
window.location == "chrome://browser/content/browser.xul"
getEventWindow = (event) ->
if event.originalTarget instanceof Window
return event.originalTarget
else
doc = event.originalTarget.ownerDocument or event.originalTarget
if doc instanceof HTMLDocument or doc instanceof XULDocument
return doc.defaultView
getEventTabWindow = (event) ->
if window = getEventWindow event
if isRootWindow window
return window.gBrowser.tabs.selectedItem?.contentWindow.wrappedJSObject
else
return window
getEventRootWindow = (event) ->
if window = getEventWindow event
return getRootWindow window
getEventTabBrowser = (event) ->
cw.gBrowser if cw = getEventRootWindow event
getRootWindow = (window) ->
return window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Window);
isElementEditable = (element) ->
return element.isContentEditable or \
element instanceof HTMLInputElement or \
element instanceof HTMLTextAreaElement or \
element instanceof HTMLSelectElement
getWindowId = (window) ->
return window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils)
.outerWindowID
getSessionStore = ->
Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore); | |
Function that returns a URI to the css file that's part of the extension | cssUri = do () ->
tools = {}
Cu.import "resource://gre/modules/Services.jsm", tools
(name) ->
baseURI = tools.Services.io.newURI __SCRIPT_URI_SPEC__, null, null
uri = tools.Services.io.newURI "resources/#{ name }.css", null, baseURI
return uri |
Loads the css identified by the name in the StyleSheetService as User Stylesheet The stylesheet is then appended to every document, but it can be overwritten by any user css | loadCss = (name) ->
_sss.loadAndRegisterSheet(cssUri(name), _sss.USER_SHEET) |
Unloads the css file that's been loaded earlier with | unloadCss = (name) ->
uri = cssUri(name)
if _sss.sheetRegistered(uri, _sss.USER_SHEET)
_sss.unregisterSheet(uri, _sss.USER_SHEET) |
processes the keyboard event and extracts string representation of the key without modifiers in case this is the kind of a key that can be handled by the extension Currently we handle letters, Escape and Tab keys | keyboardEventChar = (keyboardEvent) ->
if keyboardEvent.charCode > 0
char = String.fromCharCode(keyboardEvent.charCode)
if char.match /\s/
char = undefined
else
switch keyboardEvent.keyCode
when KeyboardEvent.DOM_VK_ESCAPE then char = 'Esc'
when KeyboardEvent.DOM_VK_BACK_SPACE then char = 'Backspace'
else char = undefined
return char |
extracts string representation of the KeyboardEvent and adds relevant modifiers (ctrl, alt, meta) in case they were pressed | keyboardEventKey = (keyboardEvent) ->
char = keyboardEventChar keyboardEvent
{
shiftKey: shift,
altKey: alt,
ctrlKey: ctrl,
metaKey: meta,
} = keyboardEvent
if alt or ctrl or meta
k = (a, b) -> if a then b else ''
return "<#{ k(ctrl, 'c') + k(alt, 'a') + k(meta, 'm') }-#{ char }>"
else
return char |
Simulate mouse click with full chain of event Copied from Vimium codebase | simulateClick = (element, modifiers) ->
document = element.ownerDocument
window = document.defaultView
modifiers ||= {}
eventSequence = ["mouseover", "mousedown", "mouseup", "click"]
for event in eventSequence
mouseEvent = document.createEvent("MouseEvents")
mouseEvent.initMouseEvent(event, true, true, window, 1, 0, 0, 0, 0, modifiers.ctrlKey, false, false,
modifiers.metaKey, 0, null) |
Debugging note: Firefox will not execute the element's default action if we dispatch this click event, but Webkit will. Dispatching a click on an input box does not seem to focus it; we do that separately | element.dispatchEvent(mouseEvent) |
Write a string into system clipboard | writeToClipboard = (text) ->
str = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
str.data = text
trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
trans.addDataFlavor("text/unicode");
trans.setTransferData("text/unicode", str, text.length * 2);
_clip.setData trans, null, Ci.nsIClipboard.kGlobalClipboard |
Write a string into system clipboard | readFromClipboard = () ->
trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
trans.addDataFlavor("text/unicode");
_clip.getData trans, Ci.nsIClipboard.kGlobalClipboard
str = {}
strLength = {}
trans.getTransferData("text/unicode", str, strLength)
if str
str = str.value.QueryInterface(Ci.nsISupportsString);
return str.data.substring 0, strLength.value / 2
return undefined
exports.WindowEventTracker = WindowEventTracker
exports.Bucket = Bucket
exports.isRootWindow = isRootWindow
exports.getEventWindow = getEventWindow
exports.getEventTabWindow = getEventTabWindow
exports.getEventRootWindow = getEventRootWindow
exports.getEventTabBrowser = getEventTabBrowser
exports.getWindowId = getWindowId
exports.getRootWindow = getRootWindow
exports.isElementEditable = isElementEditable
exports.getSessionStore = getSessionStore
exports.loadCss = loadCss
exports.unloadCss = unloadCss
exports.keyboardEventKey = keyboardEventKey
exports.simulateClick = simulateClick
exports.readFromClipboard = readFromClipboard
exports.writeToClipboard = writeToClipboard
|