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/system", 17 "firebug/cookies/baseObserver", 18 "firebug/cookies/menuUtils", 19 "firebug/cookies/cookieUtils", 20 "firebug/cookies/cookie", 21 "firebug/cookies/breakpoints", 22 "firebug/cookies/cookieEvents", 23 "firebug/cookies/cookiePermissions", 24 "firebug/cookies/editCookie", 25 "firebug/cookies/cookieClipboard", 26 ], 27 function(Xpcom, Obj, Locale, Domplate, Dom, Options, Persist, Str, Http, Css, Events, Arr, System, 28 BaseObserver, MenuUtils, CookieUtils, Cookie, Breakpoints, CookieEvents, 29 CookiePermissions, EditCookie, CookieClipboard) { 30 31 with (Domplate) { 32 33 // ********************************************************************************************* // 34 // Constants 35 36 const Cc = Components.classes; 37 const Ci = Components.interfaces; 38 39 const lastSortedColumn = "cookies.lastSortedColumn"; 40 const hiddenColsPref = "cookies.hiddenColumns"; 41 42 const panelName = "cookies"; 43 44 // ********************************************************************************************* // 45 // Templates Helpers 46 47 // Object with all rep CookieReps. 48 var CookieReps = {}; 49 50 /** 51 * @domplate Basic template for Cookies panel UI. 52 */ 53 CookieReps.Rep = domplate(Firebug.Rep, 54 { 55 getContextMenuItems: function(cookie, target, context) 56 { 57 // xxxHonza not sure how to do this better if the default Firebug's "Copy" 58 // command (cmd_copy) shouldn't be there. 59 var popup = Firebug.chrome.$("fbContextMenu"); 60 if (popup.firstChild && popup.firstChild.getAttribute("command") == "cmd_copy") 61 popup.removeChild(popup.firstChild); 62 } 63 }); 64 65 // ********************************************************************************************* // 66 // Cookie Template (domplate) 67 68 /** 69 * @domplate Represents a domplate template for cookie entry in the cookie list. 70 */ 71 CookieReps.CookieRow = domplate(CookieReps.Rep, 72 /** @lends CookieReps.CookieRow */ 73 { 74 inspectable: false, 75 76 cookieTag: 77 FOR("cookie", "$cookies", 78 TR({"class": "cookieRow", _repObject: "$cookie", onclick: "$onClickRow", 79 $sessionCookie: "$cookie|isSessionCookie", 80 $rejectedCookie: "$cookie|isRejected"}, 81 TD({"class": "cookieDebugCol cookieCol"}, 82 DIV({"class": "sourceLine cookieRowHeader", onclick: "$onClickRowHeader"}, 83 " " 84 ) 85 ), 86 TD({"class": "cookieNameCol cookieCol"}, 87 DIV({"class": "cookieNameLabel cookieLabel"}, "$cookie|getName") 88 ), 89 TD({"class": "cookieValueCol cookieCol"}, 90 DIV({"class": "cookieValueLabel cookieLabel"}, 91 SPAN("$cookie.cookie.value|getValue") 92 ) 93 ), 94 TD({"class": "cookieRawValueCol cookieCol"}, 95 DIV({"class": "cookieRawValueLabel cookieLabel"}, 96 SPAN("$cookie.cookie.rawValue|getValue") 97 ) 98 ), 99 TD({"class": "cookieDomainCol cookieCol"}, 100 SPAN({"class": "cookieDomainLabel cookieLabel", onclick: "$onClickDomain"}, 101 "$cookie|getDomain") 102 ), 103 TD({"class": "cookieRawSizeCol cookieCol"}, 104 DIV({"class": "cookieRawSizeLabel cookieLabel"}, "$cookie|getRawSize") 105 ), 106 TD({"class": "cookieSizeCol cookieCol"}, 107 DIV({"class": "cookieSizeLabel cookieLabel"}, "$cookie|getSize") 108 ), 109 TD({"class": "cookiePathCol cookieCol"}, 110 DIV({"class": "cookiePathLabel cookieLabel", "title": "$cookie|getPath"}, 111 SPAN("$cookie|getPath") 112 ) 113 ), 114 TD({"class": "cookieExpiresCol cookieCol"}, 115 DIV({"class": "cookieExpiresLabel cookieLabel"}, "$cookie|getExpires") 116 ), 117 TD({"class": "cookieHttpOnlyCol cookieCol"}, 118 DIV({"class": "cookieHttpOnlyLabel cookieLabel"}, "$cookie|isHttpOnly") 119 ), 120 TD({"class": "cookieSecurityCol cookieCol"}, 121 DIV({"class": "cookieSecurityLabel cookieLabel"}, "$cookie|isSecure") 122 ) 123 ) 124 ), 125 126 bodyRow: 127 TR({"class": "cookieInfoRow"}, 128 TD({"class": "sourceLine cookieRowHeader"}), 129 TD({"class": "cookieInfoCol", colspan: 11}) 130 ), 131 132 bodyTag: 133 DIV({"class": "cookieInfoBody", _repObject: "$cookie"}, 134 DIV({"class": "cookieInfoTabs"}, 135 A({"class": "cookieInfoValueTab cookieInfoTab", onclick: "$onClickTab", 136 view: "Value"}, 137 Locale.$STR("cookies.info.valuetab.label") 138 ), 139 A({"class": "cookieInfoRawValueTab cookieInfoTab", onclick: "$onClickTab", 140 view: "RawValue", 141 $collapsed: "$cookie|hideRawValueTab"}, 142 Locale.$STR("cookies.info.rawdatatab.Raw Data") 143 ), 144 A({"class": "cookieInfoJsonTab cookieInfoTab", onclick: "$onClickTab", 145 view: "Json", 146 $collapsed: "$cookie|hideJsonTab"}, 147 Locale.$STR("cookies.info.jsontab.JSON") 148 ), 149 A({"class": "cookieInfoXmlTab cookieInfoTab", onclick: "$onClickTab", 150 view: "Xml", 151 $collapsed: "$cookie|hideXmlTab"}, 152 Locale.$STR("cookies.info.xmltab.XML") 153 ) 154 ), 155 DIV({"class": "cookieInfoValueText cookieInfoText"}), 156 DIV({"class": "cookieInfoRawValueText cookieInfoText"}), 157 DIV({"class": "cookieInfoJsonText cookieInfoText"}), 158 DIV({"class": "cookieInfoXmlText cookieInfoText"}) 159 ), 160 161 hideRawValueTab: function(cookie) 162 { 163 return (cookie.cookie.value == cookie.cookie.rawValue); 164 }, 165 166 hideJsonTab: function(cookie) 167 { 168 return cookie.getJsonValue() ? false : true; 169 }, 170 171 hideXmlTab: function(cookie) 172 { 173 return cookie.getXmlValue() ? false : true; 174 }, 175 176 getAction: function(cookie) 177 { 178 return cookie.action; 179 }, 180 181 getName: function(cookie) 182 { 183 return cookie.cookie.name; 184 }, 185 186 getValue: function(value) 187 { 188 return Str.escapeNewLines(Str.cropString(value)); 189 }, 190 191 getDomain: function(cookie) 192 { 193 if (!cookie.cookie.host) 194 return ""; 195 196 return cookie.cookie.host; 197 }, 198 199 getExpires: function(cookie) 200 { 201 if (cookie.cookie.expires == undefined) 202 return ""; 203 204 // The first character is space so, if the table is sorted according 205 // to this column, all "Session" cookies are displayed at the begining. 206 if (cookie.cookie.expires == 0) 207 return " " + Locale.$STR("cookies.Session"); 208 209 try 210 { 211 // Format the expires date using the current locale. 212 var date = new Date(cookie.cookie.expires * 1000); 213 return date.toLocaleString(); 214 } 215 catch (err) 216 { 217 if (FBTrace.DBG_ERRORS) 218 FBTrace.sysout("cookies.CookieRow.getExpires; EXCEPTION " + err, err); 219 } 220 221 return ""; 222 }, 223 224 isHttpOnly: function(cookie) 225 { 226 return cookie.cookie.isHttpOnly ? "HttpOnly" : ""; 227 }, 228 229 isSessionCookie: function(cookie) 230 { 231 return !cookie.cookie.expires; 232 }, 233 234 isRejected: function(cookie) 235 { 236 return !!cookie.cookie.rejected; 237 }, 238 239 getRawSize: function(cookie) 240 { 241 var size = cookie.cookie.name.length + cookie.cookie.rawValue.length; 242 return Str.formatSize(size); 243 }, 244 245 getSize: function(cookie) 246 { 247 var size = cookie.cookie.name.length + cookie.cookie.value.length; 248 return Str.formatSize(size); 249 }, 250 251 getPath: function(cookie) 252 { 253 var path = cookie.cookie.path; 254 return path ? path : ""; 255 }, 256 257 isDomainCookie: function(cookie) 258 { 259 return cookie.cookie.isDomain ? Locale.$STR("cookies.domain.label") : ""; 260 }, 261 262 isSecure: function(cookie) 263 { 264 return cookie.cookie.isSecure ? Locale.$STR("cookies.secure.label") : ""; 265 }, 266 267 // Firebug rep support 268 supportsObject: function(cookie) 269 { 270 return cookie instanceof Cookie; 271 }, 272 273 browseObject: function(cookie, context) 274 { 275 return false; 276 }, 277 278 getRealObject: function(cookie, context) 279 { 280 return cookie.cookie; 281 }, 282 283 getContextMenuItems: function(cookie, target, context) 284 { 285 CookieReps.Rep.getContextMenuItems.apply(this, arguments); 286 287 var items = []; 288 var rejected = cookie.cookie.rejected; 289 290 if (!rejected) 291 { 292 items.push({ 293 label: Locale.$STR("cookies.Cut"), 294 nol10n: true, 295 command: Obj.bindFixed(this.onCut, this, cookie) 296 }); 297 } 298 299 items.push({ 300 label: Locale.$STR("cookies.Copy"), 301 nol10n: true, 302 command: Obj.bindFixed(this.onCopy, this, cookie) 303 }); 304 305 if (!rejected) 306 { 307 items.push({ 308 label: Locale.$STR("cookies.Paste"), 309 nol10n: true, 310 disabled: CookieClipboard.isCookieAvailable() ? false : true, 311 command: Obj.bindFixed(this.onPaste, this, cookie) 312 }); 313 items.push("-"); 314 } 315 316 items.push({ 317 label: Locale.$STR("cookies.CopyAll"), 318 nol10n: true, 319 command: Obj.bindFixed(this.onCopyAll, this, cookie) 320 }); 321 322 if (!rejected) 323 { 324 items.push("-"); 325 items.push({ 326 label: Locale.$STR("cookies.Delete"), 327 nol10n: true, 328 command: Obj.bindFixed(this.onRemove, this, cookie) 329 }); 330 331 items.push("-"); 332 items.push({ 333 label: Locale.$STR("cookies.Edit"), 334 nol10n: true, 335 command: Obj.bindFixed(this.onEdit, this, cookie) 336 }); 337 338 if (cookie.cookie.rawValue) 339 { 340 items.push({ 341 label: Locale.$STR("cookies.Clear Value"), 342 nol10n: true, 343 command: Obj.bindFixed(this.onClearValue, this, cookie) 344 }); 345 } 346 } 347 348 // Permissions 349 var permItems = CookiePermissions.getContextMenuItems(cookie, target, context); 350 if (permItems) 351 items = items.concat(permItems); 352 353 // Breakpoints 354 var breakOnItems = Breakpoints.getContextMenuItems(cookie, target, context); 355 if (breakOnItems) 356 items = items.concat(breakOnItems); 357 358 return items; 359 }, 360 361 // Context menu commands 362 onCut: function(clickedCookie) 363 { 364 this.onCopy(clickedCookie); 365 this.onRemove(clickedCookie); 366 }, 367 368 onCopy: function(clickedCookie) 369 { 370 CookieClipboard.copyTo(clickedCookie); 371 }, 372 373 onCopyAll: function(clickedCookie) 374 { 375 var text = ""; 376 var tbody = Dom.getAncestorByClass(clickedCookie.row, "cookieTable").firstChild; 377 for (var row = tbody.firstChild; row; row = row.nextSibling) { 378 if (Css.hasClass(row, "cookieRow") && row.repObject) 379 text += row.repObject.toString() + "\n"; 380 } 381 382 System.copyToClipboard(text); 383 }, 384 385 onPaste: function(clickedCookie) // clickedCookie can be null if the user clicks within panel area. 386 { 387 var context = Firebug.currentContext; 388 var values = CookieClipboard.getFrom(); 389 if (!values || !context) 390 return; 391 392 if (FBTrace.DBG_COOKIES) 393 FBTrace.sysout("cookies.Get cookie values from clipboard", values); 394 395 // Change name so it's unique and use the current host. 396 values.name = Firebug.CookieModule.getDefaultCookieName(context, values.name); 397 values.host = context.browser.currentURI.host; 398 399 values.rawValue = values.value; 400 values.value = unescape(values.value); 401 402 // If the expire time isn't set use the default value. 403 if (values.expires == undefined) 404 values.expires = Firebug.CookieModule.getDefaultCookieExpireTime(); 405 406 // Create/modify cookie. 407 var cookie = new Cookie(values); 408 Firebug.CookieModule.createCookie(cookie); 409 410 if (FBTrace.DBG_COOKIES) 411 checkList(context.getPanel(panelName, true)); 412 }, 413 414 onRemove: function(cookie) 415 { 416 // Get the real XPCOM cookie object and remove it. 417 var realCookie = cookie.cookie; 418 if (!cookie.cookie.rejected) 419 Firebug.CookieModule.removeCookie(realCookie.host, realCookie.name, realCookie.path); 420 }, 421 422 onEdit: function(cookie) 423 { 424 var params = { 425 cookie: cookie.cookie, 426 action: "edit", 427 window: null, 428 EditCookie: EditCookie, 429 Firebug: Firebug, 430 FBTrace: FBTrace, 431 }; 432 433 var parent = Firebug.currentContext.chrome.window; 434 return parent.openDialog("chrome://firebug/content/cookies/editCookie.xul", 435 "_blank", "chrome,centerscreen,resizable=yes,modal=yes", 436 params); 437 }, 438 439 onClearValue: function(cookie) 440 { 441 if (FBTrace.DBG_COOKIES) 442 FBTrace.sysout("cookies.onClearValue;", cookie); 443 444 var newCookie = new Cookie(cookie.cookie); 445 newCookie.cookie.rawValue = ""; 446 Firebug.CookieModule.createCookie(newCookie); 447 }, 448 449 // Event handlers 450 onClickDomain: function(event) 451 { 452 if (Events.isLeftClick(event)) 453 { 454 var domain = event.target.innerHTML; 455 if (domain) 456 { 457 Events.cancelEvent(event); 458 event.cancelBubble = true; 459 //xxxHonza www.google.com (more windows are opened) 460 // openNewTab(domain); 461 } 462 } 463 }, 464 465 onClickRowHeader: function(event) 466 { 467 Events.cancelEvent(event); 468 469 var rowHeader = event.target; 470 if (!Css.hasClass(rowHeader, "cookieRowHeader")) 471 return; 472 473 var row = Dom.getAncestorByClass(event.target, "cookieRow"); 474 if (!row) 475 return; 476 477 var context = Firebug.getElementPanel(row).context; 478 Breakpoints.onBreakOnCookie(context, row.repObject); 479 }, 480 481 onClickRow: function(event) 482 { 483 if (FBTrace.DBG_COOKIES) 484 FBTrace.sysout("cookies.Click on cookie row.", event); 485 486 if (Events.isLeftClick(event)) 487 { 488 var row = Dom.getAncestorByClass(event.target, "cookieRow"); 489 if (row) 490 { 491 this.toggleRow(row); 492 Events.cancelEvent(event); 493 } 494 } 495 }, 496 497 toggleRow: function(row, forceOpen) 498 { 499 var opened = Css.hasClass(row, "opened"); 500 if (opened && forceOpen) 501 return; 502 503 Css.toggleClass(row, "opened"); 504 505 if (Css.hasClass(row, "opened")) 506 { 507 var bodyRow = this.bodyRow.insertRows({}, row)[0]; 508 var bodyCol = Dom.getElementByClass(bodyRow, "cookieInfoCol"); 509 var cookieInfo = this.bodyTag.replace({cookie: row.repObject}, bodyCol); 510 511 // If JSON or XML tabs are available select them by default. 512 if (this.selectTabByName(cookieInfo, "Json")) 513 return; 514 515 if (this.selectTabByName(cookieInfo, "Xml")) 516 return; 517 518 this.selectTabByName(cookieInfo, "Value"); 519 } 520 else 521 { 522 row.parentNode.removeChild(row.nextSibling); 523 } 524 }, 525 526 selectTabByName: function(cookieInfoBody, tabName) 527 { 528 var tab = Dom.getChildByClass(cookieInfoBody, "cookieInfoTabs", 529 "cookieInfo" + tabName + "Tab"); 530 531 // Don't select collapsed tabs. 532 if (tab && !Css.hasClass(tab, "collapsed")) 533 return this.selectTab(tab); 534 535 return false; 536 }, 537 538 onClickTab: function(event) 539 { 540 this.selectTab(event.currentTarget); 541 }, 542 543 selectTab: function(tab) 544 { 545 var cookieInfoBody = tab.parentNode.parentNode; 546 547 var view = tab.getAttribute("view"); 548 if (cookieInfoBody.selectedTab) 549 { 550 cookieInfoBody.selectedTab.removeAttribute("selected"); 551 cookieInfoBody.selectedText.removeAttribute("selected"); 552 } 553 554 var textBodyName = "cookieInfo" + view + "Text"; 555 556 cookieInfoBody.selectedTab = tab; 557 cookieInfoBody.selectedText = Dom.getChildByClass(cookieInfoBody, textBodyName); 558 559 cookieInfoBody.selectedTab.setAttribute("selected", "true"); 560 cookieInfoBody.selectedText.setAttribute("selected", "true"); 561 562 var cookie = Firebug.getRepObject(cookieInfoBody); 563 var context = Firebug.getElementPanel(cookieInfoBody).context; 564 this.updateInfo(cookieInfoBody, cookie, context); 565 566 return true; 567 }, 568 569 updateRow: function(cookie, context) 570 { 571 var panel = context.getPanel(panelName, true); 572 if (!panel) 573 return; 574 575 var parent = cookie.row.parentNode; 576 var nextSibling = cookie.row.nextSibling; 577 parent.removeChild(cookie.row); 578 579 var row = CookieReps.CookieRow.cookieTag.insertRows({cookies: [cookie]}, 580 panel.table.lastChild.lastChild)[0]; 581 582 var opened = Css.hasClass(cookie.row, "opened"); 583 584 cookie.row = row; 585 row.repObject = cookie; 586 587 if (nextSibling && row.nextSibling != nextSibling) 588 { 589 parent.removeChild(cookie.row); 590 parent.insertBefore(row, nextSibling); 591 } 592 593 if (opened) 594 Css.setClass(row, "opened"); 595 596 Breakpoints.updateBreakpoint(context, cookie); 597 }, 598 599 updateInfo: function(cookieInfoBody, cookie, context) 600 { 601 var tab = cookieInfoBody.selectedTab; 602 if (Css.hasClass(tab, "cookieInfoValueTab")) 603 { 604 var valueBox = Dom.getChildByClass(cookieInfoBody, "cookieInfoValueText"); 605 if (!cookieInfoBody.valuePresented) 606 { 607 cookieInfoBody.valuePresented = true; 608 609 var text = cookie.cookie.value; 610 if (text != undefined) 611 Str.insertWrappedText(text, valueBox); 612 } 613 } 614 else if (Css.hasClass(tab, "cookieInfoRawValueTab")) 615 { 616 var valueBox = Dom.getChildByClass(cookieInfoBody, "cookieInfoRawValueText"); 617 if (!cookieInfoBody.rawValuePresented) 618 { 619 cookieInfoBody.rawValuePresented = true; 620 621 var text = cookie.cookie.rawValue; 622 if (text != undefined) 623 Str.insertWrappedText(text, valueBox); 624 } 625 } 626 else if (Css.hasClass(tab, "cookieInfoJsonTab")) 627 { 628 var valueBox = Dom.getChildByClass(cookieInfoBody, "cookieInfoJsonText"); 629 if (!cookieInfoBody.jsonPresented) 630 { 631 cookieInfoBody.jsonPresented = true; 632 633 var jsonObject = cookie.getJsonValue(); 634 if (jsonObject) { 635 Firebug.DOMPanel.DirTable.tag.replace( 636 {object: jsonObject, toggles: this.toggles}, valueBox); 637 } 638 } 639 } 640 else if (Css.hasClass(tab, "cookieInfoXmlTab")) 641 { 642 var valueBox = Dom.getChildByClass(cookieInfoBody, "cookieInfoXmlText"); 643 if (!cookieInfoBody.xmlPresented) 644 { 645 cookieInfoBody.xmlPresented = true; 646 647 var docElem = cookie.getXmlValue(); 648 if (docElem) { 649 var tag = Firebug.HTMLPanel.CompleteElement.getNodeTag(docElem); 650 tag.replace({object: docElem}, valueBox); 651 } 652 } 653 } 654 }, 655 656 updateTabs: function(cookieInfoBody, cookie, context) 657 { 658 // Iterate over all info-tabs and update visibility. 659 var cookieInfoTabs = Dom.getElementByClass(cookieInfoBody, "cookieInfoTabs"); 660 var tab = cookieInfoTabs.firstChild; 661 while (tab) 662 { 663 var view = tab.getAttribute("view"); 664 var hideTabCallback = CookieReps.CookieRow["hide" + view + "Tab"]; 665 if (hideTabCallback) 666 { 667 if (hideTabCallback(cookie)) 668 Css.setClass(tab, "collapsed"); 669 else 670 Css.removeClass(tab, "collapsed"); 671 } 672 673 tab = tab.nextSibling; 674 } 675 676 // If the selected tab was collapsed, make sure another one is selected. 677 if (Css.hasClass(cookieInfoBody.selectedTab, "collapsed")) 678 { 679 if (this.selectTabByName(cookieInfoBody, "Json")) 680 return; 681 682 if (this.selectTabByName(cookieInfoBody, "Xml")) 683 return; 684 685 this.selectTabByName(cookieInfoBody, "Value"); 686 } 687 } 688 }); 689 690 // ********************************************************************************************* // 691 // Console Event Templates (domplate) 692 693 /** 694 * @domplate This template is used for displaying cookie-changed events 695 * (except of "clear") in the Console tab. 696 */ 697 CookieReps.CookieChanged = domplate(CookieReps.Rep, 698 { 699 inspectable: false, 700 701 // Console 702 tag: 703 DIV({"class": "cookieEvent", _repObject: "$object"}, 704 TABLE({cellpadding: 0, cellspacing: 0}, 705 TBODY( 706 TR( 707 TD({width: "100%"}, 708 SPAN(Locale.$STR("cookies.console.cookie"), " "), 709 SPAN({"class": "cookieNameLabel", onclick: "$onClick"}, 710 "$object|getName", 711 " "), 712 SPAN({"class": "cookieActionLabel"}, 713 "$object|getAction", 714 ". "), 715 SPAN({"class": "cookieValueLabel"}, 716 "$object|getValue") 717 ), 718 TD( 719 SPAN({"class": "cookieDomainLabel", onclick: "$onClickDomain", 720 title: "$object|getOriginalURI"}, "$object|getDomain"), 721 SPAN(" ") 722 ) 723 ) 724 ) 725 ) 726 ), 727 728 // Event handlers 729 onClick: function(event) 730 { 731 if (!Events.isLeftClick(event)) 732 return; 733 734 var target = event.target; 735 736 // Get associated nsICookie object. 737 var cookieEvent = Firebug.getRepObject(target); 738 if (!cookieEvent) 739 return; 740 741 var cookieWrapper = new Cookie(CookieUtils.makeCookieObject(cookieEvent.cookie)); 742 var context = Firebug.getElementPanel(target).context; 743 context.chrome.select(cookieWrapper, panelName); 744 }, 745 746 onClickDomain: function(event) 747 { 748 }, 749 750 getOriginalURI: function(cookieEvent) 751 { 752 var context = cookieEvent.context; 753 var strippedHost = cookieEvent.rawHost; 754 755 if (!context.cookies.activeCookies) 756 return strippedHost; 757 758 var cookie = cookieEvent.cookie; 759 var activeCookies = context.cookies.activeCookies[cookie.host]; 760 if (!activeCookies) 761 return strippedHost; 762 763 var activeCookie = activeCookies[CookieUtils.getCookieId(cookie)]; 764 765 var originalURI; 766 if (activeCookie) 767 originalURI = activeCookie.originalURI.spec; 768 else 769 originalURI = cookieEvent.rawHost; 770 771 if (FBTrace.DBG_COOKIES) 772 { 773 FBTrace.sysout("cookies.context.cookies.activeCookies[" + cookie.host + "]", 774 activeCookies); 775 776 FBTrace.sysout("cookies.Original URI for: " + CookieUtils.getCookieId(cookie) + 777 " is: " + originalURI, activeCookie); 778 } 779 780 return originalURI; 781 }, 782 783 getAction: function(cookieEvent) 784 { 785 // Return properly localized action. 786 switch(cookieEvent.action) 787 { 788 case "deleted": 789 return Locale.$STR("cookies.console.deleted"); 790 case "added": 791 return Locale.$STR("cookies.console.added"); 792 case "changed": 793 return Locale.$STR("cookies.console.changed"); 794 case "cleared": 795 return Locale.$STR("cookies.console.cleared"); 796 } 797 798 return ""; 799 }, 800 801 getName: function(cookieEvent) 802 { 803 return cookieEvent.cookie.name; 804 }, 805 806 getValue: function(cookieEvent) 807 { 808 return Str.cropString(cookieEvent.cookie.value, 75); 809 }, 810 811 getDomain: function(cookieEvent) 812 { 813 return cookieEvent.cookie.host; 814 }, 815 816 // Firebug rep support 817 supportsObject: function(cookieEvent) 818 { 819 return cookieEvent instanceof CookieEvents.CookieChangedEvent; 820 }, 821 822 browseObject: function(cookieEvent, context) 823 { 824 return false; 825 }, 826 827 getRealObject: function(cookieEvent, context) 828 { 829 return cookieEvent; 830 }, 831 832 // Context menu 833 getContextMenuItems: function(cookieEvent, target, context) 834 { 835 CookieReps.Rep.getContextMenuItems.apply(this, arguments); 836 } 837 }); 838 839 // ********************************************************************************************* // 840 841 /** 842 * @domplate Represents a domplate template for displaying rejected cookies. 843 */ 844 CookieReps.CookieRejected = domplate(CookieReps.Rep, 845 /** @lends CookieReps.CookieRejected */ 846 { 847 inspectable: false, 848 849 tag: 850 DIV({"class": "cookieEvent", _repObject: "$object"}, 851 TABLE({cellpadding: 0, cellspacing: 0}, 852 TBODY( 853 TR( 854 TD({width: "100%"}, 855 SPAN({"class": "cookieRejectedLabel"}, 856 Locale.$STR("cookies.console.cookiesrejected")), 857 " ", 858 SPAN({"class": "cookieRejectedList"}, 859 "$object|getCookieList") 860 ), 861 TD( 862 SPAN({"class": "cookieDomainLabel", onclick: "$onClickDomain"}, 863 "$object|getDomain"), 864 SPAN(" ") 865 ) 866 ) 867 ) 868 ) 869 ), 870 871 supportsObject: function(object) 872 { 873 return object instanceof CookieEvents.CookieRejectedEvent; 874 }, 875 876 getDomain: function(cookieEvent) 877 { 878 return cookieEvent.uri.host; 879 }, 880 881 getCookieList: function(cookieEvent) 882 { 883 var context = cookieEvent.context; 884 var activeHost = context.cookies.activeHosts[cookieEvent.uri.host]; 885 var cookies = activeHost.receivedCookies; 886 if (!cookies) 887 return Locale.$STR("cookies.console.nocookiesreceived"); 888 889 var label = ""; 890 for (var i=0; i<cookies.length; i++) 891 label += cookies[i].cookie.name + ((i<cookies.length-1) ? ", " : ""); 892 893 return Str.cropString(label, 75); 894 }, 895 896 onClickDomain: function(event) 897 { 898 }, 899 900 // Context menu 901 getContextMenuItems: function(cookie, target, context) 902 { 903 CookieReps.Rep.getContextMenuItems.apply(this, arguments); 904 } 905 }); 906 907 // ********************************************************************************************* // 908 909 /** 910 * @domplate Represents a domplate template for cookie cleared event that is 911 * visualised in Firebug Console panel. 912 */ 913 CookieReps.CookieCleared = domplate(CookieReps.Rep, 914 /** @lends CookieReps.CookieCleared */ 915 { 916 inspectable: false, 917 918 tag: 919 DIV({_repObject: "$object"}, 920 DIV("$object|getLabel") 921 ), 922 923 supportsObject: function(object) 924 { 925 return object instanceof CookieEvents.CookieClearedEvent; 926 }, 927 928 getLabel: function() 929 { 930 return Locale.$STR("cookies.console.cookiescleared"); 931 }, 932 933 // Context menu 934 getContextMenuItems: function(cookie, target, context) 935 { 936 CookieReps.Rep.getContextMenuItems.apply(this, arguments); 937 } 938 }); 939 940 941 CookieReps.SizeInfoTip = domplate(Firebug.Rep, 942 { 943 tag: 944 TABLE({"class": "sizeInfoTip", "id": "cookiesSizeInfoTip", role:"presentation"}, 945 TBODY( 946 FOR("size", "$sizeInfo", 947 TAG("$size|sizeTag", {size: "$size"}) 948 ) 949 ) 950 ), 951 952 sizeTag: 953 TR({"class": "sizeInfoRow"}, 954 TD({"class": "sizeInfoLabelCol"}, "$size.label"), 955 TD({"class": "sizeInfoSizeCol"}, "$size|formatSize"), 956 TD({"class": "sizeInfoDetailCol"}, "$size|formatNumber") 957 ), 958 959 formatSize: function(size) 960 { 961 return Str.formatSize(size.size); 962 }, 963 964 formatNumber: function(size) 965 { 966 return size.size && size.size >= 1024 ? "(" + Str.formatNumber(size.size) + " B)" : ""; 967 }, 968 969 render: function(cookie, parentNode) 970 { 971 var size = cookie.getSize(); 972 var rawSize = cookie.getRawSize(); 973 var sizeInfo = []; 974 975 sizeInfo.push({label: Locale.$STR("cookie.sizeinfo.Size"), size: size}); 976 977 if (size != rawSize) 978 sizeInfo.push({label: Locale.$STR("cookie.sizeinfo.Raw_Size"), size: rawSize}); 979 980 this.tag.replace({sizeInfo: sizeInfo}, parentNode); 981 }, 982 }); 983 984 // ********************************************************************************************* // 985 // Header Template (domplate) 986 987 /** 988 * @domplate Represents a template for basic cookie list layout. This 989 * template also includes a header and related functionality (such as sorting). 990 */ 991 CookieReps.CookieTable = domplate(CookieReps.Rep, 992 /** @lends CookieReps.CookieTable */ 993 { 994 inspectable: false, 995 996 tableTag: 997 TABLE({"class": "cookieTable", cellpadding: 0, cellspacing: 0, hiddenCols: ""}, 998 TBODY( 999 TR({"class": "cookieHeaderRow", onclick: "$onClickHeader"}, 1000 TD({id: "cookieBreakpointBar", width: "1%", "class": "cookieHeaderCell"}, 1001 " " 1002 ), 1003 TD({id: "colName", role: "columnheader", 1004 "class": "cookieHeaderCell alphaValue a11yFocus"}, 1005 DIV({"class": "cookieHeaderCellBox", 1006 title: Locale.$STR("cookies.header.name.tooltip")}, 1007 Locale.$STR("cookies.header.name")) 1008 ), 1009 TD({id: "colValue", role: "columnheader", 1010 "class": "cookieHeaderCell alphaValue a11yFocus"}, 1011 DIV({"class": "cookieHeaderCellBox", 1012 title: Locale.$STR("cookies.header.value.tooltip")}, 1013 Locale.$STR("cookies.header.value")) 1014 ), 1015 TD({id: "colRawValue", role: "columnheader", 1016 "class": "cookieHeaderCell alphaValue a11yFocus"}, 1017 DIV({"class": "cookieHeaderCellBox", 1018 title: Locale.$STR("cookies.header.rawValue.tooltip")}, 1019 Locale.$STR("cookies.header.rawValue")) 1020 ), 1021 TD({id: "colDomain", role: "columnheader", 1022 "class": "cookieHeaderCell alphaValue a11yFocus"}, 1023 DIV({"class": "cookieHeaderCellBox", 1024 title: Locale.$STR("cookies.header.domain.tooltip")}, 1025 Locale.$STR("cookies.header.domain")) 1026 ), 1027 TD({id: "colRawSize", role: "columnheader", 1028 "class": "cookieHeaderCell a11yFocus"}, 1029 DIV({"class": "cookieHeaderCellBox", 1030 title: Locale.$STR("cookies.header.size.tooltip")}, 1031 Locale.$STR("cookies.header.rawSize")) 1032 ), 1033 TD({id: "colSize", role: "columnheader", 1034 "class": "cookieHeaderCell a11yFocus"}, 1035 DIV({"class": "cookieHeaderCellBox", 1036 title: Locale.$STR("cookies.header.size.tooltip")}, 1037 Locale.$STR("cookies.header.size")) 1038 ), 1039 TD({id: "colPath", role: "columnheader", 1040 "class": "cookieHeaderCell alphaValue a11yFocus"}, 1041 DIV({"class": "cookieHeaderCellBox", 1042 title: Locale.$STR("cookies.header.path.tooltip")}, 1043 Locale.$STR("cookies.header.path")) 1044 ), 1045 TD({id: "colExpires", role: "columnheader", 1046 "class": "cookieHeaderCell a11yFocus"}, 1047 DIV({"class": "cookieHeaderCellBox", 1048 title: Locale.$STR("cookies.header.expires.tooltip")}, 1049 Locale.$STR("cookies.header.expires")) 1050 ), 1051 TD({id: "colHttpOnly", role: "columnheader", 1052 "class": "cookieHeaderCell alphaValue a11yFocus"}, 1053 DIV({"class": "cookieHeaderCellBox", 1054 title: Locale.$STR("cookies.header.httponly.tooltip")}, 1055 Locale.$STR("cookies.header.httponly")) 1056 ), 1057 TD({id: "colSecurity", role: "columnheader", 1058 "class": "cookieHeaderCell alphaValue a11yFocus"}, 1059 DIV({"class": "cookieHeaderCellBox", 1060 title: Locale.$STR("cookies.header.security.tooltip")}, 1061 Locale.$STR("cookies.header.security")) 1062 ) 1063 ) 1064 ) 1065 ), 1066 1067 onClickHeader: function(event) 1068 { 1069 if (FBTrace.DBG_COOKIES) 1070 FBTrace.sysout("cookies.onClickHeader"); 1071 1072 if (!Events.isLeftClick(event)) 1073 return; 1074 1075 var table = Dom.getAncestorByClass(event.target, "cookieTable"); 1076 var column = Dom.getAncestorByClass(event.target, "cookieHeaderCell"); 1077 this.sortColumn(table, column); 1078 }, 1079 1080 sortColumn: function(table, col, direction) 1081 { 1082 if (!col) 1083 return; 1084 1085 if (typeof(col) == "string") 1086 { 1087 var doc = table.ownerDocument; 1088 col = doc.getElementById(col); 1089 } 1090 1091 if (!col) 1092 return; 1093 1094 var numerical = !Css.hasClass(col, "alphaValue"); 1095 1096 var colIndex = 0; 1097 for (col = col.previousSibling; col; col = col.previousSibling) 1098 ++colIndex; 1099 1100 this.sort(table, colIndex, numerical, direction); 1101 }, 1102 1103 sort: function(table, colIndex, numerical, direction) 1104 { 1105 var tbody = table.lastChild; 1106 var headerRow = tbody.firstChild; 1107 1108 // Remove class from the currently sorted column 1109 var headerSorted = Dom.getChildByClass(headerRow, "cookieHeaderSorted"); 1110 Css.removeClass(headerSorted, "cookieHeaderSorted"); 1111 1112 // Mark new column as sorted. 1113 var header = headerRow.childNodes[colIndex]; 1114 Css.setClass(header, "cookieHeaderSorted"); 1115 1116 // If the column is already using required sort direction, bubble out. 1117 if ((direction == "desc" && header.sorted == 1) || 1118 (direction == "asc" && header.sorted == -1)) 1119 return; 1120 1121 var values = []; 1122 for (var row = tbody.childNodes[1]; row; row = row.nextSibling) 1123 { 1124 var cell = row.childNodes[colIndex]; 1125 var value = numerical ? parseFloat(cell.textContent) : cell.textContent; 1126 1127 // Issue 43, expires date is formatted in the UI, so use the original cookie 1128 // value instead. 1129 if (Css.hasClass(cell, "cookieExpiresCol")) 1130 value = row.repObject.cookie.expires; 1131 1132 if (Css.hasClass(row, "opened")) 1133 { 1134 var cookieInfoRow = row.nextSibling; 1135 values.push({row: row, value: value, info: cookieInfoRow}); 1136 row = cookieInfoRow; 1137 } 1138 else 1139 { 1140 values.push({row: row, value: value}); 1141 } 1142 } 1143 1144 values.sort(function(a, b) { return a.value < b.value ? -1 : 1; }); 1145 1146 if ((header.sorted && header.sorted == 1) || (!header.sorted && direction == "asc")) 1147 { 1148 Css.removeClass(header, "sortedDescending"); 1149 Css.setClass(header, "sortedAscending"); 1150 1151 header.sorted = -1; 1152 1153 for (var i = 0; i < values.length; ++i) 1154 { 1155 tbody.appendChild(values[i].row); 1156 if (values[i].info) 1157 tbody.appendChild(values[i].info); 1158 } 1159 } 1160 else 1161 { 1162 Css.removeClass(header, "sortedAscending"); 1163 Css.setClass(header, "sortedDescending"); 1164 1165 header.sorted = 1; 1166 1167 for (var i = values.length-1; i >= 0; --i) 1168 { 1169 tbody.appendChild(values[i].row); 1170 if (values[i].info) 1171 tbody.appendChild(values[i].info); 1172 } 1173 } 1174 1175 // Remember last sorted column & direction in preferences. 1176 var prefValue = header.getAttribute("id") + " " + (header.sorted > 0 ? "desc" : "asc"); 1177 Options.set(lastSortedColumn, prefValue); 1178 }, 1179 1180 supportsObject: function(object) 1181 { 1182 return (object == this); 1183 }, 1184 1185 /** 1186 * Provides menu items for header context menu. 1187 */ 1188 getContextMenuItems: function(object, target, context) 1189 { 1190 CookieReps.Rep.getContextMenuItems.apply(this, arguments); 1191 1192 var items = []; 1193 1194 // Iterate over all columns and create a menu item for each. 1195 var table = context.getPanel(panelName, true).table; 1196 var hiddenCols = table.getAttribute("hiddenCols"); 1197 1198 var lastVisibleIndex; 1199 var visibleColCount = 0; 1200 1201 var header = Dom.getAncestorByClass(target, "cookieHeaderRow"); 1202 1203 // Skip the first column for breakpoints. 1204 var columns = Arr.cloneArray(header.childNodes); 1205 columns.shift(); 1206 1207 for (var i=0; i<columns.length; i++) 1208 { 1209 var column = columns[i]; 1210 var visible = (hiddenCols.indexOf(column.id) == -1); 1211 1212 items.push({ 1213 label: column.textContent, 1214 type: "checkbox", 1215 checked: visible, 1216 nol10n: true, 1217 command: Obj.bindFixed(this.onShowColumn, this, context, column.id) 1218 }); 1219 1220 if (visible) 1221 { 1222 lastVisibleIndex = i; 1223 visibleColCount++; 1224 } 1225 } 1226 1227 // If the last column is visible, disable its menu item. 1228 if (visibleColCount == 1) 1229 items[lastVisibleIndex].disabled = true; 1230 1231 items.push("-"); 1232 items.push({ 1233 label: Locale.$STR("net.header.Reset Header"), 1234 nol10n: true, 1235 command: Obj.bindFixed(this.onResetColumns, this, context) 1236 }); 1237 1238 return items; 1239 }, 1240 1241 onShowColumn: function(context, colId) 1242 { 1243 var table = context.getPanel(panelName, true).table; 1244 var hiddenCols = table.getAttribute("hiddenCols"); 1245 1246 // If the column is already presented in the list of hidden columns, 1247 // remove it, otherwise append. 1248 var index = hiddenCols.indexOf(colId); 1249 if (index >= 0) 1250 { 1251 table.setAttribute("hiddenCols", hiddenCols.substr(0,index-1) + 1252 hiddenCols.substr(index+colId.length)); 1253 } 1254 else 1255 { 1256 table.setAttribute("hiddenCols", hiddenCols + " " + colId); 1257 } 1258 1259 // Store current state into the preferences. 1260 Options.set(hiddenColsPref, table.getAttribute("hiddenCols")); 1261 }, 1262 1263 onResetColumns: function(context) 1264 { 1265 var panel = context.getPanel(panelName, true); 1266 var header = Dom.getElementByClass(panel.panelNode, "cookieHeaderRow"); 1267 1268 // Reset widths 1269 var columns = header.childNodes; 1270 for (var i=0; i<columns.length; i++) 1271 { 1272 var col = columns[i]; 1273 if (col.style) 1274 col.style.width = ""; 1275 } 1276 1277 // Reset visibility. 1278 Options.clear(hiddenColsPref); 1279 panel.table.setAttribute("hiddenCols", Options.get(hiddenColsPref)); 1280 1281 // Reset also sorting (no sorting by default) 1282 var headerRow = panel.table.getElementsByClassName("cookieHeaderRow")[0]; 1283 var headerSorted = Dom.getChildByClass(headerRow, "cookieHeaderSorted"); 1284 Css.removeClass(headerSorted, "cookieHeaderSorted"); 1285 Options.set(lastSortedColumn, ""); 1286 panel.refresh(); 1287 }, 1288 1289 createTable: function(parentNode) 1290 { 1291 // Create cookie table UI. 1292 var table = this.tableTag.replace({}, parentNode, this); 1293 1294 // Update columns width according to the preferences. 1295 var header = Dom.getElementByClass(table, "cookieHeaderRow"); 1296 var columns = header.getElementsByTagName("td"); 1297 for (var i=0; i<columns.length; i++) 1298 { 1299 var col = columns[i]; 1300 var colId = col.getAttribute("id"); 1301 if (!colId || !col.style) 1302 continue; 1303 1304 var width = Options.get("cookies." + colId + ".width"); 1305 if (width) 1306 col.style.width = width + "px"; 1307 } 1308 1309 return table; 1310 }, 1311 1312 render: function(cookies, parentNode) 1313 { 1314 // Create basic cookie-list structure. 1315 var table = this.createTable(parentNode); 1316 var header = Dom.getElementByClass(table, "cookieHeaderRow"); 1317 1318 var tag = CookieReps.CookieRow.cookieTag; 1319 return tag.insertRows({cookies: cookies}, header); 1320 } 1321 }); 1322 1323 // ********************************************************************************************* // 1324 1325 var OBJECTLINK = FirebugReps.OBJECTLINK; 1326 1327 // xxxHonza: TODO 1328 CookieReps.CookieRep = domplate(CookieReps.Rep, 1329 { 1330 tag: 1331 OBJECTLINK( 1332 SPAN({"class": "objectTitle"}, "$object|getTitle") 1333 ), 1334 1335 className: "cookie", 1336 1337 supportsObject: function(cookie) 1338 { 1339 return cookie instanceof Cookie; 1340 }, 1341 1342 getTitle: function(cookie) 1343 { 1344 return cookie.cookie.name; 1345 }, 1346 1347 getTooltip: function(cookie) 1348 { 1349 return cookie.cookie.value; 1350 } 1351 }); 1352 1353 // ********************************************************************************************* // 1354 // Debug helpers 1355 1356 function checkList(panel) 1357 { 1358 if (!FBTrace.DBG_COOKIES) 1359 return; 1360 1361 if (!panel || !this.panelNode) 1362 return; 1363 1364 var row = Dom.getElementByClass(this.panelNode, "cookieRow"); 1365 while (row) 1366 { 1367 var rep = row.repObject; 1368 if ((rep.cookie.name != row.firstChild.firstChild.innerHTML) || 1369 (rep.cookie.path != row.childNodes[3].firstChild.innerHTML)) 1370 { 1371 FBTrace("---> Check failed!"); 1372 FBTrace("--->" + rep.rawHost + ", " + rep.cookie.name + ", " + 1373 rep.cookie.path); 1374 FBTrace(" " + row.firstChild.firstChild.innerHTML + ", " + 1375 row.childNodes[3].firstChild.innerHTML); 1376 } 1377 1378 row = row.nextSibling; 1379 } 1380 1381 return null; 1382 } 1383 1384 // ********************************************************************************************* // 1385 // Firebug Registration 1386 1387 Firebug.registerRep( 1388 //CookieReps.CookieRep, // Cookie 1389 CookieReps.CookieTable, // Cookie table with list of cookies 1390 CookieReps.CookieRow, // Entry in the cookie table 1391 CookieReps.CookieChanged, // Console: "cookie-changed" event 1392 CookieReps.CookieRejected, // Console: "cookie-rejected" event 1393 CookieReps.CookieCleared // Console: cookies "cleared" event 1394 ); 1395 1396 return CookieReps; 1397 1398 // ********************************************************************************************* // 1399 }}); 1400 1401