зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1407347 - Prevent iframes in the inspector sidebar from rerendering when a sidebar tab is removed. r=Honza
This commit is contained in:
Родитель
e80f2e7469
Коммит
49de74e595
|
@ -25,8 +25,10 @@ class InspectorTabPanel extends Component {
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
// Optional prefix for panel IDs.
|
// Optional prefix for panel IDs.
|
||||||
idPrefix: PropTypes.string,
|
idPrefix: PropTypes.string,
|
||||||
// Optional mount callback
|
// Optional mount callback.
|
||||||
onMount: PropTypes.func,
|
onMount: PropTypes.func,
|
||||||
|
// Optional unmount callback.
|
||||||
|
onUnmount: PropTypes.func,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,6 +54,10 @@ class InspectorTabPanel extends Component {
|
||||||
let doc = this.refs.content.ownerDocument;
|
let doc = this.refs.content.ownerDocument;
|
||||||
let panels = doc.getElementById("tabpanels");
|
let panels = doc.getElementById("tabpanels");
|
||||||
|
|
||||||
|
if (this.props.onUnmount) {
|
||||||
|
this.props.onUnmount(this.refs.content, this.props);
|
||||||
|
}
|
||||||
|
|
||||||
// Move panel's content node back into list of tab panels.
|
// Move panel's content node back into list of tab panels.
|
||||||
panels.appendChild(this.refs.content.firstChild);
|
panels.appendChild(this.refs.content.firstChild);
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,6 +137,7 @@ ToolSidebar.prototype = {
|
||||||
title: title,
|
title: title,
|
||||||
url: url,
|
url: url,
|
||||||
onMount: this.onSidePanelMounted.bind(this),
|
onMount: this.onSidePanelMounted.bind(this),
|
||||||
|
onUnmount: this.onSidePanelUnmounted.bind(this),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.addTab(id, title, panel, selected, index);
|
this.addTab(id, title, panel, selected, index);
|
||||||
|
@ -163,6 +164,20 @@ ToolSidebar.prototype = {
|
||||||
iframe.setAttribute("src", props.url);
|
iframe.setAttribute("src", props.url);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onSidePanelUnmounted: function (content, props) {
|
||||||
|
let iframe = content.querySelector("iframe");
|
||||||
|
if (!iframe || !iframe.hasAttribute("src")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let win = iframe.contentWindow;
|
||||||
|
if ("destroy" in win) {
|
||||||
|
win.destroy(this._toolPanel, iframe);
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe.removeAttribute("src");
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove an existing tab.
|
* Remove an existing tab.
|
||||||
* @param {String} tabId The ID of the tab that was used to register it, or
|
* @param {String} tabId The ID of the tab that was used to register it, or
|
||||||
|
|
|
@ -61,6 +61,7 @@ define(function (require, exports, module) {
|
||||||
tabActive: this.state.tabActive,
|
tabActive: this.state.tabActive,
|
||||||
onAfterChange: this.onTabChanged},
|
onAfterChange: this.onTabChanged},
|
||||||
TabPanel({
|
TabPanel({
|
||||||
|
id: "json",
|
||||||
className: "json",
|
className: "json",
|
||||||
title: JSONView.Locale.$STR("jsonViewer.tab.JSON")},
|
title: JSONView.Locale.$STR("jsonViewer.tab.JSON")},
|
||||||
JsonPanel({
|
JsonPanel({
|
||||||
|
@ -71,6 +72,7 @@ define(function (require, exports, module) {
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
TabPanel({
|
TabPanel({
|
||||||
|
id: "rawdata",
|
||||||
className: "rawdata",
|
className: "rawdata",
|
||||||
title: JSONView.Locale.$STR("jsonViewer.tab.RawData")},
|
title: JSONView.Locale.$STR("jsonViewer.tab.RawData")},
|
||||||
TextPanel({
|
TextPanel({
|
||||||
|
@ -81,6 +83,7 @@ define(function (require, exports, module) {
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
TabPanel({
|
TabPanel({
|
||||||
|
id: "headers",
|
||||||
className: "headers",
|
className: "headers",
|
||||||
title: JSONView.Locale.$STR("jsonViewer.tab.Headers")},
|
title: JSONView.Locale.$STR("jsonViewer.tab.Headers")},
|
||||||
HeadersPanel({
|
HeadersPanel({
|
||||||
|
|
|
@ -72,14 +72,15 @@ define(function (require, exports, module) {
|
||||||
this.state = {
|
this.state = {
|
||||||
tabActive: props.tabActive,
|
tabActive: props.tabActive,
|
||||||
|
|
||||||
// This array is used to store an information whether a tab
|
// This array is used to store an object containing information on whether a tab
|
||||||
// at specific index has already been created (e.g. selected
|
// at a specified index has already been created (e.g. selected at least once) and
|
||||||
// at least once).
|
// the tab id. An example of the object structure is the following:
|
||||||
// If yes, it's rendered even if not currently selected.
|
// [{ isCreated: true, tabId: "ruleview" }, { isCreated: false, tabId: "foo" }].
|
||||||
// This is because in some cases we don't want to re-create
|
// If the tab at the specified index has already been created, it's rendered even
|
||||||
// tab content when it's being unselected/selected.
|
// if not currently selected. This is because in some cases we don't want
|
||||||
// E.g. in case of an iframe being used as a tab-content
|
// to re-create tab content when it's being unselected/selected.
|
||||||
// we want the iframe to stay in the DOM.
|
// E.g. in case of an iframe being used as a tab-content we want the iframe to
|
||||||
|
// stay in the DOM.
|
||||||
created: [],
|
created: [],
|
||||||
|
|
||||||
// True if tabs can't fit into available horizontal space.
|
// True if tabs can't fit into available horizontal space.
|
||||||
|
@ -116,24 +117,46 @@ define(function (require, exports, module) {
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
let { children, tabActive } = nextProps;
|
let { children, tabActive } = nextProps;
|
||||||
|
let panels = children.filter(panel => panel);
|
||||||
|
let created = [...this.state.created];
|
||||||
|
|
||||||
// Check type of 'tabActive' props to see if it's valid
|
// If the children props has changed due to an addition or removal of a tab,
|
||||||
// (it's 0-based index).
|
// update the state's created array with the latest tab ids and whether or not
|
||||||
|
// the tab is already created.
|
||||||
|
if (this.state.created.length != panels.length) {
|
||||||
|
created = panels.map(panel => {
|
||||||
|
// Get whether or not the tab has already been created from the previous state.
|
||||||
|
let createdEntry = this.state.created.find(entry => {
|
||||||
|
return entry && entry.tabId === panel.props.id;
|
||||||
|
});
|
||||||
|
let isCreated = !!createdEntry && createdEntry.isCreated;
|
||||||
|
let tabId = panel.props.id;
|
||||||
|
|
||||||
|
return {
|
||||||
|
isCreated,
|
||||||
|
tabId,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check type of 'tabActive' props to see if it's valid (it's 0-based index).
|
||||||
if (typeof tabActive === "number") {
|
if (typeof tabActive === "number") {
|
||||||
let panels = children.filter((panel) => panel);
|
|
||||||
|
|
||||||
// Reset to index 0 if index overflows the range of panel array
|
// Reset to index 0 if index overflows the range of panel array
|
||||||
tabActive = (tabActive < panels.length && tabActive >= 0) ?
|
tabActive = (tabActive < panels.length && tabActive >= 0) ?
|
||||||
tabActive : 0;
|
tabActive : 0;
|
||||||
|
|
||||||
let created = [...this.state.created];
|
created[tabActive] = Object.assign({}, created[tabActive], {
|
||||||
created[tabActive] = true;
|
isCreated: true,
|
||||||
|
});
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
created,
|
|
||||||
tabActive,
|
tabActive,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
created,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -209,11 +232,13 @@ define(function (require, exports, module) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let created = [...this.state.created];
|
let created = [...this.state.created];
|
||||||
created[index] = true;
|
created[index] = Object.assign({}, created[index], {
|
||||||
|
isCreated: true,
|
||||||
|
});
|
||||||
|
|
||||||
let newState = Object.assign({}, this.state, {
|
let newState = Object.assign({}, this.state, {
|
||||||
|
created,
|
||||||
tabActive: index,
|
tabActive: index,
|
||||||
created: created
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setState(newState, () => {
|
this.setState(newState, () => {
|
||||||
|
@ -335,6 +360,8 @@ define(function (require, exports, module) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let id = tab.props.id;
|
let id = tab.props.id;
|
||||||
|
let isCreated = this.state.created[index] &&
|
||||||
|
this.state.created[index].isCreated;
|
||||||
|
|
||||||
// Use 'visibility:hidden' + 'height:0' for hiding content of non-selected
|
// Use 'visibility:hidden' + 'height:0' for hiding content of non-selected
|
||||||
// tab. It's faster than 'display:none' because it avoids triggering frame
|
// tab. It's faster than 'display:none' because it avoids triggering frame
|
||||||
|
@ -354,13 +381,13 @@ define(function (require, exports, module) {
|
||||||
return (
|
return (
|
||||||
dom.div({
|
dom.div({
|
||||||
id: id ? id + "-panel" : "panel-" + index,
|
id: id ? id + "-panel" : "panel-" + index,
|
||||||
key: index,
|
key: id,
|
||||||
style: style,
|
style: style,
|
||||||
className: selected ? "tab-panel-box" : "tab-panel-box hidden",
|
className: selected ? "tab-panel-box" : "tab-panel-box hidden",
|
||||||
role: "tabpanel",
|
role: "tabpanel",
|
||||||
"aria-labelledby": id ? id + "-tab" : "tab-" + index,
|
"aria-labelledby": id ? id + "-tab" : "tab-" + index,
|
||||||
},
|
},
|
||||||
(selected || this.state.created[index]) ? panel : null
|
(selected || isCreated) ? panel : null
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -388,6 +415,8 @@ define(function (require, exports, module) {
|
||||||
class Panel extends Component {
|
class Panel extends Component {
|
||||||
static get propTypes() {
|
static get propTypes() {
|
||||||
return {
|
return {
|
||||||
|
id: PropTypes.string.isRequired,
|
||||||
|
className: PropTypes.string,
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
children: PropTypes.oneOfType([
|
children: PropTypes.oneOfType([
|
||||||
PropTypes.array,
|
PropTypes.array,
|
||||||
|
|
|
@ -98,6 +98,7 @@ class NetInfoBody extends Component {
|
||||||
// Headers tab
|
// Headers tab
|
||||||
panels.push(
|
panels.push(
|
||||||
TabPanel({
|
TabPanel({
|
||||||
|
id: "headers",
|
||||||
className: "headers",
|
className: "headers",
|
||||||
key: "headers",
|
key: "headers",
|
||||||
title: Locale.$STR("netRequest.headers")},
|
title: Locale.$STR("netRequest.headers")},
|
||||||
|
@ -109,6 +110,7 @@ class NetInfoBody extends Component {
|
||||||
if (hasParams) {
|
if (hasParams) {
|
||||||
panels.push(
|
panels.push(
|
||||||
TabPanel({
|
TabPanel({
|
||||||
|
id: "params",
|
||||||
className: "params",
|
className: "params",
|
||||||
key: "params",
|
key: "params",
|
||||||
title: Locale.$STR("netRequest.params")},
|
title: Locale.$STR("netRequest.params")},
|
||||||
|
@ -121,6 +123,7 @@ class NetInfoBody extends Component {
|
||||||
if (hasPostData) {
|
if (hasPostData) {
|
||||||
panels.push(
|
panels.push(
|
||||||
TabPanel({
|
TabPanel({
|
||||||
|
id: "post",
|
||||||
className: "post",
|
className: "post",
|
||||||
key: "post",
|
key: "post",
|
||||||
title: Locale.$STR("netRequest.post")},
|
title: Locale.$STR("netRequest.post")},
|
||||||
|
@ -131,7 +134,10 @@ class NetInfoBody extends Component {
|
||||||
|
|
||||||
// Response tab
|
// Response tab
|
||||||
panels.push(
|
panels.push(
|
||||||
TabPanel({className: "response", key: "response",
|
TabPanel({
|
||||||
|
id: "response",
|
||||||
|
className: "response",
|
||||||
|
key: "response",
|
||||||
title: Locale.$STR("netRequest.response")},
|
title: Locale.$STR("netRequest.response")},
|
||||||
ResponseTab({data: data, actions: actions})
|
ResponseTab({data: data, actions: actions})
|
||||||
)
|
)
|
||||||
|
@ -141,6 +147,7 @@ class NetInfoBody extends Component {
|
||||||
if (this.hasCookies()) {
|
if (this.hasCookies()) {
|
||||||
panels.push(
|
panels.push(
|
||||||
TabPanel({
|
TabPanel({
|
||||||
|
id: "cookies",
|
||||||
className: "cookies",
|
className: "cookies",
|
||||||
key: "cookies",
|
key: "cookies",
|
||||||
title: Locale.$STR("netRequest.cookies")},
|
title: Locale.$STR("netRequest.cookies")},
|
||||||
|
@ -156,6 +163,7 @@ class NetInfoBody extends Component {
|
||||||
if (this.hasStackTrace()) {
|
if (this.hasStackTrace()) {
|
||||||
panels.push(
|
panels.push(
|
||||||
TabPanel({
|
TabPanel({
|
||||||
|
id: "stacktrace-tab",
|
||||||
className: "stacktrace-tab",
|
className: "stacktrace-tab",
|
||||||
key: "stacktrace",
|
key: "stacktrace",
|
||||||
title: Locale.$STR("netRequest.callstack")},
|
title: Locale.$STR("netRequest.callstack")},
|
||||||
|
|
Загрузка…
Ссылка в новой задаче