mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
640 lines
21 KiB
JavaScript
640 lines
21 KiB
JavaScript
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is Special Powers code
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Mozilla Foundation.
|
|
* Portions created by the Initial Developer are Copyright (C) 2010
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Clint Talbert cmtalbert@gmail.com
|
|
* Joel Maher joel.maher@gmail.com
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK *****/
|
|
/* This code is loaded in every child process that is started by mochitest in
|
|
* order to be used as a replacement for UniversalXPConnect
|
|
*/
|
|
|
|
var Ci = Components.interfaces;
|
|
var Cc = Components.classes;
|
|
|
|
function SpecialPowersAPI() {
|
|
this._consoleListeners = [];
|
|
this._encounteredCrashDumpFiles = [];
|
|
this._unexpectedCrashDumpFiles = { };
|
|
this._crashDumpDir = null;
|
|
this._mfl = null;
|
|
this._prefEnvUndoStack = [];
|
|
this._pendingPrefs = [];
|
|
this._applyingPrefs = false;
|
|
this._fm = null;
|
|
}
|
|
|
|
function bindDOMWindowUtils(aWindow) {
|
|
if (!aWindow)
|
|
return
|
|
|
|
var util = aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
|
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
|
// This bit of magic brought to you by the letters
|
|
// B Z, and E, S and the number 5.
|
|
//
|
|
// Take all of the properties on the nsIDOMWindowUtils-implementing
|
|
// object, and rebind them onto a new object with a stub that uses
|
|
// apply to call them from this privileged scope. This way we don't
|
|
// have to explicitly stub out new methods that appear on
|
|
// nsIDOMWindowUtils.
|
|
var proto = Object.getPrototypeOf(util);
|
|
var target = {};
|
|
function rebind(desc, prop) {
|
|
if (prop in desc && typeof(desc[prop]) == "function") {
|
|
var oldval = desc[prop];
|
|
try {
|
|
desc[prop] = function() { return oldval.apply(util, arguments); };
|
|
} catch (ex) {
|
|
dump("WARNING: Special Powers failed to rebind function: " + desc + "::" + prop + "\n");
|
|
}
|
|
}
|
|
}
|
|
for (var i in proto) {
|
|
var desc = Object.getOwnPropertyDescriptor(proto, i);
|
|
rebind(desc, "get");
|
|
rebind(desc, "set");
|
|
rebind(desc, "value");
|
|
Object.defineProperty(target, i, desc);
|
|
}
|
|
return target;
|
|
}
|
|
|
|
function Observer(specialPowers, aTopic, aCallback, aIsPref) {
|
|
this._sp = specialPowers;
|
|
this._topic = aTopic;
|
|
this._callback = aCallback;
|
|
this._isPref = aIsPref;
|
|
}
|
|
|
|
Observer.prototype = {
|
|
_sp: null,
|
|
_topic: null,
|
|
_callback: null,
|
|
_isPref: false,
|
|
|
|
observe: function(aSubject, aTopic, aData) {
|
|
if ((!this._isPref && aTopic == this._topic) ||
|
|
(this._isPref && aTopic == "nsPref:changed")) {
|
|
if (aData == this._topic) {
|
|
this.cleanup();
|
|
/* The callback must execute asynchronously after all the preference observers have run */
|
|
content.window.setTimeout(this._callback, 0);
|
|
content.window.setTimeout(this._sp._finishPrefEnv, 0);
|
|
}
|
|
}
|
|
},
|
|
|
|
cleanup: function() {
|
|
if (this._isPref) {
|
|
var os = Cc["@mozilla.org/preferences-service;1"].getService()
|
|
.QueryInterface(Ci.nsIPrefBranch2);
|
|
os.removeObserver(this._topic, this);
|
|
} else {
|
|
var os = Cc["@mozilla.org/observer-service;1"]
|
|
.getService(Ci.nsIObserverService)
|
|
.QueryInterface(Ci.nsIObserverService);
|
|
os.removeObserver(this, this._topic);
|
|
}
|
|
},
|
|
};
|
|
|
|
SpecialPowersAPI.prototype = {
|
|
|
|
getDOMWindowUtils: function(aWindow) {
|
|
if (aWindow == this.window && this.DOMWindowUtils != null)
|
|
return this.DOMWindowUtils;
|
|
|
|
return bindDOMWindowUtils(aWindow);
|
|
},
|
|
|
|
removeExpectedCrashDumpFiles: function(aExpectingProcessCrash) {
|
|
var success = true;
|
|
if (aExpectingProcessCrash) {
|
|
var message = {
|
|
op: "delete-crash-dump-files",
|
|
filenames: this._encounteredCrashDumpFiles
|
|
};
|
|
if (!this._sendSyncMessage("SPProcessCrashService", message)[0]) {
|
|
success = false;
|
|
}
|
|
}
|
|
this._encounteredCrashDumpFiles.length = 0;
|
|
return success;
|
|
},
|
|
|
|
findUnexpectedCrashDumpFiles: function() {
|
|
var self = this;
|
|
var message = {
|
|
op: "find-crash-dump-files",
|
|
crashDumpFilesToIgnore: this._unexpectedCrashDumpFiles
|
|
};
|
|
var crashDumpFiles = this._sendSyncMessage("SPProcessCrashService", message)[0];
|
|
crashDumpFiles.forEach(function(aFilename) {
|
|
self._unexpectedCrashDumpFiles[aFilename] = true;
|
|
});
|
|
return crashDumpFiles;
|
|
},
|
|
|
|
/*
|
|
* Take in a list of prefs and put the original value on the _prefEnvUndoStack so we can undo
|
|
* preferences that we set. Note, that this is a stack of values to revert to, not
|
|
* what we have set.
|
|
*
|
|
* prefs: {set|clear: [[pref, value], [pref, value, Iid], ...], set|clear: [[pref, value], ...], ...}
|
|
* ex: {'set': [['foo.bar', 2], ['browser.magic', '0xfeedface']], 'remove': [['bad.pref']] }
|
|
*
|
|
* In the scenario where our prefs specify the same pref more than once, we do not guarantee
|
|
* the behavior.
|
|
*
|
|
* If a preference is not changing a value, we will ignore it.
|
|
*
|
|
* TODO: complex values for original cleanup?
|
|
*
|
|
*/
|
|
pushPrefEnv: function(inPrefs, callback) {
|
|
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
|
|
getService(Components.interfaces.nsIPrefBranch);
|
|
|
|
var pref_string = [];
|
|
pref_string[prefs.PREF_INT] = "INT";
|
|
pref_string[prefs.PREF_BOOL] = "BOOL";
|
|
pref_string[prefs.PREF_STRING] = "STRING";
|
|
|
|
var pendingActions = [];
|
|
var cleanupActions = [];
|
|
|
|
for (var action in inPrefs) { /* set|clear */
|
|
for (var idx in inPrefs[action]) {
|
|
var aPref = inPrefs[action][idx];
|
|
var prefName = aPref[0];
|
|
var prefValue = null;
|
|
var prefIid = null;
|
|
var prefType = prefs.PREF_INVALID;
|
|
var originalValue = null;
|
|
|
|
if (aPref.length == 3) {
|
|
prefValue = aPref[1];
|
|
prefIid = aPref[2];
|
|
} else if (aPref.length == 2) {
|
|
prefValue = aPref[1];
|
|
}
|
|
|
|
/* If pref is not found or invalid it doesn't exist. */
|
|
if (prefs.getPrefType(prefName) != prefs.PREF_INVALID) {
|
|
prefType = pref_string[prefs.getPrefType(prefName)];
|
|
if ((prefs.prefHasUserValue(prefName) && action == 'clear') ||
|
|
(action == 'set'))
|
|
originalValue = this._getPref(prefName, prefType);
|
|
} else if (action == 'set') {
|
|
/* prefName doesn't exist, so 'clear' is pointless */
|
|
if (aPref.length == 3) {
|
|
prefType = "COMPLEX";
|
|
} else if (aPref.length == 2) {
|
|
if (typeof(prefValue) == "boolean")
|
|
prefType = "BOOL";
|
|
else if (typeof(prefValue) == "number")
|
|
prefType = "INT";
|
|
else if (typeof(prefValue) == "string")
|
|
prefType = "CHAR";
|
|
}
|
|
}
|
|
|
|
/* PREF_INVALID: A non existing pref which we are clearing or invalid values for a set */
|
|
if (prefType == prefs.PREF_INVALID)
|
|
continue;
|
|
|
|
/* We are not going to set a pref if the value is the same */
|
|
if (originalValue == prefValue)
|
|
continue;
|
|
|
|
pendingActions.push({'action': action, 'type': prefType, 'name': prefName, 'value': prefValue, 'Iid': prefIid});
|
|
|
|
/* Push original preference value or clear into cleanup array */
|
|
var cleanupTodo = {'action': action, 'type': prefType, 'name': prefName, 'value': originalValue, 'Iid': prefIid};
|
|
if (originalValue == null) {
|
|
cleanupTodo.action = 'clear';
|
|
} else {
|
|
cleanupTodo.action = 'set';
|
|
}
|
|
cleanupActions.push(cleanupTodo);
|
|
}
|
|
}
|
|
|
|
if (pendingActions.length > 0) {
|
|
this._prefEnvUndoStack.push(cleanupActions);
|
|
this._pendingPrefs.push([pendingActions, callback]);
|
|
this._applyPrefs();
|
|
} else {
|
|
content.window.setTimeout(callback, 0);
|
|
}
|
|
},
|
|
|
|
popPrefEnv: function(callback) {
|
|
if (this._prefEnvUndoStack.length > 0) {
|
|
/* Each pop will have a valid block of preferences */
|
|
this._pendingPrefs.push([this._prefEnvUndoStack.pop(), callback]);
|
|
this._applyPrefs();
|
|
} else {
|
|
content.window.setTimeout(callback, 0);
|
|
}
|
|
},
|
|
|
|
flushPrefEnv: function(callback) {
|
|
while (this._prefEnvUndoStack.length > 1)
|
|
this.popPrefEnv(null);
|
|
|
|
this.popPrefEnv(callback);
|
|
},
|
|
|
|
/*
|
|
Iterate through one atomic set of pref actions and perform sets/clears as appropriate.
|
|
All actions performed must modify the relevant pref.
|
|
*/
|
|
_applyPrefs: function() {
|
|
if (this._applyingPrefs || this._pendingPrefs.length <= 0) {
|
|
return;
|
|
}
|
|
|
|
/* Set lock and get prefs from the _pendingPrefs queue */
|
|
this._applyingPrefs = true;
|
|
var transaction = this._pendingPrefs.shift();
|
|
var pendingActions = transaction[0];
|
|
var callback = transaction[1];
|
|
|
|
var lastPref = pendingActions[pendingActions.length-1];
|
|
this._addObserver(lastPref.name, callback, true);
|
|
|
|
for (var idx in pendingActions) {
|
|
var pref = pendingActions[idx];
|
|
if (pref.action == 'set') {
|
|
this._setPref(pref.name, pref.type, pref.value, pref.Iid);
|
|
} else if (pref.action == 'clear') {
|
|
this.clearUserPref(pref.name);
|
|
}
|
|
}
|
|
},
|
|
|
|
_addObserver: function(aTopic, aCallback, aIsPref) {
|
|
var observer = new Observer(this, aTopic, aCallback, aIsPref);
|
|
|
|
if (aIsPref) {
|
|
var os = Cc["@mozilla.org/preferences-service;1"].getService()
|
|
.QueryInterface(Ci.nsIPrefBranch2);
|
|
os.addObserver(aTopic, observer, false);
|
|
} else {
|
|
var os = Cc["@mozilla.org/observer-service;1"]
|
|
.getService(Ci.nsIObserverService)
|
|
.QueryInterface(Ci.nsIObserverService);
|
|
os.addObserver(observer, aTopic, false);
|
|
}
|
|
},
|
|
|
|
/* called from the observer when we get a pref:changed. */
|
|
_finishPrefEnv: function() {
|
|
/*
|
|
Any subsequent pref environment pushes that occurred while waiting
|
|
for the preference update are pending, and will now be executed.
|
|
*/
|
|
this.wrappedJSObject.SpecialPowers._applyingPrefs = false;
|
|
this.wrappedJSObject.SpecialPowers._applyPrefs();
|
|
},
|
|
|
|
// Mimic the get*Pref API
|
|
getBoolPref: function(aPrefName) {
|
|
return (this._getPref(aPrefName, 'BOOL'));
|
|
},
|
|
getIntPref: function(aPrefName) {
|
|
return (this._getPref(aPrefName, 'INT'));
|
|
},
|
|
getCharPref: function(aPrefName) {
|
|
return (this._getPref(aPrefName, 'CHAR'));
|
|
},
|
|
getComplexValue: function(aPrefName, aIid) {
|
|
return (this._getPref(aPrefName, 'COMPLEX', aIid));
|
|
},
|
|
|
|
// Mimic the set*Pref API
|
|
setBoolPref: function(aPrefName, aValue) {
|
|
return (this._setPref(aPrefName, 'BOOL', aValue));
|
|
},
|
|
setIntPref: function(aPrefName, aValue) {
|
|
return (this._setPref(aPrefName, 'INT', aValue));
|
|
},
|
|
setCharPref: function(aPrefName, aValue) {
|
|
return (this._setPref(aPrefName, 'CHAR', aValue));
|
|
},
|
|
setComplexValue: function(aPrefName, aIid, aValue) {
|
|
return (this._setPref(aPrefName, 'COMPLEX', aValue, aIid));
|
|
},
|
|
|
|
// Mimic the clearUserPref API
|
|
clearUserPref: function(aPrefName) {
|
|
var msg = {'op':'clear', 'prefName': aPrefName, 'prefType': ""};
|
|
this._sendSyncMessage('SPPrefService', msg);
|
|
},
|
|
|
|
// Private pref functions to communicate to chrome
|
|
_getPref: function(aPrefName, aPrefType, aIid) {
|
|
var msg = {};
|
|
if (aIid) {
|
|
// Overloading prefValue to handle complex prefs
|
|
msg = {'op':'get', 'prefName': aPrefName, 'prefType':aPrefType, 'prefValue':[aIid]};
|
|
} else {
|
|
msg = {'op':'get', 'prefName': aPrefName,'prefType': aPrefType};
|
|
}
|
|
var val = this._sendSyncMessage('SPPrefService', msg);
|
|
|
|
if (val == null || val[0] == null)
|
|
throw "Error getting pref";
|
|
return val[0];
|
|
},
|
|
_setPref: function(aPrefName, aPrefType, aValue, aIid) {
|
|
var msg = {};
|
|
if (aIid) {
|
|
msg = {'op':'set','prefName':aPrefName, 'prefType': aPrefType, 'prefValue': [aIid,aValue]};
|
|
} else {
|
|
msg = {'op':'set', 'prefName': aPrefName, 'prefType': aPrefType, 'prefValue': aValue};
|
|
}
|
|
return(this._sendSyncMessage('SPPrefService', msg)[0]);
|
|
},
|
|
|
|
//XXX: these APIs really ought to be removed, they're not e10s-safe.
|
|
// (also they're pretty Firefox-specific)
|
|
_getTopChromeWindow: function(window) {
|
|
return window.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIWebNavigation)
|
|
.QueryInterface(Ci.nsIDocShellTreeItem)
|
|
.rootTreeItem
|
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIDOMWindow)
|
|
.QueryInterface(Ci.nsIDOMChromeWindow);
|
|
},
|
|
_getDocShell: function(window) {
|
|
return window.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIWebNavigation)
|
|
.QueryInterface(Ci.nsIDocShell);
|
|
},
|
|
_getMUDV: function(window) {
|
|
return this._getDocShell(window).contentViewer
|
|
.QueryInterface(Ci.nsIMarkupDocumentViewer);
|
|
},
|
|
_getAutoCompletePopup: function(window) {
|
|
return this._getTopChromeWindow(window).document
|
|
.getElementById("PopupAutoComplete");
|
|
},
|
|
addAutoCompletePopupEventListener: function(window, listener) {
|
|
this._getAutoCompletePopup(window).addEventListener("popupshowing",
|
|
listener,
|
|
false);
|
|
},
|
|
removeAutoCompletePopupEventListener: function(window, listener) {
|
|
this._getAutoCompletePopup(window).removeEventListener("popupshowing",
|
|
listener,
|
|
false);
|
|
},
|
|
isBackButtonEnabled: function(window) {
|
|
return !this._getTopChromeWindow(window).document
|
|
.getElementById("Browser:Back")
|
|
.hasAttribute("disabled");
|
|
},
|
|
|
|
addChromeEventListener: function(type, listener, capture, allowUntrusted) {
|
|
addEventListener(type, listener, capture, allowUntrusted);
|
|
},
|
|
removeChromeEventListener: function(type, listener, capture) {
|
|
removeEventListener(type, listener, capture);
|
|
},
|
|
|
|
addErrorConsoleListener: function(listener) {
|
|
var consoleListener = {
|
|
userListener: listener,
|
|
observe: function(consoleMessage) {
|
|
this.userListener(consoleMessage.message);
|
|
}
|
|
};
|
|
|
|
Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService)
|
|
.registerListener(consoleListener);
|
|
|
|
this._consoleListeners.push(consoleListener);
|
|
},
|
|
|
|
removeErrorConsoleListener: function(listener) {
|
|
for (var index in this._consoleListeners) {
|
|
var consoleListener = this._consoleListeners[index];
|
|
if (consoleListener.userListener == listener) {
|
|
Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService)
|
|
.unregisterListener(consoleListener);
|
|
this._consoleListeners = this._consoleListeners.splice(index, 1);
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
|
|
getFullZoom: function(window) {
|
|
return this._getMUDV(window).fullZoom;
|
|
},
|
|
setFullZoom: function(window, zoom) {
|
|
this._getMUDV(window).fullZoom = zoom;
|
|
},
|
|
getTextZoom: function(window) {
|
|
return this._getMUDV(window).textZoom;
|
|
},
|
|
setTextZoom: function(window, zoom) {
|
|
this._getMUDV(window).textZoom = zoom;
|
|
},
|
|
|
|
createSystemXHR: function() {
|
|
return Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
|
.createInstance(Ci.nsIXMLHttpRequest);
|
|
},
|
|
|
|
snapshotWindow: function (win, withCaret) {
|
|
var el = this.window.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
|
el.width = win.innerWidth;
|
|
el.height = win.innerHeight;
|
|
var ctx = el.getContext("2d");
|
|
var flags = 0;
|
|
|
|
ctx.drawWindow(win, win.scrollX, win.scrollY,
|
|
win.innerWidth, win.innerHeight,
|
|
"rgb(255,255,255)",
|
|
withCaret ? ctx.DRAWWINDOW_DRAW_CARET : 0);
|
|
return el;
|
|
},
|
|
|
|
gc: function() {
|
|
this.DOMWindowUtils.garbageCollect();
|
|
},
|
|
|
|
forceGC: function() {
|
|
Components.utils.forceGC();
|
|
},
|
|
|
|
hasContentProcesses: function() {
|
|
try {
|
|
var rt = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
|
|
return rt.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
|
|
} catch (e) {
|
|
return true;
|
|
}
|
|
},
|
|
|
|
_xpcomabi: null,
|
|
|
|
get XPCOMABI() {
|
|
if (this._xpcomabi != null)
|
|
return this._xpcomabi;
|
|
|
|
var xulRuntime = Cc["@mozilla.org/xre/app-info;1"]
|
|
.getService(Components.interfaces.nsIXULAppInfo)
|
|
.QueryInterface(Components.interfaces.nsIXULRuntime);
|
|
|
|
this._xpcomabi = xulRuntime.XPCOMABI;
|
|
return this._xpcomabi;
|
|
},
|
|
|
|
executeSoon: function(aFunc) {
|
|
var tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager);
|
|
tm.mainThread.dispatch({
|
|
run: function() {
|
|
aFunc();
|
|
}
|
|
}, Ci.nsIThread.DISPATCH_NORMAL);
|
|
},
|
|
|
|
_os: null,
|
|
|
|
get OS() {
|
|
if (this._os != null)
|
|
return this._os;
|
|
|
|
var xulRuntime = Cc["@mozilla.org/xre/app-info;1"]
|
|
.getService(Components.interfaces.nsIXULAppInfo)
|
|
.QueryInterface(Components.interfaces.nsIXULRuntime);
|
|
|
|
this._os = xulRuntime.OS;
|
|
return this._os;
|
|
},
|
|
|
|
addSystemEventListener: function(target, type, listener, useCapture) {
|
|
Cc["@mozilla.org/eventlistenerservice;1"].
|
|
getService(Ci.nsIEventListenerService).
|
|
addSystemEventListener(target, type, listener, useCapture);
|
|
},
|
|
removeSystemEventListener: function(target, type, listener, useCapture) {
|
|
Cc["@mozilla.org/eventlistenerservice;1"].
|
|
getService(Ci.nsIEventListenerService).
|
|
removeSystemEventListener(target, type, listener, useCapture);
|
|
},
|
|
|
|
setLogFile: function(path) {
|
|
this._mfl = new MozillaFileLogger(path);
|
|
},
|
|
|
|
log: function(data) {
|
|
this._mfl.log(data);
|
|
},
|
|
|
|
closeLogFile: function() {
|
|
this._mfl.close();
|
|
},
|
|
|
|
addCategoryEntry: function(category, entry, value, persists, replace) {
|
|
Components.classes["@mozilla.org/categorymanager;1"].
|
|
getService(Components.interfaces.nsICategoryManager).
|
|
addCategoryEntry(category, entry, value, persists, replace);
|
|
},
|
|
|
|
getNodePrincipal: function(aNode) {
|
|
return aNode.nodePrincipal;
|
|
},
|
|
|
|
getNodeBaseURIObject: function(aNode) {
|
|
return aNode.baseURIObject;
|
|
},
|
|
|
|
getDocumentURIObject: function(aDocument) {
|
|
return aDocument.documentURIObject;
|
|
},
|
|
|
|
copyString: function(str) {
|
|
Components.classes["@mozilla.org/widget/clipboardhelper;1"].
|
|
getService(Components.interfaces.nsIClipboardHelper).
|
|
copyString(str);
|
|
},
|
|
|
|
// :jdm gets credit for this. ex: getPrivilegedProps(window, 'location.href');
|
|
getPrivilegedProps: function(obj, props) {
|
|
parts = props.split('.');
|
|
|
|
for (var i = 0; i < parts.length; i++) {
|
|
var p = parts[i];
|
|
if (obj[p]) {
|
|
obj = obj[p];
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
return obj;
|
|
},
|
|
|
|
get focusManager() {
|
|
if (this._fm != null)
|
|
return this._fm;
|
|
|
|
this._fm = Components.classes["@mozilla.org/focus-manager;1"].
|
|
getService(Components.interfaces.nsIFocusManager);
|
|
|
|
return this._fm;
|
|
},
|
|
|
|
getFocusedElementForWindow: function(targetWindow, aDeep, childTargetWindow) {
|
|
this.focusManager.getFocusedElementForWindow(targetWindow, aDeep, childTargetWindow);
|
|
},
|
|
|
|
activeWindow: function() {
|
|
return this.focusManager.activeWindow;
|
|
},
|
|
|
|
focusedWindow: function() {
|
|
return this.focusManager.focusedWindow;
|
|
},
|
|
|
|
focus: function(window) {
|
|
window.focus();
|
|
},
|
|
};
|
|
|