diff --git a/browser/components/extensions/ext-menus.js b/browser/components/extensions/ext-menus.js index 098cd4180e84..e32272159be9 100644 --- a/browser/components/extensions/ext-menus.js +++ b/browser/components/extensions/ext-menus.js @@ -238,7 +238,7 @@ var gMenuBuilder = { item.tabManager.addActiveTabPermission(); - let tab = item.tabManager.convert(contextData.tab); + let tab = contextData.tab && item.tabManager.convert(contextData.tab); let info = item.getClickInfo(contextData, wasChecked); const map = {shiftKey: "Shift", altKey: "Alt", metaKey: "Command", ctrlKey: "Ctrl"}; @@ -673,7 +673,8 @@ this.menusInternal = class extends ExtensionAPI { onClicked: new EventManager(context, "menusInternal.onClicked", fire => { let listener = (event, info, tab) => { - context.withPendingBrowser(tab.linkedBrowser, + let {linkedBrowser} = tab || tabTracker.activeTab; + context.withPendingBrowser(linkedBrowser, () => fire.sync(info, tab)); }; diff --git a/browser/components/extensions/test/browser/browser_ext_sidebarAction_contextMenu.js b/browser/components/extensions/test/browser/browser_ext_sidebarAction_contextMenu.js index 5ad4af628290..5a6236327a24 100644 --- a/browser/components/extensions/test/browser/browser_ext_sidebarAction_contextMenu.js +++ b/browser/components/extensions/test/browser/browser_ext_sidebarAction_contextMenu.js @@ -36,6 +36,9 @@ let extData = { id: "clickme-page", title: "Click me!", contexts: ["all"], + onclick(info, tab) { + browser.test.sendMessage("menu-click", tab); + }, }); }, }; @@ -59,7 +62,11 @@ add_task(async function sidebar_contextmenu() { let contentAreaContextMenu = await openContextMenuInSidebar(); let item = contentAreaContextMenu.getElementsByAttribute("label", "Click me!"); is(item.length, 1, "contextMenu item for page was found"); + + item[0].click(); await closeContextMenu(contentAreaContextMenu); + let tab = await extension.awaitMessage("menu-click"); + is(tab, null, "tab argument is optional, and missing in clicks from sidebars"); await extension.unload(); }); diff --git a/browser/extensions/formautofill/ProfileStorage.jsm b/browser/extensions/formautofill/ProfileStorage.jsm index 289fcce94b7b..eef53d685ecd 100644 --- a/browser/extensions/formautofill/ProfileStorage.jsm +++ b/browser/extensions/formautofill/ProfileStorage.jsm @@ -313,10 +313,12 @@ class AutofillRecords { add(record, {sourceSync = false} = {}) { this.log.debug("add:", record); + let recordToSave = this._cloneAndCleanUp(record); + if (sourceSync) { // Remove tombstones for incoming items that were changed on another // device. Local deletions always lose to avoid data loss. - let index = this._findIndexByGUID(record.guid, { + let index = this._findIndexByGUID(recordToSave.guid, { includeDeleted: true, }); if (index > -1) { @@ -324,31 +326,24 @@ class AutofillRecords { if (existing.deleted) { this.data.splice(index, 1); } else { - throw new Error(`Record ${record.guid} already exists`); + throw new Error(`Record ${recordToSave.guid} already exists`); } } - let recordToSave = this._clone(record); - return this._saveRecord(recordToSave, {sourceSync}); + } else if (!recordToSave.deleted) { + this._normalizeRecord(recordToSave); + + recordToSave.guid = this._generateGUID(); + recordToSave.version = this.version; + + // Metadata + let now = Date.now(); + recordToSave.timeCreated = now; + recordToSave.timeLastModified = now; + recordToSave.timeLastUsed = 0; + recordToSave.timesUsed = 0; } - if (record.deleted) { - return this._saveRecord(record); - } - - let recordToSave = this._clone(record); - this._normalizeRecord(recordToSave); - - recordToSave.guid = this._generateGUID(); - recordToSave.version = this.version; - - // Metadata - let now = Date.now(); - recordToSave.timeCreated = now; - recordToSave.timeLastModified = now; - recordToSave.timeLastUsed = 0; - recordToSave.timesUsed = 0; - - return this._saveRecord(recordToSave); + return this._saveRecord(recordToSave, {sourceSync}); } _saveRecord(record, {sourceSync = false} = {}) { @@ -428,7 +423,7 @@ class AutofillRecords { newValue = oldValue; } - if (!newValue) { + if (newValue === undefined || newValue === "") { delete recordFound[field]; } else { recordFound[field] = newValue; diff --git a/browser/extensions/formautofill/test/unit/test_addressRecords.js b/browser/extensions/formautofill/test/unit/test_addressRecords.js index 9132e6182f59..8eeb619cca71 100644 --- a/browser/extensions/formautofill/test/unit/test_addressRecords.js +++ b/browser/extensions/formautofill/test/unit/test_addressRecords.js @@ -39,7 +39,7 @@ const TEST_ADDRESS_4 = { organization: "World Wide Web Consortium", }; -const TEST_ADDRESS_FOR_UPDATE = { +const TEST_ADDRESS_WITH_EMPTY_FIELD = { "name": "Tim Berners", "street-address": "", }; @@ -299,6 +299,12 @@ add_task(async function test_add() { do_check_eq(addresses[0].timeLastUsed, 0); do_check_eq(addresses[0].timesUsed, 0); + // Empty string should be deleted before saving. + profileStorage.addresses.add(TEST_ADDRESS_WITH_EMPTY_FIELD); + let address = profileStorage.addresses.data[2]; + do_check_eq(address.name, TEST_ADDRESS_WITH_EMPTY_FIELD.name); + do_check_eq(address["street-address"], undefined); + Assert.throws(() => profileStorage.addresses.add(TEST_ADDRESS_WITH_INVALID_FIELD), /"invalidField" is not a valid field\./); }); @@ -333,7 +339,7 @@ add_task(async function test_update() { do_check_eq(getSyncChangeCounter(profileStorage.addresses, guid), 1); // Test preserveOldProperties parameter and field with empty string. - profileStorage.addresses.update(guid, TEST_ADDRESS_FOR_UPDATE, true); + profileStorage.addresses.update(guid, TEST_ADDRESS_WITH_EMPTY_FIELD, true); await onChanged; await profileStorage._saveImmediately(); @@ -348,6 +354,12 @@ add_task(async function test_update() { do_check_neq(address.timeLastModified, timeLastModified); do_check_eq(getSyncChangeCounter(profileStorage.addresses, guid), 2); + // Empty string should be deleted while updating. + profileStorage.addresses.update(profileStorage.addresses.data[0].guid, TEST_ADDRESS_WITH_EMPTY_FIELD); + address = profileStorage.addresses.data[0]; + do_check_eq(address.name, TEST_ADDRESS_WITH_EMPTY_FIELD.name); + do_check_eq(address["street-address"], undefined); + Assert.throws( () => profileStorage.addresses.update("INVALID_GUID", TEST_ADDRESS_3), /No matching record\./ diff --git a/browser/extensions/formautofill/test/unit/test_creditCardRecords.js b/browser/extensions/formautofill/test/unit/test_creditCardRecords.js index ffe0b5407776..98f1c55377af 100644 --- a/browser/extensions/formautofill/test/unit/test_creditCardRecords.js +++ b/browser/extensions/formautofill/test/unit/test_creditCardRecords.js @@ -28,6 +28,12 @@ const TEST_CREDIT_CARD_3 = { "cc-exp-year": 2000, }; +const TEST_CREDIT_CARD_WITH_EMPTY_FIELD = { + "cc-name": "", + "cc-number": "1234123412341234", + "cc-exp-month": 1, +}; + const TEST_CREDIT_CARD_WITH_2_DIGITS_YEAR = { "cc-number": "1234123412341234", "cc-exp-month": 1, @@ -170,6 +176,12 @@ add_task(async function test_add() { do_check_eq(creditCards[0].timeLastUsed, 0); do_check_eq(creditCards[0].timesUsed, 0); + // Empty string should be deleted before saving. + profileStorage.creditCards.add(TEST_CREDIT_CARD_WITH_EMPTY_FIELD); + let creditCard = profileStorage.creditCards.data[2]; + do_check_eq(creditCard["cc-exp-month"], TEST_CREDIT_CARD_WITH_EMPTY_FIELD["cc-exp-month"]); + do_check_eq(creditCard["cc-name"], undefined); + Assert.throws(() => profileStorage.creditCards.add(TEST_CREDIT_CARD_WITH_INVALID_FIELD), /"invalidField" is not a valid field\./); }); @@ -202,6 +214,12 @@ add_task(async function test_update() { do_check_neq(creditCard.timeLastModified, timeLastModified); do_check_credit_card_matches(creditCard, TEST_CREDIT_CARD_3); + // Empty string should be deleted while updating. + profileStorage.creditCards.update(profileStorage.creditCards.data[0].guid, TEST_CREDIT_CARD_WITH_EMPTY_FIELD); + creditCard = profileStorage.creditCards.data[0]; + do_check_eq(creditCard["cc-exp-month"], TEST_CREDIT_CARD_WITH_EMPTY_FIELD["cc-exp-month"]); + do_check_eq(creditCard["cc-name"], undefined); + Assert.throws( () => profileStorage.creditCards.update("INVALID_GUID", TEST_CREDIT_CARD_3), /No matching record\./ diff --git a/devtools/client/aboutdebugging/components/Aboutdebugging.js b/devtools/client/aboutdebugging/components/Aboutdebugging.js index 02181de42359..46785476ec4d 100644 --- a/devtools/client/aboutdebugging/components/Aboutdebugging.js +++ b/devtools/client/aboutdebugging/components/Aboutdebugging.js @@ -6,7 +6,7 @@ "use strict"; -const { createFactory, createClass, DOM: dom, PropTypes } = +const { createFactory, Component, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); const Services = require("Services"); @@ -46,41 +46,46 @@ const panels = [{ const defaultPanelId = "addons"; -module.exports = createClass({ - displayName: "AboutDebuggingApp", - - propTypes: { - client: PropTypes.instanceOf(DebuggerClient).isRequired, - telemetry: PropTypes.instanceOf(Telemetry).isRequired - }, - - getInitialState() { +class AboutDebuggingApp extends Component { + static get propTypes() { return { + client: PropTypes.instanceOf(DebuggerClient).isRequired, + telemetry: PropTypes.instanceOf(Telemetry).isRequired + }; + } + + constructor(props) { + super(props); + + this.state = { selectedPanelId: defaultPanelId }; - }, + + this.onHashChange = this.onHashChange.bind(this); + this.selectPanel = this.selectPanel.bind(this); + } componentDidMount() { window.addEventListener("hashchange", this.onHashChange); this.onHashChange(); this.props.telemetry.toolOpened("aboutdebugging"); - }, + } componentWillUnmount() { window.removeEventListener("hashchange", this.onHashChange); this.props.telemetry.toolClosed("aboutdebugging"); this.props.telemetry.destroy(); - }, + } onHashChange() { this.setState({ selectedPanelId: window.location.hash.substr(1) || defaultPanelId }); - }, + } selectPanel(panelId) { window.location.hash = "#" + panelId; - }, + } render() { let { client } = this.props; @@ -108,4 +113,6 @@ module.exports = createClass({ dom.div({ className: "main-content" }, panel) ); } -}); +} + +module.exports = AboutDebuggingApp; diff --git a/devtools/client/aboutdebugging/components/PanelHeader.js b/devtools/client/aboutdebugging/components/PanelHeader.js index 5629018f74ce..2e9a5c86be1f 100644 --- a/devtools/client/aboutdebugging/components/PanelHeader.js +++ b/devtools/client/aboutdebugging/components/PanelHeader.js @@ -4,21 +4,23 @@ "use strict"; -const { createClass, DOM: dom, PropTypes } = +const { Component, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); -module.exports = createClass({ - displayName: "PanelHeader", - - propTypes: { - id: PropTypes.string.isRequired, - name: PropTypes.string.isRequired - }, +class PanelHeader extends Component { + static get propTypes() { + return { + id: PropTypes.string.isRequired, + name: PropTypes.string.isRequired + }; + } render() { let { name, id } = this.props; return dom.div({ className: "header" }, dom.h1({ id, className: "header-name" }, name)); - }, -}); + } +} + +module.exports = PanelHeader; diff --git a/devtools/client/aboutdebugging/components/PanelMenu.js b/devtools/client/aboutdebugging/components/PanelMenu.js index ae7f8b699c48..b5bd5e24b7d0 100644 --- a/devtools/client/aboutdebugging/components/PanelMenu.js +++ b/devtools/client/aboutdebugging/components/PanelMenu.js @@ -4,23 +4,23 @@ "use strict"; -const { createClass, createFactory, DOM: dom, PropTypes } = +const { Component, createFactory, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); const PanelMenuEntry = createFactory(require("./PanelMenuEntry")); -module.exports = createClass({ - displayName: "PanelMenu", - - propTypes: { - panels: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.string.isRequired, - name: PropTypes.string.isRequired, - icon: PropTypes.string.isRequired, - component: PropTypes.func.isRequired - })).isRequired, - selectPanel: PropTypes.func.isRequired, - selectedPanelId: PropTypes.string - }, +class PanelMenu extends Component { + static get propTypes() { + return { + panels: PropTypes.arrayOf(PropTypes.shape({ + id: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, + icon: PropTypes.string.isRequired, + component: PropTypes.func.isRequired + })).isRequired, + selectPanel: PropTypes.func.isRequired, + selectedPanelId: PropTypes.string + }; + } render() { let { panels, selectedPanelId, selectPanel } = this.props; @@ -37,5 +37,7 @@ module.exports = createClass({ // "categories" id used for styling purposes return dom.div({ id: "categories", role: "tablist" }, panelLinks); - }, -}); + } +} + +module.exports = PanelMenu; diff --git a/devtools/client/aboutdebugging/components/PanelMenuEntry.js b/devtools/client/aboutdebugging/components/PanelMenuEntry.js index 809a98eeb8fe..41b62495bc0e 100644 --- a/devtools/client/aboutdebugging/components/PanelMenuEntry.js +++ b/devtools/client/aboutdebugging/components/PanelMenuEntry.js @@ -4,23 +4,28 @@ "use strict"; -const { createClass, DOM: dom, PropTypes } = +const { Component, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); -module.exports = createClass({ - displayName: "PanelMenuEntry", +class PanelMenuEntry extends Component { + static get propTypes() { + return { + icon: PropTypes.string.isRequired, + id: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, + selected: PropTypes.bool, + selectPanel: PropTypes.func.isRequired + }; + } - propTypes: { - icon: PropTypes.string.isRequired, - id: PropTypes.string.isRequired, - name: PropTypes.string.isRequired, - selected: PropTypes.bool, - selectPanel: PropTypes.func.isRequired - }, + constructor(props) { + super(props); + this.onClick = this.onClick.bind(this); + } onClick() { this.props.selectPanel(this.props.id); - }, + } render() { let { id, name, icon, selected } = this.props; @@ -38,4 +43,6 @@ module.exports = createClass({ dom.img({ className: "category-icon", src: icon, role: "presentation" }), dom.div({ className: "category-name" }, name)); } -}); +} + +module.exports = PanelMenuEntry; diff --git a/devtools/client/aboutdebugging/components/TargetList.js b/devtools/client/aboutdebugging/components/TargetList.js index e7e910e499f9..3a3a47bd1a1c 100644 --- a/devtools/client/aboutdebugging/components/TargetList.js +++ b/devtools/client/aboutdebugging/components/TargetList.js @@ -4,7 +4,7 @@ "use strict"; -const { createClass, DOM: dom, PropTypes } = +const { Component, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); const Services = require("Services"); @@ -18,19 +18,19 @@ const LocaleCompare = (a, b) => { return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); }; -module.exports = createClass({ - displayName: "TargetList", - - propTypes: { - client: PropTypes.instanceOf(DebuggerClient).isRequired, - debugDisabled: PropTypes.bool, - error: PropTypes.node, - id: PropTypes.string.isRequired, - name: PropTypes.string, - sort: PropTypes.bool, - targetClass: PropTypes.func.isRequired, - targets: PropTypes.arrayOf(PropTypes.object).isRequired - }, +class TargetList extends Component { + static get propTypes() { + return { + client: PropTypes.instanceOf(DebuggerClient).isRequired, + debugDisabled: PropTypes.bool, + error: PropTypes.node, + id: PropTypes.string.isRequired, + name: PropTypes.string, + sort: PropTypes.bool, + targetClass: PropTypes.func.isRequired, + targets: PropTypes.arrayOf(PropTypes.object).isRequired + }; + } render() { let { client, debugDisabled, error, targetClass, targets, sort } = this.props; @@ -52,5 +52,7 @@ module.exports = createClass({ return dom.div({ id: this.props.id, className: "targets" }, dom.h2(null, this.props.name), content); - }, -}); + } +} + +module.exports = TargetList; diff --git a/devtools/client/aboutdebugging/components/addons/Controls.js b/devtools/client/aboutdebugging/components/addons/Controls.js index 1015f5cb409e..8b4e490bc3dd 100644 --- a/devtools/client/aboutdebugging/components/addons/Controls.js +++ b/devtools/client/aboutdebugging/components/addons/Controls.js @@ -11,7 +11,7 @@ loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm"); const { Cc, Ci } = require("chrome"); -const { createFactory, createClass, DOM: dom, PropTypes } = +const { createFactory, Component, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); const Services = require("Services"); const AddonsInstallError = createFactory(require("./InstallError")); @@ -22,24 +22,31 @@ const Strings = Services.strings.createBundle( const MORE_INFO_URL = "https://developer.mozilla.org/docs/Tools" + "/about:debugging#Enabling_add-on_debugging"; -module.exports = createClass({ - displayName: "AddonsControls", - - propTypes: { - debugDisabled: PropTypes.bool - }, - - getInitialState() { +class AddonsControls extends Component { + static get propTypes() { return { + debugDisabled: PropTypes.bool + }; + } + + constructor(props) { + super(props); + + this.state = { installError: null, }; - }, + + this.onEnableAddonDebuggingChange = this.onEnableAddonDebuggingChange.bind(this); + this.loadAddonFromFile = this.loadAddonFromFile.bind(this); + this.retryInstall = this.retryInstall.bind(this); + this.installAddon = this.installAddon.bind(this); + } onEnableAddonDebuggingChange(event) { let enabled = event.target.checked; Services.prefs.setBoolPref("devtools.chrome.enabled", enabled); Services.prefs.setBoolPref("devtools.debugger.remote-enabled", enabled); - }, + } loadAddonFromFile() { this.setState({ installError: null }); @@ -60,12 +67,12 @@ module.exports = createClass({ this.installAddon(file); }); - }, + } retryInstall() { this.setState({ installError: null }); this.installAddon(this.state.lastInstallErrorFile); - }, + } installAddon(file) { AddonManager.installTemporaryAddon(file) @@ -76,7 +83,7 @@ module.exports = createClass({ console.error(e); this.setState({ installError: e.message, lastInstallErrorFile: file }); }); - }, + } render() { let { debugDisabled } = this.props; @@ -110,4 +117,6 @@ module.exports = createClass({ retryInstall: this.retryInstall, })); } -}); +} + +module.exports = AddonsControls; diff --git a/devtools/client/aboutdebugging/components/addons/InstallError.js b/devtools/client/aboutdebugging/components/addons/InstallError.js index 8d446dade458..0c37a9cf3df3 100644 --- a/devtools/client/aboutdebugging/components/addons/InstallError.js +++ b/devtools/client/aboutdebugging/components/addons/InstallError.js @@ -5,20 +5,20 @@ /* eslint-env browser */ "use strict"; -const { createClass, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); +const { Component, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); const Services = require("Services"); const Strings = Services.strings.createBundle( "chrome://devtools/locale/aboutdebugging.properties"); -module.exports = createClass({ - displayName: "AddonsInstallError", - - propTypes: { - error: PropTypes.string, - retryInstall: PropTypes.func, - }, +class AddonsInstallError extends Component { + static get propTypes() { + return { + error: PropTypes.string, + retryInstall: PropTypes.func, + }; + } render() { if (!this.props.error) { @@ -36,4 +36,6 @@ module.exports = createClass({ { className: "addons-install-retry", onClick: this.props.retryInstall }, Strings.GetStringFromName("retryTemporaryInstall"))); } -}); +} + +module.exports = AddonsInstallError; diff --git a/devtools/client/aboutdebugging/components/addons/Panel.js b/devtools/client/aboutdebugging/components/addons/Panel.js index 9e647b436a79..5658968b278d 100644 --- a/devtools/client/aboutdebugging/components/addons/Panel.js +++ b/devtools/client/aboutdebugging/components/addons/Panel.js @@ -6,7 +6,7 @@ const { AddonManager } = require("resource://gre/modules/AddonManager.jsm"); const { Management } = require("resource://gre/modules/Extension.jsm"); -const { createFactory, createClass, DOM: dom, PropTypes } = +const { createFactory, Component, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); const Services = require("Services"); @@ -27,20 +27,29 @@ const REMOTE_ENABLED_PREF = "devtools.debugger.remote-enabled"; const WEB_EXT_URL = "https://developer.mozilla.org/Add-ons" + "/WebExtensions/Getting_started_with_web-ext"; -module.exports = createClass({ - displayName: "AddonsPanel", - - propTypes: { - client: PropTypes.instanceOf(DebuggerClient).isRequired, - id: PropTypes.string.isRequired - }, - - getInitialState() { +class AddonsPanel extends Component { + static get propTypes() { return { + client: PropTypes.instanceOf(DebuggerClient).isRequired, + id: PropTypes.string.isRequired + }; + } + + constructor(props) { + super(props); + + this.state = { extensions: [], debugDisabled: false, }; - }, + + this.updateDebugStatus = this.updateDebugStatus.bind(this); + this.updateAddonsList = this.updateAddonsList.bind(this); + this.onInstalled = this.onInstalled.bind(this); + this.onUninstalled = this.onUninstalled.bind(this); + this.onEnabled = this.onEnabled.bind(this); + this.onDisabled = this.onDisabled.bind(this); + } componentDidMount() { AddonManager.addAddonListener(this); @@ -55,7 +64,7 @@ module.exports = createClass({ this.updateDebugStatus(); this.updateAddonsList(); - }, + } componentWillUnmount() { AddonManager.removeAddonListener(this); @@ -65,7 +74,7 @@ module.exports = createClass({ this.updateDebugStatus); Services.prefs.removeObserver(REMOTE_ENABLED_PREF, this.updateDebugStatus); - }, + } updateDebugStatus() { let debugDisabled = @@ -73,7 +82,7 @@ module.exports = createClass({ !Services.prefs.getBoolPref(REMOTE_ENABLED_PREF); this.setState({ debugDisabled }); - }, + } updateAddonsList() { this.props.client.listAddons() @@ -95,35 +104,35 @@ module.exports = createClass({ }, error => { throw new Error("Client error while listing addons: " + error); }); - }, + } /** * Mandatory callback as AddonManager listener. */ onInstalled() { this.updateAddonsList(); - }, + } /** * Mandatory callback as AddonManager listener. */ onUninstalled() { this.updateAddonsList(); - }, + } /** * Mandatory callback as AddonManager listener. */ onEnabled() { this.updateAddonsList(); - }, + } /** * Mandatory callback as AddonManager listener. */ onDisabled() { this.updateAddonsList(); - }, + } render() { let { client, id } = this.props; @@ -177,4 +186,6 @@ module.exports = createClass({ }) )); } -}); +} + +module.exports = AddonsPanel; diff --git a/devtools/client/aboutdebugging/components/addons/Target.js b/devtools/client/aboutdebugging/components/addons/Target.js index d3ec72142115..25ba5ae76339 100644 --- a/devtools/client/aboutdebugging/components/addons/Target.js +++ b/devtools/client/aboutdebugging/components/addons/Target.js @@ -6,7 +6,7 @@ "use strict"; -const { createClass, DOM: dom, PropTypes } = +const { Component, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); const { debugAddon, isTemporaryID, parseFileUri, uninstallAddon } = require("../../modules/addon"); @@ -122,32 +122,39 @@ function warningMessages(warnings = []) { }); } -module.exports = createClass({ - displayName: "AddonTarget", +class AddonTarget extends Component { + static get propTypes() { + return { + client: PropTypes.instanceOf(DebuggerClient).isRequired, + debugDisabled: PropTypes.bool, + target: PropTypes.shape({ + addonActor: PropTypes.string.isRequired, + addonID: PropTypes.string.isRequired, + icon: PropTypes.string, + name: PropTypes.string.isRequired, + temporarilyInstalled: PropTypes.bool, + url: PropTypes.string, + warnings: PropTypes.array, + }).isRequired + }; + } - propTypes: { - client: PropTypes.instanceOf(DebuggerClient).isRequired, - debugDisabled: PropTypes.bool, - target: PropTypes.shape({ - addonActor: PropTypes.string.isRequired, - addonID: PropTypes.string.isRequired, - icon: PropTypes.string, - name: PropTypes.string.isRequired, - temporarilyInstalled: PropTypes.bool, - url: PropTypes.string, - warnings: PropTypes.array, - }).isRequired - }, + constructor(props) { + super(props); + this.debug = this.debug.bind(this); + this.uninstall = this.uninstall.bind(this); + this.reload = this.reload.bind(this); + } debug() { let { target } = this.props; debugAddon(target.addonID); - }, + } uninstall() { let { target } = this.props; uninstallAddon(target.addonID); - }, + } reload() { let { client, target } = this.props; @@ -160,7 +167,7 @@ module.exports = createClass({ throw new Error( "Error reloading addon " + target.addonID + ": " + error); }); - }, + } render() { let { target, debugDisabled } = this.props; @@ -205,4 +212,6 @@ module.exports = createClass({ ), ); } -}); +} + +module.exports = AddonTarget; diff --git a/devtools/client/aboutdebugging/components/tabs/Panel.js b/devtools/client/aboutdebugging/components/tabs/Panel.js index 909fce206810..679c835512d9 100644 --- a/devtools/client/aboutdebugging/components/tabs/Panel.js +++ b/devtools/client/aboutdebugging/components/tabs/Panel.js @@ -6,7 +6,7 @@ "use strict"; -const { createClass, createFactory, DOM: dom, PropTypes } = +const { Component, createFactory, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); const Services = require("Services"); @@ -20,30 +20,34 @@ loader.lazyRequireGetter(this, "DebuggerClient", const Strings = Services.strings.createBundle( "chrome://devtools/locale/aboutdebugging.properties"); -module.exports = createClass({ - displayName: "TabsPanel", - - propTypes: { - client: PropTypes.instanceOf(DebuggerClient).isRequired, - id: PropTypes.string.isRequired - }, - - getInitialState() { +class TabsPanel extends Component { + static get propTypes() { return { + client: PropTypes.instanceOf(DebuggerClient).isRequired, + id: PropTypes.string.isRequired + }; + } + + constructor(props) { + super(props); + + this.state = { tabs: [] }; - }, + + this.update = this.update.bind(this); + } componentDidMount() { let { client } = this.props; client.addListener("tabListChanged", this.update); this.update(); - }, + } componentWillUnmount() { let { client } = this.props; client.removeListener("tabListChanged", this.update); - }, + } update() { this.props.client.mainRoot.listTabs().then(({ tabs }) => { @@ -68,7 +72,7 @@ module.exports = createClass({ }); this.setState({ tabs }); }); - }, + } render() { let { client, id } = this.props; @@ -95,4 +99,6 @@ module.exports = createClass({ }) )); } -}); +} + +module.exports = TabsPanel; diff --git a/devtools/client/aboutdebugging/components/tabs/Target.js b/devtools/client/aboutdebugging/components/tabs/Target.js index d946f8f61c93..015af651b223 100644 --- a/devtools/client/aboutdebugging/components/tabs/Target.js +++ b/devtools/client/aboutdebugging/components/tabs/Target.js @@ -6,29 +6,34 @@ "use strict"; -const { createClass, DOM: dom, PropTypes } = +const { Component, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); const Services = require("Services"); const Strings = Services.strings.createBundle( "chrome://devtools/locale/aboutdebugging.properties"); -module.exports = createClass({ - displayName: "TabTarget", +class TabTarget extends Component { + static get propTypes() { + return { + target: PropTypes.shape({ + icon: PropTypes.string, + outerWindowID: PropTypes.number.isRequired, + title: PropTypes.string, + url: PropTypes.string.isRequired + }).isRequired + }; + } - propTypes: { - target: PropTypes.shape({ - icon: PropTypes.string, - outerWindowID: PropTypes.number.isRequired, - title: PropTypes.string, - url: PropTypes.string.isRequired - }).isRequired - }, + constructor(props) { + super(props); + this.debug = this.debug.bind(this); + } debug() { let { target } = this.props; window.open("about:devtools-toolbox?type=tab&id=" + target.outerWindowID); - }, + } render() { let { target } = this.props; @@ -50,4 +55,6 @@ module.exports = createClass({ }, Strings.GetStringFromName("debug")) ); } -}); +} + +module.exports = TabTarget; diff --git a/devtools/client/aboutdebugging/components/workers/MultiE10sWarning.js b/devtools/client/aboutdebugging/components/workers/MultiE10sWarning.js index 0315d93597dc..260dc135a4c1 100644 --- a/devtools/client/aboutdebugging/components/workers/MultiE10sWarning.js +++ b/devtools/client/aboutdebugging/components/workers/MultiE10sWarning.js @@ -8,7 +8,7 @@ loader.lazyImporter(this, "PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm"); -const { createClass, DOM: dom } = +const { Component, DOM: dom } = require("devtools/client/shared/vendor/react"); const Services = require("Services"); const { Ci } = require("chrome"); @@ -22,8 +22,11 @@ loader.lazyRequireGetter(this, "DebuggerClient", const Strings = Services.strings.createBundle("chrome://devtools/locale/aboutdebugging.properties"); const MULTI_OPT_OUT_PREF = "dom.ipc.multiOptOut"; -module.exports = createClass({ - displayName: "multiE10SWarning", +class multiE10SWarning extends Component { + constructor(props) { + super(props); + this.onUpdatePreferenceClick = this.onUpdatePreferenceClick.bind(this); + } onUpdatePreferenceClick() { let message = Strings.GetStringFromName("multiProcessWarningConfirmUpdate2"); @@ -34,7 +37,7 @@ module.exports = createClass({ // Restart the browser. Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart); } - }, + } render() { return dom.div( @@ -58,5 +61,7 @@ module.exports = createClass({ Strings.GetStringFromName("multiProcessWarningUpdateLink2") ) ); - }, -}); + } +} + +module.exports = multiE10SWarning; diff --git a/devtools/client/aboutdebugging/components/workers/Panel.js b/devtools/client/aboutdebugging/components/workers/Panel.js index 33d3c7783651..5934f2b3be99 100644 --- a/devtools/client/aboutdebugging/components/workers/Panel.js +++ b/devtools/client/aboutdebugging/components/workers/Panel.js @@ -8,7 +8,7 @@ loader.lazyImporter(this, "PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm"); const { Ci } = require("chrome"); -const { createClass, createFactory, DOM: dom, PropTypes } = +const { Component, createFactory, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); const { getWorkerForms } = require("../../modules/worker"); const Services = require("Services"); @@ -34,24 +34,25 @@ const MORE_INFO_URL = "https://developer.mozilla.org/en-US/docs/Tools/about%3Ade const PROCESS_COUNT_PREF = "dom.ipc.processCount"; const MULTI_OPTOUT_PREF = "dom.ipc.multiOptOut"; -module.exports = createClass({ - displayName: "WorkersPanel", - - propTypes: { - client: PropTypes.instanceOf(DebuggerClient).isRequired, - id: PropTypes.string.isRequired - }, - - getInitialState() { +class WorkersPanel extends Component { + static get propTypes() { return { - workers: { - service: [], - shared: [], - other: [] - }, - processCount: 1, + client: PropTypes.instanceOf(DebuggerClient).isRequired, + id: PropTypes.string.isRequired }; - }, + } + + constructor(props) { + super(props); + + this.updateMultiE10S = this.updateMultiE10S.bind(this); + this.updateWorkers = this.updateWorkers.bind(this); + this.getRegistrationForWorker = this.getRegistrationForWorker.bind(this); + this.isE10S = this.isE10S.bind(this); + this.renderServiceWorkersError = this.renderServiceWorkersError.bind(this); + + this.state = this.initialState; + } componentDidMount() { let client = this.props.client; @@ -77,7 +78,7 @@ module.exports = createClass({ this.updateMultiE10S(); this.updateWorkers(); - }, + } componentWillUnmount() { let client = this.props.client; @@ -88,17 +89,28 @@ module.exports = createClass({ Services.prefs.removeObserver(PROCESS_COUNT_PREF, this.updateMultiE10S); Services.prefs.removeObserver(MULTI_OPTOUT_PREF, this.updateMultiE10S); - }, + } + + get initialState() { + return { + workers: { + service: [], + shared: [], + other: [] + }, + processCount: 1, + }; + } updateMultiE10S() { // We watch the pref but set the state based on // nsIXULRuntime.maxWebProcessCount. let processCount = Services.appinfo.maxWebProcessCount; this.setState({ processCount }); - }, + } updateWorkers() { - let workers = this.getInitialState().workers; + let workers = this.initialState.workers; getWorkerForms(this.props.client).then(forms => { forms.registrations.forEach(form => { @@ -156,7 +168,7 @@ module.exports = createClass({ this.setState({ workers }); }); - }, + } getRegistrationForWorker(form, registrations) { for (let registration of registrations) { @@ -165,11 +177,11 @@ module.exports = createClass({ } } return null; - }, + } isE10S() { return Services.appinfo.browserTabsRemoteAutostart; - }, + } renderServiceWorkersError() { let isWindowPrivate = PrivateBrowsingUtils.isContentWindowPrivate(window); @@ -200,7 +212,7 @@ module.exports = createClass({ Strings.GetStringFromName("configurationIsNotCompatible.learnMore") ), ); - }, + } render() { let { client, id } = this.props; @@ -255,4 +267,6 @@ module.exports = createClass({ ) ); } -}); +} + +module.exports = WorkersPanel; diff --git a/devtools/client/aboutdebugging/components/workers/ServiceWorkerTarget.js b/devtools/client/aboutdebugging/components/workers/ServiceWorkerTarget.js index d9112df3e8bd..511705645fc8 100644 --- a/devtools/client/aboutdebugging/components/workers/ServiceWorkerTarget.js +++ b/devtools/client/aboutdebugging/components/workers/ServiceWorkerTarget.js @@ -6,7 +6,7 @@ "use strict"; -const { createClass, DOM: dom, PropTypes } = +const { Component, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); const { debugWorker } = require("../../modules/worker"); const Services = require("Services"); @@ -17,36 +17,50 @@ loader.lazyRequireGetter(this, "DebuggerClient", const Strings = Services.strings.createBundle( "chrome://devtools/locale/aboutdebugging.properties"); -module.exports = createClass({ - displayName: "ServiceWorkerTarget", - - propTypes: { - client: PropTypes.instanceOf(DebuggerClient).isRequired, - debugDisabled: PropTypes.bool, - target: PropTypes.shape({ - active: PropTypes.bool, - fetch: PropTypes.bool.isRequired, - icon: PropTypes.string, - name: PropTypes.string.isRequired, - url: PropTypes.string, - scope: PropTypes.string.isRequired, - // registrationActor can be missing in e10s. - registrationActor: PropTypes.string, - workerActor: PropTypes.string - }).isRequired - }, - - getInitialState() { +class ServiceWorkerTarget extends Component { + static get propTypes() { return { + client: PropTypes.instanceOf(DebuggerClient).isRequired, + debugDisabled: PropTypes.bool, + target: PropTypes.shape({ + active: PropTypes.bool, + fetch: PropTypes.bool.isRequired, + icon: PropTypes.string, + name: PropTypes.string.isRequired, + url: PropTypes.string, + scope: PropTypes.string.isRequired, + // registrationActor can be missing in e10s. + registrationActor: PropTypes.string, + workerActor: PropTypes.string + }).isRequired + }; + } + + constructor(props) { + super(props); + + this.state = { pushSubscription: null }; - }, + + this.debug = this.debug.bind(this); + this.push = this.push.bind(this); + this.start = this.start.bind(this); + this.unregister = this.unregister.bind(this); + this.onPushSubscriptionModified = this.onPushSubscriptionModified.bind(this); + this.updatePushSubscription = this.updatePushSubscription.bind(this); + this.isRunning = this.isRunning.bind(this); + this.isActive = this.isActive.bind(this); + this.getServiceWorkerStatus = this.getServiceWorkerStatus.bind(this); + this.renderButtons = this.renderButtons.bind(this); + this.renderUnregisterLink = this.renderUnregisterLink.bind(this); + } componentDidMount() { let { client } = this.props; client.addListener("push-subscription-modified", this.onPushSubscriptionModified); this.updatePushSubscription(); - }, + } componentDidUpdate(oldProps, oldState) { let wasActive = oldProps.target.active; @@ -56,12 +70,12 @@ module.exports = createClass({ // subscription change by updating it now. this.updatePushSubscription(); } - }, + } componentWillUnmount() { let { client } = this.props; client.removeListener("push-subscription-modified", this.onPushSubscriptionModified); - }, + } debug() { if (!this.isRunning()) { @@ -71,7 +85,7 @@ module.exports = createClass({ let { client, target } = this.props; debugWorker(client, target.workerActor); - }, + } push() { if (!this.isActive() || !this.isRunning()) { @@ -86,7 +100,7 @@ module.exports = createClass({ to: target.workerActor, type: "push" }); - }, + } start() { if (!this.isActive() || this.isRunning()) { @@ -99,7 +113,7 @@ module.exports = createClass({ to: target.registrationActor, type: "start" }); - }, + } unregister() { let { client, target } = this.props; @@ -107,14 +121,14 @@ module.exports = createClass({ to: target.registrationActor, type: "unregister" }); - }, + } onPushSubscriptionModified(type, data) { let { target } = this.props; if (data.from === target.registrationActor) { this.updatePushSubscription(); } - }, + } updatePushSubscription() { if (!this.props.target.registrationActor) { @@ -129,16 +143,16 @@ module.exports = createClass({ }, ({ subscription }) => { this.setState({ pushSubscription: subscription }); }); - }, + } isRunning() { // We know the target is running if it has a worker actor. return !!this.props.target.workerActor; - }, + } isActive() { return this.props.target.active; - }, + } getServiceWorkerStatus() { if (this.isActive() && this.isRunning()) { @@ -150,7 +164,7 @@ module.exports = createClass({ // ACTIVE state. Unable to know the actual state ("installing", "waiting"), we // display a custom state "registering" for now. See Bug 1153292. return "registering"; - }, + } renderButtons() { let pushButton = dom.button({ @@ -179,7 +193,7 @@ module.exports = createClass({ return debugButton; } return startButton; - }, + } renderUnregisterLink() { if (!this.isActive()) { @@ -191,7 +205,7 @@ module.exports = createClass({ onClick: this.unregister, className: "unregister-link", }, Strings.GetStringFromName("unregister")); - }, + } render() { let { target } = this.props; @@ -240,4 +254,6 @@ module.exports = createClass({ this.renderButtons() ); } -}); +} + +module.exports = ServiceWorkerTarget; diff --git a/devtools/client/aboutdebugging/components/workers/Target.js b/devtools/client/aboutdebugging/components/workers/Target.js index 3815f9fac18e..f8c1c6a5cc44 100644 --- a/devtools/client/aboutdebugging/components/workers/Target.js +++ b/devtools/client/aboutdebugging/components/workers/Target.js @@ -6,7 +6,7 @@ "use strict"; -const { createClass, DOM: dom, PropTypes } = +const { Component, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react"); const { debugWorker } = require("../../modules/worker"); const Services = require("Services"); @@ -17,23 +17,28 @@ loader.lazyRequireGetter(this, "DebuggerClient", const Strings = Services.strings.createBundle( "chrome://devtools/locale/aboutdebugging.properties"); -module.exports = createClass({ - displayName: "WorkerTarget", +class WorkerTarget extends Component { + static get propTypes() { + return { + client: PropTypes.instanceOf(DebuggerClient).isRequired, + debugDisabled: PropTypes.bool, + target: PropTypes.shape({ + icon: PropTypes.string, + name: PropTypes.string.isRequired, + workerActor: PropTypes.string + }).isRequired + }; + } - propTypes: { - client: PropTypes.instanceOf(DebuggerClient).isRequired, - debugDisabled: PropTypes.bool, - target: PropTypes.shape({ - icon: PropTypes.string, - name: PropTypes.string.isRequired, - workerActor: PropTypes.string - }).isRequired - }, + constructor(props) { + super(props); + this.debug = this.debug.bind(this); + } debug() { let { client, target } = this.props; debugWorker(client, target.workerActor); - }, + } render() { let { target, debugDisabled } = this.props; @@ -54,4 +59,6 @@ module.exports = createClass({ }, Strings.GetStringFromName("debug")) ); } -}); +} + +module.exports = WorkerTarget; diff --git a/devtools/client/memory/app.js b/devtools/client/memory/app.js index 44189afe3208..0cc1e646e9f0 100644 --- a/devtools/client/memory/app.js +++ b/devtools/client/memory/app.js @@ -6,7 +6,7 @@ const { assert } = require("devtools/shared/DevToolsUtils"); const { appinfo } = require("Services"); -const { DOM: dom, createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react"); +const { DOM: dom, Component, createFactory, PropTypes } = require("devtools/client/shared/vendor/react"); const { connect } = require("devtools/client/shared/vendor/react-redux"); const { censusDisplays, labelDisplays, treeMapDisplays, diffingState, viewState } = require("./constants"); const { toggleRecordingAllocationStacks } = require("./actions/allocations"); @@ -51,20 +51,30 @@ const SnapshotListItem = createFactory(require("./components/SnapshotListItem")) const Heap = createFactory(require("./components/Heap")); const { app: appModel } = require("./models"); -const MemoryApp = createClass({ - displayName: "MemoryApp", +class MemoryApp extends Component { + static get propTypes() { + return appModel; + } - propTypes: appModel, + static get childContextTypes() { + return { + front: PropTypes.any, + heapWorker: PropTypes.any, + toolbox: PropTypes.any, + }; + } - childContextTypes: { - front: PropTypes.any, - heapWorker: PropTypes.any, - toolbox: PropTypes.any, - }, - - getDefaultProps() { + static get defaultProps() { return {}; - }, + } + + constructor(props) { + super(props); + this.onKeyDown = this.onKeyDown.bind(this); + this._getCensusDisplays = this._getCensusDisplays.bind(this); + this._getLabelDisplays = this._getLabelDisplays.bind(this); + this._getTreeMapDisplays = this._getTreeMapDisplays.bind(this); + } getChildContext() { return { @@ -72,18 +82,18 @@ const MemoryApp = createClass({ heapWorker: this.props.heapWorker, toolbox: this.props.toolbox, }; - }, + } componentDidMount() { // Attach the keydown listener directly to the window. When an element that // has the focus (such as a tree node) is removed from the DOM, the focus // falls back to the body. window.addEventListener("keydown", this.onKeyDown); - }, + } componentWillUnmount() { window.removeEventListener("keydown", this.onKeyDown); - }, + } onKeyDown(e) { let { snapshots, dispatch, heapWorker } = this.props; @@ -106,7 +116,7 @@ const MemoryApp = createClass({ let nextSnapshotId = snapshots[nextIndex].id; dispatch(selectSnapshotAndRefresh(heapWorker, nextSnapshotId)); } - }, + } _getCensusDisplays() { const customDisplays = getCustomCensusDisplays(); @@ -120,7 +130,7 @@ const MemoryApp = createClass({ censusDisplays.allocationStack, censusDisplays.invertedAllocationStack, ].concat(custom); - }, + } _getLabelDisplays() { const customDisplays = getCustomLabelDisplays(); @@ -133,7 +143,7 @@ const MemoryApp = createClass({ labelDisplays.coarseType, labelDisplays.allocationStack, ].concat(custom); - }, + } _getTreeMapDisplays() { const customDisplays = getCustomTreeMapDisplays(); @@ -145,7 +155,7 @@ const MemoryApp = createClass({ return [ treeMapDisplays.coarseType ].concat(custom); - }, + } render() { let { @@ -317,8 +327,8 @@ const MemoryApp = createClass({ ) ) ); - }, -}); + } +} /** * Passed into react-redux's `connect` method that is called on store change diff --git a/devtools/client/memory/components/Census.js b/devtools/client/memory/components/Census.js index b198a1c4ee99..e521bee6bc9f 100644 --- a/devtools/client/memory/components/Census.js +++ b/devtools/client/memory/components/Census.js @@ -4,24 +4,24 @@ "use strict"; -const { createClass, PropTypes, createFactory } = require("devtools/client/shared/vendor/react"); +const { Component, PropTypes, createFactory } = require("devtools/client/shared/vendor/react"); const Tree = createFactory(require("devtools/client/shared/components/Tree")); const CensusTreeItem = createFactory(require("./CensusTreeItem")); const { TREE_ROW_HEIGHT } = require("../constants"); const { censusModel, diffingModel } = require("../models"); -module.exports = createClass({ - displayName: "Census", - - propTypes: { - census: censusModel, - onExpand: PropTypes.func.isRequired, - onCollapse: PropTypes.func.isRequired, - onFocus: PropTypes.func.isRequired, - onViewSourceInDebugger: PropTypes.func.isRequired, - onViewIndividuals: PropTypes.func.isRequired, - diffing: diffingModel, - }, +class Census extends Component { + static get propTypes() { + return { + census: censusModel, + onExpand: PropTypes.func.isRequired, + onCollapse: PropTypes.func.isRequired, + onFocus: PropTypes.func.isRequired, + onViewSourceInDebugger: PropTypes.func.isRequired, + onViewIndividuals: PropTypes.func.isRequired, + diffing: diffingModel, + }; + } render() { let { @@ -77,4 +77,6 @@ module.exports = createClass({ itemHeight: TREE_ROW_HEIGHT, }); } -}); +} + +module.exports = Census; diff --git a/devtools/client/memory/components/CensusHeader.js b/devtools/client/memory/components/CensusHeader.js index dee1db6215a8..ee4e4ee85798 100644 --- a/devtools/client/memory/components/CensusHeader.js +++ b/devtools/client/memory/components/CensusHeader.js @@ -4,16 +4,16 @@ "use strict"; -const { DOM: dom, createClass } = require("devtools/client/shared/vendor/react"); +const { DOM: dom, Component } = require("devtools/client/shared/vendor/react"); const { L10N } = require("../utils"); const models = require("../models"); -module.exports = createClass({ - displayName: "CensusHeader", - - propTypes: { - diffing: models.diffingModel, - }, +class CensusHeader extends Component { + static get propTypes() { + return { + diffing: models.diffingModel, + }; + } render() { let individualsCell; @@ -71,4 +71,6 @@ module.exports = createClass({ ) ); } -}); +} + +module.exports = CensusHeader; diff --git a/devtools/client/memory/components/CensusTreeItem.js b/devtools/client/memory/components/CensusTreeItem.js index 3f11796339c4..7e386326d9e2 100644 --- a/devtools/client/memory/components/CensusTreeItem.js +++ b/devtools/client/memory/components/CensusTreeItem.js @@ -6,7 +6,7 @@ const { isSavedFrame } = require("devtools/shared/DevToolsUtils"); const { DOM: dom, - createClass, + Component, createFactory, PropTypes } = require("devtools/client/shared/vendor/react"); @@ -15,22 +15,27 @@ const Frame = createFactory(require("devtools/client/shared/components/Frame")); const { TREE_ROW_HEIGHT } = require("../constants"); const models = require("../models"); -module.exports = createClass({ - displayName: "CensusTreeItem", +class CensusTreeItem extends Component { + static get propTypes() { + return { + arrow: PropTypes.any, + depth: PropTypes.number.isRequired, + diffing: models.app.diffing, + expanded: PropTypes.bool.isRequired, + focused: PropTypes.bool.isRequired, + getPercentBytes: PropTypes.func.isRequired, + getPercentCount: PropTypes.func.isRequired, + inverted: PropTypes.bool, + item: PropTypes.object.isRequired, + onViewIndividuals: PropTypes.func.isRequired, + onViewSourceInDebugger: PropTypes.func.isRequired, + }; + } - propTypes: { - arrow: PropTypes.any, - depth: PropTypes.number.isRequired, - diffing: models.app.diffing, - expanded: PropTypes.bool.isRequired, - focused: PropTypes.bool.isRequired, - getPercentBytes: PropTypes.func.isRequired, - getPercentCount: PropTypes.func.isRequired, - inverted: PropTypes.bool, - item: PropTypes.object.isRequired, - onViewIndividuals: PropTypes.func.isRequired, - onViewSourceInDebugger: PropTypes.func.isRequired, - }, + constructor(props) { + super(props); + this.toLabel = this.toLabel.bind(this); + } shouldComponentUpdate(nextProps, nextState) { return this.props.item != nextProps.item @@ -38,7 +43,7 @@ module.exports = createClass({ || this.props.expanded != nextProps.expanded || this.props.focused != nextProps.focused || this.props.diffing != nextProps.diffing; - }, + } toLabel(name, linkToDebugger) { if (isSavedFrame(name)) { @@ -63,7 +68,7 @@ module.exports = createClass({ } return String(name); - }, + } render() { let { @@ -150,5 +155,7 @@ module.exports = createClass({ this.toLabel(item.name, onViewSourceInDebugger) ) ); - }, -}); + } +} + +module.exports = CensusTreeItem; diff --git a/devtools/client/memory/components/DominatorTree.js b/devtools/client/memory/components/DominatorTree.js index b535490c69fa..7265f4936153 100644 --- a/devtools/client/memory/components/DominatorTree.js +++ b/devtools/client/memory/components/DominatorTree.js @@ -4,7 +4,7 @@ "use strict"; -const { DOM: dom, createClass, PropTypes, createFactory } = require("devtools/client/shared/vendor/react"); +const { DOM: dom, Component, PropTypes, createFactory } = require("devtools/client/shared/vendor/react"); const { assert } = require("devtools/shared/DevToolsUtils"); const { createParentMap } = require("devtools/shared/heapsnapshot/CensusUtils"); const Tree = createFactory(require("devtools/client/shared/components/Tree")); @@ -20,18 +20,18 @@ const DOMINATOR_TREE_AUTO_EXPAND_DEPTH = 3; * A throbber that represents a subtree in the dominator tree that is actively * being incrementally loaded and fetched from the `HeapAnalysesWorker`. */ -const DominatorTreeSubtreeFetching = createFactory(createClass({ - displayName: "DominatorTreeSubtreeFetching", - - propTypes: { - depth: PropTypes.number.isRequired, - focused: PropTypes.bool.isRequired, - }, +class DominatorTreeSubtreeFetchingClass extends Component { + static get propTypes() { + return { + depth: PropTypes.number.isRequired, + focused: PropTypes.bool.isRequired, + }; + } shouldComponentUpdate(nextProps, nextState) { return this.props.depth !== nextProps.depth || this.props.focused !== nextProps.focused; - }, + } render() { let { @@ -51,26 +51,26 @@ const DominatorTreeSubtreeFetching = createFactory(createClass({ }) ); } -})); +} /** * A link to fetch and load more siblings in the dominator tree, when there are * already many loaded above. */ -const DominatorTreeSiblingLink = createFactory(createClass({ - displayName: "DominatorTreeSiblingLink", - - propTypes: { - depth: PropTypes.number.isRequired, - focused: PropTypes.bool.isRequired, - item: PropTypes.instanceOf(DominatorTreeLazyChildren).isRequired, - onLoadMoreSiblings: PropTypes.func.isRequired, - }, +class DominatorTreeSiblingLinkClass extends Component { + static get propTypes() { + return { + depth: PropTypes.number.isRequired, + focused: PropTypes.bool.isRequired, + item: PropTypes.instanceOf(DominatorTreeLazyChildren).isRequired, + onLoadMoreSiblings: PropTypes.func.isRequired, + }; + } shouldComponentUpdate(nextProps, nextState) { return this.props.depth !== nextProps.depth || this.props.focused !== nextProps.focused; - }, + } render() { let { @@ -100,22 +100,19 @@ const DominatorTreeSiblingLink = createFactory(createClass({ ) ); } -})); +} -/** - * The actual dominator tree rendered as an expandable and collapsible tree. - */ -module.exports = createClass({ - displayName: "DominatorTree", - - propTypes: { - dominatorTree: dominatorTreeModel.isRequired, - onLoadMoreSiblings: PropTypes.func.isRequired, - onViewSourceInDebugger: PropTypes.func.isRequired, - onExpand: PropTypes.func.isRequired, - onCollapse: PropTypes.func.isRequired, - onFocus: PropTypes.func.isRequired, - }, +class DominatorTree extends Component { + static get propTypes() { + return { + dominatorTree: dominatorTreeModel.isRequired, + onLoadMoreSiblings: PropTypes.func.isRequired, + onViewSourceInDebugger: PropTypes.func.isRequired, + onExpand: PropTypes.func.isRequired, + onCollapse: PropTypes.func.isRequired, + onFocus: PropTypes.func.isRequired, + }; + } shouldComponentUpdate(nextProps, nextState) { // Safe to use referential equality here because all of our mutations on @@ -125,7 +122,7 @@ module.exports = createClass({ // mutations to the expanded set occur. Because of the re-allocations, we // can continue using referential equality here. return this.props.dominatorTree !== nextProps.dominatorTree; - }, + } render() { const { dominatorTree, onViewSourceInDebugger, onLoadMoreSiblings } = this.props; @@ -216,4 +213,9 @@ module.exports = createClass({ itemHeight: TREE_ROW_HEIGHT, }); } -}); +} + +const DominatorTreeSubtreeFetching = createFactory(DominatorTreeSubtreeFetchingClass); +const DominatorTreeSiblingLink = createFactory(DominatorTreeSiblingLinkClass); + +module.exports = DominatorTree; diff --git a/devtools/client/memory/components/DominatorTreeHeader.js b/devtools/client/memory/components/DominatorTreeHeader.js index b2e22fed9790..ee7d0b0aea0d 100644 --- a/devtools/client/memory/components/DominatorTreeHeader.js +++ b/devtools/client/memory/components/DominatorTreeHeader.js @@ -4,13 +4,13 @@ "use strict"; -const { DOM: dom, createClass } = require("devtools/client/shared/vendor/react"); +const { DOM: dom, Component } = require("devtools/client/shared/vendor/react"); const { L10N } = require("../utils"); -module.exports = createClass({ - displayName: "DominatorTreeHeader", - - propTypes: { }, +class DominatorTreeHeader extends Component { + static get propTypes() { + return { }; + } render() { return dom.div( @@ -43,4 +43,6 @@ module.exports = createClass({ ) ); } -}); +} + +module.exports = DominatorTreeHeader; diff --git a/devtools/client/memory/components/DominatorTreeItem.js b/devtools/client/memory/components/DominatorTreeItem.js index c84db9d57ecf..2a25dcb290e5 100644 --- a/devtools/client/memory/components/DominatorTreeItem.js +++ b/devtools/client/memory/components/DominatorTreeItem.js @@ -5,38 +5,38 @@ "use strict"; const { assert, isSavedFrame } = require("devtools/shared/DevToolsUtils"); -const { DOM: dom, createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react"); +const { DOM: dom, Component, createFactory, PropTypes } = require("devtools/client/shared/vendor/react"); const { L10N, formatNumber, formatPercent } = require("../utils"); const Frame = createFactory(require("devtools/client/shared/components/Frame")); const { TREE_ROW_HEIGHT } = require("../constants"); -const Separator = createFactory(createClass({ - displayName: "Separator", - +class SeparatorClass extends Component { render() { return dom.span({ className: "separator" }, "›"); } -})); +} -module.exports = createClass({ - displayName: "DominatorTreeItem", +const Separator = createFactory(SeparatorClass); - propTypes: { - item: PropTypes.object.isRequired, - depth: PropTypes.number.isRequired, - arrow: PropTypes.object, - expanded: PropTypes.bool.isRequired, - focused: PropTypes.bool.isRequired, - getPercentSize: PropTypes.func.isRequired, - onViewSourceInDebugger: PropTypes.func.isRequired, - }, +class DominatorTreeItem extends Component { + static get propTypes() { + return { + item: PropTypes.object.isRequired, + depth: PropTypes.number.isRequired, + arrow: PropTypes.object, + expanded: PropTypes.bool.isRequired, + focused: PropTypes.bool.isRequired, + getPercentSize: PropTypes.func.isRequired, + onViewSourceInDebugger: PropTypes.func.isRequired, + }; + } shouldComponentUpdate(nextProps, nextState) { return this.props.item != nextProps.item || this.props.depth != nextProps.depth || this.props.expanded != nextProps.expanded || this.props.focused != nextProps.focused; - }, + } render() { let { @@ -141,5 +141,7 @@ module.exports = createClass({ `@ 0x${item.nodeId.toString(16)}`) ) ); - }, -}); + } +} + +module.exports = DominatorTreeItem; diff --git a/devtools/client/memory/components/Heap.js b/devtools/client/memory/components/Heap.js index 6565446c1dea..ff6dec242cf6 100644 --- a/devtools/client/memory/components/Heap.js +++ b/devtools/client/memory/components/Heap.js @@ -4,7 +4,7 @@ "use strict"; -const { DOM: dom, createClass, PropTypes, createFactory } = require("devtools/client/shared/vendor/react"); +const { DOM: dom, Component, PropTypes, createFactory } = require("devtools/client/shared/vendor/react"); const { assert, safeErrorString } = require("devtools/shared/DevToolsUtils"); const Census = createFactory(require("./Census")); const CensusHeader = createFactory(require("./CensusHeader")); @@ -180,29 +180,41 @@ function getError(snapshot, diffing, individuals) { * state of only a button to take a snapshot, loading states, the census view * tree, the dominator tree, etc. */ -module.exports = createClass({ - displayName: "Heap", +class Heap extends Component { + static get propTypes() { + return { + onSnapshotClick: PropTypes.func.isRequired, + onLoadMoreSiblings: PropTypes.func.isRequired, + onCensusExpand: PropTypes.func.isRequired, + onCensusCollapse: PropTypes.func.isRequired, + onDominatorTreeExpand: PropTypes.func.isRequired, + onDominatorTreeCollapse: PropTypes.func.isRequired, + onCensusFocus: PropTypes.func.isRequired, + onDominatorTreeFocus: PropTypes.func.isRequired, + onShortestPathsResize: PropTypes.func.isRequired, + snapshot: snapshotModel, + onViewSourceInDebugger: PropTypes.func.isRequired, + onPopView: PropTypes.func.isRequired, + individuals: models.individuals, + onViewIndividuals: PropTypes.func.isRequired, + onFocusIndividual: PropTypes.func.isRequired, + diffing: diffingModel, + view: models.view.isRequired, + sizes: PropTypes.object.isRequired, + }; + } - propTypes: { - onSnapshotClick: PropTypes.func.isRequired, - onLoadMoreSiblings: PropTypes.func.isRequired, - onCensusExpand: PropTypes.func.isRequired, - onCensusCollapse: PropTypes.func.isRequired, - onDominatorTreeExpand: PropTypes.func.isRequired, - onDominatorTreeCollapse: PropTypes.func.isRequired, - onCensusFocus: PropTypes.func.isRequired, - onDominatorTreeFocus: PropTypes.func.isRequired, - onShortestPathsResize: PropTypes.func.isRequired, - snapshot: snapshotModel, - onViewSourceInDebugger: PropTypes.func.isRequired, - onPopView: PropTypes.func.isRequired, - individuals: models.individuals, - onViewIndividuals: PropTypes.func.isRequired, - onFocusIndividual: PropTypes.func.isRequired, - diffing: diffingModel, - view: models.view.isRequired, - sizes: PropTypes.object.isRequired, - }, + constructor(props) { + super(props); + this._renderHeapView = this._renderHeapView.bind(this); + this._renderInitial = this._renderInitial.bind(this); + this._renderStatus = this._renderStatus.bind(this); + this._renderError = this._renderError.bind(this); + this._renderCensus = this._renderCensus.bind(this); + this._renderTreeMap = this._renderTreeMap.bind(this); + this._renderIndividuals = this._renderIndividuals.bind(this); + this._renderDominatorTree = this._renderDominatorTree.bind(this); + } /** * Render the heap view's container panel with the given contents inside of @@ -225,7 +237,7 @@ module.exports = createClass({ ...contents ) ); - }, + } _renderInitial(onSnapshotClick) { return this._renderHeapView("initial", dom.button( @@ -236,7 +248,7 @@ module.exports = createClass({ }, L10N.getStr("take-snapshot") )); - }, + } _renderStatus(state, statusText, diffing) { let throbber = ""; @@ -250,7 +262,7 @@ module.exports = createClass({ }, statusText )); - }, + } _renderError(state, statusText, error) { return this._renderHeapView( @@ -258,7 +270,7 @@ module.exports = createClass({ dom.span({ className: "snapshot-status error" }, statusText), dom.pre({}, safeErrorString(error)) ); - }, + } _renderCensus(state, census, diffing, onViewSourceInDebugger, onViewIndividuals) { assert(census.report, "Should not render census that does not have a report"); @@ -293,14 +305,14 @@ module.exports = createClass({ })); return this._renderHeapView(state, ...contents); - }, + } _renderTreeMap(state, treeMap) { return this._renderHeapView( state, TreeMap({ treeMap }) ); - }, + } _renderIndividuals(state, individuals, dominatorTree, onViewSourceInDebugger) { assert(individuals.state === individualsState.FETCHED, @@ -355,7 +367,7 @@ module.exports = createClass({ onResize: this.props.onShortestPathsResize, }) ); - }, + } _renderDominatorTree(state, onViewSourceInDebugger, dominatorTree, onLoadMoreSiblings) { const tree = dom.div( @@ -391,7 +403,7 @@ module.exports = createClass({ onResize: this.props.onShortestPathsResize, }) ); - }, + } render() { let { @@ -454,5 +466,7 @@ module.exports = createClass({ return this._renderDominatorTree(state, onViewSourceInDebugger, snapshot.dominatorTree, onLoadMoreSiblings); - }, -}); + } +} + +module.exports = Heap; diff --git a/devtools/client/memory/components/Individuals.js b/devtools/client/memory/components/Individuals.js index 44f817046c90..7fa9aea7fd16 100644 --- a/devtools/client/memory/components/Individuals.js +++ b/devtools/client/memory/components/Individuals.js @@ -4,7 +4,7 @@ "use strict"; -const { createClass, PropTypes, createFactory } = require("devtools/client/shared/vendor/react"); +const { Component, PropTypes, createFactory } = require("devtools/client/shared/vendor/react"); const Tree = createFactory(require("devtools/client/shared/components/Tree")); const DominatorTreeItem = createFactory(require("./DominatorTreeItem")); const { TREE_ROW_HEIGHT } = require("../constants"); @@ -13,15 +13,15 @@ const models = require("../models"); /** * The list of individuals in a census group. */ -module.exports = createClass({ - displayName: "Individuals", - - propTypes: { - onViewSourceInDebugger: PropTypes.func.isRequired, - onFocus: PropTypes.func.isRequired, - individuals: models.individuals, - dominatorTree: models.dominatorTreeModel, - }, +class Individuals extends Component { + static get propTypes() { + return { + onViewSourceInDebugger: PropTypes.func.isRequired, + onFocus: PropTypes.func.isRequired, + individuals: models.individuals, + dominatorTree: models.dominatorTreeModel, + }; + } render() { const { @@ -57,4 +57,6 @@ module.exports = createClass({ itemHeight: TREE_ROW_HEIGHT, }); } -}); +} + +module.exports = Individuals; diff --git a/devtools/client/memory/components/IndividualsHeader.js b/devtools/client/memory/components/IndividualsHeader.js index 6da3c09049a0..be6f9d8c691b 100644 --- a/devtools/client/memory/components/IndividualsHeader.js +++ b/devtools/client/memory/components/IndividualsHeader.js @@ -4,13 +4,13 @@ "use strict"; -const { DOM: dom, createClass } = require("devtools/client/shared/vendor/react"); +const { DOM: dom, Component } = require("devtools/client/shared/vendor/react"); const { L10N } = require("../utils"); -module.exports = createClass({ - displayName: "IndividualsHeader", - - propTypes: { }, +class IndividualsHeader extends Component { + static get propTypes() { + return { }; + } render() { return dom.div( @@ -43,4 +43,6 @@ module.exports = createClass({ ) ); } -}); +} + +module.exports = IndividualsHeader; diff --git a/devtools/client/memory/components/List.js b/devtools/client/memory/components/List.js index 6568c7ce59fa..d54f285a6525 100644 --- a/devtools/client/memory/components/List.js +++ b/devtools/client/memory/components/List.js @@ -4,21 +4,21 @@ "use strict"; -const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react"); +const { DOM: dom, Component, PropTypes } = require("devtools/client/shared/vendor/react"); /** * Generic list component that takes another react component to represent * the children nodes as `itemComponent`, and a list of items to render * as that component with a click handler. */ -module.exports = createClass({ - displayName: "List", - - propTypes: { - itemComponent: PropTypes.any.isRequired, - onClick: PropTypes.func, - items: PropTypes.array.isRequired, - }, +class List extends Component { + static get propTypes() { + return { + itemComponent: PropTypes.any.isRequired, + onClick: PropTypes.func, + items: PropTypes.array.isRequired, + }; + } render() { let { items, onClick, itemComponent: Item } = this.props; @@ -34,4 +34,6 @@ module.exports = createClass({ })) ); } -}); +} + +module.exports = List; diff --git a/devtools/client/memory/components/ShortestPaths.js b/devtools/client/memory/components/ShortestPaths.js index 51d2293a73a1..da0bd25b96a7 100644 --- a/devtools/client/memory/components/ShortestPaths.js +++ b/devtools/client/memory/components/ShortestPaths.js @@ -6,7 +6,7 @@ const { DOM: dom, - createClass, + Component, PropTypes, } = require("devtools/client/shared/vendor/react"); const { isSavedFrame } = require("devtools/shared/DevToolsUtils"); @@ -50,41 +50,43 @@ function stringifyLabel(label, id) { return `${sanitized.join(" › ")} @ 0x${id.toString(16)}`; } -module.exports = createClass({ - displayName: "ShortestPaths", +class ShortestPaths extends Component { + static get propTypes() { + return { + graph: PropTypes.shape({ + nodes: PropTypes.arrayOf(PropTypes.object), + edges: PropTypes.arrayOf(PropTypes.object), + }), + }; + } - propTypes: { - graph: PropTypes.shape({ - nodes: PropTypes.arrayOf(PropTypes.object), - edges: PropTypes.arrayOf(PropTypes.object), - }), - }, - - getInitialState() { - return { zoom: null }; - }, + constructor(props) { + super(props); + this.state = { zoom: null }; + this._renderGraph = this._renderGraph.bind(this); + } componentDidMount() { if (this.props.graph) { this._renderGraph(this.refs.container, this.props.graph); } - }, + } shouldComponentUpdate(nextProps) { return this.props.graph != nextProps.graph; - }, + } componentDidUpdate() { if (this.props.graph) { this._renderGraph(this.refs.container, this.props.graph); } - }, + } componentWillUnmount() { if (this.state.zoom) { this.state.zoom.on("zoom", null); } - }, + } _renderGraph(container, { nodes, edges }) { if (!container.firstChild) { @@ -144,7 +146,7 @@ module.exports = createClass({ const layout = dagreD3.layout(); renderer.layout(layout).run(graph, target); - }, + } render() { let contents; @@ -182,5 +184,7 @@ module.exports = createClass({ ), contents ); - }, -}); + } +} + +module.exports = ShortestPaths; diff --git a/devtools/client/memory/components/SnapshotListItem.js b/devtools/client/memory/components/SnapshotListItem.js index 8cf41ca8ba25..5bcc10744c83 100644 --- a/devtools/client/memory/components/SnapshotListItem.js +++ b/devtools/client/memory/components/SnapshotListItem.js @@ -4,7 +4,7 @@ "use strict"; -const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react"); +const { DOM: dom, Component, PropTypes } = require("devtools/client/shared/vendor/react"); const { L10N, getSnapshotTitle, @@ -16,17 +16,17 @@ const { const { diffingState } = require("../constants"); const { snapshot: snapshotModel, app: appModel } = require("../models"); -module.exports = createClass({ - displayName: "SnapshotListItem", - - propTypes: { - onClick: PropTypes.func.isRequired, - onSave: PropTypes.func.isRequired, - onDelete: PropTypes.func.isRequired, - item: snapshotModel.isRequired, - index: PropTypes.number.isRequired, - diffing: appModel.diffing, - }, +class SnapshotListItem extends Component { + static get propTypes() { + return { + onClick: PropTypes.func.isRequired, + onSave: PropTypes.func.isRequired, + onDelete: PropTypes.func.isRequired, + item: snapshotModel.isRequired, + index: PropTypes.number.isRequired, + diffing: appModel.diffing, + }; + } render() { let { item: snapshot, onClick, onSave, onDelete, diffing } = this.props; @@ -112,4 +112,6 @@ module.exports = createClass({ ) ); } -}); +} + +module.exports = SnapshotListItem; diff --git a/devtools/client/memory/components/Toolbar.js b/devtools/client/memory/components/Toolbar.js index 624a63c7bff6..eecb66201a43 100644 --- a/devtools/client/memory/components/Toolbar.js +++ b/devtools/client/memory/components/Toolbar.js @@ -4,46 +4,46 @@ "use strict"; const { assert } = require("devtools/shared/DevToolsUtils"); -const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react"); +const { DOM: dom, Component, PropTypes } = require("devtools/client/shared/vendor/react"); const { L10N } = require("../utils"); const models = require("../models"); const { viewState } = require("../constants"); -module.exports = createClass({ - displayName: "Toolbar", - - propTypes: { - censusDisplays: PropTypes.arrayOf(PropTypes.shape({ - displayName: PropTypes.string.isRequired, - })).isRequired, - censusDisplay: PropTypes.shape({ - displayName: PropTypes.string.isRequired, - }).isRequired, - onTakeSnapshotClick: PropTypes.func.isRequired, - onImportClick: PropTypes.func.isRequired, - onClearSnapshotsClick: PropTypes.func.isRequired, - onCensusDisplayChange: PropTypes.func.isRequired, - onToggleRecordAllocationStacks: PropTypes.func.isRequired, - allocations: models.allocations, - filterString: PropTypes.string, - setFilterString: PropTypes.func.isRequired, - diffing: models.diffingModel, - onToggleDiffing: PropTypes.func.isRequired, - view: models.view.isRequired, - onViewChange: PropTypes.func.isRequired, - labelDisplays: PropTypes.arrayOf(PropTypes.shape({ - displayName: PropTypes.string.isRequired, - })).isRequired, - labelDisplay: PropTypes.shape({ - displayName: PropTypes.string.isRequired, - }).isRequired, - onLabelDisplayChange: PropTypes.func.isRequired, - treeMapDisplays: PropTypes.arrayOf(PropTypes.shape({ - displayName: PropTypes.string.isRequired, - })).isRequired, - onTreeMapDisplayChange: PropTypes.func.isRequired, - snapshots: PropTypes.arrayOf(models.snapshot).isRequired, - }, +class Toolbar extends Component { + static get propTypes() { + return { + censusDisplays: PropTypes.arrayOf(PropTypes.shape({ + displayName: PropTypes.string.isRequired, + })).isRequired, + censusDisplay: PropTypes.shape({ + displayName: PropTypes.string.isRequired, + }).isRequired, + onTakeSnapshotClick: PropTypes.func.isRequired, + onImportClick: PropTypes.func.isRequired, + onClearSnapshotsClick: PropTypes.func.isRequired, + onCensusDisplayChange: PropTypes.func.isRequired, + onToggleRecordAllocationStacks: PropTypes.func.isRequired, + allocations: models.allocations, + filterString: PropTypes.string, + setFilterString: PropTypes.func.isRequired, + diffing: models.diffingModel, + onToggleDiffing: PropTypes.func.isRequired, + view: models.view.isRequired, + onViewChange: PropTypes.func.isRequired, + labelDisplays: PropTypes.arrayOf(PropTypes.shape({ + displayName: PropTypes.string.isRequired, + })).isRequired, + labelDisplay: PropTypes.shape({ + displayName: PropTypes.string.isRequired, + }).isRequired, + onLabelDisplayChange: PropTypes.func.isRequired, + treeMapDisplays: PropTypes.arrayOf(PropTypes.shape({ + displayName: PropTypes.string.isRequired, + })).isRequired, + onTreeMapDisplayChange: PropTypes.func.isRequired, + snapshots: PropTypes.arrayOf(models.snapshot).isRequired, + }; + } render() { let { @@ -298,4 +298,6 @@ module.exports = createClass({ ) ); } -}); +} + +module.exports = Toolbar; diff --git a/devtools/client/memory/components/TreeMap.js b/devtools/client/memory/components/TreeMap.js index 9e952b109996..9c0100aa96c8 100644 --- a/devtools/client/memory/components/TreeMap.js +++ b/devtools/client/memory/components/TreeMap.js @@ -4,33 +4,36 @@ "use strict"; -const { DOM: dom, createClass } = require("devtools/client/shared/vendor/react"); +const { DOM: dom, Component } = require("devtools/client/shared/vendor/react"); const { treeMapModel } = require("../models"); const startVisualization = require("./tree-map/start"); -module.exports = createClass({ - displayName: "TreeMap", +class TreeMap extends Component { + static get propTypes() { + return { + treeMap: treeMapModel + }; + } - propTypes: { - treeMap: treeMapModel - }, - - getInitialState() { - return {}; - }, + constructor(props) { + super(props); + this.state = {}; + this._stopVisualization = this._stopVisualization.bind(this); + this._startVisualization = this._startVisualization.bind(this); + } componentDidMount() { const { treeMap } = this.props; if (treeMap && treeMap.report) { this._startVisualization(); } - }, + } shouldComponentUpdate(nextProps) { const oldTreeMap = this.props.treeMap; const newTreeMap = nextProps.treeMap; return oldTreeMap !== newTreeMap; - }, + } componentDidUpdate(prevProps) { this._stopVisualization(); @@ -38,27 +41,27 @@ module.exports = createClass({ if (this.props.treeMap && this.props.treeMap.report) { this._startVisualization(); } - }, + } componentWillUnmount() { if (this.state.stopVisualization) { this.state.stopVisualization(); } - }, + } _stopVisualization() { if (this.state.stopVisualization) { this.state.stopVisualization(); this.setState({ stopVisualization: null }); } - }, + } _startVisualization() { const { container } = this.refs; const { report } = this.props.treeMap; const stopVisualization = startVisualization(container, report); this.setState({ stopVisualization }); - }, + } render() { return dom.div( @@ -68,4 +71,6 @@ module.exports = createClass({ } ); } -}); +} + +module.exports = TreeMap; diff --git a/devtools/client/shared/components/AutoCompletePopup.js b/devtools/client/shared/components/AutoCompletePopup.js index 3136deabbc22..167ee3463ceb 100644 --- a/devtools/client/shared/components/AutoCompletePopup.js +++ b/devtools/client/shared/components/AutoCompletePopup.js @@ -4,48 +4,55 @@ "use strict"; -const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react"); +const { DOM: dom, Component, PropTypes } = require("devtools/client/shared/vendor/react"); -module.exports = createClass({ - displayName: "AutocompletePopup", +class AutocompletePopup extends Component { + static get propTypes() { + return { + /** + * autocompleteProvider takes search-box's entire input text as `filter` argument + * ie. "is:cached pr" + * returned value is array of objects like below + * [{value: "is:cached protocol", displayValue: "protocol"}[, ...]] + * `value` is used to update the search-box input box for given item + * `displayValue` is used to render the autocomplete list + */ + autocompleteProvider: PropTypes.func.isRequired, + filter: PropTypes.string.isRequired, + onItemSelected: PropTypes.func.isRequired, + }; + } - propTypes: { - /** - * autocompleteProvider takes search-box's entire input text as `filter` argument - * ie. "is:cached pr" - * returned value is array of objects like below - * [{value: "is:cached protocol", displayValue: "protocol"}[, ...]] - * `value` is used to update the search-box input box for given item - * `displayValue` is used to render the autocomplete list - */ - autocompleteProvider: PropTypes.func.isRequired, - filter: PropTypes.string.isRequired, - onItemSelected: PropTypes.func.isRequired, - }, - - getInitialState() { - return this.computeState(this.props); - }, + constructor(props, context) { + super(props, context); + this.state = this.computeState(props); + this.computeState = this.computeState.bind(this); + this.jumpToTop = this.jumpToTop.bind(this); + this.jumpToBottom = this.jumpToBottom.bind(this); + this.jumpBy = this.jumpBy.bind(this); + this.select = this.select.bind(this); + this.onMouseDown = this.onMouseDown.bind(this); + } componentWillReceiveProps(nextProps) { if (this.props.filter === nextProps.filter) { return; } this.setState(this.computeState(nextProps)); - }, + } componentDidUpdate() { if (this.refs.selected) { this.refs.selected.scrollIntoView(false); } - }, + } computeState({ autocompleteProvider, filter }) { let list = autocompleteProvider(filter); let selectedIndex = list.length == 1 ? 0 : -1; return { list, selectedIndex }; - }, + } /** * Use this method to select the top-most item @@ -53,7 +60,7 @@ module.exports = createClass({ */ jumpToTop() { this.setState({ selectedIndex: 0 }); - }, + } /** * Use this method to select the bottom-most item @@ -61,7 +68,7 @@ module.exports = createClass({ */ jumpToBottom() { this.setState({ selectedIndex: this.state.list.length - 1 }); - }, + } /** * Increment the selected index with the provided increment value. Will cycle to the @@ -81,7 +88,7 @@ module.exports = createClass({ nextIndex = nextIndex < 0 ? list.length - 1 : nextIndex; } this.setState({selectedIndex: nextIndex}); - }, + } /** * Submit the currently selected item to the onItemSelected callback @@ -91,12 +98,12 @@ module.exports = createClass({ if (this.refs.selected) { this.props.onItemSelected(this.refs.selected.dataset.value); } - }, + } onMouseDown(e) { e.preventDefault(); this.setState({ selectedIndex: Number(e.target.dataset.index) }, this.select); - }, + } render() { let { list } = this.state; @@ -124,4 +131,6 @@ module.exports = createClass({ ) ); } -}); +} + +module.exports = AutocompletePopup; diff --git a/devtools/client/shared/components/Frame.js b/devtools/client/shared/components/Frame.js index 28c687eeca67..3fcb846f49c9 100644 --- a/devtools/client/shared/components/Frame.js +++ b/devtools/client/shared/components/Frame.js @@ -4,7 +4,7 @@ "use strict"; -const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react"); +const { DOM: dom, Component, PropTypes } = require("devtools/client/shared/vendor/react"); const { getSourceNames, parseURL, isScratchpadScheme, getSourceMappedFile } = require("devtools/client/shared/source-utils"); const { LocalizationHelper } = require("devtools/shared/l10n"); @@ -12,34 +12,34 @@ const { LocalizationHelper } = require("devtools/shared/l10n"); const l10n = new LocalizationHelper("devtools/client/locales/components.properties"); const webl10n = new LocalizationHelper("devtools/client/locales/webconsole.properties"); -module.exports = createClass({ - displayName: "Frame", +class Frame extends Component { + static get propTypes() { + return { + // SavedFrame, or an object containing all the required properties. + frame: PropTypes.shape({ + functionDisplayName: PropTypes.string, + source: PropTypes.string.isRequired, + line: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]), + column: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]), + }).isRequired, + // Clicking on the frame link -- probably should link to the debugger. + onClick: PropTypes.func.isRequired, + // Option to display a function name before the source link. + showFunctionName: PropTypes.bool, + // Option to display a function name even if it's anonymous. + showAnonymousFunctionName: PropTypes.bool, + // Option to display a host name after the source link. + showHost: PropTypes.bool, + // Option to display a host name if the filename is empty or just '/' + showEmptyPathAsHost: PropTypes.bool, + // Option to display a full source instead of just the filename. + showFullSourceUrl: PropTypes.bool, + // Service to enable the source map feature for console. + sourceMapService: PropTypes.object, + }; + } - propTypes: { - // SavedFrame, or an object containing all the required properties. - frame: PropTypes.shape({ - functionDisplayName: PropTypes.string, - source: PropTypes.string.isRequired, - line: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]), - column: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]), - }).isRequired, - // Clicking on the frame link -- probably should link to the debugger. - onClick: PropTypes.func.isRequired, - // Option to display a function name before the source link. - showFunctionName: PropTypes.bool, - // Option to display a function name even if it's anonymous. - showAnonymousFunctionName: PropTypes.bool, - // Option to display a host name after the source link. - showHost: PropTypes.bool, - // Option to display a host name if the filename is empty or just '/' - showEmptyPathAsHost: PropTypes.bool, - // Option to display a full source instead of just the filename. - showFullSourceUrl: PropTypes.bool, - // Service to enable the source map feature for console. - sourceMapService: PropTypes.object, - }, - - getDefaultProps() { + static get defaultProps() { return { showFunctionName: false, showAnonymousFunctionName: false, @@ -47,7 +47,13 @@ module.exports = createClass({ showEmptyPathAsHost: false, showFullSourceUrl: false, }; - }, + } + + constructor(props) { + super(props); + this._locationChanged = this._locationChanged.bind(this); + this.getSourceForClick = this.getSourceForClick.bind(this); + } componentWillMount() { if (this.props.sourceMapService) { @@ -55,7 +61,7 @@ module.exports = createClass({ this.props.sourceMapService.subscribe(source, line, column, this._locationChanged); } - }, + } componentWillUnmount() { if (this.props.sourceMapService) { @@ -63,7 +69,7 @@ module.exports = createClass({ this.props.sourceMapService.unsubscribe(source, line, column, this._locationChanged); } - }, + } _locationChanged(isSourceMapped, url, line, column) { let newState = { @@ -79,7 +85,7 @@ module.exports = createClass({ } this.setState(newState); - }, + } /** * Utility method to convert the Frame object model to the @@ -95,7 +101,7 @@ module.exports = createClass({ column, functionDisplayName: this.props.frame.functionDisplayName, }; - }, + } render() { let frame, isSourceMapped; @@ -235,4 +241,6 @@ module.exports = createClass({ return dom.span(attributes, ...elements); } -}); +} + +module.exports = Frame; diff --git a/devtools/client/shared/components/HSplitBox.js b/devtools/client/shared/components/HSplitBox.js index 402e1a9cb4ea..68e228eeca6e 100644 --- a/devtools/client/shared/components/HSplitBox.js +++ b/devtools/client/shared/components/HSplitBox.js @@ -25,61 +25,67 @@ const { DOM: dom, - createClass, + Component, PropTypes, } = require("devtools/client/shared/vendor/react"); const { assert } = require("devtools/shared/DevToolsUtils"); -module.exports = createClass({ - displayName: "HSplitBox", +class HSplitBox extends Component { + static get propTypes() { + return { + // The contents of the start pane. + start: PropTypes.any.isRequired, - propTypes: { - // The contents of the start pane. - start: PropTypes.any.isRequired, + // The contents of the end pane. + end: PropTypes.any.isRequired, - // The contents of the end pane. - end: PropTypes.any.isRequired, + // The relative width of the start pane, expressed as a number between 0 and + // 1. The relative width of the end pane is 1 - startWidth. For example, + // with startWidth = .5, both panes are of equal width; with startWidth = + // .25, the start panel will take up 1/4 width and the end panel will take + // up 3/4 width. + startWidth: PropTypes.number, - // The relative width of the start pane, expressed as a number between 0 and - // 1. The relative width of the end pane is 1 - startWidth. For example, - // with startWidth = .5, both panes are of equal width; with startWidth = - // .25, the start panel will take up 1/4 width and the end panel will take - // up 3/4 width. - startWidth: PropTypes.number, + // A minimum css width value for the start and end panes. + minStartWidth: PropTypes.any, + minEndWidth: PropTypes.any, - // A minimum css width value for the start and end panes. - minStartWidth: PropTypes.any, - minEndWidth: PropTypes.any, + // A callback fired when the user drags the splitter to resize the relative + // pane widths. The function is passed the startWidth value that would put + // the splitter underneath the users mouse. + onResize: PropTypes.func.isRequired, + }; + } - // A callback fired when the user drags the splitter to resize the relative - // pane widths. The function is passed the startWidth value that would put - // the splitter underneath the users mouse. - onResize: PropTypes.func.isRequired, - }, - - getDefaultProps() { + static get defaultProps() { return { startWidth: 0.5, minStartWidth: "20px", minEndWidth: "20px", }; - }, + } - getInitialState() { - return { + constructor(props) { + super(props); + + this.state = { mouseDown: false }; - }, + + this._onMouseDown = this._onMouseDown.bind(this); + this._onMouseUp = this._onMouseUp.bind(this); + this._onMouseMove = this._onMouseMove.bind(this); + } componentDidMount() { document.defaultView.top.addEventListener("mouseup", this._onMouseUp); document.defaultView.top.addEventListener("mousemove", this._onMouseMove); - }, + } componentWillUnmount() { document.defaultView.top.removeEventListener("mouseup", this._onMouseUp); document.defaultView.top.removeEventListener("mousemove", this._onMouseMove); - }, + } _onMouseDown(event) { if (event.button !== 0) { @@ -88,7 +94,7 @@ module.exports = createClass({ this.setState({ mouseDown: true }); event.preventDefault(); - }, + } _onMouseUp(event) { if (event.button !== 0 || !this.state.mouseDown) { @@ -97,7 +103,7 @@ module.exports = createClass({ this.setState({ mouseDown: false }); event.preventDefault(); - }, + } _onMouseMove(event) { if (!this.state.mouseDown) { @@ -113,7 +119,7 @@ module.exports = createClass({ this.props.onResize(relative / width); event.preventDefault(); - }, + } render() { /* eslint-disable no-shadow */ @@ -149,4 +155,6 @@ module.exports = createClass({ ) ); } -}); +} + +module.exports = HSplitBox; diff --git a/devtools/client/shared/components/NotificationBox.js b/devtools/client/shared/components/NotificationBox.js index 838d2ac297bf..82f11243141d 100644 --- a/devtools/client/shared/components/NotificationBox.js +++ b/devtools/client/shared/components/NotificationBox.js @@ -10,7 +10,7 @@ const { LocalizationHelper } = require("devtools/shared/l10n"); const l10n = new LocalizationHelper("devtools/client/locales/components.properties"); // Shortcuts -const { PropTypes, createClass, DOM } = React; +const { PropTypes, Component, DOM } = React; const { div, span, button } = DOM; // Priority Levels @@ -34,72 +34,81 @@ const PriorityLevels = { * See also MDN for more info about : * https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/notificationbox */ -var NotificationBox = createClass({ - displayName: "NotificationBox", - - propTypes: { - // List of notifications appended into the box. - notifications: PropTypes.arrayOf(PropTypes.shape({ - // label to appear on the notification. - label: PropTypes.string.isRequired, - - // Value used to identify the notification - value: PropTypes.string.isRequired, - - // URL of image to appear on the notification. If "" then an icon - // appropriate for the priority level is used. - image: PropTypes.string.isRequired, - - // Notification priority; see Priority Levels. - priority: PropTypes.number.isRequired, - - // Array of button descriptions to appear on the notification. - buttons: PropTypes.arrayOf(PropTypes.shape({ - // Function to be called when the button is activated. - // This function is passed three arguments: - // 1) the NotificationBox component the button is associated with - // 2) the button description as passed to appendNotification. - // 3) the element which was the target of the button press event. - // If the return value from this function is not True, then the - // notification is closed. The notification is also not closed - // if an error is thrown. - callback: PropTypes.func.isRequired, - - // The label to appear on the button. +class NotificationBox extends Component { + static get propTypes() { + return { + // List of notifications appended into the box. + notifications: PropTypes.arrayOf(PropTypes.shape({ + // label to appear on the notification. label: PropTypes.string.isRequired, - // The accesskey attribute set on the