зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to inbound a=merge on a CLOSED TREE
This commit is contained in:
Коммит
47c0bc6c06
|
@ -267,7 +267,7 @@
|
|||
accesskey="&sendPageToDevice.accesskey;"
|
||||
hidden="true">
|
||||
<menupopup id="context-sendpagetodevice-popup"
|
||||
onpopupshowing="(() => { gSync.populateSendTabToDevicesMenu(event.target, gBrowser.selectedTab); })()"/>
|
||||
onpopupshowing="(() => { gSync.populateSendTabToDevicesMenu(event.target, gBrowser.currentURI.spec, gBrowser.contentTitle, gBrowser.selectedTab.multiselected); })()"/>
|
||||
</menu>
|
||||
<menuseparator id="context-sep-viewbgimage"/>
|
||||
<menuitem id="context-viewbgimage"
|
||||
|
@ -316,7 +316,7 @@
|
|||
accesskey="&sendLinkToDevice.accesskey;"
|
||||
hidden="true">
|
||||
<menupopup id="context-sendlinktodevice-popup"
|
||||
onpopupshowing="gSync.populateSendTabToDevicesMenu(event.target, gBrowser.selectedTab);"/>
|
||||
onpopupshowing="gSync.populateSendTabToDevicesMenu(event.target, gContextMenu.linkURL, gContextMenu.linkTextStr);"/>
|
||||
</menu>
|
||||
<menuseparator id="frame-sep"/>
|
||||
<menu id="frame" label="&thisFrameMenu.label;" accesskey="&thisFrameMenu.accesskey;">
|
||||
|
|
|
@ -1036,10 +1036,14 @@ BrowserPageActions.sendToDevice = {
|
|||
onShowingSubview(panelViewNode) {
|
||||
let bodyNode = panelViewNode.querySelector(".panel-subview-body");
|
||||
let panelNode = panelViewNode.closest("panel");
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
let url = browser.currentURI.spec;
|
||||
let title = browser.contentTitle;
|
||||
let multiselected = gBrowser.selectedTab.multiselected;
|
||||
|
||||
// This is on top because it also clears the device list between state
|
||||
// changes.
|
||||
gSync.populateSendTabToDevicesMenu(bodyNode, gBrowser.selectedTab, (clientId, name, clientType, lastModified) => {
|
||||
gSync.populateSendTabToDevicesMenu(bodyNode, url, title, multiselected, (clientId, name, clientType, lastModified) => {
|
||||
if (!name) {
|
||||
return document.createXULElement("toolbarseparator");
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
<command id="cmd_printPreview" oncommand="PrintUtils.printPreview(PrintPreviewListener);"/>
|
||||
<command id="cmd_close" oncommand="BrowserCloseTabOrWindow(event);"/>
|
||||
<command id="cmd_closeWindow" oncommand="BrowserTryToCloseWindow()"/>
|
||||
<command id="cmd_toggleMute" oncommand="gBrowser.selectedTab.toggleMuteAudio()"/>
|
||||
<command id="cmd_toggleMute" oncommand="gBrowser.toggleMuteAudioOnMultiSelectedTabs(gBrowser.selectedTab)"/>
|
||||
<command id="cmd_CustomizeToolbars" oncommand="gCustomizeMode.enter()"/>
|
||||
<command id="cmd_toggleOfflineStatus" oncommand="BrowserOffline.toggleOfflineStatus();"/>
|
||||
<command id="cmd_quitApplication" oncommand="goQuitApplication()"/>
|
||||
|
|
|
@ -361,7 +361,7 @@ var gSync = {
|
|||
}
|
||||
},
|
||||
|
||||
populateSendTabToDevicesMenu(devicesPopup, aTab, createDeviceNodeFn) {
|
||||
populateSendTabToDevicesMenu(devicesPopup, url, title, multiselected, createDeviceNodeFn) {
|
||||
if (!createDeviceNodeFn) {
|
||||
createDeviceNodeFn = (clientId, name, clientType, lastModified) => {
|
||||
let eltName = name ? "menuitem" : "menuseparator";
|
||||
|
@ -386,7 +386,7 @@ var gSync = {
|
|||
|
||||
const state = UIState.get();
|
||||
if (state.status == UIState.STATUS_SIGNED_IN && this.remoteClients.length > 0) {
|
||||
this._appendSendTabDeviceList(fragment, createDeviceNodeFn, aTab);
|
||||
this._appendSendTabDeviceList(fragment, createDeviceNodeFn, url, title, multiselected);
|
||||
} else if (state.status == UIState.STATUS_SIGNED_IN) {
|
||||
this._appendSendTabSingleDevice(fragment, createDeviceNodeFn);
|
||||
} else if (state.status == UIState.STATUS_NOT_VERIFIED ||
|
||||
|
@ -402,26 +402,25 @@ var gSync = {
|
|||
// TODO: once our transition from the old-send tab world is complete,
|
||||
// this list should be built using the FxA device list instead of the client
|
||||
// collection.
|
||||
_appendSendTabDeviceList(fragment, createDeviceNodeFn, tab) {
|
||||
let tabsToSend = tab.multiselected ? gBrowser.selectedTabs : [tab];
|
||||
|
||||
function getTabUrl(t) {
|
||||
return t.linkedBrowser.currentURI.spec;
|
||||
}
|
||||
function getTabTitle(t) {
|
||||
return t.linkedBrowser.contentTitle;
|
||||
}
|
||||
_appendSendTabDeviceList(fragment, createDeviceNodeFn, url, title, multiselected) {
|
||||
let tabsToSend = multiselected ?
|
||||
gBrowser.selectedTabs.map(t => {
|
||||
return {
|
||||
url: t.linkedBrowser.currentURI.spec,
|
||||
title: t.linkedBrowser.contentTitle,
|
||||
};
|
||||
}) : [{url, title}];
|
||||
|
||||
const onSendAllCommand = (event) => {
|
||||
for (let t of tabsToSend) {
|
||||
this.sendTabToDevice(getTabUrl(t), this.remoteClients, getTabTitle(t));
|
||||
this.sendTabToDevice(t.url, this.remoteClients, t.title);
|
||||
}
|
||||
};
|
||||
const onTargetDeviceCommand = (event) => {
|
||||
const clientId = event.target.getAttribute("clientId");
|
||||
const client = this.remoteClients.find(c => c.id == clientId);
|
||||
for (let t of tabsToSend) {
|
||||
this.sendTabToDevice(getTabUrl(t), [client], getTabTitle(t));
|
||||
this.sendTabToDevice(t.url, [client], t.title);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -108,4 +108,4 @@ support-files =
|
|||
[browser_iframe_navigation.js]
|
||||
support-files =
|
||||
iframe_navigation.html
|
||||
[browser_tls_handshake_failure.js]
|
||||
[browser_navigation_failures.js]
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that the site identity indicator is properly updated for navigations
|
||||
// that fail for various reasons. In particular, we currently test TLS handshake
|
||||
// failures and about: pages that don't actually exist.
|
||||
// See bug 1492424 and bug 1493427.
|
||||
|
||||
const kSecureURI = getRootDirectory(gTestPath).replace("chrome://mochitests/content",
|
||||
"https://example.com") + "dummy_page.html";
|
||||
add_task(async function() {
|
||||
await BrowserTestUtils.withNewTab(kSecureURI, async (browser) => {
|
||||
let identityMode = window.document.getElementById("identity-box").className;
|
||||
is(identityMode, "verifiedDomain", "identity should be secure before");
|
||||
|
||||
const TLS_HANDSHAKE_FAILURE_URI = "https://ssl3.example.com/";
|
||||
// Try to connect to a server where the TLS handshake will fail.
|
||||
BrowserTestUtils.loadURI(browser, TLS_HANDSHAKE_FAILURE_URI);
|
||||
await BrowserTestUtils.browserLoaded(browser, false, TLS_HANDSHAKE_FAILURE_URI, true);
|
||||
|
||||
let newIdentityMode = window.document.getElementById("identity-box").className;
|
||||
is(newIdentityMode, "unknownIdentity", "identity should be unknown (not secure) after");
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function() {
|
||||
await BrowserTestUtils.withNewTab(kSecureURI, async (browser) => {
|
||||
let identityMode = window.document.getElementById("identity-box").className;
|
||||
is(identityMode, "verifiedDomain", "identity should be secure before");
|
||||
|
||||
const BAD_ABOUT_PAGE_URI = "about:somethingthatdoesnotexist";
|
||||
// Try to load an about: page that doesn't exist
|
||||
BrowserTestUtils.loadURI(browser, BAD_ABOUT_PAGE_URI);
|
||||
await BrowserTestUtils.browserLoaded(browser, false, BAD_ABOUT_PAGE_URI, true);
|
||||
|
||||
let newIdentityMode = window.document.getElementById("identity-box").className;
|
||||
is(newIdentityMode, "unknownIdentity", "identity should be unknown (not secure) after");
|
||||
});
|
||||
});
|
|
@ -1,25 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that the site identity indicator is properly updated for connections
|
||||
// where the TLS handshake fails.
|
||||
// See bug 1492424.
|
||||
|
||||
add_task(async function() {
|
||||
let rootURI = getRootDirectory(gTestPath).replace("chrome://mochitests/content",
|
||||
"https://example.com");
|
||||
await BrowserTestUtils.withNewTab(rootURI + "dummy_page.html", async (browser) => {
|
||||
let identityMode = window.document.getElementById("identity-box").className;
|
||||
is(identityMode, "verifiedDomain", "identity should be secure before");
|
||||
|
||||
const TLS_HANDSHAKE_FAILURE_URI = "https://ssl3.example.com/";
|
||||
// Try to connect to a server where the TLS handshake will fail.
|
||||
BrowserTestUtils.loadURI(browser, TLS_HANDSHAKE_FAILURE_URI);
|
||||
await BrowserTestUtils.browserLoaded(browser, false, TLS_HANDSHAKE_FAILURE_URI, true);
|
||||
|
||||
let newIdentityMode = window.document.getElementById("identity-box").className;
|
||||
is(newIdentityMode, "unknownIdentity", "identity should be unknown (not secure) after");
|
||||
});
|
||||
});
|
|
@ -34,6 +34,10 @@ add_task(async function test_page_contextmenu() {
|
|||
add_task(async function test_link_contextmenu() {
|
||||
const sandbox = setupSendTabMocks({ syncReady: true, clientsSynced: true, remoteClients: remoteClientsFixture,
|
||||
state: UIState.STATUS_SIGNED_IN, isSendableURI: true });
|
||||
let expectation = sandbox.mock(gSync)
|
||||
.expects("sendTabToDevice")
|
||||
.once()
|
||||
.withExactArgs("https://www.example.org/", [{id: 1, name: "Foo"}], "Click on me!!");
|
||||
|
||||
// Add a link to the page
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
|
||||
|
@ -44,17 +48,13 @@ add_task(async function test_link_contextmenu() {
|
|||
content.document.body.appendChild(a);
|
||||
});
|
||||
|
||||
await openContentContextMenu("#testingLink", "context-sendlinktodevice");
|
||||
is(document.getElementById("context-sendlinktodevice").hidden, false, "Send tab to device is shown");
|
||||
is(document.getElementById("context-sendlinktodevice").disabled, false, "Send tab to device is enabled");
|
||||
checkPopup([
|
||||
{ label: "Foo" },
|
||||
{ label: "Bar" },
|
||||
"----",
|
||||
{ label: "Send to All Devices" },
|
||||
]);
|
||||
await openContentContextMenu("#testingLink", "context-sendlinktodevice", "context-sendlinktodevice-popup");
|
||||
is(document.getElementById("context-sendlinktodevice").hidden, false, "Send link to device is shown");
|
||||
is(document.getElementById("context-sendlinktodevice").disabled, false, "Send link to device is enabled");
|
||||
document.getElementById("context-sendlinktodevice-popup").querySelector("menuitem").click();
|
||||
await hideContentContextMenu();
|
||||
|
||||
expectation.verify();
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ add_task(async function muteTabs_usingButton() {
|
|||
ok(!muted(tab3) && !activeMediaBlocked(tab3), "Tab3 is not muted and not activemedia-blocked");
|
||||
ok(!muted(tab4) && !activeMediaBlocked(tab4), "Tab4 is not muted and not activemedia-blocked");
|
||||
|
||||
// Mute tab1 which is mutliselected, thus other multiselected tabs should be affected too
|
||||
// Mute tab1 which is multiselected, thus other multiselected tabs should be affected too
|
||||
// in the following way:
|
||||
// a) muted tabs (tab0) will remain muted.
|
||||
// b) unmuted tabs (tab1, tab3) will become muted.
|
||||
|
@ -95,7 +95,6 @@ add_task(async function muteTabs_usingButton() {
|
|||
ok(muted(tab3), "Tab3 is now muted");
|
||||
ok(!muted(tab4) && !activeMediaBlocked(tab4), "Tab4 is not muted and not activemedia-blocked");
|
||||
|
||||
|
||||
for (let tab of tabs) {
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
@ -122,7 +121,7 @@ add_task(async function unmuteTabs_usingButton() {
|
|||
// Multiselecting tab0, tab1, tab2 and tab3
|
||||
await triggerClickOn(tab3, { shiftKey: true });
|
||||
|
||||
// Check mutliselection
|
||||
// Check multiselection
|
||||
for (let i = 0; i <= 3; i++) {
|
||||
ok(tabs[i].multiselected, "tab" + i + " is multiselected");
|
||||
}
|
||||
|
@ -136,7 +135,7 @@ add_task(async function unmuteTabs_usingButton() {
|
|||
ok(muted(tab4), "Tab4 is muted");
|
||||
is(gBrowser.selectedTab, tab0, "Tab0 is active");
|
||||
|
||||
// unmute tab0 which is mutliselected, thus other multiselected tabs should be affected too
|
||||
// unmute tab0 which is multiselected, thus other multiselected tabs should be affected too
|
||||
// in the following way:
|
||||
// a) muted tabs (tab3) will become unmuted.
|
||||
// b) unmuted tabs (tab0) will remain unmuted.
|
||||
|
@ -157,6 +156,58 @@ add_task(async function unmuteTabs_usingButton() {
|
|||
}
|
||||
});
|
||||
|
||||
add_task(async function muteAndUnmuteTabs_usingKeyboard() {
|
||||
let tab0 = await addMediaTab();
|
||||
let tab1 = await addMediaTab();
|
||||
let tab2 = await addMediaTab();
|
||||
let tab3 = await addMediaTab();
|
||||
let tab4 = await addMediaTab();
|
||||
|
||||
let tabs = [tab0, tab1, tab2, tab3, tab4];
|
||||
|
||||
await BrowserTestUtils.switchTab(gBrowser, tab0);
|
||||
|
||||
let mutedPromise = get_wait_for_mute_promise(tab0, true);
|
||||
EventUtils.synthesizeKey("M", {ctrlKey: true});
|
||||
await mutedPromise;
|
||||
ok(muted(tab0), "Tab0 should be muted");
|
||||
ok(!muted(tab1), "Tab1 should not be muted");
|
||||
ok(!muted(tab2), "Tab2 should not be muted");
|
||||
ok(!muted(tab3), "Tab3 should not be muted");
|
||||
ok(!muted(tab4), "Tab4 should not be muted");
|
||||
|
||||
// Multiselecting tab0, tab1, tab2 and tab3
|
||||
await triggerClickOn(tab3, { shiftKey: true });
|
||||
|
||||
// Check multiselection
|
||||
for (let i = 0; i <= 3; i++) {
|
||||
ok(tabs[i].multiselected, "tab" + i + " is multiselected");
|
||||
}
|
||||
ok(!tab4.multiselected, "tab4 is not multiselected");
|
||||
|
||||
mutedPromise = get_wait_for_mute_promise(tab0, false);
|
||||
EventUtils.synthesizeKey("M", {ctrlKey: true});
|
||||
await mutedPromise;
|
||||
ok(!muted(tab0), "Tab0 should not be muted");
|
||||
ok(!muted(tab1), "Tab1 should not be muted");
|
||||
ok(!muted(tab2), "Tab2 should not be muted");
|
||||
ok(!muted(tab3), "Tab3 should not be muted");
|
||||
ok(!muted(tab4), "Tab4 should not be muted");
|
||||
|
||||
mutedPromise = get_wait_for_mute_promise(tab0, true);
|
||||
EventUtils.synthesizeKey("M", {ctrlKey: true});
|
||||
await mutedPromise;
|
||||
ok(muted(tab0), "Tab0 should be muted");
|
||||
ok(muted(tab1), "Tab1 should be muted");
|
||||
ok(muted(tab2), "Tab2 should be muted");
|
||||
ok(muted(tab3), "Tab3 should be muted");
|
||||
ok(!muted(tab4), "Tab4 should not be muted");
|
||||
|
||||
for (let tab of tabs) {
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function playTabs_usingButton() {
|
||||
let tab0 = await addMediaTab();
|
||||
let tab1 = await addMediaTab();
|
||||
|
@ -178,7 +229,7 @@ add_task(async function playTabs_usingButton() {
|
|||
tab0.toggleMuteAudio();
|
||||
tab4.toggleMuteAudio();
|
||||
|
||||
// Check mutliselection
|
||||
// Check multiselection
|
||||
for (let i = 0; i <= 3; i++) {
|
||||
ok(tabs[i].multiselected, "tab" + i + " is multiselected");
|
||||
}
|
||||
|
@ -192,7 +243,7 @@ add_task(async function playTabs_usingButton() {
|
|||
ok(muted(tab4), "Tab4 is muted");
|
||||
is(gBrowser.selectedTab, tab0, "Tab0 is active");
|
||||
|
||||
// play tab2 which is mutliselected, thus other multiselected tabs should be affected too
|
||||
// play tab2 which is multiselected, thus other multiselected tabs should be affected too
|
||||
// in the following way:
|
||||
// a) muted tabs (tab0) will become unmuted.
|
||||
// b) unmuted tabs (tab3) will remain unmuted.
|
||||
|
@ -229,12 +280,12 @@ add_task(async function checkTabContextMenu() {
|
|||
await play(tab1, false);
|
||||
tab2.toggleMuteAudio();
|
||||
|
||||
// Mutliselect tab0, tab1, tab2.
|
||||
// multiselect tab0, tab1, tab2.
|
||||
await triggerClickOn(tab0, { ctrlKey: true });
|
||||
await triggerClickOn(tab1, { ctrlKey: true });
|
||||
await triggerClickOn(tab2, { ctrlKey: true });
|
||||
|
||||
// Check mutliselected tabs
|
||||
// Check multiselected tabs
|
||||
for (let i = 0; i <= 2; i++) {
|
||||
ok(tabs[i].multiselected, "Tab" + i + " is multi-selected");
|
||||
}
|
||||
|
|
|
@ -128,7 +128,7 @@ var paymentDialogWrapper = {
|
|||
let addressData = this.temporaryStore.addresses.get(guid) ||
|
||||
await formAutofillStorage.addresses.get(guid);
|
||||
if (!addressData) {
|
||||
throw new Error(`Shipping address not found: ${guid}`);
|
||||
throw new Error(`Address not found: ${guid}`);
|
||||
}
|
||||
|
||||
let address = this.createPaymentAddress({
|
||||
|
|
|
@ -11,6 +11,7 @@ import ObservedPropertiesMixin from "../mixins/ObservedPropertiesMixin.js";
|
|||
export default class LabelledCheckbox extends ObservedPropertiesMixin(HTMLElement) {
|
||||
static get observedAttributes() {
|
||||
return [
|
||||
"infoTooltip",
|
||||
"form",
|
||||
"label",
|
||||
"value",
|
||||
|
@ -21,6 +22,10 @@ export default class LabelledCheckbox extends ObservedPropertiesMixin(HTMLElemen
|
|||
|
||||
this._label = document.createElement("label");
|
||||
this._labelSpan = document.createElement("span");
|
||||
this._infoTooltip = document.createElement("span");
|
||||
this._infoTooltip.className = "info-tooltip";
|
||||
this._infoTooltip.setAttribute("tabindex", "0");
|
||||
this._infoTooltip.setAttribute("role", "tooltip");
|
||||
this._checkbox = document.createElement("input");
|
||||
this._checkbox.type = "checkbox";
|
||||
}
|
||||
|
@ -29,11 +34,13 @@ export default class LabelledCheckbox extends ObservedPropertiesMixin(HTMLElemen
|
|||
this.appendChild(this._label);
|
||||
this._label.appendChild(this._checkbox);
|
||||
this._label.appendChild(this._labelSpan);
|
||||
this._label.appendChild(this._infoTooltip);
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
this._labelSpan.textContent = this.label;
|
||||
this._infoTooltip.setAttribute("aria-label", this.infoTooltip);
|
||||
// We don't use the ObservedPropertiesMixin behaviour because we want to be able to mirror
|
||||
// form="" but ObservedPropertiesMixin removes attributes when "".
|
||||
if (this.hasAttribute("form")) {
|
||||
|
|
|
@ -139,6 +139,7 @@ export default class AddressForm extends PaymentStateSubscriberMixin(PaymentRequ
|
|||
this.dataset.addButtonLabel;
|
||||
}
|
||||
this.persistCheckbox.label = this.dataset.persistCheckboxLabel;
|
||||
this.persistCheckbox.infoTooltip = this.dataset.persistCheckboxInfoTooltip;
|
||||
|
||||
this.backButton.hidden = page.onboardingWizard;
|
||||
this.cancelButton.hidden = !page.onboardingWizard;
|
||||
|
@ -312,6 +313,7 @@ export default class AddressForm extends PaymentStateSubscriberMixin(PaymentRequ
|
|||
if (page.onboardingWizard && !Object.keys(savedBasicCards).length) {
|
||||
successStateChange = {
|
||||
"basic-card-page": {
|
||||
selectedStateKey: "selectedPaymentCard",
|
||||
// Preserve field values as the user may have already edited the card
|
||||
// page and went back to the address page to make a correction.
|
||||
preserveFieldValues: true,
|
||||
|
|
|
@ -150,6 +150,7 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
|
|||
this.dataset.addButtonLabel;
|
||||
}
|
||||
this.persistCheckbox.label = this.dataset.persistCheckboxLabel;
|
||||
this.persistCheckbox.infoTooltip = this.dataset.persistCheckboxInfoTooltip;
|
||||
this.addressAddLink.textContent = this.dataset.addressAddLinkLabel;
|
||||
this.addressEditLink.textContent = this.dataset.addressEditLinkLabel;
|
||||
this.acceptedCardsList.label = this.dataset.acceptedCardsLabel;
|
||||
|
@ -168,6 +169,10 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
|
|||
|
||||
this.form.querySelector("#cc-number").disabled = editing;
|
||||
|
||||
// The CVV fields should be hidden and disabled when editing.
|
||||
this.form.querySelector("#cc-csc-container").hidden = editing;
|
||||
this.form.querySelector("#cc-csc").disabled = editing;
|
||||
|
||||
// If a card is selected we want to edit it.
|
||||
if (editing) {
|
||||
this.pageTitleHeading.textContent = this.dataset.editBasicCardTitle;
|
||||
|
@ -283,6 +288,7 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
|
|||
preserveFieldValues: true,
|
||||
guid: basicCardPage.guid,
|
||||
persistCheckboxValue: this.persistCheckbox.checked,
|
||||
selectedStateKey: basicCardPage.selectedStateKey,
|
||||
},
|
||||
};
|
||||
let billingAddressGUID = this.form.querySelector("#billingAddressGUID");
|
||||
|
@ -399,7 +405,7 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
|
|||
record.isTemporary = true;
|
||||
}
|
||||
|
||||
for (let editableFieldName of ["cc-name", "cc-exp-month", "cc-exp-year"]) {
|
||||
for (let editableFieldName of ["cc-name", "cc-exp-month", "cc-exp-year", "cc-type"]) {
|
||||
record[editableFieldName] = record[editableFieldName] || "";
|
||||
}
|
||||
|
||||
|
@ -409,14 +415,23 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
|
|||
record["cc-number"] = record["cc-number"] || "";
|
||||
}
|
||||
|
||||
// Never save the CSC in storage. Storage will throw and not save the record
|
||||
// if it is passed.
|
||||
delete record["cc-csc"];
|
||||
|
||||
try {
|
||||
let {guid} = await paymentRequest.updateAutofillRecord("creditCards", record,
|
||||
basicCardPage.guid);
|
||||
let {selectedStateKey} = currentState["basic-card-page"];
|
||||
if (!selectedStateKey) {
|
||||
throw new Error(`state["basic-card-page"].selectedStateKey is required`);
|
||||
}
|
||||
this.requestStore.setState({
|
||||
page: {
|
||||
id: "payment-summary",
|
||||
},
|
||||
selectedPaymentCard: guid,
|
||||
[selectedStateKey]: guid,
|
||||
[selectedStateKey + "SecurityCode"]: this.form.querySelector("#cc-csc").value,
|
||||
});
|
||||
} catch (ex) {
|
||||
log.warn("saveRecord: error:", ex);
|
||||
|
|
|
@ -259,6 +259,7 @@ export default class PaymentDialog extends PaymentStateSubscriberMixin(HTMLEleme
|
|||
(this._isPayerRequested(state.request.paymentOptions) &&
|
||||
(!this._payerAddressPicker.selectedOption ||
|
||||
this._payerAddressPicker.classList.contains(INVALID_CLASS_NAME))) ||
|
||||
!this._paymentMethodPicker.securityCodeInput.validity.valid ||
|
||||
!this._paymentMethodPicker.selectedOption ||
|
||||
this._paymentMethodPicker.classList.contains(INVALID_CLASS_NAME) ||
|
||||
state.changesPrevented;
|
||||
|
|
|
@ -25,6 +25,7 @@ export default class PaymentMethodPicker extends RichPicker {
|
|||
this.securityCodeInput.pattern = "[0-9]{3,}";
|
||||
this.securityCodeInput.classList.add("security-code");
|
||||
this.securityCodeInput.addEventListener("change", this);
|
||||
this.securityCodeInput.addEventListener("input", this);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
|
@ -74,6 +75,11 @@ export default class PaymentMethodPicker extends RichPicker {
|
|||
`does not exist in the payment method picker`);
|
||||
}
|
||||
|
||||
let securityCodeState = state[this.selectedStateKey + "SecurityCode"];
|
||||
if (securityCodeState && securityCodeState != this.securityCodeInput.value) {
|
||||
this.securityCodeInput.defaultValue = securityCodeState;
|
||||
}
|
||||
|
||||
super.render(state);
|
||||
}
|
||||
|
||||
|
@ -88,7 +94,7 @@ export default class PaymentMethodPicker extends RichPicker {
|
|||
}
|
||||
|
||||
let acceptedNetworks = paymentRequest.getAcceptedNetworks(state.request);
|
||||
let selectedCard = state.savedBasicCards[selectedOption.value];
|
||||
let selectedCard = paymentRequest.getBasicCards(state)[selectedOption.value];
|
||||
let isSupported = selectedCard["cc-type"] &&
|
||||
acceptedNetworks.includes(selectedCard["cc-type"]);
|
||||
return isSupported;
|
||||
|
@ -100,8 +106,9 @@ export default class PaymentMethodPicker extends RichPicker {
|
|||
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case "input":
|
||||
case "change": {
|
||||
this.onChange(event);
|
||||
this.onInputOrChange(event);
|
||||
break;
|
||||
}
|
||||
case "click": {
|
||||
|
@ -111,7 +118,7 @@ export default class PaymentMethodPicker extends RichPicker {
|
|||
}
|
||||
}
|
||||
|
||||
onChange({target}) {
|
||||
onInputOrChange({target}) {
|
||||
let selectedKey = this.selectedStateKey;
|
||||
let stateChange = {};
|
||||
|
||||
|
@ -141,7 +148,9 @@ export default class PaymentMethodPicker extends RichPicker {
|
|||
page: {
|
||||
id: "basic-card-page",
|
||||
},
|
||||
"basic-card-page": {},
|
||||
"basic-card-page": {
|
||||
selectedStateKey: this.selectedStateKey,
|
||||
},
|
||||
};
|
||||
|
||||
switch (target) {
|
||||
|
|
|
@ -17,6 +17,7 @@ export let requestStore = new PaymentsStore({
|
|||
"basic-card-page": {
|
||||
guid: null,
|
||||
// preserveFieldValues: true,
|
||||
selectedStateKey: null,
|
||||
},
|
||||
"address-page": {
|
||||
guid: null,
|
||||
|
|
|
@ -172,6 +172,47 @@ payment-dialog[changes-prevented][complete-status="success"] #pay {
|
|||
left: 0;
|
||||
}
|
||||
|
||||
.persist-checkbox {
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.persist-checkbox > label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.info-tooltip {
|
||||
display: inline-block;
|
||||
background-image: url(chrome://global/skin/icons/help.svg);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: 2px 4px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.info-tooltip:focus::after,
|
||||
.info-tooltip:hover::after {
|
||||
content: attr(aria-label);
|
||||
display: block;
|
||||
position: absolute;
|
||||
padding: 2px 5px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #bebebf;
|
||||
box-shadow: 1px 1px 3px #bebebf;
|
||||
font-size: smaller;
|
||||
min-width: 188px;
|
||||
left: -86px;
|
||||
bottom: 20px;
|
||||
}
|
||||
|
||||
.info-tooltip:dir(rtl):focus::after,
|
||||
.info-tooltip:dir(rtl):hover::after {
|
||||
left: auto;
|
||||
right: -86px;
|
||||
}
|
||||
|
||||
.branding {
|
||||
background-image: url(chrome://branding/content/icon32.png);
|
||||
background-size: 16px;
|
||||
|
@ -182,6 +223,6 @@ payment-dialog[changes-prevented][complete-status="success"] #pay {
|
|||
margin-inline-end: auto;
|
||||
}
|
||||
|
||||
body[dir="rtl"] .branding {
|
||||
.branding:dir(rtl) {
|
||||
background-position: right center;
|
||||
}
|
||||
|
|
|
@ -60,7 +60,8 @@
|
|||
<!ENTITY basicCardPage.addButton.label "Add">
|
||||
<!ENTITY basicCardPage.nextButton.label "Next">
|
||||
<!ENTITY basicCardPage.updateButton.label "Update">
|
||||
<!ENTITY basicCardPage.persistCheckbox.label "Save credit card to &brandShortName; (Security code will not be saved)">
|
||||
<!ENTITY basicCardPage.persistCheckbox.label "Save credit card to &brandShortName; (CVV will not be saved)">
|
||||
<!ENTITY basicCardPage.persistCheckbox.infoTooltip "&brandShortName; can securely store your credit card information to use in forms like this, so you don’t have to enter it every time.">
|
||||
<!ENTITY addressPage.error.genericSave "There was an error saving the address.">
|
||||
<!ENTITY addressPage.cancelButton.label "Cancel">
|
||||
<!ENTITY addressPage.backButton.label "Back">
|
||||
|
@ -68,6 +69,7 @@
|
|||
<!ENTITY addressPage.nextButton.label "Next">
|
||||
<!ENTITY addressPage.updateButton.label "Update">
|
||||
<!ENTITY addressPage.persistCheckbox.label "Save address to &brandShortName;">
|
||||
<!ENTITY addressPage.persistCheckbox.infoTooltip "&brandShortName; can add your address to forms like this, so you don’t have to type it every time.">
|
||||
<!ENTITY failErrorPage.title "We couldn’t complete your payment to **host-name**">
|
||||
<!ENTITY failErrorPage.suggestionHeading "The most likely cause is a hiccup with your credit card.">
|
||||
<!ENTITY failErrorPage.suggestion1 "Make sure the card you’re using hasn’t expired">
|
||||
|
@ -199,6 +201,7 @@
|
|||
data-update-button-label="&basicCardPage.updateButton.label;"
|
||||
data-cancel-button-label="&cancelPaymentButton.label;"
|
||||
data-persist-checkbox-label="&basicCardPage.persistCheckbox.label;"
|
||||
data-persist-checkbox-info-tooltip="&basicCardPage.persistCheckbox.infoTooltip;"
|
||||
data-accepted-cards-label="&acceptedCards.label;"
|
||||
data-field-required-symbol="&fieldRequiredSymbol;"
|
||||
hidden="hidden"></basic-card-form>
|
||||
|
@ -211,6 +214,7 @@
|
|||
data-next-button-label="&addressPage.nextButton.label;"
|
||||
data-update-button-label="&addressPage.updateButton.label;"
|
||||
data-persist-checkbox-label="&addressPage.persistCheckbox.label;"
|
||||
data-persist-checkbox-info-tooltip="&addressPage.persistCheckbox.infoTooltip;"
|
||||
data-field-required-symbol="&fieldRequiredSymbol;"
|
||||
hidden="hidden"></address-form>
|
||||
|
||||
|
|
|
@ -252,7 +252,9 @@ var PaymentTestUtils = {
|
|||
* @returns {undefined}
|
||||
*/
|
||||
completePayment: () => {
|
||||
content.document.getElementById("pay").click();
|
||||
let button = content.document.getElementById("pay");
|
||||
ok(!button.disabled, "Pay button should not be disabled when clicking it");
|
||||
button.click();
|
||||
},
|
||||
|
||||
setSecurityCode: ({securityCode}) => {
|
||||
|
|
|
@ -564,6 +564,10 @@ add_task(async function test_private_persist_addresses() {
|
|||
tempAddress.name.includes(address["family-name"]), "Address.name was computed");
|
||||
}, {address: addressToAdd, tempAddressGuid});
|
||||
|
||||
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.setSecurityCode, {
|
||||
securityCode: "123",
|
||||
});
|
||||
|
||||
info("clicking pay");
|
||||
spawnPaymentDialogTask(frame, PTU.DialogContentTasks.completePayment);
|
||||
|
||||
|
|
|
@ -61,7 +61,10 @@ async function add_link(aOptions = {}) {
|
|||
if (aOptions.hasOwnProperty("setCardPersistCheckedValue")) {
|
||||
cardOptions.setPersistCheckedValue = aOptions.setCardPersistCheckedValue;
|
||||
}
|
||||
await fillInCardForm(frame, PTU.BasicCards.JaneMasterCard, cardOptions);
|
||||
await fillInCardForm(frame, {
|
||||
["cc-csc"]: 123,
|
||||
...PTU.BasicCards.JaneMasterCard,
|
||||
}, cardOptions);
|
||||
|
||||
await verifyCardNetwork(frame, cardOptions);
|
||||
await verifyPersistCheckbox(frame, cardOptions);
|
||||
|
@ -650,7 +653,10 @@ add_task(async function test_private_card_adding() {
|
|||
"Check card page state");
|
||||
});
|
||||
|
||||
await fillInCardForm(frame, PTU.BasicCards.JohnDoe);
|
||||
await fillInCardForm(frame, {
|
||||
["cc-csc"]: "999",
|
||||
...PTU.BasicCards.JohnDoe,
|
||||
});
|
||||
|
||||
await spawnPaymentDialogTask(frame, async function() {
|
||||
let {
|
||||
|
|
|
@ -103,6 +103,10 @@ add_task(async function test_change_shipping() {
|
|||
btn.click();
|
||||
});
|
||||
|
||||
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.setSecurityCode, {
|
||||
securityCode: "123",
|
||||
});
|
||||
|
||||
info("clicking pay");
|
||||
spawnPaymentDialogTask(frame, PTU.DialogContentTasks.completePayment);
|
||||
|
||||
|
@ -263,6 +267,10 @@ add_task(async function test_no_shippingchange_without_shipping() {
|
|||
}, PTU.ContentTasks.ensureNoPaymentRequestEvent);
|
||||
info("added shipping change handler");
|
||||
|
||||
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.setSecurityCode, {
|
||||
securityCode: "456",
|
||||
});
|
||||
|
||||
info("clicking pay");
|
||||
spawnPaymentDialogTask(frame, PTU.DialogContentTasks.completePayment);
|
||||
|
||||
|
|
|
@ -28,6 +28,10 @@ add_task(async function test_complete_success() {
|
|||
}
|
||||
);
|
||||
|
||||
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.setSecurityCode, {
|
||||
securityCode: "123",
|
||||
});
|
||||
|
||||
spawnPaymentDialogTask(frame, PTU.DialogContentTasks.completePayment);
|
||||
|
||||
// Add a handler to complete the payment above.
|
||||
|
@ -56,6 +60,10 @@ add_task(async function test_complete_fail() {
|
|||
}
|
||||
);
|
||||
|
||||
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.setSecurityCode, {
|
||||
securityCode: "456",
|
||||
});
|
||||
|
||||
info("clicking pay");
|
||||
spawnPaymentDialogTask(frame, PTU.DialogContentTasks.completePayment);
|
||||
|
||||
|
@ -89,6 +97,10 @@ add_task(async function test_complete_timeout() {
|
|||
}
|
||||
);
|
||||
|
||||
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.setSecurityCode, {
|
||||
securityCode: "789",
|
||||
});
|
||||
|
||||
info("clicking pay");
|
||||
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.completePayment);
|
||||
|
||||
|
|
|
@ -91,7 +91,10 @@ add_task(async function test_onboarding_wizard_without_saved_addresses_and_saved
|
|||
}, "Shipping address is selected as the billing address");
|
||||
});
|
||||
|
||||
await fillInCardForm(frame, PTU.BasicCards.JohnDoe);
|
||||
await fillInCardForm(frame, {
|
||||
["cc-csc"]: "123",
|
||||
...PTU.BasicCards.JohnDoe,
|
||||
});
|
||||
|
||||
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.clickPrimaryButton);
|
||||
|
||||
|
@ -362,7 +365,10 @@ add_task(async function test_onboarding_wizard_with_requestShipping_turned_off()
|
|||
}, "Billing Address is correctly shown");
|
||||
});
|
||||
|
||||
await fillInCardForm(frame, PTU.BasicCards.JohnDoe);
|
||||
await fillInCardForm(frame, {
|
||||
["cc-csc"]: "123",
|
||||
...PTU.BasicCards.JohnDoe,
|
||||
});
|
||||
|
||||
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.clickPrimaryButton);
|
||||
|
||||
|
|
|
@ -125,6 +125,11 @@ add_task(async function test_show_completePayment2() {
|
|||
info("select the shipping address");
|
||||
await selectPaymentDialogShippingAddressByCountry(frame, "US");
|
||||
|
||||
info("entering CSC");
|
||||
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.setSecurityCode, {
|
||||
securityCode: "123",
|
||||
});
|
||||
|
||||
info("clicking pay");
|
||||
spawnPaymentDialogTask(frame, PTU.DialogContentTasks.completePayment);
|
||||
|
||||
|
@ -234,6 +239,11 @@ add_task(async function test_supportedNetworks() {
|
|||
}
|
||||
);
|
||||
|
||||
info("entering CSC");
|
||||
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.setSecurityCode, {
|
||||
securityCode: "789",
|
||||
});
|
||||
|
||||
await spawnPaymentDialogTask(frame, () => {
|
||||
let acceptedCards = content.document.querySelector("accepted-cards");
|
||||
ok(acceptedCards && !content.isHidden(acceptedCards),
|
||||
|
|
|
@ -138,6 +138,7 @@ add_task(async function test_saveButton() {
|
|||
let year = (new Date()).getFullYear().toString();
|
||||
fillField(form.form.querySelector("#cc-exp-year"), year);
|
||||
fillField(form.form.querySelector("#cc-type"), "visa");
|
||||
fillField(form.form.querySelector("#cc-csc"), "123");
|
||||
isnot(form.form.querySelector("#billingAddressGUID").value, address2.guid,
|
||||
"Check initial billing address");
|
||||
fillField(form.form.querySelector("#billingAddressGUID"), address2.guid);
|
||||
|
@ -203,7 +204,7 @@ add_task(async function test_requiredAttributePropagated() {
|
|||
await asyncElementRendered();
|
||||
|
||||
let requiredElements = [...form.form.elements].filter(e => e.required && !e.disabled);
|
||||
is(requiredElements.length, 6, "Number of required elements");
|
||||
is(requiredElements.length, 7, "Number of required elements");
|
||||
for (let element of requiredElements) {
|
||||
if (element.id == "billingAddressGUID") {
|
||||
// The billing address has a different layout.
|
||||
|
@ -445,6 +446,7 @@ add_task(async function test_field_validity_updates() {
|
|||
let ccNumber = form.form.querySelector("#cc-number");
|
||||
let nameInput = form.form.querySelector("#cc-name");
|
||||
let typeInput = form.form.querySelector("#cc-type");
|
||||
let cscInput = form.form.querySelector("#cc-csc");
|
||||
let monthInput = form.form.querySelector("#cc-exp-month");
|
||||
let yearInput = form.form.querySelector("#cc-exp-year");
|
||||
|
||||
|
@ -460,6 +462,7 @@ add_task(async function test_field_validity_updates() {
|
|||
let year = (new Date()).getFullYear().toString();
|
||||
fillField(yearInput, year);
|
||||
fillField(typeInput, "visa");
|
||||
fillField(cscInput, "456");
|
||||
ok(ccNumber.checkValidity(), "cc-number field is valid with good input");
|
||||
ok(nameInput.checkValidity(), "cc-name field is valid with a value");
|
||||
ok(monthInput.checkValidity(), "cc-exp-month field is valid with a value");
|
||||
|
|
|
@ -115,6 +115,11 @@ async function setup({shippingRequired, payerRequired}) {
|
|||
state.selectedShippingAddress = null;
|
||||
state.selectedShippingOption = null;
|
||||
await el1.requestStore.setState(state);
|
||||
|
||||
// Fill the security code input so it doesn't interfere with checking the pay
|
||||
// button state for dropdown changes.
|
||||
el1._paymentMethodPicker.securityCodeInput.select();
|
||||
sendString("123");
|
||||
await asyncElementRendered();
|
||||
}
|
||||
|
||||
|
@ -216,6 +221,34 @@ add_task(async function runTests() {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function test_securityCodeRequired() {
|
||||
await setup({
|
||||
payerRequired: false,
|
||||
shippingRequired: false,
|
||||
});
|
||||
|
||||
let picker = el1._paymentMethodPicker;
|
||||
let payButton = document.getElementById("pay");
|
||||
|
||||
let stateChangedPromise = promiseStateChange(el1.requestStore);
|
||||
selectFirstItemOfPicker(picker);
|
||||
await stateChangedPromise;
|
||||
|
||||
picker.securityCodeInput.select();
|
||||
stateChangedPromise = promiseStateChange(el1.requestStore);
|
||||
synthesizeKey("VK_DELETE");
|
||||
await stateChangedPromise;
|
||||
|
||||
ok(payButton.disabled, "Button is disabled when CVV is empty");
|
||||
|
||||
picker.securityCodeInput.select();
|
||||
stateChangedPromise = promiseStateChange(el1.requestStore);
|
||||
sendString("123");
|
||||
await stateChangedPromise;
|
||||
|
||||
ok(!payButton.disabled, "Button is enabled when CVV is filled");
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -210,6 +210,65 @@ add_task(async function test_delete() {
|
|||
ok(options[0].textContent.includes("J Smith"), "Check remaining card #1");
|
||||
ok(options[1].textContent.includes("Jane Fields"), "Check remaining card #2");
|
||||
});
|
||||
|
||||
add_task(async function test_supportedNetworks_tempCards() {
|
||||
await picker1.requestStore.reset();
|
||||
|
||||
let request = Object.assign({}, picker1.requestStore.getState().request);
|
||||
request.paymentMethods = [
|
||||
{
|
||||
supportedMethods: "basic-card",
|
||||
data: {
|
||||
supportedNetworks: [
|
||||
"mastercard",
|
||||
"visa",
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
await picker1.requestStore.setState({
|
||||
request,
|
||||
selectedPaymentCard: "68gjdh354j",
|
||||
tempBasicCards: {
|
||||
"68gjdh354j": {
|
||||
"cc-exp": "2017-08",
|
||||
"cc-exp-month": 8,
|
||||
"cc-exp-year": 2017,
|
||||
"cc-name": "J Smith",
|
||||
"cc-number": "***********1234",
|
||||
"cc-type": "discover",
|
||||
"guid": "68gjdh354j",
|
||||
},
|
||||
},
|
||||
});
|
||||
await asyncElementRendered();
|
||||
let options = picker1.dropdown.popupBox.children;
|
||||
is(options.length, 1, "Check dropdown has one card");
|
||||
ok(options[0].textContent.includes("J Smith"), "Check remaining card #1");
|
||||
|
||||
ok(picker1.classList.contains("invalid-selected-option"),
|
||||
"Check discover is recognized as not supported");
|
||||
|
||||
info("change the card to be a visa");
|
||||
await picker1.requestStore.setState({
|
||||
tempBasicCards: {
|
||||
"68gjdh354j": {
|
||||
"cc-exp": "2017-08",
|
||||
"cc-exp-month": 8,
|
||||
"cc-exp-year": 2017,
|
||||
"cc-name": "J Smith",
|
||||
"cc-number": "***********1234",
|
||||
"cc-type": "visa",
|
||||
"guid": "68gjdh354j",
|
||||
},
|
||||
},
|
||||
});
|
||||
await asyncElementRendered();
|
||||
|
||||
ok(!picker1.classList.contains("invalid-selected-option"),
|
||||
"Check visa is recognized as supported");
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -14,4 +14,5 @@ EXTRA_JS_MODULES += [
|
|||
'UrlbarView.jsm',
|
||||
]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
|
||||
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
"extends": [
|
||||
"plugin:mozilla/browser-test"
|
||||
]
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
# 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/.
|
||||
|
||||
[DEFAULT]
|
||||
|
||||
[browser_UrlbarInput_unit.js]
|
||||
support-files = empty.xul
|
|
@ -14,7 +14,14 @@ let generalListener;
|
|||
let input;
|
||||
let inputOptions;
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
ChromeUtils.import("resource:///modules/UrlbarController.jsm", this);
|
||||
|
||||
/* global sinon */
|
||||
Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
delete window.sinon;
|
||||
});
|
||||
|
||||
/**
|
||||
* Asserts that the query context has the expected values.
|
||||
|
@ -32,15 +39,6 @@ function assertContextMatches(context, expectedValues) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {object} A fake element with minimal functions for simulating textbox etc.
|
||||
*/
|
||||
function createFakeElement() {
|
||||
return {
|
||||
addEventListener() {},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the result of a handleQuery call on the controller.
|
||||
*
|
||||
|
@ -68,7 +66,7 @@ function checkHandleQueryCall(stub, expectedQueryContextProps) {
|
|||
}
|
||||
}
|
||||
|
||||
add_task(function setup() {
|
||||
add_task(async function setup() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
fakeController = new UrlbarController();
|
||||
|
@ -76,17 +74,30 @@ add_task(function setup() {
|
|||
sandbox.stub(fakeController, "handleQuery");
|
||||
sandbox.stub(PrivateBrowsingUtils, "isWindowPrivate").returns(false);
|
||||
|
||||
let textbox = createFakeElement();
|
||||
textbox.inputField = createFakeElement();
|
||||
textbox.inputField.controllers = { insertControllerAt() {} };
|
||||
// Open a new window, so we don't affect other tests by adding extra
|
||||
// UrbarInput wrappers around the urlbar.
|
||||
let gTestRoot = getRootDirectory(gTestPath);
|
||||
|
||||
let win = window.openDialog(gTestRoot + "empty.xul",
|
||||
"", "chrome");
|
||||
await BrowserTestUtils.waitForEvent(win, "load");
|
||||
|
||||
registerCleanupFunction(async () => {
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
// Clone the elements into the new window, so we get exact copies without having
|
||||
// to replicate the xul.
|
||||
let doc = win.document;
|
||||
let textbox = doc.importNode(document.getElementById("urlbar"), true);
|
||||
doc.documentElement.appendChild(textbox);
|
||||
let panel = doc.importNode(document.getElementById("urlbar-results"), true);
|
||||
doc.documentElement.appendChild(panel);
|
||||
|
||||
inputOptions = {
|
||||
textbox,
|
||||
panel: {
|
||||
ownerDocument: {},
|
||||
querySelector() {
|
||||
return createFakeElement();
|
||||
},
|
||||
},
|
||||
panel,
|
||||
controller: fakeController,
|
||||
};
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0"?>
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/>
|
|
@ -6,4 +6,3 @@ firefox-appdir = browser
|
|||
[test_tokenizer.js]
|
||||
[test_UrlbarController_unit.js]
|
||||
[test_UrlbarController_integration.js]
|
||||
[test_UrlbarInput_unit.js]
|
||||
|
|
|
@ -60,6 +60,17 @@
|
|||
</select>
|
||||
<span data-localization="cardNetwork" class="label-text"/>
|
||||
</label>
|
||||
<label id="cc-csc-container" class="container" hidden="hidden">
|
||||
<!-- Keep these attributes in-sync with securityCodeInput in payment-method-picker.js -->
|
||||
<input id="cc-csc"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
size="3"
|
||||
required="required"
|
||||
pattern="[0-9]{3,}"
|
||||
disabled="disabled"/>
|
||||
<span data-localization="cardCVV" class="label-text"/>
|
||||
</label>
|
||||
<div id="billingAddressGUID-container" class="billingAddressRow container rich-picker">
|
||||
<select id="billingAddressGUID" required="required">
|
||||
</select>
|
||||
|
|
|
@ -185,6 +185,8 @@ cardExpiresMonth = Exp. Month
|
|||
cardExpiresYear = Exp. Year
|
||||
billingAddress = Billing Address
|
||||
cardNetwork = Card Type
|
||||
# LOCALIZATION NOTE (cardCVV): Credit card security code https://en.wikipedia.org/wiki/Card_security_code
|
||||
cardCVV = CVV
|
||||
|
||||
# LOCALIZATION NOTE: (cardNetwork.*): These are brand names and should only be translated when a locale-specific name for that brand is in common use
|
||||
cardNetwork.amex = American Express
|
||||
|
|
|
@ -44,6 +44,10 @@
|
|||
grid-area: cc-type;
|
||||
}
|
||||
|
||||
#cc-csc-container {
|
||||
grid-area: cc-csc;
|
||||
}
|
||||
|
||||
#billingAddressGUID-container {
|
||||
grid-area: billingAddressGUID;
|
||||
}
|
||||
|
|
|
@ -70,12 +70,18 @@ class AboutDebuggingApp extends Component {
|
|||
componentDidMount() {
|
||||
window.addEventListener("hashchange", this.onHashChange);
|
||||
this.onHashChange();
|
||||
this.props.telemetry.toolOpened("aboutdebugging");
|
||||
|
||||
// aboutdebugging is not connected with a toolbox so we pass -1 as the
|
||||
// toolbox session id.
|
||||
this.props.telemetry.toolOpened("aboutdebugging", -1, this);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener("hashchange", this.onHashChange);
|
||||
this.props.telemetry.toolClosed("aboutdebugging");
|
||||
|
||||
// aboutdebugging is not connected with a toolbox so we pass -1 as the
|
||||
// toolbox session id.
|
||||
this.props.telemetry.toolClosed("aboutdebugging", -1, this);
|
||||
}
|
||||
|
||||
onHashChange() {
|
||||
|
|
|
@ -59,11 +59,6 @@ AccessibilityPanel.prototype = {
|
|||
resolver = resolve;
|
||||
});
|
||||
|
||||
// Local monitoring needs to make the target remote.
|
||||
if (!this.target.isRemote) {
|
||||
await this.target.attach();
|
||||
}
|
||||
|
||||
this._telemetry = new Telemetry();
|
||||
this.panelWin.gTelemetry = this._telemetry;
|
||||
|
||||
|
@ -147,9 +142,9 @@ AccessibilityPanel.prototype = {
|
|||
|
||||
updateA11YServiceDurationTimer() {
|
||||
if (this.front.enabled) {
|
||||
this._telemetry.start(A11Y_SERVICE_DURATION, this, true);
|
||||
this._telemetry.start(A11Y_SERVICE_DURATION, this);
|
||||
} else {
|
||||
this._telemetry.finish(A11Y_SERVICE_DURATION, this, true);
|
||||
this._telemetry.finish(A11Y_SERVICE_DURATION, this);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -141,7 +141,8 @@ class Picker {
|
|||
|
||||
await this.walker.cancelPick();
|
||||
|
||||
this._telemetry.toolClosed("accessibility_picker");
|
||||
this._telemetry.toolClosed(
|
||||
"accessibility_picker", this.toolbox.sessionId, this);
|
||||
|
||||
this.walker.off("picker-accessible-hovered", this.onPickerAccessibleHovered);
|
||||
this.walker.off("picker-accessible-picked", this.onPickerAccessiblePicked);
|
||||
|
@ -172,7 +173,8 @@ class Picker {
|
|||
|
||||
await this.walker.pick(doFocus);
|
||||
|
||||
this._telemetry.toolOpened("accessibility_picker");
|
||||
this._telemetry.toolOpened(
|
||||
"accessibility_picker", this.toolbox.sessionId, this);
|
||||
|
||||
this.emit("picker-started");
|
||||
}
|
||||
|
|
|
@ -23,10 +23,6 @@ class ApplicationPanel {
|
|||
}
|
||||
|
||||
async open() {
|
||||
if (!this.toolbox.target.isRemote) {
|
||||
await this.toolbox.target.attach();
|
||||
}
|
||||
|
||||
await this.panelWin.Application.bootstrap({
|
||||
toolbox: this.toolbox,
|
||||
panel: this,
|
||||
|
|
|
@ -48,7 +48,6 @@ function navigate(target, url, waitForTargetEvent = "navigate") {
|
|||
async function openNewTabAndApplicationPanel(url) {
|
||||
const tab = await addTab(url);
|
||||
const target = await TargetFactory.forTab(tab);
|
||||
await target.attach();
|
||||
|
||||
const toolbox = await gDevTools.showToolbox(target, "application");
|
||||
const panel = toolbox.getCurrentPanel();
|
||||
|
|
|
@ -27,31 +27,16 @@ CanvasDebuggerPanel.prototype = {
|
|||
* @return object
|
||||
* A promise that is resolved when the Canvas Debugger completes opening.
|
||||
*/
|
||||
open: function() {
|
||||
let targetPromise;
|
||||
|
||||
// Local debugging needs to make the target remote.
|
||||
if (!this.target.isRemote) {
|
||||
targetPromise = this.target.attach();
|
||||
} else {
|
||||
targetPromise = Promise.resolve(this.target);
|
||||
}
|
||||
|
||||
return targetPromise
|
||||
.then(() => {
|
||||
open: async function() {
|
||||
this.panelWin.gToolbox = this._toolbox;
|
||||
this.panelWin.gTarget = this.target;
|
||||
this.panelWin.gFront = new CanvasFront(this.target.client, this.target.form);
|
||||
return this.panelWin.startupCanvasDebugger();
|
||||
})
|
||||
.then(() => {
|
||||
|
||||
await this.panelWin.startupCanvasDebugger();
|
||||
|
||||
this.isReady = true;
|
||||
this.emit("ready");
|
||||
return this;
|
||||
})
|
||||
.catch(function onError(aReason) {
|
||||
DevToolsUtils.reportException("CanvasDebuggerPanel.prototype.open", aReason);
|
||||
});
|
||||
},
|
||||
|
||||
// DevToolPanel API
|
||||
|
|
|
@ -149,7 +149,6 @@ function initCanvasDebuggerBackend(aUrl) {
|
|||
return (async function() {
|
||||
const tab = await addTab(aUrl);
|
||||
const target = await TargetFactory.forTab(tab);
|
||||
|
||||
await target.attach();
|
||||
|
||||
const front = new CanvasFront(target.client, target.form);
|
||||
|
|
|
@ -296,10 +296,6 @@ var DebuggerController = {
|
|||
connectThread: function () {
|
||||
const { newSource, fetchEventListeners } = bindActionCreators(actions, this.dispatch);
|
||||
|
||||
// TODO: bug 806775, update the globals list using aPacket.hostAnnotations
|
||||
// from bug 801084.
|
||||
// this.client.addListener("newGlobal", ...);
|
||||
|
||||
this.activeThread.addListener("newSource", (event, packet) => {
|
||||
newSource(packet.source);
|
||||
|
||||
|
@ -344,7 +340,6 @@ var DebuggerController = {
|
|||
return;
|
||||
}
|
||||
|
||||
this.client.removeListener("newGlobal");
|
||||
this.activeThread.removeListener("newSource");
|
||||
this.activeThread.removeListener("blackboxchange");
|
||||
|
||||
|
@ -495,7 +490,7 @@ Workers.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
this._tabClient.listWorkers((response) => {
|
||||
this._tabClient.listWorkers().then((response) => {
|
||||
let workerForms = Object.create(null);
|
||||
for (let worker of response.workers) {
|
||||
workerForms[worker.actor] = worker;
|
||||
|
|
|
@ -46777,7 +46777,7 @@ function mapTopLevelAwait(expression) {
|
|||
const ast = hasTopLevelAwait(expression);
|
||||
if (ast) {
|
||||
const func = wrapExpression(ast);
|
||||
return (0, _generator2.default)(_template2.default.ast(`(${func})().then(console.log).catch(console.error);`)).code;
|
||||
return (0, _generator2.default)(_template2.default.ast(`(${func})();`)).code;
|
||||
}
|
||||
|
||||
return expression;
|
||||
|
|
|
@ -7510,12 +7510,10 @@ class Telemetry {
|
|||
* Event telemetry is disabled by default. Use this method to enable it for
|
||||
* a particular category.
|
||||
*
|
||||
* @param {String} category
|
||||
* The telemetry event category e.g. "devtools.main"
|
||||
* @param {Boolean} enabled
|
||||
* Enabled: true or false.
|
||||
*/
|
||||
setEventRecordingEnabled(category, enabled) {
|
||||
setEventRecordingEnabled(enabled) {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
|
@ -7529,9 +7527,10 @@ class Telemetry {
|
|||
* properties have been received. Once they have all been received we send the
|
||||
* telemetry event.
|
||||
*
|
||||
* @param {String} category
|
||||
* The telemetry event category (a group name for events and helps to
|
||||
* avoid name conflicts) e.g. "devtools.main"
|
||||
* @param {Object} obj
|
||||
* The telemetry event or ping is associated with this object, meaning
|
||||
* that multiple events or pings for the same histogram may be run
|
||||
* concurrently, as long as they are associated with different objects.
|
||||
* @param {String} method
|
||||
* The telemetry event method (describes the type of event that
|
||||
* occurred e.g. "open")
|
||||
|
@ -7549,16 +7548,17 @@ class Telemetry {
|
|||
* "width"
|
||||
* ]
|
||||
*/
|
||||
preparePendingEvent(category, method, object, value, expected = []) {}
|
||||
preparePendingEvent(obj, method, object, value, expected = []) {}
|
||||
|
||||
/**
|
||||
* Adds an expected property for either a current or future pending event.
|
||||
* This means that if preparePendingEvent() is called before or after sending
|
||||
* the event properties they will automatically added to the event.
|
||||
*
|
||||
* @param {String} category
|
||||
* The telemetry event category (a group name for events and helps to
|
||||
* avoid name conflicts) e.g. "devtools.main"
|
||||
* @param {Object} obj
|
||||
* The telemetry event or ping is associated with this object, meaning
|
||||
* that multiple events or pings for the same histogram may be run
|
||||
* concurrently, as long as they are associated with different objects.
|
||||
* @param {String} method
|
||||
* The telemetry event method (describes the type of event that
|
||||
* occurred e.g. "open")
|
||||
|
@ -7573,16 +7573,17 @@ class Telemetry {
|
|||
* @param {String} pendingPropValue
|
||||
* The pending property value
|
||||
*/
|
||||
addEventProperty(category, method, object, value, pendingPropName, pendingPropValue) {}
|
||||
addEventProperty(obj, method, object, value, pendingPropName, pendingPropValue) {}
|
||||
|
||||
/**
|
||||
* Adds expected properties for either a current or future pending event.
|
||||
* This means that if preparePendingEvent() is called before or after sending
|
||||
* the event properties they will automatically added to the event.
|
||||
*
|
||||
* @param {String} category
|
||||
* The telemetry event category (a group name for events and helps to
|
||||
* avoid name conflicts) e.g. "devtools.main"
|
||||
* @param {Object} obj
|
||||
* The telemetry event or ping is associated with this object, meaning
|
||||
* that multiple events or pings for the same histogram may be run
|
||||
* concurrently, as long as they are associated with different objects.
|
||||
* @param {String} method
|
||||
* The telemetry event method (describes the type of event that
|
||||
* occurred e.g. "open")
|
||||
|
@ -7596,16 +7597,17 @@ class Telemetry {
|
|||
* An object containing key, value pairs that should be added to the
|
||||
* event as properties.
|
||||
*/
|
||||
addEventProperties(category, method, object, value, pendingObject) {}
|
||||
addEventProperties(obj, method, object, value, pendingObject) {}
|
||||
|
||||
/**
|
||||
* A private method that is not to be used externally. This method is used to
|
||||
* prepare a pending telemetry event for sending and then send it via
|
||||
* recordEvent().
|
||||
*
|
||||
* @param {String} category
|
||||
* The telemetry event category (a group name for events and helps to
|
||||
* avoid name conflicts) e.g. "devtools.main"
|
||||
* @param {Object} obj
|
||||
* The telemetry event or ping is associated with this object, meaning
|
||||
* that multiple events or pings for the same histogram may be run
|
||||
* concurrently, as long as they are associated with different objects.
|
||||
* @param {String} method
|
||||
* The telemetry event method (describes the type of event that
|
||||
* occurred e.g. "open")
|
||||
|
@ -7616,14 +7618,11 @@ class Telemetry {
|
|||
* The telemetry event value (a user defined value, providing context
|
||||
* for the event) e.g. "console"
|
||||
*/
|
||||
_sendPendingEvent(category, method, object, value) {}
|
||||
_sendPendingEvent(obj, method, object, value) {}
|
||||
|
||||
/**
|
||||
* Send a telemetry event.
|
||||
*
|
||||
* @param {String} category
|
||||
* The telemetry event category (a group name for events and helps to
|
||||
* avoid name conflicts) e.g. "devtools.main"
|
||||
* @param {String} method
|
||||
* The telemetry event method (describes the type of event that
|
||||
* occurred e.g. "open")
|
||||
|
@ -7641,15 +7640,22 @@ class Telemetry {
|
|||
* width: "1024"
|
||||
* }
|
||||
*/
|
||||
recordEvent(category, method, object, value, extra) {}
|
||||
recordEvent(method, object, value, extra) {}
|
||||
|
||||
/**
|
||||
* Sends telemetry pings to indicate that a tool has been opened.
|
||||
*
|
||||
* @param {String} id
|
||||
* The ID of the tool opened.
|
||||
* @param {String} sessionId
|
||||
* Toolbox session id used when we need to ensure a tool really has a
|
||||
* timer before calculating a delta.
|
||||
* @param {Object} obj
|
||||
* The telemetry event or ping is associated with this object, meaning
|
||||
* that multiple events or pings for the same histogram may be run
|
||||
* concurrently, as long as they are associated with different objects.
|
||||
*/
|
||||
toolOpened(id) {}
|
||||
toolOpened(id, sessionId, obj) {}
|
||||
|
||||
/**
|
||||
* Sends telemetry pings to indicate that a tool has been closed.
|
||||
|
@ -7657,7 +7663,7 @@ class Telemetry {
|
|||
* @param {String} id
|
||||
* The ID of the tool opened.
|
||||
*/
|
||||
toolClosed(id) {}
|
||||
toolClosed(id, sessionId, obj) {}
|
||||
}
|
||||
|
||||
module.exports = Telemetry;
|
||||
|
|
|
@ -21,10 +21,6 @@ function DebuggerPanel(iframeWindow, toolbox) {
|
|||
|
||||
DebuggerPanel.prototype = {
|
||||
open: async function() {
|
||||
if (!this.toolbox.target.isRemote) {
|
||||
await this.toolbox.target.attach();
|
||||
}
|
||||
|
||||
const {
|
||||
actions,
|
||||
store,
|
||||
|
|
|
@ -42,18 +42,18 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|||
* //
|
||||
* // NOTE: You CAN send properties before preparing the event.
|
||||
* //
|
||||
* telemetry.preparePendingEvent("devtools.main", "pause", "debugger", null, [
|
||||
* telemetry.preparePendingEvent(this, "pause", "debugger", null, [
|
||||
* "reason", "collapsed_callstacks"
|
||||
* ]);
|
||||
*
|
||||
* // Elsewhere in another codepath send the reason property
|
||||
* telemetry.addEventProperty(
|
||||
* "devtools.main", "pause", "debugger", null, "reason", "debugger-statement"
|
||||
* this, "pause", "debugger", null, "reason", "debugger-statement"
|
||||
* );
|
||||
*
|
||||
* // Elsewhere in another codepath send the collapsed_callstacks property
|
||||
* telemetry.addEventProperty(
|
||||
* "devtools.main", "pause", "debugger", null, "collapsed_callstacks", 1
|
||||
* this, "pause", "debugger", null, "collapsed_callstacks", 1
|
||||
* );
|
||||
*/
|
||||
const telemetry = new _telemetry2.default();
|
||||
|
@ -75,7 +75,7 @@ function recordEvent(eventName, fields = {}) {
|
|||
/* eslint-disable camelcase */
|
||||
|
||||
|
||||
telemetry.recordEvent("devtools.main", eventName, "debugger", null, {
|
||||
telemetry.recordEvent(eventName, "debugger", null, {
|
||||
session_id: sessionId,
|
||||
...fields
|
||||
});
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
*/
|
||||
|
||||
var gClient, gThreadClient;
|
||||
var gNewGlobal = promise.defer();
|
||||
var gNewChromeSource = promise.defer();
|
||||
|
||||
var { DevToolsLoader } = ChromeUtils.import(
|
||||
|
@ -35,12 +34,6 @@ async function attachThread(client, actor) {
|
|||
return threadClient;
|
||||
}
|
||||
|
||||
function onNewGlobal() {
|
||||
ok(true, "Received a new chrome global.");
|
||||
gClient.removeListener("newGlobal", onNewGlobal);
|
||||
gNewGlobal.resolve();
|
||||
}
|
||||
|
||||
function onNewSource(event, packet) {
|
||||
if (packet.source.url.startsWith("chrome:")) {
|
||||
ok(true, "Received a new chrome source: " + packet.source.url);
|
||||
|
@ -58,7 +51,6 @@ function resumeAndCloseConnection() {
|
|||
registerCleanupFunction(function() {
|
||||
gClient = null;
|
||||
gThreadClient = null;
|
||||
gNewGlobal = null;
|
||||
gNewChromeSource = null;
|
||||
|
||||
customLoader = null;
|
||||
|
@ -78,8 +70,7 @@ add_task(async function() {
|
|||
|
||||
// listen for a new source and global
|
||||
gThreadClient.addListener("newSource", onNewSource);
|
||||
gClient.addListener("newGlobal", onNewGlobal);
|
||||
await promise.all([gNewGlobal.promise, gNewChromeSource.promise]);
|
||||
await gNewChromeSource.promise;
|
||||
|
||||
await resumeAndCloseConnection();
|
||||
});
|
||||
|
|
|
@ -35,20 +35,13 @@ DebuggerPanel.prototype = {
|
|||
* A promise that is resolved when the Debugger completes opening.
|
||||
*/
|
||||
open: function () {
|
||||
let targetPromise;
|
||||
|
||||
// Local debugging needs to make the target remote.
|
||||
if (!this.target.isRemote) {
|
||||
targetPromise = this.target.attach();
|
||||
// Listen for tab switching events to manage focus when the content window
|
||||
// is paused and events suppressed.
|
||||
if (this.target.isLocalTab) {
|
||||
this.target.tab.addEventListener("TabSelect", this);
|
||||
} else {
|
||||
targetPromise = promise.resolve(this.target);
|
||||
}
|
||||
|
||||
return targetPromise
|
||||
.then(() => this._controller.startupDebugger())
|
||||
return Promise.resolve(this._controller.startupDebugger())
|
||||
.then(() => this._controller.connect())
|
||||
.then(() => {
|
||||
this._toolbox.on("host-changed", this.handleHostChanged);
|
||||
|
|
|
@ -11,7 +11,6 @@ const TAB_URL = EXAMPLE_URL + "doc_inline-debugger-statement.html";
|
|||
|
||||
var gClient, gThreadClient;
|
||||
var gAttached = promise.defer();
|
||||
var gNewGlobal = promise.defer();
|
||||
var gNewChromeSource = promise.defer();
|
||||
|
||||
var { DevToolsLoader } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
|
||||
|
@ -30,7 +29,7 @@ function test() {
|
|||
is(aType, "browser",
|
||||
"Root actor should identify itself as a browser.");
|
||||
|
||||
promise.all([gAttached.promise, gNewGlobal.promise, gNewChromeSource.promise])
|
||||
promise.all([gAttached.promise, gNewChromeSource.promise])
|
||||
.then(resumeAndCloseConnection)
|
||||
.then(finish)
|
||||
.catch(aError => {
|
||||
|
@ -43,8 +42,6 @@ function test() {
|
|||
|
||||
function testParentProcessTargetActor() {
|
||||
gClient.getProcess().then(aResponse => {
|
||||
gClient.addListener("newGlobal", onNewGlobal);
|
||||
|
||||
let actor = aResponse.form.actor;
|
||||
gClient.attachTarget(actor).then(([response, tabClient]) => {
|
||||
tabClient.attachThread(null).then(([aResponse, aThreadClient]) => {
|
||||
|
@ -66,13 +63,6 @@ function testParentProcessTargetActor() {
|
|||
});
|
||||
}
|
||||
|
||||
function onNewGlobal() {
|
||||
ok(true, "Received a new chrome global.");
|
||||
|
||||
gClient.removeListener("newGlobal", onNewGlobal);
|
||||
gNewGlobal.resolve();
|
||||
}
|
||||
|
||||
function onNewSource(aEvent, aPacket) {
|
||||
if (aPacket.source.url.startsWith("chrome:")) {
|
||||
ok(true, "Received a new chrome source: " + aPacket.source.url);
|
||||
|
@ -92,7 +82,6 @@ registerCleanupFunction(function () {
|
|||
gClient = null;
|
||||
gThreadClient = null;
|
||||
gAttached = null;
|
||||
gNewGlobal = null;
|
||||
gNewChromeSource = null;
|
||||
|
||||
customLoader = null;
|
||||
|
|
|
@ -1093,11 +1093,7 @@ function attachTarget(client, tab) {
|
|||
|
||||
function listWorkers(tabClient) {
|
||||
info("Listing workers.");
|
||||
return new Promise(function (resolve) {
|
||||
tabClient.listWorkers(function (response) {
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
return tabClient.listWorkers();
|
||||
}
|
||||
|
||||
function findWorker(workers, url) {
|
||||
|
|
|
@ -36,26 +36,13 @@ DomPanel.prototype = {
|
|||
* @return object
|
||||
* A promise that is resolved when the DOM panel completes opening.
|
||||
*/
|
||||
async open() {
|
||||
if (this._opening) {
|
||||
return this._opening;
|
||||
}
|
||||
|
||||
const deferred = defer();
|
||||
this._opening = deferred.promise;
|
||||
|
||||
// Local monitoring needs to make the target remote.
|
||||
if (!this.target.isRemote) {
|
||||
await this.target.attach();
|
||||
}
|
||||
|
||||
open() {
|
||||
this.initialize();
|
||||
|
||||
this.isReady = true;
|
||||
this.emit("ready");
|
||||
deferred.resolve(this);
|
||||
|
||||
return this._opening;
|
||||
return this;
|
||||
},
|
||||
|
||||
// Initialization
|
||||
|
|
|
@ -302,7 +302,9 @@ BrowserToolboxProcess.prototype = {
|
|||
}).then(proc => {
|
||||
this._dbgProcess = proc;
|
||||
|
||||
this._telemetry.toolOpened("jsbrowserdebugger");
|
||||
// jsbrowserdebugger is not connected with a toolbox so we pass -1 as the
|
||||
// toolbox session id.
|
||||
this._telemetry.toolOpened("jsbrowserdebugger", -1, this);
|
||||
|
||||
dumpn("Chrome toolbox is now running...");
|
||||
this.emit("run", this);
|
||||
|
@ -355,7 +357,9 @@ BrowserToolboxProcess.prototype = {
|
|||
this._dbgProcess.stdout.close();
|
||||
await this._dbgProcess.kill();
|
||||
|
||||
this._telemetry.toolClosed("jsbrowserdebugger");
|
||||
// jsbrowserdebugger is not connected with a toolbox so we pass -1 as the
|
||||
// toolbox session id.
|
||||
this._telemetry.toolClosed("jsbrowserdebugger", -1, this);
|
||||
|
||||
if (this.debuggerServer) {
|
||||
this.debuggerServer.off("connectionchange", this._onConnectionChange);
|
||||
|
|
|
@ -82,7 +82,7 @@ function createToolMenuElements(toolDefinition, doc) {
|
|||
try {
|
||||
const window = event.target.ownerDocument.defaultView;
|
||||
await gDevToolsBrowser.selectToolCommand(window.gBrowser, id, Cu.now());
|
||||
sendEntryPointTelemetry();
|
||||
sendEntryPointTelemetry(window);
|
||||
} catch (e) {
|
||||
console.error(`Exception while opening ${id}: ${e}\n${e.stack}`);
|
||||
}
|
||||
|
@ -110,17 +110,15 @@ function createToolMenuElements(toolDefinition, doc) {
|
|||
* `devtools/startup/devtools-startup.js` but that codepath is only used the
|
||||
* first time a toolbox is opened for a tab.
|
||||
*/
|
||||
function sendEntryPointTelemetry() {
|
||||
function sendEntryPointTelemetry(window) {
|
||||
if (!telemetry) {
|
||||
telemetry = new Telemetry();
|
||||
}
|
||||
|
||||
telemetry.addEventProperty(
|
||||
"devtools.main", "open", "tools", null, "shortcut", ""
|
||||
);
|
||||
telemetry.addEventProperty(window, "open", "tools", null, "shortcut", "");
|
||||
|
||||
telemetry.addEventProperty(
|
||||
"devtools.main", "open", "tools", null, "entrypoint", "SystemMenu"
|
||||
window, "open", "tools", null, "entrypoint", "SystemMenu"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ function DevTools() {
|
|||
|
||||
EventEmitter.decorate(this);
|
||||
this._telemetry = new Telemetry();
|
||||
this._telemetry.setEventRecordingEnabled("devtools.main", true);
|
||||
this._telemetry.setEventRecordingEnabled(true);
|
||||
|
||||
// Listen for changes to the theme pref.
|
||||
this._onThemeChanged = this._onThemeChanged.bind(this);
|
||||
|
@ -488,9 +488,7 @@ DevTools.prototype = {
|
|||
// the "open" event.
|
||||
const width = Math.ceil(toolbox.win.outerWidth / 50) * 50;
|
||||
const panelName = this.makeToolIdHumanReadable(toolId || toolbox.defaultToolId);
|
||||
this._telemetry.addEventProperty(
|
||||
"devtools.main", "enter", panelName, null, "width", width
|
||||
);
|
||||
this._telemetry.addEventProperty(toolbox, "enter", panelName, null, "width", width);
|
||||
|
||||
return toolbox;
|
||||
},
|
||||
|
@ -517,8 +515,9 @@ DevTools.prototype = {
|
|||
"DEVTOOLS_COLD_TOOLBOX_OPEN_DELAY_MS" : "DEVTOOLS_WARM_TOOLBOX_OPEN_DELAY_MS";
|
||||
this._telemetry.getKeyedHistogramById(telemetryKey).add(toolId, delay);
|
||||
|
||||
const browserWin = toolbox.win.top;
|
||||
this._telemetry.addEventProperty(
|
||||
"devtools.main", "open", "tools", null, "first_panel", panelName
|
||||
browserWin, "open", "tools", null, "first_panel", panelName
|
||||
);
|
||||
},
|
||||
|
||||
|
|
|
@ -100,6 +100,18 @@ ToolSidebar.prototype = {
|
|||
|
||||
TABPANEL_ID_PREFIX: "sidebar-panel-",
|
||||
|
||||
get toolboxSessionId() {
|
||||
const frameElement = this._panelDoc.ownerGlobal.parent.frameElement;
|
||||
|
||||
if (frameElement) {
|
||||
return frameElement.getAttribute("session_id");
|
||||
}
|
||||
|
||||
// We are not running inside a toolbox... this is probably a scratchpad
|
||||
// instance so return -1.
|
||||
return -1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a "…" button at the end of the tabstripe that toggles a dropdown menu
|
||||
* containing the list of all tabs if any become hidden due to lack of room.
|
||||
|
@ -450,13 +462,13 @@ ToolSidebar.prototype = {
|
|||
this._currentTool = this.getCurrentTabID();
|
||||
if (previousTool) {
|
||||
if (this._telemetry) {
|
||||
this._telemetry.toolClosed(previousTool);
|
||||
this._telemetry.toolClosed(previousTool, this.toolboxSessionId, this);
|
||||
}
|
||||
this.emit(previousTool + "-unselected");
|
||||
}
|
||||
|
||||
if (this._telemetry) {
|
||||
this._telemetry.toolOpened(this._currentTool);
|
||||
this._telemetry.toolOpened(this._currentTool, this.toolboxSessionId, this);
|
||||
}
|
||||
|
||||
this.emit(this._currentTool + "-selected");
|
||||
|
@ -508,7 +520,7 @@ ToolSidebar.prototype = {
|
|||
this._currentTool = id;
|
||||
|
||||
if (this._telemetry) {
|
||||
this._telemetry.toolOpened(this._currentTool);
|
||||
this._telemetry.toolOpened(this._currentTool, this.toolboxSessionId, this);
|
||||
}
|
||||
|
||||
this._selectTabSoon(id);
|
||||
|
@ -578,7 +590,7 @@ ToolSidebar.prototype = {
|
|||
}
|
||||
|
||||
if (this._currentTool && this._telemetry) {
|
||||
this._telemetry.toolClosed(this._currentTool);
|
||||
this._telemetry.toolClosed(this._currentTool, this.toolboxSessionId, this);
|
||||
}
|
||||
|
||||
this._toolPanel.emit("sidebar-destroyed", this);
|
||||
|
|
|
@ -15,7 +15,6 @@ const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties"
|
|||
add_task(async function() {
|
||||
const tab = await addTab("about:blank");
|
||||
const target = await TargetFactory.forTab(tab);
|
||||
await target.attach();
|
||||
|
||||
const toolIDs = gDevTools.getToolDefinitionArray()
|
||||
.filter(
|
||||
|
|
|
@ -35,7 +35,6 @@ function test() {
|
|||
toggleAllTools(true);
|
||||
const tab = await addTab("about:blank");
|
||||
const target = await TargetFactory.forTab(tab);
|
||||
await target.attach();
|
||||
await performChecks(target);
|
||||
gBrowser.removeCurrentTab();
|
||||
toggleAllTools(false);
|
||||
|
|
|
@ -20,14 +20,12 @@ function test() {
|
|||
addTab(TEST_URL).then(async () => {
|
||||
target = await TargetFactory.forTab(gBrowser.selectedTab);
|
||||
|
||||
target.attach().then(() => {
|
||||
toolIDs = gDevTools.getToolDefinitionArray()
|
||||
.filter(def => def.isTargetSupported(target))
|
||||
.map(def => def.id);
|
||||
gDevTools.showToolbox(target, toolIDs[0], Toolbox.HostType.BOTTOM)
|
||||
.then(startReloadTest);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function startReloadTest(aToolbox) {
|
||||
|
|
|
@ -82,11 +82,6 @@ OptionsPanel.prototype = {
|
|||
},
|
||||
|
||||
async open() {
|
||||
// For local debugging we need to make the target remote.
|
||||
if (!this.target.isRemote) {
|
||||
await this.target.attach();
|
||||
}
|
||||
|
||||
this.setupToolsList();
|
||||
this.setupToolbarButtonsList();
|
||||
this.setupThemeList();
|
||||
|
@ -266,10 +261,15 @@ OptionsPanel.prototype = {
|
|||
return tool.visibilityswitch && !tool.hiddenInOptions;
|
||||
});
|
||||
|
||||
const fragment = this.panelDoc.createDocumentFragment();
|
||||
for (const tool of toggleableTools) {
|
||||
defaultToolsBox.appendChild(createToolCheckbox(tool));
|
||||
fragment.appendChild(createToolCheckbox(tool));
|
||||
}
|
||||
|
||||
const toolsNotSupportedLabelNode =
|
||||
this.panelDoc.getElementById("tools-not-supported-label");
|
||||
defaultToolsBox.insertBefore(fragment, toolsNotSupportedLabelNode);
|
||||
|
||||
// Clean up any existent additional tools content.
|
||||
for (const label of additionalToolsBox.querySelectorAll("label")) {
|
||||
label.remove();
|
||||
|
@ -396,7 +396,15 @@ OptionsPanel.prototype = {
|
|||
|
||||
for (const prefDefinition of prefDefinitions) {
|
||||
const parent = this.panelDoc.getElementById(prefDefinition.parentId);
|
||||
parent.appendChild(createPreferenceOption(prefDefinition));
|
||||
// We want to insert the new definition after the last existing
|
||||
// definition, but before any other element.
|
||||
// For example in the "Advanced Settings" column there's indeed a <span>
|
||||
// text at the end, and we want that it stays at the end.
|
||||
// The reference element can be `null` if there's no label or if there's
|
||||
// no element after the last label. But that's OK and it will do what we
|
||||
// want.
|
||||
const referenceElement = parent.querySelector("label:last-of-type + *");
|
||||
parent.insertBefore(createPreferenceOption(prefDefinition), referenceElement);
|
||||
parent.removeAttribute("hidden");
|
||||
}
|
||||
},
|
||||
|
@ -459,6 +467,10 @@ OptionsPanel.prototype = {
|
|||
} else {
|
||||
// Hide the checkbox and label
|
||||
this.disableJSNode.parentNode.style.display = "none";
|
||||
|
||||
const triggersPageRefreshLabel =
|
||||
this.panelDoc.getElementById("triggers-page-refresh-label");
|
||||
triggersPageRefreshLabel.style.display = "none";
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -519,7 +531,7 @@ OptionsPanel.prototype = {
|
|||
"javascriptEnabled": this._origJavascriptEnabled,
|
||||
"performReload": false
|
||||
};
|
||||
this.target.activeTab.reconfigure(options, resolve);
|
||||
this.target.activeTab.reconfigure(options).then(resolve);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
<div id="tools-box" class="options-vertical-pane">
|
||||
<fieldset id="default-tools-box" class="options-groupbox">
|
||||
<legend>&options.selectDefaultTools.label2;</legend>
|
||||
<span id="tools-not-supported-label"
|
||||
class="options-citation-label theme-comment">
|
||||
&options.toolNotSupported.label;</span>
|
||||
</fieldset>
|
||||
|
||||
<fieldset id="additional-tools-box" class="options-groupbox">
|
||||
|
@ -26,9 +29,6 @@
|
|||
|
||||
<fieldset id="enabled-toolbox-buttons-box" class="options-groupbox">
|
||||
<legend>&options.selectEnabledToolboxButtons.label;</legend>
|
||||
<span id="tools-not-supported-label"
|
||||
class="options-citation-label theme-comment">
|
||||
&options.toolNotSupported.label;</span>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
|
@ -189,6 +189,7 @@
|
|||
<span>&options.enableRemote.label3;</span>
|
||||
</label>
|
||||
<span class="options-citation-label theme-comment"
|
||||
id="triggers-page-refresh-label"
|
||||
>&options.context.triggersPageRefresh;</span>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
|
|
@ -540,14 +540,15 @@ Toolbox.prototype = {
|
|||
|
||||
// Wait until the original tool is selected so that the split
|
||||
// console input will receive focus.
|
||||
const browserWin = this.win.top;
|
||||
let splitConsolePromise = promise.resolve();
|
||||
if (Services.prefs.getBoolPref(SPLITCONSOLE_ENABLED_PREF)) {
|
||||
splitConsolePromise = this.openSplitConsole();
|
||||
this.telemetry.addEventProperty(
|
||||
"devtools.main", "open", "tools", null, "splitconsole", true);
|
||||
browserWin, "open", "tools", null, "splitconsole", true);
|
||||
} else {
|
||||
this.telemetry.addEventProperty(
|
||||
"devtools.main", "open", "tools", null, "splitconsole", false);
|
||||
browserWin, "open", "tools", null, "splitconsole", false);
|
||||
}
|
||||
|
||||
await promise.all([
|
||||
|
@ -736,7 +737,7 @@ Toolbox.prototype = {
|
|||
},
|
||||
|
||||
_pingTelemetry: function() {
|
||||
this.telemetry.toolOpened("toolbox");
|
||||
this.telemetry.toolOpened("toolbox", this.sessionId, this);
|
||||
|
||||
this.telemetry.getHistogramById(HOST_HISTOGRAM).add(this._getTelemetryHostId());
|
||||
|
||||
|
@ -745,12 +746,13 @@ Toolbox.prototype = {
|
|||
const currentTheme = Services.prefs.getCharPref("devtools.theme");
|
||||
this.telemetry.keyedScalarAdd(CURRENT_THEME_SCALAR, currentTheme, 1);
|
||||
|
||||
this.telemetry.preparePendingEvent("devtools.main", "open", "tools", null, [
|
||||
const browserWin = this.win.top;
|
||||
this.telemetry.preparePendingEvent(browserWin, "open", "tools", null, [
|
||||
"entrypoint", "first_panel", "host", "shortcut",
|
||||
"splitconsole", "width", "session_id"
|
||||
]);
|
||||
this.telemetry.addEventProperty(
|
||||
"devtools.main", "open", "tools", null, "host", this._getTelemetryHostString()
|
||||
browserWin, "open", "tools", null, "host", this._getTelemetryHostString()
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -1394,9 +1396,9 @@ Toolbox.prototype = {
|
|||
*/
|
||||
togglePaintFlashing: function() {
|
||||
if (this.isPaintFlashing) {
|
||||
this.telemetry.toolOpened("paintflashing");
|
||||
this.telemetry.toolOpened("paintflashing", this.sessionId, this);
|
||||
} else {
|
||||
this.telemetry.toolClosed("paintflashing");
|
||||
this.telemetry.toolClosed("paintflashing", this.sessionId, this);
|
||||
}
|
||||
this.isPaintFlashing = !this.isPaintFlashing;
|
||||
return this.target.activeTab.reconfigure({"paintFlashing": this.isPaintFlashing});
|
||||
|
@ -1910,7 +1912,7 @@ Toolbox.prototype = {
|
|||
id === "options" ||
|
||||
this.additionalToolDefinitions.get(id)) {
|
||||
if (this.currentToolId) {
|
||||
this.telemetry.toolClosed(this.currentToolId);
|
||||
this.telemetry.toolClosed(this.currentToolId, this.sessionId, this);
|
||||
}
|
||||
|
||||
this._pingTelemetrySelectTool(id, reason);
|
||||
|
@ -1949,7 +1951,7 @@ Toolbox.prototype = {
|
|||
// On first load this.currentToolId === undefined so we need to skip sending
|
||||
// a devtools.main.exit telemetry event.
|
||||
if (this.currentToolId) {
|
||||
this.telemetry.recordEvent("devtools.main", "exit", prevPanelName, null, {
|
||||
this.telemetry.recordEvent("exit", prevPanelName, null, {
|
||||
"host": this._hostType,
|
||||
"width": width,
|
||||
"panel_name": prevPanelName,
|
||||
|
@ -1959,7 +1961,8 @@ Toolbox.prototype = {
|
|||
});
|
||||
}
|
||||
|
||||
this.telemetry.addEventProperties("devtools.main", "open", "tools", null, {
|
||||
const browserWin = this.win.top;
|
||||
this.telemetry.addEventProperties(browserWin, "open", "tools", null, {
|
||||
"width": width,
|
||||
"session_id": this.sessionId
|
||||
});
|
||||
|
@ -1968,9 +1971,9 @@ Toolbox.prototype = {
|
|||
pending.push("message_count");
|
||||
}
|
||||
|
||||
this.telemetry.preparePendingEvent("devtools.main", "enter", panelName, null, pending);
|
||||
this.telemetry.preparePendingEvent(this, "enter", panelName, null, pending);
|
||||
|
||||
this.telemetry.addEventProperties("devtools.main", "enter", panelName, null, {
|
||||
this.telemetry.addEventProperties(this, "enter", panelName, null, {
|
||||
"host": this._hostType,
|
||||
"start_state": reason,
|
||||
"panel_name": panelName,
|
||||
|
@ -1981,7 +1984,7 @@ Toolbox.prototype = {
|
|||
if (reason !== "initial_panel") {
|
||||
const width = Math.ceil(this.win.outerWidth / 50) * 50;
|
||||
this.telemetry.addEventProperty(
|
||||
"devtools.main", "enter", panelName, null, "width", width
|
||||
this, "enter", panelName, null, "width", width
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1989,10 +1992,10 @@ Toolbox.prototype = {
|
|||
// devtools/client/webconsole/webconsole-output-wrapper.js
|
||||
if (!cold && id === "webconsole") {
|
||||
this.telemetry.addEventProperty(
|
||||
"devtools.main", "enter", "webconsole", null, "message_count", 0);
|
||||
this, "enter", "webconsole", null, "message_count", 0);
|
||||
}
|
||||
|
||||
this.telemetry.toolOpened(id);
|
||||
this.telemetry.toolOpened(id, this.sessionId, this);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -2060,7 +2063,7 @@ Toolbox.prototype = {
|
|||
|
||||
return this.loadTool("webconsole").then(() => {
|
||||
this.component.setIsSplitConsoleActive(true);
|
||||
this.telemetry.recordEvent("devtools.main", "activate", "split_console", null, {
|
||||
this.telemetry.recordEvent("activate", "split_console", null, {
|
||||
"host": this._getTelemetryHostString(),
|
||||
"width": Math.ceil(this.win.outerWidth / 50) * 50,
|
||||
"session_id": this.sessionId
|
||||
|
@ -2082,7 +2085,7 @@ Toolbox.prototype = {
|
|||
this._refreshConsoleDisplay();
|
||||
this.component.setIsSplitConsoleActive(false);
|
||||
|
||||
this.telemetry.recordEvent("devtools.main", "deactivate", "split_console", null, {
|
||||
this.telemetry.recordEvent("deactivate", "split_console", null, {
|
||||
"host": this._getTelemetryHostString(),
|
||||
"width": Math.ceil(this.win.outerWidth / 50) * 50,
|
||||
"session_id": this.sessionId
|
||||
|
@ -2814,7 +2817,7 @@ Toolbox.prototype = {
|
|||
|
||||
// We normally handle toolClosed from selectTool() but in the event of the
|
||||
// toolbox closing we need to handle it here instead.
|
||||
this.telemetry.toolClosed(this.currentToolId);
|
||||
this.telemetry.toolClosed(this.currentToolId, this.sessionId, this);
|
||||
|
||||
this._lastFocusedElement = null;
|
||||
|
||||
|
@ -2906,8 +2909,8 @@ Toolbox.prototype = {
|
|||
const width = Math.ceil(win.outerWidth / 50) * 50;
|
||||
const prevPanelName = this.getTelemetryPanelNameOrOther(this.currentToolId);
|
||||
|
||||
this.telemetry.toolClosed("toolbox");
|
||||
this.telemetry.recordEvent("devtools.main", "exit", prevPanelName, null, {
|
||||
this.telemetry.toolClosed("toolbox", this.sessionId, this);
|
||||
this.telemetry.recordEvent("exit", prevPanelName, null, {
|
||||
"host": host,
|
||||
"width": width,
|
||||
"panel_name": this.getTelemetryPanelNameOrOther(this.currentToolId),
|
||||
|
@ -2915,7 +2918,7 @@ Toolbox.prototype = {
|
|||
"reason": "toolbox_close",
|
||||
"session_id": this.sessionId
|
||||
});
|
||||
this.telemetry.recordEvent("devtools.main", "close", "tools", null, {
|
||||
this.telemetry.recordEvent("close", "tools", null, {
|
||||
"host": host,
|
||||
"width": width,
|
||||
"session_id": this.sessionId
|
||||
|
|
|
@ -172,7 +172,6 @@ Inspector.prototype = {
|
|||
localizeMarkup(this.panelDoc);
|
||||
|
||||
this._cssProperties = await initCssProperties(this.toolbox);
|
||||
await this.target.attach();
|
||||
await this._getPageStyle();
|
||||
|
||||
// This may throw if the document is still loading and we are
|
||||
|
|
|
@ -1550,7 +1550,7 @@ MarkupView.prototype = {
|
|||
}
|
||||
|
||||
const end = this.telemetry.msSystemNow();
|
||||
this.telemetry.recordEvent("devtools.main", "edit_html", "inspector", null, {
|
||||
this.telemetry.recordEvent("edit_html", "inspector", null, {
|
||||
"made_changes": commit,
|
||||
"time_open": end - start,
|
||||
"session_id": this.toolbox.sessionId
|
||||
|
|
|
@ -604,7 +604,7 @@ RuleEditor.prototype = {
|
|||
// the field gets destroyed (see _newPropertyDestroy)
|
||||
this.editor.input.blur();
|
||||
|
||||
this.telemetry.recordEvent("devtools.main", "edit_rule", "ruleview", null, {
|
||||
this.telemetry.recordEvent("edit_rule", "ruleview", null, {
|
||||
"session_id": this.toolbox.sessionId
|
||||
});
|
||||
},
|
||||
|
|
|
@ -754,7 +754,7 @@ TextPropertyEditor.prototype = {
|
|||
}
|
||||
this.prop.setEnabled(!checked);
|
||||
event.stopPropagation();
|
||||
this.telemetry.recordEvent("devtools.main", "edit_rule", "ruleview", null, {
|
||||
this.telemetry.recordEvent("edit_rule", "ruleview", null, {
|
||||
"session_id": this.toolbox.sessionId
|
||||
});
|
||||
},
|
||||
|
@ -827,7 +827,7 @@ TextPropertyEditor.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
this.telemetry.recordEvent("devtools.main", "edit_rule", "ruleview", null, {
|
||||
this.telemetry.recordEvent("edit_rule", "ruleview", null, {
|
||||
"session_id": this.toolbox.sessionId
|
||||
});
|
||||
|
||||
|
@ -922,7 +922,7 @@ TextPropertyEditor.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
this.telemetry.recordEvent("devtools.main", "edit_rule", "ruleview", null, {
|
||||
this.telemetry.recordEvent("edit_rule", "ruleview", null, {
|
||||
"session_id": this.toolbox.sessionId
|
||||
});
|
||||
|
||||
|
|
|
@ -27,7 +27,8 @@ async function testRuleView(ruleView, inspector) {
|
|||
|
||||
const tooltip = ruleView.tooltips.getTooltip("previewTooltip");
|
||||
const tooltipContent = ruleView.styleDocument.createElementNS(XHTML_NS, "div");
|
||||
await tooltip.setContent(tooltipContent, {width: 100, height: 30});
|
||||
tooltip.panel.appendChild(tooltipContent);
|
||||
tooltip.setContentSize({width: 100, height: 30});
|
||||
|
||||
// Stop listening for mouse movements because it's not needed for this test,
|
||||
// and causes intermittent failures on Linux. When this test runs in the suite
|
||||
|
@ -52,7 +53,8 @@ async function testComputedView(computedView, inspector) {
|
|||
|
||||
const tooltip = computedView.tooltips.getTooltip("previewTooltip");
|
||||
const tooltipContent = computedView.styleDocument.createElementNS(XHTML_NS, "div");
|
||||
await tooltip.setContent(tooltipContent, {width: 100, height: 30});
|
||||
tooltip.panel.appendChild(tooltipContent);
|
||||
await tooltip.setContentSize({width: 100, height: 30});
|
||||
|
||||
// Stop listening for mouse movements because it's not needed for this test,
|
||||
// and causes intermittent failures on Linux. When this test runs in the suite
|
||||
|
|
|
@ -68,7 +68,8 @@ class ThreePaneOnboardingTooltip {
|
|||
this.closeButton.addEventListener("click", this.onCloseButtonClick);
|
||||
this.learnMoreLink.addEventListener("click", this.onLearnMoreLinkClick);
|
||||
|
||||
this.tooltip.setContent(container, { width: CONTAINER_WIDTH });
|
||||
this.tooltip.panel.appendChild(container);
|
||||
this.tooltip.setContentSize({ width: CONTAINER_WIDTH });
|
||||
this.tooltip.show(this.doc.querySelector("#inspector-sidebar .sidebar-toggle"), {
|
||||
position: "top",
|
||||
});
|
||||
|
|
|
@ -332,15 +332,15 @@ ToolSidebar.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
const sessionId = this._toolPanel._toolbox.sessionId;
|
||||
|
||||
currentToolId = this.getTelemetryPanelNameOrOther(currentToolId);
|
||||
|
||||
if (previousToolId) {
|
||||
const sessionId = this._toolPanel._toolbox.sessionId;
|
||||
|
||||
previousToolId = this.getTelemetryPanelNameOrOther(previousToolId);
|
||||
this._telemetry.toolClosed(previousToolId, sessionId);
|
||||
this._telemetry.toolClosed(previousToolId, sessionId, this);
|
||||
|
||||
this._telemetry.recordEvent("devtools.main", "sidepanel_changed", "inspector", null,
|
||||
this._telemetry.recordEvent("sidepanel_changed", "inspector", null,
|
||||
{
|
||||
"oldpanel": previousToolId,
|
||||
"newpanel": currentToolId,
|
||||
|
@ -349,7 +349,7 @@ ToolSidebar.prototype = {
|
|||
}
|
||||
);
|
||||
}
|
||||
this._telemetry.toolOpened(currentToolId);
|
||||
this._telemetry.toolOpened(currentToolId, sessionId, this);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -446,7 +446,8 @@ ToolSidebar.prototype = {
|
|||
}
|
||||
|
||||
if (this._currentTool && this._telemetry) {
|
||||
this._telemetry.toolClosed(this._currentTool);
|
||||
const sessionId = this._toolPanel._toolbox.sessionId;
|
||||
this._telemetry.toolClosed(this._currentTool, sessionId, this);
|
||||
}
|
||||
|
||||
this._toolPanel.emit("sidebar-destroyed", this);
|
||||
|
|
|
@ -11,10 +11,6 @@ function NetMonitorPanel(iframeWindow, toolbox) {
|
|||
|
||||
NetMonitorPanel.prototype = {
|
||||
async open() {
|
||||
if (!this.toolbox.target.isRemote) {
|
||||
await this.toolbox.target.attach();
|
||||
}
|
||||
|
||||
// Reuse an existing Network monitor API object if available.
|
||||
// It could have been created for WE API before Net panel opens.
|
||||
const api = await this.toolbox.getNetMonitorAPI();
|
||||
|
|
|
@ -317,10 +317,8 @@ class FirefoxConnector {
|
|||
};
|
||||
|
||||
// Reconfigures the tab, optionally triggering a reload.
|
||||
const reconfigureTab = (options) => {
|
||||
return new Promise((resolve) => {
|
||||
this.tabTarget.activeTab.reconfigure(options, resolve);
|
||||
});
|
||||
const reconfigureTab = options => {
|
||||
return this.tabTarget.activeTab.reconfigure(options);
|
||||
};
|
||||
|
||||
// Reconfigures the tab and waits for the target to finish navigating.
|
||||
|
|
|
@ -45,9 +45,7 @@ HarAutomation.prototype = {
|
|||
this.toolbox = toolbox;
|
||||
|
||||
const target = toolbox.target;
|
||||
target.attach().then(() => {
|
||||
this.startMonitoring(target.client, target.form);
|
||||
});
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
|
|
|
@ -114,7 +114,7 @@ function filterChange({action, state, oldState, telemetry, sessionId}) {
|
|||
trigger = "text";
|
||||
}
|
||||
|
||||
telemetry.recordEvent("devtools.main", "filters_changed", "netmonitor", null, {
|
||||
telemetry.recordEvent("filters_changed", "netmonitor", null, {
|
||||
"trigger": trigger,
|
||||
"active": activeFilters.join(","),
|
||||
"inactive": inactiveFilters.join(","),
|
||||
|
@ -128,7 +128,7 @@ function filterChange({action, state, oldState, telemetry, sessionId}) {
|
|||
* telemetry event.
|
||||
*/
|
||||
function sidePanelChange({state, oldState, telemetry, sessionId}) {
|
||||
telemetry.recordEvent("devtools.main", "sidepanel_changed", "netmonitor", null, {
|
||||
telemetry.recordEvent("sidepanel_changed", "netmonitor", null, {
|
||||
"oldpanel": oldState.ui.detailsPanelSelectedTab,
|
||||
"newpanel": state.ui.detailsPanelSelectedTab,
|
||||
"session_id": sessionId,
|
||||
|
@ -140,7 +140,7 @@ function sidePanelChange({state, oldState, telemetry, sessionId}) {
|
|||
* It's responsible for recording "edit_resend" telemetry event.
|
||||
*/
|
||||
function sendCustomRequest({telemetry, sessionId}) {
|
||||
telemetry.recordEvent("devtools.main", "edit_resend", "netmonitor", null, {
|
||||
telemetry.recordEvent("edit_resend", "netmonitor", null, {
|
||||
"session_id": sessionId,
|
||||
});
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ function sendCustomRequest({telemetry, sessionId}) {
|
|||
* It's responsible for recording "throttle_changed" telemetry event.
|
||||
*/
|
||||
function throttlingChange({action, telemetry, sessionId}) {
|
||||
telemetry.recordEvent("devtools.main", "throttle_changed", "netmonitor", null, {
|
||||
telemetry.recordEvent("throttle_changed", "netmonitor", null, {
|
||||
"mode": action.profile,
|
||||
"session_id": sessionId,
|
||||
});
|
||||
|
|
|
@ -136,12 +136,6 @@ function waitForNavigation(target) {
|
|||
});
|
||||
}
|
||||
|
||||
function reconfigureTab(target, options) {
|
||||
return new Promise((resolve) => {
|
||||
target.activeTab.reconfigure(options, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
function toggleCache(target, disabled) {
|
||||
const options = { cacheDisabled: disabled, performReload: true };
|
||||
const navigationFinished = waitForNavigation(target);
|
||||
|
@ -149,7 +143,7 @@ function toggleCache(target, disabled) {
|
|||
// Disable the cache for any toolbox that it is opened from this point on.
|
||||
Services.prefs.setBoolPref("devtools.cache.disabled", disabled);
|
||||
|
||||
return reconfigureTab(target, options).then(() => navigationFinished);
|
||||
return target.activeTab.reconfigure(options).then(() => navigationFinished);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -291,9 +285,6 @@ function initNetMonitor(url, enableCache) {
|
|||
|
||||
const target = await TargetFactory.forTab(tab);
|
||||
|
||||
await target.attach();
|
||||
info("Target remoted.");
|
||||
|
||||
const toolbox = await gDevTools.showToolbox(target, "netmonitor");
|
||||
info("Network monitor pane shown successfully.");
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Telemetry = require("devtools/client/shared/telemetry");
|
||||
|
@ -35,7 +36,7 @@ function PerformanceTelemetry(emitter) {
|
|||
PerformanceTelemetry.prototype.destroy = function() {
|
||||
if (this._previousView) {
|
||||
this._telemetry.finishKeyed(
|
||||
SELECTED_VIEW_HISTOGRAM_NAME, this._previousView, this);
|
||||
SELECTED_VIEW_HISTOGRAM_NAME, this._previousView, this, false);
|
||||
}
|
||||
|
||||
for (const [event] of EVENT_MAP_FLAGS) {
|
||||
|
@ -79,7 +80,7 @@ PerformanceTelemetry.prototype.onRecordingStateChange = function(status, model)
|
|||
PerformanceTelemetry.prototype.onViewSelected = function(viewName) {
|
||||
if (this._previousView) {
|
||||
this._telemetry.finishKeyed(
|
||||
SELECTED_VIEW_HISTOGRAM_NAME, this._previousView, this);
|
||||
SELECTED_VIEW_HISTOGRAM_NAME, this._previousView, this, false);
|
||||
}
|
||||
this._previousView = viewName;
|
||||
this._telemetry.startKeyed(SELECTED_VIEW_HISTOGRAM_NAME, viewName, this);
|
||||
|
|
|
@ -24,7 +24,6 @@ exports.initPanelInTab = async function({ tool, tab }) {
|
|||
dump(`Initializing a ${tool} panel.\n`);
|
||||
|
||||
const target = await TargetFactory.forTab(tab);
|
||||
await target.attach();
|
||||
|
||||
// Open a toolbox and wait for the connection to the performance actors
|
||||
// to be opened. This is necessary because of the WebConsole's
|
||||
|
|
|
@ -36,7 +36,9 @@ const bootstrap = {
|
|||
store: null,
|
||||
|
||||
async init() {
|
||||
this.telemetry.toolOpened("responsive");
|
||||
// responsive is not connected with a toolbox so we pass -1 as the
|
||||
// toolbox session id.
|
||||
this.telemetry.toolOpened("responsive", -1, this);
|
||||
|
||||
const store = this.store = Store();
|
||||
const provider = createElement(Provider, { store }, App());
|
||||
|
@ -46,7 +48,10 @@ const bootstrap = {
|
|||
|
||||
destroy() {
|
||||
this.store = null;
|
||||
this.telemetry.toolClosed("responsive");
|
||||
|
||||
// responsive is not connected with a toolbox so we pass -1 as the
|
||||
// toolbox session id.
|
||||
this.telemetry.toolClosed("responsive", -1, this);
|
||||
this.telemetry = null;
|
||||
},
|
||||
|
||||
|
|
|
@ -125,7 +125,7 @@ const ResponsiveUIManager = exports.ResponsiveUIManager = {
|
|||
tel.scalarAdd("devtools.responsive.toolbox_opened_first", 1);
|
||||
}
|
||||
|
||||
tel.recordEvent("devtools.main", "activate", "responsive_design", null, {
|
||||
tel.recordEvent("activate", "responsive_design", null, {
|
||||
"host": hostType,
|
||||
"width": Math.ceil(window.outerWidth / 50) * 50,
|
||||
"session_id": toolbox ? toolbox.sessionId : -1
|
||||
|
@ -188,7 +188,7 @@ const ResponsiveUIManager = exports.ResponsiveUIManager = {
|
|||
|
||||
const hostType = toolbox ? toolbox.hostType : "none";
|
||||
const t = this._telemetry;
|
||||
t.recordEvent("devtools.main", "deactivate", "responsive_design", null, {
|
||||
t.recordEvent("deactivate", "responsive_design", null, {
|
||||
"host": hostType,
|
||||
"width": Math.ceil(window.outerWidth / 50) * 50,
|
||||
"session_id": toolbox ? toolbox.sessionId : -1
|
||||
|
|
|
@ -42,7 +42,8 @@ class SettingOnboardingTooltip {
|
|||
|
||||
this.closeButton.addEventListener("click", this.onCloseButtonClick);
|
||||
|
||||
this.tooltip.setContent(container, { width: CONTAINER_WIDTH });
|
||||
this.tooltip.panel.appendChild(container);
|
||||
this.tooltip.setContentSize({ width: CONTAINER_WIDTH });
|
||||
this.tooltip.show(this.doc.getElementById("settings-button"), {
|
||||
position: "bottom",
|
||||
});
|
||||
|
|
|
@ -34,11 +34,6 @@ ShaderEditorPanel.prototype = {
|
|||
* A promise that is resolved when the Shader Editor completes opening.
|
||||
*/
|
||||
async open() {
|
||||
// Local debugging needs to make the target remote.
|
||||
if (!this.target.isRemote) {
|
||||
await this.target.attach();
|
||||
}
|
||||
|
||||
this.front = new WebGLFront(this.target.client, this.target.form);
|
||||
this.shadersListView = new ShadersListView();
|
||||
this.eventsHandler = new EventsHandler();
|
||||
|
|
|
@ -591,7 +591,7 @@ class ShadersEditorsView {
|
|||
messageDiv.textContent = message;
|
||||
div.appendChild(messageDiv);
|
||||
}
|
||||
tooltip.setContent(div);
|
||||
tooltip.panel.appendChild(div);
|
||||
|
||||
tooltip.startTogglingOnHover(node, () => true, {
|
||||
toggleDelay: GUTTER_ERROR_PANEL_DELAY
|
||||
|
|
|
@ -165,8 +165,6 @@ function initShaderEditor(aUrl) {
|
|||
const tab = await addTab(aUrl);
|
||||
const target = await TargetFactory.forTab(tab);
|
||||
|
||||
await target.attach();
|
||||
|
||||
Services.prefs.setBoolPref("devtools.shadereditor.enabled", true);
|
||||
const toolbox = await gDevTools.showToolbox(target, "shadereditor");
|
||||
const panel = toolbox.getCurrentPanel();
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
/* 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/. */
|
||||
|
||||
/**
|
||||
* WeakMapMap is a weakmap collection dual-keyed using an object and a string.
|
||||
* This is useful for keeping data compartmentalized e.g. grouped by tab.
|
||||
*
|
||||
* It's name comes from the internal structure which maps a WeakMap to a map,
|
||||
* which contains the target data.
|
||||
*
|
||||
* Usage:
|
||||
* const myWeakMapMap = new WeakMapMap();
|
||||
* const key = { randomObject: true };
|
||||
* myWeakMapMap.set(key, "text1", "Some value1");
|
||||
* myWeakMapMap.set(key, "text2", "Some value2");
|
||||
* myWeakMapMap.get(key, "text1"); // Returns "Some value1"
|
||||
* myWeakMapMap.get(key, "text2"); // Returns "Some value2"
|
||||
* myWeakMapMap.has(key, "text1"); // Returns true
|
||||
* myWeakMapMap.has(key, "notakey"); // Returns false
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
class WeakMapMap {
|
||||
constructor() {
|
||||
this.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value associated to the key and nestedKey, or undefined if
|
||||
* there is none.
|
||||
*
|
||||
* @param {Object} key
|
||||
* The key associated with the desired value.
|
||||
* @param {String} nestedKey
|
||||
* The nested key associated with the desired value.
|
||||
*/
|
||||
get(key, nestedKey) {
|
||||
if (!this.has(key, nestedKey)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this.store.get(key).get(nestedKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value associated to the key and nestedKey, or undefined if
|
||||
* there is none.
|
||||
*
|
||||
* @param {Object} key
|
||||
* The key associated with the desired value.
|
||||
* @param {String} nestedKey
|
||||
* The nested key associated with the desired value.
|
||||
*/
|
||||
has(key, nestedKey) {
|
||||
const hasKey = this.store.has(key);
|
||||
|
||||
return hasKey && this.store.get(key).has(nestedKey);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} key
|
||||
* The key associated with the value.
|
||||
* @param {String} nestedKey
|
||||
* The nested key associated with the value.
|
||||
* @param {any} value
|
||||
* The value to add.
|
||||
*/
|
||||
set(key, nestedKey, value) {
|
||||
if (!this.store.has(key)) {
|
||||
this.store.set(key, new Map());
|
||||
}
|
||||
|
||||
const innerMap = this.store.get(key);
|
||||
innerMap.set(nestedKey, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the value associated to the key and nestedKey.
|
||||
*
|
||||
* @param {Object} key
|
||||
* The key associated with the desired value.
|
||||
* @param {String} nestedKey
|
||||
* The nested key associated with the desired value.
|
||||
*
|
||||
* @returns True if an element in the store has been removed successfully.
|
||||
* False if the key is not found in the store.
|
||||
*/
|
||||
delete(key, nestedKey) {
|
||||
if (!this.store.has(key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.store.get(key).delete(nestedKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the store.
|
||||
*/
|
||||
clear() {
|
||||
this.store = new WeakMap();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WeakMapMap;
|
|
@ -72,7 +72,8 @@ function AutocompletePopup(toolboxDoc, options = {}) {
|
|||
this._listPadding = Number(listPadding);
|
||||
}
|
||||
|
||||
this._tooltip.setContent(this._list, { height: Infinity });
|
||||
this._tooltip.panel.appendChild(this._list);
|
||||
this._tooltip.setContentSize({ height: Infinity });
|
||||
|
||||
this.onClick = this.onClick.bind(this);
|
||||
this._list.addEventListener("click", this.onClick);
|
||||
|
|
|
@ -291,10 +291,6 @@ class MenuButton extends PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
// We bypass the call to HTMLTooltip. setContent and set the panel contents
|
||||
// directly here.
|
||||
//
|
||||
// Bug 1472942: Do this for all users of HTMLTooltip.
|
||||
const menu = ReactDOM.createPortal(
|
||||
typeof this.props.children === "function"
|
||||
? this.props.children()
|
||||
|
|
|
@ -53,6 +53,7 @@ DevToolsModules(
|
|||
'undo.js',
|
||||
'unicode-url.js',
|
||||
'view-source.js',
|
||||
'WeakMapMap.js',
|
||||
'webgl-utils.js',
|
||||
'zoom-keys.js',
|
||||
)
|
||||
|
|
|
@ -14,10 +14,13 @@ const Services = require("Services");
|
|||
const { TelemetryStopwatch } = require("devtools/client/shared/TelemetryStopwatch.jsm");
|
||||
const { getNthPathExcluding } = require("devtools/shared/platform/stack");
|
||||
const { TelemetryEnvironment } = require("resource://gre/modules/TelemetryEnvironment.jsm");
|
||||
const WeakMapMap = require("devtools/client/shared/WeakMapMap");
|
||||
|
||||
const CATEGORY = "devtools.main";
|
||||
|
||||
// Object to be shared among all instances.
|
||||
const PENDING_EVENTS = new Map();
|
||||
const PENDING_EVENT_PROPERTIES = new Map();
|
||||
const PENDING_EVENT_PROPERTIES = new WeakMapMap();
|
||||
const PENDING_EVENTS = new WeakMapMap();
|
||||
|
||||
class Telemetry {
|
||||
constructor() {
|
||||
|
@ -33,6 +36,7 @@ class Telemetry {
|
|||
this.setEventRecordingEnabled = this.setEventRecordingEnabled.bind(this);
|
||||
this.preparePendingEvent = this.preparePendingEvent.bind(this);
|
||||
this.addEventProperty = this.addEventProperty.bind(this);
|
||||
this.addEventProperties = this.addEventProperties.bind(this);
|
||||
this.toolOpened = this.toolOpened.bind(this);
|
||||
this.toolClosed = this.toolClosed.bind(this);
|
||||
}
|
||||
|
@ -77,11 +81,9 @@ class Telemetry {
|
|||
* @param {String} histogramId
|
||||
* A string which must be a valid histogram name.
|
||||
* @param {Object} obj
|
||||
* Optional parameter. If specified, the timer is associated with this
|
||||
* object, meaning that multiple timers for the same histogram may be
|
||||
* run concurrently, as long as they are associated with different
|
||||
* objects.
|
||||
*
|
||||
* The telemetry event or ping is associated with this object, meaning
|
||||
* that multiple events or pings for the same histogram may be run
|
||||
* concurrently, as long as they are associated with different objects.
|
||||
* @returns {Boolean}
|
||||
* True if the timer was successfully started, false otherwise. If a
|
||||
* timer already exists, it can't be started again, and the existing
|
||||
|
@ -103,10 +105,9 @@ class Telemetry {
|
|||
* @param {String} key
|
||||
* A string which must be a valid histgram key.
|
||||
* @param {Object} obj
|
||||
* Optional parameter. If specified, the timer is associated with this
|
||||
* object, meaning that multiple timers for the same histogram may be
|
||||
* run concurrently,as long as they are associated with different
|
||||
* objects.
|
||||
* The telemetry event or ping is associated with this object, meaning
|
||||
* that multiple events or pings for the same histogram may be run
|
||||
* concurrently, as long as they are associated with different objects.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* True if the timer was successfully started, false otherwise. If a
|
||||
|
@ -125,8 +126,9 @@ class Telemetry {
|
|||
* @param {String} histogramId
|
||||
* A string which must be a valid histogram name.
|
||||
* @param {Object} obj
|
||||
* Optional parameter which associates the histogram timer with the
|
||||
* given object.
|
||||
* The telemetry event or ping is associated with this object, meaning
|
||||
* that multiple events or pings for the same histogram may be run
|
||||
* concurrently, as long as they are associated with different objects.
|
||||
* @param {Boolean} canceledOkay
|
||||
* Optional parameter which will suppress any warnings that normally
|
||||
* fire when a stopwatch is finished after being canceled.
|
||||
|
@ -137,15 +139,7 @@ class Telemetry {
|
|||
* to the histogram, False otherwise.
|
||||
*/
|
||||
finish(histogramId, obj, canceledOkay) {
|
||||
// Avoid errors caused by the toolbox not being properly initialized.
|
||||
this.ignoreStopwatchErrors(true);
|
||||
|
||||
const result = TelemetryStopwatch.finish(histogramId, obj, canceledOkay);
|
||||
|
||||
// Watch for errors again.
|
||||
this.ignoreStopwatchErrors(false);
|
||||
|
||||
return result;
|
||||
return TelemetryStopwatch.finish(histogramId, obj, canceledOkay);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -158,8 +152,9 @@ class Telemetry {
|
|||
* @param {String} key
|
||||
* A string which must be a valid histogram key.
|
||||
* @param {Object} obj
|
||||
* Optional parameter which associates the histogram timer with the
|
||||
* given object.
|
||||
* The telemetry event or ping is associated with this object, meaning
|
||||
* that multiple events or pings for the same histogram may be run
|
||||
* concurrently, as long as they are associated with different objects.
|
||||
* @param {Boolean} canceledOkay
|
||||
* Optional parameter which will suppress any warnings that normally
|
||||
* fire when a stopwatch is finished after being canceled.
|
||||
|
@ -170,28 +165,7 @@ class Telemetry {
|
|||
* to the histogram, False otherwise.
|
||||
*/
|
||||
finishKeyed(histogramId, key, obj, canceledOkay) {
|
||||
// Avoid errors caused by the toolbox not being properly initialized.
|
||||
this.ignoreStopwatchErrors(true);
|
||||
|
||||
const result = TelemetryStopwatch.finishKeyed(histogramId, key, obj, canceledOkay);
|
||||
|
||||
// Watch for errors again.
|
||||
this.ignoreStopwatchErrors(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a flag to ignore TelemetryStopwatch errors.
|
||||
*
|
||||
* @param {Boolean} testing
|
||||
* Flag to select whether to ignore TelemetryStopwatch errors.
|
||||
*/
|
||||
ignoreStopwatchErrors(testing) {
|
||||
// FIXME: https://bugzil.la/1491879 - Fix telemetry support for multiple
|
||||
// tabs / windows. This method should be removed as it is hiding
|
||||
// a problem with using telemetry in multiple tabs / windows.
|
||||
TelemetryStopwatch.setTestModeEnabled(testing);
|
||||
return TelemetryStopwatch.finishKeyed(histogramId, key, obj, canceledOkay);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -366,16 +340,14 @@ class Telemetry {
|
|||
}
|
||||
|
||||
/**
|
||||
* Event telemetry is disabled by default. Use this method to enable it for
|
||||
* a particular category.
|
||||
* Event telemetry is disabled by default. Use this method to enable or
|
||||
* disable it.
|
||||
*
|
||||
* @param {String} category
|
||||
* The telemetry event category e.g. "devtools.main"
|
||||
* @param {Boolean} enabled
|
||||
* Enabled: true or false.
|
||||
*/
|
||||
setEventRecordingEnabled(category, enabled) {
|
||||
return Services.telemetry.setEventRecordingEnabled(category, enabled);
|
||||
setEventRecordingEnabled(enabled) {
|
||||
return Services.telemetry.setEventRecordingEnabled(CATEGORY, enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -388,9 +360,10 @@ class Telemetry {
|
|||
* properties have been received. Once they have all been received we send the
|
||||
* telemetry event.
|
||||
*
|
||||
* @param {String} category
|
||||
* The telemetry event category (a group name for events and helps to
|
||||
* avoid name conflicts) e.g. "devtools.main"
|
||||
* @param {Object} obj
|
||||
* The telemetry event or ping is associated with this object, meaning
|
||||
* that multiple events or pings for the same histogram may be run
|
||||
* concurrently, as long as they are associated with different objects.
|
||||
* @param {String} method
|
||||
* The telemetry event method (describes the type of event that
|
||||
* occurred e.g. "open")
|
||||
|
@ -408,8 +381,8 @@ class Telemetry {
|
|||
* "width"
|
||||
* ]
|
||||
*/
|
||||
preparePendingEvent(category, method, object, value, expected = []) {
|
||||
const sig = `${category},${method},${object},${value}`;
|
||||
preparePendingEvent(obj, method, object, value, expected = []) {
|
||||
const sig = `${method},${object},${value}`;
|
||||
|
||||
if (expected.length === 0) {
|
||||
throw new Error(`preparePendingEvent() was called without any expected ` +
|
||||
|
@ -417,17 +390,19 @@ class Telemetry {
|
|||
`CALLER: ${getCaller()}`);
|
||||
}
|
||||
|
||||
PENDING_EVENTS.set(sig, {
|
||||
const data = {
|
||||
extra: {},
|
||||
expected: new Set(expected)
|
||||
});
|
||||
};
|
||||
|
||||
const props = PENDING_EVENT_PROPERTIES.get(sig);
|
||||
PENDING_EVENTS.set(obj, sig, data);
|
||||
|
||||
const props = PENDING_EVENT_PROPERTIES.get(obj, sig);
|
||||
if (props) {
|
||||
for (const [name, val] of Object.entries(props)) {
|
||||
this.addEventProperty(category, method, object, value, name, val);
|
||||
this.addEventProperty(obj, method, object, value, name, val);
|
||||
}
|
||||
PENDING_EVENT_PROPERTIES.delete(sig);
|
||||
PENDING_EVENT_PROPERTIES.delete(obj, sig);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -436,9 +411,10 @@ class Telemetry {
|
|||
* This means that if preparePendingEvent() is called before or after sending
|
||||
* the event properties they will automatically added to the event.
|
||||
*
|
||||
* @param {String} category
|
||||
* The telemetry event category (a group name for events and helps to
|
||||
* avoid name conflicts) e.g. "devtools.main"
|
||||
* @param {Object} obj
|
||||
* The telemetry event or ping is associated with this object, meaning
|
||||
* that multiple events or pings for the same histogram may be run
|
||||
* concurrently, as long as they are associated with different objects.
|
||||
* @param {String} method
|
||||
* The telemetry event method (describes the type of event that
|
||||
* occurred e.g. "open")
|
||||
|
@ -453,31 +429,32 @@ class Telemetry {
|
|||
* @param {String} pendingPropValue
|
||||
* The pending property value
|
||||
*/
|
||||
addEventProperty(category, method, object, value, pendingPropName, pendingPropValue) {
|
||||
const sig = `${category},${method},${object},${value}`;
|
||||
addEventProperty(obj, method, object, value, pendingPropName, pendingPropValue) {
|
||||
const sig = `${method},${object},${value}`;
|
||||
const events = PENDING_EVENTS.get(obj, sig);
|
||||
|
||||
// If the pending event has not been created add the property to the pending
|
||||
// list.
|
||||
if (!PENDING_EVENTS.has(sig)) {
|
||||
const props = PENDING_EVENT_PROPERTIES.get(sig);
|
||||
if (!events) {
|
||||
const props = PENDING_EVENT_PROPERTIES.get(obj, sig);
|
||||
|
||||
if (props) {
|
||||
props[pendingPropName] = pendingPropValue;
|
||||
} else {
|
||||
PENDING_EVENT_PROPERTIES.set(sig, {
|
||||
PENDING_EVENT_PROPERTIES.set(obj, sig, {
|
||||
[pendingPropName]: pendingPropValue
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const { expected, extra } = PENDING_EVENTS.get(sig);
|
||||
const { expected, extra } = events;
|
||||
|
||||
if (expected.has(pendingPropName)) {
|
||||
extra[pendingPropName] = pendingPropValue;
|
||||
|
||||
if (expected.size === Object.keys(extra).length) {
|
||||
this._sendPendingEvent(category, method, object, value);
|
||||
this._sendPendingEvent(obj, method, object, value);
|
||||
}
|
||||
} else {
|
||||
// The property was not expected, warn and bail.
|
||||
|
@ -493,9 +470,10 @@ class Telemetry {
|
|||
* This means that if preparePendingEvent() is called before or after sending
|
||||
* the event properties they will automatically added to the event.
|
||||
*
|
||||
* @param {String} category
|
||||
* The telemetry event category (a group name for events and helps to
|
||||
* avoid name conflicts) e.g. "devtools.main"
|
||||
* @param {Object} obj
|
||||
* The telemetry event or ping is associated with this object, meaning
|
||||
* that multiple events or pings for the same histogram may be run
|
||||
* concurrently, as long as they are associated with different objects.
|
||||
* @param {String} method
|
||||
* The telemetry event method (describes the type of event that
|
||||
* occurred e.g. "open")
|
||||
|
@ -509,9 +487,9 @@ class Telemetry {
|
|||
* An object containing key, value pairs that should be added to the
|
||||
* event as properties.
|
||||
*/
|
||||
addEventProperties(category, method, object, value, pendingObject) {
|
||||
addEventProperties(obj, method, object, value, pendingObject) {
|
||||
for (const [key, val] of Object.entries(pendingObject)) {
|
||||
this.addEventProperty(category, method, object, value, key, val);
|
||||
this.addEventProperty(obj, method, object, value, key, val);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -520,9 +498,10 @@ class Telemetry {
|
|||
* prepare a pending telemetry event for sending and then send it via
|
||||
* recordEvent().
|
||||
*
|
||||
* @param {String} category
|
||||
* The telemetry event category (a group name for events and helps to
|
||||
* avoid name conflicts) e.g. "devtools.main"
|
||||
* @param {Object} obj
|
||||
* The telemetry event or ping is associated with this object, meaning
|
||||
* that multiple events or pings for the same histogram may be run
|
||||
* concurrently, as long as they are associated with different objects.
|
||||
* @param {String} method
|
||||
* The telemetry event method (describes the type of event that
|
||||
* occurred e.g. "open")
|
||||
|
@ -533,21 +512,18 @@ class Telemetry {
|
|||
* The telemetry event value (a user defined value, providing context
|
||||
* for the event) e.g. "console"
|
||||
*/
|
||||
_sendPendingEvent(category, method, object, value) {
|
||||
const sig = `${category},${method},${object},${value}`;
|
||||
const { extra } = PENDING_EVENTS.get(sig);
|
||||
_sendPendingEvent(obj, method, object, value) {
|
||||
const sig = `${method},${object},${value}`;
|
||||
const { extra } = PENDING_EVENTS.get(obj, sig);
|
||||
|
||||
PENDING_EVENTS.delete(sig);
|
||||
PENDING_EVENT_PROPERTIES.delete(sig);
|
||||
this.recordEvent(category, method, object, value, extra);
|
||||
PENDING_EVENTS.delete(obj, sig);
|
||||
PENDING_EVENT_PROPERTIES.delete(obj, sig);
|
||||
this.recordEvent(method, object, value, extra);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a telemetry event.
|
||||
*
|
||||
* @param {String} category
|
||||
* The telemetry event category (a group name for events and helps to
|
||||
* avoid name conflicts) e.g. "devtools.main"
|
||||
* @param {String} method
|
||||
* The telemetry event method (describes the type of event that
|
||||
* occurred e.g. "open")
|
||||
|
@ -565,7 +541,7 @@ class Telemetry {
|
|||
* width: "1024"
|
||||
* }
|
||||
*/
|
||||
recordEvent(category, method, object, value = null, extra = null) {
|
||||
recordEvent(method, object, value = null, extra = null) {
|
||||
// Only string values are allowed so cast all values to strings.
|
||||
if (extra) {
|
||||
for (let [name, val] of Object.entries(extra)) {
|
||||
|
@ -573,7 +549,7 @@ class Telemetry {
|
|||
extra[name] = val;
|
||||
|
||||
if (val.length > 80) {
|
||||
const sig = `${category},${method},${object},${value}`;
|
||||
const sig = `${method},${object},${value}`;
|
||||
|
||||
throw new Error(`The property "${name}" was added to a telemetry ` +
|
||||
`event with the signature ${sig} but it's value ` +
|
||||
|
@ -583,7 +559,7 @@ class Telemetry {
|
|||
}
|
||||
}
|
||||
}
|
||||
Services.telemetry.recordEvent(category, method, object, value, extra);
|
||||
Services.telemetry.recordEvent(CATEGORY, method, object, value, extra);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -591,12 +567,23 @@ class Telemetry {
|
|||
*
|
||||
* @param {String} id
|
||||
* The ID of the tool opened.
|
||||
* @param {String} sessionId
|
||||
* Toolbox session id used when we need to ensure a tool really has a
|
||||
* timer before calculating a delta.
|
||||
* @param {Object} obj
|
||||
* The telemetry event or ping is associated with this object, meaning
|
||||
* that multiple events or pings for the same histogram may be run
|
||||
* concurrently, as long as they are associated with different objects.
|
||||
*
|
||||
* NOTE: This method is designed for tools that send multiple probes on open,
|
||||
* one of those probes being a counter and the other a timer. If you
|
||||
* only have one probe you should be using another method.
|
||||
*/
|
||||
toolOpened(id) {
|
||||
toolOpened(id, sessionId, obj) {
|
||||
if (typeof sessionId === "undefined") {
|
||||
throw new Error(`toolOpened called without a sessionId parameter.`);
|
||||
}
|
||||
|
||||
const charts = getChartsFromToolId(id);
|
||||
|
||||
if (!charts) {
|
||||
|
@ -604,16 +591,16 @@ class Telemetry {
|
|||
}
|
||||
|
||||
if (charts.useTimedEvent) {
|
||||
this.preparePendingEvent("devtools.main", "tool_timer", id, null, [
|
||||
this.preparePendingEvent(obj, "tool_timer", id, null, [
|
||||
"os",
|
||||
"time_open",
|
||||
"session_id"
|
||||
]);
|
||||
this.addEventProperty("devtools.main", "tool_timer", id, null,
|
||||
this.addEventProperty(obj, "tool_timer", id, null,
|
||||
"time_open", this.msSystemNow());
|
||||
}
|
||||
if (charts.timerHist) {
|
||||
this.start(charts.timerHist, this);
|
||||
this.start(charts.timerHist, obj);
|
||||
}
|
||||
if (charts.countHist) {
|
||||
this.getHistogramById(charts.countHist).add(true);
|
||||
|
@ -629,14 +616,21 @@ class Telemetry {
|
|||
* @param {String} id
|
||||
* The ID of the tool opened.
|
||||
* @param {String} sessionId
|
||||
* Optional toolbox session id used only when a tool's chart has a
|
||||
* useTimedEvent property set to true.
|
||||
* Toolbox session id.
|
||||
* @param {Object} obj
|
||||
* The telemetry event or ping is associated with this object, meaning
|
||||
* that multiple events or pings for the same histogram may be run
|
||||
* concurrently, as long as they are associated with different objects.
|
||||
*
|
||||
* NOTE: This method is designed for tools that send multiple probes on open,
|
||||
* one of those probes being a counter and the other a timer. If you
|
||||
* only have one probe you should be using another method.
|
||||
*/
|
||||
toolClosed(id, sessionId) {
|
||||
toolClosed(id, sessionId, obj) {
|
||||
if (typeof sessionId === "undefined") {
|
||||
throw new Error(`toolClosed called without a sessionId parameter.`);
|
||||
}
|
||||
|
||||
const charts = getChartsFromToolId(id);
|
||||
|
||||
if (!charts) {
|
||||
|
@ -644,18 +638,11 @@ class Telemetry {
|
|||
}
|
||||
|
||||
if (charts.useTimedEvent) {
|
||||
const sig = `devtools.main,tool_timer,${id},null`;
|
||||
const event = PENDING_EVENTS.get(sig);
|
||||
|
||||
if (!event) {
|
||||
// FIXME: https://bugzil.la/1491879 - Fix telemetry support for multiple
|
||||
// tabs / windows. For the moment we need to bail out.
|
||||
return;
|
||||
}
|
||||
|
||||
const sig = `tool_timer,${id},null`;
|
||||
const event = PENDING_EVENTS.get(obj, sig);
|
||||
const time = this.msSystemNow() - event.extra.time_open;
|
||||
|
||||
this.addEventProperties("devtools.main", "tool_timer", id, null, {
|
||||
this.addEventProperties(obj, "tool_timer", id, null, {
|
||||
"time_open": time,
|
||||
"os": this.osNameAndVersion,
|
||||
"session_id": sessionId
|
||||
|
@ -663,7 +650,7 @@ class Telemetry {
|
|||
}
|
||||
|
||||
if (charts.timerHist) {
|
||||
this.finish(charts.timerHist, this);
|
||||
this.finish(charts.timerHist, obj, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,8 @@ async function runTests(doc) {
|
|||
const tooltip = new HTMLTooltip(doc, {useXulWrapper});
|
||||
|
||||
info("Set tooltip content");
|
||||
tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
|
||||
tooltip.panel.appendChild(getTooltipContent(doc));
|
||||
tooltip.setContentSize({width: 100, height: 50});
|
||||
|
||||
is(tooltip.isVisible(), false, "Tooltip is not visible");
|
||||
|
||||
|
|
|
@ -41,7 +41,8 @@ async function testClickInTooltipContent(doc) {
|
|||
info("Test a tooltip is not closed when clicking inside itself");
|
||||
|
||||
const tooltip = new HTMLTooltip(doc, {useXulWrapper});
|
||||
tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
|
||||
tooltip.panel.appendChild(getTooltipContent(doc));
|
||||
tooltip.setContentSize({width: 100, height: 50});
|
||||
await showTooltip(tooltip, doc.getElementById("box1"));
|
||||
|
||||
const onTooltipContainerClick = once(tooltip.container, "click");
|
||||
|
@ -57,7 +58,8 @@ async function testConsumeOutsideClicksFalse(doc) {
|
|||
const box4 = doc.getElementById("box4");
|
||||
|
||||
const tooltip = new HTMLTooltip(doc, {consumeOutsideClicks: false, useXulWrapper});
|
||||
tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
|
||||
tooltip.panel.appendChild(getTooltipContent(doc));
|
||||
tooltip.setContentSize({width: 100, height: 50});
|
||||
await showTooltip(tooltip, doc.getElementById("box1"));
|
||||
|
||||
const onBox4Clicked = once(box4, "click");
|
||||
|
@ -80,7 +82,8 @@ async function testConsumeOutsideClicksTrue(doc) {
|
|||
box4.addEventListener("click", () => box4clicks++);
|
||||
|
||||
const tooltip = new HTMLTooltip(doc, {consumeOutsideClicks: true, useXulWrapper});
|
||||
tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
|
||||
tooltip.panel.appendChild(getTooltipContent(doc));
|
||||
tooltip.setContentSize({width: 100, height: 50});
|
||||
await showTooltip(tooltip, doc.getElementById("box1"));
|
||||
|
||||
const onHidden = once(tooltip, "hidden");
|
||||
|
@ -98,7 +101,8 @@ async function testConsumeWithRightClick(doc) {
|
|||
const box4 = doc.getElementById("box4");
|
||||
|
||||
const tooltip = new HTMLTooltip(doc, {consumeOutsideClicks: true, useXulWrapper});
|
||||
tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
|
||||
tooltip.panel.appendChild(getTooltipContent(doc));
|
||||
tooltip.setContentSize({width: 100, height: 50});
|
||||
await showTooltip(tooltip, doc.getElementById("box1"));
|
||||
|
||||
// Only left-click events should be consumed, so we expect to catch a click when using
|
||||
|
@ -120,7 +124,8 @@ async function testClickInOuterIframe(doc) {
|
|||
const frame = doc.getElementById("frame");
|
||||
|
||||
const tooltip = new HTMLTooltip(doc, {useXulWrapper});
|
||||
tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
|
||||
tooltip.panel.appendChild(getTooltipContent(doc));
|
||||
tooltip.setContentSize({width: 100, height: 50});
|
||||
await showTooltip(tooltip, doc.getElementById("box1"));
|
||||
|
||||
const onHidden = once(tooltip, "hidden");
|
||||
|
@ -139,7 +144,8 @@ async function testClickInInnerIframe(doc) {
|
|||
const iframe = doc.createElementNS(HTML_NS, "iframe");
|
||||
iframe.style.width = "100px";
|
||||
iframe.style.height = "50px";
|
||||
tooltip.setContent(iframe, {width: 100, height: 50});
|
||||
tooltip.panel.appendChild(iframe);
|
||||
tooltip.setContentSize({width: 100, height: 50});
|
||||
await showTooltip(tooltip, doc.getElementById("box1"));
|
||||
|
||||
const onTooltipContainerClick = once(tooltip.container, "click");
|
||||
|
|
|
@ -85,6 +85,7 @@ function createTooltip(doc) {
|
|||
input.setAttribute("type", "text");
|
||||
div.appendChild(input);
|
||||
|
||||
tooltip.setContent(div, {width: 150, height: 50});
|
||||
tooltip.panel.appendChild(div);
|
||||
tooltip.setContentSize({width: 150, height: 50});
|
||||
return tooltip;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,8 @@ add_task(async function() {
|
|||
const tooltip = new HTMLTooltip(doc, {useXulWrapper: false});
|
||||
const div = doc.createElementNS(HTML_NS, "div");
|
||||
div.style.height = "100%";
|
||||
tooltip.setContent(div, {width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT});
|
||||
tooltip.panel.appendChild(div);
|
||||
tooltip.setContentSize({width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT});
|
||||
|
||||
const box1 = doc.getElementById("box1");
|
||||
const box2 = doc.getElementById("box2");
|
||||
|
|
|
@ -28,7 +28,8 @@ add_task(async function() {
|
|||
const tooltip = new HTMLTooltip(doc, {useXulWrapper: false});
|
||||
const div = doc.createElementNS(HTML_NS, "div");
|
||||
div.style.height = "100%";
|
||||
tooltip.setContent(div, {width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT});
|
||||
tooltip.panel.appendChild(div);
|
||||
tooltip.setContentSize({width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT});
|
||||
|
||||
const box1 = doc.getElementById("box1");
|
||||
const box2 = doc.getElementById("box2");
|
||||
|
|
|
@ -38,7 +38,8 @@ async function runTests(doc) {
|
|||
const tooltip = new HTMLTooltip(doc, {type: "arrow", useXulWrapper});
|
||||
const div = doc.createElementNS(HTML_NS, "div");
|
||||
div.style.height = "35px";
|
||||
tooltip.setContent(div, {width: 200, height: 35});
|
||||
tooltip.panel.appendChild(div);
|
||||
tooltip.setContentSize({width: 200, height: 35});
|
||||
|
||||
const {right: docRight} = doc.documentElement.getBoundingClientRect();
|
||||
|
||||
|
|
|
@ -37,7 +37,8 @@ async function runTests(doc) {
|
|||
const tooltip = new HTMLTooltip(doc, {type: "arrow", useXulWrapper});
|
||||
const div = doc.createElementNS(HTML_NS, "div");
|
||||
div.style.height = "35px";
|
||||
tooltip.setContent(div, {width: 200, height: 35});
|
||||
tooltip.panel.appendChild(div);
|
||||
tooltip.setContentSize({width: 200, height: 35});
|
||||
|
||||
const {right: docRight} = doc.documentElement.getBoundingClientRect();
|
||||
|
||||
|
|
|
@ -33,7 +33,8 @@ add_task(async function() {
|
|||
const width = 100, height = 50;
|
||||
|
||||
const tooltip = new HTMLTooltip(doc, {useXulWrapper: false});
|
||||
tooltip.setContent(getTooltipContent(doc), {width, height});
|
||||
tooltip.panel.appendChild(getTooltipContent(doc));
|
||||
tooltip.setContentSize({width, height});
|
||||
|
||||
info("Show the tooltip on each of the 4 hbox, without calling hide in between");
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ async function runTests(doc) {
|
|||
const div = doc.createElementNS(HTML_NS, "div");
|
||||
div.style.width = "200px";
|
||||
div.style.height = "35px";
|
||||
tooltip.setContent(div);
|
||||
tooltip.panel.appendChild(div);
|
||||
|
||||
const docBounds = doc.documentElement.getBoundingClientRect();
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ async function runTests(doc) {
|
|||
const div = doc.createElementNS(HTML_NS, "div");
|
||||
div.style.width = "200px";
|
||||
div.style.height = "35px";
|
||||
tooltip.setContent(div);
|
||||
tooltip.panel.appendChild(div);
|
||||
|
||||
const elements = [...doc.querySelectorAll(".anchor")];
|
||||
for (const el of elements) {
|
||||
|
|
|
@ -39,7 +39,7 @@ async function runTests(doc) {
|
|||
"width: 300px; height: 150px; background: red;";
|
||||
|
||||
info("Set tooltip content using width:auto and height:auto");
|
||||
tooltip.setContent(tooltipContent);
|
||||
tooltip.panel.appendChild(tooltipContent);
|
||||
|
||||
info("Show the tooltip and check the tooltip panel dimensions.");
|
||||
await showTooltip(tooltip, doc.getElementById("box1"));
|
||||
|
@ -53,7 +53,7 @@ async function runTests(doc) {
|
|||
info("Set tooltip content using fixed width and height:auto");
|
||||
tooltipContent.style.cssText =
|
||||
"width: auto; height: 200px; background: red;";
|
||||
tooltip.setContent(tooltipContent, { width: 400 });
|
||||
tooltip.setContentSize({ width: 400 });
|
||||
|
||||
info("Show the tooltip and check the tooltip panel height.");
|
||||
await showTooltip(tooltip, doc.getElementById("box1"));
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче