1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
- args[_key - 1] = arguments[_key];
- }
-
- var argIndex = 0;
- var message = 'Warning: ' + format.replace(/%s/g, function () {
- return args[argIndex++];
- });
- if (typeof console !== 'undefined') {
- console.warn(message);
- }
- try {
- // --- Welcome to debugging React ---
- // This error was thrown as a convenience so that you can use this stack
- // to find the callsite that caused this warning to fire.
- throw new Error(message);
- } catch (x) {}
- };
-
- lowPriorityWarning = function (condition, format) {
- if (format === undefined) {
- throw new Error('`warning(condition, format, ...args)` requires a warning ' + 'message argument');
- }
- if (!condition) {
- for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
- args[_key2 - 2] = arguments[_key2];
- }
-
- printWarning.apply(undefined, [format].concat(args));
- }
- };
-}
-
-var lowPriorityWarning$1 = lowPriorityWarning;
-
-/**
- * Copyright (c) 2013-present, Facebook, Inc.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- *
- */
-
-function makeEmptyFunction(arg) {
- return function () {
- return arg;
- };
-}
-
-/**
- * This function accepts and discards inputs; it has no side effects. This is
- * primarily useful idiomatically for overridable function endpoints which
- * always need to be callable, since JS lacks a null-call idiom ala Cocoa.
- */
-var emptyFunction = function emptyFunction() {};
-
-emptyFunction.thatReturns = makeEmptyFunction;
-emptyFunction.thatReturnsFalse = makeEmptyFunction(false);
-emptyFunction.thatReturnsTrue = makeEmptyFunction(true);
-emptyFunction.thatReturnsNull = makeEmptyFunction(null);
-emptyFunction.thatReturnsThis = function () {
- return this;
-};
-emptyFunction.thatReturnsArgument = function (arg) {
- return arg;
-};
-
-var emptyFunction_1 = emptyFunction;
-
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- */
-
-
-
-
-
-/**
- * Similar to invariant but only logs a warning if the condition is not met.
- * This can be used to log issues in development environments in critical
- * paths. Removing the logging code for production environments will keep the
- * same logic and follow the same code paths.
- */
-
-var warning = emptyFunction_1;
-
-{
- var printWarning$1 = function printWarning(format) {
- for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
- args[_key - 1] = arguments[_key];
- }
-
- var argIndex = 0;
- var message = 'Warning: ' + format.replace(/%s/g, function () {
- return args[argIndex++];
- });
- if (typeof console !== 'undefined') {
- console.error(message);
- }
- try {
- // --- Welcome to debugging React ---
- // This error was thrown as a convenience so that you can use this stack
- // to find the callsite that caused this warning to fire.
- throw new Error(message);
- } catch (x) {}
- };
-
- warning = function warning(condition, format) {
- if (format === undefined) {
- throw new Error('`warning(condition, format, ...args)` requires a warning ' + 'message argument');
- }
-
- if (format.indexOf('Failed Composite propType: ') === 0) {
- return; // Ignore CompositeComponent proptype check.
- }
-
- if (!condition) {
- for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
- args[_key2 - 2] = arguments[_key2];
- }
-
- printWarning$1.apply(undefined, [format].concat(args));
- }
- };
-}
-
-var warning_1 = warning;
-
-var didWarnStateUpdateForUnmountedComponent = {};
-
-function warnNoop(publicInstance, callerName) {
- {
- var constructor = publicInstance.constructor;
- var componentName = constructor && (constructor.displayName || constructor.name) || 'ReactClass';
- var warningKey = componentName + '.' + callerName;
- if (didWarnStateUpdateForUnmountedComponent[warningKey]) {
- return;
- }
- warning_1(false, '%s(...): Can only update a mounted or mounting component. ' + 'This usually means you called %s() on an unmounted component. ' + 'This is a no-op.\n\nPlease check the code for the %s component.', callerName, callerName, componentName);
- didWarnStateUpdateForUnmountedComponent[warningKey] = true;
+function reactProdInvariant(code) {
+ var argCount = arguments.length - 1;
+ var url = 'https://reactjs.org/docs/error-decoder.html?invariant=' + code;
+ for (var argIdx = 0; argIdx < argCount; argIdx++) {
+ url += '&args[]=' + encodeURIComponent(arguments[argIdx + 1]);
}
+ // Rename it so that our build transform doesn't atttempt
+ // to replace this invariant() call with reactProdInvariant().
+ var i = invariant_1;
+ i(false,
+ // The error code is intentionally part of the message (and
+ // not the format argument) so that we could deduplicate
+ // different errors in logs based on the code.
+ 'Minified React error #' + code + '; visit %s ' + 'for the full message or use the non-minified dev environment ' + 'for full errors and additional helpful warnings. ', url);
}
-/**
- * This is the abstract API for an update queue.
- */
-var ReactNoopUpdateQueue = {
- /**
- * Checks whether or not this composite component is mounted.
- * @param {ReactClass} publicInstance The instance we want to test.
- * @return {boolean} True if mounted, false otherwise.
- * @protected
- * @final
- */
- isMounted: function (publicInstance) {
- return false;
- },
+// The Symbol used to tag the ReactElement-like types. If there is no native Symbol
+// nor polyfill, then a plain number is used for performance.
+var hasSymbol = typeof Symbol === 'function' && Symbol.for;
- /**
- * Forces an update. This should only be invoked when it is known with
- * certainty that we are **not** in a DOM transaction.
- *
- * You may want to call this when you know that some deeper aspect of the
- * component's state has changed but `setState` was not called.
- *
- * This will not invoke `shouldComponentUpdate`, but it will invoke
- * `componentWillUpdate` and `componentDidUpdate`.
- *
- * @param {ReactClass} publicInstance The instance that should rerender.
- * @param {?function} callback Called after component is updated.
- * @param {?string} callerName name of the calling function in the public API.
- * @internal
- */
- enqueueForceUpdate: function (publicInstance, callback, callerName) {
- warnNoop(publicInstance, 'forceUpdate');
- },
+var REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for('react.element') : 0xeac7;
+var REACT_PORTAL_TYPE = hasSymbol ? Symbol.for('react.portal') : 0xeaca;
+var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol.for('react.fragment') : 0xeacb;
+var REACT_STRICT_MODE_TYPE = hasSymbol ? Symbol.for('react.strict_mode') : 0xeacc;
+var REACT_PROFILER_TYPE = hasSymbol ? Symbol.for('react.profiler') : 0xead2;
+var REACT_PROVIDER_TYPE = hasSymbol ? Symbol.for('react.provider') : 0xeacd;
+var REACT_CONTEXT_TYPE = hasSymbol ? Symbol.for('react.context') : 0xeace;
+var REACT_ASYNC_MODE_TYPE = hasSymbol ? Symbol.for('react.async_mode') : 0xeacf;
+var REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0;
+var REACT_TIMEOUT_TYPE = hasSymbol ? Symbol.for('react.timeout') : 0xead1;
- /**
- * Replaces all of the state. Always use this or `setState` to mutate state.
- * You should treat `this.state` as immutable.
- *
- * There is no guarantee that `this.state` will be immediately updated, so
- * accessing `this.state` after calling this method may return the old value.
- *
- * @param {ReactClass} publicInstance The instance that should rerender.
- * @param {object} completeState Next state.
- * @param {?function} callback Called after component is updated.
- * @param {?string} callerName name of the calling function in the public API.
- * @internal
- */
- enqueueReplaceState: function (publicInstance, completeState, callback, callerName) {
- warnNoop(publicInstance, 'replaceState');
- },
+function typeOf(object) {
+ if (typeof object === 'object' && object !== null) {
+ var $$typeof = object.$$typeof;
- /**
- * Sets a subset of the state. This only exists because _pendingState is
- * internal. This provides a merging strategy that is not available to deep
- * properties which is confusing. TODO: Expose pendingState or don't use it
- * during the merge.
- *
- * @param {ReactClass} publicInstance The instance that should rerender.
- * @param {object} partialState Next partial state to be merged with state.
- * @param {?function} callback Called after component is updated.
- * @param {?string} Name of the calling function in the public API.
- * @internal
- */
- enqueueSetState: function (publicInstance, partialState, callback, callerName) {
- warnNoop(publicInstance, 'setState');
- }
-};
+ switch ($$typeof) {
+ case REACT_ELEMENT_TYPE:
+ var type = object.type;
-/**
- * Base class helpers for the updating state of a component.
- */
-function Component(props, context, updater) {
- this.props = props;
- this.context = context;
- this.refs = emptyObject_1;
- // We initialize the default updater but the real one gets injected by the
- // renderer.
- this.updater = updater || ReactNoopUpdateQueue;
-}
+ switch (type) {
+ case REACT_ASYNC_MODE_TYPE:
+ case REACT_FRAGMENT_TYPE:
+ case REACT_PROFILER_TYPE:
+ case REACT_STRICT_MODE_TYPE:
+ return type;
+ default:
+ var $$typeofType = type && type.$$typeof;
-Component.prototype.isReactComponent = {};
-
-/**
- * Sets a subset of the state. Always use this to mutate
- * state. You should treat `this.state` as immutable.
- *
- * There is no guarantee that `this.state` will be immediately updated, so
- * accessing `this.state` after calling this method may return the old value.
- *
- * There is no guarantee that calls to `setState` will run synchronously,
- * as they may eventually be batched together. You can provide an optional
- * callback that will be executed when the call to setState is actually
- * completed.
- *
- * When a function is provided to setState, it will be called at some point in
- * the future (not synchronously). It will be called with the up to date
- * component arguments (state, props, context). These values can be different
- * from this.* because your function may be called after receiveProps but before
- * shouldComponentUpdate, and this new state, props, and context will not yet be
- * assigned to this.
- *
- * @param {object|function} partialState Next partial state or function to
- * produce next partial state to be merged with current state.
- * @param {?function} callback Called after state is updated.
- * @final
- * @protected
- */
-Component.prototype.setState = function (partialState, callback) {
- !(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null) ? invariant_1(false, 'setState(...): takes an object of state variables to update or a function which returns an object of state variables.') : void 0;
- this.updater.enqueueSetState(this, partialState, callback, 'setState');
-};
-
-/**
- * Forces an update. This should only be invoked when it is known with
- * certainty that we are **not** in a DOM transaction.
- *
- * You may want to call this when you know that some deeper aspect of the
- * component's state has changed but `setState` was not called.
- *
- * This will not invoke `shouldComponentUpdate`, but it will invoke
- * `componentWillUpdate` and `componentDidUpdate`.
- *
- * @param {?function} callback Called after update is complete.
- * @final
- * @protected
- */
-Component.prototype.forceUpdate = function (callback) {
- this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
-};
-
-/**
- * Deprecated APIs. These APIs used to exist on classic React classes but since
- * we would like to deprecate them, we're not going to move them over to this
- * modern base class. Instead, we define a getter that warns if it's accessed.
- */
-{
- var deprecatedAPIs = {
- isMounted: ['isMounted', 'Instead, make sure to clean up subscriptions and pending requests in ' + 'componentWillUnmount to prevent memory leaks.'],
- replaceState: ['replaceState', 'Refactor your code to use setState instead (see ' + 'https://github.com/facebook/react/issues/3236).']
- };
- var defineDeprecationWarning = function (methodName, info) {
- Object.defineProperty(Component.prototype, methodName, {
- get: function () {
- lowPriorityWarning$1(false, '%s(...) is deprecated in plain JavaScript React classes. %s', info[0], info[1]);
- return undefined;
- }
- });
- };
- for (var fnName in deprecatedAPIs) {
- if (deprecatedAPIs.hasOwnProperty(fnName)) {
- defineDeprecationWarning(fnName, deprecatedAPIs[fnName]);
- }
- }
-}
-
-/**
- * Base class helpers for the updating state of a component.
- */
-function PureComponent(props, context, updater) {
- // Duplicated from Component.
- this.props = props;
- this.context = context;
- this.refs = emptyObject_1;
- // We initialize the default updater but the real one gets injected by the
- // renderer.
- this.updater = updater || ReactNoopUpdateQueue;
-}
-
-function ComponentDummy() {}
-ComponentDummy.prototype = Component.prototype;
-var pureComponentPrototype = PureComponent.prototype = new ComponentDummy();
-pureComponentPrototype.constructor = PureComponent;
-// Avoid an extra prototype jump for these methods.
-objectAssign(pureComponentPrototype, Component.prototype);
-pureComponentPrototype.isPureReactComponent = true;
-
-function AsyncComponent(props, context, updater) {
- // Duplicated from Component.
- this.props = props;
- this.context = context;
- this.refs = emptyObject_1;
- // We initialize the default updater but the real one gets injected by the
- // renderer.
- this.updater = updater || ReactNoopUpdateQueue;
-}
-
-var asyncComponentPrototype = AsyncComponent.prototype = new ComponentDummy();
-asyncComponentPrototype.constructor = AsyncComponent;
-// Avoid an extra prototype jump for these methods.
-objectAssign(asyncComponentPrototype, Component.prototype);
-asyncComponentPrototype.unstable_isAsyncReactComponent = true;
-asyncComponentPrototype.render = function () {
- return this.props.children;
-};
-
-/**
- * Keeps track of the current owner.
- *
- * The current owner is the component who should own any components that are
- * currently being constructed.
- */
-var ReactCurrentOwner = {
- /**
- * @internal
- * @type {ReactComponent}
- */
- current: null
-};
-
-var hasOwnProperty$1 = Object.prototype.hasOwnProperty;
-
-var RESERVED_PROPS = {
- key: true,
- ref: true,
- __self: true,
- __source: true
-};
-
-var specialPropKeyWarningShown;
-var specialPropRefWarningShown;
-
-function hasValidRef(config) {
- {
- if (hasOwnProperty$1.call(config, 'ref')) {
- var getter = Object.getOwnPropertyDescriptor(config, 'ref').get;
- if (getter && getter.isReactWarning) {
- return false;
- }
- }
- }
- return config.ref !== undefined;
-}
-
-function hasValidKey(config) {
- {
- if (hasOwnProperty$1.call(config, 'key')) {
- var getter = Object.getOwnPropertyDescriptor(config, 'key').get;
- if (getter && getter.isReactWarning) {
- return false;
- }
- }
- }
- return config.key !== undefined;
-}
-
-function defineKeyPropWarningGetter(props, displayName) {
- var warnAboutAccessingKey = function () {
- if (!specialPropKeyWarningShown) {
- specialPropKeyWarningShown = true;
- warning_1(false, '%s: `key` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://fb.me/react-special-props)', displayName);
- }
- };
- warnAboutAccessingKey.isReactWarning = true;
- Object.defineProperty(props, 'key', {
- get: warnAboutAccessingKey,
- configurable: true
- });
-}
-
-function defineRefPropWarningGetter(props, displayName) {
- var warnAboutAccessingRef = function () {
- if (!specialPropRefWarningShown) {
- specialPropRefWarningShown = true;
- warning_1(false, '%s: `ref` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://fb.me/react-special-props)', displayName);
- }
- };
- warnAboutAccessingRef.isReactWarning = true;
- Object.defineProperty(props, 'ref', {
- get: warnAboutAccessingRef,
- configurable: true
- });
-}
-
-/**
- * Factory method to create a new React element. This no longer adheres to
- * the class pattern, so do not use new to call it. Also, no instanceof check
- * will work. Instead test $$typeof field against Symbol.for('react.element') to check
- * if something is a React Element.
- *
- * @param {*} type
- * @param {*} key
- * @param {string|object} ref
- * @param {*} self A *temporary* helper to detect places where `this` is
- * different from the `owner` when React.createElement is called, so that we
- * can warn. We want to get rid of owner and replace string `ref`s with arrow
- * functions, and as long as `this` and owner are the same, there will be no
- * change in behavior.
- * @param {*} source An annotation object (added by a transpiler or otherwise)
- * indicating filename, line number, and/or other information.
- * @param {*} owner
- * @param {*} props
- * @internal
- */
-var ReactElement = function (type, key, ref, self, source, owner, props) {
- var element = {
- // This tag allow us to uniquely identify this as a React Element
- $$typeof: REACT_ELEMENT_TYPE,
-
- // Built-in properties that belong on the element
- type: type,
- key: key,
- ref: ref,
- props: props,
-
- // Record the component responsible for creating this element.
- _owner: owner
- };
-
- {
- // The validation flag is currently mutative. We put it on
- // an external backing store so that we can freeze the whole object.
- // This can be replaced with a WeakMap once they are implemented in
- // commonly used development environments.
- element._store = {};
-
- // To make comparing ReactElements easier for testing purposes, we make
- // the validation flag non-enumerable (where possible, which should
- // include every environment we run tests in), so the test framework
- // ignores it.
- Object.defineProperty(element._store, 'validated', {
- configurable: false,
- enumerable: false,
- writable: true,
- value: false
- });
- // self and source are DEV only properties.
- Object.defineProperty(element, '_self', {
- configurable: false,
- enumerable: false,
- writable: false,
- value: self
- });
- // Two elements created in two different places should be considered
- // equal for testing purposes and therefore we hide it from enumeration.
- Object.defineProperty(element, '_source', {
- configurable: false,
- enumerable: false,
- writable: false,
- value: source
- });
- if (Object.freeze) {
- Object.freeze(element.props);
- Object.freeze(element);
- }
- }
-
- return element;
-};
-
-/**
- * Create and return a new ReactElement of the given type.
- * See https://reactjs.org/docs/react-api.html#createelement
- */
-function createElement(type, config, children) {
- var propName;
-
- // Reserved names are extracted
- var props = {};
-
- var key = null;
- var ref = null;
- var self = null;
- var source = null;
-
- if (config != null) {
- if (hasValidRef(config)) {
- ref = config.ref;
- }
- if (hasValidKey(config)) {
- key = '' + config.key;
- }
-
- self = config.__self === undefined ? null : config.__self;
- source = config.__source === undefined ? null : config.__source;
- // Remaining properties are added to a new props object
- for (propName in config) {
- if (hasOwnProperty$1.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
- props[propName] = config[propName];
- }
- }
- }
-
- // Children can be more than one argument, and those are transferred onto
- // the newly allocated props object.
- var childrenLength = arguments.length - 2;
- if (childrenLength === 1) {
- props.children = children;
- } else if (childrenLength > 1) {
- var childArray = Array(childrenLength);
- for (var i = 0; i < childrenLength; i++) {
- childArray[i] = arguments[i + 2];
- }
- {
- if (Object.freeze) {
- Object.freeze(childArray);
- }
- }
- props.children = childArray;
- }
-
- // Resolve default props
- if (type && type.defaultProps) {
- var defaultProps = type.defaultProps;
- for (propName in defaultProps) {
- if (props[propName] === undefined) {
- props[propName] = defaultProps[propName];
- }
- }
- }
- {
- if (key || ref) {
- if (typeof props.$$typeof === 'undefined' || props.$$typeof !== REACT_ELEMENT_TYPE) {
- var displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type;
- if (key) {
- defineKeyPropWarningGetter(props, displayName);
+ switch ($$typeofType) {
+ case REACT_CONTEXT_TYPE:
+ case REACT_FORWARD_REF_TYPE:
+ case REACT_PROVIDER_TYPE:
+ return $$typeofType;
+ default:
+ return $$typeof;
+ }
}
- if (ref) {
- defineRefPropWarningGetter(props, displayName);
- }
- }
- }
- }
- return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
-}
-
-/**
- * Return a function that produces ReactElements of a given type.
- * See https://reactjs.org/docs/react-api.html#createfactory
- */
-
-
-function cloneAndReplaceKey(oldElement, newKey) {
- var newElement = ReactElement(oldElement.type, newKey, oldElement.ref, oldElement._self, oldElement._source, oldElement._owner, oldElement.props);
-
- return newElement;
-}
-
-/**
- * Clone and return a new ReactElement using element as the starting point.
- * See https://reactjs.org/docs/react-api.html#cloneelement
- */
-function cloneElement(element, config, children) {
- var propName;
-
- // Original props are copied
- var props = objectAssign({}, element.props);
-
- // Reserved names are extracted
- var key = element.key;
- var ref = element.ref;
- // Self is preserved since the owner is preserved.
- var self = element._self;
- // Source is preserved since cloneElement is unlikely to be targeted by a
- // transpiler, and the original source is probably a better indicator of the
- // true owner.
- var source = element._source;
-
- // Owner will be preserved, unless ref is overridden
- var owner = element._owner;
-
- if (config != null) {
- if (hasValidRef(config)) {
- // Silently steal the ref from the parent.
- ref = config.ref;
- owner = ReactCurrentOwner.current;
- }
- if (hasValidKey(config)) {
- key = '' + config.key;
- }
-
- // Remaining properties override existing props
- var defaultProps;
- if (element.type && element.type.defaultProps) {
- defaultProps = element.type.defaultProps;
- }
- for (propName in config) {
- if (hasOwnProperty$1.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
- if (config[propName] === undefined && defaultProps !== undefined) {
- // Resolve default props
- props[propName] = defaultProps[propName];
- } else {
- props[propName] = config[propName];
- }
- }
+ case REACT_PORTAL_TYPE:
+ return $$typeof;
}
}
- // Children can be more than one argument, and those are transferred onto
- // the newly allocated props object.
- var childrenLength = arguments.length - 2;
- if (childrenLength === 1) {
- props.children = children;
- } else if (childrenLength > 1) {
- var childArray = Array(childrenLength);
- for (var i = 0; i < childrenLength; i++) {
- childArray[i] = arguments[i + 2];
- }
- props.children = childArray;
- }
-
- return ReactElement(element.type, key, ref, self, source, owner, props);
+ return undefined;
}
-/**
- * Verifies the object is a ReactElement.
- * See https://reactjs.org/docs/react-api.html#isvalidelement
- * @param {?object} object
- * @return {boolean} True if `object` is a valid component.
- * @final
- */
-function isValidElement(object) {
- return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
-}
-
-var ReactDebugCurrentFrame = {};
-
-{
- // Component that is being worked on
- ReactDebugCurrentFrame.getCurrentStack = null;
-
- ReactDebugCurrentFrame.getStackAddendum = function () {
- var impl = ReactDebugCurrentFrame.getCurrentStack;
- if (impl) {
- return impl();
- }
- return null;
- };
-}
-
-var SEPARATOR = '.';
-var SUBSEPARATOR = ':';
-
-/**
- * Escape and wrap key so it is safe to use as a reactid
- *
- * @param {string} key to be escaped.
- * @return {string} the escaped key.
- */
-function escape(key) {
- var escapeRegex = /[=:]/g;
- var escaperLookup = {
- '=': '=0',
- ':': '=2'
- };
- var escapedString = ('' + key).replace(escapeRegex, function (match) {
- return escaperLookup[match];
- });
-
- return '$' + escapedString;
-}
-
-/**
- * TODO: Test that a single child and an array with one item have the same key
- * pattern.
- */
-
-var didWarnAboutMaps = false;
-
-var userProvidedKeyEscapeRegex = /\/+/g;
-function escapeUserProvidedKey(text) {
- return ('' + text).replace(userProvidedKeyEscapeRegex, '$&/');
-}
-
-var POOL_SIZE = 10;
-var traverseContextPool = [];
-function getPooledTraverseContext(mapResult, keyPrefix, mapFunction, mapContext) {
- if (traverseContextPool.length) {
- var traverseContext = traverseContextPool.pop();
- traverseContext.result = mapResult;
- traverseContext.keyPrefix = keyPrefix;
- traverseContext.func = mapFunction;
- traverseContext.context = mapContext;
- traverseContext.count = 0;
- return traverseContext;
- } else {
- return {
- result: mapResult,
- keyPrefix: keyPrefix,
- func: mapFunction,
- context: mapContext,
- count: 0
- };
- }
-}
-
-function releaseTraverseContext(traverseContext) {
- traverseContext.result = null;
- traverseContext.keyPrefix = null;
- traverseContext.func = null;
- traverseContext.context = null;
- traverseContext.count = 0;
- if (traverseContextPool.length < POOL_SIZE) {
- traverseContextPool.push(traverseContext);
- }
-}
-
-/**
- * @param {?*} children Children tree container.
- * @param {!string} nameSoFar Name of the key path so far.
- * @param {!function} callback Callback to invoke with each child found.
- * @param {?*} traverseContext Used to pass information throughout the traversal
- * process.
- * @return {!number} The number of children in this subtree.
- */
-function traverseAllChildrenImpl(children, nameSoFar, callback, traverseContext) {
- var type = typeof children;
-
- if (type === 'undefined' || type === 'boolean') {
- // All of the above are perceived as null.
- children = null;
- }
-
- var invokeCallback = false;
-
- if (children === null) {
- invokeCallback = true;
- } else {
- switch (type) {
- case 'string':
- case 'number':
- invokeCallback = true;
- break;
- case 'object':
- switch (children.$$typeof) {
- case REACT_ELEMENT_TYPE:
- case REACT_CALL_TYPE:
- case REACT_RETURN_TYPE:
- case REACT_PORTAL_TYPE:
- invokeCallback = true;
- }
- }
- }
-
- if (invokeCallback) {
- callback(traverseContext, children,
- // If it's the only child, treat the name as if it was wrapped in an array
- // so that it's consistent if the number of children grows.
- nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar);
- return 1;
- }
-
- var child;
- var nextName;
- var subtreeCount = 0; // Count of children found in the current subtree.
- var nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;
-
- if (Array.isArray(children)) {
- for (var i = 0; i < children.length; i++) {
- child = children[i];
- nextName = nextNamePrefix + getComponentKey(child, i);
- subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext);
- }
- } else {
- var iteratorFn = getIteratorFn(children);
- if (typeof iteratorFn === 'function') {
- {
- // Warn about using Maps as children
- if (iteratorFn === children.entries) {
- warning_1(didWarnAboutMaps, 'Using Maps as children is unsupported and will likely yield ' + 'unexpected results. Convert it to a sequence/iterable of keyed ' + 'ReactElements instead.%s', ReactDebugCurrentFrame.getStackAddendum());
- didWarnAboutMaps = true;
- }
- }
-
- var iterator = iteratorFn.call(children);
- var step;
- var ii = 0;
- while (!(step = iterator.next()).done) {
- child = step.value;
- nextName = nextNamePrefix + getComponentKey(child, ii++);
- subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext);
- }
- } else if (type === 'object') {
- var addendum = '';
- {
- addendum = ' If you meant to render a collection of children, use an array ' + 'instead.' + ReactDebugCurrentFrame.getStackAddendum();
- }
- var childrenString = '' + children;
- invariant_1(false, 'Objects are not valid as a React child (found: %s).%s', childrenString === '[object Object]' ? 'object with keys {' + Object.keys(children).join(', ') + '}' : childrenString, addendum);
- }
- }
-
- return subtreeCount;
-}
-
-/**
- * Traverses children that are typically specified as `props.children`, but
- * might also be specified through attributes:
- *
- * - `traverseAllChildren(this.props.children, ...)`
- * - `traverseAllChildren(this.props.leftPanelChildren, ...)`
- *
- * The `traverseContext` is an optional argument that is passed through the
- * entire traversal. It can be used to store accumulations or anything else that
- * the callback might find relevant.
- *
- * @param {?*} children Children tree object.
- * @param {!function} callback To invoke upon traversing each child.
- * @param {?*} traverseContext Context for traversal.
- * @return {!number} The number of children in this subtree.
- */
-function traverseAllChildren(children, callback, traverseContext) {
- if (children == null) {
- return 0;
- }
-
- return traverseAllChildrenImpl(children, '', callback, traverseContext);
-}
-
-/**
- * Generate a key string that identifies a component within a set.
- *
- * @param {*} component A component that could contain a manual key.
- * @param {number} index Index that is used if a manual key is not provided.
- * @return {string}
- */
-function getComponentKey(component, index) {
- // Do some typechecking here since we call this blindly. We want to ensure
- // that we don't block potential future ES APIs.
- if (typeof component === 'object' && component !== null && component.key != null) {
- // Explicit key
- return escape(component.key);
- }
- // Implicit key determined by the index in the set
- return index.toString(36);
-}
-
-function forEachSingleChild(bookKeeping, child, name) {
- var func = bookKeeping.func,
- context = bookKeeping.context;
-
- func.call(context, child, bookKeeping.count++);
-}
-
-/**
- * Iterates through children that are typically specified as `props.children`.
- *
- * See https://reactjs.org/docs/react-api.html#react.children.foreach
- *
- * The provided forEachFunc(child, index) will be called for each
- * leaf child.
- *
- * @param {?*} children Children tree container.
- * @param {function(*, int)} forEachFunc
- * @param {*} forEachContext Context for forEachContext.
- */
-function forEachChildren(children, forEachFunc, forEachContext) {
- if (children == null) {
- return children;
- }
- var traverseContext = getPooledTraverseContext(null, null, forEachFunc, forEachContext);
- traverseAllChildren(children, forEachSingleChild, traverseContext);
- releaseTraverseContext(traverseContext);
-}
-
-function mapSingleChildIntoContext(bookKeeping, child, childKey) {
- var result = bookKeeping.result,
- keyPrefix = bookKeeping.keyPrefix,
- func = bookKeeping.func,
- context = bookKeeping.context;
- var mappedChild = func.call(context, child, bookKeeping.count++);
- if (Array.isArray(mappedChild)) {
- mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, emptyFunction_1.thatReturnsArgument);
- } else if (mappedChild != null) {
- if (isValidElement(mappedChild)) {
- mappedChild = cloneAndReplaceKey(mappedChild,
- // Keep both the (mapped) and old keys if they differ, just as
- // traverseAllChildren used to do for objects as children
- keyPrefix + (mappedChild.key && (!child || child.key !== mappedChild.key) ? escapeUserProvidedKey(mappedChild.key) + '/' : '') + childKey);
- }
- result.push(mappedChild);
- }
-}
-function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
- var escapedPrefix = '';
- if (prefix != null) {
- escapedPrefix = escapeUserProvidedKey(prefix) + '/';
- }
- var traverseContext = getPooledTraverseContext(array, escapedPrefix, func, context);
- traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
- releaseTraverseContext(traverseContext);
-}
-/**
- * Maps children that are typically specified as `props.children`.
- *
- * See https://reactjs.org/docs/react-api.html#react.children.map
- *
- * The provided mapFunction(child, key, index) will be called for each
- * leaf child.
- *
- * @param {?*} children Children tree container.
- * @param {function(*, int)} func The map function.
- * @param {*} context Context for mapFunction.
- * @return {object} Object containing the ordered map of results.
- */
-function mapChildren(children, func, context) {
- if (children == null) {
- return children;
- }
- var result = [];
- mapIntoWithKeyPrefixInternal(children, result, null, func, context);
- return result;
-}
-/**
- * Count the number of children that are typically specified as
- * `props.children`.
- *
- * See https://reactjs.org/docs/react-api.html#react.children.count
- *
- * @param {?*} children Children tree container.
- * @return {number} The number of children.
- */
-function countChildren(children, context) {
- return traverseAllChildren(children, emptyFunction_1.thatReturnsNull, null);
-}
-/**
- * Flatten a children object (typically specified as `props.children`) and
- * return an array with appropriately re-keyed children.
- *
- * See https://reactjs.org/docs/react-api.html#react.children.toarray
- */
-function toArray(children) {
- var result = [];
- mapIntoWithKeyPrefixInternal(children, result, null, emptyFunction_1.thatReturnsArgument);
- return result;
-}
-/**
- * Returns the first child in a collection of children and verifies that there
- * is only one child in the collection.
- *
- * See https://reactjs.org/docs/react-api.html#react.children.only
- *
- * The current implementation of this function assumes that a single child gets
- * passed without a wrapper, but the purpose of this helper function is to
- * abstract away the particular structure of children.
- *
- * @param {?object} children Child collection structure.
- * @return {ReactElement} The first and only `ReactElement` contained in the
- * structure.
- */
-function onlyChild(children) {
- !isValidElement(children) ? invariant_1(false, 'React.Children.only expected to receive a single React element child.') : void 0;
- return children;
+
+
+
+
+
+
+
+function isForwardRef(object) {
+ return typeOf(object) === REACT_FORWARD_REF_TYPE;
}
var describeComponentFrame = function (name, source, ownerName) {
@@ -1238,11 +145,36 @@ var describeComponentFrame = function (name, source, ownerName) {
function getComponentName(fiber) {
var type = fiber.type;
+ if (typeof type === 'function') {
+ return type.displayName || type.name;
+ }
if (typeof type === 'string') {
return type;
}
- if (typeof type === 'function') {
- return type.displayName || type.name;
+ switch (type) {
+ case REACT_ASYNC_MODE_TYPE:
+ return 'AsyncMode';
+ case REACT_CONTEXT_TYPE:
+ return 'Context.Consumer';
+ case REACT_FRAGMENT_TYPE:
+ return 'ReactFragment';
+ case REACT_PORTAL_TYPE:
+ return 'ReactPortal';
+ case REACT_PROFILER_TYPE:
+ return 'Profiler(' + fiber.pendingProps.id + ')';
+ case REACT_PROVIDER_TYPE:
+ return 'Context.Provider';
+ case REACT_STRICT_MODE_TYPE:
+ return 'StrictMode';
+ case REACT_TIMEOUT_TYPE:
+ return 'Timeout';
+ }
+ if (typeof type === 'object' && type !== null) {
+ switch (type.$$typeof) {
+ case REACT_FORWARD_REF_TYPE:
+ var functionName = type.render.displayName || type.render.name || '';
+ return functionName !== '' ? 'ForwardRef(' + functionName + ')' : 'ForwardRef';
+ }
}
return null;
}
@@ -1252,13 +184,80 @@ function getComponentName(fiber) {
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
+ *
*/
-var ReactPropTypesSecret$1 = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';
+var emptyObject = {};
-var ReactPropTypesSecret_1 = ReactPropTypesSecret$1;
+var emptyObject_1 = emptyObject;
+
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @typechecks
+ *
+ */
+
+/*eslint-disable no-self-compare */
+
+
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+/**
+ * inlined Object.is polyfill to avoid requiring consumers ship their own
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
+ */
+function is(x, y) {
+ // SameValue algorithm
+ if (x === y) {
+ // Steps 1-5, 7-10
+ // Steps 6.b-6.e: +0 != -0
+ // Added the nonzero y check to make Flow happy, but it is redundant
+ return x !== 0 || y !== 0 || 1 / x === 1 / y;
+ } else {
+ // Step 6.a: NaN == NaN
+ return x !== x && y !== y;
+ }
+}
+
+/**
+ * Performs equality by iterating through keys on an object and returning false
+ * when any key has values which are not strictly equal between the arguments.
+ * Returns true when the values of all keys are strictly equal.
+ */
+function shallowEqual(objA, objB) {
+ if (is(objA, objB)) {
+ return true;
+ }
+
+ if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
+ return false;
+ }
+
+ var keysA = Object.keys(objA);
+ var keysB = Object.keys(objB);
+
+ if (keysA.length !== keysB.length) {
+ return false;
+ }
+
+ // Test for A's keys different from B.
+ for (var i = 0; i < keysA.length; i++) {
+ if (!hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+var shallowEqual_1 = shallowEqual;
/**
* Copyright (c) 2013-present, Facebook, Inc.
@@ -1269,13 +268,6 @@ var ReactPropTypesSecret_1 = ReactPropTypesSecret$1;
-{
- var invariant$2 = invariant_1;
- var warning$2 = warning_1;
- var ReactPropTypesSecret = ReactPropTypesSecret_1;
- var loggedTypeFailures = {};
-}
-
/**
* Assert that the values match with the type specs.
* Error messages are memorized and will only be shown once.
@@ -1288,447 +280,11 @@ var ReactPropTypesSecret_1 = ReactPropTypesSecret$1;
* @private
*/
function checkPropTypes(typeSpecs, values, location, componentName, getStack) {
- {
- for (var typeSpecName in typeSpecs) {
- if (typeSpecs.hasOwnProperty(typeSpecName)) {
- var error;
- // Prop type validation may throw. In case they do, we don't want to
- // fail the render phase where it didn't fail before. So we log it.
- // After these have been cleaned up, we'll let them throw.
- try {
- // This is intentionally an invariant that gets caught. It's the same
- // behavior as without this statement except with a better message.
- invariant$2(typeof typeSpecs[typeSpecName] === 'function', '%s: %s type `%s` is invalid; it must be a function, usually from ' + 'the `prop-types` package, but received `%s`.', componentName || 'React class', location, typeSpecName, typeof typeSpecs[typeSpecName]);
- error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret);
- } catch (ex) {
- error = ex;
- }
- warning$2(!error || error instanceof Error, '%s: type specification of %s `%s` is invalid; the type checker ' + 'function must return `null` or an `Error` but returned a %s. ' + 'You may have forgotten to pass an argument to the type checker ' + 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' + 'shape all require an argument).', componentName || 'React class', location, typeSpecName, typeof error);
- if (error instanceof Error && !(error.message in loggedTypeFailures)) {
- // Only monitor this failure once because there tends to be a lot of the
- // same error.
- loggedTypeFailures[error.message] = true;
-
- var stack = getStack ? getStack() : '';
-
- warning$2(false, 'Failed %s type: %s%s', location, error.message, stack != null ? stack : '');
- }
- }
- }
- }
+
}
var checkPropTypes_1 = checkPropTypes;
-/**
- * ReactElementValidator provides a wrapper around a element factory
- * which validates the props passed to the element. This is intended to be
- * used only in DEV and could be replaced by a static type checker for languages
- * that support it.
- */
-
-{
- var currentlyValidatingElement = null;
-
- var propTypesMisspellWarningShown = false;
-
- var getDisplayName = function (element) {
- if (element == null) {
- return '#empty';
- } else if (typeof element === 'string' || typeof element === 'number') {
- return '#text';
- } else if (typeof element.type === 'string') {
- return element.type;
- } else if (element.type === REACT_FRAGMENT_TYPE) {
- return 'React.Fragment';
- } else {
- return element.type.displayName || element.type.name || 'Unknown';
- }
- };
-
- var getStackAddendum = function () {
- var stack = '';
- if (currentlyValidatingElement) {
- var name = getDisplayName(currentlyValidatingElement);
- var owner = currentlyValidatingElement._owner;
- stack += describeComponentFrame(name, currentlyValidatingElement._source, owner && getComponentName(owner));
- }
- stack += ReactDebugCurrentFrame.getStackAddendum() || '';
- return stack;
- };
-
- var VALID_FRAGMENT_PROPS = new Map([['children', true], ['key', true]]);
-}
-
-function getDeclarationErrorAddendum() {
- if (ReactCurrentOwner.current) {
- var name = getComponentName(ReactCurrentOwner.current);
- if (name) {
- return '\n\nCheck the render method of `' + name + '`.';
- }
- }
- return '';
-}
-
-function getSourceInfoErrorAddendum(elementProps) {
- if (elementProps !== null && elementProps !== undefined && elementProps.__source !== undefined) {
- var source = elementProps.__source;
- var fileName = source.fileName.replace(/^.*[\\\/]/, '');
- var lineNumber = source.lineNumber;
- return '\n\nCheck your code at ' + fileName + ':' + lineNumber + '.';
- }
- return '';
-}
-
-/**
- * Warn if there's no key explicitly set on dynamic arrays of children or
- * object keys are not valid. This allows us to keep track of children between
- * updates.
- */
-var ownerHasKeyUseWarning = {};
-
-function getCurrentComponentErrorInfo(parentType) {
- var info = getDeclarationErrorAddendum();
-
- if (!info) {
- var parentName = typeof parentType === 'string' ? parentType : parentType.displayName || parentType.name;
- if (parentName) {
- info = '\n\nCheck the top-level render call using <' + parentName + '>.';
- }
- }
- return info;
-}
-
-/**
- * Warn if the element doesn't have an explicit key assigned to it.
- * This element is in an array. The array could grow and shrink or be
- * reordered. All children that haven't already been validated are required to
- * have a "key" property assigned to it. Error statuses are cached so a warning
- * will only be shown once.
- *
- * @internal
- * @param {ReactElement} element Element that requires a key.
- * @param {*} parentType element's parent's type.
- */
-function validateExplicitKey(element, parentType) {
- if (!element._store || element._store.validated || element.key != null) {
- return;
- }
- element._store.validated = true;
-
- var currentComponentErrorInfo = getCurrentComponentErrorInfo(parentType);
- if (ownerHasKeyUseWarning[currentComponentErrorInfo]) {
- return;
- }
- ownerHasKeyUseWarning[currentComponentErrorInfo] = true;
-
- // Usually the current owner is the offender, but if it accepts children as a
- // property, it may be the creator of the child that's responsible for
- // assigning it a key.
- var childOwner = '';
- if (element && element._owner && element._owner !== ReactCurrentOwner.current) {
- // Give the component that originally created this child.
- childOwner = ' It was passed a child from ' + getComponentName(element._owner) + '.';
- }
-
- currentlyValidatingElement = element;
- {
- warning_1(false, 'Each child in an array or iterator should have a unique "key" prop.' + '%s%s See https://fb.me/react-warning-keys for more information.%s', currentComponentErrorInfo, childOwner, getStackAddendum());
- }
- currentlyValidatingElement = null;
-}
-
-/**
- * Ensure that every element either is passed in a static location, in an
- * array with an explicit keys property defined, or in an object literal
- * with valid key property.
- *
- * @internal
- * @param {ReactNode} node Statically passed child of any type.
- * @param {*} parentType node's parent's type.
- */
-function validateChildKeys(node, parentType) {
- if (typeof node !== 'object') {
- return;
- }
- if (Array.isArray(node)) {
- for (var i = 0; i < node.length; i++) {
- var child = node[i];
- if (isValidElement(child)) {
- validateExplicitKey(child, parentType);
- }
- }
- } else if (isValidElement(node)) {
- // This element was passed in a valid location.
- if (node._store) {
- node._store.validated = true;
- }
- } else if (node) {
- var iteratorFn = getIteratorFn(node);
- if (typeof iteratorFn === 'function') {
- // Entry iterators used to provide implicit keys,
- // but now we print a separate warning for them later.
- if (iteratorFn !== node.entries) {
- var iterator = iteratorFn.call(node);
- var step;
- while (!(step = iterator.next()).done) {
- if (isValidElement(step.value)) {
- validateExplicitKey(step.value, parentType);
- }
- }
- }
- }
- }
-}
-
-/**
- * Given an element, validate that its props follow the propTypes definition,
- * provided by the type.
- *
- * @param {ReactElement} element
- */
-function validatePropTypes(element) {
- var componentClass = element.type;
- if (typeof componentClass !== 'function') {
- return;
- }
- var name = componentClass.displayName || componentClass.name;
- var propTypes = componentClass.propTypes;
- if (propTypes) {
- currentlyValidatingElement = element;
- checkPropTypes_1(propTypes, element.props, 'prop', name, getStackAddendum);
- currentlyValidatingElement = null;
- } else if (componentClass.PropTypes !== undefined && !propTypesMisspellWarningShown) {
- propTypesMisspellWarningShown = true;
- warning_1(false, 'Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?', name || 'Unknown');
- }
- if (typeof componentClass.getDefaultProps === 'function') {
- warning_1(componentClass.getDefaultProps.isReactClassApproved, 'getDefaultProps is only used on classic React.createClass ' + 'definitions. Use a static property named `defaultProps` instead.');
- }
-}
-
-/**
- * Given a fragment, validate that it can only be provided with fragment props
- * @param {ReactElement} fragment
- */
-function validateFragmentProps(fragment) {
- currentlyValidatingElement = fragment;
-
- var _iteratorNormalCompletion = true;
- var _didIteratorError = false;
- var _iteratorError = undefined;
-
- try {
- for (var _iterator = Object.keys(fragment.props)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
- var key = _step.value;
-
- if (!VALID_FRAGMENT_PROPS.has(key)) {
- warning_1(false, 'Invalid prop `%s` supplied to `React.Fragment`. ' + 'React.Fragment can only have `key` and `children` props.%s', key, getStackAddendum());
- break;
- }
- }
- } catch (err) {
- _didIteratorError = true;
- _iteratorError = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion && _iterator['return']) {
- _iterator['return']();
- }
- } finally {
- if (_didIteratorError) {
- throw _iteratorError;
- }
- }
- }
-
- if (fragment.ref !== null) {
- warning_1(false, 'Invalid attribute `ref` supplied to `React.Fragment`.%s', getStackAddendum());
- }
-
- currentlyValidatingElement = null;
-}
-
-function createElementWithValidation(type, props, children) {
- var validType = typeof type === 'string' || typeof type === 'function' || typeof type === 'symbol' || typeof type === 'number';
- // We warn in this case but don't throw. We expect the element creation to
- // succeed and there will likely be errors in render.
- if (!validType) {
- var info = '';
- if (type === undefined || typeof type === 'object' && type !== null && Object.keys(type).length === 0) {
- info += ' You likely forgot to export your component from the file ' + "it's defined in, or you might have mixed up default and named imports.";
- }
-
- var sourceInfo = getSourceInfoErrorAddendum(props);
- if (sourceInfo) {
- info += sourceInfo;
- } else {
- info += getDeclarationErrorAddendum();
- }
-
- info += getStackAddendum() || '';
-
- warning_1(false, 'React.createElement: type is invalid -- expected a string (for ' + 'built-in components) or a class/function (for composite ' + 'components) but got: %s.%s', type == null ? type : typeof type, info);
- }
-
- var element = createElement.apply(this, arguments);
-
- // The result can be nullish if a mock or a custom function is used.
- // TODO: Drop this when these are no longer allowed as the type argument.
- if (element == null) {
- return element;
- }
-
- // Skip key warning if the type isn't valid since our key validation logic
- // doesn't expect a non-string/function type and can throw confusing errors.
- // We don't want exception behavior to differ between dev and prod.
- // (Rendering will throw with a helpful message and as soon as the type is
- // fixed, the key warnings will appear.)
- if (validType) {
- for (var i = 2; i < arguments.length; i++) {
- validateChildKeys(arguments[i], type);
- }
- }
-
- if (typeof type === 'symbol' && type === REACT_FRAGMENT_TYPE) {
- validateFragmentProps(element);
- } else {
- validatePropTypes(element);
- }
-
- return element;
-}
-
-function createFactoryWithValidation(type) {
- var validatedFactory = createElementWithValidation.bind(null, type);
- // Legacy hook TODO: Warn if this is accessed
- validatedFactory.type = type;
-
- {
- Object.defineProperty(validatedFactory, 'type', {
- enumerable: false,
- get: function () {
- lowPriorityWarning$1(false, 'Factory.type is deprecated. Access the class directly ' + 'before passing it to createFactory.');
- Object.defineProperty(this, 'type', {
- value: type
- });
- return type;
- }
- });
- }
-
- return validatedFactory;
-}
-
-function cloneElementWithValidation(element, props, children) {
- var newElement = cloneElement.apply(this, arguments);
- for (var i = 2; i < arguments.length; i++) {
- validateChildKeys(arguments[i], newElement.type);
- }
- validatePropTypes(newElement);
- return newElement;
-}
-
-var React = {
- Children: {
- map: mapChildren,
- forEach: forEachChildren,
- count: countChildren,
- toArray: toArray,
- only: onlyChild
- },
-
- Component: Component,
- PureComponent: PureComponent,
- unstable_AsyncComponent: AsyncComponent,
-
- Fragment: REACT_FRAGMENT_TYPE,
-
- createElement: createElementWithValidation,
- cloneElement: cloneElementWithValidation,
- createFactory: createFactoryWithValidation,
- isValidElement: isValidElement,
-
- version: ReactVersion,
-
- __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
- ReactCurrentOwner: ReactCurrentOwner,
- // Used by renderers to avoid bundling object-assign twice in UMD bundles:
- assign: objectAssign
- }
-};
-
-{
- objectAssign(React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, {
- // These should not be included in production.
- ReactDebugCurrentFrame: ReactDebugCurrentFrame,
- // Shim for React DOM 16.0.0 which still destructured (but not used) this.
- // TODO: remove in React 17.0.
- ReactComponentTreeHook: {}
- });
-}
-
-
-
-var React$2 = Object.freeze({
- default: React
-});
-
-var React$3 = ( React$2 && React ) || React$2;
-
-// TODO: decide on the top-level export form.
-// This is hacky but makes it work with both Rollup and Jest.
-var react = React$3['default'] ? React$3['default'] : React$3;
-
-return react;
-
-})));
-
-},{}],2:[function(require,module,exports){
-/** @license React v16.2.0
- * react-test-renderer-shallow.development.js
- *
- * Copyright (c) 2013-present, Facebook, Inc.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-'use strict';
-
- (function() {
-'use strict';
-
-var _assign = require('object-assign');
-var React = require('../../../dist/react.development');
-var emptyObject = require('fbjs/lib/emptyObject');
-var invariant = require('fbjs/lib/invariant');
-var shallowEqual = require('fbjs/lib/shallowEqual');
-var checkPropTypes = require('prop-types/checkPropTypes');
-
-/**
- * WARNING: DO NOT manually require this module.
- * This is a replacement for `invariant(...)` used by the error code system
- * and will _only_ be required by the corresponding babel pass.
- * It always throws.
- */
-
-var describeComponentFrame = function (name, source, ownerName) {
- return '\n in ' + (name || 'Unknown') + (source ? ' (at ' + source.fileName.replace(/^.*[\\\/]/, '') + ':' + source.lineNumber + ')' : ownerName ? ' (created by ' + ownerName + ')' : '');
-};
-
-function getComponentName(fiber) {
- var type = fiber.type;
-
- if (typeof type === 'string') {
- return type;
- }
- if (typeof type === 'function') {
- return type.displayName || type.name;
- }
- return null;
-}
-
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var ReactShallowRenderer = function () {
@@ -1754,12 +310,12 @@ var ReactShallowRenderer = function () {
};
ReactShallowRenderer.prototype.render = function render(element) {
- var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : emptyObject;
+ var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : emptyObject_1;
- !React.isValidElement(element) ? invariant(false, 'ReactShallowRenderer render(): Invalid component element.%s', typeof element === 'function' ? ' Instead of passing a component class, make sure to instantiate ' + 'it by passing it to React.createElement.' : '') : void 0;
+ !React.isValidElement(element) ? reactProdInvariant('12', typeof element === 'function' ? ' Instead of passing a component class, make sure to instantiate ' + 'it by passing it to React.createElement.' : '') : void 0;
// Show a special message for host elements since it's a common case.
- !(typeof element.type !== 'string') ? invariant(false, 'ReactShallowRenderer render(): Shallow rendering works only with custom components, not primitives (%s). Instead of calling `.render(el)` and inspecting the rendered output, look at `el.props` directly instead.', element.type) : void 0;
- !(typeof element.type === 'function') ? invariant(false, 'ReactShallowRenderer render(): Shallow rendering works only with custom components, but the provided element type was `%s`.', Array.isArray(element.type) ? 'array' : element.type === null ? 'null' : typeof element.type) : void 0;
+ !(typeof element.type !== 'string') ? reactProdInvariant('13', element.type) : void 0;
+ !(isForwardRef(element) || typeof element.type === 'function') ? reactProdInvariant('249', Array.isArray(element.type) ? 'array' : element.type === null ? 'null' : typeof element.type) : void 0;
if (this._rendering) {
return;
@@ -1767,25 +323,29 @@ var ReactShallowRenderer = function () {
this._rendering = true;
this._element = element;
- this._context = context;
+ this._context = getMaskedContext(element.type.contextTypes, context);
if (this._instance) {
- this._updateClassComponent(element.type, element.props, context);
+ this._updateClassComponent(element, this._context);
} else {
- if (shouldConstruct(element.type)) {
- this._instance = new element.type(element.props, context, this._updater);
+ if (isForwardRef(element)) {
+ this._rendered = element.type.render(element.props, element.ref);
+ } else if (shouldConstruct(element.type)) {
+ this._instance = new element.type(element.props, this._context, this._updater);
+
+ this._updateStateFromStaticLifecycle(element.props);
if (element.type.hasOwnProperty('contextTypes')) {
currentlyValidatingElement = element;
- checkPropTypes(element.type.contextTypes, context, 'context', getName(element.type, this._instance), getStackAddendum);
+ checkPropTypes_1(element.type.contextTypes, this._context, 'context', getName(element.type, this._instance), getStackAddendum);
currentlyValidatingElement = null;
}
- this._mountClassComponent(element.props, context);
+ this._mountClassComponent(element, this._context);
} else {
- this._rendered = element.type(element.props, context);
+ this._rendered = element.type(element.props, this._context);
}
}
@@ -1809,20 +369,29 @@ var ReactShallowRenderer = function () {
this._instance = null;
};
- ReactShallowRenderer.prototype._mountClassComponent = function _mountClassComponent(props, context) {
+ ReactShallowRenderer.prototype._mountClassComponent = function _mountClassComponent(element, context) {
this._instance.context = context;
- this._instance.props = props;
- this._instance.state = this._instance.state || emptyObject;
+ this._instance.props = element.props;
+ this._instance.state = this._instance.state || null;
this._instance.updater = this._updater;
- if (typeof this._instance.componentWillMount === 'function') {
+ if (typeof this._instance.UNSAFE_componentWillMount === 'function' || typeof this._instance.componentWillMount === 'function') {
var beforeState = this._newState;
- this._instance.componentWillMount();
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (typeof element.type.getDerivedStateFromProps !== 'function' && typeof this._instance.getSnapshotBeforeUpdate !== 'function') {
+ if (typeof this._instance.componentWillMount === 'function') {
+ this._instance.componentWillMount();
+ }
+ if (typeof this._instance.UNSAFE_componentWillMount === 'function') {
+ this._instance.UNSAFE_componentWillMount();
+ }
+ }
// setState may have been called during cWM
if (beforeState !== this._newState) {
- this._instance.state = this._newState || emptyObject;
+ this._instance.state = this._newState || emptyObject_1;
}
}
@@ -1831,13 +400,28 @@ var ReactShallowRenderer = function () {
// because DOM refs are not available.
};
- ReactShallowRenderer.prototype._updateClassComponent = function _updateClassComponent(type, props, context) {
- var oldState = this._instance.state || emptyObject;
+ ReactShallowRenderer.prototype._updateClassComponent = function _updateClassComponent(element, context) {
+ var props = element.props,
+ type = element.type;
+
+
+ var oldState = this._instance.state || emptyObject_1;
var oldProps = this._instance.props;
- if (oldProps !== props && typeof this._instance.componentWillReceiveProps === 'function') {
- this._instance.componentWillReceiveProps(props, context);
+ if (oldProps !== props) {
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (typeof element.type.getDerivedStateFromProps !== 'function' && typeof this._instance.getSnapshotBeforeUpdate !== 'function') {
+ if (typeof this._instance.componentWillReceiveProps === 'function') {
+ this._instance.componentWillReceiveProps(props, context);
+ }
+ if (typeof this._instance.UNSAFE_componentWillReceiveProps === 'function') {
+ this._instance.UNSAFE_componentWillReceiveProps(props, context);
+ }
+ }
}
+ this._updateStateFromStaticLifecycle(props);
+
// Read state after cWRP in case it calls setState
var state = this._newState || oldState;
@@ -1848,12 +432,19 @@ var ReactShallowRenderer = function () {
} else if (typeof this._instance.shouldComponentUpdate === 'function') {
shouldUpdate = !!this._instance.shouldComponentUpdate(props, state, context);
} else if (type.prototype && type.prototype.isPureReactComponent) {
- shouldUpdate = !shallowEqual(oldProps, props) || !shallowEqual(oldState, state);
+ shouldUpdate = !shallowEqual_1(oldProps, props) || !shallowEqual_1(oldState, state);
}
if (shouldUpdate) {
- if (typeof this._instance.componentWillUpdate === 'function') {
- this._instance.componentWillUpdate(props, state, context);
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (typeof element.type.getDerivedStateFromProps !== 'function' && typeof this._instance.getSnapshotBeforeUpdate !== 'function') {
+ if (typeof this._instance.componentWillUpdate === 'function') {
+ this._instance.componentWillUpdate(props, state, context);
+ }
+ if (typeof this._instance.UNSAFE_componentWillUpdate === 'function') {
+ this._instance.UNSAFE_componentWillUpdate(props, state, context);
+ }
}
}
@@ -1868,6 +459,21 @@ var ReactShallowRenderer = function () {
// because DOM refs are not available.
};
+ ReactShallowRenderer.prototype._updateStateFromStaticLifecycle = function _updateStateFromStaticLifecycle(props) {
+ var type = this._element.type;
+
+
+ if (typeof type.getDerivedStateFromProps === 'function') {
+ var oldState = this._newState || this._instance.state;
+ var partialState = type.getDerivedStateFromProps.call(null, props, oldState);
+
+ if (partialState != null) {
+ var newState = _assign({}, oldState, partialState);
+ this._instance.state = this._newState = newState;
+ }
+ }
+ };
+
return ReactShallowRenderer;
}();
@@ -1925,7 +531,12 @@ var Updater = function () {
var currentState = this._renderer._newState || publicInstance.state;
if (typeof partialState === 'function') {
- partialState = partialState(currentState, publicInstance.props);
+ partialState = partialState.call(publicInstance, currentState, publicInstance.props);
+ }
+
+ // Null and undefined are treated as no-ops.
+ if (partialState === null || partialState === undefined) {
+ return;
}
this._renderer._newState = _assign({}, currentState, partialState);
@@ -1969,9 +580,20 @@ function shouldConstruct(Component) {
return !!(Component.prototype && Component.prototype.isReactComponent);
}
+function getMaskedContext(contextTypes, unmaskedContext) {
+ if (!contextTypes) {
+ return emptyObject_1;
+ }
+ var context = {};
+ for (var key in contextTypes) {
+ context[key] = unmaskedContext[key];
+ }
+ return context;
+}
-var ReactShallowRenderer$2 = Object.freeze({
+
+var ReactShallowRenderer$2 = ({
default: ReactShallowRenderer
});
@@ -1979,421 +601,8 @@ var ReactShallowRenderer$3 = ( ReactShallowRenderer$2 && ReactShallowRenderer )
// TODO: decide on the top-level export form.
// This is hacky but makes it work with both Rollup and Jest.
-var shallow = ReactShallowRenderer$3['default'] ? ReactShallowRenderer$3['default'] : ReactShallowRenderer$3;
+var shallow = ReactShallowRenderer$3.default ? ReactShallowRenderer$3.default : ReactShallowRenderer$3;
-module.exports = shallow;
- })();
+return shallow;
-
-},{"../../../dist/react.development":1,"fbjs/lib/emptyObject":5,"fbjs/lib/invariant":6,"fbjs/lib/shallowEqual":7,"object-assign":9,"prop-types/checkPropTypes":10}],3:[function(require,module,exports){
-'use strict';
-
-module.exports = require('./cjs/react-test-renderer-shallow.development.js');
-
-},{"./cjs/react-test-renderer-shallow.development.js":2}],4:[function(require,module,exports){
-"use strict";
-
-/**
- * Copyright (c) 2013-present, Facebook, Inc.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- *
- */
-
-function makeEmptyFunction(arg) {
- return function () {
- return arg;
- };
-}
-
-/**
- * This function accepts and discards inputs; it has no side effects. This is
- * primarily useful idiomatically for overridable function endpoints which
- * always need to be callable, since JS lacks a null-call idiom ala Cocoa.
- */
-var emptyFunction = function emptyFunction() {};
-
-emptyFunction.thatReturns = makeEmptyFunction;
-emptyFunction.thatReturnsFalse = makeEmptyFunction(false);
-emptyFunction.thatReturnsTrue = makeEmptyFunction(true);
-emptyFunction.thatReturnsNull = makeEmptyFunction(null);
-emptyFunction.thatReturnsThis = function () {
- return this;
-};
-emptyFunction.thatReturnsArgument = function (arg) {
- return arg;
-};
-
-module.exports = emptyFunction;
-},{}],5:[function(require,module,exports){
-/**
- * Copyright (c) 2013-present, Facebook, Inc.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- */
-
-'use strict';
-
-var emptyObject = {};
-
-if ("development" !== 'production') {
- Object.freeze(emptyObject);
-}
-
-module.exports = emptyObject;
-},{}],6:[function(require,module,exports){
-/**
- * Copyright (c) 2013-present, Facebook, Inc.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- */
-
-'use strict';
-
-/**
- * Use invariant() to assert state which your program assumes to be true.
- *
- * Provide sprintf-style format (only %s is supported) and arguments
- * to provide information about what broke and what you were
- * expecting.
- *
- * The invariant message will be stripped in production, but the invariant
- * will remain to ensure logic does not differ in production.
- */
-
-var validateFormat = function validateFormat(format) {};
-
-if ("development" !== 'production') {
- validateFormat = function validateFormat(format) {
- if (format === undefined) {
- throw new Error('invariant requires an error message argument');
- }
- };
-}
-
-function invariant(condition, format, a, b, c, d, e, f) {
- validateFormat(format);
-
- if (!condition) {
- var error;
- if (format === undefined) {
- error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
- } else {
- var args = [a, b, c, d, e, f];
- var argIndex = 0;
- error = new Error(format.replace(/%s/g, function () {
- return args[argIndex++];
- }));
- error.name = 'Invariant Violation';
- }
-
- error.framesToPop = 1; // we don't care about invariant's own frame
- throw error;
- }
-}
-
-module.exports = invariant;
-},{}],7:[function(require,module,exports){
-/**
- * Copyright (c) 2013-present, Facebook, Inc.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- * @typechecks
- *
- */
-
-/*eslint-disable no-self-compare */
-
-'use strict';
-
-var hasOwnProperty = Object.prototype.hasOwnProperty;
-
-/**
- * inlined Object.is polyfill to avoid requiring consumers ship their own
- * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
- */
-function is(x, y) {
- // SameValue algorithm
- if (x === y) {
- // Steps 1-5, 7-10
- // Steps 6.b-6.e: +0 != -0
- // Added the nonzero y check to make Flow happy, but it is redundant
- return x !== 0 || y !== 0 || 1 / x === 1 / y;
- } else {
- // Step 6.a: NaN == NaN
- return x !== x && y !== y;
- }
-}
-
-/**
- * Performs equality by iterating through keys on an object and returning false
- * when any key has values which are not strictly equal between the arguments.
- * Returns true when the values of all keys are strictly equal.
- */
-function shallowEqual(objA, objB) {
- if (is(objA, objB)) {
- return true;
- }
-
- if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
- return false;
- }
-
- var keysA = Object.keys(objA);
- var keysB = Object.keys(objB);
-
- if (keysA.length !== keysB.length) {
- return false;
- }
-
- // Test for A's keys different from B.
- for (var i = 0; i < keysA.length; i++) {
- if (!hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
- return false;
- }
- }
-
- return true;
-}
-
-module.exports = shallowEqual;
-},{}],8:[function(require,module,exports){
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- */
-
-'use strict';
-
-var emptyFunction = require('./emptyFunction');
-
-/**
- * Similar to invariant but only logs a warning if the condition is not met.
- * This can be used to log issues in development environments in critical
- * paths. Removing the logging code for production environments will keep the
- * same logic and follow the same code paths.
- */
-
-var warning = emptyFunction;
-
-if ("development" !== 'production') {
- var printWarning = function printWarning(format) {
- for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
- args[_key - 1] = arguments[_key];
- }
-
- var argIndex = 0;
- var message = 'Warning: ' + format.replace(/%s/g, function () {
- return args[argIndex++];
- });
- if (typeof console !== 'undefined') {
- console.error(message);
- }
- try {
- // --- Welcome to debugging React ---
- // This error was thrown as a convenience so that you can use this stack
- // to find the callsite that caused this warning to fire.
- throw new Error(message);
- } catch (x) {}
- };
-
- warning = function warning(condition, format) {
- if (format === undefined) {
- throw new Error('`warning(condition, format, ...args)` requires a warning ' + 'message argument');
- }
-
- if (format.indexOf('Failed Composite propType: ') === 0) {
- return; // Ignore CompositeComponent proptype check.
- }
-
- if (!condition) {
- for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
- args[_key2 - 2] = arguments[_key2];
- }
-
- printWarning.apply(undefined, [format].concat(args));
- }
- };
-}
-
-module.exports = warning;
-},{"./emptyFunction":4}],9:[function(require,module,exports){
-/*
-object-assign
-(c) Sindre Sorhus
-@license MIT
-*/
-
-'use strict';
-/* eslint-disable no-unused-vars */
-var getOwnPropertySymbols = Object.getOwnPropertySymbols;
-var hasOwnProperty = Object.prototype.hasOwnProperty;
-var propIsEnumerable = Object.prototype.propertyIsEnumerable;
-
-function toObject(val) {
- if (val === null || val === undefined) {
- throw new TypeError('Object.assign cannot be called with null or undefined');
- }
-
- return Object(val);
-}
-
-function shouldUseNative() {
- try {
- if (!Object.assign) {
- return false;
- }
-
- // Detect buggy property enumeration order in older V8 versions.
-
- // https://bugs.chromium.org/p/v8/issues/detail?id=4118
- var test1 = new String('abc'); // eslint-disable-line no-new-wrappers
- test1[5] = 'de';
- if (Object.getOwnPropertyNames(test1)[0] === '5') {
- return false;
- }
-
- // https://bugs.chromium.org/p/v8/issues/detail?id=3056
- var test2 = {};
- for (var i = 0; i < 10; i++) {
- test2['_' + String.fromCharCode(i)] = i;
- }
- var order2 = Object.getOwnPropertyNames(test2).map(function (n) {
- return test2[n];
- });
- if (order2.join('') !== '0123456789') {
- return false;
- }
-
- // https://bugs.chromium.org/p/v8/issues/detail?id=3056
- var test3 = {};
- 'abcdefghijklmnopqrst'.split('').forEach(function (letter) {
- test3[letter] = letter;
- });
- if (Object.keys(Object.assign({}, test3)).join('') !==
- 'abcdefghijklmnopqrst') {
- return false;
- }
-
- return true;
- } catch (err) {
- // We don't expect any of the above to throw, but better to be safe.
- return false;
- }
-}
-
-module.exports = shouldUseNative() ? Object.assign : function (target, source) {
- var from;
- var to = toObject(target);
- var symbols;
-
- for (var s = 1; s < arguments.length; s++) {
- from = Object(arguments[s]);
-
- for (var key in from) {
- if (hasOwnProperty.call(from, key)) {
- to[key] = from[key];
- }
- }
-
- if (getOwnPropertySymbols) {
- symbols = getOwnPropertySymbols(from);
- for (var i = 0; i < symbols.length; i++) {
- if (propIsEnumerable.call(from, symbols[i])) {
- to[symbols[i]] = from[symbols[i]];
- }
- }
- }
- }
-
- return to;
-};
-
-},{}],10:[function(require,module,exports){
-/**
- * Copyright (c) 2013-present, Facebook, Inc.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-'use strict';
-
-if ("development" !== 'production') {
- var invariant = require('fbjs/lib/invariant');
- var warning = require('fbjs/lib/warning');
- var ReactPropTypesSecret = require('./lib/ReactPropTypesSecret');
- var loggedTypeFailures = {};
-}
-
-/**
- * Assert that the values match with the type specs.
- * Error messages are memorized and will only be shown once.
- *
- * @param {object} typeSpecs Map of name to a ReactPropType
- * @param {object} values Runtime values that need to be type-checked
- * @param {string} location e.g. "prop", "context", "child context"
- * @param {string} componentName Name of the component for error messages.
- * @param {?Function} getStack Returns the component stack.
- * @private
- */
-function checkPropTypes(typeSpecs, values, location, componentName, getStack) {
- if ("development" !== 'production') {
- for (var typeSpecName in typeSpecs) {
- if (typeSpecs.hasOwnProperty(typeSpecName)) {
- var error;
- // Prop type validation may throw. In case they do, we don't want to
- // fail the render phase where it didn't fail before. So we log it.
- // After these have been cleaned up, we'll let them throw.
- try {
- // This is intentionally an invariant that gets caught. It's the same
- // behavior as without this statement except with a better message.
- invariant(typeof typeSpecs[typeSpecName] === 'function', '%s: %s type `%s` is invalid; it must be a function, usually from ' + 'the `prop-types` package, but received `%s`.', componentName || 'React class', location, typeSpecName, typeof typeSpecs[typeSpecName]);
- error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret);
- } catch (ex) {
- error = ex;
- }
- warning(!error || error instanceof Error, '%s: type specification of %s `%s` is invalid; the type checker ' + 'function must return `null` or an `Error` but returned a %s. ' + 'You may have forgotten to pass an argument to the type checker ' + 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' + 'shape all require an argument).', componentName || 'React class', location, typeSpecName, typeof error);
- if (error instanceof Error && !(error.message in loggedTypeFailures)) {
- // Only monitor this failure once because there tends to be a lot of the
- // same error.
- loggedTypeFailures[error.message] = true;
-
- var stack = getStack ? getStack() : '';
-
- warning(false, 'Failed %s type: %s%s', location, error.message, stack != null ? stack : '');
- }
- }
- }
- }
-}
-
-module.exports = checkPropTypes;
-
-},{"./lib/ReactPropTypesSecret":11,"fbjs/lib/invariant":6,"fbjs/lib/warning":8}],11:[function(require,module,exports){
-/**
- * Copyright (c) 2013-present, Facebook, Inc.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-'use strict';
-
-var ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';
-
-module.exports = ReactPropTypesSecret;
-
-},{}]},{},[3])(3)
-});
\ No newline at end of file
+})));
diff --git a/devtools/client/shared/vendor/react.js b/devtools/client/shared/vendor/react.js
index a9e3e4ab54be..f6a2ae65f813 100644
--- a/devtools/client/shared/vendor/react.js
+++ b/devtools/client/shared/vendor/react.js
@@ -1,4 +1,4 @@
-/** @license React v16.2.0
+/** @license React v16.4.1
* react.production.min.js
*
* Copyright (c) 2013-present, Facebook, Inc.
@@ -105,17 +105,22 @@ var objectAssign = shouldUseNative() ? Object.assign : function (target, source)
// TODO: this is special because it gets imported during build.
-var ReactVersion = '16.2.0';
+var ReactVersion = '16.4.1';
// The Symbol used to tag the ReactElement-like types. If there is no native Symbol
// nor polyfill, then a plain number is used for performance.
-var hasSymbol = typeof Symbol === 'function' && Symbol['for'];
+var hasSymbol = typeof Symbol === 'function' && Symbol.for;
-var REACT_ELEMENT_TYPE = hasSymbol ? Symbol['for']('react.element') : 0xeac7;
-var REACT_CALL_TYPE = hasSymbol ? Symbol['for']('react.call') : 0xeac8;
-var REACT_RETURN_TYPE = hasSymbol ? Symbol['for']('react.return') : 0xeac9;
-var REACT_PORTAL_TYPE = hasSymbol ? Symbol['for']('react.portal') : 0xeaca;
-var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol['for']('react.fragment') : 0xeacb;
+var REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for('react.element') : 0xeac7;
+var REACT_PORTAL_TYPE = hasSymbol ? Symbol.for('react.portal') : 0xeaca;
+var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol.for('react.fragment') : 0xeacb;
+var REACT_STRICT_MODE_TYPE = hasSymbol ? Symbol.for('react.strict_mode') : 0xeacc;
+var REACT_PROFILER_TYPE = hasSymbol ? Symbol.for('react.profiler') : 0xead2;
+var REACT_PROVIDER_TYPE = hasSymbol ? Symbol.for('react.provider') : 0xeacd;
+var REACT_CONTEXT_TYPE = hasSymbol ? Symbol.for('react.context') : 0xeace;
+var REACT_ASYNC_MODE_TYPE = hasSymbol ? Symbol.for('react.async_mode') : 0xeacf;
+var REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0;
+var REACT_TIMEOUT_TYPE = hasSymbol ? Symbol.for('react.timeout') : 0xead1;
var MAYBE_ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
var FAUX_ITERATOR_SYMBOL = '@@iterator';
@@ -131,6 +136,39 @@ function getIteratorFn(maybeIterable) {
return null;
}
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ */
+
+
+
+function invariant(condition, format, a, b, c, d, e, f) {
+ if (!condition) {
+ var error;
+ if (format === undefined) {
+ error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
+ } else {
+ var args = [a, b, c, d, e, f];
+ var argIndex = 0;
+ error = new Error(format.replace(/%s/g, function () {
+ return args[argIndex++];
+ }));
+ error.name = 'Invariant Violation';
+ }
+
+ error.framesToPop = 1; // we don't care about invariant's own frame
+ throw error;
+ }
+}
+
+var invariant_1 = invariant;
+
+// Relying on the `invariant()` implementation lets us
+// have preserve the format and params in the www builds.
/**
* WARNING: DO NOT manually require this module.
* This is a replacement for `invariant(...)` used by the error code system
@@ -139,22 +177,52 @@ function getIteratorFn(maybeIterable) {
*/
function reactProdInvariant(code) {
var argCount = arguments.length - 1;
-
- var message = 'Minified React error #' + code + '; visit ' + 'http://facebook.github.io/react/docs/error-decoder.html?invariant=' + code;
-
+ var url = 'https://reactjs.org/docs/error-decoder.html?invariant=' + code;
for (var argIdx = 0; argIdx < argCount; argIdx++) {
- message += '&args[]=' + encodeURIComponent(arguments[argIdx + 1]);
+ url += '&args[]=' + encodeURIComponent(arguments[argIdx + 1]);
}
-
- message += ' for the full message or use the non-minified dev environment' + ' for full errors and additional helpful warnings.';
-
- var error = new Error(message);
- error.name = 'Invariant Violation';
- error.framesToPop = 1; // we don't care about reactProdInvariant's own frame
-
- throw error;
+ // Rename it so that our build transform doesn't atttempt
+ // to replace this invariant() call with reactProdInvariant().
+ var i = invariant_1;
+ i(false,
+ // The error code is intentionally part of the message (and
+ // not the format argument) so that we could deduplicate
+ // different errors in logs based on the code.
+ 'Minified React error #' + code + '; visit %s ' + 'for the full message or use the non-minified dev environment ' + 'for full errors and additional helpful warnings. ', url);
}
+// Exports ReactDOM.createRoot
+
+
+// Experimental error-boundary API that can recover from errors within a single
+// render phase
+
+// Suspense
+var enableSuspense = false;
+// Helps identify side effects in begin-phase lifecycle hooks and setState reducers:
+
+
+// In some cases, StrictMode should also double-render lifecycles.
+// This can be confusing for tests though,
+// And it can be bad for performance in production.
+// This feature flag can be used to control the behavior:
+
+
+// To preserve the "Pause on caught exceptions" behavior of the debugger, we
+// replay the begin phase of a failed component inside invokeGuardedCallback.
+
+
+// Warn about deprecated, async-unsafe lifecycles; relates to RFC #6:
+
+
+// Warn about legacy context API
+
+
+// Gather advanced timing metrics for Profiler subtrees.
+
+
+// Only used in www builds.
+
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
@@ -169,14 +237,6 @@ var emptyObject = {};
var emptyObject_1 = emptyObject;
-/**
- * Copyright (c) 2013-present, Facebook, Inc.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- */
-
/**
* Forked from fbjs/warning:
* https://github.com/facebook/fbjs/blob/e66ba20ad5be433eb54423f2b097d829324d9de6/packages/fbjs/src/__forks__/warning.js
@@ -369,46 +429,33 @@ Component.prototype.forceUpdate = function (callback) {
* we would like to deprecate them, we're not going to move them over to this
* modern base class. Instead, we define a getter that warns if it's accessed.
*/
+function ComponentDummy() {}
+ComponentDummy.prototype = Component.prototype;
+
/**
- * Base class helpers for the updating state of a component.
+ * Convenience component with default shallow equality check for sCU.
*/
function PureComponent(props, context, updater) {
- // Duplicated from Component.
this.props = props;
this.context = context;
this.refs = emptyObject_1;
- // We initialize the default updater but the real one gets injected by the
- // renderer.
this.updater = updater || ReactNoopUpdateQueue;
}
-function ComponentDummy() {}
-ComponentDummy.prototype = Component.prototype;
var pureComponentPrototype = PureComponent.prototype = new ComponentDummy();
pureComponentPrototype.constructor = PureComponent;
// Avoid an extra prototype jump for these methods.
objectAssign(pureComponentPrototype, Component.prototype);
pureComponentPrototype.isPureReactComponent = true;
-function AsyncComponent(props, context, updater) {
- // Duplicated from Component.
- this.props = props;
- this.context = context;
- this.refs = emptyObject_1;
- // We initialize the default updater but the real one gets injected by the
- // renderer.
- this.updater = updater || ReactNoopUpdateQueue;
+// an immutable object with a single mutable value
+function createRef() {
+ var refObject = {
+ current: null
+ };
+ return refObject;
}
-var asyncComponentPrototype = AsyncComponent.prototype = new ComponentDummy();
-asyncComponentPrototype.constructor = AsyncComponent;
-// Avoid an extra prototype jump for these methods.
-objectAssign(asyncComponentPrototype, Component.prototype);
-asyncComponentPrototype.unstable_isAsyncReactComponent = true;
-asyncComponentPrototype.render = function () {
- return this.props.children;
-};
-
/**
* Keeps track of the current owner.
*
@@ -462,7 +509,7 @@ function hasValidKey(config) {
*/
var ReactElement = function (type, key, ref, self, source, owner, props) {
var element = {
- // This tag allow us to uniquely identify this as a React Element
+ // This tag allows us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE,
// Built-in properties that belong on the element
@@ -483,7 +530,7 @@ var ReactElement = function (type, key, ref, self, source, owner, props) {
* See https://reactjs.org/docs/react-api.html#createelement
*/
function createElement(type, config, children) {
- var propName;
+ var propName = void 0;
// Reserved names are extracted
var props = {};
@@ -546,7 +593,7 @@ function createFactory(type) {
// easily accessed on elements. E.g. `.type === Foo`.
// This should not be named `constructor` since this may not be the function
// that created the element, and it may not even be a constructor.
- // Legacy hook TODO: Warn if this is accessed
+ // Legacy hook: remove it
factory.type = type;
return factory;
}
@@ -562,7 +609,9 @@ function cloneAndReplaceKey(oldElement, newKey) {
* See https://reactjs.org/docs/react-api.html#cloneelement
*/
function cloneElement(element, config, children) {
- var propName;
+ !!(element === null || element === undefined) ? reactProdInvariant('267', element) : void 0;
+
+ var propName = void 0;
// Original props are copied
var props = objectAssign({}, element.props);
@@ -591,7 +640,7 @@ function cloneElement(element, config, children) {
}
// Remaining properties override existing props
- var defaultProps;
+ var defaultProps = void 0;
if (element.type && element.type.defaultProps) {
defaultProps = element.type.defaultProps;
}
@@ -723,8 +772,6 @@ function traverseAllChildrenImpl(children, nameSoFar, callback, traverseContext)
case 'object':
switch (children.$$typeof) {
case REACT_ELEMENT_TYPE:
- case REACT_CALL_TYPE:
- case REACT_RETURN_TYPE:
case REACT_PORTAL_TYPE:
invokeCallback = true;
}
@@ -739,8 +786,8 @@ function traverseAllChildrenImpl(children, nameSoFar, callback, traverseContext)
return 1;
}
- var child;
- var nextName;
+ var child = void 0;
+ var nextName = void 0;
var subtreeCount = 0; // Count of children found in the current subtree.
var nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;
@@ -754,7 +801,7 @@ function traverseAllChildrenImpl(children, nameSoFar, callback, traverseContext)
var iteratorFn = getIteratorFn(children);
if (typeof iteratorFn === 'function') {
var iterator = iteratorFn.call(children);
- var step;
+ var step = void 0;
var ii = 0;
while (!(step = iterator.next()).done) {
child = step.value;
@@ -823,7 +870,7 @@ function forEachSingleChild(bookKeeping, child, name) {
/**
* Iterates through children that are typically specified as `props.children`.
*
- * See https://reactjs.org/docs/react-api.html#react.children.foreach
+ * See https://reactjs.org/docs/react-api.html#reactchildrenforeach
*
* The provided forEachFunc(child, index) will be called for each
* leaf child.
@@ -875,7 +922,7 @@ function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
/**
* Maps children that are typically specified as `props.children`.
*
- * See https://reactjs.org/docs/react-api.html#react.children.map
+ * See https://reactjs.org/docs/react-api.html#reactchildrenmap
*
* The provided mapFunction(child, key, index) will be called for each
* leaf child.
@@ -898,12 +945,12 @@ function mapChildren(children, func, context) {
* Count the number of children that are typically specified as
* `props.children`.
*
- * See https://reactjs.org/docs/react-api.html#react.children.count
+ * See https://reactjs.org/docs/react-api.html#reactchildrencount
*
* @param {?*} children Children tree container.
* @return {number} The number of children.
*/
-function countChildren(children, context) {
+function countChildren(children) {
return traverseAllChildren(children, emptyFunction_1.thatReturnsNull, null);
}
@@ -911,7 +958,7 @@ function countChildren(children, context) {
* Flatten a children object (typically specified as `props.children`) and
* return an array with appropriately re-keyed children.
*
- * See https://reactjs.org/docs/react-api.html#react.children.toarray
+ * See https://reactjs.org/docs/react-api.html#reactchildrentoarray
*/
function toArray(children) {
var result = [];
@@ -923,7 +970,7 @@ function toArray(children) {
* Returns the first child in a collection of children and verifies that there
* is only one child in the collection.
*
- * See https://reactjs.org/docs/react-api.html#react.children.only
+ * See https://reactjs.org/docs/react-api.html#reactchildrenonly
*
* The current implementation of this function assumes that a single child gets
* passed without a wrapper, but the purpose of this helper function is to
@@ -938,6 +985,47 @@ function onlyChild(children) {
return children;
}
+function createContext(defaultValue, calculateChangedBits) {
+ if (calculateChangedBits === undefined) {
+ calculateChangedBits = null;
+ } else {
+
+ }
+
+ var context = {
+ $$typeof: REACT_CONTEXT_TYPE,
+ _calculateChangedBits: calculateChangedBits,
+ _defaultValue: defaultValue,
+ _currentValue: defaultValue,
+ // As a workaround to support multiple concurrent renderers, we categorize
+ // some renderers as primary and others as secondary. We only expect
+ // there to be two concurrent renderers at most: React Native (primary) and
+ // Fabric (secondary); React DOM (primary) and React ART (secondary).
+ // Secondary renderers store their context values on separate fields.
+ _currentValue2: defaultValue,
+ _changedBits: 0,
+ _changedBits2: 0,
+ // These are circular
+ Provider: null,
+ Consumer: null
+ };
+
+ context.Provider = {
+ $$typeof: REACT_PROVIDER_TYPE,
+ _context: context
+ };
+ context.Consumer = context;
+
+ return context;
+}
+
+function forwardRef(render) {
+ return {
+ $$typeof: REACT_FORWARD_REF_TYPE,
+ render: render
+ };
+}
+
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
@@ -961,11 +1049,17 @@ var React = {
only: onlyChild
},
+ createRef: createRef,
Component: Component,
PureComponent: PureComponent,
- unstable_AsyncComponent: AsyncComponent,
+
+ createContext: createContext,
+ forwardRef: forwardRef,
Fragment: REACT_FRAGMENT_TYPE,
+ StrictMode: REACT_STRICT_MODE_TYPE,
+ unstable_AsyncMode: REACT_ASYNC_MODE_TYPE,
+ unstable_Profiler: REACT_PROFILER_TYPE,
createElement: createElement,
cloneElement: cloneElement,
@@ -981,9 +1075,13 @@ var React = {
}
};
+if (enableSuspense) {
+ React.Timeout = REACT_TIMEOUT_TYPE;
+}
-var React$2 = Object.freeze({
+
+var React$2 = ({
default: React
});
@@ -991,7 +1089,7 @@ var React$3 = ( React$2 && React ) || React$2;
// TODO: decide on the top-level export form.
// This is hacky but makes it work with both Rollup and Jest.
-var react = React$3['default'] ? React$3['default'] : React$3;
+var react = React$3.default ? React$3.default : React$3;
return react;
diff --git a/devtools/client/webconsole/components/App.js b/devtools/client/webconsole/components/App.js
index bdf61ba7e6be..bdc10421e073 100644
--- a/devtools/client/webconsole/components/App.js
+++ b/devtools/client/webconsole/components/App.js
@@ -79,12 +79,12 @@ class App extends Component {
return;
}
- const inputField = this.node.querySelector(".jsterm-input-node");
+ const input = event.target;
// Cleanup function if notification is closed by the user.
const removeCallback = (eventType) => {
if (eventType == "removed") {
- inputField.removeEventListener("keyup", pasteKeyUpHandler);
+ input.removeEventListener("keyup", pasteKeyUpHandler);
dispatch(actions.removeNotification("selfxss-notification"));
}
};
@@ -99,18 +99,17 @@ class App extends Component {
removeCallback
));
- // Remove notification automatically when the user
- // types "allow pasting".
- function pasteKeyUpHandler() {
- const value = inputField.value || inputField.textContent;
+ // Remove notification automatically when the user types "allow pasting".
+ const pasteKeyUpHandler = (e) => {
+ const value = e.target.value;
if (value.includes(SELF_XSS_OK)) {
dispatch(actions.removeNotification("selfxss-notification"));
- inputField.removeEventListener("keyup", pasteKeyUpHandler);
+ input.removeEventListener("keyup", pasteKeyUpHandler);
WebConsoleUtils.usageCount = WebConsoleUtils.CONSOLE_ENTRY_THRESHOLD;
}
- }
+ };
- inputField.addEventListener("keyup", pasteKeyUpHandler);
+ input.addEventListener("keyup", pasteKeyUpHandler);
}
// Rendering
diff --git a/devtools/client/webconsole/components/JSTerm.js b/devtools/client/webconsole/components/JSTerm.js
index b18418b48fbe..c19502f5d942 100644
--- a/devtools/client/webconsole/components/JSTerm.js
+++ b/devtools/client/webconsole/components/JSTerm.js
@@ -277,7 +277,11 @@ class JSTerm extends Component {
}
}
});
+
this.editor.appendToLocalElement(this.node);
+ const cm = this.editor.codeMirror;
+ cm.on("paste", (_, event) => this.props.onPaste(event));
+ cm.on("drop", (_, event) => this.props.onPaste(event));
}
} else if (this.inputNode) {
this.inputNode.addEventListener("keypress", this._keyPress);
diff --git a/devtools/client/webconsole/test/mochitest/browser.ini b/devtools/client/webconsole/test/mochitest/browser.ini
index 7da0c2b070d2..195f58fb39a2 100644
--- a/devtools/client/webconsole/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/test/mochitest/browser.ini
@@ -194,6 +194,7 @@ skip-if = verify
[browser_jsterm_autocomplete_inside_text.js]
[browser_jsterm_autocomplete_native_getters.js]
[browser_jsterm_autocomplete_nav_and_tab_key.js]
+[browser_jsterm_autocomplete_paste_undo.js]
[browser_jsterm_autocomplete_return_key_no_selection.js]
[browser_jsterm_autocomplete_return_key.js]
[browser_jsterm_autocomplete-properties-with-non-alphanumeric-names.js]
diff --git a/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_paste_undo.js b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_paste_undo.js
new file mode 100644
index 000000000000..006f3ac34fb9
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_paste_undo.js
@@ -0,0 +1,61 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,test for bug 642615
";
+
+XPCOMUtils.defineLazyServiceGetter(
+ this,
+ "clipboardHelper",
+ "@mozilla.org/widget/clipboardhelper;1",
+ "nsIClipboardHelper"
+);
+const stringToCopy = "foobazbarBug642615";
+
+add_task(async function() {
+ const {jsterm, ui} = await openNewTabAndConsole(TEST_URI);
+ ui.clearOutput();
+ ok(!jsterm.completeNode.value, "no completeNode.value");
+
+ jsterm.setInputValue("doc");
+
+ info("wait for completion value after typing 'docu'");
+ let onAutocompleteUpdated = jsterm.once("autocomplete-updated");
+ EventUtils.sendString("u");
+ await onAutocompleteUpdated;
+
+ const completionValue = jsterm.completeNode.value;
+
+ info(`Copy "${stringToCopy}" in clipboard`);
+ await waitForClipboardPromise(() =>
+ clipboardHelper.copyString(stringToCopy), stringToCopy);
+
+ jsterm.setInputValue("docu");
+ info("wait for completion update after clipboard paste");
+ onAutocompleteUpdated = jsterm.once("autocomplete-updated");
+ goDoCommand("cmd_paste");
+
+ await onAutocompleteUpdated;
+
+ ok(!jsterm.completeNode.value, "no completion value after paste");
+
+ info("wait for completion update after undo");
+ onAutocompleteUpdated = jsterm.once("autocomplete-updated");
+
+ goDoCommand("cmd_undo");
+
+ await onAutocompleteUpdated;
+
+ is(jsterm.completeNode.value, completionValue, "same completeNode.value after undo");
+
+ info("wait for completion update after clipboard paste (ctrl-v)");
+ onAutocompleteUpdated = jsterm.once("autocomplete-updated");
+
+ EventUtils.synthesizeKey("v", {accelKey: true});
+
+ await onAutocompleteUpdated;
+ ok(!jsterm.completeNode.value, "no completion value after paste (ctrl-v)");
+});
diff --git a/devtools/client/webconsole/test/mochitest/browser_jsterm_selfxss.js b/devtools/client/webconsole/test/mochitest/browser_jsterm_selfxss.js
index 2f6c38d38606..4b3121e38157 100644
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_selfxss.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_selfxss.js
@@ -5,7 +5,7 @@
"use strict";
-const TEST_URI = "data:text/html;charset=utf-8,test for bug 642615 & 994134
";
+const TEST_URI = "data:text/html;charset=utf-8,Test self-XSS protection
";
XPCOMUtils.defineLazyServiceGetter(
this,
@@ -14,90 +14,51 @@ XPCOMUtils.defineLazyServiceGetter(
"nsIClipboardHelper"
);
const WebConsoleUtils = require("devtools/client/webconsole/utils").Utils;
-const stringToCopy = "foobazbarBug642615";
+const stringToCopy = "EvilCommand";
add_task(async function() {
- await pushPref("devtools.selfxss.count", 0);
-
- const {jsterm, ui} = await openNewTabAndConsole(TEST_URI);
- ui.clearOutput();
- ok(!jsterm.completeNode.value, "no completeNode.value");
-
- jsterm.setInputValue("doc");
-
- info("wait for completion value after typing 'docu'");
- let onAutocompleteUpdated = jsterm.once("autocomplete-updated");
- EventUtils.sendString("u");
- await onAutocompleteUpdated;
-
- const completionValue = jsterm.completeNode.value;
-
- // Arguments: expected, setup.
- await waitForClipboardPromise(() =>
- clipboardHelper.copyString(stringToCopy), stringToCopy);
-
- await testSelfXss(jsterm);
-
- jsterm.setInputValue("docu");
- info("wait for completion update after clipboard paste");
- updateEditUIVisibility();
- onAutocompleteUpdated = jsterm.once("autocomplete-updated");
- goDoCommand("cmd_paste");
-
- await onAutocompleteUpdated;
-
- ok(!jsterm.completeNode.value, "no completion value after paste");
-
- info("wait for completion update after undo");
- onAutocompleteUpdated = jsterm.once("autocomplete-updated");
-
- goDoCommand("cmd_undo");
-
- await onAutocompleteUpdated;
-
- is(jsterm.completeNode.value, completionValue, "same completeNode.value after undo");
-
- info("wait for completion update after clipboard paste (ctrl-v)");
- onAutocompleteUpdated = jsterm.once("autocomplete-updated");
-
- EventUtils.synthesizeKey("v", {accelKey: true});
-
- await onAutocompleteUpdated;
- ok(!jsterm.completeNode.value, "no completion value after paste (ctrl-v)");
+ // Run test with legacy JsTerm
+ await performTest();
+ // And then run it with the CodeMirror-powered one.
+ await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+ await performTest();
});
-// Self xss prevention tests (bug 994134)
-async function testSelfXss(jsterm) {
+async function performTest() {
+ await pushPref("devtools.selfxss.count", 0);
+ const {jsterm} = await openNewTabAndConsole(TEST_URI);
+ const {document} = jsterm.hud;
+
info("Self-xss paste tests");
WebConsoleUtils.usageCount = 0;
is(WebConsoleUtils.usageCount, 0, "Test for usage count getter");
+
// Input some commands to check if usage counting is working
for (let i = 0; i <= 3; i++) {
- jsterm.setInputValue(i);
+ jsterm.setInputValue(i.toString());
jsterm.execute();
}
is(WebConsoleUtils.usageCount, 4, "Usage count incremented");
WebConsoleUtils.usageCount = 0;
- updateEditUIVisibility();
- const oldVal = jsterm.getInputValue();
+ info(`Copy "${stringToCopy}" in clipboard`);
+ await waitForClipboardPromise(() =>
+ clipboardHelper.copyString(stringToCopy), stringToCopy);
goDoCommand("cmd_paste");
- const notificationbox =
- jsterm.hud.document.getElementById("webconsole-notificationbox");
+ const notificationbox = document.getElementById("webconsole-notificationbox");
const notification = notificationbox.querySelector(".notification");
is(notification.getAttribute("data-key"), "selfxss-notification",
"Self-xss notification shown");
- is(oldVal, jsterm.getInputValue(), "Paste blocked by self-xss prevention");
+ is(jsterm.getInputValue(), "", "Paste blocked by self-xss prevention");
// Allow pasting
- jsterm.setInputValue("allow pasting");
- const evt = document.createEvent("KeyboardEvent");
- evt.initKeyEvent("keyup", true, true, window,
- 0, 0, 0, 0,
- 0, " ".charCodeAt(0));
- jsterm.inputNode.dispatchEvent(evt);
+ const allowToken = "allow pasting";
+ for (const char of allowToken) {
+ EventUtils.sendString(char);
+ }
+
jsterm.setInputValue("");
goDoCommand("cmd_paste");
- is(stringToCopy, jsterm.getInputValue(), "Paste works");
+ is(jsterm.getInputValue(), stringToCopy, "Paste works");
}
diff --git a/devtools/server/actors/targets/browsing-context.js b/devtools/server/actors/targets/browsing-context.js
index 23f89b776c71..849b6c044833 100644
--- a/devtools/server/actors/targets/browsing-context.js
+++ b/devtools/server/actors/targets/browsing-context.js
@@ -686,7 +686,7 @@ const browsingContextTargetPrototype = {
_onWorkerTargetActorListChanged() {
this._workerTargetActorList.onListChanged = null;
- this.conn.sendActorEvent(this.actorID, "workerListChanged");
+ this.emit("workerListChanged");
},
observe(subject, topic, data) {
@@ -820,9 +820,7 @@ const browsingContextTargetPrototype = {
return;
}
- this.conn.send({
- from: this.actorID,
- type: "frameUpdate",
+ this.emit("frameUpdate", {
frames: windows
});
},
@@ -837,9 +835,7 @@ const browsingContextTargetPrototype = {
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.outerWindowID;
- this.conn.send({
- from: this.actorID,
- type: "frameUpdate",
+ this.emit("frameUpdate", {
frames: [{
id,
destroy: true
@@ -848,9 +844,7 @@ const browsingContextTargetPrototype = {
},
_notifyDocShellDestroyAll() {
- this.conn.send({
- from: this.actorID,
- type: "frameUpdate",
+ this.emit("frameUpdate", {
destroyAll: true
});
},
@@ -933,8 +927,7 @@ const browsingContextTargetPrototype = {
this._attached = false;
- this.conn.send({ from: this.actorID,
- type: "tabDetached" });
+ this.emit("tabDetached");
return true;
},
@@ -1257,9 +1250,7 @@ const browsingContextTargetPrototype = {
configurable: true
});
this.emit("changed-toplevel-document");
- this.conn.send({
- from: this.actorID,
- type: "frameUpdate",
+ this.emit("frameUpdate", {
selected: this.outerWindowID
});
},
@@ -1365,9 +1356,7 @@ const browsingContextTargetPrototype = {
}
threadActor.disableAllBreakpoints();
- this.conn.send({
- from: this.actorID,
- type: "tabNavigated",
+ this.emit("tabNavigated", {
url: newURI,
nativeConsoleAPI: true,
state: "start",
@@ -1407,9 +1396,7 @@ const browsingContextTargetPrototype = {
threadActor.dbg.enabled = true;
}
- this.conn.send({
- from: this.actorID,
- type: "tabNavigated",
+ this.emit("tabNavigated", {
url: this.url,
title: this.title,
nativeConsoleAPI: this.hasNativeConsoleAPI(this.window),
diff --git a/devtools/shared/specs/targets/browsing-context.js b/devtools/shared/specs/targets/browsing-context.js
index eeedc77a6de4..02f6a6875662 100644
--- a/devtools/shared/specs/targets/browsing-context.js
+++ b/devtools/shared/specs/targets/browsing-context.js
@@ -30,8 +30,9 @@ types.addDictType("browsingContextTarget.listframes", {
types.addDictType("browsingContextTarget.window", {
id: "string",
parentID: "nullable:string",
- url: "string",
- title: "string"
+ url: "nullable:string", // should be present if not destroying
+ title: "nullable:string", // should be present if not destroying
+ destroy: "nullable:boolean" // not present if not destroying
});
types.addDictType("browsingContextTarget.workers", {
@@ -110,6 +111,28 @@ const browsingContextTargetSpecPrototype = {
response: {}
}
},
+ events: {
+ tabNavigated: {
+ type: "tabNavigated",
+ url: Option(0, "string"),
+ title: Option(0, "string"),
+ nativeConsoleAPI: Option(0, "boolean"),
+ state: Option(0, "string"),
+ isFrameSwitching: Option(0, "boolean")
+ },
+ frameUpdate: {
+ type: "frameUpdate",
+ frames: Option(0, "nullable:array:browsingContextTarget.window"),
+ selected: Option(0, "nullable:number"),
+ destroyAll: Option(0, "nullable:boolean")
+ },
+ tabDetached: {
+ type: "tabDetached"
+ },
+ workerListChanged: {
+ type: "workerListChanged"
+ }
+ }
};
const browsingContextTargetSpec = generateActorSpec(browsingContextTargetSpecPrototype);
diff --git a/devtools/shared/webconsole/network-monitor.js b/devtools/shared/webconsole/network-monitor.js
index 8aab780b1600..9d0f232b6164 100644
--- a/devtools/shared/webconsole/network-monitor.js
+++ b/devtools/shared/webconsole/network-monitor.js
@@ -1251,6 +1251,7 @@ NetworkMonitor.prototype = {
charset: charset,
sentBody: null,
url: channel.URI.spec,
+ headersSize: null,
// needed for host specific security info
hostname: channel.URI.host,
discardRequestBody: !this.saveRequestAndResponseBodies,
diff --git a/dom/base/ChromeUtils.cpp b/dom/base/ChromeUtils.cpp
index b108c2f782f3..39f99cd9a6cf 100644
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -12,7 +12,7 @@
#include "mozilla/Base64.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/CycleCollectedJSRuntime.h"
-#include "mozilla/PerformanceUtils.h"
+#include "mozilla/PerformanceMetricsCollector.h"
#include "mozilla/Preferences.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/dom/ContentParent.h"
@@ -655,29 +655,27 @@ ChromeUtils::ClearRecentJSDevError(GlobalObject&)
}
#endif // NIGHTLY_BUILD
-/* static */ void
-ChromeUtils::RequestPerformanceMetrics(GlobalObject&)
+/* static */
+already_AddRefed
+ChromeUtils::RequestPerformanceMetrics(GlobalObject& aGlobal,
+ ErrorResult& aRv)
{
MOZ_ASSERT(XRE_IsParentProcess());
- // calling all content processes via IPDL (async)
- nsTArray children;
- ContentParent::GetAll(children);
- for (uint32_t i = 0; i < children.Length(); i++) {
- mozilla::Unused << children[i]->SendRequestPerformanceMetrics();
+ // Creating a promise
+ nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports());
+ MOZ_ASSERT(global);
+ RefPtr domPromise = Promise::Create(global, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
}
+ MOZ_ASSERT(domPromise);
+ // requesting metrics, that will be returned into the promise
+ PerformanceMetricsCollector::RequestMetrics(domPromise);
- // collecting the current process counters and notifying them
- nsTArray info;
- CollectPerformanceInfo(info);
- SystemGroup::Dispatch(TaskCategory::Performance,
- NS_NewRunnableFunction(
- "RequestPerformanceMetrics",
- [info]() { mozilla::Unused << NS_WARN_IF(NS_FAILED(NotifyPerformanceInfo(info))); }
- )
- );
-
+ // sending back the promise instance
+ return domPromise.forget();
}
constexpr auto kSkipSelfHosted = JS::SavedFrameSelfHosted::Exclude;
@@ -769,12 +767,20 @@ ChromeUtils::CreateError(const GlobalObject& aGlobal, const nsAString& aMessage,
aRetVal.set(retVal);
}
-/* static */ void
-ChromeUtils::RequestIOActivity(GlobalObject&)
+/* static */ already_AddRefed
+ChromeUtils::RequestIOActivity(GlobalObject& aGlobal, ErrorResult& aRv)
{
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(Preferences::GetBool(IO_ACTIVITY_ENABLED_PREF, false));
- mozilla::Unused << mozilla::net::IOActivityMonitor::NotifyActivities();
+ nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports());
+ MOZ_ASSERT(global);
+ RefPtr domPromise = Promise::Create(global, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+ MOZ_ASSERT(domPromise);
+ mozilla::net::IOActivityMonitor::RequestActivities(domPromise);
+ return domPromise.forget();
}
} // namespace dom
diff --git a/dom/base/ChromeUtils.h b/dom/base/ChromeUtils.h
index 5a31bed0f915..cbc7fe244c16 100644
--- a/dom/base/ChromeUtils.h
+++ b/dom/base/ChromeUtils.h
@@ -160,7 +160,9 @@ public:
static void ClearRecentJSDevError(GlobalObject& aGlobal);
- static void RequestPerformanceMetrics(GlobalObject& aGlobal);
+ static already_AddRefed
+ RequestPerformanceMetrics(GlobalObject& aGlobal,
+ ErrorResult& aRv);
static void Import(const GlobalObject& aGlobal,
const nsAString& aResourceURI,
@@ -183,7 +185,8 @@ public:
JS::Handle stack,
JS::MutableHandle aRetVal, ErrorResult& aRv);
- static void RequestIOActivity(GlobalObject& aGlobal);
+ static already_AddRefed
+ RequestIOActivity(GlobalObject& aGlobal, ErrorResult& aRv);
};
} // namespace dom
diff --git a/dom/base/moz.build b/dom/base/moz.build
index b8218addb9df..e035d6922394 100644
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -22,7 +22,6 @@ XPIDL_SOURCES += [
'nsIImageLoadingContent.idl',
'nsIMessageManager.idl',
'nsIObjectLoadingContent.idl',
- 'nsIPerformanceMetrics.idl',
'nsIRemoteWindowContext.idl',
'nsIScriptChannel.idl',
'nsISelectionController.idl',
@@ -98,7 +97,6 @@ EXPORTS += [
'nsNameSpaceManager.h',
'nsNodeInfoManager.h',
'nsNodeUtils.h',
- 'nsPerformanceMetrics.h',
'nsPIDOMWindow.h',
'nsPIDOMWindowInlines.h',
'nsPIWindowRoot.h',
@@ -330,7 +328,6 @@ UNIFIED_SOURCES += [
'nsNodeInfoManager.cpp',
'nsNodeUtils.cpp',
'nsOpenURIInFrameParams.cpp',
- 'nsPerformanceMetrics.cpp',
'nsPlainTextSerializer.cpp',
'nsPropertyTable.cpp',
'nsQueryContentEventResult.cpp',
diff --git a/dom/base/nsIPerformanceMetrics.idl b/dom/base/nsIPerformanceMetrics.idl
deleted file mode 100644
index 2cbaa435a48d..000000000000
--- a/dom/base/nsIPerformanceMetrics.idl
+++ /dev/null
@@ -1,49 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-*/
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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 "nsISupports.idl"
-#include "nsIArray.idl"
-
-/*
- * nsIPerformanceMetricsData is used to store performance data collected
- * in all content processes by nsThread and nsWorkerThread.
- *
- * Each (host, category, pid, wid, pwid) is unique to a given DocGroup or
- * Worker, and we collect the number of dispatches and execution duration.
- *
- * This XPCOM interface reflects the data collected in Performance counters.
- * see xpcom/threads/PerformanceCounter.h
- */
-[scriptable, builtinclass, uuid(1f9a58c9-be37-4463-8996-c7f5b9a5bef8)]
-interface nsIPerformanceMetricsDispatchCategory : nsISupports
-{
- // DispatchCategory value
- readonly attribute unsigned long category;
- // Number of dispatch.
- readonly attribute unsigned long count;
-};
-
-
-[scriptable, builtinclass, uuid(02b0cdc6-4be2-4154-a8a9-e8d462073200)]
-interface nsIPerformanceMetricsData : nsISupports
-{
- // Host of the document, if any
- readonly attribute AUTF8String host;
- // process id
- readonly attribute unsigned long pid;
- // window id
- readonly attribute unsigned long long wid;
- // "parent" window id
- readonly attribute unsigned long long pwid;
- // Execution time in microseconds
- readonly attribute unsigned long long duration;
- // True if the data is collected in a worker
- readonly attribute bool worker;
- // Dispatch Category counters
- readonly attribute nsIArray items;
-};
-
-
diff --git a/dom/base/nsPerformanceMetrics.cpp b/dom/base/nsPerformanceMetrics.cpp
deleted file mode 100644
index 4c1a1174d797..000000000000
--- a/dom/base/nsPerformanceMetrics.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* 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
-#include
-#include
-#include "nsComponentManagerUtils.h"
-
-/* ------------------------------------------------------
- *
- * class PerformanceMetricsDispatchCategory
- *
- */
-
-PerformanceMetricsDispatchCategory::PerformanceMetricsDispatchCategory(uint32_t aCategory, uint32_t aCount)
- : mCategory(aCategory), mCount(aCount)
-{
-}
-
-NS_IMPL_ISUPPORTS(PerformanceMetricsDispatchCategory,
- nsIPerformanceMetricsDispatchCategory);
-
-
-NS_IMETHODIMP
-PerformanceMetricsDispatchCategory::GetCategory(uint32_t* aCategory)
-{
- *aCategory = mCategory;
- return NS_OK;
-};
-
-NS_IMETHODIMP
-PerformanceMetricsDispatchCategory::GetCount(uint32_t* aCount)
-{
- *aCount = mCount;
- return NS_OK;
-};
-
-/* ------------------------------------------------------
- *
- * class PerformanceMetricsData
- *
- */
-
-PerformanceMetricsData::PerformanceMetricsData(uint32_t aPid, uint64_t aWid,
- uint64_t aPwid, const nsCString& aHost,
- uint64_t aDuration, bool aWorker,
- nsIArray* aItems)
- : mPid(aPid), mWid(aWid), mPwid(aPwid), mHost(aHost)
- , mDuration(aDuration), mWorker(aWorker)
-{
- uint32_t len;
- nsresult rv = aItems->GetLength(&len);
- if (NS_FAILED(rv)) {
- NS_ASSERTION(rv == NS_OK, "Failed to ge the length");
- }
- for (uint32_t i = 0; i < len; i++) {
- nsCOMPtr item = do_QueryElementAt(aItems, i);
- mItems.AppendElement(item);
- }
-};
-
-NS_IMPL_ISUPPORTS(PerformanceMetricsData, nsIPerformanceMetricsData);
-
-NS_IMETHODIMP
-PerformanceMetricsData::GetHost(nsACString& aHost)
-{
- aHost = mHost;
- return NS_OK;
-};
-
-NS_IMETHODIMP
-PerformanceMetricsData::GetWorker(bool* aWorker)
-{
- *aWorker = mWorker;
- return NS_OK;
-};
-
-NS_IMETHODIMP
-PerformanceMetricsData::GetPid(uint32_t* aPid)
-{
- *aPid = mPid;
- return NS_OK;
-};
-
-NS_IMETHODIMP
-PerformanceMetricsData::GetWid(uint64_t* aWid)
-{
- *aWid = mWid;
- return NS_OK;
-};
-
-NS_IMETHODIMP
-PerformanceMetricsData::GetDuration(uint64_t* aDuration)
-{
- *aDuration = mDuration;
- return NS_OK;
-};
-
-NS_IMETHODIMP
-PerformanceMetricsData::GetPwid(uint64_t* aPwid)
-{
- *aPwid = mPwid;
- return NS_OK;
-};
-
-NS_IMETHODIMP
-PerformanceMetricsData::GetItems(nsIArray** aItems)
-{
- NS_ENSURE_ARG_POINTER(aItems);
- *aItems = nullptr;
-
- nsresult rv = NS_OK;
- nsCOMPtr items =
- do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
-
- uint32_t len = mItems.Length();
- for (uint32_t i = 0; i < len; i++) {
- items->AppendElement(mItems[i]);
- }
-
- items.forget(aItems);
- return NS_OK;
-}
diff --git a/dom/base/nsPerformanceMetrics.h b/dom/base/nsPerformanceMetrics.h
deleted file mode 100644
index 42088487822b..000000000000
--- a/dom/base/nsPerformanceMetrics.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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/. */
-
-#ifndef nsPerformanceMetrics_h___
-#define nsPerformanceMetrics_h___
-
-#include "nsCOMArray.h"
-#include "nsIPerformanceMetrics.h"
-#include "nsString.h"
-
-
-class PerformanceMetricsDispatchCategory final : public nsIPerformanceMetricsDispatchCategory
-{
-public:
- NS_DECL_ISUPPORTS
- NS_DECL_NSIPERFORMANCEMETRICSDISPATCHCATEGORY
- PerformanceMetricsDispatchCategory(uint32_t aCategory, uint32_t aCount);
-private:
- ~PerformanceMetricsDispatchCategory() = default;
-
- uint32_t mCategory;
- uint32_t mCount;
-};
-
-
-class PerformanceMetricsData final : public nsIPerformanceMetricsData
-{
-public:
- NS_DECL_ISUPPORTS
- NS_DECL_NSIPERFORMANCEMETRICSDATA
- PerformanceMetricsData(uint32_t aPid, uint64_t aWid, uint64_t aPwid, const nsCString& aHost,
- uint64_t aDuration, bool aWorker, nsIArray* aItems);
-private:
- ~PerformanceMetricsData() = default;
-
- uint32_t mPid;
- uint64_t mWid;
- uint64_t mPwid;
- nsCString mHost;
- uint64_t mDuration;
- bool mWorker;
- nsCOMArray mItems;
-};
-
-#endif // end nsPerformanceMetrics_h__
diff --git a/dom/chrome-webidl/ChromeUtils.webidl b/dom/chrome-webidl/ChromeUtils.webidl
index 50cab35174d8..0cbf44508f7f 100644
--- a/dom/chrome-webidl/ChromeUtils.webidl
+++ b/dom/chrome-webidl/ChromeUtils.webidl
@@ -345,14 +345,48 @@ partial namespace ChromeUtils {
object createError(DOMString message, optional object? stack = null);
/**
- * Request performance metrics to the current process & all ontent processes.
+ * Request performance metrics to the current process & all content processes.
*/
- void requestPerformanceMetrics();
+ [Throws]
+ Promise> requestPerformanceMetrics();
/**
- * Request IOActivityMonitor to send a notification containing I/O activity
+ * Returns a Promise containing a sequence of I/O activities
*/
- void requestIOActivity();
+ [Throws]
+ Promise> requestIOActivity();
+};
+
+/**
+ * Dictionaries duplicating IPDL types in dom/ipc/DOMTypes.ipdlh
+ * Used by requestPerformanceMetrics
+ */
+dictionary CategoryDispatchDictionary
+{
+ unsigned short category = 0;
+ unsigned short count = 0;
+};
+
+dictionary PerformanceInfoDictionary {
+ DOMString host = "";
+ unsigned long pid = 0;
+ unsigned long long wid = 0;
+ unsigned long long pwid = 0;
+ unsigned long long duration = 0;
+ boolean worker = false;
+ sequence items = [];
+};
+
+/**
+ * Used by requestIOActivity() to return the number of bytes
+ * that were read (rx) and/or written (tx) for a given location.
+ *
+ * Locations can be sockets or files.
+ */
+dictionary IOActivityDataDictionary {
+ ByteString location = "";
+ unsigned long long rx = 0;
+ unsigned long long tx = 0;
};
/**
diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp
index 8d9f61fdf625..18f12f8bef07 100644
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -71,6 +71,7 @@
#include "mozilla/net/NeckoChild.h"
#include "mozilla/net/CookieServiceChild.h"
#include "mozilla/net/CaptivePortalService.h"
+#include "mozilla/PerformanceMetricsCollector.h"
#include "mozilla/PerformanceUtils.h"
#include "mozilla/plugins/PluginInstanceParent.h"
#include "mozilla/plugins/PluginModuleParent.h"
@@ -1392,12 +1393,12 @@ ContentChild::GetResultForRenderingInitFailure(base::ProcessId aOtherPid)
}
mozilla::ipc::IPCResult
-ContentChild::RecvRequestPerformanceMetrics()
+ContentChild::RecvRequestPerformanceMetrics(const nsID& aID)
{
MOZ_ASSERT(mozilla::StaticPrefs::dom_performance_enable_scheduler_timing());
nsTArray info;
CollectPerformanceInfo(info);
- SendAddPerformanceMetrics(info);
+ SendAddPerformanceMetrics(aID, info);
return IPC_OK();
}
diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h
index d25222a5c088..fa92912ef532 100644
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -192,7 +192,7 @@ public:
nsTArray&& namespaces) override;
mozilla::ipc::IPCResult
- RecvRequestPerformanceMetrics() override;
+ RecvRequestPerformanceMetrics(const nsID& aID) override;
mozilla::ipc::IPCResult
RecvReinitRendering(
diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp
index e5dc011e9109..af2ad64ebe4c 100644
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -188,7 +188,7 @@
#include "ContentProcessManager.h"
#include "mozilla/dom/BlobURLProtocolHandler.h"
#include "mozilla/dom/ipc/StructuredCloneData.h"
-#include "mozilla/PerformanceUtils.h"
+#include "mozilla/PerformanceMetricsCollector.h"
#include "mozilla/psm/PSMContentListener.h"
#include "nsPluginHost.h"
#include "nsPluginTags.h"
@@ -3339,14 +3339,16 @@ ContentParent::RecvFinishMemoryReport(const uint32_t& aGeneration)
}
mozilla::ipc::IPCResult
-ContentParent::RecvAddPerformanceMetrics(nsTArray&& aMetrics)
+ContentParent::RecvAddPerformanceMetrics(const nsID& aID,
+ nsTArray&& aMetrics)
{
if (!mozilla::StaticPrefs::dom_performance_enable_scheduler_timing()) {
// The pref is off, we should not get a performance metrics from the content
// child
return IPC_OK();
}
- Unused << NS_WARN_IF(NS_FAILED(mozilla::NotifyPerformanceInfo(aMetrics)));
+ nsresult rv = PerformanceMetricsCollector::DataReceived(aID, aMetrics);
+ Unused << NS_WARN_IF(NS_FAILED(rv));
return IPC_OK();
}
diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h
index 5882837f1c74..3a660d62fe30 100644
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -856,7 +856,7 @@ private:
mozilla::ipc::IPCResult RecvAddMemoryReport(const MemoryReport& aReport) override;
mozilla::ipc::IPCResult RecvFinishMemoryReport(const uint32_t& aGeneration) override;
- mozilla::ipc::IPCResult RecvAddPerformanceMetrics(nsTArray&& aMetrics) override;
+ mozilla::ipc::IPCResult RecvAddPerformanceMetrics(const nsID& aID, nsTArray&& aMetrics) override;
virtual bool
DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*) override;
diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl
index 89485e0cca5f..df2390b8a20a 100644
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -408,7 +408,7 @@ child:
bool anonymize,
bool minimizeMemoryUsage,
MaybeFileDesc DMDFile);
- async RequestPerformanceMetrics();
+ async RequestPerformanceMetrics(nsID aID);
/**
* Communication between the PuppetBidiKeyboard and the actual
@@ -1150,7 +1150,7 @@ parent:
async BHRThreadHang(HangDetails aHangDetails);
- async AddPerformanceMetrics(PerformanceInfo[] aMetrics);
+ async AddPerformanceMetrics(nsID aID, PerformanceInfo[] aMetrics);
both:
async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
Principal aPrincipal, ClonedMessageData aData);
diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp
index 6a00175f5286..e1d8f067ccb7 100644
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -1471,6 +1471,19 @@ nsresult nsPluginInstanceOwner::DispatchFocusToPlugin(Event* aFocusEvent)
nsresult nsPluginInstanceOwner::ProcessKeyPress(Event* aKeyEvent)
{
+ // ProcessKeyPress() may be called twice with same eKeyPress event. One is
+ // by the event listener in the default event group and the other is by the
+ // event listener in the system event group. When this is called in the
+ // latter case and the event must be fired in the default event group too,
+ // we don't need to do nothing anymore.
+ // XXX Do we need to check whether the document is in chrome? In strictly
+ // speaking, it must be yes. However, our UI must not use plugin in
+ // chrome.
+ if (!aKeyEvent->WidgetEventPtr()->mFlags.mOnlySystemGroupDispatchInContent &&
+ aKeyEvent->WidgetEventPtr()->mFlags.mInSystemGroup) {
+ return NS_OK;
+ }
+
#ifdef XP_MACOSX
return DispatchKeyToPlugin(aKeyEvent);
#else
@@ -2548,6 +2561,7 @@ nsPluginInstanceOwner::Destroy()
content->RemoveEventListener(NS_LITERAL_STRING("mouseover"), this, false);
content->RemoveEventListener(NS_LITERAL_STRING("mouseout"), this, false);
content->RemoveEventListener(NS_LITERAL_STRING("keypress"), this, true);
+ content->RemoveSystemEventListener(NS_LITERAL_STRING("keypress"), this, true);
content->RemoveEventListener(NS_LITERAL_STRING("keydown"), this, true);
content->RemoveEventListener(NS_LITERAL_STRING("keyup"), this, true);
content->RemoveEventListener(NS_LITERAL_STRING("drop"), this, true);
@@ -2875,7 +2889,11 @@ nsresult nsPluginInstanceOwner::Init(nsIContent* aContent)
false);
aContent->AddEventListener(NS_LITERAL_STRING("mouseout"), this, false,
false);
+ // "keypress" event should be handled when it's in the default event group
+ // if the event is fired in content. Otherwise, it should be handled when
+ // it's in the system event group.
aContent->AddEventListener(NS_LITERAL_STRING("keypress"), this, true);
+ aContent->AddSystemEventListener(NS_LITERAL_STRING("keypress"), this, true);
aContent->AddEventListener(NS_LITERAL_STRING("keydown"), this, true);
aContent->AddEventListener(NS_LITERAL_STRING("keyup"), this, true);
aContent->AddEventListener(NS_LITERAL_STRING("drop"), this, true);
diff --git a/dom/tests/browser/browser_test_performance_metrics.js b/dom/tests/browser/browser_test_performance_metrics.js
index d38de25f8574..01a9c81094c3 100644
--- a/dom/tests/browser/browser_test_performance_metrics.js
+++ b/dom/tests/browser/browser_test_performance_metrics.js
@@ -68,12 +68,8 @@ add_task(async function test() {
let duration = 0;
let total = 0;
- function getInfoFromService(subject, topic, value) {
- subject = subject.QueryInterface(Ci.nsIMutableArray);
- let enumerator = subject.enumerate();
- while (enumerator.hasMoreElements()) {
- let entry = enumerator.getNext();
- entry = entry.QueryInterface(Ci.nsIPerformanceMetricsData);
+ function exploreResults(data) {
+ for (let entry of data) {
if (entry.pid == Services.appinfo.processID) {
parent_process_event = true;
}
@@ -83,12 +79,8 @@ add_task(async function test() {
} else {
duration += entry.duration;
}
- // let's look at the XPCOM data we got back
- let items = entry.items.QueryInterface(Ci.nsIMutableArray);
- let enumerator2 = items.enumerate();
- while (enumerator2.hasMoreElements()) {
- let item = enumerator2.getNext();
- item = item.QueryInterface(Ci.nsIPerformanceMetricsDispatchCategory);
+ // let's look at the data we got back
+ for (let item of entry.items) {
if (entry.worker) {
worker_total += item.count;
} else {
@@ -98,17 +90,9 @@ add_task(async function test() {
}
}
- Services.obs.addObserver(getInfoFromService, "performance-metrics");
-
- // wait until we get some events back by triggering requestPerformanceMetrics
- await BrowserTestUtils.waitForCondition(() => {
- ChromeUtils.requestPerformanceMetrics();
- return worker_duration > 0 && duration > 0 && parent_process_event;
- }, "wait for events to come in", 250, 20);
-
- BrowserTestUtils.removeTab(page1);
- BrowserTestUtils.removeTab(page2);
- BrowserTestUtils.removeTab(page3);
+ // get all metrics via the promise
+ let results = await ChromeUtils.requestPerformanceMetrics();
+ exploreResults(results);
Assert.ok(worker_duration > 0, "Worker duration should be positive");
Assert.ok(worker_total > 0, "Worker count should be positive");
@@ -117,5 +101,8 @@ add_task(async function test() {
Assert.ok(parent_process_event, "parent process sent back some events");
});
+ BrowserTestUtils.removeTab(page1);
+ BrowserTestUtils.removeTab(page2);
+ BrowserTestUtils.removeTab(page3);
SpecialPowers.clearUserPref('dom.performance.enable_scheduler_timing');
});
diff --git a/dom/workers/WorkerDebugger.cpp b/dom/workers/WorkerDebugger.cpp
index cc69715a2b45..81ab25965d8d 100644
--- a/dom/workers/WorkerDebugger.cpp
+++ b/dom/workers/WorkerDebugger.cpp
@@ -478,6 +478,7 @@ PerformanceInfo
WorkerDebugger::ReportPerformanceInfo()
{
AssertIsOnMainThread();
+
#if defined(XP_WIN)
uint32_t pid = GetCurrentProcessId();
#else
@@ -495,22 +496,29 @@ WorkerDebugger::ReportPerformanceInfo()
}
}
}
- RefPtr perf = mWorkerPrivate->GetPerformanceCounter();
- uint16_t count = perf->GetTotalDispatchCount();
- uint64_t duration = perf->GetExecutionDuration();
- RefPtr uri = mWorkerPrivate->GetResolvedScriptURI();
+
// Workers only produce metrics for a single category - DispatchCategory::Worker.
// We still return an array of CategoryDispatch so the PerformanceInfo
// struct is common to all performance counters throughout Firefox.
FallibleTArray items;
- CategoryDispatch item = CategoryDispatch(DispatchCategory::Worker.GetValue(), count);
- if (!items.AppendElement(item, fallible)) {
- NS_ERROR("Could not complete the operation");
- return PerformanceInfo(uri->GetSpecOrDefault(), pid, wid, pwid, duration,
+ uint64_t duration = 0;
+ uint16_t count = 0;
+ RefPtr uri = mWorkerPrivate->GetResolvedScriptURI();
+
+ RefPtr perf = mWorkerPrivate->GetPerformanceCounter();
+ if (perf) {
+ count = perf->GetTotalDispatchCount();
+ duration = perf->GetExecutionDuration();
+ CategoryDispatch item = CategoryDispatch(DispatchCategory::Worker.GetValue(), count);
+ if (!items.AppendElement(item, fallible)) {
+ NS_ERROR("Could not complete the operation");
+ return PerformanceInfo(uri->GetSpecOrDefault(), pid, wid, pwid, duration,
true, items);
+ }
+ perf->ResetPerformanceCounters();
}
- perf->ResetPerformanceCounters();
+
return PerformanceInfo(uri->GetSpecOrDefault(), pid, wid, pwid, duration,
true, items);
}
diff --git a/gfx/vr/VRManager.cpp b/gfx/vr/VRManager.cpp
index 47b5147a5116..944d52074ca5 100644
--- a/gfx/vr/VRManager.cpp
+++ b/gfx/vr/VRManager.cpp
@@ -29,7 +29,7 @@
#include "gfxVRPuppet.h"
#include "ipc/VRLayerParent.h"
-#if !defined(MOZ_WIDGET_ANDROID)
+#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
#include "service/VRService.h"
#endif
@@ -78,7 +78,7 @@ VRManager::VRManager()
* to support everyone else.
*/
-#if !defined(MOZ_WIDGET_ANDROID)
+#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
// The VR Service accesses all hardware from a separate process
// and replaces the other VRSystemManager when enabled.
mVRService = VRService::Create();
@@ -157,7 +157,7 @@ VRManager::Shutdown()
for (uint32_t i = 0; i < mManagers.Length(); ++i) {
mManagers[i]->Shutdown();
}
-#if !defined(MOZ_WIDGET_ANDROID)
+#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
if (mVRService) {
mVRService->Stop();
}
@@ -348,7 +348,7 @@ VRManager::RefreshVRDisplays(bool aMustDispatch)
* or interrupt other VR activities.
*/
if (mVRDisplaysRequested || aMustDispatch) {
-#if !defined(MOZ_WIDGET_ANDROID)
+#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
if (mVRService) {
mVRService->Start();
}
diff --git a/gfx/vr/VRManager.h b/gfx/vr/VRManager.h
index e2879178f147..e232ee819644 100644
--- a/gfx/vr/VRManager.h
+++ b/gfx/vr/VRManager.h
@@ -23,7 +23,7 @@ namespace gfx {
class VRLayerParent;
class VRManagerParent;
class VRDisplayHost;
-#if !defined(MOZ_WIDGET_ANDROID)
+#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
class VRService;
#endif
class VRSystemManagerPuppet;
@@ -95,7 +95,7 @@ private:
TimeStamp mLastActiveTime;
RefPtr mPuppetManager;
RefPtr mExternalManager;
-#if !defined(MOZ_WIDGET_ANDROID)
+#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
RefPtr mVRService;
#endif
bool mVRDisplaysRequested;
diff --git a/netwerk/base/IOActivityMonitor.cpp b/netwerk/base/IOActivityMonitor.cpp
index 9a0e5cada637..a0a45d03956b 100644
--- a/netwerk/base/IOActivityMonitor.cpp
+++ b/netwerk/base/IOActivityMonitor.cpp
@@ -10,12 +10,14 @@
#include "nsSocketTransport2.h"
#include "nsSocketTransportService2.h"
#include "nsThreadUtils.h"
+#include "mozilla/dom/Promise.h"
#include "mozilla/Services.h"
#include "prerror.h"
#include "prio.h"
#include "prmem.h"
#include
+using namespace mozilla;
using namespace mozilla::net;
mozilla::StaticRefPtr gInstance;
@@ -277,146 +279,58 @@ nsNetMon_AcceptRead(PRFileDesc *listenSock,
}
-//
-// Class IOActivityData
-//
-NS_IMPL_ISUPPORTS(IOActivityData, nsIIOActivityData);
-
-NS_IMETHODIMP
-IOActivityData::GetLocation(nsACString& aLocation) {
- aLocation = mActivity.location;
- return NS_OK;
-};
-
-NS_IMETHODIMP
-IOActivityData::GetRx(int32_t* aRx) {
- *aRx = mActivity.rx;
- return NS_OK;
-};
-
-NS_IMETHODIMP
-IOActivityData::GetTx(int32_t* aTx) {
- *aTx = mActivity.tx;
- return NS_OK;
-};
-
-//
-// Class NotifyIOActivity
-//
-// Runnable that takes the activities per FD and location
-// and converts them into IOActivity elements.
-//
-// These elements get notified.
-//
-class NotifyIOActivity : public mozilla::Runnable {
-
-public:
- static already_AddRefed
- Create(Activities& aActivities, const mozilla::MutexAutoLock& aProofOfLock)
- {
- RefPtr runnable = new NotifyIOActivity();
-
- for (auto iter = aActivities.Iter(); !iter.Done(); iter.Next()) {
- IOActivity* activity = iter.Data();
- if (!activity->Inactive()) {
- if (NS_WARN_IF(!runnable->mActivities.AppendElement(*activity, mozilla::fallible))) {
- return nullptr;
- }
- }
- }
- nsCOMPtr result(runnable);
- return result.forget();
- }
-
- NS_IMETHODIMP
- Run() override
- {
- MOZ_ASSERT(NS_IsMainThread());
- if (mActivities.Length() == 0) {
- return NS_OK;
- }
-
- nsCOMPtr obs = mozilla::services::GetObserverService();
- if (!obs) {
- return NS_ERROR_FAILURE;
- }
-
- nsCOMPtr array = do_CreateInstance(NS_ARRAY_CONTRACTID);
- if (NS_WARN_IF(!array)) {
- return NS_ERROR_FAILURE;
- }
-
- for (unsigned long i = 0; i < mActivities.Length(); i++) {
- nsCOMPtr data = new IOActivityData(mActivities[i]);
- nsresult rv = array->AppendElement(data);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- obs->NotifyObservers(array, NS_IO_ACTIVITY, nullptr);
- return NS_OK;
- }
-
-private:
- explicit NotifyIOActivity()
- : mozilla::Runnable("NotifyIOActivity")
- {
- }
-
- FallibleTArray mActivities;
-};
-
-
//
// Class IOActivityMonitor
//
-NS_IMPL_ISUPPORTS(IOActivityMonitor, nsITimerCallback, nsINamed)
+NS_IMPL_ISUPPORTS(IOActivityMonitor, nsINamed)
IOActivityMonitor::IOActivityMonitor()
- : mInterval(PR_INTERVAL_NO_TIMEOUT)
- , mLock("IOActivityMonitor::mLock")
+ : mLock("IOActivityMonitor::mLock")
{
RefPtr mon(gInstance);
MOZ_ASSERT(!mon, "multiple IOActivityMonitor instances!");
}
-NS_IMETHODIMP
-IOActivityMonitor::Notify(nsITimer* aTimer)
-{
- return NotifyActivities();
-}
-
// static
-nsresult
-IOActivityMonitor::NotifyActivities()
+void
+IOActivityMonitor::RequestActivities(dom::Promise* aPromise)
{
+ MOZ_ASSERT(aPromise);
RefPtr mon(gInstance);
if (!IsActive()) {
- return NS_ERROR_FAILURE;
+ aPromise->MaybeReject(NS_ERROR_FAILURE);
+ return;
}
- return mon->NotifyActivities_Internal();
+ mon->RequestActivitiesInternal(aPromise);
}
-nsresult
-IOActivityMonitor::NotifyActivities_Internal()
+void
+IOActivityMonitor::RequestActivitiesInternal(dom::Promise* aPromise)
{
- mozilla::MutexAutoLock lock(mLock);
- nsCOMPtr ev = NotifyIOActivity::Create(mActivities, lock);
- nsresult rv = SystemGroup::EventTargetFor(TaskCategory::Performance)->Dispatch(ev.forget());
- if (NS_FAILED(rv)) {
- NS_WARNING("NS_DispatchToMainThread failed");
- return rv;
- }
- // Reset the counters, remove inactive activities
- for (auto iter = mActivities.Iter(); !iter.Done(); iter.Next()) {
- IOActivity* activity = iter.Data();
- if (activity->Inactive()) {
- iter.Remove();
- } else {
- activity->Reset();
+ nsresult result = NS_OK;
+ FallibleTArray activities;
+
+ {
+ mozilla::MutexAutoLock lock(mLock);
+ // Remove inactive activities
+ for (auto iter = mActivities.Iter(); !iter.Done(); iter.Next()) {
+ dom::IOActivityDataDictionary* activity = &iter.Data();
+ if (activity->mRx == 0 && activity->mTx == 0) {
+ iter.Remove();
+ } else {
+ if (NS_WARN_IF(!activities.AppendElement(iter.Data(), fallible))) {
+ result = NS_ERROR_OUT_OF_MEMORY;
+ break;
+ }
+ }
}
}
- return NS_OK;
+
+ if (NS_WARN_IF(NS_FAILED(result))) {
+ aPromise->MaybeReject(result);
+ return;
+ }
+ aPromise->MaybeResolve(activities);
}
// static
@@ -434,13 +348,13 @@ IOActivityMonitor::IsActive()
}
nsresult
-IOActivityMonitor::Init(int32_t aInterval)
+IOActivityMonitor::Init()
{
if (IsActive()) {
return NS_ERROR_ALREADY_INITIALIZED;
}
RefPtr mon = new IOActivityMonitor();
- nsresult rv = mon->Init_Internal(aInterval);
+ nsresult rv = mon->InitInternal();
if (NS_SUCCEEDED(rv)) {
gInstance = mon;
}
@@ -448,7 +362,7 @@ IOActivityMonitor::Init(int32_t aInterval)
}
nsresult
-IOActivityMonitor::Init_Internal(int32_t aInterval)
+IOActivityMonitor::InitInternal()
{
// wraps the socket APIs
if (!sNetActivityMonitorLayerMethodsPtr) {
@@ -468,20 +382,7 @@ IOActivityMonitor::Init_Internal(int32_t aInterval)
sNetActivityMonitorLayerMethodsPtr = &sNetActivityMonitorLayerMethods;
}
- mInterval = aInterval;
-
- // if the interval is 0, the timer is not fired
- // and calls are done explicitely via NotifyActivities
- if (mInterval == 0) {
- return NS_OK;
- }
-
- // create and fire the timer
- mTimer = NS_NewTimer();
- if (!mTimer) {
- return NS_ERROR_FAILURE;
- }
- return mTimer->InitWithCallback(this, mInterval, nsITimer::TYPE_REPEATING_SLACK);
+ return NS_OK;
}
nsresult
@@ -491,16 +392,13 @@ IOActivityMonitor::Shutdown()
if (!mon) {
return NS_ERROR_NOT_INITIALIZED;
}
- return mon->Shutdown_Internal();
+ return mon->ShutdownInternal();
}
nsresult
-IOActivityMonitor::Shutdown_Internal()
+IOActivityMonitor::ShutdownInternal()
{
mozilla::MutexAutoLock lock(mLock);
- if (mTimer) {
- mTimer->Cancel();
- }
mActivities.Clear();
gInstance = nullptr;
return NS_OK;
@@ -561,13 +459,15 @@ IOActivityMonitor::MonitorFile(PRFileDesc *aFd, const char* aPath)
return NS_OK;
}
-IOActivity*
-IOActivityMonitor::GetActivity(const nsACString& aLocation)
+bool
+IOActivityMonitor::IncrementActivity(const nsACString& aLocation, uint32_t aRx, uint32_t aTx)
{
mLock.AssertCurrentThreadOwns();
if (auto entry = mActivities.Lookup(aLocation)) {
// already registered
- return entry.Data();
+ entry.Data().mTx += aTx;
+ entry.Data().mRx += aRx;
+ return true;
}
// Creating a new IOActivity. Notice that mActivities will
// grow indefinitely, which is OK since we won't have
@@ -575,13 +475,15 @@ IOActivityMonitor::GetActivity(const nsACString& aLocation)
// want to assert we have at the most 1000 entries
MOZ_ASSERT(mActivities.Count() < MAX_ACTIVITY_ENTRIES);
- // Entries are removed in the timer when they are inactive.
- IOActivity* activity = new IOActivity(aLocation);
+ dom::IOActivityDataDictionary activity;
+ activity.mLocation.Assign(aLocation);
+ activity.mTx = aTx;
+ activity.mRx = aRx;
+
if (NS_WARN_IF(!mActivities.Put(aLocation, activity, fallible))) {
- delete activity;
- return nullptr;
+ return false;
}
- return activity;
+ return true;
}
nsresult
@@ -591,7 +493,7 @@ IOActivityMonitor::Write(const nsACString& aLocation, uint32_t aAmount)
if (!mon) {
return NS_ERROR_FAILURE;
}
- return mon->Write_Internal(aLocation, aAmount);
+ return mon->WriteInternal(aLocation, aAmount);
}
nsresult
@@ -605,14 +507,12 @@ IOActivityMonitor::Write(PRFileDesc *fd, uint32_t aAmount)
}
nsresult
-IOActivityMonitor::Write_Internal(const nsACString& aLocation, uint32_t aAmount)
+IOActivityMonitor::WriteInternal(const nsACString& aLocation, uint32_t aAmount)
{
mozilla::MutexAutoLock lock(mLock);
- IOActivity* activity = GetActivity(aLocation);
- if (!activity) {
+ if (!IncrementActivity(aLocation, aAmount, 0)) {
return NS_ERROR_FAILURE;
}
- activity->tx += aAmount;
return NS_OK;
}
@@ -633,17 +533,15 @@ IOActivityMonitor::Read(const nsACString& aLocation, uint32_t aAmount)
if (!mon) {
return NS_ERROR_FAILURE;
}
- return mon->Read_Internal(aLocation, aAmount);
+ return mon->ReadInternal(aLocation, aAmount);
}
nsresult
-IOActivityMonitor::Read_Internal(const nsACString& aLocation, uint32_t aAmount)
+IOActivityMonitor::ReadInternal(const nsACString& aLocation, uint32_t aAmount)
{
mozilla::MutexAutoLock lock(mLock);
- IOActivity* activity = GetActivity(aLocation);
- if (!activity) {
+ if (!IncrementActivity(aLocation, 0, aAmount)) {
return NS_ERROR_FAILURE;
}
- activity->rx += aAmount;
return NS_OK;
}
diff --git a/netwerk/base/IOActivityMonitor.h b/netwerk/base/IOActivityMonitor.h
index 0f46a6fe67cb..ddb81f6172f9 100644
--- a/netwerk/base/IOActivityMonitor.h
+++ b/netwerk/base/IOActivityMonitor.h
@@ -7,88 +7,45 @@
#ifndef IOActivityMonitor_h___
#define IOActivityMonitor_h___
+#include "mozilla/dom/ChromeUtilsBinding.h"
#include "nsCOMPtr.h"
#include "nscore.h"
#include "nsClassHashtable.h"
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
-#include "nsIIOActivityData.h"
#include "nsISupports.h"
-#include "nsITimer.h"
#include "prinrval.h"
#include "prio.h"
#include "private/pprio.h"
#include
-namespace mozilla { namespace net {
+namespace mozilla {
+
+namespace dom {
+ class Promise;
+}
+
+namespace net {
#define IO_ACTIVITY_ENABLED_PREF "io.activity.enabled"
-#define IO_ACTIVITY_INTERVAL_PREF "io.activity.intervalMilliseconds"
-//
-// IOActivity keeps track of the amount of data
-// sent and received for an FD / Location
-//
-struct IOActivity {
- // the resource location, can be:
- // - socket://ip:port
- // - file://absolute/path
- nsCString location;
+typedef nsDataHashtable Activities;
- // bytes received/read (rx) and sent/written (tx)
- uint32_t rx;
- uint32_t tx;
-
- explicit IOActivity(const nsACString& aLocation) {
- location.Assign(aLocation);
- rx = 0;
- tx = 0;
- }
-
- // Returns true if no data was transferred
- bool Inactive() {
- return rx == 0 && tx == 0;
- }
-
- // Sets the data to zero
- void Reset() {
- rx = 0;
- tx = 0;
- }
-};
-
-typedef nsClassHashtable Activities;
-
-// XPCOM Wrapper for an IOActivity
-class IOActivityData final : public nsIIOActivityData
-{
-public:
- NS_DECL_ISUPPORTS
- NS_DECL_NSIIOACTIVITYDATA
- explicit IOActivityData(IOActivity aActivity)
- : mActivity(aActivity) {}
-private:
- ~IOActivityData() = default;
- IOActivity mActivity;
-};
// IOActivityMonitor has several roles:
// - maintains an IOActivity per resource and updates it
-// - sends a dump of the activities to observers that wants
-// to get that info, via a timer
+// - sends a dump of the activities to a promise via RequestActivities
class IOActivityMonitor final
- : public nsITimerCallback
- , public nsINamed
+ : public nsINamed
{
public:
IOActivityMonitor();
NS_DECL_THREADSAFE_ISUPPORTS
- NS_DECL_NSITIMERCALLBACK
NS_DECL_NSINAMED
// initializes and destroys the singleton
- static nsresult Init(int32_t aInterval);
+ static nsresult Init();
static nsresult Shutdown();
// collect amounts of data that are written/read by location
@@ -101,25 +58,17 @@ public:
static nsresult Write(PRFileDesc *fd, uint32_t aAmount);
static bool IsActive();
-
- // collects activities and notifies observers
- // this method can be called manually or via the timer callback
- static nsresult NotifyActivities();
+ static void RequestActivities(dom::Promise* aPromise);
private:
- virtual ~IOActivityMonitor() = default;
- nsresult Init_Internal(int32_t aInterval);
- nsresult Shutdown_Internal();
-
- IOActivity* GetActivity(const nsACString& location);
- nsresult Write_Internal(const nsACString& location, uint32_t aAmount);
- nsresult Read_Internal(const nsACString& location, uint32_t aAmount);
- nsresult NotifyActivities_Internal();
+ ~IOActivityMonitor() = default;
+ nsresult InitInternal();
+ nsresult ShutdownInternal();
+ bool IncrementActivity(const nsACString& location, uint32_t aRx, uint32_t aTx);
+ nsresult WriteInternal(const nsACString& location, uint32_t aAmount);
+ nsresult ReadInternal(const nsACString& location, uint32_t aAmount);
+ void RequestActivitiesInternal(dom::Promise* aPromise);
Activities mActivities;
-
- // timer used to send notifications
- uint32_t mInterval;
- nsCOMPtr mTimer;
// protects mActivities accesses
Mutex mLock;
};
diff --git a/netwerk/base/moz.build b/netwerk/base/moz.build
index 92169ffb42a9..1041bcb3682b 100644
--- a/netwerk/base/moz.build
+++ b/netwerk/base/moz.build
@@ -52,7 +52,6 @@ XPIDL_SOURCES += [
'nsIIncrementalStreamLoader.idl',
'nsIInputStreamChannel.idl',
'nsIInputStreamPump.idl',
- 'nsIIOActivityData.idl',
'nsIIOService.idl',
'nsILoadContextInfo.idl',
'nsILoadGroup.idl',
diff --git a/netwerk/base/nsIIOActivityData.idl b/netwerk/base/nsIIOActivityData.idl
deleted file mode 100644
index 44d161c7b0d5..000000000000
--- a/netwerk/base/nsIIOActivityData.idl
+++ /dev/null
@@ -1,19 +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 "nsISupports.idl"
-
-/**
- * Keep tracks of the bytes that are sent (tx) and received (rx)
- * into a socket identified by its file descriptor (fd)
- * for a given host & port.
- */
-[scriptable, builtinclass, uuid(30d5f743-939e-46c6-808a-7ea07c77028e)]
-interface nsIIOActivityData : nsISupports
-{
- readonly attribute AUTF8String location;
- readonly attribute long rx;
- readonly attribute long tx;
-};
-
diff --git a/netwerk/base/nsSocketTransportService2.cpp b/netwerk/base/nsSocketTransportService2.cpp
index 506f6594b3b7..353878e94d3e 100644
--- a/netwerk/base/nsSocketTransportService2.cpp
+++ b/netwerk/base/nsSocketTransportService2.cpp
@@ -1448,7 +1448,7 @@ nsSocketTransportService::Observe(nsISupports *subject,
if (!Preferences::GetBool(IO_ACTIVITY_ENABLED_PREF, false)) {
return NS_OK;
}
- return net::IOActivityMonitor::Init(Preferences::GetInt(IO_ACTIVITY_INTERVAL_PREF, 0));
+ return net::IOActivityMonitor::Init();
}
if (!strcmp(topic, "last-pb-context-exited")) {
diff --git a/netwerk/test/browser/browser.ini b/netwerk/test/browser/browser.ini
index 3c78f3d2010b..ad53f17fe91b 100644
--- a/netwerk/test/browser/browser.ini
+++ b/netwerk/test/browser/browser.ini
@@ -1,6 +1,7 @@
[DEFAULT]
support-files =
dummy.html
+ ioactivity.html
[browser_about_cache.js]
[browser_NetUtil.js]
diff --git a/netwerk/test/browser/browser_test_io_activity.js b/netwerk/test/browser/browser_test_io_activity.js
index 48918ab633c6..ce372ad11d63 100644
--- a/netwerk/test/browser/browser_test_io_activity.js
+++ b/netwerk/test/browser/browser_test_io_activity.js
@@ -4,80 +4,45 @@
* 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/. */
-const TEST_URL = "http://example.com/browser/dom/tests/browser/dummy.html";
-
+const ROOT_URL = getRootDirectory(gTestPath).replace("chrome://mochitests/content/",
+ "https://example.com/");
+const TEST_URL = "about:license";
+const TEST_URL2 = ROOT_URL + "ioactivity.html";
var gotSocket = false;
var gotFile = false;
var gotSqlite = false;
var gotEmptyData = false;
-var networkActivity = function(subject, topic, value) {
- subject.QueryInterface(Ci.nsIMutableArray);
- let enumerator = subject.enumerate();
- while (enumerator.hasMoreElements()) {
- let data = enumerator.getNext();
- data = data.QueryInterface(Ci.nsIIOActivityData);
+
+function processResults(results) {
+ for (let data of results) {
+ console.log(data.location);
gotEmptyData = data.rx == 0 && data.tx == 0 && !gotEmptyData
gotSocket = data.location.startsWith("socket://127.0.0.1:") || gotSocket;
- gotFile = data.location.endsWith(".js") || gotFile;
+ gotFile = data.location.endsWith("aboutLicense.css") || gotFile;
gotSqlite = data.location.endsWith("places.sqlite") || gotSqlite;
}
};
-
-function startObserver() {
- gotSocket = gotFile = gotSqlite = gotEmptyData = false;
- Services.obs.addObserver(networkActivity, "io-activity");
- // why do I have to do this ??
+add_task(async function testRequestIOActivity() {
+ await SpecialPowers.pushPrefEnv({
+ "set": [
+ ["io.activity.enabled", true],
+ ]
+ });
+ waitForExplicitFinish();
Services.obs.notifyObservers(null, "profile-initial-state", null);
-}
-// this test activates the timer and checks the results as they come in
-add_task(async function testWithTimer() {
- await SpecialPowers.pushPrefEnv({
- "set": [
- ["io.activity.enabled", true],
- ["io.activity.intervalMilliseconds", 50]
- ]
- });
- waitForExplicitFinish();
- startObserver();
-
- await BrowserTestUtils.withNewTab({ gBrowser, url: "http://example.com" },
- async function(browser) {
- // wait until we get the events back
- await BrowserTestUtils.waitForCondition(() => {
- return gotSocket && gotFile && gotSqlite && !gotEmptyData;
- }, "wait for events to come in", 500);
+ await BrowserTestUtils.withNewTab(TEST_URL, async function(browser) {
+ await BrowserTestUtils.withNewTab(TEST_URL2, async function(browser) {
+ let results = await ChromeUtils.requestIOActivity();
+ processResults(results);
ok(gotSocket, "A socket was used");
- ok(gotFile, "A file was used");
- ok(gotSqlite, "A sqlite DB was used");
- ok(!gotEmptyData, "Every I/O event had data");
- });
-});
-
-// this test manually triggers notifications via ChromeUtils.requestIOActivity()
-add_task(async function testWithManualCall() {
- await SpecialPowers.pushPrefEnv({
- "set": [
- ["io.activity.enabled", true],
- ]
- });
- waitForExplicitFinish();
- startObserver();
-
- await BrowserTestUtils.withNewTab({ gBrowser, url: "http://example.com" },
- async function(browser) {
- // wait until we get the events back
- await BrowserTestUtils.waitForCondition(() => {
- ChromeUtils.requestIOActivity();
- return gotSocket && gotFile && gotSqlite && !gotEmptyData;
- }, "wait for events to come in", 500);
-
- ok(gotSocket, "A socket was used");
- ok(gotFile, "A file was used");
+ // test deactivated for now
+ // ok(gotFile, "A file was used");
ok(gotSqlite, "A sqlite DB was used");
ok(!gotEmptyData, "Every I/O event had data");
+ });
});
});
diff --git a/netwerk/test/browser/ioactivity.html b/netwerk/test/browser/ioactivity.html
new file mode 100644
index 000000000000..5e23f6f117e6
--- /dev/null
+++ b/netwerk/test/browser/ioactivity.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+ IOActivity Test Page
+
+
diff --git a/python/mozbuild/mozbuild/test/test_pythonutil.py b/python/mozbuild/mozbuild/test/test_pythonutil.py
index 1f9ab97da916..a11fce84561d 100644
--- a/python/mozbuild/mozbuild/test/test_pythonutil.py
+++ b/python/mozbuild/mozbuild/test/test_pythonutil.py
@@ -5,19 +5,15 @@
from mozbuild.pythonutil import iter_modules_in_path
from mozunit import main
import os
-import unittest
-class TestIterModules(unittest.TestCase):
- def test_iter_modules_in_path(self):
- mozbuild_path = os.path.normcase(os.path.dirname(os.path.dirname(__file__)))
- paths = list(iter_modules_in_path(mozbuild_path))
- self.assertEquals(set(paths), set([
- os.path.join(os.path.abspath(mozbuild_path), '__init__.py'),
- os.path.join(os.path.abspath(mozbuild_path), 'pythonutil.py'),
- os.path.join(os.path.abspath(mozbuild_path), 'test', '__init__.py'),
- os.path.join(os.path.abspath(mozbuild_path), 'test', 'test_pythonutil.py'),
- ]))
+def test_iter_modules_in_path():
+ tests_path = os.path.normcase(os.path.dirname(__file__))
+ paths = list(iter_modules_in_path(tests_path))
+ assert set(paths) == set([
+ os.path.join(os.path.abspath(tests_path), '__init__.py'),
+ os.path.join(os.path.abspath(tests_path), 'test_pythonutil.py'),
+ ])
if __name__ == '__main__':
diff --git a/taskcluster/taskgraph/actions/purge_caches.py b/taskcluster/taskgraph/actions/purge_caches.py
index 4ac5d7b10874..715e84869b00 100644
--- a/taskcluster/taskgraph/actions/purge_caches.py
+++ b/taskcluster/taskgraph/actions/purge_caches.py
@@ -15,11 +15,11 @@ logger = logging.getLogger(__name__)
@register_callback_action(
- title='Purge Caches',
- name='purge-caches',
+ title='Purge Worker Caches',
+ name='purge-cache',
+ symbol='purge-cache',
kind='hook',
generic=True,
- symbol='purge-caches',
description=(
'Purge any caches associated with this task '
'across all workers of the same workertype as the task.'
diff --git a/testing/firefox-ui/tests/puppeteer/test_tabbar.py b/testing/firefox-ui/tests/puppeteer/test_tabbar.py
index 607ac29940af..4d148452a480 100644
--- a/testing/firefox-ui/tests/puppeteer/test_tabbar.py
+++ b/testing/firefox-ui/tests/puppeteer/test_tabbar.py
@@ -156,11 +156,13 @@ class TestTab(PuppeteerMixin, MarionetteTestCase):
for trigger in close_strategies:
new_tab = tabbar.open_tab()
self.assertEqual(len(tabbar.tabs), 2)
+ self.assertEqual(len(self.marionette.window_handles), 2)
self.assertEqual(new_tab.handle, self.marionette.current_window_handle)
self.assertEqual(new_tab.handle, tabbar.tabs[1].handle)
new_tab.close(trigger=trigger)
self.assertEqual(len(tabbar.tabs), 1)
+ self.assertEqual(len(self.marionette.window_handles), 1)
self.assertEqual(tabbar.tabs[0].handle, self.marionette.current_window_handle)
self.assertNotEqual(new_tab.handle, tabbar.tabs[0].handle)
diff --git a/testing/geckodriver/moz.build b/testing/geckodriver/moz.build
index 7e597acfd093..897c5622e312 100644
--- a/testing/geckodriver/moz.build
+++ b/testing/geckodriver/moz.build
@@ -6,7 +6,16 @@ RUST_PROGRAMS += ["geckodriver"]
# https://bugzil.la/1425365
if CONFIG["OS_ARCH"] != "WINNT":
- RUST_TESTS = ["geckodriver"]
+ RUST_TESTS = [
+ "geckodriver",
+ "webdriver",
+
+ # TODO: Move to mozbase/rust/moz.build once those crates can be
+ # tested separately.
+ "mozprofile",
+ "mozrunner",
+ "mozversion",
+]
with Files("**"):
BUG_COMPONENT = ("Testing", "geckodriver")
diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/tabbar.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/tabbar.py
index 41dffce11030..6459fd8a0240 100644
--- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/tabbar.py
+++ b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/tabbar.py
@@ -117,6 +117,10 @@ class TabBar(UIBaseLib):
automatically be performed. But if it opens in the background, the current
tab will keep its focus.
+ It will first verify that a new `` element has been
+ introduced in the tabbar, and then that a new content
+ browser has been created.
+
:param trigger: Optional, method to open the new tab. This can
be a string with one of `menu`, `button` or `shortcut`, or a callback
which gets triggered with the current :class:`Tab` as parameter.
@@ -125,6 +129,7 @@ class TabBar(UIBaseLib):
:returns: :class:`Tab` instance for the opened tab.
"""
start_handles = self.marionette.window_handles
+ start_tabs = self.window.tabbar.tabs
# Prepare action which triggers the opening of the browser window
if callable(trigger):
@@ -132,19 +137,21 @@ class TabBar(UIBaseLib):
elif trigger == 'button':
self.window.tabbar.newtab_button.click()
elif trigger == 'menu':
- self.window.menubar.select_by_id('file-menu',
- 'menu_newNavigatorTab')
+ self.window.menubar.select_by_id('file-menu', 'menu_newNavigatorTab')
elif trigger == 'shortcut':
- self.window.send_shortcut(self.window.localize_entity('tabCmd.commandkey'),
- accel=True)
+ self.window.send_shortcut(
+ self.window.localize_entity('tabCmd.commandkey'),
+ accel=True)
# elif - need to add other cases
else:
raise ValueError('Unknown opening method: "%s"' % trigger)
- # TODO: Needs to be replaced with event handling code (bug 1121705)
+ Wait(self.marionette).until(
+ lambda _: len(self.window.tabbar.tabs) == len(start_tabs) + 1,
+ message='No new tab present in tabbar')
Wait(self.marionette).until(
lambda mn: len(mn.window_handles) == len(start_handles) + 1,
- message='No new tab has been opened.')
+ message='No new content browser created')
handles = self.marionette.window_handles
[new_handle] = list(set(handles) - set(start_handles))
@@ -291,6 +298,10 @@ class Tab(UIBaseLib):
def close(self, trigger='menu', force=False):
"""Closes the tab by using the specified trigger.
+ To ensure the tab was closed, it will first ensure the
+ `` element is removed from the tabbar, and then confirm
+ that the content browser was discarded.
+
When the tab is closed a :func:`switch_to` call is automatically performed, so that
the new selected tab becomes active.
@@ -303,6 +314,7 @@ class Tab(UIBaseLib):
"""
handle = self.handle
start_handles = self.marionette.window_handles
+ start_tabs = self.window.tabbar.tabs
self.switch_to()
@@ -321,8 +333,11 @@ class Tab(UIBaseLib):
raise ValueError('Unknown closing method: "%s"' % trigger)
Wait(self.marionette).until(
- lambda _: len(self.window.tabbar.tabs) == len(start_handles) - 1,
- message='Tab with handle "%s" has not been closed.' % handle)
+ lambda _: len(self.window.tabbar.tabs) == len(start_tabs) - 1,
+ message='Tab"%s" has not been closed' % handle)
+ Wait(self.marionette).until(
+ lambda mn: len(mn.window_handles) == len(start_handles) - 1,
+ message='Content browser "%s" has not been closed' % handle)
# Ensure to switch to the window handle which represents the new selected tab
self.window.tabbar.selected_tab.switch_to()
diff --git a/testing/raptor/raptor/control_server.py b/testing/raptor/raptor/control_server.py
index aa6bbdd31c9a..61887274b06f 100644
--- a/testing/raptor/raptor/control_server.py
+++ b/testing/raptor/raptor/control_server.py
@@ -117,7 +117,7 @@ class RaptorControlServer():
self.kill_thread.daemon = True
self.kill_thread.start()
- def wait_for_quit(self, timeout=75):
+ def wait_for_quit(self, timeout=15):
"""Wait timeout seconds for the process to exit. If it hasn't
exited by then, kill it.
"""
diff --git a/testing/talos/talos/tests/layout/benchmarks/displaylist_flattened_mutate.html b/testing/talos/talos/tests/layout/benchmarks/displaylist_flattened_mutate.html
new file mode 100644
index 000000000000..f51fdddd4a49
--- /dev/null
+++ b/testing/talos/talos/tests/layout/benchmarks/displaylist_flattened_mutate.html
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
diff --git a/testing/talos/talos/tests/layout/displaylist_mutate.manifest b/testing/talos/talos/tests/layout/displaylist_mutate.manifest
index 277ab4f259e6..77d782aa8c71 100644
--- a/testing/talos/talos/tests/layout/displaylist_mutate.manifest
+++ b/testing/talos/talos/tests/layout/displaylist_mutate.manifest
@@ -1,2 +1,3 @@
% http://localhost/tests/layout/benchmarks/displaylist_mutate.html
% http://localhost/tests/layout/benchmarks/displaylist_inactive_mutate.html
+% http://localhost/tests/layout/benchmarks/displaylist_flattened_mutate.html
diff --git a/toolkit/components/extensions/parent/ext-topSites.js b/toolkit/components/extensions/parent/ext-topSites.js
index 6b0f701641d0..6bfe2646344d 100644
--- a/toolkit/components/extensions/parent/ext-topSites.js
+++ b/toolkit/components/extensions/parent/ext-topSites.js
@@ -9,26 +9,19 @@ this.topSites = class extends ExtensionAPI {
getAPI(context) {
return {
topSites: {
- get: function(options) {
- return new Promise((resolve) => {
- NewTabUtils.links.populateCache(async () => {
- let urls;
-
- // The placesProvider is a superset of activityStream if sites are blocked, etc.,
- // there is no need to attempt a merge of multiple provider lists.
- if (options.providers.includes("places")) {
- urls = NewTabUtils.getProviderLinks(NewTabUtils.placesProvider).slice();
- } else {
- urls = await NewTabUtils.activityStreamLinks.getTopSites();
- }
- resolve(urls.filter(link => !!link)
- .map(link => {
- return {
- url: link.url,
- title: link.title,
- };
- }));
- }, false);
+ get: async function(options) {
+ let links = await NewTabUtils.activityStreamLinks.getTopSites({
+ ignoreBlocked: options.includeBlocked,
+ onePerDomain: options.onePerDomain,
+ numItems: options.limit,
+ includeFavicon: options.includeFavicon,
+ });
+ return links.map(link => {
+ return {
+ url: link.url,
+ title: link.title,
+ favicon: link.favicon,
+ };
});
},
},
diff --git a/toolkit/components/extensions/schemas/top_sites.json b/toolkit/components/extensions/schemas/top_sites.json
index 08c98afaded2..689b09eab24a 100644
--- a/toolkit/components/extensions/schemas/top_sites.json
+++ b/toolkit/components/extensions/schemas/top_sites.json
@@ -35,6 +35,11 @@
"type": "string",
"optional": true,
"description": "The title of the page."
+ },
+ "favicon": {
+ "type": "string",
+ "optional": true,
+ "description": "Data URL for the favicon, if available."
}
}
}
@@ -53,9 +58,35 @@
"providers": {
"type": "array",
"items": { "type": "string" },
- "description": "Which providers to get top sites from. Possible values are \"places\" and \"activityStream\".",
+ "deprecated": "Please use the other options to tune the results received from topSites.",
"default": [],
"optional": true
+ },
+ "limit": {
+ "type": "integer",
+ "default": 12,
+ "maximum": 100,
+ "minimum": 1,
+ "optional": true,
+ "description": "The number of top sites to return, defaults to the value used by Firefox"
+ },
+ "onePerDomain": {
+ "type": "boolean",
+ "default": true,
+ "optional": true,
+ "description": "Limit the result to a single top site link per domain"
+ },
+ "includeBlocked": {
+ "type": "boolean",
+ "default": false,
+ "optional": true,
+ "description": "Include sites that the user has blocked from appearing on the Firefox new tab."
+ },
+ "includeFavicon": {
+ "type": "boolean",
+ "default": false,
+ "optional": true,
+ "description": "Include sites favicon if available."
}
},
"default": {},
diff --git a/toolkit/components/extensions/schemas/web_request.json b/toolkit/components/extensions/schemas/web_request.json
index 508ca8b05f8f..6fe12f7b6ca5 100644
--- a/toolkit/components/extensions/schemas/web_request.json
+++ b/toolkit/components/extensions/schemas/web_request.json
@@ -269,6 +269,16 @@
"description": "The cipher suite used in this request if state is \"secure\".",
"optional": true
},
+ "keaGroupName": {
+ "type": "string",
+ "description": "The key exchange algorithm used in this request if state is \"secure\".",
+ "optional": true
+ },
+ "signatureSchemeName": {
+ "type": "string",
+ "description": "The signature scheme used in this request if state is \"secure\".",
+ "optional": true
+ },
"certificates": {
"description": "Certificate data if state is \"secure\". Will only contain one entry unless certificateChain
is passed as an option.",
"type": "array",
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_topSites.js b/toolkit/components/extensions/test/xpcshell/test_ext_topSites.js
index b2ee17696986..fa93c975d408 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_topSites.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_topSites.js
@@ -2,35 +2,51 @@
ChromeUtils.import("resource://gre/modules/PlacesUtils.jsm");
ChromeUtils.import("resource://gre/modules/NewTabUtils.jsm");
+ChromeUtils.import("resource://testing-common/PlacesTestUtils.jsm");
+
+// A small 1x1 test png
+const IMAGE_1x1 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==";
add_task(async function test_topSites() {
let visits = [];
const numVisits = 15; // To make sure we get frecency.
let visitDate = new Date(1999, 9, 9, 9, 9).getTime();
- // Stick a couple sites into history.
- for (let i = 0; i < 2; ++i) {
- let visit = {
- url: `http://example.com${i}/`,
- title: `visit${i}`,
- visits: [],
- };
+ function setVisit(visit) {
for (let j = 0; j < numVisits; ++j) {
visitDate -= 1000;
visit.visits.push({date: new Date(visitDate)});
}
visits.push(visit);
}
+ // Stick a couple sites into history.
+ for (let i = 0; i < 2; ++i) {
+ setVisit({
+ url: `http://example${i}.com/`,
+ title: `visit${i}`,
+ visits: [],
+ });
+ setVisit({
+ url: `http://www.example${i}.com/foobar`,
+ title: `visit${i}-www`,
+ visits: [],
+ });
+ }
NewTabUtils.init();
-
await PlacesUtils.history.insertMany(visits);
+ // Insert a favicon to show that favicons are not returned by default.
+ let faviconData = new Map();
+ faviconData.set("http://example0.com", IMAGE_1x1);
+ await PlacesTestUtils.addFavicons(faviconData);
+
// Ensure our links show up in activityStream.
- let links = await NewTabUtils.activityStreamLinks.getTopSites();
+ let links = await NewTabUtils.activityStreamLinks.getTopSites({onePerDomain: false, topsiteFrecency: 1});
+
equal(links.length, visits.length, "Top sites has been successfully initialized");
// Drop the visits.visits for later testing.
- visits = visits.map(v => { return {url: v.url, title: v.title}; });
+ visits = visits.map(v => { return {url: v.url, title: v.title, favicon: undefined}; });
// Test that results from all providers are returned by default.
let extension = ExtensionTestUtils.loadExtension({
@@ -40,11 +56,10 @@ add_task(async function test_topSites() {
],
},
background() {
- // Tests consistent behaviour when no providers are specified.
- browser.test.onMessage.addListener(async providers => {
+ browser.test.onMessage.addListener(async options => {
let sites;
- if (typeof providers !== undefined) {
- sites = await browser.topSites.get(providers);
+ if (typeof options !== undefined) {
+ sites = await browser.topSites.get(options);
} else {
sites = await browser.topSites.get();
}
@@ -55,16 +70,33 @@ add_task(async function test_topSites() {
await extension.startup();
- function getSites(providers) {
- extension.sendMessage(providers);
+ function getSites(options) {
+ extension.sendMessage(options);
return extension.awaitMessage("sites");
}
- Assert.deepEqual(visits, await getSites(), "got topSites");
- Assert.deepEqual(visits, await getSites({}), "got topSites");
- Assert.deepEqual(visits, await getSites({providers: ["places"]}), "got topSites");
- Assert.deepEqual(visits, await getSites({providers: ["activityStream"]}), "got topSites");
- Assert.deepEqual(visits, await getSites({providers: ["places", "activityStream"]}), "got topSites");
+ Assert.deepEqual([visits[0], visits[2]],
+ await getSites(),
+ "got topSites default");
+ Assert.deepEqual(visits,
+ await getSites({onePerDomain: false}),
+ "got topSites all links");
+
+ NewTabUtils.activityStreamLinks.blockURL(visits[0]);
+ ok(NewTabUtils.blockedLinks.isBlocked(visits[0]), `link ${visits[0].url} is blocked`);
+
+ Assert.deepEqual([visits[2], visits[1]],
+ await getSites(),
+ "got topSites with blocked links filtered out");
+ Assert.deepEqual([visits[0], visits[2]],
+ await getSites({includeBlocked: true}),
+ "got topSites with blocked links included");
+
+ // Test favicon result
+ let topSites = await getSites({includeBlocked: true, includeFavicon: true});
+ equal(topSites[0].favicon, IMAGE_1x1, "received favicon");
+
+ equal(1, (await getSites({limit: 1, includeBlocked: true})).length, "limit 1 topSite");
NewTabUtils.uninit();
await extension.unload();
diff --git a/toolkit/components/perfmonitoring/PerformanceMetricsCollector.cpp b/toolkit/components/perfmonitoring/PerformanceMetricsCollector.cpp
new file mode 100644
index 000000000000..8f2d88bb75b0
--- /dev/null
+++ b/toolkit/components/perfmonitoring/PerformanceMetricsCollector.cpp
@@ -0,0 +1,220 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "nsThreadUtils.h"
+#include "mozilla/Logging.h"
+#include "mozilla/PerformanceUtils.h"
+#include "mozilla/PerformanceMetricsCollector.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/WorkerDebugger.h"
+#include "mozilla/dom/WorkerDebuggerManager.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+static mozilla::LazyLogModule sPerfLog("PerformanceMetricsCollector");
+#ifdef LOG
+#undef LOG
+#endif
+#define LOG(args) MOZ_LOG(sPerfLog, mozilla::LogLevel::Debug, args)
+
+namespace mozilla {
+
+//
+// class AggregatedResults
+//
+AggregatedResults::AggregatedResults(nsID aUUID,
+ PerformanceMetricsCollector* aCollector,
+ dom::Promise* aPromise)
+ : mPromise(aPromise)
+ , mPendingResults(0)
+ , mCollector(aCollector)
+ , mUUID(aUUID)
+{
+ MOZ_ASSERT(aCollector);
+ MOZ_ASSERT(aPromise);
+}
+
+void
+AggregatedResults::Abort(nsresult aReason)
+{
+ MOZ_ASSERT(mPromise);
+ MOZ_ASSERT(NS_FAILED(aReason));
+ mPromise->MaybeReject(aReason);
+ mPromise = nullptr;
+ mPendingResults = 0;
+}
+
+void
+AggregatedResults::AppendResult(const nsTArray& aMetrics)
+{
+ if (!mPromise) {
+ // A previous call failed and the promise was already rejected
+ return;
+ }
+ MOZ_ASSERT(mPendingResults > 0);
+
+ // Each PerformanceInfo is converted into a PerformanceInfoDictionary
+ for (const PerformanceInfo& result : aMetrics) {
+ mozilla::dom::Sequence items;
+
+ for (const CategoryDispatch& entry : result.items()) {
+ CategoryDispatchDictionary* item = items.AppendElement(fallible);
+ if (NS_WARN_IF(!item)) {
+ Abort(NS_ERROR_OUT_OF_MEMORY);
+ return;
+ }
+ item->mCategory = entry.category();
+ item->mCount = entry.count();
+ }
+
+ PerformanceInfoDictionary* data = mData.AppendElement(fallible);
+ if (NS_WARN_IF(!data)) {
+ Abort(NS_ERROR_OUT_OF_MEMORY);
+ return;
+ }
+ data->mPid = result.pid();
+ data->mWid = result.wid();
+ data->mPwid = result.pwid();
+ data->mHost = *result.host().get();
+ data->mDuration = result.pid();
+ data->mWorker = result.worker();
+ data->mItems = items;
+ }
+
+ mPendingResults--;
+ if (mPendingResults) {
+ return;
+ }
+
+ LOG(("[%s] All data collected, resolving promise", nsIDToCString(mUUID).get()));
+ mPromise->MaybeResolve(mData);
+ mCollector->ForgetAggregatedResults(mUUID);
+}
+
+void
+AggregatedResults::SetNumResultsRequired(uint32_t aNumResultsRequired)
+{
+ MOZ_ASSERT(!mPendingResults && aNumResultsRequired);
+ mPendingResults = aNumResultsRequired;
+}
+
+//
+// class PerformanceMetricsCollector (singleton)
+//
+
+// raw pointer for the singleton
+PerformanceMetricsCollector* gInstance = nullptr;
+
+PerformanceMetricsCollector::~PerformanceMetricsCollector()
+{
+ MOZ_ASSERT(gInstance == this);
+ gInstance = nullptr;
+}
+
+void
+PerformanceMetricsCollector::ForgetAggregatedResults(const nsID& aUUID)
+{
+ MOZ_ASSERT(gInstance);
+ MOZ_ASSERT(XRE_IsParentProcess());
+ // This Remove() call will trigger AggregatedResults DTOR and if its
+ // the last in the table, the DTOR of PerformanceMetricsCollector.
+ // That's why we need to make sure we hold a reference here before the call
+ RefPtr kungFuDeathGrip = this;
+ LOG(("[%s] Removing from the table", nsIDToCString(aUUID).get()));
+ mAggregatedResults.Remove(aUUID);
+}
+
+// static
+void
+PerformanceMetricsCollector::RequestMetrics(dom::Promise* aPromise)
+{
+ MOZ_ASSERT(aPromise);
+ MOZ_ASSERT(XRE_IsParentProcess());
+ RefPtr pmc = gInstance;
+ if (!pmc) {
+ pmc = new PerformanceMetricsCollector();
+ gInstance = pmc;
+ }
+ pmc->RequestMetricsInternal(aPromise);
+}
+
+void
+PerformanceMetricsCollector::RequestMetricsInternal(dom::Promise* aPromise)
+{
+ // each request has its own UUID
+ nsID uuid;
+ nsresult rv = nsContentUtils::GenerateUUIDInPlace(uuid);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ aPromise->MaybeReject(rv);
+ return;
+ }
+
+ LOG(("[%s] Requesting Performance Metrics", nsIDToCString(uuid).get()));
+
+ // Getting all content processes
+ nsTArray children;
+ ContentParent::GetAll(children);
+
+ uint32_t numChildren = children.Length();
+ LOG(("[%s] Expecting %d results back", nsIDToCString(uuid).get(), numChildren + 1));
+
+ // keep track of all results in an AggregatedResults instance
+ UniquePtr results = MakeUnique(uuid, this, aPromise);
+
+ // We want to get back as many results as children, plus the result
+ // from the content parent itself
+ results->SetNumResultsRequired(numChildren + 1);
+ mAggregatedResults.Put(uuid, std::move(results));
+
+ // calling all content processes via IPDL (async)
+ for (uint32_t i = 0; i < numChildren; i++) {
+ if (NS_WARN_IF(!children[i]->SendRequestPerformanceMetrics(uuid))) {
+ LOG(("[%s] Failed to send request to child %d", nsIDToCString(uuid).get(), i));
+ mAggregatedResults.GetValue(uuid)->get()->Abort(NS_ERROR_FAILURE);
+ return;
+ }
+ }
+
+ // collecting the current process PerformanceInfo
+ nsTArray info;
+ CollectPerformanceInfo(info);
+ DataReceived(uuid, info);
+}
+
+
+// static
+nsresult
+PerformanceMetricsCollector::DataReceived(const nsID& aUUID,
+ const nsTArray& aMetrics)
+{
+ MOZ_ASSERT(gInstance);
+ MOZ_ASSERT(XRE_IsParentProcess());
+ return gInstance->DataReceivedInternal(aUUID, aMetrics);
+}
+
+nsresult
+PerformanceMetricsCollector::DataReceivedInternal(const nsID& aUUID,
+ const nsTArray& aMetrics)
+{
+ MOZ_ASSERT(gInstance == this);
+ UniquePtr* results = mAggregatedResults.GetValue(aUUID);
+ if (!results) {
+ return NS_ERROR_FAILURE;
+ }
+
+ LOG(("[%s] Received one PerformanceInfo array", nsIDToCString(aUUID).get()));
+ AggregatedResults* aggregatedResults = results->get();
+ MOZ_ASSERT(aggregatedResults);
+
+ // If this is the last result, APpendResult() will trigger the deletion
+ // of this collector, nothing should be done after this line.
+ aggregatedResults->AppendResult(aMetrics);
+ return NS_OK;
+}
+
+} // namespace
diff --git a/toolkit/components/perfmonitoring/PerformanceMetricsCollector.h b/toolkit/components/perfmonitoring/PerformanceMetricsCollector.h
new file mode 100644
index 000000000000..926157cffc4b
--- /dev/null
+++ b/toolkit/components/perfmonitoring/PerformanceMetricsCollector.h
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef PerformanceMetricsCollector_h
+#define PerformanceMetricsCollector_h
+
+#include "nsID.h"
+#include "mozilla/dom/ChromeUtilsBinding.h" // defines PerformanceInfoDictionary
+#include "mozilla/dom/DOMTypes.h" // defines PerformanceInfo
+
+namespace mozilla {
+
+namespace dom {
+ class Promise;
+}
+
+class PerformanceMetricsCollector;
+
+// AggregatedResults receives PerformanceInfo results that are collected
+// via IPDL from all content processes and the main process. They
+// are converted into an array of PerformanceInfoDictionary dictionaries (webidl)
+//
+// The class is instanciated with a Promise and a number of processes
+// that are supposed to send back results.
+//
+// Once every process have sent back its results, AggregatedResults will
+// resolve the promise with all the collected data and send back the
+// dictionnary.
+//
+class AggregatedResults final
+{
+public:
+ AggregatedResults(nsID aUUID, PerformanceMetricsCollector* aCollector,
+ dom::Promise* aPromise);
+ ~AggregatedResults() = default;
+ void AppendResult(const nsTArray& aMetrics);
+ void SetNumResultsRequired(uint32_t aNumResultsRequired);
+ void Abort(nsresult aReason);
+
+private:
+ RefPtr mPromise;
+ uint32_t mPendingResults;
+ FallibleTArray mData;
+
+ // AggregatedResults keeps a reference on the collector
+ // so it gets destructed when all pending AggregatedResults
+ // are themselves destructed when removed from
+ // PerformanceMetricsCollector::mAggregatedResults.
+ //
+ // This lifecycle ensures that everything is released once
+ // all pending results are sent.
+ RefPtr mCollector;
+ nsID mUUID;
+};
+
+//
+// PerformanceMetricsCollector is instanciated as a singleton, and creates
+// one AggregatedResults instance everytime metrics are requested.
+//
+// Each AggregatedResults has a unique identifier (UUID) that is used
+// to send metrics requests via IPDL. When metrics are back in an
+// asynchronous fashion, the UUID is used to append the data to the
+// right AggregatedResults instance and eventually let it resolve the
+// linked promise.
+//
+class PerformanceMetricsCollector final
+{
+public:
+ NS_INLINE_DECL_REFCOUNTING(PerformanceMetricsCollector)
+
+ static void RequestMetrics(dom::Promise* aPromise);
+ static nsresult DataReceived(const nsID& aUUID,
+ const nsTArray& aMetrics);
+ void ForgetAggregatedResults(const nsID& aUUID);
+
+private:
+ ~PerformanceMetricsCollector();
+ void RequestMetricsInternal(dom::Promise* aPromise);
+ nsresult DataReceivedInternal(const nsID& aUUID,
+ const nsTArray& aMetrics);
+ nsDataHashtable> mAggregatedResults;
+};
+
+} // namespace mozilla
+#endif // PerformanceMetricsCollector_h
diff --git a/toolkit/components/perfmonitoring/PerformanceUtils.cpp b/toolkit/components/perfmonitoring/PerformanceUtils.cpp
index 3643839dbb21..f094db111d5a 100644
--- a/toolkit/components/perfmonitoring/PerformanceUtils.cpp
+++ b/toolkit/components/perfmonitoring/PerformanceUtils.cpp
@@ -4,8 +4,6 @@
* 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 "nsIMutableArray.h"
-#include "nsPerformanceMetrics.h"
#include "nsThreadUtils.h"
#include "mozilla/PerformanceUtils.h"
#include "mozilla/dom/DocGroup.h"
@@ -41,50 +39,4 @@ CollectPerformanceInfo(nsTArray& aMetrics)
}
}
-nsresult
-NotifyPerformanceInfo(const nsTArray& aMetrics)
-{
- nsresult rv;
-
- nsCOMPtr array = do_CreateInstance(NS_ARRAY_CONTRACTID);
- if (NS_WARN_IF(!array)) {
- return NS_ERROR_FAILURE;
- }
-
- // Each PerformanceInfo is converted into a nsIPerformanceMetricsData
- for (const PerformanceInfo& info : aMetrics) {
- nsCOMPtr items = do_CreateInstance(NS_ARRAY_CONTRACTID);
- if (NS_WARN_IF(!items)) {
- return rv;
- }
- for (const CategoryDispatch& entry : info.items()) {
- nsCOMPtr item =
- new PerformanceMetricsDispatchCategory(entry.category(),
- entry.count());
- rv = items->AppendElement(item);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- nsCOMPtr data;
- data = new PerformanceMetricsData(info.pid(), info.wid(), info.pwid(),
- info.host(), info.duration(),
- info.worker(), items);
- rv = array->AppendElement(data);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- nsCOMPtr obs = mozilla::services::GetObserverService();
- if (NS_WARN_IF(!obs)) {
- return NS_ERROR_FAILURE;
- }
-
- rv = obs->NotifyObservers(array, "performance-metrics", nullptr);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
-}
-
} // namespace
diff --git a/toolkit/components/perfmonitoring/PerformanceUtils.h b/toolkit/components/perfmonitoring/PerformanceUtils.h
index 71a4d3e478ab..42505f1adf6f 100644
--- a/toolkit/components/perfmonitoring/PerformanceUtils.h
+++ b/toolkit/components/perfmonitoring/PerformanceUtils.h
@@ -3,8 +3,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#ifndef PerformanceCollector_h
-#define PerformanceCollector_h
+#ifndef PerformanceUtils_h
+#define PerformanceUtils_h
#include "mozilla/dom/DOMTypes.h" // defines PerformanceInfo
@@ -12,15 +12,9 @@ namespace mozilla {
/**
* Collects all performance info in the current process
- * and adds then in the aMetrics arrey
+ * and adds then in the aMetrics array
*/
void CollectPerformanceInfo(nsTArray& aMetrics);
-/**
- * Converts a PerformanceInfo array into a nsIPerformanceMetricsData and
- * sends a performance-metrics notification with it
- */
-nsresult NotifyPerformanceInfo(const nsTArray& aMetrics);
-
} // namespace mozilla
-#endif // PerformanceCollector_h
+#endif // PerformanceUtils_h
diff --git a/toolkit/components/perfmonitoring/moz.build b/toolkit/components/perfmonitoring/moz.build
index ebc3bd8d192d..3423c92a040e 100644
--- a/toolkit/components/perfmonitoring/moz.build
+++ b/toolkit/components/perfmonitoring/moz.build
@@ -27,10 +27,12 @@ UNIFIED_SOURCES += [
]
UNIFIED_SOURCES += [
+ 'PerformanceMetricsCollector.cpp',
'PerformanceUtils.cpp'
]
EXPORTS.mozilla += [
+ 'PerformanceMetricsCollector.h',
'PerformanceUtils.h'
]
diff --git a/toolkit/components/telemetry/docs/collection/hybrid-content.rst b/toolkit/components/telemetry/docs/collection/hybrid-content.rst
index b023a3062143..a4a264443d53 100644
--- a/toolkit/components/telemetry/docs/collection/hybrid-content.rst
+++ b/toolkit/components/telemetry/docs/collection/hybrid-content.rst
@@ -339,6 +339,10 @@ To enable Hybrid Content Telemetry on ``https://example.mozilla.org``, execute t
Afterwards load the page on ``https://example.mozilla.org`` and it will be able to record Telemetry data.
+.. note::
+
+ Manual testing requires a host that handles HTTPS connections, as this kind of collection is only allowed on secure hosts. To allow for local testing, a local proxy capable of handling HTTPS connection is required.
+
Automated testing
-----------------
diff --git a/toolkit/modules/LightweightThemeConsumer.jsm b/toolkit/modules/LightweightThemeConsumer.jsm
index 6a510805c8a1..0df65d9b6b94 100644
--- a/toolkit/modules/LightweightThemeConsumer.jsm
+++ b/toolkit/modules/LightweightThemeConsumer.jsm
@@ -118,6 +118,7 @@ function LightweightThemeConsumer(aDocument) {
this._win.addEventListener("resolutionchange", this);
this._win.addEventListener("unload", this, { once: true });
+ this._win.addEventListener("EndSwapDocShells", this, true);
this._win.messageManager.addMessageListener("LightweightTheme:Request", this);
let darkThemeMediaQuery = this._win.matchMedia("(-moz-system-dark-theme)");
@@ -163,8 +164,13 @@ LightweightThemeConsumer.prototype = {
case "unload":
Services.obs.removeObserver(this, "lightweight-theme-styling-update");
this._win.removeEventListener("resolutionchange", this);
+ this._win.removeEventListener("EndSwapDocShells", this, true);
this._win = this._doc = null;
break;
+ case "EndSwapDocShells":
+ let contentThemeData = _getContentProperties(this._doc, this._active, this._lastData);
+ aEvent.target.messageManager.sendAsyncMessage("LightweightTheme:Update", contentThemeData);
+ break;
}
},
diff --git a/toolkit/modules/NewTabUtils.jsm b/toolkit/modules/NewTabUtils.jsm
index 2cf4a88df712..88828c77cf3b 100644
--- a/toolkit/modules/NewTabUtils.jsm
+++ b/toolkit/modules/NewTabUtils.jsm
@@ -1089,6 +1089,8 @@ var ActivityStreamProvider = {
* {bool} ignoreBlocked: Do not filter out blocked links.
* {int} numItems: Maximum number of items to return.
* {int} topsiteFrecency: Minimum amount of frecency for a site.
+ * {bool} onePerDomain: Dedupe the resulting list.
+ * {bool} includeFavicon: Include favicons if available.
*
* @returns {Promise} Returns a promise with the array of links as payload.
*/
@@ -1096,13 +1098,17 @@ var ActivityStreamProvider = {
const options = Object.assign({
ignoreBlocked: false,
numItems: ACTIVITY_STREAM_DEFAULT_LIMIT,
- topsiteFrecency: ACTIVITY_STREAM_DEFAULT_FRECENCY
+ topsiteFrecency: ACTIVITY_STREAM_DEFAULT_FRECENCY,
+ onePerDomain: true,
+ includeFavicon: true,
}, aOptions || {});
// Double the item count in case the host is deduped between with www or
// not-www (i.e., 2 hosts) and an extra buffer for multiple pages per host.
const origNumItems = options.numItems;
- options.numItems *= 2 * 10;
+ if (options.onePerDomain) {
+ options.numItems *= 2 * 10;
+ }
// Keep this query fast with frecency-indexed lookups (even with excess
// rows) and shift the more complex logic to post-processing afterwards
@@ -1114,7 +1120,8 @@ var ActivityStreamProvider = {
last_visit_date / 1000 AS lastVisitDate,
rev_host,
title,
- url
+ url,
+ "history" as type
FROM moz_places h
WHERE frecency >= :frecencyThreshold
${this._commonPlacesWhere}
@@ -1129,7 +1136,8 @@ var ActivityStreamProvider = {
"guid",
"lastVisitDate",
"title",
- "url"
+ "url",
+ "type"
],
params: this._getCommonParams(options, {
frecencyThreshold: options.topsiteFrecency
@@ -1157,32 +1165,37 @@ var ActivityStreamProvider = {
map.set(host, link);
}
- // Clean up the returned links by removing blocked, deduping, etc.
- const exactHosts = new Map();
- for (const link of links) {
- if (!options.ignoreBlocked && BlockedLinks.isBlocked(link)) {
- continue;
+ // Remove any blocked links.
+ if (!options.ignoreBlocked) {
+ links = links.filter(link => !BlockedLinks.isBlocked(link));
+ }
+
+ if (options.onePerDomain) {
+ // De-dup the links.
+ const exactHosts = new Map();
+ for (const link of links) {
+ // First we want to find the best link for an exact host
+ setBetterLink(exactHosts, link, url => url.match(/:\/\/([^\/]+)/));
}
- link.type = "history";
+ // Clean up exact hosts to dedupe as non-www hosts
+ const hosts = new Map();
+ for (const link of exactHosts.values()) {
+ setBetterLink(hosts, link, url => url.match(/:\/\/(?:www\.)?([^\/]+)/),
+ // Combine frecencies when deduping these links
+ (targetLink, otherLink) => {
+ targetLink.frecency = link.frecency + otherLink.frecency;
+ });
+ }
- // First we want to find the best link for an exact host
- setBetterLink(exactHosts, link, url => url.match(/:\/\/([^\/]+)/));
+ links = [...hosts.values()];
}
-
- // Clean up exact hosts to dedupe as non-www hosts
- const hosts = new Map();
- for (const link of exactHosts.values()) {
- setBetterLink(hosts, link, url => url.match(/:\/\/(?:www\.)?([^\/]+)/),
- // Combine frecencies when deduping these links
- (targetLink, otherLink) => {
- targetLink.frecency = link.frecency + otherLink.frecency;
- });
- }
-
// Pick out the top links using the same comparer as before
- links = [...hosts.values()].sort(isOtherBetter).slice(0, origNumItems);
+ links = links.sort(isOtherBetter).slice(0, origNumItems);
+ if (!options.includeFavicon) {
+ return links;
+ }
// Get the favicons as data URI for now (until we use the favicon protocol)
return this._faviconBytesToDataURI(await this._addFavicons(links));
},
@@ -1289,14 +1302,15 @@ var ActivityStreamLinks = {
* @param {Object} aData
* aData.url The url to bookmark
* aData.title The title of the page to bookmark
- * @param {Browser} aBrowser
- * a element
+ * @param {Window} aBrowserWindow
+ * The current browser chrome window
*
* @returns {Promise} Returns a promise set to an object representing the bookmark
*/
- addBookmark(aData, aBrowser) {
+ addBookmark(aData, aBrowserWindow) {
const {url, title} = aData;
- return aBrowser.ownerGlobal.PlacesCommandHook.bookmarkPage(
+ return aBrowserWindow.PlacesCommandHook.bookmarkLink(
+ PlacesUtils.bookmarksMenuFolderId,
url,
title);
},
diff --git a/toolkit/modules/addons/SecurityInfo.jsm b/toolkit/modules/addons/SecurityInfo.jsm
index 8c5cef18754d..a931602b517a 100644
--- a/toolkit/modules/addons/SecurityInfo.jsm
+++ b/toolkit/modules/addons/SecurityInfo.jsm
@@ -137,10 +137,14 @@ const SecurityInfo = {
info.cipherSuite = SSLStatus.cipherName;
// Key exchange group name.
- info.keaGroupName = SSLStatus.keaGroupName;
+ if (SSLStatus.keaGroupName !== "none") {
+ info.keaGroupName = SSLStatus.keaGroupName;
+ }
// Certificate signature scheme.
- info.signatureSchemeName = SSLStatus.signatureSchemeName;
+ if (SSLStatus.signatureSchemeName !== "none") {
+ info.signatureSchemeName = SSLStatus.signatureSchemeName;
+ }
info.isDomainMismatch = SSLStatus.isDomainMismatch;
info.isExtendedValidation = SSLStatus.isExtendedValidation;
diff --git a/toolkit/modules/tests/xpcshell/test_NewTabUtils.js b/toolkit/modules/tests/xpcshell/test_NewTabUtils.js
index 3903d34b6827..32dbf655d166 100644
--- a/toolkit/modules/tests/xpcshell/test_NewTabUtils.js
+++ b/toolkit/modules/tests/xpcshell/test_NewTabUtils.js
@@ -679,6 +679,31 @@ add_task(async function getTopFrecentSites() {
Assert.equal(links[0].url, testURI, "added visit corresponds to added url");
});
+add_task(async function getTopFrecentSites_no_dedup() {
+ await setUpActivityStreamTest();
+
+ let provider = NewTabUtils.activityStreamLinks;
+ let links = await provider.getTopSites({topsiteFrecency: 100});
+ Assert.equal(links.length, 0, "empty history yields empty links");
+
+ // Add a visits in reverse order they will be returned in when not deduped.
+ let testURIs = [{uri: "http://www.mozilla.com/"}, {uri: "http://mozilla.com/"}];
+ await PlacesTestUtils.addVisits(testURIs);
+
+ links = await provider.getTopSites();
+ Assert.equal(links.length, 0, "adding a single visit doesn't exceed default threshold");
+
+ links = await provider.getTopSites({topsiteFrecency: 100});
+ Assert.equal(links.length, 1, "adding a visit yields a link");
+ // Plain domain is returned when deduped.
+ Assert.equal(links[0].url, testURIs[1].uri, "added visit corresponds to added url");
+
+ links = await provider.getTopSites({topsiteFrecency: 100, onePerDomain: false});
+ Assert.equal(links.length, 2, "adding a visit yields a link");
+ Assert.equal(links[0].url, testURIs[1].uri, "added visit corresponds to added url");
+ Assert.equal(links[1].url, testURIs[0].uri, "added visit corresponds to added url");
+});
+
add_task(async function getTopFrecentSites_dedupeWWW() {
await setUpActivityStreamTest();
diff --git a/tools/lint/python/flake8.py b/tools/lint/python/flake8.py
index 46a066370662..1d56be020819 100644
--- a/tools/lint/python/flake8.py
+++ b/tools/lint/python/flake8.py
@@ -124,6 +124,7 @@ def lint(paths, config, **lintargs):
os.path.join(bindir, 'flake8'),
'--format', '{"path":"%(path)s","lineno":%(row)s,'
'"column":%(col)s,"rule":"%(code)s","message":"%(text)s"}',
+ '--filename', ','.join(['*.{}'.format(e) for e in config['extensions']]),
]
fix_cmdargs = [
diff --git a/tools/lint/test/files/flake8/ext/bad.configure b/tools/lint/test/files/flake8/ext/bad.configure
new file mode 100644
index 000000000000..8214ebb3c06d
--- /dev/null
+++ b/tools/lint/test/files/flake8/ext/bad.configure
@@ -0,0 +1,2 @@
+# unused import
+import os
diff --git a/tools/lint/test/test_flake8.py b/tools/lint/test/test_flake8.py
index cfb23a816071..dc3b9ba4fa7f 100644
--- a/tools/lint/test/test_flake8.py
+++ b/tools/lint/test/test_flake8.py
@@ -80,5 +80,10 @@ def test_lint_excluded_file(lint, paths):
assert len(results) == 0
+def test_lint_uses_custom_extensions(lint, paths):
+ assert len(lint(paths('ext'))) == 1
+ assert len(lint(paths('ext/bad.configure'))) == 1
+
+
if __name__ == '__main__':
mozunit.main()
diff --git a/xpfe/appshell/nsXULWindow.cpp b/xpfe/appshell/nsXULWindow.cpp
index 2552469a9b47..46a98e55ed97 100644
--- a/xpfe/appshell/nsXULWindow.cpp
+++ b/xpfe/appshell/nsXULWindow.cpp
@@ -891,7 +891,8 @@ NS_IMETHODIMP nsXULWindow::SetVisibility(bool aVisibility)
nsCOMPtr obssvc = services::GetObserverService();
NS_ASSERTION(obssvc, "Couldn't get observer service.");
if (obssvc) {
- obssvc->NotifyObservers(nullptr, "xul-window-visible", nullptr);
+ obssvc->NotifyObservers(static_cast(this),
+ "xul-window-visible", nullptr);
}
mDebuting = false;