Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2017-04-04 12:37:56 +02:00
commit a0fea1ad49
103 changed files with 5107 additions and 4700 deletions

View File

@ -22,4 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Bug 1351604 - required because DER.jsm and X509.jsm are no longer shipped with the browser
Bug 1351074 - required because bug 1352982 means removing a .jsm requires a clobber

View File

@ -17,7 +17,9 @@ function openContextMenuFor(element, shiftkey, waitForSpellCheck) {
}
if (waitForSpellCheck) {
var { onSpellCheck } = SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm", {});
var { onSpellCheck } =
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm", {});
onSpellCheck(element, actuallyOpenContextMenuFor);
} else {
actuallyOpenContextMenuFor();
@ -278,7 +280,9 @@ function* test_contextmenu(selector, menuItems, options = {}) {
if (options.waitForSpellCheck) {
info("Waiting for spell check");
yield ContentTask.spawn(gBrowser.selectedBrowser, selector, function*(contentSelector) {
let {onSpellCheck} = Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm", {});
let {onSpellCheck} =
Cu.import("resource://testing-common/AsyncSpellCheckTestHelper.jsm",
{});
let element = content.document.querySelector(contentSelector);
yield new Promise(resolve => onSpellCheck(element, resolve));
info("Spell check running");

View File

@ -231,8 +231,6 @@ var whitelist = new Set([
{file: "resource://gre-resources/checkmark.svg"},
{file: "resource://gre-resources/indeterminate-checkmark.svg"},
{file: "resource://gre-resources/radio.svg"},
// Bug 1351074
{file: "resource://gre/modules/AsyncSpellCheckTestHelper.jsm"},
// Bug 1351078
{file: "resource://gre/modules/Battery.jsm"},
// Bug 1351070

View File

@ -39,9 +39,24 @@ let whitelist = [
errorMessage: /Unknown property.*-moz-/i,
isFromDevTools: false},
// Reserved to UA sheets unless layout.css.overflow-clip-box.enabled flipped to true.
{sourceName: /res\/forms\.css$/i,
{sourceName: /(?:res|gre-resources)\/forms\.css$/i,
errorMessage: /Unknown property.*overflow-clip-box/i,
isFromDevTools: false},
// These variables are declared somewhere else, and error when we load the
// files directly. They're all marked intermittent because their appearance
// in the error console seems to not be consistent.
{sourceName: /jsonview\/css\/general\.css$/i,
intermittent: true,
errorMessage: /Property contained reference to invalid variable.*color/i,
isFromDevTools: true},
{sourceName: /webide\/skin\/logs\.css$/i,
intermittent: true,
errorMessage: /Property contained reference to invalid variable.*color/i,
isFromDevTools: true},
{sourceName: /devtools\/skin\/animationinspector\.css$/i,
intermittent: true,
errorMessage: /Property contained reference to invalid variable.*color/i,
isFromDevTools: true},
];
if (!Services.prefs.getBoolPref("full-screen-api.unprefix.enabled")) {
@ -93,20 +108,20 @@ function ignoredError(aErrorObject) {
return false;
}
function once(target, name) {
return new Promise((resolve, reject) => {
let cb = () => {
target.removeEventListener(name, cb);
resolve();
};
target.addEventListener(name, cb);
});
}
var gChromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"]
.getService(Ci.nsIChromeRegistry);
var gChromeMap = new Map();
var resHandler = Services.io.getProtocolHandler("resource")
.QueryInterface(Ci.nsIResProtocolHandler);
var gResourceMap = [];
function trackResourcePrefix(prefix) {
let uri = Services.io.newURI("resource://" + prefix + "/");
gResourceMap.unshift([prefix, resHandler.resolveURI(uri)]);
}
trackResourcePrefix("gre");
trackResourcePrefix("app");
function getBaseUriForChromeUri(chromeUri) {
let chromeFile = chromeUri + "gobbledygooknonexistentfile.reallynothere";
let uri = Services.io.newURI(chromeFile);
@ -118,35 +133,34 @@ function parseManifest(manifestUri) {
return fetchFile(manifestUri.spec).then(data => {
for (let line of data.split("\n")) {
let [type, ...argv] = line.split(/\s+/);
let component;
if (type == "content" || type == "skin") {
[component] = argv;
} else {
// skip unrelated lines
continue;
let chromeUri = `chrome://${argv[0]}/${type}/`;
gChromeMap.set(getBaseUriForChromeUri(chromeUri), chromeUri);
} else if (type == "resource") {
trackResourcePrefix(argv[0]);
}
let chromeUri = `chrome://${component}/${type}/`;
gChromeMap.set(getBaseUriForChromeUri(chromeUri), chromeUri);
}
});
}
function convertToChromeUri(fileUri) {
let baseUri = fileUri.spec;
function convertToCodeURI(fileUri) {
let baseUri = fileUri;
let path = "";
while (true) {
let slashPos = baseUri.lastIndexOf("/", baseUri.length - 2);
if (slashPos < 0) {
info(`File not accessible from chrome protocol: ${fileUri.path}`);
if (slashPos <= 0) {
// File not accessible from chrome protocol, try resource://
for (let res of gResourceMap) {
if (fileUri.startsWith(res[1]))
return fileUri.replace(res[1], "resource://" + res[0] + "/");
}
// Give up and return the original URL.
return fileUri;
}
path = baseUri.slice(slashPos + 1) + path;
baseUri = baseUri.slice(0, slashPos + 1);
if (gChromeMap.has(baseUri)) {
let chromeBaseUri = gChromeMap.get(baseUri);
let chromeUri = `${chromeBaseUri}${path}`;
return Services.io.newURI(chromeUri);
}
if (gChromeMap.has(baseUri))
return gChromeMap.get(baseUri) + path;
}
}
@ -235,10 +249,12 @@ add_task(function* checkAllTheCSS() {
// Create a clean iframe to load all the files into. This needs to live at a
// chrome URI so that it's allowed to load and parse any styles.
let testFile = getRootDirectory(gTestPath) + "dummy_page.html";
let windowless = Services.appShell.createWindowlessBrowser();
let iframe = windowless.document.createElementNS("http://www.w3.org/1999/xhtml", "html:iframe");
windowless.document.documentElement.appendChild(iframe);
let iframeLoaded = once(iframe, "load");
let HiddenFrame = Cu.import("resource:///modules/HiddenFrame.jsm", {}).HiddenFrame;
let hiddenFrame = new HiddenFrame();
let win = yield hiddenFrame.get();
let iframe = win.document.createElementNS("http://www.w3.org/1999/xhtml", "html:iframe");
win.document.documentElement.appendChild(iframe);
let iframeLoaded = BrowserTestUtils.waitForEvent(iframe, "load", true);
iframe.contentWindow.location = testFile;
yield iframeLoaded;
let doc = iframe.contentWindow.document;
@ -285,8 +301,8 @@ add_task(function* checkAllTheCSS() {
linkEl.addEventListener("load", onLoad);
linkEl.addEventListener("error", onError);
linkEl.setAttribute("type", "text/css");
let chromeUri = convertToChromeUri(uri);
linkEl.setAttribute("href", chromeUri.spec + kPathSuffix);
let chromeUri = convertToCodeURI(uri.spec);
linkEl.setAttribute("href", chromeUri + kPathSuffix);
}));
doc.head.appendChild(linkEl);
}
@ -322,7 +338,7 @@ add_task(function* checkAllTheCSS() {
// Confirm that all whitelist rules have been used.
for (let item of whitelist) {
if (!item.used && isDevtools == item.isFromDevTools) {
if (!item.used && isDevtools == item.isFromDevTools && !item.intermittent) {
ok(false, "Unused whitelist item. " +
(item.sourceName ? " sourceName: " + item.sourceName : "") +
(item.errorMessage ? " errorMessage: " + item.errorMessage : ""));
@ -344,7 +360,8 @@ add_task(function* checkAllTheCSS() {
doc.head.innerHTML = "";
doc = null;
iframe = null;
windowless.close();
windowless = null;
win = null;
hiddenFrame.destroy();
hiddenFrame = null;
imageURIsToReferencesMap = null;
});

View File

@ -179,7 +179,7 @@ Bookmarks.prototype = {
return { url, title };
}
return null;
}).filter(e => !!e);
}, this).filter(e => !!e);
},
};

View File

@ -15,11 +15,15 @@ add_task(function* () {
let expectedParents = [ PlacesUtils.toolbarFolderId ];
let itemCount = 0;
let gotFolder = false;
let bmObserver = {
onItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle) {
if (aTitle != label) {
itemCount++;
}
if (aItemType == PlacesUtils.bookmarks.TYPE_FOLDER && aTitle == "Stuff") {
gotFolder = true;
}
if (expectedParents.length > 0 && aTitle == label) {
let index = expectedParents.indexOf(aParentId);
Assert.ok(index != -1, "Found expected parent");
@ -40,6 +44,7 @@ add_task(function* () {
// Check the bookmarks have been imported to all the expected parents.
Assert.ok(!expectedParents.length, "No more expected parents");
Assert.ok(gotFolder, "Should have seen the folder get imported");
Assert.equal(itemCount, 13, "Should import all 13 items.");
// Check that the telemetry matches:
Assert.equal(MigrationUtils._importQuantities.bookmarks, itemCount, "Telemetry reporting correct.");

View File

@ -898,6 +898,23 @@ nsScriptSecurityManager::CheckLoadURIFlags(nsIURI *aSourceURI,
}
}
static bool sCanLoadChromeInContent = false;
static bool sCachedCanLoadChromeInContentPref = false;
if (!sCachedCanLoadChromeInContentPref) {
sCachedCanLoadChromeInContentPref = true;
mozilla::Preferences::AddBoolVarCache(&sCanLoadChromeInContent,
"security.allow_chrome_frames_inside_content");
}
if (sCanLoadChromeInContent) {
// Special-case the hidden window: it's allowed to load
// URI_IS_UI_RESOURCE no matter what. Bug 1145470 tracks removing this.
nsAutoCString sourceSpec;
if (NS_SUCCEEDED(aSourceBaseURI->GetSpec(sourceSpec)) &&
sourceSpec.EqualsLiteral("resource://gre-resources/hiddenWindow.html")) {
return NS_OK;
}
}
if (reportErrors) {
ReportError(nullptr, errorTag, aSourceURI, aTargetURI);
}

View File

@ -293,6 +293,13 @@ GeckoMediaPluginServiceChild::UpdateGMPCapabilities(nsTArray<GMPCapabilityData>&
}
}
void
GeckoMediaPluginServiceChild::BeginShutdown()
{
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
mShuttingDownOnGMPThread = true;
}
NS_IMETHODIMP
GeckoMediaPluginServiceChild::HasPluginForAPI(const nsACString& aAPI,
nsTArray<nsCString>* aTags,
@ -374,6 +381,13 @@ GeckoMediaPluginServiceChild::GetServiceChild()
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
if (!mServiceChild) {
if (mShuttingDownOnGMPThread) {
// We have begun shutdown. Don't allow a new connection to the main
// process to be instantiated. This also prevents new plugins being
// instantiated.
return GetServiceChildPromise::CreateAndReject(NS_ERROR_FAILURE,
__func__);
}
dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
if (!contentChild) {
return GetServiceChildPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
@ -411,6 +425,9 @@ GeckoMediaPluginServiceChild::RemoveGMPContentParent(GMPContentParent* aGMPConte
if (mServiceChild) {
mServiceChild->RemoveGMPContentParent(aGMPContentParent);
if (mShuttingDownOnGMPThread && !mServiceChild->HaveContentParents()) {
mServiceChild = nullptr;
}
}
}
@ -515,5 +532,23 @@ GMPServiceChild::Create(Endpoint<PGMPServiceChild>&& aGMPService)
return NS_SUCCEEDED(rv);
}
ipc::IPCResult
GMPServiceChild::RecvBeginShutdown()
{
RefPtr<GeckoMediaPluginServiceChild> service =
GeckoMediaPluginServiceChild::GetSingleton();
MOZ_ASSERT(service && service->mServiceChild.get() == this);
if (service) {
service->BeginShutdown();
}
return IPC_OK();
}
bool
GMPServiceChild::HaveContentParents() const
{
return mContentParents.Count() > 0;
}
} // namespace gmp
} // namespace mozilla

View File

@ -42,6 +42,8 @@ public:
static void UpdateGMPCapabilities(nsTArray<mozilla::dom::GMPCapabilityData>&& aCapabilities);
void BeginShutdown();
protected:
void InitializePlugins(AbstractThread*) override
{
@ -85,6 +87,10 @@ public:
static bool Create(Endpoint<PGMPServiceChild>&& aGMPService);
ipc::IPCResult RecvBeginShutdown() override;
bool HaveContentParents() const;
private:
nsRefPtrHashtable<nsUint64HashKey, GMPContentParent> mContentParents;
};

View File

@ -93,7 +93,6 @@ GeckoMediaPluginServiceParent::GeckoMediaPluginServiceParent()
, mWaitingForPluginsSyncShutdown(false)
, mInitPromiseMonitor("GeckoMediaPluginServiceParent::mInitPromiseMonitor")
, mLoadPluginsFromDiskComplete(false)
, mServiceUserCount(0)
, mMainThread(SystemGroup::AbstractMainThreadFor(TaskCategory::Other))
{
MOZ_ASSERT(NS_IsMainThread());
@ -469,6 +468,10 @@ GeckoMediaPluginServiceParent::UnloadPlugins()
// Move all plugins references to a local array. This way mMutex won't be
// locked when calling CloseActive (to avoid inter-locking).
Swap(plugins, mPlugins);
for (GMPServiceParent* parent : mServiceParents) {
Unused << parent->SendBeginShutdown();
}
}
LOGD(("%s::%s plugins:%" PRIuSIZE, __CLASS__, __FUNCTION__,
@ -1632,10 +1635,14 @@ GeckoMediaPluginServiceParent::BlockShutdown(nsIAsyncShutdownClient*)
}
void
GeckoMediaPluginServiceParent::ServiceUserCreated()
GeckoMediaPluginServiceParent::ServiceUserCreated(
GMPServiceParent* aServiceParent)
{
MOZ_ASSERT(mServiceUserCount >= 0);
if (++mServiceUserCount == 1) {
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(mMutex);
MOZ_ASSERT(!mServiceParents.Contains(aServiceParent));
mServiceParents.AppendElement(aServiceParent);
if (mServiceParents.Length() == 1) {
nsresult rv = GetShutdownBarrier()->AddBlocker(
this, NS_LITERAL_STRING(__FILE__), __LINE__,
NS_LITERAL_STRING("GeckoMediaPluginServiceParent shutdown"));
@ -1644,10 +1651,15 @@ GeckoMediaPluginServiceParent::ServiceUserCreated()
}
void
GeckoMediaPluginServiceParent::ServiceUserDestroyed()
GeckoMediaPluginServiceParent::ServiceUserDestroyed(
GMPServiceParent* aServiceParent)
{
MOZ_ASSERT(mServiceUserCount > 0);
if (--mServiceUserCount == 0) {
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(mMutex);
MOZ_ASSERT(mServiceParents.Length() > 0);
MOZ_ASSERT(mServiceParents.Contains(aServiceParent));
mServiceParents.RemoveElement(aServiceParent);
if (mServiceParents.IsEmpty()) {
nsresult rv = GetShutdownBarrier()->RemoveBlocker(this);
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
}
@ -1692,12 +1704,17 @@ GeckoMediaPluginServiceParent::GetById(uint32_t aPluginId)
return nullptr;
}
GMPServiceParent::GMPServiceParent(GeckoMediaPluginServiceParent* aService)
: mService(aService)
{
MOZ_ASSERT(mService);
mService->ServiceUserCreated(this);
}
GMPServiceParent::~GMPServiceParent()
{
nsCOMPtr<nsIRunnable> task = NewRunnableMethod(
"GeckoMediaPluginServiceParent::ServiceUserDestroyed",
mService.get(), &GeckoMediaPluginServiceParent::ServiceUserDestroyed);
mService->MainThread()->Dispatch(task.forget());
MOZ_ASSERT(mService);
mService->ServiceUserDestroyed(this);
}
mozilla::ipc::IPCResult
@ -1850,7 +1867,16 @@ GMPServiceParent::ActorDestroy(ActorDestroyReason aWhy)
lock.Wait();
}
NS_DispatchToCurrentThread(new DeleteGMPServiceParent(this));
// Dispatch a task to the current thread to ensure we don't delete the
// GMPServiceParent until the current calling context is finished with
// the object.
GMPServiceParent* self = this;
NS_DispatchToCurrentThread(NS_NewRunnableFunction([self]() {
// The GMPServiceParent must be destroyed on the main thread.
NS_DispatchToMainThread(NS_NewRunnableFunction([self]() {
delete self;
}));
}));
}
class OpenPGMPServiceParent : public mozilla::Runnable

View File

@ -23,6 +23,7 @@ namespace mozilla {
namespace gmp {
class GMPParent;
class GMPServiceParent;
class GeckoMediaPluginServiceParent final : public GeckoMediaPluginService
, public mozIGeckoMediaPluginChromeService
@ -60,8 +61,8 @@ public:
const mozilla::OriginAttributesPattern& aPattern);
// Notifies that some user of this class is created/destroyed.
void ServiceUserCreated();
void ServiceUserDestroyed();
void ServiceUserCreated(GMPServiceParent* aServiceParent);
void ServiceUserDestroyed(GMPServiceParent* aServiceParent);
void UpdateContentProcessGMPCapabilities();
@ -207,9 +208,10 @@ private:
// Hashes nodeId to the hashtable of storage for that nodeId.
nsRefPtrHashtable<nsCStringHashKey, GMPStorage> mTempGMPStorage;
// Tracks how many users are running (on the GMP thread). Only when this count
// drops to 0 can we safely shut down the thread.
MainThreadOnly<int32_t> mServiceUserCount;
// Tracks how many IPC connections to GMPServices running in content
// processes we have. When this is empty we can safely shut down.
// Synchronized across thread via mMutex in base class.
nsTArray<GMPServiceParent*> mServiceParents;
const RefPtr<AbstractThread> mMainThread;
};
@ -222,11 +224,7 @@ bool MatchOrigin(nsIFile* aPath,
class GMPServiceParent final : public PGMPServiceParent
{
public:
explicit GMPServiceParent(GeckoMediaPluginServiceParent* aService)
: mService(aService)
{
mService->ServiceUserCreated();
}
explicit GMPServiceParent(GeckoMediaPluginServiceParent* aService);
virtual ~GMPServiceParent();
ipc::IPCResult RecvGetGMPNodeId(const nsString& aOrigin,

View File

@ -36,6 +36,8 @@ parent:
sync GetGMPNodeId(nsString origin, nsString topLevelOrigin, nsString gmpName)
returns (nsCString id);
child:
async BeginShutdown();
};
} // namespace gmp

View File

@ -24,7 +24,8 @@ function start() {
var textarea = document.getElementById("editor");
textarea.focus();
SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm")
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm")
.onSpellCheck(textarea, function () {
var isc = SpecialPowers.wrap(textarea).editor.getInlineSpellChecker(false);
ok(isc, "Inline spell checker should exist after focus and spell check");

View File

@ -101,7 +101,8 @@ function continueTest(evt) {
editor.setSpellcheckUserOverride(true);
var inlineSpellChecker = editor.getInlineSpellChecker(true);
SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm")
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm")
.onSpellCheck(elem, function () {
var spellchecker = inlineSpellChecker.spellChecker;
try {

View File

@ -67,7 +67,8 @@ var loadListener = function(evt) {
editor.setSpellcheckUserOverride(true);
var inlineSpellChecker = editor.getInlineSpellChecker(true);
SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm")
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm")
.onSpellCheck(elem, function () {
var spellchecker = inlineSpellChecker.spellChecker;
try {

View File

@ -29,8 +29,8 @@ var selcon_de;
var script;
var onSpellCheck =
SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm")
.onSpellCheck;
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm").onSpellCheck;
/** Test for Bug 1205983 **/
SimpleTest.waitForExplicitFinish();

View File

@ -37,8 +37,8 @@ var script;
*/
var onSpellCheck =
SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm")
.onSpellCheck;
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm").onSpellCheck;
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {

View File

@ -29,8 +29,8 @@ var spellchecker;
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm",
window);
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm", window);
var elem = document.getElementById('en-US');
elem.focus();

View File

@ -21,7 +21,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=338427
/** Test for Bug 338427 **/
function init() {
var onSpellCheck =
SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm")
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm")
.onSpellCheck;
var textarea = document.getElementById("editor");
var editor = SpecialPowers.wrap(textarea).editor;

View File

@ -60,7 +60,8 @@ var loadListener = function(evt) {
editor.setSpellcheckUserOverride(true);
var inlineSpellChecker = editor.getInlineSpellChecker(true);
SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm")
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm")
.onSpellCheck(elem, function () {
var spellchecker = inlineSpellChecker.spellChecker;
try {

View File

@ -28,7 +28,8 @@ var editor_de;
var script;
var onSpellCheck =
SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm")
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm")
.onSpellCheck;
/** Test for Bug 697981 **/

View File

@ -62,7 +62,8 @@ var loadListener = function(evt) {
editor.setSpellcheckUserOverride(true);
var inlineSpellChecker = editor.getInlineSpellChecker(true);
SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm")
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm")
.onSpellCheck(elem, function () {
var spellchecker = inlineSpellChecker.spellChecker;
try {

View File

@ -49,7 +49,8 @@ function runTest() {
gMisspeltWords = ["errror", "errror"];
editDoc().designMode = "on";
SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm")
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm")
.onSpellCheck(editDoc().documentElement, evalTest);
}

View File

@ -53,7 +53,8 @@ function runTest() {
var edit = document.getElementById("edit");
edit.focus();
SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm", window);
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm", window);
onSpellCheck(edit, function () {
ok(isSpellingCheckOk(getEditor(), gMisspeltWords),
"All misspellings before editing are accounted for.");

View File

@ -112,7 +112,8 @@ function runTest()
var edit = document.getElementById("edit");
edit.focus();
SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm", window);
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm", window);
onSpellCheck(edit, runOnFocus);
}
</script>

View File

@ -11,8 +11,8 @@
SimpleTest.waitForExplicitFinish();
function runTest() {
SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm",
window);
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm", window);
var x = document.getElementById("x");
x.focus();
onSpellCheck(x, function () {

View File

@ -38,7 +38,7 @@ EXPORTS += [
'nsEditorCID.h',
]
EXTRA_JS_MODULES += [
TESTING_JS_MODULES += [
'AsyncSpellCheckTestHelper.jsm',
]

View File

@ -67,7 +67,8 @@ function RunTest() {
ok(map.exists());
hunspell.addDirectory(map);
Components.utils.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm");
Components.utils.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm");
onSpellCheck(textbox, function () {
// test that base and map dictionaries are available

View File

@ -13,7 +13,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1170484
/** Test for Bug 1170484 **/
SimpleTest.waitForExplicitFinish();
SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm", window);
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm", window);
SimpleTest.waitForFocus(doTest, window);
function doTest() {

View File

@ -54,7 +54,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1272623
}
add_task(function* () {
SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm", window);
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm", window);
// Wait for the page to be ready
yield new Promise(resolve => SimpleTest.waitForFocus(() => SimpleTest.executeSoon(resolve), window));

View File

@ -852,7 +852,7 @@ ProtectPages(void* p, size_t size)
#if defined(XP_WIN)
DWORD oldProtect;
if (!VirtualProtect(p, size, PAGE_NOACCESS, &oldProtect)) {
MOZ_CRASH_UNSAFE_PRINTF("VirtualProtect(PAGE_NOACCESS) failed! Error code: %u",
MOZ_CRASH_UNSAFE_PRINTF("VirtualProtect(PAGE_NOACCESS) failed! Error code: %lu",
GetLastError());
}
MOZ_ASSERT(oldProtect == PAGE_READWRITE);
@ -871,7 +871,7 @@ MakePagesReadOnly(void* p, size_t size)
#if defined(XP_WIN)
DWORD oldProtect;
if (!VirtualProtect(p, size, PAGE_READONLY, &oldProtect)) {
MOZ_CRASH_UNSAFE_PRINTF("VirtualProtect(PAGE_READONLY) failed! Error code: %u",
MOZ_CRASH_UNSAFE_PRINTF("VirtualProtect(PAGE_READONLY) failed! Error code: %lu",
GetLastError());
}
MOZ_ASSERT(oldProtect == PAGE_READWRITE);
@ -890,7 +890,7 @@ UnprotectPages(void* p, size_t size)
#if defined(XP_WIN)
DWORD oldProtect;
if (!VirtualProtect(p, size, PAGE_READWRITE, &oldProtect)) {
MOZ_CRASH_UNSAFE_PRINTF("VirtualProtect(PAGE_READWRITE) failed! Error code: %u",
MOZ_CRASH_UNSAFE_PRINTF("VirtualProtect(PAGE_READWRITE) failed! Error code: %lu",
GetLastError());
}
MOZ_ASSERT(oldProtect == PAGE_NOACCESS || oldProtect == PAGE_READONLY);

View File

@ -1961,6 +1961,7 @@ IonBuilder::inspectOpcode(JSOp op)
return jsop_newobject();
case JSOP_NEWARRAY:
case JSOP_SPREADCALLARRAY:
return jsop_newarray(GET_UINT32(pc));
case JSOP_NEWARRAY_COPYONWRITE:
@ -2012,6 +2013,9 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_FUNAPPLY:
return jsop_funapply(GET_ARGC(pc));
case JSOP_SPREADCALL:
return jsop_spreadcall();
case JSOP_CALL:
case JSOP_CALL_IGNORES_RV:
case JSOP_CALLITER:
@ -5050,6 +5054,45 @@ IonBuilder::jsop_funapply(uint32_t argc)
return jsop_funapplyarguments(argc);
}
AbortReasonOr<Ok>
IonBuilder::jsop_spreadcall()
{
// The arguments array is constructed by a JSOP_SPREADCALLARRAY and not
// leaked to user. The complications of spread call iterator behaviour are
// handled when the user objects are expanded and copied into this hidden
// array.
#ifdef DEBUG
// If we know class, ensure it is what we expected
MDefinition* argument = current->peek(-1);
if (TemporaryTypeSet* objTypes = argument->resultTypeSet())
if (const Class* clasp = objTypes->getKnownClass(constraints()))
MOZ_ASSERT(clasp == &ArrayObject::class_);
#endif
MDefinition* argArr = current->pop();
MDefinition* argThis = current->pop();
MDefinition* argFunc = current->pop();
// Extract call target.
TemporaryTypeSet* funTypes = argFunc->resultTypeSet();
JSFunction* target = getSingleCallTarget(funTypes);
WrappedFunction* wrappedTarget = target ? new(alloc()) WrappedFunction(target) : nullptr;
// Dense elements of argument array
MElements* elements = MElements::New(alloc(), argArr);
current->add(elements);
MApplyArray* apply = MApplyArray::New(alloc(), wrappedTarget, argFunc, elements, argThis);
current->add(apply);
current->push(apply);
MOZ_TRY(resumeAfter(apply));
// TypeBarrier the call result
TemporaryTypeSet* types = bytecodeTypes(pc);
return pushTypeBarrier(apply, types, BarrierKind::TypeSet);
}
AbortReasonOr<Ok>
IonBuilder::jsop_funapplyarray(uint32_t argc)
{

View File

@ -504,6 +504,7 @@ class IonBuilder
AbortReasonOr<Ok> jsop_funapply(uint32_t argc);
AbortReasonOr<Ok> jsop_funapplyarguments(uint32_t argc);
AbortReasonOr<Ok> jsop_funapplyarray(uint32_t argc);
AbortReasonOr<Ok> jsop_spreadcall();
AbortReasonOr<Ok> jsop_call(uint32_t argc, bool constructing, bool ignoresReturnValue);
AbortReasonOr<Ok> jsop_eval(uint32_t argc);
AbortReasonOr<Ok> jsop_label();

View File

@ -12,7 +12,8 @@ x
var p = document.getElementById('p');
var div = p.parentNode;
div.focus();
SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm", window);
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm", window);
onSpellCheck(div, function () {
var sel = window.getSelection();
sel.removeAllRanges();

View File

@ -19,7 +19,8 @@ x
sel.addRange(range);
p.parentNode.focus();
SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm", window);
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm", window);
onSpellCheck(p.parentNode, function () {
sendKey('DOWN'); // now after "1"
sendKey('DOWN'); // now make sure we get to the end

View File

@ -12,7 +12,8 @@ x
var p = document.getElementById('p');
var div = p.parentNode;
div.focus();
SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm", window);
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm", window);
onSpellCheck(div, function () {
var sel = window.getSelection();
sel.removeAllRanges();

View File

@ -19,7 +19,8 @@ x
sel.addRange(range);
p.parentNode.focus();
SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm", window);
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm", window);
onSpellCheck(p.parentNode, function () {
sendKey('DOWN'); // now after "1"
sendKey('DOWN'); // now below the P element

View File

@ -3,8 +3,8 @@
<script src="/tests/SimpleTest/EventUtils.js"></script>
<script>
document.body.firstChild.focus();
SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm",
window);
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm", window);
onSpellCheck(document.body.firstChild, function() {
document.documentElement.removeAttribute("class");
});

View File

@ -4,8 +4,8 @@
<script>
var div = document.body.firstChild;
div.focus();
SpecialPowers.Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm",
window);
SpecialPowers.Cu.import(
"resource://testing-common/AsyncSpellCheckTestHelper.jsm", window);
onSpellCheck(div, function() {
div.innerHTML = 'something missspelled<br>something elsed#';
onSpellCheck(div, function() {

View File

@ -1,50 +1,50 @@
# DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
# Tests for static position of absolutely positioned flex children.
fails == flex-abspos-staticpos-align-content-001.html flex-abspos-staticpos-align-content-001.html
fails == flex-abspos-staticpos-align-content-002.html flex-abspos-staticpos-align-content-002.html
fails == flex-abspos-staticpos-align-content-003.html flex-abspos-staticpos-align-content-003.html
fails == flex-abspos-staticpos-align-content-004.html flex-abspos-staticpos-align-content-004.html
fails == flex-abspos-staticpos-align-content-005.html flex-abspos-staticpos-align-content-005.html
fails == flex-abspos-staticpos-align-content-006.html flex-abspos-staticpos-align-content-006.html
fails == flex-abspos-staticpos-align-content-007.html flex-abspos-staticpos-align-content-007.html
fails == flex-abspos-staticpos-align-content-008.html flex-abspos-staticpos-align-content-008.html
fails == flex-abspos-staticpos-align-content-rtl-001.html flex-abspos-staticpos-align-content-rtl-001.html
fails == flex-abspos-staticpos-align-content-rtl-002.html flex-abspos-staticpos-align-content-rtl-002.html
fails == flex-abspos-staticpos-align-content-vertWM-001.html flex-abspos-staticpos-align-content-vertWM-001.html
fails == flex-abspos-staticpos-align-content-vertWM-002.html flex-abspos-staticpos-align-content-vertWM-002.html
== flex-abspos-staticpos-align-content-001.html flex-abspos-staticpos-align-content-001.html
== flex-abspos-staticpos-align-content-002.html flex-abspos-staticpos-align-content-002.html
== flex-abspos-staticpos-align-content-003.html flex-abspos-staticpos-align-content-003.html
== flex-abspos-staticpos-align-content-004.html flex-abspos-staticpos-align-content-004.html
== flex-abspos-staticpos-align-content-005.html flex-abspos-staticpos-align-content-005.html
== flex-abspos-staticpos-align-content-006.html flex-abspos-staticpos-align-content-006.html
== flex-abspos-staticpos-align-content-007.html flex-abspos-staticpos-align-content-007.html
== flex-abspos-staticpos-align-content-008.html flex-abspos-staticpos-align-content-008.html
== flex-abspos-staticpos-align-content-rtl-001.html flex-abspos-staticpos-align-content-rtl-001.html
== flex-abspos-staticpos-align-content-rtl-002.html flex-abspos-staticpos-align-content-rtl-002.html
== flex-abspos-staticpos-align-content-vertWM-001.html flex-abspos-staticpos-align-content-vertWM-001.html
== flex-abspos-staticpos-align-content-vertWM-002.html flex-abspos-staticpos-align-content-vertWM-002.html
fails == flex-abspos-staticpos-align-self-001.html flex-abspos-staticpos-align-self-001.html
fails == flex-abspos-staticpos-align-self-002.html flex-abspos-staticpos-align-self-002.html
fails == flex-abspos-staticpos-align-self-003.html flex-abspos-staticpos-align-self-003.html
fails == flex-abspos-staticpos-align-self-004.html flex-abspos-staticpos-align-self-004.html
fails == flex-abspos-staticpos-align-self-005.html flex-abspos-staticpos-align-self-005.html
fails == flex-abspos-staticpos-align-self-006.html flex-abspos-staticpos-align-self-006.html
fails == flex-abspos-staticpos-align-self-007.html flex-abspos-staticpos-align-self-007.html
fails == flex-abspos-staticpos-align-self-008.html flex-abspos-staticpos-align-self-008.html
fails == flex-abspos-staticpos-align-self-rtl-001.html flex-abspos-staticpos-align-self-rtl-001.html
fails == flex-abspos-staticpos-align-self-rtl-002.html flex-abspos-staticpos-align-self-rtl-002.html
fails == flex-abspos-staticpos-align-self-rtl-003.html flex-abspos-staticpos-align-self-rtl-003.html
fails == flex-abspos-staticpos-align-self-rtl-004.html flex-abspos-staticpos-align-self-rtl-004.html
fails == flex-abspos-staticpos-align-self-vertWM-001.html flex-abspos-staticpos-align-self-vertWM-001.html
fails == flex-abspos-staticpos-align-self-vertWM-002.html flex-abspos-staticpos-align-self-vertWM-002.html
fails == flex-abspos-staticpos-align-self-vertWM-003.html flex-abspos-staticpos-align-self-vertWM-003.html
fails == flex-abspos-staticpos-align-self-vertWM-004.html flex-abspos-staticpos-align-self-vertWM-004.html
== flex-abspos-staticpos-align-self-001.html flex-abspos-staticpos-align-self-001.html
== flex-abspos-staticpos-align-self-002.html flex-abspos-staticpos-align-self-002.html
== flex-abspos-staticpos-align-self-003.html flex-abspos-staticpos-align-self-003.html
== flex-abspos-staticpos-align-self-004.html flex-abspos-staticpos-align-self-004.html
== flex-abspos-staticpos-align-self-005.html flex-abspos-staticpos-align-self-005.html
== flex-abspos-staticpos-align-self-006.html flex-abspos-staticpos-align-self-006.html
== flex-abspos-staticpos-align-self-007.html flex-abspos-staticpos-align-self-007.html
== flex-abspos-staticpos-align-self-008.html flex-abspos-staticpos-align-self-008.html
== flex-abspos-staticpos-align-self-rtl-001.html flex-abspos-staticpos-align-self-rtl-001.html
== flex-abspos-staticpos-align-self-rtl-002.html flex-abspos-staticpos-align-self-rtl-002.html
== flex-abspos-staticpos-align-self-rtl-003.html flex-abspos-staticpos-align-self-rtl-003.html
== flex-abspos-staticpos-align-self-rtl-004.html flex-abspos-staticpos-align-self-rtl-004.html
== flex-abspos-staticpos-align-self-vertWM-001.html flex-abspos-staticpos-align-self-vertWM-001.html
== flex-abspos-staticpos-align-self-vertWM-002.html flex-abspos-staticpos-align-self-vertWM-002.html
== flex-abspos-staticpos-align-self-vertWM-003.html flex-abspos-staticpos-align-self-vertWM-003.html
== flex-abspos-staticpos-align-self-vertWM-004.html flex-abspos-staticpos-align-self-vertWM-004.html
== flex-abspos-staticpos-fallback-align-content-001.html flex-abspos-staticpos-fallback-align-content-001.html
== flex-abspos-staticpos-fallback-justify-content-001.html flex-abspos-staticpos-fallback-justify-content-001.html
fails == flex-abspos-staticpos-justify-content-001.html flex-abspos-staticpos-justify-content-001.html
fails == flex-abspos-staticpos-justify-content-002.html flex-abspos-staticpos-justify-content-002.html
== flex-abspos-staticpos-justify-content-001.html flex-abspos-staticpos-justify-content-001.html
== flex-abspos-staticpos-justify-content-002.html flex-abspos-staticpos-justify-content-002.html
== flex-abspos-staticpos-justify-content-003.html flex-abspos-staticpos-justify-content-003.html
== flex-abspos-staticpos-justify-content-004.html flex-abspos-staticpos-justify-content-004.html
fails == flex-abspos-staticpos-justify-content-005.html flex-abspos-staticpos-justify-content-005.html
fails == flex-abspos-staticpos-justify-content-006.html flex-abspos-staticpos-justify-content-006.html
== flex-abspos-staticpos-justify-content-005.html flex-abspos-staticpos-justify-content-005.html
== flex-abspos-staticpos-justify-content-006.html flex-abspos-staticpos-justify-content-006.html
== flex-abspos-staticpos-justify-content-007.html flex-abspos-staticpos-justify-content-007.html
== flex-abspos-staticpos-justify-content-008.html flex-abspos-staticpos-justify-content-008.html
fails == flex-abspos-staticpos-justify-content-rtl-001.html flex-abspos-staticpos-justify-content-rtl-001.html
fails == flex-abspos-staticpos-justify-content-rtl-002.html flex-abspos-staticpos-justify-content-rtl-002.html
fails == flex-abspos-staticpos-justify-content-vertWM-001.html flex-abspos-staticpos-justify-content-vertWM-001.html
fails == flex-abspos-staticpos-justify-content-vertWM-002.html flex-abspos-staticpos-justify-content-vertWM-002.html
== flex-abspos-staticpos-justify-content-rtl-001.html flex-abspos-staticpos-justify-content-rtl-001.html
== flex-abspos-staticpos-justify-content-rtl-002.html flex-abspos-staticpos-justify-content-rtl-002.html
== flex-abspos-staticpos-justify-content-vertWM-001.html flex-abspos-staticpos-justify-content-vertWM-001.html
== flex-abspos-staticpos-justify-content-vertWM-002.html flex-abspos-staticpos-justify-content-vertWM-002.html
== flex-abspos-staticpos-justify-self-001.html flex-abspos-staticpos-justify-self-001.html
@ -52,27 +52,27 @@ fails == flex-abspos-staticpos-justify-content-vertWM-002.html flex-abspos-stati
== flex-abspos-staticpos-margin-002.html flex-abspos-staticpos-margin-002.html
fails == grid-abspos-staticpos-align-self-001.html grid-abspos-staticpos-align-self-001.html
fails == grid-abspos-staticpos-align-self-002.html grid-abspos-staticpos-align-self-002.html
== grid-abspos-staticpos-align-self-002.html grid-abspos-staticpos-align-self-002.html
fails == grid-abspos-staticpos-align-self-img-001.html grid-abspos-staticpos-align-self-img-001.html
fails == grid-abspos-staticpos-align-self-img-002.html grid-abspos-staticpos-align-self-img-002.html
== grid-abspos-staticpos-align-self-img-002.html grid-abspos-staticpos-align-self-img-002.html
fails == grid-abspos-staticpos-align-self-rtl-001.html grid-abspos-staticpos-align-self-rtl-001.html
fails == grid-abspos-staticpos-align-self-rtl-002.html grid-abspos-staticpos-align-self-rtl-002.html
fails == grid-abspos-staticpos-align-self-rtl-003.html grid-abspos-staticpos-align-self-rtl-003.html
fails == grid-abspos-staticpos-align-self-rtl-004.html grid-abspos-staticpos-align-self-rtl-004.html
== grid-abspos-staticpos-align-self-rtl-003.html grid-abspos-staticpos-align-self-rtl-003.html
== grid-abspos-staticpos-align-self-rtl-004.html grid-abspos-staticpos-align-self-rtl-004.html
fails == grid-abspos-staticpos-align-self-vertWM-001.html grid-abspos-staticpos-align-self-vertWM-001.html
fails == grid-abspos-staticpos-align-self-vertWM-002.html grid-abspos-staticpos-align-self-vertWM-002.html
fails == grid-abspos-staticpos-align-self-vertWM-003.html grid-abspos-staticpos-align-self-vertWM-003.html
fails == grid-abspos-staticpos-align-self-vertWM-004.html grid-abspos-staticpos-align-self-vertWM-004.html
== grid-abspos-staticpos-align-self-vertWM-003.html grid-abspos-staticpos-align-self-vertWM-003.html
== grid-abspos-staticpos-align-self-vertWM-004.html grid-abspos-staticpos-align-self-vertWM-004.html
fails == grid-abspos-staticpos-justify-self-001.html grid-abspos-staticpos-justify-self-001.html
fails == grid-abspos-staticpos-justify-self-002.html grid-abspos-staticpos-justify-self-002.html
== grid-abspos-staticpos-justify-self-002.html grid-abspos-staticpos-justify-self-002.html
fails == grid-abspos-staticpos-justify-self-img-001.html grid-abspos-staticpos-justify-self-img-001.html
fails == grid-abspos-staticpos-justify-self-img-002.html grid-abspos-staticpos-justify-self-img-002.html
== grid-abspos-staticpos-justify-self-img-002.html grid-abspos-staticpos-justify-self-img-002.html
fails == grid-abspos-staticpos-justify-self-rtl-001.html grid-abspos-staticpos-justify-self-rtl-001.html
fails == grid-abspos-staticpos-justify-self-rtl-002.html grid-abspos-staticpos-justify-self-rtl-002.html
fails == grid-abspos-staticpos-justify-self-rtl-003.html grid-abspos-staticpos-justify-self-rtl-003.html
fails == grid-abspos-staticpos-justify-self-rtl-004.html grid-abspos-staticpos-justify-self-rtl-004.html
== grid-abspos-staticpos-justify-self-rtl-003.html grid-abspos-staticpos-justify-self-rtl-003.html
== grid-abspos-staticpos-justify-self-rtl-004.html grid-abspos-staticpos-justify-self-rtl-004.html
fails == grid-abspos-staticpos-justify-self-vertWM-001.html grid-abspos-staticpos-justify-self-vertWM-001.html
fails == grid-abspos-staticpos-justify-self-vertWM-002.html grid-abspos-staticpos-justify-self-vertWM-002.html
fails == grid-abspos-staticpos-justify-self-vertWM-003.html grid-abspos-staticpos-justify-self-vertWM-003.html
fails == grid-abspos-staticpos-justify-self-vertWM-004.html grid-abspos-staticpos-justify-self-vertWM-004.html
== grid-abspos-staticpos-justify-self-vertWM-003.html grid-abspos-staticpos-justify-self-vertWM-003.html
== grid-abspos-staticpos-justify-self-vertWM-004.html grid-abspos-staticpos-justify-self-vertWM-004.html

View File

@ -13,14 +13,14 @@
== flexbox-align-content-vert-001b.xhtml flexbox-align-content-vert-001b.xhtml
# Tests for cross-axis alignment (align-self / align-items properties)
fails == flexbox-align-self-baseline-horiz-001a.xhtml flexbox-align-self-baseline-horiz-001a.xhtml
fails == flexbox-align-self-baseline-horiz-001b.xhtml flexbox-align-self-baseline-horiz-001b.xhtml
== flexbox-align-self-baseline-horiz-001a.xhtml flexbox-align-self-baseline-horiz-001a.xhtml
== flexbox-align-self-baseline-horiz-001b.xhtml flexbox-align-self-baseline-horiz-001b.xhtml
== flexbox-align-self-baseline-horiz-002.xhtml flexbox-align-self-baseline-horiz-002.xhtml
== flexbox-align-self-baseline-horiz-003.xhtml flexbox-align-self-baseline-horiz-003.xhtml
== flexbox-align-self-baseline-horiz-004.xhtml flexbox-align-self-baseline-horiz-004.xhtml
== flexbox-align-self-baseline-horiz-005.xhtml flexbox-align-self-baseline-horiz-005.xhtml
fails == flexbox-align-self-baseline-horiz-006.xhtml flexbox-align-self-baseline-horiz-006.xhtml
fails == flexbox-align-self-baseline-horiz-007.xhtml flexbox-align-self-baseline-horiz-007.xhtml
== flexbox-align-self-baseline-horiz-006.xhtml flexbox-align-self-baseline-horiz-006.xhtml
== flexbox-align-self-baseline-horiz-007.xhtml flexbox-align-self-baseline-horiz-007.xhtml
== flexbox-align-self-stretch-vert-001.html flexbox-align-self-stretch-vert-001.html
== flexbox-align-self-stretch-vert-002.html flexbox-align-self-stretch-vert-002.html
@ -145,7 +145,7 @@ fails == flexbox-basic-textarea-vert-001.xhtml flexbox-basic-textarea-vert-001.x
# Tests for min-height:auto / min-width:auto on flex items
== flexbox-min-height-auto-001.html flexbox-min-height-auto-001.html
== flexbox-min-height-auto-002a.html flexbox-min-height-auto-002a.html
== flexbox-min-height-auto-002b.html flexbox-min-height-auto-002b.html
fails == flexbox-min-height-auto-002b.html flexbox-min-height-auto-002-ref.html # bug 1055354
== flexbox-min-height-auto-002c.html flexbox-min-height-auto-002c.html
fails == flexbox-min-height-auto-003.html flexbox-min-height-auto-003.html
fails == flexbox-min-height-auto-004.html flexbox-min-height-auto-004.html

View File

@ -244,8 +244,8 @@ to mochitest command.
* ... `justify-` [3]
* test_initial_storage.html `align-` [6]
* ... `justify-` [6]
* test_value_storage.html `align-` [45]
* ... `justify-` [34]
* test_value_storage.html `align-` [9]
* ... `justify-` [14]
* @page support
* test_bug887741_at-rules_in_declaration_lists.html [1]
* test_page_parser.html [30]

View File

@ -1,6 +1,7 @@
reftest.jar:
% content reftest %content/
content/reftest-content.js (reftest-content.js)
content/AsyncSpellCheckTestHelper.jsm (../../../editor/AsyncSpellCheckTestHelper.jsm)
content/httpd.jsm (../../../netwerk/test/httpserver/httpd.js)
content/StructuredLog.jsm (../../../testing/modules/StructuredLog.jsm)
* content/reftest.jsm (reftest.jsm)

View File

@ -21,7 +21,7 @@ const NS_GFXINFO_CONTRACTID = "@mozilla.org/gfx/info;1";
const BLANK_URL_FOR_CLEARING = "data:text/html;charset=UTF-8,%3C%21%2D%2DCLEAR%2D%2D%3E";
CU.import("resource://gre/modules/Timer.jsm");
CU.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm");
CU.import("chrome://reftest/content/AsyncSpellCheckTestHelper.jsm");
var gBrowserIsRemote;
var gIsWebRenderEnabled;

View File

@ -97,7 +97,9 @@ int __android_log_write(int prio, const char *tag, const char *text);
* Send a formatted string to the log, used like printf(fmt,...)
*/
int __android_log_print(int prio, const char *tag, const char *fmt, ...)
#if defined(__GNUC__)
#if defined(__MINGW32__)
__attribute__ ((format(__MINGW_PRINTF_FORMAT, 3, 4)))
#elif defined(__GNUC__)
__attribute__ ((format(printf, 3, 4)))
#endif
;

View File

@ -64,7 +64,12 @@ public:
static inline const String8 empty();
static String8 format(const char* fmt, ...) __attribute__((format (printf, 1, 2)));
static String8 format(const char* fmt, ...)
#ifdef __MINGW32__
__attribute__((format (__MINGW_PRINTF_FORMAT, 1, 2)));
#else
__attribute__((format (printf, 1, 2)));
#endif
static String8 formatV(const char* fmt, va_list args);
inline const char* string() const;

View File

@ -17,6 +17,7 @@ namespace mozilla {
namespace net {
StunAddrsRequestParent::StunAddrsRequestParent()
: mIPCClosed(false)
{
NS_GetMainThread(getter_AddRefs(mMainThread));
@ -30,17 +31,34 @@ StunAddrsRequestParent::RecvGetStunAddrs()
{
ASSERT_ON_THREAD(mMainThread);
if (mIPCClosed) {
return IPC_OK();
}
RUN_ON_THREAD(mSTSThread,
WrapRunnable(this, &StunAddrsRequestParent::GetStunAddrs_s),
WrapRunnable(RefPtr<StunAddrsRequestParent>(this),
&StunAddrsRequestParent::GetStunAddrs_s),
NS_DISPATCH_NORMAL);
return IPC_OK();
}
mozilla::ipc::IPCResult
StunAddrsRequestParent::Recv__delete__()
{
// see note below in ActorDestroy
mIPCClosed = true;
return IPC_OK();
}
void
StunAddrsRequestParent::ActorDestroy(ActorDestroyReason why)
{
// nothing to do here
// We may still have refcount>0 if we haven't made it through
// GetStunAddrs_s and SendStunAddrs_m yet, but child process
// has crashed. We must not send any more msgs to child, or
// IPDL will kill chrome process, too.
mIPCClosed = true;
}
void
@ -51,9 +69,13 @@ StunAddrsRequestParent::GetStunAddrs_s()
// get the stun addresses while on STS thread
NrIceStunAddrArray addrs = NrIceCtx::GetStunAddrs();
if (mIPCClosed) {
return;
}
// in order to return the result over IPC, we need to be on main thread
RUN_ON_THREAD(mMainThread,
WrapRunnable(this,
WrapRunnable(RefPtr<StunAddrsRequestParent>(this),
&StunAddrsRequestParent::SendStunAddrs_m,
std::move(addrs)),
NS_DISPATCH_NORMAL);
@ -64,6 +86,12 @@ StunAddrsRequestParent::SendStunAddrs_m(const NrIceStunAddrArray& addrs)
{
ASSERT_ON_THREAD(mMainThread);
if (mIPCClosed) {
// nothing to do: child probably crashed
return;
}
mIPCClosed = true;
// send the new addresses back to the child
Unused << SendOnStunAddrsAvailable(addrs);
}

View File

@ -18,6 +18,8 @@ public:
NS_IMETHOD_(MozExternalRefCountType) AddRef();
NS_IMETHOD_(MozExternalRefCountType) Release();
mozilla::ipc::IPCResult Recv__delete__() override;
protected:
virtual ~StunAddrsRequestParent() {}
@ -32,6 +34,9 @@ protected:
ThreadSafeAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD
private:
bool mIPCClosed; // true if IPDL channel has been closed (child crash)
};
} // namespace net

View File

@ -631,8 +631,16 @@
* printf-likes, and in particular this should not be used for
* PR_snprintf and friends, which are "printf-like" but which assign
* different meanings to the various formats.
*
* MinGW requires special handling due to different format specifiers
* on different platforms. The macro __MINGW_PRINTF_FORMAT maps to
* either gnu_printf or ms_printf depending on where we are compiling
* to avoid warnings on format specifiers that are legal.
*/
#ifdef __GNUC__
#ifdef __MINGW32__
#define MOZ_FORMAT_PRINTF(stringIndex, firstToCheck) \
__attribute__ ((format (__MINGW_PRINTF_FORMAT, stringIndex, firstToCheck)))
#elif __GNUC__
#define MOZ_FORMAT_PRINTF(stringIndex, firstToCheck) \
__attribute__ ((format (printf, stringIndex, firstToCheck)))
#else

View File

@ -294,7 +294,7 @@ LoginManagerPrompter.prototype = {
const buttonFlags = Ci.nsIPrompt.STD_YES_NO_BUTTONS;
var usernames = logins.map(l => l.username);
var dialogText = this._getLocalizedString("userSelectText");
var dialogText = this._getLocalizedString("userSelectText2");
var dialogTitle = this._getLocalizedString("passwordChangeTitle");
var selectedIndex = { value: null };

View File

@ -13,7 +13,7 @@ updatePasswordNoUser=Update saved password for this login?
updateButton=Update
dontUpdateButton=Don't update
userSelectText=Please confirm which user you are changing the password for
userSelectText2=Select which login to update:
passwordChangeTitle=Confirm Password Change
# Strings used by PromptService.js

View File

@ -2362,6 +2362,10 @@ pref("security.cert_pinning.process_headers_from_non_builtin_roots", false);
// their protocol with the inner URI of the view-source URI
pref("security.view-source.reachable-from-inner-protocol", false);
// If set to true, in some limited circumstances it may be possible to load
// privileged content in frames inside unprivileged content.
pref("security.allow_chrome_frames_inside_content", false);
// Services security settings
pref("services.settings.server", "https://firefox.settings.services.mozilla.com/v1");

View File

@ -6,7 +6,7 @@
#include "nsAutoPtr.h"
#include "nsIObserverService.h"
#include "nsIUUIDGenerator.h"
#include "nsIXULRuntime.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "RequestContextService.h"
@ -26,12 +26,11 @@ public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIREQUESTCONTEXT
explicit RequestContext(const nsID& id);
explicit RequestContext(const uint64_t id);
private:
virtual ~RequestContext();
nsID mID;
char mCID[NSID_LENGTH];
uint64_t mID;
Atomic<uint32_t> mBlockingTransactionCount;
nsAutoPtr<SpdyPushCache> mSpdyCache;
nsCString mUserAgentOverride;
@ -39,11 +38,10 @@ private:
NS_IMPL_ISUPPORTS(RequestContext, nsIRequestContext)
RequestContext::RequestContext(const nsID& aID)
: mBlockingTransactionCount(0)
RequestContext::RequestContext(const uint64_t aID)
: mID(aID)
, mBlockingTransactionCount(0)
{
mID = aID;
mID.ToProvidedString(mCID);
}
RequestContext::~RequestContext()
@ -89,7 +87,7 @@ RequestContext::SetSpdyPushCache(mozilla::net::SpdyPushCache *aSpdyPushCache)
}
NS_IMETHODIMP
RequestContext::GetID(nsID *outval)
RequestContext::GetID(uint64_t *outval)
{
NS_ENSURE_ARG_POINTER(outval);
*outval = mID;
@ -117,10 +115,14 @@ RequestContextService *RequestContextService::sSelf = nullptr;
NS_IMPL_ISUPPORTS(RequestContextService, nsIRequestContextService, nsIObserver)
RequestContextService::RequestContextService()
: mNextRCID(1)
{
MOZ_ASSERT(!sSelf, "multiple rcs instances!");
MOZ_ASSERT(NS_IsMainThread());
sSelf = this;
nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
runtime->GetProcessID(&mRCIDNamespace);
}
RequestContextService::~RequestContextService()
@ -165,7 +167,7 @@ RequestContextService::Create(nsISupports *aOuter, const nsIID& aIID, void **aRe
}
NS_IMETHODIMP
RequestContextService::GetRequestContext(const nsID& rcID, nsIRequestContext **rc)
RequestContextService::GetRequestContext(const uint64_t rcID, nsIRequestContext **rc)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_ARG_POINTER(rc);
@ -187,15 +189,7 @@ RequestContextService::NewRequestContext(nsIRequestContext **rc)
NS_ENSURE_ARG_POINTER(rc);
*rc = nullptr;
nsresult rv;
if (!mUUIDGen) {
mUUIDGen = do_GetService("@mozilla.org/uuid-generator;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
}
nsID rcID;
rv = mUUIDGen->GenerateUUIDInPlace(&rcID);
NS_ENSURE_SUCCESS(rv, rv);
uint64_t rcID = ((static_cast<uint64_t>(mRCIDNamespace) << 32) & 0xFFFFFFFF00000000LL) | mNextRCID++;
nsCOMPtr<nsIRequestContext> newSC = new RequestContext(rcID);
mTable.Put(rcID, newSC);
@ -205,7 +199,7 @@ RequestContextService::NewRequestContext(nsIRequestContext **rc)
}
NS_IMETHODIMP
RequestContextService::RemoveRequestContext(const nsID& rcID)
RequestContextService::RemoveRequestContext(const uint64_t rcID)
{
MOZ_ASSERT(NS_IsMainThread());
mTable.Remove(rcID);

View File

@ -12,8 +12,6 @@
#include "nsIObserver.h"
#include "nsIRequestContext.h"
class nsIUUIDGenerator;
namespace mozilla {
namespace net {
@ -37,8 +35,9 @@ private:
static RequestContextService *sSelf;
nsInterfaceHashtable<nsIDHashKey, nsIRequestContext> mTable;
nsCOMPtr<nsIUUIDGenerator> mUUIDGen;
nsInterfaceHashtable<nsUint64HashKey, nsIRequestContext> mTable;
uint32_t mRCIDNamespace;
uint32_t mNextRCID;
};
} // ::mozilla::net

View File

@ -81,7 +81,7 @@ interface nsILoadGroup : nsIRequest
* Context for managing things like js/css connection blocking,
* and per-tab connection grouping.
*/
[noscript] readonly attribute nsID requestContextID;
[noscript] readonly attribute unsigned long long requestContextID;
/**
* The set of load flags that will be added to all new requests added to

View File

@ -30,7 +30,7 @@ interface nsIRequestContext : nsISupports
/**
* A unique identifier for this request context
*/
[noscript] readonly attribute nsID ID;
[noscript] readonly attribute unsigned long long ID;
/**
* Number of active blocking transactions associated with this context
@ -81,7 +81,7 @@ interface nsIRequestContextService : nsISupports
/**
* Get an existing request context from its ID
*/
nsIRequestContext getRequestContext(in nsIDRef id);
nsIRequestContext getRequestContext(in unsigned long long id);
/**
* Create a new request context
@ -91,5 +91,5 @@ interface nsIRequestContextService : nsISupports
/**
* Remove an existing request context from its ID
*/
void removeRequestContext(in nsIDRef id);
void removeRequestContext(in unsigned long long id);
};

View File

@ -127,17 +127,11 @@ nsLoadGroup::~nsLoadGroup()
mDefaultLoadRequest = nullptr;
if (mRequestContext) {
nsID rcid;
uint64_t rcid;
mRequestContext->GetID(&rcid);
if (IsNeckoChild() && gNeckoChild) {
char rcid_str[NSID_LENGTH];
rcid.ToProvidedString(rcid_str);
nsCString rcid_nscs;
rcid_nscs.AssignASCII(rcid_str);
gNeckoChild->SendRemoveRequestContext(rcid_nscs);
gNeckoChild->SendRemoveRequestContext(rcid);
} else {
mRequestContextService->RemoveRequestContext(rcid);
}
@ -703,7 +697,7 @@ nsLoadGroup::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
}
NS_IMETHODIMP
nsLoadGroup::GetRequestContextID(nsID *aRCID)
nsLoadGroup::GetRequestContextID(uint64_t *aRCID)
{
if (!mRequestContext) {
return NS_ERROR_NOT_AVAILABLE;

View File

@ -121,7 +121,7 @@ struct HttpChannelOpenArgs
OptionalHttpResponseHead synthesizedResponseHead;
nsCString synthesizedSecurityInfoSerialization;
uint32_t cacheKey;
nsCString requestContextID;
uint64_t requestContextID;
OptionalCorsPreflightArgs preflightArgs;
uint32_t initialRwin;
bool blockAuthPrompt;

View File

@ -908,7 +908,7 @@ NeckoParent::RecvPredReset()
}
mozilla::ipc::IPCResult
NeckoParent::RecvRemoveRequestContext(const nsCString& rcid)
NeckoParent::RecvRemoveRequestContext(const uint64_t& rcid)
{
nsCOMPtr<nsIRequestContextService> rcsvc =
do_GetService("@mozilla.org/network/request-context-service;1");
@ -916,9 +916,7 @@ NeckoParent::RecvRemoveRequestContext(const nsCString& rcid)
return IPC_OK();
}
nsID id;
id.Parse(rcid.BeginReading());
rcsvc->RemoveRequestContext(id);
rcsvc->RemoveRequestContext(rcid);
return IPC_OK();
}

View File

@ -227,7 +227,7 @@ protected:
const OriginAttributes& aOriginAttributes) override;
virtual mozilla::ipc::IPCResult RecvPredReset() override;
virtual mozilla::ipc::IPCResult RecvRemoveRequestContext(const nsCString& rcid) override;
virtual mozilla::ipc::IPCResult RecvRemoveRequestContext(const uint64_t& rcid) override;
/* Throttler messages */
virtual mozilla::ipc::IPCResult RecvIncreaseThrottlePressure() override;

View File

@ -116,7 +116,7 @@ parent:
nsString password, nsString domain);
async OnAuthCancelled(uint64_t callbackId, bool userCancel);
async RemoveRequestContext(nsCString rcid);
async RemoveRequestContext(uint64_t rcid);
async PAltDataOutputStream(nsCString type, PHttpChannel channel);

View File

@ -199,6 +199,7 @@ HttpBaseChannel::HttpBaseChannel()
, mTransferSize(0)
, mDecodedBodySize(0)
, mEncodedBodySize(0)
, mRequestContextID(0)
, mContentWindowId(0)
, mTopLevelOuterContentWindowId(0)
, mRequireCORSPreflight(false)
@ -218,7 +219,6 @@ HttpBaseChannel::HttpBaseChannel()
#endif
mSelfAddr.raw.family = PR_AF_UNSPEC;
mPeerAddr.raw.family = PR_AF_UNSPEC;
mRequestContextID.Clear();
}
HttpBaseChannel::~HttpBaseChannel()
@ -2123,7 +2123,7 @@ HttpBaseChannel::RedirectTo(nsIURI *targetURI)
}
NS_IMETHODIMP
HttpBaseChannel::GetRequestContextID(nsID *aRCID)
HttpBaseChannel::GetRequestContextID(uint64_t *aRCID)
{
NS_ENSURE_ARG_POINTER(aRCID);
*aRCID = mRequestContextID;
@ -2131,7 +2131,7 @@ HttpBaseChannel::GetRequestContextID(nsID *aRCID)
}
NS_IMETHODIMP
HttpBaseChannel::SetRequestContextID(const nsID aRCID)
HttpBaseChannel::SetRequestContextID(uint64_t aRCID)
{
mRequestContextID = aRCID;
return NS_OK;
@ -3824,9 +3824,7 @@ HttpBaseChannel::GetThrottleQueue(nsIInputChannelThrottleQueue** aQueue)
bool
HttpBaseChannel::EnsureRequestContextID()
{
nsID nullID;
nullID.Clear();
if (!mRequestContextID.Equals(nullID)) {
if (mRequestContextID) {
// Already have a request context ID, no need to do the rest of this work
return true;
}

View File

@ -194,11 +194,11 @@ public:
NS_IMETHOD GetResponseStatusText(nsACString& aValue) override;
NS_IMETHOD GetRequestSucceeded(bool *aValue) override;
NS_IMETHOD RedirectTo(nsIURI *newURI) override;
NS_IMETHOD GetRequestContextID(nsID *aRCID) override;
NS_IMETHOD GetRequestContextID(uint64_t *aRCID) override;
NS_IMETHOD GetTransferSize(uint64_t *aTransferSize) override;
NS_IMETHOD GetDecodedBodySize(uint64_t *aDecodedBodySize) override;
NS_IMETHOD GetEncodedBodySize(uint64_t *aEncodedBodySize) override;
NS_IMETHOD SetRequestContextID(const nsID aRCID) override;
NS_IMETHOD SetRequestContextID(uint64_t aRCID) override;
NS_IMETHOD GetIsMainDocumentChannel(bool* aValue) override;
NS_IMETHOD SetIsMainDocumentChannel(bool aValue) override;
NS_IMETHOD GetProtocolVersion(nsACString & aProtocolVersion) override;
@ -587,7 +587,7 @@ protected:
// The network interface id that's associated with this channel.
nsCString mNetworkInterfaceId;
nsID mRequestContextID;
uint64_t mRequestContextID;
bool EnsureRequestContextID();
// ID of the top-level document's inner window this channel is being

View File

@ -2416,9 +2416,7 @@ HttpChannelChild::ContinueAsyncOpen()
NS_ENSURE_SUCCESS(rv, rv);
EnsureRequestContextID();
char rcid[NSID_LENGTH];
mRequestContextID.ToProvidedString(rcid);
openArgs.requestContextID().AssignASCII(rcid);
openArgs.requestContextID() = mRequestContextID;
char chid[NSID_LENGTH];
mChannelId.ToProvidedString(chid);

View File

@ -321,7 +321,7 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
const OptionalHttpResponseHead& aSynthesizedResponseHead,
const nsCString& aSecurityInfoSerialization,
const uint32_t& aCacheKey,
const nsCString& aRequestContextID,
const uint64_t& aRequestContextID,
const OptionalCorsPreflightArgs& aCorsPreflightArgs,
const uint32_t& aInitialRwin,
const bool& aBlockAuthPrompt,
@ -577,9 +577,7 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
}
}
nsID requestContextID;
requestContextID.Parse(aRequestContextID.BeginReading());
mChannel->SetRequestContextID(requestContextID);
mChannel->SetRequestContextID(aRequestContextID);
mSuspendAfterSynthesizeResponse = aSuspendAfterSynthesizeResponse;

View File

@ -139,7 +139,7 @@ protected:
const OptionalHttpResponseHead& aSynthesizedResponseHead,
const nsCString& aSecurityInfoSerialization,
const uint32_t& aCacheKey,
const nsCString& aRequestContextID,
const uint64_t& aRequestContextID,
const OptionalCorsPreflightArgs& aCorsPreflightArgs,
const uint32_t& aInitialRwin,
const bool& aBlockAuthPrompt,

View File

@ -289,13 +289,13 @@ NullHttpChannel::RedirectTo(nsIURI *aNewURI)
}
NS_IMETHODIMP
NullHttpChannel::GetRequestContextID(nsID *_retval)
NullHttpChannel::GetRequestContextID(uint64_t *_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
NullHttpChannel::SetRequestContextID(const nsID rcID)
NullHttpChannel::SetRequestContextID(uint64_t rcID)
{
return NS_ERROR_NOT_IMPLEMENTED;
}

View File

@ -447,7 +447,7 @@ interface nsIHttpChannel : nsIChannel
/**
* Identifies the request context for this load.
*/
[noscript, must_use] attribute nsID requestContextID;
[noscript, must_use] attribute uint64_t requestContextID;
/**
* Unique ID of the channel, shared between parent and child. Needed if

View File

@ -1002,14 +1002,14 @@ nsViewSourceChannel::RedirectTo(nsIURI *uri)
}
NS_IMETHODIMP
nsViewSourceChannel::GetRequestContextID(nsID *_retval)
nsViewSourceChannel::GetRequestContextID(uint64_t *_retval)
{
return !mHttpChannel ? NS_ERROR_NULL_POINTER :
mHttpChannel->GetRequestContextID(_retval);
}
NS_IMETHODIMP
nsViewSourceChannel::SetRequestContextID(const nsID rcid)
nsViewSourceChannel::SetRequestContextID(uint64_t rcid)
{
return !mHttpChannel ? NS_ERROR_NULL_POINTER :
mHttpChannel->SetRequestContextID(rcid);

View File

@ -59,7 +59,7 @@ repository:
[include_toolkit]
type = hg
mozilla = mozilla-central
repo = http://hg.mozilla.org/
repo = https://hg.mozilla.org/
l10n.ini = toolkit/locales/l10n.ini
This tells the l10n pieces where to find the repository, and where inside
@ -188,4 +188,4 @@ repositories are on https://hg.mozilla.org/l10n-central/.
You can search inside our localized files on
`Transvision <https://transvision.mozfr.org/>`_ and
http://dxr.mozilla.org/l10n-mozilla-aurora/.
https://dxr.mozilla.org/l10n-mozilla-aurora/source/.

26
servo/Cargo.lock generated
View File

@ -295,7 +295,7 @@ dependencies = [
"ipc-channel 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
"offscreen_gl_context 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
"offscreen_gl_context 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)",
"servo_config 0.0.1",
"webrender_traits 0.28.0 (git+https://github.com/servo/webrender)",
]
@ -457,7 +457,7 @@ dependencies = [
"msg 0.0.1",
"net 0.0.1",
"net_traits 0.0.1",
"offscreen_gl_context 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
"offscreen_gl_context 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)",
"profile_traits 0.0.1",
"script_traits 0.0.1",
"serde 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1068,7 +1068,7 @@ dependencies = [
"osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"script_traits 0.0.1",
"servo-egl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"servo-glutin 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"servo-glutin 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"servo_config 0.0.1",
"servo_geometry 0.0.1",
"servo_url 0.0.1",
@ -1848,7 +1848,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "offscreen_gl_context"
version = "0.8.4"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cgl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1859,6 +1859,7 @@ dependencies = [
"gleam 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"libloading 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2249,6 +2250,7 @@ dependencies = [
"ipc-channel 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"js 0.1.4 (git+https://github.com/servo/rust-mozjs)",
"jstraceable_derive 0.0.1",
"lazy_static 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2256,7 +2258,7 @@ dependencies = [
"msg 0.0.1",
"net_traits 0.0.1",
"num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
"offscreen_gl_context 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
"offscreen_gl_context 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)",
"open 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2350,7 +2352,7 @@ dependencies = [
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"msg 0.0.1",
"net_traits 0.0.1",
"offscreen_gl_context 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
"offscreen_gl_context 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)",
"profile_traits 0.0.1",
"rustc-serialize 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2473,7 +2475,7 @@ dependencies = [
[[package]]
name = "servo-glutin"
version = "0.10.0"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"android_glue 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2513,7 +2515,7 @@ dependencies = [
"servo-egl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"servo-fontconfig-sys 4.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"servo-freetype-sys 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"servo-glutin 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"servo-glutin 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"x11 2.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -3162,7 +3164,7 @@ dependencies = [
"lazy_static 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
"offscreen_gl_context 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
"offscreen_gl_context 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"threadpool 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
@ -3182,7 +3184,7 @@ dependencies = [
"gleam 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"ipc-channel 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"offscreen_gl_context 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
"offscreen_gl_context 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -3434,7 +3436,7 @@ dependencies = [
"checksum num_cpus 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a18c392466409c50b87369414a2680c93e739aedeb498eb2bff7d7eb569744e2"
"checksum objc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "877f30f37acef6749b1841cceab289707f211aecfc756553cd63976190e6cc2e"
"checksum odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "c3df9b730298cea3a1c3faa90b7e2f9df3a9c400d0936d6015e6165734eefcba"
"checksum offscreen_gl_context 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "21ae15594e1c0b39fd4c79d0062b437904a274bed8b5e363e3e3c89312fd50fd"
"checksum offscreen_gl_context 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ce00d0caf8994f97a07be58dbd1c4d1e05904039d9ddb9286ca6ab144ea570b1"
"checksum ogg 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "013b78ceb7fb82555a2f8a95d8e40866fe64a5d15b83c51b3e1fdd40cd903ed3"
"checksum ogg_metadata 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1753e64956b3afd900f788bf6d2e9d0986df39168be86f4b47ec2058d0c2f7"
"checksum open 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3478ed1686bd1300c8a981a940abc92b06fac9cbef747f4c668d4e032ff7b842"
@ -3480,7 +3482,7 @@ dependencies = [
"checksum servo-fontconfig 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "93f799b649b4a2bf362398910eca35240704c7e765e780349b2bb1070d892262"
"checksum servo-fontconfig-sys 4.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a0af4a4d7746467921486e5c5420f815cc016a6bf5574210d8e9c00f4afae224"
"checksum servo-freetype-sys 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9232032c2e85118c0282c6562c84cab12316e655491ba0a5d1905b2320060d1b"
"checksum servo-glutin 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17f541bd4b5a709d5133349e731379c6d74c3946f3b509d4fa8204f1833f9067"
"checksum servo-glutin 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ca5892a674fb408a09083168c9aa40cd53d4ce1a736542fd8057d36b2e50e612"
"checksum servo-skia 0.30000004.1 (registry+https://github.com/rust-lang/crates.io-index)" = "22ba980da523e91b9d2f7da9fb35f721138a1e604b8d8191e56f403e4760a9e4"
"checksum servo-websocket 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8a1ff13c5d852c2793805226e688044309f2c1d8f063784805a13e99cb75b611"
"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"

View File

@ -114,10 +114,11 @@ fn create_resource_groups(config_dir: Option<&Path>)
ssl_client: ssl_client.clone(),
connector: create_http_connector(ssl_client.clone()),
};
let private_ssl_client = create_ssl_client("certs");
let private_resource_group = ResourceGroup {
http_state: Arc::new(HttpState::new()),
ssl_client: ssl_client.clone(),
connector: create_http_connector(ssl_client),
ssl_client: private_ssl_client.clone(),
connector: create_http_connector(private_ssl_client),
};
(resource_group, private_resource_group)
}

View File

@ -54,6 +54,7 @@ image = "0.12"
ipc-channel = "0.7"
js = {git = "https://github.com/servo/rust-mozjs", features = ["promises"]}
jstraceable_derive = {path = "../jstraceable_derive"}
lazy_static = "0.2"
libc = "0.2"
log = "0.3.5"
mime = "0.2.1"

View File

@ -2132,7 +2132,21 @@ impl Document {
scripts: Default::default(),
anchors: Default::default(),
applets: Default::default(),
style_shared_lock: StyleSharedRwLock::new(),
style_shared_lock: {
lazy_static! {
/// Per-process shared lock for author-origin stylesheets
///
/// FIXME: make it per-document or per-pipeline instead:
/// https://github.com/servo/servo/issues/16027
/// (Need to figure out what to do with the style attribute
/// of elements adopted into another document.)
static ref PER_PROCESS_AUTHOR_SHARED_LOCK: StyleSharedRwLock = {
StyleSharedRwLock::new()
};
}
PER_PROCESS_AUTHOR_SHARED_LOCK.clone()
//StyleSharedRwLock::new()
},
stylesheets: DOMRefCell::new(None),
stylesheets_changed_since_reflow: Cell::new(false),
stylesheet_list: MutNullableJS::new(None),

View File

@ -60,6 +60,8 @@ extern crate ipc_channel;
extern crate js;
#[macro_use]
extern crate jstraceable_derive;
#[macro_use]
extern crate lazy_static;
extern crate libc;
#[macro_use]
extern crate log;

View File

@ -339,7 +339,7 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
.unwrap()
.borrow()
.styles().pseudos
.contains_key(&PseudoElement::Before) {
.has(&PseudoElement::Before) {
Some(self.with_pseudo(PseudoElementType::Before(None)))
} else {
None
@ -352,7 +352,7 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
.unwrap()
.borrow()
.styles().pseudos
.contains_key(&PseudoElement::After) {
.has(&PseudoElement::After) {
Some(self.with_pseudo(PseudoElementType::After(None)))
} else {
None
@ -397,31 +397,29 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
// Precompute non-eagerly-cascaded pseudo-element styles if not
// cached before.
let style_pseudo = other.style_pseudo_element();
let mut data = self.get_style_data().unwrap().borrow_mut();
match style_pseudo.cascade_type() {
// Already computed during the cascade.
PseudoElementCascadeType::Eager => {},
PseudoElementCascadeType::Eager => {
data.styles().pseudos.get(&style_pseudo)
.unwrap().values().clone()
},
PseudoElementCascadeType::Precomputed => {
if !self.get_style_data()
.unwrap()
.borrow()
.styles().pseudos.contains_key(&style_pseudo) {
let mut data = self.get_style_data().unwrap().borrow_mut();
if !data.styles().cached_pseudos.contains_key(&style_pseudo) {
let new_style =
context.stylist.precomputed_values_for_pseudo(
&context.guards,
&style_pseudo,
Some(data.styles().primary.values()),
CascadeFlags::empty());
data.styles_mut().pseudos
data.styles_mut().cached_pseudos
.insert(style_pseudo.clone(), new_style);
}
data.styles().cached_pseudos.get(&style_pseudo)
.unwrap().values().clone()
}
PseudoElementCascadeType::Lazy => {
if !self.get_style_data()
.unwrap()
.borrow()
.styles().pseudos.contains_key(&style_pseudo) {
let mut data = self.get_style_data().unwrap().borrow_mut();
if !data.styles().cached_pseudos.contains_key(&style_pseudo) {
let new_style =
context.stylist
.lazily_compute_pseudo_element_style(
@ -429,15 +427,13 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
unsafe { &self.unsafe_get() },
&style_pseudo,
data.styles().primary.values());
data.styles_mut().pseudos
data.styles_mut().cached_pseudos
.insert(style_pseudo.clone(), new_style.unwrap());
}
data.styles().cached_pseudos.get(&style_pseudo)
.unwrap().values().clone()
}
}
self.get_style_data().unwrap().borrow()
.styles().pseudos.get(&style_pseudo)
.unwrap().values().clone()
}
}
}

View File

@ -11,11 +11,11 @@ use properties::ComputedValues;
use properties::longhands::display::computed_value as display;
use restyle_hints::{RESTYLE_CSS_ANIMATIONS, RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint};
use rule_tree::StrongRuleNode;
use selector_parser::{PseudoElement, RestyleDamage, Snapshot};
use std::collections::HashMap;
use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage, Snapshot};
#[cfg(feature = "servo")] use std::collections::HashMap;
use std::fmt;
use std::hash::BuildHasherDefault;
use std::ops::{Deref, DerefMut};
#[cfg(feature = "servo")] use std::hash::BuildHasherDefault;
use std::ops::Deref;
use std::sync::Arc;
use stylist::Stylist;
use thread_state;
@ -73,33 +73,87 @@ impl fmt::Debug for ComputedStyle {
}
}
type PseudoStylesInner = HashMap<PseudoElement, ComputedStyle,
BuildHasherDefault<::fnv::FnvHasher>>;
/// A set of styles for a given element's pseudo-elements.
///
/// This is a map from pseudo-element to `ComputedStyle`.
///
/// TODO(emilio): This should probably be a small array by default instead of a
/// full-blown `HashMap`.
/// A list of styles for eagerly-cascaded pseudo-elements. Lazily-allocated.
#[derive(Clone, Debug)]
pub struct PseudoStyles(PseudoStylesInner);
pub struct EagerPseudoStyles(Option<Box<[Option<ComputedStyle>]>>);
impl PseudoStyles {
/// Construct an empty set of `PseudoStyles`.
pub fn empty() -> Self {
PseudoStyles(HashMap::with_hasher(Default::default()))
impl EagerPseudoStyles {
/// Returns whether there are any pseudo styles.
pub fn is_empty(&self) -> bool {
self.0.is_some()
}
/// Returns a reference to the style for a given eager pseudo, if it exists.
pub fn get(&self, pseudo: &PseudoElement) -> Option<&ComputedStyle> {
debug_assert!(pseudo.is_eager());
self.0.as_ref().and_then(|p| p[pseudo.eager_index()].as_ref())
}
/// Returns a mutable reference to the style for a given eager pseudo, if it exists.
pub fn get_mut(&mut self, pseudo: &PseudoElement) -> Option<&mut ComputedStyle> {
debug_assert!(pseudo.is_eager());
self.0.as_mut().and_then(|p| p[pseudo.eager_index()].as_mut())
}
/// Returns true if the EagerPseudoStyles has a ComputedStyle for |pseudo|.
pub fn has(&self, pseudo: &PseudoElement) -> bool {
self.get(pseudo).is_some()
}
/// Inserts a pseudo-element. The pseudo-element must not already exist.
pub fn insert(&mut self, pseudo: &PseudoElement, style: ComputedStyle) {
debug_assert!(!self.has(pseudo));
if self.0.is_none() {
self.0 = Some(vec![None; EAGER_PSEUDO_COUNT].into_boxed_slice());
}
self.0.as_mut().unwrap()[pseudo.eager_index()] = Some(style);
}
/// Removes a pseudo-element style if it exists, and returns it.
pub fn take(&mut self, pseudo: &PseudoElement) -> Option<ComputedStyle> {
let result = match self.0.as_mut() {
None => return None,
Some(arr) => arr[pseudo.eager_index()].take(),
};
let empty = self.0.as_ref().unwrap().iter().all(|x| x.is_none());
if empty {
self.0 = None;
}
result
}
/// Returns a list of the pseudo-elements.
pub fn keys(&self) -> Vec<PseudoElement> {
let mut v = Vec::new();
if let Some(ref arr) = self.0 {
for i in 0..EAGER_PSEUDO_COUNT {
if arr[i].is_some() {
v.push(PseudoElement::from_eager_index(i));
}
}
}
v
}
/// Sets the rule node for a given pseudo-element, which must already have an entry.
///
/// Returns true if the rule node changed.
pub fn set_rules(&mut self, pseudo: &PseudoElement, rules: StrongRuleNode) -> bool {
debug_assert!(self.has(pseudo));
let mut style = self.get_mut(pseudo).unwrap();
let changed = style.rules != rules;
style.rules = rules;
changed
}
}
impl Deref for PseudoStyles {
type Target = PseudoStylesInner;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl DerefMut for PseudoStyles {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}
/// A cache of precomputed and lazy pseudo-elements, used by servo. This isn't
/// a very efficient design, but is the result of servo having previously used
/// the eager pseudo map (when it was a map) for this cache.
#[cfg(feature = "servo")]
type PseudoElementCache = HashMap<PseudoElement, ComputedStyle, BuildHasherDefault<::fnv::FnvHasher>>;
#[cfg(feature = "gecko")]
type PseudoElementCache = ();
/// The styles associated with a node, including the styles for any
/// pseudo-elements.
@ -107,8 +161,10 @@ impl DerefMut for PseudoStyles {
pub struct ElementStyles {
/// The element's style.
pub primary: ComputedStyle,
/// The map of styles for the element's pseudos.
pub pseudos: PseudoStyles,
/// A list of the styles for the element's eagerly-cascaded pseudo-elements.
pub pseudos: EagerPseudoStyles,
/// NB: This is an empty field for gecko.
pub cached_pseudos: PseudoElementCache,
}
impl ElementStyles {
@ -116,7 +172,8 @@ impl ElementStyles {
pub fn new(primary: ComputedStyle) -> Self {
ElementStyles {
primary: primary,
pseudos: PseudoStyles::empty(),
pseudos: EagerPseudoStyles(None),
cached_pseudos: PseudoElementCache::default(),
}
}

View File

@ -42,7 +42,39 @@ use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PseudoElement(Atom, bool);
/// List of eager pseudos. Keep this in sync with the count below.
macro_rules! each_eager_pseudo {
($macro_name:ident, $atom_macro:ident) => {
$macro_name!($atom_macro!(":after"), 0);
$macro_name!($atom_macro!(":before"), 1);
}
}
/// The number of eager pseudo-elements (just ::before and ::after).
pub const EAGER_PSEUDO_COUNT: usize = 2;
impl PseudoElement {
/// Gets the canonical index of this eagerly-cascaded pseudo-element.
#[inline]
pub fn eager_index(&self) -> usize {
macro_rules! case {
($atom:expr, $idx:expr) => { if *self.as_atom() == $atom { return $idx; } }
}
each_eager_pseudo!(case, atom);
panic!("Not eager")
}
/// Creates a pseudo-element from an eager index.
#[inline]
pub fn from_eager_index(i: usize) -> Self {
macro_rules! case {
($atom:expr, $idx:expr) => { if i == $idx { return PseudoElement($atom, false); } }
}
each_eager_pseudo!(case, atom);
panic!("Not eager")
}
/// Get the pseudo-element as an atom.
#[inline]
pub fn as_atom(&self) -> &Atom {
@ -55,6 +87,35 @@ impl PseudoElement {
self.1
}
/// Whether this pseudo-element is ::before or ::after.
#[inline]
pub fn is_before_or_after(&self) -> bool {
*self.as_atom() == atom!(":before") ||
*self.as_atom() == atom!(":after")
}
/// Whether this pseudo-element is eagerly-cascaded.
#[inline]
pub fn is_eager(&self) -> bool {
macro_rules! case {
($atom:expr, $idx:expr) => { if *self.as_atom() == $atom { return true; } }
}
each_eager_pseudo!(case, atom);
return false;
}
/// Whether this pseudo-element is lazily-cascaded.
#[inline]
pub fn is_lazy(&self) -> bool {
!self.is_eager() && !self.is_precomputed()
}
/// Whether this pseudo-element is precomputed.
#[inline]
pub fn is_precomputed(&self) -> bool {
self.is_anon_box()
}
/// Construct a pseudo-element from an `Atom`, receiving whether it is also
/// an anonymous box, and don't check it on release builds.
///
@ -398,7 +459,8 @@ impl SelectorImpl {
///
/// We resolve the others lazily, see `Servo_ResolvePseudoStyle`.
pub fn pseudo_element_cascade_type(pseudo: &PseudoElement) -> PseudoElementCascadeType {
if Self::pseudo_is_before_or_after(pseudo) {
if pseudo.is_eager() {
debug_assert!(!pseudo.is_anon_box());
return PseudoElementCascadeType::Eager
}
@ -409,6 +471,19 @@ impl SelectorImpl {
PseudoElementCascadeType::Lazy
}
/// A helper to traverse each eagerly cascaded pseudo-element, executing
/// `fun` on it.
#[inline]
pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
where F: FnMut(PseudoElement),
{
macro_rules! case {
($atom:expr, $idx:expr) => { fun(PseudoElement($atom, false)); }
}
each_eager_pseudo!(case, atom);
}
#[inline]
/// Executes a function for each pseudo-element.
pub fn each_pseudo_element<F>(mut fun: F)
@ -423,13 +498,6 @@ impl SelectorImpl {
include!("generated/gecko_pseudo_element_helper.rs")
}
#[inline]
/// Returns whether the given pseudo-element is `::before` or `::after`.
pub fn pseudo_is_before_or_after(pseudo: &PseudoElement) -> bool {
*pseudo.as_atom() == atom!(":before") ||
*pseudo.as_atom() == atom!(":after")
}
#[inline]
/// Returns the relevant state flag for a given non-tree-structural
/// pseudo-class.

View File

@ -20,13 +20,11 @@ use properties::longhands::display::computed_value as display;
use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RESTYLE_CSS_ANIMATIONS, RestyleHint};
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode};
use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
use selectors::MatchAttr;
use selectors::bloom::BloomFilter;
use selectors::matching::{ElementSelectorFlags, StyleRelations};
use selectors::matching::AFFECTED_BY_PSEUDO_ELEMENTS;
use servo_config::opts;
use sink::ForgetfulSink;
use std::collections::hash_map::Entry;
use std::sync::Arc;
use stylist::ApplicableDeclarationBlock;
@ -903,9 +901,9 @@ pub trait MatchMethods : TElement {
// Compute rule nodes for eagerly-cascaded pseudo-elements.
let mut matches_different_pseudos = false;
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
let mut per_pseudo = &mut data.styles_mut().pseudos;
let mut pseudos = &mut data.styles_mut().pseudos;
debug_assert!(applicable_declarations.is_empty());
let pseudo_animation_rules = if <Self as MatchAttr>::Impl::pseudo_is_before_or_after(&pseudo) {
let pseudo_animation_rules = if pseudo.is_before_or_after() {
self.get_animation_rules(Some(&pseudo))
} else {
AnimationRules(None, None)
@ -921,19 +919,13 @@ pub trait MatchMethods : TElement {
if !applicable_declarations.is_empty() {
let new_rules =
compute_rule_node::<Self>(rule_tree, &mut applicable_declarations);
match per_pseudo.entry(pseudo) {
Entry::Occupied(mut e) => {
if e.get().rules != new_rules {
e.get_mut().rules = new_rules;
rule_nodes_changed = true;
}
},
Entry::Vacant(e) => {
e.insert(ComputedStyle::new_partial(new_rules));
matches_different_pseudos = true;
}
if pseudos.has(&pseudo) {
rule_nodes_changed = pseudos.set_rules(&pseudo, new_rules);
} else {
pseudos.insert(&pseudo, ComputedStyle::new_partial(new_rules));
matches_different_pseudos = true;
}
} else if per_pseudo.remove(&pseudo).is_some() {
} else if pseudos.take(&pseudo).is_some() {
matches_different_pseudos = true;
}
});
@ -948,7 +940,7 @@ pub trait MatchMethods : TElement {
}
// If we have any pseudo elements, indicate so in the primary StyleRelations.
if !data.styles().pseudos.is_empty() {
if data.styles().pseudos.is_empty() {
primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
}
@ -995,11 +987,10 @@ pub trait MatchMethods : TElement {
animation_rule.as_ref(),
primary_rules);
let iter = element_styles.pseudos.iter_mut().filter(|&(p, _)|
<Self as MatchAttr>::Impl::pseudo_is_before_or_after(p));
for (pseudo, ref mut computed) in iter {
let animation_rule = self.get_animation_rule(Some(pseudo));
let pseudo_rules = &mut computed.rules;
let pseudos = &mut element_styles.pseudos;
for pseudo in pseudos.keys().iter().filter(|p| p.is_before_or_after()) {
let animation_rule = self.get_animation_rule(Some(&pseudo));
let pseudo_rules = &mut pseudos.get_mut(&pseudo).unwrap().rules;
replace_rule_node(CascadeLevel::Animations,
animation_rule.as_ref(),
pseudo_rules);
@ -1198,11 +1189,10 @@ pub trait MatchMethods : TElement {
//
// Note that we've already set up the map of matching pseudo-elements
// in match_element (and handled the damage implications of changing
// which pseudos match), so now we can just iterate the map. This does
// mean collecting the keys, so that the borrow checker will let us pass
// the mutable |data| to the inner cascade function.
let matched_pseudos: Vec<PseudoElement> =
data.styles().pseudos.keys().cloned().collect();
// which pseudos match), so now we can just iterate what we have. This
// does mean collecting owned pseudos, so that the borrow checker will
// let us pass the mutable |data| to the inner cascade function.
let matched_pseudos = data.styles().pseudos.keys();
for pseudo in matched_pseudos {
// If the new primary style is display:none, we don't need pseudo
// styles, but we still need to clear any stale values.
@ -1212,7 +1202,7 @@ pub trait MatchMethods : TElement {
}
// Only ::before and ::after are animatable.
let animate = <Self as MatchAttr>::Impl::pseudo_is_before_or_after(&pseudo);
let animate = pseudo.is_before_or_after();
self.cascade_primary_or_pseudo(context, data, Some(&pseudo),
&mut possibly_expired_animations,
CascadeBooleans {

View File

@ -39,7 +39,7 @@ impl<'a> ParserContext<'a> {
pub fn new_for_cssom(url_data: &'a UrlExtraData,
error_reporter: &'a ParseErrorReporter)
-> ParserContext<'a> {
Self::new(Origin::User, url_data, error_reporter)
Self::new(Origin::Author, url_data, error_reporter)
}
}

View File

@ -3,7 +3,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#![allow(unsafe_code)]
#![deny(missing_docs)]
//! The rule tree.
@ -35,6 +34,11 @@ use thread_state;
///
/// That way, a rule node that represents a likely-to-match-again rule (like a
/// :hover rule) can be reused if we haven't GC'd it yet.
///
/// See the discussion at https://github.com/servo/servo/pull/15562 and the IRC
/// logs at http://logs.glob.uno/?c=mozilla%23servo&s=3+Apr+2017&e=3+Apr+2017
/// logs from http://logs.glob.uno/?c=mozilla%23servo&s=3+Apr+2017&e=3+Apr+2017#c644094
/// to se a discussion about the different memory orderings used here.
#[derive(Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct RuleTree {
@ -445,8 +449,11 @@ impl RuleNode {
self as *const RuleNode, self.parent.as_ref().map(|p| p.ptr()));
// NB: The other siblings we use in this function can also be dead, so
// we can't use `get` here, since it asserts.
let prev_sibling = self.prev_sibling.swap(ptr::null_mut(), Ordering::Relaxed);
let next_sibling = self.next_sibling.swap(ptr::null_mut(), Ordering::Relaxed);
let prev_sibling =
self.prev_sibling.swap(ptr::null_mut(), Ordering::Relaxed);
let next_sibling =
self.next_sibling.swap(ptr::null_mut(), Ordering::Relaxed);
// Store the `next` pointer as appropriate, either in the previous
// sibling, or in the parent otherwise.
@ -474,7 +481,7 @@ impl RuleNode {
}
let _ = writeln!(writer, " - {:?} (ref: {:?}, parent: {:?})",
self as *const _, self.refcount.load(Ordering::SeqCst),
self as *const _, self.refcount.load(Ordering::Relaxed),
self.parent.as_ref().map(|p| p.ptr()));
for _ in 0..indent {
@ -500,8 +507,8 @@ impl RuleNode {
}
fn iter_children(&self) -> RuleChildrenListIter {
// FIXME(emilio): Fiddle with memory orderings.
let first_child = self.first_child.load(Ordering::SeqCst);
// See next_sibling to see why we need Acquire semantics here.
let first_child = self.first_child.load(Ordering::Acquire);
RuleChildrenListIter {
current: if first_child.is_null() {
None
@ -549,9 +556,9 @@ impl StrongRuleNode {
}
fn next_sibling(&self) -> Option<WeakRuleNode> {
// FIXME(emilio): Investigate what ordering can we achieve without
// messing things up.
let ptr = self.get().next_sibling.load(Ordering::SeqCst);
// We use acquire semantics here to ensure proper synchronization while
// inserting in the child list.
let ptr = self.get().next_sibling.load(Ordering::Acquire);
if ptr.is_null() {
None
} else {
@ -570,6 +577,7 @@ impl StrongRuleNode {
source: StyleSource,
level: CascadeLevel) -> StrongRuleNode {
let mut last = None;
// TODO(emilio): We could avoid all the refcount churn here.
for child in self.get().iter_children() {
if child .get().level == level &&
child.get().source.as_ref().unwrap().ptr_equals(&source) {
@ -593,16 +601,18 @@ impl StrongRuleNode {
None => &self.get().first_child,
};
// We use `AqcRel` semantics to ensure the initializing writes
// in `node` are visible after the swap succeeds.
let existing =
next_sibling_ptr.compare_and_swap(ptr::null_mut(),
new_ptr,
Ordering::SeqCst);
Ordering::AcqRel);
if existing == ptr::null_mut() {
// Now we know we're in the correct position in the child list,
// we can set the back pointer, knowing that this will only be
// accessed again in a single-threaded manner when we're
// sweeping possibly dead nodes.
// Now we know we're in the correct position in the child
// list, we can set the back pointer, knowing that this will
// only be accessed again in a single-threaded manner when
// we're sweeping possibly dead nodes.
if let Some(ref l) = last {
node.prev_sibling.store(l.ptr(), Ordering::Relaxed);
}
@ -614,7 +624,8 @@ impl StrongRuleNode {
strong = WeakRuleNode { ptr: existing }.upgrade();
if strong.get().source.as_ref().unwrap().ptr_equals(&source) {
// That node happens to be for the same style source, use that.
// That node happens to be for the same style source, use
// that, and let node fall out of scope.
return strong;
}
}
@ -631,7 +642,7 @@ impl StrongRuleNode {
fn get(&self) -> &RuleNode {
if cfg!(debug_assertions) {
let node = unsafe { &*self.ptr };
assert!(node.refcount.load(Ordering::SeqCst) > 0);
assert!(node.refcount.load(Ordering::Relaxed) > 0);
}
unsafe { &*self.ptr }
}
@ -660,6 +671,12 @@ impl StrongRuleNode {
}
}
/// Returns whether this node has any child, only intended for testing
/// purposes, and called on a single-threaded fashion only.
pub unsafe fn has_children_for_testing(&self) -> bool {
!self.get().first_child.load(Ordering::Relaxed).is_null()
}
unsafe fn pop_from_free_list(&self) -> Option<WeakRuleNode> {
// NB: This can run from the root node destructor, so we can't use
// `get()`, since it asserts the refcount is bigger than zero.
@ -678,7 +695,7 @@ impl StrongRuleNode {
thread_state::get().is_script()));
}
let current = me.next_free.load(Ordering::SeqCst);
let current = me.next_free.load(Ordering::Relaxed);
if current == FREE_LIST_SENTINEL {
return None;
}
@ -689,12 +706,12 @@ impl StrongRuleNode {
debug_assert!(current != self.ptr,
"How did the root end up in the free list?");
let next = (*current).next_free.swap(ptr::null_mut(), Ordering::SeqCst);
let next = (*current).next_free.swap(ptr::null_mut(), Ordering::Relaxed);
debug_assert!(!next.is_null(),
"How did a null pointer end up in the free list?");
me.next_free.store(next, Ordering::SeqCst);
me.next_free.store(next, Ordering::Relaxed);
debug!("Popping from free list: cur: {:?}, next: {:?}", current, next);
@ -711,7 +728,7 @@ impl StrongRuleNode {
let mut current = self.ptr;
let mut seen = HashSet::new();
while current != FREE_LIST_SENTINEL {
let next = (*current).next_free.load(Ordering::SeqCst);
let next = (*current).next_free.load(Ordering::Relaxed);
assert!(!next.is_null());
assert!(!seen.contains(&next));
seen.insert(next);
@ -734,7 +751,7 @@ impl StrongRuleNode {
while let Some(weak) = self.pop_from_free_list() {
let needs_drop = {
let node = &*weak.ptr();
if node.refcount.load(Ordering::SeqCst) == 0 {
if node.refcount.load(Ordering::Relaxed) == 0 {
node.remove_from_child_list();
true
} else {
@ -748,14 +765,14 @@ impl StrongRuleNode {
}
}
me.free_count.store(0, Ordering::SeqCst);
me.free_count.store(0, Ordering::Relaxed);
debug_assert!(me.next_free.load(Ordering::SeqCst) == FREE_LIST_SENTINEL);
debug_assert!(me.next_free.load(Ordering::Relaxed) == FREE_LIST_SENTINEL);
}
unsafe fn maybe_gc(&self) {
debug_assert!(self.get().is_root(), "Can't call GC on a non-root node!");
if self.get().free_count.load(Ordering::SeqCst) > RULE_TREE_GC_INTERVAL {
if self.get().free_count.load(Ordering::Relaxed) > RULE_TREE_GC_INTERVAL {
self.gc();
}
}
@ -787,9 +804,9 @@ impl<'a> Iterator for SelfAndAncestors<'a> {
impl Clone for StrongRuleNode {
fn clone(&self) -> Self {
debug!("{:?}: {:?}+", self.ptr(), self.get().refcount.load(Ordering::SeqCst));
debug_assert!(self.get().refcount.load(Ordering::SeqCst) > 0);
self.get().refcount.fetch_add(1, Ordering::SeqCst);
debug!("{:?}: {:?}+", self.ptr(), self.get().refcount.load(Ordering::Relaxed));
debug_assert!(self.get().refcount.load(Ordering::Relaxed) > 0);
self.get().refcount.fetch_add(1, Ordering::Relaxed);
StrongRuleNode {
ptr: self.ptr,
}
@ -800,21 +817,21 @@ impl Drop for StrongRuleNode {
fn drop(&mut self) {
let node = unsafe { &*self.ptr };
debug!("{:?}: {:?}-", self.ptr(), node.refcount.load(Ordering::SeqCst));
debug!("{:?}: {:?}-", self.ptr(), node.refcount.load(Ordering::Relaxed));
debug!("Dropping node: {:?}, root: {:?}, parent: {:?}",
self.ptr,
node.root.as_ref().map(|r| r.ptr()),
node.parent.as_ref().map(|p| p.ptr()));
let should_drop = {
debug_assert!(node.refcount.load(Ordering::SeqCst) > 0);
node.refcount.fetch_sub(1, Ordering::SeqCst) == 1
debug_assert!(node.refcount.load(Ordering::Relaxed) > 0);
node.refcount.fetch_sub(1, Ordering::Relaxed) == 1
};
if !should_drop {
return
}
debug_assert_eq!(node.first_child.load(Ordering::SeqCst),
debug_assert_eq!(node.first_child.load(Ordering::Acquire),
ptr::null_mut());
if node.parent.is_none() {
debug!("Dropping root node!");
@ -829,17 +846,24 @@ impl Drop for StrongRuleNode {
let root = unsafe { &*node.root.as_ref().unwrap().ptr() };
let free_list = &root.next_free;
// We're sure we're already in the free list, don't spinloop.
if node.next_free.load(Ordering::SeqCst) != ptr::null_mut() {
// We're sure we're already in the free list, don't spinloop if we're.
// Note that this is just a fast path, so it doesn't need to have an
// strong memory ordering.
if node.next_free.load(Ordering::Relaxed) != ptr::null_mut() {
return;
}
// Ensure we "lock" the free list head swapping it with a null pointer.
let mut old_head = free_list.load(Ordering::SeqCst);
//
// Note that we use Acquire/Release semantics for the free list
// synchronization, in order to guarantee that the next_free
// reads/writes we do below are properly visible from multiple threads
// racing.
let mut old_head = free_list.load(Ordering::Relaxed);
loop {
match free_list.compare_exchange_weak(old_head,
ptr::null_mut(),
Ordering::SeqCst,
Ordering::Acquire,
Ordering::Relaxed) {
Ok(..) => {
if old_head != ptr::null_mut() {
@ -852,15 +876,25 @@ impl Drop for StrongRuleNode {
// If other thread has raced with use while using the same rule node,
// just store the old head again, we're done.
if node.next_free.load(Ordering::SeqCst) != ptr::null_mut() {
free_list.store(old_head, Ordering::SeqCst);
//
// Note that we can use relaxed operations for loading since we're
// effectively locking the free list with Acquire/Release semantics, and
// the memory ordering is already guaranteed by that locking/unlocking.
if node.next_free.load(Ordering::Relaxed) != ptr::null_mut() {
free_list.store(old_head, Ordering::Release);
return;
}
// Else store the old head as the next pointer, and store ourselves as
// the new head of the free list.
node.next_free.store(old_head, Ordering::SeqCst);
free_list.store(self.ptr(), Ordering::SeqCst);
//
// This can be relaxed since this pointer won't be read until GC.
node.next_free.store(old_head, Ordering::Relaxed);
// This can be release because of the locking of the free list, that
// ensures that all the other nodes racing with this one are using
// `Acquire`.
free_list.store(self.ptr(), Ordering::Release);
}
}
@ -877,7 +911,7 @@ impl WeakRuleNode {
debug!("Upgrading weak node: {:p}", self.ptr());
let node = unsafe { &*self.ptr };
node.refcount.fetch_add(1, Ordering::SeqCst);
node.refcount.fetch_add(1, Ordering::Relaxed);
StrongRuleNode {
ptr: self.ptr,
}

View File

@ -102,26 +102,6 @@ pub enum PseudoElementCascadeType {
Precomputed,
}
impl PseudoElementCascadeType {
/// Simple accessor to check whether the cascade type is eager.
#[inline]
pub fn is_eager(&self) -> bool {
*self == PseudoElementCascadeType::Eager
}
/// Simple accessor to check whether the cascade type is lazy.
#[inline]
pub fn is_lazy(&self) -> bool {
*self == PseudoElementCascadeType::Lazy
}
/// Simple accessor to check whether the cascade type is precomputed.
#[inline]
pub fn is_precomputed(&self) -> bool {
*self == PseudoElementCascadeType::Precomputed
}
}
/// An extension to rust-selector's `Element` trait.
pub trait ElementExt: Element<Impl=SelectorImpl> + Debug {
/// Whether this element is a `link`.
@ -134,22 +114,6 @@ pub trait ElementExt: Element<Impl=SelectorImpl> + Debug {
}
impl SelectorImpl {
/// A helper to traverse each eagerly cascaded pseudo-element, executing
/// `fun` on it.
///
/// TODO(emilio): We can optimize this for Gecko using the pseudo-element
/// macro, and we should consider doing that for Servo too.
#[inline]
pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
where F: FnMut(PseudoElement),
{
Self::each_pseudo_element(|pseudo| {
if Self::pseudo_element_cascade_type(&pseudo).is_eager() {
fun(pseudo)
}
})
}
/// A helper to traverse each precomputed pseudo-element, executing `fun` on
/// it.
///
@ -160,7 +124,7 @@ impl SelectorImpl {
where F: FnMut(PseudoElement),
{
Self::each_pseudo_element(|pseudo| {
if Self::pseudo_element_cascade_type(&pseudo).is_precomputed() {
if pseudo.is_precomputed() {
fun(pseudo)
}
})

View File

@ -19,6 +19,7 @@ use selectors::parser::{AttrSelector, SelectorMethods};
use std::borrow::Cow;
use std::fmt;
use std::fmt::Debug;
use std::mem;
/// A pseudo-element, both public and private.
///
@ -26,10 +27,13 @@ use std::fmt::Debug;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[allow(missing_docs)]
#[repr(usize)]
pub enum PseudoElement {
// Eager pseudos. Keep these first so that eager_index() works.
After = 0,
Before,
After,
Selection,
// Non-eager pseudos.
DetailsSummary,
DetailsContent,
ServoText,
@ -48,8 +52,8 @@ impl ToCss for PseudoElement {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
use self::PseudoElement::*;
dest.write_str(match *self {
Before => "::before",
After => "::after",
Before => "::before",
Selection => "::selection",
DetailsSummary => "::-servo-details-summary",
DetailsContent => "::-servo-details-content",
@ -67,25 +71,60 @@ impl ToCss for PseudoElement {
}
}
/// The number of eager pseudo-elements. Keep this in sync with cascade_type.
pub const EAGER_PSEUDO_COUNT: usize = 3;
impl PseudoElement {
/// Gets the canonical index of this eagerly-cascaded pseudo-element.
#[inline]
pub fn eager_index(&self) -> usize {
debug_assert!(self.is_eager());
self.clone() as usize
}
/// Creates a pseudo-element from an eager index.
#[inline]
pub fn from_eager_index(i: usize) -> Self {
assert!(i < EAGER_PSEUDO_COUNT);
let result: PseudoElement = unsafe { mem::transmute(i) };
debug_assert!(result.is_eager());
result
}
/// Whether the current pseudo element is :before or :after.
#[inline]
pub fn is_before_or_after(&self) -> bool {
match *self {
PseudoElement::Before |
PseudoElement::After => true,
_ => false,
}
matches!(*self, PseudoElement::After | PseudoElement::Before)
}
/// Whether this pseudo-element is eagerly-cascaded.
#[inline]
pub fn is_eager(&self) -> bool {
self.cascade_type() == PseudoElementCascadeType::Eager
}
/// Whether this pseudo-element is lazily-cascaded.
#[inline]
pub fn is_lazy(&self) -> bool {
self.cascade_type() == PseudoElementCascadeType::Lazy
}
/// Whether this pseudo-element is precomputed.
#[inline]
pub fn is_precomputed(&self) -> bool {
self.cascade_type() == PseudoElementCascadeType::Precomputed
}
/// Returns which kind of cascade type has this pseudo.
///
/// For more info on cascade types, see docs/components/style.md
///
/// Note: Keep this in sync with EAGER_PSEUDO_COUNT.
#[inline]
pub fn cascade_type(&self) -> PseudoElementCascadeType {
match *self {
PseudoElement::Before |
PseudoElement::After |
PseudoElement::Before |
PseudoElement::Selection => PseudoElementCascadeType::Eager,
PseudoElement::DetailsSummary => PseudoElementCascadeType::Lazy,
PseudoElement::DetailsContent |
@ -369,6 +408,17 @@ impl SelectorImpl {
pseudo.cascade_type()
}
/// A helper to traverse each eagerly cascaded pseudo-element, executing
/// `fun` on it.
#[inline]
pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
where F: FnMut(PseudoElement),
{
for i in 0..EAGER_PSEUDO_COUNT {
fun(PseudoElement::from_eager_index(i));
}
}
/// Executes `fun` for each pseudo-element.
#[inline]
pub fn each_pseudo_element<F>(mut fun: F)
@ -396,12 +446,6 @@ impl SelectorImpl {
pub fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState {
pc.state_flag()
}
/// Returns whether this pseudo is either :before or :after.
#[inline]
pub fn pseudo_is_before_or_after(pseudo: &PseudoElement) -> bool {
pseudo.is_before_or_after()
}
}
/// Servo's version of an element snapshot.

View File

@ -345,7 +345,7 @@ impl Stylist {
parent: Option<&Arc<ComputedValues>>,
cascade_flags: CascadeFlags)
-> ComputedStyle {
debug_assert!(SelectorImpl::pseudo_element_cascade_type(pseudo).is_precomputed());
debug_assert!(pseudo.is_precomputed());
let rule_node = match self.precomputed_pseudo_element_decls.get(pseudo) {
Some(declarations) => {
@ -435,7 +435,7 @@ impl Stylist {
fmt::Debug +
PresentationalHintsSynthetizer
{
debug_assert!(SelectorImpl::pseudo_element_cascade_type(pseudo).is_lazy());
debug_assert!(pseudo.is_lazy());
if self.pseudos_map.get(pseudo).is_none() {
return None;
}
@ -603,9 +603,7 @@ impl Stylist {
debug_assert!(!self.is_device_dirty);
debug_assert!(style_attribute.is_none() || pseudo_element.is_none(),
"Style attributes do not apply to pseudo-elements");
debug_assert!(pseudo_element.is_none() ||
!SelectorImpl::pseudo_element_cascade_type(pseudo_element.as_ref().unwrap())
.is_precomputed());
debug_assert!(pseudo_element.as_ref().map_or(true, |p| !p.is_precomputed()));
let map = match pseudo_element {
Some(ref pseudo) => self.pseudos_map.get(pseudo).unwrap(),

View File

@ -80,8 +80,8 @@ impl ToCss for AlignFlags {
ALIGN_CENTER => "center",
ALIGN_LEFT => "left",
ALIGN_RIGHT => "left",
ALIGN_BASELINE => "right",
ALIGN_LAST_BASELINE => "baseline",
ALIGN_BASELINE => "baseline",
ALIGN_LAST_BASELINE => "last baseline",
ALIGN_STRETCH => "stretch",
ALIGN_SELF_START => "self-start",
ALIGN_SELF_END => "self-end",
@ -322,33 +322,63 @@ impl Parse for JustifyItems {
// auto | normal | stretch | <baseline-position>
fn parse_auto_normal_stretch_baseline(input: &mut Parser) -> Result<AlignFlags, ()> {
if let Ok(baseline) = input.try(|input| parse_baseline(input)) {
return Ok(baseline);
}
let ident = input.expect_ident()?;
match_ignore_ascii_case! { &ident,
"auto" => Ok(ALIGN_AUTO),
"normal" => Ok(ALIGN_NORMAL),
"stretch" => Ok(ALIGN_STRETCH),
"baseline" => Ok(ALIGN_BASELINE),
_ => Err(())
}
}
// normal | stretch | <baseline-position>
fn parse_normal_stretch_baseline(input: &mut Parser) -> Result<AlignFlags, ()> {
if let Ok(baseline) = input.try(|input| parse_baseline(input)) {
return Ok(baseline);
}
let ident = input.expect_ident()?;
match_ignore_ascii_case! { &ident,
"normal" => Ok(ALIGN_NORMAL),
"stretch" => Ok(ALIGN_STRETCH),
"baseline" => Ok(ALIGN_BASELINE),
_ => Err(())
}
}
// normal | <baseline-position>
fn parse_normal_or_baseline(input: &mut Parser) -> Result<AlignFlags, ()> {
if let Ok(baseline) = input.try(|input| parse_baseline(input)) {
return Ok(baseline);
}
let ident = input.expect_ident()?;
match_ignore_ascii_case! { &ident,
"normal" => Ok(ALIGN_NORMAL),
_ => Err(())
}
}
// <baseline-position>
fn parse_baseline(input: &mut Parser) -> Result<AlignFlags, ()> {
let ident = input.expect_ident()?;
match_ignore_ascii_case! { &ident,
"baseline" => Ok(ALIGN_BASELINE),
"first" => {
if input.try(|input| input.expect_ident_matching("baseline")).is_ok() {
return Ok(ALIGN_BASELINE);
}
Err(())
},
"last" => {
if input.try(|input| input.expect_ident_matching("baseline")).is_ok() {
return Ok(ALIGN_LAST_BASELINE);
}
Err(())
},
_ => Err(())
}
}

View File

@ -32,10 +32,8 @@ function getPEMString(cert)
+ "\r\n-----END CERTIFICATE-----\r\n";
}
let certcache = Components.classes["@mozilla.org/security/nsscertcache;1"].createInstance(Ci.nsINSSCertCache);
let certdb = Components.classes["@mozilla.org/security/x509certdb;1"].createInstance(Ci.nsIX509CertDB);
certcache.cacheAllCerts();
let enumerator = certcache.getX509CachedCerts().getEnumerator();
let enumerator = certdb.getCerts().getEnumerator();
let certlist = [];
let certstring="";
while(enumerator.hasMoreElements()){

File diff suppressed because it is too large Load Diff

View File

@ -32,7 +32,12 @@ impl<'a> AutoGCRuleTree<'a> {
impl<'a> Drop for AutoGCRuleTree<'a> {
fn drop(&mut self) {
unsafe { self.0.gc() }
unsafe {
self.0.gc();
assert!(::std::thread::panicking() ||
!self.0.root().has_children_for_testing(),
"No rule nodes other than the root shall remain!");
}
}
}

View File

@ -8,7 +8,10 @@ let index = new taskcluster.Index({
});
// Create queue instance for fetching taskId
let queue = new taskcluster.Queue();
let queue = new taskcluster.Queue({
delayFactor: 750, // Good solid delay for background process
retries: 8, // A few extra retries for robustness
});
// Load input
let taskId = process.env.TARGET_TASKID;
@ -37,7 +40,7 @@ queue.task(taskId).then(task => task.expires).then(expires => {
}).catch(err => {
console.log('Error:\n%s', err);
if (err.stack) {
console.log('Stack:\n%s', err.stack());
console.log('Stack:\n%s', err.stack);
}
console.log('Properties:\n%j', err);
throw err;

View File

@ -972,7 +972,7 @@ GeckoDriver.prototype.executeJSScript = function* (cmd, resp) {
* @param {string} url
* URL to navigate to.
*/
GeckoDriver.prototype.get = function*(cmd, resp) {
GeckoDriver.prototype.get = function* (cmd, resp) {
assert.content(this.context);
assert.window(this.getCurrentWindow());
@ -991,7 +991,7 @@ GeckoDriver.prototype.get = function*(cmd, resp) {
startTime: new Date().getTime(),
};
this.mm.broadcastAsyncMessage(
"Marionette:pollForReadyState" + this.curBrowser.curFrameId,
"Marionette:waitForPageLoaded" + this.curBrowser.curFrameId,
parameters);
});
@ -1092,9 +1092,7 @@ GeckoDriver.prototype.goBack = function* (cmd, resp) {
startTime: new Date().getTime(),
};
this.mm.broadcastAsyncMessage(
// TODO: combine with
// "Marionette:pollForReadyState" + this.curBrowser.curFrameId,
"Marionette:pollForReadyState" + this.curBrowser.curFrameId,
"Marionette:waitForPageLoaded" + this.curBrowser.curFrameId,
parameters);
});
@ -1133,21 +1131,21 @@ GeckoDriver.prototype.goForward = function* (cmd, resp) {
startTime: new Date().getTime(),
};
this.mm.broadcastAsyncMessage(
// TODO: combine with
// "Marionette:pollForReadyState" + this.curBrowser.curFrameId,
"Marionette:pollForReadyState" + this.curBrowser.curFrameId,
"Marionette:waitForPageLoaded" + this.curBrowser.curFrameId,
parameters);
});
yield goForward;
};
/** Refresh the page. */
GeckoDriver.prototype.refresh = function*(cmd, resp) {
/**
* Causes the browser to reload the page in in current top-level browsing context.
*/
GeckoDriver.prototype.refresh = function* (cmd, resp) {
assert.content(this.context);
assert.window(this.getCurrentWindow());
yield this.listener.refresh();
yield this.listener.refresh({pageTimeout: this.timeouts.pageLoad});
};
/**

View File

@ -59,10 +59,19 @@ def upload_handler(request, response):
@handlers.handler
def slow_loading_handler(request, response):
time.sleep(5)
return """<!doctype html>
<title>ok</title>
<p>ok"""
# Allow the test specify the delay for delivering the content
params = dict(urlparse.parse_qsl(request.url_parts.query))
delay = int(params.get('delay', 5))
time.sleep(delay)
# Do not allow the page to be cached to circumvent the bfcache of the browser
response.headers.set("Cache-Control", "no-cache, no-store")
response.content = """<!doctype html>
<meta charset="UTF-8">
<title>Slow page loading</title>
<p>Delay: <span id="delay">{}</span></p>
""".format(delay)
class NotAliveError(Exception):

View File

@ -1,134 +0,0 @@
# 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/.
from marionette_driver import By, Wait
from marionette_driver.keys import Keys
from marionette_harness import MarionetteTestCase, skip, skip_if_mobile, WindowManagerMixin
class TestAboutPages(WindowManagerMixin, MarionetteTestCase):
def setUp(self):
super(TestAboutPages, self).setUp()
if self.marionette.session_capabilities['platformName'] == 'darwin':
self.mod_key = Keys.META
else:
self.mod_key = Keys.CONTROL
self.remote_uri = self.marionette.absolute_url("windowHandles.html")
def tearDown(self):
self.close_all_tabs()
super(TestAboutPages, self).tearDown()
def open_tab_with_link(self):
with self.marionette.using_context("content"):
self.marionette.navigate(self.remote_uri)
link = self.marionette.find_element(By.ID, "new-tab")
link.click()
@skip_if_mobile("Bug 1333209 - Process killed because of connection loss")
def test_back_forward(self):
# Bug 1311041 - Prevent changing of window handle by forcing the test
# to be run in a new tab.
new_tab = self.open_tab(trigger=self.open_tab_with_link)
self.marionette.switch_to_window(new_tab)
self.marionette.navigate("about:blank")
self.marionette.navigate(self.remote_uri)
self.marionette.navigate("about:support")
self.marionette.go_back()
self.assertEqual(self.marionette.get_url(), self.remote_uri)
self.marionette.go_forward()
self.assertEqual(self.marionette.get_url(), "about:support")
self.marionette.close()
self.marionette.switch_to_window(self.start_tab)
@skip_if_mobile("Bug 1333209 - Process killed because of connection loss")
def test_navigate_non_remote_about_pages(self):
# Bug 1311041 - Prevent changing of window handle by forcing the test
# to be run in a new tab.
new_tab = self.open_tab(trigger=self.open_tab_with_link)
self.marionette.switch_to_window(new_tab)
self.marionette.navigate("about:blank")
self.assertEqual(self.marionette.get_url(), "about:blank")
self.marionette.navigate("about:support")
self.assertEqual(self.marionette.get_url(), "about:support")
self.marionette.close()
self.marionette.switch_to_window(self.start_tab)
@skip_if_mobile("On Android no shortcuts are available")
def test_navigate_shortcut_key(self):
def open_with_shortcut():
self.marionette.navigate(self.remote_uri)
with self.marionette.using_context("chrome"):
main_win = self.marionette.find_element(By.ID, "main-window")
main_win.send_keys(self.mod_key, Keys.SHIFT, 'a')
new_tab = self.open_tab(trigger=open_with_shortcut)
self.marionette.switch_to_window(new_tab)
Wait(self.marionette).until(lambda mn: mn.get_url() == "about:addons",
message="'about:addons' hasn't been loaded")
self.marionette.close()
self.marionette.switch_to_window(self.start_tab)
@skip("Bug 1334137 - Intermittent: Process killed because of hang in getCurrentUrl()")
@skip_if_mobile("Interacting with chrome elements not available for Fennec")
def test_type_to_non_remote_tab(self):
# Bug 1311041 - Prevent changing of window handle by forcing the test
# to be run in a new tab.
new_tab = self.open_tab(trigger=self.open_tab_with_link)
self.marionette.switch_to_window(new_tab)
with self.marionette.using_context("chrome"):
urlbar = self.marionette.find_element(By.ID, 'urlbar')
urlbar.send_keys(self.mod_key + 'a')
urlbar.send_keys(self.mod_key + 'x')
urlbar.send_keys('about:support' + Keys.ENTER)
Wait(self.marionette).until(lambda mn: mn.get_url() == "about:support",
message="'about:support' hasn't been loaded")
self.marionette.close()
self.marionette.switch_to_window(self.start_tab)
@skip_if_mobile("Interacting with chrome elements not available for Fennec")
def test_type_to_remote_tab(self):
# Bug 1311041 - Prevent changing of window handle by forcing the test
# to be run in a new tab.
new_tab = self.open_tab(trigger=self.open_tab_with_link)
self.marionette.switch_to_window(new_tab)
# about:blank keeps remoteness from remote_uri
self.marionette.navigate("about:blank")
with self.marionette.using_context("chrome"):
urlbar = self.marionette.find_element(By.ID, 'urlbar')
urlbar.send_keys(self.mod_key + 'a')
urlbar.send_keys(self.mod_key + 'x')
urlbar.send_keys(self.remote_uri + Keys.ENTER)
Wait(self.marionette).until(lambda mn: mn.get_url() == self.remote_uri,
message="'{}' hasn't been loaded".format(self.remote_uri))
@skip_if_mobile("Needs application independent method to open a new tab")
def test_hang(self):
# Bug 1311041 - Prevent changing of window handle by forcing the test
# to be run in a new tab.
new_tab = self.open_tab(trigger=self.open_tab_with_link)
# Close the start tab
self.marionette.close()
self.marionette.switch_to_window(new_tab)
self.marionette.navigate(self.remote_uri)

View File

@ -3,10 +3,10 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import contextlib
import time
import urllib
from marionette_driver import By, errors, expected, Wait
from marionette_driver.keys import Keys
from marionette_harness import (
MarionetteTestCase,
run_if_e10s,
@ -21,12 +21,20 @@ def inline(doc):
return "data:text/html;charset=utf-8,%s" % urllib.quote(doc)
class TestBackForwardNavigation(WindowManagerMixin, MarionetteTestCase):
class BaseNavigationTestCase(WindowManagerMixin, MarionetteTestCase):
def setUp(self):
super(TestBackForwardNavigation, self).setUp()
super(BaseNavigationTestCase, self).setUp()
self.test_page = self.marionette.absolute_url('test.html')
self.test_page_frameset = self.marionette.absolute_url("frameset.html")
self.test_page_insecure = self.fixtures.where_is("test.html", on="https")
self.test_page_not_remote = "about:robots"
self.test_page_remote = self.marionette.absolute_url("test.html")
if self.marionette.session_capabilities["platformName"] == "darwin":
self.mod_key = Keys.META
else:
self.mod_key = Keys.CONTROL
def open_with_link():
link = self.marionette.find_element(By.ID, "new-blank-tab")
@ -39,16 +47,220 @@ class TestBackForwardNavigation(WindowManagerMixin, MarionetteTestCase):
self.assertEqual(self.history_length, 1)
def tearDown(self):
self.marionette.timeout.reset()
self.marionette.switch_to_parent_frame()
self.close_all_tabs()
super(TestBackForwardNavigation, self).tearDown()
super(BaseNavigationTestCase, self).tearDown()
@property
def history_length(self):
return self.marionette.execute_script("return window.history.length;")
def run_test(self, test_pages):
@property
def is_remote_tab(self):
with self.marionette.using_context("chrome"):
# TODO: DO NOT USE MOST RECENT WINDOW BUT CURRENT ONE
return self.marionette.execute_script("""
Components.utils.import("resource://gre/modules/AppConstants.jsm");
let win = null;
if (AppConstants.MOZ_APP_NAME == "fennec") {
Components.utils.import("resource://gre/modules/Services.jsm");
win = Services.wm.getMostRecentWindow("navigator:browser");
} else {
Components.utils.import("resource:///modules/RecentWindow.jsm");
win = RecentWindow.getMostRecentBrowserWindow();
}
let tabBrowser = null;
// Fennec
if (win.BrowserApp) {
tabBrowser = win.BrowserApp.selectedBrowser;
// Firefox
} else if (win.gBrowser) {
tabBrowser = win.gBrowser.selectedBrowser;
} else {
return null;
}
return tabBrowser.isRemoteBrowser;
""")
class TestNavigate(BaseNavigationTestCase):
def test_set_location_through_execute_script(self):
self.marionette.execute_script(
"window.location.href = '{}'".format(self.test_page_remote),
sandbox=None)
Wait(self.marionette).until(
lambda mn: self.test_page_remote == mn.get_url())
self.assertEqual("Marionette Test", self.marionette.title)
def test_navigate_chrome_unsupported_error(self):
with self.marionette.using_context("chrome"):
self.assertRaises(errors.UnsupportedOperationException,
self.marionette.navigate, "about:blank")
self.assertRaises(errors.UnsupportedOperationException, self.marionette.go_back)
self.assertRaises(errors.UnsupportedOperationException, self.marionette.go_forward)
self.assertRaises(errors.UnsupportedOperationException, self.marionette.refresh)
def test_get_current_url_returns_top_level_browsing_context_url(self):
page_iframe = self.marionette.absolute_url("test_iframe.html")
self.marionette.navigate(page_iframe)
self.assertEqual(page_iframe, self.marionette.get_url())
frame = self.marionette.find_element(By.CSS_SELECTOR, "#test_iframe")
self.marionette.switch_to_frame(frame)
self.assertEqual(page_iframe, self.marionette.get_url())
def test_get_current_url(self):
self.marionette.navigate(self.test_page_remote)
self.assertEqual(self.test_page_remote, self.marionette.get_url())
self.marionette.navigate("about:blank")
self.assertEqual("about:blank", self.marionette.get_url())
def test_navigate_in_child_frame_changes_to_top(self):
self.marionette.navigate(self.test_page_frameset)
frame = self.marionette.find_element(By.NAME, "third")
self.marionette.switch_to_frame(frame)
self.assertRaises(errors.NoSuchElementException,
self.marionette.find_element, By.NAME, "third")
self.marionette.navigate(self.test_page_frameset)
self.marionette.find_element(By.NAME, "third")
def test_invalid_url(self):
with self.assertRaises(errors.MarionetteException):
self.marionette.navigate("foo")
with self.assertRaises(errors.MarionetteException):
self.marionette.navigate("thisprotocoldoesnotexist://")
def test_find_element_state_complete(self):
self.marionette.navigate(self.test_page_remote)
state = self.marionette.execute_script(
"return window.document.readyState", sandbox=None)
self.assertEqual("complete", state)
self.assertTrue(self.marionette.find_element(By.ID, "mozLink"))
def test_navigate_timeout_error_no_remoteness_change(self):
is_remote_before_timeout = self.is_remote_tab
self.marionette.timeout.page_load = 0.5
with self.assertRaises(errors.TimeoutException):
self.marionette.navigate(self.marionette.absolute_url("slow"))
self.assertEqual(self.is_remote_tab, is_remote_before_timeout)
@run_if_e10s("Requires e10s mode enabled")
def test_navigate_timeout_error_remoteness_change(self):
self.assertTrue(self.is_remote_tab)
self.marionette.navigate("about:robots")
self.assertFalse(self.is_remote_tab)
self.marionette.timeout.page_load = 0.5
with self.assertRaises(errors.TimeoutException):
self.marionette.navigate(self.marionette.absolute_url("slow"))
# Even with the page not finished loading the browser is remote
self.assertTrue(self.is_remote_tab)
def test_navigate_to_same_image_document_twice(self):
self.marionette.navigate(self.fixtures.where_is("black.png"))
self.assertIn("black.png", self.marionette.title)
self.marionette.navigate(self.fixtures.where_is("black.png"))
self.assertIn("black.png", self.marionette.title)
def test_navigate_hash_change(self):
doc = inline("<p id=foo>")
self.marionette.navigate(doc)
self.marionette.execute_script("window.visited = true", sandbox=None)
self.marionette.navigate("{}#foo".format(doc))
self.assertTrue(self.marionette.execute_script(
"return window.visited", sandbox=None))
@skip_if_mobile("Bug 1334095 - Timeout: No new tab has been opened")
def test_about_blank_for_new_docshell(self):
self.assertEqual(self.marionette.get_url(), "about:blank")
self.marionette.navigate("about:blank")
@skip("Bug 1332064 - NoSuchElementException: Unable to locate element: :focus")
@run_if_manage_instance("Only runnable if Marionette manages the instance")
@skip_if_mobile("Bug 1322993 - Missing temporary folder")
def test_focus_after_navigation(self):
self.marionette.quit()
self.marionette.start_session()
self.marionette.navigate(inline("<input autofocus>"))
focus_el = self.marionette.find_element(By.CSS_SELECTOR, ":focus")
self.assertEqual(self.marionette.get_active_element(), focus_el)
@skip_if_mobile("Needs application independent method to open a new tab")
def test_no_hang_when_navigating_after_closing_original_tab(self):
# Close the start tab
self.marionette.switch_to_window(self.start_tab)
self.marionette.close()
self.marionette.switch_to_window(self.new_tab)
self.marionette.navigate(self.test_page_remote)
@skip("Bug 1334137 - Intermittent: Process killed because of hang in getCurrentUrl()")
@skip_if_mobile("Interacting with chrome elements not available for Fennec")
def test_type_to_non_remote_tab(self):
self.marionette.navigate(self.test_page_not_remote)
self.assertFalse(self.is_remote_tab)
with self.marionette.using_context("chrome"):
urlbar = self.marionette.find_element(By.ID, "urlbar")
urlbar.send_keys(self.mod_key + "a")
urlbar.send_keys(self.mod_key + "x")
urlbar.send_keys("about:support" + Keys.ENTER)
Wait(self.marionette).until(
lambda mn: mn.get_url() == "about:support",
message="'about:support' hasn't been loaded")
self.assertFalse(self.is_remote_tab)
@skip_if_mobile("Interacting with chrome elements not available for Fennec")
@run_if_e10s("Requires e10s mode enabled")
def test_type_to_remote_tab(self):
self.assertTrue(self.is_remote_tab)
with self.marionette.using_context("chrome"):
urlbar = self.marionette.find_element(By.ID, "urlbar")
urlbar.send_keys(self.mod_key + "a")
urlbar.send_keys(self.mod_key + "x")
urlbar.send_keys(self.test_page_remote + Keys.ENTER)
Wait(self.marionette).until(
lambda mn: mn.get_url() == self.test_page_remote,
message="'{}' hasn't been loaded".format(self.test_page_remote))
self.assertTrue(self.is_remote_tab)
@skip_if_mobile("On Android no shortcuts are available")
def test_navigate_shortcut_key(self):
def open_with_shortcut():
self.marionette.navigate(self.test_page_remote)
with self.marionette.using_context("chrome"):
main_win = self.marionette.find_element(By.ID, "main-window")
main_win.send_keys(self.mod_key, Keys.SHIFT, "a")
new_tab = self.open_tab(trigger=open_with_shortcut)
self.marionette.switch_to_window(new_tab)
Wait(self.marionette).until(lambda mn: mn.get_url() == "about:addons",
message="'about:addons' hasn't been loaded")
class TestBackForwardNavigation(BaseNavigationTestCase):
def run_bfcache_test(self, test_pages):
# Helper method to run simple back and forward testcases.
for index, page in enumerate(test_pages):
if "error" in page:
@ -59,6 +271,13 @@ class TestBackForwardNavigation(WindowManagerMixin, MarionetteTestCase):
self.assertEqual(page["url"], self.marionette.get_url())
self.assertEqual(self.history_length, index + 1)
if "is_remote" in page:
self.assertEqual(page["is_remote"], self.is_remote_tab,
"'{}' doesn't match expected remoteness state: {}".format(
page["url"], page["is_remote"]))
# Now going back in history for all test pages by backward iterating
# through the list (-1) and skipping the first entry at the end (-2).
for page in test_pages[-2::-1]:
if "error" in page:
with self.assertRaises(page["error"]):
@ -67,6 +286,12 @@ class TestBackForwardNavigation(WindowManagerMixin, MarionetteTestCase):
self.marionette.go_back()
self.assertEqual(page["url"], self.marionette.get_url())
if "is_remote" in page:
self.assertEqual(page["is_remote"], self.is_remote_tab,
"'{}' doesn't match expected remoteness state: {}".format(
page["url"], page["is_remote"]))
# Now going forward in history by skipping the first entry.
for page in test_pages[1::]:
if "error" in page:
with self.assertRaises(page["error"]):
@ -75,6 +300,11 @@ class TestBackForwardNavigation(WindowManagerMixin, MarionetteTestCase):
self.marionette.go_forward()
self.assertEqual(page["url"], self.marionette.get_url())
if "is_remote" in page:
self.assertEqual(page["is_remote"], self.is_remote_tab,
"'{}' doesn't match expected remoteness state: {}".format(
page["url"], page["is_remote"]))
def test_no_history_items(self):
# Both methods should not raise a failure if no navigation is possible
self.marionette.go_back()
@ -83,33 +313,33 @@ class TestBackForwardNavigation(WindowManagerMixin, MarionetteTestCase):
def test_data_urls(self):
test_pages = [
{"url": inline("<p>foobar</p>")},
{"url": self.test_page},
{"url": self.test_page_remote},
{"url": inline("<p>foobar</p>")},
]
self.run_test(test_pages)
self.run_bfcache_test(test_pages)
def test_same_document_hash_change(self):
test_pages = [
{"url": "{}#23".format(self.test_page)},
{"url": self.test_page},
{"url": "{}#42".format(self.test_page)},
{"url": "{}#23".format(self.test_page_remote)},
{"url": self.test_page_remote},
{"url": "{}#42".format(self.test_page_remote)},
]
self.run_test(test_pages)
self.run_bfcache_test(test_pages)
@skip("Causes crashes for JS GC (bug 1344863) and a11y (bug 1344868)")
def test_frameset(self):
test_pages = [
{"url": self.marionette.absolute_url("frameset.html")},
{"url": self.test_page},
{"url": self.test_page_remote},
{"url": self.marionette.absolute_url("frameset.html")},
]
self.run_test(test_pages)
self.run_bfcache_test(test_pages)
def test_frameset_after_navigating_in_frame(self):
test_element_locator = (By.ID, "email")
self.marionette.navigate(self.test_page)
self.assertEqual(self.marionette.get_url(), self.test_page)
self.marionette.navigate(self.test_page_remote)
self.assertEqual(self.marionette.get_url(), self.test_page_remote)
self.assertEqual(self.history_length, 1)
page = self.marionette.absolute_url("frameset.html")
self.marionette.navigate(page)
@ -137,7 +367,7 @@ class TestBackForwardNavigation(WindowManagerMixin, MarionetteTestCase):
# Go back to the non-frameset page
self.marionette.switch_to_parent_frame()
self.marionette.go_back()
self.assertEqual(self.marionette.get_url(), self.test_page)
self.assertEqual(self.marionette.get_url(), self.test_page_remote)
# Go forward to the frameset page
self.marionette.go_forward()
@ -151,47 +381,52 @@ class TestBackForwardNavigation(WindowManagerMixin, MarionetteTestCase):
self.marionette.find_element(*test_element_locator)
self.assertEqual(self.marionette.get_url(), page)
def test_image_document_to_html(self):
def test_image_to_html_to_image(self):
test_pages = [
{"url": self.marionette.absolute_url('black.png')},
{"url": self.test_page},
{"url": self.marionette.absolute_url('white.png')},
{"url": self.marionette.absolute_url("black.png")},
{"url": self.test_page_remote},
{"url": self.marionette.absolute_url("white.png")},
]
self.run_test(test_pages)
self.run_bfcache_test(test_pages)
def test_image_document_to_image_document(self):
def test_image_to_image(self):
test_pages = [
{"url": self.marionette.absolute_url('black.png')},
{"url": self.marionette.absolute_url('white.png')},
{"url": self.marionette.absolute_url("black.png")},
{"url": self.marionette.absolute_url("white.png")},
]
self.run_test(test_pages)
self.run_bfcache_test(test_pages)
@run_if_e10s("Requires e10s mode enabled")
def test_remoteness_change(self):
# TODO: Verify that a remoteness change happened
# like: self.assertNotEqual(self.marionette.current_window_handle, self.new_tab)
# about:robots is always a non-remote page for now
test_pages = [
{"url": "about:robots"},
{"url": self.test_page},
{"url": "about:robots"},
{"url": "about:robots", "is_remote": False},
{"url": self.test_page_remote, "is_remote": True},
{"url": "about:robots", "is_remote": False},
]
self.run_test(test_pages)
self.run_bfcache_test(test_pages)
@skip_if_mobile("Bug 1333209 - Process killed because of connection loss")
def test_non_remote_about_pages(self):
test_pages = [
{"url": "about:preferences", "is_remote": False},
{"url": "about:robots", "is_remote": False},
{"url": "about:support", "is_remote": False},
]
self.run_bfcache_test(test_pages)
def test_navigate_to_requested_about_page_after_error_page(self):
test_pages = [
{"url": "about:neterror"},
{"url": self.marionette.absolute_url("test.html")},
{"url": self.test_page_remote},
{"url": "about:blocked"},
]
self.run_test(test_pages)
self.run_bfcache_test(test_pages)
def test_timeout_error(self):
urls = [
self.marionette.absolute_url('slow'),
self.test_page,
self.marionette.absolute_url('slow'),
self.marionette.absolute_url("slow?delay=3"),
self.test_page_remote,
self.marionette.absolute_url("slow?delay=4"),
]
# First, load all pages completely to get them added to the cache
@ -204,172 +439,98 @@ class TestBackForwardNavigation(WindowManagerMixin, MarionetteTestCase):
self.assertEqual(urls[1], self.marionette.get_url())
# Force triggering a timeout error
self.marionette.timeout.page_load = 0.1
self.marionette.timeout.page_load = 0.5
with self.assertRaises(errors.TimeoutException):
self.marionette.go_back()
self.assertEqual(urls[0], self.marionette.get_url())
self.marionette.timeout.page_load = 300000
self.marionette.timeout.reset()
Wait(self.marionette, self.marionette.timeout.page_load).until(
lambda mn: urls[0] == mn.get_url(),
message="Slow loading page has been successfully loaded after going back")
self.assertEqual(self.marionette.find_element(By.ID, "delay").text, "3")
self.marionette.go_forward()
self.assertEqual(urls[1], self.marionette.get_url())
# Force triggering a timeout error
self.marionette.timeout.page_load = 0.1
self.marionette.timeout.page_load = 0.5
with self.assertRaises(errors.TimeoutException):
self.marionette.go_forward()
self.assertEqual(urls[2], self.marionette.get_url())
self.marionette.timeout.page_load = 300000
self.marionette.timeout.reset()
Wait(self.marionette, self.marionette.timeout.page_load).until(
lambda mn: urls[2] == mn.get_url(),
message="Slow loading page has been successfully loaded after going forward")
self.assertEqual(self.marionette.find_element(By.ID, "delay").text, "4")
def test_certificate_error(self):
test_pages = [
{"url": self.fixtures.where_is("/test.html", on="https"),
{"url": self.test_page_insecure,
"error": errors.InsecureCertificateException},
{"url": self.test_page},
{"url": self.fixtures.where_is("/test.html", on="https"),
{"url": self.test_page_remote},
{"url": self.test_page_insecure,
"error": errors.InsecureCertificateException},
]
self.run_test(test_pages)
self.run_bfcache_test(test_pages)
class TestNavigate(WindowManagerMixin, MarionetteTestCase):
class TestRefresh(BaseNavigationTestCase):
def setUp(self):
super(TestNavigate, self).setUp()
def test_basic(self):
self.marionette.navigate(self.test_page_remote)
self.assertEqual(self.test_page_remote, self.marionette.get_url())
self.marionette.navigate("about:")
self.test_doc = self.marionette.absolute_url("test.html")
self.iframe_doc = self.marionette.absolute_url("test_iframe.html")
self.marionette.execute_script("""
let elem = window.document.createElement('div');
elem.id = 'someDiv';
window.document.body.appendChild(elem);
""")
self.marionette.find_element(By.ID, "someDiv")
def tearDown(self):
self.marionette.timeout.reset()
self.close_all_tabs()
super(TestNavigate, self).tearDown()
@property
def location_href(self):
# Windows 8 has recently seen a proliferation of intermittent
# test failures to do with failing to compare "about:blank" ==
# u"about:blank". For the sake of consistenty, we encode the
# returned URL as Unicode here to ensure that the values are
# absolutely of the same type.
#
# (https://bugzilla.mozilla.org/show_bug.cgi?id=1322862)
return self.marionette.execute_script("return window.location.href").encode("utf-8")
def test_set_location_through_execute_script(self):
self.marionette.execute_script(
"window.location.href = '%s'" % self.test_doc)
Wait(self.marionette).until(
lambda _: self.test_doc == self.location_href)
self.assertEqual("Marionette Test", self.marionette.title)
def test_navigate_chrome_error(self):
with self.marionette.using_context("chrome"):
self.assertRaises(errors.UnsupportedOperationException,
self.marionette.navigate, "about:blank")
self.assertRaises(errors.UnsupportedOperationException, self.marionette.go_back)
self.assertRaises(errors.UnsupportedOperationException, self.marionette.go_forward)
self.assertRaises(errors.UnsupportedOperationException, self.marionette.refresh)
def test_get_current_url_returns_top_level_browsing_context_url(self):
self.marionette.navigate(self.iframe_doc)
self.assertEqual(self.iframe_doc, self.location_href)
frame = self.marionette.find_element(By.CSS_SELECTOR, "#test_iframe")
self.marionette.switch_to_frame(frame)
self.assertEqual(self.iframe_doc, self.marionette.get_url())
def test_get_current_url(self):
self.marionette.navigate(self.test_doc)
self.assertEqual(self.test_doc, self.marionette.get_url())
self.marionette.navigate("about:blank")
self.assertEqual("about:blank", self.marionette.get_url())
def test_refresh(self):
self.marionette.navigate(self.test_doc)
self.assertEqual("Marionette Test", self.marionette.title)
self.assertTrue(self.marionette.execute_script(
"""var elem = window.document.createElement('div'); elem.id = 'someDiv';
window.document.body.appendChild(elem); return true;"""))
self.assertFalse(self.marionette.execute_script(
"return window.document.getElementById('someDiv') == undefined"))
self.marionette.refresh()
# TODO(ato): Bug 1291320
time.sleep(0.2)
self.assertEqual("Marionette Test", self.marionette.title)
self.assertTrue(self.marionette.execute_script(
"return window.document.getElementById('someDiv') == undefined"))
self.assertEqual(self.test_page_remote, self.marionette.get_url())
with self.assertRaises(errors.NoSuchElementException):
self.marionette.find_element(By.ID, "someDiv")
def test_navigate_in_child_frame_changes_to_top(self):
frame_html = self.marionette.absolute_url("frameset.html")
def test_refresh_in_child_frame_navigates_to_top(self):
self.marionette.navigate(self.test_page_frameset)
self.assertEqual(self.test_page_frameset, self.marionette.get_url())
self.marionette.navigate(frame_html)
frame = self.marionette.find_element(By.NAME, "third")
self.marionette.switch_to_frame(frame)
self.assertRaises(errors.NoSuchElementException,
self.marionette.find_element, By.NAME, "third")
self.marionette.navigate(frame_html)
self.marionette.refresh()
self.marionette.find_element(By.NAME, "third")
@skip_if_mobile("Bug 1323755 - Socket timeout")
def test_invalid_protocol(self):
with self.assertRaises(errors.MarionetteException):
self.marionette.navigate("thisprotocoldoesnotexist://")
def test_image(self):
image = self.marionette.absolute_url('black.png')
def test_find_element_state_complete(self):
self.marionette.navigate(self.test_doc)
state = self.marionette.execute_script(
"return window.document.readyState")
self.assertEqual("complete", state)
self.assertTrue(self.marionette.find_element(By.ID, "mozLink"))
self.marionette.navigate(image)
self.assertEqual(image, self.marionette.get_url())
def test_error_when_exceeding_page_load_timeout(self):
self.marionette.timeout.page_load = 0.1
self.marionette.refresh()
self.assertEqual(image, self.marionette.get_url())
def test_timeout_error(self):
slow_page = self.marionette.absolute_url("slow?delay=3")
self.marionette.navigate(slow_page)
self.assertEqual(slow_page, self.marionette.get_url())
self.marionette.timeout.page_load = 0.5
with self.assertRaises(errors.TimeoutException):
self.marionette.navigate(self.marionette.absolute_url("slow"))
self.marionette.refresh()
self.assertEqual(slow_page, self.marionette.get_url())
def test_navigate_to_same_image_document_twice(self):
self.marionette.navigate(self.fixtures.where_is("black.png"))
self.assertIn("black.png", self.marionette.title)
self.marionette.navigate(self.fixtures.where_is("black.png"))
self.assertIn("black.png", self.marionette.title)
def test_insecure_error(self):
with self.assertRaises(errors.InsecureCertificateException):
self.marionette.navigate(self.test_page_insecure)
self.assertEqual(self.test_page_insecure, self.marionette.get_url())
def test_navigate_hash_change(self):
doc = inline("<p id=foo>")
self.marionette.navigate(doc)
self.marionette.execute_script("window.visited = true", sandbox=None)
self.marionette.navigate("{}#foo".format(doc))
self.assertTrue(self.marionette.execute_script(
"return window.visited", sandbox=None))
@skip_if_mobile("Bug 1334095 - Timeout: No new tab has been opened")
def test_about_blank_for_new_docshell(self):
""" Bug 1312674 - Hang when loading about:blank for a new docshell."""
def open_with_link():
link = self.marionette.find_element(By.ID, "new-blank-tab")
link.click()
# Open a new tab to get a new docshell created
self.marionette.navigate(self.marionette.absolute_url("windowHandles.html"))
new_tab = self.open_tab(trigger=open_with_link)
self.marionette.switch_to_window(new_tab)
self.assertEqual(self.marionette.get_url(), "about:blank")
self.marionette.navigate('about:blank')
self.marionette.close()
self.marionette.switch_to_window(self.start_window)
@run_if_manage_instance("Only runnable if Marionette manages the instance")
@skip_if_mobile("Bug 1322993 - Missing temporary folder")
def test_focus_after_navigation(self):
self.marionette.quit()
self.marionette.start_session()
self.marionette.navigate(inline("<input autofocus>"))
active_el = self.marionette.execute_script("return document.activeElement")
focus_el = self.marionette.find_element(By.CSS_SELECTOR, ":focus")
self.assertEqual(active_el, focus_el)
with self.assertRaises(errors.InsecureCertificateException):
self.marionette.refresh()
class TestTLSNavigation(MarionetteTestCase):
@ -377,7 +538,10 @@ class TestTLSNavigation(MarionetteTestCase):
secure_tls = {"acceptInsecureCerts": False}
def setUp(self):
MarionetteTestCase.setUp(self)
super(TestTLSNavigation, self).setUp()
self.test_page_insecure = self.fixtures.where_is("test.html", on="https")
self.marionette.delete_session()
self.capabilities = self.marionette.start_session(
{"requiredCapabilities": self.insecure_tls})
@ -387,7 +551,8 @@ class TestTLSNavigation(MarionetteTestCase):
self.marionette.delete_session()
except:
pass
MarionetteTestCase.tearDown(self)
super(TestTLSNavigation, self).tearDown()
@contextlib.contextmanager
def safe_session(self):
@ -410,19 +575,18 @@ class TestTLSNavigation(MarionetteTestCase):
self.marionette.delete_session()
def test_navigate_by_command(self):
self.marionette.navigate(
self.fixtures.where_is("/test.html", on="https"))
self.marionette.navigate(self.test_page_insecure)
self.assertIn("https", self.marionette.get_url())
def test_navigate_by_click(self):
link_url = self.fixtures.where_is("/test.html", on="https")
link_url = self.test_page_insecure
self.marionette.navigate(
inline("<a href=%s>https is the future</a>" % link_url))
self.marionette.find_element(By.TAG_NAME, "a").click()
self.assertIn("https", self.marionette.get_url())
def test_deactivation(self):
invalid_cert_url = self.fixtures.where_is("/test.html", on="https")
invalid_cert_url = self.test_page_insecure
print "with safe session"
with self.safe_session() as session:

View File

@ -32,8 +32,6 @@ skip-if = true # "Bug 896046"
[test_log.py]
[test_about_pages.py]
[test_execute_async_script.py]
[test_execute_script.py]
[test_simpletest_fail.js]

View File

@ -78,9 +78,6 @@ var originalOnError;
var checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
//timer for readystate
var readyStateTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
// timer for navigation commands.
var navTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
var onDOMContentLoaded;
// Send move events about this often
var EVENT_INTERVAL = 30; // milliseconds
// last touch for each fingerId
@ -112,6 +109,186 @@ var modalHandler = function() {
var sandboxes = new Sandboxes(() => curContainer.frame);
var sandboxName = "default";
/**
* The load listener singleton helps to keep track of active page load activities,
* and can be used by any command which might cause a navigation to happen. In the
* specific case of remoteness changes it allows to continue observing the current
* page load.
*/
var loadListener = {
command_id: null,
timeout: null,
timer: null,
/**
* Start listening for page unload/load events.
*
* @param {number} command_id
* ID of the currently handled message between the driver and listener.
* @param {number} timeout
* Timeout in seconds the method has to wait for the page being finished loading.
* @param {number} startTime
* Unix timestap when the navitation request got triggered.
* @param {boolean=} waitForUnloaded
* If `true` wait for page unload events, otherwise only for page load events.
*/
start: function (command_id, timeout, startTime, waitForUnloaded = true) {
this.command_id = command_id;
this.timeout = timeout;
// In case of a remoteness change, only wait the remaining time
timeout = startTime + timeout - new Date().getTime();
if (timeout <= 0) {
this.notify();
return;
}
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this.timer.initWithCallback(this, timeout, Ci.nsITimer.TYPE_ONE_SHOT);
if (waitForUnloaded) {
addEventListener("hashchange", this, false);
addEventListener("pagehide", this, false);
} else {
addEventListener("DOMContentLoaded", loadListener, false);
addEventListener("pageshow", loadListener, false);
}
},
/**
* Stop listening for page unload/load events.
*/
stop: function () {
if (this.timer) {
this.timer.cancel();
this.timer = null;
}
removeEventListener("hashchange", this);
removeEventListener("pagehide", this);
removeEventListener("DOMContentLoaded", this);
removeEventListener("pageshow", this);
},
/**
* Callback for registered DOM events.
*/
handleEvent: function (event) {
switch (event.type) {
case "pagehide":
if (event.originalTarget === curContainer.frame.document) {
removeEventListener("hashchange", this);
removeEventListener("pagehide", this);
// Now wait until the target page has been loaded
addEventListener("DOMContentLoaded", this, false);
addEventListener("pageshow", this, false);
}
break;
case "hashchange":
this.stop();
sendOk(this.command_id);
break;
case "DOMContentLoaded":
if (event.originalTarget.baseURI.startsWith("about:certerror")) {
this.stop();
sendError(new InsecureCertificateError(), this.command_id);
} else if (/about:.*(error)\?/.exec(event.originalTarget.baseURI)) {
this.stop();
sendError(new UnknownError("Reached error page: " +
event.originalTarget.baseURI), this.command_id);
// Special-case about:blocked pages which should be treated as non-error
// pages but do not raise a pageshow event.
} else if (/about:blocked\?/.exec(event.originalTarget.baseURI)) {
this.stop();
sendOk(this.command_id);
}
break;
case "pageshow":
if (event.originalTarget === curContainer.frame.document) {
this.stop();
sendOk(this.command_id);
}
break;
}
},
/**
* Callback for navigation timeout timer.
*/
notify: function (timer) {
this.stop();
sendError(new TimeoutError("Timeout loading page after " + this.timeout + "ms"),
this.command_id);
},
/**
* Continue to listen for page load events after a remoteness change happened.
*
* @param {number} command_id
* ID of the currently handled message between the driver and listener.
* @param {number} timeout
* Timeout in milliseconds the method has to wait for the page being finished loading.
* @param {number} startTime
* Unix timestap when the navitation request got triggered.
*/
waitForLoadAfterRemotenessChange: function (command_id, timeout, startTime) {
this.start(command_id, timeout, startTime, false);
},
/**
* Use a trigger callback to initiate a page load, and attach listeners if
* a page load is expected.
*
* @param {function} trigger
* Callback that triggers the page load.
* @param {number} command_id
* ID of the currently handled message between the driver and listener.
* @param {number} pageTimeout
* Timeout in milliseconds the method has to wait for the page being finished loading.
* @param {string=} url
* Optional URL, which is used to check if a page load is expected.
*/
navigate: function (trigger, command_id, timeout, url = undefined) {
let loadEventExpected = true;
if (typeof url == "string") {
try {
let requestedURL = new URL(url).toString();
loadEventExpected = navigate.isLoadEventExpected(requestedURL);
} catch (e) {
sendError(new InvalidArgumentError("Malformed URL: " + e.message), command_id);
return;
}
}
if (loadEventExpected) {
let startTime = new Date().getTime();
this.start(command_id, timeout, startTime, true);
}
try {
trigger();
} catch (e) {
if (loadEventExpected) {
this.stop();
}
sendError(new UnknownCommandError(e.message), command_id);
return;
}
if (!loadEventExpected) {
sendOk(command_id);
}
},
}
/**
* Called when listener is first started up.
* The listener sends its unique window ID and its current URI to the actor.
@ -183,7 +360,7 @@ function dispatch(fn) {
return function (msg) {
let id = msg.json.command_id;
let req = Task.spawn(function*() {
let req = Task.spawn(function* () {
if (typeof msg.json == "undefined" || msg.json instanceof Array) {
return yield fn.apply(null, msg.json);
} else {
@ -265,7 +442,7 @@ function startListeners() {
addMessageListenerId("Marionette:actionChain", actionChainFn);
addMessageListenerId("Marionette:multiAction", multiActionFn);
addMessageListenerId("Marionette:get", get);
addMessageListenerId("Marionette:pollForReadyState", pollForReadyState);
addMessageListenerId("Marionette:waitForPageLoaded", waitForPageLoaded);
addMessageListenerId("Marionette:cancelRequest", cancelRequest);
addMessageListenerId("Marionette:getCurrentUrl", getCurrentUrlFn);
addMessageListenerId("Marionette:getTitle", getTitleFn);
@ -370,7 +547,7 @@ function deleteSession(msg) {
removeMessageListenerId("Marionette:actionChain", actionChainFn);
removeMessageListenerId("Marionette:multiAction", multiActionFn);
removeMessageListenerId("Marionette:get", get);
removeMessageListenerId("Marionette:pollForReadyState", pollForReadyState);
removeMessageListenerId("Marionette:waitForPageLoaded", waitForPageLoaded);
removeMessageListenerId("Marionette:cancelRequest", cancelRequest);
removeMessageListenerId("Marionette:getTitle", getTitleFn);
removeMessageListenerId("Marionette:getPageSource", getPageSourceFn);
@ -447,8 +624,8 @@ function sendToServer(uuid, data = undefined) {
* @param {UUID} uuid
* Unique identifier of the request.
*/
function sendResponse(obj, id) {
sendToServer(id, obj);
function sendResponse(obj, uuid) {
sendToServer(uuid, obj);
}
/**
@ -885,84 +1062,31 @@ function multiAction(args, maxLen) {
setDispatch(concurrentEvent, pendingTouches);
}
/**
* Cancel the polling and remove the event listener associated with a
* current navigation request in case we're interupted by an onbeforeunload
* handler and navigation doesn't complete.
*/
function cancelRequest() {
loadListener.stop();
}
/**
* This implements the latter part of a get request (for the case we need to resume one
* when a remoteness update happens in the middle of a navigate request). This is most of
* of the work of a navigate request, but doesn't assume DOMContentLoaded is yet to fire.
*
* @param {function=} cleanupCallback
* Callback to execute when registered event handlers or observer notifications
* have to be cleaned-up.
* @param {number} command_id
* ID of the currently handled message between the driver and listener.
* @param {string=} lastSeenURL
* Last URL as seen before the navigation request got triggered.
* @param {number} pageTimeout
* Timeout in seconds the method has to wait for the page being finished loading.
* @param {number} startTime
* Unix timestap when the navitation request got triggred.
*/
function pollForReadyState(msg) {
let {cleanupCallback, command_id, lastSeenURL, pageTimeout, startTime} = msg.json;
function waitForPageLoaded(msg) {
let {command_id, pageTimeout, startTime} = msg.json;
if (typeof startTime == "undefined") {
startTime = new Date().getTime();
}
if (typeof cleanupCallback == "undefined") {
cleanupCallback = () => {};
}
let endTime = startTime + pageTimeout;
let checkLoad = () => {
navTimer.cancel();
let doc = curContainer.frame.document;
if (pageTimeout === null || new Date().getTime() <= endTime) {
// Under some conditions (eg. for error pages) the pagehide event is fired
// even with a readyState complete for the formerly loaded page.
// To prevent race conditition for goBack and goForward we have to wait
// until the last seen page has been fully unloaded.
// TODO: Bug 1333458 has to improve this.
if (!doc.location || lastSeenURL && doc.location.href === lastSeenURL) {
navTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT);
// document fully loaded
} else if (doc.readyState === "complete") {
cleanupCallback();
sendOk(command_id);
// document with an insecure cert
} else if (doc.readyState === "interactive" &&
doc.baseURI.startsWith("about:certerror")) {
cleanupCallback();
sendError(new InsecureCertificateError(), command_id);
// we have reached an error url without requesting it
} else if (doc.readyState === "interactive" &&
/about:.+(error)\?/.exec(doc.baseURI)) {
cleanupCallback();
sendError(new UnknownError("Reached error page: " + doc.baseURI), command_id);
// return early for about: urls
} else if (doc.readyState === "interactive" && doc.baseURI.startsWith("about:")) {
cleanupCallback();
sendOk(command_id);
// document not fully loaded
} else {
navTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT);
}
} else {
cleanupCallback();
sendError(new TimeoutError("Error loading page, timed out (checkLoad)"), command_id);
}
};
checkLoad();
loadListener.waitForLoadAfterRemotenessChange(command_id, pageTimeout, startTime);
}
/**
@ -972,163 +1096,69 @@ function pollForReadyState(msg) {
* driver (in chrome space).
*/
function get(msg) {
let {pageTimeout, url, command_id} = msg.json;
let startTime = new Date().getTime();
let {command_id, pageTimeout, url} = msg.json;
// We need to move to the top frame before navigating
sendSyncMessage("Marionette:switchedToFrame", {frameValue: null});
curContainer.frame = content;
let docShell = curContainer.frame
.document
.defaultView
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress);
let sawLoad = false;
let requestedURL;
let loadEventExpected = false;
try {
requestedURL = new URL(url).toString();
let curURL = curContainer.frame.location;
loadEventExpected = navigate.isLoadEventExpected(curURL, requestedURL);
} catch (e) {
sendError(new InvalidArgumentError("Malformed URL: " + e.message), command_id);
return;
}
// It's possible that a site we're being sent to will end up redirecting
// us before we end up on a page that fires DOMContentLoaded. We can ensure
// This loadListener ensures that we don't send a success signal back to
// the caller until we've seen the load of the requested URL attempted
// on this frame.
let loadListener = {
QueryInterface: XPCOMUtils.generateQI(
[Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]),
onStateChange(webProgress, request, state, status) {
if (!(request instanceof Ci.nsIChannel)) {
return;
}
const isDocument = state & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
const loadedURL = request.URI.spec;
// We have to look at the originalURL because of about: pages,
// the loadedURL is what the about: page resolves to, and is
// not the one that was requested.
const originalURL = request.originalURI.spec;
const isRequestedURL = loadedURL == requestedURL ||
originalURL == requestedURL;
if (!isDocument || !isRequestedURL) {
return;
}
// We started loading the requested document. This document
// might not be the one that ends up firing DOMContentLoaded
// (if it, for example, redirects), but because we've started
// loading this URL, we know that any future DOMContentLoaded's
// are fair game to tell the Marionette client about.
if (state & Ci.nsIWebProgressListener.STATE_START) {
sawLoad = true;
}
// This indicates network stop or last request stop outside of
// loading the document. We hit this when DOMContentLoaded is
// not triggered, which is the case for image documents.
else if (state & Ci.nsIWebProgressListener.STATE_STOP &&
content.document instanceof content.ImageDocument) {
pollForReadyState({json: {
command_id: command_id,
pageTimeout: pageTimeout,
startTime: startTime,
cleanupCallback: () => {
webProgress.removeProgressListener(loadListener);
removeEventListener("DOMContentLoaded", onDOMContentLoaded, false);
}
}});
}
},
onLocationChange() {},
onProgressChange() {},
onStatusChange() {},
onSecurityChange() {},
};
webProgress.addProgressListener(
loadListener, Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
// Prevent DOMContentLoaded events from frames from invoking this
// code, unless the event is coming from the frame associated with
// the current window (i.e. someone has used switch_to_frame).
onDOMContentLoaded = ev => {
let frameEl = ev.originalTarget.defaultView.frameElement;
let correctFrame = !frameEl || frameEl == curContainer.frame.frameElement;
// If the page we're at fired DOMContentLoaded and appears to
// be the one we asked to load, then we definitely saw the load
// occur. We need this because for error pages, like about:neterror
// for unsupported protocols, we don't end up opening a channel that
// our WebProgressListener can monitor.
if (curContainer.frame.location == requestedURL) {
sawLoad = true;
}
// We also need to make sure that if the requested URL is not
// about:blank the DOMContentLoaded we saw isn't for the initial
// about:blank of a newly created docShell.
let loadedRequestedURI = (requestedURL == "about:blank") ||
docShell.hasLoadedNonBlankURI;
if (correctFrame && sawLoad && loadedRequestedURI) {
pollForReadyState({json: {
command_id: command_id,
pageTimeout: pageTimeout,
startTime: startTime,
cleanupCallback: () => {
webProgress.removeProgressListener(loadListener);
removeEventListener("DOMContentLoaded", onDOMContentLoaded, false);
}
}});
}
};
if (typeof pageTimeout != "undefined") {
let onTimeout = () => {
if (loadEventExpected) {
removeEventListener("DOMContentLoaded", onDOMContentLoaded, false);
}
webProgress.removeProgressListener(loadListener);
sendError(new TimeoutError("Error loading page, timed out (onDOMContentLoaded)"), command_id);
};
navTimer.initWithCallback(onTimeout, pageTimeout, Ci.nsITimer.TYPE_ONE_SHOT);
}
if (loadEventExpected) {
addEventListener("DOMContentLoaded", onDOMContentLoaded, false);
}
curContainer.frame.location = requestedURL;
if (!loadEventExpected) {
sendOk(command_id);
}
loadListener.navigate(() => {
curContainer.frame.location = url;
}, command_id, pageTimeout, url);
}
/**
* Cancel the polling and remove the event listener associated with a
* current navigation request in case we're interupted by an onbeforeunload
* handler and navigation doesn't complete.
* Cause the browser to traverse one step backward in the joint history
* of the current browsing context.
*
* @param {number} command_id
* ID of the currently handled message between the driver and listener.
* @param {number} pageTimeout
* Timeout in milliseconds the method has to wait for the page being finished loading.
*/
function cancelRequest() {
navTimer.cancel();
if (onDOMContentLoaded) {
removeEventListener("DOMContentLoaded", onDOMContentLoaded, false);
}
function goBack(msg) {
let {command_id, pageTimeout} = msg.json;
loadListener.navigate(() => {
curContainer.frame.history.back();
}, command_id, pageTimeout);
}
/**
* Cause the browser to traverse one step forward in the joint history
* of the current browsing context.
*
* @param {number} command_id
* ID of the currently handled message between the driver and listener.
* @param {number} pageTimeout
* Timeout in milliseconds the method has to wait for the page being finished loading.
*/
function goForward(msg) {
let {command_id, pageTimeout} = msg.json;
loadListener.navigate(() => {
curContainer.frame.history.forward();
}, command_id, pageTimeout);
}
/**
* Causes the browser to reload the page in in current top-level browsing context.
*
* @param {number} command_id
* ID of the currently handled message between the driver and listener.
* @param {number} pageTimeout
* Timeout in milliseconds the method has to wait for the page being finished loading.
*/
function refresh(msg) {
let {command_id, pageTimeout} = msg.json;
// We need to move to the top frame before navigating
sendSyncMessage("Marionette:switchedToFrame", {frameValue: null});
curContainer.frame = content;
loadListener.navigate(() => {
curContainer.frame.location.reload(true);
}, command_id, pageTimeout);
}
/**
@ -1152,130 +1182,6 @@ function getPageSource() {
return curContainer.frame.document.documentElement.outerHTML;
}
/**
* Wait for the current page to be unloaded after a navigation got triggered.
*
* @param {function} trigger
* Callback to execute which triggers a page navigation.
* @param {function} doneCallback
* Callback to execute when the current page has been unloaded.
*
* It receives a dictionary with the following items as argument:
* loading - Flag if a page load will follow.
* lastSeenURL - Last seen URL before the navigation request.
* startTime - Time when the navigation request has been triggered.
*/
function waitForPageUnloaded(trigger, doneCallback) {
let currentURL = curContainer.frame.location.href;
let start = new Date().getTime();
function handleEvent(event) {
// In case of a remoteness change it can happen that we are no longer able
// to access the document's location. In those cases ignore the event,
// but keep the code waiting, and assume in the driver that waiting for the
// page load is necessary. Bug 1333458 should improve things.
if (typeof event.originalTarget.location == "undefined") {
return;
}
switch (event.type) {
case "hashchange":
removeEventListener("hashchange", handleEvent);
removeEventListener("pagehide", handleEvent);
removeEventListener("unload", handleEvent);
doneCallback({loading: false, lastSeenURL: currentURL});
break;
case "pagehide":
case "unload":
if (event.originalTarget === curContainer.frame.document) {
removeEventListener("hashchange", handleEvent);
removeEventListener("pagehide", handleEvent);
removeEventListener("unload", handleEvent);
doneCallback({loading: true, lastSeenURL: currentURL, startTime: start});
}
break;
}
}
addEventListener("hashchange", handleEvent, false);
addEventListener("pagehide", handleEvent, false);
addEventListener("unload", handleEvent, false);
trigger();
}
/**
* Cause the browser to traverse one step backward in the joint history
* of the current browsing context.
*
* @param {number} command_id
* ID of the currently handled message between the driver and listener.
* @param {number} pageTimeout
* Timeout in milliseconds the method has to wait for the page being finished loading.
*/
function goBack(msg) {
let {command_id, pageTimeout} = msg.json;
waitForPageUnloaded(() => {
curContainer.frame.history.back();
}, pageLoadStatus => {
if (pageLoadStatus.loading) {
pollForReadyState({json: {
command_id: command_id,
lastSeenURL: pageLoadStatus.lastSeenURL,
pageTimeout: pageTimeout,
startTime: pageLoadStatus.startTime,
}});
} else {
sendOk(command_id);
}
});
}
/**
* Cause the browser to traverse one step forward in the joint history
* of the current browsing context.
*
* @param {number} command_id
* ID of the currently handled message between the driver and listener.
* @param {number} pageTimeout
* Timeout in milliseconds the method has to wait for the page being finished loading.
*/
function goForward(msg) {
let {command_id, pageTimeout} = msg.json;
waitForPageUnloaded(() => {
curContainer.frame.history.forward();
}, pageLoadStatus => {
if (pageLoadStatus.loading) {
pollForReadyState({json: {
command_id: command_id,
lastSeenURL: pageLoadStatus.lastSeenURL,
pageTimeout: pageTimeout,
startTime: pageLoadStatus.startTime,
}});
} else {
sendOk(command_id);
}
});
}
/**
* Refresh the page
*/
function refresh(msg) {
let command_id = msg.json.command_id;
curContainer.frame.location.reload(true);
let listen = function() {
removeEventListener("DOMContentLoaded", listen, false);
sendOk(command_id);
};
addEventListener("DOMContentLoaded", listen, false);
}
/**
* Find an element in the current browsing context's document using the
* given search strategy.

View File

@ -14,106 +14,30 @@ this.navigate = {};
/**
* Determines if we expect to get a DOM load event (DOMContentLoaded)
* on navigating to the |future| URL.
* on navigating to the |url|.
*
* @param {string} current
* URL the browser is currently visiting.
* @param {string=} future
* Destination URL, if known.
* @param {string} url
* Destination URL
*
* @return {boolean}
* Full page load would be expected if future is followed.
* Full page load would be expected if url gets loaded.
*
* @throws TypeError
* If |current| is not defined, or any of |current| or |future|
* are invalid URLs.
* If |url| is an invalid URL.
*/
navigate.isLoadEventExpected = function (current, future = undefined) {
if (typeof current == "undefined") {
throw TypeError("Expected at least one URL");
}
navigate.isLoadEventExpected = function (url) {
// assume we will go somewhere exciting
if (typeof future == "undefined") {
return true;
if (typeof url == "undefined") {
throw TypeError("Expected destination URL");
}
let cur = new navigate.IdempotentURL(current);
let fut = new navigate.IdempotentURL(future);
// assume javascript:<whatever> will modify current document
// but this is not an entirely safe assumption to make,
// considering it could be used to set window.location
if (fut.protocol == "javascript:") {
return false;
}
// navigating to same url, but with any hash
if (cur.origin == fut.origin &&
cur.pathname == fut.pathname &&
fut.hash != "") {
return false;
switch (new URL(url).protocol) {
// assume javascript:<whatever> will modify current document
// but this is not an entirely safe assumption to make,
// considering it could be used to set window.location
case "javascript:":
return false;
}
return true;
};
/**
* Sane URL implementation that normalises URL fragments (hashes) and
* path names for "data:" URLs, and makes them idempotent.
*
* At the time of writing this, the web is approximately 10 000 days (or
* ~27.39 years) old. One should think that by this point we would have
* solved URLs. The following code is prudent example that we have not.
*
* When a URL with a fragment identifier but no explicit name for the
* fragment is given, i.e. "#", the {@code hash} property a {@code URL}
* object computes is an empty string. This is incidentally the same as
* the default value of URLs without fragments, causing a lot of confusion.
*
* This means that the URL "http://a/#b" produces a hash of "#b", but that
* "http://a/#" produces "". This implementation rectifies this behaviour
* by returning the actual full fragment, which is "#".
*
* "data:" URLs that contain fragments, which if they have the same origin
* and path name are not meant to cause a page reload on navigation,
* confusingly adds the fragment to the {@code pathname} property.
* This implementation remedies this behaviour by trimming it off.
*
* The practical result of this is that while {@code URL} objects are
* not idempotent, the returned URL elements from this implementation
* guarantees that |url.hash == url.hash|.
*
* @param {string|URL} o
* Object to make an URL of.
*
* @return {navigate.IdempotentURL}
* Considered by some to be a somewhat saner URL.
*
* @throws TypeError
* If |o| is not a valid type or if is a string that cannot be parsed
* as a URL.
*/
navigate.IdempotentURL = function (o) {
let url = new URL(o);
let hash = url.hash;
if (hash == "" && url.href[url.href.length - 1] == "#") {
hash = "#";
}
return {
hash: hash,
host: url.host,
hostname: url.hostname,
href: url.href,
origin: url.origin,
password: url.password,
pathname: url.pathname,
port: url.port,
protocol: url.protocol,
search: url.search,
searchParams: url.searchParams,
username: url.username,
};
};

View File

@ -10,58 +10,10 @@ Cu.import("chrome://marionette/content/navigate.js");
add_test(function test_isLoadEventExpected() {
Assert.throws(() => navigate.isLoadEventExpected(undefined),
/Expected at least one URL/);
/Expected destination URL/);
equal(true, navigate.isLoadEventExpected("http://a/"));
equal(true, navigate.isLoadEventExpected("http://a/", "http://a/"));
equal(true, navigate.isLoadEventExpected("http://a/", "http://a/b"));
equal(true, navigate.isLoadEventExpected("http://a/", "http://b"));
equal(true, navigate.isLoadEventExpected("http://a/", "data:text/html;charset=utf-8,foo"));
equal(true, navigate.isLoadEventExpected("about:blank", "http://a/"));
equal(true, navigate.isLoadEventExpected("http://a/", "about:blank"));
equal(true, navigate.isLoadEventExpected("http://a/", "https://a/"));
equal(false, navigate.isLoadEventExpected("http://a/", "javascript:whatever"));
equal(false, navigate.isLoadEventExpected("http://a/", "http://a/#"));
equal(false, navigate.isLoadEventExpected("http://a/", "http://a/#b"));
equal(false, navigate.isLoadEventExpected("http://a/#b", "http://a/#b"));
equal(false, navigate.isLoadEventExpected("http://a/#b", "http://a/#c"));
equal(false, navigate.isLoadEventExpected("data:text/html;charset=utf-8,foo", "data:text/html;charset=utf-8,foo#bar"));
equal(false, navigate.isLoadEventExpected("data:text/html;charset=utf-8,foo", "data:text/html;charset=utf-8,foo#"));
run_next_test();
});
add_test(function test_IdempotentURL() {
Assert.throws(() => new navigate.IdempotentURL(undefined));
Assert.throws(() => new navigate.IdempotentURL(true));
Assert.throws(() => new navigate.IdempotentURL({}));
Assert.throws(() => new navigate.IdempotentURL(42));
// propagated URL properties
let u1 = new URL("http://a/b");
let u2 = new navigate.IdempotentURL(u1);
equal(u1.host, u2.host);
equal(u1.hostname, u2.hostname);
equal(u1.href, u2.href);
equal(u1.origin, u2.origin);
equal(u1.password, u2.password);
equal(u1.port, u2.port);
equal(u1.protocol, u2.protocol);
equal(u1.search, u2.search);
equal(u1.username, u2.username);
// specialisations
equal("#b", new navigate.IdempotentURL("http://a/#b").hash);
equal("#", new navigate.IdempotentURL("http://a/#").hash);
equal("", new navigate.IdempotentURL("http://a/").hash);
equal("#bar", new navigate.IdempotentURL("data:text/html;charset=utf-8,foo#bar").hash);
equal("#", new navigate.IdempotentURL("data:text/html;charset=utf-8,foo#").hash);
equal("", new navigate.IdempotentURL("data:text/html;charset=utf-8,foo").hash);
equal("/", new navigate.IdempotentURL("http://a/").pathname);
equal("/", new navigate.IdempotentURL("http://a/#b").pathname);
equal("text/html;charset=utf-8,foo", new navigate.IdempotentURL("data:text/html;charset=utf-8,foo#bar").pathname);
equal(false, navigate.isLoadEventExpected("javascript:whatever"));
run_next_test();
});

View File

@ -14,7 +14,6 @@ const global = this;
Cu.importGlobalProperties(["URL"]);
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -26,6 +25,8 @@ var {
instanceOf,
} = ExtensionUtils;
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "contentPolicyService",
"@mozilla.org/addons/content-policy;1",
"nsIAddonContentPolicy");

View File

@ -541,22 +541,25 @@ var LoginManagerContent = {
}
let clobberUsername = true;
let options = {
inputElement,
};
let form = LoginFormFactory.createFromField(inputElement);
if (inputElement.type == "password") {
clobberUsername = false;
}
this._fillForm(form, true, clobberUsername, true, true, loginsFound, recipes, options);
this._fillForm(form, loginsFound, recipes, {
inputElement,
autofillForm: true,
clobberUsername,
clobberPassword: true,
userTriggered: true,
});
},
loginsFound({ form, loginsFound, recipes }) {
let doc = form.ownerDocument;
let autofillForm = gAutofillForms && !PrivateBrowsingUtils.isContentWindowPrivate(doc.defaultView);
this._fillForm(form, autofillForm, false, false, false, loginsFound, recipes);
this._fillForm(form, loginsFound, recipes, {autofillForm});
},
/**
@ -648,7 +651,11 @@ var LoginManagerContent = {
if (usernameField == acInputField && passwordField) {
this._getLoginDataFromParent(acForm, { showMasterPassword: false })
.then(({ form, loginsFound, recipes }) => {
this._fillForm(form, true, false, true, true, loginsFound, recipes);
this._fillForm(form, loginsFound, recipes, {
autofillForm: true,
clobberPassword: true,
userTriggered: true,
});
})
.then(null, Cu.reportError);
} else {
@ -983,23 +990,33 @@ var LoginManagerContent = {
* in using the provided logins and recipes.
*
* @param {LoginForm} form
* @param {bool} autofillForm denotes if we should fill the form in automatically
* @param {bool} clobberUsername controls if an existing username can be overwritten.
* If this is false and an inputElement of type password
* is also passed, the username field will be ignored.
* If this is false and no inputElement is passed, if the username
* field value is not found in foundLogins, it will not fill the password.
* @param {bool} clobberPassword controls if an existing password value can be
* overwritten
* @param {bool} userTriggered is an indication of whether this filling was triggered by
* the user
* @param {nsILoginInfo[]} foundLogins is an array of nsILoginInfo that could be used for the form
* @param {Set} recipes that could be used to affect how the form is filled
* @param {Object} [options = {}] is a list of options for this method.
- [inputElement] is an optional target input element we want to fill
* @param {nsILoginInfo[]} foundLogins an array of nsILoginInfo that could be
used for the form
* @param {Set} recipes a set of recipes that could be used to affect how the
form is filled
* @param {Object} [options = {}] a list of options for this method
* @param {HTMLInputElement} [options.inputElement = null] an optional target
* input element we want to fill
* @param {bool} [options.autofillForm = false] denotes if we should fill the
* form in automatically
* @param {bool} [options.clobberUsername = false] controls if an existing
* username can be overwritten. If this is false and an inputElement
* of type password is also passed, the username field will be ignored.
* If this is false and no inputElement is passed, if the username
* field value is not found in foundLogins, it will not fill the
* password.
* @param {bool} [options.clobberPassword = false] controls if an existing
* password value can be overwritten
* @param {bool} [options.userTriggered = false] an indication of whether
* this filling was triggered by the user
*/
_fillForm(form, autofillForm, clobberUsername, clobberPassword,
userTriggered, foundLogins, recipes, {inputElement} = {}) {
_fillForm(form, foundLogins, recipes, {
inputElement = null,
autofillForm = false,
clobberUsername = false,
clobberPassword = false,
userTriggered = false,
} = {}) {
if (form instanceof Ci.nsIDOMHTMLFormElement) {
throw new Error("_fillForm should only be called with FormLike objects");
}

Some files were not shown because too many files have changed in this diff Show More