зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to inbound. a=merge CLOSED TREE
This commit is contained in:
Коммит
9d9534d1c9
|
@ -342,6 +342,11 @@ void SessionAccessibility::ReplaceViewportCache(
|
|||
auto infos = jni::ObjectArray::New<java::GeckoBundle>(aAccessibles.Length());
|
||||
for (size_t i = 0; i < aAccessibles.Length(); i++) {
|
||||
AccessibleWrap* acc = aAccessibles.ElementAt(i);
|
||||
if (!acc) {
|
||||
MOZ_ASSERT_UNREACHABLE("Updated accessible is gone.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (aData.Length() == aAccessibles.Length()) {
|
||||
const BatchData& data = aData.ElementAt(i);
|
||||
auto bundle = acc->ToBundle(
|
||||
|
@ -363,6 +368,11 @@ void SessionAccessibility::ReplaceFocusPathCache(
|
|||
auto infos = jni::ObjectArray::New<java::GeckoBundle>(aAccessibles.Length());
|
||||
for (size_t i = 0; i < aAccessibles.Length(); i++) {
|
||||
AccessibleWrap* acc = aAccessibles.ElementAt(i);
|
||||
if (!acc) {
|
||||
MOZ_ASSERT_UNREACHABLE("Updated accessible is gone.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (aData.Length() == aAccessibles.Length()) {
|
||||
const BatchData& data = aData.ElementAt(i);
|
||||
nsCOMPtr<nsIPersistentProperties> props =
|
||||
|
|
|
@ -1009,7 +1009,11 @@ pref("browser.flash-protected-mode-flip.enable", false);
|
|||
pref("browser.flash-protected-mode-flip.done", false);
|
||||
|
||||
// Dark in-content pages
|
||||
#ifdef NIGHTLY_BUILD
|
||||
pref("browser.in-content.dark-mode", true);
|
||||
#else
|
||||
pref("browser.in-content.dark-mode", false);
|
||||
#endif
|
||||
|
||||
pref("dom.ipc.shims.enabledWarnings", false);
|
||||
|
||||
|
|
|
@ -4015,7 +4015,7 @@ const BrowserSearch = {
|
|||
// browser's offered engines.
|
||||
this._removeMaybeOfferedEngine(engineName);
|
||||
break;
|
||||
case "engine-current":
|
||||
case "engine-default":
|
||||
if (this._searchInitComplete) {
|
||||
this._updateURLBarPlaceholder(engineName);
|
||||
}
|
||||
|
|
|
@ -392,7 +392,7 @@ add_task(async function cycleEngines() {
|
|||
let promiseEngineChange = function(newEngineName) {
|
||||
return new Promise(resolve => {
|
||||
Services.obs.addObserver(function resolver(subj, topic, data) {
|
||||
if (data != "engine-current") {
|
||||
if (data != "engine-default") {
|
||||
return;
|
||||
}
|
||||
subj.QueryInterface(Ci.nsISearchEngine);
|
||||
|
|
|
@ -87,7 +87,7 @@ this.TopSitesFeed = class TopSitesFeed {
|
|||
observe(subj, topic, data) {
|
||||
// We should update the current top sites if the search engine has been changed since
|
||||
// the search engine that gets filtered out of top sites has changed.
|
||||
if (topic === "browser-search-engine-modified" && data === "engine-current" && this.store.getState().Prefs.values[NO_DEFAULT_SEARCH_TILE_EXP_PREF]) {
|
||||
if (topic === "browser-search-engine-modified" && data === "engine-default" && this.store.getState().Prefs.values[NO_DEFAULT_SEARCH_TILE_EXP_PREF]) {
|
||||
delete this._currentSearchHostname;
|
||||
this._currentSearchHostname = getShortURLForCurrentSearch();
|
||||
this.refresh({broadcast: true});
|
||||
|
|
|
@ -1164,7 +1164,7 @@ describe("Top Sites Feed", () => {
|
|||
it("should call refresh and set ._currentSearchHostname to the new engine hostname when the the default search engine has been set", () => {
|
||||
sinon.stub(feed, "refresh");
|
||||
sandbox.stub(global.Services.search, "defaultEngine").value({identifier: "ddg", searchForm: "duckduckgo.com"});
|
||||
feed.observe(null, "browser-search-engine-modified", "engine-current");
|
||||
feed.observe(null, "browser-search-engine-modified", "engine-default");
|
||||
assert.equal(feed._currentSearchHostname, "duckduckgo");
|
||||
assert.calledOnce(feed.refresh);
|
||||
});
|
||||
|
|
|
@ -260,18 +260,15 @@ var gSearchPane = {
|
|||
case "engine-removed":
|
||||
gSearchPane.remove(aEngine);
|
||||
break;
|
||||
case "engine-current":
|
||||
case "engine-default":
|
||||
// If the user is going through the drop down using up/down keys, the
|
||||
// dropdown may still be open (eg. on Windows) when engine-current is
|
||||
// dropdown may still be open (eg. on Windows) when engine-default is
|
||||
// fired, so rebuilding the list unconditionally would get in the way.
|
||||
let selectedEngine =
|
||||
document.getElementById("defaultEngine").selectedItem.engine;
|
||||
if (selectedEngine.name != aEngine.name)
|
||||
gSearchPane.buildDefaultEngineDropDown();
|
||||
break;
|
||||
case "engine-default":
|
||||
// Not relevant
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -366,7 +366,7 @@ class TelemetryHandler {
|
|||
let payload = `${info.provider}.in-content:${info.type}:${info.code || "none"}`;
|
||||
let histogram = Services.telemetry.getKeyedHistogramById(SEARCH_COUNTS_HISTOGRAM_KEY);
|
||||
histogram.add(payload);
|
||||
LOG(`SearchTelemetry::recordSearchURLTelemetry: ${payload} for ${url}`);
|
||||
LOG(`SearchTelemetry: ${payload} for ${url}`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -490,7 +490,7 @@ class ContentHandler {
|
|||
}
|
||||
|
||||
Services.telemetry.keyedScalarAdd(SEARCH_AD_CLICKS_SCALAR, info[0], 1);
|
||||
LOG(`SearchTelemetry::recordSearchURLTelemetry: Counting ad click in page for ${info[0]} ${triggerURI.spec}`);
|
||||
LOG(`SearchTelemetry: Counting ad click in page for ${info[0]} ${triggerURI.spec}`);
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
|
@ -531,7 +531,7 @@ class ContentHandler {
|
|||
}
|
||||
|
||||
Services.telemetry.keyedScalarAdd(SEARCH_WITH_ADS_SCALAR, item.info.provider, 1);
|
||||
LOG(`SearchTelemetry::recordSearchURLTelemetry: Counting ads in page for ${item.info.provider} ${info.url}`);
|
||||
LOG(`SearchTelemetry: Counting ads in page for ${item.info.provider} ${info.url}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ function promiseSetEngine() {
|
|||
ok(engine, "Engine was added.");
|
||||
ss.defaultEngine = engine;
|
||||
break;
|
||||
case "engine-current":
|
||||
case "engine-default":
|
||||
ok(ss.defaultEngine.name == "Bug 426329", "defaultEngine set");
|
||||
searchBar = BrowserSearch.searchBar;
|
||||
searchButton = searchBar.querySelector(".search-go-button");
|
||||
|
|
|
@ -17,7 +17,7 @@ function observer(aSubject, aTopic, aData) {
|
|||
if (gCurrentTest.added)
|
||||
method = "added";
|
||||
break;
|
||||
case "engine-current":
|
||||
case "engine-default":
|
||||
if (gCurrentTest.current)
|
||||
method = "current";
|
||||
break;
|
||||
|
|
|
@ -59,7 +59,7 @@ function test() {
|
|||
Services.search.defaultEngine = engine;
|
||||
break;
|
||||
|
||||
case "engine-current":
|
||||
case "engine-default":
|
||||
// We may be called again when resetting the engine at the end.
|
||||
if (!calledTestTelemetry) {
|
||||
is(Services.search.defaultEngine.name, "Foo", "Current engine is Foo");
|
||||
|
|
|
@ -112,7 +112,7 @@ add_task(async function test_urlBarChangeEngine() {
|
|||
function promisedefaultEngineChanged() {
|
||||
return new Promise(resolve => {
|
||||
function observer(aSub, aTopic, aData) {
|
||||
if (aData == "engine-current") {
|
||||
if (aData == "engine-default") {
|
||||
Assert.equal(Services.search.defaultEngine.name, TEST_ENGINE_NAME, "defaultEngine set");
|
||||
Services.obs.removeObserver(observer, "browser-search-engine-modified");
|
||||
resolve();
|
||||
|
|
|
@ -377,7 +377,7 @@ var tests = [
|
|||
await new Promise(resolve => {
|
||||
let observe = function(subject, topic, verb) {
|
||||
info("browser-search-engine-modified: " + verb);
|
||||
if (verb == "engine-current") {
|
||||
if (verb == "engine-default") {
|
||||
is(Services.search.defaultEngine.identifier, someOtherEngineID, "correct engine was switched to");
|
||||
done();
|
||||
}
|
||||
|
|
|
@ -461,12 +461,10 @@ var ContentSearch = {
|
|||
},
|
||||
|
||||
async _onObserve(data) {
|
||||
if (data === "engine-current") {
|
||||
if (data === "engine-default") {
|
||||
let engine = await this._currentEngineObj();
|
||||
this._broadcast("CurrentEngine", engine);
|
||||
} else if (data !== "engine-default") {
|
||||
// engine-default is always sent with engine-current and isn't otherwise
|
||||
// relevant to content searches.
|
||||
} else {
|
||||
let state = await this.currentStateObj();
|
||||
this._broadcast("CurrentState", state);
|
||||
}
|
||||
|
|
|
@ -77,14 +77,14 @@ add_task(async function SetDefaultEngine() {
|
|||
let deferred = PromiseUtils.defer();
|
||||
Services.obs.addObserver(function obs(subj, topic, data) {
|
||||
info("Test observed " + data);
|
||||
if (data == "engine-current") {
|
||||
ok(true, "Test observed engine-current");
|
||||
if (data == "engine-default") {
|
||||
ok(true, "Test observed engine-default");
|
||||
Services.obs.removeObserver(obs, "browser-search-engine-modified");
|
||||
deferred.resolve();
|
||||
}
|
||||
}, "browser-search-engine-modified");
|
||||
let searchPromise = waitForTestMsg(mm, "CurrentEngine");
|
||||
info("Waiting for test to observe engine-current...");
|
||||
info("Waiting for test to observe engine-default...");
|
||||
await deferred.promise;
|
||||
let msg = await searchPromise;
|
||||
checkMsg(msg, {
|
||||
|
|
|
@ -508,9 +508,8 @@ add_task(async function test_suggestion_rightclick() {
|
|||
});
|
||||
|
||||
add_task(async function test_privateWindow() {
|
||||
// Mock the search telemetry search provider info so that its
|
||||
// recordSearchURLTelemetry() function adds the in-content SEARCH_COUNTS
|
||||
// telemetry for our test engine.
|
||||
// Override the search telemetry search provider info to
|
||||
// count in-content SEARCH_COUNTs telemetry for our test engine.
|
||||
SearchTelemetry.overrideSearchTelemetryForTests({
|
||||
"example": {
|
||||
"regexp": "^http://example\\.com/",
|
||||
|
|
|
@ -88,7 +88,7 @@
|
|||
/* Override styling that sets descriptions as grey */
|
||||
#trackingGroup description.indent,
|
||||
#trackingGroup .indent > description {
|
||||
color: #000;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
[data-subcategory="trackingprotection"] {
|
||||
|
@ -101,8 +101,9 @@
|
|||
|
||||
#trackingProtectionShield {
|
||||
list-style-image: url("chrome://browser/skin/controlcenter/tracking-protection.svg");
|
||||
-moz-context-properties: fill;
|
||||
fill: #737373;
|
||||
-moz-context-properties: fill, fill-opacity;
|
||||
fill: currentColor;
|
||||
fill-opacity: 0.5;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin-inline-end: 10px;
|
||||
|
@ -112,7 +113,7 @@
|
|||
border-radius: 4px;
|
||||
margin: 3px 0;
|
||||
padding: 9px;
|
||||
border: 1px solid #D7D7DB;
|
||||
border: 1px solid var(--in-content-border-color);
|
||||
background-color: rgba(215, 215, 219, 0.2);
|
||||
}
|
||||
|
||||
|
|
|
@ -126,45 +126,26 @@ with only_when(target_is_osx):
|
|||
# MacOS SDK
|
||||
# =========
|
||||
|
||||
js_option('--with-macos-sdk', env='MACOS_SDK_DIR', nargs=1,
|
||||
help='Location of platform SDK to use')
|
||||
option('--with-macos-sdk', env='MACOS_SDK_DIR', nargs=1,
|
||||
help='Location of platform SDK to use')
|
||||
|
||||
@depends('--with-macos-sdk')
|
||||
@depends_if('--with-macos-sdk')
|
||||
@imports(_from='os.path', _import='isdir')
|
||||
@imports(_from='biplist', _import='readPlist')
|
||||
@imports('subprocess')
|
||||
@imports('which')
|
||||
@imports(_from='plistlib', _import='readPlist')
|
||||
def macos_sdk(value):
|
||||
sdk_min_version = Version('10.11')
|
||||
sdk_max_version = Version('10.13')
|
||||
sdk_max_version = Version('10.14')
|
||||
|
||||
sdk_path = None
|
||||
if value:
|
||||
sdk_path = value[0]
|
||||
else:
|
||||
try:
|
||||
xcrun = which.which('xcrun')
|
||||
args = [xcrun, '--sdk', 'macosx', '--show-sdk-path']
|
||||
sdk_path = subprocess.check_output(args).strip()
|
||||
except which.WhichError:
|
||||
# On a Linux cross-compile, we don't have xcrun, so just leave
|
||||
# the SDK unset if --with-macos-sdk isn't specified.
|
||||
pass
|
||||
|
||||
if not sdk_path:
|
||||
return
|
||||
|
||||
if not isdir(sdk_path):
|
||||
if not isdir(value[0]):
|
||||
die('SDK not found in %s. When using --with-macos-sdk, you must specify a '
|
||||
'valid SDK. SDKs are installed when the optional cross-development '
|
||||
'tools are selected during the Xcode/Developer Tools installation.'
|
||||
% sdk_path)
|
||||
obj = readPlist(os.path.join(sdk_path, 'SDKSettings.plist'))
|
||||
% value[0])
|
||||
obj = readPlist(os.path.join(value[0], 'SDKSettings.plist'))
|
||||
if not obj:
|
||||
die('Error parsing SDKSettings.plist in the SDK directory: %s' % sdk_path)
|
||||
die('Error parsing SDKSettings.plist in the SDK directory: %s' % value[0])
|
||||
if 'Version' not in obj:
|
||||
die('Error finding Version information in SDKSettings.plist from the '
|
||||
'SDK: %s' % sdk_path)
|
||||
die('Error finding Version information in SDKSettings.plist from the SDK: %s' % value[0])
|
||||
version = Version(obj['Version'])
|
||||
if version < sdk_min_version:
|
||||
die('SDK version "%s" is too old. Please upgrade to at least %s. '
|
||||
|
@ -176,7 +157,7 @@ with only_when(target_is_osx):
|
|||
'%s. You may need to point to it using --with-macos-sdk=<path> in '
|
||||
'your mozconfig. Various SDK versions are available from '
|
||||
'https://github.com/phracker/MacOSX-SDKs' % (version, sdk_max_version))
|
||||
return sdk_path
|
||||
return value[0]
|
||||
|
||||
set_config('MACOS_SDK_DIR', macos_sdk)
|
||||
|
||||
|
|
|
@ -106,6 +106,18 @@ declare module "debugger-html" {
|
|||
this: Object
|
||||
};
|
||||
|
||||
/**
|
||||
* Original Frame
|
||||
*
|
||||
* @memberof types
|
||||
* @static
|
||||
*/
|
||||
declare type OriginalFrame = {
|
||||
displayName: string,
|
||||
location?: SourceLocation,
|
||||
thread?: string
|
||||
};
|
||||
|
||||
/**
|
||||
* why
|
||||
* @memberof types
|
||||
|
|
|
@ -122,11 +122,11 @@
|
|||
"eslint-plugin-import": "^2.16.0",
|
||||
"eslint-plugin-jest": "^21.15.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.1.2",
|
||||
"eslint-plugin-mozilla": "1.1.3",
|
||||
"eslint-plugin-mozilla": "1.2.1",
|
||||
"eslint-plugin-prettier": "^3.0.0",
|
||||
"eslint-plugin-react": "^7.2.1",
|
||||
"expect.js": "^0.3.1",
|
||||
"flow-bin": "0.96.0",
|
||||
"flow-bin": "0.97.0",
|
||||
"glob": "^7.0.3",
|
||||
"husky": "^1.0.1",
|
||||
"jest": "^23.0.0",
|
||||
|
|
|
@ -8,7 +8,13 @@ const {
|
|||
workerUtils: { WorkerDispatcher }
|
||||
} = require("devtools-utils");
|
||||
|
||||
import type { SourceLocation, Source, SourceId } from "../../../src/types";
|
||||
import type {
|
||||
OriginalFrame,
|
||||
Range,
|
||||
SourceLocation,
|
||||
Source,
|
||||
SourceId
|
||||
} from "../../../src/types";
|
||||
import type { SourceMapConsumer } from "source-map";
|
||||
import type { locationOptions } from "./source-map";
|
||||
|
||||
|
@ -87,18 +93,7 @@ export const getGeneratedRangesForOriginal = async (
|
|||
sourceId: SourceId,
|
||||
url: string,
|
||||
mergeUnmappedRegions?: boolean
|
||||
): Promise<
|
||||
Array<{
|
||||
start: {
|
||||
line: number,
|
||||
column: number
|
||||
},
|
||||
end: {
|
||||
line: number,
|
||||
column: number
|
||||
}
|
||||
}>
|
||||
> =>
|
||||
): Promise<Range[]> =>
|
||||
dispatcher.invoke(
|
||||
"getGeneratedRangesForOriginal",
|
||||
sourceId,
|
||||
|
@ -108,8 +103,7 @@ export const getGeneratedRangesForOriginal = async (
|
|||
|
||||
export const getFileGeneratedRange = async (
|
||||
originalSource: Source
|
||||
): Promise<?{ start: any, end: any }> =>
|
||||
dispatcher.invoke("getFileGeneratedRange", originalSource);
|
||||
): Promise<Range> => dispatcher.invoke("getFileGeneratedRange", originalSource);
|
||||
|
||||
export const getLocationScopes = dispatcher.task("getLocationScopes");
|
||||
|
||||
|
@ -137,10 +131,8 @@ export const hasMappedSource = async (
|
|||
|
||||
export const getOriginalStackFrames = async (
|
||||
generatedLocation: SourceLocation
|
||||
): Promise<?Array<{
|
||||
displayName: string,
|
||||
location?: SourceLocation
|
||||
}>> => dispatcher.invoke("getOriginalStackFrames", generatedLocation);
|
||||
): Promise<?Array<OriginalFrame>> =>
|
||||
dispatcher.invoke("getOriginalStackFrames", generatedLocation);
|
||||
|
||||
export {
|
||||
originalToGeneratedId,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
// @flow
|
||||
|
||||
import type { SourceLocation } from "debugger-html";
|
||||
import type { OriginalFrame, SourceLocation } from "debugger-html";
|
||||
|
||||
const { getWasmXScopes } = require("./wasmXScopes");
|
||||
|
||||
|
@ -12,10 +12,7 @@ const { getWasmXScopes } = require("./wasmXScopes");
|
|||
// The function return null if not information was found.
|
||||
async function getOriginalStackFrames(
|
||||
generatedLocation: SourceLocation
|
||||
): Promise<?Array<{
|
||||
displayName: string,
|
||||
location?: SourceLocation
|
||||
}>> {
|
||||
): Promise<?Array<OriginalFrame>> {
|
||||
const wasmXScopes = await getWasmXScopes(generatedLocation.sourceId);
|
||||
if (!wasmXScopes) {
|
||||
return null;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// @flow
|
||||
/* eslint camelcase: 0*/
|
||||
|
||||
import type { SourceLocation, SourceId } from "debugger-html";
|
||||
import type { OriginalFrame, SourceLocation, SourceId } from "debugger-html";
|
||||
|
||||
const { getSourceMap } = require("./sourceMapRequests");
|
||||
const { generatedToOriginalId } = require("./index");
|
||||
|
@ -153,12 +153,7 @@ class XScope {
|
|||
this.xScope = xScopeData;
|
||||
}
|
||||
|
||||
search(
|
||||
generatedLocation: SourceLocation
|
||||
): Array<{
|
||||
displayName: string,
|
||||
location?: SourceLocation
|
||||
}> {
|
||||
search(generatedLocation: SourceLocation): Array<OriginalFrame> {
|
||||
const { code_section_offset, debug_info, sources, idIndex } = this.xScope;
|
||||
const pc = generatedLocation.line - (code_section_offset || 0);
|
||||
const scopes = filterScopes(debug_info, pc, null, idIndex);
|
||||
|
|
|
@ -4,7 +4,10 @@
|
|||
|
||||
// @flow
|
||||
|
||||
import { isOriginalId, originalToGeneratedId } from "devtools-source-map";
|
||||
import SourceMaps, {
|
||||
isOriginalId,
|
||||
originalToGeneratedId
|
||||
} from "devtools-source-map";
|
||||
import { uniqBy, zip } from "lodash";
|
||||
|
||||
import {
|
||||
|
@ -16,6 +19,7 @@ import {
|
|||
|
||||
import type {
|
||||
MappedLocation,
|
||||
Range,
|
||||
SourceLocation,
|
||||
BreakpointPositions,
|
||||
Context
|
||||
|
@ -26,13 +30,11 @@ import {
|
|||
type MemoizedAction
|
||||
} from "../../utils/memoizableAction";
|
||||
|
||||
import typeof SourceMaps from "../../../packages/devtools-source-map/src";
|
||||
|
||||
// const requests = new Map();
|
||||
|
||||
async function mapLocations(
|
||||
generatedLocations: SourceLocation[],
|
||||
{ sourceMaps }: { sourceMaps: SourceMaps }
|
||||
{ sourceMaps }: { sourceMaps: typeof SourceMaps }
|
||||
) {
|
||||
const originalLocations = await sourceMaps.getOriginalLocations(
|
||||
generatedLocations
|
||||
|
@ -82,7 +84,9 @@ async function _setBreakpointPositions(cx, sourceId, thunkArgs) {
|
|||
|
||||
let results = {};
|
||||
if (isOriginalId(sourceId)) {
|
||||
const ranges = await sourceMaps.getGeneratedRangesForOriginal(
|
||||
// Explicitly typing ranges is required to work around the following issue
|
||||
// https://github.com/facebook/flow/issues/5294
|
||||
const ranges: Range[] = await sourceMaps.getGeneratedRangesForOriginal(
|
||||
sourceId,
|
||||
generatedSource.url,
|
||||
true
|
||||
|
@ -98,8 +102,10 @@ async function _setBreakpointPositions(cx, sourceId, thunkArgs) {
|
|||
// and because we know we don't care about the end-line whitespace
|
||||
// in this case.
|
||||
if (range.end.column === Infinity) {
|
||||
range.end.line += 1;
|
||||
range.end.column = 0;
|
||||
range.end = {
|
||||
line: range.end.line + 1,
|
||||
column: 0
|
||||
};
|
||||
}
|
||||
|
||||
const bps = await client.getBreakpointPositions(generatedSource, range);
|
||||
|
|
|
@ -4,12 +4,14 @@
|
|||
|
||||
// @flow
|
||||
|
||||
import typeof SourceMaps from "devtools-source-map";
|
||||
|
||||
import type { Breakpoint } from "../../types";
|
||||
|
||||
export default function remapLocations(
|
||||
breakpoints: Breakpoint[],
|
||||
sourceId: string,
|
||||
sourceMaps: Object
|
||||
sourceMaps: SourceMaps
|
||||
) {
|
||||
const sourceBreakpoints: Promise<Breakpoint>[] = breakpoints.map(
|
||||
async breakpoint => {
|
||||
|
|
|
@ -16,11 +16,11 @@ import assert from "../../utils/assert";
|
|||
import { findClosestFunction } from "../../utils/ast";
|
||||
import { setSymbols } from "../sources/symbols";
|
||||
|
||||
import type { Frame, ThreadContext } from "../../types";
|
||||
import type { Frame, OriginalFrame, ThreadContext } from "../../types";
|
||||
import type { State } from "../../reducers/types";
|
||||
import type { ThunkArgs } from "../types";
|
||||
|
||||
import { isGeneratedId } from "devtools-source-map";
|
||||
import SourceMaps, { isGeneratedId } from "devtools-source-map";
|
||||
|
||||
function isFrameBlackboxed(state, frame) {
|
||||
const source = getSource(state, frame.location.sourceId);
|
||||
|
@ -37,7 +37,10 @@ function getSelectedFrameId(state, thread, frames) {
|
|||
return selectedFrame && selectedFrame.id;
|
||||
}
|
||||
|
||||
export function updateFrameLocation(frame: Frame, sourceMaps: any) {
|
||||
export function updateFrameLocation(
|
||||
frame: Frame,
|
||||
sourceMaps: typeof SourceMaps
|
||||
) {
|
||||
if (frame.isOriginal) {
|
||||
return Promise.resolve(frame);
|
||||
}
|
||||
|
@ -50,7 +53,7 @@ export function updateFrameLocation(frame: Frame, sourceMaps: any) {
|
|||
|
||||
function updateFrameLocations(
|
||||
frames: Frame[],
|
||||
sourceMaps: any
|
||||
sourceMaps: typeof SourceMaps
|
||||
): Promise<Frame[]> {
|
||||
if (!frames || frames.length == 0) {
|
||||
return Promise.resolve(frames);
|
||||
|
@ -107,7 +110,7 @@ function isWasmOriginalSourceFrame(frame, getState: () => State): boolean {
|
|||
|
||||
async function expandFrames(
|
||||
frames: Frame[],
|
||||
sourceMaps: any,
|
||||
sourceMaps: typeof SourceMaps,
|
||||
getState: () => State
|
||||
): Promise<Frame[]> {
|
||||
const result = [];
|
||||
|
@ -117,9 +120,9 @@ async function expandFrames(
|
|||
result.push(frame);
|
||||
continue;
|
||||
}
|
||||
const originalFrames = await sourceMaps.getOriginalStackFrames(
|
||||
frame.generatedLocation
|
||||
);
|
||||
const originalFrames: ?Array<
|
||||
OriginalFrame
|
||||
> = await sourceMaps.getOriginalStackFrames(frame.generatedLocation);
|
||||
if (!originalFrames) {
|
||||
result.push(frame);
|
||||
continue;
|
||||
|
@ -133,6 +136,10 @@ async function expandFrames(
|
|||
};
|
||||
|
||||
originalFrames.forEach((originalFrame, j) => {
|
||||
if (!originalFrame.location || !originalFrame.thread) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep outer most frame with true actor ID, and generate uniquie
|
||||
// one for the nested frames.
|
||||
const id = j == 0 ? frame.id : `${frame.id}-originalFrame${j}`;
|
||||
|
|
|
@ -101,8 +101,10 @@ function loadSourceMap(cx: Context, sourceId: SourceId) {
|
|||
|
||||
let urls = null;
|
||||
try {
|
||||
const urlInfo = { ...source };
|
||||
if (!urlInfo.url) {
|
||||
// Unable to correctly type the result of a spread on a union type.
|
||||
// See https://github.com/facebook/flow/pull/7298
|
||||
const urlInfo: Source = { ...(source: any) };
|
||||
if (!urlInfo.url && typeof urlInfo.introductionUrl === "string") {
|
||||
// If the source was dynamically generated (via eval, dynamically
|
||||
// created script elements, and so forth), it won't have a URL, so that
|
||||
// it is not collapsed into other sources from the same place. The
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
// @flow
|
||||
import { generatedToOriginalId } from "devtools-source-map";
|
||||
import SourceMaps, { generatedToOriginalId } from "devtools-source-map";
|
||||
|
||||
import assert from "../../utils/assert";
|
||||
import { recordEvent } from "../../utils/telemetry";
|
||||
|
@ -29,7 +29,7 @@ import { selectSource } from "./select";
|
|||
import type { JsSource, Source, Context } from "../../types";
|
||||
|
||||
export async function prettyPrintSource(
|
||||
sourceMaps: any,
|
||||
sourceMaps: typeof SourceMaps,
|
||||
prettySource: Source,
|
||||
generatedSource: any
|
||||
) {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
// @flow
|
||||
|
||||
import typeof SourceMaps from "devtools-source-map";
|
||||
import type { WorkerList, MainThread, Context, ThreadId } from "../../types";
|
||||
import type { State } from "../../reducers/types";
|
||||
import type { MatchedLocations } from "../../reducers/file-search";
|
||||
|
@ -34,7 +35,7 @@ export type ThunkArgs = {
|
|||
dispatch: (action: any) => Promise<any>,
|
||||
getState: () => State,
|
||||
client: typeof clientCommands,
|
||||
sourceMaps: any,
|
||||
sourceMaps: SourceMaps,
|
||||
panel: Panel
|
||||
};
|
||||
|
||||
|
|
|
@ -152,6 +152,7 @@ class App extends Component<Props, State> {
|
|||
closeQuickOpen,
|
||||
quickOpenEnabled
|
||||
} = this.props;
|
||||
const { shortcutsModalEnabled } = this.state;
|
||||
|
||||
if (activeSearch) {
|
||||
e.preventDefault();
|
||||
|
@ -162,6 +163,10 @@ class App extends Component<Props, State> {
|
|||
e.preventDefault();
|
||||
closeQuickOpen();
|
||||
}
|
||||
|
||||
if (shortcutsModalEnabled) {
|
||||
this.toggleShortcutsModal();
|
||||
}
|
||||
};
|
||||
|
||||
onCommandSlash = () => {
|
||||
|
|
|
@ -18,18 +18,27 @@
|
|||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.source-footer .commands {
|
||||
.source-footer-start {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-self: start;
|
||||
}
|
||||
|
||||
.source-footer-end {
|
||||
display: flex;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.source-footer .commands * {
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.source-footer > .commands > .action {
|
||||
.source-footer > .source-footer-start > .commands {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.source-footer > .source-footer-start > .commands > .action {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
@ -39,25 +48,33 @@
|
|||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
.source-footer > .commands > button.action:hover {
|
||||
.source-footer > .source-footer-start > .commands > button.action:hover {
|
||||
background: var(--theme-toolbar-background-hover);
|
||||
}
|
||||
|
||||
:root.theme-dark .source-footer > .commands > .action {
|
||||
:root.theme-dark .source-footer > .source-footer-start > .commands > .action {
|
||||
fill: var(--theme-body-color);
|
||||
}
|
||||
|
||||
:root.theme-dark .source-footer > .commands > .action:hover {
|
||||
:root.theme-dark
|
||||
.source-footer
|
||||
> .source-footer-start
|
||||
> .commands
|
||||
> .action:hover {
|
||||
fill: var(--theme-selection-color);
|
||||
}
|
||||
|
||||
.source-footer > .commands > div.loader {
|
||||
.source-footer > .source-footer-start > .commands > div.loader {
|
||||
vertical-align: top;
|
||||
width: 20px;
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
.source-footer > .commands > .blackboxed > .img.blackBox {
|
||||
.source-footer
|
||||
> .source-footer-start
|
||||
> .commands
|
||||
> .blackboxed
|
||||
> .img.blackBox {
|
||||
background-color: var(--theme-icon-checked-color);
|
||||
}
|
||||
|
||||
|
@ -65,7 +82,6 @@
|
|||
.source-footer .cursor-position {
|
||||
color: var(--theme-body-color);
|
||||
padding-right: 2.5px;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.source-footer .mapped-source {
|
||||
|
|
|
@ -166,7 +166,6 @@ class SourceFooter extends PureComponent<Props, State> {
|
|||
|
||||
return (
|
||||
<PaneToggleButton
|
||||
position="end"
|
||||
key="toggle"
|
||||
collapsed={this.props.endPanelCollapsed}
|
||||
horizontal={this.props.horizontal}
|
||||
|
@ -249,10 +248,12 @@ class SourceFooter extends PureComponent<Props, State> {
|
|||
render() {
|
||||
return (
|
||||
<div className="source-footer">
|
||||
{this.renderCommands()}
|
||||
{this.renderSourceSummary()}
|
||||
{this.renderCursorPosition()}
|
||||
{this.renderToggleButton()}
|
||||
<div className="source-footer-start">{this.renderCommands()}</div>
|
||||
<div className="source-footer-end">
|
||||
{this.renderSourceSummary()}
|
||||
{this.renderCursorPosition()}
|
||||
{this.renderToggleButton()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,17 +5,24 @@ exports[`SourceFooter Component default case should render 1`] = `
|
|||
className="source-footer"
|
||||
>
|
||||
<div
|
||||
className="cursor-position"
|
||||
title="(Line 1, column 1)"
|
||||
>
|
||||
(1, 1)
|
||||
</div>
|
||||
<PaneToggleButton
|
||||
collapsed={false}
|
||||
horizontal={false}
|
||||
key="toggle"
|
||||
position="end"
|
||||
className="source-footer-start"
|
||||
/>
|
||||
<div
|
||||
className="source-footer-end"
|
||||
>
|
||||
<div
|
||||
className="cursor-position"
|
||||
title="(Line 1, column 1)"
|
||||
>
|
||||
(1, 1)
|
||||
</div>
|
||||
<PaneToggleButton
|
||||
collapsed={false}
|
||||
horizontal={false}
|
||||
key="toggle"
|
||||
position="start"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
@ -24,16 +31,23 @@ exports[`SourceFooter Component move cursor should render new cursor position 1`
|
|||
className="source-footer"
|
||||
>
|
||||
<div
|
||||
className="cursor-position"
|
||||
title="(Line 6, column 11)"
|
||||
>
|
||||
(6, 11)
|
||||
</div>
|
||||
<PaneToggleButton
|
||||
collapsed={false}
|
||||
horizontal={false}
|
||||
key="toggle"
|
||||
position="end"
|
||||
className="source-footer-start"
|
||||
/>
|
||||
<div
|
||||
className="source-footer-end"
|
||||
>
|
||||
<div
|
||||
className="cursor-position"
|
||||
title="(Line 6, column 11)"
|
||||
>
|
||||
(6, 11)
|
||||
</div>
|
||||
<PaneToggleButton
|
||||
collapsed={false}
|
||||
horizontal={false}
|
||||
key="toggle"
|
||||
position="start"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -71,10 +71,12 @@ type GotoLocationType = {
|
|||
column?: number
|
||||
};
|
||||
|
||||
const maxResults = 100;
|
||||
|
||||
function filter(values, query) {
|
||||
return fuzzyAldrin.filter(values, query, {
|
||||
key: "value",
|
||||
maxResults: 1000
|
||||
maxResults: maxResults
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -84,6 +86,13 @@ export class QuickOpenModal extends Component<Props, State> {
|
|||
this.state = { results: null, selectedIndex: 0 };
|
||||
}
|
||||
|
||||
setResults(results: ?Array<QuickOpenResult>) {
|
||||
if (results) {
|
||||
results = results.slice(0, maxResults);
|
||||
}
|
||||
this.setState({ results });
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { query, shortcutsModalEnabled, toggleShortcutsModal } = this.props;
|
||||
|
||||
|
@ -123,7 +132,7 @@ export class QuickOpenModal extends Component<Props, State> {
|
|||
const { sources } = this.props;
|
||||
const results =
|
||||
query == "" ? sources : filter(sources, this.dropGoto(query));
|
||||
return this.setState({ results });
|
||||
return this.setResults(results);
|
||||
};
|
||||
|
||||
searchSymbols = (query: string) => {
|
||||
|
@ -135,18 +144,18 @@ export class QuickOpenModal extends Component<Props, State> {
|
|||
results = results.filter(result => result.title !== "anonymous");
|
||||
|
||||
if (query === "@" || query === "#") {
|
||||
return this.setState({ results });
|
||||
return this.setResults(results);
|
||||
}
|
||||
|
||||
this.setState({ results: filter(results, query.slice(1)) });
|
||||
results = filter(results, query.slice(1));
|
||||
return this.setResults(results);
|
||||
};
|
||||
|
||||
searchShortcuts = (query: string) => {
|
||||
const results = formatShortcutResults();
|
||||
if (query == "?") {
|
||||
this.setState({ results });
|
||||
this.setResults(results);
|
||||
} else {
|
||||
this.setState({ results: filter(results, query.slice(1)) });
|
||||
this.setResults(filter(results, query.slice(1)));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -154,12 +163,9 @@ export class QuickOpenModal extends Component<Props, State> {
|
|||
const { tabs, sources } = this.props;
|
||||
if (tabs.length > 0) {
|
||||
const tabUrls = tabs.map((tab: Tab) => tab.url);
|
||||
|
||||
this.setState({
|
||||
results: sources.filter(source => tabUrls.includes(source.url))
|
||||
});
|
||||
this.setResults(sources.filter(source => tabUrls.includes(source.url)));
|
||||
} else {
|
||||
this.setState({ results: sources.slice(0, 100) });
|
||||
this.setResults(sources);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -376,8 +382,7 @@ export class QuickOpenModal extends Component<Props, State> {
|
|||
if (!enabled) {
|
||||
return null;
|
||||
}
|
||||
const newResults = results && results.slice(0, 100);
|
||||
const items = this.highlightMatching(query, newResults || []);
|
||||
const items = this.highlightMatching(query, results || []);
|
||||
const expanded = !!items && items.length > 0;
|
||||
|
||||
return (
|
||||
|
@ -400,7 +405,7 @@ export class QuickOpenModal extends Component<Props, State> {
|
|||
}
|
||||
{...(this.isSourceSearch() ? { size: "big" } : {})}
|
||||
/>
|
||||
{newResults && (
|
||||
{results && (
|
||||
<ResultList
|
||||
key="results"
|
||||
items={items}
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
* 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/>. */
|
||||
|
||||
.breakpoints-pane > ._content {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.breakpoints-toggle {
|
||||
margin: 2px 3px;
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
/* TODO: add normalize */
|
||||
margin: 0;
|
||||
padding: 4px 0px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.expression-input-container {
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
.secondary-panes {
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
* 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/>. */
|
||||
|
||||
.xhr-breakpoints-pane ._content {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.xhr-input-container {
|
||||
display: flex;
|
||||
border: 1px solid transparent;
|
||||
|
|
|
@ -211,7 +211,7 @@ describe("QuickOpenModal", () => {
|
|||
wrapper.find("input").simulate("change", { target: { value: "somefil" } });
|
||||
expect(filter).toHaveBeenCalledWith([], "somefil", {
|
||||
key: "value",
|
||||
maxResults: 1000
|
||||
maxResults: 100
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -232,7 +232,7 @@ describe("QuickOpenModal", () => {
|
|||
.simulate("change", { target: { value: "somefil:33" } });
|
||||
expect(filter).toHaveBeenCalledWith([], "somefil", {
|
||||
key: "value",
|
||||
maxResults: 1000
|
||||
maxResults: 100
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -259,7 +259,7 @@ describe("QuickOpenModal", () => {
|
|||
|
||||
expect(filter).toHaveBeenCalledWith([], "someFunc", {
|
||||
key: "value",
|
||||
maxResults: 1000
|
||||
maxResults: 100
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -235,6 +235,12 @@ export type ChromeFrame = {
|
|||
location: ?SourceLocation
|
||||
};
|
||||
|
||||
export type OriginalFrame = {
|
||||
displayName: string,
|
||||
location?: SourceLocation,
|
||||
thread: string
|
||||
};
|
||||
|
||||
/**
|
||||
* ContextMenuItem
|
||||
*
|
||||
|
|
|
@ -10,7 +10,10 @@ import ReactDOM from "react-dom";
|
|||
const { Provider } = require("react-redux");
|
||||
|
||||
import { isFirefoxPanel, isDevelopment, isTesting } from "devtools-environment";
|
||||
import { startSourceMapWorker, stopSourceMapWorker } from "devtools-source-map";
|
||||
import SourceMaps, {
|
||||
startSourceMapWorker,
|
||||
stopSourceMapWorker
|
||||
} from "devtools-source-map";
|
||||
import * as search from "../workers/search";
|
||||
import * as prettyPrint from "../workers/pretty-print";
|
||||
import * as parser from "../workers/parser";
|
||||
|
@ -22,7 +25,6 @@ import App from "../components/App";
|
|||
import { asyncStore, prefs } from "./prefs";
|
||||
|
||||
import type { Panel } from "../client/firefox/types";
|
||||
import typeof SourceMaps from "../../packages/devtools-source-map/src";
|
||||
|
||||
function renderPanel(component, store) {
|
||||
const root = document.createElement("div");
|
||||
|
@ -42,7 +44,7 @@ function renderPanel(component, store) {
|
|||
|
||||
export function bootstrapStore(
|
||||
client: any,
|
||||
sourceMaps: SourceMaps,
|
||||
sourceMaps: typeof SourceMaps,
|
||||
panel: Panel,
|
||||
initialState: Object
|
||||
) {
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
// @flow
|
||||
|
||||
import typeof SourceMaps from "devtools-source-map";
|
||||
|
||||
import type { BindingLocationType, BindingType } from "../../../workers/parser";
|
||||
import { positionCmp } from "./positionCmp";
|
||||
import { filterSortedArray } from "./filtering";
|
||||
|
@ -34,7 +36,7 @@ export async function originalRangeStartsInside(
|
|||
start: SourceLocation,
|
||||
end: SourceLocation
|
||||
},
|
||||
sourceMaps: any
|
||||
sourceMaps: SourceMaps
|
||||
) {
|
||||
const endPosition = await sourceMaps.getGeneratedLocation(end, source);
|
||||
const startPosition = await sourceMaps.getGeneratedLocation(start, source);
|
||||
|
@ -58,11 +60,11 @@ export async function getApplicableBindingsForOriginalPosition(
|
|||
},
|
||||
bindingType: BindingType,
|
||||
locationType: BindingLocationType,
|
||||
sourceMaps: any
|
||||
sourceMaps: SourceMaps
|
||||
): Promise<Array<ApplicableBinding>> {
|
||||
const ranges = await sourceMaps.getGeneratedRanges(start, source);
|
||||
|
||||
const resultRanges = ranges.map(mapRange => ({
|
||||
const resultRanges: GeneratedRange[] = ranges.map(mapRange => ({
|
||||
start: {
|
||||
line: mapRange.line,
|
||||
column: mapRange.columnStart
|
||||
|
@ -91,8 +93,10 @@ export async function getApplicableBindingsForOriginalPosition(
|
|||
mappingContains(range, { start: startPosition, end: startPosition }) &&
|
||||
positionCmp(range.end, endPosition) < 0
|
||||
) {
|
||||
range.end.line = endPosition.line;
|
||||
range.end.column = endPosition.column;
|
||||
range.end = {
|
||||
line: endPosition.line,
|
||||
column: endPosition.column
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
// @flow
|
||||
|
||||
import typeof SourceMaps from "devtools-source-map";
|
||||
|
||||
import {
|
||||
getScopes,
|
||||
type SourceScope,
|
||||
|
@ -200,7 +202,7 @@ function hasLineMappings(ranges) {
|
|||
function batchScopeMappings(
|
||||
originalAstScopes: Array<SourceScope>,
|
||||
source: Source,
|
||||
sourceMaps: any
|
||||
sourceMaps: SourceMaps
|
||||
) {
|
||||
const precalculatedRanges = new Map();
|
||||
const precalculatedLocations = new Map();
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
// @flow
|
||||
|
||||
import typeof SourceMaps from "devtools-source-map";
|
||||
|
||||
import { locColumn } from "./locColumn";
|
||||
import { positionCmp } from "./positionCmp";
|
||||
import { filterSortedArray } from "./filtering";
|
||||
|
@ -34,7 +36,7 @@ export async function loadRangeMetadata(
|
|||
source: Source,
|
||||
frame: Frame,
|
||||
originalAstScopes: Array<SourceScope>,
|
||||
sourceMaps: any
|
||||
sourceMaps: SourceMaps
|
||||
): Promise<Array<MappedOriginalRange>> {
|
||||
const originalRanges: Array<
|
||||
SourceOriginalRange
|
||||
|
|
|
@ -4,17 +4,16 @@
|
|||
|
||||
// @flow
|
||||
|
||||
import { isOriginalId } from "devtools-source-map";
|
||||
import SourceMaps, { isOriginalId } from "devtools-source-map";
|
||||
import { getSource } from "../selectors";
|
||||
|
||||
import type { SourceLocation, MappedLocation, Source } from "../types";
|
||||
import typeof SourceMaps from "../../packages/devtools-source-map/src";
|
||||
|
||||
export async function getGeneratedLocation(
|
||||
state: Object,
|
||||
source: Source,
|
||||
location: SourceLocation,
|
||||
sourceMaps: Object
|
||||
sourceMaps: typeof SourceMaps
|
||||
): Promise<SourceLocation> {
|
||||
if (!isOriginalId(location.sourceId)) {
|
||||
return location;
|
||||
|
@ -40,7 +39,7 @@ export async function getGeneratedLocation(
|
|||
|
||||
export async function getOriginalLocation(
|
||||
generatedLocation: SourceLocation,
|
||||
sourceMaps: SourceMaps
|
||||
sourceMaps: typeof SourceMaps
|
||||
) {
|
||||
if (isOriginalId(generatedLocation.sourceId)) {
|
||||
return location;
|
||||
|
@ -51,7 +50,7 @@ export async function getOriginalLocation(
|
|||
|
||||
export async function getMappedLocation(
|
||||
state: Object,
|
||||
sourceMaps: Object,
|
||||
sourceMaps: typeof SourceMaps,
|
||||
location: SourceLocation
|
||||
): Promise<MappedLocation> {
|
||||
const source = getSource(state, location.sourceId);
|
||||
|
@ -72,8 +71,7 @@ export async function getMappedLocation(
|
|||
|
||||
const generatedLocation = location;
|
||||
const originalLocation = await sourceMaps.getOriginalLocation(
|
||||
generatedLocation,
|
||||
source
|
||||
generatedLocation
|
||||
);
|
||||
|
||||
return { location: originalLocation, generatedLocation };
|
||||
|
@ -81,7 +79,7 @@ export async function getMappedLocation(
|
|||
|
||||
export async function mapLocation(
|
||||
state: Object,
|
||||
sourceMaps: Object,
|
||||
sourceMaps: typeof SourceMaps,
|
||||
location: SourceLocation
|
||||
): Promise<SourceLocation> {
|
||||
const source = getSource(state, location.sourceId);
|
||||
|
@ -94,7 +92,7 @@ export async function mapLocation(
|
|||
return getGeneratedLocation(state, source, location, sourceMaps);
|
||||
}
|
||||
|
||||
return sourceMaps.getOriginalLocation(location, source);
|
||||
return sourceMaps.getOriginalLocation(location);
|
||||
}
|
||||
|
||||
export function isOriginalSource(source: ?Source) {
|
||||
|
|
|
@ -48,7 +48,8 @@ function findOrCreateNode(
|
|||
part: string,
|
||||
index: number,
|
||||
url: Object,
|
||||
debuggeeHost: ?string
|
||||
debuggeeHost: ?string,
|
||||
source: Source
|
||||
): TreeDirectory {
|
||||
const addedPartIsFile = partIsFile(index, parts, url);
|
||||
|
||||
|
@ -69,7 +70,12 @@ function findOrCreateNode(
|
|||
|
||||
// if we have a naming conflict, we'll create a new node
|
||||
if (child.type === "source" || (!childIsFile && addedPartIsFile)) {
|
||||
return createNodeInTree(part, path, subTree, childIndex);
|
||||
// pass true to findNodeInContents to sort node by url
|
||||
const { index: insertIndex } = findNodeInContents(
|
||||
subTree,
|
||||
createTreeNodeMatcher(part, !addedPartIsFile, debuggeeHost, source, true)
|
||||
);
|
||||
return createNodeInTree(part, path, subTree, insertIndex);
|
||||
}
|
||||
|
||||
// if there is no naming conflict, we can traverse into the child
|
||||
|
@ -83,7 +89,8 @@ function findOrCreateNode(
|
|||
function traverseTree(
|
||||
url: ParsedURL,
|
||||
tree: TreeDirectory,
|
||||
debuggeeHost: ?string
|
||||
debuggeeHost: ?string,
|
||||
source: Source
|
||||
): TreeNode {
|
||||
const parts = url.path.split("/").filter(p => p !== "");
|
||||
parts.unshift(url.group);
|
||||
|
@ -99,7 +106,8 @@ function traverseTree(
|
|||
part,
|
||||
index,
|
||||
url,
|
||||
debuggeeHostIfRoot
|
||||
debuggeeHostIfRoot,
|
||||
source
|
||||
);
|
||||
}, tree);
|
||||
}
|
||||
|
@ -165,7 +173,7 @@ export function addToTree(
|
|||
return;
|
||||
}
|
||||
|
||||
const finalNode = traverseTree(url, tree, debuggeeHost);
|
||||
const finalNode = traverseTree(url, tree, debuggeeHost, source);
|
||||
|
||||
// $FlowIgnore
|
||||
finalNode.contents = addSourceToNode(finalNode, url, source);
|
||||
|
|
|
@ -10,6 +10,8 @@ import { nodeHasChildren } from "./utils";
|
|||
|
||||
import type { TreeNode } from "./types";
|
||||
|
||||
import type { Source } from "../../types";
|
||||
|
||||
/*
|
||||
* Gets domain from url (without www prefix)
|
||||
*/
|
||||
|
@ -94,7 +96,9 @@ function createTreeNodeMatcherWithDebuggeeHost(
|
|||
function createTreeNodeMatcherWithNameAndOther(
|
||||
part: string,
|
||||
isDir: boolean,
|
||||
debuggeeHost: ?string
|
||||
debuggeeHost: ?string,
|
||||
source?: Source,
|
||||
sortByUrl?: boolean
|
||||
): FindNodeInContentsMatcher {
|
||||
return (node: TreeNode) => {
|
||||
if (node.name === IndexName) {
|
||||
|
@ -109,6 +113,9 @@ function createTreeNodeMatcherWithNameAndOther(
|
|||
} else if (!nodeIsDir && isDir) {
|
||||
return 1;
|
||||
}
|
||||
if (sortByUrl && node.type === "source" && source) {
|
||||
return node.contents.url.localeCompare(source.url);
|
||||
}
|
||||
|
||||
return node.name.localeCompare(part);
|
||||
};
|
||||
|
@ -125,7 +132,9 @@ function createTreeNodeMatcherWithNameAndOther(
|
|||
export function createTreeNodeMatcher(
|
||||
part: string,
|
||||
isDir: boolean,
|
||||
debuggeeHost: ?string
|
||||
debuggeeHost: ?string,
|
||||
source?: Source,
|
||||
sortByUrl?: boolean
|
||||
): FindNodeInContentsMatcher {
|
||||
if (part === IndexName) {
|
||||
// Specialied matcher, when we are looking for "(index)" position.
|
||||
|
@ -138,5 +147,11 @@ export function createTreeNodeMatcher(
|
|||
}
|
||||
|
||||
// Rest of the cases, without mentioned above.
|
||||
return createTreeNodeMatcherWithNameAndOther(part, isDir, debuggeeHost);
|
||||
return createTreeNodeMatcherWithNameAndOther(
|
||||
part,
|
||||
isDir,
|
||||
debuggeeHost,
|
||||
source,
|
||||
sortByUrl
|
||||
);
|
||||
}
|
||||
|
|
|
@ -27,9 +27,14 @@ add_task(async function() {
|
|||
|
||||
const labels = [getLabel(dbg, 4), getLabel(dbg, 3)];
|
||||
is(
|
||||
labels.includes("simple1.js?x=1") && labels.includes("simple1.js?x=2"),
|
||||
true,
|
||||
"simple1.js?x=1 and simple2.jsx=2 exist"
|
||||
getLabel(dbg, 3),
|
||||
"simple1.js?x=1",
|
||||
"simple1.js?x=1 exists"
|
||||
);
|
||||
is(
|
||||
getLabel(dbg, 4),
|
||||
"simple1.js?x=2",
|
||||
"simple1.js?x=2 exists"
|
||||
);
|
||||
|
||||
const source = findSource(dbg, "simple1.js?x=1");
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
"description": "Network monitor in developer tools",
|
||||
"dependencies": {
|
||||
"babel-plugin-transform-flow-strip-types": "^6.22.0",
|
||||
"babel-plugin-transform-react-jsx": "^6.24.1",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||
"babel-plugin-transform-react-jsx": "^6.24.1",
|
||||
"codemirror": "^5.24.2",
|
||||
"devtools-config": "=0.0.12",
|
||||
"devtools-contextmenu": "=0.0.3",
|
||||
|
@ -25,6 +25,8 @@
|
|||
"reselect": "^3.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node bin/dev-server"
|
||||
}
|
||||
"start": "node bin/dev-server",
|
||||
"test": "jest"
|
||||
},
|
||||
"devDependencies": {}
|
||||
}
|
||||
|
|
|
@ -6,5 +6,8 @@
|
|||
|
||||
module.exports = {
|
||||
// Extend from the shared list of defined globals for mochitests.
|
||||
"extends": "../../../.eslintrc.mochitests.js"
|
||||
"extends": "../../../.eslintrc.mochitests.js",
|
||||
"env": {
|
||||
"jest": true
|
||||
},
|
||||
};
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { Sort, sortReducer} = require("../../src/reducers/sort");
|
||||
const { SORT_BY } = require("../../src/constants");
|
||||
|
||||
describe("sorting reducer", () => {
|
||||
it("it should sort by sort type", () => {
|
||||
const initialState = new Sort();
|
||||
const action = {
|
||||
type: SORT_BY,
|
||||
sortType: "TimeWhen",
|
||||
};
|
||||
const expectedState = {
|
||||
type: "TimeWhen",
|
||||
ascending: true,
|
||||
};
|
||||
|
||||
expect(expectedState).toEqual(sortReducer(initialState, action));
|
||||
});
|
||||
});
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,9 +1,14 @@
|
|||
const {AddonTestUtils} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
|
||||
const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||
|
||||
const kSearchEngineID = "test_urifixup_search_engine";
|
||||
const kSearchEngineURL = "http://www.example.org/?search={searchTerms}";
|
||||
const kForceHostLookup = "browser.fixup.dns_first_for_single_words";
|
||||
|
||||
AddonTestUtils.init(this);
|
||||
AddonTestUtils.overrideCertDB();
|
||||
AddonTestUtils.createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
|
||||
|
||||
// TODO(bug 1522134), this test should also use
|
||||
// combinations of the following flags.
|
||||
var flagInputs = [
|
||||
|
@ -465,7 +470,7 @@ var testcases = [ {
|
|||
},
|
||||
];
|
||||
|
||||
if (Services.appinfo.OS.toLowerCase().startsWith("win")) {
|
||||
if (AppConstants.platform == "win") {
|
||||
testcases.push({
|
||||
input: "C:\\some\\file.txt",
|
||||
fixedURI: "file:///C:/some/file.txt",
|
||||
|
@ -494,10 +499,6 @@ function sanitize(input) {
|
|||
return input.replace(/\r|\n/g, "").trim();
|
||||
}
|
||||
|
||||
AddonTestUtils.init(this);
|
||||
AddonTestUtils.overrideCertDB();
|
||||
AddonTestUtils.createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
|
||||
|
||||
add_task(async function setup() {
|
||||
var prefList = ["browser.fixup.typo.scheme", "keyword.enabled",
|
||||
"browser.fixup.domainwhitelist.whitelisted"];
|
||||
|
|
|
@ -464,7 +464,7 @@ nsFocusManager::ElementIsFocusable(Element* aElement, uint32_t aFlags,
|
|||
bool* aIsFocusable) {
|
||||
NS_ENSURE_TRUE(aElement, NS_ERROR_INVALID_ARG);
|
||||
|
||||
*aIsFocusable = CheckIfFocusable(aElement, aFlags) != nullptr;
|
||||
*aIsFocusable = FlushAndCheckIfFocusable(aElement, aFlags) != nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1123,7 +1123,8 @@ void nsFocusManager::ActivateOrDeactivate(nsPIDOMWindowOuter* aWindow,
|
|||
void nsFocusManager::SetFocusInner(Element* aNewContent, int32_t aFlags,
|
||||
bool aFocusChanged, bool aAdjustWidget) {
|
||||
// if the element is not focusable, just return and leave the focus as is
|
||||
RefPtr<Element> elementToFocus = CheckIfFocusable(aNewContent, aFlags);
|
||||
RefPtr<Element> elementToFocus =
|
||||
FlushAndCheckIfFocusable(aNewContent, aFlags);
|
||||
if (!elementToFocus) {
|
||||
return;
|
||||
}
|
||||
|
@ -1459,16 +1460,10 @@ bool nsFocusManager::IsNonFocusableRoot(nsIContent* aContent) {
|
|||
nsContentUtils::IsUserFocusIgnored(aContent));
|
||||
}
|
||||
|
||||
Element* nsFocusManager::CheckIfFocusable(Element* aElement, uint32_t aFlags) {
|
||||
Element* nsFocusManager::FlushAndCheckIfFocusable(Element* aElement,
|
||||
uint32_t aFlags) {
|
||||
if (!aElement) return nullptr;
|
||||
|
||||
// this is a special case for some XUL elements or input number, where an
|
||||
// anonymous child is actually focusable and not the element itself.
|
||||
RefPtr<Element> redirectedFocus = GetRedirectedFocus(aElement);
|
||||
if (redirectedFocus) {
|
||||
return CheckIfFocusable(redirectedFocus, aFlags);
|
||||
}
|
||||
|
||||
nsCOMPtr<Document> doc = aElement->GetComposedDoc();
|
||||
// can't focus elements that are not in documents
|
||||
if (!doc) {
|
||||
|
@ -1481,6 +1476,13 @@ Element* nsFocusManager::CheckIfFocusable(Element* aElement, uint32_t aFlags) {
|
|||
mEventHandlingNeedsFlush = false;
|
||||
doc->FlushPendingNotifications(FlushType::EnsurePresShellInitAndFrames);
|
||||
|
||||
// this is a special case for some XUL elements or input number, where an
|
||||
// anonymous child is actually focusable and not the element itself.
|
||||
RefPtr<Element> redirectedFocus = GetRedirectedFocus(aElement);
|
||||
if (redirectedFocus) {
|
||||
return FlushAndCheckIfFocusable(redirectedFocus, aFlags);
|
||||
}
|
||||
|
||||
PresShell* presShell = doc->GetPresShell();
|
||||
if (!presShell) {
|
||||
return nullptr;
|
||||
|
@ -1749,7 +1751,7 @@ void nsFocusManager::Focus(nsPIDOMWindowOuter* aWindow, Element* aElement,
|
|||
// if the window isn't visible, for instance because it is a hidden tab,
|
||||
// update the current focus and scroll it into view but don't do anything
|
||||
// else
|
||||
if (CheckIfFocusable(aElement, aFlags)) {
|
||||
if (FlushAndCheckIfFocusable(aElement, aFlags)) {
|
||||
aWindow->SetFocusedElement(aElement, focusMethod);
|
||||
if (aFocusChanged) {
|
||||
ScrollIntoView(presShell, aElement, aFlags);
|
||||
|
@ -1829,7 +1831,7 @@ void nsFocusManager::Focus(nsPIDOMWindowOuter* aWindow, Element* aElement,
|
|||
|
||||
// check to ensure that the element is still focusable, and that nothing
|
||||
// else was focused during the events above.
|
||||
if (CheckIfFocusable(aElement, aFlags) && mFocusedWindow == aWindow &&
|
||||
if (FlushAndCheckIfFocusable(aElement, aFlags) && mFocusedWindow == aWindow &&
|
||||
mFocusedElement == nullptr) {
|
||||
mFocusedElement = aElement;
|
||||
|
||||
|
@ -2992,15 +2994,6 @@ static nsIContent* FindOwner(nsIContent* aContent) {
|
|||
nsIContent* currentContent = aContent;
|
||||
while (currentContent) {
|
||||
nsIContent* parent = currentContent->GetFlattenedTreeParent();
|
||||
if (!parent) {
|
||||
// Document root
|
||||
Document* doc = currentContent->GetUncomposedDoc();
|
||||
if (doc && doc->GetRootElement() == currentContent) {
|
||||
return currentContent;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Shadow host / Slot
|
||||
if (IsHostOrSlot(parent)) {
|
||||
|
@ -3258,12 +3251,9 @@ nsresult nsFocusManager::GetNextTabbableContent(
|
|||
}
|
||||
}
|
||||
|
||||
// If aStartContent is not in a scope owned by the root element
|
||||
// (i.e. aStartContent is already in shadow DOM),
|
||||
// search from scope including aStartContent
|
||||
nsIContent* rootElement = aRootContent->OwnerDoc()->GetRootElement();
|
||||
nsIContent* owner = FindOwner(aStartContent);
|
||||
if (owner && rootElement != owner) {
|
||||
// If aStartContent is in a scope owned by Shadow DOM search from scope
|
||||
// including aStartContent
|
||||
if (nsIContent* owner = FindOwner(aStartContent)) {
|
||||
nsIContent* contentToFocus = GetNextTabbableContentInAncestorScopes(
|
||||
owner, &aStartContent, aOriginalStartContent, aForward,
|
||||
&aCurrentTabIndex, aIgnoreTabIndex, aForDocumentNavigation);
|
||||
|
@ -3277,8 +3267,8 @@ nsresult nsFocusManager::GetNextTabbableContent(
|
|||
// We need to continue searching in light DOM, starting at the top level
|
||||
// shadow host in light DOM (updated aStartContent) and its tabindex
|
||||
// (updated aCurrentTabIndex).
|
||||
MOZ_ASSERT(FindOwner(aStartContent) == rootElement,
|
||||
"aStartContent should be owned by the root element at this point");
|
||||
MOZ_ASSERT(!FindOwner(aStartContent),
|
||||
"aStartContent should not be owned by Shadow DOM at this point");
|
||||
|
||||
nsPresContext* presContext = aPresShell->GetPresContext();
|
||||
|
||||
|
@ -3437,7 +3427,7 @@ nsresult nsFocusManager::GetNextTabbableContent(
|
|||
// append ELEMENT to NAVIGATION-ORDER."
|
||||
// and later in "For each element ELEMENT in NAVIGATION-ORDER: "
|
||||
// hosts and slots are handled before other elements.
|
||||
if (currentContent && IsHostOrSlot(currentContent)) {
|
||||
if (IsHostOrSlot(currentContent)) {
|
||||
bool focusableHostSlot;
|
||||
int32_t tabIndex =
|
||||
HostOrSlotTabIndexValue(currentContent, &focusableHostSlot);
|
||||
|
@ -3782,7 +3772,8 @@ nsresult nsFocusManager::FocusFirst(Element* aRootElement,
|
|||
if (aRootElement->GetAttr(kNameSpaceID_None,
|
||||
nsGkAtoms::retargetdocumentfocus, retarget)) {
|
||||
nsCOMPtr<Element> element = doc->GetElementById(retarget);
|
||||
nsCOMPtr<nsIContent> retargetElement = CheckIfFocusable(element, 0);
|
||||
nsCOMPtr<nsIContent> retargetElement =
|
||||
FlushAndCheckIfFocusable(element, 0);
|
||||
if (retargetElement) {
|
||||
retargetElement.forget(aNextContent);
|
||||
return NS_OK;
|
||||
|
|
|
@ -258,19 +258,21 @@ class nsFocusManager final : public nsIFocusManager,
|
|||
bool IsNonFocusableRoot(nsIContent* aContent);
|
||||
|
||||
/**
|
||||
* Checks and returns aContent if it may be focused, another content node if
|
||||
* First flushes the pending notifications to ensure the PresShell and frames
|
||||
* are updated.
|
||||
* Checks and returns aElement if it may be focused, another element node if
|
||||
* the focus should be retargeted at another node, or null if the node
|
||||
* cannot be focused. aFlags are the flags passed to SetFocus and similar
|
||||
* methods.
|
||||
*
|
||||
* An element is focusable if it is in a document, the document isn't in
|
||||
* print preview mode and the element has an nsIFrame where the
|
||||
* CheckIfFocusable method returns true. For <area> elements, there is no
|
||||
* IsFocusable method returns true. For <area> elements, there is no
|
||||
* frame, so only the IsFocusable method on the content node must be
|
||||
* true.
|
||||
*/
|
||||
mozilla::dom::Element* CheckIfFocusable(mozilla::dom::Element* aContent,
|
||||
uint32_t aFlags);
|
||||
mozilla::dom::Element* FlushAndCheckIfFocusable(
|
||||
mozilla::dom::Element* aElement, uint32_t aFlags);
|
||||
|
||||
/**
|
||||
* Blurs the currently focused element. Returns false if another element was
|
||||
|
|
|
@ -2793,12 +2793,16 @@ class LocalizationHandler : public PromiseNativeHandler {
|
|||
l10nData[i].mAttributes;
|
||||
if (!attributes.IsNull()) {
|
||||
for (size_t j = 0; j < attributes.Value().Length(); ++j) {
|
||||
// Use SetAttribute here to validate the attribute name!
|
||||
elem->SetAttribute(attributes.Value()[j].mName,
|
||||
attributes.Value()[j].mValue, rv);
|
||||
if (rv.Failed()) {
|
||||
mReturnValuePromise->MaybeRejectWithUndefined();
|
||||
return;
|
||||
nsString& name = attributes.Value()[j].mName;
|
||||
nsString& value = attributes.Value()[j].mValue;
|
||||
RefPtr<nsAtom> nameAtom = NS_Atomize(name);
|
||||
if (!elem->AttrValueIs(kNameSpaceID_None, nameAtom, value,
|
||||
eCaseMatters)) {
|
||||
rv = elem->SetAttr(kNameSpaceID_None, nameAtom, value, true);
|
||||
if (rv.Failed()) {
|
||||
mReturnValuePromise->MaybeRejectWithUndefined();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1768,7 +1768,13 @@ bool InterSliceGCRunnerFired(TimeStamp aDeadline, void* aData) {
|
|||
uint32_t(idleDuration.ToSeconds() / duration.ToSeconds() * 100);
|
||||
Telemetry::Accumulate(Telemetry::GC_SLICE_DURING_IDLE, percent);
|
||||
}
|
||||
return true;
|
||||
|
||||
// If we didn't use the whole budget, we're past the foreground sweeping slice
|
||||
// and will need to wait for the background tasks to finish, or we're at the
|
||||
// last slice. Return value on the latter case doesn't matter, and on the
|
||||
// former we want to wait a bit before polling again.
|
||||
// Returning false makes IdleTaskRunner postpone the next call a bit.
|
||||
return int64_t(sliceDuration.ToMilliseconds()) >= budget;
|
||||
}
|
||||
|
||||
// static
|
||||
|
|
|
@ -620,7 +620,7 @@ skip-if = toolkit == 'android'
|
|||
[test_bug1406102.html]
|
||||
[test_bug1421568.html]
|
||||
[test_bug1453693.html]
|
||||
skip-if = os == "mac"
|
||||
skip-if = os == "mac" # Different tab focus behavior on mac
|
||||
[test_bug1472427.html]
|
||||
[test_bug1499169.html]
|
||||
skip-if = toolkit == 'android' # Timeouts on android due to page closing issues with embedded pdf
|
||||
|
@ -668,6 +668,8 @@ skip-if = (toolkit == 'android') # Android: Bug 775227
|
|||
skip-if = (toolkit == 'android') # Android: Bug 1465387
|
||||
[test_find_nac.html]
|
||||
skip-if = (toolkit == 'android') # Android: Bug 1465387
|
||||
[test_focus_shadow_dom_root.html]
|
||||
skip-if = os == "mac" # Different tab focus behavior on mac
|
||||
[test_getAttribute_after_createAttribute.html]
|
||||
[test_getElementById.html]
|
||||
[test_getTranslationNodes.html]
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<!doctype html>
|
||||
<title>Test for bug 1544826</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<div id="host"><a href="#" id="slotted">This is focusable too</a></div>
|
||||
<script>
|
||||
const host = document.getElementById("host");
|
||||
const shadow = host.attachShadow({ mode: "open" });
|
||||
shadow.innerHTML = `
|
||||
<a id="shadow-1" href="#">This is focusable</a>
|
||||
<slot></slot>
|
||||
<a id="shadow-2" href="#">So is this</a>
|
||||
`;
|
||||
document.documentElement.remove();
|
||||
document.appendChild(host);
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.waitForFocus(function() {
|
||||
is(document.documentElement, host, "Host is the document element");
|
||||
host.offsetTop;
|
||||
synthesizeKey("KEY_Tab");
|
||||
is(shadow.activeElement.id, "shadow-1", "First link in Shadow DOM is focused");
|
||||
synthesizeKey("KEY_Tab");
|
||||
is(document.activeElement.id, "slotted", "Slotted link is focused");
|
||||
synthesizeKey("KEY_Tab");
|
||||
is(shadow.activeElement.id, "shadow-2", "Second link in Shadow DOM is focused");
|
||||
|
||||
// Now backwards.
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
is(document.activeElement.id, "slotted", "Backwards: Slotted link is focused");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
is(shadow.activeElement.id, "shadow-1", "Backwards: First slotted link is focused");
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
</script>
|
|
@ -7,12 +7,19 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1268556
|
|||
<title>Test focus behaviour for <input type='number'></title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<style>
|
||||
#input_test_style_display {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1268556">Mozilla Bug 1268556</a>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1057858">Mozilla Bug 1057858</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<input id="input" type="number">
|
||||
<input id="input_test_redirect" type="number">
|
||||
<input id="input_test_style_display" type="number" >
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
@ -22,15 +29,23 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1268556
|
|||
* This test checks that when focusing on an input type=number, the focus is
|
||||
* redirected to the anonymous text control, but the document.activeElement
|
||||
* still returns the <input type=number>.
|
||||
*
|
||||
* Tests for bug 1057858.
|
||||
* Checks that adding an element and immediately focusing it triggers exactly
|
||||
* one "focus" event and no "blur" events. The same for switching
|
||||
* `style.display` from `none` to `block`.
|
||||
**/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.waitForFocus(function() {
|
||||
test();
|
||||
test_focus_redirects_to_text_control_but_not_for_activeElement();
|
||||
test_add_element_and_focus_check_one_focus_event();
|
||||
test_style_display_none_change_to_block_check_one_focus_event();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
function test() {
|
||||
var number = document.getElementById("input");
|
||||
function test_focus_redirects_to_text_control_but_not_for_activeElement() {
|
||||
document.activeElement.blur();
|
||||
var number = document.getElementById("input_test_redirect");
|
||||
number.focus();
|
||||
|
||||
// The active element returns the input type=number.
|
||||
|
@ -48,6 +63,46 @@ function test() {
|
|||
is (focusedElement.getAttribute("type"), "text", "focusedElement should of type text");
|
||||
}
|
||||
|
||||
var blurEventCounter = 0;
|
||||
var focusEventCounter = 0;
|
||||
|
||||
function append_input_element_with_event_listeners_to_dom() {
|
||||
var inputElement = document.createElement("input");
|
||||
inputElement.type = "number";
|
||||
inputElement.addEventListener("blur", function() { ++blurEventCounter; });
|
||||
inputElement.addEventListener("focus", function() { ++focusEventCounter; });
|
||||
var content = document.getElementById("content");
|
||||
content.appendChild(inputElement);
|
||||
return inputElement;
|
||||
}
|
||||
|
||||
function test_add_element_and_focus_check_one_focus_event() {
|
||||
document.activeElement.blur();
|
||||
var inputElement = append_input_element_with_event_listeners_to_dom();
|
||||
|
||||
blurEventCounter = 0;
|
||||
focusEventCounter = 0;
|
||||
inputElement.focus();
|
||||
|
||||
is(blurEventCounter, 0, "After focus: no blur events observed.");
|
||||
is(focusEventCounter, 1, "After focus: exactly one focus event observed.");
|
||||
}
|
||||
|
||||
function test_style_display_none_change_to_block_check_one_focus_event() {
|
||||
document.activeElement.blur();
|
||||
var inputElement = document.getElementById("input_test_style_display");
|
||||
inputElement.addEventListener("blur", function() { ++blurEventCounter; });
|
||||
inputElement.addEventListener("focus", function() { ++focusEventCounter; });
|
||||
|
||||
blurEventCounter = 0;
|
||||
focusEventCounter = 0;
|
||||
inputElement.style.display = "block";
|
||||
inputElement.focus();
|
||||
|
||||
is(blurEventCounter, 0, "After focus: no blur events observed.");
|
||||
is(focusEventCounter, 1, "After focus: exactly one focus event observed.");
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
|
|
@ -66,7 +66,9 @@ CheckedInt64 UsecsToFrames(int64_t aUsecs, uint32_t aRate) {
|
|||
|
||||
// Format TimeUnit as number of frames at given rate.
|
||||
CheckedInt64 TimeUnitToFrames(const TimeUnit& aTime, uint32_t aRate) {
|
||||
return UsecsToFrames(aTime.ToMicroseconds(), aRate);
|
||||
return aTime.IsValid() ?
|
||||
UsecsToFrames(aTime.ToMicroseconds(), aRate) :
|
||||
CheckedInt64(INT64_MAX) + 1;
|
||||
}
|
||||
|
||||
nsresult SecondsToUsecs(double aSeconds, int64_t& aOutUsecs) {
|
||||
|
|
|
@ -456,7 +456,7 @@ static bool ToSessionType(const nsAString& aSessionType,
|
|||
return false;
|
||||
}
|
||||
|
||||
// 5.2.1 Is persistent session type?
|
||||
// 5.1.1 Is persistent session type?
|
||||
static bool IsPersistentSessionType(MediaKeySessionType aSessionType) {
|
||||
return aSessionType == MediaKeySessionType::Persistent_license;
|
||||
}
|
||||
|
@ -522,7 +522,7 @@ static bool IsParameterUnrecognized(const nsAString& aContentType) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// 3.1.2.3 Get Supported Capabilities for Audio/Video Type
|
||||
// 3.1.1.3 Get Supported Capabilities for Audio/Video Type
|
||||
static Sequence<MediaKeySystemMediaCapability> GetSupportedCapabilities(
|
||||
const CodecType aCodecType,
|
||||
const nsTArray<MediaKeySystemMediaCapability>& aRequestedCapabilities,
|
||||
|
@ -807,7 +807,7 @@ static bool CheckRequirement(const MediaKeysRequirement aRequirement,
|
|||
return true;
|
||||
}
|
||||
|
||||
// 3.1.2.2, step 12
|
||||
// 3.1.1.2, step 12
|
||||
// Follow the steps for the first matching condition from the following list:
|
||||
// If the sessionTypes member is present in candidate configuration.
|
||||
// Let session types be candidate configuration's sessionTypes member.
|
||||
|
@ -826,7 +826,7 @@ static Sequence<nsString> UnboxSessionTypes(
|
|||
return sessionTypes;
|
||||
}
|
||||
|
||||
// 3.1.2.2 Get Supported Configuration and Consent
|
||||
// 3.1.1.2 Get Supported Configuration and Consent
|
||||
static bool GetSupportedConfig(
|
||||
const KeySystemConfig& aKeySystem,
|
||||
const MediaKeySystemConfiguration& aCandidate,
|
||||
|
|
|
@ -385,7 +385,7 @@ void AudioSink::NotifyAudioNeeded() {
|
|||
// hardware.
|
||||
CheckedInt64 missingFrames = sampleTime - mFramesParsed;
|
||||
|
||||
if (!missingFrames.isValid()) {
|
||||
if (!missingFrames.isValid() || !sampleTime.isValid()) {
|
||||
NS_WARNING("Int overflow in AudioSink");
|
||||
mErrored = true;
|
||||
return;
|
||||
|
|
|
@ -151,6 +151,18 @@ function overlayChildNodes(fromFragment, toElement) {
|
|||
toElement.appendChild(fromFragment);
|
||||
}
|
||||
|
||||
function hasAttribute(attributes, name) {
|
||||
if (!attributes) {
|
||||
return false;
|
||||
}
|
||||
for (let attr of attributes) {
|
||||
if (attr.name === name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transplant localizable attributes of an element to another element.
|
||||
*
|
||||
|
@ -167,9 +179,11 @@ function overlayAttributes(fromElement, toElement) {
|
|||
.split(",").map(i => i.trim())
|
||||
: null;
|
||||
|
||||
// Remove existing localizable attributes.
|
||||
// Remove existing localizable attributes if they
|
||||
// will not be used in the new translation.
|
||||
for (const attr of Array.from(toElement.attributes)) {
|
||||
if (isAttrNameLocalizable(attr.name, toElement, explicitlyAllowed)) {
|
||||
if (isAttrNameLocalizable(attr.name, toElement, explicitlyAllowed)
|
||||
&& !hasAttribute(fromElement.attributes, attr.name)) {
|
||||
toElement.removeAttribute(attr.name);
|
||||
}
|
||||
}
|
||||
|
@ -183,7 +197,8 @@ function overlayAttributes(fromElement, toElement) {
|
|||
|
||||
// Set localizable attributes.
|
||||
for (const attr of Array.from(fromElement.attributes)) {
|
||||
if (isAttrNameLocalizable(attr.name, toElement, explicitlyAllowed)) {
|
||||
if (isAttrNameLocalizable(attr.name, toElement, explicitlyAllowed)
|
||||
&& toElement.getAttribute(attr.name) !== attr.value) {
|
||||
toElement.setAttribute(attr.name, attr.value);
|
||||
}
|
||||
}
|
||||
|
@ -475,10 +490,15 @@ class DOMLocalization extends Localization {
|
|||
* @param {Object<string, string>} args - KVP list of l10n arguments
|
||||
* @returns {Element}
|
||||
*/
|
||||
setAttributes(element, id, args) {
|
||||
element.setAttribute(L10NID_ATTR_NAME, id);
|
||||
setAttributes(element, id, args = null) {
|
||||
if (element.getAttribute(L10NID_ATTR_NAME) !== id) {
|
||||
element.setAttribute(L10NID_ATTR_NAME, id);
|
||||
}
|
||||
if (args) {
|
||||
element.setAttribute(L10NARGS_ATTR_NAME, JSON.stringify(args));
|
||||
let argsString = JSON.stringify(args);
|
||||
if (argsString !== element.getAttribute(L10NARGS_ATTR_NAME)) {
|
||||
element.setAttribute(L10NARGS_ATTR_NAME, argsString);
|
||||
}
|
||||
} else {
|
||||
element.removeAttribute(L10NARGS_ATTR_NAME);
|
||||
}
|
||||
|
|
|
@ -263,10 +263,11 @@ void DocumentL10n::SetAttributes(JSContext* aCx, Element& aElement,
|
|||
const nsAString& aId,
|
||||
const Optional<JS::Handle<JSObject*>>& aArgs,
|
||||
ErrorResult& aRv) {
|
||||
aElement.SetAttribute(NS_LITERAL_STRING("data-l10n-id"), aId, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
if (!aElement.AttrValueIs(kNameSpaceID_None, nsGkAtoms::datal10nid, aId,
|
||||
eCaseMatters)) {
|
||||
aElement.SetAttr(kNameSpaceID_None, nsGkAtoms::datal10nid, aId, true);
|
||||
}
|
||||
|
||||
if (aArgs.WasPassed()) {
|
||||
nsAutoString data;
|
||||
JS::Rooted<JS::Value> val(aCx, JS::ObjectValue(*aArgs.Value()));
|
||||
|
@ -274,9 +275,12 @@ void DocumentL10n::SetAttributes(JSContext* aCx, Element& aElement,
|
|||
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
||||
return;
|
||||
}
|
||||
aElement.SetAttribute(NS_LITERAL_STRING("data-l10n-args"), data, aRv);
|
||||
if (!aElement.AttrValueIs(kNameSpaceID_None, nsGkAtoms::datal10nargs, data,
|
||||
eCaseMatters)) {
|
||||
aElement.SetAttr(kNameSpaceID_None, nsGkAtoms::datal10nargs, data, true);
|
||||
}
|
||||
} else {
|
||||
aElement.RemoveAttribute(NS_LITERAL_STRING("data-l10n-args"), aRv);
|
||||
aElement.UnsetAttr(kNameSpaceID_None, nsGkAtoms::datal10nargs, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,3 +26,6 @@
|
|||
[dom/test_docl10n.html]
|
||||
[dom/test_docl10n_ready_rejected.html]
|
||||
[dom/test_docl10n_removeResourceIds.html]
|
||||
|
||||
[dom_overlays/test_same_id.html]
|
||||
[dom_overlays/test_same_id_args.html]
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test Amount of mutations generated from DOM Overlays</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<link rel="localization" href="toolkit/about/aboutTelemetry.ftl"/>
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let config = {
|
||||
attributes: true,
|
||||
attributeOldValue: true,
|
||||
characterData: true,
|
||||
characterDataOldValue: true,
|
||||
childList: true,
|
||||
subtree: true,
|
||||
};
|
||||
let allMutations = [];
|
||||
|
||||
document.addEventListener("DOMContentLoaded", async function() {
|
||||
await document.l10n.ready;
|
||||
|
||||
let inputElem = document.getElementById("search-input");
|
||||
|
||||
// Test for initial localization applied.
|
||||
is(inputElem.getAttribute("placeholder").length > 0, true);
|
||||
|
||||
let observer = new MutationObserver((mutations) => {
|
||||
for (let mutation of mutations) {
|
||||
allMutations.push(mutation);
|
||||
}
|
||||
});
|
||||
observer.observe(inputElem, config);
|
||||
|
||||
document.l10n.setAttributes(inputElem, "about-telemetry-filter-all-placeholder");
|
||||
|
||||
// Due to the async iteractions between nsINode.localize
|
||||
// and DOMLocalization.jsm, we'll need to wait two frames
|
||||
// to verify that no mutations happened.
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
// Since the l10n-id is the same as the previous one
|
||||
// no mutation should happen in result.
|
||||
is(allMutations.length, 0);
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
}, { once: true});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<input id="search-input" data-l10n-id="about-telemetry-filter-all-placeholder"></input>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,57 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test Amount of mutations generated from DOM Overlays</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<link rel="localization" href="toolkit/about/aboutTelemetry.ftl"/>
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let config = {
|
||||
attributes: true,
|
||||
attributeOldValue: true,
|
||||
characterData: true,
|
||||
characterDataOldValue: true,
|
||||
childList: true,
|
||||
subtree: true,
|
||||
};
|
||||
let allMutations = [];
|
||||
|
||||
document.addEventListener("DOMContentLoaded", async function() {
|
||||
await document.l10n.ready;
|
||||
|
||||
let inputElem = document.getElementById("search-input");
|
||||
|
||||
// Test for initial localization applied.
|
||||
is(inputElem.getAttribute("placeholder").length > 0, true);
|
||||
|
||||
let observer = new MutationObserver((mutations) => {
|
||||
for (let mutation of mutations) {
|
||||
allMutations.push(mutation);
|
||||
}
|
||||
});
|
||||
observer.observe(inputElem, config);
|
||||
|
||||
document.l10n.setAttributes(inputElem, "about-telemetry-filter-placeholder", {selectedTitle: "Test"});
|
||||
|
||||
// Due to the async iteractions between nsINode.localize
|
||||
// and DOMLocalization.jsm, we'll need to wait two frames
|
||||
// to verify that no mutations happened.
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
// Since the l10n-id is the same as the previous one
|
||||
// no mutation should happen in result.
|
||||
is(allMutations.length, 0);
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
}, { once: true});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<input id="search-input" data-l10n-id="about-telemetry-filter-placeholder" data-l10n-args='{"selectedTitle":"Test"}'></input>
|
||||
</body>
|
||||
</html>
|
|
@ -47,6 +47,7 @@
|
|||
#include "mozilla/dom/PointerEventHandler.h"
|
||||
#include "mozilla/dom/PopupBlocker.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/DocumentInlines.h"
|
||||
#include "nsAnimationManager.h"
|
||||
#include "nsNameSpaceManager.h" // for Pref-related rule management (bugs 22963,20760,31816)
|
||||
#include "nsFrame.h"
|
||||
|
@ -96,6 +97,7 @@
|
|||
#ifdef MOZ_REFLOW_PERF
|
||||
# include "nsFontMetrics.h"
|
||||
#endif
|
||||
#include "MobileViewportManager.h"
|
||||
#include "OverflowChangedTracker.h"
|
||||
#include "PositionedEventTargeting.h"
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/ScrollStyles.h"
|
||||
#include "nsStyleStruct.h" // for nsStyleDisplay and nsStyleBackground::Position
|
||||
#include "mozilla/WritingModes.h"
|
||||
#include "nsStyleStruct.h" // for nsStyleDisplay & nsStyleBackground::Position
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "mozilla/dom/BeforeUnloadEvent.h"
|
||||
#include "mozilla/dom/PopupBlocker.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/DocumentInlines.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsIWritablePropertyBag2.h"
|
||||
|
|
|
@ -8,8 +8,11 @@
|
|||
#define nsLayoutModule_h
|
||||
|
||||
#include "nscore.h"
|
||||
#include "nsID.h"
|
||||
#include "mozilla/AlreadyAddRefed.h"
|
||||
|
||||
class nsIPresentationService;
|
||||
class nsISupports;
|
||||
|
||||
// This function initializes various layout statics, as well as XPConnect.
|
||||
// It should be called only once, and before the first time any XPCOM module in
|
||||
|
|
|
@ -103,6 +103,7 @@
|
|||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/IMEStateManager.h"
|
||||
#include "mozilla/dom/HTMLVideoElement.h"
|
||||
#include "ThirdPartyUtil.h"
|
||||
#include "TouchManager.h"
|
||||
#include "DecoderDoctorLogger.h"
|
||||
#include "MediaDecoder.h"
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "nsNameSpaceManager.h"
|
||||
#include "nsCSSAnonBoxes.h"
|
||||
#include "mozilla/ServoStyleSet.h"
|
||||
#include "mozilla/ServoStyleSetInlines.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "nsDisplayList.h"
|
||||
#include "nsNodeUtils.h"
|
||||
|
|
|
@ -26,7 +26,7 @@ class WebRenderCanvasData;
|
|||
class nsPresContext;
|
||||
class nsDisplayItem;
|
||||
|
||||
nsIFrame* NS_NewHTMLCanvasFrame(PresShell* aPresShell,
|
||||
nsIFrame* NS_NewHTMLCanvasFrame(mozilla::PresShell* aPresShell,
|
||||
mozilla::ComputedStyle* aStyle);
|
||||
|
||||
class nsHTMLCanvasFrame final : public nsContainerFrame {
|
||||
|
|
|
@ -95,7 +95,9 @@ public class Tabs implements BundleEventListener {
|
|||
// Used to indicate a new tab should be appended to the current tabs.
|
||||
public static final int NEW_LAST_INDEX = -1;
|
||||
|
||||
private static final AtomicInteger sTabId = new AtomicInteger(0);
|
||||
// Bug 1410749: Desktop numbers tabs starting from 1, and various Webextension bits have
|
||||
// inherited that assumption and treat 0 as an invalid tab.
|
||||
private static final AtomicInteger sTabId = new AtomicInteger(1);
|
||||
private volatile boolean mInitialTabsAdded;
|
||||
|
||||
private Context mAppContext;
|
||||
|
|
|
@ -49,18 +49,6 @@ add_task(async function test_setTitle_and_getTitle() {
|
|||
return tab;
|
||||
}
|
||||
|
||||
// Test on tabId 0
|
||||
// Test that the default title is returned before the title is set for the tab.
|
||||
let tabTitle = await browser.browserAction.getTitle({tabId: 0});
|
||||
browser.test.assertEq("Browser Action", tabTitle,
|
||||
"Expected the default title to be returned for tabId 0");
|
||||
|
||||
// Set the title for the new tab and test that getTitle returns the correct title.
|
||||
await browser.browserAction.setTitle({tabId: 0, title: "tab 0"});
|
||||
tabTitle = await browser.browserAction.getTitle({tabId: 0});
|
||||
browser.test.assertEq("tab 0", tabTitle,
|
||||
"Expected the new tab title to be returned for tabId 0");
|
||||
|
||||
// Create and test 3 new tabs.
|
||||
let tab1 = await createAndTestNewTab("tab 1", "about:blank");
|
||||
let tab2 = await createAndTestNewTab("tab 2", "about:blank");
|
||||
|
|
|
@ -237,50 +237,6 @@ add_task(async function tabsSendMessageNoExceptionOnNonExistentTab() {
|
|||
await extension.unload();
|
||||
});
|
||||
|
||||
|
||||
add_task(async function tabsSendAndReceiveMessageTabId0() {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"permissions": ["tabs", "<all_urls>"],
|
||||
},
|
||||
|
||||
async background() {
|
||||
function contentScriptCode() {
|
||||
browser.runtime.onMessage.addListener(msg => {
|
||||
browser.test.assertEq(msg, "message to tabId 0",
|
||||
"Got the expected message from the background page");
|
||||
|
||||
return Promise.resolve("reply to background page");
|
||||
});
|
||||
browser.runtime.sendMessage("message from tabId 0");
|
||||
}
|
||||
|
||||
await browser.runtime.onMessage.addListener(async (msg, sender) => {
|
||||
browser.test.assertEq("message from tabId 0", msg,
|
||||
"Got the expected message from a content script");
|
||||
browser.test.assertTrue(sender.tab,
|
||||
"Got a sender.tab object as expected");
|
||||
browser.test.assertEq(0, sender.tab.id,
|
||||
"Got a sender.tab object with tab.id == 0");
|
||||
|
||||
const reply = await browser.tabs.sendMessage(0, "message to tabId 0");
|
||||
|
||||
browser.test.assertEq("reply to background page", reply);
|
||||
|
||||
browser.test.notifyPass("tabs.messaging.tab0");
|
||||
});
|
||||
|
||||
await browser.tabs.executeScript(0, {code: `new ${contentScriptCode}`});
|
||||
},
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
|
||||
await extension.awaitFinish("tabs.messaging.tab0");
|
||||
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/* 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/. */
|
||||
|
||||
package org.mozilla.geckoview;
|
||||
|
||||
import org.mozilla.gecko.util.EventCallback;
|
||||
|
||||
/* package */ abstract class CallbackResult<T> extends GeckoResult<T>
|
||||
implements EventCallback {
|
||||
@Override
|
||||
public void sendError(final Object response) {
|
||||
completeExceptionally(response != null ?
|
||||
new Exception(response.toString()) :
|
||||
new UnknownError());
|
||||
}
|
||||
}
|
|
@ -289,7 +289,7 @@ public final class GeckoRuntime implements Parcelable {
|
|||
@UiThread
|
||||
public @NonNull GeckoResult<Void> registerWebExtension(
|
||||
final @NonNull WebExtension webExtension) {
|
||||
final GeckoSession.CallbackResult<Void> result = new GeckoSession.CallbackResult<Void>() {
|
||||
final CallbackResult<Void> result = new CallbackResult<Void>() {
|
||||
@Override
|
||||
public void sendSuccess(final Object response) {
|
||||
complete(null);
|
||||
|
|
|
@ -266,16 +266,6 @@ public class GeckoSession implements Parcelable {
|
|||
return mCompositorReady ? mCompositor : null;
|
||||
}
|
||||
|
||||
/* package */ static abstract class CallbackResult<T> extends GeckoResult<T>
|
||||
implements EventCallback {
|
||||
@Override
|
||||
public void sendError(final Object response) {
|
||||
completeExceptionally(response != null ?
|
||||
new Exception(response.toString()) :
|
||||
new UnknownError());
|
||||
}
|
||||
}
|
||||
|
||||
private final GeckoSessionHandler<HistoryDelegate> mHistoryHandler =
|
||||
new GeckoSessionHandler<HistoryDelegate>(
|
||||
"GeckoViewHistory", this,
|
||||
|
|
|
@ -42,17 +42,16 @@ public final class RuntimeTelemetry {
|
|||
final GeckoBundle msg = new GeckoBundle(1);
|
||||
msg.putBoolean("clear", clear);
|
||||
|
||||
final GeckoSession.CallbackResult<JSONObject> result =
|
||||
new GeckoSession.CallbackResult<JSONObject>() {
|
||||
@Override
|
||||
public void sendSuccess(final Object value) {
|
||||
try {
|
||||
complete(((GeckoBundle) value).toJSONObject());
|
||||
} catch (JSONException ex) {
|
||||
completeExceptionally(ex);
|
||||
}
|
||||
final CallbackResult<JSONObject> result = new CallbackResult<JSONObject>() {
|
||||
@Override
|
||||
public void sendSuccess(final Object value) {
|
||||
try {
|
||||
complete(((GeckoBundle) value).toJSONObject());
|
||||
} catch (JSONException ex) {
|
||||
completeExceptionally(ex);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
mEventDispatcher.dispatch("GeckoView:TelemetrySnapshots", msg, result);
|
||||
|
||||
|
|
|
@ -85,8 +85,7 @@ public final class SessionFinder {
|
|||
bundle.putString("searchString", searchString);
|
||||
addFlagsToBundle(flags, bundle);
|
||||
|
||||
final GeckoSession.CallbackResult<FinderResult> result =
|
||||
new GeckoSession.CallbackResult<FinderResult>() {
|
||||
final CallbackResult<FinderResult> result = new CallbackResult<FinderResult>() {
|
||||
@Override
|
||||
public void sendSuccess(final Object response) {
|
||||
complete(new FinderResult((GeckoBundle) response));
|
||||
|
|
|
@ -23,7 +23,10 @@ class Tab {
|
|||
// Stub BrowserApp implementation for WebExtensions support.
|
||||
class GeckoViewTab extends GeckoViewModule {
|
||||
onInit() {
|
||||
const tab = new Tab(0, this.browser);
|
||||
// As this is only a stub implementation, we hardcode a single tab ID.
|
||||
// Because of bug 1410749, we can't use 0, though, and just to be safe
|
||||
// we choose a value that is unlikely to overlap with Fennec's tab IDs.
|
||||
const tab = new Tab(10001, this.browser);
|
||||
|
||||
this.window.gBrowser = this.window.BrowserApp = {
|
||||
selectedBrowser: this.browser,
|
||||
|
|
|
@ -2764,7 +2764,9 @@ class StaticAnalysis(MachCommandBase):
|
|||
path = repo.get_outgoing_files()
|
||||
|
||||
if path:
|
||||
path = map(os.path.abspath, path)
|
||||
# Create the full path list
|
||||
path_maker = lambda f_name: os.path.join(self.topsrcdir, f_name)
|
||||
path = map(path_maker, path)
|
||||
|
||||
os.chdir(self.topsrcdir)
|
||||
|
||||
|
|
|
@ -652,7 +652,8 @@ void SetMediaPluginSandbox(const char* aFilePath) {
|
|||
}
|
||||
|
||||
void SetRemoteDataDecoderSandbox(int aBroker) {
|
||||
if (PR_GetEnv("MOZ_DISABLE_RDD_SANDBOX") != nullptr) {
|
||||
if (!SandboxInfo::Get().Test(SandboxInfo::kHasSeccompBPF) ||
|
||||
PR_GetEnv("MOZ_DISABLE_RDD_SANDBOX")) {
|
||||
if (aBroker >= 0) {
|
||||
close(aBroker);
|
||||
}
|
||||
|
|
|
@ -629,6 +629,13 @@ class SandboxPolicyCommon : public SandboxPolicyBase {
|
|||
case __NR_getrandom:
|
||||
return Allow();
|
||||
|
||||
#ifdef DESKTOP
|
||||
// Bug 1543858: glibc's qsort calls sysinfo to check the
|
||||
// memory size; it falls back to assuming there's enough RAM.
|
||||
case __NR_sysinfo:
|
||||
return Error(EPERM);
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_ASAN
|
||||
// ASAN's error reporter wants to know if stderr is a tty.
|
||||
case __NR_ioctl: {
|
||||
|
|
|
@ -262,12 +262,15 @@ win32-shippable/opt:
|
|||
shippable: true
|
||||
enable-full-crashsymbols: true
|
||||
stub-installer:
|
||||
by-project:
|
||||
default: false
|
||||
mozilla-central: true
|
||||
try: true
|
||||
mozilla-beta: true
|
||||
mozilla-release: true
|
||||
by-release-type:
|
||||
nightly: true
|
||||
beta: true
|
||||
release: true
|
||||
default:
|
||||
by-project:
|
||||
# browser/confvars.sh looks for nightly-try
|
||||
try: true
|
||||
default: false
|
||||
shipping-phase: build
|
||||
shipping-product: firefox
|
||||
treeherder:
|
||||
|
@ -821,12 +824,15 @@ win32-devedition-nightly/opt:
|
|||
nightly: true
|
||||
enable-full-crashsymbols: true
|
||||
stub-installer:
|
||||
by-project:
|
||||
default: false
|
||||
mozilla-central: true
|
||||
try: true
|
||||
mozilla-beta: true
|
||||
mozilla-release: true
|
||||
by-release-type:
|
||||
nightly: true
|
||||
beta: true
|
||||
release: true
|
||||
default:
|
||||
by-project:
|
||||
# browser/confvars.sh looks for nightly-try
|
||||
try: true
|
||||
default: false
|
||||
shipping-phase: build
|
||||
shipping-product: devedition
|
||||
treeherder:
|
||||
|
|
|
@ -49,12 +49,16 @@ devtools-tests:
|
|||
yarn test &&
|
||||
cd /builds/worker/checkouts/gecko/devtools/client/webconsole/test &&
|
||||
yarn &&
|
||||
yarn test &&
|
||||
cd /builds/worker/checkouts/gecko/devtools/client/netmonitor/test &&
|
||||
yarn &&
|
||||
yarn test
|
||||
when:
|
||||
files-changed:
|
||||
- 'devtools/client/aboutdebugging-new/src/components/**'
|
||||
- 'devtools/client/framework/components/**'
|
||||
- 'devtools/client/webconsole/**'
|
||||
- 'devtools/client/netmonitor/**'
|
||||
|
||||
eslint-plugin-mozilla:
|
||||
description: eslint-plugin-mozilla integration tests
|
||||
|
|
|
@ -43,7 +43,10 @@ def set_defaults(config, jobs):
|
|||
def stub_installer(config, jobs):
|
||||
for job in jobs:
|
||||
resolve_keyed_by(
|
||||
job, 'stub-installer', item_name=job['name'], project=config.params['project']
|
||||
job, 'stub-installer', item_name=job['name'], project=config.params['project'],
|
||||
**{
|
||||
'release-type': config.params['release_type'],
|
||||
}
|
||||
)
|
||||
job.setdefault('attributes', {})
|
||||
if job.get('stub-installer'):
|
||||
|
|
|
@ -140,10 +140,18 @@ class Raptor(TestingMixin, MercurialScript, CodeCoverageMixin, AndroidMixin):
|
|||
}],
|
||||
[["--power-test"], {
|
||||
"dest": "power_test",
|
||||
"action": "store_true",
|
||||
"default": False,
|
||||
"help": "Use Raptor to measure power usage. Currently only supported for Geckoview. "
|
||||
"The host ip address must be specified either via the --host command line "
|
||||
"argument.",
|
||||
}],
|
||||
[["--memory-test"], {
|
||||
"dest": "memory_test",
|
||||
"action": "store_true",
|
||||
"default": False,
|
||||
"help": "Use Raptor to measure memory usage.",
|
||||
}],
|
||||
[["--debug-mode"], {
|
||||
"dest": "debug_mode",
|
||||
"action": "store_true",
|
||||
|
@ -225,6 +233,7 @@ class Raptor(TestingMixin, MercurialScript, CodeCoverageMixin, AndroidMixin):
|
|||
if self.host == 'HOST_IP':
|
||||
self.host = os.environ['HOST_IP']
|
||||
self.power_test = self.config.get('power_test')
|
||||
self.memory_test = self.config.get('memory_test')
|
||||
self.is_release_build = self.config.get('is_release_build')
|
||||
self.debug_mode = self.config.get('debug_mode', False)
|
||||
self.firefox_android_browsers = ["fennec", "geckoview", "refbrow", "fenix"]
|
||||
|
@ -362,6 +371,8 @@ class Raptor(TestingMixin, MercurialScript, CodeCoverageMixin, AndroidMixin):
|
|||
options.extend(['--is-release-build'])
|
||||
if self.config.get('power_test', False):
|
||||
options.extend(['--power-test'])
|
||||
if self.config.get('memory_test', False):
|
||||
options.extend(['--memory-test'])
|
||||
for key, value in kw_options.items():
|
||||
options.extend(['--%s' % key, value])
|
||||
|
||||
|
@ -473,6 +484,8 @@ class Raptor(TestingMixin, MercurialScript, CodeCoverageMixin, AndroidMixin):
|
|||
expected_perfherder = 1
|
||||
if self.config.get('power_test', None):
|
||||
expected_perfherder += 1
|
||||
if self.config.get('memory_test', None):
|
||||
expected_perfherder += 1
|
||||
if len(parser.found_perf_data) != expected_perfherder:
|
||||
self.critical("PERFHERDER_DATA was seen %d times, expected %d."
|
||||
% (len(parser.found_perf_data), expected_perfherder))
|
||||
|
@ -608,6 +621,10 @@ class Raptor(TestingMixin, MercurialScript, CodeCoverageMixin, AndroidMixin):
|
|||
src = os.path.join(self.query_abs_dirs()['abs_work_dir'], 'raptor-power.json')
|
||||
self._artifact_perf_data(src, dest)
|
||||
|
||||
if self.memory_test:
|
||||
src = os.path.join(self.query_abs_dirs()['abs_work_dir'], 'raptor-memory.json')
|
||||
self._artifact_perf_data(src, dest)
|
||||
|
||||
src = os.path.join(self.query_abs_dirs()['abs_work_dir'], 'screenshots.html')
|
||||
if os.path.exists(src):
|
||||
dest = os.path.join(env['MOZ_UPLOAD_DIR'], 'screenshots.html')
|
||||
|
|
|
@ -60,6 +60,7 @@ class RaptorRunner(MozbuildObject):
|
|||
kwargs['host'] = os.environ['HOST_IP']
|
||||
self.host = kwargs['host']
|
||||
self.power_test = kwargs['power_test']
|
||||
self.memory_test = kwargs['memory_test']
|
||||
self.is_release_build = kwargs['is_release_build']
|
||||
|
||||
def setup_benchmarks(self):
|
||||
|
@ -139,6 +140,7 @@ class RaptorRunner(MozbuildObject):
|
|||
'raptor_cmd_line_args': self.raptor_args,
|
||||
'host': self.host,
|
||||
'power_test': self.power_test,
|
||||
'memory_test': self.memory_test,
|
||||
'is_release_build': self.is_release_build,
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,8 @@ def create_parser(mach_interface=False):
|
|||
add_arg('--power-test', dest="power_test", action="store_true",
|
||||
help="Use Raptor to measure power usage. Currently supported for Geckoview. "
|
||||
"The host ip address must be specified via the --host command line argument.")
|
||||
add_arg('--memory-test', dest="memory_test", action="store_true",
|
||||
help="Use Raptor to measure memory usage.")
|
||||
add_arg('--is-release-build', dest="is_release_build", default=False,
|
||||
action='store_true',
|
||||
help="Whether the build is a release build which requires work arounds "
|
||||
|
|
|
@ -7,10 +7,12 @@
|
|||
from __future__ import absolute_import
|
||||
|
||||
import BaseHTTPServer
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
import threading
|
||||
import time
|
||||
|
||||
from mozlog import get_proxy_logger
|
||||
|
||||
|
@ -22,6 +24,81 @@ here = os.path.abspath(os.path.dirname(__file__))
|
|||
def MakeCustomHandlerClass(results_handler, shutdown_browser, write_raw_gecko_profile):
|
||||
|
||||
class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler, object):
|
||||
"""
|
||||
Control server expects messages of the form
|
||||
{'type': 'messagetype', 'data':...}
|
||||
|
||||
Each message is given a key which is calculated as
|
||||
|
||||
If type is 'webext_status', then
|
||||
the key is data['type']/data['data']
|
||||
otherwise
|
||||
the key is data['type'].
|
||||
|
||||
The contol server can be forced to wait before performing an
|
||||
action requested via POST by sending a special message
|
||||
|
||||
{'type': 'wait-set', 'data': key}
|
||||
|
||||
where key is the key of the message control server should
|
||||
perform a wait before processing. The handler will store
|
||||
this key in the wait_after_messages dict as a True value.
|
||||
|
||||
wait_after_messages[key] = True
|
||||
|
||||
For subsequent requests the handler will check the key of
|
||||
the incoming message against wait_for_messages and if it is
|
||||
found and its value is True, the handler will assign the key
|
||||
to waiting_in_state and will loop until the key is removed
|
||||
or until its value is changed to False.
|
||||
|
||||
Control server will stop waiting for a state to be continued
|
||||
or cleared after wait_timeout seconds after which the wait
|
||||
will be removed and the control server will finish processing
|
||||
the current POST request. wait_timeout defaults to 60 seconds
|
||||
but can be set globally for all wait states by sending the
|
||||
message
|
||||
|
||||
{'type': 'wait-timeout', 'data': timeout}
|
||||
|
||||
The value of waiting_in_state can be retrieved by sending the
|
||||
message
|
||||
|
||||
{'type': 'wait-get', 'data': ''}
|
||||
|
||||
which will return the value of waiting_in_state in the
|
||||
content of the response. If the value returned is not
|
||||
'None', then the control server has received a message whose
|
||||
key is recorded in wait_after_messages and is waiting before
|
||||
completing the request.
|
||||
|
||||
The control server can be told to stop waiting and to finish
|
||||
processing the current request while keeping the wait for
|
||||
subsequent requests by sending
|
||||
|
||||
{'type': 'wait-continue', 'data': ''}
|
||||
|
||||
The control server can be told to stop waiting and to finish
|
||||
processing the current request while removing the wait for
|
||||
subsequent requests by sending
|
||||
|
||||
{'type': 'wait-clear', 'data': key}
|
||||
|
||||
if key is the empty string ''
|
||||
the key in waiting_in_state is removed from wait_after_messages
|
||||
waiting_in_state is set to None
|
||||
else if key is 'all'
|
||||
all keys in wait_after_messages are removed
|
||||
else key is not in wait_after_messages
|
||||
the message is ignored
|
||||
else
|
||||
the key is removed from wait_after messages
|
||||
if the key matches the value in waiting_in_state,
|
||||
then waiting_in_state is set to None
|
||||
"""
|
||||
wait_after_messages = {}
|
||||
waiting_in_state = None
|
||||
wait_timeout = 60
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.results_handler = results_handler
|
||||
|
@ -29,6 +106,10 @@ def MakeCustomHandlerClass(results_handler, shutdown_browser, write_raw_gecko_pr
|
|||
self.write_raw_gecko_profile = write_raw_gecko_profile
|
||||
super(MyHandler, self).__init__(*args, **kwargs)
|
||||
|
||||
def log_request(self, code='-', size='-'):
|
||||
if code != 200:
|
||||
super(MyHandler, self).log_request(code, size)
|
||||
|
||||
def do_GET(self):
|
||||
# get handler, received request for test settings from web ext runner
|
||||
self.send_response(200)
|
||||
|
@ -61,6 +142,32 @@ def MakeCustomHandlerClass(results_handler, shutdown_browser, write_raw_gecko_pr
|
|||
# could have received a status update or test results
|
||||
data = json.loads(post_body)
|
||||
|
||||
if data['type'] == 'webext_status':
|
||||
wait_key = "%s/%s" % (data['type'], data['data'])
|
||||
else:
|
||||
wait_key = data['type']
|
||||
|
||||
if MyHandler.wait_after_messages.get(wait_key, None):
|
||||
LOG.info("Waiting in %s" % wait_key)
|
||||
MyHandler.waiting_in_state = wait_key
|
||||
start_time = datetime.datetime.now()
|
||||
|
||||
while MyHandler.wait_after_messages.get(wait_key, None):
|
||||
time.sleep(1)
|
||||
elapsed_time = datetime.datetime.now() - start_time
|
||||
if elapsed_time > datetime.timedelta(seconds=MyHandler.wait_timeout):
|
||||
del MyHandler.wait_after_messages[wait_key]
|
||||
MyHandler.waiting_in_state = None
|
||||
LOG.error("TEST-UNEXPECTED-ERROR | "
|
||||
"ControlServer wait %s exceeded %s seconds" %
|
||||
(wait_key, MyHandler.wait_timeout))
|
||||
|
||||
if MyHandler.wait_after_messages.get(wait_key, None) is not None:
|
||||
# If the wait is False, it was continued and we just set it back
|
||||
# to True for the next time. If it was removed by clear, we
|
||||
# leave it alone so it will not cause a wait any more.
|
||||
MyHandler.wait_after_messages[wait_key] = True
|
||||
|
||||
if data['type'] == "webext_gecko_profile":
|
||||
# received gecko profiling results
|
||||
_test = str(data['data'][0])
|
||||
|
@ -80,7 +187,7 @@ def MakeCustomHandlerClass(results_handler, shutdown_browser, write_raw_gecko_pr
|
|||
self.results_handler.add_page_timeout(str(data['data'][0]),
|
||||
str(data['data'][1]),
|
||||
dict(data['data'][2]))
|
||||
elif data['data'] == "__raptor_shutdownBrowser":
|
||||
elif data['type'] == 'webext_status' and data['data'] == "__raptor_shutdownBrowser":
|
||||
LOG.info("received " + data['type'] + ": " + str(data['data']))
|
||||
# webext is telling us it's done, and time to shutdown the browser
|
||||
self.shutdown_browser()
|
||||
|
@ -89,6 +196,39 @@ def MakeCustomHandlerClass(results_handler, shutdown_browser, write_raw_gecko_pr
|
|||
self.results_handler.add_image(str(data['data'][0]),
|
||||
str(data['data'][1]),
|
||||
str(data['data'][2]))
|
||||
elif data['type'] == 'webext_status':
|
||||
LOG.info("received " + data['type'] + ": " + str(data['data']))
|
||||
elif data['type'] == 'wait-set':
|
||||
LOG.info("received " + data['type'] + ": " + str(data['data']))
|
||||
MyHandler.wait_after_messages[str(data['data'])] = True
|
||||
elif data['type'] == 'wait-timeout':
|
||||
LOG.info("received " + data['type'] + ": " + str(data['data']))
|
||||
MyHandler.wait_timeout = data['data']
|
||||
elif data['type'] == 'wait-get':
|
||||
self.wfile.write(MyHandler.waiting_in_state)
|
||||
elif data['type'] == 'wait-continue':
|
||||
LOG.info("received " + data['type'] + ": " + str(data['data']))
|
||||
if MyHandler.waiting_in_state:
|
||||
MyHandler.wait_after_messages[MyHandler.waiting_in_state] = False
|
||||
MyHandler.waiting_in_state = None
|
||||
elif data['type'] == 'wait-clear':
|
||||
LOG.info("received " + data['type'] + ": " + str(data['data']))
|
||||
clear_key = str(data['data'])
|
||||
if clear_key == '':
|
||||
if MyHandler.waiting_in_state:
|
||||
del MyHandler.wait_after_messages[MyHandler.waiting_in_state]
|
||||
MyHandler.waiting_in_state = None
|
||||
else:
|
||||
pass
|
||||
elif clear_key == 'all':
|
||||
MyHandler.wait_after_messages = {}
|
||||
MyHandler.waiting_in_state = None
|
||||
elif clear_key not in MyHandler.wait_after_messages:
|
||||
pass
|
||||
else:
|
||||
del MyHandler.wait_after_messages[clear_key]
|
||||
if MyHandler.waiting_in_state == clear_key:
|
||||
MyHandler.waiting_in_state = None
|
||||
else:
|
||||
LOG.info("received " + data['type'] + ": " + str(data['data']))
|
||||
|
||||
|
@ -103,6 +243,19 @@ def MakeCustomHandlerClass(results_handler, shutdown_browser, write_raw_gecko_pr
|
|||
return MyHandler
|
||||
|
||||
|
||||
class ThreadedHTTPServer(BaseHTTPServer.HTTPServer):
|
||||
# See
|
||||
# https://stackoverflow.com/questions/19537132/threaded-basehttpserver-one-thread-per-request#30312766
|
||||
def process_request(self, request, client_address):
|
||||
thread = threading.Thread(target=self.__new_request,
|
||||
args=(self.RequestHandlerClass, request, client_address, self))
|
||||
thread.start()
|
||||
|
||||
def __new_request(self, handlerClass, request, address, server):
|
||||
handlerClass(request, address, server)
|
||||
self.shutdown_request(request)
|
||||
|
||||
|
||||
class RaptorControlServer():
|
||||
"""Container class for Raptor Control Server"""
|
||||
|
||||
|
@ -130,7 +283,7 @@ class RaptorControlServer():
|
|||
sock.close()
|
||||
server_address = ('', self.port)
|
||||
|
||||
server_class = BaseHTTPServer.HTTPServer
|
||||
server_class = ThreadedHTTPServer
|
||||
handler_class = MakeCustomHandlerClass(self.results_handler,
|
||||
self.shutdown_browser,
|
||||
self.write_raw_gecko_profile)
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
# 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/.
|
||||
from __future__ import absolute_import
|
||||
|
||||
import re
|
||||
|
||||
|
||||
def get_app_memory_usage(raptor):
|
||||
app_name = raptor.config['binary']
|
||||
total = 0
|
||||
re_total_memory = re.compile(r'TOTAL:\s+(\d+)')
|
||||
verbose = raptor.device._verbose
|
||||
raptor.device._verbose = False
|
||||
meminfo = raptor.device.shell_output("dumpsys meminfo %s" % app_name).split('\n')
|
||||
raptor.device._verbose = verbose
|
||||
for line in meminfo:
|
||||
match = re_total_memory.search(line)
|
||||
if match:
|
||||
total = int(match.group(1))
|
||||
break
|
||||
return total
|
||||
|
||||
|
||||
def generate_android_memory_profile(raptor, test_name):
|
||||
if not raptor.device or not raptor.config['memory_test']:
|
||||
return
|
||||
foreground = get_app_memory_usage(raptor)
|
||||
# put app into background
|
||||
verbose = raptor.device._verbose
|
||||
raptor.device._verbose = False
|
||||
raptor.device.shell_output("am start -a android.intent.action.MAIN "
|
||||
"-c android.intent.category.HOME")
|
||||
raptor.device._verbose = verbose
|
||||
background = get_app_memory_usage(raptor)
|
||||
meminfo_data = {'type': 'memory',
|
||||
'test': test_name,
|
||||
'unit': 'KB',
|
||||
'values': {
|
||||
'foreground': foreground,
|
||||
'background': background
|
||||
}}
|
||||
raptor.control_server.submit_supporting_data(meminfo_data)
|
|
@ -13,6 +13,8 @@ import sys
|
|||
import tempfile
|
||||
import time
|
||||
|
||||
import requests
|
||||
|
||||
import mozcrash
|
||||
import mozinfo
|
||||
from mozdevice import ADBDevice
|
||||
|
@ -57,6 +59,7 @@ from mozproxy import get_playback
|
|||
from results import RaptorResultsHandler
|
||||
from gecko_profile import GeckoProfile
|
||||
from power import init_android_power_test, finish_android_power_test
|
||||
from memory import generate_android_memory_profile
|
||||
from utils import view_gecko_profile
|
||||
|
||||
|
||||
|
@ -65,8 +68,8 @@ class Raptor(object):
|
|||
|
||||
def __init__(self, app, binary, run_local=False, obj_path=None,
|
||||
gecko_profile=False, gecko_profile_interval=None, gecko_profile_entries=None,
|
||||
symbols_path=None, host=None, power_test=False, is_release_build=False,
|
||||
debug_mode=False, post_startup_delay=None, activity=None):
|
||||
symbols_path=None, host=None, power_test=False, memory_test=False,
|
||||
is_release_build=False, debug_mode=False, post_startup_delay=None, activity=None):
|
||||
|
||||
# Override the magic --host HOST_IP with the value of the environment variable.
|
||||
if host == 'HOST_IP':
|
||||
|
@ -84,7 +87,9 @@ class Raptor(object):
|
|||
self.config['symbols_path'] = symbols_path
|
||||
self.config['host'] = host
|
||||
self.config['power_test'] = power_test
|
||||
self.config['memory_test'] = memory_test
|
||||
self.config['is_release_build'] = is_release_build
|
||||
self.config['enable_control_server_wait'] = memory_test
|
||||
self.raptor_venv = os.path.join(os.getcwd(), 'raptor-venv')
|
||||
self.log = get_default_logger(component='raptor-main')
|
||||
self.control_server = None
|
||||
|
@ -187,6 +192,9 @@ class Raptor(object):
|
|||
self.control_server = RaptorControlServer(self.results_handler, self.debug_mode)
|
||||
self.control_server.start()
|
||||
|
||||
if self.config['enable_control_server_wait']:
|
||||
self.control_server_wait_set('webext_status/__raptor_shutdownBrowser')
|
||||
|
||||
# for android we must make the control server available to the device
|
||||
if self.config['app'] in self.firefox_android_apps and \
|
||||
self.config['host'] in ('localhost', '127.0.0.1'):
|
||||
|
@ -336,6 +344,12 @@ class Raptor(object):
|
|||
|
||||
elapsed_time = 0
|
||||
while not self.control_server._finished:
|
||||
if self.config['enable_control_server_wait']:
|
||||
response = self.control_server_wait_get()
|
||||
if response == 'webext_status/__raptor_shutdownBrowser':
|
||||
if self.config['memory_test']:
|
||||
generate_android_memory_profile(self, test['name'])
|
||||
self.control_server_wait_continue()
|
||||
time.sleep(1)
|
||||
# we only want to force browser-shutdown on timeout if not in debug mode;
|
||||
# in debug-mode we leave the browser running (require manual shutdown)
|
||||
|
@ -365,6 +379,9 @@ class Raptor(object):
|
|||
return self.results_handler.page_timeout_list
|
||||
|
||||
def clean_up(self):
|
||||
if self.config['enable_control_server_wait']:
|
||||
self.control_server_wait_clear('all')
|
||||
|
||||
self.control_server.stop()
|
||||
if self.config['app'] not in self.firefox_android_apps:
|
||||
self.runner.stop()
|
||||
|
@ -375,15 +392,40 @@ class Raptor(object):
|
|||
pass
|
||||
self.log.info("finished")
|
||||
|
||||
def control_server_wait_set(self, state):
|
||||
response = requests.post("http://127.0.0.1:%s/" % self.control_server.port,
|
||||
json={"type": "wait-set", "data": state})
|
||||
return response.content
|
||||
|
||||
def control_server_wait_timeout(self, timeout):
|
||||
response = requests.post("http://127.0.0.1:%s/" % self.control_server.port,
|
||||
json={"type": "wait-timeout", "data": timeout})
|
||||
return response.content
|
||||
|
||||
def control_server_wait_get(self):
|
||||
response = requests.post("http://127.0.0.1:%s/" % self.control_server.port,
|
||||
json={"type": "wait-get", "data": ""})
|
||||
return response.content
|
||||
|
||||
def control_server_wait_continue(self):
|
||||
response = requests.post("http://127.0.0.1:%s/" % self.control_server.port,
|
||||
json={"type": "wait-continue", "data": ""})
|
||||
return response.content
|
||||
|
||||
def control_server_wait_clear(self, state):
|
||||
response = requests.post("http://127.0.0.1:%s/" % self.control_server.port,
|
||||
json={"type": "wait-clear", "data": state})
|
||||
return response.content
|
||||
|
||||
|
||||
class RaptorDesktop(Raptor):
|
||||
def __init__(self, app, binary, run_local=False, obj_path=None,
|
||||
gecko_profile=False, gecko_profile_interval=None, gecko_profile_entries=None,
|
||||
symbols_path=None, host=None, power_test=False, is_release_build=False,
|
||||
debug_mode=False, post_startup_delay=None, activity=None):
|
||||
symbols_path=None, host=None, power_test=False, memory_test=False,
|
||||
is_release_build=False, debug_mode=False, post_startup_delay=None, activity=None):
|
||||
Raptor.__init__(self, app, binary, run_local, obj_path, gecko_profile,
|
||||
gecko_profile_interval, gecko_profile_entries, symbols_path,
|
||||
host, power_test, is_release_build, debug_mode,
|
||||
host, power_test, memory_test, is_release_build, debug_mode,
|
||||
post_startup_delay)
|
||||
|
||||
def create_browser_handler(self):
|
||||
|
@ -446,11 +488,11 @@ class RaptorDesktop(Raptor):
|
|||
class RaptorDesktopFirefox(RaptorDesktop):
|
||||
def __init__(self, app, binary, run_local=False, obj_path=None,
|
||||
gecko_profile=False, gecko_profile_interval=None, gecko_profile_entries=None,
|
||||
symbols_path=None, host=None, power_test=False, is_release_build=False,
|
||||
debug_mode=False, post_startup_delay=None, activity=None):
|
||||
symbols_path=None, host=None, power_test=False, memory_test=False,
|
||||
is_release_build=False, debug_mode=False, post_startup_delay=None, activity=None):
|
||||
RaptorDesktop.__init__(self, app, binary, run_local, obj_path, gecko_profile,
|
||||
gecko_profile_interval, gecko_profile_entries, symbols_path,
|
||||
host, power_test, is_release_build, debug_mode,
|
||||
host, power_test, memory_test, is_release_build, debug_mode,
|
||||
post_startup_delay)
|
||||
|
||||
def disable_non_local_connections(self):
|
||||
|
@ -491,11 +533,11 @@ class RaptorDesktopFirefox(RaptorDesktop):
|
|||
class RaptorDesktopChrome(RaptorDesktop):
|
||||
def __init__(self, app, binary, run_local=False, obj_path=None,
|
||||
gecko_profile=False, gecko_profile_interval=None, gecko_profile_entries=None,
|
||||
symbols_path=None, host=None, power_test=False, is_release_build=False,
|
||||
debug_mode=False, post_startup_delay=None, activity=None):
|
||||
symbols_path=None, host=None, power_test=False, memory_test=False,
|
||||
is_release_build=False, debug_mode=False, post_startup_delay=None, activity=None):
|
||||
RaptorDesktop.__init__(self, app, binary, run_local, obj_path, gecko_profile,
|
||||
gecko_profile_interval, gecko_profile_entries, symbols_path,
|
||||
host, power_test, is_release_build, debug_mode,
|
||||
host, power_test, memory_test, is_release_build, debug_mode,
|
||||
post_startup_delay)
|
||||
|
||||
def setup_chrome_desktop_for_playback(self):
|
||||
|
@ -530,11 +572,11 @@ class RaptorDesktopChrome(RaptorDesktop):
|
|||
class RaptorAndroid(Raptor):
|
||||
def __init__(self, app, binary, run_local=False, obj_path=None,
|
||||
gecko_profile=False, gecko_profile_interval=None, gecko_profile_entries=None,
|
||||
symbols_path=None, host=None, power_test=False, is_release_build=False,
|
||||
debug_mode=False, post_startup_delay=None, activity=None):
|
||||
symbols_path=None, host=None, power_test=False, memory_test=False,
|
||||
is_release_build=False, debug_mode=False, post_startup_delay=None, activity=None):
|
||||
Raptor.__init__(self, app, binary, run_local, obj_path, gecko_profile,
|
||||
gecko_profile_interval, gecko_profile_entries, symbols_path, host,
|
||||
power_test, is_release_build, debug_mode, post_startup_delay)
|
||||
power_test, memory_test, is_release_build, debug_mode, post_startup_delay)
|
||||
|
||||
# on android, when creating the browser profile, we want to use a 'firefox' type profile
|
||||
self.profile_class = "firefox"
|
||||
|
@ -847,6 +889,7 @@ def main(args=sys.argv[1:]):
|
|||
symbols_path=args.symbols_path,
|
||||
host=args.host,
|
||||
power_test=args.power_test,
|
||||
memory_test=args.memory_test,
|
||||
is_release_build=args.is_release_build,
|
||||
debug_mode=args.debug_mode,
|
||||
post_startup_delay=args.post_startup_delay,
|
||||
|
|
|
@ -16,7 +16,8 @@ def test_verify_options(filedir):
|
|||
page_cycles=1,
|
||||
page_timeout=60000,
|
||||
debug='True',
|
||||
power_test=False)
|
||||
power_test=False,
|
||||
memory_test=False)
|
||||
parser = ArgumentParser()
|
||||
|
||||
with pytest.raises(SystemExit):
|
||||
|
@ -31,7 +32,8 @@ def test_verify_options(filedir):
|
|||
gecko_profile='False',
|
||||
is_release_build=False,
|
||||
host='sophie',
|
||||
power_test=False)
|
||||
power_test=False,
|
||||
memory_test=False)
|
||||
verify_options(parser, args) # assert no exception
|
||||
|
||||
args = Namespace(app='refbrow',
|
||||
|
@ -40,7 +42,8 @@ def test_verify_options(filedir):
|
|||
gecko_profile='False',
|
||||
is_release_build=False,
|
||||
host='sophie',
|
||||
power_test=False)
|
||||
power_test=False,
|
||||
memory_test=False)
|
||||
verify_options(parser, args) # assert no exception
|
||||
|
||||
args = Namespace(app='fenix',
|
||||
|
@ -49,7 +52,8 @@ def test_verify_options(filedir):
|
|||
gecko_profile='False',
|
||||
is_release_build=False,
|
||||
host='sophie',
|
||||
power_test=False)
|
||||
power_test=False,
|
||||
memory_test=False)
|
||||
verify_options(parser, args) # assert no exception
|
||||
|
||||
args = Namespace(app='refbrow',
|
||||
|
@ -58,7 +62,8 @@ def test_verify_options(filedir):
|
|||
gecko_profile='False',
|
||||
is_release_build=False,
|
||||
host='sophie',
|
||||
power_test=False)
|
||||
power_test=False,
|
||||
memory_test=False)
|
||||
parser = ArgumentParser()
|
||||
|
||||
verify_options(parser, args) # also will work as uses default activity
|
||||
|
|
|
@ -91,6 +91,67 @@ def test_start_and_stop_server(raptor):
|
|||
assert not raptor.control_server._server_thread.is_alive()
|
||||
|
||||
|
||||
def test_server_wait_states(raptor):
|
||||
import datetime
|
||||
|
||||
import requests
|
||||
|
||||
def post_state():
|
||||
requests.post("http://127.0.0.1:%s/" % raptor.control_server.port,
|
||||
json={"type": "webext_status",
|
||||
"data": "test status"})
|
||||
|
||||
assert raptor.control_server is None
|
||||
|
||||
raptor.create_browser_profile()
|
||||
raptor.create_browser_handler()
|
||||
raptor.start_control_server()
|
||||
|
||||
wait_time = 5
|
||||
message_state = 'webext_status/test status'
|
||||
rhc = raptor.control_server.server.RequestHandlerClass
|
||||
|
||||
# Test initial state
|
||||
assert rhc.wait_after_messages == {}
|
||||
assert rhc.waiting_in_state is None
|
||||
assert rhc.wait_timeout == 60
|
||||
assert raptor.control_server_wait_get() == 'None'
|
||||
|
||||
# Test setting a state
|
||||
assert raptor.control_server_wait_set(message_state) == ''
|
||||
assert message_state in rhc.wait_after_messages
|
||||
assert rhc.wait_after_messages[message_state]
|
||||
|
||||
# Test clearing a non-existent state
|
||||
assert raptor.control_server_wait_clear('nothing') == ''
|
||||
assert message_state in rhc.wait_after_messages
|
||||
|
||||
# Test clearing a state
|
||||
assert raptor.control_server_wait_clear(message_state) == ''
|
||||
assert message_state not in rhc.wait_after_messages
|
||||
|
||||
# Test clearing all states
|
||||
assert raptor.control_server_wait_set(message_state) == ''
|
||||
assert message_state in rhc.wait_after_messages
|
||||
assert raptor.control_server_wait_clear('all') == ''
|
||||
assert rhc.wait_after_messages == {}
|
||||
|
||||
# Test wait timeout
|
||||
# Block on post request
|
||||
assert raptor.control_server_wait_set(message_state) == ''
|
||||
assert rhc.wait_after_messages[message_state]
|
||||
assert raptor.control_server_wait_timeout(wait_time) == ''
|
||||
assert rhc.wait_timeout == wait_time
|
||||
start = datetime.datetime.now()
|
||||
post_state()
|
||||
assert datetime.datetime.now() - start < datetime.timedelta(seconds=wait_time+2)
|
||||
assert raptor.control_server_wait_get() == 'None'
|
||||
assert message_state not in rhc.wait_after_messages
|
||||
|
||||
raptor.clean_up()
|
||||
assert not raptor.control_server._server_thread.is_alive()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('app', [
|
||||
'firefox',
|
||||
pytest.mark.xfail('chrome'),
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
"tests": ["tart_flex", "ts_paint_flex"]
|
||||
},
|
||||
"other": {
|
||||
"tests": ["a11yr", "ts_paint", "tpaint", "twinopen", "sessionrestore", "sessionrestore_no_auto_restore", "tabpaint", "cpstartup", "startup_about_home_paint"]
|
||||
"tests": ["a11yr", "ts_paint", "twinopen", "sessionrestore", "sessionrestore_no_auto_restore", "tabpaint", "cpstartup", "startup_about_home_paint"]
|
||||
},
|
||||
"sessionrestore-many-windows": {
|
||||
"tests": ["sessionrestore_many_windows"]
|
||||
|
|
|
@ -270,26 +270,6 @@ class QuantumPageloadTest(PageloaderTest):
|
|||
fnbpaint = True
|
||||
|
||||
|
||||
@register_test()
|
||||
class tpaint(PageloaderTest):
|
||||
"""
|
||||
Tests the amount of time it takes the open a new window. This test does
|
||||
not include startup time. Multiple test windows are opened in succession,
|
||||
results reported are the average amount of time required to create and
|
||||
display a window in the running instance of the browser.
|
||||
(Measures ctrl-n performance.)
|
||||
"""
|
||||
tpmanifest = '${talos}/tests/tpaint/tpaint.manifest'
|
||||
tppagecycles = 20
|
||||
timeout = 300
|
||||
gecko_profile_interval = 1
|
||||
gecko_profile_entries = 2000000
|
||||
tpmozafterpaint = True
|
||||
filters = filter.ignore_first.prepare(5) + filter.median.prepare()
|
||||
unit = 'ms'
|
||||
preferences = {'security.data_uri.block_toplevel_data_uri_navigations': False}
|
||||
|
||||
|
||||
@register_test()
|
||||
class twinopen(PageloaderTest):
|
||||
"""
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче