Worker.js (6377B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 const { 8 createFactory, 9 PureComponent, 10 } = require("resource://devtools/client/shared/vendor/react.mjs"); 11 12 const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs"); 13 const { 14 connect, 15 } = require("resource://devtools/client/shared/vendor/react-redux.js"); 16 17 const { 18 a, 19 img, 20 p, 21 section, 22 span, 23 } = require("resource://devtools/client/shared/vendor/react-dom-factories.js"); 24 25 const { 26 getUnicodeUrlPath, 27 } = require("resource://devtools/client/shared/unicode-url.js"); 28 29 const FluentReact = require("resource://devtools/client/shared/vendor/fluent-react.js"); 30 const Localized = createFactory(FluentReact.Localized); 31 const { 32 l10n, 33 } = require("resource://devtools/client/application/src/modules/l10n.js"); 34 35 const { 36 services, 37 } = require("resource://devtools/client/application/src/modules/application-services.js"); 38 const Types = require("resource://devtools/client/application/src/types/index.js"); 39 40 const { 41 startWorker, 42 } = require("resource://devtools/client/application/src/actions/workers.js"); 43 44 const UIButton = createFactory( 45 require("resource://devtools/client/application/src/components/ui/UIButton.js") 46 ); 47 48 /** 49 * This component is dedicated to display a worker, more accurately a service worker, in 50 * the list of workers displayed in the application panel. It displays information about 51 * the worker as well as action links and buttons to interact with the worker (e.g. debug, 52 * unregister, update etc...). 53 */ 54 class Worker extends PureComponent { 55 static get propTypes() { 56 return { 57 isDebugEnabled: PropTypes.bool.isRequired, 58 worker: PropTypes.shape(Types.worker).isRequired, 59 // this prop get automatically injected via `connect` 60 dispatch: PropTypes.func.isRequired, 61 }; 62 } 63 64 constructor(props) { 65 super(props); 66 67 this.debug = this.debug.bind(this); 68 this.viewSource = this.viewSource.bind(this); 69 this.start = this.start.bind(this); 70 } 71 72 debug() { 73 if (!this.isRunning()) { 74 console.log("Service workers cannot be debugged if they are not running"); 75 return; 76 } 77 78 services.openWorkerInDebugger(this.props.worker.workerDescriptorFront); 79 } 80 81 viewSource() { 82 if (!this.isRunning()) { 83 console.log( 84 "Service workers cannot be inspected if they are not running" 85 ); 86 return; 87 } 88 89 services.viewWorkerSource(this.props.worker.workerDescriptorFront); 90 } 91 92 start() { 93 if (!this.isActive() || this.isRunning()) { 94 console.log("Running or inactive service workers cannot be started"); 95 return; 96 } 97 98 this.props.dispatch(startWorker(this.props.worker)); 99 } 100 101 isRunning() { 102 // We know the worker is running if it has a worker actor. 103 return !!this.props.worker.workerDescriptorFront; 104 } 105 106 isActive() { 107 return this.props.worker.state === Ci.nsIServiceWorkerInfo.STATE_ACTIVATED; 108 } 109 110 getLocalizedStatus() { 111 if (this.isActive() && this.isRunning()) { 112 return l10n.getString("serviceworker-worker-status-running"); 113 } else if (this.isActive()) { 114 return l10n.getString("serviceworker-worker-status-stopped"); 115 } 116 // NOTE: this is already localized by the service worker front 117 // (strings are in debugger.properties) 118 return this.props.worker.stateText; 119 } 120 121 getClassNameForStatus() { 122 const { state } = this.props.worker; 123 124 switch (state) { 125 case Ci.nsIServiceWorkerInfo.STATE_PARSED: 126 case Ci.nsIServiceWorkerInfo.STATE_INSTALLING: 127 return "worker__status--installing"; 128 case Ci.nsIServiceWorkerInfo.STATE_INSTALLED: 129 case Ci.nsIServiceWorkerInfo.STATE_ACTIVATING: 130 return "worker__status--waiting"; 131 case Ci.nsIServiceWorkerInfo.STATE_ACTIVATED: 132 return "worker__status--active"; 133 } 134 135 return "worker__status--default"; 136 } 137 138 formatSource(source) { 139 const parts = source.split("/"); 140 return getUnicodeUrlPath(parts[parts.length - 1]); 141 } 142 143 renderInspectLink(url) { 144 // avoid rendering the inspect link if sw is not running 145 const isDisabled = !this.isRunning(); 146 // view source instead of debugging when debugging sw is not available 147 const callbackFn = this.props.isDebugEnabled ? this.debug : this.viewSource; 148 149 const sourceUrl = span( 150 { className: "js-source-url" }, 151 this.formatSource(url) 152 ); 153 154 return isDisabled 155 ? sourceUrl 156 : a( 157 { 158 onClick: callbackFn, 159 title: url, 160 href: "#", 161 className: "js-inspect-link", 162 }, 163 sourceUrl, 164 "\u00A0", // 165 Localized( 166 { 167 id: "serviceworker-worker-inspect-icon", 168 attrs: { 169 alt: true, 170 }, 171 }, 172 img({ 173 src: "chrome://devtools/skin/images/application-debug.svg", 174 }) 175 ) 176 ); 177 } 178 179 renderStartButton() { 180 // avoid rendering the button at all for workers that are either running, 181 // or in a state that prevents them from starting (like waiting) 182 if (this.isRunning() || !this.isActive()) { 183 return null; 184 } 185 186 return Localized( 187 { id: "serviceworker-worker-start3" }, 188 UIButton({ 189 onClick: this.start, 190 className: `js-start-button`, 191 size: "micro", 192 }) 193 ); 194 } 195 196 render() { 197 const { worker } = this.props; 198 const statusText = this.getLocalizedStatus(); 199 const statusClassName = this.getClassNameForStatus(); 200 201 return section( 202 { className: "worker js-sw-worker" }, 203 p( 204 { className: "worker__icon" }, 205 img({ 206 className: "worker__icon-image", 207 src: "chrome://devtools/skin/images/debugging-workers.svg", 208 }) 209 ), 210 p({ className: "worker__source" }, this.renderInspectLink(worker.url)), 211 p( 212 { className: "worker__misc" }, 213 span( 214 { className: `js-worker-status worker__status ${statusClassName}` }, 215 statusText 216 ), 217 " ", 218 this.renderStartButton() 219 ) 220 ); 221 } 222 } 223 224 const mapDispatchToProps = dispatch => ({ dispatch }); 225 module.exports = connect(mapDispatchToProps)(Worker);