Merge fx-team to m-c. a=merge

This commit is contained in:
Ryan VanderMeulen 2014-06-09 16:47:38 -04:00
commit e8706596e2
59 changed files with 924 additions and 643 deletions

View File

@ -5311,8 +5311,8 @@ function setStyleDisabled(disabled) {
var LanguageDetectionListener = {
init: function() {
window.messageManager.addMessageListener("LanguageDetection:Result", msg => {
Translation.languageDetected(msg.target, msg.data);
window.messageManager.addMessageListener("Translation:DocumentState", msg => {
Translation.documentStateReceived(msg.target, msg.data);
});
}
};

View File

@ -24,6 +24,11 @@ const DAILY_LAST_NUMERIC_FIELD = {type: Metrics.Storage.FIELD_DAILY_LAST_NUMERIC
this.Translation = {
STATE_OFFER: 0,
STATE_TRANSLATING: 1,
STATE_TRANSLATED: 2,
STATE_ERROR: 3,
supportedSourceLanguages: ["en", "zh", "ja", "es", "de", "fr", "ru", "ar", "ko", "pt"],
supportedTargetLanguages: ["en", "pl", "tr", "vi"],
@ -38,21 +43,33 @@ this.Translation = {
return this._defaultTargetLanguage;
},
languageDetected: function(aBrowser, aDetectedLanguage) {
if (this.supportedSourceLanguages.indexOf(aDetectedLanguage) == -1 ||
aDetectedLanguage == this.defaultTargetLanguage)
return;
documentStateReceived: function(aBrowser, aData) {
if (aData.state == this.STATE_OFFER) {
if (this.supportedSourceLanguages.indexOf(aData.detectedLanguage) == -1 ||
aData.detectedLanguage == this.defaultTargetLanguage)
return;
TranslationHealthReport.recordTranslationOpportunity(aDetectedLanguage);
TranslationHealthReport.recordTranslationOpportunity(aData.detectedLanguage);
}
if (!Services.prefs.getBoolPref(TRANSLATION_PREF_SHOWUI))
return;
if (!aBrowser.translationUI)
aBrowser.translationUI = new TranslationUI(aBrowser);
let trUI = aBrowser.translationUI;
// Set all values before showing a new translation infobar.
trUI._state = aData.state;
trUI.detectedLanguage = aData.detectedLanguage;
trUI.translatedFrom = aData.translatedFrom;
trUI.translatedTo = aData.translatedTo;
trUI.originalShown = aData.originalShown;
aBrowser.translationUI.showTranslationUI(aDetectedLanguage);
trUI.showURLBarIcon();
if (trUI.shouldShowInfoBar(aBrowser.currentURI))
trUI.showTranslationInfoBar();
}
};
@ -62,7 +79,6 @@ this.Translation = {
* the infobar are:
* - detectedLanguage, code of the language detected on the web page.
* - state, the state in which the infobar should be displayed
* - STATE_{OFFER,TRANSLATING,TRANSLATED,ERROR} constants.
* - translatedFrom, if already translated, source language code.
* - translatedTo, if already translated, target language code.
* - translate, method starting the translation of the current page.
@ -78,20 +94,15 @@ function TranslationUI(aBrowser) {
}
TranslationUI.prototype = {
STATE_OFFER: 0,
STATE_TRANSLATING: 1,
STATE_TRANSLATED: 2,
STATE_ERROR: 3,
translate: function(aFrom, aTo) {
if (aFrom == aTo ||
(this.state == this.STATE_TRANSLATED &&
(this.state == Translation.STATE_TRANSLATED &&
this.translatedFrom == aFrom && this.translatedTo == aTo)) {
// Nothing to do.
return;
}
this.state = this.STATE_TRANSLATING;
this.state = Translation.STATE_TRANSLATING;
this.translatedFrom = aFrom;
this.translatedTo = aTo;
@ -101,10 +112,10 @@ TranslationUI.prototype = {
);
},
showURLBarIcon: function(aTranslated) {
showURLBarIcon: function() {
let chromeWin = this.browser.ownerGlobal;
let PopupNotifications = chromeWin.PopupNotifications;
let removeId = aTranslated ? "translate" : "translated";
let removeId = this.originalShown ? "translated" : "translate";
let notification =
PopupNotifications.getNotification(removeId, this.browser);
if (notification)
@ -121,7 +132,7 @@ TranslationUI.prototype = {
return true;
};
let addId = aTranslated ? "translated" : "translate";
let addId = this.originalShown ? "translate" : "translated";
PopupNotifications.show(this.browser, addId, null,
addId + "-notification-icon", null, null,
{dismissed: true, eventCallback: callback});
@ -138,14 +149,14 @@ TranslationUI.prototype = {
originalShown: true,
showOriginalContent: function() {
this.showURLBarIcon();
this.originalShown = true;
this.showURLBarIcon();
this.browser.messageManager.sendAsyncMessage("Translation:ShowOriginal");
},
showTranslatedContent: function() {
this.showURLBarIcon(true);
this.originalShown = false;
this.showURLBarIcon();
this.browser.messageManager.sendAsyncMessage("Translation:ShowTranslation");
},
@ -159,11 +170,11 @@ TranslationUI.prototype = {
return notif;
},
shouldShowInfoBar: function(aURI, aDetectedLanguage) {
shouldShowInfoBar: function(aURI) {
// Check if we should never show the infobar for this language.
let neverForLangs =
Services.prefs.getCharPref("browser.translation.neverForLanguages");
if (neverForLangs.split(",").indexOf(aDetectedLanguage) != -1)
if (neverForLangs.split(",").indexOf(this.detectedLanguage) != -1)
return false;
// or if we should never show the infobar for this domain.
@ -182,7 +193,7 @@ TranslationUI.prototype = {
this.showURLBarIcon();
if (!this.shouldShowInfoBar(this.browser.currentURI, aDetectedLanguage))
if (!this.shouldShowInfoBar(this.browser.currentURI))
return null;
return this.showTranslationInfoBar();
@ -192,11 +203,11 @@ TranslationUI.prototype = {
switch (msg.name) {
case "Translation:Finished":
if (msg.data.success) {
this.state = this.STATE_TRANSLATED;
this.showURLBarIcon(true);
this.state = Translation.STATE_TRANSLATED;
this.originalShown = false;
this.showURLBarIcon();
} else {
this.state = this.STATE_ERROR;
this.state = Translation.STATE_ERROR;
}
break;
}

View File

@ -12,11 +12,17 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "LanguageDetector",
"resource:///modules/translation/LanguageDetector.jsm");
const STATE_OFFER = 0;
const STATE_TRANSLATED = 2;
const STATE_ERROR = 3;
this.TranslationContentHandler = function(global, docShell) {
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress);
webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
global.addEventListener("pageshow", this);
global.addMessageListener("Translation:TranslateDocument", this);
global.addMessageListener("Translation:ShowTranslation", this);
global.addMessageListener("Translation:ShowOriginal", this);
@ -24,6 +30,35 @@ this.TranslationContentHandler = function(global, docShell) {
}
TranslationContentHandler.prototype = {
handleEvent: function(aEvent) {
// We are only listening to pageshow events.
let target = aEvent.target;
// Only handle top-level frames.
let win = target.defaultView;
if (win.parent !== win)
return;
let content = this.global.content;
if (!content.detectedLanguage)
return;
let data = {};
let trDoc = content.translationDocument;
if (trDoc) {
data.state = trDoc.translationError ? STATE_ERROR : STATE_TRANSLATED;
data.translatedFrom = trDoc.translatedFrom;
data.translatedTo = trDoc.translatedTo;
data.originalShown = trDoc.originalShown;
} else {
data.state = STATE_OFFER;
data.originalShown = true;
}
data.detectedLanguage = content.detectedLanguage;
this.global.sendAsyncMessage("Translation:DocumentState", data);
},
/* nsIWebProgressListener implementation */
onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
if (!aWebProgress.isTopLevel ||
@ -35,10 +70,14 @@ TranslationContentHandler.prototype = {
if (!url.startsWith("http://") && !url.startsWith("https://"))
return;
let content = this.global.content;
if (content.detectedLanguage)
return;
// Grab a 60k sample of text from the page.
let encoder = Cc["@mozilla.org/layout/documentEncoder;1?type=text/plain"]
.createInstance(Ci.nsIDocumentEncoder);
encoder.init(this.global.content.document, "text/plain", encoder.SkipInvisibleContent);
encoder.init(content.document, "text/plain", encoder.SkipInvisibleContent);
let string = encoder.encodeToStringWithMaxLength(60 * 1024);
// Language detection isn't reliable on very short strings.
@ -46,8 +85,17 @@ TranslationContentHandler.prototype = {
return;
LanguageDetector.detectLanguage(string).then(result => {
if (result.confident)
this.global.sendAsyncMessage("LanguageDetection:Result", result.language);
if (!result.confident)
return;
content.detectedLanguage = result.language;
let data = {
state: STATE_OFFER,
originalShown: true,
detectedLanguage: result.language
};
this.global.sendAsyncMessage("Translation:DocumentState", data);
});
},
@ -78,12 +126,17 @@ TranslationContentHandler.prototype = {
msg.data.to);
this.global.content.translationDocument = translationDocument;
translationDocument.translatedFrom = msg.data.from;
translationDocument.translatedTo = msg.data.to;
translationDocument.translationError = false;
bingTranslation.translate().then(
success => {
this.global.sendAsyncMessage("Translation:Finished", {success: true});
translationDocument.showTranslation();
},
error => {
translationDocument.translationError = true;
this.global.sendAsyncMessage("Translation:Finished", {success: false});
}
);

View File

@ -31,6 +31,11 @@ this.TranslationDocument = function(document) {
};
this.TranslationDocument.prototype = {
translatedFrom: "",
translatedTo: "",
translationError: false,
originalShown: true,
/**
* Initializes the object and populates
* the roots lists.
@ -185,6 +190,7 @@ this.TranslationDocument.prototype = {
* content.
*/
showTranslation: function() {
this.originalShown = false;
this._swapDocumentContent("translation");
},
@ -193,6 +199,7 @@ this.TranslationDocument.prototype = {
* content.
*/
showOriginal: function() {
this.originalShown = true;
this._swapDocumentContent("original");
},

View File

@ -104,7 +104,10 @@ let gTests = [
desc: "never for language",
run: function* checkNeverForLanguage() {
// Show the infobar for example.com and fr.
Translation.languageDetected(gBrowser.selectedBrowser, "fr");
Translation.documentStateReceived(gBrowser.selectedBrowser,
{state: Translation.STATE_OFFER,
originalShown: true,
detectedLanguage: "fr"});
let notif = getInfoBar();
ok(notif, "the infobar is visible");
let ui = gBrowser.selectedBrowser.translationUI;
@ -150,7 +153,10 @@ let gTests = [
desc: "never for site",
run: function* checkNeverForSite() {
// Show the infobar for example.com and fr.
Translation.languageDetected(gBrowser.selectedBrowser, "fr");
Translation.documentStateReceived(gBrowser.selectedBrowser,
{state: Translation.STATE_OFFER,
originalShown: true,
detectedLanguage: "fr"});
let notif = getInfoBar();
ok(notif, "the infobar is visible");
let ui = gBrowser.selectedBrowser.translationUI;

View File

@ -34,7 +34,7 @@ function waitForCondition(condition, nextTest, errorMsg) {
var TranslationStub = {
translate: function(aFrom, aTo) {
this.state = this.STATE_TRANSLATING;
this.state = Translation.STATE_TRANSLATING;
this.translatedFrom = aFrom;
this.translatedTo = aTo;
},
@ -45,20 +45,22 @@ var TranslationStub = {
},
failTranslation: function() {
this.state = this.STATE_ERROR;
this.state = Translation.STATE_ERROR;
this._reset();
},
finishTranslation: function() {
this.showTranslatedContent();
this.state = this.STATE_TRANSLATED;
this.state = Translation.STATE_TRANSLATED;
this._reset();
}
};
function showTranslationUI(aDetectedLanguage) {
let browser = gBrowser.selectedBrowser;
Translation.languageDetected(browser, aDetectedLanguage);
Translation.documentStateReceived(browser, {state: Translation.STATE_OFFER,
originalShown: true,
detectedLanguage: aDetectedLanguage});
let ui = browser.translationUI;
for (let name of ["translate", "_reset", "failTranslation", "finishTranslation"])
ui[name] = TranslationStub[name];
@ -100,13 +102,13 @@ function checkURLBarIcon(aExpectTranslated = false) {
function run_tests(aFinishCallback) {
info("Show an info bar saying the current page is in French");
let notif = showTranslationUI("fr");
is(notif.state, notif.translation.STATE_OFFER, "the infobar is offering translation");
is(notif.state, Translation.STATE_OFFER, "the infobar is offering translation");
is(notif._getAnonElt("detectedLanguage").value, "fr", "The detected language is displayed");
checkURLBarIcon();
info("Click the 'Translate' button");
notif._getAnonElt("translate").click();
is(notif.state, notif.translation.STATE_TRANSLATING, "the infobar is in the translating state");
is(notif.state, Translation.STATE_TRANSLATING, "the infobar is in the translating state");
ok(!!notif.translation.translatedFrom, "Translation.translate has been called");
is(notif.translation.translatedFrom, "fr", "from language correct");
is(notif.translation.translatedTo, Translation.defaultTargetLanguage, "from language correct");
@ -114,12 +116,12 @@ function run_tests(aFinishCallback) {
info("Make the translation fail and check we are in the error state.");
notif.translation.failTranslation();
is(notif.state, notif.translation.STATE_ERROR, "infobar in the error state");
is(notif.state, Translation.STATE_ERROR, "infobar in the error state");
checkURLBarIcon();
info("Click the try again button");
notif._getAnonElt("tryAgain").click();
is(notif.state, notif.translation.STATE_TRANSLATING, "infobar in the translating state");
is(notif.state, Translation.STATE_TRANSLATING, "infobar in the translating state");
ok(!!notif.translation.translatedFrom, "Translation.translate has been called");
is(notif.translation.translatedFrom, "fr", "from language correct");
is(notif.translation.translatedTo, Translation.defaultTargetLanguage, "from language correct");
@ -127,7 +129,7 @@ function run_tests(aFinishCallback) {
info("Make the translation succeed and check we are in the 'translated' state.");
notif.translation.finishTranslation();
is(notif.state, notif.translation.STATE_TRANSLATED, "infobar in the translated state");
is(notif.state, Translation.STATE_TRANSLATED, "infobar in the translated state");
checkURLBarIcon(true);
info("Test 'Show original' / 'Show Translation' buttons.");
@ -153,7 +155,7 @@ function run_tests(aFinishCallback) {
let from = notif._getAnonElt("fromLanguage");
from.value = "es";
from.doCommand();
is(notif.state, notif.translation.STATE_TRANSLATING, "infobar in the translating state");
is(notif.state, Translation.STATE_TRANSLATING, "infobar in the translating state");
ok(!!notif.translation.translatedFrom, "Translation.translate has been called");
is(notif.translation.translatedFrom, "es", "from language correct");
is(notif.translation.translatedTo, Translation.defaultTargetLanguage, "to language correct");
@ -167,7 +169,7 @@ function run_tests(aFinishCallback) {
let to = notif._getAnonElt("toLanguage");
to.value = "pl";
to.doCommand();
is(notif.state, notif.translation.STATE_TRANSLATING, "infobar in the translating state");
is(notif.state, Translation.STATE_TRANSLATING, "infobar in the translating state");
ok(!!notif.translation.translatedFrom, "Translation.translate has been called");
is(notif.translation.translatedFrom, "es", "from language correct");
is(notif.translation.translatedTo, "pl", "to language correct");
@ -180,12 +182,12 @@ function run_tests(aFinishCallback) {
info("Reopen the info bar to check that it's possible to override the detected language.");
notif = showTranslationUI("fr");
is(notif.state, notif.translation.STATE_OFFER, "the infobar is offering translation");
is(notif.state, Translation.STATE_OFFER, "the infobar is offering translation");
is(notif._getAnonElt("detectedLanguage").value, "fr", "The detected language is displayed");
// Change the language and click 'Translate'
notif._getAnonElt("detectedLanguage").value = "ja";
notif._getAnonElt("translate").click();
is(notif.state, notif.translation.STATE_TRANSLATING, "the infobar is in the translating state");
is(notif.state, Translation.STATE_TRANSLATING, "the infobar is in the translating state");
ok(!!notif.translation.translatedFrom, "Translation.translate has been called");
is(notif.translation.translatedFrom, "ja", "from language correct");
notif.close();

View File

@ -127,7 +127,7 @@
let stateName;
for (let name of ["OFFER", "TRANSLATING", "TRANSLATED", "ERROR"]) {
if (this.translation["STATE_" + name] == val) {
if (Translation["STATE_" + name] == val) {
stateName = name.toLowerCase();
break;
}
@ -188,7 +188,7 @@
<method name="translate">
<body>
<![CDATA[
if (this.state == this.translation.STATE_OFFER) {
if (this.state == Translation.STATE_OFFER) {
this._getAnonElt("fromLanguage").value =
this._getAnonElt("detectedLanguage").value;
this._getAnonElt("toLanguage").value =
@ -235,7 +235,7 @@
<![CDATA[
// Get the source language name.
let lang;
if (this.state == this.translation.STATE_OFFER)
if (this.state == Translation.STATE_OFFER)
lang = this._getAnonElt("detectedLanguage").value;
else
lang = this._getAnonElt("fromLanguage").value;

View File

@ -118,3 +118,17 @@ function spawnTest() {
yield helpers.closeToolbar(options);
yield helpers.closeTab(options);
}
/**
* Creates a regular expression that matches a string. This greatly simplifies
* matching and debugging long strings.
*
* @param {String} text
* Text to convert
* @return {RegExp}
* Regular expression matching text
*/
function getRegexForString(str) {
str = str.replace(/(\.|\\|\/|\(|\)|\[|\]|\*|\+|\?|\$|\^|\|)/g, "\\$1");
return new RegExp(str);
}

View File

@ -14,53 +14,7 @@ function testTask() {
let options = yield helpers.openTab("about:blank");
yield helpers.openToolbar(options);
let deferred = promise.defer();
let scratchpadWin = null;
let scratchpad = null;
let observer = {
onReady: function() {
scratchpad.removeObserver(observer);
let result = scratchpad.getText();
result = result.replace(/[\r\n]]*/g, "\n");
let correct = "function somefunc() {\n" +
" if (true) // Some comment\n" +
" doSomething();\n" +
" for (let n = 0; n < 500; n++) {\n" +
" if (n % 2 == 1) {\n" +
" console.log(n);\n" +
" console.log(n + 1);\n" +
" }\n" +
" }\n" +
"}";
is(result, correct, "JS has been correctly prettified");
if (scratchpadWin) {
scratchpadWin.close();
scratchpadWin = null;
}
deferred.resolve();
},
};
let onLoad = function GDT_onLoad() {
scratchpadWin.removeEventListener("load", onLoad, false);
scratchpad = scratchpadWin.Scratchpad;
scratchpad.addObserver(observer);
};
let onNotify = function(subject, topic, data) {
if (topic == "domwindowopened") {
Services.ww.unregisterNotification(onNotify);
scratchpadWin = subject.QueryInterface(Ci.nsIDOMWindow);
scratchpadWin.addEventListener("load", onLoad, false);
}
};
Services.ww.registerNotification(onNotify);
let notifyPromise = wwNotifyOnce();
helpers.audit(options, [
{
@ -74,14 +28,73 @@ function testTask() {
},
{
setup: 'jsb ' + TEST_URI,
// Should result in a new window, which should fire onReady (eventually)
exec: {
}
// Should result in a new scratchpad window
exec: { }
}
]);
yield deferred.promise;
let { subject } = yield notifyPromise;
let scratchpadWin = subject.QueryInterface(Ci.nsIDOMWindow);
yield helpers.listenOnce(scratchpadWin, "load");
let scratchpad = scratchpadWin.Scratchpad;
yield observeOnce(scratchpad);
let result = scratchpad.getText();
result = result.replace(/[\r\n]]*/g, "\n");
let correct = "function somefunc() {\n" +
" if (true) // Some comment\n" +
" doSomething();\n" +
" for (let n = 0; n < 500; n++) {\n" +
" if (n % 2 == 1) {\n" +
" console.log(n);\n" +
" console.log(n + 1);\n" +
" }\n" +
" }\n" +
"}";
is(result, correct, "JS has been correctly prettified");
if (scratchpadWin) {
scratchpadWin.close();
scratchpadWin = null;
}
yield helpers.closeToolbar(options);
yield helpers.closeTab(options);
}
/**
* A wrapper for calling Services.ww.[un]registerNotification using promises.
* @return a promise that resolves when the notification service first notifies
* with topic == "domwindowopened".
* The value of the promise is { subject: subject, topic: topic, data: data }
*/
function wwNotifyOnce() {
return new Promise(resolve => {
let onNotify = (subject, topic, data) => {
if (topic == "domwindowopened") {
Services.ww.unregisterNotification(onNotify);
resolve({ subject: subject, topic: topic, data: data });
}
};
Services.ww.registerNotification(onNotify);
});
}
/**
* YET ANOTHER WRAPPER for a place where we are using events as poor-man's
* promises. Perhaps this should be promoted to scratchpad?
*/
function observeOnce(scratchpad) {
return new Promise(resolve => {
let observer = {
onReady: function() {
scratchpad.removeObserver(observer);
resolve();
},
};
scratchpad.addObserver(observer);
});
}

View File

@ -322,11 +322,9 @@ function spawnTest() {
}
},
post: function() {
let deferred = promise.defer();
executeSoon(function() {
deferred.resolve();
return new Promise(resolve => {
executeSoon(resolve);
});
return deferred.promise;
}
},
{

View File

@ -166,18 +166,16 @@ function addTabWithToolbarRunTests(win) {
}
function addWindow(windowOptions) {
let deferred = promise.defer();
return new Promise(resolve => {
let win = OpenBrowserWindow(windowOptions);
let win = OpenBrowserWindow(windowOptions);
// This feels hacky, we should refactor it
whenDelayedStartupFinished(win, function() {
// Would like to get rid of this executeSoon, but without it the url
// (TEST_URI) provided in addTabWithToolbarRunTests hasn't loaded
executeSoon(function() {
deferred.resolve(win);
// This feels hacky, we should refactor it
whenDelayedStartupFinished(win, () => {
// Would like to get rid of this executeSoon, but without it the url
// (TEST_URI) provided in addTabWithToolbarRunTests hasn't loaded
executeSoon(() => {
resolve(win);
});
});
});
return deferred.promise;
}

View File

@ -42,7 +42,7 @@ function test() {
// var assert = require('../testharness/assert');
var promise = require('gcli/util/promise');
var Promise = require('gcli/util/promise').Promise;
var util = require('gcli/util/util');
var resource = require('gcli/types/resource');
var Status = require('gcli/types/types').Status;
@ -162,7 +162,7 @@ function checkPrediction(res, prediction) {
assert.is(typeof value.loadContents, 'function', 'resource for ' + name);
assert.is(typeof value.element, 'object', 'resource for ' + name);
return promise.resolve(res.stringify(value, context)).then(function(strung) {
return Promise.resolve(res.stringify(value, context)).then(function(strung) {
assert.is(strung, name, 'stringify for ' + name);
});
});

View File

@ -42,7 +42,7 @@ function test() {
// var assert = require('../testharness/assert');
var util = require('gcli/util/util');
var promise = require('gcli/util/promise');
var Promise = require('gcli/util/promise').Promise;
var nodetype = require('gcli/types/node');
exports.setup = function(options) {
@ -79,7 +79,7 @@ function forEachType(options, typeSpec, callback) {
var type = types.createType(typeSpec);
var reply = callback(type);
return promise.resolve(reply).then(function(value) {
return Promise.resolve(reply).then(function(value) {
// Clean up
delete typeSpec.name;
delete typeSpec.requisition;
@ -125,7 +125,7 @@ exports.testNullDefault = function(options) {
return forEachType(options, { defaultValue: null }, function(type) {
var reply = type.stringify(null, context);
return promise.resolve(reply).then(function(str) {
return Promise.resolve(reply).then(function(str) {
assert.is(str, '', 'stringify(null) for ' + type.name);
});
});

View File

@ -27,20 +27,6 @@ function whenDelayedStartupFinished(aWindow, aCallback) {
}, "browser-delayed-startup-finished", false);
}
/**
* Creates a regular expression that matches a string. This greatly simplifies
* matching and debugging long strings.
*
* @param {String} text
* Text to convert
* @return {RegExp}
* Regular expression matching text
*/
function getRegexForString(str) {
str = str.replace(/(\.|\\|\/|\(|\)|\[|\]|\*|\+|\?|\$|\^|\|)/g, "\\$1");
return new RegExp(str);
}
/**
* Force GC on shutdown, because it seems that GCLI can outrun the garbage
* collector in some situations, which causes test failures in later tests
@ -53,5 +39,5 @@ registerCleanupFunction(function tearDown() {
});
function asyncTest(generator) {
return () => Task.spawn(generator).then(null, ok.bind(null, false)).then(finish);
return () => Task.spawn(generator).catch(ok.bind(null, false)).then(finish);
}

View File

@ -18,16 +18,16 @@
// A copy of this code exists in firefox mochitests. They should be kept
// in sync. Hence the exports synonym for non AMD contexts.
this.EXPORTED_SYMBOLS = [ 'helpers' ];
var { helpers, gcli, assert } = (function() {
var helpers = {};
this.helpers = helpers;
var TargetFactory = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.TargetFactory;
var require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
var assert = { ok: ok, is: is, log: info };
var util = require('gcli/util/util');
var promise = require('gcli/util/promise');
var Promise = require('gcli/util/promise').Promise;
var cli = require('gcli/cli');
var KeyEvent = require('gcli/util/util').KeyEvent;
var gcli = require('gcli/index');
@ -137,7 +137,7 @@ helpers.addTab = function(url, callback, options) {
var reply = callback.call(null, options);
return promise.resolve(reply).then(null, function(error) {
return Promise.resolve(reply).then(null, function(error) {
ok(false, error);
}).then(function() {
tabbrowser.removeTab(options.tab);
@ -206,7 +206,7 @@ helpers.closeTab = function(options) {
delete options.chromeWindow;
delete options.isFirefox;
return promise.resolve(undefined);
return Promise.resolve(undefined);
};
/**
@ -283,13 +283,13 @@ helpers.handleError = function(ex) {
* @return A promise resolved with the event object when the event first happens
*/
helpers.listenOnce = function(element, event, useCapture) {
var deferred = promise.defer();
var onEvent = function(ev) {
element.removeEventListener(event, onEvent, useCapture);
deferred.resolve(ev);
};
element.addEventListener(event, onEvent, useCapture);
return deferred.promise;
return new Promise(function(resolve, reject) {
var onEvent = function(ev) {
element.removeEventListener(event, onEvent, useCapture);
resolve(ev);
};
element.addEventListener(event, onEvent, useCapture);
}.bind(this));
};
/**
@ -302,13 +302,13 @@ helpers.listenOnce = function(element, event, useCapture) {
* function other parameters are dropped.
*/
helpers.observeOnce = function(topic, ownsWeak=false) {
let deferred = promise.defer();
let resolver = function(subject) {
Services.obs.removeObserver(resolver, topic);
deferred.resolve(subject);
};
Services.obs.addObserver(resolver, topic, ownsWeak);
return deferred.promise;
return new Promise(function(resolve, reject) {
let resolver = function(subject) {
Services.obs.removeObserver(resolver, topic);
resolve(subject);
};
Services.obs.addObserver(resolver, topic, ownsWeak);
}.bind(this));
};
/**
@ -317,22 +317,14 @@ helpers.observeOnce = function(topic, ownsWeak=false) {
*/
helpers.promiseify = function(functionWithLastParamCallback, scope) {
return function() {
let deferred = promise.defer();
let args = [].slice.call(arguments);
args.push(function(callbackParam) {
deferred.resolve(callbackParam);
});
try {
return new Promise(resolve => {
args.push((...results) => {
resolve(results.length > 1 ? results : results[0]);
});
functionWithLastParamCallback.apply(scope, args);
}
catch (ex) {
deferred.resolve(ex);
}
return deferred.promise;
}
});
};
};
/**
@ -353,7 +345,7 @@ helpers.addTabWithToolbar = function(url, callback, options) {
var reply = callback.call(null, innerOptions);
return promise.resolve(reply).then(null, function(error) {
return Promise.resolve(reply).then(null, function(error) {
ok(false, error);
console.error(error);
}).then(function() {
@ -389,8 +381,8 @@ helpers.runTests = function(options, tests) {
info("SETUP");
var setupDone = (tests.setup != null) ?
promise.resolve(tests.setup(options)) :
promise.resolve();
Promise.resolve(tests.setup(options)) :
Promise.resolve();
var testDone = setupDone.then(function() {
return util.promiseEach(testNames, function(testName) {
@ -399,13 +391,13 @@ helpers.runTests = function(options, tests) {
if (typeof action === "function") {
var reply = action.call(tests, options);
return promise.resolve(reply);
return Promise.resolve(reply);
}
else if (Array.isArray(action)) {
return helpers.audit(options, action);
}
return promise.reject("test action '" + testName +
return Promise.reject("test action '" + testName +
"' is not a function or helpers.audit() object");
});
}, recover);
@ -413,8 +405,8 @@ helpers.runTests = function(options, tests) {
return testDone.then(function() {
info("SHUTDOWN");
return (tests.shutdown != null) ?
promise.resolve(tests.shutdown(options)) :
promise.resolve();
Promise.resolve(tests.shutdown(options)) :
Promise.resolve();
}, recover);
};
@ -548,7 +540,7 @@ helpers._createDebugCheck = function(options) {
var hintsPromise = helpers._actual.hints(options);
var predictionsPromise = helpers._actual.predictions(options);
return promise.all(hintsPromise, predictionsPromise).then(function(values) {
return Promise.all(hintsPromise, predictionsPromise).then(function(values) {
var hints = values[0];
var predictions = values[1];
var output = '';
@ -761,7 +753,7 @@ helpers._check = function(options, name, checks) {
});
if (checks == null) {
return promise.resolve();
return Promise.resolve();
}
var outstanding = [];
@ -916,7 +908,7 @@ helpers._check = function(options, name, checks) {
});
}
return promise.all(outstanding).then(function() {
return Promise.all(outstanding).then(function() {
// Ensure the promise resolves to nothing
return undefined;
});
@ -932,7 +924,7 @@ helpers._check = function(options, name, checks) {
helpers._exec = function(options, name, expected) {
var requisition = options.requisition;
if (expected == null) {
return promise.resolve({});
return Promise.resolve({});
}
var origLogErrors = cli.logErrors;
@ -1016,7 +1008,7 @@ helpers._exec = function(options, name, expected) {
if (expected.error) {
cli.logErrors = origLogErrors;
}
return promise.resolve({});
return Promise.resolve({});
}
};
@ -1029,10 +1021,10 @@ helpers._setup = function(options, name, audit) {
}
if (typeof audit.setup === 'function') {
return promise.resolve(audit.setup.call(audit));
return Promise.resolve(audit.setup.call(audit));
}
return promise.reject('\'setup\' property must be a string or a function. Is ' + audit.setup);
return Promise.reject('\'setup\' property must be a string or a function. Is ' + audit.setup);
};
/**
@ -1040,9 +1032,9 @@ helpers._setup = function(options, name, audit) {
*/
helpers._post = function(name, audit, data) {
if (typeof audit.post === 'function') {
return promise.resolve(audit.post.call(audit, data.output, data.text));
return Promise.resolve(audit.post.call(audit, data.output, data.text));
}
return promise.resolve(audit.post);
return Promise.resolve(audit.post);
};
/*
@ -1178,7 +1170,7 @@ helpers.audit = function(options, audits) {
'due to ' + audit.skipRemainingIf.name :
'';
assert.log('Skipped ' + name + ' ' + skipReason);
return promise.resolve(undefined);
return Promise.resolve(undefined);
}
}
@ -1189,13 +1181,13 @@ helpers.audit = function(options, audits) {
if (skip) {
var reason = audit.skipIf.name ? 'due to ' + audit.skipIf.name : '';
assert.log('Skipped ' + name + ' ' + reason);
return promise.resolve(undefined);
return Promise.resolve(undefined);
}
}
if (skipReason != null) {
assert.log('Skipped ' + name + ' ' + skipReason);
return promise.resolve(undefined);
return Promise.resolve(undefined);
}
var start = new Date().getTime();
@ -1210,7 +1202,7 @@ helpers.audit = function(options, audits) {
// a key-sequence (i.e. targeting terminal.js) when there is no terminal
if (chunkLen === -1) {
assert.log('Skipped ' + name + ' ' + skipReason);
return promise.resolve(undefined);
return Promise.resolve(undefined);
}
if (assert.currentTest) {
@ -1269,3 +1261,6 @@ function log(message) {
console.log(message);
}
}
return { helpers: helpers, gcli: gcli, assert: assert };
})();

View File

@ -23,6 +23,7 @@
// <INJECTED SOURCE:END>
var Promise = require('gcli/util/promise').Promise;
var mockCommands = {};
// We use an alias for exports here because this module is used in Firefox
@ -506,29 +507,28 @@ mockCommands.items = [
}
],
exec: function(args, context) {
var deferred;
if (args.method === 'reject') {
deferred = context.defer();
setTimeout(function() {
deferred.reject('rejected promise');
}, 10);
return deferred.promise;
return new Promise(function(resolve, reject) {
setTimeout(function() {
reject('rejected promise');
}, 10);
});
}
if (args.method === 'rejecttyped') {
deferred = context.defer();
setTimeout(function() {
deferred.reject(context.typedData('number', 54));
}, 10);
return deferred.promise;
return new Promise(function(resolve, reject) {
setTimeout(function() {
reject(context.typedData('number', 54));
}, 10);
});
}
if (args.method === 'throwinpromise') {
deferred = context.defer();
setTimeout(function() {
deferred.resolve('should be lost');
}, 10);
return deferred.promise.then(function() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('should be lost');
}, 10);
}).then(function() {
var t = null;
return t.foo;
});
@ -655,17 +655,14 @@ mockCommands.items = [
type: {
name: 'selection',
data: function(context) {
var deferred = context.defer();
var resolve = function() {
deferred.resolve([
'Shalom', 'Namasté', 'Hallo', 'Dydd-da',
'Chào', 'Hej', 'Saluton', 'Sawubona'
]);
};
setTimeout(resolve, 10);
return deferred.promise;
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve([
'Shalom', 'Namasté', 'Hallo', 'Dydd-da',
'Chào', 'Hej', 'Saluton', 'Sawubona'
]);
}, 10);
});
}
}
}

View File

@ -430,6 +430,7 @@ let DebuggerController = {
*/
function ThreadState() {
this._update = this._update.bind(this);
this.interruptedByResumeButton = false;
}
ThreadState.prototype = {
@ -472,14 +473,17 @@ ThreadState.prototype = {
/**
* Update the UI after a thread state change.
*/
_update: function(aEvent) {
// Ignore "interrupted" events, which are generated by the slow script
// dialog and internal events such as setting breakpoints, to avoid UI
// flicker.
if (aEvent == "interrupted") {
_update: function(aEvent, aPacket) {
// Ignore "interrupted" events, to avoid UI flicker. These are generated
// by the slow script dialog and internal events such as setting
// breakpoints. Pressing the resume button does need to be shown, though.
if (aEvent == "paused" &&
aPacket.why.type == "interrupted" &&
!this.interruptedByResumeButton) {
return;
}
this.interruptedByResumeButton = false;
DebuggerView.Toolbar.toggleResumeButtonState(this.activeThread.state);
if (gTarget && (aEvent == "paused" || aEvent == "resumed")) {

View File

@ -129,6 +129,7 @@ ToolbarView.prototype = {
DebuggerController.StackFrames.currentFrameDepth = -1;
DebuggerController.activeThread.resume(warn);
} else {
DebuggerController.ThreadState.interruptedByResumeButton = true;
DebuggerController.activeThread.interrupt();
}
},

View File

@ -156,6 +156,7 @@ skip-if = true # Bug 933950 (leaky test)
[browser_dbg_host-layout.js]
[browser_dbg_iframes.js]
[browser_dbg_instruments-pane-collapse.js]
[browser_dbg_interrupts.js]
[browser_dbg_listaddons.js]
[browser_dbg_listtabs-01.js]
[browser_dbg_listtabs-02.js]

View File

@ -0,0 +1,86 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test if the breakpoints toggle button works as advertised.
*/
const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
function test() {
let gTab, gDebuggee, gPanel, gDebugger;
let gSources, gBreakpoints, gTarget, gResumeButton, gResumeKey, gThreadClient;
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
gTab = aTab;
gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gSources = gDebugger.DebuggerView.Sources;
gBreakpoints = gDebugger.DebuggerController.Breakpoints;
gTarget = gDebugger.gTarget;
gThreadClient = gDebugger.gThreadClient;
gResumeButton = gDebugger.document.getElementById("resume");
gResumeKey = gDebugger.document.getElementById("resumeKey");
waitForSourceShown(gPanel, "-01.js")
.then(() => { gTarget.on("thread-paused", failOnPause); })
.then(addBreakpoints)
.then(() => { gTarget.off("thread-paused", failOnPause); })
.then(testResumeButton)
.then(testResumeKeyboard)
.then(() => closeDebuggerAndFinish(gPanel))
.then(null, aError => {
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
});
});
function failOnPause() {
ok (false, "A pause was sent, but it shouldn't have been");
}
function addBreakpoints() {
return promise.resolve(null)
.then(() => gPanel.addBreakpoint({ url: gSources.values[0], line: 5 }))
.then(() => gPanel.addBreakpoint({ url: gSources.values[1], line: 6 }))
.then(() => gPanel.addBreakpoint({ url: gSources.values[1], line: 7 }))
.then(() => ensureThreadClientState(gPanel, "resumed"));
}
function resume() {
let onceResumed = gTarget.once("thread-resumed");
gThreadClient.resume();
return onceResumed;
}
function testResumeButton() {
info ("Pressing the resume button, expecting a thread-paused");
ok (!gResumeButton.hasAttribute("checked"), "Resume button is not checked");
let oncePaused = gTarget.once("thread-paused");
EventUtils.sendMouseEvent({ type: "mousedown" }, gResumeButton, gDebugger);
return oncePaused
.then(() => {
is (gResumeButton.getAttribute("checked"), "true", "Resume button is checked");
})
.then(() => gThreadClient.resume())
.then(() => ensureThreadClientState(gPanel, "resumed"))
}
function testResumeKeyboard() {
let key = gResumeKey.getAttribute("keycode");
info ("Triggering a pause with keyboard (" + key + "), expecting a thread-paused");
ok (!gResumeButton.hasAttribute("checked"), "Resume button is not checked");
let oncePaused = gTarget.once("thread-paused");
EventUtils.synthesizeKey(key, { }, gDebugger);
return oncePaused
.then(() => {
is (gResumeButton.getAttribute("checked"), "true", "Resume button is checked");
})
.then(() => gThreadClient.resume())
.then(() => ensureThreadClientState(gPanel, "resumed"))
}
}

View File

@ -13,7 +13,7 @@ let gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
Services.prefs.setBoolPref("devtools.debugger.log", false);
let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
let { Promise: promise } = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {});
let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
let { require } = devtools;

View File

@ -6,6 +6,7 @@ const TEST_HOST = 'mochi.test:8888';
let { devtools } = Components.utils.import("resource://gre/modules/devtools/Loader.jsm", {});
const { Eyedropper, EyedropperManager } = devtools.require("devtools/eyedropper/eyedropper");
const { Promise: promise } = devtools.require("resource://gre/modules/Promise.jsm");
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
Services.scriptloader.loadSubScript(testDir + "../../../commandline/test/helpers.js", this);

View File

@ -245,7 +245,7 @@ Toolbox.prototype = {
this._buildDockButtons();
this._buildOptions();
this._buildTabs();
this._buildButtons();
let buttonsPromise = this._buildButtons();
this._addKeysToWindow();
this._addReloadKeys();
this._addToolSwitchingKeys();
@ -255,8 +255,10 @@ Toolbox.prototype = {
this._telemetry.toolOpened("toolbox");
this.selectTool(this._defaultToolId).then(panel => {
this.emit("ready");
deferred.resolve();
buttonsPromise.then(() => {
this.emit("ready");
deferred.resolve();
}, deferred.reject);
});
};
@ -546,17 +548,20 @@ Toolbox.prototype = {
}
if (!this.target.isLocalTab) {
return;
return Promise.resolve();
}
let spec = CommandUtils.getCommandbarSpec("devtools.toolbox.toolbarSpec");
let environment = CommandUtils.createEnvironment(this, '_target');
this._requisition = CommandUtils.createRequisition(environment);
let buttons = CommandUtils.createButtons(spec, this._target,
this.doc, this._requisition);
let container = this.doc.getElementById("toolbox-buttons");
buttons.forEach(container.appendChild.bind(container));
this.setToolboxButtonsVisibility();
return CommandUtils.createRequisition(environment).then(requisition => {
this._requisition = requisition;
return CommandUtils.createButtons(spec, this.target, this.doc,
requisition).then(buttons => {
let container = this.doc.getElementById("toolbox-buttons");
buttons.forEach(container.appendChild.bind(container));
this.setToolboxButtonsVisibility();
});
});
},
/**

View File

@ -13,6 +13,9 @@ const Cc = Components.classes;
//Services.prefs.setBoolPref("devtools.dump.emit", true);
const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
let tempScope = {};
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm", tempScope);
let LayoutHelpers = tempScope.LayoutHelpers;

View File

@ -3,6 +3,8 @@
"use strict";
const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
let {devtools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
let TargetFactory = devtools.TargetFactory;

View File

@ -41,10 +41,17 @@ const Telemetry = require("devtools/shared/telemetry");
// This lazy getter is needed to prevent a require loop
XPCOMUtils.defineLazyGetter(this, "gcli", () => {
let gcli = require("gcli/index");
require("devtools/commandline/commands-index");
gcli.load();
return gcli;
try {
require("devtools/commandline/commands-index");
return require("gcli/index");
}
catch (ex) {
console.error(ex);
}
});
XPCOMUtils.defineLazyGetter(this, "util", () => {
return require("gcli/util/util");
});
Object.defineProperty(this, "ConsoleServiceListener", {
@ -65,9 +72,10 @@ let CommandUtils = {
* Utility to ensure that things are loaded in the correct order
*/
createRequisition: function(environment) {
let temp = gcli.createDisplay; // Ensure GCLI is loaded
let Requisition = require("gcli/cli").Requisition
return new Requisition({ environment: environment });
return gcli.load().then(() => {
let Requisition = require("gcli/cli").Requisition
return new Requisition({ environment: environment });
});
},
/**
@ -80,36 +88,23 @@ let CommandUtils = {
},
/**
* A toolbarSpec is an array of buttonSpecs. A buttonSpec is an array of
* strings each of which is a GCLI command (including args if needed).
* A toolbarSpec is an array of strings each of which is a GCLI command.
*
* Warning: this method uses the unload event of the window that owns the
* buttons that are of type checkbox. this means that we don't properly
* unregister event handlers until the window is destroyed.
*/
createButtons: function(toolbarSpec, target, document, requisition) {
let reply = [];
toolbarSpec.forEach(function(buttonSpec) {
let button = document.createElement("toolbarbutton");
reply.push(button);
if (typeof buttonSpec == "string") {
buttonSpec = { typed: buttonSpec };
}
return util.promiseEach(toolbarSpec, typed => {
// Ask GCLI to parse the typed string (doesn't execute it)
requisition.update(buttonSpec.typed);
return requisition.update(typed).then(() => {
let button = document.createElement("toolbarbutton");
// Ignore invalid commands
let command = requisition.commandAssignment.value;
if (command == null) {
// TODO: Have a broken icon
// button.icon = 'Broken';
button.setAttribute("label", "X");
button.setAttribute("tooltip", "Unknown command: " + buttonSpec.typed);
button.setAttribute("disabled", "true");
}
else {
// Ignore invalid commands
let command = requisition.commandAssignment.value;
if (command == null) {
throw new Error("No command '" + typed + "'");
}
if (command.buttonId != null) {
button.id = command.buttonId;
}
@ -123,22 +118,14 @@ let CommandUtils = {
button.setAttribute("tooltiptext", command.description);
}
button.addEventListener("click", function() {
requisition.update(buttonSpec.typed);
//if (requisition.getStatus() == Status.VALID) {
requisition.exec();
/*
}
else {
console.error('incomplete commands not yet supported');
}
*/
button.addEventListener("click", () => {
requisition.updateExec(typed);
}, false);
// Allow the command button to be toggleable
if (command.state) {
button.setAttribute("autocheck", false);
let onChange = function(event, eventTab) {
let onChange = (event, eventTab) => {
if (eventTab == target.tab) {
if (command.state.isChecked(target)) {
button.setAttribute("checked", true);
@ -150,16 +137,16 @@ let CommandUtils = {
};
command.state.onChange(target, onChange);
onChange(null, target.tab);
document.defaultView.addEventListener("unload", function() {
document.defaultView.addEventListener("unload", () => {
command.state.offChange(target, onChange);
}, false);
}
}
requisition.clear();
return button;
});
});
requisition.update('');
return reply;
},
/**
@ -382,56 +369,58 @@ DeveloperToolbar.prototype.show = function(focus) {
this._doc.getElementById("Tools:DevToolbar").setAttribute("checked", "true");
this.display = gcli.createDisplay({
contentDocument: this._chromeWindow.getBrowser().contentDocument,
chromeDocument: this._doc,
chromeWindow: this._chromeWindow,
hintElement: this.tooltipPanel.hintElement,
inputElement: this._input,
completeElement: this._doc.querySelector(".gclitoolbar-complete-node"),
backgroundElement: this._doc.querySelector(".gclitoolbar-stack-node"),
outputDocument: this.outputPanel.document,
environment: CommandUtils.createEnvironment(this, "target"),
tooltipClass: "gcliterm-tooltip",
eval: null,
scratchpad: null
return gcli.load().then(() => {
this.display = gcli.createDisplay({
contentDocument: this._chromeWindow.getBrowser().contentDocument,
chromeDocument: this._doc,
chromeWindow: this._chromeWindow,
hintElement: this.tooltipPanel.hintElement,
inputElement: this._input,
completeElement: this._doc.querySelector(".gclitoolbar-complete-node"),
backgroundElement: this._doc.querySelector(".gclitoolbar-stack-node"),
outputDocument: this.outputPanel.document,
environment: CommandUtils.createEnvironment(this, "target"),
tooltipClass: "gcliterm-tooltip",
eval: null,
scratchpad: null
});
this.display.focusManager.addMonitoredElement(this.outputPanel._frame);
this.display.focusManager.addMonitoredElement(this._element);
this.display.onVisibilityChange.add(this.outputPanel._visibilityChanged,
this.outputPanel);
this.display.onVisibilityChange.add(this.tooltipPanel._visibilityChanged,
this.tooltipPanel);
this.display.onOutput.add(this.outputPanel._outputChanged, this.outputPanel);
let tabbrowser = this._chromeWindow.getBrowser();
tabbrowser.tabContainer.addEventListener("TabSelect", this, false);
tabbrowser.tabContainer.addEventListener("TabClose", this, false);
tabbrowser.addEventListener("load", this, true);
tabbrowser.addEventListener("beforeunload", this, true);
this._initErrorsCount(tabbrowser.selectedTab);
this._devtoolsUnloaded = this._devtoolsUnloaded.bind(this);
this._devtoolsLoaded = this._devtoolsLoaded.bind(this);
Services.obs.addObserver(this._devtoolsUnloaded, "devtools-unloaded", false);
Services.obs.addObserver(this._devtoolsLoaded, "devtools-loaded", false);
this._element.hidden = false;
if (focus) {
this._input.focus();
}
this._notify(NOTIFICATIONS.SHOW);
if (!DeveloperToolbar.introShownThisSession) {
this.display.maybeShowIntro();
DeveloperToolbar.introShownThisSession = true;
}
this._showPromise = null;
});
this.display.focusManager.addMonitoredElement(this.outputPanel._frame);
this.display.focusManager.addMonitoredElement(this._element);
this.display.onVisibilityChange.add(this.outputPanel._visibilityChanged,
this.outputPanel);
this.display.onVisibilityChange.add(this.tooltipPanel._visibilityChanged,
this.tooltipPanel);
this.display.onOutput.add(this.outputPanel._outputChanged, this.outputPanel);
let tabbrowser = this._chromeWindow.getBrowser();
tabbrowser.tabContainer.addEventListener("TabSelect", this, false);
tabbrowser.tabContainer.addEventListener("TabClose", this, false);
tabbrowser.addEventListener("load", this, true);
tabbrowser.addEventListener("beforeunload", this, true);
this._initErrorsCount(tabbrowser.selectedTab);
this._devtoolsUnloaded = this._devtoolsUnloaded.bind(this);
this._devtoolsLoaded = this._devtoolsLoaded.bind(this);
Services.obs.addObserver(this._devtoolsUnloaded, "devtools-unloaded", false);
Services.obs.addObserver(this._devtoolsLoaded, "devtools-loaded", false);
this._element.hidden = false;
if (focus) {
this._input.focus();
}
this._notify(NOTIFICATIONS.SHOW);
if (!DeveloperToolbar.introShownThisSession) {
this.display.maybeShowIntro();
DeveloperToolbar.introShownThisSession = true;
}
this._showPromise = null;
});
});

View File

@ -6,24 +6,27 @@
const TEST_URI = "data:text/html;charset=utf-8,<p>Tooltip Tests</p>";
function test() {
addTab(TEST_URI, function(browser, tab) {
info("Starting browser_toolbar_tooltip.js");
openTest();
addTab(TEST_URI, function() {
Task.spawn(runTest).catch(err => {
ok(false, ex);
console.error(ex);
}).then(finish);
});
}
function openTest() {
function* runTest() {
info("Starting browser_toolbar_tooltip.js");
ok(!DeveloperToolbar.visible, "DeveloperToolbar is not visible in runTest");
oneTimeObserve(DeveloperToolbar.NOTIFICATIONS.SHOW, catchFail(runTest));
let showPromise = observeOnce(DeveloperToolbar.NOTIFICATIONS.SHOW);
document.getElementById("Tools:DevToolbar").doCommand();
}
yield showPromise;
function runTest() {
let tooltipPanel = DeveloperToolbar.tooltipPanel;
DeveloperToolbar.display.focusManager.helpRequest();
DeveloperToolbar.display.inputter.setInput('help help');
yield DeveloperToolbar.display.inputter.setInput('help help');
DeveloperToolbar.display.inputter.setCursor({ start: 'help help'.length });
is(tooltipPanel._dimensions.start, 'help '.length,
@ -44,11 +47,19 @@ function runTest() {
is(tooltipPanel._dimensions.start, 0,
'search param start, when cursor at start');
ok(getLeftMargin() > 9, 'tooltip offset, when cursor at start')
finish();
}
function getLeftMargin() {
let style = DeveloperToolbar.tooltipPanel._panel.style.marginLeft;
return parseInt(style.slice(0, -2), 10);
}
function observeOnce(topic, ownsWeak=false) {
return new Promise(function(resolve, reject) {
let resolver = function(subject) {
Services.obs.removeObserver(resolver, topic);
resolve(subject);
};
Services.obs.addObserver(resolver, topic, ownsWeak);
}.bind(this));
}

View File

@ -6,7 +6,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id={BUGNUMBER}
-->
<window title="Mozilla Bug {BUGNUMBER}"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"/>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<!-- test code goes here -->
<script type="application/javascript">

View File

@ -16,7 +16,7 @@
'use strict';
var promise = require('./util/promise');
var Promise = require('./util/promise').Promise;
var centralCanon = require('./commands/commands').centralCanon;
var connectors = require('./connectors/connectors');
var converters = require('./converters/converters');
@ -117,7 +117,7 @@ exports.getApi = function() {
// And load the new items
try {
var loader = loadableModules[name];
return promise.resolve(loader(name)).then(function(newModule) {
return Promise.resolve(loader(name)).then(function(newModule) {
if (existingModule === newModule) {
return;
}
@ -138,10 +138,12 @@ exports.getApi = function() {
}
catch (ex) {
console.error(ex);
return promise.reject('Failure when loading \'' + name + '\'');
return Promise.reject('Failure when loading \'' + name + '\'');
}
};
var pendingChanges = false;
var api = {
addCommand: function(item) { return canon.addCommand(item); },
removeCommand: function(item) { return canon.removeCommand(item); },
@ -175,7 +177,10 @@ exports.getApi = function() {
}
loadableModules[name] = options.loader;
if (!options.delayedLoad) {
if (options.delayedLoad) {
pendingChanges = true;
}
else {
loadModule(name).then(null, console.error);
}
});
@ -187,17 +192,24 @@ exports.getApi = function() {
},
load: function() {
if (!pendingChanges) {
return Promise.resolve();
}
// clone loadedModules, so we can remove what is left at the end
var modules = Object.keys(loadedModules).map(function(name) {
return loadedModules[name];
});
Object.keys(loadableModules).forEach(function(name) {
var promises = Object.keys(loadableModules).map(function(name) {
delete modules[name];
loadModule(name).then(null, console.error);
return loadModule(name);
});
Object.keys(modules).forEach(unloadModule);
pendingChanges = false;
return Promise.all(promises);
}
};

View File

@ -16,7 +16,7 @@
'use strict';
var promise = require('./util/promise');
var Promise = require('./util/promise').Promise;
var util = require('./util/util');
var host = require('./util/host');
var l10n = require('./util/l10n');
@ -40,7 +40,7 @@ var TrueNamedArgument = require('./types/types').TrueNamedArgument;
var MergedArgument = require('./types/types').MergedArgument;
var ScriptArgument = require('./types/types').ScriptArgument;
var RESOLVED = promise.resolve(undefined);
var RESOLVED = Promise.resolve(undefined);
/**
* This is a list of the known command line components to enable certain
@ -196,7 +196,7 @@ Assignment.prototype.getPredictionRanked = function(context, rank) {
}
if (this.isInName()) {
return promise.resolve(undefined);
return Promise.resolve(undefined);
}
return this.getPredictions(context).then(function(predictions) {
@ -550,7 +550,7 @@ Object.defineProperty(Requisition.prototype, 'executionContext', {
if (this._executionContext == null) {
this._executionContext = {
defer: function() {
return promise.defer();
return Promise.defer();
},
typedData: function(type, data) {
return {
@ -607,7 +607,7 @@ Object.defineProperty(Requisition.prototype, 'conversionContext', {
if (this._conversionContext == null) {
this._conversionContext = {
defer: function() {
return promise.defer();
return Promise.defer();
},
createView: view.createView,
@ -861,7 +861,7 @@ Requisition.prototype.toCanonicalString = function() {
}
var val = assignment.param.type.stringify(assignment.value, ctx);
return promise.resolve(val).then(function(str) {
return Promise.resolve(val).then(function(str) {
return ' ' + str;
}.bind(this));
}.bind(this));
@ -1017,7 +1017,7 @@ Requisition.prototype.setAssignment = function(assignment, arg, options) {
this._endChangeCheckOrder(updateId);
}
return promise.resolve(undefined);
return Promise.resolve(undefined);
}.bind(this);
if (arg == null) {
@ -1210,7 +1210,7 @@ Requisition.prototype.getStateData = function(start, rank) {
var context = this.executionContext;
var predictionPromise = (typed.trim().length !== 0) ?
current.getPredictionRanked(context, rank) :
promise.resolve(null);
Promise.resolve(null);
return predictionPromise.then(function(prediction) {
// directTabText is for when the current input is a prefix of the completion
@ -1349,7 +1349,7 @@ Requisition.prototype._addSpace = function(assignment) {
return this.setAssignment(assignment, arg);
}
else {
return promise.resolve(undefined);
return Promise.resolve(undefined);
}
};
@ -1426,7 +1426,7 @@ Requisition.prototype.complete = function(cursor, rank) {
outstanding.push(assignPromise);
}
return promise.all(outstanding).then(function() {
return Promise.all(outstanding).then(function() {
return true;
}.bind(this));
}.bind(this));
@ -1438,10 +1438,10 @@ Requisition.prototype.complete = function(cursor, rank) {
Requisition.prototype.decrement = function(assignment) {
var ctx = this.executionContext;
var val = assignment.param.type.decrement(assignment.value, ctx);
return promise.resolve(val).then(function(replacement) {
return Promise.resolve(val).then(function(replacement) {
if (replacement != null) {
var val = assignment.param.type.stringify(replacement, ctx);
return promise.resolve(val).then(function(str) {
return Promise.resolve(val).then(function(str) {
var arg = assignment.arg.beget({ text: str });
return this.setAssignment(assignment, arg);
}.bind(this));
@ -1455,10 +1455,10 @@ Requisition.prototype.decrement = function(assignment) {
Requisition.prototype.increment = function(assignment) {
var ctx = this.executionContext;
var val = assignment.param.type.increment(assignment.value, ctx);
return promise.resolve(val).then(function(replacement) {
return Promise.resolve(val).then(function(replacement) {
if (replacement != null) {
var val = assignment.param.type.stringify(replacement, ctx);
return promise.resolve(val).then(function(str) {
return Promise.resolve(val).then(function(str) {
var arg = assignment.arg.beget({ text: str });
return this.setAssignment(assignment, arg);
}.bind(this));
@ -2046,7 +2046,7 @@ Requisition.prototype.exec = function(options) {
var ex = new Error(this.getStatusMessage());
// We only reject a call to exec if GCLI breaks. Errors with commands are
// exposed in the 'error' status of the Output object
return promise.resolve(onError(ex)).then(function(output) {
return Promise.resolve(onError(ex)).then(function(output) {
this.clear();
return output;
}.bind(this));
@ -2060,7 +2060,7 @@ Requisition.prototype.exec = function(options) {
catch (ex) {
var data = (typeof ex.message === 'string' && ex.stack != null) ?
ex.message : ex;
return promise.resolve(onError(data, ex));
return Promise.resolve(onError(data, ex));
}
finally {
this.clear();
@ -2099,8 +2099,9 @@ function Output(options) {
this.error = false;
this.start = new Date();
this._deferred = promise.defer();
this.promise = this._deferred.promise;
this.promise = new Promise(function(resolve, reject) {
this._resolve = resolve;
}.bind(this));
}
/**
@ -2128,7 +2129,7 @@ Output.prototype.complete = function(data, error) {
throw new Error('No type from output of ' + this.typed);
}
this._deferred.resolve();
this._resolve();
};
/**

View File

@ -16,7 +16,7 @@
'use strict';
var promise = require('../util/promise');
var Promise = require('../util/promise').Promise;
var l10n = require('../util/l10n');
var cli = require('../cli');
@ -68,13 +68,13 @@ function getHelpManData(commandData, context) {
input = l10n.lookupFormat('helpManOptional');
/*
var val = param.type.stringify(param.defaultValue);
input = promise.resolve(val).then(function(defaultValue) {
input = Promise.resolve(val).then(function(defaultValue) {
return l10n.lookupFormat('helpManDefault', [ defaultValue ]);
}.bind(this));
*/
}
return promise.resolve(input).then(function(defaultDescr) {
return Promise.resolve(input).then(function(defaultDescr) {
return '(' + (param.type.name || param.type) + ', ' + defaultDescr + ')';
}.bind(this));
},

View File

@ -18,6 +18,7 @@
var l10n = require('../util/l10n');
var settings = require('../settings');
var Promise = require('../util/promise').Promise;
/**
* Format a list of settings for display
@ -116,18 +117,16 @@ var prefList = {
],
returnType: 'prefsData',
exec: function(args, context) {
var deferred = context.defer();
// This can be slow, get out of the way of the main thread
setTimeout(function() {
var prefsData = {
settings: settings.getAll(args.search),
search: args.search
};
deferred.resolve(prefsData);
}.bind(this), 10);
return deferred.promise;
return new Promise(function(resolve, reject) {
// This can be slow, get out of the way of the main thread
setTimeout(function() {
var prefsData = {
settings: settings.getAll(args.search),
search: args.search
};
resolve(prefsData);
}.bind(this), 10);
});
}
};

View File

@ -16,7 +16,7 @@
'use strict';
var promise = require('../util/promise');
var Promise = require('../util/promise').Promise;
/**
* This is where we cache the connectors that we know about
@ -30,7 +30,7 @@ var connectors = {};
* name: 'foo',
*
* connect: function(url) {
* return promise.resolve(new FooConnection(url));
* return Promise.resolve(new FooConnection(url));
* }
* };
*/
@ -106,7 +106,7 @@ Connection.prototype.call = function(feature, data) {
* common route back to being connected once this has been called
*/
Connection.prototype.disconnect = function() {
return promise.resolve();
return Promise.resolve();
};
exports.Connection = Connection;

View File

@ -21,7 +21,7 @@ var Cu = require('chrome').Cu;
var debuggerSocketConnect = Cu.import('resource://gre/modules/devtools/dbg-client.jsm', {}).debuggerSocketConnect;
var DebuggerClient = Cu.import('resource://gre/modules/devtools/dbg-client.jsm', {}).DebuggerClient;
var promise = require('../util/promise');
var Promise = require('../util/promise').Promise;
var Connection = require('./connectors').Connection;
/**
@ -71,45 +71,38 @@ RdpConnection.create = function(url) {
this._emit = this._emit.bind(this);
var deferred = promise.defer();
this.transport = debuggerSocketConnect(this.host, this.port);
this.client = new DebuggerClient(this.transport);
this.client.connect(function() {
this.client.listTabs(function(response) {
this.actor = response.gcliActor;
deferred.resolve();
return new Promise(function(resolve, reject) {
this.transport = debuggerSocketConnect(this.host, this.port);
this.client = new DebuggerClient(this.transport);
this.client.connect(function() {
this.client.listTabs(function(response) {
this.actor = response.gcliActor;
resolve();
}.bind(this));
}.bind(this));
}.bind(this));
return deferred.promise;
};
RdpConnection.prototype = Object.create(Connection.prototype);
RdpConnection.prototype.call = function(command, data) {
var deferred = promise.defer();
return new Promise(function(resolve, reject) {
var request = { to: this.actor, type: command, data: data };
var request = { to: this.actor, type: command, data: data };
this.client.request(request, function(response) {
deferred.resolve(response.commandSpecs);
});
return deferred.promise;
this.client.request(request, function(response) {
resolve(response.commandSpecs);
});
}.bind(this));
};
RdpConnection.prototype.disconnect = function() {
var deferred = promise.defer();
return new Promise(function(resolve, reject) {
this.client.close(function() {
resolve();
});
this.client.close(function() {
deferred.resolve();
});
delete this._emit;
return deferred.promise;
delete this._emit;
}.bind(this));
};
@ -126,8 +119,9 @@ function Request(actor, typed, args) {
requestId: 'id-' + Request._nextRequestId++,
};
this._deferred = promise.defer();
this.promise = this._deferred.promise;
this.promise = new Promise(function(resolve, reject) {
this._resolve = resolve;
}.bind(this));
}
Request._nextRequestId = 0;
@ -139,7 +133,7 @@ Request._nextRequestId = 0;
* @param data the data itself
*/
Request.prototype.complete = function(error, type, data) {
this._deferred.resolve({
this._resolve({
error: error,
type: type,
data: data

View File

@ -18,7 +18,7 @@
'use strict';
var promise = require('../util/promise');
var Promise = require('../util/promise').Promise;
var host = require('../util/host');
var fileparser = require('../util/fileparser');
@ -130,7 +130,7 @@ Remoter.prototype.exposed = {
return this.requisition.update(typed).then(function() {
var assignment = this.requisition.getAssignment(param);
return promise.resolve(assignment.predictions).then(function(predictions) {
return Promise.resolve(assignment.predictions).then(function(predictions) {
return {
status: assignment.getStatus().toString(),
message: assignment.message,

View File

@ -16,7 +16,7 @@
'use strict';
var promise = require('../util/promise');
var Promise = require('../util/promise').Promise;
var util = require('../util/util');
var host = require('../util/host');
@ -233,7 +233,7 @@ function getFallbackConverter(from, to) {
exports.convert = function(data, from, to, conversionContext) {
try {
if (from === to) {
return promise.resolve(data);
return Promise.resolve(data);
}
var converter = getConverter(from, to);

View File

@ -17,7 +17,7 @@
'use strict';
var util = require('../util/util');
var promise = require('../util/promise');
var Promise = require('../util/promise').Promise;
var domtemplate = require('../util/domtemplate');
var host = require('../util/host');
@ -28,7 +28,7 @@ var CommandAssignment = require('../cli').CommandAssignment;
var fields = require('../fields/fields');
var intro = require('../ui/intro');
var RESOLVED = promise.resolve(true);
var RESOLVED = Promise.resolve(true);
/**
* Various ways in which we need to manipulate the caret/selection position.
@ -100,7 +100,7 @@ var commandLanguage = exports.commandLanguage = {
}
return commandHtmlPromise.then(function(commandHtml) {
this.commandDom = util.toDom(this.document, commandHtml);
this.commandDom = host.toDom(this.document, commandHtml);
this.requisition.commandOutputManager.onOutput.add(this.outputted, this);
var mapping = cli.getMapping(this.requisition.executionContext);
@ -248,7 +248,7 @@ var commandLanguage = exports.commandLanguage = {
}.bind(this));
}
return promise.resolve(false);
return Promise.resolve(false);
},
/**
@ -263,7 +263,7 @@ var commandLanguage = exports.commandLanguage = {
}.bind(this));
}
return promise.resolve(false);
return Promise.resolve(false);
},
/**
@ -272,7 +272,7 @@ var commandLanguage = exports.commandLanguage = {
handleReturn: function(input) {
// Deny RETURN unless the command might work
if (this.requisition.status !== Status.VALID) {
return promise.resolve(false);
return Promise.resolve(false);
}
this.terminal.history.add(input);

View File

@ -17,9 +17,9 @@
'use strict';
var util = require('../util/util');
var promise = require('../util/promise');
var Promise = require('../util/promise').Promise;
var RESOLVED = promise.resolve(true);
var RESOLVED = Promise.resolve(true);
/**
* This is where we cache the languages that we know about
@ -48,11 +48,11 @@ var baseLanguage = {
caretMoved: function() {},
handleUpArrow: function() {
return promise.resolve(false);
return Promise.resolve(false);
},
handleDownArrow: function() {
return promise.resolve(false);
return Promise.resolve(false);
},
handleTab: function() {
@ -96,7 +96,7 @@ var baseLanguage = {
},
getCompleterTemplateData: function() {
return promise.resolve({
return Promise.resolve({
statusMarkup: [
{
string: this.terminal.inputElement.value.replace(/ /g, '\u00a0'), // i.e. &nbsp;
@ -163,11 +163,11 @@ exports.createLanguage = function(name, terminal) {
if (typeof newInstance.constructor === 'function') {
var reply = newInstance.constructor(terminal);
return promise.resolve(reply).then(function() {
return Promise.resolve(reply).then(function() {
return newInstance;
});
}
else {
return promise.resolve(newInstance);
return Promise.resolve(newInstance);
}
};

View File

@ -17,6 +17,7 @@
'use strict';
var util = require('../util/util');
var host = require('../util/host');
var domtemplate = require('../util/domtemplate');
var completerHtml =
@ -68,7 +69,7 @@ function Completer(components) {
}
}
this.template = util.toDom(this.document, completerHtml);
this.template = host.toDom(this.document, completerHtml);
// We want the spans to line up without the spaces in the template
util.removeWhitespace(this.template, true);
@ -111,6 +112,10 @@ Completer.prototype.update = function(ev) {
this.choice = (ev && ev.choice != null) ? ev.choice : 0;
this._getCompleterTemplateData().then(function(data) {
if (this.template == null) {
return; // destroy() has been called
}
var template = this.template.cloneNode(true);
domtemplate.template(template, data, { stack: 'completer.html' });

View File

@ -16,14 +16,14 @@
'use strict';
var promise = require('../util/promise');
var Promise = require('../util/promise').Promise;
var util = require('../util/util');
var KeyEvent = require('../util/util').KeyEvent;
var Status = require('../types/types').Status;
var History = require('../ui/history').History;
var RESOLVED = promise.resolve(true);
var RESOLVED = Promise.resolve(true);
/**
* A wrapper to take care of the functions concerning an input element

View File

@ -17,6 +17,7 @@
'use strict';
var util = require('../util/util');
var host = require('../util/host');
var domtemplate = require('../util/domtemplate');
var CommandAssignment = require('../cli').CommandAssignment;
@ -62,7 +63,7 @@ function Tooltip(options, components) {
// We cache the fields we create so we can destroy them later
this.fields = [];
this.template = util.toDom(this.document, tooltipHtml);
this.template = host.toDom(this.document, tooltipHtml);
this.templateOptions = { blankNullUndefined: true, stack: 'tooltip.html' };
this.inputter.onChoiceChange.add(this.choiceChanged, this);

View File

@ -16,7 +16,7 @@
'use strict';
var promise = require('../util/promise');
var Promise = require('../util/promise').Promise;
var ArrayConversion = require('./types').ArrayConversion;
var ArrayArgument = require('./types').ArrayArgument;
@ -69,7 +69,7 @@ exports.items = [
}.bind(this);
var conversionPromises = arg.getArguments().map(subArgParse);
return promise.all(conversionPromises).then(function(conversions) {
return Promise.all(conversionPromises).then(function(conversions) {
return new ArrayConversion(conversions, arg);
});
},

View File

@ -16,7 +16,7 @@
'use strict';
var promise = require('../util/promise');
var Promise = require('../util/promise').Promise;
var Status = require('./types').Status;
var Conversion = require('./types').Conversion;
var BlankArgument = require('./types').BlankArgument;
@ -40,10 +40,10 @@ exports.items = [
parse: function(arg, context) {
if (arg.type === 'TrueNamedArgument') {
return promise.resolve(new Conversion(true, arg));
return Promise.resolve(new Conversion(true, arg));
}
if (arg.type === 'FalseNamedArgument') {
return promise.resolve(new Conversion(false, arg));
return Promise.resolve(new Conversion(false, arg));
}
return SelectionType.prototype.parse.call(this, arg, context);
},
@ -57,7 +57,7 @@ exports.items = [
getBlank: function(context) {
return new Conversion(false, new BlankArgument(), Status.VALID, '',
promise.resolve(this.lookup));
Promise.resolve(this.lookup));
}
}
];

View File

@ -16,7 +16,7 @@
'use strict';
var promise = require('../util/promise');
var Promise = require('../util/promise').Promise;
var l10n = require('../util/l10n');
var spell = require('../util/spell');
var SelectionType = require('./selection').SelectionType;
@ -48,7 +48,7 @@ exports.items = [
}
else {
var message = l10n.lookup('cliUnusedArg');
return promise.resolve(new Conversion(undefined, arg, Status.ERROR, message));
return Promise.resolve(new Conversion(undefined, arg, Status.ERROR, message));
}
}
},
@ -78,7 +78,7 @@ exports.items = [
parse: function(arg, context) {
var conversion = exports.parse(context, arg, this.allowNonExec);
return promise.resolve(conversion);
return Promise.resolve(conversion);
}
}
];

View File

@ -16,7 +16,7 @@
'use strict';
var promise = require('../util/promise');
var Promise = require('../util/promise').Promise;
var l10n = require('../util/l10n');
var Status = require('./types').Status;
var Conversion = require('./types').Conversion;
@ -174,7 +174,7 @@ exports.items = [
var value;
if (arg.text.replace(/\s/g, '').length === 0) {
return promise.resolve(new Conversion(undefined, arg, Status.INCOMPLETE, ''));
return Promise.resolve(new Conversion(undefined, arg, Status.INCOMPLETE, ''));
}
// Lots of room for improvement here: 1h ago, in two days, etc.
@ -220,11 +220,11 @@ exports.items = [
if (isNaN(value.getTime())) {
var msg = l10n.lookupFormat('typesDateNan', [ arg.text ]);
return promise.resolve(new Conversion(undefined, arg, Status.ERROR, msg));
return Promise.resolve(new Conversion(undefined, arg, Status.ERROR, msg));
}
}
return promise.resolve(new Conversion(value, arg));
return Promise.resolve(new Conversion(value, arg));
},
decrement: function(value, context) {

View File

@ -16,7 +16,7 @@
'use strict';
var promise = require('../util/promise');
var Promise = require('../util/promise').Promise;
var Conversion = require('./types').Conversion;
var Status = require('./types').Status;
@ -84,7 +84,7 @@ exports.items = [
if (typeof type.parse !== 'function') {
type = this.types.createType(type);
}
return promise.resolve(type);
return Promise.resolve(type);
},
// DelegateType is designed to be inherited from, so DelegateField needs a way
@ -147,7 +147,7 @@ exports.items = [
},
parse: function(arg, context) {
return promise.resolve(new Conversion(undefined, arg));
return Promise.resolve(new Conversion(undefined, arg));
}
}
];

View File

@ -16,7 +16,7 @@
'use strict';
var promise = require('../util/promise');
var Promise = require('../util/promise').Promise;
var l10n = require('../util/l10n');
var Conversion = require('./types').Conversion;
@ -86,15 +86,15 @@ JavascriptType.prototype.parse = function(arg, context) {
// No input is undefined
if (typed === '') {
return promise.resolve(new Conversion(undefined, arg, Status.INCOMPLETE));
return Promise.resolve(new Conversion(undefined, arg, Status.INCOMPLETE));
}
// Just accept numbers
if (!isNaN(parseFloat(typed)) && isFinite(typed)) {
return promise.resolve(new Conversion(typed, arg));
return Promise.resolve(new Conversion(typed, arg));
}
// Just accept constants like true/false/null/etc
if (typed.trim().match(/(null|undefined|NaN|Infinity|true|false)/)) {
return promise.resolve(new Conversion(typed, arg));
return Promise.resolve(new Conversion(typed, arg));
}
// Analyze the input text and find the beginning of the last part that
@ -103,19 +103,19 @@ JavascriptType.prototype.parse = function(arg, context) {
// There was an error analyzing the string.
if (beginning.err) {
return promise.resolve(new Conversion(typed, arg, Status.ERROR, beginning.err));
return Promise.resolve(new Conversion(typed, arg, Status.ERROR, beginning.err));
}
// If the current state is ParseState.COMPLEX, then we can't do completion.
// so bail out now
if (beginning.state === ParseState.COMPLEX) {
return promise.resolve(new Conversion(typed, arg));
return Promise.resolve(new Conversion(typed, arg));
}
// If the current state is not ParseState.NORMAL, then we are inside of a
// string which means that no completion is possible.
if (beginning.state !== ParseState.NORMAL) {
return promise.resolve(new Conversion(typed, arg, Status.INCOMPLETE, ''));
return Promise.resolve(new Conversion(typed, arg, Status.INCOMPLETE, ''));
}
var completionPart = typed.substring(beginning.startPos);
@ -130,18 +130,18 @@ JavascriptType.prototype.parse = function(arg, context) {
// We can't complete on null.foo, so bail out
if (scope == null) {
return promise.resolve(new Conversion(typed, arg, Status.ERROR,
return Promise.resolve(new Conversion(typed, arg, Status.ERROR,
l10n.lookup('jstypeParseScope')));
}
if (prop === '') {
return promise.resolve(new Conversion(typed, arg, Status.INCOMPLETE, ''));
return Promise.resolve(new Conversion(typed, arg, Status.INCOMPLETE, ''));
}
// Check if prop is a getter function on 'scope'. Functions can change
// other stuff so we can't execute them to get the next object. Stop here.
if (this._isSafeProperty(scope, prop)) {
return promise.resolve(new Conversion(typed, arg));
return Promise.resolve(new Conversion(typed, arg));
}
try {
@ -151,7 +151,7 @@ JavascriptType.prototype.parse = function(arg, context) {
// It would be nice to be able to report this error in some way but
// as it can happen just when someone types '{sessionStorage.', it
// almost doesn't really count as an error, so we ignore it
return promise.resolve(new Conversion(typed, arg, Status.VALID, ''));
return Promise.resolve(new Conversion(typed, arg, Status.VALID, ''));
}
}
}
@ -162,24 +162,24 @@ JavascriptType.prototype.parse = function(arg, context) {
// If the reason we just stopped adjusting the scope was a non-simple string,
// then we're not sure if the input is valid or invalid, so accept it
if (prop && !prop.match(/^[0-9A-Za-z]*$/)) {
return promise.resolve(new Conversion(typed, arg));
return Promise.resolve(new Conversion(typed, arg));
}
// However if the prop was a simple string, it is an error
if (scope == null) {
var msg = l10n.lookupFormat('jstypeParseMissing', [ prop ]);
return promise.resolve(new Conversion(typed, arg, Status.ERROR, msg));
return Promise.resolve(new Conversion(typed, arg, Status.ERROR, msg));
}
// If the thing we're looking for isn't a simple string, then we're not going
// to find it, but we're not sure if it's valid or invalid, so accept it
if (!matchProp.match(/^[0-9A-Za-z]*$/)) {
return promise.resolve(new Conversion(typed, arg));
return Promise.resolve(new Conversion(typed, arg));
}
// Skip Iterators and Generators.
if (this._isIteratorOrGenerator(scope)) {
return promise.resolve(new Conversion(typed, arg));
return Promise.resolve(new Conversion(typed, arg));
}
var matchLen = matchProp.length;
@ -219,7 +219,7 @@ JavascriptType.prototype.parse = function(arg, context) {
}
}
catch (ex) {
return promise.resolve(new Conversion(typed, arg, Status.INCOMPLETE, ''));
return Promise.resolve(new Conversion(typed, arg, Status.INCOMPLETE, ''));
}
// Convert to an array for sorting, and while we're at it, note if we got
@ -313,8 +313,8 @@ JavascriptType.prototype.parse = function(arg, context) {
predictions = [];
}
return promise.resolve(new Conversion(typed, arg, status, message,
promise.resolve(predictions)));
return Promise.resolve(new Conversion(typed, arg, status, message,
Promise.resolve(predictions)));
};
/**

View File

@ -16,7 +16,7 @@
'use strict';
var promise = require('../util/promise');
var Promise = require('../util/promise').Promise;
var Highlighter = require('../util/host').Highlighter;
var l10n = require('../util/l10n');
var util = require('../util/util');
@ -151,7 +151,7 @@ exports.items = [
}
}
return promise.resolve(reply);
return Promise.resolve(reply);
},
onEnter: onEnter,
@ -223,7 +223,7 @@ exports.items = [
reply.matches = util.createEmptyNodeList(doc);
}
return promise.resolve(reply);
return Promise.resolve(reply);
},
onEnter: onEnter,

View File

@ -16,7 +16,7 @@
'use strict';
var promise = require('../util/promise');
var Promise = require('../util/promise').Promise;
var l10n = require('../util/l10n');
var Status = require('./types').Status;
var Conversion = require('./types').Conversion;
@ -96,12 +96,12 @@ exports.items = [
parse: function(arg, context) {
var msg;
if (arg.text.replace(/^\s*-?/, '').length === 0) {
return promise.resolve(new Conversion(undefined, arg, Status.INCOMPLETE, ''));
return Promise.resolve(new Conversion(undefined, arg, Status.INCOMPLETE, ''));
}
if (!this.allowFloat && (arg.text.indexOf('.') !== -1)) {
msg = l10n.lookupFormat('typesNumberNotInt2', [ arg.text ]);
return promise.resolve(new Conversion(undefined, arg, Status.ERROR, msg));
return Promise.resolve(new Conversion(undefined, arg, Status.ERROR, msg));
}
var value;
@ -114,22 +114,22 @@ exports.items = [
if (isNaN(value)) {
msg = l10n.lookupFormat('typesNumberNan', [ arg.text ]);
return promise.resolve(new Conversion(undefined, arg, Status.ERROR, msg));
return Promise.resolve(new Conversion(undefined, arg, Status.ERROR, msg));
}
var max = this.getMax(context);
if (max != null && value > max) {
msg = l10n.lookupFormat('typesNumberMax', [ value, max ]);
return promise.resolve(new Conversion(undefined, arg, Status.ERROR, msg));
return Promise.resolve(new Conversion(undefined, arg, Status.ERROR, msg));
}
var min = this.getMin(context);
if (min != null && value < min) {
msg = l10n.lookupFormat('typesNumberMin', [ value, min ]);
return promise.resolve(new Conversion(undefined, arg, Status.ERROR, msg));
return Promise.resolve(new Conversion(undefined, arg, Status.ERROR, msg));
}
return promise.resolve(new Conversion(value, arg));
return Promise.resolve(new Conversion(value, arg));
},
decrement: function(value, context) {

View File

@ -16,8 +16,7 @@
'use strict';
var promise = require('../util/promise');
var Promise = require('../util/promise').Promise;
exports.clearResourceCache = function() {
ResourceCache.clear();
@ -72,7 +71,7 @@ function Resource(name, type, inline, element) {
* Get the contents of the given resource as a string.
* The base Resource leaves this unimplemented.
*/
Resource.prototype.getContents = function() {
Resource.prototype.loadContents = function() {
throw new Error('not implemented');
};
@ -98,8 +97,10 @@ function CssResource(domSheet) {
CssResource.prototype = Object.create(Resource.prototype);
CssResource.prototype.loadContents = function(callback) {
callback(this.element.ownerNode.innerHTML);
CssResource.prototype.loadContents = function() {
return new Promise(function(resolve, reject) {
resolve(this.element.ownerNode.innerHTML);
}.bind(this));
};
CssResource._getAllStyles = function() {
@ -161,22 +162,24 @@ function ScriptResource(scriptNode) {
ScriptResource.prototype = Object.create(Resource.prototype);
ScriptResource.prototype.loadContents = function(callback) {
if (this.inline) {
callback(this.element.innerHTML);
}
else {
// It would be good if there was a better way to get the script source
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState !== xhr.DONE) {
return;
}
callback(xhr.responseText);
};
xhr.open('GET', this.element.src, true);
xhr.send();
}
ScriptResource.prototype.loadContents = function() {
return new Promise(function(resolve, reject) {
if (this.inline) {
resolve(this.element.innerHTML);
}
else {
// It would be good if there was a better way to get the script source
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState !== xhr.DONE) {
return;
}
resolve(xhr.responseText);
};
xhr.open('GET', this.element.src, true);
xhr.send();
}
}.bind(this));
};
ScriptResource._getAllScripts = function() {
@ -289,9 +292,11 @@ exports.items = [
Array.prototype.push.apply(resources, ScriptResource._getAllScripts());
}
return promise.resolve(resources.map(function(resource) {
return { name: resource.name, value: resource };
}));
return new Promise(function(resolve, reject) {
resolve(resources.map(function(resource) {
return { name: resource.name, value: resource };
}));
}.bind(this));
}
}
];

View File

@ -16,7 +16,7 @@
'use strict';
var promise = require('../util/promise');
var Promise = require('../util/promise').Promise;
var l10n = require('../util/l10n');
var spell = require('../util/spell');
var Type = require('./types').Type;
@ -171,7 +171,7 @@ SelectionType.prototype.getLookup = function(context) {
* returns a promise of actual values.
*/
function resolve(thing, context) {
return promise.resolve(thing).then(function(resolved) {
return Promise.resolve(thing).then(function(resolved) {
if (typeof resolved === 'function') {
return resolve(resolved(context), context);
}
@ -279,7 +279,7 @@ exports.findPredictions = function(arg, lookup) {
};
SelectionType.prototype.parse = function(arg, context) {
return promise.resolve(this.getLookup(context)).then(function(lookup) {
return Promise.resolve(this.getLookup(context)).then(function(lookup) {
var predictions = exports.findPredictions(arg, lookup);
return exports.convertPredictions(arg, predictions);
}.bind(this));
@ -293,22 +293,22 @@ exports.convertPredictions = function(arg, predictions) {
if (predictions.length === 0) {
var msg = l10n.lookupFormat('typesSelectionNomatch', [ arg.text ]);
return new Conversion(undefined, arg, Status.ERROR, msg,
promise.resolve(predictions));
Promise.resolve(predictions));
}
if (predictions[0].name === arg.text) {
var value = predictions[0].value;
return new Conversion(value, arg, Status.VALID, '',
promise.resolve(predictions));
Promise.resolve(predictions));
}
return new Conversion(undefined, arg, Status.INCOMPLETE, '',
promise.resolve(predictions));
Promise.resolve(predictions));
};
SelectionType.prototype.getBlank = function(context) {
var predictFunc = function(context2) {
return promise.resolve(this.getLookup(context2)).then(function(lookup) {
return Promise.resolve(this.getLookup(context2)).then(function(lookup) {
return lookup.filter(function(option) {
return !option.value.hidden;
}).slice(0, Conversion.maxPredictions - 1);

View File

@ -16,7 +16,7 @@
'use strict';
var promise = require('../util/promise');
var Promise = require('../util/promise').Promise;
var Status = require('./types').Status;
var Conversion = require('./types').Conversion;
@ -64,7 +64,7 @@ exports.items = [
parse: function(arg, context) {
if (!this.allowBlank && (arg.text == null || arg.text === '')) {
return promise.resolve(new Conversion(undefined, arg, Status.INCOMPLETE, ''));
return Promise.resolve(new Conversion(undefined, arg, Status.INCOMPLETE, ''));
}
// The string '\\' (i.e. an escaped \ (represented here as '\\\\' because it
@ -87,7 +87,7 @@ exports.items = [
.replace(/\\}/g, '}')
.replace(/\uF000/g, '\\');
return promise.resolve(new Conversion(value, arg));
return Promise.resolve(new Conversion(value, arg));
}
}
];

View File

@ -17,7 +17,7 @@
'use strict';
var util = require('../util/util');
var promise = require('../util/promise');
var Promise = require('../util/promise').Promise;
/**
* We record where in the input string an argument comes so we can report
@ -814,7 +814,7 @@ Conversion.prototype.getPredictions = function(context) {
if (typeof this.predictions === 'function') {
return this.predictions(context);
}
return promise.resolve(this.predictions || []);
return Promise.resolve(this.predictions || []);
};
/**
@ -825,7 +825,7 @@ Conversion.prototype.getPredictions = function(context) {
*/
Conversion.prototype.constrainPredictionIndex = function(context, index) {
if (index == null) {
return promise.resolve();
return Promise.resolve();
}
return this.getPredictions(context).then(function(value) {

View File

@ -67,7 +67,11 @@ function Menu(options) {
menuHtmlPromise = host.staticRequire(module, './menu.html');
}
menuHtmlPromise.then(function(menuHtml) {
this.template = util.toDom(this.document, menuHtml);
if (this.document == null) {
return; // destroy() has been called
}
this.template = host.toDom(this.document, menuHtml);
}.bind(this), console.error);
// Contains the items that should be displayed

View File

@ -17,6 +17,7 @@
'use strict';
var util = require('../util/util');
var host = require('../util/host');
var domtemplate = require('../util/domtemplate');
@ -78,7 +79,7 @@ exports.createView = function(options) {
util.importCss(options.css, document, options.cssId);
}
var child = util.toDom(document, options.html);
var child = host.toDom(document, options.html);
domtemplate.template(child, options.data || {}, options.options || {});
return child;
}

View File

@ -21,7 +21,7 @@ var Cc = require('chrome').Cc;
var Ci = require('chrome').Ci;
var OS = Cu.import('resource://gre/modules/osfile.jsm', {}).OS;
var promise = require('./promise');
var Promise = require('../util/promise').Promise;
/**
* A set of functions that don't really belong in 'fs' (because they're not
@ -122,5 +122,5 @@ exports.stat = function(pathname) {
* Right now, however, we do nothing.
*/
exports.describe = function(pathname) {
return promise.resolve('');
return Promise.resolve('');
};

View File

@ -21,7 +21,7 @@ var Ci = require('chrome').Ci;
var Task = require('resource://gre/modules/Task.jsm').Task;
var promise = require('./promise');
var Promise = require('../util/promise').Promise;
var util = require('./util');
function Highlighter(document) {
@ -71,17 +71,27 @@ exports.exec = function(task) {
return Task.spawn(task);
};
/**
* Load some HTML into the given document and return a DOM element.
* This utility assumes that the html has a single root (other than whitespace)
*/
exports.toDom = function(document, html) {
var div = util.createElement(document, 'div');
util.setContents(div, html);
return div.children[0];
};
/**
* When dealing with module paths on windows we want to use the unix
* directory separator rather than the windows one, so we avoid using
* OS.Path.dirname, and use unix version on all platforms.
*/
let resourceDirName = function(path) {
let index = path.lastIndexOf("/");
var resourceDirName = function(path) {
var index = path.lastIndexOf('/');
if (index == -1) {
return ".";
return '.';
}
while (index >= 0 && path[index] == "/") {
while (index >= 0 && path[index] == '/') {
--index;
}
return path.slice(0, index + 1);
@ -92,37 +102,30 @@ let resourceDirName = function(path) {
* @see lib/gcli/util/host.js
*/
exports.staticRequire = function(requistingModule, name) {
var deferred = promise.defer();
if (name.match(/\.css$/)) {
deferred.resolve('');
return Promise.resolve('');
}
else {
var filename = resourceDirName(requistingModule.id) + '/' + name;
filename = filename.replace(/\/\.\//g, '/');
filename = 'resource://gre/modules/devtools/' + filename;
return new Promise(function(resolve, reject) {
var filename = resourceDirName(requistingModule.id) + '/' + name;
filename = filename.replace(/\/\.\//g, '/');
filename = 'resource://gre/modules/devtools/' + filename;
var xhr = Cc['@mozilla.org/xmlextras/xmlhttprequest;1']
.createInstance(Ci.nsIXMLHttpRequest);
var xhr = Cc['@mozilla.org/xmlextras/xmlhttprequest;1']
.createInstance(Ci.nsIXMLHttpRequest);
xhr.onload = function onload() {
deferred.resolve(xhr.responseText);
}.bind(this);
xhr.onload = function onload() {
resolve(xhr.responseText);
}.bind(this);
xhr.onabort = xhr.onerror = xhr.ontimeout = function(err) {
deferred.reject(err);
}.bind(this);
xhr.onabort = xhr.onerror = xhr.ontimeout = function(err) {
reject(err);
}.bind(this);
try {
xhr.open('GET', filename);
xhr.send();
}
catch (ex) {
deferred.reject(ex);
}
}.bind(this));
}
return deferred.promise;
};
/**
@ -146,64 +149,62 @@ exports.script.useTarget = function(tgt) {
// Local debugging needs to make the target remote.
var targetPromise = target.isRemote ?
promise.resolve(target) :
Promise.resolve(target) :
target.makeRemote();
return targetPromise.then(function() {
var deferred = promise.defer();
return new Promise(function(resolve, reject) {
client = target._client;
client = target._client;
client.addListener('pageError', function(packet) {
if (packet.from === consoleActor) {
// console.log('pageError', packet.pageError);
exports.script.onOutput({
level: 'exception',
message: packet.exception.class
});
}
});
client.addListener('pageError', function(packet) {
if (packet.from === consoleActor) {
// console.log('pageError', packet.pageError);
exports.script.onOutput({
level: 'exception',
message: packet.exception.class
});
}
});
client.addListener('consoleAPICall', function(type, packet) {
if (packet.from === consoleActor) {
var data = packet.message;
client.addListener('consoleAPICall', function(type, packet) {
if (packet.from === consoleActor) {
var data = packet.message;
var ev = {
level: data.level,
arguments: data.arguments,
};
if (data.filename !== 'debugger eval code') {
ev.source = {
filename: data.filename,
lineNumber: data.lineNumber,
functionName: data.functionName
var ev = {
level: data.level,
arguments: data.arguments,
};
if (data.filename !== 'debugger eval code') {
ev.source = {
filename: data.filename,
lineNumber: data.lineNumber,
functionName: data.functionName
};
}
exports.script.onOutput(ev);
}
});
consoleActor = target._form.consoleActor;
var onAttach = function(response, wcc) {
webConsoleClient = wcc;
if (response.error != null) {
reject(response);
}
else {
resolve(response);
}
exports.script.onOutput(ev);
}
});
// TODO: add _onTabNavigated code?
};
consoleActor = target._form.consoleActor;
var onAttach = function(response, wcc) {
webConsoleClient = wcc;
if (response.error != null) {
deferred.reject(response);
}
else {
deferred.resolve(response);
}
// TODO: add _onTabNavigated code?
};
var listeners = [ 'PageError', 'ConsoleAPI' ];
client.attachConsole(consoleActor, listeners, onAttach);
return deferred.promise;
var listeners = [ 'PageError', 'ConsoleAPI' ];
client.attachConsole(consoleActor, listeners, onAttach);
}.bind(this));
});
};
@ -211,21 +212,20 @@ exports.script.useTarget = function(tgt) {
* Execute some JavaScript
*/
exports.script.eval = function(javascript) {
var deferred = promise.defer();
return new Promise(function(resolve, reject) {
var onResult = function(response) {
var output = response.result;
if (typeof output === 'object' && output.type === 'undefined') {
output = undefined;
}
var onResult = function(response) {
var output = response.result;
if (typeof output === 'object' && output.type === 'undefined') {
output = undefined;
}
resolve({
input: response.input,
output: output,
exception: response.exception
});
};
deferred.resolve({
input: response.input,
output: output,
exception: response.exception
});
};
webConsoleClient.evaluateJS(javascript, onResult, {});
return deferred.promise;
webConsoleClient.evaluateJS(javascript, onResult, {});
}.bind(this));
};

View File

@ -17,9 +17,94 @@
'use strict';
var Cu = require('chrome').Cu;
module.exports = exports =
Cu.import('resource://gre/modules/devtools/deprecated-sync-thenables.js', {}).Promise;
var Cc = require('chrome').Cc;
var Ci = require('chrome').Ci;
// When we've solved the debugger/sdk/promise/gcli/helpers/overlap problem then
// we should use this instead:
// module.exports = exports = require('resource://gre/modules/Promise.jsm').Promise;
/*
* Minimalist implementation of ES6 promises built on SDK promises. 2 things to
* know:
* - There is a hack in .then() to be async which matches A+ and toolkit
* promises. GCLI code works with either sync or async promises, but async
* is more correct
* - There is an additional Promise.defer function which to matches the call
* in toolkit promises, which in turn matches the call in SDK promises
*
* Why not use toolkit promises directly? Because there is a strange bug that
* we are investigating where thread executions vanish.
*
* When we've solved the debugger/sdk/promise/gcli/helpers/overlap problem then
* we should use this instead:
* module.exports = exports = require('resource://gre/modules/Promise.jsm');
*/
var promise = require('resource://gre/modules/devtools/deprecated-sync-thenables.js', {});
/**
* An implementation of ES6 promises in terms of SDK promises
*/
var Promise = function(executor) {
this.deferred = promise.defer();
try {
executor.call(null, this.deferred.resolve, this.deferred.reject);
}
catch (ex) {
this.deferred.reject(ex);
}
}
var async = true;
/**
* The sync version of this would look like
* Promise.prototype.then = function(onResolve, onReject) {
* return this.deferred.promise.then(onResolve, onReject);
* };
*/
Promise.prototype.then = function(onResolve, onReject) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
try {
resolve(this.deferred.promise.then(onResolve, onReject));
}
catch (ex) {
reject(ex);
}
}.bind(this), 0);
}.bind(this));
};
Promise.all = promise.all;
Promise.resolve = promise.resolve;
Promise.defer = promise.defer;
exports.Promise = Promise;
/**
* Implementation of the setTimeout/clearTimeout web APIs taken from the old
* Timer.jsm module
*/
// This gives us >=2^30 unique timer IDs, enough for 1 per ms for 12.4 days.
var nextTimeoutId = 1; // setTimeout must return a positive integer
var timeoutTable = new Map(); // int -> nsITimer
var setTimeout = function(callback, millis) {
let id = nextTimeoutId++;
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
timer.initWithCallback(function setTimeout_timer() {
timeoutTable.delete(id);
callback.call(undefined);
}, millis, timer.TYPE_ONE_SHOT);
timeoutTable.set(id, timer);
return id;
}
var clearTimeout = function(aId) {
if (timeoutTable.has(aId)) {
timeoutTable.get(aId).cancel();
timeoutTable.delete(aId);
}
}

View File

@ -236,7 +236,7 @@ exports.createEvent = function(name) {
//------------------------------------------------------------------------------
var promise = require('./promise');
var Promise = require('../util/promise').Promise;
/**
* promiseEach is roughly like Array.forEach except that the action is taken to
@ -252,34 +252,34 @@ var promise = require('./promise');
*/
exports.promiseEach = function(array, action, scope) {
if (array.length === 0) {
return promise.resolve([]);
return Promise.resolve([]);
}
var deferred = promise.defer();
var replies = [];
return new Promise(function(resolve, reject) {
var replies = [];
var callNext = function(index) {
var onSuccess = function(reply) {
replies[index] = reply;
var callNext = function(index) {
var onSuccess = function(reply) {
replies[index] = reply;
if (index + 1 >= array.length) {
deferred.resolve(replies);
}
else {
callNext(index + 1);
}
if (index + 1 >= array.length) {
resolve(replies);
}
else {
callNext(index + 1);
}
};
var onFailure = function(ex) {
reject(ex);
};
var reply = action.call(scope, array[index], index, array);
Promise.resolve(reply).then(onSuccess).then(null, onFailure);
};
var onFailure = function(ex) {
deferred.reject(ex);
};
var reply = action.call(scope, array[index], index, array);
promise.resolve(reply).then(onSuccess).then(null, onFailure);
};
callNext(0);
return deferred.promise;
callNext(0);
});
};
/**
@ -517,16 +517,6 @@ exports.setContents = function(elem, contents) {
}
};
/**
* Load some HTML into the given document and return a DOM element.
* This utility assumes that the html has a single root (other than whitespace)
*/
exports.toDom = function(document, html) {
var div = exports.createElement(document, 'div');
exports.setContents(div, html);
return div.children[0];
};
/**
* How to detect if we're in an XML document.
* In a Mozilla we check that document.xmlVersion = null, however in Chrome