summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger/content/reducers/breakpoints.js
blob: 7e42098e89ad4e58abf6dce529488a43bd9aa9b0 (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 constants = require("../constants");
const Immutable = require("devtools/client/shared/vendor/seamless-immutable");
const { mergeIn, setIn, deleteIn } = require("../utils");
const { makeLocationId } = require("../queries");

const initialState = Immutable({
  breakpoints: {}
});

// Return the first argument that is a string, or null if nothing is a
// string.
function firstString(...args) {
  for (var arg of args) {
    if (typeof arg === "string") {
      return arg;
    }
  }
  return null;
}

function update(state = initialState, action, emitChange) {
  switch (action.type) {
    case constants.ADD_BREAKPOINT: {
      const id = makeLocationId(action.breakpoint.location);

      if (action.status === "start") {
        const existingBp = state.breakpoints[id];
        const bp = existingBp || Immutable(action.breakpoint);

        state = setIn(state, ["breakpoints", id], bp.merge({
          disabled: false,
          loading: true,
        // We want to do an OR here, but we can't because we need
        // empty strings to be truthy, i.e. an empty string is a valid
        // condition.
          condition: firstString(action.condition, bp.condition)
        }));

        emitChange(existingBp ? "breakpoint-enabled" : "breakpoint-added",
                 state.breakpoints[id]);
        return state;
      }
      else if (action.status === "done") {
        const { actor, text } = action.value;
        let { actualLocation } = action.value;

      // If the breakpoint moved, update the map
        if (actualLocation) {
        // XXX Bug 1227417: The `setBreakpoint` RDP request rdp
        // request returns an `actualLocation` field that doesn't
        // conform to the regular { actor, line } location shape, but
        // it has a `source` field. We should fix that.
          actualLocation = { actor: actualLocation.source.actor,
                           line: actualLocation.line };

          state = deleteIn(state, ["breakpoints", id]);

          const movedId = makeLocationId(actualLocation);
          const currentBp = state.breakpoints[movedId] || Immutable(action.breakpoint);
          const prevLocation = action.breakpoint.location;
          const newBp = currentBp.merge({ location: actualLocation });
          state = setIn(state, ["breakpoints", movedId], newBp);

          emitChange("breakpoint-moved", {
            breakpoint: newBp,
            prevLocation: prevLocation
          });
        }

        const finalLocation = (
        actualLocation ? actualLocation : action.breakpoint.location
      );
        const finalLocationId = makeLocationId(finalLocation);
        state = mergeIn(state, ["breakpoints", finalLocationId], {
          disabled: false,
          loading: false,
          actor: actor,
          text: text
        });
        emitChange("breakpoint-updated", state.breakpoints[finalLocationId]);
        return state;
      }
    else if (action.status === "error") {
      // Remove the optimistic update
      emitChange("breakpoint-removed", state.breakpoints[id]);
      return deleteIn(state, ["breakpoints", id]);
    }
      break;
    }

    case constants.REMOVE_BREAKPOINT: {
      if (action.status === "done") {
        const id = makeLocationId(action.breakpoint.location);
        const bp = state.breakpoints[id];

        if (action.disabled) {
          state = mergeIn(state, ["breakpoints", id],
                        { loading: false, disabled: true });
          emitChange("breakpoint-disabled", state.breakpoints[id]);
          return state;
        }

        state = deleteIn(state, ["breakpoints", id]);
        emitChange("breakpoint-removed", bp);
        return state;
      }
      break;
    }

    case constants.SET_BREAKPOINT_CONDITION: {
      const id = makeLocationId(action.breakpoint.location);
      const bp = state.breakpoints[id];
      emitChange("breakpoint-condition-updated", bp);

      if (!action.status) {
      // No status means that it wasn't a remote request. Just update
      // the condition locally.
        return mergeIn(state, ["breakpoints", id], {
          condition: action.condition
        });
      }
      else if (action.status === "start") {
        return mergeIn(state, ["breakpoints", id], {
          loading: true,
          condition: action.condition
        });
      }
    else if (action.status === "done") {
      return mergeIn(state, ["breakpoints", id], {
        loading: false,
        condition: action.condition,
        // Setting a condition creates a new breakpoint client as of
        // now, so we need to update the actor
        actor: action.value.actor
      });
    }
    else if (action.status === "error") {
      emitChange("breakpoint-removed", bp);
      return deleteIn(state, ["breakpoints", id]);
    }

      break;
    }}

  return state;
}

module.exports = update;