mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 15:52:07 +00:00
Merge from m-c in order to fix e10s necko hangs caused by last m-c merge.
This commit is contained in:
commit
f78fd1ae1e
@ -25,7 +25,7 @@
|
||||
|
||||
#brandName {
|
||||
font-weight: bold; font-size: larger;
|
||||
}
|
||||
}
|
||||
|
||||
#userAgent {
|
||||
direction: ltr;
|
||||
|
@ -520,6 +520,7 @@
|
||||
|
||||
<toolbar id="TabsToolbar"
|
||||
fullscreentoolbar="true"
|
||||
ordinal="100"
|
||||
collapsed="true">
|
||||
<tabs id="tabbrowser-tabs" class="tabbrowser-tabs"
|
||||
tabbrowser="content"
|
||||
|
@ -71,19 +71,14 @@ const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const STATE_RUNNING_STR = "running";
|
||||
const MAX_FILE_SIZE = 100 * 1024 * 1024; // 100 megabytes
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "ConsoleSvc",
|
||||
"@mozilla.org/consoleservice;1", "nsIConsoleService");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "ObserverSvc",
|
||||
"@mozilla.org/observer-service;1", "nsIObserverService");
|
||||
|
||||
function debug(aMsg) {
|
||||
aMsg = ("SessionStartup: " + aMsg).replace(/\S{80}/g, "$&\n");
|
||||
ConsoleSvc.logStringMessage(aMsg);
|
||||
Services.console.logStringMessage(aMsg);
|
||||
}
|
||||
|
||||
/* :::::::: The Service ::::::::::::::: */
|
||||
@ -152,8 +147,8 @@ SessionStartup.prototype = {
|
||||
|
||||
if (this._sessionType != Ci.nsISessionStartup.NO_SESSION) {
|
||||
// wait for the first browser window to open
|
||||
ObserverSvc.addObserver(this, "domwindowopened", true);
|
||||
ObserverSvc.addObserver(this, "browser:purge-session-history", true);
|
||||
Services.obs.addObserver(this, "domwindowopened", true);
|
||||
Services.obs.addObserver(this, "browser:purge-session-history", true);
|
||||
}
|
||||
},
|
||||
|
||||
@ -163,18 +158,18 @@ SessionStartup.prototype = {
|
||||
observe: function sss_observe(aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case "app-startup":
|
||||
ObserverSvc.addObserver(this, "final-ui-startup", true);
|
||||
ObserverSvc.addObserver(this, "quit-application", true);
|
||||
Services.obs.addObserver(this, "final-ui-startup", true);
|
||||
Services.obs.addObserver(this, "quit-application", true);
|
||||
break;
|
||||
case "final-ui-startup":
|
||||
ObserverSvc.removeObserver(this, "final-ui-startup");
|
||||
ObserverSvc.removeObserver(this, "quit-application");
|
||||
Services.obs.removeObserver(this, "final-ui-startup");
|
||||
Services.obs.removeObserver(this, "quit-application");
|
||||
this.init();
|
||||
break;
|
||||
case "quit-application":
|
||||
// no reason for initializing at this point (cf. bug 409115)
|
||||
ObserverSvc.removeObserver(this, "final-ui-startup");
|
||||
ObserverSvc.removeObserver(this, "quit-application");
|
||||
Services.obs.removeObserver(this, "final-ui-startup");
|
||||
Services.obs.removeObserver(this, "quit-application");
|
||||
break;
|
||||
case "domwindowopened":
|
||||
var window = aSubject;
|
||||
@ -189,7 +184,7 @@ SessionStartup.prototype = {
|
||||
this._iniString = null;
|
||||
this._sessionType = Ci.nsISessionStartup.NO_SESSION;
|
||||
// no need in repeating this, since startup state won't change
|
||||
ObserverSvc.removeObserver(this, "browser:purge-session-history");
|
||||
Services.obs.removeObserver(this, "browser:purge-session-history");
|
||||
break;
|
||||
}
|
||||
},
|
||||
@ -223,7 +218,7 @@ SessionStartup.prototype = {
|
||||
aWindow.arguments[0] == defaultArgs)
|
||||
aWindow.arguments[0] = null;
|
||||
|
||||
ObserverSvc.removeObserver(this, "domwindowopened");
|
||||
Services.obs.removeObserver(this, "domwindowopened");
|
||||
},
|
||||
|
||||
/* ........ Public API ................*/
|
||||
@ -264,7 +259,7 @@ SessionStartup.prototype = {
|
||||
createInstance(Ci.nsISupportsString);
|
||||
stateString.data = this._readFile(aFile) || "";
|
||||
|
||||
ObserverSvc.notifyObservers(stateString, "sessionstore-state-read", "");
|
||||
Services.obs.notifyObservers(stateString, "sessionstore-state-read", "");
|
||||
|
||||
return stateString.data;
|
||||
},
|
||||
|
@ -115,15 +115,13 @@ const CAPABILITIES = [
|
||||
#endif
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "NetUtil", function() {
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
return NetUtil;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "ConsoleSvc",
|
||||
"@mozilla.org/consoleservice;1", "nsIConsoleService");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "CookieSvc",
|
||||
"@mozilla.org/cookiemanager;1", "nsICookieManager2");
|
||||
|
||||
@ -132,24 +130,12 @@ XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
|
||||
"@mozilla.org/xre/app-info;1", "nsICrashReporter");
|
||||
#endif
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "IOSvc",
|
||||
"@mozilla.org/network/io-service;1", "nsIIOService");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "ObserverSvc",
|
||||
"@mozilla.org/observer-service;1", "nsIObserverService");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "SecuritySvc",
|
||||
"@mozilla.org/scriptsecuritymanager;1", "nsIScriptSecurityManager");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "WindowMediator",
|
||||
"@mozilla.org/appshell/window-mediator;1", "nsIWindowMediator");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "WindowWatcher",
|
||||
"@mozilla.org/embedcomp/window-watcher;1", "nsIWindowWatcher");
|
||||
|
||||
function debug(aMsg) {
|
||||
aMsg = ("SessionStore: " + aMsg).replace(/\S{80}/g, "$&\n");
|
||||
ConsoleSvc.logStringMessage(aMsg);
|
||||
Services.console.logStringMessage(aMsg);
|
||||
}
|
||||
|
||||
/* :::::::: The Service ::::::::::::::: */
|
||||
@ -229,12 +215,11 @@ SessionStoreService.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
this._prefBranch = Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Ci.nsIPrefService).getBranch("browser.");
|
||||
this._prefBranch = Services.prefs.getBranch("browser.");
|
||||
this._prefBranch.QueryInterface(Ci.nsIPrefBranch2);
|
||||
|
||||
OBSERVING.forEach(function(aTopic) {
|
||||
ObserverSvc.addObserver(this, aTopic, true);
|
||||
Services.obs.addObserver(this, aTopic, true);
|
||||
}, this);
|
||||
|
||||
var pbs = Cc["@mozilla.org/privatebrowsing;1"].
|
||||
@ -258,9 +243,7 @@ SessionStoreService.prototype = {
|
||||
this._prefBranch.getIntPref("sessionhistory.max_entries");
|
||||
|
||||
// get file references
|
||||
var dirService = Cc["@mozilla.org/file/directory_service;1"].
|
||||
getService(Ci.nsIProperties);
|
||||
this._sessionFile = dirService.get("ProfD", Ci.nsILocalFile);
|
||||
this._sessionFile = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
|
||||
this._sessionFileBackup = this._sessionFile.clone();
|
||||
this._sessionFile.append("sessionstore.js");
|
||||
this._sessionFileBackup.append("sessionstore.bak");
|
||||
@ -632,7 +615,7 @@ SessionStoreService.prototype = {
|
||||
}
|
||||
else {
|
||||
// Nothing to restore, notify observers things are complete.
|
||||
ObserverSvc.notifyObservers(null, NOTIFY_WINDOWS_RESTORED, "");
|
||||
Services.obs.notifyObservers(null, NOTIFY_WINDOWS_RESTORED, "");
|
||||
|
||||
// the next delayed save request should execute immediately
|
||||
this._lastSaveTime -= this._interval;
|
||||
@ -1581,33 +1564,68 @@ SessionStoreService.prototype = {
|
||||
let node = formNodes.iterateNext();
|
||||
if (!node)
|
||||
return null;
|
||||
|
||||
|
||||
const MAX_GENERATED_XPATHS = 100;
|
||||
let generatedCount = 0;
|
||||
|
||||
|
||||
let data = {};
|
||||
do {
|
||||
let nId = node.id;
|
||||
let hasDefaultValue = true;
|
||||
let value;
|
||||
|
||||
// Only generate a limited number of XPath expressions for perf reasons (cf. bug 477564)
|
||||
if (!node.id && ++generatedCount > MAX_GENERATED_XPATHS)
|
||||
if (!nId && generatedCount > MAX_GENERATED_XPATHS)
|
||||
continue;
|
||||
|
||||
let id = node.id ? "#" + node.id : XPathHelper.generate(node);
|
||||
if (node instanceof Ci.nsIDOMHTMLInputElement) {
|
||||
if (node.type != "file")
|
||||
data[id] = node.type == "checkbox" || node.type == "radio" ? node.checked : node.value;
|
||||
else
|
||||
data[id] = { type: "file", fileList: node.mozGetFileNameArray() };
|
||||
|
||||
if (node instanceof Ci.nsIDOMHTMLInputElement ||
|
||||
node instanceof Ci.nsIDOMHTMLTextAreaElement) {
|
||||
switch (node.type) {
|
||||
case "checkbox":
|
||||
case "radio":
|
||||
value = node.checked;
|
||||
hasDefaultValue = value == node.defaultChecked;
|
||||
break;
|
||||
case "file":
|
||||
value = { type: "file", fileList: node.mozGetFileNameArray() };
|
||||
hasDefaultValue = !value.fileList.length;
|
||||
break;
|
||||
default: // text, textarea
|
||||
value = node.value;
|
||||
hasDefaultValue = value == node.defaultValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (!node.multiple) {
|
||||
// <select>s without the multiple attribute are hard to determine the
|
||||
// default value, so assume we don't have the default.
|
||||
hasDefaultValue = false;
|
||||
value = node.selectedIndex;
|
||||
}
|
||||
else if (node instanceof Ci.nsIDOMHTMLTextAreaElement)
|
||||
data[id] = node.value;
|
||||
else if (!node.multiple)
|
||||
data[id] = node.selectedIndex;
|
||||
else {
|
||||
let options = Array.map(node.options, function(aOpt, aIx) aOpt.selected ? aIx : -1);
|
||||
data[id] = options.filter(function(aIx) aIx >= 0);
|
||||
// <select>s with the multiple attribute are easier to determine the
|
||||
// default value since each <option> has a defaultSelected
|
||||
let options = Array.map(node.options, function(aOpt, aIx) {
|
||||
let oSelected = aOpt.selected;
|
||||
hasDefaultValue = hasDefaultValue && (oSelected == aOpt.defaultSelected);
|
||||
return oSelected ? aIx : -1;
|
||||
});
|
||||
value = options.filter(function(aIx) aIx >= 0);
|
||||
}
|
||||
// In order to reduce XPath generation (which is slow), we only save data
|
||||
// for form fields that have been changed. (cf. bug 537289)
|
||||
if (!hasDefaultValue) {
|
||||
if (nId) {
|
||||
data["#" + nId] = value;
|
||||
}
|
||||
else {
|
||||
generatedCount++;
|
||||
data[XPathHelper.generate(node)] = value;
|
||||
}
|
||||
}
|
||||
|
||||
} while ((node = formNodes.iterateNext()));
|
||||
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
@ -2178,7 +2196,7 @@ SessionStoreService.prototype = {
|
||||
var shEntry = Cc["@mozilla.org/browser/session-history-entry;1"].
|
||||
createInstance(Ci.nsISHEntry);
|
||||
|
||||
shEntry.setURI(IOSvc.newURI(aEntry.url, null, null));
|
||||
shEntry.setURI(this._getURIFromString(aEntry.url));
|
||||
shEntry.setTitle(aEntry.title || aEntry.url);
|
||||
if (aEntry.subframe)
|
||||
shEntry.setIsSubFrame(aEntry.subframe || false);
|
||||
@ -2186,7 +2204,7 @@ SessionStoreService.prototype = {
|
||||
if (aEntry.contentType)
|
||||
shEntry.contentType = aEntry.contentType;
|
||||
if (aEntry.referrer)
|
||||
shEntry.referrerURI = IOSvc.newURI(aEntry.referrer, null, null);
|
||||
shEntry.referrerURI = this._getURIFromString(aEntry.referrer);
|
||||
|
||||
if (aEntry.cacheKey) {
|
||||
var cacheKey = Cc["@mozilla.org/supports-PRUint32;1"].
|
||||
@ -2282,7 +2300,7 @@ SessionStoreService.prototype = {
|
||||
*/
|
||||
_deserializeSessionStorage: function sss_deserializeSessionStorage(aStorageData, aDocShell) {
|
||||
for (let url in aStorageData) {
|
||||
let uri = IOSvc.newURI(url, null, null);
|
||||
let uri = this._getURIFromString(url);
|
||||
let storage = aDocShell.getSessionStorageForURI(uri, "");
|
||||
for (let key in aStorageData[url]) {
|
||||
try {
|
||||
@ -2491,7 +2509,7 @@ SessionStoreService.prototype = {
|
||||
restoreCookies: function sss_restoreCookies(aCookies) {
|
||||
// MAX_EXPIRY should be 2^63-1, but JavaScript can't handle that precision
|
||||
var MAX_EXPIRY = Math.pow(2, 62);
|
||||
for (i = 0; i < aCookies.length; i++) {
|
||||
for (let i = 0; i < aCookies.length; i++) {
|
||||
var cookie = aCookies[i];
|
||||
try {
|
||||
CookieSvc.add(cookie.host, cookie.path || "", cookie.name || "",
|
||||
@ -2568,7 +2586,7 @@ SessionStoreService.prototype = {
|
||||
// parentheses are for backwards compatibility with Firefox 2.0 and 3.0
|
||||
stateString.data = "(" + this._toJSONString(aStateObj) + ")";
|
||||
|
||||
ObserverSvc.notifyObservers(stateString, "sessionstore-state-write", "");
|
||||
Services.obs.notifyObservers(stateString, "sessionstore-state-write", "");
|
||||
|
||||
// don't touch the file if an observer has deleted all state data
|
||||
if (stateString.data)
|
||||
@ -2604,7 +2622,7 @@ SessionStoreService.prototype = {
|
||||
* Callback each window is passed to
|
||||
*/
|
||||
_forEachBrowserWindow: function sss_forEachBrowserWindow(aFunc) {
|
||||
var windowsEnum = WindowMediator.getEnumerator("navigator:browser");
|
||||
var windowsEnum = Services.wm.getEnumerator("navigator:browser");
|
||||
|
||||
while (windowsEnum.hasMoreElements()) {
|
||||
var window = windowsEnum.getNext();
|
||||
@ -2619,7 +2637,7 @@ SessionStoreService.prototype = {
|
||||
* @returns Window reference
|
||||
*/
|
||||
_getMostRecentBrowserWindow: function sss_getMostRecentBrowserWindow() {
|
||||
var win = WindowMediator.getMostRecentWindow("navigator:browser");
|
||||
var win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (!win)
|
||||
return null;
|
||||
if (!win.closed)
|
||||
@ -2627,7 +2645,7 @@ SessionStoreService.prototype = {
|
||||
|
||||
#ifdef BROKEN_WM_Z_ORDER
|
||||
win = null;
|
||||
var windowsEnum = WindowMediator.getEnumerator("navigator:browser");
|
||||
var windowsEnum = Services.wm.getEnumerator("navigator:browser");
|
||||
// this is oldest to newest, so this gets a bit ugly
|
||||
while (windowsEnum.hasMoreElements()) {
|
||||
let nextWin = windowsEnum.getNext();
|
||||
@ -2637,7 +2655,7 @@ SessionStoreService.prototype = {
|
||||
return win;
|
||||
#else
|
||||
var windowsEnum =
|
||||
WindowMediator.getZOrderDOMWindowEnumerator("navigator:browser", true);
|
||||
Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", true);
|
||||
while (windowsEnum.hasMoreElements()) {
|
||||
win = windowsEnum.getNext();
|
||||
if (!win.closed)
|
||||
@ -2653,9 +2671,7 @@ SessionStoreService.prototype = {
|
||||
* setBrowserState to treat them as open windows.
|
||||
*/
|
||||
_handleClosedWindows: function sss_handleClosedWindows() {
|
||||
var windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].
|
||||
getService(Ci.nsIWindowMediator);
|
||||
var windowsEnum = windowMediator.getEnumerator("navigator:browser");
|
||||
var windowsEnum = Services.wm.getEnumerator("navigator:browser");
|
||||
|
||||
while (windowsEnum.hasMoreElements()) {
|
||||
var window = windowsEnum.getNext();
|
||||
@ -2686,8 +2702,8 @@ SessionStoreService.prototype = {
|
||||
});
|
||||
|
||||
var window =
|
||||
WindowWatcher.openWindow(null, this._prefBranch.getCharPref("chromeURL"),
|
||||
"_blank", features, argString);
|
||||
Services.ww.openWindow(null, this._prefBranch.getCharPref("chromeURL"),
|
||||
"_blank", features, argString);
|
||||
|
||||
do {
|
||||
var ID = "window" + Math.random();
|
||||
@ -2785,7 +2801,7 @@ SessionStoreService.prototype = {
|
||||
* @returns nsIURI
|
||||
*/
|
||||
_getURIFromString: function sss_getURIFromString(aString) {
|
||||
return IOSvc.newURI(aString, null, null);
|
||||
return Services.io.newURI(aString, null, null);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -2832,8 +2848,7 @@ SessionStoreService.prototype = {
|
||||
return false;
|
||||
|
||||
// don't automatically restore in Safe Mode
|
||||
let XRE = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
|
||||
if (XRE.inSafeMode)
|
||||
if (Services.appinfo.inSafeMode)
|
||||
return true;
|
||||
|
||||
let max_resumed_crashes =
|
||||
@ -2882,7 +2897,7 @@ SessionStoreService.prototype = {
|
||||
this._restoreCount--;
|
||||
if (this._restoreCount == 0) {
|
||||
// This was the last window restored at startup, notify observers.
|
||||
ObserverSvc.notifyObservers(null,
|
||||
Services.obs.notifyObservers(null,
|
||||
this._browserSetState ? NOTIFY_BROWSER_STATE_RESTORED : NOTIFY_WINDOWS_RESTORED,
|
||||
"");
|
||||
this._browserSetState = false;
|
||||
@ -2963,9 +2978,9 @@ SessionStoreService.prototype = {
|
||||
var self = this;
|
||||
NetUtil.asyncCopy(istream, ostream, function(rc) {
|
||||
if (Components.isSuccessCode(rc)) {
|
||||
ObserverSvc.notifyObservers(null,
|
||||
"sessionstore-state-write-complete",
|
||||
"");
|
||||
Services.obs.notifyObservers(null,
|
||||
"sessionstore-state-write-complete",
|
||||
"");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -47,6 +47,14 @@ function test() {
|
||||
let tab = gBrowser.addTab(testURL);
|
||||
tab.linkedBrowser.addEventListener("load", function(aEvent) {
|
||||
this.removeEventListener("load", arguments.callee, true);
|
||||
|
||||
let expectedValue = "try to save me";
|
||||
// Since bug 537289 we only save non-default values, so we need to set each
|
||||
// form field's value after load.
|
||||
let formEls = aEvent.originalTarget.forms[0].elements;
|
||||
for (let i = 0; i < formEls.length; i++)
|
||||
formEls[i].value = expectedValue;
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"]
|
||||
@ -56,7 +64,7 @@ function test() {
|
||||
|
||||
let countGood = 0, countBad = 0;
|
||||
for each (let value in savedFormData) {
|
||||
if (value == "save me")
|
||||
if (value == expectedValue)
|
||||
countGood++;
|
||||
else
|
||||
countBad++;
|
||||
|
@ -5,22 +5,24 @@
|
||||
<head><title>Test for bug 456342</title></head>
|
||||
|
||||
<body>
|
||||
<form>
|
||||
<h3>Non-standard <input>s</h3>
|
||||
<p>Search <input type="search" value="save me" id="searchTerm"/></p>
|
||||
<p>Image Search: <input type="image search" value="save me" /></p>
|
||||
<p>Autocomplete: <input type="autocomplete" value="save me" name="fill-in"/></p>
|
||||
<p>Mistyped: <input type="txet" value="save me" name="mistyped"/></p>
|
||||
<p>Search <input type="search" id="searchTerm"/></p>
|
||||
<p>Image Search: <input type="image search" /></p>
|
||||
<p>Autocomplete: <input type="autocomplete" name="fill-in"/></p>
|
||||
<p>Mistyped: <input type="txet" name="mistyped"/></p>
|
||||
|
||||
<h3>Ignored types</h3>
|
||||
<input type="hidden" value="don't save" name="hideme"/>
|
||||
<input type="HIDDEN" value="don't save" name="hideme2"/>
|
||||
<input type="submit" value="don't save" name="submit"/>
|
||||
<input type="reset" value="don't save" name="reset"/>
|
||||
<input type="image" value="don't save" name="image"/>
|
||||
<input type="button" value="don't save" name="button"/>
|
||||
<input type="password" value="don't save" name="password"/>
|
||||
<input type="PassWord" value="don't save" name="password2"/>
|
||||
<input type="PASSWORD" value="don't save" name="password3"/>
|
||||
<input type="hidden" name="hideme"/>
|
||||
<input type="HIDDEN" name="hideme2"/>
|
||||
<input type="submit" name="submit"/>
|
||||
<input type="reset" name="reset"/>
|
||||
<input type="image" name="image"/>
|
||||
<input type="button" name="button"/>
|
||||
<input type="password" name="password"/>
|
||||
<input type="PassWord" name="password2"/>
|
||||
<input type="PASSWORD" name="password3"/>
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -81,7 +81,7 @@
|
||||
#ifdef XP_MACOSX
|
||||
@BINPATH@/plugins/DefaultPlugin.plugin/
|
||||
#elifdef XP_UNIX
|
||||
@BINPATH@/plugins/libnullplugin.so
|
||||
@BINPATH@/plugins/libnullplugin@DLL_SUFFIX@
|
||||
#elifdef XP_WIN32
|
||||
#ifndef WINCE
|
||||
@BINPATH@/plugins/npnul32.dll
|
||||
@ -191,7 +191,7 @@
|
||||
@BINPATH@/components/layout_xul.xpt
|
||||
#ifdef XP_UNIX
|
||||
#ifndef XP_MACOSX
|
||||
@BINPATH@/components/libimgicon.so
|
||||
@BINPATH@/components/libimgicon@DLL_SUFFIX@
|
||||
#endif
|
||||
#endif
|
||||
@BINPATH@/components/locale.xpt
|
||||
|
@ -48,6 +48,7 @@ class nsITransferable;
|
||||
class nsACString;
|
||||
class nsAString;
|
||||
class nsIDOMNode;
|
||||
class nsIPresShell;
|
||||
|
||||
class nsCopySupport
|
||||
{
|
||||
@ -66,16 +67,51 @@ class nsCopySupport
|
||||
static nsresult ImageCopy(nsIImageLoadingContent* aImageElement,
|
||||
PRInt32 aCopyFlags);
|
||||
|
||||
// Given the current selection, find the target that
|
||||
// before[copy,cut,paste] and [copy,cut,paste] events will fire on.
|
||||
static nsresult GetClipboardEventTarget(nsISelection *aSel,
|
||||
nsIDOMNode **aEventTarget);
|
||||
|
||||
// Get the selection as a transferable. Similar to HTMLCopy except does
|
||||
// not deal with the clipboard.
|
||||
static nsresult GetTransferableForSelection(nsISelection * aSelection,
|
||||
nsIDocument * aDocument,
|
||||
nsITransferable ** aTransferable);
|
||||
|
||||
/**
|
||||
* Retrieve the selection for the given document. If the current focus
|
||||
* within the document has its own selection, aSelection will be set to it
|
||||
* and this focused content node returned. Otherwise, aSelection will be
|
||||
* set to the document's selection and null will be returned.
|
||||
*/
|
||||
static nsIContent* GetSelectionForCopy(nsIDocument* aDocument,
|
||||
nsISelection** aSelection);
|
||||
|
||||
/**
|
||||
* Returns true if a copy operation is currently permitted based on the
|
||||
* current focus and selection within the specified document.
|
||||
*/
|
||||
static PRBool CanCopy(nsIDocument* aDocument);
|
||||
|
||||
/**
|
||||
* Fires a cut, copy or paste event, on the given presshell, depending
|
||||
* on the value of aType, which should be either NS_CUT, NS_COPY or
|
||||
* NS_PASTE, and perform the default copy action if the event was not
|
||||
* cancelled.
|
||||
*
|
||||
* If aSelection is specified, then this selection is used as the target
|
||||
* of the operation. Otherwise, GetSelectionForCopy is used to retrieve
|
||||
* the current selection.
|
||||
*
|
||||
* This will fire a cut, copy or paste event at the node at the start
|
||||
* point of the selection. If a cut or copy event is not cancelled, the
|
||||
* selection is copied to the clipboard and true is returned. Paste events
|
||||
* have no default behaviour but true will be returned. It is expected
|
||||
* that the caller will execute any needed default paste behaviour. Also,
|
||||
* note that this method only copies text to the clipboard, the caller is
|
||||
* responsible for removing the content during a cut operation if true is
|
||||
* returned.
|
||||
*
|
||||
* If the event is cancelled or an error occurs, false will be returned.
|
||||
*/
|
||||
static PRBool FireClipboardEvent(PRInt32 aType,
|
||||
nsIPresShell* aPresShell,
|
||||
nsISelection* aSelection);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -300,7 +300,9 @@ public:
|
||||
returns a non-null value for nsIContent::GetText() */
|
||||
eDATA_NODE = 1 << 10,
|
||||
/** nsHTMLMediaElement */
|
||||
eMEDIA = 1 << 11
|
||||
eMEDIA = 1 << 11,
|
||||
/** animation elements */
|
||||
eANIMATION = 1 << 12
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -50,18 +50,26 @@
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsIDOMRange.h"
|
||||
#include "imgIContainer.h"
|
||||
#include "nsIPresShell.h"
|
||||
#include "nsFocusManager.h"
|
||||
#include "nsEventDispatcher.h"
|
||||
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIContentViewerEdit.h"
|
||||
#include "nsIClipboardDragDropHooks.h"
|
||||
#include "nsIClipboardDragDropHookList.h"
|
||||
#include "nsIClipboardHelper.h"
|
||||
#include "nsISelectionController.h"
|
||||
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDOMNode.h"
|
||||
#include "nsIDOMElement.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIHTMLDocument.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsGUIEvent.h"
|
||||
#include "nsIFrame.h"
|
||||
|
||||
// image copy stuff
|
||||
#include "nsIImageLoadingContent.h"
|
||||
@ -582,26 +590,128 @@ static nsresult AppendDOMNode(nsITransferable *aTransferable,
|
||||
return AppendString(aTransferable, context, kHTMLContext);
|
||||
}
|
||||
|
||||
// Find the target that onbefore[copy,cut,paste] and on[copy,cut,paste]
|
||||
// events will fire on -- the start node of the copy selection.
|
||||
nsresult nsCopySupport::GetClipboardEventTarget(nsISelection *aSel,
|
||||
nsIDOMNode **aEventTarget)
|
||||
nsIContent*
|
||||
nsCopySupport::GetSelectionForCopy(nsIDocument* aDocument, nsISelection** aSelection)
|
||||
{
|
||||
NS_ENSURE_ARG(aSel);
|
||||
NS_ENSURE_ARG_POINTER(aEventTarget);
|
||||
*aEventTarget = nsnull;
|
||||
*aSelection = nsnull;
|
||||
|
||||
nsCOMPtr<nsIDOMRange> range;
|
||||
nsresult rv = aSel->GetRangeAt(0, getter_AddRefs(range));
|
||||
if (rv == NS_ERROR_INVALID_ARG) // empty selection
|
||||
return NS_ERROR_FAILURE;
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsIPresShell* presShell = aDocument->GetPrimaryShell();
|
||||
if (!presShell)
|
||||
return nsnull;
|
||||
|
||||
if (!range)
|
||||
return NS_ERROR_FAILURE;
|
||||
// check if the focused node in the window has a selection
|
||||
nsCOMPtr<nsPIDOMWindow> focusedWindow;
|
||||
nsIContent* content =
|
||||
nsFocusManager::GetFocusedDescendant(aDocument->GetWindow(), PR_FALSE,
|
||||
getter_AddRefs(focusedWindow));
|
||||
if (content) {
|
||||
nsIFrame* frame = content->GetPrimaryFrame();
|
||||
if (frame) {
|
||||
nsCOMPtr<nsISelectionController> selCon;
|
||||
frame->GetSelectionController(presShell->GetPresContext(), getter_AddRefs(selCon));
|
||||
if (selCon) {
|
||||
selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, aSelection);
|
||||
return content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rv = range->GetStartContainer(aEventTarget);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return (*aEventTarget) ? NS_OK : NS_ERROR_FAILURE;
|
||||
// if no selection was found, use the main selection for the window
|
||||
NS_IF_ADDREF(*aSelection = presShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL));
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsCopySupport::CanCopy(nsIDocument* aDocument)
|
||||
{
|
||||
if (!aDocument)
|
||||
return PR_FALSE;
|
||||
|
||||
nsCOMPtr<nsISelection> sel;
|
||||
GetSelectionForCopy(aDocument, getter_AddRefs(sel));
|
||||
|
||||
PRBool isCollapsed;
|
||||
sel->GetIsCollapsed(&isCollapsed);
|
||||
return !isCollapsed;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsCopySupport::FireClipboardEvent(PRInt32 aType, nsIPresShell* aPresShell, nsISelection* aSelection)
|
||||
{
|
||||
NS_ASSERTION(aType == NS_CUT || aType == NS_COPY || aType == NS_PASTE,
|
||||
"Invalid clipboard event type");
|
||||
|
||||
nsCOMPtr<nsIPresShell> presShell = aPresShell;
|
||||
if (!presShell)
|
||||
return PR_FALSE;
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = presShell->GetDocument();
|
||||
if (!doc)
|
||||
return PR_FALSE;
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> piWindow = doc->GetWindow();
|
||||
if (!piWindow)
|
||||
return PR_FALSE;
|
||||
|
||||
// if a selection was not supplied, try to find it
|
||||
nsCOMPtr<nsIContent> content;
|
||||
nsCOMPtr<nsISelection> sel = aSelection;
|
||||
if (!sel)
|
||||
content = GetSelectionForCopy(doc, getter_AddRefs(sel));
|
||||
|
||||
// retrieve the event target node from the start of the selection
|
||||
if (sel) {
|
||||
// Only cut or copy when there is an uncollapsed selection
|
||||
if (aType == NS_CUT || aType == NS_COPY) {
|
||||
PRBool isCollapsed;
|
||||
sel->GetIsCollapsed(&isCollapsed);
|
||||
if (isCollapsed)
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMRange> range;
|
||||
nsresult rv = sel->GetRangeAt(0, getter_AddRefs(range));
|
||||
if (NS_SUCCEEDED(rv) && range) {
|
||||
nsCOMPtr<nsIDOMNode> startContainer;
|
||||
range->GetStartContainer(getter_AddRefs(startContainer));
|
||||
if (startContainer)
|
||||
content = do_QueryInterface(startContainer);
|
||||
}
|
||||
}
|
||||
|
||||
// if no content node was set, just get the root
|
||||
if (!content) {
|
||||
content = doc->GetRootContent();
|
||||
if (!content)
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
// It seems to be unsafe to fire an event handler during reflow (bug 393696)
|
||||
if (!nsContentUtils::IsSafeToRunScript())
|
||||
return PR_FALSE;
|
||||
|
||||
// next, fire the cut or copy event
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
nsEvent evt(PR_TRUE, aType);
|
||||
nsEventDispatcher::Dispatch(content, presShell->GetPresContext(), &evt, nsnull,
|
||||
&status);
|
||||
// if the event was cancelled, don't do the clipboard operation
|
||||
if (status == nsEventStatus_eConsumeNoDefault)
|
||||
return PR_FALSE;
|
||||
|
||||
// no need to do anything special during a paste. Either an event listener
|
||||
// took care of it and cancelled the event, or the caller will handle it.
|
||||
// Return true to indicate the event wasn't cancelled.
|
||||
if (aType == NS_PASTE)
|
||||
return PR_TRUE;
|
||||
|
||||
// call the copy code
|
||||
if (NS_FAILED(nsCopySupport::HTMLCopy(sel, doc, nsIClipboard::kGlobalClipboard)))
|
||||
return PR_FALSE;
|
||||
|
||||
// Now that we have copied, update the clipboard commands. This should have
|
||||
// the effect of updating the paste menu item.
|
||||
piWindow->UpdateCommands(NS_LITERAL_STRING("clipboard"));
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
@ -77,33 +77,23 @@ nsContentEventHandler::nsContentEventHandler(
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentEventHandler::Init(nsQueryContentEvent* aEvent)
|
||||
nsContentEventHandler::InitCommon()
|
||||
{
|
||||
NS_ASSERTION(aEvent, "aEvent must not be null");
|
||||
|
||||
if (mSelection)
|
||||
return NS_OK;
|
||||
|
||||
aEvent->mSucceeded = PR_FALSE;
|
||||
|
||||
if (!mPresShell)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
// If text frame which has overflowing selection underline is dirty,
|
||||
// we need to flush the pending reflow here.
|
||||
nsresult rv = mPresShell->FlushPendingNotifications(Flush_Layout);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mPresShell->GetSelectionForCopy(getter_AddRefs(mSelection));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCopySupport::GetSelectionForCopy(mPresShell->GetDocument(),
|
||||
getter_AddRefs(mSelection));
|
||||
NS_ASSERTION(mSelection,
|
||||
"GetSelectionForCopy succeeded, but the result is null");
|
||||
|
||||
PRBool isCollapsed;
|
||||
rv = mSelection->GetIsCollapsed(&isCollapsed);
|
||||
if (NS_FAILED(rv))
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
aEvent->mReply.mHasSelection = !isCollapsed;
|
||||
|
||||
nsCOMPtr<nsIDOMRange> firstRange;
|
||||
rv = mSelection->GetRangeAt(0, getter_AddRefs(firstRange));
|
||||
@ -126,9 +116,26 @@ nsContentEventHandler::Init(nsQueryContentEvent* aEvent)
|
||||
|
||||
mRootContent = startNode->GetSelectionRootContent(mPresShell);
|
||||
NS_ENSURE_TRUE(mRootContent, NS_ERROR_FAILURE);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentEventHandler::Init(nsQueryContentEvent* aEvent)
|
||||
{
|
||||
NS_ASSERTION(aEvent, "aEvent must not be null");
|
||||
|
||||
nsresult rv = InitCommon();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
aEvent->mSucceeded = PR_FALSE;
|
||||
|
||||
aEvent->mReply.mContentsRoot = mRootContent.get();
|
||||
|
||||
PRBool isCollapsed;
|
||||
rv = mSelection->GetIsCollapsed(&isCollapsed);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_NOT_AVAILABLE);
|
||||
aEvent->mReply.mHasSelection = !isCollapsed;
|
||||
|
||||
nsRefPtr<nsCaret> caret;
|
||||
rv = mPresShell->GetCaret(getter_AddRefs(caret));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -143,6 +150,19 @@ nsContentEventHandler::Init(nsQueryContentEvent* aEvent)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentEventHandler::Init(nsSelectionEvent* aEvent)
|
||||
{
|
||||
NS_ASSERTION(aEvent, "aEvent must not be null");
|
||||
|
||||
nsresult rv = InitCommon();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
aEvent->mSucceeded = PR_FALSE;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Editor places a bogus BR node under its root content if the editor doesn't
|
||||
// have any text. This happens even for single line editors.
|
||||
// When we get text content and when we change the selection,
|
||||
@ -664,9 +684,8 @@ nsContentEventHandler::OnQueryCaretRect(nsQueryContentEvent* aEvent)
|
||||
nsIFrame* caretFrame = caret->GetGeometry(mSelection, &rect);
|
||||
if (!caretFrame)
|
||||
return NS_ERROR_FAILURE;
|
||||
nsPoint windowOffset(0, 0);
|
||||
caretFrame->GetWindowOffset(windowOffset);
|
||||
rect.MoveBy(windowOffset);
|
||||
rv = ConvertToRootViewRelativeOffset(caretFrame, rect);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
aEvent->mReply.mRect =
|
||||
rect.ToOutsidePixels(caretFrame->PresContext()->AppUnitsPerDevPixel());
|
||||
aEvent->mSucceeded = PR_TRUE;
|
||||
@ -747,8 +766,32 @@ nsContentEventHandler::OnQueryCharacterAtPoint(nsQueryContentEvent* aEvent)
|
||||
return rv;
|
||||
|
||||
nsIFrame* rootFrame = mPresShell->GetRootFrame();
|
||||
NS_ENSURE_TRUE(rootFrame, NS_ERROR_FAILURE);
|
||||
nsIWidget* rootWidget = rootFrame->GetWindow();
|
||||
NS_ENSURE_TRUE(rootWidget, NS_ERROR_FAILURE);
|
||||
|
||||
// The root frame's widget might be different, e.g., the event was fired on
|
||||
// a popup but the rootFrame is the document root.
|
||||
if (rootWidget != aEvent->widget) {
|
||||
NS_PRECONDITION(aEvent->widget, "The event must have the widget");
|
||||
nsIView* view = nsIView::GetViewFor(aEvent->widget);
|
||||
NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
|
||||
rootFrame = nsLayoutUtils::GetFrameFor(view);
|
||||
NS_ENSURE_TRUE(rootFrame, NS_ERROR_FAILURE);
|
||||
rootWidget = rootFrame->GetWindow();
|
||||
NS_ENSURE_TRUE(rootWidget, NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
nsQueryContentEvent eventOnRoot(PR_TRUE, NS_QUERY_CHARACTER_AT_POINT,
|
||||
rootWidget);
|
||||
eventOnRoot.refPoint = aEvent->refPoint;
|
||||
if (rootWidget != aEvent->widget) {
|
||||
eventOnRoot.refPoint += aEvent->widget->WidgetToScreenOffset();
|
||||
eventOnRoot.refPoint -= rootWidget->WidgetToScreenOffset();
|
||||
}
|
||||
nsPoint ptInRoot =
|
||||
nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, rootFrame);
|
||||
nsLayoutUtils::GetEventCoordinatesRelativeTo(&eventOnRoot, rootFrame);
|
||||
|
||||
nsIFrame* targetFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, ptInRoot);
|
||||
if (!targetFrame || targetFrame->GetType() != nsGkAtoms::textFrame) {
|
||||
// there is no character at the point.
|
||||
@ -882,11 +925,17 @@ nsContentEventHandler::OnSelectionEvent(nsSelectionEvent* aEvent)
|
||||
aEvent->mSucceeded = PR_FALSE;
|
||||
|
||||
// Get selection to manipulate
|
||||
nsCOMPtr<nsISelection> sel;
|
||||
// XXX why do we need to get them from ISM? This method should work fine
|
||||
// without ISM.
|
||||
nsresult rv = nsIMEStateManager::
|
||||
GetFocusSelectionAndRoot(getter_AddRefs(sel),
|
||||
GetFocusSelectionAndRoot(getter_AddRefs(mSelection),
|
||||
getter_AddRefs(mRootContent));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (rv != NS_ERROR_NOT_AVAILABLE) {
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
rv = Init(aEvent);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Get range from offset and length
|
||||
nsRefPtr<nsRange> range = new nsRange();
|
||||
@ -906,32 +955,32 @@ nsContentEventHandler::OnSelectionEvent(nsSelectionEvent* aEvent)
|
||||
nsCOMPtr<nsIDOMNode> endDomNode(do_QueryInterface(endNode));
|
||||
NS_ENSURE_TRUE(startDomNode && endDomNode, NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsCOMPtr<nsISelectionPrivate> selPrivate = do_QueryInterface(sel);
|
||||
nsCOMPtr<nsISelectionPrivate> selPrivate = do_QueryInterface(mSelection);
|
||||
NS_ENSURE_TRUE(selPrivate, NS_ERROR_UNEXPECTED);
|
||||
selPrivate->StartBatchChanges();
|
||||
|
||||
// Clear selection first before setting
|
||||
rv = sel->RemoveAllRanges();
|
||||
rv = mSelection->RemoveAllRanges();
|
||||
// Need to call EndBatchChanges at the end even if call failed
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (aEvent->mReversed) {
|
||||
rv = sel->Collapse(endDomNode, endOffset);
|
||||
rv = mSelection->Collapse(endDomNode, endOffset);
|
||||
} else {
|
||||
rv = sel->Collapse(startDomNode, startOffset);
|
||||
rv = mSelection->Collapse(startDomNode, startOffset);
|
||||
}
|
||||
if (NS_SUCCEEDED(rv) &&
|
||||
(startDomNode != endDomNode || startOffset != endOffset)) {
|
||||
if (aEvent->mReversed) {
|
||||
rv = sel->Extend(startDomNode, startOffset);
|
||||
rv = mSelection->Extend(startDomNode, startOffset);
|
||||
} else {
|
||||
rv = sel->Extend(endDomNode, endOffset);
|
||||
rv = mSelection->Extend(endDomNode, endOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
selPrivate->EndBatchChanges();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsISelection2>(do_QueryInterface(sel))->ScrollIntoView(
|
||||
nsCOMPtr<nsISelection2>(do_QueryInterface(mSelection))->ScrollIntoView(
|
||||
nsISelectionController::SELECTION_FOCUS_REGION, PR_FALSE, -1, -1);
|
||||
aEvent->mSucceeded = PR_TRUE;
|
||||
return NS_OK;
|
||||
|
@ -95,6 +95,10 @@ protected:
|
||||
nsCOMPtr<nsIContent> mRootContent;
|
||||
|
||||
nsresult Init(nsQueryContentEvent* aEvent);
|
||||
nsresult Init(nsSelectionEvent* aEvent);
|
||||
|
||||
// InitCommon() is called from each Init().
|
||||
nsresult InitCommon();
|
||||
|
||||
public:
|
||||
// FlatText means the text that is generated from DOM tree. The BR elements
|
||||
|
@ -483,7 +483,12 @@ nsEventDispatcher::Dispatch(nsISupports* aTarget,
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
if (target->GetContextForEventHandlers(&rv) ||
|
||||
NS_FAILED(rv)) {
|
||||
NS_ERROR("This is unsafe!");
|
||||
nsCOMPtr<nsINode> node = do_QueryInterface(target);
|
||||
if (node && nsContentUtils::IsChromeDoc(node->GetOwnerDoc())) {
|
||||
NS_WARNING("Fix the caller!");
|
||||
} else {
|
||||
NS_ERROR("This is unsafe! Fix the caller!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,6 +81,7 @@ GetRefreshDriverForDoc(nsIDocument* aDoc)
|
||||
|
||||
nsSMILAnimationController::nsSMILAnimationController()
|
||||
: mResampleNeeded(PR_FALSE),
|
||||
mDeferredStartSampling(PR_FALSE),
|
||||
mDocument(nsnull)
|
||||
{
|
||||
mAnimationElementTable.Init();
|
||||
@ -150,7 +151,11 @@ nsSMILAnimationController::Resume(PRUint32 aType)
|
||||
|
||||
if (wasPaused && !mPauseState && mChildContainerTable.Count()) {
|
||||
Sample(); // Run the first sample manually
|
||||
StartSampling(GetRefreshDriverForDoc(mDocument));
|
||||
if (mAnimationElementTable.Count()) {
|
||||
StartSampling(GetRefreshDriverForDoc(mDocument));
|
||||
} else {
|
||||
mDeferredStartSampling = PR_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -185,6 +190,14 @@ nsSMILAnimationController::RegisterAnimationElement(
|
||||
nsISMILAnimationElement* aAnimationElement)
|
||||
{
|
||||
mAnimationElementTable.PutEntry(aAnimationElement);
|
||||
if (mDeferredStartSampling) {
|
||||
// mAnimationElementTable was empty until we just inserted its first element
|
||||
NS_ABORT_IF_FALSE(mAnimationElementTable.Count() == 1,
|
||||
"we shouldn't have deferred sampling if we already had "
|
||||
"animations registered");
|
||||
mDeferredStartSampling = PR_FALSE;
|
||||
StartSampling(GetRefreshDriverForDoc(mDocument));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -695,7 +708,11 @@ nsSMILAnimationController::AddChild(nsSMILTimeContainer& aChild)
|
||||
|
||||
if (!mPauseState && mChildContainerTable.Count() == 1) {
|
||||
Sample(); // Run the first sample manually
|
||||
StartSampling(GetRefreshDriverForDoc(mDocument));
|
||||
if (mAnimationElementTable.Count()) {
|
||||
StartSampling(GetRefreshDriverForDoc(mDocument));
|
||||
} else {
|
||||
mDeferredStartSampling = PR_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -183,6 +183,7 @@ protected:
|
||||
AnimationElementHashtable mAnimationElementTable;
|
||||
TimeContainerHashtable mChildContainerTable;
|
||||
PRPackedBool mResampleNeeded;
|
||||
PRPackedBool mDeferredStartSampling;
|
||||
|
||||
// Store raw ptr to mDocument. It owns the controller, so controller
|
||||
// shouldn't outlive it
|
||||
|
@ -408,6 +408,12 @@ nsSVGAnimationElement::UnsetAttr(PRInt32 aNamespaceID,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsSVGAnimationElement::IsNodeOfType(PRUint32 aFlags) const
|
||||
{
|
||||
return !(aFlags & ~(eCONTENT | eELEMENT | eSVG | eANIMATION));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Implementation helpers
|
||||
|
||||
|
@ -74,10 +74,11 @@ public:
|
||||
PRBool aCompileEventHandlers);
|
||||
virtual void UnbindFromTree(PRBool aDeep, PRBool aNullParent);
|
||||
|
||||
// nsIContent specializations
|
||||
virtual nsresult UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aAttribute,
|
||||
PRBool aNotify);
|
||||
|
||||
virtual PRBool IsNodeOfType(PRUint32 aFlags) const;
|
||||
|
||||
// nsGenericElement specializations
|
||||
virtual PRBool ParseAttribute(PRInt32 aNamespaceID,
|
||||
nsIAtom* aAttribute,
|
||||
|
@ -151,7 +151,13 @@ function checkResults(root, step)
|
||||
if (step > 0)
|
||||
adjtestid += " dynamic step " + step;
|
||||
|
||||
if (debug) {
|
||||
var stilltodo = ((step == 0 && notWorkingYet) || (step > 0 && notWorkingYetDynamic));
|
||||
if (stilltodo)
|
||||
todo(false, adjtestid);
|
||||
else
|
||||
ok(!error, adjtestid);
|
||||
|
||||
if ((!stilltodo && error) || debug) {
|
||||
// for debugging, serialize the XML output
|
||||
var serializedXML = "";
|
||||
var rootNodes = actualoutput.childNodes;
|
||||
@ -163,14 +169,11 @@ function checkResults(root, step)
|
||||
|
||||
// remove the XUL namespace declarations to make the output more readable
|
||||
const nsrepl = new RegExp("xmlns=\"" + XUL_NS + "\" ", "g");
|
||||
dump("-------- " + adjtestid + " " + error + ":\n" +
|
||||
serializedXML.replace(nsrepl, "") + "\n");
|
||||
serializedXML = serializedXML.replace(nsrepl, "");
|
||||
if (debug)
|
||||
dump("-------- " + adjtestid + " " + error + ":\n" + serializedXML + "\n");
|
||||
is(serializedXML, "Same", "Error is: " + error);
|
||||
}
|
||||
|
||||
if ((step == 0 && notWorkingYet) || (step > 0 && notWorkingYetDynamic))
|
||||
todo(false, adjtestid);
|
||||
else
|
||||
ok(!error, adjtestid);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -40,12 +40,9 @@
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(1691a02f-53b2-4cb8-8769-48e7efc908b8)]
|
||||
[scriptable, uuid(AF13EA3A-D488-4308-B843-526E055AB943)]
|
||||
interface nsIContentViewerEdit : nsISupports
|
||||
{
|
||||
void search();
|
||||
readonly attribute boolean searchable;
|
||||
|
||||
void clearSelection();
|
||||
void selectAll();
|
||||
|
||||
@ -62,12 +59,6 @@ interface nsIContentViewerEdit : nsISupports
|
||||
void copyImage(in long aCopyFlags);
|
||||
readonly attribute boolean inImage;
|
||||
|
||||
void cutSelection();
|
||||
readonly attribute boolean cutable;
|
||||
|
||||
void paste();
|
||||
readonly attribute boolean pasteable;
|
||||
|
||||
AString getContents(in string aMimeType, in boolean aSelectionOnly);
|
||||
readonly attribute boolean canGetContents;
|
||||
};
|
||||
|
@ -99,6 +99,7 @@ CPPSRCS = \
|
||||
nsDOMClassInfo.cpp \
|
||||
nsScriptNameSpaceManager.cpp \
|
||||
nsDOMScriptObjectFactory.cpp \
|
||||
nsQueryContentEventResult.cpp \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/dom/dom-config.mk
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "nsIDOMNSEvent.h"
|
||||
#include "nsIPrivateDOMEvent.h"
|
||||
#include "nsDOMWindowUtils.h"
|
||||
#include "nsQueryContentEventResult.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsFocusManager.h"
|
||||
@ -50,6 +51,7 @@
|
||||
#include "nsIScrollableFrame.h"
|
||||
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
|
||||
#include "nsIFrame.h"
|
||||
#include "nsIWidget.h"
|
||||
@ -70,6 +72,15 @@
|
||||
#include <gdk/gdkx.h>
|
||||
#endif
|
||||
|
||||
static PRBool IsUniversalXPConnectCapable()
|
||||
{
|
||||
PRBool hasCap = PR_FALSE;
|
||||
nsresult rv = nsContentUtils::GetSecurityManager()->
|
||||
IsCapabilityEnabled("UniversalXPConnect", &hasCap);
|
||||
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
||||
return hasCap;
|
||||
}
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsDOMWindowUtils)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMWindowUtils)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMWindowUtils)
|
||||
@ -131,9 +142,9 @@ nsDOMWindowUtils::GetDocCharsetIsForced(PRBool *aIsForced)
|
||||
{
|
||||
*aIsForced = PR_FALSE;
|
||||
|
||||
PRBool hasCap = PR_FALSE;
|
||||
if (NS_FAILED(nsContentUtils::GetSecurityManager()->IsCapabilityEnabled("UniversalXPConnect", &hasCap)) || !hasCap)
|
||||
if (!IsUniversalXPConnectCapable()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
if (mWindow) {
|
||||
nsCOMPtr<nsIDocument> doc(do_QueryInterface(mWindow->GetExtantDocument()));
|
||||
@ -147,10 +158,9 @@ NS_IMETHODIMP
|
||||
nsDOMWindowUtils::GetDocumentMetadata(const nsAString& aName,
|
||||
nsAString& aValue)
|
||||
{
|
||||
PRBool hasCap = PR_FALSE;
|
||||
if (NS_FAILED(nsContentUtils::GetSecurityManager()->IsCapabilityEnabled("UniversalXPConnect", &hasCap))
|
||||
|| !hasCap)
|
||||
if (!IsUniversalXPConnectCapable()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
if (mWindow) {
|
||||
nsCOMPtr<nsIDocument> doc(do_QueryInterface(mWindow->GetExtantDocument()));
|
||||
@ -211,10 +221,9 @@ nsDOMWindowUtils::SendMouseEvent(const nsAString& aType,
|
||||
PRInt32 aModifiers,
|
||||
PRBool aIgnoreRootScrollFrame)
|
||||
{
|
||||
PRBool hasCap = PR_FALSE;
|
||||
if (NS_FAILED(nsContentUtils::GetSecurityManager()->IsCapabilityEnabled("UniversalXPConnect", &hasCap))
|
||||
|| !hasCap)
|
||||
if (!IsUniversalXPConnectCapable()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
// get the widget to send the event to
|
||||
nsPoint offset;
|
||||
@ -276,10 +285,9 @@ nsDOMWindowUtils::SendMouseScrollEvent(const nsAString& aType,
|
||||
PRInt32 aDelta,
|
||||
PRInt32 aModifiers)
|
||||
{
|
||||
PRBool hasCap = PR_FALSE;
|
||||
if (NS_FAILED(nsContentUtils::GetSecurityManager()->IsCapabilityEnabled("UniversalXPConnect", &hasCap))
|
||||
|| !hasCap)
|
||||
if (!IsUniversalXPConnectCapable()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
// get the widget to send the event to
|
||||
nsPoint offset;
|
||||
@ -327,10 +335,9 @@ nsDOMWindowUtils::SendKeyEvent(const nsAString& aType,
|
||||
PRBool aPreventDefault,
|
||||
PRBool* aDefaultActionTaken)
|
||||
{
|
||||
PRBool hasCap = PR_FALSE;
|
||||
if (NS_FAILED(nsContentUtils::GetSecurityManager()->IsCapabilityEnabled("UniversalXPConnect", &hasCap))
|
||||
|| !hasCap)
|
||||
if (!IsUniversalXPConnectCapable()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
// get the widget to send the event to
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
@ -378,10 +385,9 @@ nsDOMWindowUtils::SendNativeKeyEvent(PRInt32 aNativeKeyboardLayout,
|
||||
const nsAString& aCharacters,
|
||||
const nsAString& aUnmodifiedCharacters)
|
||||
{
|
||||
PRBool hasCap = PR_FALSE;
|
||||
if (NS_FAILED(nsContentUtils::GetSecurityManager()->IsCapabilityEnabled("UniversalXPConnect", &hasCap))
|
||||
|| !hasCap)
|
||||
if (!IsUniversalXPConnectCapable()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
// get the widget to send the event to
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
@ -399,10 +405,9 @@ nsDOMWindowUtils::SendNativeMouseEvent(PRInt32 aScreenX,
|
||||
PRInt32 aModifierFlags,
|
||||
nsIDOMElement* aElement)
|
||||
{
|
||||
PRBool hasCap = PR_FALSE;
|
||||
if (NS_FAILED(nsContentUtils::GetSecurityManager()->IsCapabilityEnabled("UniversalXPConnect", &hasCap))
|
||||
|| !hasCap)
|
||||
if (!IsUniversalXPConnectCapable()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
// get the widget to send the event to
|
||||
nsCOMPtr<nsIWidget> widget = GetWidgetForElement(aElement);
|
||||
@ -416,10 +421,9 @@ nsDOMWindowUtils::SendNativeMouseEvent(PRInt32 aScreenX,
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::ActivateNativeMenuItemAt(const nsAString& indexString)
|
||||
{
|
||||
PRBool hasCap = PR_FALSE;
|
||||
if (NS_FAILED(nsContentUtils::GetSecurityManager()->IsCapabilityEnabled("UniversalXPConnect", &hasCap))
|
||||
|| !hasCap)
|
||||
if (!IsUniversalXPConnectCapable()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
// get the widget to send the event to
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
@ -432,10 +436,9 @@ nsDOMWindowUtils::ActivateNativeMenuItemAt(const nsAString& indexString)
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::ForceUpdateNativeMenuAt(const nsAString& indexString)
|
||||
{
|
||||
PRBool hasCap = PR_FALSE;
|
||||
if (NS_FAILED(nsContentUtils::GetSecurityManager()->IsCapabilityEnabled("UniversalXPConnect", &hasCap))
|
||||
|| !hasCap)
|
||||
if (!IsUniversalXPConnectCapable()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
// get the widget to send the event to
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
@ -489,10 +492,9 @@ nsDOMWindowUtils::GetWidgetForElement(nsIDOMElement* aElement)
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::Focus(nsIDOMElement* aElement)
|
||||
{
|
||||
PRBool hasCap = PR_FALSE;
|
||||
if (NS_FAILED(nsContentUtils::GetSecurityManager()->IsCapabilityEnabled(
|
||||
"UniversalXPConnect", &hasCap)) || !hasCap)
|
||||
if (!IsUniversalXPConnectCapable()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
if (fm) {
|
||||
@ -511,11 +513,9 @@ nsDOMWindowUtils::GarbageCollect()
|
||||
// NOTE: Only do this in NON debug builds, as this function can useful
|
||||
// during debugging.
|
||||
#ifndef DEBUG
|
||||
PRBool hasCap = PR_FALSE;
|
||||
if (NS_FAILED(nsContentUtils::GetSecurityManager()->
|
||||
IsCapabilityEnabled("UniversalXPConnect", &hasCap))
|
||||
|| !hasCap)
|
||||
if (!IsUniversalXPConnectCapable()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
#endif
|
||||
|
||||
nsJSContext::CC();
|
||||
@ -554,10 +554,9 @@ nsDOMWindowUtils::SendSimpleGestureEvent(const nsAString& aType,
|
||||
PRFloat64 aDelta,
|
||||
PRInt32 aModifiers)
|
||||
{
|
||||
PRBool hasCap = PR_FALSE;
|
||||
if (NS_FAILED(nsContentUtils::GetSecurityManager()->IsCapabilityEnabled("UniversalXPConnect", &hasCap))
|
||||
|| !hasCap)
|
||||
if (!IsUniversalXPConnectCapable()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
// get the widget to send the event to
|
||||
nsPoint offset;
|
||||
@ -634,9 +633,9 @@ nsDOMWindowUtils::CompareCanvases(nsIDOMHTMLCanvasElement *aCanvas1,
|
||||
PRUint32* aMaxDifference,
|
||||
PRUint32* retVal)
|
||||
{
|
||||
PRBool hasCap = PR_FALSE;
|
||||
if (NS_FAILED(nsContentUtils::GetSecurityManager()->IsCapabilityEnabled("UniversalXPConnect", &hasCap)) || !hasCap)
|
||||
if (!IsUniversalXPConnectCapable()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
if (aCanvas1 == nsnull ||
|
||||
aCanvas2 == nsnull ||
|
||||
@ -723,11 +722,9 @@ nsDOMWindowUtils::ClearMozAfterPaintEvents()
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::DisableNonTestMouseEvents(PRBool aDisable)
|
||||
{
|
||||
PRBool hasCap = PR_FALSE;
|
||||
if (NS_FAILED(nsContentUtils::GetSecurityManager()->
|
||||
IsCapabilityEnabled("UniversalXPConnect", &hasCap)) ||
|
||||
!hasCap)
|
||||
if (!IsUniversalXPConnectCapable()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(mWindow, NS_ERROR_FAILURE);
|
||||
nsIDocShell *docShell = mWindow->GetDocShell();
|
||||
@ -741,9 +738,9 @@ nsDOMWindowUtils::DisableNonTestMouseEvents(PRBool aDisable)
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::SuppressEventHandling(PRBool aSuppress)
|
||||
{
|
||||
PRBool hasCap = PR_FALSE;
|
||||
if (NS_FAILED(nsContentUtils::GetSecurityManager()->IsCapabilityEnabled("UniversalXPConnect", &hasCap)) || !hasCap)
|
||||
if (!IsUniversalXPConnectCapable()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc(do_QueryInterface(mWindow->GetExtantDocument()));
|
||||
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
|
||||
@ -832,11 +829,9 @@ nsDOMWindowUtils::GetScreenPixelsPerCSSPixel(float* aScreenPixels)
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::GetCOWForObject()
|
||||
{
|
||||
PRBool hasCap = PR_FALSE;
|
||||
if (NS_FAILED(nsContentUtils::GetSecurityManager()->
|
||||
IsCapabilityEnabled("UniversalXPConnect", &hasCap))
|
||||
|| !hasCap)
|
||||
if (!IsUniversalXPConnectCapable()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIXPConnect> xpc = nsContentUtils::XPConnect();
|
||||
|
||||
@ -918,14 +913,243 @@ nsDOMWindowUtils::DispatchDOMEventViaPresShell(nsIDOMNode* aTarget,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
InitEvent(nsGUIEvent &aEvent, nsIntPoint *aPt = nsnull)
|
||||
{
|
||||
if (aPt) {
|
||||
aEvent.refPoint = *aPt;
|
||||
}
|
||||
aEvent.time = PR_IntervalNow();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::SendCompositionEvent(const nsAString& aType)
|
||||
{
|
||||
if (!IsUniversalXPConnectCapable()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
// get the widget to send the event to
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
if (!widget) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
PRUint32 msg;
|
||||
if (aType.EqualsLiteral("compositionstart")) {
|
||||
msg = NS_COMPOSITION_START;
|
||||
} else if (aType.EqualsLiteral("compositionend")) {
|
||||
msg = NS_COMPOSITION_END;
|
||||
} else {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCompositionEvent compositionEvent(PR_TRUE, msg, widget);
|
||||
InitEvent(compositionEvent);
|
||||
|
||||
nsEventStatus status;
|
||||
nsresult rv = widget->DispatchEvent(&compositionEvent, status);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
AppendClause(PRInt32 aClauseLength, PRUint32 aClauseAttr,
|
||||
nsTArray<nsTextRange>* aRanges)
|
||||
{
|
||||
NS_PRECONDITION(aRanges, "aRange is null");
|
||||
if (aClauseLength == 0) {
|
||||
return;
|
||||
}
|
||||
nsTextRange range;
|
||||
range.mStartOffset = aRanges->Length() == 0 ? 0 :
|
||||
aRanges->ElementAt(aRanges->Length() - 1).mEndOffset + 1;
|
||||
range.mEndOffset = range.mStartOffset + aClauseLength;
|
||||
NS_ASSERTION(range.mStartOffset <= range.mEndOffset, "range is invalid");
|
||||
NS_PRECONDITION(aClauseAttr == NS_TEXTRANGE_RAWINPUT ||
|
||||
aClauseAttr == NS_TEXTRANGE_SELECTEDRAWTEXT ||
|
||||
aClauseAttr == NS_TEXTRANGE_CONVERTEDTEXT ||
|
||||
aClauseAttr == NS_TEXTRANGE_SELECTEDCONVERTEDTEXT,
|
||||
"aClauseAttr is invalid value");
|
||||
range.mRangeType = aClauseAttr;
|
||||
aRanges->AppendElement(range);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::SendTextEvent(const nsAString& aCompositionString,
|
||||
PRInt32 aFirstClauseLength,
|
||||
PRUint32 aFirstClauseAttr,
|
||||
PRInt32 aSecondClauseLength,
|
||||
PRUint32 aSecondClauseAttr,
|
||||
PRInt32 aThirdClauseLength,
|
||||
PRUint32 aThirdClauseAttr,
|
||||
PRInt32 aCaretStart,
|
||||
PRInt32 aCaretLength)
|
||||
{
|
||||
if (!IsUniversalXPConnectCapable()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
// get the widget to send the event to
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
if (!widget) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsTextEvent textEvent(PR_TRUE, NS_TEXT_TEXT, widget);
|
||||
InitEvent(textEvent);
|
||||
|
||||
nsAutoTArray<nsTextRange, 4> textRanges;
|
||||
NS_ENSURE_TRUE(aFirstClauseLength >= 0, NS_ERROR_INVALID_ARG);
|
||||
NS_ENSURE_TRUE(aSecondClauseLength >= 0, NS_ERROR_INVALID_ARG);
|
||||
NS_ENSURE_TRUE(aThirdClauseLength >= 0, NS_ERROR_INVALID_ARG);
|
||||
AppendClause(aFirstClauseLength, aFirstClauseAttr, &textRanges);
|
||||
AppendClause(aSecondClauseLength, aSecondClauseAttr, &textRanges);
|
||||
AppendClause(aThirdClauseLength, aThirdClauseAttr, &textRanges);
|
||||
PRInt32 len = aFirstClauseLength + aSecondClauseLength + aThirdClauseLength;
|
||||
NS_ENSURE_TRUE(len == 0 || len == aCompositionString.Length(),
|
||||
NS_ERROR_FAILURE);
|
||||
|
||||
if (aCaretStart >= 0) {
|
||||
nsTextRange range;
|
||||
range.mStartOffset = aCaretStart;
|
||||
range.mEndOffset = range.mStartOffset + aCaretLength;
|
||||
range.mRangeType = NS_TEXTRANGE_CARETPOSITION;
|
||||
textRanges.AppendElement(range);
|
||||
}
|
||||
|
||||
textEvent.theText = aCompositionString;
|
||||
|
||||
textEvent.rangeCount = textRanges.Length();
|
||||
textEvent.rangeArray = textRanges.Elements();
|
||||
|
||||
nsEventStatus status;
|
||||
nsresult rv = widget->DispatchEvent(&textEvent, status);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::SendQueryContentEvent(PRUint32 aType,
|
||||
PRUint32 aOffset, PRUint32 aLength,
|
||||
PRInt32 aX, PRInt32 aY,
|
||||
nsIQueryContentEventResult **aResult)
|
||||
{
|
||||
*aResult = nsnull;
|
||||
|
||||
if (!IsUniversalXPConnectCapable()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
// get the widget to send the event to
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
if (!widget) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (aType != NS_QUERY_SELECTED_TEXT &&
|
||||
aType != NS_QUERY_TEXT_CONTENT &&
|
||||
aType != NS_QUERY_CARET_RECT &&
|
||||
aType != NS_QUERY_TEXT_RECT &&
|
||||
aType != NS_QUERY_EDITOR_RECT &&
|
||||
aType != NS_QUERY_CHARACTER_AT_POINT) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIWidget> targetWidget = widget;
|
||||
nsIntPoint pt(aX, aY);
|
||||
|
||||
if (aType == QUERY_CHARACTER_AT_POINT) {
|
||||
// Looking for the widget at the point.
|
||||
nsQueryContentEvent dummyEvent(PR_TRUE, NS_QUERY_CONTENT_STATE, widget);
|
||||
InitEvent(dummyEvent, &pt);
|
||||
nsIFrame* popupFrame =
|
||||
nsLayoutUtils::GetPopupFrameForEventCoordinates(&dummyEvent);
|
||||
|
||||
nsIntRect widgetBounds;
|
||||
nsresult rv = widget->GetClientBounds(widgetBounds);
|
||||
|
||||
// There is no popup frame at the point and the point isn't in our widget,
|
||||
// we cannot process this request.
|
||||
NS_ENSURE_TRUE(popupFrame || widgetBounds.Contains(pt),
|
||||
NS_ERROR_FAILURE);
|
||||
|
||||
// Fire the event on the widget at the point
|
||||
if (popupFrame) {
|
||||
targetWidget = popupFrame->GetWindow();
|
||||
}
|
||||
}
|
||||
|
||||
pt += widget->WidgetToScreenOffset() - targetWidget->WidgetToScreenOffset();
|
||||
|
||||
nsQueryContentEvent queryEvent(PR_TRUE, aType, targetWidget);
|
||||
InitEvent(queryEvent, &pt);
|
||||
|
||||
switch (aType) {
|
||||
case NS_QUERY_TEXT_CONTENT:
|
||||
queryEvent.InitForQueryTextContent(aOffset, aLength);
|
||||
break;
|
||||
case NS_QUERY_CARET_RECT:
|
||||
queryEvent.InitForQueryCaretRect(aOffset);
|
||||
break;
|
||||
case NS_QUERY_TEXT_RECT:
|
||||
queryEvent.InitForQueryTextRect(aOffset, aLength);
|
||||
break;
|
||||
}
|
||||
|
||||
nsEventStatus status;
|
||||
nsresult rv = targetWidget->DispatchEvent(&queryEvent, status);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsQueryContentEventResult* result = new nsQueryContentEventResult();
|
||||
NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY);
|
||||
result->SetEventResult(widget, queryEvent);
|
||||
NS_ADDREF(*aResult = result);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::SendSelectionSetEvent(PRUint32 aOffset,
|
||||
PRUint32 aLength,
|
||||
PRBool aReverse,
|
||||
PRBool *aResult)
|
||||
{
|
||||
*aResult = PR_FALSE;
|
||||
|
||||
if (!IsUniversalXPConnectCapable()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
// get the widget to send the event to
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
if (!widget) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsSelectionEvent selectionEvent(PR_TRUE, NS_SELECTION_SET, widget);
|
||||
InitEvent(selectionEvent);
|
||||
|
||||
selectionEvent.mOffset = aOffset;
|
||||
selectionEvent.mLength = aLength;
|
||||
selectionEvent.mReversed = aReverse;
|
||||
|
||||
nsEventStatus status;
|
||||
nsresult rv = widget->DispatchEvent(&selectionEvent, status);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*aResult = selectionEvent.mSucceeded;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::SendContentCommandEvent(const nsAString& aType,
|
||||
nsITransferable * aTransferable)
|
||||
{
|
||||
PRBool hasCap = PR_FALSE;
|
||||
if (NS_FAILED(nsContentUtils::GetSecurityManager()->IsCapabilityEnabled("UniversalXPConnect", &hasCap))
|
||||
|| !hasCap)
|
||||
if (!IsUniversalXPConnectCapable()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
// get the widget to send the event to
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
|
@ -3682,33 +3682,34 @@ nsGlobalWindow::CheckSecurityLeftAndTop(PRInt32* aLeft, PRInt32* aTop)
|
||||
nsContentUtils::HidePopupsInDocument(doc);
|
||||
#endif
|
||||
|
||||
PRInt32 screenLeft, screenTop, screenWidth, screenHeight;
|
||||
PRInt32 winLeft, winTop, winWidth, winHeight;
|
||||
|
||||
nsGlobalWindow* rootWindow =
|
||||
static_cast<nsGlobalWindow*>(GetPrivateRoot());
|
||||
if (rootWindow) {
|
||||
rootWindow->FlushPendingNotifications(Flush_Layout);
|
||||
}
|
||||
|
||||
// Get the window size
|
||||
nsCOMPtr<nsIBaseWindow> treeOwner;
|
||||
GetTreeOwner(getter_AddRefs(treeOwner));
|
||||
if (treeOwner)
|
||||
treeOwner->GetPositionAndSize(&winLeft, &winTop, &winWidth, &winHeight);
|
||||
|
||||
// convert those values to CSS pixels
|
||||
// XXX four separate retrievals of the prescontext
|
||||
winLeft = DevToCSSIntPixels(winLeft);
|
||||
winTop = DevToCSSIntPixels(winTop);
|
||||
winWidth = DevToCSSIntPixels(winWidth);
|
||||
winHeight = DevToCSSIntPixels(winHeight);
|
||||
|
||||
// Get the screen dimensions
|
||||
// XXX This should use nsIScreenManager once it's fully fleshed out.
|
||||
nsCOMPtr<nsIDOMScreen> screen;
|
||||
GetScreen(getter_AddRefs(screen));
|
||||
if (screen) {
|
||||
|
||||
if (treeOwner && screen) {
|
||||
PRInt32 screenLeft, screenTop, screenWidth, screenHeight;
|
||||
PRInt32 winLeft, winTop, winWidth, winHeight;
|
||||
|
||||
// Get the window size
|
||||
treeOwner->GetPositionAndSize(&winLeft, &winTop, &winWidth, &winHeight);
|
||||
|
||||
// convert those values to CSS pixels
|
||||
// XXX four separate retrievals of the prescontext
|
||||
winLeft = DevToCSSIntPixels(winLeft);
|
||||
winTop = DevToCSSIntPixels(winTop);
|
||||
winWidth = DevToCSSIntPixels(winWidth);
|
||||
winHeight = DevToCSSIntPixels(winHeight);
|
||||
|
||||
// Get the screen dimensions
|
||||
// XXX This should use nsIScreenManager once it's fully fleshed out.
|
||||
screen->GetAvailLeft(&screenLeft);
|
||||
screen->GetAvailWidth(&screenWidth);
|
||||
screen->GetAvailHeight(&screenHeight);
|
||||
@ -3724,9 +3725,7 @@ nsGlobalWindow::CheckSecurityLeftAndTop(PRInt32* aLeft, PRInt32* aTop)
|
||||
#else
|
||||
screen->GetAvailTop(&screenTop);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (screen && treeOwner) {
|
||||
if (aLeft) {
|
||||
if (screenLeft+screenWidth < *aLeft+winWidth)
|
||||
*aLeft = screenLeft+screenWidth - winWidth;
|
||||
|
@ -59,6 +59,8 @@
|
||||
#include "nsIContentViewerEdit.h"
|
||||
#include "nsIContentViewer.h"
|
||||
#include "nsFocusManager.h"
|
||||
#include "nsCopySupport.h"
|
||||
#include "nsGUIEvent.h"
|
||||
|
||||
#include "nsIClipboardDragDropHooks.h"
|
||||
#include "nsIClipboardDragDropHookList.h"
|
||||
@ -407,7 +409,71 @@ nsSelectCommand::DoSelectCommand(const char *aCommandName, nsIDOMWindow *aWindow
|
||||
#pragma mark -
|
||||
#endif
|
||||
|
||||
class nsClipboardBaseCommand : public nsIControllerCommand
|
||||
class nsClipboardCommand : public nsIControllerCommand
|
||||
{
|
||||
public:
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICONTROLLERCOMMAND
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsClipboardCommand, nsIControllerCommand)
|
||||
|
||||
nsresult
|
||||
nsClipboardCommand::IsCommandEnabled(const char* aCommandName, nsISupports *aContext, PRBool *outCmdEnabled)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(outCmdEnabled);
|
||||
*outCmdEnabled = PR_FALSE;
|
||||
|
||||
if (strcmp(aCommandName, "cmd_copy"))
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aContext);
|
||||
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = do_QueryInterface(window->GetExtantDocument());
|
||||
*outCmdEnabled = nsCopySupport::CanCopy(doc);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsClipboardCommand::DoCommand(const char *aCommandName, nsISupports *aContext)
|
||||
{
|
||||
if (strcmp(aCommandName, "cmd_copy"))
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aContext);
|
||||
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
|
||||
|
||||
nsIDocShell *docShell = window->GetDocShell();
|
||||
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
|
||||
|
||||
nsCOMPtr<nsIPresShell> presShell;
|
||||
docShell->GetPresShell(getter_AddRefs(presShell));
|
||||
NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
|
||||
|
||||
nsCopySupport::FireClipboardEvent(NS_COPY, presShell, nsnull);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsClipboardCommand::GetCommandStateParams(const char *aCommandName,
|
||||
nsICommandParams *aParams, nsISupports *aCommandContext)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsClipboardCommand::DoCommandParams(const char *aCommandName, nsICommandParams* aParams, nsISupports *aContext)
|
||||
{
|
||||
return DoCommand(aCommandName, aContext);
|
||||
}
|
||||
|
||||
#if 0
|
||||
#pragma mark -
|
||||
#endif
|
||||
|
||||
class nsSelectionCommand : public nsIControllerCommand
|
||||
{
|
||||
public:
|
||||
|
||||
@ -425,19 +491,19 @@ protected:
|
||||
};
|
||||
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsClipboardBaseCommand, nsIControllerCommand)
|
||||
NS_IMPL_ISUPPORTS1(nsSelectionCommand, nsIControllerCommand)
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
|
||||
nsClipboardBaseCommand
|
||||
nsSelectionCommand
|
||||
|
||||
----------------------------------------------------------------------------*/
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsClipboardBaseCommand::IsCommandEnabled(const char * aCommandName,
|
||||
nsISupports *aCommandContext,
|
||||
PRBool *outCmdEnabled)
|
||||
nsSelectionCommand::IsCommandEnabled(const char * aCommandName,
|
||||
nsISupports *aCommandContext,
|
||||
PRBool *outCmdEnabled)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(outCmdEnabled);
|
||||
*outCmdEnabled = PR_FALSE;
|
||||
@ -450,8 +516,8 @@ nsClipboardBaseCommand::IsCommandEnabled(const char * aCommandName,
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsClipboardBaseCommand::DoCommand(const char *aCommandName,
|
||||
nsISupports *aCommandContext)
|
||||
nsSelectionCommand::DoCommand(const char *aCommandName,
|
||||
nsISupports *aCommandContext)
|
||||
{
|
||||
nsCOMPtr<nsIContentViewerEdit> contentEdit;
|
||||
GetContentViewerEditFromContext(aCommandContext, getter_AddRefs(contentEdit));
|
||||
@ -461,17 +527,17 @@ nsClipboardBaseCommand::DoCommand(const char *aCommandName,
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsClipboardBaseCommand::GetCommandStateParams(const char *aCommandName,
|
||||
nsICommandParams *aParams,
|
||||
nsISupports *aCommandContext)
|
||||
nsSelectionCommand::GetCommandStateParams(const char *aCommandName,
|
||||
nsICommandParams *aParams,
|
||||
nsISupports *aCommandContext)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsClipboardBaseCommand::DoCommandParams(const char *aCommandName,
|
||||
nsICommandParams *aParams,
|
||||
nsISupports *aCommandContext)
|
||||
nsSelectionCommand::DoCommandParams(const char *aCommandName,
|
||||
nsICommandParams *aParams,
|
||||
nsISupports *aCommandContext)
|
||||
{
|
||||
nsCOMPtr<nsIContentViewerEdit> contentEdit;
|
||||
GetContentViewerEditFromContext(aCommandContext, getter_AddRefs(contentEdit));
|
||||
@ -481,8 +547,8 @@ nsClipboardBaseCommand::DoCommandParams(const char *aCommandName,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsClipboardBaseCommand::GetContentViewerEditFromContext(nsISupports *aContext,
|
||||
nsIContentViewerEdit **aEditInterface)
|
||||
nsSelectionCommand::GetContentViewerEditFromContext(nsISupports *aContext,
|
||||
nsIContentViewerEdit **aEditInterface)
|
||||
{
|
||||
NS_ENSURE_ARG(aEditInterface);
|
||||
*aEditInterface = nsnull;
|
||||
@ -508,7 +574,7 @@ nsClipboardBaseCommand::GetContentViewerEditFromContext(nsISupports *aContext,
|
||||
#endif
|
||||
|
||||
#define NS_DECL_CLIPBOARD_COMMAND(_cmd) \
|
||||
class _cmd : public nsClipboardBaseCommand \
|
||||
class _cmd : public nsSelectionCommand \
|
||||
{ \
|
||||
protected: \
|
||||
\
|
||||
@ -519,68 +585,11 @@ protected:
|
||||
/* no member variables, please, we're stateless! */ \
|
||||
};
|
||||
|
||||
NS_DECL_CLIPBOARD_COMMAND(nsClipboardCopyCommand)
|
||||
NS_DECL_CLIPBOARD_COMMAND(nsClipboardCutCommand)
|
||||
NS_DECL_CLIPBOARD_COMMAND(nsClipboardPasteCommand)
|
||||
NS_DECL_CLIPBOARD_COMMAND(nsClipboardCopyLinkCommand)
|
||||
NS_DECL_CLIPBOARD_COMMAND(nsClipboardImageCommands)
|
||||
NS_DECL_CLIPBOARD_COMMAND(nsClipboardSelectAllNoneCommands)
|
||||
NS_DECL_CLIPBOARD_COMMAND(nsClipboardGetContentsCommand)
|
||||
|
||||
#if 0
|
||||
#pragma mark -
|
||||
#endif
|
||||
|
||||
|
||||
nsresult
|
||||
nsClipboardCutCommand::IsClipboardCommandEnabled(const char* aCommandName, nsIContentViewerEdit* aEdit, PRBool *outCmdEnabled)
|
||||
{
|
||||
return aEdit->GetCutable(outCmdEnabled);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsClipboardCutCommand::DoClipboardCommand(const char *aCommandName, nsIContentViewerEdit* aEdit, nsICommandParams* aParams)
|
||||
{
|
||||
return aEdit->CutSelection();
|
||||
}
|
||||
|
||||
#if 0
|
||||
#pragma mark -
|
||||
#endif
|
||||
|
||||
nsresult
|
||||
nsClipboardCopyCommand::IsClipboardCommandEnabled(const char* aCommandName, nsIContentViewerEdit* aEdit, PRBool *outCmdEnabled)
|
||||
{
|
||||
return aEdit->GetCopyable(outCmdEnabled);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsClipboardCopyCommand::DoClipboardCommand(const char *aCommandName, nsIContentViewerEdit* aEdit, nsICommandParams* aParams)
|
||||
{
|
||||
return aEdit->CopySelection();
|
||||
}
|
||||
|
||||
#if 0
|
||||
#pragma mark -
|
||||
#endif
|
||||
|
||||
nsresult
|
||||
nsClipboardPasteCommand::IsClipboardCommandEnabled(const char* aCommandName, nsIContentViewerEdit* aEdit, PRBool *outCmdEnabled)
|
||||
{
|
||||
return aEdit->GetPasteable(outCmdEnabled);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsClipboardPasteCommand::DoClipboardCommand(const char *aCommandName, nsIContentViewerEdit* aEdit, nsICommandParams* aParams)
|
||||
{
|
||||
return aEdit->Paste();
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
#pragma mark -
|
||||
#endif
|
||||
|
||||
nsresult
|
||||
nsClipboardCopyLinkCommand::IsClipboardCommandEnabled(const char* aCommandName, nsIContentViewerEdit* aEdit, PRBool *outCmdEnabled)
|
||||
{
|
||||
@ -965,9 +974,9 @@ nsWindowCommandRegistration::RegisterWindowCommands(
|
||||
NS_REGISTER_NEXT_COMMAND(nsSelectCommand, sSelectTopString);
|
||||
NS_REGISTER_LAST_COMMAND(nsSelectCommand, sSelectBottomString);
|
||||
|
||||
NS_REGISTER_ONE_COMMAND(nsClipboardCopyCommand, "cmd_copy");
|
||||
NS_REGISTER_ONE_COMMAND(nsClipboardCutCommand, "cmd_cut");
|
||||
NS_REGISTER_ONE_COMMAND(nsClipboardPasteCommand, "cmd_paste");
|
||||
NS_REGISTER_ONE_COMMAND(nsClipboardCommand, "cmd_cut");
|
||||
NS_REGISTER_ONE_COMMAND(nsClipboardCommand, "cmd_copy");
|
||||
NS_REGISTER_ONE_COMMAND(nsClipboardCommand, "cmd_paste");
|
||||
NS_REGISTER_ONE_COMMAND(nsClipboardCopyLinkCommand, "cmd_copyLink");
|
||||
NS_REGISTER_FIRST_COMMAND(nsClipboardImageCommands, sCopyImageLocationString);
|
||||
NS_REGISTER_NEXT_COMMAND(nsClipboardImageCommands, sCopyImageContentsString);
|
||||
|
184
dom/base/nsQueryContentEventResult.cpp
Normal file
184
dom/base/nsQueryContentEventResult.cpp
Normal file
@ -0,0 +1,184 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** 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 mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Japan.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Masayuki Nakano <masayuki@d-toybox.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of 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 ***** */
|
||||
|
||||
#include "nsQueryContentEventResult.h"
|
||||
#include "nsGUIEvent.h"
|
||||
#include "nsIWidget.h"
|
||||
#include "nsPoint.h"
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsQueryContentEventResult)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIQueryContentEventResult)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIQueryContentEventResult)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_ADDREF(nsQueryContentEventResult)
|
||||
NS_IMPL_RELEASE(nsQueryContentEventResult)
|
||||
|
||||
nsQueryContentEventResult::nsQueryContentEventResult() :
|
||||
mEventID(0), mSucceeded(PR_FALSE)
|
||||
{
|
||||
}
|
||||
|
||||
nsQueryContentEventResult::~nsQueryContentEventResult()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsQueryContentEventResult::GetOffset(PRUint32 *aOffset)
|
||||
{
|
||||
PRBool notFound;
|
||||
nsresult rv = GetNotFound(¬Found);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(!notFound, NS_ERROR_NOT_AVAILABLE);
|
||||
*aOffset = mOffset;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static PRBool IsRectEnabled(PRUint32 aEventID)
|
||||
{
|
||||
return aEventID == NS_QUERY_CARET_RECT ||
|
||||
aEventID == NS_QUERY_TEXT_RECT ||
|
||||
aEventID == NS_QUERY_EDITOR_RECT ||
|
||||
aEventID == NS_QUERY_CHARACTER_AT_POINT;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsQueryContentEventResult::GetReversed(PRBool *aReversed)
|
||||
{
|
||||
NS_ENSURE_TRUE(mSucceeded, NS_ERROR_NOT_AVAILABLE);
|
||||
NS_ENSURE_TRUE(mEventID == NS_QUERY_SELECTED_TEXT,
|
||||
NS_ERROR_NOT_AVAILABLE);
|
||||
*aReversed = mReversed;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsQueryContentEventResult::GetLeft(PRInt32 *aLeft)
|
||||
{
|
||||
NS_ENSURE_TRUE(mSucceeded, NS_ERROR_NOT_AVAILABLE);
|
||||
NS_ENSURE_TRUE(IsRectEnabled(mEventID),
|
||||
NS_ERROR_NOT_AVAILABLE);
|
||||
*aLeft = mRect.x;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsQueryContentEventResult::GetWidth(PRInt32 *aWidth)
|
||||
{
|
||||
NS_ENSURE_TRUE(mSucceeded, NS_ERROR_NOT_AVAILABLE);
|
||||
NS_ENSURE_TRUE(IsRectEnabled(mEventID),
|
||||
NS_ERROR_NOT_AVAILABLE);
|
||||
*aWidth = mRect.width;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsQueryContentEventResult::GetTop(PRInt32 *aTop)
|
||||
{
|
||||
NS_ENSURE_TRUE(mSucceeded, NS_ERROR_NOT_AVAILABLE);
|
||||
NS_ENSURE_TRUE(IsRectEnabled(mEventID),
|
||||
NS_ERROR_NOT_AVAILABLE);
|
||||
*aTop = mRect.y;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsQueryContentEventResult::GetHeight(PRInt32 *aHeight)
|
||||
{
|
||||
NS_ENSURE_TRUE(mSucceeded, NS_ERROR_NOT_AVAILABLE);
|
||||
NS_ENSURE_TRUE(IsRectEnabled(mEventID),
|
||||
NS_ERROR_NOT_AVAILABLE);
|
||||
*aHeight = mRect.height;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsQueryContentEventResult::GetText(nsAString &aText)
|
||||
{
|
||||
NS_ENSURE_TRUE(mSucceeded, NS_ERROR_NOT_AVAILABLE);
|
||||
NS_ENSURE_TRUE(mEventID == NS_QUERY_SELECTED_TEXT ||
|
||||
mEventID == NS_QUERY_TEXT_CONTENT,
|
||||
NS_ERROR_NOT_AVAILABLE);
|
||||
aText = mString;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsQueryContentEventResult::GetSucceeded(PRBool *aSucceeded)
|
||||
{
|
||||
NS_ENSURE_TRUE(mEventID != 0, NS_ERROR_NOT_INITIALIZED);
|
||||
*aSucceeded = mSucceeded;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsQueryContentEventResult::GetNotFound(PRBool *aNotFound)
|
||||
{
|
||||
NS_ENSURE_TRUE(mSucceeded, NS_ERROR_NOT_AVAILABLE);
|
||||
NS_ENSURE_TRUE(mEventID == NS_QUERY_SELECTED_TEXT ||
|
||||
mEventID == NS_QUERY_CHARACTER_AT_POINT,
|
||||
NS_ERROR_NOT_AVAILABLE);
|
||||
*aNotFound = (mOffset == nsQueryContentEvent::NOT_FOUND);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsQueryContentEventResult::SetEventResult(nsIWidget* aWidget,
|
||||
const nsQueryContentEvent &aEvent)
|
||||
{
|
||||
mEventID = aEvent.message;
|
||||
mSucceeded = aEvent.mSucceeded;
|
||||
mReversed = aEvent.mReply.mReversed;
|
||||
mRect = aEvent.mReply.mRect;
|
||||
mOffset = aEvent.mReply.mOffset;
|
||||
mString = aEvent.mReply.mString;
|
||||
|
||||
if (!IsRectEnabled(mEventID) || !aWidget || !mSucceeded) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsIWidget* topWidget = aWidget->GetTopLevelWidget();
|
||||
if (!topWidget || topWidget == aWidget) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert the top widget related coordinates to the given widget's.
|
||||
nsIntPoint offset =
|
||||
aWidget->WidgetToScreenOffset() - topWidget->WidgetToScreenOffset();
|
||||
mRect.MoveBy(-offset);
|
||||
}
|
65
dom/base/nsQueryContentEventResult.h
Normal file
65
dom/base/nsQueryContentEventResult.h
Normal file
@ -0,0 +1,65 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** 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 mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Japan.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Masayuki Nakano <masayuki@d-toybox.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of 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 ***** */
|
||||
|
||||
#include "nsIQueryContentEventResult.h"
|
||||
#include "nsString.h"
|
||||
#include "nsRect.h"
|
||||
|
||||
class nsQueryContentEvent;
|
||||
class nsIWidget;
|
||||
|
||||
class nsQueryContentEventResult : public nsIQueryContentEventResult
|
||||
{
|
||||
public:
|
||||
nsQueryContentEventResult();
|
||||
~nsQueryContentEventResult();
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIQUERYCONTENTEVENTRESULT
|
||||
|
||||
void SetEventResult(nsIWidget* aWidget, const nsQueryContentEvent &aEvent);
|
||||
|
||||
protected:
|
||||
PRUint32 mEventID;
|
||||
|
||||
PRUint32 mOffset;
|
||||
nsString mString;
|
||||
nsIntRect mRect;
|
||||
|
||||
PRPackedBool mSucceeded;
|
||||
PRPackedBool mReversed;
|
||||
};
|
@ -80,6 +80,7 @@ XPIDLSRCS = \
|
||||
nsIDOMClientRect.idl \
|
||||
nsIDOMClientRectList.idl \
|
||||
nsIFocusManager.idl \
|
||||
nsIQueryContentEventResult.idl \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
@ -50,8 +50,9 @@ interface nsIDOMElement;
|
||||
interface nsIDOMHTMLCanvasElement;
|
||||
interface nsIDOMEvent;
|
||||
interface nsITransferable;
|
||||
interface nsIQueryContentEventResult;
|
||||
|
||||
[scriptable, uuid(5ab44028-20ed-499a-bbe4-1805a1f350c8)]
|
||||
[scriptable, uuid(00ca8d4f-61f1-4d9c-a7c1-82651b0cf02b)]
|
||||
interface nsIDOMWindowUtils : nsISupports {
|
||||
|
||||
/**
|
||||
@ -443,4 +444,173 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
*/
|
||||
void sendContentCommandEvent(in AString aType,
|
||||
[optional] in nsITransferable aTransferable);
|
||||
|
||||
/**
|
||||
* Synthesize a composition event to the window.
|
||||
*
|
||||
* Cannot be accessed from unprivileged context (not content-accessible)
|
||||
* Will throw a DOM security error if called without UniversalXPConnect
|
||||
* privileges.
|
||||
*
|
||||
* @param aType The event type: "compositionstart" or "compositionend".
|
||||
*/
|
||||
void sendCompositionEvent(in AString aType);
|
||||
|
||||
/**
|
||||
* Synthesize a text event to the window.
|
||||
*
|
||||
* Cannot be accessed from unprivileged context (not content-accessible)
|
||||
* Will throw a DOM security error if called without UniversalXPConnect
|
||||
* privileges.
|
||||
*
|
||||
* Currently, this method doesn't support 4 or more clauses composition
|
||||
* string.
|
||||
*
|
||||
* @param aCompositionString composition string
|
||||
* @param a*ClauseLengh the length of nth clause, set 0 when you
|
||||
* don't need second or third clause.
|
||||
* @param a*ClauseAttr the attribute of nth clause, uese following
|
||||
* const values.
|
||||
* @param aCaretStart the caret position in the composition string,
|
||||
* if you set negative value, this method don't
|
||||
* set the caret position to the event.
|
||||
* @param aCaretLength the caret length, if this is one or more,
|
||||
* the caret will be wide caret, otherwise,
|
||||
* it's collapsed.
|
||||
* XXX nsEditor doesn't support wide caret yet.
|
||||
*/
|
||||
|
||||
// NOTE: These values must be same to NS_TEXTRANGE_* in nsGUIEvent.h
|
||||
|
||||
const unsigned long COMPOSITION_ATTR_RAWINPUT = 0x02;
|
||||
const unsigned long COMPOSITION_ATTR_SELECTEDRAWTEXT = 0x03;
|
||||
const unsigned long COMPOSITION_ATTR_CONVERTEDTEXT = 0x04;
|
||||
const unsigned long COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT = 0x05;
|
||||
|
||||
void sendTextEvent(in AString aCompositionString,
|
||||
in long aFirstClauseLength,
|
||||
in unsigned long aFirstClauseAttr,
|
||||
in long aSecondClauseLength,
|
||||
in unsigned long aSecondClauseAttr,
|
||||
in long aThirdClauseLength,
|
||||
in unsigned long aThirdClauseAttr,
|
||||
in long aCaretStart,
|
||||
in long aCaretLength);
|
||||
|
||||
/**
|
||||
* Synthesize a query content event.
|
||||
*
|
||||
* @param aType On of the following const values. And see also each comment
|
||||
* for the other parameters and the result.
|
||||
*/
|
||||
nsIQueryContentEventResult sendQueryContentEvent(in unsigned long aType,
|
||||
in unsigned long aOffset,
|
||||
in unsigned long aLength,
|
||||
in long aX,
|
||||
in long aY);
|
||||
|
||||
// NOTE: following values are same as NS_QUERY_* in nsGUIEvent.h
|
||||
|
||||
/**
|
||||
* QUERY_SELECTED_TEXT queries the first selection range's information.
|
||||
*
|
||||
* @param aOffset Not used.
|
||||
* @param aLength Not used.
|
||||
* @param aX Not used.
|
||||
* @param aY Not used.
|
||||
*
|
||||
* @return offset, reversed and text properties of the result are available.
|
||||
*/
|
||||
const unsigned long QUERY_SELECTED_TEXT = 3200;
|
||||
|
||||
/**
|
||||
* QUERY_TEXT_CONTENT queries the text at the specified range.
|
||||
*
|
||||
* @param aOffset The first character's offset. 0 is the first character.
|
||||
* @param aLength The length of getting text. If the aLength is too long,
|
||||
* the result text is shorter than this value.
|
||||
* @param aX Not used.
|
||||
* @param aY Not used.
|
||||
*
|
||||
* @return text property of the result is available.
|
||||
*/
|
||||
const unsigned long QUERY_TEXT_CONTENT = 3201;
|
||||
|
||||
/**
|
||||
* QUERY_CARET_RECT queries the (collapsed) caret rect of the offset.
|
||||
* If the actual caret is there at the specified offset, this returns the
|
||||
* actual caret rect. Otherwise, this guesses the caret rect from the
|
||||
* metrics of the text.
|
||||
*
|
||||
* @param aOffset The caret offset. 0 is the left side of the first
|
||||
* caracter in LTR text.
|
||||
* @param aLength Not used.
|
||||
* @param aX Not used.
|
||||
* @param aY Not used.
|
||||
*
|
||||
* @return left, top, width and height properties of the result are available.
|
||||
* The left and the top properties are offset in the client area of
|
||||
* the DOM window.
|
||||
*/
|
||||
const unsigned long QUERY_CARET_RECT = 3203;
|
||||
|
||||
/**
|
||||
* QUERY_TEXT_RECT queries the specified text's rect.
|
||||
*
|
||||
* @param aOffset The first character's offset. 0 is the first character.
|
||||
* @param aLength The length of getting text. If the aLength is too long,
|
||||
* the extra length is ignored.
|
||||
* @param aX Not used.
|
||||
* @param aY Not used.
|
||||
*
|
||||
* @return left, top, width and height properties of the result are available.
|
||||
* The left and the top properties are offset in the client area of
|
||||
* the DOM window.
|
||||
*/
|
||||
const unsigned long QUERY_TEXT_RECT = 3204;
|
||||
|
||||
/**
|
||||
* QUERY_TEXT_RECT queries the focused editor's rect.
|
||||
*
|
||||
* @param aOffset Not used.
|
||||
* @param aLength Not used.
|
||||
* @param aX Not used.
|
||||
* @param aY Not used.
|
||||
*
|
||||
* @return left, top, width and height properties of the result are available.
|
||||
*/
|
||||
const unsigned long QUERY_EDITOR_RECT = 3205;
|
||||
|
||||
/**
|
||||
* QUERY_CHARACTER_AT_POINT queries the character information at the
|
||||
* specified point. The point is offset in the window.
|
||||
* NOTE: If there are some panels at the point, this method send the query
|
||||
* event to the panel's widget automatically.
|
||||
*
|
||||
* @param aOffset Not used.
|
||||
* @param aLength Not used.
|
||||
* @param aX X offset in the widget.
|
||||
* @param aY Y offset in the widget.
|
||||
*
|
||||
* @return offset, notFound, left, top, width and height properties of the
|
||||
* result are available.
|
||||
*/
|
||||
const unsigned long QUERY_CHARACTER_AT_POINT = 3208;
|
||||
|
||||
/**
|
||||
* Synthesize a selection set event to the window.
|
||||
*
|
||||
* This sets the selection as the specified information.
|
||||
*
|
||||
* @param aOffset The caret offset of the selection start.
|
||||
* @param aLength The length of the selection. If this is too long, the
|
||||
* extra length is ignored.
|
||||
* @param aReverse If true, the selection set from |aOffset + aLength| to
|
||||
* |aOffset|. Otherwise, set from |aOffset| to
|
||||
* |aOffset + aLength|.
|
||||
* @return True, if succeeded. Otherwise, false.
|
||||
*/
|
||||
boolean sendSelectionSetEvent(in unsigned long aOffset,
|
||||
in unsigned long aLength,
|
||||
in boolean aReverse);
|
||||
};
|
||||
|
61
dom/interfaces/base/nsIQueryContentEventResult.idl
Normal file
61
dom/interfaces/base/nsIQueryContentEventResult.idl
Normal file
@ -0,0 +1,61 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** 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 mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Japan.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Masayuki Nakano <masayuki@d-toybox.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of 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 ***** */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
/**
|
||||
* The result of query content events. succeeded propery can be used always.
|
||||
* Whether other properties can be used or not depends on the event.
|
||||
* See nsIDOMWindowUtils.idl, which properites can be used was documented.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(4b4ba266-b51e-4f0f-8d0e-9f13cb2a0056)]
|
||||
interface nsIQueryContentEventResult : nsISupports
|
||||
{
|
||||
readonly attribute unsigned long offset;
|
||||
readonly attribute boolean reversed;
|
||||
|
||||
readonly attribute long left;
|
||||
readonly attribute long top;
|
||||
readonly attribute long width;
|
||||
readonly attribute long height;
|
||||
readonly attribute AString text;
|
||||
|
||||
readonly attribute boolean succeeded;
|
||||
readonly attribute boolean notFound;
|
||||
};
|
@ -40,7 +40,7 @@
|
||||
|
||||
#include "nsIDOMSVGCSS2Properties.idl"
|
||||
|
||||
[scriptable, uuid(be39bc6d-3b24-4421-9599-8fdf3ad33baf)]
|
||||
[scriptable, uuid(649B0B41-C5F7-4EA2-B6DF-CFFF6B6DD30A)]
|
||||
interface nsIDOMNSCSS2Properties : nsIDOMSVGCSS2Properties
|
||||
{
|
||||
/* Non-DOM 2 extensions */
|
||||
@ -281,4 +281,7 @@ interface nsIDOMNSCSS2Properties : nsIDOMSVGCSS2Properties
|
||||
|
||||
attribute DOMString MozTabSize;
|
||||
// raises(DOMException) on setting
|
||||
|
||||
attribute DOMString MozResize;
|
||||
// raises(DOMException) on setting
|
||||
};
|
||||
|
@ -55,6 +55,7 @@ _TEST_FILES = \
|
||||
test_offsets.js \
|
||||
test_offsets.xul \
|
||||
test_windowProperties.html \
|
||||
test_clipboard_events.html \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_TEST_FILES)
|
||||
|
322
dom/tests/mochitest/general/test_clipboard_events.html
Normal file
322
dom/tests/mochitest/general/test_clipboard_events.html
Normal file
@ -0,0 +1,322 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Clipboard Events</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="border: 3px solid black; padding: 3em;">CONTENT TEXT<input id="content-input" value="INPUT TEXT"></div>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
|
||||
// Enable full privledges for clipboard read/write operations.
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
var content = document.getElementById("content");
|
||||
var contentInput = document.getElementById("content-input");
|
||||
var clipboardInitialValue = "empty";
|
||||
|
||||
// Test that clearing and reading the clipboard works. A random number
|
||||
// is used to make sure that leftover clipboard values from a previous
|
||||
// test run don't cause a false-positive test.
|
||||
var cb_text = "empty_" + Math.random();
|
||||
setClipboardText(cb_text);
|
||||
is(getClipboardText(), cb_text, "set/get clipboard text failed");
|
||||
|
||||
// Some test functions need to be run with delays.
|
||||
var delayedTests = [];
|
||||
|
||||
// Ensure window focus before running tests, otherwise key events can
|
||||
// misfire. We set the onfocus event handler here to actually begin
|
||||
// running tests, and call window.focus() afterwards.
|
||||
window.onfocus = function()
|
||||
{
|
||||
window.onfocus = null;
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
// A list of test functions to run. Before each test function is run, the
|
||||
// clipboard is initialized to clipboardInitialValue, and the contents of
|
||||
// div#content are set as the window's selection.
|
||||
var testFunctions = [
|
||||
test_dom_oncopy,
|
||||
test_dom_oncut,
|
||||
test_dom_onpaste,
|
||||
test_dom_oncopy_abort,
|
||||
test_input_oncopy,
|
||||
test_input_oncut,
|
||||
test_input_onpaste,
|
||||
test_input_oncopy_abort,
|
||||
test_input_oncut_abort,
|
||||
test_input_onpaste_abort,
|
||||
];
|
||||
|
||||
// Run the main tests. This will also populate the delayedTests array
|
||||
for (let i = 0; i < testFunctions.length; i++) {
|
||||
// Init clipboard
|
||||
setClipboardText(clipboardInitialValue);
|
||||
|
||||
// Reset value of contentInput.
|
||||
contentInput.value = "INPUT TEXT";
|
||||
|
||||
var func = testFunctions[i];
|
||||
func();
|
||||
}
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
// Calling .focus begins the test run.
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
window.focus();
|
||||
|
||||
function getClipboardText() {
|
||||
var trans = Components.classes["@mozilla.org/widget/transferable;1"]
|
||||
.createInstance();
|
||||
trans = trans.QueryInterface(Components.interfaces.nsITransferable);
|
||||
trans.addDataFlavor("text/unicode");
|
||||
|
||||
var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"]
|
||||
.getService();
|
||||
clipboard = clipboard.QueryInterface(Components.interfaces.nsIClipboard);
|
||||
clipboard.getData(trans, clipboard.kGlobalClipboard);
|
||||
|
||||
var str = new Object();
|
||||
var strLen = new Object();
|
||||
|
||||
try {
|
||||
trans.getTransferData("text/unicode", str, strLen);
|
||||
} catch(e) {
|
||||
// NS_ERROR_FAILURE will occur if the transferable object has no
|
||||
// text/unicode data in it. In that case, it's not an error:
|
||||
if (e instanceof Components.interfaces.nsIXPCException &&
|
||||
e.result == Components.results.NS_ERROR_FAILURE) {
|
||||
return null;
|
||||
} else {
|
||||
// if we don't know how to handle it then rethrow
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if (!str) return null;
|
||||
|
||||
str = str.value.QueryInterface(Components.interfaces.nsISupportsString);
|
||||
if (!str) return null;
|
||||
|
||||
str = str.data.substring(0, strLen.value / 2);
|
||||
if (!str) return null;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
function setClipboardText(text) {
|
||||
var helper = Components.classes["@mozilla.org/widget/clipboardhelper;1"]
|
||||
.getService(Components.interfaces.nsIClipboardHelper);
|
||||
helper.copyString(text);
|
||||
}
|
||||
|
||||
function selectContentDiv() {
|
||||
// Set selection
|
||||
var selection = window.getSelection();
|
||||
selection.removeAllRanges();
|
||||
selection.selectAllChildren(content);
|
||||
}
|
||||
|
||||
function selectContentInput() {
|
||||
contentInput.select();
|
||||
contentInput.focus();
|
||||
}
|
||||
|
||||
function test_dom_oncopy() {
|
||||
// Setup an oncopy event handler, fire copy. Ensure that the event
|
||||
// handler was called, and the clipboard contents have set to CONTENT TEXT.
|
||||
// Test firing oncopy event on ctrl-c:
|
||||
selectContentDiv();
|
||||
var oncopy_fired = false;
|
||||
content.oncopy = function() { oncopy_fired = true; };
|
||||
try {
|
||||
synthesizeKey("c", {accelKey: 1});
|
||||
ok(oncopy_fired, "copy event firing on DOM element");
|
||||
is(getClipboardText(), "CONTENT TEXT",
|
||||
"copy on DOM element set clipboard correctly");
|
||||
} finally {
|
||||
content.oncopy = null;
|
||||
}
|
||||
}
|
||||
|
||||
function test_dom_oncut() {
|
||||
// Setup an oncut event handler, fire cut. Ensure that the event handler
|
||||
// was called. The <div> doesn't handle a cut, so ensure that the
|
||||
// clipboard text is clipboardInitialValue, NOT "CONTENT TEXT".
|
||||
selectContentDiv();
|
||||
var oncut_fired = false;
|
||||
content.oncut = function() { oncut_fired = true; };
|
||||
try {
|
||||
synthesizeKey("x", {accelKey: 1});
|
||||
ok(!oncut_fired, "cut event firing on DOM element")
|
||||
is(getClipboardText(), clipboardInitialValue,
|
||||
"cut on DOM element did not modify clipboard");
|
||||
} finally {
|
||||
content.oncut = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function test_dom_onpaste() {
|
||||
// Setup an onpaste event handler, fire paste. Ensure that the event
|
||||
// handler was called.
|
||||
selectContentDiv();
|
||||
var onpaste_fired = false;
|
||||
content.onpaste = function() { onpaste_fired = true; };
|
||||
try {
|
||||
synthesizeKey("v", {accelKey: 1});
|
||||
ok(!onpaste_fired, "paste event firing on DOM element");
|
||||
} finally {
|
||||
content.onpaste = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function test_dom_oncopy_abort() {
|
||||
// Setup an oncopy event handler that aborts the copy, and fire the copy
|
||||
// event. Ensure that the event handler was fired, and the clipboard
|
||||
// contents have not been modified.
|
||||
selectContentDiv();
|
||||
var oncopy_fired = false;
|
||||
content.oncopy = function() { oncopy_fired = true; return false; };
|
||||
try {
|
||||
synthesizeKey("c", {accelKey: 1});
|
||||
ok(oncopy_fired, "copy event (to-be-cancelled) firing on DOM element");
|
||||
is(getClipboardText(), clipboardInitialValue,
|
||||
"aborted copy on DOM element did not modify clipboard");
|
||||
} finally {
|
||||
content.oncopy = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function test_input_oncopy() {
|
||||
// Setup an oncopy event handler, fire copy. Ensure that the event
|
||||
// handler was called, and the clipboard contents have set to INPUT TEXT.
|
||||
// Test firing oncopy event on ctrl-c:
|
||||
selectContentInput();
|
||||
var oncopy_fired = false;
|
||||
contentInput.oncopy = function() { oncopy_fired = true; };
|
||||
try {
|
||||
synthesizeKey("c", {accelKey: 1});
|
||||
ok(oncopy_fired, "copy event firing on plaintext editor");
|
||||
is(getClipboardText(), "INPUT TEXT",
|
||||
"copy on plaintext editor set clipboard correctly");
|
||||
} finally {
|
||||
contentInput.oncopy = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function test_input_oncut() {
|
||||
// Setup an oncut event handler, and fire cut. Ensure that the event
|
||||
// handler was fired, the clipboard contains the INPUT TEXT, and
|
||||
// that the input itself is empty.
|
||||
selectContentInput();
|
||||
var oncut_fired = false;
|
||||
contentInput.oncut = function() { oncut_fired = true; };
|
||||
try {
|
||||
synthesizeKey("x", {accelKey: 1});
|
||||
ok(oncut_fired, "cut event firing on plaintext editor");
|
||||
is(getClipboardText(), "INPUT TEXT",
|
||||
"cut on plaintext editor set clipboard correctly");
|
||||
is(contentInput.value, "",
|
||||
"cut on plaintext editor emptied editor");
|
||||
} finally {
|
||||
contentInput.oncut = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function test_input_onpaste() {
|
||||
// Setup an onpaste event handler, and fire paste. Ensure that the event
|
||||
// handler was fired, the clipboard contents didn't change, and that the
|
||||
// input value did change (ie. paste succeeded).
|
||||
selectContentInput();
|
||||
var onpaste_fired = false;
|
||||
contentInput.onpaste = function() { onpaste_fired = true; };
|
||||
try {
|
||||
synthesizeKey("v", {accelKey: 1});
|
||||
ok(onpaste_fired, "paste event firing on plaintext editor");
|
||||
is(getClipboardText(), clipboardInitialValue,
|
||||
"paste on plaintext editor did not modify clipboard contents");
|
||||
is(contentInput.value, clipboardInitialValue,
|
||||
"paste on plaintext editor did modify editor value");
|
||||
} finally {
|
||||
contentInput.onpaste = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function test_input_oncopy_abort() {
|
||||
// Setup an oncopy event handler, fire copy. Ensure that the event
|
||||
// handler was called, and that the clipboard value did NOT change.
|
||||
selectContentInput();
|
||||
var oncopy_fired = false;
|
||||
contentInput.oncopy = function() { oncopy_fired = true; return false; };
|
||||
try {
|
||||
synthesizeKey("c", {accelKey: 1});
|
||||
ok(oncopy_fired, "copy event (to-be-cancelled) firing on plaintext editor");
|
||||
is(getClipboardText(), clipboardInitialValue,
|
||||
"aborted copy on plaintext editor did not modify clipboard");
|
||||
} finally {
|
||||
contentInput.oncopy = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function test_input_oncut_abort() {
|
||||
// Setup an oncut event handler, and fire cut. Ensure that the event
|
||||
// handler was fired, the clipboard contains the INPUT TEXT, and
|
||||
// that the input itself is empty.
|
||||
selectContentInput();
|
||||
var oncut_fired = false;
|
||||
contentInput.oncut = function() { oncut_fired = true; return false; };
|
||||
try {
|
||||
synthesizeKey("x", {accelKey: 1});
|
||||
ok(oncut_fired, "cut event (to-be-cancelled) firing on plaintext editor");
|
||||
is(getClipboardText(), clipboardInitialValue,
|
||||
"aborted cut on plaintext editor did not modify clipboard.");
|
||||
is(contentInput.value, "INPUT TEXT",
|
||||
"aborted cut on plaintext editor did not modify editor contents");
|
||||
} finally {
|
||||
contentInput.oncut = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function test_input_onpaste_abort() {
|
||||
// Setup an onpaste event handler, and fire paste. Ensure that the event
|
||||
// handler was fired, the clipboard contents didn't change, and that the
|
||||
// input value did change (ie. paste succeeded).
|
||||
selectContentInput();
|
||||
var onpaste_fired = false;
|
||||
contentInput.onpaste = function() { onpaste_fired = true; return false; };
|
||||
try {
|
||||
synthesizeKey("v", {accelKey: 1});
|
||||
ok(onpaste_fired,
|
||||
"paste event (to-be-cancelled) firing on plaintext editor");
|
||||
is(getClipboardText(), clipboardInitialValue,
|
||||
"aborted paste on plaintext editor did not modify clipboard");
|
||||
is(contentInput.value, "INPUT TEXT",
|
||||
"aborted paste on plaintext editor did not modify modified editor value");
|
||||
} finally {
|
||||
contentInput.onpaste = null;
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1847,14 +1847,11 @@ PRBool nsHTMLEditor::HavePrivateHTMLFlavor(nsIClipboard *aClipboard)
|
||||
|
||||
NS_IMETHODIMP nsHTMLEditor::Paste(PRInt32 aSelectionType)
|
||||
{
|
||||
ForceCompositionEnd();
|
||||
|
||||
PRBool preventDefault;
|
||||
nsresult rv = FireClipboardEvent(NS_PASTE, &preventDefault);
|
||||
if (NS_FAILED(rv) || preventDefault)
|
||||
return rv;
|
||||
if (!FireClipboardEvent(NS_PASTE))
|
||||
return NS_OK;
|
||||
|
||||
// Get Clipboard Service
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
@ -1932,12 +1929,8 @@ NS_IMETHODIMP nsHTMLEditor::Paste(PRInt32 aSelectionType)
|
||||
|
||||
NS_IMETHODIMP nsHTMLEditor::PasteTransferable(nsITransferable *aTransferable)
|
||||
{
|
||||
ForceCompositionEnd();
|
||||
|
||||
PRBool preventDefault;
|
||||
nsresult rv = FireClipboardEvent(NS_PASTE, &preventDefault);
|
||||
if (NS_FAILED(rv) || preventDefault)
|
||||
return rv;
|
||||
if (!FireClipboardEvent(NS_PASTE))
|
||||
return NS_OK;
|
||||
|
||||
// handle transferable hooks
|
||||
nsCOMPtr<nsIDOMDocument> domdoc;
|
||||
@ -1948,10 +1941,8 @@ NS_IMETHODIMP nsHTMLEditor::PasteTransferable(nsITransferable *aTransferable)
|
||||
// Beware! This may flush notifications via synchronous
|
||||
// ScrollSelectionIntoView.
|
||||
nsAutoString contextStr, infoStr;
|
||||
rv = InsertFromTransferable(aTransferable, nsnull, contextStr, infoStr,
|
||||
nsnull, 0, PR_TRUE);
|
||||
|
||||
return rv;
|
||||
return InsertFromTransferable(aTransferable, nsnull, contextStr, infoStr,
|
||||
nsnull, 0, PR_TRUE);
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -60,6 +60,7 @@
|
||||
#include "nsITransferable.h"
|
||||
#include "nsIDragService.h"
|
||||
#include "nsIDOMNSUIEvent.h"
|
||||
#include "nsCopySupport.h"
|
||||
|
||||
// Misc
|
||||
#include "nsEditorUtils.h"
|
||||
@ -409,14 +410,11 @@ NS_IMETHODIMP nsPlaintextEditor::DoDrag(nsIDOMEvent *aDragEvent)
|
||||
|
||||
NS_IMETHODIMP nsPlaintextEditor::Paste(PRInt32 aSelectionType)
|
||||
{
|
||||
ForceCompositionEnd();
|
||||
|
||||
PRBool preventDefault;
|
||||
nsresult rv = FireClipboardEvent(NS_PASTE, &preventDefault);
|
||||
if (NS_FAILED(rv) || preventDefault)
|
||||
return rv;
|
||||
if (!FireClipboardEvent(NS_PASTE))
|
||||
return NS_OK;
|
||||
|
||||
// Get Clipboard Service
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
|
||||
if ( NS_FAILED(rv) )
|
||||
return rv;
|
||||
@ -446,12 +444,8 @@ NS_IMETHODIMP nsPlaintextEditor::Paste(PRInt32 aSelectionType)
|
||||
|
||||
NS_IMETHODIMP nsPlaintextEditor::PasteTransferable(nsITransferable *aTransferable)
|
||||
{
|
||||
ForceCompositionEnd();
|
||||
|
||||
PRBool preventDefault;
|
||||
nsresult rv = FireClipboardEvent(NS_PASTE, &preventDefault);
|
||||
if (NS_FAILED(rv) || preventDefault)
|
||||
return rv;
|
||||
if (!FireClipboardEvent(NS_PASTE))
|
||||
return NS_OK;
|
||||
|
||||
if (!IsModifiable())
|
||||
return NS_OK;
|
||||
@ -464,9 +458,7 @@ NS_IMETHODIMP nsPlaintextEditor::PasteTransferable(nsITransferable *aTransferabl
|
||||
|
||||
// Beware! This may flush notifications via synchronous
|
||||
// ScrollSelectionIntoView.
|
||||
rv = InsertTextFromTransferable(aTransferable, nsnull, nsnull, PR_TRUE);
|
||||
|
||||
return rv;
|
||||
return InsertTextFromTransferable(aTransferable, nsnull, nsnull, PR_TRUE);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsPlaintextEditor::CanPaste(PRInt32 aSelectionType, PRBool *aCanPaste)
|
||||
|
@ -1142,128 +1142,66 @@ nsPlaintextEditor::Redo(PRUint32 aCount)
|
||||
return result;
|
||||
}
|
||||
|
||||
nsresult nsPlaintextEditor::GetClipboardEventTarget(nsIDOMNode** aEventTarget)
|
||||
PRBool
|
||||
nsPlaintextEditor::CanCutOrCopy()
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aEventTarget);
|
||||
*aEventTarget = nsnull;
|
||||
|
||||
nsCOMPtr<nsISelection> selection;
|
||||
nsresult res = GetSelection(getter_AddRefs(selection));
|
||||
if (NS_FAILED(res))
|
||||
return res;
|
||||
if (NS_FAILED(GetSelection(getter_AddRefs(selection))))
|
||||
return PR_FALSE;
|
||||
|
||||
return nsCopySupport::GetClipboardEventTarget(selection, aEventTarget);
|
||||
PRBool isCollapsed;
|
||||
selection->GetIsCollapsed(&isCollapsed);
|
||||
return !isCollapsed;
|
||||
}
|
||||
|
||||
nsresult nsPlaintextEditor::FireClipboardEvent(PRUint32 msg,
|
||||
PRBool* aPreventDefault)
|
||||
PRBool
|
||||
nsPlaintextEditor::FireClipboardEvent(PRInt32 aType)
|
||||
{
|
||||
*aPreventDefault = PR_FALSE;
|
||||
if (aType == NS_PASTE)
|
||||
ForceCompositionEnd();
|
||||
|
||||
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
|
||||
if (!ps)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShellWeak);
|
||||
NS_ENSURE_TRUE(presShell, PR_FALSE);
|
||||
|
||||
// Unsafe to fire event during reflow (bug 396108)
|
||||
PRBool isReflowing = PR_TRUE;
|
||||
nsresult rv = ps->IsReflowLocked(&isReflowing);
|
||||
if (NS_FAILED(rv) || isReflowing)
|
||||
return NS_OK;
|
||||
nsCOMPtr<nsISelection> selection;
|
||||
if (NS_FAILED(GetSelection(getter_AddRefs(selection))))
|
||||
return PR_FALSE;
|
||||
|
||||
nsCOMPtr<nsIDOMNode> eventTarget;
|
||||
rv = GetClipboardEventTarget(getter_AddRefs(eventTarget));
|
||||
if (NS_FAILED(rv))
|
||||
// On failure to get event target, just forget about it and don't fire.
|
||||
return NS_OK;
|
||||
if (!nsCopySupport::FireClipboardEvent(aType, presShell, selection))
|
||||
return PR_FALSE;
|
||||
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
nsEvent evt(PR_TRUE, msg);
|
||||
nsEventDispatcher::Dispatch(eventTarget, ps->GetPresContext(), &evt,
|
||||
nsnull, &status);
|
||||
// if event handler return'd false (PreventDefault)
|
||||
if (status == nsEventStatus_eConsumeNoDefault)
|
||||
*aPreventDefault = PR_TRUE;
|
||||
|
||||
// Did the event handler cause the editor to be destroyed? (ie. the input
|
||||
// element was removed from the document) Don't proceed with command,
|
||||
// could crash, definitely does during paste.
|
||||
if (mDidPreDestroy)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
return NS_OK;
|
||||
// If the event handler caused the editor to be destroyed, return false.
|
||||
// Otherwise return true to indicate that the event was not cancelled.
|
||||
return !mDidPreDestroy;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsPlaintextEditor::Cut()
|
||||
{
|
||||
PRBool preventDefault;
|
||||
nsresult rv = FireClipboardEvent(NS_CUT, &preventDefault);
|
||||
if (NS_FAILED(rv) || preventDefault)
|
||||
return rv;
|
||||
|
||||
nsCOMPtr<nsISelection> selection;
|
||||
rv = GetSelection(getter_AddRefs(selection));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
PRBool isCollapsed;
|
||||
if (NS_SUCCEEDED(selection->GetIsCollapsed(&isCollapsed)) && isCollapsed)
|
||||
return NS_OK; // just return ok so no JS error is thrown
|
||||
|
||||
// ps should be guaranteed by FireClipboardEvent not failing
|
||||
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
|
||||
rv = ps->DoCopy();
|
||||
if (NS_SUCCEEDED(rv))
|
||||
rv = DeleteSelection(eNone);
|
||||
return rv;
|
||||
if (FireClipboardEvent(NS_CUT))
|
||||
return DeleteSelection(eNone);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsPlaintextEditor::CanCut(PRBool *aCanCut)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aCanCut);
|
||||
*aCanCut = PR_FALSE;
|
||||
|
||||
nsCOMPtr<nsISelection> selection;
|
||||
nsresult rv = GetSelection(getter_AddRefs(selection));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
PRBool isCollapsed;
|
||||
rv = selection->GetIsCollapsed(&isCollapsed);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
*aCanCut = !isCollapsed && IsModifiable();
|
||||
*aCanCut = IsModifiable() && CanCutOrCopy();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsPlaintextEditor::Copy()
|
||||
{
|
||||
PRBool preventDefault;
|
||||
nsresult rv = FireClipboardEvent(NS_COPY, &preventDefault);
|
||||
if (NS_FAILED(rv) || preventDefault)
|
||||
return rv;
|
||||
|
||||
// ps should be guaranteed by FireClipboardEvent not failing
|
||||
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
|
||||
return ps->DoCopy();
|
||||
FireClipboardEvent(NS_COPY);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsPlaintextEditor::CanCopy(PRBool *aCanCopy)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aCanCopy);
|
||||
*aCanCopy = PR_FALSE;
|
||||
|
||||
nsCOMPtr<nsISelection> selection;
|
||||
nsresult rv = GetSelection(getter_AddRefs(selection));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
PRBool isCollapsed;
|
||||
rv = selection->GetIsCollapsed(&isCollapsed);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
*aCanCopy = !isCollapsed;
|
||||
*aCanCopy = CanCutOrCopy();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
// Shared between OutputToString and OutputToStream
|
||||
NS_IMETHODIMP
|
||||
nsPlaintextEditor::GetAndInitDocEncoder(const nsAString& aFormatType,
|
||||
|
@ -219,10 +219,8 @@ protected:
|
||||
PRBool mIgnoreSpuriousDragEvent;
|
||||
NS_IMETHOD IgnoreSpuriousDragEvent(PRBool aIgnoreSpuriousDragEvent) {mIgnoreSpuriousDragEvent = aIgnoreSpuriousDragEvent; return NS_OK;}
|
||||
|
||||
// Wrapper for nsCopySupport::GetClipboardEventTarget, finds target to fire
|
||||
// [cut,copy,paste] and [beforecut,beforecopy,beforepaste] events at.
|
||||
nsresult GetClipboardEventTarget(nsIDOMNode** aEventTarget);
|
||||
nsresult FireClipboardEvent(PRUint32 msg, PRBool* aPreventDefault);
|
||||
PRBool CanCutOrCopy();
|
||||
PRBool FireClipboardEvent(PRInt32 aType);
|
||||
|
||||
// Data members
|
||||
protected:
|
||||
|
@ -367,22 +367,6 @@ RPCChannel::OnMaybeDequeueOne()
|
||||
AssertWorkerThread();
|
||||
mMutex.AssertNotCurrentThreadOwns();
|
||||
|
||||
if (IsOnCxxStack())
|
||||
// We're running in a nested event loop, and there's
|
||||
// RPCChannel code below us on the stack. We don't want to
|
||||
// dispatch this new message here because the assumptions made
|
||||
// by the code below us on the stack have changed. Just
|
||||
// bailing here isn't enough, however, because we also have to
|
||||
// ensure that the messages received in this nested loop
|
||||
// aren't "lost". We might be running in this nested context
|
||||
// above a non-interruptable handler; these are async
|
||||
// in/out-msg, sync in/out-msg, and rpc in-call. So, we still
|
||||
// bail here, but ensure that at each exit point, we fix up
|
||||
// the IO thread's invariant. Luckily, since we already track
|
||||
// the C++ stack, we know when these exit points are hit:
|
||||
// ExitedCxxStack().
|
||||
return;
|
||||
|
||||
Message recvd;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
@ -402,7 +386,13 @@ RPCChannel::OnMaybeDequeueOne()
|
||||
mPending.pop();
|
||||
}
|
||||
|
||||
RPC_ASSERT(!IsOnCxxStack(), "RPCChannel code not on C++ stack");
|
||||
if (IsOnCxxStack() && recvd.is_rpc() && recvd.is_reply()) {
|
||||
// We probably just received a reply in a nested loop for an
|
||||
// RPC call sent before entering that loop.
|
||||
mOutOfTurnReplies[recvd.seqno()] = recvd;
|
||||
return;
|
||||
}
|
||||
|
||||
CxxStackFrame f(*this, IN_MESSAGE, &recvd);
|
||||
|
||||
if (recvd.is_rpc())
|
||||
|
@ -54,6 +54,7 @@
|
||||
|
||||
#define MOZ_IPDL_TESTFAIL_LABEL "TEST-UNEXPECTED-FAIL"
|
||||
#define MOZ_IPDL_TESTPASS_LABEL "TEST-PASS"
|
||||
#define MOZ_IPDL_TESTINFO_LABEL "TEST-INFO"
|
||||
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -126,12 +126,14 @@ IPDLUnitTestMain(void* aData)
|
||||
IPDLUnitTestType test = IPDLUnitTestFromString(testString);
|
||||
if (!test) {
|
||||
// use this instead of |fail()| because we don't know what the test is
|
||||
fprintf(stderr, MOZ_IPDL_TESTFAIL_LABEL "| %s | unknown unit test %s\\n",
|
||||
fprintf(stderr, MOZ_IPDL_TESTFAIL_LABEL "| %s | unknown unit test %s\n",
|
||||
"<--->", testString);
|
||||
NS_RUNTIMEABORT("can't continue");
|
||||
}
|
||||
gIPDLUnitTestName = testString;
|
||||
|
||||
printf(MOZ_IPDL_TESTINFO_LABEL "| running test | %s\n", gIPDLUnitTestName);
|
||||
|
||||
std::vector<std::string> testCaseArgs;
|
||||
testCaseArgs.push_back(testString);
|
||||
|
||||
|
@ -4745,6 +4745,11 @@ nsCSSFrameConstructor::FindSVGData(nsIContent* aContent,
|
||||
return &sSuppressData;
|
||||
}
|
||||
|
||||
// We don't need frames for animation elements
|
||||
if (aContent->IsNodeOfType(nsINode::eANIMATION)) {
|
||||
return &sSuppressData;
|
||||
}
|
||||
|
||||
// Reduce the number of frames we create unnecessarily. Note that this is not
|
||||
// where we select which frame in a <switch> to render! That happens in
|
||||
// nsSVGSwitchFrame::PaintSVG.
|
||||
|
@ -398,9 +398,6 @@ private:
|
||||
|
||||
nsresult GetDocumentSelection(nsISelection **aSelection);
|
||||
|
||||
nsresult GetClipboardEventTarget(nsIDOMNode **aEventTarget);
|
||||
nsresult FireClipboardEvent(PRUint32 msg, PRBool* aPreventDefault);
|
||||
|
||||
void DestroyPresShell();
|
||||
|
||||
#ifdef NS_PRINTING
|
||||
@ -2410,8 +2407,7 @@ DocumentViewerImpl::CreateDeviceContext(nsIView* aContainerView)
|
||||
}
|
||||
|
||||
// Return the selection for the document. Note that text fields have their
|
||||
// own selection, which cannot be accessed with this method. Use
|
||||
// mPresShell->GetSelectionForCopy() instead.
|
||||
// own selection, which cannot be accessed with this method.
|
||||
nsresult DocumentViewerImpl::GetDocumentSelection(nsISelection **aSelection)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aSelection);
|
||||
@ -2431,25 +2427,12 @@ nsresult DocumentViewerImpl::GetDocumentSelection(nsISelection **aSelection)
|
||||
* nsIContentViewerEdit
|
||||
* ======================================================================================== */
|
||||
|
||||
NS_IMETHODIMP DocumentViewerImpl::Search()
|
||||
{
|
||||
// Nothing to do here.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DocumentViewerImpl::GetSearchable(PRBool *aSearchable)
|
||||
{
|
||||
// Nothing to do here.
|
||||
*aSearchable = PR_FALSE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DocumentViewerImpl::ClearSelection()
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsISelection> selection;
|
||||
|
||||
// use mPresShell->GetSelectionForCopy() ?
|
||||
// use nsCopySupport::GetSelectionForCopy() ?
|
||||
rv = GetDocumentSelection(getter_AddRefs(selection));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
@ -2464,7 +2447,7 @@ NS_IMETHODIMP DocumentViewerImpl::SelectAll()
|
||||
nsCOMPtr<nsISelection> selection;
|
||||
nsresult rv;
|
||||
|
||||
// use mPresShell->GetSelectionForCopy() ?
|
||||
// use nsCopySupport::GetSelectionForCopy() ?
|
||||
rv = GetDocumentSelection(getter_AddRefs(selection));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
@ -2494,12 +2477,8 @@ NS_IMETHODIMP DocumentViewerImpl::SelectAll()
|
||||
|
||||
NS_IMETHODIMP DocumentViewerImpl::CopySelection()
|
||||
{
|
||||
PRBool preventDefault;
|
||||
nsresult rv = FireClipboardEvent(NS_COPY, &preventDefault);
|
||||
if (NS_FAILED(rv) || preventDefault)
|
||||
return rv;
|
||||
|
||||
return mPresShell->DoCopy();
|
||||
nsCopySupport::FireClipboardEvent(NS_COPY, mPresShell, nsnull);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DocumentViewerImpl::CopyLinkLocation()
|
||||
@ -2532,118 +2511,47 @@ NS_IMETHODIMP DocumentViewerImpl::CopyImage(PRInt32 aCopyFlags)
|
||||
return nsCopySupport::ImageCopy(node, aCopyFlags);
|
||||
}
|
||||
|
||||
nsresult DocumentViewerImpl::GetClipboardEventTarget(nsIDOMNode** aEventTarget)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aEventTarget);
|
||||
*aEventTarget = nsnull;
|
||||
|
||||
if (!mPresShell)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
nsCOMPtr<nsISelection> sel;
|
||||
nsresult rv = mPresShell->GetSelectionForCopy(getter_AddRefs(sel));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
if (!sel)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
return nsCopySupport::GetClipboardEventTarget(sel, aEventTarget);
|
||||
}
|
||||
|
||||
nsresult DocumentViewerImpl::FireClipboardEvent(PRUint32 msg,
|
||||
PRBool* aPreventDefault)
|
||||
{
|
||||
*aPreventDefault = PR_FALSE;
|
||||
|
||||
NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_INITIALIZED);
|
||||
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
// It seems to be unsafe to fire an event handler during reflow (bug 393696)
|
||||
PRBool isReflowing = PR_TRUE;
|
||||
nsresult rv = mPresShell->IsReflowLocked(&isReflowing);
|
||||
if (NS_FAILED(rv) || isReflowing)
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIDOMNode> eventTarget;
|
||||
rv = GetClipboardEventTarget(getter_AddRefs(eventTarget));
|
||||
if (NS_FAILED(rv))
|
||||
// On failure to get event target, just forget about it and don't fire.
|
||||
return NS_OK;
|
||||
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
nsEvent evt(PR_TRUE, msg);
|
||||
nsEventDispatcher::Dispatch(eventTarget, mPresContext, &evt, nsnull,
|
||||
&status);
|
||||
// if event handler return'd false (PreventDefault)
|
||||
if (status == nsEventStatus_eConsumeNoDefault)
|
||||
*aPreventDefault = PR_TRUE;
|
||||
|
||||
// Ensure that the calling function can use mPresShell -- if the event
|
||||
// handler closed this window, mPresShell will be gone.
|
||||
NS_ENSURE_STATE(mPresShell);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DocumentViewerImpl::GetCopyable(PRBool *aCopyable)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aCopyable);
|
||||
*aCopyable = PR_FALSE;
|
||||
|
||||
NS_ENSURE_STATE(mPresShell);
|
||||
nsCOMPtr<nsISelection> selection;
|
||||
nsresult rv = mPresShell->GetSelectionForCopy(getter_AddRefs(selection));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
PRBool isCollapsed;
|
||||
selection->GetIsCollapsed(&isCollapsed);
|
||||
|
||||
*aCopyable = !isCollapsed;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DocumentViewerImpl::CutSelection()
|
||||
{
|
||||
// preventDefault's value is ignored because cut from the document has no
|
||||
// default behaviour.
|
||||
PRBool preventDefault;
|
||||
return FireClipboardEvent(NS_CUT, &preventDefault);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DocumentViewerImpl::GetCutable(PRBool *aCutable)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aCutable);
|
||||
*aCutable = PR_FALSE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DocumentViewerImpl::Paste()
|
||||
{
|
||||
// preventDefault's value is ignored because paste into the document has no
|
||||
// default behaviour.
|
||||
PRBool preventDefault;
|
||||
return FireClipboardEvent(NS_PASTE, &preventDefault);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DocumentViewerImpl::GetPasteable(PRBool *aPasteable)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aPasteable);
|
||||
*aPasteable = PR_FALSE;
|
||||
*aCopyable = nsCopySupport::CanCopy(mDocument);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* AString getContents (in string mimeType, in boolean selectionOnly); */
|
||||
NS_IMETHODIMP DocumentViewerImpl::GetContents(const char *mimeType, PRBool selectionOnly, nsAString& aOutValue)
|
||||
{
|
||||
aOutValue.Truncate();
|
||||
|
||||
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
|
||||
return mPresShell->DoGetContents(nsDependentCString(mimeType), 0, selectionOnly, aOutValue);
|
||||
NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
// Now we have the selection. Make sure it's nonzero:
|
||||
nsCOMPtr<nsISelection> sel;
|
||||
if (selectionOnly) {
|
||||
nsCopySupport::GetSelectionForCopy(mDocument, getter_AddRefs(sel));
|
||||
NS_ENSURE_TRUE(sel, NS_ERROR_FAILURE);
|
||||
|
||||
PRBool isCollapsed;
|
||||
sel->GetIsCollapsed(&isCollapsed);
|
||||
if (isCollapsed)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// call the copy code
|
||||
return nsCopySupport::GetContents(nsDependentCString(mimeType), 0, sel,
|
||||
mDocument, aOutValue);
|
||||
}
|
||||
|
||||
/* readonly attribute boolean canGetContents; */
|
||||
NS_IMETHODIMP DocumentViewerImpl::GetCanGetContents(PRBool *aCanGetContents)
|
||||
{
|
||||
return GetCopyable(aCanGetContents);
|
||||
NS_ENSURE_ARG_POINTER(aCanGetContents);
|
||||
*aCanGetContents = PR_FALSE;
|
||||
NS_ENSURE_STATE(mDocument);
|
||||
*aCanGetContents = nsCopySupport::CanCopy(mDocument);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef XP_MAC
|
||||
|
@ -127,8 +127,8 @@ typedef struct CapturingContentInfo {
|
||||
} CapturingContentInfo;
|
||||
|
||||
#define NS_IPRESSHELL_IID \
|
||||
{ 0xe5e070ce, 0xbc17, 0x4b5f, \
|
||||
{ 0xb2, 0x21, 0xbf, 0xc3, 0xe1, 0x68, 0xbe, 0x9b } }
|
||||
{ 0x0e170e5f, 0xf6d4, 0x44c5, \
|
||||
{ 0xbc, 0x2c, 0x44, 0x94, 0x20, 0x7e, 0xcc, 0x30 } }
|
||||
|
||||
// Constants for ScrollContentIntoView() function
|
||||
#define NS_PRESSHELL_SCROLL_TOP 0
|
||||
@ -578,27 +578,11 @@ public:
|
||||
*/
|
||||
NS_IMETHOD NotifyDestroyingFrame(nsIFrame* aFrame) = 0;
|
||||
|
||||
/**
|
||||
* Notify the Clipboard that we have something to copy.
|
||||
*/
|
||||
NS_IMETHOD DoCopy() = 0;
|
||||
|
||||
/**
|
||||
* Get the selection of the focussed element (either the page selection,
|
||||
* or the selection for a text field).
|
||||
*/
|
||||
NS_IMETHOD GetSelectionForCopy(nsISelection** outSelection) = 0;
|
||||
|
||||
/**
|
||||
* Get link location.
|
||||
*/
|
||||
NS_IMETHOD GetLinkLocation(nsIDOMNode* aNode, nsAString& aLocation) = 0;
|
||||
|
||||
/**
|
||||
* Get the doc or the selection as text or html.
|
||||
*/
|
||||
NS_IMETHOD DoGetContents(const nsACString& aMimeType, PRUint32 aFlags, PRBool aSelectionOnly, nsAString& outValue) = 0;
|
||||
|
||||
/**
|
||||
* Get the caret, if it exists. AddRefs it.
|
||||
*/
|
||||
|
@ -98,6 +98,10 @@
|
||||
#include "nsSVGOuterSVGFrame.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
#include "nsXULPopupManager.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla::layers;
|
||||
|
||||
/**
|
||||
@ -779,6 +783,28 @@ nsLayoutUtils::GetEventCoordinatesRelativeTo(const nsEvent* aEvent, nsIFrame* aF
|
||||
return widgetToView - aFrame->GetOffsetTo(rootFrame);
|
||||
}
|
||||
|
||||
nsIFrame*
|
||||
nsLayoutUtils::GetPopupFrameForEventCoordinates(const nsEvent* aEvent)
|
||||
{
|
||||
#ifdef MOZ_XUL
|
||||
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
|
||||
if (!pm) {
|
||||
return nsnull;
|
||||
}
|
||||
nsTArray<nsIFrame*> popups = pm->GetVisiblePopups();
|
||||
PRUint32 i;
|
||||
// Search from top to bottom
|
||||
for (i = 0; i < popups.Length(); i++) {
|
||||
nsIFrame* popup = popups[i];
|
||||
if (popup->GetOverflowRect().Contains(
|
||||
GetEventCoordinatesRelativeTo(aEvent, popup))) {
|
||||
return popup;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
gfxMatrix
|
||||
nsLayoutUtils::ChangeMatrixBasis(const gfxPoint &aOrigin,
|
||||
const gfxMatrix &aMatrix)
|
||||
|
@ -388,6 +388,14 @@ public:
|
||||
nsIFrame* aFrame);
|
||||
|
||||
/**
|
||||
* Get the popup frame of a given native mouse event.
|
||||
* @param aEvent the event.
|
||||
* @return Null, if there is no popup frame at the point, otherwise,
|
||||
* returns top-most popup frame at the point.
|
||||
*/
|
||||
static nsIFrame* GetPopupFrameForEventCoordinates(const nsEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Translate from widget coordinates to the view's coordinates
|
||||
* @param aPresContext the PresContext for the view
|
||||
* @param aWidget the widget
|
||||
|
@ -738,12 +738,8 @@ public:
|
||||
|
||||
NS_IMETHOD SetIgnoreFrameDestruction(PRBool aIgnore);
|
||||
NS_IMETHOD NotifyDestroyingFrame(nsIFrame* aFrame);
|
||||
|
||||
NS_IMETHOD DoCopy();
|
||||
NS_IMETHOD GetSelectionForCopy(nsISelection** outSelection);
|
||||
|
||||
NS_IMETHOD GetLinkLocation(nsIDOMNode* aNode, nsAString& aLocationString);
|
||||
NS_IMETHOD DoGetContents(const nsACString& aMimeType, PRUint32 aFlags, PRBool aSelectionOnly, nsAString& outValue);
|
||||
|
||||
NS_IMETHOD CaptureHistoryState(nsILayoutHistoryState** aLayoutHistoryState, PRBool aLeavingPage);
|
||||
|
||||
@ -4298,57 +4294,6 @@ NS_IMETHODIMP PresShell::GetLinkLocation(nsIDOMNode* aNode, nsAString& aLocation
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresShell::GetSelectionForCopy(nsISelection** outSelection)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
*outSelection = nsnull;
|
||||
|
||||
if (!mDocument) return NS_ERROR_FAILURE;
|
||||
|
||||
nsCOMPtr<nsIContent> content;
|
||||
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
if (fm) {
|
||||
nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(mDocument->GetWindow());
|
||||
|
||||
nsCOMPtr<nsIDOMElement> focusedElement;
|
||||
fm->GetFocusedElementForWindow(window, PR_FALSE, nsnull, getter_AddRefs(focusedElement));
|
||||
content = do_QueryInterface(focusedElement);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISelection> sel;
|
||||
if (content)
|
||||
{
|
||||
//check to see if we need to get selection from frame
|
||||
//optimization that MAY need to be expanded as more things implement their own "selection"
|
||||
nsCOMPtr<nsIDOMNSHTMLInputElement> htmlInputElement(do_QueryInterface(content));
|
||||
nsCOMPtr<nsIDOMNSHTMLTextAreaElement> htmlTextAreaElement(do_QueryInterface(content));
|
||||
if (htmlInputElement || htmlTextAreaElement)
|
||||
{
|
||||
nsIFrame *htmlInputFrame = content->GetPrimaryFrame();
|
||||
if (!htmlInputFrame) return NS_ERROR_FAILURE;
|
||||
|
||||
nsCOMPtr<nsISelectionController> selCon;
|
||||
rv = htmlInputFrame->
|
||||
GetSelectionController(mPresContext,getter_AddRefs(selCon));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
if (!selCon) return NS_ERROR_FAILURE;
|
||||
|
||||
rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
|
||||
getter_AddRefs(sel));
|
||||
}
|
||||
}
|
||||
if (!sel) {
|
||||
sel = mSelection->GetSelection(nsISelectionController::SELECTION_NORMAL);
|
||||
rv = NS_OK;
|
||||
}
|
||||
|
||||
*outSelection = sel;
|
||||
NS_IF_ADDREF(*outSelection);
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(void)
|
||||
PresShell::DispatchSynthMouseMove(nsGUIEvent *aEvent,
|
||||
PRBool aFlushOnHoverChange)
|
||||
@ -4405,67 +4350,6 @@ PresShell::ClearMouseCapture(nsIView* aView)
|
||||
gCaptureInfo.mAllowed = PR_FALSE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresShell::DoGetContents(const nsACString& aMimeType, PRUint32 aFlags, PRBool aSelectionOnly, nsAString& aOutValue)
|
||||
{
|
||||
aOutValue.Truncate();
|
||||
|
||||
if (!mDocument) return NS_ERROR_FAILURE;
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsISelection> sel;
|
||||
|
||||
// Now we have the selection. Make sure it's nonzero:
|
||||
if (aSelectionOnly)
|
||||
{
|
||||
rv = GetSelectionForCopy(getter_AddRefs(sel));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
if (!sel) return NS_ERROR_FAILURE;
|
||||
|
||||
PRBool isCollapsed;
|
||||
sel->GetIsCollapsed(&isCollapsed);
|
||||
if (isCollapsed)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// call the copy code
|
||||
return nsCopySupport::GetContents(aMimeType, aFlags, sel,
|
||||
mDocument, aOutValue);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresShell::DoCopy()
|
||||
{
|
||||
if (!mDocument) return NS_ERROR_FAILURE;
|
||||
|
||||
nsCOMPtr<nsISelection> sel;
|
||||
nsresult rv = GetSelectionForCopy(getter_AddRefs(sel));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
if (!sel)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// Now we have the selection. Make sure it's nonzero:
|
||||
PRBool isCollapsed;
|
||||
sel->GetIsCollapsed(&isCollapsed);
|
||||
if (isCollapsed)
|
||||
return NS_OK;
|
||||
|
||||
// call the copy code
|
||||
rv = nsCopySupport::HTMLCopy(sel, mDocument, nsIClipboard::kGlobalClipboard);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// Now that we have copied, update the Paste menu item
|
||||
nsPIDOMWindow *domWindow = mDocument->GetWindow();
|
||||
if (domWindow)
|
||||
{
|
||||
domWindow->UpdateCommands(NS_LITERAL_STRING("clipboard"));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresShell::CaptureHistoryState(nsILayoutHistoryState** aState, PRBool aLeavingPage)
|
||||
{
|
||||
@ -6156,27 +6040,16 @@ PresShell::HandleEvent(nsIView *aView,
|
||||
// list.
|
||||
if (framePresContext == rootPresContext &&
|
||||
frame == FrameManager()->GetRootFrame()) {
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
|
||||
if (pm) {
|
||||
nsTArray<nsIFrame*> popups = pm->GetVisiblePopups();
|
||||
PRUint32 i;
|
||||
// Search from top to bottom
|
||||
nsIDocument* doc = framePresContext->GetPresShell()->GetDocument();
|
||||
for (i = 0; i < popups.Length(); i++) {
|
||||
nsIFrame* popup = popups[i];
|
||||
if (popup->GetOverflowRect().Contains(
|
||||
nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, popup)) &&
|
||||
!nsContentUtils::ContentIsCrossDocDescendantOf(
|
||||
doc, popup->GetContent())) {
|
||||
// The event should target the popup
|
||||
frame = popup;
|
||||
break;
|
||||
}
|
||||
}
|
||||
nsIFrame* popupFrame =
|
||||
nsLayoutUtils::GetPopupFrameForEventCoordinates(aEvent);
|
||||
// If the popupFrame is an ancestor of the 'frame', the frame should
|
||||
// handle the event, otherwise, the popup should handle it.
|
||||
if (popupFrame &&
|
||||
!nsContentUtils::ContentIsCrossDocDescendantOf(
|
||||
framePresContext->GetPresShell()->GetDocument(),
|
||||
popupFrame->GetContent())) {
|
||||
frame = popupFrame;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
PRBool captureRetarget = PR_FALSE;
|
||||
|
@ -593,6 +593,12 @@
|
||||
#define NS_STYLE_POINTER_EVENTS_ALL 8
|
||||
#define NS_STYLE_POINTER_EVENTS_AUTO 9
|
||||
|
||||
// See nsStyleDisplay
|
||||
#define NS_STYLE_RESIZE_NONE 0
|
||||
#define NS_STYLE_RESIZE_BOTH 1
|
||||
#define NS_STYLE_RESIZE_HORIZONTAL 2
|
||||
#define NS_STYLE_RESIZE_VERTICAL 3
|
||||
|
||||
// See nsStyleText
|
||||
#define NS_STYLE_TEXT_ALIGN_DEFAULT 0
|
||||
#define NS_STYLE_TEXT_ALIGN_LEFT 1
|
||||
|
@ -57,6 +57,7 @@ _TEST_FILES = test_bug231389.html \
|
||||
test_bug476308.html \
|
||||
test_bug477531.html \
|
||||
test_bug477700.html \
|
||||
test_textarea_resize.html \
|
||||
test_bug478219.xhtml \
|
||||
test_bug542914.html \
|
||||
bug477700_subframe.html \
|
||||
|
77
layout/forms/test/test_textarea_resize.html
Normal file
77
layout/forms/test/test_textarea_resize.html
Normal file
@ -0,0 +1,77 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Bug 477700</title>
|
||||
<script type="application/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
|
||||
<textarea id="textarea" style="-moz-appearance: none; border: 0;">Text</textarea>
|
||||
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for textbox resizing **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() SimpleTest.executeSoon(doTheTest));
|
||||
|
||||
// First, test the default value which is 'both', then test explicitly
|
||||
// setting each possible value.
|
||||
var currentResize = "both";
|
||||
var resizeTypes = [ "horizontal", "vertical", "none", "inherit", "both" ];
|
||||
|
||||
function doTheTest() {
|
||||
var textarea = $("textarea");
|
||||
var rect = textarea.getBoundingClientRect();
|
||||
// assume that the resizer is in the lower right corner
|
||||
synthesizeMouse(textarea, rect.width - 8, rect.height - 8, { type:"mousedown" });
|
||||
synthesizeMouse(textarea, rect.width + 40, rect.height + 40, { type:"mousemove" });
|
||||
|
||||
var newrect = textarea.getBoundingClientRect();
|
||||
var hchange = (currentResize == "both" || currentResize == "horizontal");
|
||||
var vchange = (currentResize == "both" || currentResize == "vertical");
|
||||
|
||||
is(Math.round(newrect.width), Math.round(rect.width + (hchange ? 48 : 0)),
|
||||
currentResize + " width has increased");
|
||||
is(Math.round(newrect.height), Math.round(rect.height + (vchange ? 48 : 0)),
|
||||
currentResize + " height has increased");
|
||||
|
||||
synthesizeMouse(textarea, rect.width - 20, rect.height - 20, { type:"mousemove" });
|
||||
|
||||
newrect = textarea.getBoundingClientRect();
|
||||
is(Math.round(newrect.width), Math.round(rect.width - (hchange ? 12 : 0)),
|
||||
currentResize + " width has decreased");
|
||||
is(Math.round(newrect.height), Math.round(rect.height - (vchange ? 12 : 0)),
|
||||
currentResize + " height has decreased");
|
||||
|
||||
synthesizeMouse(textarea, rect.width - 220, rect.height - 220, { type:"mousemove" });
|
||||
|
||||
newrect = textarea.getBoundingClientRect();
|
||||
ok(hchange ? newrect.width >= 15 : Math.round(newrect.width) == Math.round(rect.width),
|
||||
currentResize + " width decreased below minimum");
|
||||
ok(vchange ? newrect.height >= 15 : Math.round(newrect.height) == Math.round(rect.height),
|
||||
currentResize + " height decreased below minimum");
|
||||
|
||||
synthesizeMouse(textarea, rect.width - 8, rect.height - 8, { type:"mouseup" });
|
||||
|
||||
currentResize = resizeTypes.shift();
|
||||
if (currentResize) {
|
||||
textarea.style.width = "auto";
|
||||
textarea.style.height = "auto";
|
||||
textarea.style.MozResize = currentResize;
|
||||
SimpleTest.executeSoon(doTheTest);
|
||||
}
|
||||
else {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1815,7 +1815,7 @@ nsGfxScrollFrameInner::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
{
|
||||
nsresult rv = mOuter->DisplayBorderBackgroundOutline(aBuilder, aLists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
||||
if (aBuilder->GetIgnoreScrollFrame() == mOuter) {
|
||||
// Don't clip the scrolled child, and don't paint scrollbars/scrollcorner.
|
||||
// The scrolled frame shouldn't have its own background/border, so we
|
||||
@ -1828,13 +1828,16 @@ nsGfxScrollFrameInner::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
// in the border-background layer, on top of our own background and
|
||||
// borders and underneath borders and backgrounds of later elements
|
||||
// in the tree.
|
||||
nsIFrame* kid = mOuter->GetFirstChild(nsnull);
|
||||
while (kid) {
|
||||
PRBool hasResizer = HasResizer();
|
||||
for (nsIFrame* kid = mOuter->GetFirstChild(nsnull); kid; kid = kid->GetNextSibling()) {
|
||||
if (kid != mScrolledFrame) {
|
||||
if (kid == mScrollCornerBox && hasResizer) {
|
||||
// skip the resizer as this will be drawn later on top of the scrolled content
|
||||
continue;
|
||||
}
|
||||
rv = mOuter->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
kid = kid->GetNextSibling();
|
||||
}
|
||||
|
||||
// Overflow clipping can never clip frames outside our subtree, so there
|
||||
@ -1860,6 +1863,15 @@ nsGfxScrollFrameInner::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
rv = mOuter->OverflowClip(aBuilder, set, aLists, clip, PR_TRUE, mIsRoot);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Place the resizer in the display list above the overflow clip. This
|
||||
// ensures that the resizer appears above the content and the mouse can
|
||||
// still target the resizer even when scrollbars are hidden.
|
||||
if (hasResizer && mScrollCornerBox) {
|
||||
rv = mOuter->BuildDisplayListForChild(aBuilder, mScrollCornerBox, aDirtyRect, aLists,
|
||||
nsIFrame::DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -2176,6 +2188,19 @@ nsGfxScrollFrameInner::CreateAnonymousContent(nsTArray<nsIContent*>& aElements)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the frame is resizable.
|
||||
nsIFrame* resizableFrame = mOuter;
|
||||
if (parent) {
|
||||
// For textarea, mOuter is the frame for the anonymous div element,
|
||||
// so get the resizability from the parent textarea instead.
|
||||
nsCOMPtr<nsIDOMHTMLTextAreaElement> textAreaElement(do_QueryInterface(parent->GetContent()));
|
||||
if (textAreaElement) {
|
||||
resizableFrame = parent;
|
||||
}
|
||||
}
|
||||
|
||||
PRBool isResizable = resizableFrame->GetStyleDisplay()->mResize != NS_STYLE_RESIZE_NONE;
|
||||
|
||||
nsIScrollableFrame *scrollable = do_QueryFrame(mOuter);
|
||||
|
||||
// At this stage in frame construction, the document element and/or
|
||||
@ -2195,7 +2220,7 @@ nsGfxScrollFrameInner::CreateAnonymousContent(nsTArray<nsIContent*>& aElements)
|
||||
ScrollbarStyles styles = scrollable->GetScrollbarStyles();
|
||||
PRBool canHaveHorizontal = styles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN;
|
||||
PRBool canHaveVertical = styles.mVertical != NS_STYLE_OVERFLOW_HIDDEN;
|
||||
if (!canHaveHorizontal && !canHaveVertical) {
|
||||
if (!canHaveHorizontal && !canHaveVertical && !isResizable) {
|
||||
// Nothing to do.
|
||||
return NS_OK;
|
||||
}
|
||||
@ -2240,7 +2265,42 @@ nsGfxScrollFrameInner::CreateAnonymousContent(nsTArray<nsIContent*>& aElements)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (canHaveHorizontal && canHaveVertical) {
|
||||
if (isResizable) {
|
||||
nsCOMPtr<nsINodeInfo> nodeInfo;
|
||||
nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::resizer, nsnull,
|
||||
kNameSpaceID_XUL);
|
||||
NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
rv = NS_NewXULElement(getter_AddRefs(mScrollCornerContent), nodeInfo);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoString dir;
|
||||
switch (resizableFrame->GetStyleDisplay()->mResize) {
|
||||
case NS_STYLE_RESIZE_HORIZONTAL:
|
||||
if (IsScrollbarOnRight()) {
|
||||
dir.AssignLiteral("right");
|
||||
}
|
||||
else {
|
||||
dir.AssignLiteral("left");
|
||||
}
|
||||
break;
|
||||
case NS_STYLE_RESIZE_VERTICAL:
|
||||
dir.AssignLiteral("bottom");
|
||||
break;
|
||||
case NS_STYLE_RESIZE_BOTH:
|
||||
dir.AssignLiteral("bottomend");
|
||||
break;
|
||||
default:
|
||||
NS_WARNING("only resizable types should have resizers");
|
||||
}
|
||||
mScrollCornerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, dir, PR_FALSE);
|
||||
mScrollCornerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::element,
|
||||
NS_LITERAL_STRING("_parent"), PR_FALSE);
|
||||
|
||||
if (!aElements.AppendElement(mScrollCornerContent))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
else if (canHaveHorizontal && canHaveVertical) {
|
||||
nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::scrollcorner, nsnull,
|
||||
kNameSpaceID_XUL);
|
||||
rv = NS_NewElement(getter_AddRefs(mScrollCornerContent),
|
||||
@ -2972,26 +3032,36 @@ static void LayoutAndInvalidate(nsBoxLayoutState& aState,
|
||||
}
|
||||
}
|
||||
|
||||
static void AdjustScrollbarRect(nsIFrame* aFrame, nsPresContext* aPresContext,
|
||||
nsRect& aRect, PRBool aVertical)
|
||||
void
|
||||
nsGfxScrollFrameInner::AdjustScrollbarRectForResizer(
|
||||
nsIFrame* aFrame, nsPresContext* aPresContext,
|
||||
nsRect& aRect, PRBool aHasResizer, PRBool aVertical)
|
||||
{
|
||||
if ((aVertical ? aRect.width : aRect.height) == 0)
|
||||
return;
|
||||
|
||||
nsPoint offsetToView;
|
||||
nsPoint offsetToWidget;
|
||||
nsIWidget* widget =
|
||||
aFrame->GetClosestView(&offsetToView)->GetNearestWidget(&offsetToWidget);
|
||||
nsPoint offset = offsetToView + offsetToWidget;
|
||||
nsIntRect widgetRect;
|
||||
if (!widget || !widget->ShowsResizeIndicator(&widgetRect))
|
||||
return;
|
||||
// if a content resizer is present, use its size. Otherwise, check if the
|
||||
// widget has a resizer.
|
||||
nsRect resizerRect;
|
||||
if (aHasResizer && mScrollCornerBox) {
|
||||
resizerRect = mScrollCornerBox->GetRect();
|
||||
}
|
||||
else {
|
||||
nsPoint offsetToView;
|
||||
nsPoint offsetToWidget;
|
||||
nsIWidget* widget =
|
||||
aFrame->GetClosestView(&offsetToView)->GetNearestWidget(&offsetToWidget);
|
||||
nsPoint offset = offsetToView + offsetToWidget;
|
||||
nsIntRect widgetRect;
|
||||
if (!widget || !widget->ShowsResizeIndicator(&widgetRect))
|
||||
return;
|
||||
|
||||
nsRect resizerRect =
|
||||
nsRect(aPresContext->DevPixelsToAppUnits(widgetRect.x) - offset.x,
|
||||
aPresContext->DevPixelsToAppUnits(widgetRect.y) - offset.y,
|
||||
aPresContext->DevPixelsToAppUnits(widgetRect.width),
|
||||
aPresContext->DevPixelsToAppUnits(widgetRect.height));
|
||||
nsRect resizerRect =
|
||||
nsRect(aPresContext->DevPixelsToAppUnits(widgetRect.x) - offset.x,
|
||||
aPresContext->DevPixelsToAppUnits(widgetRect.y) - offset.y,
|
||||
aPresContext->DevPixelsToAppUnits(widgetRect.width),
|
||||
aPresContext->DevPixelsToAppUnits(widgetRect.height));
|
||||
}
|
||||
|
||||
if (!resizerRect.Contains(aRect.BottomRight() - nsPoint(1, 1)))
|
||||
return;
|
||||
@ -3010,18 +3080,62 @@ nsGfxScrollFrameInner::LayoutScrollbars(nsBoxLayoutState& aState,
|
||||
NS_ASSERTION(!mSupppressScrollbarUpdate,
|
||||
"This should have been suppressed");
|
||||
|
||||
PRBool hasResizer = HasResizer();
|
||||
PRBool scrollbarOnLeft = !IsScrollbarOnRight();
|
||||
|
||||
// place the scrollcorner
|
||||
if (mScrollCornerBox) {
|
||||
NS_PRECONDITION(mScrollCornerBox->IsBoxFrame(), "Must be a box frame!");
|
||||
|
||||
// if a resizer is present, get its size
|
||||
nsSize resizerSize;
|
||||
if (HasResizer()) {
|
||||
// just assume a default size of 15 pixels
|
||||
nscoord defaultSize = nsPresContext::CSSPixelsToAppUnits(15);
|
||||
resizerSize.width =
|
||||
mVScrollbarBox ? mVScrollbarBox->GetMinSize(aState).width : defaultSize;
|
||||
resizerSize.height =
|
||||
mHScrollbarBox ? mHScrollbarBox->GetMinSize(aState).height : defaultSize;
|
||||
}
|
||||
else {
|
||||
resizerSize = nsSize(0, 0);
|
||||
}
|
||||
|
||||
nsRect r(0, 0, 0, 0);
|
||||
if (aContentArea.x != mScrollPort.x || scrollbarOnLeft) {
|
||||
// scrollbar (if any) on left
|
||||
r.x = aContentArea.x;
|
||||
r.width = PR_MAX(resizerSize.width, mScrollPort.x - aContentArea.x);
|
||||
NS_ASSERTION(r.width >= 0, "Scroll area should be inside client rect");
|
||||
} else {
|
||||
// scrollbar (if any) on right
|
||||
r.width = PR_MAX(resizerSize.width, aContentArea.XMost() - mScrollPort.XMost());
|
||||
r.x = aContentArea.XMost() - r.width;
|
||||
NS_ASSERTION(r.width >= 0, "Scroll area should be inside client rect");
|
||||
}
|
||||
if (aContentArea.y != mScrollPort.y) {
|
||||
NS_ERROR("top scrollbars not supported");
|
||||
} else {
|
||||
// scrollbar (if any) on bottom
|
||||
r.height = PR_MAX(resizerSize.height, aContentArea.YMost() - mScrollPort.YMost());
|
||||
r.y = aContentArea.YMost() - r.height;
|
||||
NS_ASSERTION(r.height >= 0, "Scroll area should be inside client rect");
|
||||
}
|
||||
LayoutAndInvalidate(aState, mScrollCornerBox, r);
|
||||
}
|
||||
|
||||
nsPresContext* presContext = mScrolledFrame->PresContext();
|
||||
if (mVScrollbarBox) {
|
||||
NS_PRECONDITION(mVScrollbarBox->IsBoxFrame(), "Must be a box frame!");
|
||||
nsRect vRect(mScrollPort);
|
||||
vRect.width = aContentArea.width - mScrollPort.width;
|
||||
vRect.x = IsScrollbarOnRight() ? mScrollPort.XMost() : aContentArea.x;
|
||||
vRect.x = scrollbarOnLeft ? aContentArea.x : mScrollPort.XMost();
|
||||
#ifdef DEBUG
|
||||
nsMargin margin;
|
||||
mVScrollbarBox->GetMargin(margin);
|
||||
NS_ASSERTION(margin == nsMargin(0,0,0,0), "Scrollbar margin not supported");
|
||||
#endif
|
||||
AdjustScrollbarRect(mOuter, presContext, vRect, PR_TRUE);
|
||||
AdjustScrollbarRectForResizer(mOuter, presContext, vRect, hasResizer, PR_TRUE);
|
||||
LayoutAndInvalidate(aState, mVScrollbarBox, vRect);
|
||||
}
|
||||
|
||||
@ -3035,39 +3149,10 @@ nsGfxScrollFrameInner::LayoutScrollbars(nsBoxLayoutState& aState,
|
||||
mHScrollbarBox->GetMargin(margin);
|
||||
NS_ASSERTION(margin == nsMargin(0,0,0,0), "Scrollbar margin not supported");
|
||||
#endif
|
||||
AdjustScrollbarRect(mOuter, presContext, hRect, PR_FALSE);
|
||||
AdjustScrollbarRectForResizer(mOuter, presContext, hRect, hasResizer, PR_FALSE);
|
||||
LayoutAndInvalidate(aState, mHScrollbarBox, hRect);
|
||||
}
|
||||
|
||||
// place the scrollcorner
|
||||
if (mScrollCornerBox) {
|
||||
NS_PRECONDITION(mScrollCornerBox->IsBoxFrame(), "Must be a box frame!");
|
||||
nsRect r(0, 0, 0, 0);
|
||||
if (aContentArea.x != mScrollPort.x) {
|
||||
// scrollbar (if any) on left
|
||||
r.x = aContentArea.x;
|
||||
r.width = mScrollPort.x - aContentArea.x;
|
||||
NS_ASSERTION(r.width >= 0, "Scroll area should be inside client rect");
|
||||
} else {
|
||||
// scrollbar (if any) on right
|
||||
r.x = mScrollPort.XMost();
|
||||
r.width = aContentArea.XMost() - mScrollPort.XMost();
|
||||
NS_ASSERTION(r.width >= 0, "Scroll area should be inside client rect");
|
||||
}
|
||||
if (aContentArea.y != mScrollPort.y) {
|
||||
// scrollbar (if any) on top
|
||||
r.y = aContentArea.y;
|
||||
r.height = mScrollPort.y - aContentArea.y;
|
||||
NS_ASSERTION(r.height >= 0, "Scroll area should be inside client rect");
|
||||
} else {
|
||||
// scrollbar (if any) on bottom
|
||||
r.y = mScrollPort.YMost();
|
||||
r.height = aContentArea.YMost() - mScrollPort.YMost();
|
||||
NS_ASSERTION(r.height >= 0, "Scroll area should be inside client rect");
|
||||
}
|
||||
LayoutAndInvalidate(aState, mScrollCornerBox, r);
|
||||
}
|
||||
|
||||
// may need to update fixed position children of the viewport,
|
||||
// if the client area changed size because of an incremental
|
||||
// reflow of a descendant. (If the outer frame is dirty, the fixed
|
||||
|
@ -215,6 +215,15 @@ public:
|
||||
nsMargin GetDesiredScrollbarSizes(nsBoxLayoutState* aState);
|
||||
PRBool IsLTR() const;
|
||||
PRBool IsScrollbarOnRight() const;
|
||||
// adjust the scrollbar rectangle aRect to account for any visible resizer.
|
||||
// aHasResizer specifies if there is a content resizer, however this method
|
||||
// will also check if a widget resizer is present as well.
|
||||
void AdjustScrollbarRectForResizer(nsIFrame* aFrame, nsPresContext* aPresContext,
|
||||
nsRect& aRect, PRBool aHasResizer, PRBool aVertical);
|
||||
// returns true if a resizer should be visible
|
||||
PRBool HasResizer() {
|
||||
return mScrollCornerContent && mScrollCornerContent->Tag() == nsGkAtoms::resizer;
|
||||
}
|
||||
void LayoutScrollbars(nsBoxLayoutState& aState,
|
||||
const nsRect& aContentArea,
|
||||
const nsRect& aOldScrollArea);
|
||||
|
@ -1,6 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<textarea style="display: block; border: none; width: 200px; height: 200px; padding: 50px; background: green"></textarea>
|
||||
<textarea style="display: block; border: none; width: 200px; height: 200px; padding: 50px; background: green; -moz-resize: none;"></textarea>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<textarea style="display: block; border: none; width: 200px; height: 200px; padding: 50px; background: green; max-width: 200px;"></textarea>
|
||||
<textarea style="display: block; border: none; width: 200px; height: 200px; padding: 50px; background: green; max-width: 200px; -moz-resize: none;"></textarea>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<textarea style="display: block; border: none; width: 200px; height: 200px; padding: 50px; background: green; max-height: 200px;"></textarea>
|
||||
<textarea style="display: block; border: none; width: 200px; height: 200px; padding: 50px; background: green; max-height: 200px; -moz-resize: none;"></textarea>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -8,6 +8,6 @@
|
||||
|
||||
<script type="text/javascript" src="platform.js"/>
|
||||
|
||||
<html:textarea rows="10"/>
|
||||
<html:textarea rows="10" style="-moz-resize: none;"/>
|
||||
|
||||
</window>
|
||||
|
@ -3,6 +3,6 @@
|
||||
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<html:textarea xmlns:html="http://www.w3.org/1999/xhtml" style="width: 200px; height: 200px; margin: 0;"/>
|
||||
<html:textarea xmlns:html="http://www.w3.org/1999/xhtml" style="width: 200px; height: 200px; margin: 0; -moz-resize: none;"/>
|
||||
|
||||
</window>
|
||||
|
22
layout/reftests/svg/clipPath-basic-04.svg
Normal file
22
layout/reftests/svg/clipPath-basic-04.svg
Normal file
@ -0,0 +1,22 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/licenses/publicdomain/
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
|
||||
|
||||
<title>Testcase for clipPath with animateTransform</title>
|
||||
|
||||
<!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=553053 -->
|
||||
|
||||
<defs>
|
||||
<clipPath id="clip">
|
||||
<rect width="100%" height="100%" fill="lime"/>
|
||||
<animateTransform attributeName="transform" type="scale" values="1"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
<rect width="100%" height="100%" fill="red"/>
|
||||
|
||||
<rect width="100%" height="100%" fill="lime" clip-path="url(#clip)"/>
|
||||
|
||||
</svg>
|
After Width: | Height: | Size: 626 B |
@ -24,6 +24,7 @@ include svg-integration/reftest.list
|
||||
== clipPath-basic-01.svg pass.svg
|
||||
== clipPath-basic-02.svg pass.svg
|
||||
== clipPath-basic-03.svg pass.svg
|
||||
== clipPath-basic-04.svg pass.svg
|
||||
== clipPath-winding-01.svg pass.svg
|
||||
== clip-surface-clone-01.svg clip-surface-clone-01-ref.svg
|
||||
== conditions-01.svg pass.svg
|
||||
|
@ -127,6 +127,7 @@ textarea {
|
||||
letter-spacing: normal;
|
||||
vertical-align: text-bottom;
|
||||
cursor: text;
|
||||
-moz-resize: both;
|
||||
-moz-binding: url("chrome://global/content/platformHTMLBindings.xml#textAreas");
|
||||
-moz-appearance: textfield-multiline;
|
||||
text-indent: 0;
|
||||
|
@ -6019,6 +6019,9 @@ CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue,
|
||||
nsCSSProps::kPointerEventsKTable);
|
||||
case eCSSProperty_position:
|
||||
return ParseVariant(aValue, VARIANT_HK, nsCSSProps::kPositionKTable);
|
||||
case eCSSProperty_resize:
|
||||
return ParseVariant(aValue, VARIANT_HK,
|
||||
nsCSSProps::kResizeKTable);
|
||||
case eCSSProperty_richness:
|
||||
return ParseVariant(aValue, VARIANT_HN, nsnull);
|
||||
#ifdef MOZ_MATHML
|
||||
|
@ -2185,6 +2185,17 @@ CSS_PROP_QUOTES(
|
||||
nsnull,
|
||||
CSS_PROP_NO_OFFSET,
|
||||
eStyleAnimType_None)
|
||||
CSS_PROP_DISPLAY(
|
||||
-moz-resize,
|
||||
resize,
|
||||
MozResize,
|
||||
0,
|
||||
Display,
|
||||
mResize,
|
||||
eCSSType_Value,
|
||||
kResizeKTable,
|
||||
CSS_PROP_NO_OFFSET,
|
||||
eStyleAnimType_None)
|
||||
CSS_PROP_BACKENDONLY(
|
||||
richness,
|
||||
richness,
|
||||
|
@ -1106,6 +1106,14 @@ const PRInt32 nsCSSProps::kRadialGradientSizeKTable[] = {
|
||||
eCSSKeyword_UNKNOWN,-1
|
||||
};
|
||||
|
||||
const PRInt32 nsCSSProps::kResizeKTable[] = {
|
||||
eCSSKeyword_none, NS_STYLE_RESIZE_NONE,
|
||||
eCSSKeyword_both, NS_STYLE_RESIZE_BOTH,
|
||||
eCSSKeyword_horizontal, NS_STYLE_RESIZE_HORIZONTAL,
|
||||
eCSSKeyword_vertical, NS_STYLE_RESIZE_VERTICAL,
|
||||
eCSSKeyword_UNKNOWN,-1
|
||||
};
|
||||
|
||||
const PRInt32 nsCSSProps::kSpeakKTable[] = {
|
||||
eCSSKeyword_none, NS_STYLE_SPEAK_NONE,
|
||||
eCSSKeyword_normal, NS_STYLE_SPEAK_NORMAL,
|
||||
|
@ -306,6 +306,7 @@ public:
|
||||
static const PRInt32 kPositionKTable[];
|
||||
static const PRInt32 kRadialGradientShapeKTable[];
|
||||
static const PRInt32 kRadialGradientSizeKTable[];
|
||||
static const PRInt32 kResizeKTable[];
|
||||
static const PRInt32 kSpeakKTable[];
|
||||
static const PRInt32 kSpeakHeaderKTable[];
|
||||
static const PRInt32 kSpeakNumeralKTable[];
|
||||
|
@ -372,6 +372,7 @@ struct nsCSSDisplay : public nsCSSStruct {
|
||||
nsCSSRect mClip;
|
||||
nsCSSValue mOverflowX;
|
||||
nsCSSValue mOverflowY;
|
||||
nsCSSValue mResize;
|
||||
nsCSSValue mPointerEvents;
|
||||
nsCSSValue mVisibility;
|
||||
nsCSSValue mOpacity;
|
||||
|
@ -3074,6 +3074,19 @@ nsComputedDOMStyle::GetOverflowY(nsIDOMCSSValue** aValue)
|
||||
return CallQueryInterface(val, aValue);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsComputedDOMStyle::GetResize(nsIDOMCSSValue** aValue)
|
||||
{
|
||||
nsROCSSPrimitiveValue *val = GetROCSSPrimitiveValue();
|
||||
NS_ENSURE_TRUE(val, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
val->SetIdent(nsCSSProps::ValueToKeywordEnum(GetStyleDisplay()->mResize,
|
||||
nsCSSProps::kResizeKTable));
|
||||
|
||||
return CallQueryInterface(val, aValue);
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsComputedDOMStyle::GetPageBreakAfter(nsIDOMCSSValue** aValue)
|
||||
{
|
||||
@ -4532,6 +4545,7 @@ nsComputedDOMStyle::GetQueryablePropertyMap(PRUint32* aLength)
|
||||
COMPUTED_STYLE_MAP_ENTRY_LAYOUT(_moz_outline_radius_bottomRight,OutlineRadiusBottomRight),
|
||||
COMPUTED_STYLE_MAP_ENTRY_LAYOUT(_moz_outline_radius_topLeft, OutlineRadiusTopLeft),
|
||||
COMPUTED_STYLE_MAP_ENTRY_LAYOUT(_moz_outline_radius_topRight, OutlineRadiusTopRight),
|
||||
COMPUTED_STYLE_MAP_ENTRY(resize, Resize),
|
||||
COMPUTED_STYLE_MAP_ENTRY(stack_sizing, StackSizing),
|
||||
COMPUTED_STYLE_MAP_ENTRY(_moz_tab_size, MozTabSize),
|
||||
COMPUTED_STYLE_MAP_ENTRY_LAYOUT(_moz_transform, MozTransform),
|
||||
|
@ -308,6 +308,7 @@ private:
|
||||
nsresult GetOverflow(nsIDOMCSSValue** aValue);
|
||||
nsresult GetOverflowX(nsIDOMCSSValue** aValue);
|
||||
nsresult GetOverflowY(nsIDOMCSSValue** aValue);
|
||||
nsresult GetResize(nsIDOMCSSValue** aValue);
|
||||
nsresult GetPageBreakAfter(nsIDOMCSSValue** aValue);
|
||||
nsresult GetPageBreakBefore(nsIDOMCSSValue** aValue);
|
||||
nsresult GetMozTransform(nsIDOMCSSValue** aValue);
|
||||
|
@ -3984,6 +3984,10 @@ nsRuleNode::ComputeDisplayData(void* aStartStruct,
|
||||
display->mOverflowY = NS_STYLE_OVERFLOW_AUTO;
|
||||
}
|
||||
|
||||
SetDiscrete(displayData.mResize, display->mResize, canStoreInRuleTree,
|
||||
SETDSC_ENUMERATED, parentDisplay->mResize,
|
||||
NS_STYLE_RESIZE_NONE, 0, 0, 0, 0);
|
||||
|
||||
// clip property: length, auto, inherit
|
||||
if (eCSSUnit_Inherit == displayData.mClip.mTop.GetUnit()) { // if one is inherit, they all are
|
||||
canStoreInRuleTree = PR_FALSE;
|
||||
|
@ -1806,6 +1806,7 @@ nsStyleDisplay::nsStyleDisplay()
|
||||
mBreakAfter = PR_FALSE;
|
||||
mOverflowX = NS_STYLE_OVERFLOW_VISIBLE;
|
||||
mOverflowY = NS_STYLE_OVERFLOW_VISIBLE;
|
||||
mResize = NS_STYLE_RESIZE_NONE;
|
||||
mClipFlags = NS_STYLE_CLIP_AUTO;
|
||||
mClip.SetRect(0,0,0,0);
|
||||
mOpacity = 1.0f;
|
||||
@ -1841,6 +1842,7 @@ nsStyleDisplay::nsStyleDisplay(const nsStyleDisplay& aSource)
|
||||
mBreakAfter = aSource.mBreakAfter;
|
||||
mOverflowX = aSource.mOverflowX;
|
||||
mOverflowY = aSource.mOverflowY;
|
||||
mResize = aSource.mResize;
|
||||
mClipFlags = aSource.mClipFlags;
|
||||
mClip = aSource.mClip;
|
||||
mOpacity = aSource.mOpacity;
|
||||
@ -1864,7 +1866,8 @@ nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const
|
||||
|| mDisplay != aOther.mDisplay
|
||||
|| (mFloats == NS_STYLE_FLOAT_NONE) != (aOther.mFloats == NS_STYLE_FLOAT_NONE)
|
||||
|| mOverflowX != aOther.mOverflowX
|
||||
|| mOverflowY != aOther.mOverflowY)
|
||||
|| mOverflowY != aOther.mOverflowY
|
||||
|| mResize != aOther.mResize)
|
||||
NS_UpdateHint(hint, nsChangeHint_ReconstructFrame);
|
||||
|
||||
if (mFloats != aOther.mFloats) {
|
||||
|
@ -1314,6 +1314,7 @@ struct nsStyleDisplay {
|
||||
PRPackedBool mBreakAfter; // [reset]
|
||||
PRUint8 mOverflowX; // [reset] see nsStyleConsts.h
|
||||
PRUint8 mOverflowY; // [reset] see nsStyleConsts.h
|
||||
PRUint8 mResize; // [reset] see nsStyleConsts.h
|
||||
PRUint8 mClipFlags; // [reset] see nsStyleConsts.h
|
||||
PRPackedBool mTransformPresent; // [reset] Whether there is a -moz-transform.
|
||||
nsStyleTransformMatrix mTransform; // [reset] The stored transform matrix
|
||||
|
@ -535,6 +535,15 @@ var gCSSProperties = {
|
||||
other_values: [ "1px", "3em" ],
|
||||
invalid_values: []
|
||||
},
|
||||
"-moz-resize": {
|
||||
domProp: "MozResize",
|
||||
inherited: false,
|
||||
type: CSS_TYPE_LONGHAND,
|
||||
prerequisites: { "display": "block", "overflow": "auto" },
|
||||
initial_values: [ "none" ],
|
||||
other_values: [ "both", "horizontal", "vertical" ],
|
||||
invalid_values: []
|
||||
},
|
||||
"-moz-tab-size": {
|
||||
domProp: "MozTabSize",
|
||||
inherited: true,
|
||||
|
2
layout/xul/base/reftest/reftest.list
Normal file
2
layout/xul/base/reftest/reftest.list
Normal file
@ -0,0 +1,2 @@
|
||||
== textbox-multiline-noresize.xul textbox-multiline-ref.xul
|
||||
!= textbox-multiline-resize.xul textbox-multiline-ref.xul
|
5
layout/xul/base/reftest/textbox-multiline-noresize.xul
Normal file
5
layout/xul/base/reftest/textbox-multiline-noresize.xul
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<window align="start" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<textbox style="margin: 0;" multiline="true" width="100" height="100"/>
|
||||
</window>
|
5
layout/xul/base/reftest/textbox-multiline-ref.xul
Normal file
5
layout/xul/base/reftest/textbox-multiline-ref.xul
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<window align="start" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<vbox style="-moz-appearance: textfield;" width="100" height="100"/>
|
||||
</window>
|
5
layout/xul/base/reftest/textbox-multiline-resize.xul
Normal file
5
layout/xul/base/reftest/textbox-multiline-resize.xul
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<window align="start" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<textbox style="margin: 0;" resizable="true" multiline="true" width="100" height="100"/>
|
||||
</window>
|
@ -179,23 +179,17 @@ nsResizerFrame::HandleEvent(nsPresContext* aPresContext,
|
||||
nsIntPoint screenPoint(aEvent->refPoint + aEvent->widget->WidgetToScreenOffset());
|
||||
nsIntPoint mouseMove(screenPoint - mMouseDownPoint);
|
||||
|
||||
// what direction should we go in? For content resizing, always use
|
||||
// 'bottomend'. For other windows, check the dir attribute.
|
||||
Direction direction;
|
||||
// Determine which direction to resize by checking the dir attribute.
|
||||
// For windows and menus, ensure that it can be resized in that direction.
|
||||
Direction direction = GetDirection();
|
||||
if (window || menuPopupFrame) {
|
||||
direction = GetDirection();
|
||||
if (menuPopupFrame) {
|
||||
menuPopupFrame->CanAdjustEdges(
|
||||
(direction.mHorizontal == -1) ? NS_SIDE_LEFT : NS_SIDE_RIGHT,
|
||||
(direction.mVertical == -1) ? NS_SIDE_TOP : NS_SIDE_BOTTOM, mouseMove);
|
||||
}
|
||||
}
|
||||
else if (contentToResize) {
|
||||
direction.mHorizontal =
|
||||
GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL ? -1 : 1;
|
||||
direction.mVertical = 1;
|
||||
}
|
||||
else {
|
||||
else if (!contentToResize) {
|
||||
break; // don't do anything if there's nothing to resize
|
||||
}
|
||||
|
||||
@ -233,9 +227,16 @@ nsResizerFrame::HandleEvent(nsPresContext* aPresContext,
|
||||
}
|
||||
|
||||
if (contentToResize) {
|
||||
nsIntRect cssRect =
|
||||
rect.ToAppUnits(aPresContext->AppUnitsPerDevPixel())
|
||||
.ToInsidePixels(nsPresContext::AppUnitsPerCSSPixel());
|
||||
// convert the rectangle into css pixels. When changing the size in a
|
||||
// direction, don't allow the new size to be less that the resizer's
|
||||
// size. This ensures that content isn't resized too small as to make
|
||||
// the resizer invisible.
|
||||
nsRect appUnitsRect = rect.ToAppUnits(aPresContext->AppUnitsPerDevPixel());
|
||||
if (appUnitsRect.width < mRect.width && mouseMove.x)
|
||||
appUnitsRect.width = mRect.width;
|
||||
if (appUnitsRect.height < mRect.height && mouseMove.y)
|
||||
appUnitsRect.height = mRect.height;
|
||||
nsIntRect cssRect = appUnitsRect.ToInsidePixels(nsPresContext::AppUnitsPerCSSPixel());
|
||||
|
||||
nsAutoString widthstr, heightstr;
|
||||
widthstr.AppendInt(cssRect.width);
|
||||
@ -353,7 +354,9 @@ nsResizerFrame::GetContentToResize(nsIPresShell* aPresShell, nsIBaseWindow** aWi
|
||||
}
|
||||
|
||||
if (elementid.EqualsLiteral("_parent")) {
|
||||
return mContent->GetParent();
|
||||
// return the parent, but skip over native anonymous content
|
||||
nsIContent* parent = mContent->GetParent();
|
||||
return parent ? parent->FindFirstNonNativeAnonymous() : nsnull;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(aPresShell->GetDocument());
|
||||
|
@ -151,26 +151,26 @@ window.opener.SimpleTest.waitForFocus(doTest, window);
|
||||
<resizer id="html" dir="bottomend" element="_parent"/>
|
||||
</html:div>
|
||||
<hbox id="anchor" align="start" style="margin-left: 100px;">
|
||||
<hbox id="inside-container">
|
||||
<hbox id="inside-container" align="start">
|
||||
<hbox minwidth="45" minheight="41"/>
|
||||
<resizer id="inside" dir="bottomend" element="_parent"/>
|
||||
</hbox>
|
||||
<hbox id="inside-large-container" width="70" height="70">
|
||||
<hbox id="inside-large-container" width="70" height="70" align="start">
|
||||
<resizer id="inside-large" dir="bottomend" element="_parent"/>
|
||||
</hbox>
|
||||
<hbox id="inside-with-border-container" style="border: 5px solid red; padding: 2px; margin: 2px;">
|
||||
<hbox id="inside-with-border-container" style="border: 5px solid red; padding: 2px; margin: 2px;" align="start">
|
||||
<hbox minwidth="35" minheight="30"/>
|
||||
<resizer id="inside-with-border" dir="bottomend" element="_parent"/>
|
||||
</hbox>
|
||||
</hbox>
|
||||
|
||||
<panel id="inside-popup-container" onpopupshown="popupShown(event)" onpopuphidden="popupHidden()">
|
||||
<panel id="inside-popup-container" align="start" onpopupshown="popupShown(event)" onpopuphidden="popupHidden()">
|
||||
<resizer id="inside-popup" dir="bottomend"/>
|
||||
<hbox width="50" height="50" flex="1"/>
|
||||
</panel>
|
||||
<resizer id="outside-popup" dir="bottomend" element="outside-popup-container"/>
|
||||
|
||||
<panel id="anchored-panel-container" onpopupshown="anchoredPopupShown(event)"
|
||||
<panel id="anchored-panel-container" align="start" onpopupshown="anchoredPopupShown(event)"
|
||||
onpopuphidden="popupHidden()">
|
||||
<hbox width="50" height="50" flex="1"/>
|
||||
<resizer id="anchored-panel" width="20" height="20"/>
|
||||
|
@ -546,3 +546,261 @@ function disableNonTestMouseEvents(aDisable)
|
||||
if (utils)
|
||||
utils.disableNonTestMouseEvents(aDisable);
|
||||
}
|
||||
|
||||
function _getDOMWindowUtils(aWindow)
|
||||
{
|
||||
if (!aWindow) {
|
||||
aWindow = window;
|
||||
}
|
||||
return aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthesize a composition event.
|
||||
*
|
||||
* @param aIsCompositionStart If true, this synthesize compositionstart event.
|
||||
* Otherwise, compositionend event.
|
||||
* @param aWindow Optional (If null, current |window| will be used)
|
||||
*/
|
||||
function synthesizeComposition(aIsCompositionStart, aWindow)
|
||||
{
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
var utils = _getDOMWindowUtils(aWindow);
|
||||
if (!utils) {
|
||||
return;
|
||||
}
|
||||
|
||||
utils.sendCompositionEvent(aIsCompositionStart ?
|
||||
"compositionstart" : "compositionend");
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthesize a text event.
|
||||
*
|
||||
* @param aEvent The text event's information, this has |composition|
|
||||
* and |caret| members. |composition| has |string| and
|
||||
* |clauses| members. |clauses| must be array object. Each
|
||||
* object has |length| and |attr|. And |caret| has |start| and
|
||||
* |length|. See the following tree image.
|
||||
*
|
||||
* aEvent
|
||||
* +-- composition
|
||||
* | +-- string
|
||||
* | +-- clauses[]
|
||||
* | +-- length
|
||||
* | +-- attr
|
||||
* +-- caret
|
||||
* +-- start
|
||||
* +-- length
|
||||
*
|
||||
* Set the composition string to |composition.string|. Set its
|
||||
* clauses information to the |clauses| array.
|
||||
*
|
||||
* When it's composing, set the each clauses' length to the
|
||||
* |composition.clauses[n].length|. The sum of the all length
|
||||
* values must be same as the length of |composition.string|.
|
||||
* Set nsIDOMWindowUtils.COMPOSITION_ATTR_* to the
|
||||
* |composition.clauses[n].attr|.
|
||||
*
|
||||
* When it's not composing, set 0 to the
|
||||
* |composition.clauses[0].length| and
|
||||
* |composition.clauses[0].attr|.
|
||||
*
|
||||
* Set caret position to the |caret.start|. It's offset from
|
||||
* the start of the composition string. Set caret length to
|
||||
* |caret.length|. If it's larger than 0, it should be wide
|
||||
* caret. However, current nsEditor doesn't support wide
|
||||
* caret, therefore, you should always set 0 now.
|
||||
*
|
||||
* @param aWindow Optional (If null, current |window| will be used)
|
||||
*/
|
||||
function synthesizeText(aEvent, aWindow)
|
||||
{
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
var utils = _getDOMWindowUtils(aWindow);
|
||||
if (!utils) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aEvent.composition || !aEvent.composition.clauses ||
|
||||
!aEvent.composition.clauses[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
var firstClauseLength = aEvent.composition.clauses[0].length;
|
||||
var firstClauseAttr = aEvent.composition.clauses[0].attr;
|
||||
var secondClauseLength = 0;
|
||||
var secondClauseAttr = 0;
|
||||
var thirdClauseLength = 0;
|
||||
var thirdClauseAttr = 0;
|
||||
if (aEvent.composition.clauses[1]) {
|
||||
secondClauseLength = aEvent.composition.clauses[1].length;
|
||||
secondClauseAttr = aEvent.composition.clauses[1].attr;
|
||||
if (aEvent.composition.clauses[2]) {
|
||||
thirdClauseLength = aEvent.composition.clauses[2].length;
|
||||
thirdClauseAttr = aEvent.composition.clauses[2].attr;
|
||||
}
|
||||
}
|
||||
|
||||
var caretStart = -1;
|
||||
var caretLength = 0;
|
||||
if (aEvent.caret) {
|
||||
caretStart = aEvent.caret.start;
|
||||
caretLength = aEvent.caret.length;
|
||||
}
|
||||
|
||||
utils.sendTextEvent(aEvent.composition.string,
|
||||
firstClauseLength, firstClauseAttr,
|
||||
secondClauseLength, secondClauseAttr,
|
||||
thirdClauseLength, thirdClauseAttr,
|
||||
caretStart, caretLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthesize a query selected text event.
|
||||
*
|
||||
* @param aWindow Optional (If null, current |window| will be used)
|
||||
* @return An nsIQueryContentEventResult object. If this failed,
|
||||
* the result might be null.
|
||||
*/
|
||||
function synthesizeQuerySelectedText(aWindow)
|
||||
{
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
var utils = _getDOMWindowUtils(aWindow);
|
||||
if (!utils) {
|
||||
return nsnull;
|
||||
}
|
||||
return utils.sendQueryContentEvent(utils.QUERY_SELECTED_TEXT, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthesize a query text content event.
|
||||
*
|
||||
* @param aOffset The character offset. 0 means the first character in the
|
||||
* selection root.
|
||||
* @param aLength The length of getting text. If the length is too long,
|
||||
* the extra length is ignored.
|
||||
* @param aWindow Optional (If null, current |window| will be used)
|
||||
* @return An nsIQueryContentEventResult object. If this failed,
|
||||
* the result might be null.
|
||||
*/
|
||||
function synthesizeQueryTextContent(aOffset, aLength, aWindow)
|
||||
{
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
var utils = _getDOMWindowUtils(aWindow);
|
||||
if (!utils) {
|
||||
return nsnull;
|
||||
}
|
||||
return utils.sendQueryContentEvent(utils.QUERY_TEXT_CONTENT,
|
||||
aOffset, aLength, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthesize a query caret rect event.
|
||||
*
|
||||
* @param aOffset The caret offset. 0 means left side of the first character
|
||||
* in the selection root.
|
||||
* @param aWindow Optional (If null, current |window| will be used)
|
||||
* @return An nsIQueryContentEventResult object. If this failed,
|
||||
* the result might be null.
|
||||
*/
|
||||
function synthesizeQueryCaretRect(aOffset, aWindow)
|
||||
{
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
var utils = _getDOMWindowUtils(aWindow);
|
||||
if (!utils) {
|
||||
return nsnull;
|
||||
}
|
||||
return utils.sendQueryContentEvent(utils.QUERY_CARET_RECT,
|
||||
aOffset, 0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthesize a query text rect event.
|
||||
*
|
||||
* @param aOffset The character offset. 0 means the first character in the
|
||||
* selection root.
|
||||
* @param aLength The length of the text. If the length is too long,
|
||||
* the extra length is ignored.
|
||||
* @param aWindow Optional (If null, current |window| will be used)
|
||||
* @return An nsIQueryContentEventResult object. If this failed,
|
||||
* the result might be null.
|
||||
*/
|
||||
function synthesizeQueryTextRect(aOffset, aLength, aWindow)
|
||||
{
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
var utils = _getDOMWindowUtils(aWindow);
|
||||
if (!utils) {
|
||||
return nsnull;
|
||||
}
|
||||
return utils.sendQueryContentEvent(utils.QUERY_TEXT_RECT,
|
||||
aOffset, aLength, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthesize a query editor rect event.
|
||||
*
|
||||
* @param aWindow Optional (If null, current |window| will be used)
|
||||
* @return An nsIQueryContentEventResult object. If this failed,
|
||||
* the result might be null.
|
||||
*/
|
||||
function synthesizeQueryEditorRect(aWindow)
|
||||
{
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
var utils = _getDOMWindowUtils(aWindow);
|
||||
if (!utils) {
|
||||
return nsnull;
|
||||
}
|
||||
return utils.sendQueryContentEvent(utils.QUERY_EDITOR_RECT, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthesize a character at point event.
|
||||
*
|
||||
* @param aX, aY The offset in the client area of the DOM window.
|
||||
* @param aWindow Optional (If null, current |window| will be used)
|
||||
* @return An nsIQueryContentEventResult object. If this failed,
|
||||
* the result might be null.
|
||||
*/
|
||||
function synthesizeCharAtPoint(aX, aY, aWindow)
|
||||
{
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
var utils = _getDOMWindowUtils(aWindow);
|
||||
if (!utils) {
|
||||
return nsnull;
|
||||
}
|
||||
return utils.sendQueryContentEvent(utils.QUERY_CHARACTER_AT_POINT,
|
||||
0, 0, aX, aY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthesize a selection set event.
|
||||
*
|
||||
* @param aOffset The character offset. 0 means the first character in the
|
||||
* selection root.
|
||||
* @param aLength The length of the text. If the length is too long,
|
||||
* the extra length is ignored.
|
||||
* @param aReverse If true, the selection is from |aOffset + aLength| to
|
||||
* |aOffset|. Otherwise, from |aOffset| to |aOffset + aLength|.
|
||||
* @param aWindow Optional (If null, current |window| will be used)
|
||||
* @return True, if succeeded. Otherwise false.
|
||||
*/
|
||||
function synthesizeSelectionSet(aOffset, aLength, aReverse, aWindow)
|
||||
{
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
var utils = _getDOMWindowUtils(aWindow);
|
||||
if (!utils) {
|
||||
return false;
|
||||
}
|
||||
return utils.sendSelectionSetEvent(aOffset, aLength, aReverse);
|
||||
}
|
||||
|
@ -88,6 +88,9 @@ interface nsILoginManager : nsISupports {
|
||||
* If newLoginData is a nsIPropertyBag, only the specified properties
|
||||
* will be changed. The nsILoginMetaInfo properties of oldLogin can be
|
||||
* changed in this manner.
|
||||
*
|
||||
* If the propertybag contains an item named "timesUsedIncrement", the
|
||||
* login's timesUsed property will be incremented by the item's value.
|
||||
*/
|
||||
void modifyLogin(in nsILoginInfo oldLogin, in nsISupports newLoginData);
|
||||
|
||||
|
@ -111,6 +111,9 @@ interface nsILoginManagerStorage : nsISupports {
|
||||
* If newLoginData is a nsIPropertyBag, only the specified properties
|
||||
* will be changed. The nsILoginMetaInfo properties of oldLogin can be
|
||||
* changed in this manner.
|
||||
*
|
||||
* If the propertybag contains an item named "timesUsedIncrement", the
|
||||
* login's timesUsed property will be incremented by the item's value.
|
||||
*/
|
||||
void modifyLogin(in nsILoginInfo oldLogin, in nsISupports newLoginData);
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(867407d5-10e0-43a0-bc81-a324740534ca)]
|
||||
[scriptable, uuid(20d8eb40-c494-497f-b2a6-aaa32f807ebd)]
|
||||
|
||||
/**
|
||||
* An object containing metainfo for a login stored by the login manager.
|
||||
@ -59,4 +59,27 @@ interface nsILoginMetaInfo : nsISupports {
|
||||
* addLogin and modifyLogin will throw if the GUID already exists.
|
||||
*/
|
||||
attribute AString guid;
|
||||
|
||||
/**
|
||||
* The time, in Unix Epoch milliseconds, when the login was first created.
|
||||
*/
|
||||
attribute unsigned long long timeCreated;
|
||||
|
||||
/**
|
||||
* The time, in Unix Epoch milliseconds, when the login was last submitted
|
||||
* in a form or used to begin an HTTP auth session.
|
||||
*/
|
||||
attribute unsigned long long timeLastUsed;
|
||||
|
||||
/**
|
||||
* The time, in Unix Epoch milliseconds, when the login's password was
|
||||
* last modified.
|
||||
*/
|
||||
attribute unsigned long long timePasswordChanged;
|
||||
|
||||
/**
|
||||
* The number of times the login was submitted in a form or used to begin
|
||||
* an HTTP auth session.
|
||||
*/
|
||||
attribute unsigned long timesUsed;
|
||||
};
|
||||
|
@ -121,6 +121,10 @@ nsLoginInfo.prototype = {
|
||||
// Copy nsILoginMetaInfo props
|
||||
clone.QueryInterface(Ci.nsILoginMetaInfo);
|
||||
clone.guid = this.guid;
|
||||
clone.timeCreated = this.timeCreated;
|
||||
clone.timeLastUsed = this.timeLastUsed;
|
||||
clone.timePasswordChanged = this.timePasswordChanged;
|
||||
clone.timesUsed = this.timesUsed;
|
||||
|
||||
return clone;
|
||||
},
|
||||
@ -129,7 +133,11 @@ nsLoginInfo.prototype = {
|
||||
// nsILoginMetaInfo interfaces...
|
||||
//
|
||||
|
||||
guid : null
|
||||
guid : null,
|
||||
timeCreated : null,
|
||||
timeLastUsed : null,
|
||||
timePasswordChanged : null,
|
||||
timesUsed : null
|
||||
|
||||
}; // end of nsLoginInfo implementation
|
||||
|
||||
|
@ -828,6 +828,7 @@ LoginManager.prototype = {
|
||||
// Check for autocomplete=off attribute. We don't use it to prevent
|
||||
// autofilling (for existing logins), but won't save logins when it's
|
||||
// present.
|
||||
// XXX spin out a bug that we don't update timeLastUsed in this case?
|
||||
if (this._isAutocompleteDisabled(form) ||
|
||||
this._isAutocompleteDisabled(usernameField) ||
|
||||
this._isAutocompleteDisabled(newPasswordField) ||
|
||||
@ -913,6 +914,13 @@ LoginManager.prototype = {
|
||||
this.log("...passwords differ, prompting to change.");
|
||||
prompter = getPrompter(win);
|
||||
prompter.promptToChangePassword(existingLogin, formLogin);
|
||||
} else {
|
||||
// Update the lastUsed timestamp.
|
||||
var propBag = Cc["@mozilla.org/hash-property-bag;1"].
|
||||
createInstance(Ci.nsIWritablePropertyBag);
|
||||
propBag.setProperty("timeLastUsed", Date.now());
|
||||
propBag.setProperty("timesUsedIncrement", 1);
|
||||
this.modifyLogin(existingLogin, propBag);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -380,11 +380,6 @@ LoginManagerPrompter.prototype = {
|
||||
return ok;
|
||||
}
|
||||
|
||||
var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
newLogin.init(hostname, null, realm, aUsername.value, aPassword.value,
|
||||
"", "");
|
||||
|
||||
// XXX We can't prompt with multiple logins yet (bug 227632), so
|
||||
// the entered login might correspond to an existing login
|
||||
// other than the one we originally selected.
|
||||
@ -395,13 +390,18 @@ LoginManagerPrompter.prototype = {
|
||||
if (!selectedLogin) {
|
||||
// add as new
|
||||
this.log("New login seen for " + realm);
|
||||
var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
newLogin.init(hostname, null, realm,
|
||||
aUsername.value, aPassword.value, "", "");
|
||||
this._pwmgr.addLogin(newLogin);
|
||||
} else if (aPassword.value != selectedLogin.password) {
|
||||
// update password
|
||||
this.log("Updating password for " + realm);
|
||||
this._pwmgr.modifyLogin(selectedLogin, newLogin);
|
||||
this._updateLogin(selectedLogin, aPassword.value);
|
||||
} else {
|
||||
this.log("Login unchanged, no further action needed.");
|
||||
this._updateLogin(selectedLogin);
|
||||
}
|
||||
|
||||
return ok;
|
||||
@ -589,11 +589,6 @@ LoginManagerPrompter.prototype = {
|
||||
return ok;
|
||||
}
|
||||
|
||||
var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
newLogin.init(hostname, null, httpRealm,
|
||||
username, password, "", "");
|
||||
|
||||
// XXX We can't prompt with multiple logins yet (bug 227632), so
|
||||
// the entered login might correspond to an existing login
|
||||
// other than the one we originally selected.
|
||||
@ -602,9 +597,13 @@ LoginManagerPrompter.prototype = {
|
||||
// If we didn't find an existing login, or if the username
|
||||
// changed, save as a new login.
|
||||
if (!selectedLogin) {
|
||||
// add as new
|
||||
this.log("New login seen for " + username +
|
||||
" @ " + hostname + " (" + httpRealm + ")");
|
||||
|
||||
var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
newLogin.init(hostname, null, httpRealm,
|
||||
username, password, "", "");
|
||||
if (notifyBox)
|
||||
this._showSaveLoginNotification(notifyBox, newLogin);
|
||||
else
|
||||
@ -616,12 +615,13 @@ LoginManagerPrompter.prototype = {
|
||||
" @ " + hostname + " (" + httpRealm + ")");
|
||||
if (notifyBox)
|
||||
this._showChangeLoginNotification(notifyBox,
|
||||
selectedLogin, newLogin);
|
||||
selectedLogin, password);
|
||||
else
|
||||
this._pwmgr.modifyLogin(selectedLogin, newLogin);
|
||||
this._updateLogin(selectedLogin, password);
|
||||
|
||||
} else {
|
||||
this.log("Login unchanged, no further action needed.");
|
||||
this._updateLogin(selectedLogin);
|
||||
}
|
||||
} catch (e) {
|
||||
Components.utils.reportError("LoginManagerPrompter: " +
|
||||
@ -923,9 +923,9 @@ LoginManagerPrompter.prototype = {
|
||||
var notifyBox = this._getNotifyBox();
|
||||
|
||||
if (notifyBox)
|
||||
this._showChangeLoginNotification(notifyBox, aOldLogin, aNewLogin);
|
||||
this._showChangeLoginNotification(notifyBox, aOldLogin, aNewLogin.password);
|
||||
else
|
||||
this._showChangeLoginDialog(aOldLogin, aNewLogin);
|
||||
this._showChangeLoginDialog(aOldLogin, aNewLogin.password);
|
||||
},
|
||||
|
||||
|
||||
@ -935,7 +935,7 @@ LoginManagerPrompter.prototype = {
|
||||
* Shows the Change Password notification bar.
|
||||
*
|
||||
*/
|
||||
_showChangeLoginNotification : function (aNotifyBox, aOldLogin, aNewLogin) {
|
||||
_showChangeLoginNotification : function (aNotifyBox, aOldLogin, aNewPassword) {
|
||||
var notificationText;
|
||||
if (aOldLogin.username)
|
||||
notificationText = this._getLocalizedString(
|
||||
@ -957,7 +957,7 @@ LoginManagerPrompter.prototype = {
|
||||
// The callbacks in |buttons| have a closure to access the variables
|
||||
// in scope here; set one to |this._pwmgr| so we can get back to pwmgr
|
||||
// without a getService() call.
|
||||
var pwmgr = this._pwmgr;
|
||||
var self = this;
|
||||
|
||||
var buttons = [
|
||||
// "Yes" button
|
||||
@ -966,7 +966,7 @@ LoginManagerPrompter.prototype = {
|
||||
accessKey: changeButtonAccessKey,
|
||||
popup: null,
|
||||
callback: function(aNotificationBar, aButton) {
|
||||
pwmgr.modifyLogin(aOldLogin, aNewLogin);
|
||||
self._updateLogin(aOldLogin, aNewPassword);
|
||||
}
|
||||
},
|
||||
|
||||
@ -992,7 +992,7 @@ LoginManagerPrompter.prototype = {
|
||||
* Shows the Change Password dialog.
|
||||
*
|
||||
*/
|
||||
_showChangeLoginDialog : function (aOldLogin, aNewLogin) {
|
||||
_showChangeLoginDialog : function (aOldLogin, aNewPassword) {
|
||||
const buttonFlags = Ci.nsIPrompt.STD_YES_NO_BUTTONS;
|
||||
|
||||
var dialogText;
|
||||
@ -1014,7 +1014,7 @@ LoginManagerPrompter.prototype = {
|
||||
null, {});
|
||||
if (ok) {
|
||||
this.log("Updating password for user " + aOldLogin.username);
|
||||
this._pwmgr.modifyLogin(aOldLogin, aNewLogin);
|
||||
this._updateLogin(aOldLogin, aNewPassword);
|
||||
}
|
||||
},
|
||||
|
||||
@ -1047,17 +1047,10 @@ LoginManagerPrompter.prototype = {
|
||||
usernames.length, usernames,
|
||||
selectedIndex);
|
||||
if (ok) {
|
||||
// Now that we know which login to change the password for,
|
||||
// update the missing username info in the aNewLogin.
|
||||
|
||||
// Now that we know which login to use, modify its password.
|
||||
var selectedLogin = logins[selectedIndex.value];
|
||||
|
||||
this.log("Updating password for user " + selectedLogin.username);
|
||||
|
||||
aNewLogin.username = selectedLogin.username;
|
||||
aNewLogin.usernameField = selectedLogin.usernameField;
|
||||
|
||||
this._pwmgr.modifyLogin(selectedLogin, aNewLogin);
|
||||
this._updateLogin(selectedLogin, aNewLogin.password);
|
||||
}
|
||||
},
|
||||
|
||||
@ -1069,6 +1062,25 @@ LoginManagerPrompter.prototype = {
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* _updateLogin
|
||||
*/
|
||||
_updateLogin : function (login, newPassword) {
|
||||
var now = Date.now();
|
||||
var propBag = Cc["@mozilla.org/hash-property-bag;1"].
|
||||
createInstance(Ci.nsIWritablePropertyBag);
|
||||
if (newPassword) {
|
||||
propBag.setProperty("password", newPassword);
|
||||
// Explicitly set the password change time here (even though it would
|
||||
// be changed automatically), to ensure that it's exactly the same
|
||||
// value as timeLastUsed.
|
||||
propBag.setProperty("timePasswordChanged", now);
|
||||
}
|
||||
propBag.setProperty("timeLastUsed", now);
|
||||
propBag.setProperty("timesUsedIncrement", 1);
|
||||
this._pwmgr.modifyLogin(login, propBag);
|
||||
},
|
||||
|
||||
/*
|
||||
* _getNotifyBox
|
||||
*
|
||||
|
@ -42,7 +42,7 @@ const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
|
||||
const DB_VERSION = 3; // The database schema version
|
||||
const DB_VERSION = 4; // The database schema version
|
||||
|
||||
const ENCTYPE_BASE64 = 0;
|
||||
const ENCTYPE_SDR = 1;
|
||||
@ -94,18 +94,23 @@ LoginManagerStorage_mozStorage.prototype = {
|
||||
// The current database schema.
|
||||
_dbSchema: {
|
||||
tables: {
|
||||
moz_logins: "id INTEGER PRIMARY KEY," +
|
||||
"hostname TEXT NOT NULL," +
|
||||
"httpRealm TEXT," +
|
||||
"formSubmitURL TEXT," +
|
||||
"usernameField TEXT NOT NULL," +
|
||||
"passwordField TEXT NOT NULL," +
|
||||
"encryptedUsername TEXT NOT NULL," +
|
||||
"encryptedPassword TEXT NOT NULL," +
|
||||
"guid TEXT," +
|
||||
"encType INTEGER",
|
||||
// Changes must be reflected in this._dbAreExpectedColumnsPresent
|
||||
// and this._searchLogins
|
||||
moz_logins: "id INTEGER PRIMARY KEY," +
|
||||
"hostname TEXT NOT NULL," +
|
||||
"httpRealm TEXT," +
|
||||
"formSubmitURL TEXT," +
|
||||
"usernameField TEXT NOT NULL," +
|
||||
"passwordField TEXT NOT NULL," +
|
||||
"encryptedUsername TEXT NOT NULL," +
|
||||
"encryptedPassword TEXT NOT NULL," +
|
||||
"guid TEXT," +
|
||||
"encType INTEGER," +
|
||||
"timeCreated INTEGER," +
|
||||
"timeLastUsed INTEGER," +
|
||||
"timePasswordChanged INTEGER," +
|
||||
"timesUsed INTEGER",
|
||||
// Changes must be reflected in this._dbAreExpectedColumnsPresent(),
|
||||
// this._searchLogins(), and this.modifyLogin().
|
||||
|
||||
moz_disabledHosts: "id INTEGER PRIMARY KEY," +
|
||||
"hostname TEXT UNIQUE ON CONFLICT REPLACE",
|
||||
},
|
||||
@ -259,25 +264,42 @@ LoginManagerStorage_mozStorage.prototype = {
|
||||
(encUsername.charAt(0) == '~' || encPassword.charAt(0) == '~'))
|
||||
encType = ENCTYPE_BASE64;
|
||||
|
||||
// Set timestamps
|
||||
let currentTime = Date.now();
|
||||
if (!loginClone.timeCreated)
|
||||
loginClone.timeCreated = currentTime;
|
||||
if (!loginClone.timeLastUsed)
|
||||
loginClone.timeLastUsed = currentTime;
|
||||
if (!loginClone.timePasswordChanged)
|
||||
loginClone.timePasswordChanged = currentTime;
|
||||
if (!loginClone.timesUsed)
|
||||
loginClone.timesUsed = 1;
|
||||
|
||||
let query =
|
||||
"INSERT INTO moz_logins " +
|
||||
"(hostname, httpRealm, formSubmitURL, usernameField, " +
|
||||
"passwordField, encryptedUsername, encryptedPassword, " +
|
||||
"guid, encType) " +
|
||||
"guid, encType, timeCreated, timeLastUsed, timePasswordChanged, " +
|
||||
"timesUsed) " +
|
||||
"VALUES (:hostname, :httpRealm, :formSubmitURL, :usernameField, " +
|
||||
":passwordField, :encryptedUsername, :encryptedPassword, " +
|
||||
":guid, :encType)";
|
||||
":guid, :encType, :timeCreated, :timeLastUsed, " +
|
||||
":timePasswordChanged, :timesUsed)";
|
||||
|
||||
let params = {
|
||||
hostname: loginClone.hostname,
|
||||
httpRealm: loginClone.httpRealm,
|
||||
formSubmitURL: loginClone.formSubmitURL,
|
||||
usernameField: loginClone.usernameField,
|
||||
passwordField: loginClone.passwordField,
|
||||
encryptedUsername: encUsername,
|
||||
encryptedPassword: encPassword,
|
||||
guid: loginClone.guid,
|
||||
encType: encType
|
||||
hostname: loginClone.hostname,
|
||||
httpRealm: loginClone.httpRealm,
|
||||
formSubmitURL: loginClone.formSubmitURL,
|
||||
usernameField: loginClone.usernameField,
|
||||
passwordField: loginClone.passwordField,
|
||||
encryptedUsername: encUsername,
|
||||
encryptedPassword: encPassword,
|
||||
guid: loginClone.guid,
|
||||
encType: encType,
|
||||
timeCreated: loginClone.timeCreated,
|
||||
timeLastUsed: loginClone.timeLastUsed,
|
||||
timePasswordChanged: loginClone.timePasswordChanged,
|
||||
timesUsed: loginClone.timesUsed
|
||||
};
|
||||
|
||||
let stmt;
|
||||
@ -344,11 +366,33 @@ LoginManagerStorage_mozStorage.prototype = {
|
||||
newLoginData.username, newLoginData.password,
|
||||
newLoginData.usernameField, newLoginData.passwordField);
|
||||
newLogin.QueryInterface(Ci.nsILoginMetaInfo);
|
||||
|
||||
// Automatically update metainfo when password is changed.
|
||||
if (newLogin.password != oldLogin.password)
|
||||
newLogin.timePasswordChanged = Date.now();
|
||||
} else if (newLoginData instanceof Ci.nsIPropertyBag) {
|
||||
function _bagHasProperty(aPropName) {
|
||||
try {
|
||||
newLoginData.getProperty(aPropName);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Clone the existing login, along with all its properties.
|
||||
newLogin = oldStoredLogin.clone();
|
||||
newLogin.QueryInterface(Ci.nsILoginMetaInfo);
|
||||
|
||||
// Automatically update metainfo when password is changed.
|
||||
// (Done before the main property updates, lest the caller be
|
||||
// explicitly updating both .password and .timePasswordChanged)
|
||||
if (_bagHasProperty("password")) {
|
||||
let newPassword = newLoginData.getProperty("password");
|
||||
if (newPassword != oldLogin.password)
|
||||
newLogin.timePasswordChanged = Date.now();
|
||||
}
|
||||
|
||||
let propEnum = newLoginData.enumerator;
|
||||
while (propEnum.hasMoreElements()) {
|
||||
let prop = propEnum.getNext().QueryInterface(Ci.nsIProperty);
|
||||
@ -361,16 +405,22 @@ LoginManagerStorage_mozStorage.prototype = {
|
||||
case "password":
|
||||
case "usernameField":
|
||||
case "passwordField":
|
||||
newLogin[prop.name] = prop.value;
|
||||
break;
|
||||
|
||||
// nsILoginMetaInfo properties...
|
||||
case "guid":
|
||||
newLogin.guid = prop.value;
|
||||
if (!this._isGuidUnique(newLogin.guid))
|
||||
case "timeCreated":
|
||||
case "timeLastUsed":
|
||||
case "timePasswordChanged":
|
||||
case "timesUsed":
|
||||
newLogin[prop.name] = prop.value;
|
||||
if (prop.name == "guid" && !this._isGuidUnique(newLogin.guid))
|
||||
throw "specified GUID already exists";
|
||||
break;
|
||||
|
||||
// Fake property, allows easy incrementing.
|
||||
case "timesUsedIncrement":
|
||||
newLogin.timesUsed += prop.value;
|
||||
break;
|
||||
|
||||
// Fail if caller requests setting an unknown property.
|
||||
default:
|
||||
throw "Unexpected propertybag item: " + prop.name;
|
||||
@ -396,20 +446,28 @@ LoginManagerStorage_mozStorage.prototype = {
|
||||
"encryptedUsername = :encryptedUsername, " +
|
||||
"encryptedPassword = :encryptedPassword, " +
|
||||
"guid = :guid, " +
|
||||
"encType = :encType " +
|
||||
"encType = :encType, " +
|
||||
"timeCreated = :timeCreated, " +
|
||||
"timeLastUsed = :timeLastUsed, " +
|
||||
"timePasswordChanged = :timePasswordChanged, " +
|
||||
"timesUsed = :timesUsed " +
|
||||
"WHERE id = :id";
|
||||
|
||||
let params = {
|
||||
id: idToModify,
|
||||
hostname: newLogin.hostname,
|
||||
httpRealm: newLogin.httpRealm,
|
||||
formSubmitURL: newLogin.formSubmitURL,
|
||||
usernameField: newLogin.usernameField,
|
||||
passwordField: newLogin.passwordField,
|
||||
encryptedUsername: encUsername,
|
||||
encryptedPassword: encPassword,
|
||||
guid: newLogin.guid,
|
||||
encType: ENCTYPE_SDR
|
||||
id: idToModify,
|
||||
hostname: newLogin.hostname,
|
||||
httpRealm: newLogin.httpRealm,
|
||||
formSubmitURL: newLogin.formSubmitURL,
|
||||
usernameField: newLogin.usernameField,
|
||||
passwordField: newLogin.passwordField,
|
||||
encryptedUsername: encUsername,
|
||||
encryptedPassword: encPassword,
|
||||
guid: newLogin.guid,
|
||||
encType: ENCTYPE_SDR,
|
||||
timeCreated: newLogin.timeCreated,
|
||||
timeLastUsed: newLogin.timeLastUsed,
|
||||
timePasswordChanged: newLogin.timePasswordChanged,
|
||||
timesUsed: newLogin.timesUsed
|
||||
};
|
||||
|
||||
let stmt;
|
||||
@ -518,6 +576,10 @@ LoginManagerStorage_mozStorage.prototype = {
|
||||
case "encryptedPassword":
|
||||
case "guid":
|
||||
case "encType":
|
||||
case "timeCreated":
|
||||
case "timeLastUsed":
|
||||
case "timePasswordChanged":
|
||||
case "timesUsed":
|
||||
if (value == null) {
|
||||
conditions.push(field + " isnull");
|
||||
} else {
|
||||
@ -554,6 +616,10 @@ LoginManagerStorage_mozStorage.prototype = {
|
||||
// set nsILoginMetaInfo values
|
||||
login.QueryInterface(Ci.nsILoginMetaInfo);
|
||||
login.guid = stmt.row.guid;
|
||||
login.timeCreated = stmt.row.timeCreated;
|
||||
login.timeLastUsed = stmt.row.timeLastUsed;
|
||||
login.timePasswordChanged = stmt.row.timePasswordChanged;
|
||||
login.timesUsed = stmt.row.timesUsed;
|
||||
logins.push(login);
|
||||
ids.push(stmt.row.id);
|
||||
}
|
||||
@ -1247,25 +1313,14 @@ LoginManagerStorage_mozStorage.prototype = {
|
||||
* Version 2 adds a GUID column. Existing logins are assigned a random GUID.
|
||||
*/
|
||||
_dbMigrateToVersion2 : function () {
|
||||
// Check to see if GUID column already exists.
|
||||
let exists = true;
|
||||
try {
|
||||
let stmt = this._dbConnection.createStatement(
|
||||
"SELECT guid FROM moz_logins");
|
||||
// (no need to execute statement, if it compiled we're good)
|
||||
stmt.finalize();
|
||||
} catch (e) {
|
||||
exists = false;
|
||||
}
|
||||
// Check to see if GUID column already exists, add if needed
|
||||
let query;
|
||||
if (!this._dbColumnExists("guid")) {
|
||||
query = "ALTER TABLE moz_logins ADD COLUMN guid TEXT";
|
||||
this._dbConnection.executeSimpleSQL(query);
|
||||
|
||||
// Add the new column and index only if needed.
|
||||
if (!exists) {
|
||||
this._dbConnection.executeSimpleSQL(
|
||||
"ALTER TABLE moz_logins ADD COLUMN guid TEXT");
|
||||
|
||||
this._dbConnection.executeSimpleSQL(
|
||||
"CREATE INDEX IF NOT EXISTS " +
|
||||
"moz_logins_guid_index ON moz_logins (guid)");
|
||||
query = "CREATE INDEX IF NOT EXISTS moz_logins_guid_index ON moz_logins (guid)";
|
||||
this._dbConnection.executeSimpleSQL(query);
|
||||
}
|
||||
|
||||
// Get a list of IDs for existing logins
|
||||
@ -1310,20 +1365,9 @@ LoginManagerStorage_mozStorage.prototype = {
|
||||
* Version 3 adds a encType column.
|
||||
*/
|
||||
_dbMigrateToVersion3 : function () {
|
||||
// Check to see if encType column already exists.
|
||||
let exists = true;
|
||||
let query = "SELECT encType FROM moz_logins";
|
||||
let stmt;
|
||||
try {
|
||||
stmt = this._dbConnection.createStatement(query);
|
||||
// (no need to execute statement, if it compiled we're good)
|
||||
stmt.finalize();
|
||||
} catch (e) {
|
||||
exists = false;
|
||||
}
|
||||
|
||||
// Add the new column and index only if needed.
|
||||
if (!exists) {
|
||||
// Check to see if encType column already exists, add if needed
|
||||
let query;
|
||||
if (!this._dbColumnExists("encType")) {
|
||||
query = "ALTER TABLE moz_logins ADD COLUMN encType INTEGER";
|
||||
this._dbConnection.executeSimpleSQL(query);
|
||||
|
||||
@ -1371,6 +1415,59 @@ LoginManagerStorage_mozStorage.prototype = {
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* _dbMigrateToVersion4
|
||||
*
|
||||
* Version 4 adds timeCreated, timeLastUsed, timePasswordChanged,
|
||||
* and timesUsed columns
|
||||
*/
|
||||
_dbMigrateToVersion4 : function () {
|
||||
let query;
|
||||
// Add the new columns, if needed.
|
||||
for each (let column in ["timeCreated", "timeLastUsed", "timePasswordChanged", "timesUsed"]) {
|
||||
if (!this._dbColumnExists(column)) {
|
||||
query = "ALTER TABLE moz_logins ADD COLUMN " + column + " INTEGER";
|
||||
this._dbConnection.executeSimpleSQL(query);
|
||||
}
|
||||
}
|
||||
|
||||
// Get a list of IDs for existing logins.
|
||||
let ids = [];
|
||||
query = "SELECT id FROM moz_logins WHERE timeCreated isnull OR " +
|
||||
"timeLastUsed isnull OR timePasswordChanged isnull OR timesUsed isnull";
|
||||
try {
|
||||
stmt = this._dbCreateStatement(query);
|
||||
while (stmt.executeStep())
|
||||
ids.push(stmt.row.id);
|
||||
} catch (e) {
|
||||
this.log("Failed getting IDs: " + e);
|
||||
throw e;
|
||||
} finally {
|
||||
stmt.reset();
|
||||
}
|
||||
|
||||
// Initialize logins with current time.
|
||||
query = "UPDATE moz_logins SET timeCreated = :initTime, timeLastUsed = :initTime, " +
|
||||
"timePasswordChanged = :initTime, timesUsed = 1 WHERE id = :id";
|
||||
let params = {
|
||||
id: null,
|
||||
initTime: Date.now()
|
||||
};
|
||||
for each (let id in ids) {
|
||||
params.id = id;
|
||||
try {
|
||||
stmt = this._dbCreateStatement(query, params);
|
||||
stmt.execute();
|
||||
} catch (e) {
|
||||
this.log("Failed setting timestamps: " + e);
|
||||
throw e;
|
||||
} finally {
|
||||
stmt.reset();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* _dbAreExpectedColumnsPresent
|
||||
*
|
||||
@ -1388,7 +1485,11 @@ LoginManagerStorage_mozStorage.prototype = {
|
||||
"encryptedUsername, " +
|
||||
"encryptedPassword, " +
|
||||
"guid, " +
|
||||
"encType " +
|
||||
"encType, " +
|
||||
"timeCreated, " +
|
||||
"timeLastUsed, " +
|
||||
"timePasswordChanged, " +
|
||||
"timesUsed " +
|
||||
"FROM moz_logins";
|
||||
try {
|
||||
let stmt = this._dbConnection.createStatement(query);
|
||||
@ -1415,6 +1516,24 @@ LoginManagerStorage_mozStorage.prototype = {
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* _dbColumnExists
|
||||
*
|
||||
* Checks to see if the named column already exists.
|
||||
*/
|
||||
_dbColumnExists : function (columnName) {
|
||||
let query = "SELECT " + columnName + " FROM moz_logins";
|
||||
try {
|
||||
let stmt = this._dbConnection.createStatement(query);
|
||||
// (no need to execute statement, if it compiled we're good)
|
||||
stmt.finalize();
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* _dbCleanup
|
||||
*
|
||||
|
@ -135,6 +135,11 @@ function checkTest() {
|
||||
is(gotPass, "notifyp1", "Checking submitted password");
|
||||
bar = getNotificationBar(notifyBox, "password-save");
|
||||
ok(bar, "got notification bar");
|
||||
|
||||
// Sanity check, no logins should exist yet.
|
||||
var logins = pwmgr.getAllLogins();
|
||||
is(logins.length, 0, "Should not have any logins yet");
|
||||
|
||||
clickNotificationButton(bar, kRememberButton);
|
||||
break;
|
||||
|
||||
@ -144,6 +149,16 @@ function checkTest() {
|
||||
is(gotPass, "notifyp1", "Checking submitted password");
|
||||
bar = getNotificationBar(notifyBox, "password-save");
|
||||
ok(!bar, "checking for no notification bar");
|
||||
|
||||
// Check to make sure we updated the timestamps and use count on the
|
||||
// existing loging that was submitted for this form.
|
||||
var logins = pwmgr.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login");
|
||||
ok(logins[0] instanceof Ci.nsILoginMetaInfo, "metainfo QI");
|
||||
is(logins[0].timesUsed, 2, "check .timesUsed for existing login submission");
|
||||
ok(logins[0].timeLastUsed > logins[0].timeCreated, "timeLastUsed bumped");
|
||||
ok(logins[0].timeCreated == logins[0].timePasswordChanged, "timeChanged not updated");
|
||||
|
||||
// remove that login
|
||||
pwmgr.removeLogin(login1);
|
||||
break;
|
||||
@ -286,6 +301,15 @@ function checkTest() {
|
||||
ok(bar, "got notification bar");
|
||||
clickNotificationButton(bar, kChangeButton);
|
||||
|
||||
// Check to make sure we updated the timestamps and use count for
|
||||
// the login being changed with this form.
|
||||
var logins = pwmgr.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login");
|
||||
ok(logins[0] instanceof Ci.nsILoginMetaInfo, "metainfo QI");
|
||||
is(logins[0].timesUsed, 2, "check .timesUsed incremented on change");
|
||||
ok(logins[0].timeCreated < logins[0].timeLastUsed, "timeLastUsed bumped");
|
||||
ok(logins[0].timeLastUsed == logins[0].timePasswordChanged, "timeUsed == timeChanged");
|
||||
|
||||
// cleanup
|
||||
login1.password = "pass2";
|
||||
pwmgr.removeLogin(login1);
|
||||
@ -379,6 +403,8 @@ var pwmgr = Cc["@mozilla.org/login-manager;1"].
|
||||
getService(Ci.nsILoginManager);
|
||||
ok(pwmgr != null, "Access pwmgr");
|
||||
|
||||
pwmgr.removeAllLogins();
|
||||
|
||||
var prefs = Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Ci.nsIPrefService);
|
||||
ok(prefs != null, "Access prefs");
|
||||
|
BIN
toolkit/components/passwordmgr/test/unit/data/signons-v3.sqlite
Normal file
BIN
toolkit/components/passwordmgr/test/unit/data/signons-v3.sqlite
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -237,6 +237,13 @@ const LoginTest = {
|
||||
profileFile.remove(false);
|
||||
|
||||
file.copyTo(PROFDIR, filename);
|
||||
},
|
||||
|
||||
// Returns true if the timestamp is within 30 seconds of now.
|
||||
is_about_now : function (timestamp) {
|
||||
var delta = Math.abs(timestamp - Date.now());
|
||||
var seconds = 30 * 1000;
|
||||
return delta < seconds;
|
||||
}
|
||||
};
|
||||
|
@ -254,6 +254,157 @@ storage.removeLogin(wonkyLogin);
|
||||
LoginTest.checkStorageData(storage, [], [testuser1, testuser2, testuser3]);
|
||||
|
||||
|
||||
/* ========== 12 ========== */
|
||||
testnum++;
|
||||
testdesc = "check values for v4 DB addLogin";
|
||||
|
||||
var timeuser1 = new nsLoginInfo();
|
||||
timeuser1.init("http://time1", "", null, "timeuser1", "origpass1", "foo", "bar");
|
||||
|
||||
storage.addLogin(timeuser1);
|
||||
LoginTest.checkStorageData(storage, [], [testuser1, testuser2, testuser3, timeuser1]);
|
||||
|
||||
let matchData = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag);
|
||||
matchData.setProperty("hostname", "http://time1");
|
||||
logins = storage.searchLogins({}, matchData);
|
||||
do_check_eq(1, logins.length);
|
||||
|
||||
let tu1 = logins[0];
|
||||
tu1.QueryInterface(Ci.nsILoginMetaInfo);
|
||||
let time1 = tu1.timeCreated;
|
||||
|
||||
// Check that times and usage count were initialized properly.
|
||||
do_check_true(LoginTest.is_about_now(time1));
|
||||
do_check_eq(time1, tu1.timeLastUsed);
|
||||
do_check_eq(time1, tu1.timePasswordChanged);
|
||||
do_check_eq(1, tu1.timesUsed);
|
||||
|
||||
|
||||
/* ========== 13 ========== */
|
||||
testnum++;
|
||||
testdesc = "check values for v4 DB addLogin part 2";
|
||||
|
||||
var timeuser2 = new nsLoginInfo();
|
||||
timeuser2.init("http://time2", "", null, "timeuser2", "origpass2", "foo", "bar");
|
||||
timeuser2.QueryInterface(Ci.nsILoginMetaInfo);
|
||||
// This time pre-init the times and usage count.
|
||||
timeuser2.timeCreated = 123;
|
||||
timeuser2.timeLastUsed = 456;
|
||||
timeuser2.timePasswordChanged = 789;
|
||||
timeuser2.timesUsed = 42;
|
||||
|
||||
storage.addLogin(timeuser2);
|
||||
LoginTest.checkStorageData(storage, [], [testuser1, testuser2, testuser3, timeuser1, timeuser2]);
|
||||
|
||||
matchData = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag);
|
||||
matchData.setProperty("hostname", "http://time2");
|
||||
logins = storage.searchLogins({}, matchData);
|
||||
do_check_eq(1, logins.length);
|
||||
let tu2 = logins[0];
|
||||
|
||||
tu2.QueryInterface(Ci.nsILoginMetaInfo);
|
||||
|
||||
// Check that values we specified were set, instead of defaults.
|
||||
do_check_eq(123, tu2.timeCreated);
|
||||
do_check_eq(456, tu2.timeLastUsed);
|
||||
do_check_eq(789, tu2.timePasswordChanged);
|
||||
do_check_eq(42, tu2.timesUsed);
|
||||
|
||||
|
||||
/* ========== 14 ========== */
|
||||
testnum++;
|
||||
testdesc = "check values for v4 DB modifyLogin";
|
||||
|
||||
let modData = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag);
|
||||
modData.setProperty("timesUsed", 8);
|
||||
|
||||
storage.modifyLogin(timeuser2, modData);
|
||||
|
||||
modData = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag);
|
||||
modData.setProperty("password", "newpass2");
|
||||
|
||||
storage.modifyLogin(timeuser2, modData);
|
||||
timeuser2.password = "newpass2";
|
||||
|
||||
matchData = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag);
|
||||
matchData.setProperty("hostname", "http://time2");
|
||||
logins = storage.searchLogins({}, matchData);
|
||||
do_check_eq(1, logins.length);
|
||||
tu2 = logins[0];
|
||||
|
||||
tu2.QueryInterface(Ci.nsILoginMetaInfo);
|
||||
|
||||
// Check that values we specified were set, instead of defaults.
|
||||
do_check_eq("newpass2", tu2.password);
|
||||
do_check_eq(123, tu2.timeCreated);
|
||||
do_check_eq(456, tu2.timeLastUsed);
|
||||
do_check_true(LoginTest.is_about_now(tu2.timePasswordChanged));
|
||||
do_check_eq(8, tu2.timesUsed);
|
||||
|
||||
|
||||
/* ========== 15 ========== */
|
||||
testnum++;
|
||||
testdesc = "check values for v4 DB modifyLogin part 2";
|
||||
|
||||
modData = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag);
|
||||
modData.setProperty("timesUsedIncrement", 2);
|
||||
|
||||
storage.modifyLogin(timeuser2, modData);
|
||||
|
||||
modData = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag);
|
||||
modData.setProperty("password", "newpass2again\u0394\u00e8");
|
||||
modData.setProperty("timePasswordChanged", 888); // pw change with specific time
|
||||
|
||||
storage.modifyLogin(timeuser2, modData);
|
||||
timeuser2.password = "newpass2again\u0394\u00e8";
|
||||
|
||||
matchData = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag);
|
||||
matchData.setProperty("hostname", "http://time2");
|
||||
logins = storage.searchLogins({}, matchData);
|
||||
do_check_eq(1, logins.length);
|
||||
tu2 = logins[0];
|
||||
|
||||
tu2.QueryInterface(Ci.nsILoginMetaInfo);
|
||||
|
||||
// Check that values we specified were set, instead of defaults.
|
||||
do_check_eq("newpass2again\u0394\u00e8", tu2.password);
|
||||
do_check_eq(123, tu2.timeCreated);
|
||||
do_check_eq(456, tu2.timeLastUsed);
|
||||
do_check_eq(888, tu2.timePasswordChanged);
|
||||
do_check_eq(10, tu2.timesUsed);
|
||||
|
||||
|
||||
/* ========== 16 ========== */
|
||||
testnum++;
|
||||
testdesc = "check values for v4 DB modifyLogin part 3";
|
||||
|
||||
modData = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag);
|
||||
modData.setProperty("timeCreated", 5444333222111);
|
||||
modData.setProperty("timeLastUsed", 22222);
|
||||
|
||||
storage.modifyLogin(timeuser2, modData);
|
||||
|
||||
matchData = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag);
|
||||
matchData.setProperty("hostname", "http://time2");
|
||||
logins = storage.searchLogins({}, matchData);
|
||||
do_check_eq(1, logins.length);
|
||||
tu2 = logins[0];
|
||||
|
||||
tu2.QueryInterface(Ci.nsILoginMetaInfo);
|
||||
|
||||
// Check that values we specified were set, instead of defaults.
|
||||
do_check_eq(5444333222111, tu2.timeCreated);
|
||||
do_check_eq(22222, tu2.timeLastUsed);
|
||||
do_check_eq(888, tu2.timePasswordChanged);
|
||||
do_check_eq(10, tu2.timesUsed);
|
||||
|
||||
// some cleanup
|
||||
storage.removeLogin(timeuser1);
|
||||
storage.removeLogin(timeuser2);
|
||||
LoginTest.checkStorageData(storage, [], [testuser1, testuser2, testuser3]);
|
||||
|
||||
|
||||
|
||||
LoginTest.deleteFile(OUTDIR, "signons-unittest6.sqlite");
|
||||
|
||||
} catch (e) {
|
||||
|
@ -14,7 +14,7 @@ const ENCTYPE_SDR = 1;
|
||||
|
||||
// Current schema version used by storage-mozStorage.js. This will need to be
|
||||
// kept in sync with the version there (or else the tests fail).
|
||||
const CURRENT_SCHEMA = 3;
|
||||
const CURRENT_SCHEMA = 4;
|
||||
|
||||
function run_test() {
|
||||
|
||||
@ -231,6 +231,69 @@ dbConnection.close();
|
||||
LoginTest.deleteFile(OUTDIR, "signons-v2v3.sqlite");
|
||||
|
||||
|
||||
/* ========== 7 ========== */
|
||||
testnum++;
|
||||
testdesc = "Test upgrade from v3->v4 storage"
|
||||
|
||||
LoginTest.copyFile("signons-v3.sqlite");
|
||||
// Sanity check the test file.
|
||||
dbConnection = LoginTest.openDB("signons-v3.sqlite");
|
||||
do_check_eq(3, dbConnection.schemaVersion);
|
||||
|
||||
storage = LoginTest.reloadStorage(OUTDIR, "signons-v3.sqlite");
|
||||
do_check_eq(CURRENT_SCHEMA, dbConnection.schemaVersion);
|
||||
|
||||
// Check that timestamps and counts were initialized correctly
|
||||
LoginTest.checkStorageData(storage, [], [testuser1, testuser2]);
|
||||
|
||||
var logins = storage.getAllLogins();
|
||||
for (var i = 0; i < 2; i++) {
|
||||
do_check_true(logins[i] instanceof Ci.nsILoginMetaInfo);
|
||||
do_check_eq(1, logins[i].timesUsed);
|
||||
do_check_true(LoginTest.is_about_now(logins[i].timeCreated));
|
||||
do_check_true(LoginTest.is_about_now(logins[i].timeLastUsed));
|
||||
do_check_true(LoginTest.is_about_now(logins[i].timePasswordChanged));
|
||||
}
|
||||
|
||||
/* ========== 8 ========== */
|
||||
testnum++;
|
||||
testdesc = "Test upgrade from v3->v4->v3 storage"
|
||||
|
||||
LoginTest.copyFile("signons-v3v4.sqlite");
|
||||
// Sanity check the test file.
|
||||
dbConnection = LoginTest.openDB("signons-v3v4.sqlite");
|
||||
do_check_eq(3, dbConnection.schemaVersion);
|
||||
|
||||
storage = LoginTest.reloadStorage(OUTDIR, "signons-v3v4.sqlite");
|
||||
do_check_eq(CURRENT_SCHEMA, dbConnection.schemaVersion);
|
||||
|
||||
// testuser1 already has timestamps, testuser2 does not.
|
||||
LoginTest.checkStorageData(storage, [], [testuser1, testuser2]);
|
||||
|
||||
var logins = storage.getAllLogins();
|
||||
|
||||
var t1, t2;
|
||||
if (logins[0].username == "testuser1") {
|
||||
t1 = logins[0];
|
||||
t2 = logins[1];
|
||||
} else {
|
||||
t1 = logins[1];
|
||||
t2 = logins[0];
|
||||
}
|
||||
|
||||
do_check_true(t1 instanceof Ci.nsILoginMetaInfo);
|
||||
do_check_true(t2 instanceof Ci.nsILoginMetaInfo);
|
||||
|
||||
do_check_eq(9, t1.timesUsed);
|
||||
do_check_eq(1262049951275, t1.timeCreated);
|
||||
do_check_eq(1262049951275, t1.timeLastUsed);
|
||||
do_check_eq(1262049951275, t1.timePasswordChanged);
|
||||
|
||||
do_check_eq(1, t2.timesUsed);
|
||||
do_check_true(LoginTest.is_about_now(t2.timeCreated));
|
||||
do_check_true(LoginTest.is_about_now(t2.timeLastUsed));
|
||||
do_check_true(LoginTest.is_about_now(t2.timePasswordChanged));
|
||||
|
||||
} catch (e) {
|
||||
throw "FAILED in test #" + testnum + " -- " + testdesc + ": " + e;
|
||||
}
|
||||
|
@ -292,6 +292,12 @@ function() {
|
||||
* Test adaptive autocomplete
|
||||
*/
|
||||
function run_test() {
|
||||
// always search in history + bookmarks, no matter what the default is
|
||||
var prefs = Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Ci.nsIPrefBranch);
|
||||
prefs.setIntPref("browser.urlbar.search.sources", 3);
|
||||
prefs.setIntPref("browser.urlbar.default.behavior", 0);
|
||||
|
||||
do_test_pending();
|
||||
next_test();
|
||||
}
|
||||
|
@ -740,14 +740,14 @@ function onToolbarDragOver(aEvent)
|
||||
toolbar = toolbar.parentNode;
|
||||
}
|
||||
|
||||
var previousDragItem = gCurrentDragOverItem;
|
||||
|
||||
// Make sure we are dragging over a customizable toolbar.
|
||||
if (!isCustomizableToolbar(toolbar)) {
|
||||
if (!toolbar || !isCustomizableToolbar(toolbar)) {
|
||||
gCurrentDragOverItem = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var previousDragItem = gCurrentDragOverItem;
|
||||
|
||||
if (dropTarget.localName == "toolbar") {
|
||||
gCurrentDragOverItem = dropTarget;
|
||||
} else {
|
||||
|
@ -694,6 +694,14 @@ textbox[multiline="true"] {
|
||||
-moz-binding: url("chrome://global/content/bindings/textbox.xml#input-box");
|
||||
}
|
||||
|
||||
html|textarea.textbox-textarea {
|
||||
-moz-resize: none;
|
||||
}
|
||||
|
||||
textbox[resizable="true"] > .textbox-input-box > html|textarea.textbox-textarea {
|
||||
-moz-resize: both;
|
||||
}
|
||||
|
||||
.textbox-input-box[spellcheck="true"] {
|
||||
-moz-binding: url("chrome://global/content/bindings/textbox.xml#input-box-spell");
|
||||
}
|
||||
|
@ -70,6 +70,8 @@ _CHROME_FILES = test_bug343416.xul \
|
||||
window_wheeltransaction.xul \
|
||||
test_imestate.html \
|
||||
test_plugin_scroll_consistency.html \
|
||||
test_composition_text_querycontent.xul \
|
||||
window_composition_text_querycontent.xul \
|
||||
$(NULL)
|
||||
|
||||
ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
|
||||
|
@ -143,7 +143,6 @@ protected:
|
||||
PRBool TestExtents(void);
|
||||
PRBool TestComposition(void);
|
||||
PRBool TestNotification(void);
|
||||
PRBool TestContentEvents(void);
|
||||
PRBool TestEditMessages(void);
|
||||
PRBool TestScrollMessages(void);
|
||||
|
||||
@ -1157,7 +1156,7 @@ public:
|
||||
PRInt32 mFocusCount;
|
||||
|
||||
TSFMgrImpl(TestApp* test) : mTestApp(test), mTest(nsnull), mRefCnt(0),
|
||||
mDeactivated(PR_FALSE), mFocusCount(0)
|
||||
mDeactivated(PR_FALSE), mFocusedDocument(nsnull), mFocusCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -1661,9 +1660,9 @@ TestApp::OnStateChange(nsIWebProgress *aWebProgress,
|
||||
NS_ASSERTION(aStateFlags & nsIWebProgressListener::STATE_IS_WINDOW &&
|
||||
aStateFlags & nsIWebProgressListener::STATE_STOP, "wrong state");
|
||||
if (NS_SUCCEEDED(Init())) {
|
||||
printf("Testing content events...\n");
|
||||
if (TestContentEvents())
|
||||
passed("TestContentEvents");
|
||||
mCurrentNode = mTextArea;
|
||||
mTextArea->Focus();
|
||||
|
||||
if (RunTest(&TestApp::TestEditMessages))
|
||||
passed("TestEditMessages");
|
||||
if (RunTest(&TestApp::TestScrollMessages))
|
||||
@ -2719,143 +2718,6 @@ TestApp::TestNotification(void)
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
PRBool
|
||||
TestApp::TestContentEvents(void)
|
||||
{
|
||||
mTestString = NS_LITERAL_STRING(
|
||||
"This is a test of the\r\nContent Events");
|
||||
// 0123456789012345678901 2 34567890123456
|
||||
// 0 1 2 3
|
||||
mTextArea->SetValue(mTestString);
|
||||
mTextArea->Focus();
|
||||
|
||||
nsCOMPtr<nsIWidget> widget;
|
||||
if (!GetWidget(getter_AddRefs(widget))) {
|
||||
fail("TestContentEvents: get nsIWidget");
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIWidget> topLevel = widget->GetTopLevelWidget();
|
||||
if (!topLevel) {
|
||||
fail("TestContentEvents: get top level widget");
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
nsIntRect widgetRect, topLevelRect;
|
||||
nsresult nsr = widget->GetScreenBounds(widgetRect);
|
||||
if (NS_FAILED(nsr)) {
|
||||
fail("TestContentEvents: get widget rect");
|
||||
return PR_FALSE;
|
||||
}
|
||||
nsr = topLevel->GetScreenBounds(topLevelRect);
|
||||
if (NS_FAILED(nsr)) {
|
||||
fail("TestContentEvents: get top level widget rect");
|
||||
return PR_FALSE;
|
||||
}
|
||||
nsIntPoint widgetOffset = widgetRect.TopLeft() - topLevelRect.TopLeft();
|
||||
nsEventStatus eventStatus;
|
||||
PRBool result = PR_TRUE;
|
||||
|
||||
const PRUint32 kNone = nsQueryContentEvent::NOT_FOUND;
|
||||
PRUint32 testingOffset[] = { 0, 10, 20, 23, 36 };
|
||||
PRUint32 leftSideOffset[] = { kNone, 9, 19, kNone, 35 };
|
||||
PRUint32 rightSideOffset[] = { 1, 11, kNone, 24, kNone };
|
||||
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(testingOffset); i++) {
|
||||
nsQueryContentEvent textRect(PR_TRUE, NS_QUERY_TEXT_RECT, widget);
|
||||
textRect.InitForQueryTextRect(testingOffset[i], 1);
|
||||
nsr = widget->DispatchEvent(&textRect, eventStatus);
|
||||
if (NS_FAILED(nsr) || !textRect.mSucceeded ||
|
||||
textRect.mReply.mRect.IsEmpty()) {
|
||||
fail("TestContentEvents: get text rect");
|
||||
return PR_FALSE;
|
||||
}
|
||||
nsIntRect &charRect = textRect.mReply.mRect;
|
||||
charRect.MoveBy(widgetOffset);
|
||||
// Note that charRect might be inflated at rounding to pixels!
|
||||
printf("TestContentEvents: testing... i=%lu, pt={ %ld, %ld }, size={ %ld, %ld }\n",
|
||||
i, charRect.x, charRect.y, charRect.width, charRect.height);
|
||||
|
||||
nsQueryContentEvent charAtPt1(PR_TRUE, NS_QUERY_CHARACTER_AT_POINT, widget);
|
||||
charAtPt1.refPoint.x = charRect.x + 1;
|
||||
charAtPt1.refPoint.y = charRect.y + 1;
|
||||
nsr = widget->DispatchEvent(&charAtPt1, eventStatus);
|
||||
if (NS_FAILED(nsr) || !charAtPt1.mSucceeded) {
|
||||
fail(" TestContentEvents: get char at point1");
|
||||
return PR_FALSE;
|
||||
}
|
||||
printf(" NS_QUERY_CHARACTER_AT_POINT: pt={ %ld, %ld }, offset=%lu, rect={ %ld, %ld, %ld, %ld }\n",
|
||||
charAtPt1.refPoint.x, charAtPt1.refPoint.y,
|
||||
charAtPt1.mReply.mOffset, charAtPt1.mReply.mRect.x,
|
||||
charAtPt1.mReply.mRect.y, charAtPt1.mReply.mRect.width,
|
||||
charAtPt1.mReply.mRect.height);
|
||||
if (charAtPt1.mReply.mOffset != testingOffset[i]) {
|
||||
fail(" TestContentEvents: get char at point1 (wrong offset)");
|
||||
result = PR_FALSE;
|
||||
} else if (charAtPt1.mReply.mRect != textRect.mReply.mRect) {
|
||||
fail(" TestContentEvents: get char at point1 (rect mismatch)");
|
||||
result = PR_FALSE;
|
||||
}
|
||||
|
||||
nsQueryContentEvent charAtPt2(PR_TRUE, NS_QUERY_CHARACTER_AT_POINT, widget);
|
||||
charAtPt2.refPoint.x = charRect.XMost() - 2;
|
||||
charAtPt2.refPoint.y = charRect.YMost() - 2;
|
||||
nsr = widget->DispatchEvent(&charAtPt2, eventStatus);
|
||||
if (NS_FAILED(nsr) || !charAtPt2.mSucceeded) {
|
||||
fail(" TestContentEvents: get char at point2");
|
||||
return PR_FALSE;
|
||||
}
|
||||
printf(" NS_QUERY_CHARACTER_AT_POINT: pt={ %ld, %ld }, offset=%lu, rect={ %ld, %ld, %ld, %ld }\n",
|
||||
charAtPt2.refPoint.x, charAtPt2.refPoint.y,
|
||||
charAtPt2.mReply.mOffset, charAtPt2.mReply.mRect.x,
|
||||
charAtPt2.mReply.mRect.y, charAtPt2.mReply.mRect.width,
|
||||
charAtPt2.mReply.mRect.height);
|
||||
if (charAtPt2.mReply.mOffset != testingOffset[i]) {
|
||||
fail(" TestContentEvents: get char at point2 (wrong offset)");
|
||||
result = PR_FALSE;
|
||||
} else if (charAtPt2.mReply.mRect != textRect.mReply.mRect) {
|
||||
fail(" TestContentEvents: get char at point2 (rect mismatch)");
|
||||
result = PR_FALSE;
|
||||
}
|
||||
|
||||
nsQueryContentEvent charAtPt3(PR_TRUE, NS_QUERY_CHARACTER_AT_POINT, widget);
|
||||
charAtPt3.refPoint.x = charRect.x - 2;
|
||||
charAtPt3.refPoint.y = charRect.y + 1;
|
||||
nsr = widget->DispatchEvent(&charAtPt3, eventStatus);
|
||||
if (NS_FAILED(nsr) || !charAtPt3.mSucceeded) {
|
||||
fail(" TestContentEvents: get char at point3");
|
||||
return PR_FALSE;
|
||||
}
|
||||
printf(" NS_QUERY_CHARACTER_AT_POINT: pt={ %ld, %ld }, offset=%lu, rect={ %ld, %ld, %ld, %ld }\n",
|
||||
charAtPt3.refPoint.x, charAtPt3.refPoint.y,
|
||||
charAtPt3.mReply.mOffset, charAtPt3.mReply.mRect.x,
|
||||
charAtPt3.mReply.mRect.y, charAtPt3.mReply.mRect.width,
|
||||
charAtPt3.mReply.mRect.height);
|
||||
if (charAtPt3.mReply.mOffset != leftSideOffset[i]) {
|
||||
fail(" TestContentEvents: get left side char at point (wrong offset)");
|
||||
result = PR_FALSE;
|
||||
}
|
||||
|
||||
nsQueryContentEvent charAtPt4(PR_TRUE, NS_QUERY_CHARACTER_AT_POINT, widget);
|
||||
charAtPt4.refPoint.x = charRect.XMost() + 1;
|
||||
charAtPt4.refPoint.y = charRect.YMost() - 2;
|
||||
nsr = widget->DispatchEvent(&charAtPt4, eventStatus);
|
||||
if (NS_FAILED(nsr) || !charAtPt4.mSucceeded) {
|
||||
fail(" TestContentEvents: get char at point4");
|
||||
return PR_FALSE;
|
||||
}
|
||||
printf(" NS_QUERY_CHARACTER_AT_POINT: pt={ %ld, %ld }, offset=%lu, rect={ %ld, %ld, %ld, %ld }\n",
|
||||
charAtPt4.refPoint.x, charAtPt4.refPoint.y,
|
||||
charAtPt4.mReply.mOffset, charAtPt4.mReply.mRect.x,
|
||||
charAtPt4.mReply.mRect.y, charAtPt4.mReply.mRect.width,
|
||||
charAtPt4.mReply.mRect.height);
|
||||
if (charAtPt4.mReply.mOffset != rightSideOffset[i]) {
|
||||
fail(" TestContentEvents: get right side char at point4 (wrong offset)");
|
||||
result = PR_FALSE;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
PRBool
|
||||
TestApp::TestEditMessages(void)
|
||||
{
|
||||
|
30
widget/tests/test_composition_text_querycontent.xul
Normal file
30
widget/tests/test_composition_text_querycontent.xul
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
|
||||
type="text/css"?>
|
||||
<window title="Testing composition, text and query content events"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/MochiKit/packed.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
|
||||
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
<script class="testbody" type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
window.open("window_composition_text_querycontent.xul", "_blank",
|
||||
"chrome,width=600,height=600");
|
||||
|
||||
]]>
|
||||
</script>
|
||||
</window>
|
986
widget/tests/window_composition_text_querycontent.xul
Normal file
986
widget/tests/window_composition_text_querycontent.xul
Normal file
@ -0,0 +1,986 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
|
||||
type="text/css"?>
|
||||
<window title="Testing composition, text and query content events"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onunload="onunload();">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" />
|
||||
|
||||
<panel id="panel" hidden="true"
|
||||
orient="vertical"
|
||||
onpopupshown="onPanelShown(event);"
|
||||
onpopuphidden="onPanelHidden(event);">
|
||||
<vbox id="vbox">
|
||||
<textbox id="textbox" onfocus="onFocusPanelTextbox(event);"
|
||||
multiline="true" cols="20" rows="4"/>
|
||||
</vbox>
|
||||
</panel>
|
||||
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<p id="display">
|
||||
<textarea id="textarea" cols="20" rows="4"></textarea>
|
||||
<iframe id="iframe" width="300" height="150"
|
||||
src="data:text/html,<textarea id='textarea' cols='20' rows='4'></textarea>"></iframe>
|
||||
</p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
<script class="testbody" type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
window.opener.wrappedJSObject.SimpleTest.waitForFocus(runTest, window);
|
||||
|
||||
function ok(aCondition, aMessage)
|
||||
{
|
||||
window.opener.wrappedJSObject.SimpleTest.ok(aCondition, aMessage);
|
||||
}
|
||||
|
||||
function is(aLeft, aRight, aMessage)
|
||||
{
|
||||
window.opener.wrappedJSObject.SimpleTest.is(aLeft, aRight, aMessage);
|
||||
}
|
||||
|
||||
function isnot(aLeft, aRight, aMessage)
|
||||
{
|
||||
window.opener.wrappedJSObject.SimpleTest.isnot(aLeft, aRight, aMessage);
|
||||
}
|
||||
|
||||
function finish()
|
||||
{
|
||||
window.close();
|
||||
}
|
||||
|
||||
function onunload()
|
||||
{
|
||||
window.opener.wrappedJSObject.SimpleTest.finish();
|
||||
}
|
||||
|
||||
var textarea = document.getElementById("textarea");
|
||||
var panel = document.getElementById("panel");
|
||||
var textbox = document.getElementById("textbox");
|
||||
var iframe = document.getElementById("iframe");
|
||||
var textareaInFrame;
|
||||
|
||||
const nsIDOMWindowUtils = Components.interfaces.nsIDOMWindowUtils;
|
||||
|
||||
const kIsWin = (navigator.platform.indexOf("Win") == 0);
|
||||
const kIsMac = (navigator.platform.indexOf("Mac") == 0);
|
||||
|
||||
function checkQueryContentResult(aResult, aMessage, aID)
|
||||
{
|
||||
ok(aResult, aMessage + ": the result is null");
|
||||
if (!aResult) {
|
||||
return false;
|
||||
}
|
||||
ok(aResult.succeeded, aMessage + ": the query content failed");
|
||||
return aResult.succeeded;
|
||||
}
|
||||
|
||||
function checkContent(aExpectedText, aMessage, aID)
|
||||
{
|
||||
var textContent = synthesizeQueryTextContent(0, 100);
|
||||
if (!checkQueryContentResult(textContent, aMessage +
|
||||
": synthesizeQueryTextContent " + aID)) {
|
||||
return false;
|
||||
}
|
||||
is(textContent.text, aExpectedText,
|
||||
aMessage + ": composition string is wrong" + aID);
|
||||
return textContent.text == aExpectedText;
|
||||
}
|
||||
|
||||
function checkSelection(aExpectedOffset, aExpectedText, aMessage, aID)
|
||||
{
|
||||
var selectedText = synthesizeQuerySelectedText();
|
||||
if (!checkQueryContentResult(selectedText, aMessage +
|
||||
": synthesizeQuerySelectedText " + aID)) {
|
||||
return false;
|
||||
}
|
||||
is(selectedText.offset, aExpectedOffset,
|
||||
aMessage + ": selection offset is wrong" + aID);
|
||||
is(selectedText.text, aExpectedText,
|
||||
aMessage + ": selected text is wrong" + aID);
|
||||
return selectedText.offset == aExpectedOffset &&
|
||||
selectedText.text == aExpectedText;
|
||||
}
|
||||
|
||||
function checkRect(aRect, aExpectedRect, aMessage)
|
||||
{
|
||||
is(aRect.left, aExpectedRect.left, aMessage + ": left is wrong");
|
||||
is(aRect.top, aExpectedRect.top, aMessage + " top is wrong");
|
||||
is(aRect.width, aExpectedRect.width, aMessage + ": width is wrong");
|
||||
is(aRect.height, aExpectedRect.height, aMessage + ": height is wrong");
|
||||
return aRect.left == aExpectedRect.left &&
|
||||
aRect.top == aExpectedRect.top &&
|
||||
aRect.width == aExpectedRect.width &&
|
||||
aRect.height == aExpectedRect.height;
|
||||
}
|
||||
|
||||
function checkRectContainsRect(aRect, aContainer, aMessage)
|
||||
{
|
||||
var container = { left: Math.ceil(aContainer.left),
|
||||
top: Math.ceil(aContainer.top),
|
||||
width: Math.floor(aContainer.width),
|
||||
height: Math.floor(aContainer.height) };
|
||||
|
||||
var ret = container.left <= aRect.left &&
|
||||
container.top <= aRect.top &&
|
||||
container.left + container.width >= aRect.left + aRect.width &&
|
||||
container.top + container.height >= aRect.top + aRect.height;
|
||||
ret = ret && aMessage;
|
||||
ok(ret, aMessage + " container={ left=" + container.left + ", top=" +
|
||||
container.top + ", width=" + container.width + ", height=" +
|
||||
container.height + " } rect={ left=" + aRect.left + ", top=" + aRect.top +
|
||||
", width=" + aRect.width + ", height=" + aRect.height + " }");
|
||||
return ret;
|
||||
}
|
||||
|
||||
function runCompositionTest()
|
||||
{
|
||||
textarea.value = "";
|
||||
textarea.focus();
|
||||
var caretRects = [];
|
||||
|
||||
var caretRect = synthesizeQueryCaretRect(0);
|
||||
if (!checkQueryContentResult(caretRect,
|
||||
"runCompositionTest: synthesizeQueryCaretRect #0")) {
|
||||
return false;
|
||||
}
|
||||
caretRects[0] = caretRect;
|
||||
|
||||
// start composition
|
||||
synthesizeComposition(true);
|
||||
|
||||
// input first character
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u3089",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 1, "attr": nsIDOMWindowUtils.COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 1, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u3089", "runCompositionTest", "#1-1") ||
|
||||
!checkSelection(1, "", "runCompositionTest", "#1-1")) {
|
||||
return;
|
||||
}
|
||||
|
||||
caretRect = synthesizeQueryCaretRect(1);
|
||||
if (!checkQueryContentResult(caretRect,
|
||||
"runCompositionTest: synthesizeQueryCaretRect #1-1")) {
|
||||
return false;
|
||||
}
|
||||
caretRects[1] = caretRect;
|
||||
|
||||
// input second character
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u3089\u30FC",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 2, "attr": nsIDOMWindowUtils.COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 2, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u3089\u30FC", "runCompositionTest", "#1-2") ||
|
||||
!checkSelection(2, "", "runCompositionTest", "#1-2")) {
|
||||
return;
|
||||
}
|
||||
|
||||
caretRect = synthesizeQueryCaretRect(2);
|
||||
if (!checkQueryContentResult(caretRect,
|
||||
"runCompositionTest: synthesizeQueryCaretRect #1-2")) {
|
||||
return false;
|
||||
}
|
||||
caretRects[2] = caretRect;
|
||||
|
||||
isnot(caretRects[2].left, caretRects[1].left,
|
||||
"runCompositionTest: caret isn't moved (#1-2)");
|
||||
is(caretRects[2].top, caretRects[1].top,
|
||||
"runCompositionTest: caret is moved to another line (#1-2)");
|
||||
is(caretRects[2].width, caretRects[1].width,
|
||||
"runCompositionTest: caret width is wrong (#1-2)");
|
||||
is(caretRects[2].height, caretRects[1].height,
|
||||
"runCompositionTest: caret width is wrong (#1-2)");
|
||||
|
||||
// input third character
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u3089\u30FC\u3081",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 3, "attr": nsIDOMWindowUtils.COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 3, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u3089\u30FC\u3081", "runCompositionTest", "#1-3") ||
|
||||
!checkSelection(3, "", "runCompositionTest", "#1-3")) {
|
||||
return;
|
||||
}
|
||||
|
||||
caretRect = synthesizeQueryCaretRect(3);
|
||||
if (!checkQueryContentResult(caretRect,
|
||||
"runCompositionTest: synthesizeQueryCaretRect #1-3")) {
|
||||
return false;
|
||||
}
|
||||
caretRects[3] = caretRect;
|
||||
|
||||
isnot(caretRects[3].left, caretRects[2].left,
|
||||
"runCompositionTest: caret isn't moved (#1-3)");
|
||||
is(caretRects[3].top, caretRects[2].top,
|
||||
"runCompositionTest: caret is moved to another line (#1-3)");
|
||||
is(caretRects[3].width, caretRects[2].width,
|
||||
"runCompositionTest: caret width is wrong (#1-3)");
|
||||
is(caretRects[3].height, caretRects[2].height,
|
||||
"runCompositionTest: caret height is wrong (#1-3)");
|
||||
|
||||
// moves the caret left
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u3089\u30FC\u3081",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 3, "attr": nsIDOMWindowUtils.COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 2, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u3089\u30FC\u3081", "runCompositionTest", "#1-3-1") ||
|
||||
!checkSelection(2, "", "runCompositionTest", "#1-3-1")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
caretRect = synthesizeQueryCaretRect(2);
|
||||
if (!checkQueryContentResult(caretRect,
|
||||
"runCompositionTest: synthesizeQueryCaretRect #1-3-1")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
is(caretRect.left, caretRects[2].left,
|
||||
"runCompositionTest: caret rects are different (#1-3-1, left)");
|
||||
is(caretRect.top, caretRects[2].top,
|
||||
"runCompositionTest: caret rects are different (#1-3-1, top)");
|
||||
// by bug 335359, the caret width depends on the right side's character.
|
||||
is(caretRect.width, caretRects[2].width + 1,
|
||||
"runCompositionTest: caret rects are different (#1-3-1, width)");
|
||||
is(caretRect.height, caretRects[2].height,
|
||||
"runCompositionTest: caret rects are different (#1-3-1, height)");
|
||||
|
||||
// moves the caret left
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u3089\u30FC\u3081",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 3, "attr": nsIDOMWindowUtils.COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 1, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u3089\u30FC\u3081", "runCompositionTest", "#1-3-2") ||
|
||||
!checkSelection(1, "", "runCompositionTest", "#1-3-2")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
caretRect = synthesizeQueryCaretRect(1);
|
||||
if (!checkQueryContentResult(caretRect,
|
||||
"runCompositionTest: synthesizeQueryCaretRect #1-3-2")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
is(caretRect.left, caretRects[1].left,
|
||||
"runCompositionTest: caret rects are different (#1-3-2, left)");
|
||||
is(caretRect.top, caretRects[1].top,
|
||||
"runCompositionTest: caret rects are different (#1-3-2, top)");
|
||||
// by bug 335359, the caret width depends on the right side's character.
|
||||
is(caretRect.width, caretRects[1].width + 1,
|
||||
"runCompositionTest: caret rects are different (#1-3-2, width)");
|
||||
is(caretRect.height, caretRects[1].height,
|
||||
"runCompositionTest: caret rects are different (#1-3-2, height)");
|
||||
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u3089\u30FC\u3081\u3093",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 4, "attr": nsIDOMWindowUtils.COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 4, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u3089\u30FC\u3081\u3093", "runCompositionTest", "#1-4") ||
|
||||
!checkSelection(4, "", "runCompositionTest", "#1-4")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// backspace
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u3089\u30FC\u3081",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 3, "attr": nsIDOMWindowUtils.COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 3, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u3089\u30FC\u3081", "runCompositionTest", "#1-5") ||
|
||||
!checkSelection(3, "", "runCompositionTest", "#1-5")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// re-input
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u3089\u30FC\u3081\u3093",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 4, "attr": nsIDOMWindowUtils.COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 4, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u3089\u30FC\u3081\u3093", "runCompositionTest", "#1-6") ||
|
||||
!checkSelection(4, "", "runCompositionTest", "#1-6")) {
|
||||
return;
|
||||
}
|
||||
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u3089\u30FC\u3081\u3093\u3055",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 5, "attr": nsIDOMWindowUtils.COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 5, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u3089\u30FC\u3081\u3093\u3055", "runCompositionTest", "#1-7") ||
|
||||
!checkSelection(5, "", "runCompositionTest", "#1-7")) {
|
||||
return;
|
||||
}
|
||||
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u3089\u30FC\u3081\u3093\u3055\u3044",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 6, "attr": nsIDOMWindowUtils.COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 6, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u3089\u30FC\u3081\u3093\u3055\u3044", "runCompositionTest", "#1-8") ||
|
||||
!checkSelection(6, "", "runCompositionTest", "#1-8")) {
|
||||
return;
|
||||
}
|
||||
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u3089\u30FC\u3081\u3093\u3055\u3044\u3053",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 7, "attr": nsIDOMWindowUtils.COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 7, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u3089\u30FC\u3081\u3093\u3055\u3044\u3053", "runCompositionTest", "#1-8") ||
|
||||
!checkSelection(7, "", "runCompositionTest", "#1-8")) {
|
||||
return;
|
||||
}
|
||||
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u3089\u30FC\u3081\u3093\u3055\u3044\u3053\u3046",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 8, "attr": nsIDOMWindowUtils.COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 8, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u3089\u30FC\u3081\u3093\u3055\u3044\u3053\u3046",
|
||||
"runCompositionTest", "#1-9") ||
|
||||
!checkSelection(8, "", "runCompositionTest", "#1-9")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// convert
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u30E9\u30FC\u30E1\u30F3\u6700\u9AD8",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 4,
|
||||
"attr": nsIDOMWindowUtils.COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT },
|
||||
{ "length": 2,
|
||||
"attr": nsIDOMWindowUtils.COMPOSITION_ATTR_CONVERTEDTEXT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 4, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700\u9AD8",
|
||||
"runCompositionTest", "#1-10") ||
|
||||
!checkSelection(6, "", "runCompositionTest", "#1-10")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// change the selected clause
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u30E9\u30FC\u30E1\u30F3\u6700\u9AD8",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 4,
|
||||
"attr": nsIDOMWindowUtils.COMPOSITION_ATTR_CONVERTEDTEXT },
|
||||
{ "length": 2,
|
||||
"attr": nsIDOMWindowUtils.COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 6, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700\u9AD8",
|
||||
"runCompositionTest", "#1-11") ||
|
||||
!checkSelection(6, "", "runCompositionTest", "#1-11")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// reset clauses
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 5,
|
||||
"attr": nsIDOMWindowUtils.COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT },
|
||||
{ "length": 3,
|
||||
"attr": nsIDOMWindowUtils.COMPOSITION_ATTR_CONVERTEDTEXT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 5, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046",
|
||||
"runCompositionTest", "#1-12") ||
|
||||
!checkSelection(8, "", "runCompositionTest", "#1-12")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var textRect1 = synthesizeQueryTextRect(0, 1);
|
||||
var textRect2 = synthesizeQueryTextRect(1, 1);
|
||||
if (!checkQueryContentResult(textRect1,
|
||||
"runCompositionTest: synthesizeQueryTextRect #1-12-1") ||
|
||||
!checkQueryContentResult(textRect2,
|
||||
"runCompositionTest: synthesizeQueryTextRect #1-12-2")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// commit the composition string
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 0, "attr": 0 }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 8, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046",
|
||||
"runCompositionTest", "#1-13") ||
|
||||
!checkSelection(8, "", "runCompositionTest", "#1-13")) {
|
||||
return;
|
||||
}
|
||||
|
||||
synthesizeComposition(false);
|
||||
|
||||
var textRect3 = synthesizeQueryTextRect(0, 1);
|
||||
var textRect4 = synthesizeQueryTextRect(1, 1);
|
||||
|
||||
if (!checkQueryContentResult(textRect3,
|
||||
"runCompositionTest: synthesizeQueryTextRect #1-13-1") ||
|
||||
!checkQueryContentResult(textRect4,
|
||||
"runCompositionTest: synthesizeQueryTextRect #1-13-2")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
checkRect(textRect3, textRect1, "runCompositionTest: textRect #1-13-1");
|
||||
checkRect(textRect4, textRect2, "runCompositionTest: textRect #1-13-2");
|
||||
|
||||
// restart composition
|
||||
synthesizeComposition(true);
|
||||
|
||||
// input characters
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u3057",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 1, "attr": nsIDOMWindowUtils.COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 1, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046\u3057",
|
||||
"runCompositionTest", "#2-1") ||
|
||||
!checkSelection(8 + 1, "", "runCompositionTest", "#2-1")) {
|
||||
return;
|
||||
}
|
||||
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u3058",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 1, "attr": nsIDOMWindowUtils.COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 1, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046\u3058",
|
||||
"runCompositionTest", "#2-2") ||
|
||||
!checkSelection(8 + 1, "", "runCompositionTest", "#2-2")) {
|
||||
return;
|
||||
}
|
||||
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u3058\u3087",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 2, "attr": nsIDOMWindowUtils.COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 2, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046\u3058\u3087",
|
||||
"runCompositionTest", "#2-3") ||
|
||||
!checkSelection(8 + 2, "", "runCompositionTest", "#2-3")) {
|
||||
return;
|
||||
}
|
||||
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u3058\u3087\u3046",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 3, "attr": nsIDOMWindowUtils.COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 3, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046\u3058\u3087\u3046",
|
||||
"runCompositionTest", "#2-4") ||
|
||||
!checkSelection(8 + 3, "", "runCompositionTest", "#2-4")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// commit the composition string
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u3058\u3087\u3046",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 0, "attr": 0 }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 3, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046\u3058\u3087\u3046",
|
||||
"runCompositionTest", "#2-4") ||
|
||||
!checkSelection(8 + 3, "", "runCompositionTest", "#2-4")) {
|
||||
return;
|
||||
}
|
||||
|
||||
synthesizeComposition(false);
|
||||
|
||||
// set selection
|
||||
var selectionSetTest = synthesizeSelectionSet(4, 7, false);
|
||||
ok(selectionSetTest, "runCompositionTest: selectionSetTest failed");
|
||||
|
||||
if (!checkSelection(4, "\u3055\u884C\u3053\u3046\u3058\u3087\u3046", "runCompositionTest", "#3-1")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// start composition with selection
|
||||
synthesizeComposition(true);
|
||||
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u304A",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 1, "attr": nsIDOMWindowUtils.COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 1, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u304A",
|
||||
"runCompositionTest", "#3-2") ||
|
||||
!checkSelection(4 + 1, "", "runCompositionTest", "#3-2")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// remove the composition string
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 0, "attr": 0 }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 0, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
|
||||
"runCompositionTest", "#3-3") ||
|
||||
!checkSelection(4, "", "runCompositionTest", "#3-3")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// re-input the composition string
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u3046",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 1, "attr": nsIDOMWindowUtils.COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 1, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3046",
|
||||
"runCompositionTest", "#3-4") ||
|
||||
!checkSelection(4 + 1, "", "runCompositionTest", "#3-4")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// cancel the composition
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 0, "attr": 0 }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 0, "length": 0 }
|
||||
});
|
||||
|
||||
synthesizeComposition(false);
|
||||
|
||||
if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
|
||||
"runCompositionTest", "#3-5") ||
|
||||
!checkSelection(4, "", "runCompositionTest", "#3-5")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function runCharAtPointTest(aFocusedEditor, aTargetName)
|
||||
{
|
||||
aFocusedEditor.value = "This is a test of the\nContent Events";
|
||||
// 012345678901234567890 12345678901234
|
||||
// 0 1 2 3
|
||||
|
||||
const kLFLen = kIsWin ? 2 : 1;
|
||||
|
||||
const kNone = -1;
|
||||
const kTestingOffset = [ 0, 10, 20, 21 + kLFLen, 34 + kLFLen];
|
||||
const kLeftSideOffset = [ kNone, 9, 19, kNone, 33 + kLFLen];
|
||||
const kRightSideOffset = [ 1, 11, kNone, 22 + kLFLen, kNone];
|
||||
|
||||
var editorRect = synthesizeQueryEditorRect();
|
||||
if (!checkQueryContentResult(editorRect,
|
||||
"runCharAtPointTest (" + aTargetName + "): editorRect")) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < kTestingOffset.length; i++) {
|
||||
var textRect = synthesizeQueryTextRect(kTestingOffset[i], 1);
|
||||
if (!checkQueryContentResult(textRect,
|
||||
"runCharAtPointTest (" + aTargetName + "): textRect", "i=" + i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
checkRectContainsRect(textRect, editorRect,
|
||||
"runCharAtPointTest (" + aTargetName +
|
||||
"): the text rect isn't in the editor");
|
||||
|
||||
// Test #1, getting same character rect by the point near the top-left.
|
||||
var charAtPt1 = synthesizeCharAtPoint(textRect.left + 1,
|
||||
textRect.top + 1);
|
||||
if (checkQueryContentResult(charAtPt1,
|
||||
"runCharAtPointTest (" + aTargetName + "): charAtPt1", "i=" + i)) {
|
||||
ok(!charAtPt1.notFound,
|
||||
"runCharAtPointTest (" + aTargetName + "): charAtPt1 isn't found: i=" + i);
|
||||
if (!charAtPt1.notFound) {
|
||||
is(charAtPt1.offset, kTestingOffset[i],
|
||||
"runCharAtPointTest (" + aTargetName + "): charAtPt1 offset is wrong: i=" + i);
|
||||
checkRect(charAtPt1, textRect, "runCharAtPointTest (" + aTargetName +
|
||||
"): charAtPt1 left is wrong: i=" + i);
|
||||
}
|
||||
}
|
||||
|
||||
// Test #2, getting same character rect by the point near the bottom-right.
|
||||
var charAtPt2 = synthesizeCharAtPoint(textRect.left + textRect.width - 2,
|
||||
textRect.top + textRect.height - 2);
|
||||
if (checkQueryContentResult(charAtPt2,
|
||||
"runCharAtPointTest (" + aTargetName + "): charAtPt2", "i=" + i)) {
|
||||
ok(!charAtPt2.notFound,
|
||||
"runCharAtPointTest (" + aTargetName + "): charAtPt2 isn't found: i=" + i);
|
||||
if (!charAtPt2.notFound) {
|
||||
is(charAtPt2.offset, kTestingOffset[i],
|
||||
"runCharAtPointTest (" + aTargetName + "): charAtPt2 offset is wrong: i=" + i);
|
||||
checkRect(charAtPt2, textRect, "runCharAtPointTest (" + aTargetName +
|
||||
"): charAtPt1 left is wrong: i=" + i);
|
||||
}
|
||||
}
|
||||
|
||||
// Test #3, getting left character offset.
|
||||
var charAtPt3 = synthesizeCharAtPoint(textRect.left - 2,
|
||||
textRect.top + 1);
|
||||
if (checkQueryContentResult(charAtPt3,
|
||||
"runCharAtPointTest (" + aTargetName + "): charAtPt3", "i=" + i)) {
|
||||
is(charAtPt3.notFound, kLeftSideOffset[i] == kNone,
|
||||
kLeftSideOffset[i] == kNone ?
|
||||
"runCharAtPointTest (" + aTargetName + "): charAtPt3 is found: i=" + i :
|
||||
"runCharAtPointTest (" + aTargetName + "): charAtPt3 isn't found: i=" + i);
|
||||
if (!charAtPt3.notFound) {
|
||||
is(charAtPt3.offset, kLeftSideOffset[i],
|
||||
"runCharAtPointTest (" + aTargetName + "): charAtPt3 offset is wrong: i=" + i);
|
||||
}
|
||||
}
|
||||
|
||||
// Test #4, getting right character offset.
|
||||
var charAtPt4 = synthesizeCharAtPoint(textRect.left + textRect.width + 1,
|
||||
textRect.top + textRect.height - 2);
|
||||
if (checkQueryContentResult(charAtPt4,
|
||||
"runCharAtPointTest (" + aTargetName + "): charAtPt4", "i=" + i)) {
|
||||
is(charAtPt4.notFound, kRightSideOffset[i] == kNone,
|
||||
kRightSideOffset[i] == kNone ?
|
||||
"runCharAtPointTest (" + aTargetName + "): charAtPt4 is found: i=" + i :
|
||||
"runCharAtPointTest (" + aTargetName + "): charAtPt4 isn't found: i=" + i);
|
||||
if (!charAtPt4.notFound) {
|
||||
is(charAtPt4.offset, kRightSideOffset[i],
|
||||
"runCharAtPointTest (" + aTargetName + "): charAtPt4 offset is wrong: i=" + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function runTestOnAnotherContext(aPanelOrFrame, aFocusedEditor, aTestName)
|
||||
{
|
||||
aFocusedEditor.value = "";
|
||||
|
||||
var editorRect = synthesizeQueryEditorRect();
|
||||
if (!checkQueryContentResult(editorRect, aTestName + ": editorRect")) {
|
||||
return;
|
||||
}
|
||||
|
||||
var r = aPanelOrFrame.getBoundingClientRect();
|
||||
var parentRect = { "left": r.left, "top": r.top, "width": r.right - r.left,
|
||||
"height": r.bottom - r.top };
|
||||
checkRectContainsRect(editorRect, parentRect, aTestName +
|
||||
": the editor rect coordinates are wrong");
|
||||
|
||||
// start composition
|
||||
synthesizeComposition(true);
|
||||
|
||||
// input characters
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u3078\u3093\u3057\u3093",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 4, "attr": nsIDOMWindowUtils.COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 4, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u3078\u3093\u3057\u3093", aTestName, "#1-1") ||
|
||||
!checkSelection(4, "", aTestName, "#1-1")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// convert them #1
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u8FD4\u4FE1",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 2,
|
||||
"attr": nsIDOMWindowUtils.COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 2, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u8FD4\u4FE1", aTestName, "#1-2") ||
|
||||
!checkSelection(2, "", aTestName, "#1-2")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// convert them #2
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u5909\u8EAB",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 2,
|
||||
"attr": nsIDOMWindowUtils.COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 2, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u5909\u8EAB", aTestName, "#1-3") ||
|
||||
!checkSelection(2, "", aTestName, "#1-3")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// commit them
|
||||
synthesizeText(
|
||||
{ "composition":
|
||||
{ "string": "\u5909\u8EAB",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 0, "attr": 0 }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 2, "length": 0 }
|
||||
});
|
||||
|
||||
if (!checkContent("\u5909\u8EAB", aTestName, "#1-4") ||
|
||||
!checkSelection(2, "", aTestName, "#1-4")) {
|
||||
return;
|
||||
}
|
||||
|
||||
synthesizeComposition(false);
|
||||
|
||||
is(aFocusedEditor.value, "\u5909\u8EAB",
|
||||
aTestName + ": composition isn't in the focused editor");
|
||||
if (aFocusedEditor.value != "\u5909\u8EAB") {
|
||||
return;
|
||||
}
|
||||
|
||||
var textRect = synthesizeQueryTextRect(0, 1);
|
||||
var caretRect = synthesizeQueryCaretRect(2);
|
||||
if (!checkQueryContentResult(textRect,
|
||||
aTestName + ": synthesizeQueryTextRect") ||
|
||||
!checkQueryContentResult(caretRect,
|
||||
aTestName + ": synthesizeQueryCaretRect")) {
|
||||
return;
|
||||
}
|
||||
checkRectContainsRect(textRect, editorRect, aTestName + ":testRect");
|
||||
checkRectContainsRect(caretRect, editorRect, aTestName + ":caretRect");
|
||||
}
|
||||
|
||||
function runFrameTest()
|
||||
{
|
||||
var textareaInFrame = iframe.contentDocument.getElementById("textarea");
|
||||
textareaInFrame.focus();
|
||||
runTestOnAnotherContext(iframe, textareaInFrame, "runFrameTest");
|
||||
runCharAtPointTest(textareaInFrame, "textarea in the iframe");
|
||||
}
|
||||
|
||||
var gPanelShown = false;
|
||||
var gPanelFocused = false;
|
||||
function onPanelShown(aEvent)
|
||||
{
|
||||
gPanelShown = true;
|
||||
textbox.focus();
|
||||
setTimeout(doPanelTest, 0);
|
||||
}
|
||||
|
||||
function onFocusPanelTextbox(aEvent)
|
||||
{
|
||||
gPanelFocused = true;
|
||||
setTimeout(doPanelTest, 0);
|
||||
}
|
||||
|
||||
var gIsPanelHiding = false;
|
||||
var gIsRunPanelTestInternal = false;
|
||||
function doPanelTest()
|
||||
{
|
||||
if (!gPanelFocused || !gPanelShown) {
|
||||
return;
|
||||
}
|
||||
if (gIsRunPanelTestInternal) {
|
||||
return;
|
||||
}
|
||||
gIsRunPanelTestInternal = true;
|
||||
runTestOnAnotherContext(panel, textbox, "runPanelTest");
|
||||
runCharAtPointTest(textbox, "textbox in the panel");
|
||||
gIsPanelHiding = true;
|
||||
panel.hidePopup();
|
||||
}
|
||||
|
||||
function onPanelHidden(aEvent)
|
||||
{
|
||||
panel.hidden = true;
|
||||
ok(gIsPanelHiding, "runPanelTest: the panel is hidden unexpectedly");
|
||||
finish();
|
||||
}
|
||||
|
||||
function runPanelTest()
|
||||
{
|
||||
panel.hidden = false;
|
||||
panel.openPopupAtScreen(window.screenX + window.outerWidth, 0, false);
|
||||
}
|
||||
|
||||
function runTest()
|
||||
{
|
||||
runCompositionTest();
|
||||
runCharAtPointTest(textarea, "textarea in the document");
|
||||
runFrameTest();
|
||||
runPanelTest();
|
||||
}
|
||||
|
||||
]]>
|
||||
</script>
|
||||
|
||||
</window>
|
@ -157,9 +157,10 @@ nsInputStreamTee::TeeSegment(const char *buf, PRUint32 count)
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
PRUint32 bytesWritten = 0;
|
||||
PRUint32 totalBytesWritten = 0;
|
||||
while (count) {
|
||||
rv = mSink->Write(buf + bytesWritten, count, &bytesWritten);
|
||||
PRUint32 bytesWritten = 0;
|
||||
rv = mSink->Write(buf + totalBytesWritten, count, &bytesWritten);
|
||||
if (NS_FAILED(rv)) {
|
||||
// ok, this is not a fatal error... just drop our reference to mSink
|
||||
// and continue on as if nothing happened.
|
||||
@ -169,6 +170,7 @@ nsInputStreamTee::TeeSegment(const char *buf, PRUint32 count)
|
||||
mSink = 0;
|
||||
break;
|
||||
}
|
||||
totalBytesWritten += bytesWritten;
|
||||
NS_ASSERTION(bytesWritten <= count, "wrote too much");
|
||||
count -= bytesWritten;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user