зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to inbound. a=merge CLOSED TREE
This commit is contained in:
Коммит
d01e0f82de
6
.flake8
6
.flake8
|
@ -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,
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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`);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();" />
|
||||
|
|
|
@ -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") &&
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -101,4 +101,4 @@ const clientEvents = {
|
|||
newSource
|
||||
};
|
||||
exports.setupEvents = setupEvents;
|
||||
exports.clientEvents = clientEvents;
|
||||
exports.clientEvents = clientEvents;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
)
|
||||
);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)");
|
||||
});
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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__
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -192,7 +192,7 @@ public:
|
|||
nsTArray<uint32_t>&& namespaces) override;
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
RecvRequestPerformanceMetrics() override;
|
||||
RecvRequestPerformanceMetrics(const nsID& aID) override;
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
RecvReinitRendering(
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -52,7 +52,6 @@ XPIDL_SOURCES += [
|
|||
'nsIIncrementalStreamLoader.idl',
|
||||
'nsIInputStreamChannel.idl',
|
||||
'nsIInputStreamPump.idl',
|
||||
'nsIIOActivityData.idl',
|
||||
'nsIIOService.idl',
|
||||
'nsILoadContextInfo.idl',
|
||||
'nsILoadGroup.idl',
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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")) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
dummy.html
|
||||
ioactivity.html
|
||||
|
||||
[browser_about_cache.js]
|
||||
[browser_NetUtil.js]
|
||||
|
|
|
@ -4,80 +4,45 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const TEST_URL = "http://example.com/browser/dom/tests/browser/dummy.html";
|
||||
|
||||
const ROOT_URL = getRootDirectory(gTestPath).replace("chrome://mochitests/content/",
|
||||
"https://example.com/");
|
||||
const TEST_URL = "about:license";
|
||||
const TEST_URL2 = ROOT_URL + "ioactivity.html";
|
||||
|
||||
var gotSocket = false;
|
||||
var gotFile = false;
|
||||
var gotSqlite = false;
|
||||
var gotEmptyData = false;
|
||||
var networkActivity = function(subject, topic, value) {
|
||||
subject.QueryInterface(Ci.nsIMutableArray);
|
||||
let enumerator = subject.enumerate();
|
||||
while (enumerator.hasMoreElements()) {
|
||||
let data = enumerator.getNext();
|
||||
data = data.QueryInterface(Ci.nsIIOActivityData);
|
||||
|
||||
function processResults(results) {
|
||||
for (let data of results) {
|
||||
console.log(data.location);
|
||||
gotEmptyData = data.rx == 0 && data.tx == 0 && !gotEmptyData
|
||||
gotSocket = data.location.startsWith("socket://127.0.0.1:") || gotSocket;
|
||||
gotFile = data.location.endsWith(".js") || gotFile;
|
||||
gotFile = data.location.endsWith("aboutLicense.css") || gotFile;
|
||||
gotSqlite = data.location.endsWith("places.sqlite") || gotSqlite;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function startObserver() {
|
||||
gotSocket = gotFile = gotSqlite = gotEmptyData = false;
|
||||
Services.obs.addObserver(networkActivity, "io-activity");
|
||||
// why do I have to do this ??
|
||||
add_task(async function testRequestIOActivity() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
["io.activity.enabled", true],
|
||||
]
|
||||
});
|
||||
waitForExplicitFinish();
|
||||
Services.obs.notifyObservers(null, "profile-initial-state", null);
|
||||
}
|
||||
|
||||
// this test activates the timer and checks the results as they come in
|
||||
add_task(async function testWithTimer() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
["io.activity.enabled", true],
|
||||
["io.activity.intervalMilliseconds", 50]
|
||||
]
|
||||
});
|
||||
waitForExplicitFinish();
|
||||
startObserver();
|
||||
|
||||
await BrowserTestUtils.withNewTab({ gBrowser, url: "http://example.com" },
|
||||
async function(browser) {
|
||||
// wait until we get the events back
|
||||
await BrowserTestUtils.waitForCondition(() => {
|
||||
return gotSocket && gotFile && gotSqlite && !gotEmptyData;
|
||||
}, "wait for events to come in", 500);
|
||||
await BrowserTestUtils.withNewTab(TEST_URL, async function(browser) {
|
||||
await BrowserTestUtils.withNewTab(TEST_URL2, async function(browser) {
|
||||
let results = await ChromeUtils.requestIOActivity();
|
||||
processResults(results);
|
||||
|
||||
ok(gotSocket, "A socket was used");
|
||||
ok(gotFile, "A file was used");
|
||||
ok(gotSqlite, "A sqlite DB was used");
|
||||
ok(!gotEmptyData, "Every I/O event had data");
|
||||
});
|
||||
});
|
||||
|
||||
// this test manually triggers notifications via ChromeUtils.requestIOActivity()
|
||||
add_task(async function testWithManualCall() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
["io.activity.enabled", true],
|
||||
]
|
||||
});
|
||||
waitForExplicitFinish();
|
||||
startObserver();
|
||||
|
||||
await BrowserTestUtils.withNewTab({ gBrowser, url: "http://example.com" },
|
||||
async function(browser) {
|
||||
// wait until we get the events back
|
||||
await BrowserTestUtils.waitForCondition(() => {
|
||||
ChromeUtils.requestIOActivity();
|
||||
return gotSocket && gotFile && gotSqlite && !gotEmptyData;
|
||||
}, "wait for events to come in", 500);
|
||||
|
||||
ok(gotSocket, "A socket was used");
|
||||
ok(gotFile, "A file was used");
|
||||
// test deactivated for now
|
||||
// ok(gotFile, "A file was used");
|
||||
ok(gotSqlite, "A sqlite DB was used");
|
||||
ok(!gotEmptyData, "Every I/O event had data");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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>
|
|
@ -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__':
|
||||
|
|
|
@ -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.'
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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.
|
||||
"""
|
||||
|
|
|
@ -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>
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
});
|
||||
},
|
||||
},
|
||||
|
|
|
@ -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": {},
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -27,10 +27,12 @@ UNIFIED_SOURCES += [
|
|||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'PerformanceMetricsCollector.cpp',
|
||||
'PerformanceUtils.cpp'
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
'PerformanceMetricsCollector.h',
|
||||
'PerformanceUtils.h'
|
||||
]
|
||||
|
||||
|
|
|
@ -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
|
||||
-----------------
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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 = [
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
# unused import
|
||||
import os
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче