зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to mozilla-inbound. a=merge
This commit is contained in:
Коммит
a18cb37ca6
|
@ -5547,29 +5547,53 @@ var TabContextMenu = {
|
|||
},
|
||||
reopenInContainer(event) {
|
||||
let userContextId = parseInt(event.target.getAttribute("data-usercontextid"));
|
||||
/* Create a triggering principal that is able to load the new tab
|
||||
For codebase principals that are about: chrome: or resource: we need system to load them.
|
||||
Anything other than system principal needs to have the new userContextId.
|
||||
*/
|
||||
let triggeringPrincipal = this.contextTab.linkedBrowser.contentPrincipal;
|
||||
if (triggeringPrincipal.isNullPrincipal) {
|
||||
triggeringPrincipal = Services.scriptSecurityManager.createNullPrincipal({ userContextId });
|
||||
} else if (triggeringPrincipal.isCodebasePrincipal) {
|
||||
triggeringPrincipal = Services.scriptSecurityManager.createCodebasePrincipal(triggeringPrincipal.URI, { userContextId });
|
||||
}
|
||||
let newTab = gBrowser.addTab(this.contextTab.linkedBrowser.currentURI.spec, {
|
||||
userContextId,
|
||||
pinned: this.contextTab.pinned,
|
||||
index: this.contextTab._tPos + 1,
|
||||
triggeringPrincipal,
|
||||
});
|
||||
let reopenedTabs = this.contextTab.multiselected ? gBrowser.selectedTabs : [this.contextTab];
|
||||
|
||||
if (gBrowser.selectedTab == this.contextTab) {
|
||||
gBrowser.selectedTab = newTab;
|
||||
}
|
||||
if (this.contextTab.muted) {
|
||||
if (!newTab.muted) {
|
||||
newTab.toggleMuteAudio(this.contextTab.muteReason);
|
||||
for (let tab of reopenedTabs) {
|
||||
if (tab.getAttribute("usercontextid") == userContextId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Create a triggering principal that is able to load the new tab
|
||||
For codebase principals that are about: chrome: or resource: we need system to load them.
|
||||
Anything other than system principal needs to have the new userContextId.
|
||||
*/
|
||||
let triggeringPrincipal;
|
||||
|
||||
if (tab.linkedPanel) {
|
||||
triggeringPrincipal = tab.linkedBrowser.contentPrincipal;
|
||||
} else {
|
||||
// For lazy tab browsers, get the original principal
|
||||
// from SessionStore
|
||||
let tabState = JSON.parse(SessionStore.getTabState(tab));
|
||||
try {
|
||||
triggeringPrincipal = Utils.deserializePrincipal(tabState.triggeringPrincipal_base64);
|
||||
} catch (ex) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!triggeringPrincipal || triggeringPrincipal.isNullPrincipal) {
|
||||
// Ensure that we have a null principal if we couldn't
|
||||
// deserialize it (for lazy tab browsers) ...
|
||||
// This won't always work however is safe to use.
|
||||
triggeringPrincipal = Services.scriptSecurityManager.createNullPrincipal({ userContextId });
|
||||
} else if (triggeringPrincipal.isCodebasePrincipal) {
|
||||
triggeringPrincipal = Services.scriptSecurityManager.createCodebasePrincipal(triggeringPrincipal.URI, { userContextId });
|
||||
}
|
||||
|
||||
let newTab = gBrowser.addTab(tab.linkedBrowser.currentURI.spec, {
|
||||
userContextId,
|
||||
pinned: tab.pinned,
|
||||
index: tab._tPos + 1,
|
||||
triggeringPrincipal,
|
||||
});
|
||||
|
||||
if (gBrowser.selectedTab == tab) {
|
||||
gBrowser.selectedTab = newTab;
|
||||
}
|
||||
if (tab.muted && !newTab.muted) {
|
||||
newTab.toggleMuteAudio(tab.muteReason);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -36,6 +36,7 @@ support-files =
|
|||
[browser_multiselect_tabs_pin_unpin.js]
|
||||
[browser_multiselect_tabs_positional_attrs.js]
|
||||
[browser_multiselect_tabs_reload.js]
|
||||
[browser_multiselect_tabs_reopen_in_container.js]
|
||||
[browser_multiselect_tabs_reorder.js]
|
||||
[browser_multiselect_tabs_using_Ctrl.js]
|
||||
[browser_multiselect_tabs_using_keyboard.js]
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
"use strict";
|
||||
|
||||
const PREF_MULTISELECT_TABS = "browser.tabs.multiselect";
|
||||
const PREF_PRIVACY_USER_CONTEXT_ENABLED = "privacy.userContext.enabled";
|
||||
|
||||
async function openTabMenuFor(tab) {
|
||||
let tabMenu = tab.ownerDocument.getElementById("tabContextMenu");
|
||||
|
||||
let tabMenuShown = BrowserTestUtils.waitForEvent(tabMenu, "popupshown");
|
||||
EventUtils.synthesizeMouseAtCenter(tab, {type: "contextmenu"},
|
||||
tab.ownerGlobal);
|
||||
await tabMenuShown;
|
||||
|
||||
return tabMenu;
|
||||
}
|
||||
|
||||
async function openReopenMenuForTab(tab) {
|
||||
openTabMenuFor(tab);
|
||||
|
||||
let reopenItem = tab.ownerDocument.getElementById("context_reopenInContainer");
|
||||
ok(!reopenItem.hidden, "Reopen in Container item should be shown");
|
||||
|
||||
let reopenMenu = reopenItem.getElementsByTagName("menupopup")[0];
|
||||
let reopenMenuShown = BrowserTestUtils.waitForEvent(reopenMenu, "popupshown");
|
||||
EventUtils.synthesizeMouseAtCenter(reopenItem, {type: "mousemove"},
|
||||
tab.ownerGlobal);
|
||||
await reopenMenuShown;
|
||||
|
||||
return reopenMenu;
|
||||
}
|
||||
|
||||
function checkMenuItem(reopenMenu, shown, hidden) {
|
||||
for (let id of shown) {
|
||||
ok(reopenMenu.querySelector(`menuitem[data-usercontextid="${id}"]`),
|
||||
`User context id ${id} should exist`);
|
||||
}
|
||||
for (let id of hidden) {
|
||||
ok(!reopenMenu.querySelector(`menuitem[data-usercontextid="${id}"]`),
|
||||
`User context id ${id} shouldn't exist`);
|
||||
}
|
||||
}
|
||||
|
||||
function openTabInContainer(gBrowser, tab, reopenMenu, id) {
|
||||
let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, getUrl(tab), true);
|
||||
let menuitem = reopenMenu.querySelector(`menuitem[data-usercontextid="${id}"]`);
|
||||
EventUtils.synthesizeMouseAtCenter(menuitem, {}, menuitem.ownerGlobal);
|
||||
return tabPromise;
|
||||
}
|
||||
|
||||
add_task(async function testReopen() {
|
||||
await SpecialPowers.pushPrefEnv({"set": [
|
||||
[PREF_PRIVACY_USER_CONTEXT_ENABLED, true],
|
||||
[PREF_MULTISELECT_TABS, true],
|
||||
]});
|
||||
|
||||
let tab1 = await addTab("http://mochi.test:8888/1");
|
||||
let tab2 = await addTab("http://mochi.test:8888/2");
|
||||
let tab3 = await addTab("http://mochi.test:8888/3");
|
||||
let tab4 = await addTab("http://mochi.test:8888/3", {createLazyBrowser: true});
|
||||
|
||||
await BrowserTestUtils.switchTab(gBrowser, tab1);
|
||||
|
||||
await triggerClickOn(tab2, { ctrlKey: true });
|
||||
await triggerClickOn(tab4, { ctrlKey: true });
|
||||
|
||||
for (let tab of [tab1, tab2, tab3, tab4]) {
|
||||
ok(!tab.hasAttribute("usercontextid"),
|
||||
"Tab with No Container should be opened");
|
||||
}
|
||||
|
||||
ok(tab1.multiselected, "Tab1 is multi-selected");
|
||||
ok(tab2.multiselected, "Tab2 is multi-selected");
|
||||
ok(!tab3.multiselected, "Tab3 is not multi-selected");
|
||||
ok(tab4.multiselected, "Tab4 is multi-selected");
|
||||
|
||||
is(gBrowser.visibleTabs.length, 5, "We have 5 tabs open");
|
||||
|
||||
let reopenMenu1 = await openReopenMenuForTab(tab1);
|
||||
checkMenuItem(reopenMenu1, [1, 2, 3, 4], [0]);
|
||||
let containerTab1 = await openTabInContainer(gBrowser, tab1, reopenMenu1, "1");
|
||||
|
||||
let tabs = gBrowser.visibleTabs;
|
||||
is(tabs.length, 8, "Now we have 8 tabs open");
|
||||
|
||||
is(containerTab1._tPos, 2, "containerTab1 position is 3");
|
||||
is(containerTab1.getAttribute("usercontextid"), "1",
|
||||
"Tab(1) with UCI=1 should be opened");
|
||||
is(getUrl(containerTab1), getUrl(tab1),
|
||||
"Same page (tab1) should be opened");
|
||||
|
||||
let containerTab2 = tabs[4];
|
||||
is(containerTab2.getAttribute("usercontextid"), "1",
|
||||
"Tab(2) with UCI=1 should be opened");
|
||||
await TestUtils.waitForCondition(function() {
|
||||
return getUrl(containerTab2) == getUrl(tab2);
|
||||
}, "Same page (tab2) should be opened");
|
||||
|
||||
let containerTab4 = tabs[7];
|
||||
is(containerTab2.getAttribute("usercontextid"), "1",
|
||||
"Tab(4) with UCI=1 should be opened");
|
||||
await TestUtils.waitForCondition(function() {
|
||||
return getUrl(containerTab4) == getUrl(tab4);
|
||||
}, "Same page (tab4) should be opened");
|
||||
|
||||
for (let tab of tabs.filter(t => t != tabs[0])) {
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
});
|
|
@ -21,8 +21,9 @@ function triggerClickOn(target, options) {
|
|||
return promise;
|
||||
}
|
||||
|
||||
async function addTab(url = "http://mochi.test:8888/") {
|
||||
const tab = BrowserTestUtils.addTab(gBrowser, url, { skipAnimation: true });
|
||||
async function addTab(url = "http://mochi.test:8888/", params = {}) {
|
||||
params.skipAnimation = true;
|
||||
const tab = BrowserTestUtils.addTab(gBrowser, url, params);
|
||||
const browser = gBrowser.getBrowserForTab(tab);
|
||||
await BrowserTestUtils.browserLoaded(browser);
|
||||
return tab;
|
||||
|
|
|
@ -37,22 +37,18 @@
|
|||
</h1>
|
||||
<section class="section-main">
|
||||
<p>&aboutPrivateBrowsing.info.notsaved.before;<strong>&aboutPrivateBrowsing.info.notsaved.emphasize;</strong>&aboutPrivateBrowsing.info.notsaved.after;</p>
|
||||
<div class="list-row">
|
||||
<ul>
|
||||
<li>&aboutPrivateBrowsing.info.visited;</li>
|
||||
<li>&aboutPrivateBrowsing.info.cookies;</li>
|
||||
<li>&aboutPrivateBrowsing.info.searches;</li>
|
||||
<li>&aboutPrivateBrowsing.info.temporaryFiles;</li>
|
||||
</ul>
|
||||
</div>
|
||||
<ul class="list-row">
|
||||
<li>&aboutPrivateBrowsing.info.visited;</li>
|
||||
<li>&aboutPrivateBrowsing.info.cookies;</li>
|
||||
<li>&aboutPrivateBrowsing.info.searches;</li>
|
||||
<li>&aboutPrivateBrowsing.info.temporaryFiles;</li>
|
||||
</ul>
|
||||
<p>&aboutPrivateBrowsing.info.saved.before;<strong>&aboutPrivateBrowsing.info.saved.emphasize;</strong>&aboutPrivateBrowsing.info.saved.after2;</p>
|
||||
<div class="list-row">
|
||||
<ul>
|
||||
<li>&aboutPrivateBrowsing.info.bookmarks;</li>
|
||||
<li>&aboutPrivateBrowsing.info.downloads;</li>
|
||||
<li>&aboutPrivateBrowsing.info.clipboard;</li>
|
||||
</ul>
|
||||
</div>
|
||||
<ul class="list-row">
|
||||
<li>&aboutPrivateBrowsing.info.bookmarks;</li>
|
||||
<li>&aboutPrivateBrowsing.info.downloads;</li>
|
||||
<li>&aboutPrivateBrowsing.info.clipboard;</li>
|
||||
</ul>
|
||||
<p>&aboutPrivateBrowsing.note.before;<strong>&aboutPrivateBrowsing.note.emphasize;</strong>&aboutPrivateBrowsing.note.after;</p>
|
||||
</section>
|
||||
|
||||
|
|
|
@ -204,8 +204,8 @@ let URICountListener = {
|
|||
if (!this.isHttpURI(uri)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldRecordSearchCount(browser.getTabBrowser())) {
|
||||
if (shouldRecordSearchCount(browser.getTabBrowser()) &&
|
||||
!(flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)) {
|
||||
Services.search.recordSearchURLTelemetry(uriSpec);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ html.private {
|
|||
--in-content-page-color: white;
|
||||
--in-content-text-color: white;
|
||||
--in-content-page-background: #25003e;
|
||||
--in-content-link-color-hover: white;
|
||||
--in-content-link-color-active: white;
|
||||
}
|
||||
|
||||
a:link {
|
||||
|
@ -32,22 +34,25 @@ p {
|
|||
}
|
||||
|
||||
.list-row {
|
||||
overflow: auto;
|
||||
display: grid;
|
||||
column-gap: 2em;
|
||||
grid-template-columns: repeat(auto-fill, minmax(10em, 16em));
|
||||
margin-inline-start: 3em;
|
||||
}
|
||||
|
||||
.list-row > ul > li {
|
||||
float: inline-start;
|
||||
width: 16em;
|
||||
line-height: 2em;
|
||||
margin-inline-start: 1em;
|
||||
.list-row li {
|
||||
margin-bottom: 0;
|
||||
padding: .25em 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.title {
|
||||
background-image: url("chrome://browser/skin/privatebrowsing/private-browsing.svg");
|
||||
background-position: left center;
|
||||
background-size: 2em;
|
||||
line-height: 2em;
|
||||
line-height: 1.4;
|
||||
padding-top: 0.3em;
|
||||
padding-bottom: 0.3em;
|
||||
margin-inline-start: calc(-2em - 10px);
|
||||
padding-inline-start: calc(2em + 10px);
|
||||
}
|
||||
|
@ -57,6 +62,7 @@ p {
|
|||
}
|
||||
|
||||
.about-subheader {
|
||||
--icon-size: 1.5em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 1.5em;
|
||||
|
@ -64,22 +70,25 @@ p {
|
|||
background-image: url("chrome://browser/skin/privatebrowsing/tracking-protection.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-position: left center;
|
||||
background-size: 1.5em;
|
||||
background-size: var(--icon-size);
|
||||
line-height: 1.5em;
|
||||
margin-inline-start: calc(-1.5em - 10px);
|
||||
padding-inline-start: calc(1.5em + 10px);
|
||||
margin-inline-start: calc((var(--icon-size) + 10px) * -1);
|
||||
padding-inline-start: calc(var(--icon-size) + 10px);
|
||||
}
|
||||
|
||||
.about-subheader:dir(rtl) {
|
||||
background-position: right;
|
||||
}
|
||||
|
||||
.about-info {
|
||||
font-size: .9em;
|
||||
@media (max-width: 970px) {
|
||||
.about-subheader {
|
||||
--icon-size: 1em;
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#cbTitle {
|
||||
margin-inline-end: 12px;
|
||||
.about-info {
|
||||
font-size: .9em;
|
||||
}
|
||||
|
||||
a.button {
|
||||
|
@ -89,3 +98,8 @@ a.button {
|
|||
text-decoration: none;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
a.button:hover:active {
|
||||
color: inherit;
|
||||
background-color: #6000a1;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import errno
|
|||
import json
|
||||
import os
|
||||
import platform
|
||||
import random
|
||||
import subprocess
|
||||
import sys
|
||||
import uuid
|
||||
|
@ -122,10 +121,6 @@ CATEGORIES = {
|
|||
}
|
||||
|
||||
|
||||
# We submit data to telemetry approximately every this many mach invocations
|
||||
TELEMETRY_SUBMISSION_FREQUENCY = 10
|
||||
|
||||
|
||||
def search_path(mozilla_dir, packages_txt):
|
||||
with open(os.path.join(mozilla_dir, packages_txt)) as f:
|
||||
packages = [line.rstrip().split(':') for line in f]
|
||||
|
@ -278,15 +273,15 @@ def bootstrap(topsrcdir, mozilla_dir=None):
|
|||
if should_skip_telemetry_submission(handler):
|
||||
return True
|
||||
|
||||
# But only submit about every n-th operation
|
||||
if random.randint(1, TELEMETRY_SUBMISSION_FREQUENCY) != 1:
|
||||
return
|
||||
state_dir, _ = get_state_dir()
|
||||
|
||||
machpath = os.path.join(instance.topsrcdir, 'mach')
|
||||
with open(os.devnull, 'wb') as devnull:
|
||||
subprocess.Popen([sys.executable,
|
||||
subprocess.Popen([machpath, 'python',
|
||||
'--no-virtualenv',
|
||||
os.path.join(topsrcdir, 'build',
|
||||
'submit_telemetry_data.py'),
|
||||
get_state_dir()[0]],
|
||||
state_dir],
|
||||
stdout=devnull, stderr=devnull)
|
||||
|
||||
def populate_context(context, key=None):
|
||||
|
|
|
@ -10,19 +10,14 @@ import logging
|
|||
import os
|
||||
import sys
|
||||
|
||||
HERE = os.path.abspath(os.path.dirname(__file__))
|
||||
PYTHIRDPARTY = os.path.join(HERE, '..', 'third_party', 'python')
|
||||
|
||||
# Add some required files to $PATH to ensure they are available
|
||||
sys.path.append(os.path.join(HERE, '..', 'python', 'mozbuild', 'mozbuild'))
|
||||
sys.path.append(os.path.join(PYTHIRDPARTY, 'requests'))
|
||||
sys.path.append(os.path.join(PYTHIRDPARTY, 'voluptuous'))
|
||||
|
||||
import requests
|
||||
import voluptuous
|
||||
import voluptuous.humanize
|
||||
|
||||
from mozbuild.telemetry import schema as build_telemetry_schema
|
||||
from mozbuild.telemetry import (
|
||||
schema as build_telemetry_schema,
|
||||
verify_statedir,
|
||||
)
|
||||
|
||||
BUILD_TELEMETRY_URL = 'https://incoming.telemetry.mozilla.org/{endpoint}'
|
||||
SUBMIT_ENDPOINT = 'submit/eng-workflow/build/1/{ping_uuid}'
|
||||
|
@ -135,35 +130,6 @@ def submit_telemetry_data(outgoing, submitted):
|
|||
return 0
|
||||
|
||||
|
||||
def verify_statedir(statedir):
|
||||
'''Verifies the statedir is structured according to the assumptions of
|
||||
this script
|
||||
|
||||
Requires presence of the following directories; will raise if absent:
|
||||
- statedir/telemetry
|
||||
- statedir/telemetry/outgoing
|
||||
|
||||
Creates the following directories and files if absent (first submission):
|
||||
- statedir/telemetry/submitted
|
||||
'''
|
||||
|
||||
telemetry_dir = os.path.join(statedir, 'telemetry')
|
||||
outgoing = os.path.join(telemetry_dir, 'outgoing')
|
||||
submitted = os.path.join(telemetry_dir, 'submitted')
|
||||
telemetry_log = os.path.join(telemetry_dir, 'telemetry.log')
|
||||
|
||||
if not os.path.isdir(telemetry_dir):
|
||||
raise Exception('{} does not exist'.format(telemetry_dir))
|
||||
|
||||
if not os.path.isdir(outgoing):
|
||||
raise Exception('{} does not exist'.format(outgoing))
|
||||
|
||||
if not os.path.isdir(submitted):
|
||||
os.mkdir(submitted)
|
||||
|
||||
return outgoing, submitted, telemetry_log
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) != 2:
|
||||
print('usage: python submit_telemetry_data.py <statedir>')
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
AUTOCOMPLETE_CLEAR,
|
||||
AUTOCOMPLETE_DATA_RECEIVE,
|
||||
AUTOCOMPLETE_PENDING_REQUEST,
|
||||
AUTOCOMPLETE_RETRIEVE_FROM_CACHE,
|
||||
} = require("devtools/client/webconsole/constants");
|
||||
|
||||
/**
|
||||
* Update the data used for the autocomplete popup in the console input (JsTerm).
|
||||
*
|
||||
* @param {Object} Object of the following shape:
|
||||
* - {String} inputValue: the expression to complete.
|
||||
* - {Int} cursor: The position of the cursor in the inputValue.
|
||||
* - {WebConsoleClient} client: The webconsole client.
|
||||
* - {String} frameActorId: The id of the frame we want to autocomplete in.
|
||||
* - {Boolean} force: True to force a call to the server (as opposed to retrieve
|
||||
* from the cache).
|
||||
*/
|
||||
function autocompleteUpdate({
|
||||
inputValue,
|
||||
cursor,
|
||||
client,
|
||||
frameActorId,
|
||||
force,
|
||||
}) {
|
||||
return ({dispatch, getState}) => {
|
||||
const {cache} = getState().autocomplete;
|
||||
|
||||
if (!force && (
|
||||
!inputValue ||
|
||||
/^[a-zA-Z0-9_$]/.test(inputValue.substring(cursor))
|
||||
)) {
|
||||
return dispatch(autocompleteClear());
|
||||
}
|
||||
|
||||
const input = inputValue.substring(0, cursor);
|
||||
const retrieveFromCache = !force &&
|
||||
cache &&
|
||||
cache.input &&
|
||||
input.startsWith(cache.input) &&
|
||||
/[a-zA-Z0-9]$/.test(input) &&
|
||||
frameActorId === cache.frameActorId;
|
||||
|
||||
if (retrieveFromCache) {
|
||||
return dispatch(autoCompleteDataRetrieveFromCache(input));
|
||||
}
|
||||
|
||||
return dispatch(autocompleteDataFetch({input, frameActorId, client}));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the autocompletion data should be cleared.
|
||||
*/
|
||||
function autocompleteClear() {
|
||||
return {
|
||||
type: AUTOCOMPLETE_CLEAR,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the autocompletion data should be retrieved from the cache (i.e.
|
||||
* client-side).
|
||||
*
|
||||
* @param {String} input: The input used to filter the cached data.
|
||||
*/
|
||||
function autoCompleteDataRetrieveFromCache(input) {
|
||||
return {
|
||||
type: AUTOCOMPLETE_RETRIEVE_FROM_CACHE,
|
||||
input,
|
||||
};
|
||||
}
|
||||
|
||||
let currentRequestId = 0;
|
||||
function generateRequestId() {
|
||||
return currentRequestId++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Action that fetch autocompletion data from the server.
|
||||
*
|
||||
* @param {Object} Object of the following shape:
|
||||
* - {String} input: the expression that we want to complete.
|
||||
* - {String} frameActorId: The id of the frame we want to autocomplete in.
|
||||
* - {WebConsoleClient} client: The webconsole client.
|
||||
*/
|
||||
function autocompleteDataFetch({
|
||||
input,
|
||||
frameActorId,
|
||||
client,
|
||||
}) {
|
||||
return ({dispatch}) => {
|
||||
const id = generateRequestId();
|
||||
dispatch({type: AUTOCOMPLETE_PENDING_REQUEST, id});
|
||||
client.autocomplete(input, undefined, frameActorId).then(res => {
|
||||
dispatch(autocompleteDataReceive(id, input, frameActorId, res));
|
||||
}).catch(e => {
|
||||
console.error("failed autocomplete", e);
|
||||
dispatch(autocompleteClear());
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when we receive the autocompletion data from the server.
|
||||
*
|
||||
* @param {Integer} id: The autocompletion request id. This will be used in the reducer to
|
||||
* check that we update the state with the last request results.
|
||||
* @param {String} input: The expression that was evaluated to get the data.
|
||||
* - {String} frameActorId: The id of the frame the evaluation was made in.
|
||||
* @param {Object} data: The actual data sent from the server.
|
||||
*/
|
||||
function autocompleteDataReceive(id, input, frameActorId, data) {
|
||||
return {
|
||||
type: AUTOCOMPLETE_DATA_RECEIVE,
|
||||
id,
|
||||
input,
|
||||
frameActorId,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
autocompleteUpdate,
|
||||
autocompleteDataFetch,
|
||||
autocompleteDataReceive,
|
||||
};
|
|
@ -7,6 +7,7 @@
|
|||
"use strict";
|
||||
|
||||
const actionModules = [
|
||||
require("./autocomplete"),
|
||||
require("./filters"),
|
||||
require("./messages"),
|
||||
require("./ui"),
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DevToolsModules(
|
||||
'autocomplete.js',
|
||||
'filters.js',
|
||||
'history.js',
|
||||
'index.js',
|
||||
|
|
|
@ -39,7 +39,9 @@ const {
|
|||
getHistory,
|
||||
getHistoryValue,
|
||||
} = require("devtools/client/webconsole/selectors/history");
|
||||
const {getAutocompleteState} = require("devtools/client/webconsole/selectors/autocomplete");
|
||||
const historyActions = require("devtools/client/webconsole/actions/history");
|
||||
const autocompleteActions = require("devtools/client/webconsole/actions/autocomplete");
|
||||
|
||||
// Constants used for defining the direction of JSTerm input history navigation.
|
||||
const {
|
||||
|
@ -77,6 +79,10 @@ class JSTerm extends Component {
|
|||
codeMirrorEnabled: PropTypes.bool,
|
||||
// Update position in the history after executing an expression (action).
|
||||
updateHistoryPosition: PropTypes.func.isRequired,
|
||||
// Update autocomplete popup state.
|
||||
autocompleteUpdate: PropTypes.func.isRequired,
|
||||
// Data to be displayed in the autocomplete popup.
|
||||
autocompleteData: PropTypes.object.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -94,40 +100,16 @@ class JSTerm extends Component {
|
|||
this._inputEventHandler = this._inputEventHandler.bind(this);
|
||||
this._blurEventHandler = this._blurEventHandler.bind(this);
|
||||
this.onContextMenu = this.onContextMenu.bind(this);
|
||||
this.imperativeUpdate = this.imperativeUpdate.bind(this);
|
||||
|
||||
this.SELECTED_FRAME = -1;
|
||||
|
||||
/**
|
||||
* Array that caches the user input suggestions received from the server.
|
||||
* @private
|
||||
* @type array
|
||||
*/
|
||||
this._autocompleteCache = null;
|
||||
|
||||
/**
|
||||
* The input that caused the last request to the server, whose response is
|
||||
* cached in the _autocompleteCache array.
|
||||
* @private
|
||||
* @type string
|
||||
*/
|
||||
this._autocompleteQuery = null;
|
||||
|
||||
/**
|
||||
* The frameActorId used in the last autocomplete query. Whenever this changes
|
||||
* the autocomplete cache must be invalidated.
|
||||
* @private
|
||||
* @type string
|
||||
*/
|
||||
this._lastFrameActorId = null;
|
||||
|
||||
/**
|
||||
* Last input value.
|
||||
* @type string
|
||||
*/
|
||||
this.lastInputValue = "";
|
||||
|
||||
this.currentAutoCompletionRequestId = null;
|
||||
|
||||
this.autocompletePopup = null;
|
||||
this.inputNode = null;
|
||||
this.completeNode = null;
|
||||
|
@ -356,7 +338,7 @@ class JSTerm extends Component {
|
|||
|
||||
"Ctrl-Space": () => {
|
||||
if (!this.autocompletePopup.isOpen) {
|
||||
this.updateAutocompletion(true);
|
||||
this.fetchAutocompletionProperties(true);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -403,13 +385,32 @@ class JSTerm extends Component {
|
|||
this.lastInputValue && this.setInputValue(this.lastInputValue);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.imperativeUpdate(nextProps);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
// XXX: For now, everything is handled in an imperative way and we
|
||||
// only want React to do the initial rendering of the component.
|
||||
// This should be modified when the actual refactoring will take place.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do all the imperative work needed after a Redux store update.
|
||||
*
|
||||
* @param {Object} nextProps: props passed from shouldComponentUpdate.
|
||||
*/
|
||||
imperativeUpdate(nextProps) {
|
||||
if (
|
||||
nextProps &&
|
||||
nextProps.autocompleteData !== this.props.autocompleteData &&
|
||||
nextProps.autocompleteData.pendingRequestId === null
|
||||
) {
|
||||
this.updateAutocompletionPopup(nextProps.autocompleteData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the element that holds the messages we display.
|
||||
* @type Element
|
||||
|
@ -773,7 +774,7 @@ class JSTerm extends Component {
|
|||
const value = this.getInputValue();
|
||||
if (this.lastInputValue !== value) {
|
||||
this.resizeInput();
|
||||
this.updateAutocompletion();
|
||||
this.fetchAutocompletionProperties();
|
||||
this.lastInputValue = value;
|
||||
}
|
||||
}
|
||||
|
@ -860,7 +861,7 @@ class JSTerm extends Component {
|
|||
|
||||
if (event.key === " " && !this.autocompletePopup.isOpen) {
|
||||
// Open the autocompletion popup on Ctrl-Space (if it wasn't displayed).
|
||||
this.updateAutocompletion(true);
|
||||
this.fetchAutocompletionProperties(true);
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
|
@ -1104,137 +1105,64 @@ class JSTerm extends Component {
|
|||
}
|
||||
|
||||
/**
|
||||
* Retrieves properties maching the current input for the selected frame, either from
|
||||
* the server or from a cache if possible.
|
||||
* Will bail-out if there's some text selection in the input.
|
||||
*
|
||||
* @param {Boolean} force: True to not perform any check before trying to show the
|
||||
* autocompletion popup. Defaults to false.
|
||||
* @fires autocomplete-updated
|
||||
* @returns void
|
||||
*/
|
||||
async updateAutocompletion(force = false) {
|
||||
async fetchAutocompletionProperties(force = false) {
|
||||
const inputValue = this.getInputValue();
|
||||
const {editor, inputNode} = this;
|
||||
const frameActor = this.getFrameActor(this.SELECTED_FRAME);
|
||||
|
||||
const frameActorId = this.getFrameActor(this.SELECTED_FRAME);
|
||||
const cursor = this.getSelectionStart();
|
||||
|
||||
// Complete if:
|
||||
// - `force` is true OR
|
||||
// - The input is not empty
|
||||
// - AND there is not text selected
|
||||
// - AND the input or frameActor are different from previous completion
|
||||
// - AND there is not an alphanumeric (+ "_" and "$") right after the cursor
|
||||
if (!force && (
|
||||
!inputValue ||
|
||||
const {editor, inputNode} = this;
|
||||
if (
|
||||
(inputNode && inputNode.selectionStart != inputNode.selectionEnd) ||
|
||||
(editor && editor.getSelection()) ||
|
||||
(
|
||||
!force &&
|
||||
this.lastInputValue === inputValue &&
|
||||
frameActor === this._lastFrameActorId
|
||||
) ||
|
||||
/^[a-zA-Z0-9_$]/.test(inputValue.substring(cursor))
|
||||
)) {
|
||||
(editor && editor.getSelection())
|
||||
) {
|
||||
this.clearCompletion();
|
||||
this.emit("autocomplete-updated");
|
||||
return;
|
||||
}
|
||||
|
||||
const input = this.getInputValueBeforeCursor();
|
||||
|
||||
// If the current input starts with the previous input, then we already
|
||||
// have a list of suggestions and we just need to filter the cached
|
||||
// suggestions. When the current input ends with a non-alphanumeric character we ask
|
||||
// the server again for suggestions.
|
||||
|
||||
// Check if last character is non-alphanumeric
|
||||
if (!/[a-zA-Z0-9]$/.test(input) || frameActor != this._lastFrameActorId) {
|
||||
this._autocompleteQuery = null;
|
||||
this._autocompleteCache = null;
|
||||
}
|
||||
|
||||
if (this._autocompleteQuery && input.startsWith(this._autocompleteQuery)) {
|
||||
let filterBy = input;
|
||||
if (this._autocompleteCache.isElementAccess) {
|
||||
// if we're performing an element access, we can simply retrieve whatever comes
|
||||
// after the last opening bracket.
|
||||
filterBy = input.substring(input.lastIndexOf("[") + 1);
|
||||
} else {
|
||||
// Find the last non-alphanumeric other than "_", ":", or "$" if it exists.
|
||||
const lastNonAlpha = input.match(/[^a-zA-Z0-9_$:][a-zA-Z0-9_$:]*$/);
|
||||
// If input contains non-alphanumerics, use the part after the last one
|
||||
// to filter the cache.
|
||||
if (lastNonAlpha) {
|
||||
filterBy = input.substring(input.lastIndexOf(lastNonAlpha) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
const stripWrappingQuotes = s => s.replace(/^['"`](.+(?=['"`]$))['"`]$/g, "$1");
|
||||
const filterByLc = filterBy.toLocaleLowerCase();
|
||||
const looseMatching = !filterBy || filterBy[0].toLocaleLowerCase() === filterBy[0];
|
||||
const needStripQuote = this._autocompleteCache.isElementAccess
|
||||
&& !/^[`"']/.test(filterBy);
|
||||
const newList = this._autocompleteCache.matches.filter(l => {
|
||||
if (needStripQuote) {
|
||||
l = stripWrappingQuotes(l);
|
||||
}
|
||||
|
||||
if (looseMatching) {
|
||||
return l.toLocaleLowerCase().startsWith(filterByLc);
|
||||
}
|
||||
|
||||
return l.startsWith(filterBy);
|
||||
});
|
||||
|
||||
this._receiveAutocompleteProperties(null, {
|
||||
matches: newList,
|
||||
matchProp: filterBy,
|
||||
isElementAccess: this._autocompleteCache.isElementAccess,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const requestId = gSequenceId();
|
||||
this._lastFrameActorId = frameActor;
|
||||
this.currentAutoCompletionRequestId = requestId;
|
||||
|
||||
const message = await this.webConsoleClient.autocomplete(input, cursor, frameActor);
|
||||
this._receiveAutocompleteProperties(requestId, message);
|
||||
this.props.autocompleteUpdate({
|
||||
inputValue,
|
||||
cursor,
|
||||
frameActorId,
|
||||
force,
|
||||
client: this.webConsoleClient,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the autocompletion results. This method takes
|
||||
* the completion result received from the server and updates the UI
|
||||
* accordingly.
|
||||
* Takes the data returned by the server and update the autocomplete popup state (i.e.
|
||||
* its visibility and items).
|
||||
*
|
||||
* @param number requestId
|
||||
* Request ID.
|
||||
* @param object message
|
||||
* The JSON message which holds the completion results received from
|
||||
* the content process.
|
||||
* @param {Object} data
|
||||
* The autocompletion data as returned by the webconsole actor's autocomplete
|
||||
* service. Should be of the following shape:
|
||||
* {
|
||||
* matches: {Array} array of the properties matching the input,
|
||||
* matchProp: {String} The string used to filter the properties,
|
||||
* isElementAccess: {Boolean} True when the input is an element access,
|
||||
* i.e. `document["addEve`.
|
||||
* }
|
||||
* @fires autocomplete-updated
|
||||
*/
|
||||
_receiveAutocompleteProperties(requestId, message) {
|
||||
if (this.currentAutoCompletionRequestId !== requestId) {
|
||||
return;
|
||||
}
|
||||
this.currentAutoCompletionRequestId = null;
|
||||
|
||||
// Cache whatever came from the server if the last char is
|
||||
// alphanumeric, '.' or '['.
|
||||
const inputUntilCursor = this.getInputValueBeforeCursor();
|
||||
|
||||
if (requestId != null && /[a-zA-Z0-9.\[]$/.test(inputUntilCursor)) {
|
||||
this._autocompleteCache = {
|
||||
matches: message.matches,
|
||||
matchProp: message.matchProp,
|
||||
isElementAccess: message.isElementAccess,
|
||||
};
|
||||
this._autocompleteQuery = inputUntilCursor;
|
||||
}
|
||||
|
||||
const {matches, matchProp, isElementAccess} = message;
|
||||
updateAutocompletionPopup(data) {
|
||||
const {matches, matchProp, isElementAccess} = data;
|
||||
if (!matches.length) {
|
||||
this.clearCompletion();
|
||||
this.emit("autocomplete-updated");
|
||||
return;
|
||||
}
|
||||
|
||||
const inputUntilCursor = this.getInputValueBeforeCursor();
|
||||
|
||||
const items = matches.map(label => {
|
||||
let preLabel = label.substring(0, matchProp.length);
|
||||
// If the user is performing an element access, and if they did not typed a quote,
|
||||
|
@ -1673,15 +1601,27 @@ function mapStateToProps(state) {
|
|||
return {
|
||||
history: getHistory(state),
|
||||
getValueFromHistory: (direction) => getHistoryValue(state, direction),
|
||||
autocompleteData: getAutocompleteState(state),
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
|
||||
appendToHistory: (expr) => dispatch(historyActions.appendToHistory(expr)),
|
||||
clearHistory: () => dispatch(historyActions.clearHistory()),
|
||||
updateHistoryPosition: (direction, expression) =>
|
||||
dispatch(historyActions.updateHistoryPosition(direction, expression)),
|
||||
autocompleteUpdate: ({inputValue, cursor, frameActorId, force, client}) => dispatch(
|
||||
autocompleteActions.autocompleteUpdate({
|
||||
inputValue,
|
||||
cursor,
|
||||
frameActorId,
|
||||
force,
|
||||
client,
|
||||
})
|
||||
),
|
||||
autocompleteBailOut: () => dispatch(autocompleteActions.autocompleteBailOut()),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
const actionTypes = {
|
||||
APPEND_NOTIFICATION: "APPEND_NOTIFICATION",
|
||||
APPEND_TO_HISTORY: "APPEND_TO_HISTORY",
|
||||
AUTOCOMPLETE_CLEAR: "AUTOCOMPLETE_CLEAR",
|
||||
AUTOCOMPLETE_DATA_RECEIVE: "AUTOCOMPLETE_DATA_RECEIVE",
|
||||
AUTOCOMPLETE_PENDING_REQUEST: "AUTOCOMPLETE_PENDING_REQUEST",
|
||||
AUTOCOMPLETE_RETRIEVE_FROM_CACHE: "AUTOCOMPLETE_RETRIEVE_FROM_CACHE",
|
||||
BATCH_ACTIONS: "BATCH_ACTIONS",
|
||||
CLEAR_HISTORY: "CLEAR_HISTORY",
|
||||
DEFAULT_FILTERS_RESET: "DEFAULT_FILTERS_RESET",
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
AUTOCOMPLETE_CLEAR,
|
||||
AUTOCOMPLETE_DATA_RECEIVE,
|
||||
AUTOCOMPLETE_PENDING_REQUEST,
|
||||
AUTOCOMPLETE_RETRIEVE_FROM_CACHE,
|
||||
} = require("devtools/client/webconsole/constants");
|
||||
|
||||
function getDefaultState() {
|
||||
return Object.freeze({
|
||||
cache: null,
|
||||
matches: [],
|
||||
matchProp: null,
|
||||
isElementAccess: false,
|
||||
pendingRequestId: null,
|
||||
});
|
||||
}
|
||||
|
||||
function autocomplete(state = getDefaultState(), action) {
|
||||
switch (action.type) {
|
||||
case AUTOCOMPLETE_CLEAR:
|
||||
return getDefaultState();
|
||||
case AUTOCOMPLETE_RETRIEVE_FROM_CACHE:
|
||||
return autoCompleteRetrieveFromCache(state, action);
|
||||
case AUTOCOMPLETE_PENDING_REQUEST:
|
||||
return {
|
||||
...state,
|
||||
cache: null,
|
||||
pendingRequestId: action.id,
|
||||
};
|
||||
case AUTOCOMPLETE_DATA_RECEIVE:
|
||||
if (action.id !== state.pendingRequestId) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
cache: {
|
||||
input: action.input,
|
||||
frameActorId: action.frameActorId,
|
||||
...action.data,
|
||||
},
|
||||
pendingRequestId: null,
|
||||
...action.data,
|
||||
};
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve from cache action reducer.
|
||||
*
|
||||
* @param {Object} state
|
||||
* @param {Object} action
|
||||
* @returns {Object} new state.
|
||||
*/
|
||||
function autoCompleteRetrieveFromCache(state, action) {
|
||||
const {input} = action;
|
||||
const {cache} = state;
|
||||
|
||||
let filterBy = input;
|
||||
if (cache.isElementAccess) {
|
||||
// if we're performing an element access, we can simply retrieve whatever comes
|
||||
// after the last opening bracket.
|
||||
filterBy = input.substring(input.lastIndexOf("[") + 1);
|
||||
} else {
|
||||
// Find the last non-alphanumeric other than "_", ":", or "$" if it exists.
|
||||
const lastNonAlpha = input.match(/[^a-zA-Z0-9_$:][a-zA-Z0-9_$:]*$/);
|
||||
// If input contains non-alphanumerics, use the part after the last one
|
||||
// to filter the cache.
|
||||
if (lastNonAlpha) {
|
||||
filterBy = input.substring(input.lastIndexOf(lastNonAlpha) + 1);
|
||||
}
|
||||
}
|
||||
const stripWrappingQuotes = s => s.replace(/^['"`](.+(?=['"`]$))['"`]$/g, "$1");
|
||||
const filterByLc = filterBy.toLocaleLowerCase();
|
||||
const looseMatching = !filterBy || filterBy[0].toLocaleLowerCase() === filterBy[0];
|
||||
const needStripQuote = cache.isElementAccess && !/^[`"']/.test(filterBy);
|
||||
const newList = cache.matches.filter(l => {
|
||||
if (needStripQuote) {
|
||||
l = stripWrappingQuotes(l);
|
||||
}
|
||||
|
||||
if (looseMatching) {
|
||||
return l.toLocaleLowerCase().startsWith(filterByLc);
|
||||
}
|
||||
|
||||
return l.startsWith(filterBy);
|
||||
});
|
||||
|
||||
return {
|
||||
...state,
|
||||
matches: newList,
|
||||
matchProp: filterBy,
|
||||
isElementAccess: cache.isElementAccess,
|
||||
};
|
||||
}
|
||||
|
||||
exports.autocomplete = autocomplete;
|
|
@ -5,6 +5,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const { autocomplete } = require("./autocomplete");
|
||||
const { filters } = require("./filters");
|
||||
const { messages } = require("./messages");
|
||||
const { prefs } = require("./prefs");
|
||||
|
@ -15,6 +16,7 @@ const { history } = require("./history");
|
|||
const { objectInspector } = require("devtools/client/shared/components/reps/reps.js");
|
||||
|
||||
exports.reducers = {
|
||||
autocomplete,
|
||||
filters,
|
||||
messages,
|
||||
prefs,
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DevToolsModules(
|
||||
'autocomplete.js',
|
||||
'filters.js',
|
||||
'history.js',
|
||||
'index.js',
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
function getAutocompleteState(state) {
|
||||
return state.autocomplete;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getAutocompleteState,
|
||||
};
|
|
@ -4,6 +4,7 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DevToolsModules(
|
||||
'autocomplete.js',
|
||||
'filters.js',
|
||||
'history.js',
|
||||
'messages.js',
|
||||
|
|
|
@ -20,7 +20,7 @@ add_task(async function() {
|
|||
});
|
||||
|
||||
async function performTests() {
|
||||
const { jsterm } = await openNewTabAndConsole(TEST_URI);
|
||||
const { jsterm, ui } = await openNewTabAndConsole(TEST_URI);
|
||||
|
||||
const { autocompletePopup: popup } = jsterm;
|
||||
|
||||
|
@ -33,7 +33,7 @@ async function performTests() {
|
|||
await onPopupOpen;
|
||||
|
||||
ok(popup.isOpen, "popup is open");
|
||||
const cacheMatches = jsterm._autocompleteCache.matches;
|
||||
const cacheMatches = ui.consoleOutput.getStore().getState().autocomplete.cache.matches;
|
||||
is(popup.itemCount, cacheMatches.length, "popup.itemCount is correct");
|
||||
ok(cacheMatches.includes("addEventListener"),
|
||||
"addEventListener is in the list of suggestions");
|
||||
|
|
|
@ -449,6 +449,13 @@ WebConsoleOutputWrapper.prototype = {
|
|||
setTimeout(() => {
|
||||
this.throttledDispatchPromise = null;
|
||||
|
||||
if (!store) {
|
||||
// The store is not initialized yet, we can call setTimeoutIfNeeded so the
|
||||
// messages will be handled in the next timeout when the store is ready.
|
||||
this.setTimeoutIfNeeded();
|
||||
return;
|
||||
}
|
||||
|
||||
store.dispatch(actions.messagesAdd(this.queuedMessageAdds));
|
||||
|
||||
const length = this.queuedMessageAdds.length;
|
||||
|
|
|
@ -20,6 +20,8 @@ const { Services } = jsmScope;
|
|||
// Steal various globals only available in JSM scope (and not Sandbox one)
|
||||
const {
|
||||
console,
|
||||
DOMPoint,
|
||||
DOMQuad,
|
||||
HeapSnapshot,
|
||||
StructuredCloneHolder,
|
||||
TelemetryStopwatch,
|
||||
|
@ -283,6 +285,8 @@ exports.globals = {
|
|||
factory(this.require, this.exports, this.module);
|
||||
},
|
||||
DOMParser,
|
||||
DOMPoint,
|
||||
DOMQuad,
|
||||
Element,
|
||||
Event,
|
||||
FormData,
|
||||
|
|
|
@ -98,6 +98,7 @@ add_task(async function() {
|
|||
let topContext = getBrowsingContextById(topId);
|
||||
isnot(topContext, null);
|
||||
is(topContext.parent, null);
|
||||
is(topId, browser.browsingContext.id, "<browser> has the correct browsingContext");
|
||||
|
||||
let id0 = await addFrame(browser, "frame0");
|
||||
let browsingContext0 = getBrowsingContextById(id0);
|
||||
|
|
|
@ -782,6 +782,12 @@ ChromeUtils::RequestIOActivity(GlobalObject& aGlobal, ErrorResult& aRv)
|
|||
return domPromise.forget();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<BrowsingContext>
|
||||
ChromeUtils::GetBrowsingContext(GlobalObject& aGlobal, uint64_t id)
|
||||
{
|
||||
return BrowsingContext::Get(id);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ChromeUtils::GetRootBrowsingContexts(GlobalObject& aGlobal,
|
||||
nsTArray<RefPtr<BrowsingContext>>& aBrowsingContexts)
|
||||
|
|
|
@ -189,6 +189,9 @@ public:
|
|||
static already_AddRefed<Promise>
|
||||
RequestIOActivity(GlobalObject& aGlobal, ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<BrowsingContext>
|
||||
GetBrowsingContext(GlobalObject& aGlobal, uint64_t id);
|
||||
|
||||
static void
|
||||
GetRootBrowsingContexts(GlobalObject& aGlobal,
|
||||
nsTArray<RefPtr<BrowsingContext>>& aBrowsingContexts);
|
||||
|
|
|
@ -140,7 +140,7 @@ ElemBaseType(const GLenum elemType)
|
|||
case LOCAL_GL_BOOL_VEC2:
|
||||
case LOCAL_GL_BOOL_VEC3:
|
||||
case LOCAL_GL_BOOL_VEC4:
|
||||
return webgl::AttribBaseType::Bool;
|
||||
return webgl::AttribBaseType::Boolean;
|
||||
|
||||
// -
|
||||
|
||||
|
|
|
@ -859,7 +859,7 @@ WebGLContext::DoFakeVertexAttrib0(const uint64_t vertexCount)
|
|||
////
|
||||
|
||||
switch (mGenericVertexAttribTypes[0]) {
|
||||
case webgl::AttribBaseType::Bool:
|
||||
case webgl::AttribBaseType::Boolean:
|
||||
case webgl::AttribBaseType::Float:
|
||||
gl->fVertexAttribPointer(0, 4, LOCAL_GL_FLOAT, false, 0, 0);
|
||||
break;
|
||||
|
|
|
@ -255,7 +255,7 @@ WebGLContext::GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname,
|
|||
obj = GetVertexAttribUint32Array(cx, index);
|
||||
break;
|
||||
|
||||
case webgl::AttribBaseType::Bool:
|
||||
case webgl::AttribBaseType::Boolean:
|
||||
MOZ_CRASH("impossible");
|
||||
}
|
||||
|
||||
|
|
|
@ -474,7 +474,7 @@ webgl::ToString(const webgl::AttribBaseType x)
|
|||
return "INT";
|
||||
case webgl::AttribBaseType::UInt:
|
||||
return "UINT";
|
||||
case webgl::AttribBaseType::Bool:
|
||||
case webgl::AttribBaseType::Boolean:
|
||||
return "BOOL";
|
||||
}
|
||||
MOZ_CRASH("pacify gcc6 warning");
|
||||
|
@ -554,7 +554,7 @@ webgl::LinkedProgramInfo::GetDrawFetchLimits() const
|
|||
|
||||
const auto& progBaseType = progAttrib.mActiveInfo->mBaseType;
|
||||
if ((attribDataBaseType != progBaseType) &
|
||||
(progBaseType != webgl::AttribBaseType::Bool))
|
||||
(progBaseType != webgl::AttribBaseType::Boolean))
|
||||
{
|
||||
const auto& dataType = ToString(attribDataBaseType);
|
||||
const auto& progType = ToString(progBaseType);
|
||||
|
|
|
@ -193,7 +193,7 @@ enum class AttribBaseType : uint8_t {
|
|||
Int,
|
||||
UInt,
|
||||
Float, // Also includes NormU?Int
|
||||
Bool, // Can convert from anything.
|
||||
Boolean, // Can convert from anything.
|
||||
};
|
||||
const char* ToString(AttribBaseType);
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ WebGLUniformLocation::ValidateSizeAndType(const uint8_t setterElemSize,
|
|||
|
||||
const auto& uniformType = mInfo->mActiveInfo->mBaseType;
|
||||
if (setterType != uniformType &&
|
||||
uniformType != webgl::AttribBaseType::Bool)
|
||||
uniformType != webgl::AttribBaseType::Boolean)
|
||||
{
|
||||
const auto& uniformStr = EnumString(mInfo->mActiveInfo->mElemType);
|
||||
mContext->ErrorInvalidOperation("Function used is incompatible with uniform"
|
||||
|
|
|
@ -356,6 +356,15 @@ partial namespace ChromeUtils {
|
|||
[Throws]
|
||||
Promise<sequence<IOActivityDataDictionary>> requestIOActivity();
|
||||
|
||||
/**
|
||||
* Returns the BrowsingContext referred by the given id.
|
||||
*/
|
||||
[ChromeOnly]
|
||||
BrowsingContext? getBrowsingContext(unsigned long long id);
|
||||
|
||||
/**
|
||||
* Returns all the root BrowsingContexts.
|
||||
*/
|
||||
[ChromeOnly]
|
||||
sequence<BrowsingContext> getRootBrowsingContexts();
|
||||
};
|
||||
|
|
|
@ -91,6 +91,10 @@ IsWindowAllowedToPlay(nsPIDOMWindowInner* aWindow)
|
|||
}
|
||||
|
||||
nsIDocument* approver = ApproverDocOf(*aWindow->GetExtantDoc());
|
||||
if (!approver) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nsContentUtils::IsExactSitePermAllow(approver->NodePrincipal(),
|
||||
"autoplay-media")) {
|
||||
AUTOPLAY_LOG("Allow autoplay as document has autoplay permission.");
|
||||
|
@ -163,12 +167,6 @@ IsMediaElementAllowedToPlay(const HTMLMediaElement& aElement)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!aElement.HasAudio() &&
|
||||
aElement.ReadyState() >= HTMLMediaElement_Binding::HAVE_METADATA) {
|
||||
AUTOPLAY_LOG("Allow media without audio track %p to autoplay\n", &aElement);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -323,16 +323,16 @@ pub fn tiles(
|
|||
// Since we're working in layer space, we can end up computing leftover tiles with an empty
|
||||
// size due to floating point precision issues. Detect this case so that we don't return
|
||||
// tiles with an empty size.
|
||||
let x_count = {
|
||||
let result = f32::ceil((visible_rect.max_x() - prim_rect.origin.x) / layer_tile_size.width) as u32 - t0.x;
|
||||
let x_max = {
|
||||
let result = f32::ceil((visible_rect.max_x() - prim_rect.origin.x) / layer_tile_size.width) as u32;
|
||||
if result == leftover_offset.x + 1 && leftover_layer_size.width == 0.0f32 {
|
||||
leftover_offset.x
|
||||
} else {
|
||||
result
|
||||
}
|
||||
};
|
||||
let y_count = {
|
||||
let result = f32::ceil((visible_rect.max_y() - prim_rect.origin.y) / layer_tile_size.height) as u32 - t0.y;
|
||||
let y_max = {
|
||||
let result = f32::ceil((visible_rect.max_y() - prim_rect.origin.y) / layer_tile_size.height) as u32;
|
||||
if result == leftover_offset.y + 1 && leftover_layer_size.height == 0.0f32 {
|
||||
leftover_offset.y
|
||||
} else {
|
||||
|
@ -341,14 +341,14 @@ pub fn tiles(
|
|||
};
|
||||
|
||||
let mut row_flags = EdgeAaSegmentMask::TOP;
|
||||
if y_count == 1 {
|
||||
if y_max - t0.y == 1 {
|
||||
row_flags |= EdgeAaSegmentMask::BOTTOM;
|
||||
}
|
||||
TileIterator {
|
||||
current_x: 0,
|
||||
current_y: 0,
|
||||
x_count,
|
||||
y_count,
|
||||
x_count: x_max - t0.x,
|
||||
y_count: y_max - t0.y,
|
||||
row_flags,
|
||||
origin: t0,
|
||||
tile_size: layer_tile_size,
|
||||
|
|
|
@ -1018,6 +1018,8 @@ impl ResourceCache {
|
|||
}
|
||||
};
|
||||
|
||||
assert!(descriptor.size.width != 0 && descriptor.size.height != 0);
|
||||
|
||||
self.missing_blob_images.push(
|
||||
BlobImageParams {
|
||||
request,
|
||||
|
|
|
@ -1 +1 @@
|
|||
d9bddae3796e782548b9f3dfffdbcb26cf719232
|
||||
45498e55dbb918f82e9583d03912b73b5a301a30
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
package org.mozilla.geckoview;
|
||||
|
||||
import android.support.annotation.AnyThread;
|
||||
import android.support.annotation.IntDef;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
|
@ -35,6 +36,7 @@ import org.mozilla.gecko.annotation.WrapForJNI;
|
|||
* });
|
||||
* </pre>
|
||||
*/
|
||||
@AnyThread
|
||||
public class GeckoWebExecutor {
|
||||
// We don't use this right now because we access GeckoThread directly, but
|
||||
// it's future-proofing for a world where we allow multiple GeckoRuntimes.
|
||||
|
|
|
@ -10891,6 +10891,11 @@ square7.net
|
|||
// Submitted by Dave Tharp <browsersafetymark.io@quicinc.com>
|
||||
browsersafetymark.io
|
||||
|
||||
// Bytemark Hosting : https://www.bytemark.co.uk
|
||||
// Submitted by Paul Cammish <paul.cammish@bytemark.co.uk>
|
||||
dh.bytemark.co.uk
|
||||
vm.bytemark.co.uk
|
||||
|
||||
// callidomus : https://www.callidomus.com/
|
||||
// Submitted by Marcus Popp <admin@callidomus.com>
|
||||
mycd.eu
|
||||
|
|
|
@ -66,10 +66,6 @@ public final class Portability {
|
|||
return string.toCharArray();
|
||||
}
|
||||
|
||||
public static @Local String newLocalFromLocal(@Local String local, Interner interner) {
|
||||
return local;
|
||||
}
|
||||
|
||||
// Deallocation methods
|
||||
|
||||
public static void releaseString(String str) {
|
||||
|
|
|
@ -6859,13 +6859,7 @@ public class Tokenizer implements Locator {
|
|||
seenDigits = other.seenDigits;
|
||||
endTag = other.endTag;
|
||||
shouldSuspend = false;
|
||||
|
||||
if (other.doctypeName == null) {
|
||||
doctypeName = null;
|
||||
} else {
|
||||
doctypeName = Portability.newLocalFromLocal(other.doctypeName,
|
||||
interner);
|
||||
}
|
||||
doctypeName = other.doctypeName;
|
||||
|
||||
Portability.releaseString(systemIdentifier);
|
||||
if (other.systemIdentifier == null) {
|
||||
|
@ -6890,7 +6884,7 @@ public class Tokenizer implements Locator {
|
|||
// In the C++ case, the atoms in the other tokenizer are from a
|
||||
// different tokenizer-scoped atom table. Therefore, we have to
|
||||
// obtain the correspoding atom from our own atom table.
|
||||
nonInternedTagName.setNameForNonInterned(Portability.newLocalFromLocal(other.tagName.getName(), interner)
|
||||
nonInternedTagName.setNameForNonInterned(other.tagName.getName()
|
||||
// CPPONLY: , other.tagName.isCustom()
|
||||
);
|
||||
tagName = nonInternedTagName;
|
||||
|
@ -6907,7 +6901,7 @@ public class Tokenizer implements Locator {
|
|||
// CPPONLY: // In the C++ case, the atoms in the other tokenizer are from a
|
||||
// CPPONLY: // different tokenizer-scoped atom table. Therefore, we have to
|
||||
// CPPONLY: // obtain the correspoding atom from our own atom table.
|
||||
// CPPONLY: nonInternedAttributeName.setNameForNonInterned(Portability.newLocalFromLocal(other.attributeName.getLocal(AttributeName.HTML), interner));
|
||||
// CPPONLY: nonInternedAttributeName.setNameForNonInterned(other.attributeName.getLocal(AttributeName.HTML));
|
||||
// CPPONLY: attributeName = nonInternedAttributeName;
|
||||
// CPPONLY: }
|
||||
|
||||
|
@ -6915,7 +6909,7 @@ public class Tokenizer implements Locator {
|
|||
if (other.attributes == null) {
|
||||
attributes = null;
|
||||
} else {
|
||||
attributes = other.attributes.cloneAttributes(interner);
|
||||
attributes = other.attributes.cloneAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7151,8 +7145,9 @@ public class Tokenizer implements Locator {
|
|||
|
||||
void destructor() {
|
||||
Portability.delete(nonInternedTagName);
|
||||
// CPPONLY: Portability.delete(nonInternedAttributeName);
|
||||
nonInternedTagName = null;
|
||||
// CPPONLY: Portability.delete(nonInternedAttributeName);
|
||||
// CPPONLY: nonInternedAttributeName = null;
|
||||
// The translator will write refcount tracing stuff here
|
||||
Portability.delete(attributes);
|
||||
attributes = null;
|
||||
|
|
|
@ -4786,7 +4786,7 @@ public abstract class TreeBuilder<T> implements TokenHandler,
|
|||
assert node == listOfActiveFormattingElements[nodeListPos];
|
||||
assert node == stack[nodePos];
|
||||
T clone = createElement("http://www.w3.org/1999/xhtml",
|
||||
node.name, node.attributes.cloneAttributes(null), commonAncestor.node
|
||||
node.name, node.attributes.cloneAttributes(), commonAncestor.node
|
||||
// CPPONLY: , htmlCreator(node.getHtmlCreator())
|
||||
);
|
||||
StackNode<T> newNode = createStackNode(node.getFlags(), node.ns,
|
||||
|
@ -4818,7 +4818,7 @@ public abstract class TreeBuilder<T> implements TokenHandler,
|
|||
}
|
||||
T clone = createElement("http://www.w3.org/1999/xhtml",
|
||||
formattingElt.name,
|
||||
formattingElt.attributes.cloneAttributes(null), furthestBlock.node
|
||||
formattingElt.attributes.cloneAttributes(), furthestBlock.node
|
||||
// CPPONLY: , htmlCreator(formattingElt.getHtmlCreator())
|
||||
);
|
||||
StackNode<T> formattingClone = createStackNode(
|
||||
|
@ -5005,12 +5005,12 @@ public abstract class TreeBuilder<T> implements TokenHandler,
|
|||
T clone;
|
||||
if (currentNode.isFosterParenting()) {
|
||||
clone = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", entry.name,
|
||||
entry.attributes.cloneAttributes(null)
|
||||
entry.attributes.cloneAttributes()
|
||||
// CPPONLY: , htmlCreator(entry.getHtmlCreator())
|
||||
);
|
||||
} else {
|
||||
clone = createElement("http://www.w3.org/1999/xhtml", entry.name,
|
||||
entry.attributes.cloneAttributes(null), currentNode.node
|
||||
entry.attributes.cloneAttributes(), currentNode.node
|
||||
// CPPONLY: , htmlCreator(entry.getHtmlCreator())
|
||||
);
|
||||
appendElement(clone, currentNode.node);
|
||||
|
@ -5424,7 +5424,7 @@ public abstract class TreeBuilder<T> implements TokenHandler,
|
|||
checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
|
||||
// ]NOCPP]
|
||||
// This method can't be called for custom elements
|
||||
HtmlAttributes clone = attributes.cloneAttributes(null);
|
||||
HtmlAttributes clone = attributes.cloneAttributes();
|
||||
// Attributes must not be read after calling createElement, because
|
||||
// createElement may delete attributes in C++.
|
||||
T elt;
|
||||
|
@ -6130,7 +6130,7 @@ public abstract class TreeBuilder<T> implements TokenHandler,
|
|||
StackNode<T> newNode = new StackNode<T>(-1);
|
||||
newNode.setValues(node.getFlags(), node.ns,
|
||||
node.name, node.node, node.popName,
|
||||
node.attributes.cloneAttributes(null)
|
||||
node.attributes.cloneAttributes()
|
||||
// CPPONLY: , node.getHtmlCreator()
|
||||
// [NOCPP[
|
||||
, node.getLocator()
|
||||
|
@ -6217,7 +6217,7 @@ public abstract class TreeBuilder<T> implements TokenHandler,
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked") public void loadState(
|
||||
TreeBuilderState<T> snapshot, Interner interner)
|
||||
TreeBuilderState<T> snapshot)
|
||||
throws SAXException {
|
||||
// CPPONLY: mCurrentHtmlScriptIsAsyncOrDefer = false;
|
||||
StackNode<T>[] stackCopy = snapshot.getStack();
|
||||
|
@ -6254,9 +6254,9 @@ public abstract class TreeBuilder<T> implements TokenHandler,
|
|||
StackNode<T> node = listCopy[i];
|
||||
if (node != null) {
|
||||
StackNode<T> newNode = createStackNode(node.getFlags(), node.ns,
|
||||
Portability.newLocalFromLocal(node.name, interner), node.node,
|
||||
Portability.newLocalFromLocal(node.popName, interner),
|
||||
node.attributes.cloneAttributes(null)
|
||||
node.name, node.node,
|
||||
node.popName,
|
||||
node.attributes.cloneAttributes()
|
||||
// CPPONLY: , node.getHtmlCreator()
|
||||
// [NOCPP[
|
||||
, node.getLocator()
|
||||
|
@ -6272,8 +6272,8 @@ public abstract class TreeBuilder<T> implements TokenHandler,
|
|||
int listIndex = findInArray(node, listCopy);
|
||||
if (listIndex == -1) {
|
||||
StackNode<T> newNode = createStackNode(node.getFlags(), node.ns,
|
||||
Portability.newLocalFromLocal(node.name, interner), node.node,
|
||||
Portability.newLocalFromLocal(node.popName, interner),
|
||||
node.name, node.node,
|
||||
node.popName,
|
||||
null
|
||||
// CPPONLY: , node.getHtmlCreator()
|
||||
// [NOCPP[
|
||||
|
|
|
@ -57,26 +57,12 @@ public:
|
|||
|
||||
inline void ReleaseValue() { mValue.Release(); }
|
||||
|
||||
inline nsHtml5AttributeEntry Clone(nsHtml5AtomTable* aInterner)
|
||||
inline nsHtml5AttributeEntry Clone()
|
||||
{
|
||||
// Copy the memory
|
||||
nsHtml5AttributeEntry clone(*this);
|
||||
// Increment refcount for value
|
||||
clone.mValue = this->mValue.Clone();
|
||||
if (aInterner) {
|
||||
// Now if we have an interner, we'll need to rewrite non-static atoms.
|
||||
// Only the local names may be non-static, in which case all three
|
||||
// are the same.
|
||||
nsAtom* local = GetLocal(0);
|
||||
if (!local->IsStatic()) {
|
||||
nsAutoString str;
|
||||
local->ToString(str);
|
||||
nsAtom* local = aInterner->GetAtom(str);
|
||||
clone.mLocals[0] = local;
|
||||
clone.mLocals[1] = local;
|
||||
clone.mLocals[2] = local;
|
||||
}
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
|
|
|
@ -64,7 +64,6 @@ private:
|
|||
static int32_t* XLINK_NS;
|
||||
public:
|
||||
static nsStaticAtom** ALL_NO_PREFIX;
|
||||
|
||||
private:
|
||||
static nsStaticAtom** XMLNS_PREFIX;
|
||||
static nsStaticAtom** XLINK_PREFIX;
|
||||
|
@ -72,7 +71,6 @@ private:
|
|||
static RefPtr<nsAtom>* SVG_DIFFERENT(nsAtom* name, nsAtom* camel);
|
||||
static RefPtr<nsAtom>* MATH_DIFFERENT(nsAtom* name, nsAtom* camel);
|
||||
static RefPtr<nsAtom>* COLONIFIED_LOCAL(nsAtom* name, nsAtom* suffix);
|
||||
|
||||
public:
|
||||
static RefPtr<nsAtom>* SAME_LOCAL(nsAtom* name);
|
||||
inline static int32_t levelOrderBinarySearch(jArray<int32_t, int32_t> data,
|
||||
|
|
|
@ -200,13 +200,13 @@ nsHtml5HtmlAttributes::adjustForSvg()
|
|||
}
|
||||
|
||||
nsHtml5HtmlAttributes*
|
||||
nsHtml5HtmlAttributes::cloneAttributes(nsHtml5AtomTable* aInterner)
|
||||
nsHtml5HtmlAttributes::cloneAttributes()
|
||||
{
|
||||
MOZ_ASSERT(mStorage.IsEmpty() || !mMode);
|
||||
nsHtml5HtmlAttributes* clone =
|
||||
new nsHtml5HtmlAttributes(nsHtml5AttributeName::HTML);
|
||||
for (nsHtml5AttributeEntry& entry : mStorage) {
|
||||
clone->AddEntry(entry.Clone(aInterner));
|
||||
clone->AddEntry(entry.Clone());
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ public:
|
|||
bool contains(nsHtml5AttributeName* aName);
|
||||
void adjustForMath();
|
||||
void adjustForSvg();
|
||||
nsHtml5HtmlAttributes* cloneAttributes(nsHtml5AtomTable* aInterner);
|
||||
nsHtml5HtmlAttributes* cloneAttributes();
|
||||
bool equalsAnother(nsHtml5HtmlAttributes* aOther);
|
||||
static void initializeStatics();
|
||||
static void releaseStatics();
|
||||
|
|
|
@ -478,7 +478,7 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
|
|||
mDocWriteSpeculativeTokenizer->start();
|
||||
}
|
||||
mDocWriteSpeculativeTokenizer->resetToDataState();
|
||||
mDocWriteSpeculativeTreeBuilder->loadState(mTreeBuilder, &mAtomTable);
|
||||
mDocWriteSpeculativeTreeBuilder->loadState(mTreeBuilder);
|
||||
mDocWriteSpeculativeLastWasCR = false;
|
||||
}
|
||||
|
||||
|
@ -752,7 +752,7 @@ nsHtml5Parser::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState,
|
|||
{
|
||||
mTokenizer->resetToDataState();
|
||||
mTokenizer->setLineNumber(aLine);
|
||||
mTreeBuilder->loadState(aState, &mAtomTable);
|
||||
mTreeBuilder->loadState(aState);
|
||||
mLastWasCR = false;
|
||||
mReturnToStreamParserPermitted = true;
|
||||
}
|
||||
|
|
|
@ -17,14 +17,6 @@ nsHtml5Portability::newLocalNameFromBuffer(char16_t* buf,
|
|||
return interner->GetAtom(nsDependentSubstring(buf, buf + length));
|
||||
}
|
||||
|
||||
nsAtom*
|
||||
nsHtml5Portability::newLocalFromLocal(nsAtom* local,
|
||||
nsHtml5AtomTable* interner)
|
||||
{
|
||||
// FIXME(emilio): This function should be removed.
|
||||
return local;
|
||||
}
|
||||
|
||||
static bool
|
||||
ContainsWhiteSpace(mozilla::Span<char16_t> aSpan)
|
||||
{
|
||||
|
|
|
@ -69,7 +69,6 @@ public:
|
|||
static nsHtml5String newStringFromString(nsHtml5String string);
|
||||
static jArray<char16_t, int32_t> newCharArrayFromLocal(nsAtom* local);
|
||||
static jArray<char16_t, int32_t> newCharArrayFromString(nsHtml5String string);
|
||||
static nsAtom* newLocalFromLocal(nsAtom* local, nsHtml5AtomTable* interner);
|
||||
static bool localEqualsBuffer(nsAtom* local, char16_t* buf, int32_t length);
|
||||
static bool lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString(
|
||||
const char* lowerCaseLiteral,
|
||||
|
|
|
@ -1685,7 +1685,7 @@ nsHtml5StreamParser::ContinueAfterScripts(nsHtml5Tokenizer* aTokenizer,
|
|||
// Copy state over
|
||||
mLastWasCR = aLastWasCR;
|
||||
mTokenizer->loadState(aTokenizer);
|
||||
mTreeBuilder->loadState(aTreeBuilder, &mAtomTable);
|
||||
mTreeBuilder->loadState(aTreeBuilder);
|
||||
} else {
|
||||
// We've got a successful speculation and at least a moment ago it was
|
||||
// the current speculation
|
||||
|
|
|
@ -4793,12 +4793,7 @@ nsHtml5Tokenizer::loadState(nsHtml5Tokenizer* other)
|
|||
seenDigits = other->seenDigits;
|
||||
endTag = other->endTag;
|
||||
shouldSuspend = false;
|
||||
if (!other->doctypeName) {
|
||||
doctypeName = nullptr;
|
||||
} else {
|
||||
doctypeName =
|
||||
nsHtml5Portability::newLocalFromLocal(other->doctypeName, interner);
|
||||
}
|
||||
doctypeName = other->doctypeName;
|
||||
systemIdentifier.Release();
|
||||
if (!other->systemIdentifier) {
|
||||
systemIdentifier = nullptr;
|
||||
|
@ -4819,10 +4814,8 @@ nsHtml5Tokenizer::loadState(nsHtml5Tokenizer* other)
|
|||
} else if (other->tagName->isInterned()) {
|
||||
tagName = other->tagName;
|
||||
} else {
|
||||
nonInternedTagName->setNameForNonInterned(
|
||||
nsHtml5Portability::newLocalFromLocal(other->tagName->getName(),
|
||||
interner),
|
||||
other->tagName->isCustom());
|
||||
nonInternedTagName->setNameForNonInterned(other->tagName->getName(),
|
||||
other->tagName->isCustom());
|
||||
tagName = nonInternedTagName;
|
||||
}
|
||||
if (!other->attributeName) {
|
||||
|
@ -4831,15 +4824,14 @@ nsHtml5Tokenizer::loadState(nsHtml5Tokenizer* other)
|
|||
attributeName = other->attributeName;
|
||||
} else {
|
||||
nonInternedAttributeName->setNameForNonInterned(
|
||||
nsHtml5Portability::newLocalFromLocal(
|
||||
other->attributeName->getLocal(nsHtml5AttributeName::HTML), interner));
|
||||
other->attributeName->getLocal(nsHtml5AttributeName::HTML));
|
||||
attributeName = nonInternedAttributeName;
|
||||
}
|
||||
delete attributes;
|
||||
if (!other->attributes) {
|
||||
attributes = nullptr;
|
||||
} else {
|
||||
attributes = other->attributes->cloneAttributes(interner);
|
||||
attributes = other->attributes->cloneAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4864,8 +4856,9 @@ nsHtml5Tokenizer::~nsHtml5Tokenizer()
|
|||
{
|
||||
MOZ_COUNT_DTOR(nsHtml5Tokenizer);
|
||||
delete nonInternedTagName;
|
||||
delete nonInternedAttributeName;
|
||||
nonInternedTagName = nullptr;
|
||||
delete nonInternedAttributeName;
|
||||
nonInternedAttributeName = nullptr;
|
||||
delete attributes;
|
||||
attributes = nullptr;
|
||||
}
|
||||
|
|
|
@ -3883,7 +3883,7 @@ nsHtml5TreeBuilder::adoptionAgencyEndTag(nsAtom* name)
|
|||
nsIContentHandle* clone =
|
||||
createElement(kNameSpaceID_XHTML,
|
||||
node->name,
|
||||
node->attributes->cloneAttributes(nullptr),
|
||||
node->attributes->cloneAttributes(),
|
||||
commonAncestor->node,
|
||||
htmlCreator(node->getHtmlCreator()));
|
||||
nsHtml5StackNode* newNode = createStackNode(node->getFlags(),
|
||||
|
@ -3915,7 +3915,7 @@ nsHtml5TreeBuilder::adoptionAgencyEndTag(nsAtom* name)
|
|||
nsIContentHandle* clone =
|
||||
createElement(kNameSpaceID_XHTML,
|
||||
formattingElt->name,
|
||||
formattingElt->attributes->cloneAttributes(nullptr),
|
||||
formattingElt->attributes->cloneAttributes(),
|
||||
furthestBlock->node,
|
||||
htmlCreator(formattingElt->getHtmlCreator()));
|
||||
nsHtml5StackNode* formattingClone =
|
||||
|
@ -4101,12 +4101,12 @@ nsHtml5TreeBuilder::reconstructTheActiveFormattingElements()
|
|||
clone = createAndInsertFosterParentedElement(
|
||||
kNameSpaceID_XHTML,
|
||||
entry->name,
|
||||
entry->attributes->cloneAttributes(nullptr),
|
||||
entry->attributes->cloneAttributes(),
|
||||
htmlCreator(entry->getHtmlCreator()));
|
||||
} else {
|
||||
clone = createElement(kNameSpaceID_XHTML,
|
||||
entry->name,
|
||||
entry->attributes->cloneAttributes(nullptr),
|
||||
entry->attributes->cloneAttributes(),
|
||||
currentNode->node,
|
||||
htmlCreator(entry->getHtmlCreator()));
|
||||
appendElement(clone, currentNode->node);
|
||||
|
@ -4391,7 +4391,7 @@ nsHtml5TreeBuilder::appendToCurrentNodeAndPushFormattingElementMayFoster(
|
|||
nsHtml5ElementName* elementName,
|
||||
nsHtml5HtmlAttributes* attributes)
|
||||
{
|
||||
nsHtml5HtmlAttributes* clone = attributes->cloneAttributes(nullptr);
|
||||
nsHtml5HtmlAttributes* clone = attributes->cloneAttributes();
|
||||
nsIContentHandle* elt;
|
||||
nsHtml5StackNode* current = stack[currentPtr];
|
||||
if (current->isFosterParenting()) {
|
||||
|
@ -4835,7 +4835,7 @@ nsHtml5TreeBuilder::newSnapshot()
|
|||
node->name,
|
||||
node->node,
|
||||
node->popName,
|
||||
node->attributes->cloneAttributes(nullptr),
|
||||
node->attributes->cloneAttributes(),
|
||||
node->getHtmlCreator());
|
||||
listCopy[i] = newNode;
|
||||
} else {
|
||||
|
@ -4926,8 +4926,7 @@ nsHtml5TreeBuilder::snapshotMatches(nsAHtml5TreeBuilderState* snapshot)
|
|||
}
|
||||
|
||||
void
|
||||
nsHtml5TreeBuilder::loadState(nsAHtml5TreeBuilderState* snapshot,
|
||||
nsHtml5AtomTable* interner)
|
||||
nsHtml5TreeBuilder::loadState(nsAHtml5TreeBuilderState* snapshot)
|
||||
{
|
||||
mCurrentHtmlScriptIsAsyncOrDefer = false;
|
||||
jArray<nsHtml5StackNode*, int32_t> stackCopy = snapshot->getStack();
|
||||
|
@ -4963,14 +4962,14 @@ nsHtml5TreeBuilder::loadState(nsAHtml5TreeBuilderState* snapshot,
|
|||
for (int32_t i = 0; i < listLen; i++) {
|
||||
nsHtml5StackNode* node = listCopy[i];
|
||||
if (node) {
|
||||
nsHtml5StackNode* newNode = createStackNode(
|
||||
node->getFlags(),
|
||||
node->ns,
|
||||
nsHtml5Portability::newLocalFromLocal(node->name, interner),
|
||||
node->node,
|
||||
nsHtml5Portability::newLocalFromLocal(node->popName, interner),
|
||||
node->attributes->cloneAttributes(nullptr),
|
||||
node->getHtmlCreator());
|
||||
nsHtml5StackNode* newNode =
|
||||
createStackNode(node->getFlags(),
|
||||
node->ns,
|
||||
node->name,
|
||||
node->node,
|
||||
node->popName,
|
||||
node->attributes->cloneAttributes(),
|
||||
node->getHtmlCreator());
|
||||
listOfActiveFormattingElements[i] = newNode;
|
||||
} else {
|
||||
listOfActiveFormattingElements[i] = nullptr;
|
||||
|
@ -4980,14 +4979,13 @@ nsHtml5TreeBuilder::loadState(nsAHtml5TreeBuilderState* snapshot,
|
|||
nsHtml5StackNode* node = stackCopy[i];
|
||||
int32_t listIndex = findInArray(node, listCopy);
|
||||
if (listIndex == -1) {
|
||||
nsHtml5StackNode* newNode = createStackNode(
|
||||
node->getFlags(),
|
||||
node->ns,
|
||||
nsHtml5Portability::newLocalFromLocal(node->name, interner),
|
||||
node->node,
|
||||
nsHtml5Portability::newLocalFromLocal(node->popName, interner),
|
||||
nullptr,
|
||||
node->getHtmlCreator());
|
||||
nsHtml5StackNode* newNode = createStackNode(node->getFlags(),
|
||||
node->ns,
|
||||
node->name,
|
||||
node->node,
|
||||
node->popName,
|
||||
nullptr,
|
||||
node->getHtmlCreator());
|
||||
stack[i] = newNode;
|
||||
} else {
|
||||
stack[i] = listOfActiveFormattingElements[listIndex];
|
||||
|
|
|
@ -592,9 +592,7 @@ private:
|
|||
public:
|
||||
nsAHtml5TreeBuilderState* newSnapshot();
|
||||
bool snapshotMatches(nsAHtml5TreeBuilderState* snapshot);
|
||||
void loadState(nsAHtml5TreeBuilderState* snapshot,
|
||||
nsHtml5AtomTable* interner);
|
||||
|
||||
void loadState(nsAHtml5TreeBuilderState* snapshot);
|
||||
private:
|
||||
int32_t findInArray(nsHtml5StackNode* node,
|
||||
jArray<nsHtml5StackNode*, int32_t> arr);
|
||||
|
|
|
@ -278,3 +278,33 @@ def gather_telemetry(command='', success=False, start_time=None, end_time=None,
|
|||
msg.append(str(error))
|
||||
print('\n'.join(msg) + '\n' + pprint.pformat(data))
|
||||
return None
|
||||
|
||||
|
||||
def verify_statedir(statedir):
|
||||
'''
|
||||
Verifies the statedir is structured correctly. Returns the outgoing,
|
||||
submitted and log paths.
|
||||
|
||||
Requires presence of the following directories; will raise if absent:
|
||||
- statedir/telemetry
|
||||
- statedir/telemetry/outgoing
|
||||
|
||||
Creates the following directories and files if absent (first submission):
|
||||
- statedir/telemetry/submitted
|
||||
'''
|
||||
|
||||
telemetry_dir = os.path.join(statedir, 'telemetry')
|
||||
outgoing = os.path.join(telemetry_dir, 'outgoing')
|
||||
submitted = os.path.join(telemetry_dir, 'submitted')
|
||||
telemetry_log = os.path.join(telemetry_dir, 'telemetry.log')
|
||||
|
||||
if not os.path.isdir(telemetry_dir):
|
||||
raise Exception('{} does not exist'.format(telemetry_dir))
|
||||
|
||||
if not os.path.isdir(outgoing):
|
||||
raise Exception('{} does not exist'.format(outgoing))
|
||||
|
||||
if not os.path.isdir(submitted):
|
||||
os.mkdir(submitted)
|
||||
|
||||
return outgoing, submitted, telemetry_log
|
||||
|
|
|
@ -1171,4 +1171,4 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
|
|||
|
||||
static const int32_t kUnknownId = -1;
|
||||
|
||||
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1550145901574000);
|
||||
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1550491502080000);
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -69,11 +69,8 @@ function hideAutocompletePopup(jsterm) {
|
|||
|
||||
function setJsTermValueForCompletion(jsterm, value) {
|
||||
// setInputValue does not trigger the autocompletion;
|
||||
// we need to call `updateAutocompletion` in order to display the popup. And since
|
||||
// setInputValue sets lastInputValue and updateAutocompletion checks it to trigger
|
||||
// the autocompletion request, we reset it.
|
||||
// we need to call `fetchAutocompletionProperties` in order to display the popup.
|
||||
jsterm.setInputValue(value);
|
||||
jsterm.lastInputValue = null;
|
||||
jsterm.updateAutocompletion();
|
||||
jsterm.fetchAutocompletionProperties();
|
||||
}
|
||||
|
||||
|
|
|
@ -43,10 +43,8 @@ module.exports = async function() {
|
|||
for (const char of Array.from(input)) {
|
||||
const onPopupOpened = jsterm.autocompletePopup.once("popup-opened");
|
||||
jsterm.insertStringAtCursor(char);
|
||||
// We need to remove the lastInputValue set by setInputValue(called by
|
||||
// insertStringAtCursor), and trigger autocompletion update to show the popup.
|
||||
jsterm.lastInputValue = null;
|
||||
jsterm.updateAutocompletion();
|
||||
// We need to trigger autocompletion update to show the popup.
|
||||
jsterm.fetchAutocompletionProperties();
|
||||
await onPopupOpened;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,4 +29,5 @@ addEventListener("ImageContentLoaded", function(aEvent) {
|
|||
// We may not get any responses to Browser:Init if the browser element
|
||||
// is torn down too quickly.
|
||||
var outerWindowID = docShell.outerWindowID;
|
||||
sendAsyncMessage("Browser:Init", {outerWindowID});
|
||||
var browsingContextId = docShell.browsingContext.id;
|
||||
sendAsyncMessage("Browser:Init", {outerWindowID, browsingContextId});
|
||||
|
|
|
@ -481,6 +481,18 @@
|
|||
]]></getter>
|
||||
</property>
|
||||
|
||||
<field name="_browsingContextId">null</field>
|
||||
|
||||
<property name="browsingContext" readonly="true">
|
||||
<getter><![CDATA[
|
||||
if (!this.isRemoteBrowser) {
|
||||
return this.docShell.browsingContext;
|
||||
}
|
||||
|
||||
return ChromeUtils.getBrowsingContext(this._browsingContextId);
|
||||
]]></getter>
|
||||
</property>
|
||||
|
||||
<field name="_lastSearchString">null</field>
|
||||
|
||||
|
||||
|
@ -1281,6 +1293,7 @@
|
|||
switch (aMessage.name) {
|
||||
case "Browser:Init":
|
||||
this._outerWindowID = data.outerWindowID;
|
||||
this._browsingContextId = data.browsingContextId;
|
||||
break;
|
||||
case "DOMTitleChanged":
|
||||
this._contentTitle = data.title;
|
||||
|
|
|
@ -139,3 +139,22 @@ add_task(async function test_canonicalJSON_with_deeply_nested_dicts() {
|
|||
|
||||
Assert.equal(CanonicalJSON.stringify(records), expected);
|
||||
});
|
||||
|
||||
add_task(async function test_canonicalJSON_should_use_scientific_notation() {
|
||||
/*
|
||||
We globally follow the Python float representation, except for exponents < 10
|
||||
where we drop the leading zero
|
||||
*/
|
||||
Assert.equal(CanonicalJSON.stringify(.00099), "0.00099");
|
||||
Assert.equal(CanonicalJSON.stringify(.000011), "0.000011");
|
||||
Assert.equal(CanonicalJSON.stringify(.0000011), "0.0000011");
|
||||
Assert.equal(CanonicalJSON.stringify(.000001), "0.000001");
|
||||
Assert.equal(CanonicalJSON.stringify(.00000099), "9.9e-7");
|
||||
Assert.equal(CanonicalJSON.stringify(.0000001), "1e-7");
|
||||
Assert.equal(CanonicalJSON.stringify(.000000930258908), "9.30258908e-7");
|
||||
Assert.equal(CanonicalJSON.stringify(.00000000000068272), "6.8272e-13");
|
||||
Assert.equal(CanonicalJSON.stringify(Math.pow(10, 20)), "100000000000000000000");
|
||||
Assert.equal(CanonicalJSON.stringify(Math.pow(10, 21)), "1e+21");
|
||||
Assert.equal(CanonicalJSON.stringify(Math.pow(10, 15) + 0.1), "1000000000000000.1");
|
||||
Assert.equal(CanonicalJSON.stringify(Math.pow(10, 16) * 1.1), "11000000000000000");
|
||||
});
|
||||
|
|
|
@ -1711,8 +1711,6 @@ StartRemoteClient(const char* aDesktopStartupID,
|
|||
client = new XRemoteClient();
|
||||
}
|
||||
|
||||
// There are people who build Wayland without DBus...well
|
||||
// don't judge others for personal taste.
|
||||
nsresult rv = client ? client->Init() : NS_ERROR_FAILURE;
|
||||
if (NS_FAILED(rv))
|
||||
return REMOTE_NOT_FOUND;
|
||||
|
|
Загрузка…
Ссылка в новой задаче