author | Wes Kocher <wkocher@mozilla.com> |
Fri, 10 Oct 2014 18:16:02 -0700 | |
changeset 209937 | acc80d67a89f180a107ff67d9610994b4ced2175 |
parent 209936 | 2bbfe02f6295d0a33a429f97d14658b1f9c26dbb |
child 209938 | d273b68bdcb3de2e70df0d2f6bc4aa05089a04dc |
push id | 1 |
push user | root |
push date | Mon, 20 Oct 2014 17:29:22 +0000 |
bugs | 832700 |
milestone | 35.0a1 |
backs out | 9d6312427933f235b77e02b93b2d1dd8715def1a 660b7e69fac76de9d51cec8374d0c1b60e2feadc |
--- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -604,17 +604,16 @@ GK_ATOM(mousethrough, "mousethrough") GK_ATOM(mouseup, "mouseup") GK_ATOM(mozaudiochannel, "mozaudiochannel") GK_ATOM(mozasyncpanzoom, "mozasyncpanzoom") GK_ATOM(mozfullscreenchange, "mozfullscreenchange") GK_ATOM(mozfullscreenerror, "mozfullscreenerror") GK_ATOM(mozpasspointerevents, "mozpasspointerevents") GK_ATOM(mozpointerlockchange, "mozpointerlockchange") GK_ATOM(mozpointerlockerror, "mozpointerlockerror") -GK_ATOM(mozprivatebrowsing, "mozprivatebrowsing") GK_ATOM(moz_opaque, "moz-opaque") GK_ATOM(moz_action_hint, "mozactionhint") GK_ATOM(x_moz_errormessage, "x-moz-errormessage") GK_ATOM(msthemecompatible, "msthemecompatible") GK_ATOM(multicol, "multicol") GK_ATOM(multiple, "multiple") GK_ATOM(muted, "muted") GK_ATOM(name, "name")
--- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -846,17 +846,16 @@ nsDocShell::nsDocShell(): mIsExecutingOnLoadHandler(false), mIsPrintingOrPP(false), mSavingOldViewer(false), #ifdef DEBUG mInEnsureScriptEnv(false), #endif mAffectPrivateSessionLifetime(true), mInvisible(false), - mHasLoadedNonBlankURI(false), mDefaultLoadFlags(nsIRequest::LOAD_NORMAL), mFrameType(eFrameTypeRegular), mOwnOrContainingAppId(nsIScriptSecurityManager::UNKNOWN_APP_ID), mParentCharsetSource(0) { mHistoryID = ++gDocshellIDCounter; if (gDocShellCount++ == 0) { NS_ASSERTION(sURIFixup == nullptr, @@ -1924,20 +1923,16 @@ nsDocShell::SetCurrentURI(nsIURI *aURI, // We don't want to send a location change when we're displaying an error // page, and we don't want to change our idea of "current URI" either if (mLoadType == LOAD_ERROR_PAGE) { return false; } mCurrentURI = NS_TryToMakeImmutable(aURI); - if (!NS_IsAboutBlank(mCurrentURI)) { - mHasLoadedNonBlankURI = true; - } - bool isRoot = false; // Is this the root docshell bool isSubFrame = false; // Is this a subframe navigation? nsCOMPtr<nsIDocShellTreeItem> root; GetSameTypeRootTreeItem(getter_AddRefs(root)); if (root.get() == static_cast<nsIDocShellTreeItem *>(this)) { @@ -2278,25 +2273,16 @@ nsDocShell::SetPrivateBrowsing(bool aUse obs->PrivateModeChanged(aUsePrivateBrowsing); } } } return NS_OK; } NS_IMETHODIMP -nsDocShell::GetHasLoadedNonBlankURI(bool* aResult) -{ - NS_ENSURE_ARG_POINTER(aResult); - - *aResult = mHasLoadedNonBlankURI; - return NS_OK; -} - -NS_IMETHODIMP nsDocShell::GetUseRemoteTabs(bool* aUseRemoteTabs) { NS_ENSURE_ARG_POINTER(aUseRemoteTabs); *aUseRemoteTabs = mUseRemoteTabs; return NS_OK; }
--- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -910,17 +910,16 @@ protected: // @see nsIDocShellHistory::createdDynamically bool mDynamicallyCreated; #ifdef DEBUG bool mInEnsureScriptEnv; #endif bool mAffectPrivateSessionLifetime; bool mInvisible; - bool mHasLoadedNonBlankURI; uint64_t mHistoryID; uint32_t mDefaultLoadFlags; static nsIURIFixup *sURIFixup; nsRefPtr<nsDOMNavigationTiming> mTiming; // Are we a regular frame, a browser frame, or an app frame?
--- a/docshell/base/nsIDocShell.idl +++ b/docshell/base/nsIDocShell.idl @@ -49,17 +49,17 @@ interface nsIWebBrowserPrint; interface nsIVariant; interface nsIPrivacyTransitionObserver; interface nsIReflowObserver; interface nsIScrollObserver; interface nsITabParent; typedef unsigned long nsLoadFlags; -[scriptable, builtinclass, uuid(23157a63-26fd-44a0-a0f9-fdc64dcc004c)] +[scriptable, builtinclass, uuid(2b8e4a50-7744-454d-a05b-debead8070fe)] interface nsIDocShell : nsIDocShellTreeItem { /** * Loads a given URI. This will give priority to loading the requested URI * in the object implementing this interface. If it can't be loaded here * however, the URL dispatcher will go through its normal process of content * loading. * @@ -1012,16 +1012,9 @@ interface nsIDocShell : nsIDocShellTreeI * See the documentation for setOpener and getOpener about why we * don't use attribute here instead. */ [noscript,notxpcom,nostdcall] void setOpenedRemote(in nsITabParent aOpenedRemote); [noscript,notxpcom,nostdcall] nsITabParent getOpenedRemote(); // URLSearchParams for the window.location is owned by the docShell. [noscript,notxpcom] URLSearchParams getURLSearchParams(); - - /** - * This attribute determines whether a document which is not about:blank has - * already be loaded by this docShell. - */ - [infallible] readonly attribute boolean hasLoadedNonBlankURI; - };
--- a/dom/browser-element/BrowserElementChild.js +++ b/dom/browser-element/BrowserElementChild.js @@ -51,15 +51,8 @@ if (!('BrowserElementIsPreloaded' in thi } var BrowserElementIsReady = true; let infos = sendSyncMessage('browser-element-api:call', { 'msg_name': 'hello' })[0]; docShell.QueryInterface(Ci.nsIDocShellTreeItem).name = infos.name; docShell.setFullscreenAllowed(infos.fullscreenAllowed); -if (infos.isPrivate) { - if (!docShell.hasLoadedNonBlankURI) { - Cu.reportError("We should not switch to Private Browsing after loading a document."); - } else { - docShell.QueryInterface(Ci.nsILoadContext).usePrivateBrowsing = true; - } -}
--- a/dom/browser-element/BrowserElementParent.cpp +++ b/dom/browser-element/BrowserElementParent.cpp @@ -71,24 +71,16 @@ CreateIframe(Element* aOpenerFrameElemen aName, /* aNotify = */ false); // Indicate whether the iframe is should be remote. popupFrameElement->SetAttr(kNameSpaceID_None, nsGkAtoms::Remote, aRemote ? NS_LITERAL_STRING("true") : NS_LITERAL_STRING("false"), /* aNotify = */ false); - // Copy the opener frame's mozprivatebrowsing attribute to the popup frame. - nsAutoString mozprivatebrowsing; - if (aOpenerFrameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::mozprivatebrowsing, - mozprivatebrowsing)) { - popupFrameElement->SetAttr(kNameSpaceID_None, nsGkAtoms::mozprivatebrowsing, - mozprivatebrowsing, /* aNotify = */ false); - } - return popupFrameElement.forget(); } bool DispatchCustomDOMEvent(Element* aFrameElement, const nsAString& aEventName, JSContext* cx, JS::Handle<JS::Value> aDetailValue, nsEventStatus *aStatus) {
--- a/dom/browser-element/BrowserElementParent.jsm +++ b/dom/browser-element/BrowserElementParent.jsm @@ -384,18 +384,17 @@ BrowserElementParent.prototype = { this._domRequestReady = true; this._runPendingAPICall(); } return { name: this._frameElement.getAttribute('name'), fullscreenAllowed: this._frameElement.hasAttribute('allowfullscreen') || - this._frameElement.hasAttribute('mozallowfullscreen'), - isPrivate: this._frameElement.hasAttribute('mozprivatebrowsing') + this._frameElement.hasAttribute('mozallowfullscreen') }; }, _fireCtxMenuEvent: function(data) { let detail = data.json; let evtName = detail.msg_name; debug('fireCtxMenuEventFromMsg: ' + evtName + ' ' + detail);
--- a/dom/browser-element/mochitest/browserElementTestHelpers.js +++ b/dom/browser-element/mochitest/browserElementTestHelpers.js @@ -298,8 +298,281 @@ addEventListener('unload', function() { browserElementTestHelpers.cleanUp(); }); // Wait for the load event before unlocking the test-ready event. browserElementTestHelpers.lockTestReady(); addEventListener('load', function() { SimpleTest.executeSoon(browserElementTestHelpers.unlockTestReady.bind(browserElementTestHelpers)); }); + +////////////////////////////////// +// promise.js from the addon SDK with some modifications to the module +// boilerplate. +////////////////////////////////// + +;(function(id, factory) { // Module boilerplate :( + var globals = this; + factory(function require(id) { + return globals[id]; + }, (globals[id] = {}), { uri: document.location.href + '#' + id, id: id }); +}).call(this, 'Promise', function Promise(require, exports, module) { + +'use strict'; + +module.metadata = { + "stability": "unstable" +}; + +/** + * Internal utility: Wraps given `value` into simplified promise, successfully + * fulfilled to a given `value`. Note the result is not a complete promise + * implementation, as its method `then` does not returns anything. + */ +function fulfilled(value) { + return { then: function then(fulfill) { fulfill(value); } }; +} + +/** + * Internal utility: Wraps given input into simplified promise, pre-rejected + * with a given `reason`. Note the result is not a complete promise + * implementation, as its method `then` does not returns anything. + */ +function rejected(reason) { + return { then: function then(fulfill, reject) { reject(reason); } }; +} + +/** + * Internal utility: Returns `true` if given `value` is a promise. Value is + * assumed to be a promise if it implements method `then`. + */ +function isPromise(value) { + return value && typeof(value.then) === 'function'; +} + +/** + * Creates deferred object containing fresh promise & methods to either resolve + * or reject it. The result is an object with the following properties: + * - `promise` Eventual value representation implementing CommonJS [Promises/A] + * (http://wiki.commonjs.org/wiki/Promises/A) API. + * - `resolve` Single shot function that resolves enclosed `promise` with a + * given `value`. + * - `reject` Single shot function that rejects enclosed `promise` with a given + * `reason`. + * + * An optional `prototype` argument is used as a prototype of the returned + * `promise` allowing one to implement additional API. If prototype is not + * passed then it falls back to `Object.prototype`. + * + * ## Example + * + * function fetchURI(uri, type) { + * var deferred = defer(); + * var request = new XMLHttpRequest(); + * request.open("GET", uri, true); + * request.responseType = type; + * request.onload = function onload() { + * deferred.resolve(request.response); + * } + * request.onerror = function(event) { + * deferred.reject(event); + * } + * request.send(); + * + * return deferred.promise; + * } + */ +function defer(prototype) { + // Define FIFO queue of observer pairs. Once promise is resolved & all queued + // observers are forwarded to `result` and variable is set to `null`. + var observers = []; + + // Promise `result`, which will be assigned a resolution value once promise + // is resolved. Note that result will always be assigned promise (or alike) + // object to take care of propagation through promise chains. If result is + // `null` promise is not resolved yet. + var result = null; + + prototype = (prototype || prototype === null) ? prototype : Object.prototype; + + // Create an object implementing promise API. + var promise = Object.create(prototype, { + then: { value: function then(onFulfill, onError) { + var deferred = defer(prototype); + + function resolve(value) { + // If `onFulfill` handler is provided resolve `deferred.promise` with + // result of invoking it with a resolution value. If handler is not + // provided propagate value through. + try { + deferred.resolve(onFulfill ? onFulfill(value) : value); + } + // `onFulfill` may throw exception in which case resulting promise + // is rejected with thrown exception. + catch(error) { + if (exports._reportErrors && typeof(console) === 'object') + console.error(error); + // Note: Following is equivalent of `deferred.reject(error)`, + // we use this shortcut to reduce a stack. + deferred.resolve(rejected(error)); + } + } + + function reject(reason) { + try { + if (onError) deferred.resolve(onError(reason)); + else deferred.resolve(rejected(reason)); + } + catch(error) { + if (exports._reportErrors && typeof(console) === 'object') + console.error(error) + deferred.resolve(rejected(error)); + } + } + + // If enclosed promise (`this.promise`) observers queue is still alive + // enqueue a new observer pair into it. Note that this does not + // necessary means that promise is pending, it may already be resolved, + // but we still have to queue observers to guarantee an order of + // propagation. + if (observers) { + observers.push({ resolve: resolve, reject: reject }); + } + // Otherwise just forward observer pair right to a `result` promise. + else { + result.then(resolve, reject); + } + + return deferred.promise; + }} + }) + + var deferred = { + promise: promise, + /** + * Resolves associated `promise` to a given `value`, unless it's already + * resolved or rejected. Note that resolved promise is not necessary a + * successfully fulfilled. Promise may be resolved with a promise `value` + * in which case `value` promise's fulfillment / rejection will propagate + * up to a promise resolved with `value`. + */ + resolve: function resolve(value) { + if (!result) { + // Store resolution `value` in a `result` as a promise, so that all + // the subsequent handlers can be simply forwarded to it. Since + // `result` will be a promise all the value / error propagation will + // be uniformly taken care of. + result = isPromise(value) ? value : fulfilled(value); + + // Forward already registered observers to a `result` promise in the + // order they were registered. Note that we intentionally dequeue + // observer at a time until queue is exhausted. This makes sure that + // handlers registered as side effect of observer forwarding are + // queued instead of being invoked immediately, guaranteeing FIFO + // order. + while (observers.length) { + var observer = observers.shift(); + result.then(observer.resolve, observer.reject); + } + + // Once `observers` queue is exhausted we `null`-ify it, so that + // new handlers are forwarded straight to the `result`. + observers = null; + } + }, + /** + * Rejects associated `promise` with a given `reason`, unless it's already + * resolved / rejected. This is just a (better performing) convenience + * shortcut for `deferred.resolve(reject(reason))`. + */ + reject: function reject(reason) { + // Note that if promise is resolved that does not necessary means that it + // is successfully fulfilled. Resolution value may be a promise in which + // case its result propagates. In other words if promise `a` is resolved + // with promise `b`, `a` is either fulfilled or rejected depending + // on weather `b` is fulfilled or rejected. Here `deferred.promise` is + // resolved with a promise pre-rejected with a given `reason`, there for + // `deferred.promise` is rejected with a given `reason`. This may feel + // little awkward first, but doing it this way greatly simplifies + // propagation through promise chains. + deferred.resolve(rejected(reason)); + } + }; + + return deferred; +} +exports.defer = defer; + +/** + * Returns a promise resolved to a given `value`. Optionally a second + * `prototype` argument may be provided to be used as a prototype for the + * returned promise. + */ +function resolve(value, prototype) { + var deferred = defer(prototype); + deferred.resolve(value); + return deferred.promise; +} +exports.resolve = resolve; + +/** + * Returns a promise rejected with a given `reason`. Optionally a second + * `prototype` argument may be provided to be used as a prototype for the + * returned promise. + */ +function reject(reason, prototype) { + var deferred = defer(prototype); + deferred.reject(reason); + return deferred.promise; +} +exports.reject = reject; + +var promised = (function() { + // Note: Define shortcuts and utility functions here in order to avoid + // slower property accesses and unnecessary closure creations on each + // call of this popular function. + + var call = Function.call; + var concat = Array.prototype.concat; + + // Utility function that does following: + // execute([ f, self, args...]) => f.apply(self, args) + function execute(args) { return call.apply(call, args) } + + // Utility function that takes promise of `a` array and maybe promise `b` + // as arguments and returns promise for `a.concat(b)`. + function promisedConcat(promises, unknown) { + return promises.then(function(values) { + return resolve(unknown).then(function(value) { + return values.concat([ value ]) + }); + }); + } + + return function promised(f, prototype) { + /** + Returns a wrapped `f`, which when called returns a promise that resolves to + `f(...)` passing all the given arguments to it, which by the way may be + promises. Optionally second `prototype` argument may be provided to be used + a prototype for a returned promise. + + ## Example + + var promise = promised(Array)(1, promise(2), promise(3)) + promise.then(console.log) // => [ 1, 2, 3 ] + **/ + + return function promised() { + // create array of [ f, this, args... ] + return concat.apply([ f, this ], arguments). + // reduce it via `promisedConcat` to get promised array of fulfillments + reduce(promisedConcat, resolve([], prototype)). + // finally map that to promise of `f.apply(this, args...)` + then(execute); + } + } +})(); +exports.promised = promised; + +var all = promised(Array); +exports.all = all; + +});
deleted file mode 100644 --- a/dom/browser-element/mochitest/browserElement_PrivateBrowsing.js +++ /dev/null @@ -1,46 +0,0 @@ -/* Any copyright is dedicated to the public domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -// Test that the mozprivatebrowsing attribute works. -"use strict"; - -SimpleTest.waitForExplicitFinish(); -browserElementTestHelpers.setEnabledPref(true); -browserElementTestHelpers.addPermission(); - -function createFrame(aIsPrivate) { - var iframe = document.createElement("iframe"); - SpecialPowers.wrap(iframe).mozbrowser = true; - if (aIsPrivate) { - iframe.setAttribute("mozprivatebrowsing", "true"); - } - return iframe; -} - -function createTest(aIsPrivate, aExpected, aNext) { - info("createTest " + aIsPrivate + " " + aExpected); - return new Promise((aResolve) => { - var iframe = createFrame(aIsPrivate); - document.body.appendChild(iframe); - - iframe.addEventListener("mozbrowsershowmodalprompt", function(e) { - is(e.detail.message, aExpected, "Checking localstorage"); - aResolve(); - }); - - iframe.src = "file_browserElement_PrivateBrowsing.html"; - }); -} - -function runTest() { - // We first create a iframe in non private browsing mode, set up some - // localstorage, reopen it to check that we get the previously set value. - // Finally, open it in private browsing mode and check that localstorage - // is clear. - createTest(false, "EMPTY") - .then(() => { return createTest(false, "bar"); }) - .then(() => { return createTest(true, "EMPTY"); }) - .then(SimpleTest.finish); -} - -addEventListener("testready", runTest);
deleted file mode 100644 --- a/dom/browser-element/mochitest/file_browserElement_PrivateBrowsing.html +++ /dev/null @@ -1,10 +0,0 @@ -<html> -<body> -<script> - -var initialValue = localStorage.getItem("foo") || "EMPTY"; -localStorage.setItem("foo", "bar"); -alert(initialValue); -</script> -</body> -</html>
--- a/dom/browser-element/mochitest/mochitest-oop.ini +++ b/dom/browser-element/mochitest/mochitest-oop.ini @@ -54,17 +54,16 @@ skip-if = (toolkit == 'gonk' && !debug) skip-if = (toolkit == 'gonk' && !debug) [test_browserElement_oop_OpenWindowDifferentOrigin.html] skip-if = (toolkit == 'gonk' && !debug) [test_browserElement_oop_OpenWindowInFrame.html] skip-if = (toolkit == 'gonk' && !debug) [test_browserElement_oop_OpenWindowRejected.html] skip-if = (toolkit == 'gonk' && !debug) [test_browserElement_oop_Opensearch.html] -[test_browserElement_oop_PrivateBrowsing.html] [test_browserElement_oop_PromptCheck.html] [test_browserElement_oop_PromptConfirm.html] [test_browserElement_oop_PurgeHistory.html] [test_browserElement_oop_Reload.html] [test_browserElement_oop_ReloadPostRequest.html] [test_browserElement_oop_RemoveBrowserElement.html] [test_browserElement_oop_ScrollEvent.html] [test_browserElement_oop_SecurityChange.html]
--- a/dom/browser-element/mochitest/mochitest.ini +++ b/dom/browser-element/mochitest/mochitest.ini @@ -38,17 +38,16 @@ support-files = browserElement_Metachange.js browserElement_NextPaint.js browserElement_OpenNamed.js browserElement_OpenWindow.js browserElement_OpenWindowDifferentOrigin.js browserElement_OpenWindowInFrame.js browserElement_OpenWindowRejected.js browserElement_Opensearch.js - browserElement_PrivateBrowsing.js browserElement_PromptCheck.js browserElement_PromptConfirm.js browserElement_PurgeHistory.js browserElement_Reload.js browserElement_ReloadPostRequest.js browserElement_RemoveBrowserElement.js browserElement_ScrollEvent.js browserElement_SecurityChange.js @@ -85,17 +84,16 @@ support-files = file_browserElement_NextPaint.html file_browserElement_Open1.html file_browserElement_Open2.html file_browserElement_OpenNamed.html file_browserElement_OpenNamed2.html file_browserElement_OpenWindowDifferentOrigin.html file_browserElement_OpenWindowInFrame.html file_browserElement_OpenWindowRejected.html - file_browserElement_PrivateBrowsing.html file_browserElement_SecurityChange.html file_browserElement_SetVisibleFrames2_Outer.html file_browserElement_SetVisibleFrames_Inner.html file_browserElement_SetVisibleFrames_Outer.html file_browserElement_TargetBlank.html file_browserElement_TargetTop.html file_browserElement_XFrameOptions.sjs file_browserElement_XFrameOptionsAllowFrom.html @@ -167,17 +165,16 @@ skip-if = (toolkit == 'gonk' && !debug) skip-if = (toolkit == 'gonk' && !debug) [test_browserElement_inproc_OpenWindowDifferentOrigin.html] skip-if = (toolkit == 'gonk' && !debug) [test_browserElement_inproc_OpenWindowInFrame.html] skip-if = (toolkit == 'gonk' && !debug) [test_browserElement_inproc_OpenWindowRejected.html] skip-if = (toolkit == 'gonk' && !debug) [test_browserElement_inproc_Opensearch.html] -[test_browserElement_inproc_PrivateBrowsing.html] [test_browserElement_inproc_PromptCheck.html] [test_browserElement_inproc_PromptConfirm.html] [test_browserElement_inproc_PurgeHistory.html] [test_browserElement_inproc_ReloadPostRequest.html] [test_browserElement_inproc_RemoveBrowserElement.html] [test_browserElement_inproc_ScrollEvent.html] [test_browserElement_inproc_SecurityChange.html] skip-if = toolkit == 'android' || (toolkit == 'gonk' && !debug) # android(TIMED_OUT, bug 766586) androidx86(TIMED_OUT, bug 766586)
deleted file mode 100644 --- a/dom/browser-element/mochitest/test_browserElement_inproc_PrivateBrowsing.html +++ /dev/null @@ -1,19 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=832700 ---> -<head> - <title>Test for Bug 832700</title> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <script type="application/javascript" src="browserElementTestHelpers.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=832700">Mozilla Bug 832700</a> - -<script type="application/javascript;version=1.7" src="browserElement_PrivateBrowsing.js"> -</script> - -</body> -</html>
deleted file mode 100644 --- a/dom/browser-element/mochitest/test_browserElement_oop_PrivateBrowsing.html +++ /dev/null @@ -1,19 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=832700 ---> -<head> - <title>Test for Bug 832700</title> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <script type="application/javascript" src="browserElementTestHelpers.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=832700">Mozilla Bug 832700</a> - -<script type="application/javascript;version=1.7" src="browserElement_PrivateBrowsing.js"> -</script> - -</body> -</html>