Merge mozilla-central to inbound a=merge on a CLOSED TREE

This commit is contained in:
Coroiu Cristina 2019-04-16 00:39:47 +03:00
Родитель c3ede82a5a a1a4b28ebb
Коммит c862999ac0
120 изменённых файлов: 2040 добавлений и 2017 удалений

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

@ -227,6 +227,8 @@ intl/icu/**
intl/locale/**
intl/strres/**
intl/uconv/**
# Bug 1527075: This directory is linted in github repository
intl/l10n/**
# Third-party
layout/mathml/imptests/**

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

@ -13,7 +13,11 @@ add_task(async function testAutocompleteRichResult() {
let accService = await initAccessibilityService();
info("Opening the URL bar and entering a key to show the PopupAutoCompleteRichResult panel");
await UrlbarTestUtils.promiseAutocompleteResultPopup(window, "a", waitForFocus);
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
waitForFocus,
value: "a"
});
info("Waiting for accessibility to be created for the richlistbox");
let resultsView;

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

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<blocklist lastupdate="1554976164809" xmlns="http://www.mozilla.org/2006/addons-blocklist">
<blocklist lastupdate="1555100575898" xmlns="http://www.mozilla.org/2006/addons-blocklist">
<emItems>
<emItem blockID="i334" id="{0F827075-B026-42F3-885D-98981EE7B1AE}">
<prefs/>
@ -2784,6 +2784,38 @@
<prefs/>
<versionRange minVersion="0" maxVersion="*" severity="3"/>
</emItem>
<emItem blockID="fc38e782-d55b-4fb7-8f9c-374aa18af09c" id="/^((\{61f433aa-45fd-42a9-9c90-c1d7820661d5\})|(\{86cd46b6-433a-439c-bff2-722846709f44\})|(\{98e126a4-4e70-4300-b893-3b2cca19bc9b\})|(\{8f42dc3a-1c46-4fc2-8c7f-dd76a63b1cf7\})|(\{a24d3582-2fc2-475c-8440-335736e17c6e\})|(\{cf0b5280-cd08-465d-ad7d-70308538f30a\})|(\{936f3c79-5dc9-4694-bca8-47932de3357a\})|(\{e48d5888-8736-4858-83ba-e816378ffef8\})|(\{5305f89c-ceec-4217-8bae-c9c376c7462b\})|(\{a2ac47e5-d225-4718-9b57-18a932f87610\})|(\{79f60f07-6aee-42cd-9105-c0a52f401316\}))$/">
<prefs/>
<versionRange minVersion="0" maxVersion="*" severity="3"/>
</emItem>
<emItem blockID="6fd3ab94-8e38-47f3-b129-3ca8396d0a22" id="{be6ab6a9-7004-4c5c-8df9-8d36122d8b14}">
<prefs/>
<versionRange minVersion="0" maxVersion="*" severity="3"/>
</emItem>
<emItem blockID="036e2e7d-5403-49be-92cf-b5187ceef7ec" id="/^((\{b37f383f-e60f-4eb1-ac0f-9147e0e8f2f7\})|(\{8ad567d2-3fe2-446b-bce9-a3acbe878dba\}))$/">
<prefs/>
<versionRange minVersion="0" maxVersion="*" severity="3"/>
</emItem>
<emItem blockID="03554696-58fe-4b90-89d1-72b72f88f82e" id="/^((\{8220ccaf-15a4-4f47-a670-a4119a4296a4\})|(\{9da72c11-44d7-423c-b19c-c75cd6188c3e\})|(\{99d0e4d0-c5ef-4567-b74c-80c5ed21ad99\}))$/">
<prefs/>
<versionRange minVersion="0" maxVersion="*" severity="3"/>
</emItem>
<emItem blockID="ea00841e-8dc2-4e11-9119-7a599e47d800" id="{608f71eb-5bd6-45d8-bc93-b9e812cf17b7}">
<prefs/>
<versionRange minVersion="0" maxVersion="*" severity="3"/>
</emItem>
<emItem blockID="53ef1aad-7bdb-4f4e-8d46-55d6ec2d78ab" id="H.264.Addon.Test@firefox.com">
<prefs/>
<versionRange minVersion="0" maxVersion="*" severity="3"/>
</emItem>
<emItem blockID="371796e4-387a-4dd0-9ddc-47ba1dd85be7" id="{132cb2fd-a6ae-45d2-84cf-b48d591f037d}">
<prefs/>
<versionRange minVersion="0" maxVersion="*" severity="3"/>
</emItem>
<emItem blockID="3a921aa8-d44a-4272-be63-0fd102577f59" id="{d8b03707-e39f-4b17-8e56-56354fb14af5}">
<prefs/>
<versionRange minVersion="0" maxVersion="*" severity="3"/>
</emItem>
</emItems>
<pluginItems>
<pluginItem blockID="p332">

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

@ -734,8 +734,11 @@ async function runUrlbarTest(useAwesomebar,
await waitExtra();
}
} else {
await UrlbarTestUtils.promiseAutocompleteResultPopup(win, URLBar.value,
SimpleTest.waitForFocus);
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window: win,
waitForFocus: SimpleTest.waitForFocus,
value: URLBar.value,
});
await waitExtra();
}

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

@ -10,8 +10,12 @@ ChromeUtils.defineModuleGetter(this, "UrlbarTestUtils",
const SUGGEST_URLBAR_PREF = "browser.urlbar.suggest.searches";
const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml";
function promiseAutocompleteResultPopup(inputText) {
return UrlbarTestUtils.promiseAutocompleteResultPopup(window, inputText, waitForFocus);
function promiseAutocompleteResultPopup(value) {
return UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
waitForFocus,
value,
});
}
async function addBookmark(bookmark) {
@ -131,7 +135,7 @@ add_task(async function test_webnavigation_urlbar_typed_closed_popup_transitions
await SimpleTest.promiseFocus(window);
await extension.awaitMessage("ready");
await UrlbarTestUtils.promiseAutocompleteResultPopup(window, "http://example.com/?q=typedClosed", waitForFocus);
await promiseAutocompleteResultPopup("http://example.com/?q=typedClosed");
await UrlbarTestUtils.promiseSearchComplete(window);
// Closing the popup forces a different code route that handles no results
// being displayed.

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

@ -0,0 +1,52 @@
# How to run experiments with ASRouter
This guide will tell you how to run an experiment with ASRouter messages.
Note that the actual experiment proccess and infrastructure is handled by
the experiments team (#ask-experimenter).
## Why run an experiment
* To measure the effect of a message on a Firefox metric (e.g. retention)
* To test a potentially risky message on a smaller group of users
* To compare the performance of multiple variants of messages in a controlled way
## Choose cohort IDs and request an experiment
First you should decide on a cohort ID (this can be any arbitrary unique string) for each
individual group you need to segment for your experiment.
For example, if I want to test two variants of an FXA Snippet, I might have two cohort IDs,
`FXA_SNIPPET_V1` and `FXA_SNIPPET_V2`.
You will then [request](https://experimenter.services.mozilla.com/) a new "pref-flip" study with the Firefox Experiments team.
The preferences you will submit will be based on the cohort IDs you chose.
For the FXA Snippet example, your preference name would be `browser.newtabpage.activity-stream.asrouter.providers.snippets` and values would be:
Control (default value)
```json
{"id":"snippets","enabled":true,"type":"remote","url":"https://snippets.cdn.mozilla.net/%STARTPAGE_VERSION%/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/release/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/","updateCycleInMs":14400000}
```
Variant 1:
```json
{"id":"snippets", "cohort": "FXA_SNIPPET_V1", "enabled":true,"type":"remote","url":"https://snippets.cdn.mozilla.net/%STARTPAGE_VERSION%/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/release/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/","updateCycleInMs":14400000}
```
Variant 2:
```json
{"id":"snippets", "cohort": "FXA_SNIPPET_V1", "enabled":true,"type":"remote","url":"https://snippets.cdn.mozilla.net/%STARTPAGE_VERSION%/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/release/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/","updateCycleInMs":14400000}
```
## Add targeting to your messages
You must now check for the cohort ID in the `targeting` expression of the messages you want to include in your experiments.
For the previous example, you wold include the following to target the first cohort:
```json
{
"targeting": "providerCohorts.snippets == \"FXA_SNIPPET_V1\""
}
```

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

@ -0,0 +1,128 @@
{
"title": "CFRFxABookmark",
"description": "A message shown in the bookmark panel when user adds or edits a bookmark",
"version": "1.0.0",
"type": "object",
"definitions": {
"plainText": {
"description": "Plain text (no HTML allowed)",
"type": "string"
},
"richText": {
"description": "Text with HTML subset allowed: i, b, u, strong, em, br",
"type": "string"
},
"link_url": {
"description": "Target for links or buttons",
"type": "string",
"format": "uri"
}
},
"properties": {
"title": {
"description": "Shown at the top of the message in the largest font size.",
"oneOf": [
{
"allOf": [
{"$ref": "#/definitions/richText"},
{"description": "Message to be shown"}
]
},
{
"type": "object",
"properties": {
"string_id": {
"type": "string",
"description": "Fluent id of localized string"
}
},
"required": ["string_id"]
}
]
},
"text": {
"description": "Longest part of the message, below the title, provides explanation.",
"oneOf": [
{
"allOf": [
{"$ref": "#/definitions/richText"},
{"description": "Message to be shown"}
]
},
{
"type": "object",
"properties": {
"string_id": {
"type": "string",
"description": "Fluent id of localized string"
}
},
"required": ["string_id"]
}
]
},
"link": {
"description": "Link shown at the bottom of the message, call to action",
"properties": {
"text": {
"description": "Message shown as part of anchor tag",
"oneOf": [
{
"allOf": [
{"$ref": "#/definitions/richText"},
{"description": "Message to be shown"}
]
},
{
"type": "object",
"properties": {
"string_id": {
"type": "string",
"description": "Fluent id of localized string"
}
},
"required": ["string_id"]
}
]
},
"url": {
"description": "Value for href attribute of the anchor",
"allOf": [
{"$ref": "#/definitions/link_url"},
{"description": "Link that opens in a new tab"}
]
}
},
"required": ["text", "url"]
},
"info_icon": {
"type": "object",
"description": "The small icon displayed in the top right corner of the panel. Not configurable, only the tooltip text." ,
"properties": {
"tooltiptext": {
"oneOf": [
{
"allOf": [
{"$ref": "#/definitions/plainText"},
{"description": "Message to be shown"}
]
},
{
"type": "object",
"properties": {
"string_id": {
"type": "string",
"description": "Fluent id of localized string"
}
},
"required": ["string_id"]
}
]
}
},
"required": ["tooltiptext"]
}
},
"additionalProperties": false,
"required": ["title", "text", "link", "info_icon"]
}

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

@ -0,0 +1,34 @@
import React from "react";
import {RichText} from "../../components/RichText/RichText";
import {safeURI} from "../../template-utils";
import {SnippetBase} from "../../components/SnippetBase/SnippetBase";
const DEFAULT_ICON_PATH = "chrome://branding/content/icon64.png";
export class SimpleBelowSearchSnippet extends React.PureComponent {
renderText() {
const {props} = this;
return (<RichText text={props.content.text}
customElements={this.props.customElements}
localization_id="text"
links={props.content.links}
sendClick={props.sendClick} />);
}
render() {
const {props} = this;
let className = "SimpleBelowSearchSnippet";
if (props.className) {
className += ` ${props.className}`;
}
return (<SnippetBase {...props} className={className} textStyle={this.props.textStyle}>
<img src={safeURI(props.content.icon) || DEFAULT_ICON_PATH} className="icon" />
<div>
<p className="body">{this.renderText()}</p>
{this.props.extraContent}
</div>
</SnippetBase>);
}
}

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

@ -0,0 +1,59 @@
{
"title": "SimpleBelowSearchSnippet",
"description": "A simple template with just an icon and rich text. It gets inserted below the Activity Stream search box.",
"version": "1.1.0",
"type": "object",
"definitions": {
"richText": {
"description": "Text with HTML subset allowed: i, b, u, strong, em, br",
"type": "string"
},
"link_url": {
"description": "Target for links or buttons",
"type": "string",
"format": "uri"
}
},
"properties": {
"text": {
"allOf": [
{"$ref": "#/definitions/richText"},
{"description": "Main body text of snippet. HTML subset allowed: i, b, u, strong, em, br"}
]
},
"icon": {
"type": "string",
"description": "Snippet icon. 64x64px. SVG or PNG preferred."
},
"block_button_text": {
"type": "string",
"description": "Tooltip text used for dismiss button.",
"default": "Remove this"
},
"do_not_autoblock": {
"type": "boolean",
"description": "Used to prevent blocking the snippet after the CTA link has been clicked"
},
"links": {
"additionalProperties": {
"url": {
"allOf": [
{"$ref": "#/definitions/link_url"},
{"description": "The url where the link points to."}
]
},
"metric": {
"type": "string",
"description": "Custom event name sent with telemetry event."
},
"args": {
"type": "string",
"description": "Additional parameters for link action, example which specific menu the button should open"
}
}
}
},
"additionalProperties": false,
"required": ["text"],
"dependencies": {}
}

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

@ -2,6 +2,7 @@ import {EOYSnippet} from "./EOYSnippet/EOYSnippet";
import {FXASignupSnippet} from "./FXASignupSnippet/FXASignupSnippet";
import {NewsletterSnippet} from "./NewsletterSnippet/NewsletterSnippet";
import {SendToDeviceSnippet} from "./SendToDeviceSnippet/SendToDeviceSnippet";
import {SimpleBelowSearchSnippet} from "./SimpleBelowSearchSnippet/SimpleBelowSearchSnippet";
import {SimpleSnippet} from "./SimpleSnippet/SimpleSnippet";
// Key names matching schema name of templates
@ -11,4 +12,5 @@ export const SnippetsTemplates = {
fxa_signup_snippet: FXASignupSnippet,
send_to_device_snippet: SendToDeviceSnippet,
eoy_snippet: EOYSnippet,
simple_below_search_snippet: SimpleBelowSearchSnippet,
};

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

@ -9760,12 +9760,57 @@ const SendToDeviceSnippet = props => {
processFormData: processFormData
}));
};
// CONCATENATED MODULE: ./content-src/asrouter/templates/SimpleBelowSearchSnippet/SimpleBelowSearchSnippet.jsx
function SimpleBelowSearchSnippet_extends() { SimpleBelowSearchSnippet_extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return SimpleBelowSearchSnippet_extends.apply(this, arguments); }
const SimpleBelowSearchSnippet_DEFAULT_ICON_PATH = "chrome://branding/content/icon64.png";
class SimpleBelowSearchSnippet_SimpleBelowSearchSnippet extends external_React_default.a.PureComponent {
renderText() {
const {
props
} = this;
return external_React_default.a.createElement(RichText["RichText"], {
text: props.content.text,
customElements: this.props.customElements,
localization_id: "text",
links: props.content.links,
sendClick: props.sendClick
});
}
render() {
const {
props
} = this;
let className = "SimpleBelowSearchSnippet";
if (props.className) {
className += ` ${props.className}`;
}
return external_React_default.a.createElement(SnippetBase_SnippetBase, SimpleBelowSearchSnippet_extends({}, props, {
className: className,
textStyle: this.props.textStyle
}), external_React_default.a.createElement("img", {
src: Object(template_utils["safeURI"])(props.content.icon) || SimpleBelowSearchSnippet_DEFAULT_ICON_PATH,
className: "icon"
}), external_React_default.a.createElement("div", null, external_React_default.a.createElement("p", {
className: "body"
}, this.renderText()), this.props.extraContent));
}
}
// CONCATENATED MODULE: ./content-src/asrouter/templates/template-manifest.jsx
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SnippetsTemplates", function() { return SnippetsTemplates; });
// Key names matching schema name of templates
const SnippetsTemplates = {
@ -9773,7 +9818,8 @@ const SnippetsTemplates = {
newsletter_snippet: NewsletterSnippet,
fxa_signup_snippet: FXASignupSnippet,
send_to_device_snippet: SendToDeviceSnippet,
eoy_snippet: EOYSnippet
eoy_snippet: EOYSnippet,
simple_below_search_snippet: SimpleBelowSearchSnippet_SimpleBelowSearchSnippet
};
/***/ }),

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

@ -43,8 +43,10 @@ const PINNED_TABS_TARGET_SITES = [
"outlook.live.com", "facebook.com", "www.facebook.com", "twitter.com", "www.twitter.com",
"reddit.com", "www.reddit.com", "github.com", "www.github.com", "youtube.com", "www.youtube.com",
"feedly.com", "www.feedly.com", "drive.google.com", "amazon.com", "www.amazon.com",
"messages.android.com",
"messages.android.com", "amazon.ca", "www.amazon.ca", "amazon.com.au", "www.amazon.com.au",
"amazon.co.uk", "www.amazon.co.uk", "amazon.fr", "www.amazon.fr", "amazon.de", "www.amazon.de",
];
const PINNED_TABS_TARGET_LOCALES = ["en-US", "en-CA", "en-AU", "en-GB", "en-ZA", "en-NZ", "fr", "de"];
const CFR_MESSAGES = [
{
@ -350,7 +352,7 @@ const CFR_MESSAGES = [
}],
},
},
targeting: `locale == "en-US" && !hasPinnedTabs && recentVisits[.timestamp > (currentDate|date - 3600 * 1000 * 1)]|length >= 3`,
targeting: `locale in ${JSON.stringify(PINNED_TABS_TARGET_LOCALES)} && !hasPinnedTabs && recentVisits[.timestamp > (currentDate|date - 3600 * 1000 * 1)]|length >= 3`,
frequency: {lifetime: 3},
trigger: {id: "frequentVisits", params: PINNED_TABS_TARGET_SITES},
},

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

@ -247,6 +247,16 @@ const MESSAGES = () => ([
"section_title_url": "https://www.mozilla.org/about",
},
},
{
"id": "SIMPLE_BELOW_SEARCH_TEST_1",
"template": "simple_below_search_snippet",
"content": {
"icon": TEST_ICON,
"text": "Securely store passwords, bookmarks, and more with a Firefox Account. <syncLink>Sign up</syncLink>",
"links": {"syncLink": {"url": "https://www.mozilla.org/en-US/firefox/accounts"}},
"block_button_text": "Block",
},
},
]);
const SnippetsTestMessageProvider = {

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

@ -32,15 +32,17 @@ describe("CFRMessageProvider", () => {
}
}
});
it("should restrict all messages to `en` locale for now", () => {
for (const message of messages) {
if (message.id !== "PIN_TAB") {
assert.include(message.targeting, `localeLanguageCode == "en"`);
} else {
assert.include(message.targeting, `locale == "en-US"`);
}
it("should restrict all messages to `en` locale for now (PIN TAB is handled separately)", () => {
for (const message of messages.filter(m => m.id !== "PIN_TAB")) {
assert.include(message.targeting, `localeLanguageCode == "en"`);
}
});
it("should restrict locale for PIN_TAB message", () => {
const pinTabMessage = messages.find(m => m.id === "PIN_TAB");
// 6 en-* locales, fr and de
assert.lengthOf(pinTabMessage.targeting.match(/en-|fr|de/g), 8);
});
it("should contain `www.` version of the hosts", () => {
const pinTabMessage = messages.find(m => m.id === "PIN_TAB");

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

@ -1,4 +1,5 @@
import EOYSnippetSchema from "../../../content-src/asrouter/templates/EOYSnippet/EOYSnippet.schema.json";
import SimpleBelowSearchSnippetSchema from "../../../content-src/asrouter/templates/SimpleBelowSearchSnippet/SimpleBelowSearchSnippet.schema.json";
import SimpleSnippetSchema from "../../../content-src/asrouter/templates/SimpleSnippet/SimpleSnippet.schema.json";
import {SnippetsTestMessageProvider} from "../../../lib/SnippetsTestMessageProvider.jsm";
import SubmitFormSnippetSchema from "../../../content-src/asrouter/templates/SubmitFormSnippet/SubmitFormSnippet.schema.json";
@ -9,6 +10,7 @@ const schemas = {
"fxa_signup_snippet": SubmitFormSnippetSchema,
"send_to_device_snippet": SubmitFormSnippetSchema,
"eoy_snippet": EOYSnippetSchema,
"simple_below_search_snippet": SimpleBelowSearchSnippetSchema,
};
describe("SnippetsTestMessageProvider", () => {

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

@ -0,0 +1,34 @@
import schema from "content-src/asrouter/schemas/panel/cfr-fxa-bookmark.schema.json";
const DEFAULT_CONTENT = {
"title": "Sync your bookmarks everywhere",
"text": "Great find! Now don't be left without this bookmark.",
"link": {
"text": "Sync bookmarks now",
"url": "https://mozilla.com",
},
"info_icon": {
"tooltiptext": "Learn more",
},
};
const L10N_CONTENT = {
"title": {string_id: "cfr-bookmark-title"},
"text": {string_id: "cfr-bookmark-body"},
"link": {
"text": {string_id: "cfr-bookmark-link-text"},
"url": "https://mozilla.com",
},
"info_icon": {
"tooltiptext": {string_id: "cfr-bookmark-tooltip-text"},
},
};
describe("CFR FxA Message Schema", () => {
it("should validate DEFAULT_CONTENT", () => {
assert.jsonSchema(DEFAULT_CONTENT, schema);
});
it("should validate L10N_CONTENT", () => {
assert.jsonSchema(L10N_CONTENT, schema);
});
});

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

@ -0,0 +1,47 @@
import {mount} from "enzyme";
import React from "react";
import schema from "content-src/asrouter/templates/SimpleBelowSearchSnippet/SimpleBelowSearchSnippet.schema.json";
import {SimpleBelowSearchSnippet} from "content-src/asrouter/templates/SimpleBelowSearchSnippet/SimpleBelowSearchSnippet.jsx";
const DEFAULT_CONTENT = {text: "foo"};
describe("SimpleBelowSearchSnippet", () => {
let sandbox;
let sendUserActionTelemetryStub;
/**
* mountAndCheckProps - Mounts a SimpleBelowSearchSnippet with DEFAULT_CONTENT extended with any props
* passed in the content param and validates props against the schema.
* @param {obj} content Object containing custom message content (e.g. {text, icon})
* @returns enzyme wrapper for SimpleSnippet
*/
function mountAndCheckProps(content = {}, provider = "test-provider") {
const props = {
content: {...DEFAULT_CONTENT, ...content},
provider,
sendUserActionTelemetry: sendUserActionTelemetryStub,
onAction: sandbox.stub(),
};
assert.jsonSchema(props.content, schema);
return mount(<SimpleBelowSearchSnippet {...props} />);
}
beforeEach(() => {
sandbox = sinon.createSandbox();
sendUserActionTelemetryStub = sandbox.stub();
});
afterEach(() => {
sandbox.restore();
});
it("should render .text", () => {
const wrapper = mountAndCheckProps({text: "bar"});
assert.equal(wrapper.find(".body").text(), "bar");
});
it("should render .icon", () => {
const wrapper = mountAndCheckProps({icon: "data:image/gif;base64,R0lGODl"});
assert.equal(wrapper.find(".icon").prop("src"), "data:image/gif;base64,R0lGODl");
});
});

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

@ -147,7 +147,11 @@ async function openPopupAndGetEngineButton(isSearch, popup, oneOffInstance, base
EventUtils.synthesizeMouseAtCenter(searchIcon, {});
await promise;
} else {
await UrlbarTestUtils.promiseAutocompleteResultPopup(window, "a", waitForFocus);
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
waitForFocus,
value: "a",
});
}
const contextMenu = oneOffInstance.contextMenuPopup;

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

@ -69,7 +69,11 @@ add_task(async function test_unrestored_tabs_listed() {
});
info("Searching open pages.");
await UrlbarTestUtils.promiseAutocompleteResultPopup(window, RESTRICT_TOKEN_OPENPAGE, waitForFocus);
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
waitForFocus,
value: RESTRICT_TOKEN_OPENPAGE,
});
const total = UrlbarTestUtils.getResultCount(window);
info(`Found ${total} matches`);
const quantumbar = UrlbarPrefs.get("quantumbar");

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

@ -146,9 +146,9 @@ class UrlbarController {
TelemetryStopwatch.finish(TELEMETRY_6_FIRST_RESULTS, queryContext);
}
if (queryContext.lastResultCount == 0) {
if (queryContext.results.length && queryContext.results[0].autofill) {
this.input.setValueFromResult(queryContext.results[0]);
if (queryContext.lastResultCount == 0 && queryContext.results.length) {
if (queryContext.results[0].autofill) {
this.input.autofillFirstResult(queryContext.results[0]);
}
// The first time we receive results try to connect to the heuristic
// result.

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

@ -97,6 +97,9 @@ class UrlbarInput {
this._suppressStartQuery = false;
this._untrimmedValue = "";
// This exists only for tests.
this._enableAutofillPlaceholder = true;
// Forward textbox methods and properties.
const METHODS = ["addEventListener", "removeEventListener",
"setAttribute", "hasAttribute", "removeAttribute", "getAttribute",
@ -505,7 +508,8 @@ class UrlbarInput {
// Also update userTypedValue. See bug 287996.
this.window.gBrowser.userTypedValue = this.value;
// The value setter clobbers the actiontype attribute, so update this after that.
// The value setter clobbers the actiontype attribute, so update this after
// that.
if (result) {
switch (result.type) {
case UrlbarUtils.RESULT_TYPE.TAB_SWITCH:
@ -520,6 +524,38 @@ class UrlbarInput {
return !!canonizedUrl;
}
/**
* Called by the controller when the first result of a new search is received.
* If it's an autofill result, then it may need to be autofilled, subject to a
* few restrictions.
*
* @param {UrlbarResult} result
* The first result.
*/
autofillFirstResult(result) {
if (!result.autofill) {
return;
}
let isPlaceholderSelected =
this.selectionEnd == this._autofillPlaceholder.length &&
this.selectionStart == this._lastSearchString.length &&
this._autofillPlaceholder.toLocaleLowerCase()
.startsWith(this._lastSearchString.toLocaleLowerCase());
// Don't autofill if there's already a selection (with one caveat described
// next) or the cursor isn't at the end of the input. But if there is a
// selection and it's the autofill placeholder value, then do autofill.
if (!isPlaceholderSelected &&
(this.selectionStart != this.selectionEnd ||
this.selectionEnd != this._lastSearchString.length)) {
return;
}
let { value, selectionStart, selectionEnd } = result.autofill;
this._autofillValue(value, selectionStart, selectionEnd);
}
/**
* Starts a query based on the current input value.
*
@ -747,7 +783,8 @@ class UrlbarInput {
.startsWith(value.toLocaleLowerCase())) {
this._autofillPlaceholder = "";
} else if (this._autofillPlaceholder &&
this.selectionEnd == this.value.length) {
this.selectionEnd == this.value.length &&
this._enableAutofillPlaceholder) {
let autofillValue =
value + this._autofillPlaceholder.substring(value.length);
this._autofillValue(autofillValue, value.length, autofillValue.length);
@ -1169,7 +1206,7 @@ class UrlbarInput {
* We use the observer service, so that we don't need to load extra facilities
* if they aren't being used, e.g. WebNavigation.
*
* @param {UrlbarResult} [result]
* @param {UrlbarResult} result
* The result that was selected, if any.
*/
_notifyStartNavigation(result) {

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

@ -342,11 +342,12 @@ class UrlbarView {
this.panel.removeAttribute("actionoverride");
// Make the panel span the width of the window.
let px = number => number.toFixed(2) + "px";
let documentRect =
this._getBoundsWithoutFlushing(this.document.documentElement);
let width = documentRect.right - documentRect.left;
this.panel.setAttribute("width", width);
this._mainContainer.style.maxWidth = width + "px";
this._mainContainer.style.maxWidth = px(width);
// Keep the popup items' site icons aligned with the input's identity
// icon if it's not too far from the edge of the window. We define
@ -374,22 +375,22 @@ class UrlbarView {
documentRect.right - identityRect.right :
identityRect.left;
this.panel.style.setProperty("--item-padding-start", Math.round(start) + "px");
this.panel.style.setProperty("--item-padding-end", Math.round(endOffset) + "px");
this.panel.style.setProperty("--item-padding-start", px(start));
this.panel.style.setProperty("--item-padding-end", px(endOffset));
contentWidth -= start + endOffset;
} else {
this.panel.style.removeProperty("--item-padding-start");
this.panel.style.removeProperty("--item-padding-end");
}
this.panel.style.setProperty("--item-content-width", Math.round(contentWidth) + "px");
this.panel.style.setProperty("--item-content-width", px(contentWidth));
// Align the panel with the input's parent toolbar.
let toolbarRect =
this._getBoundsWithoutFlushing(this.input.textbox.closest("toolbar"));
this.panel.style.marginInlineStart = Math.round(this.window.RTL_UI ?
this.panel.style.marginInlineStart = px(this.window.RTL_UI ?
inputRect.right - documentRect.right :
documentRect.left - inputRect.left) + "px";
this.panel.style.marginTop = Math.round(inputRect.top - toolbarRect.top) + "px";
documentRect.left - inputRect.left);
this.panel.style.marginTop = px(inputRect.top - toolbarRect.top);
this.panel.openPopup(this.input.textbox, "after_start");
}

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

@ -36,24 +36,38 @@ var UrlbarTestUtils = {
/**
* Starts a search for a given string and waits for the search to be complete.
* @param {object} win The window containing the urlbar
* @param {string} inputText the search string
* @param {function} waitForFocus The Simpletest function
* @param {boolean} fireInputEvent whether an input event should be used when
* starting the query (necessary to set userTypedValued)
* @param {object} options.window The window containing the urlbar
* @param {string} options.value the search string
* @param {function} options.waitForFocus The SimpleTest function
* @param {boolean} [options.fireInputEvent] whether an input event should be
* used when starting the query (simulates the user's typing, sets
* userTypedValued, etc.)
* @param {number} [options.selectionStart] The input's selectionStart
* @param {number} [options.selectionEnd] The input's selectionEnd
*/
async promiseAutocompleteResultPopup(win, inputText, waitForFocus, fireInputEvent = false) {
let urlbar = getUrlbarAbstraction(win);
async promiseAutocompleteResultPopup({
window,
value,
waitForFocus,
fireInputEvent = false,
selectionStart = -1,
selectionEnd = -1,
} = {}) {
let urlbar = getUrlbarAbstraction(window);
let restoreAnimationsFn = urlbar.disableAnimations();
await new Promise(resolve => waitForFocus(resolve, win));
await new Promise(resolve => waitForFocus(resolve, window));
let lastSearchString = urlbar.lastSearchString;
urlbar.focus();
urlbar.value = inputText;
urlbar.value = value;
if (selectionStart >= 0 && selectionEnd >= 0) {
urlbar.selectionEnd = selectionEnd;
urlbar.selectionStart = selectionStart;
}
if (fireInputEvent) {
// This is necessary to get the urlbar to set gBrowser.userTypedValue.
urlbar.fireInputEvent();
} else {
win.gURLBar.setAttribute("pageproxystate", "invalid");
window.gURLBar.setAttribute("pageproxystate", "invalid");
}
// An input event will start a new search, with a couple of exceptions, so
// be careful not to call startSearch if we fired an input event since that
@ -63,11 +77,11 @@ var UrlbarTestUtils = {
// directly then. The second exception is when searching with the legacy
// urlbar and an empty string.
if (!fireInputEvent ||
inputText == lastSearchString ||
(!urlbar.quantumbar && !inputText)) {
urlbar.startSearch(inputText);
value == lastSearchString ||
(!urlbar.quantumbar && !value)) {
urlbar.startSearch(value, selectionStart, selectionEnd);
}
return this.promiseSearchComplete(win, restoreAnimationsFn);
return this.promiseSearchComplete(window, restoreAnimationsFn);
},
/**
@ -339,9 +353,13 @@ class UrlbarAbstraction {
return this.oneOffSearchButtons.style.display != "none";
}
startSearch(text) {
startSearch(text, selectionStart = -1, selectionEnd = -1) {
if (this.quantumbar) {
this.urlbar.value = text;
if (selectionStart >= 0 && selectionEnd >= 0) {
this.urlbar.selectionEnd = selectionEnd;
this.urlbar.selectionStart = selectionStart;
}
this.urlbar.setAttribute("pageproxystate", "invalid");
this.urlbar.startQuery();
} else {

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

@ -26,10 +26,12 @@ skip-if = os != "mac" # Mac only feature
[browser_autoFill_backspaced.js]
[browser_autoFill_canonize.js]
[browser_autoFill_caretNotAtEnd.js]
[browser_autoFill_firstResult.js]
[browser_autoFill_placeholder.js]
[browser_autoFill_preserve.js]
[browser_autoFill_trimURLs.js]
[browser_autoFill_typed.js]
[browser_autoFill_undo.js]
[browser_canonizeURL.js]
support-files =
searchSuggestionEngine.xml

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

@ -0,0 +1,208 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// This test makes sure that autofilling the first result of a new search works
// correctly: autofill happens when it should and doesn't when it shouldn't.
"use strict";
add_task(async function init() {
await PlacesUtils.bookmarks.eraseEverything();
await PlacesUtils.history.clear();
await PlacesTestUtils.addVisits([
"http://example.com/",
]);
// Disable placeholder completion. The point of this test is to make sure the
// first result is autofilled (or not) correctly. Autofilling the placeholder
// before the search starts interferes with that.
gURLBar._enableAutofillPlaceholder = false;
registerCleanupFunction(async () => {
gURLBar._enableAutofillPlaceholder = true;
});
});
// The first result should be autofilled when all conditions are met. This also
// does a sanity check to make sure that placeholder autofill is correctly
// disabled, which is helpful for all tasks here and is why this one is first.
add_task(async function successfulAutofill() {
// Do a simple search that should autofill. This will also set up the
// autofill placeholder string, which next we make sure is *not* autofilled.
await doInitialAutofillSearch();
// As a sanity check, do another search to make sure the placeholder is *not*
// autofilled. Make sure it's not autofilled by checking the input value and
// selection *before* the search completes. If placeholder autofill was not
// correctly disabled, then these assertions will fail.
gURLBar.value = "exa";
UrlbarTestUtils.fireInputEvent(window);
// before the search completes: no autofill
Assert.equal(gURLBar.value, "exa");
Assert.equal(gURLBar.selectionStart, "exa".length);
Assert.equal(gURLBar.selectionEnd, "exa".length);
await UrlbarTestUtils.promiseSearchComplete(window);
// after the search completes: successful autofill
let details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
Assert.ok(details.autofill);
Assert.equal(gURLBar.value, "example.com/");
Assert.equal(gURLBar.selectionStart, "exa".length);
Assert.equal(gURLBar.selectionEnd, "example.com/".length);
});
// The first result should not be autofilled when it's not an autofill result.
add_task(async function firstResultNotAutofill() {
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
waitForFocus,
value: "foo",
fireInputEvent: true,
});
let details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
Assert.ok(!details.autofill);
Assert.equal(gURLBar.value, "foo");
Assert.equal(gURLBar.selectionStart, "foo".length);
Assert.equal(gURLBar.selectionEnd, "foo".length);
});
// The first result should *not* be autofilled when the placeholder is not
// selected, the selection is empty, and the caret is *not* at the end of the
// search string.
add_task(async function caretNotAtEndOfSearchString() {
// To set up the placeholder, do an initial search that triggers autofill.
await doInitialAutofillSearch();
// Now do another search but set the caret to somewhere else besides the end
// of the new search string.
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
waitForFocus,
value: "exam",
selectionStart: "exa".length,
selectionEnd: "exa".length,
});
// The first result should be an autofill result, but it should not have been
// autofilled.
let details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
Assert.ok(details.autofill);
Assert.equal(gURLBar.value, "exam");
Assert.equal(gURLBar.selectionStart, "exa".length);
Assert.equal(gURLBar.selectionEnd, "exa".length);
await cleanUp();
});
// The first result should *not* be autofilled when the placeholder is not
// selected, the selection is *not* empty, and the caret is at the end of the
// search string.
add_task(async function selectionNotEmpty() {
// To set up the placeholder, do an initial search that triggers autofill.
await doInitialAutofillSearch();
// Now do another search. Set the selection end at the end of the search
// string, but make the selection non-empty.
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
waitForFocus,
value: "exam",
selectionStart: "exa".length,
selectionEnd: "exam".length,
});
// The first result should be an autofill result, but it should not have been
// autofilled.
let details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
Assert.ok(details.autofill);
Assert.equal(gURLBar.value, "exam");
Assert.equal(gURLBar.selectionStart, "exa".length);
Assert.equal(gURLBar.selectionEnd, "exam".length);
await cleanUp();
});
// The first result should be autofilled when the placeholder is not selected,
// the selection is empty, and the caret is at the end of the search string.
add_task(async function successfulAutofillAfterSettingPlaceholder() {
// To set up the placeholder, do an initial search that triggers autofill.
await doInitialAutofillSearch();
// Now do another search.
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
waitForFocus,
value: "exam",
selectionStart: "exam".length,
selectionEnd: "exam".length,
});
// It should be autofilled.
let details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
Assert.ok(details.autofill);
Assert.equal(gURLBar.value, "example.com/");
Assert.equal(gURLBar.selectionStart, "exam".length);
Assert.equal(gURLBar.selectionEnd, "example.com/".length);
await cleanUp();
});
// The first result should be autofilled when the placeholder *is* selected --
// more precisely, when the portion of the placeholder after the new search
// string is selected.
add_task(async function successfulAutofillPlaceholderSelected() {
// To set up the placeholder, do an initial search that triggers autofill.
await doInitialAutofillSearch();
// Now do another search and select the portion of the placeholder after the
// new search string.
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
waitForFocus,
value: "exam",
selectionStart: "exam".length,
selectionEnd: "example.com/".length,
});
// It should be autofilled.
let details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
Assert.ok(details.autofill);
Assert.equal(gURLBar.value, "example.com/");
Assert.equal(gURLBar.selectionStart, "exam".length);
Assert.equal(gURLBar.selectionEnd, "example.com/".length);
await cleanUp();
});
async function doInitialAutofillSearch() {
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
waitForFocus,
value: "ex",
fireInputEvent: true,
});
let details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
Assert.ok(details.autofill);
Assert.equal(gURLBar.value, "example.com/");
Assert.equal(gURLBar.selectionStart, "ex".length);
Assert.equal(gURLBar.selectionEnd, "example.com/".length);
}
async function cleanUp() {
// In some cases above, a test task searches for "exam" at the end, and then
// the next task searches for "ex". Autofill results will not be allowed in
// the next task in that case since the old search string starts with the new
// search string. To prevent one task from interfering with the next, do a
// search that changes the search string. Also close the popup while we're
// here, although that's not really necessary.
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
waitForFocus,
value: "reset last search string",
});
await UrlbarTestUtils.promisePopupClose(window, () => {
EventUtils.synthesizeKey("KEY_Escape");
});
}

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

@ -155,10 +155,15 @@ async function typeAndCheck(values) {
continue;
}
await UrlbarTestUtils.promiseSearchComplete(window);
let restIsSpaces = !expectedInputValue.substring(i + 1).trim();
if (restIsSpaces && !UrlbarPrefs.get("quantumbar")) {
// See below. In addition to that, in awesomebar, after typing the final
// character, autofill incorrectly doesn't include the trailing space.
expectedInputValue = expectedInputValue.trim();
}
Assert.equal(gURLBar.value, expectedInputValue);
Assert.equal(gURLBar.selectionStart, i + 1);
Assert.equal(gURLBar.selectionEnd, expectedInputValue.length);
let restIsSpaces = !expectedInputValue.substring(i + 1).trim();
if (restIsSpaces) {
// Autofilled @ aliases have a trailing space. We should check that the
// space is autofilled when each preceding character is typed, but once

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

@ -0,0 +1,53 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// This test checks the behavior of text undo (Ctrl-Z, cmd_undo) in regard to
// autofill.
"use strict";
add_task(async function test() {
await PlacesUtils.bookmarks.eraseEverything();
await PlacesUtils.history.clear();
await PlacesTestUtils.addVisits([
"http://example.com/",
]);
// Search for "ex". It should autofill to example.com/.
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
waitForFocus,
value: "ex",
fireInputEvent: true,
});
let details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
Assert.ok(details.autofill);
Assert.equal(gURLBar.value, "example.com/");
Assert.equal(gURLBar.selectionStart, "ex".length);
Assert.equal(gURLBar.selectionEnd, "example.com/".length);
// Type an x.
EventUtils.synthesizeKey("x");
// Nothing should have been autofilled.
await UrlbarTestUtils.promiseSearchComplete(window);
details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
Assert.ok(!details.autofill);
Assert.equal(gURLBar.value, "exx");
Assert.equal(gURLBar.selectionStart, "exx".length);
Assert.equal(gURLBar.selectionEnd, "exx".length);
// Undo the typed x.
goDoCommand("cmd_undo");
// The text should be restored to ex[ample.com/] (with the part in brackets
// autofilled and selected).
await UrlbarTestUtils.promiseSearchComplete(window);
details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
Assert.ok(details.autofill);
Assert.equal(gURLBar.value, "example.com/");
Assert.equal(gURLBar.selectionStart, "ex".length);
Assert.equal(gURLBar.selectionEnd, "example.com/".length);
await PlacesUtils.history.clear();
});

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

@ -59,7 +59,7 @@ add_task(async function() {
});
await PlacesTestUtils.addVisits(visits);
await promiseAutocompleteResultPopup("example.com/autocomplete");
await promiseAutocompleteResultPopup("example.com/autocomplete", window, true);
let resultCount = await UrlbarTestUtils.getResultCount(window);

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

@ -31,7 +31,11 @@ add_task(async function test_switchToTab_closes() {
return event.originalTarget == baseTab;
});
await UrlbarTestUtils.promiseAutocompleteResultPopup(window, "dummy", waitForFocus);
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
waitForFocus,
value: "dummy",
});
// The first result is the heuristic, the second will be the switch to tab.
let element = await UrlbarTestUtils.waitForAutocompleteResultAt(window, 1);

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

@ -22,7 +22,12 @@ add_task(async function test_switchToTab_url() {
let tabSelectPromise = BrowserTestUtils.waitForEvent(gBrowser.tabContainer,
"TabSelect", false, event => event.target == baseTab);
await UrlbarTestUtils.promiseAutocompleteResultPopup(window, TEST_URL, waitForFocus, true);
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
waitForFocus,
value: TEST_URL,
fireInputEvent: true,
});
// The first result is the heuristic, the second will be the switch to tab.
await UrlbarTestUtils.waitForAutocompleteResultAt(window, 1);

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

@ -55,11 +55,15 @@ function promiseSearchComplete(win = window, dontAnimate = false) {
return UrlbarTestUtils.promiseSearchComplete(win, dontAnimate);
}
function promiseAutocompleteResultPopup(inputText,
function promiseAutocompleteResultPopup(value,
win = window,
fireInputEvent = false) {
return UrlbarTestUtils.promiseAutocompleteResultPopup(win, inputText,
waitForFocus, fireInputEvent);
return UrlbarTestUtils.promiseAutocompleteResultPopup({
window: win,
waitForFocus,
value,
fireInputEvent,
});
}
async function waitForAutocompleteResultAt(index) {

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

@ -51,6 +51,8 @@ skip-if = os != "mac" # Mac only feature
[../browser/browser_autoFill_canonize.js]
[../browser/browser_autoFill_preserve.js]
[../browser/browser_autoFill_trimURLs.js]
[../browser/browser_autoFill_typed.js]
[../browser/browser_autoFill_undo.js]
[../browser/browser_autocomplete_tag_star_visibility.js]
[../browser/browser_canonizeURL.js]
[../browser/browser_dragdropURL.js]

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

@ -23,8 +23,13 @@ XPCOMUtils.defineLazyModuleGetters(this, {
URLBAR_SELECTED_RESULT_METHODS: "resource:///modules/BrowserUsageTelemetry.jsm",
});
function searchInAwesomebar(inputText, win = window) {
return UrlbarTestUtils.promiseAutocompleteResultPopup(win, inputText, waitForFocus, true);
function searchInAwesomebar(value, win = window) {
return UrlbarTestUtils.promiseAutocompleteResultPopup({
window: win,
waitForFocus,
value,
fireInputEvent: true,
});
}
/**

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

@ -120,8 +120,12 @@ add_task(async function test_extension() {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
await UrlbarTestUtils.promiseAutocompleteResultPopup(window, "omniboxtest ",
waitForFocus, true);
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
waitForFocus,
value: "omniboxtest ",
fireInputEvent: true,
});
EventUtils.synthesizeKey("KEY_Enter");
assertSearchTelemetryEmpty(histograms.search_hist);

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

@ -20,8 +20,13 @@ XPCOMUtils.defineLazyModuleGetters(this, {
URLBAR_SELECTED_RESULT_METHODS: "resource:///modules/BrowserUsageTelemetry.jsm",
});
function searchInAwesomebar(inputText, win = window) {
return UrlbarTestUtils.promiseAutocompleteResultPopup(win, inputText, waitForFocus, true);
function searchInAwesomebar(value, win = window) {
return UrlbarTestUtils.promiseAutocompleteResultPopup({
window: win,
waitForFocus,
value,
fireInputEvent: true,
});
}
function assertSearchTelemetryEmpty(search_hist) {

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

@ -144,7 +144,12 @@ add_task(async function test_remotetab() {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
await UrlbarTestUtils.promiseAutocompleteResultPopup(window, "example", waitForFocus, true);
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
waitForFocus,
value: "example",
fireInputEvent: true,
});
EventUtils.synthesizeKey("KEY_ArrowDown");
EventUtils.synthesizeKey("KEY_Enter");
await p;

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

@ -438,23 +438,6 @@ already_AddRefed<BasePrincipal> BasePrincipal::CreateCodebasePrincipal(
return BasePrincipal::CreateCodebasePrincipal(uri, attrs);
}
already_AddRefed<BasePrincipal>
BasePrincipal::CloneStrippingUserContextIdAndFirstPartyDomain() {
OriginAttributes attrs = OriginAttributesRef();
attrs.StripAttributes(OriginAttributes::STRIP_USER_CONTEXT_ID |
OriginAttributes::STRIP_FIRST_PARTY_DOMAIN);
nsAutoCString originNoSuffix;
nsresult rv = GetOriginNoSuffix(originNoSuffix);
NS_ENSURE_SUCCESS(rv, nullptr);
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
NS_ENSURE_SUCCESS(rv, nullptr);
return BasePrincipal::CreateCodebasePrincipal(uri, attrs);
}
already_AddRefed<BasePrincipal> BasePrincipal::CloneForcingFirstPartyDomain(
nsIURI* aURI) {
if (NS_WARN_IF(!IsCodebasePrincipal())) {

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

@ -166,9 +166,6 @@ class BasePrincipal : public nsJSPrincipals {
PrincipalKind Kind() const { return mKind; }
already_AddRefed<BasePrincipal>
CloneStrippingUserContextIdAndFirstPartyDomain();
already_AddRefed<BasePrincipal> CloneForcingFirstPartyDomain(nsIURI* aURI);
already_AddRefed<BasePrincipal> CloneForcingOriginAttributes(

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

@ -58,6 +58,7 @@ skip-if = (os == 'linux' && bits == 32) # ADB start() fails on linux 32, see Bug
[browser_aboutdebugging_devtoolstoolbox_focus.js]
[browser_aboutdebugging_devtoolstoolbox_menubar.js]
[browser_aboutdebugging_devtoolstoolbox_performance.js]
skip-if = os == 'linux' && e10s && (asan || debug) # Same skip-if as old perf panel test suite. Bug 1254821
[browser_aboutdebugging_devtoolstoolbox_reload.js]
skip-if = verify # test loads the toolbox 2 times for each panel, might timeout or OOM
[browser_aboutdebugging_devtoolstoolbox_shortcuts.js]

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

@ -41,6 +41,7 @@ support-files =
doc_variables_2.html
doc_variables_3.html
head.js
!/devtools/client/debugger/test/mochitest/helpers/context.js
!/devtools/client/inspector/test/head.js
!/devtools/client/inspector/test/shared-head.js
!/devtools/client/shared/test/shared-head.js

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

@ -65,7 +65,7 @@ static void Register(BrowsingContext* aBrowsingContext) {
aBrowsingContext->Group()->Register(aBrowsingContext);
}
BrowsingContext* BrowsingContext::TopLevelBrowsingContext() {
BrowsingContext* BrowsingContext::Top() {
BrowsingContext* bc = this;
while (bc->mParent) {
bc = bc->mParent;
@ -307,6 +307,10 @@ void BrowsingContext::CacheChildren(bool aFromIPC) {
bool BrowsingContext::IsCached() { return sCachedBrowsingContexts->has(Id()); }
bool BrowsingContext::HasOpener() const {
return sBrowsingContexts->has(mOpenerId);
}
void BrowsingContext::GetChildren(
nsTArray<RefPtr<BrowsingContext>>& aChildren) {
MOZ_ALWAYS_TRUE(aChildren.AppendElements(mChildren));
@ -413,7 +417,7 @@ BrowsingContext* BrowsingContext::FindWithSpecialName(const nsAString& aName) {
}
if (aName.LowerCaseEqualsLiteral("_top")) {
BrowsingContext* top = TopLevelBrowsingContext();
BrowsingContext* top = Top();
return CanAccess(top) ? top : nullptr;
}
@ -490,7 +494,7 @@ void BrowsingContext::NotifyUserGestureActivation() {
// We would set the user gesture activation flag on the top level browsing
// context, which would automatically be sync to other top level browsing
// contexts which are in the different process.
RefPtr<BrowsingContext> topLevelBC = TopLevelBrowsingContext();
RefPtr<BrowsingContext> topLevelBC = Top();
USER_ACTIVATION_LOG("Get top level browsing context 0x%08" PRIx64,
topLevelBC->Id());
topLevelBC->SetIsActivatedByUserGesture(true);
@ -500,14 +504,14 @@ void BrowsingContext::NotifyResetUserGestureActivation() {
// We would reset the user gesture activation flag on the top level browsing
// context, which would automatically be sync to other top level browsing
// contexts which are in the different process.
RefPtr<BrowsingContext> topLevelBC = TopLevelBrowsingContext();
RefPtr<BrowsingContext> topLevelBC = Top();
USER_ACTIVATION_LOG("Get top level browsing context 0x%08" PRIx64,
topLevelBC->Id());
topLevelBC->SetIsActivatedByUserGesture(false);
}
bool BrowsingContext::GetUserGestureActivation() {
RefPtr<BrowsingContext> topLevelBC = TopLevelBrowsingContext();
RefPtr<BrowsingContext> topLevelBC = Top();
return topLevelBC->GetIsActivatedByUserGesture();
}
@ -602,7 +606,7 @@ void BrowsingContext::Blur(ErrorResult& aError) {
Nullable<WindowProxyHolder> BrowsingContext::GetTop(ErrorResult& aError) {
// We never return null or throw an error, but the implementation in
// nsGlobalWindow does and we need to use the same signature.
return WindowProxyHolder(TopLevelBrowsingContext());
return WindowProxyHolder(Top());
}
void BrowsingContext::GetOpener(JSContext* aCx,
@ -833,12 +837,12 @@ bool IPDLParamTraits<dom::BrowsingContext>::Read(
}
if (id == 0) {
aResult = nullptr;
*aResult = nullptr;
return true;
}
*aResult = dom::BrowsingContext::Get(id);
MOZ_ASSERT(aResult, "Deserialized absent BrowsingContext!");
MOZ_ASSERT(*aResult, "Deserialized absent BrowsingContext!");
// If this is an in-process actor, free the reference taken in ::Write().
if (!aActor->GetIPCChannel()->IsCrossProcess()) {
@ -846,7 +850,7 @@ bool IPDLParamTraits<dom::BrowsingContext>::Read(
NS_IF_RELEASE(bc);
}
return aResult != nullptr;
return *aResult != nullptr;
}
void IPDLParamTraits<dom::BrowsingContext::Transaction>::Write(

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

@ -121,6 +121,7 @@ class BrowsingContext : public nsWrapperCache,
// null if it's not.
nsIDocShell* GetDocShell() { return mDocShell; }
void SetDocShell(nsIDocShell* aDocShell);
void ClearDocShell() { mDocShell = nullptr; }
// Get the outer window object for this BrowsingContext if it is in-process
// and still has a docshell, or null otherwise.
@ -156,11 +157,15 @@ class BrowsingContext : public nsWrapperCache,
BrowsingContext* GetParent() const { return mParent; }
BrowsingContext* Top();
already_AddRefed<BrowsingContext> GetOpener() const { return Get(mOpenerId); }
void SetOpener(BrowsingContext* aOpener) {
SetOpenerId(aOpener ? aOpener->Id() : 0);
}
bool HasOpener() const;
void GetChildren(nsTArray<RefPtr<BrowsingContext>>& aChildren);
BrowsingContextGroup* Group() { return mGroup; }
@ -384,8 +389,6 @@ class BrowsingContext : public nsWrapperCache,
// reach its browsing context anymore.
void ClearWindowProxy() { mWindowProxy = nullptr; }
BrowsingContext* TopLevelBrowsingContext();
friend class Location;
friend class RemoteLocationProxy;
/**

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

@ -2160,6 +2160,11 @@ class Document : public nsINode,
bool IsScriptEnabled();
/**
* Returns true if this document was created from a nsXULPrototypeDocument.
*/
bool HasPrototypeDocument() const { return mPrototypeDocument; }
bool IsTopLevelContentDocument() const { return mIsTopLevelContentDocument; }
void SetIsTopLevelContentDocument(bool aIsTopLevelContentDocument) {
mIsTopLevelContentDocument = aIsTopLevelContentDocument;

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

@ -9575,11 +9575,12 @@ nsresult nsContentUtils::NewXULOrHTMLElement(
if (aFromParser == dom::NOT_FROM_PARSER) {
global = GetEntryGlobal();
// XUL documents always use NOT_FROM_PARSER for non-XUL elements. We can
// get the global from the document in that case.
// Documents created from the PrototypeDocumentSink always use
// NOT_FROM_PARSER for non-XUL elements. We can get the global from the
// document in that case.
if (!global) {
Document* doc = nodeInfo->GetDocument();
if (doc && doc->IsXULDocument()) {
if (doc && doc->HasPrototypeDocument()) {
global = doc->GetScopeObject();
}
}

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

@ -151,8 +151,8 @@ typedef ScrollableLayerGuid::ViewID ViewID;
// we'd need to re-institute a fixed version of bug 98158.
#define MAX_DEPTH_CONTENT_FRAMES 10
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsFrameLoader, mDocShell, mMessageManager,
mChildMessageManager, mOpener,
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsFrameLoader, mBrowsingContext,
mMessageManager, mChildMessageManager,
mParentSHistory, mRemoteBrowser)
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameLoader)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameLoader)
@ -164,11 +164,11 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameLoader)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
nsFrameLoader::nsFrameLoader(Element* aOwner, nsPIDOMWindowOuter* aOpener,
nsFrameLoader::nsFrameLoader(Element* aOwner, BrowsingContext* aBrowsingContext,
bool aNetworkCreated)
: mOwnerContent(aOwner),
: mBrowsingContext(aBrowsingContext),
mOwnerContent(aOwner),
mDetachedSubdocFrame(nullptr),
mOpener(aOpener),
mRemoteBrowser(nullptr),
mChildID(0),
mDepthTooGreat(false),
@ -184,15 +184,15 @@ nsFrameLoader::nsFrameLoader(Element* aOwner, nsPIDOMWindowOuter* aOpener,
mIsRemoteFrame(false),
mObservingOwnerContent(false) {
mIsRemoteFrame = ShouldUseRemoteProcess();
MOZ_ASSERT(!mIsRemoteFrame || !aOpener,
MOZ_ASSERT(!mIsRemoteFrame || !mBrowsingContext->HasOpener(),
"Cannot pass aOpener for a remote frame!");
}
nsFrameLoader::nsFrameLoader(Element* aOwner,
nsFrameLoader::nsFrameLoader(Element* aOwner, BrowsingContext* aBrowsingContext,
const mozilla::dom::RemotenessOptions& aOptions)
: mOwnerContent(aOwner),
: mBrowsingContext(aBrowsingContext),
mOwnerContent(aOwner),
mDetachedSubdocFrame(nullptr),
mOpener(nullptr),
mRemoteBrowser(nullptr),
mChildID(0),
mDepthTooGreat(false),
@ -211,14 +211,6 @@ nsFrameLoader::nsFrameLoader(Element* aOwner,
(!aOptions.mRemoteType.Value().IsVoid())) {
mIsRemoteFrame = true;
}
bool hasOpener =
aOptions.mOpener.WasPassed() && !aOptions.mOpener.Value().IsNull();
MOZ_ASSERT(!mIsRemoteFrame || !hasOpener,
"Cannot pass aOpener for a remote frame!");
if (hasOpener) {
// This seems slightly unwieldy.
mOpener = aOptions.mOpener.Value().Value().get()->GetDOMWindow();
}
}
nsFrameLoader::~nsFrameLoader() {
@ -228,9 +220,75 @@ nsFrameLoader::~nsFrameLoader() {
MOZ_RELEASE_ASSERT(mDestroyCalled);
}
/* static */
nsFrameLoader* nsFrameLoader::Create(Element* aOwner,
nsPIDOMWindowOuter* aOpener,
static void GetFrameName(Element* aOwnerContent, nsAString& aFrameName) {
int32_t namespaceID = aOwnerContent->GetNameSpaceID();
if (namespaceID == kNameSpaceID_XHTML && !aOwnerContent->IsInHTMLDocument()) {
aOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, aFrameName);
} else {
aOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, aFrameName);
// XXX if no NAME then use ID, after a transition period this will be
// changed so that XUL only uses ID too (bug 254284).
if (aFrameName.IsEmpty() && namespaceID == kNameSpaceID_XUL) {
aOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, aFrameName);
}
}
}
static already_AddRefed<BrowsingContext> CreateBrowsingContext(
Element* aOwner, BrowsingContext* aOpener) {
Document* doc = aOwner->OwnerDoc();
// Get our parent docshell off the document of mOwnerContent
// XXXbz this is such a total hack.... We really need to have a
// better setup for doing this.
// Determine our parent nsDocShell
RefPtr<nsDocShell> parentDocShell = nsDocShell::Cast(doc->GetDocShell());
if (NS_WARN_IF(!parentDocShell)) {
return nullptr;
}
RefPtr<BrowsingContext> parentContext = parentDocShell->GetBrowsingContext();
MOZ_DIAGNOSTIC_ASSERT(parentContext, "docShell must have BrowsingContext");
// Don't create a child docshell for a closed browsing context.
if (parentContext->GetClosed()) {
return nullptr;
}
// Determine the frame name for the new browsing context.
nsAutoString frameName;
GetFrameName(aOwner, frameName);
// Check if our new context is chrome or content
bool isContent =
parentContext->IsContent() ||
aOwner->AttrValueIs(
kNameSpaceID_None,
aOwner->IsXULElement() ? nsGkAtoms::type : nsGkAtoms::mozframetype,
nsGkAtoms::content, eIgnoreCase);
// Force mozbrowser frames to always be content, even if the mozbrowser
// interfaces are disabled.
nsCOMPtr<nsIMozBrowserFrame> mozbrowser = aOwner->GetAsMozBrowserFrame();
if (!isContent && mozbrowser) {
mozbrowser->GetMozbrowser(&isContent);
}
// If we're content but our parent isn't, we're going to want to
// start a new browsing context tree.
if (isContent && !parentContext->IsContent()) {
parentContext = nullptr;
}
BrowsingContext::Type type = isContent ? BrowsingContext::Type::Content
: BrowsingContext::Type::Chrome;
return BrowsingContext::Create(parentContext, aOpener, frameName, type);
}
nsFrameLoader* nsFrameLoader::Create(Element* aOwner, BrowsingContext* aOpener,
bool aNetworkCreated) {
NS_ENSURE_TRUE(aOwner, nullptr);
Document* doc = aOwner->OwnerDoc();
@ -260,7 +318,9 @@ nsFrameLoader* nsFrameLoader::Create(Element* aOwner,
doc->IsStaticDocument()),
nullptr);
return new nsFrameLoader(aOwner, aOpener, aNetworkCreated);
RefPtr<BrowsingContext> context = CreateBrowsingContext(aOwner, aOpener);
NS_ENSURE_TRUE(context, nullptr);
return new nsFrameLoader(aOwner, context, aNetworkCreated);
}
/* static */
@ -270,7 +330,21 @@ nsFrameLoader* nsFrameLoader::Create(
NS_ENSURE_TRUE(aOwner, nullptr);
// This version of Create is only called for Remoteness updates, so we can
// assume we need a FrameLoader here and skip the check in the other Create.
return new nsFrameLoader(aOwner, aOptions);
bool hasOpener =
aOptions.mOpener.WasPassed() && !aOptions.mOpener.Value().IsNull();
MOZ_ASSERT(!aOptions.mRemoteType.WasPassed() ||
aOptions.mRemoteType.Value().IsVoid() || !hasOpener,
"Cannot pass aOpener for a remote frame!");
// This seems slightly unwieldy.
RefPtr<BrowsingContext> opener;
if (hasOpener) {
opener = aOptions.mOpener.Value().Value().get();
}
RefPtr<BrowsingContext> context = CreateBrowsingContext(aOwner, opener);
NS_ENSURE_TRUE(context, nullptr);
return new nsFrameLoader(aOwner, context, aOptions);
}
void nsFrameLoader::LoadFrame(bool aOriginalSrc) {
@ -428,8 +502,8 @@ nsresult nsFrameLoader::ReallyStartLoadingInternal() {
if (NS_FAILED(rv)) {
return rv;
}
NS_ASSERTION(mDocShell,
"MaybeCreateDocShell succeeded with a null mDocShell");
MOZ_ASSERT(GetDocShell(),
"MaybeCreateDocShell succeeded with a null docShell");
// Just to be safe, recheck uri.
rv = CheckURILoad(mURIToLoad, mTriggeringPrincipal);
@ -531,7 +605,7 @@ nsresult nsFrameLoader::ReallyStartLoadingInternal() {
mNeedsAsyncDestroy = true;
loadState->SetLoadFlags(flags);
loadState->SetFirstParty(false);
rv = mDocShell->LoadURI(loadState);
rv = GetDocShell()->LoadURI(loadState);
mNeedsAsyncDestroy = tmpState;
mURIToLoad = nullptr;
NS_ENSURE_SUCCESS(rv, rv);
@ -590,11 +664,11 @@ nsDocShell* nsFrameLoader::GetDocShell(ErrorResult& aRv) {
aRv.Throw(rv);
return nullptr;
}
NS_ASSERTION(mDocShell,
"MaybeCreateDocShell succeeded, but null mDocShell");
MOZ_ASSERT(GetDocShell(),
"MaybeCreateDocShell succeeded, but null docShell");
}
return mDocShell;
return GetDocShell();
}
static void SetTreeOwnerAndChromeEventHandlerOnDocshellTree(
@ -740,20 +814,20 @@ bool nsFrameLoader::Show(int32_t marginWidth, int32_t marginHeight,
if (NS_FAILED(rv)) {
return false;
}
NS_ASSERTION(mDocShell, "MaybeCreateDocShell succeeded, but null mDocShell");
if (!mDocShell) {
MOZ_ASSERT(GetDocShell(), "MaybeCreateDocShell succeeded, but null docShell");
if (!GetDocShell()) {
return false;
}
mDocShell->SetMarginWidth(marginWidth);
mDocShell->SetMarginHeight(marginHeight);
GetDocShell()->SetMarginWidth(marginWidth);
GetDocShell()->SetMarginHeight(marginHeight);
mDocShell->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X,
scrollbarPrefX);
mDocShell->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_Y,
scrollbarPrefY);
GetDocShell()->SetDefaultScrollbarPreferences(
nsIScrollable::ScrollOrientation_X, scrollbarPrefX);
GetDocShell()->SetDefaultScrollbarPreferences(
nsIScrollable::ScrollOrientation_Y, scrollbarPrefY);
if (PresShell* presShell = mDocShell->GetPresShell()) {
if (PresShell* presShell = GetDocShell()->GetPresShell()) {
// Ensure root scroll frame is reflowed in case scroll preferences or
// margins have changed
nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
@ -767,7 +841,7 @@ bool nsFrameLoader::Show(int32_t marginWidth, int32_t marginHeight,
nsView* view = frame->EnsureInnerView();
if (!view) return false;
RefPtr<nsDocShell> baseWindow = mDocShell;
RefPtr<nsDocShell> baseWindow = GetDocShell();
baseWindow->InitWindow(nullptr, view->GetWidget(), 0, 0, size.width,
size.height);
// This is kinda whacky, this "Create()" call doesn't really
@ -775,13 +849,13 @@ bool nsFrameLoader::Show(int32_t marginWidth, int32_t marginHeight,
// "Create"...
baseWindow->Create();
baseWindow->SetVisibility(true);
NS_ENSURE_TRUE(mDocShell, false);
NS_ENSURE_TRUE(GetDocShell(), false);
// Trigger editor re-initialization if midas is turned on in the
// sub-document. This shouldn't be necessary, but given the way our
// editor works, it is. See
// https://bugzilla.mozilla.org/show_bug.cgi?id=284245
if (RefPtr<PresShell> presShell = mDocShell->GetPresShell()) {
if (RefPtr<PresShell> presShell = GetDocShell()->GetPresShell()) {
Document* doc = presShell->GetDocument();
nsHTMLDocument* htmlDoc =
doc && doc->IsHTMLOrXHTML() ? doc->AsHTMLDocument() : nullptr;
@ -793,7 +867,7 @@ bool nsFrameLoader::Show(int32_t marginWidth, int32_t marginHeight,
if (designMode.EqualsLiteral("on")) {
// Hold on to the editor object to let the document reattach to the
// same editor object, instead of creating a new one.
RefPtr<HTMLEditor> htmlEditor = mDocShell->GetHTMLEditor();
RefPtr<HTMLEditor> htmlEditor = GetDocShell()->GetHTMLEditor();
Unused << htmlEditor;
htmlDoc->SetDesignMode(NS_LITERAL_STRING("off"), Nothing(),
IgnoreErrors());
@ -803,9 +877,9 @@ bool nsFrameLoader::Show(int32_t marginWidth, int32_t marginHeight,
} else {
// Re-initialize the presentation for contenteditable documents
bool editable = false, hasEditingSession = false;
mDocShell->GetEditable(&editable);
mDocShell->GetHasEditingSession(&hasEditingSession);
RefPtr<HTMLEditor> htmlEditor = mDocShell->GetHTMLEditor();
GetDocShell()->GetEditable(&editable);
GetDocShell()->GetHasEditingSession(&hasEditingSession);
RefPtr<HTMLEditor> htmlEditor = GetDocShell()->GetHTMLEditor();
if (editable && hasEditingSession && htmlEditor) {
htmlEditor->PostCreate();
}
@ -825,20 +899,24 @@ bool nsFrameLoader::Show(int32_t marginWidth, int32_t marginHeight,
void nsFrameLoader::MarginsChanged(uint32_t aMarginWidth,
uint32_t aMarginHeight) {
// We assume that the margins are always zero for remote frames.
if (IsRemoteFrame()) return;
if (IsRemoteFrame()) {
return;
}
// If there's no docshell, we're probably not up and running yet.
// nsFrameLoader::Show() will take care of setting the right
// margins.
if (!mDocShell) return;
if (!GetDocShell()) {
return;
}
// Set the margins
mDocShell->SetMarginWidth(aMarginWidth);
mDocShell->SetMarginHeight(aMarginHeight);
GetDocShell()->SetMarginWidth(aMarginWidth);
GetDocShell()->SetMarginHeight(aMarginHeight);
// There's a cached property declaration block
// that needs to be updated
if (Document* doc = mDocShell->GetDocument()) {
if (Document* doc = GetDocShell()->GetDocument()) {
for (nsINode* cur = doc; cur; cur = cur->GetNextNode()) {
if (cur->IsHTMLElement(nsGkAtoms::body)) {
static_cast<HTMLBodyElement*>(cur)->ClearMappedServoStyle();
@ -848,7 +926,7 @@ void nsFrameLoader::MarginsChanged(uint32_t aMarginWidth,
// Trigger a restyle if there's a prescontext
// FIXME: This could do something much less expensive.
if (nsPresContext* presContext = mDocShell->GetPresContext()) {
if (nsPresContext* presContext = GetDocShell()->GetPresContext()) {
// rebuild, because now the same nsMappedAttributes* will produce
// a different style
presContext->RebuildAllStyleData(nsChangeHint(0),
@ -937,13 +1015,15 @@ void nsFrameLoader::Hide() {
return;
}
if (!mDocShell) return;
if (!GetDocShell()) {
return;
}
nsCOMPtr<nsIContentViewer> contentViewer;
mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
GetDocShell()->GetContentViewer(getter_AddRefs(contentViewer));
if (contentViewer) contentViewer->SetSticky(false);
RefPtr<nsDocShell> baseWin = mDocShell;
RefPtr<nsDocShell> baseWin = GetDocShell();
baseWin->SetVisibility(false);
baseWin->SetParentWidget(nullptr);
}
@ -1654,26 +1734,26 @@ void nsFrameLoader::StartDestroy() {
// Seems like this is a dynamic frame removal.
if (dynamicSubframeRemoval) {
if (mDocShell) {
mDocShell->RemoveFromSessionHistory();
if (GetDocShell()) {
GetDocShell()->RemoveFromSessionHistory();
}
}
// Let the tree owner know we're gone.
if (mIsTopLevelContent) {
if (mDocShell) {
if (GetDocShell()) {
nsCOMPtr<nsIDocShellTreeItem> parentItem;
mDocShell->GetParent(getter_AddRefs(parentItem));
GetDocShell()->GetParent(getter_AddRefs(parentItem));
nsCOMPtr<nsIDocShellTreeOwner> owner = do_GetInterface(parentItem);
if (owner) {
owner->ContentShellRemoved(mDocShell);
owner->ContentShellRemoved(GetDocShell());
}
}
}
// Let our window know that we are gone
if (mDocShell) {
nsCOMPtr<nsPIDOMWindowOuter> win_private(mDocShell->GetWindow());
if (GetDocShell()) {
nsCOMPtr<nsPIDOMWindowOuter> win_private(GetDocShell()->GetWindow());
if (win_private) {
win_private->SetFrameElementInternal(nullptr);
}
@ -1753,10 +1833,11 @@ void nsFrameLoader::DestroyDocShell() {
}
// Destroy the docshell.
if (mDocShell) {
mDocShell->Destroy();
if (GetDocShell()) {
GetDocShell()->Destroy();
}
mDocShell = nullptr;
mBrowsingContext = nullptr;
if (mChildMessageManager) {
// Stop handling events in the in-process frame script.
@ -1888,37 +1969,8 @@ bool nsFrameLoader::ShouldUseRemoteProcess() {
nsGkAtoms::_true, eCaseMatters);
}
static already_AddRefed<BrowsingContext> CreateBrowsingContext(
BrowsingContext* aParentContext, BrowsingContext* aOpenerContext,
const nsAString& aName, bool aIsContent) {
// If we're content but our parent isn't, we're going to want to start a new
// browsing context tree.
if (aIsContent && aParentContext && !aParentContext->IsContent()) {
aParentContext = nullptr;
}
BrowsingContext::Type type = aIsContent ? BrowsingContext::Type::Content
: BrowsingContext::Type::Chrome;
return BrowsingContext::Create(aParentContext, aOpenerContext, aName, type);
}
static void GetFrameName(Element* aOwnerContent, nsAString& aFrameName) {
int32_t namespaceID = aOwnerContent->GetNameSpaceID();
if (namespaceID == kNameSpaceID_XHTML && !aOwnerContent->IsInHTMLDocument()) {
aOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, aFrameName);
} else {
aOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, aFrameName);
// XXX if no NAME then use ID, after a transition period this will be
// changed so that XUL only uses ID too (bug 254284).
if (aFrameName.IsEmpty() && namespaceID == kNameSpaceID_XUL) {
aOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, aFrameName);
}
}
}
nsresult nsFrameLoader::MaybeCreateDocShell() {
if (mDocShell) {
if (GetDocShell()) {
return NS_OK;
}
if (IsRemoteFrame()) {
@ -1947,43 +1999,20 @@ nsresult nsFrameLoader::MaybeCreateDocShell() {
return NS_ERROR_NOT_AVAILABLE;
}
// Determine our parent nsDocShell
RefPtr<nsDocShell> parentDocShell = nsDocShell::Cast(doc->GetDocShell());
if (NS_WARN_IF(!parentDocShell)) {
return NS_ERROR_UNEXPECTED;
}
RefPtr<BrowsingContext> parentBC = parentDocShell->GetBrowsingContext();
MOZ_ASSERT(parentBC, "docShell must have BrowsingContext");
// nsDocShell::Create will attach itself to the passed browsing
// context inside of nsDocShell::Create
RefPtr<nsDocShell> docShell = nsDocShell::Create(mBrowsingContext);
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
// Determine the frame name for the new browsing context.
nsAutoString frameName;
GetFrameName(mOwnerContent, frameName);
// Check if our new context is chrome or content
bool isContent = parentBC->IsContent() ||
mOwnerContent->AttrValueIs(kNameSpaceID_None, TypeAttrName(),
nsGkAtoms::content, eIgnoreCase);
// Force mozbrowser frames to always be content, even if the mozbrowser
// interfaces are disabled.
nsCOMPtr<nsIMozBrowserFrame> mozbrowser =
mOwnerContent->GetAsMozBrowserFrame();
if (!isContent && mozbrowser) {
mozbrowser->GetMozbrowser(&isContent);
}
RefPtr<BrowsingContext> openerBC =
mOpener ? mOpener->GetBrowsingContext() : nullptr;
RefPtr<BrowsingContext> browsingContext =
CreateBrowsingContext(parentBC, openerBC, frameName, isContent);
mDocShell = nsDocShell::Create(browsingContext);
NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
mIsTopLevelContent = isContent && !parentBC->IsContent();
mIsTopLevelContent =
mBrowsingContext->IsContent() && !mBrowsingContext->GetParent();
if (!mNetworkCreated && !mIsTopLevelContent) {
mDocShell->SetCreatedDynamically(true);
docShell->SetCreatedDynamically(true);
}
if (mIsTopLevelContent) {
@ -1992,19 +2021,20 @@ nsresult nsFrameLoader::MaybeCreateDocShell() {
//
// XXX(nika): Consider removing the DocShellTree in the future, for
// consistency between local and remote frames..
parentDocShell->AddChild(mDocShell);
parentDocShell->AddChild(docShell);
}
// Now that we are part of the DocShellTree, attach our DocShell to our
// parent's TreeOwner.
nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
parentDocShell->GetTreeOwner(getter_AddRefs(parentTreeOwner));
AddTreeItemToTreeOwner(mDocShell, parentTreeOwner);
AddTreeItemToTreeOwner(docShell, parentTreeOwner);
// Make sure all nsDocShells have links back to the content element in the
// nearest enclosing chrome shell.
RefPtr<EventTarget> chromeEventHandler;
if (parentBC->IsContent()) {
bool parentIsContent = parentDocShell->GetBrowsingContext()->IsContent();
if (parentIsContent) {
// Our parent shell is a content shell. Get the chrome event handler from it
// and use that for our shell as well.
parentDocShell->GetChromeEventHandler(getter_AddRefs(chromeEventHandler));
@ -2014,15 +2044,15 @@ nsresult nsFrameLoader::MaybeCreateDocShell() {
chromeEventHandler = mOwnerContent;
}
mDocShell->SetChromeEventHandler(chromeEventHandler);
docShell->SetChromeEventHandler(chromeEventHandler);
// This is nasty, this code (the mDocShell->GetWindow() below)
// This is nasty, this code (the docShell->GetWindow() below)
// *must* come *after* the above call to
// mDocShell->SetChromeEventHandler() for the global window to get
// docShell->SetChromeEventHandler() for the global window to get
// the right chrome event handler.
// Tell the window about the frame that hosts it.
nsCOMPtr<nsPIDOMWindowOuter> newWindow = mDocShell->GetWindow();
nsCOMPtr<nsPIDOMWindowOuter> newWindow = docShell->GetWindow();
if (NS_WARN_IF(!newWindow)) {
// Do not call Destroy() here. See bug 472312.
NS_WARNING("Something wrong when creating the docshell for a frameloader!");
@ -2031,13 +2061,10 @@ nsresult nsFrameLoader::MaybeCreateDocShell() {
newWindow->SetFrameElementInternal(mOwnerContent);
// Set the opener window if we have one provided here XXX(nika): We
// should tell our BrowsingContext this as we create it.
// TODO(farre): Remove this when nsGlobalWindowOuter::GetOpenerWindowOuter
// starts using BrowsingContext::GetOpener.
if (mOpener) {
newWindow->SetOpenerWindow(mOpener, true);
mOpener = nullptr;
if (RefPtr<BrowsingContext> opener = mBrowsingContext->GetOpener()) {
newWindow->SetOpenerWindow(opener->GetDOMWindow(), true);
}
// Allow scripts to close the docshell if specified.
@ -2051,7 +2078,6 @@ nsresult nsFrameLoader::MaybeCreateDocShell() {
// This is kinda whacky, this call doesn't really create anything,
// but it must be called to make sure things are properly
// initialized.
RefPtr<nsDocShell> docShell = mDocShell;
if (NS_FAILED(docShell->Create())) {
// Do not call Destroy() here. See bug 472312.
NS_WARNING("Something wrong when creating the docshell for a frameloader!");
@ -2064,13 +2090,13 @@ nsresult nsFrameLoader::MaybeCreateDocShell() {
if (mIsTopLevelContent && mOwnerContent->IsXULElement(nsGkAtoms::browser) &&
!mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disablehistory)) {
// XXX(nika): Set this up more explicitly?
nsresult rv = mDocShell->InitSessionHistory();
nsresult rv = docShell->InitSessionHistory();
NS_ENSURE_SUCCESS(rv, rv);
mParentSHistory = new ParentSHistory(this);
}
OriginAttributes attrs;
if (parentDocShell->ItemType() == mDocShell->ItemType()) {
if (parentDocShell->ItemType() == docShell->ItemType()) {
attrs = parentDocShell->GetOriginAttributes();
}
@ -2081,7 +2107,7 @@ nsresult nsFrameLoader::MaybeCreateDocShell() {
//
// For example, firstPartyDomain is computed from top-level document, it
// doesn't exist in the top-level docshell.
if (parentBC->IsContent() &&
if (parentIsContent &&
!nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()) &&
!OwnerIsMozBrowserFrame()) {
OriginAttributes oa = doc->NodePrincipal()->OriginAttributesRef();
@ -2109,18 +2135,18 @@ nsresult nsFrameLoader::MaybeCreateDocShell() {
if (OwnerIsMozBrowserFrame()) {
attrs.mAppId = nsIScriptSecurityManager::NO_APP_ID;
attrs.mInIsolatedMozBrowser = OwnerIsIsolatedMozBrowserFrame();
mDocShell->SetFrameType(nsIDocShell::FRAME_TYPE_BROWSER);
docShell->SetFrameType(nsIDocShell::FRAME_TYPE_BROWSER);
} else {
nsCOMPtr<nsIDocShellTreeItem> parentCheck;
mDocShell->GetSameTypeParent(getter_AddRefs(parentCheck));
docShell->GetSameTypeParent(getter_AddRefs(parentCheck));
if (!!parentCheck) {
mDocShell->SetIsFrame();
docShell->SetIsFrame();
}
}
// Apply sandbox flags even if our owner is not an iframe, as this copies
// flags from our owning content's owning document.
// Note: ApplySandboxFlags should be called after mDocShell->SetFrameType
// Note: ApplySandboxFlags should be called after docShell->SetFrameType
// because we need to get the correct presentation URL in ApplySandboxFlags.
uint32_t sandboxFlags = 0;
HTMLIFrameElement* iframe = HTMLIFrameElement::FromNode(mOwnerContent);
@ -2146,16 +2172,16 @@ nsresult nsFrameLoader::MaybeCreateDocShell() {
// For inproc frames, set the docshell properties.
nsAutoString name;
if (mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name)) {
mDocShell->SetName(name);
docShell->SetName(name);
}
mDocShell->SetFullscreenAllowed(
docShell->SetFullscreenAllowed(
mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) ||
mOwnerContent->HasAttr(kNameSpaceID_None,
nsGkAtoms::mozallowfullscreen));
bool isPrivate = mOwnerContent->HasAttr(kNameSpaceID_None,
nsGkAtoms::mozprivatebrowsing);
if (isPrivate) {
if (mDocShell->GetHasLoadedNonBlankURI()) {
if (docShell->GetHasLoadedNonBlankURI()) {
nsContentUtils::ReportToConsoleNonLocalized(
NS_LITERAL_STRING("We should not switch to Private Browsing after "
"loading a document."),
@ -2169,30 +2195,28 @@ nsresult nsFrameLoader::MaybeCreateDocShell() {
}
}
nsDocShell::Cast(mDocShell)->SetOriginAttributes(attrs);
docShell->SetOriginAttributes(attrs);
// Typically there will be a window, however for some cases such as printing
// the document is cloned with a docshell that has no window. We check
// that the window exists to ensure we don't try to gather ancestors for
// those cases.
nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow();
if (!mDocShell->GetIsMozBrowser() &&
parentDocShell->ItemType() == mDocShell->ItemType() &&
if (!docShell->GetIsMozBrowser() &&
parentDocShell->ItemType() == docShell->ItemType() &&
!doc->IsStaticDocument() && win) {
// Propagate through the ancestor principals.
nsTArray<nsCOMPtr<nsIPrincipal>> ancestorPrincipals;
// Make a copy, so we can modify it.
ancestorPrincipals = doc->AncestorPrincipals();
ancestorPrincipals.InsertElementAt(0, doc->NodePrincipal());
nsDocShell::Cast(mDocShell)->SetAncestorPrincipals(
std::move(ancestorPrincipals));
docShell->SetAncestorPrincipals(std::move(ancestorPrincipals));
// Repeat for outer window IDs.
nsTArray<uint64_t> ancestorOuterWindowIDs;
ancestorOuterWindowIDs = doc->AncestorOuterWindowIDs();
ancestorOuterWindowIDs.InsertElementAt(0, win->WindowID());
nsDocShell::Cast(mDocShell)->SetAncestorOuterWindowIDs(
std::move(ancestorOuterWindowIDs));
docShell->SetAncestorOuterWindowIDs(std::move(ancestorOuterWindowIDs));
}
ReallyLoadFrameScripts();
@ -2249,19 +2273,19 @@ nsresult nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI) {
if (NS_FAILED(rv)) {
return rv;
}
NS_ASSERTION(mDocShell, "MaybeCreateDocShell succeeded, but null mDocShell");
if (!mDocShell) {
MOZ_ASSERT(GetDocShell(), "MaybeCreateDocShell succeeded, but null docShell");
if (!GetDocShell()) {
return NS_ERROR_FAILURE;
}
// Check that we're still in the docshell tree.
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
GetDocShell()->GetTreeOwner(getter_AddRefs(treeOwner));
NS_WARNING_ASSERTION(treeOwner,
"Trying to load a new url to a docshell without owner!");
NS_ENSURE_STATE(treeOwner);
if (mDocShell->ItemType() != nsIDocShellTreeItem::typeContent) {
if (GetDocShell()->ItemType() != nsIDocShellTreeItem::typeContent) {
// No need to do recursion-protection here XXXbz why not?? Do we really
// trust people not to screw up with non-content docshells?
return NS_OK;
@ -2270,7 +2294,7 @@ nsresult nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI) {
// Bug 8065: Don't exceed some maximum depth in content frames
// (MAX_DEPTH_CONTENT_FRAMES)
nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
mDocShell->GetSameTypeParent(getter_AddRefs(parentAsItem));
GetDocShell()->GetSameTypeParent(getter_AddRefs(parentAsItem));
int32_t depth = 0;
while (parentAsItem) {
++depth;
@ -2302,7 +2326,7 @@ nsresult nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI) {
}
}
int32_t matchCount = 0;
mDocShell->GetSameTypeParent(getter_AddRefs(parentAsItem));
GetDocShell()->GetSameTypeParent(getter_AddRefs(parentAsItem));
while (parentAsItem) {
// Check the parent URI with the URI we're loading
nsCOMPtr<nsIWebNavigation> parentAsNav(do_QueryInterface(parentAsItem));
@ -2580,27 +2604,15 @@ bool nsFrameLoader::TryRemoteBrowser() {
// If we're in a content process, create a BrowserBridgeChild actor.
if (XRE_IsContentProcess()) {
// Determine the frame name for the new browsing context.
nsAutoString frameName;
GetFrameName(mOwnerContent, frameName);
RefPtr<BrowsingContext> parentBC;
parentDocShell->GetBrowsingContext(getter_AddRefs(parentBC));
MOZ_ASSERT(parentBC, "docShell must have BrowsingContext");
// XXX(nika): due to limitations with Browsing Context Groups and multiple
// processes, we can't link up aParent yet! (Bug 1532661)
RefPtr<BrowsingContext> browsingContext =
CreateBrowsingContext(parentBC, nullptr, frameName, true);
mBrowserBridgeChild = BrowserBridgeChild::Create(
this, context, NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE), browsingContext);
this, context, NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE),
mBrowsingContext);
return !!mBrowserBridgeChild;
}
mRemoteBrowser =
ContentParent::CreateBrowser(context, ownerElement, openerContentParent,
sameTabGroupAs, nextTabParentId);
mRemoteBrowser = ContentParent::CreateBrowser(
context, ownerElement, mBrowsingContext, openerContentParent,
sameTabGroupAs, nextTabParentId);
if (!mRemoteBrowser) {
return false;
}
@ -2659,7 +2671,7 @@ bool nsFrameLoader::TryRemoteBrowser() {
bool nsFrameLoader::IsRemoteFrame() {
if (mIsRemoteFrame) {
MOZ_ASSERT(!mDocShell, "Found a remote frame with a DocShell");
MOZ_ASSERT(!GetDocShell(), "Found a remote frame with a DocShell");
return true;
}
return false;
@ -2732,13 +2744,13 @@ void nsFrameLoader::ActivateFrameEvent(const nsAString& aType, bool aCapture,
nsresult nsFrameLoader::CreateStaticClone(nsFrameLoader* aDest) {
aDest->MaybeCreateDocShell();
NS_ENSURE_STATE(aDest->mDocShell);
NS_ENSURE_STATE(aDest->GetDocShell());
nsCOMPtr<Document> kungFuDeathGrip = aDest->mDocShell->GetDocument();
nsCOMPtr<Document> kungFuDeathGrip = aDest->GetDocShell()->GetDocument();
Unused << kungFuDeathGrip;
nsCOMPtr<nsIContentViewer> viewer;
aDest->mDocShell->GetContentViewer(getter_AddRefs(viewer));
aDest->GetDocShell()->GetContentViewer(getter_AddRefs(viewer));
NS_ENSURE_STATE(viewer);
nsIDocShell* origDocShell = GetDocShell(IgnoreErrors());
@ -2747,7 +2759,7 @@ nsresult nsFrameLoader::CreateStaticClone(nsFrameLoader* aDest) {
nsCOMPtr<Document> doc = origDocShell->GetDocument();
NS_ENSURE_STATE(doc);
nsCOMPtr<Document> clonedDoc = doc->CreateStaticClone(aDest->mDocShell);
nsCOMPtr<Document> clonedDoc = doc->CreateStaticClone(aDest->GetDocShell());
viewer->SetDocument(clonedDoc);
return NS_OK;
@ -2885,13 +2897,13 @@ nsresult nsFrameLoader::EnsureMessageManager() {
if (NS_FAILED(rv)) {
return rv;
}
NS_ASSERTION(mDocShell,
"MaybeCreateDocShell succeeded, but null mDocShell");
if (!mDocShell) {
MOZ_ASSERT(GetDocShell(),
"MaybeCreateDocShell succeeded, but null docShell");
if (!GetDocShell()) {
return NS_ERROR_FAILURE;
}
mChildMessageManager = InProcessTabChildMessageManager::Create(
mDocShell, mOwnerContent, mMessageManager);
GetDocShell(), mOwnerContent, mMessageManager);
NS_ENSURE_TRUE(mChildMessageManager, NS_ERROR_UNEXPECTED);
}
return NS_OK;
@ -2937,7 +2949,7 @@ nsIFrame* nsFrameLoader::GetDetachedSubdocFrame(
}
void nsFrameLoader::ApplySandboxFlags(uint32_t sandboxFlags) {
if (mDocShell) {
if (GetDocShell()) {
uint32_t parentSandboxFlags = mOwnerContent->OwnerDoc()->GetSandboxFlags();
// The child can only add restrictions, never remove them.
@ -2947,11 +2959,11 @@ void nsFrameLoader::ApplySandboxFlags(uint32_t sandboxFlags) {
// sandboxed auxiliary navigation flag to sandboxFlags. See
// https://w3c.github.io/presentation-api/#creating-a-receiving-browsing-context
nsAutoString presentationURL;
nsContentUtils::GetPresentationURL(mDocShell, presentationURL);
nsContentUtils::GetPresentationURL(GetDocShell(), presentationURL);
if (!presentationURL.IsEmpty()) {
sandboxFlags |= SANDBOXED_AUXILIARY_NAVIGATION;
}
mDocShell->SetSandboxFlags(sandboxFlags);
GetDocShell()->SetSandboxFlags(sandboxFlags);
}
}
@ -2977,13 +2989,13 @@ void nsFrameLoader::AttributeChanged(mozilla::dom::Element* aElement,
// Notify our enclosing chrome that our type has changed. We only do this
// if our parent is chrome, since in all other cases we're random content
// subframes and the treeowner shouldn't worry about us.
if (!mDocShell) {
if (!GetDocShell()) {
MaybeUpdatePrimaryTabParent(eTabParentChanged);
return;
}
nsCOMPtr<nsIDocShellTreeItem> parentItem;
mDocShell->GetParent(getter_AddRefs(parentItem));
GetDocShell()->GetParent(getter_AddRefs(parentItem));
if (!parentItem) {
return;
}
@ -3005,14 +3017,16 @@ void nsFrameLoader::AttributeChanged(mozilla::dom::Element* aElement,
// when a content panel is no longer primary, hide any open popups it may have
if (!is_primary) {
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
if (pm) pm->HidePopupsInDocShell(mDocShell);
if (pm) {
pm->HidePopupsInDocShell(GetDocShell());
}
}
#endif
parentTreeOwner->ContentShellRemoved(mDocShell);
parentTreeOwner->ContentShellRemoved(GetDocShell());
if (aElement->AttrValueIs(kNameSpaceID_None, TypeAttrName(),
nsGkAtoms::content, eIgnoreCase)) {
parentTreeOwner->ContentShellAdded(mDocShell, is_primary);
parentTreeOwner->ContentShellAdded(GetDocShell(), is_primary);
}
}
@ -3117,7 +3131,8 @@ already_AddRefed<mozilla::dom::Promise> nsFrameLoader::DrawSnapshot(
gfx::CrossProcessPaint::StartRemote(mRemoteBrowser->GetTabId(), rect,
aScale, color, promise);
} else {
gfx::CrossProcessPaint::StartLocal(mDocShell, rect, aScale, color, promise);
gfx::CrossProcessPaint::StartLocal(GetDocShell(), rect, aScale, color,
promise);
}
return promise.forget();
@ -3152,7 +3167,7 @@ already_AddRefed<BrowsingContext> nsFrameLoader::GetBrowsingContext() {
browsingContext = mBrowserBridgeChild->GetBrowsingContext();
}
} else if (GetDocShell(IgnoreErrors())) {
browsingContext = nsDocShell::Cast(mDocShell)->GetBrowsingContext();
browsingContext = GetDocShell()->GetBrowsingContext();
}
return browsingContext.forget();
}
@ -3199,7 +3214,8 @@ void nsFrameLoader::StartPersistence(
return;
}
nsCOMPtr<Document> rootDoc = mDocShell ? mDocShell->GetDocument() : nullptr;
nsCOMPtr<Document> rootDoc =
GetDocShell() ? GetDocShell()->GetDocument() : nullptr;
nsCOMPtr<Document> foundDoc;
if (aOuterWindowID) {
foundDoc = nsContentUtils::GetSubdocumentWithOuterWindowId(rootDoc,

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

@ -20,6 +20,7 @@
#include "nsIURI.h"
#include "nsFrameMessageManager.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ParentSHistory.h"
#include "mozilla/Attributes.h"
@ -49,7 +50,6 @@ namespace mozilla {
class OriginAttributes;
namespace dom {
class BrowsingContext;
class ChromeMessageSender;
class ContentParent;
class InProcessTabChildMessageManager;
@ -99,7 +99,7 @@ class nsFrameLoader final : public nsStubMutationObserver,
public:
// Called by Frame Elements to create a new FrameLoader.
static nsFrameLoader* Create(mozilla::dom::Element* aOwner,
nsPIDOMWindowOuter* aOpener,
mozilla::dom::BrowsingContext* aOpener,
bool aNetworkCreated);
// Called by nsFrameLoaderOwner::ChangeRemoteness when switching out
@ -118,7 +118,9 @@ class nsFrameLoader final : public nsStubMutationObserver,
void StartDestroy();
void DestroyDocShell();
void DestroyComplete();
nsIDocShell* GetExistingDocShell() { return mDocShell; }
nsIDocShell* GetExistingDocShell() const {
return mBrowsingContext ? mBrowsingContext->GetDocShell() : nullptr;
}
mozilla::dom::InProcessTabChildMessageManager* GetTabChildMessageManager()
const {
return mChildMessageManager;
@ -368,9 +370,11 @@ class nsFrameLoader final : public nsStubMutationObserver,
JS::Handle<JSObject*> aGivenProto) override;
private:
nsFrameLoader(mozilla::dom::Element* aOwner, nsPIDOMWindowOuter* aOpener,
nsFrameLoader(mozilla::dom::Element* aOwner,
mozilla::dom::BrowsingContext* aBrowsingContext,
bool aNetworkCreated);
nsFrameLoader(mozilla::dom::Element* aOwner,
mozilla::dom::BrowsingContext* aBrowsingContext,
const mozilla::dom::RemotenessOptions& aOptions);
~nsFrameLoader();
@ -400,6 +404,10 @@ class nsFrameLoader final : public nsStubMutationObserver,
nsresult MaybeCreateDocShell();
nsresult EnsureMessageManager();
nsresult ReallyLoadFrameScripts();
nsDocShell* GetDocShell() const {
return mBrowsingContext ? nsDocShell::Cast(mBrowsingContext->GetDocShell())
: nullptr;
}
// Updates the subdocument position and size. This gets called only
// when we have our own in-process DocShell.
@ -443,7 +451,7 @@ class nsFrameLoader final : public nsStubMutationObserver,
nsresult PopulateUserContextIdFromAttribute(mozilla::OriginAttributes& aAttr);
RefPtr<nsDocShell> mDocShell;
RefPtr<mozilla::dom::BrowsingContext> mBrowsingContext;
nsCOMPtr<nsIURI> mURIToLoad;
nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
nsCOMPtr<nsIContentSecurityPolicy> mCsp;
@ -464,9 +472,6 @@ class nsFrameLoader final : public nsStubMutationObserver,
// a reframe, so that we know not to restore the presentation.
RefPtr<Document> mContainerDocWhileDetached;
// An opener window which should be used when the docshell is created.
nsCOMPtr<nsPIDOMWindowOuter> mOpener;
RefPtr<TabParent> mRemoteBrowser;
uint64_t mChildID;

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

@ -39,6 +39,10 @@ void nsFrameLoaderOwner::ChangeRemoteness(
RefPtr<Element> owner = do_QueryObject(this);
MOZ_ASSERT(owner);
mFrameLoader = nsFrameLoader::Create(owner, aOptions);
if (NS_WARN_IF(!mFrameLoader)) {
return;
}
mFrameLoader->LoadFrame(false);
// Now that we've got a new FrameLoader, we need to reset our

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

@ -2474,6 +2474,7 @@ void nsGlobalWindowOuter::DetachFromDocShell() {
}
mDocShell = nullptr;
mBrowsingContext->ClearDocShell();
if (mFrames) {
mFrames->SetDocShell(nullptr);

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

@ -1723,7 +1723,6 @@ void nsJSContext::EndCycleCollectionCallback(CycleCollectorResults& aResults) {
// static
bool InterSliceGCRunnerFired(TimeStamp aDeadline, void* aData) {
nsJSContext::KillInterSliceGCRunner();
MOZ_ASSERT(sActiveIntersliceGCBudget > 0);
// We use longer budgets when the CC has been locked out but the CC has tried
// to run since that means we may have significant amount garbage to collect
@ -1775,8 +1774,12 @@ bool InterSliceGCRunnerFired(TimeStamp aDeadline, void* aData) {
// static
void GCTimerFired(nsITimer* aTimer, void* aClosure) {
nsJSContext::KillGCTimer();
nsJSContext::KillInterSliceGCRunner();
if (sShuttingDown) {
nsJSContext::KillInterSliceGCRunner();
return;
}
if (sInterSliceGCRunner) {
return;
}
@ -1786,7 +1789,7 @@ void GCTimerFired(nsITimer* aTimer, void* aClosure) {
return InterSliceGCRunnerFired(aDeadline, aClosure);
},
"GCTimerFired::InterSliceGCRunnerFired", NS_INTERSLICE_GC_DELAY,
sActiveIntersliceGCBudget, false, [] { return sShuttingDown; },
sActiveIntersliceGCBudget, true, [] { return sShuttingDown; },
TaskCategory::GarbageCollection);
}
@ -2241,15 +2244,18 @@ static void DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress,
sGCUnnotifiedTotalTime +=
aDesc.lastSliceEnd(aCx) - aDesc.lastSliceStart(aCx);
// Schedule another GC slice if the GC has more work to do.
nsJSContext::KillInterSliceGCRunner();
if (!sShuttingDown && !aDesc.isComplete_) {
if (sShuttingDown || aDesc.isComplete_) {
nsJSContext::KillInterSliceGCRunner();
} else if (!sInterSliceGCRunner) {
// If incremental GC wasn't triggered by GCTimerFired, we may not
// have a runner to ensure all the slices are handled. So, create
// the runner here.
sInterSliceGCRunner = IdleTaskRunner::Create(
[](TimeStamp aDeadline) {
return InterSliceGCRunnerFired(aDeadline, nullptr);
},
"DOMGCSliceCallback::InterSliceGCRunnerFired",
NS_INTERSLICE_GC_DELAY, sActiveIntersliceGCBudget, false,
NS_INTERSLICE_GC_DELAY, sActiveIntersliceGCBudget, true,
[] { return sShuttingDown; }, TaskCategory::GarbageCollection);
}

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

@ -16,6 +16,8 @@ interface BrowsingContext {
readonly attribute BrowsingContext? parent;
readonly attribute BrowsingContext top;
sequence<BrowsingContext> getChildren();
readonly attribute nsIDocShell? docShell;

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

@ -4,6 +4,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
interface nsISupports;
[NoInterfaceObject]
interface JSWindowActor {
[Throws]
@ -22,4 +24,13 @@ JSWindowActorParent implements JSWindowActor;
interface JSWindowActorChild {
readonly attribute WindowGlobalChild manager;
};
JSWindowActorChild implements JSWindowActor;
JSWindowActorChild implements JSWindowActor;
// WebIDL callback interface version of the nsIObserver interface for use when
// calling the observe method on JSWindowActors.
//
// NOTE: This isn't marked as ChromeOnly, as it has no interface object, and
// thus cannot be conditionally exposed.
callback interface MozObserverCallback {
void observe(nsISupports subject, ByteString topic, DOMString? data);
};

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

@ -109,12 +109,8 @@ BrowsingContext* nsGenericHTMLFrameElement::GetContentWindowInternal() {
return nullptr;
}
RefPtr<nsDocShell> doc_shell = mFrameLoader->GetDocShell(IgnoreErrors());
if (!doc_shell) {
return nullptr;
}
return doc_shell->GetBrowsingContext();
RefPtr<BrowsingContext> bc = mFrameLoader->GetBrowsingContext();
return bc;
}
Nullable<WindowProxyHolder> nsGenericHTMLFrameElement::GetContentWindow() {
@ -133,9 +129,7 @@ void nsGenericHTMLFrameElement::EnsureFrameLoader() {
// Strangely enough, this method doesn't actually ensure that the
// frameloader exists. It's more of a best-effort kind of thing.
mFrameLoader = nsFrameLoader::Create(
this, mOpenerWindow ? mOpenerWindow->GetDOMWindow() : nullptr,
mNetworkCreated);
mFrameLoader = nsFrameLoader::Create(this, mOpenerWindow, mNetworkCreated);
}
nsresult nsGenericHTMLFrameElement::CreateRemoteFrameLoader(

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

@ -1087,6 +1087,7 @@ mozilla::ipc::IPCResult ContentParent::RecvLaunchRDDProcess(
/*static*/
TabParent* ContentParent::CreateBrowser(const TabContext& aContext,
Element* aFrameElement,
BrowsingContext* aBrowsingContext,
ContentParent* aOpenerContentParent,
TabParent* aSameTabGroupAs,
uint64_t aNextTabParentId) {
@ -1147,16 +1148,9 @@ TabParent* ContentParent::CreateBrowser(const TabContext& aContext,
}
}
// FIXME: This BrowsingContext should be provided by the nsFrameLoader.
// (bug 1523636)
RefPtr<CanonicalBrowsingContext> browsingContext =
BrowsingContext::Create(nullptr, nullptr, EmptyString(),
BrowsingContext::Type::Content)
.downcast<CanonicalBrowsingContext>();
// Ensure that our content process is subscribed to our newly created
// BrowsingContextGroup.
browsingContext->Group()->EnsureSubscribed(constructorSender);
aBrowsingContext->Group()->EnsureSubscribed(constructorSender);
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
cpm->RegisterRemoteFrame(tabId, ContentParentId(0), openerTabId,
@ -1188,17 +1182,19 @@ TabParent* ContentParent::CreateBrowser(const TabContext& aContext,
if (tabId == 0) {
return nullptr;
}
RefPtr<TabParent> tp = new TabParent(constructorSender, tabId, aContext,
browsingContext, chromeFlags);
RefPtr<TabParent> tp =
new TabParent(constructorSender, tabId, aContext,
aBrowsingContext->Canonical(), chromeFlags);
browsingContext->SetOwnerProcessId(constructorSender->ChildID());
aBrowsingContext->Canonical()->SetOwnerProcessId(
constructorSender->ChildID());
PBrowserParent* browser = constructorSender->SendPBrowserConstructor(
// DeallocPBrowserParent() releases this ref.
tp.forget().take(), tabId,
aSameTabGroupAs ? aSameTabGroupAs->GetTabId() : TabId(0),
aContext.AsIPCTabContext(), chromeFlags, constructorSender->ChildID(),
browsingContext, constructorSender->IsForBrowser());
aBrowsingContext, constructorSender->IsForBrowser());
if (remoteType.EqualsLiteral(LARGE_ALLOCATION_REMOTE_TYPE)) {
// Tell the TabChild object that it was created due to a Large-Allocation
@ -5711,6 +5707,17 @@ mozilla::ipc::IPCResult ContentParent::RecvAttachBrowsingContext(
child->Attach(/* aFromIPC */ true);
for (auto iter = child->Group()->ContentParentsIter(); !iter.Done();
iter.Next()) {
nsRefPtrHashKey<ContentParent>* entry = iter.Get();
if (entry->GetKey() == this) {
continue;
}
Unused << entry->GetKey()->SendAttachBrowsingContext(
child->GetIPCInitializer());
}
return IPC_OK();
}

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

@ -208,6 +208,7 @@ class ContentParent final : public PContentParent,
*/
static TabParent* CreateBrowser(const TabContext& aContext,
Element* aFrameElement,
BrowsingContext* aBrowsingContext,
ContentParent* aOpenerContentParent,
TabParent* aSameTabGroupAs,
uint64_t aNextTabParentId);

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

@ -191,6 +191,7 @@ struct PerformanceInfo
struct WindowGlobalInit
{
nsIPrincipal principal;
nsIURI documentURI;
BrowsingContext browsingContext;
uint64_t innerWindowId;
uint64_t outerWindowId;

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

@ -7,8 +7,10 @@
#include "mozilla/dom/JSWindowActorService.h"
#include "mozilla/dom/ChromeUtilsBinding.h"
#include "mozilla/dom/EventListenerBinding.h"
#include "mozilla/dom/EventTargetBinding.h"
#include "mozilla/dom/EventTarget.h"
#include "mozilla/dom/JSWindowActorBinding.h"
#include "mozilla/dom/PContent.h"
#include "mozilla/StaticPtr.h"
#include "mozJSComponentLoader.h"
@ -21,66 +23,6 @@ namespace {
StaticRefPtr<JSWindowActorService> gJSWindowActorService;
}
/**
* Helper for calling a named method on a JS Window Actor object with a single
* parameter.
*
* It will do the following:
* 1. Enter the actor object's compartment.
* 2. Convert the given parameter into a JS parameter with ToJSValue.
* 3. Call the named method, passing the single parameter.
* 4. Place the return value in aRetVal.
*
* If an error occurs during this process, this method clears any pending
* exceptions, and returns a nsresult.
*/
template <typename T>
nsresult CallJSActorMethod(nsWrapperCache* aActor, const char* aName,
T& aNativeArg, JS::MutableHandleValue aRetVal) {
// FIXME(nika): We should avoid atomizing and interning the |aName| strings
// every time we do this call. Given the limited set of possible IDs, it would
// be better to cache the `jsid` values.
aRetVal.setUndefined();
// Get the wrapper for our actor. If we don't have a wrapper, the target
// method won't be defined on it. so there's no reason to continue.
JS::Rooted<JSObject*> actor(RootingCx(), aActor->GetWrapper());
if (NS_WARN_IF(!actor)) {
return NS_ERROR_NOT_IMPLEMENTED;
}
// Enter the realm of our actor object to begin running script.
AutoEntryScript aes(actor, "CallJSActorMethod");
JSContext* cx = aes.cx();
JSAutoRealm ar(cx, actor);
// Get the method we want to call, and produce NS_ERROR_NOT_IMPLEMENTED if
// it is not present.
JS::Rooted<JS::Value> func(cx);
if (NS_WARN_IF(!JS_GetProperty(cx, actor, aName, &func) ||
func.isPrimitive())) {
JS_ClearPendingException(cx);
return NS_ERROR_NOT_IMPLEMENTED;
}
// Convert the native argument to a JS value.
JS::Rooted<JS::Value> argv(cx);
if (NS_WARN_IF(!ToJSValue(cx, aNativeArg, &argv))) {
JS_ClearPendingException(cx);
return NS_ERROR_FAILURE;
}
// Call our method.
if (NS_WARN_IF(!JS_CallFunctionValue(cx, actor, func,
JS::HandleValueArray(argv), aRetVal))) {
JS_ClearPendingException(cx);
return NS_ERROR_FAILURE;
}
return NS_OK;
}
/**
* Object corresponding to a single actor protocol. This object acts as an
* Event listener for the actor which is called for events which would
@ -300,9 +242,13 @@ NS_IMETHODIMP JSWindowActorProtocol::HandleEvent(Event* aEvent) {
return rv;
}
// Call the "handleEvent" method on our actor.
JS::Rooted<JS::Value> dummy(RootingCx());
return CallJSActorMethod(actor, "handleEvent", aEvent, &dummy);
// Build our event listener & call it.
JS::Rooted<JSObject*> global(RootingCx(),
JS::GetNonCCWObjectGlobal(actor->GetWrapper()));
RefPtr<EventListener> eventListener =
new EventListener(actor->GetWrapper(), global, nullptr, nullptr);
eventListener->HandleEvent(*aEvent, "JSWindowActorProtocol::HandleEvent");
return NS_OK;
}
NS_IMETHODIMP JSWindowActorProtocol::Observe(nsISupports* aSubject,
@ -331,44 +277,13 @@ NS_IMETHODIMP JSWindowActorProtocol::Observe(nsISupports* aSubject,
return rv;
}
// Get the wrapper for our actor. If we don't have a wrapper, the target
// method won't be defined on it. so there's no reason to continue.
JS::Rooted<JSObject*> obj(RootingCx(), actor->GetWrapper());
if (NS_WARN_IF(!obj)) {
return NS_ERROR_NOT_IMPLEMENTED;
}
// Enter the realm of our actor object to begin running script.
AutoEntryScript aes(obj, "JSWindowActorProtocol::Observe");
JSContext* cx = aes.cx();
JSAutoRealm ar(cx, obj);
JS::AutoValueArray<3> argv(cx);
if (NS_WARN_IF(
!ToJSValue(cx, aSubject, argv[0]) ||
!NonVoidByteStringToJsval(cx, nsDependentCString(aTopic), argv[1]))) {
JS_ClearPendingException(cx);
return NS_ERROR_FAILURE;
}
// aData is an optional parameter.
if (aData) {
if (NS_WARN_IF(!ToJSValue(cx, nsDependentString(aData), argv[2]))) {
JS_ClearPendingException(cx);
return NS_ERROR_FAILURE;
}
} else {
argv[2].setNull();
}
// Call the "observe" method on our actor.
JS::Rooted<JS::Value> dummy(cx);
if (NS_WARN_IF(!JS_CallFunctionName(cx, obj, "observe",
JS::HandleValueArray(argv), &dummy))) {
JS_ClearPendingException(cx);
return NS_ERROR_FAILURE;
}
// Build a observer callback.
JS::Rooted<JSObject*> global(RootingCx(),
JS::GetNonCCWObjectGlobal(actor->GetWrapper()));
RefPtr<MozObserverCallback> observerCallback =
new MozObserverCallback(actor->GetWrapper(), global, nullptr, nullptr);
observerCallback->Observe(aSubject, nsDependentCString(aTopic),
aData ? nsDependentString(aData) : VoidString());
return NS_OK;
}
@ -657,18 +572,13 @@ void JSWindowActorService::ReceiveMessage(nsISupports* aTarget,
argument.mJson = json;
argument.mSync = false;
JS::RootedValue argv(cx);
if (NS_WARN_IF(!ToJSValue(cx, argument, &argv))) {
return;
}
JS::Rooted<JSObject*> global(cx, JS::GetNonCCWObjectGlobal(aObj));
RefPtr<MessageListener> messageListener =
new MessageListener(aObj, global, nullptr, nullptr);
// Now that we have finished, call the recvAsyncMessage callback.
JS::RootedValue dummy(cx);
if (NS_WARN_IF(!JS_CallFunctionName(cx, aObj, "recvAsyncMessage",
JS::HandleValueArray(argv), &dummy))) {
JS_ClearPendingException(cx);
return;
}
JS::Rooted<JS::Value> dummy(cx);
messageListener->ReceiveMessage(argument, &dummy,
"JSWindowActorService::ReceiveMessage");
}
void JSWindowActorService::RegisterWindowRoot(EventTarget* aRoot) {
@ -681,8 +591,8 @@ void JSWindowActorService::RegisterWindowRoot(EventTarget* aRoot) {
}
}
/* static */ void JSWindowActorService::UnregisterWindowRoot(
EventTarget* aRoot) {
/* static */
void JSWindowActorService::UnregisterWindowRoot(EventTarget* aRoot) {
if (gJSWindowActorService) {
// NOTE: No need to unregister listeners here, as the root is going away.
gJSWindowActorService->mRoots.RemoveElement(aRoot);

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

@ -60,8 +60,15 @@ already_AddRefed<WindowGlobalChild> WindowGlobalChild::Create(
RefPtr<WindowGlobalChild> wgc = new WindowGlobalChild(aWindow, bc);
WindowGlobalInit init(principal, bc, wgc->mInnerWindowId,
wgc->mOuterWindowId);
// If we have already closed our browsing context, return a pre-closed
// WindowGlobalChild actor.
if (bc->GetClosed()) {
wgc->ActorDestroy(FailedConstructor);
return wgc.forget();
}
WindowGlobalInit init(principal, aWindow->GetDocumentURI(), bc,
wgc->mInnerWindowId, wgc->mOuterWindowId);
// Send the link constructor over PInProcessChild or PBrowser.
if (XRE_IsParentProcess()) {
@ -91,8 +98,6 @@ already_AddRefed<WindowGlobalChild> WindowGlobalChild::Create(
MOZ_RELEASE_ASSERT(!entry, "Duplicate WindowGlobalChild entry for ID!");
entry.OrInsert([&] { return wgc; });
// Send down our initial document URI.
wgc->SendUpdateDocumentURI(aWindow->GetDocumentURI());
return wgc.forget();
}

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

@ -41,6 +41,7 @@ static StaticAutoPtr<WGPByIdMap> gWindowGlobalParentsById;
WindowGlobalParent::WindowGlobalParent(const WindowGlobalInit& aInit,
bool aInProcess)
: mDocumentPrincipal(aInit.principal()),
mDocumentURI(aInit.documentURI()),
mInnerWindowId(aInit.innerWindowId()),
mOuterWindowId(aInit.outerWindowId()),
mInProcess(aInProcess),

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

@ -47,6 +47,9 @@ add_task(async function() {
let frameLoader = SpecialPowers.wrap(iframe).frameLoader;
is(frameLoader.docShell, null);
is(frameLoader.tabParent, null);
let browsingContext = frameLoader.browsingContext;
isnot(browsingContext, null);
is(browsingContext.docShell, null);
await contentCreated;

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

@ -11,6 +11,7 @@
#include "nsIPermissionManager.h"
#include "PermissionObserver.h"
#include "PermissionUtils.h"
#include "nsPermission.h"
namespace mozilla {
namespace dom {
@ -96,8 +97,7 @@ already_AddRefed<nsIPrincipal> PermissionStatus::GetPrincipal() const {
}
nsCOMPtr<nsIPrincipal> principal =
mozilla::BasePrincipal::Cast(doc->NodePrincipal())
->CloneStrippingUserContextIdAndFirstPartyDomain();
nsPermission::ClonePrincipalForPermission(doc->NodePrincipal());
NS_ENSURE_TRUE(principal, nullptr);
return principal.forget();

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

@ -19,6 +19,10 @@ UNIFIED_SOURCES += [
'PermissionUtils.cpp',
]
LOCAL_INCLUDES += [
'/extensions/cookie',
]
MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
FINAL_LIBRARY = 'xul'

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

@ -415,6 +415,17 @@ void PrototypeDocumentContentSink::CloseElement(Element* aElement) {
}
nsresult PrototypeDocumentContentSink::ResumeWalk() {
nsresult rv = ResumeWalkInternal();
if (NS_FAILED(rv)) {
nsContentUtils::ReportToConsoleNonLocalized(
NS_LITERAL_STRING("Failed to load document from prototype document."),
nsIScriptError::errorFlag, NS_LITERAL_CSTRING("Prototype Document"),
mDocument, mDocumentURI);
}
return rv;
}
nsresult PrototypeDocumentContentSink::ResumeWalkInternal() {
MOZ_ASSERT(mStillWalking);
// Walk the prototype and build the delegate content model. The
// walk is performed in a top-down, left-to-right fashion. That

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

@ -183,11 +183,17 @@ class PrototypeDocumentContentSink final : public nsIStreamLoaderObserver,
nsresult CreateAndInsertPI(const nsXULPrototypePI* aProtoPI);
nsresult ExecuteScript(nsXULPrototypeScript* aScript);
nsresult LoadScript(nsXULPrototypeScript* aScriptProto, bool* aBlock);
/**
* A wrapper around ResumeWalkInternal to report walking errors.
*/
nsresult ResumeWalk();
/**
* Resume (or initiate) an interrupted (or newly prepared)
* prototype walk.
*/
nsresult ResumeWalk();
nsresult ResumeWalkInternal();
/**
* Called at the end of ResumeWalk(), from StyleSheetLoaded(),

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

@ -9,7 +9,6 @@
#include <algorithm>
#include "nsThreadUtils.h"
#include "ipc/IPCMessageUtils.h"
namespace mozilla {
@ -82,6 +81,10 @@ WebBrowserPersistSerializeChild::Write(const char* aBuf, uint32_t aCount,
// refcounting.
MOZ_RELEASE_ASSERT(NS_IsMainThread(), "Fix this class to be thread-safe.");
// Limit the message size to 64k because large messages are
// potentially bad for the latency of other messages on the same channel.
static const uint32_t kMaxWrite = 65536;
// Work around bug 1181433 by sending multiple messages if
// necessary to write the entire aCount bytes, even though
// nsIOutputStream.idl says we're allowed to do a short write.
@ -89,7 +92,7 @@ WebBrowserPersistSerializeChild::Write(const char* aBuf, uint32_t aCount,
uint32_t count = aCount;
*aWritten = 0;
while (count > 0) {
uint32_t toWrite = std::min(IPC::MAX_MESSAGE_SIZE, count);
uint32_t toWrite = std::min(kMaxWrite, count);
nsTArray<uint8_t> arrayBuf;
// It would be nice if this extra copy could be avoided.
arrayBuf.AppendElements(buf, toWrite);

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

@ -92,8 +92,7 @@ void XULFrameElement::LoadSrc() {
// session history handling works like dynamic html:iframes.
// Usually xul elements are used in chrome, which doesn't have
// session history at all.
mFrameLoader = nsFrameLoader::Create(
this, opener ? opener->GetDOMWindow() : nullptr, false);
mFrameLoader = nsFrameLoader::Create(this, opener, false);
if (NS_WARN_IF(!mFrameLoader)) {
return;
}

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

@ -23,16 +23,34 @@ nsPermission::nsPermission(nsIPrincipal* aPrincipal, const nsACString& aType,
mExpireType(aExpireType),
mExpireTime(aExpireTime) {}
already_AddRefed<nsIPrincipal> nsPermission::ClonePrincipalForPermission(
nsIPrincipal* aPrincipal) {
MOZ_ASSERT(aPrincipal);
mozilla::OriginAttributes attrs = aPrincipal->OriginAttributesRef();
attrs.StripAttributes(mozilla::OriginAttributes::STRIP_USER_CONTEXT_ID |
mozilla::OriginAttributes::STRIP_FIRST_PARTY_DOMAIN);
nsAutoCString originNoSuffix;
nsresult rv = aPrincipal->GetOriginNoSuffix(originNoSuffix);
NS_ENSURE_SUCCESS(rv, nullptr);
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
NS_ENSURE_SUCCESS(rv, nullptr);
return mozilla::BasePrincipal::CreateCodebasePrincipal(uri, attrs);
}
already_AddRefed<nsPermission> nsPermission::Create(nsIPrincipal* aPrincipal,
const nsACString& aType,
uint32_t aCapability,
uint32_t aExpireType,
int64_t aExpireTime) {
NS_ENSURE_TRUE(aPrincipal, nullptr);
nsCOMPtr<nsIPrincipal> principal =
mozilla::BasePrincipal::Cast(aPrincipal)
->CloneStrippingUserContextIdAndFirstPartyDomain();
nsCOMPtr<nsIPrincipal> principal =
nsPermission::ClonePrincipalForPermission(aPrincipal);
NS_ENSURE_TRUE(principal, nullptr);
RefPtr<nsPermission> permission =
@ -80,9 +98,7 @@ nsPermission::Matches(nsIPrincipal* aPrincipal, bool aExactHost,
*aMatches = false;
nsCOMPtr<nsIPrincipal> principal =
mozilla::BasePrincipal::Cast(aPrincipal)
->CloneStrippingUserContextIdAndFirstPartyDomain();
nsPermission::ClonePrincipalForPermission(aPrincipal);
if (!principal) {
*aMatches = false;
return NS_OK;

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

@ -23,6 +23,12 @@ class nsPermission : public nsIPermission {
uint32_t aExpireType,
int64_t aExpireTime);
// This method creates a new nsIPrincipal with a stripped OriginAttributes (no
// userContextId, and no FirstPartyDomain) and a codebase equal to the origin
// of 'aPrincipal'.
static already_AddRefed<nsIPrincipal> ClonePrincipalForPermission(
nsIPrincipal* aPrincipal);
protected:
nsPermission(nsIPrincipal* aPrincipal, const nsACString& aType,
uint32_t aCapability, uint32_t aExpireType, int64_t aExpireTime);

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

@ -0,0 +1,103 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Hit-testing an iframe covered by an element with a clip-path</title>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<meta name="viewport" content="width=device-width"/>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<style>
html, body { margin: 0; }
#clipped {
width: 400px;
height: 400px;
background-color: green;
position: absolute;
top: 100px;
left: 100px;
clip-path: circle(150px);
}
iframe {
width: 400px;
height: 300px;
border: 0px solid black;
}
</style>
</head>
<body style="height: 5000px">
<iframe id="sub" srcdoc="<!DOCTYPE html><body style='height: 5000px'><div style='position: absolute; top: 150px; left: 150px; width: 300px; height: 300px; background-color: blue;'></div>
when this page loads, the blue rect should be behind the green circle. mousing over the area with the blue rect and scrolling with the wheel or trackpad should cause the iframe to scroll."></iframe>
<div id="clipped"></div>
<script>
function* test(testDriver) {
var config = getHitTestConfig();
var utils = config.utils;
// layerize the iframe
var subwindow = document.getElementById("sub").contentWindow;
var subscroller = subwindow.document.scrollingElement;
var subutils = SpecialPowers.getDOMWindowUtils(subwindow);
subutils.setDisplayPortForElement(0, 0, 400, 1000, subscroller, 1);
yield waitForApzFlushedRepaints(testDriver);
var rootViewId = utils.getViewId(document.scrollingElement);
var iframeViewId = subutils.getViewId(subscroller);
checkHitResult(hitTest({ x: 10, y: 10 }),
APZHitResultFlags.VISIBLE,
iframeViewId,
"(simple) uninteresting point inside the iframe");
checkHitResult(hitTest({ x: 500, y: 10 }),
APZHitResultFlags.VISIBLE,
rootViewId,
"(simple) uninteresting point in the root scroller");
checkHitResult(hitTest({ x: 110, y: 110 }),
APZHitResultFlags.VISIBLE,
iframeViewId,
"(simple) point in the iframe behind overlaying div, but outside the bounding box of the clip path");
checkHitResult(hitTest({ x: 160, y: 160 }),
config.isWebRender ? APZHitResultFlags.VISIBLE
: APZHitResultFlags.VISIBLE | APZHitResultFlags.IRREGULAR_AREA,
config.isWebRender ? iframeViewId : rootViewId,
"(simple) point in the iframe behind overlaying div, inside the bounding box of the clip path, but outside the actual clip shape");
checkHitResult(hitTest({ x: 300, y: 200 }),
config.isWebRender ? APZHitResultFlags.VISIBLE
: APZHitResultFlags.VISIBLE | APZHitResultFlags.IRREGULAR_AREA,
rootViewId,
"(simple) point inside the clip shape of the overlaying div");
// Now we turn the "simple" clip-path that WR can handle into a more complex
// one that needs a mask. Then run the checks again; the expected results for
// WR are slightly different
document.getElementById("clipped").style.clipPath = "polygon(50px 200px, 75px 75px, 200px 50px, 350px 200px, 200px 350px)";
yield waitForApzFlushedRepaints(testDriver);
checkHitResult(hitTest({ x: 10, y: 10 }),
APZHitResultFlags.VISIBLE,
iframeViewId,
"(complex) uninteresting point inside the iframe");
checkHitResult(hitTest({ x: 500, y: 10 }),
APZHitResultFlags.VISIBLE,
rootViewId,
"(complex) uninteresting point in the root scroller");
checkHitResult(hitTest({ x: 110, y: 110 }),
APZHitResultFlags.VISIBLE,
iframeViewId,
"(complex) point in the iframe behind overlaying div, but outside the bounding box of the clip path");
checkHitResult(hitTest({ x: 160, y: 160 }),
APZHitResultFlags.VISIBLE | APZHitResultFlags.IRREGULAR_AREA,
rootViewId,
"(complex) point in the iframe behind overlaying div, inside the bounding box of the clip path, but outside the actual clip shape");
checkHitResult(hitTest({ x: 300, y: 200 }),
APZHitResultFlags.VISIBLE | APZHitResultFlags.IRREGULAR_AREA,
rootViewId,
"(complex) point inside the clip shape of the overlaying div");
}
waitUntilApzStable()
.then(runContinuation(test))
.then(subtestDone);
</script>
</body></html>

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

@ -38,6 +38,7 @@ var subtests = [
{"file": "helper_hittest_sticky_bug1478304.html", "prefs": prefs},
{"file": "helper_hittest_clipped_fixed_modal.html", "prefs": prefs},
{"file": "helper_hittest_pointerevents_svg.html", "prefs": prefs},
{"file": "helper_hittest_clippath.html", "prefs": prefs},
];
if (isApzEnabled()) {

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

@ -2529,32 +2529,29 @@ impl PicturePrimitive {
};
let world_rect = world_rect.cast();
match transform.transform_kind() {
TransformedRectKind::AxisAligned => {
let inv_transform = transforms
.get_world_inv_transform(prim_instance.spatial_node_index);
let polygon = Polygon::from_transformed_rect_with_inverse(
if transform.is_simple_translation() {
let inv_transform = transforms
.get_world_inv_transform(prim_instance.spatial_node_index);
let polygon = Polygon::from_transformed_rect_with_inverse(
local_rect,
&matrix,
&inv_transform.cast(),
plane_split_anchor,
).unwrap();
splitter.add(polygon);
} else {
let mut clipper = Clipper::new();
let results = clipper.clip_transformed(
Polygon::from_rect(
local_rect,
&matrix,
&inv_transform.cast(),
plane_split_anchor,
).unwrap();
splitter.add(polygon);
}
TransformedRectKind::Complex => {
let mut clipper = Clipper::new();
let results = clipper.clip_transformed(
Polygon::from_rect(
local_rect,
plane_split_anchor,
),
&matrix,
Some(world_rect),
);
if let Ok(results) = results {
for poly in results {
splitter.add(poly);
}
),
&matrix,
Some(world_rect),
);
if let Ok(results) = results {
for poly in results {
splitter.add(poly);
}
}
}

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

@ -0,0 +1,6 @@
---
root:
items:
- type: rect
bounds: [ 0, 0, 200, 44 ]
color: green

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

@ -0,0 +1,10 @@
---
root:
items:
- type: stacking-context
transform-style: preserve-3d
transform: scale(-2, 44, 44727)
items:
- type: rect
bounds: [ -100, -100, 200, 101 ]
color: green

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

@ -38,3 +38,4 @@ platform(linux,mac) fuzzy(1,74) == border-scale-4.yaml border-scale-4.png
!= large-raster-root.yaml blank.yaml
== flatten-preserve-3d-root.yaml flatten-preserve-3d-root-ref.yaml
== strange-w.yaml strange-w-ref.yaml
== big-axis-aligned-scale.yaml big-axis-aligned-scale-ref.yaml

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

@ -103,11 +103,6 @@ struct SerializedStructuredCloneBuffer final {
namespace IPC {
/**
* Maximum size, in bytes, of a single IPC message.
*/
static const uint32_t MAX_MESSAGE_SIZE = 65536;
/**
* Generic enum serializer.
*

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

@ -27,7 +27,7 @@ extern JS::PersistentRootedObject gGlobal;
extern JSContext* gCx;
static int testWasmInit(int* argc, char*** argv) {
if (!wasm::HasSupport(gCx)) {
if (!wasm::HasSupport(gCx) || !InitWebAssemblyClass(gCx, gCx->global())) {
MOZ_CRASH("Failed to initialize wasm support");
}

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

@ -1713,10 +1713,16 @@ struct RunnableWithDelay {
static AutoTArray<RunnableWithDelay, 8>* sPendingIdleRunnables = nullptr;
void nsRefreshDriver::DispatchIdleRunnableAfterTick(nsIRunnable* aRunnable,
uint32_t aDelay) {
void nsRefreshDriver::DispatchIdleRunnableAfterTickUnlessExists(
nsIRunnable* aRunnable, uint32_t aDelay) {
if (!sPendingIdleRunnables) {
sPendingIdleRunnables = new AutoTArray<RunnableWithDelay, 8>();
} else {
for (uint32_t i = 0; i < sPendingIdleRunnables->Length(); ++i) {
if ((*sPendingIdleRunnables)[i].mRunnable == aRunnable) {
return;
}
}
}
RunnableWithDelay rwd = {aRunnable, aDelay};

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

@ -415,8 +415,8 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
*/
static mozilla::Maybe<mozilla::TimeStamp> GetNextTickHint();
static void DispatchIdleRunnableAfterTick(nsIRunnable* aRunnable,
uint32_t aDelay);
static void DispatchIdleRunnableAfterTickUnlessExists(nsIRunnable* aRunnable,
uint32_t aDelay);
static void CancelIdleRunnable(nsIRunnable* aRunnable);
void NotifyDOMContentLoaded();

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

@ -10904,6 +10904,15 @@ CompositorHitTestInfo nsIFrame::GetCompositorHitTestInfo(
// Anything that didn't match the above conditions is visible to hit-testing.
result = CompositorHitTestFlags::eVisibleToHitTest;
if (nsSVGIntegrationUtils::UsingMaskOrClipPathForFrame(this)) {
// If WebRender is enabled, simple clip-paths can be converted into WR
// clips that WR knows how to hit-test against, so we don't need to mark
// it as an irregular area.
if (!gfxVars::UseWebRender() ||
!nsSVGIntegrationUtils::UsingSimpleClipPathForFrame(this)) {
result += CompositorHitTestFlags::eIrregularArea;
}
}
if (aBuilder->IsBuildingNonLayerizedScrollbar()) {
// Scrollbars may be painted into a layer below the actual layer they will

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

@ -9532,19 +9532,12 @@ static Maybe<wr::WrClipId> CreateSimpleClipRegion(
nsIFrame* frame = aDisplayItem.Frame();
auto* style = frame->StyleSVGReset();
MOZ_ASSERT(style->HasClipPath() || style->HasMask());
if (style->HasMask()) {
if (!nsSVGIntegrationUtils::UsingSimpleClipPathForFrame(frame)) {
return Nothing();
}
const auto& clipPath = style->mClipPath;
if (clipPath.GetType() != StyleShapeSourceType::Shape) {
return Nothing();
}
const auto& shape = clipPath.BasicShape();
if (shape.GetShapeType() == StyleBasicShapeType::Polygon) {
return Nothing();
}
auto appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
const nsRect refBox =
@ -9602,8 +9595,8 @@ static Maybe<wr::WrClipId> CreateSimpleClipRegion(
// Please don't add more exceptions, try to find a way to define the clip
// without using a mask image.
//
// And if you _really really_ need to add an exception, add it to where
// the polygon check is.
// And if you _really really_ need to add an exception, add it to
// nsSVGIntegrationUtils::UsingSimpleClipPathForFrame
MOZ_ASSERT_UNREACHABLE("Unhandled shape id?");
return Nothing();
}

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

@ -169,6 +169,22 @@ bool nsSVGIntegrationUtils::UsingMaskOrClipPathForFrame(
return style->HasClipPath() || style->HasMask();
}
bool nsSVGIntegrationUtils::UsingSimpleClipPathForFrame(
const nsIFrame* aFrame) {
const nsStyleSVGReset* style = aFrame->StyleSVGReset();
if (!style->HasClipPath() || style->HasMask()) {
return false;
}
const auto& clipPath = style->mClipPath;
if (clipPath.GetType() != StyleShapeSourceType::Shape) {
return false;
}
const auto& shape = clipPath.BasicShape();
return (shape.GetShapeType() != StyleBasicShapeType::Polygon);
}
nsPoint nsSVGIntegrationUtils::GetOffsetToBoundingBox(nsIFrame* aFrame) {
if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
// Do NOT call GetAllInFlowRectsUnion for SVG - it will get the

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

@ -68,6 +68,12 @@ class nsSVGIntegrationUtils final {
*/
static bool UsingMaskOrClipPathForFrame(const nsIFrame* aFrame);
/**
* Returns true if the element has a clippath that is simple enough to
* be represented without a mask in WebRender.
*/
static bool UsingSimpleClipPathForFrame(const nsIFrame* aFrame);
/**
* Returns the size of the union of the border-box rects of all of
* aNonSVGFrame's continuations.

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

@ -350,7 +350,7 @@ JsepSession::Result JsepSessionImpl::CreateOffer(
const JsepOfferOptions& options, std::string* offer) {
mLastError.clear();
if (mState != kJsepStateStable) {
if (mState != kJsepStateStable && mState != kJsepStateHaveLocalOffer) {
JSEP_SET_ERROR("Cannot create offer in state " << GetStateStr(mState));
// Spec doesn't seem to say this is an error. It probably should.
return dom::PCError::InvalidStateError;
@ -642,6 +642,11 @@ JsepSession::Result JsepSessionImpl::SetLocalDescription(
if (sdp.empty()) {
sdp = mGeneratedOffer->ToString();
}
if (mState == kJsepStateHaveLocalOffer) {
// Rollback previous offer before applying the new one.
SetLocalDescription(kJsepSdpRollback, "");
MOZ_ASSERT(mState == kJsepStateStable);
}
break;
case kJsepSdpAnswer:
case kJsepSdpPranswer:
@ -790,6 +795,12 @@ JsepSession::Result JsepSessionImpl::SetRemoteDescription(
<< "\nSDP=\n"
<< sdp);
if (mState == kJsepStateHaveRemoteOffer && type == kJsepSdpOffer) {
// Rollback previous offer before applying the new one.
SetRemoteDescription(kJsepSdpRollback, "");
MOZ_ASSERT(mState == kJsepStateStable);
}
if (type == kJsepSdpRollback) {
if (mState != kJsepStateHaveRemoteOffer) {
JSEP_SET_ERROR("Cannot rollback remote description in "
@ -966,6 +977,7 @@ nsresult JsepSessionImpl::HandleNegotiatedSession(
transceiver->ClearBundleLevel();
transceiver->mSendTrack.SetActive(false);
transceiver->mRecvTrack.SetActive(false);
transceiver->SetCanRecycle();
// Do not clear mLevel yet! That will happen on the next negotiation.
continue;
}
@ -1378,7 +1390,7 @@ JsepTransceiver* JsepSessionImpl::GetTransceiverForMid(const std::string& mid) {
JsepTransceiver* JsepSessionImpl::GetTransceiverForLocal(size_t level) {
if (JsepTransceiver* transceiver = GetTransceiverForLevel(level)) {
if (WasMsectionDisabledLastNegotiation(level) && transceiver->IsStopped() &&
if (transceiver->CanRecycle() &&
transceiver->GetMediaType() != SdpMediaSection::kApplication) {
// Attempt to recycle. If this fails, the old transceiver stays put.
transceiver->Disassociate();
@ -1420,8 +1432,7 @@ JsepTransceiver* JsepSessionImpl::GetTransceiverForRemote(
const SdpMediaSection& msection) {
size_t level = msection.GetLevel();
if (JsepTransceiver* transceiver = GetTransceiverForLevel(level)) {
if (!WasMsectionDisabledLastNegotiation(level) ||
!transceiver->IsStopped()) {
if (!transceiver->CanRecycle()) {
return transceiver;
}
transceiver->Disassociate();
@ -1509,16 +1520,6 @@ nsresult JsepSessionImpl::UpdateTransceiversFromRemoteDescription(
return NS_OK;
}
bool JsepSessionImpl::WasMsectionDisabledLastNegotiation(size_t level) const {
const Sdp* answer(GetAnswer());
if (answer && (level < answer->GetMediaSectionCount())) {
return mSdpHelper.MsectionIsDisabled(answer->GetMediaSection(level));
}
return false;
}
JsepTransceiver* JsepSessionImpl::FindUnassociatedTransceiver(
SdpMediaSection::MediaType type, bool magic) {
// Look through transceivers that are not mapped to an m-section
@ -1542,7 +1543,7 @@ void JsepSessionImpl::RollbackLocalOffer() {
for (size_t i = 0; i < mTransceivers.size(); ++i) {
RefPtr<JsepTransceiver>& transceiver(mTransceivers[i]);
if (i < mOldTransceivers.size()) {
transceiver->Rollback(*mOldTransceivers[i]);
transceiver->Rollback(*mOldTransceivers[i], false);
continue;
}
@ -1550,7 +1551,7 @@ void JsepSessionImpl::RollbackLocalOffer() {
new JsepTransceiver(transceiver->GetMediaType()));
temp->mSendTrack.PopulateCodecs(mSupportedCodecs);
temp->mRecvTrack.PopulateCodecs(mSupportedCodecs);
transceiver->Rollback(*temp);
transceiver->Rollback(*temp, false);
}
mOldTransceivers.clear();
@ -1560,7 +1561,7 @@ void JsepSessionImpl::RollbackRemoteOffer() {
for (size_t i = 0; i < mTransceivers.size(); ++i) {
RefPtr<JsepTransceiver>& transceiver(mTransceivers[i]);
if (i < mOldTransceivers.size()) {
transceiver->Rollback(*mOldTransceivers[i]);
transceiver->Rollback(*mOldTransceivers[i], true);
continue;
}
@ -1574,7 +1575,7 @@ void JsepSessionImpl::RollbackRemoteOffer() {
new JsepTransceiver(transceiver->GetMediaType()));
temp->mSendTrack.PopulateCodecs(mSupportedCodecs);
temp->mRecvTrack.PopulateCodecs(mSupportedCodecs);
transceiver->Rollback(*temp);
transceiver->Rollback(*temp, true);
if (shouldRemove) {
transceiver->Stop();

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

@ -172,7 +172,6 @@ class JsepSessionImpl : public JsepSession {
nsresult ValidateOffer(const Sdp& offer);
nsresult ValidateAnswer(const Sdp& offer, const Sdp& answer);
nsresult UpdateTransceiversFromRemoteDescription(const Sdp& remote);
bool WasMsectionDisabledLastNegotiation(size_t level) const;
JsepTransceiver* GetTransceiverForLevel(size_t level);
JsepTransceiver* GetTransceiverForMid(const std::string& mid);
JsepTransceiver* GetTransceiverForLocal(size_t level);

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

@ -36,7 +36,8 @@ class JsepTransceiver {
mWasCreatedBySetRemote(false),
mStopped(false),
mRemoved(false),
mNegotiated(false) {}
mNegotiated(false),
mCanRecycle(false) {}
// Can't use default copy c'tor because of the refcount members. Ugh.
JsepTransceiver(const JsepTransceiver& orig)
@ -51,17 +52,20 @@ class JsepTransceiver {
mWasCreatedBySetRemote(orig.mWasCreatedBySetRemote),
mStopped(orig.mStopped),
mRemoved(orig.mRemoved),
mNegotiated(orig.mNegotiated) {}
mNegotiated(orig.mNegotiated),
mCanRecycle(orig.mCanRecycle) {}
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(JsepTransceiver);
void Rollback(JsepTransceiver& oldTransceiver) {
void Rollback(JsepTransceiver& oldTransceiver, bool rollbackLevel) {
MOZ_ASSERT(oldTransceiver.GetMediaType() == GetMediaType());
MOZ_ASSERT(!oldTransceiver.IsNegotiated() || !oldTransceiver.HasLevel() ||
!HasLevel() || oldTransceiver.GetLevel() == GetLevel());
mTransport = oldTransceiver.mTransport;
mLevel = oldTransceiver.mLevel;
mBundleLevel = oldTransceiver.mBundleLevel;
if (rollbackLevel) {
mLevel = oldTransceiver.mLevel;
mBundleLevel = oldTransceiver.mBundleLevel;
}
mRecvTrack = oldTransceiver.mRecvTrack;
// stop() caused by a disabled m-section in a remote offer cannot be
@ -153,6 +157,10 @@ class JsepTransceiver {
bool IsNegotiated() const { return mNegotiated; }
void SetCanRecycle() { mCanRecycle = true; }
bool CanRecycle() const { return mCanRecycle; }
// Convenience function
SdpMediaSection::MediaType GetMediaType() const {
MOZ_ASSERT(mRecvTrack.GetMediaType() == mSendTrack.GetMediaType());
@ -187,6 +195,7 @@ class JsepTransceiver {
bool mStopped;
bool mRemoved;
bool mNegotiated;
bool mCanRecycle;
};
} // namespace mozilla

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

@ -32,6 +32,7 @@ import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.KeyEvent;
@ -264,6 +265,9 @@ public class GeckoView extends FrameLayout {
* this before {@link #setSession(GeckoSession)} if there is already an open session
* set for this instance.
*
* Note: this method does not close the session and the session remains active. The
* caller is responsible for calling {@link GeckoSession#close()} when appropriate.
*
* @return The {@link GeckoSession} that was set for this instance. May be null.
*/
@UiThread
@ -427,7 +431,8 @@ public class GeckoView extends FrameLayout {
@Override
public void onAttachedToWindow() {
if (mSession == null) {
setSession(new GeckoSession(), GeckoRuntime.getDefault(getContext()));
Log.w(LOGTAG, "No GeckoSession attached to this GeckoView instance. Call setSession to attach a GeckoSession to this instance.");
return;
}
if (!mSession.isOpen()) {

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

@ -60,6 +60,9 @@ exclude: true
[68.12]: ../GeckoSession.HistoryDelegate.html
[68.13]: ../GeckoSession.html
- [`GeckoView`][65.5] will not create a [`GeckoSession`][65.9] anymore when
attached to a window without a session.
## v67
- Added [`setAutomaticFontSizeAdjustment`][67.2] to
[`GeckoRuntimeSettings`][67.3] for automatically adjusting font size settings

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

@ -6191,34 +6191,33 @@ org.to
edu.to
mil.to
// subTLDs: https://www.nic.tr/forms/eng/policies.pdf
// and: https://www.nic.tr/forms/politikalar.pdf
// Submitted by <mehmetgurevin@gmail.com>
// tr : https://nic.tr/
// https://nic.tr/forms/eng/policies.pdf
// https://nic.tr/index.php?USRACTN=PRICELST
tr
com.tr
info.tr
biz.tr
net.tr
org.tr
web.tr
gen.tr
tv.tr
av.tr
dr.tr
bbs.tr
name.tr
tel.tr
gov.tr
bel.tr
pol.tr
biz.tr
com.tr
dr.tr
edu.tr
gen.tr
gov.tr
info.tr
mil.tr
k12.tr
edu.tr
kep.tr
name.tr
net.tr
org.tr
pol.tr
tel.tr
tsk.tr
tv.tr
web.tr
// Used by Northern Cyprus
nc.tr
// Used by government agencies of Northern Cyprus
gov.nc.tr
@ -10879,6 +10878,7 @@ browsersafetymark.io
// Bytemark Hosting : https://www.bytemark.co.uk
// Submitted by Paul Cammish <paul.cammish@bytemark.co.uk>
uk0.bigv.io
dh.bytemark.co.uk
vm.bytemark.co.uk
@ -11721,6 +11721,11 @@ githubusercontent.com
// Submitted by Alex Hanselka <alex@gitlab.com>
gitlab.io
// GOV.UK Platform as a Service : https://www.cloud.service.gov.uk/
// Submitted by Tom Whitwell <tom.whitwell@digital.cabinet-office.gov.uk>
cloudapps.digital
london.cloudapps.digital
// UKHomeOffice : https://www.gov.uk/government/organisations/home-office
// Submitted by Jon Shanks <jon.shanks@digital.homeoffice.gov.uk>
homeoffice.gov.uk
@ -12088,6 +12093,10 @@ net.ru
org.ru
pp.ru
// Nabu Casa : https://www.nabucasa.com
// Submitted by Paulus Schoutsen <infra@nabucasa.com>
ui.nabu.casa
// Netlify : https://www.netlify.com
// Submitted by Jessica Parsons <jessica@netlify.com>
bitballoon.com

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

@ -6,8 +6,8 @@
var EXPORTED_SYMBOLS = ["Log"];
const {Preferences} = ChromeUtils.import("resource://gre/modules/Preferences.jsm");
const {Log: StdLog} = ChromeUtils.import("resource://gre/modules/Log.jsm");
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
const LOG_LEVEL = "remote.log.level";
@ -23,6 +23,9 @@ class Log {
}
static get verbose() {
return StdLog.Level[Preferences.get(LOG_LEVEL)] >= StdLog.Level.Info;
// we can't use Preferences.jsm before first paint,
// see ../browser/base/content/test/performance/browser_startup.js
const level = Services.prefs.getStringPref(LOG_LEVEL, "Info");
return StdLog.Level[level] >= StdLog.Level.Info;
}
}

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

@ -46,9 +46,6 @@ remote.jar:
# transport layer
content/server/HTTPD.jsm (../netwerk/test/httpserver/httpd.js)
content/server/Packet.jsm (server/Packet.jsm)
content/server/Socket.jsm (server/Socket.jsm)
content/server/Stream.jsm (server/Stream.jsm)
content/server/Transport.jsm (server/Transport.jsm)
content/server/WebSocket.jsm (server/WebSocket.jsm)
content/server/WebSocketTransport.jsm (server/WebSocketTransport.jsm)

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

@ -1,413 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This is an XPCOM service-ified copy of ../devtools/shared/transport/packets.js.
"use strict",
var EXPORTED_SYMBOLS = [
"RawPacket",
"Packet",
"JSONPacket",
"BulkPacket",
];
/**
* Packets contain read / write functionality for the different packet types
* supported by the debugging protocol, so that a transport can focus on
* delivery and queue management without worrying too much about the specific
* packet types.
*
* They are intended to be "one use only", so a new packet should be
* instantiated for each incoming or outgoing packet.
*
* A complete Packet type should expose at least the following:
* * read(stream, scriptableStream)
* Called when the input stream has data to read
* * write(stream)
* Called when the output stream is ready to write
* * get done()
* Returns true once the packet is done being read / written
* * destroy()
* Called to clean up at the end of use
*/
const {Stream} = ChromeUtils.import("chrome://remote/content/server/Stream.jsm");
const unicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Ci.nsIScriptableUnicodeConverter);
unicodeConverter.charset = "UTF-8";
const defer = function() {
let deferred = {
promise: new Promise((resolve, reject) => {
deferred.resolve = resolve;
deferred.reject = reject;
}),
};
return deferred;
};
// The transport's previous check ensured the header length did not
// exceed 20 characters. Here, we opt for the somewhat smaller, but still
// large limit of 1 TiB.
const PACKET_LENGTH_MAX = Math.pow(2, 40);
/**
* A generic Packet processing object (extended by two subtypes below).
*
* @class
*/
function Packet(transport) {
this._transport = transport;
this._length = 0;
}
/**
* Attempt to initialize a new Packet based on the incoming packet header
* we've received so far. We try each of the types in succession, trying
* JSON packets first since they are much more common.
*
* @param {string} header
* Packet header string to attempt parsing.
* @param {DebuggerTransport} transport
* Transport instance that will own the packet.
*
* @return {Packet}
* Parsed packet of the matching type, or null if no types matched.
*/
Packet.fromHeader = function(header, transport) {
return JSONPacket.fromHeader(header, transport) ||
BulkPacket.fromHeader(header, transport);
};
Packet.prototype = {
get length() {
return this._length;
},
set length(length) {
if (length > PACKET_LENGTH_MAX) {
throw new Error("Packet length " + length +
" exceeds the max length of " + PACKET_LENGTH_MAX);
}
this._length = length;
},
destroy() {
this._transport = null;
},
};
/**
* With a JSON packet (the typical packet type sent via the transport),
* data is transferred as a JSON packet serialized into a string,
* with the string length prepended to the packet, followed by a colon
* ([length]:[packet]). The contents of the JSON packet are specified in
* the Remote Debugging Protocol specification.
*
* @param {DebuggerTransport} transport
* Transport instance that will own the packet.
*/
function JSONPacket(transport) {
Packet.call(this, transport);
this._data = "";
this._done = false;
}
/**
* Attempt to initialize a new JSONPacket based on the incoming packet
* header we've received so far.
*
* @param {string} header
* Packet header string to attempt parsing.
* @param {DebuggerTransport} transport
* Transport instance that will own the packet.
*
* @return {JSONPacket}
* Parsed packet, or null if it's not a match.
*/
JSONPacket.fromHeader = function(header, transport) {
let match = this.HEADER_PATTERN.exec(header);
if (!match) {
return null;
}
let packet = new JSONPacket(transport);
packet.length = +match[1];
return packet;
};
JSONPacket.HEADER_PATTERN = /^(\d+):$/;
JSONPacket.prototype = Object.create(Packet.prototype);
Object.defineProperty(JSONPacket.prototype, "object", {
/**
* Gets the object (not the serialized string) being read or written.
*/
get() {
return this._object;
},
/**
* Sets the object to be sent when write() is called.
*/
set(object) {
this._object = object;
let data = JSON.stringify(object);
this._data = unicodeConverter.ConvertFromUnicode(data);
this.length = this._data.length;
},
});
JSONPacket.prototype.read = function(stream, scriptableStream) {
// Read in more packet data.
this._readData(stream, scriptableStream);
if (!this.done) {
// Don't have a complete packet yet.
return;
}
let json = this._data;
try {
json = unicodeConverter.ConvertToUnicode(json);
this._object = JSON.parse(json);
} catch (e) {
let msg = "Error parsing incoming packet: " + json + " (" + e +
" - " + e.stack + ")";
console.error(msg);
dump(msg + "\n");
return;
}
this._transport._onJSONObjectReady(this._object);
};
JSONPacket.prototype._readData = function(stream, scriptableStream) {
let bytesToRead = Math.min(
this.length - this._data.length,
stream.available());
this._data += scriptableStream.readBytes(bytesToRead);
this._done = this._data.length === this.length;
};
JSONPacket.prototype.write = function(stream) {
if (this._outgoing === undefined) {
// Format the serialized packet to a buffer
this._outgoing = this.length + ":" + this._data;
}
let written = stream.write(this._outgoing, this._outgoing.length);
this._outgoing = this._outgoing.slice(written);
this._done = !this._outgoing.length;
};
Object.defineProperty(JSONPacket.prototype, "done", {
get() {
return this._done;
},
});
JSONPacket.prototype.toString = function() {
return JSON.stringify(this._object, null, 2);
};
/**
* With a bulk packet, data is transferred by temporarily handing over
* the transport's input or output stream to the application layer for
* writing data directly. This can be much faster for large data sets,
* and avoids various stages of copies and data duplication inherent in
* the JSON packet type. The bulk packet looks like:
*
* bulk [actor] [type] [length]:[data]
*
* The interpretation of the data portion depends on the kind of actor and
* the packet's type. See the Remote Debugging Protocol Stream Transport
* spec for more details.
*
* @param {DebuggerTransport} transport
* Transport instance that will own the packet.
*/
function BulkPacket(transport) {
Packet.call(this, transport);
this._done = false;
this._readyForWriting = defer();
}
/**
* Attempt to initialize a new BulkPacket based on the incoming packet
* header we've received so far.
*
* @param {string} header
* Packet header string to attempt parsing.
* @param {DebuggerTransport} transport
* Transport instance that will own the packet.
*
* @return {BulkPacket}
* Parsed packet, or null if it's not a match.
*/
BulkPacket.fromHeader = function(header, transport) {
let match = this.HEADER_PATTERN.exec(header);
if (!match) {
return null;
}
let packet = new BulkPacket(transport);
packet.header = {
actor: match[1],
type: match[2],
length: +match[3],
};
return packet;
};
BulkPacket.HEADER_PATTERN = /^bulk ([^: ]+) ([^: ]+) (\d+):$/;
BulkPacket.prototype = Object.create(Packet.prototype);
BulkPacket.prototype.read = function(stream) {
// Temporarily pause monitoring of the input stream
this._transport.pauseIncoming();
let deferred = defer();
this._transport._onBulkReadReady({
actor: this.actor,
type: this.type,
length: this.length,
copyTo: (output) => {
let copying = StreamUtils.copyStream(stream, output, this.length);
deferred.resolve(copying);
return copying;
},
stream,
done: deferred,
});
// Await the result of reading from the stream
deferred.promise.then(() => {
this._done = true;
this._transport.resumeIncoming();
}, this._transport.close);
// Ensure this is only done once
this.read = () => {
throw new Error("Tried to read() a BulkPacket's stream multiple times.");
};
};
BulkPacket.prototype.write = function(stream) {
if (this._outgoingHeader === undefined) {
// Format the serialized packet header to a buffer
this._outgoingHeader = "bulk " + this.actor + " " + this.type + " " +
this.length + ":";
}
// Write the header, or whatever's left of it to write.
if (this._outgoingHeader.length) {
let written = stream.write(this._outgoingHeader,
this._outgoingHeader.length);
this._outgoingHeader = this._outgoingHeader.slice(written);
return;
}
// Temporarily pause the monitoring of the output stream
this._transport.pauseOutgoing();
let deferred = defer();
this._readyForWriting.resolve({
copyFrom: (input) => {
let copying = StreamUtils.copyStream(input, stream, this.length);
deferred.resolve(copying);
return copying;
},
stream,
done: deferred,
});
// Await the result of writing to the stream
deferred.promise.then(() => {
this._done = true;
this._transport.resumeOutgoing();
}, this._transport.close);
// Ensure this is only done once
this.write = () => {
throw new Error("Tried to write() a BulkPacket's stream multiple times.");
};
};
Object.defineProperty(BulkPacket.prototype, "streamReadyForWriting", {
get() {
return this._readyForWriting.promise;
},
});
Object.defineProperty(BulkPacket.prototype, "header", {
get() {
return {
actor: this.actor,
type: this.type,
length: this.length,
};
},
set(header) {
this.actor = header.actor;
this.type = header.type;
this.length = header.length;
},
});
Object.defineProperty(BulkPacket.prototype, "done", {
get() {
return this._done;
},
});
BulkPacket.prototype.toString = function() {
return "Bulk: " + JSON.stringify(this.header, null, 2);
};
/**
* RawPacket is used to test the transport's error handling of malformed
* packets, by writing data directly onto the stream.
* @param transport DebuggerTransport
* The transport instance that will own the packet.
* @param data string
* The raw string to send out onto the stream.
*/
function RawPacket(transport, data) {
Packet.call(this, transport);
this._data = data;
this.length = data.length;
this._done = false;
}
RawPacket.prototype = Object.create(Packet.prototype);
RawPacket.prototype.read = function() {
// this has not yet been needed for testing
throw new Error("Not implemented");
};
RawPacket.prototype.write = function(stream) {
let written = stream.write(this._data, this._data.length);
this._data = this._data.slice(written);
this._done = !this._data.length;
};
Object.defineProperty(RawPacket.prototype, "done", {
get() {
return this._done;
},
});

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

@ -1,270 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var EXPORTED_SYMBOLS = [
"ConnectionHandshake",
"SocketListener",
];
// This is an XPCOM-service-ified copy of ../devtools/shared/security/socket.js.
const {EventEmitter} = ChromeUtils.import("resource://gre/modules/EventEmitter.jsm");
const {Log} = ChromeUtils.import("chrome://remote/content/Log.jsm");
const {Preferences} = ChromeUtils.import("resource://gre/modules/Preferences.jsm");
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
DebuggerTransport: "chrome://remote/content/server/Transport.jsm",
WebSocketDebuggerTransport: "chrome://remote/content/server/WebSocketTransport.jsm",
WebSocketServer: "chrome://remote/content/server/WebSocket.jsm",
});
XPCOMUtils.defineLazyGetter(this, "log", Log.get);
XPCOMUtils.defineLazyGetter(this, "nsFile", () => CC("@mozilla.org/file/local;1", "nsIFile", "initWithPath"));
const LOOPBACKS = ["localhost", "127.0.0.1"];
const {KeepWhenOffline, LoopbackOnly} = Ci.nsIServerSocket;
this.SocketListener = class SocketListener {
constructor() {
this.socket = null;
this.network = null;
this.nextConnID = 0;
this.onConnectionCreated = null;
this.onConnectionClose = null;
EventEmitter.decorate(this);
}
get listening() {
return !!this.socket;
}
/**
* @param {String} addr
* [<network>][:<host>][:<port>]
* networks: ws, unix, tcp
*/
listen(addr) {
const [network, host, port] = addr.split(":");
try {
this._listen(network, host, port);
} catch (e) {
this.close();
throw new Error(`Unable to start socket server on ${addr}: ${e.message}`, e);
}
}
_listen(network = "tcp", host = "localhost", port = 0) {
if (typeof network != "string" ||
typeof host != "string" ||
(network != "unix" && typeof port != "number")) {
throw new TypeError();
}
if (network != "unix" && (!Number.isInteger(port) || port < 0)) {
throw new RangeError();
}
if (!SocketListener.Networks.includes(network)) {
throw new Error("Unexpected network: " + network);
}
if (!LOOPBACKS.includes(host)) {
throw new Error("Restricted to listening on loopback devices");
}
const flags = KeepWhenOffline | LoopbackOnly;
const backlog = 4;
this.socket = createSocket();
this.network = network;
switch (this.network) {
case "tcp":
case "ws":
// -1 means kernel-assigned port in Gecko
if (port == 0) {
port = -1;
}
this.socket.initSpecialConnection(port, flags, backlog);
break;
case "unix":
// concrete Unix socket
if (host.startsWith("/")) {
const file = nsFile(host);
if (file.exists()) {
file.remove(false);
}
this.socket.initWithFilename(file, Number.parseInt("666", 8), backlog);
// abstract Unix socket
} else {
this.socket.initWithAbstractAddress(host, backlog);
}
break;
}
this.socket.asyncListen(this);
}
close() {
if (this.socket) {
this.socket.close();
this.socket = null;
}
// TODO(ato): removeSocketListener?
}
get host() {
if (this.socket) {
// TODO(ato): Don't hardcode:
return "localhost";
}
return null;
}
get port() {
if (this.socket) {
return this.socket.port;
}
return null;
}
onAllowedConnection(eventName, transport) {
this.emit("accepted", transport, this);
}
// nsIServerSocketListener implementation
onSocketAccepted(socket, socketTransport) {
const conn = new ConnectionHandshake(this, socketTransport);
conn.once("allowed", this.onAllowedConnection.bind(this));
conn.handle();
}
onStopListening(socket, status) {
dump("onStopListening: " + status + "\n");
}
};
SocketListener.Networks = ["tcp", "unix", "ws"];
/**
* Created by SocketListener for each accepted incoming socket.
* This is a short-lived object used to implement a handshake,
* before the socket transport is handed back to RemoteAgent.
*/
this.ConnectionHandshake = class ConnectionHandshake {
constructor(listener, socketTransport) {
this.listener = listener;
this.socket = socketTransport;
this.transport = null;
this.destroyed = false;
EventEmitter.decorate(this);
}
destructor() {
this.listener = null;
this.socket = null;
this.transport = null;
this.destroyed = true;
}
get address() {
return `${this.host}:${this.port}`;
}
get host() {
return this.socket.host;
}
get port() {
return this.socket.port;
}
async handle() {
try {
await this.createTransport();
this.allow();
} catch (e) {
this.deny(e);
}
}
async createTransport() {
const rx = this.socket.openInputStream(0, 0, 0);
const tx = this.socket.openOutputStream(0, 0, 0);
if (this.listener.network == "ws") {
const so = await WebSocketServer.accept(this.socket, rx, tx);
this.transport = new WebSocketDebuggerTransport(so);
} else {
this.transport = new DebuggerTransport(rx, tx);
}
// This handles early disconnects from clients, primarily for failed TLS negotiation.
// We don't support TLS connections in RDP, but might be useful for future blocklist.
this.transport.hooks = {
onClosed: reason => this.deny(reason),
};
// TODO(ato): Review if this is correct:
this.transport.ready();
}
allow() {
if (this.destroyed) {
return;
}
log.debug(`Accepted connection from ${this.address}`);
this.emit("allowed", this.transport);
this.destructor();
}
deny(result) {
if (this.destroyed) {
return;
}
let err = legibleError(result);
log.warn(`Connection from ${this.address} denied: ${err.message}`, err.stack);
if (this.transport) {
this.transport.hooks = null;
this.transport.close(result);
}
this.socket.close(result);
this.destructor();
}
};
function createSocket() {
return Cc["@mozilla.org/network/server-socket;1"]
.createInstance(Ci.nsIServerSocket);
}
// TODO(ato): Move to separate module
function legibleError(obj) {
if (obj instanceof Ci.nsIException) {
for (const result in Cr) {
if (obj.result == Cr[result]) {
return {
message: result,
stack: obj.location.formattedStack,
};
}
}
return {
message: "nsIException",
stack: obj,
};
} else {
return obj;
}
}

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

@ -1,527 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This is an XPCOM service-ified copy of ../devtools/shared/transport/transport.js.
/* global Pipe, ScriptableInputStream */
"use strict";
var EXPORTED_SYMBOLS = ["DebuggerTransport"];
const CC = Components.Constructor;
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
const {EventEmitter} = ChromeUtils.import("resource://gre/modules/EventEmitter.jsm");
const {Stream} = ChromeUtils.import("chrome://remote/content/server/Stream.jsm");
const {
Packet,
JSONPacket,
BulkPacket,
} = ChromeUtils.import("chrome://remote/content/server/Packet.jsm");
const executeSoon = function(func) {
Services.tm.dispatchToMainThread(func);
};
const flags = {wantVerbose: true, wantLogging: true};
const dumpv =
flags.wantVerbose ?
function(msg) {
dump(msg + "\n");
} :
function() {};
const ScriptableInputStream = CC("@mozilla.org/scriptableinputstream;1",
"nsIScriptableInputStream", "init");
const PACKET_HEADER_MAX = 200;
/**
* An adapter that handles data transfers between the debugger client
* and server. It can work with both nsIPipe and nsIServerSocket
* transports so long as the properly created input and output streams
* are specified. (However, for intra-process connections,
* LocalDebuggerTransport, below, is more efficient than using an nsIPipe
* pair with DebuggerTransport.)
*
* @param {nsIAsyncInputStream} input
* The input stream.
* @param {nsIAsyncOutputStream} output
* The output stream.
*
* Given a DebuggerTransport instance dt:
* 1) Set dt.hooks to a packet handler object (described below).
* 2) Call dt.ready() to begin watching for input packets.
* 3) Call dt.send() / dt.startBulkSend() to send packets.
* 4) Call dt.close() to close the connection, and disengage from
* the event loop.
*
* A packet handler is an object with the following methods:
*
* - onPacket(packet) - called when we have received a complete packet.
* |packet| is the parsed form of the packet --- a JavaScript value, not
* a JSON-syntax string.
*
* - onBulkPacket(packet) - called when we have switched to bulk packet
* receiving mode. |packet| is an object containing:
* * actor: Name of actor that will receive the packet
* * type: Name of actor's method that should be called on receipt
* * length: Size of the data to be read
* * stream: This input stream should only be used directly if you
* can ensure that you will read exactly |length| bytes and
* will not close the stream when reading is complete
* * done: If you use the stream directly (instead of |copyTo|
* below), you must signal completion by resolving/rejecting
* this deferred. If it's rejected, the transport will
* be closed. If an Error is supplied as a rejection value,
* it will be logged via |dump|. If you do use |copyTo|,
* resolving is taken care of for you when copying completes.
* * copyTo: A helper function for getting your data out of the
* stream that meets the stream handling requirements above,
* and has the following signature:
*
* @param nsIAsyncOutputStream {output}
* The stream to copy to.
*
* @return {Promise}
* The promise is resolved when copying completes or
* rejected if any (unexpected) errors occur. This object
* also emits "progress" events for each chunk that is
* copied. See stream-utils.js.
*
* - onClosed(reason) - called when the connection is closed. |reason|
* is an optional nsresult or object, typically passed when the
* transport is closed due to some error in a underlying stream.
*
* See ./packets.js and the Remote Debugging Protocol specification for
* more details on the format of these packets.
*
* @class
*/
function DebuggerTransport(input, output) {
EventEmitter.decorate(this);
this._input = input;
this._scriptableInput = new ScriptableInputStream(input);
this._output = output;
// The current incoming (possibly partial) header, which will determine
// which type of Packet |_incoming| below will become.
this._incomingHeader = "";
// The current incoming Packet object
this._incoming = null;
// A queue of outgoing Packet objects
this._outgoing = [];
this.hooks = null;
this.active = false;
this._incomingEnabled = true;
this._outgoingEnabled = true;
this.close = this.close.bind(this);
}
DebuggerTransport.prototype = {
/**
* Transmit an object as a JSON packet.
*
* This method returns immediately, without waiting for the entire
* packet to be transmitted, registering event handlers as needed to
* transmit the entire packet. Packets are transmitted in the order they
* are passed to this method.
*/
send(object) {
this.emit("send", object);
const packet = new JSONPacket(this);
packet.object = object;
this._outgoing.push(packet);
this._flushOutgoing();
},
/**
* Transmit streaming data via a bulk packet.
*
* This method initiates the bulk send process by queuing up the header
* data. The caller receives eventual access to a stream for writing.
*
* N.B.: Do *not* attempt to close the stream handed to you, as it
* will continue to be used by this transport afterwards. Most users
* should instead use the provided |copyFrom| function instead.
*
* @param {Object} header
* This is modeled after the format of JSON packets above, but does
* not actually contain the data, but is instead just a routing
* header:
*
* - actor: Name of actor that will receive the packet
* - type: Name of actor's method that should be called on receipt
* - length: Size of the data to be sent
*
* @return {Promise}
* The promise will be resolved when you are allowed to write to
* the stream with an object containing:
*
* - stream: This output stream should only be used directly
* if you can ensure that you will write exactly
* |length| bytes and will not close the stream when
* writing is complete.
* - done: If you use the stream directly (instead of
* |copyFrom| below), you must signal completion by
* resolving/rejecting this deferred. If it's
* rejected, the transport will be closed. If an
* Error is supplied as a rejection value, it will
* be logged via |dump|. If you do use |copyFrom|,
* resolving is taken care of for you when copying
* completes.
* - copyFrom: A helper function for getting your data onto the
* stream that meets the stream handling requirements
* above, and has the following signature:
*
* @param {nsIAsyncInputStream} input
* The stream to copy from.
*
* @return {Promise}
* The promise is resolved when copying completes
* or rejected if any (unexpected) errors occur.
* This object also emits "progress" events for
* each chunkthat is copied. See stream-utils.js.
*/
startBulkSend(header) {
this.emit("startbulksend", header);
const packet = new BulkPacket(this);
packet.header = header;
this._outgoing.push(packet);
this._flushOutgoing();
return packet.streamReadyForWriting;
},
/**
* Close the transport.
*
* @param {(nsresult|object)=} reason
* The status code or error message that corresponds to the reason
* for closing the transport (likely because a stream closed
* or failed).
*/
close(reason) {
this.emit("close", reason);
this.active = false;
this._input.close();
this._scriptableInput.close();
this._output.close();
this._destroyIncoming();
this._destroyAllOutgoing();
if (this.hooks) {
this.hooks.onClosed(reason);
this.hooks = null;
}
if (reason) {
dumpv("Transport closed: " + reason);
} else {
dumpv("Transport closed.");
}
},
/**
* The currently outgoing packet (at the top of the queue).
*/
get _currentOutgoing() {
return this._outgoing[0];
},
/**
* Flush data to the outgoing stream. Waits until the output
* stream notifies us that it is ready to be written to (via
* onOutputStreamReady).
*/
_flushOutgoing() {
if (!this._outgoingEnabled || this._outgoing.length === 0) {
return;
}
// If the top of the packet queue has nothing more to send, remove it.
if (this._currentOutgoing.done) {
this._finishCurrentOutgoing();
}
if (this._outgoing.length > 0) {
const threadManager = Cc["@mozilla.org/thread-manager;1"].getService();
this._output.asyncWait(this, 0, 0, threadManager.currentThread);
}
},
/**
* Pause this transport's attempts to write to the output stream.
* This is used when we've temporarily handed off our output stream for
* writing bulk data.
*/
pauseOutgoing() {
this._outgoingEnabled = false;
},
/**
* Resume this transport's attempts to write to the output stream.
*/
resumeOutgoing() {
this._outgoingEnabled = true;
this._flushOutgoing();
},
// nsIOutputStreamCallback
/**
* This is called when the output stream is ready for more data to
* be written. The current outgoing packet will attempt to write some
* amount of data, but may not complete.
*/
onOutputStreamReady(stream) {
if (!this._outgoingEnabled || this._outgoing.length === 0) {
return;
}
try {
this._currentOutgoing.write(stream);
} catch (e) {
if (e.result != Cr.NS_BASE_STREAM_WOULD_BLOCK) {
this.close(e.result);
return;
}
throw e;
}
this._flushOutgoing();
},
/**
* Remove the current outgoing packet from the queue upon completion.
*/
_finishCurrentOutgoing() {
if (this._currentOutgoing) {
this._currentOutgoing.destroy();
this._outgoing.shift();
}
},
/**
* Clear the entire outgoing queue.
*/
_destroyAllOutgoing() {
for (const packet of this._outgoing) {
packet.destroy();
}
this._outgoing = [];
},
/**
* Initialize the input stream for reading. Once this method has been
* called, we watch for packets on the input stream, and pass them to
* the appropriate handlers via this.hooks.
*/
ready() {
this.active = true;
this._waitForIncoming();
},
/**
* Asks the input stream to notify us (via onInputStreamReady) when it is
* ready for reading.
*/
_waitForIncoming() {
if (this._incomingEnabled) {
const threadManager = Cc["@mozilla.org/thread-manager;1"].getService();
this._input.asyncWait(this, 0, 0, threadManager.currentThread);
}
},
/**
* Pause this transport's attempts to read from the input stream.
* This is used when we've temporarily handed off our input stream for
* reading bulk data.
*/
pauseIncoming() {
this._incomingEnabled = false;
},
/**
* Resume this transport's attempts to read from the input stream.
*/
resumeIncoming() {
this._incomingEnabled = true;
this._flushIncoming();
this._waitForIncoming();
},
// nsIInputStreamCallback
/**
* Called when the stream is either readable or closed.
*/
onInputStreamReady(stream) {
try {
while (stream.available() && this._incomingEnabled &&
this._processIncoming(stream, stream.available())) {
// Loop until there is nothing more to process
}
this._waitForIncoming();
} catch (e) {
if (e.result != Cr.NS_BASE_STREAM_WOULD_BLOCK) {
this.close(e.result);
} else {
throw e;
}
}
},
/**
* Process the incoming data. Will create a new currently incoming
* Packet if needed. Tells the incoming Packet to read as much data
* as it can, but reading may not complete. The Packet signals that
* its data is ready for delivery by calling one of this transport's
* _on*Ready methods (see ./packets.js and the _on*Ready methods below).
*
* @return {boolean}
* Whether incoming stream processing should continue for any
* remaining data.
*/
_processIncoming(stream, count) {
dumpv("Data available: " + count);
if (!count) {
dumpv("Nothing to read, skipping");
return false;
}
try {
if (!this._incoming) {
dumpv("Creating a new packet from incoming");
if (!this._readHeader(stream)) {
// Not enough data to read packet type
return false;
}
// Attempt to create a new Packet by trying to parse each possible
// header pattern.
this._incoming = Packet.fromHeader(this._incomingHeader, this);
if (!this._incoming) {
throw new Error("No packet types for header: " +
this._incomingHeader);
}
}
if (!this._incoming.done) {
// We have an incomplete packet, keep reading it.
dumpv("Existing packet incomplete, keep reading");
this._incoming.read(stream, this._scriptableInput);
}
} catch (e) {
dump(`Error reading incoming packet: (${e} - ${e.stack})\n`);
// Now in an invalid state, shut down the transport.
this.close();
return false;
}
if (!this._incoming.done) {
// Still not complete, we'll wait for more data.
dumpv("Packet not done, wait for more");
return true;
}
// Ready for next packet
this._flushIncoming();
return true;
},
/**
* Read as far as we can into the incoming data, attempting to build
* up a complete packet header (which terminates with ":"). We'll only
* read up to PACKET_HEADER_MAX characters.
*
* @return {boolean}
* True if we now have a complete header.
*/
_readHeader() {
const amountToRead = PACKET_HEADER_MAX - this._incomingHeader.length;
this._incomingHeader +=
Stream.delimitedRead(this._scriptableInput, ":", amountToRead);
if (flags.wantVerbose) {
dumpv("Header read: " + this._incomingHeader);
}
if (this._incomingHeader.endsWith(":")) {
if (flags.wantVerbose) {
dumpv("Found packet header successfully: " + this._incomingHeader);
}
return true;
}
if (this._incomingHeader.length >= PACKET_HEADER_MAX) {
throw new Error("Failed to parse packet header!");
}
// Not enough data yet.
return false;
},
/**
* If the incoming packet is done, log it as needed and clear the buffer.
*/
_flushIncoming() {
if (!this._incoming.done) {
return;
}
if (flags.wantLogging) {
dumpv("Got: " + this._incoming);
}
this._destroyIncoming();
},
/**
* Handler triggered by an incoming JSONPacket completing it's |read|
* method. Delivers the packet to this.hooks.onPacket.
*/
_onJSONObjectReady(object) {
executeSoon(() => {
// Ensure the transport is still alive by the time this runs.
if (this.active) {
this.emit("packet", object);
this.hooks.onPacket(object);
}
});
},
/**
* Handler triggered by an incoming BulkPacket entering the |read|
* phase for the stream portion of the packet. Delivers info about the
* incoming streaming data to this.hooks.onBulkPacket. See the main
* comment on the transport at the top of this file for more details.
*/
_onBulkReadReady(...args) {
executeSoon(() => {
// Ensure the transport is still alive by the time this runs.
if (this.active) {
this.emit("bulkpacket", ...args);
this.hooks.onBulkPacket(...args);
}
});
},
/**
* Remove all handlers and references related to the current incoming
* packet, either because it is now complete or because the transport
* is closing.
*/
_destroyIncoming() {
if (this._incoming) {
this._incoming.destroy();
}
this._incomingHeader = "";
this._incoming = null;
},
};

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

@ -1152,4 +1152,4 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
static const int32_t kUnknownId = -1;
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1563452699718000);
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1563798212237000);

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

@ -798,8 +798,7 @@ linux64-asan/debug:
linux64-shippable/opt:
description: "Linux64 Shippable"
# Disabled because bug 1516114 shows up much more often
# use-pgo: true
use-pgo: true
attributes:
shippable: true
enable-full-crashsymbols: true
@ -811,9 +810,7 @@ linux64-shippable/opt:
type: shippable
treeherder:
platform: linux64-shippable/opt
# Bug 1516114
symbol: B
# symbol: Bpgo(B)
symbol: Bpgo(B)
worker-type: aws-provisioner-v1/gecko-{level}-b-linux
worker:
max-run-time: 9000
@ -828,8 +825,7 @@ linux64-shippable/opt:
secrets: true
tooltool-downloads: public
need-xvfb: true
# Bug 1516114
# mozconfig-variant: profile-use
mozconfig-variant: profile-use
toolchains:
- linux64-binutils
- linux64-clang

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

@ -293,6 +293,11 @@ workers:
implementation: script-engine-autophone
os: linux
worker-type: 'gecko-{alias}'
t-bitbar-gw.*:
provisioner: proj-autophone
implementation: generic-worker
os: linux-bitbar
worker-type: 'gecko-{alias}'
t-linux(-large|-xlarge):
provisioner: aws-provisioner-v1
implementation: docker-worker

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

@ -22,7 +22,7 @@ job-defaults:
tier: 1
jobs:
linux64/pgo:
linux64-shippable/opt:
description: "Linux64 Profile Generation"
shipping-phase: build
shipping-product: firefox
@ -30,7 +30,7 @@ jobs:
product: firefox
job-name: linux64-profile
treeherder:
platform: linux64/pgo
platform: linux64-shippable/opt
worker-type: aws-provisioner-v1/gecko-{level}-b-linux
worker:
max-run-time: 1200

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

@ -22,12 +22,12 @@ job-defaults:
tier: 1
jobs:
linux64/pgo:
linux64-shippable/opt:
description: "Linux64 Instrumented"
shipping-phase: build
shipping-product: firefox
treeherder:
platform: linux64/pgo
platform: linux64-shippable/opt
worker-type: aws-provisioner-v1/gecko-{level}-b-linux
worker:
max-run-time: 7200

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

@ -392,11 +392,11 @@
},
"scriptworker": {
"hashes": [
"sha256:6f9d463379c586256f2da51c37015aee2cf4b31a86468324c871f08d2a1ea8c1",
"sha256:bdd30c73c78217c8d923a660366f50fb9a8a1029e409c6bf17be89ff1159fdf7"
"sha256:a6b093387dc35452421119b6313932025de2c6587603136442f36a7bec5d4bf2",
"sha256:dfc14639559a07a2b0883193390e313187d1f9c5107cd102ccbf88806debcb80"
],
"index": "pypi",
"version": "==23.0.0"
"version": "==23.0.2"
},
"sh": {
"hashes": [

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