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/cookies/baseObserver", 17 "firebug/cookies/menuUtils", 18 "firebug/cookies/cookieReps", 19 "firebug/cookies/cookieUtils", 20 "firebug/cookies/cookie", 21 "firebug/cookies/breakpoints", 22 "firebug/cookies/cookieObserver", 23 "firebug/cookies/cookieClipboard", 24 "firebug/chrome/tabWatcher", 25 "firebug/cookies/httpObserver", 26 "firebug/lib/system", 27 "firebug/cookies/cookie", 28 "firebug/cookies/cookiePermissions", 29 "firebug/cookies/editCookie", 30 "firebug/trace/traceListener", 31 "firebug/trace/traceModule", 32 "firebug/chrome/firefox", 33 "firebug/cookies/legacy", 34 ], 35 function(Xpcom, Obj, Locale, Domplate, Dom, Options, Persist, Str, Http, Css, Events, Arr, 36 BaseObserver, MenuUtils, CookieReps, CookieUtils, Cookier, Breakpoints, CookieObserver, 37 CookieClipboard, TabWatcher, HttpObserver, System, Cookie, CookiePermissions, EditCookie, 38 TraceListener, TraceModule, Firefox) { 39 40 with (Domplate) { 41 42 // ********************************************************************************************* // 43 // Constants 44 45 const Cc = Components.classes; 46 const Ci = Components.interfaces; 47 48 // Firefox Preferences 49 const networkPrefDomain = "network.cookie"; 50 const cookieBehaviorPref = "cookieBehavior"; 51 const cookieLifeTimePref = "lifetimePolicy"; 52 53 // Cookies preferences 54 const clearWhenDeny = "cookies.clearWhenDeny"; 55 const defaultExpireTime = "cookies.defaultExpireTime"; 56 const removeConfirmation = "cookies.removeConfirmation"; 57 const removeSessionConfirmation = "cookies.removeSessionConfirmation"; 58 59 // Services 60 const cookieManager = Xpcom.CCSV("@mozilla.org/cookiemanager;1", "nsICookieManager2"); 61 const observerService = Xpcom.CCSV("@mozilla.org/observer-service;1", "nsIObserverService"); 62 const prompts = Xpcom.CCSV("@mozilla.org/embedcomp/prompt-service;1", "nsIPromptService"); 63 64 // Preferences 65 const PrefService = Cc["@mozilla.org/preferences-service;1"]; 66 const prefService = PrefService.getService(Ci.nsIPrefService); 67 const prefs = PrefService.getService(Ci.nsIPrefBranch); 68 69 // Cookie panel ID. 70 const panelName = "cookies"; 71 72 // Helper array for prematurely created contexts 73 var contexts = new Array(); 74 75 // Register stylesheet in Firebug. This method is introduced in Firebug 1.6 76 Firebug.registerStylesheet("chrome://firebug/skin/cookies/cookies.css"); 77 78 // ********************************************************************************************* // 79 // Module Implementation 80 81 /** 82 * @module This class represents a <i>module</i> for Cookies panel. 83 * The module supports activation (enable/disable of the Cookies panel). 84 * This functionality has been introduced in Firebug 1.2 and makes possible 85 * to control activity of Firebug panels in order to avoid (performance) expensive 86 * features. 87 */ 88 Firebug.CookieModule = Obj.extend(Firebug.ActivableModule, 89 /** @lends Firebug.CookieModule */ 90 { 91 contexts: contexts, 92 93 // Set to true if all hooks for monitoring cookies are registered; otherwise false. 94 observersRegistered: false, 95 96 /** 97 * Called by Firebug when Firefox window is opened. 98 * 99 * @param {String} prefDomain Preference domain (e.g. extensions.firebug) 100 * @param {Array} prefNames Default Firebug preference array. 101 */ 102 initialize: function(prefDomain, prefNames) 103 { 104 if (FBTrace.DBG_COOKIES) 105 FBTrace.sysout("cookies.CookieModule.initialize; "); 106 107 this.traceListener = new TraceListener("cookies.", "DBG_COOKIES", true, 108 "chrome://firebug/skin/cookies/trace.css"); 109 110 TraceModule.addListener(this.traceListener); 111 112 this.panelName = panelName; 113 this.description = Locale.$STR("cookies.modulemanager.description"); 114 115 Firebug.ActivableModule.initialize.apply(this, arguments); 116 117 var permTooltip = Firebug.chrome.$("fcPermTooltip"); 118 permTooltip.fcEnabled = true; 119 120 // All the necessary observers are registered by default. Even if the 121 // panel can be disabled (entirely or for a specific host) there is 122 // no simple way to find out this now, as the context isn't available. 123 // All will be unregistered again in the initContext (if necessary). 124 // There is no big overhead, the initContext is called just after the 125 // first document request. 126 //this.registerObservers(); 127 128 // Register listener for NetInfoBody (if the API is available) so, 129 // a new tab (Cookies) can be appended into the Net panel request info. 130 var netInfoBody = Firebug.NetMonitor.NetInfoBody; 131 if ("addListener" in netInfoBody) 132 netInfoBody.addListener(this.NetInfoBody); 133 134 // Register listener within the Console panel. If document.cookie property 135 // is logged, formatted output is used. 136 //Firebug.Console.addListener(this.ConsoleListener); 137 138 // Register debugger listener for providing cookie-breakpoints. 139 //Firebug.Debugger.addListener(this.DebuggerListener); 140 141 // Dynamically overlay Break on Next button in FB 1.5.1 142 // There is a small decoration coming from each panel. 143 var bonStack = Firebug.chrome.$("fbBreakOnNextButtonStack"); 144 if (bonStack) 145 { 146 var image = document.createElement("image"); 147 image.setAttribute("id", "fbBreakOnImageCookies"); 148 image.setAttribute("class", "fbBreakOnImage"); 149 image.setAttribute("src", "chrome://firebug/skin/cookies/breakOnCookieSingle.png"); 150 bonStack.appendChild(image); 151 } 152 153 Firebug.registerUIListener(this); 154 }, 155 156 initializeUI: function() 157 { 158 Firebug.ActivableModule.initializeUI.apply(this, arguments); 159 160 // Append the styleesheet to a new console popup panel introduced in Firebug 1.6 161 this.addStyleSheet(null); 162 163 Dom.collapse(Firebug.chrome.$("fbConsoleFilter-cookies"), false); 164 }, 165 166 /** 167 * Peforms clean up when Firebug is destroyed. 168 * Called by the framework when Firebug is closed for an existing Firefox window. 169 */ 170 shutdown: function() 171 { 172 this.unregisterObservers(); 173 174 // Support for trace-console customization in Firebug 1.3 175 TraceModule.removeListener(this.traceListener); 176 177 var netInfoBody = Firebug.NetMonitor.NetInfoBody; 178 if ("removeListener" in netInfoBody) 179 netInfoBody.removeListener(this.NetInfoBody); 180 181 //Firebug.Console.removeListener(this.ConsoleListener); 182 //Firebug.Debugger.removeListener(this.DebuggerListener); 183 184 Firebug.unregisterUIListener(this); 185 }, 186 187 registerObservers: function() 188 { 189 if (this.observersRegistered) 190 { 191 if (FBTrace.DBG_COOKIES) 192 FBTrace.sysout("cookies.cookieModule.registerObservers; Observers ALREADY registered"); 193 return; 194 } 195 196 observerService.addObserver(HttpObserver, "http-on-modify-request", false); 197 observerService.addObserver(HttpObserver, "http-on-examine-response", false); 198 observerService.addObserver(PermissionObserver, "perm-changed", false); 199 registerCookieObserver(CookieObserver); 200 prefs.addObserver(networkPrefDomain, PrefObserver, false); 201 202 this.observersRegistered = true; 203 204 if (FBTrace.DBG_COOKIES) 205 FBTrace.sysout("cookies.cookieModule.registerObservers;"); 206 }, 207 208 unregisterObservers: function(context) 209 { 210 if (!this.observersRegistered) 211 { 212 if (FBTrace.DBG_COOKIES) 213 FBTrace.sysout("cookies.cookieModule.registerObservers; " + 214 "Observers ALREADY un-registered"); 215 return; 216 } 217 218 observerService.removeObserver(HttpObserver, "http-on-modify-request"); 219 observerService.removeObserver(HttpObserver, "http-on-examine-response"); 220 observerService.removeObserver(PermissionObserver, "perm-changed"); 221 unregisterCookieObserver(CookieObserver); 222 prefs.removeObserver(networkPrefDomain, PrefObserver); 223 224 this.observersRegistered = false; 225 226 if (FBTrace.DBG_COOKIES) 227 FBTrace.sysout("cookies.cookieModule.unregisterObservers;"); 228 }, 229 230 // Helper context 231 initTempContext: function(tempContext) 232 { 233 tempContext.cookieTempObserver = registerCookieObserver(new CookieTempObserver(tempContext)); 234 235 // Create sub-context for cookies. 236 tempContext.cookies = {}; 237 tempContext.cookies.activeHosts = []; 238 }, 239 240 destroyTempContext: function(tempContext, context) 241 { 242 if (!tempContext) 243 return; 244 245 if (FBTrace.DBG_COOKIES) 246 { 247 FBTrace.sysout("cookies.Copy " + tempContext.events.length + 248 " events to real-context."); 249 250 var message = "cookies.Copy active hosts ("; 251 for (var host in tempContext.cookies.activeHosts) 252 message += host + ", "; 253 message = message.substring(0, message.length - 2); 254 message += ") from temp context into the real context."; 255 FBTrace.sysout(message, tempContext); 256 } 257 258 // Copy all active hosts on the page. In case of redirects or embedded IFrames, there 259 // can be more hosts (domains) involved on the page. Cookies must be displayed for 260 // all of them. 261 context.cookies.activeHosts = cloneMap(tempContext.cookies.activeHosts); 262 263 // Clone all active (received) cookies on the page. 264 // This is probably not necessary, as the first cookie is received 265 // in http-on-examine-response and at that time the real context 266 // is already created. 267 context.cookies.activeCookies = cloneMap(tempContext.cookies.activeCookies); 268 269 // Fire all lost cookie events (those from the temp context). 270 var events = tempContext.events; 271 for (var i=0; i<events.length; i++) { 272 var e = events[i]; 273 if (FBTrace.DBG_COOKIES) 274 FBTrace.sysout("cookies.Fire fake cookie event: " + e.topic + ", " + e.data + "\n"); 275 CookieObserver.observe(e.subject, e.topic, e.data); 276 } 277 278 delete tempContext.cookies.activeHosts; 279 delete tempContext.cookies.activeCookies; 280 delete tempContext.cookies; 281 282 // Unregister temporary cookie observer. 283 tempContext.cookieTempObserver = unregisterCookieObserver(tempContext.cookieTempObserver); 284 }, 285 286 /** 287 * Called by the framework when a context is created for Firefox tab. 288 * 289 * @param {Firebug.TabContext} Context for the current Firefox tab. 290 */ 291 initContext: function(context) 292 { 293 var tabId = Firebug.getTabIdForWindow(context.window); 294 295 if (FBTrace.DBG_COOKIES) 296 FBTrace.sysout("cookies.INIT real context for: " + tabId + ", " + 297 context.getName()); 298 299 // Create sub-context for cookies. 300 // xxxHonza: the cookies object exists within the context even if 301 // the panel is disabled. 302 context.cookies = {}; 303 context.cookies.activeHosts = []; 304 305 // Initialize custom path filter for this context 306 context.cookies.pathFilter = "/"; 307 308 // List of breakpoints. 309 context.cookies.breakpoints = new CookieBreakpointGroup(); 310 context.cookies.breakpoints.load(context); 311 312 // The temp context isn't created e.g. for empty tabs, chrome pages. 313 var tempContext = contexts[tabId]; 314 if (tempContext) 315 { 316 this.destroyTempContext(tempContext, context); 317 delete contexts[tabId]; 318 319 if (FBTrace.DBG_COOKIES) 320 FBTrace.sysout("cookies.DESTROY temporary context, tabId: " + tempContext.tabId); 321 } 322 323 // The base class must be called after the context for Cookies panel is 324 // properly initialized. The panel can be created inside this function 325 // (within Firebug.ActivableModule.enablePanel), which can result in 326 // calling CookiePanel.initialize method. This method directly calls 327 // CookiePanel.refresh, which needs the context.cookies object ready. 328 Firebug.ActivableModule.initContext.apply(this, arguments); 329 330 // Unregister all observers if the panel is disabled. 331 if (!this.isEnabled(context)) 332 this.unregisterObservers(context); 333 }, 334 335 destroyContext: function(context) 336 { 337 Firebug.ActivableModule.destroyContext.apply(this, arguments); 338 339 if (!context.cookies) 340 { 341 if (FBTrace.DBG_COOKIES) 342 { 343 var tabId = Firebug.getTabIdForWindow(context.window); 344 FBTrace.sysout("cookies.DESTROY context ERROR: No context.cookies available, tabId: " + 345 tabId + ", " + context.getName()); 346 } 347 return; 348 } 349 350 context.cookies.breakpoints.store(context); 351 352 for (var p in context.cookies) 353 delete context.cookies[p]; 354 355 delete context.cookies; 356 357 if (FBTrace.DBG_COOKIES) 358 { 359 var tabId = Firebug.getTabIdForWindow(context.window); 360 FBTrace.sysout("cookies.DESTROY context, tabId: " + tabId + 361 ", " + context.getName()); 362 } 363 }, 364 365 addStyleSheet: function(panel) 366 { 367 // Use registration function instead (introduced in Firebug 1.6) 368 if (Firebug.registerStylesheet) 369 return; 370 371 function privateAppend(doc) 372 { 373 // Make sure the stylesheet isn't appended twice. 374 if (!Firebug.chrome.$("fcStyles", doc)) 375 { 376 var styleSheet = createStyleSheet(doc, "chrome://firebug/skin/cookies/cookies.css"); 377 styleSheet.setAttribute("id", "fcStyles"); 378 addStyleSheet(doc, styleSheet); 379 } 380 } 381 382 if (panel) 383 privateAppend(panel.document) 384 385 // Firebug 1.6 introduces another panel for console preview on other panels 386 // The allows to use command line in other panels too. 387 var preview = Firebug.chrome.$("fbCommandPreviewBrowser"); 388 if (preview) 389 privateAppend(preview.contentDocument); 390 }, 391 392 updateOption: function(name, value) 393 { 394 if (name == "consoleFilterTypes") 395 { 396 this.updateConsoleFilter(); 397 } 398 }, 399 400 updateConsoleFilter: function() 401 { 402 if (FBTrace.DBG_COOKIES) 403 FBTrace.sysout("cookies.updateConsoleFilter;"); 404 405 if (!Firebug.currentContext) 406 return; 407 408 // The panel can be disabled. 409 var panel = Firebug.currentContext.getPanel("console"); 410 if (!panel) 411 return; 412 413 var panelNode = panel.panelNode; 414 var className = "hideType-cookies"; 415 var filterTypes = Firebug.consoleFilterTypes; 416 417 Css.setClass(panelNode, className); 418 419 var positiveFilters = ["all", "cookies"]; 420 for (var i=0; i<positiveFilters.length; i++) 421 { 422 if (filterTypes.indexOf(positiveFilters[i]) >= 0) 423 { 424 Css.removeClass(panelNode, className); 425 break; 426 } 427 } 428 }, 429 430 showPanel: function(browser, panel) 431 { 432 // Update panel's toolbar 433 var isCookiePanel = panel && panel.name == panelName; 434 435 // Firebug 1.4, chrome changes. 436 var chrome = browser.chrome ? browser.chrome : Firebug.chrome; 437 438 var cookieButtons = Firebug.chrome.$("fbCookieButtons"); 439 Dom.collapse(cookieButtons, !isCookiePanel); 440 441 // The console panel can be displayed sooner than the Cookies 442 // panel, in such a case the Stylesheet must be ready as 443 // there are cookies logs in the console. 444 // Cookie table is also used within the net panel. 445 if (panel && (panel.name == "console" || panel.name == "net")) 446 this.addStyleSheet(panel); 447 }, 448 449 watchWindow: function(context, win) 450 { 451 context.window.addEventListener("beforeunload", this.onBeforeUnload, false); 452 }, 453 454 onBeforeUnload: function(event) 455 { 456 var view = event.target.defaultView; 457 var context = TabWatcher.getContextByWindow(view); 458 if (!context) 459 return; 460 461 var panel = context.getPanel(panelName, true); 462 if (panel) 463 panel.clear(); 464 465 if (FBTrace.DBG_COOKIES || FBTrace.DBG_ERRORS) 466 { 467 var tabId = Firebug.getTabIdForWindow(view); 468 469 if (FBTrace.DBG_COOKIES) 470 FBTrace.sysout("cookies.On before unload tab: " + tabId); 471 472 if (contexts[tabId]) 473 { 474 delete contexts[tabId]; 475 476 if (FBTrace.DBG_COOKIES) 477 FBTrace.sysout("cookies.CookieModule.onBeforeUnload; There is a temp context leak!"); 478 } 479 } 480 }, 481 482 /** 483 * Creates a new cookie in the browser. 484 * This method is used by {@link EditCookie} dialog and also when a cookie is 485 * pasted from the clipboard. 486 * 487 * @param {Cookie} Cookie object with appropriate properties. See {@link Cookie} object. 488 */ 489 createCookie: function(cookie) 490 { 491 try 492 { 493 var uri = cookie.getURI(); 494 if (!uri) 495 return; 496 497 var c = cookie.cookie; 498 499 // Fix for issue 34. The domain must be included in the cookieString if it 500 // starts with "." But don't include it otherwise, since the "." would be 501 // appended by the service. 502 var host = cookie.cookie.host; 503 var cookieString = cookie.toString(!(host.charAt(0) == ".")); 504 505 // Fix for issue 37: httpOnly cookies, and issue 47: Cannot change the HttpOnly flag 506 // HttpOnly cookies can't be changed by setCookie string 507 // See also: https://bugzilla.mozilla.org/show_bug.cgi?id=178993 508 //cookieService.setCookieString(uri, null, cookieString, null); 509 510 // Doesn't work in FF4 (issue 95) 511 //cookieService.setCookieStringFromHttp(uri, uri, null, cookieString, 512 // c.expires, null); 513 514 //xxxHonza: in what cases the cookie should be removed? 515 //var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager); 516 //cm.remove(c.host, c.name, c.path, false); 517 518 var isSession = c.expires ? false : true; 519 var cm2 = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2); 520 cm2.add(c.host, c.path, c.name, c.rawValue, c.isSecure, c.isHttpOnly, isSession, 521 c.expires || Math.round((new Date()).getTime() / 1000 + 9999999999)); 522 523 if (FBTrace.DBG_COOKIES) 524 FBTrace.sysout("cookies.createCookie: set cookie string: " + cookieString, cookie); 525 526 // xxxHonza: this shouldn't be necessary, but sometimes the CookieObserver 527 // is not triggered. 528 TabWatcher.iterateContexts(function(context) 529 { 530 context.getPanel(panelName).refresh(); 531 }); 532 } 533 catch (e) 534 { 535 if (FBTrace.DBG_ERRORS) 536 FBTrace.sysout("cookies.createCookie: set cookie string ERROR " + 537 cookieString, e); 538 } 539 }, 540 541 removeCookie: function(host, name, path) 542 { 543 cookieManager.remove(host, name, path, false); 544 545 // xxxHonza: this shouldn't be necessary, but sometimes the CookieObserver 546 // is not triggered. 547 TabWatcher.iterateContexts(function(context) 548 { 549 context.getPanel(panelName).refresh(); 550 }); 551 }, 552 553 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 554 // Support for ActivableModule 1.6 555 556 /** 557 * It's just here to exists (calling base class only) 558 */ 559 isEnabled: function(context) 560 { 561 return Firebug.ActivableModule.isEnabled.apply(this, arguments); 562 }, 563 564 /** 565 * Called when an observer (e.g. panel) is added/removed into/from the model. 566 * This is the moment when the model needs to decide whether to activate. 567 */ 568 onObserverChange: function(observer) 569 { 570 if (this.hasObservers()) 571 TabWatcher.iterateContexts(Firebug.CookieModule.registerObservers); 572 else 573 TabWatcher.iterateContexts(Firebug.CookieModule.unregisterObservers); 574 575 this.setStatus(); 576 }, 577 578 onSuspendFirebug: function() 579 { 580 TabWatcher.iterateContexts(Firebug.CookieModule.unregisterObservers); 581 582 this.setStatus(); 583 584 if (FBTrace.DBG_COOKIES) 585 FBTrace.sysout("cookies.onSuspendFirebug"); 586 }, 587 588 onResumeFirebug: function(context) 589 { 590 if (Firebug.CookieModule.isAlwaysEnabled()) 591 TabWatcher.iterateContexts(Firebug.CookieModule.registerObservers); 592 593 this.setStatus(); 594 595 if (FBTrace.DBG_COOKIES) 596 FBTrace.sysout("cookies.onResumeFirebug"); 597 }, 598 599 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 600 601 setStatus: function() 602 { 603 var fbStatus = Firefox.getElementById("firebugStatus"); 604 if (fbStatus) 605 { 606 if (this.hasObservers()) 607 fbStatus.setAttribute(panelName, "on"); 608 else 609 fbStatus.removeAttribute(panelName); 610 } 611 else 612 { 613 if (FBTrace.DBG_ERRORS) 614 FBTrace.sysout("cookies.setStatus ERROR no firebugStatus element"); 615 } 616 }, 617 618 getMenuLabel: function(option, location) 619 { 620 var host = getURIHost(location); 621 622 // In case of local files or system pages use this labels instead of host. 623 // xxxHonza: the panel should be automatically disabled for local files 624 // and system pages as there are no cookies associated. 625 // These options shouldn't be available at all. 626 if (isSystemURL(location.spec)) 627 host = Locale.$STR("cookies.SystemPages"); 628 else if (!getURIHost(location)) 629 host = Locale.$STR("cookies.LocalFiles"); 630 631 // Translate these two options in panel activable menu from cookies.properties 632 switch (option) 633 { 634 case "disable-site": 635 return Locale.$STRF("cookies.HostDisable", [host]); 636 case "enable-site": 637 return Locale.$STRF("cookies.HostEnable", [host]); 638 } 639 640 return Firebug.ActivableModule.getMenuLabel.apply(this, arguments); 641 }, 642 643 // xxxHonza: This method is overriden just to provide translated strings from 644 // cookies.properties file. 645 openPermissions: function(event, context) 646 { 647 Events.cancelEvent(event); 648 649 var browserURI = Firebug.chrome.getBrowserURI(context); 650 var host = this.getHostForURI(browserURI); 651 652 var params = { 653 permissionType: this.getPrefDomain(), 654 windowTitle: Locale.$STR(this.panelName + ".Permissions"), 655 introText: Locale.$STR(this.panelName + ".PermissionsIntro"), 656 blockVisible: true, 657 sessionVisible: false, 658 allowVisible: true, 659 prefilledHost: host, 660 }; 661 662 openWindow("Browser:Permissions", "chrome://browser/content/preferences/permissions.xul", 663 "", params); 664 }, 665 666 // UI Commands 667 onRemoveAllShowTooltip: function(tooltip, context) 668 { 669 tooltip.label = Locale.$STR("cookies.removeall.tooltip"); 670 return true; 671 }, 672 673 onRemoveAllSessionShowTooltip: function(tooltip, context) 674 { 675 tooltip.label = Locale.$STR("cookies.removeallsession.tooltip"); 676 return true; 677 }, 678 679 /** 680 * Removes cookies defined for a website 681 * @param {Object} context context, in which the cookies are defined 682 * @param {Object} [filter] filter to define, which cookies should be removed 683 * (format: {session: true/false, host: string}) 684 */ 685 removeCookies: function(context, filter) 686 { 687 var panel = context.getPanel(panelName, true); 688 if (!panel) 689 return; 690 691 for (var host in context.cookies.activeHosts) 692 { 693 var cookieEnumerator = cookieManager.getCookiesFromHost(host); 694 695 while (cookieEnumerator.hasMoreElements()) 696 { 697 var cookie = cookieEnumerator.getNext().QueryInterface(Ci.nsICookie2); 698 699 if (!filter || ((!filter.session || cookie.isSession) && (!filter.host || filter.host == cookie.host))) 700 cookieManager.remove(cookie.host, cookie.name, cookie.path, false); 701 } 702 } 703 }, 704 705 onRemoveAll: function(context) 706 { 707 if (Options.get(removeConfirmation)) 708 { 709 var check = {value: false}; 710 var flags = prompts.BUTTON_POS_0 * prompts.BUTTON_TITLE_YES + 711 prompts.BUTTON_POS_1 * prompts.BUTTON_TITLE_NO; 712 713 if (!prompts.confirmEx(context.chrome.window, Locale.$STR("Firebug"), 714 Locale.$STR("cookies.confirm.removeall"), flags, "", "", "", 715 Locale.$STR("Do_not_show_this_message_again"), check) == 0) 716 { 717 return; 718 } 719 720 // Update 'Remove Cookies' confirmation option according to the value 721 // of the dialog's "do not show again" checkbox. 722 Options.set(removeConfirmation, !check.value); 723 } 724 725 Firebug.CookieModule.removeCookies(context); 726 }, 727 728 onRemoveAllSession: function(context) 729 { 730 if (Options.get(removeSessionConfirmation)) 731 { 732 var check = {value: false}; 733 var flags = prompts.BUTTON_POS_0 * prompts.BUTTON_TITLE_YES + 734 prompts.BUTTON_POS_1 * prompts.BUTTON_TITLE_NO; 735 736 if (!prompts.confirmEx(context.chrome.window, Locale.$STR("Firebug"), 737 Locale.$STR("cookies.confirm.removeallsession"), flags, "", "", "", 738 Locale.$STR("Do_not_show_this_message_again"), check) == 0) 739 { 740 return; 741 } 742 743 // Update 'Remove Session Cookies' confirmation option according to the value 744 // of the dialog's "do not show again" checkbox. 745 Options.set(removeSessionConfirmation, !check.value) 746 } 747 748 Firebug.CookieModule.removeCookies(context, {session: true}); 749 }, 750 751 onRemoveAllFromHost: function(context, host) 752 { 753 if (Options.get(removeConfirmation)) 754 { 755 var check = {value: false}; 756 var flags = prompts.BUTTON_POS_0 * prompts.BUTTON_TITLE_YES + 757 prompts.BUTTON_POS_1 * prompts.BUTTON_TITLE_NO; 758 759 if (!prompts.confirmEx(context.chrome.window, Locale.$STR("Firebug"), 760 Locale.$STRF("cookies.confirm.Remove_All_From_Host", [host]), flags, "", "", "", 761 Locale.$STR("Do_not_show_this_message_again"), check) == 0) 762 { 763 return; 764 } 765 766 // Update 'Remove Cookies' confirmation option according to the value 767 // of the dialog's "do not show again" checkbox. 768 Options.set(removeConfirmation, !check.value); 769 } 770 771 Firebug.CookieModule.removeCookies(context, {host: host}); 772 }, 773 774 onCreateCookieShowTooltip: function(tooltip, context) 775 { 776 var host = context.window.location.host; 777 tooltip.label = Locale.$STRF("cookies.createcookie.tooltip", [host]); 778 return true; 779 }, 780 781 onCreateCookie: function(context) 782 { 783 if (FBTrace.DBG_COOKIES) 784 FBTrace.sysout("cookies.onCreateCookie"); 785 786 // There is an excepion if the window is closed or not initialized (empty tab) 787 var host; 788 try { 789 host = context.window.location.host 790 } 791 catch (err) { 792 alert(Locale.$STR("cookies.message.There_is_no_active_page")); 793 return; 794 } 795 796 // Name and domain. 797 var cookie = new Object(); 798 cookie.name = this.getDefaultCookieName(context); 799 cookie.host = host; 800 801 // The edit dialog uses raw value. 802 cookie.rawValue = Locale.$STR("cookies.createcookie.defaultvalue"); 803 804 // Default path 805 var path = context.window.location.pathname || "/"; 806 cookie.path = path.substr(0, (path.lastIndexOf("/") || 1)); 807 808 // Set defaul expiration time. 809 cookie.expires = this.getDefaultCookieExpireTime(); 810 811 var params = { 812 cookie: cookie, 813 action: "create", 814 window: context.window, 815 EditCookie: EditCookie, 816 Firebug: Firebug, 817 FBTrace: FBTrace, 818 }; 819 820 var parent = context.chrome.window; 821 parent.openDialog("chrome://firebug/content/cookies/editCookie.xul", 822 "_blank", "chrome,centerscreen,resizable=yes,modal=yes", 823 params); 824 }, 825 826 getDefaultCookieName: function(context, defaultName) 827 { 828 var counter = 0; 829 var cookieDefaultName = defaultName || "Cookie"; 830 var cookieName = cookieDefaultName; 831 var exists = false; 832 var panel = context.getPanel(panelName); 833 834 do 835 { 836 exists = false; 837 838 var row = Dom.getElementByClass(panel.panelNode, "cookieRow"); 839 while (row) 840 { 841 var rep = row.repObject; 842 843 // If the cookie is expanded, there is a row without the repObject 844 if (rep && rep.cookie.name == cookieName) 845 { 846 counter++; 847 exists = true; 848 cookieName = cookieDefaultName + "-" + counter; 849 break; 850 } 851 row = row.nextSibling; 852 } 853 } while (exists) 854 855 return cookieName; 856 }, 857 858 getDefaultCookieExpireTime: function() 859 { 860 // Get default expire time interval (in seconds) and add it to the 861 // current time. 862 var defaultInterval = Options.get(defaultExpireTime); 863 var now = new Date(); 864 now.setTime(now.getTime() + (defaultInterval * 1000)); 865 866 // Return final expiration time. 867 return (now.getTime() / 1000); 868 }, 869 870 /** 871 * Exports all existing cookies in the browser into a cookies.txt file. 872 * This action is available in the Cookies panel toolbar. 873 */ 874 onExportAll: function(context) 875 { 876 try 877 { 878 var fp = Xpcom.CCIN("@mozilla.org/filepicker;1", "nsIFilePicker"); 879 fp.init(window, null, Ci.nsIFilePicker.modeSave); 880 fp.appendFilters(Ci.nsIFilePicker.filterAll | Ci.nsIFilePicker.filterText); 881 fp.filterIndex = 1; 882 fp.defaultString = "cookies.txt"; 883 884 var rv = fp.show(); 885 if (rv == Ci.nsIFilePicker.returnOK || rv == Ci.nsIFilePicker.returnReplace) 886 { 887 var foStream = Xpcom.CCIN("@mozilla.org/network/file-output-stream;1", "nsIFileOutputStream"); 888 foStream.init(fp.file, 0x02 | 0x08 | 0x20, 0666, 0); // write, create, truncate 889 890 var e = cookieManager.enumerator; 891 while(e.hasMoreElements()) 892 { 893 var cookie = e.getNext(); 894 cookie = cookie.QueryInterface(Ci.nsICookie2); 895 var cookieWrapper = new Cookie(CookieUtils.makeCookieObject(cookie)); 896 var cookieInfo = cookieWrapper.toText(); 897 foStream.write(cookieInfo, cookieInfo.length); 898 } 899 900 foStream.close(); 901 } 902 } 903 catch (err) 904 { 905 if (FBTrace.DBG_COOKIES) 906 FBTrace.sysout("cookies.onExportAll EXCEPTION", err); 907 } 908 }, 909 910 onExportForSiteShowTooltip: function(tooltip, context) 911 { 912 var host = context.window.location.host; 913 tooltip.label = Locale.$STRF("cookies.export.Export_For_Site_Tooltip", [host]); 914 return true; 915 }, 916 917 /** 918 * Exports cookies for the current site into a cookies.txt file 919 * This action is available in the Cookies panel toolbar. 920 */ 921 onExportForSite: function(context) 922 { 923 try 924 { 925 var fp = Xpcom.CCIN("@mozilla.org/filepicker;1", "nsIFilePicker"); 926 fp.init(window, null, Ci.nsIFilePicker.modeSave); 927 fp.appendFilters(Ci.nsIFilePicker.filterAll | Ci.nsIFilePicker.filterText); 928 fp.filterIndex = 1; 929 fp.defaultString = "cookies.txt"; 930 931 var rv = fp.show(); 932 if (rv == Ci.nsIFilePicker.returnOK || rv == Ci.nsIFilePicker.returnReplace) 933 { 934 var foStream = Xpcom.CCIN("@mozilla.org/network/file-output-stream;1", 935 "nsIFileOutputStream"); 936 foStream.init(fp.file, 0x02 | 0x08 | 0x20, 0666, 0); // write, create, truncate 937 938 var panel = context.getPanel(panelName, true); 939 var tbody = Dom.getElementByClass(panel.panelNode, "cookieTable").firstChild; 940 for (var row = tbody.firstChild; row; row = row.nextSibling) 941 { 942 if (Css.hasClass(row, "cookieRow") && row.repObject) 943 { 944 var cookieInfo = row.repObject.toText(); 945 foStream.write(cookieInfo, cookieInfo.length); 946 } 947 } 948 949 foStream.close(); 950 } 951 } 952 catch (err) 953 { 954 if (FBTrace.DBG_COOKIES) 955 FBTrace.sysout("cookies.onExportForSite EXCEPTION", err); 956 } 957 }, 958 959 onFilter: function(context, pref) 960 { 961 var value = Options.get(pref); 962 Options.set(pref, !value); 963 964 TabWatcher.iterateContexts(function(context) 965 { 966 var panel = context.getPanel(panelName, true); 967 if (panel) 968 panel.refresh(); 969 }); 970 }, 971 972 onFilterPopupShowing: function(menu) 973 { 974 var items = menu.getElementsByTagName("menuitem"); 975 for (var i=0; i<items.length; i++) 976 { 977 var item = items[i]; 978 var prefValue = Options.get(item.value); 979 if (prefValue) 980 item.setAttribute("checked", "true"); 981 else 982 item.removeAttribute("checked"); 983 } 984 985 return true; 986 }, 987 988 // Custom path filter 989 onFilterPanelShowing: function(filterPanel, context) 990 { 991 if (FBTrace.DBG_COOKIES) 992 FBTrace.sysout("cookies.onFilterPanelShowing ", filterPanel); 993 994 // Initialize filter input field. 995 filterPanel.init(context.cookies.pathFilter); 996 997 // A menu does not take the keyboard focus and keyboard messages are 998 // sent to the window. In order to avoid unwante shortcuts execution 999 // register a window keypress listeners for the time when the filter 1000 // popup is displayed and stop propagation of these events. 1001 // https://developer.mozilla.org/en/XUL/PopupGuide/PopupKeys 1002 window.addEventListener("keypress", this.onFilterKeyPress, true); 1003 return true; 1004 }, 1005 1006 onFilterPanelHiding: function(filterPanel, context) 1007 { 1008 window.removeEventListener("keypress", this.onFilterKeyPress, true); 1009 return true; 1010 }, 1011 1012 onFilterKeyPress: function(event) 1013 { 1014 // Stop propagation of keypress events when filter popup is displayed. 1015 event.stopPropagation(); 1016 }, 1017 1018 onFilterPanelApply: function(context) 1019 { 1020 var parentMenu = Firebug.chrome.$("fcFilterMenuPopup"); 1021 var filterPanel = Firebug.chrome.$("fcCustomPathFilterPanel"); 1022 1023 if (FBTrace.DBG_COOKIES) 1024 FBTrace.sysout("cookies.onApplyPathFilter, filter: " + filterPanel.value, 1025 filterPanel); 1026 1027 // Use the filter from panel. 1028 context.cookies.pathFilter = filterPanel.value; 1029 1030 // Refresh cookie list. 1031 var panel = context.getPanel(panelName); 1032 panel.refresh(); 1033 1034 // Close menu. 1035 parentMenu.hidePopup(); 1036 }, 1037 1038 onViewAll: function(context) 1039 { 1040 parent.openDialog("chrome://browser/content/preferences/cookies.xul", 1041 "_blank", "chrome,resizable=yes", null); 1042 }, 1043 1044 onViewExceptions: function(context) 1045 { 1046 var params = { 1047 blockVisible : true, 1048 sessionVisible : true, 1049 allowVisible : true, 1050 prefilledHost : "", 1051 permissionType : "cookie", 1052 windowTitle : Locale.$STR("cookies.ExceptionsTitle"), 1053 introText : Locale.$STR("cookies.Intro") 1054 }; 1055 1056 parent.openDialog("chrome://browser/content/preferences/permissions.xul", 1057 "_blank","chrome,resizable=yes", params); 1058 }, 1059 1060 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1061 // Console Panel Options 1062 1063 /** 1064 * Extend Console panel's option menu. 1065 */ 1066 onOptionsMenu: function(context, panel, items) 1067 { 1068 if (panel.name != "console") 1069 return; 1070 1071 var cookies = [ 1072 MenuUtils.optionMenu(context, "cookies.showCookieEvents", 1073 "cookies.tip.showCookieEvents", Firebug.prefDomain, "cookies.logEvents"), 1074 ]; 1075 1076 // The option is disabled if the panel is disabled. 1077 if (!this.isEnabled(context)) 1078 cookies[0].disabled = true; 1079 1080 // Append new option at the right position. 1081 for (var i=0; i<items.length; i++) 1082 { 1083 var item = items[i]; 1084 if (item.option == "showStackTrace") 1085 { 1086 Arr.arrayInsert(items, i+1, cookies); 1087 return; 1088 } 1089 } 1090 1091 // If "showStackTrace" is not there append at the end. 1092 Arr.arrayInsert(items, items.length, cookies); 1093 }, 1094 }); 1095 1096 // ********************************************************************************************* // 1097 // Custom info tab within Net panel 1098 1099 /** 1100 * @domplate Represents domplate template for cookie body that is displayed if 1101 * a cookie entry in the cookie list is expanded. 1102 */ 1103 Firebug.CookieModule.NetInfoBody = domplate(Firebug.Rep, 1104 /** @lends Firebug.CookieModule.NetInfoBody */ 1105 { 1106 tag: 1107 DIV({"class": "netInfoCookiesList"}, 1108 DIV({"class": "netInfoHeadersGroup netInfoCookiesGroup", $collapsed: "$cookiesInfo|hideReceivedCookies"}, 1109 SPAN(Locale.$STR("cookies.netinfo.Received Cookies")) 1110 ), 1111 DIV({"class": "netInfoReceivedCookies netInfoCookies"}), 1112 DIV({"class": "netInfoHeadersGroup netInfoCookiesGroup", $collapsed: "$cookiesInfo|hideSentCookies"}, 1113 SPAN(Locale.$STR("cookies.netinfo.Sent Cookies")) 1114 ), 1115 DIV({"class": "netInfoSentCookies netInfoCookies"}) 1116 ), 1117 1118 hideReceivedCookies: function(cookiesInfo) 1119 { 1120 return !cookiesInfo.receivedCookies.length; 1121 }, 1122 1123 hideSentCookies: function(cookiesInfo) 1124 { 1125 return !cookiesInfo.sentCookies.length; 1126 }, 1127 1128 // NetInfoBody listener 1129 initTabBody: function(infoBox, file) 1130 { 1131 var sentCookiesHeader = this.findHeader(file.requestHeaders, "Cookie"); 1132 var receivedCookiesHeader = this.findHeader(file.responseHeaders, "Set-Cookie"); 1133 1134 // Create tab only if there are some cookies. 1135 if (sentCookiesHeader || receivedCookiesHeader) 1136 Firebug.NetMonitor.NetInfoBody.appendTab(infoBox, "Cookies", 1137 Locale.$STR("cookies.Panel")); 1138 }, 1139 1140 destroyTabBody: function(infoBox, file) 1141 { 1142 }, 1143 1144 updateTabBody: function(infoBox, file, context) 1145 { 1146 var tab = infoBox.selectedTab; 1147 if (!tab || tab.dataPresented || !Css.hasClass(tab, "netInfoCookiesTab")) 1148 return; 1149 1150 tab.dataPresented = true; 1151 1152 if (FBTrace.DBG_COOKIES) 1153 FBTrace.sysout("cookies.NetInfoBodyListener.updateTabBody", 1154 [file.requestHeaders, file.responseHeaders]); 1155 1156 var sentCookiesHeader = this.findHeader(file.requestHeaders, "Cookie"); 1157 var receivedCookiesHeader = this.findHeader(file.responseHeaders, "Set-Cookie"); 1158 1159 // Parse all received cookies and generate UI. 1160 var receivedCookies = []; 1161 var sentCookies = []; 1162 1163 // Parse received cookies. 1164 if (receivedCookiesHeader) { 1165 var cookies = receivedCookiesHeader.split("\n"); 1166 for (var i=0; i<cookies.length; i++) { 1167 var cookie = CookieUtils.parseFromString(cookies[i]); 1168 if (!cookie.host) 1169 cookie.host = file.request.URI.host; 1170 receivedCookies.push(new Cookie(CookieUtils.makeCookieObject(cookie))); 1171 } 1172 } 1173 1174 // Parse sent cookies. 1175 sentCookies = CookieUtils.parseSentCookiesFromString(sentCookiesHeader); 1176 1177 // Create basic UI content 1178 var tabBody = Dom.getElementByClass(infoBox, "netInfoCookiesText"); 1179 this.tag.replace({cookiesInfo: { 1180 receivedCookies: receivedCookies, 1181 sentCookies: sentCookies, 1182 }}, tabBody); 1183 1184 // Generate UI for received cookies. 1185 if (receivedCookies.length) { 1186 CookieReps.CookieTable.render(receivedCookies, 1187 Dom.getElementByClass(tabBody, "netInfoReceivedCookies")); 1188 } 1189 1190 // Generate UI for sent cookies. 1191 if (sentCookies.length) { 1192 CookieReps.CookieTable.render(sentCookies, 1193 Dom.getElementByClass(tabBody, "netInfoSentCookies")); 1194 } 1195 }, 1196 1197 // Helpers 1198 findHeader: function(headers, name) 1199 { 1200 if (!headers) 1201 return null; 1202 1203 for (var i=0; i<headers.length; i++) { 1204 if (headers[i].name == name) 1205 return headers[i].value; 1206 } 1207 1208 return null; 1209 } 1210 }); 1211 1212 // ********************************************************************************************* // 1213 // Permission observer 1214 1215 /** 1216 * @class Represents an observer for perm-changed event that is dispatched 1217 * by Firefox is cookie permissions are changed. 1218 */ 1219 var PermissionObserver = Obj.extend(BaseObserver, 1220 /** @lends PermissionObserver */ 1221 { 1222 observe: function(aSubject, aTopic, aData) 1223 { 1224 if (aTopic != "perm-changed") 1225 return; 1226 1227 if (FBTrace.DBG_COOKIES) 1228 FBTrace.sysout("cookies.observe: " + aTopic + ", " + aData); 1229 1230 var fn = Obj.bind(CookiePermissions.updatePermButton, CookiePermissions); 1231 TabWatcher.iterateContexts(fn); 1232 } 1233 }); 1234 1235 // ********************************************************************************************* // 1236 1237 function CookieBreakpointGroup() 1238 { 1239 this.breakpoints = []; 1240 } 1241 1242 CookieBreakpointGroup.prototype = Obj.extend(new Firebug.Breakpoint.BreakpointGroup(), 1243 { 1244 name: "cookieBreakpoints", 1245 title: Locale.$STR("cookies.Cookie Breakpoints"), 1246 1247 addBreakpoint: function(cookie) 1248 { 1249 this.breakpoints.push(new Breakpoints.Breakpoint(cookie)); 1250 }, 1251 1252 removeBreakpoint: function(cookie) 1253 { 1254 var bp = this.findBreakpoint(cookie); 1255 Arr.remove(this.breakpoints, bp); 1256 }, 1257 1258 matchBreakpoint: function(bp, args) 1259 { 1260 var cookie = args[0]; 1261 return (bp.name == cookie.name) && 1262 (bp.host == cookie.host) && 1263 (bp.path == cookie.path); 1264 }, 1265 1266 // Persistence 1267 load: function(context) 1268 { 1269 var panelState = Persist.getPersistedState(context, panelName); 1270 if (panelState.breakpoints) 1271 this.breakpoints = panelState.breakpoints; 1272 }, 1273 1274 store: function(context) 1275 { 1276 var panelState = Persist.getPersistedState(context, panelName); 1277 panelState.breakpoints = this.breakpoints; 1278 } 1279 }); 1280 1281 // ********************************************************************************************* // 1282 // Registration Helpers 1283 1284 function registerCookieObserver(observer) 1285 { 1286 if (observer.registered) 1287 return; 1288 1289 if (FBTrace.DBG_COOKIES) 1290 FBTrace.sysout("cookies.registerCookieObserver"); 1291 1292 observerService.addObserver(observer, "cookie-changed", false); 1293 observerService.addObserver(observer, "cookie-rejected", false); 1294 1295 observer.registered = true; 1296 1297 return observer; 1298 } 1299 1300 function unregisterCookieObserver(observer) 1301 { 1302 if (!observer.registered) 1303 return; 1304 1305 if (FBTrace.DBG_COOKIES) 1306 FBTrace.sysout("cookies.unregisterCookieObserver"); 1307 1308 observerService.removeObserver(observer, "cookie-changed"); 1309 observerService.removeObserver(observer, "cookie-rejected"); 1310 1311 observer.registered = false; 1312 } 1313 1314 // ********************************************************************************************* // 1315 // Preference observer 1316 1317 // xxxHonza: is this still needed? 1318 /** 1319 * @class Represents an observer for nsPref:changed event dispatched when 1320 * an user preference is changed (e.g. using about:config) 1321 */ 1322 var PrefObserver = Obj.extend(BaseObserver, 1323 /** @lends PrefObserver */ 1324 { 1325 observe: function(aSubject, aTopic, aData) 1326 { 1327 if (aTopic != "nsPref:changed") 1328 return; 1329 1330 if (FBTrace.DBG_COOKIES) 1331 FBTrace.sysout("cookies.observe: " + aTopic + ", " + aData); 1332 1333 if (aData == networkPrefDomain + "." + cookieBehaviorPref || 1334 aData == networkPrefDomain + "." + cookieLifeTimePref) { 1335 var fn = CookiePermissions.updatePermButton; 1336 TabWatcher.iterateContexts(fn); 1337 } 1338 } 1339 }); 1340 1341 // ********************************************************************************************* // 1342 // Used till the real context isn't available (in initContext), bug if Firebug) 1343 1344 function CookieTempObserver(tempContext) { 1345 this.tempContext = tempContext; 1346 } 1347 1348 CookieTempObserver.prototype = Obj.extend(BaseObserver, { 1349 observe: function(subject, topic, data) { 1350 this.tempContext.appendCookieEvent(subject, topic, data); 1351 } 1352 }); 1353 1354 // ********************************************************************************************* // 1355 // Array Helpers 1356 1357 function cloneMap(map) 1358 { 1359 var newMap = []; 1360 for (var item in map) 1361 newMap[item] = map[item]; 1362 1363 return newMap; 1364 } 1365 1366 // ********************************************************************************************* // 1367 // Firebug Registration 1368 1369 // Expose to XUL scope 1370 Firebug.CookieModule.Perm = CookiePermissions; 1371 1372 // Expose for tests 1373 Firebug.CookieModule.CookieReps = CookieReps; 1374 1375 Firebug.registerActivableModule(Firebug.CookieModule); 1376 1377 return Firebug.CookieModule; 1378 1379 // ********************************************************************************************* // 1380 }}); 1381