Merge m-c to autoland, a=merge

MozReview-Commit-ID: HVH8TbXbw7N
This commit is contained in:
Wes Kocher 2017-02-01 16:30:50 -08:00
Родитель 6bcf4f072e 1121c7a402
Коммит f5eab47fb9
152 изменённых файлов: 18517 добавлений и 16726 удалений

Просмотреть файл

@ -76,9 +76,7 @@ ProxyAccessibleBase<Derived>::ClearChildDoc(DocAccessibleParent* aChildDoc)
// in SetChildDoc(). This could result in two subsequent calls to
// ClearChildDoc() even though mChildren.Length() == 1.
MOZ_ASSERT(mChildren.Length() <= 1);
if (mChildren.RemoveElement(aChildDoc)) {
mOuterDoc = false;
}
mChildren.RemoveElement(aChildDoc);
}
template <class Derived>

Просмотреть файл

@ -3526,6 +3526,9 @@
<certItem issuerName="MFExCzAJBgNVBAYTAkpQMRMwEQYDVQQKEwpGdWppIFhlcm94MS0wKwYDVQQDEyRGdWppIFhlcm94IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IDI=">
<serialNumber>AUa47POQ1dN5</serialNumber>
</certItem>
<certItem issuerName="MFAxJDAiBgNVBAsTG0dsb2JhbFNpZ24gRUNDIFJvb3QgQ0EgLSBSNDETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbg==">
<serialNumber>RnQ3dg5KdDZs0nyFZk4=</serialNumber>
</certItem>
<certItem issuerName="MFYxCzAJBgNVBAYTAkpQMQ8wDQYDVQQKEwZKSVBERUMxGjAYBgNVBAsTEUpDQU4gU3ViIFJvb3QgQ0EwMRowGAYDVQQDExFKQ0FOIFN1YiBSb290IENBMA==">
<serialNumber>BAAAAAABK84ykc0=</serialNumber>
</certItem>
@ -3571,6 +3574,9 @@
<certItem issuerName="MFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBSb290IENB">
<serialNumber>AJiWmg==</serialNumber>
</certItem>
<certItem issuerName="MIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp">
<serialNumber>TA5iEg==</serialNumber>
</certItem>
<certItem issuerName="MDcxJDAiBgNVBAMTG1JDUyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEPMA0GA1UEChMGSFQgc3Js">
<serialNumber>AN9bfYOvlR1t</serialNumber>
</certItem>
@ -3811,6 +3817,9 @@
<certItem issuerName="MEcxCzAJBgNVBAYTAkhLMRYwFAYDVQQKEw1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25na29uZyBQb3N0IFJvb3QgQ0EgMQ==">
<serialNumber>BGU=</serialNumber>
</certItem>
<certItem issuerName="MFAxJDAiBgNVBAsTG0dsb2JhbFNpZ24gRUNDIFJvb3QgQ0EgLSBSNDETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbg==">
<serialNumber>Hwexgn/ZCJicZPcsIyI8zxQ=</serialNumber>
</certItem>
<certItem issuerName="MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h">
<serialNumber>Fw==</serialNumber>
</certItem>
@ -3946,6 +3955,9 @@
<certItem issuerName="MG0xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRswGQYDVQQLExJQcmltYXJ5IENsYXNzIDIgQ0ExJjAkBgNVBAMTHUdsb2JhbFNpZ24gUHJpbWFyeSBDbGFzcyAyIENB">
<serialNumber>BAAAAAABHkSl6Co=</serialNumber>
</certItem>
<certItem issuerName="MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==">
<serialNumber>Iqpyf/YoGgvHc8HiDAxAI8o=</serialNumber>
</certItem>
<certItem issuerName="MEgxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlvbjEXMBUGA1UEAxMOU2VjdXJlVHJ1c3QgQ0E=">
<serialNumber>MABJTA==</serialNumber>
</certItem>
@ -4390,6 +4402,9 @@
<certItem issuerName="MEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0E=">
<serialNumber>AjqK</serialNumber>
</certItem>
<certItem issuerName="MFAxJDAiBgNVBAsTG0dsb2JhbFNpZ24gRUNDIFJvb3QgQ0EgLSBSNDETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbg==">
<serialNumber>RnQ3dYovwvB0D5q2YGY=</serialNumber>
</certItem>
<certItem issuerName="MIGuMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTE2MDQGA1UEAxMtVVROLVVTRVJGaXJzdC1DbGllbnQgQXV0aGVudGljYXRpb24gYW5kIEVtYWls">
<serialNumber>D/wZ7+m1Mv8SONSEFcs73w==</serialNumber>
</certItem>
@ -4471,6 +4486,9 @@
<certItem issuerName="MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEcx">
<serialNumber>ESDDtMgFFiaUfKo7HD9qImM7</serialNumber>
</certItem>
<certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
<serialNumber>BydSYg==</serialNumber>
</certItem>
<certItem issuerName="MFgxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1cmVTaWduIFJvb3RDQTEx">
<serialNumber>Aw==</serialNumber>
</certItem>

Просмотреть файл

@ -92,9 +92,6 @@
<field name="mProgressListeners">
[]
</field>
<field name="mActiveResizeDisplayportSuppression">
null
</field>
<field name="mTabsProgressListeners">
[]
</field>
@ -4926,28 +4923,7 @@
<parameter name="aTopic"/>
<parameter name="aData"/>
<body><![CDATA[
let browser;
switch (aTopic) {
case "live-resize-start": {
browser = this.mCurrentTab.linkedBrowser;
let fl = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
if (fl && fl.tabParent && !this.mActiveResizeDisplayportSuppression) {
fl.tabParent.suppressDisplayport(true);
this.mActiveResizeDisplayportSuppression = browser;
}
break;
}
case "live-resize-end": {
browser = this.mActiveResizeDisplayportSuppression;
if (browser) {
let fl = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
if (fl && fl.tabParent) {
fl.tabParent.suppressDisplayport(false);
this.mActiveResizeDisplayportSuppression = null;
}
}
break;
}
case "contextual-identity-updated": {
for (let tab of this.tabs) {
if (tab.getAttribute("usercontextid") == aData) {
@ -4970,8 +4946,6 @@
this.mCurrentBrowser = document.getAnonymousElementByAttribute(this, "anonid", "initialBrowser");
this.mCurrentBrowser.permanentKey = {};
Services.obs.addObserver(this, "live-resize-start", false);
Services.obs.addObserver(this, "live-resize-end", false);
Services.obs.addObserver(this, "contextual-identity-updated", false);
this.mCurrentTab = this.tabContainer.firstChild;
@ -5074,8 +5048,6 @@
<destructor>
<![CDATA[
Services.obs.removeObserver(this, "live-resize-start");
Services.obs.removeObserver(this, "live-resize-end");
Services.obs.removeObserver(this, "contextual-identity-updated");
for (let tab of this.tabs) {

Просмотреть файл

@ -256,7 +256,6 @@ skip-if = os == "mac" # mac: Intermittent failures, bug 925225
skip-if = os == "mac" # Bug 1102331 - does focus things on the content window which break in e10s mode (still causes orange on Mac 10.10)
[browser_bug710878.js]
[browser_bug719271.js]
skip-if = e10s # Bug 1315042
[browser_bug724239.js]
[browser_bug734076.js]
[browser_bug735471.js]
@ -407,7 +406,6 @@ skip-if = os == "linux" # Bug 1329991 - test fails intermittently on Linux build
skip-if = os == 'linux' # Bug 1304272
[browser_tab_close_dependent_window.js]
[browser_tabDrop.js]
skip-if = e10s # Bug 1315042
[browser_tabReorder.js]
[browser_tab_detach_restore.js]
[browser_tab_drag_drop_perwindow.js]
@ -418,9 +416,8 @@ skip-if = buildapp == 'mulet' || (e10s && (debug || asan)) # Bug 1312436
skip-if = os == "linux" || os == "mac" # No tabs in titlebar on linux
# Disabled on OS X because of bug 967917
[browser_tabfocus.js]
skip-if = e10s # Bug 1315042
[browser_tabkeynavigation.js]
skip-if = true || (os == "mac" && !e10s) # Bug 1315042, Bug 1237713 - OSX eats keypresses for some reason
skip-if = (os == "mac" && !e10s) # Bug 1237713 - OSX eats keypresses for some reason
[browser_tabopen_reflows.js]
[browser_tabs_close_beforeunload.js]
support-files =

6
browser/extensions/e10srollout/bootstrap.js поставляемый
Просмотреть файл

@ -69,9 +69,11 @@ function defineCohort() {
// This is also the proper place to set the blocklist pref
// in case it is necessary.
// Tab Mix Plus exception tracked at bug 1185672.
Preferences.set(PREF_E10S_ADDON_BLOCKLIST,
"{dc572301-7619-498c-a57d-39143191b318}");
// bug 1185672 - Tab Mix Plus
"{dc572301-7619-498c-a57d-39143191b318};" +
// bug 1332692 - LastPass
"support@lastpass.com;");
} else {
Preferences.reset(PREF_E10S_ADDON_POLICY);
}

Просмотреть файл

@ -10,7 +10,7 @@
<Description about="urn:mozilla:install-manifest">
<em:id>e10srollout@mozilla.org</em:id>
<em:version>1.7</em:version>
<em:version>1.8</em:version>
<em:type>2</em:type>
<em:bootstrap>true</em:bootstrap>
<em:multiprocessCompatible>true</em:multiprocessCompatible>

Просмотреть файл

@ -293,18 +293,9 @@ nsScriptSecurityManager::GetChannelResultPrincipal(nsIChannel* aChannel,
if (loadInfo) {
if (!aIgnoreSandboxing && loadInfo->GetLoadingSandboxed()) {
RefPtr<nsNullPrincipal> prin;
if (loadInfo->LoadingPrincipal()) {
prin =
nsNullPrincipal::CreateWithInheritedAttributes(loadInfo->LoadingPrincipal());
} else {
OriginAttributes attrs;
loadInfo->GetOriginAttributes(&attrs);
attrs.StripAttributes(OriginAttributes::STRIP_ADDON_ID);
prin = nsNullPrincipal::Create(attrs);
}
prin.forget(aPrincipal);
return NS_OK;
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(loadInfo->GetSandboxedLoadingPrincipal(aPrincipal)));
MOZ_ASSERT(*aPrincipal);
return NS_OK;
}
bool forceInherit = loadInfo->GetForceInheritPrincipal();

Просмотреть файл

@ -943,7 +943,13 @@ endif
# optimization level here, if necessary. (The Cargo.toml files already
# specify debug-assertions appropriately for --{disable,enable}-debug.)
ifndef MOZ_OPTIMIZE
rustflags_override = RUSTFLAGS='-C opt-level=0'
rustflags = -C opt-level=0
# Unfortunately, -C opt-level=0 implies -C debug-assertions, so we need
# to explicitly disable them when MOZ_DEBUG is not set.
ifndef MOZ_DEBUG
rustflags += -C debug-assertions=no
endif
rustflags_override = RUSTFLAGS='$(rustflags)'
endif
CARGO_BUILD = env $(rustflags_override) \

Просмотреть файл

@ -157,7 +157,7 @@ module.exports = createClass({
title: BOXMODEL_L10N.getStr("boxmodel.content"),
})
)
),
)
),
BoxModelEditable({
box: "margin",

Просмотреть файл

@ -94,7 +94,8 @@ module.exports = envConfig => {
"AppConstants": "{ DEBUG: true, DEBUG_JS_MODULES: true }",
"loader": `{
lazyRequireGetter: () => {},
lazyGetter: () => {}
lazyGetter: () => {},
lazyImporter: () => {}
}`,
"dump": "console.log",
})

Просмотреть файл

@ -349,7 +349,7 @@ define(function (require, exports, module) {
return (
DOM.div({ className: ["tabs", this.props.className].join(" ") },
this.renderMenuItems(),
this.renderPanels(),
this.renderPanels()
)
);
},

Просмотреть файл

@ -8,7 +8,6 @@
#include "mozilla/TimeStamp.h"
#include "mozilla/dom/IdleDeadline.h"
#include "mozilla/dom/Performance.h"
#include "mozilla/dom/PerformanceTiming.h"
#include "mozilla/dom/TimeoutManager.h"
#include "mozilla/dom/WindowBinding.h"
@ -20,138 +19,56 @@
namespace mozilla {
namespace dom {
IdleRequest::IdleRequest(JSContext* aCx, nsPIDOMWindowInner* aWindow,
IdleRequestCallback& aCallback, uint32_t aHandle)
: mWindow(aWindow)
, mCallback(&aCallback)
IdleRequest::IdleRequest(IdleRequestCallback* aCallback, uint32_t aHandle)
: mCallback(aCallback)
, mHandle(aHandle)
, mTimeoutHandle(Nothing())
{
MOZ_ASSERT(aWindow);
// Get the calling location.
nsJSUtils::GetCallingLocation(aCx, mFileName, &mLineNo, &mColumn);
MOZ_DIAGNOSTIC_ASSERT(mCallback);
}
IdleRequest::~IdleRequest()
{
}
NS_IMPL_CYCLE_COLLECTION_CLASS(IdleRequest)
NS_IMPL_CYCLE_COLLECTION(IdleRequest, mCallback)
NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequest)
NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IdleRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IdleRequest)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequest)
NS_INTERFACE_MAP_ENTRY(nsIRunnable)
NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
NS_INTERFACE_MAP_ENTRY(nsIIncrementalRunnable)
NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimeoutHandler)
NS_INTERFACE_MAP_END
nsresult
IdleRequest::SetTimeout(uint32_t aTimeout)
{
int32_t handle;
nsresult rv = mWindow->TimeoutManager().SetTimeout(
this, aTimeout, false, Timeout::Reason::eIdleCallbackTimeout, &handle);
mTimeoutHandle = Some(handle);
return rv;
}
nsresult
IdleRequest::Run()
{
if (mCallback) {
RunIdleRequestCallback(false);
}
return NS_OK;
}
nsresult
IdleRequest::Cancel()
{
mCallback = nullptr;
CancelTimeout();
if (isInList()) {
remove();
}
Release();
return NS_OK;
}
void
IdleRequest::SetDeadline(TimeStamp aDeadline)
IdleRequest::SetTimeoutHandle(int32_t aHandle)
{
mozilla::dom::Performance* perf = mWindow->GetPerformance();
mDeadline =
perf ? perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline) : 0.0;
mTimeoutHandle = Some(aHandle);
}
uint32_t
IdleRequest::GetTimeoutHandle() const
{
MOZ_DIAGNOSTIC_ASSERT(mTimeoutHandle.isSome());
return mTimeoutHandle.value();
}
nsresult
IdleRequest::RunIdleRequestCallback(bool aDidTimeout)
IdleRequest::IdleRun(nsPIDOMWindowInner* aWindow,
DOMHighResTimeStamp aDeadline,
bool aDidTimeout)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_DIAGNOSTIC_ASSERT(mCallback);
if (!aDidTimeout) {
CancelTimeout();
}
remove();
ErrorResult error;
RefPtr<IdleDeadline> deadline =
new IdleDeadline(mWindow, aDidTimeout, mDeadline);
new IdleDeadline(aWindow, aDidTimeout, aDeadline);
mCallback->Call(*deadline, error, "requestIdleCallback handler");
mCallback = nullptr;
Release();
error.SuppressException();
return error.StealNSResult();
}
void
IdleRequest::CancelTimeout()
{
if (mTimeoutHandle.isSome()) {
mWindow->TimeoutManager().ClearTimeout(
mTimeoutHandle.value(), Timeout::Reason::eIdleCallbackTimeout);
}
}
nsresult
IdleRequest::Call()
{
SetDeadline(TimeStamp::Now());
return RunIdleRequestCallback(true);
}
void
IdleRequest::GetLocation(const char** aFileName, uint32_t* aLineNo,
uint32_t* aColumn)
{
*aFileName = mFileName.get();
*aLineNo = mLineNo;
*aColumn = mColumn;
}
void
IdleRequest::MarkForCC()
{
mCallback->MarkForCC();
}
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -15,7 +15,6 @@
#include "nsICancelableRunnable.h"
#include "nsIIncrementalRunnable.h"
#include "nsIRunnable.h"
#include "nsITimeoutHandler.h"
#include "nsString.h"
class nsPIDOMWindowInner;
@ -25,28 +24,19 @@ namespace dom {
class IdleRequestCallback;
class IdleRequest final : public nsITimeoutHandler
, public nsIRunnable
, public nsICancelableRunnable
, public nsIIncrementalRunnable
, public LinkedListElement<IdleRequest>
class IdleRequest final : public nsISupports,
public LinkedListElement<IdleRequest>
{
public:
IdleRequest(JSContext* aCx, nsPIDOMWindowInner* aWindow,
IdleRequestCallback& aCallback, uint32_t aHandle);
IdleRequest(IdleRequestCallback* aCallback, uint32_t aHandle);
virtual nsresult Call() override;
virtual void GetLocation(const char** aFileName, uint32_t* aLineNo,
uint32_t* aColumn) override;
virtual void MarkForCC() override;
nsresult IdleRun(nsPIDOMWindowInner* aWindow,
DOMHighResTimeStamp aDeadline,
bool aDidTimeout);
nsresult SetTimeout(uint32_t aTimout);
nsresult RunIdleRequestCallback(bool aDidTimeout);
void CancelTimeout();
NS_DECL_NSIRUNNABLE;
virtual nsresult Cancel() override;
virtual void SetDeadline(mozilla::TimeStamp aDeadline) override;
void SetTimeoutHandle(int32_t aHandle);
bool HasTimeout() const { return mTimeoutHandle.isSome(); }
uint32_t GetTimeoutHandle() const;
uint32_t Handle() const
{
@ -54,22 +44,14 @@ public:
}
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IdleRequest, nsITimeoutHandler)
NS_DECL_CYCLE_COLLECTION_CLASS(IdleRequest)
private:
~IdleRequest();
// filename, line number and JS language version string of the
// caller of setTimeout()
nsCString mFileName;
uint32_t mLineNo;
uint32_t mColumn;
nsCOMPtr<nsPIDOMWindowInner> mWindow;
RefPtr<IdleRequestCallback> mCallback;
uint32_t mHandle;
const uint32_t mHandle;
mozilla::Maybe<int32_t> mTimeoutHandle;
DOMHighResTimeStamp mDeadline;
};
} // namespace dom

Просмотреть файл

@ -26,6 +26,7 @@ static StaticRefPtr<TabGroup> sChromeTabGroup;
TabGroup::TabGroup(bool aIsChrome)
: mLastWindowLeft(false)
, mThrottledQueuesInitialized(false)
, mIsChrome(aIsChrome)
{
for (size_t i = 0; i < size_t(TaskCategory::Count); i++) {
TaskCategory category = static_cast<TaskCategory>(i);
@ -59,7 +60,7 @@ TabGroup::~TabGroup()
{
MOZ_ASSERT(mDocGroups.IsEmpty());
MOZ_ASSERT(mWindows.IsEmpty());
MOZ_RELEASE_ASSERT(mLastWindowLeft);
MOZ_RELEASE_ASSERT(mLastWindowLeft || mIsChrome);
}
void
@ -173,7 +174,7 @@ TabGroup::Leave(nsPIDOMWindowOuter* aWindow)
// The Chrome TabGroup doesn't have cyclical references through mEventTargets
// to itself, meaning that we don't have to worry about nulling mEventTargets
// out after the last window leaves.
if (sChromeTabGroup != this && mWindows.IsEmpty()) {
if (!mIsChrome && mWindows.IsEmpty()) {
mLastWindowLeft = true;
// There is a RefPtr cycle TabGroup -> DispatcherEventTarget -> TabGroup. To
@ -270,7 +271,7 @@ TabGroup::EventTargetFor(TaskCategory aCategory) const
{
MOZ_ASSERT(aCategory != TaskCategory::Count);
if (aCategory == TaskCategory::Worker || aCategory == TaskCategory::Timer) {
MOZ_RELEASE_ASSERT(mThrottledQueuesInitialized || this == sChromeTabGroup);
MOZ_RELEASE_ASSERT(mThrottledQueuesInitialized || mIsChrome);
}
if (NS_WARN_IF(mLastWindowLeft)) {

Просмотреть файл

@ -129,10 +129,15 @@ private:
void EnsureThrottledEventQueues();
~TabGroup();
DocGroupMap mDocGroups;
// Thread-safe members
Atomic<bool> mLastWindowLeft;
nsTArray<nsPIDOMWindowOuter*> mWindows;
Atomic<bool> mThrottledQueuesInitialized;
const bool mIsChrome;
// Main thread only
DocGroupMap mDocGroups;
nsTArray<nsPIDOMWindowOuter*> mWindows;
nsCOMPtr<nsIEventTarget> mEventTargets[size_t(TaskCategory::Count)];
RefPtr<AbstractThread> mAbstractThreads[size_t(TaskCategory::Count)];
};

Просмотреть файл

@ -41,7 +41,11 @@ public:
// default main thread being used.
nsresult InitTimer(nsIEventTarget* aTarget, uint32_t aDelay);
enum class Reason { eTimeoutOrInterval, eIdleCallbackTimeout };
enum class Reason
{
eTimeoutOrInterval,
eIdleCallbackTimeout,
};
#ifdef DEBUG
bool HasRefCnt(uint32_t aCount) const;
@ -76,6 +80,8 @@ public:
// True if this is a timeout coming from a tracking script
bool mIsTracking;
// Used to allow several reasons for setting a timeout, where each
// 'Reason' value is using a possibly overlapping set of id:s.
Reason mReason;
// Returned as value of setTimeout()

Просмотреть файл

@ -0,0 +1,43 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "TimeoutHandler.h"
namespace mozilla {
namespace dom {
TimeoutHandler::TimeoutHandler(JSContext* aCx)
: TimeoutHandler()
{
nsJSUtils::GetCallingLocation(aCx, mFileName, &mLineNo, &mColumn);
}
nsresult
TimeoutHandler::Call()
{
return NS_OK;
}
void
TimeoutHandler::GetLocation(const char** aFileName, uint32_t* aLineNo,
uint32_t* aColumn)
{
*aFileName = mFileName.get();
*aLineNo = mLineNo;
*aColumn = mColumn;
}
NS_IMPL_CYCLE_COLLECTION_0(TimeoutHandler)
NS_IMPL_CYCLE_COLLECTING_ADDREF(TimeoutHandler)
NS_IMPL_CYCLE_COLLECTING_RELEASE(TimeoutHandler)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TimeoutHandler)
NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
NS_INTERFACE_MAP_END
} // namespace dom
} // namespace mozilla

50
dom/base/TimeoutHandler.h Normal file
Просмотреть файл

@ -0,0 +1,50 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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/. */
#ifndef mozilla_dom_timeout_handler_h
#define mozilla_dom_timeout_handler_h
#include "nsCOMPtr.h"
#include "nsISupports.h"
#include "nsITimeoutHandler.h"
namespace mozilla {
namespace dom {
/**
* Utility class for implementing nsITimeoutHandlers, designed to be subclassed.
*/
class TimeoutHandler : public nsITimeoutHandler
{
public:
// TimeoutHandler doesn't actually contain cycles, but subclasses
// probably will.
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(TimeoutHandler)
virtual nsresult Call() override;
virtual void GetLocation(const char** aFileName, uint32_t* aLineNo,
uint32_t* aColumn) override;
virtual void MarkForCC() override {}
protected:
TimeoutHandler() : mFileName(""), mLineNo(0), mColumn(0) {}
explicit TimeoutHandler(JSContext *aCx);
virtual ~TimeoutHandler() {}
private:
TimeoutHandler(const TimeoutHandler&) = delete;
TimeoutHandler& operator=(const TimeoutHandler&) = delete;
TimeoutHandler& operator=(const TimeoutHandler&&) = delete;
nsCString mFileName;
uint32_t mLineNo;
uint32_t mColumn;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_timeout_handler_h

Просмотреть файл

@ -205,6 +205,7 @@ EXPORTS.mozilla.dom += [
'TabGroup.h',
'Text.h',
'Timeout.h',
'TimeoutHandler.h',
'TimeoutManager.h',
'TreeWalker.h',
'WebKitCSSMatrix.h',
@ -345,6 +346,7 @@ UNIFIED_SOURCES += [
'TextInputProcessor.cpp',
'ThirdPartyUtil.cpp',
'Timeout.cpp',
'TimeoutHandler.cpp',
'TimeoutManager.cpp',
'TreeWalker.cpp',
'WebKitCSSMatrix.cpp',

Просмотреть файл

@ -6176,7 +6176,9 @@ nsresult
nsContentTypeParser::GetType(nsAString& aResult) const
{
nsresult rv = GetParameter(nullptr, aResult);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_FAILED(rv)) {
return rv;
}
nsContentUtils::ASCIIToLower(aResult);
return NS_OK;
}

Просмотреть файл

@ -23,6 +23,7 @@
#include "mozilla/dom/StorageEvent.h"
#include "mozilla/dom/StorageEventBinding.h"
#include "mozilla/dom/Timeout.h"
#include "mozilla/dom/TimeoutHandler.h"
#include "mozilla/dom/TimeoutManager.h"
#include "mozilla/IntegerPrintfMacros.h"
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
@ -522,29 +523,266 @@ DialogValueHolder::Get(JSContext* aCx, JS::Handle<JSObject*> aScope,
}
}
void
nsGlobalWindow::PostThrottledIdleCallback()
class IdleRequestExecutor final : public nsIRunnable
, public nsICancelableRunnable
, public nsIIncrementalRunnable
{
AssertIsOnMainThread();
public:
explicit IdleRequestExecutor(nsGlobalWindow* aWindow)
: mDispatched(false)
, mDeadline(TimeStamp::Now())
, mWindow(aWindow)
{
MOZ_DIAGNOSTIC_ASSERT(mWindow);
MOZ_DIAGNOSTIC_ASSERT(mWindow->IsInnerWindow());
}
if (mThrottledIdleRequestCallbacks.isEmpty())
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IdleRequestExecutor, nsIRunnable)
NS_DECL_NSIRUNNABLE
nsresult Cancel() override;
void SetDeadline(TimeStamp aDeadline) override;
void MaybeDispatch();
private:
~IdleRequestExecutor() {}
bool mDispatched;
TimeStamp mDeadline;
RefPtr<nsGlobalWindow> mWindow;
};
NS_IMPL_CYCLE_COLLECTION_CLASS(IdleRequestExecutor)
NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutor)
NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutor)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IdleRequestExecutor)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IdleRequestExecutor)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutor)
NS_INTERFACE_MAP_ENTRY(nsIRunnable)
NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
NS_INTERFACE_MAP_ENTRY(nsIIncrementalRunnable)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRunnable)
NS_INTERFACE_MAP_END
NS_IMETHODIMP
IdleRequestExecutor::Run()
{
MOZ_ASSERT(NS_IsMainThread());
mDispatched = false;
if (mWindow) {
return mWindow->ExecuteIdleRequest(mDeadline);
}
return NS_OK;
}
nsresult
IdleRequestExecutor::Cancel()
{
MOZ_ASSERT(NS_IsMainThread());
mWindow = nullptr;
return NS_OK;
}
void
IdleRequestExecutor::SetDeadline(TimeStamp aDeadline)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mWindow) {
return;
}
RefPtr<IdleRequest> request(mThrottledIdleRequestCallbacks.popFirst());
// ownership transferred from mThrottledIdleRequestCallbacks to
// mIdleRequestCallbacks
mIdleRequestCallbacks.insertBack(request);
mDeadline = aDeadline;
}
void
IdleRequestExecutor::MaybeDispatch()
{
MOZ_DIAGNOSTIC_ASSERT(mWindow);
if (mDispatched) {
return;
}
mDispatched = true;
RefPtr<IdleRequestExecutor> request = this;
NS_IdleDispatchToCurrentThread(request.forget());
}
/* static */ void
nsGlobalWindow::InsertIdleCallbackIntoList(IdleRequest* aRequest,
IdleRequests& aList)
class IdleRequestExecutorTimeoutHandler final : public TimeoutHandler
{
aList.insertBack(aRequest);
public:
explicit IdleRequestExecutorTimeoutHandler(IdleRequestExecutor* aExecutor)
: mExecutor(aExecutor)
{
}
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IdleRequestExecutorTimeoutHandler,
TimeoutHandler)
nsresult Call() override
{
mExecutor->MaybeDispatch();
return NS_OK;
}
private:
~IdleRequestExecutorTimeoutHandler() {}
RefPtr<IdleRequestExecutor> mExecutor;
};
NS_IMPL_CYCLE_COLLECTION_INHERITED(IdleRequestExecutorTimeoutHandler, TimeoutHandler, mExecutor)
NS_IMPL_ADDREF_INHERITED(IdleRequestExecutorTimeoutHandler, TimeoutHandler)
NS_IMPL_RELEASE_INHERITED(IdleRequestExecutorTimeoutHandler, TimeoutHandler)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler)
NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
NS_INTERFACE_MAP_END_INHERITING(TimeoutHandler)
void
nsGlobalWindow::ScheduleIdleRequestDispatch()
{
AssertIsOnMainThread();
if (mIdleRequestCallbacks.isEmpty()) {
if (mIdleRequestExecutor) {
mIdleRequestExecutor->Cancel();
mIdleRequestExecutor = nullptr;
}
return;
}
if (!mIdleRequestExecutor) {
mIdleRequestExecutor = new IdleRequestExecutor(this);
}
nsPIDOMWindowOuter* outer = GetOuterWindow();
if (outer && outer->AsOuter()->IsBackground()) {
nsCOMPtr<nsITimeoutHandler> handler = new IdleRequestExecutorTimeoutHandler(mIdleRequestExecutor);
int32_t dummy;
// Set a timeout handler with a timeout of 0 ms to throttle idle
// callback requests coming from a backround window using
// background timeout throttling.
mTimeoutManager->SetTimeout(handler, 0, false,
Timeout::Reason::eIdleCallbackTimeout, &dummy);
return;
}
mIdleRequestExecutor->MaybeDispatch();
}
void
nsGlobalWindow::InsertIdleCallback(IdleRequest* aRequest)
{
AssertIsOnMainThread();
mIdleRequestCallbacks.insertBack(aRequest);
aRequest->AddRef();
}
void
nsGlobalWindow::RemoveIdleCallback(mozilla::dom::IdleRequest* aRequest)
{
AssertIsOnMainThread();
if (aRequest->HasTimeout()) {
mTimeoutManager->ClearTimeout(aRequest->GetTimeoutHandle(),
Timeout::Reason::eIdleCallbackTimeout);
}
aRequest->removeFrom(mIdleRequestCallbacks);
aRequest->Release();
}
nsresult
nsGlobalWindow::RunIdleRequest(IdleRequest* aRequest,
DOMHighResTimeStamp aDeadline,
bool aDidTimeout)
{
AssertIsOnMainThread();
RefPtr<IdleRequest> request(aRequest);
nsresult result = request->IdleRun(AsInner(), aDeadline, aDidTimeout);
RemoveIdleCallback(request);
return result;
}
nsresult
nsGlobalWindow::ExecuteIdleRequest(TimeStamp aDeadline)
{
AssertIsOnMainThread();
RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
if (!request) {
// There are no more idle requests, so stop scheduling idle
// request callbacks.
return NS_OK;
}
DOMHighResTimeStamp deadline = 0.0;
if (Performance* perf = GetPerformance()) {
deadline = perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline);
}
nsresult result = RunIdleRequest(request, deadline, false);
ScheduleIdleRequestDispatch();
return result;
}
class IdleRequestTimeoutHandler final : public TimeoutHandler
{
public:
IdleRequestTimeoutHandler(JSContext* aCx,
IdleRequest* aIdleRequest,
nsPIDOMWindowInner* aWindow)
: TimeoutHandler(aCx)
, mIdleRequest(aIdleRequest)
, mWindow(aWindow)
{
}
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IdleRequestTimeoutHandler,
TimeoutHandler)
nsresult Call() override
{
return nsGlobalWindow::Cast(mWindow)->RunIdleRequest(mIdleRequest, 0.0, true);
}
private:
~IdleRequestTimeoutHandler() {}
RefPtr<IdleRequest> mIdleRequest;
nsCOMPtr<nsPIDOMWindowInner> mWindow;
};
NS_IMPL_CYCLE_COLLECTION_INHERITED(IdleRequestTimeoutHandler,
TimeoutHandler,
mIdleRequest,
mWindow)
NS_IMPL_ADDREF_INHERITED(IdleRequestTimeoutHandler, TimeoutHandler)
NS_IMPL_RELEASE_INHERITED(IdleRequestTimeoutHandler, TimeoutHandler)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestTimeoutHandler)
NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
NS_INTERFACE_MAP_END_INHERITING(TimeoutHandler)
uint32_t
nsGlobalWindow::RequestIdleCallback(JSContext* aCx,
IdleRequestCallback& aCallback,
@ -554,33 +792,35 @@ nsGlobalWindow::RequestIdleCallback(JSContext* aCx,
MOZ_RELEASE_ASSERT(IsInnerWindow());
AssertIsOnMainThread();
uint32_t handle = ++mIdleRequestCallbackCounter;
uint32_t handle = mIdleRequestCallbackCounter++;
RefPtr<IdleRequest> request =
new IdleRequest(aCx, AsInner(), aCallback, handle);
new IdleRequest(&aCallback, handle);
if (aOptions.mTimeout.WasPassed()) {
aError = request->SetTimeout(aOptions.mTimeout.Value());
if (NS_WARN_IF(aError.Failed())) {
int32_t timeoutHandle;
nsCOMPtr<nsITimeoutHandler> handler(new IdleRequestTimeoutHandler(aCx, request, AsInner()));
nsresult rv = mTimeoutManager->SetTimeout(
handler, aOptions.mTimeout.Value(), false,
Timeout::Reason::eIdleCallbackTimeout, &timeoutHandle);
if (NS_WARN_IF(NS_FAILED(rv))) {
return 0;
}
request->SetTimeoutHandle(timeoutHandle);
}
nsGlobalWindow* outer = GetOuterWindowInternal();
if (outer && outer->AsOuter()->IsBackground()) {
// mThrottledIdleRequestCallbacks now owns request
InsertIdleCallbackIntoList(request, mThrottledIdleRequestCallbacks);
// If the list of idle callback requests is not empty it means that
// we've already dispatched the first idle request. It is the
// responsibility of that to dispatch the next.
bool needsScheduling = mIdleRequestCallbacks.isEmpty();
// mIdleRequestCallbacks now owns request
InsertIdleCallback(request);
NS_DelayedDispatchToCurrentThread(
NewRunnableMethod(this, &nsGlobalWindow::PostThrottledIdleCallback),
10000);
} else {
MOZ_ASSERT(mThrottledIdleRequestCallbacks.isEmpty());
// mIdleRequestCallbacks now owns request
InsertIdleCallbackIntoList(request, mIdleRequestCallbacks);
NS_IdleDispatchToCurrentThread(request.forget());
if (needsScheduling) {
ScheduleIdleRequestDispatch();
}
return handle;
@ -593,7 +833,7 @@ nsGlobalWindow::CancelIdleCallback(uint32_t aHandle)
for (IdleRequest* r : mIdleRequestCallbacks) {
if (r->Handle() == aHandle) {
r->Cancel();
RemoveIdleCallback(r);
break;
}
}
@ -602,25 +842,14 @@ nsGlobalWindow::CancelIdleCallback(uint32_t aHandle)
void
nsGlobalWindow::DisableIdleCallbackRequests()
{
if (mIdleRequestExecutor) {
mIdleRequestExecutor->Cancel();
mIdleRequestExecutor = nullptr;
}
while (!mIdleRequestCallbacks.isEmpty()) {
RefPtr<IdleRequest> request = mIdleRequestCallbacks.popFirst();
request->Cancel();
}
while (!mThrottledIdleRequestCallbacks.isEmpty()) {
RefPtr<IdleRequest> request = mThrottledIdleRequestCallbacks.popFirst();
request->Cancel();
}
}
void nsGlobalWindow::UnthrottleIdleCallbackRequests()
{
AssertIsOnMainThread();
while (!mThrottledIdleRequestCallbacks.isEmpty()) {
RefPtr<IdleRequest> request(mThrottledIdleRequestCallbacks.popFirst());
mIdleRequestCallbacks.insertBack(request);
NS_IdleDispatchToCurrentThread(request.forget());
RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
RemoveIdleCallback(request);
}
}
@ -629,7 +858,6 @@ nsGlobalWindow::IsBackgroundInternal() const
{
return !mOuterWindow || mOuterWindow->IsBackground();
}
namespace mozilla {
namespace dom {
extern uint64_t
@ -1220,6 +1448,7 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
mFocusMethod(0),
mSerial(0),
mIdleRequestCallbackCounter(1),
mIdleRequestExecutor(nullptr),
#ifdef DEBUG
mSetOpenerWindowCalled(false),
#endif
@ -1931,14 +2160,11 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWakeLock)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingStorageEvents)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleRequestExecutor)
for (IdleRequest* request : tmp->mIdleRequestCallbacks) {
cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
}
for (IdleRequest* request : tmp->mThrottledIdleRequestCallbacks) {
cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleObservers)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads)
@ -2043,6 +2269,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow)
tmp->UnlinkHostObjectURIs();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleRequestExecutor)
tmp->DisableIdleCallbackRequests();
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
@ -2981,7 +3208,11 @@ nsGlobalWindow::PreloadLocalStorage()
// private browsing windows do not persist local storage to disk so we should
// only try to precache storage when we're not a private browsing window.
if (principal->GetPrivateBrowsingId() == 0) {
storageManager->PrecacheStorage(principal);
nsCOMPtr<nsIDOMStorage> storage;
rv = storageManager->PrecacheStorage(principal, getter_AddRefs(storage));
if (NS_SUCCEEDED(rv)) {
mLocalStorage = static_cast<Storage*>(storage.get());
}
}
}
@ -10042,7 +10273,6 @@ void nsGlobalWindow::SetIsBackground(bool aIsBackground)
inner->mTimeoutManager->ResetTimersForThrottleReduction();
}
inner->UnthrottleIdleCallbackRequests();
inner->SyncGamepadState();
}
@ -11563,48 +11793,43 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
}
bool isPrivateBrowsing = IsPrivateBrowsing();
// In addition to determining if this is a storage event, the
// isPrivateBrowsing checks here enforce that the source storage area's
// private browsing state matches this window's state. These flag checks
// and their maintenance independent from the principal's OriginAttributes
// matter because chrome docshells that are part of private browsing windows
// can be private browsing without having their OriginAttributes set (because
// they have the system principal).
if ((!nsCRT::strcmp(aTopic, "dom-storage2-changed") && !isPrivateBrowsing) ||
(!nsCRT::strcmp(aTopic, "dom-private-storage2-changed") && isPrivateBrowsing)) {
if (!IsInnerWindow() || !AsInner()->IsCurrentInnerWindow()) {
return NS_OK;
}
nsIPrincipal *principal;
nsresult rv;
nsIPrincipal *principal = GetPrincipal();
if (!principal) {
return NS_OK;
}
RefPtr<StorageEvent> event = static_cast<StorageEvent*>(aSubject);
if (!event) {
return NS_ERROR_FAILURE;
}
RefPtr<Storage> changingStorage = event->GetStorageArea();
if (!changingStorage) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDOMStorage> istorage = changingStorage.get();
bool fireMozStorageChanged = false;
nsAutoString eventType;
eventType.AssignLiteral("storage");
principal = GetPrincipal();
if (!principal) {
return NS_OK;
}
if (changingStorage->IsPrivate() != IsPrivateBrowsing()) {
return NS_OK;
}
if (!NS_strcmp(aData, u"sessionStorage")) {
nsCOMPtr<nsIDOMStorage> changingStorage = event->GetStorageArea();
MOZ_ASSERT(changingStorage);
switch (changingStorage->GetType())
{
case Storage::SessionStorage:
{
bool check = false;
nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(GetDocShell());
if (storageManager) {
rv = storageManager->CheckStorage(principal, istorage, &check);
nsresult rv = storageManager->CheckStorage(principal, changingStorage,
&check);
if (NS_FAILED(rv)) {
return rv;
}
@ -11625,44 +11850,43 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
if (fireMozStorageChanged) {
eventType.AssignLiteral("MozSessionStorageChanged");
}
break;
}
case Storage::LocalStorage:
{
// Allow event fire only for the same principal storages
// XXX We have to use EqualsIgnoreDomain after bug 495337 lands
nsIPrincipal* storagePrincipal = changingStorage->GetPrincipal();
else {
MOZ_ASSERT(!NS_strcmp(aData, u"localStorage"));
nsIPrincipal* storagePrincipal = event->GetPrincipal();
if (!storagePrincipal) {
return NS_OK;
}
bool equals = false;
rv = storagePrincipal->Equals(principal, &equals);
nsresult rv = storagePrincipal->Equals(principal, &equals);
NS_ENSURE_SUCCESS(rv, rv);
if (!equals)
if (!equals) {
return NS_OK;
}
fireMozStorageChanged = mLocalStorage == event->GetStorageArea();
fireMozStorageChanged = mLocalStorage == changingStorage;
if (fireMozStorageChanged) {
eventType.AssignLiteral("MozLocalStorageChanged");
}
break;
}
default:
return NS_OK;
}
// Clone the storage event included in the observer notification. We want
// to dispatch clones rather than the original event.
ErrorResult error;
RefPtr<StorageEvent> newEvent = CloneStorageEvent(eventType, event, error);
RefPtr<StorageEvent> clonedEvent =
CloneStorageEvent(eventType, event, error);
if (error.Failed()) {
return error.StealNSResult();
}
newEvent->SetTrusted(true);
clonedEvent->SetTrusted(true);
if (fireMozStorageChanged) {
WidgetEvent* internalEvent = newEvent->WidgetEventPtr();
WidgetEvent* internalEvent = clonedEvent->WidgetEventPtr();
internalEvent->mFlags.mOnlyChromeDispatch = true;
}
@ -11671,12 +11895,12 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
// store the domain in which the change happened and fire the
// events if we're ever thawed.
mPendingStorageEvents.AppendElement(newEvent);
mPendingStorageEvents.AppendElement(clonedEvent);
return NS_OK;
}
bool defaultActionEnabled;
DispatchEvent(newEvent, &defaultActionEnabled);
DispatchEvent(clonedEvent, &defaultActionEnabled);
return NS_OK;
}
@ -11767,10 +11991,19 @@ nsGlobalWindow::CloneStorageEvent(const nsAString& aType,
aEvent->GetUrl(dict.mUrl);
RefPtr<Storage> storageArea = aEvent->GetStorageArea();
MOZ_ASSERT(storageArea);
RefPtr<Storage> storage;
if (storageArea->GetType() == Storage::LocalStorage) {
// If null, this is a localStorage event received by IPC.
if (!storageArea) {
storage = GetLocalStorage(aRv);
if (aRv.Failed() || !storage) {
return nullptr;
}
// We must apply the current change to the 'local' localStorage.
storage->ApplyEvent(aEvent);
} else if (storageArea->GetType() == Storage::LocalStorage) {
storage = GetLocalStorage(aRv);
} else {
MOZ_ASSERT(storageArea->GetType() == Storage::SessionStorage);
@ -11782,7 +12015,7 @@ nsGlobalWindow::CloneStorageEvent(const nsAString& aType,
}
MOZ_ASSERT(storage);
MOZ_ASSERT(storage->IsForkOf(storageArea));
MOZ_ASSERT_IF(storageArea, storage->IsForkOf(storageArea));
dict.mStorageArea = storage;
@ -12943,6 +13176,13 @@ nsGlobalWindow::EventListenerAdded(nsIAtom* aType)
aType == nsGkAtoms::onvrdisplaypresentchange) {
NotifyVREventListenerAdded();
}
// We need to initialize localStorage in order to receive notifications.
if (aType == nsGkAtoms::onstorage) {
ErrorResult rv;
GetLocalStorage(rv);
rv.SuppressException();
}
}
void

Просмотреть файл

@ -102,6 +102,8 @@ struct nsRect;
class nsWindowSizes;
class IdleRequestExecutor;
namespace mozilla {
class AbstractThread;
class DOMEventTargetHelper;
@ -119,6 +121,7 @@ class Gamepad;
enum class ImageBitmapFormat : uint8_t;
class IdleRequest;
class IdleRequestCallback;
class IncrementalRunnable;
class Location;
class MediaQueryList;
class MozSelfSupport;
@ -1106,7 +1109,6 @@ public:
mozilla::ErrorResult& aError);
void CancelIdleCallback(uint32_t aHandle);
#ifdef MOZ_WEBSPEECH
mozilla::dom::SpeechSynthesis*
GetSpeechSynthesis(mozilla::ErrorResult& aError);
@ -1786,6 +1788,18 @@ public:
virtual mozilla::AbstractThread*
AbstractMainThreadFor(mozilla::TaskCategory aCategory) override;
void DisableIdleCallbackRequests();
uint32_t IdleRequestHandle() const { return mIdleRequestCallbackCounter; }
nsresult RunIdleRequest(mozilla::dom::IdleRequest* aRequest,
DOMHighResTimeStamp aDeadline, bool aDidTimeout);
nsresult ExecuteIdleRequest(TimeStamp aDeadline);
void ScheduleIdleRequestDispatch();
typedef mozilla::LinkedList<mozilla::dom::IdleRequest> IdleRequests;
void InsertIdleCallback(mozilla::dom::IdleRequest* aRequest);
void RemoveIdleCallback(mozilla::dom::IdleRequest* aRequest);
protected:
// These members are only used on outer window objects. Make sure
// you never set any of these on an inner object!
@ -1923,19 +1937,10 @@ protected:
uint32_t mSerial;
void DisableIdleCallbackRequests();
void UnthrottleIdleCallbackRequests();
void PostThrottledIdleCallback();
typedef mozilla::LinkedList<mozilla::dom::IdleRequest> IdleRequests;
static void InsertIdleCallbackIntoList(mozilla::dom::IdleRequest* aRequest,
IdleRequests& aList);
// The current idle request callback handle
uint32_t mIdleRequestCallbackCounter;
IdleRequests mIdleRequestCallbacks;
IdleRequests mThrottledIdleRequestCallbacks;
RefPtr<IdleRequestExecutor> mIdleRequestExecutor;
#ifdef DEBUG
bool mSetOpenerWindowCalled;
@ -2012,6 +2017,7 @@ protected:
friend class mozilla::dom::PostMessageEvent;
friend class DesktopNotification;
friend class mozilla::dom::TimeoutManager;
friend class IdleRequestExecutor;
static WindowByIdTable* sWindowsById;
static bool sWarnedAboutWindowInternal;

Просмотреть файл

@ -5346,7 +5346,6 @@ CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& aWindow, double aX,
DrawSurfaceOptions(gfx::SamplingFilter::POINT),
DrawOptions(GlobalAlpha(), UsedOperation(),
AntialiasMode::NONE));
mTarget->Flush();
} else {
mTarget->SetTransform(matrix);
}

Просмотреть файл

@ -39,6 +39,6 @@ load 1286458-1.html
load 1299062-1.html
load 1305312-1.html
load 1298576-1.html
load 1334366-1.html
asserts-if(stylo,1) load 1334366-1.html # bug 1324700
load 1334647-1.html

Просмотреть файл

@ -270,16 +270,10 @@ Event::GetType(nsAString& aType)
return NS_OK;
}
static EventTarget*
GetDOMEventTarget(nsIDOMEventTarget* aTarget)
{
return aTarget ? aTarget->GetTargetForDOMEvent() : nullptr;
}
EventTarget*
Event::GetTarget() const
{
return GetDOMEventTarget(mEvent->mTarget);
return mEvent->GetDOMEventTarget();
}
NS_IMETHODIMP
@ -292,7 +286,7 @@ Event::GetTarget(nsIDOMEventTarget** aTarget)
EventTarget*
Event::GetCurrentTarget() const
{
return GetDOMEventTarget(mEvent->mCurrentTarget);
return mEvent->GetCurrentDOMEventTarget();
}
NS_IMETHODIMP
@ -339,11 +333,7 @@ Event::GetExplicitOriginalTarget(nsIDOMEventTarget** aRealEventTarget)
EventTarget*
Event::GetOriginalTarget() const
{
if (mEvent->mOriginalTarget) {
return GetDOMEventTarget(mEvent->mOriginalTarget);
}
return GetTarget();
return mEvent->GetOriginalDOMEventTarget();
}
NS_IMETHODIMP
@ -402,8 +392,17 @@ Event::Constructor(const GlobalObject& aGlobal,
ErrorResult& aRv)
{
nsCOMPtr<mozilla::dom::EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
RefPtr<Event> e = new Event(t, nullptr, nullptr);
bool trusted = e->Init(t);
return Constructor(t, aType, aParam);
}
// static
already_AddRefed<Event>
Event::Constructor(EventTarget* aEventTarget,
const nsAString& aType,
const EventInit& aParam)
{
RefPtr<Event> e = new Event(aEventTarget, nullptr, nullptr);
bool trusted = e->Init(aEventTarget);
e->InitEvent(aType, aParam.mBubbles, aParam.mCancelable);
e->SetTrusted(trusted);
e->SetComposed(aParam.mComposed);
@ -1207,7 +1206,7 @@ Event::Deserialize(const IPC::Message* aMsg, PickleIterator* aIter)
}
NS_IMETHODIMP_(void)
Event::SetOwner(mozilla::dom::EventTarget* aOwner)
Event::SetOwner(EventTarget* aOwner)
{
mOwner = nullptr;

Просмотреть файл

@ -142,6 +142,10 @@ public:
LayoutDeviceIntPoint aPoint,
CSSIntPoint aDefaultPoint);
static already_AddRefed<Event> Constructor(EventTarget* aEventTarget,
const nsAString& aType,
const EventInit& aParam);
static already_AddRefed<Event> Constructor(const GlobalObject& aGlobal,
const nsAString& aType,
const EventInit& aParam,

Просмотреть файл

@ -13,6 +13,8 @@
#include "mozilla/dom/Event.h"
#include "mozilla/dom/StorageEventBinding.h"
class nsIPrincipal;
namespace mozilla {
namespace dom {
@ -34,6 +36,7 @@ protected:
nsString mNewValue;
nsString mUrl;
RefPtr<Storage> mStorageArea;
nsCOMPtr<nsIPrincipal> mPrincipal;
public:
virtual StorageEvent* AsStorageEvent();
@ -79,6 +82,18 @@ public:
{
return mStorageArea;
}
// Non WebIDL methods
void SetPrincipal(nsIPrincipal* aPrincipal)
{
MOZ_ASSERT(!mPrincipal);
mPrincipal = aPrincipal;
}
nsIPrincipal* GetPrincipal() const
{
return mPrincipal;
}
};
} // namespace dom

Просмотреть файл

@ -140,10 +140,8 @@ CreateFileTaskChild::SetSuccessRequestResult(const FileSystemResponseValue& aVal
const FileSystemFileResponse& r = aValue.get_FileSystemFileResponse();
aRv = NS_NewLocalFile(r.realPath(), true, getter_AddRefs(mTargetPath));
if (NS_WARN_IF(aRv.Failed())) {
return;
}
mBlobImpl = static_cast<BlobChild*>(r.blobChild())->GetBlobImpl();
MOZ_ASSERT(mBlobImpl);
}
void
@ -162,9 +160,10 @@ CreateFileTaskChild::HandlerCallback()
return;
}
RefPtr<File> file = File::CreateFromFile(mFileSystem->GetParentObject(),
mTargetPath);
RefPtr<File> file = File::Create(mFileSystem->GetParentObject(), mBlobImpl);
mPromise->MaybeResolve(file);
mBlobImpl = nullptr;
mPromise = nullptr;
}
@ -233,13 +232,10 @@ CreateFileTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const
{
AssertIsOnBackgroundThread();
nsAutoString path;
aRv = mTargetPath->GetPath(path);
if (NS_WARN_IF(aRv.Failed())) {
return FileSystemDirectoryResponse();
}
return FileSystemFileResponse(path, EmptyString());
RefPtr<BlobImpl> blobImpl = new BlobImplFile(mTargetPath);
BlobParent* blobParent =
BlobParent::GetOrCreate(mRequestParent->Manager(), blobImpl);
return FileSystemFileResponse(blobParent, nullptr);
}
nsresult

Просмотреть файл

@ -59,6 +59,8 @@ private:
RefPtr<Promise> mPromise;
nsCOMPtr<nsIFile> mTargetPath;
// This is the content of what we want to store. Then, when the File is
// created, this will be used to store the new object.
RefPtr<BlobImpl> mBlobImpl;
// This is going to be the content of the file, received by createFile()

Просмотреть файл

@ -39,16 +39,6 @@ class Directory final
, public nsWrapperCache
{
public:
struct FileOrDirectoryPath
{
nsString mPath;
enum {
eFilePath,
eDirectoryPath
} mType;
};
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Directory)

Просмотреть файл

@ -205,8 +205,8 @@ FileSystemTaskChildBase::SetError(const nsresult& aErrorValue)
*/
FileSystemTaskParentBase::FileSystemTaskParentBase(FileSystemBase* aFileSystem,
const FileSystemParams& aParam,
FileSystemRequestParent* aParent)
const FileSystemParams& aParam,
FileSystemRequestParent* aParent)
: mErrorValue(NS_OK)
, mFileSystem(aFileSystem)
, mRequestParent(aParent)

Просмотреть файл

@ -88,14 +88,22 @@ GetDirectoryListingTaskChild::GetRequestParams(const nsString& aSerializedDOMPat
{
mFileSystem->AssertIsOnOwningThread();
// this is the real path.
nsAutoString path;
aRv = mTargetPath->GetPath(path);
if (NS_WARN_IF(aRv.Failed())) {
return FileSystemGetDirectoryListingParams();
}
// this is the dom path.
nsAutoString directoryPath;
mDirectory->GetPath(directoryPath, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return FileSystemGetDirectoryListingParams();
}
return FileSystemGetDirectoryListingParams(aSerializedDOMPath, path,
mFilters);
directoryPath, mFilters);
}
void
@ -110,22 +118,41 @@ GetDirectoryListingTaskChild::SetSuccessRequestResult(const FileSystemResponseVa
for (uint32_t i = 0; i < r.data().Length(); ++i) {
const FileSystemDirectoryListingResponseData& data = r.data()[i];
Directory::FileOrDirectoryPath element;
if (data.type() == FileSystemDirectoryListingResponseData::TFileSystemDirectoryListingResponseFile) {
element.mType = Directory::FileOrDirectoryPath::eFilePath;
element.mPath = data.get_FileSystemDirectoryListingResponseFile().fileRealPath();
} else {
MOZ_ASSERT(data.type() == FileSystemDirectoryListingResponseData::TFileSystemDirectoryListingResponseDirectory);
element.mType = Directory::FileOrDirectoryPath::eDirectoryPath;
element.mPath = data.get_FileSystemDirectoryListingResponseDirectory().directoryRealPath();
}
if (!mTargetData.AppendElement(element, fallible)) {
OwningFileOrDirectory* ofd = mTargetData.AppendElement(fallible);
if (!ofd) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
if (data.type() == FileSystemDirectoryListingResponseData::TFileSystemDirectoryListingResponseFile) {
const FileSystemDirectoryListingResponseFile& d =
data.get_FileSystemDirectoryListingResponseFile();
RefPtr<BlobImpl> blobImpl =
static_cast<BlobChild*>(d.blobChild())->GetBlobImpl();
MOZ_ASSERT(blobImpl);
RefPtr<File> file = File::Create(mFileSystem->GetParentObject(), blobImpl);
MOZ_ASSERT(file);
ofd->SetAsFile() = file;
} else {
MOZ_ASSERT(data.type() == FileSystemDirectoryListingResponseData::TFileSystemDirectoryListingResponseDirectory);
const FileSystemDirectoryListingResponseDirectory& d =
data.get_FileSystemDirectoryListingResponseDirectory();
nsCOMPtr<nsIFile> path;
aRv = NS_NewLocalFile(d.directoryRealPath(), true, getter_AddRefs(path));
if (NS_WARN_IF(aRv.Failed())) {
return;
}
RefPtr<Directory> directory =
Directory::Create(mFileSystem->GetParentObject(), path, mFileSystem);
MOZ_ASSERT(directory);
ofd->SetAsDirectory() = directory;
}
}
}
@ -145,81 +172,7 @@ GetDirectoryListingTaskChild::HandlerCallback()
return;
}
size_t count = mTargetData.Length();
nsAutoString directoryPath;
ErrorResult error;
mDirectory->GetPath(directoryPath, error);
if (NS_WARN_IF(error.Failed())) {
mPromise->MaybeReject(error.StealNSResult());
mPromise = nullptr;
return;
}
Sequence<OwningFileOrDirectory> listing;
if (!listing.SetLength(count, mozilla::fallible_t())) {
mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
mPromise = nullptr;
return;
}
for (unsigned i = 0; i < count; i++) {
nsCOMPtr<nsIFile> path;
nsresult rv = NS_NewLocalFile(mTargetData[i].mPath, true,
getter_AddRefs(path));
if (NS_WARN_IF(NS_FAILED(rv))) {
mPromise->MaybeReject(rv);
mPromise = nullptr;
return;
}
#ifdef DEBUG
nsCOMPtr<nsIFile> rootPath;
rv = NS_NewLocalFile(mFileSystem->LocalOrDeviceStorageRootPath(), false,
getter_AddRefs(rootPath));
if (NS_WARN_IF(NS_FAILED(rv))) {
mPromise->MaybeReject(rv);
mPromise = nullptr;
return;
}
MOZ_ASSERT(FileSystemUtils::IsDescendantPath(rootPath, path));
#endif
if (mTargetData[i].mType == Directory::FileOrDirectoryPath::eDirectoryPath) {
RefPtr<Directory> directory =
Directory::Create(mFileSystem->GetParentObject(), path, mFileSystem);
MOZ_ASSERT(directory);
// Propogate mFilter onto sub-Directory object:
directory->SetContentFilters(mFilters);
listing[i].SetAsDirectory() = directory;
} else {
MOZ_ASSERT(mTargetData[i].mType == Directory::FileOrDirectoryPath::eFilePath);
RefPtr<File> file =
File::CreateFromFile(mFileSystem->GetParentObject(), path);
MOZ_ASSERT(file);
nsAutoString filePath;
filePath.Assign(directoryPath);
// This is specific for unix root filesystem.
if (!directoryPath.EqualsLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL)) {
filePath.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
}
nsAutoString name;
file->GetName(name);
filePath.Append(name);
file->SetPath(filePath);
listing[i].SetAsFile() = file;
}
}
mPromise->MaybeResolve(listing);
mPromise->MaybeResolve(mTargetData);
mPromise = nullptr;
}
@ -259,6 +212,7 @@ GetDirectoryListingTaskParent::GetDirectoryListingTaskParent(FileSystemBase* aFi
const FileSystemGetDirectoryListingParams& aParam,
FileSystemRequestParent* aParent)
: FileSystemTaskParentBase(aFileSystem, aParam, aParent)
, mDOMPath(aParam.domPath())
, mFilters(aParam.filters())
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
@ -276,12 +230,35 @@ GetDirectoryListingTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const
nsTArray<FileSystemDirectoryListingResponseData> inputs;
for (unsigned i = 0; i < mTargetData.Length(); i++) {
if (mTargetData[i].mType == Directory::FileOrDirectoryPath::eFilePath) {
if (mTargetData[i].mType == FileOrDirectoryPath::eFilePath) {
nsCOMPtr<nsIFile> path;
nsresult rv = NS_NewLocalFile(mTargetData[i].mPath, true,
getter_AddRefs(path));
if (NS_WARN_IF(NS_FAILED(rv))) {
return FileSystemErrorResponse(rv);
}
FileSystemDirectoryListingResponseFile fileData;
fileData.fileRealPath() = mTargetData[i].mPath;
RefPtr<BlobImpl> blobImpl = new BlobImplFile(path);
nsAutoString filePath;
filePath.Assign(mDOMPath);
// This is specific for unix root filesystem.
if (!mDOMPath.EqualsLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL)) {
filePath.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
}
nsAutoString name;
blobImpl->GetName(name);
filePath.Append(name);
blobImpl->SetPath(filePath);
fileData.blobParent() =
BlobParent::GetOrCreate(mRequestParent->Manager(), blobImpl);
inputs.AppendElement(fileData);
} else {
MOZ_ASSERT(mTargetData[i].mType == Directory::FileOrDirectoryPath::eDirectoryPath);
MOZ_ASSERT(mTargetData[i].mType == FileOrDirectoryPath::eDirectoryPath);
FileSystemDirectoryListingResponseDirectory directoryData;
directoryData.directoryRealPath() = mTargetData[i].mPath;
inputs.AppendElement(directoryData);
@ -395,10 +372,10 @@ GetDirectoryListingTaskParent::IOWork()
continue;
}
Directory::FileOrDirectoryPath element;
FileOrDirectoryPath element;
element.mPath = path;
element.mType = isDir ? Directory::FileOrDirectoryPath::eDirectoryPath
: Directory::FileOrDirectoryPath::eFilePath;
element.mType = isDir ? FileOrDirectoryPath::eDirectoryPath
: FileOrDirectoryPath::eFilePath;
if (!mTargetData.AppendElement(element, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;

Просмотреть файл

@ -58,9 +58,7 @@ private:
nsCOMPtr<nsIFile> mTargetPath;
nsString mFilters;
// We cannot store File or Directory objects bacause this object is created
// on a different thread and File and Directory are not thread-safe.
FallibleTArray<Directory::FileOrDirectoryPath> mTargetData;
FallibleTArray<OwningFileOrDirectory> mTargetData;
};
class GetDirectoryListingTaskParent final : public FileSystemTaskParentBase
@ -87,11 +85,20 @@ private:
IOWork() override;
nsCOMPtr<nsIFile> mTargetPath;
nsString mDOMPath;
nsString mFilters;
// We cannot store File or Directory objects bacause this object is created
// on a different thread and File and Directory are not thread-safe.
FallibleTArray<Directory::FileOrDirectoryPath> mTargetData;
struct FileOrDirectoryPath
{
nsString mPath;
enum {
eFilePath,
eDirectoryPath
} mType;
};
FallibleTArray<FileOrDirectoryPath> mTargetData;
};
} // namespace dom

Просмотреть файл

@ -58,7 +58,6 @@ GetFileOrDirectoryTaskChild::GetFileOrDirectoryTaskChild(FileSystemBase* aFileSy
bool aDirectoryOnly)
: FileSystemTaskChildBase(aFileSystem)
, mTargetPath(aTargetPath)
, mIsDirectory(aDirectoryOnly)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
@ -100,23 +99,26 @@ GetFileOrDirectoryTaskChild::SetSuccessRequestResult(const FileSystemResponseVal
case FileSystemResponseValue::TFileSystemFileResponse: {
FileSystemFileResponse r = aValue;
aRv = NS_NewLocalFile(r.realPath(), true, getter_AddRefs(mTargetPath));
if (NS_WARN_IF(aRv.Failed())) {
return;
}
RefPtr<BlobImpl> blobImpl =
static_cast<BlobChild*>(r.blobChild())->GetBlobImpl();
MOZ_ASSERT(blobImpl);
mIsDirectory = false;
mResultFile = File::Create(mFileSystem->GetParentObject(), blobImpl);
MOZ_ASSERT(mResultFile);
break;
}
case FileSystemResponseValue::TFileSystemDirectoryResponse: {
FileSystemDirectoryResponse r = aValue;
aRv = NS_NewLocalFile(r.realPath(), true, getter_AddRefs(mTargetPath));
nsCOMPtr<nsIFile> file;
aRv = NS_NewLocalFile(r.realPath(), true, getter_AddRefs(file));
if (NS_WARN_IF(aRv.Failed())) {
return;
}
mIsDirectory = true;
mResultDirectory = Directory::Create(mFileSystem->GetParentObject(),
file, mFileSystem);
MOZ_ASSERT(mResultDirectory);
break;
}
default: {
@ -141,20 +143,16 @@ GetFileOrDirectoryTaskChild::HandlerCallback()
return;
}
if (mIsDirectory) {
RefPtr<Directory> dir = Directory::Create(mFileSystem->GetParentObject(),
mTargetPath,
mFileSystem);
MOZ_ASSERT(dir);
mPromise->MaybeResolve(dir);
if (mResultDirectory) {
mPromise->MaybeResolve(mResultDirectory);
mResultDirectory = nullptr;
mPromise = nullptr;
return;
}
RefPtr<File> file = File::CreateFromFile(mFileSystem->GetParentObject(),
mTargetPath);
mPromise->MaybeResolve(file);
MOZ_ASSERT(mResultFile);
mPromise->MaybeResolve(mResultFile);
mResultFile = nullptr;
mPromise = nullptr;
}
@ -216,7 +214,10 @@ GetFileOrDirectoryTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const
return FileSystemDirectoryResponse(path);
}
return FileSystemFileResponse(path, EmptyString());
RefPtr<BlobImpl> blobImpl = new BlobImplFile(mTargetPath);
BlobParent* blobParent =
BlobParent::GetOrCreate(mRequestParent->Manager(), blobImpl);
return FileSystemFileResponse(blobParent, nullptr);
}
nsresult

Просмотреть файл

@ -54,8 +54,8 @@ private:
RefPtr<Promise> mPromise;
nsCOMPtr<nsIFile> mTargetPath;
// Whether we get a directory.
bool mIsDirectory;
RefPtr<File> mResultFile;
RefPtr<Directory> mResultDirectory;
};
class GetFileOrDirectoryTaskParent final : public FileSystemTaskParentBase

Просмотреть файл

@ -303,22 +303,10 @@ GetFilesHelper::RunMainThread()
}
// Create the sequence of Files.
for (uint32_t i = 0; i < mTargetPathArray.Length(); ++i) {
nsCOMPtr<nsIFile> file;
mErrorResult =
NS_NewLocalFile(mTargetPathArray[i].mRealPath, true,
getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(mErrorResult))) {
mFiles.Clear();
return;
}
RefPtr<File> domFile =
File::CreateFromFile(mGlobal, file);
for (uint32_t i = 0; i < mTargetBlobImplArray.Length(); ++i) {
RefPtr<File> domFile = File::Create(mGlobal, mTargetBlobImplArray[i]);
MOZ_ASSERT(domFile);
domFile->SetPath(mTargetPathArray[i].mDomPath);
if (!mFiles.AppendElement(domFile, fallible)) {
mErrorResult = NS_ERROR_OUT_OF_MEMORY;
mFiles.Clear();
@ -395,16 +383,13 @@ GetFilesHelperBase::ExploreDirectory(const nsAString& aDOMPath, nsIFile* aFile)
domPath.Append(leafName);
if (isFile) {
FileData* data = mTargetPathArray.AppendElement(fallible);
if (!data) {
RefPtr<BlobImpl> blobImpl = new BlobImplFile(currFile);
blobImpl->SetPath(domPath);
if (!mTargetBlobImplArray.AppendElement(blobImpl, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (NS_WARN_IF(NS_FAILED(currFile->GetPath(data->mRealPath)))) {
continue;
}
data->mDomPath = domPath;
continue;
}

Просмотреть файл

@ -63,13 +63,8 @@ protected:
bool mRecursiveFlag;
// We populate this array in the I/O thread with the paths of the Files that
// we want to send as result to the promise objects.
struct FileData {
nsString mDomPath;
nsString mRealPath;
};
FallibleTArray<FileData> mTargetPathArray;
// We populate this array in the I/O thread with the BlobImpl.
FallibleTArray<RefPtr<BlobImpl>> mTargetBlobImplArray;
nsTHashtable<nsCStringHashKey> mExploredDirectories;
};

Просмотреть файл

@ -122,8 +122,11 @@ GetFilesTaskChild::SetSuccessRequestResult(const FileSystemResponseValue& aValue
for (uint32_t i = 0; i < r.data().Length(); ++i) {
const FileSystemFileResponse& data = r.data()[i];
mTargetData[i].mRealPath = data.realPath();
mTargetData[i].mDOMPath = data.domPath();
RefPtr<BlobImpl> blobImpl =
static_cast<BlobChild*>(data.blobChild())->GetBlobImpl();
MOZ_ASSERT(blobImpl);
mTargetData[i] = File::Create(mFileSystem->GetParentObject(), blobImpl);
}
}
@ -142,48 +145,7 @@ GetFilesTaskChild::HandlerCallback()
return;
}
size_t count = mTargetData.Length();
Sequence<RefPtr<File>> listing;
if (!listing.SetLength(count, mozilla::fallible_t())) {
mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
mPromise = nullptr;
return;
}
for (unsigned i = 0; i < count; i++) {
nsCOMPtr<nsIFile> path;
nsresult rv = NS_NewLocalFile(mTargetData[i].mRealPath, true,
getter_AddRefs(path));
if (NS_WARN_IF(NS_FAILED(rv))) {
mPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
mPromise = nullptr;
return;
}
#ifdef DEBUG
nsCOMPtr<nsIFile> rootPath;
rv = NS_NewLocalFile(mFileSystem->LocalOrDeviceStorageRootPath(), false,
getter_AddRefs(rootPath));
if (NS_WARN_IF(NS_FAILED(rv))) {
mPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
mPromise = nullptr;
return;
}
MOZ_ASSERT(FileSystemUtils::IsDescendantPath(rootPath, path));
#endif
RefPtr<File> file =
File::CreateFromFile(mFileSystem->GetParentObject(), path);
MOZ_ASSERT(file);
file->SetPath(mTargetData[i].mDOMPath);
listing[i] = file;
}
mPromise->MaybeResolve(listing);
mPromise->MaybeResolve(mTargetData);
mPromise = nullptr;
}
@ -239,17 +201,17 @@ GetFilesTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const
InfallibleTArray<PBlobParent*> blobs;
FallibleTArray<FileSystemFileResponse> inputs;
if (!inputs.SetLength(mTargetPathArray.Length(), mozilla::fallible_t())) {
if (!inputs.SetLength(mTargetBlobImplArray.Length(), mozilla::fallible_t())) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
FileSystemFilesResponse response;
return response;
}
for (unsigned i = 0; i < mTargetPathArray.Length(); i++) {
FileSystemFileResponse fileData;
fileData.realPath() = mTargetPathArray[i].mRealPath;
fileData.domPath() = mTargetPathArray[i].mDomPath;
inputs[i] = fileData;
for (unsigned i = 0; i < mTargetBlobImplArray.Length(); i++) {
BlobParent* blobParent =
BlobParent::GetOrCreate(mRequestParent->Manager(),
mTargetBlobImplArray[i]);
inputs[i] = FileSystemFileResponse(blobParent, nullptr);
}
FileSystemFilesResponse response;

Просмотреть файл

@ -60,12 +60,7 @@ private:
bool mRecursiveFlag;
// We store the fullpath and the dom path of Files.
struct FileData {
nsString mRealPath;
nsString mDOMPath;
};
FallibleTArray<FileData> mTargetData;
FallibleTArray<RefPtr<File>> mTargetData;
};
class GetFilesTaskParent final : public FileSystemTaskParentBase

Просмотреть файл

@ -31,6 +31,7 @@ struct FileSystemGetDirectoryListingParams
{
nsString filesystem;
nsString realPath;
nsString domPath;
// 'filters' could be an array rather than a semicolon separated string
// (we'd then use InfallibleTArray<nsString> internally), but that is

Просмотреть файл

@ -12,8 +12,7 @@ namespace dom {
struct FileSystemFileResponse
{
nsString realPath;
nsString domPath;
PBlob blob;
};
struct FileSystemDirectoryResponse
@ -23,8 +22,7 @@ struct FileSystemDirectoryResponse
struct FileSystemDirectoryListingResponseFile
{
// This is the full real path for the file that we are sending via IPC.
nsString fileRealPath;
PBlob blob;
};
struct FileSystemDirectoryListingResponseDirectory

Просмотреть файл

@ -20,8 +20,15 @@ interface nsIDOMStorageManager : nsISupports
/**
* This starts async preloading of a storage cache for scope
* defined by the principal.
*
* Because of how multi-e10s support was implemented in bug 1285898, the
* StorageCache instance can no longer use a timer to keep itself alive. So a
* Storage instance is returned if precaching believes the principal may have
* localStorage data. (Previously the StorageCache would be brought into
* existence and kept alive by the timer so that it could be returned if a
* call to createStorage was made due to a request by the page.)
*/
void precacheStorage(in nsIPrincipal aPrincipal);
nsIDOMStorage precacheStorage(in nsIPrincipal aPrincipal);
/**
* Returns instance of DOM storage object for given principal.

Просмотреть файл

@ -35,6 +35,7 @@
#include "mozilla/dom/PCrashReporterChild.h"
#include "mozilla/dom/ProcessGlobal.h"
#include "mozilla/dom/PushNotifier.h"
#include "mozilla/dom/Storage.h"
#include "mozilla/dom/StorageIPC.h"
#include "mozilla/dom/TabGroup.h"
#include "mozilla/dom/workers/ServiceWorkerManager.h"
@ -1398,6 +1399,15 @@ ContentChild::RecvBidiKeyboardNotify(const bool& aIsLangRTL,
return IPC_OK();
}
static StaticRefPtr<CancelableRunnable> gFirstIdleTask;
static void
FirstIdle(void)
{
MOZ_ASSERT(gFirstIdleTask);
gFirstIdleTask = nullptr;
ContentChild::GetSingleton()->SendFirstIdle();
}
mozilla::jsipc::PJavaScriptChild *
ContentChild::AllocPJavaScriptChild()
@ -1457,6 +1467,15 @@ ContentChild::RecvPBrowserConstructor(PBrowserChild* aActor,
{
MOZ_ASSERT(!IsShuttingDown());
static bool hasRunOnce = false;
if (!hasRunOnce) {
hasRunOnce = true;
MOZ_ASSERT(!gFirstIdleTask);
RefPtr<CancelableRunnable> firstIdleTask = NewCancelableRunnableFunction(FirstIdle);
gFirstIdleTask = firstIdleTask;
NS_IdleDispatchToCurrentThread(firstIdleTask.forget());
}
return nsIContentChild::RecvPBrowserConstructor(aActor, aTabId, aContext,
aChromeFlags, aCpID, aIsForBrowser);
}
@ -1995,6 +2014,9 @@ ContentChild::ActorDestroy(ActorDestroyReason why)
// keep persistent state.
ProcessChild::QuickExit();
#else
if (gFirstIdleTask) {
gFirstIdleTask->Cancel();
}
nsHostObjectProtocolHandler::RemoveDataEntries();
@ -3029,6 +3051,20 @@ ContentChild::RecvBlobURLUnregistration(const nsCString& aURI)
return IPC_OK();
}
mozilla::ipc::IPCResult
ContentChild::RecvDispatchLocalStorageChange(const nsString& aDocumentURI,
const nsString& aKey,
const nsString& aOldValue,
const nsString& aNewValue,
const IPC::Principal& aPrincipal,
const bool& aIsPrivate)
{
Storage::DispatchStorageEvent(Storage::LocalStorage,
aDocumentURI, aKey, aOldValue, aNewValue,
aPrincipal, aIsPrivate, nullptr, true);
return IPC_OK();
}
#if defined(XP_WIN) && defined(ACCESSIBILITY)
bool
ContentChild::SendGetA11yContentId()

Просмотреть файл

@ -408,6 +408,14 @@ public:
virtual mozilla::ipc::IPCResult
RecvInitBlobURLs(nsTArray<BlobURLRegistrationData>&& aRegistations) override;
virtual mozilla::ipc::IPCResult
RecvDispatchLocalStorageChange(const nsString& aDocumentURI,
const nsString& aKey,
const nsString& aOldValue,
const nsString& aNewValue,
const IPC::Principal& aPrincipal,
const bool& aIsPrivate) override;
virtual mozilla::ipc::IPCResult RecvLastPrivateDocShellDestroyed() override;
virtual mozilla::ipc::IPCResult RecvFilePathUpdate(const nsString& aStorageType,

Просмотреть файл

@ -51,6 +51,7 @@
#include "mozilla/dom/PContentPermissionRequestParent.h"
#include "mozilla/dom/PCycleCollectWithLogsParent.h"
#include "mozilla/dom/ServiceWorkerRegistrar.h"
#include "mozilla/dom/Storage.h"
#include "mozilla/dom/StorageIPC.h"
#include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
#include "mozilla/dom/power/PowerManagerService.h"
@ -156,6 +157,7 @@
#include "nsThreadUtils.h"
#include "nsToolkitCompsCID.h"
#include "nsWidgetsCID.h"
#include "PreallocatedProcessManager.h"
#include "ProcessPriorityManager.h"
#include "SandboxHal.h"
#include "ScreenManagerParent.h"
@ -476,6 +478,23 @@ static const char* sObserverTopics[] = {
"cacheservice:empty-cache",
};
// PreallocateProcess is called by the PreallocatedProcessManager.
// ContentParent then takes this process back within GetNewOrUsedBrowserProcess.
/*static*/ already_AddRefed<ContentParent>
ContentParent::PreallocateProcess()
{
RefPtr<ContentParent> process =
new ContentParent(/* aOpener = */ nullptr,
NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE));
if (!process->LaunchSubprocess(PROCESS_PRIORITY_PREALLOC)) {
return nullptr;
}
process->Init();
return process.forget();
}
/*static*/ void
ContentParent::StartUp()
{
@ -509,6 +528,9 @@ ContentParent::StartUp()
BackgroundChild::Startup();
// Try to preallocate a process that we can use later.
PreallocatedProcessManager::AllocateAfterDelay();
sDisableUnsafeCPOWWarnings = PR_GetEnv("DISABLE_UNSAFE_CPOW_WARNINGS");
#if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX)
@ -575,6 +597,74 @@ ContentParent::JoinAllSubprocesses()
sCanLaunchSubprocesses = false;
}
/*static*/ uint32_t
ContentParent::GetPoolSize(const nsAString& aContentProcessType)
{
if (!sBrowserContentParents) {
return 0;
}
nsTArray<ContentParent*>* parents =
sBrowserContentParents->Get(aContentProcessType);
return parents ? parents->Length() : 0;
}
/*static*/ nsTArray<ContentParent*>&
ContentParent::GetOrCreatePool(const nsAString& aContentProcessType)
{
if (!sBrowserContentParents) {
sBrowserContentParents =
new nsClassHashtable<nsStringHashKey, nsTArray<ContentParent*>>;
}
return *sBrowserContentParents->LookupOrAdd(aContentProcessType);
}
/*static*/ uint32_t
ContentParent::GetMaxProcessCount(const nsAString& aContentProcessType)
{
int32_t maxContentParents;
nsAutoCString processCountPref("dom.ipc.processCount.");
processCountPref.Append(NS_ConvertUTF16toUTF8(aContentProcessType));
if (NS_FAILED(Preferences::GetInt(processCountPref.get(), &maxContentParents))) {
maxContentParents = Preferences::GetInt("dom.ipc.processCount", 1);
}
if (maxContentParents < 1) {
maxContentParents = 1;
}
return static_cast<uint32_t>(maxContentParents);
}
/*static*/ bool
ContentParent::IsMaxProcessCountReached(const nsAString& aContentProcessType)
{
return GetPoolSize(aContentProcessType) >= GetMaxProcessCount(aContentProcessType);
}
/*static*/ already_AddRefed<ContentParent>
ContentParent::RandomSelect(const nsTArray<ContentParent*>& aContentParents,
ContentParent* aOpener, int32_t aMaxContentParents)
{
uint32_t maxSelectable = std::min(static_cast<uint32_t>(aContentParents.Length()),
static_cast<uint32_t>(aMaxContentParents));
uint32_t startIdx = rand() % maxSelectable;
uint32_t currIdx = startIdx;
do {
RefPtr<ContentParent> p = aContentParents[currIdx];
NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in sBrowserContentParents?");
if (p->mOpener == aOpener) {
return p.forget();
}
currIdx = (currIdx + 1) % maxSelectable;
} while (currIdx != startIdx);
return nullptr;
}
/*static*/ already_AddRefed<ContentParent>
ContentParent::GetNewOrUsedBrowserProcess(const nsAString& aRemoteType,
ProcessPriority aPriority,
@ -585,58 +675,42 @@ ContentParent::GetNewOrUsedBrowserProcess(const nsAString& aRemoteType,
if (aNew) {
*aNew = false;
}
if (!sBrowserContentParents) {
sBrowserContentParents =
new nsClassHashtable<nsStringHashKey, nsTArray<ContentParent*>>;
}
// Decide which pool of content parents we are going to be pulling from based
// on the aRemoteType and aLargeAllocationProcess flag.
nsAutoString contentProcessType(aLargeAllocationProcess
? NS_LITERAL_STRING(LARGE_ALLOCATION_REMOTE_TYPE)
: aRemoteType);
nsTArray<ContentParent*>* contentParents =
sBrowserContentParents->LookupOrAdd(contentProcessType);
int32_t maxContentParents;
nsAutoCString processCountPref("dom.ipc.processCount.");
processCountPref.Append(NS_ConvertUTF16toUTF8(contentProcessType));
if (NS_FAILED(Preferences::GetInt(processCountPref.get(), &maxContentParents))) {
maxContentParents = Preferences::GetInt("dom.ipc.processCount", 1);
nsTArray<ContentParent*>& contentParents = GetOrCreatePool(contentProcessType);
uint32_t maxContentParents = GetMaxProcessCount(contentProcessType);
RefPtr<ContentParent> p;
if (contentParents.Length() >= uint32_t(maxContentParents) &&
(p = RandomSelect(contentParents, aOpener, maxContentParents))) {
return p.forget();
}
if (maxContentParents < 1) {
maxContentParents = 1;
// Try to take the preallocated process only for the default process type.
if (contentProcessType.Equals(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE)) &&
(p = PreallocatedProcessManager::Take())) {
// For pre-allocated process we have not set the opener yet.
p->mOpener = aOpener;
} else {
p = new ContentParent(aOpener, contentProcessType);
if (aNew) {
*aNew = true;
}
if (!p->LaunchSubprocess(aPriority)) {
return nullptr;
}
p->Init();
}
if (contentParents->Length() >= uint32_t(maxContentParents)) {
uint32_t maxSelectable = std::min(static_cast<uint32_t>(contentParents->Length()),
static_cast<uint32_t>(maxContentParents));
uint32_t startIdx = rand() % maxSelectable;
uint32_t currIdx = startIdx;
do {
RefPtr<ContentParent> p = (*contentParents)[currIdx];
NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in sBrowserContentParents?");
if (p->mOpener == aOpener) {
return p.forget();
}
currIdx = (currIdx + 1) % maxSelectable;
} while (currIdx != startIdx);
}
RefPtr<ContentParent> p = new ContentParent(aOpener, contentProcessType);
if (aNew) {
*aNew = true;
}
if (!p->LaunchSubprocess(aPriority)) {
return nullptr;
}
p->Init();
contentParents->AppendElement(p);
contentParents.AppendElement(p);
return p.forget();
}
@ -2250,6 +2324,17 @@ ContentParent::RecvGetShowPasswordSetting(bool* showPassword)
return IPC_OK();
}
mozilla::ipc::IPCResult
ContentParent::RecvFirstIdle()
{
// When the ContentChild goes idle, it sends us a FirstIdle message
// which we use as a good time to prelaunch another process. If we
// prelaunch any sooner than this, then we'll be competing with the
// child process and slowing it down.
PreallocatedProcessManager::AllocateAfterDelay();
return IPC_OK();
}
mozilla::ipc::IPCResult
ContentParent::RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel,
const bool& aHidden)
@ -2552,6 +2637,13 @@ ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline,
SerializeURI(nullptr, *aUserContentCSSURL);
}
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
nsAutoString cpId;
cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
obs->NotifyObservers(static_cast<nsIObserver*>(this), "ipc:content-initializing", cpId.get());
}
return IPC_OK();
}
@ -4647,6 +4739,25 @@ ContentParent::RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aUR
return IPC_OK();
}
mozilla::ipc::IPCResult
ContentParent::RecvBroadcastLocalStorageChange(const nsString& aDocumentURI,
const nsString& aKey,
const nsString& aOldValue,
const nsString& aNewValue,
const Principal& aPrincipal,
const bool& aIsPrivate)
{
for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
if (cp != this) {
Unused << cp->SendDispatchLocalStorageChange(
nsString(aDocumentURI), nsString(aKey), nsString(aOldValue),
nsString(aNewValue), IPC::Principal(aPrincipal), aIsPrivate);
}
}
return IPC_OK();
}
mozilla::ipc::IPCResult
ContentParent::RecvGetA11yContentId(uint32_t* aContentId)
{

Просмотреть файл

@ -63,6 +63,8 @@ class SandboxBroker;
class SandboxBrokerPolicyFactory;
#endif
class PreallocatedProcessManagerImpl;
namespace embedding {
class PrintingParent;
}
@ -116,10 +118,17 @@ class ContentParent final : public PContentParent
typedef mozilla::ipc::PrincipalInfo PrincipalInfo;
typedef mozilla::dom::ClonedMessageData ClonedMessageData;
friend class mozilla::PreallocatedProcessManagerImpl;
public:
virtual bool IsContentParent() const override { return true; }
/**
* Create a subprocess suitable for use later as a content process.
*/
static already_AddRefed<ContentParent> PreallocateProcess();
/**
* Start up the content-process machinery. This might include
* scheduling pre-launch tasks.
@ -137,6 +146,22 @@ public:
*/
static void JoinAllSubprocesses();
static uint32_t GetPoolSize(const nsAString& aContentProcessType);
static uint32_t GetMaxProcessCount(const nsAString& aContentProcessType);
static bool IsMaxProcessCountReached(const nsAString& aContentProcessType);
/**
* Picks a random content parent from |aContentParents| with a given |aOpener|
* respecting the index limit set by |aMaxContentParents|.
* Returns null if non available.
*/
static already_AddRefed<ContentParent>
RandomSelect(const nsTArray<ContentParent*>& aContentParents,
ContentParent* aOpener,
int32_t maxContentParents);
/**
* Get or create a content process for:
* 1. browser iframe
@ -552,6 +577,14 @@ public:
virtual mozilla::ipc::IPCResult
RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aURI) override;
virtual mozilla::ipc::IPCResult
RecvBroadcastLocalStorageChange(const nsString& aDocumentURI,
const nsString& aKey,
const nsString& aOldValue,
const nsString& aNewValue,
const IPC::Principal& aPrincipal,
const bool& aIsPrivate) override;
virtual mozilla::ipc::IPCResult
RecvGetA11yContentId(uint32_t* aContentId) override;
@ -713,6 +746,11 @@ private:
TabParent* aTopLevel, const TabId& aTabId,
uint64_t* aId);
/**
* Get or create the corresponding content parent array to |aContentProcessType|.
*/
static nsTArray<ContentParent*>& GetOrCreatePool(const nsAString& aContentProcessType);
virtual mozilla::ipc::IPCResult RecvInitBackground(Endpoint<mozilla::ipc::PBackgroundParent>&& aEndpoint) override;
virtual mozilla::ipc::IPCResult RecvGetProcessAttributes(ContentParentId* aCpId,
@ -965,6 +1003,8 @@ private:
virtual mozilla::ipc::IPCResult RecvPrivateDocShellsExist(const bool& aExist) override;
virtual mozilla::ipc::IPCResult RecvFirstIdle() override;
virtual mozilla::ipc::IPCResult RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel,
const bool& aHidden) override;

Просмотреть файл

@ -643,6 +643,12 @@ child:
async BlobURLUnregistration(nsCString aURI);
async DispatchLocalStorageChange(nsString documentURI,
nsString key,
nsString oldValue,
nsString newValue,
Principal principal,
bool isPrivate);
async GMPsChanged(GMPCapabilityData[] capabilities);
@ -896,6 +902,9 @@ parent:
// Notify the parent of the presence or absence of private docshells
async PrivateDocShellsExist(bool aExist);
// Tell the parent that the child has gone idle for the first time.
async FirstIdle();
async AudioChannelServiceStatus(bool aActiveTelephonyChannel,
bool aContentOrNormalChannel,
bool aAnyActiveChannel);
@ -1148,6 +1157,13 @@ parent:
async UnstoreAndBroadcastBlobURLUnregistration(nsCString url);
async BroadcastLocalStorageChange(nsString documentURI,
nsString key,
nsString oldValue,
nsString newValue,
Principal principal,
bool isPrivate);
/**
* Messages for communicating child Telemetry to the parent process
*/

Просмотреть файл

@ -0,0 +1,278 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "mozilla/PreallocatedProcessManager.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Preferences.h"
#include "mozilla/Unused.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/ScriptSettings.h"
#include "nsIPropertyBag2.h"
#include "ProcessPriorityManager.h"
#include "nsServiceManagerUtils.h"
// This number is fairly arbitrary ... the intention is to put off
// launching another app process until the last one has finished
// loading its content, to reduce CPU/memory/IO contention.
#define DEFAULT_ALLOCATE_DELAY 1000
using namespace mozilla;
using namespace mozilla::hal;
using namespace mozilla::dom;
namespace mozilla {
/**
* This singleton class implements the static methods on
* PreallocatedProcessManager.
*/
class PreallocatedProcessManagerImpl final
: public nsIObserver
{
public:
static PreallocatedProcessManagerImpl* Singleton();
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
// See comments on PreallocatedProcessManager for these methods.
void AllocateAfterDelay();
void AllocateOnIdle();
void AllocateNow();
already_AddRefed<ContentParent> Take();
private:
static mozilla::StaticRefPtr<PreallocatedProcessManagerImpl> sSingleton;
PreallocatedProcessManagerImpl();
~PreallocatedProcessManagerImpl() {}
DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManagerImpl);
void Init();
void RereadPrefs();
void Enable();
void Disable();
void CloseProcess();
void ObserveProcessShutdown(nsISupports* aSubject);
bool mEnabled;
bool mShutdown;
RefPtr<ContentParent> mPreallocatedProcess;
};
/* static */ StaticRefPtr<PreallocatedProcessManagerImpl>
PreallocatedProcessManagerImpl::sSingleton;
/* static */ PreallocatedProcessManagerImpl*
PreallocatedProcessManagerImpl::Singleton()
{
MOZ_ASSERT(NS_IsMainThread());
if (!sSingleton) {
sSingleton = new PreallocatedProcessManagerImpl();
sSingleton->Init();
ClearOnShutdown(&sSingleton);
}
// First time when we init sSingleton, the pref database might not be in a
// reliable state (we are too early), so despite dom.ipc.processPrelaunch.enabled
// is set to true Preferences::GetBool will return false (it cannot find the pref).
// Since Init() above will be called only once, and the pref will not be changed,
// the manger will stay disabled. To prevent that let's re-read the pref each time
// someone accessing the manager singleton. This is a hack but this is not a hot code
// so it should be fine.
sSingleton->RereadPrefs();
return sSingleton;
}
NS_IMPL_ISUPPORTS(PreallocatedProcessManagerImpl, nsIObserver)
PreallocatedProcessManagerImpl::PreallocatedProcessManagerImpl()
: mEnabled(false)
, mShutdown(false)
{}
void
PreallocatedProcessManagerImpl::Init()
{
Preferences::AddStrongObserver(this, "dom.ipc.processPrelaunch.enabled");
// We have to respect processCount at all time. This is especially important
// for testing.
Preferences::AddStrongObserver(this, "dom.ipc.processCount");
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {
os->AddObserver(this, "ipc:content-shutdown",
/* weakRef = */ false);
os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
/* weakRef = */ false);
}
RereadPrefs();
}
NS_IMETHODIMP
PreallocatedProcessManagerImpl::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData)
{
if (!strcmp("ipc:content-shutdown", aTopic)) {
ObserveProcessShutdown(aSubject);
} else if (!strcmp("nsPref:changed", aTopic)) {
// The only other observer we registered was for our prefs.
RereadPrefs();
} else if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
Preferences::RemoveObserver(this, "dom.ipc.processPrelaunch.enabled");
Preferences::RemoveObserver(this, "dom.ipc.processCount");
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {
os->RemoveObserver(this, "ipc:content-shutdown");
os->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
}
} else {
MOZ_ASSERT(false);
}
return NS_OK;
}
void
PreallocatedProcessManagerImpl::RereadPrefs()
{
if (Preferences::GetBool("dom.ipc.processPrelaunch.enabled")) {
Enable();
} else {
Disable();
}
if (ContentParent::IsMaxProcessCountReached(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE))) {
CloseProcess();
}
}
already_AddRefed<ContentParent>
PreallocatedProcessManagerImpl::Take()
{
return mPreallocatedProcess.forget();
}
void
PreallocatedProcessManagerImpl::Enable()
{
if (mEnabled) {
return;
}
mEnabled = true;
AllocateAfterDelay();
}
void
PreallocatedProcessManagerImpl::AllocateAfterDelay()
{
if (!mEnabled || mPreallocatedProcess) {
return;
}
// Originally AllocateOnIdle() was post here, but since the gecko parent
// message loop in practice never goes idle, that didn't work out well.
// Let's just launch the process after the delay.
NS_DelayedDispatchToCurrentThread(
NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateNow),
Preferences::GetUint("dom.ipc.processPrelaunch.delayMs",
DEFAULT_ALLOCATE_DELAY));
}
void
PreallocatedProcessManagerImpl::AllocateOnIdle()
{
if (!mEnabled || mPreallocatedProcess) {
return;
}
NS_IdleDispatchToCurrentThread(NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateNow));
}
void
PreallocatedProcessManagerImpl::AllocateNow()
{
if (!mEnabled || mPreallocatedProcess ||
ContentParent::IsMaxProcessCountReached(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE))) {
return;
}
mPreallocatedProcess = ContentParent::PreallocateProcess();
}
void
PreallocatedProcessManagerImpl::Disable()
{
if (!mEnabled) {
return;
}
mEnabled = false;
CloseProcess();
}
void
PreallocatedProcessManagerImpl::CloseProcess()
{
if (mPreallocatedProcess) {
mPreallocatedProcess->ShutDownProcess(ContentParent::SEND_SHUTDOWN_MESSAGE);
mPreallocatedProcess = nullptr;
}
}
void
PreallocatedProcessManagerImpl::ObserveProcessShutdown(nsISupports* aSubject)
{
if (!mPreallocatedProcess) {
return;
}
nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
NS_ENSURE_TRUE_VOID(props);
uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN;
props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID);
NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN);
if (childID == mPreallocatedProcess->ChildID()) {
mPreallocatedProcess = nullptr;
}
}
inline PreallocatedProcessManagerImpl* GetPPMImpl()
{
return PreallocatedProcessManagerImpl::Singleton();
}
/* static */ void
PreallocatedProcessManager::AllocateAfterDelay()
{
GetPPMImpl()->AllocateAfterDelay();
}
/* static */ void
PreallocatedProcessManager::AllocateOnIdle()
{
GetPPMImpl()->AllocateOnIdle();
}
/* static */ void
PreallocatedProcessManager::AllocateNow()
{
GetPPMImpl()->AllocateNow();
}
/* static */ already_AddRefed<ContentParent>
PreallocatedProcessManager::Take()
{
return GetPPMImpl()->Take();
}
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,82 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef mozilla_PreallocatedProcessManager_h
#define mozilla_PreallocatedProcessManager_h
#include "base/basictypes.h"
#include "mozilla/AlreadyAddRefed.h"
namespace mozilla {
namespace dom {
class ContentParent;
} // namespace dom
/**
* This class manages a ContentParent that it starts up ahead of any particular
* need. You can then call Take() to get this process and use it. Since we
* already started it up, it should be ready for use faster than if you'd
* created the process when you needed it.
*
* This class watches the dom.ipc.processPrelaunch.enabled pref. If it changes
* from false to true, it preallocates a process. If it changes from true to
* false, it kills the preallocated process, if any.
*
* We don't expect this pref to flip between true and false in production, but
* flipping the pref is important for tests.
*/
class PreallocatedProcessManager final
{
typedef mozilla::dom::ContentParent ContentParent;
public:
/**
* Create a process after a delay. We wait for a period of time (specified
* by the dom.ipc.processPrelaunch.delayMs pref), then wait for this process
* to go idle, then allocate the new process.
*
* If the dom.ipc.processPrelaunch.enabled pref is false, or if we already
* have a preallocated process, this function does nothing.
*/
static void AllocateAfterDelay();
/**
* Create a process once this process goes idle.
*
* If the dom.ipc.processPrelaunch.enabled pref is false, or if we already
* have a preallocated process, this function does nothing.
*/
static void AllocateOnIdle();
/**
* Create a process right now.
*
* If the dom.ipc.processPrelaunch.enabled pref is false, or if we already
* have a preallocated process, this function does nothing.
*/
static void AllocateNow();
/**
* Take the preallocated process, if we have one. If we don't have one, this
* returns null.
*
* If you call Take() twice in a row, the second call is guaranteed to return
* null.
*
* After you Take() the preallocated process, you need to call one of the
* Allocate* functions (or change the dom.ipc.processPrelaunch pref from
* false to true) before we'll create a new process.
*/
static already_AddRefed<ContentParent> Take();
private:
PreallocatedProcessManager();
DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManager);
};
} // namespace mozilla
#endif // defined mozilla_PreallocatedProcessManager_h

Просмотреть файл

@ -3296,6 +3296,18 @@ TabParent::RecvRequestCrossBrowserNavigation(const uint32_t& aGlobalIndex)
return IPC_OK();
}
void
TabParent::LiveResizeStarted()
{
SuppressDisplayport(true);
}
void
TabParent::LiveResizeStopped()
{
SuppressDisplayport(false);
}
NS_IMETHODIMP
FakeChannel::OnAuthAvailable(nsISupports *aContext, nsIAuthInformation *aAuthInfo)
{

Просмотреть файл

@ -8,6 +8,7 @@
#define mozilla_tabs_TabParent_h
#include "js/TypeDecls.h"
#include "LiveResizeListener.h"
#include "mozilla/ContentCache.h"
#include "mozilla/dom/AudioChannelBinding.h"
#include "mozilla/dom/ipc/IdType.h"
@ -91,6 +92,7 @@ class TabParent final : public PBrowserParent
, public TabContext
, public nsAPostRefreshObserver
, public nsIWebBrowserPersistable
, public LiveResizeListener
{
typedef mozilla::dom::ClonedMessageData ClonedMessageData;
@ -593,6 +595,10 @@ public:
mozilla::ipc::IPCResult RecvEnsureLayersConnected() override;
// LiveResizeListener implementation
void LiveResizeStarted() override;
void LiveResizeStopped() override;
protected:
bool ReceiveMessage(const nsString& aMessage,
bool aSync,

Просмотреть файл

@ -39,6 +39,7 @@ EXPORTS.mozilla.dom += [
]
EXPORTS.mozilla += [
'PreallocatedProcessManager.h',
'ProcessHangMonitor.h',
'ProcessHangMonitorIPC.h',
'ProcessPriorityManager.h',
@ -58,6 +59,7 @@ UNIFIED_SOURCES += [
'nsIContentChild.cpp',
'nsIContentParent.cpp',
'PermissionMessageUtils.cpp',
'PreallocatedProcessManager.cpp',
'ProcessPriorityManager.cpp',
'ScreenManagerParent.cpp',
'StructuredCloneData.cpp',

Просмотреть файл

@ -21,3 +21,5 @@ skip-if = !e10s
support-files =
blob_verify.sjs
!/dom/canvas/test/captureStream_common.js
[test_Preallocated.html]
skip-if = !e10s

Просмотреть файл

@ -0,0 +1,51 @@
<!DOCTYPE HTML>
<html>
<!--
Test that the preallocated process starts up.
-->
<head>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="../browserElementTestHelpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.7">
"use strict";
SimpleTest.waitForExplicitFinish();
function expectProcessCreated() {
return new Promise(resolve => {
function parentExpectProcessCreated() {
Components.utils.import('resource://gre/modules/Services.jsm');
let topic = "ipc:content-initializing";
let obs = { observe: function() {
Services.obs.removeObserver(obs, topic);
sendAsyncMessage('process-created');
}}
Services.obs.addObserver(obs, topic, /* weak = */ false);
}
let helper = SpecialPowers.loadChromeScript(parentExpectProcessCreated);
SimpleTest.registerCleanupFunction(function() { helper.destroy() });
helper.addMessageListener('process-created', resolve);
});
}
expectProcessCreated().then(() => {
ok(true, "Process creation detected.");
SimpleTest.finish();
});
// Kill existing preallocated process.
SpecialPowers.pushPrefEnv({'set':[["dom.ipc.processPrelaunch.enabled", false]]}).then(() => {
// Make sure we have the capacity to launch preallocated process.
SpecialPowers.pushPrefEnv({'set':[["dom.ipc.processCount", 100]]}).then(() => {
// Enable preallocated process and run the test.
SpecialPowers.pushPrefEnv({'set':[["dom.ipc.processPrelaunch.enabled", true]]});
});
});
</script>
</body>
</html>

Просмотреть файл

@ -63,7 +63,14 @@ public:
NS_IMETHOD
Run() override
{
MOZ_ASSERT(mPort);
NS_ASSERT_OWNINGTHREAD(Runnable);
// The port can be cycle collected while this runnable is pending in
// the event queue.
if (!mPort) {
return NS_OK;
}
MOZ_ASSERT(mPort->mPostMessageRunnable == this);
nsresult rv = DispatchMessage();
@ -81,6 +88,8 @@ public:
nsresult
Cancel() override
{
NS_ASSERT_OWNINGTHREAD(Runnable);
mPort = nullptr;
mData = nullptr;
return NS_OK;
@ -90,6 +99,8 @@ private:
nsresult
DispatchMessage() const
{
NS_ASSERT_OWNINGTHREAD(Runnable);
nsCOMPtr<nsIGlobalObject> globalObject = mPort->GetParentObject();
AutoJSAPI jsapi;

Просмотреть файл

@ -23,11 +23,8 @@
);
worker.onerror = function(error) {
var msg = error.message;
if (msg.match(/^NetworkError/) || msg.match(/Failed to load worker script/)) {
// this means CSP blocked it
msg = "blocked";
}
// this means CSP blocked it
var msg = !("message" in error) ? "blocked" : e.message;
window.parent.postMessage({id:page_id, message:msg}, 'http://mochi.test:8888');
error.preventDefault();
};

Просмотреть файл

@ -53,9 +53,6 @@ function testWorker([mime, shouldLoad]) {
};
worker.onerror = (error) => {
ok(!shouldLoad, `worker with wrong mime '${mime}' should be blocked`);
let msg = error.message;
ok(msg.match(/^NetworkError/) || msg.match(/Failed to load worker script/),
"should gets correct error message");
error.preventDefault();
resolve();
}
@ -74,9 +71,6 @@ function testWorkerImportScripts([mime, shouldLoad]) {
};
worker.onerror = (error) => {
ok(!shouldLoad, `worker/importScripts with wrong mime '${mime}' should be blocked`);
let msg = error.message;
ok(msg.match(/^NetworkError/) || msg.match(/Failed to load worker script/),
"should gets correct error message");
error.preventDefault();
resolve();
}

Просмотреть файл

@ -14,6 +14,9 @@
#include "nsIPrincipal.h"
#include "nsICookiePermission.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/PermissionMessageUtils.h"
#include "mozilla/dom/StorageBinding.h"
#include "mozilla/dom/StorageEvent.h"
#include "mozilla/dom/StorageEventBinding.h"
@ -25,6 +28,9 @@
#include "nsServiceManagerUtils.h"
namespace mozilla {
using namespace ipc;
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Storage, mManager, mPrincipal, mWindow)
@ -58,7 +64,6 @@ Storage::Storage(nsPIDOMWindowInner* aWindow,
Storage::~Storage()
{
mCache->KeepAlive();
}
/* virtual */ JSObject*
@ -212,6 +217,30 @@ void
Storage::BroadcastChangeNotification(const nsSubstring& aKey,
const nsSubstring& aOldValue,
const nsSubstring& aNewValue)
{
if (!XRE_IsParentProcess() && GetType() == LocalStorage && mPrincipal) {
// If we are in a child process, we want to send a message to the parent in
// order to broadcast the StorageEvent correctly to any child process.
dom::ContentChild* cc = dom::ContentChild::GetSingleton();
Unused << NS_WARN_IF(!cc->SendBroadcastLocalStorageChange(
mDocumentURI, nsString(aKey), nsString(aOldValue), nsString(aNewValue),
IPC::Principal(mPrincipal), mIsPrivate));
}
DispatchStorageEvent(GetType(), mDocumentURI, aKey, aOldValue, aNewValue,
mPrincipal, mIsPrivate, this, false);
}
/* static */ void
Storage::DispatchStorageEvent(StorageType aStorageType,
const nsAString& aDocumentURI,
const nsAString& aKey,
const nsAString& aOldValue,
const nsAString& aNewValue,
nsIPrincipal* aPrincipal,
bool aIsPrivate,
Storage* aStorage,
bool aImmediateDispatch)
{
StorageEventInit dict;
dict.mBubbles = false;
@ -219,21 +248,67 @@ Storage::BroadcastChangeNotification(const nsSubstring& aKey,
dict.mKey = aKey;
dict.mNewValue = aNewValue;
dict.mOldValue = aOldValue;
dict.mStorageArea = this;
dict.mUrl = mDocumentURI;
dict.mStorageArea = aStorage;
dict.mUrl = aDocumentURI;
// Note, this DOM event should never reach JS. It is cloned later in
// nsGlobalWindow.
RefPtr<StorageEvent> event =
StorageEvent::Constructor(nullptr, NS_LITERAL_STRING("storage"), dict);
event->SetPrincipal(aPrincipal);
RefPtr<StorageNotifierRunnable> r =
new StorageNotifierRunnable(event,
GetType() == LocalStorage
aStorageType == LocalStorage
? u"localStorage"
: u"sessionStorage",
IsPrivate());
NS_DispatchToMainThread(r);
aIsPrivate);
if (aImmediateDispatch) {
Unused << r->Run();
} else {
NS_DispatchToMainThread(r);
}
// If we are in the parent process and we have the principal, we want to
// broadcast this event to every other process.
if (aStorageType == LocalStorage && XRE_IsParentProcess() && aPrincipal) {
for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
Unused << cp->SendDispatchLocalStorageChange(
nsString(aDocumentURI), nsString(aKey), nsString(aOldValue),
nsString(aNewValue), IPC::Principal(aPrincipal), aIsPrivate);
}
}
}
void
Storage::ApplyEvent(StorageEvent* aStorageEvent)
{
MOZ_ASSERT(aStorageEvent);
nsAutoString key;
nsAutoString old;
nsAutoString value;
aStorageEvent->GetKey(key);
aStorageEvent->GetNewValue(value);
// No key means clearing the full storage.
if (key.IsVoid()) {
MOZ_ASSERT(value.IsVoid());
mCache->Clear(this, StorageCache::E10sPropagated);
return;
}
// No new value means removing the key.
if (value.IsVoid()) {
mCache->RemoveItem(this, key, old, StorageCache::E10sPropagated);
return;
}
// Otherwise, we set the new value.
mCache->SetItem(this, key, value, old, StorageCache::E10sPropagated);
}
static const char kPermissionType[] = "cookie";

Просмотреть файл

@ -24,6 +24,7 @@ namespace dom {
class StorageManagerBase;
class StorageCache;
class StorageEvent;
class Storage final
: public nsIDOMStorage
@ -129,6 +130,28 @@ public:
return mCache == aOther->mCache;
}
// aStorage can be null if this method is called by ContentChild.
//
// aImmediateDispatch is for use by (main-thread) IPC code so that PContent
// ordering can be maintained. Without this, the event would be enqueued and
// run in a future turn of the event loop, potentially allowing other PContent
// Recv* methods to trigger script that wants to assume our localstorage
// changes have already been applied. This is the case for message manager
// messages which are used by ContentTask testing logic and webextensions.
static void
DispatchStorageEvent(StorageType aStorageType,
const nsAString& aDocumentURI,
const nsAString& aKey,
const nsAString& aOldValue,
const nsAString& aNewValue,
nsIPrincipal* aPrincipal,
bool aIsPrivate,
Storage* aStorage,
bool aImmediateDispatch);
void
ApplyEvent(StorageEvent* aStorageEvent);
protected:
// The method checks whether the caller can use a storage.
// CanUseStorage is called before any DOM initiated operation

Просмотреть файл

@ -59,7 +59,7 @@ GetDataSetIndex(const Storage* aStorage)
NS_IMPL_ADDREF(StorageCacheBridge)
// Since there is no consumer of return value of Release, we can turn this
// Since there is no consumer of return value of Release, we can turn this
// method to void to make implementation of asynchronous StorageCache::Release
// much simpler.
NS_IMETHODIMP_(void) StorageCacheBridge::Release(void)
@ -149,7 +149,7 @@ StorageCache::Init(StorageManagerBase* aManager,
// Check the quota string has (or has not) the identical origin suffix as
// this storage cache is bound to.
MOZ_ASSERT(StringBeginsWith(mQuotaOriginScope, mOriginSuffix));
MOZ_ASSERT(mOriginSuffix.IsEmpty() != StringBeginsWith(mQuotaOriginScope,
MOZ_ASSERT(mOriginSuffix.IsEmpty() != StringBeginsWith(mQuotaOriginScope,
NS_LITERAL_CSTRING("^")));
mUsage = aManager->GetOriginUsage(mQuotaOriginScope);
@ -198,28 +198,33 @@ StorageCache::DataSet(const Storage* aStorage)
}
bool
StorageCache::ProcessUsageDelta(const Storage* aStorage, int64_t aDelta)
StorageCache::ProcessUsageDelta(const Storage* aStorage, int64_t aDelta,
const MutationSource aSource)
{
return ProcessUsageDelta(GetDataSetIndex(aStorage), aDelta);
return ProcessUsageDelta(GetDataSetIndex(aStorage), aDelta, aSource);
}
bool
StorageCache::ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta)
StorageCache::ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta,
const MutationSource aSource)
{
// Check if we are in a low disk space situation
if (aDelta > 0 && mManager && mManager->IsLowDiskSpace()) {
if (aSource == ContentMutation &&
aDelta > 0 && mManager && mManager->IsLowDiskSpace()) {
return false;
}
// Check limit per this origin
Data& data = mData[aGetDataSetIndex];
uint64_t newOriginUsage = data.mOriginQuotaUsage + aDelta;
if (aDelta > 0 && newOriginUsage > StorageManagerBase::GetQuota()) {
if (aSource == ContentMutation &&
aDelta > 0 && newOriginUsage > StorageManagerBase::GetQuota()) {
return false;
}
// Now check eTLD+1 limit
if (mUsage && !mUsage->CheckAndSetETLD1UsageDelta(aGetDataSetIndex, aDelta)) {
if (mUsage &&
!mUsage->CheckAndSetETLD1UsageDelta(aGetDataSetIndex, aDelta, aSource)) {
return false;
}
@ -246,60 +251,6 @@ StorageCache::Preload()
namespace {
// This class is passed to timer as a tick observer. It refers the cache
// and keeps it alive for a time.
class StorageCacheHolder : public nsITimerCallback
{
virtual ~StorageCacheHolder() {}
NS_DECL_ISUPPORTS
NS_IMETHOD
Notify(nsITimer* aTimer) override
{
mCache = nullptr;
return NS_OK;
}
RefPtr<StorageCache> mCache;
public:
explicit StorageCacheHolder(StorageCache* aCache) : mCache(aCache) {}
};
NS_IMPL_ISUPPORTS(StorageCacheHolder, nsITimerCallback)
} // namespace
void
StorageCache::KeepAlive()
{
// Missing reference back to the manager means the cache is not responsible
// for its lifetime. Used for keeping sessionStorage live forever.
if (!mManager) {
return;
}
if (!NS_IsMainThread()) {
// Timer and the holder must be initialized on the main thread.
NS_DispatchToMainThread(NewRunnableMethod(this, &StorageCache::KeepAlive));
return;
}
nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
if (!timer) {
return;
}
RefPtr<StorageCacheHolder> holder = new StorageCacheHolder(this);
timer->InitWithCallback(holder, DOM_STORAGE_CACHE_KEEP_ALIVE_TIME_MS,
nsITimer::TYPE_ONE_SHOT);
mKeepAliveTimer.swap(timer);
}
namespace {
// The AutoTimer provided by telemetry headers is only using static,
// i.e. compile time known ID, but here we know the ID only at run time.
// Hence a new class.
@ -439,7 +390,8 @@ StorageCache::GetItem(const Storage* aStorage, const nsAString& aKey,
nsresult
StorageCache::SetItem(const Storage* aStorage, const nsAString& aKey,
const nsString& aValue, nsString& aOld)
const nsString& aValue, nsString& aOld,
const MutationSource aSource)
{
// Size of the cache that will change after this action.
int64_t delta = 0;
@ -462,7 +414,7 @@ StorageCache::SetItem(const Storage* aStorage, const nsAString& aKey,
delta += static_cast<int64_t>(aValue.Length()) -
static_cast<int64_t>(aOld.Length());
if (!ProcessUsageDelta(aStorage, delta)) {
if (!ProcessUsageDelta(aStorage, delta, aSource)) {
return NS_ERROR_DOM_QUOTA_REACHED;
}
@ -472,7 +424,7 @@ StorageCache::SetItem(const Storage* aStorage, const nsAString& aKey,
data.mKeys.Put(aKey, aValue);
if (Persist(aStorage)) {
if (aSource == ContentMutation && Persist(aStorage)) {
if (!sDatabase) {
NS_ERROR("Writing to localStorage after the database has been shut down"
", data lose!");
@ -491,7 +443,7 @@ StorageCache::SetItem(const Storage* aStorage, const nsAString& aKey,
nsresult
StorageCache::RemoveItem(const Storage* aStorage, const nsAString& aKey,
nsString& aOld)
nsString& aOld, const MutationSource aSource)
{
if (Persist(aStorage)) {
WaitForPreload(Telemetry::LOCALDOMSTORAGE_REMOVEKEY_BLOCKING_MS);
@ -509,10 +461,10 @@ StorageCache::RemoveItem(const Storage* aStorage, const nsAString& aKey,
// Recalculate the cached data size
const int64_t delta = -(static_cast<int64_t>(aOld.Length()) +
static_cast<int64_t>(aKey.Length()));
Unused << ProcessUsageDelta(aStorage, delta);
Unused << ProcessUsageDelta(aStorage, delta, aSource);
data.mKeys.Remove(aKey);
if (Persist(aStorage)) {
if (aSource == ContentMutation && Persist(aStorage)) {
if (!sDatabase) {
NS_ERROR("Writing to localStorage after the database has been shut down"
", data lose!");
@ -526,7 +478,7 @@ StorageCache::RemoveItem(const Storage* aStorage, const nsAString& aKey,
}
nsresult
StorageCache::Clear(const Storage* aStorage)
StorageCache::Clear(const Storage* aStorage, const MutationSource aSource)
{
bool refresh = false;
if (Persist(aStorage)) {
@ -548,11 +500,11 @@ StorageCache::Clear(const Storage* aStorage)
bool hadData = !!data.mKeys.Count();
if (hadData) {
Unused << ProcessUsageDelta(aStorage, -data.mOriginQuotaUsage);
Unused << ProcessUsageDelta(aStorage, -data.mOriginQuotaUsage, aSource);
data.mKeys.Clear();
}
if (Persist(aStorage) && (refresh || hadData)) {
if (aSource == ContentMutation && Persist(aStorage) && (refresh || hadData)) {
if (!sDatabase) {
NS_ERROR("Writing to localStorage after the database has been shut down"
", data lose!");
@ -667,9 +619,6 @@ StorageCache::LoadItem(const nsAString& aKey, const nsString& aValue)
void
StorageCache::LoadDone(nsresult aRv)
{
// Keep the preloaded cache alive for a time
KeepAlive();
MonitorAutoLock monitor(mMonitor);
mLoadResult = aRv;
mLoaded = true;
@ -730,12 +679,13 @@ StorageUsage::LoadUsage(const int64_t aUsage)
bool
StorageUsage::CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex,
const int64_t aDelta)
const int64_t aDelta, const StorageCache::MutationSource aSource)
{
MOZ_ASSERT(NS_IsMainThread());
int64_t newUsage = mUsage[aDataSetIndex] + aDelta;
if (aDelta > 0 && newUsage > StorageManagerBase::GetQuota()) {
if (aSource == StorageCache::ContentMutation &&
aDelta > 0 && newUsage > StorageManagerBase::GetQuota()) {
return false;
}

Просмотреть файл

@ -78,8 +78,24 @@ class StorageCache : public StorageCacheBridge
public:
NS_IMETHOD_(void) Release(void);
// Note: We pass aOriginNoSuffix through the ctor here, because
// StorageCacheHashKey's ctor is creating this class and
enum MutationSource {
// The mutation is a result of an explicit JS mutation in this process.
// The mutation should be sent to the sDatabase. Quota will be checked and
// QuotaExceededError may be returned without the mutation being applied.
ContentMutation,
// The mutation initially was triggered in a different process and is being
// propagated to this cache via Storage::ApplyEvent. The mutation should
// not be sent to the sDatabase because the originating process is already
// doing that. (In addition to the redundant writes being wasteful, there
// is the potential for other processes to see inconsistent state from the
// database while preloading.) Quota will be updated but not checked
// because it's assumed it was checked in another process and data-coherency
// is more important than slightly exceeding quota.
E10sPropagated
};
// Note: We pass aOriginNoSuffix through the ctor here, because
// StorageCacheHashKey's ctor is creating this class and
// accepts reversed-origin-no-suffix as an argument - the hashing key.
explicit StorageCache(const nsACString* aOriginNoSuffix);
@ -109,10 +125,13 @@ public:
nsresult GetItem(const Storage* aStorage, const nsAString& aKey,
nsAString& aRetval);
nsresult SetItem(const Storage* aStorage, const nsAString& aKey,
const nsString& aValue, nsString& aOld);
const nsString& aValue, nsString& aOld,
const MutationSource aSource=ContentMutation);
nsresult RemoveItem(const Storage* aStorage, const nsAString& aKey,
nsString& aOld);
nsresult Clear(const Storage* aStorage);
nsString& aOld,
const MutationSource aSource=ContentMutation);
nsresult Clear(const Storage* aStorage,
const MutationSource aSource=ContentMutation);
void GetKeys(const Storage* aStorage, nsTArray<nsString>& aKeys);
@ -182,8 +201,18 @@ private:
// Changes the quota usage on the given data set if it fits the quota.
// If not, then false is returned and no change to the set must be done.
bool ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta);
bool ProcessUsageDelta(const Storage* aStorage, const int64_t aDelta);
// A special case is if aSource==E10sPropagated, then we will return true even
// if the change would put us over quota. This is done to ensure coherency of
// caches between processes in the face of races. It does allow an attacker
// to potentially use N multiples of the quota storage limit if they can
// arrange for their origin to execute code in N processes. However, this is
// not considered a particularly concerning threat model because it's already
// very possible for a rogue page to attempt to intentionally fill up the
// user's storage through the use of multiple domains.
bool ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta,
const MutationSource aSource=ContentMutation);
bool ProcessUsageDelta(const Storage* aStorage, const int64_t aDelta,
const MutationSource aSource=ContentMutation);
private:
// When a cache is reponsible for its life time (in case of localStorage data
@ -196,9 +225,6 @@ private:
// Obtained from the manager during initialization (Init method).
RefPtr<StorageUsage> mUsage;
// Timer that holds this cache alive for a while after it has been preloaded.
nsCOMPtr<nsITimer> mKeepAliveTimer;
// Principal the cache has been initially created for, this is used only for
// sessionStorage access checks since sessionStorage objects are strictly
// scoped by a principal. localStorage objects on the other hand are scoped
@ -211,7 +237,7 @@ private:
// The origin attributes suffix
nsCString mOriginSuffix;
// The eTLD+1 scope used to count quota usage. It is in the reversed format
// The eTLD+1 scope used to count quota usage. It is in the reversed format
// and contains the origin attributes suffix.
nsCString mQuotaOriginScope;
@ -277,7 +303,8 @@ class StorageUsage : public StorageUsageBridge
public:
explicit StorageUsage(const nsACString& aOriginScope);
bool CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex, int64_t aUsageDelta);
bool CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex, int64_t aUsageDelta,
const StorageCache::MutationSource aSource);
private:
virtual const nsCString& OriginScope() { return mOriginScope; }

Просмотреть файл

@ -224,6 +224,12 @@ StorageDBChild::RecvObserve(const nsCString& aTopic,
mozilla::ipc::IPCResult
StorageDBChild::RecvOriginsHavingData(nsTArray<nsCString>&& aOrigins)
{
// Force population of mOriginsHavingData even if there are no origins so that
// ShouldPreloadOrigin does not generate false positives for all origins.
if (!aOrigins.Length()) {
Unused << OriginsHavingData();
}
for (uint32_t i = 0; i < aOrigins.Length(); ++i) {
OriginsHavingData().PutEntry(aOrigins[i]);
}

Просмотреть файл

@ -309,7 +309,7 @@ StorageManagerBase::DropCache(StorageCache* aCache)
}
nsresult
StorageManagerBase::GetStorageInternal(bool aCreate,
StorageManagerBase::GetStorageInternal(CreateMode aCreateMode,
mozIDOMWindow* aWindow,
nsIPrincipal* aPrincipal,
const nsAString& aDocumentURI,
@ -331,12 +331,12 @@ StorageManagerBase::GetStorageInternal(bool aCreate,
// Get or create a cache for the given scope
if (!cache) {
if (!aCreate) {
if (aCreateMode == CreateMode::UseIfExistsNeverCreate) {
*aRetval = nullptr;
return NS_OK;
}
if (!aRetval) {
if (aCreateMode == CreateMode::CreateIfShouldPreload) {
// This is a demand to just preload the cache, if the scope has
// no data stored, bypass creation and preload of the cache.
StorageDBBridge* db = StorageCache::GetDatabase();
@ -372,10 +372,11 @@ StorageManagerBase::GetStorageInternal(bool aCreate,
}
NS_IMETHODIMP
StorageManagerBase::PrecacheStorage(nsIPrincipal* aPrincipal)
StorageManagerBase::PrecacheStorage(nsIPrincipal* aPrincipal,
nsIDOMStorage** aRetval)
{
return GetStorageInternal(true, nullptr, aPrincipal, EmptyString(), false,
nullptr);
return GetStorageInternal(CreateMode::CreateIfShouldPreload, nullptr,
aPrincipal, EmptyString(), false, aRetval);
}
NS_IMETHODIMP
@ -385,8 +386,8 @@ StorageManagerBase::CreateStorage(mozIDOMWindow* aWindow,
bool aPrivate,
nsIDOMStorage** aRetval)
{
return GetStorageInternal(true, aWindow, aPrincipal, aDocumentURI, aPrivate,
aRetval);
return GetStorageInternal(CreateMode::CreateAlways, aWindow, aPrincipal,
aDocumentURI, aPrivate, aRetval);
}
NS_IMETHODIMP
@ -395,8 +396,8 @@ StorageManagerBase::GetStorage(mozIDOMWindow* aWindow,
bool aPrivate,
nsIDOMStorage** aRetval)
{
return GetStorageInternal(false, aWindow, aPrincipal, EmptyString(), aPrivate,
aRetval);
return GetStorageInternal(CreateMode::UseIfExistsNeverCreate, aWindow,
aPrincipal, EmptyString(), aPrivate, aRetval);
}
NS_IMETHODIMP

Просмотреть файл

@ -90,8 +90,17 @@ private:
const nsACString& aOriginNoSuffix,
nsIPrincipal* aPrincipal);
enum class CreateMode {
// GetStorage: do not create if it's not already in memory.
UseIfExistsNeverCreate,
// CreateStorage: Create it if it's not already in memory.
CreateAlways,
// PrecacheStorage: Create only if the database says we ShouldPreloadOrigin.
CreateIfShouldPreload
};
// Helper for creation of DOM storage objects
nsresult GetStorageInternal(bool aCreate,
nsresult GetStorageInternal(CreateMode aCreate,
mozIDOMWindow* aWindow,
nsIPrincipal* aPrincipal,
const nsAString& aDocumentURI,

Просмотреть файл

@ -2,6 +2,7 @@
support-files =
browser_frame_elements.html
page_privatestorageevent.html
page_localstorage_e10s.html
position.html
test-console-api.html
test_bug1004814.html
@ -40,6 +41,8 @@ skip-if = e10s
skip-if = !e10s || os != "win" || processor != "x86" # Large-Allocation requires e10s
[browser_largeAllocation_non_win32.js]
skip-if = !e10s || (os == "win" && processor == "x86") # Large-Allocation requires e10s
[browser_localStorage_e10s.js]
skip-if = !e10s # This is a test of e10s functionality.
[browser_localStorage_privatestorageevent.js]
[browser_test__content.js]
[browser_test_new_window_from_content.js]

Просмотреть файл

@ -0,0 +1,330 @@
const HELPER_PAGE_URL =
"http://example.com/browser/dom/tests/browser/page_localstorage_e10s.html";
const HELPER_PAGE_ORIGIN = "http://example.com/";
// Simple tab wrapper abstracting our messaging mechanism;
class KnownTab {
constructor(name, tab) {
this.name = name;
this.tab = tab;
}
cleanup() {
this.tab = null;
}
}
// Simple data structure class to help us track opened tabs and their pids.
class KnownTabs {
constructor() {
this.byPid = new Map();
this.byName = new Map();
}
cleanup() {
this.byPid = null;
this.byName = null;
}
}
/**
* Open our helper page in a tab in its own content process, asserting that it
* really is in its own process.
*/
function* openTestTabInOwnProcess(name, knownTabs) {
let url = HELPER_PAGE_URL + '?' + encodeURIComponent(name);
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
let pid = tab.linkedBrowser.frameLoader.tabParent.osPid;
ok(!knownTabs.byName.has(name), "tab needs its own name: " + name);
ok(!knownTabs.byPid.has(pid), "tab needs to be in its own process: " + pid);
let knownTab = new KnownTab(name, tab);
knownTabs.byPid.set(pid, knownTab);
knownTabs.byName.set(name, knownTab);
return knownTab;
}
/**
* Close all the tabs we opened.
*/
function* cleanupTabs(knownTabs) {
for (let knownTab of knownTabs.byName.values()) {
yield BrowserTestUtils.removeTab(knownTab.tab);
knownTab.cleanup();
}
knownTabs.cleanup();
}
/**
* Clear the origin's storage so that "OriginsHavingData" will return false for
* our origin. Note that this is only the case for AsyncClear() which is
* explicitly issued against a cache, or AsyncClearAll() which we can trigger
* by wiping all storage. However, the more targeted domain clearings that
* we can trigger via observer, AsyncClearMatchingOrigin and
* AsyncClearMatchingOriginAttributes will not clear the hashtable entry for
* the origin.
*
* So we explicitly access the cache here in the parent for the origin and issue
* an explicit clear. Clearing all storage might be a little easier but seems
* like asking for intermittent failures.
*/
function clearOriginStorageEnsuringNoPreload() {
let principal =
Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(
HELPER_PAGE_ORIGIN);
// We want to use createStorage to force the cache to be created so we can
// issue the clear. It's possible for getStorage to return false but for the
// origin preload hash to still have our origin in it.
let storage = Services.domStorageManager.createStorage(null, principal, "");
storage.clear();
// We don't need to wait for anything. The clear call will have queued the
// clear operation on the database thread, and the child process requests
// for origins will likewise be answered via the database thread.
}
function* verifyTabPreload(knownTab, expectStorageExists) {
let storageExists = yield ContentTask.spawn(
knownTab.tab.linkedBrowser,
HELPER_PAGE_ORIGIN,
function(origin) {
let principal =
Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(
origin);
return !!Services.domStorageManager.getStorage(null, principal);
});
is(storageExists, expectStorageExists, "Storage existence === preload");
}
/**
* Instruct the given tab to execute the given series of mutations. For
* simplicity, the mutations representation matches the expected events rep.
*/
function* mutateTabStorage(knownTab, mutations) {
yield ContentTask.spawn(
knownTab.tab.linkedBrowser,
{ mutations },
function(args) {
return content.wrappedJSObject.mutateStorage(args.mutations);
});
}
/**
* Instruct the given tab to add a "storage" event listener and record all
* received events. verifyTabStorageEvents is the corresponding method to
* check and assert the recorded events.
*/
function* recordTabStorageEvents(knownTab) {
yield ContentTask.spawn(
knownTab.tab.linkedBrowser,
{},
function() {
return content.wrappedJSObject.listenForStorageEvents();
});
}
/**
* Retrieve the current localStorage contents perceived by the tab and assert
* that they match the provided expected state.
*/
function* verifyTabStorageState(knownTab, expectedState) {
let actualState = yield ContentTask.spawn(
knownTab.tab.linkedBrowser,
{},
function() {
return content.wrappedJSObject.getStorageState();
});
for (let [expectedKey, expectedValue] of Object.entries(expectedState)) {
ok(actualState.hasOwnProperty(expectedKey), "key present: " + expectedKey);
is(actualState[expectedKey], expectedValue, "value correct");
}
for (let actualKey of Object.keys(actualState)) {
if (!expectedState.hasOwnProperty(actualKey)) {
ok(false, "actual state has key it shouldn't have: " + actualKey);
}
}
}
/**
* Retrieve and clear the storage events recorded by the tab and assert that
* they match the provided expected events. For simplicity, the expected events
* representation is the same as that used by mutateTabStorage.
*/
function* verifyTabStorageEvents(knownTab, expectedEvents) {
let actualEvents = yield ContentTask.spawn(
knownTab.tab.linkedBrowser,
{},
function() {
return content.wrappedJSObject.returnAndClearStorageEvents();
});
is(actualEvents.length, expectedEvents.length, "right number of events");
for (let i = 0; i < actualEvents.length; i++) {
let [actualKey, actualNewValue, actualOldValue] = actualEvents[i];
let [expectedKey, expectedNewValue, expectedOldValue] = expectedEvents[i];
is(actualKey, expectedKey, "keys match");
is(actualNewValue, expectedNewValue, "new values match");
is(actualOldValue, expectedOldValue, "old values match");
}
}
// We spin up a ton of child processes.
requestLongerTimeout(4);
/**
* Verify the basics of our multi-e10s localStorage support. We are focused on
* whitebox testing two things. When this is being written, broadcast filtering
* is not in place, but the test is intended to attempt to verify that its
* implementation does not break things.
*
* 1) That pages see the same localStorage state in a timely fashion when
* engaging in non-conflicting operations. We are not testing races or
* conflict resolution; the spec does not cover that.
*
* 2) That there are no edge-cases related to when the Storage instance is
* created for the page or the StorageCache for the origin. (StorageCache is
* what actually backs the Storage binding exposed to the page.) This
* matters because the following reasons can exist for them to be created:
* - Preload, on the basis of knowing the origin uses localStorage. The
* interesting edge case is when we have the same origin open in different
* processes and the origin starts using localStorage when it did not
* before. Preload will not have instantiated bindings, which could impact
* correctness.
* - The page accessing localStorage for read or write purposes. This is the
* obvious, boring one.
* - The page adding a "storage" listener. This is less obvious and
* interacts with the preload edge-case mentioned above. The page needs to
* hear "storage" events even if the page has not touched localStorage
* itself and its origin had nothing stored in localStorage when the page
* was created.
*
* We use the same simple child page in all tabs that:
* - can be instructed to listen for and record "storage" events
* - can be instructed to issue a series of localStorage writes
* - can be instructed to return the current entire localStorage contents
*
* We open the 5 following tabs:
* - Open a "writer" tab that does not listen for "storage" events and will
* issue only writes.
* - Open a "listener" tab instructed to listen for "storage" events
* immediately. We expect it to capture all events.
* - Open an "reader" tab that does not listen for "storage" events and will
* only issue reads when instructed.
* - Open a "lateWriteThenListen" tab that initially does nothing. We will
* later tell it to issue a write and then listen for events to make sure it
* captures the later events.
* - Open "lateOpenSeesPreload" tab after we've done everything and ensure that
* it preloads/precaches the data without us having touched localStorage or
* added an event listener.
*/
add_task(function*() {
// (There's already one about:blank page open and we open 5 new tabs, so 6
// processes. Actually, 7, just in case.)
yield SpecialPowers.pushPrefEnv({
set: [
["dom.ipc.processCount", 7]
]
});
// Ensure that there is no localstorage data or potential false positives for
// localstorage preloads by forcing the origin to be cleared prior to the
// start of our test.
clearOriginStorageEnsuringNoPreload();
// - Open tabs. Don't configure any of them yet.
const knownTabs = new KnownTabs();
const writerTab = yield* openTestTabInOwnProcess("writer", knownTabs);
const listenerTab = yield* openTestTabInOwnProcess("listener", knownTabs);
const readerTab = yield* openTestTabInOwnProcess("reader", knownTabs);
const lateWriteThenListenTab = yield* openTestTabInOwnProcess(
"lateWriteThenListen", knownTabs);
// Sanity check that preloading did not occur in the tabs.
yield* verifyTabPreload(writerTab, false);
yield* verifyTabPreload(listenerTab, false);
yield* verifyTabPreload(readerTab, false);
// - Configure the tabs.
yield* recordTabStorageEvents(listenerTab);
// - Issue the initial batch of writes and verify.
const initialWriteMutations = [
//[key (null=clear), newValue (null=delete), oldValue (verification)]
["getsCleared", "1", null],
["alsoGetsCleared", "2", null],
[null, null, null],
["stays", "3", null],
["clobbered", "pre", null],
["getsDeletedLater", "4", null],
["getsDeletedImmediately", "5", null],
["getsDeletedImmediately", null, "5"],
["alsoStays", "6", null],
["getsDeletedLater", null, "4"],
["clobbered", "post", "pre"]
];
const initialWriteState = {
stays: "3",
clobbered: "post",
alsoStays: "6"
};
yield* mutateTabStorage(writerTab, initialWriteMutations);
yield* verifyTabStorageState(writerTab, initialWriteState);
yield* verifyTabStorageEvents(listenerTab, initialWriteMutations);
yield* verifyTabStorageState(listenerTab, initialWriteState);
yield* verifyTabStorageState(readerTab, initialWriteState);
// - Issue second set of writes from lateWriteThenListen
const lateWriteMutations = [
["lateStays", "10", null],
["lateClobbered", "latePre", null],
["lateDeleted", "11", null],
["lateClobbered", "lastPost", "latePre"],
["lateDeleted", null, "11"]
];
const lateWriteState = Object.assign({}, initialWriteState, {
lateStays: "10",
lateClobbered: "lastPost"
});
yield* mutateTabStorage(lateWriteThenListenTab, lateWriteMutations);
yield* recordTabStorageEvents(lateWriteThenListenTab);
yield* verifyTabStorageState(writerTab, lateWriteState);
yield* verifyTabStorageEvents(listenerTab, lateWriteMutations);
yield* verifyTabStorageState(listenerTab, lateWriteState);
yield* verifyTabStorageState(readerTab, lateWriteState);
// - Issue last set of writes from writerTab.
const lastWriteMutations = [
["lastStays", "20", null],
["lastDeleted", "21", null],
["lastClobbered", "lastPre", null],
["lastClobbered", "lastPost", "lastPre"],
["lastDeleted", null, "21"]
];
const lastWriteState = Object.assign({}, lateWriteState, {
lastStays: "20",
lastClobbered: "lastPost"
});
yield* mutateTabStorage(writerTab, lastWriteMutations);
yield* verifyTabStorageState(writerTab, lastWriteState);
yield* verifyTabStorageEvents(listenerTab, lastWriteMutations);
yield* verifyTabStorageState(listenerTab, lastWriteState);
yield* verifyTabStorageState(readerTab, lastWriteState);
yield* verifyTabStorageEvents(lateWriteThenListenTab, lastWriteMutations);
yield* verifyTabStorageState(lateWriteThenListenTab, lastWriteState);
// - Open a fresh tab and make sure it sees the precache/preload
const lateOpenSeesPreload =
yield* openTestTabInOwnProcess("lateOpenSeesPreload", knownTabs);
yield* verifyTabPreload(lateOpenSeesPreload, true);
// - Clean up.
yield* cleanupTabs(knownTabs);
clearOriginStorageEnsuringNoPreload();
});

Просмотреть файл

@ -0,0 +1,56 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<script>
/**
* Helper page used by browser_localStorage_e10s.js.
**/
var pageName = document.location.search.substring(1);
window.addEventListener(
"load",
() => { document.getElementById("pageNameH").textContent = pageName; });
var recordedEvents = null;
function storageListener(event) {
recordedEvents.push([event.key, event.newValue, event.oldValue]);
}
function listenForStorageEvents() {
recordedEvents = [];
window.addEventListener("storage", storageListener);
}
function mutateStorage(mutations) {
mutations.forEach(function([key, value]) {
if (key !== null) {
if (value === null) {
localStorage.removeItem(key);
} else {
localStorage.setItem(key, value);
}
} else {
localStorage.clear();
}
});
}
function getStorageState() {
let numKeys = localStorage.length;
let state = {};
for (var iKey = 0; iKey < numKeys; iKey++) {
let key = localStorage.key(iKey);
state[key] = localStorage.getItem(key);
}
return state;
}
function returnAndClearStorageEvents() {
let loggedEvents = recordedEvents;
recordedEvents = [];
return loggedEvents;
}
</script>
</head>
<body><h2 id="pageNameH"></h2></body>
</html>

Просмотреть файл

@ -1557,19 +1557,27 @@ void
CacheCreator::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
{
AssertIsOnMainThread();
MOZ_ASSERT(aValue.isObject());
if (!aValue.isObject()) {
FailLoaders(NS_ERROR_FAILURE);
return;
}
JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
Cache* cache = nullptr;
nsresult rv = UNWRAP_OBJECT(Cache, obj, cache);
MOZ_ALWAYS_SUCCEEDS(rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
FailLoaders(NS_ERROR_FAILURE);
return;
}
mCache = cache;
MOZ_ASSERT(mCache);
MOZ_DIAGNOSTIC_ASSERT(mCache);
// If the worker is canceled, CancelMainThread() will have cleared the
// loaders.
// loaders via DeleteCache().
for (uint32_t i = 0, len = mLoaders.Length(); i < len; ++i) {
MOZ_DIAGNOSTIC_ASSERT(mLoaders[i]);
mLoaders[i]->Load(cache);
}
}
@ -1581,19 +1589,16 @@ CacheCreator::DeleteCache()
// This is called when the load is canceled which can occur before
// mCacheStorage is initialized.
if (!mCacheStorage) {
return;
if (mCacheStorage) {
// It's safe to do this while Cache::Match() and Cache::Put() calls are
// running.
IgnoredErrorResult rv;
RefPtr<Promise> promise = mCacheStorage->Delete(mCacheName, rv);
// We don't care to know the result of the promise object.
}
// It's safe to do this while Cache::Match() and Cache::Put() calls are
// running.
IgnoredErrorResult rv;
RefPtr<Promise> promise = mCacheStorage->Delete(mCacheName, rv);
if (NS_WARN_IF(rv.Failed())) {
return;
}
// We don't care to know the result of the promise object.
// Always call this here to ensure the loaders array is cleared.
FailLoaders(NS_ERROR_FAILURE);
}

Просмотреть файл

@ -494,6 +494,82 @@ private:
}
};
class ReportCompileErrorRunnable final : public WorkerRunnable
{
public:
static void
CreateAndDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
RefPtr<ReportCompileErrorRunnable> runnable =
new ReportCompileErrorRunnable(aCx, aWorkerPrivate);
runnable->Dispatch();
}
private:
ReportCompileErrorRunnable(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
: WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount)
{
aWorkerPrivate->AssertIsOnWorkerThread();
}
void
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
{
aWorkerPrivate->AssertIsOnWorkerThread();
// Dispatch may fail if the worker was canceled, no need to report that as
// an error, so don't call base class PostDispatch.
}
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
{
if (aWorkerPrivate->IsFrozen() ||
aWorkerPrivate->IsParentWindowPaused()) {
MOZ_ASSERT(!IsDebuggerRunnable());
aWorkerPrivate->QueueRunnable(this);
return true;
}
if (aWorkerPrivate->IsSharedWorker()) {
aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, EmptyString(),
EmptyString(),
EmptyString(), 0, 0,
JSREPORT_ERROR,
/* isErrorEvent */ false);
return true;
}
if (aWorkerPrivate->IsServiceWorker()) {
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
if (swm) {
swm->HandleError(aCx, aWorkerPrivate->GetPrincipal(),
aWorkerPrivate->WorkerName(),
aWorkerPrivate->ScriptURL(),
EmptyString(), EmptyString(), EmptyString(),
0, 0, JSREPORT_ERROR, JSEXN_ERR);
}
return true;
}
if (!aWorkerPrivate->IsAcceptingEvents()) {
return true;
}
RefPtr<Event> event =
Event::Constructor(aWorkerPrivate, NS_LITERAL_STRING("error"),
EventInit());
event->SetTrusted(true);
nsEventStatus status = nsEventStatus_eIgnore;
aWorkerPrivate->DispatchDOMEvent(nullptr, event, nullptr, &status);
return true;
}
};
class CompileScriptRunnable final : public WorkerRunnable
{
nsString mScriptURL;
@ -537,9 +613,15 @@ private:
}
// Make sure to propagate exceptions from rv onto aCx, so that they will get
// reported after we return. We do this for all failures on rv, because now
// we're using rv to track all the state we care about.
//
// reported after we return. We want to propagate just JS exceptions,
// because all the other errors are handled when the script is loaded.
// See: https://dom.spec.whatwg.org/#concept-event-fire
if (rv.Failed() && !rv.IsJSException()) {
ReportCompileErrorRunnable::CreateAndDispatch(aCx, aWorkerPrivate);
rv.SuppressException();
return false;
}
// This is a little dumb, but aCx is in the null compartment here because we
// set it up that way in our Run(), since we had not created the global at
// that point yet. So we need to enter the compartment of our global,
@ -1117,7 +1199,8 @@ private:
if (aWorkerPrivate->IsSharedWorker()) {
aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, mMessage, mFilename,
mLine, mLineNumber,
mColumnNumber, mFlags);
mColumnNumber, mFlags,
/* isErrorEvent */ true);
return true;
}
@ -3291,7 +3374,8 @@ WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers(
const nsAString& aLine,
uint32_t aLineNumber,
uint32_t aColumnNumber,
uint32_t aFlags)
uint32_t aFlags,
bool aIsErrorEvent)
{
AssertIsOnMainThread();
@ -3322,31 +3406,42 @@ WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers(
// May be null.
nsPIDOMWindowInner* window = sharedWorker->GetOwner();
RootedDictionary<ErrorEventInit> errorInit(aCx);
errorInit.mBubbles = false;
errorInit.mCancelable = true;
errorInit.mMessage = aMessage;
errorInit.mFilename = aFilename;
errorInit.mLineno = aLineNumber;
errorInit.mColno = aColumnNumber;
RefPtr<Event> event;
RefPtr<ErrorEvent> errorEvent =
ErrorEvent::Constructor(sharedWorker, NS_LITERAL_STRING("error"),
errorInit);
if (!errorEvent) {
if (aIsErrorEvent) {
RootedDictionary<ErrorEventInit> errorInit(aCx);
errorInit.mBubbles = false;
errorInit.mCancelable = true;
errorInit.mMessage = aMessage;
errorInit.mFilename = aFilename;
errorInit.mLineno = aLineNumber;
errorInit.mColno = aColumnNumber;
event = ErrorEvent::Constructor(sharedWorker, NS_LITERAL_STRING("error"),
errorInit);
} else {
event = Event::Constructor(sharedWorker, NS_LITERAL_STRING("error"),
EventInit());
}
if (!event) {
ThrowAndReport(window, NS_ERROR_UNEXPECTED);
continue;
}
errorEvent->SetTrusted(true);
event->SetTrusted(true);
bool defaultActionEnabled;
nsresult rv = sharedWorker->DispatchEvent(errorEvent, &defaultActionEnabled);
nsresult rv = sharedWorker->DispatchEvent(event, &defaultActionEnabled);
if (NS_FAILED(rv)) {
ThrowAndReport(window, rv);
continue;
}
if (!aIsErrorEvent) {
continue;
}
if (defaultActionEnabled) {
// Add the owning window to our list so that we will fire an error event
// at it later.

Просмотреть файл

@ -403,7 +403,8 @@ public:
const nsAString& aLine,
uint32_t aLineNumber,
uint32_t aColumnNumber,
uint32_t aFlags);
uint32_t aFlags,
bool aIsErrorEvent);
void
WorkerScriptLoaded();

Просмотреть файл

@ -25,7 +25,6 @@ Tests of DOM Worker Threads
worker.onerror = function(event) {
is(event.target, worker);
is(event.message, 'NetworkError: Failed to load worker script at "nonexistent_worker.js"');
event.preventDefault();
SimpleTest.finish();
};
@ -38,4 +37,3 @@ Tests of DOM Worker Threads
</pre>
</body>
</html>

Просмотреть файл

@ -25,7 +25,6 @@ function test(script) {
worker.onerror = function(event) {
is(event.target, worker);
ok(event.message.startsWith("NetworkError: Failed to load worker script"))
event.preventDefault();
runTests();
};

Просмотреть файл

@ -13,15 +13,13 @@
<script class="testbody" type="text/javascript">
"use strict";
var loadErrorMessage = 'SecurityError: Failed to load worker script at "about:blank"';
function nextTest() {
(function(){
function workerfunc() {
var subworker = new Worker("about:blank");
subworker.onerror = function(e) {
e.preventDefault();
postMessage(e.message);
postMessage("ERROR");
}
}
var b = new Blob([workerfunc+'workerfunc();']);
@ -37,7 +35,7 @@ function nextTest() {
return;
}
w.onmessage = function(e) {
is(e.data, loadErrorMessage,
is(e.data, "ERROR",
"Should catch the error when loading inner script");
if (++i < 2) callworker(i);
else SimpleTest.finish();
@ -54,8 +52,6 @@ try {
var worker = new Worker("about:blank");
worker.onerror = function(e) {
e.preventDefault();
is(e.message, loadErrorMessage,
"Should get the right error from the toplevel script");
nextTest();
}

Просмотреть файл

@ -5070,19 +5070,16 @@ EditorBase::IsActiveInDOMWindow()
}
bool
EditorBase::IsAcceptableInputEvent(nsIDOMEvent* aEvent)
EditorBase::IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent)
{
// If the event is trusted, the event should always cause input.
NS_ENSURE_TRUE(aEvent, false);
WidgetEvent* widgetEvent = aEvent->WidgetEventPtr();
if (NS_WARN_IF(!widgetEvent)) {
if (NS_WARN_IF(!aGUIEvent)) {
return false;
}
// If this is dispatched by using cordinates but this editor doesn't have
// focus, we shouldn't handle it.
if (widgetEvent->IsUsingCoordinates()) {
if (aGUIEvent->IsUsingCoordinates()) {
nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
if (!focusedContent) {
return false;
@ -5095,8 +5092,7 @@ EditorBase::IsAcceptableInputEvent(nsIDOMEvent* aEvent)
// Note that if we allow to handle such events, editor may be confused by
// strange event order.
bool needsWidget = false;
WidgetGUIEvent* widgetGUIEvent = nullptr;
switch (widgetEvent->mMessage) {
switch (aGUIEvent->mMessage) {
case eUnidentifiedEvent:
// If events are not created with proper event interface, their message
// are initialized with eUnidentifiedEvent. Let's ignore such event.
@ -5108,25 +5104,26 @@ EditorBase::IsAcceptableInputEvent(nsIDOMEvent* aEvent)
case eCompositionCommitAsIs:
// Don't allow composition events whose internal event are not
// WidgetCompositionEvent.
widgetGUIEvent = aEvent->WidgetEventPtr()->AsCompositionEvent();
if (!aGUIEvent->AsCompositionEvent()) {
return false;
}
needsWidget = true;
break;
default:
break;
}
if (needsWidget &&
(!widgetGUIEvent || !widgetGUIEvent->mWidget)) {
if (needsWidget && !aGUIEvent->mWidget) {
return false;
}
// Accept all trusted events.
if (widgetEvent->IsTrusted()) {
if (aGUIEvent->IsTrusted()) {
return true;
}
// Ignore untrusted mouse event.
// XXX Why are we handling other untrusted input events?
if (widgetEvent->AsMouseEventBase()) {
if (aGUIEvent->AsMouseEventBase()) {
return false;
}

Просмотреть файл

@ -259,7 +259,8 @@ public:
* IME event handlers.
*/
virtual nsresult BeginIMEComposition(WidgetCompositionEvent* aEvent);
virtual nsresult UpdateIMEComposition(nsIDOMEvent* aDOMTextEvent) = 0;
virtual nsresult UpdateIMEComposition(
WidgetCompositionEvent* aCompositionChangeEvet) = 0;
void EndIMEComposition();
void SwitchTextDirectionTo(uint32_t aDirection);
@ -880,12 +881,12 @@ public:
virtual bool IsActiveInDOMWindow();
/**
* Whether the aEvent should be handled by this editor or not. When this
* returns FALSE, The aEvent shouldn't be handled on this editor,
* i.e., The aEvent should be handled by another inner editor or ancestor
* Whether the aGUIEvent should be handled by this editor or not. When this
* returns false, The aGUIEvent shouldn't be handled on this editor,
* i.e., The aGUIEvent should be handled by another inner editor or ancestor
* elements.
*/
virtual bool IsAcceptableInputEvent(nsIDOMEvent* aEvent);
virtual bool IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent);
/**
* FindSelectionRoot() returns a selection root of this editor when aNode

Просмотреть файл

@ -213,7 +213,7 @@ EditorEventListener::InstallToEditor()
void
EditorEventListener::Disconnect()
{
if (!mEditorBase) {
if (DetachedFromEditor()) {
return;
}
UninstallFromEditor();
@ -300,8 +300,7 @@ EditorEventListener::UninstallFromEditor()
already_AddRefed<nsIPresShell>
EditorEventListener::GetPresShell()
{
NS_PRECONDITION(mEditorBase,
"The caller must check whether this is connected to an editor");
MOZ_ASSERT(!DetachedFromEditor());
return mEditorBase->GetPresShell();
}
@ -315,8 +314,7 @@ EditorEventListener::GetPresContext()
nsIContent*
EditorEventListener::GetFocusedRootContent()
{
NS_ENSURE_TRUE(mEditorBase, nullptr);
MOZ_ASSERT(!DetachedFromEditor());
nsCOMPtr<nsIContent> focusedContent = mEditorBase->GetFocusedContent();
if (!focusedContent) {
return nullptr;
@ -335,8 +333,7 @@ EditorEventListener::GetFocusedRootContent()
bool
EditorEventListener::EditorHasFocus()
{
NS_PRECONDITION(mEditorBase,
"The caller must check whether this is connected to an editor");
MOZ_ASSERT(!DetachedFromEditor());
nsCOMPtr<nsIContent> focusedContent = mEditorBase->GetFocusedContent();
if (!focusedContent) {
return false;
@ -347,16 +344,23 @@ EditorEventListener::EditorHasFocus()
NS_IMPL_ISUPPORTS(EditorEventListener, nsIDOMEventListener)
bool
EditorEventListener::DetachedFromEditor() const
{
return !mEditorBase;
}
bool
EditorEventListener::DetachedFromEditorOrDefaultPrevented(
WidgetEvent* aWidgetEvent) const
{
return NS_WARN_IF(!aWidgetEvent) || DetachedFromEditor() ||
aWidgetEvent->DefaultPrevented();
}
NS_IMETHODIMP
EditorEventListener::HandleEvent(nsIDOMEvent* aEvent)
{
NS_ENSURE_TRUE(mEditorBase, NS_ERROR_FAILURE);
nsCOMPtr<nsIEditor> kungFuDeathGrip = mEditorBase;
Unused << kungFuDeathGrip; // mEditorBase is not referred to in this function
WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
// Let's handle each event with the message of the internal event of the
// coming event. If the DOM event was created with improper interface,
// e.g., keydown event is created with |new MouseEvent("keydown", {});|,
@ -367,6 +371,7 @@ EditorEventListener::HandleEvent(nsIDOMEvent* aEvent)
// calling it, this queries the specific interface. If it would fail,
// each event handler would just ignore the event. So, in this method,
// you don't need to check if the QI succeeded before each call.
WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
switch (internalEvent->mMessage) {
// dragenter
case eDragEnter: {
@ -453,19 +458,19 @@ EditorEventListener::HandleEvent(nsIDOMEvent* aEvent)
}
// focus
case eFocus:
return Focus(aEvent);
return Focus(internalEvent);
// blur
case eBlur:
return Blur(aEvent);
return Blur(internalEvent);
// text
case eCompositionChange:
return HandleText(aEvent);
return HandleChangeComposition(internalEvent->AsCompositionEvent());
// compositionstart
case eCompositionStart:
return HandleStartComposition(aEvent);
return HandleStartComposition(internalEvent->AsCompositionEvent());
// compositionend
case eCompositionEnd:
HandleEndComposition(aEvent);
HandleEndComposition(internalEvent->AsCompositionEvent());
return NS_OK;
default:
break;
@ -476,10 +481,10 @@ EditorEventListener::HandleEvent(nsIDOMEvent* aEvent)
// We should accept "focus" and "blur" event even if it's synthesized with
// wrong interface for compatibility with older Gecko.
if (eventType.EqualsLiteral("focus")) {
return Focus(aEvent);
return Focus(internalEvent);
}
if (eventType.EqualsLiteral("blur")) {
return Blur(aEvent);
return Blur(internalEvent);
}
#ifdef DEBUG
nsPrintfCString assertMessage("Editor doesn't handle \"%s\" event "
@ -540,18 +545,22 @@ bool IsCtrlShiftPressed(nsIDOMKeyEvent* aEvent, bool& isRTL)
nsresult
EditorEventListener::KeyUp(nsIDOMKeyEvent* aKeyEvent)
{
NS_ENSURE_TRUE(aKeyEvent, NS_OK);
if (NS_WARN_IF(!aKeyEvent) || DetachedFromEditor()) {
return NS_OK;
}
if (!mHaveBidiKeyboards) {
return NS_OK;
}
// XXX Why doesn't this method check if it's consumed?
RefPtr<EditorBase> editorBase(mEditorBase);
uint32_t keyCode = 0;
aKeyEvent->GetKeyCode(&keyCode);
if ((keyCode == nsIDOMKeyEvent::DOM_VK_SHIFT ||
keyCode == nsIDOMKeyEvent::DOM_VK_CONTROL) &&
mShouldSwitchTextDirection && mEditorBase->IsPlaintextEditor()) {
mEditorBase->SwitchTextDirectionTo(mSwitchToRTL ?
mShouldSwitchTextDirection && editorBase->IsPlaintextEditor()) {
editorBase->SwitchTextDirectionTo(mSwitchToRTL ?
nsIPlaintextEditor::eEditorRightToLeft :
nsIPlaintextEditor::eEditorLeftToRight);
mShouldSwitchTextDirection = false;
@ -562,12 +571,15 @@ EditorEventListener::KeyUp(nsIDOMKeyEvent* aKeyEvent)
nsresult
EditorEventListener::KeyDown(nsIDOMKeyEvent* aKeyEvent)
{
NS_ENSURE_TRUE(aKeyEvent, NS_OK);
if (NS_WARN_IF(!aKeyEvent) || DetachedFromEditor()) {
return NS_OK;
}
if (!mHaveBidiKeyboards) {
return NS_OK;
}
// XXX Why isn't this method check if it's consumed?
uint32_t keyCode = 0;
aKeyEvent->GetKeyCode(&keyCode);
if (keyCode == nsIDOMKeyEvent::DOM_VK_SHIFT) {
@ -587,29 +599,23 @@ EditorEventListener::KeyDown(nsIDOMKeyEvent* aKeyEvent)
nsresult
EditorEventListener::KeyPress(nsIDOMKeyEvent* aKeyEvent)
{
NS_ENSURE_TRUE(aKeyEvent, NS_OK);
if (!mEditorBase->IsAcceptableInputEvent(aKeyEvent->AsEvent())) {
if (NS_WARN_IF(!aKeyEvent)) {
return NS_OK;
}
// DOM event handling happens in two passes, the client pass and the system
// pass. We do all of our processing in the system pass, to allow client
// handlers the opportunity to cancel events and prevent typing in the editor.
// If the client pass cancelled the event, defaultPrevented will be true
// below.
bool defaultPrevented;
aKeyEvent->AsEvent()->GetDefaultPrevented(&defaultPrevented);
if (defaultPrevented) {
RefPtr<EditorBase> editorBase(mEditorBase);
WidgetKeyboardEvent* keypressEvent =
aKeyEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
MOZ_ASSERT(keypressEvent,
"DOM key event's internal event must be WidgetKeyboardEvent");
if (!editorBase->IsAcceptableInputEvent(keypressEvent) ||
DetachedFromEditorOrDefaultPrevented(keypressEvent)) {
return NS_OK;
}
nsresult rv = mEditorBase->HandleKeyPressEvent(aKeyEvent);
nsresult rv = editorBase->HandleKeyPressEvent(aKeyEvent);
NS_ENSURE_SUCCESS(rv, rv);
aKeyEvent->AsEvent()->GetDefaultPrevented(&defaultPrevented);
if (defaultPrevented) {
if (DetachedFromEditorOrDefaultPrevented(keypressEvent)) {
return NS_OK;
}
@ -618,11 +624,7 @@ EditorEventListener::KeyPress(nsIDOMKeyEvent* aKeyEvent)
}
// Now, ask the native key bindings to handle the event.
WidgetKeyboardEvent* keyEvent =
aKeyEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
MOZ_ASSERT(keyEvent,
"DOM key event's internal event must be WidgetKeyboardEvent");
nsIWidget* widget = keyEvent->mWidget;
nsIWidget* widget = keypressEvent->mWidget;
// If the event is created by chrome script, the widget is always nullptr.
if (!widget) {
nsCOMPtr<nsIPresShell> ps = GetPresShell();
@ -631,10 +633,10 @@ EditorEventListener::KeyPress(nsIDOMKeyEvent* aKeyEvent)
NS_ENSURE_TRUE(widget, NS_OK);
}
nsCOMPtr<nsIDocument> doc = mEditorBase->GetDocument();
nsCOMPtr<nsIDocument> doc = editorBase->GetDocument();
bool handled = widget->ExecuteNativeKeyBinding(
nsIWidget::NativeKeyBindingsForRichTextEditor,
*keyEvent, DoCommandCallback, doc);
*keypressEvent, DoCommandCallback, doc);
if (handled) {
aKeyEvent->AsEvent()->PreventDefault();
}
@ -644,9 +646,15 @@ EditorEventListener::KeyPress(nsIDOMKeyEvent* aKeyEvent)
nsresult
EditorEventListener::MouseClick(nsIDOMMouseEvent* aMouseEvent)
{
if (NS_WARN_IF(!aMouseEvent) || DetachedFromEditor()) {
return NS_OK;
}
// nothing to do if editor isn't editable or clicked on out of the editor.
if (mEditorBase->IsReadonly() || mEditorBase->IsDisabled() ||
!mEditorBase->IsAcceptableInputEvent(aMouseEvent->AsEvent())) {
RefPtr<EditorBase> editorBase(mEditorBase);
WidgetMouseEvent* clickEvent =
aMouseEvent->AsEvent()->WidgetEventPtr()->AsMouseEvent();
if (editorBase->IsReadonly() || editorBase->IsDisabled() ||
!editorBase->IsAcceptableInputEvent(clickEvent)) {
return NS_OK;
}
@ -657,26 +665,23 @@ EditorEventListener::MouseClick(nsIDOMMouseEvent* aMouseEvent)
if (presContext) {
IMEStateManager::OnClickInEditor(presContext, GetFocusedRootContent(),
aMouseEvent);
}
if (DetachedFromEditor()) {
return NS_OK;
}
}
}
bool preventDefault;
nsresult rv = aMouseEvent->AsEvent()->GetDefaultPrevented(&preventDefault);
if (NS_FAILED(rv) || preventDefault) {
if (DetachedFromEditorOrDefaultPrevented(clickEvent)) {
// We're done if 'preventdefault' is true (see for example bug 70698).
return rv;
}
// IMEStateManager::OnClickInEditor() may cause anything because it may
// set input context. For example, it may cause opening VKB, changing focus
// or reflow. So, mEditorBase here might have been gone.
if (!mEditorBase) {
return NS_OK;
}
// If we got a mouse down inside the editing area, we should force the
// IME to commit before we change the cursor position
mEditorBase->ForceCompositionEnd();
editorBase->ForceCompositionEnd();
if (DetachedFromEditor()) {
return NS_OK;
}
int16_t button = -1;
aMouseEvent->GetButton(&button);
@ -689,6 +694,10 @@ EditorEventListener::MouseClick(nsIDOMMouseEvent* aMouseEvent)
nsresult
EditorEventListener::HandleMiddleClickPaste(nsIDOMMouseEvent* aMouseEvent)
{
MOZ_ASSERT(aMouseEvent);
MOZ_ASSERT(!DetachedFromEditorOrDefaultPrevented(
aMouseEvent->AsEvent()->WidgetEventPtr()));
if (!Preferences::GetBool("middlemouse.paste", false)) {
// Middle click paste isn't enabled.
return NS_OK;
@ -704,7 +713,8 @@ EditorEventListener::HandleMiddleClickPaste(nsIDOMMouseEvent* aMouseEvent)
return NS_ERROR_NULL_POINTER;
}
RefPtr<Selection> selection = mEditorBase->GetSelection();
RefPtr<EditorBase> editorBase(mEditorBase);
RefPtr<Selection> selection = editorBase->GetSelection();
if (selection) {
selection->Collapse(parent, offset);
}
@ -716,7 +726,7 @@ EditorEventListener::HandleMiddleClickPaste(nsIDOMMouseEvent* aMouseEvent)
nsCOMPtr<nsIEditorMailSupport> mailEditor;
if (ctrlKey) {
mailEditor = do_QueryObject(mEditorBase);
mailEditor = do_QueryObject(editorBase);
}
nsresult rv;
@ -734,7 +744,7 @@ EditorEventListener::HandleMiddleClickPaste(nsIDOMMouseEvent* aMouseEvent)
if (mailEditor) {
mailEditor->PasteAsQuotation(clipboard);
} else {
mEditorBase->Paste(clipboard);
editorBase->Paste(clipboard);
}
// Prevent the event from propagating up to be possibly handled
@ -750,16 +760,12 @@ bool
EditorEventListener::NotifyIMEOfMouseButtonEvent(
nsIDOMMouseEvent* aMouseEvent)
{
MOZ_ASSERT(aMouseEvent);
if (!EditorHasFocus()) {
return false;
}
bool defaultPrevented;
nsresult rv = aMouseEvent->AsEvent()->GetDefaultPrevented(&defaultPrevented);
NS_ENSURE_SUCCESS(rv, false);
if (defaultPrevented) {
return false;
}
nsPresContext* presContext = GetPresContext();
NS_ENSURE_TRUE(presContext, false);
return IMEStateManager::OnMouseButtonEventInEditor(presContext,
@ -770,27 +776,36 @@ EditorEventListener::NotifyIMEOfMouseButtonEvent(
nsresult
EditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent)
{
// FYI: We don't need to check if it's already consumed here because
// we need to commit composition at mouse button operation.
// FYI: This may be called by HTMLEditorEventListener::MouseDown() even
// when the event is not acceptable for committing composition.
if (mEditorBase) {
mEditorBase->ForceCompositionEnd();
if (DetachedFromEditor()) {
return NS_OK;
}
RefPtr<EditorBase> editorBase(mEditorBase);
editorBase->ForceCompositionEnd();
return NS_OK;
}
nsresult
EditorEventListener::HandleText(nsIDOMEvent* aTextEvent)
EditorEventListener::HandleChangeComposition(
WidgetCompositionEvent* aCompositionChangeEvent)
{
if (!mEditorBase->IsAcceptableInputEvent(aTextEvent)) {
MOZ_ASSERT(!aCompositionChangeEvent->DefaultPrevented(),
"eCompositionChange event shouldn't be cancelable");
RefPtr<EditorBase> editorBase(mEditorBase);
if (DetachedFromEditor() ||
!editorBase->IsAcceptableInputEvent(aCompositionChangeEvent)) {
return NS_OK;
}
// if we are readonly or disabled, then do nothing.
if (mEditorBase->IsReadonly() || mEditorBase->IsDisabled()) {
if (editorBase->IsReadonly() || editorBase->IsDisabled()) {
return NS_OK;
}
return mEditorBase->UpdateIMEComposition(aTextEvent);
return editorBase->UpdateIMEComposition(aCompositionChangeEvent);
}
/**
@ -800,7 +815,9 @@ EditorEventListener::HandleText(nsIDOMEvent* aTextEvent)
nsresult
EditorEventListener::DragEnter(nsIDOMDragEvent* aDragEvent)
{
NS_ENSURE_TRUE(aDragEvent, NS_OK);
if (NS_WARN_IF(!aDragEvent) || DetachedFromEditor()) {
return NS_OK;
}
nsCOMPtr<nsIPresShell> presShell = GetPresShell();
NS_ENSURE_TRUE(presShell, NS_OK);
@ -823,15 +840,13 @@ EditorEventListener::DragEnter(nsIDOMDragEvent* aDragEvent)
nsresult
EditorEventListener::DragOver(nsIDOMDragEvent* aDragEvent)
{
NS_ENSURE_TRUE(aDragEvent, NS_OK);
nsCOMPtr<nsIDOMNode> parent;
bool defaultPrevented;
aDragEvent->AsEvent()->GetDefaultPrevented(&defaultPrevented);
if (defaultPrevented) {
if (NS_WARN_IF(!aDragEvent) ||
DetachedFromEditorOrDefaultPrevented(
aDragEvent->AsEvent()->WidgetEventPtr())) {
return NS_OK;
}
nsCOMPtr<nsIDOMNode> parent;
aDragEvent->GetRangeParent(getter_AddRefs(parent));
nsCOMPtr<nsIContent> dropParent = do_QueryInterface(parent);
NS_ENSURE_TRUE(dropParent, NS_ERROR_FAILURE);
@ -886,7 +901,15 @@ EditorEventListener::CleanupDragDropCaret()
nsresult
EditorEventListener::DragExit(nsIDOMDragEvent* aDragEvent)
{
NS_ENSURE_TRUE(aDragEvent, NS_OK);
// XXX If aDragEvent was created by chrome script, its defaultPrevented
// may be true, though. We shouldn't handle such event but we don't
// have a way to distinguish if coming event is created by chrome script.
NS_WARNING_ASSERTION(
!aDragEvent->AsEvent()->WidgetEventPtr()->DefaultPrevented(),
"eDragExit shouldn't be cancelable");
if (NS_WARN_IF(!aDragEvent) || DetachedFromEditor()) {
return NS_OK;
}
CleanupDragDropCaret();
@ -896,13 +919,11 @@ EditorEventListener::DragExit(nsIDOMDragEvent* aDragEvent)
nsresult
EditorEventListener::Drop(nsIDOMDragEvent* aDragEvent)
{
NS_ENSURE_TRUE(aDragEvent, NS_OK);
CleanupDragDropCaret();
bool defaultPrevented;
aDragEvent->AsEvent()->GetDefaultPrevented(&defaultPrevented);
if (defaultPrevented) {
if (NS_WARN_IF(!aDragEvent) ||
DetachedFromEditorOrDefaultPrevented(
aDragEvent->AsEvent()->WidgetEventPtr())) {
return NS_OK;
}
@ -913,7 +934,8 @@ EditorEventListener::Drop(nsIDOMDragEvent* aDragEvent)
if (!dropParent->IsEditable() || !CanDrop(aDragEvent)) {
// was it because we're read-only?
if ((mEditorBase->IsReadonly() || mEditorBase->IsDisabled()) &&
RefPtr<EditorBase> editorBase(mEditorBase);
if ((editorBase->IsReadonly() || editorBase->IsDisabled()) &&
!IsFileControlTextBox()) {
// it was decided to "eat" the event as this is the "least surprise"
// since someone else handling it might be unintentional and the
@ -926,14 +948,19 @@ EditorEventListener::Drop(nsIDOMDragEvent* aDragEvent)
aDragEvent->AsEvent()->StopPropagation();
aDragEvent->AsEvent()->PreventDefault();
return mEditorBase->InsertFromDrop(aDragEvent->AsEvent());
RefPtr<EditorBase> editorBase(mEditorBase);
return editorBase->InsertFromDrop(aDragEvent->AsEvent());
}
bool
EditorEventListener::CanDrop(nsIDOMDragEvent* aEvent)
{
MOZ_ASSERT(!DetachedFromEditorOrDefaultPrevented(
aEvent->AsEvent()->WidgetEventPtr()));
// if the target doc is read-only, we can't drop
if (mEditorBase->IsReadonly() || mEditorBase->IsDisabled()) {
RefPtr<EditorBase> editorBase(mEditorBase);
if (editorBase->IsReadonly() || editorBase->IsDisabled()) {
return false;
}
@ -949,7 +976,7 @@ EditorEventListener::CanDrop(nsIDOMDragEvent* aEvent)
// can be dropped as well.
if (!types.Contains(NS_LITERAL_STRING(kTextMime)) &&
!types.Contains(NS_LITERAL_STRING(kMozTextInternal)) &&
(mEditorBase->IsPlaintextEditor() ||
(editorBase->IsPlaintextEditor() ||
(!types.Contains(NS_LITERAL_STRING(kHTMLMime)) &&
!types.Contains(NS_LITERAL_STRING(kFileMime))))) {
return false;
@ -967,7 +994,7 @@ EditorEventListener::CanDrop(nsIDOMDragEvent* aEvent)
// There is a source node, so compare the source documents and this document.
// Disallow drops on the same document.
nsCOMPtr<nsIDOMDocument> domdoc = mEditorBase->GetDOMDocument();
nsCOMPtr<nsIDOMDocument> domdoc = editorBase->GetDOMDocument();
NS_ENSURE_TRUE(domdoc, false);
nsCOMPtr<nsIDOMDocument> sourceDoc;
@ -987,7 +1014,7 @@ EditorEventListener::CanDrop(nsIDOMDragEvent* aEvent)
return true;
}
RefPtr<Selection> selection = mEditorBase->GetSelection();
RefPtr<Selection> selection = editorBase->GetSelection();
if (!selection) {
return false;
}
@ -1029,46 +1056,63 @@ EditorEventListener::CanDrop(nsIDOMDragEvent* aEvent)
}
nsresult
EditorEventListener::HandleStartComposition(nsIDOMEvent* aCompositionEvent)
EditorEventListener::HandleStartComposition(
WidgetCompositionEvent* aCompositionStartEvent)
{
if (!mEditorBase->IsAcceptableInputEvent(aCompositionEvent)) {
RefPtr<EditorBase> editorBase(mEditorBase);
if (DetachedFromEditor() ||
!editorBase->IsAcceptableInputEvent(aCompositionStartEvent)) {
return NS_OK;
}
WidgetCompositionEvent* compositionStart =
aCompositionEvent->WidgetEventPtr()->AsCompositionEvent();
return mEditorBase->BeginIMEComposition(compositionStart);
// Although, "compositionstart" should be cancelable, but currently,
// eCompositionStart event coming from widget is not cancelable.
MOZ_ASSERT(!aCompositionStartEvent->DefaultPrevented(),
"eCompositionStart shouldn't be cancelable");
return editorBase->BeginIMEComposition(aCompositionStartEvent);
}
void
EditorEventListener::HandleEndComposition(nsIDOMEvent* aCompositionEvent)
EditorEventListener::HandleEndComposition(
WidgetCompositionEvent* aCompositionEndEvent)
{
if (!mEditorBase->IsAcceptableInputEvent(aCompositionEvent)) {
RefPtr<EditorBase> editorBase(mEditorBase);
if (DetachedFromEditor() ||
!editorBase->IsAcceptableInputEvent(aCompositionEndEvent)) {
return;
}
mEditorBase->EndIMEComposition();
MOZ_ASSERT(!aCompositionEndEvent->DefaultPrevented(),
"eCompositionEnd shouldn't be cancelable");
editorBase->EndIMEComposition();
}
nsresult
EditorEventListener::Focus(nsIDOMEvent* aEvent)
EditorEventListener::Focus(WidgetEvent* aFocusEvent)
{
NS_ENSURE_TRUE(aEvent, NS_OK);
if (NS_WARN_IF(!aFocusEvent) || DetachedFromEditor()) {
return NS_OK;
}
// XXX If aFocusEvent was created by chrome script, its defaultPrevented
// may be true, though. We shouldn't handle such event but we don't
// have a way to distinguish if coming event is created by chrome script.
NS_WARNING_ASSERTION(!aFocusEvent->DefaultPrevented(),
"eFocus event shouldn't be cancelable");
// Don't turn on selection and caret when the editor is disabled.
if (mEditorBase->IsDisabled()) {
RefPtr<EditorBase> editorBase(mEditorBase);
if (editorBase->IsDisabled()) {
return NS_OK;
}
// Spell check a textarea the first time that it is focused.
SpellCheckIfNeeded();
if (!mEditorBase) {
if (!editorBase) {
// In e10s, this can cause us to flush notifications, which can destroy
// the node we're about to focus.
return NS_OK;
}
nsCOMPtr<nsIDOMEventTarget> target;
aEvent->GetTarget(getter_AddRefs(target));
nsCOMPtr<nsIDOMEventTarget> target = aFocusEvent->GetDOMEventTarget();
nsCOMPtr<nsINode> node = do_QueryInterface(target);
NS_ENSURE_TRUE(node, NS_ERROR_UNEXPECTED);
@ -1086,7 +1130,7 @@ EditorEventListener::Focus(nsIDOMEvent* aEvent)
// contenteditable editor. So, the editableRoot value is invalid for
// the plain text editor, and it will be set to the wrong limiter of
// the selection. However, fortunately, actual bugs are not found yet.
nsCOMPtr<nsIContent> editableRoot = mEditorBase->FindSelectionRoot(node);
nsCOMPtr<nsIContent> editableRoot = editorBase->FindSelectionRoot(node);
// make sure that the element is really focused in case an earlier
// listener in the chain changed the focus.
@ -1100,8 +1144,8 @@ EditorEventListener::Focus(nsIDOMEvent* aEvent)
return NS_OK;
}
nsCOMPtr<nsIDOMEventTarget> originalTarget;
aEvent->GetOriginalTarget(getter_AddRefs(originalTarget));
nsCOMPtr<nsIDOMEventTarget> originalTarget =
aFocusEvent->GetOriginalDOMEventTarget();
nsCOMPtr<nsIContent> originalTargetAsContent =
do_QueryInterface(originalTarget);
@ -1116,21 +1160,32 @@ EditorEventListener::Focus(nsIDOMEvent* aEvent)
}
}
mEditorBase->OnFocus(target);
editorBase->OnFocus(target);
if (DetachedFromEditorOrDefaultPrevented(aFocusEvent)) {
return NS_OK;
}
nsCOMPtr<nsIPresShell> ps = GetPresShell();
NS_ENSURE_TRUE(ps, NS_OK);
nsCOMPtr<nsIContent> focusedContent = mEditorBase->GetFocusedContentForIME();
nsCOMPtr<nsIContent> focusedContent = editorBase->GetFocusedContentForIME();
IMEStateManager::OnFocusInEditor(ps->GetPresContext(), focusedContent,
mEditorBase);
editorBase);
return NS_OK;
}
nsresult
EditorEventListener::Blur(nsIDOMEvent* aEvent)
EditorEventListener::Blur(WidgetEvent* aBlurEvent)
{
NS_ENSURE_TRUE(aEvent, NS_OK);
if (NS_WARN_IF(!aBlurEvent) || DetachedFromEditor()) {
return NS_OK;
}
// XXX If aBlurEvent was created by chrome script, its defaultPrevented
// may be true, though. We shouldn't handle such event but we don't
// have a way to distinguish if coming event is created by chrome script.
NS_WARNING_ASSERTION(!aBlurEvent->DefaultPrevented(),
"eBlur event shouldn't be cancelable");
// check if something else is focused. If another element is focused, then
// we should not change the selection.
@ -1140,7 +1195,8 @@ EditorEventListener::Blur(nsIDOMEvent* aEvent)
nsCOMPtr<nsIDOMElement> element;
fm->GetFocusedElement(getter_AddRefs(element));
if (!element) {
mEditorBase->FinalizeSelection();
RefPtr<EditorBase> editorBase(mEditorBase);
editorBase->FinalizeSelection();
}
return NS_OK;
}
@ -1148,20 +1204,26 @@ EditorEventListener::Blur(nsIDOMEvent* aEvent)
void
EditorEventListener::SpellCheckIfNeeded()
{
MOZ_ASSERT(!DetachedFromEditor());
// If the spell check skip flag is still enabled from creation time,
// disable it because focused editors are allowed to spell check.
RefPtr<EditorBase> editorBase(mEditorBase);
uint32_t currentFlags = 0;
mEditorBase->GetFlags(&currentFlags);
editorBase->GetFlags(&currentFlags);
if(currentFlags & nsIPlaintextEditor::eEditorSkipSpellCheck) {
currentFlags ^= nsIPlaintextEditor::eEditorSkipSpellCheck;
mEditorBase->SetFlags(currentFlags);
editorBase->SetFlags(currentFlags);
}
}
bool
EditorEventListener::IsFileControlTextBox()
{
Element* root = mEditorBase->GetRoot();
MOZ_ASSERT(!DetachedFromEditor());
RefPtr<EditorBase> editorBase(mEditorBase);
Element* root = editorBase->GetRoot();
if (!root || !root->ChromeOnlyAccess()) {
return false;
}
@ -1176,6 +1238,8 @@ EditorEventListener::IsFileControlTextBox()
bool
EditorEventListener::ShouldHandleNativeKeyBindings(nsIDOMKeyEvent* aKeyEvent)
{
MOZ_ASSERT(!DetachedFromEditor());
// Only return true if the target of the event is a desendant of the active
// editing host in order to match the similar decision made in
// nsXBLWindowKeyHandler.
@ -1191,13 +1255,14 @@ EditorEventListener::ShouldHandleNativeKeyBindings(nsIDOMKeyEvent* aKeyEvent)
return false;
}
RefPtr<EditorBase> editorBase(mEditorBase);
nsCOMPtr<nsIHTMLEditor> htmlEditor =
do_QueryInterface(static_cast<nsIEditor*>(mEditorBase));
do_QueryInterface(static_cast<nsIEditor*>(editorBase));
if (!htmlEditor) {
return false;
}
nsCOMPtr<nsIDocument> doc = mEditorBase->GetDocument();
nsCOMPtr<nsIDocument> doc = editorBase->GetDocument();
if (doc->HasFlag(NODE_IS_EDITABLE)) {
// Don't need to perform any checks in designMode documents.
return true;

Просмотреть файл

@ -6,6 +6,7 @@
#ifndef EditorEventListener_h
#define EditorEventListener_h
#include "mozilla/EventForwards.h"
#include "nsCOMPtr.h"
#include "nsError.h"
#include "nsIDOMEventListener.h"
@ -60,14 +61,14 @@ protected:
nsresult KeyUp(nsIDOMKeyEvent* aKeyEvent);
#endif
nsresult KeyPress(nsIDOMKeyEvent* aKeyEvent);
nsresult HandleText(nsIDOMEvent* aTextEvent);
nsresult HandleStartComposition(nsIDOMEvent* aCompositionEvent);
void HandleEndComposition(nsIDOMEvent* aCompositionEvent);
nsresult HandleChangeComposition(WidgetCompositionEvent* aCompositionEvent);
nsresult HandleStartComposition(WidgetCompositionEvent* aCompositionEvent);
void HandleEndComposition(WidgetCompositionEvent* aCompositionEvent);
virtual nsresult MouseDown(nsIDOMMouseEvent* aMouseEvent);
virtual nsresult MouseUp(nsIDOMMouseEvent* aMouseEvent) { return NS_OK; }
virtual nsresult MouseClick(nsIDOMMouseEvent* aMouseEvent);
nsresult Focus(nsIDOMEvent* aEvent);
nsresult Blur(nsIDOMEvent* aEvent);
nsresult Focus(WidgetEvent* aFocusEvent);
nsresult Blur(WidgetEvent* aBlurEvent);
nsresult DragEnter(nsIDOMDragEvent* aDragEvent);
nsresult DragOver(nsIDOMDragEvent* aDragEvent);
nsresult DragExit(nsIDOMDragEvent* aDragEvent);
@ -85,6 +86,19 @@ protected:
bool ShouldHandleNativeKeyBindings(nsIDOMKeyEvent* aKeyEvent);
nsresult HandleMiddleClickPaste(nsIDOMMouseEvent* aMouseEvent);
/**
* DetachedFromEditor() returns true if editor was detached.
* Otherwise, false.
*/
bool DetachedFromEditor() const;
/**
* DetachedFromEditorOrDefaultPrevented() returns true if editor was detached
* and/or the event was consumed. Otherwise, i.e., attached editor can
* handle the event, returns true.
*/
bool DetachedFromEditorOrDefaultPrevented(WidgetEvent* aEvent) const;
EditorBase* mEditorBase; // weak
RefPtr<nsCaret> mCaret;
bool mCommitText;

Просмотреть файл

@ -5171,23 +5171,22 @@ HTMLEditor::OurWindowHasFocus()
}
bool
HTMLEditor::IsAcceptableInputEvent(nsIDOMEvent* aEvent)
HTMLEditor::IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent)
{
if (!EditorBase::IsAcceptableInputEvent(aEvent)) {
if (!EditorBase::IsAcceptableInputEvent(aGUIEvent)) {
return false;
}
// While there is composition, all composition events in its top level window
// are always fired on the composing editor. Therefore, if this editor has
// composition, the composition events should be handled in this editor.
if (mComposition && aEvent->WidgetEventPtr()->AsCompositionEvent()) {
if (mComposition && aGUIEvent->AsCompositionEvent()) {
return true;
}
NS_ENSURE_TRUE(mDocWeak, false);
nsCOMPtr<nsIDOMEventTarget> target;
aEvent->GetTarget(getter_AddRefs(target));
nsCOMPtr<nsIDOMEventTarget> target = aGUIEvent->GetDOMEventTarget();
NS_ENSURE_TRUE(target, false);
nsCOMPtr<nsIDocument> document = do_QueryReferent(mDocWeak);
@ -5211,8 +5210,7 @@ HTMLEditor::IsAcceptableInputEvent(nsIDOMEvent* aEvent)
// If the event is a mouse event, we need to check if the target content is
// the focused editing host or its descendant.
nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aEvent);
if (mouseEvent) {
if (aGUIEvent->AsMouseEventBase()) {
nsIContent* editingHost = GetActiveEditingHost();
// If there is no active editing host, we cannot handle the mouse event
// correctly.

Просмотреть файл

@ -117,7 +117,7 @@ public:
virtual Element* GetEditorRoot() override;
virtual already_AddRefed<nsIContent> FindSelectionRoot(
nsINode *aNode) override;
virtual bool IsAcceptableInputEvent(nsIDOMEvent* aEvent) override;
virtual bool IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent) override;
virtual already_AddRefed<nsIContent> GetInputEventTargetContent() override;
virtual bool IsEditable(nsINode* aNode) override;
using EditorBase::IsEditable;

Просмотреть файл

@ -7,6 +7,7 @@
#include "HTMLEditUtils.h"
#include "mozilla/HTMLEditor.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/Selection.h"
#include "nsCOMPtr.h"
@ -52,6 +53,12 @@ HTMLEditorEventListener::GetHTMLEditor()
nsresult
HTMLEditorEventListener::MouseUp(nsIDOMMouseEvent* aMouseEvent)
{
if (DetachedFromEditor()) {
return NS_OK;
}
// FYI: We need to notify HTML editor of mouseup even if it's consumed
// because HTML editor always needs to release grabbing resizer.
HTMLEditor* htmlEditor = GetHTMLEditor();
nsCOMPtr<nsIDOMEventTarget> target;
@ -71,10 +78,17 @@ HTMLEditorEventListener::MouseUp(nsIDOMMouseEvent* aMouseEvent)
nsresult
HTMLEditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent)
{
if (NS_WARN_IF(!aMouseEvent) || DetachedFromEditor()) {
return NS_OK;
}
WidgetMouseEvent* mousedownEvent =
aMouseEvent->AsEvent()->WidgetEventPtr()->AsMouseEvent();
HTMLEditor* htmlEditor = GetHTMLEditor();
// Contenteditable should disregard mousedowns outside it.
// IsAcceptableInputEvent() checks it for a mouse event.
if (!htmlEditor->IsAcceptableInputEvent(aMouseEvent->AsEvent())) {
if (!htmlEditor->IsAcceptableInputEvent(mousedownEvent)) {
// If it's not acceptable mousedown event (including when mousedown event
// is fired outside of the active editing host), we need to commit
// composition because it will be change the selection to the clicked
@ -82,6 +96,9 @@ HTMLEditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent)
return EditorEventListener::MouseDown(aMouseEvent);
}
// XXX This method may change selection. So, we need to commit composition
// here, first.
// Detect only "context menu" click
// XXX This should be easier to do!
// But eDOMEvents_contextmenu and eContextMenu is not exposed in any event
@ -103,7 +120,7 @@ HTMLEditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent)
nsCOMPtr<nsIDOMElement> element = do_QueryInterface(target);
if (isContextClick || (buttonNumber == 0 && clickCount == 2)) {
RefPtr<Selection> selection = mEditorBase->GetSelection();
RefPtr<Selection> selection = htmlEditor->GetSelection();
NS_ENSURE_TRUE(selection, NS_OK);
// Get location of mouse within target node
@ -175,6 +192,9 @@ HTMLEditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent)
} else {
htmlEditor->SelectElement(element);
}
if (DetachedFromEditor()) {
return NS_OK;
}
}
}
// HACK !!! Context click places the caret but the context menu consumes

Просмотреть файл

@ -797,17 +797,19 @@ TextEditor::BeginIMEComposition(WidgetCompositionEvent* aEvent)
}
nsresult
TextEditor::UpdateIMEComposition(nsIDOMEvent* aDOMTextEvent)
TextEditor::UpdateIMEComposition(WidgetCompositionEvent* aCompsitionChangeEvent)
{
MOZ_ASSERT(aDOMTextEvent, "aDOMTextEvent must not be nullptr");
MOZ_ASSERT(aCompsitionChangeEvent,
"aCompsitionChangeEvent must not be nullptr");
WidgetCompositionEvent* compositionChangeEvent =
aDOMTextEvent->WidgetEventPtr()->AsCompositionEvent();
NS_ENSURE_TRUE(compositionChangeEvent, NS_ERROR_INVALID_ARG);
MOZ_ASSERT(compositionChangeEvent->mMessage == eCompositionChange,
"The internal event should be eCompositionChange");
if (NS_WARN_IF(!aCompsitionChangeEvent)) {
return NS_ERROR_INVALID_ARG;
}
if (!EnsureComposition(compositionChangeEvent)) {
MOZ_ASSERT(aCompsitionChangeEvent->mMessage == eCompositionChange,
"The event should be eCompositionChange");
if (!EnsureComposition(aCompsitionChangeEvent)) {
return NS_OK;
}
@ -828,7 +830,7 @@ TextEditor::UpdateIMEComposition(nsIDOMEvent* aDOMTextEvent)
MOZ_ASSERT(!mPlaceHolderBatch,
"UpdateIMEComposition() must be called without place holder batch");
TextComposition::CompositionChangeEventHandlingMarker
compositionChangeEventHandlingMarker(mComposition, compositionChangeEvent);
compositionChangeEventHandlingMarker(mComposition, aCompsitionChangeEvent);
NotifyEditorObservers(eNotifyEditorObserversOfBefore);
@ -838,7 +840,7 @@ TextEditor::UpdateIMEComposition(nsIDOMEvent* aDOMTextEvent)
{
AutoPlaceHolderBatch batch(this, nsGkAtoms::IMETxnName);
rv = InsertText(compositionChangeEvent->mData);
rv = InsertText(aCompsitionChangeEvent->mData);
if (caretP) {
caretP->SetSelection(selection);
@ -850,7 +852,7 @@ TextEditor::UpdateIMEComposition(nsIDOMEvent* aDOMTextEvent)
// compositionend event, we don't need to notify editor observes of this
// change.
// NOTE: We must notify after the auto batch will be gone.
if (!compositionChangeEvent->IsFollowedByCompositionEnd()) {
if (!aCompsitionChangeEvent->IsFollowedByCompositionEnd()) {
NotifyEditorObservers(eNotifyEditorObserversOfEnd);
}

Просмотреть файл

@ -133,7 +133,8 @@ public:
virtual already_AddRefed<dom::EventTarget> GetDOMEventTarget() override;
virtual nsresult BeginIMEComposition(WidgetCompositionEvent* aEvent) override;
virtual nsresult UpdateIMEComposition(nsIDOMEvent* aTextEvent) override;
virtual nsresult UpdateIMEComposition(
WidgetCompositionEvent* aCompositionChangeEvet) override;
virtual already_AddRefed<nsIContent> GetInputEventTargetContent() override;

Просмотреть файл

@ -24,7 +24,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=674770
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
SpecialPowers.pushPrefEnv({"set":[["middlemouse.paste", true], ["dom.ipc.processCount", 1]]}, startTests);
SpecialPowers.pushPrefEnv({"set":[["middlemouse.paste", true]]}, startTests);
});
function startTests() {

Просмотреть файл

@ -116,7 +116,6 @@ gfxContext::~gfxContext()
mStateStack[i].drawTarget->PopClip();
}
}
mDT->Flush();
}
void

Просмотреть файл

@ -915,6 +915,8 @@ ProcessPriorityToString(ProcessPriority aPriority)
switch (aPriority) {
case PROCESS_PRIORITY_MASTER:
return "MASTER";
case PROCESS_PRIORITY_PREALLOC:
return "PREALLOC";
case PROCESS_PRIORITY_FOREGROUND_HIGH:
return "FOREGROUND_HIGH";
case PROCESS_PRIORITY_FOREGROUND:

Просмотреть файл

@ -60,6 +60,9 @@ enum ProcessPriority {
PROCESS_PRIORITY_BACKGROUND,
PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE,
PROCESS_PRIORITY_FOREGROUND_KEYBOARD,
// The special class for the preallocated process, high memory priority but
// low CPU priority.
PROCESS_PRIORITY_PREALLOC,
// Any priority greater than or equal to FOREGROUND is considered
// "foreground" for the purposes of priority testing, for example
// CurrentProcessIsForeground().

Просмотреть файл

@ -269,6 +269,19 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoadInfo,
principalToInheritInfo = principalToInheritInfoTemp;
}
OptionalPrincipalInfo sandboxedLoadingPrincipalInfo = mozilla::void_t();
if (aLoadInfo->GetLoadingSandboxed()) {
PrincipalInfo sandboxedLoadingPrincipalInfoTemp;
nsCOMPtr<nsIPrincipal> sandboxedLoadingPrincipal;
rv = aLoadInfo->GetSandboxedLoadingPrincipal(
getter_AddRefs(sandboxedLoadingPrincipal));
NS_ENSURE_SUCCESS(rv, rv);
rv = PrincipalToPrincipalInfo(sandboxedLoadingPrincipal,
&sandboxedLoadingPrincipalInfoTemp);
NS_ENSURE_SUCCESS(rv, rv);
sandboxedLoadingPrincipalInfo = sandboxedLoadingPrincipalInfoTemp;
}
nsTArray<PrincipalInfo> redirectChainIncludingInternalRedirects;
for (const nsCOMPtr<nsIPrincipal>& principal : aLoadInfo->RedirectChainIncludingInternalRedirects()) {
rv = PrincipalToPrincipalInfo(principal, redirectChainIncludingInternalRedirects.AppendElement());
@ -286,6 +299,7 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoadInfo,
loadingPrincipalInfo,
triggeringPrincipalInfo,
principalToInheritInfo,
sandboxedLoadingPrincipalInfo,
aLoadInfo->GetSecurityFlags(),
aLoadInfo->InternalContentPolicyType(),
static_cast<uint32_t>(aLoadInfo->GetTainting()),
@ -342,6 +356,13 @@ LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsIPrincipal> sandboxedLoadingPrincipal;
if (loadInfoArgs.sandboxedLoadingPrincipalInfo().type() != OptionalPrincipalInfo::Tvoid_t) {
sandboxedLoadingPrincipal =
PrincipalInfoToPrincipal(loadInfoArgs.sandboxedLoadingPrincipalInfo(), &rv);
NS_ENSURE_SUCCESS(rv, rv);
}
nsTArray<nsCOMPtr<nsIPrincipal>> redirectChainIncludingInternalRedirects;
for (const PrincipalInfo& principalInfo : loadInfoArgs.redirectChainIncludingInternalRedirects()) {
nsCOMPtr<nsIPrincipal> redirectedPrincipal =
@ -362,6 +383,7 @@ LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
new mozilla::LoadInfo(loadingPrincipal,
triggeringPrincipal,
principalToInherit,
sandboxedLoadingPrincipal,
loadInfoArgs.securityFlags(),
loadInfoArgs.contentPolicyType(),
static_cast<LoadTainting>(loadInfoArgs.tainting()),

Просмотреть файл

@ -44,6 +44,12 @@ using mozilla::TimeDuration;
*/
JS_STATIC_ASSERT(JS::gcreason::NUM_TELEMETRY_REASONS >= JS::gcreason::NUM_REASONS);
static inline decltype(mozilla::MakeEnumeratedRange(PHASE_FIRST, PHASE_LIMIT))
AllPhases()
{
return mozilla::MakeEnumeratedRange(PHASE_FIRST, PHASE_LIMIT);
}
const char*
js::gcstats::ExplainInvocationKind(JSGCInvocationKind gckind)
{
@ -114,6 +120,8 @@ struct ExtraPhaseInfo
// Index into the set of parallel arrays of timing data, for parents with
// at least one multi-parented child
size_t dagSlot;
ExtraPhaseInfo() : depth(0), dagSlot(0) {}
};
static const Phase PHASE_NO_PARENT = PHASE_LIMIT;
@ -210,7 +218,7 @@ static const PhaseInfo phases[] = {
// numbers.
};
static ExtraPhaseInfo phaseExtra[PHASE_LIMIT] = { { 0, 0 } };
static mozilla::EnumeratedArray<Phase, PHASE_LIMIT, ExtraPhaseInfo> phaseExtra;
// Mapping from all nodes with a multi-parented child to a Vector of all
// multi-parented children and their descendants. (Single-parented children will
@ -237,7 +245,7 @@ struct AllPhaseIterator {
// subtree nodes.
mozilla::Vector<Phase, 0, SystemAllocPolicy>::Range descendants;
explicit AllPhaseIterator(const Statistics::PhaseTimeTable table)
explicit AllPhaseIterator()
: current(0)
, baseLevel(0)
, activeSlot(PHASE_DAG_NONE)
@ -271,13 +279,14 @@ struct AllPhaseIterator {
return;
}
if (phaseExtra[current].dagSlot != PHASE_DAG_NONE) {
auto phase = Phase(current);
if (phaseExtra[phase].dagSlot != PHASE_DAG_NONE) {
// The current phase has a shared subtree. Load them up into
// 'descendants' and advance to the first child.
activeSlot = phaseExtra[current].dagSlot;
activeSlot = phaseExtra[phase].dagSlot;
descendants = dagDescendants[activeSlot].all();
MOZ_ASSERT(!descendants.empty());
baseLevel += phaseExtra[current].depth + 1;
baseLevel += phaseExtra[phase].depth + 1;
return;
}
@ -343,18 +352,19 @@ Join(const FragmentVector& fragments, const char* separator = "") {
}
static TimeDuration
SumChildTimes(size_t phaseSlot, Phase phase, const Statistics::PhaseTimeTable phaseTimes)
SumChildTimes(size_t phaseSlot, Phase phase, const Statistics::PhaseTimeTable& phaseTimes)
{
// Sum the contributions from single-parented children.
TimeDuration total = 0;
size_t depth = phaseExtra[phase].depth;
for (unsigned i = phase + 1; i < PHASE_LIMIT && phaseExtra[i].depth > depth; i++) {
for (unsigned i = phase + 1; i < PHASE_LIMIT && phaseExtra[Phase(i)].depth > depth; i++) {
if (phases[i].parent == phase)
total += phaseTimes[phaseSlot][i];
total += phaseTimes[phaseSlot][Phase(i)];
}
// Sum the contributions from multi-parented children.
size_t dagSlot = phaseExtra[phase].dagSlot;
MOZ_ASSERT(dagSlot <= Statistics::MaxMultiparentPhases - 1);
if (dagSlot != PHASE_DAG_NONE) {
for (auto edge : dagChildEdges) {
if (edge.parent == phase)
@ -448,13 +458,13 @@ Statistics::formatCompactSummaryMessage() const
}
UniqueChars
Statistics::formatCompactSlicePhaseTimes(const PhaseTimeTable phaseTimes) const
Statistics::formatCompactSlicePhaseTimes(const PhaseTimeTable& phaseTimes) const
{
static const TimeDuration MaxUnaccountedTime = TimeDuration::FromMicroseconds(100);
FragmentVector fragments;
char buffer[128];
for (AllPhaseIterator iter(phaseTimes); !iter.done(); iter.advance()) {
for (AllPhaseIterator iter; !iter.done(); iter.advance()) {
Phase phase;
size_t dagSlot;
size_t level;
@ -575,14 +585,14 @@ Statistics::formatDetailedSliceDescription(unsigned i, const SliceData& slice)
}
UniqueChars
Statistics::formatDetailedPhaseTimes(const PhaseTimeTable phaseTimes)
Statistics::formatDetailedPhaseTimes(const PhaseTimeTable& phaseTimes)
{
static const char* LevelToIndent[] = { "", " ", " ", " " };
static const TimeDuration MaxUnaccountedChildTime = TimeDuration::FromMicroseconds(50);
FragmentVector fragments;
char buffer[128];
for (AllPhaseIterator iter(phaseTimes); !iter.done(); iter.advance()) {
for (AllPhaseIterator iter; !iter.done(); iter.advance()) {
Phase phase;
size_t dagSlot;
size_t level;
@ -781,11 +791,11 @@ FilterJsonKey(const char*const buffer)
}
UniqueChars
Statistics::formatJsonPhaseTimes(const PhaseTimeTable phaseTimes)
Statistics::formatJsonPhaseTimes(const PhaseTimeTable& phaseTimes)
{
FragmentVector fragments;
char buffer[128];
for (AllPhaseIterator iter(phaseTimes); !iter.done(); iter.advance()) {
for (AllPhaseIterator iter; !iter.done(); iter.advance()) {
Phase phase;
size_t dagSlot;
iter.get(&phase, &dagSlot);
@ -819,11 +829,8 @@ Statistics::Statistics(JSRuntime* rt)
enableProfiling_(false),
sliceCount_(0)
{
PodArrayZero(phaseTotals);
PodArrayZero(counts);
PodArrayZero(phaseStartTimes);
for (auto& phaseTime : phaseTimes)
PodArrayZero(phaseTime);
for (auto& count : counts)
count = 0;
const char* env = getenv("MOZ_GCTIMER");
if (env) {
@ -863,11 +870,13 @@ Statistics::~Statistics()
/* static */ bool
Statistics::initialize()
{
for (size_t i = 0; i < PHASE_LIMIT; i++) {
#ifdef DEBUG
for (auto i : AllPhases()) {
MOZ_ASSERT(phases[i].index == i);
for (size_t j = 0; j < PHASE_LIMIT; j++)
for (auto j : AllPhases())
MOZ_ASSERT_IF(i != j, phases[i].telemetryBucket != phases[j].telemetryBucket);
}
#endif
// Create a static table of descendants for every phase with multiple
// children. This assumes that all descendants come linearly in the
@ -896,7 +905,7 @@ Statistics::initialize()
mozilla::Vector<Phase, 0, SystemAllocPolicy> stack;
if (!stack.append(PHASE_LIMIT)) // Dummy entry to avoid special-casing the first node
return false;
for (int i = 0; i < PHASE_LIMIT; i++) {
for (auto i : AllPhases()) {
if (phases[i].parent == PHASE_NO_PARENT ||
phases[i].parent == PHASE_MULTI_PARENTS)
{
@ -906,7 +915,7 @@ Statistics::initialize()
stack.popBack();
}
phaseExtra[i].depth = stack.length();
if (!stack.append(Phase(i)))
if (!stack.append(i))
return false;
}
@ -974,11 +983,11 @@ LongestPhaseSelfTime(const Statistics::PhaseTimeTable& times)
TimeDuration selfTimes[PHASE_LIMIT];
// Start with total times, including children's times.
for (size_t i = 0; i < PHASE_LIMIT; ++i)
selfTimes[i] = SumPhase(Phase(i), times);
for (auto i : AllPhases())
selfTimes[i] = SumPhase(i, times);
// Subtract out the children's times.
for (size_t i = 0; i < PHASE_LIMIT; ++i) {
for (auto i : AllPhases()) {
Phase parent = phases[i].parent;
if (parent == PHASE_MULTI_PARENTS) {
// Subtract out only the time for the children specific to this
@ -986,6 +995,7 @@ LongestPhaseSelfTime(const Statistics::PhaseTimeTable& times)
for (auto edge : dagChildEdges) {
if (edge.parent == parent) {
size_t dagSlot = phaseExtra[edge.parent].dagSlot;
MOZ_ASSERT(dagSlot <= Statistics::MaxMultiparentPhases - 1);
CheckSelfTime(parent, edge.child, times, selfTimes, times[dagSlot][edge.child]);
MOZ_ASSERT(selfTimes[parent] >= times[dagSlot][edge.child]);
selfTimes[parent] -= times[dagSlot][edge.child];
@ -993,17 +1003,17 @@ LongestPhaseSelfTime(const Statistics::PhaseTimeTable& times)
}
} else if (parent != PHASE_NO_PARENT) {
MOZ_ASSERT(selfTimes[parent] >= selfTimes[i]);
CheckSelfTime(parent, Phase(i), times, selfTimes, selfTimes[i]);
CheckSelfTime(parent, i, times, selfTimes, selfTimes[i]);
selfTimes[parent] -= selfTimes[i];
}
}
TimeDuration longestTime = 0;
Phase longestPhase = PHASE_NONE;
for (size_t i = 0; i < PHASE_LIMIT; ++i) {
for (auto i : AllPhases()) {
if (selfTimes[i] > longestTime) {
longestTime = selfTimes[i];
longestPhase = Phase(i);
longestPhase = i;
}
}
@ -1042,7 +1052,7 @@ void
Statistics::endGC()
{
for (auto j : IntegerRange(NumTimingArrays)) {
for (int i = 0; i < PHASE_LIMIT; i++)
for (auto i : AllPhases())
phaseTotals[j][i] += phaseTimes[j][i];
}
@ -1116,8 +1126,12 @@ Statistics::beginSlice(const ZoneGCStats& zoneStats, JSGCInvocationKind gckind,
if (first)
beginGC(gckind);
SliceData data(budget, reason, TimeStamp::Now(), GetPageFaultCount(), runtime->gc.state());
if (!slices.append(data)) {
if (!slices.emplaceBack(budget,
reason,
TimeStamp::Now(),
GetPageFaultCount(),
runtime->gc.state()))
{
// If we are OOM, set a flag to indicate we have missing slice data.
aborted = true;
return;
@ -1181,7 +1195,8 @@ Statistics::endSlice()
// Do this after the slice callback since it uses these values.
if (last) {
PodArrayZero(counts);
for (auto& count : counts)
count = 0;
// Clear the timers at the end of a GC because we accumulate time in
// between GCs for some (which come before PHASE_GC_BEGIN in the list.)
@ -1279,8 +1294,11 @@ Statistics::beginPhase(Phase phase)
phaseNesting[phaseNestingDepth] = phase;
phaseNestingDepth++;
if (phases[phase].parent == PHASE_MULTI_PARENTS)
if (phases[phase].parent == PHASE_MULTI_PARENTS) {
MOZ_ASSERT(parent != PHASE_NO_PARENT);
activeDagSlot = phaseExtra[parent].dagSlot;
}
MOZ_ASSERT(activeDagSlot <= MaxMultiparentPhases - 1);
phaseStartTimes[phase] = TimeStamp::Now();
}

Просмотреть файл

@ -7,6 +7,7 @@
#ifndef gc_Statistics_h
#define gc_Statistics_h
#include "mozilla/Array.h"
#include "mozilla/EnumeratedArray.h"
#include "mozilla/IntegerRange.h"
#include "mozilla/Maybe.h"
@ -28,7 +29,9 @@ class GCParallelTask;
namespace gcstats {
enum Phase : uint8_t {
PHASE_MUTATOR,
PHASE_FIRST,
PHASE_MUTATOR = PHASE_FIRST,
PHASE_GC_BEGIN,
PHASE_WAIT_BACKGROUND_THREAD,
PHASE_MARK_DISCARD_CODE,
@ -170,6 +173,12 @@ const char* ExplainInvocationKind(JSGCInvocationKind gckind);
*/
struct Statistics
{
template <typename T, size_t Length>
using Array = mozilla::Array<T, Length>;
template <typename IndexType, IndexType SizeAsEnumValue, typename ValueType>
using EnumeratedArray = mozilla::EnumeratedArray<IndexType, SizeAsEnumValue, ValueType>;
using TimeDuration = mozilla::TimeDuration;
using TimeStamp = mozilla::TimeStamp;
@ -191,13 +200,17 @@ struct Statistics
static const size_t NumTimingArrays = MaxMultiparentPhases + 1;
/* Create a convenient type for referring to tables of phase times. */
using PhaseTimeTable = TimeDuration[NumTimingArrays][PHASE_LIMIT];
using PhaseTimeTable =
Array<EnumeratedArray<Phase, PHASE_LIMIT, TimeDuration>, NumTimingArrays>;
static MOZ_MUST_USE bool initialize();
explicit Statistics(JSRuntime* rt);
~Statistics();
Statistics(const Statistics&) = delete;
Statistics& operator=(const Statistics&) = delete;
void beginPhase(Phase phase);
void endPhase(Phase phase);
void endParallelPhase(Phase phase, const GCParallelTask* task);
@ -290,10 +303,7 @@ struct Statistics
resetReason(gc::AbortReason::None),
start(start),
startFaults(startFaults)
{
for (auto i : mozilla::IntegerRange(NumTimingArrays))
mozilla::PodArrayZero(phaseTimes[i]);
}
{}
SliceBudget budget;
JS::gcreason::Reason reason;
@ -337,7 +347,7 @@ struct Statistics
SliceDataVector slices;
/* Most recent time when the given phase started. */
TimeStamp phaseStartTimes[PHASE_LIMIT];
EnumeratedArray<Phase, PHASE_LIMIT, TimeStamp> phaseStartTimes;
/* Bookkeeping for GC timings when timingMutator is true */
TimeStamp timedGCStart;
@ -350,7 +360,7 @@ struct Statistics
PhaseTimeTable phaseTotals;
/* Number of events of this type for this GC. */
unsigned int counts[STAT_LIMIT];
EnumeratedArray<Stat, STAT_LIMIT, unsigned int> counts;
/* Allocated space before the GC started. */
size_t preBytes;
@ -359,7 +369,7 @@ struct Statistics
mutable TimeDuration maxPauseInInterval;
/* Phases that are currently on stack. */
Phase phaseNesting[MAX_NESTING];
Array<Phase, MAX_NESTING> phaseNesting;
size_t phaseNestingDepth;
size_t activeDagSlot;
@ -370,7 +380,7 @@ struct Statistics
* suspensions by suspending multiple stacks with a PHASE_SUSPENSION in
* between).
*/
Phase suspendedPhases[MAX_NESTING * 3];
Array<Phase, MAX_NESTING * 3> suspendedPhases;
size_t suspended;
/* Sweep times for SCCs of compartments. */
@ -397,8 +407,7 @@ FOR_EACH_GC_PROFILE_TIME(DEFINE_TIME_KEY)
KeyCount
};
using ProfileDurations =
mozilla::EnumeratedArray<ProfileKey, ProfileKey::KeyCount, TimeDuration>;
using ProfileDurations = EnumeratedArray<ProfileKey, ProfileKey::KeyCount, TimeDuration>;
TimeDuration profileThreshold_;
bool enableProfiling_;
@ -414,16 +423,16 @@ FOR_EACH_GC_PROFILE_TIME(DEFINE_TIME_KEY)
void sccDurations(TimeDuration* total, TimeDuration* maxPause);
void printStats();
UniqueChars formatCompactSlicePhaseTimes(const PhaseTimeTable phaseTimes) const;
UniqueChars formatCompactSlicePhaseTimes(const PhaseTimeTable& phaseTimes) const;
UniqueChars formatDetailedDescription();
UniqueChars formatDetailedSliceDescription(unsigned i, const SliceData& slice);
UniqueChars formatDetailedPhaseTimes(const PhaseTimeTable phaseTimes);
UniqueChars formatDetailedPhaseTimes(const PhaseTimeTable& phaseTimes);
UniqueChars formatDetailedTotals();
UniqueChars formatJsonDescription(uint64_t timestamp);
UniqueChars formatJsonSliceDescription(unsigned i, const SliceData& slice);
UniqueChars formatJsonPhaseTimes(const PhaseTimeTable phaseTimes);
UniqueChars formatJsonPhaseTimes(const PhaseTimeTable& phaseTimes);
double computeMMU(TimeDuration resolution) const;

Просмотреть файл

@ -0,0 +1,11 @@
if (!('oomTest' in this))
quit();
let g = newGlobal();
let dbg = new Debugger;
let gw = dbg.addDebuggee(g);
g.eval("function f(){}");
oomTest(() => {
gw.makeDebuggeeValue(g.f).script.source.sourceMapURL = 'a';
});

Просмотреть файл

@ -0,0 +1,6 @@
// |jit-test| exitstatus: 6;
for (var x of [0]) {
timeout(0.001);
for (;;) {}
}

Просмотреть файл

@ -863,35 +863,32 @@ IonBuilder::build()
AbortReasonOr<Ok>
IonBuilder::processIterators()
{
// Find phis that must directly hold an iterator live.
Vector<MPhi*, 0, SystemAllocPolicy> worklist;
// Find and mark phis that must transitively hold an iterator live.
Vector<MDefinition*, 8, SystemAllocPolicy> worklist;
for (size_t i = 0; i < iterators_.length(); i++) {
MDefinition* def = iterators_[i];
if (def->isPhi()) {
if (!worklist.append(def->toPhi()))
return abort(AbortReason::Alloc);
} else {
for (MUseDefIterator iter(def); iter; iter++) {
if (iter.def()->isPhi()) {
if (!worklist.append(iter.def()->toPhi()))
return abort(AbortReason::Alloc);
}
}
}
if (!worklist.append(iterators_[i]))
return abort(AbortReason::Alloc);
iterators_[i]->setInWorklist();
}
// Propagate the iterator and live status of phis to all other connected
// phis.
while (!worklist.empty()) {
MPhi* phi = worklist.popCopy();
phi->setIterator();
phi->setImplicitlyUsedUnchecked();
MDefinition* def = worklist.popCopy();
def->setNotInWorklist();
for (MUseDefIterator iter(phi); iter; iter++) {
if (iter.def()->isPhi()) {
MPhi* other = iter.def()->toPhi();
if (!other->isIterator() && !worklist.append(other))
if (def->isPhi()) {
MPhi* phi = def->toPhi();
phi->setIterator();
phi->setImplicitlyUsedUnchecked();
}
for (MUseDefIterator iter(def); iter; iter++) {
MDefinition* use = iter.def();
if (!use->isInWorklist() && (!use->isPhi() || !use->toPhi()->isIterator())) {
if (!worklist.append(use))
return abort(AbortReason::Alloc);
use->setInWorklist();
}
}
}

Просмотреть файл

@ -525,7 +525,9 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
* ScriptSourceObject, and those that are (element; elementAttributeName)
* aren't preserved by XDR. So this can be simple.
*/
ss->initFromOptions(cx, *options);
if (!ss->initFromOptions(cx, *options))
return false;
sourceObject = ScriptSourceObject::create(cx, ss);
if (xdr->hasScriptSourceObjectOut()) {
// When the ScriptSourceObjectOut is provided by ParseTask, it

Просмотреть файл

@ -474,11 +474,12 @@ class ScriptSource
if (--refs == 0)
js_delete(this);
}
bool initFromOptions(ExclusiveContext* cx, const ReadOnlyCompileOptions& options,
mozilla::Maybe<uint32_t> parameterListEnd = mozilla::Nothing());
bool setSourceCopy(ExclusiveContext* cx,
JS::SourceBufferHolder& srcBuf,
SourceCompressionTask* tok);
MOZ_MUST_USE bool initFromOptions(ExclusiveContext* cx,
const ReadOnlyCompileOptions& options,
mozilla::Maybe<uint32_t> parameterListEnd = mozilla::Nothing());
MOZ_MUST_USE bool setSourceCopy(ExclusiveContext* cx,
JS::SourceBufferHolder& srcBuf,
SourceCompressionTask* tok);
void setSourceRetrievable() { sourceRetrievable_ = true; }
bool sourceRetrievable() const { return sourceRetrievable_; }
bool hasSourceData() const { return !data.is<Missing>(); }
@ -522,22 +523,21 @@ class ScriptSource
JS::ScriptSourceInfo* info) const;
MOZ_MUST_USE bool setSource(ExclusiveContext* cx,
mozilla::UniquePtr<char16_t[], JS::FreePolicy>&& source,
UniqueTwoByteChars&& source,
size_t length);
void setSource(SharedImmutableTwoByteString&& string);
MOZ_MUST_USE bool setCompressedSource(
ExclusiveContext* cx,
mozilla::UniquePtr<char[], JS::FreePolicy>&& raw,
size_t rawLength,
size_t sourceLength);
MOZ_MUST_USE bool setCompressedSource(ExclusiveContext* cx,
UniqueChars&& raw,
size_t rawLength,
size_t sourceLength);
void setCompressedSource(SharedImmutableString&& raw, size_t sourceLength);
// XDR handling
template <XDRMode mode>
bool performXDR(XDRState<mode>* xdr);
MOZ_MUST_USE bool performXDR(XDRState<mode>* xdr);
bool setFilename(ExclusiveContext* cx, const char* filename);
MOZ_MUST_USE bool setFilename(ExclusiveContext* cx, const char* filename);
const char* introducerFilename() const {
return introducerFilename_ ? introducerFilename_.get() : filename_.get();
}
@ -553,7 +553,7 @@ class ScriptSource
}
// Display URLs
bool setDisplayURL(ExclusiveContext* cx, const char16_t* displayURL);
MOZ_MUST_USE bool setDisplayURL(ExclusiveContext* cx, const char16_t* displayURL);
bool hasDisplayURL() const { return displayURL_ != nullptr; }
const char16_t * displayURL() {
MOZ_ASSERT(hasDisplayURL());
@ -561,7 +561,7 @@ class ScriptSource
}
// Source maps
bool setSourceMapURL(ExclusiveContext* cx, const char16_t* sourceMapURL);
MOZ_MUST_USE bool setSourceMapURL(ExclusiveContext* cx, const char16_t* sourceMapURL);
bool hasSourceMapURL() const { return sourceMapURL_ != nullptr; }
const char16_t * sourceMapURL() {
MOZ_ASSERT(hasSourceMapURL());

Просмотреть файл

@ -7490,7 +7490,9 @@ DebuggerSource_setSourceMapURL(JSContext* cx, unsigned argc, Value* vp)
if (!stableChars.initTwoByte(cx, str))
return false;
ss->setSourceMapURL(cx, stableChars.twoByteChars());
if (!ss->setSourceMapURL(cx, stableChars.twoByteChars()))
return false;
args.rval().setUndefined();
return true;
}

Просмотреть файл

@ -93,7 +93,7 @@ fails == 413928-2.html 413928-2.html
== 425338-1b.html 425338-1b.html
== 489517-1.html 489517-1.html
== 489887-1.html 489887-1.html
== 492231-1.html 492231-1.html
# == 492231-1.html 492231-1.html
== 496006-1.html 496006-1.html
fails == 503269-1.html 503269-1.html
== 503957-1.html 503957-1.html
@ -153,7 +153,7 @@ fuzzy-if(xulRuntime.widgetToolkit=="gtk3",1,11) == 869833-1.xul 869833-1.xul
== 1155359-1.xul 1155359-1.xul
== 1157726-1.html 1157726-1.html
== 1161752.html 1161752.html
fails == 1161752-5-embed.html 1161752-5-embed.html
== 1161752-5-embed.html 1161752-5-embed.html
# == brackets-1a-ltr.html brackets-1a-ltr.html
# == brackets-1a-rtl.html brackets-1a-rtl.html
# == brackets-1b-ltr.html brackets-1b-ltr.html

Просмотреть файл

@ -68,8 +68,8 @@ fuzzy-if(true,1,33) fuzzy-if(d2d,48,350) fuzzy-if(cocoaWidget,1,332) fuzzy-if(An
# == table-collapse-1.html table-collapse-1.html
# when border-collapse: collapse
fuzzy-if(azureQuartz,1,3) fuzzy-if(skiaContent,1,116) == invalidate-1a.html invalidate-1a.html
fuzzy-if(azureQuartz,1,3) fuzzy-if(skiaContent,1,117) == invalidate-1b.html invalidate-1b.html
fuzzy-if(skiaContent,1,116) == invalidate-1a.html invalidate-1a.html
fuzzy-if(skiaContent,1,117) == invalidate-1b.html invalidate-1b.html
# test that border-radius is reduced for scrollbars
fails == scrollbar-clamping-1.html scrollbar-clamping-1.html

Просмотреть файл

@ -1014,7 +1014,7 @@ fails == 420790-1.xhtml 420790-1.xhtml
== 421419-1.html 421419-1.html
== 421436-1a.html 421436-1a.html
== 421436-1b.html 421436-1b.html
fails asserts-if(stylo,4) == 421632-1.html 421632-1.html # bug 1335314
fails asserts-if(stylo,0-4) == 421632-1.html 421632-1.html # bug 1335314
fails == 421710-1.html 421710-1.html
fails-if(Android) fails-if(usesRepeatResampling) == 421885-1.xml 421885-1.xml
== 421955-1.html 421955-1.html

Просмотреть файл

@ -67,10 +67,10 @@ random-if(cocoaWidget&&azureSkia) random-if(!cocoaWidget||OSX==1006||OSX==1007)
# azure quartz uses CGDrawLinearGradient instead of DrawShading
# so we have less control over degenerate behaviour as tested by this
# test
# fails-if((azureSkia&&!azureSkiaGL)||azureQuartz||(azureSkiaGL&&Android)) == linear-gradient-1a.html linear-gradient-1a.html
# fails-if((azureSkia&&!azureSkiaGL)||(azureSkiaGL&&Android)) == linear-gradient-1a.html linear-gradient-1a.html
# this passes with cairo on 10.7 and 10.8 but not with azure for reasons unknown
# fails-if((azureSkia&&!azureSkiaGL)||azureQuartz||(azureSkiaGL&&Android)) == linear-gradient-1b.html linear-gradient-1b.html
# fails-if((azureSkia&&!azureSkiaGL)||(azureSkiaGL&&Android)) == linear-gradient-1b.html linear-gradient-1b.html
== zero-dimensions.html zero-dimensions.html
@ -84,7 +84,7 @@ random-if(cocoaWidget&&azureSkia) random-if(!cocoaWidget||OSX==1006||OSX==1007)
== ctm-singular-sanity.html ctm-singular-sanity.html
== ctm-1.html ctm-1.html
fails-if(azureQuartz&&OSX==1006) == 672646-alpha-radial-gradient.html 672646-alpha-radial-gradient.html
== 672646-alpha-radial-gradient.html 672646-alpha-radial-gradient.html
fails == 674003-alpha-radial-gradient-superlum.html 674003-alpha-radial-gradient-superlum.html
fails asserts-if(stylo,1) == 693610-1.html 693610-1.html # bug 1324700

Просмотреть файл

@ -31,6 +31,6 @@ fails == name-case-sensitivity.html name-case-sensitivity.html
fails == dependent-builtin.html dependent-builtin.html
fails == redefine-builtin.html redefine-builtin.html
fails == redefine-attr-mapping.html redefine-attr-mapping.html
fails == disclosure-styles.html disclosure-styles.html
fails asserts-if(stylo,2) == disclosure-styles.html disclosure-styles.html # bug 1324704
fails == symbols-function.html symbols-function.html
fails == symbols-function-invalid.html symbols-function-invalid.html

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше