Merge autoland to mozilla-central. a=merge

This commit is contained in:
Noemi Erli 2022-03-02 06:30:37 +02:00
commit b12fccca4a
408 changed files with 18205 additions and 16427 deletions

View File

@ -454,7 +454,9 @@ DocAccessible* DocManager::CreateDocOrRootAccessible(Document* aDocument) {
return nullptr;
}
// Ignore documents without presshell and not having root frame.
// Ignore documents without presshell. We must not ignore documents with no
// root frame because DOM focus can hit such documents and ignoring them would
// prevent a11y focus.
PresShell* presShell = aDocument->GetPresShell();
if (!presShell || presShell->IsDestroying()) {
return nullptr;

View File

@ -74,8 +74,9 @@ void TextAttrsMgr::GetAttributes(AccAttributes* aAttributes,
}
nsIFrame* rootFrame = mHyperTextAcc->GetFrame();
MOZ_ASSERT(rootFrame, "No frame for accessible!");
if (!rootFrame) return;
if (!rootFrame) {
return;
}
nsIContent *offsetNode = nullptr, *offsetElm = nullptr;
nsIFrame* frame = nullptr;

View File

@ -35,6 +35,15 @@ Note that scenario 1 or 2 could happen for a child document as well.
Scenario 3 would then apply for any child documents encountered while building the accessibility trees for these top level DocAccessibles.
### Scenario 5: Document gets focus before layout begins
It is possible for a document to get focus before layout has begun and before DOM loading is complete.
In that case, there will be a PresShell, but it will have no root frame.
Despite this, it is necessary to create the document because otherwise, a11y focus would go nowhere while the document had DOM focus.
1. [a11y::FocusManager gets notified of a DOM focus change (FocusManager::NotifyOfDOMFocus)](https://searchfox.org/mozilla-central/rev/04dbb1a865894aec20eb02585aa75acccc0b72d5/accessible/base/FocusManager.cpp#126).
2. It gets the DocAccessible for the child document (DocManager::GetDocAccessible).
3. Because it doesn't exist yet, [the DocAccessible gets created (DocManager::CreateDocOrRootAccessible)](https://searchfox.org/mozilla-central/rev/4e87b5392eafe1f1d49017e76f7317b06ec0b1d8/accessible/base/DocManager.cpp#62).
## 2. Initial tree creation
1. When a DocAccessible is created, it [creates a refresh observer (NotificationController)](https://searchfox.org/mozilla-central/rev/36f79bed679ad7ec46f7cd05868a8f6dc823e1be/accessible/generic/DocAccessible.cpp#368) which performs various processing asynchronously.
2. When the NotificationController is created, it [schedules processing for the next possible refresh tick](https://searchfox.org/mozilla-central/rev/36f79bed679ad7ec46f7cd05868a8f6dc823e1be/accessible/base/NotificationController.cpp#39).
@ -42,6 +51,9 @@ Scenario 3 would then apply for any child documents encountered while building t
4. For a top level document, the [DocAccessibleChild IPC actor is created](https://searchfox.org/mozilla-central/rev/36f79bed679ad7ec46f7cd05868a8f6dc823e1be/accessible/generic/DocAccessible.cpp#1752). See the section on IPC actor creation below.
5. The [DOM tree is walked and the accessibility tree is built for the document down (DocAccessible::CacheChildrenInSubtree)](https://searchfox.org/mozilla-central/rev/36f79bed679ad7ec46f7cd05868a8f6dc823e1be/accessible/generic/DocAccessible.cpp#1789).
Note that the document might still have no layout frame if the PresShell still has no frame; see scenario 5 in DocAccessible creation above.
Nevertheless, DoInitialUpdate must be called because otherwise, we wouldn't create the IPC actor, which would in turn mean remote documents in this state couldn't get a11y focus.
## 3. Child document binding
Child document here refers to a child document in the same process; e.g. an in-process iframe or a parent process document such as an about: page.

View File

@ -103,6 +103,9 @@ class LocalAccessible : public nsISupports, public Accessible {
/**
* Return frame for this accessible.
* Note that this will return null for display: contents. Also,
* DocAccessible::GetFrame can return null if the frame tree hasn't been
* created yet.
*/
virtual nsIFrame* GetFrame() const;

View File

@ -511,6 +511,9 @@ pref("browser.urlbar.merino.clientVariants", "");
// Whether the best match feature in the urlbar is enabled.
pref("browser.urlbar.bestMatch.enabled", false);
// Whether best match results can be blocked.
pref("browser.urlbar.bestMatch.blockingEnabled", false);
pref("browser.altClickSave", false);
// Enable logging downloads operations to the Console.

View File

@ -399,6 +399,12 @@ var SidebarUI = {
* @return {Promise}
*/
toggle(commandID = this.lastOpenedId, triggerNode) {
if (
CustomizationHandler.isCustomizing() ||
CustomizationHandler.isExitingCustomizeMode
) {
return Promise.resolve();
}
// First priority for a default value is this.lastOpenedId which is set during show()
// and not reset in hide(), unlike currentID. If show() hasn't been called and we don't
// have a persisted command either, or the command doesn't exist anymore, then

View File

@ -22,3 +22,87 @@ add_task(async function test_sidebar_keys() {
let options = { accelKey: true, shiftKey: AppConstants.platform == "macosx" };
await testSidebarKeyToggle("h", options, "viewHistorySidebar");
});
add_task(async function test_sidebar_in_customize_mode() {
// Test bug 1756385 - widgets to appear unchecked in customize mode. Test that
// the sidebar button widget doesn't appear checked, and that the sidebar
// button toggle is inert while in customize mode.
let { CustomizableUI } = ChromeUtils.import(
"resource:///modules/CustomizableUI.jsm"
);
registerCleanupFunction(() => SidebarUI.hide());
let placement = CustomizableUI.getPlacementOfWidget("sidebar-button");
if (!(placement?.area == CustomizableUI.AREA_NAVBAR)) {
CustomizableUI.addWidgetToArea(
"sidebar-button",
CustomizableUI.AREA_NAVBAR,
0
);
CustomizableUI.ensureWidgetPlacedInWindow("sidebar-button", window);
registerCleanupFunction(function() {
CustomizableUI.removeWidgetFromArea("sidebar-button");
});
}
let widgetIcon = CustomizableUI.getWidget("sidebar-button")
.forWindow(window)
.node?.querySelector(".toolbarbutton-icon");
// Get the alpha value of the sidebar toggle widget's background
let getBGAlpha = () =>
InspectorUtils.colorToRGBA(
getComputedStyle(widgetIcon).getPropertyValue("background-color")
).a;
let promiseShown = BrowserTestUtils.waitForEvent(window, "SidebarShown");
SidebarUI.show("viewBookmarksSidebar");
await promiseShown;
Assert.greater(
getBGAlpha(),
0,
"Sidebar widget background should appear checked"
);
// Enter customize mode. This should disable the toggle and make the sidebar
// toggle widget appear unchecked.
let customizationReadyPromise = BrowserTestUtils.waitForEvent(
gNavToolbox,
"customizationready"
);
gCustomizeMode.enter();
await customizationReadyPromise;
Assert.equal(
getBGAlpha(),
0,
"Sidebar widget background should appear unchecked"
);
// Attempt toggle - should fail in customize mode.
await SidebarUI.toggle();
ok(SidebarUI.isOpen, "Sidebar is still open");
// Exit customize mode. This should re-enable the toggle and make the sidebar
// toggle widget appear checked again, since toggle() didn't hide the sidebar.
let afterCustomizationPromise = BrowserTestUtils.waitForEvent(
gNavToolbox,
"aftercustomization"
);
gCustomizeMode.exit();
await afterCustomizationPromise;
Assert.greater(
getBGAlpha(),
0,
"Sidebar widget background should appear checked again"
);
await SidebarUI.toggle();
ok(!SidebarUI.isOpen, "Sidebar is closed");
Assert.equal(
getBGAlpha(),
0,
"Sidebar widget background should appear unchecked"
);
});

View File

@ -26,18 +26,11 @@ XPCOMUtils.defineLazyModuleGetters(this, {
ShellService: "resource:///modules/ShellService.jsm",
UpdatePing: "resource://gre/modules/UpdatePing.jsm",
});
XPCOMUtils.defineLazyServiceGetter(
this,
"WindowsUIUtils",
"@mozilla.org/windows-ui-utils;1",
"nsIWindowsUIUtils"
);
XPCOMUtils.defineLazyServiceGetter(
this,
"UpdateManager",
"@mozilla.org/updates/update-manager;1",
"nsIUpdateManager"
);
XPCOMUtils.defineLazyServiceGetters(this, {
UpdateManager: ["@mozilla.org/updates/update-manager;1", "nsIUpdateManager"],
WinTaskbar: ["@mozilla.org/windows-taskbar;1", "nsIWinTaskbar"],
WindowsUIUtils: ["@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils"],
});
XPCOMUtils.defineLazyGetter(this, "gSystemPrincipal", () =>
Services.scriptSecurityManager.getSystemPrincipal()
@ -48,6 +41,11 @@ XPCOMUtils.defineLazyGlobalGetters(this, [URL]);
const ONCE_DOMAINS = ["mozilla.org", "firefox.com"];
const ONCE_PREF = "browser.startup.homepage_override.once";
// Index of Private Browsing icon in firefox.exe
// Must line up with the one in nsNativeAppSupportWin.h.
const PRIVATE_BROWSING_ICON_INDEX = 5;
const PRIVACY_SEGMENTATION_PREF = "browser.privacySegmentation.enabled";
function shouldLoadURI(aURI) {
if (aURI && !aURI.schemeIs("chrome")) {
return true;
@ -276,6 +274,20 @@ function openBrowserWindow(
win.docShell.QueryInterface(
Ci.nsILoadContext
).usePrivateBrowsing = true;
if (Services.prefs.getBoolPref(PRIVACY_SEGMENTATION_PREF)) {
// TODO: Changing this after the Window has been painted causes it to
// change Taskbar icons if the original one had a different AUMID.
// This must stay pref'ed off until this is resolved.
// https://bugzilla.mozilla.org/show_bug.cgi?id=1751010
WinTaskbar.setGroupIdForWindow(win, WinTaskbar.defaultPrivateGroupId);
WindowsUIUtils.setWindowIconFromExe(
win,
Services.dirsvc.get("XREExeF", Ci.nsIFile).path,
// This corresponds to the definitions in
// nsNativeAppSupportWin.h
PRIVATE_BROWSING_ICON_INDEX
);
}
}
let openTime = win.openTime;

View File

@ -29,33 +29,6 @@
* dispatches the commands that apply to individual items.
*/
/**
* A few words on focus and focusrings
*
* We do quite a few hacks in the Downloads Panel for focusrings. In fact, we
* basically suppress most if not all XUL-level focusrings, and style/draw
* them ourselves (using :focus instead of -moz-focusring). There are a few
* reasons for this:
*
* 1) Richlists on OSX don't have focusrings; instead, they are shown as
* selected. This makes for some ambiguity when we have a focused/selected
* item in the list, and the mouse is hovering a completed download (which
* highlights).
* 2) Windows doesn't show focusrings until after the first time that tab is
* pressed (and by then you're focusing the second item in the panel).
* 3) Richlistbox sets -moz-focusring even when we select it with a mouse.
*
* In general, the desired behaviour is to focus the first item after pressing
* tab/down, and show that focus with a ring. Then, if the mouse moves over
* the panel, to hide that focus ring; essentially resetting us to the state
* before pressing the key.
*
* We end up capturing the tab/down key events, and preventing their default
* behaviour. We then set a "keyfocus" attribute on the panel, which allows
* us to draw a ring around the currently focused element. If the panel is
* closed or the mouse moves over the panel, we remove the attribute.
*/
"use strict";
var { XPCOMUtils } = ChromeUtils.import(
@ -249,6 +222,9 @@ var DownloadsPanel = {
Services.telemetry.scalarAdd("downloads.panel_shown", 1);
DownloadsCommon.log("Opening the downloads panel.");
this._openedManually = openedManually;
this._preventFocusRing = !openedManually || !isKeyPress;
if (this.isPanelShowing) {
DownloadsCommon.log("Panel is already showing - focusing instead.");
this._focusPanel();
@ -263,7 +239,7 @@ var DownloadsPanel = {
// called while another window is closing (like the window for selecting
// whether to save or open the file), and that would cause the panel to
// close immediately.
setTimeout(() => this._openPopupIfDataReady(openedManually, isKeyPress), 0);
setTimeout(() => this._openPopupIfDataReady(), 0);
DownloadsCommon.log("Waiting for the downloads panel to appear.");
this._state = this.kStateWaitingData;
@ -299,36 +275,17 @@ var DownloadsPanel = {
);
},
/**
* Returns whether the user has started keyboard navigation.
*/
get keyFocusing() {
return this.panel.hasAttribute("keyfocus");
},
/**
* Set to true if the user has started keyboard navigation, and we should be
* showing focusrings in the panel. Also adds a mousemove event handler to
* the panel which disables keyFocusing.
*/
set keyFocusing(aValue) {
if (aValue) {
this.panel.setAttribute("keyfocus", "true");
this.panel.addEventListener("mousemove", this);
} else {
this.panel.removeAttribute("keyfocus");
this.panel.removeEventListener("mousemove", this);
}
},
/**
* Handles the mousemove event for the panel, which disables focusring
* visualization.
*/
handleEvent(aEvent) {
switch (aEvent.type) {
case "mousemove":
this.keyFocusing = false;
if (this.panel.contains(document.activeElement)) {
// Let mouse movement remove focus rings and reset focus in the panel.
// This behavior is copied from PanelMultiView.
document.activeElement.blur();
DownloadsView.richListBox.removeAttribute("force-focus-visible");
this._preventFocusRing = true;
this._focusPanel();
}
break;
case "keydown":
this._onKeyDown(aEvent);
@ -395,9 +352,7 @@ var DownloadsPanel = {
this._delayTimeout = null;
}
// Removes the keyfocus attribute so that we stop handling keyboard
// navigation.
this.keyFocusing = false;
DownloadsView.richListBox.removeAttribute("force-focus-visible");
// Since at most one popup is open at any given time, we can set globally.
DownloadsCommon.getIndicatorData(
@ -438,6 +393,7 @@ var DownloadsPanel = {
// Handle keypress to be able to preventDefault() events before they reach
// the richlistbox, for keyboard navigation.
this.panel.addEventListener("keypress", this);
this.panel.addEventListener("mousemove", this);
DownloadsView.richListBox.addEventListener("focus", this);
DownloadsView.richListBox.addEventListener("select", this);
},
@ -449,6 +405,7 @@ var DownloadsPanel = {
_unattachEventListeners() {
this.panel.removeEventListener("keydown", this);
this.panel.removeEventListener("keypress", this);
this.panel.removeEventListener("mousemove", this);
DownloadsView.richListBox.removeEventListener("focus", this);
DownloadsView.richListBox.removeEventListener("select", this);
},
@ -475,20 +432,21 @@ var DownloadsPanel = {
this._handlePotentiallySpammyDownloadActivation(aEvent);
return;
}
// If the user has pressed the tab, up, or down cursor key, start keyboard
// navigation, thus enabling focusrings in the panel. Keyboard navigation
// is automatically disabled if the user moves the mouse on the panel, or
// if the panel is closed.
if (
(aEvent.keyCode == aEvent.DOM_VK_TAB ||
aEvent.keyCode == aEvent.DOM_VK_UP ||
aEvent.keyCode == aEvent.DOM_VK_DOWN) &&
!this.keyFocusing
) {
this.keyFocusing = true;
}
let richListBox = DownloadsView.richListBox;
// If the user has pressed the up or down cursor key, force-enable focus
// indicators for the richlistbox. :focus-visible doesn't work in this case
// because the the focused element may not change here if the richlistbox
// already had focus. The force-focus-visible attribute will be removed
// again if the user moves the mouse on the panel or if the panel is closed.
if (
aEvent.keyCode == aEvent.DOM_VK_UP ||
aEvent.keyCode == aEvent.DOM_VK_DOWN
) {
richListBox.setAttribute("force-focus-visible", "true");
}
// If the footer is focused and the downloads list has at least 1 element
// in it, focus the last element in the list when going up.
if (aEvent.keyCode == aEvent.DOM_VK_UP && richListBox.firstElementChild) {
@ -575,17 +533,15 @@ var DownloadsPanel = {
return;
}
let element = document.commandDispatcher.focusedElement;
while (element && element != this.panel) {
element = element.parentNode;
if (document.activeElement && this.panel.contains(document.activeElement)) {
return;
}
if (!element) {
if (DownloadsView.richListBox.itemCount > 0) {
DownloadsView.richListBox.selectedIndex = 0;
DownloadsView.richListBox.focus();
} else {
DownloadsFooter.focus();
}
let focusOptions = { preventFocusRing: !!this._preventFocusRing };
if (DownloadsView.richListBox.itemCount > 0) {
DownloadsView.richListBox.selectedIndex = 0;
DownloadsView.richListBox.focus(focusOptions);
} else {
DownloadsFooter.focus(focusOptions);
}
},
@ -636,7 +592,7 @@ var DownloadsPanel = {
/**
* Opens the downloads panel when data is ready to be displayed.
*/
_openPopupIfDataReady(openedManually, isKeyPress) {
_openPopupIfDataReady() {
// We don't want to open the popup if we already displayed it, or if we are
// still loading data.
if (this._state != this.kStateWaitingData || DownloadsView.loading) {
@ -675,11 +631,6 @@ var DownloadsPanel = {
DownloadsCommon.log("Opening downloads panel popup.");
if (isKeyPress) {
// If the panel was opened via a keypress, enable focus indicators.
this.keyFocusing = true;
}
// Delay displaying the panel because this function will sometimes be
// called while another window is closing (like the window for selecting
// whether to save or open the file), and that would cause the panel to
@ -698,7 +649,7 @@ var DownloadsPanel = {
this._state = this.kStateHidden;
});
if (!openedManually) {
if (!this._openedManually) {
this._delayPopupItems();
}
}, 0);
@ -1528,9 +1479,9 @@ var DownloadsSummary = {
/**
* Focuses the root element of the summary.
*/
focus() {
focus(focusOptions) {
if (this._summaryNode) {
this._summaryNode.focus();
this._summaryNode.focus(focusOptions);
}
},
@ -1624,11 +1575,11 @@ var DownloadsFooter = {
* is visible, focus it. If not, focus the "Show all downloads"
* button.
*/
focus() {
focus(focusOptions) {
if (this._showingSummary) {
DownloadsSummary.focus();
DownloadsSummary.focus(focusOptions);
} else {
DownloadsView.downloadsHistory.focus({ preventFocusRing: true });
DownloadsView.downloadsHistory.focus(focusOptions);
}
},

View File

@ -152,21 +152,9 @@ add_task(async function test_downloads_keynav() {
EventUtils.synthesizeMouseAtCenter(listbox.getItemAtIndex(1), {
type: "mousemove",
});
is(listbox.selectedIndex, 1, "downloads list selected index after mousemove");
is(listbox.selectedIndex, 0, "downloads list selected index after mousemove");
checkTabbing(listbox, 1);
EventUtils.synthesizeKey("VK_UP", {});
is(
document.activeElement,
listbox,
"downloads list is focused after up to index 0"
);
is(
listbox.selectedIndex,
0,
"downloads list selected index after up to index 0"
);
checkTabbing(listbox, 0);
EventUtils.synthesizeKey("VK_UP", {});
is(

View File

@ -35,6 +35,8 @@ XPCOMUtils.defineLazyPreferenceGetter(
* @property {string} title
* The title of the group, this may be automatically generated or
* user assigned.
* @property {boolean} hidden
* Whether the group is hidden or not.
* @property {string} builder
* The builder that was used to create the group (e.g. "domain", "pinned").
* @property {object} builderMetadata
@ -104,24 +106,46 @@ const SnapshotGroups = new (class SnapshotGroups {
* Modifies the metadata for a snapshot group.
*
* @param {SnapshotGroup} group
* The details of the group to modify. If lastAccessed and SnapshotCount are specified, then they are ignored.
* The partial details of the group to modify. Must include the group's id. Any other properties
* update those properties of the group. If builder, imageUrl, lastAccessed or snapshotCount are
* specified then they are ignored.
*/
async updateMetadata(group) {
await PlacesUtils.withConnectionWrapper(
"SnapshotsGroups.jsm:updateMetadata",
db => {
return db.executeCached(
`
UPDATE moz_places_metadata_snapshots_groups
SET title = :title, builder = :builder, builder_data = :builder_data
WHERE id = :id
`,
{
id: group.id,
title: group.title,
builder: group.builder,
builder_data: JSON.stringify(group.builderMetadata),
async db => {
let params = { id: group.id };
let updates = {
title: group.title,
builder_data:
"builderMetadata" in group
? JSON.stringify(group.builderMetadata)
: undefined,
};
if ("hidden" in group) {
updates.hidden = group.hidden ? 1 : 0;
}
let setters = [];
for (let [key, value] of Object.entries(updates)) {
if (value !== undefined) {
setters.push(`${key} = :${key}`);
params[key] = value;
}
}
if (!setters.length) {
return;
}
await db.executeCached(
`
UPDATE moz_places_metadata_snapshots_groups
SET ${setters.join(", ")}
WHERE id = :id
`,
params
);
}
);
@ -189,6 +213,8 @@ const SnapshotGroups = new (class SnapshotGroups {
* @param {number} [options.limit]
* A numerical limit to the number of snapshots to retrieve, defaults to 50.
* Use -1 to specify no limit.
* @param {boolean} [options.hidden]
* Pass true to also return hidden groups.
* @param {string} [options.builder]
* Limit searching snapshot groups to results from a particular builder.
* @param {boolean} [options.skipMinimum]
@ -198,10 +224,15 @@ const SnapshotGroups = new (class SnapshotGroups {
* @returns {SnapshotGroup[]}
* An array of snapshot groups, in descending order of last access time.
*/
async query({ limit = 50, builder = "", skipMinimum = false } = {}) {
async query({
limit = 50,
builder = undefined,
hidden = false,
skipMinimum = false,
} = {}) {
let db = await PlacesUtils.promiseDBConnection();
let params = { builder };
let params = {};
let sizeFragment = "";
let limitFragment = "";
if (!skipMinimum) {
@ -213,9 +244,22 @@ const SnapshotGroups = new (class SnapshotGroups {
limitFragment = "LIMIT :limit";
}
let whereTerms = [];
if (builder) {
whereTerms.push("builder = :builder");
params.builder = builder;
}
if (!hidden) {
whereTerms.push("hidden = 0");
}
let where = whereTerms.length ? `WHERE ${whereTerms.join(" AND ")}` : "";
let rows = await db.executeCached(
`
SELECT g.id, g.title, g.builder, g.builder_data,
SELECT g.id, g.title, g.hidden, g.builder, g.builder_data,
COUNT(s.group_id) AS snapshot_count,
MAX(sn.last_interaction_at) AS last_access,
(SELECT group_concat(IFNULL(preview_image_url, ''), '|')
@ -233,7 +277,7 @@ const SnapshotGroups = new (class SnapshotGroups {
FROM moz_places_metadata_snapshots_groups g
LEFT JOIN moz_places_metadata_groups_to_snapshots s ON s.group_id = g.id
LEFT JOIN moz_places_metadata_snapshots sn ON sn.place_id = s.place_id
WHERE builder = :builder OR :builder = ""
${where}
GROUP BY g.id ${sizeFragment}
ORDER BY last_access DESC
${limitFragment}
@ -398,6 +442,7 @@ const SnapshotGroups = new (class SnapshotGroups {
id: row.getResultByName("id"),
imageUrl,
title: row.getResultByName("title"),
hidden: row.getResultByName("hidden") == 1,
builder: row.getResultByName("builder"),
builderMetadata: JSON.parse(row.getResultByName("builder_data")),
snapshotCount: row.getResultByName("snapshot_count"),

View File

@ -67,83 +67,103 @@ add_task(async function test_change_location_from_Toolbar() {
let toolbarNode = getToolbarNodeForItemGuid(toolbarBookmark.guid);
await withBookmarksDialog(
false,
async function openPropertiesDialog() {
let placesContext = document.getElementById("placesContext");
let promisePopup = BrowserTestUtils.waitForEvent(
placesContext,
"popupshown"
);
EventUtils.synthesizeMouseAtCenter(toolbarNode, {
button: 2,
type: "contextmenu",
add_task(
async function change_location_from_toolbar_dialog_instantEditBookmark() {
await SpecialPowers.pushPrefEnv({
set: [["browser.bookmarks.editDialog.delayedApply.enabled", false]],
});
await promisePopup;
await withBookmarksDialog(
false,
async function openPropertiesDialog() {
let placesContext = document.getElementById("placesContext");
let promisePopup = BrowserTestUtils.waitForEvent(
placesContext,
"popupshown"
);
EventUtils.synthesizeMouseAtCenter(toolbarNode, {
button: 2,
type: "contextmenu",
});
await promisePopup;
let properties = document.getElementById(
"placesContext_show_bookmark:info"
);
placesContext.activateItem(properties);
},
async function test(dialogWin) {
// Check the initial location.
let locationPicker = dialogWin.document.getElementById(
"editBMPanel_locationField"
);
Assert.equal(
locationPicker.value,
TEST_URL,
"The location is the expected one."
);
let properties = document.getElementById(
"placesContext_show_bookmark:info"
);
placesContext.activateItem(properties);
},
async function test(dialogWin) {
// Check the initial location.
let locationPicker = dialogWin.document.getElementById(
"editBMPanel_locationField"
);
Assert.equal(
locationPicker.value,
TEST_URL,
"InstantEditBookmark: The location is the expected one."
);
let promiseLocationChange = PlacesTestUtils.waitForNotification(
"bookmark-url-changed",
events => events.some(e => e.url === TEST_URL2),
"places"
);
// Update the "location" field.
fillBookmarkTextField(
"editBMPanel_locationField",
TEST_URL2,
dialogWin,
false
);
await TestUtils.waitForCondition(
() => locationPicker.value === TEST_URL2,
"The location is correct after update."
);
locationPicker.blur();
await promiseLocationChange;
Assert.equal(
dialogWin.gEditItemOverlay.uri.spec,
TEST_URL2,
"The location is the expected one."
);
locationPicker.focus();
// Confirm and close the dialog.
EventUtils.synthesizeKey("VK_RETURN", {}, dialogWin);
let promiseLocationChange = PlacesTestUtils.waitForNotification(
"bookmark-url-changed",
events => events.some(e => e.url === TEST_URL2),
"places"
);
// Update the "location" field.
fillBookmarkTextField(
"editBMPanel_locationField",
TEST_URL2,
dialogWin,
false
);
await TestUtils.waitForCondition(
() => locationPicker.value === TEST_URL2,
"InstantEditBookmark: The location is correct after update."
);
locationPicker.blur();
await promiseLocationChange;
Assert.equal(
dialogWin.gEditItemOverlay.uri.spec,
TEST_URL2,
"InstantEditBookmark: The location is the expected one."
);
locationPicker.focus();
// Confirm and close the dialog.
EventUtils.synthesizeKey("VK_RETURN", {}, dialogWin);
let updatedBm = await PlacesUtils.bookmarks.fetch(toolbarBookmark.guid);
Assert.equal(
updatedBm.url,
TEST_URL2,
"Should have updated the bookmark location in the database."
let updatedBm = await PlacesUtils.bookmarks.fetch(
toolbarBookmark.guid
);
Assert.equal(
updatedBm.url,
TEST_URL2,
"InstantEditBookmark: Should have updated the bookmark location in the database."
);
}
);
}
);
});
add_task(async function test_change_location_from_Sidebar() {
let bm = await PlacesUtils.bookmarks.fetch({ url: TEST_URL2 });
await withSidebarTree("bookmarks", async function(tree) {
tree.selectItems([bm.guid]);
add_task(async function change_location_from_toolbar_dialog_editBookmark() {
await SpecialPowers.pushPrefEnv({
set: [["browser.bookmarks.editDialog.delayedApply.enabled", true]],
});
await withBookmarksDialog(
false,
function openPropertiesDialog() {
tree.controller.doCommand("placesCmd_show:info");
async function openPropertiesDialog() {
let placesContext = document.getElementById("placesContext");
let promisePopup = BrowserTestUtils.waitForEvent(
placesContext,
"popupshown"
);
EventUtils.synthesizeMouseAtCenter(toolbarNode, {
button: 2,
type: "contextmenu",
});
await promisePopup;
let properties = document.getElementById(
"placesContext_show_bookmark:info"
);
placesContext.activateItem(properties);
},
async function test(dialogWin) {
// Check the initial location.
@ -153,13 +173,7 @@ add_task(async function test_change_location_from_Sidebar() {
Assert.equal(
locationPicker.value,
TEST_URL2,
"The location is the expected one."
);
let promiseLocationChange = PlacesTestUtils.waitForNotification(
"bookmark-url-changed",
events => events.some(e => e.url === TEST_URL3),
"places"
"EditBookmark: The current location is the expected one."
);
// Update the "location" field.
@ -169,22 +183,140 @@ add_task(async function test_change_location_from_Sidebar() {
dialogWin,
false
);
await TestUtils.waitForCondition(
() => locationPicker.value === TEST_URL3,
"The location is correct after update."
locationPicker.blur();
Assert.equal(
locationPicker.value,
TEST_URL3,
"EditBookmark: The changed location is the expected one."
);
locationPicker.focus();
// Confirm and close the dialog.
EventUtils.synthesizeKey("VK_RETURN", {}, dialogWin);
await promiseLocationChange;
let updatedBm = await PlacesUtils.bookmarks.fetch(bm.guid);
Assert.equal(
updatedBm.url,
TEST_URL3,
"Should have updated the bookmark location in the database."
);
}
);
let updatedBm = await PlacesUtils.bookmarks.fetch(toolbarBookmark.guid);
Assert.equal(
updatedBm.url,
TEST_URL3,
"EditBookmark: Should have updated the bookmark location in the database."
);
});
add_task(
async function instantEditBookmark_test_change_location_from_Sidebar() {
await SpecialPowers.pushPrefEnv({
set: [["browser.bookmarks.editDialog.delayedApply.enabled", false]],
});
let bm = await PlacesUtils.bookmarks.fetch({ url: TEST_URL });
await withSidebarTree("bookmarks", async function(tree) {
tree.selectItems([bm.guid]);
await withBookmarksDialog(
false,
function openPropertiesDialog() {
tree.controller.doCommand("placesCmd_show:info");
},
async function test(dialogWin) {
// Check the initial location.
let locationPicker = dialogWin.document.getElementById(
"editBMPanel_locationField"
);
Assert.equal(
locationPicker.value,
TEST_URL,
"Sidebar - InstantEditBookmark: The current location is the expected one."
);
let promiseLocationChange = PlacesTestUtils.waitForNotification(
"bookmark-url-changed",
events => events.some(e => e.url === TEST_URL3),
"places"
);
// Update the "location" field.
fillBookmarkTextField(
"editBMPanel_locationField",
TEST_URL3,
dialogWin,
false
);
await TestUtils.waitForCondition(
() => locationPicker.value === TEST_URL3,
"Sidebar - InstantEditBookmark: The location is correct after update."
);
// Confirm and close the dialog.
EventUtils.synthesizeKey("VK_RETURN", {}, dialogWin);
await promiseLocationChange;
let updatedBm = await PlacesUtils.bookmarks.fetch(bm.guid);
Assert.equal(
updatedBm.url,
TEST_URL3,
"Sidebar - InstantEditBookmark: Should have updated the bookmark location in the database."
);
}
);
});
}
);
add_task(async function editBookmark_test_change_location_from_Sidebar() {
await SpecialPowers.pushPrefEnv({
set: [["browser.bookmarks.editDialog.delayedApply.enabled", true]],
});
let bm = await PlacesUtils.bookmarks.fetch({ url: TEST_URL3 });
await withSidebarTree("bookmarks", async function(tree) {
tree.selectItems([bm.guid]);
await withBookmarksDialog(
false,
function openPropertiesDialog() {
tree.controller.doCommand("placesCmd_show:info");
},
async function test(dialogWin) {
// Check the initial location.
let locationPicker = dialogWin.document.getElementById(
"editBMPanel_locationField"
);
Assert.equal(
locationPicker.value,
TEST_URL3,
"Sidebar - EditBookmark: The current location is the expected one."
);
// Update the "location" field.
fillBookmarkTextField(
"editBMPanel_locationField",
TEST_URL2,
dialogWin,
false
);
Assert.equal(
locationPicker.value,
TEST_URL2,
"Sidebar - EditBookmark: The location is changed in dialog for prefered one."
);
// Confirm and close the dialog.
EventUtils.synthesizeKey("VK_RETURN", {}, dialogWin);
}
);
let updatedBm = await PlacesUtils.bookmarks.fetch(bm.guid);
Assert.equal(
updatedBm.url,
TEST_URL2,
"Sidebar - EditBookmark: Should have updated the bookmark location in the database."
);
});
});
});

View File

@ -308,6 +308,43 @@ function assertSnapshotGroup(group, expected) {
}
}
/**
* Asserts that the list of snapshot groups match the expected values.
*
* @param {SnapshotGroup[]} received
* The received snapshots.
* @param {SnapshotGroup[]} expected
* The expected snapshots.
*/
async function assertSnapshotGroupList(received, expected) {
info(
`Found ${received.length} snapshot groups:\n ${JSON.stringify(received)}`
);
Assert.equal(
received.length,
expected.length,
"Should have the expected number of snapshots"
);
for (let i = 0; i < expected.length; i++) {
assertSnapshotGroup(received[i], expected[i]);
}
}
/**
* Given a list of snapshot groups and a list of ids returns the groups in that
* order.
*
* @param {SnapshotGroup[]} list
* The list of groups.
* @param {string[]} order
* The ids of groups.
* @returns {SnapshotGroup[]} The list of groups in the order expected.
*/
function orderedGroups(list, order) {
let groups = Object.fromEntries(list.map(g => [g.id, g]));
return order.map(id => groups[id]);
}
/**
* Queries overlapping snapshots from the database and asserts their expected values.
*

View File

@ -81,6 +81,7 @@ add_task(async function test_add_and_query() {
assertSnapshotGroup(groups[0], {
title: "Test Group",
builder: "domain",
hidden: false,
snapshotCount: data.length,
lastAccessed: now - 10000,
imageUrl: getPageThumbURL(TEST_URL1),
@ -96,6 +97,7 @@ add_task(async function test_add_and_query() {
assertSnapshotGroup(groups[0], {
title: "Test Group",
builder: "domain",
hidden: false,
snapshotCount: data.length,
lastAccessed: now - 10000,
imageUrl: previewImageURL,
@ -111,6 +113,7 @@ add_task(async function test_add_and_query() {
assertSnapshotGroup(groups[0], {
title: "Test Group",
builder: "domain",
hidden: false,
snapshotCount: data.length,
lastAccessed: now - 10000,
imageUrl: previewImageURL,
@ -134,6 +137,7 @@ add_task(async function test_add_and_query_builderMetadata() {
assertSnapshotGroup(groups[0], {
title: "Test Group",
builder: "domain",
hidden: false,
builderMetadata: { domain: "example.com" },
snapshotCount: urls.length,
});
@ -179,14 +183,57 @@ add_task(async function test_update_metadata() {
);
groups[0].title = "Modified title";
// This should be ignored.
groups[0].builder = "pinned";
await SnapshotGroups.updateMetadata(groups[0]);
let updated_groups = await SnapshotGroups.query({ skipMinimum: true });
Assert.equal(updated_groups.length, 1, "Should return 1 SnapshotGroup");
assertSnapshotGroup(groups[0], {
assertSnapshotGroup(updated_groups[0], {
title: "Modified title",
builder: "pinned",
builder: "domain",
snapshotCount: [TEST_URL3, TEST_URL2, TEST_URL1].length,
});
await SnapshotGroups.updateMetadata({
id: groups[0].id,
title: "Only changed title",
});
updated_groups = await SnapshotGroups.query({ skipMinimum: true });
Assert.equal(updated_groups.length, 1, "Should return 1 SnapshotGroup");
assertSnapshotGroup(updated_groups[0], {
title: "Only changed title",
builder: "domain",
snapshotCount: [TEST_URL3, TEST_URL2, TEST_URL1].length,
});
await SnapshotGroups.updateMetadata({
id: groups[0].id,
builderMetadata: { foo: "bar" },
});
updated_groups = await SnapshotGroups.query({ skipMinimum: true });
Assert.equal(updated_groups.length, 1, "Should return 1 SnapshotGroup");
assertSnapshotGroup(updated_groups[0], {
title: "Only changed title",
builder: "domain",
builderMetadata: { foo: "bar" },
snapshotCount: [TEST_URL3, TEST_URL2, TEST_URL1].length,
});
await SnapshotGroups.updateMetadata({
id: groups[0].id,
title: "Modified title",
builderMetadata: null,
});
updated_groups = await SnapshotGroups.query({ skipMinimum: true });
Assert.equal(updated_groups.length, 1, "Should return 1 SnapshotGroup");
assertSnapshotGroup(updated_groups[0], {
title: "Modified title",
builder: "domain",
builderMetadata: null,
snapshotCount: [TEST_URL3, TEST_URL2, TEST_URL1].length,
});
});
@ -194,11 +241,11 @@ add_task(async function test_update_metadata() {
add_task(async function test_update_urls() {
let groups = await SnapshotGroups.query({ skipMinimum: true });
Assert.equal(groups.length, 1, "Should return 1 snapshot group");
Assert.equal(
groups[0].title,
"Modified title",
"SnapshotGroup title should be retrieved"
);
assertSnapshotGroup(groups[0], {
title: "Modified title",
builder: "domain",
snapshotCount: [TEST_URL3, TEST_URL2, TEST_URL1].length,
});
await SnapshotGroups.updateUrls(groups[0].id, [
TEST_URL5,
@ -210,7 +257,7 @@ add_task(async function test_update_urls() {
Assert.equal(updated_groups.length, 1, "Should return 1 SnapshotGroup");
assertSnapshotGroup(groups[0], {
title: "Modified title",
builder: "pinned",
builder: "domain",
snapshotCount: [TEST_URL5, TEST_URL3, TEST_URL1].length,
});
});
@ -380,3 +427,127 @@ add_task(async function test_minimum_size() {
snapshotCount: 4,
});
});
add_task(async function test_hidden_groups() {
await delete_all_groups();
let group1 = await SnapshotGroups.add(
{
title: "Test Group 1",
builder: "domain",
},
[]
);
let group2 = await SnapshotGroups.add(
{
title: "Test Group 2",
builder: "domain",
},
[]
);
let groups = await SnapshotGroups.query({ skipMinimum: true });
Assert.equal(groups.length, 2, "Should return all groups.");
assertSnapshotGroupList(orderedGroups(groups, [group1, group2]), [
{
title: "Test Group 1",
builder: "domain",
hidden: false,
snapshotCount: 0,
},
{
title: "Test Group 2",
builder: "domain",
hidden: false,
snapshotCount: 0,
},
]);
await SnapshotGroups.updateMetadata({
id: group1,
hidden: true,
});
groups = await SnapshotGroups.query({ skipMinimum: true });
Assert.equal(groups.length, 1, "Should be only one visible group.");
assertSnapshotGroup(groups[0], {
title: "Test Group 2",
builder: "domain",
hidden: false,
snapshotCount: 0,
});
groups = await SnapshotGroups.query({ hidden: true, skipMinimum: true });
Assert.equal(groups.length, 2, "Should be two total groups.");
assertSnapshotGroupList(orderedGroups(groups, [group1, group2]), [
{
title: "Test Group 1",
builder: "domain",
hidden: true,
snapshotCount: 0,
},
{
title: "Test Group 2",
builder: "domain",
hidden: false,
snapshotCount: 0,
},
]);
await SnapshotGroups.updateMetadata({
id: group1,
hidden: false,
});
groups = await SnapshotGroups.query({ skipMinimum: true });
Assert.equal(groups.length, 2, "Should be two visible groups.");
assertSnapshotGroupList(orderedGroups(groups, [group1, group2]), [
{
title: "Test Group 1",
builder: "domain",
hidden: false,
snapshotCount: 0,
},
{
title: "Test Group 2",
builder: "domain",
hidden: false,
snapshotCount: 0,
},
]);
groups = await SnapshotGroups.query({ hidden: true, skipMinimum: true });
Assert.equal(groups.length, 2, "Should be two total groups.");
await SnapshotGroups.updateMetadata({
id: group2,
hidden: true,
});
groups = await SnapshotGroups.query({ skipMinimum: true });
Assert.equal(groups.length, 1, "Should be only one visible group.");
assertSnapshotGroup(groups[0], {
title: "Test Group 1",
builder: "domain",
hidden: false,
snapshotCount: 0,
});
groups = await SnapshotGroups.query({ hidden: true, skipMinimum: true });
Assert.equal(groups.length, 2, "Should be two total groups.");
assertSnapshotGroupList(orderedGroups(groups, [group1, group2]), [
{
title: "Test Group 1",
builder: "domain",
hidden: false,
snapshotCount: 0,
},
{
title: "Test Group 2",
builder: "domain",
hidden: true,
snapshotCount: 0,
},
]);
});

View File

@ -68,6 +68,7 @@ async function renderInfo({
infoLinkUrl ||
RPMGetFormatURLPref("app.support.baseURL") + "private-browsing-myths"
);
linkEl.setAttribute("target", "_blank");
linkEl.addEventListener("click", () => {
window.PrivateBrowsingRecordClick("info_link");
@ -111,6 +112,7 @@ async function renderPromo({
if (vpnPromoUrl) {
linkEl.setAttribute("href", vpnPromoUrl);
linkEl.setAttribute("target", "_blank");
linkEl.addEventListener("click", () => {
window.PrivateBrowsingRecordClick("promo_link");
});

View File

@ -539,9 +539,14 @@ add_task(async function test_experiment_messaging_system() {
"should format the infoLinkUrl url"
);
is(
content.document.querySelector(".promo a").getAttribute("href"),
"http://bar.example.com/" + locale,
"should format the promoLinkUrl url"
content.document.querySelector(".info a").getAttribute("target"),
"_blank",
"should open info url in new tab"
);
is(
content.document.querySelector(".promo a").getAttribute("target"),
"_blank",
"should open promo url in new tab"
);
});

View File

@ -54,6 +54,9 @@ const PREF_URLBAR_DEFAULTS = new Map([
// this value. See UrlbarProviderPlaces.
["autoFill.stddevMultiplier", [0.0, "float"]],
// Whether best match results can be blocked.
["bestMatch.blockingEnabled", false],
// Whether the best match feature is enabled.
["bestMatch.enabled", true],

View File

@ -99,9 +99,7 @@ class ProviderQuickSuggest extends UrlbarProvider {
* @returns {string} The help URL for the Quick Suggest best match feature.
*/
get bestMatchHelpUrl() {
return (
Services.urlFormatter.formatURLPref("app.support.baseURL") + "top-pick"
);
return this.helpUrl;
}
/**
@ -294,6 +292,11 @@ class ProviderQuickSuggest extends UrlbarProvider {
* Whether the result was blocked.
*/
blockResult(result) {
if (!UrlbarPrefs.get("bestMatch.blockingEnabled")) {
this.logger.info("Blocking disabled, ignoring key shortcut");
return false;
}
this.logger.info("Blocking result: " + JSON.stringify(result));
this.blockSuggestion(result.payload.originalUrl);
return true;

View File

@ -1285,7 +1285,9 @@ class UrlbarView {
body.appendChild(bottom);
item._elements.set("bottom", bottom);
this._addRowButton(item, "block", "firefox-suggest-urlbar-block");
if (UrlbarPrefs.get("bestMatch.blockingEnabled")) {
this._addRowButton(item, "block", "firefox-suggest-urlbar-block");
}
if (result.payload.helpUrl) {
this._addRowButton(item, "help", result.payload.helpL10nId);
}

View File

@ -1,7 +1,13 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// Tests best match rows in the view.
// Tests best match rows in the view. See also:
//
// browser_quicksuggest_bestMatch.js
// UI test for quick suggest best matches specifically
// test_quicksuggest_bestMatch.js
// Tests triggering quick suggest best matches and things that don't depend on
// the view
"use strict";
@ -31,6 +37,27 @@ add_task(async function nonsponsoredHelpButton() {
});
});
// Tests a non-sponsored best match row with help and block buttons.
add_task(async function nonsponsoredHelpAndBlockButtons() {
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.bestMatch.blockingEnabled", true]],
});
let result = makeBestMatchResult({ helpUrl: "https://example.com/help" });
await withProvider(result, async () => {
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: "test",
});
await checkBestMatchRow({
result,
hasHelpButton: true,
hasBlockButton: true,
});
await UrlbarTestUtils.promisePopupClose(window);
});
await SpecialPowers.popPrefEnv();
});
// Tests a sponsored best match row.
add_task(async function sponsored() {
let result = makeBestMatchResult({ isSponsored: true });
@ -60,84 +87,121 @@ add_task(async function sponsoredHelpButton() {
});
});
// Tests keyboard selection using the arrow keys.
add_task(async function arrowKeys() {
await doKeySelectionTest(false);
// Tests a sponsored best match row with help and block buttons.
add_task(async function sponsoredHelpAndBlockButtons() {
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.bestMatch.blockingEnabled", true]],
});
let result = makeBestMatchResult({
isSponsored: true,
helpUrl: "https://example.com/help",
});
await withProvider(result, async () => {
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: "test",
});
await checkBestMatchRow({
result,
isSponsored: true,
hasHelpButton: true,
hasBlockButton: true,
});
await UrlbarTestUtils.promisePopupClose(window);
});
await SpecialPowers.popPrefEnv();
});
// Tests keyboard selection using the tab key.
add_task(async function tabKey() {
await doKeySelectionTest(true);
});
async function doKeySelectionTest(useTabKey) {
info(`Starting key selection test with useTabKey=${useTabKey}`);
// Tests keyboard selection.
add_task(async function keySelection() {
let result = makeBestMatchResult({
isSponsored: true,
helpUrl: "https://example.com/help",
});
await withProvider(result, async () => {
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: "test",
});
await checkBestMatchRow({ result, isSponsored: true, hasHelpButton: true });
// Ordered list of class names of the elements that should be selected as
// the tab key is pressed.
// Ordered list of class names of the elements that should be selected.
let expectedClassNames = [
"urlbarView-row-inner",
"urlbarView-button-block",
"urlbarView-button-help",
];
let sendKey = reverse => {
if (useTabKey) {
EventUtils.synthesizeKey("KEY_Tab", { shiftKey: reverse });
} else if (reverse) {
EventUtils.synthesizeKey("KEY_ArrowUp");
} else {
EventUtils.synthesizeKey("KEY_ArrowDown");
}
};
// Test with and without the block button.
for (let showBlockButton of [false, true]) {
UrlbarPrefs.set("bestMatch.blockingEnabled", showBlockButton);
// First tab in forward order and then in reverse order.
for (let reverse of [false, true]) {
info(`Doing key selection with reverse=${reverse}`);
// The block button is not immediately removed or added when
// `bestMatch.blockingEnabled` is toggled while the panel is open, so we
// need to do a new search each time we change it.
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: "test",
});
await checkBestMatchRow({
result,
isSponsored: true,
hasHelpButton: true,
hasBlockButton: showBlockButton,
});
let classNames = [...expectedClassNames];
if (reverse) {
classNames.reverse();
// Test with the tab key vs. arrow keys and in order vs. reverse order.
for (let useTabKey of [false, true]) {
for (let reverse of [false, true]) {
info(
"Doing key selection: " +
JSON.stringify({ showBlockButton, useTabKey, reverse })
);
let classNames = [...expectedClassNames];
if (!showBlockButton) {
classNames.splice(classNames.indexOf("urlbarView-button-block"), 1);
}
if (reverse) {
classNames.reverse();
}
let sendKey = () => {
if (useTabKey) {
EventUtils.synthesizeKey("KEY_Tab", { shiftKey: reverse });
} else if (reverse) {
EventUtils.synthesizeKey("KEY_ArrowUp");
} else {
EventUtils.synthesizeKey("KEY_ArrowDown");
}
};
// Move selection through each expected element.
for (let className of classNames) {
info("Expecting selection: " + className);
sendKey();
Assert.ok(gURLBar.view.isOpen, "View remains open");
let { selectedElement } = gURLBar.view;
Assert.ok(selectedElement, "Selected element exists");
Assert.ok(
selectedElement.classList.contains(className),
"Expected element is selected"
);
}
sendKey();
Assert.ok(
gURLBar.view.isOpen,
"View remains open after keying through best match row"
);
}
}
for (let className of classNames) {
sendKey(reverse);
Assert.ok(gURLBar.view.isOpen, "View remains open");
let { selectedElement } = gURLBar.view;
Assert.ok(selectedElement, "Selected element exists");
Assert.ok(
selectedElement.classList.contains(className),
"Expected element is selected"
);
}
sendKey(reverse);
Assert.ok(
gURLBar.view.isOpen,
"View remains open after keying through best match row"
);
await UrlbarTestUtils.promisePopupClose(window);
UrlbarPrefs.clear("bestMatch.blockingEnabled");
}
await UrlbarTestUtils.promisePopupClose(window);
});
}
});
async function checkBestMatchRow({
result,
isSponsored = false,
hasHelpButton = false,
hasBlockButton = false,
}) {
Assert.equal(
UrlbarTestUtils.getResultCount(window),
@ -189,7 +253,11 @@ async function checkBestMatchRow({
}
let blockButton = row._buttons.get("block");
Assert.ok(blockButton, "Row has a block button");
if (hasBlockButton) {
Assert.ok(blockButton, "Row has a block button");
} else {
Assert.ok(!blockButton, "Row does not have a block button");
}
let helpButton = row._buttons.get("help");
Assert.equal(

View File

@ -291,11 +291,20 @@ class QSTestUtils {
this.Assert.ok(helpButton, "The help button should be present");
this.Assert.equal(result.payload.helpUrl, LEARN_MORE_URL, "Result helpURL");
this.Assert.equal(
!!row._buttons.get("block"),
isBestMatch,
"The block button is present iff the suggestion is a best match"
);
let blockButton = row._buttons.get("block");
if (!isBestMatch) {
this.Assert.ok(
!blockButton,
"The block button is not present since the row is not a best match"
);
} else if (!UrlbarPrefs.get("bestMatch.blockingEnabled")) {
this.Assert.ok(
!blockButton,
"The block button is not present since blocking is disabled"
);
} else {
this.Assert.ok(blockButton, "The block button is present");
}
return details;
}

View File

@ -29,7 +29,10 @@ const SUGGESTIONS = [1, 2, 3].map(i => ({
add_task(async function init() {
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.bestMatch.enabled", true]],
set: [
["browser.urlbar.bestMatch.enabled", true],
["browser.urlbar.bestMatch.blockingEnabled", true],
],
});
await PlacesUtils.history.clear();
@ -44,15 +47,31 @@ add_task(async function init() {
// Picks the block button with the keyboard.
add_task(async function basicBlock_keyboard() {
await doBasicBlockTest(true);
await doBasicBlockTest(() => {
// Arrow down twice to select the block button: once to select the main
// part of the best match row, once to select the block button.
EventUtils.synthesizeKey("KEY_ArrowDown", { repeat: 2 });
EventUtils.synthesizeKey("KEY_Enter");
});
});
// Picks the block button with the mouse.
add_task(async function basicBlock_mouse() {
await doBasicBlockTest(false);
await doBasicBlockTest(blockButton => {
EventUtils.synthesizeMouseAtCenter(blockButton, {});
});
});
async function doBasicBlockTest(useKeyboard) {
// Uses the key shortcut to block a best match.
add_task(async function basicBlock_keyShortcut() {
await doBasicBlockTest(() => {
// Arrow down once to select the best match row.
EventUtils.synthesizeKey("KEY_ArrowDown");
EventUtils.synthesizeKey("KEY_Delete", { shiftKey: true });
});
});
async function doBasicBlockTest(doBlock) {
// Do a search that triggers the best match.
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
@ -70,18 +89,9 @@ async function doBasicBlockTest(useKeyboard) {
isBestMatch: true,
});
// Pick the block button.
if (useKeyboard) {
// Arrow down twice to select the block button: once to select the main part
// of the best match row, once to select the block button.
EventUtils.synthesizeKey("KEY_ArrowDown", { repeat: 2 });
EventUtils.synthesizeKey("KEY_Enter");
} else {
EventUtils.synthesizeMouseAtCenter(
details.element.row._buttons.get("block"),
{}
);
}
// Block the suggestion.
let blockButton = details.element.row._buttons.get("block");
doBlock(blockButton);
// The row should have been removed.
Assert.ok(
@ -144,3 +154,63 @@ add_task(async function blockMultiple() {
await UrlbarTestUtils.promisePopupClose(window);
await UrlbarProviderQuickSuggest.clearBlockedSuggestions();
});
// Tests with blocking disabled.
add_task(async function blockingDisabled() {
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.bestMatch.blockingEnabled", false]],
});
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: SUGGESTIONS[0].keywords[0],
});
let expectedResultCount = 2;
Assert.equal(
UrlbarTestUtils.getResultCount(window),
expectedResultCount,
"Two rows are present after searching (heuristic + best match)"
);
// `assertIsQuickSuggest()` asserts that the block button is not present when
// `bestMatch.blockingEnabled` is false, but check it again here since it's
// central to this test.
let details = await QuickSuggestTestUtils.assertIsQuickSuggest({
window,
originalUrl: SUGGESTIONS[0].url,
isBestMatch: true,
});
Assert.ok(
!details.element.row._buttons.get("block"),
"Block button is not present"
);
// Arrow down once to select the best match row and then press the key
// shortcut to block.
EventUtils.synthesizeKey("KEY_ArrowDown");
EventUtils.synthesizeKey("KEY_Delete", { shiftKey: true });
// Nothing should happen.
Assert.ok(
UrlbarTestUtils.isPopupOpen(window),
"View remains open after key shortcut"
);
Assert.equal(
UrlbarTestUtils.getResultCount(window),
expectedResultCount,
"Same number of results after key shortcut"
);
await QuickSuggestTestUtils.assertIsQuickSuggest({
window,
originalUrl: SUGGESTIONS[0].url,
isBestMatch: true,
});
Assert.ok(
!(await UrlbarProviderQuickSuggest.isSuggestionBlocked(SUGGESTIONS[0].url)),
"Suggestion is not blocked"
);
await UrlbarTestUtils.promisePopupClose(window);
await SpecialPowers.popPrefEnv();
});

View File

@ -141,52 +141,34 @@ let privateWindowTask = {
// Implementation
var WinTaskbarJumpList = {
_builder: null,
_tasks: null,
_shuttingDown: false,
var Builder = class {
constructor(builder) {
this._builder = builder;
this._tasks = null;
this._pendingStatements = {};
this._shuttingDown = false;
// These are ultimately controlled by prefs, so we disable
// everything until is read from there
this._showTasks = false;
this._showFrequent = false;
this._showRecent = false;
this._maxItemCount = 0;
}
/**
* Startup, shutdown, and update
*/
refreshPrefs(showTasks, showFrequent, showRecent, maxItemCount) {
this._showTasks = showTasks;
this._showFrequent = showFrequent;
this._showRecent = showRecent;
this._maxItemCount = maxItemCount;
}
startup: function WTBJL_startup() {
// exit if this isn't win7 or higher.
if (!this._initTaskbar()) {
return;
}
updateShutdownState(shuttingDown) {
this._shuttingDown = shuttingDown;
}
// Store our task list config data
this._tasks = tasksCfg;
if (PrivateBrowsingUtils.enabled) {
tasksCfg.push(privateWindowTask);
}
// retrieve taskbar related prefs.
this._refreshPrefs();
// observer for private browsing and our prefs branch
this._initObs();
// jump list refresh timer
this._updateTimer();
},
update: function WTBJL_update() {
// are we disabled via prefs? don't do anything!
if (!this._enabled) {
return;
}
// do what we came here to do, update the taskbar jumplist
this._buildList();
},
_shutdown: function WTBJL__shutdown() {
this._shuttingDown = true;
this._free();
},
delete() {
delete this._builder;
}
/**
* List building
@ -198,13 +180,15 @@ var WinTaskbarJumpList = {
* commitBuild() will commit for real.
*/
_pendingStatements: {},
_hasPendingStatements: function WTBJL__hasPendingStatements() {
_hasPendingStatements() {
return !!Object.keys(this._pendingStatements).length;
},
}
async _buildList() {
if (this._hasPendingStatements()) {
async buildList() {
if (
(this._showFrequent || this._showRecent) &&
this._hasPendingStatements()
) {
// We were requested to update the list while another update was in
// progress, this could happen at shutdown, idle or privatebrowsing.
// Abort the current list building.
@ -238,7 +222,7 @@ var WinTaskbarJumpList = {
}
this._commitBuild();
},
}
/**
* Taskbar api wrappers
@ -251,10 +235,13 @@ var WinTaskbarJumpList = {
// Prior to building, delete removed items from history.
this._clearHistory(URIsToRemove);
}
},
}
_commitBuild: function WTBJL__commitBuild() {
if (this._hasPendingStatements()) {
_commitBuild() {
if (
(this._showFrequent || this._showRecent) &&
this._hasPendingStatements()
) {
return;
}
@ -263,9 +250,9 @@ var WinTaskbarJumpList = {
this._builder.abortListBuild();
}
});
},
}
_buildTasks: function WTBJL__buildTasks() {
_buildTasks() {
var items = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
this._tasks.forEach(function(task) {
if (
@ -290,9 +277,9 @@ var WinTaskbarJumpList = {
items
);
}
},
}
_buildCustom: function WTBJL__buildCustom(title, items) {
_buildCustom(title, items) {
if (items.length) {
this._builder.addListToBuild(
this._builder.JUMPLIST_CATEGORY_CUSTOMLIST,
@ -300,9 +287,9 @@ var WinTaskbarJumpList = {
title
);
}
},
}
_buildFrequent: function WTBJL__buildFrequent() {
_buildFrequent() {
// Windows supports default frequent and recent lists,
// but those depend on internal windows visit tracking
// which we don't populate. So we build our own custom
@ -339,9 +326,9 @@ var WinTaskbarJumpList = {
},
this
);
},
}
_buildRecent: function WTBJL__buildRecent() {
_buildRecent() {
var items = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
// Frequent items will be skipped, so we select a double amount of
// entries and stop fetching results at _maxItemCount.
@ -385,23 +372,17 @@ var WinTaskbarJumpList = {
},
this
);
},
}
_deleteActiveJumpList: function WTBJL__deleteAJL() {
_deleteActiveJumpList() {
this._builder.deleteActiveList();
},
}
/**
* Jump list item creation helpers
*/
_getHandlerAppItem: function WTBJL__getHandlerAppItem(
name,
description,
args,
iconIndex,
faviconPageUri
) {
_getHandlerAppItem(name, description, args, iconIndex, faviconPageUri) {
var file = Services.dirsvc.get("XREExeF", Ci.nsIFile);
var handlerApp = Cc[
@ -422,25 +403,13 @@ var WinTaskbarJumpList = {
item.iconIndex = iconIndex;
item.faviconPageUri = faviconPageUri;
return item;
},
_getSeparatorItem: function WTBJL__getSeparatorItem() {
var item = Cc["@mozilla.org/windows-jumplistseparator;1"].createInstance(
Ci.nsIJumpListSeparator
);
return item;
},
}
/**
* Nav history helpers
*/
_getHistoryResults: function WTBLJL__getHistoryResults(
aSortingMode,
aLimit,
aCallback,
aScope
) {
_getHistoryResults(aSortingMode, aLimit, aCallback, aScope) {
var options = PlacesUtils.history.getNewQueryOptions();
options.maxResults = aLimit;
options.sortingMode = aSortingMode;
@ -464,12 +433,12 @@ var WinTaskbarJumpList = {
);
},
handleCompletion(aReason) {
aCallback.call(WinTaskbarJumpList, null);
aCallback.call(aScope, null);
},
});
},
}
_clearHistory: function WTBJL__clearHistory(uriSpecsToRemove) {
_clearHistory(uriSpecsToRemove) {
let URIsToRemove = uriSpecsToRemove
.map(spec => {
try {
@ -484,6 +453,66 @@ var WinTaskbarJumpList = {
if (URIsToRemove.length) {
PlacesUtils.history.remove(URIsToRemove).catch(Cu.reportError);
}
}
};
var WinTaskbarJumpList = {
// We build two separate jump lists -- one for the regular Firefox icon
// and one for the Private Browsing icon
_builder: null,
_pbBuilder: null,
_builtPb: false,
_shuttingDown: false,
/**
* Startup, shutdown, and update
*/
startup: function WTBJL_startup() {
// exit if this isn't win7 or higher.
if (!this._initTaskbar()) {
return;
}
if (PrivateBrowsingUtils.enabled) {
tasksCfg.push(privateWindowTask);
}
// Store our task list config data
this._builder._tasks = tasksCfg;
this._pbBuilder._tasks = tasksCfg;
// retrieve taskbar related prefs.
this._refreshPrefs();
// observer for private browsing and our prefs branch
this._initObs();
// jump list refresh timer
this._updateTimer();
},
update: function WTBJL_update() {
// are we disabled via prefs? don't do anything!
if (!this._enabled) {
return;
}
// we only need to do this once, but we do it here
// to avoid main thread io on startup
if (!this._builtPb) {
this._pbBuilder.buildList();
this._builtPb = true;
}
// do what we came here to do, update the taskbar jumplist
this._builder.buildList();
},
_shutdown: function WTBJL__shutdown() {
this._builder.updateShutdownState(true);
this._pbBuilder.updateShutdownState(true);
this._shuttingDown = true;
this._free();
},
/**
@ -492,10 +521,17 @@ var WinTaskbarJumpList = {
_refreshPrefs: function WTBJL__refreshPrefs() {
this._enabled = _prefs.getBoolPref(PREF_TASKBAR_ENABLED);
this._showFrequent = _prefs.getBoolPref(PREF_TASKBAR_FREQUENT);
this._showRecent = _prefs.getBoolPref(PREF_TASKBAR_RECENT);
this._showTasks = _prefs.getBoolPref(PREF_TASKBAR_TASKS);
this._maxItemCount = _prefs.getIntPref(PREF_TASKBAR_ITEMCOUNT);
var showTasks = _prefs.getBoolPref(PREF_TASKBAR_TASKS);
this._builder.refreshPrefs(
showTasks,
_prefs.getBoolPref(PREF_TASKBAR_FREQUENT),
_prefs.getBoolPref(PREF_TASKBAR_RECENT),
_prefs.getIntPref(PREF_TASKBAR_ITEMCOUNT)
);
// showTasks is the only relevant pref for the Private Browsing Jump List
// the others are are related to frequent/recent entries, which are
// explicitly disabled for it
this._pbBuilder.refreshPrefs(showTasks, false, false, 0);
},
/**
@ -503,11 +539,15 @@ var WinTaskbarJumpList = {
*/
_initTaskbar: function WTBJL__initTaskbar() {
this._builder = _taskbarService.createJumpListBuilder();
if (!this._builder || !this._builder.available) {
var builder = _taskbarService.createJumpListBuilder(false);
var pbBuilder = _taskbarService.createJumpListBuilder(true);
if (!builder || !builder.available || !pbBuilder || !pbBuilder.available) {
return false;
}
this._builder = new Builder(builder, true, true, true);
this._pbBuilder = new Builder(pbBuilder, true, false, false);
return true;
},
@ -571,7 +611,8 @@ var WinTaskbarJumpList = {
this._freeObs();
this._updateTimer();
this._updateIdleObserver();
delete this._builder;
this._builder.delete();
this._pbBuilder.delete();
},
notify: function WTBJL_notify(aTimer) {

View File

@ -12,9 +12,3 @@
--downloads-item-details-opacity: 0.6;
--downloads-item-disabled-opacity: 0.6;
}
#downloadsPanel[keyfocus] :is(#downloadsListBox:focus > richlistitem[selected], #downloadsSummary:focus),
.downloadButton:focus {
outline: 1px -moz-dialogtext dotted;
outline-offset: -1px;
}

View File

@ -10,10 +10,3 @@
--downloads-item-details-opacity: 0.7;
--downloads-item-disabled-opacity: 0.7;
}
.downloadButton:focus > .button-box,
#downloadsPanel[keyfocus] #downloadsListBox:focus > richlistitem[selected],
#downloadsPanel[keyfocus] #downloadsSummary:focus {
outline: var(--focus-outline);
outline-offset: calc(var(--focus-outline-width) * -1);
}

View File

@ -180,6 +180,12 @@ toolbarpaletteitem toolbarbutton[disabled] {
color: inherit !important;
}
#PersonalToolbar toolbarpaletteitem toolbarbutton[checked="true"],
toolbar toolbarpaletteitem toolbarbutton[checked="true"]
> :where(.toolbarbutton-icon, .toolbarbutton-text, .toolbarbutton-badge-stack) {
background-color: revert !important;
}
toolbarpaletteitem > toolbarbutton > .toolbarbutton-icon,
toolbarpaletteitem > toolbarbutton > .toolbarbutton-badge-stack > .toolbarbutton-icon,
toolbarpaletteitem > #search-container,

View File

@ -52,19 +52,26 @@
#downloadsListBox > richlistitem.openWhenFinished.hoveringMainArea:hover,
#downloadsListBox > richlistitem[verdict]:hover,
#downloadsListBox > richlistitem.openWhenFinished:hover,
.downloadsPanelFooterButton:hover,
#downloadsPanel[keyfocus] #downloadsListBox:focus > richlistitem[selected],
#downloadsHistory:focus {
.downloadsPanelFooterButton:hover {
background-color: var(--panel-item-hover-bgcolor);
}
#downloadsListBox > richlistitem[state="1"][exists].hoveringMainArea:hover:active,
#downloadsListBox > richlistitem.openWhenFinished.hoveringMainArea:hover:active,
.downloadsPanelFooterButton[open="true"],
#downloadsListBox > richlistitem[verdict]:hover:active {
#downloadsListBox > richlistitem[verdict]:hover:active,
.downloadsPanelFooterButton[open="true"] {
background-color: var(--panel-item-active-bgcolor);
}
#downloadsListBox:focus-visible > richlistitem[selected],
#downloadsListBox[force-focus-visible]:focus > richlistitem[selected],
.downloadButton:focus-visible,
#downloadsSummary:focus-visible,
.downloadsPanelFooterButton:focus-visible {
outline: var(--focus-outline);
outline-offset: calc(var(--focus-outline-width) * -1);
}
#downloadsListBox > richlistitem[verdict="Insecure"] .downloadDetails,
#downloadsListBox > richlistitem[verdict="Malware"] .downloadDetails {
color: #C50042;
@ -87,18 +94,11 @@
margin-top: 1.5em;
}
#downloadsPanel:not([keyfocus]) .downloadsPanelFooterButton:-moz-focusring {
outline: none;
}
#downloadsSummary {
background: var(--arrowpanel-dimmed);
border-top: 1px solid var(--panel-separator-color);
/* Reserve the same space as the button and separator in download items. */
padding-inline-end: 59px;
}
#downloadsSummary {
-moz-user-focus: normal;
}

View File

@ -10,14 +10,6 @@
border: 0;
}
#downloadsSummary:focus-visible {
outline-offset: -5px;
}
#downloadsPanel:not([keyfocus]) #downloadsSummary:focus-visible {
outline: none;
}
/*** List items and similar elements in the summary ***/
:root {
@ -27,17 +19,6 @@
--downloads-item-disabled-opacity: 0.6;
}
#downloadsPanel:not([keyfocus]) .downloadButton:focus-visible {
outline: none;
}
/*** Highlighted list items ***/
#downloadsPanel[keyfocus] #downloadsListBox:focus > richlistitem[selected] {
outline: 1px -moz-dialogtext dotted;
outline-offset: -1px;
}
/*** Progress bars ***/
@media (prefers-contrast) {

View File

@ -1203,7 +1203,12 @@
"../node_modules/parse-script-tags/node_modules/@babel/types/lib/validators/isSpecifierDefault.js": 1051,
"../node_modules/parse-script-tags/node_modules/@babel/types/lib/validators/isValidES3Identifier.js": 1052,
"../node_modules/parse-script-tags/node_modules/@babel/types/lib/validators/isVar.js": 1053,
"../node_modules/parse-script-tags/node_modules/@babel/parser/lib/index.js": 1054
"../node_modules/parse-script-tags/node_modules/@babel/parser/lib/index.js": 1054,
"../node_modules/babel-loader/lib/index.js??ref--1!../../shared/worker-handler.js": 1055,
"../node_modules/babel-loader/lib/index.js??ref--1!../packages/devtools-source-map/src/utils/network-request.js": 1056,
"../node_modules/babel-loader/lib/index.js??ref--1!../../shared/worker-dispatcher.js": 1057,
"../node_modules/babel-loader/lib/index.js??ref--1!../packages/devtools-source-map/src/utils/privileged-network-request.js": 1058,
"../node_modules/babel-loader/lib/index.js??ref--1!../../shared/worker-utils.js": 1059
},
"usedIds": {
"0": 0,
@ -2260,7 +2265,12 @@
"1051": 1051,
"1052": 1052,
"1053": 1053,
"1054": 1054
"1054": 1054,
"1055": 1055,
"1056": 1056,
"1057": 1057,
"1058": 1058,
"1059": 1059
}
},
"chunks": {

View File

@ -6026,22 +6026,7 @@ function isSpreadProperty(node, opts) {
}
/***/ }),
/* 560 */
/***/ (function(module, exports, __webpack_require__) {
/* 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/>. */
const networkRequest = __webpack_require__(567);
const workerUtils = __webpack_require__(568);
module.exports = {
networkRequest,
workerUtils
};
/***/ }),
/* 560 */,
/* 561 */
/***/ (function(module, exports, __webpack_require__) {
@ -7923,213 +7908,8 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate
/***/ }),
/* 567 */
/***/ (function(module, exports) {
/* 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/>. */
function networkRequest(url, opts) {
const UNSUPPORTED_PROTOCOLS = ["chrome://", "resource://"];
if (UNSUPPORTED_PROTOCOLS.some(protocol => url.startsWith(protocol))) {
return Promise.reject(`unsupported protocol for sourcemap request ${url}`);
}
return fetch(url, {
cache: opts.loadFromCache ? "default" : "no-cache"
}).then(res => {
if (res.status >= 200 && res.status < 300) {
if (res.headers.get("Content-Type") === "application/wasm") {
return res.arrayBuffer().then(buffer => ({
content: buffer,
isDwarf: true
}));
}
return res.text().then(text => ({
content: text
}));
}
return Promise.reject(`request failed with status ${res.status}`);
});
}
module.exports = networkRequest;
/***/ }),
/* 568 */
/***/ (function(module, exports) {
/* 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/>. */
function WorkerDispatcher() {
this.msgId = 1;
this.worker = null; // Map of message ids -> promise resolution functions, for dispatching worker responses
this.pendingCalls = new Map();
this._onMessage = this._onMessage.bind(this);
}
WorkerDispatcher.prototype = {
start(url, win = window) {
this.worker = new win.Worker(url);
this.worker.onerror = err => {
console.error(`Error in worker ${url}`, err.message);
};
this.worker.addEventListener("message", this._onMessage);
},
stop() {
if (!this.worker) {
return;
}
this.worker.removeEventListener("message", this._onMessage);
this.worker.terminate();
this.worker = null;
this.pendingCalls.clear();
},
task(method, {
queue = false
} = {}) {
const calls = [];
const push = args => {
return new Promise((resolve, reject) => {
if (queue && calls.length === 0) {
Promise.resolve().then(flush);
}
calls.push({
args,
resolve,
reject
});
if (!queue) {
flush();
}
});
};
const flush = () => {
const items = calls.slice();
calls.length = 0;
if (!this.worker) {
return;
}
const id = this.msgId++;
this.worker.postMessage({
id,
method,
calls: items.map(item => item.args)
});
this.pendingCalls.set(id, items);
};
return (...args) => push(args);
},
invoke(method, ...args) {
return this.task(method)(...args);
},
_onMessage({
data: result
}) {
const items = this.pendingCalls.get(result.id);
this.pendingCalls.delete(result.id);
if (!items) {
return;
}
if (!this.worker) {
return;
}
result.results.forEach((resultData, i) => {
const {
resolve,
reject
} = items[i];
if (resultData.error) {
const err = new Error(resultData.message);
err.metadata = resultData.metadata;
reject(err);
} else {
resolve(resultData.response);
}
});
}
};
function workerHandler(publicInterface) {
return function (msg) {
const {
id,
method,
calls
} = msg.data;
Promise.all(calls.map(args => {
try {
const response = publicInterface[method].apply(undefined, args);
if (response instanceof Promise) {
return response.then(val => ({
response: val
}), err => asErrorMessage(err));
}
return {
response
};
} catch (error) {
return asErrorMessage(error);
}
})).then(results => {
globalThis.postMessage({
id,
results
});
});
};
}
function asErrorMessage(error) {
if (typeof error === "object" && error && "message" in error) {
// Error can't be sent via postMessage, so be sure to convert to
// string.
return {
error: true,
message: error.message,
metadata: error.metadata
};
}
return {
error: true,
message: error == null ? error : error.toString(),
metadata: undefined
};
}
module.exports = {
WorkerDispatcher,
workerHandler
};
/***/ }),
/* 567 */,
/* 568 */,
/* 569 */
/***/ (function(module, exports, __webpack_require__) {
@ -32245,7 +32025,7 @@ var _validate = __webpack_require__(895);
var _mapExpression = _interopRequireDefault(__webpack_require__(896));
var _devtoolsUtils = __webpack_require__(560);
var _workerUtils = __webpack_require__(1059);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@ -32256,10 +32036,6 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
/* 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/>. */
const {
workerHandler
} = _devtoolsUtils.workerUtils;
function clearState() {
(0, _ast.clearASTs)();
(0, _getScopes.clearScopes)();
@ -32267,7 +32043,7 @@ function clearState() {
(0, _getSymbols.clearSymbols)();
}
self.onmessage = workerHandler({
self.onmessage = (0, _workerUtils.workerHandler)({
findOutOfScopeLocations: _findOutOfScopeLocations.default,
getSymbols: _getSymbols.getSymbols,
getScopes: _getScopes.default,
@ -76505,6 +76281,187 @@ exports.tokTypes = types;
//# sourceMappingURL=index.js.map
/***/ }),
/* 1055 */,
/* 1056 */,
/* 1057 */,
/* 1058 */,
/* 1059 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
function WorkerDispatcher() {
this.msgId = 1;
this.worker = null; // Map of message ids -> promise resolution functions, for dispatching worker responses
this.pendingCalls = new Map();
this._onMessage = this._onMessage.bind(this);
}
WorkerDispatcher.prototype = {
start(url, win = window) {
this.worker = new win.Worker(url);
this.worker.onerror = err => {
console.error(`Error in worker ${url}`, err.message);
};
this.worker.addEventListener("message", this._onMessage);
},
stop() {
if (!this.worker) {
return;
}
this.worker.removeEventListener("message", this._onMessage);
this.worker.terminate();
this.worker = null;
this.pendingCalls.clear();
},
task(method, {
queue = false
} = {}) {
const calls = [];
const push = args => {
return new Promise((resolve, reject) => {
if (queue && calls.length === 0) {
Promise.resolve().then(flush);
}
calls.push({
args,
resolve,
reject
});
if (!queue) {
flush();
}
});
};
const flush = () => {
const items = calls.slice();
calls.length = 0;
if (!this.worker) {
return;
}
const id = this.msgId++;
this.worker.postMessage({
id,
method,
calls: items.map(item => item.args)
});
this.pendingCalls.set(id, items);
};
return (...args) => push(args);
},
invoke(method, ...args) {
return this.task(method)(...args);
},
_onMessage({
data: result
}) {
const items = this.pendingCalls.get(result.id);
this.pendingCalls.delete(result.id);
if (!items) {
return;
}
if (!this.worker) {
return;
}
result.results.forEach((resultData, i) => {
const {
resolve,
reject
} = items[i];
if (resultData.error) {
const err = new Error(resultData.message);
err.metadata = resultData.metadata;
reject(err);
} else {
resolve(resultData.response);
}
});
}
};
function workerHandler(publicInterface) {
return function (msg) {
const {
id,
method,
calls
} = msg.data;
Promise.all(calls.map(args => {
try {
const response = publicInterface[method].apply(undefined, args);
if (response instanceof Promise) {
return response.then(val => ({
response: val
}), err => asErrorMessage(err));
}
return {
response
};
} catch (error) {
return asErrorMessage(error);
}
})).then(results => {
globalThis.postMessage({
id,
results
});
});
};
}
function asErrorMessage(error) {
if (typeof error === "object" && error && "message" in error) {
// Error can't be sent via postMessage, so be sure to convert to
// string.
return {
error: true,
message: error.message,
metadata: error.metadata
};
}
return {
error: true,
message: error == null ? error : error.toString(),
metadata: undefined
};
} // Might be loaded within a worker thread where `module` isn't available.
if (true) {
module.exports = {
WorkerDispatcher,
workerHandler
};
}
/***/ })
/******/ ]);
});

View File

@ -79,21 +79,182 @@ return /******/ (function(modules) { // webpackBootstrap
/************************************************************************/
/******/ ({
/***/ 560:
/***/ 1059:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
const networkRequest = __webpack_require__(567);
const workerUtils = __webpack_require__(568);
module.exports = {
networkRequest,
workerUtils
function WorkerDispatcher() {
this.msgId = 1;
this.worker = null; // Map of message ids -> promise resolution functions, for dispatching worker responses
this.pendingCalls = new Map();
this._onMessage = this._onMessage.bind(this);
}
WorkerDispatcher.prototype = {
start(url, win = window) {
this.worker = new win.Worker(url);
this.worker.onerror = err => {
console.error(`Error in worker ${url}`, err.message);
};
this.worker.addEventListener("message", this._onMessage);
},
stop() {
if (!this.worker) {
return;
}
this.worker.removeEventListener("message", this._onMessage);
this.worker.terminate();
this.worker = null;
this.pendingCalls.clear();
},
task(method, {
queue = false
} = {}) {
const calls = [];
const push = args => {
return new Promise((resolve, reject) => {
if (queue && calls.length === 0) {
Promise.resolve().then(flush);
}
calls.push({
args,
resolve,
reject
});
if (!queue) {
flush();
}
});
};
const flush = () => {
const items = calls.slice();
calls.length = 0;
if (!this.worker) {
return;
}
const id = this.msgId++;
this.worker.postMessage({
id,
method,
calls: items.map(item => item.args)
});
this.pendingCalls.set(id, items);
};
return (...args) => push(args);
},
invoke(method, ...args) {
return this.task(method)(...args);
},
_onMessage({
data: result
}) {
const items = this.pendingCalls.get(result.id);
this.pendingCalls.delete(result.id);
if (!items) {
return;
}
if (!this.worker) {
return;
}
result.results.forEach((resultData, i) => {
const {
resolve,
reject
} = items[i];
if (resultData.error) {
const err = new Error(resultData.message);
err.metadata = resultData.metadata;
reject(err);
} else {
resolve(resultData.response);
}
});
}
};
function workerHandler(publicInterface) {
return function (msg) {
const {
id,
method,
calls
} = msg.data;
Promise.all(calls.map(args => {
try {
const response = publicInterface[method].apply(undefined, args);
if (response instanceof Promise) {
return response.then(val => ({
response: val
}), err => asErrorMessage(err));
}
return {
response
};
} catch (error) {
return asErrorMessage(error);
}
})).then(results => {
globalThis.postMessage({
id,
results
});
});
};
}
function asErrorMessage(error) {
if (typeof error === "object" && error && "message" in error) {
// Error can't be sent via postMessage, so be sure to convert to
// string.
return {
error: true,
message: error.message,
metadata: error.metadata
};
}
return {
error: true,
message: error == null ? error : error.toString(),
metadata: undefined
};
} // Might be loaded within a worker thread where `module` isn't available.
if (true) {
module.exports = {
WorkerDispatcher,
workerHandler
};
}
/***/ }),
/***/ 566:
@ -518,215 +679,6 @@ function compareByGeneratedPositionsInflated(mappingA, mappingB) {
exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated;
/***/ }),
/***/ 567:
/***/ (function(module, exports) {
/* 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/>. */
function networkRequest(url, opts) {
const UNSUPPORTED_PROTOCOLS = ["chrome://", "resource://"];
if (UNSUPPORTED_PROTOCOLS.some(protocol => url.startsWith(protocol))) {
return Promise.reject(`unsupported protocol for sourcemap request ${url}`);
}
return fetch(url, {
cache: opts.loadFromCache ? "default" : "no-cache"
}).then(res => {
if (res.status >= 200 && res.status < 300) {
if (res.headers.get("Content-Type") === "application/wasm") {
return res.arrayBuffer().then(buffer => ({
content: buffer,
isDwarf: true
}));
}
return res.text().then(text => ({
content: text
}));
}
return Promise.reject(`request failed with status ${res.status}`);
});
}
module.exports = networkRequest;
/***/ }),
/***/ 568:
/***/ (function(module, exports) {
/* 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/>. */
function WorkerDispatcher() {
this.msgId = 1;
this.worker = null; // Map of message ids -> promise resolution functions, for dispatching worker responses
this.pendingCalls = new Map();
this._onMessage = this._onMessage.bind(this);
}
WorkerDispatcher.prototype = {
start(url, win = window) {
this.worker = new win.Worker(url);
this.worker.onerror = err => {
console.error(`Error in worker ${url}`, err.message);
};
this.worker.addEventListener("message", this._onMessage);
},
stop() {
if (!this.worker) {
return;
}
this.worker.removeEventListener("message", this._onMessage);
this.worker.terminate();
this.worker = null;
this.pendingCalls.clear();
},
task(method, {
queue = false
} = {}) {
const calls = [];
const push = args => {
return new Promise((resolve, reject) => {
if (queue && calls.length === 0) {
Promise.resolve().then(flush);
}
calls.push({
args,
resolve,
reject
});
if (!queue) {
flush();
}
});
};
const flush = () => {
const items = calls.slice();
calls.length = 0;
if (!this.worker) {
return;
}
const id = this.msgId++;
this.worker.postMessage({
id,
method,
calls: items.map(item => item.args)
});
this.pendingCalls.set(id, items);
};
return (...args) => push(args);
},
invoke(method, ...args) {
return this.task(method)(...args);
},
_onMessage({
data: result
}) {
const items = this.pendingCalls.get(result.id);
this.pendingCalls.delete(result.id);
if (!items) {
return;
}
if (!this.worker) {
return;
}
result.results.forEach((resultData, i) => {
const {
resolve,
reject
} = items[i];
if (resultData.error) {
const err = new Error(resultData.message);
err.metadata = resultData.metadata;
reject(err);
} else {
resolve(resultData.response);
}
});
}
};
function workerHandler(publicInterface) {
return function (msg) {
const {
id,
method,
calls
} = msg.data;
Promise.all(calls.map(args => {
try {
const response = publicInterface[method].apply(undefined, args);
if (response instanceof Promise) {
return response.then(val => ({
response: val
}), err => asErrorMessage(err));
}
return {
response
};
} catch (error) {
return asErrorMessage(error);
}
})).then(results => {
globalThis.postMessage({
id,
results
});
});
};
}
function asErrorMessage(error) {
if (typeof error === "object" && error && "message" in error) {
// Error can't be sent via postMessage, so be sure to convert to
// string.
return {
error: true,
message: error.message,
metadata: error.metadata
};
}
return {
error: true,
message: error == null ? error : error.toString(),
metadata: undefined
};
}
module.exports = {
WorkerDispatcher,
workerHandler
};
/***/ }),
/***/ 581:
@ -3366,17 +3318,13 @@ module.exports = __webpack_require__(901);
var _prettyFast = _interopRequireDefault(__webpack_require__(902));
var _devtoolsUtils = __webpack_require__(560);
var _workerUtils = __webpack_require__(1059);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/* 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/>. */
const {
workerHandler
} = _devtoolsUtils.workerUtils;
function prettyPrint({
url,
indent,
@ -3414,7 +3362,7 @@ function invertMappings(mappings) {
});
}
self.onmessage = workerHandler({
self.onmessage = (0, _workerUtils.workerHandler)({
prettyPrint
});

View File

@ -79,66 +79,15 @@ return /******/ (function(modules) { // webpackBootstrap
/************************************************************************/
/******/ ({
/***/ 560:
/***/ 1059:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
const networkRequest = __webpack_require__(567);
const workerUtils = __webpack_require__(568);
module.exports = {
networkRequest,
workerUtils
};
/***/ }),
/***/ 567:
/***/ (function(module, exports) {
/* 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/>. */
function networkRequest(url, opts) {
const UNSUPPORTED_PROTOCOLS = ["chrome://", "resource://"];
if (UNSUPPORTED_PROTOCOLS.some(protocol => url.startsWith(protocol))) {
return Promise.reject(`unsupported protocol for sourcemap request ${url}`);
}
return fetch(url, {
cache: opts.loadFromCache ? "default" : "no-cache"
}).then(res => {
if (res.status >= 200 && res.status < 300) {
if (res.headers.get("Content-Type") === "application/wasm") {
return res.arrayBuffer().then(buffer => ({
content: buffer,
isDwarf: true
}));
}
return res.text().then(text => ({
content: text
}));
}
return Promise.reject(`request failed with status ${res.status}`);
});
}
module.exports = networkRequest;
/***/ }),
/***/ 568:
/***/ (function(module, exports) {
/* 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/>. */
function WorkerDispatcher() {
this.msgId = 1;
this.worker = null; // Map of message ids -> promise resolution functions, for dispatching worker responses
@ -296,12 +245,15 @@ function asErrorMessage(error) {
message: error == null ? error : error.toString(),
metadata: undefined
};
}
} // Might be loaded within a worker thread where `module` isn't available.
module.exports = {
WorkerDispatcher,
workerHandler
};
if (true) {
module.exports = {
WorkerDispatcher,
workerHandler
};
}
/***/ }),
@ -574,17 +526,14 @@ var _getMatches = _interopRequireDefault(__webpack_require__(701));
var _projectSearch = __webpack_require__(909);
var _devtoolsUtils = __webpack_require__(560);
var _workerUtils = __webpack_require__(1059);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/* 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/>. */
const {
workerHandler
} = _devtoolsUtils.workerUtils;
self.onmessage = workerHandler({
self.onmessage = (0, _workerUtils.workerHandler)({
getMatches: _getMatches.default,
findSourceMatches: _projectSearch.findSourceMatches
});

View File

@ -93,232 +93,6 @@ module.exports = __WEBPACK_EXTERNAL_MODULE_112__;
/***/ }),
/***/ 560:
/***/ (function(module, exports, __webpack_require__) {
/* 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/>. */
const networkRequest = __webpack_require__(567);
const workerUtils = __webpack_require__(568);
module.exports = {
networkRequest,
workerUtils
};
/***/ }),
/***/ 567:
/***/ (function(module, exports) {
/* 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/>. */
function networkRequest(url, opts) {
const UNSUPPORTED_PROTOCOLS = ["chrome://", "resource://"];
if (UNSUPPORTED_PROTOCOLS.some(protocol => url.startsWith(protocol))) {
return Promise.reject(`unsupported protocol for sourcemap request ${url}`);
}
return fetch(url, {
cache: opts.loadFromCache ? "default" : "no-cache"
}).then(res => {
if (res.status >= 200 && res.status < 300) {
if (res.headers.get("Content-Type") === "application/wasm") {
return res.arrayBuffer().then(buffer => ({
content: buffer,
isDwarf: true
}));
}
return res.text().then(text => ({
content: text
}));
}
return Promise.reject(`request failed with status ${res.status}`);
});
}
module.exports = networkRequest;
/***/ }),
/***/ 568:
/***/ (function(module, exports) {
/* 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/>. */
function WorkerDispatcher() {
this.msgId = 1;
this.worker = null; // Map of message ids -> promise resolution functions, for dispatching worker responses
this.pendingCalls = new Map();
this._onMessage = this._onMessage.bind(this);
}
WorkerDispatcher.prototype = {
start(url, win = window) {
this.worker = new win.Worker(url);
this.worker.onerror = err => {
console.error(`Error in worker ${url}`, err.message);
};
this.worker.addEventListener("message", this._onMessage);
},
stop() {
if (!this.worker) {
return;
}
this.worker.removeEventListener("message", this._onMessage);
this.worker.terminate();
this.worker = null;
this.pendingCalls.clear();
},
task(method, {
queue = false
} = {}) {
const calls = [];
const push = args => {
return new Promise((resolve, reject) => {
if (queue && calls.length === 0) {
Promise.resolve().then(flush);
}
calls.push({
args,
resolve,
reject
});
if (!queue) {
flush();
}
});
};
const flush = () => {
const items = calls.slice();
calls.length = 0;
if (!this.worker) {
return;
}
const id = this.msgId++;
this.worker.postMessage({
id,
method,
calls: items.map(item => item.args)
});
this.pendingCalls.set(id, items);
};
return (...args) => push(args);
},
invoke(method, ...args) {
return this.task(method)(...args);
},
_onMessage({
data: result
}) {
const items = this.pendingCalls.get(result.id);
this.pendingCalls.delete(result.id);
if (!items) {
return;
}
if (!this.worker) {
return;
}
result.results.forEach((resultData, i) => {
const {
resolve,
reject
} = items[i];
if (resultData.error) {
const err = new Error(resultData.message);
err.metadata = resultData.metadata;
reject(err);
} else {
resolve(resultData.response);
}
});
}
};
function workerHandler(publicInterface) {
return function (msg) {
const {
id,
method,
calls
} = msg.data;
Promise.all(calls.map(args => {
try {
const response = publicInterface[method].apply(undefined, args);
if (response instanceof Promise) {
return response.then(val => ({
response: val
}), err => asErrorMessage(err));
}
return {
response
};
} catch (error) {
return asErrorMessage(error);
}
})).then(results => {
globalThis.postMessage({
id,
results
});
});
};
}
function asErrorMessage(error) {
if (typeof error === "object" && error && "message" in error) {
// Error can't be sent via postMessage, so be sure to convert to
// string.
return {
error: true,
message: error.message,
metadata: error.metadata
};
}
return {
error: true,
message: error == null ? error : error.toString(),
metadata: undefined
};
}
module.exports = {
WorkerDispatcher,
workerHandler
};
/***/ }),
/***/ 6:
/***/ (function(module, exports) {
@ -1283,8 +1057,6 @@ Object.defineProperty(exports, "__esModule", {
});
exports.vendored = void 0;
var devtoolsUtils = _interopRequireWildcard(__webpack_require__(560));
var fuzzaldrinPlus = _interopRequireWildcard(__webpack_require__(931));
var transition = _interopRequireWildcard(__webpack_require__(934));
@ -1321,7 +1093,6 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
// !!! Should remain synchronized with .babel/transform-mc.js !!!
const vendored = {
classnames: _classnames.default,
"devtools-utils": devtoolsUtils,
"fuzzaldrin-plus": fuzzaldrinPlus,
"react-aria-components/src/tabs": reactAriaComponentsTabs,
"react-transition-group/Transition": transition

View File

@ -18,7 +18,6 @@
},
"dependencies": {
"babel-plugin-transform-async-to-generator": "^6.22.0",
"devtools-utils": "0.0.14",
"devtools-wasm-dwarf": "0.0.1",
"md5": "^2.2.1",
"regenerator-runtime": "^0.13.0",

View File

@ -2,9 +2,7 @@
* 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/>. */
const {
workerUtils: { WorkerDispatcher },
} = require("devtools-utils");
const { WorkerDispatcher } = require("devtools/client/shared/worker-utils");
export const dispatcher = new WorkerDispatcher();

View File

@ -7,7 +7,7 @@
* @module utils/source-map-worker
*/
const { networkRequest } = require("devtools-utils");
const { networkRequest } = require("./utils/network-request");
const { SourceMapConsumer, SourceMapGenerator } = require("source-map");
const { createConsumer } = require("./utils/createConsumer");

View File

@ -30,10 +30,12 @@ async function setupBundleFixtureAndData(name) {
sourceMapBaseURL: `http://example.com/${name}.js`,
};
require("devtools-utils/src/network-request").mockImplementationOnce(() => {
const content = getMap(`fixtures/${name}.js.map`);
return { content };
});
require("../utils/network-request").networkRequest.mockImplementationOnce(
() => {
const content = getMap(`fixtures/${name}.js.map`);
return { content };
}
);
return getOriginalURLs(source);
}

View File

@ -2,7 +2,7 @@
* 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/>. */
jest.mock("devtools-utils/src/network-request");
jest.mock("../utils/network-request");
const {
getOriginalLocation,
getGeneratedLocation,

View File

@ -2,8 +2,8 @@
* 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/>. */
jest.mock("devtools-utils/src/network-request");
const networkRequest = require("devtools-utils/src/network-request");
jest.mock("../utils/network-request");
const { networkRequest } = require("../utils/network-request");
const {
getOriginalURLs,

View File

@ -7,7 +7,7 @@
const { WasmRemap } = require("../utils/wasmRemap");
const { createConsumer } = require("../utils/createConsumer");
jest.mock("devtools-utils/src/network-request");
jest.mock("../utils/network-request");
describe("wasm source maps", () => {
test("smoke test", async () => {
@ -96,16 +96,18 @@ describe("wasm source maps", () => {
isWasm: true,
};
require("devtools-utils/src/network-request").mockImplementationOnce(() => {
const content = JSON.stringify({
version: 3,
file: "min.js",
names: [],
sources: ["one.js"],
mappings: "CAAC,IAAM",
});
return { content };
});
require("../utils/network-request").networkRequest.mockImplementationOnce(
() => {
const content = JSON.stringify({
version: 3,
file: "min.js",
names: [],
sources: ["one.js"],
mappings: "CAAC,IAAM",
});
return { content };
}
);
const { getOriginalURLs, getOriginalLocation } = require("../source-map");

View File

@ -2,7 +2,8 @@
* 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/>. */
const { networkRequest } = require("devtools-utils");
const { networkRequest } = require("./network-request");
const { getSourceMap, setSourceMap } = require("./sourceMapRequests");
const { WasmRemap } = require("./wasmRemap");
const { convertToJSON } = require("devtools-wasm-dwarf");

View File

@ -32,4 +32,4 @@ function networkRequest(url, opts) {
]);
}
module.exports = networkRequest;
module.exports = { networkRequest };

View File

@ -24,4 +24,4 @@ function networkRequest(url, opts) {
});
}
module.exports = networkRequest;
module.exports = { networkRequest };

View File

@ -21,9 +21,7 @@ const {
const { getOriginalStackFrames } = require("./utils/getOriginalStackFrames");
const { setAssetRootURL } = require("./utils/assetRoot");
const {
workerUtils: { workerHandler },
} = require("devtools-utils");
const { workerHandler } = require("devtools/client/shared/worker-utils");
// The interface is implemented in source-map to be
// easier to unit test.

View File

@ -1,3 +0,0 @@
{
"presets": ["stage-3"]
}

View File

@ -1,362 +0,0 @@
Mozilla Public License, version 2.0
1. Definitions
1.1. "Contributor"
means each individual or legal entity that creates, contributes to the
creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used by a
Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached the
notice in Exhibit A, the Executable Form of such Source Code Form, and
Modifications of such Source Code Form, in each case including portions
thereof.
1.5. "Incompatible With Secondary Licenses"
means
a. that the initial Contributor has attached the notice described in
Exhibit B to the Covered Software; or
b. that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the terms of
a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in a
separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible, whether
at the time of the initial grant or subsequently, any and all of the
rights conveyed by this License.
1.10. "Modifications"
means any of the following:
a. any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered Software; or
b. any new file in Source Code Form that contains any Covered Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the License,
by the making, using, selling, offering for sale, having made, import,
or transfer of either its Contributions or its Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU Lesser
General Public License, Version 2.1, the GNU Affero General Public
License, Version 3.0, or any later versions of those licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that controls, is
controlled by, or is under common control with You. For purposes of this
definition, "control" means (a) the power, direct or indirect, to cause
the direction or management of such entity, whether by contract or
otherwise, or (b) ownership of more than fifty percent (50%) of the
outstanding shares or beneficial ownership of such entity.
2. License Grants and Conditions
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
a. under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
b. under Patent Claims of such Contributor to make, use, sell, offer for
sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
a. for any code that a Contributor has removed from Covered Software; or
b. for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
c. under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights to
grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
Section 2.1.
3. Responsibilities
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
a. such Covered Software must also be made available in Source Code Form,
as described in Section 3.1, and You must inform recipients of the
Executable Form how they can obtain a copy of such Source Code Form by
reasonable means in a timely manner, at a charge no more than the cost
of distribution to the recipient; and
b. You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter the
recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty, or
limitations of liability) contained within the Source Code Form of the
Covered Software, except that You may alter any license notices to the
extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
If it is impossible for You to comply with any of the terms of this License
with respect to some or all of the Covered Software due to statute,
judicial order, or regulation then You must: (a) comply with the terms of
this License to the maximum extent possible; and (b) describe the
limitations and the code they affect. Such description must be placed in a
text file included with all distributions of the Covered Software under
this License. Except to the extent prohibited by statute or regulation,
such description must be sufficiently detailed for a recipient of ordinary
skill to be able to understand it.
5. Termination
5.1. The rights granted under this License will terminate automatically if You
fail to comply with any of its terms. However, if You become compliant,
then the rights granted under this License from a particular Contributor
are reinstated (a) provisionally, unless and until such Contributor
explicitly and finally terminates Your grants, and (b) on an ongoing
basis, if such Contributor fails to notify You of the non-compliance by
some reasonable means prior to 60 days after You have come back into
compliance. Moreover, Your grants from a particular Contributor are
reinstated on an ongoing basis if such Contributor notifies You of the
non-compliance by some reasonable means, this is the first time You have
received notice of non-compliance with this License from such
Contributor, and You become compliant prior to 30 days after Your receipt
of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
license agreements (excluding distributors and resellers) which have been
validly granted by You or Your distributors under this License prior to
termination shall survive termination.
6. Disclaimer of Warranty
Covered Software is provided under this License on an "as is" basis,
without warranty of any kind, either expressed, implied, or statutory,
including, without limitation, warranties that the Covered Software is free
of defects, merchantable, fit for a particular purpose or non-infringing.
The entire risk as to the quality and performance of the Covered Software
is with You. Should any Covered Software prove defective in any respect,
You (not any Contributor) assume the cost of any necessary servicing,
repair, or correction. This disclaimer of warranty constitutes an essential
part of this License. No use of any Covered Software is authorized under
this License except under this disclaimer.
7. Limitation of Liability
Under no circumstances and under no legal theory, whether tort (including
negligence), contract, or otherwise, shall any Contributor, or anyone who
distributes Covered Software as permitted above, be liable to You for any
direct, indirect, special, incidental, or consequential damages of any
character including, without limitation, damages for lost profits, loss of
goodwill, work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses, even if such party shall have been
informed of the possibility of such damages. This limitation of liability
shall not apply to liability for death or personal injury resulting from
such party's negligence to the extent applicable law prohibits such
limitation. Some jurisdictions do not allow the exclusion or limitation of
incidental or consequential damages, so this exclusion and limitation may
not apply to You.
8. Litigation
Any litigation relating to this License may be brought only in the courts
of a jurisdiction where the defendant maintains its principal place of
business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions. Nothing
in this Section shall prevent a party's ability to bring cross-claims or
counter-claims.
9. Miscellaneous
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides that
the language of a contract shall be construed against the drafter shall not
be used to construe this License against a Contributor.
10. Versions of the License
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses If You choose to distribute Source Code Form that is
Incompatible With Secondary Licenses under the terms of this version of
the License, the notice described in Exhibit B of this License must be
attached.
Exhibit A - Source Code Form License Notice
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/.
If it is not possible or desirable to put the notice in a particular file,
then You may include the notice in a location (such as a LICENSE file in a
relevant directory) where a recipient would be likely to look for such a
notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
This Source Code Form is "Incompatible
With Secondary Licenses", as defined by
the Mozilla Public License, v. 2.0.

View File

@ -1,4 +0,0 @@
## Devtools Utils
* _networkRequest_ - CORS network requests
* _workerUtils_ - worker task communication

View File

@ -1,11 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
const networkRequest = require("./src/network-request");
const workerUtils = require("./src/worker-utils");
module.exports = {
networkRequest,
workerUtils,
};

View File

@ -1,16 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
const { resolve } = require("path");
const rootDir = resolve(__dirname, "src");
module.exports = {
rootDir,
displayName: "devtools-utils test",
testMatch: ["**/tests/**/*.js"],
testPathIgnorePatterns: [],
testURL: "http://localhost/",
transformIgnorePatterns: [],
setupFiles: [],
moduleNameMapper: {},
};

View File

@ -1,15 +0,0 @@
{
"name": "devtools-utils",
"version": "0.0.14",
"description": "DevTools Utils",
"main": "index.js",
"scripts": {
"license-check": "devtools-license-check",
"test": "jest --projects jest.config.js"
},
"author": "",
"license": "MPL-2.0",
"dependencies": {
"babel-preset-stage-3": "^6.22.0"
}
}

View File

@ -1,5 +0,0 @@
{
"globals": {
"jest": true
}
}

View File

@ -1,55 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
jest.useFakeTimers();
const networkRequest = require("../network-request");
describe("network request", () => {
beforeEach(() => {
global.fetch = jest.fn();
});
it("successful fetch", async () => {
global.fetch.mockImplementation(async () => ({
status: 200,
headers: { get: () => "application/json" },
text: async () => "Yay",
}));
const res = await networkRequest("foo");
expect(res).toEqual({ content: "Yay" });
});
it("wasm successful fetch", async () => {
global.fetch.mockImplementation(async () => ({
status: 200,
headers: { get: () => "application/wasm" },
arrayBuffer: async () => "Yay",
}));
const res = await networkRequest("foo");
expect(res).toEqual({ content: "Yay", isDwarf: true });
});
it("failed fetch", async () => {
global.fetch.mockImplementation(async () => ({
status: 400,
headers: { get: () => "application/json" },
text: async () => "Sad",
}));
await expect(networkRequest("foo")).rejects.toThrow(
expect.objectContaining({ message: "failed to request foo" })
);
});
it("timed out fetch", async () => {
global.fetch.mockImplementation(async () => {});
// eslint-disable-next-line jest/valid-expect-in-promise
networkRequest("foo").catch(e => {
expect(e.message).toEqual("Connect timeout error");
});
jest.runAllTimers();
});
});

View File

@ -1,128 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
const { WorkerDispatcher, workerHandler } = require("../worker-utils");
describe("worker utils", () => {
it("starts a worker", () => {
const dispatcher = new WorkerDispatcher();
global.Worker = jest.fn(function() {
this.addEventListener = jest.fn();
});
dispatcher.start("foo", globalThis);
expect(dispatcher.worker).toEqual(global.Worker.mock.instances[0]);
});
it("stops a worker", () => {
const dispatcher = new WorkerDispatcher();
const terminateMock = jest.fn();
global.Worker = jest.fn(() => ({
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
terminate: terminateMock,
}));
dispatcher.start("", globalThis);
dispatcher.stop();
expect(dispatcher.worker).toEqual(null);
expect(terminateMock.mock.calls).toHaveLength(1);
});
it("dispatches a task", () => {
const dispatcher = new WorkerDispatcher();
const postMessageMock = jest.fn();
const addEventListenerMock = jest.fn();
global.Worker = jest.fn(() => {
return {
postMessage: postMessageMock,
addEventListener: addEventListenerMock,
};
});
dispatcher.start("", globalThis);
const task = dispatcher.task("foo");
task("bar");
const postMessageMockCall = postMessageMock.mock.calls[0][0];
expect(postMessageMockCall).toEqual({
calls: [["bar"]],
id: 1,
method: "foo",
});
expect(addEventListenerMock.mock.calls).toHaveLength(1);
});
it("dispatches a queued task", async () => {
const dispatcher = new WorkerDispatcher();
let postMessageMock;
const postMessagePromise = new Promise(resolve => {
postMessageMock = jest.fn(resolve);
});
const addEventListenerMock = jest.fn();
global.Worker = jest.fn(() => {
return {
postMessage: postMessageMock,
addEventListener: addEventListenerMock,
};
});
dispatcher.start("", globalThis);
const task = dispatcher.task("foo", { queue: true });
task("bar");
task("baz");
expect(postMessageMock).not.toHaveBeenCalled();
await postMessagePromise;
const postMessageMockCall = postMessageMock.mock.calls[0][0];
expect(postMessageMockCall).toEqual({
calls: [["bar"], ["baz"]],
id: 1,
method: "foo",
});
expect(addEventListenerMock.mock.calls).toHaveLength(1);
});
it("test workerHandler error case", async () => {
let postMessageMock;
const postMessagePromise = new Promise(resolve => {
postMessageMock = jest.fn(resolve);
});
globalThis.postMessage = postMessageMock;
const callee = {
doSomething: () => {
throw new Error("failed");
},
};
const handler = workerHandler(callee);
handler({ data: { id: 53, method: "doSomething", calls: [[]] } });
await postMessagePromise;
expect(postMessageMock.mock.calls[0][0]).toEqual({
id: 53,
results: [
{
error: true,
message: "failed",
metadata: undefined,
},
],
});
});
});

View File

@ -14,7 +14,6 @@
* Both are fine, but cannot be mixed for the same module.
*/
import * as devtoolsUtils from "devtools-utils";
import * as fuzzaldrinPlus from "fuzzaldrin-plus";
import * as transition from "react-transition-group/Transition";
import * as reactAriaComponentsTabs from "react-aria-components/src/tabs";
@ -28,7 +27,6 @@ import classnames from "classnames";
// !!! Should remain synchronized with .babel/transform-mc.js !!!
export const vendored = {
classnames,
"devtools-utils": devtoolsUtils,
"fuzzaldrin-plus": fuzzaldrinPlus,
"react-aria-components/src/tabs": reactAriaComponentsTabs,
"react-transition-group/Transition": transition,

View File

@ -2,8 +2,7 @@
* 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/>. */
import { workerUtils } from "devtools-utils";
const { WorkerDispatcher } = workerUtils;
import { WorkerDispatcher } from "devtools/client/shared/worker-utils";
export class ParserDispatcher extends WorkerDispatcher {
async findOutOfScopeLocations(sourceId, position) {

View File

@ -11,8 +11,7 @@ import { getNextStep } from "./steps";
import { hasSyntaxError } from "./validate";
import mapExpression from "./mapExpression";
import { workerUtils } from "devtools-utils";
const { workerHandler } = workerUtils;
import { workerHandler } from "devtools/client/shared/worker-utils";
function clearState() {
clearASTs();

View File

@ -3,8 +3,7 @@
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
import { prefs } from "../../utils/prefs";
import { workerUtils } from "devtools-utils";
const { WorkerDispatcher } = workerUtils;
import { WorkerDispatcher } from "devtools/client/shared/worker-utils";
let dispatcher;
let workerPath;

View File

@ -4,8 +4,7 @@
import prettyFast from "pretty-fast";
import { workerUtils } from "devtools-utils";
const { workerHandler } = workerUtils;
import { workerHandler } from "devtools/client/shared/worker-utils";
function prettyPrint({ url, indent, sourceText }) {
const prettified = prettyFast(sourceText, {

View File

@ -2,8 +2,7 @@
* 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/>. */
import { workerUtils } from "devtools-utils";
const { WorkerDispatcher } = workerUtils;
import { WorkerDispatcher } from "devtools/client/shared/worker-utils";
let startArgs;
let dispatcher;

View File

@ -4,7 +4,6 @@
import getMatches from "./get-matches";
import { findSourceMatches } from "./project-search";
import { workerUtils } from "devtools-utils";
const { workerHandler } = workerUtils;
import { workerHandler } from "devtools/client/shared/worker-utils";
self.onmessage = workerHandler({ getMatches, findSourceMatches });

View File

@ -60,8 +60,14 @@ module.exports = {
new ObjectRestSpreadPlugin(),
new ExtractTextPlugin("[name].css"),
new webpack.NormalModuleReplacementPlugin(
/.\/src\/network-request/,
"./src/privileged-network-request"
/\.\/utils\/network-request/,
"./utils/privileged-network-request"
),
// This additional NormalModuleReplacementPlugin is for files in the same
// folder as network-request which use require("./network-request");
new webpack.NormalModuleReplacementPlugin(
/\.\/network-request/,
"./privileged-network-request"
),
new webpack.DefinePlugin({
"process.env": {

View File

@ -36,11 +36,11 @@ add_task(async function() {
"resource://devtools/client/shared/vendor/fluent-react.js",
"resource://devtools/client/shared/vendor/react-dom.js",
"resource://devtools/client/shared/vendor/react.js",
"resource://devtools/client/debugger/dist/vendors.js",
"resource://devtools/client/shared/vendor/react-prop-types.js",
"resource://devtools/client/shared/vendor/react-dom-factories.js",
"resource://devtools/client/shared/vendor/react-redux.js",
"resource://devtools/client/shared/vendor/redux.js",
"resource://devtools/client/shared/worker-utils.js",
"resource://devtools/client/debugger/src/workers/parser/index.js",
"resource://devtools/client/shared/source-map/index.js",
"resource://devtools/client/shared/components/menu/MenuButton.js",

View File

@ -78,7 +78,7 @@ add_task(async function() {
// "resource://devtools/client/netmonitor/src/actions/messages.js",
// "resource://devtools/client/netmonitor/src/actions/search.js",
// "resource://devtools/client/netmonitor/src/workers/search/index.js",
// "resource://devtools/client/netmonitor/src/workers/worker-utils.js",
// "resource://devtools/client/shared/worker-utils",
// ];
// runDuplicatedModulesTest(loaders, allowedDupes);

View File

@ -79,7 +79,7 @@ function initUserSettings() {
try {
const [defaultTargetBrowsers, targetBrowsers] = await Promise.all([
UserSettings.getDefaultTargetBrowsers(),
UserSettings.getBrowsersList(),
UserSettings.getTargetBrowsers(),
]);

View File

@ -5,13 +5,13 @@
// Test for the default browsers of user settings.
const {
getDefaultTargetBrowsers,
getBrowsersList,
} = require("devtools/client/inspector/shared/compatibility-user-settings");
add_task(async () => {
info("Check whether each default browsers data are unique by id and status");
const defaultBrowsers = await getDefaultTargetBrowsers();
const defaultBrowsers = await getBrowsersList();
for (const target of defaultBrowsers) {
const count = defaultBrowsers.reduce(

View File

@ -216,8 +216,11 @@ class Rule {
*/
async getCompatibilityIssues() {
if (!this.compatibilityIssues) {
const targetBrowsers = await getTargetBrowsers();
const compatibility = await this.inspector.inspectorFront.getCompatibilityFront();
const [targetBrowsers, compatibility] = await Promise.all([
getTargetBrowsers(),
this.inspector.inspectorFront.getCompatibilityFront(),
]);
this.compatibilityIssues = await compatibility.getCSSDeclarationBlockIssues(
this.domRule.declarations,
targetBrowsers

View File

@ -18,12 +18,12 @@ const TARGET_BROWSERS = [
{
// Chrome doesn't need any prefix for both user-select and text-size-adjust.
id: "chrome",
version: "84",
status: "current",
},
{
// The safari_ios needs -webkit prefix for both properties.
id: "safari_ios",
version: "13",
status: "current",
},
];
@ -40,7 +40,6 @@ const TEST_URI = `
const TEST_DATA_INITIAL = [
{
selector: "div",
rules: [
{},
{
@ -61,7 +60,6 @@ const TEST_DATA_INITIAL = [
const TEST_DATA_FIX_USER_SELECT = [
{
selector: "div",
rules: [
{},
{
@ -83,7 +81,6 @@ const TEST_DATA_FIX_USER_SELECT = [
// still show an inline warning for its experimental status.
const TEST_DATA_FIX_EXPERIMENTAL_SUPPORTED = [
{
selector: "div",
rules: [
{},
{
@ -112,6 +109,11 @@ add_task(async function() {
await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
const { inspector, view } = await openRuleView();
// We're only looking for properties on this single node so select it here instead of
// passing `selector` to `runCSSCompatibilityTests` (otherwise addition requests are sent
// to the server and we may end up with pending promises when the toolbox closes).
await selectNode("div", inspector);
await runCSSCompatibilityTests(view, inspector, TEST_DATA_INITIAL);
info(

View File

@ -27,10 +27,16 @@ const TARGET_BROWSER_STATUS = ["esr", "current", "beta", "nightly"];
const TARGET_BROWSER_PREF = "devtools.inspector.compatibility.target-browsers";
/**
* Returns the full list of browsers in the RemoteSetting devtools-compatibility-browsers
* collection (which is a flat copy of MDN compat data), sorted by browser and version.
*
* @returns Promise<Array<Object>>
* @returns Promise<Array<Object>> : Objects in the array have the following shape:
* - {string} id: The browser id (e.g. `firefox`,`safari_ios`). Should be one of TARGET_BROWSER_ID
* - {string} name: The browser display name (e.g. `Firefox`,`Safari for IOS`, )
* - {string} version: The browser version (e.g. `99`,`15.3`, `1.0.4`, )
* - {string} status: The browser status (e.g. `current`,`beta`, ). Should be one of TARGET_BROWSER_STATUS
*/
async function getDefaultTargetBrowsers() {
async function getBrowsersList() {
const records = await RemoteSettings("devtools-compatibility-browsers", {
filterFunc: record => {
if (
@ -77,20 +83,56 @@ async function getDefaultTargetBrowsers() {
}
/**
* Returns the list of browsers for which we should check compatibility issues.
*
* @returns Promise<Array<Object>>
* @returns Promise<Array<Object>> : Objects in the array have the following shape:
* - {string} id: The browser id (e.g. `firefox`,`safari_ios`). Should be one of TARGET_BROWSER_ID
* - {string} name: The browser display name (e.g. `Firefox`,`Safari for IOS`, )
* - {string} version: The browser version (e.g. `99`,`15.3`, `1.0.4`, )
* - {string} status: The browser status (e.g. `current`,`beta`, ). Should be one of TARGET_BROWSER_STATUS
*/
async function getTargetBrowsers() {
const targetsString = Services.prefs.getCharPref(TARGET_BROWSER_PREF, "");
return targetsString ? JSON.parse(targetsString) : getDefaultTargetBrowsers();
const browsers = await getBrowsersList();
// If not value are stored in the pref, it means the user did not chose specific browsers,
// so we need to return the full list.
if (!targetsString) {
return browsers;
}
const selectedBrowsersAndStatuses = JSON.parse(targetsString);
return browsers.filter(
browser =>
!!selectedBrowsersAndStatuses.find(
({ id, status }) => browser.id == id && browser.status == status
)
);
}
function setTargetBrowsers(targets) {
Services.prefs.setCharPref(TARGET_BROWSER_PREF, JSON.stringify(targets));
/**
* Store the list of browser id and status that should be used for checking compatibility
* issues.
*
* @param {Object[]} browsers
* @param {string} browsers[].id: The browser id. Should be one of TARGET_BROWSER_ID
* @param {string} browsers[].status: The browser status. Should be one of TARGET_BROWSER_STATUS
*/
function setTargetBrowsers(browsers) {
Services.prefs.setCharPref(
TARGET_BROWSER_PREF,
JSON.stringify(
// Only store the id and the status
browsers.map(browser => ({
id: browser.id,
status: browser.status,
}))
)
);
}
module.exports = {
getDefaultTargetBrowsers,
getBrowsersList,
getTargetBrowsers,
setTargetBrowsers,
};

View File

@ -6,7 +6,3 @@
DIRS += [
"search",
]
DevToolsModules(
"worker-utils.js",
)

View File

@ -4,9 +4,7 @@
"use strict";
const {
WorkerDispatcher,
} = require("devtools/client/netmonitor/src/workers/worker-utils");
const { WorkerDispatcher } = require("devtools/client/shared/worker-utils");
let startArgs;
let dispatcher;

View File

@ -7,7 +7,7 @@
/* eslint-disable no-undef*/
importScripts(
"resource://devtools/client/netmonitor/src/workers/search/search.js",
"resource://devtools/client/netmonitor/src/workers/worker-utils.js"
"resource://devtools/client/shared/worker-utils.js"
);
// Implementation of search worker (runs in worker scope).

View File

@ -1,128 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
"use strict";
function WorkerDispatcher() {
this.msgId = 1;
this.worker = null;
}
WorkerDispatcher.prototype = {
start(url, win) {
this.worker = new win.Worker(url);
this.worker.onerror = () => {
console.error(`Error in worker ${url}`);
};
},
stop() {
if (!this.worker) {
return;
}
this.worker.terminate();
this.worker = null;
},
task(method, { queue = false } = {}) {
const calls = [];
const push = args => {
return new Promise((resolve, reject) => {
if (queue && calls.length === 0) {
Promise.resolve().then(flush);
}
calls.push([args, resolve, reject]);
if (!queue) {
flush();
}
});
};
const flush = () => {
const items = calls.slice();
calls.length = 0;
if (!this.worker) {
return;
}
const id = this.msgId++;
this.worker.postMessage({
id,
method,
calls: items.map(item => item[0]),
});
const listener = ({ data: result }) => {
if (result.id !== id) {
return;
}
if (!this.worker) {
return;
}
this.worker.removeEventListener("message", listener);
result.results.forEach((resultData, i) => {
const [, resolve, reject] = items[i];
if (resultData.error) {
reject(resultData.error);
} else {
resolve(resultData.response);
}
});
};
this.worker.addEventListener("message", listener);
};
return (...args) => push(args);
},
invoke(method, ...args) {
return this.task(method)(...args);
},
};
function workerHandler(publicInterface) {
return function(msg) {
const { id, method, calls } = msg.data;
Promise.all(
calls.map(args => {
try {
const response = publicInterface[method].apply(undefined, args);
if (response instanceof Promise) {
return response.then(
val => ({ response: val }),
// Error can't be sent via postMessage, so be sure to
// convert to string.
err => ({ error: err.toString() })
);
}
return { response };
} catch (error) {
// Error can't be sent via postMessage, so be sure to convert to
// string.
return { error: error.toString() };
}
})
).then(results => {
self.postMessage({ id, results });
});
};
}
// Might be loaded within a worker thread where `module` isn't available.
if (typeof module !== "undefined") {
module.exports = {
WorkerDispatcher,
workerHandler,
};
}

View File

@ -41,7 +41,6 @@ function isRequire(t, node) {
const VENDORS = [
"classnames",
"devtools-environment",
"devtools-utils",
"fuzzaldrin-plus",
"react-aria-components/src/tabs",
"react-transition-group/Transition",

View File

@ -56,6 +56,7 @@ DevToolsModules(
"view-source.js",
"WeakMapMap.js",
"webgl-utils.js",
"worker-utils.js",
"workers-listener.js",
"zoom-keys.js",
)

View File

@ -79,66 +79,15 @@ return /******/ (function(modules) { // webpackBootstrap
/************************************************************************/
/******/ ({
/***/ 560:
/***/ 1059:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
const networkRequest = __webpack_require__(567);
const workerUtils = __webpack_require__(568);
module.exports = {
networkRequest,
workerUtils
};
/***/ }),
/***/ 567:
/***/ (function(module, exports) {
/* 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/>. */
function networkRequest(url, opts) {
const UNSUPPORTED_PROTOCOLS = ["chrome://", "resource://"];
if (UNSUPPORTED_PROTOCOLS.some(protocol => url.startsWith(protocol))) {
return Promise.reject(`unsupported protocol for sourcemap request ${url}`);
}
return fetch(url, {
cache: opts.loadFromCache ? "default" : "no-cache"
}).then(res => {
if (res.status >= 200 && res.status < 300) {
if (res.headers.get("Content-Type") === "application/wasm") {
return res.arrayBuffer().then(buffer => ({
content: buffer,
isDwarf: true
}));
}
return res.text().then(text => ({
content: text
}));
}
return Promise.reject(`request failed with status ${res.status}`);
});
}
module.exports = networkRequest;
/***/ }),
/***/ 568:
/***/ (function(module, exports) {
/* 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/>. */
function WorkerDispatcher() {
this.msgId = 1;
this.worker = null; // Map of message ids -> promise resolution functions, for dispatching worker responses
@ -296,12 +245,15 @@ function asErrorMessage(error) {
message: error == null ? error : error.toString(),
metadata: undefined
};
}
} // Might be loaded within a worker thread where `module` isn't available.
module.exports = {
WorkerDispatcher,
workerHandler
};
if (true) {
module.exports = {
WorkerDispatcher,
workerHandler
};
}
/***/ }),
@ -798,10 +750,8 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
* 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/>. */
const {
workerUtils: {
WorkerDispatcher
}
} = __webpack_require__(560);
WorkerDispatcher
} = __webpack_require__(1059);
const dispatcher = new WorkerDispatcher();
exports.dispatcher = dispatcher;

View File

@ -79,72 +79,7 @@ return /******/ (function(modules) { // webpackBootstrap
/************************************************************************/
/******/ ({
/***/ 533:
/***/ (function(module, exports) {
module.exports =
(() => {
let factory;
function define(...args) {
if (factory) {
throw new Error("expected a single define call");
}
if (
args.length !== 2 ||
!Array.isArray(args[0]) ||
args[0].length !== 0 ||
typeof args[1] !== "function"
) {
throw new Error("whatwg-url had unexpected factory arguments.");
}
factory = args[1];
}
define.amd = true;
const existingDefine = Object.getOwnPropertyDescriptor(globalThis, "define");
globalThis.define = define;
let err;
try {
importScripts("resource://devtools/client/shared/vendor/whatwg-url.js");
if (!factory) {
throw new Error("Failed to load whatwg-url factory");
}
} finally {
if (existingDefine) {
Object.defineProperty(globalThis, "define", existingDefine);
} else {
delete globalThis.define;
}
}
return factory();
})()
;
/***/ }),
/***/ 560:
/***/ (function(module, exports, __webpack_require__) {
/* 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/>. */
const networkRequest = __webpack_require__(567);
const workerUtils = __webpack_require__(568);
module.exports = {
networkRequest,
workerUtils
};
/***/ }),
/***/ 567:
/***/ 1058:
/***/ (function(module, exports) {
/* This Source Code Form is subject to the terms of the Mozilla Public
@ -177,16 +112,21 @@ function networkRequest(url, opts) {
});
}
module.exports = networkRequest;
module.exports = {
networkRequest
};
/***/ }),
/***/ 568:
/***/ (function(module, exports) {
/***/ 1059:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* 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/>. */
function WorkerDispatcher() {
this.msgId = 1;
this.worker = null; // Map of message ids -> promise resolution functions, for dispatching worker responses
@ -344,12 +284,63 @@ function asErrorMessage(error) {
message: error == null ? error : error.toString(),
metadata: undefined
};
} // Might be loaded within a worker thread where `module` isn't available.
if (true) {
module.exports = {
WorkerDispatcher,
workerHandler
};
}
module.exports = {
WorkerDispatcher,
workerHandler
};
/***/ }),
/***/ 533:
/***/ (function(module, exports) {
module.exports =
(() => {
let factory;
function define(...args) {
if (factory) {
throw new Error("expected a single define call");
}
if (
args.length !== 2 ||
!Array.isArray(args[0]) ||
args[0].length !== 0 ||
typeof args[1] !== "function"
) {
throw new Error("whatwg-url had unexpected factory arguments.");
}
factory = args[1];
}
define.amd = true;
const existingDefine = Object.getOwnPropertyDescriptor(globalThis, "define");
globalThis.define = define;
let err;
try {
importScripts("resource://devtools/client/shared/vendor/whatwg-url.js");
if (!factory) {
throw new Error("Failed to load whatwg-url factory");
}
} finally {
if (existingDefine) {
Object.defineProperty(globalThis, "define", existingDefine);
} else {
delete globalThis.define;
}
}
return factory();
})()
;
/***/ }),
@ -2125,10 +2116,8 @@ const {
} = __webpack_require__(927);
const {
workerUtils: {
workerHandler
}
} = __webpack_require__(560); // The interface is implemented in source-map to be
workerHandler
} = __webpack_require__(1059); // The interface is implemented in source-map to be
// easier to unit test.
@ -2165,7 +2154,7 @@ self.onmessage = workerHandler({
*/
const {
networkRequest
} = __webpack_require__(560);
} = __webpack_require__(1058);
const {
SourceMapConsumer,
@ -4543,7 +4532,7 @@ module.exports = assert;
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
const {
networkRequest
} = __webpack_require__(560);
} = __webpack_require__(1058);
const {
getSourceMap,

View File

@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
"use strict";
function WorkerDispatcher() {
this.msgId = 1;
this.worker = null;
@ -139,7 +141,10 @@ function asErrorMessage(error) {
};
}
module.exports = {
WorkerDispatcher,
workerHandler,
};
// Might be loaded within a worker thread where `module` isn't available.
if (typeof module !== "undefined") {
module.exports = {
WorkerDispatcher,
workerHandler,
};
}

View File

@ -15,6 +15,16 @@ In order to download up-to-date data, you need to run the following commands:
- `yarn install --no-lockfile` and select the latest package version for the `@mdn/browser-compat-data` package
- `yarn update`
This should save the JSON files directly in `devtools/shared/compatibility/dataset/`.
This should save the `css-properties.json` JSON file directly in `devtools/shared/compatibility/dataset/`.
For the browsers data, we need to update the RemoteSettings collection.
While connected to the Mozilla Corporate VPN (See https://mana.mozilla.org/wiki/display/SD/VPN), log into https://settings-writer.stage.mozaws.net/v1/admin/#/buckets/main/collections/devtools-compatibility-browsers/records (Using `OpenID Connect (LDAP)`). Then copy the authentication header using the icon in the top toolbar.
You can then run `SERVER=stage AUTH='XXX' yarn update-rs-records`, replacing `XXX` with the value of the authentication header.
The logs should indicate if the collection was updated, and if so, provide a short summary of the modifications.
If the collection was updated, then run Firefox, and use the [RemoteSettings DevTools WebExtension](https://github.com/mozilla-extensions/remote-settings-devtools) to use the `Stage (preview)` environment.
Then open the compatibility panel and make sure that the updated browsers do appear in the `Settings` panel.
If everything looks right, you can run the same command, on the prod server this time: `SERVER=prod AUTH='XXX' yarn update-rs-records`
Check that all tests still pass. It is possible that changes in the structure or contents of the latest dataset will cause tests to fail. If that is the case, fix the tests. **Do not manually change the contents or structure of the local dataset** because any changes will be overwritten by the next update from the official dataset.

View File

@ -7,11 +7,10 @@
// This dataset needs to be manually synchronized periodically
// The subsets from the dataset required by the Compatibility panel are:
// * browsers: https://github.com/mdn/browser-compat-data/tree/master/browsers
// * css.properties: https://github.com/mdn/browser-compat-data/tree/master/css
// The MDN compatibility data is available as a node package ("@mdn/browser-compat-data").
// This node script downloads `browsers.json` and `css-properties.json` and updates the relevant files.
// The MDN compatibility data is available as a node package ("@mdn/browser-compat-data"),
// which is used here to update `../dataset/css-properties.json`.
/* global __dirname */
@ -19,7 +18,6 @@
const compatData = require("@mdn/browser-compat-data");
exportData(compatData.css.properties, "css-properties.json");
exportData(compatData.browsers, "browsers.json");
function exportData(data, fileName) {
const fs = require("fs");

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,5 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
"browsers.json",
"css-properties.json",
)

View File

@ -39,6 +39,7 @@ const BROWSER_BASED_DIRS = [
"resource://devtools/client/shared/source-map",
"resource://devtools/client/shared/redux",
"resource://devtools/client/shared/vendor",
"resource://devtools/client/shared/worker-utils",
];
const COMMON_LIBRARY_DIRS = ["resource://devtools/client/shared/vendor"];

View File

@ -2297,7 +2297,11 @@ bool nsContentUtils::ShouldResistFingerprinting(nsIChannel* aChannel) {
/* static */
bool nsContentUtils::UseStandinsForNativeColors() {
return ShouldResistFingerprinting() ||
return ShouldResistFingerprinting(
"we want to have consistent colors across the browser if RFP is "
"enabled, so we check the global preference"
"not excluding chrome browsers or webpages, so we call the legacy "
"RFP function to prevent that") ||
StaticPrefs::ui_use_standins_for_native_colors();
}

View File

@ -920,6 +920,7 @@ nsGlobalWindowInner::nsGlobalWindowInner(nsGlobalWindowOuter* aOuterWindow,
mHasSeenGamepadInput(false),
mHintedWasLoading(false),
mHasOpenedExternalProtocolFrame(false),
mScrollMarksOnHScrollbar(false),
mStorageAllowedReasonCache(0),
mSuspendDepth(0),
mFreezeDepth(0),
@ -7707,9 +7708,10 @@ ContentMediaController* nsGlobalWindowInner::GetContentMediaController() {
return mContentMediaController;
}
void nsGlobalWindowInner::SetScrollMarks(
const nsTArray<uint32_t>& aScrollMarks) {
void nsGlobalWindowInner::SetScrollMarks(const nsTArray<uint32_t>& aScrollMarks,
bool aOnHScrollbar) {
mScrollMarks.Assign(aScrollMarks);
mScrollMarksOnHScrollbar = aOnHScrollbar;
// Mark the scrollbar for repainting.
if (mDoc) {
@ -7717,7 +7719,7 @@ void nsGlobalWindowInner::SetScrollMarks(
if (presShell) {
nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
if (sf) {
sf->InvalidateVerticalScrollbar();
sf->InvalidateScrollbars();
}
}
}

View File

@ -1316,7 +1316,9 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
}
nsTArray<uint32_t>& GetScrollMarks() { return mScrollMarks; }
void SetScrollMarks(const nsTArray<uint32_t>& aScrollMarks);
bool GetScrollMarksOnHScrollbar() const { return mScrollMarksOnHScrollbar; }
void SetScrollMarks(const nsTArray<uint32_t>& aScrollMarks,
bool aOnHScrollbar);
// Don't use this value directly, call StorageAccess::StorageAllowedForWindow
// instead.
@ -1411,6 +1413,8 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
// activation once already. Only relevant for top windows.
bool mHasOpenedExternalProtocolFrame : 1;
bool mScrollMarksOnHScrollbar : 1;
nsCheapSet<nsUint32HashKey> mGamepadIndexSet;
nsRefPtrHashtable<nsGenericHashKey<mozilla::dom::GamepadHandle>,
mozilla::dom::Gamepad>

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Add a tag script to check delazification strategy</title>
</head>
<body>
<!-- <script id="watchme" src="file_delazification_strategy.js"></script> -->
</body>
</html>

View File

@ -0,0 +1,91 @@
function baz() {}
function bar() {}
function foo() {
bar();
}
foo();
// For testing, we require the script to be parsed off-htread. To schedule a
// script off-thread, we require the script to be at least 5 KB. Thus, here is
// one comment which is used to trick this heuristics:
//
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWWX0Oxoc;,.... ....,;coxO0XWWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWX0kdlc;'.. ..';:ldk0XWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWNKkoc,.. ..,cok0NWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKko:'. .':okKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKkl,. .,lkKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNOo;. .;oONMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXkl'. 'lkXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNOl' 'lONMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMWKo, ,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMNk:. .:kNMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMXx, ,xXMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMWXd' 'dXWMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMXd' 'dXMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMNx' 'xNMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMWO; ;OWMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMKl. .lXMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMWk, ,kWMMMMMMMMMMMMM
// MMMMMMMMMMMMXo. .lXMMMMMMMMMMMM
// MMMMMMMMMMW0; ...... ;0WMMMMMMMMMM
// MMMMMMMMMWk' ..,:loxxkOOkxdo:,. 'kWMMMMMMMMM
// MMMMMMMMWx. .':lxO000000000000000ko;. .xWMMMMMMMM
// MMMMMMMNd. .'cdk00000000000000000000000x;. ..',;ccloodddddddollc:;,'.. .dNMMMMMMM
// MMMMMMNd. ,dO000000000000000000000000000Oo' ..;coxk00000000000000000000000Okdlc;'. .dNMMMMMM
// MMMMMNd. .cO000000000000000000000000000000k; .;lxO0000000000000000000000000000000000Okoc,. .dWMMMMM
// MMMMWx. .'cO0000000000000000000000000000Oc. .;ok0000K00000000000000000000000000000000000000ko, .kWMMMM
// MMMMO' ,kK0000000000000000000000000000Oc. .:x000000000000000OkdoollcccllodxkO0000K00000000000k' 'OMMMM
// MMMK; .'lO000000000000000000000000000000Ol'... 'd000000000000Odl;'.. ..',:ldkO00000000KO, ;KMMM
// MMNo ,dkO0K00000000000000000000000000000000OOkxdoc;,,ck0000000000Oo;. .'ck000000KO; oNMM
// MMk. .o0000000000000000000000000000000000000000000000000000000000d, .o000000K0: .OMM
// MX: .o00000000000000000000000000000000000000000000000000000000Oc. c000000K0l. :XM
// Wx. .c00000000000000000000000000000000000000000000000000000000x, :OK000000o. .xW
// X: .:dxO00000000000000000000000000000000000000000000000000000Oo' ,k0000000d. :X
// O. .:xOO0000000000000000000000000000000000000000000000000000Ol. .x000000Kd. .O
// o ..'''''''',,:lx000000000000000000000000000000000000000000x, .lOOOOkkko. o
// ; .:x00000000000000000000000000000000000000000O:. ......... ;
// . 'd00000000000000000000000000000000000000000Ol. .
// . ,k000000000000000000000000000000000000000000o. .
// .o0000000000000000000000000000000000000000000d.
// c00000000000000000000000000000000000000000000o.
// ;OK0000000000000000000000000000000000000000000o.
// ,kK00000000000000000000000000000000000000000000xoc:,'..
// .x00000000000000000000000000000000000000000000000000Okxolc;,'..
// .d000000000000000K000000000000000000000000000000000000000000OOxdl:,..
// . .o00000000000000OO0000000000000000000000000000000000000000000000000Oxo:'. .
// . .l0000000000000Oc:k0000000000000000000000000000000000000000000000000000Oxl,. .
// ; c000000000000Kk, ,x000000000000000000000000000000xodkO00000000000000000000xc. ;
// o ..''.. :000000000000Kx' ,k0000000000000000000000000000Oc. ..';codkO000000000000000k:. o
// O. .,lxO00Okxl:,. :O000000000000d. ;k0000000000000000000000000000l. ..,cok0000000000000d' .O
// X: ,d000000000000kdc;. :O000000000000l. .c0000000000000000000000000000o. .;oO00000000000k; :X
// Wx. 'x00000000000000000Oxl;..:O00000000000O: .x000000000000000000000000000d. 'oO00000K0000k, .kW
// MX: c0000000000000000000000Oxk00000000000Kk, cO00000000000000000000000000d. ;k0000000000d. :XM
// MMO. :O000000000000000000000000000000000000x. cO00000000000000000000000000d. ,k000000000O: .OMM
// MMNo .d000000000000000000000000000000000000l. .d000000000000000000000000000o. cO000000000o. .oNMM
// MMMK; 'd00000000000000000000000000000000000c ,k000000000000000000000000000l. 'x000000000d. ;KMMM
// MMMMO' .lO000000000000000000000000000000000kl;. .o00000000000000000000000000K0c .d000000000d. 'OMMMM
// MMMMWx. ;x00000000000000000000000000000000000ko:lO000000000000000000000000000O; .x000000000d. .xWMMMM
// MMMMMWd. .ck00000000000000000000000000000000000000000000000000000000000000000k, ;kK00000000l. .dWMMMMM
// MMMMMMNd. .:x000000000000000000000000000000000000000000000000000000000000000d. .o000000000O; .dNMMMMMM
// MMMMMMMNd. .;dO000000000000000000000000000000000000000000000000000000000000l. .o0000000000d. .dNMMMMMMM
// MMMMMMMMWx. .,,'.. 'cx00000000000000000000000000000000000000000000000000000000KO: .;d0000000000x, .xWMMMMMMMM
// MMMMMMMMMWk' 'd00Oxdl;'. .,lx000000000000000000000000000000000000000000000000000000k' .:dO0000000000x, 'kWMMMMMMMMM
// MMMMMMMMMMW0; .:x000000kd:'. .,lk000000000000000000000000000000000000000000000000000d. ..,cdk0K000000000Oo. :0WMMMMMMMMMM
// MMMMMMMMMMMMXo. .,ok000000Odc. ,x000000000000000000000000000000000000000000000000000d,...'',;:cldxO000000000000K0d;. .oXMMMMMMMMMMMM
// MMMMMMMMMMMMMWk, .:x0000000Oo;.. .,oO000000000000000000000000000000000000000000000000000000OOOO00000000000000000000Od;. ,kWMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMXl. .;dO0000000Oxdoooxk0000000000OxxO0000000000000000000000000000000000000000000000000000000000000000Oxc' .lXMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMWO; 'lk00000000000000000000Oxc' ..:oxO0K000000000000000000000000000000000000000000000000000000Oko:'. ;OWMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMNx, .;ok00000000000000kdc'. ..;cdxO00000000000000000000000000000000000000000000Okxdoc;'. ,xNMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMXd' .,:ldxkkkkxdl:,. ..,:cldxkkkO000000000000Okkxdlc:;;;:::::;;;,,'... 'dXMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMXd' ...... ....'',,,,,,,,'.... 'dXWMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMXx,. ,xXMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMNk:. .:kNMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMWKo,. .,oKWMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNOl' 'lONMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXkl'. .'lkXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNOo;. .;oONMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKkl;. .,lkKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKko:'. .':okKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWNKkoc,.. ..,cok0NWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWX0kdlc;'.. ..';:ldk0XWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWWX0Oxoc;,.... ....,;coxO0XNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

View File

@ -755,6 +755,11 @@ support-files =
file_js_cache_save_after_load.js
file_js_cache_syntax_error.html
file_js_cache_syntax_error.js
[test_delazification_strategy.html]
skip-if = verify
support-files =
file_delazification_strategy.html
file_delazification_strategy.js
[test_setInterval_from_start.html]
[test_setInterval_uncatchable_exception.html]
skip-if = debug == false

View File

@ -0,0 +1,175 @@
<!DOCTYPE html>
<html>
<!-- https://bugzilla.mozilla.org/show_bug.cgi?id=1753709 -->
<!-- Script delazification strategy is not supposed to have any observable
side-effect. To make it observable, the ScriptLoader is instrumented to
trigger events on the script tag. These events are used to validate that
the strategy is used as execpected. This does not garantee that all
functions are delazified properly, but this should be checked in the JS
engine test suite.
-->
<head>
<meta charset="utf-8">
<title>Test for triggering eager delazification.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script type="application/javascript">
async function WaitForScriptTagEvent() {
var url = "file_delazification_strategy.html";
var iframe = document.createElement("iframe");
// Call the resolve function when the event is one of the expected events.
// This is made to be used by a promise and provided to event listeners.
function resolve_with_event(resolve, evt) {
// If we have multiple script tags in the loaded source, make sure
// we only watch a single one.
if (evt.target.id != "watchme")
return;
switch (evt.type) {
case "delazification_on_demand_only":
case "delazification_concurrent_depth_first":
case "delazification_parse_everything_eagerly":
resolve(evt.type.split('_').slice(1).join('_'));
break;
case "scriptloader_main_thread_compile":
resolve(evt.type);
break;
}
return;
}
// Create an event listener, which resolves a promise.
let log_event;
let scriptLoaderTrace = new Promise((resolve, reject) => {
log_event = resolve_with_event.bind(this, resolve);
});
// Wait until the iframe is fully loaded.
await new Promise(resolve => {
iframe.onload = resolve;
iframe.src = url;
document.body.appendChild(iframe);
});
// Register all events.
let events = [
"delazification_on_demand_only",
"delazification_concurrent_depth_first",
"delazification_parse_everything_eagerly",
"scriptloader_main_thread_compile"
];
let iwin = iframe.contentWindow;
for (let evt of events) {
iwin.addEventListener(evt, log_event);
}
// Add a script tag, which will trigger one of the previous events.
let script = document.createElement("script");
script.setAttribute("id", "watchme");
script.setAttribute("src", "file_delazification_strategy.js");
iframe.contentDocument.body.appendChild(script);
// Wait for the event emitted by ScriptLoader, while processing the
// previous script.
let result = await scriptLoaderTrace;
// Remove the events and the iframe.
for (let evt of events) {
iwin.removeEventListener(evt, log_event);
}
document.body.removeChild(iframe);
return result;
}
// Setting dom.expose_test_interfaces pref causes the
// nsScriptLoadRequest to fire event on script tags, with information
// about its internal state. The ScriptLoader source send events to
// trace these and resolve a promise with the path taken by the
// script loader.
//
// Setting dom.script_loader.bytecode_cache.enabled to false in order
// to prevent the bytecode cache to perturb this test case.
//
// Setting dom.script_loader.external_scripts.speculate_* are used to
// force off-main-thread compilation, while hoping that we have enough
// processors to run the test case
//
// Setting dom.delazification.* are used to select the delazification
// strategy and to check that it is well selected.
promise_test(async function() {
await SpecialPowers.pushPrefEnv({set: [
['dom.expose_test_interfaces', true],
['dom.script_loader.bytecode_cache.enabled', false],
['dom.script_loader.external_scripts.speculate_non_parser_inserted.enable', true],
['dom.script_loader.external_scripts.speculate_async.enabled', true],
['dom.script_loader.external_scripts.speculate_link_preload.enabled', true],
// Parse everything eagerly
['dom.script_loader.delazification.strategy', 255],
['dom.script_loader.delazification.max_size', 0],
['dom.script_loader.delazification.min_mem', 0],
]});
assert_equals(await WaitForScriptTagEvent(), "on_demand_only",
"[1] AttemptAsyncScriptCompile: On demand only");
}, "Check that max_size can disable delazification strategy");
promise_test(async function() {
await SpecialPowers.pushPrefEnv({set: [
['dom.expose_test_interfaces', true],
['dom.script_loader.bytecode_cache.enabled', false],
// Enable OffMainThread compilation for everything, and cross-fingers
// about the number of CPU.
['dom.script_loader.external_scripts.speculate_non_parser_inserted.enable', true],
['dom.script_loader.external_scripts.speculate_async.enabled', true],
['dom.script_loader.external_scripts.speculate_link_preload.enabled', true],
// Parse everything eagerly
['dom.script_loader.delazification.strategy', 255],
['dom.script_loader.delazification.max_size', 10485760],
// 4 TB should of RAM be enough.
['dom.script_loader.delazification.min_mem', 4096],
]});
assert_equals(await WaitForScriptTagEvent(), "on_demand_only",
"[2] AttemptAsyncScriptCompile: On demand only");
}, "Check that min_mem can disable delazification strategy");
promise_test(async function() {
await SpecialPowers.pushPrefEnv({set: [
['dom.expose_test_interfaces', true],
['dom.script_loader.bytecode_cache.enabled', false],
// Enable OffMainThread compilation for everything, and cross-fingers
// about the number of CPU.
['dom.script_loader.external_scripts.speculate_non_parser_inserted.enable', true],
['dom.script_loader.external_scripts.speculate_async.enabled', true],
['dom.script_loader.external_scripts.speculate_link_preload.enabled', true],
['dom.script_loader.delazification.max_size', 10485760],
['dom.script_loader.delazification.min_mem', 0],
]});
await SpecialPowers.pushPrefEnv({set: [
['dom.script_loader.delazification.strategy', 0],
]});
assert_equals(await WaitForScriptTagEvent(), "on_demand_only",
"[3] AttemptAsyncScriptCompile: On demand only");
await SpecialPowers.pushPrefEnv({set: [
['dom.script_loader.delazification.strategy', 1],
]});
assert_equals(await WaitForScriptTagEvent(), "concurrent_depth_first",
"[3] AttemptAsyncScriptCompile: Concurrent Depth First");
await SpecialPowers.pushPrefEnv({set: [
['dom.script_loader.delazification.strategy', 255],
]});
assert_equals(await WaitForScriptTagEvent(), "parse_everything_eagerly",
"[3] AttemptAsyncScriptCompile: Parse Everything Eagerly");
}, "Check enabling delazification strategy works");
done();
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1753709">Mozilla Bug 1753709</a>
</body>
</html>

View File

@ -627,7 +627,6 @@ DOMInterfaces = {
'PeerConnectionImpl': {
'nativeType': 'mozilla::PeerConnectionImpl',
'headerFile': 'PeerConnectionImpl.h',
'wrapperCache': False
},
'PerformanceResourceTiming' : {

View File

@ -501,9 +501,8 @@ class AdjustedTargetForShadow {
int32_t blurRadius = state.ShadowBlurRadius();
bounds.Inflate(blurRadius);
bounds.RoundOut();
bounds.ToIntRect(&mTempRect);
if (!mFinalTarget->CanCreateSimilarDrawTarget(mTempRect.Size(),
if (!bounds.ToIntRect(&mTempRect) ||
!mFinalTarget->CanCreateSimilarDrawTarget(mTempRect.Size(),
SurfaceFormat::B8G8R8A8)) {
mTarget = mFinalTarget;
mCtx = nullptr;
@ -595,7 +594,7 @@ class AdjustedTarget {
bounds = bounds.Intersect(*aBounds);
}
gfx::Rect boundsAfterFilter = BoundsAfterFilter(bounds, aCtx);
if (!aCtx->IsTargetValid()) {
if (!aCtx->IsTargetValid() || !boundsAfterFilter.IsFinite()) {
return;
}

View File

@ -179,17 +179,19 @@ void FetchService::FetchInstance::OnResponseEnd(
}
// Get PerformanceTimingData from FetchDriver.
IPCPerformanceTimingData ipcPerformanceTiming;
nsString initiatorType;
nsString entryName;
UniquePtr<PerformanceTimingData> performanceTiming(
mFetchDriver->GetPerformanceTimingData(initiatorType, entryName));
MOZ_ASSERT(performanceTiming);
if (performanceTiming != nullptr) {
ipcPerformanceTiming = performanceTiming->ToIPC();
}
initiatorType = u"navigation"_ns;
FetchServiceResponse response =
MakeTuple(std::move(mResponse), performanceTiming->ToIPC(), initiatorType,
entryName);
FetchServiceResponse response = MakeTuple(
std::move(mResponse), ipcPerformanceTiming, initiatorType, entryName);
// Resolve the FetchServiceResponsePromise
mResponsePromiseHolder.ResolveIfExists(std::move(response), __func__);

View File

@ -1132,10 +1132,9 @@ int32_t HTMLFormElement::CompareFormControlPosition(Element* aElement1,
/* static */
void HTMLFormElement::AssertDocumentOrder(
const nsTArray<nsGenericHTMLFormElement*>& aControls, nsIContent* aForm) {
// TODO: remove the return statement with bug 598468.
// TODO: remove the if directive with bug 598468.
// This is done to prevent asserts in some edge cases.
return;
# if 0
// Only iterate if aControls is not empty, since otherwise
// |aControls.Length() - 1| will be a very large unsigned number... not what
// we want here.
@ -1146,6 +1145,7 @@ void HTMLFormElement::AssertDocumentOrder(
"Form controls not ordered correctly");
}
}
# endif
}
/**
@ -1158,10 +1158,9 @@ void HTMLFormElement::AssertDocumentOrder(
void HTMLFormElement::AssertDocumentOrder(
const nsTArray<RefPtr<nsGenericHTMLFormElement>>& aControls,
nsIContent* aForm) {
// TODO: remove the return statement with bug 598468.
// TODO: remove the if directive with bug 598468.
// This is done to prevent asserts in some edge cases.
return;
# if 0
// Only iterate if aControls is not empty, since otherwise
// |aControls.Length() - 1| will be a very large unsigned number... not what
// we want here.
@ -1172,6 +1171,7 @@ void HTMLFormElement::AssertDocumentOrder(
"Form controls not ordered correctly");
}
}
# endif
}
#endif

View File

@ -28,11 +28,11 @@ void VsyncParent::UpdateVsyncSource(
}
if (mObservingVsync && mVsyncDispatcher) {
mVsyncDispatcher->RemoveChildRefreshTimer(this);
mVsyncDispatcher->RemoveVsyncObserver(this);
}
mVsyncDispatcher = mVsyncSource->GetRefreshTimerVsyncDispatcher();
if (mObservingVsync) {
mVsyncDispatcher->AddChildRefreshTimer(this);
mVsyncDispatcher->AddVsyncObserver(this);
}
}
@ -69,7 +69,7 @@ mozilla::ipc::IPCResult VsyncParent::RecvObserve() {
AssertIsOnInitialThread();
if (!mObservingVsync) {
if (mVsyncDispatcher) {
mVsyncDispatcher->AddChildRefreshTimer(this);
mVsyncDispatcher->AddVsyncObserver(this);
}
mObservingVsync = true;
return IPC_OK();
@ -81,7 +81,7 @@ mozilla::ipc::IPCResult VsyncParent::RecvUnobserve() {
AssertIsOnInitialThread();
if (mObservingVsync) {
if (mVsyncDispatcher) {
mVsyncDispatcher->RemoveChildRefreshTimer(this);
mVsyncDispatcher->RemoveVsyncObserver(this);
}
mObservingVsync = false;
return IPC_OK();
@ -93,7 +93,7 @@ void VsyncParent::ActorDestroy(ActorDestroyReason aActorDestroyReason) {
MOZ_ASSERT(!mDestroyed);
AssertIsOnInitialThread();
if (mObservingVsync && mVsyncDispatcher) {
mVsyncDispatcher->RemoveChildRefreshTimer(this);
mVsyncDispatcher->RemoveVsyncObserver(this);
}
mVsyncDispatcher = nullptr;
mDestroyed = true;

View File

@ -567,7 +567,7 @@ class RTCPeerConnection {
// Add a reference to the PeerConnection to global list (before init).
_globalPCList.addPC(this);
this._impl.initialize(observer, this._win, Services.tm.currentThread);
this._impl.initialize(observer, this._win);
this.setConfiguration(rtcConfig);

View File

@ -629,6 +629,10 @@ nsresult MediaTransportHandlerSTS::SetIceConfig(
mInitPromise->Then(
mStsThread, __func__,
[=, self = RefPtr<MediaTransportHandlerSTS>(this)]() {
if (!mIceCtx) {
CSFLogError(LOGTAG, "%s: mIceCtx is null", __FUNCTION__);
return;
}
NrIceCtx::Config config;
config.mPolicy = toNrIcePolicy(aIcePolicy);
if (config.mPolicy == NrIceCtx::ICE_POLICY_ALL && mForceNoHost) {

View File

@ -9,38 +9,45 @@
namespace mozilla {
PacketDumper::PacketDumper(PeerConnectionImpl* aPc) : mPc(aPc) {}
PacketDumper::PacketDumper(const std::string& aPcHandle) {
if (!aPcHandle.empty()) {
PeerConnectionWrapper pcw(aPcHandle);
mPc = pcw.impl();
/* static */
RefPtr<PacketDumper> PacketDumper::GetPacketDumper(
const std::string& aPcHandle) {
MOZ_ASSERT(NS_IsMainThread());
PeerConnectionWrapper pcw(aPcHandle);
if (pcw.impl()) {
return pcw.impl()->GetPacketDumper();
}
return new PacketDumper("");
}
PacketDumper::~PacketDumper() {
RefPtr<Runnable> pcDisposeRunnable = media::NewRunnableFrom(std::bind(
[](RefPtr<PeerConnectionImpl> pc) { return NS_OK; }, mPc.forget()));
NS_DispatchToMainThread(pcDisposeRunnable);
}
PacketDumper::PacketDumper(const std::string& aPcHandle)
: mPcHandle(aPcHandle),
mPacketDumpEnabled(false),
mPacketDumpFlagsMutex("Packet dump flags mutex") {}
void PacketDumper::Dump(size_t level, dom::mozPacketDumpType type, bool sending,
const void* data, size_t size) {
void PacketDumper::Dump(size_t aLevel, dom::mozPacketDumpType aType,
bool aSending, const void* aData, size_t aSize) {
// Optimization; avoids making a copy of the buffer, but we need to lock a
// mutex and check the flags. Could be optimized further, if we really want to
if (!mPc || !mPc->ShouldDumpPacket(level, type, sending)) {
if (!ShouldDumpPacket(aLevel, aType, aSending)) {
return;
}
RefPtr<PeerConnectionImpl> pc = mPc;
UniquePtr<uint8_t[]> ownedPacket = MakeUnique<uint8_t[]>(size);
memcpy(ownedPacket.get(), data, size);
UniquePtr<uint8_t[]> ownedPacket = MakeUnique<uint8_t[]>(aSize);
memcpy(ownedPacket.get(), aData, aSize);
RefPtr<Runnable> dumpRunnable = media::NewRunnableFrom(std::bind(
[pc, level, type, sending,
size](UniquePtr<uint8_t[]>& packet) -> nsresult {
pc->DumpPacket_m(level, type, sending, packet, size);
[this, self = RefPtr<PacketDumper>(this), aLevel, aType, aSending,
aSize](UniquePtr<uint8_t[]>& aPacket) -> nsresult {
// Check again; packet dump might have been disabled since the dispatch
if (ShouldDumpPacket(aLevel, aType, aSending)) {
PeerConnectionWrapper pcw(mPcHandle);
RefPtr<PeerConnectionImpl> pc = pcw.impl();
if (pc) {
pc->DumpPacket_m(aLevel, aType, aSending, aPacket, aSize);
}
}
return NS_OK;
},
std::move(ownedPacket)));
@ -48,4 +55,70 @@ void PacketDumper::Dump(size_t level, dom::mozPacketDumpType type, bool sending,
NS_DispatchToMainThread(dumpRunnable);
}
nsresult PacketDumper::EnablePacketDump(unsigned long aLevel,
dom::mozPacketDumpType aType,
bool aSending) {
mPacketDumpEnabled = true;
std::vector<unsigned>* packetDumpFlags;
if (aSending) {
packetDumpFlags = &mSendPacketDumpFlags;
} else {
packetDumpFlags = &mRecvPacketDumpFlags;
}
unsigned flag = 1 << (unsigned)aType;
MutexAutoLock lock(mPacketDumpFlagsMutex);
if (aLevel >= packetDumpFlags->size()) {
packetDumpFlags->resize(aLevel + 1);
}
(*packetDumpFlags)[aLevel] |= flag;
return NS_OK;
}
nsresult PacketDumper::DisablePacketDump(unsigned long aLevel,
dom::mozPacketDumpType aType,
bool aSending) {
std::vector<unsigned>* packetDumpFlags;
if (aSending) {
packetDumpFlags = &mSendPacketDumpFlags;
} else {
packetDumpFlags = &mRecvPacketDumpFlags;
}
unsigned flag = 1 << (unsigned)aType;
MutexAutoLock lock(mPacketDumpFlagsMutex);
if (aLevel < packetDumpFlags->size()) {
(*packetDumpFlags)[aLevel] &= ~flag;
}
return NS_OK;
}
bool PacketDumper::ShouldDumpPacket(size_t aLevel, dom::mozPacketDumpType aType,
bool aSending) const {
if (!mPacketDumpEnabled) {
return false;
}
MutexAutoLock lock(mPacketDumpFlagsMutex);
const std::vector<unsigned>* packetDumpFlags;
if (aSending) {
packetDumpFlags = &mSendPacketDumpFlags;
} else {
packetDumpFlags = &mRecvPacketDumpFlags;
}
if (aLevel < packetDumpFlags->size()) {
unsigned flag = 1 << (unsigned)aType;
return flag & packetDumpFlags->at(aLevel);
}
return false;
}
} // namespace mozilla

View File

@ -14,18 +14,35 @@ class PeerConnectionImpl;
class PacketDumper {
public:
explicit PacketDumper(PeerConnectionImpl* aPc);
explicit PacketDumper(const std::string& aPcHandle);
PacketDumper(const PacketDumper&) = delete;
~PacketDumper();
static RefPtr<PacketDumper> GetPacketDumper(const std::string& aPcHandle);
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PacketDumper)
PacketDumper(const PacketDumper&) = delete;
PacketDumper& operator=(const PacketDumper&) = delete;
void Dump(size_t level, dom::mozPacketDumpType type, bool sending,
const void* data, size_t size);
void Dump(size_t aLevel, dom::mozPacketDumpType aType, bool aSending,
const void* aData, size_t aSize);
nsresult EnablePacketDump(unsigned long aLevel, dom::mozPacketDumpType aType,
bool aSending);
nsresult DisablePacketDump(unsigned long aLevel, dom::mozPacketDumpType aType,
bool aSending);
private:
RefPtr<PeerConnectionImpl> mPc;
friend class PeerConnectionImpl;
explicit PacketDumper(const std::string& aPcHandle);
~PacketDumper() = default;
bool ShouldDumpPacket(size_t aLevel, dom::mozPacketDumpType aType,
bool aSending) const;
// This class is not cycle-collected, so it cannot hold onto a strong ref
const std::string mPcHandle;
std::vector<unsigned> mSendPacketDumpFlags;
std::vector<unsigned> mRecvPacketDumpFlags;
Atomic<bool> mPacketDumpEnabled;
mutable Mutex mPacketDumpFlagsMutex;
};
} // namespace mozilla

View File

@ -207,7 +207,7 @@ class PeerConnectionCtxObserver : public nsIObserver {
nsresult rv = NS_OK;
rv = observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
rv = observerService->AddObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID,
false);
MOZ_ALWAYS_SUCCEEDS(rv);
rv = observerService->AddObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
@ -218,7 +218,7 @@ class PeerConnectionCtxObserver : public nsIObserver {
NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) override {
if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
if (strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID) == 0) {
CSFLogDebug(LOGTAG, "Shutting down PeerConnectionCtx");
PeerConnectionCtx::Destroy();
@ -229,7 +229,8 @@ class PeerConnectionCtxObserver : public nsIObserver {
nsresult rv = observerService->RemoveObserver(
this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
MOZ_ALWAYS_SUCCEEDS(rv);
rv = observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
rv = observerService->RemoveObserver(this,
NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
MOZ_ALWAYS_SUCCEEDS(rv);
// Make sure we're not deleted while still inside ::Observe()
@ -257,7 +258,7 @@ class PeerConnectionCtxObserver : public nsIObserver {
services::GetObserverService();
if (observerService) {
observerService->RemoveObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
observerService->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
}
}
};
@ -265,16 +266,10 @@ class PeerConnectionCtxObserver : public nsIObserver {
NS_IMPL_ISUPPORTS(PeerConnectionCtxObserver, nsIObserver);
PeerConnectionCtx* PeerConnectionCtx::gInstance;
nsIThread* PeerConnectionCtx::gMainThread;
StaticRefPtr<PeerConnectionCtxObserver>
PeerConnectionCtx::gPeerConnectionCtxObserver;
nsresult PeerConnectionCtx::InitializeGlobal(nsIThread* mainThread) {
if (!gMainThread) {
gMainThread = mainThread;
}
MOZ_ASSERT(gMainThread == mainThread);
nsresult PeerConnectionCtx::InitializeGlobal() {
MOZ_ASSERT(NS_IsMainThread());
nsresult res;
@ -542,8 +537,8 @@ static void GMPReady_m() {
};
static void GMPReady() {
PeerConnectionCtx::gMainThread->Dispatch(WrapRunnableNM(&GMPReady_m),
NS_DISPATCH_NORMAL);
GetMainThreadEventTarget()->Dispatch(WrapRunnableNM(&GMPReady_m),
NS_DISPATCH_NORMAL);
};
void PeerConnectionCtx::initGMP() {

View File

@ -80,7 +80,7 @@ class SharedWebrtcState {
// * Upstream webrtc state shared across all Calls (processing thread)
class PeerConnectionCtx {
public:
static nsresult InitializeGlobal(nsIThread* mainThread);
static nsresult InitializeGlobal();
static PeerConnectionCtx* GetInstance();
static bool isActive();
static void Destroy();
@ -165,7 +165,6 @@ class PeerConnectionCtx {
static PeerConnectionCtx* gInstance;
public:
static nsIThread* gMainThread;
static mozilla::StaticRefPtr<mozilla::PeerConnectionCtxObserver>
gPeerConnectionCtxObserver;
};

Some files were not shown because too many files have changed in this diff Show More