home *** CD-ROM | disk | FTP | other *** search
/ PC Advisor 2010 April / PCA177.iso / ESSENTIALS / Firefox Setup.exe / nonlocalized / components / nsPrivateBrowsingService.js < prev    next >
Encoding:
Text File  |  2009-07-15  |  17.6 KB  |  513 lines

  1. //@line 38 "e:\builds\moz2_slave\win32_build\build\browser\components\privatebrowsing\src\nsPrivateBrowsingService.js"
  2.  
  3. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  4.  
  5. ////////////////////////////////////////////////////////////////////////////////
  6. //// Utilities
  7.  
  8. /**
  9.  * Returns true if the string passed in is part of the root domain of the
  10.  * current string.  For example, if this is "www.mozilla.org", and we pass in
  11.  * "mozilla.org", this will return true.  It would return false the other way
  12.  * around.
  13.  */
  14. String.prototype.hasRootDomain = function hasRootDomain(aDomain)
  15. {
  16.   let index = this.indexOf(aDomain);
  17.   // If aDomain is not found, we know we do not have it as a root domain.
  18.   if (index == -1)
  19.     return false;
  20.  
  21.   // If the strings are the same, we obviously have a match.
  22.   if (this == aDomain)
  23.     return true;
  24.  
  25.   // Otherwise, we have aDomain as our root domain iff the index of aDomain is
  26.   // aDomain.length subtracted from our length and (since we do not have an
  27.   // exact match) the character before the index is a dot or slash.
  28.   let prevChar = this[index - 1];
  29.   return (index == (this.length - aDomain.length)) &&
  30.          (prevChar == "." || prevChar == "/");
  31. }
  32.  
  33. ////////////////////////////////////////////////////////////////////////////////
  34. //// Constants
  35.  
  36. const Cc = Components.classes;
  37. const Ci = Components.interfaces;
  38. const Cu = Components.utils;
  39. const Cr = Components.results;
  40.  
  41. ////////////////////////////////////////////////////////////////////////////////
  42. //// PrivateBrowsingService
  43.  
  44. function PrivateBrowsingService() {
  45.   this._obs.addObserver(this, "profile-after-change", true);
  46.   this._obs.addObserver(this, "quit-application-granted", true);
  47.   this._obs.addObserver(this, "private-browsing", true);
  48. }
  49.  
  50. PrivateBrowsingService.prototype = {
  51.   // Observer Service
  52.   __obs: null,
  53.   get _obs() {
  54.     if (!this.__obs)
  55.       this.__obs = Cc["@mozilla.org/observer-service;1"].
  56.                    getService(Ci.nsIObserverService);
  57.     return this.__obs;
  58.   },
  59.  
  60.   // Preferences Service
  61.   __prefs: null,
  62.   get _prefs() {
  63.     if (!this.__prefs)
  64.       this.__prefs = Cc["@mozilla.org/preferences-service;1"].
  65.                      getService(Ci.nsIPrefBranch);
  66.     return this.__prefs;
  67.   },
  68.  
  69.   // Whether the private browsing mode is currently active or not.
  70.   _inPrivateBrowsing: false,
  71.  
  72.   // Saved browser state before entering the private mode.
  73.   _savedBrowserState: null,
  74.  
  75.   // Whether we're in the process of shutting down
  76.   _quitting: false,
  77.  
  78.   // How to treat the non-private session
  79.   _saveSession: true,
  80.  
  81.   // Make sure we don't allow re-enterant changing of the private mode
  82.   _alreadyChangingMode: false,
  83.  
  84.   // Whether we're entering the private browsing mode at application startup
  85.   _autoStart: false,
  86.  
  87.   // Whether the private browsing mode has been started automatically
  88.   _autoStarted: false,
  89.  
  90.   // XPCOM registration
  91.   classDescription: "PrivateBrowsing Service",
  92.   contractID: "@mozilla.org/privatebrowsing;1",
  93.   classID: Components.ID("{c31f4883-839b-45f6-82ad-a6a9bc5ad599}"),
  94.   _xpcom_categories: [
  95.     { category: "app-startup", service: true }
  96.   ],
  97.  
  98.   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrivateBrowsingService, 
  99.                                          Ci.nsIObserver,
  100.                                          Ci.nsISupportsWeakReference]),
  101.  
  102.   _unload: function PBS__destroy() {
  103.     // Force an exit from the private browsing mode on shutdown
  104.     this._quitting = true;
  105.     if (this._inPrivateBrowsing)
  106.       this.privateBrowsingEnabled = false;
  107.   },
  108.  
  109.   _onBeforePrivateBrowsingModeChange: function PBS__onBeforePrivateBrowsingModeChange() {
  110.     // nothing needs to be done here if we're auto-starting
  111.     if (!this._autoStart) {
  112.       let ss = Cc["@mozilla.org/browser/sessionstore;1"].
  113.                getService(Ci.nsISessionStore);
  114.       let blankState = JSON.stringify({
  115.         "windows": [{
  116.           "tabs": [{
  117.             "entries": [{
  118.               "url": "about:blank"
  119.             }]
  120.           }],
  121.           "_closedTabs": []
  122.         }]
  123.       });
  124.  
  125.       // whether we should save and close the current session
  126.       this._saveSession = true;
  127.       try {
  128.         if (this._prefs.getBoolPref("browser.privatebrowsing.keep_current_session"))
  129.           this._saveSession = false;
  130.       } catch (ex) {}
  131.  
  132.       if (this._inPrivateBrowsing) {
  133.         // save the whole browser state in order to restore all windows/tabs later
  134.         if (this._saveSession && !this._savedBrowserState) {
  135.           if (this._getBrowserWindow())
  136.             this._savedBrowserState = ss.getBrowserState();
  137.           else // no open browser windows, just restore a blank state on exit
  138.             this._savedBrowserState = blankState;
  139.         }
  140.       }
  141.  
  142.       this._closePageInfoWindows();
  143.  
  144.       if (!this._quitting && this._saveSession) {
  145.         let browserWindow = this._getBrowserWindow();
  146.  
  147.         // if there are open browser windows, load a dummy session to get a distinct 
  148.         // separation between private and non-private sessions
  149.         if (browserWindow) {
  150.           // set an empty session to transition from/to pb mode, see bug 476463
  151.           ss.setBrowserState(blankState);
  152.  
  153.           // just in case the only remaining window after setBrowserState is different.
  154.           // it probably shouldn't be with the current sessionstore impl, but we shouldn't
  155.           // rely on behaviour the API doesn't guarantee
  156.           let browser = this._getBrowserWindow().gBrowser;
  157.  
  158.           // this ensures a clean slate from which to transition into or out of
  159.           // private browsing
  160.           browser.addTab();
  161.           browser.removeTab(browser.tabContainer.firstChild);
  162.         }
  163.       }
  164.     }
  165.     else
  166.       this._saveSession = false;
  167.   },
  168.  
  169.   _onAfterPrivateBrowsingModeChange: function PBS__onAfterPrivateBrowsingModeChange() {
  170.     // nothing to do here if we're auto-starting or the current session is being
  171.     // used
  172.     if (!this._autoStart && this._saveSession) {
  173.       let ss = Cc["@mozilla.org/browser/sessionstore;1"].
  174.                getService(Ci.nsISessionStore);
  175.       // if we have transitioned out of private browsing mode and the session is
  176.       // to be restored, do it now
  177.       if (!this._inPrivateBrowsing) {
  178.         ss.setBrowserState(this._savedBrowserState);
  179.         this._savedBrowserState = null;
  180.  
  181.         this._closePageInfoWindows();
  182.       }
  183.       else {
  184.         // otherwise, if we have transitioned into private browsing mode, load
  185.         // about:privatebrowsing
  186.         let privateBrowsingState = {
  187.           "windows": [{
  188.             "tabs": [{
  189.               "entries": [{
  190.                 "url": "about:privatebrowsing"
  191.               }]
  192.             }],
  193.             "_closedTabs": []
  194.           }]
  195.         };
  196.         // Transition into private browsing mode
  197.         ss.setBrowserState(JSON.stringify(privateBrowsingState));
  198.       }
  199.     }
  200.   },
  201.  
  202.   _canEnterPrivateBrowsingMode: function PBS__canEnterPrivateBrowsingMode() {
  203.     let cancelEnter = Cc["@mozilla.org/supports-PRBool;1"].
  204.                       createInstance(Ci.nsISupportsPRBool);
  205.     cancelEnter.data = false;
  206.     this._obs.notifyObservers(cancelEnter, "private-browsing-cancel-vote", "enter");
  207.     return !cancelEnter.data;
  208.   },
  209.  
  210.   _canLeavePrivateBrowsingMode: function PBS__canLeavePrivateBrowsingMode() {
  211.     let cancelLeave = Cc["@mozilla.org/supports-PRBool;1"].
  212.                       createInstance(Ci.nsISupportsPRBool);
  213.     cancelLeave.data = false;
  214.     this._obs.notifyObservers(cancelLeave, "private-browsing-cancel-vote", "exit");
  215.     return !cancelLeave.data;
  216.   },
  217.  
  218.   _getBrowserWindow: function PBS__getBrowserWindow() {
  219.     return Cc["@mozilla.org/appshell/window-mediator;1"].
  220.            getService(Ci.nsIWindowMediator).
  221.            getMostRecentWindow("navigator:browser");
  222.   },
  223.  
  224.   _closePageInfoWindows: function PBS__closePageInfoWindows() {
  225.     let pageInfoEnum = Cc["@mozilla.org/appshell/window-mediator;1"].
  226.                        getService(Ci.nsIWindowMediator).
  227.                        getEnumerator("Browser:page-info");
  228.     while (pageInfoEnum.hasMoreElements()) {
  229.       let win = pageInfoEnum.getNext();
  230.       win.close();
  231.     }
  232.   },
  233.  
  234.   // nsIObserver
  235.  
  236.   observe: function PBS_observe(aSubject, aTopic, aData) {
  237.     switch (aTopic) {
  238.       case "profile-after-change":
  239.         // If the autostart prefs has been set, simulate entering the
  240.         // private browsing mode upon startup.
  241.         // This won't interfere with the session store component, because
  242.         // that component will be initialized on final-ui-startup.
  243.         this._autoStart = this._prefs.getBoolPref("browser.privatebrowsing.autostart");
  244.         if (this._autoStart) {
  245.           this._autoStarted = true;
  246.           this.privateBrowsingEnabled = true;
  247.           this._autoStart = false;
  248.         }
  249.         this._obs.removeObserver(this, "profile-after-change");
  250.         break;
  251.       case "quit-application-granted":
  252.         this._unload();
  253.         break;
  254.       case "private-browsing":
  255.         // clear all auth tokens
  256.         let sdr = Cc["@mozilla.org/security/sdr;1"].
  257.                   getService(Ci.nsISecretDecoderRing);
  258.         sdr.logoutAndTeardown();
  259.     
  260.         // clear plain HTTP auth sessions
  261.         let authMgr = Cc['@mozilla.org/network/http-auth-manager;1'].
  262.                       getService(Ci.nsIHttpAuthManager);
  263.         authMgr.clearAll();
  264.  
  265.         // Prevent any SSL sockets from remaining open.  Without this, SSL
  266.         // websites may fail to load after switching the private browsing mode
  267.         // because the SSL sockets may still be open while the corresponding
  268.         // NSS resources have been destroyed by the logoutAndTeardown call
  269.         // above.  See bug 463256 for more information.
  270.         let ios = Cc["@mozilla.org/network/io-service;1"].
  271.                   getService(Ci.nsIIOService);
  272.         if (!ios.offline) {
  273.           ios.offline = true;
  274.           ios.offline = false;
  275.         }
  276.  
  277.         if (!this._inPrivateBrowsing) {
  278.           // Clear the error console
  279.           let consoleService = Cc["@mozilla.org/consoleservice;1"].
  280.                                getService(Ci.nsIConsoleService);
  281.           consoleService.logStringMessage(null); // trigger the listeners
  282.           consoleService.reset();
  283.         }
  284.         break;
  285.     }
  286.   },
  287.  
  288.   // nsIPrivateBrowsingService
  289.  
  290.   /**
  291.    * Return the current status of private browsing.
  292.    */
  293.   get privateBrowsingEnabled PBS_get_privateBrowsingEnabled() {
  294.     return this._inPrivateBrowsing;
  295.   },
  296.  
  297.   /**
  298.    * Enter or leave private browsing mode.
  299.    */
  300.   set privateBrowsingEnabled PBS_set_privateBrowsingEnabled(val) {
  301.     // Allowing observers to set the private browsing status from their
  302.     // notification handlers is not desired, because it will change the
  303.     // status of the service while it's in the process of another transition.
  304.     // So, we detect a reentrant call here and throw an error.
  305.     // This is documented in nsIPrivateBrowsingService.idl.
  306.     if (this._alreadyChangingMode)
  307.       throw Cr.NS_ERROR_FAILURE;
  308.  
  309.     try {
  310.       this._alreadyChangingMode = true;
  311.  
  312.       if (val != this._inPrivateBrowsing) {
  313.         if (val) {
  314.           if (!this._canEnterPrivateBrowsingMode())
  315.             return;
  316.         }
  317.         else {
  318.           if (!this._canLeavePrivateBrowsingMode())
  319.             return;
  320.         }
  321.  
  322.         this._autoStarted = val ?
  323.           this._prefs.getBoolPref("browser.privatebrowsing.autostart") : false;
  324.         this._inPrivateBrowsing = val != false;
  325.  
  326.         let data = val ? "enter" : "exit";
  327.  
  328.         let quitting = Cc["@mozilla.org/supports-PRBool;1"].
  329.                        createInstance(Ci.nsISupportsPRBool);
  330.         quitting.data = this._quitting;
  331.  
  332.         // notify observers of the pending private browsing mode change
  333.         this._obs.notifyObservers(quitting, "private-browsing-change-granted", data);
  334.  
  335.         // destroy the current session and start initial cleanup
  336.         this._onBeforePrivateBrowsingModeChange();
  337.  
  338.         this._obs.notifyObservers(quitting, "private-browsing", data);
  339.  
  340.         // load the appropriate session
  341.         this._onAfterPrivateBrowsingModeChange();
  342.       }
  343.     } catch (ex) {
  344.       Cu.reportError("Exception thrown while processing the " +
  345.         "private browsing mode change request: " + ex.toString());
  346.     } finally {
  347.       this._alreadyChangingMode = false;
  348.     }
  349.   },
  350.  
  351.   /**
  352.    * Whether private browsing has been started automatically.
  353.    */
  354.   get autoStarted PBS_get_autoStarted() {
  355.     return this._autoStarted;
  356.   },
  357.  
  358.   removeDataFromDomain: function PBS_removeDataFromDomain(aDomain)
  359.   {
  360.  
  361.     // clear any and all network geolocation provider sessions
  362.     try {
  363.         this._prefs.deleteBranch("geo.wifi.access_token.");
  364.     } catch (e) {}
  365.     
  366.     // History
  367.     let (bh = Cc["@mozilla.org/browser/global-history;2"].
  368.               getService(Ci.nsIBrowserHistory)) {
  369.       bh.removePagesFromHost(aDomain, true);
  370.     }
  371.  
  372.     // Cache
  373.     let (cs = Cc["@mozilla.org/network/cache-service;1"].
  374.               getService(Ci.nsICacheService)) {
  375.       // NOTE: there is no way to clear just that domain, so we clear out
  376.       //       everything)
  377.       try {
  378.         cs.evictEntries(Ci.nsICache.STORE_ANYWHERE);
  379.       } catch (ex) {
  380.         Cu.reportError("Exception thrown while clearing the cache: " +
  381.           ex.toString());
  382.       }
  383.     }
  384.  
  385.     // Cookies
  386.     let (cm = Cc["@mozilla.org/cookiemanager;1"].
  387.               getService(Ci.nsICookieManager)) {
  388.       let enumerator = cm.enumerator;
  389.       while (enumerator.hasMoreElements()) {
  390.         let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie);
  391.         if (cookie.host.hasRootDomain(aDomain))
  392.           cm.remove(cookie.host, cookie.name, cookie.path, false);
  393.       }
  394.     }
  395.  
  396.     // Downloads
  397.     let (dm = Cc["@mozilla.org/download-manager;1"].
  398.               getService(Ci.nsIDownloadManager)) {
  399.       // Active downloads
  400.       let enumerator = dm.activeDownloads;
  401.       while (enumerator.hasMoreElements()) {
  402.         let dl = enumerator.getNext().QueryInterface(Ci.nsIDownload);
  403.         if (dl.source.host.hasRootDomain(aDomain)) {
  404.           dm.cancelDownload(dl.id);
  405.           dm.removeDownload(dl.id);
  406.         }
  407.       }
  408.  
  409.       // Completed downloads
  410.       let db = dm.DBConnection;
  411.       // NOTE: This is lossy, but we feel that it is OK to be lossy here and not
  412.       //       invoke the cost of creating a URI for each download entry and
  413.       //       ensure that the hostname matches.
  414.       let stmt = db.createStatement(
  415.         "DELETE FROM moz_downloads " +
  416.         "WHERE source LIKE ?1 ESCAPE '/' " +
  417.         "AND state NOT IN (?2, ?3, ?4)"
  418.       );
  419.       let pattern = stmt.escapeStringForLIKE(aDomain, "/");
  420.       stmt.bindStringParameter(0, "%" + pattern + "%");
  421.       stmt.bindInt32Parameter(1, Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING);
  422.       stmt.bindInt32Parameter(2, Ci.nsIDownloadManager.DOWNLOAD_PAUSED);
  423.       stmt.bindInt32Parameter(3, Ci.nsIDownloadManager.DOWNLOAD_QUEUED);
  424.       try {
  425.         stmt.execute();
  426.       }
  427.       finally {
  428.         stmt.finalize();
  429.       }
  430.  
  431.       // We want to rebuild the list if the UI is showing, so dispatch the
  432.       // observer topic
  433.       let os = Cc["@mozilla.org/observer-service;1"].
  434.                getService(Ci.nsIObserverService);
  435.       os.notifyObservers(null, "download-manager-remove-download", null);
  436.     }
  437.  
  438.     // Passwords
  439.     let (lm = Cc["@mozilla.org/login-manager;1"].
  440.               getService(Ci.nsILoginManager)) {
  441.       // Clear all passwords for domain
  442.       try {
  443.         let logins = lm.getAllLogins({});
  444.         for (let i = 0; i < logins.length; i++)
  445.           if (logins[i].hostname.hasRootDomain(aDomain))
  446.             lm.removeLogin(logins[i]);
  447.       }
  448.       // XXXehsan: is there a better way to do this rather than this
  449.       // hacky comparison?
  450.       catch (ex if ex.message.indexOf("User canceled Master Password entry") != -1) { }
  451.  
  452.       // Clear any "do not save for this site" for this domain
  453.       let disabledHosts = lm.getAllDisabledHosts({});
  454.       for (let i = 0; i < disabledHosts.length; i++)
  455.         if (disabledHosts[i].hasRootDomain(aDomain))
  456.           lm.setLoginSavingEnabled(disabledHosts, true);
  457.     }
  458.  
  459.     // Permissions
  460.     let (pm = Cc["@mozilla.org/permissionmanager;1"].
  461.               getService(Ci.nsIPermissionManager)) {
  462.       // Enumerate all of the permissions, and if one matches, remove it
  463.       let enumerator = pm.enumerator;
  464.       while (enumerator.hasMoreElements()) {
  465.         let perm = enumerator.getNext().QueryInterface(Ci.nsIPermission);
  466.         if (perm.host.hasRootDomain(aDomain))
  467.           pm.remove(perm.host, perm.type);
  468.       }
  469.     }
  470.  
  471.     // Content Preferences
  472.     let (cp = Cc["@mozilla.org/content-pref/service;1"].
  473.               getService(Ci.nsIContentPrefService)) {
  474.       let db = cp.DBConnection;
  475.       // First we need to get the list of "groups" which are really just domains
  476.       let names = [];
  477.       let stmt = db.createStatement(
  478.         "SELECT name " +
  479.         "FROM groups " +
  480.         "WHERE name LIKE ?1 ESCAPE '/'"
  481.       );
  482.       let pattern = stmt.escapeStringForLIKE(aDomain, "/");
  483.       stmt.bindStringParameter(0, "%" + pattern);
  484.       try {
  485.         while (stmt.executeStep())
  486.           if (stmt.getString(0).hasRootDomain(aDomain))
  487.             names.push(stmt.getString(0));
  488.       }
  489.       finally {
  490.         stmt.finalize();
  491.       }
  492.  
  493.       // Now, for each name we got back, remove all of its prefs.
  494.       for (let i = 0; i < names.length; i++) {
  495.         // The service only cares about the host of the URI, so we don't need a
  496.         // full nsIURI object here.
  497.         let uri = { host: names[i]};
  498.         let enumerator = cp.getPrefs(uri).enumerator;
  499.         while (enumerator.hasMoreElements()) {
  500.           let pref = enumerator.getNext().QueryInterface(Ci.nsIProperty);
  501.           cp.removePref(uri, pref.name);
  502.         }
  503.       }
  504.     }
  505.  
  506.     // Everybody else (including extensions)
  507.     this._obs.notifyObservers(null, "browser:purge-domain-data", aDomain);
  508.   }
  509. };
  510.  
  511. function NSGetModule(compMgr, fileSpec)
  512.   XPCOMUtils.generateModule([PrivateBrowsingService]);
  513.