mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-26 20:30:41 +00:00
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
commit
fde361620f
@ -1015,7 +1015,6 @@ pref("apz.allow_zooming", true);
|
||||
|
||||
// Gaia relies heavily on scroll events for now, so lets fire them
|
||||
// more often than the default value (100).
|
||||
pref("apz.asyncscroll.throttle", 40);
|
||||
pref("apz.pan_repaint_interval", 16);
|
||||
|
||||
// APZ physics settings, tuned by UX designers
|
||||
|
@ -5009,6 +5009,10 @@ nsBrowserAccess.prototype = {
|
||||
isTabContentWindow: function (aWindow) {
|
||||
return gBrowser.browsers.some(browser => browser.contentWindow == aWindow);
|
||||
},
|
||||
|
||||
canClose() {
|
||||
return CanCloseWindow();
|
||||
},
|
||||
}
|
||||
|
||||
function getTogglableToolbars() {
|
||||
@ -6565,6 +6569,26 @@ var IndexedDBPromptHelper = {
|
||||
}
|
||||
};
|
||||
|
||||
function CanCloseWindow()
|
||||
{
|
||||
// Avoid redundant calls to canClose from showing multiple
|
||||
// PermitUnload dialogs.
|
||||
if (window.skipNextCanClose) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (let browser of gBrowser.browsers) {
|
||||
let {permitUnload, timedOut} = browser.permitUnload();
|
||||
if (timedOut) {
|
||||
return true;
|
||||
}
|
||||
if (!permitUnload) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function WindowIsClosing()
|
||||
{
|
||||
if (TabView.isVisible()) {
|
||||
@ -6575,27 +6599,19 @@ function WindowIsClosing()
|
||||
if (!closeWindow(false, warnAboutClosingWindow))
|
||||
return false;
|
||||
|
||||
// Bug 967873 - Proxy nsDocumentViewer::PermitUnload to the child process
|
||||
if (gMultiProcessBrowser)
|
||||
// In theory we should exit here and the Window's internal Close
|
||||
// method should trigger canClose on nsBrowserAccess. However, by
|
||||
// that point it's too late to be able to show a prompt for
|
||||
// PermitUnload. So we do it here, when we still can.
|
||||
if (CanCloseWindow()) {
|
||||
// This flag ensures that the later canClose call does nothing.
|
||||
// It's only needed to make tests pass, since they detect the
|
||||
// prompt even when it's not actually shown.
|
||||
window.skipNextCanClose = true;
|
||||
return true;
|
||||
|
||||
for (let browser of gBrowser.browsers) {
|
||||
let ds = browser.docShell;
|
||||
// Passing true to permitUnload indicates we plan on closing the window.
|
||||
// This means that once unload is permitted, all further calls to
|
||||
// permitUnload will be ignored. This avoids getting multiple prompts
|
||||
// to unload the page.
|
||||
if (ds.contentViewer && !ds.contentViewer.permitUnload(true)) {
|
||||
// ... however, if the user aborts closing, we need to undo that,
|
||||
// to ensure they get prompted again when we next try to close the window.
|
||||
// We do this on the window's toplevel docshell instead of on the tab, so
|
||||
// that all tabs we iterated before will get this reset.
|
||||
window.getInterface(Ci.nsIDocShell).contentViewer.resetCloseWindow();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -131,6 +131,11 @@ chatBrowserAccess.prototype = {
|
||||
isTabContentWindow: function (aWindow) {
|
||||
return this.contentWindow == aWindow;
|
||||
},
|
||||
|
||||
canClose() {
|
||||
let {BrowserUtils} = Cu.import("resource://gre/modules/BrowserUtils.jsm", {});
|
||||
return BrowserUtils.canCloseWindow(window);
|
||||
},
|
||||
};
|
||||
|
||||
</script>
|
||||
|
@ -1,4 +1,4 @@
|
||||
// -*- indent-tabs-mode: nil; js-indent-level: 4 -*-
|
||||
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
@ -567,28 +567,17 @@ Sanitizer.prototype = {
|
||||
openWindows: {
|
||||
privateStateForNewWindow: "non-private",
|
||||
_canCloseWindow: function(aWindow) {
|
||||
// Bug 967873 - Proxy nsDocumentViewer::PermitUnload to the child process
|
||||
if (!aWindow.gMultiProcessBrowser) {
|
||||
// Cargo-culted out of browser.js' WindowIsClosing because we don't care
|
||||
// about TabView or the regular 'warn me before closing windows with N tabs'
|
||||
// stuff here, and more importantly, we want to set aCallerClosesWindow to true
|
||||
// when calling into permitUnload:
|
||||
for (let browser of aWindow.gBrowser.browsers) {
|
||||
let ds = browser.docShell;
|
||||
// 'true' here means we will be closing the window soon, so please don't dispatch
|
||||
// another onbeforeunload event when we do so. If unload is *not* permitted somewhere,
|
||||
// we will reset the flag that this triggers everywhere so that we don't interfere
|
||||
// with the browser after all:
|
||||
if (ds.contentViewer && !ds.contentViewer.permitUnload(true)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (aWindow.CanCloseWindow()) {
|
||||
// We already showed PermitUnload for the window, so let's
|
||||
// make sure we don't do it again when we actually close the
|
||||
// window.
|
||||
aWindow.skipNextCanClose = true;
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
_resetAllWindowClosures: function(aWindowList) {
|
||||
for (let win of aWindowList) {
|
||||
win.getInterface(Ci.nsIDocShell).contentViewer.resetCloseWindow();
|
||||
win.skipNextCanClose = false;
|
||||
}
|
||||
},
|
||||
clear: Task.async(function*() {
|
||||
|
@ -1156,25 +1156,29 @@
|
||||
this._tabAttrModified(this.mCurrentTab, ["selected"]);
|
||||
|
||||
if (oldBrowser != newBrowser &&
|
||||
oldBrowser.docShell &&
|
||||
oldBrowser.docShell.contentViewer.inPermitUnload) {
|
||||
// Since the user is switching away from a tab that has
|
||||
// a beforeunload prompt active, we remove the prompt.
|
||||
// This prevents confusing user flows like the following:
|
||||
// 1. User attempts to close Firefox
|
||||
// 2. User switches tabs (ingoring a beforeunload prompt)
|
||||
// 3. User returns to tab, presses "Leave page"
|
||||
let promptBox = this.getTabModalPromptBox(oldBrowser);
|
||||
let prompts = promptBox.listPrompts();
|
||||
// There might not be any prompts here if the tab was closed
|
||||
// while in an onbeforeunload prompt, which will have
|
||||
// destroyed aforementioned prompt already, so check there's
|
||||
// something to remove, first:
|
||||
if (prompts.length) {
|
||||
// NB: This code assumes that the beforeunload prompt
|
||||
// is the top-most prompt on the tab.
|
||||
prompts[prompts.length - 1].abortPrompt();
|
||||
}
|
||||
oldBrowser.getInPermitUnload) {
|
||||
oldBrowser.getInPermitUnload(inPermitUnload => {
|
||||
if (!inPermitUnload) {
|
||||
return;
|
||||
}
|
||||
// Since the user is switching away from a tab that has
|
||||
// a beforeunload prompt active, we remove the prompt.
|
||||
// This prevents confusing user flows like the following:
|
||||
// 1. User attempts to close Firefox
|
||||
// 2. User switches tabs (ingoring a beforeunload prompt)
|
||||
// 3. User returns to tab, presses "Leave page"
|
||||
let promptBox = this.getTabModalPromptBox(oldBrowser);
|
||||
let prompts = promptBox.listPrompts();
|
||||
// There might not be any prompts here if the tab was closed
|
||||
// while in an onbeforeunload prompt, which will have
|
||||
// destroyed aforementioned prompt already, so check there's
|
||||
// something to remove, first:
|
||||
if (prompts.length) {
|
||||
// NB: This code assumes that the beforeunload prompt
|
||||
// is the top-most prompt on the tab.
|
||||
prompts[prompts.length - 1].abortPrompt();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
oldBrowser._urlbarFocused = (gURLBar && gURLBar.focused);
|
||||
@ -2103,6 +2107,7 @@
|
||||
if (aParams) {
|
||||
var animate = aParams.animate;
|
||||
var byMouse = aParams.byMouse;
|
||||
var skipPermitUnload = aParams.skipPermitUnload;
|
||||
}
|
||||
|
||||
// Handle requests for synchronously removing an already
|
||||
@ -2115,7 +2120,7 @@
|
||||
|
||||
var isLastTab = (this.tabs.length - this._removingTabs.length == 1);
|
||||
|
||||
if (!this._beginRemoveTab(aTab, false, null, true))
|
||||
if (!this._beginRemoveTab(aTab, false, null, true, skipPermitUnload))
|
||||
return;
|
||||
|
||||
if (!aTab.pinned && !aTab.hidden && aTab._fullyOpen && byMouse)
|
||||
@ -2163,6 +2168,7 @@
|
||||
<parameter name="aTabWillBeMoved"/>
|
||||
<parameter name="aCloseWindowWithLastTab"/>
|
||||
<parameter name="aCloseWindowFastpath"/>
|
||||
<parameter name="aSkipPermitUnload"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (aTab.closing ||
|
||||
@ -2193,23 +2199,20 @@
|
||||
newTab = true;
|
||||
}
|
||||
|
||||
if (!aTab._pendingPermitUnload && !aTabWillBeMoved) {
|
||||
let ds = browser.docShell;
|
||||
if (ds && ds.contentViewer) {
|
||||
// We need to block while calling permitUnload() because it
|
||||
// processes the event queue and may lead to another removeTab()
|
||||
// call before permitUnload() returns.
|
||||
aTab._pendingPermitUnload = true;
|
||||
let permitUnload = ds.contentViewer.permitUnload();
|
||||
delete aTab._pendingPermitUnload;
|
||||
// If we were closed during onbeforeunload, we return false now
|
||||
// so we don't (try to) close the same tab again. Of course, we
|
||||
// also stop if the unload was cancelled by the user:
|
||||
if (aTab.closing || !permitUnload) {
|
||||
// NB: deliberately keep the _closedDuringPermitUnload set to
|
||||
// true so we keep exiting early in case of multiple calls.
|
||||
return false;
|
||||
}
|
||||
if (!aTab._pendingPermitUnload && !aTabWillBeMoved && !aSkipPermitUnload) {
|
||||
// We need to block while calling permitUnload() because it
|
||||
// processes the event queue and may lead to another removeTab()
|
||||
// call before permitUnload() returns.
|
||||
aTab._pendingPermitUnload = true;
|
||||
let {permitUnload} = browser.permitUnload();
|
||||
delete aTab._pendingPermitUnload;
|
||||
// If we were closed during onbeforeunload, we return false now
|
||||
// so we don't (try to) close the same tab again. Of course, we
|
||||
// also stop if the unload was cancelled by the user:
|
||||
if (aTab.closing || !permitUnload) {
|
||||
// NB: deliberately keep the _closedDuringPermitUnload set to
|
||||
// true so we keep exiting early in case of multiple calls.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4001,8 +4004,8 @@
|
||||
break;
|
||||
case "sizemodechange":
|
||||
if (aEvent.target == window) {
|
||||
this.mCurrentBrowser.docShellIsActive =
|
||||
(window.windowState != window.STATE_MINIMIZED);
|
||||
this.mCurrentBrowser.setDocShellIsActiveAndForeground(
|
||||
window.windowState != window.STATE_MINIMIZED);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -4027,13 +4030,19 @@
|
||||
}
|
||||
case "DOMWindowClose": {
|
||||
if (this.tabs.length == 1) {
|
||||
// We already did PermitUnload in the content process
|
||||
// for this tab (the only one in the window). So we don't
|
||||
// need to do it again for any tabs.
|
||||
window.skipNextCanClose = true;
|
||||
window.close();
|
||||
return;
|
||||
}
|
||||
|
||||
let tab = this.getTabForBrowser(browser);
|
||||
if (tab) {
|
||||
this.removeTab(tab);
|
||||
// Skip running PermitUnload since it already happened in
|
||||
// the content process.
|
||||
this.removeTab(tab, {skipPermitUnload: true});
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -4317,12 +4326,18 @@
|
||||
if (!event.isTrusted)
|
||||
return;
|
||||
|
||||
if (this.tabs.length == 1)
|
||||
if (this.tabs.length == 1) {
|
||||
// We already did PermitUnload in nsGlobalWindow::Close
|
||||
// for this tab. There are no other tabs we need to do
|
||||
// PermitUnload for.
|
||||
window.skipNextCanClose = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var tab = this._getTabForContentWindow(event.target);
|
||||
if (tab) {
|
||||
this.removeTab(tab);
|
||||
// Skip running PermitUnload since it already happened.
|
||||
this.removeTab(tab, {skipPermitUnload: true});
|
||||
event.preventDefault();
|
||||
}
|
||||
]]>
|
||||
|
@ -149,7 +149,6 @@ skip-if = e10s # Bug 1101993 - times out for unknown reasons when run in the dir
|
||||
[browser_backButtonFitts.js]
|
||||
skip-if = os == "mac" # The Fitt's Law back button is not supported on OS X
|
||||
[browser_beforeunload_duplicate_dialogs.js]
|
||||
skip-if = e10s # bug 967873 means permitUnload doesn't work in e10s mode
|
||||
[browser_blob-channelname.js]
|
||||
[browser_bookmark_titles.js]
|
||||
skip-if = buildapp == 'mulet' || toolkit == "windows" # Disabled on Windows due to frequent failures (bugs 825739, 841341)
|
||||
|
@ -71,7 +71,6 @@ public:
|
||||
bool MayLoadInternal(nsIURI* aURI) override;
|
||||
|
||||
nsCOMPtr<nsIURI> mURI;
|
||||
nsCOMPtr<nsIContentSecurityPolicy> mCSP;
|
||||
};
|
||||
|
||||
#endif // nsNullPrincipal_h__
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "nsNetCID.h"
|
||||
#include "jswrapper.h"
|
||||
|
||||
#include "mozilla/dom/nsCSPContext.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/HashFunctions.h"
|
||||
@ -76,7 +77,12 @@ nsPrincipal::nsPrincipal()
|
||||
{ }
|
||||
|
||||
nsPrincipal::~nsPrincipal()
|
||||
{ }
|
||||
{
|
||||
// let's clear the principal within the csp to avoid a tangling pointer
|
||||
if (mCSP) {
|
||||
static_cast<nsCSPContext*>(mCSP.get())->clearLoadingPrincipal();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsPrincipal::Init(nsIURI *aCodebase, const OriginAttributes& aOriginAttributes)
|
||||
@ -404,7 +410,7 @@ nsPrincipal::Read(nsIObjectInputStream* aStream)
|
||||
// need to link in the CSP context here (link in the URI of the protected
|
||||
// resource).
|
||||
if (csp) {
|
||||
csp->SetRequestContext(codebase, nullptr, nullptr);
|
||||
csp->SetRequestContext(nullptr, this);
|
||||
}
|
||||
|
||||
SetDomain(domain);
|
||||
|
1
config/external/nss/nss.def
vendored
1
config/external/nss/nss.def
vendored
@ -668,6 +668,7 @@ SSL_SetStapledOCSPResponses
|
||||
SSL_SetURL
|
||||
SSL_SNISocketConfigHook
|
||||
SSL_VersionRangeGet
|
||||
SSL_VersionRangeGetDefault
|
||||
SSL_VersionRangeGetSupported
|
||||
SSL_VersionRangeSet
|
||||
SSL_VersionRangeSetDefault
|
||||
|
@ -127,6 +127,8 @@ $(addprefix $(TOPOBJDIR)/,$(MANIFEST_TARGETS)): FORCE
|
||||
# Files to build with the recursive backend and simply copy
|
||||
$(TOPOBJDIR)/dist/bin/platform.ini: $(TOPOBJDIR)/toolkit/xre/platform.ini
|
||||
|
||||
$(TOPOBJDIR)/toolkit/xre/platform.ini: $(TOPOBJDIR)/config/buildid
|
||||
|
||||
# The xpidl target in config/makefiles/xpidl requires the install manifest for
|
||||
# dist/idl to have been processed.
|
||||
$(TOPOBJDIR)/config/makefiles/xpidl/xpidl: $(TOPOBJDIR)/install-dist_idl
|
||||
|
@ -5982,8 +5982,20 @@ nsDocShell::GetIsOffScreenBrowser(bool* aIsOffScreen)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::SetIsActiveAndForeground(bool aIsActive)
|
||||
{
|
||||
return SetIsActiveInternal(aIsActive, false);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::SetIsActive(bool aIsActive)
|
||||
{
|
||||
return SetIsActiveInternal(aIsActive, true);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDocShell::SetIsActiveInternal(bool aIsActive, bool aIsHidden)
|
||||
{
|
||||
// We disallow setting active on chrome docshells.
|
||||
if (mItemType == nsIDocShellTreeItem::typeChrome) {
|
||||
@ -5996,7 +6008,7 @@ nsDocShell::SetIsActive(bool aIsActive)
|
||||
// Tell the PresShell about it.
|
||||
nsCOMPtr<nsIPresShell> pshell = GetPresShell();
|
||||
if (pshell) {
|
||||
pshell->SetIsActive(aIsActive);
|
||||
pshell->SetIsActive(aIsActive, aIsHidden);
|
||||
}
|
||||
|
||||
// Tell the window about it
|
||||
@ -6030,7 +6042,11 @@ nsDocShell::SetIsActive(bool aIsActive)
|
||||
}
|
||||
|
||||
if (!docshell->GetIsBrowserOrApp()) {
|
||||
docshell->SetIsActive(aIsActive);
|
||||
if (aIsHidden) {
|
||||
docshell->SetIsActive(aIsActive);
|
||||
} else {
|
||||
docshell->SetIsActiveAndForeground(aIsActive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7789,7 +7805,7 @@ nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal,
|
||||
mTiming->NotifyBeforeUnload();
|
||||
|
||||
bool okToUnload;
|
||||
rv = mContentViewer->PermitUnload(false, &okToUnload);
|
||||
rv = mContentViewer->PermitUnload(&okToUnload);
|
||||
|
||||
if (NS_SUCCEEDED(rv) && !okToUnload) {
|
||||
// The user chose not to unload the page, interrupt the load.
|
||||
@ -10127,7 +10143,7 @@ nsDocShell::InternalLoad(nsIURI* aURI,
|
||||
// protocol handler deals with this for javascript: URLs.
|
||||
if (!isJavaScript && aFileName.IsVoid() && mContentViewer) {
|
||||
bool okToUnload;
|
||||
rv = mContentViewer->PermitUnload(false, &okToUnload);
|
||||
rv = mContentViewer->PermitUnload(&okToUnload);
|
||||
|
||||
if (NS_SUCCEEDED(rv) && !okToUnload) {
|
||||
// The user chose not to unload the page, interrupt the
|
||||
|
@ -485,6 +485,8 @@ protected:
|
||||
uint32_t aRedirectFlags,
|
||||
uint32_t aStateFlags) override;
|
||||
|
||||
nsresult SetIsActiveInternal(bool aIsActive, bool aIsHidden);
|
||||
|
||||
/**
|
||||
* Helper function that determines if channel is an HTTP POST.
|
||||
*
|
||||
|
@ -31,7 +31,7 @@ class nsDOMNavigationTiming;
|
||||
[ptr] native nsDOMNavigationTimingPtr(nsDOMNavigationTiming);
|
||||
[ref] native nsIContentViewerTArray(nsTArray<nsCOMPtr<nsIContentViewer> >);
|
||||
|
||||
[scriptable, builtinclass, uuid(fbd04c99-e149-473f-8a68-44f53d82f98b)]
|
||||
[scriptable, builtinclass, uuid(91b6c1f3-fc5f-43a9-88f4-9286bd19387f)]
|
||||
interface nsIContentViewer : nsISupports
|
||||
{
|
||||
[noscript] void init(in nsIWidgetPtr aParentWidget,
|
||||
@ -45,12 +45,8 @@ interface nsIContentViewer : nsISupports
|
||||
/**
|
||||
* Checks if the document wants to prevent unloading by firing beforeunload on
|
||||
* the document, and if it does, prompts the user. The result is returned.
|
||||
*
|
||||
* @param aCallerClosesWindow indicates that the current caller will close the
|
||||
* window. If the method returns true, all subsequent calls will be
|
||||
* ignored.
|
||||
*/
|
||||
boolean permitUnload([optional] in boolean aCallerClosesWindow);
|
||||
boolean permitUnload();
|
||||
|
||||
/**
|
||||
* Exposes whether we're blocked in a call to permitUnload.
|
||||
@ -62,8 +58,7 @@ interface nsIContentViewer : nsISupports
|
||||
* track of whether the user has responded to a prompt.
|
||||
* Used internally by the scriptable version to ensure we only prompt once.
|
||||
*/
|
||||
[noscript,nostdcall] boolean permitUnloadInternal(in boolean aCallerClosesWindow,
|
||||
inout boolean aShouldPrompt);
|
||||
[noscript,nostdcall] boolean permitUnloadInternal(inout boolean aShouldPrompt);
|
||||
|
||||
/**
|
||||
* Exposes whether we're in the process of firing the beforeunload event.
|
||||
@ -71,16 +66,6 @@ interface nsIContentViewer : nsISupports
|
||||
*/
|
||||
readonly attribute boolean beforeUnloadFiring;
|
||||
|
||||
/**
|
||||
* Works in tandem with permitUnload, if the caller decides not to close the
|
||||
* window it indicated it will, it is the caller's responsibility to reset
|
||||
* that with this method.
|
||||
*
|
||||
* @Note this method is only meant to be called on documents for which the
|
||||
* caller has indicated that it will close the window. If that is not the case
|
||||
* the behavior of this method is undefined.
|
||||
*/
|
||||
void resetCloseWindow();
|
||||
void pageHide(in boolean isUnload);
|
||||
|
||||
/**
|
||||
|
@ -43,7 +43,7 @@ interface nsITabParent;
|
||||
|
||||
typedef unsigned long nsLoadFlags;
|
||||
|
||||
[scriptable, builtinclass, uuid(b1df6e41-c8dd-45c2-bc18-dd330d986214)]
|
||||
[scriptable, builtinclass, uuid(41b1cf17-b37b-4a62-9df8-5f67cfecab3f)]
|
||||
interface nsIDocShell : nsIDocShellTreeItem
|
||||
{
|
||||
/**
|
||||
@ -628,6 +628,12 @@ interface nsIDocShell : nsIDocShellTreeItem
|
||||
*/
|
||||
attribute boolean isActive;
|
||||
|
||||
/**
|
||||
* Sets whether a docshell is active, as above, but ensuring it does
|
||||
* not discard its layers
|
||||
*/
|
||||
void setIsActiveAndForeground(in boolean aIsActive);
|
||||
|
||||
/**
|
||||
* Puts the docshell in prerendering mode. noscript because we want only
|
||||
* native code to be able to put a docshell in prerendering.
|
||||
|
@ -83,7 +83,6 @@ skip-if = e10s # Bug 1220927 - Test tries to do addSHistoryListener on content.
|
||||
[browser_loadURI.js]
|
||||
[browser_multiple_pushState.js]
|
||||
[browser_onbeforeunload_navigation.js]
|
||||
skip-if = e10s
|
||||
[browser_search_notification.js]
|
||||
[browser_timelineMarkers-01.js]
|
||||
[browser_timelineMarkers-02.js]
|
||||
|
@ -1,38 +1,41 @@
|
||||
/* The test text decoded correctly as Shift_JIS */
|
||||
const rightText="\u30E6\u30CB\u30B3\u30FC\u30C9\u306F\u3001\u3059\u3079\u3066\u306E\u6587\u5B57\u306B\u56FA\u6709\u306E\u756A\u53F7\u3092\u4ED8\u4E0E\u3057\u307E\u3059";
|
||||
const TEXT = {
|
||||
/* The test text decoded correctly as Shift_JIS */
|
||||
rightText: "\u30E6\u30CB\u30B3\u30FC\u30C9\u306F\u3001\u3059\u3079\u3066\u306E\u6587\u5B57\u306B\u56FA\u6709\u306E\u756A\u53F7\u3092\u4ED8\u4E0E\u3057\u307E\u3059",
|
||||
|
||||
const enteredText1="The quick brown fox jumps over the lazy dog";
|
||||
const enteredText2="\u03BE\u03B5\u03C3\u03BA\u03B5\u03C0\u03AC\u03B6\u03C9\u0020\u03C4\u1F74\u03BD\u0020\u03C8\u03C5\u03C7\u03BF\u03C6\u03B8\u03CC\u03C1\u03B1\u0020\u03B2\u03B4\u03B5\u03BB\u03C5\u03B3\u03BC\u03AF\u03B1";
|
||||
enteredText1: "The quick brown fox jumps over the lazy dog",
|
||||
enteredText2: "\u03BE\u03B5\u03C3\u03BA\u03B5\u03C0\u03AC\u03B6\u03C9\u0020\u03C4\u1F74\u03BD\u0020\u03C8\u03C5\u03C7\u03BF\u03C6\u03B8\u03CC\u03C1\u03B1\u0020\u03B2\u03B4\u03B5\u03BB\u03C5\u03B3\u03BC\u03AF\u03B1",
|
||||
};
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
|
||||
gBrowser.selectedTab = gBrowser.addTab(rootDir + "test-form_sjis.html");
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
|
||||
BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(afterOpen);
|
||||
}
|
||||
|
||||
function afterOpen() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
|
||||
BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(afterChangeCharset);
|
||||
|
||||
gBrowser.contentDocument.getElementById("testtextarea").value = enteredText1;
|
||||
gBrowser.contentDocument.getElementById("testinput").value = enteredText2;
|
||||
|
||||
/* Force the page encoding to Shift_JIS */
|
||||
BrowserSetForcedCharacterSet("Shift_JIS");
|
||||
ContentTask.spawn(gBrowser.selectedBrowser, TEXT, function(TEXT) {
|
||||
content.document.getElementById("testtextarea").value = TEXT.enteredText1;
|
||||
content.document.getElementById("testinput").value = TEXT.enteredText2;
|
||||
}).then(() => {
|
||||
/* Force the page encoding to Shift_JIS */
|
||||
BrowserSetForcedCharacterSet("Shift_JIS");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function afterChangeCharset() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterChangeCharset, true);
|
||||
|
||||
is(gBrowser.contentDocument.getElementById("testpar").innerHTML, rightText,
|
||||
"encoding successfully changed");
|
||||
is(gBrowser.contentDocument.getElementById("testtextarea").value, enteredText1,
|
||||
"text preserved in <textarea>");
|
||||
is(gBrowser.contentDocument.getElementById("testinput").value, enteredText2,
|
||||
"text preserved in <input>");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
ContentTask.spawn(gBrowser.selectedBrowser, TEXT, function(TEXT) {
|
||||
is(content.document.getElementById("testpar").innerHTML, TEXT.rightText,
|
||||
"encoding successfully changed");
|
||||
is(content.document.getElementById("testtextarea").value, TEXT.enteredText1,
|
||||
"text preserved in <textarea>");
|
||||
is(content.document.getElementById("testinput").value, TEXT.enteredText2,
|
||||
"text preserved in <input>");
|
||||
}).then(() => {
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
@ -1,39 +1,18 @@
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
|
||||
gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug234628-1.html");
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
|
||||
runCharsetTest(rootDir + "file_bug234628-1.html", afterOpen, "windows-1251", afterChangeCharset);
|
||||
}
|
||||
|
||||
function afterOpen(event) {
|
||||
if (event.target != gBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
function afterOpen() {
|
||||
is(content.document.documentElement.textContent.indexOf('\u20AC'), 129, "Parent doc should be windows-1252 initially");
|
||||
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
|
||||
|
||||
is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u20AC'), 129, "Parent doc should be windows-1252 initially");
|
||||
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 85, "Child doc should be windows-1252 initially");
|
||||
|
||||
BrowserSetForcedCharacterSet("windows-1251");
|
||||
is(content.frames[0].document.documentElement.textContent.indexOf('\u20AC'), 85, "Child doc should be windows-1252 initially");
|
||||
}
|
||||
|
||||
function afterChangeCharset(event) {
|
||||
if (event.target != gBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterChangeCharset, true);
|
||||
function afterChangeCharset() {
|
||||
is(content.document.documentElement.textContent.indexOf('\u0402'), 129, "Parent doc should decode as windows-1251 subsequently");
|
||||
is(content.frames[0].document.documentElement.textContent.indexOf('\u0402'), 85, "Child doc should decode as windows-1251 subsequently");
|
||||
|
||||
is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u0402'), 129, "Parent doc should decode as windows-1251 subsequently");
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u0402'), 85, "Child doc should decode as windows-1251 subsequently");
|
||||
|
||||
is(gBrowser.contentDocument.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.characterSet, "windows-1251", "Child doc should report windows-1251 subsequently");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
is(content.document.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
|
||||
is(content.frames[0].document.characterSet, "windows-1251", "Child doc should report windows-1251 subsequently");
|
||||
}
|
||||
|
@ -1,39 +1,18 @@
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
|
||||
gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug234628-10.html");
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
|
||||
runCharsetTest(rootDir + "file_bug234628-10.html", afterOpen, "windows-1251", afterChangeCharset);
|
||||
}
|
||||
|
||||
function afterOpen(event) {
|
||||
if (event.target != gBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
function afterOpen() {
|
||||
is(content.document.documentElement.textContent.indexOf('\u20AC'), 151, "Parent doc should be windows-1252 initially");
|
||||
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
|
||||
|
||||
is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u20AC'), 151, "Parent doc should be windows-1252 initially");
|
||||
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 71, "Child doc should be utf-8 initially");
|
||||
|
||||
BrowserSetForcedCharacterSet("windows-1251");
|
||||
is(content.frames[0].document.documentElement.textContent.indexOf('\u20AC'), 71, "Child doc should be utf-8 initially");
|
||||
}
|
||||
|
||||
function afterChangeCharset(event) {
|
||||
if (event.target != gBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterChangeCharset, true);
|
||||
function afterChangeCharset() {
|
||||
is(content.document.documentElement.textContent.indexOf('\u0402'), 151, "Parent doc should decode as windows-1251 subsequently");
|
||||
is(content.frames[0].document.documentElement.textContent.indexOf('\u20AC'), 71, "Child doc should decode as utf-8 subsequently");
|
||||
|
||||
is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u0402'), 151, "Parent doc should decode as windows-1251 subsequently");
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 71, "Child doc should decode as utf-8 subsequently");
|
||||
|
||||
is(gBrowser.contentDocument.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.characterSet, "UTF-8", "Child doc should report UTF-8 subsequently");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
is(content.document.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
|
||||
is(content.frames[0].document.characterSet, "UTF-8", "Child doc should report UTF-8 subsequently");
|
||||
}
|
||||
|
@ -1,39 +1,18 @@
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
|
||||
gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug234628-11.html");
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
|
||||
runCharsetTest(rootDir + "file_bug234628-11.html", afterOpen, "windows-1251", afterChangeCharset);
|
||||
}
|
||||
|
||||
function afterOpen(event) {
|
||||
if (event.target != gBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
function afterOpen() {
|
||||
is(content.document.documentElement.textContent.indexOf('\u20AC'), 193, "Parent doc should be windows-1252 initially");
|
||||
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
|
||||
|
||||
is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u20AC'), 193, "Parent doc should be windows-1252 initially");
|
||||
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 107, "Child doc should be utf-8 initially");
|
||||
|
||||
BrowserSetForcedCharacterSet("windows-1251");
|
||||
is(content.frames[0].document.documentElement.textContent.indexOf('\u20AC'), 107, "Child doc should be utf-8 initially");
|
||||
}
|
||||
|
||||
function afterChangeCharset(event) {
|
||||
if (event.target != gBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterChangeCharset, true);
|
||||
function afterChangeCharset() {
|
||||
is(content.document.documentElement.textContent.indexOf('\u0402'), 193, "Parent doc should decode as windows-1251 subsequently");
|
||||
is(content.frames[0].document.documentElement.textContent.indexOf('\u20AC'), 107, "Child doc should decode as utf-8 subsequently");
|
||||
|
||||
is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u0402'), 193, "Parent doc should decode as windows-1251 subsequently");
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 107, "Child doc should decode as utf-8 subsequently");
|
||||
|
||||
is(gBrowser.contentDocument.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.characterSet, "UTF-8", "Child doc should report UTF-8 subsequently");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
is(content.document.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
|
||||
is(content.frames[0].document.characterSet, "UTF-8", "Child doc should report UTF-8 subsequently");
|
||||
}
|
||||
|
@ -1,39 +1,18 @@
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
|
||||
gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug234628-2.html");
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
|
||||
runCharsetTest(rootDir + "file_bug234628-2.html", afterOpen, "windows-1251", afterChangeCharset);
|
||||
}
|
||||
|
||||
function afterOpen(event) {
|
||||
if (event.target != gBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
function afterOpen() {
|
||||
is(content.document.documentElement.textContent.indexOf('\u20AC'), 129, "Parent doc should be windows-1252 initially");
|
||||
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
|
||||
|
||||
is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u20AC'), 129, "Parent doc should be windows-1252 initially");
|
||||
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u00E2\u201A\u00AC'), 78, "Child doc should be windows-1252 initially");
|
||||
|
||||
BrowserSetForcedCharacterSet("windows-1251");
|
||||
is(content.frames[0].document.documentElement.textContent.indexOf('\u00E2\u201A\u00AC'), 78, "Child doc should be windows-1252 initially");
|
||||
}
|
||||
|
||||
function afterChangeCharset(event) {
|
||||
if (event.target != gBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterChangeCharset, true);
|
||||
function afterChangeCharset() {
|
||||
is(content.document.documentElement.textContent.indexOf('\u0402'), 129, "Parent doc should decode as windows-1251 subsequently");
|
||||
is(content.frames[0].document.documentElement.textContent.indexOf('\u0432\u201A\u00AC'), 78, "Child doc should decode as windows-1251 subsequently");
|
||||
|
||||
is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u0402'), 129, "Parent doc should decode as windows-1251 subsequently");
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u0432\u201A\u00AC'), 78, "Child doc should decode as windows-1251 subsequently");
|
||||
|
||||
is(gBrowser.contentDocument.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.characterSet, "windows-1251", "Child doc should report windows-1251 subsequently");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
is(content.document.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
|
||||
is(content.frames[0].document.characterSet, "windows-1251", "Child doc should report windows-1251 subsequently");
|
||||
}
|
||||
|
@ -1,39 +1,18 @@
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
|
||||
gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug234628-3.html");
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
|
||||
runCharsetTest(rootDir + "file_bug234628-3.html", afterOpen, "windows-1251", afterChangeCharset);
|
||||
}
|
||||
|
||||
function afterOpen(event) {
|
||||
if (event.target != gBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
function afterOpen() {
|
||||
is(content.document.documentElement.textContent.indexOf('\u20AC'), 118, "Parent doc should be windows-1252 initially");
|
||||
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
|
||||
|
||||
is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u20AC'), 118, "Parent doc should be windows-1252 initially");
|
||||
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 73, "Child doc should be utf-8 initially");
|
||||
|
||||
BrowserSetForcedCharacterSet("windows-1251");
|
||||
is(content.frames[0].document.documentElement.textContent.indexOf('\u20AC'), 73, "Child doc should be utf-8 initially");
|
||||
}
|
||||
|
||||
function afterChangeCharset(event) {
|
||||
if (event.target != gBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterChangeCharset, true);
|
||||
function afterChangeCharset() {
|
||||
is(content.document.documentElement.textContent.indexOf('\u0402'), 118, "Parent doc should decode as windows-1251 subsequently");
|
||||
is(content.frames[0].document.documentElement.textContent.indexOf('\u0432\u201A\u00AC'), 73, "Child doc should decode as windows-1251 subsequently");
|
||||
|
||||
is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u0402'), 118, "Parent doc should decode as windows-1251 subsequently");
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u0432\u201A\u00AC'), 73, "Child doc should decode as windows-1251 subsequently");
|
||||
|
||||
is(gBrowser.contentDocument.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.characterSet, "windows-1251", "Child doc should report windows-1251 subsequently");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
is(content.document.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
|
||||
is(content.frames[0].document.characterSet, "windows-1251", "Child doc should report windows-1251 subsequently");
|
||||
}
|
||||
|
@ -1,39 +1,18 @@
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
|
||||
gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug234628-4.html");
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
|
||||
runCharsetTest(rootDir + "file_bug234628-4.html", afterOpen, "windows-1251", afterChangeCharset);
|
||||
}
|
||||
|
||||
function afterOpen(event) {
|
||||
if (event.target != gBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
function afterOpen() {
|
||||
is(content.document.documentElement.textContent.indexOf('\u20AC'), 132, "Parent doc should be windows-1252 initially");
|
||||
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
|
||||
|
||||
is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u20AC'), 132, "Parent doc should be windows-1252 initially");
|
||||
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 79, "Child doc should be utf-8 initially");
|
||||
|
||||
BrowserSetForcedCharacterSet("windows-1251");
|
||||
is(content.frames[0].document.documentElement.textContent.indexOf('\u20AC'), 79, "Child doc should be utf-8 initially");
|
||||
}
|
||||
|
||||
function afterChangeCharset(event) {
|
||||
if (event.target != gBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterChangeCharset, true);
|
||||
function afterChangeCharset() {
|
||||
is(content.document.documentElement.textContent.indexOf('\u0402'), 132, "Parent doc should decode as windows-1251 subsequently");
|
||||
is(content.frames[0].document.documentElement.textContent.indexOf('\u20AC'), 79, "Child doc should decode as utf-8 subsequently");
|
||||
|
||||
is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u0402'), 132, "Parent doc should decode as windows-1251 subsequently");
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 79, "Child doc should decode as utf-8 subsequently");
|
||||
|
||||
is(gBrowser.contentDocument.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.characterSet, "UTF-8", "Child doc should report UTF-8 subsequently");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
is(content.document.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
|
||||
is(content.frames[0].document.characterSet, "UTF-8", "Child doc should report UTF-8 subsequently");
|
||||
}
|
||||
|
@ -1,39 +1,18 @@
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
|
||||
gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug234628-5.html");
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
|
||||
runCharsetTest(rootDir + "file_bug234628-5.html", afterOpen, "windows-1251", afterChangeCharset);
|
||||
}
|
||||
|
||||
function afterOpen(event) {
|
||||
if (event.target != gBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
function afterOpen() {
|
||||
is(content.document.documentElement.textContent.indexOf('\u20AC'), 146, "Parent doc should be windows-1252 initially");
|
||||
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
|
||||
|
||||
is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u20AC'), 146, "Parent doc should be windows-1252 initially");
|
||||
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 87, "Child doc should be utf-16 initially");
|
||||
|
||||
BrowserSetForcedCharacterSet("windows-1251");
|
||||
is(content.frames[0].document.documentElement.textContent.indexOf('\u20AC'), 87, "Child doc should be utf-16 initially");
|
||||
}
|
||||
|
||||
function afterChangeCharset(event) {
|
||||
if (event.target != gBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterChangeCharset, true);
|
||||
function afterChangeCharset() {
|
||||
is(content.document.documentElement.textContent.indexOf('\u0402'), 146, "Parent doc should decode as windows-1251 subsequently");
|
||||
is(content.frames[0].document.documentElement.textContent.indexOf('\u20AC'), 87, "Child doc should decode as utf-16 subsequently");
|
||||
|
||||
is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u0402'), 146, "Parent doc should decode as windows-1251 subsequently");
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 87, "Child doc should decode as utf-16 subsequently");
|
||||
|
||||
is(gBrowser.contentDocument.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.characterSet, "UTF-16LE", "Child doc should report UTF-16LE subsequently");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
is(content.document.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
|
||||
is(content.frames[0].document.characterSet, "UTF-16LE", "Child doc should report UTF-16LE subsequently");
|
||||
}
|
||||
|
@ -1,39 +1,18 @@
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
|
||||
gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug234628-6.html");
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
|
||||
runCharsetTest(rootDir + "file_bug234628-6.html", afterOpen, "windows-1251", afterChangeCharset);
|
||||
}
|
||||
|
||||
function afterOpen(event) {
|
||||
if (event.target != gBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
function afterOpen() {
|
||||
is(content.document.documentElement.textContent.indexOf('\u20AC'), 190, "Parent doc should be windows-1252 initially");
|
||||
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
|
||||
|
||||
is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u20AC'), 190, "Parent doc should be windows-1252 initially");
|
||||
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 109, "Child doc should be utf-16 initially");
|
||||
|
||||
BrowserSetForcedCharacterSet("windows-1251");
|
||||
is(content.frames[0].document.documentElement.textContent.indexOf('\u20AC'), 109, "Child doc should be utf-16 initially");
|
||||
}
|
||||
|
||||
function afterChangeCharset(event) {
|
||||
if (event.target != gBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterChangeCharset, true);
|
||||
function afterChangeCharset() {
|
||||
is(content.document.documentElement.textContent.indexOf('\u0402'), 190, "Parent doc should decode as windows-1251 subsequently");
|
||||
is(content.frames[0].document.documentElement.textContent.indexOf('\u20AC'), 109, "Child doc should decode as utf-16 subsequently");
|
||||
|
||||
is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u0402'), 190, "Parent doc should decode as windows-1251 subsequently");
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 109, "Child doc should decode as utf-16 subsequently");
|
||||
|
||||
is(gBrowser.contentDocument.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.characterSet, "UTF-16BE", "Child doc should report UTF-16 subsequently");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
is(content.document.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
|
||||
is(content.frames[0].document.characterSet, "UTF-16BE", "Child doc should report UTF-16 subsequently");
|
||||
}
|
||||
|
@ -1,39 +1,18 @@
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
|
||||
gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug234628-7.html");
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
|
||||
runCharsetTest(rootDir + "file_bug234628-7.html", afterOpen, "windows-1251", afterChangeCharset);
|
||||
}
|
||||
|
||||
function afterOpen(event) {
|
||||
if (event.target != gBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
function afterOpen() {
|
||||
is(content.document.documentElement.textContent.indexOf('\u20AC'), 188, "Parent doc should be windows-1252 initially");
|
||||
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
|
||||
|
||||
is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u20AC'), 188, "Parent doc should be windows-1252 initially");
|
||||
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 107, "Child doc should be utf-8 initially");
|
||||
|
||||
BrowserSetForcedCharacterSet("windows-1251");
|
||||
is(content.frames[0].document.documentElement.textContent.indexOf('\u20AC'), 107, "Child doc should be utf-8 initially");
|
||||
}
|
||||
|
||||
function afterChangeCharset(event) {
|
||||
if (event.target != gBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterChangeCharset, true);
|
||||
function afterChangeCharset() {
|
||||
is(content.document.documentElement.textContent.indexOf('\u0402'), 188, "Parent doc should decode as windows-1251 subsequently");
|
||||
is(content.frames[0].document.documentElement.textContent.indexOf('\u0432\u201A\u00AC'), 107, "Child doc should decode as windows-1251 subsequently");
|
||||
|
||||
is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u0402'), 188, "Parent doc should decode as windows-1251 subsequently");
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u0432\u201A\u00AC'), 107, "Child doc should decode as windows-1251 subsequently");
|
||||
|
||||
is(gBrowser.contentDocument.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.characterSet, "windows-1251", "Child doc should report windows-1251 subsequently");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
is(content.document.characterSet, "windows-1251", "Parent doc should report windows-1251 subsequently");
|
||||
is(content.frames[0].document.characterSet, "windows-1251", "Child doc should report windows-1251 subsequently");
|
||||
}
|
||||
|
@ -1,23 +1,11 @@
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
|
||||
gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug234628-8.html");
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
|
||||
runCharsetTest(rootDir + "file_bug234628-8.html", afterOpen);
|
||||
}
|
||||
|
||||
function afterOpen(event) {
|
||||
if (event.target != gBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
function afterOpen() {
|
||||
is(content.document.documentElement.textContent.indexOf('\u0402'), 156, "Parent doc should be windows-1251");
|
||||
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
|
||||
|
||||
is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u0402'), 156, "Parent doc should be windows-1251");
|
||||
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u0402'), 99, "Child doc should be windows-1251");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
is(content.frames[0].document.documentElement.textContent.indexOf('\u0402'), 99, "Child doc should be windows-1251");
|
||||
}
|
||||
|
||||
|
@ -1,23 +1,11 @@
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
|
||||
gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug234628-9.html");
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
|
||||
runCharsetTest(rootDir + "file_bug234628-9.html", afterOpen);
|
||||
}
|
||||
|
||||
function afterOpen(event) {
|
||||
if (event.target != gBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
function afterOpen() {
|
||||
is(content.document.documentElement.textContent.indexOf('\u20AC'), 145, "Parent doc should be UTF-16");
|
||||
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
|
||||
|
||||
is(gBrowser.contentDocument.documentElement.textContent.indexOf('\u20AC'), 145, "Parent doc should be UTF-16");
|
||||
|
||||
is(gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument.documentElement.textContent.indexOf('\u20AC'), 96, "Child doc should be windows-1252");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
is(content.frames[0].document.documentElement.textContent.indexOf('\u20AC'), 96, "Child doc should be windows-1252");
|
||||
}
|
||||
|
||||
|
@ -22,28 +22,30 @@ function test() {
|
||||
let tab = gBrowser.addTab(doc);
|
||||
let tabBrowser = tab.linkedBrowser;
|
||||
|
||||
tabBrowser.addEventListener('load', function(aEvent) {
|
||||
tabBrowser.removeEventListener('load', arguments.callee, true);
|
||||
BrowserTestUtils.browserLoaded(tab.linkedBrowser).then(() => {
|
||||
return ContentTask.spawn(tab.linkedBrowser, null, () => {
|
||||
return new Promise(resolve => {
|
||||
// The main page has loaded. Now wait for the iframe to load.
|
||||
let iframe = content.document.getElementById('iframe');
|
||||
iframe.addEventListener('load', function listener(aEvent) {
|
||||
|
||||
// The main page has loaded. Now wait for the iframe to load.
|
||||
let iframe = tabBrowser.contentWindow.document.getElementById('iframe');
|
||||
iframe.addEventListener('load', function(aEvent) {
|
||||
// Wait for the iframe to load the new document, not about:blank.
|
||||
if (!iframe.src)
|
||||
return;
|
||||
|
||||
// Wait for the iframe to load the new document, not about:blank.
|
||||
if (!iframe.src)
|
||||
return;
|
||||
iframe.removeEventListener('load', listener, true);
|
||||
let shistory = content
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.sessionHistory;
|
||||
|
||||
iframe.removeEventListener('load', arguments.callee, true);
|
||||
let shistory = tabBrowser.contentWindow
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.sessionHistory;
|
||||
|
||||
is(shistory.count, 1, 'shistory count should be 1.');
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
finish();
|
||||
|
||||
}, true);
|
||||
}, true);
|
||||
is(shistory.count, 1, 'shistory count should be 1.');
|
||||
resolve();
|
||||
}, true);
|
||||
});
|
||||
});
|
||||
}).then(() => {
|
||||
gBrowser.removeTab(tab);
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
@ -4,28 +4,18 @@ function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug852909.png");
|
||||
gBrowser.selectedBrowser.addEventListener("load", image, true);
|
||||
BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(image);
|
||||
}
|
||||
|
||||
function image(event) {
|
||||
if (event.target != gBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
gBrowser.selectedBrowser.removeEventListener("load", image, true);
|
||||
|
||||
ok(!gBrowser.selectedTab.mayEnableCharacterEncodingMenu, "Docshell should say the menu should be disabled for images.");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug852909.pdf");
|
||||
gBrowser.selectedBrowser.addEventListener("load", pdf, true);
|
||||
BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(pdf);
|
||||
}
|
||||
|
||||
function pdf(event) {
|
||||
if (event.target != gBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
gBrowser.selectedBrowser.removeEventListener("load", pdf, true);
|
||||
|
||||
ok(!gBrowser.selectedTab.mayEnableCharacterEncodingMenu, "Docshell should say the menu should be disabled for PDF.js.");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
|
@ -2,40 +2,39 @@
|
||||
* %83%86%83%6a%83%52%81%5b%83%68%82%cd%81%41%82%b7%82%d7%82%c4%82%cc%95%b6%8e%9a%82%c9%8c%c5%97%4c%82%cc%94%d4%8d%86%82%f0%95%74%97%5e%82%b5%82%dc%82%b7
|
||||
*/
|
||||
|
||||
/* The test text decoded correctly as Shift_JIS */
|
||||
const rightText="\u30E6\u30CB\u30B3\u30FC\u30C9\u306F\u3001\u3059\u3079\u3066\u306E\u6587\u5B57\u306B\u56FA\u6709\u306E\u756A\u53F7\u3092\u4ED8\u4E0E\u3057\u307E\u3059";
|
||||
|
||||
/* The test text decoded incorrectly as Windows-1251. This is the "right" wrong
|
||||
text; anything else is unexpected. */
|
||||
const wrongText="\u0453\u2020\u0453\u006A\u0453\u0052\u0403\u005B\u0453\u0068\u201A\u041D\u0403\u0041\u201A\u00B7\u201A\u0427\u201A\u0414\u201A\u041C\u2022\u00B6\u040B\u0459\u201A\u0419\u040A\u0415\u2014\u004C\u201A\u041C\u201D\u0424\u040C\u2020\u201A\u0440\u2022\u0074\u2014\u005E\u201A\u00B5\u201A\u042C\u201A\u00B7";
|
||||
|
||||
function testContent(text) {
|
||||
is(gBrowser.contentDocument.getElementById("testpar").innerHTML, text,
|
||||
"<p> contains expected text");
|
||||
is(gBrowser.contentDocument.getElementById("testtextarea").innerHTML, text,
|
||||
"<textarea> contains expected text");
|
||||
is(gBrowser.contentDocument.getElementById("testinput").value, text,
|
||||
"<input> contains expected text");
|
||||
return ContentTask.spawn(gBrowser.selectedBrowser, text, text => {
|
||||
is(content.document.getElementById("testpar").innerHTML, text,
|
||||
"<p> contains expected text");
|
||||
is(content.document.getElementById("testtextarea").innerHTML, text,
|
||||
"<textarea> contains expected text");
|
||||
is(content.document.getElementById("testinput").value, text,
|
||||
"<input> contains expected text");
|
||||
});
|
||||
}
|
||||
|
||||
function afterOpen() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterOpen, true);
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterChangeCharset, true);
|
||||
BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(afterChangeCharset);
|
||||
|
||||
/* The test text decoded incorrectly as Windows-1251. This is the "right" wrong
|
||||
text; anything else is unexpected. */
|
||||
const wrongText="\u0453\u2020\u0453\u006A\u0453\u0052\u0403\u005B\u0453\u0068\u201A\u041D\u0403\u0041\u201A\u00B7\u201A\u0427\u201A\u0414\u201A\u041C\u2022\u00B6\u040B\u0459\u201A\u0419\u040A\u0415\u2014\u004C\u201A\u041C\u201D\u0424\u040C\u2020\u201A\u0440\u2022\u0074\u2014\u005E\u201A\u00B5\u201A\u042C\u201A\u00B7";
|
||||
|
||||
/* Test that the content on load is the expected wrong decoding */
|
||||
testContent(wrongText);
|
||||
|
||||
/* Force the page encoding to Shift_JIS */
|
||||
BrowserSetForcedCharacterSet("Shift_JIS");
|
||||
testContent(wrongText).then(() => {
|
||||
BrowserSetForcedCharacterSet("Shift_JIS");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function afterChangeCharset() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", afterChangeCharset, true);
|
||||
/* The test text decoded correctly as Shift_JIS */
|
||||
const rightText="\u30E6\u30CB\u30B3\u30FC\u30C9\u306F\u3001\u3059\u3079\u3066\u306E\u6587\u5B57\u306B\u56FA\u6709\u306E\u756A\u53F7\u3092\u4ED8\u4E0E\u3057\u307E\u3059";
|
||||
|
||||
/* test that the content is decoded correctly */
|
||||
testContent(rightText);
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
testContent(rightText).then(() => {
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
function test() {
|
||||
@ -51,5 +50,5 @@ function test() {
|
||||
var rootDir = Services.io.newFileURI(dir).spec;
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab(rootDir + "test-form_sjis.html");
|
||||
gBrowser.selectedBrowser.addEventListener("load", afterOpen, true);
|
||||
BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(afterOpen);
|
||||
}
|
||||
|
@ -60,3 +60,40 @@ function timelineTestOpenUrl(url) {
|
||||
|
||||
return Promise.all([tabSwitchPromise, loadPromise]).then(([_, tab]) => tab);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for charset tests. It loads |url| in a new tab,
|
||||
* runs |check1| in a ContentTask when the page is ready, switches the
|
||||
* charset to |charset|, and then runs |check2| in a ContentTask when
|
||||
* the page has finished reloading.
|
||||
*
|
||||
* |charset| and |check2| can be omitted, in which case the test
|
||||
* finishes when |check1| completes.
|
||||
*/
|
||||
function runCharsetTest(url, check1, charset, check2) {
|
||||
waitForExplicitFinish();
|
||||
|
||||
BrowserTestUtils.openNewForegroundTab(gBrowser, url, true).then(afterOpen);
|
||||
|
||||
function afterOpen() {
|
||||
if (charset) {
|
||||
BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(afterChangeCharset);
|
||||
|
||||
ContentTask.spawn(gBrowser.selectedBrowser, null, check1).then(() => {
|
||||
BrowserSetForcedCharacterSet(charset);
|
||||
});
|
||||
} else {
|
||||
ContentTask.spawn(gBrowser.selectedBrowser, null, check1).then(() => {
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function afterChangeCharset() {
|
||||
ContentTask.spawn(gBrowser.selectedBrowser, null, check2).then(() => {
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -89,7 +89,6 @@ function testApp(isValidWidget) {
|
||||
"mozbrowsertitlechange",
|
||||
"mozbrowseropenwindow",
|
||||
"mozbrowserscroll",
|
||||
"mozbrowserasyncscroll"
|
||||
].forEach( function(topic) {
|
||||
ifr.addEventListener(topic, function() {
|
||||
ok(false, topic + " should be hidden");
|
||||
@ -173,7 +172,7 @@ function checkIsWidgetScript(testMozbrowserEvent) {
|
||||
var win = content.window.open("about:blank"); /* test mozbrowseropenwindow */
|
||||
/*Close new window to avoid mochitest "unable to restore focus" failures.*/
|
||||
win.close();
|
||||
content.window.scrollTo(4000, 4000); /* test mozbrowser(async)scroll */
|
||||
content.window.scrollTo(4000, 4000); /* test mozbrowserscroll */
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,35 +3,22 @@
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
var archiveReaderEnabled = false;
|
||||
|
||||
var testGenerator = testSteps();
|
||||
var testGenerator;
|
||||
|
||||
function runTest()
|
||||
{
|
||||
enableArchiveReader();
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
testGenerator.next();
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [ ["dom.archivereader.enabled", true] ]}, function() {
|
||||
testGenerator = testSteps();
|
||||
return testGenerator.next();
|
||||
});
|
||||
}
|
||||
|
||||
function finishTest()
|
||||
{
|
||||
resetArchiveReader();
|
||||
|
||||
SimpleTest.executeSoon(function() {
|
||||
SpecialPowers.popPrefEnv(function() {
|
||||
testGenerator.close();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
function enableArchiveReader()
|
||||
{
|
||||
archiveReaderEnabled = SpecialPowers.getBoolPref("dom.archivereader.enabled");
|
||||
SpecialPowers.setBoolPref("dom.archivereader.enabled", true);
|
||||
}
|
||||
|
||||
function resetArchiveReader()
|
||||
{
|
||||
SpecialPowers.setBoolPref("dom.archivereader.enabled", archiveReaderEnabled);
|
||||
}
|
||||
|
@ -64,8 +64,8 @@
|
||||
ok(false, "Should have thrown for bogus encoding label.");
|
||||
} catch (e) {
|
||||
ok(e instanceof RangeError, "Expected a RangeError");
|
||||
finishTest();
|
||||
}
|
||||
finishTest();
|
||||
}
|
||||
|
||||
function testSteps()
|
||||
|
@ -2326,7 +2326,7 @@ nsDOMWindowUtils::GetAsyncPanZoomEnabled(bool *aResult)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::SetAsyncScrollOffset(nsIDOMNode* aNode,
|
||||
int32_t aX, int32_t aY)
|
||||
float aX, float aY)
|
||||
{
|
||||
nsCOMPtr<Element> element = do_QueryInterface(aNode);
|
||||
if (!element) {
|
||||
|
@ -174,6 +174,7 @@
|
||||
|
||||
// FOR CSP (autogenerated by xpidl)
|
||||
#include "nsIContentSecurityPolicy.h"
|
||||
#include "mozilla/dom/nsCSPContext.h"
|
||||
#include "mozilla/dom/nsCSPService.h"
|
||||
#include "nsHTMLStyleSheet.h"
|
||||
#include "nsHTMLCSSStyleSheet.h"
|
||||
@ -2585,24 +2586,6 @@ nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
CSPErrorQueue::Add(const char* aMessageName)
|
||||
{
|
||||
mErrors.AppendElement(aMessageName);
|
||||
}
|
||||
|
||||
void
|
||||
CSPErrorQueue::Flush(nsIDocument* aDocument)
|
||||
{
|
||||
for (uint32_t i = 0; i < mErrors.Length(); i++) {
|
||||
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
|
||||
NS_LITERAL_CSTRING("CSP"), aDocument,
|
||||
nsContentUtils::eSECURITY_PROPERTIES,
|
||||
mErrors[i]);
|
||||
}
|
||||
mErrors.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages)
|
||||
{
|
||||
@ -2781,7 +2764,7 @@ nsDocument::InitCSP(nsIChannel* aChannel)
|
||||
aChannel->GetURI(getter_AddRefs(selfURI));
|
||||
|
||||
// Store the request context for violation reports
|
||||
csp->SetRequestContext(nullptr, nullptr, aChannel);
|
||||
csp->SetRequestContext(this, nullptr);
|
||||
|
||||
// ----- if the doc is an app and we want a default CSP, apply it.
|
||||
if (applyAppDefaultCSP) {
|
||||
@ -4600,7 +4583,12 @@ nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject)
|
||||
// Now that we know what our window is, we can flush the CSP errors to the
|
||||
// Web Console. We are flushing all messages that occured and were stored
|
||||
// in the queue prior to this point.
|
||||
FlushCSPWebConsoleErrorQueue();
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
NodePrincipal()->GetCsp(getter_AddRefs(csp));
|
||||
if (csp) {
|
||||
static_cast<nsCSPContext*>(csp.get())->flushConsoleMessages();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIHttpChannelInternal> internalChannel =
|
||||
do_QueryInterface(GetChannel());
|
||||
if (internalChannel) {
|
||||
|
@ -661,29 +661,6 @@ protected:
|
||||
bool mHaveShutDown;
|
||||
};
|
||||
|
||||
class CSPErrorQueue
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Note this was designed to be passed string literals. If you give it
|
||||
* a dynamically allocated string, it is your responsibility to make sure
|
||||
* it never dies and is properly freed!
|
||||
*/
|
||||
void Add(const char* aMessageName);
|
||||
void Flush(nsIDocument* aDocument);
|
||||
|
||||
CSPErrorQueue()
|
||||
{
|
||||
}
|
||||
|
||||
~CSPErrorQueue()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
nsAutoTArray<const char*,5> mErrors;
|
||||
};
|
||||
|
||||
// Base class for our document implementations.
|
||||
//
|
||||
// Note that this class *implements* nsIDOMXMLDocument, but it's not
|
||||
@ -1740,11 +1717,6 @@ private:
|
||||
bool IsLoopDocument(nsIChannel* aChannel);
|
||||
nsresult InitCSP(nsIChannel* aChannel);
|
||||
|
||||
void FlushCSPWebConsoleErrorQueue()
|
||||
{
|
||||
mCSPWebConsoleErrorQueue.Flush(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the (non-anonymous) content in this document for aFrame. It will
|
||||
* be aFrame's content node if that content is in this document and not
|
||||
@ -1868,8 +1840,6 @@ private:
|
||||
nsrefcnt mStackRefCnt;
|
||||
bool mNeedsReleaseAfterStackRefCntRelease;
|
||||
|
||||
CSPErrorQueue mCSPWebConsoleErrorQueue;
|
||||
|
||||
nsCOMPtr<nsIDocument> mMasterDocument;
|
||||
RefPtr<mozilla::dom::ImportManager> mImportManager;
|
||||
nsTArray<nsCOMPtr<nsINode> > mSubImportLinks;
|
||||
|
@ -7998,6 +7998,17 @@ nsGlobalWindow::CanClose()
|
||||
{
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
|
||||
if (mIsChrome) {
|
||||
nsCOMPtr<nsIBrowserDOMWindow> bwin;
|
||||
nsIDOMChromeWindow* chromeWin = static_cast<nsGlobalChromeWindow*>(this);
|
||||
chromeWin->GetBrowserDOMWindow(getter_AddRefs(bwin));
|
||||
|
||||
bool canClose = true;
|
||||
if (bwin && NS_SUCCEEDED(bwin->CanClose(&canClose))) {
|
||||
return canClose;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mDocShell) {
|
||||
return true;
|
||||
}
|
||||
@ -8011,7 +8022,7 @@ nsGlobalWindow::CanClose()
|
||||
mDocShell->GetContentViewer(getter_AddRefs(cv));
|
||||
if (cv) {
|
||||
bool canClose;
|
||||
nsresult rv = cv->PermitUnload(false, &canClose);
|
||||
nsresult rv = cv->PermitUnload(&canClose);
|
||||
if (NS_SUCCEEDED(rv) && !canClose)
|
||||
return false;
|
||||
|
||||
|
@ -304,79 +304,4 @@ BrowserElementParent::OpenWindowInProcess(nsIDOMWindow* aOpenerWindow,
|
||||
return !!*aReturnWindow ? opened : BrowserElementParent::OPEN_WINDOW_CANCELLED;
|
||||
}
|
||||
|
||||
class DispatchAsyncScrollEventRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
DispatchAsyncScrollEventRunnable(TabParent* aTabParent,
|
||||
const CSSRect& aContentRect,
|
||||
const CSSSize& aContentSize)
|
||||
: mTabParent(aTabParent)
|
||||
, mContentRect(aContentRect)
|
||||
, mContentSize(aContentSize)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run();
|
||||
|
||||
private:
|
||||
RefPtr<TabParent> mTabParent;
|
||||
const CSSRect mContentRect;
|
||||
const CSSSize mContentSize;
|
||||
};
|
||||
|
||||
NS_IMETHODIMP DispatchAsyncScrollEventRunnable::Run()
|
||||
{
|
||||
nsCOMPtr<Element> frameElement = mTabParent->GetOwnerElement();
|
||||
NS_ENSURE_STATE(frameElement);
|
||||
nsIDocument *doc = frameElement->OwnerDoc();
|
||||
nsCOMPtr<nsIGlobalObject> globalObject = doc->GetScopeObject();
|
||||
NS_ENSURE_TRUE(globalObject, NS_ERROR_UNEXPECTED);
|
||||
|
||||
// Create the event's detail object.
|
||||
AsyncScrollEventDetail detail;
|
||||
detail.mLeft = mContentRect.x;
|
||||
detail.mTop = mContentRect.y;
|
||||
detail.mWidth = mContentRect.width;
|
||||
detail.mHeight = mContentRect.height;
|
||||
detail.mScrollWidth = mContentRect.width;
|
||||
detail.mScrollHeight = mContentRect.height;
|
||||
|
||||
AutoSafeJSContext cx;
|
||||
JS::Rooted<JSObject*> globalJSObject(cx, globalObject->GetGlobalJSObject());
|
||||
NS_ENSURE_TRUE(globalJSObject, NS_ERROR_UNEXPECTED);
|
||||
|
||||
JSAutoCompartment ac(cx, globalJSObject);
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
|
||||
if (!ToJSValue(cx, detail, &val)) {
|
||||
MOZ_CRASH("Failed to convert dictionary to JS::Value due to OOM.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
DispatchCustomDOMEvent(frameElement,
|
||||
NS_LITERAL_STRING("mozbrowserasyncscroll"),
|
||||
cx,
|
||||
val, &status);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
BrowserElementParent::DispatchAsyncScrollEvent(TabParent* aTabParent,
|
||||
const CSSRect& aContentRect,
|
||||
const CSSSize& aContentSize)
|
||||
{
|
||||
// Do not dispatch a mozbrowserasyncscroll event of a widget to its embedder
|
||||
nsCOMPtr<Element> frameElement = aTabParent->GetOwnerElement();
|
||||
NS_ENSURE_TRUE(frameElement, false);
|
||||
nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(frameElement);
|
||||
if (browserFrame && browserFrame->GetReallyIsWidget()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<DispatchAsyncScrollEventRunnable> runnable =
|
||||
new DispatchAsyncScrollEventRunnable(aTabParent, aContentRect,
|
||||
aContentSize);
|
||||
return NS_SUCCEEDED(NS_DispatchToMainThread(runnable));
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -109,26 +109,6 @@ public:
|
||||
const nsACString& aFeatures,
|
||||
nsIDOMWindow** aReturnWindow);
|
||||
|
||||
/**
|
||||
* Fire a mozbrowserasyncscroll CustomEvent on the given TabParent's frame element.
|
||||
* This event's detail is an AsyncScrollEventDetail dictionary.
|
||||
*
|
||||
* @param aContentRect: The portion of the page which is currently visible
|
||||
* onscreen in CSS pixels.
|
||||
*
|
||||
* @param aContentSize: The content width/height in CSS pixels.
|
||||
*
|
||||
* aContentRect.top + aContentRect.height may be larger than aContentSize.height.
|
||||
* This indicates that the content is over-scrolled, which occurs when the
|
||||
* page "rubber-bands" after being scrolled all the way to the bottom.
|
||||
* Similarly, aContentRect.left + aContentRect.width may be greater than
|
||||
* contentSize.width, and both left and top may be negative.
|
||||
*/
|
||||
static bool
|
||||
DispatchAsyncScrollEvent(dom::TabParent* aTabParent,
|
||||
const CSSRect& aContentRect,
|
||||
const CSSSize& aContentSize);
|
||||
|
||||
private:
|
||||
static OpenWindowResult
|
||||
DispatchOpenWindowEvent(dom::Element* aOpenerFrameElement,
|
||||
|
@ -9,6 +9,27 @@ SimpleTest.waitForExplicitFinish();
|
||||
browserElementTestHelpers.setEnabledPref(true);
|
||||
browserElementTestHelpers.addPermission();
|
||||
|
||||
// We'll need to get the appId from the current document,
|
||||
// it's either SpecialPowers.Ci.nsIScriptSecurityManager.NO_APP_ID when
|
||||
// we are not running inside an app (e.g. Firefox Desktop),
|
||||
// or the appId of Mochitest app when we are running inside that app
|
||||
// (e.g. Emulator).
|
||||
var currentAppId = SpecialPowers.wrap(document).nodePrincipal.appId;
|
||||
var inApp =
|
||||
currentAppId !== SpecialPowers.Ci.nsIScriptSecurityManager.NO_APP_ID;
|
||||
// We will also need the manifest URL and set it on iframes.
|
||||
var currentAppManifestURL;
|
||||
|
||||
if (inApp) {
|
||||
let appsService = SpecialPowers.Cc["@mozilla.org/AppsService;1"]
|
||||
.getService(SpecialPowers.Ci.nsIAppsService);
|
||||
|
||||
currentAppManifestURL = appsService.getManifestURLByLocalId(currentAppId);
|
||||
};
|
||||
|
||||
info('appId=' + currentAppId);
|
||||
info('manifestURL=' + currentAppManifestURL);
|
||||
|
||||
function setup() {
|
||||
let appInfo = SpecialPowers.Cc['@mozilla.org/xre/app-info;1']
|
||||
.getService(SpecialPowers.Ci.nsIXULAppInfo);
|
||||
@ -27,110 +48,134 @@ function tearDown() {
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
let path = location.pathname;
|
||||
let imeUrl = location.protocol + '//' + location.host +
|
||||
path.substring(0, path.lastIndexOf('/')) +
|
||||
'/file_inputmethod.html';
|
||||
SpecialPowers.pushPermissions([{
|
||||
type: 'input',
|
||||
allow: true,
|
||||
context: {
|
||||
url: imeUrl,
|
||||
originAttributes: {inBrowser: true}
|
||||
}
|
||||
}], SimpleTest.waitForFocus.bind(SimpleTest, createFrames));
|
||||
createFrames();
|
||||
}
|
||||
|
||||
var gFrames = [];
|
||||
var gInputMethodFrames = [];
|
||||
var gInputFrame;
|
||||
|
||||
function createFrames() {
|
||||
// Create two input method iframes.
|
||||
let loadendCount = 0;
|
||||
let countLoadend = function() {
|
||||
if (this === gInputFrame) {
|
||||
// The frame script running in the frame where the input is hosted.
|
||||
let appFrameScript = function appFrameScript() {
|
||||
let input = content.document.body.firstElementChild;
|
||||
input.oninput = function() {
|
||||
sendAsyncMessage('test:InputMethod:oninput', {
|
||||
from: 'input',
|
||||
value: this.value
|
||||
});
|
||||
};
|
||||
|
||||
input.onblur = function() {
|
||||
// "Expected" lost of focus since the test is finished.
|
||||
if (input.value === '#0#1hello') {
|
||||
return;
|
||||
}
|
||||
|
||||
sendAsyncMessage('test:InputMethod:oninput', {
|
||||
from: 'input',
|
||||
error: true,
|
||||
value: 'Unexpected lost of focus on the input frame!'
|
||||
});
|
||||
};
|
||||
|
||||
input.focus();
|
||||
}
|
||||
|
||||
// Inject frame script to receive input.
|
||||
let mm = SpecialPowers.getBrowserFrameMessageManager(gInputFrame);
|
||||
mm.loadFrameScript('data:,(' + encodeURIComponent(appFrameScript.toString()) + ')();', false);
|
||||
mm.addMessageListener("test:InputMethod:oninput", next);
|
||||
} else {
|
||||
ok(this.setInputMethodActive, 'Can access setInputMethodActive.');
|
||||
|
||||
// The frame script running in the input method frames.
|
||||
|
||||
let appFrameScript = function appFrameScript() {
|
||||
content.addEventListener("message", function(evt) {
|
||||
sendAsyncMessage('test:InputMethod:imFrameMessage', {
|
||||
from: 'im',
|
||||
value: evt.data
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Inject frame script to receive message.
|
||||
let mm = SpecialPowers.getBrowserFrameMessageManager(this);
|
||||
mm.loadFrameScript('data:,(' + appFrameScript.toString() + ')();', false);
|
||||
mm.addMessageListener("test:InputMethod:imFrameMessage", next);
|
||||
}
|
||||
|
||||
loadendCount++;
|
||||
if (loadendCount === 3) {
|
||||
startTest();
|
||||
setPermissions();
|
||||
}
|
||||
};
|
||||
|
||||
// Create an input field to receive string from input method iframes.
|
||||
gInputFrame = document.createElement('iframe');
|
||||
gInputFrame.setAttribute('mozbrowser', 'true');
|
||||
gInputFrame.src =
|
||||
'data:text/html,<input autofocus value="hello" />' +
|
||||
'<p>This is targetted mozbrowser frame.</p>';
|
||||
document.body.appendChild(gInputFrame);
|
||||
gInputFrame.addEventListener('mozbrowserloadend', countLoadend);
|
||||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
let frame = gInputMethodFrames[i] = document.createElement('iframe');
|
||||
frame.setAttribute('mozbrowser', 'true');
|
||||
if (currentAppManifestURL) {
|
||||
frame.setAttribute('mozapp', currentAppManifestURL);
|
||||
}
|
||||
};
|
||||
frame.src = 'file_empty.html#' + i;
|
||||
document.body.appendChild(frame);
|
||||
frame.addEventListener('mozbrowserloadend', countLoadend);
|
||||
}
|
||||
}
|
||||
|
||||
// Create an input field to receive string from input method iframes.
|
||||
gInputFrame = document.createElement('iframe');
|
||||
gInputFrame.setAttribute('mozbrowser', 'true');
|
||||
gInputFrame.src =
|
||||
'data:text/html,<input autofocus value="hello" />' +
|
||||
'<p>This is targetted mozbrowser frame.</p>';
|
||||
document.body.appendChild(gInputFrame);
|
||||
gInputFrame.addEventListener('mozbrowserloadend', countLoadend);
|
||||
function setPermissions() {
|
||||
let permissions = [{
|
||||
type: 'input',
|
||||
allow: true,
|
||||
context: {
|
||||
url: SimpleTest.getTestFileURL('/file_empty.html'),
|
||||
originAttributes: {
|
||||
appId: currentAppId,
|
||||
inBrowser: true
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
let frame = gFrames[i] = document.createElement('iframe');
|
||||
gFrames[i].setAttribute('mozbrowser', 'true');
|
||||
// When the input method iframe is activated, it will send the URL
|
||||
// hash to current focused element. We set different hash to each
|
||||
// iframe so that iframes can be differentiated by their hash.
|
||||
frame.src = 'file_inputmethod.html#' + i;
|
||||
document.body.appendChild(frame);
|
||||
frame.addEventListener('mozbrowserloadend', countLoadend);
|
||||
if (inApp) {
|
||||
// The current document would also need to be given access for IPC to
|
||||
// recognize our permission (why)?
|
||||
permissions.push({
|
||||
type: 'input', allow: true, context: document });
|
||||
}
|
||||
|
||||
SpecialPowers.pushPermissions(permissions,
|
||||
SimpleTest.waitForFocus.bind(SimpleTest, startTest));
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
// Set focus to the input field and wait for input methods' inputting.
|
||||
SpecialPowers.DOMWindowUtils.focus(gInputFrame);
|
||||
function startTest() {
|
||||
// The frame script running in the frame where the input is hosted.
|
||||
let appFrameScript = function appFrameScript() {
|
||||
let input = content.document.body.firstElementChild;
|
||||
input.oninput = function() {
|
||||
sendAsyncMessage('test:InputMethod:oninput', {
|
||||
from: 'input',
|
||||
value: this.value
|
||||
});
|
||||
};
|
||||
|
||||
let req0 = gFrames[0].setInputMethodActive(true);
|
||||
input.onblur = function() {
|
||||
// "Expected" lost of focus since the test is finished.
|
||||
if (input.value === '#0#1hello') {
|
||||
return;
|
||||
}
|
||||
|
||||
sendAsyncMessage('test:InputMethod:oninput', {
|
||||
from: 'input',
|
||||
error: true,
|
||||
value: 'Unexpected lost of focus on the input frame!'
|
||||
});
|
||||
};
|
||||
|
||||
input.focus();
|
||||
};
|
||||
|
||||
// Inject frame script to receive input.
|
||||
let mm = SpecialPowers.getBrowserFrameMessageManager(gInputFrame);
|
||||
mm.loadFrameScript('data:,(' + encodeURIComponent(appFrameScript.toString()) + ')();', false);
|
||||
mm.addMessageListener("test:InputMethod:oninput", next);
|
||||
|
||||
gInputMethodFrames.forEach((frame) => {
|
||||
ok(frame.setInputMethodActive, 'Can access setInputMethodActive.');
|
||||
|
||||
// The frame script running in the input method frames.
|
||||
let appFrameScript = function appFrameScript() {
|
||||
let im = content.navigator.mozInputMethod;
|
||||
im.oninputcontextchange = function() {
|
||||
let ctx = im.inputcontext;
|
||||
// Report back to parent frame on status of ctx gotten.
|
||||
// (A setTimeout() here to ensure this always happens after
|
||||
// DOMRequest succeed.)
|
||||
content.setTimeout(() => {
|
||||
sendAsyncMessage('test:InputMethod:imFrameMessage', {
|
||||
from: 'im',
|
||||
value: content.location.hash + !!ctx
|
||||
});
|
||||
});
|
||||
|
||||
// If there is a context, send out the hash.
|
||||
if (ctx) {
|
||||
ctx.replaceSurroundingText(content.location.hash);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Inject frame script to receive message.
|
||||
let mm = SpecialPowers.getBrowserFrameMessageManager(frame);
|
||||
mm.loadFrameScript('data:,(' + encodeURIComponent(appFrameScript.toString()) + ')();', false);
|
||||
mm.addMessageListener("test:InputMethod:imFrameMessage", next);
|
||||
});
|
||||
|
||||
// Set focus to the input field and wait for input methods' inputting.
|
||||
SpecialPowers.DOMWindowUtils.focus(gInputFrame);
|
||||
|
||||
let req0 = gInputMethodFrames[0].setInputMethodActive(true);
|
||||
req0.onsuccess = function() {
|
||||
ok(true, 'setInputMethodActive succeeded (0).');
|
||||
};
|
||||
@ -203,14 +248,14 @@ function next(msg) {
|
||||
|
||||
gCount++;
|
||||
|
||||
let req0 = gFrames[0].setInputMethodActive(false);
|
||||
let req0 = gInputMethodFrames[0].setInputMethodActive(false);
|
||||
req0.onsuccess = function() {
|
||||
ok(true, 'setInputMethodActive succeeded (0).');
|
||||
};
|
||||
req0.onerror = function() {
|
||||
ok(false, 'setInputMethodActive failed (0): ' + this.error.name);
|
||||
};
|
||||
let req1 = gFrames[1].setInputMethodActive(true);
|
||||
let req1 = gInputMethodFrames[1].setInputMethodActive(true);
|
||||
req1.onsuccess = function() {
|
||||
ok(true, 'setInputMethodActive succeeded (1).');
|
||||
};
|
||||
@ -259,7 +304,7 @@ function next(msg) {
|
||||
|
||||
// Receive the second input from the second iframe.
|
||||
// Deactive the second iframe.
|
||||
let req3 = gFrames[1].setInputMethodActive(false);
|
||||
let req3 = gInputMethodFrames[1].setInputMethodActive(false);
|
||||
req3.onsuccess = function() {
|
||||
ok(true, 'setInputMethodActive(false) succeeded (2).');
|
||||
};
|
||||
|
@ -1,20 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
var im = navigator.mozInputMethod;
|
||||
if (im) {
|
||||
im.oninputcontextchange = function() {
|
||||
var ctx = im.inputcontext;
|
||||
// Report back to parent frame on status of ctx gotten.
|
||||
window.postMessage(window.location.hash + !!ctx, '*');
|
||||
// If there is a context, send out the hash.
|
||||
if (ctx) {
|
||||
ctx.replaceSurroundingText(location.hash);
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<p>This frame represents the input method frame.</p>
|
||||
</body>
|
||||
</html>
|
@ -76,8 +76,10 @@ skip-if = (toolkit == 'gonk') # Disabled on emulator. See bug 1144015 comment 8
|
||||
[test_browserElement_oop_PrivateBrowsing.html]
|
||||
[test_browserElement_oop_PromptCheck.html]
|
||||
[test_browserElement_oop_PromptConfirm.html]
|
||||
# Disabled on B2G Emulator because permission cannot be asserted in content process,
|
||||
# need to fix either bug 1094055 or bug 1020135.
|
||||
[test_browserElement_oop_Proxy.html]
|
||||
skip-if = (toolkit == 'gonk') # Disabled on B2G Emulator bug 1198163
|
||||
skip-if = (toolkit == 'gonk')
|
||||
[test_browserElement_oop_PurgeHistory.html]
|
||||
[test_browserElement_oop_Reload.html]
|
||||
[test_browserElement_oop_ReloadPostRequest.html]
|
||||
@ -89,7 +91,7 @@ skip-if = toolkit == 'android' || (toolkit == 'gonk' && !debug) #TIMED_OUT, bug
|
||||
skip-if = (toolkit == 'gonk') # Disabled on b2g due to bug 1097419
|
||||
[test_browserElement_oop_SendEvent.html]
|
||||
[test_browserElement_oop_SetInputMethodActive.html]
|
||||
skip-if = (os == "android") || (toolkit == 'gonk') # Disabled on B2G Emulator bug 1198163
|
||||
skip-if = (os == "android")
|
||||
[test_browserElement_oop_SetVisible.html]
|
||||
[test_browserElement_oop_SetVisibleFrames.html]
|
||||
[test_browserElement_oop_SetVisibleFrames2.html]
|
||||
|
@ -129,12 +129,10 @@ support-files =
|
||||
file_focus.html
|
||||
file_http_401_response.sjs
|
||||
file_http_407_response.sjs
|
||||
file_inputmethod.html
|
||||
file_microdata.html
|
||||
file_microdata_bad_itemref.html
|
||||
file_microdata_itemref.html
|
||||
file_microformats.html
|
||||
file_inputmethod.html
|
||||
file_post_request.html
|
||||
file_wyciwyg.html
|
||||
file_audio.html
|
||||
@ -213,8 +211,10 @@ skip-if = (toolkit == 'gonk' && !debug)
|
||||
[test_browserElement_inproc_PrivateBrowsing.html]
|
||||
[test_browserElement_inproc_PromptCheck.html]
|
||||
[test_browserElement_inproc_PromptConfirm.html]
|
||||
# Disabled on B2G Emulator because permission cannot be asserted in content process,
|
||||
# need to fix either bug 1094055 or bug 1020135.
|
||||
[test_browserElement_inproc_Proxy.html]
|
||||
skip-if = (toolkit == 'gonk') # Disabled on B2G Emulator bug 1198163
|
||||
skip-if = (toolkit == 'gonk')
|
||||
[test_browserElement_inproc_PurgeHistory.html]
|
||||
[test_browserElement_inproc_ReloadPostRequest.html]
|
||||
[test_browserElement_inproc_RemoveBrowserElement.html]
|
||||
@ -226,7 +226,7 @@ skip-if = (toolkit == 'gonk') # Disabled on b2g due to bug 1097419
|
||||
[test_browserElement_inproc_SendEvent.html]
|
||||
# The setInputMethodActive() tests will timed out on Android
|
||||
[test_browserElement_inproc_SetInputMethodActive.html]
|
||||
skip-if = (os == "android") || (toolkit == 'gonk') # Disabled on B2G Emulator bug 1198163
|
||||
skip-if = (os == "android")
|
||||
[test_browserElement_inproc_SetVisible.html]
|
||||
[test_browserElement_inproc_SetVisibleFrames.html]
|
||||
[test_browserElement_inproc_SetVisibleFrames2.html]
|
||||
|
@ -4847,7 +4847,8 @@ CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& window, double x,
|
||||
// Rendering directly is faster and can be done if mTarget supports Azure
|
||||
// and does not need alpha blending.
|
||||
if (gfxPlatform::GetPlatform()->SupportsAzureContentForDrawTarget(mTarget) &&
|
||||
GlobalAlpha() == 1.0f)
|
||||
GlobalAlpha() == 1.0f &&
|
||||
UsedOperation() == CompositionOp::OP_OVER)
|
||||
{
|
||||
thebes = new gfxContext(mTarget);
|
||||
thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21,
|
||||
@ -4892,7 +4893,7 @@ CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& window, double x,
|
||||
gfx::Rect sourceRect(0, 0, sw, sh);
|
||||
mTarget->DrawSurface(source, destRect, sourceRect,
|
||||
DrawSurfaceOptions(gfx::Filter::POINT),
|
||||
DrawOptions(GlobalAlpha(), CompositionOp::OP_OVER,
|
||||
DrawOptions(GlobalAlpha(), UsedOperation(),
|
||||
AntialiasMode::NONE));
|
||||
mTarget->Flush();
|
||||
} else {
|
||||
|
@ -214,7 +214,7 @@ ContentEventHandler::QueryContentRect(nsIContent* aContent,
|
||||
resultRect.UnionRect(resultRect, frameRect);
|
||||
}
|
||||
|
||||
aEvent->mReply.mRect = LayoutDevicePixel::FromUntyped(
|
||||
aEvent->mReply.mRect = LayoutDeviceIntRect::FromUnknownRect(
|
||||
resultRect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel()));
|
||||
aEvent->mSucceeded = true;
|
||||
|
||||
@ -1081,7 +1081,7 @@ ContentEventHandler::OnQueryTextRect(WidgetQueryContentEvent* aEvent)
|
||||
} else {
|
||||
rect.UnionRect(rect, frameRect);
|
||||
}
|
||||
aEvent->mReply.mRect = LayoutDevicePixel::FromUntyped(
|
||||
aEvent->mReply.mRect = LayoutDeviceIntRect::FromUnknownRect(
|
||||
rect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel()));
|
||||
aEvent->mReply.mWritingMode = lastFrame->GetWritingMode();
|
||||
aEvent->mSucceeded = true;
|
||||
@ -1129,7 +1129,7 @@ ContentEventHandler::OnQueryCaretRect(WidgetQueryContentEvent* aEvent)
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nscoord appUnitsPerDevPixel =
|
||||
caretFrame->PresContext()->AppUnitsPerDevPixel();
|
||||
aEvent->mReply.mRect = LayoutDevicePixel::FromUntyped(
|
||||
aEvent->mReply.mRect = LayoutDeviceIntRect::FromUnknownRect(
|
||||
caretRect.ToOutsidePixels(appUnitsPerDevPixel));
|
||||
aEvent->mReply.mWritingMode = caretFrame->GetWritingMode();
|
||||
aEvent->mReply.mOffset = aEvent->mInput.mOffset;
|
||||
@ -1189,7 +1189,7 @@ ContentEventHandler::OnQueryCaretRect(WidgetQueryContentEvent* aEvent)
|
||||
rv = ConvertToRootRelativeOffset(frame, rect);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
aEvent->mReply.mRect = LayoutDevicePixel::FromUntyped(
|
||||
aEvent->mReply.mRect = LayoutDeviceIntRect::FromUnknownRect(
|
||||
rect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel()));
|
||||
// If the caret rect is empty, let's make it non-empty rect.
|
||||
if (!aEvent->mReply.mRect.width) {
|
||||
|
@ -807,9 +807,9 @@ IMEContentObserver::OnMouseButtonEvent(nsPresContext* aPresContext,
|
||||
notification.mMouseButtonEventData.mEventMessage = aMouseEvent->mMessage;
|
||||
notification.mMouseButtonEventData.mOffset = charAtPt.mReply.mOffset;
|
||||
notification.mMouseButtonEventData.mCursorPos.Set(
|
||||
LayoutDeviceIntPoint::ToUntyped(charAtPt.refPoint));
|
||||
charAtPt.refPoint.ToUnknownPoint());
|
||||
notification.mMouseButtonEventData.mCharRect.Set(
|
||||
LayoutDevicePixel::ToUntyped(charAtPt.mReply.mRect));
|
||||
charAtPt.mReply.mRect.ToUnknownRect());
|
||||
notification.mMouseButtonEventData.mButton = aMouseEvent->button;
|
||||
notification.mMouseButtonEventData.mButtons = aMouseEvent->buttons;
|
||||
notification.mMouseButtonEventData.mModifiers = aMouseEvent->modifiers;
|
||||
|
@ -338,8 +338,8 @@ WheelTransaction::GetScreenPoint(WidgetGUIEvent* aEvent)
|
||||
{
|
||||
NS_ASSERTION(aEvent, "aEvent is null");
|
||||
NS_ASSERTION(aEvent->widget, "aEvent-widget is null");
|
||||
return LayoutDeviceIntPoint::ToUntyped(aEvent->refPoint +
|
||||
aEvent->widget->WidgetToScreenOffset());
|
||||
return (aEvent->refPoint + aEvent->widget->WidgetToScreenOffset())
|
||||
.ToUnknownPoint();
|
||||
}
|
||||
|
||||
/* static */ uint32_t
|
||||
|
@ -1522,7 +1522,7 @@ nsHTMLDocument::Open(JSContext* cx,
|
||||
|
||||
if (cv) {
|
||||
bool okToUnload;
|
||||
if (NS_SUCCEEDED(cv->PermitUnload(false, &okToUnload)) && !okToUnload) {
|
||||
if (NS_SUCCEEDED(cv->PermitUnload(&okToUnload)) && !okToUnload) {
|
||||
// We don't want to unload, so stop here, but don't throw an
|
||||
// exception.
|
||||
nsCOMPtr<nsIDocument> ret = this;
|
||||
|
@ -199,7 +199,7 @@ this.Keyboard = {
|
||||
}
|
||||
if (!Utils.checkPermissionForMM(mm, permName)) {
|
||||
dump("Keyboard.jsm: Message " + msg.name +
|
||||
" from a content process with no '" + permName + "' privileges.");
|
||||
" from a content process with no '" + permName + "' privileges.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ interface nsIOpenURIInFrameParams : nsISupports
|
||||
attribute boolean isPrivate;
|
||||
};
|
||||
|
||||
[scriptable, uuid(99f5a347-722c-4337-bd38-f14ec94801b3)]
|
||||
[scriptable, uuid(31da1ce2-aec4-4c26-ac66-d622935c3bf4)]
|
||||
|
||||
/**
|
||||
* The C++ source has access to the browser script source through
|
||||
@ -99,6 +99,14 @@ interface nsIBrowserDOMWindow : nsISupports
|
||||
* @return whether the window is the main content window for any
|
||||
* currently open tab in this toplevel browser window.
|
||||
*/
|
||||
boolean isTabContentWindow(in nsIDOMWindow aWindow);
|
||||
boolean isTabContentWindow(in nsIDOMWindow aWindow);
|
||||
|
||||
/**
|
||||
* This function is responsible for calling
|
||||
* nsIContentViewer::PermitUnload on each frame in the window. It
|
||||
* returns true if closing the window is allowed. See canClose() in
|
||||
* BrowserUtils.jsm for a simple implementation of this method.
|
||||
*/
|
||||
boolean canClose();
|
||||
};
|
||||
|
||||
|
@ -49,7 +49,7 @@ interface nsIJSRAIIHelper;
|
||||
interface nsIContentPermissionRequest;
|
||||
interface nsIObserver;
|
||||
|
||||
[scriptable, uuid(a30a95ac-3b95-4251-88dc-8efa89ba9f9c)]
|
||||
[scriptable, uuid(3f3f2bf4-d411-44b2-b2f7-dee5948c4763)]
|
||||
interface nsIDOMWindowUtils : nsISupports {
|
||||
|
||||
/**
|
||||
@ -1431,7 +1431,7 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
* with that offset if async scrolling is enabled, and then the offset
|
||||
* will be removed. Only call this while test-controlled refreshes is enabled.
|
||||
*/
|
||||
void setAsyncScrollOffset(in nsIDOMNode aNode, in int32_t aX, in int32_t aY);
|
||||
void setAsyncScrollOffset(in nsIDOMNode aNode, in float aX, in float aY);
|
||||
|
||||
/**
|
||||
* Set async zoom value. aRootElement should be the document element of our
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
#include "domstubs.idl"
|
||||
|
||||
[scriptable, uuid(3dd203e4-66ec-40fd-acde-43f0b35c98e9)]
|
||||
[scriptable, uuid(7615408c-1fb3-4128-8dd5-a3e2f3fa8842)]
|
||||
interface nsITabParent : nsISupports
|
||||
{
|
||||
void injectTouchEvent(in AString aType,
|
||||
@ -28,6 +28,17 @@ interface nsITabParent : nsISupports
|
||||
*/
|
||||
attribute boolean docShellIsActive;
|
||||
|
||||
/**
|
||||
* As an optimisation, setting the docshell's active state to
|
||||
* inactive also triggers a layer invalidation to free up some
|
||||
* potentially unhelpful memory usage. This attribute should be
|
||||
* used where callers would like to set the docshell's state
|
||||
* without losing any layer data.
|
||||
*
|
||||
* Otherwise, this does the same as setting the attribute above.
|
||||
*/
|
||||
void setDocShellIsActiveAndForeground(in boolean aIsActive);
|
||||
|
||||
/**
|
||||
* During interactions where painting performance
|
||||
* is more important than scrolling, we may temporarily
|
||||
|
@ -8,6 +8,7 @@
|
||||
interface nsIURI;
|
||||
interface nsIChannel;
|
||||
interface nsIDocShell;
|
||||
interface nsIDOMDocument;
|
||||
interface nsIPrincipal;
|
||||
interface nsIURI;
|
||||
|
||||
@ -20,7 +21,7 @@ interface nsIURI;
|
||||
|
||||
typedef unsigned short CSPDirective;
|
||||
|
||||
[scriptable, uuid(36c6d419-24c2-40e8-9adb-11d0b1341770)]
|
||||
[scriptable, builtinclass, uuid(b9a029c3-9484-4bf7-826d-0c6b545790bc)]
|
||||
interface nsIContentSecurityPolicy : nsISerializable
|
||||
{
|
||||
/**
|
||||
@ -86,12 +87,6 @@ interface nsIContentSecurityPolicy : nsISerializable
|
||||
*/
|
||||
bool getReferrerPolicy(out unsigned long policy);
|
||||
|
||||
/**
|
||||
* Remove a policy associated with this CSP context.
|
||||
* @throws NS_ERROR_FAILURE if the index is out of bounds or invalid.
|
||||
*/
|
||||
void removePolicy(in unsigned long index);
|
||||
|
||||
/**
|
||||
* Parse and install a CSP policy.
|
||||
* @param aPolicy
|
||||
@ -173,16 +168,12 @@ interface nsIContentSecurityPolicy : nsISerializable
|
||||
|
||||
/**
|
||||
* Called after the CSP object is created to fill in appropriate request
|
||||
* context and give it a reference to its owning principal for violation
|
||||
* report generation.
|
||||
* This will use whatever data is available, choosing earlier arguments first
|
||||
* if multiple are available. Either way, it will attempt to obtain the URI,
|
||||
* referrer and the principal from whatever is available. If the channel is
|
||||
* available, it'll also store that for processing policy-uri directives.
|
||||
* context. Either use
|
||||
* * aDocument (preferred), or if no document is available, then provide
|
||||
* * aPrincipal
|
||||
*/
|
||||
void setRequestContext(in nsIURI selfURI,
|
||||
in nsIURI referrer,
|
||||
in nsIChannel aChannel);
|
||||
void setRequestContext(in nsIDOMDocument aDocument,
|
||||
in nsIPrincipal aPrincipal);
|
||||
|
||||
/**
|
||||
* Verifies ancestry as permitted by the policy.
|
||||
|
@ -2262,6 +2262,39 @@ ContentChild::RecvPreferenceUpdate(const PrefSetting& aPref)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvDataStoragePut(const nsString& aFilename,
|
||||
const DataStorageItem& aItem)
|
||||
{
|
||||
RefPtr<DataStorage> storage = DataStorage::GetIfExists(aFilename);
|
||||
if (storage) {
|
||||
storage->Put(aItem.key(), aItem.value(), aItem.type());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvDataStorageRemove(const nsString& aFilename,
|
||||
const nsCString& aKey,
|
||||
const DataStorageType& aType)
|
||||
{
|
||||
RefPtr<DataStorage> storage = DataStorage::GetIfExists(aFilename);
|
||||
if (storage) {
|
||||
storage->Remove(aKey, aType);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvDataStorageClear(const nsString& aFilename)
|
||||
{
|
||||
RefPtr<DataStorage> storage = DataStorage::GetIfExists(aFilename);
|
||||
if (storage) {
|
||||
storage->Clear();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvNotifyAlertsObserver(const nsCString& aType, const nsString& aData)
|
||||
{
|
||||
|
@ -338,6 +338,13 @@ public:
|
||||
|
||||
virtual bool RecvPreferenceUpdate(const PrefSetting& aPref) override;
|
||||
|
||||
virtual bool RecvDataStoragePut(const nsString& aFilename,
|
||||
const DataStorageItem& aItem) override;
|
||||
virtual bool RecvDataStorageRemove(const nsString& aFilename,
|
||||
const nsCString& aKey,
|
||||
const DataStorageType& aType) override;
|
||||
virtual bool RecvDataStorageClear(const nsString& aFilename) override;
|
||||
|
||||
virtual bool RecvNotifyAlertsObserver(const nsCString& aType,
|
||||
const nsString& aData) override;
|
||||
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "imgIContainer.h"
|
||||
#include "mozIApplication.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/DataStorage.h"
|
||||
#include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
|
||||
#include "mozilla/docshell/OfflineCacheUpdateParent.h"
|
||||
#include "mozilla/dom/DataStoreService.h"
|
||||
@ -1222,10 +1223,12 @@ ContentParent::RecvGetBlocklistState(const uint32_t& aPluginId,
|
||||
|
||||
bool
|
||||
ContentParent::RecvFindPlugins(const uint32_t& aPluginEpoch,
|
||||
nsresult* aRv,
|
||||
nsTArray<PluginTag>* aPlugins,
|
||||
uint32_t* aNewPluginEpoch)
|
||||
{
|
||||
return mozilla::plugins::FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch);
|
||||
*aRv = mozilla::plugins::FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*static*/ TabParent*
|
||||
@ -2670,6 +2673,15 @@ ContentParent::RecvReadFontList(InfallibleTArray<FontListEntry>* retValue)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvReadDataStorageArray(const nsString& aFilename,
|
||||
InfallibleTArray<DataStorageItem>* aValues)
|
||||
{
|
||||
RefPtr<DataStorage> storage = DataStorage::Get(aFilename);
|
||||
storage->GetAll(aValues);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvReadPermissions(InfallibleTArray<IPC::Permission>* aPermissions)
|
||||
{
|
||||
|
@ -191,6 +191,7 @@ public:
|
||||
virtual bool RecvConnectPluginBridge(const uint32_t& aPluginId, nsresult* aRv) override;
|
||||
virtual bool RecvGetBlocklistState(const uint32_t& aPluginId, uint32_t* aIsBlocklisted) override;
|
||||
virtual bool RecvFindPlugins(const uint32_t& aPluginEpoch,
|
||||
nsresult* aRv,
|
||||
nsTArray<PluginTag>* aPlugins,
|
||||
uint32_t* aNewPluginEpoch) override;
|
||||
|
||||
@ -742,6 +743,9 @@ private:
|
||||
virtual bool RecvReadPrefsArray(InfallibleTArray<PrefSetting>* aPrefs) override;
|
||||
virtual bool RecvReadFontList(InfallibleTArray<FontListEntry>* retValue) override;
|
||||
|
||||
virtual bool RecvReadDataStorageArray(const nsString& aFilename,
|
||||
InfallibleTArray<DataStorageItem>* aValues) override;
|
||||
|
||||
virtual bool RecvReadPermissions(InfallibleTArray<IPC::Permission>* aPermissions) override;
|
||||
|
||||
virtual bool RecvSetClipboard(const IPCDataTransfer& aDataTransfer,
|
||||
|
@ -695,7 +695,7 @@ child:
|
||||
/**
|
||||
* Update the child side docShell active (resource use) state.
|
||||
*/
|
||||
SetDocShellIsActive(bool aIsActive);
|
||||
SetDocShellIsActive(bool aIsActive, bool aIsHidden);
|
||||
|
||||
/**
|
||||
* Notify the child that it shouldn't paint the offscreen displayport.
|
||||
|
@ -91,6 +91,7 @@ using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
|
||||
using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h";
|
||||
using struct LookAndFeelInt from "mozilla/widget/WidgetMessageUtils.h";
|
||||
using class mozilla::dom::ipc::StructuredCloneData from "ipc/IPCMessageUtils.h";
|
||||
using mozilla::DataStorageType from "ipc/DataStorageIPCUtils.h";
|
||||
|
||||
union ChromeRegistryItem
|
||||
{
|
||||
@ -324,6 +325,12 @@ struct PrefSetting {
|
||||
MaybePrefValue userValue;
|
||||
};
|
||||
|
||||
struct DataStorageItem {
|
||||
nsCString key;
|
||||
nsCString value;
|
||||
DataStorageType type;
|
||||
};
|
||||
|
||||
struct DataStoreSetting {
|
||||
nsString name;
|
||||
nsString originURL;
|
||||
@ -559,6 +566,10 @@ child:
|
||||
|
||||
PreferenceUpdate(PrefSetting pref);
|
||||
|
||||
DataStoragePut(nsString aFilename, DataStorageItem aItem);
|
||||
DataStorageRemove(nsString aFilename, nsCString aKey, DataStorageType aType);
|
||||
DataStorageClear(nsString aFilename);
|
||||
|
||||
NotifyAlertsObserver(nsCString topic, nsString data);
|
||||
|
||||
GeolocationUpdate(GeoPosition somewhere);
|
||||
@ -763,7 +774,7 @@ parent:
|
||||
* process. |newPluginEpoch| is the current epoch in the chrome process. If
|
||||
* |pluginEpoch == newPluginEpoch|, then |plugins| will be left empty.
|
||||
*/
|
||||
sync FindPlugins(uint32_t pluginEpoch) returns (PluginTag[] plugins, uint32_t newPluginEpoch);
|
||||
sync FindPlugins(uint32_t pluginEpoch) returns (nsresult aResult, PluginTag[] plugins, uint32_t newPluginEpoch);
|
||||
|
||||
async PJavaScript();
|
||||
|
||||
@ -839,6 +850,9 @@ parent:
|
||||
|
||||
sync ReadFontList() returns (FontListEntry[] retValue);
|
||||
|
||||
sync ReadDataStorageArray(nsString aFilename)
|
||||
returns (DataStorageItem[] retValue);
|
||||
|
||||
sync SyncMessage(nsString aMessage, ClonedMessageData aData,
|
||||
CpowEntry[] aCpows, Principal aPrincipal)
|
||||
returns (StructuredCloneData[] retval);
|
||||
|
@ -2299,11 +2299,15 @@ TabChild::RecvSetUpdateHitRegion(const bool& aEnabled)
|
||||
}
|
||||
|
||||
bool
|
||||
TabChild::RecvSetDocShellIsActive(const bool& aIsActive)
|
||||
TabChild::RecvSetDocShellIsActive(const bool& aIsActive, const bool& aIsHidden)
|
||||
{
|
||||
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
|
||||
if (docShell) {
|
||||
docShell->SetIsActive(aIsActive);
|
||||
if (aIsHidden) {
|
||||
docShell->SetIsActive(aIsActive);
|
||||
} else {
|
||||
docShell->SetIsActiveAndForeground(aIsActive);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -534,7 +534,7 @@ protected:
|
||||
virtual bool DeallocPRenderFrameChild(PRenderFrameChild* aFrame) override;
|
||||
virtual bool RecvDestroy() override;
|
||||
virtual bool RecvSetUpdateHitRegion(const bool& aEnabled) override;
|
||||
virtual bool RecvSetDocShellIsActive(const bool& aIsActive) override;
|
||||
virtual bool RecvSetDocShellIsActive(const bool& aIsActive, const bool& aIsHidden) override;
|
||||
virtual bool RecvNavigateByKey(const bool& aForward, const bool& aForDocumentNavigation) override;
|
||||
|
||||
virtual bool RecvRequestNotifyAfterRemotePaint() override;
|
||||
|
@ -2909,7 +2909,7 @@ NS_IMETHODIMP
|
||||
TabParent::SetDocShellIsActive(bool isActive)
|
||||
{
|
||||
mDocShellIsActive = isActive;
|
||||
Unused << SendSetDocShellIsActive(isActive);
|
||||
Unused << SendSetDocShellIsActive(isActive, true);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -2920,6 +2920,14 @@ TabParent::GetDocShellIsActive(bool* aIsActive)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TabParent::SetDocShellIsActiveAndForeground(bool isActive)
|
||||
{
|
||||
mDocShellIsActive = isActive;
|
||||
Unused << SendSetDocShellIsActive(isActive, false);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TabParent::SuppressDisplayport(bool aEnabled)
|
||||
{
|
||||
|
@ -749,7 +749,7 @@ nsJSChannel::EvaluateScript()
|
||||
if (cv) {
|
||||
bool okToUnload;
|
||||
|
||||
if (NS_SUCCEEDED(cv->PermitUnload(false, &okToUnload)) &&
|
||||
if (NS_SUCCEEDED(cv->PermitUnload(&okToUnload)) &&
|
||||
!okToUnload) {
|
||||
// The user didn't want to unload the current
|
||||
// page, translate this into an undefined
|
||||
|
@ -248,7 +248,7 @@ GMPDecryptorParent::RecvRejectPromise(const uint32_t& aPromiseId,
|
||||
const nsCString& aMessage)
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::RecvRejectPromise(promiseId=%u, exception=%d, msg='%s')",
|
||||
this, aException, aMessage.get()));
|
||||
this, aPromiseId, aException, aMessage.get()));
|
||||
|
||||
if (!mIsOpen) {
|
||||
NS_WARNING("Trying to use a dead GMP decrypter!");
|
||||
|
@ -247,6 +247,10 @@ GMPVideoDecoder::Input(MediaRawData* aSample)
|
||||
mAdapter->SetLastStreamOffset(sample->mOffset);
|
||||
|
||||
GMPUniquePtr<GMPVideoEncodedFrame> frame = CreateFrame(sample);
|
||||
if (!frame) {
|
||||
mCallback->Error();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsTArray<uint8_t> info; // No codec specific per-frame info to pass.
|
||||
nsresult rv = mGMP->Decode(Move(frame), false, info, 0);
|
||||
if (NS_FAILED(rv)) {
|
||||
|
@ -2418,9 +2418,11 @@ nsPluginHost::FindPluginsInContent(bool aCreatePluginList, bool* aPluginsChanged
|
||||
MOZ_ASSERT(XRE_IsContentProcess());
|
||||
|
||||
dom::ContentChild* cp = dom::ContentChild::GetSingleton();
|
||||
nsresult rv;
|
||||
nsTArray<PluginTag> plugins;
|
||||
uint32_t parentEpoch;
|
||||
if (!cp->SendFindPlugins(ChromeEpochForContent(), &plugins, &parentEpoch)) {
|
||||
if (!cp->SendFindPlugins(ChromeEpochForContent(), &rv, &plugins, &parentEpoch) ||
|
||||
NS_FAILED(rv)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
@ -2634,7 +2636,7 @@ nsresult nsPluginHost::FindPlugins(bool aCreatePluginList, bool * aPluginsChange
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsresult
|
||||
mozilla::plugins::FindPluginsForContent(uint32_t aPluginEpoch,
|
||||
nsTArray<PluginTag>* aPlugins,
|
||||
uint32_t* aNewPluginEpoch)
|
||||
@ -2642,11 +2644,10 @@ mozilla::plugins::FindPluginsForContent(uint32_t aPluginEpoch,
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
|
||||
host->FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch);
|
||||
return true;
|
||||
return host->FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch);
|
||||
}
|
||||
|
||||
void
|
||||
nsresult
|
||||
nsPluginHost::FindPluginsForContent(uint32_t aPluginEpoch,
|
||||
nsTArray<PluginTag>* aPlugins,
|
||||
uint32_t* aNewPluginEpoch)
|
||||
@ -2654,11 +2655,14 @@ nsPluginHost::FindPluginsForContent(uint32_t aPluginEpoch,
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
// Load plugins so that the epoch is correct.
|
||||
LoadPlugins();
|
||||
nsresult rv = LoadPlugins();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
*aNewPluginEpoch = ChromeEpoch();
|
||||
if (aPluginEpoch == ChromeEpoch()) {
|
||||
return;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsTArray<nsCOMPtr<nsIInternalPluginTag>> plugins;
|
||||
@ -2693,6 +2697,7 @@ nsPluginHost::FindPluginsForContent(uint32_t aPluginEpoch,
|
||||
tag->mLastModifiedTime,
|
||||
tag->IsFromExtension()));
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// This function is not relevant for fake plugins.
|
||||
|
@ -118,9 +118,9 @@ public:
|
||||
void GetPlugins(nsTArray<nsCOMPtr<nsIInternalPluginTag>>& aPluginArray,
|
||||
bool aIncludeDisabled = false);
|
||||
|
||||
void FindPluginsForContent(uint32_t aPluginEpoch,
|
||||
nsTArray<mozilla::plugins::PluginTag>* aPlugins,
|
||||
uint32_t* aNewPluginEpoch);
|
||||
nsresult FindPluginsForContent(uint32_t aPluginEpoch,
|
||||
nsTArray<mozilla::plugins::PluginTag>* aPlugins,
|
||||
uint32_t* aNewPluginEpoch);
|
||||
|
||||
nsresult GetURL(nsISupports* pluginInst,
|
||||
const char* url,
|
||||
|
@ -19,7 +19,7 @@ bool
|
||||
SetupBridge(uint32_t aPluginId, dom::ContentParent* aContentParent,
|
||||
bool aForceBridgeNow, nsresult* aResult, uint32_t* aRunID);
|
||||
|
||||
bool
|
||||
nsresult
|
||||
FindPluginsForContent(uint32_t aPluginEpoch,
|
||||
nsTArray<PluginTag>* aPlugins,
|
||||
uint32_t* aNewPluginEpoch);
|
||||
|
@ -1344,30 +1344,10 @@ Promise::RejectInternal(JSContext* aCx,
|
||||
void
|
||||
Promise::Settle(JS::Handle<JS::Value> aValue, PromiseState aState)
|
||||
{
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
if (!mGlobal && mFullfillmentStack) {
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
JSContext* cx = jsapi.cx();
|
||||
JS::RootedObject stack(cx, mFullfillmentStack);
|
||||
JSAutoCompartment ac(cx, stack);
|
||||
JS::RootedString stackJSString(cx);
|
||||
if (JS::BuildStackString(cx, stack, &stackJSString)) {
|
||||
nsAutoJSString stackString;
|
||||
if (stackString.init(cx, stackJSString)) {
|
||||
// Put the string in the crash report here, since we're about to crash
|
||||
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("cced_promise_stack"),
|
||||
NS_ConvertUTF16toUTF8(stackString));
|
||||
} else {
|
||||
JS_ClearPendingException(cx);
|
||||
}
|
||||
} else {
|
||||
JS_ClearPendingException(cx);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mGlobal->IsDying()) {
|
||||
MOZ_ASSERT(mGlobal,
|
||||
"We really should have a global here. Except we sometimes don't "
|
||||
"in the wild for some odd reason");
|
||||
if (!mGlobal || mGlobal->IsDying()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -127,7 +127,10 @@ this.PushDB.prototype = {
|
||||
this._dbStoreName,
|
||||
function txnCb(aTxn, aStore) {
|
||||
console.debug("delete: Removing record", aKeyID);
|
||||
aStore.delete(aKeyID);
|
||||
aStore.get(aKeyID).onsuccess = event => {
|
||||
aTxn.result = event.target.result;
|
||||
aStore.delete(aKeyID);
|
||||
};
|
||||
},
|
||||
resolve,
|
||||
reject
|
||||
@ -213,28 +216,26 @@ this.PushDB.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates all push registrations for an origin.
|
||||
* Reduces all records associated with an origin to a single value.
|
||||
*
|
||||
* @param {String} origin The origin, matched as a prefix against the scope.
|
||||
* @param {String} originAttributes Additional origin attributes. Requires
|
||||
* an exact match.
|
||||
* @param {Function} updateFunc A function that receives the existing
|
||||
* registration record as its argument, and returns a new record. The record
|
||||
* will not be updated if the function returns `null`, `undefined`, or an
|
||||
* invalid record. If the function returns `false`, the record will be
|
||||
* dropped.
|
||||
* @returns {Promise} A promise that resolves once all records have been
|
||||
* updated.
|
||||
* @param {Function} callback A function with the signature `(result,
|
||||
* record, cursor)`, where `result` is the value returned by the previous
|
||||
* invocation, `record` is the registration, and `cursor` is an `IDBCursor`.
|
||||
* @param {Object} [initialValue] The value to use for the first invocation.
|
||||
* @returns {Promise} Resolves with the value of the last invocation.
|
||||
*/
|
||||
updateByOrigin: function(origin, originAttributes, updateFunc) {
|
||||
console.debug("updateByOrigin()");
|
||||
reduceByOrigin: function(origin, originAttributes, callback, initialValue) {
|
||||
console.debug("forEachOrigin()");
|
||||
|
||||
return new Promise((resolve, reject) =>
|
||||
this.newTxn(
|
||||
"readwrite",
|
||||
this._dbStoreName,
|
||||
(aTxn, aStore) => {
|
||||
aTxn.result = [];
|
||||
aTxn.result = initialValue;
|
||||
|
||||
let index = aStore.index("identifiers");
|
||||
let range = IDBKeyRange.bound(
|
||||
@ -247,19 +248,7 @@ this.PushDB.prototype = {
|
||||
return;
|
||||
}
|
||||
let record = this.toPushRecord(cursor.value);
|
||||
let newRecord = updateFunc(record);
|
||||
if (newRecord === false) {
|
||||
console.debug("updateByOrigin: Removing record for key ID",
|
||||
record.keyID);
|
||||
cursor.delete();
|
||||
} else if (this.isValidRecord(newRecord)) {
|
||||
console.debug("updateByOrigin: Updating record for key ID",
|
||||
record.keyID, newRecord);
|
||||
cursor.update(newRecord);
|
||||
} else {
|
||||
console.error("updateByOrigin: Ignoring invalid update for record",
|
||||
record.keyID, newRecord);
|
||||
}
|
||||
aTxn.result = callback(aTxn.result, record, cursor);
|
||||
cursor.continue();
|
||||
};
|
||||
},
|
||||
|
@ -195,6 +195,9 @@ PushRecord.prototype = {
|
||||
},
|
||||
|
||||
quotaChanged() {
|
||||
if (!this.hasPermission()) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
return this.getLastVisit()
|
||||
.then(lastVisit => lastVisit > this.lastPush);
|
||||
},
|
||||
@ -207,7 +210,7 @@ PushRecord.prototype = {
|
||||
return this.quota === 0;
|
||||
},
|
||||
|
||||
toRegistration() {
|
||||
toSubscription() {
|
||||
return {
|
||||
pushEndpoint: this.pushEndpoint,
|
||||
lastPush: this.lastPush,
|
||||
@ -215,13 +218,6 @@ PushRecord.prototype = {
|
||||
p256dhKey: this.p256dhPublicKey,
|
||||
};
|
||||
},
|
||||
|
||||
toRegister() {
|
||||
return {
|
||||
pushEndpoint: this.pushEndpoint,
|
||||
p256dhKey: this.p256dhPublicKey,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
// Define lazy getters for the principal and scope URI. IndexedDB can't store
|
||||
|
@ -27,6 +27,10 @@ const CONNECTION_PROTOCOLS = [PushServiceWebSocket, PushServiceHttp2];
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AlarmService",
|
||||
"resource://gre/modules/AlarmService.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gContentSecurityManager",
|
||||
"@mozilla.org/contentsecuritymanager;1",
|
||||
"nsIContentSecurityManager");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["PushService"];
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "console", () => {
|
||||
@ -282,14 +286,14 @@ this.PushService = {
|
||||
inBrowser: data.browserOnly });
|
||||
this._db.getAllByOriginAttributes(originAttributes)
|
||||
.then(records => Promise.all(records.map(record =>
|
||||
this._db.delete(record.keyID).then(
|
||||
_ => this._backgroundUnregister(record),
|
||||
err => {
|
||||
this._db.delete(record.keyID)
|
||||
.catch(err => {
|
||||
console.error("webapps-clear-data: Error removing record",
|
||||
record, err);
|
||||
|
||||
this._backgroundUnregister(record);
|
||||
// This is the record we were unable to delete.
|
||||
return record;
|
||||
})
|
||||
.then(maybeDeleted => this._backgroundUnregister(maybeDeleted))
|
||||
)
|
||||
));
|
||||
|
||||
@ -297,16 +301,23 @@ this.PushService = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends an unregister request to the server in the background. If the
|
||||
* service is not connected, this function is a no-op.
|
||||
*
|
||||
* @param {PushRecord} record The record to unregister.
|
||||
*/
|
||||
_backgroundUnregister: function(record) {
|
||||
if (this._service.isConnected()) {
|
||||
// courtesy, but don't establish a connection
|
||||
// just for it
|
||||
console.debug("backgroundUnregister: Notifying server", record);
|
||||
return this._sendUnregister(record)
|
||||
.catch(function(e) {
|
||||
console.error("backgroundUnregister: Error notifying server", e);
|
||||
});
|
||||
console.debug("backgroundUnregister()");
|
||||
|
||||
if (!this._service.isConnected() || !record) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.debug("backgroundUnregister: Notifying server", record);
|
||||
this._sendUnregister(record).catch(e => {
|
||||
console.error("backgroundUnregister: Error notifying server", e);
|
||||
});
|
||||
},
|
||||
|
||||
// utility function used to add/remove observers in startObservers() and
|
||||
@ -320,16 +331,34 @@ this.PushService = {
|
||||
}
|
||||
},
|
||||
|
||||
_findService: function(serverURI) {
|
||||
var uri;
|
||||
var service;
|
||||
if (serverURI) {
|
||||
for (let connProtocol of CONNECTION_PROTOCOLS) {
|
||||
uri = connProtocol.checkServerURI(serverURI);
|
||||
if (uri) {
|
||||
service = connProtocol;
|
||||
break;
|
||||
}
|
||||
_findService: function(serverURL) {
|
||||
console.debug("findService()");
|
||||
|
||||
let uri;
|
||||
let service;
|
||||
|
||||
if (!serverURL) {
|
||||
console.warn("findService: No dom.push.serverURL found");
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
uri = Services.io.newURI(serverURL, null, null);
|
||||
} catch (e) {
|
||||
console.warn("findService: Error creating valid URI from",
|
||||
"dom.push.serverURL", serverURL);
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!gContentSecurityManager.isURIPotentiallyTrustworthy(uri)) {
|
||||
console.warn("findService: Untrusted server URI", uri.spec);
|
||||
return [];
|
||||
}
|
||||
|
||||
for (let connProtocol of CONNECTION_PROTOCOLS) {
|
||||
if (connProtocol.validServerURI(uri)) {
|
||||
service = connProtocol;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return [service, uri];
|
||||
@ -421,22 +450,7 @@ this.PushService = {
|
||||
if (options.serverURI) {
|
||||
// this is use for xpcshell test.
|
||||
|
||||
var uri;
|
||||
var service;
|
||||
if (!options.service) {
|
||||
for (let connProtocol of CONNECTION_PROTOCOLS) {
|
||||
uri = connProtocol.checkServerURI(options.serverURI);
|
||||
if (uri) {
|
||||
service = connProtocol;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
uri = Services.io.newURI(options.serverURI, null, null);
|
||||
service = options.service;
|
||||
} catch(e) {}
|
||||
}
|
||||
let [service, uri] = this._findService(options.serverURI);
|
||||
if (!service) {
|
||||
this._setState(PUSH_SERVICE_INIT);
|
||||
return;
|
||||
@ -573,7 +587,7 @@ this.PushService = {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.dropRegistrations()
|
||||
return this.dropUnexpiredRegistrations()
|
||||
.then(_ => {
|
||||
this._db.close();
|
||||
this._db = null;
|
||||
@ -664,12 +678,35 @@ this.PushService = {
|
||||
}
|
||||
},
|
||||
|
||||
dropRegistrations: function() {
|
||||
return this._notifyAllAppsRegister()
|
||||
.then(_ => this._db.drop());
|
||||
/**
|
||||
* Drops all active registrations and notifies the associated service
|
||||
* workers. This function is called when the user switches Push servers,
|
||||
* or when the server invalidates all existing registrations.
|
||||
*
|
||||
* We ignore expired registrations because they're already handled in other
|
||||
* code paths. Registrations that expired after exceeding their quotas are
|
||||
* evicted at startup, or on the next `idle-daily` event. Registrations that
|
||||
* expired because the user revoked the notification permission are evicted
|
||||
* once the permission is reinstated.
|
||||
*/
|
||||
dropUnexpiredRegistrations: function() {
|
||||
let subscriptionChanges = [];
|
||||
return this._db.clearIf(record => {
|
||||
if (record.isExpired()) {
|
||||
return false;
|
||||
}
|
||||
subscriptionChanges.push(record);
|
||||
return true;
|
||||
}).then(() => {
|
||||
this.notifySubscriptionChanges(subscriptionChanges);
|
||||
});
|
||||
},
|
||||
|
||||
_notifySubscriptionChangeObservers: function(record) {
|
||||
if (!record) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify XPCOM observers.
|
||||
Services.obs.notifyObservers(
|
||||
null,
|
||||
@ -704,31 +741,51 @@ this.PushService = {
|
||||
}
|
||||
},
|
||||
|
||||
// Fires a push-register system message to all applications that have
|
||||
// registration.
|
||||
_notifyAllAppsRegister: function() {
|
||||
console.debug("notifyAllAppsRegister()");
|
||||
// records are objects describing the registration as stored in IndexedDB.
|
||||
return this._db.getAllUnexpired().then(records => {
|
||||
records.forEach(record => {
|
||||
this._notifySubscriptionChangeObservers(record);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
dropRegistrationAndNotifyApp: function(aKeyId) {
|
||||
return this._db.getByKeyID(aKeyId).then(record => {
|
||||
this._notifySubscriptionChangeObservers(record);
|
||||
return this._db.delete(aKeyId);
|
||||
});
|
||||
},
|
||||
|
||||
updateRegistrationAndNotifyApp: function(aOldKey, aRecord) {
|
||||
return this._db.delete(aOldKey)
|
||||
.then(_ => this._db.put(aRecord))
|
||||
/**
|
||||
* Drops a registration and notifies the associated service worker. If the
|
||||
* registration does not exist, this function is a no-op.
|
||||
*
|
||||
* @param {String} keyID The registration ID to remove.
|
||||
* @returns {Promise} Resolves once the worker has been notified.
|
||||
*/
|
||||
dropRegistrationAndNotifyApp: function(aKeyID) {
|
||||
return this._db.delete(aKeyID)
|
||||
.then(record => this._notifySubscriptionChangeObservers(record));
|
||||
},
|
||||
|
||||
/**
|
||||
* Replaces an existing registration and notifies the associated service
|
||||
* worker.
|
||||
*
|
||||
* @param {String} aOldKey The registration ID to replace.
|
||||
* @param {PushRecord} aNewRecord The new record.
|
||||
* @returns {Promise} Resolves once the worker has been notified.
|
||||
*/
|
||||
updateRegistrationAndNotifyApp: function(aOldKey, aNewRecord) {
|
||||
return this.updateRecordAndNotifyApp(aOldKey, _ => aNewRecord);
|
||||
},
|
||||
/**
|
||||
* Updates a registration and notifies the associated service worker.
|
||||
*
|
||||
* @param {String} keyID The registration ID to update.
|
||||
* @param {Function} updateFunc Returns the updated record.
|
||||
* @returns {Promise} Resolves with the updated record once the worker
|
||||
* has been notified.
|
||||
*/
|
||||
updateRecordAndNotifyApp: function(aKeyID, aUpdateFunc) {
|
||||
return this._db.update(aKeyID, aUpdateFunc)
|
||||
.then(record => {
|
||||
this._notifySubscriptionChangeObservers(record);
|
||||
return record;
|
||||
});
|
||||
},
|
||||
|
||||
notifySubscriptionChanges: function(records) {
|
||||
records.forEach(record => {
|
||||
this._notifySubscriptionChangeObservers(record);
|
||||
});
|
||||
},
|
||||
|
||||
ensureP256dhKey: function(record) {
|
||||
if (record.p256dhPublicKey && record.p256dhPrivateKey) {
|
||||
return Promise.resolve(record);
|
||||
@ -748,19 +805,6 @@ this.PushService = {
|
||||
});
|
||||
},
|
||||
|
||||
updateRecordAndNotifyApp: function(aKeyID, aUpdateFunc) {
|
||||
return this._db.update(aKeyID, aUpdateFunc)
|
||||
.then(record => {
|
||||
this._notifySubscriptionChangeObservers(record);
|
||||
return record;
|
||||
});
|
||||
},
|
||||
|
||||
dropRecordAndNotifyApp: function(aRecord) {
|
||||
return this._db.delete(aRecord.keyID)
|
||||
.then(_ => this._notifySubscriptionChangeObservers(aRecord));
|
||||
},
|
||||
|
||||
_recordDidNotNotify: function(reason) {
|
||||
Services.telemetry.
|
||||
getHistogramById("PUSH_API_NOTIFICATION_RECEIVED_BUT_DID_NOT_NOTIFY").
|
||||
@ -935,7 +979,7 @@ this.PushService = {
|
||||
err => this._onRegisterError(err))
|
||||
.then(record => {
|
||||
this._deletePendingRequest(aPageRecord);
|
||||
return record.toRegister();
|
||||
return record.toSubscription();
|
||||
}, err => {
|
||||
this._deletePendingRequest(aPageRecord);
|
||||
throw err;
|
||||
@ -1076,12 +1120,12 @@ this.PushService = {
|
||||
if (isChanged) {
|
||||
// If the user revisited the site, drop the expired push
|
||||
// registration and re-register.
|
||||
return this._db.delete(record.keyID);
|
||||
return this.dropRegistrationAndNotifyApp(record.keyID);
|
||||
}
|
||||
throw new Error("Push subscription expired");
|
||||
}).then(_ => this._lookupOrPutPendingRequest(aPageRecord));
|
||||
}
|
||||
return record.toRegister();
|
||||
return record.toSubscription();
|
||||
});
|
||||
},
|
||||
|
||||
@ -1194,12 +1238,12 @@ this.PushService = {
|
||||
if (record.isExpired()) {
|
||||
return record.quotaChanged().then(isChanged => {
|
||||
if (isChanged) {
|
||||
return this._db.delete(record.keyID).then(_ => null);
|
||||
return this.dropRegistrationAndNotifyApp(record.keyID).then(_ => null);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
return record.toRegistration();
|
||||
return record.toSubscription();
|
||||
});
|
||||
},
|
||||
|
||||
@ -1212,7 +1256,7 @@ this.PushService = {
|
||||
if (isChanged) {
|
||||
// If the user revisited the site, drop the expired push
|
||||
// registration and notify the associated service worker.
|
||||
return this.dropRecordAndNotifyApp(record);
|
||||
return this.dropRegistrationAndNotifyApp(record.keyID);
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error("dropExpiredRegistrations: Error dropping registration",
|
||||
@ -1259,69 +1303,84 @@ this.PushService = {
|
||||
// Permission set to "allow". Drop all expired registrations for this
|
||||
// site, notify the associated service workers, and reset the quota
|
||||
// for active registrations.
|
||||
return this._updateByPrincipal(
|
||||
return this._reduceByPrincipal(
|
||||
permission.principal,
|
||||
record => this._permissionAllowed(record)
|
||||
);
|
||||
(subscriptionChanges, record, cursor) => {
|
||||
this._permissionAllowed(subscriptionChanges, record, cursor);
|
||||
return subscriptionChanges;
|
||||
},
|
||||
[]
|
||||
).then(subscriptionChanges => {
|
||||
this.notifySubscriptionChanges(subscriptionChanges);
|
||||
});
|
||||
} else if (isChange || (isAllow && type == "deleted")) {
|
||||
// Permission set to "block" or "always ask," or "allow" permission
|
||||
// removed. Expire all registrations for this site.
|
||||
return this._updateByPrincipal(
|
||||
return this._reduceByPrincipal(
|
||||
permission.principal,
|
||||
record => this._permissionDenied(record)
|
||||
(memo, record, cursor) => this._permissionDenied(record, cursor)
|
||||
);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
_updateByPrincipal: function(principal, updateFunc) {
|
||||
return this._db.updateByOrigin(
|
||||
_reduceByPrincipal: function(principal, callback, initialValue) {
|
||||
return this._db.reduceByOrigin(
|
||||
principal.URI.prePath,
|
||||
ChromeUtils.originAttributesToSuffix(principal.originAttributes),
|
||||
updateFunc
|
||||
callback,
|
||||
initialValue
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Expires all registrations if the push permission is revoked. We only
|
||||
* expire the registration so we can notify the service worker as soon as
|
||||
* the permission is reinstated. If we just deleted the registration, the
|
||||
* worker wouldn't be notified until the next visit to the site.
|
||||
* The update function called for each registration record if the push
|
||||
* permission is revoked. We only expire the record so we can notify the
|
||||
* service worker as soon as the permission is reinstated. If we just
|
||||
* deleted the record, the worker wouldn't be notified until the next visit
|
||||
* to the site.
|
||||
*
|
||||
* @param {Array} A list of records to expire.
|
||||
* @returns {Promise} A promise resolved with the expired records.
|
||||
* @param {PushRecord} record The record to expire.
|
||||
* @param {IDBCursor} cursor The IndexedDB cursor.
|
||||
*/
|
||||
_permissionDenied: function(record) {
|
||||
_permissionDenied: function(record, cursor) {
|
||||
console.debug("permissionDenied()");
|
||||
|
||||
if (!record.quotaApplies() || record.isExpired()) {
|
||||
// Ignore already-expired records.
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
// Drop the registration in the background.
|
||||
this._backgroundUnregister(record);
|
||||
record.setQuota(0);
|
||||
return record;
|
||||
cursor.update(record);
|
||||
},
|
||||
|
||||
/**
|
||||
* Drops all expired registrations, notifies the associated service
|
||||
* workers, and resets the quota for active registrations if the push
|
||||
* permission is granted.
|
||||
* The update function called for each registration record if the push
|
||||
* permission is granted. If the record has expired, it will be dropped;
|
||||
* otherwise, its quota will be reset to the default value.
|
||||
*
|
||||
* @param {Array} A list of records to refresh.
|
||||
* @returns {Promise} A promise resolved with the refreshed records.
|
||||
* @param {Array} subscriptionChanges A list of records whose associated
|
||||
* service workers should be notified once the transaction has committed.
|
||||
* @param {PushRecord} record The record to update.
|
||||
* @param {IDBCursor} cursor The IndexedDB cursor.
|
||||
*/
|
||||
_permissionAllowed: function(record) {
|
||||
_permissionAllowed: function(subscriptionChanges, record, cursor) {
|
||||
console.debug("permissionAllowed()");
|
||||
|
||||
if (!record.quotaApplies()) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
if (record.isExpired()) {
|
||||
// If the registration has expired, drop and notify the worker
|
||||
// unconditionally.
|
||||
this._notifySubscriptionChangeObservers(record);
|
||||
return false;
|
||||
subscriptionChanges.push(record);
|
||||
cursor.delete();
|
||||
return;
|
||||
}
|
||||
record.resetQuota();
|
||||
return record;
|
||||
cursor.update(record);
|
||||
},
|
||||
};
|
||||
|
@ -441,26 +441,8 @@ this.PushServiceHttp2 = {
|
||||
return this._mainPushService !== null;
|
||||
},
|
||||
|
||||
checkServerURI: function(serverURL) {
|
||||
if (!serverURL) {
|
||||
console.warn("checkServerURI: No dom.push.serverURL found");
|
||||
return;
|
||||
}
|
||||
|
||||
let uri;
|
||||
try {
|
||||
uri = Services.io.newURI(serverURL, null, null);
|
||||
} catch(e) {
|
||||
console.warn("checkServerURI: Error creating valid URI from",
|
||||
"dom.push.serverURL", serverURL);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (uri.scheme !== "https") {
|
||||
console.warn("checkServerURI: Unsupported scheme", uri.scheme);
|
||||
return null;
|
||||
}
|
||||
return uri;
|
||||
validServerURI: function(serverURI) {
|
||||
return serverURI.scheme == "http" || serverURI.scheme == "https";
|
||||
},
|
||||
|
||||
connect: function(subscriptions) {
|
||||
@ -858,14 +840,8 @@ PushRecordHttp2.prototype = Object.create(PushRecord.prototype, {
|
||||
},
|
||||
});
|
||||
|
||||
PushRecordHttp2.prototype.toRegistration = function() {
|
||||
let registration = PushRecord.prototype.toRegistration.call(this);
|
||||
registration.pushReceiptEndpoint = this.pushReceiptEndpoint;
|
||||
return registration;
|
||||
};
|
||||
|
||||
PushRecordHttp2.prototype.toRegister = function() {
|
||||
let register = PushRecord.prototype.toRegister.call(this);
|
||||
register.pushReceiptEndpoint = this.pushReceiptEndpoint;
|
||||
return register;
|
||||
PushRecordHttp2.prototype.toSubscription = function() {
|
||||
let subscription = PushRecord.prototype.toSubscription.call(this);
|
||||
subscription.pushReceiptEndpoint = this.pushReceiptEndpoint;
|
||||
return subscription;
|
||||
};
|
||||
|
@ -203,26 +203,8 @@ this.PushServiceWebSocket = {
|
||||
}
|
||||
},
|
||||
|
||||
checkServerURI: function(serverURL) {
|
||||
if (!serverURL) {
|
||||
console.warn("checkServerURI: No dom.push.serverURL found");
|
||||
return;
|
||||
}
|
||||
|
||||
let uri;
|
||||
try {
|
||||
uri = Services.io.newURI(serverURL, null, null);
|
||||
} catch(e) {
|
||||
console.warn("checkServerURI: Error creating valid URI from",
|
||||
"dom.push.serverURL", serverURL);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (uri.scheme !== "wss") {
|
||||
console.warn("checkServerURI: Unsupported websocket scheme", uri.scheme);
|
||||
return null;
|
||||
}
|
||||
return uri;
|
||||
validServerURI: function(serverURI) {
|
||||
return serverURI.scheme == "ws" || serverURI.scheme == "wss";
|
||||
},
|
||||
|
||||
get _UAID() {
|
||||
@ -840,7 +822,7 @@ this.PushServiceWebSocket = {
|
||||
if (this._UAID != reply.uaid) {
|
||||
console.debug("handleHelloReply: Received new UAID");
|
||||
|
||||
this._mainPushService.dropRegistrations()
|
||||
this._mainPushService.dropUnexpiredRegistrations()
|
||||
.then(finishHandshake.bind(this));
|
||||
|
||||
return;
|
||||
@ -1472,8 +1454,8 @@ PushRecordWebSocket.prototype = Object.create(PushRecord.prototype, {
|
||||
},
|
||||
});
|
||||
|
||||
PushRecordWebSocket.prototype.toRegistration = function() {
|
||||
let registration = PushRecord.prototype.toRegistration.call(this);
|
||||
registration.version = this.version;
|
||||
return registration;
|
||||
PushRecordWebSocket.prototype.toSubscription = function() {
|
||||
let subscription = PushRecord.prototype.toSubscription.call(this);
|
||||
subscription.version = this.version;
|
||||
return subscription;
|
||||
};
|
||||
|
@ -7,6 +7,7 @@ var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
Cu.import('resource://gre/modules/Task.jsm');
|
||||
Cu.import('resource://gre/modules/Timer.jsm');
|
||||
Cu.import('resource://gre/modules/Promise.jsm');
|
||||
Cu.import('resource://gre/modules/Preferences.jsm');
|
||||
|
153
dom/push/test/xpcshell/test_drop_expired.js
Normal file
153
dom/push/test/xpcshell/test_drop_expired.js
Normal file
@ -0,0 +1,153 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '2c43af06-ab6e-476a-adc4-16cbda54fb89';
|
||||
|
||||
var db;
|
||||
var quotaURI;
|
||||
var permURI;
|
||||
|
||||
function visitURI(uri, timestamp) {
|
||||
return addVisit({
|
||||
uri: uri,
|
||||
title: uri.spec,
|
||||
visits: [{
|
||||
visitDate: timestamp * 1000,
|
||||
transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
|
||||
}],
|
||||
});
|
||||
}
|
||||
|
||||
var putRecord = Task.async(function* ({scope, perm, quota, lastPush, lastVisit}) {
|
||||
let uri = Services.io.newURI(scope, null, null);
|
||||
|
||||
Services.perms.add(uri, 'desktop-notification',
|
||||
Ci.nsIPermissionManager[perm]);
|
||||
do_register_cleanup(() => {
|
||||
Services.perms.remove(uri, 'desktop-notification');
|
||||
});
|
||||
|
||||
yield visitURI(uri, lastVisit);
|
||||
|
||||
yield db.put({
|
||||
channelID: uri.path,
|
||||
pushEndpoint: 'https://example.org/push' + uri.path,
|
||||
scope: uri.spec,
|
||||
pushCount: 0,
|
||||
lastPush: lastPush,
|
||||
version: null,
|
||||
originAttributes: '',
|
||||
quota: quota,
|
||||
});
|
||||
|
||||
return uri;
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
userAgentID: userAgentID,
|
||||
});
|
||||
|
||||
db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* setUp() {
|
||||
// An expired registration that should be evicted on startup. Permission is
|
||||
// granted for this origin, and the last visit is more recent than the last
|
||||
// push message.
|
||||
yield putRecord({
|
||||
scope: 'https://example.com/expired-quota-restored',
|
||||
perm: 'ALLOW_ACTION',
|
||||
quota: 0,
|
||||
lastPush: Date.now() - 10,
|
||||
lastVisit: Date.now(),
|
||||
});
|
||||
|
||||
// An expired registration that we should evict when the origin is visited
|
||||
// again.
|
||||
quotaURI = yield putRecord({
|
||||
scope: 'https://example.xyz/expired-quota-exceeded',
|
||||
perm: 'ALLOW_ACTION',
|
||||
quota: 0,
|
||||
lastPush: Date.now() - 10,
|
||||
lastVisit: Date.now() - 20,
|
||||
});
|
||||
|
||||
// An expired registration that we should evict when permission is granted
|
||||
// again.
|
||||
permURI = yield putRecord({
|
||||
scope: 'https://example.info/expired-perm-revoked',
|
||||
perm: 'DENY_ACTION',
|
||||
quota: 0,
|
||||
lastPush: Date.now() - 10,
|
||||
lastVisit: Date.now(),
|
||||
});
|
||||
|
||||
// An active registration that we should leave alone.
|
||||
yield putRecord({
|
||||
scope: 'https://example.ninja/active',
|
||||
perm: 'ALLOW_ACTION',
|
||||
quota: 16,
|
||||
lastPush: Date.now() - 10,
|
||||
lastVisit: Date.now() - 20,
|
||||
});
|
||||
|
||||
let subChangePromise = promiseObserverNotification(
|
||||
'push-subscription-change',
|
||||
(subject, data) => data == 'https://example.com/expired-quota-restored'
|
||||
);
|
||||
|
||||
PushService.init({
|
||||
serverURI: 'wss://push.example.org/',
|
||||
networkInfo: new MockDesktopNetworkInfo(),
|
||||
db,
|
||||
makeWebSocket(uri) {
|
||||
return new MockWebSocket(uri, {
|
||||
onHello(request) {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: userAgentID,
|
||||
}));
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
yield waitForPromise(subChangePromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for subscription change event on startup');
|
||||
});
|
||||
|
||||
add_task(function* test_site_visited() {
|
||||
let subChangePromise = promiseObserverNotification(
|
||||
'push-subscription-change',
|
||||
(subject, data) => data == 'https://example.xyz/expired-quota-exceeded'
|
||||
);
|
||||
|
||||
yield visitURI(quotaURI, Date.now());
|
||||
PushService.observe(null, 'idle-daily', '');
|
||||
|
||||
yield waitForPromise(subChangePromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for subscription change event after visit');
|
||||
});
|
||||
|
||||
add_task(function* test_perm_restored() {
|
||||
let subChangePromise = promiseObserverNotification(
|
||||
'push-subscription-change',
|
||||
(subject, data) => data == 'https://example.info/expired-perm-revoked'
|
||||
);
|
||||
|
||||
Services.perms.add(permURI, 'desktop-notification',
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
|
||||
yield waitForPromise(subChangePromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for subscription change event after permission');
|
||||
});
|
@ -5,9 +5,13 @@
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '1ca1cf66-eeb4-4df7-87c1-d5c92906ab90';
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs();
|
||||
setPrefs({
|
||||
userAgentID: userAgentID,
|
||||
});
|
||||
disableServiceWorkerEvents(
|
||||
'https://example.com/page/1',
|
||||
'https://example.com/page/2',
|
||||
@ -74,7 +78,7 @@ add_task(function* test_notification_incomplete() {
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
messageType: 'hello',
|
||||
status: 200,
|
||||
uaid: '1ca1cf66-eeb4-4df7-87c1-d5c92906ab90'
|
||||
uaid: userAgentID,
|
||||
}));
|
||||
this.serverSendMsg(JSON.stringify({
|
||||
// Missing "updates" field; should ignore message.
|
||||
|
@ -7,6 +7,8 @@ const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
const userAgentID = '28cd09e2-7506-42d8-9e50-b02785adc7ef';
|
||||
|
||||
var db;
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
@ -15,12 +17,24 @@ function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
let putRecord = Task.async(function* (perm, record) {
|
||||
let uri = Services.io.newURI(record.scope, null, null);
|
||||
|
||||
Services.perms.add(uri, 'desktop-notification',
|
||||
Ci.nsIPermissionManager[perm]);
|
||||
do_register_cleanup(() => {
|
||||
Services.perms.remove(uri, 'desktop-notification');
|
||||
});
|
||||
|
||||
yield db.put(record);
|
||||
});
|
||||
|
||||
add_task(function* test_expiration_history_observer() {
|
||||
let db = PushServiceWebSocket.newPushDB();
|
||||
db = PushServiceWebSocket.newPushDB();
|
||||
do_register_cleanup(() => db.drop().then(_ => db.close()));
|
||||
|
||||
// A registration that we'll expire...
|
||||
yield db.put({
|
||||
yield putRecord('ALLOW_ACTION', {
|
||||
channelID: '379c0668-8323-44d2-a315-4ee83f1a9ee9',
|
||||
pushEndpoint: 'https://example.org/push/1',
|
||||
scope: 'https://example.com/deals',
|
||||
@ -32,7 +46,7 @@ add_task(function* test_expiration_history_observer() {
|
||||
});
|
||||
|
||||
// ...And a registration that we'll evict on startup.
|
||||
yield db.put({
|
||||
yield putRecord('ALLOW_ACTION', {
|
||||
channelID: '4cb6e454-37cf-41c4-a013-4e3a7fdd0bf1',
|
||||
pushEndpoint: 'https://example.org/push/3',
|
||||
scope: 'https://example.com/stuff',
|
||||
@ -101,7 +115,7 @@ add_task(function* test_expiration_history_observer() {
|
||||
});
|
||||
|
||||
// Add an expired registration that we'll revive later.
|
||||
yield db.put({
|
||||
yield putRecord('ALLOW_ACTION', {
|
||||
channelID: 'eb33fc90-c883-4267-b5cb-613969e8e349',
|
||||
pushEndpoint: 'https://example.org/push/2',
|
||||
scope: 'https://example.com/auctions',
|
||||
|
@ -79,7 +79,6 @@ add_task(function* test1() {
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/subscribe5xxCode",
|
||||
service: PushServiceHttp2,
|
||||
db
|
||||
});
|
||||
|
||||
|
@ -83,7 +83,6 @@ add_task(function* test1() {
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/subscribe",
|
||||
service: PushServiceHttp2,
|
||||
db
|
||||
});
|
||||
|
||||
|
@ -93,7 +93,6 @@ add_task(function* test1() {
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/subscribe",
|
||||
service: PushServiceHttp2,
|
||||
db
|
||||
});
|
||||
|
||||
|
@ -88,7 +88,6 @@ add_task(function* test1() {
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/subscribe",
|
||||
service: PushServiceHttp2,
|
||||
db
|
||||
});
|
||||
|
||||
|
@ -66,7 +66,6 @@ add_task(function* test1() {
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/subscribe",
|
||||
service: PushServiceHttp2,
|
||||
db
|
||||
});
|
||||
|
||||
|
@ -4,6 +4,7 @@ tail =
|
||||
# Push notifications and alarms are currently disabled on Android.
|
||||
skip-if = toolkit == 'android'
|
||||
|
||||
[test_drop_expired.js]
|
||||
[test_notification_ack.js]
|
||||
[test_notification_data.js]
|
||||
[test_notification_duplicate.js]
|
||||
|
@ -31,11 +31,11 @@
|
||||
#include "nsIWebNavigation.h"
|
||||
#include "nsMimeTypes.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsNullPrincipal.h"
|
||||
#include "nsIContentPolicy.h"
|
||||
#include "nsSupportsPrimitives.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsString.h"
|
||||
#include "nsScriptSecurityManager.h"
|
||||
#include "nsStringStream.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/dom/CSPReportBinding.h"
|
||||
@ -265,7 +265,10 @@ NS_IMPL_ISUPPORTS_CI(nsCSPContext,
|
||||
nsISerializable)
|
||||
|
||||
nsCSPContext::nsCSPContext()
|
||||
: mSelfURI(nullptr)
|
||||
: mInnerWindowID(0)
|
||||
, mLoadingContext(nullptr)
|
||||
, mLoadingPrincipal(nullptr)
|
||||
, mQueueUpMessages(true)
|
||||
{
|
||||
CSPCONTEXTLOG(("nsCSPContext::nsCSPContext"));
|
||||
}
|
||||
@ -337,18 +340,6 @@ nsCSPContext::GetReferrerPolicy(uint32_t* outPolicy, bool* outIsSet)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCSPContext::RemovePolicy(uint32_t aIndex)
|
||||
{
|
||||
if (aIndex >= mPolicies.Length()) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
mPolicies.RemoveElementAt(aIndex);
|
||||
// reset cache since effective policy changes
|
||||
mShouldLoadCache.Clear();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCSPContext::AppendPolicy(const nsAString& aPolicyString,
|
||||
bool aReportOnly)
|
||||
@ -358,7 +349,7 @@ nsCSPContext::AppendPolicy(const nsAString& aPolicyString,
|
||||
|
||||
// Use the mSelfURI from setRequestContext, see bug 991474
|
||||
NS_ASSERTION(mSelfURI, "mSelfURI required for AppendPolicy, but not set");
|
||||
nsCSPPolicy* policy = nsCSPParser::parseContentSecurityPolicy(aPolicyString, mSelfURI, aReportOnly, mInnerWindowID);
|
||||
nsCSPPolicy* policy = nsCSPParser::parseContentSecurityPolicy(aPolicyString, mSelfURI, aReportOnly, this);
|
||||
if (policy) {
|
||||
mPolicies.AppendElement(policy);
|
||||
// reset cache since effective policy changes
|
||||
@ -590,55 +581,94 @@ nsCSPContext::LogViolationDetails(uint16_t aViolationType,
|
||||
#undef CASE_CHECK_AND_REPORT
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCSPContext::SetRequestContext(nsIURI* aSelfURI,
|
||||
nsIURI* aReferrer,
|
||||
nsIChannel* aChannel)
|
||||
nsCSPContext::SetRequestContext(nsIDOMDocument* aDOMDocument,
|
||||
nsIPrincipal* aPrincipal)
|
||||
{
|
||||
NS_PRECONDITION(aSelfURI || aChannel, "Need aSelfURI or aChannel to set the context properly");
|
||||
NS_ENSURE_ARG(aSelfURI || aChannel);
|
||||
NS_PRECONDITION(aDOMDocument || aPrincipal,
|
||||
"Can't set context without doc or principal");
|
||||
NS_ENSURE_ARG(aDOMDocument || aPrincipal);
|
||||
|
||||
// first use aSelfURI. If that's not available get the URI from aChannel.
|
||||
mSelfURI = aSelfURI;
|
||||
if (!mSelfURI) {
|
||||
nsresult rv = aChannel->GetURI(getter_AddRefs(mSelfURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
NS_ASSERTION(mSelfURI, "No aSelfURI and no URI available from channel in SetRequestContext, can not translate 'self' into actual URI");
|
||||
|
||||
if (aChannel) {
|
||||
mInnerWindowID = nsContentUtils::GetInnerWindowID(aChannel);
|
||||
aChannel->GetLoadGroup(getter_AddRefs(mCallingChannelLoadGroup));
|
||||
|
||||
// Storing the nsINode from the LoadInfo of the original channel,
|
||||
// so we can reuse that information when sending reports.
|
||||
nsCOMPtr<nsILoadInfo> loadInfo;
|
||||
aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
|
||||
if (loadInfo) {
|
||||
nsINode* loadingNode = loadInfo->LoadingNode();
|
||||
if (loadingNode) {
|
||||
mLoadingContext = do_GetWeakReference(loadingNode);
|
||||
}
|
||||
}
|
||||
if (aDOMDocument) {
|
||||
nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDOMDocument);
|
||||
mLoadingContext = do_GetWeakReference(doc);
|
||||
mSelfURI = doc->GetDocumentURI();
|
||||
doc->GetReferrer(mReferrer);
|
||||
mInnerWindowID = doc->InnerWindowID();
|
||||
// the innerWindowID is not available for CSPs delivered through the
|
||||
// header at the time setReqeustContext is called - let's queue up
|
||||
// console messages until it becomes available, see flushConsoleMessages
|
||||
mQueueUpMessages = !mInnerWindowID;
|
||||
mCallingChannelLoadGroup = doc->GetDocumentLoadGroup();
|
||||
}
|
||||
else {
|
||||
NS_WARNING("Channel needed (but null) in SetRequestContext. Cannot query loadgroup, which means report sending may fail.");
|
||||
}
|
||||
|
||||
mReferrer = aReferrer;
|
||||
if (!mReferrer) {
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
|
||||
if (httpChannel) {
|
||||
httpChannel->GetReferrer(getter_AddRefs(mReferrer));
|
||||
}
|
||||
else {
|
||||
NS_WARNING("Channel provided to SetRequestContext is not an nsIHttpChannel so referrer is not available for reporting." );
|
||||
}
|
||||
NS_WARNING("No Document in SetRequestContext; can not query loadgroup; sending reports may fail.");
|
||||
mLoadingPrincipal = aPrincipal;
|
||||
mLoadingPrincipal->GetURI(getter_AddRefs(mSelfURI));
|
||||
// if no document is available, then it also does not make sense to queue console messages
|
||||
// sending messages to the browser conolse instead of the web console in that case.
|
||||
mQueueUpMessages = false;
|
||||
}
|
||||
|
||||
NS_ASSERTION(mSelfURI, "mSelfURI not available, can not translate 'self' into actual URI");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
struct ConsoleMsgQueueElem {
|
||||
nsXPIDLString mMsg;
|
||||
nsString mSourceName;
|
||||
nsString mSourceLine;
|
||||
uint32_t mLineNumber;
|
||||
uint32_t mColumnNumber;
|
||||
uint32_t mSeverityFlag;
|
||||
};
|
||||
|
||||
void
|
||||
nsCSPContext::flushConsoleMessages()
|
||||
{
|
||||
// should flush messages even if doc is not available
|
||||
nsCOMPtr<nsIDocument> doc = do_QueryReferent(mLoadingContext);
|
||||
if (doc) {
|
||||
mInnerWindowID = doc->InnerWindowID();
|
||||
}
|
||||
mQueueUpMessages = false;
|
||||
|
||||
for (uint32_t i = 0; i < mConsoleMsgQueue.Length(); i++) {
|
||||
ConsoleMsgQueueElem &elem = mConsoleMsgQueue[i];
|
||||
CSP_LogMessage(elem.mMsg, elem.mSourceName, elem.mSourceLine,
|
||||
elem.mLineNumber, elem.mColumnNumber,
|
||||
elem.mSeverityFlag, "CSP", mInnerWindowID);
|
||||
}
|
||||
mConsoleMsgQueue.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPContext::logToConsole(const char16_t* aName,
|
||||
const char16_t** aParams,
|
||||
uint32_t aParamsLength,
|
||||
const nsAString& aSourceName,
|
||||
const nsAString& aSourceLine,
|
||||
uint32_t aLineNumber,
|
||||
uint32_t aColumnNumber,
|
||||
uint32_t aSeverityFlag)
|
||||
{
|
||||
// let's check if we have to queue up console messages
|
||||
if (mQueueUpMessages) {
|
||||
nsXPIDLString msg;
|
||||
CSP_GetLocalizedStr(aName, aParams, aParamsLength, getter_Copies(msg));
|
||||
ConsoleMsgQueueElem &elem = *mConsoleMsgQueue.AppendElement();
|
||||
elem.mMsg = msg;
|
||||
elem.mSourceName = PromiseFlatString(aSourceName);
|
||||
elem.mSourceLine = PromiseFlatString(aSourceLine);
|
||||
elem.mLineNumber = aLineNumber;
|
||||
elem.mColumnNumber = aColumnNumber;
|
||||
elem.mSeverityFlag = aSeverityFlag;
|
||||
return;
|
||||
}
|
||||
CSP_LogLocalizedStr(aName, aParams, aParamsLength, aSourceName,
|
||||
aSourceLine, aLineNumber, aColumnNumber,
|
||||
aSeverityFlag, "CSP", mInnerWindowID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends CSP violation reports to all sources listed under report-uri.
|
||||
*
|
||||
@ -720,10 +750,8 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource,
|
||||
report.mCsp_report.mOriginal_policy = originalPolicy;
|
||||
|
||||
// referrer
|
||||
if (mReferrer) {
|
||||
nsAutoCString referrerURI;
|
||||
mReferrer->GetSpec(referrerURI);
|
||||
report.mCsp_report.mReferrer = NS_ConvertUTF8toUTF16(referrerURI);
|
||||
if (!mReferrer.IsEmpty()) {
|
||||
report.mCsp_report.mReferrer = mReferrer;
|
||||
}
|
||||
|
||||
// violated-directive
|
||||
@ -765,12 +793,11 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource,
|
||||
nsTArray<nsString> reportURIs;
|
||||
mPolicies[aViolatedPolicyIndex]->getReportURIs(reportURIs);
|
||||
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = do_QueryReferent(mLoadingContext);
|
||||
nsCOMPtr<nsIURI> reportURI;
|
||||
nsCOMPtr<nsIChannel> reportChannel;
|
||||
|
||||
nsCOMPtr<nsIDOMNode> loadingContext = do_QueryReferent(mLoadingContext);
|
||||
nsCOMPtr<nsINode> loadingNode = do_QueryInterface(loadingContext);
|
||||
|
||||
for (uint32_t r = 0; r < reportURIs.Length(); r++) {
|
||||
nsAutoCString reportURICstring = NS_ConvertUTF16toUTF8(reportURIs[r]);
|
||||
// try to create a new uri from every report-uri string
|
||||
@ -779,34 +806,24 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource,
|
||||
const char16_t* params[] = { reportURIs[r].get() };
|
||||
CSPCONTEXTLOG(("Could not create nsIURI for report URI %s",
|
||||
reportURICstring.get()));
|
||||
CSP_LogLocalizedStr(MOZ_UTF16("triedToSendReport"),
|
||||
params, ArrayLength(params),
|
||||
aSourceFile, aScriptSample, aLineNum, 0,
|
||||
nsIScriptError::errorFlag, "CSP", mInnerWindowID);
|
||||
logToConsole(MOZ_UTF16("triedToSendReport"), params, ArrayLength(params),
|
||||
aSourceFile, aScriptSample, aLineNum, 0, nsIScriptError::errorFlag);
|
||||
continue; // don't return yet, there may be more URIs
|
||||
}
|
||||
|
||||
nsIDocShell* docShell = nullptr;
|
||||
|
||||
// try to create a new channel for every report-uri
|
||||
if (loadingNode) {
|
||||
nsIDocument* doc = loadingNode->OwnerDoc();
|
||||
if (doc) {
|
||||
docShell = doc->GetDocShell();
|
||||
}
|
||||
if (doc) {
|
||||
rv = NS_NewChannel(getter_AddRefs(reportChannel),
|
||||
reportURI,
|
||||
loadingNode,
|
||||
nsILoadInfo::SEC_NORMAL,
|
||||
doc,
|
||||
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
nsIContentPolicy::TYPE_CSP_REPORT);
|
||||
}
|
||||
else {
|
||||
nsCOMPtr<nsIPrincipal> nullPrincipal = nsNullPrincipal::Create();
|
||||
NS_ENSURE_TRUE(nullPrincipal, NS_ERROR_FAILURE);
|
||||
rv = NS_NewChannel(getter_AddRefs(reportChannel),
|
||||
reportURI,
|
||||
nullPrincipal,
|
||||
nsILoadInfo::SEC_NORMAL,
|
||||
mLoadingPrincipal,
|
||||
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
nsIContentPolicy::TYPE_CSP_REPORT);
|
||||
}
|
||||
|
||||
@ -823,10 +840,8 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource,
|
||||
|
||||
if (!isHttpScheme) {
|
||||
const char16_t* params[] = { reportURIs[r].get() };
|
||||
CSP_LogLocalizedStr(MOZ_UTF16("reportURInotHttpsOrHttp2"),
|
||||
params, ArrayLength(params),
|
||||
aSourceFile, aScriptSample, aLineNum, 0,
|
||||
nsIScriptError::errorFlag, "CSP", mInnerWindowID);
|
||||
logToConsole(MOZ_UTF16("reportURInotHttpsOrHttp2"), params, ArrayLength(params),
|
||||
aSourceFile, aScriptSample, aLineNum, 0, nsIScriptError::errorFlag);
|
||||
}
|
||||
|
||||
// make sure this is an anonymous request (no cookies) so in case the
|
||||
@ -841,8 +856,9 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource,
|
||||
// we need to set an nsIChannelEventSink on the channel object
|
||||
// so we can tell it to not follow redirects when posting the reports
|
||||
RefPtr<CSPReportRedirectSink> reportSink = new CSPReportRedirectSink();
|
||||
if (docShell) {
|
||||
nsCOMPtr<nsINetworkInterceptController> interceptController = do_QueryInterface(docShell);
|
||||
if (doc && doc->GetDocShell()) {
|
||||
nsCOMPtr<nsINetworkInterceptController> interceptController =
|
||||
do_QueryInterface(doc->GetDocShell());
|
||||
reportSink->SetInterceptController(interceptController);
|
||||
}
|
||||
reportChannel->SetNotificationCallbacks(reportSink);
|
||||
@ -853,32 +869,6 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource,
|
||||
rv = reportChannel->SetLoadGroup(mCallingChannelLoadGroup);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// check content policy
|
||||
int16_t shouldLoad = nsIContentPolicy::ACCEPT;
|
||||
nsCOMPtr<nsIContentPolicy> cp = do_GetService(NS_CONTENTPOLICY_CONTRACTID);
|
||||
if (!cp) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
rv = cp->ShouldLoad(nsIContentPolicy::TYPE_CSP_REPORT,
|
||||
reportURI,
|
||||
mSelfURI,
|
||||
nullptr, // Context
|
||||
EmptyCString(), // mime type
|
||||
nullptr, // Extra parameter
|
||||
nullptr, // optional request principal
|
||||
&shouldLoad);
|
||||
|
||||
// refuse to load if we can't do a security check
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (NS_CP_REJECTED(shouldLoad)) {
|
||||
// skip unauthorized URIs
|
||||
CSPCONTEXTLOG(("nsIContentPolicy blocked sending report to %s",
|
||||
reportURICstring.get()));
|
||||
continue; // don't return yet, there may be more URIs
|
||||
}
|
||||
|
||||
// wire in the string input stream to send the report
|
||||
nsCOMPtr<nsIStringInputStream> sis(do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID));
|
||||
NS_ASSERTION(sis, "nsIStringInputStream is needed but not available to send CSP violation reports");
|
||||
@ -897,7 +887,7 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource,
|
||||
}
|
||||
|
||||
RefPtr<CSPViolationReportListener> listener = new CSPViolationReportListener();
|
||||
rv = reportChannel->AsyncOpen(listener, nullptr);
|
||||
rv = reportChannel->AsyncOpen2(listener);
|
||||
|
||||
// AsyncOpen should not fail, but could if there's no load group (like if
|
||||
// SetRequestContext is not given a channel). This should fail quietly and
|
||||
@ -907,10 +897,8 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource,
|
||||
if (NS_FAILED(rv)) {
|
||||
const char16_t* params[] = { reportURIs[r].get() };
|
||||
CSPCONTEXTLOG(("AsyncOpen failed for report URI %s", params[0]));
|
||||
CSP_LogLocalizedStr(MOZ_UTF16("triedToSendReport"),
|
||||
params, ArrayLength(params),
|
||||
aSourceFile, aScriptSample, aLineNum, 0,
|
||||
nsIScriptError::errorFlag, "CSP", mInnerWindowID);
|
||||
logToConsole(MOZ_UTF16("triedToSendReport"), params, ArrayLength(params),
|
||||
aSourceFile, aScriptSample, aLineNum, 0, nsIScriptError::errorFlag);
|
||||
} else {
|
||||
CSPCONTEXTLOG(("Sent violation report to URI %s", reportURICstring.get()));
|
||||
}
|
||||
@ -933,7 +921,6 @@ class CSPReportSenderRunnable final : public nsRunnable
|
||||
const nsAString& aSourceFile,
|
||||
const nsAString& aScriptSample,
|
||||
uint32_t aLineNum,
|
||||
uint64_t aInnerWindowID,
|
||||
nsCSPContext* aCSPContext)
|
||||
: mBlockedContentSource(aBlockedContentSource)
|
||||
, mOriginalURI(aOriginalURI)
|
||||
@ -943,7 +930,6 @@ class CSPReportSenderRunnable final : public nsRunnable
|
||||
, mSourceFile(aSourceFile)
|
||||
, mScriptSample(aScriptSample)
|
||||
, mLineNum(aLineNum)
|
||||
, mInnerWindowID(aInnerWindowID)
|
||||
, mCSPContext(aCSPContext)
|
||||
{
|
||||
NS_ASSERTION(!aViolatedDirective.IsEmpty(), "Can not send reports without a violated directive");
|
||||
@ -996,12 +982,10 @@ class CSPReportSenderRunnable final : public nsRunnable
|
||||
nsString blockedDataChar16 = NS_ConvertUTF8toUTF16(blockedDataStr);
|
||||
const char16_t* params[] = { mViolatedDirective.get(),
|
||||
blockedDataChar16.get() };
|
||||
|
||||
CSP_LogLocalizedStr(mReportOnlyFlag ? MOZ_UTF16("CSPROViolationWithURI") :
|
||||
MOZ_UTF16("CSPViolationWithURI"),
|
||||
params, ArrayLength(params),
|
||||
mSourceFile, mScriptSample, mLineNum, 0,
|
||||
nsIScriptError::errorFlag, "CSP", mInnerWindowID);
|
||||
mCSPContext->logToConsole(mReportOnlyFlag ? MOZ_UTF16("CSPROViolationWithURI") :
|
||||
MOZ_UTF16("CSPViolationWithURI"),
|
||||
params, ArrayLength(params), mSourceFile, mScriptSample,
|
||||
mLineNum, 0, nsIScriptError::errorFlag);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
@ -1016,8 +1000,7 @@ class CSPReportSenderRunnable final : public nsRunnable
|
||||
nsString mSourceFile;
|
||||
nsString mScriptSample;
|
||||
uint32_t mLineNum;
|
||||
uint64_t mInnerWindowID;
|
||||
RefPtr<nsCSPContext> mCSPContext;
|
||||
RefPtr<nsCSPContext> mCSPContext;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1066,7 +1049,6 @@ nsCSPContext::AsyncReportViolation(nsISupports* aBlockedContentSource,
|
||||
aSourceFile,
|
||||
aScriptSample,
|
||||
aLineNum,
|
||||
mInnerWindowID,
|
||||
this));
|
||||
return NS_OK;
|
||||
}
|
||||
@ -1374,7 +1356,7 @@ nsCSPContext::Read(nsIObjectInputStream* aStream)
|
||||
nsCSPPolicy* policy = nsCSPParser::parseContentSecurityPolicy(policyString,
|
||||
mSelfURI,
|
||||
reportOnly,
|
||||
mInnerWindowID);
|
||||
this);
|
||||
if (policy) {
|
||||
mPolicies.AppendElement(policy);
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
{ 0xbf, 0xe0, 0x27, 0xce, 0xb9, 0x23, 0xd9, 0xac } }
|
||||
|
||||
class nsINetworkInterceptController;
|
||||
struct ConsoleMsgQueueElem;
|
||||
|
||||
class nsCSPContext : public nsIContentSecurityPolicy
|
||||
{
|
||||
@ -40,6 +41,22 @@ class nsCSPContext : public nsIContentSecurityPolicy
|
||||
public:
|
||||
nsCSPContext();
|
||||
|
||||
/**
|
||||
* SetRequestContext() needs to be called before the innerWindowID
|
||||
* is initialized on the document. Use this function to call back to
|
||||
* flush queued up console messages and initalize the innerWindowID.
|
||||
*/
|
||||
void flushConsoleMessages();
|
||||
|
||||
void logToConsole(const char16_t* aName,
|
||||
const char16_t** aParams,
|
||||
uint32_t aParamsLength,
|
||||
const nsAString& aSourceName,
|
||||
const nsAString& aSourceLine,
|
||||
uint32_t aLineNumber,
|
||||
uint32_t aColumnNumber,
|
||||
uint32_t aSeverityFlag);
|
||||
|
||||
nsresult SendReports(nsISupports* aBlockedContentSource,
|
||||
nsIURI* aOriginalURI,
|
||||
nsAString& aViolatedDirective,
|
||||
@ -57,6 +74,13 @@ class nsCSPContext : public nsIContentSecurityPolicy
|
||||
const nsAString& aScriptSample,
|
||||
uint32_t aLineNum);
|
||||
|
||||
// Hands off! Don't call this method unless you know what you
|
||||
// are doing. It's only supposed to be called from within
|
||||
// the principal destructor to avoid a tangling pointer.
|
||||
void clearLoadingPrincipal() {
|
||||
mLoadingPrincipal = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
bool permitsInternal(CSPDirective aDir,
|
||||
nsIURI* aContentLocation,
|
||||
@ -76,13 +100,22 @@ class nsCSPContext : public nsIContentSecurityPolicy
|
||||
uint32_t aViolatedPolicyIndex,
|
||||
uint32_t aLineNumber);
|
||||
|
||||
nsCOMPtr<nsIURI> mReferrer;
|
||||
nsString mReferrer;
|
||||
uint64_t mInnerWindowID; // used for web console logging
|
||||
nsTArray<nsCSPPolicy*> mPolicies;
|
||||
nsCOMPtr<nsIURI> mSelfURI;
|
||||
nsDataHashtable<nsCStringHashKey, int16_t> mShouldLoadCache;
|
||||
nsCOMPtr<nsILoadGroup> mCallingChannelLoadGroup;
|
||||
nsWeakPtr mLoadingContext;
|
||||
// The CSP hangs off the principal, so let's store a raw pointer of the principal
|
||||
// to avoid memory leaks. Within the destructor of the principal we explicitly
|
||||
// set mLoadingPrincipal to null.
|
||||
nsIPrincipal* mLoadingPrincipal;
|
||||
|
||||
// helper members used to queue up web console messages till
|
||||
// the windowID becomes available. see flushConsoleMessages()
|
||||
nsTArray<ConsoleMsgQueueElem> mConsoleMsgQueue;
|
||||
bool mQueueUpMessages;
|
||||
};
|
||||
|
||||
// Class that listens to violation report transmission and logs errors.
|
||||
|
@ -122,14 +122,14 @@ nsCSPTokenizer::tokenizeCSPPolicy(const nsAString &aPolicyString,
|
||||
|
||||
nsCSPParser::nsCSPParser(cspTokens& aTokens,
|
||||
nsIURI* aSelfURI,
|
||||
uint64_t aInnerWindowID)
|
||||
nsCSPContext* aCSPContext)
|
||||
: mHasHashOrNonce(false)
|
||||
, mUnsafeInlineKeywordSrc(nullptr)
|
||||
, mChildSrc(nullptr)
|
||||
, mFrameSrc(nullptr)
|
||||
, mTokens(aTokens)
|
||||
, mSelfURI(aSelfURI)
|
||||
, mInnerWindowID(aInnerWindowID)
|
||||
, mCSPContext(aCSPContext)
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPParser::nsCSPParser"));
|
||||
}
|
||||
@ -296,16 +296,16 @@ nsCSPParser::logWarningErrorToConsole(uint32_t aSeverityFlag,
|
||||
uint32_t aParamsLength)
|
||||
{
|
||||
CSPPARSERLOG(("nsCSPParser::logWarningErrorToConsole: %s", aProperty));
|
||||
|
||||
nsXPIDLString logMsg;
|
||||
CSP_GetLocalizedStr(NS_ConvertUTF8toUTF16(aProperty).get(),
|
||||
aParams,
|
||||
aParamsLength,
|
||||
getter_Copies(logMsg));
|
||||
|
||||
CSP_LogMessage(logMsg, EmptyString(), EmptyString(),
|
||||
0, 0, aSeverityFlag,
|
||||
"CSP", mInnerWindowID);
|
||||
// send console messages off to the context and let the context
|
||||
// deal with it (potentially messages need to be queued up)
|
||||
mCSPContext->logToConsole(NS_ConvertUTF8toUTF16(aProperty).get(),
|
||||
aParams,
|
||||
aParamsLength,
|
||||
EmptyString(), // aSourceName
|
||||
EmptyString(), // aSourceLine
|
||||
0, // aLineNumber
|
||||
0, // aColumnNumber
|
||||
aSeverityFlag); // aFlags
|
||||
}
|
||||
|
||||
bool
|
||||
@ -1116,7 +1116,7 @@ nsCSPPolicy*
|
||||
nsCSPParser::parseContentSecurityPolicy(const nsAString& aPolicyString,
|
||||
nsIURI *aSelfURI,
|
||||
bool aReportOnly,
|
||||
uint64_t aInnerWindowID)
|
||||
nsCSPContext* aCSPContext)
|
||||
{
|
||||
if (CSPPARSERLOGENABLED()) {
|
||||
CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, policy: %s",
|
||||
@ -1138,7 +1138,7 @@ nsCSPParser::parseContentSecurityPolicy(const nsAString& aPolicyString,
|
||||
nsTArray< nsTArray<nsString> > tokens;
|
||||
nsCSPTokenizer::tokenizeCSPPolicy(aPolicyString, tokens);
|
||||
|
||||
nsCSPParser parser(tokens, aSelfURI, aInnerWindowID);
|
||||
nsCSPParser parser(tokens, aSelfURI, aCSPContext);
|
||||
|
||||
// Start the parser to generate a new CSPPolicy using the generated tokens.
|
||||
nsCSPPolicy* policy = parser.policy();
|
||||
|
@ -100,12 +100,12 @@ class nsCSPParser {
|
||||
static nsCSPPolicy* parseContentSecurityPolicy(const nsAString &aPolicyString,
|
||||
nsIURI *aSelfURI,
|
||||
bool aReportOnly,
|
||||
uint64_t aInnerWindowID);
|
||||
nsCSPContext* aCSPContext);
|
||||
|
||||
private:
|
||||
nsCSPParser(cspTokens& aTokens,
|
||||
nsIURI* aSelfURI,
|
||||
uint64_t aInnerWindowID);
|
||||
nsCSPContext* aCSPContext);
|
||||
~nsCSPParser();
|
||||
|
||||
|
||||
@ -245,7 +245,7 @@ class nsCSPParser {
|
||||
cspTokens mTokens;
|
||||
nsIURI* mSelfURI;
|
||||
nsCSPPolicy* mPolicy;
|
||||
uint64_t mInnerWindowID; // used for console reporting
|
||||
nsCSPContext* mCSPContext; // used for console logging
|
||||
};
|
||||
|
||||
#endif /* nsCSPParser_h___ */
|
||||
|
@ -237,12 +237,17 @@ DoContentSecurityChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo)
|
||||
break;
|
||||
}
|
||||
|
||||
case nsIContentPolicy::TYPE_WEBSOCKET:
|
||||
case nsIContentPolicy::TYPE_CSP_REPORT: {
|
||||
case nsIContentPolicy::TYPE_WEBSOCKET: {
|
||||
MOZ_ASSERT(false, "contentPolicyType not supported yet");
|
||||
break;
|
||||
}
|
||||
|
||||
case nsIContentPolicy::TYPE_CSP_REPORT: {
|
||||
mimeTypeGuess = EmptyCString();
|
||||
requestingContext = aLoadInfo->LoadingNode();
|
||||
break;
|
||||
}
|
||||
|
||||
case nsIContentPolicy::TYPE_XSLT: {
|
||||
mimeTypeGuess = NS_LITERAL_CSTRING("application/xml");
|
||||
requestingContext = aLoadInfo->LoadingNode();
|
||||
@ -420,7 +425,8 @@ nsContentSecurityManager::IsURIPotentiallyTrustworthy(nsIURI* aURI, bool* aIsTru
|
||||
|
||||
if (scheme.EqualsLiteral("https") ||
|
||||
scheme.EqualsLiteral("file") ||
|
||||
scheme.EqualsLiteral("app")) {
|
||||
scheme.EqualsLiteral("app") ||
|
||||
scheme.EqualsLiteral("wss")) {
|
||||
*aIsTrustWorthy = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -90,26 +90,18 @@ nsresult runTest(uint32_t aExpectedPolicyCount, // this should be 0 for policies
|
||||
const char* aPolicy,
|
||||
const char* aExpextedResult) {
|
||||
|
||||
// we init the csp with http://www.selfuri.com
|
||||
nsCOMPtr<nsIURI> selfURI;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(selfURI), "http://www.selfuri.com");
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIScriptSecurityManager> secman =
|
||||
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsIPrincipal> systemPrincipal;
|
||||
rv = secman->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
|
||||
|
||||
// we init the csp with http://www.selfuri.com
|
||||
nsCOMPtr<nsIURI> selfURI;
|
||||
rv = NS_NewURI(getter_AddRefs(selfURI), "http://www.selfuri.com");
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// we also init the csp with a dummyChannel, which is unused
|
||||
// for the parser tests but surpresses assertions in SetRequestContext.
|
||||
nsCOMPtr<nsIChannel> dummyChannel;
|
||||
rv = NS_NewChannel(getter_AddRefs(dummyChannel),
|
||||
selfURI,
|
||||
systemPrincipal,
|
||||
nsILoadInfo::SEC_NORMAL,
|
||||
nsIContentPolicy::TYPE_OTHER);
|
||||
nsCOMPtr<nsIPrincipal> selfURIPrincipal;
|
||||
rv = secman->GetSimpleCodebasePrincipal(selfURI, getter_AddRefs(selfURIPrincipal));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// create a CSP object
|
||||
@ -117,12 +109,9 @@ nsresult runTest(uint32_t aExpectedPolicyCount, // this should be 0 for policies
|
||||
do_CreateInstance(NS_CSPCONTEXT_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// for testing the parser we only need to set selfURI which is needed
|
||||
// to translate the keyword 'self' into an actual URI. All other
|
||||
// arguments can be nullptrs.
|
||||
csp->SetRequestContext(selfURI,
|
||||
nullptr, // nsIURI* aReferrer
|
||||
dummyChannel);
|
||||
// for testing the parser we only need to set a principal which is needed
|
||||
// to translate the keyword 'self' into an actual URI.
|
||||
rv = csp->SetRequestContext(nullptr, selfURIPrincipal);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// append a policy
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
var app;
|
||||
var principal;
|
||||
|
||||
function setupTest() {
|
||||
// We have to install an app in order for the app URL to be valid
|
||||
@ -37,21 +38,12 @@
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
// We have to use a mochitest to test app:// urls,
|
||||
// as app channels can't be instanciated in xpcshell.
|
||||
// Because app protocol depends on webapps.jsm,
|
||||
// which doesn't instanciate properly on xpcshell without many hacks
|
||||
let appchan = SpecialPowers.Services.io.newChannel2(gManifestURL,
|
||||
null,
|
||||
null,
|
||||
null, // aLoadingNode
|
||||
SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
SpecialPowers.Ci.nsILoadInfo.SEC_NORMAL,
|
||||
SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
|
||||
try {
|
||||
csp.setRequestContext(null, null, appchan);
|
||||
var secMan = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(SpecialPowers.Ci.nsIScriptSecurityManager);
|
||||
var manifestURI = SpecialPowers.Services.io.newURI(gManifestURL, null, null);
|
||||
principal = secMan.getSimpleCodebasePrincipal(manifestURI);
|
||||
csp.setRequestContext(null, principal);
|
||||
ok(true, "setRequestContext hasn't thown");
|
||||
} catch(e) {
|
||||
ok(false, "setRequestContext throws");
|
||||
|
@ -15,6 +15,8 @@ var httpServer = new HttpServer();
|
||||
httpServer.start(-1);
|
||||
var testsToFinish = 0;
|
||||
|
||||
var principal;
|
||||
|
||||
const REPORT_SERVER_PORT = httpServer.identity.primaryPort;
|
||||
const REPORT_SERVER_URI = "http://localhost";
|
||||
const REPORT_SERVER_PATH = "/report";
|
||||
@ -72,14 +74,13 @@ function makeTest(id, expectedJSON, useReportOnlyPolicy, callback) {
|
||||
var selfuri = NetUtil.newURI(REPORT_SERVER_URI +
|
||||
":" + REPORT_SERVER_PORT +
|
||||
"/foo/self");
|
||||
var selfchan = NetUtil.newChannel({
|
||||
uri: selfuri,
|
||||
loadUsingSystemPrincipal: true});
|
||||
|
||||
dump("Created test " + id + " : " + policy + "\n\n");
|
||||
|
||||
// make the reports seem authentic by "binding" them to a channel.
|
||||
csp.setRequestContext(selfuri, null, selfchan);
|
||||
let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager);
|
||||
principal = ssm.getSimpleCodebasePrincipal(selfuri);
|
||||
csp.setRequestContext(null, principal);
|
||||
|
||||
// Load up the policy
|
||||
// set as report-only if that's the case
|
||||
|
@ -7,15 +7,6 @@
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
dictionary AsyncScrollEventDetail {
|
||||
float top = 0;
|
||||
float left = 0;
|
||||
float width = 0;
|
||||
float height = 0;
|
||||
float scrollWidth = 0;
|
||||
float scrollHeight = 0;
|
||||
};
|
||||
|
||||
dictionary OpenWindowEventDetail {
|
||||
DOMString url = "";
|
||||
DOMString name = "";
|
||||
|
@ -530,11 +530,11 @@ struct BaseRect {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamp this rectangle to be inside aRect. The function returns a copy of
|
||||
* this rect after it is forced inside the bounds of aRect. It will attempt to
|
||||
* retain the size but will shrink the dimensions that don't fit.
|
||||
* Translate this rectangle to be inside aRect. If it doesn't fit inside
|
||||
* aRect then the dimensions that don't fit will be shrunk so that they
|
||||
* do fit. The resulting rect is returned.
|
||||
*/
|
||||
MOZ_WARN_UNUSED_RESULT Sub ForceInside(const Sub& aRect) const
|
||||
MOZ_WARN_UNUSED_RESULT Sub MoveInsideAndClamp(const Sub& aRect) const
|
||||
{
|
||||
Sub rect(std::max(aRect.x, x),
|
||||
std::max(aRect.y, y),
|
||||
|
@ -1257,6 +1257,7 @@ DrawTargetCairo::Mask(const Pattern &aSource,
|
||||
}
|
||||
|
||||
cairo_set_source(mContext, source);
|
||||
cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
|
||||
cairo_mask(mContext, mask);
|
||||
|
||||
cairo_pattern_destroy(mask);
|
||||
|
@ -229,6 +229,12 @@ IntRectTyped<units> RoundedOut(const RectTyped<units>& aRect)
|
||||
int32_t(copy.height));
|
||||
}
|
||||
|
||||
template<class units>
|
||||
RectTyped<units> IntRectToRect(const IntRectTyped<units>& aRect)
|
||||
{
|
||||
return RectTyped<units>(aRect.x, aRect.y, aRect.width, aRect.height);
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -15,6 +15,8 @@
|
||||
#include "GeckoProfiler.h"
|
||||
#include "mozilla/gfx/MacIOSurface.h"
|
||||
|
||||
#include <OpenGL/OpenGL.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace gl {
|
||||
|
||||
@ -296,6 +298,9 @@ CreateOffscreenFBOContext(CreateContextFlags flags)
|
||||
RefPtr<GLContextCGL> glContext = new GLContextCGL(dummyCaps, context,
|
||||
true, profile);
|
||||
|
||||
if (gfxPrefs::GLMultithreaded()) {
|
||||
CGLEnable(glContext->GetCGLContext(), kCGLCEMPEngine);
|
||||
}
|
||||
return glContext.forget();
|
||||
}
|
||||
|
||||
|
@ -66,11 +66,12 @@ Compositor::DrawDiagnostics(DiagnosticFlags aFlags,
|
||||
while (const gfx::IntRect* rect = screenIter.Next())
|
||||
{
|
||||
DrawDiagnostics(aFlags | DiagnosticFlags::REGION_RECT,
|
||||
ToRect(*rect), aClipRect, aTransform, aFlashCounter);
|
||||
IntRectToRect(*rect), aClipRect, aTransform,
|
||||
aFlashCounter);
|
||||
}
|
||||
}
|
||||
|
||||
DrawDiagnostics(aFlags, ToRect(aVisibleRegion.GetBounds()),
|
||||
DrawDiagnostics(aFlags, IntRectToRect(aVisibleRegion.GetBounds()),
|
||||
aClipRect, aTransform, aFlashCounter);
|
||||
}
|
||||
|
||||
|
@ -76,8 +76,8 @@ static gfxFloat RecoverZDepth(const Matrix4x4& aTransform, const gfxPoint& aPoin
|
||||
* unsolved without changing our rendering code.
|
||||
*/
|
||||
static LayerSortOrder CompareDepth(Layer* aOne, Layer* aTwo) {
|
||||
gfxRect ourRect = aOne->GetEffectiveVisibleRegion().GetBounds();
|
||||
gfxRect otherRect = aTwo->GetEffectiveVisibleRegion().GetBounds();
|
||||
gfxRect ourRect = ThebesRect(aOne->GetEffectiveVisibleRegion().GetBounds());
|
||||
gfxRect otherRect = ThebesRect(aTwo->GetEffectiveVisibleRegion().GetBounds());
|
||||
|
||||
MOZ_ASSERT(aOne->GetParent() && aOne->GetParent()->Extend3DContext() &&
|
||||
aTwo->GetParent() && aTwo->GetParent()->Extend3DContext());
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user