merge mozilla-central to mozilla-inbound. r=merge a=merge

This commit is contained in:
Sebastian Hengst 2017-10-27 00:00:25 +02:00
Родитель 4d087e6f8b b181c484ad
Коммит d10e26c913
362 изменённых файлов: 23612 добавлений и 32371 удалений

Просмотреть файл

@ -64,7 +64,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=483573
{
ID: muteBtn,
actionName: "press",
events: CLICK_EVENTS,
eventSeq: [
// new focusChecker(muteBtn),
new nameChecker(muteBtn, "Unmute"),
@ -81,7 +80,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=483573
{
ID: playBtn,
actionName: "press",
events: CLICK_EVENTS,
eventSeq: [
// new focusChecker(playBtn),
new nameChecker(playBtn, "Pause"),

Просмотреть файл

@ -185,12 +185,10 @@ var TabsInTitlebar = {
// On Windows 10, adjust the window controls to span the entire
// tab strip height if we're not showing a menu bar.
if (AppConstants.isPlatformAndVersionAtLeast("win", "10.0")) {
if (!menuHeight) {
// Add a pixel to slightly overlap the navbar border.
titlebarContentHeight = fullTabsHeight + 1;
$("titlebar-buttonbox").style.height = titlebarContentHeight + "px";
}
if (AppConstants.isPlatformAndVersionAtLeast("win", "10.0") &&
!menuHeight) {
titlebarContentHeight = fullTabsHeight;
$("titlebar-buttonbox").style.height = titlebarContentHeight + "px";
}
// If the menubar is around (menuHeight is non-zero), try to adjust

Просмотреть файл

@ -726,6 +726,14 @@ var gCookiesWindow = {
aEvent.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) {
this.deleteCookie();
aEvent.preventDefault();
} else if (aEvent.getModifierState("Accel") &&
document.getElementById("key_selectAll")
.getAttribute("key")
.toLocaleLowerCase()
.charCodeAt(0) == aEvent.charCode) {
let view = gCookiesWindow._view;
view.selection.selectAll();
aEvent.preventDefault();
}
},

Просмотреть файл

@ -8,7 +8,12 @@
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/cookies.dtd" >
<!DOCTYPE dialog [
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
<!ENTITY % cookiesDTD SYSTEM "chrome://browser/locale/preferences/cookies.dtd">
%cookiesDTD;
]>
<window id="CookiesDialog" windowtype="Browser:Cookies"
class="windowDialog" title="&window.title;"
@ -27,6 +32,7 @@
<keyset>
<key key="&windowClose.key;" modifiers="accel" oncommand="window.close();"/>
<key key="&focusSearch1.key;" modifiers="accel" oncommand="gCookiesWindow.focusFilterBox();"/>
<key id="key_selectAll" key="&selectAllCmd.key;" modifiers="accel"/>
</keyset>
<vbox flex="1" class="contentPane largeDialogContainer">

Просмотреть файл

@ -115,13 +115,7 @@ this.UITour = {
// to automatically open the appMenu when annotating this target.
widgetName: "appMenu-fxa-label",
}],
["addons", {
query: (aDocument) => {
// select toolbar icon if exist, fallback to appMenu item
let node = aDocument.getElementById("add-ons-button");
return node ? node : aDocument.getElementById("appMenu-addons-button");
},
}],
["addons", {query: "#appMenu-addons-button"}],
["appMenu", {
addTargetListener: (aDocument, aCallback) => {
let panelPopup = aDocument.defaultView.PanelUI.panel;
@ -152,13 +146,7 @@ this.UITour = {
}],
["help", {query: "#appMenu-help-button"}],
["home", {query: "#home-button"}],
["library", {
query: (aDocument) => {
// select toolbar icon if exist, fallback to appMenu item
let node = aDocument.getElementById("library-button");
return node ? node : aDocument.getElementById("appMenu-library-button");
},
}],
["library", {query: "#appMenu-library-button"}],
["pocket", {
allowAdd: true,
query: (aDocument) => {

Просмотреть файл

@ -6,41 +6,7 @@ var gContentWindow;
add_task(setup_UITourTest);
add_UITour_task(async function test_highlight_library_icon_in_toolbar() {
let highlight = document.getElementById("UITourHighlight");
is_element_hidden(highlight, "Highlight should initially be hidden");
// Test highlighting the library button
let highlightVisiblePromise = elementVisiblePromise(highlight, "Should show highlight");
gContentAPI.showHighlight("library");
await highlightVisiblePromise;
UITour.getTarget(window, "library").then((target) => {
is("library-button", target.node.id, "Should highlight the right target");
});
});
add_UITour_task(async function test_highlight_addons_icon_in_toolbar() {
CustomizableUI.addWidgetToArea("add-ons-button", CustomizableUI.AREA_NAVBAR, 0);
ok(!UITour.availableTargetsCache.has(window),
"Targets should be evicted from cache after widget change");
let highlight = document.getElementById("UITourHighlight");
is_element_hidden(highlight, "Highlight should initially be hidden");
// Test highlighting the addons button on toolbar
let highlightVisiblePromise = elementVisiblePromise(highlight, "Should show highlight");
gContentAPI.showHighlight("addons");
await highlightVisiblePromise;
UITour.getTarget(window, "addons").then((target) => {
is("add-ons-button", target.node.id, "Should highlight the right target");
CustomizableUI.removeWidgetFromArea("add-ons-button");
});
});
add_UITour_task(async function test_highlight_library_and_show_library_subview() {
CustomizableUI.removeWidgetFromArea("library-button");
ok(!UITour.availableTargetsCache.has(window),
"Targets should be evicted from cache after widget change");
let highlight = document.getElementById("UITourHighlight");
is_element_hidden(highlight, "Highlight should initially be hidden");
@ -71,6 +37,4 @@ add_UITour_task(async function test_highlight_library_and_show_library_subview()
gContentAPI.hideMenu("appMenu");
await appMenuHiddenPromise;
is(appMenu.state, "closed", "Should close the app menu");
CustomizableUI.addWidgetToArea("library", CustomizableUI.AREA_NAVBAR, 0);
});

Просмотреть файл

@ -123,6 +123,30 @@ const CONTENT = {
persistWhileVisible: true,
popupIconURL: "chrome://formautofill/content/icon-credit-card.svg",
hideClose: true,
checkbox: {
get checked() {
return Services.prefs.getBoolPref("services.sync.engine.creditcards");
},
get label() {
// Only set the label when the fallowing conditions existed:
// - sync account is set
// - credit card sync is disabled
// - credit card sync is available
// otherwise return null label to hide checkbox.
return Services.prefs.prefHasUserValue("services.sync.username") &&
!Services.prefs.getBoolPref("services.sync.engine.creditcards") &&
Services.prefs.getBoolPref("services.sync.engine.creditcards.available") ?
GetStringFromName("creditCardsSyncCheckbox") : null;
},
callback(event) {
let {secondaryButton, menubutton} = event.target.parentNode.parentNode.parentNode;
let checked = event.target.checked;
Services.prefs.setBoolPref("services.sync.engine.creditcards", checked);
secondaryButton.disabled = checked;
menubutton.disabled = checked;
log.debug("Set creditCard sync to", checked);
},
},
},
},
};
@ -166,6 +190,11 @@ let FormAutofillDoorhanger = {
return [mainAction, secondaryActions];
},
_getNotificationElm(browser, id) {
let notificationId = id + "-notification";
let chromeDoc = browser.ownerDocument;
return chromeDoc.getElementById(notificationId);
},
/**
* Append the link label element to the popupnotificationcontent.
* @param {XULElement} browser
@ -225,26 +254,20 @@ let FormAutofillDoorhanger = {
if (!options.checkbox) {
return;
}
let id = notificationId + "-notification";
let chromeDoc = browser.ownerDocument;
let notification = chromeDoc.getElementById(id);
let cb = notification.checkbox;
let {checkbox} = this._getNotificationElm(browser, notificationId);
if (cb) {
cb.addEventListener("command", options.checkbox.callback);
if (checkbox && !checkbox.hidden) {
checkbox.addEventListener("command", options.checkbox.callback);
}
},
_removeCheckboxListener(browser, {notificationId, options}) {
if (!options.checkbox) {
return;
}
let id = notificationId + "-notification";
let chromeDoc = browser.ownerDocument;
let notification = chromeDoc.getElementById(id);
let cb = notification.checkbox;
let {checkbox} = this._getNotificationElm(browser, notificationId);
if (cb) {
cb.removeEventListener("command", options.checkbox.callback);
if (checkbox && !checkbox.hidden) {
checkbox.removeEventListener("command", options.checkbox.callback);
}
},
/**

Просмотреть файл

@ -539,11 +539,14 @@ this.FormAutofillHeuristics = {
* all field details in the form.
*/
getFormInfo(form, allowDuplicates = false) {
if (form.elements.length <= 0) {
const eligibleFields = Array.from(form.elements)
.filter(elem => FormAutofillUtils.isFieldEligibleForAutofill(elem));
if (eligibleFields.length <= 0) {
return [];
}
let fieldScanner = new FieldScanner(form.elements);
let fieldScanner = new FieldScanner(eligibleFields);
while (!fieldScanner.parsingFinished) {
let parsedPhoneFields = this._parsePhoneFields(fieldScanner);
let parsedAddressFields = this._parseAddressFields(fieldScanner);
@ -629,10 +632,6 @@ this.FormAutofillHeuristics = {
},
getInfo(element) {
if (!FormAutofillUtils.isFieldEligibleForAutofill(element)) {
return null;
}
let info = element.getAutocompleteInfo();
// An input[autocomplete="on"] will not be early return here since it stll
// needs to find the field name.

Просмотреть файл

@ -203,7 +203,12 @@ FormAutofillParent.prototype = {
break;
}
case "FormAutofill:SaveCreditCard": {
await this.profileStorage.creditCards.normalizeCCNumberFields(data.creditcard);
// TODO: "MasterPassword.ensureLoggedIn" can be removed after the storage
// APIs are refactored to be async functions (bug 1399367).
if (!await MasterPassword.ensureLoggedIn()) {
log.warn("User canceled master password entry");
return;
}
this.profileStorage.creditCards.add(data.creditcard);
break;
}
@ -454,15 +459,14 @@ FormAutofillParent.prototype = {
return;
}
try {
await this.profileStorage.creditCards.normalizeCCNumberFields(creditCard.record);
this.profileStorage.creditCards.add(creditCard.record);
} catch (e) {
if (e.result != Cr.NS_ERROR_ABORT) {
throw e;
}
// TODO: "MasterPassword.ensureLoggedIn" can be removed after the storage
// APIs are refactored to be async functions (bug 1399367).
if (!await MasterPassword.ensureLoggedIn()) {
log.warn("User canceled master password entry");
return;
}
this.profileStorage.creditCards.add(creditCard.record);
},
_onFormSubmit(data, target) {

Просмотреть файл

@ -36,19 +36,42 @@ this.MasterPassword = {
},
/**
* Display the master password login prompt no matter it's logged in or not.
* If an existing MP prompt is already open, the result from it will be used instead.
*
* @returns {Promise<boolean>} True if it's logged in or no password is set and false
* if it's still not logged in (prompt canceled or other error).
* @returns {boolean} True if master password is logged in and false if not.
*/
async prompt() {
get isLoggedIn() {
return Services.logins.isLoggedIn;
},
/**
* @returns {boolean} True if there is another master password login dialog
* existing and false otherwise.
*/
get isUIBusy() {
return Services.logins.uiBusy;
},
/**
* Ensure the master password is logged in. It will display the master password
* login prompt or do nothing if it's logged in already. If an existing MP
* prompt is already prompted, the result from it will be used instead.
*
* @param {boolean} reauth Prompt the login dialog no matter it's logged in
* or not if it's set to true.
* @returns {Promise<boolean>} True if it's logged in or no password is set
* and false if it's still not logged in (prompt
* canceled or other error).
*/
async ensureLoggedIn(reauth = false) {
if (!this.isEnabled) {
return true;
}
if (this.isLoggedIn && !reauth) {
return true;
}
// If a prompt is already showing then wait for and focus it.
if (Services.logins.uiBusy) {
if (this.isUIBusy) {
return this.waitForExistingDialog();
}
@ -81,17 +104,28 @@ this.MasterPassword = {
* @returns {Promise<string>} resolves to the decrypted string, or rejects otherwise.
*/
async decrypt(cipherText, reauth = false) {
let loggedIn = false;
if (reauth) {
loggedIn = await this.prompt();
} else {
loggedIn = await this.waitForExistingDialog();
}
if (!loggedIn) {
if (!await this.ensureLoggedIn(reauth)) {
throw Components.Exception("User canceled master password entry", Cr.NS_ERROR_ABORT);
}
return cryptoSDR.decrypt(cipherText);
},
/**
* Decrypts cipherText synchronously. "ensureLoggedIn()" needs to be called
* outside in case another dialog is showing.
*
* NOTE: This method will be removed soon once the ProfileStorage APIs are
* refactored to be async functions (bug 1399367). Please use async
* version instead.
*
* @deprecated
* @param {string} cipherText Encrypted string including the algorithm details.
* @returns {string} The decrypted string.
*/
decryptSync(cipherText) {
if (this.isUIBusy) {
throw Components.Exception("\"ensureLoggedIn()\" should be called first", Cr.NS_ERROR_UNEXPECTED);
}
return cryptoSDR.decrypt(cipherText);
},
@ -102,13 +136,32 @@ this.MasterPassword = {
* @returns {Promise<string>} resolves to the encrypted string (with algorithm), otherwise rejects.
*/
async encrypt(plainText) {
if (Services.logins.uiBusy && !await this.waitForExistingDialog()) {
if (!await this.ensureLoggedIn()) {
throw Components.Exception("User canceled master password entry", Cr.NS_ERROR_ABORT);
}
return cryptoSDR.encrypt(plainText);
},
/**
* Encrypts plainText synchronously. "ensureLoggedIn()" needs to be called
* outside in case another dialog is showing.
*
* NOTE: This method will be removed soon once the ProfileStorage APIs are
* refactored to be async functions (bug 1399367). Please use async
* version instead.
*
* @deprecated
* @param {string} plainText A plain string to be encrypted.
* @returns {string} The encrypted cipher string.
*/
encryptSync(plainText) {
if (this.isUIBusy) {
throw Components.Exception("\"ensureLoggedIn()\" should be called first", Cr.NS_ERROR_UNEXPECTED);
}
return cryptoSDR.encrypt(plainText);
},
/**
* Resolve when master password dialogs are closed, immediately if none are open.
*
@ -118,10 +171,9 @@ this.MasterPassword = {
* Resolves with whether the user is logged in to MP.
*/
async waitForExistingDialog() {
if (!Services.logins.uiBusy) {
log.debug("waitForExistingDialog: Dialog isn't showing. isLoggedIn:",
Services.logins.isLoggedIn);
return Services.logins.isLoggedIn;
if (!this.isUIBusy) {
log.debug("waitForExistingDialog: Dialog isn't showing. isLoggedIn:", this.isLoggedIn);
return this.isLoggedIn;
}
return new Promise((resolve) => {

Просмотреть файл

@ -58,8 +58,8 @@
*
* // credit card fields
* cc-name,
* cc-number, // e.g. ************1234
* cc-number-encrypted,
* cc-number, // will be stored in masked format (************1234)
* // (see details below)
* cc-exp-month,
* cc-exp-year, // 2-digit year will be converted to 4 digits
* // upon saving
@ -69,6 +69,8 @@
* cc-given-name,
* cc-additional-name,
* cc-family-name,
* cc-number-encrypted, // encrypted from the original unmasked "cc-number"
* // (see details below)
* cc-exp,
*
* // metadata
@ -81,6 +83,22 @@
* ]
* }
*
*
* Encrypt-related Credit Card Fields (cc-number & cc-number-encrypted):
*
* When saving or updating a credit-card record, the storage will encrypt the
* value of "cc-number", store the encrypted number in "cc-number-encrypted"
* field, and replace "cc-number" field with the masked number. These all happen
* in "_computeFields". We do reverse actions in "_stripComputedFields", which
* decrypts "cc-number-encrypted", restores it to "cc-number", and deletes
* "cc-number-encrypted". Therefore, calling "_stripComputedFields" followed by
* "_computeFields" can make sure the encrypt-related fields are up-to-date.
*
* In general, you have to decrypt the number by your own outside ProfileStorage
* when necessary. However, you will get the decrypted records when querying
* data with "rawData=true" to ensure they're ready to sync.
*
*
* Sync Metadata:
*
* Records may also have a _sync field, which consists of:
@ -182,7 +200,6 @@ const VALID_ADDRESS_COMPUTED_FIELDS = [
const VALID_CREDIT_CARD_FIELDS = [
"cc-name",
"cc-number",
"cc-number-encrypted",
"cc-exp-month",
"cc-exp-year",
];
@ -191,6 +208,7 @@ const VALID_CREDIT_CARD_COMPUTED_FIELDS = [
"cc-given-name",
"cc-additional-name",
"cc-family-name",
"cc-number-encrypted",
"cc-exp",
];
@ -247,7 +265,7 @@ class AutofillRecords {
this._schemaVersion = schemaVersion;
let hasChanges = (result, record) => this._migrateRecord(record) || result;
if (this._store.data[this._collectionName].reduce(hasChanges, false)) {
if (this.data.reduce(hasChanges, false)) {
this._store.saveSoon();
}
}
@ -262,6 +280,16 @@ class AutofillRecords {
return this._schemaVersion;
}
/**
* Gets the data of this collection.
*
* @returns {array}
* The data object.
*/
get data() {
return this._store.data[this._collectionName];
}
// Ensures that we don't try to apply synced records with newer schema
// versions. This is a temporary measure to ensure we don't accidentally
// bump the schema version without a syncing strategy in place (bug 1377204).
@ -292,9 +320,9 @@ class AutofillRecords {
includeDeleted: true,
});
if (index > -1) {
let existing = this._store.data[this._collectionName][index];
let existing = this.data[index];
if (existing.deleted) {
this._store.data[this._collectionName].splice(index, 1);
this.data.splice(index, 1);
} else {
throw new Error(`Record ${record.guid} already exists`);
}
@ -349,7 +377,7 @@ class AutofillRecords {
sync.changeCounter = 0;
}
this._store.data[this._collectionName].push(recordToSave);
this.data.push(recordToSave);
this._store.saveSoon();
@ -379,13 +407,16 @@ class AutofillRecords {
update(guid, record, preserveOldProperties = false) {
this.log.debug("update:", guid, record);
let recordFound = this._findByGUID(guid);
if (!recordFound) {
let recordFoundIndex = this._findIndexByGUID(guid);
if (recordFoundIndex == -1) {
throw new Error("No matching record.");
}
// Clone the record by Object assign API to preserve the property with empty string.
let recordToUpdate = Object.assign({}, record);
// Clone the record before modifying it to avoid exposing incomplete changes.
let recordFound = this._clone(this.data[recordFoundIndex]);
this._stripComputedFields(recordFound);
let recordToUpdate = this._clone(record);
this._normalizeRecord(recordToUpdate);
for (let field of this.VALID_FIELDS) {
@ -412,8 +443,8 @@ class AutofillRecords {
syncMetadata.changeCounter += 1;
}
this._stripComputedFields(recordFound);
this._computeFields(recordFound);
this.data[recordFoundIndex] = recordFound;
this._store.saveSoon();
Services.obs.notifyObservers(null, "formautofill-storage-changed", "update");
@ -461,7 +492,7 @@ class AutofillRecords {
this.log.warn("attempting to remove non-existing entry", guid);
return;
}
let existing = this._store.data[this._collectionName][index];
let existing = this.data[index];
if (existing.deleted) {
return; // already a tombstone - don't touch it.
}
@ -469,7 +500,7 @@ class AutofillRecords {
if (existingSync) {
// existing sync metadata means it has been synced. This means we must
// leave a tombstone behind.
this._store.data[this._collectionName][index] = {
this.data[index] = {
guid,
timeLastModified: Date.now(),
deleted: true,
@ -479,7 +510,7 @@ class AutofillRecords {
} else {
// If there's no sync meta-data, this record has never been synced, so
// we can delete it.
this._store.data[this._collectionName].splice(index, 1);
this.data.splice(index, 1);
}
}
@ -507,7 +538,7 @@ class AutofillRecords {
}
// The record is cloned to avoid accidental modifications from outside.
let clonedRecord = this._clone(recordFound);
let clonedRecord = this._cloneAndCleanUp(recordFound);
if (rawData) {
this._stripComputedFields(clonedRecord);
} else {
@ -529,9 +560,9 @@ class AutofillRecords {
getAll({rawData = false, includeDeleted = false} = {}) {
this.log.debug("getAll", rawData, includeDeleted);
let records = this._store.data[this._collectionName].filter(r => !r.deleted || includeDeleted);
let records = this.data.filter(r => !r.deleted || includeDeleted);
// Records are cloned to avoid accidental modifications from outside.
let clonedRecords = records.map(r => this._clone(r));
let clonedRecords = records.map(r => this._cloneAndCleanUp(r));
clonedRecords.forEach(record => {
if (rawData) {
this._stripComputedFields(record);
@ -595,16 +626,17 @@ class AutofillRecords {
* remote record, and the shared parent that we synthesize from the last
* synced fields - see _maybeStoreLastSyncedField.
*
* @param {Object} localRecord
* The changed local record, currently in storage.
* @param {Object} strippedLocalRecord
* The changed local record, currently in storage. Computed fields
* are stripped.
* @param {Object} remoteRecord
* The remote record.
* @returns {Object|null}
* The merged record, or `null` if there are conflicts and the
* records can't be merged.
*/
_mergeSyncedRecords(localRecord, remoteRecord) {
let sync = this._getSyncMetaData(localRecord, true);
_mergeSyncedRecords(strippedLocalRecord, remoteRecord) {
let sync = this._getSyncMetaData(strippedLocalRecord, true);
// Copy all internal fields from the remote record. We'll update their
// values in `_replaceRecordAt`.
@ -623,30 +655,30 @@ class AutofillRecords {
// determine if the local and remote values are different. Hashing is
// expensive, but we don't expect this to happen frequently.
let lastSyncedValue = sync.lastSyncedFields[field];
isLocalSame = lastSyncedValue == sha512(localRecord[field]);
isLocalSame = lastSyncedValue == sha512(strippedLocalRecord[field]);
isRemoteSame = lastSyncedValue == sha512(remoteRecord[field]);
} else {
// Otherwise, if the field hasn't changed since the last sync, we know
// it's the same locally.
isLocalSame = true;
isRemoteSame = localRecord[field] == remoteRecord[field];
isRemoteSame = strippedLocalRecord[field] == remoteRecord[field];
}
let value;
if (isLocalSame && isRemoteSame) {
// Local and remote are the same; doesn't matter which one we pick.
value = localRecord[field];
value = strippedLocalRecord[field];
} else if (isLocalSame && !isRemoteSame) {
value = remoteRecord[field];
} else if (!isLocalSame && isRemoteSame) {
// We don't need to bump the change counter when taking the local
// change, because the counter must already be > 0 if we're attempting
// a three-way merge.
value = localRecord[field];
} else if (localRecord[field] == remoteRecord[field]) {
value = strippedLocalRecord[field];
} else if (strippedLocalRecord[field] == remoteRecord[field]) {
// Shared parent doesn't match either local or remote, but the values
// are identical, so there's no conflict.
value = localRecord[field];
value = strippedLocalRecord[field];
} else {
// Both local and remote changed to different values. We'll need to fork
// the local record to resolve the conflict.
@ -676,12 +708,12 @@ class AutofillRecords {
* it's uploaded.
*/
_replaceRecordAt(index, remoteRecord, {keepSyncMetadata = false} = {}) {
let localRecord = this._store.data[this._collectionName][index];
let localRecord = this.data[index];
let newRecord = this._clone(remoteRecord);
this._stripComputedFields(newRecord);
this._store.data[this._collectionName][index] = newRecord;
this.data[index] = newRecord;
if (keepSyncMetadata) {
// It's safe to move the Sync metadata from the old record to the new
@ -720,18 +752,14 @@ class AutofillRecords {
* Clones a local record, giving the clone a new GUID and Sync metadata. The
* original record remains unchanged in storage.
*
* @param {Object} localRecord
* The local record.
* @param {Object} strippedLocalRecord
* The local record. Computed fields are stripped.
* @returns {string}
* A clone of the local record with a new GUID.
*/
_forkLocalRecord(localRecord) {
let forkedLocalRecord = this._clone(localRecord);
this._stripComputedFields(forkedLocalRecord);
_forkLocalRecord(strippedLocalRecord) {
let forkedLocalRecord = this._cloneAndCleanUp(strippedLocalRecord);
forkedLocalRecord.guid = this._generateGUID();
this._store.data[this._collectionName].push(forkedLocalRecord);
// Give the record fresh Sync metadata and bump its change counter as a
// side effect. This also excludes the forked record from de-duping on the
@ -740,6 +768,7 @@ class AutofillRecords {
this._getSyncMetaData(forkedLocalRecord, true);
this._computeFields(forkedLocalRecord);
this.data.push(forkedLocalRecord);
return forkedLocalRecord;
}
@ -770,7 +799,7 @@ class AutofillRecords {
throw new Error(`Record ${remoteRecord.guid} not found`);
}
let localRecord = this._store.data[this._collectionName][localIndex];
let localRecord = this.data[localIndex];
let sync = this._getSyncMetaData(localRecord, true);
let forkedGUID = null;
@ -781,7 +810,10 @@ class AutofillRecords {
keepSyncMetadata: false,
});
} else {
let mergedRecord = this._mergeSyncedRecords(localRecord, remoteRecord);
let strippedLocalRecord = this._clone(localRecord);
this._stripComputedFields(strippedLocalRecord);
let mergedRecord = this._mergeSyncedRecords(strippedLocalRecord, remoteRecord);
if (mergedRecord) {
// Local and remote modified, but we were able to merge. Replace the
// local record with the merged record.
@ -791,7 +823,7 @@ class AutofillRecords {
} else {
// Merge conflict. Fork the local record, then replace the original
// with the merged record.
let forkedLocalRecord = this._forkLocalRecord(localRecord);
let forkedLocalRecord = this._forkLocalRecord(strippedLocalRecord);
forkedGUID = forkedLocalRecord.guid;
this._replaceRecordAt(localIndex, remoteRecord, {
keepSyncMetadata: false,
@ -821,11 +853,11 @@ class AutofillRecords {
let sync = this._getSyncMetaData(tombstone, true);
sync.changeCounter = 0;
this._store.data[this._collectionName].push(tombstone);
this.data.push(tombstone);
return;
}
let existing = this._store.data[this._collectionName][index];
let existing = this.data[index];
let sync = this._getSyncMetaData(existing, true);
if (sync.changeCounter > 0) {
// Deleting a record with unsynced local changes. To avoid potential
@ -842,7 +874,7 @@ class AutofillRecords {
// Removing a record that's not changed locally, and that's not already
// deleted. Replace the record with a synced tombstone.
this._store.data[this._collectionName][index] = {
this.data[index] = {
guid,
timeLastModified: Date.now(),
deleted: true,
@ -864,7 +896,7 @@ class AutofillRecords {
pullSyncChanges() {
let changes = {};
let profiles = this._store.data[this._collectionName];
let profiles = this.data;
for (let profile of profiles) {
let sync = this._getSyncMetaData(profile, true);
if (sync.changeCounter < 1) {
@ -921,7 +953,7 @@ class AutofillRecords {
* metadata for all items is removed.
*/
resetSync() {
for (let record of this._store.data[this._collectionName]) {
for (let record of this.data) {
delete record._sync;
}
// XXX - we should probably also delete all tombstones?
@ -951,7 +983,7 @@ class AutofillRecords {
}
let index = this._findIndexByGUID(oldID);
let profile = this._store.data[this._collectionName][index];
let profile = this.data[index];
if (!profile) {
throw new Error("changeGUID: no source record");
}
@ -985,49 +1017,50 @@ class AutofillRecords {
* fields that match incoming remote records. This avoids creating
* duplicate profiles with the same information.
*
* @param {Object} record
* @param {Object} remoteRecord
* The remote record.
* @returns {string|null}
* The GUID of the matching local record, or `null` if no records
* match.
*/
findDuplicateGUID(record) {
if (!record.guid) {
findDuplicateGUID(remoteRecord) {
if (!remoteRecord.guid) {
throw new Error("Record missing GUID");
}
this._ensureMatchingVersion(record);
if (record.deleted) {
this._ensureMatchingVersion(remoteRecord);
if (remoteRecord.deleted) {
// Tombstones don't carry enough info to de-dupe, and we should have
// handled them separately when applying the record.
throw new Error("Tombstones can't have duplicates");
}
let records = this._store.data[this._collectionName];
for (let profile of records) {
if (profile.deleted) {
let localRecords = this.data;
for (let localRecord of localRecords) {
if (localRecord.deleted) {
continue;
}
if (profile.guid == record.guid) {
throw new Error(`Record ${record.guid} already exists`);
if (localRecord.guid == remoteRecord.guid) {
throw new Error(`Record ${remoteRecord.guid} already exists`);
}
if (this._getSyncMetaData(profile)) {
// This record has already been uploaded, so it can't be a dupe of
if (this._getSyncMetaData(localRecord)) {
// This local record has already been uploaded, so it can't be a dupe of
// another incoming item.
continue;
}
let keys = new Set(Object.keys(record));
for (let key of Object.keys(profile)) {
// Ignore computed fields when matching records as they aren't synced at all.
let strippedLocalRecord = this._clone(localRecord);
this._stripComputedFields(strippedLocalRecord);
let keys = new Set(Object.keys(remoteRecord));
for (let key of Object.keys(strippedLocalRecord)) {
keys.add(key);
}
// Ignore internal and computed fields when matching records. Internal
// fields are synced, but almost certainly have different values than the
// local record, and we'll update them in `reconcile`. Computed fields
// aren't synced at all.
// Ignore internal fields when matching records. Internal fields are synced,
// but almost certainly have different values than the local record, and
// we'll update them in `reconcile`.
for (let field of INTERNAL_FIELDS) {
keys.delete(field);
}
for (let field of this.VALID_COMPUTED_FIELDS) {
keys.delete(field);
}
if (!keys.size) {
// This shouldn't ever happen; a valid record will always have fields
// that aren't computed or internal. Sync can't do anything about that,
@ -1039,13 +1072,13 @@ class AutofillRecords {
// For now, we ensure that both (or neither) records have the field
// with matching values. This doesn't account for the version yet
// (bug 1377204).
same = key in profile == key in record && profile[key] == record[key];
same = key in strippedLocalRecord == key in remoteRecord && strippedLocalRecord[key] == remoteRecord[key];
if (!same) {
break;
}
}
if (same) {
return profile.guid;
return strippedLocalRecord.guid;
}
}
return null;
@ -1056,6 +1089,10 @@ class AutofillRecords {
*/
_clone(record) {
return Object.assign({}, record);
}
_cloneAndCleanUp(record) {
let result = {};
for (let key in record) {
// Do not expose hidden fields and fields with empty value (mainly used
@ -1069,11 +1106,11 @@ class AutofillRecords {
_findByGUID(guid, {includeDeleted = false} = {}) {
let found = this._findIndexByGUID(guid, {includeDeleted});
return found < 0 ? undefined : this._store.data[this._collectionName][found];
return found < 0 ? undefined : this.data[found];
}
_findIndexByGUID(guid, {includeDeleted = false} = {}) {
return this._store.data[this._collectionName].findIndex(record => {
return this.data.findIndex(record => {
return record.guid == guid && (!record.deleted || includeDeleted);
});
}
@ -1410,7 +1447,7 @@ class Addresses extends AutofillRecords {
*/
mergeToStorage(targetAddress) {
let mergedGUIDs = [];
for (let address of this._store.data[this._collectionName]) {
for (let address of this.data) {
if (!address.deleted && this.mergeIfPossible(address.guid, targetAddress)) {
mergedGUIDs.push(address.guid);
}
@ -1425,6 +1462,13 @@ class CreditCards extends AutofillRecords {
super(store, "creditCards", VALID_CREDIT_CARD_FIELDS, VALID_CREDIT_CARD_COMPUTED_FIELDS, CREDIT_CARD_SCHEMA_VERSION);
}
_getMaskedCCNumber(ccNumber) {
if (ccNumber.length <= 4) {
throw new Error(`Invalid credit card number`);
}
return "*".repeat(ccNumber.length - 4) + ccNumber.substr(-4);
}
_computeFields(creditCard) {
// NOTE: Remember to bump the schema version number if any of the existing
// computing algorithm changes. (No need to bump when just adding new
@ -1448,15 +1492,31 @@ class CreditCards extends AutofillRecords {
hasNewComputedFields = true;
}
// Encrypt credit card number
if (!("cc-number-encrypted" in creditCard)) {
let ccNumber = (creditCard["cc-number"] || "").replace(/\s/g, "");
if (FormAutofillUtils.isCCNumber(ccNumber)) {
creditCard["cc-number"] = this._getMaskedCCNumber(ccNumber);
creditCard["cc-number-encrypted"] = MasterPassword.encryptSync(ccNumber);
} else {
delete creditCard["cc-number"];
// Computed fields are always present in the storage no matter it's
// empty or not.
creditCard["cc-number-encrypted"] = "";
}
}
return hasNewComputedFields;
}
_normalizeFields(creditCard) {
// Check if cc-number is normalized(normalizeCCNumberFields should be called first).
if (!creditCard["cc-number-encrypted"] || !creditCard["cc-number"].includes("*")) {
throw new Error("Credit card number needs to be normalized first.");
_stripComputedFields(creditCard) {
if (creditCard["cc-number-encrypted"]) {
creditCard["cc-number"] = MasterPassword.decryptSync(creditCard["cc-number-encrypted"]);
}
super._stripComputedFields(creditCard);
}
_normalizeFields(creditCard) {
// Normalize name
if (creditCard["cc-given-name"] || creditCard["cc-additional-name"] || creditCard["cc-family-name"]) {
if (!creditCard["cc-name"]) {
@ -1493,31 +1553,6 @@ class CreditCards extends AutofillRecords {
}
}
}
/**
* Normalize credit card number related field for saving. It should always be
* called before adding/updating credit card records.
*
* @param {Object} creditCard
* The creditCard record with plaintext number only.
*/
async normalizeCCNumberFields(creditCard) {
// Fields that should not be set by content.
delete creditCard["cc-number-encrypted"];
// Validate and encrypt credit card numbers, and calculate the masked numbers
if (creditCard["cc-number"]) {
let ccNumber = creditCard["cc-number"].replace(/\s/g, "");
delete creditCard["cc-number"];
if (!FormAutofillUtils.isCCNumber(ccNumber)) {
throw new Error("Credit card number contains invalid characters or is under 12 digits.");
}
creditCard["cc-number-encrypted"] = await MasterPassword.encrypt(ccNumber);
creditCard["cc-number"] = "*".repeat(ccNumber.length - 4) + ccNumber.substr(-4);
}
}
}
function ProfileStorage(path) {

Просмотреть файл

@ -263,9 +263,12 @@ class EditCreditCard extends EditDialog {
this._elements.ccNumber.setCustomValidity(true);
return;
}
let storage = await this.getStorage();
await storage.normalizeCCNumberFields(creditCard);
await this.saveRecord(creditCard, this._record ? this._record.guid : null);
// TODO: "MasterPassword.ensureLoggedIn" can be removed after the storage
// APIs are refactored to be async functions (bug 1399367).
if (await MasterPassword.ensureLoggedIn()) {
await this.saveRecord(creditCard, this._record ? this._record.guid : null);
}
window.close();
}

Просмотреть файл

@ -348,7 +348,7 @@ class ManageCreditCards extends ManageRecords {
async openEditDialog(creditCard) {
// If master password is set, ask for password if user is trying to edit an
// existing credit card.
if (!this._hasMasterPassword || !creditCard || await MasterPassword.prompt()) {
if (!creditCard || !this._hasMasterPassword || await MasterPassword.ensureLoggedIn(true)) {
this.prefWin.gSubDialog.open(EDIT_CREDIT_CARD_URL, null, creditCard);
}
}

Просмотреть файл

@ -20,6 +20,9 @@ changeAutofillOptionsOSX = Change Form Autofill Preferences
# LOCALIZATION NOTE (addressesSyncCheckbox): If Sync is enabled, this checkbox is displayed on the doorhanger
# shown when saving addresses.
addressesSyncCheckbox = Share addresses with synced devices
# LOCALIZATION NOTE (creditCardsSyncCheckbox): If Sync is enabled and credit card sync is available,
# this checkbox is displayed on the doorhanger shown when saving credit card.
creditCardsSyncCheckbox = Share credit cards with synced devices
# LOCALIZATION NOTE (updateAddressMessage, createAddressLabel, updateAddressLabel): Used on the doorhanger
# when an address change is detected.
updateAddressMessage = Would you like to update your address with this new information?

Просмотреть файл

@ -51,7 +51,6 @@ add_task(async function test_submit_creditCard_saved() {
await promiseShown;
await clickDoorhangerButton(MAIN_BUTTON);
await TestUtils.topicObserved("formautofill-storage-changed");
}
);
@ -163,3 +162,129 @@ add_task(async function test_submit_creditCard_saved_with_mp_enabled_but_cancele
is(creditCards.length, 2, "Still 2 credit cards in storage");
LoginTestUtils.masterPassword.disable();
});
add_task(async function test_submit_creditCard_unavailable_with_sync_account() {
await SpecialPowers.pushPrefEnv({
"set": [
[SYNC_USERNAME_PREF, "foo@bar.com"],
],
});
await BrowserTestUtils.withNewTab({gBrowser, url: CREDITCARD_FORM_URL},
async function(browser) {
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
"popupshown");
is(SpecialPowers.getBoolPref(SYNC_CREDITCARDS_AVAILABLE_PREF), false,
"creditCards sync should be unavailable by default");
await ContentTask.spawn(browser, null, async function() {
let form = content.document.getElementById("form");
let name = form.querySelector("#cc-name");
name.focus();
name.setUserInput("User 2");
let number = form.querySelector("#cc-number");
number.setUserInput("1234123412341234");
// Wait 500ms before submission to make sure the input value applied
await new Promise(resolve => setTimeout(resolve, 500));
form.querySelector("input[type=submit]").click();
});
await promiseShown;
let cb = getDoorhangerCheckbox();
ok(cb.hidden, "Sync checkbox should be hidden");
await clickDoorhangerButton(SECONDARY_BUTTON);
}
);
});
add_task(async function test_submit_creditCard_with_sync_account() {
await SpecialPowers.pushPrefEnv({
"set": [
[SYNC_USERNAME_PREF, "foo@bar.com"],
[SYNC_CREDITCARDS_AVAILABLE_PREF, true],
],
});
await BrowserTestUtils.withNewTab({gBrowser, url: CREDITCARD_FORM_URL},
async function(browser) {
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
"popupshown");
await ContentTask.spawn(browser, null, async function() {
let form = content.document.getElementById("form");
let name = form.querySelector("#cc-name");
name.focus();
name.setUserInput("User 2");
let number = form.querySelector("#cc-number");
number.setUserInput("1234123412341234");
// Wait 500ms before submission to make sure the input value applied
await new Promise(resolve => setTimeout(resolve, 500));
form.querySelector("input[type=submit]").click();
});
await promiseShown;
let cb = getDoorhangerCheckbox();
ok(!cb.hidden, "Sync checkbox should be visible");
is(SpecialPowers.getBoolPref(SYNC_CREDITCARDS_PREF), false,
"creditCards sync should be disabled by default");
// Verify if the checkbox and button state is changed.
let secondaryButton = getDoorhangerButton(SECONDARY_BUTTON);
let menuButton = getDoorhangerButton(MENU_BUTTON);
is(cb.checked, false, "Checkbox state should match creditCards sync state");
is(secondaryButton.disabled, false, "Not saving button should be enabled");
is(menuButton.disabled, false, "Never saving menu button should be enabled");
// Click the checkbox to enable credit card sync.
cb.click();
is(SpecialPowers.getBoolPref(SYNC_CREDITCARDS_PREF), true,
"creditCards sync should be enabled after checked");
is(secondaryButton.disabled, true, "Not saving button should be disabled");
is(menuButton.disabled, true, "Never saving menu button should be disabled");
// Click the checkbox again to disable credit card sync.
cb.click();
is(SpecialPowers.getBoolPref(SYNC_CREDITCARDS_PREF), false,
"creditCards sync should be disabled after unchecked");
is(secondaryButton.disabled, false, "Not saving button should be enabled again");
is(menuButton.disabled, false, "Never saving menu button should be enabled again");
await clickDoorhangerButton(MAIN_BUTTON);
}
);
});
add_task(async function test_submit_creditCard_with_synced_already() {
await SpecialPowers.pushPrefEnv({
"set": [
[SYNC_CREDITCARDS_PREF, true],
[SYNC_USERNAME_PREF, "foo@bar.com"],
[SYNC_CREDITCARDS_AVAILABLE_PREF, true],
],
});
await BrowserTestUtils.withNewTab({gBrowser, url: CREDITCARD_FORM_URL},
async function(browser) {
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
"popupshown");
await ContentTask.spawn(browser, null, async function() {
let form = content.document.getElementById("form");
let name = form.querySelector("#cc-name");
name.focus();
name.setUserInput("User 2");
let number = form.querySelector("#cc-number");
number.setUserInput("1234123412341234");
// Wait 500ms before submission to make sure the input value applied
await new Promise(resolve => setTimeout(resolve, 500));
form.querySelector("input[type=submit]").click();
});
await promiseShown;
let cb = getDoorhangerCheckbox();
ok(cb.hidden, "Sync checkbox should be hidden");
await clickDoorhangerButton(MAIN_BUTTON);
}
);
});

Просмотреть файл

@ -2,10 +2,11 @@
BASE_URL, TEST_ADDRESS_1, TEST_ADDRESS_2, TEST_ADDRESS_3, TEST_ADDRESS_4, TEST_ADDRESS_5,
TEST_CREDIT_CARD_1, TEST_CREDIT_CARD_2, TEST_CREDIT_CARD_3, FORM_URL, CREDITCARD_FORM_URL,
FTU_PREF, ENABLED_AUTOFILL_ADDRESSES_PREF, AUTOFILL_CREDITCARDS_AVAILABLE_PREF, ENABLED_AUTOFILL_CREDITCARDS_PREF,
SYNC_USERNAME_PREF, SYNC_ADDRESSES_PREF,
SYNC_USERNAME_PREF, SYNC_ADDRESSES_PREF, SYNC_CREDITCARDS_PREF, SYNC_CREDITCARDS_AVAILABLE_PREF,
sleep, expectPopupOpen, openPopupOn, expectPopupClose, closePopup, clickDoorhangerButton,
getAddresses, saveAddress, removeAddresses, saveCreditCard,
getDisplayedPopupItems, getDoorhangerCheckbox, waitForMasterPasswordDialog */
getDisplayedPopupItems, getDoorhangerCheckbox, waitForMasterPasswordDialog,
getNotification, getDoorhangerButton */
"use strict";
@ -25,6 +26,8 @@ const AUTOFILL_CREDITCARDS_AVAILABLE_PREF = "extensions.formautofill.creditCards
const ENABLED_AUTOFILL_CREDITCARDS_PREF = "extensions.formautofill.creditCards.enabled";
const SYNC_USERNAME_PREF = "services.sync.username";
const SYNC_ADDRESSES_PREF = "services.sync.engine.addresses";
const SYNC_CREDITCARDS_PREF = "services.sync.engine.creditcards";
const SYNC_CREDITCARDS_AVAILABLE_PREF = "services.sync.engine.creditcards.available";
const TEST_ADDRESS_1 = {
"given-name": "John",
@ -248,10 +251,15 @@ async function clickDoorhangerButton(button, index) {
await popuphidden;
}
function getDoorhangerCheckbox() {
return getNotification().checkbox;
}
function getDoorhangerButton(button) {
return getNotification()[button];
}
// Wait for the master password dialog to popup and enter the password to log in
// if "login" is "true" or dismiss it directly if otherwise.

Просмотреть файл

@ -90,7 +90,7 @@ function getTempFile(leafName) {
return file;
}
async function initProfileStorage(fileName, records) {
async function initProfileStorage(fileName, records, collectionName = "addresses") {
let {ProfileStorage} = Cu.import("resource://formautofill/ProfileStorage.jsm", {});
let path = getTempFile(fileName).path;
let profileStorage = new ProfileStorage(path);
@ -103,7 +103,7 @@ async function initProfileStorage(fileName, records) {
let onChanged = TestUtils.topicObserved("formautofill-storage-changed",
(subject, data) => data == "add");
for (let record of records) {
do_check_true(profileStorage.addresses.add(record));
do_check_true(profileStorage[collectionName].add(record));
await onChanged;
}
await profileStorage._saveImmediately();

Просмотреть файл

@ -52,29 +52,16 @@ const TEST_CREDIT_CARD_WITH_SPACES_BETWEEN_DIGITS = {
"cc-number": "1111 2222 3333 4444",
};
const TEST_CREDIT_CARD_WITH_INVALID_NUMBERS = {
"cc-name": "John Doe",
"cc-number": "abcdefg",
};
const TEST_CREDIT_CARD_WITH_SHORT_NUMBERS = {
"cc-name": "John Doe",
"cc-number": "1234567890",
};
let prepareTestCreditCards = async function(path) {
let profileStorage = new ProfileStorage(path);
await profileStorage.initialize();
let onChanged = TestUtils.topicObserved("formautofill-storage-changed",
(subject, data) => data == "add");
let encryptedCC_1 = Object.assign({}, TEST_CREDIT_CARD_1);
await profileStorage.creditCards.normalizeCCNumberFields(encryptedCC_1);
do_check_true(profileStorage.creditCards.add(encryptedCC_1));
do_check_true(profileStorage.creditCards.add(TEST_CREDIT_CARD_1));
await onChanged;
do_check_true(profileStorage.creditCards.add(TEST_CREDIT_CARD_2));
await onChanged;
let encryptedCC_2 = Object.assign({}, TEST_CREDIT_CARD_2);
await profileStorage.creditCards.normalizeCCNumberFields(encryptedCC_2);
do_check_true(profileStorage.creditCards.add(encryptedCC_2));
await profileStorage._saveImmediately();
};
@ -87,6 +74,7 @@ let do_check_credit_card_matches = (creditCardWithMeta, creditCard) => {
do_check_neq(matches, null);
do_check_eq(creditCardWithMeta["cc-number"].length, creditCard["cc-number"].length);
do_check_eq(creditCard["cc-number"].endsWith(matches[2]), true);
do_check_neq(creditCard["cc-number-encrypted"], "");
} else {
do_check_eq(creditCardWithMeta[key], creditCard[key]);
}
@ -182,9 +170,7 @@ add_task(async function test_add() {
do_check_eq(creditCards[0].timeLastUsed, 0);
do_check_eq(creditCards[0].timesUsed, 0);
let encryptedCC_invalid = Object.assign({}, TEST_CREDIT_CARD_WITH_INVALID_FIELD);
await profileStorage.creditCards.normalizeCCNumberFields(encryptedCC_invalid);
Assert.throws(() => profileStorage.creditCards.add(encryptedCC_invalid),
Assert.throws(() => profileStorage.creditCards.add(TEST_CREDIT_CARD_WITH_INVALID_FIELD),
/"invalidField" is not a valid field\./);
});
@ -203,7 +189,6 @@ add_task(async function test_update() {
(subject, data) => data == "update");
do_check_neq(creditCards[1]["cc-name"], undefined);
await profileStorage.creditCards.normalizeCCNumberFields(TEST_CREDIT_CARD_3);
profileStorage.creditCards.update(guid, TEST_CREDIT_CARD_3);
await onChanged;
await profileStorage._saveImmediately();
@ -222,10 +207,8 @@ add_task(async function test_update() {
/No matching record\./
);
let encryptedCC_invalid = Object.assign({}, TEST_CREDIT_CARD_WITH_INVALID_FIELD);
await profileStorage.creditCards.normalizeCCNumberFields(encryptedCC_invalid);
Assert.throws(
() => profileStorage.creditCards.update(guid, encryptedCC_invalid),
() => profileStorage.creditCards.update(guid, TEST_CREDIT_CARD_WITH_INVALID_FIELD),
/"invalidField" is not a valid field\./
);
});
@ -236,11 +219,8 @@ add_task(async function test_validate() {
let profileStorage = new ProfileStorage(path);
await profileStorage.initialize();
await profileStorage.creditCards.normalizeCCNumberFields(TEST_CREDIT_CARD_WITH_INVALID_EXPIRY_DATE);
profileStorage.creditCards.add(TEST_CREDIT_CARD_WITH_INVALID_EXPIRY_DATE);
await profileStorage.creditCards.normalizeCCNumberFields(TEST_CREDIT_CARD_WITH_2_DIGITS_YEAR);
profileStorage.creditCards.add(TEST_CREDIT_CARD_WITH_2_DIGITS_YEAR);
await profileStorage.creditCards.normalizeCCNumberFields(TEST_CREDIT_CARD_WITH_SPACES_BETWEEN_DIGITS);
profileStorage.creditCards.add(TEST_CREDIT_CARD_WITH_SPACES_BETWEEN_DIGITS);
let creditCards = profileStorage.creditCards.getAll();
@ -256,20 +236,6 @@ add_task(async function test_validate() {
do_check_eq(creditCards[1]["cc-exp"], year + "-" + month.toString().padStart(2, "0"));
do_check_eq(creditCards[2]["cc-number"].length, 16);
try {
await profileStorage.creditCards.normalizeCCNumberFields(TEST_CREDIT_CARD_WITH_INVALID_NUMBERS);
throw new Error("Not receiving invalid characters error");
} catch (e) {
Assert.equal(e.message, "Credit card number contains invalid characters or is under 12 digits.");
}
try {
await profileStorage.creditCards.normalizeCCNumberFields(TEST_CREDIT_CARD_WITH_SHORT_NUMBERS);
throw new Error("Not receiving invalid characters error");
} catch (e) {
Assert.equal(e.message, "Credit card number contains invalid characters or is under 12 digits.");
}
});
add_task(async function test_notifyUsed() {

Просмотреть файл

@ -157,12 +157,6 @@ const TESTCASES = [
contactType: "",
},
},
{
description: "non-input element",
document: `<label id="targetElement">street</label>`,
elementId: "targetElement",
expectedReturnValue: null,
},
{
description: "input element with \"submit\" type",
document: `<input id="targetElement" type="submit" />`,

Просмотреть файл

@ -5,6 +5,7 @@
"use strict";
Cu.import("resource://formautofill/FormAutofillParent.jsm");
Cu.import("resource://formautofill/MasterPassword.jsm");
Cu.import("resource://formautofill/ProfileStorage.jsm");
const TEST_ADDRESS_1 = {
@ -166,13 +167,16 @@ add_task(async function test_getRecords_creditCards() {
await formAutofillParent.init();
await formAutofillParent.profileStorage.initialize();
let collection = profileStorage.creditCards;
let decryptedCCNumber = [TEST_CREDIT_CARD_1["cc-number"], TEST_CREDIT_CARD_2["cc-number"]];
await collection.normalizeCCNumberFields(TEST_CREDIT_CARD_1);
await collection.normalizeCCNumberFields(TEST_CREDIT_CARD_2);
sinon.stub(collection, "getAll", () => [Object.assign({}, TEST_CREDIT_CARD_1), Object.assign({}, TEST_CREDIT_CARD_2)]);
let encryptedCCRecords = [TEST_CREDIT_CARD_1, TEST_CREDIT_CARD_2].map(record => {
let clonedRecord = Object.assign({}, record);
clonedRecord["cc-number"] = collection._getMaskedCCNumber(record["cc-number"]);
clonedRecord["cc-number-encrypted"] = MasterPassword.encryptSync(record["cc-number"]);
return clonedRecord;
});
sinon.stub(collection, "getAll", () => [Object.assign({}, encryptedCCRecords[0]), Object.assign({}, encryptedCCRecords[1])]);
let CreditCardsWithDecryptedNumber = [
Object.assign({}, TEST_CREDIT_CARD_1, {"cc-number-decrypted": decryptedCCNumber[0]}),
Object.assign({}, TEST_CREDIT_CARD_2, {"cc-number-decrypted": decryptedCCNumber[1]}),
Object.assign({}, encryptedCCRecords[0], {"cc-number-decrypted": TEST_CREDIT_CARD_1["cc-number"]}),
Object.assign({}, encryptedCCRecords[1], {"cc-number-decrypted": TEST_CREDIT_CARD_2["cc-number"]}),
];
let testCases = [
@ -229,7 +233,7 @@ add_task(async function test_getRecords_creditCards() {
searchString: "John Doe",
},
mpEnabled: true,
expectedResult: [TEST_CREDIT_CARD_1],
expectedResult: encryptedCCRecords.slice(0, 1),
},
{
description: "Return all creditCards if focused field is cc number (with masterpassword)",
@ -239,7 +243,7 @@ add_task(async function test_getRecords_creditCards() {
searchString: "123",
},
mpEnabled: true,
expectedResult: [TEST_CREDIT_CARD_1, TEST_CREDIT_CARD_2],
expectedResult: encryptedCCRecords,
},
];

Просмотреть файл

@ -11,7 +11,7 @@ const TEST_STORE_FILE_NAME = "test-profile.json";
// changed on a remote device)
//
// To further help understanding this, a few of the testcases are annotated.
const RECONCILE_TESTCASES = [
const ADDRESS_RECONCILE_TESTCASES = [
{
description: "Local change",
parent: {
@ -464,6 +464,458 @@ const RECONCILE_TESTCASES = [
},
];
const CREDIT_CARD_RECONCILE_TESTCASES = [
{
description: "Local change",
parent: {
// So when we last wrote the record to the server, it had these values.
"guid": "2bbd2d8fbc6b",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
},
local: [{
// The current local record - by comparing against parent we can see that
// only the cc-number has changed locally.
"cc-name": "John Doe",
"cc-number": "4444333322221111",
}],
remote: {
// This is the incoming record. It has the same values as "parent", so
// we can deduce the record hasn't actually been changed remotely so we
// can safely ignore the incoming record and write our local changes.
"guid": "2bbd2d8fbc6b",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
},
reconciled: {
"guid": "2bbd2d8fbc6b",
"cc-name": "John Doe",
"cc-number": "4444333322221111",
},
},
{
description: "Remote change",
parent: {
"guid": "e3680e9f890d",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
},
local: [{
"cc-name": "John Doe",
"cc-number": "1111222233334444",
}],
remote: {
"guid": "e3680e9f890d",
"version": 1,
"cc-name": "John Doe",
"cc-number": "4444333322221111",
},
reconciled: {
"guid": "e3680e9f890d",
"cc-name": "John Doe",
"cc-number": "4444333322221111",
},
},
{
description: "New local field",
parent: {
"guid": "0cba738b1be0",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
},
local: [{
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"cc-exp-month": 12,
}],
remote: {
"guid": "0cba738b1be0",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
},
reconciled: {
"guid": "0cba738b1be0",
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"cc-exp-month": 12,
},
},
{
description: "New remote field",
parent: {
"guid": "be3ef97f8285",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
},
local: [{
"cc-name": "John Doe",
"cc-number": "1111222233334444",
}],
remote: {
"guid": "be3ef97f8285",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"cc-exp-month": 12,
},
reconciled: {
"guid": "be3ef97f8285",
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"cc-exp-month": 12,
},
},
{
description: "Deleted field locally",
parent: {
"guid": "9627322248ec",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"cc-exp-month": 12,
},
local: [{
"cc-name": "John Doe",
"cc-number": "1111222233334444",
}],
remote: {
"guid": "9627322248ec",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"cc-exp-month": 12,
},
reconciled: {
"guid": "9627322248ec",
"cc-name": "John Doe",
"cc-number": "1111222233334444",
},
},
{
description: "Deleted field remotely",
parent: {
"guid": "7d7509f3eeb2",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"cc-exp-month": 12,
},
local: [{
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"cc-exp-month": 12,
}],
remote: {
"guid": "7d7509f3eeb2",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
},
reconciled: {
"guid": "7d7509f3eeb2",
"cc-name": "John Doe",
"cc-number": "1111222233334444",
},
},
{
description: "Local and remote changes to unrelated fields",
parent: {
// The last time we wrote this to the server, "cc-exp-month" was 12.
"guid": "e087a06dfc57",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"cc-exp-month": 12,
},
local: [{
// The current local record - so locally we've changed "cc-number".
"cc-name": "John Doe",
"cc-number": "4444333322221111",
"cc-exp-month": 12,
}],
remote: {
// Remotely, we've changed "cc-exp-month" to 1.
"guid": "e087a06dfc57",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"cc-exp-month": 1,
},
reconciled: {
"guid": "e087a06dfc57",
"cc-name": "John Doe",
"cc-number": "4444333322221111",
"cc-exp-month": 1,
},
},
{
description: "Multiple local changes",
parent: {
"guid": "340a078c596f",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
},
local: [{
"cc-name": "Skip",
"cc-number": "1111222233334444",
}, {
"cc-name": "Skip",
"cc-number": "1111222233334444",
"cc-exp-month": 12,
}],
remote: {
"guid": "340a078c596f",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"cc-exp-year": 2000,
},
reconciled: {
"guid": "340a078c596f",
"cc-name": "Skip",
"cc-number": "1111222233334444",
"cc-exp-month": 12,
"cc-exp-year": 2000,
},
},
{
// Local and remote diverged from the shared parent, but the values are the
// same, so we shouldn't fork.
description: "Same change to local and remote",
parent: {
"guid": "0b3a72a1bea2",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
},
local: [{
"cc-name": "John Doe",
"cc-number": "4444333322221111",
}],
remote: {
"guid": "0b3a72a1bea2",
"version": 1,
"cc-name": "John Doe",
"cc-number": "4444333322221111",
},
reconciled: {
"guid": "0b3a72a1bea2",
"cc-name": "John Doe",
"cc-number": "4444333322221111",
},
},
{
description: "Conflicting changes to single field",
parent: {
// This is what we last wrote to the sync server.
"guid": "62068784d089",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
},
local: [{
// The current version of the local record - the cc-number has changed locally.
"cc-name": "John Doe",
"cc-number": "1111111111111111",
}],
remote: {
// An incoming record has a different cc-number than any of the above!
"guid": "62068784d089",
"version": 1,
"cc-name": "John Doe",
"cc-number": "4444333322221111",
},
forked: {
// So we've forked the local record to a new GUID (and the next sync is
// going to write this as a new record)
"cc-name": "John Doe",
"cc-number": "1111111111111111",
},
reconciled: {
// And we've updated the local version of the record to be the remote version.
guid: "62068784d089",
"cc-name": "John Doe",
"cc-number": "4444333322221111",
},
},
{
description: "Conflicting changes to multiple fields",
parent: {
"guid": "244dbb692e94",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"cc-exp-month": 12,
},
local: [{
"cc-name": "John Doe",
"cc-number": "1111111111111111",
"cc-exp-month": 1,
}],
remote: {
"guid": "244dbb692e94",
"version": 1,
"cc-name": "John Doe",
"cc-number": "4444333322221111",
"cc-exp-month": 3,
},
forked: {
"cc-name": "John Doe",
"cc-number": "1111111111111111",
"cc-exp-month": 1,
},
reconciled: {
"guid": "244dbb692e94",
"cc-name": "John Doe",
"cc-number": "4444333322221111",
"cc-exp-month": 3,
},
},
{
description: "Field deleted locally, changed remotely",
parent: {
"guid": "6fc45e03d19a",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"cc-exp-month": 12,
},
local: [{
"cc-name": "John Doe",
"cc-number": "1111222233334444",
}],
remote: {
"guid": "6fc45e03d19a",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"cc-exp-month": 3,
},
forked: {
"cc-name": "John Doe",
"cc-number": "1111222233334444",
},
reconciled: {
"guid": "6fc45e03d19a",
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"cc-exp-month": 3,
},
},
{
description: "Field changed locally, deleted remotely",
parent: {
"guid": "fff9fa27fa18",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"cc-exp-month": 12,
},
local: [{
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"cc-exp-month": 3,
}],
remote: {
"guid": "fff9fa27fa18",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
},
forked: {
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"cc-exp-month": 3,
},
reconciled: {
"guid": "fff9fa27fa18",
"cc-name": "John Doe",
"cc-number": "1111222233334444",
},
},
{
// Created, last modified should be synced; last used and times used should
// be local. Remote created time older than local, remote modified time
// newer than local.
description: "Created, last modified time reconciliation without local changes",
parent: {
"guid": "5113f329c42f",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"timeCreated": 1234,
"timeLastModified": 5678,
"timeLastUsed": 5678,
"timesUsed": 6,
},
local: [],
remote: {
"guid": "5113f329c42f",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"timeCreated": 1200,
"timeLastModified": 5700,
"timeLastUsed": 5700,
"timesUsed": 3,
},
reconciled: {
"guid": "5113f329c42f",
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"timeCreated": 1200,
"timeLastModified": 5700,
"timeLastUsed": 5678,
"timesUsed": 6,
},
},
{
// Local changes, remote created time newer than local, remote modified time
// older than local.
description: "Created, last modified time reconciliation with local changes",
parent: {
"guid": "791e5608b80a",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"timeCreated": 1234,
"timeLastModified": 5678,
"timeLastUsed": 5678,
"timesUsed": 6,
},
local: [{
"cc-name": "John Doe",
"cc-number": "4444333322221111",
}],
remote: {
"guid": "791e5608b80a",
"version": 1,
"cc-name": "John Doe",
"cc-number": "1111222233334444",
"timeCreated": 1300,
"timeLastModified": 5000,
"timeLastUsed": 5000,
"timesUsed": 3,
},
reconciled: {
"guid": "791e5608b80a",
"cc-name": "John Doe",
"cc-number": "4444333322221111",
"timeCreated": 1234,
"timeLastUsed": 5678,
"timesUsed": 6,
},
},
];
add_task(async function test_reconcile_unknown_version() {
let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME);
@ -536,38 +988,47 @@ add_task(async function test_reconcile_idempotent() {
});
add_task(async function test_reconcile_three_way_merge() {
let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME);
let TESTCASES = {
addresses: ADDRESS_RECONCILE_TESTCASES,
creditCards: CREDIT_CARD_RECONCILE_TESTCASES,
};
for (let test of RECONCILE_TESTCASES) {
do_print(test.description);
for (let collectionName in TESTCASES) {
do_print(`Start to test reconcile on ${collectionName}`);
profileStorage.addresses.add(test.parent, {sourceSync: true});
let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME, null, collectionName);
for (let updatedRecord of test.local) {
profileStorage.addresses.update(test.parent.guid, updatedRecord);
}
for (let test of TESTCASES[collectionName]) {
do_print(test.description);
let localRecord = profileStorage.addresses.get(test.parent.guid, {
rawData: true,
});
profileStorage[collectionName].add(test.parent, {sourceSync: true});
let {forkedGUID} = profileStorage.addresses.reconcile(test.remote);
let reconciledRecord = profileStorage.addresses.get(test.parent.guid, {
rawData: true,
});
if (forkedGUID) {
let forkedRecord = profileStorage.addresses.get(forkedGUID, {
for (let updatedRecord of test.local) {
profileStorage[collectionName].update(test.parent.guid, updatedRecord);
}
let localRecord = profileStorage[collectionName].get(test.parent.guid, {
rawData: true,
});
notEqual(forkedRecord.guid, reconciledRecord.guid);
equal(forkedRecord.timeLastModified, localRecord.timeLastModified);
ok(objectMatches(forkedRecord, test.forked),
`${test.description} should fork record`);
} else {
ok(!test.forked, `${test.description} should not fork record`);
}
let {forkedGUID} = profileStorage[collectionName].reconcile(test.remote);
let reconciledRecord = profileStorage[collectionName].get(test.parent.guid, {
rawData: true,
});
if (forkedGUID) {
let forkedRecord = profileStorage[collectionName].get(forkedGUID, {
rawData: true,
});
ok(objectMatches(reconciledRecord, test.reconciled));
notEqual(forkedRecord.guid, reconciledRecord.guid);
equal(forkedRecord.timeLastModified, localRecord.timeLastModified);
ok(objectMatches(forkedRecord, test.forked),
`${test.description} should fork record`);
} else {
ok(!test.forked, `${test.description} should not fork record`);
}
ok(objectMatches(reconciledRecord, test.reconciled));
}
}
});

Просмотреть файл

@ -45,9 +45,6 @@ function add_storage_task(test_function) {
for (let [storage, record] of [[profileStorage.addresses, TEST_ADDRESS_1],
[profileStorage.creditCards, testCC1]]) {
if (storage.normalizeCCNumberFields) {
await storage.normalizeCCNumberFields(record);
}
await test_function(storage, record);
}
});

Просмотреть файл

@ -621,9 +621,7 @@ add_task(async function test_computeCreditCardFields() {
await profileStorage.initialize();
for (let testcase of CREDIT_CARD_COMPUTE_TESTCASES) {
let encryptedCC = Object.assign({}, testcase.creditCard);
await profileStorage.creditCards.normalizeCCNumberFields(encryptedCC);
profileStorage.creditCards.add(encryptedCC);
profileStorage.creditCards.add(testcase.creditCard);
}
await profileStorage._saveImmediately();
@ -645,9 +643,7 @@ add_task(async function test_normalizeCreditCardFields() {
await profileStorage.initialize();
for (let testcase of CREDIT_CARD_NORMALIZE_TESTCASES) {
let encryptedCC = Object.assign({}, testcase.creditCard);
await profileStorage.creditCards.normalizeCCNumberFields(encryptedCC);
profileStorage.creditCards.add(encryptedCC);
profileStorage.creditCards.add(testcase.creditCard);
}
await profileStorage._saveImmediately();

Просмотреть файл

@ -53,12 +53,13 @@ add_task(async function test_clean_up_uitour_after_closing_overlay() {
is(highlight.state, "open", "Should show UITour highlight");
is(highlight.getAttribute("targetName"), "library", "UITour should highlight library");
// Close the overlay by clicking the overlay close button
// Close the overlay by clicking the overlay
// Should not click the close button here since the close button is hovered by appmenu and can't be clicked on win7
highlightClosePromise = promisePopupChange(highlight, "closed");
BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-close-btn", {}, tab.linkedBrowser);
BrowserTestUtils.synthesizeMouseAtPoint(2, 2, {}, tab.linkedBrowser);
await promiseOnboardingOverlayClosed(tab.linkedBrowser);
await highlightClosePromise;
is(highlight.state, "closed", "Should close UITour highlight after closing the overlay by clicking the overlay close button");
is(highlight.state, "closed", "Should close UITour highlight after closing the overlay by clicking the overlay");
// Trigger UITour showHighlight again
highlightOpenPromise = promisePopupChange(highlight, "open");

Просмотреть файл

@ -783,7 +783,7 @@
"zh-TW": {
"default": {
"visibleDefaultEngines": [
"yahoo-zh-TW", "google", "ddg", "findbook-zh-TW", "wikipedia-zh-TW", "yahoo-zh-TW-HK", "yahoo-bid-zh-TW", "yahoo-answer-zh-TW"
"yahoo-zh-TW", "google", "ddg", "wikipedia-zh-TW", "yahoo-zh-TW-HK", "yahoo-bid-zh-TW", "yahoo-answer-zh-TW"
]
}
}

Просмотреть файл

@ -1,18 +0,0 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>Findbook</ShortName>
<Description>Findbook 書籍搜尋</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAArHEA/6xxAP+scQD/rHEA/6xxAP+scQD/rHEA/6xxAP+scQD/rHEA/6xxAP+scQD/rHEA/7a2tv8AAAAArHEA/LZ3AP/LhgT/1YwF/9SMBP/UjAT/1IwE/9SMBP/UjAT/1IwE/9SMBf/UjAX/1IwF/6xxAP+2trb/ypMqDqxxAP+2eAD/y4YC/9OMBf/QgQD/9uvV////////////uXAA/9WMBP/UjAX/1IwF/9SMBf+scQD/tra2/61zAuKscQD/tngA/8uGAv/UjAX/z4EA//Xmyv///////////7pwAP/VjAT/1IwF/9SMBf/UjAX/rHEA/7a2tv+scQD/rHEA/7Z4AP/LhgT/1IwF/8+BAP/15sr///////////+6cAD/1YwE/9SMBf/UjAX/1IwF/6xxAP+2trb/rHEA/6xxAP+2eAD/y4YC/9OMBf/QgQD/9ebK////////////tGQA/9GCAP/PgAD/0YQA/9SMBf+scQD/tra2/6xxAP+scQD/tngA/8uGBP/UjAX/z4EA//Xmyv/////////////////////////////////QggD/rHEA/7a2tv+scQD/rHEA/7Z4AP/LhgL/1IwF/9CBAP/15sr///////////+5hCL/yJg5/8iYO//BjjD/04kA/6xxAP+2trb/rHEA/6xxAP+2eAD/y4YE/9SMBf/OgQD/9ebK////////////um0A/9aKAP/UiQD/1YoA/9SLA/+scQD/tra2/6xxAP+scQD/tngA/8uGBP/UjAX/0IEA//Xmyf///////////7pwAP/VjAT/1IwF/9SLA//Tiwb/rHEA/7a2tv+scQD/rHEA/7Z4AP/LhgL/1IwE/859AP/15sr///////////+1ZwD/04YA/9GEAP/RgAD/zKVa/6xxAP+2trb/rHEA/6xxAP+2eAD/y4YB/9WMBf////////////////////////////////////////////////+scQD/tra2/6xxAP+scQD/tXUA/8uDAP/SkRj/yJc5/8iXOf/Ilzn/yJc5/8iWN//Ilzn/x5c5/8iXOf/Ilzn/rHEA/7a2tv+scQD/rHEA/7B2CP+scQD/rHEA/6xxAP+scQD/rHEA/6xxAP+scQD/rHEA/6xxAP+scQD/rHEA/61xAP+2trb/rHEA/6xyAfvp+///6PD//+z2///w+///9f////v//////////////////////////////////////////////6xxAP8AAAAArncM8axxAP+scQD/rHEA/6xxAP+scQD/rHEA/6xxAP+scQD/rHEA/6xxAP+scQD/rHEA/691B/mtcwPugAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAA%3D%3D</Image>
<Url type="application/x-suggestions+json" method="GET" template="http://findbook.tw/search/suggest?q={searchTerms}&amp;utm_source=ff-bundled&amp;utm_medium=mozsearch&amp;utm_campaign=search" />
<Url type="text/html" method="GET" template="http://findbook.tw/search" resultdomain="findbook.tw">
<Param name="q" value="{searchTerms}"/>
<Param name="utm_source" value="ff-bundled"/>
<Param name="utm_medium" value="mozsearch"/>
<Param name="utm_campaign" value="search"/>
</Url>
<SearchForm>http://findbook.tw/search</SearchForm>
</SearchPlugin>

Просмотреть файл

@ -132,9 +132,6 @@
#main-window:not([tabsintitlebar]) #TabsToolbar:not([collapsed="true"]) + #nav-bar,
#TabsToolbar:not([collapsed="true"]) + #nav-bar:-moz-lwtheme {
box-shadow: 0 calc(-1 * var(--tab-toolbar-navbar-overlap)) 0 var(--tabs-border);
/* Position the toolbar above the bottom of background tabs */
position: relative;
z-index: 1;
}
/* Always draw a border on Yosemite to ensure the border is well-defined there
@ -148,11 +145,7 @@
}
#main-window[tabsintitlebar] #TabsToolbar:not([collapsed="true"]) + #nav-bar:not(:-moz-lwtheme) {
border-top: 1px solid var(--tabs-border);
background-clip: padding-box;
/* Position the toolbar above the bottom of background tabs */
position: relative;
z-index: 1;
box-shadow: 0 calc(-1 * var(--tab-toolbar-navbar-overlap)) 0 var(--tabs-border);
}
}

Просмотреть файл

@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">
<defs>
<linearGradient id="gradient" gradientUnits="userSpaceOnUse" x1="30" y1="12.85" x2="30" y2="47.15">
<stop offset="0" style="stop-color: #e63b2e"/>
<stop offset="1" style="stop-color: #c33931"/>
<linearGradient id="gradient" gradientUnits="userSpaceOnUse" x1="20" y1="2.85" x2="20" y2="37.15">
<stop offset="0" stop-color="#e63b2e"/>
<stop offset="1" stop-color="#c33931"/>
</linearGradient>
</defs>
<path fill-rule="evenodd" clip-rule="evenodd" fill="url(#gradient)" d="M49.048,17.648H29.004 c-2.289-0.016-2.809-1.142-3.165-2.401c-0.359-1.269-1.076-2.397-3.229-2.397c-5.775,0-5.42,0-6.167,0 c-2.153,0-2.87,1.127-3.229,2.397c-0.359,1.269-0.882,2.403-3.214,2.403h0.94c-0.519,0.008-0.937,0.433-0.937,0.958v27.583 c0,0.53,0.426,0.959,0.952,0.959h38.093c0.526,0,0.952-0.429,0.952-0.959V18.607C50,18.077,49.574,17.648,49.048,17.648z M18.441,27.932c0-2.119,1.705-3.837,3.809-3.837c2.103,0,3.809,1.718,3.809,3.837c0,2.119-1.705,3.837-3.809,3.837 C20.146,31.769,18.441,30.051,18.441,27.932z M36.717,41.83c-1.525,0-1.525-2.305-6.864-2.305c-5.339,0-5.339,2.305-6.864,2.305 c-0.842,0-1.526-0.512-1.526-1.537c0-1.024,1.271-3.842,8.39-3.842c7.119,0,8.39,2.804,8.39,3.842 C38.243,41.331,37.56,41.83,36.717,41.83z M37.485,31.769c-2.104,0-3.809-1.718-3.809-3.837c0-2.119,1.705-3.837,3.809-3.837 c2.104,0,3.809,1.718,3.809,3.837C41.294,30.051,39.588,31.769,37.485,31.769z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="url(#gradient)" d="M39.048,6.798H19.004 C16.715,6.782 16.195,5.656 15.839,4.397 15.48,3.128 14.763,2 12.61,2 6.835,2 7.1899998,2 6.443,2 4.29,2 3.573,3.127 3.214,4.397 2.855,5.666 2.332,6.8 0,6.8H0.94 C0.421,6.808 0.003,7.233 0.003,7.758v27.583c0,0.53 0.426,0.959 0.952,0.959H39.048 C39.574,36.3 40,35.871 40,35.341 V 7.757 C40,7.227 39.574,6.798 39.048,6.798 Z M8.4409998,17.082c0,-2.119 1.7050002,-3.837 3.8090002,-3.837 2.103,0 3.809,1.718 3.809,3.837 0,2.119 -1.705,3.837 -3.809,3.837 -2.104,0 -3.8090002,-1.718 -3.8090002,-3.837z M26.717,30.98c-1.525,0 -1.525,-2.305 -6.864,-2.305 -5.339,0 -5.339,2.305 -6.864,2.305 -0.842,0 -1.526,-0.512 -1.526,-1.537 0,-1.024 1.271,-3.842 8.39,-3.842 7.119,0 8.39,2.804 8.39,3.842 0,1.038 -0.683,1.537 -1.526,1.537z m 0.768,-10.061c-2.104,0 -3.809,-1.718 -3.809,-3.837 0,-2.119 1.705,-3.837 3.809,-3.837 2.104,0 3.809,1.718 3.809,3.837 0,2.119 -1.706,3.837 -3.809,3.837z"/>
</svg>

До

Ширина:  |  Высота:  |  Размер: 1.5 KiB

После

Ширина:  |  Высота:  |  Размер: 1.5 KiB

Просмотреть файл

@ -8,8 +8,7 @@
define(function (require, exports, module) {
const { DOM: dom, createFactory, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
const TreeViewClass = require("devtools/client/shared/components/tree/TreeView");
const TreeView = createFactory(TreeViewClass);
const TreeView = createFactory(require("devtools/client/shared/components/tree/TreeView"));
const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
const { createFactories } = require("devtools/client/shared/react-utils");
@ -19,8 +18,6 @@ define(function (require, exports, module) {
const { Toolbar, ToolbarButton } = createFactories(require("./reps/Toolbar"));
const { div } = dom;
const AUTO_EXPAND_MAX_SIZE = 100 * 1024;
const AUTO_EXPAND_MAX_LEVEL = 7;
function isObject(value) {
return Object(value) === value;
@ -42,7 +39,7 @@ define(function (require, exports, module) {
PropTypes.bool,
PropTypes.number
]),
jsonTextLength: PropTypes.number,
expandedNodes: PropTypes.instanceOf(Set),
searchFilter: PropTypes.string,
actions: PropTypes.object,
},
@ -96,15 +93,6 @@ define(function (require, exports, module) {
width: "100%"
}];
// Expand the document by default if its size isn't bigger than 100KB.
let expandedNodes = new Set();
if (this.props.jsonTextLength <= AUTO_EXPAND_MAX_SIZE) {
expandedNodes = TreeViewClass.getExpandedNodes(
this.props.data,
{maxLevel: AUTO_EXPAND_MAX_LEVEL}
);
}
// Render tree component.
return TreeView({
object: this.props.data,
@ -112,7 +100,7 @@ define(function (require, exports, module) {
onFilter: this.onFilter,
columns: columns,
renderValue: this.renderValue,
expandedNodes: expandedNodes,
expandedNodes: this.props.expandedNodes,
});
},

Просмотреть файл

@ -34,7 +34,8 @@ define(function (require, exports, module) {
PropTypes.array,
PropTypes.bool,
PropTypes.number
])
]),
expandedNodes: PropTypes.instanceOf(Set),
},
getInitialState: function () {
@ -60,7 +61,7 @@ define(function (require, exports, module) {
title: JSONView.Locale.$STR("jsonViewer.tab.JSON")},
JsonPanel({
data: this.props.json,
jsonTextLength: this.props.jsonText.length,
expandedNodes: this.props.expandedNodes,
actions: this.props.actions,
searchFilter: this.state.searchFilter
})

Просмотреть файл

@ -201,24 +201,23 @@ Converter.prototype = {
// To save with the proper extension we need the original content type,
// which has been replaced by application/vnd.mozilla.json.view
function fixSave(request) {
let originalType;
let match;
if (request instanceof Ci.nsIHttpChannel) {
try {
let header = request.getResponseHeader("Content-Type");
originalType = header.split(";")[0];
match = header.match(/^(application\/(?:[^;]+\+)?json)(?:;|$)/);
} catch (err) {
// Handled below
}
} else {
let uri = request.QueryInterface(Ci.nsIChannel).URI.spec;
let match = uri.match(/^data:(.*?)[,;]/);
if (match) {
originalType = match[1];
}
match = uri.match(/^data:(application\/(?:[^;,]+\+)?json)[;,]/);
}
const JSON_TYPES = ["application/json", "application/manifest+json"];
if (!JSON_TYPES.includes(originalType)) {
originalType = JSON_TYPES[0];
let originalType;
if (match) {
originalType = match[1];
} else {
originalType = "application/json";
}
request.QueryInterface(Ci.nsIWritablePropertyBag);
request.setProperty("contentType", originalType);

Просмотреть файл

@ -90,8 +90,7 @@ JsonViewSniffer.prototype = {
// Check the response content type and if it's a valid type
// such as application/json or application/manifest+json
// change it to new internal type consumed by JSON View.
const JSON_TYPES = ["application/json", "application/manifest+json"];
if (JSON_TYPES.includes(request.contentType)) {
if (/^application\/(?:.+\+)?json$/.test(request.contentType)) {
return JSON_VIEW_MIME_TYPE;
}
}

Просмотреть файл

@ -10,8 +10,11 @@ define(function (require, exports, module) {
const { render } = require("devtools/client/shared/vendor/react-dom");
const { createFactories } = require("devtools/client/shared/react-utils");
const { MainTabbedArea } = createFactories(require("./components/MainTabbedArea"));
const TreeViewClass = require("devtools/client/shared/components/tree/TreeView");
const json = document.getElementById("json");
const AUTO_EXPAND_MAX_SIZE = 100 * 1024;
const AUTO_EXPAND_MAX_LEVEL = 7;
let prettyURL;
@ -35,6 +38,16 @@ define(function (require, exports, module) {
input.json = err;
}
// Expand the document by default if its size isn't bigger than 100KB.
if (!(input.json instanceof Error) && input.jsonText.length <= AUTO_EXPAND_MAX_SIZE) {
input.expandedNodes = TreeViewClass.getExpandedNodes(
input.json,
{maxLevel: AUTO_EXPAND_MAX_LEVEL}
);
} else {
input.expandedNodes = new Set();
}
json.remove();
/**

Просмотреть файл

@ -24,6 +24,7 @@ support-files =
[browser_jsonview_bug_1380828.js]
[browser_jsonview_ignore_charset.js]
[browser_jsonview_content_type.js]
[browser_jsonview_copy_headers.js]
subsuite = clipboard
skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts

Просмотреть файл

@ -0,0 +1,80 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const mimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
const handlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"]
.getService(Ci.nsIHandlerService);
let contentTypes = {
valid: [
"application/json",
"application/manifest+json",
"application/vnd.api+json",
"application/hal+json",
"application/json+json",
"application/whatever+json",
],
invalid: [
"text/json",
"text/hal+json",
"application/jsona",
"application/whatever+jsona",
],
};
add_task(function* () {
info("Test JSON content types started");
// Prevent saving files to disk.
let useDownloadDir = SpecialPowers.getBoolPref("browser.download.useDownloadDir");
SpecialPowers.setBoolPref("browser.download.useDownloadDir", false);
let { MockFilePicker } = SpecialPowers;
MockFilePicker.init(window);
MockFilePicker.returnValue = MockFilePicker.returnCancel;
for (let kind of Object.keys(contentTypes)) {
let isValid = kind === "valid";
for (let type of contentTypes[kind]) {
// Prevent "Open or Save" dialogs, which would make the test fail.
let mimeInfo = mimeSvc.getFromTypeAndExtension(type, null);
let exists = handlerSvc.exists(mimeInfo);
let {alwaysAskBeforeHandling} = mimeInfo;
mimeInfo.alwaysAskBeforeHandling = false;
handlerSvc.store(mimeInfo);
yield testType(isValid, type);
yield testType(isValid, type, ";foo=bar+json");
// Restore old nsIMIMEInfo
if (exists) {
Object.assign(mimeInfo, {alwaysAskBeforeHandling});
handlerSvc.store(mimeInfo);
} else {
handlerSvc.remove(mimeInfo);
}
}
}
// Restore old pref
registerCleanupFunction(function () {
MockFilePicker.cleanup();
SpecialPowers.setBoolPref("browser.download.useDownloadDir", useDownloadDir);
});
});
function testType(isValid, type, params = "") {
const TEST_JSON_URL = "data:" + type + params + ",[1,2,3]";
return addJsonViewTab(TEST_JSON_URL).then(async function () {
ok(isValid, "The JSON Viewer should only load for valid content types.");
is(await evalInContent("document.contentType"), type, "Got the right content type");
let count = await getElementCount(".jsonPanelBox .treeTable .treeRow");
is(count, 3, "There must be expected number of rows");
}, function () {
ok(!isValid, "The JSON Viewer should only not load for invalid content types.");
});
}

Просмотреть файл

@ -31,6 +31,11 @@ add_task(function* () {
// Clicking the label collapses the auto-expanded node.
yield clickJsonNode(".jsonPanelBox .treeTable .treeLabel");
is(yield countRows(), 1, "There must be one row");
// Collapsed nodes are preserved when switching panels.
yield selectJsonViewContentTab("headers");
yield selectJsonViewContentTab("json");
is(yield countRows(), 1, "There must still be one row");
});
function countRows() {

Просмотреть файл

@ -114,3 +114,8 @@ addMessageListener("Test:JsonView:WaitForFilter", function (msg) {
observer.observe(firstRow, { attributes: true });
});
addMessageListener("Test:JsonView:Eval", function (msg) {
let result = content.eval(msg.data.code);
sendAsyncMessage(msg.name, {result});
});

Просмотреть файл

@ -47,6 +47,12 @@ function addJsonViewTab(url, timeout = -1) {
let frameScriptUrl = rootDir + "doc_frame_script.js";
browser.messageManager.loadFrameScript(frameScriptUrl, false);
// Check if there is a JSONView object.
if (!content.window.wrappedJSObject.JSONView) {
deferred.reject("JSON Viewer did not load.");
return;
}
// Resolve if the JSONView is fully loaded or wait
// for an initialization event.
if (content.window.wrappedJSObject.JSONView.initialized) {
@ -171,3 +177,8 @@ function waitForFilter() {
function normalizeNewLines(value) {
return value.replace("(\r\n|\n)", "\n");
}
function evalInContent(code) {
return executeInContent("Test:JsonView:Eval", {code})
.then(result => result.result);
}

Просмотреть файл

@ -24,8 +24,9 @@
},
"firefox": {
"webSocketConnection": false,
"proxyHost": "localhost:9000",
"webSocketHost": "localhost:6080",
"host": "localhost",
"webSocketPort": 8116,
"tcpPort": 6080,
"mcPath": "./firefox"
},
"development": {

Просмотреть файл

@ -9,19 +9,18 @@
"codemirror": "^5.24.2",
"devtools-config": "=0.0.12",
"devtools-contextmenu": "=0.0.3",
"devtools-launchpad": "=0.0.96",
"devtools-modules": "=0.0.31",
"devtools-launchpad": "=0.0.103",
"devtools-modules": "=0.0.32",
"devtools-source-editor": "=0.0.3",
"immutable": "^3.8.1",
"jszip": "^3.1.3",
"react": "=15.3.2",
"react-dom": "=15.3.2",
"react": "=15.6.1",
"react-dom": "=15.6.1",
"react-redux": "=5.0.3",
"redux": "^3.6.0",
"reselect": "^2.5.4"
},
"devDependencies": {
"babel-preset-es2015": "^6.6.0",
"babel-register": "^6.24.0",
"file-loader": "^0.10.1"
},

Просмотреть файл

@ -23,21 +23,18 @@ let webpackConfig = {
test: /\.(png|svg)$/,
loader: "file-loader?name=[path][name].[ext]",
},
{
/*
* The version of webpack used in the launchpad seems to have trouble
* with the require("raw!${file}") that we use for the properties
* file in l10.js.
* This loader goes through the whole code and remove the "raw!" prefix
* so the raw-loader declared in devtools-launchpad config can load
* those files.
*/
test: /\.js$/,
loader: "rewrite-raw",
},
{
test: /\.js$/,
loaders: [
/**
* The version of webpack used in the launchpad seems to have trouble
* with the require("raw!${file}") that we use for the properties
* file in l10.js.
* This loader goes through the whole code and remove the "raw!" prefix
* so the raw-loader declared in devtools-launchpad config can load
* those files.
*/
"rewrite-raw",
// Replace all references to this.browserRequire() by require()
"rewrite-browser-require",
// Replace all references to loader.lazyRequire() by require()
@ -49,7 +46,7 @@ let webpackConfig = {
resolveLoader: {
modules: [
path.resolve("./node_modules"),
"node_modules",
path.resolve("../shared/webpack"),
]
},
@ -64,8 +61,9 @@ let webpackConfig = {
resolve: {
modules: [
// Make sure webpack is always looking for modules in
// `webconsole/node_modules` directory first.
path.resolve(__dirname, "node_modules"), "node_modules"
// `netmonitor/node_modules` directory first.
path.resolve(__dirname, "node_modules"),
"node_modules",
],
alias: {
"Services": "devtools-modules/src/Services",
@ -138,9 +136,22 @@ let config = toolboxConfig(webpackConfig, getConfig(), {
});
// Remove loaders from devtools-launchpad's webpack.config.js
// * For svg-inline loader:
// Netmonitor uses file loader to bundle image assets instead of svg-inline-loader
config.module.rules = config.module.rules
.filter((rule) => !["svg-inline-loader"].includes(rule.loader));
// For svg-inline loader:
// Using file loader to bundle image assets instead of svg-inline-loader
config.module.rules = config.module.rules.filter((rule) => !["svg-inline-loader"].includes(rule.loader));
// For PostCSS loader:
// Disable PostCSS loader
config.module.rules.forEach(rule => {
if (Array.isArray(rule.use)) {
rule.use.some((use, idx) => {
if (use.loader === "postcss-loader") {
rule.use = rule.use.slice(0, idx);
return true;
}
return false;
});
}
});
module.exports = config;

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -196,7 +196,13 @@ skip-if = true # Bug 1403188
# old console skip-if = e10s # Bug 1042253 - webconsole e10s tests
[browser_jsterm_accessibility.js]
[browser_jsterm_add_edited_input_to_history.js]
[browser_jsterm_autocomplete_array_no_index.js]
[browser_jsterm_autocomplete_escape_key.js]
[browser_jsterm_autocomplete_helpers.js]
[browser_jsterm_autocomplete_inside_text.js]
[browser_jsterm_autocomplete_nav_and_tab_key.js]
[browser_jsterm_autocomplete_return_key_no_selection.js]
[browser_jsterm_autocomplete_return_key.js]
[browser_jsterm_autocomplete-properties-with-non-alphanumeric-names.js]
[browser_jsterm_copy_command.js]
[browser_jsterm_dollar.js]
@ -218,8 +224,6 @@ skip-if = true # Bug 1404850
skip-if = true # Bug 1408919
[browser_webconsole_autocomplete_in_debugger_stackframe.js]
skip-if = true # Bug 1408920
[browser_webconsole_autocomplete_keys.js]
skip-if = true # Bug 1408921
[browser_webconsole_autocomplete_popup.js]
skip-if = true # Bug 1408922
[browser_webconsole_autocomplete_popup_close_on_tab_switch.js]

Просмотреть файл

@ -11,12 +11,9 @@
const TEST_URI = "data:text/html;charset=utf-8,Web Console test for bug 817834";
add_task(function* () {
let hud = yield openNewTabAndConsole(TEST_URI);
// Clearing history that might have been set in previous tests.
yield hud.jsterm.clearHistory();
add_task(async function () {
let hud = await openNewTabAndConsole(TEST_URI);
testEditedInputHistory(hud);
yield hud.jsterm.clearHistory();
});
function testEditedInputHistory(hud) {

Просмотреть файл

@ -0,0 +1,43 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// See Bug 585991.
const TEST_URI = `data:text/html;charset=utf-8,
<head>
<script>
window.foo = [1,2,3];
</script>
</head>
<body>bug 585991 - Autocomplete popup on array</body>`;
add_task(async function () {
let { jsterm } = await openNewTabAndConsole(TEST_URI);
const {
autocompletePopup: popup,
completeNode,
inputNode,
} = jsterm;
let onPopUpOpen = popup.once("popup-opened");
info("wait for popup to show");
jsterm.setInputValue("foo");
EventUtils.synthesizeKey(".", {});
await onPopUpOpen;
let popupItems = popup.getItems().map(e => e.label);
is(popupItems.includes("0"), false, "Completing on an array doesn't show numbers.");
info("press Escape to close the popup");
const onPopupClose = popup.once("popup-closed");
EventUtils.synthesizeKey("VK_ESCAPE", {});
await onPopupClose;
});

Просмотреть файл

@ -0,0 +1,55 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// See Bug 585991.
const TEST_URI = `data:text/html;charset=utf-8,
<head>
<script>
/* Create a prototype-less object so popup does not contain native
* Object prototype properties.
*/
window.foo = Object.create(null);
Object.assign(window.foo, {
item0: "value0",
item1: "value1",
});
</script>
</head>
<body>bug 585991 - autocomplete popup escape key usage test</body>`;
add_task(async function () {
let { jsterm } = await openNewTabAndConsole(TEST_URI);
info("web console opened");
const {
autocompletePopup: popup,
completeNode,
inputNode,
} = jsterm;
let onPopUpOpen = popup.once("popup-opened");
info("wait for completion: window.foo.");
jsterm.setInputValue("window.foo");
EventUtils.synthesizeKey(".", {});
await onPopUpOpen;
ok(popup.isOpen, "popup is open");
ok(popup.itemCount, "popup has items");
info("press Escape to close the popup");
const onPopupClose = popup.once("popup-closed");
EventUtils.synthesizeKey("VK_ESCAPE", {});
await onPopupClose;
ok(!popup.isOpen, "popup is not open after VK_ESCAPE");
is(jsterm.getInputValue(), "window.foo.", "completion was cancelled");
ok(!completeNode.value, "completeNode is empty");
});

Просмотреть файл

@ -0,0 +1,62 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// See Bug 812618.
const TEST_URI = `data:text/html;charset=utf-8,
<head>
<script>
window.testBugA = "hello world";
window.testBugB = "hello world 2";
</script>
</head>
<body>bug 812618 - test completion inside text</body>`;
add_task(async function () {
let { jsterm } = await openNewTabAndConsole(TEST_URI);
info("web console opened");
const {
autocompletePopup: popup,
completeNode,
inputNode,
} = jsterm;
const onPopUpOpen = popup.once("popup-opened");
const dumpString = "dump(window.testBu)";
jsterm.setInputValue(dumpString);
inputNode.selectionStart = inputNode.selectionEnd = dumpString.indexOf(")");
EventUtils.synthesizeKey("g", {});
await onPopUpOpen;
ok(popup.isOpen, "popup is open");
is(popup.itemCount, 2, "popup.itemCount is correct");
EventUtils.synthesizeKey("VK_DOWN", {});
is(popup.selectedIndex, 0, "popup.selectedIndex is correct");
ok(!completeNode.value, "completeNode.value is empty");
let items = popup.getItems().map(e => e.label);
let expectedItems = ["testBugB", "testBugA"];
is(items.join("-"), expectedItems.join("-"), "getItems returns the items we expect");
info("press Tab and wait for popup to hide");
const onPopupClose = popup.once("popup-closed");
EventUtils.synthesizeKey("VK_TAB", {});
await onPopupClose;
// At this point the completion suggestion should be accepted.
ok(!popup.isOpen, "popup is not open");
const expectedInput = "dump(window.testBugB)";
is(jsterm.getInputValue(), expectedInput, "completion was successful after VK_TAB");
is(inputNode.selectionStart, expectedInput.length - 1, "cursor location is correct");
is(inputNode.selectionStart, inputNode.selectionEnd, "cursor location (confirmed)");
ok(!completeNode.value, "completeNode is empty");
});

Просмотреть файл

@ -0,0 +1,109 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// See Bug 585991.
const TEST_URI = `data:text/html;charset=utf-8,
<head>
<script>
/* Create a prototype-less object so popup does not contain native
* Object prototype properties.
*/
window.foo = Object.create(null);
Object.assign(window.foo, {
item0: "value0",
item1: "value1",
item2: "value2",
item3: "value3",
});
</script>
</head>
<body>bug 585991 - autocomplete popup navigation and tab key usage test</body>`;
add_task(async function () {
let { jsterm } = await openNewTabAndConsole(TEST_URI);
info("web console opened");
const {
autocompletePopup: popup,
completeNode,
inputNode,
} = jsterm;
ok(!popup.isOpen, "popup is not open");
const onPopUpOpen = popup.once("popup-opened");
jsterm.setInputValue("window.foo");
// Shows the popup
EventUtils.synthesizeKey(".", {});
await onPopUpOpen;
ok(popup.isOpen, "popup is open");
const popupItems = popup.getItems().map(e => e.label);
const expectedPopupItems = [
"item3",
"item2",
"item1",
"item0",
];
is(popup.itemCount, expectedPopupItems.length, "popup.itemCount is correct");
is(popupItems.join("-"), expectedPopupItems.join("-"),
"getItems returns the items we expect");
is(popup.selectedIndex, expectedPopupItems.length - 1,
"Index of the first item from bottom is selected.");
EventUtils.synthesizeKey("VK_DOWN", {});
let prefix = jsterm.getInputValue().replace(/[\S]/g, " ");
is(popup.selectedIndex, 0, "index 0 is selected");
is(popup.selectedItem.label, "item3", "item3 is selected");
is(completeNode.value, prefix + "item3", "completeNode.value holds item3");
EventUtils.synthesizeKey("VK_DOWN", {});
is(popup.selectedIndex, 1, "index 1 is selected");
is(popup.selectedItem.label, "item2", "item2 is selected");
is(completeNode.value, prefix + "item2", "completeNode.value holds item2");
EventUtils.synthesizeKey("VK_UP", {});
is(popup.selectedIndex, 0, "index 0 is selected");
is(popup.selectedItem.label, "item3", "item3 is selected");
is(completeNode.value, prefix + "item3", "completeNode.value holds item3");
let currentSelectionIndex = popup.selectedIndex;
EventUtils.synthesizeKey("VK_PAGE_DOWN", {});
ok(popup.selectedIndex > currentSelectionIndex, "Index is greater after PGDN");
currentSelectionIndex = popup.selectedIndex;
EventUtils.synthesizeKey("VK_PAGE_UP", {});
ok(popup.selectedIndex < currentSelectionIndex, "Index is less after Page UP");
EventUtils.synthesizeKey("VK_END", {});
is(popup.selectedIndex, expectedPopupItems.length - 1, "index is last after End");
EventUtils.synthesizeKey("VK_HOME", {});
is(popup.selectedIndex, 0, "index is first after Home");
info("press Tab and wait for popup to hide");
const onPopupClose = popup.once("popup-closed");
EventUtils.synthesizeKey("VK_TAB", {});
await onPopupClose;
// At this point the completion suggestion should be accepted.
ok(!popup.isOpen, "popup is not open");
is(jsterm.getInputValue(), "window.foo.item3",
"completion was successful after VK_TAB");
ok(!completeNode.value, "completeNode is empty");
});

Просмотреть файл

@ -0,0 +1,81 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// See Bug 585991.
const TEST_URI = `data:text/html;charset=utf-8,
<head>
<script>
/* Create a prototype-less object so popup does not contain native
* Object prototype properties.
*/
window.foobar = Object.create(null);
Object.assign(window.foobar, {
item0: "value0",
item1: "value1",
item2: "value2",
item3: "value3",
});
</script>
</head>
<body>bug 585991 - test pressing return with open popup</body>`;
// We should turn off auto-multiline editing during these tests
const PREF_AUTO_MULTILINE = "devtools.webconsole.autoMultiline";
add_task(async function () {
Services.prefs.setBoolPref(PREF_AUTO_MULTILINE, false);
let { jsterm } = await openNewTabAndConsole(TEST_URI);
const {
autocompletePopup: popup,
completeNode,
inputNode,
} = jsterm;
let onPopUpOpen = popup.once("popup-opened");
info("wait for completion suggestions: window.foobar.");
jsterm.setInputValue("window.fooba");
EventUtils.synthesizeKey("r", {});
EventUtils.synthesizeKey(".", {});
await onPopUpOpen;
ok(popup.isOpen, "popup is open");
const expectedPopupItems = [
"item3",
"item2",
"item1",
"item0",
];
is(popup.itemCount, expectedPopupItems.length, "popup.itemCount is correct");
is(popup.selectedIndex, expectedPopupItems.length - 1,
"First index from bottom is selected");
EventUtils.synthesizeKey("VK_DOWN", {});
is(popup.selectedIndex, 0, "index 0 is selected");
is(popup.selectedItem.label, "item3", "item3 is selected");
let prefix = jsterm.getInputValue().replace(/[\S]/g, " ");
is(completeNode.value, prefix + "item3", "completeNode.value holds item3");
info("press Return to accept suggestion. wait for popup to hide");
const onPopupClose = popup.once("popup-closed");
EventUtils.synthesizeKey("VK_RETURN", {});
await onPopupClose;
ok(!popup.isOpen, "popup is not open after VK_RETURN");
is(jsterm.getInputValue(), "window.foobar.item3",
"completion was successful after VK_RETURN");
ok(!completeNode.value, "completeNode is empty");
Services.prefs.clearUserPref(PREF_AUTO_MULTILINE);
});

Просмотреть файл

@ -0,0 +1,49 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// See Bug 873250.
const TEST_URI = `data:text/html;charset=utf-8,
<head>
<script>
window.testBugA = "hello world";
window.testBugB = "hello world 2";
</script>
</head>
<body>bug 873250 - test pressing return with open popup, but no selection</body>`;
add_task(async function () {
let { jsterm } = await openNewTabAndConsole(TEST_URI);
const {
autocompletePopup: popup,
completeNode,
inputNode,
} = jsterm;
const onPopUpOpen = popup.once("popup-opened");
info("wait for popup to show");
jsterm.setInputValue("window.testBu");
EventUtils.synthesizeKey("g", {});
await onPopUpOpen;
ok(popup.isOpen, "popup is open");
is(popup.itemCount, 2, "popup.itemCount is correct");
isnot(popup.selectedIndex, -1, "popup.selectedIndex is correct");
info("press Return and wait for popup to hide");
const onPopUpClose = popup.once("popup-closed");
executeSoon(() => EventUtils.synthesizeKey("VK_RETURN", {}));
await onPopUpClose;
ok(!popup.isOpen, "popup is not open after VK_RETURN");
is(jsterm.getInputValue(), "", "inputNode is empty after VK_RETURN");
is(completeNode.value, "", "completeNode is empty");
is(jsterm.history[jsterm.history.length - 1], "window.testBug",
"jsterm history is correct");
});

Просмотреть файл

@ -11,13 +11,10 @@
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
"new-console-output/test/mochitest/test-jsterm-dollar.html";
add_task(function* () {
let hud = yield openNewTabAndConsole(TEST_URI);
yield test$(hud);
yield test$$(hud);
// Clear history to not affect next tests.
yield hud.jsterm.clearHistory();
add_task(async function () {
let hud = await openNewTabAndConsole(TEST_URI);
await test$(hud);
await test$$(hud);
});
async function test$(hud) {

Просмотреть файл

@ -14,37 +14,34 @@ const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " +
"persisting history - bug 943306";
const INPUT_HISTORY_COUNT = 10;
add_task(function* () {
add_task(async function() {
info("Setting custom input history pref to " + INPUT_HISTORY_COUNT);
Services.prefs.setIntPref("devtools.webconsole.inputHistoryCount", INPUT_HISTORY_COUNT);
// First tab: run a bunch of commands and then make sure that you can
// navigate through their history.
let hud1 = yield openNewTabAndConsole(TEST_URI);
// Clearing history that might have been set in previous tests.
yield hud1.jsterm.clearHistory();
let hud1 = await openNewTabAndConsole(TEST_URI);
is(JSON.stringify(hud1.jsterm.history), "[]", "No history on first tab initially");
yield populateInputHistory(hud1);
await populateInputHistory(hud1);
is(JSON.stringify(hud1.jsterm.history),
'["0","1","2","3","4","5","6","7","8","9"]',
"First tab has populated history");
// Second tab: Just make sure that you can navigate through the history
// generated by the first tab.
let hud2 = yield openNewTabAndConsole(TEST_URI);
let hud2 = await openNewTabAndConsole(TEST_URI, false);
is(JSON.stringify(hud2.jsterm.history),
'["0","1","2","3","4","5","6","7","8","9"]',
"Second tab has populated history");
yield testNavigatingHistoryInUI(hud2);
await testNavigatingHistoryInUI(hud2);
is(JSON.stringify(hud2.jsterm.history),
'["0","1","2","3","4","5","6","7","8","9",""]',
"An empty entry has been added in the second tab due to history perusal");
// Third tab: Should have the same history as first tab, but if we run a
// command, then the history of the first and second shouldn't be affected
let hud3 = yield openNewTabAndConsole(TEST_URI);
let hud3 = await openNewTabAndConsole(TEST_URI, false);
is(JSON.stringify(hud3.jsterm.history),
'["0","1","2","3","4","5","6","7","8","9"]',
"Third tab has populated history");
@ -67,15 +64,15 @@ add_task(function* () {
// Fourth tab: Should have the latest command from the third tab, followed
// by the rest of the history from the first tab.
let hud4 = yield openNewTabAndConsole(TEST_URI);
let hud4 = await openNewTabAndConsole(TEST_URI, false);
is(JSON.stringify(hud4.jsterm.history),
'["1","2","3","4","5","6","7","8","9","\\"hello from third tab\\""]',
"Fourth tab has most recent history");
yield hud4.jsterm.clearHistory();
await hud4.jsterm.clearHistory();
is(JSON.stringify(hud4.jsterm.history), "[]", "Clearing history for a tab works");
let hud5 = yield openNewTabAndConsole(TEST_URI);
let hud5 = await openNewTabAndConsole(TEST_URI, false);
is(JSON.stringify(hud5.jsterm.history), "[]",
"Clearing history carries over to a new tab");
@ -87,14 +84,14 @@ add_task(function* () {
* Populate the history by running the following commands:
* [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
*/
function* populateInputHistory(hud) {
async function populateInputHistory(hud) {
let jsterm = hud.jsterm;
for (let i = 0; i < INPUT_HISTORY_COUNT; i++) {
// Set input value separately from execute so UP arrow accurately navigates
// history.
jsterm.setInputValue(i);
yield jsterm.execute();
await jsterm.execute();
}
}

Просмотреть файл

@ -10,8 +10,8 @@
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
"test/test-console.html";
add_task(function* () {
let hud = yield openNewTabAndConsole(TEST_URI);
add_task(async function () {
let hud = await openNewTabAndConsole(TEST_URI);
testCompletion(hud);
});

Просмотреть файл

@ -9,8 +9,8 @@
const TEST_URI = "data:text/html,Testing jsterm with no input";
add_task(function* () {
let hud = yield openNewTabAndConsole(TEST_URI);
add_task(async function() {
let hud = await openNewTabAndConsole(TEST_URI);
testCompletion(hud);
});

Просмотреть файл

@ -9,8 +9,8 @@
const TEST_URI = "data:text/html,Testing jsterm focus";
add_task(function* () {
let hud = yield openNewTabAndConsole(TEST_URI);
add_task(async function() {
let hud = await openNewTabAndConsole(TEST_URI);
let jsterm = hud.jsterm;
let input = jsterm.inputNode;

Просмотреть файл

@ -1,369 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// See Bug 585991.
const TEST_URI = "data:text/html;charset=utf-8,<p>bug 585991 - autocomplete " +
"popup keyboard usage test";
// We should turn off auto-multiline editing during these tests
const PREF_AUTO_MULTILINE = "devtools.webconsole.autoMultiline";
var HUD, popup, jsterm, inputNode, completeNode;
add_task(function* () {
Services.prefs.setBoolPref(PREF_AUTO_MULTILINE, false);
yield loadTab(TEST_URI);
let hud = yield openConsole();
yield consoleOpened(hud);
yield popupHideAfterTab();
yield testReturnKey();
yield dontShowArrayNumbers();
yield testReturnWithNoSelection();
yield popupHideAfterReturnWithNoSelection();
yield testCompletionInText();
yield popupHideAfterCompletionInText();
HUD = popup = jsterm = inputNode = completeNode = null;
Services.prefs.setBoolPref(PREF_AUTO_MULTILINE, true);
});
var consoleOpened = Task.async(function* (hud) {
let deferred = defer();
HUD = hud;
info("web console opened");
jsterm = HUD.jsterm;
yield jsterm.execute("window.foobarBug585991={" +
"'item0': 'value0'," +
"'item1': 'value1'," +
"'item2': 'value2'," +
"'item3': 'value3'" +
"}");
yield jsterm.execute("window.testBug873250a = 'hello world';"
+ "window.testBug873250b = 'hello world 2';");
popup = jsterm.autocompletePopup;
completeNode = jsterm.completeNode;
inputNode = jsterm.inputNode;
ok(!popup.isOpen, "popup is not open");
popup.once("popup-opened", () => {
ok(popup.isOpen, "popup is open");
// 4 values, and the following properties:
// __defineGetter__ __defineSetter__ __lookupGetter__ __lookupSetter__
// __proto__ hasOwnProperty isPrototypeOf propertyIsEnumerable
// toLocaleString toString toSource unwatch valueOf watch constructor.
is(popup.itemCount, 19, "popup.itemCount is correct");
let sameItems = popup.getItems().reverse().map(function (e) {
return e.label;
});
ok(sameItems.every(function (prop, index) {
return [
"__defineGetter__",
"__defineSetter__",
"__lookupGetter__",
"__lookupSetter__",
"__proto__",
"constructor",
"hasOwnProperty",
"isPrototypeOf",
"item0",
"item1",
"item2",
"item3",
"propertyIsEnumerable",
"toLocaleString",
"toSource",
"toString",
"unwatch",
"valueOf",
"watch",
][index] === prop;
}), "getItems returns the items we expect");
is(popup.selectedIndex, 18,
"Index of the first item from bottom is selected.");
EventUtils.synthesizeKey("VK_DOWN", {});
let prefix = jsterm.getInputValue().replace(/[\S]/g, " ");
is(popup.selectedIndex, 0, "index 0 is selected");
is(popup.selectedItem.label, "watch", "watch is selected");
is(completeNode.value, prefix + "watch",
"completeNode.value holds watch");
EventUtils.synthesizeKey("VK_DOWN", {});
is(popup.selectedIndex, 1, "index 1 is selected");
is(popup.selectedItem.label, "valueOf", "valueOf is selected");
is(completeNode.value, prefix + "valueOf",
"completeNode.value holds valueOf");
EventUtils.synthesizeKey("VK_UP", {});
is(popup.selectedIndex, 0, "index 0 is selected");
is(popup.selectedItem.label, "watch", "watch is selected");
is(completeNode.value, prefix + "watch",
"completeNode.value holds watch");
let currentSelectionIndex = popup.selectedIndex;
EventUtils.synthesizeKey("VK_PAGE_DOWN", {});
ok(popup.selectedIndex > currentSelectionIndex,
"Index is greater after PGDN");
currentSelectionIndex = popup.selectedIndex;
EventUtils.synthesizeKey("VK_PAGE_UP", {});
ok(popup.selectedIndex < currentSelectionIndex,
"Index is less after Page UP");
EventUtils.synthesizeKey("VK_END", {});
is(popup.selectedIndex, 18, "index is last after End");
EventUtils.synthesizeKey("VK_HOME", {});
is(popup.selectedIndex, 0, "index is first after Home");
info("press Tab and wait for popup to hide");
popup.once("popup-closed", () => {
deferred.resolve();
});
EventUtils.synthesizeKey("VK_TAB", {});
});
jsterm.setInputValue("window.foobarBug585991");
EventUtils.synthesizeKey(".", {});
return deferred.promise;
});
function popupHideAfterTab() {
let deferred = defer();
// At this point the completion suggestion should be accepted.
ok(!popup.isOpen, "popup is not open");
is(jsterm.getInputValue(), "window.foobarBug585991.watch",
"completion was successful after VK_TAB");
ok(!completeNode.value, "completeNode is empty");
popup.once("popup-opened", function onShown() {
ok(popup.isOpen, "popup is open");
is(popup.itemCount, 19, "popup.itemCount is correct");
is(popup.selectedIndex, 18, "First index from bottom is selected");
EventUtils.synthesizeKey("VK_DOWN", {});
let prefix = jsterm.getInputValue().replace(/[\S]/g, " ");
is(popup.selectedIndex, 0, "index 0 is selected");
is(popup.selectedItem.label, "watch", "watch is selected");
is(completeNode.value, prefix + "watch",
"completeNode.value holds watch");
popup.once("popup-closed", function onHidden() {
ok(!popup.isOpen, "popup is not open after VK_ESCAPE");
is(jsterm.getInputValue(), "window.foobarBug585991.",
"completion was cancelled");
ok(!completeNode.value, "completeNode is empty");
deferred.resolve();
}, false);
info("press Escape to close the popup");
executeSoon(function () {
EventUtils.synthesizeKey("VK_ESCAPE", {});
});
}, false);
info("wait for completion: window.foobarBug585991.");
executeSoon(function () {
jsterm.setInputValue("window.foobarBug585991");
EventUtils.synthesizeKey(".", {});
});
return deferred.promise;
}
function testReturnKey() {
let deferred = defer();
popup.once("popup-opened", function onShown() {
ok(popup.isOpen, "popup is open");
is(popup.itemCount, 19, "popup.itemCount is correct");
is(popup.selectedIndex, 18, "First index from bottom is selected");
EventUtils.synthesizeKey("VK_DOWN", {});
let prefix = jsterm.getInputValue().replace(/[\S]/g, " ");
is(popup.selectedIndex, 0, "index 0 is selected");
is(popup.selectedItem.label, "watch", "watch is selected");
is(completeNode.value, prefix + "watch",
"completeNode.value holds watch");
EventUtils.synthesizeKey("VK_DOWN", {});
is(popup.selectedIndex, 1, "index 1 is selected");
is(popup.selectedItem.label, "valueOf", "valueOf is selected");
is(completeNode.value, prefix + "valueOf",
"completeNode.value holds valueOf");
popup.once("popup-closed", function onHidden() {
ok(!popup.isOpen, "popup is not open after VK_RETURN");
is(jsterm.getInputValue(), "window.foobarBug585991.valueOf",
"completion was successful after VK_RETURN");
ok(!completeNode.value, "completeNode is empty");
deferred.resolve();
}, false);
info("press Return to accept suggestion. wait for popup to hide");
executeSoon(() => EventUtils.synthesizeKey("VK_RETURN", {}));
}, false);
info("wait for completion suggestions: window.foobarBug585991.");
executeSoon(function () {
jsterm.setInputValue("window.foobarBug58599");
EventUtils.synthesizeKey("1", {});
EventUtils.synthesizeKey(".", {});
});
return deferred.promise;
}
function* dontShowArrayNumbers() {
let deferred = defer();
info("dontShowArrayNumbers");
yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
content.wrappedJSObject.foobarBug585991 = ["Sherlock Holmes"];
});
jsterm = HUD.jsterm;
popup = jsterm.autocompletePopup;
popup.once("popup-opened", function onShown() {
let sameItems = popup.getItems().map(function (e) {
return e.label;
});
ok(!sameItems.some(function (prop) {
prop === "0";
}), "Completing on an array doesn't show numbers.");
popup.once("popup-closed", function popupHidden() {
deferred.resolve();
}, false);
info("wait for popup to hide");
executeSoon(() => EventUtils.synthesizeKey("VK_ESCAPE", {}));
}, false);
info("wait for popup to show");
executeSoon(() => {
jsterm.setInputValue("window.foobarBug585991");
EventUtils.synthesizeKey(".", {});
});
return deferred.promise;
}
function testReturnWithNoSelection() {
let deferred = defer();
info("test pressing return with open popup, but no selection, see bug 873250");
popup.once("popup-opened", function onShown() {
ok(popup.isOpen, "popup is open");
is(popup.itemCount, 2, "popup.itemCount is correct");
isnot(popup.selectedIndex, -1, "popup.selectedIndex is correct");
info("press Return and wait for popup to hide");
popup.once("popup-closed", function popupHidden() {
deferred.resolve();
});
executeSoon(() => EventUtils.synthesizeKey("VK_RETURN", {}));
});
executeSoon(() => {
info("wait for popup to show");
jsterm.setInputValue("window.testBu");
EventUtils.synthesizeKey("g", {});
});
return deferred.promise;
}
function popupHideAfterReturnWithNoSelection() {
ok(!popup.isOpen, "popup is not open after VK_RETURN");
is(jsterm.getInputValue(), "", "inputNode is empty after VK_RETURN");
is(completeNode.value, "", "completeNode is empty");
is(jsterm.history[jsterm.history.length - 1], "window.testBug",
"jsterm history is correct");
return promise.resolve();
}
function testCompletionInText() {
info("test that completion works inside text, see bug 812618");
let deferred = defer();
popup.once("popup-opened", function onShown() {
ok(popup.isOpen, "popup is open");
is(popup.itemCount, 2, "popup.itemCount is correct");
EventUtils.synthesizeKey("VK_DOWN", {});
is(popup.selectedIndex, 0, "popup.selectedIndex is correct");
ok(!completeNode.value, "completeNode.value is empty");
let items = popup.getItems().reverse().map(e => e.label);
let sameItems = items.every((prop, index) =>
["testBug873250a", "testBug873250b"][index] === prop);
ok(sameItems, "getItems returns the items we expect");
info("press Tab and wait for popup to hide");
popup.once("popup-closed", function popupHidden() {
deferred.resolve();
});
EventUtils.synthesizeKey("VK_TAB", {});
});
jsterm.setInputValue("dump(window.testBu)");
inputNode.selectionStart = inputNode.selectionEnd = 18;
EventUtils.synthesizeKey("g", {});
return deferred.promise;
}
function popupHideAfterCompletionInText() {
// At this point the completion suggestion should be accepted.
ok(!popup.isOpen, "popup is not open");
is(jsterm.getInputValue(), "dump(window.testBug873250b)",
"completion was successful after VK_TAB");
is(inputNode.selectionStart, 26, "cursor location is correct");
is(inputNode.selectionStart, inputNode.selectionEnd,
"cursor location (confirmed)");
ok(!completeNode.value, "completeNode is empty");
return promise.resolve();
}

Просмотреть файл

@ -10,33 +10,33 @@
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/mochitest/test-batching.html";
const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
add_task(function* () {
let hud = yield openNewTabAndConsole(TEST_URI);
add_task(async function() {
let hud = await openNewTabAndConsole(TEST_URI);
const messageNumber = 100;
yield testSimpleBatchLogging(hud, messageNumber);
yield testBatchLoggingAndClear(hud, messageNumber);
await testSimpleBatchLogging(hud, messageNumber);
await testBatchLoggingAndClear(hud, messageNumber);
});
function* testSimpleBatchLogging(hud, messageNumber) {
yield ContentTask.spawn(gBrowser.selectedBrowser, messageNumber,
async function testSimpleBatchLogging(hud, messageNumber) {
await ContentTask.spawn(gBrowser.selectedBrowser, messageNumber,
function (numMessages) {
content.wrappedJSObject.batchLog(numMessages);
}
);
for (let i = 0; i < messageNumber; i++) {
let node = yield waitFor(() => findMessageAtIndex(hud, i, i));
let node = await waitFor(() => findMessageAtIndex(hud, i, i));
is(node.textContent, i.toString(), `message at index "${i}" is the expected one`);
}
}
function* testBatchLoggingAndClear(hud, messageNumber) {
yield ContentTask.spawn(gBrowser.selectedBrowser, messageNumber,
async function testBatchLoggingAndClear(hud, messageNumber) {
await ContentTask.spawn(gBrowser.selectedBrowser, messageNumber,
function (numMessages) {
content.wrappedJSObject.batchLogAndClear(numMessages);
}
);
yield waitFor(() => findMessage(hud, l10n.getStr("consoleCleared")));
await waitFor(() => findMessage(hud, l10n.getStr("consoleCleared")));
ok(true, "console cleared message is displayed");
// Passing the text argument as an empty string will returns all the message,

Просмотреть файл

@ -28,23 +28,23 @@ const TEST_URI = `data:text/html;charset=utf-8,<script>
// Test the Copy menu item of the webconsole copies the expected clipboard text for
// different log messages.
add_task(function* () {
add_task(async function() {
let observer = new PrefObserver("");
let onPrefUpdated = observer.once(PREF_MESSAGE_TIMESTAMP, () => {});
Services.prefs.setBoolPref(PREF_MESSAGE_TIMESTAMP, true);
yield onPrefUpdated;
await onPrefUpdated;
let hud = yield openNewTabAndConsole(TEST_URI);
let hud = await openNewTabAndConsole(TEST_URI);
hud.jsterm.clearOutput();
info("Call the log function defined in the test page");
yield ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
await ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
content.wrappedJSObject.logStuff();
});
info("Test copy menu item for the simple log");
let message = yield waitFor(() => findMessage(hud, "simple text message"));
let clipboardText = yield copyMessageContent(hud, message);
let message = await waitFor(() => findMessage(hud, "simple text message"));
let clipboardText = await copyMessageContent(hud, message);
ok(true, "Clipboard text was found and saved");
info("Check copied text for simple log message");
@ -55,8 +55,8 @@ add_task(function* () {
"Log line has the right format:\n" + lines[0]);
info("Test copy menu item for the stack trace message");
message = yield waitFor(() => findMessage(hud, "console.trace"));
clipboardText = yield copyMessageContent(hud, message);
message = await waitFor(() => findMessage(hud, "console.trace"));
clipboardText = await copyMessageContent(hud, message);
ok(true, "Clipboard text was found and saved");
info("Check copied text for stack trace message");
@ -72,11 +72,11 @@ add_task(function* () {
onPrefUpdated = observer.once(PREF_MESSAGE_TIMESTAMP, () => {});
Services.prefs.setBoolPref(PREF_MESSAGE_TIMESTAMP, false);
yield onPrefUpdated;
await onPrefUpdated;
info("Test copy menu item for the simple log");
message = yield waitFor(() => findMessage(hud, "simple text message"));
clipboardText = yield copyMessageContent(hud, message);
message = await waitFor(() => findMessage(hud, "simple text message"));
clipboardText = await copyMessageContent(hud, message);
ok(true, "Clipboard text was found and saved");
info("Check copied text for simple log message");
@ -87,8 +87,8 @@ add_task(function* () {
"Log line has the right format:\n" + lines[0]);
info("Test copy menu item for the stack trace message");
message = yield waitFor(() => findMessage(hud, "console.trace"));
clipboardText = yield copyMessageContent(hud, message);
message = await waitFor(() => findMessage(hud, "console.trace"));
clipboardText = await copyMessageContent(hud, message);
ok(true, "Clipboard text was found and saved");
info("Check copied text for stack trace message");
@ -108,13 +108,13 @@ add_task(function* () {
* Simple helper method to open the context menu on a given message, and click on the copy
* menu item.
*/
function* copyMessageContent(hud, message) {
let menuPopup = yield openContextMenu(hud, message);
async function copyMessageContent(hud, message) {
let menuPopup = await openContextMenu(hud, message);
let copyMenuItem = menuPopup.querySelector("#console-menu-copy");
ok(copyMenuItem, "copy menu item is enabled");
let clipboardText;
yield waitForClipboardPromise(
await waitForClipboardPromise(
() => copyMenuItem.click(),
data => {
clipboardText = data;

Просмотреть файл

@ -12,48 +12,48 @@ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
"new-console-output/test/mochitest/test-console.html?_date=" + Date.now();
const CONTEXT_MENU_ID = "#console-menu-copy-url";
add_task(function* () {
add_task(async function() {
// Enable net messages in the console for this test.
yield pushPref("devtools.webconsole.filter.net", true);
await pushPref("devtools.webconsole.filter.net", true);
let hud = yield openNewTabAndConsole(TEST_URI);
let hud = await openNewTabAndConsole(TEST_URI);
hud.jsterm.clearOutput();
info("Test Copy URL menu item for text log");
info("Logging a text message in the content window");
yield ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
await ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
content.wrappedJSObject.console.log("simple text message");
});
let message = yield waitFor(() => findMessage(hud, "simple text message"));
let message = await waitFor(() => findMessage(hud, "simple text message"));
ok(message, "Text log found in the console");
info("Open and check the context menu for the logged text message");
let menuPopup = yield openContextMenu(hud, message);
let menuPopup = await openContextMenu(hud, message);
let copyURLItem = menuPopup.querySelector(CONTEXT_MENU_ID);
ok(!copyURLItem, "Copy URL menu item is hidden for a simple text message");
yield hideContextMenu(hud);
await hideContextMenu(hud);
hud.jsterm.clearOutput();
info("Test Copy URL menu item for network log");
info("Reload the content window to produce a network log");
yield ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
await ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
content.wrappedJSObject.location.reload();
});
message = yield waitFor(() => findMessage(hud, "test-console.html"));
message = await waitFor(() => findMessage(hud, "test-console.html"));
ok(message, "Network log found in the console");
info("Open and check the context menu for the logged network message");
menuPopup = yield openContextMenu(hud, message);
menuPopup = await openContextMenu(hud, message);
copyURLItem = menuPopup.querySelector(CONTEXT_MENU_ID);
ok(copyURLItem, "Copy url menu item is available in context menu");
info("Click on Copy URL menu item and wait for clipboard to be updated");
yield waitForClipboardPromise(() => copyURLItem.click(), TEST_URI);
await waitForClipboardPromise(() => copyURLItem.click(), TEST_URI);
ok(true, "Expected text was copied to the clipboard.");
yield hideContextMenu(hud);
await hideContextMenu(hud);
});

Просмотреть файл

@ -22,22 +22,22 @@ const TEST_URI = `data:text/html;charset=utf-8,<script>
</script>`;
const copyObjectMenuItemId = "#console-menu-copy-object";
add_task(function* () {
let hud = yield openNewTabAndConsole(TEST_URI);
add_task(async function() {
let hud = await openNewTabAndConsole(TEST_URI);
let [msgWithText, msgWithObj, msgNested] =
yield waitFor(() => findMessages(hud, "foo"));
await waitFor(() => findMessages(hud, "foo"));
ok(msgWithText && msgWithObj && msgNested, "Three messages should have appeared");
let [groupMsgObj] = yield waitFor(() => findMessages(hud, "group", ".message-body"));
let [collapsedGroupMsgObj] = yield waitFor(() =>
let [groupMsgObj] = await waitFor(() => findMessages(hud, "group", ".message-body"));
let [collapsedGroupMsgObj] = await waitFor(() =>
findMessages(hud, "collapsed", ".message-body"));
let [numberMsgObj] = yield waitFor(() => findMessages(hud, `532`, ".message-body"));
let [trueMsgObj] = yield waitFor(() => findMessages(hud, `true`, ".message-body"));
let [falseMsgObj] = yield waitFor(() => findMessages(hud, `false`, ".message-body"));
let [undefinedMsgObj] = yield waitFor(() => findMessages(hud, `undefined`,
let [numberMsgObj] = await waitFor(() => findMessages(hud, `532`, ".message-body"));
let [trueMsgObj] = await waitFor(() => findMessages(hud, `true`, ".message-body"));
let [falseMsgObj] = await waitFor(() => findMessages(hud, `false`, ".message-body"));
let [undefinedMsgObj] = await waitFor(() => findMessages(hud, `undefined`,
".message-body"));
let [nullMsgObj] = yield waitFor(() => findMessages(hud, `null`, ".message-body"));
let [nullMsgObj] = await waitFor(() => findMessages(hud, `null`, ".message-body"));
ok(nullMsgObj, "One message with null value should have appeared");
let text = msgWithText.querySelector(".objectBox-string");
@ -49,50 +49,50 @@ add_task(function* () {
let topObjInMsg = msgNested.querySelector(".objectBox-array");
let nestedObjInMsg = msgNested.querySelector(".objectBox-object");
let consoleMessages = yield waitFor(() => findMessages(hud, "console.log(\"foo\");",
let consoleMessages = await waitFor(() => findMessages(hud, "console.log(\"foo\");",
".message-location"));
yield testCopyObjectMenuItemDisabled(hud, consoleMessages[0]);
await testCopyObjectMenuItemDisabled(hud, consoleMessages[0]);
info(`Check "Copy object" is enabled for text only messages
thus copying the text`);
yield testCopyObject(hud, text, `foo`, false);
await testCopyObject(hud, text, `foo`, false);
info(`Check "Copy object" is enabled for text in complex messages
thus copying the text`);
yield testCopyObject(hud, textInMsgWithObj, `foo`, false);
await testCopyObject(hud, textInMsgWithObj, `foo`, false);
info("Check `Copy object` is enabled for objects in complex messages");
yield testCopyObject(hud, objInMsgWithObj, `{"baz":1}`, true);
await testCopyObject(hud, objInMsgWithObj, `{"baz":1}`, true);
info("Check `Copy object` is enabled for top object in nested messages");
yield testCopyObject(hud, topObjInMsg, `["foo",{"baz":1},2]`, true);
await testCopyObject(hud, topObjInMsg, `["foo",{"baz":1},2]`, true);
info("Check `Copy object` is enabled for nested object in nested messages");
yield testCopyObject(hud, nestedObjInMsg, `{"baz":1}`, true);
await testCopyObject(hud, nestedObjInMsg, `{"baz":1}`, true);
info("Check `Copy object` is disabled on `console.group('group')` messages");
yield testCopyObjectMenuItemDisabled(hud, groupMsgObj);
await testCopyObjectMenuItemDisabled(hud, groupMsgObj);
info(`Check "Copy object" is disabled in "console.groupCollapsed('collapsed')"
messages`);
yield testCopyObjectMenuItemDisabled(hud, collapsedGroupMsgObj);
await testCopyObjectMenuItemDisabled(hud, collapsedGroupMsgObj);
// Check for primitive objects
info("Check `Copy object` is enabled for numbers");
yield testCopyObject(hud, numberMsgObj, `532`, false);
await testCopyObject(hud, numberMsgObj, `532`, false);
info("Check `Copy object` is enabled for booleans");
yield testCopyObject(hud, trueMsgObj, `true`, false);
yield testCopyObject(hud, falseMsgObj, `false`, false);
await testCopyObject(hud, trueMsgObj, `true`, false);
await testCopyObject(hud, falseMsgObj, `false`, false);
info("Check `Copy object` is enabled for undefined and null");
yield testCopyObject(hud, undefinedMsgObj, `undefined`, false);
yield testCopyObject(hud, nullMsgObj, `null`, false);
await testCopyObject(hud, undefinedMsgObj, `undefined`, false);
await testCopyObject(hud, nullMsgObj, `null`, false);
});
function* testCopyObject(hud, element, expectedMessage, objectInput) {
async function testCopyObject(hud, element, expectedMessage, objectInput) {
info("Check `Copy object` is enabled");
let menuPopup = yield openContextMenu(hud, element);
let menuPopup = await openContextMenu(hud, element);
let copyObjectMenuItem = menuPopup.querySelector(copyObjectMenuItemId);
ok(!copyObjectMenuItem.disabled,
"`Copy object` is enabled for object in complex message");
@ -103,19 +103,19 @@ function* testCopyObject(hud, element, expectedMessage, objectInput) {
};
info("Click on `Copy object`");
yield waitForClipboardPromise(() => copyObjectMenuItem.click(), validatorFn);
await waitForClipboardPromise(() => copyObjectMenuItem.click(), validatorFn);
info("`Copy object` by using the access-key O");
menuPopup = yield openContextMenu(hud, element);
yield waitForClipboardPromise(() => synthesizeKeyShortcut("O"), validatorFn);
menuPopup = await openContextMenu(hud, element);
await waitForClipboardPromise(() => synthesizeKeyShortcut("O"), validatorFn);
}
function* testCopyObjectMenuItemDisabled(hud, element) {
let menuPopup = yield openContextMenu(hud, element);
async function testCopyObjectMenuItemDisabled(hud, element) {
let menuPopup = await openContextMenu(hud, element);
let copyObjectMenuItem = menuPopup.querySelector(copyObjectMenuItemId);
ok(copyObjectMenuItem.disabled, `"Copy object" is disabled for messages
with no variables/objects`);
yield hideContextMenu(hud);
await hideContextMenu(hud);
}
function prettyPrintMessage(message, isObject) {

Просмотреть файл

@ -11,41 +11,41 @@
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
"new-console-output/test/mochitest/test-console.html";
add_task(function* () {
add_task(async function() {
// Enable net messages in the console for this test.
yield pushPref("devtools.webconsole.filter.net", true);
await pushPref("devtools.webconsole.filter.net", true);
let hud = yield openNewTabAndConsole(TEST_URI);
let hud = await openNewTabAndConsole(TEST_URI);
hud.jsterm.clearOutput();
info("Test Open URL menu item for text log");
info("Logging a text message in the content window");
yield ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
await ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
content.wrappedJSObject.console.log("simple text message");
});
let message = yield waitFor(() => findMessage(hud, "simple text message"));
let message = await waitFor(() => findMessage(hud, "simple text message"));
ok(message, "Text log found in the console");
info("Open and check the context menu for the logged text message");
let menuPopup = yield openContextMenu(hud, message);
let menuPopup = await openContextMenu(hud, message);
let openUrlItem = menuPopup.querySelector("#console-menu-open-url");
ok(!openUrlItem, "Open URL menu item is not available");
yield hideContextMenu(hud);
await hideContextMenu(hud);
hud.jsterm.clearOutput();
info("Test Open URL menu item for network log");
info("Reload the content window to produce a network log");
yield ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
await ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
content.wrappedJSObject.location.reload();
});
message = yield waitFor(() => findMessage(hud, "test-console.html"));
message = await waitFor(() => findMessage(hud, "test-console.html"));
ok(message, "Network log found in the console");
info("Open and check the context menu for the logged network message");
menuPopup = yield openContextMenu(hud, message);
menuPopup = await openContextMenu(hud, message);
openUrlItem = menuPopup.querySelector("#console-menu-open-url");
ok(openUrlItem, "Open URL menu item is available");
@ -53,8 +53,8 @@ add_task(function* () {
let tabLoaded = listenToTabLoad();
info("Click on Open URL menu item and wait for new tab to open");
openUrlItem.click();
yield hideContextMenu(hud);
let newTab = yield tabLoaded;
await hideContextMenu(hud);
let newTab = await tabLoaded;
let newTabHref = newTab.linkedBrowser._contentWindow.location.href;
is(newTabHref, TEST_URI, "Tab was opened with the expected URL");

Просмотреть файл

@ -15,11 +15,11 @@ const TEST_URI = `data:text/html;charset=utf-8,<script>
console.log(["foo", window.bar, 2]);
</script>`;
add_task(function* () {
let hud = yield openNewTabAndConsole(TEST_URI);
add_task(async function() {
let hud = await openNewTabAndConsole(TEST_URI);
let [msgWithText, msgWithObj, msgNested] =
yield waitFor(() => findMessages(hud, "foo"));
await waitFor(() => findMessages(hud, "foo"));
ok(msgWithText && msgWithObj && msgNested, "Three messages should have appeared");
let text = msgWithText.querySelector(".objectBox-string");
@ -32,49 +32,49 @@ add_task(function* () {
let nestedObjInMsg = msgNested.querySelector(".objectBox-object");
info("Check store as global variable is disabled for text only messages");
let menuPopup = yield openContextMenu(hud, text);
let menuPopup = await openContextMenu(hud, text);
let storeMenuItem = menuPopup.querySelector("#console-menu-store");
ok(storeMenuItem.disabled, "store as global variable is disabled for text message");
yield hideContextMenu(hud);
await hideContextMenu(hud);
info("Check store as global variable is disabled for text in complex messages");
menuPopup = yield openContextMenu(hud, textInMsgWithObj);
menuPopup = await openContextMenu(hud, textInMsgWithObj);
storeMenuItem = menuPopup.querySelector("#console-menu-store");
ok(storeMenuItem.disabled,
"store as global variable is disabled for text in complex message");
yield hideContextMenu(hud);
await hideContextMenu(hud);
info("Check store as global variable is enabled for objects in complex messages");
yield storeAsVariable(hud, objInMsgWithObj);
await storeAsVariable(hud, objInMsgWithObj);
is(hud.jsterm.getInputValue(), "temp0", "Input was set");
let executedResult = yield hud.jsterm.execute();
let executedResult = await hud.jsterm.execute();
ok(executedResult.textContent.includes("{ baz: 1 }"),
"Correct variable assigned into console");
info("Check store as global variable is enabled for top object in nested messages");
yield storeAsVariable(hud, topObjInMsg);
await storeAsVariable(hud, topObjInMsg);
is(hud.jsterm.getInputValue(), "temp1", "Input was set");
executedResult = yield hud.jsterm.execute();
executedResult = await hud.jsterm.execute();
ok(executedResult.textContent.includes(`[ "foo", {\u2026}, 2 ]`),
"Correct variable assigned into console " + executedResult.textContent);
info("Check store as global variable is enabled for nested object in nested messages");
yield storeAsVariable(hud, nestedObjInMsg);
await storeAsVariable(hud, nestedObjInMsg);
is(hud.jsterm.getInputValue(), "temp2", "Input was set");
executedResult = yield hud.jsterm.execute();
executedResult = await hud.jsterm.execute();
ok(executedResult.textContent.includes("{ baz: 1 }"),
"Correct variable assigned into console " + executedResult.textContent);
});
function* storeAsVariable(hud, element) {
async function storeAsVariable(hud, element) {
info("Check store as global variable is enabled");
let menuPopup = yield openContextMenu(hud, element);
let menuPopup = await openContextMenu(hud, element);
let storeMenuItem = menuPopup.querySelector("#console-menu-store");
ok(!storeMenuItem.disabled,
"store as global variable is enabled for object in complex message");
@ -84,8 +84,8 @@ function* storeAsVariable(hud, element) {
storeMenuItem.click();
info("Wait for console input to be updated with the temp variable");
yield onceInputSet;
await onceInputSet;
info("Wait for context menu to be hidden");
yield hideContextMenu(hud);
await hideContextMenu(hud);
}

Просмотреть файл

@ -8,16 +8,16 @@
"use strict";
const { MESSAGE_LEVEL } = require("devtools/client/webconsole/new-console-output/constants");
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/mochitest/test-console-filters.html";
add_task(function* () {
let hud = yield openNewTabAndConsole(TEST_URI);
add_task(async function () {
let hud = await openNewTabAndConsole(TEST_URI);
const outputNode = hud.ui.outputNode;
const toolbar = yield waitFor(() => {
const toolbar = await waitFor(() => {
return outputNode.querySelector(".webconsole-filterbar-primary");
});
ok(toolbar, "Toolbar found");
// Show the filter bar
toolbar.querySelector(".devtools-filter-icon").click();
const filterBar = yield waitFor(() => {
const filterBar = await waitFor(() => {
return outputNode.querySelector(".webconsole-filterbar-secondary");
});
ok(filterBar, "Filter bar is shown when filter icon is clicked.");
@ -39,20 +39,22 @@ add_task(function* () {
// Check that messages are not shown when their filter is turned off.
filterBar.querySelector(".error").click();
yield waitFor(() => findMessages(hud, "").length == 4);
await waitFor(() => findMessages(hud, "").length == 4);
ok(true, "When a filter is turned off, its messages are not shown.");
// Check that the ui settings were persisted.
yield closeTabAndToolbox();
yield testFilterPersistence();
await closeTabAndToolbox();
await testFilterPersistence();
});
function filterIsEnabled(button) {
return button.classList.contains("checked");
}
function* testFilterPersistence() {
let hud = yield openNewTabAndConsole(TEST_URI);
async function testFilterPersistence() {
let hud = await openNewTabAndConsole(TEST_URI);
const outputNode = hud.ui.outputNode;
const filterBar = yield waitFor(() => {
const filterBar = await waitFor(() => {
return outputNode.querySelector(".webconsole-filterbar-secondary");
});
ok(filterBar, "Filter bar ui setting is persisted.");

Просмотреть файл

@ -9,10 +9,10 @@
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/mochitest/test-console-filters.html";
add_task(function* () {
let hud = yield openNewTabAndConsole(TEST_URI);
add_task(async function() {
let hud = await openNewTabAndConsole(TEST_URI);
let filterButtons = yield getFilterButtons(hud);
let filterButtons = await getFilterButtons(hud);
info("Disable all filters");
filterButtons.forEach(filterButton => {
if (filterIsEnabled(filterButton)) {
@ -21,32 +21,33 @@ add_task(function* () {
});
info("Close and re-open the console");
yield closeTabAndToolbox();
hud = yield openNewTabAndConsole(TEST_URI);
await closeTabAndToolbox();
hud = await openNewTabAndConsole(TEST_URI);
info("Check that all filters are disabled, and enable them");
filterButtons = yield getFilterButtons(hud);
filterButtons = await getFilterButtons(hud);
filterButtons.forEach(filterButton => {
ok(!filterIsEnabled(filterButton), "filter is disabled");
filterButton.click();
});
info("Close and re-open the console");
yield closeTabAndToolbox();
hud = yield openNewTabAndConsole(TEST_URI);
await closeTabAndToolbox();
hud = await openNewTabAndConsole(TEST_URI);
info("Check that all filters are enabled");
filterButtons = yield getFilterButtons(hud);
filterButtons = await getFilterButtons(hud);
filterButtons.forEach(filterButton => {
ok(filterIsEnabled(filterButton), "filter is enabled");
});
// Check that the ui settings were persisted.
yield closeTabAndToolbox();
await closeTabAndToolbox();
});
function* getFilterButtons(hud) {
async function getFilterButtons(hud) {
const outputNode = hud.ui.outputNode;
info("Wait for console toolbar to appear");
const toolbar = yield waitFor(() => {
const toolbar = await waitFor(() => {
return outputNode.querySelector(".webconsole-filterbar-primary");
});
// Show the filter bar if it is hidden
@ -55,7 +56,7 @@ function* getFilterButtons(hud) {
}
info("Wait for console filterbar to appear");
const filterBar = yield waitFor(() => {
const filterBar = await waitFor(() => {
return outputNode.querySelector(".webconsole-filterbar-secondary");
});
ok(filterBar, "Filter bar is shown when filter icon is clicked.");

Просмотреть файл

@ -13,8 +13,8 @@ const TEST_URI =
console.log("console message 1");
</script>`;
add_task(function* () {
let hud = yield openNewTabAndConsole(TEST_URI);
add_task(async function() {
let hud = await openNewTabAndConsole(TEST_URI);
let inputNode = hud.jsterm.inputNode;
info("Focus after console is opened");
@ -24,19 +24,19 @@ add_task(function* () {
ok(hasFocus(inputNode), "input node is focused after output is cleared");
info("Focus during message logging");
ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
content.wrappedJSObject.console.log("console message 2");
});
let msg = yield waitFor(() => findMessage(hud, "console message 2"));
let msg = await waitFor(() => findMessage(hud, "console message 2"));
ok(hasFocus(inputNode, "input node is focused, first time"));
info("Focus after clicking in the output area");
yield waitForBlurredInput(hud);
await waitForBlurredInput(hud);
EventUtils.sendMouseEvent({type: "click"}, msg);
ok(hasFocus(inputNode), "input node is focused, second time");
info("Setting a text selection and making sure a click does not re-focus");
yield waitForBlurredInput(hud);
await waitForBlurredInput(hud);
let selection = hud.iframeWindow.getSelection();
selection.selectAllChildren(msg.querySelector(".message-body"));
EventUtils.sendMouseEvent({type: "click"}, msg);

Просмотреть файл

@ -16,11 +16,11 @@ const TEST_URI =
</script>
`;
add_task(function* () {
let hud = yield openNewTabAndConsole(TEST_URI);
add_task(async function () {
let hud = await openNewTabAndConsole(TEST_URI);
info("Web Console opened");
const outputScroller = hud.ui.outputScroller;
yield waitFor(() => findMessages(hud, "").length == 100);
await waitFor(() => findMessages(hud, "").length == 100);
let currentPosition = outputScroller.scrollTop;
const bottom = currentPosition;
hud.jsterm.inputNode.focus();
@ -53,7 +53,7 @@ add_task(function* () {
clearShortcut = WCUL10n.getStr("webconsole.clear.key");
}
synthesizeKeyShortcut(clearShortcut);
yield waitFor(() => findMessages(hud, "").length == 0);
await waitFor(() => findMessages(hud, "").length == 0);
ok(hasFocus(hud.jsterm.inputNode), "jsterm input is focused");
// Focus filter

Просмотреть файл

@ -12,12 +12,12 @@ requestLongerTimeout(2);
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
"new-console-output/test/mochitest/test-location-debugger-link.html";
add_task(function* () {
add_task(async function() {
// Force the new debugger UI, in case this gets uplifted with the old
// debugger still turned on
yield pushPref("devtools.debugger.new-debugger-frontend", true);
yield pushPref("devtools.webconsole.filter.error", true);
yield pushPref("devtools.webconsole.filter.log", true);
await pushPref("devtools.debugger.new-debugger-frontend", true);
await pushPref("devtools.webconsole.filter.error", true);
await pushPref("devtools.webconsole.filter.log", true);
// On e10s, the exception thrown in test-location-debugger-link-errors.js
// is triggered in child process and is ignored by test harness
@ -25,18 +25,18 @@ add_task(function* () {
expectUncaughtException();
}
let hud = yield openNewTabAndConsole(TEST_URI);
let hud = await openNewTabAndConsole(TEST_URI);
let target = TargetFactory.forTab(gBrowser.selectedTab);
let toolbox = gDevTools.getToolbox(target);
yield testOpenInDebugger(hud, toolbox, "document.bar");
await testOpenInDebugger(hud, toolbox, "document.bar");
info("Selecting the console again");
yield toolbox.selectTool("webconsole");
yield testOpenInDebugger(hud, toolbox, "Blah Blah");
await toolbox.selectTool("webconsole");
await testOpenInDebugger(hud, toolbox, "Blah Blah");
// // check again the first node.
info("Selecting the console again");
yield toolbox.selectTool("webconsole");
yield testOpenInDebugger(hud, toolbox, "document.bar");
await toolbox.selectTool("webconsole");
await testOpenInDebugger(hud, toolbox, "document.bar");
});

Просмотреть файл

@ -7,22 +7,22 @@
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/mochitest/test-location-styleeditor-link.html";
add_task(function* () {
yield pushPref("devtools.webconsole.filter.css", true);
let hud = yield openNewTabAndConsole(TEST_URI);
add_task(async function () {
await pushPref("devtools.webconsole.filter.css", true);
let hud = await openNewTabAndConsole(TEST_URI);
let target = TargetFactory.forTab(gBrowser.selectedTab);
let toolbox = gDevTools.getToolbox(target);
yield testViewSource(hud, toolbox, "\u2018font-weight\u2019");
await testViewSource(hud, toolbox, "\u2018font-weight\u2019");
info("Selecting the console again");
yield toolbox.selectTool("webconsole");
yield testViewSource(hud, toolbox, "\u2018color\u2019");
await toolbox.selectTool("webconsole");
await testViewSource(hud, toolbox, "\u2018color\u2019");
});
function* testViewSource(hud, toolbox, text) {
async function testViewSource(hud, toolbox, text) {
info(`Testing message with text "${text}"`);
let messageNode = yield waitFor(() => findMessage(hud, text));
let messageNode = await waitFor(() => findMessage(hud, text));
let frameLinkNode = messageNode.querySelector(".message-location .frame-link");
ok(frameLinkNode, "The message does have a location link");
@ -35,10 +35,10 @@ function* testViewSource(hud, toolbox, text) {
EventUtils.sendMouseEvent({ type: "click" },
messageNode.querySelector(".frame-link-filename"));
let panel = yield onStyleEditorSelected;
let panel = await onStyleEditorSelected;
ok(true, "The style editor is selected when clicking on the location element");
yield onStyleEditorReady(panel);
await onStyleEditorReady(panel);
info("style editor window focused");
let href = frameLinkNode.getAttribute("data-url");
@ -47,10 +47,10 @@ function* testViewSource(hud, toolbox, text) {
let editor = getEditorForHref(panel.UI, href);
ok(editor, "found style editor for " + href);
yield performLineCheck(panel.UI, editor, line - 1);
await performLineCheck(panel.UI, editor, line - 1);
}
function* onStyleEditorReady(panel) {
async function onStyleEditorReady(panel) {
let win = panel.panelWindow;
ok(win, "Style Editor Window is defined");
ok(panel.UI, "Style Editor UI is defined");
@ -74,10 +74,10 @@ function getEditorForHref(styleEditorUI, href) {
return foundEditor;
}
function* performLineCheck(styleEditorUI, editor, line) {
async function performLineCheck(styleEditorUI, editor, line) {
info("wait for source editor to load");
// Get out of the styleeditor-selected event loop.
yield waitForTick();
await waitForTick();
is(editor.sourceEditor.getCursor().line, line,
"correct line is selected");

Просмотреть файл

@ -21,15 +21,15 @@ const HTML = `
`;
const TEST_URI = "data:text/html;charset=utf-8," + encodeURI(HTML);
add_task(function* () {
const hud = yield openNewTabAndConsole(TEST_URI);
add_task(async function() {
const hud = await openNewTabAndConsole(TEST_URI);
const toolbox = gDevTools.getToolbox(hud.target);
yield ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
await ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
content.wrappedJSObject.logNode("h1");
});
let msg = yield waitFor(() => findMessage(hud, "<h1>"));
let msg = await waitFor(() => findMessage(hud, "<h1>"));
let node = msg.querySelector(".objectBox-node");
ok(node !== null, "Node was logged as expected");
const view = node.ownerDocument.defaultView;
@ -38,7 +38,7 @@ add_task(function* () {
let onNodeHighlight = toolbox.once("node-highlight");
EventUtils.synthesizeMouseAtCenter(node, {type: "mousemove"}, view);
let nodeFront = yield onNodeHighlight;
let nodeFront = await onNodeHighlight;
is(nodeFront.displayName, "h1", "The correct node was highlighted");
info("Unhighlight the node by moving away from the node");
@ -46,6 +46,6 @@ add_task(function* () {
let btn = toolbox.doc.querySelector(".toolbox-dock-button");
EventUtils.synthesizeMouseAtCenter(btn, {type: "mousemove"}, view);
yield onNodeUnhighlight;
await onNodeUnhighlight;
ok(true, "node-unhighlight event was fired when moving away from the node");
});

Просмотреть файл

@ -11,13 +11,13 @@ const TEST_URI = "data:text/html;charset=utf-8,<p>Web Console test for " +
let created = false;
let destroyed = false;
add_task(function* () {
add_task(async function() {
setupObserver();
yield openNewTabAndConsole(TEST_URI);
yield waitFor(() => created);
await openNewTabAndConsole(TEST_URI);
await waitFor(() => created);
yield closeTabAndToolbox(gBrowser.selectedTab);
yield waitFor(() => destroyed);
await closeTabAndToolbox(gBrowser.selectedTab);
await waitFor(() => destroyed);
});
function setupObserver() {

Просмотреть файл

@ -27,18 +27,18 @@ const PAGE_URL = `data:text/html,
</html>`;
add_task(function* () {
yield pushPref("devtools.source-map.client-service.enabled", true);
yield pushPref("devtools.webconsole.filter.css", true);
add_task(async function() {
await pushPref("devtools.source-map.client-service.enabled", true);
await pushPref("devtools.webconsole.filter.css", true);
const hud = yield openNewTabAndConsole(PAGE_URL);
const hud = await openNewTabAndConsole(PAGE_URL);
info("Waiting for css warning");
let node = yield waitFor(() => findMessage(hud, "octopus"));
let node = await waitFor(() => findMessage(hud, "octopus"));
ok(!!node, "css warning seen");
info("Waiting for source map to be applied");
let found = yield waitFor(() => {
let found = await waitFor(() => {
let frameLinkNode = node.querySelector(".message-location .frame-link");
let url = frameLinkNode.getAttribute("data-url");
return url.includes("scss");

Просмотреть файл

@ -24,11 +24,11 @@ const PAGE_URL = `data:text/html,
</html>`;
add_task(function* () {
yield pushPref("devtools.source-map.client-service.enabled", true);
add_task(async function() {
await pushPref("devtools.source-map.client-service.enabled", true);
const hud = yield openNewTabAndConsole(PAGE_URL);
const hud = await openNewTabAndConsole(PAGE_URL);
const node = yield waitFor(() => findMessage(hud, "Source map error"));
const node = await waitFor(() => findMessage(hud, "Source map error"));
ok(node, "source map error is displayed in web console");
});

Просмотреть файл

@ -24,17 +24,17 @@ const PAGE_URL = `data:text/html,
</html>`;
add_task(function* () {
add_task(async function() {
// Force the new debugger UI, in case this gets uplifted with the old
// debugger still turned on
yield pushPref("devtools.debugger.new-debugger-frontend", true);
yield pushPref("devtools.source-map.client-service.enabled", true);
await pushPref("devtools.debugger.new-debugger-frontend", true);
await pushPref("devtools.source-map.client-service.enabled", true);
const hud = yield openNewTabAndConsole(PAGE_URL);
const hud = await openNewTabAndConsole(PAGE_URL);
const toolbox = hud.ui.newConsoleOutput.toolbox;
info("Finding \"here\" message and waiting for source map to be applied");
yield waitFor(() => {
await waitFor(() => {
let node = findMessage(hud, "here");
if (!node) {
return false;
@ -44,12 +44,12 @@ add_task(function* () {
return url.includes("nosuchfile");
});
yield testOpenInDebugger(hud, toolbox, "here");
await testOpenInDebugger(hud, toolbox, "here");
info("Selecting the console again");
yield toolbox.selectTool("webconsole");
await toolbox.selectTool("webconsole");
const node = yield waitFor(() => findMessage(hud, "original source"));
const node = await waitFor(() => findMessage(hud, "original source"));
ok(node, "source map error is displayed in web console");
ok(!!node.querySelector(".learn-more-link"), "source map error has learn more link");

Просмотреть файл

@ -11,7 +11,7 @@
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
"new-console-output/test/mochitest/test-stacktrace-location-debugger-link.html";
add_task(function* () {
add_task(async function() {
// Force the new debugger UI, in case this gets uplifted with the old
// debugger still turned on
Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", true);
@ -21,29 +21,29 @@ add_task(function* () {
Services.prefs.clearUserPref("devtools.webconsole.filter.log");
});
let hud = yield openNewTabAndConsole(TEST_URI);
let hud = await openNewTabAndConsole(TEST_URI);
let target = TargetFactory.forTab(gBrowser.selectedTab);
let toolbox = gDevTools.getToolbox(target);
yield testOpenInDebugger(hud, toolbox, "console.trace()");
await testOpenInDebugger(hud, toolbox, "console.trace()");
});
function* testOpenInDebugger(hud, toolbox, text) {
async function testOpenInDebugger(hud, toolbox, text) {
info(`Testing message with text "${text}"`);
let messageNode = yield waitFor(() => findMessage(hud, text));
let messageNode = await waitFor(() => findMessage(hud, text));
let frameLinksNode = messageNode.querySelectorAll(".stack-trace .frame-link");
is(frameLinksNode.length, 3,
"The message does have the expected number of frames in the stacktrace");
for (let frameLinkNode of frameLinksNode) {
yield checkClickOnNode(hud, toolbox, frameLinkNode);
await checkClickOnNode(hud, toolbox, frameLinkNode);
info("Selecting the console again");
yield toolbox.selectTool("webconsole");
await toolbox.selectTool("webconsole");
}
}
function* checkClickOnNode(hud, toolbox, frameLinkNode) {
async function checkClickOnNode(hud, toolbox, frameLinkNode) {
info("checking click on node location");
let onSourceInDebuggerOpened = once(hud.ui, "source-in-debugger-opened");
@ -51,7 +51,7 @@ function* checkClickOnNode(hud, toolbox, frameLinkNode) {
EventUtils.sendMouseEvent({ type: "click" },
frameLinkNode.querySelector(".frame-link-source"));
yield onSourceInDebuggerOpened;
await onSourceInDebuggerOpened;
let url = frameLinkNode.getAttribute("data-url");
let dbg = toolbox.getPanel("jsdebugger");

Просмотреть файл

@ -7,8 +7,8 @@
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/mochitest/test-console.html";
add_task(function* () {
let hud = yield openNewTabAndConsole(TEST_URI);
add_task(async function() {
let hud = await openNewTabAndConsole(TEST_URI);
info("console.log with a string argument");
let receivedMessages = waitForMessages({
@ -19,16 +19,16 @@ add_task(function* () {
}],
});
yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
await ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
content.wrappedJSObject.stringLog();
});
yield receivedMessages;
await receivedMessages;
info("evaluating a string constant");
let jsterm = hud.jsterm;
yield jsterm.execute("\"string\\nconstant\"");
let msg = yield waitFor(() => findMessage(hud, "constant"));
await jsterm.execute("\"string\\nconstant\"");
let msg = await waitFor(() => findMessage(hud, "constant"));
let body = msg.querySelector(".message-body");
// On the other hand, a string constant result should be quoted, but
// newlines should be let through.

Просмотреть файл

@ -19,36 +19,36 @@ const TEST_URI = `data:text/html;charset=utf-8,
</script>`;
const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages";
add_task(function* () {
let hud = yield openNewTabAndConsole(TEST_URI);
add_task(async function() {
let hud = await openNewTabAndConsole(TEST_URI);
info("Call the log function defined in the test page");
yield ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
await ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
content.wrappedJSObject.logMessage();
});
yield testPrefDefaults(hud);
await testPrefDefaults(hud);
let observer = new PrefObserver("");
let toolbox = gDevTools.getToolbox(hud.target);
let optionsPanel = yield toolbox.selectTool("options");
yield togglePref(optionsPanel, observer);
let optionsPanel = await toolbox.selectTool("options");
await togglePref(optionsPanel, observer);
observer.destroy();
yield testChangedPref(hud);
await testChangedPref(hud);
Services.prefs.clearUserPref(PREF_MESSAGE_TIMESTAMP);
});
function* testPrefDefaults(hud) {
async function testPrefDefaults(hud) {
let prefValue = Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP);
ok(!prefValue, "Messages should have no timestamp by default (pref check)");
let message = yield waitFor(() => findMessage(hud, "simple text message"));
let message = await waitFor(() => findMessage(hud, "simple text message"));
is(message.querySelectorAll(".timestamp").length, 0,
"Messages should have no timestamp by default (element check)");
}
function* togglePref(panel, observer) {
async function togglePref(panel, observer) {
info("Options panel opened");
info("Changing pref");
@ -56,13 +56,13 @@ function* togglePref(panel, observer) {
let checkbox = panel.panelDoc.getElementById("webconsole-timestamp-messages");
checkbox.click();
yield prefChanged;
await prefChanged;
}
function* testChangedPref(hud) {
async function testChangedPref(hud) {
let prefValue = Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP);
ok(prefValue, "Messages should have timestamps (pref check)");
let message = yield waitFor(() => findMessage(hud, "simple text message"));
let message = await waitFor(() => findMessage(hud, "simple text message"));
is(message.querySelectorAll(".timestamp").length, 1,
"Messages should have timestamp (element check)");
}

Просмотреть файл

@ -42,16 +42,24 @@ registerCleanupFunction(function* () {
*
* @param string url
* The URL for the tab to be opened.
* @param Boolean clearJstermHistory
* true (default) if the jsterm history should be cleared.
* @return Promise
* Resolves when the tab has been added, loaded and the toolbox has been opened.
* Resolves to the toolbox.
*/
var openNewTabAndConsole = Task.async(function* (url) {
let toolbox = yield openNewTabAndToolbox(url, "webconsole");
async function openNewTabAndConsole(url, clearJstermHistory = true) {
let toolbox = await openNewTabAndToolbox(url, "webconsole");
let hud = toolbox.getCurrentPanel().hud;
hud.jsterm._lazyVariablesView = false;
if (clearJstermHistory) {
// Clearing history that might have been set in previous tests.
await hud.jsterm.clearHistory();
}
return hud;
});
}
/**
* Wait for messages in the web console output, resolving once they are received.
@ -220,18 +228,18 @@ function waitForNodeMutation(node, observeConfig = {}) {
* The text to search for. This should be contained in the
* message. The searching is done with @see findMessage.
*/
function* testOpenInDebugger(hud, toolbox, text) {
async function testOpenInDebugger(hud, toolbox, text) {
info(`Finding message for open-in-debugger test; text is "${text}"`);
let messageNode = yield waitFor(() => findMessage(hud, text));
let messageNode = await waitFor(() => findMessage(hud, text));
let frameLinkNode = messageNode.querySelector(".message-location .frame-link");
ok(frameLinkNode, "The message does have a location link");
yield checkClickOnNode(hud, toolbox, frameLinkNode);
await checkClickOnNode(hud, toolbox, frameLinkNode);
}
/**
* Helper function for testOpenInDebugger.
*/
function* checkClickOnNode(hud, toolbox, frameLinkNode) {
async function checkClickOnNode(hud, toolbox, frameLinkNode) {
info("checking click on node location");
let url = frameLinkNode.getAttribute("data-url");
@ -245,7 +253,7 @@ function* checkClickOnNode(hud, toolbox, frameLinkNode) {
EventUtils.sendMouseEvent({ type: "click" },
frameLinkNode.querySelector(".frame-link-filename"));
yield onSourceInDebuggerOpened;
await onSourceInDebuggerOpened;
let dbg = toolbox.getPanel("jsdebugger");
is(

Просмотреть файл

@ -322,7 +322,10 @@ waitForAllPaints(function() {
var parentElement = addDiv(null,
{ style: 'overflow-y: scroll; height: 100px;' });
var div = addDiv(null,
{ style: 'animation: background-color 100s; position: relative; top: 100px;' });
{ style: 'animation: background-color 100s; ' +
'position: relative; ' +
'top: 60px;' }); // This element is in-view in the parent, but
// out of view in the grandparent.
grandParent.appendChild(parentElement);
parentElement.appendChild(div);
var animation = div.getAnimations()[0];

Просмотреть файл

@ -2019,21 +2019,21 @@ MediaFormatReader::DecodeDemuxedSamples(TrackType aTrack,
decoder.mFlushed = false;
decoder.mDecoder->Decode(aSample)
->Then(mTaskQueue, __func__,
[self, this, aTrack, &decoder]
[self, aTrack, &decoder]
(const MediaDataDecoder::DecodedData& aResults) {
decoder.mDecodeRequest.Complete();
NotifyNewOutput(aTrack, aResults);
self->NotifyNewOutput(aTrack, aResults);
// When we recovered from a GPU crash and get the first decoded
// frame, report the recovery time telemetry.
if (aTrack == TrackType::kVideoTrack) {
GPUProcessCrashTelemetryLogger::ReportTelemetry(
mMediaDecoderOwnerID, decoder.mDecoder.get());
self->mMediaDecoderOwnerID, decoder.mDecoder.get());
}
},
[self, this, aTrack, &decoder](const MediaResult& aError) {
[self, aTrack, &decoder](const MediaResult& aError) {
decoder.mDecodeRequest.Complete();
NotifyError(aTrack, aError);
self->NotifyError(aTrack, aError);
})
->Track(decoder.mDecodeRequest);
}
@ -2208,21 +2208,21 @@ MediaFormatReader::DrainDecoder(TrackType aTrack)
RefPtr<MediaFormatReader> self = this;
decoder.mDecoder->Drain()
->Then(mTaskQueue, __func__,
[self, this, aTrack, &decoder]
[self, aTrack, &decoder]
(const MediaDataDecoder::DecodedData& aResults) {
decoder.mDrainRequest.Complete();
if (aResults.IsEmpty()) {
decoder.mDrainState = DrainState::DrainCompleted;
} else {
NotifyNewOutput(aTrack, aResults);
self->NotifyNewOutput(aTrack, aResults);
// Let's see if we have any more data available to drain.
decoder.mDrainState = DrainState::PartialDrainPending;
}
ScheduleUpdate(aTrack);
self->ScheduleUpdate(aTrack);
},
[self, this, aTrack, &decoder](const MediaResult& aError) {
[self, aTrack, &decoder](const MediaResult& aError) {
decoder.mDrainRequest.Complete();
NotifyError(aTrack, aError);
self->NotifyError(aTrack, aError);
})
->Track(decoder.mDrainRequest);
LOG("Requesting %s decoder to drain", TrackTypeToStr(aTrack));

Просмотреть файл

@ -56,7 +56,7 @@ RemoteVideoDecoder::Init()
RefPtr<RemoteVideoDecoder> self = this;
return InvokeAsync(VideoDecoderManagerChild::GetManagerAbstractThread(),
__func__,
[self, this]() { return mActor->Init(); })
[self]() { return self->mActor->Init(); })
->Then(VideoDecoderManagerChild::GetManagerAbstractThread(),
__func__,
[self, this](TrackType aTrack) {
@ -79,7 +79,7 @@ RemoteVideoDecoder::Decode(MediaRawData* aSample)
RefPtr<MediaRawData> sample = aSample;
return InvokeAsync(VideoDecoderManagerChild::GetManagerAbstractThread(),
__func__,
[self, this, sample]() { return mActor->Decode(sample); });
[self, sample]() { return self->mActor->Decode(sample); });
}
RefPtr<MediaDataDecoder::FlushPromise>
@ -87,7 +87,7 @@ RemoteVideoDecoder::Flush()
{
RefPtr<RemoteVideoDecoder> self = this;
return InvokeAsync(VideoDecoderManagerChild::GetManagerAbstractThread(),
__func__, [self, this]() { return mActor->Flush(); });
__func__, [self]() { return self->mActor->Flush(); });
}
RefPtr<MediaDataDecoder::DecodePromise>
@ -95,7 +95,7 @@ RemoteVideoDecoder::Drain()
{
RefPtr<RemoteVideoDecoder> self = this;
return InvokeAsync(VideoDecoderManagerChild::GetManagerAbstractThread(),
__func__, [self, this]() { return mActor->Drain(); });
__func__, [self]() { return self->mActor->Drain(); });
}
RefPtr<ShutdownPromise>
@ -103,8 +103,8 @@ RemoteVideoDecoder::Shutdown()
{
RefPtr<RemoteVideoDecoder> self = this;
return InvokeAsync(VideoDecoderManagerChild::GetManagerAbstractThread(),
__func__, [self, this]() {
mActor->Shutdown();
__func__, [self]() {
self->mActor->Shutdown();
return ShutdownPromise::CreateAndResolve(true, __func__);
});
}

Просмотреть файл

@ -166,7 +166,7 @@ VideoDecoderParent::RecvInput(const MediaRawDataIPDL& aData)
ProcessDecodedData(aResults);
Unused << SendInputExhausted();
},
[self, this](const MediaResult& aError) { Error(aError); });
[self](const MediaResult& aError) { self->Error(aError); });
return IPC_OK();
}
@ -224,12 +224,12 @@ VideoDecoderParent::RecvFlush()
RefPtr<VideoDecoderParent> self = this;
mDecoder->Flush()->Then(
mManagerTaskQueue, __func__,
[self, this]() {
if (!mDestroyed) {
Unused << SendFlushComplete();
[self]() {
if (!self->mDestroyed) {
Unused << self->SendFlushComplete();
}
},
[self, this](const MediaResult& aError) { Error(aError); });
[self](const MediaResult& aError) { self->Error(aError); });
return IPC_OK();
}
@ -248,7 +248,7 @@ VideoDecoderParent::RecvDrain()
Unused << SendDrainComplete();
}
},
[self, this](const MediaResult& aError) { Error(aError); });
[self](const MediaResult& aError) { self->Error(aError); });
return IPC_OK();
}

Просмотреть файл

@ -13,10 +13,11 @@
#include "mp4_demuxer/MoofParser.h"
#include "mozilla/Logging.h"
#include "mozilla/Maybe.h"
#include "mozilla/Result.h"
#include "MediaData.h"
#ifdef MOZ_FMP4
#include "mp4_demuxer/AtomType.h"
#include "mp4_demuxer/ByteReader.h"
#include "mp4_demuxer/BufferReader.h"
#include "mp4_demuxer/Stream.h"
#endif
#include "nsAutoPtr.h"
@ -477,9 +478,15 @@ private:
AtomParser(const MediaContainerType& aType, const MediaByteBuffer* aData,
StopAt aStop = StopAt::eEnd)
{
mValid = Init(aType, aData, aStop).isOk();
}
Result<Ok, nsresult> Init(const MediaContainerType& aType, const MediaByteBuffer* aData,
StopAt aStop)
{
const MediaContainerType mType(aType); // for logging macro.
mp4_demuxer::ByteReader reader(aData);
mp4_demuxer::BufferReader reader(aData);
mp4_demuxer::AtomType initAtom("moov");
mp4_demuxer::AtomType mediaAtom("moof");
mp4_demuxer::AtomType dataAtom("mdat");
@ -496,9 +503,12 @@ private:
};
while (reader.Remaining() >= 8) {
uint64_t size = reader.ReadU32();
uint32_t tmp;
MOZ_TRY_VAR(tmp, reader.ReadU32());
uint64_t size = tmp;
const uint8_t* typec = reader.Peek(4);
mp4_demuxer::AtomType type(reader.ReadU32());
MOZ_TRY_VAR(tmp, reader.ReadU32());
mp4_demuxer::AtomType type(tmp);
MSE_DEBUGV(AtomParser ,"Checking atom:'%c%c%c%c' @ %u",
typec[0], typec[1], typec[2], typec[3],
(uint32_t)reader.Offset() - 8);
@ -510,8 +520,7 @@ private:
mLastInvalidBox[2] = typec[2];
mLastInvalidBox[3] = typec[3];
mLastInvalidBox[4] = '\0';
mValid = false;
break;
return Err(NS_ERROR_FAILURE);
}
if (mInitOffset.isNothing() &&
mp4_demuxer::AtomType(type) == initAtom) {
@ -527,10 +536,7 @@ private:
}
if (size == 1) {
// 64 bits size.
if (!reader.CanReadType<uint64_t>()) {
break;
}
size = reader.ReadU64();
MOZ_TRY_VAR(size, reader.ReadU64());
} else if (size == 0) {
// Atom extends to the end of the buffer, it can't have what we're
// looking for.
@ -556,6 +562,8 @@ private:
break;
}
}
return Ok();
}
bool StartWithInitSegment() const
@ -574,7 +582,7 @@ private:
Maybe<size_t> mInitOffset;
Maybe<size_t> mMediaOffset;
Maybe<size_t> mDataOffset;
bool mValid = true;
bool mValid;
char mLastInvalidBox[5];
};

Просмотреть файл

@ -24,7 +24,7 @@ extern mozilla::LazyLogModule gMediaDemuxerLog;
using mozilla::media::TimeUnit;
using mozilla::media::TimeInterval;
using mozilla::media::TimeIntervals;
using mp4_demuxer::ByteReader;
using mp4_demuxer::BufferReader;
namespace mozilla {
@ -538,9 +538,10 @@ MP3TrackDemuxer::FindNextFrame()
break;
}
ByteReader reader(buffer, read);
BufferReader reader(buffer, read);
uint32_t bytesToSkip = 0;
foundFrame = mParser.Parse(&reader, &bytesToSkip);
auto res = mParser.Parse(&reader, &bytesToSkip);
foundFrame = res.isOk() ? res.unwrap() : false;
frameHeaderOffset =
mOffset + reader.Offset() - FrameParser::FrameHeader::SIZE;
@ -638,7 +639,7 @@ MP3TrackDemuxer::GetNextFrame(const MediaByteRange& aRange)
if (mNumParsedFrames == 1) {
// First frame parsed, let's read VBR info if available.
ByteReader reader(frame->Data(), frame->Size());
BufferReader reader(frame->Data(), frame->Size());
mParser.ParseVBRHeader(&reader);
mFirstFrameOffset = frame->mOffset;
}

Просмотреть файл

@ -11,6 +11,8 @@
#include "mozilla/Assertions.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/Pair.h"
#include "mozilla/ResultExtensions.h"
#include "VideoUtils.h"
extern mozilla::LazyLogModule gMediaDemuxerLog;
@ -19,7 +21,7 @@ extern mozilla::LazyLogModule gMediaDemuxerLog;
#define MP3LOGV(msg, ...) \
MOZ_LOG(gMediaDemuxerLog, LogLevel::Verbose, ("MP3Demuxer " msg, ##__VA_ARGS__))
using mp4_demuxer::ByteReader;
using mp4_demuxer::BufferReader;
namespace mozilla {
@ -93,8 +95,8 @@ FrameParser::VBRInfo() const
return mVBRHeader;
}
bool
FrameParser::Parse(ByteReader* aReader, uint32_t* aBytesToSkip)
Result<bool, nsresult>
FrameParser::Parse(BufferReader* aReader, uint32_t* aBytesToSkip)
{
MOZ_ASSERT(aReader && aBytesToSkip);
*aBytesToSkip = 0;
@ -104,8 +106,9 @@ FrameParser::Parse(ByteReader* aReader, uint32_t* aBytesToSkip)
// ID3v1 tags may only be at file end.
// TODO: should we try to read ID3 tags at end of file/mid-stream, too?
const size_t prevReaderOffset = aReader->Offset();
const uint32_t tagSize = mID3Parser.Parse(aReader);
if (tagSize) {
uint32_t tagSize;
MOZ_TRY_VAR(tagSize, mID3Parser.Parse(aReader));
if (!!tagSize) {
// ID3 tag found, skip past it.
const uint32_t skipSize = tagSize - ID3Parser::ID3Header::SIZE;
@ -128,7 +131,9 @@ FrameParser::Parse(ByteReader* aReader, uint32_t* aBytesToSkip)
}
}
while (aReader->CanRead8() && !mFrame.ParseNext(aReader->ReadU8())) { }
for (auto res = aReader->ReadU8();
res.isOk() && !mFrame.ParseNext(res.unwrap()); res = aReader->ReadU8())
{}
if (mFrame.Length()) {
// MP3 frame found.
@ -438,8 +443,8 @@ FrameParser::VBRHeader::Offset(float aDurationFac) const
return offset;
}
bool
FrameParser::VBRHeader::ParseXing(ByteReader* aReader)
Result<bool, nsresult>
FrameParser::VBRHeader::ParseXing(BufferReader* aReader)
{
static const uint32_t XING_TAG = BigEndian::readUint32("Xing");
static const uint32_t INFO_TAG = BigEndian::readUint32("Info");
@ -456,25 +461,28 @@ FrameParser::VBRHeader::ParseXing(ByteReader* aReader)
const size_t prevReaderOffset = aReader->Offset();
// We have to search for the Xing header as its position can change.
while (aReader->CanRead32() &&
aReader->PeekU32() != XING_TAG && aReader->PeekU32() != INFO_TAG) {
for (auto res = aReader->PeekU32();
res.isOk() && res.unwrap() != XING_TAG && res.unwrap() != INFO_TAG;) {
aReader->Read(1);
res = aReader->PeekU32();
}
if (aReader->CanRead32()) {
// Skip across the VBR header ID tag.
aReader->ReadU32();
mType = XING;
// Skip across the VBR header ID tag.
MOZ_TRY(aReader->ReadU32());
mType = XING;
uint32_t flags;
MOZ_TRY_VAR(flags, aReader->ReadU32());
if (flags & NUM_FRAMES) {
uint32_t frames;
MOZ_TRY_VAR(frames, aReader->ReadU32());
mNumAudioFrames = Some(frames);
}
uint32_t flags = 0;
if (aReader->CanRead32()) {
flags = aReader->ReadU32();
}
if (flags & NUM_FRAMES && aReader->CanRead32()) {
mNumAudioFrames = Some(aReader->ReadU32());
}
if (flags & NUM_BYTES && aReader->CanRead32()) {
mNumBytes = Some(aReader->ReadU32());
if (flags & NUM_BYTES) {
uint32_t bytes;
MOZ_TRY_VAR(bytes, aReader->ReadU32());
mNumBytes = Some(bytes);
}
if (flags & TOC && aReader->Remaining() >= vbr_header::TOC_SIZE) {
if (!mNumBytes) {
@ -483,21 +491,25 @@ FrameParser::VBRHeader::ParseXing(ByteReader* aReader)
} else {
mTOC.clear();
mTOC.reserve(vbr_header::TOC_SIZE);
uint8_t data;
for (size_t i = 0; i < vbr_header::TOC_SIZE; ++i) {
mTOC.push_back(1.0f / 256.0f * aReader->ReadU8() * mNumBytes.value());
MOZ_TRY_VAR(data, aReader->ReadU8());
mTOC.push_back(1.0f / 256.0f * data * mNumBytes.value());
}
}
}
if (flags & VBR_SCALE && aReader->CanRead32()) {
mScale = Some(aReader->ReadU32());
if (flags & VBR_SCALE) {
uint32_t scale;
MOZ_TRY_VAR(scale, aReader->ReadU32());
mScale = Some(scale);
}
aReader->Seek(prevReaderOffset);
return mType == XING;
}
bool
FrameParser::VBRHeader::ParseVBRI(ByteReader* aReader)
Result<bool, nsresult>
FrameParser::VBRHeader::ParseVBRI(BufferReader* aReader)
{
static const uint32_t TAG = BigEndian::readUint32("VBRI");
static const uint32_t OFFSET = 32 + FrameParser::FrameHeader::SIZE;
@ -508,15 +520,21 @@ FrameParser::VBRHeader::ParseVBRI(ByteReader* aReader)
// ParseVBRI assumes that the ByteReader offset points to the beginning of a
// frame, therefore as a simple check, we look for the presence of a frame
// sync at that position.
MOZ_ASSERT((aReader->PeekU16() & 0xFFE0) == 0xFFE0);
auto sync = aReader->PeekU16();
if (sync.isOk()) { // To avoid compiler complains 'set but unused'.
MOZ_ASSERT((sync.unwrap() & 0xFFE0) == 0xFFE0);
}
const size_t prevReaderOffset = aReader->Offset();
// VBRI have a fixed relative position, so let's check for it there.
if (aReader->Remaining() > MIN_FRAME_SIZE) {
aReader->Seek(prevReaderOffset + OFFSET);
if (aReader->ReadU32() == TAG) {
uint32_t tag, frames;
MOZ_TRY_VAR(tag, aReader->ReadU32());
if (tag == TAG) {
aReader->Seek(prevReaderOffset + FRAME_COUNT_OFFSET);
mNumAudioFrames = Some(aReader->ReadU32());
MOZ_TRY_VAR(frames, aReader->ReadU32());
mNumAudioFrames = Some(frames);
mType = VBRI;
aReader->Seek(prevReaderOffset);
return true;
@ -527,9 +545,11 @@ FrameParser::VBRHeader::ParseVBRI(ByteReader* aReader)
}
bool
FrameParser::VBRHeader::Parse(ByteReader* aReader)
FrameParser::VBRHeader::Parse(BufferReader* aReader)
{
const bool rv = ParseVBRI(aReader) || ParseXing(aReader);
auto res = MakePair(ParseVBRI(aReader), ParseXing(aReader));
const bool rv = (res.first().isOk() && res.first().unwrap()) ||
(res.second().isOk() && res.second().unwrap());
if (rv) {
MP3LOG("VBRHeader::Parse found valid VBR/CBR header: type=%s"
" NumAudioFrames=%u NumBytes=%u Scale=%u TOC-size=%zu",
@ -574,7 +594,7 @@ FrameParser::Frame::Header() const
}
bool
FrameParser::ParseVBRHeader(ByteReader* aReader)
FrameParser::ParseVBRHeader(BufferReader* aReader)
{
return mVBRHeader.Parse(aReader);
}
@ -599,12 +619,14 @@ static const uint8_t MIN_MAJOR_VER = 2;
static const uint8_t MAX_MAJOR_VER = 4;
} // namespace id3_header
uint32_t
ID3Parser::Parse(ByteReader* aReader)
Result<uint32_t, nsresult>
ID3Parser::Parse(BufferReader* aReader)
{
MOZ_ASSERT(aReader);
while (aReader->CanRead8() && !mHeader.ParseNext(aReader->ReadU8())) { }
for (auto res = aReader->ReadU8();
res.isOk() && !mHeader.ParseNext(res.unwrap()); res = aReader->ReadU8())
{}
return mHeader.TotalTagSize();
}

Просмотреть файл

@ -8,7 +8,8 @@
#include <vector>
#include "mozilla/Maybe.h"
#include "mp4_demuxer/ByteReader.h"
#include "mozilla/Result.h"
#include "mp4_demuxer/BufferReader.h"
namespace mozilla {
@ -81,9 +82,9 @@ public:
// Returns the parsed ID3 header. Note: check for validity.
const ID3Header& Header() const;
// Parses contents of given ByteReader for a valid ID3v2 header.
// Parses contents of given BufferReader for a valid ID3v2 header.
// Returns the total ID3v2 tag size if successful and zero otherwise.
uint32_t Parse(mp4_demuxer::ByteReader* aReader);
Result<uint32_t, nsresult> Parse(mp4_demuxer::BufferReader* aReader);
// Resets the state to allow for a new parsing session.
void Reset();
@ -227,20 +228,20 @@ public:
// The offset of the passed ByteReader needs to point to an MPEG frame
// begin, as a VBRI-style header is searched at a fixed offset relative to
// frame begin. Returns whether a valid VBR header was found in the range.
bool Parse(mp4_demuxer::ByteReader* aReader);
bool Parse(mp4_demuxer::BufferReader* aReader);
private:
// Parses contents of given ByteReader for a valid Xing header.
// The initial ByteReader offset will be preserved.
// Returns whether a valid Xing header was found in the range.
bool ParseXing(mp4_demuxer::ByteReader* aReader);
Result<bool, nsresult> ParseXing(mp4_demuxer::BufferReader* aReader);
// Parses contents of given ByteReader for a valid VBRI header.
// The initial ByteReader offset will be preserved. It also needs to point
// to the beginning of a valid MPEG frame, as VBRI headers are searched
// at a fixed offset relative to frame begin.
// Returns whether a valid VBRI header was found in the range.
bool ParseVBRI(mp4_demuxer::ByteReader* aReader);
Result<bool, nsresult> ParseVBRI(mp4_demuxer::BufferReader* aReader);
// The total number of frames expected as parsed from a VBR header.
Maybe<uint32_t> mNumAudioFrames;
@ -311,17 +312,17 @@ public:
// - resets ID3Header if no valid header was parsed yet
void EndFrameSession();
// Parses contents of given ByteReader for a valid frame header and returns
// Parses contents of given BufferReader for a valid frame header and returns
// true if one was found. After returning, the variable passed to
// 'aBytesToSkip' holds the amount of bytes to be skipped (if any) in order to
// jump across a large ID3v2 tag spanning multiple buffers.
bool Parse(mp4_demuxer::ByteReader* aReader, uint32_t* aBytesToSkip);
Result<bool, nsresult> Parse(mp4_demuxer::BufferReader* aReader, uint32_t* aBytesToSkip);
// Parses contents of given ByteReader for a valid VBR header.
// The offset of the passed ByteReader needs to point to an MPEG frame begin,
// Parses contents of given BufferReader for a valid VBR header.
// The offset of the passed BufferReader needs to point to an MPEG frame begin,
// as a VBRI-style header is searched at a fixed offset relative to frame
// begin. Returns whether a valid VBR header was found.
bool ParseVBRHeader(mp4_demuxer::ByteReader* aReader);
bool ParseVBRHeader(mp4_demuxer::BufferReader* aReader);
private:
// ID3 header parser.

Просмотреть файл

@ -80,8 +80,8 @@ RefPtr<ShutdownPromise>
AOMDecoder::Shutdown()
{
RefPtr<AOMDecoder> self = this;
return InvokeAsync(mTaskQueue, __func__, [self, this]() {
auto res = aom_codec_destroy(&mCodec);
return InvokeAsync(mTaskQueue, __func__, [self]() {
auto res = aom_codec_destroy(&self->mCodec);
if (res != AOM_CODEC_OK) {
LOG_RESULT(res, "aom_codec_destroy");
}

Просмотреть файл

@ -86,9 +86,9 @@ RefPtr<ShutdownPromise>
VPXDecoder::Shutdown()
{
RefPtr<VPXDecoder> self = this;
return InvokeAsync(mTaskQueue, __func__, [self, this]() {
vpx_codec_destroy(&mVPX);
vpx_codec_destroy(&mVPXAlpha);
return InvokeAsync(mTaskQueue, __func__, [self]() {
vpx_codec_destroy(&self->mVPX);
vpx_codec_destroy(&self->mVPXAlpha);
return ShutdownPromise::CreateAndResolve(true, __func__);
});
}

Просмотреть файл

@ -276,12 +276,12 @@ RefPtr<MediaDataDecoder::FlushPromise>
VorbisDataDecoder::Flush()
{
RefPtr<VorbisDataDecoder> self = this;
return InvokeAsync(mTaskQueue, __func__, [self, this]() {
return InvokeAsync(mTaskQueue, __func__, [self]() {
// Ignore failed results from vorbis_synthesis_restart. They
// aren't fatal and it fails when ResetDecode is called at a
// time when no vorbis data has been read.
vorbis_synthesis_restart(&mVorbisDsp);
mLastFrameTime.reset();
vorbis_synthesis_restart(&self->mVorbisDsp);
self->mLastFrameTime.reset();
return FlushPromise::CreateAndResolve(true, __func__);
});
}

Просмотреть файл

@ -57,12 +57,12 @@ public:
RefPtr<EMEDecryptor> self = this;
mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)
->Then(mTaskQueue, __func__,
[self, this](RefPtr<MediaRawData> aSample) {
mKeyRequest.Complete();
ThrottleDecode(aSample);
[self](RefPtr<MediaRawData> aSample) {
self->mKeyRequest.Complete();
self->ThrottleDecode(aSample);
},
[self, this]() {
mKeyRequest.Complete();
[self]() {
self->mKeyRequest.Complete();
})
->Track(mKeyRequest);
@ -74,12 +74,12 @@ public:
RefPtr<EMEDecryptor> self = this;
mThroughputLimiter.Throttle(aSample)
->Then(mTaskQueue, __func__,
[self, this] (RefPtr<MediaRawData> aSample) {
mThrottleRequest.Complete();
AttemptDecode(aSample);
[self] (RefPtr<MediaRawData> aSample) {
self->mThrottleRequest.Complete();
self->AttemptDecode(aSample);
},
[self, this]() {
mThrottleRequest.Complete();
[self]() {
self->mThrottleRequest.Complete();
})
->Track(mThrottleRequest);
}
@ -144,13 +144,13 @@ public:
RefPtr<EMEDecryptor> self = this;
mDecoder->Decode(aDecrypted.mSample)
->Then(mTaskQueue, __func__,
[self, this](const DecodedData& aResults) {
mDecodeRequest.Complete();
mDecodePromise.ResolveIfExists(aResults, __func__);
[self](const DecodedData& aResults) {
self->mDecodeRequest.Complete();
self->mDecodePromise.ResolveIfExists(aResults, __func__);
},
[self, this](const MediaResult& aError) {
mDecodeRequest.Complete();
mDecodePromise.RejectIfExists(aError, __func__);
[self](const MediaResult& aError) {
self->mDecodeRequest.Complete();
self->mDecodePromise.RejectIfExists(aError, __func__);
})
->Track(mDecodeRequest);
}
@ -286,8 +286,8 @@ EMEMediaDataDecoderProxy::Decode(MediaRawData* aSample)
})
->Track(mDecodeRequest);
},
[self, this]() {
mKeyRequest.Complete();
[self]() {
self->mKeyRequest.Complete();
MOZ_CRASH("Should never get here");
})
->Track(mKeyRequest);

Просмотреть файл

@ -550,7 +550,7 @@ RemoteDataDecoder::Decode(MediaRawData* aSample)
RefPtr<RemoteDataDecoder> self = this;
RefPtr<MediaRawData> sample = aSample;
return InvokeAsync(mTaskQueue, __func__, [self, sample, this]() {
return InvokeAsync(mTaskQueue, __func__, [self, sample]() {
jni::ByteBuffer::LocalRef bytes = jni::ByteBuffer::New(
const_cast<uint8_t*>(sample->Data()), sample->Size());
@ -562,9 +562,9 @@ RemoteDataDecoder::Decode(MediaRawData* aSample)
}
bufferInfo->Set(0, sample->Size(), sample->mTime.ToMicroseconds(), 0);
mDrainStatus = DrainStatus::DRAINABLE;
return mJavaDecoder->Input(bytes, bufferInfo, GetCryptoInfoFromSample(sample))
? mDecodePromise.Ensure(__func__)
self->mDrainStatus = DrainStatus::DRAINABLE;
return self->mJavaDecoder->Input(bytes, bufferInfo, GetCryptoInfoFromSample(sample))
? self->mDecodePromise.Ensure(__func__)
: DecodePromise::CreateAndReject(
MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__), __func__);

Просмотреть файл

@ -73,8 +73,8 @@ AppleATDecoder::Decode(MediaRawData* aSample)
(unsigned long long)aSample->Size());
RefPtr<AppleATDecoder> self = this;
RefPtr<MediaRawData> sample = aSample;
return InvokeAsync(mTaskQueue, __func__, [self, this, sample] {
return ProcessDecode(sample);
return InvokeAsync(mTaskQueue, __func__, [self, sample] {
return self->ProcessDecode(sample);
});
}
@ -121,8 +121,8 @@ RefPtr<ShutdownPromise>
AppleATDecoder::Shutdown()
{
RefPtr<AppleATDecoder> self = this;
return InvokeAsync(mTaskQueue, __func__, [self, this]() {
ProcessShutdown();
return InvokeAsync(mTaskQueue, __func__, [self]() {
self->ProcessShutdown();
return ShutdownPromise::CreateAndResolve(true, __func__);
});
}

Просмотреть файл

@ -113,8 +113,8 @@ AppleVTDecoder::Shutdown()
{
if (mTaskQueue) {
RefPtr<AppleVTDecoder> self = this;
return InvokeAsync(mTaskQueue, __func__, [self, this]() {
ProcessShutdown();
return InvokeAsync(mTaskQueue, __func__, [self]() {
self->ProcessShutdown();
return ShutdownPromise::CreateAndResolve(true, __func__);
});
}

Просмотреть файл

@ -90,8 +90,8 @@ FFmpegDataDecoder<LIBAV_VER>::Shutdown()
{
if (mTaskQueue) {
RefPtr<FFmpegDataDecoder<LIBAV_VER>> self = this;
return InvokeAsync(mTaskQueue, __func__, [self, this]() {
ProcessShutdown();
return InvokeAsync(mTaskQueue, __func__, [self]() {
self->ProcessShutdown();
return ShutdownPromise::CreateAndResolve(true, __func__);
});
}

Просмотреть файл

@ -217,8 +217,8 @@ FFmpegVideoDecoder<LIBAV_VER>::DoDecode(MediaRawData* aSample, bool* aGotFrame,
#endif
)) {
while (inputSize) {
uint8_t* data;
int size;
uint8_t* data = inputData;
int size = inputSize;
int len = mLib->av_parser_parse2(
mCodecParser, mCodecContext, &data, &size, inputData, inputSize,
aSample->mTime.ToMicroseconds(), aSample->mTimecode.ToMicroseconds(),

Просмотреть файл

@ -204,9 +204,9 @@ OmxDataDecoder::Drain()
LOG("");
RefPtr<OmxDataDecoder> self = this;
return InvokeAsync(mOmxTaskQueue, __func__, [self, this]() {
RefPtr<DecodePromise> p = mDrainPromise.Ensure(__func__);
SendEosBuffer();
return InvokeAsync(mOmxTaskQueue, __func__, [self]() {
RefPtr<DecodePromise> p = self->mDrainPromise.Ensure(__func__);
self->SendEosBuffer();
return p;
});
}

Просмотреть файл

@ -18,7 +18,7 @@ MediaDataDecoderProxy::Init()
}
RefPtr<MediaDataDecoderProxy> self = this;
return InvokeAsync(mProxyThread, __func__,
[self, this]() { return mProxyDecoder->Init(); });
[self]() { return self->mProxyDecoder->Init(); });
}
RefPtr<MediaDataDecoder::DecodePromise>
@ -31,8 +31,8 @@ MediaDataDecoderProxy::Decode(MediaRawData* aSample)
}
RefPtr<MediaDataDecoderProxy> self = this;
RefPtr<MediaRawData> sample = aSample;
return InvokeAsync(mProxyThread, __func__, [self, this, sample]() {
return mProxyDecoder->Decode(sample);
return InvokeAsync(mProxyThread, __func__, [self, sample]() {
return self->mProxyDecoder->Decode(sample);
});
}
@ -46,7 +46,7 @@ MediaDataDecoderProxy::Flush()
}
RefPtr<MediaDataDecoderProxy> self = this;
return InvokeAsync(mProxyThread, __func__,
[self, this]() { return mProxyDecoder->Flush(); });
[self]() { return self->mProxyDecoder->Flush(); });
}
RefPtr<MediaDataDecoder::DecodePromise>
@ -59,7 +59,7 @@ MediaDataDecoderProxy::Drain()
}
RefPtr<MediaDataDecoderProxy> self = this;
return InvokeAsync(mProxyThread, __func__,
[self, this]() { return mProxyDecoder->Drain(); });
[self]() { return self->mProxyDecoder->Drain(); });
}
RefPtr<ShutdownPromise>
@ -76,7 +76,7 @@ MediaDataDecoderProxy::Shutdown()
}
RefPtr<MediaDataDecoderProxy> self = this;
return InvokeAsync(mProxyThread, __func__,
[self, this]() { return mProxyDecoder->Shutdown(); });
[self]() { return self->mProxyDecoder->Shutdown(); });
}
nsCString

Просмотреть файл

@ -11,7 +11,7 @@
#include "mozilla/Assertions.h"
#include "mozilla/EndianUtils.h"
#include "mp4_demuxer/ByteReader.h"
#include "mp4_demuxer/BufferReader.h"
#include "nsAutoPtr.h"
#include "VideoUtils.h"
#include "TimeUnits.h"
@ -166,8 +166,8 @@ WAVTrackDemuxer::RIFFParserInit()
if (!riffHeader) {
return false;
}
ByteReader RIFFReader(riffHeader->Data(), 12);
mRIFFParser.Parse(RIFFReader);
BufferReader RIFFReader(riffHeader->Data(), 12);
Unused << mRIFFParser.Parse(RIFFReader);
return mRIFFParser.RiffHeader().IsValid(11);
}
@ -178,8 +178,8 @@ WAVTrackDemuxer::HeaderParserInit()
if (!header) {
return false;
}
ByteReader HeaderReader(header->Data(), 8);
mHeaderParser.Parse(HeaderReader);
BufferReader HeaderReader(header->Data(), 8);
Unused << mHeaderParser.Parse(HeaderReader);
return true;
}
@ -190,9 +190,9 @@ WAVTrackDemuxer::FmtChunkParserInit()
if (!fmtChunk) {
return false;
}
ByteReader fmtReader(fmtChunk->Data(),
mHeaderParser.GiveHeader().ChunkSize());
mFmtParser.Parse(fmtReader);
BufferReader fmtReader(fmtChunk->Data(),
mHeaderParser.GiveHeader().ChunkSize());
Unused << mFmtParser.Parse(fmtReader);
return true;
}
@ -205,8 +205,10 @@ WAVTrackDemuxer::ListChunkParserInit(uint32_t aChunkSize)
if (!infoTag) {
return false;
}
ByteReader infoTagReader(infoTag->Data(), 4);
if (!infoTagReader.CanRead32() || infoTagReader.ReadU32() != INFO_CODE) {
BufferReader infoTagReader(infoTag->Data(), 4);
auto res = infoTagReader.ReadU32();
if (res.isErr() || (res.isOk() && res.unwrap() != INFO_CODE)) {
return false;
}
@ -619,10 +621,12 @@ WAVTrackDemuxer::Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize)
// RIFFParser
uint32_t
RIFFParser::Parse(ByteReader& aReader)
Result<uint32_t, nsresult>
RIFFParser::Parse(BufferReader& aReader)
{
while (aReader.CanRead8() && !mRiffHeader.ParseNext(aReader.ReadU8())) { }
for (auto res = aReader.ReadU8();
res.isOk() && !mRiffHeader.ParseNext(res.unwrap()); res = aReader.ReadU8())
{}
if (mRiffHeader.IsValid()) {
return RIFF_CHUNK_SIZE;
@ -698,10 +702,12 @@ RIFFParser::RIFFHeader::Update(uint8_t c)
// HeaderParser
uint32_t
HeaderParser::Parse(ByteReader& aReader)
Result<uint32_t, nsresult>
HeaderParser::Parse(BufferReader& aReader)
{
while (aReader.CanRead8() && !mHeader.ParseNext(aReader.ReadU8())) { }
for (auto res = aReader.ReadU8();
res.isOk() && !mHeader.ParseNext(res.unwrap()); res = aReader.ReadU8())
{}
if (mHeader.IsValid()) {
return CHUNK_HEAD_SIZE;
@ -777,10 +783,12 @@ HeaderParser::ChunkHeader::Update(uint8_t c)
// FormatParser
uint32_t
FormatParser::Parse(ByteReader& aReader)
Result<uint32_t, nsresult>
FormatParser::Parse(BufferReader& aReader)
{
while (aReader.CanRead8() && !mFmtChunk.ParseNext(aReader.ReadU8())) { }
for (auto res = aReader.ReadU8();
res.isOk() && !mFmtChunk.ParseNext(res.unwrap()); res = aReader.ReadU8())
{}
if (mFmtChunk.IsValid()) {
return FMT_CHUNK_MIN_SIZE;

Просмотреть файл

@ -9,9 +9,9 @@
#include "MediaResource.h"
namespace mp4_demuxer {
class ByteReader;
class BufferReader;
}
using mp4_demuxer::ByteReader;
using mp4_demuxer::BufferReader;
namespace mozilla {
@ -56,7 +56,7 @@ private:
public:
const RIFFHeader& RiffHeader() const;
uint32_t Parse(ByteReader& aReader);
Result<uint32_t, nsresult> Parse(BufferReader& aReader);
void Reset();
@ -90,7 +90,7 @@ private:
public:
const ChunkHeader& GiveHeader() const;
uint32_t Parse(ByteReader& aReader);
Result<uint32_t, nsresult> Parse(BufferReader& aReader);
void Reset();
@ -126,7 +126,7 @@ private:
public:
const FormatChunk& FmtChunk() const;
uint32_t Parse(ByteReader& aReader);
Result<uint32_t, nsresult> Parse(BufferReader& aReader);
void Reset();

Просмотреть файл

@ -10777,10 +10777,12 @@ nsIFrame::GetPseudoElement(CSSPseudoElementType aType)
}
static bool
IsFrameScrolledOutOfView(nsIFrame *aFrame)
IsFrameScrolledOutOfView(nsIFrame* aTarget,
const nsRect& aTargetRect,
nsIFrame* aParent)
{
nsIScrollableFrame* scrollableFrame =
nsLayoutUtils::GetNearestScrollableFrame(aFrame,
nsLayoutUtils::GetNearestScrollableFrame(aParent,
nsLayoutUtils::SCROLLABLE_SAME_DOC |
nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
if (!scrollableFrame) {
@ -10788,11 +10790,10 @@ IsFrameScrolledOutOfView(nsIFrame *aFrame)
}
nsIFrame *scrollableParent = do_QueryFrame(scrollableFrame);
nsRect rect = aFrame->GetVisualOverflowRectRelativeToSelf();
nsRect transformedRect =
nsLayoutUtils::TransformFrameRectToAncestor(aFrame,
rect,
nsLayoutUtils::TransformFrameRectToAncestor(aTarget,
aTargetRect,
scrollableParent);
nsRect scrollableRect = scrollableParent->GetVisualOverflowRect();
@ -10814,13 +10815,14 @@ IsFrameScrolledOutOfView(nsIFrame *aFrame)
return false;
}
return IsFrameScrolledOutOfView(parent);
return IsFrameScrolledOutOfView(aTarget, aTargetRect, parent);
}
bool
nsIFrame::IsScrolledOutOfView()
{
return IsFrameScrolledOutOfView(this);
nsRect rect = GetVisualOverflowRectRelativeToSelf();
return IsFrameScrolledOutOfView(this, rect, this);
}
gfx::Matrix

Просмотреть файл

@ -727,11 +727,15 @@ RetainedDisplayListBuilder::AttemptPartialUpdate(nscolor aBackstop)
if (mPreviousCaret != mBuilder.GetCaretFrame()) {
if (mPreviousCaret) {
mBuilder.MarkFrameModifiedDuringBuilding(mPreviousCaret);
if (mBuilder.MarkFrameModifiedDuringBuilding(mPreviousCaret)) {
modifiedFrames.AppendElement(mPreviousCaret);
}
}
if (mBuilder.GetCaretFrame()) {
mBuilder.MarkFrameModifiedDuringBuilding(mBuilder.GetCaretFrame());
if (mBuilder.MarkFrameModifiedDuringBuilding(mBuilder.GetCaretFrame())) {
modifiedFrames.AppendElement(mBuilder.GetCaretFrame());
}
}
mPreviousCaret = mBuilder.GetCaretFrame();
@ -760,7 +764,7 @@ RetainedDisplayListBuilder::AttemptPartialUpdate(nscolor aBackstop)
//printf_stderr("Painting --- Modified list (dirty %d,%d,%d,%d):\n",
// modifiedDirty.x, modifiedDirty.y, modifiedDirty.width, modifiedDirty.height);
//nsFrame::PrintDisplayList(&builder, modifiedDL);
//nsFrame::PrintDisplayList(&mBuilder, modifiedDL);
} else {
// TODO: We can also skip layer building and painting if
@ -777,7 +781,7 @@ RetainedDisplayListBuilder::AttemptPartialUpdate(nscolor aBackstop)
MergeDisplayLists(&modifiedDL, &mList, &mList);
//printf_stderr("Painting --- Merged list:\n");
//nsFrame::PrintDisplayList(&builder, list);
//nsFrame::PrintDisplayList(&mBuilder, mList);
merged = true;
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше