Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | "use strict"; |
michael@0 | 6 | |
michael@0 | 7 | const dbginfo = new WeakMap(); |
michael@0 | 8 | |
michael@0 | 9 | // These functions implement search within the debugger. Since |
michael@0 | 10 | // search in the debugger is different from other components, |
michael@0 | 11 | // we can't use search.js CodeMirror addon. This is a slightly |
michael@0 | 12 | // modified version of that addon. Depends on searchcursor.js. |
michael@0 | 13 | |
michael@0 | 14 | function SearchState() { |
michael@0 | 15 | this.posFrom = this.posTo = this.query = null; |
michael@0 | 16 | } |
michael@0 | 17 | |
michael@0 | 18 | function getSearchState(cm) { |
michael@0 | 19 | return cm.state.search || (cm.state.search = new SearchState()); |
michael@0 | 20 | } |
michael@0 | 21 | |
michael@0 | 22 | function getSearchCursor(cm, query, pos) { |
michael@0 | 23 | // If the query string is all lowercase, do a case insensitive search. |
michael@0 | 24 | return cm.getSearchCursor(query, pos, |
michael@0 | 25 | typeof query == "string" && query == query.toLowerCase()); |
michael@0 | 26 | } |
michael@0 | 27 | |
michael@0 | 28 | /** |
michael@0 | 29 | * If there's a saved search, selects the next results. |
michael@0 | 30 | * Otherwise, creates a new search and selects the first |
michael@0 | 31 | * result. |
michael@0 | 32 | */ |
michael@0 | 33 | function doSearch(ctx, rev, query) { |
michael@0 | 34 | let { cm } = ctx; |
michael@0 | 35 | let state = getSearchState(cm); |
michael@0 | 36 | |
michael@0 | 37 | if (state.query) { |
michael@0 | 38 | searchNext(ctx, rev); |
michael@0 | 39 | return; |
michael@0 | 40 | } |
michael@0 | 41 | |
michael@0 | 42 | cm.operation(function () { |
michael@0 | 43 | if (state.query) return; |
michael@0 | 44 | |
michael@0 | 45 | state.query = query; |
michael@0 | 46 | state.posFrom = state.posTo = { line: 0, ch: 0 }; |
michael@0 | 47 | searchNext(ctx, rev); |
michael@0 | 48 | }); |
michael@0 | 49 | } |
michael@0 | 50 | |
michael@0 | 51 | /** |
michael@0 | 52 | * Selects the next result of a saved search. |
michael@0 | 53 | */ |
michael@0 | 54 | function searchNext(ctx, rev) { |
michael@0 | 55 | let { cm, ed } = ctx; |
michael@0 | 56 | cm.operation(function () { |
michael@0 | 57 | let state = getSearchState(cm) |
michael@0 | 58 | let cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo); |
michael@0 | 59 | |
michael@0 | 60 | if (!cursor.find(rev)) { |
michael@0 | 61 | cursor = getSearchCursor(cm, state.query, rev ? |
michael@0 | 62 | { line: cm.lastLine(), ch: null } : { line: cm.firstLine(), ch: 0 }); |
michael@0 | 63 | if (!cursor.find(rev)) |
michael@0 | 64 | return; |
michael@0 | 65 | } |
michael@0 | 66 | |
michael@0 | 67 | ed.alignLine(cursor.from().line, "center"); |
michael@0 | 68 | cm.setSelection(cursor.from(), cursor.to()); |
michael@0 | 69 | state.posFrom = cursor.from(); |
michael@0 | 70 | state.posTo = cursor.to(); |
michael@0 | 71 | }); |
michael@0 | 72 | } |
michael@0 | 73 | |
michael@0 | 74 | /** |
michael@0 | 75 | * Clears the currently saved search. |
michael@0 | 76 | */ |
michael@0 | 77 | function clearSearch(cm) { |
michael@0 | 78 | let state = getSearchState(cm); |
michael@0 | 79 | |
michael@0 | 80 | if (!state.query) |
michael@0 | 81 | return; |
michael@0 | 82 | |
michael@0 | 83 | state.query = null; |
michael@0 | 84 | } |
michael@0 | 85 | |
michael@0 | 86 | // Exported functions |
michael@0 | 87 | |
michael@0 | 88 | /** |
michael@0 | 89 | * This function is called whenever Editor is extended with functions |
michael@0 | 90 | * from this module. See Editor.extend for more info. |
michael@0 | 91 | */ |
michael@0 | 92 | function initialize(ctx) { |
michael@0 | 93 | let { ed } = ctx; |
michael@0 | 94 | |
michael@0 | 95 | dbginfo.set(ed, { |
michael@0 | 96 | breakpoints: {}, |
michael@0 | 97 | debugLocation: null |
michael@0 | 98 | }); |
michael@0 | 99 | } |
michael@0 | 100 | |
michael@0 | 101 | /** |
michael@0 | 102 | * True if editor has a visual breakpoint at that line, false |
michael@0 | 103 | * otherwise. |
michael@0 | 104 | */ |
michael@0 | 105 | function hasBreakpoint(ctx, line) { |
michael@0 | 106 | let { cm } = ctx; |
michael@0 | 107 | let markers = cm.lineInfo(line).gutterMarkers; |
michael@0 | 108 | |
michael@0 | 109 | return markers != null && |
michael@0 | 110 | markers.breakpoints.classList.contains("breakpoint"); |
michael@0 | 111 | } |
michael@0 | 112 | |
michael@0 | 113 | /** |
michael@0 | 114 | * Adds a visual breakpoint for a specified line. Third |
michael@0 | 115 | * parameter 'cond' can hold any object. |
michael@0 | 116 | * |
michael@0 | 117 | * After adding a breakpoint, this function makes Editor to |
michael@0 | 118 | * emit a breakpointAdded event. |
michael@0 | 119 | */ |
michael@0 | 120 | function addBreakpoint(ctx, line, cond) { |
michael@0 | 121 | if (hasBreakpoint(ctx, line)) |
michael@0 | 122 | return; |
michael@0 | 123 | |
michael@0 | 124 | let { ed, cm } = ctx; |
michael@0 | 125 | let meta = dbginfo.get(ed); |
michael@0 | 126 | let info = cm.lineInfo(line); |
michael@0 | 127 | |
michael@0 | 128 | ed.addMarker(line, "breakpoints", "breakpoint"); |
michael@0 | 129 | meta.breakpoints[line] = { condition: cond }; |
michael@0 | 130 | |
michael@0 | 131 | info.handle.on("delete", function onDelete() { |
michael@0 | 132 | info.handle.off("delete", onDelete); |
michael@0 | 133 | meta.breakpoints[info.line] = null; |
michael@0 | 134 | }); |
michael@0 | 135 | |
michael@0 | 136 | ed.emit("breakpointAdded", line); |
michael@0 | 137 | } |
michael@0 | 138 | |
michael@0 | 139 | /** |
michael@0 | 140 | * Removes a visual breakpoint from a specified line and |
michael@0 | 141 | * makes Editor to emit a breakpointRemoved event. |
michael@0 | 142 | */ |
michael@0 | 143 | function removeBreakpoint(ctx, line) { |
michael@0 | 144 | if (!hasBreakpoint(ctx, line)) |
michael@0 | 145 | return; |
michael@0 | 146 | |
michael@0 | 147 | let { ed, cm } = ctx; |
michael@0 | 148 | let meta = dbginfo.get(ed); |
michael@0 | 149 | let info = cm.lineInfo(line); |
michael@0 | 150 | |
michael@0 | 151 | meta.breakpoints[info.line] = null; |
michael@0 | 152 | ed.removeMarker(info.line, "breakpoints", "breakpoint"); |
michael@0 | 153 | ed.emit("breakpointRemoved", line); |
michael@0 | 154 | } |
michael@0 | 155 | |
michael@0 | 156 | /** |
michael@0 | 157 | * Returns a list of all breakpoints in the current Editor. |
michael@0 | 158 | */ |
michael@0 | 159 | function getBreakpoints(ctx) { |
michael@0 | 160 | let { ed } = ctx; |
michael@0 | 161 | let meta = dbginfo.get(ed); |
michael@0 | 162 | |
michael@0 | 163 | return Object.keys(meta.breakpoints).reduce((acc, line) => { |
michael@0 | 164 | if (meta.breakpoints[line] != null) |
michael@0 | 165 | acc.push({ line: line, condition: meta.breakpoints[line].condition }); |
michael@0 | 166 | return acc; |
michael@0 | 167 | }, []); |
michael@0 | 168 | } |
michael@0 | 169 | |
michael@0 | 170 | /** |
michael@0 | 171 | * Saves a debug location information and adds a visual anchor to |
michael@0 | 172 | * the breakpoints gutter. This is used by the debugger UI to |
michael@0 | 173 | * display the line on which the Debugger is currently paused. |
michael@0 | 174 | */ |
michael@0 | 175 | function setDebugLocation(ctx, line) { |
michael@0 | 176 | let { ed } = ctx; |
michael@0 | 177 | let meta = dbginfo.get(ed); |
michael@0 | 178 | |
michael@0 | 179 | clearDebugLocation(ctx); |
michael@0 | 180 | |
michael@0 | 181 | meta.debugLocation = line; |
michael@0 | 182 | ed.addMarker(line, "breakpoints", "debugLocation"); |
michael@0 | 183 | ed.addLineClass(line, "debug-line"); |
michael@0 | 184 | } |
michael@0 | 185 | |
michael@0 | 186 | /** |
michael@0 | 187 | * Returns a line number that corresponds to the current debug |
michael@0 | 188 | * location. |
michael@0 | 189 | */ |
michael@0 | 190 | function getDebugLocation(ctx) { |
michael@0 | 191 | let { ed } = ctx; |
michael@0 | 192 | let meta = dbginfo.get(ed); |
michael@0 | 193 | |
michael@0 | 194 | return meta.debugLocation; |
michael@0 | 195 | } |
michael@0 | 196 | |
michael@0 | 197 | /** |
michael@0 | 198 | * Clears the debug location. Clearing the debug location |
michael@0 | 199 | * also removes a visual anchor from the breakpoints gutter. |
michael@0 | 200 | */ |
michael@0 | 201 | function clearDebugLocation(ctx) { |
michael@0 | 202 | let { ed } = ctx; |
michael@0 | 203 | let meta = dbginfo.get(ed); |
michael@0 | 204 | |
michael@0 | 205 | if (meta.debugLocation != null) { |
michael@0 | 206 | ed.removeMarker(meta.debugLocation, "breakpoints", "debugLocation"); |
michael@0 | 207 | ed.removeLineClass(meta.debugLocation, "debug-line"); |
michael@0 | 208 | meta.debugLocation = null; |
michael@0 | 209 | } |
michael@0 | 210 | } |
michael@0 | 211 | |
michael@0 | 212 | /** |
michael@0 | 213 | * Starts a new search. |
michael@0 | 214 | */ |
michael@0 | 215 | function find(ctx, query) { |
michael@0 | 216 | clearSearch(ctx.cm); |
michael@0 | 217 | doSearch(ctx, false, query); |
michael@0 | 218 | } |
michael@0 | 219 | |
michael@0 | 220 | /** |
michael@0 | 221 | * Finds the next item based on the currently saved search. |
michael@0 | 222 | */ |
michael@0 | 223 | function findNext(ctx, query) { |
michael@0 | 224 | doSearch(ctx, false, query); |
michael@0 | 225 | } |
michael@0 | 226 | |
michael@0 | 227 | /** |
michael@0 | 228 | * Finds the previous item based on the currently saved search. |
michael@0 | 229 | */ |
michael@0 | 230 | function findPrev(ctx, query) { |
michael@0 | 231 | doSearch(ctx, true, query); |
michael@0 | 232 | } |
michael@0 | 233 | |
michael@0 | 234 | |
michael@0 | 235 | // Export functions |
michael@0 | 236 | |
michael@0 | 237 | [ |
michael@0 | 238 | initialize, hasBreakpoint, addBreakpoint, removeBreakpoint, |
michael@0 | 239 | getBreakpoints, setDebugLocation, getDebugLocation, |
michael@0 | 240 | clearDebugLocation, find, findNext, findPrev |
michael@0 | 241 | ].forEach(function (func) { module.exports[func.name] = func; }); |