зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to autoland, a=merge
MozReview-Commit-ID: HVH8TbXbw7N
This commit is contained in:
Коммит
f5eab47fb9
|
@ -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 =
|
||||
|
|
|
@ -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
|
|
@ -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(¤tFlags);
|
||||
editorBase->GetFlags(¤tFlags);
|
||||
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
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче