1 /* See license.txt for terms of usage */ 2 3 define([ 4 "firebug/lib/xpcom", 5 "firebug/lib/object", 6 "firebug/lib/locale", 7 "firebug/lib/domplate", 8 "firebug/lib/dom", 9 "firebug/lib/options", 10 "firebug/lib/persist", 11 "firebug/lib/string", 12 "firebug/lib/http", 13 "firebug/lib/css", 14 "firebug/lib/events", 15 "firebug/lib/array", 16 "firebug/lib/search", 17 "firebug/cookies/menuUtils", 18 "firebug/cookies/cookieReps", 19 "firebug/cookies/headerResizer", 20 "firebug/cookies/cookieObserver", 21 "firebug/cookies/cookieUtils", 22 "firebug/cookies/cookie", 23 "firebug/cookies/breakpoints", 24 "firebug/cookies/cookiePermissions", 25 "firebug/cookies/cookieClipboard", 26 ], 27 function(Xpcom, Obj, Locale, Domplate, Dom, Options, Persist, Str, Http, Css, Events, Arr, Search, 28 MenuUtils, CookieReps, HeaderResizer, CookieObserver, CookieUtils, Cookie, Breakpoints, 29 CookiePermissions, CookieClipboard) { 30 31 with (Domplate) { 32 33 // ********************************************************************************************* // 34 // Constants 35 36 const Cc = Components.classes; 37 const Ci = Components.interfaces; 38 39 // Cookies preferences 40 const showRejectedCookies = "cookies.showRejectedCookies"; 41 const lastSortedColumn = "cookies.lastSortedColumn"; 42 const hiddenColsPref = "cookies.hiddenColumns"; 43 const removeConfirmation = "cookies.removeConfirmation"; 44 45 // Services 46 var cookieManager = Xpcom.CCSV("@mozilla.org/cookiemanager;1", "nsICookieManager2"); 47 48 const panelName = "cookies"; 49 50 // ********************************************************************************************* // 51 // Panel Implementation 52 53 /** 54 * @panel This class represents the Cookies panel that is displayed within 55 * Firebug UI. 56 */ 57 function CookiePanel() {} 58 59 CookiePanel.prototype = Obj.extend(Firebug.ActivablePanel, 60 /** @lends CookiePanel */ 61 { 62 name: panelName, 63 title: Locale.$STR("cookies.Panel"), 64 searchable: true, 65 breakable: true, 66 order: 200, // Place just after the Net panel. 67 68 initialize: function(context, doc) 69 { 70 // xxxHonza 71 // This initialization is made as soon as the Cookies panel 72 // is opened the first time. 73 // This means that columns are *not* resizeable within the console 74 // (rejected cookies) till this activation isn't executed. 75 76 // Initialize event listeners before the ancestor is called. 77 var hcr = HeaderResizer; 78 this.onMouseClick = Obj.bind(hcr.onMouseClick, hcr); 79 this.onMouseDown = Obj.bind(hcr.onMouseDown, hcr); 80 this.onMouseMove = Obj.bind(hcr.onMouseMove, hcr); 81 this.onMouseUp = Obj.bind(hcr.onMouseUp, hcr); 82 this.onMouseOut = Obj.bind(hcr.onMouseOut, hcr); 83 84 this.onContextMenu = Obj.bind(this.onContextMenu, this); 85 86 Firebug.ActivablePanel.initialize.apply(this, arguments); 87 88 Firebug.ConsolePanel.prototype.addListener(this); 89 90 // Just after the initialization, so the this.document member is set. 91 Firebug.CookieModule.addStyleSheet(this); 92 93 this.refresh(); 94 }, 95 96 shutdown: function() 97 { 98 Firebug.ConsolePanel.prototype.removeListener(this); 99 }, 100 101 /** 102 * Renders list of cookies displayed within the Cookies panel. 103 */ 104 refresh: function() 105 { 106 if (!Firebug.CookieModule.isEnabled(this.context)) 107 return; 108 109 // Create cookie list table. 110 this.table = CookieReps.CookieTable.createTable(this.panelNode); 111 112 // Cookies are displayed only for web pages. 113 var location = this.context.window.location; 114 if (!location) 115 return; 116 117 var protocol = location.protocol; 118 if (protocol.indexOf("http") != 0) 119 return; 120 121 // Get list of cookies for the current page. 122 var cookies = []; 123 var iter = cookieManager.enumerator; 124 while (iter.hasMoreElements()) 125 { 126 var cookie = iter.getNext(); 127 if (!cookie) 128 break; 129 130 cookie = cookie.QueryInterface(Ci.nsICookie2); 131 if (!CookieObserver.isCookieFromContext(this.context, cookie)) 132 continue; 133 134 var cookieWrapper = new Cookie(CookieUtils.makeCookieObject(cookie)); 135 cookies.push(cookieWrapper); 136 } 137 138 // If the filter allow it, display all rejected cookies as well. 139 if (Options.get(showRejectedCookies)) 140 { 141 // xxxHonza the this.context.cookies is sometimes null, but 142 // this must be because FB isn't correctly initialized. 143 if (!this.context.cookies) 144 { 145 if (FBTrace.DBG_COOKIES) 146 { 147 FBTrace.sysout( 148 "cookies.Cookie context isn't properly initialized - ERROR: " + 149 this.context.getName()); 150 } 151 return; 152 } 153 154 var activeHosts = this.context.cookies.activeHosts; 155 for (var hostName in activeHosts) 156 { 157 var host = activeHosts[hostName]; 158 if (!host.rejected) 159 continue; 160 161 var receivedCookies = host.receivedCookies; 162 if (receivedCookies) 163 cookies = Arr.extendArray(cookies, receivedCookies); 164 } 165 } 166 167 // Generate HTML list of cookies using domplate. 168 if (cookies.length) 169 { 170 var header = Dom.getElementByClass(this.table, "cookieHeaderRow"); 171 var tag = CookieReps.CookieRow.cookieTag; 172 var row = tag.insertRows({cookies: cookies}, header)[0]; 173 for (var i=0; i<cookies.length; i++) 174 { 175 var cookie = cookies[i]; 176 cookie.row = row; 177 178 Breakpoints.updateBreakpoint(this.context, cookie); 179 row = row.nextSibling; 180 } 181 } 182 183 if (FBTrace.DBG_COOKIES) 184 FBTrace.sysout("cookies.Cookie list refreshed.", cookies); 185 186 // Sort automaticaly the last sorted column. The preference stores 187 // two things: name of the sorted column and sort direction asc|desc. 188 // Example: colExpires asc 189 var prefValue = Options.get(lastSortedColumn); 190 if (prefValue) { 191 var values = prefValue.split(" "); 192 CookieReps.CookieTable.sortColumn(this.table, values[0], values[1]); 193 } 194 195 // Update visibility of columns according to the preferences 196 var hiddenCols = Options.get(hiddenColsPref); 197 if (hiddenCols) 198 this.table.setAttribute("hiddenCols", hiddenCols); 199 }, 200 201 initializeNode: function(oldPanelNode) 202 { 203 if (FBTrace.DBG_COOKIES) 204 FBTrace.sysout("cookies.CookiePanel.initializeNode"); 205 206 // xxxHonza 207 // This method isn't called when FB UI is detached. So, the columns 208 // are *not* resizable when FB is open in external window. 209 210 // Register event handlers for table column resizing. 211 this.document.addEventListener("click", this.onMouseClick, true); 212 this.document.addEventListener("mousedown", this.onMouseDown, true); 213 this.document.addEventListener("mousemove", this.onMouseMove, true); 214 this.document.addEventListener("mouseup", this.onMouseUp, true); 215 this.document.addEventListener("mouseout", this.onMouseOut, true); 216 217 this.panelNode.addEventListener("contextmenu", this.onContextMenu, false); 218 }, 219 220 destroyNode: function() 221 { 222 if (FBTrace.DBG_COOKIES) 223 FBTrace.sysout("cookies.CookiePanel.destroyNode"); 224 225 this.document.removeEventListener("mouseclick", this.onMouseClick, true); 226 this.document.removeEventListener("mousedown", this.onMouseDown, true); 227 this.document.removeEventListener("mousemove", this.onMouseMove, true); 228 this.document.removeEventListener("mouseup", this.onMouseUp, true); 229 this.document.removeEventListener("mouseout", this.onMouseOut, true); 230 231 this.panelNode.removeEventListener("contextmenu", this.onContextMenu, false); 232 }, 233 234 onContextMenu: function(event) 235 { 236 Breakpoints.onContextMenu(this.context, event); 237 }, 238 239 detach: function(oldChrome, newChrome) 240 { 241 Firebug.ActivablePanel.detach.apply(this, arguments); 242 }, 243 244 reattach: function(doc) 245 { 246 Firebug.ActivablePanel.reattach.apply(this, arguments); 247 }, 248 249 clear: function() 250 { 251 if (this.panelNode) 252 Dom.clearNode(this.panelNode); 253 254 this.table = null; 255 }, 256 257 show: function(state) 258 { 259 // Update permission button in the toolbar. 260 CookiePermissions.updatePermButton(this.context); 261 262 // For backward compatibility with Firebug 1.1 263 // 264 // Firebug 1.6 removes Firebug.DisabledPanelPage, simplifies the activation 265 // and the following code is not necessary any more. 266 if (Firebug.ActivableModule && Firebug.DisabledPanelPage) 267 { 268 var shouldShow = Firebug.CookieModule.isEnabled(this.context); 269 this.showToolbarButtons("fbCookieButtons", shouldShow); 270 if (!shouldShow) 271 { 272 // The activation model has been changed in Firebug 1.4. This is 273 // just to keep backward compatibility. 274 if (Firebug.DisabledPanelPage.show) 275 Firebug.DisabledPanelPage.show(this, Firebug.CookieModule); 276 else 277 Firebug.CookieModule.disabledPanelPage.show(this); 278 return; 279 } 280 } 281 else 282 { 283 this.showToolbarButtons("fbCookieButtons", true); 284 } 285 286 if (Firebug.chrome.setGlobalAttribute) 287 { 288 Firebug.chrome.setGlobalAttribute("cmd_firebug_resumeExecution", "breakable", "true"); 289 Firebug.chrome.setGlobalAttribute("cmd_firebug_resumeExecution", "tooltiptext", 290 Locale.$STR("cookies.Break On Cookie")); 291 } 292 }, 293 294 hide: function() 295 { 296 this.showToolbarButtons("fbCookieButtons", false); 297 }, 298 299 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 300 // Options menu 301 302 getOptionsMenuItems: function(context) 303 { 304 return [ 305 MenuUtils.optionAllowGlobally(context, "cookies.AllowGlobally", 306 "cookies.tip.AllowGlobally", "network.cookie", "cookieBehavior"), 307 /*MenuUtils.optionMenu(context, "cookies.clearWhenDeny", 308 "cookies.tip.clearWhenDeny", Firebug.prefDomain, clearWhenDeny),*/ 309 MenuUtils.optionMenu(context, "cookies.Confirm cookie removal", 310 "cookies.tip.Confirm cookie removal", Firebug.prefDomain, removeConfirmation) 311 ]; 312 }, 313 314 getContextMenuItems: function(object, target) 315 { 316 var items = []; 317 318 // If the user clicked at a cookie row, the context menu is already 319 // initialized and so, bail out. 320 var cookieRow = Dom.getAncestorByClass(target, "cookieRow"); 321 if (cookieRow) 322 return items; 323 324 // Also bail out if the user clicked on the header. 325 var header = Dom.getAncestorByClass(target, "cookieHeaderRow"); 326 if (header) 327 return items; 328 329 // Make sure default items (cmd_copy) is removed. 330 CookieReps.Rep.getContextMenuItems.apply(this, arguments); 331 332 return items; 333 }, 334 335 search: function(text) 336 { 337 if (!text) 338 return; 339 340 // Make previously visible nodes invisible again 341 if (this.matchSet) 342 { 343 for (var i in this.matchSet) 344 Css.removeClass(this.matchSet[i], "matched"); 345 } 346 347 this.matchSet = []; 348 349 function findRow(node) { return Dom.getAncestorByClass(node, "cookieRow"); } 350 var search = new Search.TextSearch(this.panelNode, findRow); 351 352 var cookieRow = search.find(text); 353 if (!cookieRow) 354 return false; 355 356 for (; cookieRow; cookieRow = search.findNext()) 357 { 358 Css.setClass(cookieRow, "matched"); 359 this.matchSet.push(cookieRow); 360 } 361 362 return true; 363 }, 364 365 getPopupObject: function(target) 366 { 367 var header = Dom.getAncestorByClass(target, "cookieHeaderRow"); 368 if (header) 369 return CookieReps.CookieTable; 370 371 return Firebug.ActivablePanel.getPopupObject.apply(this, arguments); 372 }, 373 374 findRepObject: function(cookie) 375 { 376 var strippedHost = CookieUtils.makeStrippedHost(cookie.host); 377 378 var result = null; 379 this.enumerateCookies(function(rep) 380 { 381 if (rep.rawHost == strippedHost && 382 rep.cookie.name == cookie.name && 383 rep.cookie.path == cookie.path) 384 { 385 result = rep; 386 return true; // break iteration 387 } 388 }); 389 390 return result; 391 }, 392 393 supportsObject: function(object) 394 { 395 return object instanceof Cookie; 396 }, 397 398 updateSelection: function(cookie) 399 { 400 var repCookie = this.findRepObject(cookie.cookie); 401 if (!repCookie) 402 return; 403 404 CookieReps.CookieRow.toggleRow(repCookie.row, true); 405 Dom.scrollIntoCenterView(repCookie.row); 406 }, 407 408 enumerateCookies: function(fn) 409 { 410 if (!this.table) 411 return; 412 413 var rows = Dom.getElementsByClass(this.table, "cookieRow"); 414 for (var i=0; i<rows.length; i++) 415 { 416 var cookie = Firebug.getRepObject(rows[i]); 417 if (!cookie) 418 continue; 419 420 if (fn(cookie)) 421 break; 422 } 423 }, 424 425 getEditor: function(target, value) 426 { 427 if (!this.conditionEditor) 428 this.conditionEditor = new Breakpoints.ConditionEditor(this.document); 429 return this.conditionEditor; 430 }, 431 432 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 433 // Support for Break On Next 434 435 breakOnNext: function(breaking) 436 { 437 this.context.breakOnCookie = breaking; 438 439 if (FBTrace.DBG_COOKIES) 440 FBTrace.sysout("cookies.breakOnNext; " + context.breakOnCookie + ", " + 441 context.getName()); 442 }, 443 444 shouldBreakOnNext: function() 445 { 446 return this.context.breakOnCookie; 447 }, 448 449 getBreakOnNextTooltip: function(enabled) 450 { 451 return (enabled ? Locale.$STR("cookies.Disable Break On Cookie") : 452 Locale.$STR("cookies.Break On Cookie")); 453 }, 454 455 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 456 // Console Panel Listeners 457 458 onFilterSet: function(logTypes) 459 { 460 logTypes.cookies = 1; 461 }, 462 463 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 464 // Panel Activation 465 466 onActivationChanged: function(enable) 467 { 468 if (FBTrace.DBG_COOKIES || FBTrace.DBG_ACTIVATION) 469 FBTrace.sysout("cookies.CookiePanel.onActivationChanged; " + enable); 470 471 if (enable) 472 { 473 Firebug.CookieModule.addObserver(this); 474 Firebug.Debugger.addListener(Firebug.CookieModule.DebuggerListener); 475 Firebug.Console.addListener(Firebug.CookieModule.ConsoleListener); 476 } 477 else 478 { 479 Firebug.CookieModule.removeObserver(this); 480 Firebug.Debugger.removeListener(Firebug.CookieModule.DebuggerListener); 481 Firebug.Console.removeListener(Firebug.CookieModule.ConsoleListener); 482 } 483 }, 484 485 // Support for info tips. 486 showInfoTip: function(infoTip, target, x, y) 487 { 488 var row = Dom.getAncestorByClass(target, "cookieRow"); 489 if (row && row.repObject) 490 { 491 if (Dom.getAncestorByClass(target, "cookieSizeCol") || 492 Dom.getAncestorByClass(target, "cookieRawSizeCol")) 493 { 494 var infoTipCookieId = "cookiesize-"+row.repObject.name; 495 if (infoTipCookieId == this.infoTipCookieId && row.repObject == this.infoTipCookie) 496 return true; 497 498 this.infoTipCookieId = infoTipCookieId; 499 this.infoTipCookie = row.repObject; 500 return this.populateSizeInfoTip(infoTip, row.repObject); 501 } 502 } 503 504 delete this.infoTipCookieId; 505 return false; 506 }, 507 508 populateSizeInfoTip: function(infoTip, cookie) 509 { 510 CookieReps.SizeInfoTip.render(cookie, infoTip); 511 return true; 512 }, 513 }); 514 515 // ********************************************************************************************* // 516 // Cookie Breakpoints 517 518 /** 519 * @class Represents {@link Firebug.Debugger} listener. This listener is reponsible for 520 * providing a list of cookie-breakpoints into the Breakpoints side-panel. 521 */ 522 Firebug.CookieModule.DebuggerListener = 523 { 524 getBreakpoints: function(context, groups) 525 { 526 if (!context.cookies.breakpoints.isEmpty()) 527 groups.push(context.cookies.breakpoints); 528 } 529 }; 530 531 // ********************************************************************************************* // 532 // Custom output in the Console panel for: document.cookie 533 534 Firebug.CookieModule.ConsoleListener = 535 { 536 tag: 537 DIV({_repObject: "$object"}, 538 DIV({"class": "documentCookieBody"}) 539 ), 540 541 log: function(context, object, className, sourceLink) 542 { 543 //xxxHonza: chromebug says it's null sometimes. 544 if (!context) 545 return; 546 547 if (object !== context.window.document.cookie) 548 return; 549 550 // Parse "document.cookie" string. 551 var cookies = CookieUtils.parseSentCookiesFromString(object); 552 if (!cookies || !cookies.length) 553 return; 554 555 // Create empty log row that serves as a container for list of cookies 556 // crated from the document.cookie property. 557 var appendObject = Firebug.ConsolePanel.prototype.appendObject; 558 var row = Firebug.ConsoleBase.logRow(appendObject, object, context, 559 "documentCookie", this, null, true); 560 561 var rowBody = Dom.getElementByClass(row, "documentCookieBody"); 562 CookieReps.CookieTable.render(cookies, rowBody); 563 }, 564 565 logFormatted: function(context, objects, className, sourceLink) 566 { 567 } 568 }; 569 570 // ********************************************************************************************* // 571 // Registration 572 573 Firebug.registerPanel(CookiePanel); 574 575 return CookiePanel; 576 577 // ********************************************************************************************* // 578 }}); 579 580