mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 10:44:56 +00:00
Merge mozilla-central to mozilla-inbound
This commit is contained in:
commit
a0fea1ad49
2
CLOBBER
2
CLOBBER
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
});
|
||||
|
@ -179,7 +179,7 @@ Bookmarks.prototype = {
|
||||
return { url, title };
|
||||
}
|
||||
return null;
|
||||
}).filter(e => !!e);
|
||||
}, this).filter(e => !!e);
|
||||
},
|
||||
};
|
||||
|
||||
|
Binary file not shown.
@ -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.");
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -36,6 +36,8 @@ parent:
|
||||
|
||||
sync GetGMPNodeId(nsString origin, nsString topLevelOrigin, nsString gmpName)
|
||||
returns (nsCString id);
|
||||
child:
|
||||
async BeginShutdown();
|
||||
};
|
||||
|
||||
} // namespace gmp
|
||||
|
@ -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");
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
|
@ -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() {
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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 **/
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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.");
|
||||
|
@ -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>
|
||||
|
@ -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 () {
|
||||
|
@ -38,7 +38,7 @@ EXPORTS += [
|
||||
'nsEditorCID.h',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
TESTING_JS_MODULES += [
|
||||
'AsyncSpellCheckTestHelper.jsm',
|
||||
]
|
||||
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
});
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 };
|
||||
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
26
servo/Cargo.lock
generated
@ -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"
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
})
|
||||
|
@ -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.
|
||||
|
@ -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(),
|
||||
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
@ -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
@ -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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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});
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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):
|
||||
|
@ -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)
|
@ -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:
|
||||
|
@ -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]
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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");
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user