mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Merge autoland to mozilla-central. a=merge
This commit is contained in:
commit
a8850882a7
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,6 +15,7 @@ ID
|
||||
|
||||
# Vim swap files.
|
||||
.*.sw[a-z]
|
||||
.sw[a-z]
|
||||
|
||||
# Emacs directory variable files.
|
||||
**/.dir-locals.el
|
||||
|
@ -759,6 +759,9 @@ BrowserGlue.prototype = {
|
||||
|
||||
// Initialize the default l10n resource sources for L10nRegistry.
|
||||
let locales = Services.locale.getPackagedLocales();
|
||||
const greSource = new FileSource("toolkit", locales, "resource://gre/localization/{locale}/");
|
||||
L10nRegistry.registerSource(greSource);
|
||||
|
||||
const appSource = new FileSource("app", locales, "resource://app/localization/{locale}/");
|
||||
L10nRegistry.registerSource(appSource);
|
||||
|
||||
|
@ -278,3 +278,22 @@ add_task(async function window_open_form_test() {
|
||||
gBrowser.removeTab(tab);
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
});
|
||||
|
||||
/**
|
||||
* A test for using an IP address as the first party domain.
|
||||
*/
|
||||
add_task(async function ip_address_test() {
|
||||
const ipAddr = "127.0.0.1";
|
||||
const ipHost = `http://${ipAddr}/browser/browser/components/originattributes/test/browser/`;
|
||||
|
||||
let tab = BrowserTestUtils.addTab(gBrowser, ipHost + "test_firstParty.html");
|
||||
await BrowserTestUtils.browserLoaded(tab.linkedBrowser, true);
|
||||
|
||||
await ContentTask.spawn(tab.linkedBrowser, { firstPartyDomain: ipAddr }, async function(attrs) {
|
||||
info("document principal: " + content.document.nodePrincipal.origin);
|
||||
Assert.equal(content.document.nodePrincipal.originAttributes.firstPartyDomain,
|
||||
attrs.firstPartyDomain, "The firstPartyDomain should be set properly for the IP address");
|
||||
});
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
|
@ -6,13 +6,12 @@
|
||||
<ShortName>Freelang (br)</ShortName>
|
||||
<Description>Geriadur Freelang</Description>
|
||||
<InputEncoding>UTF-8</InputEncoding>
|
||||
<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAEAAAAAAAAAAAAAAAEAAAAAAAD5+vYAQxGlAHp1DQDMzJoAnp4+AIWEDADc3b0AgH8CAJmZNADS06gAe3oAAJ6ePwCCgQUAfn0AALGxZAB/fQAAgIAAAIGAAABcP14AtrZvAIKDAACGhQsAuLhyAIqKFgDS0acAx8eSAP7+/gB7dg0AeHETALOzaACtrFsAc2ofAMLCiABPJ4MAi4oXAHh4AAB5eAAA+vv3AIGAAgB6eAAAubp2AFw/YABMIYwA9PTqAHx7AACNjBoAmJgyAH9+AABgRlUAgIEAAOjo0wCBgQAApKRKAIGAAwD7+/gAurp3AIKEAAC3t28Ag4QAAPT06wCXljAAzc2cAEUWnAB0ax4AqalVALu8egCjokgAjo4eAKurWACIhxEAhYQJALCwYwDq6tcA4+PKAJ+fQQCgn0EA+fn3AG5gLgB/fgIAyMiTAHp5AAD///8AiosPALe3cQCQkCIAfXwAAEMRpAB/fwAAgH8AAIF/AACBggAAi4sYAIKCAACPjiAA4eLGAJ2ePQChoUUA0NGkAHZtGwDJypcAh4YOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAERERERERERERLwc8LBEREREREREREREREFhYWzdVERFYJg0RERERERERES8ZNFgRWEVHClgQERERERFYYExQEVkRLWM1LxFYERFYL1NRVBERWBFYYU9DJCwsJEIaURURERFZWCdbCQAySSVRURMvERFZVUBeK0gDKEQWHmQvEREREUtRBgguBB0YXw0RWREREVg5OyNYIFFdCgsOVRERERERWzIsEQU2PS9YRkpXWBEREQ9BIhERDBcRERFSWBERERERVQtcFBERWBEzEhwRERERERFYMCpaEVkRTjEQWRERERERET8+Hyk4TT8hXBEREREREVgROgJWGwFiOlgREQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</Image>
|
||||
<Url type="text/html" method="POST" template="http://www.freelang.com/enligne/breton.php?lg=fr" resultdomain="freelang.com">
|
||||
<Image width="16" height="16">data:image/x-icon;base64,AAABAAIAEBAAAAEACAC8AgAAJgAAACAgAAABAAgABgYAAOICAACJUE5HDQoaCgAAAA1JSERSAAAAEAAAABAIBgAAAB/z/2EAAAKDSURBVDiNfZNLSFRRGMd/595jo811emiOSvTQKSMtKAlb5Gi4aCSKKEJ64KoWBUEQBIWBRRRBRLQpiB5UEDSLMiILKshFaGmLHlZkUUFS46OZZsKcO/d+LWYaTaH/4qzO+X/f//t+R9F6WBgvEVAKpQ2s2Ahnr3axq3kl5650sn9zNf3+fMR2QCkADCZKKeZE4pw/08GMxCgPKkpI+DycCFViOi5HrnaDOfZMTzIQoaZvkKjXw5cSH5f9PrCFF/MKMF2XQ1uXgeuCKFATDFQqRbBsPmHTIFxbBs7fdAIoHNOg+dF77iyfzfC0vH8jKMflQH2QN4ND4Ao4Qo5SFE3Ny+ZFoG3tUoZ9udmiWQO/z2JL1RIisRgAltbEDx7gWEMDynVBhDytqZ41K+0kMm4GIjRVVlFsWeCkQGtagrV4tObo4w4klaIuUM6CggIuPOsGrSd0oBQvv3+j0Ovl4c4drCgt4UZvL3WXLhFaEOB6UxMOwoXuHkTr9EgyMqlf3YrAp6EhXg8OUDt3LntqamgMBJiem8svO8nT/n4WFRZSNnMmn6NRkik7OxeVBUkyx2iS4oIZnA6tYX1FBR+GfzBi2ywp9pOrcwDY297Omc4uxDAyBhn6EOHaxg38GPnNySdP+BwZANNM9+o4hBYv5u72bSilyD92nIRto9MrVpRaFi3BVTTfbENcFzEM8HjGVqg19/v62HfvPqcaQxR5p5KIxjBAmO7x0BgoZ/et27giiKGyWI9HXJTi1UCE38kkH79Hxtb4c3SUiz3PYcqUSWT/Q6oI6xYuZFM4nL1roBQuIH+zTlIGGhH8+RZf43Ha3777z2+cXBclggn4vRYnHnekWcjoD87z7kUU/IwHAAAAAElFTkSuQmCCiVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAFzUlEQVRYhbWXaWxUVRTHf/e9mc7CzBSmlC5MQYqtpSjQKQITWhUhRFCjISyS+MEVjV/co5KYaPxg/GDUxCXRmIgRYpStlppoRaKIFK0o1FppS6F2gbbTdugyr53lXT+8FmaYGVqKng/vJfeee87//O85954reOVVyRTFFImy8dg/FHVc4KPVBXS47ayqP8+9v7axq2wexwoyJ7YxVedIyfrfO3isuhGAI0Wz8LssPFf5F/bRCNWLciYXxNU4dIyGWftHJ/ZQhN0r5uLSwmhpKn/mTedw0SxCqkLFUg+BaWl8XTKbOT2DPHqwmQPe2QYbQkwdQFpU5/mKem5p6AbgVG46X/jm8mNxFl3pNkbNCiD4cE0hCEDC5qOtrGjsoXpR9rUzkBXQWHq6D4A+h4WmHCchs4l/ZjriFcejFJIdt85n37I8Tme5kkY/eQBS4k+3snd5Hu7hEPtvziNgt0ywSNCTbqPHZcUc1YkqAl0RGPRcBQBVwJLsbNoGBvj49oJx2wmGUklOQOPtT35lZ9k8vlqal7BMmcjANq8Xs6rSPRw0aBSJUVxJwqrgm8W5/FCclXQ+JQMmIdheXoYWDlPT3pG4h3Ls+BhLuFR77Hda+XxtkaEeiSTMJzIgJUjJ5oULeHDJEj6o/S3RqgRFCNbOz2ebtwSrKfVOCgE+jwctGk0eaLLBLIedd+5Yx96GBoZCofjopMSkKLy3fh0Pe71I4Pj5Lmo7zyUBKllXcD2dg4MG6iQsJc2Bx0qXkmG309IfSIr6oZLFPOz1ogjB4OgoAW0kwTFI7r6hkGyHgxNd3UntQBIGzKrKXYVGtntczoQF061Wnvb5UBUFKSX7Ghpo6e+Pc243m3ly+TJ0KXnj558xEiV5jiQwYDebSbdaAdiwYAFum/VSwgFZ0+zkOpxIKanv7uKlgwfRxyJGSlbmefhy00b8msabNTUpHadkYDgU4vzQMIUZM8lxOtm7ZTNPHKjiL38vCEHbwCAfHf8NLRLh7aPH6NU03DYrZXlzeMRbgtNq4flvq42cSFEZsSISrmMp2XrjQj7bsAFljGZ/MMiBxkYOt7bSEggQjkZxW63ku934PB5Kc3NBwls1R/n05EmGQ+FJOU8OAAkSnri5lBfLyvG4XEkXSiS9wSA17e3sqvuTPQ0NqEIhzWRCl5KRcJiIrl90k2onkgC4BMLjcrImP5/S3BxynU5UodCnaZwNBDjeeY6hcIiCDDcl2TkUZ87EbbNjUVV0JMOhMM19fXx7+jT7/v6b/pERkqFIASBZyBIEFLjd3Ft0A/fftIjiWZmoIj6PRQz1EV1HFYI+TeOF6mp2nDxJRI93N+nbsDDDzbM+31hl2OgOBvn+zBka/b34g0Eius50q5WizExWeGaTYbOjCoEWiZBht/Pu+vVYTSbeq62NY+LKAKQk2zGNZ3w+Hi8tpSUQ4K2aGqqamqjr6kaXumFsPOqxck1TVTYVF7O9vIwFmZnoUmIxmXh9zWo+r6+nN+bgSg1ASm6dO5f371yHP6ixZc8eDre2MhQKG/NCwGX0jwMJ6To76+o4dPYMFVvvozQnl6iUONIslM+Zw/5TjVcAICWqovDcSh9bFt7Ii98dpKqpGd248iZdXghB5+AQj1ZUcujBB0i3GA1Mhs0Wp5ZwEpoUhddW3cb8GTO4c+dOKhubxk66yfcAsSDqerqp7Wi/ONQ+MJACgJHkbC8vo3t4iG2VVZwbGo7xO4Xng5REdZ3msbviwsgIR9ra4lRM44oA9xQW0nZhgB0nTlyMwPhfve9YcaVZQEp21dUZOSRiqyAmsB/OttI/Ohrv/BrFZbGwODublkCA1386kmDXFNtg9l/efPwHstLjIX/GdLbu3kP74ACX0zlhU3otkqaqvFBexsvfH6IipvRiZepvw8tFjn+MLlUIeGr5MipPNfLOsV+u8WEyeQQX/6uuu476nh6qmpqN4f8fwCXJsNlo6u2lbWBwQt1/AUtVOfRNAUAmAAAAAElFTkSuQmCC</Image>
|
||||
<Url type="text/html" method="POST" template="https://www.freelang.com/enligne/breton.php" resultdomain="freelang.com" rel="searchform">
|
||||
<Param name="dico" value="fr_bre_fra"/>
|
||||
<Param name="lg" value="fr"/>
|
||||
<Param name="mot1" value="{searchTerms}"/>
|
||||
<Param name="mot2" value=""/>
|
||||
<Param name="entier" value="on"/>
|
||||
</Url>
|
||||
|
||||
<SearchForm>http://www.freelang.com/enligne/breton.php</SearchForm>
|
||||
</SearchPlugin>
|
||||
|
130
browser/extensions/activity-stream/bootstrap.js
vendored
130
browser/extensions/activity-stream/bootstrap.js
vendored
@ -4,7 +4,6 @@
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
@ -15,72 +14,6 @@ XPCOMUtils.defineLazyServiceGetter(this, "resProto",
|
||||
|
||||
const RESOURCE_HOST = "activity-stream";
|
||||
|
||||
const BROWSER_READY_NOTIFICATION = "sessionstore-windows-restored";
|
||||
const RESOURCE_BASE = "resource://activity-stream";
|
||||
|
||||
let activityStream;
|
||||
let modulesToUnload = new Set();
|
||||
let waitingForBrowserReady = true;
|
||||
|
||||
// Lazily load ActivityStream then find related modules to unload
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ActivityStream",
|
||||
"resource://activity-stream/lib/ActivityStream.jsm", null, null, () => {
|
||||
// Helper to fetch a resource directory listing and call back with each item
|
||||
const processListing = async (uri, cb) => {
|
||||
try {
|
||||
(await (await fetch(uri)).text())
|
||||
.split("\n").slice(2).forEach(line => cb(line.split(" ").slice(1)));
|
||||
} catch (e) {
|
||||
// Silently ignore listings that fail to load
|
||||
// probably because the resource: has been unloaded
|
||||
}
|
||||
};
|
||||
|
||||
// Look for modules one level deeper than the top resource URI
|
||||
processListing(RESOURCE_BASE, ([directory, , , type]) => {
|
||||
if (type === "DIRECTORY") {
|
||||
// Look into this directory for .jsm files
|
||||
const subDir = `${RESOURCE_BASE}/${directory}`;
|
||||
processListing(subDir, ([name]) => {
|
||||
if (name && name.search(/\.jsm$/) !== -1) {
|
||||
modulesToUnload.add(`${subDir}/${name}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* init - Initializes an instance of ActivityStream. This could be called by
|
||||
* the startup() function exposed by bootstrap.js.
|
||||
*/
|
||||
function init() {
|
||||
// Don't re-initialize
|
||||
if (activityStream && activityStream.initialized) {
|
||||
return;
|
||||
}
|
||||
activityStream = new ActivityStream();
|
||||
try {
|
||||
activityStream.init();
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* uninit - Uninitializes the activityStream instance, if it exsits.This could be
|
||||
* called by the shutdown() function exposed by bootstrap.js.
|
||||
*
|
||||
* @param {type} reason Reason for uninitialization. Could be uninstall, upgrade, or PREF_OFF
|
||||
*/
|
||||
function uninit(reason) {
|
||||
// Make sure to only uninit once in case both pref change and shutdown happen
|
||||
if (activityStream) {
|
||||
activityStream.uninit(reason);
|
||||
activityStream = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an old pref has a custom value to migrate. Clears the pref so that
|
||||
* it's the default after migrating (to avoid future need to migrate).
|
||||
@ -113,12 +46,14 @@ function migratePref(oldPrefName, cbIfNotDefault) {
|
||||
Services.prefs.clearUserPref(oldPrefName);
|
||||
}
|
||||
|
||||
/**
|
||||
* onBrowserReady - Continues startup of the add-on after browser is ready.
|
||||
*/
|
||||
function onBrowserReady() {
|
||||
waitingForBrowserReady = false;
|
||||
init();
|
||||
// The functions below are required by bootstrap.js
|
||||
|
||||
this.install = function install(data, reason) {};
|
||||
|
||||
this.startup = function startup(data, reason) {
|
||||
resProto.setSubstitutionWithFlags(RESOURCE_HOST,
|
||||
Services.io.newURI("chrome/content/", null, data.resourceURI),
|
||||
resProto.ALLOW_CONTENT_ACCESS);
|
||||
|
||||
// Do a one time migration of Tiles about:newtab prefs that have been modified
|
||||
migratePref("browser.newtabpage.rows", rows => {
|
||||
@ -140,57 +75,10 @@ function onBrowserReady() {
|
||||
migratePref("browser.newtabpage.activity-stream.topSitesCount", count => {
|
||||
Services.prefs.setIntPref("browser.newtabpage.activity-stream.topSitesRows", Math.ceil(count / 6));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* observe - nsIObserver callback to handle various browser notifications.
|
||||
*/
|
||||
function observe(subject, topic, data) {
|
||||
switch (topic) {
|
||||
case BROWSER_READY_NOTIFICATION:
|
||||
Services.obs.removeObserver(observe, BROWSER_READY_NOTIFICATION);
|
||||
// Avoid running synchronously during this event that's used for timing
|
||||
Services.tm.dispatchToMainThread(() => onBrowserReady());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The functions below are required by bootstrap.js
|
||||
|
||||
this.install = function install(data, reason) {};
|
||||
|
||||
this.startup = function startup(data, reason) {
|
||||
resProto.setSubstitutionWithFlags(RESOURCE_HOST,
|
||||
Services.io.newURI("chrome/content/", null, data.resourceURI),
|
||||
resProto.ALLOW_CONTENT_ACCESS);
|
||||
|
||||
// Only start Activity Stream up when the browser UI is ready
|
||||
if (Services.startup.startingUp) {
|
||||
Services.obs.addObserver(observe, BROWSER_READY_NOTIFICATION);
|
||||
} else {
|
||||
// Handle manual install or automatic install after manual uninstall
|
||||
onBrowserReady();
|
||||
}
|
||||
};
|
||||
|
||||
this.shutdown = function shutdown(data, reason) {
|
||||
resProto.setSubstitution(RESOURCE_HOST, null);
|
||||
|
||||
// Uninitialize Activity Stream
|
||||
uninit(reason);
|
||||
|
||||
// Stop waiting for browser to be ready
|
||||
if (waitingForBrowserReady) {
|
||||
Services.obs.removeObserver(observe, BROWSER_READY_NOTIFICATION);
|
||||
}
|
||||
|
||||
// Unload any add-on modules that might might have been imported
|
||||
modulesToUnload.forEach(Cu.unload);
|
||||
};
|
||||
|
||||
this.uninstall = function uninstall(data, reason) {
|
||||
if (activityStream) {
|
||||
activityStream.uninstall(reason);
|
||||
activityStream = null;
|
||||
}
|
||||
};
|
||||
this.uninstall = function uninstall(data, reason) {};
|
||||
|
@ -9,43 +9,74 @@ var EXPORTED_SYMBOLS = [ "AboutNewTab" ];
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "AutoMigrate",
|
||||
"resource:///modules/AutoMigrate.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "NewTabUtils",
|
||||
"resource://gre/modules/NewTabUtils.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "RemotePages",
|
||||
"resource://gre/modules/RemotePageManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
ActivityStream: "resource://activity-stream/lib/ActivityStream.jsm",
|
||||
RemotePages: "resource://gre/modules/RemotePageManager.jsm"
|
||||
});
|
||||
|
||||
const BROWSER_READY_NOTIFICATION = "sessionstore-windows-restored";
|
||||
|
||||
var AboutNewTab = {
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
|
||||
|
||||
// AboutNewTab
|
||||
|
||||
pageListener: null,
|
||||
|
||||
isOverridden: false,
|
||||
|
||||
activityStream: null,
|
||||
|
||||
/**
|
||||
* init - Initializes an instance of Activity Stream if one doesn't exist already
|
||||
* and creates the instance of Remote Page Manager which Activity Stream
|
||||
* uses for message passing.
|
||||
*
|
||||
* @param {obj} pageListener - Optional argument. An existing instance of RemotePages
|
||||
* which Activity Stream has previously made, and we
|
||||
* would like to re-use.
|
||||
*/
|
||||
init(pageListener) {
|
||||
if (this.isOverridden) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Since `init` can be called via `reset` at a later time with an existing
|
||||
// pageListener, we want to only add the observer if we are initializing
|
||||
// without this pageListener argument. This means it was the first call to `init`
|
||||
if (!pageListener) {
|
||||
Services.obs.addObserver(this, BROWSER_READY_NOTIFICATION);
|
||||
}
|
||||
|
||||
this.pageListener = pageListener || new RemotePages(["about:home", "about:newtab", "about:welcome"]);
|
||||
this.pageListener.addMessageListener("NewTab:Customize", this.customize);
|
||||
this.pageListener.addMessageListener("NewTab:MaybeShowMigrateMessage",
|
||||
this.maybeShowMigrateMessage);
|
||||
},
|
||||
|
||||
maybeShowMigrateMessage({ target }) {
|
||||
AutoMigrate.shouldShowMigratePrompt(target.browser).then((prompt) => {
|
||||
if (prompt) {
|
||||
AutoMigrate.showUndoNotificationBar(target.browser);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
customize(message) {
|
||||
NewTabUtils.allPages.enabled = message.data.enabled;
|
||||
NewTabUtils.allPages.enhanced = message.data.enhanced;
|
||||
/**
|
||||
* onBrowserReady - Continues the initialization of Activity Stream after browser is ready.
|
||||
*/
|
||||
onBrowserReady() {
|
||||
if (this.activityStream && this.activityStream.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.activityStream = new ActivityStream();
|
||||
try {
|
||||
this.activityStream.init();
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* uninit - Uninitializes Activity Stream if it exists, and destroys the pageListener
|
||||
* if it exists.
|
||||
*/
|
||||
uninit() {
|
||||
if (this.activityStream) {
|
||||
this.activityStream.uninit();
|
||||
this.activityStream = null;
|
||||
}
|
||||
|
||||
if (this.pageListener) {
|
||||
this.pageListener.destroy();
|
||||
this.pageListener = null;
|
||||
@ -59,9 +90,6 @@ var AboutNewTab = {
|
||||
return null;
|
||||
if (shouldPassPageListener) {
|
||||
this.pageListener = null;
|
||||
pageListener.removeMessageListener("NewTab:Customize", this.customize);
|
||||
pageListener.removeMessageListener("NewTab:MaybeShowMigrateMessage",
|
||||
this.maybeShowMigrateMessage);
|
||||
return pageListener;
|
||||
}
|
||||
this.uninit();
|
||||
@ -71,5 +99,17 @@ var AboutNewTab = {
|
||||
reset(pageListener) {
|
||||
this.isOverridden = false;
|
||||
this.init(pageListener);
|
||||
},
|
||||
|
||||
// nsIObserver implementation
|
||||
|
||||
observe(subject, topic, data) {
|
||||
switch (topic) {
|
||||
case BROWSER_READY_NOTIFICATION:
|
||||
Services.obs.removeObserver(this, BROWSER_READY_NOTIFICATION);
|
||||
// Avoid running synchronously during this event that's used for timing
|
||||
Services.tm.dispatchToMainThread(() => this.onBrowserReady());
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "nsIEffectiveTLDService.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsIURIWithPrincipal.h"
|
||||
#include "nsURLHelper.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -58,6 +59,28 @@ OriginAttributes::SetFirstPartyDomain(const bool aIsTopLevelDocument,
|
||||
return;
|
||||
}
|
||||
|
||||
if (rv == NS_ERROR_HOST_IS_IP_ADDRESS) {
|
||||
// If the host is an IPv4/IPv6 address, we still accept it as a
|
||||
// valid firstPartyDomain.
|
||||
nsAutoCString ipAddr;
|
||||
rv = aURI->GetHost(ipAddr);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
if (net_IsValidIPv6Addr(ipAddr.BeginReading(), ipAddr.Length())) {
|
||||
// According to RFC2732, the host of an IPv6 address should be an
|
||||
// IPv6reference. The GetHost() of nsIURI will only return the IPv6
|
||||
// address. So, we need to convert it back to IPv6reference here.
|
||||
mFirstPartyDomain.Truncate();
|
||||
mFirstPartyDomain.AssignLiteral("[");
|
||||
mFirstPartyDomain.Append(NS_ConvertUTF8toUTF16(ipAddr));
|
||||
mFirstPartyDomain.AppendLiteral("]");
|
||||
} else {
|
||||
mFirstPartyDomain = NS_ConvertUTF8toUTF16(ipAddr);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoCString scheme;
|
||||
rv = aURI->GetScheme(scheme);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
@ -65,8 +65,13 @@ pref("devtools.inspector.shapesHighlighter.enabled", true);
|
||||
pref("devtools.flexboxinspector.enabled", false);
|
||||
// Enable the new Animation Inspector
|
||||
pref("devtools.new-animationinspector.enabled", true);
|
||||
// Enable the Variable Fonts editor
|
||||
|
||||
// Enable the Variable Fonts editor only in Nightly
|
||||
#if defined(NIGHTLY_BUILD)
|
||||
pref("devtools.inspector.fonteditor.enabled", true);
|
||||
#else
|
||||
pref("devtools.inspector.fonteditor.enabled", false);
|
||||
#endif
|
||||
// Enable the font highlight-on-hover feature
|
||||
pref("devtools.inspector.fonthighlighter.enabled", false);
|
||||
|
||||
|
@ -28,6 +28,7 @@ const MAX_VERTICAL_OFFSET = 3;
|
||||
// line in text selection.
|
||||
const RE_SCRATCHPAD_ERROR = /(?:@Scratchpad\/\d+:|\()(\d+):?(\d+)?(?:\)|\n)/;
|
||||
const RE_JUMP_TO_LINE = /^(\d+):?(\d+)?/;
|
||||
const AUTOCOMPLETE_MARK_CLASSNAME = "cm-auto-complete-shadow-text";
|
||||
|
||||
const Services = require("Services");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
@ -58,22 +59,23 @@ const CM_SCRIPTS = [
|
||||
const CM_IFRAME = "chrome://devtools/content/sourceeditor/codemirror/cmiframe.html";
|
||||
|
||||
const CM_MAPPING = [
|
||||
"clearHistory",
|
||||
"defaultCharWidth",
|
||||
"extendSelection",
|
||||
"focus",
|
||||
"getCursor",
|
||||
"getScrollInfo",
|
||||
"getSelection",
|
||||
"getViewport",
|
||||
"hasFocus",
|
||||
"lineCount",
|
||||
"somethingSelected",
|
||||
"getCursor",
|
||||
"setSelection",
|
||||
"getSelection",
|
||||
"replaceSelection",
|
||||
"extendSelection",
|
||||
"undo",
|
||||
"redo",
|
||||
"clearHistory",
|
||||
"openDialog",
|
||||
"redo",
|
||||
"refresh",
|
||||
"getScrollInfo",
|
||||
"getViewport"
|
||||
"replaceSelection",
|
||||
"setSelection",
|
||||
"somethingSelected",
|
||||
"undo",
|
||||
];
|
||||
|
||||
const editors = new WeakMap();
|
||||
@ -179,6 +181,16 @@ function Editor(config) {
|
||||
// indenting with tabs, insert one tab. Otherwise insert N
|
||||
// whitespaces where N == indentUnit option.
|
||||
this.config.extraKeys.Tab = cm => {
|
||||
if (config.extraKeys && config.extraKeys.Tab) {
|
||||
// If a consumer registers its own extraKeys.Tab, we execute it before doing
|
||||
// anything else. If it returns false, that mean that all the key handling work is
|
||||
// done, so we can do an early return.
|
||||
const res = config.extraKeys.Tab(cm);
|
||||
if (res === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (cm.somethingSelected()) {
|
||||
cm.indentSelection("add");
|
||||
return;
|
||||
@ -1262,6 +1274,32 @@ Editor.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
getAutoCompletionText() {
|
||||
const cm = editors.get(this);
|
||||
const mark = cm.getAllMarks().find(m => m.className === AUTOCOMPLETE_MARK_CLASSNAME);
|
||||
if (!mark) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return mark.title || "";
|
||||
},
|
||||
|
||||
setAutoCompletionText: function(text) {
|
||||
const cursor = this.getCursor();
|
||||
const cm = editors.get(this);
|
||||
const className = AUTOCOMPLETE_MARK_CLASSNAME;
|
||||
|
||||
cm.getAllMarks().forEach(mark => {
|
||||
if (mark.className === className) {
|
||||
mark.clear();
|
||||
}
|
||||
});
|
||||
|
||||
if (text) {
|
||||
cm.markText({...cursor, ch: cursor.ch - 1}, cursor, { className, title: text });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Extends an instance of the Editor object with additional
|
||||
* functions. Each function will be called with context as
|
||||
|
@ -382,6 +382,11 @@ textarea.jsterm-input-node:focus {
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.jsterm-cm .cm-auto-complete-shadow-text::after {
|
||||
content: attr(title);
|
||||
color: var(--theme-comment);
|
||||
}
|
||||
|
||||
/* Security styles */
|
||||
|
||||
.message.security > .indent {
|
||||
|
@ -170,14 +170,6 @@ class JSTerm extends Component {
|
||||
// such as the browser console which doesn't have a toolbox.
|
||||
this.autocompletePopup = new AutocompletePopup(tooltipDoc, autocompleteOptions);
|
||||
|
||||
this.inputBorderSize = this.inputNode
|
||||
? this.inputNode.getBoundingClientRect().height - this.inputNode.clientHeight
|
||||
: 0;
|
||||
|
||||
// Update the character width and height needed for the popup offset
|
||||
// calculations.
|
||||
this._updateCharSize();
|
||||
|
||||
if (this.props.codeMirrorEnabled) {
|
||||
if (this.node) {
|
||||
this.editor = new Editor({
|
||||
@ -190,17 +182,51 @@ class JSTerm extends Component {
|
||||
tabIndex: "0",
|
||||
viewportMargin: Infinity,
|
||||
extraKeys: {
|
||||
"Enter": (e, cm) => {
|
||||
if (!this.autocompletePopup.isOpen && (
|
||||
e.shiftKey || !Debugger.isCompilableUnit(this.getInputValue())
|
||||
)) {
|
||||
// shift return or incomplete statement
|
||||
"Enter": () => {
|
||||
// No need to handle shift + Enter as it's natively handled by CodeMirror.
|
||||
if (
|
||||
!this.autocompletePopup.isOpen &&
|
||||
!Debugger.isCompilableUnit(this.getInputValue())
|
||||
) {
|
||||
// incomplete statement
|
||||
return "CodeMirror.Pass";
|
||||
}
|
||||
|
||||
if (this._autocompletePopupNavigated &&
|
||||
this.autocompletePopup.isOpen &&
|
||||
this.autocompletePopup.selectedIndex > -1
|
||||
) {
|
||||
return this.acceptProposedCompletion();
|
||||
}
|
||||
|
||||
this.execute();
|
||||
return null;
|
||||
},
|
||||
|
||||
"Tab": () => {
|
||||
// Generate a completion and accept the first proposed value.
|
||||
if (
|
||||
this.complete(this.COMPLETE_HINT_ONLY) &&
|
||||
this.lastCompletion &&
|
||||
this.acceptProposedCompletion()
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.hasEmptyInput()) {
|
||||
this.editor.codeMirror.getInputField().blur();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.editor.somethingSelected()) {
|
||||
this.insertStringAtCursor("\t");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Input is not empty and some text is selected, let the editor handle this.
|
||||
return true;
|
||||
},
|
||||
|
||||
"Up": () => {
|
||||
let inputUpdated;
|
||||
if (this.autocompletePopup.isOpen) {
|
||||
@ -217,6 +243,7 @@ class JSTerm extends Component {
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
"Down": () => {
|
||||
let inputUpdated;
|
||||
if (this.autocompletePopup.isOpen) {
|
||||
@ -234,6 +261,36 @@ class JSTerm extends Component {
|
||||
return null;
|
||||
},
|
||||
|
||||
"Left": () => {
|
||||
if (this.autocompletePopup.isOpen || this.lastCompletion.value) {
|
||||
this.clearCompletion();
|
||||
}
|
||||
return "CodeMirror.Pass";
|
||||
},
|
||||
|
||||
"Right": () => {
|
||||
const haveSuggestion =
|
||||
this.autocompletePopup.isOpen || this.lastCompletion.value;
|
||||
const useCompletion =
|
||||
this.canCaretGoNext() || this._autocompletePopupNavigated;
|
||||
|
||||
if (
|
||||
haveSuggestion &&
|
||||
useCompletion &&
|
||||
this.complete(this.COMPLETE_HINT_ONLY) &&
|
||||
this.lastCompletion.value &&
|
||||
this.acceptProposedCompletion()
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.autocompletePopup.isOpen) {
|
||||
this.clearCompletion();
|
||||
}
|
||||
|
||||
return "CodeMirror.Pass";
|
||||
},
|
||||
|
||||
"Ctrl-N": () => {
|
||||
// Control-N differs from down arrow: it ignores autocomplete state.
|
||||
// Note that we preserve the default 'down' navigation within
|
||||
@ -251,8 +308,8 @@ class JSTerm extends Component {
|
||||
},
|
||||
|
||||
"Ctrl-P": () => {
|
||||
// Control-N differs from down arrow: it ignores autocomplete state.
|
||||
// Note that we preserve the default 'down' navigation within
|
||||
// Control-P differs from up arrow: it ignores autocomplete state.
|
||||
// Note that we preserve the default 'up' navigation within
|
||||
// multiline text.
|
||||
if (
|
||||
Services.appinfo.OS === "Darwin"
|
||||
@ -263,11 +320,62 @@ class JSTerm extends Component {
|
||||
}
|
||||
|
||||
this.clearCompletion();
|
||||
return "CodeMirror.Pass";
|
||||
},
|
||||
|
||||
"Esc": () => {
|
||||
if (this.autocompletePopup.isOpen) {
|
||||
this.clearCompletion();
|
||||
return null;
|
||||
}
|
||||
|
||||
return "CodeMirror.Pass";
|
||||
},
|
||||
|
||||
"PageUp": () => {
|
||||
if (this.autocompletePopup.isOpen) {
|
||||
if (this.complete(this.COMPLETE_PAGEUP)) {
|
||||
this._autocompletePopupNavigated = true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return "CodeMirror.Pass";
|
||||
},
|
||||
|
||||
"PageDown": () => {
|
||||
if (this.autocompletePopup.isOpen) {
|
||||
if (this.complete(this.COMPLETE_PAGEDOWN)) {
|
||||
this._autocompletePopupNavigated = true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return "CodeMirror.Pass";
|
||||
},
|
||||
|
||||
"Home": () => {
|
||||
if (this.autocompletePopup.isOpen) {
|
||||
this.autocompletePopup.selectedIndex = 0;
|
||||
return null;
|
||||
}
|
||||
|
||||
return "CodeMirror.Pass";
|
||||
},
|
||||
|
||||
"End": () => {
|
||||
if (this.autocompletePopup.isOpen) {
|
||||
this.autocompletePopup.selectedIndex =
|
||||
this.autocompletePopup.itemCount - 1;
|
||||
return null;
|
||||
}
|
||||
|
||||
return "CodeMirror.Pass";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.editor.on("change", this._inputEventHandler);
|
||||
this.editor.appendToLocalElement(this.node);
|
||||
const cm = this.editor.codeMirror;
|
||||
cm.on("paste", (_, event) => this.props.onPaste(event));
|
||||
@ -280,6 +388,14 @@ class JSTerm extends Component {
|
||||
this.focus();
|
||||
}
|
||||
|
||||
this.inputBorderSize = this.inputNode
|
||||
? this.inputNode.getBoundingClientRect().height - this.inputNode.clientHeight
|
||||
: 0;
|
||||
|
||||
// Update the character and chevron width needed for the popup offset calculations.
|
||||
this._inputCharWidth = this._getInputCharWidth();
|
||||
this._chevronWidth = this.editor ? null : this._getChevronWidth();
|
||||
|
||||
this.hud.window.addEventListener("blur", this._blurEventHandler);
|
||||
this.lastInputValue && this.setInputValue(this.lastInputValue);
|
||||
}
|
||||
@ -576,12 +692,13 @@ class JSTerm extends Component {
|
||||
* The new value to set.
|
||||
* @returns void
|
||||
*/
|
||||
setInputValue(newValue) {
|
||||
setInputValue(newValue = "") {
|
||||
if (this.props.codeMirrorEnabled) {
|
||||
if (this.editor) {
|
||||
this.editor.setText(newValue);
|
||||
// Set the cursor at the end of the input.
|
||||
this.editor.setCursor({line: this.editor.getDoc().lineCount(), ch: 0});
|
||||
this.editor.setAutoCompletionText();
|
||||
}
|
||||
} else {
|
||||
if (!this.inputNode) {
|
||||
@ -603,21 +720,30 @@ class JSTerm extends Component {
|
||||
*/
|
||||
getInputValue() {
|
||||
if (this.props.codeMirrorEnabled) {
|
||||
return this.editor.getText() || "";
|
||||
return this.editor ? this.editor.getText() || "" : "";
|
||||
}
|
||||
|
||||
return this.inputNode ? this.inputNode.value || "" : "";
|
||||
}
|
||||
|
||||
getSelectionStart() {
|
||||
if (this.props.codeMirrorEnabled) {
|
||||
return this.getInputValueBeforeCursor().length;
|
||||
}
|
||||
|
||||
return this.inputNode ? this.inputNode.selectionStart : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The inputNode "input" and "keyup" event handler.
|
||||
* @private
|
||||
*/
|
||||
_inputEventHandler() {
|
||||
if (this.lastInputValue != this.getInputValue()) {
|
||||
const value = this.getInputValue();
|
||||
if (this.lastInputValue !== value) {
|
||||
this.resizeInput();
|
||||
this.complete(this.COMPLETE_HINT_ONLY);
|
||||
this.lastInputValue = this.getInputValue();
|
||||
this.lastInputValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -642,7 +768,6 @@ class JSTerm extends Component {
|
||||
const inputNode = this.inputNode;
|
||||
const inputValue = this.getInputValue();
|
||||
let inputUpdated = false;
|
||||
|
||||
if (event.ctrlKey) {
|
||||
switch (event.charCode) {
|
||||
case 101:
|
||||
@ -999,10 +1124,8 @@ class JSTerm extends Component {
|
||||
* or false otherwise.
|
||||
*/
|
||||
complete(type, callback) {
|
||||
const inputNode = this.inputNode;
|
||||
const inputValue = this.getInputValue();
|
||||
const frameActor = this.getFrameActor(this.SELECTED_FRAME);
|
||||
|
||||
// If the inputNode has no value, then don't try to complete on it.
|
||||
if (!inputValue) {
|
||||
this.clearCompletion();
|
||||
@ -1011,8 +1134,12 @@ class JSTerm extends Component {
|
||||
return false;
|
||||
}
|
||||
|
||||
const {editor, inputNode} = this;
|
||||
// Only complete if the selection is empty.
|
||||
if (inputNode.selectionStart != inputNode.selectionEnd) {
|
||||
if (
|
||||
(inputNode && inputNode.selectionStart != inputNode.selectionEnd) ||
|
||||
(editor && editor.getSelection())
|
||||
) {
|
||||
this.clearCompletion();
|
||||
this.callback && callback(this);
|
||||
this.emit("autocomplete-updated");
|
||||
@ -1020,8 +1147,7 @@ class JSTerm extends Component {
|
||||
}
|
||||
|
||||
// Update the completion results.
|
||||
if (this.lastCompletion.value != inputValue ||
|
||||
frameActor != this._lastFrameActorId) {
|
||||
if (this.lastCompletion.value != inputValue || frameActor != this._lastFrameActorId) {
|
||||
this._updateCompletionResult(type, callback);
|
||||
return false;
|
||||
}
|
||||
@ -1058,20 +1184,20 @@ class JSTerm extends Component {
|
||||
* Optional, function to invoke when completion results are received.
|
||||
*/
|
||||
_updateCompletionResult(type, callback) {
|
||||
const value = this.getInputValue();
|
||||
const frameActor = this.getFrameActor(this.SELECTED_FRAME);
|
||||
if (this.lastCompletion.value == this.getInputValue() &&
|
||||
frameActor == this._lastFrameActorId) {
|
||||
if (this.lastCompletion.value == value && frameActor == this._lastFrameActorId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const requestId = gSequenceId();
|
||||
const cursor = this.inputNode.selectionStart;
|
||||
const input = this.getInputValue().substring(0, cursor);
|
||||
const cursor = this.getSelectionStart();
|
||||
const input = value.substring(0, cursor);
|
||||
const cache = this._autocompleteCache;
|
||||
|
||||
// If the current input starts with the previous input, then we already
|
||||
// have a list of suggestions and we just need to filter the cached
|
||||
// suggestions. When the current input ends with a non-alphanumeri;
|
||||
// suggestions. When the current input ends with a non-alphanumeric
|
||||
// character we ask the server again for suggestions.
|
||||
|
||||
// Check if last character is non-alphanumeric
|
||||
@ -1079,7 +1205,6 @@ class JSTerm extends Component {
|
||||
this._autocompleteQuery = null;
|
||||
this._autocompleteCache = null;
|
||||
}
|
||||
|
||||
if (this._autocompleteQuery && input.startsWith(this._autocompleteQuery)) {
|
||||
let filterBy = input;
|
||||
// Find the last non-alphanumeric other than _ or $ if it exists.
|
||||
@ -1090,9 +1215,7 @@ class JSTerm extends Component {
|
||||
filterBy = input.substring(input.lastIndexOf(lastNonAlpha) + 1);
|
||||
}
|
||||
|
||||
const newList = cache.sort().filter(function(l) {
|
||||
return l.startsWith(filterBy);
|
||||
});
|
||||
const newList = cache.sort().filter(l => l.startsWith(filterBy));
|
||||
|
||||
this.lastCompletion = {
|
||||
requestId: null,
|
||||
@ -1104,7 +1227,6 @@ class JSTerm extends Component {
|
||||
this._receiveAutocompleteProperties(null, callback, response);
|
||||
return;
|
||||
}
|
||||
|
||||
this._lastFrameActorId = frameActor;
|
||||
|
||||
this.lastCompletion = {
|
||||
@ -1120,6 +1242,19 @@ class JSTerm extends Component {
|
||||
input, cursor, autocompleteCallback, frameActor);
|
||||
}
|
||||
|
||||
getInputValueBeforeCursor() {
|
||||
if (this.editor) {
|
||||
return this.editor.getDoc().getRange({line: 0, ch: 0}, this.editor.getCursor());
|
||||
}
|
||||
|
||||
if (this.inputNode) {
|
||||
const cursor = this.inputNode.selectionStart;
|
||||
return this.getInputValue().substring(0, cursor);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the autocompletion results. This method takes
|
||||
* the completion result received from the server and updates the UI
|
||||
@ -1134,7 +1269,6 @@ class JSTerm extends Component {
|
||||
* the content process.
|
||||
*/
|
||||
_receiveAutocompleteProperties(requestId, callback, message) {
|
||||
const inputNode = this.inputNode;
|
||||
const inputValue = this.getInputValue();
|
||||
if (this.lastCompletion.value == inputValue ||
|
||||
requestId != this.lastCompletion.requestId) {
|
||||
@ -1142,8 +1276,7 @@ class JSTerm extends Component {
|
||||
}
|
||||
// Cache whatever came from the server if the last char is
|
||||
// alphanumeric or '.'
|
||||
const cursor = inputNode.selectionStart;
|
||||
const inputUntilCursor = inputValue.substring(0, cursor);
|
||||
const inputUntilCursor = this.getInputValueBeforeCursor();
|
||||
|
||||
if (requestId != null && /[a-zA-Z0-9.]$/.test(inputUntilCursor)) {
|
||||
this._autocompleteCache = message.matches;
|
||||
@ -1159,10 +1292,7 @@ class JSTerm extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
const items = matches.reverse().map(function(match) {
|
||||
return { preLabel: lastPart, label: match };
|
||||
});
|
||||
|
||||
const items = matches.reverse().map(match => ({ preLabel: lastPart, label: match }));
|
||||
const popup = this.autocompletePopup;
|
||||
popup.setItems(items);
|
||||
|
||||
@ -1172,11 +1302,27 @@ class JSTerm extends Component {
|
||||
matchProp: lastPart,
|
||||
};
|
||||
if (items.length > 1 && !popup.isOpen) {
|
||||
const str = this.getInputValue().substr(0, this.inputNode.selectionStart);
|
||||
const offset = str.length - (str.lastIndexOf("\n") + 1) - lastPart.length;
|
||||
const x = offset * this._inputCharWidth;
|
||||
popup.openPopup(inputNode, x + this._chevronWidth);
|
||||
this._autocompletePopupNavigated = false;
|
||||
let popupAlignElement;
|
||||
let xOffset;
|
||||
let yOffset;
|
||||
|
||||
if (this.editor) {
|
||||
popupAlignElement = this.node.querySelector(".CodeMirror-cursor");
|
||||
// We need to show the popup at the ".".
|
||||
xOffset = -1 * lastPart.length * this._inputCharWidth;
|
||||
yOffset = 4;
|
||||
} else if (this.inputNode) {
|
||||
const offset = inputUntilCursor.length -
|
||||
(inputUntilCursor.lastIndexOf("\n") + 1) -
|
||||
lastPart.length;
|
||||
xOffset = (offset * this._inputCharWidth) + this._chevronWidth;
|
||||
popupAlignElement = this.inputNode;
|
||||
}
|
||||
|
||||
if (popupAlignElement) {
|
||||
popup.openPopup(popupAlignElement, xOffset, yOffset);
|
||||
this._autocompletePopupNavigated = false;
|
||||
}
|
||||
} else if (items.length < 2 && popup.isOpen) {
|
||||
popup.hidePopup();
|
||||
this._autocompletePopupNavigated = false;
|
||||
@ -1201,7 +1347,7 @@ class JSTerm extends Component {
|
||||
|
||||
onAutocompleteSelect() {
|
||||
// Render the suggestion only if the cursor is at the end of the input.
|
||||
if (this.inputNode.selectionStart != this.getInputValue().length) {
|
||||
if (this.getSelectionStart() != this.getInputValue().length) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1228,9 +1374,11 @@ class JSTerm extends Component {
|
||||
if (this.autocompletePopup.isOpen) {
|
||||
// Trigger a blur/focus of the JSTerm input to force screen readers to read the
|
||||
// value again.
|
||||
this.inputNode.blur();
|
||||
if (this.inputNode) {
|
||||
this.inputNode.blur();
|
||||
}
|
||||
this.autocompletePopup.once("popup-closed", () => {
|
||||
this.inputNode.focus();
|
||||
this.focus();
|
||||
});
|
||||
this.autocompletePopup.hidePopup();
|
||||
this._autocompletePopupNavigated = false;
|
||||
@ -1253,6 +1401,7 @@ class JSTerm extends Component {
|
||||
this.insertStringAtCursor(
|
||||
currentItem.label.substring(this.lastCompletion.matchProp.length)
|
||||
);
|
||||
|
||||
updated = true;
|
||||
}
|
||||
|
||||
@ -1268,12 +1417,25 @@ class JSTerm extends Component {
|
||||
* @param string str
|
||||
*/
|
||||
insertStringAtCursor(str) {
|
||||
const cursor = this.inputNode.selectionStart;
|
||||
const value = this.getInputValue();
|
||||
this.setInputValue(value.substr(0, cursor) +
|
||||
str + value.substr(cursor));
|
||||
const newCursor = cursor + str.length;
|
||||
this.inputNode.selectionStart = this.inputNode.selectionEnd = newCursor;
|
||||
const prefix = this.getInputValueBeforeCursor();
|
||||
const suffix = value.replace(prefix, "");
|
||||
|
||||
// We need to retrieve the cursor before setting the new value.
|
||||
const editorCursor = this.editor && this.editor.getCursor();
|
||||
|
||||
this.setInputValue(prefix + str + suffix);
|
||||
|
||||
if (this.inputNode) {
|
||||
const newCursor = prefix.length + str.length;
|
||||
this.inputNode.selectionStart = this.inputNode.selectionEnd = newCursor;
|
||||
} else if (this.editor) {
|
||||
// Set the cursor on the same line it was already at, after the autocompleted text
|
||||
this.editor.setCursor({
|
||||
line: editorCursor.line,
|
||||
ch: editorCursor.ch + str.length
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1283,23 +1445,30 @@ class JSTerm extends Component {
|
||||
* The proposed suffix for the inputNode value.
|
||||
*/
|
||||
updateCompleteNode(suffix) {
|
||||
if (!this.completeNode) {
|
||||
return;
|
||||
if (this.completeNode) {
|
||||
// completion prefix = input, with non-control chars replaced by spaces
|
||||
const prefix = suffix ? this.getInputValue().replace(/[\S]/g, " ") : "";
|
||||
this.completeNode.value = prefix + suffix;
|
||||
}
|
||||
|
||||
// completion prefix = input, with non-control chars replaced by spaces
|
||||
const prefix = suffix ? this.getInputValue().replace(/[\S]/g, " ") : "";
|
||||
this.completeNode.value = prefix + suffix;
|
||||
if (this.editor) {
|
||||
this.editor.setAutoCompletionText(suffix);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the width and height of a single character of the input box.
|
||||
* Calculates and returns the width of a single character of the input box.
|
||||
* This will be used in opening the popup at the correct offset.
|
||||
*
|
||||
* @private
|
||||
* @returns {Number|null}: Width off the "x" char, or null if the input does not exist.
|
||||
*/
|
||||
_updateCharSize() {
|
||||
if (this.props.codeMirrorEnabled || !this.inputNode) {
|
||||
return;
|
||||
_getInputCharWidth() {
|
||||
if (!this.inputNode && !this.node) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.editor) {
|
||||
return this.editor.defaultCharWidth();
|
||||
}
|
||||
|
||||
const doc = this.hud.document;
|
||||
@ -1313,12 +1482,28 @@ class JSTerm extends Component {
|
||||
WebConsoleUtils.copyTextStyles(this.inputNode, tempLabel);
|
||||
tempLabel.textContent = "x";
|
||||
doc.documentElement.appendChild(tempLabel);
|
||||
this._inputCharWidth = tempLabel.offsetWidth;
|
||||
const width = tempLabel.offsetWidth;
|
||||
tempLabel.remove();
|
||||
// Calculate the width of the chevron placed at the beginning of the input
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates and returns the width of the chevron icon.
|
||||
* This will be used in opening the popup at the correct offset.
|
||||
*
|
||||
* @returns {Number|null}: Width of the icon, or null if the input does not exist.
|
||||
*/
|
||||
_getChevronWidth() {
|
||||
if (!this.inputNode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Calculate the width of the chevron placed at the beginning of the input
|
||||
// box. Remove 4 more pixels to accommodate the padding of the popup.
|
||||
this._chevronWidth = +doc.defaultView.getComputedStyle(this.inputNode)
|
||||
.paddingLeft.replace(/[^0-9.]/g, "") - 4;
|
||||
const doc = this.hud.document;
|
||||
return doc.defaultView
|
||||
.getComputedStyle(this.inputNode)
|
||||
.paddingLeft.replace(/[^0-9.]/g, "") - 4;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
|
@ -184,6 +184,7 @@ skip-if = verify
|
||||
[browser_jsterm_accessibility.js]
|
||||
[browser_jsterm_add_edited_input_to_history.js]
|
||||
[browser_jsterm_autocomplete_array_no_index.js]
|
||||
[browser_jsterm_autocomplete_arrow_keys.js]
|
||||
[browser_jsterm_autocomplete_cached_results.js]
|
||||
[browser_jsterm_autocomplete_crossdomain_iframe.js]
|
||||
[browser_jsterm_autocomplete_escape_key.js]
|
||||
|
@ -12,17 +12,18 @@
|
||||
const TEST_URI = "data:text/html;charset=utf-8,Web Console test for bug 817834";
|
||||
|
||||
add_task(async function() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
await testEditedInputHistory(hud);
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function testEditedInputHistory(hud) {
|
||||
const jsterm = hud.jsterm;
|
||||
const inputNode = jsterm.inputNode;
|
||||
async function performTests() {
|
||||
const {jsterm} = await openNewTabAndConsole(TEST_URI);
|
||||
|
||||
ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty");
|
||||
is(inputNode.selectionStart, 0);
|
||||
is(inputNode.selectionEnd, 0);
|
||||
checkJsTermCursor(jsterm, 0, "Cursor is at expected position");
|
||||
|
||||
jsterm.setInputValue('"first item"');
|
||||
EventUtils.synthesizeKey("KEY_ArrowUp");
|
||||
|
@ -10,6 +10,14 @@
|
||||
const TEST_URI = "data:text/html;charset=utf8,test autocompletion with $ or _";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const { jsterm } = await openNewTabAndConsole(TEST_URI);
|
||||
|
||||
await jsterm.execute("var testObject = {$$aaab: '', $$aaac: ''}");
|
||||
@ -28,7 +36,7 @@ add_task(async function() {
|
||||
await testAutocomplete(jsterm, "blargh");
|
||||
await testAutocomplete(jsterm, "foobar.a");
|
||||
await testAutocomplete(jsterm, "blargh.a");
|
||||
});
|
||||
}
|
||||
|
||||
async function testAutocomplete(jsterm, inputString) {
|
||||
jsterm.setInputValue(inputString);
|
||||
|
@ -16,6 +16,14 @@ const TEST_URI = `data:text/html;charset=utf-8,
|
||||
<body>bug 585991 - Autocomplete popup on array</body>`;
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const { jsterm } = await openNewTabAndConsole(TEST_URI);
|
||||
|
||||
const {
|
||||
@ -38,4 +46,4 @@ add_task(async function() {
|
||||
EventUtils.synthesizeKey("KEY_Escape");
|
||||
|
||||
await onPopupClose;
|
||||
});
|
||||
}
|
||||
|
@ -0,0 +1,69 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_URI = `data:text/html;charset=utf-8,<head><script>
|
||||
/* Create a prototype-less object so popup does not contain native
|
||||
* Object prototype properties.
|
||||
*/
|
||||
window.foo = Object.create(null, Object.getOwnPropertyDescriptors({
|
||||
aa: "a",
|
||||
bb: "b",
|
||||
}));
|
||||
</script></head><body>Autocomplete text navigation key usage test</body>`;
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const { jsterm } = await openNewTabAndConsole(TEST_URI);
|
||||
const { autocompletePopup: popup } = jsterm;
|
||||
|
||||
const checkInput = (expected, assertionInfo) =>
|
||||
checkJsTermValueAndCursor(jsterm, expected, assertionInfo);
|
||||
|
||||
let onPopUpOpen = popup.once("popup-opened");
|
||||
jsterm.setInputValue("window.foo");
|
||||
EventUtils.sendString(".");
|
||||
await onPopUpOpen;
|
||||
|
||||
info("Trigger autocomplete popup opening");
|
||||
// checkInput is asserting the cursor position with the "|" char.
|
||||
checkInput("window.foo.|");
|
||||
is(popup.isOpen, true, "popup is open");
|
||||
checkJsTermCompletionValue(jsterm, " aa", "completeNode has expected value");
|
||||
|
||||
info("Test that arrow left closes the popup and clears complete node");
|
||||
let onPopUpClose = popup.once("popup-closed");
|
||||
EventUtils.synthesizeKey("KEY_ArrowLeft");
|
||||
await onPopUpClose;
|
||||
checkInput("window.foo|.");
|
||||
is(popup.isOpen, false, "popup is closed");
|
||||
checkJsTermCompletionValue(jsterm, "", "completeNode is empty");
|
||||
|
||||
info("Trigger autocomplete popup opening again");
|
||||
onPopUpOpen = popup.once("popup-opened");
|
||||
jsterm.setInputValue("window.foo");
|
||||
EventUtils.sendString(".");
|
||||
await onPopUpOpen;
|
||||
|
||||
checkInput("window.foo.|");
|
||||
is(popup.isOpen, true, "popup is open");
|
||||
checkJsTermCompletionValue(jsterm, " aa", "completeNode has expected value");
|
||||
|
||||
info("Test that arrow right selects selected autocomplete item");
|
||||
onPopUpClose = popup.once("popup-closed");
|
||||
EventUtils.synthesizeKey("KEY_ArrowRight");
|
||||
await onPopUpClose;
|
||||
checkInput("window.foo.aa|");
|
||||
is(popup.isOpen, false, "popup is closed");
|
||||
checkJsTermCompletionValue(jsterm, "", "completeNode is empty");
|
||||
}
|
@ -11,38 +11,58 @@
|
||||
const TEST_URI = "data:text/html;charset=utf8,<p>test cached autocompletion results";
|
||||
|
||||
add_task(async function() {
|
||||
const { jsterm } = await openNewTabAndConsole(TEST_URI);
|
||||
const {
|
||||
autocompletePopup: popup,
|
||||
completeNode,
|
||||
inputNode: input,
|
||||
} = jsterm;
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
const jstermComplete = (value, offset) =>
|
||||
jstermSetValueAndComplete(jsterm, value, offset);
|
||||
async function performTests() {
|
||||
const { jsterm } = await openNewTabAndConsole(TEST_URI);
|
||||
const { autocompletePopup: popup } = jsterm;
|
||||
|
||||
const jstermComplete = (value, caretPosition) =>
|
||||
jstermSetValueAndComplete(jsterm, value, caretPosition);
|
||||
|
||||
// Test if 'doc' gives 'document'
|
||||
await jstermComplete("doc");
|
||||
is(input.value, "doc", "'docu' completion (input.value)");
|
||||
is(completeNode.value, " ument", "'docu' completion (completeNode)");
|
||||
is(jsterm.getInputValue(), "doc", "'docu' completion (input.value)");
|
||||
checkJsTermCompletionValue(jsterm, " ument", "'docu' completion (completeNode)");
|
||||
|
||||
// Test typing 'window.'.
|
||||
// Test typing 'window.'.'
|
||||
await jstermComplete("window.");
|
||||
ok(popup.getItems().length > 0, "'window.' gave a list of suggestions");
|
||||
|
||||
await jsterm.execute("window.docfoobar = true");
|
||||
info("Add a property on the window object");
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, {}, () => {
|
||||
content.wrappedJSObject.window.docfoobar = true;
|
||||
});
|
||||
|
||||
// Test typing 'window.doc'.
|
||||
await jstermComplete("window.doc");
|
||||
// Test typing d (i.e. input is now 'window.d').
|
||||
let onUpdated = jsterm.once("autocomplete-updated");
|
||||
EventUtils.synthesizeKey("d");
|
||||
await onUpdated;
|
||||
ok(!getPopupLabels(popup).includes("docfoobar"),
|
||||
"autocomplete cached results do not contain docfoobar. list has not been updated");
|
||||
"autocomplete popup does not contain docfoobar. List has not been updated");
|
||||
|
||||
// Test typing o (i.e. input is now 'window.do').
|
||||
jsterm.once("autocomplete-updated");
|
||||
EventUtils.synthesizeKey("o");
|
||||
await onUpdated;
|
||||
ok(!getPopupLabels(popup).includes("docfoobar"),
|
||||
"autocomplete popup does not contain docfoobar. List has not been updated");
|
||||
|
||||
// Test that backspace does not cause a request to the server
|
||||
await jstermComplete("window.do");
|
||||
onUpdated = jsterm.once("autocomplete-updated");
|
||||
EventUtils.synthesizeKey("KEY_Backspace");
|
||||
await onUpdated;
|
||||
ok(!getPopupLabels(popup).includes("docfoobar"),
|
||||
"autocomplete cached results do not contain docfoobar. list has not been updated");
|
||||
|
||||
await jsterm.execute("delete window.docfoobar");
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, {}, () => {
|
||||
delete content.wrappedJSObject.window.docfoobar;
|
||||
});
|
||||
|
||||
// Test if 'window.getC' gives 'getComputedStyle'
|
||||
await jstermComplete("window.");
|
||||
@ -58,13 +78,18 @@ add_task(async function() {
|
||||
await jstermComplete("dump(window.)", -1);
|
||||
ok(popup.getItems().length > 0, "'dump(window.' gave a list of suggestions");
|
||||
|
||||
await jsterm.execute("window.docfoobar = true");
|
||||
info("Add a property on the window object");
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, {}, () => {
|
||||
content.wrappedJSObject.window.docfoobar = true;
|
||||
});
|
||||
|
||||
// Make sure 'dump(window.doc)' does not contain 'docfoobar'.
|
||||
await jstermComplete("dump(window.doc)", -1);
|
||||
// Make sure 'dump(window.d)' does not contain 'docfoobar'.
|
||||
onUpdated = jsterm.once("autocomplete-updated");
|
||||
EventUtils.synthesizeKey("d");
|
||||
await onUpdated;
|
||||
ok(!getPopupLabels(popup).includes("docfoobar"),
|
||||
"autocomplete cached results do not contain docfoobar. list has not been updated");
|
||||
});
|
||||
}
|
||||
|
||||
function getPopupLabels(popup) {
|
||||
return popup.getItems().map(item => item.label);
|
||||
|
@ -8,7 +8,16 @@
|
||||
|
||||
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
|
||||
"test/mochitest/test-iframe-parent.html";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
const { jsterm } = hud;
|
||||
|
||||
@ -34,4 +43,4 @@ add_task(async function() {
|
||||
hud.jsterm.execute("window.location");
|
||||
await onParentLocation;
|
||||
ok(true, "root document's location is accessible");
|
||||
});
|
||||
}
|
||||
|
@ -23,13 +23,18 @@ const TEST_URI = `data:text/html;charset=utf-8,
|
||||
<body>bug 585991 - autocomplete popup escape key usage test</body>`;
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const { jsterm } = await openNewTabAndConsole(TEST_URI);
|
||||
info("web console opened");
|
||||
|
||||
const {
|
||||
autocompletePopup: popup,
|
||||
completeNode,
|
||||
} = jsterm;
|
||||
const { autocompletePopup: popup } = jsterm;
|
||||
|
||||
const onPopUpOpen = popup.once("popup-opened");
|
||||
|
||||
@ -50,5 +55,5 @@ add_task(async function() {
|
||||
|
||||
ok(!popup.isOpen, "popup is not open after VK_ESCAPE");
|
||||
is(jsterm.getInputValue(), "window.foo.", "completion was cancelled");
|
||||
ok(!completeNode.value, "completeNode is empty");
|
||||
});
|
||||
ok(!getJsTermCompletionValue(jsterm), "completeNode is empty");
|
||||
}
|
||||
|
@ -11,6 +11,14 @@
|
||||
const TEST_URI = "data:text/html;charset=utf-8,test for bug 592442";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const { jsterm } = await openNewTabAndConsole(TEST_URI);
|
||||
|
||||
try {
|
||||
@ -19,4 +27,4 @@ add_task(async function() {
|
||||
} catch (ex) {
|
||||
ok(false, "an error was thrown when an extraneous bracket was inserted");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -11,12 +11,20 @@
|
||||
const TEST_URI = "data:text/html;charset=utf8,<p>test JSTerm Helpers autocomplete";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const {jsterm} = await openNewTabAndConsole(TEST_URI);
|
||||
await testInspectAutoCompletion(jsterm, "i", true);
|
||||
await testInspectAutoCompletion(jsterm, "window.", false);
|
||||
await testInspectAutoCompletion(jsterm, "dump(i", true);
|
||||
await testInspectAutoCompletion(jsterm, "window.dump(i", true);
|
||||
});
|
||||
}
|
||||
|
||||
async function testInspectAutoCompletion(jsterm, inputValue, expectInspect) {
|
||||
jsterm.setInputValue(inputValue);
|
||||
|
@ -8,6 +8,14 @@
|
||||
"use strict";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const hud = await openNewTabAndConsole("about:config");
|
||||
ok(hud, "we have a console");
|
||||
ok(hud.iframeWindow, "we have the console UI window");
|
||||
@ -18,5 +26,5 @@ add_task(async function() {
|
||||
|
||||
// Test typing 'docu'.
|
||||
await jstermSetValueAndComplete(jsterm, "docu");
|
||||
is(jsterm.completeNode.value, " ment", "'docu' completion");
|
||||
});
|
||||
checkJsTermCompletionValue(jsterm, " ment", "'docu' completion");
|
||||
}
|
||||
|
@ -12,6 +12,14 @@ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
|
||||
"test/mochitest/test-autocomplete-in-stackframe.html";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
// Force the old debugger UI since it's directly used (see Bug 1301705)
|
||||
await pushPref("devtools.debugger.new-debugger-frontend", false);
|
||||
|
||||
@ -93,7 +101,7 @@ add_task(async function() {
|
||||
// Test if 'foo2Obj[0].' throws no errors.
|
||||
await jstermComplete("foo2Obj[0].");
|
||||
is(getPopupLabels(popup).length, 0, "no items for foo2Obj[0]");
|
||||
});
|
||||
}
|
||||
|
||||
function getPopupLabels(popup) {
|
||||
return popup.getItems().map(item => item.label);
|
||||
|
@ -17,21 +17,23 @@ const TEST_URI = `data:text/html;charset=utf-8,
|
||||
<body>bug 812618 - test completion inside text</body>`;
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const { jsterm } = await openNewTabAndConsole(TEST_URI);
|
||||
info("web console opened");
|
||||
|
||||
const {
|
||||
autocompletePopup: popup,
|
||||
completeNode,
|
||||
inputNode,
|
||||
} = jsterm;
|
||||
const { autocompletePopup: popup } = jsterm;
|
||||
|
||||
const onPopUpOpen = popup.once("popup-opened");
|
||||
|
||||
const dumpString = "dump(window.testBu)";
|
||||
jsterm.setInputValue(dumpString);
|
||||
inputNode.selectionStart = inputNode.selectionEnd = dumpString.indexOf(")");
|
||||
EventUtils.sendString("g");
|
||||
jstermSetValueAndComplete(jsterm, dumpString, -1);
|
||||
|
||||
await onPopUpOpen;
|
||||
|
||||
@ -40,7 +42,7 @@ add_task(async function() {
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
is(popup.selectedIndex, 0, "popup.selectedIndex is correct");
|
||||
ok(!completeNode.value, "completeNode.value is empty");
|
||||
ok(!getJsTermCompletionValue(jsterm), "completeNode.value is empty");
|
||||
|
||||
const items = popup.getItems().map(e => e.label);
|
||||
const expectedItems = ["testBugB", "testBugA"];
|
||||
@ -56,7 +58,6 @@ add_task(async function() {
|
||||
ok(!popup.isOpen, "popup is not open");
|
||||
const expectedInput = "dump(window.testBugB)";
|
||||
is(jsterm.getInputValue(), expectedInput, "completion was successful after VK_TAB");
|
||||
is(inputNode.selectionStart, expectedInput.length - 1, "cursor location is correct");
|
||||
is(inputNode.selectionStart, inputNode.selectionEnd, "cursor location (confirmed)");
|
||||
ok(!completeNode.value, "completeNode is empty");
|
||||
});
|
||||
checkJsTermCursor(jsterm, expectedInput.length - 1, "cursor location is correct");
|
||||
ok(!getJsTermCompletionValue(jsterm), "completeNode is empty");
|
||||
}
|
||||
|
@ -11,12 +11,17 @@
|
||||
const TEST_URI = "data:text/html;charset=utf-8,Test document.body autocompletion";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const { jsterm } = await openNewTabAndConsole(TEST_URI);
|
||||
|
||||
const {
|
||||
autocompletePopup: popup,
|
||||
completeNode,
|
||||
} = jsterm;
|
||||
const { autocompletePopup: popup } = jsterm;
|
||||
|
||||
ok(!popup.isOpen, "popup is not open");
|
||||
const onPopupOpen = popup.once("popup-opened");
|
||||
@ -29,7 +34,7 @@ add_task(async function() {
|
||||
ok(popup.isOpen, "popup is open");
|
||||
is(popup.itemCount, jsterm._autocompleteCache.length, "popup.itemCount is correct");
|
||||
ok(jsterm._autocompleteCache.includes("addEventListener"),
|
||||
"addEventListener is in the list of suggestions");
|
||||
"addEventListener is in the list of suggestions");
|
||||
ok(jsterm._autocompleteCache.includes("bgColor"),
|
||||
"bgColor is in the list of suggestions");
|
||||
ok(jsterm._autocompleteCache.includes("ATTRIBUTE_NODE"),
|
||||
@ -53,5 +58,5 @@ add_task(async function() {
|
||||
// > document.bo <-- input
|
||||
// > -----------dy <-- autocomplete
|
||||
const spaces = " ".repeat(inputStr.length + 1);
|
||||
is(completeNode.value, spaces + "dy", "autocomplete shows document.body");
|
||||
});
|
||||
checkJsTermCompletionValue(jsterm, spaces + "dy", "autocomplete shows document.body");
|
||||
}
|
||||
|
@ -25,13 +25,18 @@ const TEST_URI = `data:text/html;charset=utf-8,
|
||||
<body>bug 585991 - autocomplete popup navigation and tab key usage test</body>`;
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const { jsterm } = await openNewTabAndConsole(TEST_URI);
|
||||
info("web console opened");
|
||||
|
||||
const {
|
||||
autocompletePopup: popup,
|
||||
completeNode,
|
||||
} = jsterm;
|
||||
const { autocompletePopup: popup } = jsterm;
|
||||
|
||||
ok(!popup.isOpen, "popup is not open");
|
||||
|
||||
@ -63,19 +68,19 @@ add_task(async function() {
|
||||
const prefix = jsterm.getInputValue().replace(/[\S]/g, " ");
|
||||
is(popup.selectedIndex, 0, "index 0 is selected");
|
||||
is(popup.selectedItem.label, "item3", "item3 is selected");
|
||||
is(completeNode.value, prefix + "item3", "completeNode.value holds item3");
|
||||
checkJsTermCompletionValue(jsterm, prefix + "item3", "completeNode.value holds item3");
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
|
||||
is(popup.selectedIndex, 1, "index 1 is selected");
|
||||
is(popup.selectedItem.label, "item2", "item2 is selected");
|
||||
is(completeNode.value, prefix + "item2", "completeNode.value holds item2");
|
||||
checkJsTermCompletionValue(jsterm, prefix + "item2", "completeNode.value holds item2");
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowUp");
|
||||
|
||||
is(popup.selectedIndex, 0, "index 0 is selected");
|
||||
is(popup.selectedItem.label, "item3", "item3 is selected");
|
||||
is(completeNode.value, prefix + "item3", "completeNode.value holds item3");
|
||||
checkJsTermCompletionValue(jsterm, prefix + "item3", "completeNode.value holds item3");
|
||||
|
||||
let currentSelectionIndex = popup.selectedIndex;
|
||||
|
||||
@ -104,5 +109,5 @@ add_task(async function() {
|
||||
ok(!popup.isOpen, "popup is not open");
|
||||
is(jsterm.getInputValue(), "window.foo.item3",
|
||||
"completion was successful after KEY_Tab");
|
||||
ok(!completeNode.value, "completeNode is empty");
|
||||
});
|
||||
ok(!getJsTermCompletionValue(jsterm), "completeNode is empty");
|
||||
}
|
||||
|
@ -16,9 +16,17 @@ XPCOMUtils.defineLazyServiceGetter(
|
||||
const stringToCopy = "foobazbarBug642615";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const {jsterm, ui} = await openNewTabAndConsole(TEST_URI);
|
||||
ui.clearOutput();
|
||||
ok(!jsterm.completeNode.value, "no completeNode.value");
|
||||
ok(!getJsTermCompletionValue(jsterm), "no completeNode.value");
|
||||
|
||||
jsterm.setInputValue("doc");
|
||||
|
||||
@ -27,7 +35,7 @@ add_task(async function() {
|
||||
EventUtils.sendString("u");
|
||||
await onAutocompleteUpdated;
|
||||
|
||||
const completionValue = jsterm.completeNode.value;
|
||||
const completionValue = getJsTermCompletionValue(jsterm);
|
||||
|
||||
info(`Copy "${stringToCopy}" in clipboard`);
|
||||
await waitForClipboardPromise(() =>
|
||||
@ -36,20 +44,21 @@ add_task(async function() {
|
||||
jsterm.setInputValue("docu");
|
||||
info("wait for completion update after clipboard paste");
|
||||
onAutocompleteUpdated = jsterm.once("autocomplete-updated");
|
||||
goDoCommand("cmd_paste");
|
||||
EventUtils.synthesizeKey("v", {accelKey: true});
|
||||
|
||||
await onAutocompleteUpdated;
|
||||
|
||||
ok(!jsterm.completeNode.value, "no completion value after paste");
|
||||
ok(!getJsTermCompletionValue(jsterm), "no completion value after paste");
|
||||
|
||||
info("wait for completion update after undo");
|
||||
onAutocompleteUpdated = jsterm.once("autocomplete-updated");
|
||||
|
||||
goDoCommand("cmd_undo");
|
||||
EventUtils.synthesizeKey("z", {accelKey: true});
|
||||
|
||||
await onAutocompleteUpdated;
|
||||
|
||||
is(jsterm.completeNode.value, completionValue, "same completeNode.value after undo");
|
||||
checkJsTermCompletionValue(jsterm, completionValue,
|
||||
"same completeNode.value after undo");
|
||||
|
||||
info("wait for completion update after clipboard paste (ctrl-v)");
|
||||
onAutocompleteUpdated = jsterm.once("autocomplete-updated");
|
||||
@ -57,5 +66,5 @@ add_task(async function() {
|
||||
EventUtils.synthesizeKey("v", {accelKey: true});
|
||||
|
||||
await onAutocompleteUpdated;
|
||||
ok(!jsterm.completeNode.value, "no completion value after paste (ctrl-v)");
|
||||
});
|
||||
ok(!getJsTermCompletionValue(jsterm), "no completion value after paste (ctrl-v)");
|
||||
}
|
||||
|
@ -25,11 +25,16 @@ const TEST_URI = `data:text/html;charset=utf-8,
|
||||
<body>bug 585991 - test pressing return with open popup</body>`;
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const { jsterm } = await openNewTabAndConsole(TEST_URI);
|
||||
const {
|
||||
autocompletePopup: popup,
|
||||
completeNode,
|
||||
} = jsterm;
|
||||
const { autocompletePopup: popup } = jsterm;
|
||||
|
||||
const onPopUpOpen = popup.once("popup-opened");
|
||||
|
||||
@ -57,7 +62,7 @@ add_task(async function() {
|
||||
is(popup.selectedIndex, 0, "index 0 is selected");
|
||||
is(popup.selectedItem.label, "item3", "item3 is selected");
|
||||
const prefix = jsterm.getInputValue().replace(/[\S]/g, " ");
|
||||
is(completeNode.value, prefix + "item3", "completeNode.value holds item3");
|
||||
checkJsTermCompletionValue(jsterm, prefix + "item3", "completeNode.value holds item3");
|
||||
|
||||
info("press Return to accept suggestion. wait for popup to hide");
|
||||
const onPopupClose = popup.once("popup-closed");
|
||||
@ -68,5 +73,5 @@ add_task(async function() {
|
||||
ok(!popup.isOpen, "popup is not open after KEY_Enter");
|
||||
is(jsterm.getInputValue(), "window.foobar.item3",
|
||||
"completion was successful after KEY_Enter");
|
||||
ok(!completeNode.value, "completeNode is empty");
|
||||
});
|
||||
ok(!getJsTermCompletionValue(jsterm), "completeNode is empty");
|
||||
}
|
||||
|
@ -21,15 +21,20 @@ const {
|
||||
} = require("devtools/client/webconsole/selectors/history");
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const {
|
||||
jsterm,
|
||||
ui,
|
||||
} = await openNewTabAndConsole(TEST_URI);
|
||||
|
||||
const {
|
||||
autocompletePopup: popup,
|
||||
completeNode,
|
||||
} = jsterm;
|
||||
const { autocompletePopup: popup } = jsterm;
|
||||
|
||||
const onPopUpOpen = popup.once("popup-opened");
|
||||
|
||||
@ -50,10 +55,10 @@ add_task(async function() {
|
||||
|
||||
ok(!popup.isOpen, "popup is not open after KEY_Enter");
|
||||
is(jsterm.getInputValue(), "", "inputNode is empty after KEY_Enter");
|
||||
is(completeNode.value, "", "completeNode is empty");
|
||||
ok(!getJsTermCompletionValue(jsterm), "completeNode is empty");
|
||||
|
||||
const state = ui.consoleOutput.getStore().getState();
|
||||
const entries = getHistoryEntries(state);
|
||||
is(entries[entries.length - 1], "window.testBug",
|
||||
"jsterm history is correct");
|
||||
});
|
||||
}
|
||||
|
@ -10,64 +10,71 @@
|
||||
const TEST_URI = "data:text/html;charset=utf8,<p>test code completion";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const {jsterm, ui} = await openNewTabAndConsole(TEST_URI);
|
||||
const input = jsterm.inputNode;
|
||||
|
||||
// Test typing 'docu'.
|
||||
await jstermSetValueAndComplete(jsterm, "docu");
|
||||
is(input.value, "docu", "'docu' completion (input.value)");
|
||||
is(jsterm.completeNode.value, " ment", "'docu' completion (completeNode)");
|
||||
is(jsterm.getInputValue(), "docu", "'docu' completion (input.value)");
|
||||
checkJsTermCompletionValue(jsterm, " ment", "'docu' completion (completeNode)");
|
||||
|
||||
// Test typing 'docu' and press tab.
|
||||
await jstermSetValueAndComplete(jsterm, "docu", undefined, jsterm.COMPLETE_FORWARD);
|
||||
is(input.value, "document", "'docu' tab completion");
|
||||
is(input.selectionStart, 8, "start selection is alright");
|
||||
is(input.selectionEnd, 8, "end selection is alright");
|
||||
is(jsterm.completeNode.value.replace(/ /g, ""), "", "'docu' completed");
|
||||
is(jsterm.getInputValue(), "document", "'docu' tab completion");
|
||||
|
||||
checkJsTermCursor(jsterm, "document".length, "cursor is at the end of 'document'");
|
||||
is(getJsTermCompletionValue(jsterm).replace(/ /g, ""), "", "'docu' completed");
|
||||
|
||||
// Test typing 'window.Ob' and press tab. Just 'window.O' is
|
||||
// ambiguous: could be window.Object, window.Option, etc.
|
||||
await jstermSetValueAndComplete(jsterm, "window.Ob", undefined,
|
||||
jsterm.COMPLETE_FORWARD);
|
||||
is(input.value, "window.Object", "'window.Ob' tab completion");
|
||||
is(jsterm.getInputValue(), "window.Object", "'window.Ob' tab completion");
|
||||
|
||||
// Test typing 'document.getElem'.
|
||||
await jstermSetValueAndComplete(
|
||||
jsterm, "document.getElem", undefined, jsterm.COMPLETE_FORWARD);
|
||||
is(input.value, "document.getElem", "'document.getElem' completion");
|
||||
is(jsterm.completeNode.value, " entsByTagNameNS",
|
||||
is(jsterm.getInputValue(), "document.getElem", "'document.getElem' completion");
|
||||
checkJsTermCompletionValue(jsterm, " entsByTagNameNS",
|
||||
"'document.getElem' completion");
|
||||
|
||||
// Test pressing tab another time.
|
||||
await jsterm.complete(jsterm.COMPLETE_FORWARD);
|
||||
is(input.value, "document.getElem", "'document.getElem' completion");
|
||||
is(jsterm.completeNode.value, " entsByTagName",
|
||||
is(jsterm.getInputValue(), "document.getElem", "'document.getElem' completion");
|
||||
checkJsTermCompletionValue(jsterm, " entsByTagName",
|
||||
"'document.getElem' another tab completion");
|
||||
|
||||
// Test pressing shift_tab.
|
||||
await jstermComplete(jsterm, jsterm.COMPLETE_BACKWARD);
|
||||
is(input.value, "document.getElem", "'document.getElem' untab completion");
|
||||
is(jsterm.completeNode.value, " entsByTagNameNS",
|
||||
is(jsterm.getInputValue(), "document.getElem", "'document.getElem' untab completion");
|
||||
checkJsTermCompletionValue(jsterm, " entsByTagNameNS",
|
||||
"'document.getElem' completion");
|
||||
|
||||
ui.clearOutput();
|
||||
|
||||
await jstermSetValueAndComplete(jsterm, "docu");
|
||||
is(jsterm.completeNode.value, " ment", "'docu' completion");
|
||||
checkJsTermCompletionValue(jsterm, " ment", "'docu' completion");
|
||||
|
||||
await jsterm.execute();
|
||||
is(jsterm.completeNode.value, "", "clear completion on execute()");
|
||||
checkJsTermCompletionValue(jsterm, "", "clear completion on execute()");
|
||||
|
||||
// Test multi-line completion works
|
||||
await jstermSetValueAndComplete(jsterm, "console.log('one');\nconsol");
|
||||
is(jsterm.completeNode.value, " \n e",
|
||||
checkJsTermCompletionValue(jsterm, " \n e",
|
||||
"multi-line completion");
|
||||
|
||||
// Test non-object autocompletion.
|
||||
await jstermSetValueAndComplete(jsterm, "Object.name.sl");
|
||||
is(jsterm.completeNode.value, " ice", "non-object completion");
|
||||
checkJsTermCompletionValue(jsterm, " ice", "non-object completion");
|
||||
|
||||
// Test string literal autocompletion.
|
||||
await jstermSetValueAndComplete(jsterm, "'Asimov'.sl");
|
||||
is(jsterm.completeNode.value, " ice", "string literal completion");
|
||||
});
|
||||
checkJsTermCompletionValue(jsterm, " ice", "string literal completion");
|
||||
}
|
||||
|
@ -27,6 +27,14 @@ const TEST_URI = `data:text/html;charset=utf-8,
|
||||
</body>`;
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const {jsterm} = await openNewTabAndConsole(TEST_URI);
|
||||
const random = Math.random();
|
||||
const string = "Text: " + random;
|
||||
@ -42,7 +50,7 @@ add_task(async function() {
|
||||
}
|
||||
);
|
||||
await testCopy(jsterm, `$("#${id}")`, outerHTML);
|
||||
});
|
||||
}
|
||||
|
||||
function testCopy(jsterm, stringToCopy, expectedResult) {
|
||||
return waitForClipboardPromise(() => {
|
||||
|
@ -9,27 +9,64 @@
|
||||
|
||||
const TEST_URI = "data:text/html;charset=utf-8,Test console select all";
|
||||
|
||||
add_task(async function testCtrlA() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const {jsterm} = await openNewTabAndConsole(TEST_URI);
|
||||
|
||||
const jsterm = hud.jsterm;
|
||||
jsterm.setInputValue("Ignore These Four Words");
|
||||
const inputNode = jsterm.inputNode;
|
||||
|
||||
// Test select all with (cmd|control) + a.
|
||||
EventUtils.synthesizeKey("a", { accelKey: true });
|
||||
|
||||
const inputLength = inputNode.selectionEnd - inputNode.selectionStart;
|
||||
const inputLength = getSelectionTextLength(jsterm);
|
||||
is(inputLength, jsterm.getInputValue().length, "Select all of input");
|
||||
|
||||
// (cmd|control) + e cannot be disabled on Linux so skip this section on that
|
||||
// OS.
|
||||
// (cmd|control) + e cannot be disabled on Linux so skip this section on that OS.
|
||||
if (Services.appinfo.OS !== "Linux") {
|
||||
// Test do nothing on Control + E.
|
||||
jsterm.setInputValue("Ignore These Four Words");
|
||||
inputNode.selectionStart = 0;
|
||||
setCursorAtStart(jsterm);
|
||||
EventUtils.synthesizeKey("e", { accelKey: true });
|
||||
is(inputNode.selectionStart, 0,
|
||||
"control|cmd + e does not move to end of input");
|
||||
checkSelectionStart(jsterm, 0, "control|cmd + e does not move to end of input");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getSelectionTextLength(jsterm) {
|
||||
if (jsterm.inputNode) {
|
||||
return jsterm.inputNode.selectionEnd - jsterm.inputNode.selectionStart;
|
||||
}
|
||||
|
||||
if (jsterm.editor) {
|
||||
return jsterm.editor.getSelection().length;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function setCursorAtStart(jsterm) {
|
||||
if (jsterm.inputNode) {
|
||||
jsterm.inputNode.selectionStart = 0;
|
||||
}
|
||||
|
||||
if (jsterm.editor) {
|
||||
jsterm.editor.setCursor({line: 0, ch: 0});
|
||||
}
|
||||
}
|
||||
|
||||
function checkSelectionStart(jsterm, expectedCursorIndex, assertionInfo) {
|
||||
if (jsterm.inputNode) {
|
||||
const { selectionStart } = jsterm.inputNode;
|
||||
is(selectionStart, expectedCursorIndex, assertionInfo);
|
||||
} else {
|
||||
const [ selection ] = jsterm.editor.codeMirror.listSelections();
|
||||
const { head} = selection;
|
||||
is(head.ch, expectedCursorIndex, assertionInfo);
|
||||
}
|
||||
}
|
||||
|
@ -15,214 +15,267 @@ const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " +
|
||||
"bug 804845 and bug 619598";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const {jsterm} = await openNewTabAndConsole(TEST_URI);
|
||||
|
||||
ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty");
|
||||
is(jsterm.inputNode.selectionStart, 0);
|
||||
is(jsterm.inputNode.selectionEnd, 0);
|
||||
checkJsTermCursor(jsterm, 0, "Cursor is at the start of the input");
|
||||
|
||||
testSingleLineInputNavNoHistory(jsterm);
|
||||
testMultiLineInputNavNoHistory(jsterm);
|
||||
await testNavWithHistory(jsterm);
|
||||
});
|
||||
}
|
||||
|
||||
function testSingleLineInputNavNoHistory(jsterm) {
|
||||
const inputNode = jsterm.inputNode;
|
||||
const checkInput = (expected, assertionInfo) =>
|
||||
checkJsTermValueAndCursor(jsterm, expected, assertionInfo);
|
||||
|
||||
// Single char input
|
||||
EventUtils.sendString("1");
|
||||
is(inputNode.selectionStart, 1, "caret location after single char input");
|
||||
checkInput("1|", "caret location after single char input");
|
||||
|
||||
// nav to start/end with ctrl-a and ctrl-e;
|
||||
synthesizeLineStartKey();
|
||||
is(inputNode.selectionStart, 0,
|
||||
"caret location after single char input and ctrl-a");
|
||||
checkInput("|1", "caret location after single char input and ctrl-a");
|
||||
|
||||
synthesizeLineEndKey();
|
||||
is(inputNode.selectionStart, 1,
|
||||
"caret location after single char input and ctrl-e");
|
||||
checkInput("1|", "caret location after single char input and ctrl-e");
|
||||
|
||||
// Second char input
|
||||
EventUtils.sendString("2");
|
||||
checkInput("12|", "caret location after second char input");
|
||||
|
||||
// nav to start/end with up/down keys; verify behaviour using ctrl-p/ctrl-n
|
||||
EventUtils.synthesizeKey("KEY_ArrowUp");
|
||||
is(inputNode.selectionStart, 0,
|
||||
"caret location after two char input and KEY_ArrowUp");
|
||||
checkInput("|12", "caret location after two char input and KEY_ArrowUp");
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
is(inputNode.selectionStart, 2,
|
||||
"caret location after two char input and KEY_ArrowDown");
|
||||
checkInput("12|", "caret location after two char input and KEY_ArrowDown");
|
||||
|
||||
synthesizeLineStartKey();
|
||||
is(inputNode.selectionStart, 0,
|
||||
"move caret to beginning of 2 char input with ctrl-a");
|
||||
checkInput("|12", "move caret to beginning of 2 char input with ctrl-a");
|
||||
|
||||
synthesizeLineStartKey();
|
||||
is(inputNode.selectionStart, 0,
|
||||
"no change of caret location on repeat ctrl-a");
|
||||
synthesizeLineUpKey();
|
||||
is(inputNode.selectionStart, 0,
|
||||
"no change of caret location on ctrl-p from beginning of line");
|
||||
|
||||
synthesizeLineEndKey();
|
||||
is(inputNode.selectionStart, 2,
|
||||
"move caret to end of 2 char input with ctrl-e");
|
||||
synthesizeLineEndKey();
|
||||
is(inputNode.selectionStart, 2,
|
||||
"no change of caret location on repeat ctrl-e");
|
||||
synthesizeLineDownKey();
|
||||
is(inputNode.selectionStart, 2,
|
||||
"no change of caret location on ctrl-n from end of line");
|
||||
checkInput("|12", "no change of caret location on repeat ctrl-a");
|
||||
|
||||
synthesizeLineUpKey();
|
||||
is(inputNode.selectionStart, 0, "ctrl-p moves to start of line");
|
||||
checkInput("|12", "no change of caret location on ctrl-p from beginning of line");
|
||||
|
||||
synthesizeLineEndKey();
|
||||
checkInput("12|", "move caret to end of 2 char input with ctrl-e");
|
||||
|
||||
synthesizeLineEndKey();
|
||||
checkInput("12|", "no change of caret location on repeat ctrl-e");
|
||||
|
||||
synthesizeLineDownKey();
|
||||
is(inputNode.selectionStart, 2, "ctrl-n moves to end of line");
|
||||
checkInput("12|", "no change of caret location on ctrl-n from end of line");
|
||||
|
||||
synthesizeLineUpKey();
|
||||
checkInput("|12", "ctrl-p moves to start of line");
|
||||
|
||||
synthesizeLineDownKey();
|
||||
checkInput("12|", "ctrl-n moves to end of line");
|
||||
}
|
||||
|
||||
function testMultiLineInputNavNoHistory(jsterm) {
|
||||
const inputNode = jsterm.inputNode;
|
||||
const checkInput = (expected, assertionInfo) =>
|
||||
checkJsTermValueAndCursor(jsterm, expected, assertionInfo);
|
||||
|
||||
const lineValues = ["one", "2", "something longer", "", "", "three!"];
|
||||
jsterm.setInputValue("");
|
||||
// simulate shift-return
|
||||
for (let i = 0; i < lineValues.length; i++) {
|
||||
jsterm.setInputValue(jsterm.getInputValue() + lineValues[i]);
|
||||
for (const lineValue of lineValues) {
|
||||
jsterm.setInputValue(jsterm.getInputValue() + lineValue);
|
||||
EventUtils.synthesizeKey("KEY_Enter", {shiftKey: true});
|
||||
}
|
||||
const inputValue = jsterm.getInputValue();
|
||||
is(inputNode.selectionStart, inputNode.selectionEnd);
|
||||
is(inputNode.selectionStart, inputValue.length,
|
||||
"caret at end of multiline input");
|
||||
|
||||
// possibility newline is represented by one ('\r', '\n') or two
|
||||
// ('\r\n') chars
|
||||
const newlineString = inputValue.match(/(\r\n?|\n\r?)$/)[0];
|
||||
checkInput(
|
||||
`one
|
||||
2
|
||||
something longer
|
||||
|
||||
|
||||
three!
|
||||
|`, "caret at end of multiline input");
|
||||
|
||||
// Ok, test navigating within the multi-line string!
|
||||
EventUtils.synthesizeKey("KEY_ArrowUp");
|
||||
let expectedStringAfterCarat = lineValues[5] + newlineString;
|
||||
is(jsterm.getInputValue().slice(inputNode.selectionStart), expectedStringAfterCarat,
|
||||
"up arrow from end of multiline");
|
||||
checkInput(
|
||||
`one
|
||||
2
|
||||
something longer
|
||||
|
||||
|
||||
|three!
|
||||
`, "up arrow from end of multiline");
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
is(jsterm.getInputValue().slice(inputNode.selectionStart), "",
|
||||
"down arrow from within multiline");
|
||||
checkInput(
|
||||
`one
|
||||
2
|
||||
something longer
|
||||
|
||||
|
||||
three!
|
||||
|`, "down arrow from within multiline");
|
||||
|
||||
// navigate up through input lines
|
||||
synthesizeLineUpKey();
|
||||
is(jsterm.getInputValue().slice(inputNode.selectionStart), expectedStringAfterCarat,
|
||||
"ctrl-p from end of multiline");
|
||||
checkInput(
|
||||
`one
|
||||
2
|
||||
something longer
|
||||
|
||||
for (let i = 4; i >= 0; i--) {
|
||||
|
||||
|three!
|
||||
`, "ctrl-p from end of multiline");
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
synthesizeLineUpKey();
|
||||
expectedStringAfterCarat = lineValues[i] + newlineString +
|
||||
expectedStringAfterCarat;
|
||||
is(jsterm.getInputValue().slice(inputNode.selectionStart),
|
||||
expectedStringAfterCarat, "ctrl-p from within line " + i +
|
||||
" of multiline input");
|
||||
}
|
||||
|
||||
checkInput(
|
||||
`|one
|
||||
2
|
||||
something longer
|
||||
|
||||
|
||||
three!
|
||||
`, "reached start of input");
|
||||
|
||||
synthesizeLineUpKey();
|
||||
is(inputNode.selectionStart, 0, "reached start of input");
|
||||
is(jsterm.getInputValue(), inputValue,
|
||||
"no change to multiline input on ctrl-p from beginning of multiline");
|
||||
checkInput(
|
||||
`|one
|
||||
2
|
||||
something longer
|
||||
|
||||
|
||||
three!
|
||||
`, "no change to multiline input on ctrl-p from beginning of multiline");
|
||||
|
||||
// navigate to end of first line
|
||||
synthesizeLineEndKey();
|
||||
let caretPos = inputNode.selectionStart;
|
||||
let expectedStringBeforeCarat = lineValues[0];
|
||||
is(jsterm.getInputValue().slice(0, caretPos), expectedStringBeforeCarat,
|
||||
"ctrl-e into multiline input");
|
||||
checkInput(
|
||||
`one|
|
||||
2
|
||||
something longer
|
||||
|
||||
|
||||
three!
|
||||
`, "ctrl-e into multiline input");
|
||||
|
||||
synthesizeLineEndKey();
|
||||
is(inputNode.selectionStart, caretPos,
|
||||
"repeat ctrl-e doesn't change caret position in multiline input");
|
||||
checkInput(
|
||||
`one|
|
||||
2
|
||||
something longer
|
||||
|
||||
// navigate down one line; ctrl-a to the beginning; ctrl-e to end
|
||||
for (let i = 1; i < lineValues.length; i++) {
|
||||
synthesizeLineDownKey();
|
||||
synthesizeLineStartKey();
|
||||
caretPos = inputNode.selectionStart;
|
||||
expectedStringBeforeCarat += newlineString;
|
||||
is(jsterm.getInputValue().slice(0, caretPos), expectedStringBeforeCarat,
|
||||
"ctrl-a to beginning of line " + (i + 1) + " in multiline input");
|
||||
|
||||
synthesizeLineEndKey();
|
||||
caretPos = inputNode.selectionStart;
|
||||
expectedStringBeforeCarat += lineValues[i];
|
||||
is(jsterm.getInputValue().slice(0, caretPos), expectedStringBeforeCarat,
|
||||
"ctrl-e to end of line " + (i + 1) + "in multiline input");
|
||||
}
|
||||
three!
|
||||
`, "repeat ctrl-e doesn't change caret position in multiline input");
|
||||
|
||||
synthesizeLineDownKey();
|
||||
synthesizeLineStartKey();
|
||||
checkInput(
|
||||
`one
|
||||
|2
|
||||
something longer
|
||||
|
||||
|
||||
three!
|
||||
`);
|
||||
|
||||
synthesizeLineEndKey();
|
||||
synthesizeLineDownKey();
|
||||
synthesizeLineStartKey();
|
||||
checkInput(
|
||||
`one
|
||||
2
|
||||
|something longer
|
||||
|
||||
|
||||
three!
|
||||
`);
|
||||
}
|
||||
|
||||
async function testNavWithHistory(jsterm) {
|
||||
const inputNode = jsterm.inputNode;
|
||||
const checkInput = (expected, assertionInfo) =>
|
||||
checkJsTermValueAndCursor(jsterm, expected, assertionInfo);
|
||||
|
||||
// NOTE: Tests does NOT currently define behaviour for ctrl-p/ctrl-n with
|
||||
// caret placed _within_ single line input
|
||||
const values = [
|
||||
'"single line input"',
|
||||
'"a longer single-line input to check caret repositioning"',
|
||||
'"multi-line"\n"input"\n"here!"',
|
||||
"single line input",
|
||||
"a longer single-line input to check caret repositioning",
|
||||
"multi-line\ninput\nhere",
|
||||
];
|
||||
|
||||
// submit to history
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
jsterm.setInputValue(values[i]);
|
||||
for (const value of values) {
|
||||
jsterm.setInputValue(value);
|
||||
await jsterm.execute();
|
||||
}
|
||||
is(inputNode.selectionStart, 0, "caret location at start of empty line");
|
||||
|
||||
checkInput("|", "caret location at start of empty line");
|
||||
|
||||
synthesizeLineUpKey();
|
||||
is(inputNode.selectionStart, values[values.length - 1].length,
|
||||
"caret location correct at end of last history input");
|
||||
checkInput("multi-line\ninput\nhere|", "caret location at end of last history input");
|
||||
|
||||
// Navigate backwards history with ctrl-p
|
||||
for (let i = values.length - 1; i > 0; i--) {
|
||||
const match = values[i].match(/(\n)/g);
|
||||
if (match) {
|
||||
// multi-line inputs won't update from history unless caret at beginning
|
||||
synthesizeLineStartKey();
|
||||
for (let j = 0; j < match.length; j++) {
|
||||
synthesizeLineUpKey();
|
||||
}
|
||||
synthesizeLineUpKey();
|
||||
} else {
|
||||
// single-line inputs will update from history from end of line
|
||||
synthesizeLineUpKey();
|
||||
}
|
||||
is(jsterm.getInputValue(), values[i - 1],
|
||||
"ctrl-p updates inputNode from backwards history values[" + i - 1 + "]");
|
||||
}
|
||||
synthesizeLineStartKey();
|
||||
checkInput("multi-line\ninput\n|here",
|
||||
"caret location at beginning of last line of last history input");
|
||||
|
||||
let inputValue = jsterm.getInputValue();
|
||||
synthesizeLineUpKey();
|
||||
is(inputNode.selectionStart, 0,
|
||||
"ctrl-p at beginning of history moves caret location to beginning " +
|
||||
"of line");
|
||||
is(jsterm.getInputValue(), inputValue,
|
||||
"no change to input value on ctrl-p from beginning of line");
|
||||
checkInput("multi-line\n|input\nhere",
|
||||
"caret location at beginning of second line of last history input");
|
||||
|
||||
synthesizeLineUpKey();
|
||||
checkInput("|multi-line\ninput\nhere",
|
||||
"caret location at beginning of first line of last history input");
|
||||
|
||||
synthesizeLineUpKey();
|
||||
checkInput("a longer single-line input to check caret repositioning|",
|
||||
"caret location at the end of second history input");
|
||||
|
||||
synthesizeLineUpKey();
|
||||
checkInput("single line input|", "caret location at the end of first history input");
|
||||
|
||||
synthesizeLineUpKey();
|
||||
checkInput("|single line input",
|
||||
"ctrl-p at beginning of history moves caret location to beginning of line");
|
||||
|
||||
// Navigate forwards history with ctrl-n
|
||||
for (let i = 1; i < values.length; i++) {
|
||||
synthesizeLineDownKey();
|
||||
is(jsterm.getInputValue(), values[i],
|
||||
"ctrl-n updates inputNode from forwards history values[" + i + "]");
|
||||
is(inputNode.selectionStart, values[i].length,
|
||||
"caret location correct at end of history input for values[" + i + "]");
|
||||
}
|
||||
synthesizeLineDownKey();
|
||||
ok(!jsterm.getInputValue(), "ctrl-n at end of history updates to empty input");
|
||||
checkInput("a longer single-line input to check caret repositioning|",
|
||||
"caret location at the end of second history input");
|
||||
|
||||
synthesizeLineDownKey();
|
||||
checkInput("multi-line\ninput\nhere|", "caret location at end of last history input");
|
||||
|
||||
synthesizeLineDownKey();
|
||||
checkInput("|", "ctrl-n at end of history updates to empty input");
|
||||
|
||||
// Simulate editing multi-line
|
||||
inputValue = "one\nlinebreak";
|
||||
const inputValue = "one\nlinebreak";
|
||||
jsterm.setInputValue(inputValue);
|
||||
checkInput("one\nlinebreak|");
|
||||
|
||||
// Attempt nav within input
|
||||
synthesizeLineUpKey();
|
||||
is(jsterm.getInputValue(), inputValue,
|
||||
"ctrl-p from end of multi-line does not trigger history");
|
||||
checkInput("one|\nlinebreak", "ctrl-p from end of multi-line does not trigger history");
|
||||
|
||||
synthesizeLineStartKey();
|
||||
checkInput("|one\nlinebreak");
|
||||
|
||||
synthesizeLineUpKey();
|
||||
is(jsterm.getInputValue(), values[values.length - 1],
|
||||
"ctrl-p from start of multi-line triggers history");
|
||||
checkInput("multi-line\ninput\nhere|",
|
||||
"ctrl-p from start of multi-line triggers history");
|
||||
}
|
||||
|
||||
function synthesizeLineStartKey() {
|
||||
|
@ -6,6 +6,14 @@
|
||||
const TEST_URI = "data:text/html,Test evaluating document";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
const {jsterm} = hud;
|
||||
|
||||
@ -14,4 +22,4 @@ add_task(async function() {
|
||||
jsterm.execute("document");
|
||||
const {node} = await onMessage;
|
||||
is(node.textContent.includes("xray"), false, "document - no XrayWrapper");
|
||||
});
|
||||
}
|
||||
|
@ -12,10 +12,18 @@ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
|
||||
"test/mochitest/test-jsterm-dollar.html";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
await test$(hud);
|
||||
await test$$(hud);
|
||||
});
|
||||
}
|
||||
|
||||
async function test$(hud) {
|
||||
hud.ui.clearOutput();
|
||||
|
@ -6,6 +6,14 @@
|
||||
const TEST_URI = "data:text/html,Test error documentation";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
const {jsterm} = hud;
|
||||
|
||||
@ -30,4 +38,4 @@ add_task(async function() {
|
||||
ok(learnMoreLink, `There is a [Learn More] link for "${errorMessageName}" error`);
|
||||
is(learnMoreLink.title, title, `The link has the expected "${title}" title`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -9,6 +9,14 @@
|
||||
const TEST_URI = "data:text/html,Test error documentation";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
const {jsterm} = hud;
|
||||
|
||||
@ -18,4 +26,4 @@ add_task(async function() {
|
||||
jsterm.execute("new Request('',{redirect:'foo'})");
|
||||
await onErrorMessage;
|
||||
ok(true, "Error message displayed as expected, without crashing the console.");
|
||||
});
|
||||
}
|
||||
|
@ -6,6 +6,14 @@
|
||||
const TEST_URI = "data:text/html,Test <code>clear()</code> jsterm helper";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
|
||||
const onMessage = waitForMessage(hud, "message");
|
||||
@ -19,4 +27,4 @@ add_task(async function() {
|
||||
hud.jsterm.execute("clear()");
|
||||
await onCleared;
|
||||
ok(true, "Console was cleared");
|
||||
});
|
||||
}
|
||||
|
@ -16,6 +16,14 @@ const TEST_URI = `data:text/html,
|
||||
`;
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
const jsterm = hud.jsterm;
|
||||
|
||||
@ -38,4 +46,4 @@ add_task(async function() {
|
||||
jsterm.execute("$('div')");
|
||||
message = await onMessage;
|
||||
ok(message, "`$('div')` does return null");
|
||||
});
|
||||
}
|
||||
|
@ -16,6 +16,14 @@ const TEST_URI = `data:text/html,
|
||||
`;
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
const jsterm = hud.jsterm;
|
||||
|
||||
@ -38,4 +46,4 @@ add_task(async function() {
|
||||
jsterm.execute("$$('div')");
|
||||
message = await onMessage;
|
||||
ok(message, "`$$('div')` returns an empty array");
|
||||
});
|
||||
}
|
||||
|
@ -16,6 +16,14 @@ const TEST_URI = `data:text/html,
|
||||
`;
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
const jsterm = hud.jsterm;
|
||||
|
||||
@ -28,4 +36,4 @@ add_task(async function() {
|
||||
jsterm.execute("$x('.//li', document.body)[0]");
|
||||
message = await onMessage;
|
||||
ok(message, "`$x()` result can be used right away");
|
||||
});
|
||||
}
|
||||
|
@ -7,6 +7,14 @@ const TEST_URI = "data:text/html,Test <code>help()</code> jsterm helper";
|
||||
const HELP_URL = "https://developer.mozilla.org/docs/Tools/Web_Console/Helpers";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
const jsterm = hud.jsterm;
|
||||
|
||||
@ -28,4 +36,4 @@ add_task(async function() {
|
||||
"There is no results shown for the help commands");
|
||||
is(openedLinks, 3, "correct number of pages opened by the help calls");
|
||||
hud.openLink = oldOpenLink;
|
||||
});
|
||||
}
|
||||
|
@ -7,6 +7,14 @@ const TEST_URI =
|
||||
"data:text/html,Test <code>keys()</code> & <code>values()</code> jsterm helper";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
const jsterm = hud.jsterm;
|
||||
|
||||
@ -24,4 +32,4 @@ add_task(async function() {
|
||||
jsterm.execute("keys(window)");
|
||||
message = await onMessage;
|
||||
ok(message, "`keys(window)` worked");
|
||||
});
|
||||
}
|
||||
|
@ -6,6 +6,14 @@
|
||||
const TEST_URI = "data:text/html,Test <code>pprint()</code> jsterm helper";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
const {jsterm} = hud;
|
||||
|
||||
@ -31,4 +39,4 @@ add_task(async function() {
|
||||
jsterm.execute("pprint(function() { var someCanaryValue = 42; })");
|
||||
message = await onMessage;
|
||||
ok(message, "`pprint(function)` shows function source");
|
||||
});
|
||||
}
|
||||
|
@ -23,6 +23,14 @@
|
||||
"use strict";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
let browserConsole, webConsole, objInspector;
|
||||
|
||||
// We don't use `pushPref()` because we need to revert the same pref later
|
||||
@ -52,8 +60,11 @@ add_task(async function() {
|
||||
objInspector = await getObjectInspector(webConsole);
|
||||
testJSTermIsVisible(webConsole);
|
||||
await testObjectInspectorPropertiesAreSet(objInspector);
|
||||
|
||||
info("Close webconsole and browser console");
|
||||
await closeConsole(browserTab);
|
||||
});
|
||||
await HUDService.toggleBrowserConsole();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns either the Variables View or Object Inspector depending on which is
|
||||
|
@ -30,8 +30,11 @@ async function performTests() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
const { jsterm } = hud;
|
||||
|
||||
const checkInput = (expected, assertionInfo) =>
|
||||
checkJsTermValueAndCursor(jsterm, expected, assertionInfo);
|
||||
|
||||
jsterm.focus();
|
||||
ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty");
|
||||
checkInput("|", "input is empty");
|
||||
|
||||
info("Execute each test value in the console");
|
||||
for (const value of TEST_VALUES) {
|
||||
@ -39,94 +42,67 @@ async function performTests() {
|
||||
await jsterm.execute();
|
||||
}
|
||||
|
||||
const values = TEST_VALUES;
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowUp");
|
||||
|
||||
is(jsterm.getInputValue(), values[4], "VK_UP: jsterm.getInputValue() #4 is correct");
|
||||
is(getCaretPosition(jsterm), values[4].length, "caret location is correct");
|
||||
checkInput("document.location|", "↑: input #4 is correct");
|
||||
ok(inputHasNoSelection(jsterm));
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowUp");
|
||||
|
||||
is(jsterm.getInputValue(), values[3], "VK_UP: jsterm.getInputValue() #3 is correct");
|
||||
is(getCaretPosition(jsterm), values[3].length, "caret location is correct");
|
||||
checkInput("document;\nwindow;\ndocument.body|", "↑: input #3 is correct");
|
||||
ok(inputHasNoSelection(jsterm));
|
||||
|
||||
info("Select text and ensure hitting arrow up twice won't navigate the history");
|
||||
setCursorAtPosition(jsterm, values[3].length - 2);
|
||||
info("Move cursor and ensure hitting arrow up twice won't navigate the history");
|
||||
EventUtils.synthesizeKey("KEY_ArrowLeft");
|
||||
EventUtils.synthesizeKey("KEY_ArrowLeft");
|
||||
checkInput("document;\nwindow;\ndocument.bo|dy");
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowUp");
|
||||
EventUtils.synthesizeKey("KEY_ArrowUp");
|
||||
|
||||
is(jsterm.getInputValue(), values[3],
|
||||
"VK_UP two times: jsterm.getInputValue() #3 is correct");
|
||||
is(getCaretPosition(jsterm), jsterm.getInputValue().indexOf("\n"),
|
||||
"caret location is correct");
|
||||
checkInput("document;|\nwindow;\ndocument.body", "↑↑: input #3 is correct");
|
||||
ok(inputHasNoSelection(jsterm));
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowUp");
|
||||
|
||||
is(jsterm.getInputValue(), values[3],
|
||||
"VK_UP again: jsterm.getInputValue() #3 is correct");
|
||||
is(getCaretPosition(jsterm), 0, "caret location is correct");
|
||||
checkInput("|document;\nwindow;\ndocument.body", "↑ again: input #3 is correct");
|
||||
ok(inputHasNoSelection(jsterm));
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowUp");
|
||||
|
||||
is(jsterm.getInputValue(), values[2], "VK_UP: jsterm.getInputValue() #2 is correct");
|
||||
checkInput("document.body|", "↑: input #2 is correct");
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowUp");
|
||||
|
||||
is(jsterm.getInputValue(), values[1], "VK_UP: jsterm.getInputValue() #1 is correct");
|
||||
checkInput("window|", "↑: input #1 is correct");
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowUp");
|
||||
|
||||
is(jsterm.getInputValue(), values[0], "VK_UP: jsterm.getInputValue() #0 is correct");
|
||||
is(getCaretPosition(jsterm), values[0].length, "caret location is correct");
|
||||
checkInput("document|", "↑: input #0 is correct");
|
||||
ok(inputHasNoSelection(jsterm));
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
|
||||
is(jsterm.getInputValue(), values[1], "VK_DOWN: jsterm.getInputValue() #1 is correct");
|
||||
is(getCaretPosition(jsterm), values[1].length, "caret location is correct");
|
||||
checkInput("window|", "↓: input #1 is correct");
|
||||
ok(inputHasNoSelection(jsterm));
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
|
||||
is(jsterm.getInputValue(), values[2], "VK_DOWN: jsterm.getInputValue() #2 is correct");
|
||||
checkInput("document.body|", "↓: input #2 is correct");
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
|
||||
is(jsterm.getInputValue(), values[3], "VK_DOWN: jsterm.getInputValue() #3 is correct");
|
||||
is(getCaretPosition(jsterm), values[3].length, "caret location is correct");
|
||||
checkInput("document;\nwindow;\ndocument.body|", "↓: input #3 is correct");
|
||||
ok(inputHasNoSelection(jsterm));
|
||||
|
||||
setCursorAtPosition(jsterm, 2);
|
||||
checkInput("do|cument;\nwindow;\ndocument.body");
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
|
||||
is(jsterm.getInputValue(), values[3],
|
||||
"VK_DOWN two times: jsterm.getInputValue() #3 is correct");
|
||||
|
||||
ok(getCaretPosition(jsterm) > jsterm.getInputValue().lastIndexOf("\n")
|
||||
&& inputHasNoSelection(jsterm),
|
||||
"caret location is correct");
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
|
||||
is(jsterm.getInputValue(), values[3],
|
||||
"VK_DOWN again: jsterm.getInputValue() #3 is correct");
|
||||
is(getCaretPosition(jsterm), values[3].length, "caret location is correct");
|
||||
checkInput("document;\nwindow;\ndo|cument.body", "↓↓: input #3 is correct");
|
||||
ok(inputHasNoSelection(jsterm));
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
|
||||
is(jsterm.getInputValue(), values[4], "VK_DOWN: jsterm.getInputValue() #4 is correct");
|
||||
checkInput("document;\nwindow;\ndocument.body|", "↓ again: input #3 is correct");
|
||||
ok(inputHasNoSelection(jsterm));
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
checkInput("document.location|", "↓: input #4 is correct");
|
||||
|
||||
ok(!jsterm.getInputValue(), "VK_DOWN: jsterm.getInputValue() is empty");
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
checkInput("|", "↓: input is empty");
|
||||
}
|
||||
|
||||
function setCursorAtPosition(jsterm, pos) {
|
||||
@ -151,16 +127,6 @@ function setCursorAtPosition(jsterm, pos) {
|
||||
return inputNode.setSelectionRange(pos, pos);
|
||||
}
|
||||
|
||||
function getCaretPosition(jsterm) {
|
||||
const {inputNode, editor} = jsterm;
|
||||
|
||||
if (editor) {
|
||||
return editor.getDoc().getRange({line: 0, ch: 0}, editor.getCursor()).length;
|
||||
}
|
||||
|
||||
return inputNode.selectionStart;
|
||||
}
|
||||
|
||||
function inputHasNoSelection(jsterm) {
|
||||
if (jsterm.editor) {
|
||||
return !jsterm.editor.getDoc().getSelection();
|
||||
|
@ -10,29 +10,24 @@
|
||||
const TEST_URI = "data:text/html;charset=utf-8,Test for jsterm multine input";
|
||||
|
||||
add_task(async function() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
const input = hud.jsterm.inputNode;
|
||||
async function performTests() {
|
||||
const {jsterm, ui} = await openNewTabAndConsole(TEST_URI);
|
||||
const inputContainer = ui.window.document.querySelector(".jsterm-input-container");
|
||||
|
||||
info("Focus the jsterm input");
|
||||
input.focus();
|
||||
|
||||
const ordinaryHeight = input.clientHeight;
|
||||
const ordinaryHeight = inputContainer.clientHeight;
|
||||
|
||||
// Set a multiline value
|
||||
input.value = "hello\nworld\n";
|
||||
|
||||
// Set the caret at the end of input
|
||||
const length = input.value.length;
|
||||
input.selectionEnd = length;
|
||||
input.selectionStart = length;
|
||||
|
||||
info("Type 'd' in jsterm to trigger height change for the input");
|
||||
EventUtils.sendString("d");
|
||||
ok(input.clientHeight > ordinaryHeight, "the input expanded");
|
||||
jsterm.setInputValue("hello\nworld\n");
|
||||
ok(inputContainer.clientHeight > ordinaryHeight, "the input expanded");
|
||||
|
||||
info("Erase the value and test if the inputNode shrinks again");
|
||||
input.value = "";
|
||||
EventUtils.sendString("d");
|
||||
is(input.clientHeight, ordinaryHeight, "the input's height is normal again");
|
||||
});
|
||||
jsterm.setInputValue("");
|
||||
is(inputContainer.clientHeight, ordinaryHeight, "the input's height is normal again");
|
||||
}
|
||||
|
@ -10,6 +10,14 @@
|
||||
const TEST_URI = "data:text/html;charset=utf8,<p>test inspect() command";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const toolbox = await openNewTabAndToolbox(TEST_URI, "webconsole");
|
||||
const hud = toolbox.getCurrentPanel().hud;
|
||||
|
||||
@ -55,7 +63,7 @@ add_task(async function() {
|
||||
const inspectPrimitiveNode = await waitFor(() =>
|
||||
findInspectResultMessage(hud.ui.outputNode, 2));
|
||||
is(inspectPrimitiveNode.textContent, 1, "The primitive is displayed as expected");
|
||||
});
|
||||
}
|
||||
|
||||
function findInspectResultMessage(node, index) {
|
||||
return node.querySelectorAll(".message.result")[index];
|
||||
|
@ -7,6 +7,14 @@
|
||||
const TEST_URI = "data:text/html,Test <code>instanceof</code> evaluation";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
const {jsterm} = hud;
|
||||
|
||||
@ -24,4 +32,4 @@ add_task(async function() {
|
||||
jsterm.execute("({}) instanceof Array");
|
||||
message = await onMessage;
|
||||
ok(message, "`instanceof Array` has expected result");
|
||||
});
|
||||
}
|
||||
|
@ -76,6 +76,14 @@ const DATA = [
|
||||
];
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
// Let's reset the counts.
|
||||
Services.telemetry.clearEvents();
|
||||
|
||||
@ -83,37 +91,30 @@ add_task(async function() {
|
||||
const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
|
||||
ok(!snapshot.parent, "No events have been logged for the main process");
|
||||
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
const { inputNode } = hud.jsterm;
|
||||
const {jsterm} = await openNewTabAndConsole(TEST_URI);
|
||||
|
||||
for (const {input, shiftKey} of SHOULD_ENTER_MULTILINE) {
|
||||
hud.jsterm.setInputValue(input);
|
||||
jsterm.setInputValue(input);
|
||||
EventUtils.synthesizeKey("VK_RETURN", { shiftKey });
|
||||
|
||||
const inputValue = hud.jsterm.getInputValue();
|
||||
is(inputNode.selectionStart, inputNode.selectionEnd, "selection is collapsed");
|
||||
is(inputNode.selectionStart, inputValue.length, "caret at end of multiline input");
|
||||
|
||||
const inputWithNewline = input + "\n";
|
||||
is(inputValue, inputWithNewline, "Input value is correct");
|
||||
// We need to remove the spaces at the end of the input since code mirror do some
|
||||
// automatic indent in some case.
|
||||
const newValue = jsterm.getInputValue().replace(/ +$/g, "");
|
||||
is(newValue, input + "\n", "A new line was added");
|
||||
}
|
||||
|
||||
for (const {input, shiftKey} of SHOULD_EXECUTE) {
|
||||
hud.jsterm.setInputValue(input);
|
||||
jsterm.setInputValue(input);
|
||||
EventUtils.synthesizeKey("VK_RETURN", { shiftKey });
|
||||
|
||||
await waitFor(() => !hud.jsterm.getInputValue());
|
||||
|
||||
const inputValue = hud.jsterm.getInputValue();
|
||||
is(inputNode.selectionStart, 0, "selection starts/ends at 0");
|
||||
is(inputNode.selectionEnd, 0, "selection starts/ends at 0");
|
||||
is(inputValue, "", "Input value is cleared");
|
||||
await waitFor(() => !jsterm.getInputValue());
|
||||
is(jsterm.getInputValue(), "", "Input is cleared");
|
||||
}
|
||||
|
||||
await hud.jsterm.execute("document.\nlocation.\nhref");
|
||||
await jsterm.execute("document.\nlocation.\nhref");
|
||||
|
||||
checkEventTelemetry();
|
||||
});
|
||||
}
|
||||
|
||||
function checkEventTelemetry() {
|
||||
const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
|
||||
|
@ -11,27 +11,30 @@ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
|
||||
"test/mochitest/test-console.html";
|
||||
|
||||
add_task(async function() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
testCompletion(hud);
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
function testCompletion(hud) {
|
||||
async function performTests() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
const jsterm = hud.jsterm;
|
||||
const input = jsterm.inputNode;
|
||||
|
||||
// Test typing 'var d = 5;' and press RETURN
|
||||
jsterm.setInputValue("var d = ");
|
||||
EventUtils.sendString("5;");
|
||||
is(input.value, "var d = 5;", "var d = 5;");
|
||||
is(jsterm.completeNode.value, "", "no completion");
|
||||
is(jsterm.getInputValue(), "var d = 5;", "var d = 5;");
|
||||
checkJsTermCompletionValue(jsterm, "", "no completion");
|
||||
EventUtils.synthesizeKey("KEY_Enter");
|
||||
is(jsterm.completeNode.value, "", "clear completion on execute()");
|
||||
checkJsTermCompletionValue(jsterm, "", "clear completion on execute()");
|
||||
|
||||
// Test typing 'var a = d' and press RETURN
|
||||
jsterm.setInputValue("var a = ");
|
||||
EventUtils.sendString("d");
|
||||
is(input.value, "var a = d", "var a = d");
|
||||
is(jsterm.completeNode.value, "", "no completion");
|
||||
is(jsterm.getInputValue(), "var a = d", "var a = d");
|
||||
checkJsTermCompletionValue(jsterm, "", "no completion");
|
||||
EventUtils.synthesizeKey("KEY_Enter");
|
||||
is(jsterm.completeNode.value, "", "clear completion on execute()");
|
||||
checkJsTermCompletionValue(jsterm, "", "clear completion on execute()");
|
||||
}
|
||||
|
@ -10,19 +10,22 @@
|
||||
const TEST_URI = "data:text/html,Testing jsterm with no input";
|
||||
|
||||
add_task(async function() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
testCompletion(hud);
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
function testCompletion(hud) {
|
||||
async function performTests() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
const jsterm = hud.jsterm;
|
||||
const input = jsterm.inputNode;
|
||||
|
||||
// With empty input, tab through
|
||||
jsterm.setInputValue("");
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(jsterm.getInputValue(), "", "inputnode is empty - matched");
|
||||
ok(!hasFocus(input), "input isn't focused anymore");
|
||||
ok(!isJstermFocused(jsterm), "input isn't focused anymore");
|
||||
jsterm.focus();
|
||||
|
||||
// With non-empty input, insert a tab
|
||||
@ -30,5 +33,5 @@ function testCompletion(hud) {
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
is(jsterm.getInputValue(), "window.Bug583816\t",
|
||||
"input content - matched");
|
||||
ok(hasFocus(input), "input is still focused");
|
||||
ok(isJstermFocused(jsterm), "input is still focused");
|
||||
}
|
||||
|
@ -7,6 +7,14 @@ const TEST_URI =
|
||||
"data:text/html,Test evaluating null and undefined";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
const jsterm = hud.jsterm;
|
||||
|
||||
@ -20,4 +28,4 @@ add_task(async function() {
|
||||
jsterm.execute("undefined");
|
||||
message = await onMessage;
|
||||
ok(message, "`undefined` returned the expected value");
|
||||
});
|
||||
}
|
||||
|
@ -12,6 +12,14 @@ const TEST_URI = "data:text/html;charset=utf-8,<p>bug 900448 - autocomplete " +
|
||||
const TEST_URI_NAVIGATE = "data:text/html;charset=utf-8,<p>testing autocomplete closes";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
const popup = hud.jsterm.autocompletePopup;
|
||||
const popupShown = once(popup, "popup-opened");
|
||||
@ -24,4 +32,4 @@ add_task(async function() {
|
||||
await addTab(TEST_URI_NAVIGATE);
|
||||
|
||||
ok(!popup.isOpen, "Popup closes on tab switch");
|
||||
});
|
||||
}
|
||||
|
@ -13,6 +13,14 @@ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
|
||||
const dpr = "--dpr 1";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
ok(hud, "web console opened");
|
||||
|
||||
@ -23,7 +31,7 @@ add_task(async function() {
|
||||
// overflow
|
||||
await createScrollbarOverflow();
|
||||
await testFullpageClipboardScrollbar(hud);
|
||||
});
|
||||
}
|
||||
|
||||
async function testClipboard(hud) {
|
||||
const command = `:screenshot --clipboard ${dpr}`;
|
||||
|
@ -14,13 +14,21 @@ const FileUtils = (ChromeUtils.import("resource://gre/modules/FileUtils.jsm", {}
|
||||
const dpr = "--dpr 1";
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
await addTab(TEST_URI);
|
||||
|
||||
const hud = await openConsole();
|
||||
ok(hud, "web console opened");
|
||||
|
||||
await testFile(hud);
|
||||
});
|
||||
}
|
||||
|
||||
async function testFile(hud) {
|
||||
// Test capture to file
|
||||
|
@ -351,9 +351,9 @@ function hasFocus(node) {
|
||||
*
|
||||
* @param {JsTerm} jsterm
|
||||
* @param {String} value : The value to set the jsterm to.
|
||||
* @param {Integer} caretIndexOffset : A number that will be added to value.length
|
||||
* when setting the caret. A negative number will place the caret
|
||||
* in (end - offset) position. Default to 0 (caret set at the end)
|
||||
* @param {Integer} caretPosition : The index where to place the cursor. A negative
|
||||
* number will place the caret at (value.length - offset) position.
|
||||
* Default to value.length (caret set at the end).
|
||||
* @param {Integer} completionType : One of the following jsterm property
|
||||
* - COMPLETE_FORWARD
|
||||
* - COMPLETE_BACKWARD
|
||||
@ -363,11 +363,29 @@ function hasFocus(node) {
|
||||
* Will default to COMPLETE_HINT_ONLY.
|
||||
* @returns {Promise} resolves when the jsterm is completed.
|
||||
*/
|
||||
function jstermSetValueAndComplete(jsterm, value, caretIndexOffset = 0, completionType) {
|
||||
const {inputNode} = jsterm;
|
||||
inputNode.value = value;
|
||||
const index = value.length + caretIndexOffset;
|
||||
inputNode.setSelectionRange(index, index);
|
||||
function jstermSetValueAndComplete(
|
||||
jsterm,
|
||||
value,
|
||||
caretPosition = value.length,
|
||||
completionType
|
||||
) {
|
||||
jsterm.setInputValue(value);
|
||||
|
||||
if (caretPosition < 0) {
|
||||
caretPosition = value.length + caretPosition;
|
||||
}
|
||||
|
||||
if (Number.isInteger(caretPosition)) {
|
||||
if (jsterm.inputNode) {
|
||||
const {inputNode} = jsterm;
|
||||
inputNode.value = value;
|
||||
inputNode.setSelectionRange(caretPosition, caretPosition);
|
||||
}
|
||||
|
||||
if (jsterm.editor) {
|
||||
jsterm.editor.setCursor(jsterm.editor.getPosition(caretPosition));
|
||||
}
|
||||
}
|
||||
|
||||
return jstermComplete(jsterm, completionType);
|
||||
}
|
||||
@ -391,6 +409,120 @@ function jstermComplete(jsterm, completionType = jsterm.COMPLETE_HINT_ONLY) {
|
||||
return updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the jsterm has the expected completion value.
|
||||
*
|
||||
* @param {JsTerm} jsterm
|
||||
* @param {String} expectedValue
|
||||
* @param {String} assertionInfo: Description of the assertion passed to `is`.
|
||||
*/
|
||||
function checkJsTermCompletionValue(jsterm, expectedValue, assertionInfo) {
|
||||
const completionValue = getJsTermCompletionValue(jsterm);
|
||||
if (completionValue === null) {
|
||||
ok(false, "Couldn't retrieve the completion value");
|
||||
}
|
||||
|
||||
info(`Expects "${expectedValue}", is "${completionValue}"`);
|
||||
|
||||
if (jsterm.completeNode) {
|
||||
is(completionValue, expectedValue, assertionInfo);
|
||||
} else {
|
||||
// CodeMirror jsterm doesn't need to add prefix-spaces.
|
||||
is(completionValue, expectedValue.trim(), assertionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the cursor on jsterm is at the expected position.
|
||||
*
|
||||
* @param {JsTerm} jsterm
|
||||
* @param {Integer} expectedCursorIndex
|
||||
* @param {String} assertionInfo: Description of the assertion passed to `is`.
|
||||
*/
|
||||
function checkJsTermCursor(jsterm, expectedCursorIndex, assertionInfo) {
|
||||
if (jsterm.inputNode) {
|
||||
const {selectionStart, selectionEnd} = jsterm.inputNode;
|
||||
is(selectionStart, expectedCursorIndex, assertionInfo);
|
||||
ok(selectionStart === selectionEnd);
|
||||
} else {
|
||||
is(jsterm.editor.getCursor().ch, expectedCursorIndex, assertionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the jsterm value and the cursor position given an expected string containing
|
||||
* a "|" to indicate the expected cursor position.
|
||||
*
|
||||
* @param {JsTerm} jsterm
|
||||
* @param {String} expectedStringWithCursor:
|
||||
* String with a "|" to indicate the expected cursor position.
|
||||
* For example, this is how you assert an empty value with the focus "|",
|
||||
* and this indicates the value should be "test" and the cursor at the
|
||||
* end of the input: "test|".
|
||||
* @param {String} assertionInfo: Description of the assertion passed to `is`.
|
||||
*/
|
||||
function checkJsTermValueAndCursor(jsterm, expectedStringWithCursor, assertionInfo) {
|
||||
info(`Checking jsterm state: \n${expectedStringWithCursor}`);
|
||||
if (!expectedStringWithCursor.includes("|")) {
|
||||
ok(false,
|
||||
`expectedStringWithCursor must contain a "|" char to indicate cursor position`);
|
||||
}
|
||||
|
||||
const inputValue = expectedStringWithCursor.replace("|", "");
|
||||
is(jsterm.getInputValue(), inputValue, "jsterm has expected value");
|
||||
if (jsterm.inputNode) {
|
||||
is(jsterm.inputNode.selectionStart, jsterm.inputNode.selectionEnd);
|
||||
is(jsterm.inputNode.selectionStart, expectedStringWithCursor.indexOf("|"),
|
||||
assertionInfo);
|
||||
} else {
|
||||
const lines = expectedStringWithCursor.split("\n");
|
||||
const lineWithCursor = lines.findIndex(line => line.includes("|"));
|
||||
const {ch, line} = jsterm.editor.getCursor();
|
||||
is(line, lineWithCursor, assertionInfo + " - correct line");
|
||||
is(ch, lines[lineWithCursor].indexOf("|"), assertionInfo + " - correct ch");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the jsterm completion value, whether there's CodeMirror enabled or not.
|
||||
*
|
||||
* @param {JsTerm} jsterm
|
||||
* @returns {String}
|
||||
*/
|
||||
function getJsTermCompletionValue(jsterm) {
|
||||
if (jsterm.completeNode) {
|
||||
return jsterm.completeNode.value;
|
||||
}
|
||||
|
||||
if (jsterm.editor) {
|
||||
return jsterm.editor.getAutoCompletionText();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean indicating if the jsterm is focused, whether there's CodeMirror
|
||||
* enabled or not.
|
||||
*
|
||||
* @param {JsTerm} jsterm
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
function isJstermFocused(jsterm) {
|
||||
const document = jsterm.outputNode.ownerDocument;
|
||||
const documentIsFocused = document.hasFocus();
|
||||
|
||||
if (jsterm.inputNode) {
|
||||
return document.activeElement == jsterm.inputNode && documentIsFocused;
|
||||
}
|
||||
|
||||
if (jsterm.editor) {
|
||||
return documentIsFocused && jsterm.editor.hasFocus();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the JavaScript debugger.
|
||||
*
|
||||
|
@ -18,7 +18,7 @@ hg clone http://hg.mozilla.org/mozilla-central
|
||||
|
||||
## Building and running locally
|
||||
|
||||
Fortunately, the Firefox team has made a very good job of automating the building process with bootstrap scripts and putting [documentation](https://developer.mozilla.org/En/Simple_Firefox_build) together.
|
||||
Fortunately, the Firefox team has made a very good job of automating the building process with bootstrap scripts and putting [documentation](https://developer.mozilla.org/docs/Mozilla/Developer_guide/Build_Instructions/Simple_Firefox_build) together.
|
||||
|
||||
The very first time you are building Firefox, run:
|
||||
|
||||
|
@ -26,7 +26,7 @@ PeriodicWave::PeriodicWave(AudioContext* aContext,
|
||||
, mDisableNormalization(aDisableNormalization)
|
||||
{
|
||||
MOZ_ASSERT(aContext);
|
||||
MOZ_ASSERT(aRealData || aImagData);
|
||||
MOZ_ASSERT((aRealData || aImagData) || aLength == 2);
|
||||
|
||||
// Caller should have checked this and thrown.
|
||||
MOZ_ASSERT(aLength > 0);
|
||||
@ -44,21 +44,29 @@ PeriodicWave::PeriodicWave(AudioContext* aContext,
|
||||
auto data = static_cast<float*>(buffer->Data());
|
||||
mCoefficients.mBuffer = std::move(buffer);
|
||||
|
||||
if (aRealData) {
|
||||
PodCopy(data, aRealData, aLength);
|
||||
} else {
|
||||
if (!aRealData && !aImagData) {
|
||||
PodZero(data, aLength);
|
||||
}
|
||||
mCoefficients.mChannelData.AppendElement(data);
|
||||
|
||||
data += aLength;
|
||||
if (aImagData) {
|
||||
PodCopy(data, aImagData, aLength);
|
||||
mCoefficients.mChannelData.AppendElement(data);
|
||||
data += aLength;
|
||||
data[0] = 0.0f;
|
||||
data[1] = 1.0f;
|
||||
mCoefficients.mChannelData.AppendElement(data);
|
||||
} else {
|
||||
PodZero(data, aLength);
|
||||
}
|
||||
mCoefficients.mChannelData.AppendElement(data);
|
||||
if (aRealData) {
|
||||
PodCopy(data, aRealData, aLength);
|
||||
} else {
|
||||
PodZero(data, aLength);
|
||||
}
|
||||
mCoefficients.mChannelData.AppendElement(data);
|
||||
|
||||
data += aLength;
|
||||
if (aImagData) {
|
||||
PodCopy(data, aImagData, aLength);
|
||||
} else {
|
||||
PodZero(data, aLength);
|
||||
}
|
||||
mCoefficients.mChannelData.AppendElement(data);
|
||||
}
|
||||
mCoefficients.mVolume = 1.0f;
|
||||
mCoefficients.mBufferFormat = AUDIO_FORMAT_FLOAT32;
|
||||
}
|
||||
@ -69,10 +77,6 @@ PeriodicWave::Constructor(const GlobalObject& aGlobal,
|
||||
const PeriodicWaveOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (!aOptions.mReal.WasPassed() && !aOptions.mImag.WasPassed()) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aOptions.mReal.WasPassed() && aOptions.mImag.WasPassed() &&
|
||||
aOptions.mReal.Value().Length() != aOptions.mImag.Value().Length()) {
|
||||
@ -80,8 +84,17 @@ PeriodicWave::Constructor(const GlobalObject& aGlobal,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t length =
|
||||
aOptions.mReal.WasPassed() ? aOptions.mReal.Value().Length() : aOptions.mImag.Value().Length();
|
||||
uint32_t length = 0;
|
||||
if (aOptions.mReal.WasPassed()) {
|
||||
length = aOptions.mReal.Value().Length();
|
||||
} else if (aOptions.mImag.WasPassed()) {
|
||||
length = aOptions.mImag.Value().Length();
|
||||
} else {
|
||||
// If nothing has been passed, this PeriodicWave will be a sine wave: 2
|
||||
// elements for each array, the second imaginary component set to 1.0.
|
||||
length = 2;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
return nullptr;
|
||||
|
@ -41,10 +41,9 @@ addLoadEvent(function() {
|
||||
ac.createPeriodicWave(new Float32Array(4097), new Float32Array(4097));
|
||||
});
|
||||
|
||||
// real || imag
|
||||
expectException(function() {
|
||||
expectNoException(function() {
|
||||
new PeriodicWave(ac, {});
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
});
|
||||
|
||||
// real.size == imag.size
|
||||
expectException(function() {
|
||||
|
@ -20,9 +20,6 @@ dictionary PeriodicWaveOptions : PeriodicWaveConstraints {
|
||||
};
|
||||
|
||||
[Pref="dom.webaudio.enabled",
|
||||
// XXXbz The second arg is not optional in the spec, but that looks
|
||||
// like a spec bug to me. See
|
||||
// <https://github.com/WebAudio/web-audio-api/issues/1116>.
|
||||
Constructor(BaseAudioContext context, optional PeriodicWaveOptions options)]
|
||||
interface PeriodicWave {
|
||||
};
|
||||
|
@ -1233,6 +1233,34 @@ gfxTextRun::GetAdvanceWidth(Range aRange, PropertyProvider *aProvider,
|
||||
return result + GetAdvanceForGlyphs(ligatureRange);
|
||||
}
|
||||
|
||||
gfxFloat
|
||||
gfxTextRun::GetMinAdvanceWidth(Range aRange)
|
||||
{
|
||||
MOZ_ASSERT(aRange.end <= GetLength(), "Substring out of range");
|
||||
|
||||
Range ligatureRange = aRange;
|
||||
ShrinkToLigatureBoundaries(&ligatureRange);
|
||||
|
||||
gfxFloat result = std::max(
|
||||
ComputePartialLigatureWidth(Range(aRange.start, ligatureRange.start),
|
||||
nullptr),
|
||||
ComputePartialLigatureWidth(Range(ligatureRange.end, aRange.end),
|
||||
nullptr));
|
||||
|
||||
// XXX Do we need to take spacing into account? When each grapheme cluster
|
||||
// takes its own line, we shouldn't be adding spacings around them.
|
||||
gfxFloat clusterAdvance = 0;
|
||||
for (uint32_t i = ligatureRange.start; i < ligatureRange.end; ++i) {
|
||||
clusterAdvance += GetAdvanceForGlyph(i);
|
||||
if (i + 1 == ligatureRange.end || IsClusterStart(i + 1)) {
|
||||
result = std::max(result, clusterAdvance);
|
||||
clusterAdvance = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
gfxTextRun::SetLineBreaks(Range aRange,
|
||||
bool aLineBreakBefore, bool aLineBreakAfter,
|
||||
|
@ -326,6 +326,12 @@ public:
|
||||
return GetAdvanceWidth(Range(this), nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the minimum advance width for a substring assuming line
|
||||
* breaking is allowed everywhere.
|
||||
*/
|
||||
gfxFloat GetMinAdvanceWidth(Range aRange);
|
||||
|
||||
/**
|
||||
* Clear all stored line breaks for the given range (both before and after),
|
||||
* and then set the line-break state before aRange.start to aBreakBefore and
|
||||
|
@ -8549,6 +8549,16 @@ nsTextFrame::AddInlineMinISizeForFlow(gfxContext *aRenderingContext,
|
||||
return;
|
||||
}
|
||||
|
||||
// If overflow-wrap is break-word, we can wrap everywhere.
|
||||
if (textStyle->WordCanWrap(this)) {
|
||||
aData->OptionallyBreak();
|
||||
aData->mCurrentLine +=
|
||||
textRun->GetMinAdvanceWidth(Range(start, flowEndInTextRun));
|
||||
aData->mTrailingWhitespace = 0;
|
||||
aData->OptionallyBreak();
|
||||
return;
|
||||
}
|
||||
|
||||
AutoTArray<gfxTextRun::HyphenType, BIG_TEXT_NODE_SIZE> hyphBuffer;
|
||||
if (hyphenating) {
|
||||
if (hyphBuffer.AppendElements(flowEndInTextRun - start, fallible)) {
|
||||
|
@ -2234,6 +2234,7 @@ pref("network.proxy.autoconfig_url.include_path", false);
|
||||
// until we reach interval_max or the PAC file is successfully loaded).
|
||||
pref("network.proxy.autoconfig_retry_interval_min", 5); // 5 seconds
|
||||
pref("network.proxy.autoconfig_retry_interval_max", 300); // 5 minutes
|
||||
pref("network.proxy.enable_wpad_over_dhcp", true);
|
||||
|
||||
// Use the HSTS preload list by default
|
||||
pref("network.stricttransportsecurity.preloadlist", true);
|
||||
|
@ -1043,4 +1043,4 @@ ProxyAutoConfig::MyIPAddress(const JS::CallArgs &aArgs)
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
} // namespace mozilla
|
@ -38,6 +38,7 @@ XPIDL_SOURCES += [
|
||||
'nsIDashboard.idl',
|
||||
'nsIDashboardEventNotifier.idl',
|
||||
'nsIDeprecationWarner.idl',
|
||||
'nsIDHCPClient.idl',
|
||||
'nsIDivertableChannel.idl',
|
||||
'nsIDownloader.idl',
|
||||
'nsIEncodedChannel.idl',
|
||||
|
19
netwerk/base/nsIDHCPClient.idl
Normal file
19
netwerk/base/nsIDHCPClient.idl
Normal file
@ -0,0 +1,19 @@
|
||||
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 "nsISupports.idl"
|
||||
|
||||
/**
|
||||
* This interface allows the proxy code to access the DHCP Options in a platform-specific way
|
||||
*/
|
||||
[scriptable, uuid(aee75dc0-be1a-46b9-9e0c-31a6899c175c)]
|
||||
interface nsIDHCPClient : nsISupports
|
||||
{
|
||||
|
||||
/**
|
||||
* returns the DHCP Option designated by the option parameter
|
||||
*/
|
||||
ACString getOption(in uint8_t option);
|
||||
};
|
@ -5,17 +5,20 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsPACMan.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIAsyncVerifyRedirectCallback.h"
|
||||
#include "nsIAuthPrompt.h"
|
||||
#include "nsIPromptFactory.h"
|
||||
#include "nsIDHCPClient.h"
|
||||
#include "nsIHttpChannel.h"
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsIPrefBranch.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIAsyncVerifyRedirectCallback.h"
|
||||
#include "nsIPromptFactory.h"
|
||||
#include "nsIProtocolProxyService.h"
|
||||
#include "nsISystemProxySettings.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@ -26,6 +29,8 @@ LazyLogModule gProxyLog("proxy");
|
||||
|
||||
#undef LOG
|
||||
#define LOG(args) MOZ_LOG(gProxyLog, LogLevel::Debug, args)
|
||||
#define MOZ_WPAD_URL "http://wpad/wpad.dat"
|
||||
#define MOZ_DHCP_WPAD_OPTION 252
|
||||
|
||||
// The PAC thread does evaluations of both PAC files and
|
||||
// nsISystemProxySettings because they can both block the calling thread and we
|
||||
@ -75,6 +80,26 @@ GetExtraJSContextHeapSize()
|
||||
return extraSize < 0 ? 0 : extraSize;
|
||||
}
|
||||
|
||||
// Read network proxy type from preference
|
||||
// Used to verify that the preference is WPAD in nsPACMan::ConfigureWPAD
|
||||
nsresult
|
||||
GetNetworkProxyTypeFromPref(int32_t* type)
|
||||
{
|
||||
*type = 0;
|
||||
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
|
||||
if (!prefs) {
|
||||
LOG(("Failed to get a preference service object"));
|
||||
return NS_ERROR_FACTORY_NOT_REGISTERED;
|
||||
}
|
||||
nsresult rv = prefs->GetIntPref("network.proxy.type", type);
|
||||
if (!NS_SUCCEEDED(rv)) {
|
||||
LOG(("Failed to retrieve network.proxy.type from prefs"));
|
||||
return rv;
|
||||
}
|
||||
LOG(("network.proxy.type pref retrieved: %d\n", *type));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@ -92,12 +117,12 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void SetPACString(const nsCString &pacString)
|
||||
void SetPACString(const nsACString &pacString)
|
||||
{
|
||||
mPACString = pacString;
|
||||
}
|
||||
|
||||
void SetPACURL(const nsCString &pacURL)
|
||||
void SetPACURL(const nsACString &pacURL)
|
||||
{
|
||||
mPACURL = pacURL;
|
||||
}
|
||||
@ -191,14 +216,43 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<nsPACMan> mPACMan;
|
||||
RefPtr<nsPACMan> mPACMan;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// ConfigureWPADComplete allows the PAC thread to tell the main thread that
|
||||
// the URL for the PAC file has been found
|
||||
class ConfigureWPADComplete final : public Runnable
|
||||
{
|
||||
public:
|
||||
ConfigureWPADComplete(nsPACMan *aPACMan, const nsACString &aPACURISpec)
|
||||
: Runnable("net::ConfigureWPADComplete"),
|
||||
mPACMan(aPACMan),
|
||||
mPACURISpec(aPACURISpec)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
|
||||
mPACMan->AssignPACURISpec(mPACURISpec);
|
||||
mPACMan->ContinueLoadingAfterPACUriKnown();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<nsPACMan> mPACMan;
|
||||
nsCString mPACURISpec;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// ExecutePACThreadAction is used to proxy actions from the main
|
||||
// thread onto the PAC thread. There are 3 options: process the queue,
|
||||
// cancel the queue, and setup the javascript context with a new PAC file
|
||||
// thread onto the PAC thread. There are 4 options: process the queue,
|
||||
// cancel the queue, query DHCP for the PAC option
|
||||
// and setup the javascript context with a new PAC file
|
||||
|
||||
class ExecutePACThreadAction final : public Runnable
|
||||
{
|
||||
@ -211,6 +265,7 @@ public:
|
||||
, mCancelStatus(NS_OK)
|
||||
, mSetupPAC(false)
|
||||
, mExtraHeapSize(0)
|
||||
, mConfigureWPAD(false)
|
||||
{ }
|
||||
|
||||
void CancelQueue (nsresult status)
|
||||
@ -221,7 +276,7 @@ public:
|
||||
|
||||
void SetupPAC (const char *text,
|
||||
uint32_t datalen,
|
||||
nsCString &pacURI,
|
||||
const nsACString &pacURI,
|
||||
uint32_t extraHeapSize)
|
||||
{
|
||||
mSetupPAC = true;
|
||||
@ -230,6 +285,11 @@ public:
|
||||
mExtraHeapSize = extraHeapSize;
|
||||
}
|
||||
|
||||
void ConfigureWPAD()
|
||||
{
|
||||
mConfigureWPAD = true;
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread(), "wrong thread");
|
||||
@ -254,6 +314,15 @@ public:
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mConfigureWPAD) {
|
||||
nsAutoCString spec;
|
||||
mConfigureWPAD = false;
|
||||
mPACMan->ConfigureWPAD(spec);
|
||||
RefPtr<ConfigureWPADComplete> runnable = new ConfigureWPADComplete(mPACMan, spec);
|
||||
mPACMan->Dispatch(runnable.forget());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mPACMan->ProcessPendingQ();
|
||||
return NS_OK;
|
||||
}
|
||||
@ -268,6 +337,7 @@ private:
|
||||
uint32_t mExtraHeapSize;
|
||||
nsCString mSetupPACData;
|
||||
nsCString mSetupPACURI;
|
||||
bool mConfigureWPAD;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -289,7 +359,7 @@ PendingPACQuery::PendingPACQuery(nsPACMan* pacMan,
|
||||
}
|
||||
|
||||
void
|
||||
PendingPACQuery::Complete(nsresult status, const nsCString &pacString)
|
||||
PendingPACQuery::Complete(nsresult status, const nsACString &pacString)
|
||||
{
|
||||
if (!mCallback)
|
||||
return;
|
||||
@ -302,7 +372,7 @@ PendingPACQuery::Complete(nsresult status, const nsCString &pacString)
|
||||
}
|
||||
|
||||
void
|
||||
PendingPACQuery::UseAlternatePACFile(const nsCString &pacURL)
|
||||
PendingPACQuery::UseAlternatePACFile(const nsACString &pacURL)
|
||||
{
|
||||
if (!mCallback)
|
||||
return;
|
||||
@ -337,6 +407,9 @@ nsPACMan::nsPACMan(nsIEventTarget *mainThreadEventTarget)
|
||||
, mShutdown(false)
|
||||
, mLoadFailureCount(0)
|
||||
, mInProgress(false)
|
||||
, mAutoDetect(false)
|
||||
, mWPADOverDHCPEnabled(false)
|
||||
, mProxyConfigType(0)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "pacman must be created on main thread");
|
||||
if (!sThreadLocalSetup){
|
||||
@ -354,6 +427,7 @@ nsPACMan::~nsPACMan()
|
||||
if (mPACThread) {
|
||||
if (NS_IsMainThread()) {
|
||||
mPACThread->Shutdown();
|
||||
mPACThread = nullptr;
|
||||
}
|
||||
else {
|
||||
RefPtr<ShutdownThread> runnable = new ShutdownThread(mPACThread);
|
||||
@ -396,7 +470,7 @@ nsPACMan::AsyncGetProxyForURI(nsIURI *uri,
|
||||
TimeStamp::Now() > mScheduledReload) {
|
||||
LOG(("nsPACMan::AsyncGetProxyForURI reload as scheduled\n"));
|
||||
|
||||
LoadPACFromURI(EmptyCString());
|
||||
LoadPACFromURI(mAutoDetect? EmptyCString(): mPACURISpec);
|
||||
}
|
||||
|
||||
RefPtr<PendingPACQuery> query =
|
||||
@ -429,16 +503,15 @@ nsPACMan::PostQuery(PendingPACQuery *query)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsPACMan::LoadPACFromURI(const nsCString &spec)
|
||||
nsPACMan::LoadPACFromURI(const nsACString &aSpec)
|
||||
{
|
||||
NS_ENSURE_STATE(!mShutdown);
|
||||
NS_ENSURE_ARG(!spec.IsEmpty() || !mPACURISpec.IsEmpty());
|
||||
|
||||
nsCOMPtr<nsIStreamLoader> loader =
|
||||
do_CreateInstance(NS_STREAMLOADER_CONTRACTID);
|
||||
NS_ENSURE_STATE(loader);
|
||||
|
||||
LOG(("nsPACMan::LoadPACFromURI %s\n", spec.get()));
|
||||
LOG(("nsPACMan::LoadPACFromURI aSpec: %s\n", aSpec.BeginReading()));
|
||||
// Since we might get called from nsProtocolProxyService::Init, we need to
|
||||
// post an event back to the main thread before we try to use the IO service.
|
||||
//
|
||||
@ -459,18 +532,67 @@ nsPACMan::LoadPACFromURI(const nsCString &spec)
|
||||
CancelExistingLoad();
|
||||
|
||||
mLoader = loader;
|
||||
if (!spec.IsEmpty()) {
|
||||
mPACURISpec = spec;
|
||||
mPACURIRedirectSpec.Truncate();
|
||||
mNormalPACURISpec.Truncate(); // set at load time
|
||||
mLoadFailureCount = 0; // reset
|
||||
}
|
||||
mPACURIRedirectSpec.Truncate();
|
||||
mNormalPACURISpec.Truncate(); // set at load time
|
||||
mLoadFailureCount = 0; // reset
|
||||
mAutoDetect = aSpec.IsEmpty();
|
||||
mPACURISpec.Assign(aSpec);
|
||||
|
||||
// reset to Null
|
||||
mScheduledReload = TimeStamp();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsPACMan::GetPACFromDHCP(nsACString &aSpec)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread(), "wrong thread");
|
||||
if (!mDHCPClient) {
|
||||
LOG(("nsPACMan::GetPACFromDHCP DHCP option %d query failed because there is no DHCP client available\n", MOZ_DHCP_WPAD_OPTION));
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
nsresult rv;
|
||||
rv = mDHCPClient->GetOption(MOZ_DHCP_WPAD_OPTION, aSpec);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("nsPACMan::GetPACFromDHCP DHCP option %d query failed with result %d\n", MOZ_DHCP_WPAD_OPTION, (uint32_t)rv));
|
||||
} else {
|
||||
LOG(("nsPACMan::GetPACFromDHCP DHCP option %d query succeeded, finding PAC URL %s\n", MOZ_DHCP_WPAD_OPTION, aSpec.BeginReading()));
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsPACMan::ConfigureWPAD(nsACString &aSpec)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread(), "wrong thread");
|
||||
|
||||
MOZ_RELEASE_ASSERT(mProxyConfigType == nsIProtocolProxyService::PROXYCONFIG_WPAD,
|
||||
"WPAD is being executed when not selected by user");
|
||||
|
||||
aSpec.Truncate();
|
||||
if (mWPADOverDHCPEnabled) {
|
||||
GetPACFromDHCP(aSpec);
|
||||
}
|
||||
|
||||
if (aSpec.IsEmpty()) {
|
||||
// We diverge from the WPAD spec here in that we don't walk the
|
||||
// hosts's FQDN, stripping components until we hit a TLD. Doing so
|
||||
// is dangerous in the face of an incomplete list of TLDs, and TLDs
|
||||
// get added over time. We could consider doing only a single
|
||||
// substitution of the first component, if that proves to help
|
||||
// compatibility.
|
||||
aSpec.AssignLiteral(MOZ_WPAD_URL);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsPACMan::AssignPACURISpec(const nsACString &aSpec)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
|
||||
mPACURISpec.Assign(aSpec);
|
||||
}
|
||||
|
||||
void
|
||||
nsPACMan::StartLoading()
|
||||
{
|
||||
@ -483,6 +605,29 @@ nsPACMan::StartLoading()
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAutoDetect) {
|
||||
GetNetworkProxyTypeFromPref(&mProxyConfigType);
|
||||
RefPtr<ExecutePACThreadAction> wpadConfigurer =
|
||||
new ExecutePACThreadAction(this);
|
||||
wpadConfigurer->ConfigureWPAD();
|
||||
if (mPACThread) {
|
||||
mPACThread->Dispatch(wpadConfigurer, nsIEventTarget::DISPATCH_NORMAL);
|
||||
}
|
||||
} else {
|
||||
ContinueLoadingAfterPACUriKnown();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsPACMan::ContinueLoadingAfterPACUriKnown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
|
||||
|
||||
// CancelExistingLoad was called...
|
||||
if (!mLoader) {
|
||||
PostCancelPendingQ(NS_ERROR_ABORT);
|
||||
return;
|
||||
}
|
||||
if (NS_SUCCEEDED(mLoader->Init(this, nullptr))) {
|
||||
// Always hit the origin server when loading PAC.
|
||||
nsCOMPtr<nsIIOService> ios = do_GetIOService();
|
||||
@ -800,6 +945,7 @@ nsresult
|
||||
nsPACMan::Init(nsISystemProxySettings *systemProxySettings)
|
||||
{
|
||||
mSystemProxySettings = systemProxySettings;
|
||||
mDHCPClient = do_GetService(NS_DHCPCLIENT_CONTRACTID);
|
||||
|
||||
nsresult rv =
|
||||
NS_NewNamedThread("ProxyResolution", getter_AddRefs(mPACThread));
|
||||
@ -808,4 +954,4 @@ nsPACMan::Init(nsISystemProxySettings *systemProxySettings)
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
} // namespace mozilla
|
@ -7,23 +7,24 @@
|
||||
#ifndef nsPACMan_h__
|
||||
#define nsPACMan_h__
|
||||
|
||||
#include "nsIStreamLoader.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIChannelEventSink.h"
|
||||
#include "ProxyAutoConfig.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/net/NeckoTargetHolder.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIChannelEventSink.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIStreamLoader.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsString.h"
|
||||
#include "ProxyAutoConfig.h"
|
||||
|
||||
class nsISystemProxySettings;
|
||||
class nsIDHCPClient;
|
||||
class nsIThread;
|
||||
|
||||
namespace mozilla {
|
||||
@ -52,8 +53,8 @@ public:
|
||||
* newPACURL should be 0 length.
|
||||
*/
|
||||
virtual void OnQueryComplete(nsresult status,
|
||||
const nsCString &pacString,
|
||||
const nsCString &newPACURL) = 0;
|
||||
const nsACString &pacString,
|
||||
const nsACString &newPACURL) = 0;
|
||||
};
|
||||
|
||||
class PendingPACQuery final : public Runnable,
|
||||
@ -65,8 +66,8 @@ public:
|
||||
bool mainThreadResponse);
|
||||
|
||||
// can be called from either thread
|
||||
void Complete(nsresult status, const nsCString &pacString);
|
||||
void UseAlternatePACFile(const nsCString &pacURL);
|
||||
void Complete(nsresult status, const nsACString &pacString);
|
||||
void UseAlternatePACFile(const nsACString &pacURL);
|
||||
|
||||
nsCString mSpec;
|
||||
nsCString mScheme;
|
||||
@ -127,11 +128,11 @@ public:
|
||||
* the PAC file, any asynchronous PAC queries will be queued up to be
|
||||
* processed once the PAC file finishes loading.
|
||||
*
|
||||
* @param pacSpec
|
||||
* @param aSpec
|
||||
* The non normalized uri spec of this URI used for comparison with
|
||||
* system proxy settings to determine if the PAC uri has changed.
|
||||
*/
|
||||
nsresult LoadPACFromURI(const nsCString &pacSpec);
|
||||
nsresult LoadPACFromURI(const nsACString &aSpec);
|
||||
|
||||
/**
|
||||
* Returns true if we are currently loading the PAC file.
|
||||
@ -166,6 +167,10 @@ public:
|
||||
return IsPACURI(tmp);
|
||||
}
|
||||
|
||||
bool IsUsingWPAD() {
|
||||
return mAutoDetect;
|
||||
}
|
||||
|
||||
nsresult Init(nsISystemProxySettings *);
|
||||
static nsPACMan *sInstance;
|
||||
|
||||
@ -173,6 +178,8 @@ public:
|
||||
void ProcessPendingQ();
|
||||
void CancelPendingQ(nsresult);
|
||||
|
||||
void SetWPADOverDHCPEnabled(bool aValue) { mWPADOverDHCPEnabled = aValue; }
|
||||
|
||||
private:
|
||||
NS_DECL_NSISTREAMLOADEROBSERVER
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
@ -180,8 +187,10 @@ private:
|
||||
|
||||
friend class PendingPACQuery;
|
||||
friend class PACLoadComplete;
|
||||
friend class ConfigureWPADComplete;
|
||||
friend class ExecutePACThreadAction;
|
||||
friend class WaitForThreadShutdown;
|
||||
friend class TestPACMan;
|
||||
|
||||
~nsPACMan();
|
||||
|
||||
@ -195,6 +204,11 @@ private:
|
||||
*/
|
||||
void StartLoading();
|
||||
|
||||
/**
|
||||
* Continue loading the PAC file.
|
||||
*/
|
||||
void ContinueLoadingAfterPACUriKnown();
|
||||
|
||||
/**
|
||||
* Reload the PAC file if there is reason to.
|
||||
*/
|
||||
@ -212,15 +226,22 @@ private:
|
||||
*/
|
||||
nsresult PostQuery(PendingPACQuery *query);
|
||||
|
||||
// Having found the PAC URI on the PAC thread, copy it to a string which
|
||||
// can be altered on the main thread.
|
||||
void AssignPACURISpec(const nsACString &aSpec);
|
||||
|
||||
// PAC thread operations only
|
||||
void PostProcessPendingQ();
|
||||
void PostCancelPendingQ(nsresult);
|
||||
bool ProcessPending();
|
||||
nsresult GetPACFromDHCP(nsACString &aSpec);
|
||||
nsresult ConfigureWPAD(nsACString &aSpec);
|
||||
|
||||
private:
|
||||
ProxyAutoConfig mPAC;
|
||||
nsCOMPtr<nsIThread> mPACThread;
|
||||
nsCOMPtr<nsISystemProxySettings> mSystemProxySettings;
|
||||
nsCOMPtr<nsIDHCPClient> mDHCPClient;
|
||||
|
||||
LinkedList<PendingPACQuery> mPendingQ; /* pac thread only */
|
||||
|
||||
@ -239,6 +260,9 @@ private:
|
||||
|
||||
bool mInProgress;
|
||||
bool mIncludePath;
|
||||
bool mAutoDetect;
|
||||
bool mWPADOverDHCPEnabled;
|
||||
int32_t mProxyConfigType;
|
||||
};
|
||||
|
||||
extern LazyLogModule gProxyLog;
|
||||
@ -246,4 +270,4 @@ extern LazyLogModule gProxyLog;
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // nsPACMan_h__
|
||||
#endif // nsPACMan_h__
|
@ -62,7 +62,6 @@ namespace net {
|
||||
#define PROXY_PREF_BRANCH "network.proxy"
|
||||
#define PROXY_PREF(x) PROXY_PREF_BRANCH "." x
|
||||
|
||||
#define WPAD_URL "http://wpad/wpad.dat"
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
@ -311,8 +310,8 @@ private:
|
||||
// Called asynchronously, so we do not need to post another PLEvent
|
||||
// before calling DoCallback.
|
||||
void OnQueryComplete(nsresult status,
|
||||
const nsCString &pacString,
|
||||
const nsCString &newPACURL) override
|
||||
const nsACString &pacString,
|
||||
const nsACString &newPACURL) override
|
||||
{
|
||||
// If we've already called DoCallback then, nothing more to do.
|
||||
if (!mCallback)
|
||||
@ -821,6 +820,7 @@ nsProtocolProxyService::nsProtocolProxyService()
|
||||
, mSOCKSProxyVersion(4)
|
||||
, mSOCKSProxyRemoteDNS(false)
|
||||
, mProxyOverTLS(true)
|
||||
, mWPADOverDHCPEnabled(false)
|
||||
, mPACMan(nullptr)
|
||||
, mSessionStart(PR_Now())
|
||||
, mFailedProxyTimeout(30 * 60) // 30 minute default
|
||||
@ -1087,6 +1087,12 @@ nsProtocolProxyService::PrefsChanged(nsIPrefBranch *prefBranch,
|
||||
mProxyOverTLS);
|
||||
}
|
||||
|
||||
if (!pref || !strcmp(pref, PROXY_PREF("enable_wpad_over_dhcp"))) {
|
||||
proxy_GetBoolPref(prefBranch, PROXY_PREF("enable_wpad_over_dhcp"),
|
||||
mWPADOverDHCPEnabled);
|
||||
reloadPAC = reloadPAC || mProxyConfig == PROXYCONFIG_WPAD;
|
||||
}
|
||||
|
||||
if (!pref || !strcmp(pref, PROXY_PREF("failover_timeout")))
|
||||
proxy_GetIntPref(prefBranch, PROXY_PREF("failover_timeout"),
|
||||
mFailedProxyTimeout);
|
||||
@ -1119,19 +1125,15 @@ nsProtocolProxyService::PrefsChanged(nsIPrefBranch *prefBranch,
|
||||
ResetPACThread();
|
||||
}
|
||||
} else if (mProxyConfig == PROXYCONFIG_WPAD) {
|
||||
// We diverge from the WPAD spec here in that we don't walk the
|
||||
// hosts's FQDN, stripping components until we hit a TLD. Doing so
|
||||
// is dangerous in the face of an incomplete list of TLDs, and TLDs
|
||||
// get added over time. We could consider doing only a single
|
||||
// substitution of the first component, if that proves to help
|
||||
// compatibility.
|
||||
tempString.AssignLiteral(WPAD_URL);
|
||||
LOG(("Auto-detecting proxy - Reset Pac Thread"));
|
||||
ResetPACThread();
|
||||
} else if (mSystemProxySettings) {
|
||||
// Get System Proxy settings if available
|
||||
AsyncConfigureFromPAC(false, false);
|
||||
}
|
||||
if (!tempString.IsEmpty())
|
||||
if (!tempString.IsEmpty() || mProxyConfig == PROXYCONFIG_WPAD) {
|
||||
ConfigureFromPAC(tempString, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1482,7 +1484,6 @@ nsProtocolProxyService::SetupPACThread(nsIEventTarget *mainThreadEventTarget)
|
||||
else {
|
||||
rv = mPACMan->Init(nullptr);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
mPACMan->Shutdown();
|
||||
mPACMan = nullptr;
|
||||
@ -1508,11 +1509,15 @@ nsProtocolProxyService::ConfigureFromPAC(const nsCString &spec,
|
||||
nsresult rv = SetupPACThread();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (mPACMan->IsPACURI(spec) && !forceReload)
|
||||
bool autodetect = spec.IsEmpty();
|
||||
if (!forceReload && ((!autodetect && mPACMan->IsPACURI(spec)) ||
|
||||
(autodetect && mPACMan->IsUsingWPAD()))) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mFailedProxies.Clear();
|
||||
|
||||
mPACMan->SetWPADOverDHCPEnabled(mWPADOverDHCPEnabled);
|
||||
return mPACMan->LoadPACFromURI(spec);
|
||||
}
|
||||
|
||||
@ -1565,8 +1570,6 @@ nsProtocolProxyService::ReloadPAC()
|
||||
nsAutoCString pacSpec;
|
||||
if (type == PROXYCONFIG_PAC)
|
||||
prefs->GetCharPref(PROXY_PREF("autoconfig_url"), pacSpec);
|
||||
else if (type == PROXYCONFIG_WPAD)
|
||||
pacSpec.AssignLiteral(WPAD_URL);
|
||||
else if (type == PROXYCONFIG_SYSTEM) {
|
||||
if (mSystemProxySettings) {
|
||||
AsyncConfigureFromPAC(true, true);
|
||||
@ -1575,7 +1578,7 @@ nsProtocolProxyService::ReloadPAC()
|
||||
}
|
||||
}
|
||||
|
||||
if (!pacSpec.IsEmpty())
|
||||
if (!pacSpec.IsEmpty() || type == PROXYCONFIG_WPAD)
|
||||
ConfigureFromPAC(pacSpec, true);
|
||||
return NS_OK;
|
||||
}
|
||||
@ -1599,8 +1602,8 @@ class nsAsyncBridgeRequest final : public nsPACManCallback
|
||||
}
|
||||
|
||||
void OnQueryComplete(nsresult status,
|
||||
const nsCString &pacString,
|
||||
const nsCString &newPACURL) override
|
||||
const nsACString &pacString,
|
||||
const nsACString &newPACURL) override
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mCompleted = true;
|
||||
@ -2546,4 +2549,4 @@ nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo &info,
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
} // namespace mozilla
|
@ -400,6 +400,7 @@ protected:
|
||||
int32_t mSOCKSProxyVersion;
|
||||
bool mSOCKSProxyRemoteDNS;
|
||||
bool mProxyOverTLS;
|
||||
bool mWPADOverDHCPEnabled;
|
||||
|
||||
RefPtr<nsPACMan> mPACMan; // non-null if we are using PAC
|
||||
nsCOMPtr<nsISystemProxySettings> mSystemProxySettings;
|
||||
@ -423,4 +424,4 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsProtocolProxyService, NS_PROTOCOL_PROXY_SERVICE_
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // !nsProtocolProxyService_h__
|
||||
#endif // !nsProtocolProxyService_h__
|
@ -335,6 +335,10 @@
|
||||
#define NS_SYSTEMPROXYSETTINGS_CONTRACTID \
|
||||
"@mozilla.org/system-proxy-settings;1"
|
||||
|
||||
// component implementing nsIDHCPClient.
|
||||
#define NS_DHCPCLIENT_CONTRACTID \
|
||||
"@mozilla.org/dhcp-client;1"
|
||||
|
||||
// service implementing nsIStreamTransportService
|
||||
#define NS_STREAMTRANSPORTSERVICE_CONTRACTID \
|
||||
"@mozilla.org/network/stream-transport-service;1"
|
||||
|
282
netwerk/test/gtest/TestPACMan.cpp
Normal file
282
netwerk/test/gtest/TestPACMan.cpp
Normal file
@ -0,0 +1,282 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "../../../xpcom/threads/nsThreadManager.h"
|
||||
#include "nsIDHCPClient.h"
|
||||
#include "nsIPrefBranch.h"
|
||||
#include "nsComponentManager.h"
|
||||
#include "mozilla/ModuleUtils.h"
|
||||
#include "../../base/nsPACMan.h"
|
||||
|
||||
|
||||
#define TEST_WPAD_DHCP_OPTION "http://pac/pac.dat"
|
||||
#define TEST_ASSIGNED_PAC_URL "http://assignedpac/pac.dat"
|
||||
#define WPAD_PREF 4
|
||||
#define NETWORK_PROXY_TYPE_PREF_NAME "network.proxy.type"
|
||||
#define GETTING_NETWORK_PROXY_TYPE_FAILED -1
|
||||
|
||||
nsCString WPADOptionResult;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
nsresult
|
||||
SetNetworkProxyType(int32_t pref)
|
||||
{
|
||||
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
|
||||
if (!prefs) {
|
||||
return NS_ERROR_FACTORY_NOT_REGISTERED;
|
||||
}
|
||||
return prefs->SetIntPref(NETWORK_PROXY_TYPE_PREF_NAME, pref);
|
||||
}
|
||||
|
||||
nsresult
|
||||
GetNetworkProxyType(int32_t* pref)
|
||||
{
|
||||
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
|
||||
if (!prefs) {
|
||||
return NS_ERROR_FACTORY_NOT_REGISTERED;
|
||||
}
|
||||
return prefs->GetIntPref(NETWORK_PROXY_TYPE_PREF_NAME, pref);
|
||||
}
|
||||
|
||||
class nsTestDHCPClient final : public nsIDHCPClient
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIDHCPCLIENT
|
||||
|
||||
nsTestDHCPClient() {};
|
||||
nsresult Init(){
|
||||
return NS_OK;
|
||||
};
|
||||
|
||||
private:
|
||||
~nsTestDHCPClient() {};
|
||||
};
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTestDHCPClient::GetOption(uint8_t option, nsACString & _retval)
|
||||
{
|
||||
_retval.Assign(WPADOptionResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsTestDHCPClient, nsIDHCPClient)
|
||||
|
||||
#define NS_TESTDHCPCLIENTSERVICE_CID /* {FEBF1D69-4D7D-4891-9524-045AD18B5592} */\
|
||||
{ 0xFEBF1D69, 0x4D7D, 0x4891, \
|
||||
{0x95, 0x24, 0x04, 0x5a, 0xd1, 0x8b, 0x55, 0x92 } }
|
||||
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsTestDHCPClient, Init)
|
||||
NS_DEFINE_NAMED_CID(NS_TESTDHCPCLIENTSERVICE_CID);
|
||||
|
||||
static const mozilla::Module::CIDEntry kSysDHCPClientCIDs[] = {
|
||||
{ &kNS_TESTDHCPCLIENTSERVICE_CID, false, nullptr, nsTestDHCPClientConstructor },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module::ContractIDEntry kSysDHCPClientContracts[] = {
|
||||
{ NS_DHCPCLIENT_CONTRACTID, &kNS_TESTDHCPCLIENTSERVICE_CID },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module kSysDHCPClientModule = {
|
||||
mozilla::Module::kVersion,
|
||||
kSysDHCPClientCIDs,
|
||||
kSysDHCPClientContracts
|
||||
};
|
||||
|
||||
NSMODULE_DEFN(nsDHCPClientModule) = &kSysDHCPClientModule;
|
||||
|
||||
void
|
||||
SetOptionResult(const char* result)
|
||||
{
|
||||
WPADOptionResult.Assign(result);
|
||||
}
|
||||
|
||||
class ProcessPendingEventsAction final : public Runnable
|
||||
{
|
||||
public:
|
||||
ProcessPendingEventsAction() : Runnable("net::ProcessPendingEventsAction") { }
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
if (NS_HasPendingEvents(nullptr)) {
|
||||
NS_WARNING("Found pending requests on PAC thread");
|
||||
nsresult rv;
|
||||
rv = NS_ProcessPendingEvents(nullptr);
|
||||
EXPECT_EQ(NS_OK, rv);
|
||||
}
|
||||
NS_WARNING("No pending requests on PAC thread");
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
class TestPACMan : public ::testing::Test {
|
||||
protected:
|
||||
|
||||
RefPtr<nsPACMan> mPACMan;
|
||||
|
||||
void
|
||||
ProcessAllEvents()
|
||||
{
|
||||
ProcessPendingEventsOnPACThread();
|
||||
nsresult rv;
|
||||
while (NS_HasPendingEvents(nullptr)) {
|
||||
NS_WARNING("Pending events on main thread");
|
||||
rv = NS_ProcessPendingEvents(nullptr);
|
||||
ASSERT_EQ(NS_OK, rv);
|
||||
ProcessPendingEventsOnPACThread();
|
||||
}
|
||||
NS_WARNING("End of pending events on main thread");
|
||||
}
|
||||
|
||||
|
||||
// This method is used to ensure that all pending events on the main thread
|
||||
// and the Proxy thread are processsed.
|
||||
// It iterates over ProcessAllEvents because simply calling ProcessAllEvents once
|
||||
// did not reliably process the events on both threads on all platforms.
|
||||
void
|
||||
ProcessAllEventsTenTimes(){
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ProcessAllEvents();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void
|
||||
SetUp()
|
||||
{
|
||||
ASSERT_EQ(NS_OK, GetNetworkProxyType(&originalNetworkProxyTypePref));
|
||||
nsFactoryEntry* factoryEntry = nsComponentManagerImpl::gComponentManager
|
||||
->GetFactoryEntry(kNS_TESTDHCPCLIENTSERVICE_CID);
|
||||
if (factoryEntry) {
|
||||
nsresult rv = nsComponentManagerImpl::gComponentManager->UnregisterFactory(kNS_TESTDHCPCLIENTSERVICE_CID, factoryEntry->mFactory);
|
||||
ASSERT_EQ(NS_OK, rv);
|
||||
}
|
||||
nsComponentManagerImpl::gComponentManager->RegisterModule(&kSysDHCPClientModule, nullptr);
|
||||
|
||||
mPACMan = new nsPACMan(nullptr);
|
||||
mPACMan->SetWPADOverDHCPEnabled(true);
|
||||
mPACMan->Init(nullptr);
|
||||
ASSERT_EQ(NS_OK, SetNetworkProxyType(WPAD_PREF));
|
||||
|
||||
}
|
||||
|
||||
virtual void
|
||||
TearDown()
|
||||
{
|
||||
|
||||
mPACMan->Shutdown();
|
||||
if (originalNetworkProxyTypePref != GETTING_NETWORK_PROXY_TYPE_FAILED) {
|
||||
ASSERT_EQ(NS_OK, SetNetworkProxyType(originalNetworkProxyTypePref));
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDHCPClient>
|
||||
GetPACManDHCPCient()
|
||||
{
|
||||
return mPACMan->mDHCPClient;
|
||||
}
|
||||
|
||||
void
|
||||
SetPACManDHCPCient(nsCOMPtr<nsIDHCPClient> aValue)
|
||||
{
|
||||
mPACMan->mDHCPClient = aValue;
|
||||
}
|
||||
|
||||
void
|
||||
AssertPACSpecEqualTo(const char* aExpected)
|
||||
{
|
||||
ASSERT_STREQ(aExpected, mPACMan->mPACURISpec.Data());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
int32_t originalNetworkProxyTypePref = GETTING_NETWORK_PROXY_TYPE_FAILED;
|
||||
|
||||
void ProcessPendingEventsOnPACThread(){
|
||||
RefPtr<ProcessPendingEventsAction> action =
|
||||
new ProcessPendingEventsAction();
|
||||
|
||||
mPACMan->mPACThread->Dispatch(action, nsIEventTarget::DISPATCH_SYNC);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestPACMan, TestCreateDHCPClientAndGetOption) {
|
||||
SetOptionResult(TEST_WPAD_DHCP_OPTION);
|
||||
nsCString spec;
|
||||
|
||||
GetPACManDHCPCient()->GetOption(252, spec);
|
||||
|
||||
ASSERT_STREQ(TEST_WPAD_DHCP_OPTION, spec.Data());
|
||||
}
|
||||
|
||||
TEST_F(TestPACMan, TestCreateDHCPClientAndGetEmptyOption) {
|
||||
SetOptionResult("");
|
||||
nsCString spec;
|
||||
spec.AssignLiteral(TEST_ASSIGNED_PAC_URL);
|
||||
|
||||
GetPACManDHCPCient()->GetOption(252, spec);
|
||||
|
||||
ASSERT_TRUE(spec.IsEmpty());
|
||||
}
|
||||
|
||||
TEST_F(TestPACMan, WhenTheDHCPClientExistsAndDHCPIsNonEmptyDHCPOptionIsUsedAsPACUri) {
|
||||
SetOptionResult(TEST_WPAD_DHCP_OPTION);
|
||||
|
||||
mPACMan->LoadPACFromURI(EmptyCString());
|
||||
ProcessAllEventsTenTimes();
|
||||
|
||||
ASSERT_STREQ(TEST_WPAD_DHCP_OPTION, WPADOptionResult.Data());
|
||||
AssertPACSpecEqualTo(TEST_WPAD_DHCP_OPTION);
|
||||
}
|
||||
|
||||
TEST_F(TestPACMan, WhenTheDHCPResponseIsEmptyWPADDefaultsToStandardURL) {
|
||||
SetOptionResult(EmptyCString().Data());
|
||||
|
||||
mPACMan->LoadPACFromURI(EmptyCString());
|
||||
ASSERT_TRUE(NS_HasPendingEvents(nullptr));
|
||||
ProcessAllEventsTenTimes();
|
||||
|
||||
ASSERT_STREQ("", WPADOptionResult.Data());
|
||||
AssertPACSpecEqualTo("http://wpad/wpad.dat");
|
||||
}
|
||||
|
||||
TEST_F(TestPACMan, WhenThereIsNoDHCPClientWPADDefaultsToStandardURL) {
|
||||
SetOptionResult(TEST_WPAD_DHCP_OPTION);
|
||||
SetPACManDHCPCient(nullptr);
|
||||
|
||||
mPACMan->LoadPACFromURI(EmptyCString());
|
||||
ProcessAllEventsTenTimes();
|
||||
|
||||
ASSERT_STREQ(TEST_WPAD_DHCP_OPTION, WPADOptionResult.Data());
|
||||
AssertPACSpecEqualTo("http://wpad/wpad.dat");
|
||||
}
|
||||
|
||||
TEST_F(TestPACMan, WhenWPADOverDHCPIsPreffedOffWPADDefaultsToStandardURL) {
|
||||
SetOptionResult(TEST_WPAD_DHCP_OPTION);
|
||||
mPACMan->SetWPADOverDHCPEnabled(false);
|
||||
|
||||
mPACMan->LoadPACFromURI(EmptyCString());
|
||||
ProcessAllEventsTenTimes();
|
||||
|
||||
ASSERT_STREQ(TEST_WPAD_DHCP_OPTION, WPADOptionResult.Data());
|
||||
AssertPACSpecEqualTo("http://wpad/wpad.dat");
|
||||
}
|
||||
|
||||
TEST_F(TestPACMan, WhenPACUriIsSetDirectlyItIsUsedRatherThanWPAD) {
|
||||
SetOptionResult(TEST_WPAD_DHCP_OPTION);
|
||||
nsCString spec;
|
||||
spec.AssignLiteral(TEST_ASSIGNED_PAC_URL);
|
||||
|
||||
mPACMan->LoadPACFromURI(spec);
|
||||
ProcessAllEventsTenTimes();
|
||||
|
||||
AssertPACSpecEqualTo(TEST_ASSIGNED_PAC_URL);
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
@ -10,6 +10,7 @@ UNIFIED_SOURCES += [
|
||||
'TestHttpAuthUtils.cpp',
|
||||
'TestMIMEInputStream.cpp',
|
||||
'TestMozURL.cpp',
|
||||
'TestPACMan.cpp',
|
||||
'TestPartiallySeekableInputStream.cpp',
|
||||
'TestProtocolProxyService.cpp',
|
||||
'TestReadStreamToString.cpp',
|
||||
@ -30,3 +31,8 @@ LOCAL_INCLUDES += [
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul-gtest'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'!/xpcom',
|
||||
'/xpcom/components'
|
||||
]
|
@ -430,8 +430,15 @@ class GitRepository(Repository):
|
||||
|
||||
def working_directory_clean(self, untracked=False, ignored=False):
|
||||
args = ['status', '--porcelain']
|
||||
if not untracked:
|
||||
|
||||
# Even in --porcelain mode, behavior is affected by the
|
||||
# ``status.showUntrackedFiles`` option, which means we need to be
|
||||
# explicit about how to treat untracked files.
|
||||
if untracked:
|
||||
args.append('--untracked-files=all')
|
||||
else:
|
||||
args.append('--untracked-files=no')
|
||||
|
||||
if ignored:
|
||||
args.append('--ignored')
|
||||
|
||||
|
@ -31,6 +31,7 @@ class MachCommands(MachCommandBase):
|
||||
@Command('python-safety', category='testing',
|
||||
description='Run python requirements safety checks')
|
||||
@CommandArgument('--python',
|
||||
default='2.7',
|
||||
help='Version of Python for Pipenv to use. When given a '
|
||||
'Python version, Pipenv will automatically scan your '
|
||||
'system for a Python that matches that given version.')
|
||||
@ -38,9 +39,7 @@ class MachCommands(MachCommandBase):
|
||||
self.logger = commandline.setup_logging(
|
||||
"python-safety", {"raw": sys.stdout})
|
||||
|
||||
python = python or self.virtualenv_manager.python_path
|
||||
self.activate_pipenv(pipfile=os.path.join(here, 'Pipfile'), args=[
|
||||
'--python', python], populate=True)
|
||||
self.activate_pipenv(pipfile=os.path.join(here, 'Pipfile'), python=python, populate=True)
|
||||
|
||||
pattern = '**/*requirements*.txt'
|
||||
path = mozpath.normsep(os.path.dirname(os.path.dirname(here)))
|
||||
|
@ -1165,4 +1165,4 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
|
||||
|
||||
static const int32_t kUnknownId = -1;
|
||||
|
||||
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1539861752318000);
|
||||
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1540232248625000);
|
||||
|
@ -8,7 +8,7 @@
|
||||
/*****************************************************************************/
|
||||
|
||||
#include <stdint.h>
|
||||
const PRTime gPreloadListExpirationTime = INT64_C(1542280764880000);
|
||||
const PRTime gPreloadListExpirationTime = INT64_C(1542651331206000);
|
||||
%%
|
||||
0-1.party, 1
|
||||
0.me.uk, 1
|
||||
@ -2807,6 +2807,8 @@ appmobile.io, 1
|
||||
appninjas.com, 1
|
||||
apponic.com, 1
|
||||
apponline.com, 1
|
||||
apprenticeship.gov, 1
|
||||
apprenticeships.gov, 1
|
||||
approbo.com, 1
|
||||
approvedtreecare.com, 1
|
||||
apps.co, 1
|
||||
|
@ -644,6 +644,9 @@ static const char flashPluginSandboxRules[] = R"SANDBOX_LITERAL(
|
||||
(global-name "com.apple.inputmethodkit.getxpcendpoint")
|
||||
(global-name "com.apple.decalog4.incoming")
|
||||
(global-name "com.apple.windowserver.active"))
|
||||
; bug 1475707
|
||||
(if (= macosMinorVersion 9)
|
||||
(allow mach-lookup (global-name "com.apple.xpcd")))
|
||||
|
||||
; Fonts
|
||||
(allow file-read*
|
||||
|
File diff suppressed because one or more lines are too long
@ -13,7 +13,6 @@ transforms:
|
||||
- taskgraph.transforms.build_attrs:transforms
|
||||
- taskgraph.transforms.build_lints:transforms
|
||||
- taskgraph.transforms.use_toolchains:transforms
|
||||
- taskgraph.transforms.use_fetches:transforms
|
||||
- taskgraph.transforms.job:transforms
|
||||
- taskgraph.transforms.task:transforms
|
||||
|
||||
|
@ -7,7 +7,6 @@ kind-dependencies:
|
||||
|
||||
transforms:
|
||||
- taskgraph.transforms.tests:transforms
|
||||
- taskgraph.transforms.use_fetches:transforms
|
||||
- taskgraph.transforms.job:transforms
|
||||
- taskgraph.transforms.coalesce:transforms
|
||||
- taskgraph.transforms.task:transforms
|
||||
|
@ -9,7 +9,6 @@ kind-dependencies:
|
||||
|
||||
transforms:
|
||||
- taskgraph.transforms.try_job:transforms
|
||||
- taskgraph.transforms.use_fetches:transforms
|
||||
- taskgraph.transforms.use_toolchains:transforms
|
||||
- taskgraph.transforms.job:transforms
|
||||
- taskgraph.transforms.task:transforms
|
||||
|
@ -77,6 +77,9 @@ job_description_schema = Schema({
|
||||
Optional('files-changed'): [basestring],
|
||||
}),
|
||||
|
||||
# A list of artifacts to install from 'fetch' tasks.
|
||||
Optional('fetches'): [basestring],
|
||||
|
||||
# A description of how to run this job.
|
||||
'run': {
|
||||
# The key to a job implementation in a peer module to this one
|
||||
@ -128,6 +131,54 @@ def rewrite_when_to_optimization(config, jobs):
|
||||
yield job
|
||||
|
||||
|
||||
def get_attribute(dict, key, attributes, attribute_name):
|
||||
'''Get `attribute_name` from the given `attributes` dict, and if there
|
||||
is a corresponding value, set `key` in `dict` to that value.'''
|
||||
value = attributes.get(attribute_name)
|
||||
if value:
|
||||
dict[key] = value
|
||||
|
||||
|
||||
@transforms.add
|
||||
def use_fetches(config, jobs):
|
||||
artifacts = {}
|
||||
|
||||
for task in config.kind_dependencies_tasks:
|
||||
if task.kind != 'fetch':
|
||||
continue
|
||||
|
||||
name = task.label.replace('%s-' % task.kind, '')
|
||||
get_attribute(artifacts, name, task.attributes, 'fetch-artifact')
|
||||
|
||||
for job in jobs:
|
||||
fetches = job.pop('fetches', [])
|
||||
|
||||
# Hack added for `mach artifact toolchain` to support reading toolchain
|
||||
# kinds in isolation.
|
||||
if config.params.get('ignore_fetches'):
|
||||
fetches[:] = []
|
||||
|
||||
for fetch in fetches:
|
||||
if fetch not in artifacts:
|
||||
raise Exception('Missing fetch job for %s-%s: %s' % (
|
||||
config.kind, job['name'], fetch))
|
||||
|
||||
if not artifacts[fetch].startswith('public/'):
|
||||
raise Exception('non-public artifacts not supported')
|
||||
|
||||
if fetches:
|
||||
job.setdefault('dependencies', {}).update(
|
||||
('fetch-%s' % f, 'fetch-%s' % f)
|
||||
for f in fetches)
|
||||
|
||||
env = job.setdefault('worker', {}).setdefault('env', {})
|
||||
env['MOZ_FETCHES'] = {'task-reference': ' '.join(
|
||||
'%s@<fetch-%s>' % (artifacts[f], f)
|
||||
for f in fetches)}
|
||||
|
||||
yield job
|
||||
|
||||
|
||||
@transforms.add
|
||||
def make_task_description(config, jobs):
|
||||
"""Given a build description, create a task description"""
|
||||
|
@ -1,60 +0,0 @@
|
||||
# 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/.
|
||||
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from .base import (
|
||||
TransformSequence,
|
||||
)
|
||||
|
||||
|
||||
transforms = TransformSequence()
|
||||
|
||||
|
||||
def get_attribute(dict, key, attributes, attribute_name):
|
||||
'''Get `attribute_name` from the given `attributes` dict, and if there
|
||||
is a corresponding value, set `key` in `dict` to that value.'''
|
||||
value = attributes.get(attribute_name)
|
||||
if value:
|
||||
dict[key] = value
|
||||
|
||||
|
||||
@transforms.add
|
||||
def use_fetches(config, jobs):
|
||||
artifacts = {}
|
||||
|
||||
for task in config.kind_dependencies_tasks:
|
||||
if task.kind != 'fetch':
|
||||
continue
|
||||
|
||||
name = task.label.replace('%s-' % task.kind, '')
|
||||
get_attribute(artifacts, name, task.attributes, 'fetch-artifact')
|
||||
|
||||
for job in jobs:
|
||||
fetches = job.pop('fetches', [])
|
||||
|
||||
# Hack added for `mach artifact toolchain` to support reading toolchain
|
||||
# kinds in isolation.
|
||||
if config.params.get('ignore_fetches'):
|
||||
fetches[:] = []
|
||||
|
||||
for fetch in fetches:
|
||||
if fetch not in artifacts:
|
||||
raise Exception('Missing fetch job for %s-%s: %s' % (
|
||||
config.kind, job['name'], fetch))
|
||||
|
||||
if not artifacts[fetch].startswith('public/'):
|
||||
raise Exception('non-public artifacts not supported')
|
||||
|
||||
if fetches:
|
||||
job.setdefault('dependencies', {}).update(
|
||||
('fetch-%s' % f, 'fetch-%s' % f)
|
||||
for f in fetches)
|
||||
|
||||
env = job.setdefault('worker', {}).setdefault('env', {})
|
||||
env['MOZ_FETCHES'] = {'task-reference': ' '.join(
|
||||
'%s@<fetch-%s>' % (artifacts[f], f)
|
||||
for f in fetches)}
|
||||
|
||||
yield job
|
@ -6,8 +6,7 @@
|
||||
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import BaseHTTPServer
|
||||
import SimpleHTTPServer
|
||||
|
||||
import errno
|
||||
import logging
|
||||
import threading
|
||||
@ -15,15 +14,22 @@ import posixpath
|
||||
import socket
|
||||
import sys
|
||||
import os
|
||||
import urllib
|
||||
import urlparse
|
||||
import re
|
||||
import moznetwork
|
||||
import time
|
||||
from SocketServer import ThreadingMixIn
|
||||
|
||||
from six import iteritems
|
||||
from six.moves.socketserver import ThreadingMixIn
|
||||
from six.moves.BaseHTTPServer import HTTPServer
|
||||
|
||||
from six.moves.urllib.parse import (
|
||||
urlsplit,
|
||||
unquote,
|
||||
)
|
||||
from six.moves.SimpleHTTPServer import SimpleHTTPRequestHandler
|
||||
|
||||
|
||||
class EasyServer(ThreadingMixIn, BaseHTTPServer.HTTPServer):
|
||||
class EasyServer(ThreadingMixIn, HTTPServer):
|
||||
allow_reuse_address = True
|
||||
acceptable_errors = (errno.EPIPE, errno.ECONNABORTED)
|
||||
|
||||
@ -50,7 +56,7 @@ class Request(object):
|
||||
def __init__(self, uri, headers, rfile=None):
|
||||
self.uri = uri
|
||||
self.headers = headers
|
||||
parsed = urlparse.urlsplit(uri)
|
||||
parsed = urlsplit(uri)
|
||||
for i, attr in enumerate(self.uri_attrs):
|
||||
setattr(self, attr, parsed[i])
|
||||
try:
|
||||
@ -63,7 +69,7 @@ class Request(object):
|
||||
self.body = None
|
||||
|
||||
|
||||
class RequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
class RequestHandler(SimpleHTTPRequestHandler):
|
||||
|
||||
docroot = os.getcwd() # current working directory at time of import
|
||||
proxy_host_dirs = False
|
||||
@ -72,7 +78,7 @@ class RequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
request = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, *args, **kwargs)
|
||||
SimpleHTTPRequestHandler.__init__(self, *args, **kwargs)
|
||||
self.extensions_map['.svg'] = 'image/svg+xml'
|
||||
|
||||
def _try_handler(self, method):
|
||||
@ -89,7 +95,7 @@ class RequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
(response_code, headerdict, data) = \
|
||||
handler['function'](self.request, *m.groups())
|
||||
self.send_response(response_code)
|
||||
for (keyword, value) in headerdict.iteritems():
|
||||
for (keyword, value) in iteritems(headerdict):
|
||||
self.send_header(keyword, value)
|
||||
self.end_headers()
|
||||
self.wfile.write(data)
|
||||
@ -102,9 +108,9 @@ class RequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
"""Find the on-disk path to serve this request from,
|
||||
using self.path_mappings and self.docroot.
|
||||
Return (url_path, disk_path)."""
|
||||
path_components = filter(None, self.request.path.split('/'))
|
||||
for prefix, disk_path in self.path_mappings.iteritems():
|
||||
prefix_components = filter(None, prefix.split('/'))
|
||||
path_components = list(filter(None, self.request.path.split('/')))
|
||||
for prefix, disk_path in iteritems(self.path_mappings):
|
||||
prefix_components = list(filter(None, prefix.split('/')))
|
||||
if len(path_components) < len(prefix_components):
|
||||
continue
|
||||
if path_components[:len(prefix_components)] == prefix_components:
|
||||
@ -115,7 +121,7 @@ class RequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
return None
|
||||
|
||||
def parse_request(self):
|
||||
retval = SimpleHTTPServer.SimpleHTTPRequestHandler.parse_request(self)
|
||||
retval = SimpleHTTPRequestHandler.parse_request(self)
|
||||
self.request = Request(self.path, self.headers, self.rfile)
|
||||
return retval
|
||||
|
||||
@ -129,7 +135,7 @@ class RequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
if self.request.netloc and self.proxy_host_dirs:
|
||||
self.path = '/' + self.request.netloc + \
|
||||
self.path
|
||||
SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
|
||||
SimpleHTTPRequestHandler.do_GET(self)
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
@ -158,9 +164,9 @@ class RequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
# except we serve from self.docroot instead of os.getcwd(), and
|
||||
# parse_request()/do_GET() have already stripped the query string and
|
||||
# fragment and mangled the path for proxying, if required.
|
||||
path = posixpath.normpath(urllib.unquote(self.path))
|
||||
path = posixpath.normpath(unquote(self.path))
|
||||
words = path.split('/')
|
||||
words = filter(None, words)
|
||||
words = list(filter(None, words))
|
||||
path = self.disk_root
|
||||
for word in words:
|
||||
drive, word = os.path.splitdrive(word)
|
||||
|
@ -7,7 +7,7 @@ from __future__ import absolute_import
|
||||
from setuptools import setup
|
||||
|
||||
PACKAGE_VERSION = '0.7'
|
||||
deps = ['moznetwork >= 0.24']
|
||||
deps = ['moznetwork >= 0.24', 'mozinfo >= 1.0.0', 'six >= 1.10.0']
|
||||
|
||||
setup(name='mozhttpd',
|
||||
version=PACKAGE_VERSION,
|
||||
|
@ -8,7 +8,6 @@ from __future__ import absolute_import
|
||||
|
||||
import mozfile
|
||||
import mozhttpd
|
||||
import urllib2
|
||||
import os
|
||||
import unittest
|
||||
import json
|
||||
@ -16,6 +15,16 @@ import tempfile
|
||||
|
||||
import mozunit
|
||||
|
||||
from six.moves.urllib.request import (
|
||||
HTTPHandler,
|
||||
ProxyHandler,
|
||||
Request,
|
||||
build_opener,
|
||||
install_opener,
|
||||
urlopen,
|
||||
)
|
||||
from six.moves.urllib.error import HTTPError
|
||||
|
||||
here = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
@ -54,7 +63,7 @@ class ApiTest(unittest.TestCase):
|
||||
def try_get(self, server_port, querystr):
|
||||
self.resource_get_called = 0
|
||||
|
||||
f = urllib2.urlopen(self.get_url('/api/resource/1', server_port, querystr))
|
||||
f = urlopen(self.get_url('/api/resource/1', server_port, querystr))
|
||||
try:
|
||||
self.assertEqual(f.getcode(), 200)
|
||||
except AttributeError:
|
||||
@ -67,9 +76,11 @@ class ApiTest(unittest.TestCase):
|
||||
|
||||
postdata = {'hamburgers': '1234'}
|
||||
try:
|
||||
f = urllib2.urlopen(self.get_url('/api/resource/', server_port, querystr),
|
||||
data=json.dumps(postdata))
|
||||
except urllib2.HTTPError as e:
|
||||
f = urlopen(
|
||||
self.get_url('/api/resource/', server_port, querystr),
|
||||
data=json.dumps(postdata),
|
||||
)
|
||||
except HTTPError as e:
|
||||
# python 2.4
|
||||
self.assertEqual(e.code, 201)
|
||||
body = e.fp.read()
|
||||
@ -84,8 +95,8 @@ class ApiTest(unittest.TestCase):
|
||||
def try_del(self, server_port, querystr):
|
||||
self.resource_del_called = 0
|
||||
|
||||
opener = urllib2.build_opener(urllib2.HTTPHandler)
|
||||
request = urllib2.Request(self.get_url('/api/resource/1', server_port, querystr))
|
||||
opener = build_opener(HTTPHandler)
|
||||
request = Request(self.get_url('/api/resource/1', server_port, querystr))
|
||||
request.get_method = lambda: 'DEL'
|
||||
f = opener.open(request)
|
||||
|
||||
@ -127,8 +138,8 @@ class ApiTest(unittest.TestCase):
|
||||
# GET: By default we don't serve any files if we just define an API
|
||||
exception_thrown = False
|
||||
try:
|
||||
urllib2.urlopen(self.get_url('/', server_port, None))
|
||||
except urllib2.HTTPError as e:
|
||||
urlopen(self.get_url('/', server_port, None))
|
||||
except HTTPError as e:
|
||||
self.assertEqual(e.code, 404)
|
||||
exception_thrown = True
|
||||
self.assertTrue(exception_thrown)
|
||||
@ -143,8 +154,8 @@ class ApiTest(unittest.TestCase):
|
||||
# GET: Return 404 for non-existent endpoint
|
||||
exception_thrown = False
|
||||
try:
|
||||
urllib2.urlopen(self.get_url('/api/resource/', server_port, None))
|
||||
except urllib2.HTTPError as e:
|
||||
urlopen(self.get_url('/api/resource/', server_port, None))
|
||||
except HTTPError as e:
|
||||
self.assertEqual(e.code, 404)
|
||||
exception_thrown = True
|
||||
self.assertTrue(exception_thrown)
|
||||
@ -152,9 +163,11 @@ class ApiTest(unittest.TestCase):
|
||||
# POST: POST should also return 404
|
||||
exception_thrown = False
|
||||
try:
|
||||
urllib2.urlopen(self.get_url('/api/resource/', server_port, None),
|
||||
data=json.dumps({}))
|
||||
except urllib2.HTTPError as e:
|
||||
urlopen(
|
||||
self.get_url('/api/resource/', server_port, None),
|
||||
data=json.dumps({}),
|
||||
)
|
||||
except HTTPError as e:
|
||||
self.assertEqual(e.code, 404)
|
||||
exception_thrown = True
|
||||
self.assertTrue(exception_thrown)
|
||||
@ -162,12 +175,11 @@ class ApiTest(unittest.TestCase):
|
||||
# DEL: DEL should also return 404
|
||||
exception_thrown = False
|
||||
try:
|
||||
opener = urllib2.build_opener(urllib2.HTTPHandler)
|
||||
request = urllib2.Request(self.get_url('/api/resource/', server_port,
|
||||
None))
|
||||
opener = build_opener(HTTPHandler)
|
||||
request = Request(self.get_url('/api/resource/', server_port, None))
|
||||
request.get_method = lambda: 'DEL'
|
||||
opener.open(request)
|
||||
except urllib2.HTTPError:
|
||||
except HTTPError:
|
||||
self.assertEqual(e.code, 404)
|
||||
exception_thrown = True
|
||||
self.assertTrue(exception_thrown)
|
||||
@ -181,7 +193,7 @@ class ApiTest(unittest.TestCase):
|
||||
server_port = httpd.httpd.server_port
|
||||
|
||||
# We defined a docroot, so we expect a directory listing
|
||||
f = urllib2.urlopen(self.get_url('/', server_port, None))
|
||||
f = urlopen(self.get_url('/', server_port, None))
|
||||
try:
|
||||
self.assertEqual(f.getcode(), 200)
|
||||
except AttributeError:
|
||||
@ -197,7 +209,7 @@ class ProxyTest(unittest.TestCase):
|
||||
|
||||
def tearDown(self):
|
||||
# reset proxy opener in case it changed
|
||||
urllib2.install_opener(None)
|
||||
install_opener(None)
|
||||
|
||||
def test_proxy(self):
|
||||
docroot = tempfile.mkdtemp()
|
||||
@ -211,7 +223,7 @@ class ProxyTest(unittest.TestCase):
|
||||
|
||||
def index_contents(host): return '%s index' % host
|
||||
|
||||
index = file(os.path.join(docroot, index_filename), 'w')
|
||||
index = open(os.path.join(docroot, index_filename), 'w')
|
||||
index.write(index_contents('*'))
|
||||
index.close()
|
||||
|
||||
@ -219,12 +231,13 @@ class ProxyTest(unittest.TestCase):
|
||||
httpd.start(block=False)
|
||||
server_port = httpd.httpd.server_port
|
||||
|
||||
proxy_support = urllib2.ProxyHandler({'http': 'http://127.0.0.1:%d' %
|
||||
server_port})
|
||||
urllib2.install_opener(urllib2.build_opener(proxy_support))
|
||||
proxy_support = ProxyHandler({
|
||||
'http': 'http://127.0.0.1:%d' % server_port,
|
||||
})
|
||||
install_opener(build_opener(proxy_support))
|
||||
|
||||
for host in hosts:
|
||||
f = urllib2.urlopen(url(host))
|
||||
f = urlopen(url(host))
|
||||
try:
|
||||
self.assertEqual(f.getcode(), 200)
|
||||
except AttributeError:
|
||||
@ -239,18 +252,19 @@ class ProxyTest(unittest.TestCase):
|
||||
httpd.start(block=False)
|
||||
server_port = httpd.httpd.server_port
|
||||
|
||||
proxy_support = urllib2.ProxyHandler({'http': 'http://127.0.0.1:%d' %
|
||||
server_port})
|
||||
urllib2.install_opener(urllib2.build_opener(proxy_support))
|
||||
proxy_support = ProxyHandler({
|
||||
'http': 'http://127.0.0.1:%d' % server_port,
|
||||
})
|
||||
install_opener(build_opener(proxy_support))
|
||||
|
||||
# set up dirs
|
||||
for host in hosts:
|
||||
os.mkdir(os.path.join(docroot, host))
|
||||
file(os.path.join(docroot, host, index_filename), 'w') \
|
||||
open(os.path.join(docroot, host, index_filename), 'w') \
|
||||
.write(index_contents(host))
|
||||
|
||||
for host in hosts:
|
||||
f = urllib2.urlopen(url(host))
|
||||
f = urlopen(url(host))
|
||||
try:
|
||||
self.assertEqual(f.getcode(), 200)
|
||||
except AttributeError:
|
||||
@ -259,8 +273,8 @@ class ProxyTest(unittest.TestCase):
|
||||
|
||||
exc = None
|
||||
try:
|
||||
urllib2.urlopen(url(unproxied_host))
|
||||
except urllib2.HTTPError as e:
|
||||
urlopen(url(unproxied_host))
|
||||
except HTTPError as e:
|
||||
exc = e
|
||||
self.assertNotEqual(exc, None)
|
||||
self.assertEqual(exc.code, 404)
|
||||
|
@ -1,9 +1,10 @@
|
||||
[DEFAULT]
|
||||
subsuite = mozbase, os == "linux"
|
||||
skip-if = python == 3
|
||||
[api.py]
|
||||
skip-if = python == 3
|
||||
[baseurl.py]
|
||||
[basic.py]
|
||||
[filelisting.py]
|
||||
skip-if = python == 3
|
||||
[paths.py]
|
||||
[requestlog.py]
|
||||
|
@ -9,7 +9,9 @@ from mozfile import TemporaryDirectory
|
||||
import mozhttpd
|
||||
import os
|
||||
import unittest
|
||||
import urllib2
|
||||
|
||||
from six.moves.urllib.request import urlopen
|
||||
from six.moves.urllib.error import HTTPError
|
||||
|
||||
import mozunit
|
||||
|
||||
@ -17,13 +19,13 @@ import mozunit
|
||||
class PathTest(unittest.TestCase):
|
||||
|
||||
def try_get(self, url, expected_contents):
|
||||
f = urllib2.urlopen(url)
|
||||
f = urlopen(url)
|
||||
self.assertEqual(f.getcode(), 200)
|
||||
self.assertEqual(f.read(), expected_contents)
|
||||
|
||||
def try_get_expect_404(self, url):
|
||||
with self.assertRaises(urllib2.HTTPError) as cm:
|
||||
urllib2.urlopen(url)
|
||||
with self.assertRaises(HTTPError) as cm:
|
||||
urlopen(url)
|
||||
self.assertEqual(404, cm.exception.code)
|
||||
|
||||
def test_basic(self):
|
||||
@ -36,8 +38,8 @@ class PathTest(unittest.TestCase):
|
||||
path_mappings={'/files': d2}
|
||||
)
|
||||
httpd.start(block=False)
|
||||
self.try_get(httpd.get_url("/test1.txt"), "test 1 contents")
|
||||
self.try_get(httpd.get_url("/files/test2.txt"), "test 2 contents")
|
||||
self.try_get(httpd.get_url("/test1.txt"), b"test 1 contents")
|
||||
self.try_get(httpd.get_url("/files/test2.txt"), b"test 2 contents")
|
||||
self.try_get_expect_404(httpd.get_url("/files/test2_nope.txt"))
|
||||
httpd.stop()
|
||||
|
||||
@ -51,8 +53,8 @@ class PathTest(unittest.TestCase):
|
||||
'/abc': d2, }
|
||||
)
|
||||
httpd.start(block=False)
|
||||
self.try_get(httpd.get_url("/abcxyz/test1.txt"), "test 1 contents")
|
||||
self.try_get(httpd.get_url("/abc/test2.txt"), "test 2 contents")
|
||||
self.try_get(httpd.get_url("/abcxyz/test1.txt"), b"test 1 contents")
|
||||
self.try_get(httpd.get_url("/abc/test2.txt"), b"test 2 contents")
|
||||
httpd.stop()
|
||||
|
||||
def test_multipart_path_mapping(self):
|
||||
@ -63,7 +65,7 @@ class PathTest(unittest.TestCase):
|
||||
path_mappings={'/abc/def/ghi': d1}
|
||||
)
|
||||
httpd.start(block=False)
|
||||
self.try_get(httpd.get_url("/abc/def/ghi/test1.txt"), "test 1 contents")
|
||||
self.try_get(httpd.get_url("/abc/def/ghi/test1.txt"), b"test 1 contents")
|
||||
self.try_get_expect_404(httpd.get_url("/abc/test1.txt"))
|
||||
self.try_get_expect_404(httpd.get_url("/abc/def/test1.txt"))
|
||||
httpd.stop()
|
||||
|
@ -5,10 +5,11 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import mozhttpd
|
||||
import urllib2
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from six.moves.urllib.request import urlopen
|
||||
|
||||
import mozunit
|
||||
|
||||
here = os.path.dirname(os.path.abspath(__file__))
|
||||
@ -21,7 +22,7 @@ class RequestLogTest(unittest.TestCase):
|
||||
httpd = mozhttpd.MozHttpd(port=0, docroot=here, log_requests=log_requests)
|
||||
httpd.start(block=False)
|
||||
url = "http://%s:%s/" % ('127.0.0.1', httpd.httpd.server_port)
|
||||
f = urllib2.urlopen(url)
|
||||
f = urlopen(url)
|
||||
f.read()
|
||||
|
||||
return httpd.request_log
|
||||
|
@ -135091,6 +135091,18 @@
|
||||
{}
|
||||
]
|
||||
],
|
||||
"css/css-text/overflow-wrap/overflow-wrap-min-content-size-002.html": [
|
||||
[
|
||||
"/css/css-text/overflow-wrap/overflow-wrap-min-content-size-002.html",
|
||||
[
|
||||
[
|
||||
"/css/css-text/overflow-wrap/reference/overflow-wrap-min-content-size-002-ref.html",
|
||||
"=="
|
||||
]
|
||||
],
|
||||
{}
|
||||
]
|
||||
],
|
||||
"css/css-text/overflow-wrap/word-wrap-001.html": [
|
||||
[
|
||||
"/css/css-text/overflow-wrap/word-wrap-001.html",
|
||||
@ -257609,6 +257621,11 @@
|
||||
{}
|
||||
]
|
||||
],
|
||||
"css/css-text/overflow-wrap/reference/overflow-wrap-min-content-size-002-ref.html": [
|
||||
[
|
||||
{}
|
||||
]
|
||||
],
|
||||
"css/css-text/support/1x1-green.png": [
|
||||
[
|
||||
{}
|
||||
@ -534201,6 +534218,10 @@
|
||||
"5858dbb88a775bb8975f338d866b6fc837485364",
|
||||
"reftest"
|
||||
],
|
||||
"css/css-text/overflow-wrap/overflow-wrap-min-content-size-002.html": [
|
||||
"ae7abc617493b9e2c9313215a3f38b77c37d9450",
|
||||
"reftest"
|
||||
],
|
||||
"css/css-text/overflow-wrap/reference/overflow-wrap-break-word-001-ref.html": [
|
||||
"0b16a0bdb25ddd647ad96dd82e3430274667ee87",
|
||||
"support"
|
||||
@ -534221,6 +534242,10 @@
|
||||
"f3e09183b565f71e38158cc5cd4d96ab5fbf25d4",
|
||||
"support"
|
||||
],
|
||||
"css/css-text/overflow-wrap/reference/overflow-wrap-min-content-size-002-ref.html": [
|
||||
"3686ae6a0e278a970b861c165f0f840df302db70",
|
||||
"support"
|
||||
],
|
||||
"css/css-text/overflow-wrap/word-wrap-001.html": [
|
||||
"8bafc4d48bbfee1e6c465a95b29792ba33c30346",
|
||||
"reftest"
|
||||
|
@ -1,3 +1,3 @@
|
||||
[mix-blend-mode-blended-element-overflow-hidden-and-border-radius.html]
|
||||
expected:
|
||||
if os == "linux": FAIL
|
||||
if os == "linux" and not webrender: FAIL
|
||||
|
@ -1,3 +1,3 @@
|
||||
[mix-blend-mode-intermediate-element-overflow-hidden-and-border-radius.html]
|
||||
expected:
|
||||
if os == "linux": FAIL
|
||||
if os == "linux" and not webrender: FAIL
|
||||
|
@ -1,3 +1,3 @@
|
||||
[mix-blend-mode-parent-with-border-radius.html]
|
||||
expected:
|
||||
if os == "linux": FAIL
|
||||
if os == "linux" and not webrender: FAIL
|
||||
|
@ -1,2 +0,0 @@
|
||||
[overflow-wrap-min-content-size-001.html]
|
||||
expected: FAIL
|
@ -164,12 +164,6 @@
|
||||
[MediaStreamTrackAudioSourceNode interface: existence and properties of interface prototype object's @@unscopables property]
|
||||
expected: FAIL
|
||||
|
||||
[PeriodicWave must be primary interface of new PeriodicWave(context)]
|
||||
expected: FAIL
|
||||
|
||||
[Stringification of new PeriodicWave(context)]
|
||||
expected: FAIL
|
||||
|
||||
[AudioWorklet interface: existence and properties of interface object]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>CSS Text Test: overflow-wrap: break-word and intrinsic sizing</title>
|
||||
<link rel="author" title="Xidorn Quan" href="https://www.upsuper.org/">
|
||||
<link rel="author" title="Mozilla" href="https://www.mozilla.org/">
|
||||
<link rel="help" href="https://drafts.csswg.org/css-text-3/#overflow-wrap-property">
|
||||
<meta name="flags" content="">
|
||||
<link rel="match" href="reference/overflow-wrap-min-content-size-002-ref.html">
|
||||
<meta name="assert" content="overflow-wrap:break-word doesn't break grapheme cluster and min-content intrinsic size should take that into account.">
|
||||
<style>
|
||||
#wrapper {
|
||||
width: 0px;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
#test {
|
||||
float: left;
|
||||
border: 2px solid blue;
|
||||
}
|
||||
</style>
|
||||
|
||||
<p>Test passes if the glyphs are completely inside the blue box.
|
||||
<div id="wrapper">
|
||||
<div id="test">நிிிிநிிிி</div>
|
||||
</div>
|
@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>CSS Text Test reference</title>
|
||||
<link rel="author" title="Xidorn Quan" href="https://www.upsuper.org/">
|
||||
<link rel="author" title="Mozilla" href="https://www.mozilla.org/">
|
||||
<style>
|
||||
#test {
|
||||
float: left;
|
||||
border: 2px solid blue;
|
||||
}
|
||||
</style>
|
||||
|
||||
<p>Test passes if the glyphs are completely inside the blue box.
|
||||
<div id="wrapper">
|
||||
<div id="test">நிிிி<br>நிிிி</div>
|
||||
</div>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user