Merge autoland to mozilla-central. a=merge

This commit is contained in:
Norisz Fay 2021-10-21 12:35:33 +03:00
Родитель 0ddfc47075 ae1dc1dcf2
Коммит 2e3b0483e3
210 изменённых файлов: 5182 добавлений и 2423 удалений

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

@ -646,12 +646,40 @@ nsRect LocalAccessible::ParentRelativeBounds() {
}
}
if (mParent) {
boundingFrame = mParent->GetFrame();
// We need to find a frame to make our bounds relative to. We'll store this
// in `boundingFrame`. Ultimately, we'll create screen-relative coordinates
// by summing the x, y offsets of our ancestors' bounds in
// RemoteAccessibleBase::Bounds(), so it is important that our bounding
// frame have a corresponding accessible.
if (IsDoc() &&
nsCoreUtils::IsTopLevelContentDocInProcess(AsDoc()->DocumentNode())) {
// Tab documents and OOP iframe docs won't have ancestor accessibles with
// frames. We'll use their presshell root frame instead.
// XXX bug 1736635: Should DocAccessibles return their presShell frame on
// GetFrame()?
boundingFrame = nsLayoutUtils::GetContainingBlockForClientRect(frame);
}
// Iterate through ancestors to find one with a frame.
LocalAccessible* parent = mParent;
while (parent && !boundingFrame) {
if (parent->IsDoc()) {
// If we find a doc accessible, use its presshell's root frame
// (since doc accessibles themselves don't have frames).
boundingFrame = nsLayoutUtils::GetContainingBlockForClientRect(frame);
break;
}
if ((boundingFrame = parent->GetFrame())) {
// Otherwise, if the parent has a frame, use that
break;
}
parent = parent->LocalParent();
}
if (!boundingFrame) {
// if we can't get the bounding frame, use the pres shell root
MOZ_ASSERT_UNREACHABLE("No ancestor with frame?");
boundingFrame = nsLayoutUtils::GetContainingBlockForClientRect(frame);
}

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

@ -14,3 +14,4 @@ skip-if = webrender # Bug 1734271
https_first_disabled = true
skip-if = e10s && os == 'win' # bug 1372296
[browser_zero_area.js]
[browser_test_display_contents.js]

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

@ -0,0 +1,48 @@
/* 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";
/* import-globals-from ../../mochitest/layout.js */
async function testContentBounds(browser, acc) {
let [
expectedX,
expectedY,
expectedWidth,
expectedHeight,
] = await getContentBoundsForDOMElm(browser, getAccessibleDOMNodeID(acc));
let contentDPR = await getContentDPR(browser);
let [x, y, width, height] = getBounds(acc, contentDPR);
let prettyAccName = prettyName(acc);
is(x, expectedX, "Wrong x coordinate of " + prettyAccName);
is(y, expectedY, "Wrong y coordinate of " + prettyAccName);
is(width, expectedWidth, "Wrong width of " + prettyAccName);
ok(height >= expectedHeight, "Wrong height of " + prettyAccName);
}
async function runTests(browser, accDoc) {
let p = findAccessibleChildByID(accDoc, "div");
let p2 = findAccessibleChildByID(accDoc, "p");
await testContentBounds(browser, p);
await testContentBounds(browser, p2);
}
/**
* Test accessible bounds for accs with display:contents
*/
addAccessibleTask(
`
<div id="div">before
<ul id="ul" style="display: contents;">
<li id="li" style="display: contents;">
<p id="p">item</p>
</li>
</ul>
</div>`,
runTests,
{ iframe: true, remoteIframe: true }
);

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

@ -681,6 +681,40 @@ pref("security.allow_parent_unrestricted_js_loads", false);
// Unload tabs when available memory is running low
pref("browser.tabs.unloadOnLowMemory", true);
#if defined(XP_MACOSX)
// During low memory periods, poll with this frequency (milliseconds)
// until memory is no longer low. Changes to the pref take effect immediately.
// Browser restart not required. Chosen to be consistent with the windows
// implementation, but otherwise the 10s value is arbitrary.
pref("browser.lowMemoryPollingIntervalMS", 10000);
// Pref to control the reponse taken on macOS when the OS is under memory
// pressure. Changes to the pref take effect immediately. Browser restart not
// required. The pref value is a bitmask:
// 0x0: No response (other than recording for telemetry, crash reporting)
// 0x1: Use the tab unloading feature to reduce memory use. Requires that
// the above "browser.tabs.unloadOnLowMemory" pref be set to true for tab
// unloading to occur.
// 0x2: Issue the internal "memory-pressure" notification to reduce memory use
// 0x3: Both 0x1 and 0x2.
#if defined(NIGHTLY_BUILD)
pref("browser.lowMemoryResponseMask", 3);
#else
pref("browser.lowMemoryResponseMask", 0);
#endif
// Controls which macOS memory-pressure level triggers the browser low memory
// response. Changes to the pref take effect immediately. Browser restart not
// required. By default, use the "critical" level as that occurs after "warn"
// and we only want to trigger the low memory reponse when necessary.
// The macOS system memory-pressure level is either none, "warn", or
// "critical". The OS notifies the browser when the level changes. A false
// value for the pref indicates the low memory response should occur when
// reaching the "critical" level. A true value indicates the response should
// occur when reaching the "warn" level.
pref("browser.lowMemoryResponseOnWarn", false);
#endif
pref("browser.ctrlTab.sortByRecentlyUsed", false);
// By default, do not export HTML at shutdown.

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

@ -375,17 +375,12 @@ var FullScreen = {
// don't need that kind of precision in our CSS.
shiftSize = shiftSize.toFixed(2);
let toolbox = document.getElementById("navigator-toolbox");
let browserEl = document.getElementById("browser");
if (shiftSize > 0) {
toolbox.style.setProperty("transform", `translateY(${shiftSize}px)`);
toolbox.style.setProperty("z-index", "2");
toolbox.style.setProperty("position", "relative");
browserEl.style.setProperty("position", "relative");
} else {
toolbox.style.removeProperty("transform");
toolbox.style.removeProperty("z-index");
toolbox.style.removeProperty("position");
browserEl.style.removeProperty("position");
}
},

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

@ -236,7 +236,7 @@ panelview[mainview] > .panel-header {
%endif
#tabbrowser-tabs[positionpinnedtabs] > #tabbrowser-arrowscrollbox > .tabbrowser-tab[pinned] {
position: fixed !important;
position: absolute !important;
display: block;
}

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

@ -4103,6 +4103,7 @@ BrowserGlue.prototype = {
const buttons = [
{
"l10n-id": "restore-session-startup-suggestion-button",
primary: true,
callback: () => {
win.PanelUI.selectAndMarkItem([
"appMenu-history-button",

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

@ -297,6 +297,10 @@ $letter-fallback-color: $white;
}
.edit-topsites-wrapper {
.top-site-inner > .top-site-button > .tile {
border: 1px solid var(--newtab-border-color);
}
.modal {
box-shadow: $shadow-secondary;
left: 0;

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

@ -760,6 +760,9 @@ main.has-snippet {
visibility: hidden;
}
.edit-topsites-wrapper .top-site-inner > .top-site-button > .tile {
border: 1px solid var(--newtab-border-color);
}
.edit-topsites-wrapper .modal {
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.2);
left: 0;

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

@ -764,6 +764,9 @@ main.has-snippet {
visibility: hidden;
}
.edit-topsites-wrapper .top-site-inner > .top-site-button > .tile {
border: 1px solid var(--newtab-border-color);
}
.edit-topsites-wrapper .modal {
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.2);
left: 0;

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

@ -760,6 +760,9 @@ main.has-snippet {
visibility: hidden;
}
.edit-topsites-wrapper .top-site-inner > .top-site-button > .tile {
border: 1px solid var(--newtab-border-color);
}
.edit-topsites-wrapper .modal {
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.2);
left: 0;

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

@ -34,6 +34,7 @@ const KNOWN_SEARCH_SOURCES = new Map([
["searchbar", "searchbar"],
["system", "system"],
["urlbar", "urlbar"],
["urlbar-handoff", "urlbar_handoff"],
["urlbar-searchmode", "urlbar_searchmode"],
["webextension", "webextension"],
]);
@ -204,6 +205,7 @@ class BrowserSearchTelemetryHandler {
case "urlbar":
case "searchbar":
case "urlbar-searchmode":
case "urlbar-handoff":
this._handleSearchAndUrlbar(browser, engine, source, details);
break;
case "abouthome":

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

@ -51,6 +51,7 @@ SEARCH_COUNTS - SAP usage
- ``searchbar``
- ``system``
- ``urlbar`` Except aliases and search mode.
- ``urlbar_handoff`` Used when searching from about:newtab.
- ``urlbar-searchmode`` Used when the Urlbar is in search mode.
- ``webextension``
@ -62,6 +63,7 @@ browser.engagement.navigation.*
Possible SAPs are:
- ``urlbar`` Except search mode.
- ``urlbar_handoff`` Used when searching from about:newtab.
- ``urlbar_searchmode`` Used when the Urlbar is in search mode.
- ``searchbar``
- ``about_home``
@ -115,6 +117,7 @@ browser.search.content.*
They are broken down by the originating SAP where known:
- ``urlbar`` Except search mode.
- ``urlbar_handoff`` Used when searching from about:newtab.
- ``urlbar_searchmode`` Used when the Urlbar is in search mode.
- ``searchbar``
- ``about_home``

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

@ -72,7 +72,15 @@ add_task(async function setup() {
SearchSERPTelemetry.overrideSearchTelemetryForTests(TEST_PROVIDER_INFO);
await waitForIdle();
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.suggest.searches", true]],
set: [
["browser.urlbar.suggest.searches", true],
[
"browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar",
true,
],
// Ensure to add search suggestion telemetry as search_suggestion not search_formhistory.
["browser.urlbar.maxHistoricalSearchSuggestions", 0],
],
});
// Enable local telemetry recording for the duration of the tests.
let oldCanRecord = Services.telemetry.canRecordExtended;
@ -190,6 +198,50 @@ add_task(async function test_source_urlbar() {
);
});
add_task(async function test_source_urlbar_handoff() {
let tab;
await track_ad_click(
"urlbar-handoff",
"urlbar_handoff",
async () => {
tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
BrowserTestUtils.loadURI(tab.linkedBrowser, "about:newtab");
await BrowserTestUtils.browserStopped(tab.linkedBrowser, "about:newtab");
info("Focus on search input in newtab content");
await SpecialPowers.spawn(tab.linkedBrowser, [], async function() {
const searchInput = content.document.querySelector(".fake-editable");
searchInput.click();
});
info("Get suggestions");
for (const c of "searchSuggestion".split("")) {
EventUtils.synthesizeKey(c);
/* eslint-disable mozilla/no-arbitrary-setTimeout */
await new Promise(r => setTimeout(r, 50));
}
await TestUtils.waitForCondition(async () => {
const index = await getFirstSuggestionIndex();
return index >= 0;
}, "Wait until suggestions are ready");
let idx = await getFirstSuggestionIndex();
Assert.greaterOrEqual(idx, 0, "there should be a first suggestion");
const onLoaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
while (idx--) {
EventUtils.sendKey("down");
}
EventUtils.sendKey("return");
await onLoaded;
return tab;
},
async () => {
BrowserTestUtils.removeTab(tab);
}
);
});
add_task(async function test_source_searchbar() {
let tab;
await track_ad_click(

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

@ -4208,6 +4208,13 @@ var SessionStoreInternal = {
aWindow.__SSi
]._lastClosedTabGroupCount = newLastClosedTabGroupCount;
if (!this._isWindowLoaded(aWindow)) {
// from now on, the data will come from the actual window
delete this._statesToRestore[WINDOW_RESTORE_IDS.get(aWindow)];
WINDOW_RESTORE_IDS.delete(aWindow);
delete this._windows[aWindow.__SSi]._restoring;
}
// Restore tabs, if any.
if (winData.tabs.length) {
this.restoreTabs(aWindow, tabs, winData.tabs, selectTab);
@ -4402,13 +4409,6 @@ var SessionStoreInternal = {
restoreTabs(aWindow, aTabs, aTabData, aSelectTab) {
var tabbrowser = aWindow.gBrowser;
if (!this._isWindowLoaded(aWindow)) {
// from now on, the data will come from the actual window
delete this._statesToRestore[WINDOW_RESTORE_IDS.get(aWindow)];
WINDOW_RESTORE_IDS.delete(aWindow);
delete this._windows[aWindow.__SSi]._restoring;
}
let numTabsToRestore = aTabs.length;
let numTabsInWindow = tabbrowser.tabs.length;
let tabsDataArray = this._windows[aWindow.__SSi].tabs;

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

@ -101,6 +101,7 @@ skip-if =
[browser_pinned_tabs.js]
skip-if = debug || ccov # Bug 1625525
[browser_restore_srcdoc.js]
[browser_restore_tabless_window.js]
[browser_unrestored_crashedTabs.js]
skip-if =
!e10s || !crashreporter

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

@ -0,0 +1,56 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* It's possible for us to restore windows without tabs,
* when on Windows/Linux the user closes the last tab as
* a means of closing the last window. That last tab
* would appear as a closed tab in session state for the
* window, with no open tabs (so the state would be resumed
* as showing the homepage). See bug 490136 for context.
* This test checks that in this case, the resulting window
* is functional and indeed has the required previously
* closed tabs available.
*/
add_task(async function test_restoring_tabless_window() {
let newWin = await BrowserTestUtils.openNewBrowserWindow();
let newState = {
windows: [
{
tabs: [],
_closedTabs: [
{
state: { entries: [{ url: "about:" }] },
title: "About:",
},
],
},
],
};
await setWindowState(newWin, newState, true);
let tab = await BrowserTestUtils.openNewForegroundTab(
newWin.gBrowser,
"https://example.com"
);
await TabStateFlusher.flush(tab.linkedBrowser);
let receivedState = SessionStore.getWindowState(newWin);
let { tabs, selected } = receivedState.windows[0];
is(tabs.length, 2, "Should have two tabs");
is(selected, 2, "Should have selected the new tab");
ok(
tabs[1]?.entries.some(e => e.url == "https://example.com/"),
"Should have found the new URL"
);
let closedTabData = SessionStore.getClosedTabData(newWin);
is(closedTabData.length, 1, "Should have found 1 closed tab");
is(
closedTabData[0]?.state.entries[0].url,
"about:",
"Should have found the right URL for the closed tab"
);
await BrowserTestUtils.closeWindow(newWin);
});

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

@ -8,7 +8,7 @@ const { TabUnloader } = ChromeUtils.import(
);
async function refreshData() {
const sortedTabs = await TabUnloader.getSortedTabs();
const sortedTabs = await TabUnloader.getSortedTabs(null);
const tabTable = document.querySelector(".tab-table > tbody");
const getHost = uri => {
try {
@ -114,7 +114,7 @@ async function onLoad() {
document
.getElementById("button-unload")
.addEventListener("click", async () => {
await TabUnloader.unloadLeastRecentlyUsedTab();
await TabUnloader.unloadLeastRecentlyUsedTab(null);
await refreshData();
});
await refreshData();

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

@ -684,6 +684,7 @@ class UrlbarInput {
*
*/
handoff(searchString, searchEngine) {
this._isHandoffSession = true;
if (UrlbarPrefs.get("shouldHandOffToSearchMode") && searchEngine) {
this.search(searchString, {
searchEngine,
@ -2232,15 +2233,22 @@ class UrlbarInput {
_recordSearch(engine, event, searchActionDetails = {}) {
const isOneOff = this.view.oneOffSearchButtons.eventTargetIsAOneOff(event);
BrowserSearchTelemetry.recordSearch(
this.window.gBrowser.selectedBrowser,
engine,
let source = "urlbar";
if (this._isHandoffSession) {
source = "urlbar-handoff";
} else if (this.searchMode && !isOneOff) {
// Without checking !isOneOff, we might record the string
// oneoff_urlbar-searchmode in the SEARCH_COUNTS probe (in addition to
// oneoff_urlbar and oneoff_searchbar). The extra information is not
// necessary; the intent is the same regardless of whether the user is
// in search mode when they do a key-modified click/enter on a one-off.
this.searchMode && !isOneOff ? "urlbar-searchmode" : "urlbar",
source = "urlbar-searchmode";
}
BrowserSearchTelemetry.recordSearch(
this.window.gBrowser.selectedBrowser,
engine,
source,
{ ...searchActionDetails, isOneOff }
);
}
@ -2733,6 +2741,8 @@ class UrlbarInput {
_on_blur(event) {
this.focusedViaMousedown = false;
this._isHandoffSession = false;
// We cannot count every blur events after a missed engagement as abandoment
// because the user may have clicked on some view element that executes
// a command causing a focus change. For example opening preferences from

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

@ -328,6 +328,8 @@ support-files =
urlbarTelemetryUrlbarDynamic.css
[browser_urlbar_telemetry_extension.js]
tags = search-telemetry
[browser_urlbar_telemetry_handoff.js]
tags = search-telemetry
[browser_urlbar_telemetry_places.js]
https_first_disabled = true
tags = search-telemetry

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

@ -0,0 +1,150 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const { SearchSERPTelemetry } = ChromeUtils.import(
"resource:///modules/SearchSERPTelemetry.jsm"
);
const TEST_PROVIDER_INFO = [
{
telemetryId: "example",
searchPageRegexp: /^https:\/\/example.com\/browser\/browser\/components\/search\/test\/browser\/searchTelemetry(?:Ad)?.html/,
queryParamName: "s",
codeParamName: "abc",
codePrefixes: ["ff"],
followOnParamNames: ["a"],
extraAdServersRegexps: [/^https:\/\/example\.com\/ad2?/],
},
];
function getPageUrl(useAdPage = false) {
let page = useAdPage ? "searchTelemetryAd.html" : "searchTelemetry.html";
return `https://example.com/browser/browser/components/search/test/browser/${page}`;
}
// sharedData messages are only passed to the child on idle. Therefore
// we wait for a few idles to try and ensure the messages have been able
// to be passed across and handled.
async function waitForIdle() {
for (let i = 0; i < 10; i++) {
await new Promise(resolve => Services.tm.idleDispatchToMainThread(resolve));
}
}
add_task(async function setup() {
SearchSERPTelemetry.overrideSearchTelemetryForTests(TEST_PROVIDER_INFO);
await waitForIdle();
await SpecialPowers.pushPrefEnv({
set: [
[
"browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar",
true,
],
],
});
await SearchTestUtils.installSearchExtension({
search_url: getPageUrl(true),
search_url_get_params: "s={searchTerms}&abc=ff",
suggest_url:
"https://example.com/browser/browser/components/search/test/browser/searchSuggestionEngine.sjs",
suggest_url_get_params: "query={searchTerms}",
});
const testEngine = Services.search.getEngineByName("Example");
const originalEngine = await Services.search.getDefault();
await Services.search.setDefault(testEngine);
const oldCanRecord = Services.telemetry.canRecordExtended;
Services.telemetry.canRecordExtended = true;
Services.telemetry.setEventRecordingEnabled("navigation", true);
registerCleanupFunction(async function() {
await Services.search.setDefault(originalEngine);
await PlacesUtils.history.clear();
Services.telemetry.canRecordExtended = oldCanRecord;
Services.telemetry.setEventRecordingEnabled("navigation", false);
SearchSERPTelemetry.overrideSearchTelemetryForTests();
Services.telemetry.clearScalars();
Services.telemetry.clearEvents();
});
});
add_task(async function test_search() {
Services.telemetry.clearScalars();
Services.telemetry.clearEvents();
const histogram = TelemetryTestUtils.getAndClearKeyedHistogram(
"SEARCH_COUNTS"
);
info("Load about:newtab in new window");
const newtab = "about:newtab";
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
BrowserTestUtils.loadURI(tab.linkedBrowser, newtab);
await BrowserTestUtils.browserStopped(tab.linkedBrowser, newtab);
info("Focus on search input in newtab content");
await SpecialPowers.spawn(tab.linkedBrowser, [], async function() {
const searchInput = content.document.querySelector(".fake-editable");
searchInput.click();
});
info("Search and wait the result");
const onLoaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
EventUtils.synthesizeKey("q");
EventUtils.synthesizeKey("VK_RETURN");
await onLoaded;
info("Check the telemetries");
await assertScalars([
["browser.engagement.navigation.urlbar_handoff", "search_enter", 1],
]);
await assertHistogram(histogram, [
["example.in-content:sap:ff", 1],
["other-Example.urlbar-handoff", 1],
]);
TelemetryTestUtils.assertEvents(
[
[
"navigation",
"search",
"urlbar_handoff",
"enter",
{ engine: "other-Example" },
],
],
{ category: "navigation", method: "search" }
);
BrowserTestUtils.removeTab(tab);
});
async function assertHistogram(histogram, expectedResults) {
await TestUtils.waitForCondition(() => {
const snapshot = histogram.snapshot();
return expectedResults.every(([key]) => key in snapshot);
}, "Wait until the histogram has expected keys");
for (const [key, value] of expectedResults) {
TelemetryTestUtils.assertKeyedHistogramSum(histogram, key, value);
}
}
async function assertScalars(expectedResults) {
await TestUtils.waitForCondition(() => {
const scalars = TelemetryTestUtils.getProcessScalars("parent", true);
return expectedResults.every(([scalarName]) => scalarName in scalars);
}, "Wait until the scalars have expected keyed scalars");
const scalars = TelemetryTestUtils.getProcessScalars("parent", true);
for (const [scalarName, key, value] of expectedResults) {
TelemetryTestUtils.assertKeyedScalar(scalars, scalarName, key, value);
}
}

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

@ -513,6 +513,7 @@ ${RemoveDefaultBrowserAgentShortcut}
${AddAssociationIfNoneExist} ".webm" "FirefoxHTML$5"
${AddAssociationIfNoneExist} ".svg" "FirefoxHTML$5"
${AddAssociationIfNoneExist} ".webp" "FirefoxHTML$5"
${AddAssociationIfNoneExist} ".avif" "FirefoxHTML$5"
; An empty string is used for the 5th param because FirefoxHTML is not a
; protocol handler
@ -604,6 +605,7 @@ ${RemoveDefaultBrowserAgentShortcut}
WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".xhtml" "FirefoxHTML$2"
WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".svg" "FirefoxHTML$2"
WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".webp" "FirefoxHTML$2"
WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".avif" "FirefoxHTML$2"
WriteRegStr ${RegKey} "$0\Capabilities\StartMenu" "StartMenuInternet" "$1"
@ -673,6 +675,7 @@ ${RemoveDefaultBrowserAgentShortcut}
${WriteApplicationsSupportedType} ${RegKey} ".svg"
${WriteApplicationsSupportedType} ${RegKey} ".webm"
${WriteApplicationsSupportedType} ${RegKey} ".webp"
${WriteApplicationsSupportedType} ${RegKey} ".avif"
${WriteApplicationsSupportedType} ${RegKey} ".xht"
${WriteApplicationsSupportedType} ${RegKey} ".xhtml"
${WriteApplicationsSupportedType} ${RegKey} ".xml"
@ -1622,6 +1625,8 @@ Function SetAsDefaultAppUserHKCU
Pop $0
AppAssocReg::SetAppAsDefault "$R9" ".webp" "file"
Pop $0
AppAssocReg::SetAppAsDefault "$R9" ".avif" "file"
Pop $0
AppAssocReg::SetAppAsDefault "$R9" ".xht" "file"
Pop $0
AppAssocReg::SetAppAsDefault "$R9" ".xhtml" "file"

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

@ -496,6 +496,7 @@ Section "Uninstall"
${un.RegCleanFileHandler} ".webm" "FirefoxHTML-$AppUserModelID"
${un.RegCleanFileHandler} ".svg" "FirefoxHTML-$AppUserModelID"
${un.RegCleanFileHandler} ".webp" "FirefoxHTML-$AppUserModelID"
${un.RegCleanFileHandler} ".avif" "FirefoxHTML-$AppUserModelID"
SetShellVarContext all ; Set SHCTX to HKLM
${un.GetSecondInstallPath} "Software\Mozilla" $R9

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

@ -29,6 +29,10 @@ const MIN_TABS_COUNT = 10;
// Weight for non-discardable tabs.
const NEVER_DISCARD = 100000;
// Default minimum inactive duration. Tabs that were accessed in the last
// period of this duration are not unloaded.
const kMinInactiveDurationInMs = 600000; // ten minutes
let criteriaTypes = [
["isNonDiscardable", NEVER_DISCARD],
["isLoading", 8],
@ -82,6 +86,10 @@ let DefaultTabUnloaderMethods = {
return MIN_TABS_COUNT;
},
getNow() {
return Date.now();
},
*iterateTabs() {
for (let win of Services.wm.getEnumerator("navigator:browser")) {
for (let tab of win.gBrowser.tabs) {
@ -155,7 +163,7 @@ var TabUnloader = {
},
// This method is exposed on nsITabUnloader
async unloadTabAsync() {
async unloadTabAsync(minInactiveDuration = kMinInactiveDurationInMs) {
const watcher = Cc["@mozilla.org/xpcom/memory-watcher;1"].getService(
Ci.nsIAvailableMemoryWatcherBase
);
@ -174,7 +182,9 @@ var TabUnloader = {
}
this._isUnloading = true;
const isTabUnloaded = await this.unloadLeastRecentlyUsedTab();
const isTabUnloaded = await this.unloadLeastRecentlyUsedTab(
minInactiveDuration
);
this._isUnloading = false;
watcher.onUnloadAttemptCompleted(
@ -186,6 +196,10 @@ var TabUnloader = {
* Get a list of tabs that can be discarded. This list includes all tabs in
* all windows and is sorted based on a weighting described below.
*
* @param minInactiveDuration If this value is a number, tabs that were accessed
* in the last |minInactiveDuration| msec are not unloaded even if they
* are least-recently-used.
*
* @param tabMethods an helper object with methods called by this algorithm.
*
* The algorithm used is:
@ -210,11 +224,24 @@ var TabUnloader = {
* The tabMethods are used so that unit tests can use false tab objects and
* override their behaviour.
*/
async getSortedTabs(tabMethods = DefaultTabUnloaderMethods) {
async getSortedTabs(
minInactiveDuration = kMinInactiveDurationInMs,
tabMethods = DefaultTabUnloaderMethods
) {
let tabs = [];
const now = tabMethods.getNow();
let lowestWeight = 1000;
for (let tab of tabMethods.iterateTabs()) {
if (
typeof minInactiveDuration == "number" &&
now - tab.tab.lastAccessed < minInactiveDuration
) {
// Skip "fresh" tabs, which were accessed within the specified duration.
continue;
}
let weight = determineTabBaseWeight(tab, tabMethods);
// Don't add tabs that have a weight of -1.
@ -279,8 +306,10 @@ var TabUnloader = {
* Select and discard one tab.
* @returns true if a tab was unloaded, otherwise false.
*/
async unloadLeastRecentlyUsedTab() {
let sortedTabs = await this.getSortedTabs();
async unloadLeastRecentlyUsedTab(
minInactiveDuration = kMinInactiveDurationInMs
) {
const sortedTabs = await this.getSortedTabs(minInactiveDuration);
for (let tabInfo of sortedTabs) {
if (!this.isDiscardable(tabInfo)) {

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

@ -86,7 +86,7 @@ async function pressure() {
"TabBrowserDiscarded",
true
);
TabUnloader.unloadTabAsync();
TabUnloader.unloadTabAsync(null);
return tabDiscarded;
}
@ -106,12 +106,12 @@ function pressureAndObserve(aExpectedTopic) {
};
Services.obs.addObserver(observer, aExpectedTopic);
});
TabUnloader.unloadTabAsync();
TabUnloader.unloadTabAsync(null);
return promise;
}
async function compareTabOrder(expectedOrder) {
let tabInfo = await TabUnloader.getSortedTabs();
let tabInfo = await TabUnloader.getSortedTabs(null);
is(
tabInfo.length,
@ -214,7 +214,7 @@ add_task(async function test() {
ok(pinnedSoundTab.soundPlaying, "tab is still playing sound");
// There are no unloadable tabs.
TabUnloader.unloadTabAsync();
TabUnloader.unloadTabAsync(null);
ok(soundTab.linkedPanel, "a tab playing sound is never unloaded");
const histogram = TelemetryTestUtils.getAndClearHistogram(

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

@ -41,6 +41,10 @@ let TestTabUnloaderMethods = {
return 3;
},
getNow() {
return 100;
},
*iterateProcesses(tab) {
for (let process of tab.process.split(",")) {
yield Number(process);
@ -133,6 +137,13 @@ let unloadTests = [
],
result: "2,0,3,5,1,4",
},
{
// Since TestTabUnloaderMethods.getNow() returns 100 and the test
// passes minInactiveDuration = 0 to TabUnloader.getSortedTabs(),
// tab 200 and 300 are excluded from the result.
tabs: ["300", "10", "50", "100", "200"],
result: "1,2,3",
},
{
tabs: ["1", "2", "3", "4", "5", "6"],
process: ["1", "2", "1", "1", "1", "1"],
@ -407,7 +418,10 @@ add_task(async function doTests() {
TestTabUnloaderMethods.iterateTabs = iterateTabs;
let expectedOrder = "";
let sortedTabs = await TabUnloader.getSortedTabs(TestTabUnloaderMethods);
const sortedTabs = await TabUnloader.getSortedTabs(
0,
TestTabUnloaderMethods
);
for (let tab of sortedTabs) {
if (expectedOrder) {
expectedOrder += ",";

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

@ -31,8 +31,15 @@
--arrowpanel-field-background: rgba(12,12,13,.3);
}
#browser {
/* #browser and #navigator-toolbox must have relative positions so that the
latter can slide over the former in fullscreen mode. */
position: relative;
}
#navigator-toolbox {
appearance: none;
position: relative;
}
#navigator-toolbox:not(:-moz-lwtheme) {

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

@ -30,7 +30,8 @@ panel[type="arrow"][side="right"] {
#customizationui-widget-panel,
#identity-popup,
#permission-popup,
#protections-popup {
#protections-popup,
#appMenu-notification-popup {
margin-top: -1px; /* Overrides value from panelUI.inc.css */
}

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

@ -55,6 +55,7 @@
--urlbar-box-hover-bgcolor: var(--button-hover-bgcolor);
--urlbar-box-active-bgcolor: var(--button-active-bgcolor);
--urlbar-box-text-color: inherit;
--urlbar-box-hover-text-color: var(--urlbar-box-text-color);
--urlbar-min-height: 32px;
--urlbar-icon-fill-opacity: 0.9;
--urlbar-icon-padding: 6px; /* (32px - 2px border - 2px padding - 16px icon) / 2 */
@ -74,6 +75,10 @@
--autocomplete-popup-separator-color: color-mix(in srgb, currentColor 86%, transparent);
--urlbar-icon-fill-opacity: 1;
--checkbox-checked-border-color: var(--checkbox-checked-bgcolor);
--urlbar-box-hover-bgcolor: SelectedItem;
--urlbar-box-active-bgcolor: SelectedItem;
--urlbar-box-hover-text-color: SelectedItemText;
}
}

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

@ -462,15 +462,17 @@ description#identity-popup-content-verifier,
.protections-popup-category:hover,
.protections-popup-footer-button:-moz-focusring,
.protections-popup-footer-button:hover,
#protections-popup-not-blocking-section-why:hover,
#protections-popup-show-report-stack:hover > .protections-popup-footer-button {
background-color: var(--arrowpanel-dimmed);
background-color: var(--panel-item-hover-bgcolor);
outline: none;
}
.protections-popup-category:hover:active,
.protections-popup-footer-button:hover:active,
#protections-popup-not-blocking-section-why:hover:active,
#protections-popup-show-report-stack:hover:active > .protections-popup-footer-button {
background-color: var(--arrowpanel-dimmed-further);
background-color: var(--panel-item-active-bgcolor);
}
/* This subview could get filled with a lot of trackers, set a maximum size
@ -589,11 +591,14 @@ description#identity-popup-content-verifier,
}
#protections-popup-not-blocking-section-why:hover {
background-color: var(--arrowpanel-dimmed);
outline: 4px solid var(--arrowpanel-dimmed);
outline: 4px solid var(--panel-item-hover-bgcolor);
text-decoration: none;
}
#protections-popup-not-blocking-section-why:hover:active {
outline-color: var(--panel-item-active-bgcolor);
}
.protections-popup-category.notFound {
color: var(--panel-description-color);
fill: var(--panel-description-color);
@ -1005,18 +1010,6 @@ description#identity-popup-content-verifier,
margin-inline-end: 0;
}
.protections-popup-category:hover,
.protections-popup-footer-button:hover,
#protections-popup-show-report-stack:hover > .protections-popup-footer-button {
background-color: var(--panel-item-hover-bgcolor);
}
.protections-popup-category:hover:active,
.protections-popup-footer-button:hover:active,
#protections-popup-show-report-stack:hover:active > .protections-popup-footer-button {
background-color: var(--panel-item-active-bgcolor);
}
.protections-popup-category:focus-visible,
.protections-popup-footer-button:focus-visible {
box-shadow: var(--panelview-toolbarbutton-focus-box-shadow);

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

@ -164,7 +164,8 @@ panelmultiview[transitioning] > .panel-viewcontainer > .panel-viewstack > panelv
#customizationui-widget-panel,
#identity-popup,
#permission-popup,
#protections-popup {
#protections-popup,
#appMenu-notification-popup {
margin-top: -4px;
}

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

@ -74,9 +74,9 @@
border-radius: var(--urlbar-icon-border-radius);
}
#urlbar[focused="true"] #identity-box[pageproxystate="valid"].notSecureText > .identity-box-button,
#urlbar[focused="true"] #identity-box[pageproxystate="valid"].chromeUI > .identity-box-button,
#urlbar[focused="true"] #identity-box[pageproxystate="valid"].extensionPage > .identity-box-button,
#urlbar[focused="true"] #identity-box[pageproxystate="valid"].notSecureText > .identity-box-button:not(:hover, [open=true]),
#urlbar[focused="true"] #identity-box[pageproxystate="valid"].chromeUI > .identity-box-button:not(:hover, [open=true]),
#urlbar[focused="true"] #identity-box[pageproxystate="valid"].extensionPage > .identity-box-button:not(:hover, [open=true]),
#urlbar[focused="true"] #urlbar-label-box {
background-color: var(--urlbar-box-focus-bgcolor);
}
@ -85,6 +85,7 @@
#identity-box[pageproxystate="valid"].chromeUI > .identity-box-button:hover:not([open]),
#identity-box[pageproxystate="valid"].extensionPage > .identity-box-button:hover:not([open]) {
background-color: var(--urlbar-box-hover-bgcolor);
color: var(--urlbar-box-hover-text-color);
}
#identity-box[pageproxystate="valid"].notSecureText > .identity-box-button:hover:active,
@ -94,6 +95,7 @@
#identity-box[pageproxystate="valid"].extensionPage > .identity-box-button:hover:active,
#identity-box[pageproxystate="valid"].extensionPage > .identity-box-button[open=true] {
background-color: var(--urlbar-box-active-bgcolor);
color: var(--urlbar-box-hover-text-color);
}
#urlbar[searchmode]:not([focused="true"]) > #urlbar-input-container > #urlbar-search-mode-indicator,

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

@ -314,20 +314,15 @@
}
#urlbar-search-mode-indicator-close:hover {
background-color: hsla(0,0%,70%,.2);
background-color: var(--urlbar-box-hover-bgcolor);
color: var(--urlbar-box-hover-text-color);
}
#urlbar-search-mode-indicator-close:hover:active {
background-color: hsla(0,0%,70%,.3);
background-color: var(--urlbar-box-active-bgcolor);
color: var(--urlbar-box-hover-text-color);
}
/* Use system colors for low/high contrast mode */
@media (prefers-contrast) {
#urlbar-search-mode-indicator {
background-color: SelectedItem;
outline-color: SelectedItem;
color: SelectedItemText;
}
#urlbar-search-mode-indicator-close {
fill-opacity: 1.0;
}
@ -406,14 +401,16 @@
.urlbar-page-action:not([disabled]):hover,
#urlbar-go-button:hover,
.search-go-button:hover {
background-color: hsla(0,0%,70%,.2);
background-color: var(--urlbar-box-hover-bgcolor);
color: var(--urlbar-box-hover-text-color);
}
.urlbar-page-action:not([disabled])[open],
.urlbar-page-action:not([disabled]):hover:active,
#urlbar-go-button:hover:active,
.search-go-button:hover:active {
background-color: hsla(0,0%,70%,.3);
background-color: var(--urlbar-box-active-bgcolor);
color: var(--urlbar-box-hover-text-color);
}
.urlbar-page-action:-moz-focusring {
@ -594,16 +591,18 @@
overflow: hidden;
}
#urlbar[focused="true"] #urlbar-zoom-button {
#urlbar[focused="true"] #urlbar-zoom-button:not(:hover) {
background-color: var(--urlbar-box-focus-bgcolor);
}
#urlbar-zoom-button:hover {
background-color: var(--urlbar-box-hover-bgcolor);
color: var(--urlbar-box-hover-text-color);
}
#urlbar-zoom-button:hover:active {
background-color: var(--urlbar-box-active-bgcolor);
color: var(--urlbar-box-hover-text-color);
}
@keyframes urlbar-zoom-reset-pulse {

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

@ -4,26 +4,15 @@
Visual Studio Projects
======================
The build system contains alpha support for generating Visual Studio
project files to aid with development.
The build system automatically generates Visual Studio project files to aid
with development, as part of a normal ``mach build`` from the command line.
To generate Visual Studio project files, you'll need to have a configured tree::
You can find the solution file at ``$OBJDIR/msvs/mozilla.sln``.
mach configure
If you want to generate the project files before/without doing a full build,
running ``./mach configure && ./mach build-backend -b VisualStudio`` will do
so.
(If you have built recently, your tree is already configured.)
Then, simply generate the Visual Studio build backend::
mach build-backend -b VisualStudio
If all goes well, the path to the generated Solution (``.sln``) file should be
printed. You should be able to open that solution with Visual Studio 2010 or
newer.
Currently, output is hard-coded to the Visual Studio 2010 format. If you open
the solution in a newer Visual Studio release, you will be prompted to upgrade
projects. Simply click through the wizard to do that.
Structure of Solution
=====================
@ -59,20 +48,8 @@ Libraries
Updating Project Files
======================
As you pull and update the source tree, your Visual Studio files may fall out
of sync with the build configuration. The tree should still build fine from
within Visual Studio. But source files may be missing and IntelliSense may not
have the proper build configuration.
To account for this, you'll want to periodically regenerate the Visual Studio
project files. You can do this within Visual Studio by building the
``Build Targets :: visual-studio`` project or by running
``mach build-backend -b VisualStudio`` from the command line.
Currently, regeneration rewrites the original project files. **If you've made
any customizations to the solution or projects, they will likely get
overwritten.** We would like to improve this user experience in the
future.
Either re-running ``./mach build`` or ``./mach build-backend -b VisualStudio``
will update the Visual Studio files after the tree changes.
Moving Project Files Around
===========================

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

@ -95,7 +95,7 @@ ifdef LIBFUZZER
ifndef MOZ_TSAN
ifndef FUZZING_JS_FUZZILLI
# These options should match what is implicitly enabled for `clang -fsanitize=fuzzer`
# here: https://github.com/llvm/llvm-project/blob/release/8.x/clang/lib/Driver/SanitizerArgs.cpp#L354
# here: https://github.com/llvm/llvm-project/blob/release/13.x/clang/lib/Driver/SanitizerArgs.cpp#L422
#
# -sanitizer-coverage-inline-8bit-counters Increments 8-bit counter for every edge.
# -sanitizer-coverage-level=4 Enable coverage for all blocks, critical edges, and indirect calls.
@ -103,7 +103,14 @@ ifndef FUZZING_JS_FUZZILLI
# -sanitizer-coverage-pc-table Create a static PC table.
#
# In TSan builds, we must not pass any of these, because sanitizer coverage is incompatible with TSan.
rustflags_sancov += -Cpasses=sancov -Cllvm-args=-sanitizer-coverage-inline-8bit-counters -Cllvm-args=-sanitizer-coverage-level=4 -Cllvm-args=-sanitizer-coverage-trace-compares -Cllvm-args=-sanitizer-coverage-pc-table
#
# sancov legacy pass was removed in rustc 1.57 and replaced by sancov-module
ifeq (1,$(words $(filter 1.53.% 1.54.% 1.55.% 1.56.%,$(RUSTC_VERSION))))
rustflags_sancov += -Cpasses=sancov
else
rustflags_sancov += -Cpasses=sancov-module
endif
rustflags_sancov += -Cllvm-args=-sanitizer-coverage-inline-8bit-counters -Cllvm-args=-sanitizer-coverage-level=4 -Cllvm-args=-sanitizer-coverage-trace-compares -Cllvm-args=-sanitizer-coverage-pc-table
endif
endif
endif

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

@ -43,14 +43,17 @@ add_task(async function() {
info("Test CORSPreflightDidNotSucceed");
onCorsMessage = waitForMessage(
hud,
`CORS preflight response did not succeed`
`(Reason: CORS preflight response did not succeed). Status code: `
);
makeFaultyCorsCall("CORSPreflightDidNotSucceed");
message = await onCorsMessage;
await checkCorsMessage(message, "CORSPreflightDidNotSucceed");
info("Test CORS did not succeed");
onCorsMessage = waitForMessage(hud, "Reason: CORS request did not succeed");
onCorsMessage = waitForMessage(
hud,
"(Reason: CORS request did not succeed). Status code: "
);
makeFaultyCorsCall("CORSDidNotSucceed");
message = await onCorsMessage;
await checkCorsMessage(message, "CORSDidNotSucceed");
@ -67,7 +70,9 @@ add_task(async function() {
info("Test CORSMissingAllowOrigin");
onCorsMessage = waitForMessage(
hud,
`Reason: CORS header ${quote("Access-Control-Allow-Origin")} missing`
`(Reason: CORS header ${quote(
"Access-Control-Allow-Origin"
)} missing). Status code: `
);
makeFaultyCorsCall("CORSMissingAllowOrigin");
message = await onCorsMessage;

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

@ -145,17 +145,17 @@ const corsParams =
"?utm_source=devtools&utm_medium=firefox-cors-errors&utm_campaign=default";
const CorsErrorDocs = {
CORSDisabled: "CORSDisabled",
CORSDidNotSucceed: "CORSDidNotSucceed",
CORSDidNotSucceed2: "CORSDidNotSucceed",
CORSOriginHeaderNotAdded: "CORSOriginHeaderNotAdded",
CORSExternalRedirectNotAllowed: "CORSExternalRedirectNotAllowed",
CORSRequestNotHttp: "CORSRequestNotHttp",
CORSMissingAllowOrigin: "CORSMissingAllowOrigin",
CORSMissingAllowOrigin2: "CORSMissingAllowOrigin",
CORSMultipleAllowOriginNotAllowed: "CORSMultipleAllowOriginNotAllowed",
CORSAllowOriginNotMatchingOrigin: "CORSAllowOriginNotMatchingOrigin",
CORSNotSupportingCredentials: "CORSNotSupportingCredentials",
CORSMethodNotFound: "CORSMethodNotFound",
CORSMissingAllowCredentials: "CORSMissingAllowCredentials",
CORSPreflightDidNotSucceed2: "CORSPreflightDidNotSucceed",
CORSPreflightDidNotSucceed3: "CORSPreflightDidNotSucceed",
CORSInvalidAllowMethod: "CORSInvalidAllowMethod",
CORSInvalidAllowHeader: "CORSInvalidAllowHeader",
CORSMissingAllowHeaderFromPreflight2: "CORSMissingAllowHeaderFromPreflight",

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

@ -256,6 +256,7 @@
#include "nsCSSPropertyID.h"
#include "nsCSSProps.h"
#include "nsCSSPseudoElements.h"
#include "nsCSSRendering.h"
#include "nsCanvasFrame.h"
#include "nsCaseTreatment.h"
#include "nsCharsetSource.h"
@ -396,6 +397,7 @@
#include "nsSerializationHelper.h"
#include "nsServiceManagerUtils.h"
#include "nsStringFlags.h"
#include "nsStyleUtil.h"
#include "nsStringIterator.h"
#include "nsStyleSheetService.h"
#include "nsStyleStruct.h"
@ -14452,6 +14454,88 @@ Element* Document::TopLayerPop(FunctionRef<bool(Element*)> aPredicateFunc) {
return removedElement;
}
void Document::GetWireframe(bool aIncludeNodes,
Nullable<Wireframe>& aWireframe) {
using FrameForPointOptions = nsLayoutUtils::FrameForPointOptions;
using FrameForPointOption = nsLayoutUtils::FrameForPointOption;
FlushPendingNotifications(FlushType::Layout);
PresShell* shell = GetPresShell();
if (!shell) {
return;
}
nsPresContext* pc = shell->GetPresContext();
if (!pc) {
return;
}
auto& wireframe = aWireframe.SetValue();
nsStyleUtil::GetSerializedColorValue(shell->GetCanvasBackground(),
wireframe.mCanvasBackground.Construct());
FrameForPointOptions options;
options.mBits += FrameForPointOption::IgnoreCrossDoc;
options.mBits += FrameForPointOption::IgnorePaintSuppression;
options.mBits += FrameForPointOption::OnlyVisible;
AutoTArray<nsIFrame*, 32> frames;
const RelativeTo relativeTo{shell->GetRootFrame(),
mozilla::ViewportType::Layout};
nsLayoutUtils::GetFramesForArea(relativeTo, pc->GetVisibleArea(), frames,
options);
// TODO(emilio): We could rewrite hit testing to return nsDisplayItem*s or
// something perhaps, but seems hard / like it'd involve at least some extra
// copying around, since they don't outlive GetFramesForArea.
auto& rects = wireframe.mRects.Construct();
if (!rects.SetCapacity(frames.Length(), fallible)) {
return;
}
for (nsIFrame* frame : frames) {
// Can't really fail because SetCapacity succeeded.
auto& taggedRect = *rects.AppendElement(fallible);
const auto r =
CSSRect::FromAppUnits(nsLayoutUtils::TransformFrameRectToAncestor(
frame, frame->GetRectRelativeToSelf(), relativeTo));
if (aIncludeNodes) {
if (nsIContent* c = frame->GetContent()) {
taggedRect.mNode.Construct(c);
}
}
taggedRect.mRect.Construct(MakeRefPtr<DOMRectReadOnly>(
ToSupports(this), r.x, r.y, r.width, r.height));
taggedRect.mType.Construct() = [&] {
if (frame->IsTextFrame()) {
nsStyleUtil::GetSerializedColorValue(
frame->StyleText()->mWebkitTextFillColor.CalcColor(frame),
taggedRect.mColor.Construct());
return WireframeRectType::Text;
}
if (frame->IsImageFrame() || frame->IsSVGOuterSVGFrame()) {
return WireframeRectType::Image;
}
if (frame->IsThemed()) {
return WireframeRectType::Background;
}
bool drawImage = false;
bool drawColor = false;
const nscolor color = nsCSSRendering::DetermineBackgroundColor(
pc, frame->Style(), frame, drawImage, drawColor);
if (drawImage &&
!frame->StyleBackground()->mImage.BottomLayer().mImage.IsNone()) {
return WireframeRectType::Image;
}
if (drawColor) {
nsStyleUtil::GetSerializedColorValue(color,
taggedRect.mColor.Construct());
return WireframeRectType::Background;
}
return WireframeRectType::Unknown;
}();
}
}
Element* Document::GetTopLayerTop() {
if (mTopLayer.IsEmpty()) {
return nullptr;

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

@ -3402,6 +3402,9 @@ class Document : public nsINode,
return !GetFullscreenError(aCallerType);
}
MOZ_CAN_RUN_SCRIPT void GetWireframe(bool aIncludeNodes,
Nullable<Wireframe>&);
Element* GetTopLayerTop();
// Return the fullscreen element in the top layer
Element* GetUnretargetedFullScreenElement() const;

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

@ -9,6 +9,7 @@
*/
#include "mozilla/dom/Selection.h"
#include "mozilla/intl/Bidi.h"
#include "mozilla/AccessibleCaretEventHub.h"
#include "mozilla/AsyncEventDispatcher.h"
@ -385,7 +386,9 @@ Nullable<int16_t> Selection::GetCaretBidiLevel(
aRv.Throw(NS_ERROR_NOT_INITIALIZED);
return Nullable<int16_t>();
}
nsBidiLevel caretBidiLevel = mFrameSelection->GetCaretBidiLevel();
mozilla::intl::Bidi::EmbeddingLevel caretBidiLevel =
static_cast<mozilla::intl::Bidi::EmbeddingLevel>(
mFrameSelection->GetCaretBidiLevel());
return (caretBidiLevel & BIDI_LEVEL_UNDEFINED)
? Nullable<int16_t>()
: Nullable<int16_t>(caretBidiLevel);
@ -403,7 +406,7 @@ void Selection::SetCaretBidiLevel(const Nullable<int16_t>& aCaretBidiLevel,
mFrameSelection->UndefineCaretBidiLevel();
} else {
mFrameSelection->SetCaretBidiLevelAndMaybeSchedulePaint(
aCaretBidiLevel.Value());
mozilla::intl::Bidi::EmbeddingLevel(aCaretBidiLevel.Value()));
}
}
@ -1357,7 +1360,8 @@ nsIFrame* Selection::GetPrimaryOrCaretFrameForNodeOffset(nsIContent* aContent,
CaretAssociationHint hint = mFrameSelection->GetHint();
if (aVisual) {
nsBidiLevel caretBidiLevel = mFrameSelection->GetCaretBidiLevel();
mozilla::intl::Bidi::EmbeddingLevel caretBidiLevel =
mFrameSelection->GetCaretBidiLevel();
return nsCaret::GetCaretFrameForNodeOffset(
mFrameSelection, aContent, aOffset, hint, caretBidiLevel,
@ -3298,9 +3302,10 @@ void Selection::Modify(const nsAString& aAlter, const nsAString& aDirection,
// If the paragraph direction of the focused frame is right-to-left,
// we may have to swap the direction of movement.
if (nsIFrame* frame = GetPrimaryFrameForFocusNode(visual)) {
nsBidiDirection paraDir = nsBidiPresUtils::ParagraphDirection(frame);
mozilla::intl::Bidi::Direction paraDir =
nsBidiPresUtils::ParagraphDirection(frame);
if (paraDir == NSBIDI_RTL && visual) {
if (paraDir == mozilla::intl::Bidi::Direction::RTL && visual) {
if (amount == eSelectBeginLine) {
amount = eSelectEndLine;
forward = !forward;
@ -3474,7 +3479,9 @@ nsresult Selection::SelectionLanguageChange(bool aLangRTL) {
RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
// if the direction of the language hasn't changed, nothing to do
nsBidiLevel kbdBidiLevel = aLangRTL ? NSBIDI_RTL : NSBIDI_LTR;
mozilla::intl::Bidi::EmbeddingLevel kbdBidiLevel =
aLangRTL ? mozilla::intl::Bidi::EmbeddingLevel::RTL()
: mozilla::intl::Bidi::EmbeddingLevel::LTR();
if (kbdBidiLevel == frameSelection->mKbdBidiLevel) {
return NS_OK;
}
@ -3488,12 +3495,12 @@ nsresult Selection::SelectionLanguageChange(bool aLangRTL) {
auto [frameStart, frameEnd] = focusFrame->GetOffsets();
RefPtr<nsPresContext> context = GetPresContext();
nsBidiLevel levelBefore, levelAfter;
mozilla::intl::Bidi::EmbeddingLevel levelBefore, levelAfter;
if (!context) {
return NS_ERROR_FAILURE;
}
nsBidiLevel level = focusFrame->GetEmbeddingLevel();
mozilla::intl::Bidi::EmbeddingLevel level = focusFrame->GetEmbeddingLevel();
int32_t focusOffset = static_cast<int32_t>(FocusOffset());
if ((focusOffset != frameStart) && (focusOffset != frameEnd))
// the cursor is not at a frame boundary, so the level of both the
@ -3511,26 +3518,30 @@ nsresult Selection::SelectionLanguageChange(bool aLangRTL) {
levelAfter = levels.mLevelAfter;
}
if (IS_SAME_DIRECTION(levelBefore, levelAfter)) {
if (levelBefore.IsSameDirection(levelAfter)) {
// if cursor is between two characters with the same orientation, changing
// the keyboard language must toggle the cursor level between the level of
// the character with the lowest level (if the new language corresponds to
// the orientation of that character) and this level plus 1 (if the new
// language corresponds to the opposite orientation)
if ((level != levelBefore) && (level != levelAfter))
if ((level != levelBefore) && (level != levelAfter)) {
level = std::min(levelBefore, levelAfter);
if (IS_SAME_DIRECTION(level, kbdBidiLevel))
}
if (level.IsSameDirection(kbdBidiLevel)) {
frameSelection->SetCaretBidiLevelAndMaybeSchedulePaint(level);
else
frameSelection->SetCaretBidiLevelAndMaybeSchedulePaint(level + 1);
} else {
frameSelection->SetCaretBidiLevelAndMaybeSchedulePaint(
mozilla::intl::Bidi::EmbeddingLevel(level + 1));
}
} else {
// if cursor is between characters with opposite orientations, changing the
// keyboard language must change the cursor level to that of the adjacent
// character with the orientation corresponding to the new language.
if (IS_SAME_DIRECTION(levelBefore, kbdBidiLevel))
if (levelBefore.IsSameDirection(kbdBidiLevel)) {
frameSelection->SetCaretBidiLevelAndMaybeSchedulePaint(levelBefore);
else
} else {
frameSelection->SetCaretBidiLevelAndMaybeSchedulePaint(levelAfter);
}
}
// The caret might have moved, so invalidate the desired position

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

@ -38,18 +38,18 @@ var tests = {
font : {
uri_test : "font_bad",
result : null,
category: "CORSMissingAllowOrigin",
category: "CORSMissingAllowOrigin2",
},
shape_outside : {
uri_test : "bad_shape_outside",
result : null,
category: "CORSMissingAllowOrigin",
category: "CORSMissingAllowOrigin2",
ignore_windowID: true,
},
mask_image : {
uri_test : "bad_mask_image",
result : null,
category: "CORSMissingAllowOrigin",
category: "CORSMissingAllowOrigin2",
ignore_windowID: true,
},
}

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

@ -12,6 +12,7 @@
#include "nsContentUtils.h"
#include "mozilla/intl/Bidi.h"
#include "mozilla/PresShell.h"
#include "mozilla/PresShellInlines.h"
#include "mozilla/SVGImageContext.h"
@ -3503,11 +3504,11 @@ struct MOZ_STACK_CLASS CanvasBidiProcessor
using ContextState = CanvasRenderingContext2D::ContextState;
virtual void SetText(const char16_t* aText, int32_t aLength,
nsBidiDirection aDirection) override {
mozilla::intl::Bidi::Direction aDirection) override {
mFontgrp->UpdateUserFonts(); // ensure user font generation is current
// adjust flags for current direction run
gfx::ShapedTextFlags flags = mTextRunFlags;
if (aDirection == NSBIDI_RTL) {
if (aDirection == mozilla::intl::Bidi::Direction::RTL) {
flags |= gfx::ShapedTextFlags::TEXT_IS_RTL;
} else {
flags &= ~gfx::ShapedTextFlags::TEXT_IS_RTL;
@ -3873,7 +3874,9 @@ TextMetrics* CanvasRenderingContext2D::DrawOrMeasureText(
// calls bidi algo twice since it needs the full text width and the
// bounding boxes before rendering anything
aError = nsBidiPresUtils::ProcessText(
textToDraw.get(), textToDraw.Length(), isRTL ? NSBIDI_RTL : NSBIDI_LTR,
textToDraw.get(), textToDraw.Length(),
isRTL ? mozilla::intl::Bidi::EmbeddingLevel::RTL()
: mozilla::intl::Bidi::EmbeddingLevel::LTR(),
presShell->GetPresContext(), processor, nsBidiPresUtils::MODE_MEASURE,
nullptr, 0, &totalWidthCoord, &mBidiEngine);
if (aError.Failed()) {
@ -4014,7 +4017,9 @@ TextMetrics* CanvasRenderingContext2D::DrawOrMeasureText(
processor.mDoMeasureBoundingBox = false;
aError = nsBidiPresUtils::ProcessText(
textToDraw.get(), textToDraw.Length(), isRTL ? NSBIDI_RTL : NSBIDI_LTR,
textToDraw.get(), textToDraw.Length(),
isRTL ? mozilla::intl::Bidi::EmbeddingLevel::RTL()
: mozilla::intl::Bidi::EmbeddingLevel::LTR(),
presShell->GetPresContext(), processor, nsBidiPresUtils::MODE_DRAW,
nullptr, 0, nullptr, &mBidiEngine);

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

@ -9,6 +9,7 @@
#include "mozilla/dom/BasicRenderingContext2D.h"
#include "mozilla/dom/CanvasRenderingContext2DBinding.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/intl/Bidi.h"
#include "mozilla/gfx/Rect.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/Attributes.h"
@ -19,8 +20,8 @@
#include "FilterDescription.h"
#include "gfx2DGlue.h"
#include "nsICanvasRenderingContextInternal.h"
#include "nsBidi.h"
#include "nsColor.h"
#include "nsIFrame.h"
class gfxFontGroup;
class nsGlobalWindowInner;
@ -797,7 +798,7 @@ class CanvasRenderingContext2D final : public nsICanvasRenderingContextInternal,
nsTArray<RegionInfo> mHitRegionsOptions;
nsBidi mBidiEngine;
mozilla::intl::Bidi mBidiEngine;
/**
* Returns true if a shadow should be drawn along with a

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

@ -67,6 +67,7 @@ dictionary DnsCacheEntry {
double expiration = 0;
boolean trr = false;
DOMString originAttributesSuffix = "";
DOMString flags = "";
};
[GenerateConversionToJS]

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

@ -10,17 +10,17 @@ BlockMixedActiveContent = Blocked loading mixed active content “%1$S”
# CORS
# LOCALIZATION NOTE: Do not translate "Access-Control-Allow-Origin", Access-Control-Allow-Credentials, Access-Control-Allow-Methods, Access-Control-Allow-Headers
CORSDisabled=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS disabled).
CORSDidNotSucceed=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS request did not succeed).
CORSDidNotSucceed2=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS request did not succeed). Status code: %2$S.
CORSOriginHeaderNotAdded=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS header Origin cannot be added).
CORSExternalRedirectNotAllowed=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS request external redirect not allowed).
CORSRequestNotHttp=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS request not http).
CORSMissingAllowOrigin=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS header Access-Control-Allow-Origin missing).
CORSMissingAllowOrigin2=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS header Access-Control-Allow-Origin missing). Status code: %2$S.
CORSMultipleAllowOriginNotAllowed=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: Multiple CORS header Access-Control-Allow-Origin not allowed).
CORSAllowOriginNotMatchingOrigin=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS header Access-Control-Allow-Origin does not match %2$S).
CORSNotSupportingCredentials=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: Credential is not supported if the CORS header Access-Control-Allow-Origin is *).
CORSMethodNotFound=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: Did not find method in CORS header Access-Control-Allow-Methods).
CORSMissingAllowCredentials=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: expected true in CORS header Access-Control-Allow-Credentials).
CORSPreflightDidNotSucceed2=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS preflight response did not succeed).
CORSPreflightDidNotSucceed3=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS preflight response did not succeed). Status code: %2$S.
CORSInvalidAllowMethod=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: invalid token %2$S in CORS header Access-Control-Allow-Methods).
CORSInvalidAllowHeader=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: invalid token %2$S in CORS header Access-Control-Allow-Headers).
CORSMissingAllowHeaderFromPreflight2=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: header %2$S is not allowed according to header Access-Control-Allow-Headers from CORS preflight response).

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

@ -463,7 +463,7 @@ class VideoData : public MediaData {
const media::TimeUnit& aTime, const media::TimeUnit& aDuration,
const YCbCrBuffer& aBuffer, bool aKeyframe,
const media::TimeUnit& aTimecode, const IntRect& aPicture,
layers::KnowsCompositor* aAllocator = nullptr);
layers::KnowsCompositor* aAllocator);
static already_AddRefed<VideoData> CreateAndCopyData(
const VideoInfo& aInfo, ImageContainer* aContainer, int64_t aOffset,

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

@ -970,7 +970,7 @@ already_AddRefed<VideoData> ChromiumCDMParent::CreateVideoFrame(
mVideoInfo, mImageContainer, mLastStreamOffset,
media::TimeUnit::FromMicroseconds(aFrame.mTimestamp()),
media::TimeUnit::FromMicroseconds(aFrame.mDuration()), b, false,
media::TimeUnit::FromMicroseconds(-1), pictureRegion);
media::TimeUnit::FromMicroseconds(-1), pictureRegion, mKnowsCompositor);
return v.forget();
}
@ -1037,7 +1037,8 @@ void ChromiumCDMParent::ActorDestroy(ActorDestroyReason aWhy) {
RefPtr<MediaDataDecoder::InitPromise> ChromiumCDMParent::InitializeVideoDecoder(
const gmp::CDMVideoDecoderConfig& aConfig, const VideoInfo& aInfo,
RefPtr<layers::ImageContainer> aImageContainer) {
RefPtr<layers::ImageContainer> aImageContainer,
RefPtr<layers::KnowsCompositor> aKnowsCompositor) {
MOZ_ASSERT(mGMPThread->IsOnCurrentThread());
if (mIsShutdown) {
return MediaDataDecoder::InitPromise::CreateAndReject(
@ -1084,6 +1085,7 @@ RefPtr<MediaDataDecoder::InitPromise> ChromiumCDMParent::InitializeVideoDecoder(
mVideoDecoderInitialized = true;
mImageContainer = aImageContainer;
mKnowsCompositor = aKnowsCompositor;
mVideoInfo = aInfo;
mVideoFrameBufferSize = bufferSize;

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

@ -88,7 +88,8 @@ class ChromiumCDMParent final : public PChromiumCDMParent,
// a Close() function.
RefPtr<MediaDataDecoder::InitPromise> InitializeVideoDecoder(
const gmp::CDMVideoDecoderConfig& aConfig, const VideoInfo& aInfo,
RefPtr<layers::ImageContainer> aImageContainer);
RefPtr<layers::ImageContainer> aImageContainer,
RefPtr<layers::KnowsCompositor> aKnowsCompositor);
RefPtr<MediaDataDecoder::DecodePromise> DecryptAndDecodeFrame(
MediaRawData* aSample);
@ -181,6 +182,7 @@ class ChromiumCDMParent final : public PChromiumCDMParent,
MozPromiseHolder<MediaDataDecoder::DecodePromise> mDecodePromise;
RefPtr<layers::ImageContainer> mImageContainer;
RefPtr<layers::KnowsCompositor> mKnowsCompositor;
VideoInfo mVideoInfo;
uint64_t mLastStreamOffset = 0;

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

@ -211,10 +211,10 @@ RefPtr<MediaDataDecoder::DecodePromise> AOMDecoder::ProcessDecode(
: ColorRange::LIMITED;
RefPtr<VideoData> v;
v = VideoData::CreateAndCopyData(mInfo, mImageContainer, aSample->mOffset,
aSample->mTime, aSample->mDuration, b,
aSample->mKeyframe, aSample->mTimecode,
mInfo.ScaledImageRect(img->d_w, img->d_h));
v = VideoData::CreateAndCopyData(
mInfo, mImageContainer, aSample->mOffset, aSample->mTime,
aSample->mDuration, b, aSample->mKeyframe, aSample->mTimecode,
mInfo.ScaledImageRect(img->d_w, img->d_h), nullptr);
if (!v) {
LOG("Image allocation error source %ux%u display %ux%u picture %ux%u",

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

@ -69,9 +69,10 @@ already_AddRefed<MediaData> BlankVideoDataCreator::Create(
buffer.mYUVColorSpace = gfx::YUVColorSpace::BT601;
return VideoData::CreateAndCopyData(
mInfo, mImageContainer, aSample->mOffset, aSample->mTime,
aSample->mDuration, buffer, aSample->mKeyframe, aSample->mTime, mPicture);
return VideoData::CreateAndCopyData(mInfo, mImageContainer, aSample->mOffset,
aSample->mTime, aSample->mDuration,
buffer, aSample->mKeyframe,
aSample->mTime, mPicture, nullptr);
}
BlankAudioDataCreator::BlankAudioDataCreator(uint32_t aChannelCount,

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

@ -20,7 +20,8 @@ ChromiumCDMVideoDecoder::ChromiumCDMVideoDecoder(
mConfig(aParams.mConfig),
mCrashHelper(aParams.mCrashHelper),
mGMPThread(GetGMPThread()),
mImageContainer(aParams.mImageContainer) {}
mImageContainer(aParams.mImageContainer),
mKnowsCompositor(aParams.mKnowsCompositor) {}
ChromiumCDMVideoDecoder::~ChromiumCDMVideoDecoder() = default;
@ -89,10 +90,12 @@ RefPtr<MediaDataDecoder::InitPromise> ChromiumCDMVideoDecoder::Init() {
RefPtr<gmp::ChromiumCDMParent> cdm = mCDMParent;
VideoInfo info = mConfig;
RefPtr<layers::ImageContainer> imageContainer = mImageContainer;
return InvokeAsync(
mGMPThread, __func__, [cdm, config, info, imageContainer]() {
return cdm->InitializeVideoDecoder(config, info, imageContainer);
});
RefPtr<layers::KnowsCompositor> knowsCompositor = mKnowsCompositor;
return InvokeAsync(mGMPThread, __func__,
[cdm, config, info, imageContainer, knowsCompositor]() {
return cdm->InitializeVideoDecoder(
config, info, imageContainer, knowsCompositor);
});
}
nsCString ChromiumCDMVideoDecoder::GetDescriptionName() const {

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

@ -9,6 +9,7 @@
#include "ChromiumCDMParent.h"
#include "PlatformDecoderModule.h"
#include "mozilla/layers/KnowsCompositor.h"
namespace mozilla {
@ -40,6 +41,7 @@ class ChromiumCDMVideoDecoder
RefPtr<GMPCrashHelper> mCrashHelper;
nsCOMPtr<nsISerialEventTarget> mGMPThread;
RefPtr<layers::ImageContainer> mImageContainer;
RefPtr<layers::KnowsCompositor> mKnowsCompositor;
MozPromiseHolder<InitPromise> mInitPromise;
bool mConvertToAnnexB = false;
};

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

@ -34,7 +34,8 @@ static bool IsOnGMPThread() {
GMPVideoDecoderParams::GMPVideoDecoderParams(const CreateDecoderParams& aParams)
: mConfig(aParams.VideoConfig()),
mImageContainer(aParams.mImageContainer),
mCrashHelper(aParams.mCrashHelper) {}
mCrashHelper(aParams.mCrashHelper),
mKnowsCompositor(aParams.mKnowsCompositor) {}
void GMPVideoDecoder::Decoded(GMPVideoi420Frame* aDecodedFrame) {
GMPUniquePtr<GMPVideoi420Frame> decodedFrame(aDecodedFrame);
@ -64,7 +65,7 @@ void GMPVideoDecoder::Decoded(GMPVideoi420Frame* aDecodedFrame) {
mConfig, mImageContainer, mLastStreamOffset,
media::TimeUnit::FromMicroseconds(decodedFrame->Timestamp()),
media::TimeUnit::FromMicroseconds(decodedFrame->Duration()), b, false,
media::TimeUnit::FromMicroseconds(-1), pictureRegion);
media::TimeUnit::FromMicroseconds(-1), pictureRegion, mKnowsCompositor);
RefPtr<GMPVideoDecoder> self = this;
if (v) {
mDecodedData.AppendElement(std::move(v));
@ -123,7 +124,8 @@ GMPVideoDecoder::GMPVideoDecoder(const GMPVideoDecoderParams& aParams)
mHost(nullptr),
mConvertNALUnitLengths(false),
mCrashHelper(aParams.mCrashHelper),
mImageContainer(aParams.mImageContainer) {}
mImageContainer(aParams.mImageContainer),
mKnowsCompositor(aParams.mKnowsCompositor) {}
void GMPVideoDecoder::InitTags(nsTArray<nsCString>& aTags) {
if (MP4Decoder::IsH264(mConfig.mMimeType)) {

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

@ -4,6 +4,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/layers/KnowsCompositor.h"
#if !defined(GMPVideoDecoder_h_)
# define GMPVideoDecoder_h_
@ -22,6 +23,7 @@ struct MOZ_STACK_CLASS GMPVideoDecoderParams {
const VideoInfo& mConfig;
layers::ImageContainer* mImageContainer;
GMPCrashHelper* mCrashHelper;
layers::KnowsCompositor* mKnowsCompositor;
};
DDLoggedTypeDeclNameAndBase(GMPVideoDecoder, MediaDataDecoder);
@ -87,6 +89,7 @@ class GMPVideoDecoder : public MediaDataDecoder,
int64_t mLastStreamOffset = 0;
RefPtr<layers::ImageContainer> mImageContainer;
RefPtr<layers::KnowsCompositor> mKnowsCompositor;
MozPromiseHolder<DecodePromise> mDecodePromise;
MozPromiseHolder<DecodePromise> mDrainPromise;

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

@ -952,7 +952,7 @@ already_AddRefed<VideoData> MediaDataHelper::CreateYUV420VideoData(
media::TimeUnit::FromMicroseconds(1), // We don't know the duration.
b,
0, // Filled later by caller.
media::TimeUnit::FromMicroseconds(-1), info.ImageRect());
media::TimeUnit::FromMicroseconds(-1), info.ImageRect(), nullptr);
MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug,
("YUV420 VideoData: disp width %d, height %d, pic width %d, height "

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

@ -48,8 +48,12 @@ already_AddRefed<XRViewerPose> XRFrame::GetViewerPose(
return nullptr;
}
// TODO (Bug 1616390) - Validate that poses may be reported:
// https://immersive-web.github.io/webxr/#poses-may-be-reported
if (!mSession->CanReportPoses()) {
aRv.ThrowSecurityError(
"The visibilityState of the XRSpace's XRSession "
"that is passed to GetViewerPose must be 'visible'.");
return nullptr;
}
// TODO (Bug 1616393) - Check if poses must be limited:
// https://immersive-web.github.io/webxr/#poses-must-be-limited
@ -145,11 +149,10 @@ already_AddRefed<XRPose> XRFrame::GetPose(const XRSpace& aSpace,
return nullptr;
}
// TODO (Bug 1616390) - Validate that poses may be reported:
// https://immersive-web.github.io/webxr/#poses-may-be-reported
if (aSpace.GetSession()->VisibilityState() != XRVisibilityState::Visible) {
aRv.ThrowInvalidStateError(
"An XRSpace s visibilityState in not 'visible'.");
if (!mSession->CanReportPoses()) {
aRv.ThrowSecurityError(
"The visibilityState of the XRSpace's XRSession "
"that is passed to GetPose must be 'visible'.");
return nullptr;
}

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

@ -175,11 +175,19 @@ bool XRSession::IsImmersive() const {
return mDisplayClient != nullptr;
}
XRVisibilityState XRSession::VisibilityState() {
XRVisibilityState XRSession::VisibilityState() const {
return XRVisibilityState::Visible;
// TODO (Bug 1609771): Implement changing visibility state
}
// https://immersive-web.github.io/webxr/#poses-may-be-reported
// Given that an XRSession cannot be requested without explicit consent
// by the user, the only necessary check is whether the XRSession's
// visiblityState is 'visible'.
bool XRSession::CanReportPoses() const {
return VisibilityState() == XRVisibilityState::Visible;
}
// https://immersive-web.github.io/webxr/#dom-xrsession-updaterenderstate
void XRSession::UpdateRenderState(const XRRenderStateInit& aNewState,
ErrorResult& aRv) {

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

@ -64,7 +64,7 @@ class XRSession final : public DOMEventTargetHelper, public nsARefreshObserver {
JS::Handle<JSObject*> aGivenProto) override;
// WebIDL Attributes
XRVisibilityState VisibilityState();
XRVisibilityState VisibilityState() const;
XRRenderState* RenderState();
XRInputSourceArray* InputSources();
@ -98,6 +98,7 @@ class XRSession final : public DOMEventTargetHelper, public nsARefreshObserver {
void ExitPresent();
RefPtr<XRViewerPose> PooledViewerPose(const gfx::Matrix4x4Double& aTransform,
bool aEmulatedPosition);
bool CanReportPoses() const;
// nsARefreshObserver
MOZ_CAN_RUN_SCRIPT

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

@ -716,3 +716,25 @@ partial interface Document {
[ChromeOnly]
readonly attribute boolean isInitialDocument;
};
// Extension to allow chrome code to get some wireframe-like structure.
enum WireframeRectType {
"image",
"background",
"text",
"unknown",
};
dictionary WireframeTaggedRect {
DOMRectReadOnly rect;
DOMString? color; /* Only relevant for "background" rects */
WireframeRectType type;
Node? node;
};
dictionary Wireframe {
DOMString canvasBackground;
sequence<WireframeTaggedRect> rects;
};
partial interface Document {
[ChromeOnly]
Wireframe? getWireframe(optional boolean aIncludeNodes = false);
};

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

@ -1578,11 +1578,10 @@ nsXULPrototypeScript::nsXULPrototypeScript(uint32_t aLineNo)
mStencil(nullptr) {}
static nsresult WriteStencil(nsIObjectOutputStream* aStream, JSContext* aCx,
const JS::ReadOnlyCompileOptions& aOptions,
JS::Stencil* aStencil) {
JS::TranscodeBuffer buffer;
JS::TranscodeResult code;
code = JS::EncodeStencil(aCx, aOptions, aStencil, buffer);
code = JS::EncodeStencil(aCx, aStencil, buffer);
if (code != JS::TranscodeResult::Ok) {
if (code == JS::TranscodeResult::Throw) {
@ -1689,10 +1688,7 @@ nsresult nsXULPrototypeScript::Serialize(
JSContext* cx = jsapi.cx();
MOZ_ASSERT(xpc::CompilationScope() == JS::CurrentGlobalOrNull(cx));
JS::CompileOptions options(cx);
FillCompileOptions(options);
return WriteStencil(aStream, cx, options, mStencil);
return WriteStencil(aStream, cx, mStencil);
}
nsresult nsXULPrototypeScript::SerializeOutOfLine(

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

@ -10,22 +10,23 @@
#include <stdio.h> // for nullptr, stdout
#include <string.h> // for strcmp
#include "ChangeAttributeTransaction.h" // for ChangeAttributeTransaction
#include "CompositionTransaction.h" // for CompositionTransaction
#include "CreateElementTransaction.h" // for CreateElementTransaction
#include "DeleteNodeTransaction.h" // for DeleteNodeTransaction
#include "DeleteRangeTransaction.h" // for DeleteRangeTransaction
#include "DeleteTextTransaction.h" // for DeleteTextTransaction
#include "EditAggregateTransaction.h" // for EditAggregateTransaction
#include "EditTransactionBase.h" // for EditTransactionBase
#include "EditorEventListener.h" // for EditorEventListener
#include "gfxFontUtils.h" // for gfxFontUtils
#include "HTMLEditUtils.h" // for HTMLEditUtils
#include "InsertNodeTransaction.h" // for InsertNodeTransaction
#include "InsertTextTransaction.h" // for InsertTextTransaction
#include "JoinNodeTransaction.h" // for JoinNodeTransaction
#include "PlaceholderTransaction.h" // for PlaceholderTransaction
#include "SplitNodeTransaction.h" // for SplitNodeTransaction
#include "ChangeAttributeTransaction.h" // for ChangeAttributeTransaction
#include "CompositionTransaction.h" // for CompositionTransaction
#include "CreateElementTransaction.h" // for CreateElementTransaction
#include "DeleteNodeTransaction.h" // for DeleteNodeTransaction
#include "DeleteRangeTransaction.h" // for DeleteRangeTransaction
#include "DeleteTextTransaction.h" // for DeleteTextTransaction
#include "EditAggregateTransaction.h" // for EditAggregateTransaction
#include "EditTransactionBase.h" // for EditTransactionBase
#include "EditorEventListener.h" // for EditorEventListener
#include "gfxFontUtils.h" // for gfxFontUtils
#include "HTMLEditUtils.h" // for HTMLEditUtils
#include "InsertNodeTransaction.h" // for InsertNodeTransaction
#include "InsertTextTransaction.h" // for InsertTextTransaction
#include "JoinNodeTransaction.h" // for JoinNodeTransaction
#include "PlaceholderTransaction.h" // for PlaceholderTransaction
#include "SplitNodeTransaction.h" // for SplitNodeTransaction
#include "mozilla/intl/Bidi.h"
#include "mozilla/BasePrincipal.h" // for BasePrincipal
#include "mozilla/CheckedInt.h" // for CheckedInt
#include "mozilla/ComposerCommandsUpdater.h" // for ComposerCommandsUpdater
@ -5765,12 +5766,13 @@ EditorBase::AutoCaretBidiLevelManager::AutoCaretBidiLevelManager(
nsPrevNextBidiLevels levels = frameSelection->GetPrevNextBidiLevels(
aPointAtCaret.GetContainerAsContent(), aPointAtCaret.Offset(), true);
nsBidiLevel levelBefore = levels.mLevelBefore;
nsBidiLevel levelAfter = levels.mLevelAfter;
mozilla::intl::Bidi::EmbeddingLevel levelBefore = levels.mLevelBefore;
mozilla::intl::Bidi::EmbeddingLevel levelAfter = levels.mLevelAfter;
nsBidiLevel currentCaretLevel = frameSelection->GetCaretBidiLevel();
mozilla::intl::Bidi::EmbeddingLevel currentCaretLevel =
frameSelection->GetCaretBidiLevel();
nsBidiLevel levelOfDeletion;
mozilla::intl::Bidi::EmbeddingLevel levelOfDeletion;
levelOfDeletion = (nsIEditor::eNext == aDirectionAndAmount ||
nsIEditor::eNextWord == aDirectionAndAmount)
? levelAfter

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

@ -6,6 +6,7 @@
#ifndef mozilla_EditorBase_h
#define mozilla_EditorBase_h
#include "mozilla/intl/Bidi.h"
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc.
#include "mozilla/EditAction.h" // for EditAction and EditSubAction
#include "mozilla/EditorDOMPoint.h" // for EditorDOMPoint
@ -28,7 +29,6 @@
#include "nsGkAtoms.h"
#include "nsIContentInlines.h" // for nsINode::IsEditable()
#include "nsIEditor.h" // for nsIEditor, etc.
#include "nsIFrame.h" // for nsBidiLevel
#include "nsISelectionController.h" // for nsISelectionController constants
#include "nsISelectionListener.h" // for nsISelectionListener
#include "nsISupportsImpl.h" // for EditorBase::Release, etc.
@ -1985,7 +1985,7 @@ class EditorBase : public nsIEditor,
void MaybeUpdateCaretBidiLevel(const EditorBase& aEditorBase) const;
private:
Maybe<nsBidiLevel> mNewCaretBidiLevel;
Maybe<mozilla::intl::Bidi::EmbeddingLevel> mNewCaretBidiLevel;
bool mFailed = false;
bool mCanceled = false;
};

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

@ -13,6 +13,7 @@
#include <ostream>
#include "mozilla/Mutex.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/gfx/MacIOSurface.h"
#include "mozilla/layers/NativeLayer.h"
@ -119,6 +120,8 @@ class NativeLayerRootCA : public NativeLayerRoot {
bool aIsOpaque) override;
void SetWindowIsFullscreen(bool aFullscreen);
void NoteMouseMove();
void NoteMouseMoveAtTime(const TimeStamp& aTime);
protected:
explicit NativeLayerRootCA(CALayer* aLayer);
@ -129,17 +132,20 @@ class NativeLayerRootCA : public NativeLayerRoot {
~Representation();
void Commit(WhichRepresentation aRepresentation,
const nsTArray<RefPtr<NativeLayerCA>>& aSublayers,
bool aWindowIsFullscreen);
bool aWindowIsFullscreen, bool aMouseMovedRecently);
CALayer* FindVideoLayerToIsolate(
WhichRepresentation aRepresentation,
const nsTArray<RefPtr<NativeLayerCA>>& aSublayers);
CALayer* mRootCALayer = nullptr; // strong
bool mMutated = false;
bool mMutatedLayerStructure = false;
bool mMutatedMouseMovedRecently = false;
};
template <typename F>
void ForAllRepresentations(F aFn);
void UpdateMouseMovedRecently();
Mutex mMutex; // protects all other fields
Representation mOnscreenRepresentation;
Representation mOffscreenRepresentation;
@ -163,6 +169,12 @@ class NativeLayerRootCA : public NativeLayerRoot {
// Updated by the layer's view's window to match the fullscreen state
// of that window.
bool mWindowIsFullscreen = false;
// Updated by the layer's view's window call to NoteMouseMoveAtTime().
TimeStamp mLastMouseMoveTime;
// Has the mouse recently moved?
bool mMouseMovedRecently = false;
};
class RenderSourceNLRS;

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

@ -134,7 +134,9 @@ static CALayer* MakeOffscreenRootCALayer() {
NativeLayerRootCA::NativeLayerRootCA(CALayer* aLayer)
: mMutex("NativeLayerRootCA"),
mOnscreenRepresentation(aLayer),
mOffscreenRepresentation(MakeOffscreenRootCALayer()) {}
mOffscreenRepresentation(MakeOffscreenRootCALayer()) {
NoteMouseMove();
}
NativeLayerRootCA::~NativeLayerRootCA() {
MOZ_RELEASE_ASSERT(mSublayers.IsEmpty(),
@ -162,7 +164,7 @@ void NativeLayerRootCA::AppendLayer(NativeLayer* aLayer) {
mSublayers.AppendElement(layerCA);
layerCA->SetBackingScale(mBackingScale);
layerCA->SetRootWindowIsFullscreen(mWindowIsFullscreen);
ForAllRepresentations([&](Representation& r) { r.mMutated = true; });
ForAllRepresentations([&](Representation& r) { r.mMutatedLayerStructure = true; });
}
void NativeLayerRootCA::RemoveLayer(NativeLayer* aLayer) {
@ -172,7 +174,7 @@ void NativeLayerRootCA::RemoveLayer(NativeLayer* aLayer) {
MOZ_RELEASE_ASSERT(layerCA);
mSublayers.RemoveElement(layerCA);
ForAllRepresentations([&](Representation& r) { r.mMutated = true; });
ForAllRepresentations([&](Representation& r) { r.mMutatedLayerStructure = true; });
}
void NativeLayerRootCA::SetLayers(const nsTArray<RefPtr<NativeLayer>>& aLayers) {
@ -195,7 +197,7 @@ void NativeLayerRootCA::SetLayers(const nsTArray<RefPtr<NativeLayer>>& aLayers)
if (layersCA != mSublayers) {
mSublayers = std::move(layersCA);
ForAllRepresentations([&](Representation& r) { r.mMutated = true; });
ForAllRepresentations([&](Representation& r) { r.mMutatedLayerStructure = true; });
}
}
@ -238,7 +240,9 @@ bool NativeLayerRootCA::CommitToScreen() {
return false;
}
mOnscreenRepresentation.Commit(WhichRepresentation::ONSCREEN, mSublayers, mWindowIsFullscreen);
UpdateMouseMovedRecently();
mOnscreenRepresentation.Commit(WhichRepresentation::ONSCREEN, mSublayers, mWindowIsFullscreen,
mMouseMovedRecently);
mCommitPending = false;
}
@ -286,7 +290,8 @@ void NativeLayerRootCA::OnNativeLayerRootSnapshotterDestroyed(
void NativeLayerRootCA::CommitOffscreen() {
MutexAutoLock lock(mMutex);
mOffscreenRepresentation.Commit(WhichRepresentation::OFFSCREEN, mSublayers, mWindowIsFullscreen);
mOffscreenRepresentation.Commit(WhichRepresentation::OFFSCREEN, mSublayers, mWindowIsFullscreen,
false);
}
template <typename F>
@ -299,7 +304,7 @@ NativeLayerRootCA::Representation::Representation(CALayer* aRootCALayer)
: mRootCALayer([aRootCALayer retain]) {}
NativeLayerRootCA::Representation::~Representation() {
if (mMutated) {
if (mMutatedLayerStructure) {
// Clear the root layer's sublayers. At this point the window is usually closed, so this
// transaction does not cause any screen updates.
AutoCATransaction transaction;
@ -311,8 +316,12 @@ NativeLayerRootCA::Representation::~Representation() {
void NativeLayerRootCA::Representation::Commit(WhichRepresentation aRepresentation,
const nsTArray<RefPtr<NativeLayerCA>>& aSublayers,
bool aWindowIsFullscreen) {
if (!mMutated &&
bool aWindowIsFullscreen, bool aMouseMovedRecently) {
bool mightIsolate = (aRepresentation == WhichRepresentation::ONSCREEN &&
StaticPrefs::gfx_core_animation_specialize_video());
bool mustRebuild = (mightIsolate && mMutatedMouseMovedRecently);
if (!mMutatedLayerStructure && !mustRebuild &&
std::none_of(aSublayers.begin(), aSublayers.end(), [=](const RefPtr<NativeLayerCA>& layer) {
return layer->HasUpdate(aRepresentation);
})) {
@ -329,7 +338,7 @@ void NativeLayerRootCA::Representation::Commit(WhichRepresentation aRepresentati
layer->ApplyChanges(aRepresentation);
}
if (mMutated) {
if (mMutatedLayerStructure || mustRebuild) {
NSMutableArray<CALayer*>* sublayers = [NSMutableArray arrayWithCapacity:aSublayers.Length()];
for (auto layer : aSublayers) {
[sublayers addObject:layer->UnderlyingCALayer(aRepresentation)];
@ -341,8 +350,7 @@ void NativeLayerRootCA::Representation::Commit(WhichRepresentation aRepresentati
// sublayers have been set, because we need the relationships there to do the
// bounds checking of layer spaces against each other.
// Bug 1731821 should eliminate this entire if block.
if (aWindowIsFullscreen && aRepresentation == WhichRepresentation::ONSCREEN &&
StaticPrefs::gfx_core_animation_specialize_video()) {
if (mightIsolate && aWindowIsFullscreen && !aMouseMovedRecently) {
CALayer* isolatedLayer = FindVideoLayerToIsolate(aRepresentation, aSublayers);
if (isolatedLayer) {
// Create a full coverage black layer behind the isolated layer.
@ -360,9 +368,10 @@ void NativeLayerRootCA::Representation::Commit(WhichRepresentation aRepresentati
mRootCALayer.sublayers = @[ blackLayer, isolatedLayer ];
}
}
mMutated = false;
}
mMutatedLayerStructure = false;
mMutatedMouseMovedRecently = false;
}
CALayer* NativeLayerRootCA::Representation::FindVideoLayerToIsolate(
@ -480,11 +489,32 @@ void NativeLayerRootCA::SetWindowIsFullscreen(bool aFullscreen) {
for (auto layer : mSublayers) {
layer->SetRootWindowIsFullscreen(mWindowIsFullscreen);
}
// Treat this as a mouse move, for purposes of resetting our timer.
NoteMouseMove();
PrepareForCommit();
CommitToScreen();
}
}
void NativeLayerRootCA::NoteMouseMove() { mLastMouseMoveTime = TimeStamp::NowLoRes(); }
void NativeLayerRootCA::NoteMouseMoveAtTime(const TimeStamp& aTime) { mLastMouseMoveTime = aTime; }
void NativeLayerRootCA::UpdateMouseMovedRecently() {
static const double SECONDS_TO_WAIT = 2.0;
bool newMouseMovedRecently =
((TimeStamp::NowLoRes() - mLastMouseMoveTime).ToSeconds() < SECONDS_TO_WAIT);
if (newMouseMovedRecently != mMouseMovedRecently) {
mMouseMovedRecently = newMouseMovedRecently;
ForAllRepresentations([&](Representation& r) { r.mMutatedMouseMovedRecently = true; });
}
}
NativeLayerRootSnapshotterCA::NativeLayerRootSnapshotterCA(NativeLayerRootCA* aLayerRoot,
RefPtr<GLContext>&& aGL,
CALayer* aRootCALayer)

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

@ -597,7 +597,6 @@ struct DIGroup {
return;
}
gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
std::vector<RefPtr<ScaledFont>> fonts;
bool validFonts = true;
RefPtr<WebRenderDrawEventRecorder> recorder =
@ -620,8 +619,8 @@ struct DIGroup {
fonts = std::move(aScaledFonts);
});
RefPtr<gfx::DrawTarget> dummyDt = gfx::Factory::CreateDrawTarget(
gfx::BackendType::SKIA, gfx::IntSize(1, 1), format);
RefPtr<gfx::DrawTarget> dummyDt =
gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateRecordingDrawTarget(
recorder, dummyDt, mLayerBounds.ToUnknownRect());
@ -1092,15 +1091,14 @@ static bool IsItemProbablyActive(
Matrix t2d;
bool is2D = t.Is2D(&t2d);
GP("active: %d\n", transformItem->MayBeAnimated(aDisplayListBuilder));
return transformItem->MayBeAnimated(aDisplayListBuilder, false) ||
!is2D ||
return transformItem->MayBeAnimated(aDisplayListBuilder) || !is2D ||
HasActiveChildren(*transformItem->GetChildren(), aBuilder,
aResources, aSc, aManager, aDisplayListBuilder);
}
case DisplayItemType::TYPE_OPACITY: {
nsDisplayOpacity* opacityItem = static_cast<nsDisplayOpacity*>(aItem);
bool active = opacityItem->NeedsActiveLayer(aDisplayListBuilder,
opacityItem->Frame(), false);
opacityItem->Frame());
GP("active: %d\n", active);
return active ||
HasActiveChildren(*opacityItem->GetChildren(), aBuilder,

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

@ -169,8 +169,8 @@ Maybe<BlobImageKeyData> SourceSurfaceBlobImage::RecordDrawing(
fonts = std::move(aScaledFonts);
});
RefPtr<DrawTarget> dummyDt = Factory::CreateDrawTarget(
BackendType::SKIA, IntSize(1, 1), SurfaceFormat::OS_RGBA);
RefPtr<DrawTarget> dummyDt =
gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
RefPtr<DrawTarget> dt =
Factory::CreateRecordingDrawTarget(recorder, dummyDt, imageRectOrigin);

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

@ -0,0 +1,278 @@
/* 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/. */
#include "gtest/gtest.h"
#include "mozilla/intl/Bidi.h"
#include "mozilla/Span.h"
namespace mozilla::intl {
struct VisualRun {
Span<const char16_t> string;
Bidi::Direction direction;
};
/**
* An iterator for visual runs in a paragraph. See Bug 1736597 for integrating
* this into the public API.
*/
class MOZ_STACK_CLASS VisualRunIter {
public:
VisualRunIter(Bidi& aBidi, Span<const char16_t> aParagraph,
Bidi::EmbeddingLevel aLevel)
: mBidi(aBidi), mParagraph(aParagraph) {
// Crash in case of errors by calling unwrap. If this were a real API, this
// would be a TryCreate call.
mBidi.SetParagraph(aParagraph, aLevel).unwrap();
mRunCount = mBidi.CountRuns().unwrap();
}
Maybe<VisualRun> Next() {
if (mRunIndex >= mRunCount) {
return Nothing();
}
int32_t stringIndex = -1;
int32_t stringLength = -1;
Bidi::Direction direction =
mBidi.GetVisualRun(mRunIndex, &stringIndex, &stringLength);
Span<const char16_t> string(mParagraph.Elements() + stringIndex,
stringLength);
mRunIndex++;
return Some(VisualRun{string, direction});
}
private:
Bidi& mBidi;
Span<const char16_t> mParagraph = Span<const char16_t>();
int32_t mRunIndex = 0;
int32_t mRunCount = 0;
};
struct LogicalRun {
Span<const char16_t> string;
Bidi::EmbeddingLevel embeddingLevel;
};
/**
* An iterator for logical runs in a paragraph. See Bug 1736597 for integrating
* this into the public API.
*/
class MOZ_STACK_CLASS LogicalRunIter {
public:
LogicalRunIter(Bidi& aBidi, Span<const char16_t> aParagraph,
Bidi::EmbeddingLevel aLevel)
: mBidi(aBidi), mParagraph(aParagraph) {
// Crash in case of errors by calling unwrap. If this were a real API, this
// would be a TryCreate call.
mBidi.SetParagraph(aParagraph, aLevel).unwrap();
mBidi.CountRuns().unwrap();
}
Maybe<LogicalRun> Next() {
if (mRunIndex >= static_cast<int32_t>(mParagraph.Length())) {
return Nothing();
}
int32_t logicalLimit;
Bidi::EmbeddingLevel embeddingLevel;
mBidi.GetLogicalRun(mRunIndex, &logicalLimit, &embeddingLevel);
Span<const char16_t> string(mParagraph.Elements() + mRunIndex,
logicalLimit - mRunIndex);
mRunIndex = logicalLimit;
return Some(LogicalRun{string, embeddingLevel});
}
private:
Bidi& mBidi;
Span<const char16_t> mParagraph = Span<const char16_t>();
int32_t mRunIndex = 0;
};
TEST(IntlBidi, SimpleLTR)
{
Bidi bidi{};
LogicalRunIter logicalRunIter(bidi, MakeStringSpan(u"this is a paragraph"),
Bidi::EmbeddingLevel::DefaultLTR());
ASSERT_EQ(bidi.GetParagraphEmbeddingLevel(), 0);
ASSERT_EQ(bidi.GetParagraphDirection(), Bidi::ParagraphDirection::LTR);
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isSome());
ASSERT_EQ(logicalRun->string, MakeStringSpan(u"this is a paragraph"));
ASSERT_EQ(logicalRun->embeddingLevel, 0);
ASSERT_EQ(logicalRun->embeddingLevel.Direction(), Bidi::Direction::LTR);
}
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isNothing());
}
}
TEST(IntlBidi, SimpleRTL)
{
Bidi bidi{};
LogicalRunIter logicalRunIter(bidi, MakeStringSpan(u"فايرفوكس رائع"),
Bidi::EmbeddingLevel::DefaultLTR());
ASSERT_EQ(bidi.GetParagraphEmbeddingLevel(), 1);
ASSERT_EQ(bidi.GetParagraphDirection(), Bidi::ParagraphDirection::RTL);
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isSome());
ASSERT_EQ(logicalRun->string, MakeStringSpan(u"فايرفوكس رائع"));
ASSERT_EQ(logicalRun->embeddingLevel.Direction(), Bidi::Direction::RTL);
ASSERT_EQ(logicalRun->embeddingLevel, 1);
}
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isNothing());
}
}
TEST(IntlBidi, MultiLevel)
{
Bidi bidi{};
LogicalRunIter logicalRunIter(
bidi, MakeStringSpan(u"Firefox is awesome: رائع Firefox"),
Bidi::EmbeddingLevel::DefaultLTR());
ASSERT_EQ(bidi.GetParagraphEmbeddingLevel(), 0);
ASSERT_EQ(bidi.GetParagraphDirection(), Bidi::ParagraphDirection::Mixed);
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isSome());
ASSERT_EQ(logicalRun->string, MakeStringSpan(u"Firefox is awesome: "));
ASSERT_EQ(logicalRun->embeddingLevel, 0);
}
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isSome());
ASSERT_EQ(logicalRun->string, MakeStringSpan(u"رائع"));
ASSERT_EQ(logicalRun->embeddingLevel, 1);
}
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isSome());
ASSERT_EQ(logicalRun->string, MakeStringSpan(u" Firefox"));
ASSERT_EQ(logicalRun->embeddingLevel, 0);
}
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isNothing());
}
}
TEST(IntlBidi, RtlOverride)
{
Bidi bidi{};
// Set the paragraph using the RTL embedding mark U+202B, and the LTR
// embedding mark U+202A to increase the embedding level. This mark switches
// the weakly directional character "_". This demonstrates that embedding
// levels can be computed.
LogicalRunIter logicalRunIter(
bidi, MakeStringSpan(u"ltr\u202b___رائع___\u202a___ltr__"),
Bidi::EmbeddingLevel::DefaultLTR());
ASSERT_EQ(bidi.GetParagraphEmbeddingLevel(), 0);
ASSERT_EQ(bidi.GetParagraphDirection(), Bidi::ParagraphDirection::Mixed);
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isSome());
ASSERT_EQ(logicalRun->string, MakeStringSpan(u"ltr"));
ASSERT_EQ(logicalRun->embeddingLevel, 0);
ASSERT_EQ(logicalRun->embeddingLevel.Direction(), Bidi::Direction::LTR);
}
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isSome());
ASSERT_EQ(logicalRun->string, MakeStringSpan(u"\u202b___رائع___"));
ASSERT_EQ(logicalRun->embeddingLevel, 1);
ASSERT_EQ(logicalRun->embeddingLevel.Direction(), Bidi::Direction::RTL);
}
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isSome());
ASSERT_EQ(logicalRun->string, MakeStringSpan(u"\u202a___ltr__"));
ASSERT_EQ(logicalRun->embeddingLevel, 2);
ASSERT_EQ(logicalRun->embeddingLevel.Direction(), Bidi::Direction::LTR);
}
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isNothing());
}
}
TEST(IntlBidi, VisualRuns)
{
Bidi bidi{};
VisualRunIter visualRunIter(
bidi,
MakeStringSpan(
u"first visual run التشغيل البصري الثاني third visual run"),
Bidi::EmbeddingLevel::DefaultLTR());
{
Maybe<VisualRun> run = visualRunIter.Next();
ASSERT_TRUE(run.isSome());
ASSERT_EQ(run->string, MakeStringSpan(u"first visual run "));
ASSERT_EQ(run->direction, Bidi::Direction::LTR);
}
{
Maybe<VisualRun> run = visualRunIter.Next();
ASSERT_TRUE(run.isSome());
ASSERT_EQ(run->string, MakeStringSpan(u"التشغيل البصري الثاني"));
ASSERT_EQ(run->direction, Bidi::Direction::RTL);
}
{
Maybe<VisualRun> run = visualRunIter.Next();
ASSERT_TRUE(run.isSome());
ASSERT_EQ(run->string, MakeStringSpan(u" third visual run"));
ASSERT_EQ(run->direction, Bidi::Direction::LTR);
}
{
Maybe<VisualRun> run = visualRunIter.Next();
ASSERT_TRUE(run.isNothing());
}
}
TEST(IntlBidi, VisualRunsWithEmbeds)
{
// Compare this test to the logical order test.
Bidi bidi{};
VisualRunIter visualRunIter(
bidi, MakeStringSpan(u"ltr\u202b___رائع___\u202a___ltr___"),
Bidi::EmbeddingLevel::DefaultLTR());
{
Maybe<VisualRun> run = visualRunIter.Next();
ASSERT_TRUE(run.isSome());
ASSERT_EQ(run->string, MakeStringSpan(u"ltr"));
ASSERT_EQ(run->direction, Bidi::Direction::LTR);
}
{
Maybe<VisualRun> run = visualRunIter.Next();
ASSERT_TRUE(run.isSome());
ASSERT_EQ(run->string, MakeStringSpan(u"\u202a___ltr___"));
ASSERT_EQ(run->direction, Bidi::Direction::LTR);
}
{
Maybe<VisualRun> run = visualRunIter.Next();
ASSERT_TRUE(run.isSome());
ASSERT_EQ(run->string, MakeStringSpan(u"\u202b___رائع___"));
ASSERT_EQ(run->direction, Bidi::Direction::RTL);
}
{
Maybe<VisualRun> run = visualRunIter.Next();
ASSERT_TRUE(run.isNothing());
}
}
} // namespace mozilla::intl

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

@ -0,0 +1,193 @@
/* 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/. */
#include "gtest/gtest.h"
#include "mozilla/intl/DateIntervalFormat.h"
#include "mozilla/intl/DateTimeFormat.h"
#include "mozilla/intl/DateTimePart.h"
#include "mozilla/Span.h"
#include "unicode/uformattedvalue.h"
#include "TestBuffer.h"
namespace mozilla::intl {
const double DATE201901030000GMT = 1546473600000.0;
const double DATE201901050000GMT = 1546646400000.0;
TEST(IntlDateIntervalFormat, TryFormatDateTime)
{
UniquePtr<DateIntervalFormat> dif =
DateIntervalFormat::TryCreate(MakeStringSpan("en-US"),
MakeStringSpan(u"MMddHHmm"),
MakeStringSpan(u"GMT"))
.unwrap();
AutoFormattedDateInterval formatted;
// Pass two same Date time, 'equal' should be true.
bool equal;
auto result = dif->TryFormatDateTime(DATE201901030000GMT, DATE201901030000GMT,
formatted, &equal);
ASSERT_TRUE(result.isOk());
ASSERT_TRUE(equal);
auto spanResult = formatted.ToSpan();
ASSERT_TRUE(spanResult.isOk());
ASSERT_EQ(spanResult.unwrap(), MakeStringSpan(u"01/03, 00:00"));
result = dif->TryFormatDateTime(DATE201901030000GMT, DATE201901050000GMT,
formatted, &equal);
ASSERT_TRUE(result.isOk());
ASSERT_FALSE(equal);
spanResult = formatted.ToSpan();
ASSERT_TRUE(spanResult.isOk());
ASSERT_EQ(spanResult.unwrap(),
MakeStringSpan(u"01/03, 00:00 – 01/05, 00:00"));
}
TEST(IntlDateIntervalFormat, TryFormatCalendar)
{
auto dateTimePatternGenerator =
DateTimePatternGenerator::TryCreate("en").unwrap();
UniquePtr<DateTimeFormat> dtFormat =
DateTimeFormat::TryCreateFromSkeleton(
MakeStringSpan("en-US"), MakeStringSpan(u"yMMddHHmm"),
dateTimePatternGenerator.get(), Nothing(),
Some(MakeStringSpan(u"GMT")))
.unwrap();
UniquePtr<DateIntervalFormat> dif =
DateIntervalFormat::TryCreate(MakeStringSpan("en-US"),
MakeStringSpan(u"MMddHHmm"),
MakeStringSpan(u"GMT"))
.unwrap();
AutoFormattedDateInterval formatted;
// Two Calendar objects with the same date time.
auto sameCal = dtFormat->CloneCalendar(DATE201901030000GMT);
ASSERT_TRUE(sameCal.isOk());
auto cal = sameCal.unwrap();
bool equal;
auto result = dif->TryFormatCalendar(*cal, *cal, formatted, &equal);
ASSERT_TRUE(result.isOk());
ASSERT_TRUE(equal);
auto spanResult = formatted.ToSpan();
ASSERT_TRUE(spanResult.isOk());
ASSERT_EQ(spanResult.unwrap(), MakeStringSpan(u"01/03, 00:00"));
auto startCal = dtFormat->CloneCalendar(DATE201901030000GMT);
ASSERT_TRUE(startCal.isOk());
auto endCal = dtFormat->CloneCalendar(DATE201901050000GMT);
ASSERT_TRUE(endCal.isOk());
result = dif->TryFormatCalendar(*startCal.unwrap(), *endCal.unwrap(),
formatted, &equal);
ASSERT_TRUE(result.isOk());
ASSERT_FALSE(equal);
spanResult = formatted.ToSpan();
ASSERT_TRUE(spanResult.isOk());
ASSERT_EQ(spanResult.unwrap(),
MakeStringSpan(u"01/03, 00:00 – 01/05, 00:00"));
}
TEST(IntlDateIntervalFormat, TryFormattedToParts)
{
UniquePtr<DateIntervalFormat> dif =
DateIntervalFormat::TryCreate(MakeStringSpan("en-US"),
MakeStringSpan(u"MMddHHmm"),
MakeStringSpan(u"GMT"))
.unwrap();
AutoFormattedDateInterval formatted;
bool equal;
auto result = dif->TryFormatDateTime(DATE201901030000GMT, DATE201901050000GMT,
formatted, &equal);
ASSERT_TRUE(result.isOk());
ASSERT_FALSE(equal);
Span<const char16_t> formattedSpan = formatted.ToSpan().unwrap();
ASSERT_EQ(formattedSpan, MakeStringSpan(u"01/03, 00:00 – 01/05, 00:00"));
mozilla::intl::DateTimePartVector parts;
result = dif->TryFormattedToParts(formatted, parts);
ASSERT_TRUE(result.isOk());
auto getSubSpan = [formattedSpan, &parts](size_t index) {
size_t start = index == 0 ? 0 : parts[index - 1].mEndIndex;
size_t end = parts[index].mEndIndex;
return formattedSpan.FromTo(start, end);
};
ASSERT_EQ(parts[0].mType, DateTimePartType::Month);
ASSERT_EQ(getSubSpan(0), MakeStringSpan(u"01"));
ASSERT_EQ(parts[0].mSource, DateTimePartSource::StartRange);
ASSERT_EQ(parts[1].mType, DateTimePartType::Literal);
ASSERT_EQ(getSubSpan(1), MakeStringSpan(u"/"));
ASSERT_EQ(parts[1].mSource, DateTimePartSource::StartRange);
ASSERT_EQ(parts[2].mType, DateTimePartType::Day);
ASSERT_EQ(getSubSpan(2), MakeStringSpan(u"03"));
ASSERT_EQ(parts[2].mSource, DateTimePartSource::StartRange);
ASSERT_EQ(parts[3].mType, DateTimePartType::Literal);
ASSERT_EQ(getSubSpan(3), MakeStringSpan(u", "));
ASSERT_EQ(parts[3].mSource, DateTimePartSource::StartRange);
ASSERT_EQ(parts[4].mType, DateTimePartType::Hour);
ASSERT_EQ(getSubSpan(4), MakeStringSpan(u"00"));
ASSERT_EQ(parts[4].mSource, DateTimePartSource::StartRange);
ASSERT_EQ(parts[5].mType, DateTimePartType::Literal);
ASSERT_EQ(getSubSpan(5), MakeStringSpan(u":"));
ASSERT_EQ(parts[5].mSource, DateTimePartSource::StartRange);
ASSERT_EQ(parts[6].mType, DateTimePartType::Minute);
ASSERT_EQ(getSubSpan(6), MakeStringSpan(u"00"));
ASSERT_EQ(parts[6].mSource, DateTimePartSource::StartRange);
ASSERT_EQ(parts[7].mType, DateTimePartType::Literal);
ASSERT_EQ(getSubSpan(7), MakeStringSpan(u""));
ASSERT_EQ(parts[7].mSource, DateTimePartSource::Shared);
ASSERT_EQ(parts[8].mType, DateTimePartType::Month);
ASSERT_EQ(getSubSpan(8), MakeStringSpan(u"01"));
ASSERT_EQ(parts[8].mSource, DateTimePartSource::EndRange);
ASSERT_EQ(parts[9].mType, DateTimePartType::Literal);
ASSERT_EQ(getSubSpan(9), MakeStringSpan(u"/"));
ASSERT_EQ(parts[9].mSource, DateTimePartSource::EndRange);
ASSERT_EQ(parts[10].mType, DateTimePartType::Day);
ASSERT_EQ(getSubSpan(10), MakeStringSpan(u"05"));
ASSERT_EQ(parts[10].mSource, DateTimePartSource::EndRange);
ASSERT_EQ(parts[11].mType, DateTimePartType::Literal);
ASSERT_EQ(getSubSpan(11), MakeStringSpan(u", "));
ASSERT_EQ(parts[11].mSource, DateTimePartSource::EndRange);
ASSERT_EQ(parts[12].mType, DateTimePartType::Hour);
ASSERT_EQ(getSubSpan(12), MakeStringSpan(u"00"));
ASSERT_EQ(parts[12].mSource, DateTimePartSource::EndRange);
ASSERT_EQ(parts[13].mType, DateTimePartType::Literal);
ASSERT_EQ(getSubSpan(13), MakeStringSpan(u":"));
ASSERT_EQ(parts[13].mSource, DateTimePartSource::EndRange);
ASSERT_EQ(parts[14].mType, DateTimePartType::Minute);
ASSERT_EQ(getSubSpan(14), MakeStringSpan(u"00"));
ASSERT_EQ(parts[14].mSource, DateTimePartSource::EndRange);
ASSERT_EQ(parts.length(), 15u);
}
} // namespace mozilla::intl

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

@ -5,6 +5,7 @@
#include "mozilla/intl/Calendar.h"
#include "mozilla/intl/DateTimeFormat.h"
#include "mozilla/intl/DateTimePart.h"
#include "mozilla/intl/DateTimePatternGenerator.h"
#include "mozilla/Span.h"
#include "TestBuffer.h"
@ -537,4 +538,59 @@ TEST(IntlDateTimeFormat, GetAvailableLocales)
ASSERT_EQ(chinese, 1);
}
TEST(IntlDateTimeFormat, TryFormatToParts)
{
auto dateTimePatternGenerator =
DateTimePatternGenerator::TryCreate("en").unwrap();
UniquePtr<DateTimeFormat> dtFormat =
DateTimeFormat::TryCreateFromSkeleton(
MakeStringSpan("en-US"), MakeStringSpan(u"yMMddHHmm"),
dateTimePatternGenerator.get(), Nothing(),
Some(MakeStringSpan(u"GMT")))
.unwrap();
TestBuffer<char16_t> buffer;
mozilla::intl::DateTimePartVector parts;
auto result = dtFormat->TryFormatToParts(DATE, buffer, parts);
ASSERT_TRUE(result.isOk());
std::u16string_view strView = buffer.get_string_view();
ASSERT_EQ(strView, u"09/23/2002, 17:07");
auto getSubStringView = [strView, &parts](size_t index) {
size_t pos = index == 0 ? 0 : parts[index - 1].mEndIndex;
size_t count = parts[index].mEndIndex - pos;
return strView.substr(pos, count);
};
ASSERT_EQ(parts[0].mType, DateTimePartType::Month);
ASSERT_EQ(getSubStringView(0), u"09");
ASSERT_EQ(parts[1].mType, DateTimePartType::Literal);
ASSERT_EQ(getSubStringView(1), u"/");
ASSERT_EQ(parts[2].mType, DateTimePartType::Day);
ASSERT_EQ(getSubStringView(2), u"23");
ASSERT_EQ(parts[3].mType, DateTimePartType::Literal);
ASSERT_EQ(getSubStringView(3), u"/");
ASSERT_EQ(parts[4].mType, DateTimePartType::Year);
ASSERT_EQ(getSubStringView(4), u"2002");
ASSERT_EQ(parts[5].mType, DateTimePartType::Literal);
ASSERT_EQ(getSubStringView(5), u", ");
ASSERT_EQ(parts[6].mType, DateTimePartType::Hour);
ASSERT_EQ(getSubStringView(6), u"17");
ASSERT_EQ(parts[7].mType, DateTimePartType::Literal);
ASSERT_EQ(getSubStringView(7), u":");
ASSERT_EQ(parts[8].mType, DateTimePartType::Minute);
ASSERT_EQ(getSubStringView(8), u"07");
ASSERT_EQ(parts.length(), 9u);
}
} // namespace mozilla::intl

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

@ -5,9 +5,11 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
UNIFIED_SOURCES += [
"TestBidi.cpp",
"TestCalendar.cpp",
"TestCollator.cpp",
"TestCurrency.cpp",
"TestDateIntervalFormat.cpp",
"TestDateTimeFormat.cpp",
"TestListFormat.cpp",
"TestLocale.cpp",

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

@ -4,10 +4,13 @@
# 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/.
EXPORTS.mozilla.intl = [
"src/Bidi.h",
"src/Calendar.h",
"src/Collator.h",
"src/Currency.h",
"src/DateIntervalFormat.h",
"src/DateTimeFormat.h",
"src/DateTimePart.h",
"src/DateTimePatternGenerator.h",
"src/ICU4CGlue.h",
"src/ICU4CLibrary.h",
@ -27,10 +30,13 @@ EXPORTS.mozilla.intl = [
]
UNIFIED_SOURCES += [
"src/Bidi.cpp",
"src/Calendar.cpp",
"src/Collator.cpp",
"src/Currency.cpp",
"src/DateIntervalFormat.cpp",
"src/DateTimeFormat.cpp",
"src/DateTimeFormatUtils.cpp",
"src/DateTimePatternGenerator.cpp",
"src/ICU4CGlue.cpp",
"src/ICU4CLibrary.cpp",

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

@ -0,0 +1,163 @@
/* 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/. */
#include "mozilla/intl/Bidi.h"
#include "mozilla/Casting.h"
#include "mozilla/intl/ICU4CGlue.h"
#include "unicode/ubidi.h"
namespace mozilla::intl {
Bidi::Bidi() { mBidi = ubidi_open(); }
Bidi::~Bidi() { ubidi_close(mBidi.GetMut()); }
ICUResult Bidi::SetParagraph(Span<const char16_t> aParagraph,
Bidi::EmbeddingLevel aLevel) {
// Do not allow any reordering of the runs, as this can change the
// performance characteristics of working with runs. In the default mode,
// the levels can be iterated over directly, rather than relying on computing
// logical runs on the fly. This can have negative performance characteristics
// compared to iterating over the levels.
//
// In the UBIDI_REORDER_RUNS_ONLY the levels are encoded with additional
// information which can be safely ignored in this Bidi implementation.
// Note that this check is here since setting the mode must be done before
// calls to setting the paragraph.
MOZ_ASSERT(ubidi_getReorderingMode(mBidi.GetMut()) == UBIDI_REORDER_DEFAULT);
UErrorCode status = U_ZERO_ERROR;
ubidi_setPara(mBidi.GetMut(), aParagraph.Elements(),
AssertedCast<int32_t>(aParagraph.Length()), aLevel, nullptr,
&status);
mLevels = nullptr;
return ToICUResult(status);
}
Bidi::ParagraphDirection Bidi::GetParagraphDirection() const {
switch (ubidi_getDirection(mBidi.GetConst())) {
case UBIDI_LTR:
return Bidi::ParagraphDirection::LTR;
case UBIDI_RTL:
return Bidi::ParagraphDirection::RTL;
case UBIDI_MIXED:
return Bidi::ParagraphDirection::Mixed;
case UBIDI_NEUTRAL:
// This is only used in `ubidi_getBaseDirection` which is unused in this
// API.
MOZ_ASSERT_UNREACHABLE("Unexpected UBiDiDirection value.");
};
return Bidi::ParagraphDirection::Mixed;
}
/* static */
void Bidi::ReorderVisual(const EmbeddingLevel* aLevels, int32_t aLength,
int32_t* aIndexMap) {
ubidi_reorderVisual(reinterpret_cast<const uint8_t*>(aLevels), aLength,
aIndexMap);
}
static Bidi::Direction ToBidiDirection(UBiDiDirection aDirection) {
switch (aDirection) {
case UBIDI_LTR:
return Bidi::Direction::LTR;
case UBIDI_RTL:
return Bidi::Direction::RTL;
case UBIDI_MIXED:
case UBIDI_NEUTRAL:
MOZ_ASSERT_UNREACHABLE("Unexpected UBiDiDirection value.");
}
return Bidi::Direction::LTR;
}
Result<int32_t, ICUError> Bidi::CountRuns() {
UErrorCode status = U_ZERO_ERROR;
int32_t runCount = ubidi_countRuns(mBidi.GetMut(), &status);
if (U_FAILURE(status)) {
return Err(ToICUError(status));
}
mLength = ubidi_getProcessedLength(mBidi.GetConst());
mLevels = mLength > 0 ? reinterpret_cast<const Bidi::EmbeddingLevel*>(
ubidi_getLevels(mBidi.GetMut(), &status))
: nullptr;
if (U_FAILURE(status)) {
return Err(ToICUError(status));
}
return runCount;
}
void Bidi::GetLogicalRun(int32_t aLogicalStart, int32_t* aLogicalLimitOut,
Bidi::EmbeddingLevel* aLevelOut) {
MOZ_ASSERT(mLevels, "CountRuns hasn't been run?");
MOZ_RELEASE_ASSERT(aLogicalStart < mLength, "Out of bound");
EmbeddingLevel level = mLevels[aLogicalStart];
int32_t limit;
for (limit = aLogicalStart + 1; limit < mLength; limit++) {
if (mLevels[limit] != level) {
break;
}
}
*aLogicalLimitOut = limit;
*aLevelOut = level;
}
bool Bidi::EmbeddingLevel::IsDefaultLTR() const {
return mValue == UBIDI_DEFAULT_LTR;
};
bool Bidi::EmbeddingLevel::IsDefaultRTL() const {
return mValue == UBIDI_DEFAULT_RTL;
};
bool Bidi::EmbeddingLevel::IsRTL() const {
// If the least significant bit is 1, then the embedding level
// is right-to-left.
// If the least significant bit is 0, then the embedding level
// is left-to-right.
return (mValue & 0x1) == 1;
};
bool Bidi::EmbeddingLevel::IsLTR() const { return !IsRTL(); };
bool Bidi::EmbeddingLevel::IsSameDirection(EmbeddingLevel aOther) const {
return (((mValue ^ aOther) & 1) == 0);
}
Bidi::EmbeddingLevel Bidi::EmbeddingLevel::LTR() {
return Bidi::EmbeddingLevel(0);
};
Bidi::EmbeddingLevel Bidi::EmbeddingLevel::RTL() {
return Bidi::EmbeddingLevel(1);
};
Bidi::EmbeddingLevel Bidi::EmbeddingLevel::DefaultLTR() {
return Bidi::EmbeddingLevel(UBIDI_DEFAULT_LTR);
};
Bidi::EmbeddingLevel Bidi::EmbeddingLevel::DefaultRTL() {
return Bidi::EmbeddingLevel(UBIDI_DEFAULT_RTL);
};
Bidi::Direction Bidi::EmbeddingLevel::Direction() {
return IsRTL() ? Direction::RTL : Direction::LTR;
};
uint8_t Bidi::EmbeddingLevel::Value() const { return mValue; }
Bidi::EmbeddingLevel Bidi::GetParagraphEmbeddingLevel() const {
return Bidi::EmbeddingLevel(ubidi_getParaLevel(mBidi.GetConst()));
}
Bidi::Direction Bidi::GetVisualRun(int32_t aRunIndex, int32_t* aLogicalStart,
int32_t* aLength) {
return ToBidiDirection(
ubidi_getVisualRun(mBidi.GetMut(), aRunIndex, aLogicalStart, aLength));
}
} // namespace mozilla::intl

241
intl/components/src/Bidi.h Normal file
Просмотреть файл

@ -0,0 +1,241 @@
/* 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/. */
#ifndef intl_components_Bidi_h_
#define intl_components_Bidi_h_
#include "mozilla/intl/ICU4CGlue.h"
struct UBiDi;
namespace mozilla::intl {
/**
* This component is a Mozilla-focused API for working with bidirectional (bidi)
* text. Text is commonly displayed left to right (LTR), especially for
* Latin-based alphabets. However, languages like Arabic and Hebrew displays
* text right to left (RTL). When displaying text, LTR and RTL text can be
* combined together in the same paragraph. This class gives tools for working
* with unidirectional, and mixed direction paragraphs.
*
* See the Unicode Bidirectional Algorithm document for implementation details:
* https://unicode.org/reports/tr9/
*/
class Bidi final {
public:
Bidi();
~Bidi();
// Not copyable or movable
Bidi(const Bidi&) = delete;
Bidi& operator=(const Bidi&) = delete;
/**
* This enum unambiguously classifies text runs as either being left to right,
* or right to left.
*/
enum class Direction : uint8_t {
// Left to right text.
LTR = 0,
// Right to left text.
RTL = 1,
};
/**
* This enum indicates the text direction for the set paragraph. Some
* paragraphs are unidirectional, where they only have one direction, or a
* paragraph could use both LTR and RTL. In this case the paragraph's
* direction would be mixed.
*/
enum ParagraphDirection { LTR, RTL, Mixed };
/**
* Embedding levels are numbers that indicate how deeply the bidi text is
* embedded, and the direction of text on that embedding level. When switching
* between strongly LTR code points and strongly RTL code points the embedding
* level normally switches between an embedding level of 0 (LTR) and 1 (RTL).
* The only time the embedding level increases is if the embedding code points
* are used. This is the Left-to-Right Embedding (LRE) code point (U+202A), or
* the Right-to-Left Embedding (RLE) code point (U+202B). The minimum
* embedding level of text is zero, and the maximum explicit depth is 125.
*
* The most significant bit is reserved for additional meaning. It can be used
* to signify in certain APIs that the text should by default be LTR or RTL if
* no strongly directional code points are found.
*
* Bug 1736595: At the time of this writing, some places in Gecko code use a 1
* in the most significant bit to indicate that an embedding level has not
* been set. This leads to an ambiguous understanding of what the most
* significant bit actually means.
*/
class EmbeddingLevel {
public:
explicit EmbeddingLevel(uint8_t aValue) : mValue(aValue) {}
explicit EmbeddingLevel(int aValue)
: mValue(static_cast<uint8_t>(aValue)) {}
EmbeddingLevel() = default;
// Enable the copy operators, but disable move as this is only a uint8_t.
EmbeddingLevel(const EmbeddingLevel& other) = default;
EmbeddingLevel& operator=(const EmbeddingLevel& other) = default;
/**
* Determine the direction of the embedding level by looking at the least
* significant bit. If it is 0, then it is LTR. If it is 1, then it is RTL.
*/
Bidi::Direction Direction();
/**
* Create a left-to-right embedding level.
*/
static EmbeddingLevel LTR();
/**
* Create an right-to-left embedding level.
*/
static EmbeddingLevel RTL();
/**
* When passed into `SetParagraph`, the direction is determined by first
* strongly directional character, with the default set to left-to-right if
* none is found.
*
* This is encoded with the highest bit set to 1.
*/
static EmbeddingLevel DefaultLTR();
/**
* When passed into `SetParagraph`, the direction is determined by first
* strongly directional character, with the default set to right-to-left if
* none is found.
*
* * This is encoded with the highest and lowest bits set to 1.
*/
static EmbeddingLevel DefaultRTL();
bool IsDefaultLTR() const;
bool IsDefaultRTL() const;
bool IsLTR() const;
bool IsRTL() const;
bool IsSameDirection(EmbeddingLevel aOther) const;
/**
* Get the underlying value as a uint8_t.
*/
uint8_t Value() const;
/**
* Implicitly convert to the underlying value.
*/
operator uint8_t() const { return mValue; }
private:
uint8_t mValue = 0;
};
/**
* Set the current paragraph of text to analyze for its bidi properties. This
* performs the Unicode bidi algorithm as specified by:
* https://unicode.org/reports/tr9/
*
* After setting the text, the other getter methods can be used to find out
* the directionality of the paragraph text.
*/
ICUResult SetParagraph(Span<const char16_t> aParagraph,
EmbeddingLevel aLevel);
/**
* Get the embedding level for the paragraph that was set by SetParagraph.
*/
EmbeddingLevel GetParagraphEmbeddingLevel() const;
/**
* Get the directionality of the paragraph text that was set by SetParagraph.
*/
ParagraphDirection GetParagraphDirection() const;
/**
* Get the number of runs. This function may invoke the actual reordering on
* the Bidi object, after SetParagraph may have resolved only the levels of
* the text. Therefore, `CountRuns` may have to allocate memory, and may fail
* doing so.
*/
Result<int32_t, ICUError> CountRuns();
/**
* Get the next logical run. The logical runs are a run of text that has the
* same directionality and embedding level. These runs are in memory order,
* and not in display order.
*
* Important! `Bidi::CountRuns` must be called before calling this method.
*
* @param aLogicalStart is the offset into the paragraph text that marks the
* logical start of the text.
* @param aLogicalLimitOut is an out param that is the length of the string
* that makes up the logical run.
* @param aLevelOut is an out parameter that returns the embedding level for
* the run
*/
void GetLogicalRun(int32_t aLogicalStart, int32_t* aLogicalLimitOut,
EmbeddingLevel* aLevelOut);
/**
* This is a convenience function that does not use the ICU Bidi object.
* It is intended to be used for when an application has determined the
* embedding levels of objects (character sequences) and just needs to have
* them reordered (L2).
*
* @param aLevels is an array with `aLength` levels that have been
* determined by the application.
*
* @param aLength is the number of levels in the array, or, semantically,
* the number of objects to be reordered. It must be greater than 0.
*
* @param aIndexMap is a pointer to an array of `aLength`
* indexes which will reflect the reordering of the characters.
* The array does not need to be initialized.
* The index map will result in
* `aIndexMap[aVisualIndex]==aLogicalIndex`.
*/
static void ReorderVisual(const EmbeddingLevel* aLevels, int32_t aLength,
int32_t* aIndexMap);
/**
* Get one run's logical start, length, and directionality. In an RTL run, the
* character at the logical start is visually on the right of the displayed
* run. The length is the number of characters in the run.
* `Bidi::CountRuns` should be called before the runs are retrieved.
*
* @param aRunIndex is the number of the run in visual order, in the
* range `[0..CountRuns-1]`.
*
* @param aLogicalStart is the first logical character index in the text.
* The pointer may be `nullptr` if this index is not needed.
*
* @param aLength is the number of characters (at least one) in the run.
* The pointer may be `nullptr` if this is not needed.
*
* Note that in right-to-left runs, the code places modifier letters before
* base characters and second surrogates before first ones.
*/
Direction GetVisualRun(int32_t aRunIndex, int32_t* aLogicalStart,
int32_t* aLength);
private:
ICUPointer<UBiDi> mBidi = ICUPointer<UBiDi>(nullptr);
/**
* An array of levels that is the same length as the paragraph from
* `Bidi::SetParagraph`.
*/
const EmbeddingLevel* mLevels = nullptr;
/**
* The length of the paragraph from `Bidi::SetParagraph`.
*/
int32_t mLength = 0;
};
} // namespace mozilla::intl
#endif

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

@ -121,14 +121,10 @@ class Calendar final {
~Calendar();
/**
* TODO(Bug 1686965) - Temporarily get the underlying ICU object while
* migrating to the unified API. This should be removed when completing the
* migration.
*/
UCalendar* UnsafeGetUCalendar() const { return mCalendar; }
private:
friend class DateIntervalFormat;
UCalendar* GetUCalendar() const { return mCalendar; }
UCalendar* mCalendar = nullptr;
};

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

@ -0,0 +1,287 @@
/* 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/. */
#include "unicode/udateintervalformat.h"
#include "DateTimeFormatUtils.h"
#include "ScopedICUObject.h"
#include "mozilla/intl/Calendar.h"
#include "mozilla/intl/DateIntervalFormat.h"
namespace mozilla::intl {
/**
* PartitionDateTimeRangePattern ( dateTimeFormat, x, y ), steps 9-11.
*
* Examine the formatted value to see if any interval span field is present.
*
* https://tc39.es/ecma402/#sec-partitiondatetimerangepattern
*/
static ICUResult DateFieldsPracticallyEqual(
const UFormattedValue* aFormattedValue, bool* aEqual) {
if (!aFormattedValue) {
return Err(ICUError::InternalError);
}
MOZ_ASSERT(aEqual);
*aEqual = false;
UErrorCode status = U_ZERO_ERROR;
UConstrainedFieldPosition* fpos = ucfpos_open(&status);
if (U_FAILURE(status)) {
return Err(ToICUError(status));
}
ScopedICUObject<UConstrainedFieldPosition, ucfpos_close> toCloseFpos(fpos);
// We're only interested in UFIELD_CATEGORY_DATE_INTERVAL_SPAN fields.
ucfpos_constrainCategory(fpos, UFIELD_CATEGORY_DATE_INTERVAL_SPAN, &status);
if (U_FAILURE(status)) {
return Err(ToICUError(status));
}
bool hasSpan = ufmtval_nextPosition(aFormattedValue, fpos, &status);
if (U_FAILURE(status)) {
return Err(ToICUError(status));
}
// When no date interval span field was found, both dates are "practically
// equal" per PartitionDateTimeRangePattern.
*aEqual = !hasSpan;
return Ok();
}
/* static */
Result<UniquePtr<DateIntervalFormat>, ICUError> DateIntervalFormat::TryCreate(
Span<const char> aLocale, Span<const char16_t> aSkeleton,
Span<const char16_t> aTimeZone) {
UErrorCode status = U_ZERO_ERROR;
UDateIntervalFormat* dif = udtitvfmt_open(
IcuLocale(AssertNullTerminatedString(aLocale)), aSkeleton.data(),
AssertedCast<int32_t>(aSkeleton.size()), aTimeZone.data(),
AssertedCast<int32_t>(aTimeZone.size()), &status);
if (U_FAILURE(status)) {
return Err(ToICUError(status));
}
return UniquePtr<DateIntervalFormat>(new DateIntervalFormat(dif));
}
DateIntervalFormat::~DateIntervalFormat() {
MOZ_ASSERT(mDateIntervalFormat);
udtitvfmt_close(mDateIntervalFormat.GetMut());
}
AutoFormattedDateInterval::AutoFormattedDateInterval() {
mFormatted = udtitvfmt_openResult(&mError);
if (U_FAILURE(mError)) {
mFormatted = nullptr;
}
}
const UFormattedValue* AutoFormattedDateInterval::Value() const {
if (!IsValid()) {
return nullptr;
}
UErrorCode status = U_ZERO_ERROR;
const UFormattedValue* value = udtitvfmt_resultAsValue(mFormatted, &status);
if (U_FAILURE(status)) {
return nullptr;
}
return value;
}
Result<Span<const char16_t>, ICUError> AutoFormattedDateInterval::ToSpan()
const {
if (!IsValid()) {
return Err(GetError());
}
const UFormattedValue* value = Value();
if (!value) {
return Err(ICUError::InternalError);
}
UErrorCode status = U_ZERO_ERROR;
int32_t strLength;
const char16_t* str = ufmtval_getString(value, &strLength, &status);
if (U_FAILURE(status)) {
return Err(ToICUError(status));
}
return Span{str, AssertedCast<size_t>(strLength)};
}
AutoFormattedDateInterval::~AutoFormattedDateInterval() {
if (mFormatted) {
udtitvfmt_closeResult(mFormatted);
}
}
ICUResult DateIntervalFormat::TryFormatCalendar(
const Calendar& aStart, const Calendar& aEnd,
AutoFormattedDateInterval& aFormatted, bool* aPracticallyEqual) const {
MOZ_ASSERT(aFormatted.IsValid());
UErrorCode status = U_ZERO_ERROR;
udtitvfmt_formatCalendarToResult(
mDateIntervalFormat.GetConst(), aStart.GetUCalendar(),
aEnd.GetUCalendar(), aFormatted.GetUFormattedDateInterval(), &status);
if (U_FAILURE(status)) {
return Err(ToICUError(status));
}
MOZ_TRY(DateFieldsPracticallyEqual(aFormatted.Value(), aPracticallyEqual));
return Ok();
}
ICUResult DateIntervalFormat::TryFormatDateTime(
double aStart, double aEnd, AutoFormattedDateInterval& aFormatted,
bool* aPracticallyEqual) const {
MOZ_ASSERT(aFormatted.IsValid());
UErrorCode status = U_ZERO_ERROR;
udtitvfmt_formatToResult(mDateIntervalFormat.GetConst(), aStart, aEnd,
aFormatted.GetUFormattedDateInterval(), &status);
if (U_FAILURE(status)) {
return Err(ToICUError(status));
}
MOZ_TRY(DateFieldsPracticallyEqual(aFormatted.Value(), aPracticallyEqual));
return Ok();
}
ICUResult DateIntervalFormat::TryFormattedToParts(
const AutoFormattedDateInterval& aFormatted,
DateTimePartVector& aParts) const {
MOZ_ASSERT(aFormatted.IsValid());
const UFormattedValue* value = aFormatted.Value();
if (!value) {
return Err(ICUError::InternalError);
}
size_t lastEndIndex = 0;
auto AppendPart = [&](DateTimePartType type, size_t endIndex,
DateTimePartSource source) {
if (!aParts.emplaceBack(type, endIndex, source)) {
return false;
}
lastEndIndex = endIndex;
return true;
};
UErrorCode status = U_ZERO_ERROR;
UConstrainedFieldPosition* fpos = ucfpos_open(&status);
if (U_FAILURE(status)) {
return Err(ToICUError(status));
}
ScopedICUObject<UConstrainedFieldPosition, ucfpos_close> toCloseFpos(fpos);
size_t categoryEndIndex = 0;
DateTimePartSource source = DateTimePartSource::Shared;
while (true) {
bool hasMore = ufmtval_nextPosition(value, fpos, &status);
if (U_FAILURE(status)) {
return Err(ToICUError(status));
}
if (!hasMore) {
break;
}
int32_t category = ucfpos_getCategory(fpos, &status);
if (U_FAILURE(status)) {
return Err(ToICUError(status));
}
int32_t field = ucfpos_getField(fpos, &status);
if (U_FAILURE(status)) {
return Err(ToICUError(status));
}
int32_t beginIndexInt, endIndexInt;
ucfpos_getIndexes(fpos, &beginIndexInt, &endIndexInt, &status);
if (U_FAILURE(status)) {
return Err(ToICUError(status));
}
MOZ_ASSERT(beginIndexInt <= endIndexInt,
"field iterator returning invalid range");
size_t beginIndex = AssertedCast<size_t>(beginIndexInt);
size_t endIndex = AssertedCast<size_t>(endIndexInt);
// Indices are guaranteed to be returned in order (from left to right).
MOZ_ASSERT(lastEndIndex <= beginIndex,
"field iteration didn't return fields in order start to "
"finish as expected");
if (category == UFIELD_CATEGORY_DATE_INTERVAL_SPAN) {
// Append any remaining literal parts before changing the source kind.
if (lastEndIndex < beginIndex) {
if (!AppendPart(DateTimePartType::Literal, beginIndex, source)) {
return Err(ICUError::InternalError);
}
}
// The special field category UFIELD_CATEGORY_DATE_INTERVAL_SPAN has only
// two allowed values (0 or 1), indicating the begin of the start- resp.
// end-date.
MOZ_ASSERT(field == 0 || field == 1,
"span category has unexpected value");
source = field == 0 ? DateTimePartSource::StartRange
: DateTimePartSource::EndRange;
categoryEndIndex = endIndex;
continue;
}
// Ignore categories other than UFIELD_CATEGORY_DATE.
if (category != UFIELD_CATEGORY_DATE) {
continue;
}
DateTimePartType type =
ConvertUFormatFieldToPartType(static_cast<UDateFormatField>(field));
if (lastEndIndex < beginIndex) {
if (!AppendPart(DateTimePartType::Literal, beginIndex, source)) {
return Err(ICUError::InternalError);
}
}
if (!AppendPart(type, endIndex, source)) {
return Err(ICUError::InternalError);
}
if (endIndex == categoryEndIndex) {
// Append any remaining literal parts before changing the source kind.
if (lastEndIndex < endIndex) {
if (!AppendPart(DateTimePartType::Literal, endIndex, source)) {
return Err(ICUError::InternalError);
}
}
source = DateTimePartSource::Shared;
}
}
// Append any final literal.
auto spanResult = aFormatted.ToSpan();
if (spanResult.isErr()) {
return spanResult.propagateErr();
}
size_t formattedSize = spanResult.unwrap().size();
if (lastEndIndex < formattedSize) {
if (!AppendPart(DateTimePartType::Literal, formattedSize, source)) {
return Err(ICUError::InternalError);
}
}
return Ok();
}
} // namespace mozilla::intl

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

@ -0,0 +1,159 @@
/* 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/. */
#ifndef intl_components_DateIntervalFormat_h_
#define intl_components_DateIntervalFormat_h_
#include "mozilla/intl/DateTimePart.h"
#include "mozilla/intl/ICU4CGlue.h"
#include "mozilla/intl/ICUError.h"
#include "mozilla/Result.h"
#include "mozilla/Span.h"
#include "mozilla/UniquePtr.h"
#include "unicode/utypes.h"
struct UDateIntervalFormat;
struct UFormattedDateInterval;
struct UFormattedValue;
namespace mozilla::intl {
class AutoFormattedDateInterval;
class Calendar;
/**
* This component is a Mozilla-focused API for the date range formatting
* provided by ICU. This DateIntervalFormat class helps to format the range
* between two date-time values.
*
* https://tc39.es/ecma402/#sec-formatdatetimerange
* https://tc39.es/ecma402/#sec-formatdatetimerangetoparts
*/
class DateIntervalFormat final {
public:
/**
* Create a DateIntervalFormat object from locale, skeleton and time zone.
* The format of skeleton can be found in [1].
*
* Note: Skeleton will be removed in the future.
*
* [1]: https://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
*/
static Result<UniquePtr<DateIntervalFormat>, ICUError> TryCreate(
Span<const char> aLocale, Span<const char16_t> aSkeleton,
Span<const char16_t> aTimeZone);
~DateIntervalFormat();
/**
* Format a date-time range between two Calendar objects.
*
* DateIntervalFormat cannot be changed to use a proleptic Gregorian
* calendar, so use this method if the start date is before the Gregorian
* calendar is introduced(October 15, 1582), otherwise use TryFormatDateTime
* instead.
*
* The result will be stored in aFormatted, caller can use
* AutoFormattedDateInterval::ToSpan() to get the formatted string, or pass
* the aFormatted to TryFormattedToParts to get the parts vector.
*
* aPracticallyEqual will be set to true if the date times of the two
* calendars are equal.
*/
ICUResult TryFormatCalendar(const Calendar& aStart, const Calendar& aEnd,
AutoFormattedDateInterval& aFormatted,
bool* aPracticallyEqual) const;
/**
* Format a date-time range between two Unix epoch times in milliseconds.
*
* The result will be stored in aFormatted, caller can use
* AutoFormattedDateInterval::ToSpan() to get the formatted string, or pass
* the aFormatted to TryFormattedToParts to get the parts vector.
*
* aPracticallyEqual will be set to true if the date times of the two
* Unix epoch times are equal.
*/
ICUResult TryFormatDateTime(double aStart, double aEnd,
AutoFormattedDateInterval& aFormatted,
bool* aPracticallyEqual) const;
/**
* Convert the formatted DateIntervalFormat into several parts.
*
* The caller get the formatted result from either TryFormatCalendar, or
* TryFormatDateTime methods, and instantiate the DateTimePartVector. This
* method will generate the parts and insert them into the vector.
*
* See:
* https://tc39.es/ecma402/#sec-formatdatetimerangetoparts
*/
ICUResult TryFormattedToParts(const AutoFormattedDateInterval& aFormatted,
DateTimePartVector& aParts) const;
private:
DateIntervalFormat() = delete;
explicit DateIntervalFormat(UDateIntervalFormat* aDif)
: mDateIntervalFormat(aDif) {}
DateIntervalFormat(const DateIntervalFormat&) = delete;
DateIntervalFormat& operator=(const DateIntervalFormat&) = delete;
ICUPointer<UDateIntervalFormat> mDateIntervalFormat =
ICUPointer<UDateIntervalFormat>(nullptr);
};
/**
* A RAII class to hold the formatted value of DateIntervalFormat.
*
* The caller will need to create this AutoFormattedDateInterval on the stack,
* and call IsValid() method to check if the native object
* (UFormattedDateInterval) has been created properly, and then passes this
* object to the methods of DateIntervalFormat.
* The result of the DateIntervalFormat's method will be stored in this object,
* the caller can use ToSpan() method to get the formatted string, or pass it
* to DateIntervalFormat::TryFormattedToParts to get the DateTimePart vector.
*
* The formatted value will be released once this class is destructed.
*/
class MOZ_RAII AutoFormattedDateInterval {
public:
AutoFormattedDateInterval();
~AutoFormattedDateInterval();
AutoFormattedDateInterval(const AutoFormattedDateInterval& other) = delete;
AutoFormattedDateInterval& operator=(const AutoFormattedDateInterval& other) =
delete;
AutoFormattedDateInterval(AutoFormattedDateInterval&& other) = delete;
AutoFormattedDateInterval& operator=(AutoFormattedDateInterval&& other) =
delete;
/**
* Check if the native UFormattedDateInterval was created successfully.
*/
bool IsValid() const { return !!mFormatted; }
/**
* Get error code if IsValid() returns false.
*/
ICUError GetError() const { return ToICUError(mError); }
/**
* Get the formatted result.
*/
Result<Span<const char16_t>, ICUError> ToSpan() const;
private:
friend class DateIntervalFormat;
UFormattedDateInterval* GetUFormattedDateInterval() const {
return mFormatted;
}
const UFormattedValue* Value() const;
UFormattedDateInterval* mFormatted = nullptr;
UErrorCode mError = U_ZERO_ERROR;
};
} // namespace mozilla::intl
#endif

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

@ -9,6 +9,7 @@
#include "unicode/udatpg.h"
#include "unicode/ures.h"
#include "DateTimeFormatUtils.h"
#include "ScopedICUObject.h"
#include "mozilla/EnumSet.h"
@ -1109,4 +1110,62 @@ const char* DateTimeFormat::ToString(DateTimeFormat::HourCycle aHourCycle) {
}
MOZ_CRASH("Unexpected DateTimeFormat::HourCycle");
}
ICUResult DateTimeFormat::TryFormatToParts(
UFieldPositionIterator* aFieldPositionIterator, size_t aSpanSize,
DateTimePartVector& aParts) const {
ScopedICUObject<UFieldPositionIterator, ufieldpositer_close> toClose(
aFieldPositionIterator);
size_t lastEndIndex = 0;
auto AppendPart = [&](DateTimePartType type, size_t endIndex) {
// For the part defined in FormatDateTimeToParts, it doesn't have ||Source||
// property, we store Shared for simplicity,
if (!aParts.emplaceBack(type, endIndex, DateTimePartSource::Shared)) {
return false;
}
lastEndIndex = endIndex;
return true;
};
int32_t fieldInt, beginIndexInt, endIndexInt;
while ((fieldInt = ufieldpositer_next(aFieldPositionIterator, &beginIndexInt,
&endIndexInt)) >= 0) {
MOZ_ASSERT(beginIndexInt <= endIndexInt,
"field iterator returning invalid range");
size_t beginIndex = AssertedCast<size_t>(beginIndexInt);
size_t endIndex = AssertedCast<size_t>(endIndexInt);
// Technically this isn't guaranteed. But it appears true in pratice,
// and http://bugs.icu-project.org/trac/ticket/12024 is expected to
// correct the documentation lapse.
MOZ_ASSERT(lastEndIndex <= beginIndex,
"field iteration didn't return fields in order start to "
"finish as expected");
DateTimePartType type =
ConvertUFormatFieldToPartType(static_cast<UDateFormatField>(fieldInt));
if (lastEndIndex < beginIndex) {
if (!AppendPart(DateTimePartType::Literal, beginIndex)) {
return Err(ICUError::InternalError);
}
}
if (!AppendPart(type, endIndex)) {
return Err(ICUError::InternalError);
}
}
// Append any final literal.
if (lastEndIndex < aSpanSize) {
if (!AppendPart(DateTimePartType::Literal, aSpanSize)) {
return Err(ICUError::InternalError);
}
}
return Ok();
}
} // namespace mozilla::intl

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

@ -9,6 +9,8 @@
#include "mozilla/Assertions.h"
#include "mozilla/intl/ICU4CGlue.h"
#include "mozilla/intl/ICUError.h"
#include "mozilla/intl/DateTimePart.h"
#include "mozilla/intl/DateTimePatternGenerator.h"
#include "mozilla/Maybe.h"
#include "mozilla/Result.h"
@ -353,6 +355,44 @@ class DateTimeFormat final {
}
};
/**
* Format the Unix epoch time into a DateTimePartVector.
*
* The caller has to create the buffer and the vector and pass to this method.
* The formatted string will be stored in the buffer and formatted parts in
* the vector.
*
* aUnixEpoch is the number of milliseconds since 1 January 1970, UTC.
*
* See:
* https://tc39.es/ecma402/#sec-formatdatetimetoparts
*/
template <typename B>
ICUResult TryFormatToParts(double aUnixEpoch, B& aBuffer,
DateTimePartVector& aParts) const {
static_assert(std::is_same<typename B::CharType, char16_t>::value,
"Only char16_t is supported (for UTF-16 support) now.");
UErrorCode status = U_ZERO_ERROR;
UFieldPositionIterator* fpositer = ufieldpositer_open(&status);
if (U_FAILURE(status)) {
return Err(ToICUError(status));
}
auto result = FillBufferWithICUCall(
aBuffer, [this, aUnixEpoch, fpositer](UChar* chars, int32_t size,
UErrorCode* status) {
return udat_formatForFields(mDateFormat, aUnixEpoch, chars, size,
fpositer, status);
});
if (result.isErr()) {
ufieldpositer_close(fpositer);
return result.propagateErr();
}
return TryFormatToParts(fpositer, aBuffer.length(), aParts);
}
/**
* Copies the pattern for the current DateTimeFormat to a buffer.
*
@ -423,13 +463,6 @@ class DateTimeFormat final {
~DateTimeFormat();
/**
* TODO(Bug 1686965) - Temporarily get the underlying ICU object while
* migrating to the unified API. This should be removed when completing the
* migration.
*/
UDateFormat* UnsafeGetUDateFormat() const { return mDateFormat; }
/**
* Clones the Calendar from a DateTimeFormat, and sets its time with the
* relative milliseconds since 1 January 1970, UTC.
@ -474,6 +507,9 @@ class DateTimeFormat final {
ICUResult CacheSkeleton(Span<const char16_t> aSkeleton);
ICUResult TryFormatToParts(UFieldPositionIterator* aFieldPositionIterator,
size_t aSpanSize,
DateTimePartVector& aParts) const;
/**
* Replaces all hour pattern characters in |patternOrSkeleton| to use the
* matching hour representation for |hourCycle|.

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

@ -0,0 +1,104 @@
/* 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/. */
#include "mozilla/Assertions.h"
#include "DateTimeFormatUtils.h"
namespace mozilla::intl {
DateTimePartType ConvertUFormatFieldToPartType(UDateFormatField fieldName) {
// See intl/icu/source/i18n/unicode/udat.h for a detailed field list. This
// switch is deliberately exhaustive: cases might have to be added/removed
// if this code is compiled with a different ICU with more
// UDateFormatField enum initializers. Please guard such cases with
// appropriate ICU version-testing #ifdefs, should cross-version divergence
// occur.
switch (fieldName) {
case UDAT_ERA_FIELD:
return DateTimePartType::Era;
case UDAT_YEAR_FIELD:
case UDAT_YEAR_WOY_FIELD:
case UDAT_EXTENDED_YEAR_FIELD:
return DateTimePartType::Year;
case UDAT_YEAR_NAME_FIELD:
return DateTimePartType::YearName;
case UDAT_MONTH_FIELD:
case UDAT_STANDALONE_MONTH_FIELD:
return DateTimePartType::Month;
case UDAT_DATE_FIELD:
case UDAT_JULIAN_DAY_FIELD:
return DateTimePartType::Day;
case UDAT_HOUR_OF_DAY1_FIELD:
case UDAT_HOUR_OF_DAY0_FIELD:
case UDAT_HOUR1_FIELD:
case UDAT_HOUR0_FIELD:
return DateTimePartType::Hour;
case UDAT_MINUTE_FIELD:
return DateTimePartType::Minute;
case UDAT_SECOND_FIELD:
return DateTimePartType::Second;
case UDAT_DAY_OF_WEEK_FIELD:
case UDAT_STANDALONE_DAY_FIELD:
case UDAT_DOW_LOCAL_FIELD:
case UDAT_DAY_OF_WEEK_IN_MONTH_FIELD:
return DateTimePartType::Weekday;
case UDAT_AM_PM_FIELD:
case UDAT_FLEXIBLE_DAY_PERIOD_FIELD:
return DateTimePartType::DayPeriod;
case UDAT_TIMEZONE_FIELD:
case UDAT_TIMEZONE_GENERIC_FIELD:
case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD:
return DateTimePartType::TimeZoneName;
case UDAT_FRACTIONAL_SECOND_FIELD:
return DateTimePartType::FractionalSecondDigits;
#ifndef U_HIDE_INTERNAL_API
case UDAT_RELATED_YEAR_FIELD:
return DateTimePartType::RelatedYear;
#endif
case UDAT_DAY_OF_YEAR_FIELD:
case UDAT_WEEK_OF_YEAR_FIELD:
case UDAT_WEEK_OF_MONTH_FIELD:
case UDAT_MILLISECONDS_IN_DAY_FIELD:
case UDAT_TIMEZONE_RFC_FIELD:
case UDAT_QUARTER_FIELD:
case UDAT_STANDALONE_QUARTER_FIELD:
case UDAT_TIMEZONE_SPECIAL_FIELD:
case UDAT_TIMEZONE_ISO_FIELD:
case UDAT_TIMEZONE_ISO_LOCAL_FIELD:
case UDAT_AM_PM_MIDNIGHT_NOON_FIELD:
#ifndef U_HIDE_INTERNAL_API
case UDAT_TIME_SEPARATOR_FIELD:
#endif
// These fields are all unsupported.
return DateTimePartType::Unknown;
#ifndef U_HIDE_DEPRECATED_API
case UDAT_FIELD_COUNT:
MOZ_ASSERT_UNREACHABLE(
"format field sentinel value returned by "
"iterator!");
#endif
}
MOZ_ASSERT_UNREACHABLE(
"unenumerated, undocumented format field returned "
"by iterator");
return DateTimePartType::Unknown;
}
} // namespace mozilla::intl

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

@ -0,0 +1,14 @@
/* 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/. */
#ifndef intl_components_DateTimeFormatUtils_h_
#define intl_components_DateTimeFormatUtils_h_
#include "unicode/udat.h"
#include "mozilla/intl/DateTimePart.h"
namespace mozilla::intl {
DateTimePartType ConvertUFormatFieldToPartType(UDateFormatField fieldName);
} // namespace mozilla::intl
#endif

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

@ -0,0 +1,84 @@
/* 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/. */
#ifndef intl_components_DateTimePart_h_
#define intl_components_DateTimePart_h_
#include <cstddef>
#include <cstdint>
#include "mozilla/Vector.h"
namespace mozilla::intl {
enum class DateTimePartType : int16_t {
Literal,
Weekday,
Era,
Year,
YearName,
RelatedYear,
Month,
Day,
DayPeriod,
Hour,
Minute,
Second,
FractionalSecondDigits,
TimeZoneName,
Unknown
};
enum class DateTimePartSource : int16_t { Shared, StartRange, EndRange };
/**
* The 'Part' object defined in FormatDateTimeToParts and
* FormatDateTimeRangeToParts
*
* Each part consists of three properties: ||Type||, ||Value|| and ||Source||,
* with the ||Source|| property is set to DateTimePartSource::Shared by default.
* (Note: From the spec, the part from FormatDateTimeToParts doesn't have the
* ||Source|| property, so if the caller is FormatDateTimeToParts, it should
* ignore the ||Source|| property).
*
* To store DateTimePart more efficiently, it doesn't store the ||Value|| of
* type string in this struct. Instead, it stores the end index of the string
* in the buffer(which is passed to DateTimeFormat::TryFormatToParts() or
* can be got by calling AutoFormattedDateInterval::ToSpan()). The begin index
* of the ||Value|| is the mEndIndex of the previous part.
*
* Buffer
* 0 i j
* +---------------+---------------+---------------+
* | Part[0].Value | Part[1].Value | Part[2].Value | ....
* +---------------+---------------+---------------+
*
* Part[0].mEndIndex is i. Part[0].Value is stored in the Buffer[0..i].
* Part[1].mEndIndex is j. Part[1].Value is stored in the Buffer[i..j].
*
* See:
* https://tc39.es/ecma402/#sec-formatdatetimetoparts
* https://tc39.es/ecma402/#sec-formatdatetimerangetoparts
*/
struct DateTimePart {
DateTimePart(DateTimePartType type, size_t endIndex,
DateTimePartSource source)
: mEndIndex(endIndex), mType(type), mSource(source) {}
// See the above comments for details, mEndIndex is placed first for reducing
// padding.
size_t mEndIndex;
DateTimePartType mType;
DateTimePartSource mSource;
};
// The common parts are 'month', 'literal', 'day', 'literal', 'year', 'literal',
// 'hour', 'literal', 'minute', 'literal', which are 10 parts, for DateTimeRange
// the number will be doubled, so choosing 32 as the initial length to prevent
// heap allocation.
constexpr size_t INITIAL_DATETIME_PART_VECTOR_SIZE = 32;
using DateTimePartVector =
mozilla::Vector<DateTimePart, INITIAL_DATETIME_PART_VECTOR_SIZE>;
} // namespace mozilla::intl
#endif

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

@ -51,25 +51,6 @@ enum nsCharType {
*/
typedef enum nsCharType nsCharType;
/**
* Find the direction of an embedding level or paragraph level set by
* the Unicode Bidi Algorithm. (Even levels are left-to-right, odd
* levels right-to-left.
*/
#define IS_LEVEL_RTL(level) (((level)&1) == 1)
/**
* Check whether two bidi levels have the same parity and thus the same
* directionality
*/
#define IS_SAME_DIRECTION(level1, level2) (((level1 ^ level2) & 1) == 0)
/**
* Convert from nsBidiLevel to nsBidiDirection
*/
#define DIRECTION_FROM_LEVEL(level) \
((IS_LEVEL_RTL(level)) ? NSBIDI_RTL : NSBIDI_LTR)
/**
* definitions of bidirection character types by category
*/

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

@ -96,9 +96,9 @@ extern JS_PUBLIC_API JSObject* InstantiateModuleStencil(
JSContext* cx, const InstantiateOptions& options, Stencil* stencil);
// Serialize the Stencil into the transcode buffer.
extern JS_PUBLIC_API TranscodeResult
EncodeStencil(JSContext* cx, const JS::ReadOnlyCompileOptions& options,
Stencil* stencil, TranscodeBuffer& buffer);
extern JS_PUBLIC_API TranscodeResult EncodeStencil(JSContext* cx,
Stencil* stencil,
TranscodeBuffer& buffer);
// Deserialize data and create a new Stencil.
extern JS_PUBLIC_API TranscodeResult

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

@ -351,6 +351,24 @@ static mozilla::intl::Collator* NewIntlCollator(
return coll.release();
}
static mozilla::intl::Collator* GetOrCreateCollator(
JSContext* cx, Handle<CollatorObject*> collator) {
// Obtain a cached mozilla::intl::Collator object.
mozilla::intl::Collator* coll = collator->getCollator();
if (coll) {
return coll;
}
coll = NewIntlCollator(cx, collator);
if (!coll) {
return nullptr;
}
collator->setCollator(coll);
intl::AddICUCellMemory(collator, CollatorObject::EstimatedMemoryUse);
return coll;
}
static bool intl_CompareStrings(JSContext* cx, mozilla::intl::Collator* coll,
HandleString str1, HandleString str2,
MutableHandleValue result) {
@ -389,16 +407,9 @@ bool js::intl_CompareStrings(JSContext* cx, unsigned argc, Value* vp) {
Rooted<CollatorObject*> collator(cx,
&args[0].toObject().as<CollatorObject>());
// Obtain a cached mozilla::intl::Collator object.
mozilla::intl::Collator* coll = collator->getCollator();
mozilla::intl::Collator* coll = GetOrCreateCollator(cx, collator);
if (!coll) {
coll = NewIntlCollator(cx, collator);
if (!coll) {
return false;
}
collator->setCollator(coll);
intl::AddICUCellMemory(collator, CollatorObject::EstimatedMemoryUse);
return false;
}
// Use the UCollator to actually compare the strings.

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

@ -20,7 +20,6 @@
#include "gc/ZoneAllocator.h"
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_INTERNAL_INTL_ERROR
#include "js/Value.h"
#include "unicode/uformattedvalue.h"
#include "vm/JSContext.h"
#include "vm/JSObject.h"
#include "vm/SelfHosting.h"
@ -148,17 +147,3 @@ void js::intl::RemoveICUCellMemory(JSFreeOp* fop, JSObject* obj,
size_t nbytes) {
fop->removeCellMemory(obj, nbytes, MemoryUse::ICUObject);
}
JSString* js::intl::FormattedValueToString(
JSContext* cx, const UFormattedValue* formattedValue) {
UErrorCode status = U_ZERO_ERROR;
int32_t strLength;
const char16_t* str = ufmtval_getString(formattedValue, &strLength, &status);
if (U_FAILURE(status)) {
ReportInternalError(cx);
return nullptr;
}
return NewStringCopyN<CanGC>(cx, str,
mozilla::AssertedCast<uint32_t>(strLength));
}

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

@ -19,8 +19,6 @@
#include "unicode/utypes.h"
#include "vm/StringType.h"
struct UFormattedValue;
namespace mozilla::intl {
enum class ICUError : uint8_t;
}
@ -165,10 +163,6 @@ static JSString* CallICU(JSContext* cx, const ICUStringFunction& strFn) {
void AddICUCellMemory(JSObject* obj, size_t nbytes);
void RemoveICUCellMemory(JSFreeOp* fop, JSObject* obj, size_t nbytes);
JSString* FormattedValueToString(JSContext* cx,
const UFormattedValue* formattedValue);
} // namespace intl
} // namespace js

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

@ -11,7 +11,9 @@
#include "mozilla/Assertions.h"
#include "mozilla/EnumSet.h"
#include "mozilla/intl/Calendar.h"
#include "mozilla/intl/DateIntervalFormat.h"
#include "mozilla/intl/DateTimeFormat.h"
#include "mozilla/intl/DateTimePart.h"
#include "mozilla/intl/DateTimePatternGenerator.h"
#include "mozilla/intl/Locale.h"
#include "mozilla/intl/TimeZone.h"
@ -34,12 +36,6 @@
#include "js/PropertyAndElement.h" // JS_DefineFunctions, JS_DefineProperties
#include "js/PropertySpec.h"
#include "js/StableStringChars.h"
#include "unicode/udat.h"
#include "unicode/udateintervalformat.h"
#include "unicode/uenum.h"
#include "unicode/ufieldpositer.h"
#include "unicode/uloc.h"
#include "unicode/utypes.h"
#include "vm/DateTime.h"
#include "vm/GlobalObject.h"
#include "vm/JSContext.h"
@ -195,7 +191,8 @@ void js::DateTimeFormatObject::finalize(JSFreeOp* fop, JSObject* obj) {
auto* dateTimeFormat = &obj->as<DateTimeFormatObject>();
mozilla::intl::DateTimeFormat* df = dateTimeFormat->getDateFormat();
UDateIntervalFormat* dif = dateTimeFormat->getDateIntervalFormat();
mozilla::intl::DateIntervalFormat* dif =
dateTimeFormat->getDateIntervalFormat();
if (df) {
intl::RemoveICUCellMemory(
@ -208,7 +205,7 @@ void js::DateTimeFormatObject::finalize(JSFreeOp* fop, JSObject* obj) {
intl::RemoveICUCellMemory(
fop, obj, DateTimeFormatObject::UDateIntervalFormatEstimatedMemoryUse);
udtitvfmt_close(dif);
delete dif;
}
}
@ -988,6 +985,25 @@ static mozilla::intl::DateTimeFormat* NewDateTimeFormat(
return df.release();
}
static mozilla::intl::DateTimeFormat* GetOrCreateDateTimeFormat(
JSContext* cx, Handle<DateTimeFormatObject*> dateTimeFormat) {
// Obtain a cached mozilla::intl::DateTimeFormat object.
mozilla::intl::DateTimeFormat* df = dateTimeFormat->getDateFormat();
if (df) {
return df;
}
df = NewDateTimeFormat(cx, dateTimeFormat);
if (!df) {
return nullptr;
}
dateTimeFormat->setDateFormat(df);
intl::AddICUCellMemory(dateTimeFormat,
DateTimeFormatObject::UDateFormatEstimatedMemoryUse);
return df;
}
template <typename T>
static bool SetResolvedProperty(JSContext* cx, HandleObject resolved,
HandlePropertyName name,
@ -1019,17 +1035,10 @@ bool js::intl_resolveDateTimeFormatComponents(JSContext* cx, unsigned argc,
bool includeDateTimeFields = args[2].toBoolean();
// Obtain a cached mozilla::intl::DateTimeFormat object.
mozilla::intl::DateTimeFormat* df = dateTimeFormat->getDateFormat();
mozilla::intl::DateTimeFormat* df =
GetOrCreateDateTimeFormat(cx, dateTimeFormat);
if (!df) {
df = NewDateTimeFormat(cx, dateTimeFormat);
if (!df) {
return false;
}
dateTimeFormat->setDateFormat(df);
intl::AddICUCellMemory(dateTimeFormat,
DateTimeFormatObject::UDateFormatEstimatedMemoryUse);
return false;
}
auto result = df->ResolveComponents();
@ -1135,133 +1144,79 @@ static bool intl_FormatDateTime(JSContext* cx,
using FieldType = js::ImmutablePropertyNamePtr JSAtomState::*;
static FieldType GetFieldTypeForFormatField(UDateFormatField fieldName) {
// See intl/icu/source/i18n/unicode/udat.h for a detailed field list. This
// switch is deliberately exhaustive: cases might have to be added/removed
// if this code is compiled with a different ICU with more
// UDateFormatField enum initializers. Please guard such cases with
// appropriate ICU version-testing #ifdefs, should cross-version divergence
// occur.
switch (fieldName) {
case UDAT_ERA_FIELD:
static FieldType GetFieldTypeForPartType(mozilla::intl::DateTimePartType type) {
switch (type) {
case mozilla::intl::DateTimePartType::Literal:
return &JSAtomState::literal;
case mozilla::intl::DateTimePartType::Era:
return &JSAtomState::era;
case UDAT_YEAR_FIELD:
case UDAT_YEAR_WOY_FIELD:
case UDAT_EXTENDED_YEAR_FIELD:
case mozilla::intl::DateTimePartType::Year:
return &JSAtomState::year;
case UDAT_YEAR_NAME_FIELD:
case mozilla::intl::DateTimePartType::YearName:
return &JSAtomState::yearName;
case UDAT_MONTH_FIELD:
case UDAT_STANDALONE_MONTH_FIELD:
return &JSAtomState::month;
case UDAT_DATE_FIELD:
case UDAT_JULIAN_DAY_FIELD:
return &JSAtomState::day;
case UDAT_HOUR_OF_DAY1_FIELD:
case UDAT_HOUR_OF_DAY0_FIELD:
case UDAT_HOUR1_FIELD:
case UDAT_HOUR0_FIELD:
return &JSAtomState::hour;
case UDAT_MINUTE_FIELD:
return &JSAtomState::minute;
case UDAT_SECOND_FIELD:
return &JSAtomState::second;
case UDAT_DAY_OF_WEEK_FIELD:
case UDAT_STANDALONE_DAY_FIELD:
case UDAT_DOW_LOCAL_FIELD:
case UDAT_DAY_OF_WEEK_IN_MONTH_FIELD:
return &JSAtomState::weekday;
case UDAT_AM_PM_FIELD:
return &JSAtomState::dayPeriod;
case UDAT_TIMEZONE_FIELD:
case UDAT_TIMEZONE_GENERIC_FIELD:
case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD:
return &JSAtomState::timeZoneName;
case UDAT_FRACTIONAL_SECOND_FIELD:
return &JSAtomState::fractionalSecond;
case UDAT_FLEXIBLE_DAY_PERIOD_FIELD:
return &JSAtomState::dayPeriod;
#ifndef U_HIDE_INTERNAL_API
case UDAT_RELATED_YEAR_FIELD:
case mozilla::intl::DateTimePartType::RelatedYear:
return &JSAtomState::relatedYear;
#endif
case UDAT_DAY_OF_YEAR_FIELD:
case UDAT_WEEK_OF_YEAR_FIELD:
case UDAT_WEEK_OF_MONTH_FIELD:
case UDAT_MILLISECONDS_IN_DAY_FIELD:
case UDAT_TIMEZONE_RFC_FIELD:
case UDAT_QUARTER_FIELD:
case UDAT_STANDALONE_QUARTER_FIELD:
case UDAT_TIMEZONE_SPECIAL_FIELD:
case UDAT_TIMEZONE_ISO_FIELD:
case UDAT_TIMEZONE_ISO_LOCAL_FIELD:
case UDAT_AM_PM_MIDNIGHT_NOON_FIELD:
#ifndef U_HIDE_INTERNAL_API
case UDAT_TIME_SEPARATOR_FIELD:
#endif
// These fields are all unsupported.
case mozilla::intl::DateTimePartType::Month:
return &JSAtomState::month;
case mozilla::intl::DateTimePartType::Day:
return &JSAtomState::day;
case mozilla::intl::DateTimePartType::Hour:
return &JSAtomState::hour;
case mozilla::intl::DateTimePartType::Minute:
return &JSAtomState::minute;
case mozilla::intl::DateTimePartType::Second:
return &JSAtomState::second;
case mozilla::intl::DateTimePartType::Weekday:
return &JSAtomState::weekday;
case mozilla::intl::DateTimePartType::DayPeriod:
return &JSAtomState::dayPeriod;
case mozilla::intl::DateTimePartType::TimeZoneName:
return &JSAtomState::timeZoneName;
case mozilla::intl::DateTimePartType::FractionalSecondDigits:
return &JSAtomState::fractionalSecond;
case mozilla::intl::DateTimePartType::Unknown:
return &JSAtomState::unknown;
#ifndef U_HIDE_DEPRECATED_API
case UDAT_FIELD_COUNT:
MOZ_ASSERT_UNREACHABLE(
"format field sentinel value returned by "
"iterator!");
#endif
}
MOZ_ASSERT_UNREACHABLE(
MOZ_CRASH(
"unenumerated, undocumented format field returned "
"by iterator");
return nullptr;
}
static bool intl_FormatToPartsDateTime(JSContext* cx,
const mozilla::intl::DateTimeFormat* df,
ClippedTime x, FieldType source,
MutableHandleValue result) {
MOZ_ASSERT(x.isValid());
UErrorCode status = U_ZERO_ERROR;
UFieldPositionIterator* fpositer = ufieldpositer_open(&status);
if (U_FAILURE(status)) {
intl::ReportInternalError(cx);
return false;
static FieldType GetFieldTypeForPartSource(
mozilla::intl::DateTimePartSource source) {
switch (source) {
case mozilla::intl::DateTimePartSource::Shared:
return &JSAtomState::shared;
case mozilla::intl::DateTimePartSource::StartRange:
return &JSAtomState::startRange;
case mozilla::intl::DateTimePartSource::EndRange:
return &JSAtomState::endRange;
}
ScopedICUObject<UFieldPositionIterator, ufieldpositer_close> toClose(
fpositer);
RootedString overallResult(cx);
overallResult = CallICU(cx, [df, x, fpositer](UChar* chars, int32_t size,
UErrorCode* status) {
return udat_formatForFields(
// TODO(Bug 1686965) - The use of UnsafeGetUDateFormat is a temporary
// migration step until the field position iterator is supported.
df->UnsafeGetUDateFormat(), x.toDouble(), chars, size, fpositer,
status);
});
MOZ_CRASH(
"unenumerated, undocumented format field returned "
"by iterator");
}
// A helper function to create an ArrayObject from DateTimePart objects.
// When hasNoSource is true, we don't need to create the ||Source|| property for
// the DateTimePart object.
static bool CreateDateTimePartArray(
JSContext* cx, mozilla::Span<const char16_t> formattedSpan,
bool hasNoSource, const mozilla::intl::DateTimePartVector& parts,
MutableHandleValue result) {
RootedString overallResult(cx, NewStringCopy<CanGC>(cx, formattedSpan));
if (!overallResult) {
return false;
}
RootedArrayObject partsArray(cx, NewDenseEmptyArray(cx));
RootedArrayObject partsArray(cx,
NewDenseFullyAllocatedArray(cx, parts.length()));
if (!partsArray) {
return false;
}
partsArray->ensureDenseInitializedLength(0, parts.length());
if (overallResult->length() == 0) {
// An empty string contains no parts, so avoid extra work below.
@ -1269,92 +1224,69 @@ static bool intl_FormatToPartsDateTime(JSContext* cx,
return true;
}
size_t lastEndIndex = 0;
RootedObject singlePart(cx);
RootedValue val(cx);
auto AppendPart = [&](FieldType type, size_t beginIndex, size_t endIndex) {
size_t index = 0;
size_t beginIndex = 0;
for (const mozilla::intl::DateTimePart& part : parts) {
singlePart = NewPlainObject(cx);
if (!singlePart) {
return false;
}
FieldType type = GetFieldTypeForPartType(part.mType);
val = StringValue(cx->names().*type);
if (!DefineDataProperty(cx, singlePart, cx->names().type, val)) {
return false;
}
JSLinearString* partSubstr = NewDependentString(
cx, overallResult, beginIndex, endIndex - beginIndex);
if (!partSubstr) {
MOZ_ASSERT(part.mEndIndex > beginIndex);
JSLinearString* partStr = NewDependentString(cx, overallResult, beginIndex,
part.mEndIndex - beginIndex);
if (!partStr) {
return false;
}
val = StringValue(partSubstr);
val = StringValue(partStr);
if (!DefineDataProperty(cx, singlePart, cx->names().value, val)) {
return false;
}
if (source) {
if (!hasNoSource) {
FieldType source = GetFieldTypeForPartSource(part.mSource);
val = StringValue(cx->names().*source);
if (!DefineDataProperty(cx, singlePart, cx->names().source, val)) {
return false;
}
}
if (!NewbornArrayPush(cx, partsArray, ObjectValue(*singlePart))) {
return false;
}
lastEndIndex = endIndex;
return true;
};
int32_t fieldInt, beginIndexInt, endIndexInt;
while ((fieldInt = ufieldpositer_next(fpositer, &beginIndexInt,
&endIndexInt)) >= 0) {
MOZ_ASSERT(beginIndexInt >= 0);
MOZ_ASSERT(endIndexInt >= 0);
MOZ_ASSERT(beginIndexInt <= endIndexInt,
"field iterator returning invalid range");
size_t beginIndex(beginIndexInt);
size_t endIndex(endIndexInt);
// Technically this isn't guaranteed. But it appears true in pratice,
// and http://bugs.icu-project.org/trac/ticket/12024 is expected to
// correct the documentation lapse.
MOZ_ASSERT(lastEndIndex <= beginIndex,
"field iteration didn't return fields in order start to "
"finish as expected");
if (FieldType type = GetFieldTypeForFormatField(
static_cast<UDateFormatField>(fieldInt))) {
if (lastEndIndex < beginIndex) {
if (!AppendPart(&JSAtomState::literal, lastEndIndex, beginIndex)) {
return false;
}
}
if (!AppendPart(type, beginIndex, endIndex)) {
return false;
}
}
}
// Append any final literal.
if (lastEndIndex < overallResult->length()) {
if (!AppendPart(&JSAtomState::literal, lastEndIndex,
overallResult->length())) {
return false;
}
beginIndex = part.mEndIndex;
partsArray->initDenseElement(index++, ObjectValue(*singlePart));
}
MOZ_ASSERT(index == parts.length());
MOZ_ASSERT(beginIndex == formattedSpan.size());
result.setObject(*partsArray);
return true;
}
static bool intl_FormatToPartsDateTime(JSContext* cx,
const mozilla::intl::DateTimeFormat* df,
ClippedTime x, bool hasNoSource,
MutableHandleValue result) {
MOZ_ASSERT(x.isValid());
FormatBuffer<char16_t, intl::INITIAL_CHAR_BUFFER_SIZE> buffer(cx);
mozilla::intl::DateTimePartVector parts;
auto r = df->TryFormatToParts(x.toDouble(), buffer, parts);
if (r.isErr()) {
intl::ReportInternalError(cx, r.unwrapErr());
return false;
}
return CreateDateTimePartArray(cx, buffer, hasNoSource, parts, result);
}
bool js::intl_FormatDateTime(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 3);
@ -1375,31 +1307,23 @@ bool js::intl_FormatDateTime(JSContext* cx, unsigned argc, Value* vp) {
return false;
}
// Obtain a cached DateTimeFormat object.
mozilla::intl::DateTimeFormat* df = dateTimeFormat->getDateFormat();
mozilla::intl::DateTimeFormat* df =
GetOrCreateDateTimeFormat(cx, dateTimeFormat);
if (!df) {
df = NewDateTimeFormat(cx, dateTimeFormat);
if (!df) {
return false;
}
dateTimeFormat->setDateFormat(df);
intl::AddICUCellMemory(dateTimeFormat,
DateTimeFormatObject::UDateFormatEstimatedMemoryUse);
return false;
}
// Use the UDateFormat to actually format the time stamp.
FieldType source = nullptr;
return formatToParts
? intl_FormatToPartsDateTime(cx, df, x, source, args.rval())
: intl_FormatDateTime(cx, df, x, args.rval());
// Use the DateTimeFormat to actually format the time stamp.
return formatToParts ? intl_FormatToPartsDateTime(
cx, df, x, /* hasNoSource */ true, args.rval())
: intl_FormatDateTime(cx, df, x, args.rval());
}
/**
* Returns a new UDateIntervalFormat with the locale and date-time formatting
* Returns a new DateIntervalFormat with the locale and date-time formatting
* options of the given DateTimeFormat.
*/
static UDateIntervalFormat* NewUDateIntervalFormat(
static mozilla::intl::DateIntervalFormat* NewDateIntervalFormat(
JSContext* cx, Handle<DateTimeFormatObject*> dateTimeFormat,
mozilla::intl::DateTimeFormat& mozDtf) {
RootedValue value(cx);
@ -1443,25 +1367,47 @@ static UDateIntervalFormat* NewUDateIntervalFormat(
return nullptr;
}
UErrorCode status = U_ZERO_ERROR;
UDateIntervalFormat* dif = udtitvfmt_open(
IcuLocale(locale.get()), skeleton.data(), skeleton.length(),
timeZoneChars.data(), timeZoneChars.size(), &status);
if (U_FAILURE(status)) {
intl::ReportInternalError(cx);
auto dif = mozilla::intl::DateIntervalFormat::TryCreate(
mozilla::MakeStringSpan(locale.get()), skeleton, timeZoneChars);
if (dif.isErr()) {
js::intl::ReportInternalError(cx, dif.unwrapErr());
return nullptr;
}
return dif.unwrap().release();
}
static mozilla::intl::DateIntervalFormat* GetOrCreateDateIntervalFormat(
JSContext* cx, Handle<DateTimeFormatObject*> dateTimeFormat,
mozilla::intl::DateTimeFormat& mozDtf) {
// Obtain a cached DateIntervalFormat object.
mozilla::intl::DateIntervalFormat* dif =
dateTimeFormat->getDateIntervalFormat();
if (dif) {
return dif;
}
dif = NewDateIntervalFormat(cx, dateTimeFormat, mozDtf);
if (!dif) {
return nullptr;
}
dateTimeFormat->setDateIntervalFormat(dif);
intl::AddICUCellMemory(
dateTimeFormat,
DateTimeFormatObject::UDateIntervalFormatEstimatedMemoryUse);
return dif;
}
/**
* PartitionDateTimeRangePattern ( dateTimeFormat, x, y )
*/
static const UFormattedValue* PartitionDateTimeRangePattern(
static bool PartitionDateTimeRangePattern(
JSContext* cx, const mozilla::intl::DateTimeFormat* df,
const UDateIntervalFormat* dif, UFormattedDateInterval* formatted,
ClippedTime x, ClippedTime y) {
const mozilla::intl::DateIntervalFormat* dif,
mozilla::intl::AutoFormattedDateInterval& formatted, ClippedTime x,
ClippedTime y, bool* equal) {
MOZ_ASSERT(x.isValid());
MOZ_ASSERT(y.isValid());
MOZ_ASSERT(x.toDouble() <= y.toDouble());
@ -1480,7 +1426,7 @@ static const UFormattedValue* PartitionDateTimeRangePattern(
constexpr double GregorianChangeDatePlusOneDay =
GregorianChangeDate + msPerDay;
UErrorCode status = U_ZERO_ERROR;
mozilla::intl::ICUResult result = Ok();
if (x.toDouble() < GregorianChangeDatePlusOneDay) {
// Create calendar objects for the start and end date by cloning the date
// formatter calendar. The date formatter calendar already has the correct
@ -1488,70 +1434,28 @@ static const UFormattedValue* PartitionDateTimeRangePattern(
auto startCal = df->CloneCalendar(x.toDouble());
if (startCal.isErr()) {
intl::ReportInternalError(cx, startCal.unwrapErr());
return nullptr;
return false;
}
auto endCal = df->CloneCalendar(y.toDouble());
if (endCal.isErr()) {
intl::ReportInternalError(cx, endCal.unwrapErr());
return nullptr;
return false;
}
udtitvfmt_formatCalendarToResult(
dif, startCal.unwrap()->UnsafeGetUCalendar(),
endCal.unwrap()->UnsafeGetUCalendar(), formatted, &status);
result = dif->TryFormatCalendar(*startCal.unwrap(), *endCal.unwrap(),
formatted, equal);
} else {
// The common fast path which doesn't require creating calendar objects.
udtitvfmt_formatToResult(dif, x.toDouble(), y.toDouble(), formatted,
&status);
}
if (U_FAILURE(status)) {
intl::ReportInternalError(cx);
return nullptr;
result =
dif->TryFormatDateTime(x.toDouble(), y.toDouble(), formatted, equal);
}
const UFormattedValue* formattedValue =
udtitvfmt_resultAsValue(formatted, &status);
if (U_FAILURE(status)) {
intl::ReportInternalError(cx);
return nullptr;
}
return formattedValue;
}
/**
* PartitionDateTimeRangePattern ( dateTimeFormat, x, y ), steps 9-11.
*
* Examine the formatted value to see if any interval span field is present.
*/
static bool DateFieldsPracticallyEqual(JSContext* cx,
const UFormattedValue* formattedValue,
bool* equal) {
UErrorCode status = U_ZERO_ERROR;
UConstrainedFieldPosition* fpos = ucfpos_open(&status);
if (U_FAILURE(status)) {
intl::ReportInternalError(cx);
return false;
}
ScopedICUObject<UConstrainedFieldPosition, ucfpos_close> toCloseFpos(fpos);
// We're only interested in UFIELD_CATEGORY_DATE_INTERVAL_SPAN fields.
ucfpos_constrainCategory(fpos, UFIELD_CATEGORY_DATE_INTERVAL_SPAN, &status);
if (U_FAILURE(status)) {
intl::ReportInternalError(cx);
if (result.isErr()) {
intl::ReportInternalError(cx, result.unwrapErr());
return false;
}
bool hasSpan = ufmtval_nextPosition(formattedValue, fpos, &status);
if (U_FAILURE(status)) {
intl::ReportInternalError(cx);
return false;
}
// When no date interval span field was found, both dates are "practically
// equal" per PartitionDateTimeRangePattern.
*equal = !hasSpan;
return true;
}
@ -1560,26 +1464,17 @@ static bool DateFieldsPracticallyEqual(JSContext* cx,
*/
static bool FormatDateTimeRange(JSContext* cx,
const mozilla::intl::DateTimeFormat* df,
const UDateIntervalFormat* dif, ClippedTime x,
ClippedTime y, MutableHandleValue result) {
UErrorCode status = U_ZERO_ERROR;
UFormattedDateInterval* formatted = udtitvfmt_openResult(&status);
if (U_FAILURE(status)) {
intl::ReportInternalError(cx);
return false;
}
ScopedICUObject<UFormattedDateInterval, udtitvfmt_closeResult> toClose(
formatted);
const UFormattedValue* formattedValue =
PartitionDateTimeRangePattern(cx, df, dif, formatted, x, y);
if (!formattedValue) {
const mozilla::intl::DateIntervalFormat* dif,
ClippedTime x, ClippedTime y,
MutableHandleValue result) {
mozilla::intl::AutoFormattedDateInterval formatted;
if (!formatted.IsValid()) {
intl::ReportInternalError(cx, formatted.GetError());
return false;
}
// PartitionDateTimeRangePattern, steps 9-11.
bool equal;
if (!DateFieldsPracticallyEqual(cx, formattedValue, &equal)) {
if (!PartitionDateTimeRangePattern(cx, df, dif, formatted, x, y, &equal)) {
return false;
}
@ -1588,7 +1483,12 @@ static bool FormatDateTimeRange(JSContext* cx,
return intl_FormatDateTime(cx, df, x, result);
}
JSString* resultStr = intl::FormattedValueToString(cx, formattedValue);
auto spanResult = formatted.ToSpan();
if (spanResult.isErr()) {
intl::ReportInternalError(cx, spanResult.unwrapErr());
return false;
}
JSString* resultStr = NewStringCopy<CanGC>(cx, spanResult.unwrap());
if (!resultStr) {
return false;
}
@ -1600,205 +1500,41 @@ static bool FormatDateTimeRange(JSContext* cx,
/**
* FormatDateTimeRangeToParts ( dateTimeFormat, x, y )
*/
static bool FormatDateTimeRangeToParts(JSContext* cx,
const mozilla::intl::DateTimeFormat* df,
const UDateIntervalFormat* dif,
ClippedTime x, ClippedTime y,
MutableHandleValue result) {
UErrorCode status = U_ZERO_ERROR;
UFormattedDateInterval* formatted = udtitvfmt_openResult(&status);
if (U_FAILURE(status)) {
intl::ReportInternalError(cx);
return false;
}
ScopedICUObject<UFormattedDateInterval, udtitvfmt_closeResult> toClose(
formatted);
const UFormattedValue* formattedValue =
PartitionDateTimeRangePattern(cx, df, dif, formatted, x, y);
if (!formattedValue) {
static bool FormatDateTimeRangeToParts(
JSContext* cx, const mozilla::intl::DateTimeFormat* df,
const mozilla::intl::DateIntervalFormat* dif, ClippedTime x, ClippedTime y,
MutableHandleValue result) {
mozilla::intl::AutoFormattedDateInterval formatted;
if (!formatted.IsValid()) {
intl::ReportInternalError(cx, formatted.GetError());
return false;
}
// PartitionDateTimeRangePattern, steps 9-11.
bool equal;
if (!DateFieldsPracticallyEqual(cx, formattedValue, &equal)) {
if (!PartitionDateTimeRangePattern(cx, df, dif, formatted, x, y, &equal)) {
return false;
}
// PartitionDateTimeRangePattern, step 12.
if (equal) {
FieldType source = &JSAtomState::shared;
return intl_FormatToPartsDateTime(cx, df, x, source, result);
return intl_FormatToPartsDateTime(cx, df, x, /* hasNoSource */ false,
result);
}
RootedString overallResult(cx,
intl::FormattedValueToString(cx, formattedValue));
if (!overallResult) {
mozilla::intl::DateTimePartVector parts;
auto r = dif->TryFormattedToParts(formatted, parts);
if (r.isErr()) {
intl::ReportInternalError(cx, r.unwrapErr());
return false;
}
RootedArrayObject partsArray(cx, NewDenseEmptyArray(cx));
if (!partsArray) {
auto spanResult = formatted.ToSpan();
if (spanResult.isErr()) {
intl::ReportInternalError(cx, spanResult.unwrapErr());
return false;
}
size_t lastEndIndex = 0;
RootedObject singlePart(cx);
RootedValue val(cx);
auto AppendPart = [&](FieldType type, size_t beginIndex, size_t endIndex,
FieldType source) {
singlePart = NewPlainObject(cx);
if (!singlePart) {
return false;
}
val = StringValue(cx->names().*type);
if (!DefineDataProperty(cx, singlePart, cx->names().type, val)) {
return false;
}
JSLinearString* partSubstr = NewDependentString(
cx, overallResult, beginIndex, endIndex - beginIndex);
if (!partSubstr) {
return false;
}
val = StringValue(partSubstr);
if (!DefineDataProperty(cx, singlePart, cx->names().value, val)) {
return false;
}
val = StringValue(cx->names().*source);
if (!DefineDataProperty(cx, singlePart, cx->names().source, val)) {
return false;
}
if (!NewbornArrayPush(cx, partsArray, ObjectValue(*singlePart))) {
return false;
}
lastEndIndex = endIndex;
return true;
};
UConstrainedFieldPosition* fpos = ucfpos_open(&status);
if (U_FAILURE(status)) {
intl::ReportInternalError(cx);
return false;
}
ScopedICUObject<UConstrainedFieldPosition, ucfpos_close> toCloseFpos(fpos);
size_t categoryEndIndex = 0;
FieldType source = &JSAtomState::shared;
while (true) {
bool hasMore = ufmtval_nextPosition(formattedValue, fpos, &status);
if (U_FAILURE(status)) {
intl::ReportInternalError(cx);
return false;
}
if (!hasMore) {
break;
}
int32_t category = ucfpos_getCategory(fpos, &status);
if (U_FAILURE(status)) {
intl::ReportInternalError(cx);
return false;
}
int32_t field = ucfpos_getField(fpos, &status);
if (U_FAILURE(status)) {
intl::ReportInternalError(cx);
return false;
}
int32_t beginIndexInt, endIndexInt;
ucfpos_getIndexes(fpos, &beginIndexInt, &endIndexInt, &status);
if (U_FAILURE(status)) {
intl::ReportInternalError(cx);
return false;
}
MOZ_ASSERT(beginIndexInt >= 0);
MOZ_ASSERT(endIndexInt >= 0);
MOZ_ASSERT(beginIndexInt <= endIndexInt,
"field iterator returning invalid range");
size_t beginIndex = size_t(beginIndexInt);
size_t endIndex = size_t(endIndexInt);
// Indices are guaranteed to be returned in order (from left to right).
MOZ_ASSERT(lastEndIndex <= beginIndex,
"field iteration didn't return fields in order start to "
"finish as expected");
if (category == UFIELD_CATEGORY_DATE_INTERVAL_SPAN) {
// Append any remaining literal parts before changing the source kind.
if (lastEndIndex < beginIndex) {
if (!AppendPart(&JSAtomState::literal, lastEndIndex, beginIndex,
source)) {
return false;
}
}
// The special field category UFIELD_CATEGORY_DATE_INTERVAL_SPAN has only
// two allowed values (0 or 1), indicating the begin of the start- resp.
// end-date.
MOZ_ASSERT(field == 0 || field == 1,
"span category has unexpected value");
source = field == 0 ? &JSAtomState::startRange : &JSAtomState::endRange;
categoryEndIndex = endIndex;
continue;
}
// Ignore categories other than UFIELD_CATEGORY_DATE.
if (category != UFIELD_CATEGORY_DATE) {
continue;
}
// Append the field if supported. If not supported, append it as part of the
// next literal part.
if (FieldType type =
GetFieldTypeForFormatField(static_cast<UDateFormatField>(field))) {
if (lastEndIndex < beginIndex) {
if (!AppendPart(&JSAtomState::literal, lastEndIndex, beginIndex,
source)) {
return false;
}
}
if (!AppendPart(type, beginIndex, endIndex, source)) {
return false;
}
}
if (endIndex == categoryEndIndex) {
// Append any remaining literal parts before changing the source kind.
if (lastEndIndex < endIndex) {
if (!AppendPart(&JSAtomState::literal, lastEndIndex, endIndex,
source)) {
return false;
}
}
source = &JSAtomState::shared;
}
}
// Append any final literal.
if (lastEndIndex < overallResult->length()) {
if (!AppendPart(&JSAtomState::literal, lastEndIndex,
overallResult->length(), source)) {
return false;
}
}
result.setObject(*partsArray);
return true;
return CreateDateTimePartArray(cx, spanResult.unwrap(),
/* hasNoSource */ false, parts, result);
}
bool js::intl_FormatDateTimeRange(JSContext* cx, unsigned argc, Value* vp) {
@ -1836,34 +1572,19 @@ bool js::intl_FormatDateTimeRange(JSContext* cx, unsigned argc, Value* vp) {
MOZ_ASSERT(x.toDouble() <= y.toDouble(),
"start date mustn't be after the end date");
// Obtain a cached mozilla::intl::DateTimeFormat object.
mozilla::intl::DateTimeFormat* df = dateTimeFormat->getDateFormat();
mozilla::intl::DateTimeFormat* df =
GetOrCreateDateTimeFormat(cx, dateTimeFormat);
if (!df) {
df = NewDateTimeFormat(cx, dateTimeFormat);
if (!df) {
return false;
}
dateTimeFormat->setDateFormat(df);
intl::AddICUCellMemory(dateTimeFormat,
DateTimeFormatObject::UDateFormatEstimatedMemoryUse);
return false;
}
// Obtain a cached UDateIntervalFormat object.
UDateIntervalFormat* dif = dateTimeFormat->getDateIntervalFormat();
mozilla::intl::DateIntervalFormat* dif =
GetOrCreateDateIntervalFormat(cx, dateTimeFormat, *df);
if (!dif) {
dif = NewUDateIntervalFormat(cx, dateTimeFormat, *df);
if (!dif) {
return false;
}
dateTimeFormat->setDateIntervalFormat(dif);
intl::AddICUCellMemory(
dateTimeFormat,
DateTimeFormatObject::UDateIntervalFormatEstimatedMemoryUse);
return false;
}
// Use the UDateIntervalFormat to actually format the time range.
// Use the DateIntervalFormat to actually format the time range.
return formatToParts
? FormatDateTimeRangeToParts(cx, df, dif, x, y, args.rval())
: FormatDateTimeRange(cx, df, dif, x, y, args.rval());

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

@ -13,11 +13,10 @@
#include "js/RootingAPI.h"
#include "vm/NativeObject.h"
struct UDateIntervalFormat;
namespace mozilla::intl {
class DateTimeFormat;
}
class DateIntervalFormat;
} // namespace mozilla::intl
namespace js {
@ -27,8 +26,8 @@ class DateTimeFormatObject : public NativeObject {
static const JSClass& protoClass_;
static constexpr uint32_t INTERNALS_SLOT = 0;
static constexpr uint32_t UDATE_FORMAT_SLOT = 1;
static constexpr uint32_t UDATE_INTERVAL_FORMAT_SLOT = 2;
static constexpr uint32_t DATE_FORMAT_SLOT = 1;
static constexpr uint32_t DATE_INTERVAL_FORMAT_SLOT = 2;
static constexpr uint32_t SLOT_COUNT = 3;
static_assert(INTERNALS_SLOT == INTL_INTERNALS_OBJECT_SLOT,
@ -42,7 +41,7 @@ class DateTimeFormatObject : public NativeObject {
static constexpr size_t UDateIntervalFormatEstimatedMemoryUse = 133064;
mozilla::intl::DateTimeFormat* getDateFormat() const {
const auto& slot = getFixedSlot(UDATE_FORMAT_SLOT);
const auto& slot = getFixedSlot(DATE_FORMAT_SLOT);
if (slot.isUndefined()) {
return nullptr;
}
@ -50,19 +49,20 @@ class DateTimeFormatObject : public NativeObject {
}
void setDateFormat(mozilla::intl::DateTimeFormat* dateFormat) {
setFixedSlot(UDATE_FORMAT_SLOT, PrivateValue(dateFormat));
setFixedSlot(DATE_FORMAT_SLOT, PrivateValue(dateFormat));
}
UDateIntervalFormat* getDateIntervalFormat() const {
const auto& slot = getFixedSlot(UDATE_INTERVAL_FORMAT_SLOT);
mozilla::intl::DateIntervalFormat* getDateIntervalFormat() const {
const auto& slot = getFixedSlot(DATE_INTERVAL_FORMAT_SLOT);
if (slot.isUndefined()) {
return nullptr;
}
return static_cast<UDateIntervalFormat*>(slot.toPrivate());
return static_cast<mozilla::intl::DateIntervalFormat*>(slot.toPrivate());
}
void setDateIntervalFormat(UDateIntervalFormat* dateIntervalFormat) {
setFixedSlot(UDATE_INTERVAL_FORMAT_SLOT, PrivateValue(dateIntervalFormat));
void setDateIntervalFormat(
mozilla::intl::DateIntervalFormat* dateIntervalFormat) {
setFixedSlot(DATE_INTERVAL_FORMAT_SLOT, PrivateValue(dateIntervalFormat));
}
private:

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

@ -210,6 +210,24 @@ static mozilla::intl::ListFormat* NewListFormat(
return nullptr;
}
static mozilla::intl::ListFormat* GetOrCreateListFormat(
JSContext* cx, Handle<ListFormatObject*> listFormat) {
// Obtain a cached mozilla::intl::ListFormat object.
mozilla::intl::ListFormat* lf = listFormat->getListFormatSlot();
if (lf) {
return lf;
}
lf = NewListFormat(cx, listFormat);
if (!lf) {
return nullptr;
}
listFormat->setListFormatSlot(lf);
intl::AddICUCellMemory(listFormat, ListFormatObject::EstimatedMemoryUse);
return lf;
}
/**
* FormatList ( listFormat, list )
*/
@ -300,16 +318,9 @@ bool js::intl_FormatList(JSContext* cx, unsigned argc, Value* vp) {
bool formatToParts = args[2].toBoolean();
// Obtain a cached mozilla::intl::ListFormat object.
mozilla::intl::ListFormat* lf = listFormat->getListFormatSlot();
mozilla::intl::ListFormat* lf = GetOrCreateListFormat(cx, listFormat);
if (!lf) {
lf = NewListFormat(cx, listFormat);
if (!lf) {
return false;
}
listFormat->setListFormatSlot(lf);
intl::AddICUCellMemory(listFormat, ListFormatObject::EstimatedMemoryUse);
return false;
}
// Collect all strings and their lengths.

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

@ -778,6 +778,44 @@ static Formatter* NewNumberFormat(JSContext* cx,
return nullptr;
}
static mozilla::intl::NumberFormat* GetOrCreateNumberFormat(
JSContext* cx, Handle<NumberFormatObject*> numberFormat) {
// Obtain a cached mozilla::intl::NumberFormat object.
mozilla::intl::NumberFormat* nf = numberFormat->getNumberFormatter();
if (nf) {
return nf;
}
nf = NewNumberFormat<mozilla::intl::NumberFormat>(cx, numberFormat);
if (!nf) {
return nullptr;
}
numberFormat->setNumberFormatter(nf);
intl::AddICUCellMemory(numberFormat, NumberFormatObject::EstimatedMemoryUse);
return nf;
}
static mozilla::intl::NumberRangeFormat* GetOrCreateNumberRangeFormat(
JSContext* cx, Handle<NumberFormatObject*> numberFormat) {
// Obtain a cached mozilla::intl::NumberRangeFormat object.
mozilla::intl::NumberRangeFormat* nrf =
numberFormat->getNumberRangeFormatter();
if (nrf) {
return nrf;
}
nrf = NewNumberFormat<mozilla::intl::NumberRangeFormat>(cx, numberFormat);
if (!nrf) {
return nullptr;
}
numberFormat->setNumberRangeFormatter(nrf);
intl::AddICUCellMemory(numberFormat,
NumberFormatObject::EstimatedRangeFormatterMemoryUse);
return nrf;
}
static FieldType GetFieldTypeForNumberPartType(
mozilla::intl::NumberPartType type) {
switch (type) {
@ -1126,17 +1164,9 @@ bool js::intl_FormatNumber(JSContext* cx, unsigned argc, Value* vp) {
}
#endif
// Obtain a cached mozilla::intl::NumberFormat object.
mozilla::intl::NumberFormat* nf = numberFormat->getNumberFormatter();
mozilla::intl::NumberFormat* nf = GetOrCreateNumberFormat(cx, numberFormat);
if (!nf) {
nf = NewNumberFormat<mozilla::intl::NumberFormat>(cx, numberFormat);
if (!nf) {
return false;
}
numberFormat->setNumberFormatter(nf);
intl::AddICUCellMemory(numberFormat,
NumberFormatObject::EstimatedMemoryUse);
return false;
}
// Actually format the number
@ -1466,18 +1496,10 @@ bool js::intl_FormatNumberRange(JSContext* cx, unsigned argc, Value* vp) {
return false;
}
// Obtain a cached mozilla::intl::NumberFormat object.
using NumberRangeFormat = mozilla::intl::NumberRangeFormat;
NumberRangeFormat* nf = numberFormat->getNumberRangeFormatter();
NumberRangeFormat* nf = GetOrCreateNumberRangeFormat(cx, numberFormat);
if (!nf) {
nf = NewNumberFormat<NumberRangeFormat>(cx, numberFormat);
if (!nf) {
return false;
}
numberFormat->setNumberRangeFormatter(nf);
intl::AddICUCellMemory(
numberFormat, NumberFormatObject::EstimatedRangeFormatterMemoryUse);
return false;
}
auto valueRepresentableAsDouble = [](const Value& val, double* num) {

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

@ -291,6 +291,25 @@ static mozilla::intl::PluralRules* NewPluralRules(
return result.unwrap().release();
}
static mozilla::intl::PluralRules* GetOrCreatePluralRules(
JSContext* cx, Handle<PluralRulesObject*> pluralRules) {
// Obtain a cached PluralRules object.
mozilla::intl::PluralRules* pr = pluralRules->getPluralRules();
if (pr) {
return pr;
}
pr = NewPluralRules(cx, pluralRules);
if (!pr) {
return nullptr;
}
pluralRules->setPluralRules(pr);
intl::AddICUCellMemory(pluralRules,
PluralRulesObject::UPluralRulesEstimatedMemoryUse);
return pr;
}
bool js::intl_SelectPluralRule(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
@ -300,18 +319,10 @@ bool js::intl_SelectPluralRule(JSContext* cx, unsigned argc, Value* vp) {
double x = args[1].toNumber();
// Obtain a cached PluralRules object.
using PluralRules = mozilla::intl::PluralRules;
PluralRules* pr = pluralRules->getPluralRules();
PluralRules* pr = GetOrCreatePluralRules(cx, pluralRules);
if (!pr) {
pr = NewPluralRules(cx, pluralRules);
if (!pr) {
return false;
}
pluralRules->setPluralRules(pr);
intl::AddICUCellMemory(pluralRules,
PluralRulesObject::UPluralRulesEstimatedMemoryUse);
return false;
}
auto keywordResult = pr->Select(x);
@ -345,18 +356,10 @@ bool js::intl_SelectPluralRuleRange(JSContext* cx, unsigned argc, Value* vp) {
return false;
}
// Obtain a cached PluralRules object.
using PluralRules = mozilla::intl::PluralRules;
PluralRules* pr = pluralRules->getPluralRules();
PluralRules* pr = GetOrCreatePluralRules(cx, pluralRules);
if (!pr) {
pr = NewPluralRules(cx, pluralRules);
if (!pr) {
return false;
}
pluralRules->setPluralRules(pr);
intl::AddICUCellMemory(pluralRules,
PluralRulesObject::UPluralRulesEstimatedMemoryUse);
return false;
}
auto keywordResult = pr->SelectRange(x, y);
@ -382,18 +385,10 @@ bool js::intl_GetPluralCategories(JSContext* cx, unsigned argc, Value* vp) {
Rooted<PluralRulesObject*> pluralRules(
cx, &args[0].toObject().as<PluralRulesObject>());
// Obtain a cached PluralRules object.
using PluralRules = mozilla::intl::PluralRules;
PluralRules* pr = pluralRules->getPluralRules();
PluralRules* pr = GetOrCreatePluralRules(cx, pluralRules);
if (!pr) {
pr = NewPluralRules(cx, pluralRules);
if (!pr) {
return false;
}
pluralRules->setPluralRules(pr);
intl::AddICUCellMemory(pluralRules,
PluralRulesObject::UPluralRulesEstimatedMemoryUse);
return false;
}
auto categoriesResult = pr->Categories();

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

@ -267,6 +267,26 @@ static mozilla::intl::RelativeTimeFormat* NewRelativeTimeFormatter(
return nullptr;
}
static mozilla::intl::RelativeTimeFormat* GetOrCreateRelativeTimeFormat(
JSContext* cx, Handle<RelativeTimeFormatObject*> relativeTimeFormat) {
// Obtain a cached RelativeDateTimeFormatter object.
mozilla::intl::RelativeTimeFormat* rtf =
relativeTimeFormat->getRelativeTimeFormatter();
if (rtf) {
return rtf;
}
rtf = NewRelativeTimeFormatter(cx, relativeTimeFormat);
if (!rtf) {
return nullptr;
}
relativeTimeFormat->setRelativeTimeFormatter(rtf);
intl::AddICUCellMemory(relativeTimeFormat,
RelativeTimeFormatObject::EstimatedMemoryUse);
return rtf;
}
bool js::intl_FormatRelativeTime(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 4);
@ -289,18 +309,10 @@ bool js::intl_FormatRelativeTime(JSContext* cx, unsigned argc, Value* vp) {
return false;
}
// Obtain a cached URelativeDateTimeFormatter object.
mozilla::intl::RelativeTimeFormat* rtf =
relativeTimeFormat->getRelativeTimeFormatter();
GetOrCreateRelativeTimeFormat(cx, relativeTimeFormat);
if (!rtf) {
rtf = NewRelativeTimeFormatter(cx, relativeTimeFormat);
if (!rtf) {
return false;
}
relativeTimeFormat->setRelativeTimeFormatter(rtf);
intl::AddICUCellMemory(relativeTimeFormat,
RelativeTimeFormatObject::EstimatedMemoryUse);
return false;
}
intl::FieldType jsUnitType;

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

@ -4091,9 +4091,7 @@ JSObject* JS::InstantiateModuleStencil(JSContext* cx,
return gcOutput.get().module;
}
JS::TranscodeResult JS::EncodeStencil(JSContext* cx,
const JS::ReadOnlyCompileOptions& options,
JS::Stencil* stencil,
JS::TranscodeResult JS::EncodeStencil(JSContext* cx, JS::Stencil* stencil,
TranscodeBuffer& buffer) {
XDRStencilEncoder encoder(cx, buffer);
XDRResult res = encoder.codeStencil(*stencil);

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

@ -213,7 +213,7 @@ BEGIN_TEST(testStencil_Transcode) {
CHECK(stencil);
// Encode Stencil to XDR
JS::TranscodeResult res = JS::EncodeStencil(cx, options, stencil, buffer);
JS::TranscodeResult res = JS::EncodeStencil(cx, stencil, buffer);
CHECK(res == JS::TranscodeResult::Ok);
CHECK(!buffer.empty());
@ -291,7 +291,7 @@ BEGIN_TEST(testStencil_TranscodeBorrowing) {
CHECK(stencil);
// Encode Stencil to XDR
JS::TranscodeResult res = JS::EncodeStencil(cx, options, stencil, buffer);
JS::TranscodeResult res = JS::EncodeStencil(cx, stencil, buffer);
CHECK(res == JS::TranscodeResult::Ok);
CHECK(!buffer.empty());
}

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

@ -2245,7 +2245,7 @@ static bool StartIncrementalEncoding(JSContext* cx,
return false;
}
if (!source->startIncrementalEncoding(cx, options, std::move(initial))) {
if (!source->startIncrementalEncoding(cx, std::move(initial))) {
return false;
}

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

@ -116,7 +116,7 @@ static JSScript* CompileSourceBufferAndStartIncrementalEncoding(
}
}
if (!script->scriptSource()->startIncrementalEncoding(cx, options,
if (!script->scriptSource()->startIncrementalEncoding(cx,
std::move(stencil))) {
return nullptr;
}

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

@ -2220,13 +2220,12 @@ JSScript* GlobalHelperThreadState::finishSingleParseTask(
}
if (!script->scriptSource()->startIncrementalEncoding(
cx, parseTask->options, std::move(initial))) {
cx, std::move(initial))) {
return nullptr;
}
} else if (parseTask->extensibleStencil_) {
if (!script->scriptSource()->startIncrementalEncoding(
cx, parseTask->options,
std::move(parseTask->extensibleStencil_))) {
cx, std::move(parseTask->extensibleStencil_))) {
return nullptr;
}
}

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

@ -1787,7 +1787,7 @@ void ScriptSource::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
}
bool ScriptSource::startIncrementalEncoding(
JSContext* cx, const JS::ReadOnlyCompileOptions& options,
JSContext* cx,
UniquePtr<frontend::ExtensibleCompilationStencil>&& initial) {
// Encoding failures are reported by the xdrFinalizeEncoder function.
if (containsAsmJS()) {
@ -1808,7 +1808,7 @@ bool ScriptSource::startIncrementalEncoding(
mozilla::MakeScopeExit([&] { xdrEncoder_.reset(nullptr); });
XDRResult res = xdrEncoder_->setInitial(
cx, options,
cx,
std::forward<UniquePtr<frontend::ExtensibleCompilationStencil>>(initial));
if (res.isErr()) {
// On encoding failure, let failureCase destroy encoder and return true

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

@ -1029,7 +1029,7 @@ class ScriptSource {
bool hasEncoder() const { return bool(xdrEncoder_); }
[[nodiscard]] bool startIncrementalEncoding(
JSContext* cx, const JS::ReadOnlyCompileOptions& options,
JSContext* cx,
UniquePtr<frontend::ExtensibleCompilationStencil>&& initial);
[[nodiscard]] bool addDelazificationToIncrementalEncoding(

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

@ -291,7 +291,7 @@ XDRIncrementalStencilEncoder::~XDRIncrementalStencilEncoder() {
}
XDRResult XDRIncrementalStencilEncoder::setInitial(
JSContext* cx, const JS::ReadOnlyCompileOptions& options,
JSContext* cx,
UniquePtr<frontend::ExtensibleCompilationStencil>&& initial) {
MOZ_TRY(frontend::StencilXDR::checkCompilationStencil(*initial));

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