зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to inbound. a=merge CLOSED TREE
--HG-- rename : devtools/client/responsive.html/components/NetworkThrottlingSelector.js => devtools/client/shared/components/throttling/NetworkThrottlingSelector.js rename : devtools/client/responsive.html/actions/network-throttling.js => devtools/client/shared/components/throttling/actions.js rename : devtools/client/shared/network-throttling-profiles.js => devtools/client/shared/components/throttling/profiles.js rename : devtools/client/responsive.html/reducers/network-throttling.js => devtools/client/shared/components/throttling/reducer.js
This commit is contained in:
Коммит
435ba6ef00
|
@ -6,6 +6,7 @@
|
|||
#include "RootAccessibleWrap.h"
|
||||
|
||||
#include "Compatibility.h"
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
#include "nsCoreUtils.h"
|
||||
#include "nsWinUtils.h"
|
||||
|
||||
|
@ -155,8 +156,11 @@ RootAccessibleWrap::get_accFocus(
|
|||
/* [retval][out] */ VARIANT __RPC_FAR *pvarChild)
|
||||
{
|
||||
HRESULT hr = DocAccessibleWrap::get_accFocus(pvarChild);
|
||||
if (FAILED(hr) || pvarChild->vt != VT_EMPTY) {
|
||||
// We got a definite result (either failure or an accessible).
|
||||
if (FAILED(hr) || pvarChild->vt != VT_EMPTY || !IsWin8OrLater()) {
|
||||
// 1. We got a definite result (either failure or an accessible); or
|
||||
// 2. This is Windows 7, where we don't want to retrieve the focus from a
|
||||
// remote document because this causes mysterious intermittent crashes
|
||||
// when we're called by UIA clients; see bug 1424505.
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
|
|
@ -213,9 +213,9 @@ gTests.push({
|
|||
// since we didn't set _cleanShutdown.
|
||||
let richlistbox = tagsField.popup.richlistbox;
|
||||
// Focus and select first result.
|
||||
Assert.ok(richlistbox.itemCount, 1, "We have 1 autocomplete result");
|
||||
Assert.equal(richlistbox.itemCount, 1, "We have 1 autocomplete result");
|
||||
tagsField.popup.selectedIndex = 0;
|
||||
Assert.ok(richlistbox.selectedItems.length, 1,
|
||||
Assert.equal(richlistbox.selectedItems.length, 1,
|
||||
"We have selected a tag from the autocomplete popup");
|
||||
info("About to focus the autocomplete results");
|
||||
richlistbox.focus();
|
||||
|
|
|
@ -20,6 +20,7 @@ da
|
|||
de
|
||||
dsb
|
||||
el
|
||||
en-CA
|
||||
en-GB
|
||||
en-ZA
|
||||
eo
|
||||
|
|
|
@ -27,6 +27,7 @@ locales = [
|
|||
"de",
|
||||
"dsb",
|
||||
"el",
|
||||
"en-CA",
|
||||
"en-GB",
|
||||
"en-ZA",
|
||||
"eo",
|
||||
|
|
|
@ -222,6 +222,13 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"en-CA": {
|
||||
"default": {
|
||||
"visibleDefaultEngines": [
|
||||
"google", "amazon-ca", "bing", "ddg", "ebay-ca", "twitter", "wikipedia"
|
||||
]
|
||||
}
|
||||
},
|
||||
"en-GB": {
|
||||
"default": {
|
||||
"visibleDefaultEngines": [
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(MOZILLA_DIR)/build/binary-location.mk
|
||||
|
||||
browser_path := '"$(browser_path)"'
|
||||
|
||||
_PROFILE_DIR = $(TARGET_DEPTH)/_profile/pgo
|
||||
|
||||
ABSOLUTE_TOPSRCDIR = $(abspath $(MOZILLA_DIR))
|
||||
_CERTS_SRC_DIR = $(ABSOLUTE_TOPSRCDIR)/build/pgo/certs
|
||||
|
||||
AUTOMATION_PPARGS = \
|
||||
-DBROWSER_PATH=$(browser_path) \
|
||||
-DXPC_BIN_PATH='"$(DIST)/bin"' \
|
||||
-DBIN_SUFFIX='"$(BIN_SUFFIX)"' \
|
||||
-DPROFILE_DIR='"$(_PROFILE_DIR)"' \
|
||||
-DCERTS_SRC_DIR='"$(_CERTS_SRC_DIR)"' \
|
||||
-DPERL='"$(PERL)"' \
|
||||
$(NULL)
|
||||
|
||||
ifeq ($(OS_ARCH),Darwin)
|
||||
AUTOMATION_PPARGS += -DIS_MAC=1
|
||||
else
|
||||
AUTOMATION_PPARGS += -DIS_MAC=0
|
||||
endif
|
||||
|
||||
ifeq ($(OS_ARCH),Linux)
|
||||
AUTOMATION_PPARGS += -DIS_LINUX=1
|
||||
else
|
||||
AUTOMATION_PPARGS += -DIS_LINUX=0
|
||||
endif
|
||||
|
||||
ifeq ($(host_os), cygwin)
|
||||
AUTOMATION_PPARGS += -DIS_CYGWIN=1
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_TESTS), 1)
|
||||
AUTOMATION_PPARGS += -DIS_TEST_BUILD=1
|
||||
else
|
||||
AUTOMATION_PPARGS += -DIS_TEST_BUILD=0
|
||||
endif
|
||||
|
||||
ifeq ($(MOZ_DEBUG), 1)
|
||||
AUTOMATION_PPARGS += -DIS_DEBUG_BUILD=1
|
||||
else
|
||||
AUTOMATION_PPARGS += -DIS_DEBUG_BUILD=0
|
||||
endif
|
||||
|
||||
ifdef MOZ_CRASHREPORTER
|
||||
AUTOMATION_PPARGS += -DCRASHREPORTER=1
|
||||
else
|
||||
AUTOMATION_PPARGS += -DCRASHREPORTER=0
|
||||
endif
|
||||
|
||||
ifdef MOZ_ASAN
|
||||
AUTOMATION_PPARGS += -DIS_ASAN=1
|
||||
else
|
||||
AUTOMATION_PPARGS += -DIS_ASAN=0
|
||||
endif
|
||||
|
||||
automation.py: $(MOZILLA_DIR)/build/automation.py.in $(MOZILLA_DIR)/build/automation-build.mk
|
||||
$(call py_action,preprocessor, \
|
||||
$(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $< -o $@)
|
||||
|
||||
GARBAGE += automation.py automation.pyc
|
|
@ -53,7 +53,6 @@ _IS_CYGWIN = False
|
|||
#endif
|
||||
#expand _BIN_SUFFIX = __BIN_SUFFIX__
|
||||
|
||||
#expand _DEFAULT_APP = "./" + __BROWSER_PATH__
|
||||
#expand _CERTS_SRC_DIR = __CERTS_SRC_DIR__
|
||||
#expand _IS_TEST_BUILD = __IS_TEST_BUILD__
|
||||
#expand _IS_DEBUG_BUILD = __IS_DEBUG_BUILD__
|
||||
|
@ -99,7 +98,6 @@ class Automation(object):
|
|||
|
||||
UNIXISH = not IS_WIN32 and not IS_MAC
|
||||
|
||||
DEFAULT_APP = _DEFAULT_APP
|
||||
CERTS_SRC_DIR = _CERTS_SRC_DIR
|
||||
IS_TEST_BUILD = _IS_TEST_BUILD
|
||||
IS_DEBUG_BUILD = _IS_DEBUG_BUILD
|
||||
|
@ -138,7 +136,6 @@ class Automation(object):
|
|||
"runApp",
|
||||
"Process",
|
||||
"DIST_BIN",
|
||||
"DEFAULT_APP",
|
||||
"CERTS_SRC_DIR",
|
||||
"environment",
|
||||
"IS_TEST_BUILD",
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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 distibuted with this
|
||||
# file, You can obtain one at http://mozilla.og/MPL/2.0/.
|
||||
|
||||
import sys
|
||||
import buildconfig
|
||||
from mozbuild.preprocessor import Preprocessor
|
||||
|
||||
|
||||
def main(output, input_file):
|
||||
pp = Preprocessor()
|
||||
pp.context.update(buildconfig.defines['ALLDEFINES'])
|
||||
|
||||
substs = buildconfig.substs
|
||||
|
||||
# Substs taken verbatim.
|
||||
substs_vars = (
|
||||
'BIN_SUFFIX',
|
||||
)
|
||||
for var in substs_vars:
|
||||
pp.context[var] = '"%s"' % substs[var]
|
||||
|
||||
# Derived values.
|
||||
for key, condition in (
|
||||
('IS_MAC', substs['OS_ARCH'] == 'Darwin'),
|
||||
('IS_LINUX', substs['OS_ARCH'] == 'Linux'),
|
||||
('IS_TEST_BUILD', substs.get('ENABLE_TESTS') == '1'),
|
||||
('IS_DEBUG_BUILD', substs.get('MOZ_DEBUG') == '1'),
|
||||
('CRASHREPORTER', substs.get('MOZ_CRASHREPORTER')),
|
||||
('IS_ASAN', substs.get('MOZ_ASAN'))):
|
||||
if condition:
|
||||
pp.context[key] = '1'
|
||||
else:
|
||||
pp.context[key] = '0'
|
||||
|
||||
pp.context.update({
|
||||
'XPC_BIN_PATH': '"%s/dist/bin"' % buildconfig.topobjdir,
|
||||
'CERTS_SRC_DIR': '"%s/build/pgo/certs"' % buildconfig.topsrcdir,
|
||||
})
|
||||
|
||||
pp.out = output
|
||||
pp.do_include(input_file)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(*sys.agv[1:])
|
|
@ -96,6 +96,20 @@ if CONFIG['MOZ_APP_BASENAME']:
|
|||
appini.script = 'appini_header.py'
|
||||
appini.inputs = ['!application.ini']
|
||||
|
||||
if CONFIG['ENABLE_TESTS']:
|
||||
GENERATED_FILES += ['automation.py']
|
||||
auto = GENERATED_FILES['automation.py']
|
||||
auto.script = 'gen_automation.py'
|
||||
auto.inputs = ['automation.py.in']
|
||||
|
||||
TEST_HARNESS_FILES.reftest += [
|
||||
'!automation.py',
|
||||
]
|
||||
|
||||
TEST_HARNESS_FILES.testing.mochitest += [
|
||||
'!automation.py',
|
||||
]
|
||||
|
||||
# NOTE: Keep .gdbinit in the topsrcdir for people who run gdb from the topsrcdir.
|
||||
OBJDIR_FILES += ['/.gdbinit']
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# 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/.
|
||||
|
||||
# LOCALIZATION NOTE These strings are used inside the NetworkThrottlingSelector
|
||||
# component used to throttle network bandwidth.
|
||||
#
|
||||
# The correct localization of this file might be to keep it in
|
||||
# English, or another language commonly spoken among web developers.
|
||||
# You want to make that choice consistent across the developer tools.
|
||||
# A good criteria is the language in which you'd find the best
|
||||
# documentation on web development on the web.
|
||||
|
||||
# LOCALIZATION NOTE (responsive.noThrottling): UI option in a menu to configure
|
||||
# network throttling. This option is the default and disables throttling so you
|
||||
# just have normal network conditions. There is not very much room in the UI
|
||||
# so a short string would be best if possible.
|
||||
responsive.noThrottling=No throttling
|
|
@ -67,12 +67,6 @@ responsive.remoteOnly=Responsive Design Mode is only available for remote browse
|
|||
# container tab.
|
||||
responsive.noContainerTabs=Responsive Design Mode is currently unavailable in container tabs.
|
||||
|
||||
# LOCALIZATION NOTE (responsive.noThrottling): UI option in a menu to configure
|
||||
# network throttling. This option is the default and disables throttling so you
|
||||
# just have normal network conditions. There is not very much room in the UI
|
||||
# so a short string would be best if possible.
|
||||
responsive.noThrottling=No throttling
|
||||
|
||||
# LOCALIZATION NOTE (responsive.changeDevicePixelRatio): tooltip for the
|
||||
# device pixel ratio dropdown when is enabled.
|
||||
responsive.changeDevicePixelRatio=Change device pixel ratio of the viewport
|
||||
|
|
|
@ -94,6 +94,31 @@
|
|||
margin-top: 1px;
|
||||
}
|
||||
|
||||
/* Throttling Button */
|
||||
|
||||
#global-network-throttling-selector:not(:hover) {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#global-network-throttling-selector:hover {
|
||||
background-color: var(--toolbarbutton-background);
|
||||
}
|
||||
|
||||
#global-network-throttling-selector {
|
||||
width: 92px;
|
||||
padding-right: 12px;
|
||||
background-image: var(--drop-down-icon-url);
|
||||
background-position: right 6px;
|
||||
background-repeat: no-repeat;
|
||||
fill: var(--theme-toolbar-photon-icon-color);
|
||||
}
|
||||
|
||||
/* Make sure the Throttle button icon is vertically centered on Mac */
|
||||
:root[platform="mac"] #global-network-throttling-selector {
|
||||
height: 17px;
|
||||
background-position-y: 5px;
|
||||
}
|
||||
|
||||
/* Search box */
|
||||
|
||||
.devtools-searchbox {
|
||||
|
|
|
@ -49,6 +49,11 @@ const DISABLE_CACHE_LABEL = L10N.getStr("netmonitor.toolbar.disableCache.label")
|
|||
loader.lazyRequireGetter(this, "showMenu", "devtools/client/netmonitor/src/utils/menu", true);
|
||||
loader.lazyRequireGetter(this, "HarMenuUtils", "devtools/client/netmonitor/src/har/har-menu-utils", true);
|
||||
|
||||
// Throttling
|
||||
const Types = require("devtools/client/shared/components/throttling/types");
|
||||
const NetworkThrottlingSelector = createFactory(require("devtools/client/shared/components/throttling/NetworkThrottlingSelector"));
|
||||
const { changeNetworkThrottling } = require("devtools/client/shared/components/throttling/actions");
|
||||
|
||||
/**
|
||||
* Network monitor toolbar component.
|
||||
*
|
||||
|
@ -80,11 +85,15 @@ class Toolbar extends Component {
|
|||
singleRow: PropTypes.bool.isRequired,
|
||||
// Callback for opening split console.
|
||||
openSplitConsole: PropTypes.func,
|
||||
networkThrottling: PropTypes.shape(Types.networkThrottling).isRequired,
|
||||
// Executed when throttling changes (through toolbar button).
|
||||
onChangeNetworkThrottling: PropTypes.func.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.autocompleteProvider = this.autocompleteProvider.bind(this);
|
||||
this.onSearchBoxFocus = this.onSearchBoxFocus.bind(this);
|
||||
this.toggleRequestFilterType = this.toggleRequestFilterType.bind(this);
|
||||
|
@ -105,6 +114,7 @@ class Toolbar extends Component {
|
|||
|| this.props.recording !== nextProps.recording
|
||||
|| this.props.singleRow !== nextProps.singleRow
|
||||
|| !Object.is(this.props.requestFilterTypes, nextProps.requestFilterTypes)
|
||||
|| this.props.networkThrottling !== nextProps.networkThrottling
|
||||
|
||||
// Filtered requests are useful only when searchbox is focused
|
||||
|| !!(this.refs.searchbox && this.refs.searchbox.focused);
|
||||
|
@ -261,6 +271,22 @@ class Toolbar extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render network throttling selector button.
|
||||
*/
|
||||
renderThrottlingSelector() {
|
||||
let {
|
||||
networkThrottling,
|
||||
onChangeNetworkThrottling,
|
||||
} = this.props;
|
||||
|
||||
return NetworkThrottlingSelector({
|
||||
className: "devtools-button",
|
||||
networkThrottling,
|
||||
onChangeNetworkThrottling,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Render drop down button with HAR related actions.
|
||||
*/
|
||||
|
@ -362,6 +388,7 @@ class Toolbar extends Component {
|
|||
this.renderPersistlogCheckbox(persistentLogsEnabled, togglePersistentLogs),
|
||||
this.renderCacheCheckbox(browserCacheDisabled, toggleBrowserCache),
|
||||
this.renderSeparator(),
|
||||
this.renderThrottlingSelector(),
|
||||
this.renderHarButton(),
|
||||
)
|
||||
)
|
||||
|
@ -377,6 +404,7 @@ class Toolbar extends Component {
|
|||
this.renderPersistlogCheckbox(persistentLogsEnabled, togglePersistentLogs),
|
||||
this.renderCacheCheckbox(browserCacheDisabled, toggleBrowserCache),
|
||||
this.renderSeparator(),
|
||||
this.renderThrottlingSelector(),
|
||||
this.renderHarButton(),
|
||||
),
|
||||
span({ className: "devtools-toolbar-group devtools-toolbar-two-rows-2" },
|
||||
|
@ -395,6 +423,7 @@ module.exports = connect(
|
|||
persistentLogsEnabled: state.ui.persistentLogsEnabled,
|
||||
recording: getRecordingState(state),
|
||||
requestFilterTypes: state.filters.requestFilterTypes,
|
||||
networkThrottling: state.networkThrottling,
|
||||
}),
|
||||
(dispatch) => ({
|
||||
clearRequests: () => dispatch(Actions.clearRequests()),
|
||||
|
@ -405,5 +434,7 @@ module.exports = connect(
|
|||
toggleRecording: () => dispatch(Actions.toggleRecording()),
|
||||
togglePersistentLogs: () => dispatch(Actions.togglePersistentLogs()),
|
||||
toggleRequestFilterType: (type) => dispatch(Actions.toggleRequestFilterType(type)),
|
||||
onChangeNetworkThrottling: (enabled, profile) =>
|
||||
dispatch(changeNetworkThrottling(enabled, profile)),
|
||||
}),
|
||||
)(Toolbar);
|
||||
|
|
|
@ -12,6 +12,13 @@ const { getDisplayedTimingMarker } = require("../selectors/index");
|
|||
// To be removed once FF60 is deprecated
|
||||
loader.lazyRequireGetter(this, "TimelineFront", "devtools/shared/fronts/timeline", true);
|
||||
|
||||
// Network throttling
|
||||
loader.lazyRequireGetter(this, "throttlingProfiles", "devtools/client/shared/components/throttling/profiles");
|
||||
loader.lazyRequireGetter(this, "EmulationFront", "devtools/shared/fronts/emulation", true);
|
||||
|
||||
/**
|
||||
* Connector to Firefox backend.
|
||||
*/
|
||||
class FirefoxConnector {
|
||||
constructor() {
|
||||
// Public methods
|
||||
|
@ -29,6 +36,7 @@ class FirefoxConnector {
|
|||
this.viewSourceInDebugger = this.viewSourceInDebugger.bind(this);
|
||||
this.requestData = this.requestData.bind(this);
|
||||
this.getTimingMarker = this.getTimingMarker.bind(this);
|
||||
this.updateNetworkThrottling = this.updateNetworkThrottling.bind(this);
|
||||
|
||||
// Internals
|
||||
this.getLongString = this.getLongString.bind(this);
|
||||
|
@ -59,6 +67,7 @@ class FirefoxConnector {
|
|||
owner: this.owner,
|
||||
});
|
||||
|
||||
// Register all listeners
|
||||
await this.addListeners();
|
||||
|
||||
// Listener for `will-navigate` event is (un)registered outside
|
||||
|
@ -69,6 +78,10 @@ class FirefoxConnector {
|
|||
if (this.tabTarget) {
|
||||
this.tabTarget.on("will-navigate", this.willNavigate);
|
||||
this.tabTarget.on("navigate", this.navigate);
|
||||
|
||||
// Initialize Emulation front for network throttling.
|
||||
const { tab } = await this.tabTarget.client.getTab();
|
||||
this.emulationFront = EmulationFront(this.tabTarget.client, tab);
|
||||
}
|
||||
|
||||
// Displaying cache events is only intended for the UI panel.
|
||||
|
@ -84,6 +97,11 @@ class FirefoxConnector {
|
|||
|
||||
await this.removeListeners();
|
||||
|
||||
if (this.emulationFront) {
|
||||
this.emulationFront.destroy();
|
||||
this.emulationFront = null;
|
||||
}
|
||||
|
||||
if (this.tabTarget) {
|
||||
this.tabTarget.off("will-navigate", this.willNavigate);
|
||||
this.tabTarget.off("navigate", this.navigate);
|
||||
|
@ -412,6 +430,20 @@ class FirefoxConnector {
|
|||
return getDisplayedTimingMarker(state, name);
|
||||
}
|
||||
|
||||
async updateNetworkThrottling(enabled, profile) {
|
||||
if (!enabled) {
|
||||
await this.emulationFront.clearNetworkThrottling();
|
||||
} else {
|
||||
const data = throttlingProfiles.find(({ id }) => id == profile);
|
||||
const { download, upload, latency } = data;
|
||||
await this.emulationFront.setNetworkThrottling({
|
||||
downloadThroughput: download,
|
||||
uploadThroughput: upload,
|
||||
latency,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire events for the owner object.
|
||||
*/
|
||||
|
|
|
@ -27,6 +27,7 @@ class Connector {
|
|||
this.viewSourceInDebugger = this.viewSourceInDebugger.bind(this);
|
||||
this.requestData = this.requestData.bind(this);
|
||||
this.getTimingMarker = this.getTimingMarker.bind(this);
|
||||
this.updateNetworkThrottling = this.updateNetworkThrottling.bind(this);
|
||||
}
|
||||
|
||||
// Connect/Disconnect API
|
||||
|
@ -114,6 +115,10 @@ class Connector {
|
|||
getTimingMarker() {
|
||||
return this.connector.getTimingMarker(...arguments);
|
||||
}
|
||||
|
||||
updateNetworkThrottling() {
|
||||
return this.connector.updateNetworkThrottling(...arguments);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.Connector = Connector;
|
||||
|
|
|
@ -12,6 +12,7 @@ const batching = require("./middleware/batching");
|
|||
const prefs = require("./middleware/prefs");
|
||||
const thunk = require("./middleware/thunk");
|
||||
const recording = require("./middleware/recording");
|
||||
const throttling = require("./middleware/throttling");
|
||||
|
||||
// Reducers
|
||||
const rootReducer = require("./reducers/index");
|
||||
|
@ -43,7 +44,8 @@ function configureStore(connector) {
|
|||
thunk,
|
||||
prefs,
|
||||
batching,
|
||||
recording(connector)
|
||||
recording(connector),
|
||||
throttling(connector),
|
||||
);
|
||||
|
||||
return createStore(rootReducer, initialState, middleware);
|
||||
|
|
|
@ -6,5 +6,6 @@ DevToolsModules(
|
|||
'batching.js',
|
||||
'prefs.js',
|
||||
'recording.js',
|
||||
'throttling.js',
|
||||
'thunk.js',
|
||||
)
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
CHANGE_NETWORK_THROTTLING,
|
||||
} = require("devtools/client/shared/components/throttling/actions");
|
||||
|
||||
/**
|
||||
* Network throttling middleware is responsible for
|
||||
* updating/syncing currently connected backend
|
||||
* according to user actions.
|
||||
*/
|
||||
function throttlingMiddleware(connector) {
|
||||
return store => next => action => {
|
||||
const res = next(action);
|
||||
if (action.type === CHANGE_NETWORK_THROTTLING) {
|
||||
connector.updateNetworkThrottling(action.enabled, action.profile);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = throttlingMiddleware;
|
|
@ -11,6 +11,7 @@ const { sortReducer } = require("./sort");
|
|||
const { filters } = require("./filters");
|
||||
const { timingMarkers } = require("./timing-markers");
|
||||
const { ui } = require("./ui");
|
||||
const networkThrottling = require("devtools/client/shared/components/throttling/reducer");
|
||||
|
||||
module.exports = batchingReducer(
|
||||
combineReducers({
|
||||
|
@ -19,5 +20,6 @@ module.exports = batchingReducer(
|
|||
filters,
|
||||
timingMarkers,
|
||||
ui,
|
||||
networkThrottling,
|
||||
})
|
||||
);
|
||||
|
|
|
@ -10,6 +10,10 @@
|
|||
|
||||
const { createEnum } = require("devtools/client/shared/enum");
|
||||
|
||||
const {
|
||||
CHANGE_NETWORK_THROTTLING,
|
||||
} = require("devtools/client/shared/components/throttling/actions");
|
||||
|
||||
createEnum([
|
||||
|
||||
// Add a new device.
|
||||
|
@ -34,7 +38,7 @@ createEnum([
|
|||
"CHANGE_DISPLAY_PIXEL_RATIO",
|
||||
|
||||
// Change the network throttling profile.
|
||||
"CHANGE_NETWORK_THROTTLING",
|
||||
CHANGE_NETWORK_THROTTLING,
|
||||
|
||||
// The pixel ratio of the viewport has changed. This may be triggered by the user
|
||||
// when changing the device displayed in the viewport, or when a pixel ratio is
|
||||
|
|
|
@ -9,7 +9,6 @@ DevToolsModules(
|
|||
'display-pixel-ratio.js',
|
||||
'index.js',
|
||||
'location.js',
|
||||
'network-throttling.js',
|
||||
'reload-conditions.js',
|
||||
'screenshot.js',
|
||||
'touch-simulation.js',
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
CHANGE_NETWORK_THROTTLING,
|
||||
} = require("./index");
|
||||
|
||||
module.exports = {
|
||||
|
||||
changeNetworkThrottling(enabled, profile) {
|
||||
return {
|
||||
type: CHANGE_NETWORK_THROTTLING,
|
||||
enabled,
|
||||
profile,
|
||||
};
|
||||
},
|
||||
|
||||
};
|
|
@ -18,7 +18,7 @@ const {
|
|||
updateDeviceModal,
|
||||
updatePreferredDevices,
|
||||
} = require("./actions/devices");
|
||||
const { changeNetworkThrottling } = require("./actions/network-throttling");
|
||||
const { changeNetworkThrottling } = require("devtools/client/shared/components/throttling/actions");
|
||||
const { changeReloadCondition } = require("./actions/reload-conditions");
|
||||
const { takeScreenshot } = require("./actions/screenshot");
|
||||
const { changeTouchSimulation } = require("./actions/touch-simulation");
|
||||
|
@ -92,7 +92,7 @@ class App extends Component {
|
|||
|
||||
onChangeNetworkThrottling(enabled, profile) {
|
||||
window.postMessage({
|
||||
type: "change-network-throtting",
|
||||
type: "change-network-throttling",
|
||||
enabled,
|
||||
profile,
|
||||
}, "*");
|
||||
|
|
|
@ -11,7 +11,7 @@ const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
|||
const { getStr } = require("../utils/l10n");
|
||||
const Types = require("../types");
|
||||
const DevicePixelRatioSelector = createFactory(require("./DevicePixelRatioSelector"));
|
||||
const NetworkThrottlingSelector = createFactory(require("./NetworkThrottlingSelector"));
|
||||
const NetworkThrottlingSelector = createFactory(require("devtools/client/shared/components/throttling/NetworkThrottlingSelector"));
|
||||
const ReloadConditions = createFactory(require("./ReloadConditions"));
|
||||
|
||||
class GlobalToolbar extends PureComponent {
|
||||
|
|
|
@ -11,7 +11,6 @@ DevToolsModules(
|
|||
'DevicePixelRatioSelector.js',
|
||||
'DeviceSelector.js',
|
||||
'GlobalToolbar.js',
|
||||
'NetworkThrottlingSelector.js',
|
||||
'ReloadConditions.js',
|
||||
'ResizableViewport.js',
|
||||
'ToggleMenu.js',
|
||||
|
|
|
@ -13,7 +13,7 @@ const TOOL_URL = "chrome://devtools/content/responsive.html/index.xhtml";
|
|||
|
||||
loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/debugger-client", true);
|
||||
loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
|
||||
loader.lazyRequireGetter(this, "throttlingProfiles", "devtools/client/shared/network-throttling-profiles");
|
||||
loader.lazyRequireGetter(this, "throttlingProfiles", "devtools/client/shared/components/throttling/profiles");
|
||||
loader.lazyRequireGetter(this, "swapToInnerBrowser", "devtools/client/responsive.html/browser/swap", true);
|
||||
loader.lazyRequireGetter(this, "startup", "devtools/client/responsive.html/utils/window", true);
|
||||
loader.lazyRequireGetter(this, "message", "devtools/client/responsive.html/utils/message");
|
||||
|
@ -516,7 +516,7 @@ ResponsiveUI.prototype = {
|
|||
case "change-device":
|
||||
this.onChangeDevice(event);
|
||||
break;
|
||||
case "change-network-throtting":
|
||||
case "change-network-throttling":
|
||||
this.onChangeNetworkThrottling(event);
|
||||
break;
|
||||
case "change-pixel-ratio":
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
exports.devices = require("./reducers/devices");
|
||||
exports.displayPixelRatio = require("./reducers/display-pixel-ratio");
|
||||
exports.location = require("./reducers/location");
|
||||
exports.networkThrottling = require("./reducers/network-throttling");
|
||||
exports.networkThrottling = require("devtools/client/shared/components/throttling/reducer");
|
||||
exports.reloadConditions = require("./reducers/reload-conditions");
|
||||
exports.screenshot = require("./reducers/screenshot");
|
||||
exports.touchSimulation = require("./reducers/touch-simulation");
|
||||
|
|
|
@ -8,7 +8,6 @@ DevToolsModules(
|
|||
'devices.js',
|
||||
'display-pixel-ratio.js',
|
||||
'location.js',
|
||||
'network-throttling.js',
|
||||
'reload-conditions.js',
|
||||
'screenshot.js',
|
||||
'touch-simulation.js',
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
CHANGE_NETWORK_THROTTLING,
|
||||
} = require("../actions/index");
|
||||
|
||||
const INITIAL_NETWORK_THROTTLING = {
|
||||
enabled: false,
|
||||
profile: "",
|
||||
};
|
||||
|
||||
let reducers = {
|
||||
|
||||
[CHANGE_NETWORK_THROTTLING](throttling, { enabled, profile }) {
|
||||
return {
|
||||
enabled,
|
||||
profile,
|
||||
};
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = function(throttling = INITIAL_NETWORK_THROTTLING, action) {
|
||||
let reducer = reducers[action.type];
|
||||
if (!reducer) {
|
||||
return throttling;
|
||||
}
|
||||
return reducer(throttling, action);
|
||||
};
|
|
@ -3,7 +3,7 @@ http://creativecommons.org/publicdomain/zero/1.0/ */
|
|||
|
||||
"use strict";
|
||||
|
||||
const throttlingProfiles = require("devtools/client/shared/network-throttling-profiles");
|
||||
const throttlingProfiles = require("devtools/client/shared/components/throttling/profiles");
|
||||
|
||||
// Tests changing network throttling
|
||||
const TEST_URL = "data:text/html;charset=utf-8,Network throttling test";
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
const {
|
||||
changeNetworkThrottling,
|
||||
} = require("devtools/client/responsive.html/actions/network-throttling");
|
||||
} = require("devtools/client/shared/components/throttling/actions");
|
||||
|
||||
add_task(async function() {
|
||||
let store = Store();
|
||||
|
|
|
@ -8,7 +8,8 @@ DIRS += [
|
|||
'reps',
|
||||
'splitter',
|
||||
'tabs',
|
||||
'tree'
|
||||
'throttling',
|
||||
'tree',
|
||||
]
|
||||
|
||||
DevToolsModules(
|
||||
|
|
|
@ -8,18 +8,33 @@ const { PureComponent } = require("devtools/client/shared/vendor/react");
|
|||
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||
|
||||
const Types = require("../types");
|
||||
const { getStr } = require("../utils/l10n");
|
||||
const throttlingProfiles = require("devtools/client/shared/network-throttling-profiles");
|
||||
const Types = require("./types");
|
||||
const throttlingProfiles = require("./profiles");
|
||||
|
||||
// Localization
|
||||
const { LocalizationHelper } = require("devtools/shared/l10n");
|
||||
const L10N = new LocalizationHelper(
|
||||
"devtools/client/locales/network-throttling.properties");
|
||||
|
||||
/**
|
||||
* This component represents selector button that can be used
|
||||
* to throttle network bandwidth.
|
||||
*/
|
||||
class NetworkThrottlingSelector extends PureComponent {
|
||||
static get propTypes() {
|
||||
return {
|
||||
className: PropTypes.string,
|
||||
networkThrottling: PropTypes.shape(Types.networkThrottling).isRequired,
|
||||
onChangeNetworkThrottling: PropTypes.func.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
static get defaultProps() {
|
||||
return {
|
||||
className: "",
|
||||
};
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onSelectChange = this.onSelectChange.bind(this);
|
||||
|
@ -30,7 +45,7 @@ class NetworkThrottlingSelector extends PureComponent {
|
|||
onChangeNetworkThrottling,
|
||||
} = this.props;
|
||||
|
||||
if (target.value == getStr("responsive.noThrottling")) {
|
||||
if (target.value == L10N.getStr("responsive.noThrottling")) {
|
||||
onChangeNetworkThrottling(false, "");
|
||||
return;
|
||||
}
|
||||
|
@ -45,16 +60,17 @@ class NetworkThrottlingSelector extends PureComponent {
|
|||
|
||||
render() {
|
||||
let {
|
||||
className,
|
||||
networkThrottling,
|
||||
} = this.props;
|
||||
|
||||
let selectClass = "toolbar-dropdown";
|
||||
let selectClass = className + " toolbar-dropdown";
|
||||
let selectedProfile;
|
||||
if (networkThrottling.enabled) {
|
||||
selectClass += " selected";
|
||||
selectedProfile = networkThrottling.profile;
|
||||
} else {
|
||||
selectedProfile = getStr("responsive.noThrottling");
|
||||
selectedProfile = L10N.getStr("responsive.noThrottling");
|
||||
}
|
||||
|
||||
let listContent = [
|
||||
|
@ -62,7 +78,7 @@ class NetworkThrottlingSelector extends PureComponent {
|
|||
{
|
||||
key: "disabled",
|
||||
},
|
||||
getStr("responsive.noThrottling")
|
||||
L10N.getStr("responsive.noThrottling")
|
||||
),
|
||||
dom.option(
|
||||
{
|
|
@ -0,0 +1,22 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const actionTypes = {
|
||||
CHANGE_NETWORK_THROTTLING: "CHANGE_NETWORK_THROTTLING",
|
||||
};
|
||||
|
||||
function changeNetworkThrottling(enabled, profile) {
|
||||
return {
|
||||
type: actionTypes.CHANGE_NETWORK_THROTTLING,
|
||||
enabled,
|
||||
profile,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
...actionTypes,
|
||||
changeNetworkThrottling,
|
||||
};
|
|
@ -0,0 +1,13 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
DevToolsModules(
|
||||
'actions.js',
|
||||
'NetworkThrottlingSelector.js',
|
||||
'profiles.js',
|
||||
'reducer.js',
|
||||
'types.js',
|
||||
)
|
|
@ -0,0 +1,29 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
CHANGE_NETWORK_THROTTLING,
|
||||
} = require("./actions");
|
||||
|
||||
const INITIAL_STATE = {
|
||||
enabled: false,
|
||||
profile: "",
|
||||
};
|
||||
|
||||
function throttlingReducer(state = INITIAL_STATE, action) {
|
||||
switch (action.type) {
|
||||
case CHANGE_NETWORK_THROTTLING: {
|
||||
return {
|
||||
enabled: action.enabled,
|
||||
profile: action.profile
|
||||
};
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = throttlingReducer;
|
|
@ -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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||
|
||||
/**
|
||||
* Network throttling state.
|
||||
*/
|
||||
exports.networkThrottling = {
|
||||
// Whether or not network throttling is enabled
|
||||
enabled: PropTypes.bool,
|
||||
// Name of the selected throttling profile
|
||||
profile: PropTypes.string,
|
||||
};
|
|
@ -38,7 +38,6 @@ DevToolsModules(
|
|||
'keycodes.js',
|
||||
'link.js',
|
||||
'natural-sort.js',
|
||||
'network-throttling-profiles.js',
|
||||
'node-attribute-parser.js',
|
||||
'options-view.js',
|
||||
'output-parser.js',
|
||||
|
|
|
@ -2850,11 +2850,7 @@ HTMLMediaElement::Seek(double aTime,
|
|||
// The media backend is responsible for dispatching the timeupdate
|
||||
// event if it changes the playback position as a result of the seek.
|
||||
LOG(LogLevel::Debug, ("%p SetCurrentTime(%f) starting seek", this, aTime));
|
||||
nsresult rv = mDecoder->Seek(aTime, aSeekType);
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
mDecoder->Seek(aTime, aSeekType);
|
||||
|
||||
// We changed whether we're seeking so we need to AddRemoveSelfReference.
|
||||
AddRemoveSelfReference();
|
||||
|
|
|
@ -510,7 +510,8 @@ MediaDecoder::Play()
|
|||
}
|
||||
|
||||
if (IsEnded()) {
|
||||
return Seek(0, SeekTarget::PrevSyncPoint);
|
||||
Seek(0, SeekTarget::PrevSyncPoint);
|
||||
return NS_OK;
|
||||
} else if (mPlayState == PLAY_STATE_LOADING) {
|
||||
mNextState = PLAY_STATE_PLAYING;
|
||||
return NS_OK;
|
||||
|
@ -520,7 +521,7 @@ MediaDecoder::Play()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
void
|
||||
MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
@ -540,7 +541,6 @@ MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType)
|
|||
if (mPlayState == PLAY_STATE_ENDED) {
|
||||
ChangeState(GetOwner()->GetPaused() ? PLAY_STATE_PAUSED : PLAY_STATE_PLAYING);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -134,7 +134,7 @@ public:
|
|||
// Seek to the time position in (seconds) from the start of the video.
|
||||
// If aDoFastSeek is true, we'll seek to the sync point/keyframe preceeding
|
||||
// the seek target.
|
||||
virtual nsresult Seek(double aTime, SeekTarget::Type aSeekType);
|
||||
void Seek(double aTime, SeekTarget::Type aSeekType);
|
||||
|
||||
// Initialize state machine and schedule it.
|
||||
nsresult InitializeStateMachine();
|
||||
|
|
|
@ -3522,18 +3522,25 @@ MediaFormatReader::GetMozDebugReaderData(nsACString& aString)
|
|||
audioDecoderName = mAudio.mDecoder
|
||||
? mAudio.mDecoder->GetDescriptionName()
|
||||
: mAudio.mDescription;
|
||||
audioType = mInfo.mAudio.mMimeType;
|
||||
audioType = mAudio.mInfo ? mAudio.mInfo->mMimeType : mInfo.mAudio.mMimeType;
|
||||
}
|
||||
if (HasVideo()) {
|
||||
MutexAutoLock mon(mVideo.mMutex);
|
||||
videoDecoderName = mVideo.mDecoder
|
||||
? mVideo.mDecoder->GetDescriptionName()
|
||||
: mVideo.mDescription;
|
||||
videoType = mInfo.mVideo.mMimeType;
|
||||
videoType = mVideo.mInfo ? mVideo.mInfo->mMimeType : mInfo.mVideo.mMimeType;
|
||||
}
|
||||
|
||||
result += nsPrintfCString(
|
||||
"Audio Decoder(%s): %s\n", audioType.get(), audioDecoderName.get());
|
||||
result +=
|
||||
nsPrintfCString("Audio Decoder(%s, %u channels @ %0.1fkHz): %s\n",
|
||||
audioType.get(),
|
||||
mAudio.mInfo ? mAudio.mInfo->GetAsAudioInfo()->mChannels
|
||||
: mInfo.mAudio.mChannels,
|
||||
(mAudio.mInfo ? mAudio.mInfo->GetAsAudioInfo()->mRate
|
||||
: mInfo.mAudio.mRate) /
|
||||
1000.0f,
|
||||
audioDecoderName.get());
|
||||
result += nsPrintfCString("Audio Frames Decoded: %" PRIu64 "\n",
|
||||
mAudio.mNumSamplesOutputTotal);
|
||||
if (HasAudio()) {
|
||||
|
|
|
@ -418,6 +418,12 @@ MediaSource::Enabled(JSContext* cx, JSObject* aGlobal)
|
|||
return Preferences::GetBool("media.mediasource.enabled");
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
MediaSource::ExperimentalEnabled(JSContext* cx, JSObject* aGlobal)
|
||||
{
|
||||
return Preferences::GetBool("media.mediasource.experimental.enabled");
|
||||
}
|
||||
|
||||
void
|
||||
MediaSource::SetLiveSeekableRange(double aStart, double aEnd, ErrorResult& aRv)
|
||||
{
|
||||
|
|
|
@ -79,6 +79,7 @@ public:
|
|||
static nsresult IsTypeSupported(const nsAString& aType, DecoderDoctorDiagnostics* aDiagnostics);
|
||||
|
||||
static bool Enabled(JSContext* cx, JSObject* aGlobal);
|
||||
static bool ExperimentalEnabled(JSContext* cx, JSObject* aGlobal);
|
||||
|
||||
IMPL_EVENT_HANDLER(sourceopen);
|
||||
IMPL_EVENT_HANDLER(sourceended);
|
||||
|
|
|
@ -297,6 +297,53 @@ SourceBuffer::RangeRemoval(double aStart, double aEnd)
|
|||
->Track(mPendingRemoval);
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::ChangeType(const nsAString& aType, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
DecoderDoctorDiagnostics diagnostics;
|
||||
nsresult rv = MediaSource::IsTypeSupported(aType, &diagnostics);
|
||||
diagnostics.StoreFormatDiagnostics(mMediaSource->GetOwner()
|
||||
? mMediaSource->GetOwner()->GetExtantDoc()
|
||||
: nullptr,
|
||||
aType, NS_SUCCEEDED(rv), __func__);
|
||||
MSE_API("ChangeType(aType=%s)%s",
|
||||
NS_ConvertUTF16toUTF8(aType).get(),
|
||||
rv == NS_OK ? "" : " [not supported]");
|
||||
if (NS_FAILED(rv)) {
|
||||
DDLOG(DDLogCategory::API, "ChangeType", rv);
|
||||
aRv.Throw(rv);
|
||||
return;
|
||||
}
|
||||
if (!mMediaSource->GetDecoder() ||
|
||||
mMediaSource->GetDecoder()->OwnerHasError()) {
|
||||
MSE_DEBUG("HTMLMediaElement.error is not null");
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
if (!IsAttached() || mUpdating) {
|
||||
DDLOG(DDLogCategory::API, "ChangeType", NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mMediaSource->ReadyState() != MediaSourceReadyState::Closed);
|
||||
if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
|
||||
mMediaSource->SetReadyState(MediaSourceReadyState::Open);
|
||||
}
|
||||
if (mCurrentAttributes.GetAppendState() == AppendState::PARSING_MEDIA_SEGMENT){
|
||||
DDLOG(DDLogCategory::API, "ChangeType", NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
Maybe<MediaContainerType> containerType = MakeMediaContainerType(aType);
|
||||
MOZ_ASSERT(containerType);
|
||||
mType = *containerType;
|
||||
ResetParserState();
|
||||
mTrackBuffersManager->ChangeType(mType);
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::Detach()
|
||||
{
|
||||
|
|
|
@ -93,6 +93,8 @@ public:
|
|||
|
||||
void Remove(double aStart, double aEnd, ErrorResult& aRv);
|
||||
|
||||
void ChangeType(const nsAString& aType, ErrorResult& aRv);
|
||||
|
||||
IMPL_EVENT_HANDLER(updatestart);
|
||||
IMPL_EVENT_HANDLER(update);
|
||||
IMPL_EVENT_HANDLER(updateend);
|
||||
|
@ -183,7 +185,7 @@ private:
|
|||
|
||||
MozPromiseRequestHolder<SourceBufferTask::AppendPromise> mPendingAppend;
|
||||
MozPromiseRequestHolder<SourceBufferTask::RangeRemovalPromise> mPendingRemoval;
|
||||
const MediaContainerType mType;
|
||||
MediaContainerType mType;
|
||||
|
||||
RefPtr<TimeRanges> mBuffered;
|
||||
|
||||
|
|
|
@ -24,7 +24,8 @@ public:
|
|||
Reset,
|
||||
RangeRemoval,
|
||||
EvictData,
|
||||
Detach
|
||||
Detach,
|
||||
ChangeType
|
||||
};
|
||||
|
||||
typedef Pair<bool, SourceBufferAttributes> AppendBufferResult;
|
||||
|
@ -112,6 +113,20 @@ public:
|
|||
const char* GetTypeName() const override { return "Detach"; }
|
||||
};
|
||||
|
||||
class ChangeTypeTask : public SourceBufferTask {
|
||||
public:
|
||||
explicit ChangeTypeTask(const MediaContainerType& aType)
|
||||
: mType(aType)
|
||||
{
|
||||
}
|
||||
|
||||
static const Type sType = Type::ChangeType;
|
||||
Type GetType() const override { return Type::ChangeType; }
|
||||
const char* GetTypeName() const override { return "ChangeType"; }
|
||||
|
||||
const MediaContainerType mType;
|
||||
};
|
||||
|
||||
} // end mozilla namespace
|
||||
|
||||
#endif
|
||||
|
|
|
@ -111,6 +111,7 @@ TrackBuffersManager::TrackBuffersManager(MediaSourceDecoder* aParentDecoder,
|
|||
: mInputBuffer(new MediaByteBuffer)
|
||||
, mBufferFull(false)
|
||||
, mFirstInitializationSegmentReceived(false)
|
||||
, mChangeTypeReceived(false)
|
||||
, mNewMediaSegmentStarted(false)
|
||||
, mActiveTrack(false)
|
||||
, mType(aType)
|
||||
|
@ -280,6 +281,14 @@ TrackBuffersManager::ProcessTasks()
|
|||
ShutdownDemuxers();
|
||||
ResetTaskQueue();
|
||||
return;
|
||||
case Type::ChangeType:
|
||||
MOZ_RELEASE_ASSERT(!mCurrentTask);
|
||||
mType = task->As<ChangeTypeTask>()->mType;
|
||||
mChangeTypeReceived = true;
|
||||
mInitData = nullptr;
|
||||
CompleteResetParserState();
|
||||
CreateDemuxerforMIMEType();
|
||||
break;
|
||||
default:
|
||||
NS_WARNING("Invalid Task");
|
||||
}
|
||||
|
@ -385,6 +394,15 @@ TrackBuffersManager::EvictData(const TimeUnit& aPlaybackTime, int64_t aSize)
|
|||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
TrackBuffersManager::ChangeType(const MediaContainerType& aType)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
QueueTask(new ChangeTypeTask(aType));
|
||||
}
|
||||
|
||||
|
||||
TimeIntervals
|
||||
TrackBuffersManager::Buffered() const
|
||||
{
|
||||
|
@ -475,10 +493,13 @@ TrackBuffersManager::CompleteResetParserState()
|
|||
}
|
||||
|
||||
// We could be left with a demuxer in an unusable state. It needs to be
|
||||
// recreated. We store in the InputBuffer an init segment which will be parsed
|
||||
// during the next Segment Parser Loop and a new demuxer will be created and
|
||||
// initialized.
|
||||
if (mFirstInitializationSegmentReceived) {
|
||||
// recreated. Unless we have a pending changeType operation, we store in the
|
||||
// InputBuffer an init segment which will be parsed during the next Segment
|
||||
// Parser Loop and a new demuxer will be created and initialized.
|
||||
// If we are in the middle of a changeType operation, then we do not have an
|
||||
// init segment yet. The next appendBuffer operation will need to provide such
|
||||
// init segment.
|
||||
if (mFirstInitializationSegmentReceived && !mChangeTypeReceived) {
|
||||
MOZ_ASSERT(mInitData && mInitData->Length(), "we must have an init segment");
|
||||
// The aim here is really to destroy our current demuxer.
|
||||
CreateDemuxerforMIMEType();
|
||||
|
@ -486,8 +507,10 @@ TrackBuffersManager::CompleteResetParserState()
|
|||
// to mInputBuffer as it will get modified in the Segment Parser Loop.
|
||||
mInputBuffer = new MediaByteBuffer;
|
||||
mInputBuffer->AppendElements(*mInitData);
|
||||
RecreateParser(true);
|
||||
} else {
|
||||
RecreateParser(false);
|
||||
}
|
||||
RecreateParser(true);
|
||||
}
|
||||
|
||||
int64_t
|
||||
|
@ -721,7 +744,7 @@ TrackBuffersManager::SegmentParserLoop()
|
|||
MediaResult haveInitSegment = mParser->IsInitSegmentPresent(mInputBuffer);
|
||||
if (NS_SUCCEEDED(haveInitSegment)) {
|
||||
SetAppendState(AppendState::PARSING_INIT_SEGMENT);
|
||||
if (mFirstInitializationSegmentReceived) {
|
||||
if (mFirstInitializationSegmentReceived && !mChangeTypeReceived) {
|
||||
// This is a new initialization segment. Obsolete the old one.
|
||||
RecreateParser(false);
|
||||
}
|
||||
|
@ -772,8 +795,12 @@ TrackBuffersManager::SegmentParserLoop()
|
|||
return;
|
||||
}
|
||||
if (mSourceBufferAttributes->GetAppendState() == AppendState::PARSING_MEDIA_SEGMENT) {
|
||||
// 1. If the first initialization segment received flag is false, then run the append error algorithm with the decode error parameter set to true and abort this algorithm.
|
||||
if (!mFirstInitializationSegmentReceived) {
|
||||
// 1. If the first initialization segment received flag is false, then run
|
||||
// the append error algorithm with the decode error parameter set to
|
||||
// true and abort this algorithm.
|
||||
// Or we are in the process of changeType, in which case we must first
|
||||
// get an init segment before getting a media segment.
|
||||
if (!mFirstInitializationSegmentReceived || mChangeTypeReceived) {
|
||||
RejectAppend(NS_ERROR_FAILURE, __func__);
|
||||
return;
|
||||
}
|
||||
|
@ -1108,13 +1135,17 @@ TrackBuffersManager::OnDemuxerInitDone(const MediaResult& aResult)
|
|||
if (mFirstInitializationSegmentReceived) {
|
||||
if (numVideos != mVideoTracks.mNumTracks ||
|
||||
numAudios != mAudioTracks.mNumTracks ||
|
||||
(numVideos && info.mVideo.mMimeType != mVideoTracks.mInfo->mMimeType) ||
|
||||
(numAudios && info.mAudio.mMimeType != mAudioTracks.mInfo->mMimeType)) {
|
||||
(!mChangeTypeReceived &&
|
||||
((numVideos &&
|
||||
info.mVideo.mMimeType != mVideoTracks.mInfo->mMimeType) ||
|
||||
(numAudios &&
|
||||
info.mAudio.mMimeType != mAudioTracks.mInfo->mMimeType)))) {
|
||||
RejectAppend(NS_ERROR_FAILURE, __func__);
|
||||
return;
|
||||
}
|
||||
// 1. If more than one track for a single type are present (ie 2 audio tracks),
|
||||
// then the Track IDs match the ones in the first initialization segment.
|
||||
// 1. If more than one track for a single type are present (ie 2 audio
|
||||
// tracks), then the Track IDs match the ones in the first initialization
|
||||
// segment.
|
||||
// TODO
|
||||
// 2. Add the appropriate track descriptions from this initialization
|
||||
// segment to each of the track buffers.
|
||||
|
@ -1214,6 +1245,9 @@ TrackBuffersManager::OnDemuxerInitDone(const MediaResult& aResult)
|
|||
mVideoTracks.mLastInfo = new TrackInfoSharedPtr(info.mVideo, streamID);
|
||||
}
|
||||
|
||||
// We have now completed the changeType operation.
|
||||
mChangeTypeReceived = false;
|
||||
|
||||
UniquePtr<EncryptionInfo> crypto = mInputDemuxer->GetCrypto();
|
||||
if (crypto && crypto->IsEncrypted()) {
|
||||
// Try and dispatch 'encrypted'. Won't go if ready state still HAVE_NOTHING.
|
||||
|
|
|
@ -115,6 +115,9 @@ public:
|
|||
// and if still more space is needed remove from the end.
|
||||
EvictDataResult EvictData(const media::TimeUnit& aPlaybackTime, int64_t aSize);
|
||||
|
||||
// Queue a task to run ChangeType
|
||||
void ChangeType(const MediaContainerType& aType);
|
||||
|
||||
// Returns the buffered range currently managed.
|
||||
// This may be called on any thread.
|
||||
// Buffered must conform to http://w3c.github.io/media-source/index.html#widl-SourceBuffer-buffered
|
||||
|
@ -205,10 +208,11 @@ private:
|
|||
// Accessed on both the main thread and the task queue.
|
||||
Atomic<bool> mBufferFull;
|
||||
bool mFirstInitializationSegmentReceived;
|
||||
bool mChangeTypeReceived;
|
||||
// Set to true once a new segment is started.
|
||||
bool mNewMediaSegmentStarted;
|
||||
bool mActiveTrack;
|
||||
const MediaContainerType mType;
|
||||
MediaContainerType mType;
|
||||
|
||||
// ContainerParser objects and methods.
|
||||
// Those are used to parse the incoming input buffer.
|
||||
|
|
Двоичный файл не отображается.
|
@ -0,0 +1 @@
|
|||
Cache-Control: no-store
|
|
@ -4,6 +4,7 @@ support-files =
|
|||
mediasource.js
|
||||
seek.webm seek.webm^headers^
|
||||
seek_lowres.webm seek_lowres.webm^headers^
|
||||
bipbop/bipbop_300-3s.webm bipbop/bipbop_300-3s.webm^headers^
|
||||
bipbop/bipbop2s.mp4 bipbop/bipbop2s.mp4^headers^
|
||||
bipbop/bipbopinit.mp4 bipbop/bipbop_audioinit.mp4 bipbop/bipbop_videoinit.mp4
|
||||
bipbop/bipbop1.m4s bipbop/bipbop_audio1.m4s bipbop/bipbop_video1.m4s
|
||||
|
@ -63,6 +64,8 @@ skip-if = toolkit == 'android' # Not supported on android
|
|||
skip-if = toolkit == 'android' #timeout android bug 1199531
|
||||
[test_BufferingWait_mp4.html]
|
||||
skip-if = toolkit == 'android' # Not supported on android
|
||||
[test_ChangeType.html]
|
||||
skip-if = toolkit == 'android' # Not supported on android
|
||||
[test_ChangeWhileWaitingOnMissingData_mp4.html]
|
||||
skip-if = toolkit == 'android' # Not supported on android
|
||||
[test_DrainOnMissingData_mp4.html]
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=windows-1252">
|
||||
<title>MSE: changeType allow to change container and codec type</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="mediasource.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test"><script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
async function setupTest() {
|
||||
await SpecialPowers.pushPrefEnv({'set': [['media.mediasource.experimental.enabled', true]]});
|
||||
}
|
||||
setupTest();
|
||||
|
||||
runWithMSE(function(ms, el) {
|
||||
el.controls = true;
|
||||
once(ms, 'sourceopen').then(function() {
|
||||
// Log events for debugging.
|
||||
var events = ["suspend", "play", "canplay", "canplaythrough", "loadstart", "loadedmetadata",
|
||||
"loadeddata", "playing", "ended", "error", "stalled", "emptied", "abort",
|
||||
"waiting", "pause", "durationchange", "seeking", "seeked"];
|
||||
function logEvent(e) {
|
||||
info("got " + e.type + " event");
|
||||
}
|
||||
events.forEach(function(e) {
|
||||
el.addEventListener(e, logEvent);
|
||||
});
|
||||
|
||||
ok(true, "Receive a sourceopen event");
|
||||
|
||||
var videosb = ms.addSourceBuffer("video/mp4");
|
||||
if (typeof videosb.changeType === 'undefined') {
|
||||
info('changeType API is not available');
|
||||
}
|
||||
|
||||
el.addEventListener("error", function(e) {
|
||||
ok(false, "should not fire '" + e.type + "' event");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
is(el.readyState, el.HAVE_NOTHING, "readyState is HAVE_NOTHING");
|
||||
var promises = [];
|
||||
promises.push(fetchAndLoad(videosb, 'bipbop/bipbop', ['init'], '.mp4'));
|
||||
promises.push(once(el, 'loadedmetadata'));
|
||||
Promise.all(promises)
|
||||
.then(function() {
|
||||
ok(true, "got loadedmetadata event");
|
||||
var promises = [];
|
||||
promises.push(once(el, 'loadeddata'));
|
||||
promises.push(once(el, 'canplay'));
|
||||
promises.push(fetchAndLoad(videosb, 'bipbop/bipbop', range(1,3), '.m4s'));
|
||||
return Promise.all(promises);
|
||||
})
|
||||
.then(function() {
|
||||
ok(true, "got canplay event");
|
||||
el.play();
|
||||
videosb.timestampOffset = el.buffered.end(0);
|
||||
return fetchAndLoad(videosb, 'bipbop/bipbop_480_624kbps-video', ['init'], '.mp4');
|
||||
})
|
||||
.then(fetchAndLoad.bind(null, videosb, 'bipbop/bipbop_480_624kbps-video', range(1,3), '.m4s'))
|
||||
.then(function() {
|
||||
videosb.timestampOffset = el.buffered.end(0);
|
||||
try {
|
||||
videosb.changeType("video/webm");
|
||||
} catch (e) {
|
||||
ok(false, "shouldn't throw an exception");
|
||||
SimpleTest.finish();
|
||||
throw e;
|
||||
}
|
||||
return fetchAndLoad(videosb, 'bipbop/bipbop_300-3s', [''], '.webm');
|
||||
})
|
||||
.then(function() {
|
||||
ms.endOfStream();
|
||||
return once(el, 'ended');
|
||||
})
|
||||
.then(function() {
|
||||
ok(el.currentTime >= el.buffered.end(0), "played to the end");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -146,7 +146,7 @@ public:
|
|||
|
||||
ConstantSourceNode::ConstantSourceNode(AudioContext* aContext)
|
||||
: AudioScheduledSourceNode(aContext,
|
||||
1,
|
||||
2,
|
||||
ChannelCountMode::Max,
|
||||
ChannelInterpretation::Speakers)
|
||||
, mOffset(new AudioParam(this, ConstantSourceNodeEngine::OFFSET,
|
||||
|
|
|
@ -46,4 +46,8 @@ interface SourceBuffer : EventTarget {
|
|||
void abort();
|
||||
[Throws]
|
||||
void remove(double start, unrestricted double end);
|
||||
// Experimental function as proposed in:
|
||||
// https://github.com/w3c/media-source/issues/155
|
||||
[Throws, Func="mozilla::dom::MediaSource::ExperimentalEnabled"]
|
||||
void changeType(DOMString type);
|
||||
};
|
||||
|
|
|
@ -424,7 +424,7 @@ bool Pickle::ExtractBuffers(PickleIterator* iter, size_t length, BufferList* buf
|
|||
DCHECK(alignment == 4 || alignment == 8);
|
||||
DCHECK(intptr_t(header_) % alignment == 0);
|
||||
|
||||
if (AlignInt(length) < length) {
|
||||
if (AlignInt(length) < length || iter->iter_.Done()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -354,6 +354,49 @@ SentinelReadError(const char* aClassName)
|
|||
MOZ_CRASH_UNSAFE_PRINTF("incorrect sentinel when reading %s", aClassName);
|
||||
}
|
||||
|
||||
void
|
||||
StateTransition(bool aIsDelete, State* aNext)
|
||||
{
|
||||
switch (*aNext) {
|
||||
case State::Null:
|
||||
if (aIsDelete) {
|
||||
*aNext = State::Dead;
|
||||
}
|
||||
break;
|
||||
case State::Dead:
|
||||
LogicError("__delete__()d actor");
|
||||
break;
|
||||
default:
|
||||
LogicError("corrupted actor state");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ReEntrantDeleteStateTransition(bool aIsDelete,
|
||||
bool aIsDeleteReply,
|
||||
ReEntrantDeleteState* aNext)
|
||||
{
|
||||
switch (*aNext) {
|
||||
case ReEntrantDeleteState::Null:
|
||||
if (aIsDelete) {
|
||||
*aNext = ReEntrantDeleteState::Dying;
|
||||
}
|
||||
break;
|
||||
case ReEntrantDeleteState::Dead:
|
||||
LogicError("__delete__()d actor");
|
||||
break;
|
||||
case ReEntrantDeleteState::Dying:
|
||||
if (aIsDeleteReply) {
|
||||
*aNext = ReEntrantDeleteState::Dead;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LogicError("corrupted actor state");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TableToArray(const nsTHashtable<nsPtrHashKey<void>>& aTable,
|
||||
nsTArray<void*>& aArray)
|
||||
|
|
|
@ -131,6 +131,7 @@ enum RacyInterruptPolicy {
|
|||
RIPParentWins
|
||||
};
|
||||
|
||||
|
||||
class IToplevelProtocol;
|
||||
|
||||
class IProtocol : public HasResultCodes
|
||||
|
@ -749,6 +750,29 @@ DuplicateHandle(HANDLE aSourceHandle,
|
|||
*/
|
||||
void AnnotateSystemError();
|
||||
|
||||
enum class State
|
||||
{
|
||||
Dead,
|
||||
Null,
|
||||
Start = Null
|
||||
};
|
||||
|
||||
void
|
||||
StateTransition(bool aIsDelete, State* aNext);
|
||||
|
||||
enum class ReEntrantDeleteState
|
||||
{
|
||||
Dead,
|
||||
Null,
|
||||
Dying,
|
||||
Start = Null,
|
||||
};
|
||||
|
||||
void
|
||||
ReEntrantDeleteStateTransition(bool aIsDelete,
|
||||
bool aIsDeleteReply,
|
||||
ReEntrantDeleteState* aNext);
|
||||
|
||||
/**
|
||||
* An endpoint represents one end of a partially initialized IPDL channel. To
|
||||
* set up a new top-level protocol:
|
||||
|
|
|
@ -138,28 +138,6 @@ def _backstagePass():
|
|||
def _iterType(ptr):
|
||||
return Type('PickleIterator', ptr=ptr)
|
||||
|
||||
def _nullState(proto=None):
|
||||
pfx = ''
|
||||
if proto is not None: pfx = proto.name() +'::'
|
||||
return ExprVar(pfx +'__Null')
|
||||
|
||||
def _deadState(proto=None):
|
||||
pfx = ''
|
||||
if proto is not None: pfx = proto.name() +'::'
|
||||
return ExprVar(pfx +'__Dead')
|
||||
|
||||
def _dyingState(proto=None):
|
||||
pfx = ''
|
||||
if proto is not None: pfx = proto.name() +'::'
|
||||
return ExprVar(pfx +'__Dying')
|
||||
|
||||
def _startState(proto=None, fq=False):
|
||||
pfx = ''
|
||||
if proto:
|
||||
if fq: pfx = proto.fullname() +'::'
|
||||
else: pfx = proto.name() +'::'
|
||||
return ExprVar(pfx +'__Start')
|
||||
|
||||
def _deleteId():
|
||||
return ExprVar('Msg___delete____ID')
|
||||
|
||||
|
@ -1077,6 +1055,17 @@ def _subtreeUsesShmem(p):
|
|||
return True
|
||||
return False
|
||||
|
||||
def _stateType(hasReentrantDelete):
|
||||
if hasReentrantDelete:
|
||||
return Type('mozilla::ipc::ReEntrantDeleteState')
|
||||
else:
|
||||
return Type('mozilla::ipc::State')
|
||||
|
||||
def _startState(hasReentrantDelete):
|
||||
pfx = _stateType(hasReentrantDelete).name + '::'
|
||||
return ExprVar(pfx + 'Start')
|
||||
|
||||
|
||||
class Protocol(ipdl.ast.Protocol):
|
||||
def cxxTypedefs(self):
|
||||
return self.decl.cxxtypedefs
|
||||
|
@ -1197,16 +1186,23 @@ class Protocol(ipdl.ast.Protocol):
|
|||
return ExprVar('mState')
|
||||
|
||||
def fqStateType(self):
|
||||
return Type(self.decl.type.name() +'::State')
|
||||
return _stateType(self.decl.type.hasReentrantDelete)
|
||||
|
||||
def startState(self):
|
||||
return _startState(self.decl.type)
|
||||
return _startState(self.decl.type.hasReentrantDelete)
|
||||
|
||||
def nullState(self):
|
||||
return _nullState(self.decl.type)
|
||||
pfx = self.fqStateType().name + '::'
|
||||
return ExprVar(pfx + 'Null')
|
||||
|
||||
def deadState(self):
|
||||
return _deadState(self.decl.type)
|
||||
pfx = self.fqStateType().name + '::'
|
||||
return ExprVar(pfx + 'Dead')
|
||||
|
||||
def dyingState(self):
|
||||
assert self.decl.type.hasReentrantDelete
|
||||
pfx = self.fqStateType().name + '::'
|
||||
return ExprVar(pfx + 'Dying')
|
||||
|
||||
def managerVar(self, thisexpr=None):
|
||||
assert thisexpr is not None or not self.decl.type.isToplevel()
|
||||
|
@ -1570,26 +1566,10 @@ class _GenerateProtocolCode(ipdl.ast.Visitor):
|
|||
ns.addstmts([ edecl, Whitespace.NL ])
|
||||
self.funcDefns.append(edefn)
|
||||
|
||||
# state information
|
||||
stateenum = TypeEnum('State')
|
||||
# NB: __Dead is the first state on purpose, so that it has
|
||||
# value '0'
|
||||
stateenum.addId(_deadState().name)
|
||||
stateenum.addId(_nullState().name)
|
||||
if self.protocol.decl.type.hasReentrantDelete:
|
||||
stateenum.addId(_dyingState().name)
|
||||
stateenum.addId(_startState().name, _nullState().name)
|
||||
|
||||
ns.addstmts([ StmtDecl(Decl(stateenum,'')), Whitespace.NL ])
|
||||
|
||||
# spit out message type enum and classes
|
||||
msgenum = msgenums(self.protocol)
|
||||
ns.addstmts([ StmtDecl(Decl(msgenum, '')), Whitespace.NL ])
|
||||
|
||||
tfDecl, tfDefn = _splitFuncDeclDefn(self.genTransitionFunc())
|
||||
ns.addstmts([ tfDecl, Whitespace.NL ])
|
||||
self.funcDefns.append(tfDefn)
|
||||
|
||||
for md in p.messageDecls:
|
||||
decls = []
|
||||
|
||||
|
@ -1645,62 +1625,6 @@ class _GenerateProtocolCode(ipdl.ast.Visitor):
|
|||
return openfunc
|
||||
|
||||
|
||||
def genTransitionFunc(self):
|
||||
ptype = self.protocol.decl.type
|
||||
|
||||
# bool Transition(MessageType msg, State* next)
|
||||
# The state we are transitioning from is stored in *next.
|
||||
msgtypevar = ExprVar('msg')
|
||||
nextvar = ExprVar('next')
|
||||
|
||||
transitionfunc = FunctionDefn(FunctionDecl(
|
||||
'Transition',
|
||||
params=[ Decl(Type('MessageType'), msgtypevar.name),
|
||||
Decl(Type('State', ptr=1), nextvar.name) ],
|
||||
ret=Type.VOID))
|
||||
|
||||
fromswitch = StmtSwitch(ExprDeref(nextvar))
|
||||
|
||||
# special case for Null
|
||||
nullerrorblock = Block()
|
||||
if ptype.hasDelete:
|
||||
ifdelete = StmtIf(ExprBinary(_deleteId(), '==', msgtypevar))
|
||||
if ptype.hasReentrantDelete:
|
||||
nextState = _dyingState()
|
||||
else:
|
||||
nextState = _deadState()
|
||||
ifdelete.addifstmt(
|
||||
StmtExpr(ExprAssn(ExprDeref(nextvar), nextState)))
|
||||
nullerrorblock.addstmt(ifdelete)
|
||||
nullerrorblock.addstmt(StmtBreak())
|
||||
fromswitch.addcase(CaseLabel(_nullState().name), nullerrorblock)
|
||||
|
||||
# special case for Dead
|
||||
deadblock = Block()
|
||||
deadblock.addstmts([
|
||||
_logicError('__delete__()d actor'),
|
||||
StmtBreak() ])
|
||||
fromswitch.addcase(CaseLabel(_deadState().name), deadblock)
|
||||
|
||||
# special case for Dying
|
||||
if ptype.hasReentrantDelete:
|
||||
dyingblock = Block()
|
||||
ifdelete = StmtIf(ExprBinary(_deleteReplyId(), '==', msgtypevar))
|
||||
ifdelete.addifstmt(
|
||||
StmtExpr(ExprAssn(ExprDeref(nextvar), _deadState())))
|
||||
dyingblock.addstmt(ifdelete)
|
||||
dyingblock.addstmt(StmtBreak())
|
||||
fromswitch.addcase(CaseLabel(_dyingState().name), dyingblock)
|
||||
|
||||
unreachedblock = Block()
|
||||
unreachedblock.addstmts([
|
||||
_logicError('corrupted actor state'),
|
||||
StmtBreak() ])
|
||||
fromswitch.addcase(DefaultLabel(), unreachedblock)
|
||||
|
||||
transitionfunc.addstmt(fromswitch)
|
||||
|
||||
return transitionfunc
|
||||
|
||||
##--------------------------------------------------
|
||||
|
||||
|
@ -3061,6 +2985,9 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
|||
self.hdrfile.addthings([
|
||||
CppDirective('include', '"mozilla/ipc/MessageChannel.h"'),
|
||||
Whitespace.NL ])
|
||||
self.hdrfile.addthings([
|
||||
CppDirective('include', '"mozilla/ipc/ProtocolUtils.h"'),
|
||||
Whitespace.NL ])
|
||||
|
||||
hasAsyncReturns = False
|
||||
for md in p.messageDecls:
|
||||
|
@ -3127,8 +3054,6 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
|||
|
||||
self.cls.addstmt(Whitespace.NL)
|
||||
|
||||
self.cls.addstmts([ Typedef(p.fqStateType(), 'State'), Whitespace.NL ])
|
||||
|
||||
if hasAsyncReturns:
|
||||
self.cls.addstmt(Label.PUBLIC)
|
||||
for md in p.messageDecls:
|
||||
|
@ -3400,7 +3325,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
|||
if ptype.hasReentrantDelete:
|
||||
msgVar = ExprVar(params[0].name)
|
||||
ifdying = StmtIf(ExprBinary(
|
||||
ExprBinary(ExprVar('mState'), '==', _dyingState(ptype)),
|
||||
ExprBinary(ExprVar('mState'), '==', self.protocol.dyingState()),
|
||||
'&&',
|
||||
ExprBinary(
|
||||
ExprBinary(ExprCall(ExprSelect(msgVar, '.', 'is_reply')), '!=', ExprLiteral.TRUE),
|
||||
|
@ -3619,7 +3544,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
|||
self.cls.addstmts([ deallocself, Whitespace.NL ])
|
||||
|
||||
## private members
|
||||
self.cls.addstmt(StmtDecl(Decl(Type('State'), p.stateVar().name)))
|
||||
self.cls.addstmt(StmtDecl(Decl(self.protocol.fqStateType(), p.stateVar().name)))
|
||||
|
||||
for managed in ptype.manages:
|
||||
self.cls.addstmts([
|
||||
|
@ -3902,7 +3827,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
|||
self.side),
|
||||
actorvar)),
|
||||
StmtExpr(ExprAssn(_actorState(actorvar),
|
||||
_startState(actorproto, fq=1)))
|
||||
_startState(md.decl.type.cdtype.hasReentrantDelete)))
|
||||
]
|
||||
|
||||
def failCtorIf(self, md, cond):
|
||||
|
@ -4703,13 +4628,28 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
|||
return idvar, saveIdStmts
|
||||
|
||||
def transition(self, md, actor=None, reply=False):
|
||||
if actor is not None: stateexpr = _actorState(actor)
|
||||
else: stateexpr = self.protocol.stateVar()
|
||||
|
||||
msgid = md.pqMsgId() if not reply else md.pqReplyId()
|
||||
return [ StmtExpr(ExprCall(ExprVar(self.protocol.name +'::Transition'),
|
||||
args=[ ExprVar(msgid),
|
||||
ExprAddrOf(stateexpr) ])) ]
|
||||
args = [
|
||||
ExprVar('true' if _deleteId().name == msgid else 'false'),
|
||||
]
|
||||
if self.protocol.decl.type.hasReentrantDelete:
|
||||
function = 'ReEntrantDeleteStateTransition'
|
||||
args.append(
|
||||
ExprVar('true' if _deleteReplyId().name == msgid else 'false'),
|
||||
)
|
||||
else:
|
||||
function = 'StateTransition'
|
||||
|
||||
if actor is not None:
|
||||
stateexpr = _actorState(actor)
|
||||
else:
|
||||
stateexpr = self.protocol.stateVar()
|
||||
|
||||
args.append(ExprAddrOf(stateexpr))
|
||||
|
||||
return [
|
||||
StmtExpr(ExprCall(ExprVar(function), args=args))
|
||||
]
|
||||
|
||||
def endRead(self, msgexpr, iterexpr):
|
||||
msgtype = ExprCall(ExprSelect(msgexpr, '.', 'type'), [ ])
|
||||
|
|
|
@ -263,9 +263,9 @@ var XPCOMUtils = {
|
|||
* @param aServices
|
||||
* An object with a property for each service to be
|
||||
* imported, where the property name is the name of the
|
||||
* symbol to define, and the value is a 2-element array
|
||||
* containing the contract ID and the interface name of the
|
||||
* service, as passed to defineLazyServiceGetter.
|
||||
* symbol to define, and the value is a 1 or 2 element array
|
||||
* containing the contract ID and, optionally, the interface
|
||||
* name of the service, as passed to defineLazyServiceGetter.
|
||||
*/
|
||||
defineLazyServiceGetters: function XPCU_defineLazyServiceGetters(
|
||||
aObject, aServices)
|
||||
|
@ -274,7 +274,7 @@ var XPCOMUtils = {
|
|||
// Note: This is hot code, and cross-compartment array wrappers
|
||||
// are not JIT-friendly to destructuring or spread operators, so
|
||||
// we need to use indexed access instead.
|
||||
this.defineLazyServiceGetter(aObject, name, service[0], service[1]);
|
||||
this.defineLazyServiceGetter(aObject, name, service[0], service[1] || null);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -7,10 +7,6 @@ _DEST_DIR = $(DEPTH)/_tests/reftest
|
|||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
# We're installing to _tests/reftest
|
||||
TARGET_DEPTH = ../..
|
||||
include $(topsrcdir)/build/automation-build.mk
|
||||
|
||||
# copy harness and the reftest extension bits to $(_DEST_DIR)
|
||||
# This needs to happen after jar.mn handling from rules.mk included above.
|
||||
# The order of the :: rules ensures that.
|
||||
|
|
|
@ -14,9 +14,7 @@ JAR_MANIFESTS += ['jar.mn']
|
|||
FINAL_TARGET_PP_FILES += ['install.rdf']
|
||||
FINAL_TARGET_FILES += ['bootstrap.js']
|
||||
|
||||
GENERATED_FILES += ['automation.py']
|
||||
TEST_HARNESS_FILES.reftest += [
|
||||
'!automation.py',
|
||||
'/build/mobile/remoteautomation.py',
|
||||
'/build/pgo/server-locations.txt',
|
||||
'/testing/mochitest/server.js',
|
||||
|
|
|
@ -551,6 +551,9 @@ pref("media.mediasource.webm.enabled", true);
|
|||
#endif
|
||||
pref("media.mediasource.webm.audio.enabled", true);
|
||||
|
||||
// Whether to enable MediaSource v2 support.
|
||||
pref("media.mediasource.experimental.enabled", false);
|
||||
|
||||
pref("media.benchmark.vp9.threshold", 150);
|
||||
pref("media.benchmark.frames", 300);
|
||||
pref("media.benchmark.timeout", 1000);
|
||||
|
|
|
@ -21,6 +21,7 @@ class FreeBSDBootstrapper(BaseBootstrapper):
|
|||
'npm',
|
||||
'pkgconf',
|
||||
'py%s%s-sqlite3' % sys.version_info[0:2],
|
||||
'python3',
|
||||
'rust',
|
||||
'watchman',
|
||||
'zip',
|
||||
|
|
|
@ -40,7 +40,10 @@ from ..frontend.data import (
|
|||
ObjdirFiles,
|
||||
PerSourceFlag,
|
||||
Program,
|
||||
SimpleProgram,
|
||||
HostLibrary,
|
||||
HostProgram,
|
||||
HostSimpleProgram,
|
||||
SharedLibrary,
|
||||
Sources,
|
||||
StaticLibrary,
|
||||
|
@ -53,6 +56,7 @@ from ..util import (
|
|||
from ..frontend.context import (
|
||||
AbsolutePath,
|
||||
ObjDirPath,
|
||||
RenamedSourcePath,
|
||||
)
|
||||
|
||||
|
||||
|
@ -79,9 +83,19 @@ class BackendTupfile(object):
|
|||
self.variables = {}
|
||||
self.static_lib = None
|
||||
self.shared_lib = None
|
||||
self.program = None
|
||||
self.programs = []
|
||||
self.host_programs = []
|
||||
self.host_library = None
|
||||
self.exports = set()
|
||||
|
||||
# These files are special, ignore anything that generates them or
|
||||
# depends on them.
|
||||
self._skip_files = [
|
||||
'signmar',
|
||||
'libxul.so',
|
||||
'libtestcrasher.so',
|
||||
]
|
||||
|
||||
self.fh = FileAvoidWrite(self.name, capture_diff=True, dry_run=dry_run)
|
||||
self.fh.write('# THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT.\n')
|
||||
self.fh.write('\n')
|
||||
|
@ -98,6 +112,11 @@ class BackendTupfile(object):
|
|||
extra_inputs=None, extra_outputs=None, check_unchanged=False):
|
||||
inputs = inputs or []
|
||||
outputs = outputs or []
|
||||
|
||||
for f in inputs + outputs:
|
||||
if any(f.endswith(skip_file) for skip_file in self._skip_files):
|
||||
return
|
||||
|
||||
display = display or ""
|
||||
self.include_rules()
|
||||
flags = ""
|
||||
|
@ -206,11 +225,17 @@ class TupBackend(CommonBackend):
|
|||
self._backend_files = {}
|
||||
self._cmd = MozbuildObject.from_environment()
|
||||
self._manifest_entries = OrderedDefaultDict(set)
|
||||
self._compile_env_gen_files = (
|
||||
|
||||
# These are a hack to approximate things that are needed for the
|
||||
# compile phase.
|
||||
self._compile_env_files = (
|
||||
'*.api',
|
||||
'*.c',
|
||||
'*.cfg',
|
||||
'*.cpp',
|
||||
'*.h',
|
||||
'*.inc',
|
||||
'*.msg',
|
||||
'*.py',
|
||||
'*.rs',
|
||||
)
|
||||
|
@ -283,9 +308,6 @@ class TupBackend(CommonBackend):
|
|||
|
||||
def _gen_shared_library(self, backend_file):
|
||||
shlib = backend_file.shared_lib
|
||||
if shlib.name == 'libxul.so':
|
||||
# This will fail to link currently due to missing rust symbols.
|
||||
return
|
||||
|
||||
if shlib.cxx_link:
|
||||
mkshlib = (
|
||||
|
@ -313,9 +335,6 @@ class TupBackend(CommonBackend):
|
|||
list_file = self._make_list_file(backend_file.objdir, objs, list_file_name)
|
||||
|
||||
inputs = objs + static_libs + shared_libs
|
||||
if any(i.endswith('libxul.so') for i in inputs):
|
||||
# Don't attempt to link anything that depends on libxul.
|
||||
return
|
||||
|
||||
symbols_file = []
|
||||
if shlib.symbols_file:
|
||||
|
@ -345,23 +364,27 @@ class TupBackend(CommonBackend):
|
|||
shlib.install_target,
|
||||
shlib.lib_name))
|
||||
|
||||
def _gen_programs(self, backend_file):
|
||||
for p in backend_file.programs:
|
||||
self._gen_program(backend_file, p)
|
||||
|
||||
def _gen_program(self, backend_file):
|
||||
cc_or_cxx = 'CXX' if backend_file.program.cxx_link else 'CC'
|
||||
objs, _, shared_libs, os_libs, static_libs = self._expand_libs(backend_file.program)
|
||||
def _gen_program(self, backend_file, prog):
|
||||
cc_or_cxx = 'CXX' if prog.cxx_link else 'CC'
|
||||
objs, _, shared_libs, os_libs, static_libs = self._expand_libs(prog)
|
||||
static_libs = self._lib_paths(backend_file.objdir, static_libs)
|
||||
shared_libs = self._lib_paths(backend_file.objdir, shared_libs)
|
||||
|
||||
inputs = objs + static_libs + shared_libs
|
||||
if any(i.endswith('libxul.so') for i in inputs):
|
||||
# Don't attempt to link anything that depends on libxul.
|
||||
return
|
||||
|
||||
list_file_name = '%s.list' % backend_file.program.name.replace('.', '_')
|
||||
list_file_name = '%s.list' % prog.name.replace('.', '_')
|
||||
list_file = self._make_list_file(backend_file.objdir, objs, list_file_name)
|
||||
|
||||
outputs = [mozpath.relpath(backend_file.program.output_path.full_path,
|
||||
backend_file.objdir)]
|
||||
if isinstance(prog, SimpleProgram):
|
||||
outputs = [prog.name]
|
||||
else:
|
||||
outputs = [mozpath.relpath(prog.output_path.full_path,
|
||||
backend_file.objdir)]
|
||||
|
||||
cmd = (
|
||||
[backend_file.environment.substs[cc_or_cxx], '-o', '%o'] +
|
||||
backend_file.local_flags['CXX_LDFLAGS'] +
|
||||
|
@ -381,6 +404,57 @@ class TupBackend(CommonBackend):
|
|||
)
|
||||
|
||||
|
||||
def _gen_host_library(self, backend_file):
|
||||
objs = backend_file.host_library.objs
|
||||
inputs = objs
|
||||
outputs = [backend_file.host_library.name]
|
||||
cmd = (
|
||||
[backend_file.environment.substs['HOST_AR']] +
|
||||
[backend_file.environment.substs['HOST_AR_FLAGS'].replace('$@', '%o')] +
|
||||
objs
|
||||
)
|
||||
backend_file.rule(
|
||||
cmd=cmd,
|
||||
inputs=inputs,
|
||||
outputs=outputs,
|
||||
display='AR %o'
|
||||
)
|
||||
|
||||
|
||||
def _gen_host_programs(self, backend_file):
|
||||
for p in backend_file.host_programs:
|
||||
self._gen_host_program(backend_file, p)
|
||||
|
||||
|
||||
def _gen_host_program(self, backend_file, prog):
|
||||
_, _, _, extra_libs, _ = self._expand_libs(prog)
|
||||
objs = prog.objs
|
||||
outputs = [prog.program]
|
||||
host_libs = []
|
||||
for lib in prog.linked_libraries:
|
||||
if isinstance(lib, HostLibrary):
|
||||
host_libs.append(lib)
|
||||
host_libs = self._lib_paths(backend_file.objdir, host_libs)
|
||||
|
||||
inputs = objs + host_libs
|
||||
use_cxx = any(f.endswith(('.cc', '.cpp')) for f in prog.source_files())
|
||||
cc_or_cxx = 'HOST_CXX' if use_cxx else 'HOST_CC'
|
||||
cmd = (
|
||||
[backend_file.environment.substs[cc_or_cxx], '-o', '%o'] +
|
||||
backend_file.local_flags['HOST_CXX_LDFLAGS'] +
|
||||
backend_file.local_flags['HOST_LDFLAGS'] +
|
||||
objs +
|
||||
host_libs +
|
||||
extra_libs
|
||||
)
|
||||
backend_file.rule(
|
||||
cmd=cmd,
|
||||
inputs=inputs,
|
||||
outputs=outputs,
|
||||
display='LINK %o'
|
||||
)
|
||||
|
||||
|
||||
def _gen_static_library(self, backend_file):
|
||||
ar = [
|
||||
backend_file.environment.substs['AR'],
|
||||
|
@ -422,7 +496,7 @@ class TupBackend(CommonBackend):
|
|||
skip_files = []
|
||||
|
||||
if self.environment.is_artifact_build:
|
||||
skip_files = self._compile_env_gen_files
|
||||
skip_files = self._compile_env_gen
|
||||
|
||||
for f in obj.outputs:
|
||||
if any(mozpath.match(f, p) for p in skip_files):
|
||||
|
@ -464,10 +538,12 @@ class TupBackend(CommonBackend):
|
|||
backend_file.static_lib = obj
|
||||
elif isinstance(obj, SharedLibrary):
|
||||
backend_file.shared_lib = obj
|
||||
elif isinstance(obj, HostProgram):
|
||||
pass
|
||||
elif isinstance(obj, Program):
|
||||
backend_file.program = obj
|
||||
elif isinstance(obj, (HostProgram, HostSimpleProgram)):
|
||||
backend_file.host_programs.append(obj)
|
||||
elif isinstance(obj, HostLibrary):
|
||||
backend_file.host_library = obj
|
||||
elif isinstance(obj, (Program, SimpleProgram)):
|
||||
backend_file.programs.append(obj)
|
||||
elif isinstance(obj, DirectoryTraversal):
|
||||
pass
|
||||
|
||||
|
@ -490,11 +566,13 @@ class TupBackend(CommonBackend):
|
|||
|
||||
for objdir, backend_file in sorted(self._backend_files.items()):
|
||||
backend_file.gen_sources_rules([self._installed_files])
|
||||
for condition, gen_method in ((backend_file.shared_lib, self._gen_shared_library),
|
||||
(backend_file.static_lib and backend_file.static_lib.no_expand_lib,
|
||||
self._gen_static_library),
|
||||
(backend_file.program, self._gen_program)):
|
||||
if condition:
|
||||
for var, gen_method in ((backend_file.shared_lib, self._gen_shared_library),
|
||||
(backend_file.static_lib and backend_file.static_lib.no_expand_lib,
|
||||
self._gen_static_library),
|
||||
(backend_file.programs, self._gen_programs),
|
||||
(backend_file.host_programs, self._gen_host_programs),
|
||||
(backend_file.host_library, self._gen_host_library)):
|
||||
if var:
|
||||
backend_file.export_shell()
|
||||
gen_method(backend_file)
|
||||
for obj in backend_file.delayed_generated_files:
|
||||
|
@ -539,7 +617,6 @@ class TupBackend(CommonBackend):
|
|||
# TODO: These are directories that don't work in the tup backend
|
||||
# yet, because things they depend on aren't built yet.
|
||||
skip_directories = (
|
||||
'layout/style/test', # HostSimplePrograms
|
||||
'toolkit/library', # libxul.so
|
||||
)
|
||||
if obj.script and obj.method and obj.relobjdir not in skip_directories:
|
||||
|
@ -569,7 +646,8 @@ class TupBackend(CommonBackend):
|
|||
if exports:
|
||||
backend_file.export(exports)
|
||||
|
||||
if any(f in obj.outputs for f in ('source-repo.h', 'buildid.h')):
|
||||
if any(f.endswith(('automation.py', 'source-repo.h', 'buildid.h'))
|
||||
for f in obj.outputs):
|
||||
extra_outputs = [self._early_generated_files]
|
||||
else:
|
||||
extra_outputs = [self._installed_files] if obj.required_for_compile else []
|
||||
|
@ -610,14 +688,14 @@ class TupBackend(CommonBackend):
|
|||
if not path:
|
||||
raise Exception("Cannot install to " + target)
|
||||
|
||||
if target.startswith('_tests'):
|
||||
# TODO: TEST_HARNESS_FILES present a few challenges for the tup
|
||||
# backend (bug 1372381).
|
||||
return
|
||||
|
||||
for path, files in obj.files.walk():
|
||||
self._add_features(target, path)
|
||||
for f in files:
|
||||
output_group = None
|
||||
if any(mozpath.match(mozpath.basename(f), p)
|
||||
for p in self._compile_env_files):
|
||||
output_group = self._installed_files
|
||||
|
||||
if not isinstance(f, ObjDirPath):
|
||||
backend_file = self._get_backend_file(mozpath.join(target, path))
|
||||
if '*' in f:
|
||||
|
@ -637,13 +715,27 @@ class TupBackend(CommonBackend):
|
|||
yield p + '/'
|
||||
prefix = ''.join(_prefix(f.full_path))
|
||||
self.backend_input_files.add(prefix)
|
||||
|
||||
output_dir = ''
|
||||
# If we have a RenamedSourcePath here, the common backend
|
||||
# has generated this object from a jar manifest, and we
|
||||
# can rely on 'path' to be our destination path relative
|
||||
# to any wildcard match. Otherwise, the output file may
|
||||
# contribute to our destination directory.
|
||||
if not isinstance(f, RenamedSourcePath):
|
||||
output_dir = ''.join(_prefix(mozpath.dirname(f)))
|
||||
|
||||
finder = FileFinder(prefix)
|
||||
for p, _ in finder.find(f.full_path[len(prefix):]):
|
||||
install_dir = prefix[len(obj.srcdir) + 1:]
|
||||
output = p
|
||||
if f.target_basename and '*' not in f.target_basename:
|
||||
output = mozpath.join(f.target_basename, output)
|
||||
backend_file.symlink_rule(mozpath.join(prefix, p),
|
||||
output=mozpath.join(f.target_basename, p),
|
||||
output_group=self._installed_files)
|
||||
output=mozpath.join(output_dir, output),
|
||||
output_group=output_group)
|
||||
else:
|
||||
backend_file.symlink_rule(f.full_path, output=f.target_basename, output_group=self._installed_files)
|
||||
backend_file.symlink_rule(f.full_path, output=f.target_basename, output_group=output_group)
|
||||
else:
|
||||
if (self.environment.is_artifact_build and
|
||||
any(mozpath.match(f.target_basename, p) for p in self._compile_env_gen_files)):
|
||||
|
@ -653,18 +745,16 @@ class TupBackend(CommonBackend):
|
|||
|
||||
# We're not generating files in these directories yet, so
|
||||
# don't attempt to install files generated from them.
|
||||
if f.context.relobjdir not in ('layout/style/test',
|
||||
'toolkit/library',
|
||||
if f.context.relobjdir not in ('toolkit/library',
|
||||
'js/src/shell'):
|
||||
output = mozpath.join('$(MOZ_OBJ_ROOT)', target, path,
|
||||
f.target_basename)
|
||||
gen_backend_file = self._get_backend_file(f.context.relobjdir)
|
||||
if gen_backend_file.requires_delay([f]):
|
||||
output_group = self._installed_files if f.target_basename.endswith('.h') else None
|
||||
gen_backend_file.delayed_installed_files.append((f.full_path, output, output_group))
|
||||
else:
|
||||
gen_backend_file.symlink_rule(f.full_path, output=output,
|
||||
output_group=self._installed_files)
|
||||
output_group=output_group)
|
||||
|
||||
|
||||
def _process_final_target_pp_files(self, obj, backend_file):
|
||||
|
|
|
@ -532,6 +532,14 @@ class HostSimpleProgram(HostMixin, BaseProgram):
|
|||
SUFFIX_VAR = 'HOST_BIN_SUFFIX'
|
||||
KIND = 'host'
|
||||
|
||||
def source_files(self):
|
||||
for srcs in self.sources.values():
|
||||
for f in srcs:
|
||||
if ('host_%s' % mozpath.basename(mozpath.splitext(f)[0]) ==
|
||||
mozpath.splitext(self.program)[0]):
|
||||
return [f]
|
||||
return []
|
||||
|
||||
|
||||
def cargo_output_directory(context, target_var):
|
||||
# cargo creates several directories and places its build artifacts
|
||||
|
|
|
@ -14,6 +14,7 @@ import os
|
|||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
|
@ -823,6 +824,8 @@ class RunProgram(MachCommandBase):
|
|||
help='Run the program with the crash reporter enabled.')
|
||||
@CommandArgument('--setpref', action='append', default=[], group=prog_group,
|
||||
help='Set the specified pref before starting the program. Can be set multiple times. Prefs can also be set in ~/.mozbuild/machrc in the [runprefs] section - see `./mach settings` for more information.')
|
||||
@CommandArgument('--temp-profile', action='store_true', group=prog_group,
|
||||
help='Run the program using a new temporary profile created inside the objdir.')
|
||||
|
||||
@CommandArgumentGroup('debugging')
|
||||
@CommandArgument('--debug', action='store_true', group='debugging',
|
||||
|
@ -846,7 +849,7 @@ class RunProgram(MachCommandBase):
|
|||
@CommandArgument('--show-dump-stats', action='store_true', group='DMD',
|
||||
help='Show stats when doing dumps.')
|
||||
def run(self, params, remote, background, noprofile, disable_e10s,
|
||||
enable_crash_reporter, setpref, debug, debugger,
|
||||
enable_crash_reporter, setpref, temp_profile, debug, debugger,
|
||||
debugger_args, dmd, mode, stacks, show_dump_stats):
|
||||
|
||||
if conditions.is_android(self):
|
||||
|
@ -895,7 +898,11 @@ class RunProgram(MachCommandBase):
|
|||
for pref in prefs:
|
||||
prefs[pref] = Preferences.cast(prefs[pref])
|
||||
|
||||
path = os.path.join(self.topobjdir, 'tmp', 'scratch_user')
|
||||
if (temp_profile):
|
||||
path = tempfile.mkdtemp(dir=os.path.join(self.topobjdir, 'tmp'), prefix='profile-')
|
||||
else:
|
||||
path = os.path.join(self.topobjdir, 'tmp', 'profile-default')
|
||||
|
||||
profile = Profile(path, preferences=prefs)
|
||||
args.append('-profile')
|
||||
args.append(profile.profile)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
[DEFAULT]
|
||||
head = head_psm.js
|
||||
tags = psm
|
||||
firefox-appdir = browser
|
||||
support-files =
|
||||
bad_certs/**
|
||||
ocsp_certs/**
|
||||
|
|
|
@ -287,14 +287,31 @@ LogManager.prototype = {
|
|||
/**
|
||||
* Finds all logs older than maxErrorAge and deletes them using async I/O.
|
||||
*/
|
||||
async cleanupLogs() {
|
||||
cleanupLogs() {
|
||||
let maxAge = this._prefs.get("log.appender.file.maxErrorAge", DEFAULT_MAX_ERROR_AGE);
|
||||
let threshold = Date.now() - 1000 * maxAge;
|
||||
this._log.debug("Log cleanup threshold time: " + threshold);
|
||||
|
||||
let shouldDelete = fileInfo => {
|
||||
return fileInfo.lastModificationDate.getTime() < threshold;
|
||||
};
|
||||
return this._deleteLogFiles(shouldDelete);
|
||||
},
|
||||
|
||||
/**
|
||||
* Finds all logs and removes them.
|
||||
*/
|
||||
removeAllLogs() {
|
||||
return this._deleteLogFiles(() => true);
|
||||
},
|
||||
|
||||
// Delete some log files. A callback is invoked for each found log file to
|
||||
// determine if that file should be removed.
|
||||
async _deleteLogFiles(cbShouldDelete) {
|
||||
this._cleaningUpFileLogs = true;
|
||||
let logDir = FileUtils.getDir("ProfD", this._logFileSubDirectoryEntries);
|
||||
let iterator = new OS.File.DirectoryIterator(logDir.path);
|
||||
let maxAge = this._prefs.get("log.appender.file.maxErrorAge", DEFAULT_MAX_ERROR_AGE);
|
||||
let threshold = Date.now() - 1000 * maxAge;
|
||||
|
||||
this._log.debug("Log cleanup threshold time: " + threshold);
|
||||
await iterator.forEach(async (entry) => {
|
||||
// Note that we don't check this.logFilePrefix is in the name - we cleanup
|
||||
// all files in this directory regardless of that prefix so old logfiles
|
||||
|
@ -306,7 +323,7 @@ LogManager.prototype = {
|
|||
try {
|
||||
// need to call .stat() as the enumerator doesn't give that to us on *nix.
|
||||
let info = await OS.File.stat(entry.path);
|
||||
if (info.lastModificationDate.getTime() >= threshold) {
|
||||
if (!cbShouldDelete(info)) {
|
||||
return;
|
||||
}
|
||||
this._log.trace(" > Cleanup removing " + entry.name +
|
||||
|
|
|
@ -223,3 +223,37 @@ add_task(async function test_logFileError() {
|
|||
|
||||
lm.finalize();
|
||||
});
|
||||
|
||||
function countLogFiles() {
|
||||
let logsdir = FileUtils.getDir("ProfD", ["weave", "logs"], true);
|
||||
let count = 0;
|
||||
let entries = logsdir.directoryEntries;
|
||||
while (entries.hasMoreElements()) {
|
||||
count += 1;
|
||||
entries.getNext();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Test that removeAllLogs removes all log files.
|
||||
add_task(async function test_logFileError() {
|
||||
Services.prefs.setBoolPref("log-manager.test.log.appender.file.logOnError", true);
|
||||
Services.prefs.setBoolPref("log-manager.test.log.appender.file.logOnSuccess", true);
|
||||
|
||||
let lm = new LogManager("log-manager.test.", ["TestLog2"], "test");
|
||||
|
||||
let log = Log.repository.getLogger("TestLog2");
|
||||
log.info("an info message");
|
||||
let reason = await lm.resetFileLog();
|
||||
Assert.equal(reason, lm.SUCCESS_LOG_WRITTEN, "success log was written.");
|
||||
|
||||
log.error("an error message");
|
||||
reason = await lm.resetFileLog();
|
||||
Assert.equal(reason, lm.ERROR_LOG_WRITTEN);
|
||||
|
||||
Assert.equal(countLogFiles(), 2, "expect 2 log files");
|
||||
await lm.removeAllLogs();
|
||||
Assert.equal(countLogFiles(), 0, "should be no log files after removing them");
|
||||
|
||||
lm.finalize();
|
||||
});
|
||||
|
|
|
@ -775,7 +775,20 @@ ErrorHandler.prototype = {
|
|||
break;
|
||||
case "weave:service:start-over:finish":
|
||||
// ensure we capture any logs between the last sync and the reset completing.
|
||||
this.resetFileLog();
|
||||
this.resetFileLog().then(() => {
|
||||
// although for privacy reasons we also delete all logs (but we allow
|
||||
// a preference to avoid this to help with debugging.)
|
||||
if (!Svc.Prefs.get("log.keepLogsOnReset", false)) {
|
||||
return this._logManager.removeAllLogs().then(() => {
|
||||
Svc.Obs.notify("weave:service:remove-file-log");
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}).catch(err => {
|
||||
// So we failed to delete the logs - take the ironic option of
|
||||
// writing this error to the logs we failed to delete!
|
||||
this._log.error("Failed to delete logs on reset", err);
|
||||
});
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -401,3 +401,28 @@ add_test(function test_logErrorCleanup_age() {
|
|||
Svc.Obs.notify("weave:service:sync:error");
|
||||
}, delay, this, "cleanup-timer");
|
||||
});
|
||||
|
||||
add_task(async function test_remove_log_on_startOver() {
|
||||
Svc.Prefs.set("log.appender.file.logOnError", true);
|
||||
|
||||
let log = Log.repository.getLogger("Sync.Test.FileLog");
|
||||
const MESSAGE = "this WILL show up";
|
||||
log.info(MESSAGE);
|
||||
|
||||
let promiseLogWritten = promiseOneObserver("weave:service:reset-file-log");
|
||||
// Fake an unsuccessful sync.
|
||||
Svc.Obs.notify("weave:service:sync:error");
|
||||
|
||||
await promiseLogWritten;
|
||||
// Should have at least 1 log file.
|
||||
let entries = logsdir.directoryEntries;
|
||||
Assert.ok(entries.hasMoreElements());
|
||||
|
||||
// Fake a reset.
|
||||
let promiseRemoved = promiseOneObserver("weave:service:remove-file-log");
|
||||
Svc.Obs.notify("weave:service:start-over:finish");
|
||||
await promiseRemoved;
|
||||
|
||||
// should be no files left.
|
||||
Assert.ok(!logsdir.directoryEntries.hasMoreElements());
|
||||
});
|
||||
|
|
|
@ -71,7 +71,6 @@ const SYNC_WIPE_REMOTE = "wipeRemote";
|
|||
const ACTION_ADD = "add";
|
||||
const ACTION_DELETE = "delete";
|
||||
const ACTION_MODIFY = "modify";
|
||||
const ACTION_PRIVATE_BROWSING = "private-browsing";
|
||||
const ACTION_SET_ENABLED = "set-enabled";
|
||||
const ACTION_SYNC = "sync";
|
||||
const ACTION_SYNC_RESET_CLIENT = SYNC_RESET_CLIENT;
|
||||
|
@ -84,7 +83,6 @@ const ACTIONS = [
|
|||
ACTION_ADD,
|
||||
ACTION_DELETE,
|
||||
ACTION_MODIFY,
|
||||
ACTION_PRIVATE_BROWSING,
|
||||
ACTION_SET_ENABLED,
|
||||
ACTION_SYNC,
|
||||
ACTION_SYNC_RESET_CLIENT,
|
||||
|
@ -96,7 +94,6 @@ const ACTIONS = [
|
|||
|
||||
const OBSERVER_TOPICS = ["fxaccounts:onlogin",
|
||||
"fxaccounts:onlogout",
|
||||
"private-browsing",
|
||||
"profile-before-change",
|
||||
"sessionstore-windows-restored",
|
||||
"weave:service:tracking-started",
|
||||
|
@ -171,10 +168,6 @@ var TPS = {
|
|||
Logger.logInfo("----------event observed: " + topic);
|
||||
|
||||
switch (topic) {
|
||||
case "private-browsing":
|
||||
Logger.logInfo("private browsing " + data);
|
||||
break;
|
||||
|
||||
case "profile-before-change":
|
||||
OBSERVER_TOPICS.forEach(function(topic) {
|
||||
Services.obs.removeObserver(this, topic);
|
||||
|
|
|
@ -7,10 +7,6 @@
|
|||
_DEST_DIR = $(DEPTH)/_tests/$(relativesrcdir)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
# We're installing to _tests/testing/mochitest, so this is the depth
|
||||
# necessary for relative objdir paths.
|
||||
TARGET_DEPTH = ../../..
|
||||
include $(topsrcdir)/build/automation-build.mk
|
||||
|
||||
libs::
|
||||
(cd $(DIST)/xpi-stage && tar $(TAR_CREATE_FLAGS) - mochijar) | (cd $(_DEST_DIR) && tar -xf -)
|
||||
|
|
|
@ -26,12 +26,7 @@ MOCHITEST_MANIFESTS += [
|
|||
]
|
||||
MOCHITEST_CHROME_MANIFESTS += ['chrome/chrome.ini']
|
||||
|
||||
GENERATED_FILES += [
|
||||
'automation.py',
|
||||
]
|
||||
|
||||
TEST_HARNESS_FILES.testing.mochitest += [
|
||||
'!automation.py',
|
||||
'/build/mobile/remoteautomation.py',
|
||||
'/build/pgo/server-locations.txt',
|
||||
'/build/sanitizers/lsan_suppressions.txt',
|
||||
|
|
|
@ -1,17 +1,11 @@
|
|||
[constant-source-basic.html]
|
||||
[X Factory method: node.channelCount is not equal to 2. Got 1.]
|
||||
expected: FAIL
|
||||
|
||||
[X Factory method: node.offset.minValue is not equal to -3.4028234663852886e+38. Got -Infinity.]
|
||||
expected: FAIL
|
||||
|
||||
[X Factory method: node.offset.maxValue is not equal to 3.4028234663852886e+38. Got Infinity.]
|
||||
expected: FAIL
|
||||
|
||||
[< [createConstantSource()\] 3 out of 11 assertions were failed.]
|
||||
expected: FAIL
|
||||
|
||||
[X Constructor: node.channelCount is not equal to 2. Got 1.]
|
||||
[< [createConstantSource()\] 2 out of 11 assertions were failed.]
|
||||
expected: FAIL
|
||||
|
||||
[X Constructor: node.offset.minValue is not equal to -3.4028234663852886e+38. Got -Infinity.]
|
||||
|
@ -20,7 +14,7 @@
|
|||
[X Constructor: node.offset.maxValue is not equal to 3.4028234663852886e+38. Got Infinity.]
|
||||
expected: FAIL
|
||||
|
||||
[< [new ConstantSourceNode()\] 3 out of 11 assertions were failed.]
|
||||
[< [new ConstantSourceNode()\] 2 out of 11 assertions were failed.]
|
||||
expected: FAIL
|
||||
|
||||
[X start(-1) threw "NotSupportedError" instead of RangeError.]
|
||||
|
|
|
@ -137,7 +137,7 @@ var test = {
|
|||
var toolbar =
|
||||
PlacesUtils.getFolderContents(PlacesUtils.toolbarFolderId,
|
||||
false, true).root;
|
||||
Assert.ok(toolbar.childCount, 1);
|
||||
Assert.equal(toolbar.childCount, 1);
|
||||
|
||||
var folderNode = toolbar.getChild(0);
|
||||
Assert.equal(folderNode.type, folderNode.RESULT_TYPE_FOLDER);
|
||||
|
|
|
@ -43,7 +43,7 @@ var quotesTest = {
|
|||
toolbar.containerOpen = true;
|
||||
|
||||
// test for our quoted folder
|
||||
Assert.ok(toolbar.childCount, 1);
|
||||
Assert.equal(toolbar.childCount, 1);
|
||||
var folderNode = toolbar.getChild(0);
|
||||
Assert.equal(folderNode.type, folderNode.RESULT_TYPE_FOLDER);
|
||||
Assert.equal(folderNode.title, this._folderTitle);
|
||||
|
|
|
@ -604,12 +604,8 @@ function waitForConnectionClosed(aCallback) {
|
|||
* @param [optional] aStack
|
||||
* The stack frame used to report the error.
|
||||
*/
|
||||
function do_check_valid_places_guid(aGuid,
|
||||
aStack) {
|
||||
if (!aStack) {
|
||||
aStack = Components.stack.caller;
|
||||
}
|
||||
Assert.ok(/^[a-zA-Z0-9\-_]{12}$/.test(aGuid), aStack);
|
||||
function do_check_valid_places_guid(aGuid) {
|
||||
Assert.ok(/^[a-zA-Z0-9\-_]{12}$/.test(aGuid), "Should be a valid GUID");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -621,21 +617,17 @@ function do_check_valid_places_guid(aGuid,
|
|||
* The stack frame used to report the error.
|
||||
* @return the associated the guid.
|
||||
*/
|
||||
function do_get_guid_for_uri(aURI,
|
||||
aStack) {
|
||||
if (!aStack) {
|
||||
aStack = Components.stack.caller;
|
||||
}
|
||||
function do_get_guid_for_uri(aURI) {
|
||||
let stmt = DBConn().createStatement(
|
||||
`SELECT guid
|
||||
FROM moz_places
|
||||
WHERE url_hash = hash(:url) AND url = :url`
|
||||
);
|
||||
stmt.params.url = aURI.spec;
|
||||
Assert.ok(stmt.executeStep(), aStack);
|
||||
Assert.ok(stmt.executeStep(), "GUID for URI statement should succeed");
|
||||
let guid = stmt.row.guid;
|
||||
stmt.finalize();
|
||||
do_check_valid_places_guid(guid, aStack);
|
||||
do_check_valid_places_guid(guid);
|
||||
return guid;
|
||||
}
|
||||
|
||||
|
@ -649,11 +641,10 @@ function do_get_guid_for_uri(aURI,
|
|||
*/
|
||||
function do_check_guid_for_uri(aURI,
|
||||
aGUID) {
|
||||
let caller = Components.stack.caller;
|
||||
let guid = do_get_guid_for_uri(aURI, caller);
|
||||
let guid = do_get_guid_for_uri(aURI);
|
||||
if (aGUID) {
|
||||
do_check_valid_places_guid(aGUID, caller);
|
||||
Assert.equal(guid, aGUID, caller);
|
||||
do_check_valid_places_guid(aGUID);
|
||||
Assert.equal(guid, aGUID, "Should have a guid in moz_places for the URI");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -666,21 +657,17 @@ function do_check_guid_for_uri(aURI,
|
|||
* The stack frame used to report the error.
|
||||
* @return the associated the guid.
|
||||
*/
|
||||
function do_get_guid_for_bookmark(aId,
|
||||
aStack) {
|
||||
if (!aStack) {
|
||||
aStack = Components.stack.caller;
|
||||
}
|
||||
function do_get_guid_for_bookmark(aId) {
|
||||
let stmt = DBConn().createStatement(
|
||||
`SELECT guid
|
||||
FROM moz_bookmarks
|
||||
WHERE id = :item_id`
|
||||
);
|
||||
stmt.params.item_id = aId;
|
||||
Assert.ok(stmt.executeStep(), aStack);
|
||||
Assert.ok(stmt.executeStep(), "Should succeed executing the SQL statement");
|
||||
let guid = stmt.row.guid;
|
||||
stmt.finalize();
|
||||
do_check_valid_places_guid(guid, aStack);
|
||||
do_check_valid_places_guid(guid);
|
||||
return guid;
|
||||
}
|
||||
|
||||
|
@ -694,11 +681,10 @@ function do_get_guid_for_bookmark(aId,
|
|||
*/
|
||||
function do_check_guid_for_bookmark(aId,
|
||||
aGUID) {
|
||||
let caller = Components.stack.caller;
|
||||
let guid = do_get_guid_for_bookmark(aId, caller);
|
||||
let guid = do_get_guid_for_bookmark(aId);
|
||||
if (aGUID) {
|
||||
do_check_valid_places_guid(aGUID, caller);
|
||||
Assert.equal(guid, aGUID, caller);
|
||||
do_check_valid_places_guid(aGUID);
|
||||
Assert.equal(guid, aGUID, "Should have the correct GUID for the bookmark");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -129,15 +129,14 @@ VisitObserver.prototype = {
|
|||
*/
|
||||
function do_check_title_for_uri(aURI,
|
||||
aTitle) {
|
||||
let stack = Components.stack.caller;
|
||||
let stmt = DBConn().createStatement(
|
||||
`SELECT title
|
||||
FROM moz_places
|
||||
WHERE url_hash = hash(:url) AND url = :url`
|
||||
);
|
||||
stmt.params.url = aURI.spec;
|
||||
Assert.ok(stmt.executeStep(), stack);
|
||||
Assert.equal(stmt.row.title, aTitle, stack);
|
||||
Assert.ok(stmt.executeStep());
|
||||
Assert.equal(stmt.row.title, aTitle);
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
|
|
|
@ -244,7 +244,7 @@ function ensureAnnotationsSet(aGuid, aAnnos) {
|
|||
}
|
||||
|
||||
function ensureItemsMoved(...items) {
|
||||
Assert.ok(observer.itemsMoved.size, items.length);
|
||||
Assert.equal(observer.itemsMoved.size, items.length);
|
||||
for (let item of items) {
|
||||
Assert.ok(observer.itemsMoved.has(item.guid));
|
||||
let info = observer.itemsMoved.get(item.guid);
|
||||
|
|
|
@ -450,14 +450,16 @@ security:
|
|||
pkcs11_modules_loaded:
|
||||
bug_numbers:
|
||||
- 1369911
|
||||
- 1445961
|
||||
description: >
|
||||
A keyed boolean indicating the library names of the PKCS#11 modules that
|
||||
have been loaded by the browser.
|
||||
expires: "62"
|
||||
expires: "69"
|
||||
kind: boolean
|
||||
keyed: true
|
||||
notification_emails:
|
||||
- seceng-telemetry@mozilla.com
|
||||
- dkeeler@mozilla.com
|
||||
release_channel_collection: opt-out
|
||||
record_in_processes:
|
||||
- main
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:xbl="http://www.mozilla.org/xbl">
|
||||
|
||||
<!-- This binding is needed on <scrollbar> to prevent content process
|
||||
startup performance regression; to be removed in bug 1458375 -->
|
||||
<binding id="empty"/>
|
||||
|
||||
<binding id="basecontrol">
|
||||
<implementation implements="nsIDOMXULControlElement">
|
||||
<!-- public implementation -->
|
||||
|
|
|
@ -5,68 +5,68 @@ ChromeUtils.import("resource://gre/modules/BinarySearch.jsm");
|
|||
|
||||
function run_test() {
|
||||
// empty array
|
||||
ok([], 1, false, 0);
|
||||
check([], 1, false, 0);
|
||||
|
||||
// one-element array
|
||||
ok([2], 2, true, 0);
|
||||
ok([2], 1, false, 0);
|
||||
ok([2], 3, false, 1);
|
||||
check([2], 2, true, 0);
|
||||
check([2], 1, false, 0);
|
||||
check([2], 3, false, 1);
|
||||
|
||||
// two-element array
|
||||
ok([2, 4], 2, true, 0);
|
||||
ok([2, 4], 4, true, 1);
|
||||
ok([2, 4], 1, false, 0);
|
||||
ok([2, 4], 3, false, 1);
|
||||
ok([2, 4], 5, false, 2);
|
||||
check([2, 4], 2, true, 0);
|
||||
check([2, 4], 4, true, 1);
|
||||
check([2, 4], 1, false, 0);
|
||||
check([2, 4], 3, false, 1);
|
||||
check([2, 4], 5, false, 2);
|
||||
|
||||
// three-element array
|
||||
ok([2, 4, 6], 2, true, 0);
|
||||
ok([2, 4, 6], 4, true, 1);
|
||||
ok([2, 4, 6], 6, true, 2);
|
||||
ok([2, 4, 6], 1, false, 0);
|
||||
ok([2, 4, 6], 3, false, 1);
|
||||
ok([2, 4, 6], 5, false, 2);
|
||||
ok([2, 4, 6], 7, false, 3);
|
||||
check([2, 4, 6], 2, true, 0);
|
||||
check([2, 4, 6], 4, true, 1);
|
||||
check([2, 4, 6], 6, true, 2);
|
||||
check([2, 4, 6], 1, false, 0);
|
||||
check([2, 4, 6], 3, false, 1);
|
||||
check([2, 4, 6], 5, false, 2);
|
||||
check([2, 4, 6], 7, false, 3);
|
||||
|
||||
// duplicates
|
||||
ok([2, 2], 2, true, 0);
|
||||
ok([2, 2], 1, false, 0);
|
||||
ok([2, 2], 3, false, 2);
|
||||
check([2, 2], 2, true, 0);
|
||||
check([2, 2], 1, false, 0);
|
||||
check([2, 2], 3, false, 2);
|
||||
|
||||
// duplicates on the left
|
||||
ok([2, 2, 4], 2, true, 1);
|
||||
ok([2, 2, 4], 4, true, 2);
|
||||
ok([2, 2, 4], 1, false, 0);
|
||||
ok([2, 2, 4], 3, false, 2);
|
||||
ok([2, 2, 4], 5, false, 3);
|
||||
check([2, 2, 4], 2, true, 1);
|
||||
check([2, 2, 4], 4, true, 2);
|
||||
check([2, 2, 4], 1, false, 0);
|
||||
check([2, 2, 4], 3, false, 2);
|
||||
check([2, 2, 4], 5, false, 3);
|
||||
|
||||
// duplicates on the right
|
||||
ok([2, 4, 4], 2, true, 0);
|
||||
ok([2, 4, 4], 4, true, 1);
|
||||
ok([2, 4, 4], 1, false, 0);
|
||||
ok([2, 4, 4], 3, false, 1);
|
||||
ok([2, 4, 4], 5, false, 3);
|
||||
check([2, 4, 4], 2, true, 0);
|
||||
check([2, 4, 4], 4, true, 1);
|
||||
check([2, 4, 4], 1, false, 0);
|
||||
check([2, 4, 4], 3, false, 1);
|
||||
check([2, 4, 4], 5, false, 3);
|
||||
|
||||
// duplicates in the middle
|
||||
ok([2, 4, 4, 6], 2, true, 0);
|
||||
ok([2, 4, 4, 6], 4, true, 1);
|
||||
ok([2, 4, 4, 6], 6, true, 3);
|
||||
ok([2, 4, 4, 6], 1, false, 0);
|
||||
ok([2, 4, 4, 6], 3, false, 1);
|
||||
ok([2, 4, 4, 6], 5, false, 3);
|
||||
ok([2, 4, 4, 6], 7, false, 4);
|
||||
check([2, 4, 4, 6], 2, true, 0);
|
||||
check([2, 4, 4, 6], 4, true, 1);
|
||||
check([2, 4, 4, 6], 6, true, 3);
|
||||
check([2, 4, 4, 6], 1, false, 0);
|
||||
check([2, 4, 4, 6], 3, false, 1);
|
||||
check([2, 4, 4, 6], 5, false, 3);
|
||||
check([2, 4, 4, 6], 7, false, 4);
|
||||
|
||||
// duplicates all around
|
||||
ok([2, 2, 4, 4, 6, 6], 2, true, 0);
|
||||
ok([2, 2, 4, 4, 6, 6], 4, true, 2);
|
||||
ok([2, 2, 4, 4, 6, 6], 6, true, 4);
|
||||
ok([2, 2, 4, 4, 6, 6], 1, false, 0);
|
||||
ok([2, 2, 4, 4, 6, 6], 3, false, 2);
|
||||
ok([2, 2, 4, 4, 6, 6], 5, false, 4);
|
||||
ok([2, 2, 4, 4, 6, 6], 7, false, 6);
|
||||
check([2, 2, 4, 4, 6, 6], 2, true, 0);
|
||||
check([2, 2, 4, 4, 6, 6], 4, true, 2);
|
||||
check([2, 2, 4, 4, 6, 6], 6, true, 4);
|
||||
check([2, 2, 4, 4, 6, 6], 1, false, 0);
|
||||
check([2, 2, 4, 4, 6, 6], 3, false, 2);
|
||||
check([2, 2, 4, 4, 6, 6], 5, false, 4);
|
||||
check([2, 2, 4, 4, 6, 6], 7, false, 6);
|
||||
}
|
||||
|
||||
function ok(array, target, expectedFound, expectedIdx) {
|
||||
function check(array, target, expectedFound, expectedIdx) {
|
||||
let [found, idx] = BinarySearch.search(cmp, array, target);
|
||||
Assert.equal(found, expectedFound);
|
||||
Assert.equal(idx, expectedIdx);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
scrollbar {
|
||||
-moz-appearance: scrollbar;
|
||||
-moz-binding: url("chrome://global/content/bindings/general.xml#empty");
|
||||
cursor: default;
|
||||
background-color: white;
|
||||
}
|
||||
|
@ -81,6 +82,7 @@ scrollcorner {
|
|||
@media print {
|
||||
html|div scrollbar {
|
||||
-moz-appearance: scrollbar;
|
||||
-moz-binding: url("chrome://global/content/bindings/general.xml#empty");
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
scrollbar {
|
||||
-moz-appearance: scrollbar-horizontal;
|
||||
-moz-binding: url("chrome://global/content/bindings/general.xml#empty");
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
|
@ -62,7 +63,7 @@ scrollbarbutton {
|
|||
|
||||
/* ::::: square at the corner of two scrollbars ::::: */
|
||||
|
||||
scrollcorner {
|
||||
scrollcorner {
|
||||
/* XXX -moz-appearance: scrollcorner; */
|
||||
width: 16px;
|
||||
cursor: default;
|
||||
|
|
Загрузка…
Ссылка в новой задаче