summaryrefslogtreecommitdiffstats
path: root/devtools/client/application/src/components/service-workers/Registration.js
blob: 97569f57e2ced13bd5a44532437ea08c610418d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

const {
  createFactory,
  PureComponent,
} = require("resource://devtools/client/shared/vendor/react.js");

const {
  connect,
} = require("resource://devtools/client/shared/vendor/react-redux.js");
const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");

const {
  article,
  aside,
  h2,
  header,
  li,
  p,
  time,
  ul,
} = require("resource://devtools/client/shared/vendor/react-dom-factories.js");

const {
  getUnicodeUrl,
} = require("resource://devtools/client/shared/unicode-url.js");

const FluentReact = require("resource://devtools/client/shared/vendor/fluent-react.js");
const Localized = createFactory(FluentReact.Localized);

const Types = require("resource://devtools/client/application/src/types/index.js");

const {
  unregisterWorker,
} = require("resource://devtools/client/application/src/actions/workers.js");

const UIButton = createFactory(
  require("resource://devtools/client/application/src/components/ui/UIButton.js")
);

const Worker = createFactory(
  require("resource://devtools/client/application/src/components/service-workers/Worker.js")
);

/**
 * This component is dedicated to display a service worker registration, along
 * the list of attached workers to it.
 * It displays information about the registration as well as an Unregister
 * button.
 */
class Registration extends PureComponent {
  static get propTypes() {
    return {
      className: PropTypes.string,
      isDebugEnabled: PropTypes.bool.isRequired,
      registration: PropTypes.shape(Types.registration).isRequired,
      // this prop get automatically injected via `connect`
      dispatch: PropTypes.func.isRequired,
    };
  }

  constructor(props) {
    super(props);

    this.unregister = this.unregister.bind(this);
  }

  unregister() {
    this.props.dispatch(unregisterWorker(this.props.registration));
  }

  isActive() {
    const { workers } = this.props.registration;
    return workers.some(
      x => x.state === Ci.nsIServiceWorkerInfo.STATE_ACTIVATED
    );
  }

  formatScope(scope) {
    const [, remainder] = getUnicodeUrl(scope).split("://");
    // remove the last slash from the url, if present
    // or return the full scope if there's no remainder
    return remainder ? remainder.replace(/\/$/, "") : scope;
  }

  render() {
    const { registration, isDebugEnabled, className } = this.props;

    const unregisterButton = this.isActive()
      ? Localized(
          { id: "serviceworker-worker-unregister" },
          UIButton({
            onClick: this.unregister,
            className: "js-unregister-button",
          })
        )
      : null;

    const lastUpdated = registration.lastUpdateTime
      ? Localized(
          {
            id: "serviceworker-worker-updated",
            // XXX: $date should normally be a Date object, but we pass the timestamp as a
            // workaround. See Bug 1465718. registration.lastUpdateTime is in microseconds,
            // convert to a valid timestamp in milliseconds by dividing by 1000.
            $date: registration.lastUpdateTime / 1000,
            time: time({ className: "js-sw-updated" }),
          },
          p({ className: "registration__updated-time" })
        )
      : null;

    const scope = h2(
      {
        title: registration.scope,
        className: "registration__scope js-sw-scope devtools-ellipsis-text",
      },
      this.formatScope(registration.scope)
    );

    return li(
      { className: className ? className : "" },
      article(
        { className: "registration js-sw-container" },
        header({ className: "registration__header" }, scope, lastUpdated),
        aside({ className: "registration__controls" }, unregisterButton),
        // render list of workers
        ul(
          { className: "registration__workers" },
          registration.workers.map(worker => {
            return li(
              {
                key: worker.id,
                className: "registration__workers-item",
              },
              Worker({
                worker,
                isDebugEnabled,
              })
            );
          })
        )
      )
    );
  }
}

const mapDispatchToProps = dispatch => ({ dispatch });
module.exports = connect(mapDispatchToProps)(Registration);