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

This commit is contained in:
Sebastian Hengst 2017-09-05 12:38:47 +02:00
commit 3d05c8d3ca
208 changed files with 4762 additions and 8328 deletions

View File

@ -2239,7 +2239,18 @@ function loadOneOrMoreURIs(aURIString, aTriggeringPrincipal) {
}
}
function focusAndSelectUrlBar() {
/**
* Focuses the location bar input field and selects its contents.
*
* @param [optional] userInitiatedFocus
* Whether this focus is caused by an user interaction whose intention
* was to use the location bar. For example, using a shortcut to go to
* the location bar, or a contextual menu to search from it.
* The default is false and should be used in all those cases where the
* code focuses the location bar but that's not the primary user
* intention, like when opening a new tab.
*/
function focusAndSelectUrlBar(userInitiatedFocus = false) {
// In customize mode, the url bar is disabled. If a new tab is opened or the
// user switches to a different tab, this function gets called before we've
// finished leaving customize mode, and the url bar will still be disabled.
@ -2247,7 +2258,7 @@ function focusAndSelectUrlBar() {
// we've finished leaving customize mode.
if (CustomizationHandler.isExitingCustomizeMode) {
gNavToolbox.addEventListener("aftercustomization", function() {
focusAndSelectUrlBar();
focusAndSelectUrlBar(userInitiatedFocus);
}, {once: true});
return true;
@ -2257,7 +2268,9 @@ function focusAndSelectUrlBar() {
if (window.fullScreen)
FullScreen.showNavToolbox();
gURLBar.userInitiatedFocus = userInitiatedFocus;
gURLBar.select();
gURLBar.userInitiatedFocus = false;
if (document.activeElement == gURLBar.inputField)
return true;
}
@ -2265,7 +2278,7 @@ function focusAndSelectUrlBar() {
}
function openLocation() {
if (focusAndSelectUrlBar())
if (focusAndSelectUrlBar(true))
return;
if (window.location.href != getBrowserURL()) {
@ -3815,7 +3828,7 @@ const BrowserSearch = {
let focusUrlBarIfSearchFieldIsNotActive = function(aSearchBar) {
if (!aSearchBar || document.activeElement != aSearchBar.textbox.inputField) {
focusAndSelectUrlBar();
focusAndSelectUrlBar(true);
}
};

View File

@ -133,8 +133,6 @@ var whitelist = [
{file: "chrome://global/content/customizeToolbar.xul"},
// Bug 1343837
{file: "chrome://global/content/findUtils.js"},
// Bug 1343843
{file: "chrome://global/content/url-classifier/unittests.xul"},
// Bug 1348362
{file: "chrome://global/skin/icons/warning-64.png", platforms: ["linux", "win"]},
// Bug 1348525

View File

@ -1,3 +1,4 @@
/* eslint-disable mozilla/no-arbitrary-setTimeout */
// The order of the tests here matters!
const SUGGEST_ALL_PREF = "browser.search.suggest.enabled";
@ -38,7 +39,7 @@ add_task(async function focus() {
setupVisibleHint();
gURLBar.blur();
let popupPromise = promisePopupShown(gURLBar.popup);
gURLBar.focus();
focusAndSelectUrlBar(true);
await popupPromise;
Assert.ok(gURLBar.popup.popupOpen, "popup should be open");
assertVisible(true);
@ -64,23 +65,40 @@ add_task(async function focus() {
await BrowserTestUtils.loadURI(gBrowser.selectedBrowser, "about:blank");
});
add_task(async function new_tab() {
// Opening a new tab when the urlbar is unfocused, should focusing it and thus
// open the popup in order to show the notification.
add_task(async function click_on_focused() {
// Even if the location bar is already focused, we should still show the popup
// and the notification on click.
setupVisibleHint();
gURLBar.blur();
// Won't show the hint since it's not user initiated.
gURLBar.focus();
await new Promise(resolve => setTimeout(resolve, 500));
Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed");
let popupPromise = promisePopupShown(gURLBar.popup);
// openNewForegroundTab doesn't focus the urlbar.
await BrowserTestUtils.synthesizeKey("t", { accelKey: true }, gBrowser.selectedBrowser);
EventUtils.synthesizeMouseAtCenter(gURLBar.inputField, { button: 0, type: "mousedown" });
await popupPromise;
Assert.ok(gURLBar.popup.popupOpen, "popup should be open");
assertVisible(true);
assertFooterVisible(false);
Assert.equal(gURLBar.popup._matchCount, 0, "popup should have no results");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
gURLBar.blur();
Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed");
});
add_task(async function new_tab() {
// Opening a new tab when the urlbar is unfocused, should focus it but not
// open the popup.
setupVisibleHint();
gURLBar.blur();
// openNewForegroundTab doesn't focus the urlbar.
await BrowserTestUtils.synthesizeKey("t", { accelKey: true }, gBrowser.selectedBrowser);
await new Promise(resolve => setTimeout(resolve, 500));
Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
add_task(async function privateWindow() {
// Since suggestions are disabled in private windows, the notification should
// not appear even when suggestions are otherwise enabled.

View File

@ -184,6 +184,12 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
<property name="maxRows"
onget="return this.popup.maxResults;"/>
<!--
Set by focusAndSelectUrlBar to indicate whether the next focus event was
initiated by an explicit user action. See the "focus" handler below.
-->
<field name="userInitiatedFocus">false</field>
<!--
onBeforeValueGet is called by the base-binding's .value getter.
It can return an object with a "value" property, to override the
@ -1445,6 +1451,27 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
]]></body>
</method>
<method name="maybeShowSearchSuggestionsNotificationOnFocus">
<parameter name="mouseFocused"/>
<body><![CDATA[
let whichNotification = this.whichSearchSuggestionsNotification;
if (this._showSearchSuggestionNotificationOnMouseFocus &&
mouseFocused) {
// Force showing the opt-out notification.
this._whichSearchSuggestionsNotification = whichNotification = "opt-out";
}
if (whichNotification == "opt-out") {
try {
this.popup.openAutocompletePopup(this, this);
} finally {
if (mouseFocused) {
delete this._whichSearchSuggestionsNotification;
this._showSearchSuggestionNotificationOnMouseFocus = false;
}
}
}
]]></body>
</method>
</implementation>
<handlers>
@ -1469,6 +1496,14 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
}
]]></handler>
<handler event="mousedown"><![CDATA[
// Eventually show the opt-out notification even if the location bar is
// empty, focused, and the user clicks on it.
if (event.button == 0 && this.focused && this.textValue == "") {
this.maybeShowSearchSuggestionsNotificationOnFocus(true);
}
]]></handler>
<handler event="focus"><![CDATA[
if (event.originalTarget == this.inputField) {
this._hideURLTooltip();
@ -1477,8 +1512,8 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
UpdatePopupNotificationsVisibility();
}
// We show the opt-out notification on every kind of focus to the urlbar
// included opening a new tab, but we want to enforce at least one
// We show the opt-out notification when the mouse/keyboard focus the
// urlbar, but in any case we want to enforce at least one
// notification when the user focuses it with the mouse.
let whichNotification = this.whichSearchSuggestionsNotification;
if (whichNotification == "opt-out" &&
@ -1486,25 +1521,16 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
this._showSearchSuggestionNotificationOnMouseFocus = true;
}
// Check whether the focus change came from a user mouse action.
// Check whether the focus change came from a keyboard/mouse action.
let focusMethod = Services.focus.getLastFocusMethod(window);
let mouseFocused = !!(focusMethod & Services.focus.FLAG_BYMOUSE);
if (this._showSearchSuggestionNotificationOnMouseFocus &&
mouseFocused) {
// Force showing the opt-out notification.
this._whichSearchSuggestionsNotification = whichNotification = "opt-out";
// If it's a focus started by code and the primary user intention was
// not to go to the location bar, don't show a notification.
if (!focusMethod && !this.userInitiatedFocus) {
return;
}
if (whichNotification == "opt-out") {
try {
this.popup.openAutocompletePopup(this, this);
} finally {
if (mouseFocused) {
delete this._whichSearchSuggestionsNotification;
this._showSearchSuggestionNotificationOnMouseFocus = false;
}
}
}
let mouseFocused = !!(focusMethod & Services.focus.FLAG_BYMOUSE);
this.maybeShowSearchSuggestionsNotificationOnFocus(mouseFocused);
}
]]></handler>

View File

@ -2113,7 +2113,7 @@ BrowserGlue.prototype = {
if (currentUIVersion < 52) {
// Keep old devtools log persistence behavior after splitting netmonitor and
// webconsole prefs (bug 1307881).
if (Services.prefs.getBoolPref("devtools.webconsole.persistlog")) {
if (Services.prefs.getBoolPref("devtools.webconsole.persistlog", false)) {
Services.prefs.setBoolPref("devtools.netmonitor.persistlog", true);
}
}

View File

@ -382,6 +382,7 @@ var BookmarkPropertiesPanel = {
if (this._batching)
return;
if (PlacesUIUtils.useAsyncTransactions) {
this._topUndoEntry = PlacesTransactions.topUndoEntry;
this._batchBlockingDeferred = PromiseUtils.defer();
PlacesTransactions.batch(async () => {
await this._batchBlockingDeferred.promise;
@ -394,7 +395,7 @@ var BookmarkPropertiesPanel = {
_endBatch() {
if (!this._batching)
return;
return false;
if (PlacesUIUtils.useAsyncTransactions) {
this._batchBlockingDeferred.resolve();
@ -403,6 +404,9 @@ var BookmarkPropertiesPanel = {
PlacesUtils.transactionManager.endBatch(false);
}
this._batching = false;
let changed = this._topUndoEntry != PlacesTransactions.topUndoEntry;
delete this._topUndoEntry;
return changed;
},
// nsISupports
@ -446,11 +450,14 @@ var BookmarkPropertiesPanel = {
// changes done as part of Undo may change the panel contents and by
// that force it to commit more transactions.
gEditItemOverlay.uninitPanel(true);
this._endBatch();
if (PlacesUIUtils.useAsyncTransactions)
PlacesTransactions.undo().catch(Components.utils.reportError);
else
let changed = this._endBatch();
if (PlacesUIUtils.useAsyncTransactions) {
if (changed) {
PlacesTransactions.undo().catch(Components.utils.reportError);
}
} else {
PlacesUtils.transactionManager.undoTransaction();
}
window.arguments[0].performed = false;
},

View File

@ -21,6 +21,7 @@ support-files =
[browser_bookmarkProperties_addKeywordForThisSearch.js]
[browser_bookmarkProperties_addLivemark.js]
[browser_bookmarkProperties_bookmarkAllTabs.js]
[browser_bookmarkProperties_cancel.js]
[browser_bookmarkProperties_editTagContainer.js]
[browser_bookmarkProperties_readOnlyRoot.js]
[browser_bookmarksProperties.js]

View File

@ -0,0 +1,118 @@
"use strict";
/* global sinon */
Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
const sandbox = sinon.sandbox.create();
registerCleanupFunction(async function() {
sandbox.restore();
delete window.sinon;
await PlacesUtils.bookmarks.eraseEverything();
await PlacesTestUtils.clearHistory();
});
let bookmarks; // Bookmarks added via insertTree.
let bookmarkIds; // Map of Guids to Ids.
add_task(async function setup() {
bookmarks = await PlacesUtils.bookmarks.insertTree({
guid: PlacesUtils.bookmarks.unfiledGuid,
children: [{
title: "bm1",
url: "http://example.com",
}, {
title: "bm2",
url: "http://example.com/2",
}]
});
bookmarkIds = await PlacesUtils.promiseManyItemIds([
bookmarks[0].guid, bookmarks[1].guid
]);
// Undo is called asynchronously - and not waited for. Since we're not
// expecting undo to be called, we can only tell this by stubbing it.
sandbox.stub(PlacesTransactions, "undo").returns(Promise.resolve());
});
// Tests for bug 1391393 - Ensures that if the user cancels the bookmark properties
// dialog without having done any changes, then no undo is called.
add_task(async function test_cancel_with_no_changes() {
if (!PlacesUIUtils.useAsyncTransactions) {
Assert.ok(true, "Skipping test as async transactions are turned off");
return;
}
await withSidebarTree("bookmarks", async (tree) => {
tree.selectItems([bookmarkIds.get(bookmarks[0].guid)]);
// Delete the bookmark to put something in the undo history.
// Rather than calling cmd_delete, we call the remove directly, so that we
// can await on it finishing, and be guaranteed that there's something
// in the history.
await tree.controller.remove("Remove Selection");
tree.selectItems([bookmarkIds.get(bookmarks[1].guid)]);
// Now open the bookmarks dialog and cancel it.
await withBookmarksDialog(
true,
function openDialog() {
tree.controller.doCommand("placesCmd_show:info");
},
async function test(dialogWin) {
let acceptButton = dialogWin.document.documentElement.getButton("accept");
await BrowserTestUtils.waitForCondition(() => !acceptButton.disabled,
"The accept button should be enabled");
}
);
Assert.ok(PlacesTransactions.undo.notCalled, "undo should not have been called");
// Check the bookmark is still removed.
Assert.ok(!(await PlacesUtils.bookmarks.fetch(bookmarks[0].guid)),
"The originally removed bookmark should not exist.");
Assert.ok(await PlacesUtils.bookmarks.fetch(bookmarks[1].guid),
"The second bookmark should still exist");
});
});
add_task(async function test_cancel_with_changes() {
if (!PlacesUIUtils.useAsyncTransactions) {
Assert.ok(true, "Skipping test as async transactions are turned off");
return;
}
await withSidebarTree("bookmarks", async (tree) => {
tree.selectItems([bookmarkIds.get(bookmarks[1].guid)]);
// Now open the bookmarks dialog and cancel it.
await withBookmarksDialog(
true,
function openDialog() {
tree.controller.doCommand("placesCmd_show:info");
},
async function test(dialogWin) {
let acceptButton = dialogWin.document.documentElement.getButton("accept");
await BrowserTestUtils.waitForCondition(() => !acceptButton.disabled,
"The accept button should be enabled");
let promiseTitleChangeNotification = promiseBookmarksNotification(
"onItemChanged", (itemId, prop, isAnno, val) => prop == "title" && val == "n");
fillBookmarkTextField("editBMPanel_namePicker", "n", dialogWin);
// The dialog is instant apply.
await promiseTitleChangeNotification;
// Ensure that the addition really is finished before we hit cancel.
await PlacesTestUtils.promiseAsyncUpdates();
}
);
Assert.ok(PlacesTransactions.undo.calledOnce,
"undo should have been called once.");
});
});

View File

@ -64,6 +64,9 @@ add_task(async function() {
let tags = PlacesUtils.tagging.getTagsForURI(uri);
Assert.equal(tags.length, 1, "Found the right number of tags");
Assert.ok(tags.includes("tag2"), "Found the expected tag");
// Ensure that the addition really is finished before we hit cancel.
await PlacesTestUtils.promiseAsyncUpdates();
}
);

View File

@ -5,6 +5,16 @@
"use strict";
add_task(async function test() {
let bookmark = await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
title: "Plain Bob",
url: "http://example.com"
});
registerCleanupFunction(async () => {
await PlacesUtils.bookmarks.remove(bookmark);
});
let sidebarBox = document.getElementById("sidebar-box");
is(sidebarBox.hidden, true, "The sidebar should be hidden");

View File

@ -46,6 +46,11 @@ add_task(async function setup() {
async function run_drag_test(startBookmarkIndex, insertionIndex,
realInsertionIndex, expectTransactionCreated = true) {
if (!PlacesUIUtils.useAsyncTransactions) {
Assert.ok(true, "Skipping test as async transactions are turned off");
return;
}
// Reset the stubs so that previous test runs don't count against us.
PlacesUIUtils.getTransactionForData.reset();
PlacesTransactions.batch.reset();

View File

@ -1086,24 +1086,32 @@ var gPrivacyPane = {
safeBrowsingMalwarePref.value = enableSafeBrowsing.checked;
if (enableSafeBrowsing.checked) {
blockDownloads.removeAttribute("disabled");
if (blockDownloads.checked) {
if (blockDownloads) {
blockDownloads.removeAttribute("disabled");
if (blockDownloads.checked) {
blockUncommonUnwanted.removeAttribute("disabled");
}
} else {
blockUncommonUnwanted.removeAttribute("disabled");
}
} else {
blockDownloads.setAttribute("disabled", "true");
if (blockDownloads) {
blockDownloads.setAttribute("disabled", "true");
}
blockUncommonUnwanted.setAttribute("disabled", "true");
}
});
blockDownloads.addEventListener("command", function() {
blockDownloadsPref.value = blockDownloads.checked;
if (blockDownloads.checked) {
blockUncommonUnwanted.removeAttribute("disabled");
} else {
blockUncommonUnwanted.setAttribute("disabled", "true");
}
});
if (blockDownloads) {
blockDownloads.addEventListener("command", function() {
blockDownloadsPref.value = blockDownloads.checked;
if (blockDownloads.checked) {
blockUncommonUnwanted.removeAttribute("disabled");
} else {
blockUncommonUnwanted.setAttribute("disabled", "true");
}
});
}
blockUncommonUnwanted.addEventListener("command", function() {
blockUnwantedPref.value = blockUncommonUnwanted.checked;
@ -1112,8 +1120,8 @@ var gPrivacyPane = {
let malware = malwareTable.value
.split(",")
.filter(x => x !== "goog-unwanted-proto" &&
x !== "goog-unwanted-shavar" &&
x !== "test-unwanted-simple");
x !== "goog-unwanted-shavar" &&
x !== "test-unwanted-simple");
if (blockUncommonUnwanted.checked) {
if (malware.indexOf("goog-malware-shavar") != -1) {
@ -1135,13 +1143,18 @@ var gPrivacyPane = {
enableSafeBrowsing.checked = safeBrowsingPhishingPref.value && safeBrowsingMalwarePref.value;
if (!enableSafeBrowsing.checked) {
blockDownloads.setAttribute("disabled", "true");
if (blockDownloads) {
blockDownloads.setAttribute("disabled", "true");
}
blockUncommonUnwanted.setAttribute("disabled", "true");
}
blockDownloads.checked = blockDownloadsPref.value;
if (!blockDownloadsPref.value) {
blockUncommonUnwanted.setAttribute("disabled", "true");
if (blockDownloads) {
blockDownloads.checked = blockDownloadsPref.value;
if (!blockDownloadsPref.value) {
blockUncommonUnwanted.setAttribute("disabled", "true");
}
}
blockUncommonUnwanted.checked = blockUnwantedPref.value && blockUncommonPref.value;

View File

@ -732,9 +732,11 @@
label="&enableSafeBrowsing.label;"
accesskey="&enableSafeBrowsing.accesskey;" />
<vbox class="indent">
#ifdef MOZILLA_OFFICIAL
<checkbox id="blockDownloads"
label="&blockDownloads.label;"
accesskey="&blockDownloads.accesskey;" />
#endif
<checkbox id="blockUncommonUnwanted"
label="&blockUncommonAndUnwanted.label;"
accesskey="&blockUncommonAndUnwanted.accesskey;" />

View File

@ -40,9 +40,14 @@ add_task(async function() {
let blockDownloads = doc.getElementById("blockDownloads");
let blockUncommon = doc.getElementById("blockUncommonUnwanted");
let checked = checkbox.checked;
if (!AppConstants.MOZILLA_OFFICIAL) {
is(blockDownloads, undefined, "downloads protection is disabled in un-official builds");
} else {
is(blockDownloads.hasAttribute("disabled"), !checked, "block downloads checkbox is set correctly");
}
is(checked, val1 && val2, "safebrowsing preference is initialized correctly");
// should be disabled when checked is false (= pref is turned off)
is(blockDownloads.hasAttribute("disabled"), !checked, "block downloads checkbox is set correctly");
is(blockUncommon.hasAttribute("disabled"), !checked, "block uncommon checkbox is set correctly");
// scroll the checkbox into the viewport and click checkbox
@ -57,8 +62,10 @@ add_task(async function() {
// check if the other checkboxes have updated
checked = checkbox.checked;
is(blockDownloads.hasAttribute("disabled"), !checked, "block downloads checkbox is set correctly");
is(blockUncommon.hasAttribute("disabled"), !checked || !blockDownloads.checked, "block uncommon checkbox is set correctly");
if (blockDownloads) {
is(blockDownloads.hasAttribute("disabled"), !checked, "block downloads checkbox is set correctly");
is(blockUncommon.hasAttribute("disabled"), !checked || !blockDownloads.checked, "block uncommon checkbox is set correctly");
}
}
await checkPrefSwitch(true, true);

View File

@ -36,6 +36,11 @@ add_task(async function() {
let doc = gBrowser.selectedBrowser.contentDocument;
let checkbox = doc.getElementById("blockDownloads");
if (!AppConstants.MOZILLA_OFFICIAL) {
is(checkbox, undefined, "downloads protection is disabled in un-official builds");
return;
}
let blockUncommon = doc.getElementById("blockUncommonUnwanted");
let checked = checkbox.checked;
is(checked, val, "downloads preference is initialized correctly");

View File

@ -29,6 +29,8 @@ class ManageRecords {
this._subStorageName = subStorageName;
this._elements = elements;
this._records = [];
this._newRequest = false;
this._isLoadingRecords = false;
this.prefWin = window.opener;
this.localizeDocument();
window.addEventListener("DOMContentLoaded", this, {once: true});
@ -70,17 +72,40 @@ class ManageRecords {
}
/**
* Load records and render them.
* Load records and render them. This function is a wrapper for _loadRecords
* to ensure any reentrant will be handled well.
*/
async loadRecords() {
// This function can be early returned when there is any reentrant happends.
// "_newRequest" needs to be set to ensure all changes will be applied.
if (this._isLoadingRecords) {
this._newRequest = true;
return;
}
this._isLoadingRecords = true;
await this._loadRecords();
// _loadRecords should be invoked again if there is any multiple entrant
// during running _loadRecords(). This step ensures that the latest request
// still is applied.
while (this._newRequest) {
this._newRequest = false;
await this._loadRecords();
}
this._isLoadingRecords = false;
// For testing only: Notify when records are loaded
this._elements.records.dispatchEvent(new CustomEvent("RecordsLoaded"));
}
async _loadRecords() {
let storage = await this.getStorage();
let records = storage.getAll();
// Sort by last modified time starting with most recent
records.sort((a, b) => b.timeLastModified - a.timeLastModified);
await this.renderRecordElements(records);
this.updateButtonsStates(this._selectedOptions.length);
// For testing only: Notify when records are loaded
this._elements.records.dispatchEvent(new CustomEvent("RecordsLoaded"));
}
/**

View File

@ -51,6 +51,25 @@
}
#onboarding-overlay-button::after {
content: " ";
border-radius: 50%;
margin-top: -1px;
margin-inline-start: -13px;
border: 2px solid #f2f2f2;
background: #0A84FF;
padding: 0;
width: 10px;
height: 10px;
min-width: unset;
max-width: unset;
display: block;
box-sizing: content-box;
float: inline-end;
position: relative;
}
#onboarding-overlay-button:hover::after,
#onboarding-overlay-button.onboarding-speech-bubble::after {
background: #0060df;
font-size: 13px;
text-align: center;
@ -58,10 +77,11 @@
box-sizing: content-box;
font-weight: 400;
content: attr(aria-label);
display: inline-block;
border: 1px solid transparent;
border-radius: 2px;
padding: 10px 16px;
width: auto;
height: auto;
min-width: 100px;
max-width: 140px;
white-space: pre-line;

View File

@ -21,6 +21,8 @@ const BRAND_SHORT_NAME = Services.strings
.GetStringFromName("brandShortName");
const PROMPT_COUNT_PREF = "browser.onboarding.notification.prompt-count";
const ONBOARDING_DIALOG_ID = "onboarding-overlay-dialog";
const ONBOARDING_MIN_WIDTH_PX = 960;
const SPEECH_BUBBLE_MIN_WIDTH_PX = 1150;
/**
* Add any number of tours, key is the tourId, value should follow the format below
@ -380,11 +382,18 @@ class Onboarding {
}
_resizeUI() {
// Don't show the overlay UI before we get to a better, responsive design.
if (this._window.document.body.getBoundingClientRect().width < 960) {
let width = this._window.document.body.getBoundingClientRect().width;
if (width < ONBOARDING_MIN_WIDTH_PX) {
// Don't show the overlay UI before we get to a better, responsive design.
this.destroy();
return;
}
this._initUI();
if (this._isFirstSession && width >= SPEECH_BUBBLE_MIN_WIDTH_PX) {
this._overlayIcon.classList.add("onboarding-speech-bubble");
} else {
this._initUI();
this._overlayIcon.classList.remove("onboarding-speech-bubble");
}
}
@ -780,15 +789,31 @@ class Onboarding {
}
}
_muteNotificationOnFirstSession() {
if (Services.prefs.prefHasUserValue("browser.onboarding.notification.tour-ids-queue")) {
// There is a queue. We had prompted before, this must not be the 1st session.
get _isFirstSession() {
// Should only directly return on the "false" case. Consider:
// 1. On the 1st session, `_firstSession` is true
// 2. During the 1st session, user resizes window so that the UI is destroyed
// 3. After the 1st mute session, user resizes window so that the UI is re-init
if (this._firstSession === false) {
return false;
}
this._firstSession = true;
let muteDuration = Services.prefs.getIntPref("browser.onboarding.notification.mute-duration-on-first-session-ms");
if (muteDuration == 0) {
// Don't mute when this is set to 0 on purpose.
// There is a queue, which means we had prompted tour notifications before. Therefore this is not the 1st session.
if (Services.prefs.prefHasUserValue("browser.onboarding.notification.tour-ids-queue")) {
this._firstSession = false;
}
// When this is set to 0 on purpose, always judge as not the 1st session
if (Services.prefs.getIntPref("browser.onboarding.notification.mute-duration-on-first-session-ms") === 0) {
this._firstSession = false;
}
return this._firstSession;
}
_muteNotificationOnFirstSession() {
if (!this._isFirstSession) {
return false;
}
@ -802,6 +827,7 @@ class Onboarding {
}]);
return true;
}
let muteDuration = Services.prefs.getIntPref("browser.onboarding.notification.mute-duration-on-first-session-ms");
return Date.now() - lastTime <= muteDuration;
}
@ -869,6 +895,9 @@ class Onboarding {
if (this._muteNotificationOnFirstSession()) {
return;
}
// After the notification mute on the 1st session,
// we don't want to show the speech bubble by default
this._overlayIcon.classList.remove("onboarding-speech-bubble");
let queue = this._getNotificationQueue();
let startQueueLength = queue.length;

View File

@ -205,9 +205,9 @@ l10n-check::
$(PYTHON) $(topsrcdir)/toolkit/mozapps/installer/unpack.py $(DIST)/l10n-stage/$(MOZ_PKG_DIR)$(_RESPATH)
(cd $(DIST)/l10n-stage && test $$(cat $(MOZ_PKG_DIR)$(_RESPATH)/update.locale) = x-test)
@# package langpack as web extension, too, run some tests on it
$(MAKE) package-langpack-x-test L10NBASEDIR='$(PWD)' WEBEXT_LANGPACKS=1
$(MAKE) analyze-langpack-x-test
$(MAKE) check-clobber-l10n-x-test
$(MAKE) package-langpack-x-test L10NBASEDIR='$(PWD)' WEBEXT_LANGPACKS=1 MOZ_SIMPLE_PACKAGE_NAME=
$(MAKE) analyze-langpack-x-test MOZ_SIMPLE_PACKAGE_NAME=
$(MAKE) check-clobber-l10n-x-test MOZ_SIMPLE_PACKAGE_NAME=
# Helper rules to have AB_CD set to the locale we test for testing
# We need to split this out from l10n-check, as that needs AB_CD to be en-US
@ -222,5 +222,5 @@ check-clobber-l10n-%:
$(MAKE) clobber-x-test
$(RM) -r $(DIST)/l10n-stage $(DIST)/xpi-stage/locale-$(AB_CD)
$(RM) -r $(DIST)/*.$(AB_CD).*
$(RM) -r $(UNPACKED_INSTALLER)
$(RM) -r $(UNPACKED_INSTALLER) $(ABS_DIST)/$(LANGPACK)
$(RM) -rf $(DIST)/install/sea/*.$(AB_CD).*

View File

@ -62,9 +62,9 @@ function ProgressGraphHelper(win, propertyCSSName, animationType, keyframes, dur
this.keyframes = keyframesObject;
this.devtoolsKeyframes = keyframes;
this.valueHelperFunction = this.getValueHelperFunction();
this.animation = this.targetEl.animate(this.keyframes, effectTiming);
this.animation.pause();
this.valueHelperFunction = this.getValueHelperFunction();
}
ProgressGraphHelper.prototype = {
@ -216,10 +216,15 @@ ProgressGraphHelper.prototype = {
getDiscreteValueHelperFunction: function () {
const discreteValues = [];
this.keyframes.forEach(keyframe => {
if (!discreteValues.includes(keyframe.value)) {
discreteValues.push(keyframe.value);
// Set style once since the computed value may differ from specified keyframe value.
this.targetEl.style[this.propertyJSName] = keyframe.value;
const style = this.win.getComputedStyle(this.targetEl)[this.propertyJSName];
if (!discreteValues.includes(style)) {
discreteValues.push(style);
}
});
this.targetEl.style[this.propertyJSName] = "unset";
return value => {
return discreteValues.indexOf(value) / (discreteValues.length - 1);
};

View File

@ -26,6 +26,15 @@ const TEST_CASES = [
{ x: 1000, y: 1, color: "rgb(0, 255, 0)" }
]
},
"background-repeat": {
expectedClass: "discrete",
expectedValues: [
{ x: 0, y: 0 },
{ x: 499.999, y: 0 },
{ x: 500, y: 1 },
{ x: 1000, y: 1 },
]
},
"font-size": {
expectedClass: "length",
expectedValues: [
@ -74,6 +83,15 @@ const TEST_CASES = [
{ x: 1000, y: 1, color: "rgb(255, 0, 0)" }
]
},
"background-repeat": {
expectedClass: "discrete",
expectedValues: [
{ x: 0, y: 0 },
{ x: 499.999, y: 0 },
{ x: 500, y: 1 },
{ x: 1000, y: 1 },
]
},
"font-size": {
expectedClass: "length",
expectedValues: [
@ -127,6 +145,17 @@ const TEST_CASES = [
{ x: 1000, y: 1, color: "rgb(0, 255, 0)" }
]
},
"background-repeat": {
expectedClass: "discrete",
expectedValues: [
{ x: 0, y: 0 },
{ x: 249.999, y: 0 },
{ x: 250, y: 1 },
{ x: 749.999, y: 1 },
{ x: 750, y: 0 },
{ x: 1000, y: 0 },
]
},
"font-size": {
expectedClass: "length",
expectedValues: [
@ -184,6 +213,15 @@ const TEST_CASES = [
{ x: 1000, y: 1, color: "rgb(0, 255, 0)" }
]
},
"background-repeat": {
expectedClass: "discrete",
expectedValues: [
{ x: 0, y: 0 },
{ x: 499.999, y: 0 },
{ x: 500, y: 1 },
{ x: 1000, y: 1 },
]
},
"font-size": {
expectedClass: "length",
expectedValues: [

View File

@ -27,12 +27,14 @@
document.querySelector("#target1").animate(
[{ backgroundColor: "red",
backgroundRepeat: "space",
fontSize: "10px",
marginLeft: "0px",
opacity: 0,
textAlign: "right",
transform: "translate(0px)" },
{ backgroundColor: "lime",
backgroundRepeat: "round",
fontSize: "20px",
marginLeft: "100px",
opacity: 1,
@ -41,12 +43,14 @@
document.querySelector("#target2").animate(
[{ backgroundColor: "lime",
backgroundRepeat: "space",
fontSize: "20px",
marginLeft: "100px",
opacity: 1,
textAlign: "center",
transform: "translate(100px)" },
{ backgroundColor: "red",
backgroundRepeat: "round",
fontSize: "10px",
marginLeft: "0px",
opacity: 0,
@ -55,18 +59,21 @@
document.querySelector("#target3").animate(
[{ backgroundColor: "red",
backgroundRepeat: "space",
fontSize: "10px",
marginLeft: "0px",
opacity: 0,
textAlign: "right",
transform: "translate(0px)" },
{ backgroundColor: "blue",
backgroundRepeat: "round",
fontSize: "20px",
marginLeft: "100px",
opacity: 1,
textAlign: "center",
transform: "translate(100px)" },
{ backgroundColor: "lime",
backgroundRepeat: "space",
fontSize: "10px",
marginLeft: "0px",
opacity: 0,
@ -75,6 +82,7 @@
document.querySelector("#target4").animate(
[{ backgroundColor: "red",
backgroundRepeat: "space",
fontSize: "10px",
marginLeft: "0px",
opacity: 0,
@ -82,6 +90,7 @@
transform: "translate(0px)",
easing: "steps(2)" },
{ backgroundColor: "lime",
backgroundRepeat: "round",
fontSize: "20px",
marginLeft: "100px",
opacity: 1,

View File

@ -278,7 +278,7 @@ Tools.performance = {
inMenu: true,
isTargetSupported: function (target) {
return target.hasActor("profiler");
return target.hasActor("performance");
},
build: function (frame, target) {

View File

@ -2668,9 +2668,9 @@ Toolbox.prototype = {
* necessary because of the WebConsole's `profile` and `profileEnd` methods.
*/
initPerformance: Task.async(function* () {
// If target does not have profiler actor (addons), do not
// If target does not have performance actor (addons), do not
// even register the shared performance connection.
if (!this.target.hasActor("profiler")) {
if (!this.target.hasActor("performance")) {
return promise.resolve();
}

View File

@ -4,7 +4,7 @@
"use strict";
var Cu = Components.utils;
const loaders = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {});
const loaders = Cu.import("resource://devtools/shared/base-loader.js", {});
const { devtools } = Cu.import("resource://devtools/shared/Loader.jsm", {});
const { joinURI } = devtools.require("devtools/shared/path");
const { assert } = devtools.require("devtools/shared/DevToolsUtils");
@ -103,11 +103,9 @@ function BrowserLoaderBuilder({ baseURI, window, useOnlyShared, commonLibRequire
}
const opts = {
id: "browser-loader",
sharedGlobal: true,
sandboxPrototype: window,
sandboxName: "DevTools (UI loader)",
noSandboxAddonId: true,
paths: Object.assign({}, dynamicPaths, loaderOptions.paths),
invisibleToDebugger: loaderOptions.invisibleToDebugger,
requireHook: (id, require) => {

View File

@ -5,16 +5,17 @@
"use strict";
const {Loader} = Components.utils.import("resource://gre/modules/commonjs/toolkit/loader.js", {});
const {Loader, Require} =
Components.utils.import("resource://devtools/shared/base-loader.js", {});
const loader = new Loader.Loader({
const loader = new Loader({
paths: {
"": "resource://gre/modules/commonjs/",
"devtools": "resource://devtools",
},
globals: {},
});
const require = Loader.Require(loader, { id: "undo-test" });
const require = Require(loader, { id: "undo-test" });
const {UndoStack} = require("devtools/client/shared/undo");

View File

@ -33,7 +33,7 @@ Starting with Firefox 36 (thanks to [bug 1069673](https://bugzilla.mozilla.org/s
1. Detecting if the server has an actor: all you need is access to the `Toolbox` instance, which all panels do, when they get instantiated. Then you can do:
```js
let hasProfilerActor = toolbox.target.hasActor("profiler");
let hasPerformanceActor = toolbox.target.hasActor("performance");
```
The `hasActor` method returns a boolean synchronously.

View File

@ -7,7 +7,7 @@
const {Ci, Cu} = require("chrome");
const protocol = require("devtools/shared/protocol");
const {serializeStack, parseStack} = require("toolkit/loader");
const {serializeStack, parseStack} = Cu.import("resource://devtools/shared/base-loader.js", {});
const { functionCallSpec, callWatcherSpec } = require("devtools/shared/specs/call-watcher");
const { CallWatcherFront } = require("devtools/shared/fronts/call-watcher");

View File

@ -47,7 +47,6 @@ DevToolsModules(
'preference.js',
'pretty-print-worker.js',
'process.js',
'profiler.js',
'promises.js',
'reflow.js',
'root.js',
@ -96,9 +95,6 @@ with Files('monitor.js'):
with Files('performance*'):
BUG_COMPONENT = ('Firefox', 'Developer Tools: Performance Tools (Profiler/Timeline)')
with Files('profiler.js'):
BUG_COMPONENT = ('Firefox', 'Developer Tools: Performance Tools (Profiler/Timeline)')
with Files('source.js'):
BUG_COMPONENT = ('Firefox', 'Developer Tools: Debugger')

View File

@ -1,50 +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";
const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
const { Profiler } = require("devtools/server/performance/profiler");
const { actorBridgeWithSpec } = require("devtools/server/actors/common");
const { profilerSpec } = require("devtools/shared/specs/profiler");
/**
* This actor wraps the Profiler module at devtools/server/performance/profiler.js
* and provides RDP definitions.
*
* @see devtools/server/performance/profiler.js for documentation.
*/
exports.ProfilerActor = ActorClassWithSpec(profilerSpec, {
initialize: function (conn) {
Actor.prototype.initialize.call(this, conn);
this._onProfilerEvent = this._onProfilerEvent.bind(this);
this.bridge = new Profiler();
this.bridge.on("*", this._onProfilerEvent);
},
destroy: function () {
this.bridge.off("*", this._onProfilerEvent);
this.bridge.destroy();
Actor.prototype.destroy.call(this);
},
startProfiler: actorBridgeWithSpec("start"),
stopProfiler: actorBridgeWithSpec("stop"),
getProfile: actorBridgeWithSpec("getProfile"),
getFeatures: actorBridgeWithSpec("getFeatures"),
getBufferInfo: actorBridgeWithSpec("getBufferInfo"),
getStartOptions: actorBridgeWithSpec("getStartOptions"),
isActive: actorBridgeWithSpec("isActive"),
sharedLibraries: actorBridgeWithSpec("sharedLibraries"),
registerEventNotifications: actorBridgeWithSpec("registerEventNotifications"),
unregisterEventNotifications: actorBridgeWithSpec("unregisterEventNotifications"),
setProfilerStatusInterval: actorBridgeWithSpec("setProfilerStatusInterval"),
/**
* Pipe events from Profiler module to this actor.
*/
_onProfilerEvent: function (eventName, ...data) {
this.emit(eventName, ...data);
},
});

View File

@ -549,11 +549,6 @@ var DebuggerServer = {
type: { tab: true }
});
if ("nsIProfiler" in Ci) {
this.registerModule("devtools/server/actors/profiler", {
prefix: "profiler",
constructor: "ProfilerActor",
type: { tab: true }
});
this.registerModule("devtools/server/actors/performance", {
prefix: "performance",
constructor: "PerformanceActor",

View File

@ -1,90 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-shadow */
"use strict";
/**
* Tests whether the profiler module and actor have the correct state on
* initialization, activation, and when a clients' connection closes.
*/
const Profiler = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
const MAX_PROFILER_ENTRIES = 10000000;
function run_test() {
// Ensure the profiler is not running when the test starts (it could
// happen if the MOZ_PROFILER_STARTUP environment variable is set).
Profiler.StopProfiler();
get_chrome_actors((client1, form1) => {
let actor1 = form1.profilerActor;
get_chrome_actors((client2, form2) => {
let actor2 = form2.profilerActor;
test_activate(client1, actor1, client2, actor2, () => {
do_test_finished();
});
});
});
do_test_pending();
}
function test_activate(client1, actor1, client2, actor2, callback) {
// Profiler should be inactive at this point.
client1.request({ to: actor1, type: "isActive" }, response => {
do_check_false(Profiler.IsActive());
do_check_false(response.isActive);
do_check_eq(response.currentTime, undefined);
do_check_true(typeof response.position === "number");
do_check_true(typeof response.totalSize === "number");
do_check_true(typeof response.generation === "number");
// Start the profiler on the first connection....
client1.request(
{ to: actor1, type: "startProfiler", entries: MAX_PROFILER_ENTRIES }, response => {
do_check_true(Profiler.IsActive());
do_check_true(response.started);
do_check_true(typeof response.position === "number");
do_check_true(typeof response.totalSize === "number");
do_check_true(typeof response.generation === "number");
do_check_true(response.position >= 0 && response.position < response.totalSize);
do_check_true(response.totalSize === MAX_PROFILER_ENTRIES);
// On the next connection just make sure the actor has been instantiated.
client2.request({ to: actor2, type: "isActive" }, response => {
do_check_true(Profiler.IsActive());
do_check_true(response.isActive);
do_check_true(response.currentTime > 0);
do_check_true(typeof response.position === "number");
do_check_true(typeof response.totalSize === "number");
do_check_true(typeof response.generation === "number");
do_check_true(response.position >= 0 && response.position < response.totalSize);
do_check_true(response.totalSize === MAX_PROFILER_ENTRIES);
let origConnectionClosed = DebuggerServer._connectionClosed;
DebuggerServer._connectionClosed = function (conn) {
origConnectionClosed.call(this, conn);
// The first client is the only actor that started the profiler,
// however the second client can request the accumulated profile data
// at any moment, so the profiler module shouldn't have deactivated.
do_check_true(Profiler.IsActive());
DebuggerServer._connectionClosed = function (conn) {
origConnectionClosed.call(this, conn);
// Now there are no open clients at all, it should *definitely*
// be deactivated by now.
do_check_false(Profiler.IsActive());
callback();
};
client2.close();
};
client1.close();
});
});
});
}

View File

@ -1,44 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests whether the profiler actor correctly handles the case where the
* built-in module was already started.
*/
const Profiler = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
const WAIT_TIME = 1000; // ms
function run_test() {
// Ensure the profiler is already running when the test starts.
Profiler.StartProfiler(1000000, 1, ["js"], 1);
DevToolsUtils.waitForTime(WAIT_TIME).then(() => {
get_chrome_actors((client, form) => {
let actor = form.profilerActor;
test_start_time(client, actor, () => {
client.close().then(do_test_finished);
});
});
});
do_test_pending();
}
function test_start_time(client, actor, callback) {
// Profiler should already be active at this point.
client.request({ to: actor, type: "isActive" }, firstResponse => {
do_check_true(Profiler.IsActive());
do_check_true(firstResponse.isActive);
do_check_true(firstResponse.currentTime > 0);
client.request({ to: actor, type: "getProfile" }, secondResponse => {
do_check_true("profile" in secondResponse);
do_check_true(secondResponse.currentTime > firstResponse.currentTime);
callback();
});
});
}

View File

@ -1,122 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-shadow, max-nested-callbacks */
"use strict";
/**
* Tests if the profiler actor returns its buffer status via getBufferInfo.
*/
const Profiler = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
const INITIAL_WAIT_TIME = 100; // ms
const MAX_WAIT_TIME = 20000; // ms
const MAX_PROFILER_ENTRIES = 10000000;
// eslint-disable-next-line no-unused-vars
function run_test() {
// Ensure the profiler is not running when the test starts (it could
// happen if the MOZ_PROFILER_STARTUP environment variable is set).
Profiler.StopProfiler();
get_chrome_actors((client, form) => {
let actor = form.profilerActor;
check_empty_buffer(client, actor, () => {
activate_profiler(client, actor, startTime => {
wait_for_samples(client, actor, () => {
check_buffer(client, actor, () => {
deactivate_profiler(client, actor, () => {
client.close().then(do_test_finished);
});
});
});
});
});
});
do_test_pending();
}
function check_buffer(client, actor, callback) {
client.request({ to: actor, type: "isActive" }, response => {
do_check_true(typeof response.position === "number");
do_check_true(typeof response.totalSize === "number");
do_check_true(typeof response.generation === "number");
do_check_true(response.position > 0 && response.position < response.totalSize);
do_check_true(response.totalSize === MAX_PROFILER_ENTRIES);
// There's no way we'll fill the buffer in this test.
do_check_true(response.generation === 0);
callback();
});
}
function check_empty_buffer(client, actor, callback) {
client.request({ to: actor, type: "isActive" }, response => {
do_check_false(Profiler.IsActive());
do_check_false(response.isActive);
do_check_true(response.position === void 0);
do_check_true(response.totalSize === void 0);
do_check_true(response.generation === void 0);
do_check_false(response.isActive);
do_check_eq(response.currentTime, undefined);
calback();
});
}
function activate_profiler(client, actor, callback) {
client.request(
{ to: actor, type: "startProfiler", entries: MAX_PROFILER_ENTRIES }, response => {
do_check_true(response.started);
client.request({ to: actor, type: "isActive" }, response => {
do_check_true(response.isActive);
callback(response.currentTime);
});
});
}
function deactivate_profiler(client, actor, callback) {
client.request({ to: actor, type: "stopProfiler" }, response => {
do_check_false(response.started);
client.request({ to: actor, type: "isActive" }, response => {
do_check_false(response.isActive);
callback();
});
});
}
function wait_for_samples(client, actor, callback) {
function attempt(delay) {
// Spin for the requested time, then take a sample.
let start = Date.now();
do_print("Attempt: delay = " + delay);
while (Date.now() - start < delay) {
/* Empty */
}
do_print("Attempt: finished waiting.");
client.request({ to: actor, type: "getProfile" }, response => {
// At this point, we may or may not have samples, depending on
// whether the spin loop above has given the profiler enough time
// to get started.
if (response.profile.threads[0].samples.length == 0) {
if (delay < MAX_WAIT_TIME) {
// Double the spin-wait time and try again.
do_print("Attempt: no samples, going around again.");
attempt(delay * 2);
} else {
// We've waited long enough, so just fail.
do_print("Attempt: waited a long time, but no samples were collected.");
do_print("Giving up.");
do_check_true(false);
}
return;
}
callback();
});
}
// Start off with a 100 millisecond delay.
attempt(INITIAL_WAIT_TIME);
}

View File

@ -1,66 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-shadow, max-nested-callbacks */
"use strict";
/**
* Tests whether the profiler module is kept active when there are multiple
* client consumers and one requests deactivation.
*/
const Profiler = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
function run_test() {
get_chrome_actors((client1, form1) => {
let actor1 = form1.profilerActor;
get_chrome_actors((client2, form2) => {
let actor2 = form2.profilerActor;
test_close(client1, actor1, client2, actor2, () => {
client1.close(() => {
client2.close(() => {
do_test_finished();
});
});
});
});
});
do_test_pending();
}
function activate_profiler(client, actor, callback) {
client.request({ to: actor, type: "startProfiler" }, response => {
do_check_true(response.started);
do_check_true(Profiler.IsActive());
client.request({ to: actor, type: "isActive" }, response => {
do_check_true(response.isActive);
callback();
});
});
}
function deactivate_profiler(client, actor, callback) {
client.request({ to: actor, type: "stopProfiler" }, response => {
do_check_false(response.started);
do_check_true(Profiler.IsActive());
client.request({ to: actor, type: "isActive" }, response => {
do_check_true(response.isActive);
callback();
});
});
}
function test_close(client1, actor1, client2, actor2, callback) {
activate_profiler(client1, actor1, () => {
activate_profiler(client2, actor2, () => {
deactivate_profiler(client1, actor1, () => {
deactivate_profiler(client2, actor2, () => {
callback();
});
});
});
});
}

View File

@ -1,107 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-shadow, max-nested-callbacks */
"use strict";
/**
* Tests if the profiler actor can correctly retrieve a profile after
* it is activated.
*/
const INITIAL_WAIT_TIME = 100; // ms
const MAX_WAIT_TIME = 20000; // ms
function run_test() {
get_chrome_actors((client, form) => {
let actor = form.profilerActor;
activate_profiler(client, actor, startTime => {
test_data(client, actor, startTime, () => {
deactivate_profiler(client, actor, () => {
client.close().then(do_test_finished);
});
});
});
});
do_test_pending();
}
function activate_profiler(client, actor, callback) {
client.request({ to: actor, type: "startProfiler" }, response => {
do_check_true(response.started);
client.request({ to: actor, type: "isActive" }, response => {
do_check_true(response.isActive);
callback(response.currentTime);
});
});
}
function deactivate_profiler(client, actor, callback) {
client.request({ to: actor, type: "stopProfiler" }, response => {
do_check_false(response.started);
client.request({ to: actor, type: "isActive" }, response => {
do_check_false(response.isActive);
callback();
});
});
}
function test_data(client, actor, startTime, callback) {
function attempt(delay) {
// No idea why, but Components.stack.sourceLine returns null.
let funcLine = Components.stack.lineNumber - 2;
// Spin for the requested time, then take a sample.
let start = Date.now();
let stack;
do_print("Attempt: delay = " + delay);
while (Date.now() - start < delay) {
stack = Components.stack;
}
do_print("Attempt: finished waiting.");
client.request({ to: actor, type: "getProfile", startTime }, response => {
// Any valid getProfile response should have the following top
// level structure.
do_check_eq(typeof response.profile, "object");
do_check_eq(typeof response.profile.meta, "object");
do_check_eq(typeof response.profile.meta.platform, "string");
do_check_eq(typeof response.profile.threads, "object");
do_check_eq(typeof response.profile.threads[0], "object");
do_check_eq(typeof response.profile.threads[0].samples, "object");
// At this point, we may or may not have samples, depending on
// whether the spin loop above has given the profiler enough time
// to get started.
if (response.profile.threads[0].samples.length == 0) {
if (delay < MAX_WAIT_TIME) {
// Double the spin-wait time and try again.
do_print("Attempt: no samples, going around again.");
attempt(delay * 2);
} else {
// We've waited long enough, so just fail.
do_print("Attempt: waited a long time, but no samples were collected.");
do_print("Giving up.");
do_check_true(false);
}
return;
}
// Now check the samples. At least one sample is expected to
// have been in the busy wait above.
let loc = stack.name + " (" + stack.filename + ":" + funcLine + ")";
let thread0 = response.profile.threads[0];
do_check_true(thread0.samples.data.some(sample => {
let frames = getInflatedStackLocations(thread0, sample);
return frames.length != 0 &&
frames.some(location => (location == loc));
}));
callback();
});
}
// Start off with a 100 millisecond delay.
attempt(INITIAL_WAIT_TIME);
}

View File

@ -1,59 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests the event notification service for the profiler actor.
*/
const { ProfilerFront } = require("devtools/shared/fronts/profiler");
add_task(function* () {
let [client, form] = yield getChromeActors();
let front = new ProfilerFront(client, form);
let events = [0, 0, 0, 0];
front.on("console-api-profiler", () => events[0]++);
front.on("profiler-started", () => events[1]++);
front.on("profiler-stopped", () => events[2]++);
client.addListener("eventNotification", (type, response) => {
do_check_true(type === "eventNotification");
events[3]++;
});
yield front.startProfiler();
yield front.stopProfiler();
// All should be empty without binding events
do_check_true(events[0] === 0);
do_check_true(events[1] === 0);
do_check_true(events[2] === 0);
do_check_true(events[3] === 0);
let ret = yield front.registerEventNotifications(
{ events: ["console-api-profiler", "profiler-started", "profiler-stopped"] });
do_check_true(ret.registered.length === 3);
yield front.startProfiler();
do_check_true(events[0] === 0);
do_check_true(events[1] === 1);
do_check_true(events[2] === 0);
do_check_true(events[3] === 1, "compatibility events supported for eventNotifications");
yield front.stopProfiler();
do_check_true(events[0] === 0);
do_check_true(events[1] === 1);
do_check_true(events[2] === 1);
do_check_true(events[3] === 2, "compatibility events supported for eventNotifications");
ret = yield front.unregisterEventNotifications(
{ events: ["console-api-profiler", "profiler-started", "profiler-stopped"] });
do_check_true(ret.registered.length === 3);
});
function getChromeActors() {
let deferred = defer();
get_chrome_actors((client, form) => deferred.resolve([client, form]));
return deferred.promise;
}

View File

@ -1,69 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests the event notification service for the profiler actor.
*/
const Profiler = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
const MAX_PROFILER_ENTRIES = 10000000;
const { ProfilerFront } = require("devtools/shared/fronts/profiler");
const { waitForTime } = DevToolsUtils;
add_task(function* () {
let [client, form] = yield getChromeActors();
let front = new ProfilerFront(client, form);
// Ensure the profiler is not running when the test starts (it could
// happen if the MOZ_PROFILER_STARTUP environment variable is set).
Profiler.StopProfiler();
let eventsCalled = 0;
let handledThreeTimes = defer();
front.on("profiler-status", (response) => {
dump("'profiler-status' fired\n");
do_check_true(typeof response.position === "number");
do_check_true(typeof response.totalSize === "number");
do_check_true(typeof response.generation === "number");
do_check_true(response.position > 0 && response.position < response.totalSize);
do_check_true(response.totalSize === MAX_PROFILER_ENTRIES);
// There's no way we'll fill the buffer in this test.
do_check_true(response.generation === 0);
eventsCalled++;
if (eventsCalled > 2) {
handledThreeTimes.resolve();
}
});
yield front.setProfilerStatusInterval(1);
dump("Set the profiler-status event interval to 1\n");
yield front.startProfiler();
yield waitForTime(500);
yield front.stopProfiler();
do_check_true(eventsCalled === 0,
"No 'profiler-status' events should be fired before registering.");
let ret = yield front.registerEventNotifications({ events: ["profiler-status"] });
do_check_true(ret.registered.length === 1);
yield front.startProfiler();
yield handledThreeTimes.promise;
yield front.stopProfiler();
do_check_true(eventsCalled >= 3,
"profiler-status fired atleast three times while recording");
let totalEvents = eventsCalled;
yield waitForTime(50);
do_check_true(totalEvents === eventsCalled,
"No more profiler-status events after recording.");
});
function getChromeActors() {
let deferred = defer();
get_chrome_actors((client, form) => deferred.resolve([client, form]));
return deferred.promise;
}

View File

@ -1,116 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-shadow, max-nested-callbacks */
"use strict";
/**
* Tests if the profiler actor returns its buffer status via getBufferInfo.
*/
const Profiler = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
const INITIAL_WAIT_TIME = 100; // ms
const MAX_WAIT_TIME = 20000; // ms
const MAX_PROFILER_ENTRIES = 10000000;
function run_test() {
// Ensure the profiler is not running when the test starts (it could
// happen if the MOZ_PROFILER_STARTUP environment variable is set).
Profiler.StopProfiler();
get_chrome_actors((client, form) => {
let actor = form.profilerActor;
check_empty_buffer(client, actor, () => {
activate_profiler(client, actor, startTime => {
wait_for_samples(client, actor, () => {
check_buffer(client, actor, () => {
deactivate_profiler(client, actor, () => {
client.close().then(do_test_finished);
});
});
});
});
});
});
do_test_pending();
}
function check_empty_buffer(client, actor, callback) {
client.request({ to: actor, type: "getBufferInfo" }, response => {
do_check_true(response.position === 0);
do_check_true(response.totalSize === 0);
do_check_true(response.generation === 0);
callback();
});
}
function check_buffer(client, actor, callback) {
client.request({ to: actor, type: "getBufferInfo" }, response => {
do_check_true(typeof response.position === "number");
do_check_true(typeof response.totalSize === "number");
do_check_true(typeof response.generation === "number");
do_check_true(response.position > 0 && response.position < response.totalSize);
do_check_true(response.totalSize === MAX_PROFILER_ENTRIES);
// There's no way we'll fill the buffer in this test.
do_check_true(response.generation === 0);
callback();
});
}
function activate_profiler(client, actor, callback) {
client.request(
{ to: actor, type: "startProfiler", entries: MAX_PROFILER_ENTRIES }, response => {
do_check_true(response.started);
client.request({ to: actor, type: "isActive" }, response => {
do_check_true(response.isActive);
callback(response.currentTime);
});
});
}
function deactivate_profiler(client, actor, callback) {
client.request({ to: actor, type: "stopProfiler" }, response => {
do_check_false(response.started);
client.request({ to: actor, type: "isActive" }, response => {
do_check_false(response.isActive);
callback();
});
});
}
function wait_for_samples(client, actor, callback) {
function attempt(delay) {
// Spin for the requested time, then take a sample.
let start = Date.now();
do_print("Attempt: delay = " + delay);
/* eslint-disable no-empty */
while (Date.now() - start < delay) {}
do_print("Attempt: finished waiting.");
client.request({ to: actor, type: "getProfile" }, response => {
// At this point, we may or may not have samples, depending on
// whether the spin loop above has given the profiler enough time
// to get started.
if (response.profile.threads[0].samples.length == 0) {
if (delay < MAX_WAIT_TIME) {
// Double the spin-wait time and try again.
do_print("Attempt: no samples, going around again.");
attempt(delay * 2);
} else {
// We've waited long enough, so just fail.
do_print("Attempt: waited a long time, but no samples were collected.");
do_print("Giving up.");
do_check_true(false);
}
return;
}
callback();
});
}
// Start off with a 100 millisecond delay.
attempt(INITIAL_WAIT_TIME);
}

View File

@ -1,31 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests whether the profiler responds to "getFeatures" adequately.
*/
function run_test() {
get_chrome_actors((client, form) => {
let actor = form.profilerActor;
test_getfeatures(client, actor, () => {
client.close().then(() => {
do_test_finished();
});
});
});
do_test_pending();
}
function test_getfeatures(client, actor, callback) {
client.request({ to: actor, type: "getFeatures" }, response => {
do_check_eq(typeof response.features, "object");
do_check_true(response.features.length >= 1);
do_check_eq(typeof response.features[0], "string");
do_check_true(response.features.indexOf("js") != -1);
callback();
});
}

View File

@ -1,41 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests whether the profiler responds to "sharedLibraries" adequately.
*/
function run_test() {
get_chrome_actors((client, form) => {
let actor = form.profilerActor;
test_sharedlibraries(client, actor, () => {
client.close().then(() => {
do_test_finished();
});
});
});
do_test_pending();
}
function test_sharedlibraries(client, actor, callback) {
client.request({ to: actor, type: "sharedLibraries" }, response => {
const libs = response.sharedLibraries;
do_check_eq(typeof libs, "object");
do_check_true(Array.isArray(libs));
do_check_eq(typeof libs, "object");
do_check_true(libs.length >= 1);
do_check_eq(typeof libs[0], "object");
do_check_eq(typeof libs[0].name, "string");
do_check_eq(typeof libs[0].path, "string");
do_check_eq(typeof libs[0].debugName, "string");
do_check_eq(typeof libs[0].debugPath, "string");
do_check_eq(typeof libs[0].arch, "string");
do_check_eq(typeof libs[0].start, "number");
do_check_eq(typeof libs[0].end, "number");
do_check_true(libs[0].start <= libs[0].end);
callback();
});
}

View File

@ -200,15 +200,6 @@ reason = only ran on B2G
[test_source-01.js]
[test_wasm_source-01.js]
[test_breakpoint-actor-map.js]
[test_profiler_activation-01.js]
[test_profiler_activation-02.js]
[test_profiler_close.js]
[test_profiler_data.js]
[test_profiler_events-01.js]
[test_profiler_events-02.js]
[test_profiler_getbufferinfo.js]
[test_profiler_getfeatures.js]
[test_profiler_sharedlibraries.js]
[test_unsafeDereference.js]
[test_add_actors.js]
[test_ignore_caught_exceptions.js]

View File

@ -5,12 +5,13 @@
"use strict";
/**
* Manages the addon-sdk loader instance used to load the developer tools.
* Manages the base loader (base-loader.js) instance used to load the developer tools.
*/
var { utils: Cu } = Components;
var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
var { Loader, descriptor, resolveURI } = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {});
var { Loader, Require, resolveURI, unload } =
Cu.import("resource://devtools/shared/base-loader.js", {});
var { requireRawId } = Cu.import("resource://devtools/shared/loader-plugin-raw.jsm", {});
this.EXPORTED_SYMBOLS = ["DevToolsLoader", "devtools", "BuiltinProvider",
@ -67,14 +68,11 @@ BuiltinProvider.prototype = {
if (this.invisibleToDebugger) {
paths.promise = "resource://gre/modules/Promise-backend.js";
}
this.loader = new Loader.Loader({
id: "fx-devtools",
this.loader = new Loader({
paths,
invisibleToDebugger: this.invisibleToDebugger,
sharedGlobal: true,
sharedGlobalBlocklist: [],
sandboxName: "DevTools (Module loader)",
noSandboxAddonId: true,
requireHook: (id, require) => {
if (id.startsWith("raw!")) {
return requireRawId(id, require);
@ -85,7 +83,7 @@ BuiltinProvider.prototype = {
},
unload: function (reason) {
Loader.unload(this.loader, reason);
unload(this.loader, reason);
delete this.loader;
},
};
@ -168,7 +166,7 @@ DevToolsLoader.prototype = {
this._provider.invisibleToDebugger = this.invisibleToDebugger;
this._provider.load();
this.require = Loader.Require(this._provider.loader, { id: "devtools" });
this.require = Require(this._provider.loader, { id: "devtools" });
// Fetch custom pseudo modules and globals
let { modules, globals } = this.require("devtools/shared/builtin-modules");
@ -193,7 +191,7 @@ DevToolsLoader.prototype = {
// Register custom globals to the current loader instance
globals.loader.id = this.id;
Object.defineProperties(loader.globals, descriptor(globals));
Object.defineProperties(loader.globals, Object.getOwnPropertyDescriptors(globals));
// Expose lazy helpers on loader
this.lazyGetter = globals.loader.lazyGetter;

View File

@ -0,0 +1,682 @@
/* 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/. */
/* global __URI__ */
/* exported Loader, resolveURI, Module, Require, unload */
"use strict";
this.EXPORTED_SYMBOLS = ["Loader", "resolveURI", "Module", "Require", "unload"];
const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu,
results: Cr, manager: Cm } = Components;
const systemPrincipal = CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")();
const { loadSubScript } = Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader);
const { notifyObservers } = Cc["@mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
const { normalize, dirname } = Cu.import("resource://gre/modules/osfile/ospath_unix.jsm", {});
XPCOMUtils.defineLazyServiceGetter(this, "resProto",
"@mozilla.org/network/protocol;1?name=resource",
"nsIResProtocolHandler");
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
const { defineLazyGetter } = XPCOMUtils;
// Define some shortcuts.
const bind = Function.call.bind(Function.bind);
function* getOwnIdentifiers(x) {
yield* Object.getOwnPropertyNames(x);
yield* Object.getOwnPropertySymbols(x);
}
function isJSONURI(uri) {
return uri.endsWith(".json");
}
function isJSMURI(uri) {
return uri.endsWith(".jsm");
}
function isJSURI(uri) {
return uri.endsWith(".js");
}
const AbsoluteRegExp = /^(resource|chrome|file|jar):/;
function isAbsoluteURI(uri) {
return AbsoluteRegExp.test(uri);
}
function isRelative(id) {
return id.startsWith(".");
}
function sourceURI(uri) {
return String(uri).split(" -> ").pop();
}
function isntLoaderFrame(frame) {
return frame.fileName !== __URI__;
}
function parseURI(uri) {
return String(uri).split(" -> ").pop();
}
function parseStack(stack) {
let lines = String(stack).split("\n");
return lines.reduce(function (frames, line) {
if (line) {
let atIndex = line.indexOf("@");
let columnIndex = line.lastIndexOf(":");
let lineIndex = line.lastIndexOf(":", columnIndex - 1);
let fileName = parseURI(line.slice(atIndex + 1, lineIndex));
let lineNumber = parseInt(line.slice(lineIndex + 1, columnIndex), 10);
let columnNumber = parseInt(line.slice(columnIndex + 1), 10);
let name = line.slice(0, atIndex).split("(").shift();
frames.unshift({
fileName: fileName,
name: name,
lineNumber: lineNumber,
columnNumber: columnNumber
});
}
return frames;
}, []);
}
function serializeStack(frames) {
return frames.reduce(function (stack, frame) {
return frame.name + "@" +
frame.fileName + ":" +
frame.lineNumber + ":" +
frame.columnNumber + "\n" +
stack;
}, "");
}
function readURI(uri) {
let nsURI = NetUtil.newURI(uri);
if (nsURI.scheme == "resource") {
// Resolve to a real URI, this will catch any obvious bad paths without
// logging assertions in debug builds, see bug 1135219
uri = resProto.resolveURI(nsURI);
}
let stream = NetUtil.newChannel({
uri: NetUtil.newURI(uri, "UTF-8"),
loadUsingSystemPrincipal: true}
).open2();
let count = stream.available();
let data = NetUtil.readInputStreamToString(stream, count, {
charset: "UTF-8"
});
stream.close();
return data;
}
// Combines all arguments into a resolved, normalized path
function join(base, ...paths) {
// If this is an absolute URL, we need to normalize only the path portion,
// or we wind up stripping too many slashes and producing invalid URLs.
let match = /^((?:resource|file|chrome)\:\/\/[^\/]*|jar:[^!]+!)(.*)/.exec(base);
if (match) {
return match[1] + normalize([match[2], ...paths].join("/"));
}
return normalize([base, ...paths].join("/"));
}
// Function takes set of options and returns a JS sandbox. Function may be
// passed set of options:
// - `name`: A string value which identifies the sandbox in about:memory. Will
// throw exception if omitted.
// - `prototype`: Ancestor for the sandbox that will be created. Defaults to
// `{}`.
// - `invisibleToDebugger`: True, if the sandbox is part of the debugger
// implementation and should not be tracked by debugger API.
// For more details see:
// https://developer.mozilla.org/en/Components.utils.Sandbox
function Sandbox(options) {
// Normalize options and rename to match `Cu.Sandbox` expectations.
options = {
// Do not expose `Components` if you really need them (bad idea!) you
// still can expose via prototype.
wantComponents: false,
sandboxName: options.name,
sandboxPrototype: "prototype" in options ? options.prototype : {},
invisibleToDebugger: "invisibleToDebugger" in options ?
options.invisibleToDebugger : false,
waiveInterposition: false
};
let sandbox = Cu.Sandbox(systemPrincipal, options);
delete sandbox.Components;
return sandbox;
}
// Populates `exports` of the given CommonJS `module` object, in the context
// of the given `loader` by evaluating code associated with it.
function load(loader, module) {
let { sandboxes, globals } = loader;
let require = Require(loader, module);
// We expose set of properties defined by `CommonJS` specification via
// prototype of the sandbox. Also globals are deeper in the prototype
// chain so that each module has access to them as well.
let descriptors = {
require: {
configurable: true,
enumerable: true,
writable: true,
value: require
},
module: {
configurable: true,
enumerable: true,
writable: true,
value: module
},
exports: {
configurable: true,
enumerable: true,
writable: true,
value: module.exports
},
};
let sandbox;
if (loader.useSharedGlobalSandbox) {
// Create a new object in this sandbox, that will be used as
// the scope object for this particular module
sandbox = new loader.sharedGlobalSandbox.Object();
descriptors.lazyRequire = {
configurable: true,
value: lazyRequire.bind(sandbox),
};
descriptors.lazyRequireModule = {
configurable: true,
value: lazyRequireModule.bind(sandbox),
};
if ("console" in globals) {
descriptors.console = {
configurable: true,
get() {
return globals.console;
},
};
}
let define = Object.getOwnPropertyDescriptor(globals, "define");
if (define && define.value) {
descriptors.define = define;
}
if ("DOMParser" in globals) {
descriptors.DOMParser = Object.getOwnPropertyDescriptor(globals, "DOMParser");
}
Object.defineProperties(sandbox, descriptors);
} else {
sandbox = Sandbox({
name: module.uri,
prototype: Object.create(globals, descriptors),
invisibleToDebugger: loader.invisibleToDebugger
});
}
sandboxes[module.uri] = sandbox;
let originalExports = module.exports;
try {
loadSubScript(module.uri, sandbox, "UTF-8");
} catch (error) {
let { message, fileName, lineNumber } = error;
let stack = error.stack || Error().stack;
let frames = parseStack(stack).filter(isntLoaderFrame);
let toString = String(error);
let file = sourceURI(fileName);
// Note that `String(error)` where error is from subscript loader does
// not puts `:` after `"Error"` unlike regular errors thrown by JS code.
// If there is a JS stack then this error has already been handled by an
// inner module load.
if (/^Error opening input stream/.test(String(error))) {
let caller = frames.slice(0).pop();
fileName = caller.fileName;
lineNumber = caller.lineNumber;
message = "Module `" + module.id + "` is not found at " + module.uri;
toString = message;
} else if (frames[frames.length - 1].fileName !== file) {
// Workaround for a Bug 910653. Errors thrown by subscript loader
// do not include `stack` field and above created error won't have
// fileName or lineNumber of the module being loaded, so we ensure
// it does.
frames.push({ fileName: file, lineNumber: lineNumber, name: "" });
}
let prototype = typeof (error) === "object" ? error.constructor.prototype :
Error.prototype;
throw Object.create(prototype, {
message: { value: message, writable: true, configurable: true },
fileName: { value: fileName, writable: true, configurable: true },
lineNumber: { value: lineNumber, writable: true, configurable: true },
stack: { value: serializeStack(frames), writable: true, configurable: true },
toString: { value: () => toString, writable: true, configurable: true },
});
}
// Only freeze the exports object if we created it ourselves. Modules
// which completely replace the exports object and still want it
// frozen need to freeze it themselves.
if (module.exports === originalExports) {
Object.freeze(module.exports);
}
return module;
}
// Utility function to normalize module `uri`s so they have `.js` extension.
function normalizeExt(uri) {
if (isJSURI(uri) || isJSONURI(uri) || isJSMURI(uri)) {
return uri;
}
return uri + ".js";
}
// Utility function to join paths. In common case `base` is a
// `requirer.uri` but in some cases it may be `baseURI`. In order to
// avoid complexity we require `baseURI` with a trailing `/`.
function resolve(id, base) {
if (!isRelative(id)) {
return id;
}
let baseDir = dirname(base);
let resolved;
if (baseDir.includes(":")) {
resolved = join(baseDir, id);
} else {
resolved = normalize(`${baseDir}/${id}`);
}
// Joining and normalizing removes the "./" from relative files.
// We need to ensure the resolution still has the root
if (base.startsWith("./")) {
resolved = "./" + resolved;
}
return resolved;
}
function compileMapping(paths) {
// Make mapping array that is sorted from longest path to shortest path.
let mapping = Object.keys(paths)
.sort((a, b) => b.length - a.length)
.map(path => [path, paths[path]]);
const PATTERN = /([.\\?+*(){}[\]^$])/g;
const escapeMeta = str => str.replace(PATTERN, "\\$1");
let patterns = [];
paths = {};
for (let [path, uri] of mapping) {
// Strip off any trailing slashes to make comparisons simpler
if (path.endsWith("/")) {
path = path.slice(0, -1);
uri = uri.replace(/\/+$/, "");
}
paths[path] = uri;
// We only want to match path segments explicitly. Examples:
// * "foo/bar" matches for "foo/bar"
// * "foo/bar" matches for "foo/bar/baz"
// * "foo/bar" does not match for "foo/bar-1"
// * "foo/bar/" does not match for "foo/bar"
// * "foo/bar/" matches for "foo/bar/baz"
//
// Check for an empty path, an exact match, or a substring match
// with the next character being a forward slash.
if (path == "") {
patterns.push("");
} else {
patterns.push(`${escapeMeta(path)}(?=$|/)`);
}
}
let pattern = new RegExp(`^(${patterns.join("|")})`);
// This will replace the longest matching path mapping at the start of
// the ID string with its mapped value.
return id => {
return id.replace(pattern, (m0, m1) => paths[m1]);
};
}
function resolveURI(id, mapping) {
// Do not resolve if already a resource URI
if (isAbsoluteURI(id)) {
return normalizeExt(id);
}
return normalizeExt(mapping(id));
}
/**
* Defines lazy getters on the given object, which lazily require the
* given module the first time they are accessed, and then resolve that
* module's exported properties.
*
* @param {object} obj
* The target object on which to define the lazy getters.
* @param {string} moduleId
* The ID of the module to require, as passed to require().
* @param {Array<string | object>} args
* Any number of properties to import from the module. A string
* will cause the property to be defined which resolves to the
* same property in the module's exports. An object will define a
* lazy getter for every value in the object which corresponds to
* the given key in the module's exports, as in an ordinary
* destructuring assignment.
*/
function lazyRequire(obj, moduleId, ...args) {
let module;
let getModule = () => {
if (!module) {
module = this.require(moduleId);
}
return module;
};
for (let props of args) {
if (typeof props !== "object") {
props = {[props]: props};
}
for (let [fromName, toName] of Object.entries(props)) {
defineLazyGetter(obj, toName, () => getModule()[fromName]);
}
}
}
/**
* Defines a lazy getter on the given object which causes a module to be
* lazily imported the first time it is accessed.
*
* @param {object} obj
* The target object on which to define the lazy getter.
* @param {string} moduleId
* The ID of the module to require, as passed to require().
* @param {string} [prop = moduleId]
* The name of the lazy getter property to define.
*/
function lazyRequireModule(obj, moduleId, prop = moduleId) {
defineLazyGetter(obj, prop, () => this.require(moduleId));
}
// Creates version of `require` that will be exposed to the given `module`
// in the context of the given `loader`. Each module gets own limited copy
// of `require` that is allowed to load only a modules that are associated
// with it during link time.
function Require(loader, requirer) {
let {
modules, mapping, mappingCache, requireHook
} = loader;
function require(id) {
if (!id) {
// Throw if `id` is not passed.
throw Error("You must provide a module name when calling require() from "
+ requirer.id, requirer.uri);
}
if (requireHook) {
return requireHook(id, _require);
}
return _require(id);
}
function _require(id) {
let { uri, requirement } = getRequirements(id);
let module = null;
// If module is already cached by loader then just use it.
if (uri in modules) {
module = modules[uri];
} else if (isJSMURI(uri)) {
module = modules[uri] = Module(requirement, uri);
module.exports = Cu.import(uri, {});
} else if (isJSONURI(uri)) {
let data;
// First attempt to load and parse json uri
// ex: `test.json`
// If that doesn"t exist, check for `test.json.js`
// for node parity
try {
data = JSON.parse(readURI(uri));
module = modules[uri] = Module(requirement, uri);
module.exports = data;
} catch (err) {
// If error thrown from JSON parsing, throw that, do not
// attempt to find .json.js file
if (err && /JSON\.parse/.test(err.message)) {
throw err;
}
uri = uri + ".js";
}
}
// If not yet cached, load and cache it.
// We also freeze module to prevent it from further changes
// at runtime.
if (!(uri in modules)) {
// Many of the loader's functionalities are dependent
// on modules[uri] being set before loading, so we set it and
// remove it if we have any errors.
module = modules[uri] = Module(requirement, uri);
try {
Object.freeze(load(loader, module));
} catch (e) {
// Clear out modules cache so we can throw on a second invalid require
delete modules[uri];
// Also clear out the Sandbox that was created
delete loader.sandboxes[uri];
throw e;
}
}
return module.exports;
}
// Resolution function taking a module name/path and
// returning a resourceURI and a `requirement` used by the loader.
// Used by both `require` and `require.resolve`.
function getRequirements(id) {
if (!id) {
// Throw if `id` is not passed.
throw Error("you must provide a module name when calling require() from "
+ requirer.id, requirer.uri);
}
let requirement, uri;
if (modules[id]) {
uri = requirement = id;
} else if (requirer) {
// Resolve `id` to its requirer if it's relative.
requirement = resolve(id, requirer.id);
} else {
requirement = id;
}
// Resolves `uri` of module using loaders resolve function.
if (!uri) {
if (mappingCache.has(requirement)) {
uri = mappingCache.get(requirement);
} else {
uri = resolveURI(requirement, mapping);
mappingCache.set(requirement, uri);
}
}
// Throw if `uri` can not be resolved.
if (!uri) {
throw Error("Module: Can not resolve '" + id + "' module required by " +
requirer.id + " located at " + requirer.uri, requirer.uri);
}
return { uri: uri, requirement: requirement };
}
// Expose the `resolve` function for this `Require` instance
require.resolve = _require.resolve = function (id) {
let { uri } = getRequirements(id);
return uri;
};
// This is like webpack's require.context. It returns a new require
// function that prepends the prefix to any requests.
require.context = prefix => {
return id => {
return require(prefix + id);
};
};
return require;
}
// Makes module object that is made available to CommonJS modules when they
// are evaluated, along with `exports` and `require`.
function Module(id, uri) {
return Object.create(null, {
id: { enumerable: true, value: id },
exports: { enumerable: true, writable: true, value: Object.create(null),
configurable: true },
uri: { value: uri }
});
}
// Takes `loader`, and unload `reason` string and notifies all observers that
// they should cleanup after them-self.
function unload(loader, reason) {
// subject is a unique object created per loader instance.
// This allows any code to cleanup on loader unload regardless of how
// it was loaded. To handle unload for specific loader subject may be
// asserted against loader.destructor or require("@loader/unload")
// Note: We don not destroy loader's module cache or sandboxes map as
// some modules may do cleanup in subsequent turns of event loop. Destroying
// cache may cause module identity problems in such cases.
let subject = { wrappedJSObject: loader.destructor };
notifyObservers(subject, "sdk:loader:destroy", reason);
}
// Function makes new loader that can be used to load CommonJS modules.
// Loader takes following options:
// - `paths`: Mandatory dictionary of require path mapped to absolute URIs.
// Object keys are path prefix used in require(), values are URIs where each
// prefix should be mapped to.
// - `sharedGlobal`: Boolean, if True, loads all module in a single, shared
// global in order to create only one global and compartment.
// - `globals`: Optional map of globals, that all module scopes will inherit
// from. Map is also exposed under `globals` property of the returned loader
// so it can be extended further later. Defaults to `{}`.
// - `sandboxName`: String, name of the sandbox displayed in about:memory.
// - `invisibleToDebugger`: Boolean. Should be true when loading debugger
// modules, in order to ignore them from the Debugger API.
// - `sandboxPrototype`: Object used to define globals on all module's
// sandboxes.
// - `requireHook`: Optional function used to replace native require function
// from loader. This function receive the module path as first argument,
// and native require method as second argument.
function Loader(options) {
let { paths, sharedGlobal, globals } = options;
if (!globals) {
globals = {};
}
// We create an identity object that will be dispatched on an unload
// event as subject. This way unload listeners will be able to assert
// which loader is unloaded. Please note that we intentionally don"t
// use `loader` as subject to prevent a loader access leakage through
// observer notifications.
let destructor = Object.create(null);
let mapping = compileMapping(paths);
// Define pseudo modules.
let modules = {
"@loader/unload": destructor,
"@loader/options": options,
"chrome": { Cc, Ci, Cu, Cr, Cm,
CC: bind(CC, Components), components: Components,
ChromeWorker
}
};
const builtinModuleExports = modules;
modules = {};
for (let id of Object.keys(builtinModuleExports)) {
// We resolve `uri` from `id` since modules are cached by `uri`.
let uri = resolveURI(id, mapping);
let module = Module(id, uri);
// Lazily expose built-in modules in order to
// allow them to be loaded lazily.
Object.defineProperty(module, "exports", {
enumerable: true,
get: function () {
return builtinModuleExports[id];
}
});
modules[uri] = module;
}
// Create the unique sandbox we will be using for all modules,
// so that we prevent creating a new compartment per module.
// The side effect is that all modules will share the same
// global objects.
let sharedGlobalSandbox = Sandbox({
name: options.sandboxName || "DevTools",
invisibleToDebugger: options.invisibleToDebugger || false,
prototype: options.sandboxPrototype || globals,
});
if (options.sandboxPrototype) {
// If we were given a sandboxPrototype, we have to define the globals on
// the sandbox directly. Note that this will not work for callers who
// depend on being able to add globals after the loader was created.
for (let name of getOwnIdentifiers(globals)) {
Object.defineProperty(sharedGlobalSandbox, name,
Object.getOwnPropertyDescriptor(globals, name));
}
}
// Loader object is just a representation of a environment
// state. We mark its properties non-enumerable
// as they are pure implementation detail that no one should rely upon.
let returnObj = {
destructor: { enumerable: false, value: destructor },
globals: { enumerable: false, value: globals },
mapping: { enumerable: false, value: mapping },
mappingCache: { enumerable: false, value: new Map() },
// Map of module objects indexed by module URIs.
modules: { enumerable: false, value: modules },
useSharedGlobalSandbox: { enumerable: false, value: !!sharedGlobal },
sharedGlobalSandbox: { enumerable: false, value: sharedGlobalSandbox },
// Map of module sandboxes indexed by module URIs.
sandboxes: { enumerable: false, value: {} },
// Whether the modules loaded should be ignored by the debugger
invisibleToDebugger: { enumerable: false,
value: options.invisibleToDebugger || false },
requireHook: { enumerable: false, value: options.requireHook },
};
return Object.create(null, returnObj);
}

View File

@ -14,7 +14,6 @@
*/
const { Cu, CC, Cc, Ci } = require("chrome");
const { Loader } = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {});
const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
const jsmScope = Cu.import("resource://gre/modules/Services.jsm", {});
const { Services } = jsmScope;
@ -172,7 +171,6 @@ function lazyRequireGetter(obj, property, module, destructure) {
// List of pseudo modules exposed to all devtools modules.
exports.modules = {
"Services": Object.create(Services),
"toolkit/loader": Loader,
promise,
PromiseDebugging,
ChromeUtils,

View File

@ -26,7 +26,6 @@ DevToolsModules(
'performance-recording.js',
'performance.js',
'preference.js',
'profiler.js',
'promises.js',
'reflow.js',
'storage.js',

View File

@ -1,77 +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";
const { Cu } = require("chrome");
const {
Front,
FrontClassWithSpec,
custom
} = require("devtools/shared/protocol");
const { profilerSpec } = require("devtools/shared/specs/profiler");
/**
* This can be used on older Profiler implementations, but the methods cannot
* be changed -- you must introduce a new method, and detect the server.
*/
exports.ProfilerFront = FrontClassWithSpec(profilerSpec, {
initialize: function (client, form) {
Front.prototype.initialize.call(this, client, form);
this.actorID = form.profilerActor;
this.manage(this);
this._onProfilerEvent = this._onProfilerEvent.bind(this);
this.on("*", this._onProfilerEvent);
},
destroy: function () {
this.off("*", this._onProfilerEvent);
Front.prototype.destroy.call(this);
},
/**
* If using the protocol.js Fronts, then make stringify default,
* since the read/write mechanisms will expose it as an object anyway, but
* this lets other consumers who connect directly (xpcshell tests, Gecko Profiler) to
* have unchanged behaviour.
*/
getProfile: custom(function (options) {
return this._getProfile(Object.assign({ stringify: true }, options));
}, {
impl: "_getProfile"
}),
/**
* Also emit an old `eventNotification` for older consumers of the profiler.
*/
_onProfilerEvent: function (eventName, data) {
// If this event already passed through once, don't repropagate
if (data.relayed) {
return;
}
data.relayed = true;
if (eventName === "eventNotification") {
// If this is `eventNotification`, this is coming from an older Gecko (<Fx42)
// that doesn't use protocol.js style events. Massage it to emit a protocol.js
// style event as well.
this.emit(data.topic, data);
} else {
// Otherwise if a modern protocol.js event, emit it also as `eventNotification`
// for compatibility reasons on the client (like for any add-ons/Gecko Profiler
// using this event) and log a deprecation message if there is a listener.
this.conn.emit("eventNotification", {
subject: data.subject,
topic: data.topic,
data: data.data,
details: data.details
});
if (this.conn._getListeners("eventNotification").length) {
Cu.reportError(`
ProfilerActor's "eventNotification" on the DebuggerClient has been deprecated.
Use the ProfilerFront found in "devtools/server/actors/profiler".`);
}
}
},
});

View File

@ -44,6 +44,7 @@ JAR_MANIFESTS += ['jar.mn']
DevToolsModules(
'async-storage.js',
'async-utils.js',
'base-loader.js',
'builtin-modules.js',
'content-observer.js',
'debounce.js',

View File

@ -31,7 +31,6 @@ DevToolsModules(
'performance-recording.js',
'performance.js',
'preference.js',
'profiler.js',
'promises.js',
'reflow.js',
'script.js',

View File

@ -1,121 +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";
const {
Arg,
Option,
RetVal,
generateActorSpec,
types
} = require("devtools/shared/protocol");
types.addType("profiler-data", {
// On Fx42+, the profile is only deserialized on the front; older
// servers will get the profiler data as an object from nsIProfiler,
// causing one parse/stringify cycle, then again implicitly in a packet.
read: (v) => {
if (typeof v.profile === "string") {
// Create a new response object since `profile` is read only.
let newValue = Object.create(null);
newValue.profile = JSON.parse(v.profile);
newValue.currentTime = v.currentTime;
return newValue;
}
return v;
}
});
const profilerSpec = generateActorSpec({
typeName: "profiler",
/**
* The set of events the ProfilerActor emits over RDP.
*/
events: {
"console-api-profiler": {
data: Arg(0, "json"),
},
"profiler-started": {
data: Arg(0, "json"),
},
"profiler-stopped": {
data: Arg(0, "json"),
},
"profiler-status": {
data: Arg(0, "json"),
},
// Only for older geckos, pre-protocol.js ProfilerActor (<Fx42).
// Emitted on other events as a transition from older profiler events
// to newer ones.
"eventNotification": {
subject: Option(0, "json"),
topic: Option(0, "string"),
details: Option(0, "json")
}
},
methods: {
startProfiler: {
// Write out every property in the request, since we want all these options to be
// on the packet's top-level for backwards compatibility, when the profiler actor
// was not using protocol.js (<Fx42)
request: {
entries: Option(0, "nullable:number"),
interval: Option(0, "nullable:number"),
features: Option(0, "nullable:array:string"),
threadFilters: Option(0, "nullable:array:string"),
},
response: RetVal("json"),
},
stopProfiler: {
response: RetVal("json"),
},
getProfile: {
request: {
startTime: Option(0, "nullable:number"),
stringify: Option(0, "nullable:boolean")
},
response: RetVal("profiler-data")
},
getFeatures: {
response: RetVal("json")
},
getBufferInfo: {
response: RetVal("json")
},
getStartOptions: {
response: RetVal("json")
},
isActive: {
response: RetVal("json")
},
sharedLibraries: {
response: RetVal("json")
},
registerEventNotifications: {
// Explicitly enumerate the arguments
// @see ProfilerActor#startProfiler
request: {
events: Option(0, "nullable:array:string"),
},
response: RetVal("json")
},
unregisterEventNotifications: {
// Explicitly enumerate the arguments
// @see ProfilerActor#startProfiler
request: {
events: Option(0, "nullable:array:string"),
},
response: RetVal("json")
},
setProfilerStatusInterval: {
request: { interval: Arg(0, "number") },
oneway: true
}
}
});
exports.profilerSpec = profilerSpec;

View File

@ -279,6 +279,7 @@
#include "nsIURIClassifier.h"
#include "mozilla/DocumentStyleRootIterator.h"
#include "mozilla/ServoRestyleManager.h"
#include "mozilla/ClearOnShutdown.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -319,6 +320,65 @@ GetHttpChannelHelper(nsIChannel* aChannel, nsIHttpChannel** aHttpChannel)
return NS_OK;
}
////////////////////////////////////////////////////////////////////
// PrincipalFlashClassifier
// Classify the flash based on the document principal.
// The usage of this class is as follows:
//
// 1) Call AsyncClassify() as early as possible to asynchronously do
// classification against all the flash blocking related tables
// via nsIURIClassifier.asyncClassifyLocalWithTables.
//
// 2) At any time you need the classification result, call Result()
// and it is guaranteed to give you the result. Note that you have
// to specify "aIsThirdParty" to the function so please make sure
// you can already correctly decide if the document is third-party.
//
// Behind the scenes, the sync classification API
// (nsIURIClassifier.classifyLocalWithTable) may be called as a fallback to
// synchronously get the result if the asyncClassifyLocalWithTables hasn't
// been done yet.
//
// 3) You can call Result() as many times as you want and only the first time
// it may unfortunately call the blocking sync API. The subsequent call
// will just return the result that came out at the first time.
//
class PrincipalFlashClassifier final : public nsIURIClassifierCallback
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIURICLASSIFIERCALLBACK
PrincipalFlashClassifier();
// Fire async classification based on the given principal.
void AsyncClassify(nsIPrincipal* aPrincipal);
// Would block if the result hasn't come out.
mozilla::dom::FlashClassification ClassifyMaybeSync(nsIPrincipal* aPrincipal,
bool aIsThirdParty);
private:
~PrincipalFlashClassifier() = default;
void Reset();
bool EnsureUriClassifier();
mozilla::dom::FlashClassification CheckIfClassifyNeeded(nsIPrincipal* aPrincipal);
mozilla::dom::FlashClassification Resolve(bool aIsThirdParty);
mozilla::dom::FlashClassification AsyncClassifyInternal(nsIPrincipal* aPrincipal);
void GetClassificationTables(bool aIsThirdParty, nsACString& aTables);
// For the fallback sync classification.
nsCOMPtr<nsIURI> mClassificationURI;
nsCOMPtr<nsIURIClassifier> mUriClassifier;
bool mAsyncClassified;
nsTArray<nsCString> mMatchedTables;
mozilla::dom::FlashClassification mResult;
};
#define NAME_NOT_VALID ((nsSimpleContentList*)1)
nsIdentifierMapEntry::~nsIdentifierMapEntry()
@ -1575,6 +1635,9 @@ nsDocument::nsDocument(const char* aContentType)
// void state used to differentiate an empty source from an unselected source
mPreloadPictureFoundSource.SetIsVoid(true);
// For determining if this is a flash document which should be
// blocked based on its principal.
mPrincipalFlashClassifier = new PrincipalFlashClassifier();
}
void
@ -2761,6 +2824,11 @@ nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
}
// Perform a async flash classification based on the doc principal
// in an early stage to reduce the blocking time.
mFlashClassification = FlashClassification::Unclassified;
mPrincipalFlashClassifier->AsyncClassify(GetPrincipal());
return NS_OK;
}
@ -13483,6 +13551,23 @@ nsIDocument::UpdateStyleBackendType()
#endif
}
/**
* Retrieves the classification of the Flash plugins in the document based on
* the classification lists. We perform AsyncInitFlashClassification on
* StartDocumentLoad() and the result may not be initialized when this function
* gets called. In that case, We can only unfortunately have a blocking wait.
*
* For more information, see
* toolkit/components/url-classifier/flash-block-lists.rst
*/
FlashClassification
nsDocument::PrincipalFlashClassification()
{
MOZ_ASSERT(mPrincipalFlashClassifier);
return mPrincipalFlashClassifier->ClassifyMaybeSync(GetPrincipal(),
IsThirdParty());
}
/**
* Helper function for |nsDocument::PrincipalFlashClassification|
*
@ -13523,27 +13608,279 @@ ArrayContainsTable(const nsTArray<nsCString>& aTableArray,
return false;
}
/**
* Retrieves the classification of the Flash plugins in the document based on
* the classification lists.
*
* For more information, see
* toolkit/components/url-classifier/flash-block-lists.rst
*/
FlashClassification
nsDocument::PrincipalFlashClassification()
namespace {
// An object to store all preferences we need for flash blocking feature.
struct PrefStore
{
nsresult rv;
PrefStore()
{
Preferences::AddBoolVarCache(&mFlashBlockEnabled,
"plugins.flashBlock.enabled");
Preferences::AddBoolVarCache(&mPluginsHttpOnly,
"plugins.http_https_only");
bool httpOnly = Preferences::GetBool("plugins.http_https_only", true);
bool flashBlock = Preferences::GetBool("plugins.flashBlock.enabled", false);
// We only need to register string-typed preferences.
Preferences::RegisterCallback(UpdateStringPrefs, "urlclassifier.flashAllowTable", this);
Preferences::RegisterCallback(UpdateStringPrefs, "urlclassifier.flashAllowExceptTable", this);
Preferences::RegisterCallback(UpdateStringPrefs, "urlclassifier.flashTable", this);
Preferences::RegisterCallback(UpdateStringPrefs, "urlclassifier.flashExceptTable", this);
Preferences::RegisterCallback(UpdateStringPrefs, "urlclassifier.flashSubDocTable", this);
Preferences::RegisterCallback(UpdateStringPrefs, "urlclassifier.flashSubDocExceptTable", this);
// If neither pref is on, skip the null-principal and principal URI checks.
if (!httpOnly && !flashBlock) {
UpdateStringPrefs();
}
~PrefStore()
{
Preferences::UnregisterCallback(UpdateStringPrefs, "urlclassifier.flashAllowTable", this);
Preferences::UnregisterCallback(UpdateStringPrefs, "urlclassifier.flashAllowExceptTable", this);
Preferences::UnregisterCallback(UpdateStringPrefs, "urlclassifier.flashTable", this);
Preferences::UnregisterCallback(UpdateStringPrefs, "urlclassifier.flashExceptTable", this);
Preferences::UnregisterCallback(UpdateStringPrefs, "urlclassifier.flashSubDocTable", this);
Preferences::UnregisterCallback(UpdateStringPrefs, "urlclassifier.flashSubDocExceptTable", this);
}
void UpdateStringPrefs()
{
Preferences::GetCString("urlclassifier.flashAllowTable", mAllowTables);
Preferences::GetCString("urlclassifier.flashAllowExceptTable", mAllowExceptionsTables);
Preferences::GetCString("urlclassifier.flashTable", mDenyTables);
Preferences::GetCString("urlclassifier.flashExceptTable", mDenyExceptionsTables);
Preferences::GetCString("urlclassifier.flashSubDocTable", mSubDocDenyTables);
Preferences::GetCString("urlclassifier.flashSubDocExceptTable", mSubDocDenyExceptionsTables);
}
static void UpdateStringPrefs(const char*, void* aClosure)
{
static_cast<PrefStore*>(aClosure)->UpdateStringPrefs();
}
bool mFlashBlockEnabled;
bool mPluginsHttpOnly;
nsCString mAllowTables;
nsCString mAllowExceptionsTables;
nsCString mDenyTables;
nsCString mDenyExceptionsTables;
nsCString mSubDocDenyTables;
nsCString mSubDocDenyExceptionsTables;
};
static const
PrefStore& GetPrefStore()
{
static UniquePtr<PrefStore> sPrefStore;
if (!sPrefStore) {
sPrefStore.reset(new PrefStore());
ClearOnShutdown(&sPrefStore);
}
return *sPrefStore;
}
} // end of unnamed namespace.
////////////////////////////////////////////////////////////////////
// PrincipalFlashClassifier implementation.
NS_IMPL_ISUPPORTS(PrincipalFlashClassifier, nsIURIClassifierCallback)
PrincipalFlashClassifier::PrincipalFlashClassifier()
{
Reset();
}
void
PrincipalFlashClassifier::Reset()
{
mAsyncClassified = false;
mMatchedTables.Clear();
mResult = FlashClassification::Unclassified;
}
void
PrincipalFlashClassifier::GetClassificationTables(bool aIsThirdParty,
nsACString& aTables)
{
aTables.Truncate();
auto& prefs = GetPrefStore();
MaybeAddTableToTableList(prefs.mAllowTables, aTables);
MaybeAddTableToTableList(prefs.mAllowExceptionsTables, aTables);
MaybeAddTableToTableList(prefs.mDenyTables, aTables);
MaybeAddTableToTableList(prefs.mDenyExceptionsTables, aTables);
if (aIsThirdParty) {
MaybeAddTableToTableList(prefs.mSubDocDenyTables, aTables);
MaybeAddTableToTableList(prefs.mSubDocDenyExceptionsTables, aTables);
}
}
bool
PrincipalFlashClassifier::EnsureUriClassifier()
{
if (!mUriClassifier) {
mUriClassifier = do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID);
}
return !!mUriClassifier;
}
FlashClassification
PrincipalFlashClassifier::ClassifyMaybeSync(nsIPrincipal* aPrincipal, bool aIsThirdParty)
{
if (FlashClassification::Unclassified != mResult) {
// We already have the result. Just return it.
return mResult;
}
// TODO: Bug 1342333 - Entirely remove the use of the sync API
// (ClassifyLocalWithTables).
if (!mAsyncClassified) {
//
// We may
// 1) have called AsyncClassifyLocalWithTables but OnClassifyComplete
// hasn't been called.
// 2) haven't even called AsyncClassifyLocalWithTables.
//
// In both cases we need to do the synchronous classification as the fallback.
//
if (!EnsureUriClassifier()) {
return FlashClassification::Denied;
}
mResult = CheckIfClassifyNeeded(aPrincipal);
if (FlashClassification::Unclassified != mResult) {
return mResult;
}
nsresult rv;
nsAutoCString classificationTables;
GetClassificationTables(aIsThirdParty, classificationTables);
if (!mClassificationURI) {
rv = aPrincipal->GetURI(getter_AddRefs(mClassificationURI));
if (NS_FAILED(rv) || !mClassificationURI) {
mResult = FlashClassification::Denied;
return mResult;
}
}
rv = mUriClassifier->ClassifyLocalWithTables(mClassificationURI,
classificationTables,
mMatchedTables);
if (NS_WARN_IF(NS_FAILED(rv))) {
if (rv == NS_ERROR_MALFORMED_URI) {
// This means that the URI had no hostname (ex: file://doc.html). In this
// case, we allow the default (Unknown plugin) behavior.
mResult = FlashClassification::Unknown;
} else {
mResult = FlashClassification::Denied;
}
return mResult;
}
}
// Resolve the result based on mMatchedTables and aIsThirdParty.
mResult = Resolve(aIsThirdParty);
MOZ_ASSERT(FlashClassification::Unclassified != mResult);
// The subsequent call of Result() will return the resolved result
// and never reach here until Reset() is called.
return mResult;
}
/*virtual*/ nsresult
PrincipalFlashClassifier::OnClassifyComplete(nsresult /*aErrorCode*/,
const nsACString& aLists, // Only this matters.
const nsACString& /*aProvider*/,
const nsACString& /*aPrefix*/)
{
mAsyncClassified = true;
if (FlashClassification::Unclassified != mResult) {
// Result() has been called prior to this callback.
return NS_OK;
}
// TODO: Bug 1364804 - We should use a callback type which notifies
// the result as a string array rather than a formatted string.
// We only populate the matched list without resolving the classification
// result because we are not sure if the parent doc has been properly set.
// We also parse the comma-separated tables to array. (the code is copied
// from Classifier::SplitTables.)
nsACString::const_iterator begin, iter, end;
aLists.BeginReading(begin);
aLists.EndReading(end);
while (begin != end) {
iter = begin;
FindCharInReadable(',', iter, end);
nsDependentCSubstring table = Substring(begin,iter);
if (!table.IsEmpty()) {
mMatchedTables.AppendElement(Substring(begin, iter));
}
begin = iter;
if (begin != end) {
begin++;
}
}
return NS_OK;
}
// We resolve the classification result based on aIsThirdParty
// and the matched tables we got ealier on (via either sync or async API).
FlashClassification
PrincipalFlashClassifier::Resolve(bool aIsThirdParty)
{
MOZ_ASSERT(FlashClassification::Unclassified == mResult,
"We already have resolved classification result.");
if (mMatchedTables.IsEmpty()) {
return FlashClassification::Unknown;
}
nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
auto& prefs = GetPrefStore();
if (ArrayContainsTable(mMatchedTables, prefs.mDenyTables) &&
!ArrayContainsTable(mMatchedTables, prefs.mDenyExceptionsTables)) {
return FlashClassification::Denied;
} else if (ArrayContainsTable(mMatchedTables, prefs.mAllowTables) &&
!ArrayContainsTable(mMatchedTables, prefs.mAllowExceptionsTables)) {
return FlashClassification::Allowed;
}
if (aIsThirdParty && ArrayContainsTable(mMatchedTables, prefs.mSubDocDenyTables) &&
!ArrayContainsTable(mMatchedTables, prefs.mSubDocDenyExceptionsTables)) {
return FlashClassification::Denied;
}
return FlashClassification::Unknown;
}
void
PrincipalFlashClassifier::AsyncClassify(nsIPrincipal* aPrincipal)
{
MOZ_ASSERT(FlashClassification::Unclassified == mResult,
"The old classification result should be reset first.");
Reset();
mResult = AsyncClassifyInternal(aPrincipal);
}
FlashClassification
PrincipalFlashClassifier::CheckIfClassifyNeeded(nsIPrincipal* aPrincipal)
{
nsresult rv;
auto& prefs = GetPrefStore();
// If neither pref is on, skip the null-principal and principal URI checks.
if (prefs.mPluginsHttpOnly && !prefs.mFlashBlockEnabled) {
return FlashClassification::Unknown;
}
nsCOMPtr<nsIPrincipal> principal = aPrincipal;
if (principal->GetIsNullPrincipal()) {
return FlashClassification::Denied;
}
@ -13554,7 +13891,7 @@ nsDocument::PrincipalFlashClassification()
return FlashClassification::Denied;
}
if (httpOnly) {
if (prefs.mPluginsHttpOnly) {
// Only allow plugins for documents from an HTTP/HTTPS origin. This should
// allow dependent data: URIs to load plugins, but not:
// * chrome documents
@ -13570,49 +13907,50 @@ nsDocument::PrincipalFlashClassification()
// If flash blocking is disabled, it is equivalent to all sites being
// on neither list.
if (!flashBlock) {
if (!prefs.mFlashBlockEnabled) {
return FlashClassification::Unknown;
}
nsAutoCString allowTables, allowExceptionsTables,
denyTables, denyExceptionsTables,
subDocDenyTables, subDocDenyExceptionsTables,
tables;
Preferences::GetCString("urlclassifier.flashAllowTable", allowTables);
MaybeAddTableToTableList(allowTables, tables);
Preferences::GetCString("urlclassifier.flashAllowExceptTable",
allowExceptionsTables);
MaybeAddTableToTableList(allowExceptionsTables, tables);
Preferences::GetCString("urlclassifier.flashTable", denyTables);
MaybeAddTableToTableList(denyTables, tables);
Preferences::GetCString("urlclassifier.flashExceptTable",
denyExceptionsTables);
MaybeAddTableToTableList(denyExceptionsTables, tables);
return FlashClassification::Unclassified;
}
bool isThirdPartyDoc = IsThirdParty();
if (isThirdPartyDoc) {
Preferences::GetCString("urlclassifier.flashSubDocTable",
subDocDenyTables);
MaybeAddTableToTableList(subDocDenyTables, tables);
Preferences::GetCString("urlclassifier.flashSubDocExceptTable",
subDocDenyExceptionsTables);
MaybeAddTableToTableList(subDocDenyExceptionsTables, tables);
// Using nsIURIClassifier.asyncClassifyLocalWithTables to do classification
// against the flash related tables based on the given principal.
FlashClassification
PrincipalFlashClassifier::AsyncClassifyInternal(nsIPrincipal* aPrincipal)
{
auto result = CheckIfClassifyNeeded(aPrincipal);
if (FlashClassification::Unclassified != result) {
return result;
}
// We haven't been able to decide if it's a third party document
// since determining if a document is third-party may depend on its
// parent document. At the time we call AsyncClassifyInternal
// (i.e. StartDocumentLoad) the parent document may not have been
// set. As a result, we wait until Resolve() to be called to
// take "is third party" into account. At this point, we just assume
// it's third-party to include every list.
nsAutoCString tables;
GetClassificationTables(true, tables);
if (tables.IsEmpty()) {
return FlashClassification::Unknown;
}
nsCOMPtr<nsIURIClassifier> uriClassifier =
do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
if (!EnsureUriClassifier()) {
return FlashClassification::Denied;
}
nsTArray<nsCString> results;
rv = uriClassifier->ClassifyLocalWithTables(classificationURI,
tables,
results);
nsresult rv = aPrincipal->GetURI(getter_AddRefs(mClassificationURI));
if (NS_FAILED(rv) || !mClassificationURI) {
return FlashClassification::Denied;
}
rv = mUriClassifier->AsyncClassifyLocalWithTables(mClassificationURI,
tables,
this);
if (NS_FAILED(rv)) {
if (rv == NS_ERROR_MALFORMED_URI) {
// This means that the URI had no hostname (ex: file://doc.html). In this
@ -13623,24 +13961,7 @@ nsDocument::PrincipalFlashClassification()
}
}
if (results.IsEmpty()) {
return FlashClassification::Unknown;
}
if (ArrayContainsTable(results, denyTables) &&
!ArrayContainsTable(results, denyExceptionsTables)) {
return FlashClassification::Denied;
} else if (ArrayContainsTable(results, allowTables) &&
!ArrayContainsTable(results, allowExceptionsTables)) {
return FlashClassification::Allowed;
}
if (isThirdPartyDoc && ArrayContainsTable(results, subDocDenyTables) &&
!ArrayContainsTable(results, subDocDenyExceptionsTables)) {
return FlashClassification::Denied;
}
return FlashClassification::Unknown;
return FlashClassification::Unclassified;
}
FlashClassification

View File

@ -72,6 +72,7 @@
#include "CustomElementRegistry.h"
#include "mozilla/dom/Performance.h"
#include "mozilla/Maybe.h"
#include "nsIURIClassifier.h"
#define XML_DECLARATION_BITS_DECLARATION_EXISTS (1 << 0)
#define XML_DECLARATION_BITS_ENCODING_EXISTS (1 << 1)
@ -346,6 +347,9 @@ protected:
bool mHaveShutDown;
};
// For classifying a flash document based on its principal.
class PrincipalFlashClassifier;
// Base class for our document implementations.
class nsDocument : public nsIDocument,
public nsIDOMDocument,
@ -1184,6 +1188,7 @@ protected:
// non-null when this document is in fullscreen mode.
nsWeakPtr mFullscreenRoot;
RefPtr<PrincipalFlashClassifier> mPrincipalFlashClassifier;
mozilla::dom::FlashClassification mFlashClassification;
// Do not use this value directly. Call the |IsThirdParty()| method, which
// caches its result here.

View File

@ -266,6 +266,17 @@ IMEStateManager::NotifyIMEOfBlurForChildProcess()
}
MOZ_ASSERT(sFocusedIMEWidget);
if (MOZ_LOG_TEST(sISMLog, LogLevel::Debug) && sTextCompositions) {
RefPtr<TextComposition> composition =
sTextCompositions->GetCompositionFor(sFocusedIMEWidget);
if (composition) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" NotifyIMEOfBlurForChildProcess(), sFocusedIMEWidget still has "
"composition"));
}
}
NotifyIME(NOTIFY_IME_OF_BLUR, sFocusedIMEWidget, sFocusedIMETabParent);
MOZ_ASSERT(!sFocusedIMETabParent);
@ -486,9 +497,12 @@ IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
if (composition) {
// However, don't commit the composition if we're being inactivated
// but the composition should be kept even during deactive.
// Note that oldWidget and sFocusedIMEWidget may be different here (in
// such case, sFocusedIMEWidget is perhaps nullptr). For example, IME
// may receive only blur notification but still has composition.
// We need to clean up only the oldWidget's composition state here.
if (aPresContext ||
!sFocusedIMEWidget->IMENotificationRequestsRef().
WantDuringDeactive()) {
!oldWidget->IMENotificationRequestsRef().WantDuringDeactive()) {
MOZ_LOG(sISMLog, LogLevel::Info,
(" OnChangeFocusInternal(), requesting to commit composition to "
"the (previous) focused widget"));

View File

@ -3837,6 +3837,8 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
mReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING, "HTMLMediaElement::mReadyState"),
mLoadWaitStatus(NOT_WAITING),
mVolume(1.0),
mIsAudioTrackAudible(false),
mMuted(0),
mPreloadAction(PRELOAD_UNDEFINED),
mLastCurrentTime(0.0),
mFragmentStart(-1.0),
@ -3850,8 +3852,7 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
mLoadedDataFired(false),
mAutoplaying(true),
mAutoplayEnabled(true),
mPaused(true),
mMuted(0),
mPaused(true, *this),
mStatsShowing(false),
mAllowCasting(false),
mIsCasting(false),
@ -3881,7 +3882,6 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
mHasUserInteraction(false),
mFirstFrameLoaded(false),
mDefaultPlaybackStartPosition(0.0),
mIsAudioTrackAudible(false),
mHasSuspendTaint(false),
mMediaTracksConstructed(false),
mVisibilityState(Visibility::UNTRACKED),
@ -3896,8 +3896,6 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
double defaultVolume = Preferences::GetFloat("media.default_volume", 1.0);
SetVolume(defaultVolume, rv);
mPaused.SetOuter(this);
RegisterActivityObserver();
NotifyOwnerDocumentActivityChanged();
@ -4188,17 +4186,16 @@ void
HTMLMediaElement::WakeLockBoolWrapper::UpdateWakeLock()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mOuter);
bool playing = !mValue;
bool isAudible = mOuter->Volume() > 0.0 &&
!mOuter->mMuted &&
mOuter->mIsAudioTrackAudible;
bool isAudible = mOuter.Volume() > 0.0 &&
!mOuter.mMuted &&
mOuter.mIsAudioTrackAudible;
// when playing audible media.
if (playing && isAudible) {
mOuter->WakeLockCreate();
mOuter.WakeLockCreate();
} else {
mOuter->WakeLockRelease();
mOuter.WakeLockRelease();
}
}

View File

@ -818,15 +818,13 @@ protected:
class WakeLockBoolWrapper {
public:
explicit WakeLockBoolWrapper(bool val = false)
: mValue(val)
, mOuter(nullptr)
WakeLockBoolWrapper(bool aVal, HTMLMediaElement& aOuter)
: mValue(aVal)
, mOuter(aOuter)
{}
~WakeLockBoolWrapper() {};
void SetOuter(HTMLMediaElement* outer) { mOuter = outer; }
MOZ_IMPLICIT operator bool() const { return mValue; }
WakeLockBoolWrapper& operator=(bool val);
@ -836,7 +834,7 @@ protected:
void UpdateWakeLock();
private:
bool mValue;
HTMLMediaElement* mOuter;
HTMLMediaElement& mOuter;
};
// Holds references to the DOM wrappers for the MediaStreams that we're
@ -1450,6 +1448,18 @@ protected:
// Current audio volume
double mVolume;
// True if the audio track is not silent.
bool mIsAudioTrackAudible;
enum MutedReasons {
MUTED_BY_CONTENT = 0x01,
MUTED_BY_INVALID_PLAYBACK_RATE = 0x02,
MUTED_BY_AUDIO_CHANNEL = 0x04,
MUTED_BY_AUDIO_TRACK = 0x08
};
uint32_t mMuted;
UniquePtr<const MetadataTags> mTags;
// URI of the resource we're attempting to load. This stores the value we
@ -1551,15 +1561,6 @@ protected:
// 'Pause' method, or playback not yet having started.
WakeLockBoolWrapper mPaused;
enum MutedReasons {
MUTED_BY_CONTENT = 0x01,
MUTED_BY_INVALID_PLAYBACK_RATE = 0x02,
MUTED_BY_AUDIO_CHANNEL = 0x04,
MUTED_BY_AUDIO_TRACK = 0x08
};
uint32_t mMuted;
// True if the media statistics are currently being shown by the builtin
// video controls
bool mStatsShowing;
@ -1780,9 +1781,6 @@ private:
// be seeked even before the media is loaded.
double mDefaultPlaybackStartPosition;
// True if the audio track is not silent.
bool mIsAudioTrackAudible;
// True if media element has been marked as 'tainted' and can't
// participate in video decoder suspending.
bool mHasSuspendTaint;

View File

@ -401,9 +401,7 @@ class MediaRecorder::Session: public nsIObserver,
// safe to delete this Session.
// Also avoid to run if this session already call stop before
if (!mSession->mStopIssued) {
ErrorResult result;
mSession->mStopIssued = true;
recorder->Stop(result);
recorder->StopForSessionDestruction();
if (NS_FAILED(NS_DispatchToMainThread(new DestroyRunnable(mSession.forget())))) {
MOZ_ASSERT(false, "NS_DispatchToMainThread failed");
}
@ -818,10 +816,12 @@ private:
{
MOZ_ASSERT(NS_IsMainThread());
CleanupStreams();
NS_DispatchToMainThread(
new DispatchStartEventRunnable(this, NS_LITERAL_STRING("start")));
if (NS_FAILED(rv)) {
mRecorder->ForceInactive();
NS_DispatchToMainThread(
NewRunnableMethod<nsresult>("dom::MediaRecorder::NotifyError",
mRecorder,
@ -1475,6 +1475,25 @@ MediaRecorder::GetSourceMediaStream()
return mPipeStream ? mPipeStream.get() : mAudioNode->GetStream();
}
void
MediaRecorder::ForceInactive()
{
LOG(LogLevel::Debug, ("MediaRecorder.ForceInactive %p", this));
mState = RecordingState::Inactive;
}
void
MediaRecorder::StopForSessionDestruction()
{
LOG(LogLevel::Debug, ("MediaRecorder.StopForSessionDestruction %p", this));
MediaRecorderReporter::RemoveMediaRecorder(this);
// We do not perform a mState != RecordingState::Recording) check here as
// we may already be inactive due to ForceInactive().
mState = RecordingState::Inactive;
MOZ_ASSERT(mSessions.Length() > 0);
mSessions.LastElement()->Stop();
}
void
MediaRecorder::InitializeDomExceptions()
{

View File

@ -140,6 +140,13 @@ protected:
// available at the time the error event is fired. Note, depending on when
// this is called there may not be a JS stack to capture.
void InitializeDomExceptions();
// Set the recorder state to inactive. This is needed to handle error states
// in the recorder where state must transition to inactive before full
// stoppage can be reached.
void ForceInactive();
// Stop the recorder and its internal session. This should be used by
// sessions that are in the process of being destroyed.
void StopForSessionDestruction();
// DOM wrapper for source media stream. Will be null when input is audio node.
RefPtr<DOMMediaStream> mDOMStream;
// Source audio node. Will be null when input is a media stream.

View File

@ -67,7 +67,7 @@ RemoteVideoDecoder::Init()
mConversion = mActor->NeedsConversion();
return InitPromise::CreateAndResolve(aTrack, __func__);
},
[self, this](const MediaResult& aError) {
[self](const MediaResult& aError) {
return InitPromise::CreateAndReject(aError, __func__);
});
}

View File

@ -8,6 +8,7 @@
#ifdef XP_WIN
#include "WMFDecoderModule.h"
#include "mozilla/WindowsVersion.h"
#endif
#ifdef MOZ_FFVPX
#include "FFVPXRuntimeLinker.h"
@ -341,7 +342,7 @@ PDMFactory::CreatePDMs()
}
#ifdef XP_WIN
if (MediaPrefs::PDMWMFEnabled()) {
if (MediaPrefs::PDMWMFEnabled() && !IsWin7AndPre2000Compatible()) {
m = new WMFDecoderModule();
RefPtr<PlatformDecoderModule> remote = new dom::RemoteDecoderModule(m);
StartupPDM(remote);

View File

@ -19,6 +19,7 @@
#include <initguid.h>
#include <stdint.h>
#include "mozilla/mscom/EnsureMTA.h"
#include "mozilla/WindowsVersion.h"
#ifdef WMF_MUST_DEFINE_AAC_MFT_CLSID
// Some SDK versions don't define the AAC decoder CLSID.
@ -228,6 +229,16 @@ LoadDLLs()
HRESULT
MFStartup()
{
if (IsWin7AndPre2000Compatible()) {
/*
* Specific exclude the usage of WMF on Win 7 with compatibility mode
* prior to Win 2000 as we may crash while trying to startup WMF.
* Using GetVersionEx API which takes compatibility mode into account.
* See Bug 1279171.
*/
return E_FAIL;
}
HRESULT hr = LoadDLLs();
if (FAILED(hr)) {
return hr;

File diff suppressed because one or more lines are too long

View File

@ -1,21 +1,21 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<!--This testing should match the 15th frame of street.mp4. The
15th frame's time is 0.466666, so seek to a time which is a little
greater than 0.466666, the display frame should be the 15th frame.
<!--This testing should match the 55th frame of gizmo.mp4. The
55th frame's time is 1.8s, so seek to a time which is a little
greater than 1.8s, the display frame should be the 55th frame.
-->
<head>
<script type="text/javascript">
function doTest() {
var video = document.getElementById("v1");
video.src = "../street.mp4";
video.src = "../gizmo.mp4";
video.preload = "metadata";
video.currentTime = 0.466667;
video.currentTime = 1.801;
video.addEventListener("seeked", function() {
// Since the our media pipeline send the frame to imageBridge, then fire
// seeked event, the target frame may not be showns on the screen.
// seeked event, the target frame may not be shown on the screen.
// So using canvas to access the target frame in the imageContainer in
// videoElement.
var canvas = document.getElementById("canvas");

View File

@ -1,4 +1,4 @@
skip-if(Android) fuzzy-if(OSX,22,49977) skip-if(winWidget) fuzzy-if(gtkWidget&&layersGPUAccelerated,70,600) HTTP(..) == short.mp4.firstframe.html short.mp4.firstframe-ref.html
skip-if(Android) fuzzy-if(OSX,23,51392) fuzzy-if(winWidget,59,76797) fuzzy-if(gtkWidget&&layersGPUAccelerated,60,1800) HTTP(..) == short.mp4.lastframe.html short.mp4.lastframe-ref.html
skip-if(Android) skip-if(winWidget) fuzzy-if(gtkWidget&&layersGPUAccelerated,55,4281) fuzzy-if(OSX,3,111852) HTTP(..) == bipbop_300_215kbps.mp4.lastframe.html bipbop_300_215kbps.mp4.lastframe-ref.html
skip-if(Android) fuzzy-if(winWidget,65,307093) HTTP(..) == street.mp4.seek.html street.mp4.15thframe-ref.html
skip-if(Android) fuzzy-if(OSX,25,175921) fuzzy-if(winWidget,64,179198) HTTP(..) == gizmo.mp4.seek.html gizmo.mp4.55thframe-ref.html

File diff suppressed because one or more lines are too long

View File

@ -27,8 +27,7 @@ function cloneLoaded(event) {
manager.finished(e.token);
}
function tryClone(event) {
var e = event.target;
function tryClone(e) {
var clone = e.cloneNode(false);
clone.token = `${e.token}(cloned)`;
manager.started(clone.token);
@ -70,15 +69,26 @@ function tryClone(event) {
// does a network fetch it will get a resource with the wrong length and we get a test
// failure.
function initTest(test, token) {
async function initTest(test, token) {
var e = document.createElement("video");
e.preload = "auto";
e.src = test.name;
e._expectedDuration = test.duration;
ok(true, `Trying to load ${test.name}, duration=${test.duration}`);
e.addEventListener("loadeddata", tryClone, {once: true});
e.token = token;
manager.started(token);
// Since 320x240.ogv is less than 32KB, we need to wait for the
// 'suspend' event to ensure the partial block is flushed to the cache
// otherwise the cloned resource will create a new channel when it
// has no data to read at position 0. The new channel will download
// a different file than the original resource and fail the duration
// test.
let p1 = once(e, "loadeddata");
let p2 = once(e, "suspend");
await p1;
await p2;
tryClone(e);
}
SimpleTest.waitForExplicitFinish();

View File

@ -26,7 +26,7 @@ function startTest() {
'Events fired from onerror should include an error with a stack trace indicating ' +
'an error in this test');
is(mediaRecorder.mimeType, '', 'mimetype should be empty');
is(mediaRecorder.state, 'recording', 'state is recording');
is(mediaRecorder.state, 'inactive', 'state is inactive');
info('onerror callback fired');
callbackStep = 1;
};

View File

@ -45,6 +45,8 @@ SpecialPowers.pushPrefEnv({"set": [["media.ogg.enabled", false]]},
'Mime type in ondataavailable = ' + expectedMimeType);
};
mediaRecorder.onerror = function(evt) {
is(evt.target.state, 'inactive',
'Media recorder is inactive after firing error');
ok(evt instanceof MediaRecorderErrorEvent,
'Events fired from onerror should be MediaRecorderErrorEvent');
is(evt.type, 'error',
@ -56,13 +58,17 @@ SpecialPowers.pushPrefEnv({"set": [["media.ogg.enabled", false]]},
ok(evt.error.stack.includes('test_mediarecorder_getencodeddata.html'),
'Events fired from onerror should include an error with a stack trace indicating ' +
'an error in this test');
try {
mediaRecorder.requestData();
ok(false, 'requestdata should fire an exception if called on an inactive recorder');
} catch (e) {
ok(e instanceof DOMException, 'requestdata should fire an exception ' +
'if called on an inactive recorder');
is(e.name, 'InvalidStateError', 'Exception name should be InvalidStateError');
}
onErrorFired = true;
};
mediaRecorder.start(0);
is(mediaRecorder.state, 'recording', 'Media recorder should be recording');
is(mediaRecorder.stream, stream,
'Media recorder stream = element stream at the start of recording');
mediaRecorder.requestData();
}, 100);
}
);

View File

@ -24,9 +24,8 @@ function startTest() {
// Expected callback sequence should be:
// 1. onerror (from start)
// 2. onerror (from pause)
// 3. ondataavailable
// 4. onstop
// 2. ondataavailable
// 3. onstop
var callbackStep = 0;
var mediaRecorder = new MediaRecorder(stream);
@ -37,19 +36,22 @@ function startTest() {
if (callbackStep == 1) {
try {
mediaRecorder.pause();
ok(false, 'pause should fire an exception if called on an inactive recorder');
} catch(e) {
ok(false, 'Should not get exception in pause call.');
ok(e instanceof DOMException, 'pause should fire an exception ' +
'if called on an inactive recorder');
is(e.name, 'InvalidStateError', 'Exception name should be InvalidStateError');
}
}
ok(callbackStep < 3, 'onerror callback fired as expected.');
ok(callbackStep == 1, 'onerror callback should handle be the 1st event fired');
is(e.error.name, 'UnknownError', 'Error name should be UnknownError.');
ok(e.error.stack.includes('test_mediarecorder_unsupported_src.html'),
'Events fired from onerror should include an error with a stack trace indicating ' +
'an error in this test');
is(mediaRecorder.mimeType, '', 'mimetype should be empty');
is(mediaRecorder.state, 'recording', 'state is recording');
is(mediaRecorder.state, 'inactive', 'state is inactive');
info('onerror callback fired');
}
};
mediaRecorder.onwarning = function () {
ok(false, 'Unexpected onwarning callback fired.');
@ -58,7 +60,7 @@ function startTest() {
mediaRecorder.ondataavailable = function (evt) {
callbackStep++;
info('ondataavailable callback fired');
is(callbackStep, 3, 'should fired ondataavailable callback');
is(callbackStep, 2, 'ondataavailable callback should handle the 2nd event fired');
is(evt.data.size, 0, 'data size should be zero');
ok(evt instanceof BlobEvent,
'Events fired from ondataavailable should be BlobEvent');
@ -69,7 +71,7 @@ function startTest() {
callbackStep++;
info('onstop callback fired');
is(mediaRecorder.state, 'inactive', 'state should be inactive');
is(callbackStep, 4, 'should fired onstop callback');
is(callbackStep, 3, 'onstop callback should handle the 3rd event fired');
SimpleTest.finish();
};

View File

@ -36,27 +36,6 @@ using namespace workers;
namespace {
// Helper classes
class MOZ_STACK_CLASS PerformanceEntryComparator final
{
public:
bool Equals(const PerformanceEntry* aElem1,
const PerformanceEntry* aElem2) const
{
MOZ_ASSERT(aElem1 && aElem2,
"Trying to compare null performance entries");
return aElem1->StartTime() == aElem2->StartTime();
}
bool LessThan(const PerformanceEntry* aElem1,
const PerformanceEntry* aElem2) const
{
MOZ_ASSERT(aElem1 && aElem2,
"Trying to compare null performance entries");
return aElem1->StartTime() < aElem2->StartTime();
}
};
class PrefEnabledRunnable final
: public WorkerCheckAPIExposureOnMainThreadRunnable
{

View File

@ -94,6 +94,27 @@ protected:
nsString mEntryType;
};
// Helper classes
class MOZ_STACK_CLASS PerformanceEntryComparator final
{
public:
bool Equals(const PerformanceEntry* aElem1,
const PerformanceEntry* aElem2) const
{
MOZ_ASSERT(aElem1 && aElem2,
"Trying to compare null performance entries");
return aElem1->StartTime() == aElem2->StartTime();
}
bool LessThan(const PerformanceEntry* aElem1,
const PerformanceEntry* aElem2) const
{
MOZ_ASSERT(aElem1 && aElem2,
"Trying to compare null performance entries");
return aElem1->StartTime() < aElem2->StartTime();
}
};
} // namespace dom
} // namespace mozilla

View File

@ -113,12 +113,13 @@ PerformanceObserver::Notify()
RefPtr<PerformanceObserverEntryList> list =
new PerformanceObserverEntryList(this, mQueuedEntries);
mQueuedEntries.Clear();
ErrorResult rv;
mCallback->Call(this, *list, *this, rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
}
mQueuedEntries.Clear();
}
void
@ -169,6 +170,17 @@ PerformanceObserver::Observe(const PerformanceObserverInit& aOptions,
mEntryTypes.SwapElements(validEntryTypes);
mPerformance->AddObserver(this);
if (aOptions.mBuffered) {
for (auto entryType : mEntryTypes) {
nsTArray<RefPtr<PerformanceEntry>> existingEntries;
mPerformance->GetEntriesByType(entryType, existingEntries);
if (!existingEntries.IsEmpty()) {
mQueuedEntries.AppendElements(existingEntries);
}
}
}
mConnected = true;
}

View File

@ -66,6 +66,7 @@ PerformanceObserverEntryList::GetEntries(
aRetval.AppendElement(entry);
}
aRetval.Sort(PerformanceEntryComparator());
}
void
@ -79,6 +80,7 @@ PerformanceObserverEntryList::GetEntriesByType(
aRetval.AppendElement(entry);
}
}
aRetval.Sort(PerformanceEntryComparator());
}
void
@ -88,9 +90,18 @@ PerformanceObserverEntryList::GetEntriesByName(
nsTArray<RefPtr<PerformanceEntry>>& aRetval)
{
aRetval.Clear();
const bool typePassed = aEntryType.WasPassed();
for (const RefPtr<PerformanceEntry>& entry : mEntries) {
if (entry->GetName().Equals(aName)) {
aRetval.AppendElement(entry);
if (!entry->GetName().Equals(aName)) {
continue;
}
if (typePassed &&
!entry->GetEntryType().Equals(aEntryType.Value())) {
continue;
}
aRetval.AppendElement(entry);
}
aRetval.Sort(PerformanceEntryComparator());
}

View File

@ -60,27 +60,13 @@ var _fromByTestLists =
midComp: "35px",
toComp: "40px"}),
],
lengthNoUnitsSVG: [
new AnimTestcaseFromBy("0", "50", { fromComp: "0",
midComp: "25",
toComp: "50"}),
new AnimTestcaseFromBy("30", "10", { fromComp: "30",
midComp: "35",
toComp: "40"}),
],
lengthPx: [
new AnimTestcaseFromBy("0px", "8px", { fromComp: "0px",
midComp: "4px",
toComp: "8px"}),
new AnimTestcaseFromBy("1px", "10px", { midComp: "6px", toComp: "11px"}),
],
lengthPxSVG: [
new AnimTestcaseFromBy("0px", "8px", { fromComp: "0",
midComp: "4",
toComp: "8"}),
new AnimTestcaseFromBy("1px", "10px", { fromComp: "1",
midComp: "6",
toComp: "11"}),
new AnimTestcaseFromBy("1px", "10px", { fromComp: "1px",
midComp: "6px",
toComp: "11px"}),
],
opacity: [
new AnimTestcaseFromBy("1", "-1", { midComp: "0.5", toComp: "0"}),
@ -176,6 +162,6 @@ var gFromByBundles =
new AnimTestcaseFromBy("1", "2, 3"),
]),
new TestcaseBundle(gPropList.stroke_width,
[].concat(_fromByTestLists.lengthNoUnitsSVG,
_fromByTestLists.lengthPxSVG))
[].concat(_fromByTestLists.lengthNoUnits,
_fromByTestLists.lengthPx))
];

View File

@ -88,38 +88,19 @@ var _fromToTestLists = {
midComp: "55px",
toComp: "80px"}),
],
lengthNoUnitsSVG: [
new AnimTestcaseFromTo("0", "20", { fromComp: "0",
midComp: "10",
toComp: "20"}),
new AnimTestcaseFromTo("50", "0", { fromComp: "50",
midComp: "25",
toComp: "0"}),
new AnimTestcaseFromTo("30", "80", { fromComp: "30",
midComp: "55",
toComp: "80"}),
],
lengthPx: [
new AnimTestcaseFromTo("0px", "12px", { fromComp: "0px",
midComp: "6px"}),
new AnimTestcaseFromTo("16px", "0px", { midComp: "8px",
toComp: "0px"}),
new AnimTestcaseFromTo("10px", "20px", { midComp: "15px"}),
new AnimTestcaseFromTo("41px", "1px", { midComp: "21px"}),
],
lengthPxSVG: [
new AnimTestcaseFromTo("0px", "12px", { fromComp: "0",
midComp: "6",
toComp: "12"}),
new AnimTestcaseFromTo("16px", "0px", { fromComp: "16",
midComp: "8",
toComp: "0"}),
new AnimTestcaseFromTo("10px", "20px", { fromComp: "10",
midComp: "15",
toComp: "20"}),
new AnimTestcaseFromTo("41px", "1px", { fromComp: "41",
midComp: "21",
toComp: "1"}),
midComp: "6px",
toComp: "12px"}),
new AnimTestcaseFromTo("16px", "0px", { fromComp: "16px",
midComp: "8px",
toComp: "0px"}),
new AnimTestcaseFromTo("10px", "20px", { fromComp: "10px",
midComp: "15px",
toComp: "20px"}),
new AnimTestcaseFromTo("41px", "1px", { fromComp: "41px",
midComp: "21px",
toComp: "1px"}),
],
lengthPctSVG: [
new AnimTestcaseFromTo("20.5%", "0.5%", { midComp: "10.5%" }),
@ -130,12 +111,12 @@ var _fromToTestLists = {
"px and percent values"),
],
lengthPxNoUnitsSVG: [
new AnimTestcaseFromTo("10", "20px", { fromComp: "10",
midComp: "15",
toComp: "20"}),
new AnimTestcaseFromTo("10px", "20", { fromComp: "10",
midComp: "15",
toComp: "20"}),
new AnimTestcaseFromTo("10", "20px", { fromComp: "10px",
midComp: "15px",
toComp: "20px"}),
new AnimTestcaseFromTo("10px", "20", { fromComp: "10px",
midComp: "15px",
toComp: "20px"}),
],
opacity: [
new AnimTestcaseFromTo("1", "0", { midComp: "0.5" }),
@ -410,8 +391,8 @@ var gFromToBundles = [
midComp: "1, 3, 3, 5, 5, 2, 2, 4, 4, 6"}),
])),
new TestcaseBundle(gPropList.stroke_dashoffset,
[].concat(_fromToTestLists.lengthNoUnitsSVG,
_fromToTestLists.lengthPxSVG,
[].concat(_fromToTestLists.lengthNoUnits,
_fromToTestLists.lengthPx,
_fromToTestLists.lengthPxPctSVG,
_fromToTestLists.lengthPctSVG,
_fromToTestLists.lengthPxNoUnitsSVG)),
@ -429,13 +410,13 @@ var gFromToBundles = [
]),
new TestcaseBundle(gPropList.stroke_opacity, _fromToTestLists.opacity),
new TestcaseBundle(gPropList.stroke_width,
[].concat(_fromToTestLists.lengthNoUnitsSVG,
_fromToTestLists.lengthPxSVG,
[].concat(_fromToTestLists.lengthNoUnits,
_fromToTestLists.lengthPx,
_fromToTestLists.lengthPxPctSVG,
_fromToTestLists.lengthPctSVG,
_fromToTestLists.lengthPxNoUnitsSVG, [
new AnimTestcaseFromTo("inherit", "7px",
{ fromComp: "1", midComp: "4", toComp: "7" }),
{ fromComp: "1px", midComp: "4px", toComp: "7px" }),
])),
new TestcaseBundle(gPropList.text_anchor, [
new AnimTestcaseFromTo("start", "middle"),

View File

@ -87,22 +87,6 @@ var _pacedTestLists =
comp1: "8px"
}),
],
lengthNoUnitsSVG : [
new AnimTestcasePaced("2; 0; 4",
{ comp0: "2",
comp1_6: "1",
comp1_3: "0",
comp2_3: "2",
comp1: "4"
}),
new AnimTestcasePaced("10; 12; 8",
{ comp0: "10",
comp1_6: "11",
comp1_3: "12",
comp2_3: "10",
comp1: "8"
}),
],
lengthPx : [
new AnimTestcasePaced("0px; 2px; 6px",
{ comp0: "0px",
@ -119,22 +103,6 @@ var _pacedTestLists =
comp1: "8px"
}),
],
lengthPxSVG : [
new AnimTestcasePaced("0px; 2px; 6px",
{ comp0: "0",
comp1_6: "1",
comp1_3: "2",
comp2_3: "4",
comp1: "6"
}),
new AnimTestcasePaced("10px; 12px; 8px",
{ comp0: "10",
comp1_6: "11",
comp1_3: "12",
comp2_3: "10",
comp1: "8"
}),
],
lengthPctSVG : [
new AnimTestcasePaced("5%; 6%; 4%",
{ comp0: "5%",
@ -308,13 +276,13 @@ var gPacedBundles =
}),
])),
new TestcaseBundle(gPropList.stroke_dashoffset,
[].concat(_pacedTestLists.lengthNoUnitsSVG,
_pacedTestLists.lengthPxSVG,
[].concat(_pacedTestLists.lengthNoUnits,
_pacedTestLists.lengthPx,
_pacedTestLists.lengthPctSVG,
_pacedTestLists.lengthPxPctSVG)),
new TestcaseBundle(gPropList.stroke_width,
[].concat(_pacedTestLists.lengthNoUnitsSVG,
_pacedTestLists.lengthPxSVG,
[].concat(_pacedTestLists.lengthNoUnits,
_pacedTestLists.lengthPx,
_pacedTestLists.lengthPctSVG,
_pacedTestLists.lengthPxPctSVG)),
// XXXdholbert TODO: test 'stroke-dasharray' once we support animating it

View File

@ -19,16 +19,13 @@ support-files =
[test_smilBackwardsSeeking.xhtml]
[test_smilCSSFontStretchRelative.xhtml]
[test_smilCSSFromBy.xhtml]
skip-if = stylo
[test_smilCSSFromTo.xhtml]
skip-if = stylo
# [test_smilCSSInherit.xhtml]
# disabled until bug 501183 is fixed
[test_smilCSSInvalidValues.xhtml]
[test_smilCSSPaced.xhtml]
skip-if = stylo
[test_smilChangeAfterFrozen.xhtml]
skip-if = stylo
skip-if = stylo # bug 1358955.
[test_smilConditionalProcessing.html]
[test_smilContainerBinding.xhtml]
[test_smilCrossContainer.xhtml]
@ -44,11 +41,8 @@ skip-if = toolkit == 'android'
[test_smilKeyTimes.xhtml]
[test_smilKeyTimesPacedMode.xhtml]
[test_smilMappedAttrFromBy.xhtml]
skip-if = stylo
[test_smilMappedAttrFromTo.xhtml]
skip-if = stylo
[test_smilMappedAttrPaced.xhtml]
skip-if = stylo
[test_smilMinTiming.html]
[test_smilRepeatDuration.html]
[test_smilRepeatTiming.xhtml]
@ -60,7 +54,6 @@ skip-if = toolkit == 'android' #TIMED_OUT
[test_smilSyncTransform.xhtml]
[test_smilSyncbaseTarget.xhtml]
[test_smilTextZoom.xhtml]
skip-if = stylo
[test_smilTimeEvents.xhtml]
[test_smilTiming.xhtml]
[test_smilTimingZeroIntervals.xhtml]

View File

@ -113,6 +113,9 @@ var SMILUtil =
getMotionFakeAttributeName : function() {
return "_motion";
},
// Return stripped px value from specified value.
stripPx: str => str.replace(/px\s*$/, ''),
};
@ -404,6 +407,19 @@ AnimTestcase.prototype =
for (var i in aSeekList) {
var entry = aSeekList[i];
SMILUtil.getSVGRoot().setCurrentTime(entry[0]);
// Bug 1379908: The computed value of stroke-* properties should be
// serialized with px units, but currently Gecko and Servo don't do that
// when animating these values.
if (['stroke-width',
'stroke-dasharray',
'stroke-dashoffset'].includes(aTargetAttr.attrName)) {
var attr = SMILUtil.stripPx(
SMILUtil.getAttributeValue(aTargetElem, aTargetAttr));
var expectedVal = SMILUtil.stripPx(entry[1]);
is(attr, expectedVal, entry[2]);
return;
}
is(SMILUtil.getAttributeValue(aTargetElem, aTargetAttr),
entry[1], entry[2]);
}

View File

@ -30,6 +30,16 @@ SimpleTest.waitForExplicitFinish();
function verifyStyle(aNode, aPropertyName, aExpectedVal)
{
var computedVal = SMILUtil.getComputedStyleSimple(aNode, aPropertyName);
// Bug 1379908: The computed value of stroke-* properties should be
// serialized with px units, but currently Gecko and Servo don't do that
// when animating these values.
if ('stroke-width' == aPropertyName) {
var expectedVal = SMILUtil.stripPx(aExpectedVal);
var unitlessComputedVal = SMILUtil.stripPx(computedVal);
is(unitlessComputedVal, expectedVal, "computed value of " + aPropertyName);
return;
}
is(computedVal, aExpectedVal, "computed value of " + aPropertyName);
}
@ -58,16 +68,16 @@ function main()
verifyStyle(rect, "stroke-width", "5px");
svg.setCurrentTime(1);
verifyStyle(text, "font-size", "20px");
verifyStyle(rect, "stroke-width", "20");
verifyStyle(rect, "stroke-width", "20px");
svg.setCurrentTime(1.5);
verifyStyle(text, "font-size", "30px");
verifyStyle(rect, "stroke-width", "30");
verifyStyle(rect, "stroke-width", "30px");
svg.setCurrentTime(2);
verifyStyle(text, "font-size", "40px");
verifyStyle(rect, "stroke-width", "40");
verifyStyle(rect, "stroke-width", "40px");
svg.setCurrentTime(3);
verifyStyle(text, "font-size", "40px");
verifyStyle(rect, "stroke-width", "40");
verifyStyle(rect, "stroke-width", "40px");
} catch (e) {
// If anything goes wrong, make sure we restore textZoom before bubbling
// the exception upwards, so that we don't mess up subsequent tests.

View File

@ -9,6 +9,7 @@
dictionary PerformanceObserverInit {
required sequence<DOMString> entryTypes;
boolean buffered = false;
};
callback PerformanceObserverCallback = void (PerformanceObserverEntryList entries, PerformanceObserver observer);

View File

@ -50,6 +50,7 @@
#include "nsThreadUtils.h"
#include "mozilla/dom/NodeListBinding.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/ServoStyleSet.h"
#include "mozilla/Unused.h"
using namespace mozilla;
@ -709,77 +710,9 @@ nsBindingManager::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc,
return NS_OK;
}
typedef nsTHashtable<nsPtrHashKey<nsIStyleRuleProcessor> > RuleProcessorSet;
static RuleProcessorSet*
GetContentSetRuleProcessors(nsTHashtable<nsRefPtrHashKey<nsIContent>>* aContentSet)
{
RuleProcessorSet* set = nullptr;
for (auto iter = aContentSet->Iter(); !iter.Done(); iter.Next()) {
nsIContent* boundContent = iter.Get()->GetKey();
for (nsXBLBinding* binding = boundContent->GetXBLBinding(); binding;
binding = binding->GetBaseBinding()) {
nsIStyleRuleProcessor* ruleProc =
binding->PrototypeBinding()->GetRuleProcessor();
if (ruleProc) {
if (!set) {
set = new RuleProcessorSet;
}
set->PutEntry(ruleProc);
}
}
}
return set;
}
void
nsBindingManager::WalkAllRules(nsIStyleRuleProcessor::EnumFunc aFunc,
ElementDependentRuleProcessorData* aData)
{
if (!mBoundContentSet) {
return;
}
nsAutoPtr<RuleProcessorSet> set;
set = GetContentSetRuleProcessors(mBoundContentSet);
if (!set) {
return;
}
for (auto iter = set->Iter(); !iter.Done(); iter.Next()) {
nsIStyleRuleProcessor* ruleProcessor = iter.Get()->GetKey();
(*(aFunc))(ruleProcessor, aData);
}
}
nsresult
nsBindingManager::MediumFeaturesChanged(nsPresContext* aPresContext,
bool* aRulesChanged)
{
*aRulesChanged = false;
if (!mBoundContentSet) {
return NS_OK;
}
nsAutoPtr<RuleProcessorSet> set;
set = GetContentSetRuleProcessors(mBoundContentSet);
if (!set) {
return NS_OK;
}
for (auto iter = set->Iter(); !iter.Done(); iter.Next()) {
nsIStyleRuleProcessor* ruleProcessor = iter.Get()->GetKey();
bool thisChanged = ruleProcessor->MediumFeaturesChanged(aPresContext);
*aRulesChanged = *aRulesChanged || thisChanged;
}
return NS_OK;
}
void
nsBindingManager::AppendAllSheets(nsTArray<StyleSheet*>& aArray)
nsBindingManager::EnumerateBoundContentBindings(
const BoundContentBindingCallback& aCallback) const
{
if (!mBoundContentSet) {
return;
@ -787,13 +720,93 @@ nsBindingManager::AppendAllSheets(nsTArray<StyleSheet*>& aArray)
for (auto iter = mBoundContentSet->Iter(); !iter.Done(); iter.Next()) {
nsIContent* boundContent = iter.Get()->GetKey();
for (nsXBLBinding* binding = boundContent->GetXBLBinding(); binding;
for (nsXBLBinding* binding = boundContent->GetXBLBinding();
binding;
binding = binding->GetBaseBinding()) {
binding->PrototypeBinding()->AppendStyleSheetsTo(aArray);
aCallback(binding);
}
}
}
void
nsBindingManager::WalkAllRules(nsIStyleRuleProcessor::EnumFunc aFunc,
ElementDependentRuleProcessorData* aData)
{
EnumerateBoundContentBindings([=](nsXBLBinding* aBinding) {
nsIStyleRuleProcessor* ruleProcessor =
aBinding->PrototypeBinding()->GetRuleProcessor();
if (ruleProcessor) {
(*(aFunc))(ruleProcessor, aData);
}
});
}
bool
nsBindingManager::MediumFeaturesChanged(nsPresContext* aPresContext)
{
bool rulesChanged = false;
RefPtr<nsPresContext> presContext = aPresContext;
bool isStyledByServo = mDocument->IsStyledByServo();
EnumerateBoundContentBindings([=, &rulesChanged](nsXBLBinding* aBinding) {
if (isStyledByServo) {
ServoStyleSet* styleSet = aBinding->PrototypeBinding()->GetServoStyleSet();
if (styleSet) {
bool styleSetChanged = false;
if (styleSet->IsPresContextChanged(presContext)) {
styleSetChanged = styleSet->SetPresContext(presContext);
} else {
// PresContext is not changed. This means aPresContext is still
// alive since the last time it initialized this XBL styleset.
// It's safe to check whether medium features changed.
bool viewportUnitsUsed = false;
styleSetChanged =
styleSet->MediumFeaturesChangedRules(&viewportUnitsUsed);
MOZ_ASSERT(!viewportUnitsUsed,
"Non-master stylesets shouldn't get flagged as using "
"viewport units!");
}
rulesChanged = rulesChanged || styleSetChanged;
}
} else {
nsIStyleRuleProcessor* ruleProcessor =
aBinding->PrototypeBinding()->GetRuleProcessor();
if (ruleProcessor) {
bool thisChanged = ruleProcessor->MediumFeaturesChanged(presContext);
rulesChanged = rulesChanged || thisChanged;
}
}
});
return rulesChanged;
}
void
nsBindingManager::UpdateBoundContentBindingsForServo(nsPresContext* aPresContext)
{
MOZ_ASSERT(mDocument->IsStyledByServo(),
"This should be called only by servo-backend!");
RefPtr<nsPresContext> presContext = aPresContext;
EnumerateBoundContentBindings([=](nsXBLBinding* aBinding) {
nsXBLPrototypeBinding* protoBinding = aBinding->PrototypeBinding();
ServoStyleSet* styleSet = protoBinding->GetServoStyleSet();
if (styleSet && styleSet->StyleSheetsHaveChanged()) {
protoBinding->ComputeServoStyleSet(presContext);
}
});
}
void
nsBindingManager::AppendAllSheets(nsTArray<StyleSheet*>& aArray)
{
EnumerateBoundContentBindings([&aArray](nsXBLBinding* aBinding) {
aBinding->PrototypeBinding()->AppendStyleSheetsTo(aArray);
});
}
static void
InsertAppendedContent(XBLChildrenElement* aPoint,
nsIContent* aFirstNewContent)

View File

@ -130,13 +130,16 @@ public:
void WalkAllRules(nsIStyleRuleProcessor::EnumFunc aFunc,
ElementDependentRuleProcessorData* aData);
/**
* Do any processing that needs to happen as a result of a change in
* the characteristics of the medium, and return whether this rule
* processor's rules have changed (e.g., because of media queries).
*/
nsresult MediumFeaturesChanged(nsPresContext* aPresContext,
bool* aRulesChanged);
// Do any processing that needs to happen as a result of a change in the
// characteristics of the medium, and return whether this rule processor's
// rules or the servo style set have changed (e.g., because of media
// queries).
bool MediumFeaturesChanged(nsPresContext* aPresContext);
// Update the content bindings in mBoundContentSet due to medium features
// changed.
void UpdateBoundContentBindingsForServo(nsPresContext* aPresContext);
void AppendAllSheets(nsTArray<mozilla::StyleSheet*>& aArray);
@ -193,6 +196,12 @@ protected:
// Call PostProcessAttachedQueueEvent() on a timer.
static void PostPAQEventCallback(nsITimer* aTimer, void* aClosure);
// Enumerate each bound content's bindings (including its base bindings)
// in mBoundContentSet.
using BoundContentBindingCallback = std::function<void (nsXBLBinding*)>;
void EnumerateBoundContentBindings(
const BoundContentBindingCallback& aCallback) const;
// MEMBER VARIABLES
protected:
// A set of nsIContent that currently have a binding installed.

View File

@ -569,6 +569,14 @@ nsXBLPrototypeBinding::GetRuleProcessor()
return nullptr;
}
void
nsXBLPrototypeBinding::ComputeServoStyleSet(nsPresContext* aPresContext)
{
if (mResources) {
mResources->ComputeServoStyleSet(aPresContext);
}
}
ServoStyleSet*
nsXBLPrototypeBinding::GetServoStyleSet() const
{

View File

@ -131,6 +131,7 @@ public:
void AppendStyleSheetsTo(nsTArray<mozilla::StyleSheet*>& aResult) const;
nsIStyleRuleProcessor* GetRuleProcessor();
void ComputeServoStyleSet(nsPresContext* aPresContext);
mozilla::ServoStyleSet* GetServoStyleSet() const;
nsresult FlushSkinSheets();

View File

@ -167,23 +167,14 @@ nsXBLPrototypeResources::GatherRuleProcessor()
void
nsXBLPrototypeResources::ComputeServoStyleSet(nsPresContext* aPresContext)
{
mServoStyleSet.reset(new ServoStyleSet(ServoStyleSet::Kind::ForXBL));
mServoStyleSet->Init(aPresContext, nullptr);
nsTArray<RefPtr<ServoStyleSheet>> sheets(mStyleSheetList.Length());
for (StyleSheet* sheet : mStyleSheetList) {
MOZ_ASSERT(sheet->IsServo(),
"This should only be called with Servo-flavored style backend!");
// The XBL style sheets aren't document level sheets, but we need to
// decide a particular SheetType to add them to style set. This type
// doesn't affect the place where we pull those rules from
// stylist::push_applicable_declarations_as_xbl_only_stylist().
mServoStyleSet->AppendStyleSheet(SheetType::Doc, sheet->AsServo());
sheets.AppendElement(sheet->AsServo());
}
mServoStyleSet->UpdateStylistIfNeeded();
// The PresContext of the bound document could be destroyed anytime later,
// which shouldn't be used for XBL styleset, so we clear it here to avoid
// dangling pointer.
mServoStyleSet->ClearPresContext();
mServoStyleSet = ServoStyleSet::CreateXBLServoStyleSet(aPresContext, sheets);
}
void

View File

@ -59,7 +59,7 @@ random-if(Android) needs-focus != spellcheck-textarea-property-dynamic-override.
random-if(Android) needs-focus != spellcheck-textarea-property-dynamic-override-inherit.html spellcheck-textarea-ref.html
needs-focus == caret_on_focus.html caret_on_focus-ref.html
needs-focus != caret_on_textarea_lastline.html caret_on_textarea_lastline-ref.html
needs-focus == input-text-onfocus-reframe.html input-text-onfocus-reframe-ref.html
fuzzy-if(Android,1,1) needs-focus == input-text-onfocus-reframe.html input-text-onfocus-reframe-ref.html
fuzzy-if(skiaContent,3,1) needs-focus == input-text-notheme-onfocus-reframe.html input-text-notheme-onfocus-reframe-ref.html
needs-focus == caret_after_reframe.html caret_after_reframe-ref.html
== nobogusnode-1.html nobogusnode-ref.html

View File

@ -7,10 +7,10 @@
<script src="data:application/javascript,"></script>
<link rel="stylesheet" type="text/css"
charset="UTF-16BE"
href="data:text/css,%00b%00o%00d%00y%00{%00c%00o%00l%00o%00r%00:%00g%00r%00e%00e%00n}">
href="data:text/css,%00b%00o%00d%00y%00{%00c%00o%00l%00o%00r%00:%00g%00r%00e%00e%00n%00}">
</head>
<body>
This should be green
</body>
</html>

View File

@ -1330,7 +1330,7 @@ fuzzy-if(skiaContent,1,5) == 482659-1d.html 482659-1-ref.html
== 483565.xul 483565-ref.xul
== 484256-1.html 484256-1-ref.html
== 484256-2.html 484256-1-ref.html
fails-if(stylo||styloVsGecko) == 485012-1.html 485012-1-ref.html # bug 1396093
== 485012-1.html 485012-1-ref.html
== 485275-1.html 485275-1-ref.html
== 485275-1.svg 485275-1-ref.html
== 486052-1.html 486052-1-ref.html

View File

@ -74,6 +74,11 @@ SERVO_BINDING_FUNC(Servo_StyleSet_RebuildCachedData, void,
// they wrap the value in a struct.
SERVO_BINDING_FUNC(Servo_StyleSet_MediumFeaturesChanged, uint8_t,
RawServoStyleSetBorrowed set, bool* viewport_units_used)
// We'd like to return `OriginFlags` here, but bindgen bitfield enums don't
// work as return values with the Linux 32-bit ABI at the moment because
// they wrap the value in a struct.
SERVO_BINDING_FUNC(Servo_StyleSet_SetDevice, uint8_t,
RawServoStyleSetBorrowed set, RawGeckoPresContextOwned pres_context)
SERVO_BINDING_FUNC(Servo_StyleSet_Drop, void, RawServoStyleSetOwned set)
SERVO_BINDING_FUNC(Servo_StyleSet_CompatModeChanged, void,
RawServoStyleSetBorrowed raw_data)

View File

@ -105,10 +105,37 @@ ServoStyleSet::~ServoStyleSet()
}
}
UniquePtr<ServoStyleSet>
ServoStyleSet::CreateXBLServoStyleSet(
nsPresContext* aPresContext,
const nsTArray<RefPtr<ServoStyleSheet>>& aNewSheets)
{
auto set = MakeUnique<ServoStyleSet>(Kind::ForXBL);
set->Init(aPresContext, nullptr);
// The XBL style sheets aren't document level sheets, but we need to
// decide a particular SheetType to add them to style set. This type
// doesn't affect the place where we pull those rules from
// stylist::push_applicable_declarations_as_xbl_only_stylist().
set->ReplaceSheets(SheetType::Doc, aNewSheets);
// Update stylist immediately.
set->UpdateStylist();
// The PresContext of the bound document could be destroyed anytime later,
// which shouldn't be used for XBL styleset, so we clear it here to avoid
// dangling pointer.
set->mPresContext = nullptr;
return set;
}
void
ServoStyleSet::Init(nsPresContext* aPresContext, nsBindingManager* aBindingManager)
{
mPresContext = aPresContext;
mLastPresContextUsesXBLStyleSet = aPresContext;
mRawSet.reset(Servo_StyleSet_Init(aPresContext));
mBindingManager = aBindingManager;
@ -164,15 +191,37 @@ ServoStyleSet::InvalidateStyleForCSSRuleChanges()
mPresContext->RestyleManager()->AsServo()->PostRestyleEventForCSSRuleChanges();
}
bool
ServoStyleSet::SetPresContext(nsPresContext* aPresContext)
{
MOZ_ASSERT(IsForXBL(), "Only XBL styleset can set PresContext!");
mLastPresContextUsesXBLStyleSet = aPresContext;
const OriginFlags rulesChanged = static_cast<OriginFlags>(
Servo_StyleSet_SetDevice(mRawSet.get(), aPresContext));
if (rulesChanged != OriginFlags(0)) {
MarkOriginsDirty(rulesChanged);
return true;
}
return false;
}
nsRestyleHint
ServoStyleSet::MediumFeaturesChanged(bool aViewportChanged)
{
bool viewportUnitsUsed = false;
const OriginFlags rulesChanged = static_cast<OriginFlags>(
Servo_StyleSet_MediumFeaturesChanged(mRawSet.get(), &viewportUnitsUsed));
bool rulesChanged = MediumFeaturesChangedRules(&viewportUnitsUsed);
if (rulesChanged != OriginFlags(0)) {
MarkOriginsDirty(rulesChanged);
if (mBindingManager &&
mBindingManager->MediumFeaturesChanged(mPresContext)) {
SetStylistXBLStyleSheetsDirty();
rulesChanged = true;
}
if (rulesChanged) {
return eRestyle_Subtree;
}
@ -183,6 +232,22 @@ ServoStyleSet::MediumFeaturesChanged(bool aViewportChanged)
return nsRestyleHint(0);
}
bool
ServoStyleSet::MediumFeaturesChangedRules(bool* aViewportUnitsUsed)
{
MOZ_ASSERT(aViewportUnitsUsed);
const OriginFlags rulesChanged = static_cast<OriginFlags>(
Servo_StyleSet_MediumFeaturesChanged(mRawSet.get(), aViewportUnitsUsed));
if (rulesChanged != OriginFlags(0)) {
MarkOriginsDirty(rulesChanged);
return true;
}
return false;
}
MOZ_DEFINE_MALLOC_SIZE_OF(ServoStyleSetMallocSizeOf)
void
@ -731,13 +796,6 @@ ServoStyleSet::InsertStyleSheetBefore(SheetType aType,
return NS_OK;
}
void
ServoStyleSet::UpdateStyleSheet(ServoStyleSheet* aSheet)
{
MOZ_ASSERT(aSheet);
// TODO(emilio): Get rid of this.
}
int32_t
ServoStyleSet::SheetCount(SheetType aType) const
{
@ -1372,13 +1430,21 @@ ServoStyleSet::UpdateStylist()
{
MOZ_ASSERT(StylistNeedsUpdate());
// There's no need to compute invalidations and such for an XBL styleset,
// since they are loaded and unloaded synchronously, and they don't have to
// deal with dynamic content changes.
Element* root =
IsMaster() ? mPresContext->Document()->GetDocumentElement() : nullptr;
if (mStylistState & StylistState::StyleSheetsDirty) {
// There's no need to compute invalidations and such for an XBL styleset,
// since they are loaded and unloaded synchronously, and they don't have to
// deal with dynamic content changes.
Element* root =
IsMaster() ? mPresContext->Document()->GetDocumentElement() : nullptr;
Servo_StyleSet_FlushStyleSheets(mRawSet.get(), root);
}
if (MOZ_UNLIKELY(mStylistState & StylistState::XBLStyleSheetsDirty)) {
MOZ_ASSERT(IsMaster(), "Only master styleset can mark XBL stylesets dirty!");
mBindingManager->UpdateBoundContentBindingsForServo(mPresContext);
}
Servo_StyleSet_FlushStyleSheets(mRawSet.get(), root);
mStylistState = StylistState::NotDirty;
}

View File

@ -46,18 +46,22 @@ struct TreeMatchContext;
namespace mozilla {
/**
* A few flags used to track which kind of stylist state we may need to
* update.
*/
// A few flags used to track which kind of stylist state we may need to
// update.
enum class StylistState : uint8_t {
/** The stylist is not dirty, we should do nothing */
// The stylist is not dirty, we should do nothing.
NotDirty = 0,
/** The style sheets have changed, so we need to update the style data. */
StyleSheetsDirty,
// The style sheets have changed, so we need to update the style data.
StyleSheetsDirty = 1 << 0,
// Some of the style sheets of the bound elements in binding manager have
// changed, so we need to tell the binding manager to update style data.
XBLStyleSheetsDirty = 1 << 1,
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(StylistState)
// Bitfield type to represent Servo stylesheet origins.
enum class OriginFlags : uint8_t {
UserAgent = 0x01,
@ -114,6 +118,10 @@ public:
explicit ServoStyleSet(Kind aKind);
~ServoStyleSet();
static UniquePtr<ServoStyleSet>
CreateXBLServoStyleSet(nsPresContext* aPresContext,
const nsTArray<RefPtr<ServoStyleSheet>>& aNewSheets);
void Init(nsPresContext* aPresContext, nsBindingManager* aBindingManager);
void BeginShutdown();
void Shutdown();
@ -133,6 +141,9 @@ public:
nsRestyleHint MediumFeaturesChanged(bool aViewportChanged);
// aViewportChanged outputs whether any viewport units is used.
bool MediumFeaturesChangedRules(bool* aViewportUnitsUsed);
void InvalidateStyleForCSSRuleChanges();
void AddSizeOfIncludingThis(nsWindowSizes& aSizes) const;
@ -231,11 +242,6 @@ public:
ServoStyleSheet* aNewSheet,
ServoStyleSheet* aReferenceSheet);
// Notify servo that the underlying raw sheet has changed, through cloning.
// This should only be called on a just-cloned sheet, because it does not
// mark the stylesheets as dirty either here or in servo.
void UpdateStyleSheet(ServoStyleSheet* aSheet);
int32_t SheetCount(SheetType aType) const;
ServoStyleSheet* StyleSheetAt(SheetType aType, int32_t aIndex) const;
@ -428,11 +434,15 @@ public:
// Returns the style rule map.
ServoStyleRuleMap* StyleRuleMap();
// Clear mPresContext. This is needed after XBL ServoStyleSet is created.
void ClearPresContext() {
mPresContext = nullptr;
// Return whether this is the last PresContext which uses this XBL styleset.
bool IsPresContextChanged(nsPresContext* aPresContext) const {
return aPresContext != mLastPresContextUsesXBLStyleSet;
}
// Set PresContext (i.e. Device) for mRawSet. This should be called only
// by XBL stylesets. Returns true if there is any rule changing.
bool SetPresContext(nsPresContext* aPresContext);
/**
* Returns true if a modification to an an attribute with the specified
* local name might require us to restyle the element.
@ -525,7 +535,12 @@ private:
*/
void SetStylistStyleSheetsDirty()
{
mStylistState = StylistState::StyleSheetsDirty;
mStylistState |= StylistState::StyleSheetsDirty;
}
void SetStylistXBLStyleSheetsDirty()
{
mStylistState |= StylistState::XBLStyleSheetsDirty;
}
bool StylistNeedsUpdate() const
@ -565,7 +580,15 @@ private:
ServoStyleSheet* aSheet);
const Kind mKind;
nsPresContext* mPresContext;
// Nullptr if this is an XBL style set.
nsPresContext* MOZ_NON_OWNING_REF mPresContext = nullptr;
// Because XBL style set could be used by multiple PresContext, we need to
// store the last PresContext pointer which uses this XBL styleset for
// computing medium rule changes.
void* MOZ_NON_OWNING_REF mLastPresContextUsesXBLStyleSet = nullptr;
UniquePtr<RawServoStyleSet> mRawSet;
EnumeratedArray<SheetType, SheetType::Count,
nsTArray<RefPtr<ServoStyleSheet>>> mSheets;

View File

@ -2686,8 +2686,8 @@ nsStyleSet::MediumFeaturesChanged(bool aViewportChanged)
}
if (mBindingManager) {
bool thisChanged = false;
mBindingManager->MediumFeaturesChanged(presContext, &thisChanged);
bool thisChanged =
mBindingManager->MediumFeaturesChanged(presContext);
stylesChanged = stylesChanged || thisChanged;
}

View File

@ -94,8 +94,8 @@ void nsStyleUtil::AppendEscapedCSSString(const nsAString& aString,
const char16_t* in = aString.BeginReading();
const char16_t* const end = aString.EndReading();
for (; in != end; in++) {
if (*in < 0x20 || (*in >= 0x7F && *in < 0xA0)) {
// Escape U+0000 through U+001F and U+007F through U+009F numerically.
if (*in < 0x20 || *in == 0x7F) {
// Escape U+0000 through U+001F and U+007F numerically.
aReturn.AppendPrintf("\\%x ", *in);
} else {
if (*in == '"' || *in == '\'' || *in == '\\') {
@ -158,8 +158,8 @@ nsStyleUtil::AppendEscapedCSSIdent(const nsAString& aIdent, nsAString& aReturn)
char16_t ch = *in;
if (ch == 0x00) {
aReturn.Append(char16_t(0xFFFD));
} else if (ch < 0x20 || (0x7F <= ch && ch < 0xA0)) {
// Escape U+0000 through U+001F and U+007F through U+009F numerically.
} else if (ch < 0x20 || 0x7F == ch) {
// Escape U+0000 through U+001F and U+007F numerically.
aReturn.AppendPrintf("\\%x ", *in);
} else {
// Escape ASCII non-identifier printables as a backslash plus

View File

@ -242,7 +242,6 @@ skip-if = !stylo
skip-if = android_version == '18' #debug-only failure; timed out #Android 4.3 aws only; bug 1030419
[test_media_queries_dynamic.html]
[test_media_queries_dynamic_xbl.html]
fail-if = stylo # bug 1382078
[test_media_query_list.html]
[test_media_query_serialization.html]
[test_moz_device_pixel_ratio.html]

View File

@ -75,7 +75,7 @@ is(CSS.escape('-9a'), '-\\39 a', "escapingFailed Char: -9a");
is(CSS.escape('--a'), '--a', 'Should not need to escape leading "--"');
is(CSS.escape('\x80\x2D\x5F\xA9'), '\\80 \x2D\x5F\xA9', "escapingFailed Char: \\x80\\x2D\\x5F\\xA9");
is(CSS.escape('\x7F\x80\x2D\x5F\xA9'), '\\7f \x80\x2D\x5F\xA9', "escapingFailed Char: \\x7F\\x80\\x2D\\x5F\\xA9");
is(CSS.escape('\xA0\xA1\xA2'), '\xA0\xA1\xA2', "escapingFailed Char: \\xA0\\xA1\\xA2");
is(CSS.escape('a0123456789b'), 'a0123456789b', "escapingFailed Char: a0123465789");
is(CSS.escape('abcdefghijklmnopqrstuvwxyz'), 'abcdefghijklmnopqrstuvwxyz', "escapingFailed Char: abcdefghijklmnopqrstuvwxyz");

View File

@ -18,6 +18,8 @@
// That's not the point of the test, though; the point is only that
// *that text* is properly escaped.
const isStylo = SpecialPowers.DOMWindowUtils.isStyledByServo;
// There is one "pattern" for each code path through the error reporter
// that might need to escape some kind of user-supplied text.
// Each "pattern" is tested once with each of the "substitution"s below:
@ -39,13 +41,14 @@ let patterns = [
// _AtKeyword
{ i: "x{@<t>: }", o: "declaration but found \u2018@<i>\u2019." },
// _String
{ i: "x{ '<t>'}" , o: "declaration but found \u2018'<s>'\u2019." },
{ i: "x{ '<t>'}" , o: isStylo ? 'declaration but found \u2018"<s>"\u2019.'
: "declaration but found \u2018'<s>'\u2019." },
// _Bad_String
{ i: "x{ '<t>\n}", o: "declaration but found \u2018'<s>\u2019." },
// FIXME: temporarily disabled https://bugzilla.mozilla.org/show_bug.cgi?id=1396664
{ i: "x{ '<t>\n}", o: isStylo ? "declaration but found \u2018\"<bad string>\n\u2019."
: "declaration but found \u2018'<s>\u2019." },
];
const isStylo = SpecialPowers.DOMWindowUtils.isStyledByServo;
// Stylo's CSS parser only reports the 'url(' token, not the actual bad URL.
if (!isStylo) {
patterns.push(
@ -97,7 +100,7 @@ const substitutions = [
// ASCII printables that must be escaped in identifiers.
// Most of these should not be escaped in strings.
{ t: "\\!\\\"\\#\\$", i: "\\!\\\"\\#\\$", s: "!\\\"#$" },
{ t: "\\%\\&\\'\\(", i: "\\%\\&\\'\\(", s: "%&\\'(" },
{ t: "\\%\\&\\'\\(", i: "\\%\\&\\'\\(", s: isStylo ? "%&'(" : "%&\\'(" },
{ t: "\\)\\*\\+\\,", i: "\\)\\*\\+\\,", s: ")*+," },
{ t: "\\.\\/\\:\\;", i: "\\.\\/\\:\\;", s: "./:;" },
{ t: "\\<\\=\\>\\?", i: "\\<\\=\\>\\?", s: "<=>?", },
@ -127,26 +130,26 @@ const substitutions = [
s: "\\1c \\1d \\1e \\1f " },
// U+007F (DELETE) and U+0080 - U+009F (C1 controls)
{ t: "\\\x7f\\\x80\\\x81\\\x82", i: "\\7f \\80 \\81 \\82 ",
s: "\\7f \\80 \\81 \\82 " },
{ t: "\\\x83\\\x84\\\x85\\\x86", i: "\\83 \\84 \\85 \\86 ",
s: "\\83 \\84 \\85 \\86 " },
{ t: "\\\x87\\\x88\\\x89\\\x8A", i: "\\87 \\88 \\89 \\8a ",
s: "\\87 \\88 \\89 \\8a " },
{ t: "\\\x8B\\\x8C\\\x8D\\\x8E", i: "\\8b \\8c \\8d \\8e ",
s: "\\8b \\8c \\8d \\8e " },
{ t: "\\\x8F\\\x90\\\x91\\\x92", i: "\\8f \\90 \\91 \\92 ",
s: "\\8f \\90 \\91 \\92 " },
{ t: "\\\x93\\\x94\\\x95\\\x96", i: "\\93 \\94 \\95 \\96 ",
s: "\\93 \\94 \\95 \\96 " },
{ t: "\\\x97\\\x98\\\x99\\\x9A", i: "\\97 \\98 \\99 \\9a ",
s: "\\97 \\98 \\99 \\9a " },
{ t: "\\\x9B\\\x9C\\\x9D\\\x9E\\\x9F", i: "\\9b \\9c \\9d \\9e \\9f ",
s: "\\9b \\9c \\9d \\9e \\9f " },
{ t: "\\\x7f\\\x80\\\x81\\\x82", i: "\\7f \x80\x81\x82",
s: "\\7f \x80\x81\x82" },
{ t: "\\\x83\\\x84\\\x85\\\x86", i: "\x83\x84\x85\x86",
s: "\x83\x84\x85\x86" },
{ t: "\\\x87\\\x88\\\x89\\\x8A", i: "\x87\x88\x89\x8A",
s: "\x87\x88\x89\x8A" },
{ t: "\\\x8B\\\x8C\\\x8D\\\x8E", i: "\x8B\x8C\x8D\x8E",
s: "\x8B\x8C\x8D\x8E" },
{ t: "\\\x8F\\\x90\\\x91\\\x92", i: "\x8F\x90\x91\x92",
s: "\x8F\x90\x91\x92" },
{ t: "\\\x93\\\x94\\\x95\\\x96", i: "\x93\x94\x95\x96",
s: "\x93\x94\x95\x96" },
{ t: "\\\x97\\\x98\\\x99\\\x9A", i: "\x97\x98\x99\x9A",
s: "\x97\x98\x99\x9A" },
{ t: "\\\x9B\\\x9C\\\x9D\\\x9E\\\x9F", i: "\x9B\x9C\x9D\x9E\x9F",
s: "\x9B\x9C\x9D\x9E\x9F" },
// CSS doesn't bother with the full Unicode rules for identifiers,
// instead declaring that any code point greater than or equal to
// U+00A0 is a valid identifier character. Test a small handful
// U+0080 is a valid identifier character. Test a small handful
// of both basic and astral plane characters.
// Arabic (caution to editors: there is a possibly-invisible U+200E

View File

@ -35,9 +35,7 @@
#include <stddef.h>
typedef struct LinkedList_s LinkedList;
struct LinkedList_s {
struct LinkedList {
LinkedList *next;
LinkedList *prev;
};

View File

@ -420,12 +420,12 @@ static pthread_key_t tlsIndex;
#define malloc_mutex_t CRITICAL_SECTION
#define malloc_spinlock_t CRITICAL_SECTION
#elif defined(XP_DARWIN)
typedef struct {
struct malloc_mutex_t {
OSSpinLock lock;
} malloc_mutex_t;
typedef struct {
};
struct malloc_spinlock_t {
OSSpinLock lock;
} malloc_spinlock_t;
};
#else
typedef pthread_mutex_t malloc_mutex_t;
typedef pthread_mutex_t malloc_spinlock_t;
@ -449,14 +449,12 @@ static malloc_mutex_t init_lock = PTHREAD_MUTEX_INITIALIZER;
* Statistics data structures.
*/
typedef struct malloc_bin_stats_s malloc_bin_stats_t;
struct malloc_bin_stats_s {
struct malloc_bin_stats_t {
/* Current number of runs in this bin. */
unsigned long curruns;
};
typedef struct arena_stats_s arena_stats_t;
struct arena_stats_s {
struct arena_stats_t {
/* Number of bytes currently mapped. */
size_t mapped;
@ -483,8 +481,7 @@ enum ChunkType {
};
/* Tree of extents. */
typedef struct extent_node_s extent_node_t;
struct extent_node_s {
struct extent_node_t {
/* Linkage for the size/address-ordered tree. */
rb_node(extent_node_t) link_szad;
@ -517,8 +514,7 @@ typedef rb_tree(extent_node_t) extent_tree_t;
# define MALLOC_RTREE_NODESIZE CACHELINE
#endif
typedef struct malloc_rtree_s malloc_rtree_t;
struct malloc_rtree_s {
struct malloc_rtree_t {
malloc_spinlock_t lock;
void **root;
unsigned height;
@ -530,12 +526,11 @@ struct malloc_rtree_s {
* Arena data structures.
*/
typedef struct arena_s arena_t;
typedef struct arena_bin_s arena_bin_t;
struct arena_t;
struct arena_bin_t;
/* Each element of the chunk map corresponds to one page within the chunk. */
typedef struct arena_chunk_map_s arena_chunk_map_t;
struct arena_chunk_map_s {
struct arena_chunk_map_t {
/*
* Linkage for run trees. There are two disjoint uses:
*
@ -618,8 +613,7 @@ typedef rb_tree(arena_chunk_map_t) arena_avail_tree_t;
typedef rb_tree(arena_chunk_map_t) arena_run_tree_t;
/* Arena chunk header. */
typedef struct arena_chunk_s arena_chunk_t;
struct arena_chunk_s {
struct arena_chunk_t {
/* Arena that owns the chunk. */
arena_t *arena;
@ -644,8 +638,7 @@ struct arena_chunk_s {
};
typedef rb_tree(arena_chunk_t) arena_chunk_tree_t;
typedef struct arena_run_s arena_run_t;
struct arena_run_s {
struct arena_run_t {
#if defined(MOZ_DEBUG) || defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
uint32_t magic;
# define ARENA_RUN_MAGIC 0x384adf93
@ -664,7 +657,7 @@ struct arena_run_s {
unsigned regs_mask[1]; /* Dynamically sized. */
};
struct arena_bin_s {
struct arena_bin_t {
/*
* Current run being used to service allocations of this bin's size
* class.
@ -699,7 +692,7 @@ struct arena_bin_s {
malloc_bin_stats_t stats;
};
struct arena_s {
struct arena_t {
#if defined(MOZ_DEBUG) || defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
uint32_t magic;
# define ARENA_MAGIC 0x947d3d24

View File

@ -158,6 +158,41 @@ IsNotWin7PreRTM()
return IsWin7SP1OrLater() || IsWindowsBuildOrLater(7600);
}
inline bool
IsWin7AndPre2000Compatible() {
/*
* See Bug 1279171.
* We'd like to avoid using WMF on specific OS version when compatibility
* mode is in effect. The purpose of this function is to check if FF runs on
* Win7 OS with application compatibility mode being set to 95/98/ME.
* Those compatibility mode options (95/98/ME) can only display and
* be selected for 32-bit application.
* If the compatibility mode is in effect, the GetVersionEx function will
* report the OS as it identifies itself, which may not be the OS that is
* installed.
* Note : 1) We only target for Win7 build number greater than 7600.
* 2) GetVersionEx may be altered or unavailable for release after
* Win8.1. Set pragma to avoid build warning as error.
*/
bool isWin7 = IsNotWin7PreRTM() && !IsWin8OrLater();
if (!isWin7) {
return false;
}
OSVERSIONINFOEX info;
ZeroMemory(&info, sizeof(OSVERSIONINFOEX));
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
#pragma warning(push)
#pragma warning(disable:4996)
bool success = GetVersionEx((LPOSVERSIONINFO) &info);
#pragma warning(pop)
if (!success) {
return false;
}
return info.dwMajorVersion < 5;
}
} // namespace mozilla
#endif /* mozilla_WindowsVersion_h */

Binary file not shown.

Before

Width:  |  Height:  |  Size: 573 B

After

Width:  |  Height:  |  Size: 230 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 622 B

After

Width:  |  Height:  |  Size: 238 B

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