aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAldo Cortesi <aldo@nullcube.com>2016-06-07 12:56:51 +1200
committerAldo Cortesi <aldo@nullcube.com>2016-06-07 12:56:51 +1200
commit75ebd638eb6f6ed74f1f191d4f9e30277f4f2d34 (patch)
tree9bff77b84d485549f2d9cfad61a4c54b6315088d
parente037fe05ff1f0c2893b3f51e06e0261ca4245d63 (diff)
parent80292c0ac3c9684ee945546c9b45334178518e4e (diff)
downloadmitmproxy-75ebd638eb6f6ed74f1f191d4f9e30277f4f2d34.tar.gz
mitmproxy-75ebd638eb6f6ed74f1f191d4f9e30277f4f2d34.tar.bz2
mitmproxy-75ebd638eb6f6ed74f1f191d4f9e30277f4f2d34.zip
Merge remote-tracking branch 'upstream/master'
-rw-r--r--.travis.yml4
-rw-r--r--mitmproxy/web/__init__.py2
-rw-r--r--mitmproxy/web/static/app.js445
-rw-r--r--mitmproxy/web/static/vendor.js1881
-rw-r--r--netlib/tcp.py6
-rw-r--r--pathod/language/base.py16
-rw-r--r--pathod/language/generators.py3
-rw-r--r--pathod/language/http.py30
-rw-r--r--pathod/language/message.py7
-rw-r--r--pathod/pathoc.py77
-rw-r--r--setup.py2
-rw-r--r--test/pathod/test_language_base.py13
-rw-r--r--test/pathod/test_language_http.py146
-rw-r--r--test/pathod/test_language_websocket.py14
-rw-r--r--test/pathod/test_pathoc.py57
-rw-r--r--test/pathod/test_pathod.py28
-rw-r--r--test/pathod/tutils.py3
-rw-r--r--tox.ini2
-rw-r--r--web/package.json5
-rw-r--r--web/src/js/components/flowtable-columns.js293
-rw-r--r--web/src/js/components/flowtable.js78
-rw-r--r--web/src/js/ducks/utils/list.js9
-rw-r--r--web/src/js/ducks/utils/view.js18
23 files changed, 1600 insertions, 1539 deletions
diff --git a/.travis.yml b/.travis.yml
index 5f065d53..f6731c0b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -22,9 +22,9 @@ matrix:
git:
depth: 9999999
- python: 3.5
- env: SCOPE="netlib test/mitmproxy/script test/pathod/test_utils.py test/pathod/test_log.py test/pathod/test_language_generators.py test/pathod/test_language_writer.py test/pathod/test_language_base.py"
+ env: SCOPE="netlib test/mitmproxy/script test/pathod/test_utils.py test/pathod/test_log.py test/pathod/test_language_generators.py test/pathod/test_language_base.py test/pathod/test_language_http.py"
- python: 3.5
- env: SCOPE="netlib test/mitmproxy/script test/pathod/test_utils.py test/pathod/test_log.py test/pathod/test_language_generators.py test/pathod/test_language_writer.py test/pathod/test_language_base.py" NO_ALPN=1
+ env: SCOPE="netlib test/mitmproxy/script test/pathod/test_utils.py test/pathod/test_log.py test/pathod/test_language_generators.py test/pathod/test_language_base.py test/pathod/test_language_http.py" NO_ALPN=1
- python: 2.7
env: DOCS=1
script: 'cd docs && make html'
diff --git a/mitmproxy/web/__init__.py b/mitmproxy/web/__init__.py
index cd497ce2..b21c894f 100644
--- a/mitmproxy/web/__init__.py
+++ b/mitmproxy/web/__init__.py
@@ -1,2 +1,2 @@
from mitmproxy.web import master
-__all__ = ["master"] \ No newline at end of file
+__all__ = ["master"]
diff --git a/mitmproxy/web/static/app.js b/mitmproxy/web/static/app.js
index 88b5e85a..b3736582 100644
--- a/mitmproxy/web/static/app.js
+++ b/mitmproxy/web/static/app.js
@@ -1212,251 +1212,221 @@ var _utils2 = require("../utils.js");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-var TLSColumn = _react2.default.createClass({
- displayName: "TLSColumn",
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
- statics: {
- Title: _react2.default.createClass({
- displayName: "Title",
+function TLSColumn(_ref) {
+ var flow = _ref.flow;
- render: function render() {
- return _react2.default.createElement("th", _extends({}, this.props, { className: "col-tls " + (this.props.className || "") }));
- }
- }),
- sortKeyFun: function sortKeyFun(flow) {
- return flow.request.scheme;
- }
- },
- render: function render() {
- var flow = this.props.flow;
- var ssl = flow.request.scheme === "https";
- var classes;
- if (ssl) {
- classes = "col-tls col-tls-https";
- } else {
- classes = "col-tls col-tls-http";
- }
- return _react2.default.createElement("td", { className: classes });
+ var ssl = flow.request.scheme === "https";
+ var classes = void 0;
+ if (ssl) {
+ classes = "col-tls col-tls-https";
+ } else {
+ classes = "col-tls col-tls-http";
}
-});
+ return _react2.default.createElement("td", { className: classes });
+}
+TLSColumn.Title = function (_ref2) {
+ var _ref2$className = _ref2.className;
+ var className = _ref2$className === undefined ? "" : _ref2$className;
-var IconColumn = _react2.default.createClass({
- displayName: "IconColumn",
+ var props = _objectWithoutProperties(_ref2, ["className"]);
- statics: {
- Title: _react2.default.createClass({
- displayName: "Title",
+ return _react2.default.createElement("th", _extends({}, props, { className: "col-tls " + className }));
+};
+TLSColumn.sortKeyFun = function (flow) {
+ return flow.request.scheme;
+};
- render: function render() {
- return _react2.default.createElement("th", _extends({}, this.props, { className: "col-icon " + (this.props.className || "") }));
- }
- })
- },
- render: function render() {
- var flow = this.props.flow;
+function IconColumn(_ref3) {
+ var flow = _ref3.flow;
- var icon;
- if (flow.response) {
- var contentType = _utils.ResponseUtils.getContentType(flow.response);
-
- //TODO: We should assign a type to the flow somewhere else.
- if (flow.response.status_code === 304) {
- icon = "resource-icon-not-modified";
- } else if (300 <= flow.response.status_code && flow.response.status_code < 400) {
- icon = "resource-icon-redirect";
- } else if (contentType && contentType.indexOf("image") >= 0) {
- icon = "resource-icon-image";
- } else if (contentType && contentType.indexOf("javascript") >= 0) {
- icon = "resource-icon-js";
- } else if (contentType && contentType.indexOf("css") >= 0) {
- icon = "resource-icon-css";
- } else if (contentType && contentType.indexOf("html") >= 0) {
- icon = "resource-icon-document";
- }
- }
- if (!icon) {
- icon = "resource-icon-plain";
- }
+ var icon = void 0;
+ if (flow.response) {
+ var contentType = _utils.ResponseUtils.getContentType(flow.response);
- icon += " resource-icon";
- return _react2.default.createElement(
- "td",
- { className: "col-icon" },
- _react2.default.createElement("div", { className: icon })
- );
+ //TODO: We should assign a type to the flow somewhere else.
+ if (flow.response.status_code === 304) {
+ icon = "resource-icon-not-modified";
+ } else if (300 <= flow.response.status_code && flow.response.status_code < 400) {
+ icon = "resource-icon-redirect";
+ } else if (contentType && contentType.indexOf("image") >= 0) {
+ icon = "resource-icon-image";
+ } else if (contentType && contentType.indexOf("javascript") >= 0) {
+ icon = "resource-icon-js";
+ } else if (contentType && contentType.indexOf("css") >= 0) {
+ icon = "resource-icon-css";
+ } else if (contentType && contentType.indexOf("html") >= 0) {
+ icon = "resource-icon-document";
+ }
+ }
+ if (!icon) {
+ icon = "resource-icon-plain";
}
-});
-var PathColumn = _react2.default.createClass({
- displayName: "PathColumn",
+ icon += " resource-icon";
+ return _react2.default.createElement(
+ "td",
+ { className: "col-icon" },
+ _react2.default.createElement("div", { className: icon })
+ );
+}
+IconColumn.Title = function (_ref4) {
+ var _ref4$className = _ref4.className;
+ var className = _ref4$className === undefined ? "" : _ref4$className;
- statics: {
- Title: _react2.default.createClass({
- displayName: "Title",
+ var props = _objectWithoutProperties(_ref4, ["className"]);
- render: function render() {
- return _react2.default.createElement(
- "th",
- _extends({}, this.props, { className: "col-path " + (this.props.className || "") }),
- "Path"
- );
- }
- }),
- sortKeyFun: function sortKeyFun(flow) {
- return _utils.RequestUtils.pretty_url(flow.request);
- }
- },
- render: function render() {
- var flow = this.props.flow;
- return _react2.default.createElement(
- "td",
- { className: "col-path" },
- flow.request.is_replay ? _react2.default.createElement("i", { className: "fa fa-fw fa-repeat pull-right" }) : null,
- flow.intercepted ? _react2.default.createElement("i", { className: "fa fa-fw fa-pause pull-right" }) : null,
- _utils.RequestUtils.pretty_url(flow.request)
- );
- }
-});
+ return _react2.default.createElement("th", _extends({}, props, { className: "col-icon " + className }));
+};
-var MethodColumn = _react2.default.createClass({
- displayName: "MethodColumn",
+function PathColumn(_ref5) {
+ var flow = _ref5.flow;
- statics: {
- Title: _react2.default.createClass({
- displayName: "Title",
+ return _react2.default.createElement(
+ "td",
+ { className: "col-path" },
+ flow.request.is_replay ? _react2.default.createElement("i", { className: "fa fa-fw fa-repeat pull-right" }) : null,
+ flow.intercepted ? _react2.default.createElement("i", { className: "fa fa-fw fa-pause pull-right" }) : null,
+ _utils.RequestUtils.pretty_url(flow.request)
+ );
+}
+PathColumn.Title = function (_ref6) {
+ var _ref6$className = _ref6.className;
+ var className = _ref6$className === undefined ? "" : _ref6$className;
- render: function render() {
- return _react2.default.createElement(
- "th",
- _extends({}, this.props, { className: "col-method " + (this.props.className || "") }),
- "Method"
- );
- }
- }),
- sortKeyFun: function sortKeyFun(flow) {
- return flow.request.method;
- }
- },
- render: function render() {
- var flow = this.props.flow;
- return _react2.default.createElement(
- "td",
- { className: "col-method" },
- flow.request.method
- );
- }
-});
+ var props = _objectWithoutProperties(_ref6, ["className"]);
-var StatusColumn = _react2.default.createClass({
- displayName: "StatusColumn",
+ return _react2.default.createElement(
+ "th",
+ _extends({}, props, { className: "col-path " + className }),
+ "Path"
+ );
+};
+PathColumn.sortKeyFun = function (flow) {
+ return _utils.RequestUtils.pretty_url(flow.request);
+};
- statics: {
- Title: _react2.default.createClass({
- displayName: "Title",
+function MethodColumn(_ref7) {
+ var flow = _ref7.flow;
- render: function render() {
- return _react2.default.createElement(
- "th",
- _extends({}, this.props, { className: "col-status " + (this.props.className || "") }),
- "Status"
- );
- }
- }),
- sortKeyFun: function sortKeyFun(flow) {
- return flow.response ? flow.response.status_code : undefined;
- }
- },
- render: function render() {
- var flow = this.props.flow;
- var status;
- if (flow.response) {
- status = flow.response.status_code;
- } else {
- status = null;
- }
- return _react2.default.createElement(
- "td",
- { className: "col-status" },
- status
- );
+ return _react2.default.createElement(
+ "td",
+ { className: "col-method" },
+ flow.request.method
+ );
+}
+MethodColumn.Title = function (_ref8) {
+ var _ref8$className = _ref8.className;
+ var className = _ref8$className === undefined ? "" : _ref8$className;
+
+ var props = _objectWithoutProperties(_ref8, ["className"]);
+
+ return _react2.default.createElement(
+ "th",
+ _extends({}, props, { className: "col-method " + className }),
+ "Method"
+ );
+};
+MethodColumn.sortKeyFun = function (flow) {
+ return flow.request.method;
+};
+
+function StatusColumn(_ref9) {
+ var flow = _ref9.flow;
+
+ var status = void 0;
+ if (flow.response) {
+ status = flow.response.status_code;
+ } else {
+ status = null;
}
-});
+ return _react2.default.createElement(
+ "td",
+ { className: "col-status" },
+ status
+ );
+}
+StatusColumn.Title = function (_ref10) {
+ var _ref10$className = _ref10.className;
+ var className = _ref10$className === undefined ? "" : _ref10$className;
-var SizeColumn = _react2.default.createClass({
- displayName: "SizeColumn",
+ var props = _objectWithoutProperties(_ref10, ["className"]);
- statics: {
- Title: _react2.default.createClass({
- displayName: "Title",
+ return _react2.default.createElement(
+ "th",
+ _extends({}, props, { className: "col-status " + className }),
+ "Status"
+ );
+};
+StatusColumn.sortKeyFun = function (flow) {
+ return flow.response ? flow.response.status_code : undefined;
+};
- render: function render() {
- return _react2.default.createElement(
- "th",
- _extends({}, this.props, { className: "col-size " + (this.props.className || "") }),
- "Size"
- );
- }
- }),
- sortKeyFun: function sortKeyFun(flow) {
- var total = flow.request.contentLength;
- if (flow.response) {
- total += flow.response.contentLength || 0;
- }
- return total;
- }
- },
- render: function render() {
- var flow = this.props.flow;
+function SizeColumn(_ref11) {
+ var flow = _ref11.flow;
- var total = flow.request.contentLength;
- if (flow.response) {
- total += flow.response.contentLength || 0;
- }
- var size = (0, _utils2.formatSize)(total);
- return _react2.default.createElement(
- "td",
- { className: "col-size" },
- size
- );
+ var total = flow.request.contentLength;
+ if (flow.response) {
+ total += flow.response.contentLength || 0;
}
-});
+ var size = (0, _utils2.formatSize)(total);
+ return _react2.default.createElement(
+ "td",
+ { className: "col-size" },
+ size
+ );
+}
+SizeColumn.Title = function (_ref12) {
+ var _ref12$className = _ref12.className;
+ var className = _ref12$className === undefined ? "" : _ref12$className;
-var TimeColumn = _react2.default.createClass({
- displayName: "TimeColumn",
+ var props = _objectWithoutProperties(_ref12, ["className"]);
- statics: {
- Title: _react2.default.createClass({
- displayName: "Title",
+ return _react2.default.createElement(
+ "th",
+ _extends({}, props, { className: "col-size " + className }),
+ "Size"
+ );
+};
+SizeColumn.sortKeyFun = function (flow) {
+ var total = flow.request.contentLength;
+ if (flow.response) {
+ total += flow.response.contentLength || 0;
+ }
+ return total;
+};
- render: function render() {
- return _react2.default.createElement(
- "th",
- _extends({}, this.props, { className: "col-time " + (this.props.className || "") }),
- "Time"
- );
- }
- }),
- sortKeyFun: function sortKeyFun(flow) {
- if (flow.response) {
- return flow.response.timestamp_end - flow.request.timestamp_start;
- }
- }
- },
- render: function render() {
- var flow = this.props.flow;
- var time;
- if (flow.response) {
- time = (0, _utils2.formatTimeDelta)(1000 * (flow.response.timestamp_end - flow.request.timestamp_start));
- } else {
- time = "...";
- }
- return _react2.default.createElement(
- "td",
- { className: "col-time" },
- time
- );
+function TimeColumn(_ref13) {
+ var flow = _ref13.flow;
+
+ var time = void 0;
+ if (flow.response) {
+ time = (0, _utils2.formatTimeDelta)(1000 * (flow.response.timestamp_end - flow.request.timestamp_start));
+ } else {
+ time = "...";
}
-});
+ return _react2.default.createElement(
+ "td",
+ { className: "col-time" },
+ time
+ );
+}
+TimeColumn.Title = function (_ref14) {
+ var _ref14$className = _ref14.className;
+ var className = _ref14$className === undefined ? "" : _ref14$className;
+
+ var props = _objectWithoutProperties(_ref14, ["className"]);
+
+ return _react2.default.createElement(
+ "th",
+ _extends({}, props, { className: "col-time " + className }),
+ "Time"
+ );
+};
+TimeColumn.sortKeyFun = function (flow) {
+ return flow.response.timestamp_end - flow.request.timestamp_start;
+};
var all_columns = [TLSColumn, IconColumn, PathColumn, MethodColumn, StatusColumn, SizeColumn, TimeColumn];
@@ -1525,12 +1495,17 @@ FlowRow.propTypes = {
selected: _react2.default.PropTypes.bool
};
-function FlowRow(props) {
- var flow = props.flow;
+function FlowRow(_ref) {
+ var flow = _ref.flow;
+ var selected = _ref.selected;
+ var highlight = _ref.highlight;
+ var columns = _ref.columns;
+ var selectFlow = _ref.selectFlow;
+
var className = (0, _classnames2.default)({
- "selected": props.selected,
- "highlighted": props.highlight && parseFilter(props.highlight)(flow),
+ "selected": selected,
+ "highlighted": highlight && parseFilter(highlight)(flow),
"intercepted": flow.intercepted,
"has-request": flow.request,
"has-response": flow.response
@@ -1539,10 +1514,10 @@ function FlowRow(props) {
return _react2.default.createElement(
"tr",
{ className: className, onClick: function onClick() {
- return props.selectFlow(flow);
+ return selectFlow(flow);
} },
- props.columns.map(function (Column) {
- return _react2.default.createElement(Column, { key: Column.displayName, flow: flow });
+ columns.map(function (Column) {
+ return _react2.default.createElement(Column, { key: Column.name, flow: flow });
})
);
}
@@ -1553,8 +1528,6 @@ var FlowRowContainer = (0, _reactRedux.connect)(function (state, ownProps) {
highlight: state.flows.highlight,
selected: state.flows.selected.indexOf(ownProps.flowId) >= 0
};
-}, function (dispatch, ownProps) {
- return {};
})(FlowRow);
var FlowTableHead = function (_React$Component) {
@@ -1608,11 +1581,11 @@ var FlowTableHead = function (_React$Component) {
null,
this.props.columns.map(function (Column) {
return _react2.default.createElement(Column.Title, {
- key: Column.displayName,
+ key: Column.name,
onClick: function onClick() {
return _this2.onClick(Column);
},
- className: sortColumn === Column && sortType
+ className: sortColumn === Column ? sortType : undefined
});
})
);
@@ -1769,8 +1742,6 @@ var FlowTableContainer = (0, _reactRedux.connect)(function (state) {
return {
flows: state.flows.view
};
-}, function (dispatch) {
- return {};
})(FlowTable);
exports.default = FlowTableContainer;
@@ -4757,22 +4728,23 @@ function makeList(actionType, fetchURL) {
list = [].concat(_toConsumableArray(state.list));
itemIndex = state.indexOf[action.item.id];
list[itemIndex] = action.item;
- return _extends({}, defaultState, {
- list: list
+ return _extends({}, state, {
+ list: list,
+ byId: _extends({}, state.byId, _defineProperty({}, action.item.id, action.item))
});
case REMOVE:
list = [].concat(_toConsumableArray(state.list));
itemIndex = state.indexOf[action.item.id];
list.splice(itemIndex, 1);
- return _extends({}, defaultState, {
+ return _extends({}, state, {
list: list,
byId: _extends({}, state.byId, _defineProperty({}, action.item.id, undefined)),
indexOf: _extends({}, state.indexOf, _defineProperty({}, action.item.id, undefined))
});
case REQUEST_LIST:
- return _extends({}, defaultState, {
+ return _extends({}, state, {
isFetching: true
});
@@ -4977,13 +4949,14 @@ function updateViewList(currentView, currentList, nextList, action) {
if (!isInView && shouldBeInView) return sortedInsert(currentView, sortFn, action.item);
if (isInView && !shouldBeInView) return sortedRemove(currentView, sortFn, action.item);
- if (isInView && shouldBeInView && sortFn && sortFn(currentItemState) !== sortFn(nextItemState)) {
+ if (isInView && shouldBeInView) {
var _ret = function () {
var s = [].concat(_toConsumableArray(currentView));
- s.sort(makeCompareFn(sortFn));
s.indexOf = function (x) {
return sortedIndexOf(s, x, sortFn);
};
+ s[s.indexOf(currentItemState)] = nextItemState;
+ if (sortFn && sortFn(currentItemState) !== sortFn(nextItemState)) s.sort(makeCompareFn(sortFn));
return {
v: s
};
diff --git a/mitmproxy/web/static/vendor.js b/mitmproxy/web/static/vendor.js
index 7fab13f5..b2e49043 100644
--- a/mitmproxy/web/static/vendor.js
+++ b/mitmproxy/web/static/vendor.js
@@ -7089,7 +7089,7 @@ var AutoFocusUtils = {
};
module.exports = AutoFocusUtils;
-},{"./ReactDOMComponentTree":116,"fbjs/lib/focusNode":223}],78:[function(require,module,exports){
+},{"./ReactDOMComponentTree":117,"fbjs/lib/focusNode":223}],78:[function(require,module,exports){
/**
* Copyright 2013-present Facebook, Inc.
* All rights reserved.
@@ -7644,7 +7644,7 @@ module.exports = CSSProperty;
var CSSProperty = require('./CSSProperty');
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
-var ReactPerf = require('./ReactPerf');
+var ReactInstrumentation = require('./ReactInstrumentation');
var camelizeStyleName = require('fbjs/lib/camelizeStyleName');
var dangerousStyleValue = require('./dangerousStyleValue');
@@ -7798,6 +7798,10 @@ var CSSPropertyOperations = {
* @param {ReactDOMComponent} component
*/
setValueForStyles: function (node, styles, component) {
+ if (process.env.NODE_ENV !== 'production') {
+ ReactInstrumentation.debugTool.onNativeOperation(component._debugID, 'update styles', styles);
+ }
+
var style = node.style;
for (var styleName in styles) {
if (!styles.hasOwnProperty(styleName)) {
@@ -7829,14 +7833,10 @@ var CSSPropertyOperations = {
};
-ReactPerf.measureMethods(CSSPropertyOperations, 'CSSPropertyOperations', {
- setValueForStyles: 'setValueForStyles'
-});
-
module.exports = CSSPropertyOperations;
}).call(this,require('_process'))
-},{"./CSSProperty":79,"./ReactPerf":157,"./dangerousStyleValue":189,"_process":33,"fbjs/lib/ExecutionEnvironment":215,"fbjs/lib/camelizeStyleName":217,"fbjs/lib/hyphenateStyleName":228,"fbjs/lib/memoizeStringOnly":235,"fbjs/lib/warning":239}],81:[function(require,module,exports){
+},{"./CSSProperty":79,"./ReactInstrumentation":146,"./dangerousStyleValue":189,"_process":33,"fbjs/lib/ExecutionEnvironment":215,"fbjs/lib/camelizeStyleName":217,"fbjs/lib/hyphenateStyleName":228,"fbjs/lib/memoizeStringOnly":235,"fbjs/lib/warning":239}],81:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -8271,7 +8271,8 @@ var ChangeEventPlugin = {
};
module.exports = ChangeEventPlugin;
-},{"./EventConstants":92,"./EventPluginHub":93,"./EventPropagators":96,"./ReactDOMComponentTree":116,"./ReactUpdates":165,"./SyntheticEvent":174,"./getEventTarget":197,"./isEventSupported":204,"./isTextInputElement":205,"fbjs/lib/ExecutionEnvironment":215,"fbjs/lib/keyOf":233}],83:[function(require,module,exports){
+},{"./EventConstants":92,"./EventPluginHub":93,"./EventPropagators":96,"./ReactDOMComponentTree":117,"./ReactUpdates":165,"./SyntheticEvent":174,"./getEventTarget":197,"./isEventSupported":204,"./isTextInputElement":205,"fbjs/lib/ExecutionEnvironment":215,"fbjs/lib/keyOf":233}],83:[function(require,module,exports){
+(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -8288,7 +8289,8 @@ module.exports = ChangeEventPlugin;
var DOMLazyTree = require('./DOMLazyTree');
var Danger = require('./Danger');
var ReactMultiChildUpdateTypes = require('./ReactMultiChildUpdateTypes');
-var ReactPerf = require('./ReactPerf');
+var ReactDOMComponentTree = require('./ReactDOMComponentTree');
+var ReactInstrumentation = require('./ReactInstrumentation');
var createMicrosoftUnsafeLocalFunction = require('./createMicrosoftUnsafeLocalFunction');
var setInnerHTML = require('./setInnerHTML');
@@ -8383,6 +8385,25 @@ function replaceDelimitedText(openingComment, closingComment, stringText) {
removeDelimitedText(parentNode, openingComment, closingComment);
}
}
+
+ if (process.env.NODE_ENV !== 'production') {
+ ReactInstrumentation.debugTool.onNativeOperation(ReactDOMComponentTree.getInstanceFromNode(openingComment)._debugID, 'replace text', stringText);
+ }
+}
+
+var dangerouslyReplaceNodeWithMarkup = Danger.dangerouslyReplaceNodeWithMarkup;
+if (process.env.NODE_ENV !== 'production') {
+ dangerouslyReplaceNodeWithMarkup = function (oldChild, markup, prevInstance) {
+ Danger.dangerouslyReplaceNodeWithMarkup(oldChild, markup);
+ if (prevInstance._debugID !== 0) {
+ ReactInstrumentation.debugTool.onNativeOperation(prevInstance._debugID, 'replace with', markup.toString());
+ } else {
+ var nextInstance = ReactDOMComponentTree.getInstanceFromNode(markup.node);
+ if (nextInstance._debugID !== 0) {
+ ReactInstrumentation.debugTool.onNativeOperation(nextInstance._debugID, 'mount', markup.toString());
+ }
+ }
+ };
}
/**
@@ -8390,7 +8411,7 @@ function replaceDelimitedText(openingComment, closingComment, stringText) {
*/
var DOMChildrenOperations = {
- dangerouslyReplaceNodeWithMarkup: Danger.dangerouslyReplaceNodeWithMarkup,
+ dangerouslyReplaceNodeWithMarkup: dangerouslyReplaceNodeWithMarkup,
replaceDelimitedText: replaceDelimitedText,
@@ -8402,23 +8423,42 @@ var DOMChildrenOperations = {
* @internal
*/
processUpdates: function (parentNode, updates) {
+ if (process.env.NODE_ENV !== 'production') {
+ var parentNodeDebugID = ReactDOMComponentTree.getInstanceFromNode(parentNode)._debugID;
+ }
+
for (var k = 0; k < updates.length; k++) {
var update = updates[k];
switch (update.type) {
case ReactMultiChildUpdateTypes.INSERT_MARKUP:
insertLazyTreeChildAt(parentNode, update.content, getNodeAfter(parentNode, update.afterNode));
+ if (process.env.NODE_ENV !== 'production') {
+ ReactInstrumentation.debugTool.onNativeOperation(parentNodeDebugID, 'insert child', { toIndex: update.toIndex, content: update.content.toString() });
+ }
break;
case ReactMultiChildUpdateTypes.MOVE_EXISTING:
moveChild(parentNode, update.fromNode, getNodeAfter(parentNode, update.afterNode));
+ if (process.env.NODE_ENV !== 'production') {
+ ReactInstrumentation.debugTool.onNativeOperation(parentNodeDebugID, 'move child', { fromIndex: update.fromIndex, toIndex: update.toIndex });
+ }
break;
case ReactMultiChildUpdateTypes.SET_MARKUP:
setInnerHTML(parentNode, update.content);
+ if (process.env.NODE_ENV !== 'production') {
+ ReactInstrumentation.debugTool.onNativeOperation(parentNodeDebugID, 'replace children', update.content.toString());
+ }
break;
case ReactMultiChildUpdateTypes.TEXT_CONTENT:
setTextContent(parentNode, update.content);
+ if (process.env.NODE_ENV !== 'production') {
+ ReactInstrumentation.debugTool.onNativeOperation(parentNodeDebugID, 'replace text', update.content.toString());
+ }
break;
case ReactMultiChildUpdateTypes.REMOVE_NODE:
removeChild(parentNode, update.fromNode);
+ if (process.env.NODE_ENV !== 'production') {
+ ReactInstrumentation.debugTool.onNativeOperation(parentNodeDebugID, 'remove child', { fromIndex: update.fromIndex });
+ }
break;
}
}
@@ -8426,12 +8466,10 @@ var DOMChildrenOperations = {
};
-ReactPerf.measureMethods(DOMChildrenOperations, 'DOMChildrenOperations', {
- replaceDelimitedText: 'replaceDelimitedText'
-});
-
module.exports = DOMChildrenOperations;
-},{"./DOMLazyTree":84,"./Danger":88,"./ReactMultiChildUpdateTypes":152,"./ReactPerf":157,"./createMicrosoftUnsafeLocalFunction":188,"./setInnerHTML":209,"./setTextContent":210}],84:[function(require,module,exports){
+}).call(this,require('_process'))
+
+},{"./DOMLazyTree":84,"./Danger":88,"./ReactDOMComponentTree":117,"./ReactInstrumentation":146,"./ReactMultiChildUpdateTypes":151,"./createMicrosoftUnsafeLocalFunction":188,"./setInnerHTML":209,"./setTextContent":210,"_process":33}],84:[function(require,module,exports){
/**
* Copyright 2015-present, Facebook, Inc.
* All rights reserved.
@@ -8445,9 +8483,14 @@ module.exports = DOMChildrenOperations;
'use strict';
+var DOMNamespaces = require('./DOMNamespaces');
+
var createMicrosoftUnsafeLocalFunction = require('./createMicrosoftUnsafeLocalFunction');
var setTextContent = require('./setTextContent');
+var ELEMENT_NODE_TYPE = 1;
+var DOCUMENT_FRAGMENT_NODE_TYPE = 11;
+
/**
* In IE (8-11) and Edge, appending nodes with no children is dramatically
* faster than appending a full subtree, so we essentially queue up the
@@ -8482,8 +8525,10 @@ var insertTreeBefore = createMicrosoftUnsafeLocalFunction(function (parentNode,
// DocumentFragments aren't actually part of the DOM after insertion so
// appending children won't update the DOM. We need to ensure the fragment
// is properly populated first, breaking out of our lazy approach for just
- // this level.
- if (tree.node.nodeType === 11) {
+ // this level. Also, some <object> plugins (like Flash Player) will read
+ // <param> nodes immediately upon insertion into the DOM, so <object>
+ // must also be populated prior to insertion into the DOM.
+ if (tree.node.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE || tree.node.nodeType === ELEMENT_NODE_TYPE && tree.node.nodeName.toLowerCase() === 'object' && (tree.node.namespaceURI == null || tree.node.namespaceURI === DOMNamespaces.html)) {
insertTreeChildren(tree);
parentNode.insertBefore(tree.node, referenceNode);
} else {
@@ -8521,12 +8566,17 @@ function queueText(tree, text) {
}
}
+function toString() {
+ return this.node.nodeName;
+}
+
function DOMLazyTree(node) {
return {
node: node,
children: [],
html: null,
- text: null
+ text: null,
+ toString: toString
};
}
@@ -8537,7 +8587,7 @@ DOMLazyTree.queueHTML = queueHTML;
DOMLazyTree.queueText = queueText;
module.exports = DOMLazyTree;
-},{"./createMicrosoftUnsafeLocalFunction":188,"./setTextContent":210}],85:[function(require,module,exports){
+},{"./DOMNamespaces":85,"./createMicrosoftUnsafeLocalFunction":188,"./setTextContent":210}],85:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -8791,8 +8841,9 @@ module.exports = DOMProperty;
'use strict';
var DOMProperty = require('./DOMProperty');
+var ReactDOMComponentTree = require('./ReactDOMComponentTree');
var ReactDOMInstrumentation = require('./ReactDOMInstrumentation');
-var ReactPerf = require('./ReactPerf');
+var ReactInstrumentation = require('./ReactInstrumentation');
var quoteAttributeValueForBrowser = require('./quoteAttributeValueForBrowser');
var warning = require('fbjs/lib/warning');
@@ -8900,9 +8951,6 @@ var DOMPropertyOperations = {
* @param {*} value
*/
setValueForProperty: function (node, name, value) {
- if (process.env.NODE_ENV !== 'production') {
- ReactDOMInstrumentation.debugTool.onSetValueForProperty(node, name, value);
- }
var propertyInfo = DOMProperty.properties.hasOwnProperty(name) ? DOMProperty.properties[name] : null;
if (propertyInfo) {
var mutationMethod = propertyInfo.mutationMethod;
@@ -8910,6 +8958,7 @@ var DOMPropertyOperations = {
mutationMethod(node, value);
} else if (shouldIgnoreValue(propertyInfo, value)) {
this.deleteValueForProperty(node, name);
+ return;
} else if (propertyInfo.mustUseProperty) {
var propName = propertyInfo.propertyName;
// Must explicitly cast values for HAS_SIDE_EFFECTS-properties to the
@@ -8934,6 +8983,14 @@ var DOMPropertyOperations = {
}
} else if (DOMProperty.isCustomAttribute(name)) {
DOMPropertyOperations.setValueForAttribute(node, name, value);
+ return;
+ }
+
+ if (process.env.NODE_ENV !== 'production') {
+ ReactDOMInstrumentation.debugTool.onSetValueForProperty(node, name, value);
+ var payload = {};
+ payload[name] = value;
+ ReactInstrumentation.debugTool.onNativeOperation(ReactDOMComponentTree.getInstanceFromNode(node)._debugID, 'update attribute', payload);
}
},
@@ -8946,6 +9003,12 @@ var DOMPropertyOperations = {
} else {
node.setAttribute(name, '' + value);
}
+
+ if (process.env.NODE_ENV !== 'production') {
+ var payload = {};
+ payload[name] = value;
+ ReactInstrumentation.debugTool.onNativeOperation(ReactDOMComponentTree.getInstanceFromNode(node)._debugID, 'update attribute', payload);
+ }
},
/**
@@ -8955,9 +9018,6 @@ var DOMPropertyOperations = {
* @param {string} name
*/
deleteValueForProperty: function (node, name) {
- if (process.env.NODE_ENV !== 'production') {
- ReactDOMInstrumentation.debugTool.onDeleteValueForProperty(node, name);
- }
var propertyInfo = DOMProperty.properties.hasOwnProperty(name) ? DOMProperty.properties[name] : null;
if (propertyInfo) {
var mutationMethod = propertyInfo.mutationMethod;
@@ -8979,20 +9039,19 @@ var DOMPropertyOperations = {
} else if (DOMProperty.isCustomAttribute(name)) {
node.removeAttribute(name);
}
+
+ if (process.env.NODE_ENV !== 'production') {
+ ReactDOMInstrumentation.debugTool.onDeleteValueForProperty(node, name);
+ ReactInstrumentation.debugTool.onNativeOperation(ReactDOMComponentTree.getInstanceFromNode(node)._debugID, 'remove attribute', name);
+ }
}
};
-ReactPerf.measureMethods(DOMPropertyOperations, 'DOMPropertyOperations', {
- setValueForProperty: 'setValueForProperty',
- setValueForAttribute: 'setValueForAttribute',
- deleteValueForProperty: 'deleteValueForProperty'
-});
-
module.exports = DOMPropertyOperations;
}).call(this,require('_process'))
-},{"./DOMProperty":86,"./ReactDOMInstrumentation":124,"./ReactPerf":157,"./quoteAttributeValueForBrowser":207,"_process":33,"fbjs/lib/warning":239}],88:[function(require,module,exports){
+},{"./DOMProperty":86,"./ReactDOMComponentTree":117,"./ReactDOMInstrumentation":125,"./ReactInstrumentation":146,"./quoteAttributeValueForBrowser":207,"_process":33,"fbjs/lib/warning":239}],88:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -9325,7 +9384,7 @@ var EnterLeaveEventPlugin = {
};
module.exports = EnterLeaveEventPlugin;
-},{"./EventConstants":92,"./EventPropagators":96,"./ReactDOMComponentTree":116,"./SyntheticMouseEvent":178,"fbjs/lib/keyOf":233}],92:[function(require,module,exports){
+},{"./EventConstants":92,"./EventPropagators":96,"./ReactDOMComponentTree":117,"./SyntheticMouseEvent":178,"fbjs/lib/keyOf":233}],92:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -9662,7 +9721,7 @@ var EventPluginHub = {
module.exports = EventPluginHub;
}).call(this,require('_process'))
-},{"./EventPluginRegistry":94,"./EventPluginUtils":95,"./ReactErrorUtils":140,"./accumulateInto":185,"./forEachAccumulated":193,"_process":33,"fbjs/lib/invariant":229}],94:[function(require,module,exports){
+},{"./EventPluginRegistry":94,"./EventPluginUtils":95,"./ReactErrorUtils":139,"./accumulateInto":185,"./forEachAccumulated":193,"_process":33,"fbjs/lib/invariant":229}],94:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -10138,7 +10197,7 @@ var EventPluginUtils = {
module.exports = EventPluginUtils;
}).call(this,require('_process'))
-},{"./EventConstants":92,"./ReactErrorUtils":140,"_process":33,"fbjs/lib/invariant":229,"fbjs/lib/warning":239}],96:[function(require,module,exports){
+},{"./EventConstants":92,"./ReactErrorUtils":139,"_process":33,"fbjs/lib/invariant":229,"fbjs/lib/warning":239}],96:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -10781,7 +10840,7 @@ var LinkedValueUtils = {
module.exports = LinkedValueUtils;
}).call(this,require('_process'))
-},{"./ReactPropTypeLocations":159,"./ReactPropTypes":160,"_process":33,"fbjs/lib/invariant":229,"fbjs/lib/warning":239}],101:[function(require,module,exports){
+},{"./ReactPropTypeLocations":158,"./ReactPropTypes":159,"_process":33,"fbjs/lib/invariant":229,"fbjs/lib/warning":239}],101:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -10995,7 +11054,7 @@ var React = {
module.exports = React;
}).call(this,require('_process'))
-},{"./ReactChildren":105,"./ReactClass":106,"./ReactComponent":107,"./ReactDOMFactories":120,"./ReactElement":137,"./ReactElementValidator":138,"./ReactPropTypes":160,"./ReactVersion":166,"./onlyChild":206,"_process":33,"fbjs/lib/warning":239,"object-assign":32}],103:[function(require,module,exports){
+},{"./ReactChildren":105,"./ReactClass":106,"./ReactComponent":107,"./ReactDOMFactories":121,"./ReactElement":136,"./ReactElementValidator":137,"./ReactPropTypes":159,"./ReactVersion":166,"./onlyChild":206,"_process":33,"fbjs/lib/warning":239,"object-assign":32}],103:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -11313,7 +11372,7 @@ var ReactBrowserEventEmitter = _assign({}, ReactEventEmitterMixin, {
});
module.exports = ReactBrowserEventEmitter;
-},{"./EventConstants":92,"./EventPluginRegistry":94,"./ReactEventEmitterMixin":141,"./ViewportMetrics":184,"./getVendorPrefixedEventName":202,"./isEventSupported":204,"object-assign":32}],104:[function(require,module,exports){
+},{"./EventConstants":92,"./EventPluginRegistry":94,"./ReactEventEmitterMixin":140,"./ViewportMetrics":184,"./getVendorPrefixedEventName":202,"./isEventSupported":204,"object-assign":32}],104:[function(require,module,exports){
(function (process){
/**
* Copyright 2014-present, Facebook, Inc.
@@ -11442,7 +11501,7 @@ var ReactChildReconciler = {
module.exports = ReactChildReconciler;
}).call(this,require('_process'))
-},{"./KeyEscapeUtils":99,"./ReactReconciler":162,"./instantiateReactComponent":203,"./shouldUpdateReactComponent":211,"./traverseAllChildren":212,"_process":33,"fbjs/lib/warning":239}],105:[function(require,module,exports){
+},{"./KeyEscapeUtils":99,"./ReactReconciler":161,"./instantiateReactComponent":203,"./shouldUpdateReactComponent":211,"./traverseAllChildren":212,"_process":33,"fbjs/lib/warning":239}],105:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -11500,6 +11559,8 @@ function forEachSingleChild(bookKeeping, child, name) {
/**
* Iterates through children that are typically specified as `props.children`.
*
+ * See https://facebook.github.io/react/docs/top-level-api.html#react.children.foreach
+ *
* The provided forEachFunc(child, index) will be called for each
* leaf child.
*
@@ -11575,7 +11636,9 @@ function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
/**
* Maps children that are typically specified as `props.children`.
*
- * The provided mapFunction(child, index) will be called for each
+ * See https://facebook.github.io/react/docs/top-level-api.html#react.children.map
+ *
+ * The provided mapFunction(child, key, index) will be called for each
* leaf child.
*
* @param {?*} children Children tree container.
@@ -11600,6 +11663,8 @@ function forEachSingleChildDummy(traverseContext, child, name) {
* Count the number of children that are typically specified as
* `props.children`.
*
+ * See https://facebook.github.io/react/docs/top-level-api.html#react.children.count
+ *
* @param {?*} children Children tree container.
* @return {number} The number of children.
*/
@@ -11610,6 +11675,8 @@ function countChildren(children, context) {
/**
* Flatten a children object (typically specified as `props.children`) and
* return an array with appropriately re-keyed children.
+ *
+ * See https://facebook.github.io/react/docs/top-level-api.html#react.children.toarray
*/
function toArray(children) {
var result = [];
@@ -11626,7 +11693,7 @@ var ReactChildren = {
};
module.exports = ReactChildren;
-},{"./PooledClass":101,"./ReactElement":137,"./traverseAllChildren":212,"fbjs/lib/emptyFunction":221}],106:[function(require,module,exports){
+},{"./PooledClass":101,"./ReactElement":136,"./traverseAllChildren":212,"fbjs/lib/emptyFunction":221}],106:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -12257,6 +12324,7 @@ var ReactClass = {
/**
* Creates a composite component class given a class specification.
+ * See https://facebook.github.io/react/docs/top-level-api.html#react.createclass
*
* @param {object} spec Class specification (which must define `render`).
* @return {function} Component constructor function.
@@ -12353,7 +12421,7 @@ var ReactClass = {
module.exports = ReactClass;
}).call(this,require('_process'))
-},{"./ReactComponent":107,"./ReactElement":137,"./ReactNoopUpdateQueue":155,"./ReactPropTypeLocationNames":158,"./ReactPropTypeLocations":159,"_process":33,"fbjs/lib/emptyObject":222,"fbjs/lib/invariant":229,"fbjs/lib/keyMirror":232,"fbjs/lib/keyOf":233,"fbjs/lib/warning":239,"object-assign":32}],107:[function(require,module,exports){
+},{"./ReactComponent":107,"./ReactElement":136,"./ReactNoopUpdateQueue":155,"./ReactPropTypeLocationNames":157,"./ReactPropTypeLocations":158,"_process":33,"fbjs/lib/emptyObject":222,"fbjs/lib/invariant":229,"fbjs/lib/keyMirror":232,"fbjs/lib/keyOf":233,"fbjs/lib/warning":239,"object-assign":32}],107:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -12478,7 +12546,7 @@ if (process.env.NODE_ENV !== 'production') {
module.exports = ReactComponent;
}).call(this,require('_process'))
-},{"./ReactInstrumentation":147,"./ReactNoopUpdateQueue":155,"./canDefineProperty":187,"_process":33,"fbjs/lib/emptyObject":222,"fbjs/lib/invariant":229,"fbjs/lib/warning":239}],108:[function(require,module,exports){
+},{"./ReactInstrumentation":146,"./ReactNoopUpdateQueue":155,"./canDefineProperty":187,"_process":33,"fbjs/lib/emptyObject":222,"fbjs/lib/invariant":229,"fbjs/lib/warning":239}],108:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -12494,7 +12562,6 @@ module.exports = ReactComponent;
var DOMChildrenOperations = require('./DOMChildrenOperations');
var ReactDOMIDOperations = require('./ReactDOMIDOperations');
-var ReactPerf = require('./ReactPerf');
/**
* Abstracts away all functionality of the reconciler that requires knowledge of
@@ -12518,12 +12585,8 @@ var ReactComponentBrowserEnvironment = {
};
-ReactPerf.measureMethods(ReactComponentBrowserEnvironment, 'ReactComponentBrowserEnvironment', {
- replaceNodeWithMarkup: 'replaceNodeWithMarkup'
-});
-
module.exports = ReactComponentBrowserEnvironment;
-},{"./DOMChildrenOperations":83,"./ReactDOMIDOperations":122,"./ReactPerf":157}],109:[function(require,module,exports){
+},{"./DOMChildrenOperations":83,"./ReactDOMIDOperations":123}],109:[function(require,module,exports){
(function (process){
/**
* Copyright 2014-present, Facebook, Inc.
@@ -12581,6 +12644,155 @@ module.exports = ReactComponentEnvironment;
},{"_process":33,"fbjs/lib/invariant":229}],110:[function(require,module,exports){
(function (process){
/**
+ * Copyright 2016-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule ReactComponentTreeDevtool
+ */
+
+'use strict';
+
+var invariant = require('fbjs/lib/invariant');
+
+var tree = {};
+var rootIDs = [];
+
+function updateTree(id, update) {
+ if (!tree[id]) {
+ tree[id] = {
+ parentID: null,
+ ownerID: null,
+ text: null,
+ childIDs: [],
+ displayName: 'Unknown',
+ isMounted: false,
+ updateCount: 0
+ };
+ }
+ update(tree[id]);
+}
+
+function purgeDeep(id) {
+ var item = tree[id];
+ if (item) {
+ var childIDs = item.childIDs;
+
+ delete tree[id];
+ childIDs.forEach(purgeDeep);
+ }
+}
+
+var ReactComponentTreeDevtool = {
+ onSetDisplayName: function (id, displayName) {
+ updateTree(id, function (item) {
+ return item.displayName = displayName;
+ });
+ },
+ onSetChildren: function (id, nextChildIDs) {
+ updateTree(id, function (item) {
+ var prevChildIDs = item.childIDs;
+ item.childIDs = nextChildIDs;
+
+ nextChildIDs.forEach(function (nextChildID) {
+ var nextChild = tree[nextChildID];
+ !nextChild ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Expected devtool events to fire for the child ' + 'before its parent includes it in onSetChildren().') : invariant(false) : void 0;
+ !(nextChild.displayName != null) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Expected onSetDisplayName() to fire for the child ' + 'before its parent includes it in onSetChildren().') : invariant(false) : void 0;
+ !(nextChild.childIDs != null || nextChild.text != null) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Expected onSetChildren() or onSetText() to fire for the child ' + 'before its parent includes it in onSetChildren().') : invariant(false) : void 0;
+ !nextChild.isMounted ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Expected onMountComponent() to fire for the child ' + 'before its parent includes it in onSetChildren().') : invariant(false) : void 0;
+
+ if (prevChildIDs.indexOf(nextChildID) === -1) {
+ nextChild.parentID = id;
+ }
+ });
+ });
+ },
+ onSetOwner: function (id, ownerID) {
+ updateTree(id, function (item) {
+ return item.ownerID = ownerID;
+ });
+ },
+ onSetText: function (id, text) {
+ updateTree(id, function (item) {
+ return item.text = text;
+ });
+ },
+ onMountComponent: function (id) {
+ updateTree(id, function (item) {
+ return item.isMounted = true;
+ });
+ },
+ onMountRootComponent: function (id) {
+ rootIDs.push(id);
+ },
+ onUpdateComponent: function (id) {
+ updateTree(id, function (item) {
+ return item.updateCount++;
+ });
+ },
+ onUnmountComponent: function (id) {
+ updateTree(id, function (item) {
+ return item.isMounted = false;
+ });
+ rootIDs = rootIDs.filter(function (rootID) {
+ return rootID !== id;
+ });
+ },
+ purgeUnmountedComponents: function () {
+ if (ReactComponentTreeDevtool._preventPurging) {
+ // Should only be used for testing.
+ return;
+ }
+
+ Object.keys(tree).filter(function (id) {
+ return !tree[id].isMounted;
+ }).forEach(purgeDeep);
+ },
+ isMounted: function (id) {
+ var item = tree[id];
+ return item ? item.isMounted : false;
+ },
+ getChildIDs: function (id) {
+ var item = tree[id];
+ return item ? item.childIDs : [];
+ },
+ getDisplayName: function (id) {
+ var item = tree[id];
+ return item ? item.displayName : 'Unknown';
+ },
+ getOwnerID: function (id) {
+ var item = tree[id];
+ return item ? item.ownerID : null;
+ },
+ getParentID: function (id) {
+ var item = tree[id];
+ return item ? item.parentID : null;
+ },
+ getText: function (id) {
+ var item = tree[id];
+ return item ? item.text : null;
+ },
+ getUpdateCount: function (id) {
+ var item = tree[id];
+ return item ? item.updateCount : 0;
+ },
+ getRootIDs: function () {
+ return rootIDs;
+ },
+ getRegisteredIDs: function () {
+ return Object.keys(tree);
+ }
+};
+
+module.exports = ReactComponentTreeDevtool;
+}).call(this,require('_process'))
+
+},{"_process":33,"fbjs/lib/invariant":229}],111:[function(require,module,exports){
+(function (process){
+/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
@@ -12602,7 +12814,6 @@ var ReactErrorUtils = require('./ReactErrorUtils');
var ReactInstanceMap = require('./ReactInstanceMap');
var ReactInstrumentation = require('./ReactInstrumentation');
var ReactNodeTypes = require('./ReactNodeTypes');
-var ReactPerf = require('./ReactPerf');
var ReactPropTypeLocations = require('./ReactPropTypeLocations');
var ReactPropTypeLocationNames = require('./ReactPropTypeLocationNames');
var ReactReconciler = require('./ReactReconciler');
@@ -12638,6 +12849,28 @@ function warnIfInvalidElement(Component, element) {
}
}
+function invokeComponentDidMountWithTimer() {
+ var publicInstance = this._instance;
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'componentDidMount');
+ }
+ publicInstance.componentDidMount();
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'componentDidMount');
+ }
+}
+
+function invokeComponentDidUpdateWithTimer(prevProps, prevState, prevContext) {
+ var publicInstance = this._instance;
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'componentDidUpdate');
+ }
+ publicInstance.componentDidUpdate(prevProps, prevState, prevContext);
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'componentDidUpdate');
+ }
+}
+
function shouldConstruct(Component) {
return Component.prototype && Component.prototype.isReactComponent;
}
@@ -12697,6 +12930,7 @@ var ReactCompositeComponentMixin = {
this._nativeContainerInfo = null;
// See ReactUpdateQueue
+ this._updateBatchNumber = null;
this._pendingElement = null;
this._pendingStateQueue = null;
this._pendingReplaceState = false;
@@ -12805,7 +13039,11 @@ var ReactCompositeComponentMixin = {
}
if (inst.componentDidMount) {
- transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
+ if (process.env.NODE_ENV !== 'production') {
+ transaction.getReactMountReady().enqueue(invokeComponentDidMountWithTimer, this);
+ } else {
+ transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
+ }
}
return markup;
@@ -12826,11 +13064,35 @@ var ReactCompositeComponentMixin = {
_constructComponentWithoutOwner: function (publicProps, publicContext) {
var Component = this._currentElement.type;
+ var instanceOrElement;
if (shouldConstruct(Component)) {
- return new Component(publicProps, publicContext, ReactUpdateQueue);
+ if (process.env.NODE_ENV !== 'production') {
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'ctor');
+ }
+ }
+ instanceOrElement = new Component(publicProps, publicContext, ReactUpdateQueue);
+ if (process.env.NODE_ENV !== 'production') {
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'ctor');
+ }
+ }
} else {
- return Component(publicProps, publicContext, ReactUpdateQueue);
+ // This can still be an instance in case of factory components
+ // but we'll count this as time spent rendering as the more common case.
+ if (process.env.NODE_ENV !== 'production') {
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'render');
+ }
+ }
+ instanceOrElement = Component(publicProps, publicContext, ReactUpdateQueue);
+ if (process.env.NODE_ENV !== 'production') {
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'render');
+ }
+ }
}
+ return instanceOrElement;
},
performInitialMountWithErrorHandling: function (renderedElement, nativeParent, nativeContainerInfo, transaction, context) {
@@ -12860,7 +13122,17 @@ var ReactCompositeComponentMixin = {
performInitialMount: function (renderedElement, nativeParent, nativeContainerInfo, transaction, context) {
var inst = this._instance;
if (inst.componentWillMount) {
+ if (process.env.NODE_ENV !== 'production') {
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'componentWillMount');
+ }
+ }
inst.componentWillMount();
+ if (process.env.NODE_ENV !== 'production') {
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'componentWillMount');
+ }
+ }
// When mounting, calls to `setState` by `componentWillMount` will set
// `this._pendingStateQueue` without triggering a re-render.
if (this._pendingStateQueue) {
@@ -12878,6 +13150,12 @@ var ReactCompositeComponentMixin = {
var markup = ReactReconciler.mountComponent(this._renderedComponent, transaction, nativeParent, nativeContainerInfo, this._processChildContext(context));
+ if (process.env.NODE_ENV !== 'production') {
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onSetChildren(this._debugID, this._renderedComponent._debugID !== 0 ? [this._renderedComponent._debugID] : []);
+ }
+ }
+
return markup;
},
@@ -12899,12 +13177,22 @@ var ReactCompositeComponentMixin = {
if (inst.componentWillUnmount && !inst._calledComponentWillUnmount) {
inst._calledComponentWillUnmount = true;
+ if (process.env.NODE_ENV !== 'production') {
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'componentWillUnmount');
+ }
+ }
if (safely) {
var name = this.getName() + '.componentWillUnmount()';
ReactErrorUtils.invokeGuardedCallback(name, inst.componentWillUnmount.bind(inst));
} else {
inst.componentWillUnmount();
}
+ if (process.env.NODE_ENV !== 'production') {
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'componentWillUnmount');
+ }
+ }
}
if (this._renderedComponent) {
@@ -13087,10 +13375,10 @@ var ReactCompositeComponentMixin = {
performUpdateIfNecessary: function (transaction) {
if (this._pendingElement != null) {
ReactReconciler.receiveComponent(this, this._pendingElement, transaction, this._context);
- }
-
- if (this._pendingStateQueue !== null || this._pendingForceUpdate) {
+ } else if (this._pendingStateQueue !== null || this._pendingForceUpdate) {
this.updateComponent(transaction, this._currentElement, this._currentElement, this._context, this._context);
+ } else {
+ this._updateBatchNumber = null;
}
},
@@ -13137,17 +13425,41 @@ var ReactCompositeComponentMixin = {
// _pendingStateQueue which will ensure that any state updates gets
// immediately reconciled instead of waiting for the next batch.
if (willReceive && inst.componentWillReceiveProps) {
+ if (process.env.NODE_ENV !== 'production') {
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'componentWillReceiveProps');
+ }
+ }
inst.componentWillReceiveProps(nextProps, nextContext);
+ if (process.env.NODE_ENV !== 'production') {
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'componentWillReceiveProps');
+ }
+ }
}
var nextState = this._processPendingState(nextProps, nextContext);
+ var shouldUpdate = true;
- var shouldUpdate = this._pendingForceUpdate || !inst.shouldComponentUpdate || inst.shouldComponentUpdate(nextProps, nextState, nextContext);
+ if (!this._pendingForceUpdate && inst.shouldComponentUpdate) {
+ if (process.env.NODE_ENV !== 'production') {
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'shouldComponentUpdate');
+ }
+ }
+ shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext);
+ if (process.env.NODE_ENV !== 'production') {
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'shouldComponentUpdate');
+ }
+ }
+ }
if (process.env.NODE_ENV !== 'production') {
process.env.NODE_ENV !== 'production' ? warning(shouldUpdate !== undefined, '%s.shouldComponentUpdate(): Returned undefined instead of a ' + 'boolean value. Make sure to return true or false.', this.getName() || 'ReactCompositeComponent') : void 0;
}
+ this._updateBatchNumber = null;
if (shouldUpdate) {
this._pendingForceUpdate = false;
// Will set `this.props`, `this.state` and `this.context`.
@@ -13213,7 +13525,17 @@ var ReactCompositeComponentMixin = {
}
if (inst.componentWillUpdate) {
+ if (process.env.NODE_ENV !== 'production') {
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'componentWillUpdate');
+ }
+ }
inst.componentWillUpdate(nextProps, nextState, nextContext);
+ if (process.env.NODE_ENV !== 'production') {
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'componentWillUpdate');
+ }
+ }
}
this._currentElement = nextElement;
@@ -13225,7 +13547,11 @@ var ReactCompositeComponentMixin = {
this._updateRenderedComponent(transaction, unmaskedContext);
if (hasComponentDidUpdate) {
- transaction.getReactMountReady().enqueue(inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext), inst);
+ if (process.env.NODE_ENV !== 'production') {
+ transaction.getReactMountReady().enqueue(invokeComponentDidUpdateWithTimer.bind(this, prevProps, prevState, prevContext), this);
+ } else {
+ transaction.getReactMountReady().enqueue(inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext), inst);
+ }
}
},
@@ -13247,8 +13573,16 @@ var ReactCompositeComponentMixin = {
this._renderedNodeType = ReactNodeTypes.getType(nextRenderedElement);
this._renderedComponent = this._instantiateReactComponent(nextRenderedElement);
+
var nextMarkup = ReactReconciler.mountComponent(this._renderedComponent, transaction, this._nativeParent, this._nativeContainerInfo, this._processChildContext(context));
- this._replaceNodeWithMarkup(oldNativeNode, nextMarkup);
+
+ if (process.env.NODE_ENV !== 'production') {
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onSetChildren(this._debugID, this._renderedComponent._debugID !== 0 ? [this._renderedComponent._debugID] : []);
+ }
+ }
+
+ this._replaceNodeWithMarkup(oldNativeNode, nextMarkup, prevComponentInstance);
}
},
@@ -13257,8 +13591,8 @@ var ReactCompositeComponentMixin = {
*
* @protected
*/
- _replaceNodeWithMarkup: function (oldNativeNode, nextMarkup) {
- ReactComponentEnvironment.replaceNodeWithMarkup(oldNativeNode, nextMarkup);
+ _replaceNodeWithMarkup: function (oldNativeNode, nextMarkup, prevInstance) {
+ ReactComponentEnvironment.replaceNodeWithMarkup(oldNativeNode, nextMarkup, prevInstance);
},
/**
@@ -13266,8 +13600,20 @@ var ReactCompositeComponentMixin = {
*/
_renderValidatedComponentWithoutOwnerOrContext: function () {
var inst = this._instance;
+
+ if (process.env.NODE_ENV !== 'production') {
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onBeginLifeCycleTimer(this._debugID, 'render');
+ }
+ }
var renderedComponent = inst.render();
if (process.env.NODE_ENV !== 'production') {
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onEndLifeCycleTimer(this._debugID, 'render');
+ }
+ }
+
+ if (process.env.NODE_ENV !== 'production') {
// We allow auto-mocks to proceed as if they're returning null.
if (renderedComponent === undefined && inst.render._isMockFunction) {
// This is probably bad practice. Consider warning here and
@@ -13293,6 +13639,7 @@ var ReactCompositeComponentMixin = {
!(
// TODO: An `isValidNode` function would probably be more appropriate
renderedComponent === null || renderedComponent === false || ReactElement.isValidElement(renderedComponent)) ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s.render(): A valid React element (or null) must be returned. You may have ' + 'returned undefined, an array or some other invalid object.', this.getName() || 'ReactCompositeComponent') : invariant(false) : void 0;
+
return renderedComponent;
},
@@ -13361,12 +13708,6 @@ var ReactCompositeComponentMixin = {
};
-ReactPerf.measureMethods(ReactCompositeComponentMixin, 'ReactCompositeComponent', {
- mountComponent: 'mountComponent',
- updateComponent: 'updateComponent',
- _renderValidatedComponent: '_renderValidatedComponent'
-});
-
var ReactCompositeComponent = {
Mixin: ReactCompositeComponentMixin
@@ -13376,7 +13717,7 @@ var ReactCompositeComponent = {
module.exports = ReactCompositeComponent;
}).call(this,require('_process'))
-},{"./ReactComponentEnvironment":109,"./ReactCurrentOwner":111,"./ReactElement":137,"./ReactErrorUtils":140,"./ReactInstanceMap":146,"./ReactInstrumentation":147,"./ReactNodeTypes":154,"./ReactPerf":157,"./ReactPropTypeLocationNames":158,"./ReactPropTypeLocations":159,"./ReactReconciler":162,"./ReactUpdateQueue":164,"./shouldUpdateReactComponent":211,"_process":33,"fbjs/lib/emptyObject":222,"fbjs/lib/invariant":229,"fbjs/lib/warning":239,"object-assign":32}],111:[function(require,module,exports){
+},{"./ReactComponentEnvironment":109,"./ReactCurrentOwner":112,"./ReactElement":136,"./ReactErrorUtils":139,"./ReactInstanceMap":145,"./ReactInstrumentation":146,"./ReactNodeTypes":154,"./ReactPropTypeLocationNames":157,"./ReactPropTypeLocations":158,"./ReactReconciler":161,"./ReactUpdateQueue":164,"./shouldUpdateReactComponent":211,"_process":33,"fbjs/lib/emptyObject":222,"fbjs/lib/invariant":229,"fbjs/lib/warning":239,"object-assign":32}],112:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -13408,7 +13749,7 @@ var ReactCurrentOwner = {
};
module.exports = ReactCurrentOwner;
-},{}],112:[function(require,module,exports){
+},{}],113:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -13428,7 +13769,6 @@ module.exports = ReactCurrentOwner;
var ReactDOMComponentTree = require('./ReactDOMComponentTree');
var ReactDefaultInjection = require('./ReactDefaultInjection');
var ReactMount = require('./ReactMount');
-var ReactPerf = require('./ReactPerf');
var ReactReconciler = require('./ReactReconciler');
var ReactUpdates = require('./ReactUpdates');
var ReactVersion = require('./ReactVersion');
@@ -13440,11 +13780,9 @@ var warning = require('fbjs/lib/warning');
ReactDefaultInjection.inject();
-var render = ReactPerf.measure('React', 'render', ReactMount.render);
-
var React = {
findDOMNode: findDOMNode,
- render: render,
+ render: ReactMount.render,
unmountComponentAtNode: ReactMount.unmountComponentAtNode,
version: ReactVersion,
@@ -13516,7 +13854,7 @@ if (process.env.NODE_ENV !== 'production') {
module.exports = React;
}).call(this,require('_process'))
-},{"./ReactDOMComponentTree":116,"./ReactDefaultInjection":134,"./ReactMount":150,"./ReactPerf":157,"./ReactReconciler":162,"./ReactUpdates":165,"./ReactVersion":166,"./findDOMNode":191,"./getNativeComponentFromComposite":199,"./renderSubtreeIntoContainer":208,"_process":33,"fbjs/lib/ExecutionEnvironment":215,"fbjs/lib/warning":239}],113:[function(require,module,exports){
+},{"./ReactDOMComponentTree":117,"./ReactDefaultInjection":135,"./ReactMount":149,"./ReactReconciler":161,"./ReactUpdates":165,"./ReactVersion":166,"./findDOMNode":191,"./getNativeComponentFromComposite":199,"./renderSubtreeIntoContainer":208,"_process":33,"fbjs/lib/ExecutionEnvironment":215,"fbjs/lib/warning":239}],114:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -13541,7 +13879,7 @@ var ReactDOMButton = {
};
module.exports = ReactDOMButton;
-},{"./DisabledInputUtils":90}],114:[function(require,module,exports){
+},{"./DisabledInputUtils":90}],115:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -13578,9 +13916,11 @@ var ReactDOMInput = require('./ReactDOMInput');
var ReactDOMOption = require('./ReactDOMOption');
var ReactDOMSelect = require('./ReactDOMSelect');
var ReactDOMTextarea = require('./ReactDOMTextarea');
+var ReactInstrumentation = require('./ReactInstrumentation');
var ReactMultiChild = require('./ReactMultiChild');
-var ReactPerf = require('./ReactPerf');
+var ReactServerRenderingTransaction = require('./ReactServerRenderingTransaction');
+var emptyFunction = require('fbjs/lib/emptyFunction');
var escapeTextContentForBrowser = require('./escapeTextContentForBrowser');
var invariant = require('fbjs/lib/invariant');
var isEventSupported = require('./isEventSupported');
@@ -13699,6 +14039,9 @@ function assertValidProps(component, props) {
}
function enqueuePutListener(inst, registrationName, listener, transaction) {
+ if (transaction instanceof ReactServerRenderingTransaction) {
+ return;
+ }
if (process.env.NODE_ENV !== 'production') {
// IE8 has no API for event capturing and the `onScroll` event doesn't
// bubble.
@@ -13707,10 +14050,6 @@ function enqueuePutListener(inst, registrationName, listener, transaction) {
var containerInfo = inst._nativeContainerInfo;
var isDocumentFragment = containerInfo._node && containerInfo._node.nodeType === DOC_FRAGMENT_TYPE;
var doc = isDocumentFragment ? containerInfo._node : containerInfo._ownerDocument;
- if (!doc) {
- // Server rendering.
- return;
- }
listenTo(registrationName, doc);
transaction.getReactMountReady().enqueue(putListener, {
inst: inst,
@@ -13729,6 +14068,19 @@ function optionPostMount() {
ReactDOMOption.postMountWrapper(inst);
}
+var setContentChildForInstrumentation = emptyFunction;
+if (process.env.NODE_ENV !== 'production') {
+ setContentChildForInstrumentation = function (contentToUse) {
+ var debugID = this._debugID;
+ var contentDebugID = debugID + '#text';
+ this._contentDebugID = contentDebugID;
+ ReactInstrumentation.debugTool.onSetDisplayName(contentDebugID, '#text');
+ ReactInstrumentation.debugTool.onSetText(contentDebugID, '' + contentToUse);
+ ReactInstrumentation.debugTool.onMountComponent(contentDebugID);
+ ReactInstrumentation.debugTool.onSetChildren(debugID, [contentDebugID]);
+ };
+}
+
// There are so many media events, it makes sense to just
// maintain a list rather than create a `trapBubbledEvent` for each
var mediaEvents = {
@@ -13889,6 +14241,7 @@ function ReactDOMComponent(element) {
this._flags = 0;
if (process.env.NODE_ENV !== 'production') {
this._ancestorInfo = null;
+ this._contentDebugID = null;
}
}
@@ -14004,7 +14357,7 @@ ReactDOMComponent.Mixin = {
div.innerHTML = '<' + type + '></' + type + '>';
el = div.removeChild(div.firstChild);
} else {
- el = ownerDocument.createElement(this._currentElement.type);
+ el = ownerDocument.createElement(this._currentElement.type, props.is || null);
}
} else {
el = ownerDocument.createElementNS(namespaceURI, this._currentElement.type);
@@ -14134,6 +14487,9 @@ ReactDOMComponent.Mixin = {
if (contentToUse != null) {
// TODO: Validate that text is allowed as a child of this node
ret = escapeTextContentForBrowser(contentToUse);
+ if (process.env.NODE_ENV !== 'production') {
+ setContentChildForInstrumentation.call(this, contentToUse);
+ }
} else if (childrenToUse != null) {
var mountImages = this.mountChildren(childrenToUse, transaction, context);
ret = mountImages.join('');
@@ -14168,6 +14524,9 @@ ReactDOMComponent.Mixin = {
var childrenToUse = contentToUse != null ? null : props.children;
if (contentToUse != null) {
// TODO: Validate that text is allowed as a child of this node
+ if (process.env.NODE_ENV !== 'production') {
+ setContentChildForInstrumentation.call(this, contentToUse);
+ }
DOMLazyTree.queueText(lazyTree, contentToUse);
} else if (childrenToUse != null) {
var mountImages = this.mountChildren(childrenToUse, transaction, context);
@@ -14376,17 +14735,34 @@ ReactDOMComponent.Mixin = {
this.updateChildren(null, transaction, context);
} else if (lastHasContentOrHtml && !nextHasContentOrHtml) {
this.updateTextContent('');
+ if (process.env.NODE_ENV !== 'production') {
+ ReactInstrumentation.debugTool.onSetChildren(this._debugID, []);
+ }
}
if (nextContent != null) {
if (lastContent !== nextContent) {
this.updateTextContent('' + nextContent);
+ if (process.env.NODE_ENV !== 'production') {
+ this._contentDebugID = this._debugID + '#text';
+ setContentChildForInstrumentation.call(this, nextContent);
+ }
}
} else if (nextHtml != null) {
if (lastHtml !== nextHtml) {
this.updateMarkup('' + nextHtml);
}
+ if (process.env.NODE_ENV !== 'production') {
+ ReactInstrumentation.debugTool.onSetChildren(this._debugID, []);
+ }
} else if (nextChildren != null) {
+ if (process.env.NODE_ENV !== 'production') {
+ if (this._contentDebugID) {
+ ReactInstrumentation.debugTool.onUnmountComponent(this._contentDebugID);
+ this._contentDebugID = null;
+ }
+ }
+
this.updateChildren(nextChildren, transaction, context);
}
},
@@ -14436,6 +14812,13 @@ ReactDOMComponent.Mixin = {
this._rootNodeID = null;
this._domID = null;
this._wrapperState = null;
+
+ if (process.env.NODE_ENV !== 'production') {
+ if (this._contentDebugID) {
+ ReactInstrumentation.debugTool.onUnmountComponent(this._contentDebugID);
+ this._contentDebugID = null;
+ }
+ }
},
getPublicInstance: function () {
@@ -14444,17 +14827,12 @@ ReactDOMComponent.Mixin = {
};
-ReactPerf.measureMethods(ReactDOMComponent.Mixin, 'ReactDOMComponent', {
- mountComponent: 'mountComponent',
- receiveComponent: 'receiveComponent'
-});
-
_assign(ReactDOMComponent.prototype, ReactDOMComponent.Mixin, ReactMultiChild.Mixin);
module.exports = ReactDOMComponent;
}).call(this,require('_process'))
-},{"./AutoFocusUtils":77,"./CSSPropertyOperations":80,"./DOMLazyTree":84,"./DOMNamespaces":85,"./DOMProperty":86,"./DOMPropertyOperations":87,"./EventConstants":92,"./EventPluginHub":93,"./EventPluginRegistry":94,"./ReactBrowserEventEmitter":103,"./ReactComponentBrowserEnvironment":108,"./ReactDOMButton":113,"./ReactDOMComponentFlags":115,"./ReactDOMComponentTree":116,"./ReactDOMInput":123,"./ReactDOMOption":125,"./ReactDOMSelect":126,"./ReactDOMTextarea":129,"./ReactMultiChild":151,"./ReactPerf":157,"./escapeTextContentForBrowser":190,"./isEventSupported":204,"./validateDOMNesting":213,"_process":33,"fbjs/lib/invariant":229,"fbjs/lib/keyOf":233,"fbjs/lib/shallowEqual":238,"fbjs/lib/warning":239,"object-assign":32}],115:[function(require,module,exports){
+},{"./AutoFocusUtils":77,"./CSSPropertyOperations":80,"./DOMLazyTree":84,"./DOMNamespaces":85,"./DOMProperty":86,"./DOMPropertyOperations":87,"./EventConstants":92,"./EventPluginHub":93,"./EventPluginRegistry":94,"./ReactBrowserEventEmitter":103,"./ReactComponentBrowserEnvironment":108,"./ReactDOMButton":114,"./ReactDOMComponentFlags":116,"./ReactDOMComponentTree":117,"./ReactDOMInput":124,"./ReactDOMOption":126,"./ReactDOMSelect":127,"./ReactDOMTextarea":130,"./ReactInstrumentation":146,"./ReactMultiChild":150,"./ReactServerRenderingTransaction":163,"./escapeTextContentForBrowser":190,"./isEventSupported":204,"./validateDOMNesting":213,"_process":33,"fbjs/lib/emptyFunction":221,"fbjs/lib/invariant":229,"fbjs/lib/keyOf":233,"fbjs/lib/shallowEqual":238,"fbjs/lib/warning":239,"object-assign":32}],116:[function(require,module,exports){
/**
* Copyright 2015-present, Facebook, Inc.
* All rights reserved.
@@ -14473,7 +14851,7 @@ var ReactDOMComponentFlags = {
};
module.exports = ReactDOMComponentFlags;
-},{}],116:[function(require,module,exports){
+},{}],117:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -14663,7 +15041,7 @@ var ReactDOMComponentTree = {
module.exports = ReactDOMComponentTree;
}).call(this,require('_process'))
-},{"./DOMProperty":86,"./ReactDOMComponentFlags":115,"_process":33,"fbjs/lib/invariant":229}],117:[function(require,module,exports){
+},{"./DOMProperty":86,"./ReactDOMComponentFlags":116,"_process":33,"fbjs/lib/invariant":229}],118:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -14700,7 +15078,7 @@ function ReactDOMContainerInfo(topLevelWrapper, node) {
module.exports = ReactDOMContainerInfo;
}).call(this,require('_process'))
-},{"./validateDOMNesting":213,"_process":33}],118:[function(require,module,exports){
+},{"./validateDOMNesting":213,"_process":33}],119:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -14765,7 +15143,7 @@ ReactDOMDebugTool.addDevtool(ReactDOMUnknownPropertyDevtool);
module.exports = ReactDOMDebugTool;
}).call(this,require('_process'))
-},{"./ReactDOMUnknownPropertyDevtool":131,"_process":33,"fbjs/lib/warning":239}],119:[function(require,module,exports){
+},{"./ReactDOMUnknownPropertyDevtool":132,"_process":33,"fbjs/lib/warning":239}],120:[function(require,module,exports){
/**
* Copyright 2014-present, Facebook, Inc.
* All rights reserved.
@@ -14826,7 +15204,7 @@ _assign(ReactDOMEmptyComponent.prototype, {
});
module.exports = ReactDOMEmptyComponent;
-},{"./DOMLazyTree":84,"./ReactDOMComponentTree":116,"object-assign":32}],120:[function(require,module,exports){
+},{"./DOMLazyTree":84,"./ReactDOMComponentTree":117,"object-assign":32}],121:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -15006,7 +15384,7 @@ var ReactDOMFactories = mapObject({
module.exports = ReactDOMFactories;
}).call(this,require('_process'))
-},{"./ReactElement":137,"./ReactElementValidator":138,"_process":33,"fbjs/lib/mapObject":234}],121:[function(require,module,exports){
+},{"./ReactElement":136,"./ReactElementValidator":137,"_process":33,"fbjs/lib/mapObject":234}],122:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -15025,7 +15403,7 @@ var ReactDOMFeatureFlags = {
};
module.exports = ReactDOMFeatureFlags;
-},{}],122:[function(require,module,exports){
+},{}],123:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -15041,7 +15419,6 @@ module.exports = ReactDOMFeatureFlags;
var DOMChildrenOperations = require('./DOMChildrenOperations');
var ReactDOMComponentTree = require('./ReactDOMComponentTree');
-var ReactPerf = require('./ReactPerf');
/**
* Operations used to process updates to DOM nodes.
@@ -15060,12 +15437,8 @@ var ReactDOMIDOperations = {
}
};
-ReactPerf.measureMethods(ReactDOMIDOperations, 'ReactDOMIDOperations', {
- dangerouslyProcessChildrenUpdates: 'dangerouslyProcessChildrenUpdates'
-});
-
module.exports = ReactDOMIDOperations;
-},{"./DOMChildrenOperations":83,"./ReactDOMComponentTree":116,"./ReactPerf":157}],123:[function(require,module,exports){
+},{"./DOMChildrenOperations":83,"./ReactDOMComponentTree":117}],124:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -15154,6 +15527,8 @@ var ReactDOMInput = {
if (process.env.NODE_ENV !== 'production') {
LinkedValueUtils.checkPropTypes('input', props, inst._currentElement._owner);
+ var owner = inst._currentElement._owner;
+
if (props.valueLink !== undefined && !didWarnValueLink) {
process.env.NODE_ENV !== 'production' ? warning(false, '`valueLink` prop on `input` is deprecated; set `value` and `onChange` instead.') : void 0;
didWarnValueLink = true;
@@ -15163,11 +15538,11 @@ var ReactDOMInput = {
didWarnCheckedLink = true;
}
if (props.checked !== undefined && props.defaultChecked !== undefined && !didWarnCheckedDefaultChecked) {
- process.env.NODE_ENV !== 'production' ? warning(false, 'Input elements must be either controlled or uncontrolled ' + '(specify either the checked prop, or the defaultChecked prop, but not ' + 'both). Decide between using a controlled or uncontrolled input ' + 'element and remove one of these props. More info: ' + 'https://fb.me/react-controlled-components') : void 0;
+ process.env.NODE_ENV !== 'production' ? warning(false, '%s contains an input of type %s with both checked and defaultChecked props. ' + 'Input elements must be either controlled or uncontrolled ' + '(specify either the checked prop, or the defaultChecked prop, but not ' + 'both). Decide between using a controlled or uncontrolled input ' + 'element and remove one of these props. More info: ' + 'https://fb.me/react-controlled-components', owner && owner.getName() || 'A component', props.type) : void 0;
didWarnCheckedDefaultChecked = true;
}
if (props.value !== undefined && props.defaultValue !== undefined && !didWarnValueDefaultValue) {
- process.env.NODE_ENV !== 'production' ? warning(false, 'Input elements must be either controlled or uncontrolled ' + '(specify either the value prop, or the defaultValue prop, but not ' + 'both). Decide between using a controlled or uncontrolled input ' + 'element and remove one of these props. More info: ' + 'https://fb.me/react-controlled-components') : void 0;
+ process.env.NODE_ENV !== 'production' ? warning(false, '%s contains an input of type %s with both value and defaultValue props. ' + 'Input elements must be either controlled or uncontrolled ' + '(specify either the value prop, or the defaultValue prop, but not ' + 'both). Decide between using a controlled or uncontrolled input ' + 'element and remove one of these props. More info: ' + 'https://fb.me/react-controlled-components', owner && owner.getName() || 'A component', props.type) : void 0;
didWarnValueDefaultValue = true;
}
warnIfValueIsNull(props);
@@ -15198,7 +15573,7 @@ var ReactDOMInput = {
var owner = inst._currentElement._owner;
if ((initialValue || !inst._wrapperState.controlled) && controlled && !didWarnUncontrolledToControlled) {
- process.env.NODE_ENV !== 'production' ? warning(false, '%s is changing a uncontrolled input of type %s to be controlled. ' + 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' + 'Decide between using a controlled or uncontrolled input ' + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components', owner && owner.getName() || 'A component', props.type) : void 0;
+ process.env.NODE_ENV !== 'production' ? warning(false, '%s is changing an uncontrolled input of type %s to be controlled. ' + 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' + 'Decide between using a controlled or uncontrolled input ' + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components', owner && owner.getName() || 'A component', props.type) : void 0;
didWarnUncontrolledToControlled = true;
}
if (inst._wrapperState.controlled && (defaultValue || !controlled) && !didWarnControlledToUncontrolled) {
@@ -15273,7 +15648,7 @@ function _handleChange(event) {
module.exports = ReactDOMInput;
}).call(this,require('_process'))
-},{"./DOMPropertyOperations":87,"./DisabledInputUtils":90,"./LinkedValueUtils":100,"./ReactDOMComponentTree":116,"./ReactUpdates":165,"_process":33,"fbjs/lib/invariant":229,"fbjs/lib/warning":239,"object-assign":32}],124:[function(require,module,exports){
+},{"./DOMPropertyOperations":87,"./DisabledInputUtils":90,"./LinkedValueUtils":100,"./ReactDOMComponentTree":117,"./ReactUpdates":165,"_process":33,"fbjs/lib/invariant":229,"fbjs/lib/warning":239,"object-assign":32}],125:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -15290,7 +15665,7 @@ module.exports = ReactDOMInput;
var ReactDOMDebugTool = require('./ReactDOMDebugTool');
module.exports = { debugTool: ReactDOMDebugTool };
-},{"./ReactDOMDebugTool":118}],125:[function(require,module,exports){
+},{"./ReactDOMDebugTool":119}],126:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -15403,7 +15778,7 @@ var ReactDOMOption = {
module.exports = ReactDOMOption;
}).call(this,require('_process'))
-},{"./ReactChildren":105,"./ReactDOMComponentTree":116,"./ReactDOMSelect":126,"_process":33,"fbjs/lib/warning":239,"object-assign":32}],126:[function(require,module,exports){
+},{"./ReactChildren":105,"./ReactDOMComponentTree":117,"./ReactDOMSelect":127,"_process":33,"fbjs/lib/warning":239,"object-assign":32}],127:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -15620,7 +15995,7 @@ function _handleChange(event) {
module.exports = ReactDOMSelect;
}).call(this,require('_process'))
-},{"./DisabledInputUtils":90,"./LinkedValueUtils":100,"./ReactDOMComponentTree":116,"./ReactUpdates":165,"_process":33,"fbjs/lib/warning":239,"object-assign":32}],127:[function(require,module,exports){
+},{"./DisabledInputUtils":90,"./LinkedValueUtils":100,"./ReactDOMComponentTree":117,"./ReactUpdates":165,"_process":33,"fbjs/lib/warning":239,"object-assign":32}],128:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -15833,7 +16208,7 @@ var ReactDOMSelection = {
};
module.exports = ReactDOMSelection;
-},{"./getNodeForCharacterOffset":200,"./getTextContentAccessor":201,"fbjs/lib/ExecutionEnvironment":215}],128:[function(require,module,exports){
+},{"./getNodeForCharacterOffset":200,"./getTextContentAccessor":201,"fbjs/lib/ExecutionEnvironment":215}],129:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -15853,7 +16228,7 @@ var _assign = require('object-assign');
var DOMChildrenOperations = require('./DOMChildrenOperations');
var DOMLazyTree = require('./DOMLazyTree');
var ReactDOMComponentTree = require('./ReactDOMComponentTree');
-var ReactPerf = require('./ReactPerf');
+var ReactInstrumentation = require('./ReactInstrumentation');
var escapeTextContentForBrowser = require('./escapeTextContentForBrowser');
var invariant = require('fbjs/lib/invariant');
@@ -15901,6 +16276,8 @@ _assign(ReactDOMTextComponent.prototype, {
*/
mountComponent: function (transaction, nativeParent, nativeContainerInfo, context) {
if (process.env.NODE_ENV !== 'production') {
+ ReactInstrumentation.debugTool.onSetText(this._debugID, this._stringText);
+
var parentInfo;
if (nativeParent != null) {
parentInfo = nativeParent._ancestorInfo;
@@ -15964,6 +16341,10 @@ _assign(ReactDOMTextComponent.prototype, {
this._stringText = nextStringText;
var commentNodes = this.getNativeNode();
DOMChildrenOperations.replaceDelimitedText(commentNodes[0], commentNodes[1], nextStringText);
+
+ if (process.env.NODE_ENV !== 'production') {
+ ReactInstrumentation.debugTool.onSetText(this._debugID, nextStringText);
+ }
}
}
},
@@ -15998,15 +16379,10 @@ _assign(ReactDOMTextComponent.prototype, {
});
-ReactPerf.measureMethods(ReactDOMTextComponent.prototype, 'ReactDOMTextComponent', {
- mountComponent: 'mountComponent',
- receiveComponent: 'receiveComponent'
-});
-
module.exports = ReactDOMTextComponent;
}).call(this,require('_process'))
-},{"./DOMChildrenOperations":83,"./DOMLazyTree":84,"./ReactDOMComponentTree":116,"./ReactPerf":157,"./escapeTextContentForBrowser":190,"./validateDOMNesting":213,"_process":33,"fbjs/lib/invariant":229,"object-assign":32}],129:[function(require,module,exports){
+},{"./DOMChildrenOperations":83,"./DOMLazyTree":84,"./ReactDOMComponentTree":117,"./ReactInstrumentation":146,"./escapeTextContentForBrowser":190,"./validateDOMNesting":213,"_process":33,"fbjs/lib/invariant":229,"object-assign":32}],130:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -16152,7 +16528,7 @@ function _handleChange(event) {
module.exports = ReactDOMTextarea;
}).call(this,require('_process'))
-},{"./DOMPropertyOperations":87,"./DisabledInputUtils":90,"./LinkedValueUtils":100,"./ReactDOMComponentTree":116,"./ReactUpdates":165,"_process":33,"fbjs/lib/invariant":229,"fbjs/lib/warning":239,"object-assign":32}],130:[function(require,module,exports){
+},{"./DOMPropertyOperations":87,"./DisabledInputUtils":90,"./LinkedValueUtils":100,"./ReactDOMComponentTree":117,"./ReactUpdates":165,"_process":33,"fbjs/lib/invariant":229,"fbjs/lib/warning":239,"object-assign":32}],131:[function(require,module,exports){
(function (process){
/**
* Copyright 2015-present, Facebook, Inc.
@@ -16290,7 +16666,7 @@ module.exports = {
};
}).call(this,require('_process'))
-},{"_process":33,"fbjs/lib/invariant":229}],131:[function(require,module,exports){
+},{"_process":33,"fbjs/lib/invariant":229}],132:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -16358,7 +16734,7 @@ var ReactDOMUnknownPropertyDevtool = {
module.exports = ReactDOMUnknownPropertyDevtool;
}).call(this,require('_process'))
-},{"./DOMProperty":86,"./EventPluginRegistry":94,"_process":33,"fbjs/lib/warning":239}],132:[function(require,module,exports){
+},{"./DOMProperty":86,"./EventPluginRegistry":94,"_process":33,"fbjs/lib/warning":239}],133:[function(require,module,exports){
(function (process){
/**
* Copyright 2016-present, Facebook, Inc.
@@ -16373,7 +16749,9 @@ module.exports = ReactDOMUnknownPropertyDevtool;
'use strict';
-var ReactInvalidSetStateWarningDevTool = require('./ReactInvalidSetStateWarningDevTool');
+var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
+
+var performanceNow = require('fbjs/lib/performanceNow');
var warning = require('fbjs/lib/warning');
var eventHandlers = [];
@@ -16394,6 +16772,70 @@ function emitEvent(handlerFunctionName, arg1, arg2, arg3, arg4, arg5) {
}
}
+var isProfiling = false;
+var flushHistory = [];
+var currentFlushNesting = 0;
+var currentFlushMeasurements = null;
+var currentFlushStartTime = null;
+var currentTimerDebugID = null;
+var currentTimerStartTime = null;
+var currentTimerType = null;
+
+function clearHistory() {
+ ReactComponentTreeDevtool.purgeUnmountedComponents();
+ ReactNativeOperationHistoryDevtool.clearHistory();
+}
+
+function getTreeSnapshot(registeredIDs) {
+ return registeredIDs.reduce(function (tree, id) {
+ var ownerID = ReactComponentTreeDevtool.getOwnerID(id);
+ var parentID = ReactComponentTreeDevtool.getParentID(id);
+ tree[id] = {
+ displayName: ReactComponentTreeDevtool.getDisplayName(id),
+ text: ReactComponentTreeDevtool.getText(id),
+ updateCount: ReactComponentTreeDevtool.getUpdateCount(id),
+ childIDs: ReactComponentTreeDevtool.getChildIDs(id),
+ // Text nodes don't have owners but this is close enough.
+ ownerID: ownerID || ReactComponentTreeDevtool.getOwnerID(parentID),
+ parentID: parentID
+ };
+ return tree;
+ }, {});
+}
+
+function resetMeasurements() {
+ if (process.env.NODE_ENV !== 'production') {
+ var previousStartTime = currentFlushStartTime;
+ var previousMeasurements = currentFlushMeasurements || [];
+ var previousOperations = ReactNativeOperationHistoryDevtool.getHistory();
+
+ if (!isProfiling || currentFlushNesting === 0) {
+ currentFlushStartTime = null;
+ currentFlushMeasurements = null;
+ clearHistory();
+ return;
+ }
+
+ if (previousMeasurements.length || previousOperations.length) {
+ var registeredIDs = ReactComponentTreeDevtool.getRegisteredIDs();
+ flushHistory.push({
+ duration: performanceNow() - previousStartTime,
+ measurements: previousMeasurements || [],
+ operations: previousOperations || [],
+ treeSnapshot: getTreeSnapshot(registeredIDs)
+ });
+ }
+
+ clearHistory();
+ currentFlushStartTime = performanceNow();
+ currentFlushMeasurements = [];
+ }
+}
+
+function checkDebugID(debugID) {
+ process.env.NODE_ENV !== 'production' ? warning(debugID, 'ReactDebugTool: debugID may not be empty.') : void 0;
+}
+
var ReactDebugTool = {
addDevtool: function (devtool) {
eventHandlers.push(devtool);
@@ -16406,35 +16848,147 @@ var ReactDebugTool = {
}
}
},
+ beginProfiling: function () {
+ if (process.env.NODE_ENV !== 'production') {
+ if (isProfiling) {
+ return;
+ }
+
+ isProfiling = true;
+ flushHistory.length = 0;
+ resetMeasurements();
+ }
+ },
+ endProfiling: function () {
+ if (process.env.NODE_ENV !== 'production') {
+ if (!isProfiling) {
+ return;
+ }
+
+ isProfiling = false;
+ resetMeasurements();
+ }
+ },
+ getFlushHistory: function () {
+ if (process.env.NODE_ENV !== 'production') {
+ return flushHistory;
+ }
+ },
+ onBeginFlush: function () {
+ if (process.env.NODE_ENV !== 'production') {
+ currentFlushNesting++;
+ resetMeasurements();
+ }
+ emitEvent('onBeginFlush');
+ },
+ onEndFlush: function () {
+ if (process.env.NODE_ENV !== 'production') {
+ resetMeasurements();
+ currentFlushNesting--;
+ }
+ emitEvent('onEndFlush');
+ },
+ onBeginLifeCycleTimer: function (debugID, timerType) {
+ checkDebugID(debugID);
+ emitEvent('onBeginLifeCycleTimer', debugID, timerType);
+ if (process.env.NODE_ENV !== 'production') {
+ if (isProfiling && currentFlushNesting > 0) {
+ process.env.NODE_ENV !== 'production' ? warning(!currentTimerType, 'There is an internal error in the React performance measurement code. ' + 'Did not expect %s timer to start while %s timer is still in ' + 'progress for %s instance.', timerType, currentTimerType || 'no', debugID === currentTimerDebugID ? 'the same' : 'another') : void 0;
+ currentTimerStartTime = performanceNow();
+ currentTimerDebugID = debugID;
+ currentTimerType = timerType;
+ }
+ }
+ },
+ onEndLifeCycleTimer: function (debugID, timerType) {
+ checkDebugID(debugID);
+ if (process.env.NODE_ENV !== 'production') {
+ if (isProfiling && currentFlushNesting > 0) {
+ process.env.NODE_ENV !== 'production' ? warning(currentTimerType === timerType, 'There is an internal error in the React performance measurement code. ' + 'We did not expect %s timer to stop while %s timer is still in ' + 'progress for %s instance. Please report this as a bug in React.', timerType, currentTimerType || 'no', debugID === currentTimerDebugID ? 'the same' : 'another') : void 0;
+ currentFlushMeasurements.push({
+ timerType: timerType,
+ instanceID: debugID,
+ duration: performanceNow() - currentTimerStartTime
+ });
+ currentTimerStartTime = null;
+ currentTimerDebugID = null;
+ currentTimerType = null;
+ }
+ }
+ emitEvent('onEndLifeCycleTimer', debugID, timerType);
+ },
+ onBeginReconcilerTimer: function (debugID, timerType) {
+ checkDebugID(debugID);
+ emitEvent('onBeginReconcilerTimer', debugID, timerType);
+ },
+ onEndReconcilerTimer: function (debugID, timerType) {
+ checkDebugID(debugID);
+ emitEvent('onEndReconcilerTimer', debugID, timerType);
+ },
onBeginProcessingChildContext: function () {
emitEvent('onBeginProcessingChildContext');
},
onEndProcessingChildContext: function () {
emitEvent('onEndProcessingChildContext');
},
+ onNativeOperation: function (debugID, type, payload) {
+ checkDebugID(debugID);
+ emitEvent('onNativeOperation', debugID, type, payload);
+ },
onSetState: function () {
emitEvent('onSetState');
},
- onMountRootComponent: function (internalInstance) {
- emitEvent('onMountRootComponent', internalInstance);
+ onSetDisplayName: function (debugID, displayName) {
+ checkDebugID(debugID);
+ emitEvent('onSetDisplayName', debugID, displayName);
+ },
+ onSetChildren: function (debugID, childDebugIDs) {
+ checkDebugID(debugID);
+ emitEvent('onSetChildren', debugID, childDebugIDs);
+ },
+ onSetOwner: function (debugID, ownerDebugID) {
+ checkDebugID(debugID);
+ emitEvent('onSetOwner', debugID, ownerDebugID);
+ },
+ onSetText: function (debugID, text) {
+ checkDebugID(debugID);
+ emitEvent('onSetText', debugID, text);
+ },
+ onMountRootComponent: function (debugID) {
+ checkDebugID(debugID);
+ emitEvent('onMountRootComponent', debugID);
},
- onMountComponent: function (internalInstance) {
- emitEvent('onMountComponent', internalInstance);
+ onMountComponent: function (debugID) {
+ checkDebugID(debugID);
+ emitEvent('onMountComponent', debugID);
},
- onUpdateComponent: function (internalInstance) {
- emitEvent('onUpdateComponent', internalInstance);
+ onUpdateComponent: function (debugID) {
+ checkDebugID(debugID);
+ emitEvent('onUpdateComponent', debugID);
},
- onUnmountComponent: function (internalInstance) {
- emitEvent('onUnmountComponent', internalInstance);
+ onUnmountComponent: function (debugID) {
+ checkDebugID(debugID);
+ emitEvent('onUnmountComponent', debugID);
}
};
-ReactDebugTool.addDevtool(ReactInvalidSetStateWarningDevTool);
+if (process.env.NODE_ENV !== 'production') {
+ var ReactInvalidSetStateWarningDevTool = require('./ReactInvalidSetStateWarningDevTool');
+ var ReactNativeOperationHistoryDevtool = require('./ReactNativeOperationHistoryDevtool');
+ var ReactComponentTreeDevtool = require('./ReactComponentTreeDevtool');
+ ReactDebugTool.addDevtool(ReactInvalidSetStateWarningDevTool);
+ ReactDebugTool.addDevtool(ReactComponentTreeDevtool);
+ ReactDebugTool.addDevtool(ReactNativeOperationHistoryDevtool);
+ var url = ExecutionEnvironment.canUseDOM && window.location.href || '';
+ if (/[?&]react_perf\b/.test(url)) {
+ ReactDebugTool.beginProfiling();
+ }
+}
module.exports = ReactDebugTool;
}).call(this,require('_process'))
-},{"./ReactInvalidSetStateWarningDevTool":148,"_process":33,"fbjs/lib/warning":239}],133:[function(require,module,exports){
+},{"./ReactComponentTreeDevtool":110,"./ReactInvalidSetStateWarningDevTool":147,"./ReactNativeOperationHistoryDevtool":153,"_process":33,"fbjs/lib/ExecutionEnvironment":215,"fbjs/lib/performanceNow":237,"fbjs/lib/warning":239}],134:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -16503,8 +17057,7 @@ var ReactDefaultBatchingStrategy = {
};
module.exports = ReactDefaultBatchingStrategy;
-},{"./ReactUpdates":165,"./Transaction":183,"fbjs/lib/emptyFunction":221,"object-assign":32}],134:[function(require,module,exports){
-(function (process){
+},{"./ReactUpdates":165,"./Transaction":183,"fbjs/lib/emptyFunction":221,"object-assign":32}],135:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -16522,7 +17075,6 @@ var BeforeInputEventPlugin = require('./BeforeInputEventPlugin');
var ChangeEventPlugin = require('./ChangeEventPlugin');
var DefaultEventPluginOrder = require('./DefaultEventPluginOrder');
var EnterLeaveEventPlugin = require('./EnterLeaveEventPlugin');
-var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
var HTMLDOMPropertyConfig = require('./HTMLDOMPropertyConfig');
var ReactComponentBrowserEnvironment = require('./ReactComponentBrowserEnvironment');
var ReactDOMComponent = require('./ReactDOMComponent');
@@ -16585,553 +17137,12 @@ function inject() {
ReactInjection.Updates.injectBatchingStrategy(ReactDefaultBatchingStrategy);
ReactInjection.Component.injectEnvironment(ReactComponentBrowserEnvironment);
-
- if (process.env.NODE_ENV !== 'production') {
- var url = ExecutionEnvironment.canUseDOM && window.location.href || '';
- if (/[?&]react_perf\b/.test(url)) {
- var ReactDefaultPerf = require('./ReactDefaultPerf');
- ReactDefaultPerf.start();
- }
- }
}
module.exports = {
inject: inject
};
-}).call(this,require('_process'))
-
-},{"./BeforeInputEventPlugin":78,"./ChangeEventPlugin":82,"./DefaultEventPluginOrder":89,"./EnterLeaveEventPlugin":91,"./HTMLDOMPropertyConfig":98,"./ReactComponentBrowserEnvironment":108,"./ReactDOMComponent":114,"./ReactDOMComponentTree":116,"./ReactDOMEmptyComponent":119,"./ReactDOMTextComponent":128,"./ReactDOMTreeTraversal":130,"./ReactDefaultBatchingStrategy":133,"./ReactDefaultPerf":135,"./ReactEventListener":142,"./ReactInjection":144,"./ReactReconcileTransaction":161,"./SVGDOMPropertyConfig":167,"./SelectEventPlugin":168,"./SimpleEventPlugin":169,"_process":33,"fbjs/lib/ExecutionEnvironment":215}],135:[function(require,module,exports){
-(function (process){
-/**
- * Copyright 2013-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- * @providesModule ReactDefaultPerf
- */
-
-'use strict';
-
-var DOMProperty = require('./DOMProperty');
-var ReactDOMComponentTree = require('./ReactDOMComponentTree');
-var ReactDefaultPerfAnalysis = require('./ReactDefaultPerfAnalysis');
-var ReactMount = require('./ReactMount');
-var ReactPerf = require('./ReactPerf');
-
-var performanceNow = require('fbjs/lib/performanceNow');
-var warning = require('fbjs/lib/warning');
-
-function roundFloat(val) {
- return Math.floor(val * 100) / 100;
-}
-
-function addValue(obj, key, val) {
- obj[key] = (obj[key] || 0) + val;
-}
-
-// Composite/text components don't have any built-in ID: we have to make our own
-var compositeIDMap;
-var compositeIDCounter = 17000;
-function getIDOfComposite(inst) {
- if (!compositeIDMap) {
- compositeIDMap = new WeakMap();
- }
- if (compositeIDMap.has(inst)) {
- return compositeIDMap.get(inst);
- } else {
- var id = compositeIDCounter++;
- compositeIDMap.set(inst, id);
- return id;
- }
-}
-
-function getID(inst) {
- if (inst.hasOwnProperty('_rootNodeID')) {
- return inst._rootNodeID;
- } else {
- return getIDOfComposite(inst);
- }
-}
-
-function stripComplexValues(key, value) {
- if (typeof value !== 'object' || Array.isArray(value) || value == null) {
- return value;
- }
- var prototype = Object.getPrototypeOf(value);
- if (!prototype || prototype === Object.prototype) {
- return value;
- }
- return '<not serializable>';
-}
-
-// This implementation of ReactPerf is going away some time mid 15.x.
-// While we plan to keep most of the API, the actual format of measurements
-// will change dramatically. To signal this, we wrap them into an opaque-ish
-// object to discourage reaching into it until the API stabilizes.
-function wrapLegacyMeasurements(measurements) {
- return { __unstable_this_format_will_change: measurements };
-}
-function unwrapLegacyMeasurements(measurements) {
- return measurements && measurements.__unstable_this_format_will_change || measurements;
-}
-
-var warnedAboutPrintDOM = false;
-var warnedAboutGetMeasurementsSummaryMap = false;
-
-var ReactDefaultPerf = {
- _allMeasurements: [], // last item in the list is the current one
- _mountStack: [0],
- _compositeStack: [],
- _injected: false,
-
- start: function () {
- if (!ReactDefaultPerf._injected) {
- ReactPerf.injection.injectMeasure(ReactDefaultPerf.measure);
- }
-
- ReactDefaultPerf._allMeasurements.length = 0;
- ReactPerf.enableMeasure = true;
- },
-
- stop: function () {
- ReactPerf.enableMeasure = false;
- },
-
- getLastMeasurements: function () {
- return wrapLegacyMeasurements(ReactDefaultPerf._allMeasurements);
- },
-
- printExclusive: function (measurements) {
- measurements = unwrapLegacyMeasurements(measurements || ReactDefaultPerf._allMeasurements);
- var summary = ReactDefaultPerfAnalysis.getExclusiveSummary(measurements);
- console.table(summary.map(function (item) {
- return {
- 'Component class name': item.componentName,
- 'Total inclusive time (ms)': roundFloat(item.inclusive),
- 'Exclusive mount time (ms)': roundFloat(item.exclusive),
- 'Exclusive render time (ms)': roundFloat(item.render),
- 'Mount time per instance (ms)': roundFloat(item.exclusive / item.count),
- 'Render time per instance (ms)': roundFloat(item.render / item.count),
- 'Instances': item.count
- };
- }));
- // TODO: ReactDefaultPerfAnalysis.getTotalTime() does not return the correct
- // number.
- },
-
- printInclusive: function (measurements) {
- measurements = unwrapLegacyMeasurements(measurements || ReactDefaultPerf._allMeasurements);
- var summary = ReactDefaultPerfAnalysis.getInclusiveSummary(measurements);
- console.table(summary.map(function (item) {
- return {
- 'Owner > component': item.componentName,
- 'Inclusive time (ms)': roundFloat(item.time),
- 'Instances': item.count
- };
- }));
- console.log('Total time:', ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms');
- },
-
- getMeasurementsSummaryMap: function (measurements) {
- process.env.NODE_ENV !== 'production' ? warning(warnedAboutGetMeasurementsSummaryMap, '`ReactPerf.getMeasurementsSummaryMap(...)` is deprecated. Use ' + '`ReactPerf.getWasted(...)` instead.') : void 0;
- warnedAboutGetMeasurementsSummaryMap = true;
- return ReactDefaultPerf.getWasted(measurements);
- },
-
- getWasted: function (measurements) {
- measurements = unwrapLegacyMeasurements(measurements);
- var summary = ReactDefaultPerfAnalysis.getInclusiveSummary(measurements, true);
- return summary.map(function (item) {
- return {
- 'Owner > component': item.componentName,
- 'Wasted time (ms)': item.time,
- 'Instances': item.count
- };
- });
- },
-
- printWasted: function (measurements) {
- measurements = unwrapLegacyMeasurements(measurements || ReactDefaultPerf._allMeasurements);
- console.table(ReactDefaultPerf.getWasted(measurements));
- console.log('Total time:', ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms');
- },
-
- printDOM: function (measurements) {
- process.env.NODE_ENV !== 'production' ? warning(warnedAboutPrintDOM, '`ReactPerf.printDOM(...)` is deprecated. Use ' + '`ReactPerf.printOperations(...)` instead.') : void 0;
- warnedAboutPrintDOM = true;
- return ReactDefaultPerf.printOperations(measurements);
- },
-
- printOperations: function (measurements) {
- measurements = unwrapLegacyMeasurements(measurements || ReactDefaultPerf._allMeasurements);
- var summary = ReactDefaultPerfAnalysis.getDOMSummary(measurements);
- console.table(summary.map(function (item) {
- var result = {};
- result[DOMProperty.ID_ATTRIBUTE_NAME] = item.id;
- result.type = item.type;
- result.args = JSON.stringify(item.args, stripComplexValues);
- return result;
- }));
- console.log('Total time:', ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms');
- },
-
- _recordWrite: function (id, fnName, totalTime, args) {
- // TODO: totalTime isn't that useful since it doesn't count paints/reflows
- var entry = ReactDefaultPerf._allMeasurements[ReactDefaultPerf._allMeasurements.length - 1];
- var writes = entry.writes;
- writes[id] = writes[id] || [];
- writes[id].push({
- type: fnName,
- time: totalTime,
- args: args
- });
- },
-
- measure: function (moduleName, fnName, func) {
- return function () {
- for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
- args[_key] = arguments[_key];
- }
-
- var totalTime;
- var rv;
- var start;
-
- var entry = ReactDefaultPerf._allMeasurements[ReactDefaultPerf._allMeasurements.length - 1];
-
- if (fnName === '_renderNewRootComponent' || fnName === 'flushBatchedUpdates') {
- // A "measurement" is a set of metrics recorded for each flush. We want
- // to group the metrics for a given flush together so we can look at the
- // components that rendered and the DOM operations that actually
- // happened to determine the amount of "wasted work" performed.
- ReactDefaultPerf._allMeasurements.push(entry = {
- exclusive: {},
- inclusive: {},
- render: {},
- counts: {},
- writes: {},
- displayNames: {},
- hierarchy: {},
- totalTime: 0,
- created: {}
- });
- start = performanceNow();
- rv = func.apply(this, args);
- entry.totalTime = performanceNow() - start;
- return rv;
- } else if (fnName === '_mountImageIntoNode' || moduleName === 'ReactDOMIDOperations' || moduleName === 'CSSPropertyOperations' || moduleName === 'DOMChildrenOperations' || moduleName === 'DOMPropertyOperations' || moduleName === 'ReactComponentBrowserEnvironment') {
- start = performanceNow();
- rv = func.apply(this, args);
- totalTime = performanceNow() - start;
-
- if (fnName === '_mountImageIntoNode') {
- ReactDefaultPerf._recordWrite('', fnName, totalTime, args[0]);
- } else if (fnName === 'dangerouslyProcessChildrenUpdates') {
- // special format
- args[1].forEach(function (update) {
- var writeArgs = {};
- if (update.fromIndex !== null) {
- writeArgs.fromIndex = update.fromIndex;
- }
- if (update.toIndex !== null) {
- writeArgs.toIndex = update.toIndex;
- }
- if (update.content !== null) {
- writeArgs.content = update.content;
- }
- ReactDefaultPerf._recordWrite(args[0]._rootNodeID, update.type, totalTime, writeArgs);
- });
- } else {
- // basic format
- var id = args[0];
- if (moduleName === 'EventPluginHub') {
- id = id._rootNodeID;
- } else if (fnName === 'replaceNodeWithMarkup') {
- // Old node is already unmounted; can't get its instance
- id = ReactDOMComponentTree.getInstanceFromNode(args[1].node)._rootNodeID;
- } else if (fnName === 'replaceDelimitedText') {
- id = getID(ReactDOMComponentTree.getInstanceFromNode(args[0]));
- } else if (typeof id === 'object') {
- id = getID(ReactDOMComponentTree.getInstanceFromNode(args[0]));
- }
- ReactDefaultPerf._recordWrite(id, fnName, totalTime, Array.prototype.slice.call(args, 1));
- }
- return rv;
- } else if (moduleName === 'ReactCompositeComponent' && (fnName === 'mountComponent' || fnName === 'updateComponent' || // TODO: receiveComponent()?
- fnName === '_renderValidatedComponent')) {
-
- if (this._currentElement.type === ReactMount.TopLevelWrapper) {
- return func.apply(this, args);
- }
-
- var rootNodeID = getIDOfComposite(this);
- var isRender = fnName === '_renderValidatedComponent';
- var isMount = fnName === 'mountComponent';
-
- var mountStack = ReactDefaultPerf._mountStack;
-
- if (isRender) {
- addValue(entry.counts, rootNodeID, 1);
- } else if (isMount) {
- entry.created[rootNodeID] = true;
- mountStack.push(0);
- }
-
- ReactDefaultPerf._compositeStack.push(rootNodeID);
-
- start = performanceNow();
- rv = func.apply(this, args);
- totalTime = performanceNow() - start;
-
- ReactDefaultPerf._compositeStack.pop();
-
- if (isRender) {
- addValue(entry.render, rootNodeID, totalTime);
- } else if (isMount) {
- var subMountTime = mountStack.pop();
- mountStack[mountStack.length - 1] += totalTime;
- addValue(entry.exclusive, rootNodeID, totalTime - subMountTime);
- addValue(entry.inclusive, rootNodeID, totalTime);
- } else {
- addValue(entry.inclusive, rootNodeID, totalTime);
- }
-
- entry.displayNames[rootNodeID] = {
- current: this.getName(),
- owner: this._currentElement._owner ? this._currentElement._owner.getName() : '<root>'
- };
-
- return rv;
- } else if ((moduleName === 'ReactDOMComponent' || moduleName === 'ReactDOMTextComponent') && (fnName === 'mountComponent' || fnName === 'receiveComponent')) {
-
- rv = func.apply(this, args);
- entry.hierarchy[getID(this)] = ReactDefaultPerf._compositeStack.slice();
- return rv;
- } else {
- return func.apply(this, args);
- }
- };
- }
-};
-
-module.exports = ReactDefaultPerf;
-}).call(this,require('_process'))
-
-},{"./DOMProperty":86,"./ReactDOMComponentTree":116,"./ReactDefaultPerfAnalysis":136,"./ReactMount":150,"./ReactPerf":157,"_process":33,"fbjs/lib/performanceNow":237,"fbjs/lib/warning":239}],136:[function(require,module,exports){
-/**
- * Copyright 2013-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- * @providesModule ReactDefaultPerfAnalysis
- */
-
-'use strict';
-
-// Don't try to save users less than 1.2ms (a number I made up)
-
-var _assign = require('object-assign');
-
-var DONT_CARE_THRESHOLD = 1.2;
-var DOM_OPERATION_TYPES = {
- '_mountImageIntoNode': 'set innerHTML',
- INSERT_MARKUP: 'set innerHTML',
- MOVE_EXISTING: 'move',
- REMOVE_NODE: 'remove',
- SET_MARKUP: 'set innerHTML',
- TEXT_CONTENT: 'set textContent',
- 'setValueForProperty': 'update attribute',
- 'setValueForAttribute': 'update attribute',
- 'deleteValueForProperty': 'remove attribute',
- 'setValueForStyles': 'update styles',
- 'replaceNodeWithMarkup': 'replace',
- 'replaceDelimitedText': 'replace'
-};
-
-function getTotalTime(measurements) {
- // TODO: return number of DOM ops? could be misleading.
- // TODO: measure dropped frames after reconcile?
- // TODO: log total time of each reconcile and the top-level component
- // class that triggered it.
- var totalTime = 0;
- for (var i = 0; i < measurements.length; i++) {
- var measurement = measurements[i];
- totalTime += measurement.totalTime;
- }
- return totalTime;
-}
-
-function getDOMSummary(measurements) {
- var items = [];
- measurements.forEach(function (measurement) {
- Object.keys(measurement.writes).forEach(function (id) {
- measurement.writes[id].forEach(function (write) {
- items.push({
- id: id,
- type: DOM_OPERATION_TYPES[write.type] || write.type,
- args: write.args
- });
- });
- });
- });
- return items;
-}
-
-function getExclusiveSummary(measurements) {
- var candidates = {};
- var displayName;
-
- for (var i = 0; i < measurements.length; i++) {
- var measurement = measurements[i];
- var allIDs = _assign({}, measurement.exclusive, measurement.inclusive);
-
- for (var id in allIDs) {
- displayName = measurement.displayNames[id].current;
-
- candidates[displayName] = candidates[displayName] || {
- componentName: displayName,
- inclusive: 0,
- exclusive: 0,
- render: 0,
- count: 0
- };
- if (measurement.render[id]) {
- candidates[displayName].render += measurement.render[id];
- }
- if (measurement.exclusive[id]) {
- candidates[displayName].exclusive += measurement.exclusive[id];
- }
- if (measurement.inclusive[id]) {
- candidates[displayName].inclusive += measurement.inclusive[id];
- }
- if (measurement.counts[id]) {
- candidates[displayName].count += measurement.counts[id];
- }
- }
- }
-
- // Now make a sorted array with the results.
- var arr = [];
- for (displayName in candidates) {
- if (candidates[displayName].exclusive >= DONT_CARE_THRESHOLD) {
- arr.push(candidates[displayName]);
- }
- }
-
- arr.sort(function (a, b) {
- return b.exclusive - a.exclusive;
- });
-
- return arr;
-}
-
-function getInclusiveSummary(measurements, onlyClean) {
- var candidates = {};
- var inclusiveKey;
-
- for (var i = 0; i < measurements.length; i++) {
- var measurement = measurements[i];
- var allIDs = _assign({}, measurement.exclusive, measurement.inclusive);
- var cleanComponents;
-
- if (onlyClean) {
- cleanComponents = getUnchangedComponents(measurement);
- }
-
- for (var id in allIDs) {
- if (onlyClean && !cleanComponents[id]) {
- continue;
- }
-
- var displayName = measurement.displayNames[id];
-
- // Inclusive time is not useful for many components without knowing where
- // they are instantiated. So we aggregate inclusive time with both the
- // owner and current displayName as the key.
- inclusiveKey = displayName.owner + ' > ' + displayName.current;
-
- candidates[inclusiveKey] = candidates[inclusiveKey] || {
- componentName: inclusiveKey,
- time: 0,
- count: 0
- };
-
- if (measurement.inclusive[id]) {
- candidates[inclusiveKey].time += measurement.inclusive[id];
- }
- if (measurement.counts[id]) {
- candidates[inclusiveKey].count += measurement.counts[id];
- }
- }
- }
-
- // Now make a sorted array with the results.
- var arr = [];
- for (inclusiveKey in candidates) {
- if (candidates[inclusiveKey].time >= DONT_CARE_THRESHOLD) {
- arr.push(candidates[inclusiveKey]);
- }
- }
-
- arr.sort(function (a, b) {
- return b.time - a.time;
- });
-
- return arr;
-}
-
-function getUnchangedComponents(measurement) {
- // For a given reconcile, look at which components did not actually
- // render anything to the DOM and return a mapping of their ID to
- // the amount of time it took to render the entire subtree.
- var cleanComponents = {};
- var writes = measurement.writes;
- var hierarchy = measurement.hierarchy;
- var dirtyComposites = {};
- Object.keys(writes).forEach(function (id) {
- writes[id].forEach(function (write) {
- // Root mounting (innerHTML set) is recorded with an ID of ''
- if (id !== '' && hierarchy.hasOwnProperty(id)) {
- hierarchy[id].forEach(function (c) {
- return dirtyComposites[c] = true;
- });
- }
- });
- });
- var allIDs = _assign({}, measurement.exclusive, measurement.inclusive);
-
- for (var id in allIDs) {
- var isDirty = false;
- // See if any of the DOM operations applied to this component's subtree.
- if (dirtyComposites[id]) {
- isDirty = true;
- }
- // check if component newly created
- if (measurement.created[id]) {
- isDirty = true;
- }
- if (!isDirty && measurement.counts[id] > 0) {
- cleanComponents[id] = true;
- }
- }
- return cleanComponents;
-}
-
-var ReactDefaultPerfAnalysis = {
- getExclusiveSummary: getExclusiveSummary,
- getInclusiveSummary: getInclusiveSummary,
- getDOMSummary: getDOMSummary,
- getTotalTime: getTotalTime
-};
-
-module.exports = ReactDefaultPerfAnalysis;
-},{"object-assign":32}],137:[function(require,module,exports){
+},{"./BeforeInputEventPlugin":78,"./ChangeEventPlugin":82,"./DefaultEventPluginOrder":89,"./EnterLeaveEventPlugin":91,"./HTMLDOMPropertyConfig":98,"./ReactComponentBrowserEnvironment":108,"./ReactDOMComponent":115,"./ReactDOMComponentTree":117,"./ReactDOMEmptyComponent":120,"./ReactDOMTextComponent":129,"./ReactDOMTreeTraversal":131,"./ReactDefaultBatchingStrategy":134,"./ReactEventListener":141,"./ReactInjection":143,"./ReactReconcileTransaction":160,"./SVGDOMPropertyConfig":167,"./SelectEventPlugin":168,"./SimpleEventPlugin":169}],136:[function(require,module,exports){
(function (process){
/**
* Copyright 2014-present, Facebook, Inc.
@@ -17248,6 +17259,10 @@ var ReactElement = function (type, key, ref, self, source, owner, props) {
return element;
};
+/**
+ * Create and return a new ReactElement of the given type.
+ * See https://facebook.github.io/react/docs/top-level-api.html#react.createelement
+ */
ReactElement.createElement = function (type, config, children) {
var propName;
@@ -17261,6 +17276,11 @@ ReactElement.createElement = function (type, config, children) {
if (config != null) {
if (process.env.NODE_ENV !== 'production') {
+ process.env.NODE_ENV !== 'production' ? warning(
+ /* eslint-disable no-proto */
+ config.__proto__ == null || config.__proto__ === Object.prototype,
+ /* eslint-enable no-proto */
+ 'React.createElement(...): Expected props argument to be a plain object. ' + 'Properties defined in its prototype chain will be ignored.') : void 0;
ref = !config.hasOwnProperty('ref') || Object.getOwnPropertyDescriptor(config, 'ref').get ? null : config.ref;
key = !config.hasOwnProperty('key') || Object.getOwnPropertyDescriptor(config, 'key').get ? null : '' + config.key;
} else {
@@ -17332,6 +17352,10 @@ ReactElement.createElement = function (type, config, children) {
return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
};
+/**
+ * Return a function that produces ReactElements of a given type.
+ * See https://facebook.github.io/react/docs/top-level-api.html#react.createfactory
+ */
ReactElement.createFactory = function (type) {
var factory = ReactElement.createElement.bind(null, type);
// Expose the type on the factory and the prototype so that it can be
@@ -17349,6 +17373,10 @@ ReactElement.cloneAndReplaceKey = function (oldElement, newKey) {
return newElement;
};
+/**
+ * Clone and return a new ReactElement using element as the starting point.
+ * See https://facebook.github.io/react/docs/top-level-api.html#react.cloneelement
+ */
ReactElement.cloneElement = function (element, config, children) {
var propName;
@@ -17369,6 +17397,13 @@ ReactElement.cloneElement = function (element, config, children) {
var owner = element._owner;
if (config != null) {
+ if (process.env.NODE_ENV !== 'production') {
+ process.env.NODE_ENV !== 'production' ? warning(
+ /* eslint-disable no-proto */
+ config.__proto__ == null || config.__proto__ === Object.prototype,
+ /* eslint-enable no-proto */
+ 'React.cloneElement(...): Expected props argument to be a plain object. ' + 'Properties defined in its prototype chain will be ignored.') : void 0;
+ }
if (config.ref !== undefined) {
// Silently steal the ref from the parent.
ref = config.ref;
@@ -17411,6 +17446,8 @@ ReactElement.cloneElement = function (element, config, children) {
};
/**
+ * Verifies the object is a ReactElement.
+ * See https://facebook.github.io/react/docs/top-level-api.html#react.isvalidelement
* @param {?object} object
* @return {boolean} True if `object` is a valid component.
* @final
@@ -17422,7 +17459,7 @@ ReactElement.isValidElement = function (object) {
module.exports = ReactElement;
}).call(this,require('_process'))
-},{"./ReactCurrentOwner":111,"./canDefineProperty":187,"_process":33,"fbjs/lib/warning":239,"object-assign":32}],138:[function(require,module,exports){
+},{"./ReactCurrentOwner":112,"./canDefineProperty":187,"_process":33,"fbjs/lib/warning":239,"object-assign":32}],137:[function(require,module,exports){
(function (process){
/**
* Copyright 2014-present, Facebook, Inc.
@@ -17707,7 +17744,7 @@ var ReactElementValidator = {
module.exports = ReactElementValidator;
}).call(this,require('_process'))
-},{"./ReactCurrentOwner":111,"./ReactElement":137,"./ReactPropTypeLocationNames":158,"./ReactPropTypeLocations":159,"./canDefineProperty":187,"./getIteratorFn":198,"_process":33,"fbjs/lib/invariant":229,"fbjs/lib/warning":239}],139:[function(require,module,exports){
+},{"./ReactCurrentOwner":112,"./ReactElement":136,"./ReactPropTypeLocationNames":157,"./ReactPropTypeLocations":158,"./canDefineProperty":187,"./getIteratorFn":198,"_process":33,"fbjs/lib/invariant":229,"fbjs/lib/warning":239}],138:[function(require,module,exports){
/**
* Copyright 2014-present, Facebook, Inc.
* All rights reserved.
@@ -17738,7 +17775,7 @@ var ReactEmptyComponent = {
ReactEmptyComponent.injection = ReactEmptyComponentInjection;
module.exports = ReactEmptyComponent;
-},{}],140:[function(require,module,exports){
+},{}],139:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -17818,7 +17855,7 @@ if (process.env.NODE_ENV !== 'production') {
module.exports = ReactErrorUtils;
}).call(this,require('_process'))
-},{"_process":33}],141:[function(require,module,exports){
+},{"_process":33}],140:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -17852,7 +17889,7 @@ var ReactEventEmitterMixin = {
};
module.exports = ReactEventEmitterMixin;
-},{"./EventPluginHub":93}],142:[function(require,module,exports){
+},{"./EventPluginHub":93}],141:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -18010,7 +18047,7 @@ var ReactEventListener = {
};
module.exports = ReactEventListener;
-},{"./PooledClass":101,"./ReactDOMComponentTree":116,"./ReactUpdates":165,"./getEventTarget":197,"fbjs/lib/EventListener":214,"fbjs/lib/ExecutionEnvironment":215,"fbjs/lib/getUnboundedScrollPosition":226,"object-assign":32}],143:[function(require,module,exports){
+},{"./PooledClass":101,"./ReactDOMComponentTree":117,"./ReactUpdates":165,"./getEventTarget":197,"fbjs/lib/EventListener":214,"fbjs/lib/ExecutionEnvironment":215,"fbjs/lib/getUnboundedScrollPosition":226,"object-assign":32}],142:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -18032,7 +18069,7 @@ var ReactFeatureFlags = {
};
module.exports = ReactFeatureFlags;
-},{}],144:[function(require,module,exports){
+},{}],143:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -18054,7 +18091,6 @@ var ReactClass = require('./ReactClass');
var ReactEmptyComponent = require('./ReactEmptyComponent');
var ReactBrowserEventEmitter = require('./ReactBrowserEventEmitter');
var ReactNativeComponent = require('./ReactNativeComponent');
-var ReactPerf = require('./ReactPerf');
var ReactUpdates = require('./ReactUpdates');
var ReactInjection = {
@@ -18066,12 +18102,11 @@ var ReactInjection = {
EventPluginUtils: EventPluginUtils.injection,
EventEmitter: ReactBrowserEventEmitter.injection,
NativeComponent: ReactNativeComponent.injection,
- Perf: ReactPerf.injection,
Updates: ReactUpdates.injection
};
module.exports = ReactInjection;
-},{"./DOMProperty":86,"./EventPluginHub":93,"./EventPluginUtils":95,"./ReactBrowserEventEmitter":103,"./ReactClass":106,"./ReactComponentEnvironment":109,"./ReactEmptyComponent":139,"./ReactNativeComponent":153,"./ReactPerf":157,"./ReactUpdates":165}],145:[function(require,module,exports){
+},{"./DOMProperty":86,"./EventPluginHub":93,"./EventPluginUtils":95,"./ReactBrowserEventEmitter":103,"./ReactClass":106,"./ReactComponentEnvironment":109,"./ReactEmptyComponent":138,"./ReactNativeComponent":152,"./ReactUpdates":165}],144:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -18196,7 +18231,7 @@ var ReactInputSelection = {
};
module.exports = ReactInputSelection;
-},{"./ReactDOMSelection":127,"fbjs/lib/containsNode":218,"fbjs/lib/focusNode":223,"fbjs/lib/getActiveElement":224}],146:[function(require,module,exports){
+},{"./ReactDOMSelection":128,"fbjs/lib/containsNode":218,"fbjs/lib/focusNode":223,"fbjs/lib/getActiveElement":224}],145:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -18245,7 +18280,7 @@ var ReactInstanceMap = {
};
module.exports = ReactInstanceMap;
-},{}],147:[function(require,module,exports){
+},{}],146:[function(require,module,exports){
/**
* Copyright 2016-present, Facebook, Inc.
* All rights reserved.
@@ -18262,7 +18297,7 @@ module.exports = ReactInstanceMap;
var ReactDebugTool = require('./ReactDebugTool');
module.exports = { debugTool: ReactDebugTool };
-},{"./ReactDebugTool":132}],148:[function(require,module,exports){
+},{"./ReactDebugTool":133}],147:[function(require,module,exports){
(function (process){
/**
* Copyright 2016-present, Facebook, Inc.
@@ -18302,7 +18337,7 @@ var ReactInvalidSetStateWarningDevTool = {
module.exports = ReactInvalidSetStateWarningDevTool;
}).call(this,require('_process'))
-},{"_process":33,"fbjs/lib/warning":239}],149:[function(require,module,exports){
+},{"_process":33,"fbjs/lib/warning":239}],148:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -18353,7 +18388,7 @@ var ReactMarkupChecksum = {
};
module.exports = ReactMarkupChecksum;
-},{"./adler32":186}],150:[function(require,module,exports){
+},{"./adler32":186}],149:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -18379,7 +18414,6 @@ var ReactElement = require('./ReactElement');
var ReactFeatureFlags = require('./ReactFeatureFlags');
var ReactInstrumentation = require('./ReactInstrumentation');
var ReactMarkupChecksum = require('./ReactMarkupChecksum');
-var ReactPerf = require('./ReactPerf');
var ReactReconciler = require('./ReactReconciler');
var ReactUpdateQueue = require('./ReactUpdateQueue');
var ReactUpdates = require('./ReactUpdates');
@@ -18617,6 +18651,10 @@ var ReactMount = {
* @return {ReactComponent} nextComponent
*/
_renderNewRootComponent: function (nextElement, container, shouldReuseMarkup, context) {
+ if (process.env.NODE_ENV !== 'production') {
+ ReactInstrumentation.debugTool.onBeginFlush();
+ }
+
// Various parts of our code (such as ReactCompositeComponent's
// _renderValidatedComponent) assume that calls to render aren't nested;
// verify that that's the case.
@@ -18627,6 +18665,12 @@ var ReactMount = {
ReactBrowserEventEmitter.ensureScrollValueMonitoring();
var componentInstance = instantiateReactComponent(nextElement);
+ if (process.env.NODE_ENV !== 'production') {
+ // Mute future events from the top level wrapper.
+ // It is an implementation detail that devtools should not know about.
+ componentInstance._debugID = 0;
+ }
+
// The initial render is synchronous but any updates that happen during
// rendering, in componentWillMount or componentDidMount, will be batched
// according to the current batching strategy.
@@ -18637,7 +18681,9 @@ var ReactMount = {
instancesByReactRootID[wrapperID] = componentInstance;
if (process.env.NODE_ENV !== 'production') {
- ReactInstrumentation.debugTool.onMountRootComponent(componentInstance);
+ // The instance here is TopLevelWrapper so we report mount for its child.
+ ReactInstrumentation.debugTool.onMountRootComponent(componentInstance._renderedComponent._debugID);
+ ReactInstrumentation.debugTool.onEndFlush();
}
return componentInstance;
@@ -18717,6 +18763,7 @@ var ReactMount = {
/**
* Renders a React component into the DOM in the supplied `container`.
+ * See https://facebook.github.io/react/docs/top-level-api.html#reactdom.render
*
* If the React component was previously rendered into `container`, this will
* perform an update on it and only mutate the DOM as necessary to reflect the
@@ -18733,6 +18780,7 @@ var ReactMount = {
/**
* Unmounts and destroys the React component rendered in the `container`.
+ * See https://facebook.github.io/react/docs/top-level-api.html#reactdom.unmountcomponentatnode
*
* @param {DOMElement} container DOM element containing a React component.
* @return {boolean} True if a component was found in and unmounted from
@@ -18824,18 +18872,20 @@ var ReactMount = {
setInnerHTML(container, markup);
ReactDOMComponentTree.precacheNode(instance, container.firstChild);
}
+
+ if (process.env.NODE_ENV !== 'production') {
+ var nativeNode = ReactDOMComponentTree.getInstanceFromNode(container.firstChild);
+ if (nativeNode._debugID !== 0) {
+ ReactInstrumentation.debugTool.onNativeOperation(nativeNode._debugID, 'mount', markup.toString());
+ }
+ }
}
};
-ReactPerf.measureMethods(ReactMount, 'ReactMount', {
- _renderNewRootComponent: '_renderNewRootComponent',
- _mountImageIntoNode: '_mountImageIntoNode'
-});
-
module.exports = ReactMount;
}).call(this,require('_process'))
-},{"./DOMLazyTree":84,"./DOMProperty":86,"./ReactBrowserEventEmitter":103,"./ReactCurrentOwner":111,"./ReactDOMComponentTree":116,"./ReactDOMContainerInfo":117,"./ReactDOMFeatureFlags":121,"./ReactElement":137,"./ReactFeatureFlags":143,"./ReactInstrumentation":147,"./ReactMarkupChecksum":149,"./ReactPerf":157,"./ReactReconciler":162,"./ReactUpdateQueue":164,"./ReactUpdates":165,"./instantiateReactComponent":203,"./setInnerHTML":209,"./shouldUpdateReactComponent":211,"_process":33,"fbjs/lib/emptyObject":222,"fbjs/lib/invariant":229,"fbjs/lib/warning":239}],151:[function(require,module,exports){
+},{"./DOMLazyTree":84,"./DOMProperty":86,"./ReactBrowserEventEmitter":103,"./ReactCurrentOwner":112,"./ReactDOMComponentTree":117,"./ReactDOMContainerInfo":118,"./ReactDOMFeatureFlags":122,"./ReactElement":136,"./ReactFeatureFlags":142,"./ReactInstrumentation":146,"./ReactMarkupChecksum":148,"./ReactReconciler":161,"./ReactUpdateQueue":164,"./ReactUpdates":165,"./instantiateReactComponent":203,"./setInnerHTML":209,"./shouldUpdateReactComponent":211,"_process":33,"fbjs/lib/emptyObject":222,"fbjs/lib/invariant":229,"fbjs/lib/warning":239}],150:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -18851,12 +18901,14 @@ module.exports = ReactMount;
'use strict';
var ReactComponentEnvironment = require('./ReactComponentEnvironment');
+var ReactInstrumentation = require('./ReactInstrumentation');
var ReactMultiChildUpdateTypes = require('./ReactMultiChildUpdateTypes');
var ReactCurrentOwner = require('./ReactCurrentOwner');
var ReactReconciler = require('./ReactReconciler');
var ReactChildReconciler = require('./ReactChildReconciler');
+var emptyFunction = require('fbjs/lib/emptyFunction');
var flattenChildren = require('./flattenChildren');
var invariant = require('fbjs/lib/invariant');
@@ -18973,6 +19025,15 @@ function processQueue(inst, updateQueue) {
ReactComponentEnvironment.processChildrenUpdates(inst, updateQueue);
}
+var setChildrenForInstrumentation = emptyFunction;
+if (process.env.NODE_ENV !== 'production') {
+ setChildrenForInstrumentation = function (children) {
+ ReactInstrumentation.debugTool.onSetChildren(this._debugID, children ? Object.keys(children).map(function (key) {
+ return children[key]._debugID;
+ }) : []);
+ };
+}
+
/**
* ReactMultiChild are capable of reconciling multiple children.
*
@@ -19034,6 +19095,7 @@ var ReactMultiChild = {
mountChildren: function (nestedChildren, transaction, context) {
var children = this._reconcilerInstantiateChildren(nestedChildren, transaction, context);
this._renderedChildren = children;
+
var mountImages = [];
var index = 0;
for (var name in children) {
@@ -19044,6 +19106,11 @@ var ReactMultiChild = {
mountImages.push(mountImage);
}
}
+
+ if (process.env.NODE_ENV !== 'production') {
+ setChildrenForInstrumentation.call(this, children);
+ }
+
return mountImages;
},
@@ -19150,6 +19217,10 @@ var ReactMultiChild = {
processQueue(this, updates);
}
this._renderedChildren = nextChildren;
+
+ if (process.env.NODE_ENV !== 'production') {
+ setChildrenForInstrumentation.call(this, nextChildren);
+ }
},
/**
@@ -19241,7 +19312,7 @@ var ReactMultiChild = {
module.exports = ReactMultiChild;
}).call(this,require('_process'))
-},{"./ReactChildReconciler":104,"./ReactComponentEnvironment":109,"./ReactCurrentOwner":111,"./ReactMultiChildUpdateTypes":152,"./ReactReconciler":162,"./flattenChildren":192,"_process":33,"fbjs/lib/invariant":229}],152:[function(require,module,exports){
+},{"./ReactChildReconciler":104,"./ReactComponentEnvironment":109,"./ReactCurrentOwner":112,"./ReactInstrumentation":146,"./ReactMultiChildUpdateTypes":151,"./ReactReconciler":161,"./flattenChildren":192,"_process":33,"fbjs/lib/emptyFunction":221,"fbjs/lib/invariant":229}],151:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -19274,7 +19345,7 @@ var ReactMultiChildUpdateTypes = keyMirror({
});
module.exports = ReactMultiChildUpdateTypes;
-},{"fbjs/lib/keyMirror":232}],153:[function(require,module,exports){
+},{"fbjs/lib/keyMirror":232}],152:[function(require,module,exports){
(function (process){
/**
* Copyright 2014-present, Facebook, Inc.
@@ -19373,7 +19444,45 @@ var ReactNativeComponent = {
module.exports = ReactNativeComponent;
}).call(this,require('_process'))
-},{"_process":33,"fbjs/lib/invariant":229,"object-assign":32}],154:[function(require,module,exports){
+},{"_process":33,"fbjs/lib/invariant":229,"object-assign":32}],153:[function(require,module,exports){
+/**
+ * Copyright 2016-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule ReactNativeOperationHistoryDevtool
+ */
+
+'use strict';
+
+var history = [];
+
+var ReactNativeOperationHistoryDevtool = {
+ onNativeOperation: function (debugID, type, payload) {
+ history.push({
+ instanceID: debugID,
+ type: type,
+ payload: payload
+ });
+ },
+ clearHistory: function () {
+ if (ReactNativeOperationHistoryDevtool._preventClearing) {
+ // Should only be used for tests.
+ return;
+ }
+
+ history = [];
+ },
+ getHistory: function () {
+ return history;
+ }
+};
+
+module.exports = ReactNativeOperationHistoryDevtool;
+},{}],154:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -19414,7 +19523,7 @@ var ReactNodeTypes = {
module.exports = ReactNodeTypes;
}).call(this,require('_process'))
-},{"./ReactElement":137,"_process":33,"fbjs/lib/invariant":229}],155:[function(require,module,exports){
+},{"./ReactElement":136,"_process":33,"fbjs/lib/invariant":229}],155:[function(require,module,exports){
(function (process){
/**
* Copyright 2015-present, Facebook, Inc.
@@ -19619,106 +19728,6 @@ module.exports = ReactOwner;
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
- * @providesModule ReactPerf
- */
-
-'use strict';
-
-/**
- * ReactPerf is a general AOP system designed to measure performance. This
- * module only has the hooks: see ReactDefaultPerf for the analysis tool.
- */
-
-var ReactPerf = {
- /**
- * Boolean to enable/disable measurement. Set to false by default to prevent
- * accidental logging and perf loss.
- */
- enableMeasure: false,
-
- /**
- * Holds onto the measure function in use. By default, don't measure
- * anything, but we'll override this if we inject a measure function.
- */
- storedMeasure: _noMeasure,
-
- /**
- * @param {object} object
- * @param {string} objectName
- * @param {object<string>} methodNames
- */
- measureMethods: function (object, objectName, methodNames) {
- if (process.env.NODE_ENV !== 'production') {
- for (var key in methodNames) {
- if (!methodNames.hasOwnProperty(key)) {
- continue;
- }
- object[key] = ReactPerf.measure(objectName, methodNames[key], object[key]);
- }
- }
- },
-
- /**
- * Use this to wrap methods you want to measure. Zero overhead in production.
- *
- * @param {string} objName
- * @param {string} fnName
- * @param {function} func
- * @return {function}
- */
- measure: function (objName, fnName, func) {
- if (process.env.NODE_ENV !== 'production') {
- var measuredFunc = null;
- var wrapper = function () {
- if (ReactPerf.enableMeasure) {
- if (!measuredFunc) {
- measuredFunc = ReactPerf.storedMeasure(objName, fnName, func);
- }
- return measuredFunc.apply(this, arguments);
- }
- return func.apply(this, arguments);
- };
- wrapper.displayName = objName + '_' + fnName;
- return wrapper;
- }
- return func;
- },
-
- injection: {
- /**
- * @param {function} measure
- */
- injectMeasure: function (measure) {
- ReactPerf.storedMeasure = measure;
- }
- }
-};
-
-/**
- * Simply passes through the measured function, without measuring it.
- *
- * @param {string} objName
- * @param {string} fnName
- * @param {function} func
- * @return {function}
- */
-function _noMeasure(objName, fnName, func) {
- return func;
-}
-
-module.exports = ReactPerf;
-}).call(this,require('_process'))
-
-},{"_process":33}],158:[function(require,module,exports){
-(function (process){
-/**
- * Copyright 2013-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
* @providesModule ReactPropTypeLocationNames
*/
@@ -19737,7 +19746,7 @@ if (process.env.NODE_ENV !== 'production') {
module.exports = ReactPropTypeLocationNames;
}).call(this,require('_process'))
-},{"_process":33}],159:[function(require,module,exports){
+},{"_process":33}],158:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -19760,7 +19769,7 @@ var ReactPropTypeLocations = keyMirror({
});
module.exports = ReactPropTypeLocations;
-},{"fbjs/lib/keyMirror":232}],160:[function(require,module,exports){
+},{"fbjs/lib/keyMirror":232}],159:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -20141,7 +20150,7 @@ function getClassName(propValue) {
}
module.exports = ReactPropTypes;
-},{"./ReactElement":137,"./ReactPropTypeLocationNames":158,"./getIteratorFn":198,"fbjs/lib/emptyFunction":221}],161:[function(require,module,exports){
+},{"./ReactElement":136,"./ReactPropTypeLocationNames":157,"./getIteratorFn":198,"fbjs/lib/emptyFunction":221}],160:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -20304,7 +20313,7 @@ _assign(ReactReconcileTransaction.prototype, Transaction.Mixin, Mixin);
PooledClass.addPoolingTo(ReactReconcileTransaction);
module.exports = ReactReconcileTransaction;
-},{"./CallbackQueue":81,"./PooledClass":101,"./ReactBrowserEventEmitter":103,"./ReactInputSelection":145,"./Transaction":183,"object-assign":32}],162:[function(require,module,exports){
+},{"./CallbackQueue":81,"./PooledClass":101,"./ReactBrowserEventEmitter":103,"./ReactInputSelection":144,"./Transaction":183,"object-assign":32}],161:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -20322,6 +20331,8 @@ module.exports = ReactReconcileTransaction;
var ReactRef = require('./ReactRef');
var ReactInstrumentation = require('./ReactInstrumentation');
+var invariant = require('fbjs/lib/invariant');
+
/**
* Helper to call ReactRef.attachRefs with this composite component, split out
* to avoid allocations in the transaction mount-ready queue.
@@ -20344,12 +20355,20 @@ var ReactReconciler = {
* @internal
*/
mountComponent: function (internalInstance, transaction, nativeParent, nativeContainerInfo, context) {
+ if (process.env.NODE_ENV !== 'production') {
+ if (internalInstance._debugID !== 0) {
+ ReactInstrumentation.debugTool.onBeginReconcilerTimer(internalInstance._debugID, 'mountComponent');
+ }
+ }
var markup = internalInstance.mountComponent(transaction, nativeParent, nativeContainerInfo, context);
if (internalInstance._currentElement && internalInstance._currentElement.ref != null) {
transaction.getReactMountReady().enqueue(attachRefs, internalInstance);
}
if (process.env.NODE_ENV !== 'production') {
- ReactInstrumentation.debugTool.onMountComponent(internalInstance);
+ if (internalInstance._debugID !== 0) {
+ ReactInstrumentation.debugTool.onEndReconcilerTimer(internalInstance._debugID, 'mountComponent');
+ ReactInstrumentation.debugTool.onMountComponent(internalInstance._debugID);
+ }
}
return markup;
},
@@ -20369,10 +20388,18 @@ var ReactReconciler = {
* @internal
*/
unmountComponent: function (internalInstance, safely) {
+ if (process.env.NODE_ENV !== 'production') {
+ if (internalInstance._debugID !== 0) {
+ ReactInstrumentation.debugTool.onBeginReconcilerTimer(internalInstance._debugID, 'unmountComponent');
+ }
+ }
ReactRef.detachRefs(internalInstance, internalInstance._currentElement);
internalInstance.unmountComponent(safely);
if (process.env.NODE_ENV !== 'production') {
- ReactInstrumentation.debugTool.onUnmountComponent(internalInstance);
+ if (internalInstance._debugID !== 0) {
+ ReactInstrumentation.debugTool.onEndReconcilerTimer(internalInstance._debugID, 'unmountComponent');
+ ReactInstrumentation.debugTool.onUnmountComponent(internalInstance._debugID);
+ }
}
},
@@ -20402,6 +20429,12 @@ var ReactReconciler = {
return;
}
+ if (process.env.NODE_ENV !== 'production') {
+ if (internalInstance._debugID !== 0) {
+ ReactInstrumentation.debugTool.onBeginReconcilerTimer(internalInstance._debugID, 'receiveComponent');
+ }
+ }
+
var refsChanged = ReactRef.shouldUpdateRefs(prevElement, nextElement);
if (refsChanged) {
@@ -20415,7 +20448,10 @@ var ReactReconciler = {
}
if (process.env.NODE_ENV !== 'production') {
- ReactInstrumentation.debugTool.onUpdateComponent(internalInstance);
+ if (internalInstance._debugID !== 0) {
+ ReactInstrumentation.debugTool.onEndReconcilerTimer(internalInstance._debugID, 'receiveComponent');
+ ReactInstrumentation.debugTool.onUpdateComponent(internalInstance._debugID);
+ }
}
},
@@ -20426,10 +20462,24 @@ var ReactReconciler = {
* @param {ReactReconcileTransaction} transaction
* @internal
*/
- performUpdateIfNecessary: function (internalInstance, transaction) {
+ performUpdateIfNecessary: function (internalInstance, transaction, updateBatchNumber) {
+ if (internalInstance._updateBatchNumber !== updateBatchNumber) {
+ // The component's enqueued batch number should always be the current
+ // batch or the following one.
+ !(internalInstance._updateBatchNumber == null || internalInstance._updateBatchNumber === updateBatchNumber + 1) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'performUpdateIfNecessary: Unexpected batch number (current %s, ' + 'pending %s)', updateBatchNumber, internalInstance._updateBatchNumber) : invariant(false) : void 0;
+ return;
+ }
+ if (process.env.NODE_ENV !== 'production') {
+ if (internalInstance._debugID !== 0) {
+ ReactInstrumentation.debugTool.onBeginReconcilerTimer(internalInstance._debugID, 'performUpdateIfNecessary');
+ }
+ }
internalInstance.performUpdateIfNecessary(transaction);
if (process.env.NODE_ENV !== 'production') {
- ReactInstrumentation.debugTool.onUpdateComponent(internalInstance);
+ if (internalInstance._debugID !== 0) {
+ ReactInstrumentation.debugTool.onEndReconcilerTimer(internalInstance._debugID, 'performUpdateIfNecessary');
+ ReactInstrumentation.debugTool.onUpdateComponent(internalInstance._debugID);
+ }
}
}
@@ -20438,7 +20488,7 @@ var ReactReconciler = {
module.exports = ReactReconciler;
}).call(this,require('_process'))
-},{"./ReactInstrumentation":147,"./ReactRef":163,"_process":33}],163:[function(require,module,exports){
+},{"./ReactInstrumentation":146,"./ReactRef":162,"_process":33,"fbjs/lib/invariant":229}],162:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -20517,7 +20567,81 @@ ReactRef.detachRefs = function (instance, element) {
};
module.exports = ReactRef;
-},{"./ReactOwner":156}],164:[function(require,module,exports){
+},{"./ReactOwner":156}],163:[function(require,module,exports){
+/**
+ * Copyright 2014-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule ReactServerRenderingTransaction
+ */
+
+'use strict';
+
+var _assign = require('object-assign');
+
+var PooledClass = require('./PooledClass');
+var Transaction = require('./Transaction');
+
+/**
+ * Executed within the scope of the `Transaction` instance. Consider these as
+ * being member methods, but with an implied ordering while being isolated from
+ * each other.
+ */
+var TRANSACTION_WRAPPERS = [];
+
+var noopCallbackQueue = {
+ enqueue: function () {}
+};
+
+/**
+ * @class ReactServerRenderingTransaction
+ * @param {boolean} renderToStaticMarkup
+ */
+function ReactServerRenderingTransaction(renderToStaticMarkup) {
+ this.reinitializeTransaction();
+ this.renderToStaticMarkup = renderToStaticMarkup;
+ this.useCreateElement = false;
+}
+
+var Mixin = {
+ /**
+ * @see Transaction
+ * @abstract
+ * @final
+ * @return {array} Empty list of operation wrap procedures.
+ */
+ getTransactionWrappers: function () {
+ return TRANSACTION_WRAPPERS;
+ },
+
+ /**
+ * @return {object} The queue to collect `onDOMReady` callbacks with.
+ */
+ getReactMountReady: function () {
+ return noopCallbackQueue;
+ },
+
+ /**
+ * `PooledClass` looks for this, and will invoke this before allowing this
+ * instance to be reused.
+ */
+ destructor: function () {},
+
+ checkpoint: function () {},
+
+ rollback: function () {}
+};
+
+_assign(ReactServerRenderingTransaction.prototype, Transaction.Mixin, Mixin);
+
+PooledClass.addPoolingTo(ReactServerRenderingTransaction);
+
+module.exports = ReactServerRenderingTransaction;
+},{"./PooledClass":101,"./Transaction":183,"object-assign":32}],164:[function(require,module,exports){
(function (process){
/**
* Copyright 2015-present, Facebook, Inc.
@@ -20736,7 +20860,7 @@ var ReactUpdateQueue = {
module.exports = ReactUpdateQueue;
}).call(this,require('_process'))
-},{"./ReactCurrentOwner":111,"./ReactInstanceMap":146,"./ReactUpdates":165,"_process":33,"fbjs/lib/invariant":229,"fbjs/lib/warning":239}],165:[function(require,module,exports){
+},{"./ReactCurrentOwner":112,"./ReactInstanceMap":145,"./ReactUpdates":165,"_process":33,"fbjs/lib/invariant":229,"fbjs/lib/warning":239}],165:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -20756,13 +20880,14 @@ var _assign = require('object-assign');
var CallbackQueue = require('./CallbackQueue');
var PooledClass = require('./PooledClass');
var ReactFeatureFlags = require('./ReactFeatureFlags');
-var ReactPerf = require('./ReactPerf');
+var ReactInstrumentation = require('./ReactInstrumentation');
var ReactReconciler = require('./ReactReconciler');
var Transaction = require('./Transaction');
var invariant = require('fbjs/lib/invariant');
var dirtyComponents = [];
+var updateBatchNumber = 0;
var asapCallbackQueue = CallbackQueue.getPooled();
var asapEnqueued = false;
@@ -20857,6 +20982,13 @@ function runBatchedUpdates(transaction) {
// them before their children by sorting the array.
dirtyComponents.sort(mountOrderComparator);
+ // Any updates enqueued while reconciling must be performed after this entire
+ // batch. Otherwise, if dirtyComponents is [A, B] where A has children B and
+ // C, B could update twice in a single batch if C's render enqueues an update
+ // to B (since B would have already updated, we should skip it, and the only
+ // way we can know to do so is by checking the batch counter).
+ updateBatchNumber++;
+
for (var i = 0; i < len; i++) {
// If a component is unmounted before pending changes apply, it will still
// be here, but we assume that it has cleared its _pendingCallbacks and
@@ -20880,7 +21012,7 @@ function runBatchedUpdates(transaction) {
console.time(markerName);
}
- ReactReconciler.performUpdateIfNecessary(component, transaction.reconcileTransaction);
+ ReactReconciler.performUpdateIfNecessary(component, transaction.reconcileTransaction, updateBatchNumber);
if (markerName) {
console.timeEnd(markerName);
@@ -20895,6 +21027,10 @@ function runBatchedUpdates(transaction) {
}
var flushBatchedUpdates = function () {
+ if (process.env.NODE_ENV !== 'production') {
+ ReactInstrumentation.debugTool.onBeginFlush();
+ }
+
// ReactUpdatesFlushTransaction's wrappers will clear the dirtyComponents
// array and perform any updates enqueued by mount-ready handlers (i.e.,
// componentDidUpdate) but we need to check here too in order to catch
@@ -20914,8 +21050,11 @@ var flushBatchedUpdates = function () {
CallbackQueue.release(queue);
}
}
+
+ if (process.env.NODE_ENV !== 'production') {
+ ReactInstrumentation.debugTool.onEndFlush();
+ }
};
-flushBatchedUpdates = ReactPerf.measure('ReactUpdates', 'flushBatchedUpdates', flushBatchedUpdates);
/**
* Mark a component as needing a rerender, adding an optional callback to a
@@ -20936,6 +21075,9 @@ function enqueueUpdate(component) {
}
dirtyComponents.push(component);
+ if (component._updateBatchNumber == null) {
+ component._updateBatchNumber = updateBatchNumber + 1;
+ }
}
/**
@@ -20981,7 +21123,7 @@ var ReactUpdates = {
module.exports = ReactUpdates;
}).call(this,require('_process'))
-},{"./CallbackQueue":81,"./PooledClass":101,"./ReactFeatureFlags":143,"./ReactPerf":157,"./ReactReconciler":162,"./Transaction":183,"_process":33,"fbjs/lib/invariant":229,"object-assign":32}],166:[function(require,module,exports){
+},{"./CallbackQueue":81,"./PooledClass":101,"./ReactFeatureFlags":142,"./ReactInstrumentation":146,"./ReactReconciler":161,"./Transaction":183,"_process":33,"fbjs/lib/invariant":229,"object-assign":32}],166:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -20995,7 +21137,7 @@ module.exports = ReactUpdates;
'use strict';
-module.exports = '15.0.2';
+module.exports = '15.1.0';
},{}],167:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -21494,7 +21636,7 @@ var SelectEventPlugin = {
};
module.exports = SelectEventPlugin;
-},{"./EventConstants":92,"./EventPropagators":96,"./ReactDOMComponentTree":116,"./ReactInputSelection":145,"./SyntheticEvent":174,"./isTextInputElement":205,"fbjs/lib/ExecutionEnvironment":215,"fbjs/lib/getActiveElement":224,"fbjs/lib/keyOf":233,"fbjs/lib/shallowEqual":238}],169:[function(require,module,exports){
+},{"./EventConstants":92,"./EventPropagators":96,"./ReactDOMComponentTree":117,"./ReactInputSelection":144,"./SyntheticEvent":174,"./isTextInputElement":205,"fbjs/lib/ExecutionEnvironment":215,"fbjs/lib/getActiveElement":224,"fbjs/lib/keyOf":233,"fbjs/lib/shallowEqual":238}],169:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -22125,7 +22267,7 @@ var SimpleEventPlugin = {
module.exports = SimpleEventPlugin;
}).call(this,require('_process'))
-},{"./EventConstants":92,"./EventPropagators":96,"./ReactDOMComponentTree":116,"./SyntheticAnimationEvent":170,"./SyntheticClipboardEvent":171,"./SyntheticDragEvent":173,"./SyntheticEvent":174,"./SyntheticFocusEvent":175,"./SyntheticKeyboardEvent":177,"./SyntheticMouseEvent":178,"./SyntheticTouchEvent":179,"./SyntheticTransitionEvent":180,"./SyntheticUIEvent":181,"./SyntheticWheelEvent":182,"./getEventCharCode":194,"_process":33,"fbjs/lib/EventListener":214,"fbjs/lib/emptyFunction":221,"fbjs/lib/invariant":229,"fbjs/lib/keyOf":233}],170:[function(require,module,exports){
+},{"./EventConstants":92,"./EventPropagators":96,"./ReactDOMComponentTree":117,"./SyntheticAnimationEvent":170,"./SyntheticClipboardEvent":171,"./SyntheticDragEvent":173,"./SyntheticEvent":174,"./SyntheticFocusEvent":175,"./SyntheticKeyboardEvent":177,"./SyntheticMouseEvent":178,"./SyntheticTouchEvent":179,"./SyntheticTransitionEvent":180,"./SyntheticUIEvent":181,"./SyntheticWheelEvent":182,"./getEventCharCode":194,"_process":33,"fbjs/lib/EventListener":214,"fbjs/lib/emptyFunction":221,"fbjs/lib/invariant":229,"fbjs/lib/keyOf":233}],170:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -23554,6 +23696,8 @@ var warning = require('fbjs/lib/warning');
/**
* Returns the DOM node rendered by this element.
*
+ * See https://facebook.github.io/react/docs/top-level-api.html#reactdom.finddomnode
+ *
* @param {ReactComponent|DOMElement} componentOrElement
* @return {?DOMElement} The root node of this element.
*/
@@ -23588,7 +23732,7 @@ function findDOMNode(componentOrElement) {
module.exports = findDOMNode;
}).call(this,require('_process'))
-},{"./ReactCurrentOwner":111,"./ReactDOMComponentTree":116,"./ReactInstanceMap":146,"./getNativeComponentFromComposite":199,"_process":33,"fbjs/lib/invariant":229,"fbjs/lib/warning":239}],192:[function(require,module,exports){
+},{"./ReactCurrentOwner":112,"./ReactDOMComponentTree":117,"./ReactInstanceMap":145,"./getNativeComponentFromComposite":199,"_process":33,"fbjs/lib/invariant":229,"fbjs/lib/warning":239}],192:[function(require,module,exports){
(function (process){
/**
* Copyright 2013-present, Facebook, Inc.
@@ -24209,6 +24353,7 @@ var _assign = require('object-assign');
var ReactCompositeComponent = require('./ReactCompositeComponent');
var ReactEmptyComponent = require('./ReactEmptyComponent');
var ReactNativeComponent = require('./ReactNativeComponent');
+var ReactInstrumentation = require('./ReactInstrumentation');
var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');
@@ -24231,6 +24376,21 @@ function getDeclarationErrorAddendum(owner) {
return '';
}
+function getDisplayName(instance) {
+ var element = instance._currentElement;
+ 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 (instance.getName) {
+ return instance.getName() || 'Unknown';
+ } else {
+ return element.type.displayName || element.type.name || 'Unknown';
+ }
+}
+
/**
* Check if the type reference is a known internal type. I.e. not a user
* provided composite type.
@@ -24242,6 +24402,8 @@ function isInternalComponentType(type) {
return typeof type === 'function' && typeof type.prototype !== 'undefined' && typeof type.prototype.mountComponent === 'function' && typeof type.prototype.receiveComponent === 'function';
}
+var nextDebugID = 1;
+
/**
* Given a ReactNode, create an instance that will actually be mounted.
*
@@ -24252,7 +24414,8 @@ function isInternalComponentType(type) {
function instantiateReactComponent(node) {
var instance;
- if (node === null || node === false) {
+ var isEmpty = node === null || node === false;
+ if (isEmpty) {
instance = ReactEmptyComponent.create(instantiateReactComponent);
} else if (typeof node === 'object') {
var element = node;
@@ -24290,6 +24453,20 @@ function instantiateReactComponent(node) {
instance._warnedAboutRefsInRender = false;
}
+ if (process.env.NODE_ENV !== 'production') {
+ var debugID = isEmpty ? 0 : nextDebugID++;
+ instance._debugID = debugID;
+
+ if (debugID !== 0) {
+ var displayName = getDisplayName(instance);
+ ReactInstrumentation.debugTool.onSetDisplayName(debugID, displayName);
+ var owner = node && node._owner;
+ if (owner) {
+ ReactInstrumentation.debugTool.onSetOwner(debugID, owner._debugID);
+ }
+ }
+ }
+
// Internal instances should fully constructed at this point, so they should
// not get any new fields added to them at this point.
if (process.env.NODE_ENV !== 'production') {
@@ -24304,7 +24481,7 @@ function instantiateReactComponent(node) {
module.exports = instantiateReactComponent;
}).call(this,require('_process'))
-},{"./ReactCompositeComponent":110,"./ReactEmptyComponent":139,"./ReactNativeComponent":153,"_process":33,"fbjs/lib/invariant":229,"fbjs/lib/warning":239,"object-assign":32}],204:[function(require,module,exports){
+},{"./ReactCompositeComponent":111,"./ReactEmptyComponent":138,"./ReactInstrumentation":146,"./ReactNativeComponent":152,"_process":33,"fbjs/lib/invariant":229,"fbjs/lib/warning":239,"object-assign":32}],204:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -24427,10 +24604,13 @@ var invariant = require('fbjs/lib/invariant');
/**
* Returns the first child in a collection of children and verifies that there
- * is only one child in the collection. The current implementation of this
- * function assumes that a single child gets passed without a wrapper, but the
- * purpose of this helper function is to abstract away the particular structure
- * of children.
+ * is only one child in the collection.
+ *
+ * See https://facebook.github.io/react/docs/top-level-api.html#react.children.only
+ *
+ * The current implementation of this function assumes that a single child gets
+ * passed without a wrapper, but the purpose of this helper function is to
+ * abstract away the particular structure of children.
*
* @param {?object} children Child collection structure.
* @return {ReactElement} The first and only `ReactElement` contained in the
@@ -24444,7 +24624,7 @@ function onlyChild(children) {
module.exports = onlyChild;
}).call(this,require('_process'))
-},{"./ReactElement":137,"_process":33,"fbjs/lib/invariant":229}],207:[function(require,module,exports){
+},{"./ReactElement":136,"_process":33,"fbjs/lib/invariant":229}],207:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -24488,7 +24668,7 @@ module.exports = quoteAttributeValueForBrowser;
var ReactMount = require('./ReactMount');
module.exports = ReactMount.renderSubtreeIntoContainer;
-},{"./ReactMount":150}],209:[function(require,module,exports){
+},{"./ReactMount":149}],209:[function(require,module,exports){
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
@@ -24817,7 +24997,7 @@ function traverseAllChildren(children, callback, traverseContext) {
module.exports = traverseAllChildren;
}).call(this,require('_process'))
-},{"./KeyEscapeUtils":99,"./ReactCurrentOwner":111,"./ReactElement":137,"./getIteratorFn":198,"_process":33,"fbjs/lib/invariant":229,"fbjs/lib/warning":239}],213:[function(require,module,exports){
+},{"./KeyEscapeUtils":99,"./ReactCurrentOwner":112,"./ReactElement":136,"./getIteratorFn":198,"_process":33,"fbjs/lib/invariant":229,"fbjs/lib/warning":239}],213:[function(require,module,exports){
(function (process){
/**
* Copyright 2015-present, Facebook, Inc.
@@ -25227,18 +25407,18 @@ var EventListener = {
* @param {function} callback Callback function.
* @return {object} Object with a `remove` method.
*/
- listen: function (target, eventType, callback) {
+ listen: function listen(target, eventType, callback) {
if (target.addEventListener) {
target.addEventListener(eventType, callback, false);
return {
- remove: function () {
+ remove: function remove() {
target.removeEventListener(eventType, callback, false);
}
};
} else if (target.attachEvent) {
target.attachEvent('on' + eventType, callback);
return {
- remove: function () {
+ remove: function remove() {
target.detachEvent('on' + eventType, callback);
}
};
@@ -25253,11 +25433,11 @@ var EventListener = {
* @param {function} callback Callback function.
* @return {object} Object with a `remove` method.
*/
- capture: function (target, eventType, callback) {
+ capture: function capture(target, eventType, callback) {
if (target.addEventListener) {
target.addEventListener(eventType, callback, true);
return {
- remove: function () {
+ remove: function remove() {
target.removeEventListener(eventType, callback, true);
}
};
@@ -25271,7 +25451,7 @@ var EventListener = {
}
},
- registerDefault: function () {}
+ registerDefault: function registerDefault() {}
};
module.exports = EventListener;
@@ -25396,7 +25576,7 @@ module.exports = camelizeStyleName;
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
- * @typechecks
+ *
*/
var isTextNode = require('./isTextNode');
@@ -25405,10 +25585,6 @@ var isTextNode = require('./isTextNode');
/**
* Checks if a given DOM node contains or is another DOM node.
- *
- * @param {?DOMNode} outerNode Outer DOM node.
- * @param {?DOMNode} innerNode Inner DOM node.
- * @return {boolean} True if `outerNode` contains or is `innerNode`.
*/
function containsNode(outerNode, innerNode) {
if (!outerNode || !innerNode) {
@@ -25419,7 +25595,7 @@ function containsNode(outerNode, innerNode) {
return false;
} else if (isTextNode(innerNode)) {
return containsNode(outerNode, innerNode.parentNode);
- } else if (outerNode.contains) {
+ } else if ('contains' in outerNode) {
return outerNode.contains(innerNode);
} else if (outerNode.compareDocumentPosition) {
return !!(outerNode.compareDocumentPosition(innerNode) & 16);
@@ -25657,6 +25833,7 @@ module.exports = createNodesFromMarkup;
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
+ *
*/
function makeEmptyFunction(arg) {
@@ -25670,7 +25847,7 @@ function makeEmptyFunction(arg) {
* primarily useful idiomatically for overridable function endpoints which
* always need to be callable, since JS lacks a null-call idiom ala Cocoa.
*/
-function emptyFunction() {}
+var emptyFunction = function emptyFunction() {};
emptyFunction.thatReturns = makeEmptyFunction;
emptyFunction.thatReturnsFalse = makeEmptyFunction(false);
@@ -26114,7 +26291,7 @@ var invariant = require('./invariant');
* @param {object} obj
* @return {object}
*/
-var keyMirror = function (obj) {
+var keyMirror = function keyMirror(obj) {
var ret = {};
var key;
!(obj instanceof Object && !Array.isArray(obj)) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'keyMirror(...): Argument must be an object.') : invariant(false) : void 0;
@@ -26153,7 +26330,7 @@ module.exports = keyMirror;
* 'xa12' in that case. Resolve keys you want to use once at startup time, then
* reuse those resolutions.
*/
-var keyOf = function (oneKeyObj) {
+var keyOf = function keyOf(oneKeyObj) {
var key;
for (key in oneKeyObj) {
if (!oneKeyObj.hasOwnProperty(key)) {
@@ -26225,6 +26402,7 @@ module.exports = mapObject;
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
+ *
* @typechecks static-only
*/
@@ -26232,9 +26410,6 @@ module.exports = mapObject;
/**
* Memoizes the return value of a function that accepts one string argument.
- *
- * @param {function} callback
- * @return {function}
*/
function memoizeStringOnly(callback) {
@@ -26295,11 +26470,11 @@ var performanceNow;
* because of Facebook's testing infrastructure.
*/
if (performance.now) {
- performanceNow = function () {
+ performanceNow = function performanceNow() {
return performance.now();
};
} else {
- performanceNow = function () {
+ performanceNow = function performanceNow() {
return Date.now();
};
}
@@ -26398,7 +26573,7 @@ var emptyFunction = require('./emptyFunction');
var warning = emptyFunction;
if (process.env.NODE_ENV !== 'production') {
- warning = function (condition, format) {
+ warning = function warning(condition, format) {
for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
args[_key - 2] = arguments[_key];
}
@@ -53172,7 +53347,7 @@ return jQuery;
module.exports = require('react/lib/ReactDOM');
-},{"react/lib/ReactDOM":112}],"react-redux":[function(require,module,exports){
+},{"react/lib/ReactDOM":113}],"react-redux":[function(require,module,exports){
'use strict';
exports.__esModule = true;
diff --git a/netlib/tcp.py b/netlib/tcp.py
index de12102e..0eec326b 100644
--- a/netlib/tcp.py
+++ b/netlib/tcp.py
@@ -580,8 +580,10 @@ class _Connection(object):
@contextlib.contextmanager
def _closer(client):
- yield
- client.close()
+ try:
+ yield
+ finally:
+ client.close()
class TCPClient(_Connection):
diff --git a/pathod/language/base.py b/pathod/language/base.py
index 1369a3c7..25f3fd1a 100644
--- a/pathod/language/base.py
+++ b/pathod/language/base.py
@@ -261,7 +261,7 @@ class _Component(Token):
"""
A value component of the primary specification of an message.
- Components produce byte values desribe the bytes of the message.
+ Components produce byte values describing the bytes of the message.
"""
def values(self, settings): # pragma: no cover
@@ -272,9 +272,9 @@ class _Component(Token):
def string(self, settings=None):
"""
- A string representation of the object.
+ A bytestring representation of the object.
"""
- return "".join(i[:] for i in self.values(settings or {}))
+ return b"".join(i[:] for i in self.values(settings or {}))
class KeyValue(_Component):
@@ -391,7 +391,7 @@ class Integer(_Component):
"Integer value must be between %s and %s." % self.bounds,
0, 0
)
- self.value = str(value)
+ self.value = str(value).encode()
@classmethod
def expr(cls):
@@ -401,10 +401,10 @@ class Integer(_Component):
return e.setParseAction(lambda x: cls(*x))
def values(self, settings):
- return self.value
+ return [self.value]
def spec(self):
- return "%s%s" % (self.preamble, self.value)
+ return "%s%s" % (self.preamble, self.value.decode())
def freeze(self, settings_):
return self
@@ -555,7 +555,7 @@ class NestedMessage(Token):
try:
self.parsed = self.nest_type(
self.nest_type.expr().parseString(
- value.val,
+ value.val.decode(),
parseAll=True
)
)
@@ -578,4 +578,4 @@ class NestedMessage(Token):
def freeze(self, settings):
f = self.parsed.freeze(settings).spec()
- return self.__class__(TokValueLiteral(strutils.bytes_to_escaped_str(f)))
+ return self.__class__(TokValueLiteral(strutils.bytes_to_escaped_str(f.encode())))
diff --git a/pathod/language/generators.py b/pathod/language/generators.py
index 9fff3082..68705941 100644
--- a/pathod/language/generators.py
+++ b/pathod/language/generators.py
@@ -3,6 +3,7 @@ import random
import mmap
import six
+import sys
DATATYPES = dict(
ascii_letters=string.ascii_letters.encode(),
@@ -68,7 +69,7 @@ class RandomGenerator(object):
def __getitem__(self, x):
chars = DATATYPES[self.dtype]
if isinstance(x, slice):
- return b"".join(rand_byte(chars) for _ in range(*x.indices(self.length)))
+ return b"".join(rand_byte(chars) for _ in range(*x.indices(min(self.length, sys.maxsize))))
return rand_byte(chars)
def __repr__(self):
diff --git a/pathod/language/http.py b/pathod/language/http.py
index b2308d5e..4cc7db5f 100644
--- a/pathod/language/http.py
+++ b/pathod/language/http.py
@@ -57,7 +57,7 @@ class _HeaderMixin(object):
unique_name = None
def format_header(self, key, value):
- return [key, ": ", value, "\r\n"]
+ return [key, b": ", value, b"\r\n"]
def values(self, settings):
return self.format_header(
@@ -88,7 +88,7 @@ class ShortcutUserAgent(_HeaderMixin, base.OptionsOrValue):
def values(self, settings):
value = self.value.val
if self.option_used:
- value = user_agents.get_by_shortcut(value.lower())[2]
+ value = user_agents.get_by_shortcut(value.lower().decode())[2].encode()
return self.format_header(
self.key.get_generator(settings),
@@ -109,7 +109,7 @@ def get_header(val, headers):
class _HTTPMessage(message.Message):
- version = "HTTP/1.1"
+ version = b"HTTP/1.1"
@property
def actions(self):
@@ -133,10 +133,10 @@ class _HTTPMessage(message.Message):
def values(self, settings):
vals = self.preamble(settings)
- vals.append("\r\n")
+ vals.append(b"\r\n")
for h in self.headers:
vals.extend(h.values(settings))
- vals.append("\r\n")
+ vals.append(b"\r\n")
if self.body:
vals.extend(self.body.values(settings))
return vals
@@ -171,18 +171,18 @@ class Response(_HTTPMessage):
return self.tok(Reason)
def preamble(self, settings):
- l = [self.version, " "]
+ l = [self.version, b" "]
l.extend(self.status_code.values(settings))
status_code = int(self.status_code.value)
- l.append(" ")
+ l.append(b" ")
if self.reason:
l.extend(self.reason.values(settings))
else:
l.append(
status_codes.RESPONSES.get(
status_code,
- "Unknown code"
- )
+ b"Unknown code"
+ ).encode()
)
return l
@@ -205,8 +205,8 @@ class Response(_HTTPMessage):
if not get_header(i[0], self.headers):
tokens.append(
Header(
- base.TokValueLiteral(i[0]),
- base.TokValueLiteral(i[1]))
+ base.TokValueLiteral(i[0].decode()),
+ base.TokValueLiteral(i[1].decode()))
)
if not self.raw:
if not get_header("Content-Length", self.headers):
@@ -294,11 +294,11 @@ class Request(_HTTPMessage):
def preamble(self, settings):
v = self.method.values(settings)
- v.append(" ")
+ v.append(b" ")
v.extend(self.path.values(settings))
if self.nested_response:
v.append(self.nested_response.parsed.spec())
- v.append(" ")
+ v.append(b" ")
v.append(self.version)
return v
@@ -314,8 +314,8 @@ class Request(_HTTPMessage):
if not get_header(i[0], self.headers):
tokens.append(
Header(
- base.TokValueLiteral(i[0]),
- base.TokValueLiteral(i[1])
+ base.TokValueLiteral(i[0].decode()),
+ base.TokValueLiteral(i[1].decode())
)
)
if not self.raw:
diff --git a/pathod/language/message.py b/pathod/language/message.py
index 33124856..fea4f4de 100644
--- a/pathod/language/message.py
+++ b/pathod/language/message.py
@@ -1,5 +1,6 @@
import abc
from . import actions, exceptions
+from netlib import strutils
LOG_TRUNCATE = 1024
@@ -49,7 +50,7 @@ class Message(object):
def preview_safe(self):
"""
- Return a copy of this message that issafe for previews.
+ Return a copy of this message that is safe for previews.
"""
tokens = [i for i in self.tokens if not isinstance(i, actions.PauseAt)]
return self.__class__(tokens)
@@ -80,10 +81,10 @@ class Message(object):
# We truncate at 1k.
if hasattr(v, "values"):
v = [x[:LOG_TRUNCATE] for x in v.values(settings)]
- v = "".join(v).encode("string_escape")
+ v = strutils.bytes_to_escaped_str(b"".join(v))
elif hasattr(v, "__len__"):
v = v[:LOG_TRUNCATE]
- v = v.encode("string_escape")
+ v = strutils.bytes_to_escaped_str(v)
ret[i] = v
ret["spec"] = self.spec()
return ret
diff --git a/pathod/pathoc.py b/pathod/pathoc.py
index 5cfb4591..3eb91637 100644
--- a/pathod/pathoc.py
+++ b/pathod/pathoc.py
@@ -247,7 +247,10 @@ class Pathoc(tcp.TCPClient):
def socks_connect(self, connect_to):
try:
- client_greet = socks.ClientGreeting(socks.VERSION.SOCKS5, [socks.METHOD.NO_AUTHENTICATION_REQUIRED])
+ client_greet = socks.ClientGreeting(
+ socks.VERSION.SOCKS5,
+ [socks.METHOD.NO_AUTHENTICATION_REQUIRED]
+ )
client_greet.to_file(self.wfile)
self.wfile.flush()
@@ -286,44 +289,47 @@ class Pathoc(tcp.TCPClient):
if self.use_http2 and not self.ssl:
raise NotImplementedError("HTTP2 without SSL is not supported.")
- ret = tcp.TCPClient.connect(self)
+ try:
+ ret = tcp.TCPClient.connect(self)
+ if connect_to:
+ self.http_connect(connect_to)
- if connect_to:
- self.http_connect(connect_to)
+ self.sslinfo = None
+ if self.ssl:
+ try:
+ alpn_protos = [b'http/1.1']
+ if self.use_http2:
+ alpn_protos.append(b'h2')
+
+ self.convert_to_ssl(
+ sni=self.sni,
+ cert=self.clientcert,
+ method=self.ssl_version,
+ options=self.ssl_options,
+ cipher_list=self.ciphers,
+ alpn_protos=alpn_protos
+ )
+ except TlsException as v:
+ raise PathocError(str(v))
- self.sslinfo = None
- if self.ssl:
- try:
- alpn_protos = [b'http/1.1']
- if self.use_http2:
- alpn_protos.append(b'h2')
-
- self.convert_to_ssl(
- sni=self.sni,
- cert=self.clientcert,
- method=self.ssl_version,
- options=self.ssl_options,
- cipher_list=self.ciphers,
- alpn_protos=alpn_protos
+ self.sslinfo = SSLInfo(
+ self.connection.get_peer_cert_chain(),
+ self.get_current_cipher(),
+ self.get_alpn_proto_negotiated()
)
- except TlsException as v:
- raise PathocError(str(v))
-
- self.sslinfo = SSLInfo(
- self.connection.get_peer_cert_chain(),
- self.get_current_cipher(),
- self.get_alpn_proto_negotiated()
- )
- if showssl:
- print(str(self.sslinfo), file=fp)
-
- if self.use_http2:
- self.protocol.check_alpn()
- if not self.http2_skip_connection_preface:
- self.protocol.perform_client_connection_preface()
+ if showssl:
+ print(str(self.sslinfo), file=fp)
- if self.timeout:
- self.settimeout(self.timeout)
+ if self.use_http2:
+ self.protocol.check_alpn()
+ if not self.http2_skip_connection_preface:
+ self.protocol.perform_client_connection_preface()
+
+ if self.timeout:
+ self.settimeout(self.timeout)
+ except Exception:
+ self.close()
+ raise
return ret
def stop(self):
@@ -355,6 +361,7 @@ class Pathoc(tcp.TCPClient):
return
if frm is None:
self.ws_framereader.join()
+ self.ws_framereader = None
return
yield frm
diff --git a/setup.py b/setup.py
index 2e7f0a0b..dd34465c 100644
--- a/setup.py
+++ b/setup.py
@@ -75,7 +75,7 @@ setup(
"passlib>=1.6.5, <1.7",
"pyasn1>=0.1.9, <0.2",
"pyOpenSSL>=16.0, <17.0",
- "pyparsing>=2.0, <2.1", # 2.1.1 breaks our binaries, see https://sourceforge.net/p/pyparsing/bugs/93/
+ "pyparsing>=2.1.3, <2.2",
"pyperclip>=1.5.22, <1.6",
"requests>=2.9.1, <2.10",
"six>=1.10, <1.11",
diff --git a/test/pathod/test_language_base.py b/test/pathod/test_language_base.py
index 075dc2b8..7c7d8cf9 100644
--- a/test/pathod/test_language_base.py
+++ b/test/pathod/test_language_base.py
@@ -55,8 +55,15 @@ class TestTokValueLiteral:
v = base.TokValueLiteral("f\x00oo")
assert v.spec() == repr(v) == r"'f\x00oo'"
- v = base.TokValueLiteral("\"")
- assert v.spec() == repr(v) == '\'"\''
+ v = base.TokValueLiteral('"')
+ assert v.spec() == repr(v) == """ '"' """.strip()
+
+ # While pyparsing has a escChar argument for QuotedString,
+ # escChar only performs scapes single-character escapes and does not work for e.g. r"\x02".
+ # Thus, we cannot use that option, which means we cannot have single quotes in strings.
+ # To fix this, we represent single quotes as r"\x07".
+ v = base.TokValueLiteral("'")
+ assert v.spec() == r"'\x27'"
def roundtrip(self, spec):
e = base.TokValueLiteral.expr()
@@ -311,7 +318,7 @@ def test_options_or_value():
def test_integer():
e = base.Integer.expr()
v = e.parseString("200")[0]
- assert v.string() == "200"
+ assert v.string() == b"200"
assert v.spec() == "200"
assert v.freeze({}).value == v.value
diff --git a/test/pathod/test_language_http.py b/test/pathod/test_language_http.py
index d1870a63..18059e3a 100644
--- a/test/pathod/test_language_http.py
+++ b/test/pathod/test_language_http.py
@@ -1,15 +1,15 @@
-from six.moves import cStringIO as StringIO
+from six import BytesIO
from pathod import language
from pathod.language import http, base
import tutils
def parse_request(s):
- return language.parse_pathoc(s).next()
+ return next(language.parse_pathoc(s))
def test_make_error_response():
- d = StringIO()
+ d = BytesIO()
s = http.make_error_response("foo")
language.serve(s, d, {})
@@ -24,17 +24,17 @@ class TestRequest:
def test_simple(self):
r = parse_request('GET:"/foo"')
- assert r.method.string() == "GET"
- assert r.path.string() == "/foo"
+ assert r.method.string() == b"GET"
+ assert r.path.string() == b"/foo"
r = parse_request('GET:/foo')
- assert r.path.string() == "/foo"
+ assert r.path.string() == b"/foo"
r = parse_request('GET:@1k')
assert len(r.path.string()) == 1024
def test_multiple(self):
r = list(language.parse_pathoc("GET:/ PUT:/"))
- assert r[0].method.string() == "GET"
- assert r[1].method.string() == "PUT"
+ assert r[0].method.string() == b"GET"
+ assert r[1].method.string() == b"PUT"
assert len(r) == 2
l = """
@@ -54,8 +54,8 @@ class TestRequest:
"""
r = list(language.parse_pathoc(l))
assert len(r) == 2
- assert r[0].method.string() == "GET"
- assert r[1].method.string() == "PUT"
+ assert r[0].method.string() == b"GET"
+ assert r[1].method.string() == b"PUT"
l = """
get:"http://localhost:9999/p/200":ir,@1
@@ -63,8 +63,8 @@ class TestRequest:
"""
r = list(language.parse_pathoc(l))
assert len(r) == 2
- assert r[0].method.string() == "GET"
- assert r[1].method.string() == "GET"
+ assert r[0].method.string() == b"GET"
+ assert r[1].method.string() == b"GET"
def test_nested_response(self):
l = "get:/p:s'200'"
@@ -75,7 +75,7 @@ class TestRequest:
assert r[0].values({})
def test_render(self):
- s = StringIO()
+ s = BytesIO()
r = parse_request("GET:'/foo'")
assert language.serve(
r,
@@ -90,8 +90,8 @@ class TestRequest:
ir,@1
"""
r = parse_request(l)
- assert r.method.string() == "GET"
- assert r.path.string() == "/foo"
+ assert r.method.string() == b"GET"
+ assert r.path.string() == b"/foo"
assert r.actions
l = """
@@ -106,8 +106,8 @@ class TestRequest:
ir,@1
"""
r = parse_request(l)
- assert r.method.string() == "GET"
- assert r.path.string().endswith("bar")
+ assert r.method.string() == b"GET"
+ assert r.path.string().endswith(b"bar")
assert r.actions
def test_spec(self):
@@ -128,66 +128,66 @@ class TestRequest:
def test_websocket(self):
r = parse_request('ws:/path/')
res = r.resolve(language.Settings())
- assert res.method.string().lower() == "get"
- assert res.tok(http.Path).value.val == "/path/"
- assert res.tok(http.Method).value.val.lower() == "get"
- assert http.get_header("Upgrade", res.headers).value.val == "websocket"
+ assert res.method.string().lower() == b"get"
+ assert res.tok(http.Path).value.val == b"/path/"
+ assert res.tok(http.Method).value.val.lower() == b"get"
+ assert http.get_header(b"Upgrade", res.headers).value.val == b"websocket"
r = parse_request('ws:put:/path/')
res = r.resolve(language.Settings())
- assert r.method.string().lower() == "put"
- assert res.tok(http.Path).value.val == "/path/"
- assert res.tok(http.Method).value.val.lower() == "put"
- assert http.get_header("Upgrade", res.headers).value.val == "websocket"
+ assert r.method.string().lower() == b"put"
+ assert res.tok(http.Path).value.val == b"/path/"
+ assert res.tok(http.Method).value.val.lower() == b"put"
+ assert http.get_header(b"Upgrade", res.headers).value.val == b"websocket"
class TestResponse:
def dummy_response(self):
- return language.parse_pathod("400'msg'").next()
+ return next(language.parse_pathod("400'msg'"))
def test_response(self):
- r = language.parse_pathod("400:m'msg'").next()
- assert r.status_code.string() == "400"
- assert r.reason.string() == "msg"
+ r = next(language.parse_pathod("400:m'msg'"))
+ assert r.status_code.string() == b"400"
+ assert r.reason.string() == b"msg"
- r = language.parse_pathod("400:m'msg':b@100b").next()
- assert r.reason.string() == "msg"
+ r = next(language.parse_pathod("400:m'msg':b@100b"))
+ assert r.reason.string() == b"msg"
assert r.body.values({})
assert str(r)
- r = language.parse_pathod("200").next()
- assert r.status_code.string() == "200"
+ r = next(language.parse_pathod("200"))
+ assert r.status_code.string() == b"200"
assert not r.reason
- assert "OK" in [i[:] for i in r.preamble({})]
+ assert b"OK" in [i[:] for i in r.preamble({})]
def test_render(self):
- s = StringIO()
- r = language.parse_pathod("400:m'msg'").next()
+ s = BytesIO()
+ r = next(language.parse_pathod("400:m'msg'"))
assert language.serve(r, s, {})
- r = language.parse_pathod("400:p0,100:dr").next()
+ r = next(language.parse_pathod("400:p0,100:dr"))
assert "p0" in r.spec()
s = r.preview_safe()
assert "p0" not in s.spec()
def test_raw(self):
- s = StringIO()
- r = language.parse_pathod("400:b'foo'").next()
+ s = BytesIO()
+ r = next(language.parse_pathod("400:b'foo'"))
language.serve(r, s, {})
v = s.getvalue()
- assert "Content-Length" in v
+ assert b"Content-Length" in v
- s = StringIO()
- r = language.parse_pathod("400:b'foo':r").next()
+ s = BytesIO()
+ r = next(language.parse_pathod("400:b'foo':r"))
language.serve(r, s, {})
v = s.getvalue()
- assert "Content-Length" not in v
+ assert b"Content-Length" not in v
def test_length(self):
def testlen(x):
- s = StringIO()
- x = x.next()
+ s = BytesIO()
+ x = next(x)
language.serve(x, s, language.Settings())
assert x.length(language.Settings()) == len(s.getvalue())
testlen(language.parse_pathod("400:m'msg':r"))
@@ -196,8 +196,8 @@ class TestResponse:
def test_maximum_length(self):
def testlen(x):
- x = x.next()
- s = StringIO()
+ x = next(x)
+ s = BytesIO()
m = x.maximum_length({})
language.serve(x, s, {})
assert m >= len(s.getvalue())
@@ -225,19 +225,19 @@ class TestResponse:
tutils.raises("ascii", language.parse_pathod, "foo:b\xf0")
def test_parse_header(self):
- r = language.parse_pathod('400:h"foo"="bar"').next()
- assert http.get_header("foo", r.headers)
+ r = next(language.parse_pathod('400:h"foo"="bar"'))
+ assert http.get_header(b"foo", r.headers)
def test_parse_pause_before(self):
- r = language.parse_pathod("400:p0,10").next()
+ r = next(language.parse_pathod("400:p0,10"))
assert r.actions[0].spec() == "p0,10"
def test_parse_pause_after(self):
- r = language.parse_pathod("400:pa,10").next()
+ r = next(language.parse_pathod("400:pa,10"))
assert r.actions[0].spec() == "pa,10"
def test_parse_pause_random(self):
- r = language.parse_pathod("400:pr,10").next()
+ r = next(language.parse_pathod("400:pr,10"))
assert r.actions[0].spec() == "pr,10"
def test_parse_stress(self):
@@ -245,29 +245,29 @@ class TestResponse:
# returns an int and a python 2.7 int on windows has 32bit precision.
# Therefore, we should keep the body length < 2147483647 bytes in our
# tests.
- r = language.parse_pathod("400:b@1g").next()
+ r = next(language.parse_pathod("400:b@1g"))
assert r.length({})
def test_spec(self):
def rt(s):
- s = language.parse_pathod(s).next().spec()
- assert language.parse_pathod(s).next().spec() == s
+ s = next(language.parse_pathod(s)).spec()
+ assert next(language.parse_pathod(s)).spec() == s
rt("400:b@100g")
rt("400")
rt("400:da")
def test_websockets(self):
- r = language.parse_pathod("ws").next()
+ r = next(language.parse_pathod("ws"))
tutils.raises("no websocket key", r.resolve, language.Settings())
- res = r.resolve(language.Settings(websocket_key="foo"))
- assert res.status_code.string() == "101"
+ res = r.resolve(language.Settings(websocket_key=b"foo"))
+ assert res.status_code.string() == b"101"
def test_ctype_shortcut():
e = http.ShortcutContentType.expr()
v = e.parseString("c'foo'")[0]
- assert v.key.val == "Content-Type"
- assert v.value.val == "foo"
+ assert v.key.val == b"Content-Type"
+ assert v.value.val == b"foo"
s = v.spec()
assert s == e.parseString(s)[0].spec()
@@ -282,8 +282,8 @@ def test_ctype_shortcut():
def test_location_shortcut():
e = http.ShortcutLocation.expr()
v = e.parseString("l'foo'")[0]
- assert v.key.val == "Location"
- assert v.value.val == "foo"
+ assert v.key.val == b"Location"
+ assert v.value.val == b"foo"
s = v.spec()
assert s == e.parseString(s)[0].spec()
@@ -296,23 +296,23 @@ def test_location_shortcut():
def test_shortcuts():
- assert language.parse_pathod(
- "400:c'foo'").next().headers[0].key.val == "Content-Type"
- assert language.parse_pathod(
- "400:l'foo'").next().headers[0].key.val == "Location"
+ assert next(language.parse_pathod(
+ "400:c'foo'")).headers[0].key.val == b"Content-Type"
+ assert next(language.parse_pathod(
+ "400:l'foo'")).headers[0].key.val == b"Location"
- assert "Android" in tutils.render(parse_request("get:/:ua"))
- assert "User-Agent" in tutils.render(parse_request("get:/:ua"))
+ assert b"Android" in tutils.render(parse_request("get:/:ua"))
+ assert b"User-Agent" in tutils.render(parse_request("get:/:ua"))
def test_user_agent():
e = http.ShortcutUserAgent.expr()
v = e.parseString("ua")[0]
- assert "Android" in v.string()
+ assert b"Android" in v.string()
e = http.ShortcutUserAgent.expr()
v = e.parseString("u'a'")[0]
- assert "Android" not in v.string()
+ assert b"Android" not in v.string()
v = e.parseString("u@100'")[0]
assert len(str(v.freeze({}).value)) > 100
@@ -324,7 +324,7 @@ def test_user_agent():
def test_nested_response():
e = http.NestedResponse.expr()
v = e.parseString("s'200'")[0]
- assert v.value.val == "200"
+ assert v.value.val == b"200"
tutils.raises(
language.ParseException,
e.parseString,
@@ -340,9 +340,7 @@ def test_nested_response():
def test_nested_response_freeze():
e = http.NestedResponse(
base.TokValueLiteral(
- "200:b'foo':i10,'\\x27'".encode(
- "string_escape"
- )
+ r"200:b\'foo\':i10,\'\\x27\'"
)
)
assert e.freeze({})
diff --git a/test/pathod/test_language_websocket.py b/test/pathod/test_language_websocket.py
index 29155dba..58297141 100644
--- a/test/pathod/test_language_websocket.py
+++ b/test/pathod/test_language_websocket.py
@@ -6,7 +6,7 @@ import tutils
def parse_request(s):
- return language.parse_pathoc(s).next()
+ return next(language.parse_pathoc(s))
class TestWebsocketFrame:
@@ -93,9 +93,9 @@ class TestWebsocketFrame:
def test_rawbody(self):
frm = self.fr("wf:mask:r'foo'")
assert len(frm.payload) == 3
- assert frm.payload != "foo"
+ assert frm.payload != b"foo"
- assert self.fr("wf:r'foo'").payload == "foo"
+ assert self.fr("wf:r'foo'").payload == b"foo"
def test_construction_2(self):
# Simple server frame
@@ -109,7 +109,7 @@ class TestWebsocketFrame:
assert frm.header.masking_key
frm = self.fr("wf:b'foo':k'abcd'", is_client=True)
assert frm.header.mask
- assert frm.header.masking_key == 'abcd'
+ assert frm.header.masking_key == b'abcd'
# Server frame, mask explicitly set
frm = self.fr("wf:b'foo':mask")
@@ -117,7 +117,7 @@ class TestWebsocketFrame:
assert frm.header.masking_key
frm = self.fr("wf:b'foo':k'abcd'")
assert frm.header.mask
- assert frm.header.masking_key == 'abcd'
+ assert frm.header.masking_key == b'abcd'
# Client frame, mask explicitly unset
frm = self.fr("wf:b'foo':-mask", is_client=True)
@@ -128,7 +128,7 @@ class TestWebsocketFrame:
assert not frm.header.mask
# We're reading back a corrupted frame - the first 3 characters of the
# mask is mis-interpreted as the payload
- assert frm.payload == "abc"
+ assert frm.payload == b"abc"
def test_knone(self):
with tutils.raises("expected 4 bytes"):
@@ -138,5 +138,5 @@ class TestWebsocketFrame:
assert self.fr("wf:l3:b'foo'").header.payload_length == 3
frm = self.fr("wf:l2:b'foo'")
assert frm.header.payload_length == 2
- assert frm.payload == "fo"
+ assert frm.payload == b"fo"
tutils.raises("expected 1024 bytes", self.fr, "wf:l1024:b'foo'")
diff --git a/test/pathod/test_pathoc.py b/test/pathod/test_pathoc.py
index 8b69a2a6..dc5011c0 100644
--- a/test/pathod/test_pathoc.py
+++ b/test/pathod/test_pathoc.py
@@ -17,43 +17,26 @@ def test_response():
class PathocTestDaemon(tutils.DaemonTests):
- def tval(
- self,
- requests,
- showreq=False,
- showresp=False,
- explain=False,
- showssl=False,
- hexdump=False,
- timeout=None,
- ignorecodes=(),
- ignoretimeout=None,
- showsummary=True
- ):
+ def tval(self, requests, timeout=None, showssl=False, **kwargs):
s = StringIO()
c = pathoc.Pathoc(
("127.0.0.1", self.d.port),
ssl=self.ssl,
- showreq=showreq,
- showresp=showresp,
- explain=explain,
- hexdump=hexdump,
- ignorecodes=ignorecodes,
- ignoretimeout=ignoretimeout,
- showsummary=showsummary,
- fp=s
+ fp=s,
+ **kwargs
)
with c.connect(showssl=showssl, fp=s):
if timeout:
c.settimeout(timeout)
for i in requests:
r = language.parse_pathoc(i).next()
- if explain:
+ if kwargs.get("explain"):
r = r.freeze(language.Settings())
try:
c.request(r)
except NetlibException:
pass
+ self.d.wait_for_silence()
return s.getvalue()
@@ -66,14 +49,10 @@ class TestDaemonSSL(PathocTestDaemon):
)
def test_sni(self):
- c = pathoc.Pathoc(
- ("127.0.0.1", self.d.port),
- ssl=True,
- sni="foobar.com",
- fp=None
+ self.tval(
+ ["get:/p/200"],
+ sni="foobar.com"
)
- with c.connect():
- c.request("get:/p/200")
log = self.d.log()
assert log[0]["request"]["sni"] == "foobar.com"
@@ -81,15 +60,10 @@ class TestDaemonSSL(PathocTestDaemon):
assert "certificate chain" in self.tval(["get:/p/200"], showssl=True)
def test_clientcert(self):
- c = pathoc.Pathoc(
- ("127.0.0.1", self.d.port),
- ssl=True,
+ self.tval(
+ ["get:/p/200"],
clientcert=tutils.test_data.path("data/clientcert/client.pem"),
- fp=None
)
- with c.connect():
- c.request("get:/p/200")
-
log = self.d.log()
assert log[0]["request"]["clientcert"]["keyinfo"]
@@ -170,9 +144,7 @@ class TestDaemon(PathocTestDaemon):
assert "Invalid server response" in self.tval(["get:'/p/200:d2'"])
def test_websocket_shutdown(self):
- c = pathoc.Pathoc(("127.0.0.1", self.d.port), fp=None)
- with c.connect():
- c.request("ws:/")
+ self.tval(["ws:/"])
def test_wait_finish(self):
c = pathoc.Pathoc(
@@ -182,9 +154,10 @@ class TestDaemon(PathocTestDaemon):
)
with c.connect():
c.request("ws:/")
- c.request("wf:f'wf:x100'")
- [i for i in c.wait(timeout=0, finish=False)]
- [i for i in c.wait(timeout=0)]
+ c.request("wf:f'wf'")
+ # This should read a frame and close the websocket reader
+ assert len([i for i in c.wait(timeout=5, finish=False)]) == 1
+ assert not [i for i in c.wait(timeout=0)]
def test_connect_fail(self):
to = ("foobar", 80)
diff --git a/test/pathod/test_pathod.py b/test/pathod/test_pathod.py
index 0bbad6c2..ed4ef49f 100644
--- a/test/pathod/test_pathod.py
+++ b/test/pathod/test_pathod.py
@@ -145,14 +145,14 @@ class CommonTests(tutils.DaemonTests):
def test_invalid_first_line(self):
c = tcp.TCPClient(("localhost", self.d.port))
- c.connect()
- if self.ssl:
- c.convert_to_ssl()
- c.wfile.write("foo\n\n\n")
- c.wfile.flush()
- l = self.d.last_log()
- assert l["type"] == "error"
- assert "foo" in l["msg"]
+ with c.connect():
+ if self.ssl:
+ c.convert_to_ssl()
+ c.wfile.write("foo\n\n\n")
+ c.wfile.flush()
+ l = self.d.last_log()
+ assert l["type"] == "error"
+ assert "foo" in l["msg"]
def test_invalid_content_length(self):
tutils.raises(
@@ -238,12 +238,12 @@ class TestDaemonSSL(CommonTests):
c = tcp.TCPClient(("localhost", self.d.port))
c.rbufsize = 0
c.wbufsize = 0
- c.connect()
- c.wfile.write("\0\0\0\0")
- tutils.raises(TlsException, c.convert_to_ssl)
- l = self.d.last_log()
- assert l["type"] == "error"
- assert "SSL" in l["msg"]
+ with c.connect():
+ c.wfile.write("\0\0\0\0")
+ tutils.raises(TlsException, c.convert_to_ssl)
+ l = self.d.last_log()
+ assert l["type"] == "error"
+ assert "SSL" in l["msg"]
def test_ssl_cipher(self):
r, _ = self.pathoc([r"get:/p/202"])
diff --git a/test/pathod/tutils.py b/test/pathod/tutils.py
index 56cd2002..bf5e3165 100644
--- a/test/pathod/tutils.py
+++ b/test/pathod/tutils.py
@@ -3,6 +3,7 @@ import re
import shutil
import requests
from six.moves import cStringIO as StringIO
+from six import BytesIO
import urllib
from netlib import tcp
@@ -147,6 +148,6 @@ test_data = utils.Data(__name__)
def render(r, settings=language.Settings()):
r = r.resolve(settings)
- s = StringIO()
+ s = BytesIO()
assert language.serve(r, s, settings)
return s.getvalue()
diff --git a/tox.ini b/tox.ini
index f94dfb49..a1e6a816 100644
--- a/tox.ini
+++ b/tox.ini
@@ -8,7 +8,7 @@ deps = -rrequirements.txt
commands = py.test -n 8 --timeout 60 ./test
[testenv:py35]
-commands = py.test -n 8 --timeout 60 test/netlib test/mitmproxy/script test/pathod/test_utils.py test/pathod/test_log.py test/pathod/test_language_generators.py test/pathod/test_language_writer.py test/pathod/test_language_base.py
+commands = py.test -n 8 --timeout 60 test/netlib test/mitmproxy/script test/pathod/test_utils.py test/pathod/test_log.py test/pathod/test_language_generators.py test/pathod/test_language_writer.py test/pathod/test_language_base.py test/pathod/test_language_http.py
[testenv:lint]
deps = flake8
diff --git a/web/package.json b/web/package.json
index 3bbed9e8..97a5ad3e 100644
--- a/web/package.json
+++ b/web/package.json
@@ -21,8 +21,8 @@
"flux": "^2.1.1",
"jquery": "^2.2.3",
"lodash": "^4.11.2",
- "react": "^15.0.2",
- "react-dom": "^15.0.2",
+ "react": "^15.1.0",
+ "react-dom": "^15.1.0",
"react-redux": "^4.4.5",
"react-router": "^2.4.0",
"redux": "^3.5.2",
@@ -53,7 +53,6 @@
"gulp-sourcemaps": "^1.6.0",
"gulp-util": "^3.0.7",
"jest": "^0.1.40",
- "redux-thunk": "^2.1.0",
"uglifyify": "^3.0.1",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0",
diff --git a/web/src/js/components/flowtable-columns.js b/web/src/js/components/flowtable-columns.js
index dbbe8847..1eae6409 100644
--- a/web/src/js/components/flowtable-columns.js
+++ b/web/src/js/components/flowtable-columns.js
@@ -1,190 +1,121 @@
-import React from "react";
-import {RequestUtils, ResponseUtils} from "../flow/utils.js";
-import {formatSize, formatTimeDelta} from "../utils.js";
-
-var TLSColumn = React.createClass({
- statics: {
- Title: React.createClass({
- render: function(){
- return <th {...this.props} className={"col-tls " + (this.props.className || "") }></th>;
- }
- }),
- sortKeyFun: function(flow){
- return flow.request.scheme;
- }
- },
- render: function () {
- var flow = this.props.flow;
- var ssl = (flow.request.scheme === "https");
- var classes;
- if (ssl) {
- classes = "col-tls col-tls-https";
- } else {
- classes = "col-tls col-tls-http";
- }
- return <td className={classes}></td>;
- }
-});
-
-
-var IconColumn = React.createClass({
- statics: {
- Title: React.createClass({
- render: function(){
- return <th {...this.props} className={"col-icon " + (this.props.className || "") }></th>;
- }
- })
- },
- render: function () {
- var flow = this.props.flow;
-
- var icon;
- if (flow.response) {
- var contentType = ResponseUtils.getContentType(flow.response);
-
- //TODO: We should assign a type to the flow somewhere else.
- if (flow.response.status_code === 304) {
- icon = "resource-icon-not-modified";
- } else if (300 <= flow.response.status_code && flow.response.status_code < 400) {
- icon = "resource-icon-redirect";
- } else if (contentType && contentType.indexOf("image") >= 0) {
- icon = "resource-icon-image";
- } else if (contentType && contentType.indexOf("javascript") >= 0) {
- icon = "resource-icon-js";
- } else if (contentType && contentType.indexOf("css") >= 0) {
- icon = "resource-icon-css";
- } else if (contentType && contentType.indexOf("html") >= 0) {
- icon = "resource-icon-document";
- }
- }
- if (!icon) {
- icon = "resource-icon-plain";
- }
-
-
- icon += " resource-icon";
- return <td className="col-icon">
- <div className={icon}></div>
- </td>;
+import React from "react"
+import {RequestUtils, ResponseUtils} from "../flow/utils.js"
+import {formatSize, formatTimeDelta} from "../utils.js"
+
+
+function TLSColumn({flow}) {
+ let ssl = (flow.request.scheme === "https")
+ let classes
+ if (ssl) {
+ classes = "col-tls col-tls-https"
+ } else {
+ classes = "col-tls col-tls-http"
}
-});
-
-var PathColumn = React.createClass({
- statics: {
- Title: React.createClass({
- render: function(){
- return <th {...this.props} className={"col-path " + (this.props.className || "") }>Path</th>;
- }
- }),
- sortKeyFun: function(flow){
- return RequestUtils.pretty_url(flow.request);
+ return <td className={classes}></td>
+}
+TLSColumn.Title = ({className = "", ...props}) => <th {...props} className={"col-tls " + className }></th>
+TLSColumn.sortKeyFun = flow => flow.request.scheme
+
+
+function IconColumn({flow}) {
+ let icon
+ if (flow.response) {
+ var contentType = ResponseUtils.getContentType(flow.response)
+
+ //TODO: We should assign a type to the flow somewhere else.
+ if (flow.response.status_code === 304) {
+ icon = "resource-icon-not-modified"
+ } else if (300 <= flow.response.status_code && flow.response.status_code < 400) {
+ icon = "resource-icon-redirect"
+ } else if (contentType && contentType.indexOf("image") >= 0) {
+ icon = "resource-icon-image"
+ } else if (contentType && contentType.indexOf("javascript") >= 0) {
+ icon = "resource-icon-js"
+ } else if (contentType && contentType.indexOf("css") >= 0) {
+ icon = "resource-icon-css"
+ } else if (contentType && contentType.indexOf("html") >= 0) {
+ icon = "resource-icon-document"
}
- },
- render: function () {
- var flow = this.props.flow;
- return <td className="col-path">
- {flow.request.is_replay ? <i className="fa fa-fw fa-repeat pull-right"></i> : null}
- {flow.intercepted ? <i className="fa fa-fw fa-pause pull-right"></i> : null}
- { RequestUtils.pretty_url(flow.request) }
- </td>;
}
-});
-
-
-var MethodColumn = React.createClass({
- statics: {
- Title: React.createClass({
- render: function(){
- return <th {...this.props} className={"col-method " + (this.props.className || "") }>Method</th>;
- }
- }),
- sortKeyFun: function(flow){
- return flow.request.method;
- }
- },
- render: function () {
- var flow = this.props.flow;
- return <td className="col-method">{flow.request.method}</td>;
+ if (!icon) {
+ icon = "resource-icon-plain"
}
-});
-
-
-var StatusColumn = React.createClass({
- statics: {
- Title: React.createClass({
- render: function(){
- return <th {...this.props} className={"col-status " + (this.props.className || "") }>Status</th>;
- }
- }),
- sortKeyFun: function(flow){
- return flow.response ? flow.response.status_code : undefined;
- }
- },
- render: function () {
- var flow = this.props.flow;
- var status;
- if (flow.response) {
- status = flow.response.status_code;
- } else {
- status = null;
- }
- return <td className="col-status">{status}</td>;
+
+ icon += " resource-icon"
+ return <td className="col-icon">
+ <div className={icon}></div>
+ </td>
+}
+IconColumn.Title = ({className = "", ...props}) => <th {...props} className={"col-icon " + className }></th>
+
+
+function PathColumn({flow}) {
+ return <td className="col-path">
+ {flow.request.is_replay ? <i className="fa fa-fw fa-repeat pull-right"></i> : null}
+ {flow.intercepted ? <i className="fa fa-fw fa-pause pull-right"></i> : null}
+ { RequestUtils.pretty_url(flow.request) }
+ </td>
+}
+PathColumn.Title = ({className = "", ...props}) =>
+ <th {...props} className={"col-path " + className }>Path</th>
+PathColumn.sortKeyFun = flow => RequestUtils.pretty_url(flow.request)
+
+
+function MethodColumn({flow}) {
+ return <td className="col-method">{flow.request.method}</td>
+}
+MethodColumn.Title = ({className = "", ...props}) =>
+ <th {...props} className={"col-method " + className }>Method</th>
+MethodColumn.sortKeyFun = flow => flow.request.method
+
+
+function StatusColumn({flow}) {
+ let status
+ if (flow.response) {
+ status = flow.response.status_code
+ } else {
+ status = null
}
-});
-
-
-var SizeColumn = React.createClass({
- statics: {
- Title: React.createClass({
- render: function(){
- return <th {...this.props} className={"col-size " + (this.props.className || "") }>Size</th>;
- }
- }),
- sortKeyFun: function(flow){
- var total = flow.request.contentLength;
- if (flow.response) {
- total += flow.response.contentLength || 0;
- }
- return total;
- }
- },
- render: function () {
- var flow = this.props.flow;
+ return <td className="col-status">{status}</td>
- var total = flow.request.contentLength;
- if (flow.response) {
- total += flow.response.contentLength || 0;
- }
- var size = formatSize(total);
- return <td className="col-size">{size}</td>;
+}
+StatusColumn.Title = ({className = "", ...props}) =>
+ <th {...props} className={"col-status " + className }>Status</th>
+StatusColumn.sortKeyFun = flow => flow.response ? flow.response.status_code : undefined
+
+
+function SizeColumn({flow}) {
+ let total = flow.request.contentLength
+ if (flow.response) {
+ total += flow.response.contentLength || 0
}
-});
-
-
-var TimeColumn = React.createClass({
- statics: {
- Title: React.createClass({
- render: function(){
- return <th {...this.props} className={"col-time " + (this.props.className || "") }>Time</th>;
- }
- }),
- sortKeyFun: function(flow){
- if(flow.response) {
- return flow.response.timestamp_end - flow.request.timestamp_start;
- }
- }
- },
- render: function () {
- var flow = this.props.flow;
- var time;
- if (flow.response) {
- time = formatTimeDelta(1000 * (flow.response.timestamp_end - flow.request.timestamp_start));
- } else {
- time = "...";
- }
- return <td className="col-time">{time}</td>;
+ let size = formatSize(total)
+ return <td className="col-size">{size}</td>
+
+}
+SizeColumn.Title = ({className = "", ...props}) =>
+ <th {...props} className={"col-size " + className }>Size</th>
+SizeColumn.sortKeyFun = flow => {
+ let total = flow.request.contentLength
+ if (flow.response) {
+ total += flow.response.contentLength || 0
+ }
+ return total
+}
+
+
+function TimeColumn({flow}) {
+ let time
+ if (flow.response) {
+ time = formatTimeDelta(1000 * (flow.response.timestamp_end - flow.request.timestamp_start))
+ } else {
+ time = "..."
}
-});
+ return <td className="col-time">{time}</td>
+}
+TimeColumn.Title = ({className = "", ...props}) =>
+ <th {...props} className={"col-time " + className }>Time</th>
+TimeColumn.sortKeyFun = flow => flow.response.timestamp_end - flow.request.timestamp_start
var all_columns = [
@@ -195,6 +126,6 @@ var all_columns = [
StatusColumn,
SizeColumn,
TimeColumn
-];
+]
-export default all_columns;
+export default all_columns
diff --git a/web/src/js/components/flowtable.js b/web/src/js/components/flowtable.js
index 0241cd78..d621387c 100644
--- a/web/src/js/components/flowtable.js
+++ b/web/src/js/components/flowtable.js
@@ -19,21 +19,20 @@ FlowRow.propTypes = {
selected: React.PropTypes.bool,
};
-function FlowRow(props) {
- const flow = props.flow;
+function FlowRow({flow, selected, highlight, columns, selectFlow}) {
const className = classNames({
- "selected": props.selected,
- "highlighted": props.highlight && parseFilter(props.highlight)(flow),
+ "selected": selected,
+ "highlighted": highlight && parseFilter(highlight)(flow),
"intercepted": flow.intercepted,
"has-request": flow.request,
"has-response": flow.response,
});
return (
- <tr className={className} onClick={() => props.selectFlow(flow)}>
- {props.columns.map(Column => (
- <Column key={Column.displayName} flow={flow}/>
+ <tr className={className} onClick={() => selectFlow(flow)}>
+ {columns.map(Column => (
+ <Column key={Column.name} flow={flow}/>
))}
</tr>
);
@@ -44,11 +43,8 @@ const FlowRowContainer = connect(
flow: state.flows.all.byId[ownProps.flowId],
highlight: state.flows.highlight,
selected: state.flows.selected.indexOf(ownProps.flowId) >= 0
- }),
- (dispatch, ownProps) => ({
-
})
-)(FlowRow);
+)(FlowRow)
class FlowTableHead extends React.Component {
@@ -59,7 +55,7 @@ class FlowTableHead extends React.Component {
constructor(props, context) {
super(props, context);
- this.state = { sortColumn: undefined, sortDesc: false };
+ this.state = {sortColumn: undefined, sortDesc: false};
}
onClick(Column) {
@@ -69,20 +65,20 @@ class FlowTableHead extends React.Component {
if (Column === this.state.sortColumn) {
sortDesc = !sortDesc;
- this.setState({ sortDesc });
+ this.setState({sortDesc});
} else {
- this.setState({ sortColumn: hasSort && Column, sortDesc: false });
+ this.setState({sortColumn: hasSort && Column, sortDesc: false});
}
let sortKeyFun = Column.sortKeyFun;
if (sortDesc) {
- sortKeyFun = hasSort && function() {
- const k = Column.sortKeyFun.apply(this, arguments);
- if (_.isString(k)) {
- return reverseString("" + k);
- }
- return -k;
- };
+ sortKeyFun = hasSort && function () {
+ const k = Column.sortKeyFun.apply(this, arguments);
+ if (_.isString(k)) {
+ return reverseString("" + k);
+ }
+ return -k;
+ };
}
this.props.setSortKeyFun(sortKeyFun);
@@ -95,9 +91,9 @@ class FlowTableHead extends React.Component {
<tr>
{this.props.columns.map(Column => (
<Column.Title
- key={Column.displayName}
+ key={Column.name}
onClick={() => this.onClick(Column)}
- className={sortColumn === Column && sortType}
+ className={sortColumn === Column ? sortType : undefined}
/>
))}
</tr>
@@ -118,7 +114,7 @@ class FlowTable extends React.Component {
constructor(props, context) {
super(props, context);
- this.state = { vScroll: calcVScroll() };
+ this.state = {vScroll: calcVScroll()};
this.onViewportUpdate = this.onViewportUpdate.bind(this);
}
@@ -132,7 +128,7 @@ class FlowTable extends React.Component {
}
componentWillReceiveProps(nextProps) {
- if(nextProps.selected && nextProps.selected !== this.props.selected){
+ if (nextProps.selected && nextProps.selected !== this.props.selected) {
window.setTimeout(() => this.scrollIntoView(nextProps.selected), 1)
}
}
@@ -154,7 +150,7 @@ class FlowTable extends React.Component {
if (!shallowEqual(this.state.vScroll, vScroll) ||
this.state.viewportTop !== viewportTop) {
- this.setState({ vScroll, viewportTop });
+ this.setState({vScroll, viewportTop});
}
}
@@ -190,22 +186,22 @@ class FlowTable extends React.Component {
<div className="flow-table" onScroll={this.onViewportUpdate}>
<table>
<thead ref="head" style={{ transform }}>
- <FlowTableHead
- columns={flowtable_columns}
- setSortKeyFun={this.props.setSortKeyFun}
- />
+ <FlowTableHead
+ columns={flowtable_columns}
+ setSortKeyFun={this.props.setSortKeyFun}
+ />
</thead>
<tbody>
- <tr style={{ height: vScroll.paddingTop }}></tr>
- {flows.map(flow => (
- <FlowRowContainer
- key={flow.id}
- flowId={flow.id}
- columns={flowtable_columns}
- selectFlow={this.props.selectFlow}
- />
- ))}
- <tr style={{ height: vScroll.paddingBottom }}></tr>
+ <tr style={{ height: vScroll.paddingTop }}></tr>
+ {flows.map(flow => (
+ <FlowRowContainer
+ key={flow.id}
+ flowId={flow.id}
+ columns={flowtable_columns}
+ selectFlow={this.props.selectFlow}
+ />
+ ))}
+ <tr style={{ height: vScroll.paddingBottom }}></tr>
</tbody>
</table>
</div>
@@ -221,8 +217,6 @@ const parseFilter = _.memoize(Filt.parse)
const FlowTableContainer = connect(
state => ({
flows: state.flows.view,
- }),
- dispatch => ({
})
)(FlowTable)
diff --git a/web/src/js/ducks/utils/list.js b/web/src/js/ducks/utils/list.js
index a5ce7250..a830fe99 100644
--- a/web/src/js/ducks/utils/list.js
+++ b/web/src/js/ducks/utils/list.js
@@ -62,8 +62,9 @@ export default function makeList(actionType, fetchURL) {
itemIndex = state.indexOf[action.item.id]
list[itemIndex] = action.item
return {
- ...defaultState,
- list
+ ...state,
+ list,
+ byId: {...state.byId, [action.item.id]: action.item},
}
case REMOVE:
@@ -71,7 +72,7 @@ export default function makeList(actionType, fetchURL) {
itemIndex = state.indexOf[action.item.id]
list.splice(itemIndex, 1)
return {
- ...defaultState,
+ ...state,
list,
byId: {...state.byId, [action.item.id]: undefined},
indexOf: {...state.indexOf, [action.item.id]: undefined},
@@ -79,7 +80,7 @@ export default function makeList(actionType, fetchURL) {
case REQUEST_LIST:
return {
- ...defaultState,
+ ...state,
isFetching: true
}
diff --git a/web/src/js/ducks/utils/view.js b/web/src/js/ducks/utils/view.js
index 5535ed83..fa23efcd 100644
--- a/web/src/js/ducks/utils/view.js
+++ b/web/src/js/ducks/utils/view.js
@@ -43,12 +43,8 @@ const sortedRemove = (list, sortFn, item) => {
}
export function sortedIndexOf(list, value, sortFn) {
- if (sortFn === false){
- let i = 0
- while (i < list.length && list[i].id !== value.id){
- i++
- }
- return i
+ if (!sortFn) {
+ sortFn = x => 0 // This triggers the linear search for flows that have the same sort value.
}
let low = 0,
@@ -57,7 +53,7 @@ export function sortedIndexOf(list, value, sortFn) {
mid;
while (low < high) {
mid = (low + high) >>> 1;
- if ((sortFn(list[mid]) < val) ) {
+ if (sortFn(list[mid]) < val) {
low = mid + 1
} else {
high = mid
@@ -96,10 +92,12 @@ export function updateViewList(currentView, currentList, nextList, action, filte
return sortedInsert(currentView, sortFn, action.item)
if (isInView && !shouldBeInView)
return sortedRemove(currentView, sortFn, action.item)
- if (isInView && shouldBeInView && sortFn && sortFn(currentItemState) !== sortFn(nextItemState)) {
+ if (isInView && shouldBeInView) {
let s = [...currentView]
- s.sort(makeCompareFn(sortFn))
s.indexOf = x => sortedIndexOf(s, x, sortFn)
+ s[s.indexOf(currentItemState)] = nextItemState
+ if (sortFn && sortFn(currentItemState) !== sortFn(nextItemState))
+ s.sort(makeCompareFn(sortFn))
return s
}
return currentView
@@ -123,4 +121,4 @@ export function updateViewFilter(list, filterFn = defaultFilterFn, sortFn = defa
filtered.indexOf = x => sortedIndexOf(filtered, x, sortFn)
return filtered
-} \ No newline at end of file
+}