merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2015-11-12 12:00:55 +01:00
commit fde361620f
218 changed files with 2809 additions and 2154 deletions

View File

@ -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

View File

@ -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;
}
/**

View File

@ -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>

View File

@ -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*() {

View File

@ -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();
}
]]>

View File

@ -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)

View File

@ -71,7 +71,6 @@ public:
bool MayLoadInternal(nsIURI* aURI) override;
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIContentSecurityPolicy> mCSP;
};
#endif // nsNullPrincipal_h__

View File

@ -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);

View File

@ -668,6 +668,7 @@ SSL_SetStapledOCSPResponses
SSL_SetURL
SSL_SNISocketConfigHook
SSL_VersionRangeGet
SSL_VersionRangeGetDefault
SSL_VersionRangeGetSupported
SSL_VersionRangeSet
SSL_VersionRangeSetDefault

View File

@ -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

View File

@ -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

View File

@ -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.
*

View File

@ -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);
/**

View File

@ -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.

View File

@ -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]

View File

@ -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();
});
}

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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();
});
}

View File

@ -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();

View File

@ -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);
}

View File

@ -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();
});
}
}

View File

@ -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 */
}
}

View File

@ -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);
}

View File

@ -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()

View File

@ -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) {

View File

@ -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) {

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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,

View File

@ -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).');
};

View File

@ -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>

View File

@ -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]

View File

@ -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]

View File

@ -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 {

View File

@ -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) {

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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();
};

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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)
{

View File

@ -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;

View File

@ -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)
{

View File

@ -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,

View File

@ -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.

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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)
{

View File

@ -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

View File

@ -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!");

View File

@ -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)) {

View File

@ -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.

View File

@ -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,

View File

@ -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);

View File

@ -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;
}

View File

@ -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();
};
},

View File

@ -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

View File

@ -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);
},
};

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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');

View 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');
});

View File

@ -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.

View File

@ -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',

View File

@ -79,7 +79,6 @@ add_task(function* test1() {
PushService.init({
serverURI: serverURL + "/subscribe5xxCode",
service: PushServiceHttp2,
db
});

View File

@ -83,7 +83,6 @@ add_task(function* test1() {
PushService.init({
serverURI: serverURL + "/subscribe",
service: PushServiceHttp2,
db
});

View File

@ -93,7 +93,6 @@ add_task(function* test1() {
PushService.init({
serverURI: serverURL + "/subscribe",
service: PushServiceHttp2,
db
});

View File

@ -88,7 +88,6 @@ add_task(function* test1() {
PushService.init({
serverURI: serverURL + "/subscribe",
service: PushServiceHttp2,
db
});

View File

@ -66,7 +66,6 @@ add_task(function* test1() {
PushService.init({
serverURI: serverURL + "/subscribe",
service: PushServiceHttp2,
db
});

View File

@ -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]

View File

@ -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);
}

View File

@ -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.

View File

@ -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();

View File

@ -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___ */

View File

@ -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;
}

View File

@ -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

View File

@ -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");

View File

@ -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

View File

@ -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 = "";

View File

@ -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),

View File

@ -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);

View File

@ -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

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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