1 /* See license.txt for terms of usage */ 2 3 define([ 4 "firebug/lib/object", 5 "firebug/lib/locale", 6 "firebug/lib/string", 7 "firebug/lib/domplate", 8 "firebug/lib/dom", 9 "firebug/lib/css", 10 "firebug/lib/events", 11 "firebug/cookies/cookieUtils" 12 ], 13 function(Obj, Locale, Str, Domplate, Dom, Css, Events, CookieUtils) { 14 15 with (Domplate) { 16 17 // ********************************************************************************************* // 18 // Constants 19 20 const panelName = "cookies"; 21 22 // ********************************************************************************************* // 23 // Implementation 24 25 var Breakpoints = 26 { 27 breakOnCookie: function(context, cookie, action) 28 { 29 if (FBTrace.DBG_COOKIES) 30 FBTrace.sysout("cookies.breakOnCookie; " + action); 31 32 var halt = false; 33 var conditionIsFalse = false; 34 35 // If there is an enabled breakpoint with condition: 36 // 1) break if the condition is evaluated to true. 37 var bp = context.cookies.breakpoints.findBreakpoint(CookieUtils.makeCookieObject(cookie)); 38 if (bp && bp.checked) 39 { 40 halt = true; 41 if (bp.condition) 42 { 43 halt = bp.evaluateCondition(context, cookie); 44 conditionIsFalse = !halt; 45 } 46 } 47 48 // 2) If break on next flag is set and there is no condition evaluated to false, 49 // break with "break on next" breaking cause (this new breaking cause can override 50 // an existing one that is set when evaluating a breakpoint condition). 51 if (context.breakOnCookie && !conditionIsFalse) 52 { 53 context.breakingCause = { 54 title: Locale.$STR("cookies.Break On Cookie"), 55 message: Str.cropString(unescape(cookie.name + "; " + cookie.value), 200) 56 }; 57 halt = true; 58 } 59 60 // Ignore if there is no reason to break. 61 if (!halt) 62 return; 63 64 // Even if the execution was stopped at breakpoint reset the global 65 // breakOnCookie flag. 66 context.breakOnCookie = false; 67 68 this.breakNow(context); 69 70 // Clear breakpoint associated with removed cookie. 71 if (action == "deleted") 72 { 73 breakpoints.removeBreakpoint(bp); 74 context.invalidatePanels("breakpoints"); 75 } 76 }, 77 78 breakNow: function(context) 79 { 80 if (Firebug.Breakpoint && Firebug.Breakpoint.updatePanelTab) 81 { 82 var panel = context.getPanel(panelName, true); 83 Firebug.Breakpoint.updatePanelTab(panel, false); 84 Firebug.Breakpoint.breakNow(context.getPanel(panelName, true)); 85 } 86 }, 87 88 getContextMenuItems: function(cookie, target, context) 89 { 90 var items = []; 91 items.push("-"); 92 93 var cookieName = Str.cropString(cookie.cookie.name, 40); 94 var bp = context.cookies.breakpoints.findBreakpoint(cookie.cookie); 95 96 items.push({ 97 nol10n: true, 98 tooltiptext: Locale.$STRF("cookies.menu.tooltip.Break On Cookie", [cookieName]), 99 label: Locale.$STRF("cookies.menu.Break On Cookie", [cookieName]), 100 type: "checkbox", 101 checked: bp != null, 102 command: Obj.bindFixed(this.onBreakOnCookie, this, context, cookie), 103 }); 104 105 if (bp) 106 { 107 items.push( 108 {label: "cookies.menu.Edit Breakpoint Condition", 109 command: Obj.bindFixed(this.editBreakpointCondition, this, context, cookie) } 110 ); 111 } 112 113 return items; 114 }, 115 116 onBreakOnCookie: function(context, cookie) 117 { 118 if (FBTrace.DBG_COOKIES) 119 FBTrace.sysout("cookies.breakOnCookie; ", context); 120 121 var breakpoints = context.cookies.breakpoints; 122 123 // Remove an existing or create a new breakpoint. 124 var row = cookie.row; 125 cookie = cookie.cookie; 126 var bp = breakpoints.findBreakpoint(cookie); 127 if (bp) 128 { 129 breakpoints.removeBreakpoint(cookie); 130 row.removeAttribute("breakpoint"); 131 row.removeAttribute("disabledBreakpoint"); 132 } 133 else 134 { 135 breakpoints.addBreakpoint(cookie); 136 row.setAttribute("breakpoint", "true"); 137 } 138 }, 139 140 updateBreakpoint: function(context, cookie) 141 { 142 // Make sure a breakpoint is displayed. 143 var bp = context.cookies.breakpoints.findBreakpoint(cookie.cookie) 144 if (!bp) 145 return; 146 147 var row = cookie.row; 148 row.setAttribute("breakpoint", "true"); 149 row.setAttribute("disabledBreakpoint", bp.checked ? "false" : "true"); 150 }, 151 152 onContextMenu: function(context, event) 153 { 154 if (!Css.hasClass(event.target, "sourceLine")) 155 return; 156 157 var row = Dom.getAncestorByClass(event.target, "cookieRow"); 158 if (!row) 159 return; 160 161 var cookie = row.repObject; 162 var bp = context.cookies.breakpoints.findBreakpoint(cookie.cookie); 163 if (!bp) 164 return; 165 166 this.editBreakpointCondition(context, cookie); 167 Events.cancelEvent(event); 168 }, 169 170 editBreakpointCondition: function(context, cookie) 171 { 172 var bp = context.cookies.breakpoints.findBreakpoint(cookie.cookie); 173 if (!bp) 174 return; 175 176 var condition = bp ? bp.condition : ""; 177 178 var panel = context.getPanel(panelName); 179 panel.selectedSourceBox = cookie.row; 180 Firebug.Editor.startEditing(cookie.row, condition); 181 }, 182 } 183 184 // ********************************************************************************************* // 185 // Cookie Breakpoints 186 187 Breakpoints.BreakpointTemplate = Domplate.domplate(Firebug.Rep, 188 { 189 inspectable: false, 190 191 tag: 192 DIV({"class": "breakpointRow focusRow", $disabled: "$bp|isDisabled", _repObject: "$bp", 193 role: "option", "aria-checked": "$bp.checked"}, 194 DIV({"class": "breakpointBlockHead"}, 195 INPUT({"class": "breakpointCheckbox", type: "checkbox", 196 _checked: "$bp.checked", tabindex: "-1", onclick: "$onEnable"}), 197 SPAN("$bp|getTitle"), 198 DIV({"class": "breakpointMutationType"}, "$bp|getType"), 199 IMG({"class": "closeButton", src: "blank.gif", onclick: "$onRemove"}) 200 ), 201 DIV({"class": "breakpointCode"}, 202 SPAN("$bp|getValue") 203 ) 204 ), 205 206 getTitle: function(bp) 207 { 208 return bp.name; 209 }, 210 211 getValue: function(bp) 212 { 213 return bp.host + bp.path; 214 }, 215 216 getType: function(bp) 217 { 218 return Locale.$STR("Break On Cookie Change"); 219 }, 220 221 isDisabled: function(bp) 222 { 223 return !bp.checked; 224 }, 225 226 onRemove: function(event) 227 { 228 Events.cancelEvent(event); 229 230 var bpPanel = Firebug.getElementPanel(event.target); 231 var context = bpPanel.context; 232 233 if (!Css.hasClass(event.target, "closeButton")) 234 return; 235 236 // Remove from list of breakpoints. 237 var row = Dom.getAncestorByClass(event.target, "breakpointRow"); 238 context.cookies.breakpoints.removeBreakpoint(row.repObject); 239 240 bpPanel.refresh(); 241 242 var cookiePanel = context.getPanel(panelName, true); 243 if (!cookiePanel) 244 return; 245 246 var cookie = cookiePanel.findRepObject(row.repObject); 247 if (cookie) 248 { 249 cookie.row.removeAttribute("breakpoint"); 250 cookie.row.removeAttribute("disabledBreakpoint"); 251 } 252 }, 253 254 onEnable: function(event) 255 { 256 var checkBox = event.target; 257 var bpRow = Dom.getAncestorByClass(checkBox, "breakpointRow"); 258 259 if (checkBox.checked) 260 { 261 Css.removeClass(bpRow, "disabled"); 262 bpRow.setAttribute("aria-checked", "true"); 263 } 264 else 265 { 266 Css.setClass(bpRow, "disabled"); 267 bpRow.setAttribute("aria-checked", "false"); 268 } 269 270 var bp = bpRow.repObject; 271 bp.checked = checkBox.checked; 272 273 var bpPanel = Firebug.getElementPanel(checkBox); 274 275 var cookiePanel = bpPanel.context.getPanel(panelName, true); 276 if (!cookiePanel) 277 return; 278 279 var cookie = cookiePanel.findRepObject(bp); 280 if (cookie) 281 cookie.row.setAttribute("disabledBreakpoint", bp.checked ? "false" : "true"); 282 }, 283 284 supportsObject: function(object) 285 { 286 return object instanceof Breakpoints.Breakpoint; 287 } 288 }); 289 290 // ********************************************************************************************* // 291 // Editor for Cookie breakpoint condition. 292 293 Breakpoints.ConditionEditor = function(doc) 294 { 295 Firebug.Breakpoint.ConditionEditor.apply(this, arguments); 296 } 297 298 Breakpoints.ConditionEditor.prototype = Domplate.domplate(Firebug.Breakpoint.ConditionEditor.prototype, 299 { 300 endEditing: function(target, value, cancel) 301 { 302 if (cancel) 303 return; 304 305 var cookie = target.repObject; 306 var panel = Firebug.getElementPanel(target); 307 var bp = panel.context.cookies.breakpoints.findBreakpoint(cookie.cookie); 308 if (bp) 309 bp.condition = value; 310 } 311 }); 312 313 // ********************************************************************************************* // 314 315 /** 316 * @domplate Template for cookie breakpoint displayed in the Breakpoints side 317 * panel. 318 */ 319 Breakpoints.Breakpoint = function(cookie) 320 { 321 this.name = cookie.name; 322 this.host = cookie.host; 323 this.path = cookie.path; 324 325 this.condition = ""; 326 this.checked = true; 327 328 this.onEvaluateFails = Obj.bind(this.onEvaluateFails, this); 329 this.onEvaluateSucceeds = Obj.bind(this.onEvaluateSucceeds, this); 330 }; 331 332 Breakpoints.Breakpoint.prototype = 333 { 334 evaluateCondition: function(context, cookie) 335 { 336 try 337 { 338 var scope = {}; 339 scope["value"] = cookie.value; 340 scope["cookie"] = CookieUtils.makeCookieObject(cookie); 341 342 // The callbacks will set this if the condition is true or if the eval faults. 343 delete context.breakingCause; 344 345 // Construct expression to evaluate. Native JSON support is available since 346 // Firefox 3.5 and breakpoints since Firebug 1.5, which supports min Fx 3.5 347 // So, all is good. 348 var expr = "(function (){var scope = " + JSON.stringify(scope) + 349 "; with (scope) { return " + this.condition + ";}})();" 350 351 // Evaluate condition using Firebug's command line. 352 var rc = Firebug.CommandLine.evaluate(expr, context, null, context.window, 353 this.onEvaluateSucceeds, this.onEvaluateFails); 354 355 if (FBTrace.DBG_COOKIES) 356 FBTrace.sysout("cookies.evaluateCondition; rc " + rc, {expr: expr, scope: scope}); 357 358 return !!context.breakingCause; 359 } 360 catch (err) 361 { 362 if (FBTrace.DBG_COOKIES) 363 FBTrace.sysout("cookies.evaluateCondition; EXCEPTION", err); 364 } 365 366 return false; 367 }, 368 369 onEvaluateSucceeds: function(result, context) 370 { 371 if (FBTrace.DBG_COOKIES) 372 FBTrace.sysout("cookies.onEvaluateSucceeds; " + result, result); 373 374 // Don't set the breakingCause if the breakpoint condition is evaluated to false. 375 if (!result) 376 return; 377 378 context.breakingCause = { 379 title: Locale.$STR("cookies.Break On Cookie"), 380 message: Str.cropString(unescape(this.name + "; " + this.condition + "; "), 200) 381 }; 382 }, 383 384 onEvaluateFails: function(result, context) 385 { 386 if (FBTrace.DBG_COOKIES) 387 FBTrace.sysout("cookies.onEvaluateFails; " + result, result); 388 389 context.breakingCause = { 390 title: Locale.$STR("cookies.Break On Cookie"), 391 message: Locale.$STR("cookies.Breakpoint condition evaluation fails"), 392 prevValue: this.condition, newValue:result 393 }; 394 } 395 } 396 397 // ********************************************************************************************* // 398 // Registration 399 400 Firebug.registerRep(Breakpoints.BreakpointTemplate); 401 402 return Breakpoints; 403 404 // ********************************************************************************************* // 405 }}); 406