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

This commit is contained in:
Bogdan Tara 2018-07-07 01:11:36 +03:00
commit d01e0f82de
84 changed files with 25226 additions and 23107 deletions

View File

@ -4,9 +4,13 @@ ignore = E121, E123, E126, E129, E133, E226, E241, E242, E704, W503, E402, E741
max-line-length = 99
exclude =
browser/extensions/mortar/ppapi/,
browser/moz.configure,
build/moz.configure/*.configure,
build/pymake/,
js/*.configure,
memory/moz.configure,
mobile/android/*.configure,
node_modules,
security/nss/,
testing/mochitest/pywebsocket,
tools/lint/test/files,

View File

@ -45,12 +45,6 @@ var gIdentityHandler = {
*/
_state: 0,
/**
* This flag gets set if the identity popup was opened by a keypress,
* to be able to focus it on the popupshown event.
*/
_popupTriggeredByKeyboard: false,
/**
* RegExp used to decide if an about url should be shown as being part of
* the browser UI.
@ -831,7 +825,14 @@ var gIdentityHandler = {
return;
}
this._popupTriggeredByKeyboard = event.type == "keypress";
// Move focus to the next available element in the identity popup.
// This is required by role=alertdialog and fixes an issue where
// an already open panel would steal focus from the identity popup.
if (event.type == "keypress") {
let panelView = PanelView.forNode(this._identityPopupMainView);
this._identityPopupMainView.addEventListener("ViewShown", () => panelView.focusFirstNavigableElement(),
{once: true});
}
// Make sure that the display:none style we set in xul is removed now that
// the popup is actually needed
@ -853,13 +854,6 @@ var gIdentityHandler = {
onPopupShown(event) {
if (event.target == this._identityPopup) {
if (this._popupTriggeredByKeyboard) {
// Move focus to the next available element in the identity popup.
// This is required by role=alertdialog and fixes an issue where
// an already open panel would steal focus from the identity popup.
document.commandDispatcher.advanceFocusIntoSubtree(this._identityPopup);
}
window.addEventListener("focus", this, true);
}
},

View File

@ -87,6 +87,19 @@ const whitelist = {
]),
};
// Items on this list are allowed to be loaded but not
// required, as opposed to items in the main whitelist,
// which are all required.
const intermittently_loaded_whitelist = {
components: new Set([
"nsAsyncShutdown.js",
]),
modules: new Set([
"resource://gre/modules/sessionstore/Utils.jsm",
"resource://gre/modules/TelemetryStopwatch.jsm",
]),
};
const blacklist = {
services: new Set([
"@mozilla.org/base/telemetry-startup;1",
@ -145,6 +158,10 @@ add_task(async function() {
return false;
});
loadedList[scriptType] = loadedList[scriptType].filter(c => {
return !intermittently_loaded_whitelist[scriptType].has(c);
});
is(loadedList[scriptType].length, 0,
`should have no unexpected ${scriptType} loaded on content process startup`);

View File

@ -15,6 +15,7 @@ add_task(async function() {
// Ensure all the frame data is in the test compartment to avoid traversing
// a cross compartment wrapper for each pixel.
let frames = Cu.cloneInto(startupRecorder.data.frames, {});
ok(frames.length > 0, "Should have captured some frames.");
let unexpectedRects = 0;
let alreadyFocused = false;

View File

@ -47,7 +47,7 @@
</vbox>
</vbox>
<button id="identity-popup-security-expander"
class="identity-popup-expander"
class="identity-popup-expander subviewkeynav"
when-connection="not-secure secure secure-ev secure-cert-user-overridden"
oncommand="gIdentityHandler.showSecuritySubView();"/>
</hbox>
@ -84,22 +84,22 @@
crop="end">&trackingProtection.reloadRequired2;</description>
<button id="tracking-action-reload"
class="tracking-protection-button"
class="tracking-protection-button subviewkeynav"
label="&trackingProtection.reload2.label;"
accesskey="&trackingProtection.reload2.accesskey;"
oncommand="TrackingProtection.hideIdentityPopupAndReload();" />
<button id="tracking-action-unblock"
class="tracking-protection-button"
class="tracking-protection-button subviewkeynav"
label="&trackingProtection.unblock3.label;"
accesskey="&trackingProtection.unblock3.accesskey;"
oncommand="TrackingProtection.disableForCurrentPage();" />
<button id="tracking-action-unblock-private"
class="tracking-protection-button"
class="tracking-protection-button subviewkeynav"
label="&trackingProtection.unblockPrivate3.label;"
accesskey="&trackingProtection.unblockPrivate3.accesskey;"
oncommand="TrackingProtection.disableForCurrentPage();" />
<button id="tracking-action-block"
class="tracking-protection-button"
class="tracking-protection-button subviewkeynav"
label="&trackingProtection.block4.label;"
accesskey="&trackingProtection.block4.accesskey;"
oncommand="TrackingProtection.enableForCurrentPage();" />

View File

@ -1363,7 +1363,7 @@ var PanelView = class extends AssociatedToNode {
*
* @return {Array}
*/
getNavigableElements() {
_getNavigableElements() {
let buttons = Array.from(this.node.querySelectorAll(
".subviewbutton:not([disabled]), .subviewkeynav:not([disabled])"));
let dwu = this._dwu;
@ -1379,7 +1379,7 @@ var PanelView = class extends AssociatedToNode {
* undefined at any time.
*
* The element is usually, but not necessarily, in the "buttons" property
* which in turn is initialized from the getNavigableElements list.
* which in turn is initialized from the _getNavigableElements list.
*/
get selectedElement() {
return this._selectedElement && this._selectedElement.get();
@ -1392,6 +1392,15 @@ var PanelView = class extends AssociatedToNode {
}
}
/**
* Focuses and moves keyboard selection to the first navigable element.
* This is a no-op if there are no navigable elements.
*/
focusFirstNavigableElement() {
this.selectedElement = this._getNavigableElements()[0];
this.focusSelectedElement();
}
/**
* Based on going up or down, select the previous or next focusable button.
*
@ -1472,7 +1481,7 @@ var PanelView = class extends AssociatedToNode {
let buttons = this.buttons;
if (!buttons || !buttons.length) {
buttons = this.buttons = this.getNavigableElements();
buttons = this.buttons = this._getNavigableElements();
// Set the 'tabindex' attribute on the buttons to make sure they're focussable.
for (let button of buttons) {
if (!button.classList.contains("subviewbutton-back") &&

View File

@ -10,7 +10,7 @@ const kHelpButtonId = "appMenu-help-button";
add_task(async function testUpDownKeys() {
await gCUITestUtils.openMainMenu();
let buttons = PanelView.forNode(PanelUI.mainView).getNavigableElements();
let buttons = PanelView.forNode(PanelUI.mainView)._getNavigableElements();
for (let button of buttons) {
if (button.disabled)
@ -39,7 +39,7 @@ add_task(async function testUpDownKeys() {
add_task(async function testEnterKeyBehaviors() {
await gCUITestUtils.openMainMenu();
let buttons = PanelView.forNode(PanelUI.mainView).getNavigableElements();
let buttons = PanelView.forNode(PanelUI.mainView)._getNavigableElements();
// Navigate to the 'Help' button, which points to a subview.
EventUtils.synthesizeKey("KEY_ArrowUp");
@ -56,7 +56,7 @@ add_task(async function testEnterKeyBehaviors() {
EventUtils.synthesizeKey("KEY_Enter");
await promise;
let helpButtons = PanelView.forNode(PanelUI.helpView).getNavigableElements();
let helpButtons = PanelView.forNode(PanelUI.helpView)._getNavigableElements();
Assert.ok(helpButtons[0].classList.contains("subviewbutton-back"),
"First button in help view should be a back button");
@ -132,7 +132,7 @@ add_task(async function testLeftRightKeys() {
add_task(async function testTabKey() {
await gCUITestUtils.openMainMenu();
let buttons = PanelView.forNode(PanelUI.mainView).getNavigableElements();
let buttons = PanelView.forNode(PanelUI.mainView)._getNavigableElements();
for (let button of buttons) {
if (button.disabled)
@ -165,7 +165,7 @@ add_task(async function testTabKey() {
add_task(async function testInterleavedTabAndArrowKeys() {
await gCUITestUtils.openMainMenu();
let buttons = PanelView.forNode(PanelUI.mainView).getNavigableElements();
let buttons = PanelView.forNode(PanelUI.mainView)._getNavigableElements();
let tab = false;
for (let button of buttons) {
@ -188,7 +188,7 @@ add_task(async function testInterleavedTabAndArrowKeys() {
add_task(async function testSpaceDownAfterTabNavigation() {
await gCUITestUtils.openMainMenu();
let buttons = PanelView.forNode(PanelUI.mainView).getNavigableElements();
let buttons = PanelView.forNode(PanelUI.mainView)._getNavigableElements();
let button;
for (button of buttons) {

View File

@ -281,8 +281,10 @@
where += "-background";
} else {
var newTabPref = Services.prefs.getBoolPref("browser.search.openintab");
if (((aEvent instanceof KeyboardEvent) && aEvent.altKey) ^ newTabPref)
if (((aEvent instanceof KeyboardEvent && aEvent.altKey) ^ newTabPref) &&
!isTabEmpty(gBrowser.selectedTab)) {
where = "tab";
}
if ((aEvent instanceof MouseEvent) &&
(aEvent.button == 1 || aEvent.getModifierState("Accel"))) {
where = "tab";
@ -1783,8 +1785,10 @@
}
} else {
var newTabPref = Services.prefs.getBoolPref("browser.search.openintab");
if (((aEvent instanceof KeyboardEvent) && aEvent.altKey) ^ newTabPref)
if (((aEvent instanceof KeyboardEvent && aEvent.altKey) ^ newTabPref) &&
!isTabEmpty(gBrowser.selectedTab)) {
where = "tab";
}
if ((aEvent instanceof MouseEvent) &&
(aEvent.button == 1 || aEvent.getModifierState("Accel"))) {
where = "tab";

View File

@ -95,6 +95,21 @@ startupRecorder.prototype = {
return;
}
// We only care about the first paint notification for browser windows, and
// not other types (for example, the gfx sanity test window)
if (topic == firstPaintNotification) {
// In the case we're handling xul-window-visible, we'll have been handed
// an nsIXULWindow instead of an nsIDOMWindow.
if (subject instanceof Ci.nsIXULWindow) {
subject = subject.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
}
if (subject.document.documentElement.getAttribute("windowtype") != "navigator:browser") {
return;
}
}
if (topic == "image-drawing" || topic == "image-loading") {
this.data.images[topic].add(data);
return;
@ -104,7 +119,9 @@ startupRecorder.prototype = {
if (topic == firstPaintNotification &&
Services.prefs.getBoolPref("browser.startup.record", false)) {
win = Services.wm.getMostRecentWindow("navigator:browser");
// Because of the check for navigator:browser we made earlier, we know
// that if we got here, then the subject must be the first browser window.
win = subject;
canvas = win.document.createElementNS("http://www.w3.org/1999/xhtml",
"canvas");
canvas.mozOpaque = true;

View File

@ -293,7 +293,7 @@ class PlacesFeed {
break;
}
case at.BOOKMARK_URL:
NewTabUtils.activityStreamLinks.addBookmark(action.data, action._target.browser);
NewTabUtils.activityStreamLinks.addBookmark(action.data, action._target.browser.ownerGlobal);
break;
case at.DELETE_BOOKMARK_BY_ID:
NewTabUtils.activityStreamLinks.deleteBookmark(action.data);

View File

@ -14,6 +14,16 @@ import six
here = os.path.abspath(os.path.dirname(__file__))
try:
# buildconfig doesn't yet support Python 3, so we can use pathlib to
# resolve the topsrcdir relative to our current location.
from pathlib import Path
topsrcdir = Path(here).parents[2]
except ImportError:
from mozbuild.base import MozbuildObject
build = MozbuildObject.from_environment(cwd=here)
topsrcdir = build.topsrcdir
StringIO = six.StringIO
'''Helper to make python unit tests report the way that the Mozilla
@ -234,6 +244,7 @@ def main(*args, **kwargs):
module = __import__('__main__')
args.extend([
'--rootdir', topsrcdir,
'-c', os.path.join(here, 'pytest.ini'),
'-vv',
'-p', 'mozlog.pytest_mozlog.plugin',

View File

@ -101,4 +101,4 @@ const clientEvents = {
newSource
};
exports.setupEvents = setupEvents;
exports.clientEvents = clientEvents;
exports.clientEvents = clientEvents;

View File

@ -205,6 +205,11 @@ networkMenu.sizeGB=%S GB
# unavailable.
networkMenu.sizeUnavailable=
# LOCALIZATION NOTE (networkMenu.sizeUnavailable.title): This is the tooltip
# displayed in the network menu specifying that the transferred size of a
# request is unavailable.
networkMenu.sizeUnavailable.title=Transferred size is not available
# LOCALIZATION NOTE (networkMenu.sizeCached): This is the label displayed
# in the network menu specifying the transferred of a request is
# cached.

View File

@ -15,6 +15,7 @@ const { div } = dom;
const SIZE_CACHED = L10N.getStr("networkMenu.sizeCached");
const SIZE_SERVICE_WORKER = L10N.getStr("networkMenu.sizeServiceWorker");
const SIZE_UNAVAILABLE = L10N.getStr("networkMenu.sizeUnavailable");
const SIZE_UNAVAILABLE_TITLE = L10N.getStr("networkMenu.sizeUnavailable.title");
const UPDATED_TRANSFERRED_PROPS = [
"transferredSize",
"fromCache",
@ -46,8 +47,10 @@ class RequestListColumnTransferredSize extends Component {
text = SIZE_UNAVAILABLE;
}
const title = text == SIZE_UNAVAILABLE ? SIZE_UNAVAILABLE_TITLE : text;
return (
div({ className: "requests-list-column requests-list-transferred", title: text },
div({ className: "requests-list-column requests-list-transferred", title: title },
text
)
);

View File

@ -76,7 +76,7 @@ function AutocompletePopup(toolboxDoc, options = {}) {
}
this._list.className = "devtools-autocomplete-listbox " + theme + "-theme";
this._tooltip.setContent(this._list);
this._tooltip.setContent(this._list, { height: Infinity });
this.onClick = this.onClick.bind(this);
this._list.addEventListener("click", this.onClick);

View File

@ -17,7 +17,7 @@ You should start by upgrading our prop-types library to match the latest version
```bash
git clone https://github.com/facebook/react.git
cd react
git checkout v16.2.0 # or the version you are targetting
git checkout v16.4.1 # or the version you are targetting
```
## Preparing to Build
@ -27,6 +27,7 @@ We need to disable minification and tree shaking as they overcomplicate the upgr
- Open scripts/rollup/build.js
- Find a method called `function getRollupOutputOptions()`
- After `sourcemap: false` add `treeshake: false` and `freeze: false`
- Remove `freeze: !isProduction,` from the same section.
- Change this:
```js
@ -66,40 +67,6 @@ yarn
yarn build
```
### Package Testing Utilities
Go through `build/packages/react-test-renderer` and in each file remove all code meant for a production build e.g.
Change this:
```js
if (process.env.NODE_ENV === 'production') {
module.exports = require('./cjs/react-test-renderer-shallow.production.min.js');
} else {
module.exports = require('./cjs/react-test-renderer-shallow.development.js');
}
```
To this:
```js
module.exports = require('./cjs/react-test-renderer-shallow.development.js');
```
**NOTE: Be sure to remove all `process.env` conditions from inside the files in the cjs folder.**
Also in the cjs folder replace the React require paths to point at the current React version:
```js
var React = require('../../../dist/react.development');
```
From within `build/packages/react-test-renderer`:
```bash
browserify shallow.js -o react-test-renderer-shallow.js --standalone ShallowRenderer
```
### Copy the Files Into your Firefox Repo
```bash
@ -112,7 +79,7 @@ cp build/dist/react.development.js <gecko-dev>/devtools/client/shared/vendor/rea
cp build/dist/react-dom.development.js <gecko-dev>/devtools/client/shared/vendor/react-dom-dev.js
cp build/dist/react-dom-server.browser.development.js <gecko-dev>/devtools/client/shared/vendor/react-dom-server-dev.js
cp build/dist/react-dom-test-utils.development.js <gecko-dev>/devtools/client/shared/vendor/react-dom-test-utils-dev.js
cp build/packages/react-test-renderer/react-test-renderer-shallow.js <gecko-dev>/devtools/client/shared/vendor/react-test-renderer-shallow.js
cp build/dist/react-test-renderer-shallow.production.min.js <gecko-dev>/devtools/client/shared/vendor/react-test-renderer-shallow.js
```
From this point we will no longer need your react repository so feel free to delete it.

View File

@ -1,4 +1,4 @@
/** @license React v16.2.0
/** @license React v16.4.1
* react.development.js
*
* Copyright (c) 2013-present, Facebook, Inc.
@ -108,17 +108,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';
@ -134,31 +139,6 @@ function getIteratorFn(maybeIterable) {
return null;
}
/**
* 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.
*/
/**
* 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.
*
*/
var emptyObject = {};
{
Object.freeze(emptyObject);
}
var emptyObject_1 = emptyObject;
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
@ -213,6 +193,59 @@ function invariant(condition, format, a, b, c, d, e, f) {
var invariant_1 = invariant;
// Relying on the `invariant()` implementation lets us
// have preserve the format and params in the www builds.
// 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.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
var emptyObject = {};
{
Object.freeze(emptyObject);
}
var emptyObject_1 = emptyObject;
/**
* Forked from fbjs/warning:
* https://github.com/facebook/fbjs/blob/e66ba20ad5be433eb54423f2b097d829324d9de6/packages/fbjs/src/__forks__/warning.js
@ -368,13 +401,13 @@ var didWarnStateUpdateForUnmountedComponent = {};
function warnNoop(publicInstance, callerName) {
{
var constructor = publicInstance.constructor;
var componentName = constructor && (constructor.displayName || constructor.name) || 'ReactClass';
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);
warning_1(false, "Can't call %s on a component that is not yet mounted. " + 'This is a no-op, but it might indicate a bug in your application. ' + 'Instead, assign to `this.state` directly or define a `state = {};` ' + 'class property with the desired state in the %s component.', callerName, componentName);
didWarnStateUpdateForUnmountedComponent[warningKey] = true;
}
}
@ -534,46 +567,36 @@ Component.prototype.forceUpdate = function (callback) {
}
}
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
};
{
Object.seal(refObject);
}
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.
*
@ -597,8 +620,8 @@ var RESERVED_PROPS = {
__source: true
};
var specialPropKeyWarningShown;
var specialPropRefWarningShown;
var specialPropKeyWarningShown = void 0;
var specialPropRefWarningShown = void 0;
function hasValidRef(config) {
{
@ -674,7 +697,7 @@ function defineRefPropWarningGetter(props, displayName) {
*/
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
@ -733,7 +756,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 = {};
@ -821,7 +844,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) ? invariant_1(false, 'React.cloneElement(...): The argument must be a React element, but you passed %s.', element) : void 0;
var propName = void 0;
// Original props are copied
var props = objectAssign({}, element.props);
@ -850,7 +875,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;
}
@ -1004,8 +1029,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;
}
@ -1020,8 +1043,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;
@ -1037,13 +1060,13 @@ function traverseAllChildrenImpl(children, nameSoFar, callback, traverseContext)
{
// 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 ? warning_1(false, '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()) : void 0;
didWarnAboutMaps = true;
}
}
var iterator = iteratorFn.call(children);
var step;
var step = void 0;
var ii = 0;
while (!(step = iterator.next()).done) {
child = step.value;
@ -1115,7 +1138,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.
@ -1167,7 +1190,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.
@ -1190,12 +1213,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);
}
@ -1203,7 +1226,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 = [];
@ -1215,7 +1238,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
@ -1230,18 +1253,105 @@ function onlyChild(children) {
return children;
}
function createContext(defaultValue, calculateChangedBits) {
if (calculateChangedBits === undefined) {
calculateChangedBits = null;
} else {
{
!(calculateChangedBits === null || typeof calculateChangedBits === 'function') ? warning_1(false, 'createContext: Expected the optional second argument to be a ' + 'function. Instead received: %s', calculateChangedBits) : void 0;
}
}
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;
{
context._currentRenderer = null;
context._currentRenderer2 = null;
}
return context;
}
function forwardRef(render) {
{
!(typeof render === 'function') ? warning_1(false, 'forwardRef requires a render function but was given %s.', render === null ? 'null' : typeof render) : void 0;
if (render != null) {
!(render.defaultProps == null && render.propTypes == null) ? warning_1(false, 'forwardRef render functions do not support propTypes or defaultProps. ' + 'Did you accidentally pass a React component?') : void 0;
}
}
return {
$$typeof: REACT_FORWARD_REF_TYPE,
render: render
};
}
var describeComponentFrame = function (name, source, ownerName) {
return '\n in ' + (name || 'Unknown') + (source ? ' (at ' + source.fileName.replace(/^.*[\\\/]/, '') + ':' + source.lineNumber + ')' : ownerName ? ' (created by ' + ownerName + ')' : '');
};
function isValidElementType(type) {
return typeof type === 'string' || typeof type === 'function' ||
// Note: its typeof might be other than 'symbol' or 'number' if it's a polyfill.
type === REACT_FRAGMENT_TYPE || type === REACT_ASYNC_MODE_TYPE || type === REACT_PROFILER_TYPE || type === REACT_STRICT_MODE_TYPE || type === REACT_TIMEOUT_TYPE || typeof type === 'object' && type !== null && (type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE);
}
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;
}
@ -1326,26 +1436,38 @@ var checkPropTypes_1 = checkPropTypes;
* that support it.
*/
var currentlyValidatingElement = void 0;
var propTypesMisspellWarningShown = void 0;
var getDisplayName = function () {};
var getStackAddendum = function () {};
{
var currentlyValidatingElement = null;
currentlyValidatingElement = null;
var propTypesMisspellWarningShown = false;
propTypesMisspellWarningShown = false;
var getDisplayName = function (element) {
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) {
}
var type = element.type;
if (type === REACT_FRAGMENT_TYPE) {
return 'React.Fragment';
} else if (typeof type === 'object' && type !== null && type.$$typeof === REACT_FORWARD_REF_TYPE) {
var functionName = type.render.displayName || type.render.name || '';
return functionName !== '' ? 'ForwardRef(' + functionName + ')' : 'ForwardRef';
} else {
return element.type.displayName || element.type.name || 'Unknown';
return type.displayName || type.name || 'Unknown';
}
};
var getStackAddendum = function () {
getStackAddendum = function () {
var stack = '';
if (currentlyValidatingElement) {
var name = getDisplayName(currentlyValidatingElement);
@ -1355,8 +1477,6 @@ var checkPropTypes_1 = checkPropTypes;
stack += ReactDebugCurrentFrame.getStackAddendum() || '';
return stack;
};
var VALID_FRAGMENT_PROPS = new Map([['children', true], ['key', true]]);
}
function getDeclarationErrorAddendum() {
@ -1469,7 +1589,7 @@ function validateChildKeys(node, parentType) {
// but now we print a separate warning for them later.
if (iteratorFn !== node.entries) {
var iterator = iteratorFn.call(node);
var step;
var step = void 0;
while (!(step = iterator.next()).done) {
if (isValidElement(step.value)) {
validateExplicitKey(step.value, parentType);
@ -1487,22 +1607,31 @@ function validateChildKeys(node, parentType) {
* @param {ReactElement} element
*/
function validatePropTypes(element) {
var componentClass = element.type;
if (typeof componentClass !== 'function') {
var type = element.type;
var name = void 0,
propTypes = void 0;
if (typeof type === 'function') {
// Class or functional component
name = type.displayName || type.name;
propTypes = type.propTypes;
} else if (typeof type === 'object' && type !== null && type.$$typeof === REACT_FORWARD_REF_TYPE) {
// ForwardRef
var functionName = type.render.displayName || type.render.name || '';
name = functionName !== '' ? 'ForwardRef(' + functionName + ')' : 'ForwardRef';
propTypes = type.propTypes;
} else {
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) {
} else if (type.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.');
if (typeof type.getDefaultProps === 'function') {
!type.getDefaultProps.isReactClassApproved ? warning_1(false, 'getDefaultProps is only used on classic React.createClass ' + 'definitions. Use a static property named `defaultProps` instead.') : void 0;
}
}
@ -1513,31 +1642,12 @@ function validatePropTypes(element) {
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;
}
var keys = Object.keys(fragment.props);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (key !== 'children' && key !== 'key') {
warning_1(false, 'Invalid prop `%s` supplied to `React.Fragment`. ' + 'React.Fragment can only have `key` and `children` props.%s', key, getStackAddendum());
break;
}
}
@ -1549,7 +1659,8 @@ function validateFragmentProps(fragment) {
}
function createElementWithValidation(type, props, children) {
var validType = typeof type === 'string' || typeof type === 'function' || typeof type === 'symbol' || typeof type === 'number';
var validType = isValidElementType(type);
// 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) {
@ -1567,7 +1678,16 @@ function createElementWithValidation(type, props, children) {
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 typeString = void 0;
if (type === null) {
typeString = 'null';
} else if (Array.isArray(type)) {
typeString = 'array';
} else {
typeString = typeof type;
}
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', typeString, info);
}
var element = createElement.apply(this, arguments);
@ -1589,7 +1709,7 @@ function createElementWithValidation(type, props, children) {
}
}
if (typeof type === 'symbol' && type === REACT_FRAGMENT_TYPE) {
if (type === REACT_FRAGMENT_TYPE) {
validateFragmentProps(element);
} else {
validatePropTypes(element);
@ -1600,9 +1720,8 @@ function createElementWithValidation(type, props, children) {
function createFactoryWithValidation(type) {
var validatedFactory = createElementWithValidation.bind(null, type);
// Legacy hook TODO: Warn if this is accessed
validatedFactory.type = type;
// Legacy hook: remove it
{
Object.defineProperty(validatedFactory, 'type', {
enumerable: false,
@ -1637,11 +1756,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: createElementWithValidation,
cloneElement: cloneElementWithValidation,
@ -1657,6 +1782,10 @@ var React = {
}
};
if (enableSuspense) {
React.Timeout = REACT_TIMEOUT_TYPE;
}
{
objectAssign(React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, {
// These should not be included in production.
@ -1669,7 +1798,7 @@ var React = {
var React$2 = Object.freeze({
var React$2 = ({
default: React
});
@ -1677,7 +1806,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;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/** @license React v16.2.0
/** @license React v16.4.1
* react-dom-test-utils.development.js
*
* Copyright (c) 2013-present, Facebook, Inc.
@ -19,13 +19,6 @@ var ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
var _assign = ReactInternals.assign;
/**
* 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.
*/
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
@ -80,6 +73,9 @@ function invariant(condition, format, a, b, c, d, e, f) {
var invariant_1 = invariant;
// Relying on the `invariant()` implementation lets us
// have preserve the format and params in the www builds.
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
@ -204,6 +200,9 @@ var ReactInternals$1 = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
var ReactCurrentOwner = ReactInternals$1.ReactCurrentOwner;
var ReactDebugCurrentFrame = ReactInternals$1.ReactDebugCurrentFrame;
// 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.
// Before we know whether it is functional or class
var FunctionalComponent = 1;
var ClassComponent = 2;
@ -212,19 +211,22 @@ var HostRoot = 3; // Root of a host tree. Could be nested inside another node.
var HostComponent = 5;
var HostText = 6;
// Don't change these two values:
var NoEffect = 0; // 0b00000000
// 0b00000001
// Don't change these two values. They're used by React Dev Tools.
var NoEffect = /* */0;
// You can change the rest (and add more).
var Placement = 2; // 0b00000010
// 0b00000100
// 0b00000110
// 0b00001000
// 0b00010000
// 0b00100000
// 0b01000000
// 0b10000000
var Placement = /* */2;
// Union of all host effects
var MOUNTING = 1;
var MOUNTED = 2;
@ -238,15 +240,15 @@ function isFiberMountedImpl(fiber) {
if ((node.effectTag & Placement) !== NoEffect) {
return MOUNTING;
}
while (node['return']) {
node = node['return'];
while (node.return) {
node = node.return;
if ((node.effectTag & Placement) !== NoEffect) {
return MOUNTING;
}
}
} else {
while (node['return']) {
node = node['return'];
while (node.return) {
node = node.return;
}
}
if (node.tag === HostRoot) {
@ -284,7 +286,7 @@ function findCurrentFiberUsingSlowPath(fiber) {
var a = fiber;
var b = alternate;
while (true) {
var parentA = a['return'];
var parentA = a.return;
var parentB = parentA ? parentA.alternate : null;
if (!parentA || !parentB) {
// We're at the root.
@ -314,7 +316,7 @@ function findCurrentFiberUsingSlowPath(fiber) {
invariant_1(false, 'Unable to find node on an unmounted component.');
}
if (a['return'] !== b['return']) {
if (a.return !== b.return) {
// The return pointer of A and the return pointer of B point to different
// fibers. We assume that return pointers never criss-cross, so A must
// belong to the child set of A.return, and B must belong to the child
@ -382,7 +384,6 @@ function findCurrentFiberUsingSlowPath(fiber) {
/* eslint valid-typeof: 0 */
var didWarnForAddedNewProperty = false;
var isProxySupported = typeof Proxy === 'function';
var EVENT_POOL_SIZE = 10;
var shouldBeReleasedProperties = ['dispatchConfig', '_targetInst', 'nativeEvent', 'isDefaultPrevented', 'isPropagationStopped', '_dispatchListeners', '_dispatchInstances'];
@ -543,24 +544,26 @@ SyntheticEvent.Interface = EventInterface;
/**
* Helper to reduce boilerplate when creating subclasses.
*
* @param {function} Class
* @param {?object} Interface
*/
SyntheticEvent.augmentClass = function (Class, Interface) {
SyntheticEvent.extend = function (Interface) {
var Super = this;
var E = function () {};
E.prototype = Super.prototype;
var prototype = new E();
function Class() {
return Super.apply(this, arguments);
}
_assign(prototype, Class.prototype);
Class.prototype = prototype;
Class.prototype.constructor = Class;
Class.Interface = _assign({}, Super.Interface, Interface);
Class.augmentClass = Super.augmentClass;
Class.extend = Super.extend;
addEventPoolingTo(Class);
return Class;
};
/** Proxying after everything set on SyntheticEvent
@ -568,6 +571,10 @@ SyntheticEvent.augmentClass = function (Class, Interface) {
* in which some Event properties are set to undefined (GH#10010)
*/
{
var isProxySupported = typeof Proxy === 'function' &&
// https://github.com/facebook/react/issues/12011
!Object.isSealed(new Proxy({}, {}));
if (isProxySupported) {
/*eslint-disable no-func-assign */
SyntheticEvent = new Proxy(SyntheticEvent, {
@ -578,7 +585,7 @@ SyntheticEvent.augmentClass = function (Class, Interface) {
return new Proxy(constructor.apply(that, args), {
set: function (target, prop, value) {
if (prop !== 'isPersistent' && !target.constructor.Interface.hasOwnProperty(prop) && shouldBeReleasedProperties.indexOf(prop) === -1) {
warning_1(didWarnForAddedNewProperty || target.isPersistent(), "This synthetic event is reused for performance reasons. If you're " + "seeing this, you're adding a new property in the synthetic event object. " + 'The property is never released. See ' + 'https://fb.me/react-event-pooling for more information.');
!(didWarnForAddedNewProperty || target.isPersistent()) ? warning_1(false, "This synthetic event is reused for performance reasons. If you're " + "seeing this, you're adding a new property in the synthetic event object. " + 'The property is never released. See ' + 'https://fb.me/react-event-pooling for more information.') : void 0;
didWarnForAddedNewProperty = true;
}
target[prop] = value;
@ -623,7 +630,7 @@ function getPooledWarningPropertyDefinition(propName, getVal) {
function warn(action, result) {
var warningCondition = false;
warning_1(warningCondition, "This synthetic event is reused for performance reasons. If you're seeing this, " + "you're %s `%s` on a released/nullified synthetic event. %s. " + 'If you must keep the original synthetic event around, use event.persist(). ' + 'See https://fb.me/react-event-pooling for more information.', action, propName, result);
!warningCondition ? warning_1(false, "This synthetic event is reused for performance reasons. If you're seeing this, " + "you're %s `%s` on a released/nullified synthetic event. %s. " + 'If you must keep the original synthetic event around, use event.persist(). ' + 'See https://fb.me/react-event-pooling for more information.', action, propName, result) : void 0;
}
}
@ -654,6 +661,14 @@ function addEventPoolingTo(EventConstructor) {
var SyntheticEvent$1 = SyntheticEvent;
// Do not uses the below two methods directly!
// Instead use constants exported from DOMTopLevelEventTypes in ReactDOM.
// (It is the only module that is allowed to access these methods.)
function unsafeCastStringToDOMTopLevelType(topLevelType) {
return topLevelType;
}
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
@ -770,90 +785,101 @@ function getVendorPrefixedEventName(eventName) {
}
}
return '';
return eventName;
}
/**
* Types of raw signals from the browser caught at the top level.
*
* For events like 'submit' which don't consistently bubble (which we
* trap at a lower node than `document`), binding at `document` would
* cause duplicate events so we don't include them here.
* To identify top level events in ReactDOM, we use constants defined by this
* module. This is the only module that uses the unsafe* methods to express
* that the constants actually correspond to the browser event names. This lets
* us save some bundle size by avoiding a top level type -> event name map.
* The rest of ReactDOM code should import top level types from this file.
*/
var topLevelTypes$1 = {
topAbort: 'abort',
topAnimationEnd: getVendorPrefixedEventName('animationend') || 'animationend',
topAnimationIteration: getVendorPrefixedEventName('animationiteration') || 'animationiteration',
topAnimationStart: getVendorPrefixedEventName('animationstart') || 'animationstart',
topBlur: 'blur',
topCancel: 'cancel',
topCanPlay: 'canplay',
topCanPlayThrough: 'canplaythrough',
topChange: 'change',
topClick: 'click',
topClose: 'close',
topCompositionEnd: 'compositionend',
topCompositionStart: 'compositionstart',
topCompositionUpdate: 'compositionupdate',
topContextMenu: 'contextmenu',
topCopy: 'copy',
topCut: 'cut',
topDoubleClick: 'dblclick',
topDrag: 'drag',
topDragEnd: 'dragend',
topDragEnter: 'dragenter',
topDragExit: 'dragexit',
topDragLeave: 'dragleave',
topDragOver: 'dragover',
topDragStart: 'dragstart',
topDrop: 'drop',
topDurationChange: 'durationchange',
topEmptied: 'emptied',
topEncrypted: 'encrypted',
topEnded: 'ended',
topError: 'error',
topFocus: 'focus',
topInput: 'input',
topKeyDown: 'keydown',
topKeyPress: 'keypress',
topKeyUp: 'keyup',
topLoadedData: 'loadeddata',
topLoad: 'load',
topLoadedMetadata: 'loadedmetadata',
topLoadStart: 'loadstart',
topMouseDown: 'mousedown',
topMouseMove: 'mousemove',
topMouseOut: 'mouseout',
topMouseOver: 'mouseover',
topMouseUp: 'mouseup',
topPaste: 'paste',
topPause: 'pause',
topPlay: 'play',
topPlaying: 'playing',
topProgress: 'progress',
topRateChange: 'ratechange',
topScroll: 'scroll',
topSeeked: 'seeked',
topSeeking: 'seeking',
topSelectionChange: 'selectionchange',
topStalled: 'stalled',
topSuspend: 'suspend',
topTextInput: 'textInput',
topTimeUpdate: 'timeupdate',
topToggle: 'toggle',
topTouchCancel: 'touchcancel',
topTouchEnd: 'touchend',
topTouchMove: 'touchmove',
topTouchStart: 'touchstart',
topTransitionEnd: getVendorPrefixedEventName('transitionend') || 'transitionend',
topVolumeChange: 'volumechange',
topWaiting: 'waiting',
topWheel: 'wheel'
};
var TOP_ABORT = unsafeCastStringToDOMTopLevelType('abort');
var TOP_ANIMATION_END = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('animationend'));
var TOP_ANIMATION_ITERATION = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('animationiteration'));
var TOP_ANIMATION_START = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('animationstart'));
var TOP_BLUR = unsafeCastStringToDOMTopLevelType('blur');
var TOP_CAN_PLAY = unsafeCastStringToDOMTopLevelType('canplay');
var TOP_CAN_PLAY_THROUGH = unsafeCastStringToDOMTopLevelType('canplaythrough');
var TOP_CANCEL = unsafeCastStringToDOMTopLevelType('cancel');
var TOP_CHANGE = unsafeCastStringToDOMTopLevelType('change');
var TOP_CLICK = unsafeCastStringToDOMTopLevelType('click');
var TOP_CLOSE = unsafeCastStringToDOMTopLevelType('close');
var TOP_COMPOSITION_END = unsafeCastStringToDOMTopLevelType('compositionend');
var TOP_COMPOSITION_START = unsafeCastStringToDOMTopLevelType('compositionstart');
var TOP_COMPOSITION_UPDATE = unsafeCastStringToDOMTopLevelType('compositionupdate');
var TOP_CONTEXT_MENU = unsafeCastStringToDOMTopLevelType('contextmenu');
var TOP_COPY = unsafeCastStringToDOMTopLevelType('copy');
var TOP_CUT = unsafeCastStringToDOMTopLevelType('cut');
var TOP_DOUBLE_CLICK = unsafeCastStringToDOMTopLevelType('dblclick');
var TOP_DRAG = unsafeCastStringToDOMTopLevelType('drag');
var TOP_DRAG_END = unsafeCastStringToDOMTopLevelType('dragend');
var TOP_DRAG_ENTER = unsafeCastStringToDOMTopLevelType('dragenter');
var TOP_DRAG_EXIT = unsafeCastStringToDOMTopLevelType('dragexit');
var TOP_DRAG_LEAVE = unsafeCastStringToDOMTopLevelType('dragleave');
var TOP_DRAG_OVER = unsafeCastStringToDOMTopLevelType('dragover');
var TOP_DRAG_START = unsafeCastStringToDOMTopLevelType('dragstart');
var TOP_DROP = unsafeCastStringToDOMTopLevelType('drop');
var TOP_DURATION_CHANGE = unsafeCastStringToDOMTopLevelType('durationchange');
var TOP_EMPTIED = unsafeCastStringToDOMTopLevelType('emptied');
var TOP_ENCRYPTED = unsafeCastStringToDOMTopLevelType('encrypted');
var TOP_ENDED = unsafeCastStringToDOMTopLevelType('ended');
var TOP_ERROR = unsafeCastStringToDOMTopLevelType('error');
var TOP_FOCUS = unsafeCastStringToDOMTopLevelType('focus');
var BrowserEventConstants = {
topLevelTypes: topLevelTypes$1
};
var TOP_INPUT = unsafeCastStringToDOMTopLevelType('input');
var TOP_KEY_DOWN = unsafeCastStringToDOMTopLevelType('keydown');
var TOP_KEY_PRESS = unsafeCastStringToDOMTopLevelType('keypress');
var TOP_KEY_UP = unsafeCastStringToDOMTopLevelType('keyup');
var TOP_LOAD = unsafeCastStringToDOMTopLevelType('load');
var TOP_LOAD_START = unsafeCastStringToDOMTopLevelType('loadstart');
var TOP_LOADED_DATA = unsafeCastStringToDOMTopLevelType('loadeddata');
var TOP_LOADED_METADATA = unsafeCastStringToDOMTopLevelType('loadedmetadata');
var TOP_MOUSE_DOWN = unsafeCastStringToDOMTopLevelType('mousedown');
var TOP_MOUSE_MOVE = unsafeCastStringToDOMTopLevelType('mousemove');
var TOP_MOUSE_OUT = unsafeCastStringToDOMTopLevelType('mouseout');
var TOP_MOUSE_OVER = unsafeCastStringToDOMTopLevelType('mouseover');
var TOP_MOUSE_UP = unsafeCastStringToDOMTopLevelType('mouseup');
var TOP_PASTE = unsafeCastStringToDOMTopLevelType('paste');
var TOP_PAUSE = unsafeCastStringToDOMTopLevelType('pause');
var TOP_PLAY = unsafeCastStringToDOMTopLevelType('play');
var TOP_PLAYING = unsafeCastStringToDOMTopLevelType('playing');
var TOP_PROGRESS = unsafeCastStringToDOMTopLevelType('progress');
var TOP_RATE_CHANGE = unsafeCastStringToDOMTopLevelType('ratechange');
var TOP_SCROLL = unsafeCastStringToDOMTopLevelType('scroll');
var TOP_SEEKED = unsafeCastStringToDOMTopLevelType('seeked');
var TOP_SEEKING = unsafeCastStringToDOMTopLevelType('seeking');
var TOP_SELECTION_CHANGE = unsafeCastStringToDOMTopLevelType('selectionchange');
var TOP_STALLED = unsafeCastStringToDOMTopLevelType('stalled');
var TOP_SUSPEND = unsafeCastStringToDOMTopLevelType('suspend');
var TOP_TEXT_INPUT = unsafeCastStringToDOMTopLevelType('textInput');
var TOP_TIME_UPDATE = unsafeCastStringToDOMTopLevelType('timeupdate');
var TOP_TOGGLE = unsafeCastStringToDOMTopLevelType('toggle');
var TOP_TOUCH_CANCEL = unsafeCastStringToDOMTopLevelType('touchcancel');
var TOP_TOUCH_END = unsafeCastStringToDOMTopLevelType('touchend');
var TOP_TOUCH_MOVE = unsafeCastStringToDOMTopLevelType('touchmove');
var TOP_TOUCH_START = unsafeCastStringToDOMTopLevelType('touchstart');
var TOP_TRANSITION_END = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('transitionend'));
var TOP_VOLUME_CHANGE = unsafeCastStringToDOMTopLevelType('volumechange');
var TOP_WAITING = unsafeCastStringToDOMTopLevelType('waiting');
var TOP_WHEEL = unsafeCastStringToDOMTopLevelType('wheel');
// List of events that need to be individually attached to media elements.
// Note that events in this list will *not* be listened to at the top level
// unless they're explicitly whitelisted in `ReactBrowserEventEmitter.listenTo`.
var findDOMNode = ReactDOM.findDOMNode;
var _ReactDOM$__SECRET_IN = ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
@ -865,14 +891,35 @@ var ReactDOMComponentTree = _ReactDOM$__SECRET_IN.ReactDOMComponentTree;
var ReactDOMEventListener = _ReactDOM$__SECRET_IN.ReactDOMEventListener;
var topLevelTypes = BrowserEventConstants.topLevelTypes;
function Event(suffix) {}
/**
* @class ReactTestUtils
*/
/**
* Simulates a top level event being dispatched from a raw event that occurred
* on an `Element` node.
* @param {number} topLevelType A number from `TopLevelEventTypes`
* @param {!Element} node The dom to simulate an event occurring on.
* @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
*/
function simulateNativeEventOnNode(topLevelType, node, fakeNativeEvent) {
fakeNativeEvent.target = node;
ReactDOMEventListener.dispatchEvent(topLevelType, fakeNativeEvent);
}
/**
* Simulates a top level event being dispatched from a raw event that occurred
* on the `ReactDOMComponent` `comp`.
* @param {Object} topLevelType A type from `BrowserEventConstants.topLevelTypes`.
* @param {!ReactDOMComponent} comp
* @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
*/
function simulateNativeEventOnDOMComponent(topLevelType, comp, fakeNativeEvent) {
simulateNativeEventOnNode(topLevelType, findDOMNode(comp), fakeNativeEvent);
}
function findAllInRenderedFiberTreeInternal(fiber, test) {
if (!fiber) {
return [];
@ -891,7 +938,7 @@ function findAllInRenderedFiberTreeInternal(fiber, test) {
}
}
if (node.child) {
node.child['return'] = node;
node.child.return = node;
node = node.child;
continue;
}
@ -899,12 +946,12 @@ function findAllInRenderedFiberTreeInternal(fiber, test) {
return ret;
}
while (!node.sibling) {
if (!node['return'] || node['return'] === currentParent) {
if (!node.return || node.return === currentParent) {
return ret;
}
node = node['return'];
node = node.return;
}
node.sibling['return'] = node['return'];
node.sibling.return = node.return;
node = node.sibling;
}
}
@ -1085,29 +1132,6 @@ var ReactTestUtils = {
return this;
},
/**
* Simulates a top level event being dispatched from a raw event that occurred
* on an `Element` node.
* @param {Object} topLevelType A type from `BrowserEventConstants.topLevelTypes`
* @param {!Element} node The dom to simulate an event occurring on.
* @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
*/
simulateNativeEventOnNode: function (topLevelType, node, fakeNativeEvent) {
fakeNativeEvent.target = node;
ReactDOMEventListener.dispatchEvent(topLevelType, fakeNativeEvent);
},
/**
* Simulates a top level event being dispatched from a raw event that occurred
* on the `ReactDOMComponent` `comp`.
* @param {Object} topLevelType A type from `BrowserEventConstants.topLevelTypes`.
* @param {!ReactDOMComponent} comp
* @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
*/
simulateNativeEventOnDOMComponent: function (topLevelType, comp, fakeNativeEvent) {
ReactTestUtils.simulateNativeEventOnNode(topLevelType, findDOMNode(comp), fakeNativeEvent);
},
nativeTouchData: function (x, y) {
return {
touches: [{ pageX: x, pageY: y }]
@ -1157,17 +1181,16 @@ function makeSimulator(eventType) {
// Normally extractEvent enqueues a state restore, but we'll just always
// do that since we we're by-passing it here.
ReactControlledComponent.enqueueStateRestore(domNode);
EventPluginHub.enqueueEvents(event);
EventPluginHub.processEventQueue(true);
EventPluginHub.runEventsInBatch(event, true);
});
ReactControlledComponent.restoreStateIfNeeded();
};
}
function buildSimulators() {
ReactTestUtils.Simulate = {};
var eventType;
var eventType = void 0;
for (eventType in EventPluginRegistry.eventNameDispatchConfigs) {
/**
* @param {!Element|ReactDOMComponent} domComponentOrNode
@ -1207,32 +1230,33 @@ buildSimulators();
* to dispatch synthetic events.
*/
function makeNativeSimulator(eventType) {
function makeNativeSimulator(eventType, topLevelType) {
return function (domComponentOrNode, nativeEventData) {
var fakeNativeEvent = new Event(eventType);
_assign(fakeNativeEvent, nativeEventData);
if (ReactTestUtils.isDOMComponent(domComponentOrNode)) {
ReactTestUtils.simulateNativeEventOnDOMComponent(eventType, domComponentOrNode, fakeNativeEvent);
simulateNativeEventOnDOMComponent(topLevelType, domComponentOrNode, fakeNativeEvent);
} else if (domComponentOrNode.tagName) {
// Will allow on actual dom nodes.
ReactTestUtils.simulateNativeEventOnNode(eventType, domComponentOrNode, fakeNativeEvent);
simulateNativeEventOnNode(topLevelType, domComponentOrNode, fakeNativeEvent);
}
};
}
Object.keys(topLevelTypes).forEach(function (eventType) {
// Event type is stored as 'topClick' - we transform that to 'click'
var convenienceName = eventType.indexOf('top') === 0 ? eventType.charAt(3).toLowerCase() + eventType.substr(4) : eventType;
[[TOP_ABORT, 'abort'], [TOP_ANIMATION_END, 'animationEnd'], [TOP_ANIMATION_ITERATION, 'animationIteration'], [TOP_ANIMATION_START, 'animationStart'], [TOP_BLUR, 'blur'], [TOP_CAN_PLAY_THROUGH, 'canPlayThrough'], [TOP_CAN_PLAY, 'canPlay'], [TOP_CANCEL, 'cancel'], [TOP_CHANGE, 'change'], [TOP_CLICK, 'click'], [TOP_CLOSE, 'close'], [TOP_COMPOSITION_END, 'compositionEnd'], [TOP_COMPOSITION_START, 'compositionStart'], [TOP_COMPOSITION_UPDATE, 'compositionUpdate'], [TOP_CONTEXT_MENU, 'contextMenu'], [TOP_COPY, 'copy'], [TOP_CUT, 'cut'], [TOP_DOUBLE_CLICK, 'doubleClick'], [TOP_DRAG_END, 'dragEnd'], [TOP_DRAG_ENTER, 'dragEnter'], [TOP_DRAG_EXIT, 'dragExit'], [TOP_DRAG_LEAVE, 'dragLeave'], [TOP_DRAG_OVER, 'dragOver'], [TOP_DRAG_START, 'dragStart'], [TOP_DRAG, 'drag'], [TOP_DROP, 'drop'], [TOP_DURATION_CHANGE, 'durationChange'], [TOP_EMPTIED, 'emptied'], [TOP_ENCRYPTED, 'encrypted'], [TOP_ENDED, 'ended'], [TOP_ERROR, 'error'], [TOP_FOCUS, 'focus'], [TOP_INPUT, 'input'], [TOP_KEY_DOWN, 'keyDown'], [TOP_KEY_PRESS, 'keyPress'], [TOP_KEY_UP, 'keyUp'], [TOP_LOAD_START, 'loadStart'], [TOP_LOAD_START, 'loadStart'], [TOP_LOAD, 'load'], [TOP_LOADED_DATA, 'loadedData'], [TOP_LOADED_METADATA, 'loadedMetadata'], [TOP_MOUSE_DOWN, 'mouseDown'], [TOP_MOUSE_MOVE, 'mouseMove'], [TOP_MOUSE_OUT, 'mouseOut'], [TOP_MOUSE_OVER, 'mouseOver'], [TOP_MOUSE_UP, 'mouseUp'], [TOP_PASTE, 'paste'], [TOP_PAUSE, 'pause'], [TOP_PLAY, 'play'], [TOP_PLAYING, 'playing'], [TOP_PROGRESS, 'progress'], [TOP_RATE_CHANGE, 'rateChange'], [TOP_SCROLL, 'scroll'], [TOP_SEEKED, 'seeked'], [TOP_SEEKING, 'seeking'], [TOP_SELECTION_CHANGE, 'selectionChange'], [TOP_STALLED, 'stalled'], [TOP_SUSPEND, 'suspend'], [TOP_TEXT_INPUT, 'textInput'], [TOP_TIME_UPDATE, 'timeUpdate'], [TOP_TOGGLE, 'toggle'], [TOP_TOUCH_CANCEL, 'touchCancel'], [TOP_TOUCH_END, 'touchEnd'], [TOP_TOUCH_MOVE, 'touchMove'], [TOP_TOUCH_START, 'touchStart'], [TOP_TRANSITION_END, 'transitionEnd'], [TOP_VOLUME_CHANGE, 'volumeChange'], [TOP_WAITING, 'waiting'], [TOP_WHEEL, 'wheel']].forEach(function (_ref) {
var topLevelType = _ref[0],
eventType = _ref[1];
/**
* @param {!Element|ReactDOMComponent} domComponentOrNode
* @param {?Event} nativeEventData Fake native event to use in SyntheticEvent.
*/
ReactTestUtils.SimulateNative[convenienceName] = makeNativeSimulator(eventType);
ReactTestUtils.SimulateNative[eventType] = makeNativeSimulator(eventType, topLevelType);
});
var ReactTestUtils$2 = Object.freeze({
var ReactTestUtils$2 = ({
default: ReactTestUtils
});
@ -1240,7 +1264,7 @@ var ReactTestUtils$3 = ( ReactTestUtils$2 && ReactTestUtils ) || ReactTestUtils$
// TODO: decide on the top-level export form.
// This is hacky but makes it work with both Rollup and Jest.
var testUtils = ReactTestUtils$3['default'] ? ReactTestUtils$3['default'] : ReactTestUtils$3;
var testUtils = ReactTestUtils$3.default ? ReactTestUtils$3.default : ReactTestUtils$3;
return testUtils;

View File

@ -1,4 +1,4 @@
/** @license React v16.2.0
/** @license React v16.4.1
* react-dom-test-utils.production.min.js
*
* Copyright (c) 2013-present, Facebook, Inc.
@ -16,6 +16,39 @@ var ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
var _assign = ReactInternals.assign;
/**
* 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
@ -24,30 +57,20 @@ var _assign = ReactInternals.assign;
*/
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);
}
/**
* 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.
*
*/
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
@ -116,6 +139,9 @@ var ReactInternals$1 = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
var ReactCurrentOwner = ReactInternals$1.ReactCurrentOwner;
// 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.
// Before we know whether it is functional or class
var FunctionalComponent = 1;
var ClassComponent = 2;
@ -124,19 +150,22 @@ var HostRoot = 3; // Root of a host tree. Could be nested inside another node.
var HostComponent = 5;
var HostText = 6;
// Don't change these two values:
var NoEffect = 0; // 0b00000000
// 0b00000001
// Don't change these two values. They're used by React Dev Tools.
var NoEffect = /* */0;
// You can change the rest (and add more).
var Placement = 2; // 0b00000010
// 0b00000100
// 0b00000110
// 0b00001000
// 0b00010000
// 0b00100000
// 0b01000000
// 0b10000000
var Placement = /* */2;
// Union of all host effects
var MOUNTING = 1;
var MOUNTED = 2;
@ -150,15 +179,15 @@ function isFiberMountedImpl(fiber) {
if ((node.effectTag & Placement) !== NoEffect) {
return MOUNTING;
}
while (node['return']) {
node = node['return'];
while (node.return) {
node = node.return;
if ((node.effectTag & Placement) !== NoEffect) {
return MOUNTING;
}
}
} else {
while (node['return']) {
node = node['return'];
while (node.return) {
node = node.return;
}
}
if (node.tag === HostRoot) {
@ -196,7 +225,7 @@ function findCurrentFiberUsingSlowPath(fiber) {
var a = fiber;
var b = alternate;
while (true) {
var parentA = a['return'];
var parentA = a.return;
var parentB = parentA ? parentA.alternate : null;
if (!parentA || !parentB) {
// We're at the root.
@ -226,7 +255,7 @@ function findCurrentFiberUsingSlowPath(fiber) {
reactProdInvariant('188');
}
if (a['return'] !== b['return']) {
if (a.return !== b.return) {
// The return pointer of A and the return pointer of B point to different
// fibers. We assume that return pointers never criss-cross, so A must
// belong to the child set of A.return, and B must belong to the child
@ -439,24 +468,26 @@ SyntheticEvent.Interface = EventInterface;
/**
* Helper to reduce boilerplate when creating subclasses.
*
* @param {function} Class
* @param {?object} Interface
*/
SyntheticEvent.augmentClass = function (Class, Interface) {
SyntheticEvent.extend = function (Interface) {
var Super = this;
var E = function () {};
E.prototype = Super.prototype;
var prototype = new E();
function Class() {
return Super.apply(this, arguments);
}
_assign(prototype, Class.prototype);
Class.prototype = prototype;
Class.prototype.constructor = Class;
Class.Interface = _assign({}, Super.Interface, Interface);
Class.augmentClass = Super.augmentClass;
Class.extend = Super.extend;
addEventPoolingTo(Class);
return Class;
};
/** Proxying after everything set on SyntheticEvent
@ -490,6 +521,14 @@ function addEventPoolingTo(EventConstructor) {
EventConstructor.release = releasePooledEvent;
}
// Do not uses the below two methods directly!
// Instead use constants exported from DOMTopLevelEventTypes in ReactDOM.
// (It is the only module that is allowed to access these methods.)
function unsafeCastStringToDOMTopLevelType(topLevelType) {
return topLevelType;
}
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
@ -606,90 +645,101 @@ function getVendorPrefixedEventName(eventName) {
}
}
return '';
return eventName;
}
/**
* Types of raw signals from the browser caught at the top level.
*
* For events like 'submit' which don't consistently bubble (which we
* trap at a lower node than `document`), binding at `document` would
* cause duplicate events so we don't include them here.
* To identify top level events in ReactDOM, we use constants defined by this
* module. This is the only module that uses the unsafe* methods to express
* that the constants actually correspond to the browser event names. This lets
* us save some bundle size by avoiding a top level type -> event name map.
* The rest of ReactDOM code should import top level types from this file.
*/
var topLevelTypes$1 = {
topAbort: 'abort',
topAnimationEnd: getVendorPrefixedEventName('animationend') || 'animationend',
topAnimationIteration: getVendorPrefixedEventName('animationiteration') || 'animationiteration',
topAnimationStart: getVendorPrefixedEventName('animationstart') || 'animationstart',
topBlur: 'blur',
topCancel: 'cancel',
topCanPlay: 'canplay',
topCanPlayThrough: 'canplaythrough',
topChange: 'change',
topClick: 'click',
topClose: 'close',
topCompositionEnd: 'compositionend',
topCompositionStart: 'compositionstart',
topCompositionUpdate: 'compositionupdate',
topContextMenu: 'contextmenu',
topCopy: 'copy',
topCut: 'cut',
topDoubleClick: 'dblclick',
topDrag: 'drag',
topDragEnd: 'dragend',
topDragEnter: 'dragenter',
topDragExit: 'dragexit',
topDragLeave: 'dragleave',
topDragOver: 'dragover',
topDragStart: 'dragstart',
topDrop: 'drop',
topDurationChange: 'durationchange',
topEmptied: 'emptied',
topEncrypted: 'encrypted',
topEnded: 'ended',
topError: 'error',
topFocus: 'focus',
topInput: 'input',
topKeyDown: 'keydown',
topKeyPress: 'keypress',
topKeyUp: 'keyup',
topLoadedData: 'loadeddata',
topLoad: 'load',
topLoadedMetadata: 'loadedmetadata',
topLoadStart: 'loadstart',
topMouseDown: 'mousedown',
topMouseMove: 'mousemove',
topMouseOut: 'mouseout',
topMouseOver: 'mouseover',
topMouseUp: 'mouseup',
topPaste: 'paste',
topPause: 'pause',
topPlay: 'play',
topPlaying: 'playing',
topProgress: 'progress',
topRateChange: 'ratechange',
topScroll: 'scroll',
topSeeked: 'seeked',
topSeeking: 'seeking',
topSelectionChange: 'selectionchange',
topStalled: 'stalled',
topSuspend: 'suspend',
topTextInput: 'textInput',
topTimeUpdate: 'timeupdate',
topToggle: 'toggle',
topTouchCancel: 'touchcancel',
topTouchEnd: 'touchend',
topTouchMove: 'touchmove',
topTouchStart: 'touchstart',
topTransitionEnd: getVendorPrefixedEventName('transitionend') || 'transitionend',
topVolumeChange: 'volumechange',
topWaiting: 'waiting',
topWheel: 'wheel'
};
var TOP_ABORT = unsafeCastStringToDOMTopLevelType('abort');
var TOP_ANIMATION_END = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('animationend'));
var TOP_ANIMATION_ITERATION = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('animationiteration'));
var TOP_ANIMATION_START = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('animationstart'));
var TOP_BLUR = unsafeCastStringToDOMTopLevelType('blur');
var TOP_CAN_PLAY = unsafeCastStringToDOMTopLevelType('canplay');
var TOP_CAN_PLAY_THROUGH = unsafeCastStringToDOMTopLevelType('canplaythrough');
var TOP_CANCEL = unsafeCastStringToDOMTopLevelType('cancel');
var TOP_CHANGE = unsafeCastStringToDOMTopLevelType('change');
var TOP_CLICK = unsafeCastStringToDOMTopLevelType('click');
var TOP_CLOSE = unsafeCastStringToDOMTopLevelType('close');
var TOP_COMPOSITION_END = unsafeCastStringToDOMTopLevelType('compositionend');
var TOP_COMPOSITION_START = unsafeCastStringToDOMTopLevelType('compositionstart');
var TOP_COMPOSITION_UPDATE = unsafeCastStringToDOMTopLevelType('compositionupdate');
var TOP_CONTEXT_MENU = unsafeCastStringToDOMTopLevelType('contextmenu');
var TOP_COPY = unsafeCastStringToDOMTopLevelType('copy');
var TOP_CUT = unsafeCastStringToDOMTopLevelType('cut');
var TOP_DOUBLE_CLICK = unsafeCastStringToDOMTopLevelType('dblclick');
var TOP_DRAG = unsafeCastStringToDOMTopLevelType('drag');
var TOP_DRAG_END = unsafeCastStringToDOMTopLevelType('dragend');
var TOP_DRAG_ENTER = unsafeCastStringToDOMTopLevelType('dragenter');
var TOP_DRAG_EXIT = unsafeCastStringToDOMTopLevelType('dragexit');
var TOP_DRAG_LEAVE = unsafeCastStringToDOMTopLevelType('dragleave');
var TOP_DRAG_OVER = unsafeCastStringToDOMTopLevelType('dragover');
var TOP_DRAG_START = unsafeCastStringToDOMTopLevelType('dragstart');
var TOP_DROP = unsafeCastStringToDOMTopLevelType('drop');
var TOP_DURATION_CHANGE = unsafeCastStringToDOMTopLevelType('durationchange');
var TOP_EMPTIED = unsafeCastStringToDOMTopLevelType('emptied');
var TOP_ENCRYPTED = unsafeCastStringToDOMTopLevelType('encrypted');
var TOP_ENDED = unsafeCastStringToDOMTopLevelType('ended');
var TOP_ERROR = unsafeCastStringToDOMTopLevelType('error');
var TOP_FOCUS = unsafeCastStringToDOMTopLevelType('focus');
var BrowserEventConstants = {
topLevelTypes: topLevelTypes$1
};
var TOP_INPUT = unsafeCastStringToDOMTopLevelType('input');
var TOP_KEY_DOWN = unsafeCastStringToDOMTopLevelType('keydown');
var TOP_KEY_PRESS = unsafeCastStringToDOMTopLevelType('keypress');
var TOP_KEY_UP = unsafeCastStringToDOMTopLevelType('keyup');
var TOP_LOAD = unsafeCastStringToDOMTopLevelType('load');
var TOP_LOAD_START = unsafeCastStringToDOMTopLevelType('loadstart');
var TOP_LOADED_DATA = unsafeCastStringToDOMTopLevelType('loadeddata');
var TOP_LOADED_METADATA = unsafeCastStringToDOMTopLevelType('loadedmetadata');
var TOP_MOUSE_DOWN = unsafeCastStringToDOMTopLevelType('mousedown');
var TOP_MOUSE_MOVE = unsafeCastStringToDOMTopLevelType('mousemove');
var TOP_MOUSE_OUT = unsafeCastStringToDOMTopLevelType('mouseout');
var TOP_MOUSE_OVER = unsafeCastStringToDOMTopLevelType('mouseover');
var TOP_MOUSE_UP = unsafeCastStringToDOMTopLevelType('mouseup');
var TOP_PASTE = unsafeCastStringToDOMTopLevelType('paste');
var TOP_PAUSE = unsafeCastStringToDOMTopLevelType('pause');
var TOP_PLAY = unsafeCastStringToDOMTopLevelType('play');
var TOP_PLAYING = unsafeCastStringToDOMTopLevelType('playing');
var TOP_PROGRESS = unsafeCastStringToDOMTopLevelType('progress');
var TOP_RATE_CHANGE = unsafeCastStringToDOMTopLevelType('ratechange');
var TOP_SCROLL = unsafeCastStringToDOMTopLevelType('scroll');
var TOP_SEEKED = unsafeCastStringToDOMTopLevelType('seeked');
var TOP_SEEKING = unsafeCastStringToDOMTopLevelType('seeking');
var TOP_SELECTION_CHANGE = unsafeCastStringToDOMTopLevelType('selectionchange');
var TOP_STALLED = unsafeCastStringToDOMTopLevelType('stalled');
var TOP_SUSPEND = unsafeCastStringToDOMTopLevelType('suspend');
var TOP_TEXT_INPUT = unsafeCastStringToDOMTopLevelType('textInput');
var TOP_TIME_UPDATE = unsafeCastStringToDOMTopLevelType('timeupdate');
var TOP_TOGGLE = unsafeCastStringToDOMTopLevelType('toggle');
var TOP_TOUCH_CANCEL = unsafeCastStringToDOMTopLevelType('touchcancel');
var TOP_TOUCH_END = unsafeCastStringToDOMTopLevelType('touchend');
var TOP_TOUCH_MOVE = unsafeCastStringToDOMTopLevelType('touchmove');
var TOP_TOUCH_START = unsafeCastStringToDOMTopLevelType('touchstart');
var TOP_TRANSITION_END = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('transitionend'));
var TOP_VOLUME_CHANGE = unsafeCastStringToDOMTopLevelType('volumechange');
var TOP_WAITING = unsafeCastStringToDOMTopLevelType('waiting');
var TOP_WHEEL = unsafeCastStringToDOMTopLevelType('wheel');
// List of events that need to be individually attached to media elements.
// Note that events in this list will *not* be listened to at the top level
// unless they're explicitly whitelisted in `ReactBrowserEventEmitter.listenTo`.
var findDOMNode = ReactDOM.findDOMNode;
var _ReactDOM$__SECRET_IN = ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
@ -701,14 +751,35 @@ var ReactDOMComponentTree = _ReactDOM$__SECRET_IN.ReactDOMComponentTree;
var ReactDOMEventListener = _ReactDOM$__SECRET_IN.ReactDOMEventListener;
var topLevelTypes = BrowserEventConstants.topLevelTypes;
function Event(suffix) {}
/**
* @class ReactTestUtils
*/
/**
* Simulates a top level event being dispatched from a raw event that occurred
* on an `Element` node.
* @param {number} topLevelType A number from `TopLevelEventTypes`
* @param {!Element} node The dom to simulate an event occurring on.
* @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
*/
function simulateNativeEventOnNode(topLevelType, node, fakeNativeEvent) {
fakeNativeEvent.target = node;
ReactDOMEventListener.dispatchEvent(topLevelType, fakeNativeEvent);
}
/**
* Simulates a top level event being dispatched from a raw event that occurred
* on the `ReactDOMComponent` `comp`.
* @param {Object} topLevelType A type from `BrowserEventConstants.topLevelTypes`.
* @param {!ReactDOMComponent} comp
* @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
*/
function simulateNativeEventOnDOMComponent(topLevelType, comp, fakeNativeEvent) {
simulateNativeEventOnNode(topLevelType, findDOMNode(comp), fakeNativeEvent);
}
function findAllInRenderedFiberTreeInternal(fiber, test) {
if (!fiber) {
return [];
@ -727,7 +798,7 @@ function findAllInRenderedFiberTreeInternal(fiber, test) {
}
}
if (node.child) {
node.child['return'] = node;
node.child.return = node;
node = node.child;
continue;
}
@ -735,12 +806,12 @@ function findAllInRenderedFiberTreeInternal(fiber, test) {
return ret;
}
while (!node.sibling) {
if (!node['return'] || node['return'] === currentParent) {
if (!node.return || node.return === currentParent) {
return ret;
}
node = node['return'];
node = node.return;
}
node.sibling['return'] = node['return'];
node.sibling.return = node.return;
node = node.sibling;
}
}
@ -921,29 +992,6 @@ var ReactTestUtils = {
return this;
},
/**
* Simulates a top level event being dispatched from a raw event that occurred
* on an `Element` node.
* @param {Object} topLevelType A type from `BrowserEventConstants.topLevelTypes`
* @param {!Element} node The dom to simulate an event occurring on.
* @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
*/
simulateNativeEventOnNode: function (topLevelType, node, fakeNativeEvent) {
fakeNativeEvent.target = node;
ReactDOMEventListener.dispatchEvent(topLevelType, fakeNativeEvent);
},
/**
* Simulates a top level event being dispatched from a raw event that occurred
* on the `ReactDOMComponent` `comp`.
* @param {Object} topLevelType A type from `BrowserEventConstants.topLevelTypes`.
* @param {!ReactDOMComponent} comp
* @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
*/
simulateNativeEventOnDOMComponent: function (topLevelType, comp, fakeNativeEvent) {
ReactTestUtils.simulateNativeEventOnNode(topLevelType, findDOMNode(comp), fakeNativeEvent);
},
nativeTouchData: function (x, y) {
return {
touches: [{ pageX: x, pageY: y }]
@ -993,17 +1041,16 @@ function makeSimulator(eventType) {
// Normally extractEvent enqueues a state restore, but we'll just always
// do that since we we're by-passing it here.
ReactControlledComponent.enqueueStateRestore(domNode);
EventPluginHub.enqueueEvents(event);
EventPluginHub.processEventQueue(true);
EventPluginHub.runEventsInBatch(event, true);
});
ReactControlledComponent.restoreStateIfNeeded();
};
}
function buildSimulators() {
ReactTestUtils.Simulate = {};
var eventType;
var eventType = void 0;
for (eventType in EventPluginRegistry.eventNameDispatchConfigs) {
/**
* @param {!Element|ReactDOMComponent} domComponentOrNode
@ -1043,32 +1090,33 @@ buildSimulators();
* to dispatch synthetic events.
*/
function makeNativeSimulator(eventType) {
function makeNativeSimulator(eventType, topLevelType) {
return function (domComponentOrNode, nativeEventData) {
var fakeNativeEvent = new Event(eventType);
_assign(fakeNativeEvent, nativeEventData);
if (ReactTestUtils.isDOMComponent(domComponentOrNode)) {
ReactTestUtils.simulateNativeEventOnDOMComponent(eventType, domComponentOrNode, fakeNativeEvent);
simulateNativeEventOnDOMComponent(topLevelType, domComponentOrNode, fakeNativeEvent);
} else if (domComponentOrNode.tagName) {
// Will allow on actual dom nodes.
ReactTestUtils.simulateNativeEventOnNode(eventType, domComponentOrNode, fakeNativeEvent);
simulateNativeEventOnNode(topLevelType, domComponentOrNode, fakeNativeEvent);
}
};
}
Object.keys(topLevelTypes).forEach(function (eventType) {
// Event type is stored as 'topClick' - we transform that to 'click'
var convenienceName = eventType.indexOf('top') === 0 ? eventType.charAt(3).toLowerCase() + eventType.substr(4) : eventType;
[[TOP_ABORT, 'abort'], [TOP_ANIMATION_END, 'animationEnd'], [TOP_ANIMATION_ITERATION, 'animationIteration'], [TOP_ANIMATION_START, 'animationStart'], [TOP_BLUR, 'blur'], [TOP_CAN_PLAY_THROUGH, 'canPlayThrough'], [TOP_CAN_PLAY, 'canPlay'], [TOP_CANCEL, 'cancel'], [TOP_CHANGE, 'change'], [TOP_CLICK, 'click'], [TOP_CLOSE, 'close'], [TOP_COMPOSITION_END, 'compositionEnd'], [TOP_COMPOSITION_START, 'compositionStart'], [TOP_COMPOSITION_UPDATE, 'compositionUpdate'], [TOP_CONTEXT_MENU, 'contextMenu'], [TOP_COPY, 'copy'], [TOP_CUT, 'cut'], [TOP_DOUBLE_CLICK, 'doubleClick'], [TOP_DRAG_END, 'dragEnd'], [TOP_DRAG_ENTER, 'dragEnter'], [TOP_DRAG_EXIT, 'dragExit'], [TOP_DRAG_LEAVE, 'dragLeave'], [TOP_DRAG_OVER, 'dragOver'], [TOP_DRAG_START, 'dragStart'], [TOP_DRAG, 'drag'], [TOP_DROP, 'drop'], [TOP_DURATION_CHANGE, 'durationChange'], [TOP_EMPTIED, 'emptied'], [TOP_ENCRYPTED, 'encrypted'], [TOP_ENDED, 'ended'], [TOP_ERROR, 'error'], [TOP_FOCUS, 'focus'], [TOP_INPUT, 'input'], [TOP_KEY_DOWN, 'keyDown'], [TOP_KEY_PRESS, 'keyPress'], [TOP_KEY_UP, 'keyUp'], [TOP_LOAD_START, 'loadStart'], [TOP_LOAD_START, 'loadStart'], [TOP_LOAD, 'load'], [TOP_LOADED_DATA, 'loadedData'], [TOP_LOADED_METADATA, 'loadedMetadata'], [TOP_MOUSE_DOWN, 'mouseDown'], [TOP_MOUSE_MOVE, 'mouseMove'], [TOP_MOUSE_OUT, 'mouseOut'], [TOP_MOUSE_OVER, 'mouseOver'], [TOP_MOUSE_UP, 'mouseUp'], [TOP_PASTE, 'paste'], [TOP_PAUSE, 'pause'], [TOP_PLAY, 'play'], [TOP_PLAYING, 'playing'], [TOP_PROGRESS, 'progress'], [TOP_RATE_CHANGE, 'rateChange'], [TOP_SCROLL, 'scroll'], [TOP_SEEKED, 'seeked'], [TOP_SEEKING, 'seeking'], [TOP_SELECTION_CHANGE, 'selectionChange'], [TOP_STALLED, 'stalled'], [TOP_SUSPEND, 'suspend'], [TOP_TEXT_INPUT, 'textInput'], [TOP_TIME_UPDATE, 'timeUpdate'], [TOP_TOGGLE, 'toggle'], [TOP_TOUCH_CANCEL, 'touchCancel'], [TOP_TOUCH_END, 'touchEnd'], [TOP_TOUCH_MOVE, 'touchMove'], [TOP_TOUCH_START, 'touchStart'], [TOP_TRANSITION_END, 'transitionEnd'], [TOP_VOLUME_CHANGE, 'volumeChange'], [TOP_WAITING, 'waiting'], [TOP_WHEEL, 'wheel']].forEach(function (_ref) {
var topLevelType = _ref[0],
eventType = _ref[1];
/**
* @param {!Element|ReactDOMComponent} domComponentOrNode
* @param {?Event} nativeEventData Fake native event to use in SyntheticEvent.
*/
ReactTestUtils.SimulateNative[convenienceName] = makeNativeSimulator(eventType);
ReactTestUtils.SimulateNative[eventType] = makeNativeSimulator(eventType, topLevelType);
});
var ReactTestUtils$2 = Object.freeze({
var ReactTestUtils$2 = ({
default: ReactTestUtils
});
@ -1076,7 +1124,7 @@ var ReactTestUtils$3 = ( ReactTestUtils$2 && ReactTestUtils ) || ReactTestUtils$
// TODO: decide on the top-level export form.
// This is hacky but makes it work with both Rollup and Jest.
var testUtils = ReactTestUtils$3['default'] ? ReactTestUtils$3['default'] : ReactTestUtils$3;
var testUtils = ReactTestUtils$3.default ? ReactTestUtils$3.default : ReactTestUtils$3;
return testUtils;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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. `<Foo />.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;

View File

@ -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

View File

@ -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);

View File

@ -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]

View File

@ -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,<p>test for bug 642615</p>";
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)");
});

View File

@ -5,7 +5,7 @@
"use strict";
const TEST_URI = "data:text/html;charset=utf-8,<p>test for bug 642615 & 994134</p>";
const TEST_URI = "data:text/html;charset=utf-8,<p>Test self-XSS protection</p>";
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");
}

View File

@ -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),

View File

@ -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);

View File

@ -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,

View File

@ -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<Promise>
ChromeUtils::RequestPerformanceMetrics(GlobalObject& aGlobal,
ErrorResult& aRv)
{
MOZ_ASSERT(XRE_IsParentProcess());
// calling all content processes via IPDL (async)
nsTArray<ContentParent*> children;
ContentParent::GetAll(children);
for (uint32_t i = 0; i < children.Length(); i++) {
mozilla::Unused << children[i]->SendRequestPerformanceMetrics();
// Creating a promise
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
MOZ_ASSERT(global);
RefPtr<Promise> 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<PerformanceInfo> 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<Promise>
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<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
MOZ_ASSERT(global);
RefPtr<Promise> 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

View File

@ -160,7 +160,9 @@ public:
static void ClearRecentJSDevError(GlobalObject& aGlobal);
static void RequestPerformanceMetrics(GlobalObject& aGlobal);
static already_AddRefed<Promise>
RequestPerformanceMetrics(GlobalObject& aGlobal,
ErrorResult& aRv);
static void Import(const GlobalObject& aGlobal,
const nsAString& aResourceURI,
@ -183,7 +185,8 @@ public:
JS::Handle<JSObject*> stack,
JS::MutableHandle<JSObject*> aRetVal, ErrorResult& aRv);
static void RequestIOActivity(GlobalObject& aGlobal);
static already_AddRefed<Promise>
RequestIOActivity(GlobalObject& aGlobal, ErrorResult& aRv);
};
} // namespace dom

View File

@ -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',

View File

@ -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;
};

View File

@ -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 <nsIMutableArray.h>
#include <nsArrayUtils.h>
#include <nsPerformanceMetrics.h>
#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<nsIPerformanceMetricsDispatchCategory> 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<nsIMutableArray> 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;
}

View File

@ -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<nsIPerformanceMetricsDispatchCategory> mItems;
};
#endif // end nsPerformanceMetrics_h__

View File

@ -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<sequence<PerformanceInfoDictionary>> 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<sequence<IOActivityDataDictionary>> 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<CategoryDispatchDictionary> 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;
};
/**

View File

@ -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<PerformanceInfo> info;
CollectPerformanceInfo(info);
SendAddPerformanceMetrics(info);
SendAddPerformanceMetrics(aID, info);
return IPC_OK();
}

View File

@ -192,7 +192,7 @@ public:
nsTArray<uint32_t>&& namespaces) override;
mozilla::ipc::IPCResult
RecvRequestPerformanceMetrics() override;
RecvRequestPerformanceMetrics(const nsID& aID) override;
mozilla::ipc::IPCResult
RecvReinitRendering(

View File

@ -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<PerformanceInfo>&& aMetrics)
ContentParent::RecvAddPerformanceMetrics(const nsID& aID,
nsTArray<PerformanceInfo>&& 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();
}

View File

@ -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<PerformanceInfo>&& aMetrics) override;
mozilla::ipc::IPCResult RecvAddPerformanceMetrics(const nsID& aID, nsTArray<PerformanceInfo>&& aMetrics) override;
virtual bool
DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*) override;

View File

@ -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);

View File

@ -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);

View File

@ -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');
});

View File

@ -478,6 +478,7 @@ PerformanceInfo
WorkerDebugger::ReportPerformanceInfo()
{
AssertIsOnMainThread();
#if defined(XP_WIN)
uint32_t pid = GetCurrentProcessId();
#else
@ -495,22 +496,29 @@ WorkerDebugger::ReportPerformanceInfo()
}
}
}
RefPtr<PerformanceCounter> perf = mWorkerPrivate->GetPerformanceCounter();
uint16_t count = perf->GetTotalDispatchCount();
uint64_t duration = perf->GetExecutionDuration();
RefPtr<nsIURI> 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<CategoryDispatch> 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<nsIURI> uri = mWorkerPrivate->GetResolvedScriptURI();
RefPtr<PerformanceCounter> 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);
}

View File

@ -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();
}

View File

@ -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<VRSystemManagerPuppet> mPuppetManager;
RefPtr<VRSystemManagerExternal> mExternalManager;
#if !defined(MOZ_WIDGET_ANDROID)
#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
RefPtr<VRService> mVRService;
#endif
bool mVRDisplaysRequested;

View File

@ -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 <vector>
using namespace mozilla;
using namespace mozilla::net;
mozilla::StaticRefPtr<IOActivityMonitor> 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<nsIRunnable>
Create(Activities& aActivities, const mozilla::MutexAutoLock& aProofOfLock)
{
RefPtr<NotifyIOActivity> 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<nsIRunnable> result(runnable);
return result.forget();
}
NS_IMETHODIMP
Run() override
{
MOZ_ASSERT(NS_IsMainThread());
if (mActivities.Length() == 0) {
return NS_OK;
}
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (!obs) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIMutableArray> 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<nsIIOActivityData> 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<IOActivity> 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<IOActivityMonitor> 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<IOActivityMonitor> 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<nsIRunnable> 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<dom::IOActivityDataDictionary> 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<IOActivityMonitor> 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;
}

View File

@ -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 <stdint.h>
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<nsCStringHashKey, dom::IOActivityDataDictionary> 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<nsCStringHashKey, IOActivity> 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<nsITimer> mTimer;
// protects mActivities accesses
Mutex mLock;
};

View File

@ -52,7 +52,6 @@ XPIDL_SOURCES += [
'nsIIncrementalStreamLoader.idl',
'nsIInputStreamChannel.idl',
'nsIInputStreamPump.idl',
'nsIIOActivityData.idl',
'nsIIOService.idl',
'nsILoadContextInfo.idl',
'nsILoadGroup.idl',

View File

@ -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;
};

View File

@ -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")) {

View File

@ -1,6 +1,7 @@
[DEFAULT]
support-files =
dummy.html
ioactivity.html
[browser_about_cache.js]
[browser_NetUtil.js]

View File

@ -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");
});
});
});

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
</head>
<html>
<body>
<p>IOActivity Test Page</p>
</body>
</html>

View File

@ -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__':

View File

@ -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.'

View File

@ -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)

View File

@ -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")

View File

@ -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 `<tab>` 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
`<tab>` 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()

View File

@ -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.
"""

View File

@ -0,0 +1,56 @@
<html>
<head>
<style>
div {
opacity: 0.95;
width:10px;
height:10px;
background-color:green;
display: inline-block;
}
</style>
</head>
<body id="body">
</body>
<script>
var start = null;
var divCount = 2500;
var maxIterations = 600;
for (var i = 0; i < divCount; i++) {
var div = document.createElement("div");
div.id = i;
document.getElementById("body").appendChild(div);
}
var iteration = 0;
function runFrame() {
if (document.getElementById(iteration).style.backgroundColor == "red") {
document.getElementById(iteration).style.backgroundColor = "green";
} else {
document.getElementById(iteration).style.backgroundColor = "red";
}
iteration++;
iteration = iteration % divCount;
if (--maxIterations == 0) {
var end = performance.now();
if (window.tpRecordTime) {
window.tpRecordTime(end - start, start);
}
return;
}
window.requestAnimationFrame(runFrame);
}
addEventListener("load", function() {
TalosContentProfiler.resume("displaylist_flattened_mutate.html loaded", true).then(() => {
start = performance.now();
window.requestAnimationFrame(runFrame);
});
});
</script>
<script type="text/javascript" src="chrome://talos-powers-content/content/TalosContentProfiler.js"></script>
</html>

View File

@ -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

View File

@ -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,
};
});
},
},

View File

@ -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": {},

View File

@ -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 <code>certificateChain</code> is passed as an option.",
"type": "array",

View File

@ -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();

View File

@ -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<dom::PerformanceInfo>& 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<mozilla::dom::CategoryDispatchDictionary> 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<PerformanceMetricsCollector> 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<PerformanceMetricsCollector> 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<ContentParent*> 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<AggregatedResults> results = MakeUnique<AggregatedResults>(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<PerformanceInfo> info;
CollectPerformanceInfo(info);
DataReceived(uuid, info);
}
// static
nsresult
PerformanceMetricsCollector::DataReceived(const nsID& aUUID,
const nsTArray<PerformanceInfo>& aMetrics)
{
MOZ_ASSERT(gInstance);
MOZ_ASSERT(XRE_IsParentProcess());
return gInstance->DataReceivedInternal(aUUID, aMetrics);
}
nsresult
PerformanceMetricsCollector::DataReceivedInternal(const nsID& aUUID,
const nsTArray<PerformanceInfo>& aMetrics)
{
MOZ_ASSERT(gInstance == this);
UniquePtr<AggregatedResults>* 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

View File

@ -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<dom::PerformanceInfo>& aMetrics);
void SetNumResultsRequired(uint32_t aNumResultsRequired);
void Abort(nsresult aReason);
private:
RefPtr<dom::Promise> mPromise;
uint32_t mPendingResults;
FallibleTArray<dom::PerformanceInfoDictionary> 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<PerformanceMetricsCollector> 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<dom::PerformanceInfo>& aMetrics);
void ForgetAggregatedResults(const nsID& aUUID);
private:
~PerformanceMetricsCollector();
void RequestMetricsInternal(dom::Promise* aPromise);
nsresult DataReceivedInternal(const nsID& aUUID,
const nsTArray<dom::PerformanceInfo>& aMetrics);
nsDataHashtable<nsIDHashKey, UniquePtr<AggregatedResults>> mAggregatedResults;
};
} // namespace mozilla
#endif // PerformanceMetricsCollector_h

View File

@ -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<PerformanceInfo>& aMetrics)
}
}
nsresult
NotifyPerformanceInfo(const nsTArray<PerformanceInfo>& aMetrics)
{
nsresult rv;
nsCOMPtr<nsIMutableArray> 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<nsIMutableArray> items = do_CreateInstance(NS_ARRAY_CONTRACTID);
if (NS_WARN_IF(!items)) {
return rv;
}
for (const CategoryDispatch& entry : info.items()) {
nsCOMPtr<nsIPerformanceMetricsDispatchCategory> item =
new PerformanceMetricsDispatchCategory(entry.category(),
entry.count());
rv = items->AppendElement(item);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
nsCOMPtr<nsIPerformanceMetricsData> 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<nsIObserverService> 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

View File

@ -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<dom::PerformanceInfo>& aMetrics);
/**
* Converts a PerformanceInfo array into a nsIPerformanceMetricsData and
* sends a performance-metrics notification with it
*/
nsresult NotifyPerformanceInfo(const nsTArray<dom::PerformanceInfo>& aMetrics);
} // namespace mozilla
#endif // PerformanceCollector_h
#endif // PerformanceUtils_h

View File

@ -27,10 +27,12 @@ UNIFIED_SOURCES += [
]
UNIFIED_SOURCES += [
'PerformanceMetricsCollector.cpp',
'PerformanceUtils.cpp'
]
EXPORTS.mozilla += [
'PerformanceMetricsCollector.h',
'PerformanceUtils.h'
]

View File

@ -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
-----------------

View File

@ -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;
}
},

View File

@ -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 <browser> 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);
},

View File

@ -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;

View File

@ -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();

View File

@ -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 = [

View File

@ -0,0 +1,2 @@
# unused import
import os

View File

@ -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()

View File

@ -891,7 +891,8 @@ NS_IMETHODIMP nsXULWindow::SetVisibility(bool aVisibility)
nsCOMPtr<nsIObserverService> obssvc = services::GetObserverService();
NS_ASSERTION(obssvc, "Couldn't get observer service.");
if (obssvc) {
obssvc->NotifyObservers(nullptr, "xul-window-visible", nullptr);
obssvc->NotifyObservers(static_cast<nsIXULWindow*>(this),
"xul-window-visible", nullptr);
}
mDebuting = false;