Merge autoland to mozilla-central. a=merge

This commit is contained in:
Butkovits Atila 2022-07-09 12:34:00 +03:00
Родитель b917ced89f 5125d899f8
Коммит 2f40a88cf8
467 изменённых файлов: 15866 добавлений и 8878 удалений

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

@ -180,6 +180,9 @@ pref("app.update.langpack.enabled", true);
pref("app.update.background.timeoutSec", 600);
// By default, check for updates when the browser is not running every 7 hours.
pref("app.update.background.interval", 25200);
// By default, snapshot Firefox Messaging System targeting for use by the
// background update task every 30 minutes.
pref("app.update.background.messaging.targeting.snapshot.intervalSec", 1800);
#endif
#ifdef XP_MACOSX

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

@ -2796,6 +2796,14 @@ BrowserGlue.prototype = {
lazy.RemoteAgent.running) &&
Services.prefs.getBoolPref("app.update.disabledForTesting", false);
if (!disabledForTesting) {
try {
lazy.BackgroundUpdate.scheduleFirefoxMessagingSystemTargetingSnapshotting();
} catch (e) {
Cu.reportError(
"There was an error scheduling Firefox Messaging System targeting snapshotting: " +
e
);
}
lazy.BackgroundUpdate.maybeScheduleBackgroundUpdateTask();
}
},

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

@ -13,7 +13,9 @@ const { AddonManager } = ChromeUtils.import(
const INTENSITY_SOFT = "soft";
const INTENSITY_BALANCED = "balanced";
const INTENSITY_BOLD = "bold";
const ID_SUFFIX_FOR_PRIMARY_INTENSITY = `-${INTENSITY_BALANCED}-colorway@mozilla.org`;
const ID_SUFFIX_COLORWAY = "-colorway@mozilla.org";
const ID_SUFFIX_PRIMARY_INTENSITY = `-${INTENSITY_BALANCED}${ID_SUFFIX_COLORWAY}`;
const ID_SUFFIX_DARK_COLORWAY = `-${INTENSITY_BOLD}${ID_SUFFIX_COLORWAY}`;
const ID_SUFFIXES_FOR_SECONDARY_INTENSITIES = new RegExp(
`-(${INTENSITY_SOFT}|${INTENSITY_BOLD})-colorway@mozilla\\.org$`
);
@ -89,12 +91,21 @@ const ColorwayCloset = {
}
// If the current active theme is part of our collection, make the UI reflect
// that. Otherwise go ahead and enable the first theme in our list.
// that. Otherwise go ahead and enable the first colorway in our list.
this.selectedColorway = this.colorways.find(colorway => colorway.isActive);
if (this.selectedColorway) {
this.refresh();
} else {
this.colorwayGroups[0].enable();
let colorwayToEnable = this.colorwayGroups[0];
// If the user has been using a theme with a dark color scheme, make an
// effort to default to a colorway with a dark color scheme as well.
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
let firstDarkColorway = this.colorways.find(colorway =>
colorway.id.endsWith(ID_SUFFIX_DARK_COLORWAY)
);
colorwayToEnable = firstDarkColorway || colorwayToEnable;
}
colorwayToEnable.enable();
}
},
@ -174,7 +185,7 @@ const ColorwayCloset = {
_getColorwayGroupId(colorwayId) {
let groupId = colorwayId.replace(
ID_SUFFIXES_FOR_SECONDARY_INTENSITIES,
ID_SUFFIX_FOR_PRIMARY_INTENSITY
ID_SUFFIX_PRIMARY_INTENSITY
);
return this.colorwayGroups.map(addon => addon.id).includes(groupId)
? groupId
@ -184,7 +195,7 @@ const ColorwayCloset = {
_changeIntensity(colorwayId, intensity) {
return colorwayId.replace(
MATCH_INTENSITY_FROM_ID,
`-${intensity}-colorway@mozilla.org`
`-${intensity}${ID_SUFFIX_COLORWAY}`
);
},
@ -233,7 +244,7 @@ const ColorwayCloset = {
this.selectedColorway.id
);
this.hasIntensities = this.groupIdForSelectedColorway.endsWith(
ID_SUFFIX_FOR_PRIMARY_INTENSITY
ID_SUFFIX_PRIMARY_INTENSITY
);
for (let input of this.el.colorwayRadios.children) {
if (input.value == this.groupIdForSelectedColorway) {

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

@ -737,8 +737,11 @@
}
}
}
},
"secondary": {
}
},
"secondary": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
@ -749,7 +752,7 @@
"value": {
"allOf": [
{
"$ref": "#/definitions/plainText"
"$ref": "file:///ExtensionDoorhanger.schema.json#/definitions/plainText"
},
{
"description": "Button label override used when a localized version is not available."
@ -780,7 +783,7 @@
"string_id": {
"allOf": [
{
"$ref": "#/definitions/plainText"
"$ref": "file:///ExtensionDoorhanger.schema.json#/definitions/plainText"
},
{
"description": "Id of localized string for button"
@ -807,7 +810,7 @@
"url": {
"allOf": [
{
"$ref": "#/definitions/linkUrl"
"$ref": "file:///ExtensionDoorhanger.schema.json#/definitions/linkUrl"
},
{
"description": "URL used in combination with the primary action dispatched."

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

@ -0,0 +1,697 @@
[
{
"id": "WNP_THANK_YOU",
"template": "update_action",
"content": {
"action": {
"id": "moments-wnp",
"data": {
"url": "https://www.mozilla.org/%LOCALE%/etc/firefox/retention/thank-you-a/",
"expireDelta": 172800000
}
}
},
"trigger": {
"id": "momentsUpdate"
}
},
{
"id": "WHATS_NEW_FINGERPRINTER_COUNTER_ALT",
"template": "whatsnew_panel_message",
"order": 6,
"content": {
"bucket_id": "WHATS_NEW_72",
"published_date": 1574776601000,
"title": "Title",
"icon_url": "chrome://activity-stream/content/data/content/assets/protection-report-icon.png",
"icon_alt": {
"string_id": "cfr-badge-reader-label-newfeature"
},
"body": "Message body",
"link_text": "Click here",
"cta_url": "about:blank",
"cta_type": "OPEN_PROTECTION_REPORT"
},
"targeting": "firefoxVersion >= 72",
"trigger": {
"id": "whatsNewPanelOpened"
}
},
{
"id": "WHATS_NEW_70_1",
"template": "whatsnew_panel_message",
"order": 3,
"content": {
"bucket_id": "WHATS_NEW_70_1",
"published_date": 1560969794394,
"title": "Protection Is Our Focus",
"icon_url": "chrome://activity-stream/content/data/content/assets/whatsnew-send-icon.png",
"icon_alt": "Firefox Send Logo",
"body": "The New Enhanced Tracking Protection, gives you the best level of protection and performance. Discover how this version is the safest version of firefox ever made.",
"cta_url": "https://blog.mozilla.org/",
"cta_type": "OPEN_URL"
},
"targeting": "firefoxVersion > 69",
"trigger": {
"id": "whatsNewPanelOpened"
}
},
{
"id": "WHATS_NEW_70_2",
"template": "whatsnew_panel_message",
"order": 1,
"content": {
"bucket_id": "WHATS_NEW_70_1",
"published_date": 1560969794394,
"title": "Another thing new in Firefox 70",
"body": "The New Enhanced Tracking Protection, gives you the best level of protection and performance. Discover how this version is the safest version of firefox ever made.",
"link_text": "Learn more on our blog",
"cta_url": "https://blog.mozilla.org/",
"cta_type": "OPEN_URL"
},
"targeting": "firefoxVersion > 69",
"trigger": {
"id": "whatsNewPanelOpened"
}
},
{
"id": "WHATS_NEW_SEARCH_SHORTCUTS_84",
"template": "whatsnew_panel_message",
"order": 2,
"content": {
"bucket_id": "WHATS_NEW_SEARCH_SHORTCUTS_84",
"published_date": 1560969794394,
"title": "Title",
"icon_url": "chrome://global/skin/icons/check.svg",
"icon_alt": "",
"body": "Message content",
"cta_url": "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/search-shortcuts",
"cta_type": "OPEN_URL",
"link_text": "Click here"
},
"targeting": "firefoxVersion >= 84",
"trigger": {
"id": "whatsNewPanelOpened"
}
},
{
"id": "WHATS_NEW_PIONEER_82",
"template": "whatsnew_panel_message",
"order": 1,
"content": {
"bucket_id": "WHATS_NEW_PIONEER_82",
"published_date": 1603152000000,
"title": "Put your data to work for a better internet",
"body": "Contribute your data to Mozilla's Pioneer program to help researchers understand pressing technology issues like misinformation, data privacy, and ethical AI.",
"cta_url": "about:blank",
"cta_where": "tab",
"cta_type": "OPEN_ABOUT_PAGE",
"link_text": "Join Pioneer"
},
"targeting": "firefoxVersion >= 82",
"trigger": {
"id": "whatsNewPanelOpened"
}
},
{
"id": "WHATS_NEW_MEDIA_SESSION_82",
"template": "whatsnew_panel_message",
"order": 3,
"content": {
"bucket_id": "WHATS_NEW_MEDIA_SESSION_82",
"published_date": 1603152000000,
"title": "Title",
"body": "Message content",
"cta_url": "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/media-keyboard-control",
"cta_type": "OPEN_URL",
"link_text": "Click here"
},
"targeting": "firefoxVersion >= 82",
"trigger": {
"id": "whatsNewPanelOpened"
}
},
{
"id": "WHATS_NEW_69_1",
"template": "whatsnew_panel_message",
"order": 1,
"content": {
"bucket_id": "WHATS_NEW_69_1",
"published_date": 1557346235089,
"title": "Something new in Firefox 69",
"body": "The New Enhanced Tracking Protection, gives you the best level of protection and performance. Discover how this version is the safest version of firefox ever made.",
"link_text": "Learn more on our blog",
"cta_url": "https://blog.mozilla.org/",
"cta_type": "OPEN_URL"
},
"targeting": "firefoxVersion > 68",
"trigger": {
"id": "whatsNewPanelOpened"
}
},
{
"id": "PERSONALIZED_CFR_MESSAGE",
"template": "cfr_doorhanger",
"groups": [
"cfr"
],
"content": {
"layout": "icon_and_message",
"category": "cfrFeatures",
"bucket_id": "PERSONALIZED_CFR_MESSAGE",
"notification_text": "Personalized CFR Recommendation",
"heading_text": {
"string_id": "cfr-doorhanger-bookmark-fxa-header"
},
"info_icon": {
"label": {
"attributes": {
"tooltiptext": {
"string_id": "cfr-doorhanger-fxa-close-btn-tooltip"
}
}
},
"sumo_path": "https://example.com"
},
"text": {
"string_id": "cfr-doorhanger-bookmark-fxa-body"
},
"icon": "chrome://branding/content/icon64.png",
"icon_class": "cfr-doorhanger-large-icon",
"persistent_doorhanger": true,
"buttons": {
"primary": {
"label": {
"string_id": "cfr-doorhanger-milestone-ok-button"
},
"action": {
"type": "OPEN_URL",
"data": {
"args": "https://send.firefox.com/login/?utm_source=activity-stream&entrypoint=activity-stream-cfr-pdf",
"where": "tabshifted"
}
}
},
"secondary": [
{
"label": {
"string_id": "cfr-doorhanger-extension-cancel-button"
},
"action": {
"type": "CANCEL"
}
},
{
"label": {
"string_id": "cfr-doorhanger-extension-never-show-recommendation"
}
},
{
"label": {
"string_id": "cfr-doorhanger-extension-manage-settings-button"
},
"action": {
"type": "OPEN_PREFERENCES_PAGE",
"data": {
"category": "general-cfrfeatures"
}
}
}
]
}
},
"targeting": "scores.PERSONALIZED_CFR_MESSAGE.score > scoreThreshold",
"trigger": {
"id": "openURL",
"patterns": [
"*://*/*.pdf"
]
}
},
{
"id": "SPOTLIGHT_MESSAGE_93",
"template": "spotlight",
"groups": [
"panel-test-provider"
],
"content": {
"template": "logo-and-content",
"logo": {
"imageURL": "chrome://browser/content/logos/vpn-promo-logo.svg"
},
"body": {
"title": {
"label": {
"string_id": "spotlight-public-wifi-vpn-header"
}
},
"text": {
"label": {
"string_id": "spotlight-public-wifi-vpn-body"
}
},
"primary": {
"label": {
"string_id": "spotlight-public-wifi-vpn-primary-button"
},
"action": {
"type": "OPEN_URL",
"data": {
"args": "https://www.mozilla.org/en-US/products/vpn/",
"where": "tabshifted"
}
}
},
"secondary": {
"label": {
"string_id": "spotlight-public-wifi-vpn-link"
},
"action": {
"type": "CANCEL"
}
}
}
},
"frequency": {
"lifetime": 3
},
"trigger": {
"id": "defaultBrowserCheck"
}
},
{
"id": "TCP_CFR_MESSAGE_103",
"groups": [
"cfr"
],
"template": "cfr_doorhanger",
"content": {
"bucket_id": "TCP_CFR",
"layout": "icon_and_message",
"icon": "chrome://branding/content/about-logo@2x.png",
"icon_class": "cfr-doorhanger-large-icon",
"heading_text": {
"string_id": "cfr-total-cookie-protection-header"
},
"text": {
"string_id": "cfr-total-cookie-protection-body"
},
"buttons": {
"primary": {
"label": {
"string_id": "cfr-doorhanger-milestone-close-button"
},
"action": {
"type": "CANCEL"
}
},
"secondary": []
},
"anchor_id": "tracking-protection-icon-container",
"skip_address_bar_notifier": true
},
"frequency": {
"lifetime": 1
},
"trigger": {
"id": "openURL",
"patterns": [
"*://*/*"
]
},
"targeting": "firefoxVersion >= 103 && 'privacy.restrict3rdpartystorage.rollout.enabledByDefault'|preferenceValue"
},
{
"id": "BETTER_INTERNET_GLOBAL_ROLLOUT",
"groups": [
"eco"
],
"content": {
"template": "logo-and-content",
"logo": {
"imageURL": "chrome://activity-stream/content/data/content/assets/remote/mountain.svg",
"size": "115px"
},
"body": {
"title": {
"label": {
"string_id": "spotlight-better-internet-header"
},
"size": "22px"
},
"text": {
"label": {
"string_id": "spotlight-better-internet-body"
},
"size": "16px"
},
"primary": {
"label": {
"string_id": "spotlight-pin-primary-button"
},
"action": {
"type": "PIN_FIREFOX_TO_TASKBAR"
}
},
"secondary": {
"label": {
"string_id": "spotlight-pin-secondary-button"
},
"action": {
"type": "CANCEL"
}
}
}
},
"trigger": {
"id": "defaultBrowserCheck"
},
"template": "spotlight",
"frequency": {
"lifetime": 1
},
"targeting": "userMonthlyActivity|length >= 1 && userMonthlyActivity|length <= 6 && doesAppNeedPin"
},
{
"id": "PEACE_OF_MIND_GLOBAL_ROLLOUT",
"groups": [
"eco"
],
"content": {
"template": "logo-and-content",
"logo": {
"imageURL": "chrome://activity-stream/content/data/content/assets/remote/umbrella.png",
"size": "115px"
},
"body": {
"title": {
"label": {
"string_id": "spotlight-peace-mind-header"
},
"size": "22px"
},
"text": {
"label": {
"string_id": "spotlight-peace-mind-body"
},
"size": "15px"
},
"primary": {
"label": {
"string_id": "spotlight-pin-primary-button"
},
"action": {
"type": "PIN_FIREFOX_TO_TASKBAR"
}
},
"secondary": {
"label": {
"string_id": "spotlight-pin-secondary-button"
},
"action": {
"type": "CANCEL"
}
}
}
},
"trigger": {
"id": "defaultBrowserCheck"
},
"template": "spotlight",
"frequency": {
"lifetime": 1
},
"targeting": "userMonthlyActivity|length >= 7 && userMonthlyActivity|length <= 13 && doesAppNeedPin"
},
{
"id": "MULTISTAGE_SPOTLIGHT_MESSAGE",
"groups": [
"panel-test-provider"
],
"template": "spotlight",
"content": {
"id": "control",
"template": "multistage",
"backdrop": "transparent",
"transitions": true,
"screens": [
{
"id": "AW_PIN_FIREFOX",
"content": {
"has_noodles": true,
"title": {
"string_id": "mr1-onboarding-pin-header"
},
"logo": {},
"hero_text": {
"string_id": "mr1-welcome-screen-hero-text"
},
"help_text": {
"text": {
"string_id": "mr1-onboarding-welcome-image-caption"
}
},
"primary_button": {
"label": {
"string_id": "mr1-onboarding-pin-primary-button-label"
},
"action": {
"navigate": true,
"type": "PIN_FIREFOX_TO_TASKBAR"
}
},
"secondary_button": {
"label": {
"string_id": "mr1-onboarding-set-default-secondary-button-label"
},
"action": {
"navigate": true
}
}
}
},
{
"id": "AW_SET_DEFAULT",
"content": {
"has_noodles": true,
"logo": {
"imageURL": "chrome://browser/content/logos/vpn-promo-logo.svg",
"height": "100px"
},
"title": {
"fontSize": "36px",
"fontWeight": 276,
"string_id": "mr1-onboarding-default-header"
},
"subtitle": {
"string_id": "mr1-onboarding-default-subtitle"
},
"primary_button": {
"label": {
"string_id": "mr1-onboarding-default-primary-button-label"
},
"action": {
"navigate": true,
"type": "SET_DEFAULT_BROWSER"
}
},
"secondary_button": {
"label": {
"string_id": "mr1-onboarding-set-default-secondary-button-label"
},
"action": {
"navigate": true
}
}
}
},
{
"id": "BACKGROUND_IMAGE",
"content": {
"background": "url(chrome://activity-stream/content/data/content/assets/proton-bkg.avif) no-repeat center/cover",
"text_color": "light",
"logo": {
"imageURL": "https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/a3c640c8-7594-4bb2-bc18-8b4744f3aaf2.gif"
},
"title": "A dialog with a background image",
"subtitle": "The text color is configurable",
"primary_button": {
"label": "Continue",
"action": {
"navigate": true
}
},
"secondary_button": {
"label": {
"string_id": "mr1-onboarding-set-default-secondary-button-label"
},
"action": {
"navigate": true
}
}
}
},
{
"id": "BACKGROUND_COLOR",
"content": {
"background": "white",
"logo": {
"height": "200px",
"imageURL": ""
},
"title": {
"fontSize": "36px",
"fontWeight": 276,
"raw": "Peace of mind."
},
"title_style": "fancy shine",
"text_color": "dark",
"subtitle": "For the best privacy protection, keep Firefox in easy reach.",
"primary_button": {
"label": "Continue",
"action": {
"navigate": true
}
},
"secondary_button": {
"label": {
"string_id": "mr1-onboarding-set-default-secondary-button-label"
},
"action": {
"navigate": true
}
}
}
}
]
},
"frequency": {
"lifetime": 3
},
"trigger": {
"id": "defaultBrowserCheck"
}
},
{
"id": "PB_FOCUS_PROMO",
"groups": [
"panel-test-provider"
],
"template": "spotlight",
"content": {
"template": "multistage",
"backdrop": "transparent",
"screens": [
{
"id": "PBM_FIREFOX_FOCUS",
"order": 0,
"content": {
"logo": {
"imageURL": "chrome://browser/content/assets/focus-logo.svg",
"height": "48px"
},
"title": {
"string_id": "spotlight-focus-promo-title"
},
"subtitle": {
"string_id": "spotlight-focus-promo-subtitle"
},
"dismiss_button": {
"action": {
"navigate": true
}
},
"ios": {
"action": {
"data": {
"args": "https://app.adjust.com/167k4ih?campaign=firefox-desktop&adgroup=pb&creative=focus-omc172&redirect=https%3A%2F%2Fapps.apple.com%2Fus%2Fapp%2Ffirefox-focus-privacy-browser%2Fid1055677337",
"where": "tabshifted"
},
"type": "OPEN_URL",
"navigate": true
}
},
"android": {
"action": {
"data": {
"args": "https://app.adjust.com/167k4ih?campaign=firefox-desktop&adgroup=pb&creative=focus-omc172&redirect=https%3A%2F%2Fplay.google.com%2Fstore%2Fapps%2Fdetails%3Fid%3Dorg.mozilla.focus",
"where": "tabshifted"
},
"type": "OPEN_URL",
"navigate": true
}
},
"email_link": {
"action": {
"data": {
"args": "https://mozilla.org",
"where": "tabshifted"
},
"type": "OPEN_URL",
"navigate": true
}
},
"tiles": {
"type": "mobile_downloads",
"data": {
"QR_code": {
"image_url": "chrome://browser/content/assets/focus-qr-code.svg",
"alt_text": {
"string_id": "spotlight-focus-promo-qr-code"
},
"image_overrides": {
"de": "chrome://browser/content/assets/klar-qr-code.svg"
}
},
"email": {
"link_text": "Email yourself a link"
},
"marketplace_buttons": [
"ios",
"android"
]
}
}
}
}
]
},
"trigger": {
"id": "defaultBrowserCheck"
}
},
{
"id": "PB_NEWTAB_VPN_PROMO",
"template": "pb_newtab",
"content": {
"promoEnabled": true,
"promoType": "VPN",
"infoEnabled": true,
"infoBody": "fluent:about-private-browsing-info-description-private-window",
"infoLinkText": "fluent:about-private-browsing-learn-more-link",
"infoTitleEnabled": false,
"promoLinkType": "button",
"promoLinkText": "fluent:about-private-browsing-prominent-cta",
"promoSectionStyle": "below-search",
"promoHeader": "fluent:about-private-browsing-get-privacy",
"promoTitle": "fluent:about-private-browsing-hide-activity-1",
"promoTitleEnabled": true,
"promoImageLarge": "chrome://browser/content/assets/moz-vpn.svg",
"promoButton": {
"action": {
"type": "OPEN_URL",
"data": {
"args": "https://vpn.mozilla.org/"
}
}
}
},
"groups": [
"panel-test-provider"
],
"targeting": "region != 'CN' && !hasActiveEnterprisePolicies",
"frequency": {
"lifetime": 3
}
}
]

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

@ -10,8 +10,12 @@ from itertools import chain
from pathlib import Path
from urllib.parse import urlparse
import jsonschema
SCHEMA_BASE_DIR = Path("..", "templates")
PANEL_TEST_PROVIDER_MESSAGES = Path("PanelTestProvider.messages.json")
MESSAGE_TYPES = {
"CFRUrlbarChiclet": Path("CFR", "templates", "CFRUrlbarChiclet.schema.json"),
@ -25,6 +29,24 @@ MESSAGE_TYPES = {
}
class NestedRefResolver(jsonschema.RefResolver):
"""A custom ref resolver that handles bundled schema.
This is the resolver used by Experimenter.
"""
def __init__(self, schema):
super().__init__(base_uri=None, referrer=None)
if "$id" in schema:
self.store[schema["$id"]] = schema
if "$defs" in schema:
for dfn in schema["$defs"].values():
if "$id" in dfn:
self.store[dfn["$id"]] = dfn
def read_schema(path):
"""Read a schema from disk and parse it as JSON."""
with path.open("r") as f:
@ -45,7 +67,12 @@ def extract_template_values(template):
def patch_schema(schema):
"""Patch the given schema.
All relative references will be re-mapped to absolute references.
The JSON schema validator that Experimenter uses
(https://pypi.org/project/jsonschema/) does not support relative references,
nor does it support bundled schemas. We rewrite the schema so that all
relative refs are transformed into absolute refs via the schema's `$id`.
See-also: https://github.com/python-jsonschema/jsonschema/issues/313
"""
schema_id = schema["$id"]
@ -132,10 +159,11 @@ def main(check=False):
b_keys = set(templates[b])
intersection = a_keys.intersection(b_keys)
if len(intersection):
raise ValueError(
f"Schema {a} and {b} have overlapping template values: {', '.join(intersection)}"
)
if len(intersection):
raise ValueError(
f"Schema {a} and {b} have overlapping template values: "
f"{', '.join(intersection)}"
)
all_templates = list(chain.from_iterable(templates.values()))
@ -194,6 +222,8 @@ def main(check=False):
raise ValueError("Schemas do not match!")
check_schema(filename, schema=on_disk)
else:
with filename.open("wb") as f:
print(f"Generating {filename} ...")
@ -201,6 +231,28 @@ def main(check=False):
f.write(b"\n")
def check_schema(filename, schema):
"""Check that the schema validates.
This uses the same validation configuration that is used in Experimenter.
"""
print("Validating messages with Experimenter JSON Schema validator...")
resolver = NestedRefResolver(schema)
with PANEL_TEST_PROVIDER_MESSAGES.open("r") as f:
messages = json.load(f)
for message in messages:
# PanelTestProvider overrides the targeting of all messages. Some
# messages are missing targeting and will fail without this patch.
message["targeting"] = 'providerCohorts.panel_local_testing == "SHOW_TEST"'
msg_id = message["id"]
print(f"Validating {msg_id} with {filename}...")
jsonschema.validate(instance=message, schema=schema, resolver=resolver)
if __name__ == "__main__":
parser = ArgumentParser(description=main.__doc__)
parser.add_argument(

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

@ -329,8 +329,11 @@
}
}
}
},
"secondary": {
}
},
"secondary": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {

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

@ -449,6 +449,13 @@ const TargetingGetters = {
return lazy.isXPIInstallEnabled;
},
get addonsInfo() {
let bts = Cc["@mozilla.org/backgroundtasks;1"]?.getService(
Ci.nsIBackgroundTasks
);
if (bts?.isBackgroundTaskMode) {
return { addons: {}, isFullData: true };
}
return lazy.AddonManager.getActiveAddons(["extension", "service"]).then(
({ addons, fullData }) => {
const info = {};
@ -472,6 +479,13 @@ const TargetingGetters = {
);
},
get searchEngines() {
const NONE = { installed: [], current: "" };
let bts = Cc["@mozilla.org/backgroundtasks;1"]?.getService(
Ci.nsIBackgroundTasks
);
if (bts?.isBackgroundTaskMode) {
return Promise.resolve(NONE);
}
return new Promise(resolve => {
// Note: calling init ensures this code is only executed after Search has been initialized
Services.search
@ -482,7 +496,7 @@ const TargetingGetters = {
installed: engines.map(engine => engine.identifier),
});
})
.catch(() => resolve({ installed: [], current: "" }));
.catch(() => resolve(NONE));
});
},
get isDefaultBrowser() {
@ -611,6 +625,12 @@ const TargetingGetters = {
return lazy.ClientEnvironment.userId;
},
get profileRestartCount() {
let bts = Cc["@mozilla.org/backgroundtasks;1"]?.getService(
Ci.nsIBackgroundTasks
);
if (bts?.isBackgroundTaskMode) {
return 0;
}
// Counter starts at 1 when a profile is created, substract 1 so the value
// returned matches expectations
return (
@ -649,6 +669,15 @@ const TargetingGetters = {
);
},
get activeNotifications() {
let bts = Cc["@mozilla.org/backgroundtasks;1"]?.getService(
Ci.nsIBackgroundTasks
);
if (bts?.isBackgroundTaskMode) {
// This might need to hook into the alert service to enumerate relevant
// persistent native notifications.
return false;
}
let window = lazy.BrowserWindowTracker.getTopWindow();
// Technically this doesn't mean we have active notifications,
@ -692,6 +721,40 @@ const TargetingGetters = {
const ASRouterTargeting = {
Environment: TargetingGetters,
/**
* Snapshot the current targeting environment.
*
* Asynchronous getters are handled. Getters that throw or reject
* are ignored.
*
* @param {object} target - the environment to snapshot.
* @return {object} snapshot of target with `environment` object and `version`
* integer.
*/
async getEnvironmentSnapshot(target = ASRouterTargeting.Environment) {
// One promise for each named property. Label promises with property name.
let promises = Object.keys(target).map(async name => [
name,
await target[name],
]);
// Ignore properties that are rejected.
let results = await Promise.allSettled(promises);
let environment = {};
for (let result of results) {
if (result.status === "fulfilled") {
let [name, value] = result.value;
environment[name] = value;
}
}
// Should we need to migrate in the future.
const snapshot = { environment, version: 1 };
return snapshot;
},
isTriggerMatch(trigger = {}, candidateMessageTrigger = {}) {
if (trigger.id !== candidateMessageTrigger.id) {
return false;

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

@ -39,13 +39,15 @@ const DEFAULT_CONTENT = {
data: { url: "https://example.com" },
},
},
secondary: {
label: {
value: "Not Now",
attributes: { accesskey: "N" },
secondary: [
{
label: {
value: "Not Now",
attributes: { accesskey: "N" },
},
action: { type: "CANCEL" },
},
action: { type: "CANCEL" },
},
],
},
};
@ -75,10 +77,12 @@ const L10N_CONTENT = {
data: { url: "https://example.com" },
},
},
secondary: {
label: { string_id: "btn_cancel_id" },
action: { type: "CANCEL" },
},
secondary: [
{
label: { string_id: "btn_cancel_id" },
action: { type: "CANCEL" },
},
],
},
};

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

@ -0,0 +1,24 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
"use strict";
const { ASRouterTargeting } = ChromeUtils.import(
"resource://activity-stream/lib/ASRouterTargeting.jsm"
);
add_task(async function should_ignore_rejections() {
let target = {
get foo() {
return new Promise(resolve => resolve(1));
},
get bar() {
return new Promise((resolve, reject) => reject(new Error("unspecified")));
},
};
let snapshot = await ASRouterTargeting.getEnvironmentSnapshot(target);
deepEqual(snapshot, { environment: { foo: 1 }, version: 1 });
});

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

@ -18,6 +18,7 @@ skip-if =
[test_AboutWelcomeAttribution.js]
[test_ASRouterTargeting_attribution.js]
skip-if = toolkit != "cocoa" # osx specific tests
[test_ASRouterTargeting_snapshot.js]
[test_AboutWelcomeTelemetry.js]
[test_OnboardingMessageProvider.js]
[test_PanelTestProvider.js]

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

@ -47,7 +47,10 @@ function Article(props) {
openInPocketReader,
} = props;
const url = new URL(article.url || article.resolved_url || "");
if (!article.url && !article.resolved_url && !article.given_url) {
return null;
}
const url = new URL(article.url || article.resolved_url || article.given_url);
const urlSearchParams = new URLSearchParams(utmParams);
if (
@ -67,7 +70,7 @@ function Article(props) {
article.thumbnail ||
encodeThumbnail(article?.top_image_url || article?.images?.["1"]?.src);
const alt = article.alt || "thumbnail image";
const title = article.title || article.resolved_title;
const title = article.title || article.resolved_title || article.given_title;
// Sometimes domain_metadata is not there, depending on the source.
const publisher =
article.publisher ||

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

@ -173,7 +173,12 @@ function Article(props) {
utmParams,
openInPocketReader
} = props;
const url = new URL(article.url || article.resolved_url || "");
if (!article.url && !article.resolved_url && !article.given_url) {
return null;
}
const url = new URL(article.url || article.resolved_url || article.given_url);
const urlSearchParams = new URLSearchParams(utmParams);
if (openInPocketReader && article.item_id && !url.href.match(/getpocket\.com\/read/)) {
@ -187,7 +192,7 @@ function Article(props) {
const thumbnail = article.thumbnail || encodeThumbnail(article?.top_image_url || article?.images?.["1"]?.src);
const alt = article.alt || "thumbnail image";
const title = article.title || article.resolved_title; // Sometimes domain_metadata is not there, depending on the source.
const title = article.title || article.resolved_title || article.given_title; // Sometimes domain_metadata is not there, depending on the source.
const publisher = article.publisher || article.domain_metadata?.name || article.resolved_domain;
return /*#__PURE__*/react.createElement("li", {

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

@ -636,8 +636,8 @@ Section "Uninstall"
${If} ${FileExists} "$INSTDIR\installation_telemetry.json"
Delete /REBOOTOK "$INSTDIR\installation_telemetry.json"
${EndIf}
${If} ${FileExists} "$INSTDIR\postSigningData.json"
Delete /REBOOTOK "$INSTDIR\postSigningData.json"
${If} ${FileExists} "$INSTDIR\postSigningData"
Delete /REBOOTOK "$INSTDIR\postSigningData"
${EndIf}
; Explicitly remove empty webapprt dir in case it exists (bug 757978).

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

@ -383,7 +383,7 @@ const BuiltInThemeConfig = new Map([
[
"activist-balanced-colorway@mozilla.org",
{
version: "1.1",
version: "1.1.1",
path: "resource://builtin-themes/colorways/2022activist/balanced/",
collection: "independent-voices",
l10nId: {
@ -425,7 +425,7 @@ const BuiltInThemeConfig = new Map([
[
"dreamer-balanced-colorway@mozilla.org",
{
version: "1.1",
version: "1.1.1",
path: "resource://builtin-themes/colorways/2022dreamer/balanced/",
collection: "independent-voices",
l10nId: {

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

@ -7,11 +7,15 @@
},
"name": "Activist – Balanced",
"author": "Mozilla",
"version": "1.1",
"version": "1.1.1",
"icons": {
"32": "icon.svg"
},
"theme": {
"properties": {
"color_scheme": "light",
"content_color_scheme": "auto"
},
"colors": {
"tab_background_text": "hsl(0, 0%, 100%)",
"tab_text": "hsl(0, 0%, 0%)",

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

@ -7,11 +7,15 @@
},
"name": "Dreamer – Balanced",
"author": "Mozilla",
"version": "1.1",
"version": "1.1.1",
"icons": {
"32": "icon.svg"
},
"theme": {
"properties": {
"color_scheme": "light",
"content_color_scheme": "auto"
},
"colors": {
"tab_background_text": "hsl(0, 0%, 100%)",
"tab_text": "hsl(0, 0%, 0%)",

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

@ -52,6 +52,7 @@
#include "mozilla/ContentBlockingAllowList.h"
#include "mozilla/ContentBlockingNotifier.h"
#include "mozilla/ContentBlockingUserInteraction.h"
#include "mozilla/ContentPrincipal.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/DocLoadingTimelineMarker.h"
@ -16795,7 +16796,14 @@ Document::GetContentBlockingEvents() {
RefPtr<MozPromise<int, bool, true>> Document::RequestStorageAccessAsyncHelper(
nsPIDOMWindowInner* aInnerWindow, BrowsingContext* aBrowsingContext,
nsIPrincipal* aPrincipal, bool aHasUserInteraction,
ContentBlockingNotifier::StorageAccessPermissionGrantedReason aNotifier) {
ContentBlockingNotifier::StorageAccessPermissionGrantedReason aNotifier,
bool requireFinalChecks) {
if (!requireFinalChecks) {
// Try to allow access for the given principal.
return StorageAccessAPIHelper::AllowAccessFor(aPrincipal, aBrowsingContext,
aNotifier);
}
RefPtr<Document> self(this);
RefPtr<nsPIDOMWindowInner> inner(aInnerWindow);
RefPtr<nsIPrincipal> principal(aPrincipal);
@ -17031,7 +17039,8 @@ already_AddRefed<mozilla::dom::Promise> Document::RequestStorageAccess(
// on work changing state to reflect the result of the API. If it resolves,
// the request was granted. If it rejects it was denied.
RequestStorageAccessAsyncHelper(inner, bc, NodePrincipal(), true,
ContentBlockingNotifier::eStorageAccessAPI)
ContentBlockingNotifier::eStorageAccessAPI,
true)
->Then(
GetCurrentSerialEventTarget(), __func__,
[self, inner, promise] {
@ -17186,7 +17195,8 @@ already_AddRefed<mozilla::dom::Promise> Document::RequestStorageAccessForOrigin(
// typical requestStorageAccess function.
return self->RequestStorageAccessAsyncHelper(
inner, bc, principal, hasUserActivation,
ContentBlockingNotifier::ePrivilegeStorageAccessForOriginAPI);
ContentBlockingNotifier::ePrivilegeStorageAccessForOriginAPI,
true);
},
// If the IPC rejects, we should reject our promise here which will
// cause a rejection of the promise we already returned
@ -17212,6 +17222,271 @@ already_AddRefed<mozilla::dom::Promise> Document::RequestStorageAccessForOrigin(
return promise.forget();
}
already_AddRefed<Promise> Document::RequestStorageAccessUnderSite(
const nsAString& aSerializedSite, ErrorResult& aRv) {
nsIGlobalObject* global = GetScopeObject();
if (!global) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
RefPtr<Promise> promise = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
}
// Check that we have user activation before proceeding to prevent
// rapid calls to the API to leak information.
if (!ConsumeTransientUserGestureActivation()) {
// Report an error to the console for this case
nsContentUtils::ReportToConsole(
nsIScriptError::errorFlag, "requestStorageAccess"_ns, this,
nsContentUtils::eDOM_PROPERTIES, "RequestStorageAccessUserGesture");
promise->MaybeRejectWithUndefined();
return promise.forget();
}
// Check if the provided URI is different-site to this Document
nsCOMPtr<nsIURI> siteURI;
nsresult rv = NS_NewURI(getter_AddRefs(siteURI), aSerializedSite);
if (NS_WARN_IF(NS_FAILED(rv))) {
promise->MaybeRejectWithUndefined();
return promise.forget();
}
bool isCrossSiteArgument;
rv = NodePrincipal()->IsThirdPartyURI(siteURI, &isCrossSiteArgument);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return nullptr;
}
if (!isCrossSiteArgument) {
promise->MaybeRejectWithUndefined();
return promise.forget();
}
// Check if this party has broad cookie permissions.
Maybe<bool> resultBecauseCookiesApproved =
StorageAccessAPIHelper::CheckCookiesPermittedDecidesStorageAccessAPI(
CookieJarSettings(), NodePrincipal());
if (resultBecauseCookiesApproved.isSome()) {
if (resultBecauseCookiesApproved.value()) {
promise->MaybeResolveWithUndefined();
return promise.forget();
}
promise->MaybeRejectWithUndefined();
return promise.forget();
}
// Check if browser settings preclude this document getting storage
// access under the provided site
bool isOnRejectForeignAllowList = RejectForeignAllowList::Check(this);
Maybe<bool> resultBecauseBrowserSettings =
StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI(
CookieJarSettings(), true, isOnRejectForeignAllowList, false, true);
if (resultBecauseBrowserSettings.isSome()) {
if (resultBecauseBrowserSettings.value()) {
promise->MaybeResolveWithUndefined();
return promise.forget();
}
promise->MaybeRejectWithUndefined();
return promise.forget();
}
// Check that this Document is same-site to the top
Maybe<bool> resultBecauseCallContext = StorageAccessAPIHelper::
CheckSameSiteCallingContextDecidesStorageAccessAPI(this, false);
if (resultBecauseCallContext.isSome()) {
if (resultBecauseCallContext.value()) {
promise->MaybeResolveWithUndefined();
return promise.forget();
}
promise->MaybeRejectWithUndefined();
return promise.forget();
}
// Set a permission in the parent process that this document wants storage
// access under the argument's site, resolving our returned promise on success
ContentChild* cc = ContentChild::GetSingleton();
if (!cc) {
// TODO(bug 1778561): Make this work in non-content processes.
promise->MaybeRejectWithUndefined();
return promise.forget();
}
cc->SendSetAllowStorageAccessRequestFlag(NodePrincipal(), siteURI)
->Then(
GetCurrentSerialEventTarget(), __func__,
[promise](bool success) {
if (success) {
promise->MaybeResolveWithUndefined();
} else {
promise->MaybeRejectWithUndefined();
}
},
[promise](mozilla::ipc::ResponseRejectReason reason) {
promise->MaybeRejectWithUndefined();
});
// Return the promise that is resolved in the async handler above
return promise.forget();
}
already_AddRefed<Promise> Document::CompleteStorageAccessRequestFromSite(
const nsAString& aSerializedOrigin, ErrorResult& aRv) {
nsIGlobalObject* global = GetScopeObject();
if (!global) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
RefPtr<Promise> promise = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
}
// Check that the provided URI is different-site to this Document
nsCOMPtr<nsIURI> argumentURI;
nsresult rv = NS_NewURI(getter_AddRefs(argumentURI), aSerializedOrigin);
if (NS_WARN_IF(NS_FAILED(rv))) {
promise->MaybeRejectWithUndefined();
return promise.forget();
}
bool isCrossSiteArgument;
rv = NodePrincipal()->IsThirdPartyURI(argumentURI, &isCrossSiteArgument);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return nullptr;
}
if (!isCrossSiteArgument) {
promise->MaybeRejectWithUndefined();
return promise.forget();
}
// Check if browser settings preclude this document getting storage
// access under the provided site
bool isOnRejectForeignAllowList = RejectForeignAllowList::Check(argumentURI);
Maybe<bool> resultBecauseBrowserSettings =
StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI(
CookieJarSettings(), true, isOnRejectForeignAllowList, false, true);
if (resultBecauseBrowserSettings.isSome()) {
if (resultBecauseBrowserSettings.value()) {
promise->MaybeResolveWithUndefined();
return promise.forget();
}
promise->MaybeRejectWithUndefined();
return promise.forget();
}
// Check that this Document is same-site to the top
Maybe<bool> resultBecauseCallContext = StorageAccessAPIHelper::
CheckSameSiteCallingContextDecidesStorageAccessAPI(this, false);
if (resultBecauseCallContext.isSome()) {
if (resultBecauseCallContext.value()) {
promise->MaybeResolveWithUndefined();
return promise.forget();
}
promise->MaybeRejectWithUndefined();
return promise.forget();
}
// Create principal of the embedded site requesting storage access
nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateContentPrincipal(
argumentURI, NodePrincipal()->OriginAttributesRef());
if (!principal) {
promise->MaybeRejectWithUndefined();
return promise.forget();
}
// Get versions of these objects that we can use in lambdas for callbacks
RefPtr<Document> self(this);
RefPtr<BrowsingContext> bc = GetBrowsingContext();
nsCOMPtr<nsPIDOMWindowInner> inner = GetInnerWindow();
// Test that the permission was set by a call to RequestStorageAccessUnderSite
// from a top level document that is same-site with the argument
ContentChild* cc = ContentChild::GetSingleton();
if (!cc) {
// TODO(bug 1778561): Make this work in non-content processes.
promise->MaybeRejectWithUndefined();
return promise.forget();
}
cc->SendTestAllowStorageAccessRequestFlag(NodePrincipal(), argumentURI)
->Then(
GetCurrentSerialEventTarget(), __func__,
[inner, bc, self, principal](bool success) {
if (success) {
// If that resolved with true, check that we don't already have a
// permission that gives cookie access.
return StorageAccessAPIHelper::
AsyncCheckCookiesPermittedDecidesStorageAccessAPI(bc,
principal);
}
return MozPromise<Maybe<bool>, nsresult, true>::CreateAndReject(
NS_ERROR_FAILURE, __func__);
},
[](mozilla::ipc::ResponseRejectReason reason) {
return MozPromise<Maybe<bool>, nsresult, true>::CreateAndReject(
NS_ERROR_FAILURE, __func__);
})
->Then(
GetCurrentSerialEventTarget(), __func__,
[inner, bc, principal, self, promise](Maybe<bool> cookieResult) {
// Handle the result of the cookie permission check that took place
// in the ContentParent.
if (cookieResult.isSome()) {
if (cookieResult.value()) {
return StorageAccessAPIHelper::
StorageAccessPermissionGrantPromise::CreateAndResolve(
StorageAccessAPIHelper::eAllowAutoGrant, __func__);
}
return StorageAccessAPIHelper::
StorageAccessPermissionGrantPromise::CreateAndReject(
false, __func__);
}
// Check for the existing storage access permission
nsAutoCString type;
bool ok =
AntiTrackingUtils::CreateStoragePermissionKey(principal, type);
if (!ok) {
return StorageAccessAPIHelper::
StorageAccessPermissionGrantPromise::CreateAndReject(
false, __func__);
}
if (AntiTrackingUtils::CheckStoragePermission(
self->NodePrincipal(), type,
nsContentUtils::IsInPrivateBrowsing(self), nullptr, 0)) {
return StorageAccessAPIHelper::
StorageAccessPermissionGrantPromise::CreateAndResolve(
StorageAccessAPIHelper::eAllowAutoGrant, __func__);
}
// Try to request storage access, ignoring the final checks.
// We ignore the final checks because this is where the "grant"
// either by prompt doorhanger or autogrant takes place. We already
// gathered an equivalent grant in requestStorageAccessUnderSite.
return self->RequestStorageAccessAsyncHelper(
inner, bc, principal, true,
ContentBlockingNotifier::eStorageAccessAPI, false);
},
// If the IPC rejects, we should reject our promise here which will
// cause a rejection of the promise we already returned
[promise]() {
return MozPromise<int, bool, true>::CreateAndReject(false,
__func__);
})
->Then(
GetCurrentSerialEventTarget(), __func__,
// If the previous handlers resolved, we should reinstate user
// activation and resolve the promise we returned in Step 5.
[self, inner, promise] {
inner->SaveStorageAccessPermissionGranted();
promise->MaybeResolveWithUndefined();
},
// If the previous handler rejected, we should reject the promise
// returned by this function.
[promise] { promise->MaybeRejectWithUndefined(); });
return promise.forget();
}
RefPtr<Document::AutomaticStorageAccessPermissionGrantPromise>
Document::AutomaticStorageAccessPermissionCanBeGranted(bool hasUserActivation) {
// requestStorageAccessForOrigin may not require user activation. If we don't

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

@ -1257,13 +1257,19 @@ class Document : public nsINode,
RefPtr<MozPromise<int, bool, true>> RequestStorageAccessAsyncHelper(
nsPIDOMWindowInner* aInnerWindow, BrowsingContext* aBrowsingContext,
nsIPrincipal* aPrincipal, bool aHasUserInteraction,
ContentBlockingNotifier::StorageAccessPermissionGrantedReason aNotifier);
ContentBlockingNotifier::StorageAccessPermissionGrantedReason aNotifier,
bool performFinalChecks);
already_AddRefed<Promise> RequestStorageAccess(ErrorResult& aRv);
already_AddRefed<Promise> RequestStorageAccessForOrigin(
const nsAString& aThirdPartyOrigin, const bool aRequireUserInteraction,
ErrorResult& aRv);
already_AddRefed<Promise> RequestStorageAccessUnderSite(
const nsAString& aSerializedSite, ErrorResult& aRv);
already_AddRefed<Promise> CompleteStorageAccessRequestFromSite(
const nsAString& aSerializedOrigin, ErrorResult& aRv);
bool UseRegularPrincipal() const;
/**

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

@ -5047,7 +5047,14 @@ Storage* nsGlobalWindowInner::GetLocalStorage(ErrorResult& aError) {
return mLocalStorage;
}
IDBFactory* nsGlobalWindowInner::GetIndexedDB(ErrorResult& aError) {
IDBFactory* nsGlobalWindowInner::GetIndexedDB(JSContext* aCx,
ErrorResult& aError) {
if (!IDBFactory::IsEnabled(aCx, AsGlobal()->GetGlobalJSObject())) {
// Let window.indexedDB be an attribute with a null value, to prevent
// undefined identifier error
return nullptr;
}
if (!mIndexedDB) {
// This may keep mIndexedDB null without setting an error.
auto res = IDBFactory::CreateForWindow(this);

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

@ -744,7 +744,8 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
mozilla::dom::Storage* GetSessionStorage(mozilla::ErrorResult& aError);
mozilla::dom::Storage* GetLocalStorage(mozilla::ErrorResult& aError);
mozilla::dom::Selection* GetSelection(mozilla::ErrorResult& aError);
mozilla::dom::IDBFactory* GetIndexedDB(mozilla::ErrorResult& aError);
mozilla::dom::IDBFactory* GetIndexedDB(JSContext* aCx,
mozilla::ErrorResult& aError);
already_AddRefed<nsICSSDeclaration> GetComputedStyle(
mozilla::dom::Element& aElt, const nsAString& aPseudoElt,
mozilla::ErrorResult& aError) override;

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

@ -2,66 +2,107 @@
const isWorker = typeof DedicatedWorkerGlobalScope === "function";
function check(content, expected, item) {
const exposed = expected ? "is exposed without" : "is not exposed with";
const worker = isWorker ? "in worker" : "in window";
is(
content.eval(`!!globalThis.${item}`),
expected,
`${item} ${exposed} pbmode ${worker}`
);
}
function checkCaches(content, expected) {
check(content, expected, "caches");
check(content, expected, "Cache");
check(content, expected, "CacheStorage");
}
function checkIDB(content, expected) {
check(content, expected, "indexedDB");
check(content, expected, "IDBFactory");
check(content, expected, "IDBKeyRange");
check(content, expected, "IDBOpenDBRequest");
check(content, expected, "IDBRequest");
check(content, expected, "IDBVersionChangeEvent");
// These are always accessed by jakearchibald/idb@v3 without existence checks.
// https://github.com/jakearchibald/idb/blob/e1c7c44dbba38415745afc782b8e247da8c833f2/lib/idb.mjs#L152
check(content, true, "IDBCursor");
check(content, true, "IDBDatabase");
check(content, true, "IDBIndex");
check(content, true, "IDBObjectStore");
check(content, true, "IDBTransaction");
}
function checkSW(content, expected) {
if (isWorker) {
// Currently not supported. Bug 1131324
return;
function checkAll(content, inPrivateBrowsing) {
function check(
item,
{ valueExpected, enumerationExpected, parent = "globalThis" }
) {
const exposed = valueExpected ? "is exposed" : "is not exposed";
const pbmode = inPrivateBrowsing ? "with pbmode" : "without pbmode";
const enumerated = enumerationExpected
? "is enumerated"
: "is not enumerated";
const worker = isWorker ? "in worker" : "in window";
is(
content.eval(`!!${parent}.${item}`),
valueExpected,
`${parent}.${item} ${exposed} ${pbmode} ${worker}`
);
is(
content.eval(`"${item}" in ${parent}`),
enumerationExpected,
`${parent}.${item} ${enumerated} ${pbmode} ${worker}`
);
}
check(content, expected, "navigator.serviceWorker");
check(content, expected, "ServiceWorker");
check(content, expected, "ServiceWorkerContainer");
check(content, expected, "ServiceWorkerRegistration");
check(content, expected, "NavigationPreloadManager");
check(content, expected, "PushManager");
check(content, expected, "PushSubscription");
check(content, expected, "PushSubscriptionOptions");
}
function checkAll(content, expected) {
checkCaches(content, expected);
checkIDB(content, expected);
checkSW(content, expected);
function checkNotExposedInPBM(item, parent) {
check(item, {
valueExpected: !inPrivateBrowsing,
enumerationExpected: !inPrivateBrowsing,
parent,
});
}
function checkCaches() {
checkNotExposedInPBM("caches");
checkNotExposedInPBM("Cache");
checkNotExposedInPBM("CacheStorage");
}
function checkIDB() {
checkNotExposedInPBM("IDBFactory");
checkNotExposedInPBM("IDBKeyRange");
checkNotExposedInPBM("IDBOpenDBRequest");
checkNotExposedInPBM("IDBRequest");
checkNotExposedInPBM("IDBVersionChangeEvent");
// These are always accessed by jakearchibald/idb@v3 without existence checks.
// https://github.com/jakearchibald/idb/blob/e1c7c44dbba38415745afc782b8e247da8c833f2/lib/idb.mjs#L152
check("IDBCursor", {
valueExpected: true,
enumerationExpected: true,
});
check("IDBDatabase", {
valueExpected: true,
enumerationExpected: true,
});
check("IDBIndex", {
valueExpected: true,
enumerationExpected: true,
});
check("IDBObjectStore", {
valueExpected: true,
enumerationExpected: true,
});
check("IDBTransaction", {
valueExpected: true,
enumerationExpected: true,
});
// https://www.msn.com/feed accesses indexedDB as a global variable without existence check
// We need to always expose the attribute itself
check("indexedDB", {
valueExpected: !inPrivateBrowsing,
enumerationExpected: true,
});
}
function checkSW() {
if (isWorker) {
// Currently not supported. Bug 1131324
return;
}
checkNotExposedInPBM("serviceWorker", "navigator");
checkNotExposedInPBM("ServiceWorker");
checkNotExposedInPBM("ServiceWorkerContainer");
checkNotExposedInPBM("ServiceWorkerRegistration");
checkNotExposedInPBM("NavigationPreloadManager");
checkNotExposedInPBM("PushManager");
checkNotExposedInPBM("PushSubscription");
checkNotExposedInPBM("PushSubscriptionOptions");
}
checkCaches();
checkIDB();
checkSW();
}
if (isWorker) {
importScripts("/tests/SimpleTest/WorkerSimpleTest.js");
globalThis.onmessage = ev => {
const { expected } = ev.data;
checkAll(globalThis, expected);
const { inPrivateBrowsing } = ev.data;
checkAll(globalThis, inPrivateBrowsing);
postMessage({
kind: "info",
next: true,

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

@ -27,11 +27,11 @@
});
}
function runWorkerTest(content, expected) {
function runWorkerTest(content, inPrivateBrowsing) {
return new Promise((resolve, reject) => {
/** @type {Worker} */
const worker = content.eval("new Worker('/chrome/dom/base/test/chrome/file_hide_in_pbmode.js')");
worker.postMessage({ expected });
worker.postMessage({ inPrivateBrowsing });
worker.onerror = reject;
listenForTests(worker);
worker.addEventListener("message", ev => {
@ -46,13 +46,13 @@
async function runTest() {
// sanity check
let win = await openBrowserWindow(contentPage, { private: false });
checkAll(win.content, true);
await runWorkerTest(win.content, true);
checkAll(win.content, false);
await runWorkerTest(win.content, false);
win.close();
win = await openBrowserWindow(contentPage, { private: true });
checkAll(win.content, false);
await runWorkerTest(win.content, false);
checkAll(win.content, true);
await runWorkerTest(win.content, true);
win.close();
SimpleTest.finish();

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

@ -1600,7 +1600,7 @@ DOMInterfaces = {
'nativeType': 'nsGlobalWindowInner',
'headerFile': 'nsGlobalWindow.h',
'implicitJSContext': [
'requestIdleCallback'
'requestIdleCallback', 'indexedDB'
],
},
@ -1627,7 +1627,7 @@ DOMInterfaces = {
'WorkerGlobalScope': {
'headerFile': 'mozilla/dom/WorkerScope.h',
'implicitJSContext': [ 'importScripts' ],
'implicitJSContext': [ 'importScripts', 'indexedDB' ],
},
'Worklet': {

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

@ -6,6 +6,8 @@
#include "OffscreenCanvasRenderingContext2D.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#include "mozilla/dom/FontFaceSetImpl.h"
#include "mozilla/dom/FontFaceSet.h"
#include "mozilla/dom/OffscreenCanvasRenderingContext2DBinding.h"
#include "mozilla/dom/OffscreenCanvas.h"
#include "mozilla/dom/WorkerCommon.h"
@ -125,6 +127,17 @@ static void SerializeFontForCanvas(const StyleFontFamilyList& aList,
bool OffscreenCanvasRenderingContext2D::SetFontInternal(const nsACString& aFont,
ErrorResult& aError) {
nsIGlobalObject* global = GetParentObject();
FontFaceSet* fontFaceSet = global ? global->Fonts() : nullptr;
FontFaceSetImpl* fontFaceSetImpl =
fontFaceSet ? fontFaceSet->GetImpl() : nullptr;
RefPtr<URLExtraData> urlExtraData =
fontFaceSetImpl ? fontFaceSetImpl->GetURLExtraData() : nullptr;
if (fontFaceSetImpl) {
fontFaceSetImpl->FlushUserFontSet();
}
// In the OffscreenCanvas case we don't have the context necessary to call
// GetFontStyleForServo(), as we do in the main-thread canvas context, so
// instead we borrow ParseFontShorthandForMatching to parse the attribute.
@ -134,7 +147,7 @@ bool OffscreenCanvasRenderingContext2D::SetFontInternal(const nsACString& aFont,
gfxFontStyle fontStyle;
float size = 0.0f;
if (!ServoCSSParser::ParseFontShorthandForMatching(
aFont, nullptr, list, fontStyle.style, fontStyle.stretch,
aFont, urlExtraData, list, fontStyle.style, fontStyle.stretch,
fontStyle.weight, &size)) {
return false;
}
@ -144,15 +157,15 @@ bool OffscreenCanvasRenderingContext2D::SetFontInternal(const nsACString& aFont,
// TODO: Get a userFontSet from the Worker and pass to the fontGroup
// TODO: Should we be passing a language? Where from?
// TODO: Cache fontGroups in the Worker (use an nsFontCache?)
gfxFontGroup* fontGroup =
gfxPlatform::GetPlatform()->CreateFontGroup(nullptr, // aPresContext
list, // aFontFamilyList
&fontStyle, // aStyle
nullptr, // aLanguage
false, // aExplicitLanguage
nullptr, // aTextPerf
nullptr, // aUserFontSet
1.0); // aDevToCssSize
gfxFontGroup* fontGroup = gfxPlatform::GetPlatform()->CreateFontGroup(
nullptr, // aPresContext
list, // aFontFamilyList
&fontStyle, // aStyle
nullptr, // aLanguage
false, // aExplicitLanguage
nullptr, // aTextPerf
fontFaceSetImpl, // aUserFontSet
1.0); // aDevToCssSize
CurrentState().fontGroup = fontGroup;
SerializeFontForCanvas(list, fontStyle, CurrentState().font);
CurrentState().fontFont = nsFont(StyleFontFamily{list, false, false},

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

@ -53,6 +53,7 @@
#include "mozilla/ipc/URIUtils.h"
#include "gfxPlatform.h"
#include "gfxPlatformFontList.h"
#include "mozilla/AntiTrackingUtils.h"
#include "mozilla/AppShutdown.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/BasePrincipal.h"
@ -6500,6 +6501,93 @@ mozilla::ipc::IPCResult ContentParent::RecvCompleteAllowAccessFor(
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvSetAllowStorageAccessRequestFlag(
nsIPrincipal* aEmbeddedPrincipal, nsIURI* aEmbeddingOrigin,
SetAllowStorageAccessRequestFlagResolver&& aResolver) {
MOZ_ASSERT(aEmbeddedPrincipal);
MOZ_ASSERT(aEmbeddingOrigin);
// Get the permission manager and build the key.
RefPtr<PermissionManager> permManager = PermissionManager::GetInstance();
if (!permManager) {
aResolver(false);
return IPC_OK();
}
nsCOMPtr<nsIURI> embeddedURI = aEmbeddedPrincipal->GetURI();
nsCString permissionKey;
bool success = AntiTrackingUtils::CreateStorageRequestPermissionKey(
embeddedURI, permissionKey);
if (!success) {
aResolver(false);
return IPC_OK();
}
// Set the permission to ALLOW for a prefence specified amount of seconds.
// Time units are inconsistent, be careful
int64_t when = (PR_Now() / PR_USEC_PER_MSEC) +
StaticPrefs::dom_storage_access_forward_declared_lifetime() *
PR_MSEC_PER_SEC;
nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateContentPrincipal(
aEmbeddingOrigin, aEmbeddedPrincipal->OriginAttributesRef());
nsresult rv = permManager->AddFromPrincipal(
principal, permissionKey, nsIPermissionManager::ALLOW_ACTION,
nsIPermissionManager::EXPIRE_TIME, when);
if (NS_FAILED(rv)) {
aResolver(false);
return IPC_OK();
}
// Resolve with success if we set the permission.
aResolver(true);
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvTestAllowStorageAccessRequestFlag(
nsIPrincipal* aEmbeddingPrincipal, nsIURI* aEmbeddedOrigin,
TestAllowStorageAccessRequestFlagResolver&& aResolver) {
MOZ_ASSERT(aEmbeddingPrincipal);
MOZ_ASSERT(aEmbeddedOrigin);
// Get the permission manager and build the key.
RefPtr<PermissionManager> permManager = PermissionManager::GetInstance();
if (!permManager) {
aResolver(false);
return IPC_OK();
}
nsCString requestPermissionKey;
bool success = AntiTrackingUtils::CreateStorageRequestPermissionKey(
aEmbeddedOrigin, requestPermissionKey);
if (!success) {
aResolver(false);
return IPC_OK();
}
// Get the permission and resolve false if it is not set to ALLOW.
uint32_t access = nsIPermissionManager::UNKNOWN_ACTION;
nsresult rv = permManager->TestPermissionFromPrincipal(
aEmbeddingPrincipal, requestPermissionKey, &access);
if (NS_FAILED(rv)) {
aResolver(false);
return IPC_OK();
}
if (access != nsIPermissionManager::ALLOW_ACTION) {
aResolver(false);
return IPC_OK();
}
// Remove the permission, failing if the permission manager fails
rv = permManager->RemoveFromPrincipal(aEmbeddingPrincipal,
requestPermissionKey);
if (NS_FAILED(rv)) {
aResolver(false);
return IPC_OK();
}
// At this point, signal to our caller that the permission was set
aResolver(true);
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvStoreUserInteractionAsPermission(
const Principal& aPrincipal) {
if (!ValidatePrincipal(aPrincipal)) {

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

@ -1259,6 +1259,14 @@ class ContentParent final : public PContentParent,
aReason,
CompleteAllowAccessForResolver&& aResolver);
mozilla::ipc::IPCResult RecvSetAllowStorageAccessRequestFlag(
nsIPrincipal* aEmbeddedPrincipal, nsIURI* aEmbeddingOrigin,
SetAllowStorageAccessRequestFlagResolver&& aResolver);
mozilla::ipc::IPCResult RecvTestAllowStorageAccessRequestFlag(
nsIPrincipal* aEmbeddingPrincipal, nsIURI* aEmbeddedOrigin,
TestAllowStorageAccessRequestFlagResolver&& aResolver);
mozilla::ipc::IPCResult RecvStoreUserInteractionAsPermission(
const Principal& aPrincipal);

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

@ -1650,6 +1650,16 @@ parent:
StorageAccessPermissionGrantedReason aReason)
returns (StorageAccessPromptChoices? choice);
async SetAllowStorageAccessRequestFlag(
nsIPrincipal aEmbeddingPrincipal,
nsIURI aEmbeddedOrigin)
returns (bool success);
async TestAllowStorageAccessRequestFlag(
nsIPrincipal aEmbeddedPrincipal,
nsIURI aEmbeddingOrigin)
returns (bool success);
async StoreUserInteractionAsPermission(Principal aPrincipal);
async TestCookiePermissionDecided(MaybeDiscardedBrowsingContext aContext,

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

@ -305,6 +305,8 @@ var interfaceNamesInGlobalScope = [
// IMPORTANT: Do not change this list without review from a DOM peer!
"TextEncoder",
// IMPORTANT: Do not change this list without review from a DOM peer!
"TextMetrics",
// IMPORTANT: Do not change this list without review from a DOM peer!
"TransformStream",
// IMPORTANT: Do not change this list without review from a DOM peer!
"TransformStreamDefaultController",

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

@ -362,7 +362,7 @@ interface CanvasPattern {
void setTransform(optional DOMMatrix2DInit matrix = {});
};
[Exposed=Window]
[Exposed=(Window,Worker)]
interface TextMetrics {
// x-direction

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

@ -544,6 +544,11 @@ partial interface Document {
Promise<boolean> hasStorageAccess();
[Pref="dom.storage_access.enabled", NewObject]
Promise<void> requestStorageAccess();
// https://github.com/privacycg/storage-access/pull/100
[Pref="dom.storage_access.forward_declared.enabled", NewObject]
Promise<void> requestStorageAccessUnderSite(DOMString serializedSite);
[Pref="dom.storage_access.forward_declared.enabled", NewObject]
Promise<void> completeStorageAccessRequestFromSite(DOMString serializedSite);
};
// A privileged API to give chrome privileged code and the content script of the

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

@ -24,7 +24,9 @@ OffscreenCanvasRenderingContext2D includes CanvasFillStrokeStyles;
OffscreenCanvasRenderingContext2D includes CanvasShadowStyles;
OffscreenCanvasRenderingContext2D includes CanvasRect;
OffscreenCanvasRenderingContext2D includes CanvasDrawPath;
OffscreenCanvasRenderingContext2D includes CanvasText;
OffscreenCanvasRenderingContext2D includes CanvasDrawImage;
OffscreenCanvasRenderingContext2D includes CanvasImageData;
OffscreenCanvasRenderingContext2D includes CanvasPathDrawingStyles;
OffscreenCanvasRenderingContext2D includes CanvasTextDrawingStyles;
OffscreenCanvasRenderingContext2D includes CanvasPathMethods;

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

@ -70,7 +70,7 @@ partial interface mixin WindowOrWorkerGlobalScope {
// http://w3c.github.io/IndexedDB/#factory-interface
partial interface mixin WindowOrWorkerGlobalScope {
// readonly attribute IDBFactory indexedDB; // bug 1776789
[Throws, Func="IDBFactory::IsEnabled"]
[Throws]
readonly attribute IDBFactory? indexedDB;
};

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

@ -676,9 +676,15 @@ already_AddRefed<Promise> WorkerGlobalScope::Fetch(
}
already_AddRefed<IDBFactory> WorkerGlobalScope::GetIndexedDB(
ErrorResult& aErrorResult) {
JSContext* aCx, ErrorResult& aErrorResult) {
AssertIsOnWorkerThread();
if (!IDBFactory::IsEnabled(aCx, GetGlobalJSObject())) {
// Let window.indexedDB be an attribute with a null value, to prevent
// undefined identifier error
return nullptr;
}
RefPtr<IDBFactory> indexedDB = mIndexedDB;
if (!indexedDB) {

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

@ -312,7 +312,8 @@ class WorkerGlobalScope : public WorkerGlobalScopeBase {
bool IsSecureContext() const;
already_AddRefed<IDBFactory> GetIndexedDB(ErrorResult& aErrorResult);
already_AddRefed<IDBFactory> GetIndexedDB(JSContext* aCx,
ErrorResult& aErrorResult);
already_AddRefed<cache::CacheStorage> GetCaches(ErrorResult& aRv);

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

@ -289,6 +289,8 @@ var interfaceNamesInGlobalScope = [
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "TextEncoder", insecureContext: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "TextMetrics", insecureContext: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "TransformStream", insecureContext: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{

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

@ -169,8 +169,8 @@ bool IsOAForceStripPermission(const nsACString& aType) {
// Array of permission prefixes which should be isolated only by site.
// These site-scoped permissions are stored under their site's principal.
// GetAllForPrincipal also needs to look for these especially.
static constexpr std::array<nsLiteralCString, 1> kSiteScopedPermissions = {
{"3rdPartyStorage^"_ns}};
static constexpr std::array<nsLiteralCString, 2> kSiteScopedPermissions = {
{"3rdPartyStorage^"_ns, "AllowStorageAccessRequest^"_ns}};
bool IsSiteScopedPermission(const nsACString& aType) {
if (aType.IsEmpty()) {

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

@ -54,11 +54,24 @@ using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::unicode;
void gfxCharacterMap::NotifyReleased() {
if (mShared) {
gfxPlatformFontList::PlatformFontList()->RemoveCmap(this);
nsrefcnt gfxCharacterMap::NotifyMaybeReleased() {
auto* pfl = gfxPlatformFontList::PlatformFontList();
pfl->Lock();
// Something may have pulled our raw pointer out of gfxPlatformFontList before
// we were able to complete the release.
if (mRefCnt > 0) {
pfl->Unlock();
return mRefCnt;
}
if (mShared) {
pfl->RemoveCmap(this);
}
pfl->Unlock();
delete this;
return 0;
}
gfxFontEntry::gfxFontEntry(const nsACString& aName, bool aIsStandardFace)
@ -1918,7 +1931,7 @@ void gfxFontFamily::SearchAllFontsForChar(GlobalFontMatch* aMatchData) {
gfxFontFamily::~gfxFontFamily() {
// Should not be dropped by stylo, but the InitFontList thread might use
// a transient gfxFontFamily and that's OK.
MOZ_ASSERT(NS_IsMainThread() || gfxPlatformFontList::IsInitFontListThread());
MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal());
}
// returns true if other names were found, false otherwise

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

@ -81,9 +81,9 @@ class gfxCharacterMap : public gfxSparseBitSet {
--mRefCnt;
NS_LOG_RELEASE(this, mRefCnt, "gfxCharacterMap");
if (mRefCnt == 0) {
NotifyReleased();
// |this| has been deleted.
return 0;
// Because we have a raw pointer in gfxPlatformFontList that we may race
// access with, we may not release here.
return NotifyMaybeReleased();
}
return mRefCnt;
}
@ -112,9 +112,9 @@ class gfxCharacterMap : public gfxSparseBitSet {
bool mShared;
protected:
void NotifyReleased();
nsrefcnt NotifyMaybeReleased();
nsAutoRefCnt mRefCnt;
mozilla::ThreadSafeAutoRefCnt mRefCnt;
private:
gfxCharacterMap(const gfxCharacterMap&);

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

@ -301,6 +301,20 @@ gfxPlatformFontList::gfxPlatformFontList(bool aNeedFullnamePostscriptNames)
}
RegisterStrongMemoryReporter(new MemoryReporter());
// initialize lang group pref font defaults (i.e. serif/sans-serif)
mDefaultGenericsLangGroup.AppendElements(ArrayLength(gPrefLangNames));
for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); i++) {
nsAutoCString prefDefaultFontType("font.default.");
prefDefaultFontType.Append(GetPrefLangName(eFontPrefLang(i)));
nsAutoCString serifOrSans;
Preferences::GetCString(prefDefaultFontType.get(), serifOrSans);
if (serifOrSans.EqualsLiteral("sans-serif")) {
mDefaultGenericsLangGroup[i] = StyleGenericFontFamily::SansSerif;
} else {
mDefaultGenericsLangGroup[i] = StyleGenericFontFamily::Serif;
}
}
}
gfxPlatformFontList::~gfxPlatformFontList() {
@ -1870,12 +1884,13 @@ fontlist::Pointer gfxPlatformFontList::GetShmemCharMapLocked(
}
// lookup cmap in the shared cmap set, adding if not already present
gfxCharacterMap* gfxPlatformFontList::FindCharMap(gfxCharacterMap* aCmap) {
already_AddRefed<gfxCharacterMap> gfxPlatformFontList::FindCharMap(
gfxCharacterMap* aCmap) {
AutoLock lock(mLock);
aCmap->CalcHash();
gfxCharacterMap* cmap = mSharedCmaps.PutEntry(aCmap)->GetKey();
cmap->mShared = true;
return cmap;
return do_AddRef(cmap);
}
// remove the cmap from the shared cmap set
@ -2395,22 +2410,6 @@ StyleGenericFontFamily gfxPlatformFontList::GetDefaultGeneric(
AutoLock lock(mLock);
// initialize lang group pref font defaults (i.e. serif/sans-serif)
if (MOZ_UNLIKELY(mDefaultGenericsLangGroup.IsEmpty())) {
mDefaultGenericsLangGroup.AppendElements(ArrayLength(gPrefLangNames));
for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); i++) {
nsAutoCString prefDefaultFontType("font.default.");
prefDefaultFontType.Append(GetPrefLangName(eFontPrefLang(i)));
nsAutoCString serifOrSans;
Preferences::GetCString(prefDefaultFontType.get(), serifOrSans);
if (serifOrSans.EqualsLiteral("sans-serif")) {
mDefaultGenericsLangGroup[i] = StyleGenericFontFamily::SansSerif;
} else {
mDefaultGenericsLangGroup[i] = StyleGenericFontFamily::Serif;
}
}
}
if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) {
return mDefaultGenericsLangGroup[uint32_t(aLang)];
}

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

@ -497,7 +497,7 @@ class gfxPlatformFontList : public gfxFontInfoLoader {
// Search for existing cmap that matches the input; return the input if no
// match is found.
gfxCharacterMap* FindCharMap(gfxCharacterMap* aCmap);
already_AddRefed<gfxCharacterMap> FindCharMap(gfxCharacterMap* aCmap);
// Remove the cmap from the shared cmap set.
void RemoveCmap(const gfxCharacterMap* aCharMap);

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

@ -8,6 +8,7 @@
#include "gfxUserFontSet.h"
#include "gfxPlatform.h"
#include "gfxFontConstants.h"
#include "mozilla/Atomics.h"
#include "mozilla/FontPropertyTypes.h"
#include "mozilla/Preferences.h"
#include "mozilla/ProfilerLabels.h"
@ -34,7 +35,7 @@ mozilla::LogModule* gfxUserFontSet::GetUserFontsLog() {
#define LOG_ENABLED() \
MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug)
static uint64_t sFontSetGeneration = 0;
static Atomic<uint64_t> sFontSetGeneration(0);
gfxUserFontEntry::gfxUserFontEntry(
gfxUserFontSet* aFontSet, const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
@ -250,7 +251,7 @@ size_t gfxUserFontData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
/*virtual*/
gfxUserFontFamily::~gfxUserFontFamily() {
// Should not be dropped by stylo
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal());
}
already_AddRefed<gfxFontSrcPrincipal> gfxFontFaceSrc::LoadPrincipal(
@ -1039,9 +1040,9 @@ void gfxUserFontSet::AddUserFontEntry(const nsCString& aFamilyName,
void gfxUserFontSet::IncrementGeneration(bool aIsRebuild) {
// add one, increment again if zero
++sFontSetGeneration;
if (sFontSetGeneration == 0) ++sFontSetGeneration;
mGeneration = sFontSetGeneration;
do {
mGeneration = ++sFontSetGeneration;
} while (mGeneration == 0);
if (aIsRebuild) {
mRebuildGeneration = mGeneration;
}

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

@ -1131,7 +1131,9 @@ bool DCSurfaceVideo::CallVideoProcessorBlt() {
: DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
hr = swapChain3->SetColorSpace1(outputColorSpace);
if (FAILED(hr)) {
gfxCriticalNote << "SetColorSpace1 failed: " << gfx::hexa(hr);
gfxCriticalNoteOnce << "SetColorSpace1 failed: " << gfx::hexa(hr);
RenderThread::Get()->NotifyWebRenderError(
wr::WebRenderError::VIDEO_OVERLAY);
return false;
}
videoContext1->VideoProcessorSetOutputColorSpace1(videoProcessor,

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

@ -805,28 +805,36 @@ ScriptSourceObject* ModuleObject::scriptSourceObject() const {
.as<ScriptSourceObject>();
}
bool ModuleObject::initAsyncSlots(JSContext* cx, bool isAsync,
bool ModuleObject::initAsyncSlots(JSContext* cx, bool hasTopLevelAwait,
HandleObject asyncParentModulesList) {
initReservedSlot(AsyncSlot, BooleanValue(isAsync));
initReservedSlot(HasTopLevelAwaitSlot, BooleanValue(hasTopLevelAwait));
initReservedSlot(AsyncParentModulesSlot,
ObjectValue(*asyncParentModulesList));
return true;
}
constexpr uint32_t ASYNC_EVALUATING_POST_ORDER_FALSE = 0;
constexpr uint32_t ASYNC_EVALUATING_POST_ORDER_INIT = 1;
uint32_t AsyncPostOrder = ASYNC_EVALUATING_POST_ORDER_INIT;
uint32_t nextPostOrder() {
uint32_t ordinal = AsyncPostOrder;
MOZ_ASSERT(AsyncPostOrder < MAX_UINT32);
AsyncPostOrder++;
static uint32_t NextPostOrder(JSRuntime* rt) {
uint32_t ordinal = rt->moduleAsyncEvaluatingPostOrder;
MOZ_ASSERT(ordinal < MAX_UINT32);
rt->moduleAsyncEvaluatingPostOrder++;
return ordinal;
}
// Reset the runtime's moduleAsyncEvaluatingPostOrder counter when the last
// module that was async evaluating is finished.
//
// The graph is not re-entrant and any future modules will be independent from
// this one.
static void MaybeResetPostOrderCounter(JSRuntime* rt,
uint32_t finishedPostOrder) {
if (rt->moduleAsyncEvaluatingPostOrder == finishedPostOrder) {
rt->moduleAsyncEvaluatingPostOrder = ASYNC_EVALUATING_POST_ORDER_INIT;
}
}
void ModuleObject::setAsyncEvaluating() {
initReservedSlot(AsyncEvaluatingPostOrderSlot,
PrivateUint32Value(nextPostOrder()));
PrivateUint32Value(NextPostOrder(runtimeFromMainThread())));
}
void ModuleObject::initScriptSlots(HandleScript script) {
@ -953,8 +961,8 @@ void ModuleObject::setStatus(ModuleStatus newStatus) {
setReservedSlot(StatusSlot, ModuleStatusValue(newStatus));
}
bool ModuleObject::isAsync() const {
return getReservedSlot(AsyncSlot).toBoolean();
bool ModuleObject::hasTopLevelAwait() const {
return getReservedSlot(HasTopLevelAwaitSlot).toBoolean();
}
bool ModuleObject::isAsyncEvaluating() const {
@ -968,12 +976,8 @@ bool ModuleObject::wasAsyncEvaluating() const {
}
void ModuleObject::setAsyncEvaluatingFalse() {
if (AsyncPostOrder == getAsyncEvaluatingPostOrder()) {
// If this condition is true, we can reset postOrder.
// Graph is not re-entrant and any future modules will be independent from
// this one.
AsyncPostOrder = ASYNC_EVALUATING_POST_ORDER_INIT;
}
JSRuntime* rt = runtimeFromMainThread();
MaybeResetPostOrderCounter(rt, getAsyncEvaluatingPostOrder());
return setReservedSlot(AsyncEvaluatingPostOrderSlot,
PrivateUint32Value(ASYNC_EVALUATING_POST_ORDER_FALSE));
}
@ -1208,7 +1212,7 @@ bool ModuleObject::execute(JSContext* cx, Handle<ModuleObject*> self) {
RootedScript script(cx, self->script());
auto guardA = mozilla::MakeScopeExit([&] {
if (self->isAsync()) {
if (self->hasTopLevelAwait()) {
// Handled in AsyncModuleExecutionFulfilled and
// AsyncModuleExecutionRejected.
return;
@ -2068,163 +2072,6 @@ ModuleObject* js::CallModuleResolveHook(JSContext* cx,
return &result->as<ModuleObject>();
}
bool js::AsyncModuleExecutionFulfilledHandler(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
JSFunction& func = args.callee().as<JSFunction>();
Rooted<ModuleObject*> module(
cx, &func.getExtendedSlot(FunctionExtended::MODULE_SLOT)
.toObject()
.as<ModuleObject>());
AsyncModuleExecutionFulfilled(cx, module);
args.rval().setUndefined();
return true;
}
bool js::AsyncModuleExecutionRejectedHandler(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
JSFunction& func = args.callee().as<JSFunction>();
Rooted<ModuleObject*> module(
cx, &func.getExtendedSlot(FunctionExtended::MODULE_SLOT)
.toObject()
.as<ModuleObject>());
AsyncModuleExecutionRejected(cx, module, args.get(0));
args.rval().setUndefined();
return true;
}
// Top Level Await
// https://tc39.es/proposal-top-level-await/#sec-asyncmodulexecutionfulfilled
void js::AsyncModuleExecutionFulfilled(JSContext* cx,
Handle<ModuleObject*> module) {
// Step 1.
MOZ_ASSERT(module->status() == ModuleStatus::Evaluated);
// Step 2.
MOZ_ASSERT(module->isAsyncEvaluating());
ModuleObject::onTopLevelEvaluationFinished(module);
if (module->hasTopLevelCapability()) {
MOZ_ASSERT(module->getCycleRoot() == module);
if (!ModuleObject::topLevelCapabilityResolve(cx, module)) {
// If Resolve fails, there's nothing more we can do here.
cx->clearPendingException();
}
}
Rooted<ModuleVector> sortedList(cx);
if (!GatherAvailableModuleAncestors(cx, module, &sortedList)) {
// We have been interrupted or have OOM'd -- all bets are off, reject the
// promise. Not much more we can do.
if (!cx->isExceptionPending()) {
AsyncModuleExecutionRejected(cx, module, UndefinedHandleValue);
return;
}
RootedValue exception(cx);
if (!cx->getPendingException(&exception)) {
return;
}
cx->clearPendingException();
AsyncModuleExecutionRejected(cx, module, exception);
}
// this is out of step with the spec in order to be able to OOM
module->setAsyncEvaluatingFalse();
Rooted<ModuleObject*> m(cx);
for (ModuleObject* obj : sortedList) {
m = obj;
// Step 2.
if (!m->isAsyncEvaluating()) {
MOZ_ASSERT(m->hadEvaluationError());
return;
}
if (m->isAsync()) {
// Steps for ExecuteAsyncModule
MOZ_ASSERT(m->status() == ModuleStatus::Evaluating ||
m->status() == ModuleStatus::Evaluated);
MOZ_ASSERT(m->isAsync());
MOZ_ASSERT(m->isAsyncEvaluating());
MOZ_ALWAYS_TRUE(ModuleObject::execute(cx, m));
} else {
if (!ModuleObject::execute(cx, m)) {
MOZ_ASSERT(cx->isExceptionPending());
RootedValue exception(cx);
if (!cx->getPendingException(&exception)) {
return;
}
cx->clearPendingException();
AsyncModuleExecutionRejected(cx, m, exception);
} else {
m->setAsyncEvaluatingFalse();
if (m->hasTopLevelCapability()) {
MOZ_ASSERT(m->getCycleRoot() == m);
if (!ModuleObject::topLevelCapabilityResolve(cx, m)) {
// If Resolve fails, there's nothing more we can do here.
cx->clearPendingException();
}
}
}
}
}
// Step 6.
// Return undefined.
}
// https://tc39.es/proposal-top-level-await/#sec-asyncmodulexecutionrejected
void js::AsyncModuleExecutionRejected(JSContext* cx,
Handle<ModuleObject*> module,
HandleValue error) {
// Step 1.
MOZ_ASSERT(module->status() == ModuleStatus::Evaluated ||
module->status() == ModuleStatus::Evaluated_Error);
// Step 2.
if (!module->isAsyncEvaluating()) {
MOZ_ASSERT(module->hadEvaluationError());
return;
}
ModuleObject::onTopLevelEvaluationFinished(module);
// Step 3.
MOZ_ASSERT(!module->hadEvaluationError());
// Step 4.
module->setEvaluationError(error);
// Step 5.
module->setAsyncEvaluatingFalse();
// Step 6.
uint32_t length = module->asyncParentModules()->length();
Rooted<ModuleObject*> parent(cx);
for (uint32_t i = 0; i < length; i++) {
parent =
&module->asyncParentModules()->get(i).toObject().as<ModuleObject>();
AsyncModuleExecutionRejected(cx, parent, error);
}
// Step 7.
if (module->hasTopLevelCapability()) {
MOZ_ASSERT(module->getCycleRoot() == module);
if (!ModuleObject::topLevelCapabilityReject(cx, module, error)) {
// If Reject fails, there's nothing more we can do here.
cx->clearPendingException();
}
}
// Return undefined.
}
bool ModuleObject::topLevelCapabilityResolve(JSContext* cx,
Handle<ModuleObject*> module) {
RootedValue rval(cx);

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

@ -259,6 +259,28 @@ enum class ModuleStatus : int32_t {
Evaluated_Error // Sub-state of Evaluated with error value set.
};
// Special values for ModuleObject's AsyncEvaluatingPostOrderSlot slot, which is
// used to implement the AsyncEvaluation field of cyclic module records.
//
// The spec requires us to distinguish true, false, and 'never previously set to
// true', as well as the order in which the field was set to true for async
// evaluating modules.
//
// This is arranged by using an integer to record the order. Both undefined and
// ASYNC_EVALUATING_POST_ORDER_FALSE are used to mean false, with undefined also
// meaning never previously set to true.
//
// See https://tc39.es/ecma262/#sec-cyclic-module-records for field defintion.
// See https://tc39.es/ecma262/#sec-async-module-execution-fulfilled for sort
// requirement.
// False value that also indicates that the field was previously true.
constexpr uint32_t ASYNC_EVALUATING_POST_ORDER_FALSE = 0;
// Initial value for the runtime's counter used to generate these values; the
// first non-false value.
constexpr uint32_t ASYNC_EVALUATING_POST_ORDER_INIT = 1;
class ModuleObject : public NativeObject {
public:
enum ModuleSlot {
@ -278,7 +300,7 @@ class ModuleObject : public NativeObject {
FunctionDeclarationsSlot,
DFSIndexSlot,
DFSAncestorIndexSlot,
AsyncSlot,
HasTopLevelAwaitSlot,
AsyncEvaluatingPostOrderSlot,
TopLevelCapabilitySlot,
AsyncParentModulesSlot,
@ -339,7 +361,7 @@ class ModuleObject : public NativeObject {
static PromiseObject* createTopLevelCapability(JSContext* cx,
Handle<ModuleObject*> module);
bool isAsync() const;
bool hasTopLevelAwait() const;
bool isAsyncEvaluating() const;
bool wasAsyncEvaluating() const;
void setAsyncEvaluating();
@ -381,7 +403,7 @@ class ModuleObject : public NativeObject {
static bool createEnvironment(JSContext* cx, Handle<ModuleObject*> self);
bool initAsyncSlots(JSContext* cx, bool isAsync,
bool initAsyncSlots(JSContext* cx, bool hasTopLevelAwait,
HandleObject asyncParentModulesList);
static bool GatherAsyncParentCompletions(
@ -405,21 +427,6 @@ ModuleObject* CallModuleResolveHook(JSContext* cx,
HandleValue referencingPrivate,
HandleObject moduleRequest);
// https://tc39.es/proposal-top-level-await/#sec-asyncmodulexecutionfulfilled
void AsyncModuleExecutionFulfilled(JSContext* cx, Handle<ModuleObject*> module);
// https://tc39.es/proposal-top-level-await/#sec-asyncmodulexecutionrejected
void AsyncModuleExecutionRejected(JSContext* cx, Handle<ModuleObject*> module,
HandleValue error);
// https://tc39.es/proposal-top-level-await/#sec-asyncmodulexecutionfulfilled
bool AsyncModuleExecutionFulfilledHandler(JSContext* cx, unsigned argc,
Value* vp);
// https://tc39.es/proposal-top-level-await/#sec-asyncmodulexecutionrejected
bool AsyncModuleExecutionRejectedHandler(JSContext* cx, unsigned argc,
Value* vp);
JSObject* StartDynamicModuleImport(JSContext* cx, HandleScript script,
HandleValue specifier, HandleValue options);

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

@ -163,12 +163,12 @@ assertEq(l.dfsAncestorIndex, 0);
// ==== async and promises getters ====
const m = parseModule(`
`);
assertEq(m.async, false);
assertEq(m.hasTopLevelAwait, false);
assertEq(m.topLevelCapability, undefined);
assertEq(m.asyncEvaluatingPostOrder, undefined);
assertEq(m.asyncParentModules[0], undefined);
assertEq(m.pendingAsyncDependencies, undefined);
testGetter(m, "async");
testGetter(m, "hasTopLevelAwait");
testGetter(m, "topLevelCapability");
testGetter(m, "asyncEvaluatingPostOrder");
testGetter(m, "asyncParentModules");

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

@ -0,0 +1,12 @@
// |jit-test| skip-if: !('oomTest' in this)
var dbgGlobal = newGlobal({newCompartment: true});
var dbg = new dbgGlobal.Debugger();
dbg.addDebuggee(this);
oomTest(() => {
wasmEvalText(`
(import "" "" (func $d))
(func try call $d end)
`);
});

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

@ -305,7 +305,8 @@ DEFINE_GETTER_FUNCTIONS(ModuleObject, maybeDfsIndex, Uint32OrUndefinedValue,
IdentFilter)
DEFINE_GETTER_FUNCTIONS(ModuleObject, maybeDfsAncestorIndex,
Uint32OrUndefinedValue, IdentFilter)
DEFINE_GETTER_FUNCTIONS(ModuleObject, isAsync, BooleanValue, IdentFilter)
DEFINE_GETTER_FUNCTIONS(ModuleObject, hasTopLevelAwait, BooleanValue,
IdentFilter)
DEFINE_GETTER_FUNCTIONS(ModuleObject, maybeTopLevelCapability,
ObjectOrUndefinedValue, IdentFilter)
DEFINE_GETTER_FUNCTIONS(ModuleObject, maybeAsyncEvaluatingPostOrder,
@ -332,7 +333,8 @@ static const JSPropertySpec ShellModuleObjectWrapper_accessors[] = {
JS_PSG("dfsIndex", ShellModuleObjectWrapper_maybeDfsIndexGetter, 0),
JS_PSG("dfsAncestorIndex",
ShellModuleObjectWrapper_maybeDfsAncestorIndexGetter, 0),
JS_PSG("async", ShellModuleObjectWrapper_isAsyncGetter, 0),
JS_PSG("hasTopLevelAwait", ShellModuleObjectWrapper_hasTopLevelAwaitGetter,
0),
JS_PSG("topLevelCapability",
ShellModuleObjectWrapper_maybeTopLevelCapabilityGetter, 0),
JS_PSG("asyncEvaluatingPostOrder",

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

@ -16,6 +16,7 @@
#include "vm/GeneratorObject.h"
#include "vm/GlobalObject.h"
#include "vm/Interpreter.h"
#include "vm/Modules.h"
#include "vm/NativeObject.h"
#include "vm/PromiseObject.h" // js::PromiseObject
#include "vm/Realm.h"
@ -279,6 +280,33 @@ JSFunction* NewHandler(JSContext* cx, Native handler,
return handlerFun;
}
static bool AsyncModuleExecutionFulfilledHandler(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
JSFunction& func = args.callee().as<JSFunction>();
Rooted<ModuleObject*> module(
cx, &func.getExtendedSlot(FunctionExtended::MODULE_SLOT)
.toObject()
.as<ModuleObject>());
AsyncModuleExecutionFulfilled(cx, module);
args.rval().setUndefined();
return true;
}
static bool AsyncModuleExecutionRejectedHandler(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
JSFunction& func = args.callee().as<JSFunction>();
Rooted<ModuleObject*> module(
cx, &func.getExtendedSlot(FunctionExtended::MODULE_SLOT)
.toObject()
.as<ModuleObject>());
AsyncModuleExecutionRejected(cx, module, args.get(0));
args.rval().setUndefined();
return true;
}
AsyncFunctionGeneratorObject* AsyncFunctionGeneratorObject::create(
JSContext* cx, Handle<ModuleObject*> module) {
// TODO: Module is currently hitching a ride with

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

@ -2427,7 +2427,7 @@ void DebugEnvironmentProxy::initSnapshot(ArrayObject& o) {
} else {
auto* moduleEnv = ModuleEnvironmentObject::find(&environment());
MOZ_ASSERT(moduleEnv);
MOZ_ASSERT(moduleEnv->module().isAsync());
MOZ_ASSERT(moduleEnv->module().hasTopLevelAwait());
}
}
#endif

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

@ -37,6 +37,8 @@ using mozilla::Utf8Unit;
////////////////////////////////////////////////////////////////////////////////
// Public API
using NameList = GCVector<JSAtom*, 0, SystemAllocPolicy>;
JS_PUBLIC_API JS::SupportedAssertionsHook JS::GetSupportedAssertionsHook(
JSRuntime* rt) {
AssertHeapIsIdle();
@ -332,10 +334,9 @@ static ArrayObject* NewList(JSContext* cx) {
return NewArrayWithNullProto(cx);
}
static bool ArrayContainsName(Handle<ArrayObject*> array,
Handle<JSAtom*> name) {
for (uint32_t i = 0; i < array->length(); i++) {
if (array->getDenseElement(i) == StringValue(name)) {
static bool ContainsElement(Handle<NameList> list, JSAtom* atom) {
for (JSAtom* a : list) {
if (a == atom) {
return true;
}
}
@ -368,26 +369,23 @@ static size_t CountElements(Handle<ModuleVector> stack, ModuleObject* module) {
// https://tc39.es/ecma262/#sec-getexportednames
// ES2023 16.2.1.6.2 GetExportedNames
static ArrayObject* ModuleGetExportedNames(
JSContext* cx, Handle<ModuleObject*> module,
MutableHandle<ModuleSet> exportStarSet) {
static bool ModuleGetExportedNames(JSContext* cx, Handle<ModuleObject*> module,
MutableHandle<ModuleSet> exportStarSet,
MutableHandle<NameList> exportedNames) {
// Step 4. Let exportedNames be a new empty List.
Rooted<ArrayObject*> exportedNames(cx, NewList(cx));
if (!exportedNames) {
return nullptr;
}
MOZ_ASSERT(exportedNames.empty());
// Step 2. If exportStarSet contains module, then:
if (exportStarSet.has(module)) {
// Step 2.a. We've reached the starting point of an export * circularity.
// Step 2.b. Return a new empty List.
return exportedNames;
return true;
}
// Step 3. Append module to exportStarSet.
if (!exportStarSet.put(module)) {
ReportOutOfMemory(cx);
return nullptr;
return false;
}
// Step 5. For each ExportEntry Record e of module.[[LocalExportEntries]], do:
@ -399,8 +397,9 @@ static ArrayObject* ModuleGetExportedNames(
.as<ExportEntryObject>();
// Step 5.a. Assert: module provides the direct binding for this export.
// Step 5.b. Append e.[[ExportName]] to exportedNames.
if (!NewbornArrayPush(cx, exportedNames, StringValue(e->exportName()))) {
return nullptr;
if (!exportedNames.append(e->exportName())) {
ReportOutOfMemory(cx);
return false;
}
}
@ -414,8 +413,9 @@ static ArrayObject* ModuleGetExportedNames(
.as<ExportEntryObject>();
// Step 6.a. Assert: module imports a specific binding for this export.
// Step 6.b. Append e.[[ExportName]] to exportedNames.
if (!NewbornArrayPush(cx, exportedNames, StringValue(e->exportName()))) {
return nullptr;
if (!exportedNames.append(e->exportName())) {
ReportOutOfMemory(cx);
return false;
}
}
@ -436,27 +436,27 @@ static ArrayObject* ModuleGetExportedNames(
requestedModule = HostResolveImportedModule(cx, module, moduleRequest,
ModuleStatus::Unlinked);
if (!requestedModule) {
return nullptr;
return false;
}
// Step 7.b. Let starNames be ?
// requestedModule.GetExportedNames(exportStarSet).
starNames = ModuleGetExportedNames(cx, requestedModule, exportStarSet);
if (!starNames) {
return nullptr;
Rooted<NameList> starNames(cx);
if (!ModuleGetExportedNames(cx, requestedModule, exportStarSet,
&starNames)) {
return false;
}
// Step 7.c. For each element n of starNames, do:
for (uint32_t j = 0; j != starNames->length(); j++) {
name = &starNames->getDenseElement(j).toString()->asAtom();
for (JSAtom* name : starNames) {
// Step 7.c.i. If SameValue(n, "default") is false, then:
if (name != cx->names().default_) {
// Step 7.c.i.1. If n is not an element of exportedNames, then:
if (!ArrayContainsName(exportedNames, name)) {
if (!ContainsElement(exportedNames, name)) {
// Step 7.c.i.1.a. Append n to exportedNames.
if (!NewbornArrayPush(cx, exportedNames, StringValue(name))) {
return nullptr;
if (!exportedNames.append(name)) {
ReportOutOfMemory(cx);
return false;
}
}
}
@ -464,7 +464,7 @@ static ArrayObject* ModuleGetExportedNames(
}
// Step 8. Return exportedNames.
return exportedNames;
return true;
}
static ModuleObject* HostResolveImportedModule(
@ -710,9 +710,8 @@ ModuleNamespaceObject* js::GetOrCreateModuleNamespace(
if (!ns) {
// Step 3.a. Let exportedNames be ? module.GetExportedNames().
Rooted<ModuleSet> exportStarSet(cx);
Rooted<ArrayObject*> exportedNames(cx);
exportedNames = ModuleGetExportedNames(cx, module, &exportStarSet);
if (!exportedNames) {
Rooted<NameList> exportedNames(cx);
if (!ModuleGetExportedNames(cx, module, &exportStarSet, &exportedNames)) {
return nullptr;
}
@ -725,8 +724,8 @@ ModuleNamespaceObject* js::GetOrCreateModuleNamespace(
// Step 3.c. For each element name of exportedNames, do:
Rooted<JSAtom*> name(cx);
Rooted<Value> resolution(cx);
for (uint32_t i = 0; i != exportedNames->length(); i++) {
name = &exportedNames->getDenseElement(i).toString()->asAtom();
for (JSAtom* atom : exportedNames) {
name = atom;
// Step 3.c.i. Let resolution be ? module.ResolveExport(name).
if (!ModuleResolveExport(cx, module, name, &resolution)) {
@ -1450,7 +1449,7 @@ static bool InnerModuleEvaluation(JSContext* cx, Handle<ModuleObject*> module,
// Step 12. If module.[[PendingAsyncDependencies]] > 0 or module.[[HasTLA]] is
// true, then:
if (module->pendingAsyncDependencies() > 0 || module->isAsync()) {
if (module->pendingAsyncDependencies() > 0 || module->hasTopLevelAwait()) {
// Step 12.a. Assert: module.[[AsyncEvaluation]] is false and was never
// previously set to true.
MOZ_ASSERT(!module->isAsyncEvaluating() && !module->wasAsyncEvaluating());
@ -1522,7 +1521,7 @@ static bool ExecuteAsyncModule(JSContext* cx, Handle<ModuleObject*> module) {
module->status() == ModuleStatus::Evaluated);
// Step 2. Assert: module.[[HasTLA]] is true.
MOZ_ASSERT(module->isAsync());
MOZ_ASSERT(module->hasTopLevelAwait());
// Steps 3 - 8 are performed by the AsyncAwait opcode.
@ -1610,7 +1609,7 @@ static bool GatherAvailableModuleAncestors(
// Step 1.a.vi.2. If m.[[HasTLA]] is false, perform
// GatherAvailableAncestors(m, execList).
if (!m->isAsync() &&
if (!m->hasTopLevelAwait() &&
!::GatherAvailableModuleAncestors(cx, m, execList)) {
return false;
}
@ -1621,3 +1620,127 @@ static bool GatherAvailableModuleAncestors(
// Step 2. Return unused.
return true;
}
// https://tc39.es/proposal-top-level-await/#sec-asyncmodulexecutionfulfilled
void js::AsyncModuleExecutionFulfilled(JSContext* cx,
Handle<ModuleObject*> module) {
// Step 1.
MOZ_ASSERT(module->status() == ModuleStatus::Evaluated);
// Step 2.
MOZ_ASSERT(module->isAsyncEvaluating());
ModuleObject::onTopLevelEvaluationFinished(module);
if (module->hasTopLevelCapability()) {
MOZ_ASSERT(module->getCycleRoot() == module);
if (!ModuleObject::topLevelCapabilityResolve(cx, module)) {
// If Resolve fails, there's nothing more we can do here.
cx->clearPendingException();
}
}
Rooted<ModuleVector> sortedList(cx);
if (!js::GatherAvailableModuleAncestors(cx, module, &sortedList)) {
// We have been interrupted or have OOM'd -- all bets are off, reject the
// promise. Not much more we can do.
if (!cx->isExceptionPending()) {
AsyncModuleExecutionRejected(cx, module, UndefinedHandleValue);
return;
}
RootedValue exception(cx);
if (!cx->getPendingException(&exception)) {
return;
}
cx->clearPendingException();
AsyncModuleExecutionRejected(cx, module, exception);
}
// this is out of step with the spec in order to be able to OOM
module->setAsyncEvaluatingFalse();
Rooted<ModuleObject*> m(cx);
for (ModuleObject* obj : sortedList) {
m = obj;
// Step 2.
if (!m->isAsyncEvaluating()) {
MOZ_ASSERT(m->hadEvaluationError());
return;
}
if (m->hasTopLevelAwait()) {
MOZ_ALWAYS_TRUE(ExecuteAsyncModule(cx, m));
} else {
if (!ModuleObject::execute(cx, m)) {
MOZ_ASSERT(cx->isExceptionPending());
RootedValue exception(cx);
if (!cx->getPendingException(&exception)) {
return;
}
cx->clearPendingException();
AsyncModuleExecutionRejected(cx, m, exception);
} else {
m->setAsyncEvaluatingFalse();
if (m->hasTopLevelCapability()) {
MOZ_ASSERT(m->getCycleRoot() == m);
if (!ModuleObject::topLevelCapabilityResolve(cx, m)) {
// If Resolve fails, there's nothing more we can do here.
cx->clearPendingException();
}
}
}
}
}
// Step 6.
// Return undefined.
}
// https://tc39.es/proposal-top-level-await/#sec-asyncmodulexecutionrejected
void js::AsyncModuleExecutionRejected(JSContext* cx,
Handle<ModuleObject*> module,
HandleValue error) {
// Step 1.
MOZ_ASSERT(module->status() == ModuleStatus::Evaluated ||
module->status() == ModuleStatus::Evaluated_Error);
// Step 2.
if (!module->isAsyncEvaluating()) {
MOZ_ASSERT(module->hadEvaluationError());
return;
}
ModuleObject::onTopLevelEvaluationFinished(module);
// Step 3.
MOZ_ASSERT(!module->hadEvaluationError());
// Step 4.
module->setEvaluationError(error);
// Step 5.
module->setAsyncEvaluatingFalse();
// Step 6.
uint32_t length = module->asyncParentModules()->length();
Rooted<ModuleObject*> parent(cx);
for (uint32_t i = 0; i < length; i++) {
parent =
&module->asyncParentModules()->get(i).toObject().as<ModuleObject>();
AsyncModuleExecutionRejected(cx, parent, error);
}
// Step 7.
if (module->hasTopLevelCapability()) {
MOZ_ASSERT(module->getCycleRoot() == module);
if (!ModuleObject::topLevelCapabilityReject(cx, module, error)) {
// If Reject fails, there's nothing more we can do here.
cx->clearPendingException();
}
}
// Return undefined.
}

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

@ -41,6 +41,11 @@ bool ModuleEvaluate(JSContext* cx, Handle<ModuleObject*> module,
bool GatherAvailableModuleAncestors(JSContext* cx, Handle<ModuleObject*> module,
MutableHandle<ModuleVector> sortedList);
void AsyncModuleExecutionFulfilled(JSContext* cx, Handle<ModuleObject*> module);
void AsyncModuleExecutionRejected(JSContext* cx, Handle<ModuleObject*> module,
HandleValue error);
} // namespace js
#endif // vm_Modules_h

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

@ -158,6 +158,7 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
stackFormat_(parentRuntime ? js::StackFormat::Default
: js::StackFormat::SpiderMonkey),
wasmInstances(mutexid::WasmRuntimeInstances),
moduleAsyncEvaluatingPostOrder(ASYNC_EVALUATING_POST_ORDER_INIT),
moduleResolveHook(),
moduleMetadataHook(),
moduleDynamicImportHook(),

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

@ -978,6 +978,15 @@ struct JSRuntime {
// threads for purposes of wasm::InterruptRunningCode().
js::ExclusiveData<js::wasm::InstanceVector> wasmInstances;
// A counter used when recording the order in which modules had their
// AsyncEvaluating field set to true. This is used to order queued
// evaluations. This is reset when the last module that was async evaluating
// is finished.
//
// See https://tc39.es/ecma262/#sec-async-module-execution-fulfilled step 10
// for use.
js::MainThreadData<uint32_t> moduleAsyncEvaluatingPostOrder;
// The implementation-defined abstract operation HostResolveImportedModule.
js::MainThreadData<JS::ModuleResolveHook> moduleResolveHook;

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

@ -651,7 +651,7 @@ void BaseCompiler::insertBreakablePoint(CallSiteDesc::Kind kind) {
CodeOffset(masm.currentOffset()));
// Branch destination
MOZ_ASSERT(masm.currentOffset() == uint32_t(L.offset()));
MOZ_ASSERT_IF(!masm.oom(), masm.currentOffset() == uint32_t(L.offset()));
#elif defined(JS_CODEGEN_X86)
// 83 MODRM OFFS IB
static_assert(Instance::offsetOfDebugTrapHandler() < 128);
@ -669,7 +669,7 @@ void BaseCompiler::insertBreakablePoint(CallSiteDesc::Kind kind) {
CodeOffset(masm.currentOffset()));
// Branch destination
MOZ_ASSERT(masm.currentOffset() == uint32_t(L.offset()));
MOZ_ASSERT_IF(!masm.oom(), masm.currentOffset() == uint32_t(L.offset()));
#elif defined(JS_CODEGEN_ARM64)
ScratchPtr scratch(*this);
ARMRegister tmp(scratch, 64);

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

@ -278,17 +278,22 @@ static void MaybeSanitizeException(JSContext* cx,
// to less-privileged code.
nsIPrincipal* callerPrincipal = nsContentUtils::SubjectPrincipal(cx);
// No need to sanitize uncatchable exceptions, just return.
if (!JS_IsExceptionPending(cx)) {
return;
}
// Re-enter the unwrappedFun Realm to do get the current exception, so we
// don't end up unnecessarily wrapping exceptions.
{ // Scope for JSAutoRealm
JSAutoRealm ar(cx, unwrappedFun);
JS::ExceptionStack exnStack(cx);
// If JS::GetPendingExceptionStack returns false, this was an uncatchable
// exception, or we somehow failed to wrap the exception into our
// compartment. In either case, treating this as uncatchable exception,
// by returning without setting any exception on the JSContext,
// seems fine.
// If JS::GetPendingExceptionStack returns false, we somehow failed to wrap
// the exception into our compartment. It seems fine to treat this as an
// uncatchable exception by returning without setting any exception on the
// JS context.
if (!JS::GetPendingExceptionStack(cx, &exnStack)) {
JS_ClearPendingException(cx);
return;

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

@ -121,7 +121,7 @@ class FontFaceSetImpl : public nsISupports, public gfxUserFontSet {
*/
virtual void DidRefresh() { MOZ_ASSERT_UNREACHABLE("Not implemented!"); }
virtual void FlushUserFontSet() {}
virtual void FlushUserFontSet() = 0;
static nsPresContext* GetPresContextFor(gfxUserFontSet* aUserFontSet) {
const auto* set = static_cast<FontFaceSetImpl*>(aUserFontSet);

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

@ -135,6 +135,36 @@ void FontFaceSetWorkerImpl::InitializeOnMainThread() {
void FontFaceSetWorkerImpl::Destroy() {
RecursiveMutexAutoLock lock(mMutex);
class DestroyRunnable final : public Runnable {
public:
DestroyRunnable(FontFaceSetWorkerImpl* aFontFaceSet,
nsTHashtable<nsPtrHashKey<nsFontFaceLoader>>&& aLoaders)
: Runnable("FontFaceSetWorkerImpl::Destroy"),
mFontFaceSet(aFontFaceSet),
mLoaders(std::move(aLoaders)) {}
protected:
~DestroyRunnable() override = default;
NS_IMETHOD Run() override {
for (const auto& key : mLoaders.Keys()) {
key->Cancel();
}
return NS_OK;
}
// We save a reference to the FontFaceSetWorkerImpl because the loaders
// contain a non-owning reference to it.
RefPtr<FontFaceSetWorkerImpl> mFontFaceSet;
nsTHashtable<nsPtrHashKey<nsFontFaceLoader>> mLoaders;
};
if (!mLoaders.IsEmpty() && !NS_IsMainThread()) {
auto runnable = MakeRefPtr<DestroyRunnable>(this, std::move(mLoaders));
NS_DispatchToMainThread(runnable);
}
mWorkerRef = nullptr;
FontFaceSetImpl::Destroy();
}
@ -191,6 +221,33 @@ uint64_t FontFaceSetWorkerImpl::GetInnerWindowID() {
return mWorkerRef->Private()->WindowID();
}
void FontFaceSetWorkerImpl::FlushUserFontSet() {
RecursiveMutexAutoLock lock(mMutex);
// If there was a change to the mNonRuleFaces array, then there could
// have been a modification to the user font set.
bool modified = mNonRuleFacesDirty;
mNonRuleFacesDirty = false;
for (size_t i = 0, i_end = mNonRuleFaces.Length(); i < i_end; ++i) {
InsertNonRuleFontFace(mNonRuleFaces[i].mFontFace, modified);
}
// Remove any residual families that have no font entries.
for (auto it = mFontFamilies.Iter(); !it.Done(); it.Next()) {
if (!it.Data()->FontListLength()) {
it.Remove();
}
}
if (modified) {
IncrementGeneration(true);
mHasLoadingFontFacesIsDirty = true;
CheckLoadingStarted();
CheckLoadingFinished();
}
}
nsresult FontFaceSetWorkerImpl::StartLoad(gfxUserFontEntry* aUserFontEntry,
uint32_t aSrcIndex) {
RecursiveMutexAutoLock lock(mMutex);

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

@ -28,6 +28,8 @@ class FontFaceSetWorkerImpl final : public FontFaceSetImpl {
already_AddRefed<URLExtraData> GetURLExtraData() override;
void FlushUserFontSet() override;
// gfxUserFontSet
nsresult StartLoad(gfxUserFontEntry* aUserFontEntry,

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

@ -16,6 +16,7 @@
# include <pthread.h>
#endif
#include "mozilla/Attributes.h"
#include "mozilla/ThreadSafety.h"
#if defined(XP_DARWIN)
// For information about the following undocumented flags and functions see
@ -40,11 +41,10 @@ static_assert(sizeof(os_unfair_lock) == sizeof(OSSpinLock),
#endif // defined(XP_DARWIN)
// Mutexes based on spinlocks. We can't use normal pthread spinlocks in all
// places, because they require malloc()ed memory, which causes
// bootstrapping issues in some cases. We also can't use constructors,
// because for statics, they would fire after the first use of malloc,
// resetting the locks.
struct Mutex {
// places, because they require malloc()ed memory, which causes bootstrapping
// issues in some cases. We also can't use constructors, because for statics,
// they would fire after the first use of malloc, resetting the locks.
struct CAPABILITY Mutex {
#if defined(XP_WIN)
CRITICAL_SECTION mMutex;
#elif defined(XP_DARWIN)
@ -86,7 +86,7 @@ struct Mutex {
return true;
}
inline void Lock() {
inline void Lock() CAPABILITY_ACQUIRE() {
#if defined(XP_WIN)
EnterCriticalSection(&mMutex);
#elif defined(XP_DARWIN)
@ -109,7 +109,7 @@ struct Mutex {
#endif
}
inline void Unlock() {
inline void Unlock() CAPABILITY_RELEASE() {
#if defined(XP_WIN)
LeaveCriticalSection(&mMutex);
#elif defined(XP_DARWIN)
@ -137,12 +137,14 @@ struct Mutex {
// Ideally, we'd use the same type of locks everywhere, but SRWLocks
// everywhere incur a performance penalty. See bug 1418389.
#if defined(XP_WIN)
struct StaticMutex {
struct CAPABILITY StaticMutex {
SRWLOCK mMutex;
inline void Lock() { AcquireSRWLockExclusive(&mMutex); }
inline void Lock() CAPABILITY_ACQUIRE() { AcquireSRWLockExclusive(&mMutex); }
inline void Unlock() { ReleaseSRWLockExclusive(&mMutex); }
inline void Unlock() CAPABILITY_RELEASE() {
ReleaseSRWLockExclusive(&mMutex);
}
};
// Normally, we'd use a constexpr constructor, but MSVC likes to create
@ -166,10 +168,15 @@ typedef Mutex StaticMutex;
#endif
template <typename T>
struct MOZ_RAII AutoLock {
explicit AutoLock(T& aMutex) : mMutex(aMutex) { mMutex.Lock(); }
struct SCOPED_CAPABILITY MOZ_RAII AutoLock {
explicit AutoLock(T& aMutex) CAPABILITY_ACQUIRE(aMutex) : mMutex(aMutex) {
mMutex.Lock();
}
~AutoLock() { mMutex.Unlock(); }
~AutoLock() CAPABILITY_RELEASE() { mMutex.Unlock(); }
AutoLock(const AutoLock&) = delete;
AutoLock(AutoLock&&) = delete;
private:
T& mMutex;

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

@ -4641,11 +4641,8 @@ inline void MozJemalloc::moz_dispose_arena(arena_id_t aArenaId) {
// of malloc during fork(). These functions are only called if the program is
// running in threaded mode, so there is no need to check whether the program
// is threaded here.
# ifndef XP_DARWIN
static
# endif
void
_malloc_prefork(void) {
FORK_HOOK
void _malloc_prefork(void) NO_THREAD_SAFETY_ANALYSIS {
// Acquire all mutexes in a safe order.
gArenas.mLock.Lock();
@ -4658,11 +4655,8 @@ static
huge_mtx.Lock();
}
# ifndef XP_DARWIN
static
# endif
void
_malloc_postfork_parent(void) {
FORK_HOOK
void _malloc_postfork_parent(void) NO_THREAD_SAFETY_ANALYSIS {
// Release all mutexes, now that fork() has completed.
huge_mtx.Unlock();
@ -4675,11 +4669,8 @@ static
gArenas.mLock.Unlock();
}
# ifndef XP_DARWIN
static
# endif
void
_malloc_postfork_child(void) {
FORK_HOOK
void _malloc_postfork_child(void) {
// Reinitialize all mutexes, now that fork() has completed.
huge_mtx.Init();

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

@ -27,8 +27,8 @@ static bool sStdoutOrStderr = false;
static Mutex sMutex MOZ_UNANNOTATED;
#ifndef _WIN32
static void prefork() { sMutex.Lock(); }
static void postfork_parent() { sMutex.Unlock(); }
static void prefork() NO_THREAD_SAFETY_ANALYSIS { sMutex.Lock(); }
static void postfork_parent() NO_THREAD_SAFETY_ANALYSIS { sMutex.Unlock(); }
static void postfork_child() { sMutex.Init(); }
#endif

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

@ -793,14 +793,16 @@ class GMut {
MOZ_RELEASE_ASSERT(page.mBaseAddr == aPtr);
if (page.mState == AllocPageState::Freed) {
LOG("EnsureValidAndInUse(%p), use-after-free\n", aPtr);
// An operation on a freed page? This is a particular kind of
// use-after-free. Deliberately touch the page in question, in order to
// cause a crash that triggers the usual PHC machinery. But unlock sMutex
// first, because that self-same PHC machinery needs to re-lock it, and
// the crash causes non-local control flow so sMutex won't be unlocked
// the normal way in the caller.
LOG("EnsureValidAndInUse(%p), use-after-free\n", aPtr);
PUSH_IGNORE_THREAD_SAFETY
sMutex.Unlock();
POP_THREAD_SAFETY
*static_cast<uint8_t*>(aPtr) = 0;
MOZ_CRASH("unreachable");
}
@ -877,9 +879,11 @@ class GMut {
*aInfo = {TagUnknown, nullptr, 0, 0};
}
static void prefork() { sMutex.Lock(); }
static void postfork_parent() { sMutex.Unlock(); }
#ifndef XP_WIN
static void prefork() NO_THREAD_SAFETY_ANALYSIS { sMutex.Lock(); }
static void postfork_parent() NO_THREAD_SAFETY_ANALYSIS { sMutex.Unlock(); }
static void postfork_child() { sMutex.Init(); }
#endif
void IncPageAllocHits(GMutLock) { mPageAllocHits++; }
void IncPageAllocMisses(GMutLock) { mPageAllocMisses++; }

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

@ -3640,6 +3640,19 @@
value: false
mirror: always
# Forward-Declared Storage-access API.
- name: dom.storage_access.forward_declared.enabled
type: bool
value: false
mirror: always
# How long the Forward-Declared Storage-access API allows between pair requests
# in seconds
- name: dom.storage_access.forward_declared.lifetime
type: uint32_t
value: 15 * 60
mirror: always
# The maximum number of origins that a given third-party tracker is allowed
# to have concurrent access to before the user is presented with a storage
# access prompt. Only effective when the auto_grants pref is turned on.

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

@ -57,13 +57,15 @@ class ActivationContext:
def test_new_package_appears_in_pkg_resources():
try:
# "carrot" was chosen as the package to use because:
# "hyperframe" was chosen as the package to use because:
# * It has to be a package that doesn't exist in-scope at the start (so,
# all vendored modules included in the test virtualenv aren't usage).
# all vendored modules included in the test virtualenv aren't usable).
# * It must be on our internal PyPI mirror.
# Of the options, "carrot" is a small install that fits these requirements.
pkg_resources.get_distribution("carrot")
assert False, "Expected to not find 'carrot' as the initial state of the test"
# Of the options, "hyperframe" is a small install that fits these requirements.
pkg_resources.get_distribution("hyperframe")
assert (
False
), "Expected to not find 'hyperframe' as the initial state of the test"
except pkg_resources.DistributionNotFound:
pass
@ -84,7 +86,7 @@ def test_new_package_appears_in_pkg_resources():
)
venv = PythonVirtualenv(venv_dir)
venv.pip_install(["carrot==0.10.7"])
venv.pip_install(["hyperframe==5.2.0"])
initial_metadata = MozSiteMetadata.from_runtime()
try:
@ -92,7 +94,7 @@ def test_new_package_appears_in_pkg_resources():
with metadata.update_current_site(venv.python_path):
activate_virtualenv(venv)
assert pkg_resources.get_distribution("carrot").version == "0.10.7"
assert pkg_resources.get_distribution("hyperframe").version == "5.2.0"
finally:
MozSiteMetadata.current = initial_metadata

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

@ -431,7 +431,7 @@ fxms-schemas:
cwd: '{checkout}'
command: >
cd browser/components/newtab/content-src/asrouter/schemas &&
python3 make-schemas.py --check
../../../../../../mach python -- make-schemas.py --check
when:
files-changed:
- 'browser/components/newtab/content-src/asrouter/schemas/make-schemas.py'

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

@ -14,6 +14,8 @@ export MOZCONFIG=$GECKO_PATH/mozconfig
cat <<EOT >> $MOZCONFIG
# Enable debug mode
ac_add_options --enable-debug
# Enable clang-plugin in order to have all defines activated for static-analysis
ac_add_options --enable-clang-plugin
# Enable GC zeal, a testing and debugging feature that helps find GC-related bugs in JSAPI applications.
ac_add_options --enable-gczeal
# Do not treat warnings as errors

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

@ -1,16 +0,0 @@
[2d.text.draw.generic.family.html]
[Test that drawing sans-serif produces the same result between canvas and OffscreenCanvas]
expected: FAIL
[Test that drawing serif produces the same result between canvas and OffscreenCanvas]
expected: FAIL
[Test that drawing cursive produces the same result between canvas and OffscreenCanvas]
expected: FAIL
[Test that drawing fantasy produces the same result between canvas and OffscreenCanvas]
expected: FAIL
[Test that drawing monospace produces the same result between canvas and OffscreenCanvas]
expected: FAIL

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

@ -1,17 +0,0 @@
[2d.text.draw.generic.family.w.html]
expected: ERROR
[Test that drawing sans-serif produces the same result between canvas and OffscreenCanvas in a Worker]
expected: TIMEOUT
[Test that drawing serif produces the same result between canvas and OffscreenCanvas in a Worker]
expected: TIMEOUT
[Test that drawing cursive produces the same result between canvas and OffscreenCanvas in a Worker]
expected: TIMEOUT
[Test that drawing fantasy produces the same result between canvas and OffscreenCanvas in a Worker]
expected: TIMEOUT
[Test that drawing monospace produces the same result between canvas and OffscreenCanvas in a Worker]
expected: TIMEOUT

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

@ -1,4 +0,0 @@
[2d.text.align.default.html]
[OffscreenCanvas test: 2d.text.align.default]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.align.default.worker.html]
[2d]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.align.invalid.html]
[OffscreenCanvas test: 2d.text.align.invalid]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.align.invalid.worker.html]
[2d]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.baseline.default.html]
[OffscreenCanvas test: 2d.text.baseline.default]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.baseline.default.worker.html]
[2d]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.baseline.invalid.html]
[OffscreenCanvas test: 2d.text.baseline.invalid]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.baseline.invalid.worker.html]
[2d]
expected: FAIL

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

@ -1,7 +0,0 @@
[2d.text.draw.align.center.html]
expected:
if (os == "android") and not debug: [OK, TIMEOUT]
[textAlign center is the center of the em squares (not the bounding box)]
expected:
if (os == "android") and not debug: [FAIL, TIMEOUT]
FAIL

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

@ -1,4 +0,0 @@
[2d.text.draw.align.center.worker.html]
[textAlign center is the center of the em squares (not the bounding box)]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.draw.align.end.ltr.html]
[textAlign end with ltr is the right edge]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.draw.align.end.ltr.worker.html]
[textAlign end with ltr is the right edge]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.draw.align.end.rtl.html]
[textAlign end with rtl is the left edge]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.draw.align.end.rtl.worker.html]
[textAlign end with rtl is the left edge]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.draw.align.left.html]
[textAlign left is the left of the first em square (not the bounding box)]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.draw.align.left.worker.html]
[textAlign left is the left of the first em square (not the bounding box)]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.draw.align.right.html]
[textAlign right is the right of the last em square (not the bounding box)]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.draw.align.right.worker.html]
[textAlign right is the right of the last em square (not the bounding box)]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.draw.align.start.ltr.html]
[textAlign start with ltr is the left edge]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.draw.align.start.ltr.worker.html]
[textAlign start with ltr is the left edge]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.draw.align.start.rtl.html]
[textAlign start with rtl is the right edge]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.draw.align.start.rtl.worker.html]
[textAlign start with rtl is the right edge]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.draw.baseline.alphabetic.html]
[OffscreenCanvas test: 2d.text.draw.baseline.alphabetic]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.draw.baseline.alphabetic.worker.html]
[2d]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.draw.baseline.bottom.html]
[textBaseline bottom is the bottom of the em square (not the bounding box)]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.draw.baseline.bottom.worker.html]
[textBaseline bottom is the bottom of the em square (not the bounding box)]
expected: FAIL

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

@ -1,4 +1,5 @@
[2d.text.draw.baseline.hanging.html]
[OffscreenCanvas test: 2d.text.draw.baseline.hanging]
expected: FAIL
expected:
if (os == 'android'): FAIL

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

@ -1,4 +1,5 @@
[2d.text.draw.baseline.hanging.worker.html]
[2d]
expected: FAIL
expected:
if (os == 'android'): FAIL

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

@ -1,4 +0,0 @@
[2d.text.draw.baseline.middle.html]
[textBaseline middle is the middle of the em square (not the bounding box)]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.draw.baseline.middle.worker.html]
[textBaseline middle is the middle of the em square (not the bounding box)]
expected: FAIL

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

@ -1,4 +0,0 @@
[2d.text.draw.baseline.top.html]
[textBaseline top is the top of the em square (not the bounding box)]
expected: FAIL

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