mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-12 04:45:45 +00:00
Merge m-c to inbound on a CLOSED TREE.
This commit is contained in:
commit
30a912783c
@ -878,3 +878,13 @@ pref("identity.fxaccounts.auth.uri", "https://api-accounts.dev.lcip.org/v1");
|
||||
// Gaia relies heavily on scroll events for now, so lets fire them
|
||||
// more often than the default value (100).
|
||||
pref("apz.asyncscroll.throttle", 40);
|
||||
|
||||
// This preference allows FirefoxOS apps (and content, I think) to force
|
||||
// the use of software (instead of hardware accelerated) 2D canvases by
|
||||
// creating a context like this:
|
||||
//
|
||||
// canvas.getContext('2d', { willReadFrequently: true })
|
||||
//
|
||||
// Using a software canvas can save memory when JS calls getImageData()
|
||||
// on the canvas frequently. See bug 884226.
|
||||
pref("gfx.canvas.willReadFrequently.enable", true);
|
||||
|
@ -8,6 +8,8 @@ let Cu = Components.utils;
|
||||
let Cc = Components.classes;
|
||||
let Ci = Components.interfaces;
|
||||
|
||||
dump("############ ErrorPage.js\n");
|
||||
|
||||
let ErrorPageHandler = {
|
||||
_reload: function() {
|
||||
docShell.QueryInterface(Ci.nsIWebNavigation).reload(Ci.nsIWebNavigation.LOAD_FLAGS_NONE);
|
||||
@ -31,12 +33,8 @@ let ErrorPageHandler = {
|
||||
}
|
||||
},
|
||||
|
||||
domContentLoadedHandler: function(e) {
|
||||
let target = e.originalTarget;
|
||||
let targetDocShell = target.defaultView
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation);
|
||||
if (targetDocShell != docShell) {
|
||||
_bindPageEvent: function(target) {
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -52,11 +50,23 @@ let ErrorPageHandler = {
|
||||
}
|
||||
},
|
||||
|
||||
domContentLoadedHandler: function(e) {
|
||||
let target = e.originalTarget;
|
||||
let targetDocShell = target.defaultView
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation);
|
||||
if (targetDocShell != docShell) {
|
||||
return;
|
||||
}
|
||||
this._bindPageEvent(target);
|
||||
},
|
||||
|
||||
init: function() {
|
||||
addMessageListener("ErrorPage:ReloadPage", this._reload.bind(this));
|
||||
addEventListener('DOMContentLoaded',
|
||||
this.domContentLoadedHandler.bind(this),
|
||||
true);
|
||||
this._bindPageEvent(content.document);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -149,6 +149,26 @@ let ErrorPage = {
|
||||
}
|
||||
},
|
||||
|
||||
_listenError: function(frameLoader) {
|
||||
let self = this;
|
||||
let frameElement = frameLoader.ownerElement;
|
||||
let injectErrorPageScript = function() {
|
||||
let mm = frameLoader.messageManager;
|
||||
try {
|
||||
mm.loadFrameScript(kErrorPageFrameScript, true, true);
|
||||
} catch (e) {
|
||||
dump('Error loading ' + kErrorPageFrameScript + ' as frame script: ' + e + '\n');
|
||||
}
|
||||
mm.addMessageListener('ErrorPage:AddCertException', self._addCertException.bind(self));
|
||||
frameElement.removeEventListener('mozbrowsererror', injectErrorPageScript, true);
|
||||
};
|
||||
|
||||
frameElement.addEventListener('mozbrowsererror',
|
||||
injectErrorPageScript,
|
||||
true // use capture
|
||||
);
|
||||
},
|
||||
|
||||
init: function errorPageInit() {
|
||||
Services.obs.addObserver(this, 'in-process-browser-or-app-frame-shown', false);
|
||||
Services.obs.addObserver(this, 'remote-browser-frame-shown', false);
|
||||
@ -156,17 +176,7 @@ let ErrorPage = {
|
||||
|
||||
observe: function errorPageObserve(aSubject, aTopic, aData) {
|
||||
let frameLoader = aSubject.QueryInterface(Ci.nsIFrameLoader);
|
||||
let mm = frameLoader.messageManager;
|
||||
|
||||
// This won't happen from dom/ipc/preload.js in non-OOP builds.
|
||||
try {
|
||||
if (Services.prefs.getBoolPref("dom.ipc.tabs.disabled") === true) {
|
||||
mm.loadFrameScript(kErrorPageFrameScript, true, true);
|
||||
}
|
||||
} catch (e) {
|
||||
dump('Error loading ' + kErrorPageFrameScript + ' as frame script: ' + e + '\n');
|
||||
}
|
||||
mm.addMessageListener('ErrorPage:AddCertException', this._addCertException.bind(this));
|
||||
this._listenError(frameLoader);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"revision": "ac80bbe24c39a813ab65b98bb56dce2e42207b22",
|
||||
"revision": "d5d2b5614c32e17c5c3c736181a66bfa3fc5c3ef",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -301,18 +301,13 @@ let DocShellCapabilitiesListener = {
|
||||
_latestCapabilities: "",
|
||||
|
||||
init: function () {
|
||||
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebProgress);
|
||||
|
||||
webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);
|
||||
gFrameTree.addObserver(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* onLocationChange() is called as soon as we start loading a page after
|
||||
* we are certain that there's nothing blocking the load (e.g. a content
|
||||
* policy added by AdBlock or the like).
|
||||
* onFrameTreeReset() is called as soon as we start loading a page.
|
||||
*/
|
||||
onLocationChange: function() {
|
||||
onFrameTreeReset: function() {
|
||||
// The order of docShell capabilities cannot change while we're running
|
||||
// so calling join() without sorting before is totally sufficient.
|
||||
let caps = DocShellCapabilities.collect(docShell).join(",");
|
||||
@ -322,15 +317,7 @@ let DocShellCapabilitiesListener = {
|
||||
this._latestCapabilities = caps;
|
||||
MessageQueue.push("disallow", () => caps || null);
|
||||
}
|
||||
},
|
||||
|
||||
onStateChange: function () {},
|
||||
onProgressChange: function () {},
|
||||
onStatusChange: function () {},
|
||||
onSecurityChange: function () {},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
|
||||
Ci.nsISupportsWeakReference])
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -346,12 +333,12 @@ let SessionStorageListener = {
|
||||
init: function () {
|
||||
addEventListener("MozStorageChanged", this);
|
||||
Services.obs.addObserver(this, "browser:purge-domain-data", true);
|
||||
Services.obs.addObserver(this, "browser:purge-session-history", true);
|
||||
gFrameTree.addObserver(this);
|
||||
},
|
||||
|
||||
handleEvent: function (event) {
|
||||
// Ignore events triggered by localStorage or globalStorage changes.
|
||||
if (isSessionStorageEvent(event)) {
|
||||
if (gFrameTree.contains(event.target) && isSessionStorageEvent(event)) {
|
||||
this.collect();
|
||||
}
|
||||
},
|
||||
@ -363,7 +350,17 @@ let SessionStorageListener = {
|
||||
},
|
||||
|
||||
collect: function () {
|
||||
MessageQueue.push("storage", () => SessionStorage.collect(docShell));
|
||||
if (docShell) {
|
||||
MessageQueue.push("storage", () => SessionStorage.collect(docShell, gFrameTree));
|
||||
}
|
||||
},
|
||||
|
||||
onFrameTreeCollected: function () {
|
||||
this.collect();
|
||||
},
|
||||
|
||||
onFrameTreeReset: function () {
|
||||
this.collect();
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
|
||||
|
@ -11,7 +11,7 @@ const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
||||
|
||||
const EXPORTED_METHODS = ["addObserver", "contains", "map"];
|
||||
const EXPORTED_METHODS = ["addObserver", "contains", "map", "forEach"];
|
||||
|
||||
/**
|
||||
* A FrameTree represents all frames that were reachable when the document
|
||||
@ -161,6 +161,34 @@ FrameTreeInternal.prototype = {
|
||||
return walk(this.content);
|
||||
},
|
||||
|
||||
/**
|
||||
* Applies the given function |cb| to all frames stored in the tree. Use this
|
||||
* method if |map()| doesn't suit your needs and you want more control over
|
||||
* how data is collected.
|
||||
*
|
||||
* @param cb (function)
|
||||
* This callback receives the current frame as the only argument.
|
||||
*/
|
||||
forEach: function (cb) {
|
||||
let frames = this._frames;
|
||||
|
||||
function walk(frame) {
|
||||
cb(frame);
|
||||
|
||||
if (!frames.has(frame)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Array.forEach(frame.frames, subframe => {
|
||||
if (frames.has(subframe)) {
|
||||
cb(subframe);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
walk(this.content);
|
||||
},
|
||||
|
||||
/**
|
||||
* Stores a given |frame| and its children in the frame tree.
|
||||
*
|
||||
|
@ -36,6 +36,8 @@ Cu.import("resource://gre/modules/osfile/_PromiseWorker.jsm", this);
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/AsyncShutdown.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "console",
|
||||
"resource://gre/modules/devtools/Console.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
|
||||
"resource://gre/modules/TelemetryStopwatch.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
@ -102,7 +104,7 @@ const TaskUtils = {
|
||||
return promise.then(
|
||||
null,
|
||||
function onError(reason) {
|
||||
Cu.reportError("Uncaught asynchronous error: " + reason + " at\n" + reason.stack);
|
||||
console.error("Uncaught asynchronous error", reason, "at", reason.stack);
|
||||
throw reason;
|
||||
}
|
||||
);
|
||||
@ -179,8 +181,7 @@ let SessionFileInternal = {
|
||||
this._recordTelemetry(msg.telemetry);
|
||||
} catch (ex) {
|
||||
TelemetryStopwatch.cancel("FX_SESSION_RESTORE_WRITE_FILE_LONGEST_OP_MS", refObj);
|
||||
Cu.reportError("Could not write session state file " + this.path
|
||||
+ ": " + ex);
|
||||
console.error("Could not write session state file ", this.path, ex);
|
||||
}
|
||||
|
||||
if (isFinalWrite) {
|
||||
@ -193,7 +194,7 @@ let SessionFileInternal = {
|
||||
SessionWorker.post("writeLoadStateOnceAfterStartup", [aLoadState]).then(msg => {
|
||||
this._recordTelemetry(msg.telemetry);
|
||||
return msg;
|
||||
}, Cu.reportError);
|
||||
}, console.error);
|
||||
},
|
||||
|
||||
createBackupCopy: function (ext) {
|
||||
|
@ -17,7 +17,8 @@ Cu.import("resource://gre/modules/TelemetryStopwatch.jsm", this);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
|
||||
"resource:///modules/sessionstore/SessionStore.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "console",
|
||||
"resource://gre/modules/devtools/Console.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionFile",
|
||||
"resource:///modules/sessionstore/SessionFile.jsm");
|
||||
|
||||
@ -309,7 +310,7 @@ let SessionSaverInternal = {
|
||||
return SessionFile.write(data).then(() => {
|
||||
this.updateLastSaveTime();
|
||||
notify(null, "sessionstore-state-write-complete");
|
||||
}, Cu.reportError);
|
||||
}, console.error);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -12,20 +12,29 @@ const Ci = Components.interfaces;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivacyLevel",
|
||||
"resource:///modules/sessionstore/PrivacyLevel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "console",
|
||||
"resource://gre/modules/devtools/Console.jsm");
|
||||
|
||||
// Returns the principal for a given |frame| contained in a given |docShell|.
|
||||
function getPrincipalForFrame(docShell, frame) {
|
||||
let ssm = Services.scriptSecurityManager;
|
||||
let uri = frame.document.documentURIObject;
|
||||
return ssm.getDocShellCodebasePrincipal(uri, docShell);
|
||||
}
|
||||
|
||||
this.SessionStorage = Object.freeze({
|
||||
/**
|
||||
* Updates all sessionStorage "super cookies"
|
||||
* @param aDocShell
|
||||
* @param docShell
|
||||
* That tab's docshell (containing the sessionStorage)
|
||||
* @param frameTree
|
||||
* The docShell's FrameTree instance.
|
||||
* @return Returns a nested object that will have hosts as keys and per-host
|
||||
* session storage data as values. For example:
|
||||
* {"example.com": {"key": "value", "my_number": 123}}
|
||||
*/
|
||||
collect: function (aDocShell) {
|
||||
return SessionStorageInternal.collect(aDocShell);
|
||||
collect: function (docShell, frameTree) {
|
||||
return SessionStorageInternal.collect(docShell, frameTree);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -45,36 +54,40 @@ this.SessionStorage = Object.freeze({
|
||||
let SessionStorageInternal = {
|
||||
/**
|
||||
* Reads all session storage data from the given docShell.
|
||||
* @param aDocShell
|
||||
* @param docShell
|
||||
* A tab's docshell (containing the sessionStorage)
|
||||
* @param frameTree
|
||||
* The docShell's FrameTree instance.
|
||||
* @return Returns a nested object that will have hosts as keys and per-host
|
||||
* session storage data as values. For example:
|
||||
* {"example.com": {"key": "value", "my_number": 123}}
|
||||
*/
|
||||
collect: function (aDocShell) {
|
||||
collect: function (docShell, frameTree) {
|
||||
let data = {};
|
||||
let webNavigation = aDocShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
let shistory = webNavigation.sessionHistory;
|
||||
let visitedOrigins = new Set();
|
||||
|
||||
for (let i = 0; shistory && i < shistory.count; i++) {
|
||||
let principal = History.getPrincipalForEntry(shistory, i, aDocShell);
|
||||
frameTree.forEach(frame => {
|
||||
let principal = getPrincipalForFrame(docShell, frame);
|
||||
if (!principal) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the root domain of the current history entry
|
||||
// and use that as a key for the per-host storage data.
|
||||
let origin = principal.jarPrefix + principal.origin;
|
||||
if (data.hasOwnProperty(origin)) {
|
||||
if (visitedOrigins.has(origin)) {
|
||||
// Don't read a host twice.
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
let originData = this._readEntry(principal, aDocShell);
|
||||
// Mark the current origin as visited.
|
||||
visitedOrigins.add(origin);
|
||||
|
||||
let originData = this._readEntry(principal, docShell);
|
||||
if (Object.keys(originData).length) {
|
||||
data[origin] = originData;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return Object.keys(data).length ? data : null;
|
||||
},
|
||||
@ -89,10 +102,11 @@ let SessionStorageInternal = {
|
||||
* {"example.com": {"key": "value", "my_number": 123}}
|
||||
*/
|
||||
restore: function (aDocShell, aStorageData) {
|
||||
for (let [host, data] in Iterator(aStorageData)) {
|
||||
for (let host of Object.keys(aStorageData)) {
|
||||
let data = aStorageData[host];
|
||||
let uri = Services.io.newURI(host, null, null);
|
||||
let principal = Services.scriptSecurityManager.getDocShellCodebasePrincipal(uri, aDocShell);
|
||||
let storageManager = aDocShell.QueryInterface(Components.interfaces.nsIDOMStorageManager);
|
||||
let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
|
||||
|
||||
// There is no need to pass documentURI, it's only used to fill documentURI property of
|
||||
// domstorage event, which in this case has no consumer. Prevention of events in case
|
||||
@ -104,7 +118,7 @@ let SessionStorageInternal = {
|
||||
storage.setItem(key, value);
|
||||
} catch (e) {
|
||||
// throws e.g. for URIs that can't have sessionStorage
|
||||
Cu.reportError(e);
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -122,7 +136,7 @@ let SessionStorageInternal = {
|
||||
let storage;
|
||||
|
||||
try {
|
||||
let storageManager = aDocShell.QueryInterface(Components.interfaces.nsIDOMStorageManager);
|
||||
let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
|
||||
storage = storageManager.getStorage(aPrincipal);
|
||||
} catch (e) {
|
||||
// sessionStorage might throw if it's turned off, see bug 458954
|
||||
@ -142,25 +156,3 @@ let SessionStorageInternal = {
|
||||
return hostData;
|
||||
}
|
||||
};
|
||||
|
||||
let History = {
|
||||
/**
|
||||
* Returns a given history entry's URI.
|
||||
* @param aHistory
|
||||
* That tab's session history
|
||||
* @param aIndex
|
||||
* The history entry's index
|
||||
* @param aDocShell
|
||||
* That tab's docshell
|
||||
*/
|
||||
getPrincipalForEntry: function History_getPrincipalForEntry(aHistory,
|
||||
aIndex,
|
||||
aDocShell) {
|
||||
try {
|
||||
return Services.scriptSecurityManager.getDocShellCodebasePrincipal(
|
||||
aHistory.getEntryAtIndex(aIndex, false).URI, aDocShell);
|
||||
} catch (e) {
|
||||
// This might throw for some reason.
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -102,6 +102,8 @@ XPCOMUtils.defineLazyServiceGetter(this, "gScreenManager",
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "Telemetry",
|
||||
"@mozilla.org/base/telemetry;1", "nsITelemetry");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "console",
|
||||
"resource://gre/modules/devtools/Console.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DocShellCapabilities",
|
||||
"resource:///modules/sessionstore/DocShellCapabilities.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Messenger",
|
||||
@ -959,7 +961,7 @@ let SessionStoreInternal = {
|
||||
// Let everyone know we're done.
|
||||
this._deferredInitialized.resolve();
|
||||
}
|
||||
}, Cu.reportError);
|
||||
}, console.error);
|
||||
};
|
||||
|
||||
aWindow.addEventListener("load", onload);
|
||||
@ -3136,7 +3138,7 @@ let SessionStoreInternal = {
|
||||
cookie.value, !!cookie.secure, !!cookie.httponly, true,
|
||||
"expiry" in cookie ? cookie.expiry : MAX_EXPIRY);
|
||||
}
|
||||
catch (ex) { Cu.reportError(ex); } // don't let a single cookie stop recovering
|
||||
catch (ex) { console.error(ex); } // don't let a single cookie stop recovering
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -12,6 +12,8 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
||||
Cu.import("resource://gre/modules/Promise.jsm", this);
|
||||
Cu.import("resource://gre/modules/Task.jsm", this);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "console",
|
||||
"resource://gre/modules/devtools/Console.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Messenger",
|
||||
"resource:///modules/sessionstore/Messenger.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivacyLevel",
|
||||
@ -328,7 +330,7 @@ let TabStateInternal = {
|
||||
history = syncHandler.collectSessionHistory(includePrivateData);
|
||||
} catch (e) {
|
||||
// This may happen if the tab has crashed.
|
||||
Cu.reportError(e);
|
||||
console.error(e);
|
||||
return tabData;
|
||||
}
|
||||
|
||||
@ -364,7 +366,7 @@ let TabStateInternal = {
|
||||
if (key != "storage" || includePrivateData) {
|
||||
tabData[key] = data[key];
|
||||
} else {
|
||||
tabData.storage = {};
|
||||
let storage = {};
|
||||
let isPinned = tab.pinned;
|
||||
|
||||
// If we're not allowed to include private data, let's filter out hosts
|
||||
@ -372,9 +374,13 @@ let TabStateInternal = {
|
||||
for (let host of Object.keys(data.storage)) {
|
||||
let isHttps = host.startsWith("https:");
|
||||
if (PrivacyLevel.canSave({isHttps: isHttps, isPinned: isPinned})) {
|
||||
tabData.storage[host] = data.storage[host];
|
||||
storage[host] = data.storage[host];
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(storage).length) {
|
||||
tabData.storage = storage;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -43,6 +43,8 @@ Cu.import("resource://gre/modules/TelemetryStopwatch.jsm");
|
||||
Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "console",
|
||||
"resource://gre/modules/devtools/Console.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionFile",
|
||||
"resource:///modules/sessionstore/SessionFile.jsm");
|
||||
|
||||
@ -85,7 +87,7 @@ SessionStartup.prototype = {
|
||||
|
||||
SessionFile.read().then(
|
||||
this._onSessionFileRead.bind(this),
|
||||
Cu.reportError
|
||||
console.error
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -20,6 +20,7 @@ support-files =
|
||||
browser_pageStyle_sample_nested.html
|
||||
browser_scrollPositions_sample.html
|
||||
browser_scrollPositions_sample_frameset.html
|
||||
browser_sessionStorage.html
|
||||
browser_248970_b_sample.html
|
||||
browser_339445_sample.html
|
||||
browser_346337_sample.html
|
||||
|
@ -59,12 +59,12 @@ add_task(function flush_on_duplicate() {
|
||||
"sessionStorage data has been flushed when duplicating tabs");
|
||||
|
||||
yield promiseTabRestored(tab2);
|
||||
let {storage} = JSON.parse(ss.getTabState(tab2));
|
||||
gBrowser.removeTab(tab2)
|
||||
let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
|
||||
is(storage["http://example.com"].test, "on-duplicate",
|
||||
"sessionStorage data has been flushed when duplicating tabs");
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
gBrowser.removeTab(tab2);
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>browser_sessionStorage.html</title>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript;version=1.8">
|
||||
let isOuter = window == window.top;
|
||||
let args = window.location.search.slice(1).split("&");
|
||||
let rand = args[0];
|
||||
|
||||
if (isOuter) {
|
||||
let iframe = document.createElement("iframe");
|
||||
let isSecure = args.indexOf("secure") > -1;
|
||||
let scheme = isSecure ? "https" : "http";
|
||||
iframe.setAttribute("src", scheme + "://example.com" + location.pathname + "?" + rand);
|
||||
document.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
if (sessionStorage.length === 0) {
|
||||
sessionStorage.test = (isOuter ? "outer" : "inner") + "-value-" + rand;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -3,35 +3,39 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
let tmp = {};
|
||||
Cu.import("resource://gre/modules/Promise.jsm", tmp);
|
||||
let {Promise} = tmp;
|
||||
const RAND = Math.random();
|
||||
const URL = "http://mochi.test:8888/browser/" +
|
||||
"browser/components/sessionstore/test/browser_sessionStorage.html" +
|
||||
"?" + RAND;
|
||||
|
||||
const INITIAL_VALUE = "initial-value-" + Date.now();
|
||||
const OUTER_VALUE = "outer-value-" + RAND;
|
||||
const INNER_VALUE = "inner-value-" + RAND;
|
||||
|
||||
/**
|
||||
* This test ensures that setting, modifying and restoring sessionStorage data
|
||||
* works as expected.
|
||||
*/
|
||||
add_task(function session_storage() {
|
||||
let tab = yield createTabWithStorageData(["http://example.com", "http://mochi.test:8888"]);
|
||||
let tab = gBrowser.addTab(URL);
|
||||
let browser = tab.linkedBrowser;
|
||||
yield promiseBrowserLoaded(browser);
|
||||
|
||||
// Flush to make sure chrome received all data.
|
||||
SyncHandlers.get(browser).flush();
|
||||
|
||||
let {storage} = JSON.parse(ss.getTabState(tab));
|
||||
is(storage["http://example.com"].test, INITIAL_VALUE,
|
||||
is(storage["http://example.com"].test, INNER_VALUE,
|
||||
"sessionStorage data for example.com has been serialized correctly");
|
||||
is(storage["http://mochi.test:8888"].test, INITIAL_VALUE,
|
||||
is(storage["http://mochi.test:8888"].test, OUTER_VALUE,
|
||||
"sessionStorage data for mochi.test has been serialized correctly");
|
||||
|
||||
// Ensure that modifying sessionStore values works.
|
||||
yield modifySessionStorage(browser, {test: "modified"});
|
||||
yield modifySessionStorage2(browser, {test: "modified2"});
|
||||
SyncHandlers.get(browser).flush();
|
||||
|
||||
let {storage} = JSON.parse(ss.getTabState(tab));
|
||||
is(storage["http://example.com"].test, INITIAL_VALUE,
|
||||
is(storage["http://example.com"].test, "modified2",
|
||||
"sessionStorage data for example.com has been serialized correctly");
|
||||
is(storage["http://mochi.test:8888"].test, "modified",
|
||||
"sessionStorage data for mochi.test has been serialized correctly");
|
||||
@ -45,22 +49,40 @@ add_task(function session_storage() {
|
||||
SyncHandlers.get(browser2).flush();
|
||||
|
||||
let {storage} = JSON.parse(ss.getTabState(tab2));
|
||||
is(storage["http://example.com"].test, INITIAL_VALUE,
|
||||
is(storage["http://example.com"].test, "modified2",
|
||||
"sessionStorage data for example.com has been duplicated correctly");
|
||||
is(storage["http://mochi.test:8888"].test, "modified",
|
||||
"sessionStorage data for mochi.test has been duplicated correctly");
|
||||
|
||||
// Ensure that the content script retains restored data
|
||||
// (by e.g. duplicateTab) and send it along with new data.
|
||||
yield modifySessionStorage(browser2, {test: "modified2"});
|
||||
// (by e.g. duplicateTab) and sends it along with new data.
|
||||
yield modifySessionStorage(browser2, {test: "modified3"});
|
||||
SyncHandlers.get(browser2).flush();
|
||||
|
||||
let {storage} = JSON.parse(ss.getTabState(tab2));
|
||||
is(storage["http://example.com"].test, INITIAL_VALUE,
|
||||
is(storage["http://example.com"].test, "modified2",
|
||||
"sessionStorage data for example.com has been duplicated correctly");
|
||||
is(storage["http://mochi.test:8888"].test, "modified2",
|
||||
is(storage["http://mochi.test:8888"].test, "modified3",
|
||||
"sessionStorage data for mochi.test has been duplicated correctly");
|
||||
|
||||
// Check that loading a new URL discards data.
|
||||
browser2.loadURI("http://mochi.test:8888/");
|
||||
yield promiseBrowserLoaded(browser2);
|
||||
SyncHandlers.get(browser2).flush();
|
||||
|
||||
let {storage} = JSON.parse(ss.getTabState(tab2));
|
||||
is(storage["http://mochi.test:8888"].test, "modified3",
|
||||
"navigating retains correct storage data");
|
||||
ok(!storage["http://example.com"], "storage data was discarded");
|
||||
|
||||
// Check that loading a new URL discards data.
|
||||
browser2.loadURI("about:mozilla");
|
||||
yield promiseBrowserLoaded(browser2);
|
||||
SyncHandlers.get(browser2).flush();
|
||||
|
||||
let state = JSON.parse(ss.getTabState(tab2));
|
||||
ok(!state.hasOwnProperty("storage"), "storage data was discarded");
|
||||
|
||||
// Clean up.
|
||||
gBrowser.removeTab(tab);
|
||||
gBrowser.removeTab(tab2);
|
||||
@ -71,10 +93,12 @@ add_task(function session_storage() {
|
||||
* sessionStorage data collected for tabs.
|
||||
*/
|
||||
add_task(function purge_domain() {
|
||||
let tab = yield createTabWithStorageData(["http://example.com", "http://mochi.test:8888"]);
|
||||
let tab = gBrowser.addTab(URL);
|
||||
let browser = tab.linkedBrowser;
|
||||
yield promiseBrowserLoaded(browser);
|
||||
|
||||
yield notifyObservers(browser, "browser:purge-domain-data", "mochi.test");
|
||||
// Purge data for "mochi.test".
|
||||
yield purgeDomainData(browser, "mochi.test");
|
||||
|
||||
// Flush to make sure chrome received all data.
|
||||
SyncHandlers.get(browser).flush();
|
||||
@ -82,56 +106,36 @@ add_task(function purge_domain() {
|
||||
let {storage} = JSON.parse(ss.getTabState(tab));
|
||||
ok(!storage["http://mochi.test:8888"],
|
||||
"sessionStorage data for mochi.test has been purged");
|
||||
is(storage["http://example.com"].test, INITIAL_VALUE,
|
||||
is(storage["http://example.com"].test, INNER_VALUE,
|
||||
"sessionStorage data for example.com has been preserved");
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
|
||||
/**
|
||||
* This test ensures that purging session history data also purges data from
|
||||
* sessionStorage data collected for tabs
|
||||
*/
|
||||
add_task(function purge_shistory() {
|
||||
let tab = yield createTabWithStorageData(["http://example.com", "http://mochi.test:8888"]);
|
||||
let browser = tab.linkedBrowser;
|
||||
|
||||
yield notifyObservers(browser, "browser:purge-session-history");
|
||||
|
||||
// Flush to make sure chrome received all data.
|
||||
SyncHandlers.get(browser).flush();
|
||||
|
||||
let {storage} = JSON.parse(ss.getTabState(tab));
|
||||
ok(!storage["http://example.com"],
|
||||
"sessionStorage data for example.com has been purged");
|
||||
is(storage["http://mochi.test:8888"].test, INITIAL_VALUE,
|
||||
"sessionStorage data for mochi.test has been preserved");
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
|
||||
/**
|
||||
* This test ensures that collecting sessionStorage data respects the privacy
|
||||
* levels as set by the user.
|
||||
*/
|
||||
add_task(function respect_privacy_level() {
|
||||
let tab = yield createTabWithStorageData(["http://example.com", "https://example.com"]);
|
||||
let tab = gBrowser.addTab(URL + "&secure");
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
gBrowser.removeTab(tab);
|
||||
|
||||
let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
|
||||
is(storage["http://example.com"].test, INITIAL_VALUE,
|
||||
is(storage["http://mochi.test:8888"].test, OUTER_VALUE,
|
||||
"http sessionStorage data has been saved");
|
||||
is(storage["https://example.com"].test, INITIAL_VALUE,
|
||||
is(storage["https://example.com"].test, INNER_VALUE,
|
||||
"https sessionStorage data has been saved");
|
||||
|
||||
// Disable saving data for encrypted sites.
|
||||
Services.prefs.setIntPref("browser.sessionstore.privacy_level", 1);
|
||||
|
||||
let tab = yield createTabWithStorageData(["http://example.com", "https://example.com"]);
|
||||
let tab = gBrowser.addTab(URL + "&secure");
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
gBrowser.removeTab(tab);
|
||||
|
||||
let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
|
||||
is(storage["http://example.com"].test, INITIAL_VALUE,
|
||||
is(storage["http://mochi.test:8888"].test, OUTER_VALUE,
|
||||
"http sessionStorage data has been saved");
|
||||
ok(!storage["https://example.com"],
|
||||
"https sessionStorage data has *not* been saved");
|
||||
@ -140,17 +144,15 @@ add_task(function respect_privacy_level() {
|
||||
Services.prefs.setIntPref("browser.sessionstore.privacy_level", 2);
|
||||
|
||||
// Check that duplicating a tab copies all private data.
|
||||
let tab = yield createTabWithStorageData(["http://example.com", "https://example.com"]);
|
||||
let tab = gBrowser.addTab(URL + "&secure");
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
let tab2 = gBrowser.duplicateTab(tab);
|
||||
yield promiseBrowserLoaded(tab2.linkedBrowser);
|
||||
yield promiseTabRestored(tab2);
|
||||
gBrowser.removeTab(tab);
|
||||
|
||||
// With privacy_level=2 the |tab| shouldn't have any sessionStorage data.
|
||||
let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
|
||||
ok(!storage["http://example.com"],
|
||||
"http sessionStorage data has *not* been saved");
|
||||
ok(!storage["https://example.com"],
|
||||
"https sessionStorage data has *not* been saved");
|
||||
ok(!storage, "sessionStorage data has *not* been saved");
|
||||
|
||||
// Restore the default privacy level and close the duplicated tab.
|
||||
Services.prefs.clearUserPref("browser.sessionstore.privacy_level");
|
||||
@ -158,42 +160,26 @@ add_task(function respect_privacy_level() {
|
||||
|
||||
// With privacy_level=0 the duplicated |tab2| should persist all data.
|
||||
let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
|
||||
is(storage["http://example.com"].test, INITIAL_VALUE,
|
||||
is(storage["http://mochi.test:8888"].test, OUTER_VALUE,
|
||||
"http sessionStorage data has been saved");
|
||||
is(storage["https://example.com"].test, INITIAL_VALUE,
|
||||
is(storage["https://example.com"].test, INNER_VALUE,
|
||||
"https sessionStorage data has been saved");
|
||||
});
|
||||
|
||||
function createTabWithStorageData(urls) {
|
||||
return Task.spawn(function task() {
|
||||
let tab = gBrowser.addTab();
|
||||
let browser = tab.linkedBrowser;
|
||||
|
||||
for (let url of urls) {
|
||||
browser.loadURI(url);
|
||||
yield promiseBrowserLoaded(browser);
|
||||
yield modifySessionStorage(browser, {test: INITIAL_VALUE});
|
||||
}
|
||||
|
||||
throw new Task.Result(tab);
|
||||
});
|
||||
}
|
||||
|
||||
function waitForStorageEvent(browser) {
|
||||
return promiseContentMessage(browser, "ss-test:MozStorageChanged");
|
||||
}
|
||||
|
||||
function waitForUpdateMessage(browser) {
|
||||
return promiseContentMessage(browser, "SessionStore:update");
|
||||
}
|
||||
|
||||
function modifySessionStorage(browser, data) {
|
||||
browser.messageManager.sendAsyncMessage("ss-test:modifySessionStorage", data);
|
||||
return waitForStorageEvent(browser);
|
||||
}
|
||||
|
||||
function notifyObservers(browser, topic, data) {
|
||||
let msg = {topic: topic, data: data};
|
||||
browser.messageManager.sendAsyncMessage("ss-test:notifyObservers", msg);
|
||||
return waitForUpdateMessage(browser);
|
||||
function modifySessionStorage2(browser, data) {
|
||||
browser.messageManager.sendAsyncMessage("ss-test:modifySessionStorage2", data);
|
||||
return waitForStorageEvent(browser);
|
||||
}
|
||||
|
||||
function purgeDomainData(browser, domain) {
|
||||
return sendMessage(browser, "ss-test:purgeDomainData", domain);
|
||||
}
|
||||
|
@ -33,8 +33,15 @@ addMessageListener("ss-test:modifySessionStorage", function (msg) {
|
||||
}
|
||||
});
|
||||
|
||||
addMessageListener("ss-test:notifyObservers", function ({data: {topic, data}}) {
|
||||
Services.obs.notifyObservers(null, topic, data || "");
|
||||
addMessageListener("ss-test:modifySessionStorage2", function (msg) {
|
||||
for (let key of Object.keys(msg.data)) {
|
||||
content.frames[0].sessionStorage[key] = msg.data[key];
|
||||
}
|
||||
});
|
||||
|
||||
addMessageListener("ss-test:purgeDomainData", function ({data: domain}) {
|
||||
Services.obs.notifyObservers(null, "browser:purge-domain-data", domain);
|
||||
content.setTimeout(() => sendAsyncMessage("ss-test:purgeDomainData"));
|
||||
});
|
||||
|
||||
addMessageListener("ss-test:getStyleSheets", function (msg) {
|
||||
|
@ -61,44 +61,59 @@ ManifestEditor.prototype = {
|
||||
return this.update();
|
||||
},
|
||||
|
||||
_onEval: function(evalString) {
|
||||
let manifest = this.manifest;
|
||||
eval("manifest" + evalString);
|
||||
_onEval: function(variable, value) {
|
||||
let parent = this._descend(variable.ownerView.symbolicPath);
|
||||
try {
|
||||
parent[variable.name] = JSON.parse(value);
|
||||
} catch(e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
|
||||
this.update();
|
||||
},
|
||||
|
||||
_onSwitch: function(variable, newName) {
|
||||
let manifest = this.manifest;
|
||||
let newSymbolicName = variable.ownerView.symbolicName +
|
||||
"['" + newName + "']";
|
||||
if (newSymbolicName == variable.symbolicName) {
|
||||
if (variable.name == newName) {
|
||||
return;
|
||||
}
|
||||
|
||||
let evalString = "manifest" + newSymbolicName + " = " +
|
||||
"manifest" + variable.symbolicName + ";" +
|
||||
"delete manifest" + variable.symbolicName;
|
||||
let parent = this._descend(variable.ownerView.symbolicPath);
|
||||
parent[newName] = parent[variable.name];
|
||||
delete parent[variable.name];
|
||||
|
||||
eval(evalString);
|
||||
this.update();
|
||||
},
|
||||
|
||||
_onDelete: function(variable) {
|
||||
let manifest = this.manifest;
|
||||
let evalString = "delete manifest" + variable.symbolicName;
|
||||
eval(evalString);
|
||||
let parent = this._descend(variable.ownerView.symbolicPath);
|
||||
delete parent[variable.name];
|
||||
},
|
||||
|
||||
_onNew: function(variable, newName, newValue) {
|
||||
let manifest = this.manifest;
|
||||
let symbolicName = variable.symbolicName + "['" + newName + "']";
|
||||
let evalString = "manifest" + symbolicName + " = " + newValue + ";";
|
||||
eval(evalString);
|
||||
let parent = this._descend(variable.symbolicPath);
|
||||
try {
|
||||
parent[newName] = JSON.parse(newValue);
|
||||
} catch(e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
|
||||
this.update();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the value located at a given path in the manifest.
|
||||
* @param path array
|
||||
* A string for each path component: ["developer", "name"]
|
||||
*/
|
||||
_descend: function(path) {
|
||||
let parent = this.manifest;
|
||||
while (path.length) {
|
||||
parent = parent[path.shift()];
|
||||
}
|
||||
return parent;
|
||||
},
|
||||
|
||||
update: function() {
|
||||
this.editor.createHierarchy();
|
||||
this.editor.rawObject = this.manifest;
|
||||
this.editor.commitHierarchy();
|
||||
|
||||
|
@ -6,7 +6,6 @@ const {Services} = Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const MANIFEST_EDITOR_ENABLED = "devtools.appmanager.manifestEditor.enabled";
|
||||
|
||||
|
||||
let gManifestWindow, gManifestEditor;
|
||||
|
||||
function test() {
|
||||
@ -22,7 +21,9 @@ function test() {
|
||||
gManifestWindow = getManifestWindow();
|
||||
gManifestEditor = getProjectsWindow().UI.manifestEditor;
|
||||
yield changeManifestValue("name", "the best app");
|
||||
yield changeManifestValueBad("name", "the worst app");
|
||||
yield addNewManifestProperty("developer", "foo", "bar");
|
||||
yield addNewManifestPropertyBad("developer", "blob", "bob");
|
||||
gManifestWindow = null;
|
||||
gManifestEditor = null;
|
||||
|
||||
@ -64,6 +65,33 @@ function changeManifestValue(key, value) {
|
||||
});
|
||||
}
|
||||
|
||||
function changeManifestValueBad(key, value) {
|
||||
return Task.spawn(function() {
|
||||
let propElem = gManifestWindow.document
|
||||
.querySelector("[id ^= '" + key + "']");
|
||||
is(propElem.querySelector(".name").value, key,
|
||||
"Key doesn't match expected value");
|
||||
|
||||
let valueElem = propElem.querySelector(".value");
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, valueElem, gManifestWindow);
|
||||
|
||||
let valueInput = propElem.querySelector(".element-value-input");
|
||||
// Leaving out quotes will result in an error, so no change should be made.
|
||||
valueInput.value = value;
|
||||
EventUtils.sendKey("RETURN", gManifestWindow);
|
||||
|
||||
yield waitForUpdate();
|
||||
// Elements have all been replaced, re-select them
|
||||
propElem = gManifestWindow.document.querySelector("[id ^= '" + key + "']");
|
||||
valueElem = propElem.querySelector(".value");
|
||||
isnot(valueElem.value, '"' + value + '"',
|
||||
"Value was changed, but it should not have been");
|
||||
|
||||
isnot(gManifestEditor.manifest[key], value,
|
||||
"Manifest was changed, but it should not have been");
|
||||
});
|
||||
}
|
||||
|
||||
function addNewManifestProperty(parent, key, value) {
|
||||
return Task.spawn(function() {
|
||||
let parentElem = gManifestWindow.document
|
||||
@ -103,3 +131,34 @@ function addNewManifestProperty(parent, key, value) {
|
||||
"Manifest doesn't contain expected value");
|
||||
});
|
||||
}
|
||||
|
||||
function addNewManifestPropertyBad(parent, key, value) {
|
||||
return Task.spawn(function() {
|
||||
let parentElem = gManifestWindow.document
|
||||
.querySelector("[id ^= '" + parent + "']");
|
||||
ok(parentElem,
|
||||
"Found parent element");
|
||||
let addPropertyElem = parentElem
|
||||
.querySelector(".variables-view-add-property");
|
||||
ok(addPropertyElem,
|
||||
"Found add-property button");
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, addPropertyElem, gManifestWindow);
|
||||
|
||||
let nameInput = parentElem.querySelector(".element-name-input");
|
||||
nameInput.value = key;
|
||||
EventUtils.sendKey("TAB", gManifestWindow);
|
||||
|
||||
let valueInput = parentElem.querySelector(".element-value-input");
|
||||
// Leaving out quotes will result in an error, so no change should be made.
|
||||
valueInput.value = value;
|
||||
EventUtils.sendKey("RETURN", gManifestWindow);
|
||||
|
||||
yield waitForUpdate();
|
||||
|
||||
let newElem = gManifestWindow.document.querySelector("[id ^= '" + key + "']");
|
||||
ok(!newElem, "Key was added, but it should not have been");
|
||||
ok(!(key in gManifestEditor.manifest[parent]),
|
||||
"Manifest contains key, but it should not");
|
||||
});
|
||||
}
|
||||
|
@ -841,7 +841,6 @@ StackFrames.prototype = {
|
||||
|
||||
// Start recording any added variables or properties in any scope and
|
||||
// clear existing scopes to create each one dynamically.
|
||||
DebuggerView.Variables.createHierarchy();
|
||||
DebuggerView.Variables.empty();
|
||||
|
||||
// If watch expressions evaluation results are available, create a scope
|
||||
@ -1540,6 +1539,7 @@ Tracer.prototype = {
|
||||
location: location,
|
||||
id: this._idCounter++
|
||||
};
|
||||
|
||||
this._stack.push(item);
|
||||
DebuggerView.Tracer.addTrace({
|
||||
type: "call",
|
||||
@ -1598,7 +1598,7 @@ Tracer.prototype = {
|
||||
getOwnPropertyNames: (callback) => {
|
||||
callback({
|
||||
ownPropertyNames: aObject.ownProperties
|
||||
? Object.keys(aObject.ownProperties)
|
||||
? Object.keys(aObject.ownProperties)
|
||||
: []
|
||||
});
|
||||
},
|
||||
|
@ -1088,8 +1088,10 @@ function TracerView() {
|
||||
DevToolsUtils.makeInfallible(this._onToggleTracing.bind(this));
|
||||
this._onStartTracing =
|
||||
DevToolsUtils.makeInfallible(this._onStartTracing.bind(this));
|
||||
this._onClear = DevToolsUtils.makeInfallible(this._onClear.bind(this));
|
||||
this._onSelect = DevToolsUtils.makeInfallible(this._onSelect.bind(this));
|
||||
this._onClear =
|
||||
DevToolsUtils.makeInfallible(this._onClear.bind(this));
|
||||
this._onSelect =
|
||||
DevToolsUtils.makeInfallible(this._onSelect.bind(this));
|
||||
this._onMouseOver =
|
||||
DevToolsUtils.makeInfallible(this._onMouseOver.bind(this));
|
||||
this._onSearch = DevToolsUtils.makeInfallible(this._onSearch.bind(this));
|
||||
@ -1118,13 +1120,10 @@ TracerView.prototype = Heritage.extend(WidgetMethods, {
|
||||
}
|
||||
|
||||
this.widget = new FastListWidget(document.getElementById("tracer-traces"));
|
||||
|
||||
this._traceButton.removeAttribute("hidden");
|
||||
this._tracerTab.removeAttribute("hidden");
|
||||
|
||||
this._tracerDeck = document.getElementById("tracer-deck");
|
||||
this._search = document.getElementById("tracer-search");
|
||||
|
||||
this._template = document.getElementsByClassName("trace-item-template")[0];
|
||||
this._templateItem = this._template.getElementsByClassName("trace-item")[0];
|
||||
this._templateTypeIcon = this._template.getElementsByClassName("trace-type")[0];
|
||||
@ -1133,12 +1132,15 @@ TracerView.prototype = Heritage.extend(WidgetMethods, {
|
||||
this.widget.addEventListener("select", this._onSelect, false);
|
||||
this.widget.addEventListener("mouseover", this._onMouseOver, false);
|
||||
this.widget.addEventListener("mouseout", this._unhighlightMatchingItems, false);
|
||||
|
||||
this._search.addEventListener("input", this._onSearch, false);
|
||||
|
||||
this._startTooltip = L10N.getStr("startTracingTooltip");
|
||||
this._stopTooltip = L10N.getStr("stopTracingTooltip");
|
||||
this._tracingNotStartedString = L10N.getStr("tracingNotStartedText");
|
||||
this._noFunctionCallsString = L10N.getStr("noFunctionCallsText");
|
||||
|
||||
this._traceButton.setAttribute("tooltiptext", this._startTooltip);
|
||||
this.emptyText = this._tracingNotStartedString;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1171,23 +1173,38 @@ TracerView.prototype = Heritage.extend(WidgetMethods, {
|
||||
/**
|
||||
* Function invoked either by the "startTracing" command or by
|
||||
* _onToggleTracing to start execution tracing in the backend.
|
||||
*
|
||||
* @return object
|
||||
* A promise resolved once the tracing has successfully started.
|
||||
*/
|
||||
_onStartTracing: function() {
|
||||
this._tracerDeck.selectedIndex = 0;
|
||||
this._traceButton.setAttribute("checked", true);
|
||||
this._traceButton.setAttribute("tooltiptext", this._stopTooltip);
|
||||
|
||||
this.empty();
|
||||
DebuggerController.Tracer.startTracing();
|
||||
this.emptyText = this._noFunctionCallsString;
|
||||
|
||||
let deferred = promise.defer();
|
||||
DebuggerController.Tracer.startTracing(deferred.resolve);
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Function invoked by _onToggleTracing to stop execution tracing in the
|
||||
* backend.
|
||||
*
|
||||
* @return object
|
||||
* A promise resolved once the tracing has successfully stopped.
|
||||
*/
|
||||
_onStopTracing: function() {
|
||||
this._traceButton.removeAttribute("checked");
|
||||
this._traceButton.setAttribute("tooltiptext", this._startTooltip);
|
||||
DebuggerController.Tracer.stopTracing();
|
||||
|
||||
this.emptyText = this._tracingNotStartedString;
|
||||
|
||||
let deferred = promise.defer();
|
||||
DebuggerController.Tracer.stopTracing(deferred.resolve);
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1336,7 +1353,6 @@ TracerView.prototype = Heritage.extend(WidgetMethods, {
|
||||
selectTab: function() {
|
||||
const tabs = this._tracerTab.parentElement;
|
||||
tabs.selectedIndex = Array.indexOf(tabs.children, this._tracerTab);
|
||||
this._tracerDeck.selectedIndex = 0;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1856,8 +1872,9 @@ VariableBubbleView.prototype = {
|
||||
this._tooltip.setVariableContent(objectActor, {
|
||||
searchPlaceholder: L10N.getStr("emptyPropertiesFilterText"),
|
||||
searchEnabled: Prefs.variablesSearchboxVisible,
|
||||
eval: aString => {
|
||||
DebuggerController.StackFrames.evaluate(aString);
|
||||
eval: (variable, value) => {
|
||||
let string = variable.evaluationMacro(variable, value);
|
||||
DebuggerController.StackFrames.evaluate(string);
|
||||
DebuggerView.VariableBubble.hideContents();
|
||||
}
|
||||
}, {
|
||||
|
@ -162,7 +162,10 @@ let DebuggerView = {
|
||||
emptyText: L10N.getStr("emptyVariablesText"),
|
||||
onlyEnumVisible: Prefs.variablesOnlyEnumVisible,
|
||||
searchEnabled: Prefs.variablesSearchboxVisible,
|
||||
eval: DebuggerController.StackFrames.evaluate,
|
||||
eval: (variable, value) => {
|
||||
let string = variable.evaluationMacro(variable, value);
|
||||
DebuggerController.StackFrames.evaluate(string);
|
||||
},
|
||||
lazyEmpty: true
|
||||
});
|
||||
|
||||
|
@ -372,36 +372,25 @@
|
||||
<tabpanel id="callstack-tabpanel">
|
||||
<vbox id="callstack-list" flex="1"/>
|
||||
</tabpanel>
|
||||
<tabpanel id="tracer-tabpanel" flex="1">
|
||||
<deck id="tracer-deck" selectedIndex="1" flex="1">
|
||||
<vbox flex="1">
|
||||
<vbox id="tracer-traces" flex="1">
|
||||
<hbox class="trace-item-template" hidden="true">
|
||||
<hbox class="trace-item" align="center" flex="1" crop="end">
|
||||
<label class="trace-type plain"/>
|
||||
<label class="trace-name plain" crop="end"/>
|
||||
</hbox>
|
||||
</hbox>
|
||||
</vbox>
|
||||
<toolbar id="tracer-toolbar" class="devtools-toolbar">
|
||||
<toolbarbutton id="clear-tracer"
|
||||
label="&debuggerUI.clearButton;"
|
||||
tooltiptext="&debuggerUI.clearButton.tooltip;"
|
||||
command="clearTraces"
|
||||
class="devtools-toolbarbutton"/>
|
||||
<textbox id="tracer-search"
|
||||
class="devtools-searchinput"
|
||||
flex="1"
|
||||
type="search"/>
|
||||
</toolbar>
|
||||
</vbox>
|
||||
<vbox id="tracer-message"
|
||||
flex="1"
|
||||
align="center"
|
||||
pack="center">
|
||||
<description value="&debuggerUI.tracingNotStarted.label;" />
|
||||
</vbox>
|
||||
</deck>
|
||||
<tabpanel id="tracer-tabpanel">
|
||||
<vbox id="tracer-traces" flex="1"/>
|
||||
<hbox class="trace-item-template" hidden="true">
|
||||
<hbox class="trace-item" align="center" flex="1" crop="end">
|
||||
<label class="trace-type plain"/>
|
||||
<label class="trace-name plain" crop="end"/>
|
||||
</hbox>
|
||||
</hbox>
|
||||
<toolbar id="tracer-toolbar" class="devtools-toolbar">
|
||||
<toolbarbutton id="clear-tracer"
|
||||
label="&debuggerUI.clearButton;"
|
||||
tooltiptext="&debuggerUI.clearButton.tooltip;"
|
||||
command="clearTraces"
|
||||
class="devtools-toolbarbutton"/>
|
||||
<textbox id="tracer-search"
|
||||
class="devtools-searchinput"
|
||||
flex="1"
|
||||
type="search"/>
|
||||
</toolbar>
|
||||
</tabpanel>
|
||||
</tabpanels>
|
||||
</tabbox>
|
||||
|
@ -203,6 +203,7 @@ support-files =
|
||||
[browser_dbg_tracing-02.js]
|
||||
[browser_dbg_tracing-03.js]
|
||||
[browser_dbg_tracing-04.js]
|
||||
[browser_dbg_tracing-05.js]
|
||||
[browser_dbg_variables-view-01.js]
|
||||
[browser_dbg_variables-view-02.js]
|
||||
[browser_dbg_variables-view-03.js]
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
|
||||
|
||||
let gTab, gDebuggee, gPanel, gDebugger, gVariables;
|
||||
let gTab, gDebuggee, gPanel, gDebugger;
|
||||
|
||||
function test() {
|
||||
SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]}, () => {
|
||||
@ -16,7 +16,6 @@ function test() {
|
||||
gDebuggee = aDebuggee;
|
||||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
gVariables = gDebugger.DebuggerView.Variables;
|
||||
|
||||
waitForSourceShown(gPanel, "code_tracing-01.js")
|
||||
.then(() => startTracing(gPanel))
|
||||
@ -82,5 +81,4 @@ registerCleanupFunction(function() {
|
||||
gDebuggee = null;
|
||||
gPanel = null;
|
||||
gDebugger = null;
|
||||
gVariables = null;
|
||||
});
|
||||
|
85
browser/devtools/debugger/test/browser_dbg_tracing-05.js
Normal file
85
browser/devtools/debugger/test/browser_dbg_tracing-05.js
Normal file
@ -0,0 +1,85 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that text describing the tracing state is correctly displayed.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
|
||||
|
||||
let gTab, gDebuggee, gPanel, gDebugger;
|
||||
let gTracer, gL10N;
|
||||
|
||||
function test() {
|
||||
SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]}, () => {
|
||||
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
|
||||
gTab = aTab;
|
||||
gDebuggee = aDebuggee;
|
||||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
gTracer = gDebugger.DebuggerView.Tracer;
|
||||
gL10N = gDebugger.L10N;
|
||||
|
||||
waitForSourceShown(gPanel, "code_tracing-01.js")
|
||||
.then(testTracingNotStartedText)
|
||||
.then(() => gTracer._onStartTracing())
|
||||
.then(testFunctionCallsUnavailableText)
|
||||
.then(clickButton)
|
||||
.then(() => waitForClientEvents(aPanel, "traces"))
|
||||
.then(testNoEmptyText)
|
||||
.then(() => gTracer._onClear())
|
||||
.then(testFunctionCallsUnavailableText)
|
||||
.then(() => gTracer._onStopTracing())
|
||||
.then(testTracingNotStartedText)
|
||||
.then(() => gTracer._onClear())
|
||||
.then(testTracingNotStartedText)
|
||||
.then(() => {
|
||||
const deferred = promise.defer();
|
||||
SpecialPowers.popPrefEnv(deferred.resolve);
|
||||
return deferred.promise;
|
||||
})
|
||||
.then(() => closeDebuggerAndFinish(gPanel))
|
||||
.then(null, aError => {
|
||||
DevToolsUtils.reportException("browser_dbg_tracing-05.js", aError);
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testTracingNotStartedText() {
|
||||
let label = gDebugger.document.querySelector("#tracer-tabpanel .fast-list-widget-empty-text");
|
||||
ok(label,
|
||||
"A label is displayed in the tracer tabpanel.");
|
||||
is(label.getAttribute("value"), gL10N.getStr("tracingNotStartedText"),
|
||||
"The correct {{tracingNotStartedText}} is displayed in the tracer tabpanel.");
|
||||
}
|
||||
|
||||
function testFunctionCallsUnavailableText() {
|
||||
let label = gDebugger.document.querySelector("#tracer-tabpanel .fast-list-widget-empty-text");
|
||||
ok(label,
|
||||
"A label is displayed in the tracer tabpanel.");
|
||||
is(label.getAttribute("value"), gL10N.getStr("noFunctionCallsText"),
|
||||
"The correct {{noFunctionCallsText}} is displayed in the tracer tabpanel.");
|
||||
}
|
||||
|
||||
function testNoEmptyText() {
|
||||
let label = gDebugger.document.querySelector("#tracer-tabpanel .fast-list-widget-empty-text");
|
||||
ok(!label,
|
||||
"No label should be displayed in the tracer tabpanel.");
|
||||
}
|
||||
|
||||
function clickButton() {
|
||||
EventUtils.sendMouseEvent({ type: "click" },
|
||||
gDebuggee.document.querySelector("button"),
|
||||
gDebuggee);
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gTab = null;
|
||||
gDebuggee = null;
|
||||
gPanel = null;
|
||||
gDebugger = null;
|
||||
gTracer = null;
|
||||
gL10N = null;
|
||||
});
|
@ -45,6 +45,7 @@ const FastListWidget = module.exports = function FastListWidget(aNode) {
|
||||
|
||||
// Delegate some of the associated node's methods to satisfy the interface
|
||||
// required by MenuContainer instances.
|
||||
ViewHelpers.delegateWidgetAttributeMethods(this, aNode);
|
||||
ViewHelpers.delegateWidgetEventMethods(this, aNode);
|
||||
}
|
||||
|
||||
@ -152,18 +153,6 @@ FastListWidget.prototype = {
|
||||
return this._orderedMenuElementsArray[index];
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the value of the named attribute on this container.
|
||||
*
|
||||
* @param string name
|
||||
* The name of the attribute.
|
||||
* @return string
|
||||
* The current attribute value.
|
||||
*/
|
||||
getAttribute: function(name) {
|
||||
return this._parent.getAttribute(name);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a new attribute or changes an existing attribute on this container.
|
||||
*
|
||||
@ -174,6 +163,10 @@ FastListWidget.prototype = {
|
||||
*/
|
||||
setAttribute: function(name, value) {
|
||||
this._parent.setAttribute(name, value);
|
||||
|
||||
if (name == "emptyText") {
|
||||
this._textWhenEmpty = value;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -184,6 +177,10 @@ FastListWidget.prototype = {
|
||||
*/
|
||||
removeAttribute: function(name) {
|
||||
this._parent.removeAttribute(name);
|
||||
|
||||
if (name == "emptyText") {
|
||||
this._removeEmptyText();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -203,11 +200,51 @@ FastListWidget.prototype = {
|
||||
boxObject.scrollBy(-this._list.clientWidth, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the text displayed in this container when empty.
|
||||
* @param string aValue
|
||||
*/
|
||||
set _textWhenEmpty(aValue) {
|
||||
if (this._emptyTextNode) {
|
||||
this._emptyTextNode.setAttribute("value", aValue);
|
||||
}
|
||||
this._emptyTextValue = aValue;
|
||||
this._showEmptyText();
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates and appends a label signaling that this container is empty.
|
||||
*/
|
||||
_showEmptyText: function() {
|
||||
if (this._emptyTextNode || !this._emptyTextValue) {
|
||||
return;
|
||||
}
|
||||
let label = this.document.createElement("label");
|
||||
label.className = "plain fast-list-widget-empty-text";
|
||||
label.setAttribute("value", this._emptyTextValue);
|
||||
|
||||
this._parent.insertBefore(label, this._list);
|
||||
this._emptyTextNode = label;
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the label signaling that this container is empty.
|
||||
*/
|
||||
_removeEmptyText: function() {
|
||||
if (!this._emptyTextNode) {
|
||||
return;
|
||||
}
|
||||
this._parent.removeChild(this._emptyTextNode);
|
||||
this._emptyTextNode = null;
|
||||
},
|
||||
|
||||
window: null,
|
||||
document: null,
|
||||
_parent: null,
|
||||
_list: null,
|
||||
_selectedItem: null,
|
||||
_orderedMenuElementsArray: null,
|
||||
_itemsByElement: null
|
||||
_itemsByElement: null,
|
||||
_emptyTextNode: null,
|
||||
_emptyTextValue: ""
|
||||
};
|
||||
|
@ -295,7 +295,7 @@ SideMenuWidget.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the text displayed in this container as a when empty.
|
||||
* Sets the text displayed in this container when empty.
|
||||
* @param string aValue
|
||||
*/
|
||||
set _textWhenEmpty(aValue) {
|
||||
|
@ -189,7 +189,7 @@ SimpleListWidget.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the text displayed in this container as a when empty.
|
||||
* Sets the text displayed in this container when empty.
|
||||
* @param string aValue
|
||||
*/
|
||||
set _textWhenEmpty(aValue) {
|
||||
|
@ -154,21 +154,22 @@ VariablesView.prototype = {
|
||||
if (!this._store.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._store.length = 0;
|
||||
this._itemsByElement.clear();
|
||||
this._prevHierarchy = this._currHierarchy;
|
||||
this._currHierarchy = new Map(); // Don't clear, this is just simple swapping.
|
||||
|
||||
// Check if this empty operation may be executed lazily.
|
||||
if (this.lazyEmpty && aTimeout > 0) {
|
||||
this._emptySoon(aTimeout);
|
||||
return;
|
||||
}
|
||||
|
||||
let list = this._list;
|
||||
|
||||
while (list.hasChildNodes()) {
|
||||
list.firstChild.remove();
|
||||
while (this._list.hasChildNodes()) {
|
||||
this._list.firstChild.remove();
|
||||
}
|
||||
|
||||
this._store.length = 0;
|
||||
this._itemsByElement.clear();
|
||||
|
||||
this._appendEmptyNotice();
|
||||
this._toggleSearchVisibility(false);
|
||||
},
|
||||
@ -192,9 +193,6 @@ VariablesView.prototype = {
|
||||
let prevList = this._list;
|
||||
let currList = this._list = this.document.createElement("scrollbox");
|
||||
|
||||
this._store.length = 0;
|
||||
this._itemsByElement.clear();
|
||||
|
||||
this.window.setTimeout(() => {
|
||||
prevList.removeEventListener("keypress", this._onViewKeyPress, false);
|
||||
prevList.removeEventListener("keydown", this._onViewKeyDown, false);
|
||||
@ -1173,7 +1171,7 @@ VariablesView.getterOrSetterDeleteCallback = function(aItem) {
|
||||
|
||||
// Make sure the right getter/setter to value override macro is applied
|
||||
// to the target object.
|
||||
aItem.ownerView.eval(aItem.evaluationMacro(aItem, ""));
|
||||
aItem.ownerView.eval(aItem, "");
|
||||
|
||||
return true; // Don't hide the element.
|
||||
};
|
||||
@ -2291,12 +2289,42 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets this variable's path to the topmost scope.
|
||||
* Gets this variable's path to the topmost scope in the form of a string
|
||||
* meant for use via eval() or a similar approach.
|
||||
* For example, a symbolic name may look like "arguments['0']['foo']['bar']".
|
||||
* @return string
|
||||
*/
|
||||
get symbolicName() this._symbolicName,
|
||||
|
||||
/**
|
||||
* Gets this variable's symbolic path to the topmost scope.
|
||||
* @return array
|
||||
* @see Variable._buildSymbolicPath
|
||||
*/
|
||||
get symbolicPath() {
|
||||
if (this._symbolicPath) {
|
||||
return this._symbolicPath;
|
||||
}
|
||||
this._symbolicPath = this._buildSymbolicPath();
|
||||
return this._symbolicPath;
|
||||
},
|
||||
|
||||
/**
|
||||
* Build this variable's path to the topmost scope in form of an array of
|
||||
* strings, one for each segment of the path.
|
||||
* For example, a symbolic path may look like ["0", "foo", "bar"].
|
||||
* @return array
|
||||
*/
|
||||
_buildSymbolicPath: function(path = []) {
|
||||
if (this.name) {
|
||||
path.unshift(this.name);
|
||||
if (this.ownerView instanceof Variable) {
|
||||
return this.ownerView._buildSymbolicPath(path);
|
||||
}
|
||||
}
|
||||
return path;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns this variable's value from the descriptor if available.
|
||||
* @return any
|
||||
@ -2715,7 +2743,7 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
if (!this._variablesView.preventDisableOnChange) {
|
||||
this._disable();
|
||||
}
|
||||
this.ownerView.eval(this.evaluationMacro(this, aString));
|
||||
this.ownerView.eval(this, aString);
|
||||
}
|
||||
}, e);
|
||||
},
|
||||
@ -2806,6 +2834,7 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
},
|
||||
|
||||
_symbolicName: "",
|
||||
_symbolicPath: null,
|
||||
_absoluteName: "",
|
||||
_initialDescriptor: null,
|
||||
_separatorLabel: null,
|
||||
@ -2858,7 +2887,7 @@ Property.prototype["@@iterator"] = function*() {
|
||||
|
||||
/**
|
||||
* Forget everything recorded about added scopes, variables or properties.
|
||||
* @see VariablesView.createHierarchy
|
||||
* @see VariablesView.commitHierarchy
|
||||
*/
|
||||
VariablesView.prototype.clearHierarchy = function() {
|
||||
this._prevHierarchy.clear();
|
||||
@ -2866,17 +2895,13 @@ VariablesView.prototype.clearHierarchy = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Start recording a hierarchy of any added scopes, variables or properties.
|
||||
* @see VariablesView.commitHierarchy
|
||||
*/
|
||||
VariablesView.prototype.createHierarchy = function() {
|
||||
this._prevHierarchy = this._currHierarchy;
|
||||
this._currHierarchy = new Map(); // Don't clear, this is just simple swapping.
|
||||
};
|
||||
|
||||
/**
|
||||
* Briefly flash the variables that changed between the previous and current
|
||||
* scope/variable/property hierarchies and reopen previously expanded nodes.
|
||||
* Perform operations on all the VariablesView Scopes, Variables and Properties
|
||||
* after you've added all the items you wanted.
|
||||
*
|
||||
* Calling this method is optional, and does the following:
|
||||
* - styles the items overridden by other items in parent scopes
|
||||
* - reopens the items which were previously expanded
|
||||
* - flashes the items whose values changed
|
||||
*/
|
||||
VariablesView.prototype.commitHierarchy = function() {
|
||||
for (let [, currItem] of this._currHierarchy) {
|
||||
|
@ -767,7 +767,11 @@ this.WidgetMethods = {
|
||||
*/
|
||||
set emptyText(aValue) {
|
||||
this._emptyText = aValue;
|
||||
this._widget.setAttribute("emptyText", aValue);
|
||||
|
||||
// Apply the emptyText attribute right now if there are no child items.
|
||||
if (!this._itemsByElement.size) {
|
||||
this._widget.setAttribute("emptyText", aValue);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -20,6 +20,12 @@
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* FastListWidget */
|
||||
|
||||
.fast-list-widget-container {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* SideMenuWidget */
|
||||
|
||||
.side-menu-widget-container {
|
||||
|
@ -3469,7 +3469,6 @@ JSTerm.prototype = {
|
||||
_updateVariablesView: function JST__updateVariablesView(aOptions)
|
||||
{
|
||||
let view = aOptions.view;
|
||||
view.createHierarchy();
|
||||
view.empty();
|
||||
|
||||
// We need to avoid pruning the object inspection starting point.
|
||||
@ -3516,20 +3515,24 @@ JSTerm.prototype = {
|
||||
* @private
|
||||
* @param object aOptions
|
||||
* The options used for |this._updateVariablesView()|.
|
||||
* @param string aString
|
||||
* The string that the variables view wants to evaluate.
|
||||
* @param object aVar
|
||||
* The Variable object instance for the edited property.
|
||||
* @param string aValue
|
||||
* The value the edited property was changed to.
|
||||
*/
|
||||
_variablesViewEvaluate: function JST__variablesViewEvaluate(aOptions, aString)
|
||||
_variablesViewEvaluate:
|
||||
function JST__variablesViewEvaluate(aOptions, aVar, aValue)
|
||||
{
|
||||
let updater = this._updateVariablesView.bind(this, aOptions);
|
||||
let onEval = this._silentEvalCallback.bind(this, updater);
|
||||
let string = aVar.evaluationMacro(aVar, aValue);
|
||||
|
||||
let evalOptions = {
|
||||
frame: this.SELECTED_FRAME,
|
||||
bindObjectActor: aOptions.objectActor.actor,
|
||||
};
|
||||
|
||||
this.requestEvaluation(aString, evalOptions).then(onEval, onEval);
|
||||
this.requestEvaluation(string, evalOptions).then(onEval, onEval);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -333,9 +333,14 @@ These should match what Safari and other Apple applications use on OS X Lion. --
|
||||
<!ENTITY customizeMenu.addToPanel.label "Add to Menu">
|
||||
<!ENTITY customizeMenu.addToPanel.accesskey "M">
|
||||
<!ENTITY customizeMenu.moveToToolbar.label "Move to Toolbar">
|
||||
<!ENTITY customizeMenu.moveToToolbar.accesskey "M">
|
||||
<!ENTITY customizeMenu.moveToToolbar.accesskey "o">
|
||||
<!-- LOCALIZATION NOTE (customizeMenu.moveToPanel.accesskey) can appear on the
|
||||
same context menu as menubarCmd and personalbarCmd, so they should have
|
||||
different access keys. customizeMenu.moveToToolbar and
|
||||
customizeMenu.moveToPanel are mutually exclusive, so can share access
|
||||
keys. -->
|
||||
<!ENTITY customizeMenu.moveToPanel.label "Move to Menu">
|
||||
<!ENTITY customizeMenu.moveToPanel.accesskey "M">
|
||||
<!ENTITY customizeMenu.moveToPanel.accesskey "o">
|
||||
<!ENTITY customizeMenu.removeFromToolbar.label "Remove from Toolbar">
|
||||
<!ENTITY customizeMenu.removeFromToolbar.accesskey "R">
|
||||
<!ENTITY customizeMenu.removeFromMenu.label "Remove from Menu">
|
||||
|
@ -50,10 +50,6 @@
|
||||
- button that toggles all breakpoints for all sources. -->
|
||||
<!ENTITY debuggerUI.sources.toggleBreakpoints "Enable/disable all breakpoints">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.tracingNotStarted.label): This is the text
|
||||
- displayed when tracing hasn't started in the debugger UI. -->
|
||||
<!ENTITY debuggerUI.tracingNotStarted.label "Tracing has not started.">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.startTracing): This is the text displayed in
|
||||
- the button to start execution tracing. -->
|
||||
<!ENTITY debuggerUI.startTracing "Start Tracing">
|
||||
|
@ -85,6 +85,14 @@ noEventListenersText=No event listeners to display
|
||||
# when there are no stack frames.
|
||||
noStackFramesText=No stack frames to display
|
||||
|
||||
# LOCALIZATION NOTE (noStackFramesText): The text to display in the traces tab
|
||||
# when there are no function calls.
|
||||
noFunctionCallsText=No function calls to display
|
||||
|
||||
# LOCALIZATION NOTE (tracingNotStartedText): The text to display in the traces tab
|
||||
# when when tracing hasn't started yet.
|
||||
tracingNotStartedText=Tracing has not started
|
||||
|
||||
# LOCALIZATION NOTE (eventCheckboxTooltip): The tooltip text to display when
|
||||
# the user hovers over the checkbox used to toggle an event breakpoint.
|
||||
eventCheckboxTooltip=Toggle breaking on this event
|
||||
|
@ -30,7 +30,8 @@
|
||||
</content>
|
||||
|
||||
<handlers>
|
||||
<handler event="click" clickcount="1" action="this._onClick()"/>
|
||||
<handler event="click" button="0" clickcount="1" action="this._onClick()"/>
|
||||
<handler event="click" button="1" clickcount="1" action="event.stopPropagation(); this._onClose()"/>
|
||||
<handler event="dblclick" action="this._onDoubleClick(); event.stopPropagation();"/>
|
||||
</handlers>
|
||||
|
||||
|
@ -32,15 +32,6 @@ function testState(aState) {
|
||||
}
|
||||
}
|
||||
|
||||
function notifyPrecise()
|
||||
{
|
||||
Services.obs.notifyObservers(null, "metro_precise_input", null);
|
||||
}
|
||||
|
||||
function notifyImprecise()
|
||||
{
|
||||
Services.obs.notifyObservers(null, "metro_imprecise_input", null);
|
||||
}
|
||||
|
||||
gTests.push({
|
||||
desc: "precise/imprecise input switcher",
|
||||
|
114
browser/metro/base/tests/mochitest/browser_tabs_container.js
Normal file
114
browser/metro/base/tests/mochitest/browser_tabs_container.js
Normal file
@ -0,0 +1,114 @@
|
||||
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
runTests();
|
||||
}
|
||||
|
||||
function setup() {
|
||||
yield waitForCondition(function () {
|
||||
return Elements.tabList && Elements.tabList.strip;
|
||||
});
|
||||
|
||||
if (!ContextUI.tabbarVisible) {
|
||||
ContextUI.displayTabs();
|
||||
}
|
||||
}
|
||||
function tearDown() {
|
||||
cleanUpOpenedTabs();
|
||||
}
|
||||
|
||||
function isElementVisible(elm) {
|
||||
return elm.ownerDocument.defaultView.getComputedStyle(elm).getPropertyValue("visibility") == "visible";
|
||||
}
|
||||
|
||||
|
||||
gTests.push({
|
||||
desc: "No scrollbuttons when tab strip doesnt require overflow",
|
||||
setUp: setup,
|
||||
run: function () {
|
||||
let tabStrip = Elements.tabList.strip;
|
||||
let tabs = Elements.tabList.strip.querySelectorAll("documenttab");
|
||||
|
||||
// sanity check tab count:
|
||||
is(tabs.length, 1, "1 tab present");
|
||||
|
||||
// test imprecise mode
|
||||
let imprecisePromise = waitForObserver("metro_imprecise_input");
|
||||
notifyImprecise();
|
||||
yield imprecisePromise;
|
||||
|
||||
ok(!isElementVisible(tabStrip._scrollButtonUp), "left scrollbutton is hidden in imprecise mode");
|
||||
ok(!isElementVisible(tabStrip._scrollButtonDown), "right scrollbutton is hidden in imprecise mode");
|
||||
|
||||
// test precise mode
|
||||
let precisePromise = waitForObserver("metro_precise_input");
|
||||
notifyPrecise();
|
||||
yield precisePromise;
|
||||
|
||||
todo(!isElementVisible(tabStrip._scrollButtonUp), "Bug 952297 - left scrollbutton is hidden in precise mode");
|
||||
ok(!isElementVisible(tabStrip._scrollButtonDown), "right scrollbutton is hidden in precise mode");
|
||||
},
|
||||
tearDown: tearDown
|
||||
});
|
||||
|
||||
gTests.push({
|
||||
desc: "Scrollbuttons not visible when tabstrip has overflow in imprecise input mode",
|
||||
setUp: function(){
|
||||
yield setup();
|
||||
// ensure we're in imprecise mode
|
||||
let imprecisePromise = waitForObserver("metro_imprecise_input");
|
||||
notifyImprecise();
|
||||
yield imprecisePromise;
|
||||
},
|
||||
run: function () {
|
||||
// add enough tabs to get overflow in the tabstrip
|
||||
let strip = Elements.tabList.strip;
|
||||
ok(strip && strip.scrollClientSize && strip.scrollSize, "Sanity check tabstrip strip is present and expected properties available");
|
||||
|
||||
while (strip.scrollSize <= strip.scrollClientSize) {
|
||||
yield addTab("about:mozilla");
|
||||
}
|
||||
|
||||
ok(!isElementVisible(Elements.tabList.strip._scrollButtonUp), "left scrollbutton is hidden in imprecise mode");
|
||||
ok(!isElementVisible(Elements.tabList.strip._scrollButtonDown), "right scrollbutton is hidden in imprecise mode");
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
gTests.push({
|
||||
desc: "Scrollbuttons become visible when tabstrip has overflow in precise input mode",
|
||||
setUp: function(){
|
||||
yield setup();
|
||||
// ensure we're in precise mode
|
||||
let precisePromise = waitForObserver("metro_precise_input");
|
||||
notifyPrecise();
|
||||
yield precisePromise;
|
||||
},
|
||||
run: function () {
|
||||
let strip = Elements.tabList.strip;
|
||||
ok(strip && strip.scrollClientSize && strip.scrollSize, "Sanity check tabstrip is present and expected properties available");
|
||||
|
||||
// add enough tabs to get overflow in the tabstrip
|
||||
while (strip.scrollSize <= strip.scrollClientSize) {
|
||||
yield addTab("about:mozilla");
|
||||
}
|
||||
|
||||
let tabs = Elements.tabList.strip.querySelectorAll("documenttab");
|
||||
// select the first tab
|
||||
Elements.tabs.selectedTab = tabs[0];
|
||||
ok(isElementVisible(Elements.tabList.strip._scrollButtonDown), "right scrollbutton should be visible when tabList has overflow");
|
||||
todo(!isElementVisible(Elements.tabList.strip._scrollButtonUp), "Bug 952297 - left scrollbutton should not visible when 1st tab is selected and tablist has overflow");
|
||||
|
||||
|
||||
// select the last tab
|
||||
Elements.tabs.selectedTab = tabs[tabs.length-1];
|
||||
ok(isElementVisible(Elements.tabList.strip._scrollButtonUp), "left scrollbutton should be visible when tablist has overflow and last tab is selected");
|
||||
todo(!isElementVisible(Elements.tabList.strip._scrollButtonDown), "Bug 952297 - right scrollbutton should not visible when last tab is selected and tablist has overflow");
|
||||
|
||||
}
|
||||
});
|
@ -556,6 +556,25 @@ function waitForObserver(aObsEvent, aTimeoutMs) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*=============================================================================
|
||||
* Input mode helpers - these helpers notify observers to metro_precise_input
|
||||
* and metro_imprecise_input respectively, triggering the same behaviour as user touch or mouse input
|
||||
*
|
||||
* Usage: let promise = waitForObservers("metro_imprecise_input");
|
||||
* notifyImprecise();
|
||||
* yield promise; // you are now in imprecise mode
|
||||
*===========================================================================*/
|
||||
function notifyPrecise()
|
||||
{
|
||||
Services.obs.notifyObservers(null, "metro_precise_input", null);
|
||||
}
|
||||
|
||||
function notifyImprecise()
|
||||
{
|
||||
Services.obs.notifyObservers(null, "metro_imprecise_input", null);
|
||||
}
|
||||
|
||||
/*=============================================================================
|
||||
* Native input helpers - these helpers send input directly to the os
|
||||
* generating os level input events that get processed by widget and
|
||||
|
@ -51,6 +51,7 @@ support-files =
|
||||
[browser_remotetabs.js]
|
||||
[browser_snappedState.js]
|
||||
[browser_tabs.js]
|
||||
[browser_tabs_container.js]
|
||||
[browser_test.js]
|
||||
[browser_tiles.js]
|
||||
[browser_topsites.js]
|
||||
|
@ -1233,15 +1233,6 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
|
||||
transition: height 100ms ease-out, width 100ms ease-out;
|
||||
}
|
||||
|
||||
#social-share-button {
|
||||
list-style-image: url("chrome://browser/skin/social/share-button.png");
|
||||
}
|
||||
|
||||
#social-share-button[open],
|
||||
#social-share-button:hover:active {
|
||||
list-style-image: url("chrome://browser/skin/social/share-button-active.png");
|
||||
}
|
||||
|
||||
.social-share-toolbar {
|
||||
border-right: 1px solid #dedede;
|
||||
background: linear-gradient(to bottom, rgba(247,247,247,.99), rgba(247,247,247,.95));
|
||||
|
@ -101,37 +101,17 @@
|
||||
-moz-image-region: rect(0px,32px,16px,16px);
|
||||
}
|
||||
|
||||
#start-tracing {
|
||||
padding: 4px;
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
#clear-tracer {
|
||||
/* Make this button as narrow as the text inside it. */
|
||||
min-width: 1px;
|
||||
}
|
||||
|
||||
#tracer-message {
|
||||
/* Prevent the container deck from aquiring the height from this message. */
|
||||
min-height: 1px;
|
||||
}
|
||||
|
||||
.trace-name {
|
||||
-moz-padding-start: 4px !important;
|
||||
}
|
||||
|
||||
#tracer-traces > scrollbox {
|
||||
overflow: scroll;
|
||||
/* Hack to enable hardware acceleration */
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
/* Tracer dark theme */
|
||||
|
||||
.theme-dark #tracer-message {
|
||||
color: #f5f7fa; /* Light foreground text */
|
||||
}
|
||||
|
||||
.theme-dark .trace-item {
|
||||
color: #f5f7fa; /* Light foreground text */
|
||||
}
|
||||
@ -167,10 +147,6 @@
|
||||
|
||||
/* Tracer light theme */
|
||||
|
||||
.theme-light #tracer-message {
|
||||
color: #292e33; /* Dark foreground text */
|
||||
}
|
||||
|
||||
.theme-light .trace-item {
|
||||
color: #292e33; /* Dark foreground text */
|
||||
}
|
||||
|
@ -283,6 +283,24 @@
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
/* FastListWidget */
|
||||
|
||||
.fast-list-widget-container {
|
||||
/* Hack: force hardware acceleration */
|
||||
transform: translateZ(1px);
|
||||
}
|
||||
|
||||
.theme-dark .fast-list-widget-empty-text {
|
||||
padding: 12px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.theme-light .fast-list-widget-empty-text {
|
||||
padding: 4px 8px;
|
||||
color: GrayText;
|
||||
}
|
||||
|
||||
/* SideMenuWidget */
|
||||
|
||||
.side-menu-widget-container {
|
||||
|
@ -103,37 +103,17 @@
|
||||
-moz-image-region: rect(0px,32px,16px,16px);
|
||||
}
|
||||
|
||||
#start-tracing {
|
||||
padding: 4px;
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
#clear-tracer {
|
||||
/* Make this button as narrow as the text inside it. */
|
||||
min-width: 1px;
|
||||
}
|
||||
|
||||
#tracer-message {
|
||||
/* Prevent the container deck from aquiring the height from this message. */
|
||||
min-height: 1px;
|
||||
}
|
||||
|
||||
.trace-name {
|
||||
-moz-padding-start: 4px !important;
|
||||
}
|
||||
|
||||
#tracer-traces > scrollbox {
|
||||
overflow: scroll;
|
||||
/* Hack to enable hardware acceleration */
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
/* Tracer dark theme */
|
||||
|
||||
.theme-dark #tracer-message {
|
||||
color: #f5f7fa; /* Light foreground text */
|
||||
}
|
||||
|
||||
.theme-dark .trace-item {
|
||||
color: #f5f7fa; /* Light foreground text */
|
||||
}
|
||||
@ -169,10 +149,6 @@
|
||||
|
||||
/* Tracer light theme */
|
||||
|
||||
.theme-light #tracer-message {
|
||||
color: #292e33; /* Dark foreground text */
|
||||
}
|
||||
|
||||
.theme-light .trace-item {
|
||||
color: #292e33; /* Dark foreground text */
|
||||
}
|
||||
|
@ -283,6 +283,24 @@
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
/* FastListWidget */
|
||||
|
||||
.fast-list-widget-container {
|
||||
/* Hack: force hardware acceleration */
|
||||
transform: translateZ(1px);
|
||||
}
|
||||
|
||||
.theme-dark .fast-list-widget-empty-text {
|
||||
padding: 12px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.theme-light .fast-list-widget-empty-text {
|
||||
padding: 4px 8px;
|
||||
color: GrayText;
|
||||
}
|
||||
|
||||
/* SideMenuWidget */
|
||||
|
||||
.side-menu-widget-container {
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 695 B After Width: | Height: | Size: 1.3 KiB |
@ -101,37 +101,17 @@
|
||||
-moz-image-region: rect(0px,32px,16px,16px);
|
||||
}
|
||||
|
||||
#start-tracing {
|
||||
padding: 4px;
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
#clear-tracer {
|
||||
/* Make this button as narrow as the text inside it. */
|
||||
min-width: 1px;
|
||||
}
|
||||
|
||||
#tracer-message {
|
||||
/* Prevent the container deck from aquiring the height from this message. */
|
||||
min-height: 1px;
|
||||
}
|
||||
|
||||
.trace-name {
|
||||
-moz-padding-start: 4px !important;
|
||||
}
|
||||
|
||||
#tracer-traces > scrollbox {
|
||||
overflow: scroll;
|
||||
/* Hack to enable hardware acceleration */
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
/* Tracer dark theme */
|
||||
|
||||
.theme-dark #tracer-message {
|
||||
color: #f5f7fa; /* Light foreground text */
|
||||
}
|
||||
|
||||
.theme-dark .trace-item {
|
||||
color: #f5f7fa; /* Light foreground text */
|
||||
}
|
||||
@ -167,10 +147,6 @@
|
||||
|
||||
/* Tracer light theme */
|
||||
|
||||
.theme-light #tracer-message {
|
||||
color: #292e33; /* Dark foreground text */
|
||||
}
|
||||
|
||||
.theme-light .trace-item {
|
||||
color: #292e33; /* Dark foreground text */
|
||||
}
|
||||
|
@ -287,6 +287,24 @@
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
/* FastListWidget */
|
||||
|
||||
.fast-list-widget-container {
|
||||
/* Hack: force hardware acceleration */
|
||||
transform: translateZ(1px);
|
||||
}
|
||||
|
||||
.theme-dark .fast-list-widget-empty-text {
|
||||
padding: 12px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.theme-light .fast-list-widget-empty-text {
|
||||
padding: 4px 8px;
|
||||
color: GrayText;
|
||||
}
|
||||
|
||||
/* SideMenuWidget */
|
||||
|
||||
.side-menu-widget-container {
|
||||
|
@ -17,6 +17,11 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// The Opus format supports up to 8 channels, and supports multitrack audio up
|
||||
// to 255 channels, but the current implementation supports only mono and
|
||||
// stereo, and downmixes any more than that.
|
||||
static const int MAX_SUPPORTED_AUDIO_CHANNELS = 8;
|
||||
|
||||
// http://www.opus-codec.org/docs/html_api-1.0.2/group__opus__encoder.html
|
||||
// In section "opus_encoder_init", channels must be 1 or 2 of input signal.
|
||||
static const int MAX_CHANNELS = 2;
|
||||
@ -143,15 +148,15 @@ OpusTrackEncoder::Init(int aChannels, int aSamplingRate)
|
||||
// This monitor is used to wake up other methods that are waiting for encoder
|
||||
// to be completely initialized.
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
|
||||
NS_ENSURE_TRUE((aChannels <= MAX_SUPPORTED_AUDIO_CHANNELS) && (aChannels > 0),
|
||||
NS_ERROR_FAILURE);
|
||||
|
||||
// This version of encoder API only support 1 or 2 channels,
|
||||
// So set the mChannels less or equal 2 and
|
||||
// let InterleaveTrackData downmix pcm data.
|
||||
mChannels = aChannels > MAX_CHANNELS ? MAX_CHANNELS : aChannels;
|
||||
|
||||
if (aChannels <= 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// According to www.opus-codec.org, creating an opus encoder requires the
|
||||
// sampling rate of source signal be one of 8000, 12000, 16000, 24000, or
|
||||
// 48000. If this constraint is not satisfied, we resample the input to 48kHz.
|
||||
@ -171,6 +176,7 @@ OpusTrackEncoder::Init(int aChannels, int aSamplingRate)
|
||||
}
|
||||
}
|
||||
mSamplingRate = aSamplingRate;
|
||||
NS_ENSURE_TRUE(mSamplingRate > 0, NS_ERROR_FAILURE);
|
||||
|
||||
int error = 0;
|
||||
mEncoder = opus_encoder_create(GetOutputSampleRate(), mChannels,
|
||||
|
@ -41,13 +41,13 @@ protected:
|
||||
|
||||
nsresult Init(int aChannels, int aSamplingRate) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Get the samplerate of the data to be fed to the Opus encoder. This might be
|
||||
* different from the intput samplerate if resampling occurs.
|
||||
* different from the input samplerate if resampling occurs.
|
||||
*/
|
||||
int GetOutputSampleRate();
|
||||
|
||||
private:
|
||||
/**
|
||||
* The Opus encoder from libopus.
|
||||
*/
|
||||
|
85
content/media/gtest/TestTrackEncoder.cpp
Normal file
85
content/media/gtest/TestTrackEncoder.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "OpusTrackEncoder.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
class TestOpusTrackEncoder : public OpusTrackEncoder
|
||||
{
|
||||
public:
|
||||
// Return true if it has successfully initialized the Opus encoder.
|
||||
bool TestOpusCreation(int aChannels, int aSamplingRate)
|
||||
{
|
||||
if (Init(aChannels, aSamplingRate) == NS_OK) {
|
||||
if (GetPacketDuration()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return the sample rate of data to be fed to the Opus encoder, could be
|
||||
// re-sampled if it was not one of the Opus supported sampling rates.
|
||||
// Init() is expected to be called first.
|
||||
int TestGetOutputSampleRate()
|
||||
{
|
||||
return mInitialized ? GetOutputSampleRate() : 0;
|
||||
}
|
||||
};
|
||||
|
||||
static bool
|
||||
TestOpusInit(int aChannels, int aSamplingRate)
|
||||
{
|
||||
TestOpusTrackEncoder encoder;
|
||||
return encoder.TestOpusCreation(aChannels, aSamplingRate);
|
||||
}
|
||||
|
||||
static int
|
||||
TestOpusResampler(int aChannels, int aSamplingRate)
|
||||
{
|
||||
TestOpusTrackEncoder encoder;
|
||||
EXPECT_TRUE(encoder.TestOpusCreation(aChannels, aSamplingRate));
|
||||
return encoder.TestGetOutputSampleRate();
|
||||
}
|
||||
|
||||
TEST(Media, OpusEncoder_Init)
|
||||
{
|
||||
// Expect false with 0 or negative channels of input signal.
|
||||
EXPECT_FALSE(TestOpusInit(0, 16000));
|
||||
EXPECT_FALSE(TestOpusInit(-1, 16000));
|
||||
|
||||
// The Opus format supports up to 8 channels, and supports multitrack audio up
|
||||
// to 255 channels, but the current implementation supports only mono and
|
||||
// stereo, and downmixes any more than that.
|
||||
// Expect false with channels of input signal exceed the max supported number.
|
||||
EXPECT_FALSE(TestOpusInit(8 + 1, 16000));
|
||||
|
||||
// Should accept channels within valid range.
|
||||
for (int i = 1; i <= 8; i++) {
|
||||
EXPECT_TRUE(TestOpusInit(i, 16000));
|
||||
}
|
||||
|
||||
// Expect false with 0 or negative sampling rate of input signal.
|
||||
EXPECT_FALSE(TestOpusInit(1, 0));
|
||||
EXPECT_FALSE(TestOpusInit(1, -1));
|
||||
}
|
||||
|
||||
TEST(Media, OpusEncoder_Resample)
|
||||
{
|
||||
// Sampling rates of data to be fed to Opus encoder, should remain unchanged
|
||||
// if it is one of Opus supported rates (8000, 12000, 16000, 24000 and 48000
|
||||
// (kHz)) at initialization.
|
||||
EXPECT_TRUE(TestOpusResampler(1, 8000) == 8000);
|
||||
EXPECT_TRUE(TestOpusResampler(1, 12000) == 12000);
|
||||
EXPECT_TRUE(TestOpusResampler(1, 16000) == 16000);
|
||||
EXPECT_TRUE(TestOpusResampler(1, 24000) == 24000);
|
||||
EXPECT_TRUE(TestOpusResampler(1, 48000) == 48000);
|
||||
|
||||
// Otherwise, it should be resampled to 48kHz by resampler.
|
||||
EXPECT_FALSE(TestOpusResampler(1, 9600) == 9600);
|
||||
EXPECT_FALSE(TestOpusResampler(1, 44100) == 44100);
|
||||
}
|
22
content/media/gtest/moz.build
Normal file
22
content/media/gtest/moz.build
Normal file
@ -0,0 +1,22 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
LIBRARY_NAME = 'media_gtest'
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'TestTrackEncoder.cpp',
|
||||
]
|
||||
|
||||
LIBXUL_LIBRARY = True
|
||||
|
||||
EXPORT_LIBRARY = True
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/content/media/encoder',
|
||||
]
|
||||
|
@ -49,7 +49,10 @@ if CONFIG['MOZ_OMX_DECODER']:
|
||||
|
||||
PARALLEL_DIRS += ['webspeech']
|
||||
|
||||
TEST_DIRS += ['test']
|
||||
TEST_DIRS += [
|
||||
'test',
|
||||
'gtest',
|
||||
]
|
||||
|
||||
EXPORTS += [
|
||||
'AbstractMediaDecoder.h',
|
||||
|
@ -36,11 +36,8 @@ MediaOmxReader::MediaOmxReader(AbstractMediaDecoder *aDecoder) :
|
||||
|
||||
MediaOmxReader::~MediaOmxReader()
|
||||
{
|
||||
ResetDecode();
|
||||
VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
|
||||
if (container) {
|
||||
container->ClearCurrentFrame();
|
||||
}
|
||||
ReleaseMediaResources();
|
||||
ReleaseDecoder();
|
||||
mOmxDecoder.clear();
|
||||
}
|
||||
|
||||
|
@ -49,11 +49,6 @@ if (!('BrowserElementIsPreloaded' in this)) {
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
// Those are produc-specific files that's sometimes unavailable.
|
||||
try {
|
||||
Services.scriptloader.loadSubScript("chrome://browser/content/ErrorPage.js");
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementPanning.js");
|
||||
ContentPanning.init();
|
||||
|
@ -9,3 +9,4 @@ support-files =
|
||||
[test_bug944397.html]
|
||||
[test_bug949059.html]
|
||||
[test_sendkey_cancel.html]
|
||||
[test_delete_focused_element.html]
|
||||
|
@ -123,13 +123,74 @@ function test_endComposition() {
|
||||
} else {
|
||||
todo(false, 'endComposition changed the input field incorrectly.');
|
||||
}
|
||||
inputmethod_cleanup();
|
||||
test_onSelectionChange();
|
||||
}, function (e) {
|
||||
ok(false, 'endComposition failed: ' + e.name);
|
||||
inputmethod_cleanup();
|
||||
});
|
||||
}
|
||||
|
||||
function test_onSelectionChange() {
|
||||
var sccTimeout = setTimeout(function() {
|
||||
ok(false, 'selectionchange event not fired');
|
||||
cleanup(true);
|
||||
}, 3000);
|
||||
|
||||
function cleanup(failed) {
|
||||
gContext.onselectionchange = null;
|
||||
clearTimeout(sccTimeout);
|
||||
if (failed) {
|
||||
inputmethod_cleanup();
|
||||
}
|
||||
else {
|
||||
test_onSurroundingTextChange();
|
||||
}
|
||||
}
|
||||
|
||||
gContext.onselectionchange = function() {
|
||||
ok(true, 'onselectionchange fired');
|
||||
cleanup();
|
||||
};
|
||||
|
||||
gContext.sendKey(0, 'j'.charCodeAt(0), 0).then(function() {
|
||||
// do nothing and wait for onselectionchange event
|
||||
}, function(e) {
|
||||
ok(false, 'sendKey failed: ' + e.name);
|
||||
cleanup(true);
|
||||
});
|
||||
}
|
||||
|
||||
function test_onSurroundingTextChange() {
|
||||
var sccTimeout = setTimeout(function() {
|
||||
ok(false, 'surroundingtextchange event not fired');
|
||||
cleanup(true);
|
||||
}, 3000);
|
||||
|
||||
function cleanup(failed) {
|
||||
gContext.onsurroundingtextchange = null;
|
||||
clearTimeout(sccTimeout);
|
||||
if (failed) {
|
||||
inputmethod_cleanup();
|
||||
}
|
||||
else {
|
||||
// in case we want more tests leave this
|
||||
inputmethod_cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
gContext.onsurroundingtextchange = function() {
|
||||
ok(true, 'onsurroundingtextchange fired');
|
||||
cleanup();
|
||||
};
|
||||
|
||||
gContext.sendKey(0, 'j'.charCodeAt(0), 0).then(function() {
|
||||
// do nothing and wait for onselectionchange event
|
||||
}, function(e) {
|
||||
ok(false, 'sendKey failed: ' + e.name);
|
||||
cleanup(true);
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
107
dom/inputmethod/mochitest/test_delete_focused_element.html
Normal file
107
dom/inputmethod/mochitest/test_delete_focused_element.html
Normal file
@ -0,0 +1,107 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=952741
|
||||
-->
|
||||
<head>
|
||||
<title>Test focused element deletion for InputMethod API.</title>
|
||||
<script type="application/javascript;version=1.7" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript;version=1.7" src="inputmethod_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=952741">Mozilla Bug 952741</a>
|
||||
<input type="text" />
|
||||
<p id="display"></p>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="application/javascript;version=1.7">
|
||||
|
||||
inputmethod_setup(function() {
|
||||
runTest();
|
||||
});
|
||||
|
||||
// The frame script running in file_test_app.html.
|
||||
function appFrameScript() {
|
||||
let input = content.document.getElementById('test-input');
|
||||
let textarea = content.document.createElement('textarea');
|
||||
textarea.lang = 'en';
|
||||
|
||||
content.document.body.appendChild(textarea);
|
||||
|
||||
textarea.onfocus = function() {
|
||||
textarea.parentNode.removeChild(textarea);
|
||||
sendAsyncMessage('test:InputMethod:finished', {});
|
||||
};
|
||||
|
||||
content.setTimeout(function() {
|
||||
textarea.focus();
|
||||
input.parentNode.removeChild(input);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
var timeoutId = null;
|
||||
|
||||
// Create an app frame to recieve keyboard inputs.
|
||||
let app = document.createElement('iframe');
|
||||
app.src = 'file_test_app.html';
|
||||
app.setAttribute('mozbrowser', true);
|
||||
document.body.appendChild(app);
|
||||
app.addEventListener('mozbrowserloadend', function() {
|
||||
let mm = SpecialPowers.getBrowserFrameMessageManager(app);
|
||||
mm.loadFrameScript('data:,(' + appFrameScript.toString() + ')();', false);
|
||||
mm.addMessageListener("test:InputMethod:finished", function() {
|
||||
timeoutId = setTimeout(function() {
|
||||
ok(false, 'No inputcontextchange event when textarea is deleted.');
|
||||
inputmethod_cleanup();
|
||||
}, 20000);
|
||||
});
|
||||
});
|
||||
|
||||
let im = navigator.mozInputMethod;
|
||||
let count = 0;
|
||||
im.oninputcontextchange = function() {
|
||||
switch (count++) {
|
||||
case 0:
|
||||
if (!im.inputcontext) {
|
||||
break;
|
||||
}
|
||||
is(im.inputcontext.lang, 'zh', 'input was focused.');
|
||||
return;
|
||||
case 1:
|
||||
if (im.inputcontext) {
|
||||
break;
|
||||
}
|
||||
ok(true, 'input was blurred.');
|
||||
return;
|
||||
case 2:
|
||||
if (!im.inputcontext) {
|
||||
break;
|
||||
}
|
||||
is(im.inputcontext.lang, 'en', 'textarea was focused.');
|
||||
return;
|
||||
case 3:
|
||||
if (im.inputcontext) {
|
||||
break;
|
||||
}
|
||||
ok(true, 'textarea was removed.');
|
||||
clearTimeout(timeoutId);
|
||||
inputmethod_cleanup();
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
count = 100;
|
||||
ok(false, 'Should not arrive here.');
|
||||
inputmethod_cleanup();
|
||||
};
|
||||
|
||||
// Set current page as an input method.
|
||||
SpecialPowers.wrap(im).setActive(true);
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -94,11 +94,6 @@ const BrowserElementIsPreloaded = true;
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
// Those are produc-specific files that's sometimes unavailable.
|
||||
try {
|
||||
Services.scriptloader.loadSubScript("chrome://browser/content/ErrorPage.js", global);
|
||||
} catch (e) {
|
||||
}
|
||||
Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementPanning.js", global);
|
||||
Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementChildPreload.js", global);
|
||||
|
||||
|
@ -869,15 +869,12 @@ var WifiManager = (function() {
|
||||
manager.state = "UNINITIALIZED";
|
||||
return;
|
||||
}
|
||||
// This command is mandatory for Nexus 4. But some devices like
|
||||
// Galaxy S2 don't support it. Continue to start wpa_supplicant
|
||||
// even if we fail to set wifi operation mode to station.
|
||||
gNetworkService.setWifiOperationMode(manager.ifname,
|
||||
WIFI_FIRMWARE_STATION,
|
||||
function (status) {
|
||||
if (status) {
|
||||
callback(status);
|
||||
manager.state = "UNINITIALIZED";
|
||||
return;
|
||||
}
|
||||
|
||||
function doStartSupplicant() {
|
||||
cancelWaitForDriverReadyTimer();
|
||||
wifiCommand.startSupplicant(function (status) {
|
||||
|
@ -41,10 +41,10 @@ CheckLength(ExclusiveContext *cx, size_t length)
|
||||
}
|
||||
|
||||
static bool
|
||||
SetSourceURL(ExclusiveContext *cx, TokenStream &tokenStream, ScriptSource *ss)
|
||||
SetDisplayURL(ExclusiveContext *cx, TokenStream &tokenStream, ScriptSource *ss)
|
||||
{
|
||||
if (tokenStream.hasSourceURL()) {
|
||||
if (!ss->setSourceURL(cx, tokenStream.sourceURL()))
|
||||
if (tokenStream.hasDisplayURL()) {
|
||||
if (!ss->setDisplayURL(cx, tokenStream.displayURL()))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -368,7 +368,7 @@ frontend::CompileScript(ExclusiveContext *cx, LifoAlloc *alloc, HandleObject sco
|
||||
if (!MaybeCheckEvalFreeVariables(cx, evalCaller, scopeChain, parser, pc.ref()))
|
||||
return nullptr;
|
||||
|
||||
if (!SetSourceURL(cx, parser.tokenStream, ss))
|
||||
if (!SetDisplayURL(cx, parser.tokenStream, ss))
|
||||
return nullptr;
|
||||
|
||||
if (!SetSourceMap(cx, parser.tokenStream, ss))
|
||||
@ -597,7 +597,7 @@ CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, const ReadOnlyComp
|
||||
JS_ASSERT(IsAsmJSModuleNative(fun->native()));
|
||||
}
|
||||
|
||||
if (!SetSourceURL(cx, parser.tokenStream, ss))
|
||||
if (!SetDisplayURL(cx, parser.tokenStream, ss))
|
||||
return false;
|
||||
|
||||
if (!SetSourceMap(cx, parser.tokenStream, ss))
|
||||
|
@ -273,7 +273,7 @@ TokenStream::TokenStream(ExclusiveContext *cx, const ReadOnlyCompileOptions &opt
|
||||
prevLinebase(nullptr),
|
||||
userbuf(cx, base - options.column, length + options.column), // See comment below
|
||||
filename(options.filename()),
|
||||
sourceURL_(nullptr),
|
||||
displayURL_(nullptr),
|
||||
sourceMapURL_(nullptr),
|
||||
tokenbuf(cx),
|
||||
cx(cx),
|
||||
@ -333,7 +333,7 @@ TokenStream::TokenStream(ExclusiveContext *cx, const ReadOnlyCompileOptions &opt
|
||||
|
||||
TokenStream::~TokenStream()
|
||||
{
|
||||
js_free(sourceURL_);
|
||||
js_free(displayURL_);
|
||||
js_free(sourceMapURL_);
|
||||
|
||||
JS_ASSERT_IF(originPrincipals, originPrincipals->refcount);
|
||||
@ -808,7 +808,7 @@ TokenStream::getDirectives(bool isMultiline, bool shouldWarnDeprecated)
|
||||
// comment. To avoid potentially expensive lookahead and backtracking, we
|
||||
// only check for this case if we encounter a '#' character.
|
||||
|
||||
if (!getSourceURL(isMultiline, shouldWarnDeprecated))
|
||||
if (!getDisplayURL(isMultiline, shouldWarnDeprecated))
|
||||
return false;
|
||||
if (!getSourceMappingURL(isMultiline, shouldWarnDeprecated))
|
||||
return false;
|
||||
@ -864,13 +864,18 @@ TokenStream::getDirective(bool isMultiline, bool shouldWarnDeprecated,
|
||||
}
|
||||
|
||||
bool
|
||||
TokenStream::getSourceURL(bool isMultiline, bool shouldWarnDeprecated)
|
||||
TokenStream::getDisplayURL(bool isMultiline, bool shouldWarnDeprecated)
|
||||
{
|
||||
// Match comments of the form "//# sourceURL=<url>" or
|
||||
// "/\* //# sourceURL=<url> *\/"
|
||||
//
|
||||
// Note that while these are labeled "sourceURL" in the source text,
|
||||
// internally we refer to it as a "displayURL" to distinguish what the
|
||||
// developer would like to refer to the source as from the source's actual
|
||||
// URL.
|
||||
|
||||
return getDirective(isMultiline, shouldWarnDeprecated, " sourceURL=", 11,
|
||||
"sourceURL", &sourceURL_);
|
||||
"sourceURL", &displayURL_);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -628,12 +628,12 @@ class MOZ_STACK_CLASS TokenStream
|
||||
return userbuf.limit();
|
||||
}
|
||||
|
||||
bool hasSourceURL() const {
|
||||
return sourceURL_ != nullptr;
|
||||
bool hasDisplayURL() const {
|
||||
return displayURL_ != nullptr;
|
||||
}
|
||||
|
||||
jschar *sourceURL() {
|
||||
return sourceURL_;
|
||||
jschar *displayURL() {
|
||||
return displayURL_;
|
||||
}
|
||||
|
||||
bool hasSourceMapURL() const {
|
||||
@ -848,7 +848,7 @@ class MOZ_STACK_CLASS TokenStream
|
||||
bool getDirective(bool isMultiline, bool shouldWarnDeprecated,
|
||||
const char *directive, int directiveLength,
|
||||
const char *errorMsgPragma, jschar **destination);
|
||||
bool getSourceURL(bool isMultiline, bool shouldWarnDeprecated);
|
||||
bool getDisplayURL(bool isMultiline, bool shouldWarnDeprecated);
|
||||
bool getSourceMappingURL(bool isMultiline, bool shouldWarnDeprecated);
|
||||
|
||||
// |expect| cannot be an EOL char.
|
||||
@ -889,7 +889,7 @@ class MOZ_STACK_CLASS TokenStream
|
||||
const jschar *prevLinebase; // start of previous line; nullptr if on the first line
|
||||
TokenBuf userbuf; // user input buffer
|
||||
const char *filename; // input filename or null
|
||||
jschar *sourceURL_; // the user's requested source URL or null
|
||||
jschar *displayURL_; // the user's requested source URL or null
|
||||
jschar *sourceMapURL_; // source map's filename or null
|
||||
CharBuffer tokenbuf; // current token string buffer
|
||||
bool maybeEOL[256]; // probabilistic EOL lookup table
|
||||
|
39
js/src/jit-test/tests/debug/Debugger-findScripts-18.js
Normal file
39
js/src/jit-test/tests/debug/Debugger-findScripts-18.js
Normal file
@ -0,0 +1,39 @@
|
||||
// In a debuggee with multiple scripts with varying displayURLs (aka //#
|
||||
// sourceURL), findScripts can filter by displayURL.
|
||||
|
||||
var g = newGlobal();
|
||||
|
||||
g.eval("function f(){} //# sourceURL=f.js");
|
||||
g.eval("function g(){} //# sourceURL=g.js");
|
||||
g.eval("function h(){}");
|
||||
|
||||
var dbg = new Debugger();
|
||||
var gw = dbg.addDebuggee(g);
|
||||
var fw = gw.makeDebuggeeValue(g.f);
|
||||
var ggw = gw.makeDebuggeeValue(g.g);
|
||||
var hw = gw.makeDebuggeeValue(g.h);
|
||||
|
||||
var fScripts = dbg.findScripts({ displayURL: "f.js" });
|
||||
assertEq(fScripts.indexOf(fw.script) != -1, true);
|
||||
assertEq(fScripts.indexOf(ggw.script), -1);
|
||||
assertEq(fScripts.indexOf(hw.script), -1);
|
||||
|
||||
var gScripts = dbg.findScripts({ displayURL: "g.js" });
|
||||
assertEq(gScripts.indexOf(ggw.script) != -1, true);
|
||||
assertEq(gScripts.indexOf(fw.script), -1);
|
||||
assertEq(gScripts.indexOf(hw.script), -1);
|
||||
|
||||
var allScripts = dbg.findScripts();
|
||||
assertEq(allScripts.indexOf(fw.script) != -1, true);
|
||||
assertEq(allScripts.indexOf(ggw.script) != -1, true);
|
||||
assertEq(allScripts.indexOf(hw.script) != -1, true);
|
||||
|
||||
try {
|
||||
dbg.findScripts({ displayURL: 3 });
|
||||
// Should never get here because the above line should throw
|
||||
// JSMSG_UNEXPECTED_TYPE.
|
||||
assertEq(true, false);
|
||||
} catch(e) {
|
||||
assertEq(e.name, "TypeError");
|
||||
assertEq(e.message.contains("displayURL"), true);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/* -*- Mode: javascript; js-indent-level: 4; -*- */
|
||||
// Source.prototype.sourceURL can be a string or null.
|
||||
// Source.prototype.displayURL can be a string or null.
|
||||
|
||||
let g = newGlobal('new-compartment');
|
||||
let dbg = new Debugger;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* -*- Mode: javascript; js-indent-level: 4; -*- */
|
||||
// Source.prototype.sourceURL can be a string or null.
|
||||
// Source.prototype.displayURL can be a string or null.
|
||||
|
||||
let g = newGlobal('new-compartment');
|
||||
let dbg = new Debugger;
|
||||
@ -15,7 +15,7 @@ g.evaluate("function f(x) { return 2*x; }");
|
||||
assertEq(getDisplayURL(), null);
|
||||
|
||||
// With a source url
|
||||
g.evaluate("function f(x) { return 2*x; }", {sourceURL: 'file:///var/foo.js'});
|
||||
g.evaluate("function f(x) { return 2*x; }", {displayURL: 'file:///var/foo.js'});
|
||||
assertEq(getDisplayURL(), 'file:///var/foo.js');
|
||||
|
||||
// Nested functions
|
||||
@ -25,7 +25,7 @@ dbg.onDebuggerStatement = function (frame) {
|
||||
assertEq(frame.script.source.displayURL, 'file:///var/bar.js');
|
||||
};
|
||||
g.evaluate('(function () { (function () { debugger; })(); })();',
|
||||
{sourceURL: 'file:///var/bar.js'});
|
||||
{displayURL: 'file:///var/bar.js'});
|
||||
assertEq(fired, true);
|
||||
|
||||
// Comment pragmas
|
||||
@ -66,6 +66,6 @@ assertEq(getDisplayURL(), 'http://example.com/bar.js');
|
||||
// With both a comment and the evaluate option.
|
||||
g.evaluate('function f() {}\n' +
|
||||
'//# sourceURL=http://example.com/foo.js',
|
||||
{sourceMapURL: 'http://example.com/bar.js'});
|
||||
{displayURL: 'http://example.com/bar.js'});
|
||||
assertEq(getDisplayURL(), 'http://example.com/foo.js');
|
||||
|
||||
|
@ -1326,7 +1326,7 @@ ScriptSource::destroy()
|
||||
JS_ASSERT(ready());
|
||||
adjustDataSize(0);
|
||||
js_free(filename_);
|
||||
js_free(sourceURL_);
|
||||
js_free(displayURL_);
|
||||
js_free(sourceMapURL_);
|
||||
if (originPrincipals_)
|
||||
JS_DropPrincipals(TlsPerThreadData.get()->runtimeFromMainThread(), originPrincipals_);
|
||||
@ -1417,29 +1417,29 @@ ScriptSource::performXDR(XDRState<mode> *xdr)
|
||||
sourceMapURL_[sourceMapURLLen] = '\0';
|
||||
}
|
||||
|
||||
uint8_t haveSourceURL = hasSourceURL();
|
||||
if (!xdr->codeUint8(&haveSourceURL))
|
||||
uint8_t haveDisplayURL = hasDisplayURL();
|
||||
if (!xdr->codeUint8(&haveDisplayURL))
|
||||
return false;
|
||||
|
||||
if (haveSourceURL) {
|
||||
uint32_t sourceURLLen = (mode == XDR_DECODE) ? 0 : js_strlen(sourceURL_);
|
||||
if (!xdr->codeUint32(&sourceURLLen))
|
||||
if (haveDisplayURL) {
|
||||
uint32_t displayURLLen = (mode == XDR_DECODE) ? 0 : js_strlen(displayURL_);
|
||||
if (!xdr->codeUint32(&displayURLLen))
|
||||
return false;
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
size_t byteLen = (sourceURLLen + 1) * sizeof(jschar);
|
||||
sourceURL_ = static_cast<jschar *>(xdr->cx()->malloc_(byteLen));
|
||||
if (!sourceURL_)
|
||||
size_t byteLen = (displayURLLen + 1) * sizeof(jschar);
|
||||
displayURL_ = static_cast<jschar *>(xdr->cx()->malloc_(byteLen));
|
||||
if (!displayURL_)
|
||||
return false;
|
||||
}
|
||||
if (!xdr->codeChars(sourceURL_, sourceURLLen)) {
|
||||
if (!xdr->codeChars(displayURL_, displayURLLen)) {
|
||||
if (mode == XDR_DECODE) {
|
||||
js_free(sourceURL_);
|
||||
sourceURL_ = nullptr;
|
||||
js_free(displayURL_);
|
||||
displayURL_ = nullptr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
sourceURL_[sourceURLLen] = '\0';
|
||||
displayURL_[displayURLLen] = '\0';
|
||||
}
|
||||
|
||||
uint8_t haveFilename = !!filename_;
|
||||
@ -1475,10 +1475,10 @@ ScriptSource::setFilename(ExclusiveContext *cx, const char *filename)
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptSource::setSourceURL(ExclusiveContext *cx, const jschar *sourceURL)
|
||||
ScriptSource::setDisplayURL(ExclusiveContext *cx, const jschar *displayURL)
|
||||
{
|
||||
JS_ASSERT(sourceURL);
|
||||
if (hasSourceURL()) {
|
||||
JS_ASSERT(displayURL);
|
||||
if (hasDisplayURL()) {
|
||||
if (cx->isJSContext() &&
|
||||
!JS_ReportErrorFlagsAndNumber(cx->asJSContext(), JSREPORT_WARNING,
|
||||
js_GetErrorMessage, nullptr,
|
||||
@ -1488,20 +1488,20 @@ ScriptSource::setSourceURL(ExclusiveContext *cx, const jschar *sourceURL)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
size_t len = js_strlen(sourceURL) + 1;
|
||||
size_t len = js_strlen(displayURL) + 1;
|
||||
if (len == 1)
|
||||
return true;
|
||||
sourceURL_ = js_strdup(cx, sourceURL);
|
||||
if (!sourceURL_)
|
||||
displayURL_ = js_strdup(cx, displayURL);
|
||||
if (!displayURL_)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
const jschar *
|
||||
ScriptSource::sourceURL()
|
||||
ScriptSource::displayURL()
|
||||
{
|
||||
JS_ASSERT(hasSourceURL());
|
||||
return sourceURL_;
|
||||
JS_ASSERT(hasDisplayURL());
|
||||
return displayURL_;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -375,7 +375,7 @@ class ScriptSource
|
||||
uint32_t length_;
|
||||
uint32_t compressedLength_;
|
||||
char *filename_;
|
||||
jschar *sourceURL_;
|
||||
jschar *displayURL_;
|
||||
jschar *sourceMapURL_;
|
||||
JSPrincipals *originPrincipals_;
|
||||
|
||||
@ -392,7 +392,7 @@ class ScriptSource
|
||||
length_(0),
|
||||
compressedLength_(0),
|
||||
filename_(nullptr),
|
||||
sourceURL_(nullptr),
|
||||
displayURL_(nullptr),
|
||||
sourceMapURL_(nullptr),
|
||||
originPrincipals_(originPrincipals),
|
||||
sourceRetrievable_(false),
|
||||
@ -440,10 +440,10 @@ class ScriptSource
|
||||
return filename_;
|
||||
}
|
||||
|
||||
// Source URLs
|
||||
bool setSourceURL(ExclusiveContext *cx, const jschar *sourceURL);
|
||||
const jschar *sourceURL();
|
||||
bool hasSourceURL() const { return sourceURL_ != nullptr; }
|
||||
// Display URLs
|
||||
bool setDisplayURL(ExclusiveContext *cx, const jschar *displayURL);
|
||||
const jschar *displayURL();
|
||||
bool hasDisplayURL() const { return displayURL_ != nullptr; }
|
||||
|
||||
// Source maps
|
||||
bool setSourceMapURL(ExclusiveContext *cx, const jschar *sourceMapURL);
|
||||
|
@ -862,7 +862,7 @@ Evaluate(JSContext *cx, unsigned argc, jsval *vp)
|
||||
RootedObject element(cx);
|
||||
RootedString elementProperty(cx);
|
||||
JSAutoByteString fileNameBytes;
|
||||
RootedString sourceURL(cx);
|
||||
RootedString displayURL(cx);
|
||||
RootedString sourceMapURL(cx);
|
||||
unsigned lineNumber = 1;
|
||||
RootedObject global(cx, nullptr);
|
||||
@ -920,11 +920,11 @@ Evaluate(JSContext *cx, unsigned argc, jsval *vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!JS_GetProperty(cx, opts, "sourceURL", &v))
|
||||
if (!JS_GetProperty(cx, opts, "displayURL", &v))
|
||||
return false;
|
||||
if (!v.isUndefined()) {
|
||||
sourceURL = ToString(cx, v);
|
||||
if (!sourceURL)
|
||||
displayURL = ToString(cx, v);
|
||||
if (!displayURL)
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1031,11 +1031,11 @@ Evaluate(JSContext *cx, unsigned argc, jsval *vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sourceURL && !script->scriptSource()->hasSourceURL()) {
|
||||
const jschar *surl = JS_GetStringCharsZ(cx, sourceURL);
|
||||
if (!surl)
|
||||
if (displayURL && !script->scriptSource()->hasDisplayURL()) {
|
||||
const jschar *durl = JS_GetStringCharsZ(cx, displayURL);
|
||||
if (!durl)
|
||||
return false;
|
||||
if (!script->scriptSource()->setSourceURL(cx, surl))
|
||||
if (!script->scriptSource()->setDisplayURL(cx, durl))
|
||||
return false;
|
||||
}
|
||||
if (sourceMapURL && !script->scriptSource()->hasSourceMapURL()) {
|
||||
|
@ -52,6 +52,7 @@
|
||||
macro(defineSetter, defineSetter, "__defineSetter__") \
|
||||
macro(delete, delete_, "delete") \
|
||||
macro(deleteProperty, deleteProperty, "deleteProperty") \
|
||||
macro(displayURL, displayURL, "displayURL") \
|
||||
macro(done, done, "done") \
|
||||
macro(each, each, "each") \
|
||||
macro(elementType, elementType, "elementType") \
|
||||
|
@ -2321,8 +2321,8 @@ class Debugger::ScriptQuery {
|
||||
public:
|
||||
/* Construct a ScriptQuery to use matching scripts for |dbg|. */
|
||||
ScriptQuery(JSContext *cx, Debugger *dbg):
|
||||
cx(cx), debugger(dbg), compartments(cx->runtime()), url(cx),
|
||||
innermostForCompartment(cx->runtime())
|
||||
cx(cx), debugger(dbg), compartments(cx->runtime()), url(cx), displayURL(cx),
|
||||
displayURLChars(nullptr), innermostForCompartment(cx->runtime())
|
||||
{}
|
||||
|
||||
/*
|
||||
@ -2419,6 +2419,16 @@ class Debugger::ScriptQuery {
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for a 'displayURL' property. */
|
||||
if (!JSObject::getProperty(cx, query, query, cx->names().displayURL, &displayURL))
|
||||
return false;
|
||||
if (!displayURL.isUndefined() && !displayURL.isString()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
|
||||
"query object's 'displayURL' property",
|
||||
"neither undefined nor a string");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2427,6 +2437,7 @@ class Debugger::ScriptQuery {
|
||||
url.setUndefined();
|
||||
hasLine = false;
|
||||
innermost = false;
|
||||
displayURLChars = nullptr;
|
||||
return matchAllDebuggeeGlobals();
|
||||
}
|
||||
|
||||
@ -2490,6 +2501,14 @@ class Debugger::ScriptQuery {
|
||||
/* url as a C string. */
|
||||
JSAutoByteString urlCString;
|
||||
|
||||
/* If this is a string, matching scripts' sources have displayURLs equal to
|
||||
* it. */
|
||||
RootedValue displayURL;
|
||||
|
||||
/* displayURL as a jschar* */
|
||||
const jschar *displayURLChars;
|
||||
size_t displayURLLength;
|
||||
|
||||
/* True if the query contained a 'line' property. */
|
||||
bool hasLine;
|
||||
|
||||
@ -2553,15 +2572,23 @@ class Debugger::ScriptQuery {
|
||||
}
|
||||
|
||||
/*
|
||||
* Given that parseQuery or omittedQuery has been called, prepare to
|
||||
* match scripts. Set urlCString as appropriate.
|
||||
* Given that parseQuery or omittedQuery has been called, prepare to match
|
||||
* scripts. Set urlCString and displayURLChars as appropriate.
|
||||
*/
|
||||
bool prepareQuery() {
|
||||
/* Compute urlCString, if a url was given. */
|
||||
/* Compute urlCString and displayURLChars, if a url or displayURL was
|
||||
* given respectively. */
|
||||
if (url.isString()) {
|
||||
if (!urlCString.encodeLatin1(cx, url.toString()))
|
||||
return false;
|
||||
}
|
||||
if (displayURL.isString()) {
|
||||
JSString *s = displayURL.toString();
|
||||
displayURLChars = s->getChars(cx);
|
||||
displayURLLength = s->length();
|
||||
if (!displayURLChars)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -2590,6 +2617,15 @@ class Debugger::ScriptQuery {
|
||||
if (line < script->lineno() || script->lineno() + js_GetScriptLineExtent(script) < line)
|
||||
return;
|
||||
}
|
||||
if (displayURLChars) {
|
||||
if (!script->scriptSource() || !script->scriptSource()->hasDisplayURL())
|
||||
return;
|
||||
const jschar *s = script->scriptSource()->displayURL();
|
||||
if (CompareChars(s, js_strlen(s), displayURLChars, displayURLLength) != 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (innermost) {
|
||||
/*
|
||||
* For 'innermost' queries, we don't place scripts in |vector| right
|
||||
@ -3807,8 +3843,8 @@ DebuggerSource_getDisplayURL(JSContext *cx, unsigned argc, Value *vp)
|
||||
ScriptSource *ss = sourceObject->source();
|
||||
JS_ASSERT(ss);
|
||||
|
||||
if (ss->hasSourceURL()) {
|
||||
JSString *str = JS_NewUCStringCopyZ(cx, ss->sourceURL());
|
||||
if (ss->hasDisplayURL()) {
|
||||
JSString *str = JS_NewUCStringCopyZ(cx, ss->displayURL());
|
||||
if (!str)
|
||||
return false;
|
||||
args.rval().setString(str);
|
||||
|
@ -22,6 +22,7 @@
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TabRowTextAppearance.Url"
|
||||
android:textSize="14sp"/>
|
||||
android:textSize="14sp"
|
||||
android:maxLength="1024"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -28,7 +28,8 @@
|
||||
style="@style/Widget.TwoLinePageRow.Url"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawablePadding="5dp"/>
|
||||
android:drawablePadding="5dp"
|
||||
android:maxLength="1024"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -19,8 +19,22 @@ public class ClientRecord extends Record {
|
||||
public static final long CLIENTS_TTL = 21 * 24 * 60 * 60; // 21 days in seconds.
|
||||
public static final String DEFAULT_CLIENT_NAME = "Default Name";
|
||||
|
||||
/**
|
||||
* Each of these fields is 'owned' by the client it represents. For example,
|
||||
* the "version" field is the Firefox version of that client; some time after
|
||||
* that client upgrades, it'll upload a new record with its new version.
|
||||
*
|
||||
* The only exception is for commands. When a command is sent to a client, the
|
||||
* sender will download its current record, append the command to the
|
||||
* "commands" array, and reupload the record. After processing, the recipient
|
||||
* will reupload its record with an empty commands array.
|
||||
*
|
||||
* Note that the version, then, will remain the version of the recipient, as
|
||||
* with the other descriptive fields.
|
||||
*/
|
||||
public String name = ClientRecord.DEFAULT_CLIENT_NAME;
|
||||
public String type = ClientRecord.CLIENT_TYPE;
|
||||
public String version = null; // Free-form string, optional.
|
||||
public JSONArray commands;
|
||||
|
||||
public ClientRecord(String guid, String collection, long lastModified, boolean deleted) {
|
||||
@ -48,6 +62,11 @@ public class ClientRecord extends Record {
|
||||
protected void initFromPayload(ExtendedJSONObject payload) {
|
||||
this.name = (String) payload.get("name");
|
||||
this.type = (String) payload.get("type");
|
||||
try {
|
||||
this.version = (String) payload.get("version");
|
||||
} catch (Exception e) {
|
||||
// Oh well.
|
||||
}
|
||||
|
||||
try {
|
||||
commands = payload.getArray("commands");
|
||||
@ -62,6 +81,8 @@ public class ClientRecord extends Record {
|
||||
putPayload(payload, "id", this.guid);
|
||||
putPayload(payload, "name", this.name);
|
||||
putPayload(payload, "type", this.type);
|
||||
putPayload(payload, "version", this.version);
|
||||
|
||||
if (this.commands != null) {
|
||||
payload.put("commands", this.commands);
|
||||
}
|
||||
@ -82,6 +103,7 @@ public class ClientRecord extends Record {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't compare versions.
|
||||
ClientRecord other = (ClientRecord) o;
|
||||
if (!RepoUtils.stringsEqual(other.name, this.name) ||
|
||||
!RepoUtils.stringsEqual(other.type, this.type)) {
|
||||
@ -99,6 +121,7 @@ public class ClientRecord extends Record {
|
||||
|
||||
out.name = this.name;
|
||||
out.type = this.type;
|
||||
out.version = this.version;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.mozilla.gecko.background.common.GlobalConstants;
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.sync.CommandProcessor;
|
||||
import org.mozilla.gecko.sync.CommandProcessor.Command;
|
||||
@ -56,7 +57,7 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage {
|
||||
protected ClientsDatabaseAccessor db;
|
||||
|
||||
protected volatile boolean shouldWipe;
|
||||
protected volatile boolean commandsProcessedShouldUpload;
|
||||
protected volatile boolean shouldUploadLocalRecord; // Set if, e.g., we received commands or need to refresh our version.
|
||||
protected final AtomicInteger uploadAttemptsCount = new AtomicInteger();
|
||||
protected final List<ClientRecord> toUpload = new ArrayList<ClientRecord>();
|
||||
|
||||
@ -183,11 +184,9 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage {
|
||||
try {
|
||||
r = (ClientRecord) factory.createRecord(record.decrypt());
|
||||
if (clientsDelegate.isLocalGUID(r.guid)) {
|
||||
Logger.info(LOG_TAG, "Local client GUID exists on server and was downloaded");
|
||||
|
||||
Logger.info(LOG_TAG, "Local client GUID exists on server and was downloaded.");
|
||||
localAccountGUIDDownloaded = true;
|
||||
session.config.persistServerClientRecordTimestamp(r.lastModified);
|
||||
processCommands(r.commands);
|
||||
handleDownloadedLocalRecord(r);
|
||||
} else {
|
||||
// Only need to store record if it isn't our local one.
|
||||
wipeAndStore(r);
|
||||
@ -268,7 +267,7 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage {
|
||||
|
||||
// If we're processing our record, we have a little more cleanup
|
||||
// to do.
|
||||
commandsProcessedShouldUpload = false;
|
||||
shouldUploadLocalRecord = false;
|
||||
session.config.persistServerClientRecordTimestamp(responseTimestamp);
|
||||
session.advance();
|
||||
}
|
||||
@ -279,7 +278,7 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage {
|
||||
|
||||
// If upload failed because of `ifUnmodifiedSince` then there are new
|
||||
// commands uploaded to our record. We must download and process them first.
|
||||
if (!commandsProcessedShouldUpload ||
|
||||
if (!shouldUploadLocalRecord ||
|
||||
statusCode == HttpStatus.SC_PRECONDITION_FAILED ||
|
||||
uploadAttemptsCount.incrementAndGet() > MAX_UPLOAD_FAILURE_COUNT) {
|
||||
|
||||
@ -293,7 +292,7 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage {
|
||||
}
|
||||
Logger.trace(LOG_TAG, "Retrying upload…");
|
||||
// Preconditions:
|
||||
// commandsProcessedShouldUpload == true &&
|
||||
// shouldUploadLocalRecord == true &&
|
||||
// statusCode != 412 &&
|
||||
// uploadAttemptCount < MAX_UPLOAD_FAILURE_COUNT
|
||||
checkAndUpload();
|
||||
@ -357,12 +356,17 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage {
|
||||
return VersionConstants.CLIENTS_ENGINE_VERSION;
|
||||
}
|
||||
|
||||
protected String getLocalClientVersion() {
|
||||
return GlobalConstants.MOZ_APP_VERSION;
|
||||
}
|
||||
|
||||
protected ClientRecord newLocalClientRecord(ClientsDataDelegate delegate) {
|
||||
final String ourGUID = delegate.getAccountGUID();
|
||||
final String ourName = delegate.getClientName();
|
||||
|
||||
ClientRecord r = new ClientRecord(ourGUID);
|
||||
r.name = ourName;
|
||||
r.version = getLocalClientVersion();
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -373,7 +377,7 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage {
|
||||
}
|
||||
|
||||
protected boolean shouldUpload() {
|
||||
if (commandsProcessedShouldUpload) {
|
||||
if (shouldUploadLocalRecord) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -390,13 +394,22 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage {
|
||||
return age >= CLIENTS_TTL_REFRESH;
|
||||
}
|
||||
|
||||
protected void handleDownloadedLocalRecord(ClientRecord r) {
|
||||
session.config.persistServerClientRecordTimestamp(r.lastModified);
|
||||
|
||||
if (!getLocalClientVersion().equals(r.version)) {
|
||||
shouldUploadLocalRecord = true;
|
||||
}
|
||||
processCommands(r.commands);
|
||||
}
|
||||
|
||||
protected void processCommands(JSONArray commands) {
|
||||
if (commands == null ||
|
||||
commands.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
commandsProcessedShouldUpload = true;
|
||||
shouldUploadLocalRecord = true;
|
||||
CommandProcessor processor = CommandProcessor.getProcessor();
|
||||
|
||||
for (Object o : commands) {
|
||||
|
@ -13,7 +13,7 @@ import org.mozilla.gecko.FennecNativeActions;
|
||||
import org.mozilla.gecko.FennecNativeDriver;
|
||||
import org.mozilla.gecko.FennecTalosAssert;
|
||||
import org.mozilla.gecko.tests.components.*;
|
||||
import org.mozilla.gecko.tests.helpers.*;
|
||||
import org.mozilla.gecko.tests.helpers.HelperInitializer;
|
||||
|
||||
import com.jayway.android.robotium.solo.Solo;
|
||||
|
||||
@ -121,13 +121,7 @@ abstract class UITest extends ActivityInstrumentationTestCase2<Activity>
|
||||
}
|
||||
|
||||
private void initHelpers() {
|
||||
// Other helpers make assertions so init AssertionHelper first.
|
||||
AssertionHelper.init(this);
|
||||
|
||||
DeviceHelper.init(this);
|
||||
GeckoHelper.init(this);
|
||||
NavigationHelper.init(this);
|
||||
WaitHelper.init(this);
|
||||
HelperInitializer.init(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -18,7 +18,7 @@ public final class AssertionHelper {
|
||||
|
||||
private AssertionHelper() { /* To disallow instantation. */ }
|
||||
|
||||
public static void init(final UITestContext context) {
|
||||
protected static void init(final UITestContext context) {
|
||||
sAsserter = context.getAsserter();
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ public final class DeviceHelper {
|
||||
assertTrue("The device is a tablet", isTablet());
|
||||
}
|
||||
|
||||
public static void init(final UITestContext context) {
|
||||
protected static void init(final UITestContext context) {
|
||||
sActivity = context.getActivity();
|
||||
sSolo = context.getSolo();
|
||||
|
||||
|
@ -21,7 +21,7 @@ public final class GeckoHelper {
|
||||
|
||||
private GeckoHelper() { /* To disallow instantiation. */ }
|
||||
|
||||
public static void init(final UITestContext context) {
|
||||
protected static void init(final UITestContext context) {
|
||||
sActivity = context.getActivity();
|
||||
sActions = context.getActions();
|
||||
}
|
||||
|
27
mobile/android/base/tests/helpers/HelperInitializer.java
Normal file
27
mobile/android/base/tests/helpers/HelperInitializer.java
Normal file
@ -0,0 +1,27 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.tests.helpers;
|
||||
|
||||
import org.mozilla.gecko.tests.UITestContext;
|
||||
|
||||
/**
|
||||
* AssertionHelper is statically imported in many places. Thus we want to hide
|
||||
* its init method outside of this package. We initialize the remaining helper
|
||||
* classes from here so that all the init methods are package protected.
|
||||
*/
|
||||
public final class HelperInitializer {
|
||||
|
||||
private HelperInitializer() { /* To disallow instantiation. */ }
|
||||
|
||||
public static void init(final UITestContext context) {
|
||||
// Other helpers make assertions so init AssertionHelper first.
|
||||
AssertionHelper.init(context);
|
||||
|
||||
DeviceHelper.init(context);
|
||||
GeckoHelper.init(context);
|
||||
NavigationHelper.init(context);
|
||||
WaitHelper.init(context);
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ final public class NavigationHelper {
|
||||
|
||||
private static ToolbarComponent sToolbar;
|
||||
|
||||
public static void init(final UITestContext context) {
|
||||
protected static void init(final UITestContext context) {
|
||||
sContext = context;
|
||||
sSolo = context.getSolo();
|
||||
|
||||
|
@ -40,7 +40,7 @@ public final class WaitHelper {
|
||||
|
||||
private WaitHelper() { /* To disallow instantiation. */ }
|
||||
|
||||
public static void init(final UITestContext context) {
|
||||
protected static void init(final UITestContext context) {
|
||||
sContext = context;
|
||||
sSolo = context.getSolo();
|
||||
sActions = context.getActions();
|
||||
|
@ -75,13 +75,7 @@ var SelectionHandler = {
|
||||
observe: function sh_observe(aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case "Gesture:SingleTap": {
|
||||
if (this._activeType == this.TYPE_SELECTION) {
|
||||
let data = JSON.parse(aData);
|
||||
if (this._pointInSelection(data.x, data.y))
|
||||
this.copySelection();
|
||||
else
|
||||
this._closeSelection();
|
||||
} else if (this._activeType == this.TYPE_CURSOR) {
|
||||
if (this._activeType == this.TYPE_CURSOR) {
|
||||
// attachCaret() is called in the "Gesture:SingleTap" handler in BrowserEventHandler
|
||||
// We're guaranteed to call this first, because this observer was added last
|
||||
this._deactivate();
|
||||
@ -239,21 +233,14 @@ var SelectionHandler = {
|
||||
// Clear any existing selection from the document
|
||||
this._contentWindow.getSelection().removeAllRanges();
|
||||
|
||||
if (aOptions.mode == this.SELECT_ALL) {
|
||||
this._getSelectionController().selectAll();
|
||||
} else if (aOptions.mode == this.SELECT_AT_POINT) {
|
||||
if (!this._domWinUtils.selectAtPoint(aOptions.x, aOptions.y, Ci.nsIDOMWindowUtils.SELECT_WORDNOSPACE)) {
|
||||
this._deactivate();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
Services.console.logStringMessage("Invalid selection mode " + aOptions.mode);
|
||||
// Perform the appropriate selection method, if we can't determine method, or it fails, return
|
||||
if (!this._performSelection(aOptions)) {
|
||||
this._deactivate();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Double check results of successful selection operation
|
||||
let selection = this._getSelection();
|
||||
// If the range didn't have any text, let's bail
|
||||
if (!selection || selection.rangeCount == 0 || selection.getRangeAt(0).collapsed) {
|
||||
this._deactivate();
|
||||
return false;
|
||||
@ -283,6 +270,29 @@ var SelectionHandler = {
|
||||
return true;
|
||||
},
|
||||
|
||||
/*
|
||||
* Called to perform a selection operation, given a target element, selection method, starting point etc.
|
||||
*/
|
||||
_performSelection: function sh_performSelection(aOptions) {
|
||||
if (aOptions.mode == this.SELECT_ALL) {
|
||||
if (this._targetElement instanceof HTMLPreElement) {
|
||||
// Use SELECT_PARAGRAPH else we default to entire page including trailing whitespace
|
||||
return this._domWinUtils.selectAtPoint(1, 1, Ci.nsIDOMWindowUtils.SELECT_PARAGRAPH);
|
||||
} else {
|
||||
// Else default to selectALL Document
|
||||
this._getSelectionController().selectAll();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (aOptions.mode == this.SELECT_AT_POINT) {
|
||||
return this._domWinUtils.selectAtPoint(aOptions.x, aOptions.y, Ci.nsIDOMWindowUtils.SELECT_WORDNOSPACE);
|
||||
}
|
||||
|
||||
Services.console.logStringMessage("Invalid selection mode " + aOptions.mode);
|
||||
return false;
|
||||
},
|
||||
|
||||
/* Return true if the current selection (given by aPositions) is near to where the coordinates passed in */
|
||||
_selectionNearClick: function(aX, aY, aPositions) {
|
||||
let distance = 0;
|
||||
@ -517,14 +527,7 @@ var SelectionHandler = {
|
||||
},
|
||||
|
||||
selectAll: function sh_selectAll(aElement) {
|
||||
if (this._activeType != this.TYPE_SELECTION) {
|
||||
this.startSelection(aElement, { mode : this.SELECT_ALL });
|
||||
} else {
|
||||
let selectionController = this._getSelectionController();
|
||||
selectionController.selectAll();
|
||||
this._updateCacheForSelection();
|
||||
this._positionHandles();
|
||||
}
|
||||
this.startSelection(aElement, { mode : this.SELECT_ALL });
|
||||
},
|
||||
|
||||
/*
|
||||
|
116
mobile/android/chrome/content/aboutFeedback.js
Normal file
116
mobile/android/chrome/content/aboutFeedback.js
Normal file
@ -0,0 +1,116 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
let Cc = Components.classes;
|
||||
let Ci = Components.interfaces;
|
||||
let Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function dump(a) {
|
||||
Services.console.logStringMessage(a);
|
||||
}
|
||||
|
||||
function sendMessageToJava(aMessage) {
|
||||
Services.androidBridge.handleGeckoMessage(JSON.stringify(aMessage));
|
||||
}
|
||||
|
||||
function init() {
|
||||
let sumoLink = Services.urlFormatter.formatURLPref("app.support.baseURL");
|
||||
document.getElementById("sumo-link").href = sumoLink;
|
||||
|
||||
window.addEventListener("popstate", function (aEvent) {
|
||||
updateActiveSection(aEvent.state ? aEvent.state.section : "intro")
|
||||
}, false);
|
||||
|
||||
// Fill "Last visited site" input with most recent history entry URL.
|
||||
Services.obs.addObserver(function observer(aSubject, aTopic, aData) {
|
||||
document.getElementById("last-url").value = aData;
|
||||
}, "Feedback:LastUrl", false);
|
||||
|
||||
sendMessageToJava({ type: "Feedback:LastUrl" });
|
||||
}
|
||||
|
||||
function uninit() {
|
||||
Services.obs.removeObserver(this, "Feedback:LastUrl");
|
||||
}
|
||||
|
||||
function switchSection(aSection) {
|
||||
history.pushState({ section: aSection }, aSection);
|
||||
updateActiveSection(aSection);
|
||||
}
|
||||
|
||||
function updateActiveSection(aSection) {
|
||||
document.querySelector("section[active]").removeAttribute("active");
|
||||
document.getElementById(aSection).setAttribute("active", true);
|
||||
}
|
||||
|
||||
function openPlayStore() {
|
||||
sendMessageToJava({ type: "Feedback:OpenPlayStore" });
|
||||
|
||||
window.close();
|
||||
}
|
||||
|
||||
function maybeLater() {
|
||||
window.close();
|
||||
|
||||
sendMessageToJava({ type: "Feedback:MaybeLater" });
|
||||
}
|
||||
|
||||
function sendFeedback(aEvent) {
|
||||
// Prevent the page from reloading.
|
||||
aEvent.preventDefault();
|
||||
|
||||
let section = history.state.section;
|
||||
|
||||
// Sanity check.
|
||||
if (section != "sad") {
|
||||
Cu.reportError("Trying to send feedback from an invalid section: " + section);
|
||||
return;
|
||||
}
|
||||
|
||||
let sectionElement = document.getElementById(section);
|
||||
let descriptionElement = sectionElement.querySelector(".description");
|
||||
|
||||
// Bail if the description value isn't valid. HTML5 form validation will take care
|
||||
// of showing an error message for us.
|
||||
if (!descriptionElement.validity.valid)
|
||||
return;
|
||||
|
||||
let data = new FormData();
|
||||
data.append("description", descriptionElement.value);
|
||||
data.append("_type", 2);
|
||||
|
||||
let urlElement = document.getElementById("last-url");
|
||||
// Bail if the URL value isn't valid. HTML5 form validation will take care
|
||||
// of showing an error message for us.
|
||||
if (!urlElement.validity.valid)
|
||||
return;
|
||||
|
||||
// Only send a URL string if the user provided one.
|
||||
if (urlElement.value) {
|
||||
data.append("add_url", true);
|
||||
data.append("url", urlElement.value);
|
||||
}
|
||||
|
||||
let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
|
||||
data.append("device", sysInfo.get("device"));
|
||||
data.append("manufacturer", sysInfo.get("manufacturer"));
|
||||
|
||||
let req = new XMLHttpRequest();
|
||||
req.addEventListener("error", function() {
|
||||
Cu.reportError("Error sending feedback to input.mozilla.org: " + req.statusText);
|
||||
}, false);
|
||||
req.addEventListener("abort", function() {
|
||||
Cu.reportError("Aborted sending feedback to input.mozilla.org: " + req.statusText);
|
||||
}, false);
|
||||
|
||||
let postURL = Services.urlFormatter.formatURLPref("app.feedback.postURL");
|
||||
req.open("POST", postURL, true);
|
||||
req.send(data);
|
||||
|
||||
switchSection("thanks-" + section);
|
||||
}
|
@ -74,118 +74,6 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script type="application/javascript;version=1.8"><![CDATA[
|
||||
let Cc = Components.classes;
|
||||
let Ci = Components.interfaces;
|
||||
let Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function dump(a) {
|
||||
Services.console.logStringMessage(a);
|
||||
}
|
||||
|
||||
function sendMessageToJava(aMessage) {
|
||||
Services.androidBridge.handleGeckoMessage(JSON.stringify(aMessage));
|
||||
}
|
||||
|
||||
function init() {
|
||||
let sumoLink = Services.urlFormatter.formatURLPref("app.support.baseURL");
|
||||
document.getElementById("sumo-link").href = sumoLink;
|
||||
|
||||
window.addEventListener("popstate", function (aEvent) {
|
||||
updateActiveSection(aEvent.state ? aEvent.state.section : "intro")
|
||||
}, false);
|
||||
|
||||
// Fill "Last visited site" input with most recent history entry URL.
|
||||
Services.obs.addObserver(function observer(aSubject, aTopic, aData) {
|
||||
document.getElementById("last-url").value = aData;
|
||||
}, "Feedback:LastUrl", false);
|
||||
|
||||
sendMessageToJava({ type: "Feedback:LastUrl" });
|
||||
}
|
||||
|
||||
function uninit() {
|
||||
Services.obs.removeObserver(this, "Feedback:LastUrl");
|
||||
}
|
||||
|
||||
function switchSection(aSection) {
|
||||
history.pushState({ section: aSection }, aSection);
|
||||
updateActiveSection(aSection);
|
||||
}
|
||||
|
||||
function updateActiveSection(aSection) {
|
||||
document.querySelector("section[active]").removeAttribute("active");
|
||||
document.getElementById(aSection).setAttribute("active", true);
|
||||
}
|
||||
|
||||
function openPlayStore() {
|
||||
sendMessageToJava({ type: "Feedback:OpenPlayStore" });
|
||||
|
||||
window.close();
|
||||
}
|
||||
|
||||
function maybeLater() {
|
||||
window.close();
|
||||
|
||||
sendMessageToJava({ type: "Feedback:MaybeLater" });
|
||||
}
|
||||
|
||||
function sendFeedback(aEvent) {
|
||||
// Prevent the page from reloading.
|
||||
aEvent.preventDefault();
|
||||
|
||||
let section = history.state.section;
|
||||
|
||||
// Sanity check.
|
||||
if (section != "sad") {
|
||||
Cu.reportError("Trying to send feedback from an invalid section: " + section);
|
||||
return;
|
||||
}
|
||||
|
||||
let sectionElement = document.getElementById(section);
|
||||
let descriptionElement = sectionElement.querySelector(".description");
|
||||
|
||||
// Bail if the description value isn't valid. HTML5 form validation will take care
|
||||
// of showing an error message for us.
|
||||
if (!descriptionElement.validity.valid)
|
||||
return;
|
||||
|
||||
let data = new FormData();
|
||||
data.append("description", descriptionElement.value);
|
||||
data.append("_type", 2);
|
||||
|
||||
let urlElement = document.getElementById("last-url");
|
||||
// Bail if the URL value isn't valid. HTML5 form validation will take care
|
||||
// of showing an error message for us.
|
||||
if (!urlElement.validity.valid)
|
||||
return;
|
||||
|
||||
// Only send a URL string if the user provided one.
|
||||
if (urlElement.value) {
|
||||
data.append("add_url", true);
|
||||
data.append("url", urlElement.value);
|
||||
}
|
||||
|
||||
let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
|
||||
data.append("device", sysInfo.get("device"));
|
||||
data.append("manufacturer", sysInfo.get("manufacturer"));
|
||||
|
||||
let req = new XMLHttpRequest();
|
||||
req.addEventListener("error", function() {
|
||||
Cu.reportError("Error sending feedback to input.mozilla.org: " + req.statusText);
|
||||
}, false);
|
||||
req.addEventListener("abort", function() {
|
||||
Cu.reportError("Aborted sending feedback to input.mozilla.org: " + req.statusText);
|
||||
}, false);
|
||||
|
||||
let postURL = Services.urlFormatter.formatURLPref("app.feedback.postURL");
|
||||
req.open("POST", postURL, true);
|
||||
req.send(data);
|
||||
|
||||
switchSection("thanks-" + section);
|
||||
}
|
||||
]]></script>
|
||||
|
||||
<script type="application/javascript;version=1.8" src="chrome://browser/content/aboutFeedback.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -16,6 +16,7 @@ chrome.jar:
|
||||
content/aboutDownloads.xhtml (content/aboutDownloads.xhtml)
|
||||
content/aboutDownloads.js (content/aboutDownloads.js)
|
||||
content/aboutFeedback.xhtml (content/aboutFeedback.xhtml)
|
||||
content/aboutFeedback.js (content/aboutFeedback.js)
|
||||
content/aboutPrivateBrowsing.xhtml (content/aboutPrivateBrowsing.xhtml)
|
||||
content/aboutReader.html (content/aboutReader.html)
|
||||
content/aboutReader.js (content/aboutReader.js)
|
||||
|
@ -304,12 +304,14 @@ TabTracker.prototype = {
|
||||
|
||||
switch (topic) {
|
||||
case "domwindowopened":
|
||||
// Add tab listeners now that a window has opened.
|
||||
subject.addEventListener("load", (event) => {
|
||||
let onLoad = () => {
|
||||
subject.removeEventListener("load", onLoad, false);
|
||||
// Only register after the window is done loading to avoid unloads.
|
||||
this._registerListenersForWindow(subject);
|
||||
}, false);
|
||||
};
|
||||
|
||||
// Add tab listeners now that a window has opened.
|
||||
subject.addEventListener("load", onLoad, false);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
@ -8,6 +8,10 @@ if (typeof(Ci) == 'undefined') {
|
||||
var Ci = Components.interfaces;
|
||||
}
|
||||
|
||||
if (typeof(Cc) == 'undefined') {
|
||||
var Cc = Components.classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special Powers Exception - used to throw exceptions nicely
|
||||
**/
|
||||
|
@ -489,7 +489,7 @@ ThreadActor.prototype = {
|
||||
},
|
||||
|
||||
get youngestFrame() {
|
||||
if (!this.state == "paused") {
|
||||
if (this.state != "paused") {
|
||||
return null;
|
||||
}
|
||||
return this.dbg.getNewestFrame();
|
||||
|
@ -1014,8 +1014,12 @@ DebuggerServerConnection.prototype = {
|
||||
}
|
||||
|
||||
var ret = null;
|
||||
// Dispatch the request to the actor.
|
||||
if (actor.requestTypes && actor.requestTypes[aPacket.type]) {
|
||||
|
||||
// handle "requestTypes" RDP request.
|
||||
if (aPacket.type == "requestTypes") {
|
||||
ret = { from: actor.actorID, requestTypes: Object.keys(actor.requestTypes) };
|
||||
} else if (actor.requestTypes && actor.requestTypes[aPacket.type]) {
|
||||
// Dispatch the request to the actor.
|
||||
try {
|
||||
this.currentPacket = aPacket;
|
||||
ret = actor.requestTypes[aPacket.type].bind(actor)(aPacket, this);
|
||||
|
52
toolkit/devtools/server/tests/unit/test_requestTypes.js
Normal file
52
toolkit/devtools/server/tests/unit/test_requestTypes.js
Normal file
@ -0,0 +1,52 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
let promise = devtools.require("sdk/core/promise");
|
||||
|
||||
function test_requestTypes_request(aClient, anActor)
|
||||
{
|
||||
var calls = [];
|
||||
|
||||
calls.push(test_existent_actor(aClient, anActor));
|
||||
|
||||
promise.all(calls).then(() => {
|
||||
aClient.close(() => {
|
||||
do_test_finished();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function test_existent_actor(aClient, anActor)
|
||||
{
|
||||
let deferred = promise.defer();
|
||||
|
||||
aClient.request({ to: anActor, type: "requestTypes" }, function (aResponse) {
|
||||
var expectedRequestTypes = Object.keys(DebuggerServer.
|
||||
globalActorFactories["chromeDebugger"].
|
||||
prototype.requestTypes);
|
||||
|
||||
do_check_true(Array.isArray(aResponse.requestTypes));
|
||||
do_check_eq(JSON.stringify(aResponse.requestTypes),
|
||||
JSON.stringify(expectedRequestTypes));
|
||||
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function run_test()
|
||||
{
|
||||
DebuggerServer.init(function () { return true; });
|
||||
DebuggerServer.addBrowserActors();
|
||||
var client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
client.connect(function() {
|
||||
client.listTabs(function(aResponse) {
|
||||
test_requestTypes_request(client, aResponse.chromeDebugger);
|
||||
});
|
||||
});
|
||||
|
||||
do_test_pending();
|
||||
}
|
@ -193,3 +193,5 @@ reason = bug 820380
|
||||
[test_trace_actor-08.js]
|
||||
[test_trace_actor-09.js]
|
||||
[test_ignore_caught_exceptions.js]
|
||||
[test_requestTypes.js]
|
||||
reason = bug 937197
|
||||
|
@ -245,7 +245,7 @@ let NetworkHelper = {
|
||||
Ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE |
|
||||
Ci.nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE_IF_BUSY;
|
||||
|
||||
NetUtil.asyncFetch(channel, function (aInputStream, aStatusCode, aRequest) {
|
||||
NetUtil.asyncFetch(channel, (aInputStream, aStatusCode, aRequest) => {
|
||||
if (!components.isSuccessCode(aStatusCode)) {
|
||||
aCallback(null);
|
||||
return;
|
||||
|
@ -448,6 +448,7 @@ COMPONENT_LIBS += \
|
||||
gfxtest \
|
||||
ssltest \
|
||||
xpcom_glue_gtest \
|
||||
media_gtest \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
|
@ -145,21 +145,58 @@ struct UserInputData {
|
||||
::Touch touches[MAX_POINTERS];
|
||||
} motion;
|
||||
};
|
||||
|
||||
Modifiers DOMModifiers() const;
|
||||
};
|
||||
|
||||
Modifiers
|
||||
UserInputData::DOMModifiers() const
|
||||
{
|
||||
Modifiers result = 0;
|
||||
if (metaState & (AMETA_ALT_ON | AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
|
||||
result |= MODIFIER_ALT;
|
||||
}
|
||||
if (metaState & (AMETA_SHIFT_ON |
|
||||
AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
|
||||
result |= MODIFIER_SHIFT;
|
||||
}
|
||||
if (metaState & AMETA_FUNCTION_ON) {
|
||||
result |= MODIFIER_FN;
|
||||
}
|
||||
if (metaState & (AMETA_CTRL_ON |
|
||||
AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
|
||||
result |= MODIFIER_CONTROL;
|
||||
}
|
||||
if (metaState & (AMETA_META_ON |
|
||||
AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
|
||||
result |= MODIFIER_META;
|
||||
}
|
||||
if (metaState & AMETA_CAPS_LOCK_ON) {
|
||||
result |= MODIFIER_CAPSLOCK;
|
||||
}
|
||||
if (metaState & AMETA_NUM_LOCK_ON) {
|
||||
result |= MODIFIER_NUMLOCK;
|
||||
}
|
||||
if (metaState & AMETA_SCROLL_LOCK_ON) {
|
||||
result |= MODIFIER_SCROLLLOCK;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
sendMouseEvent(uint32_t msg, uint64_t timeMs, int x, int y, bool forwardToChildren)
|
||||
sendMouseEvent(uint32_t msg, UserInputData& data, bool forwardToChildren)
|
||||
{
|
||||
WidgetMouseEvent event(true, msg, nullptr,
|
||||
WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
|
||||
|
||||
event.refPoint.x = x;
|
||||
event.refPoint.y = y;
|
||||
event.time = timeMs;
|
||||
event.refPoint.x = data.motion.touches[0].coords.getX();
|
||||
event.refPoint.y = data.motion.touches[0].coords.getY();
|
||||
event.time = data.timeMs;
|
||||
event.button = WidgetMouseEvent::eLeftButton;
|
||||
event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
|
||||
if (msg != NS_MOUSE_MOVE)
|
||||
event.clickCount = 1;
|
||||
event.modifiers = data.DOMModifiers();
|
||||
|
||||
event.mFlags.mNoCrossProcessBoundaryForwarding = !forwardToChildren;
|
||||
|
||||
@ -206,6 +243,7 @@ sendTouchEvent(UserInputData& data, bool* captured)
|
||||
WidgetTouchEvent event(true, msg, nullptr);
|
||||
|
||||
event.time = data.timeMs;
|
||||
event.modifiers = data.DOMModifiers();
|
||||
|
||||
int32_t i;
|
||||
if (msg == NS_TOUCH_END) {
|
||||
@ -231,6 +269,9 @@ private:
|
||||
const UserInputData& mData;
|
||||
sp<KeyCharacterMap> mKeyCharMap;
|
||||
|
||||
char16_t mChar;
|
||||
char16_t mUnmodifiedChar;
|
||||
|
||||
uint32_t mDOMKeyCode;
|
||||
KeyNameIndex mDOMKeyNameIndex;
|
||||
char16_t mDOMPrintableKeyValue;
|
||||
@ -244,9 +285,21 @@ private:
|
||||
return IsKeyPress() && (mData.flags & AKEY_EVENT_FLAG_LONG_PRESS);
|
||||
}
|
||||
|
||||
uint32_t CharCode() const;
|
||||
char16_t PrintableKeyValue() const;
|
||||
|
||||
int32_t UnmodifiedMetaState() const
|
||||
{
|
||||
return mData.metaState &
|
||||
~(AMETA_ALT_ON | AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON |
|
||||
AMETA_CTRL_ON | AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON |
|
||||
AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
|
||||
}
|
||||
|
||||
static bool IsControlChar(char16_t aChar)
|
||||
{
|
||||
return (aChar < ' ' || aChar == 0x7F);
|
||||
}
|
||||
|
||||
void DispatchKeyDownEvent();
|
||||
void DispatchKeyUpEvent();
|
||||
nsEventStatus DispatchKeyEventInternal(uint32_t aEventMessage);
|
||||
@ -254,47 +307,44 @@ private:
|
||||
|
||||
KeyEventDispatcher::KeyEventDispatcher(const UserInputData& aData,
|
||||
KeyCharacterMap* aKeyCharMap) :
|
||||
mData(aData), mKeyCharMap(aKeyCharMap)
|
||||
mData(aData), mKeyCharMap(aKeyCharMap), mChar(0), mUnmodifiedChar(0),
|
||||
mDOMPrintableKeyValue(0)
|
||||
{
|
||||
// XXX Printable key's keyCode value should be computed with actual
|
||||
// input character.
|
||||
mDOMKeyCode = (mData.key.keyCode < ArrayLength(kKeyMapping)) ?
|
||||
kKeyMapping[mData.key.keyCode] : 0;
|
||||
mDOMKeyNameIndex = GetKeyNameIndex(mData.key.keyCode);
|
||||
mDOMPrintableKeyValue = PrintableKeyValue();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
KeyEventDispatcher::CharCode() const
|
||||
{
|
||||
if (!mKeyCharMap.get()) {
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
// XXX If the charCode is not a printable character, the charCode should be
|
||||
// computed without Ctrl/Alt/Meta modifiers.
|
||||
char16_t ch = mKeyCharMap->getCharacter(mData.key.keyCode, mData.metaState);
|
||||
return (ch >= ' ') ? static_cast<uint32_t>(ch) : 0;
|
||||
|
||||
mChar = mKeyCharMap->getCharacter(mData.key.keyCode, mData.metaState);
|
||||
if (IsControlChar(mChar)) {
|
||||
mChar = 0;
|
||||
}
|
||||
int32_t unmodifiedMetaState = UnmodifiedMetaState();
|
||||
if (mData.metaState == unmodifiedMetaState) {
|
||||
mUnmodifiedChar = mChar;
|
||||
} else {
|
||||
mUnmodifiedChar = mKeyCharMap->getCharacter(mData.key.keyCode,
|
||||
unmodifiedMetaState);
|
||||
if (IsControlChar(mUnmodifiedChar)) {
|
||||
mUnmodifiedChar = 0;
|
||||
}
|
||||
}
|
||||
|
||||
mDOMPrintableKeyValue = PrintableKeyValue();
|
||||
}
|
||||
|
||||
char16_t
|
||||
KeyEventDispatcher::PrintableKeyValue() const
|
||||
{
|
||||
if (mDOMKeyNameIndex != KEY_NAME_INDEX_USE_STRING || !mKeyCharMap.get()) {
|
||||
if (mDOMKeyNameIndex != KEY_NAME_INDEX_USE_STRING) {
|
||||
return 0;
|
||||
}
|
||||
char16_t ch = mKeyCharMap->getCharacter(mData.key.keyCode, mData.metaState);
|
||||
if (ch >= ' ') {
|
||||
return static_cast<char16_t>(ch);
|
||||
}
|
||||
int32_t unmodifiedMetaState = mData.metaState &
|
||||
~(AMETA_ALT_ON | AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON |
|
||||
AMETA_CTRL_ON | AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON |
|
||||
AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
|
||||
if (unmodifiedMetaState == mData.metaState) {
|
||||
return 0;
|
||||
}
|
||||
ch = mKeyCharMap->getCharacter(mData.key.keyCode, unmodifiedMetaState);
|
||||
return (ch >= ' ') ? static_cast<char16_t>(ch) : 0;
|
||||
return mChar ? mChar : mUnmodifiedChar;
|
||||
}
|
||||
|
||||
nsEventStatus
|
||||
@ -302,7 +352,9 @@ KeyEventDispatcher::DispatchKeyEventInternal(uint32_t aEventMessage)
|
||||
{
|
||||
WidgetKeyboardEvent event(true, aEventMessage, nullptr);
|
||||
if (aEventMessage == NS_KEY_PRESS) {
|
||||
event.charCode = CharCode();
|
||||
// XXX If the charCode is not a printable character, the charCode
|
||||
// should be computed without Ctrl/Alt/Meta modifiers.
|
||||
event.charCode = static_cast<uint32_t>(mChar);
|
||||
}
|
||||
if (!event.charCode) {
|
||||
event.keyCode = mDOMKeyCode;
|
||||
@ -313,6 +365,7 @@ KeyEventDispatcher::DispatchKeyEventInternal(uint32_t aEventMessage)
|
||||
if (mDOMPrintableKeyValue) {
|
||||
event.mKeyValue = mDOMPrintableKeyValue;
|
||||
}
|
||||
event.modifiers = mData.DOMModifiers();
|
||||
event.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_MOBILE;
|
||||
event.time = mData.timeMs;
|
||||
return nsWindow::DispatchInputEvent(event);
|
||||
@ -634,11 +687,7 @@ GeckoInputDispatcher::dispatchOnce()
|
||||
msg = NS_MOUSE_BUTTON_UP;
|
||||
break;
|
||||
}
|
||||
sendMouseEvent(msg,
|
||||
data.timeMs,
|
||||
data.motion.touches[0].coords.getX(),
|
||||
data.motion.touches[0].coords.getY(),
|
||||
status != nsEventStatus_eConsumeNoDefault);
|
||||
sendMouseEvent(msg, data, status != nsEventStatus_eConsumeNoDefault);
|
||||
break;
|
||||
}
|
||||
case UserInputData::KEY_DATA: {
|
||||
|
Loading…
Reference in New Issue
Block a user