зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to m-c a=merge
This commit is contained in:
Коммит
2cf14d30b8
|
@ -599,6 +599,33 @@ AST_MATCHER(QualType, isRefCounted) {
|
|||
return isClassRefCounted(Node);
|
||||
}
|
||||
|
||||
#if CLANG_VERSION_FULL < 304
|
||||
|
||||
/// The 'equalsBoundeNode' matcher was added in clang 3.4.
|
||||
/// Since infra runs clang 3.3, we polyfill it here.
|
||||
AST_POLYMORPHIC_MATCHER_P(equalsBoundNode,
|
||||
std::string, ID) {
|
||||
BoundNodesTree bindings = Builder->build();
|
||||
bool haveMatchingResult = false;
|
||||
struct Visitor : public BoundNodesTree::Visitor {
|
||||
const NodeType &Node;
|
||||
std::string ID;
|
||||
bool &haveMatchingResult;
|
||||
Visitor(const NodeType &Node, const std::string &ID, bool &haveMatchingResult)
|
||||
: Node(Node), ID(ID), haveMatchingResult(haveMatchingResult) {}
|
||||
void visitMatch(const BoundNodes &BoundNodesView) override {
|
||||
if (BoundNodesView.getNodeAs<NodeType>(ID) == &Node) {
|
||||
haveMatchingResult = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
Visitor visitor(Node, ID, haveMatchingResult);
|
||||
bindings.visitMatches(&visitor);
|
||||
return haveMatchingResult;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -704,8 +731,14 @@ DiagnosticsMatcher::DiagnosticsMatcher()
|
|||
).bind("node"),
|
||||
&noAddRefReleaseOnReturnChecker);
|
||||
|
||||
// Match declrefs with type "pointer to object of ref-counted type" inside a
|
||||
// lambda, where the declaration they reference is not inside the lambda.
|
||||
// This excludes arguments and local variables, leaving only captured
|
||||
// variables.
|
||||
astMatcher.addMatcher(lambdaExpr(
|
||||
hasDescendant(declRefExpr(hasType(pointerType(pointee(isRefCounted())))).bind("node"))
|
||||
hasDescendant(declRefExpr(hasType(pointerType(pointee(isRefCounted()))),
|
||||
to(decl().bind("decl"))).bind("declref")),
|
||||
unless(hasDescendant(decl(equalsBoundNode("decl"))))
|
||||
),
|
||||
&refCountedInsideLambdaChecker);
|
||||
|
||||
|
@ -917,14 +950,14 @@ void DiagnosticsMatcher::RefCountedInsideLambdaChecker::run(
|
|||
const MatchFinder::MatchResult &Result) {
|
||||
DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
|
||||
unsigned errorID = Diag.getDiagnosticIDs()->getCustomDiagID(
|
||||
DiagnosticIDs::Error, "Refcounted variable %0 of type %1 cannot be used inside a lambda");
|
||||
DiagnosticIDs::Error, "Refcounted variable %0 of type %1 cannot be captured by a lambda");
|
||||
unsigned noteID = Diag.getDiagnosticIDs()->getCustomDiagID(
|
||||
DiagnosticIDs::Note, "Please consider using a smart pointer");
|
||||
const DeclRefExpr *node = Result.Nodes.getNodeAs<DeclRefExpr>("node");
|
||||
const DeclRefExpr *declref = Result.Nodes.getNodeAs<DeclRefExpr>("declref");
|
||||
|
||||
Diag.Report(node->getLocStart(), errorID) << node->getFoundDecl() <<
|
||||
node->getType()->getPointeeType();
|
||||
Diag.Report(node->getLocStart(), noteID);
|
||||
Diag.Report(declref->getLocStart(), errorID) << declref->getFoundDecl() <<
|
||||
declref->getType()->getPointeeType();
|
||||
Diag.Report(declref->getLocStart(), noteID);
|
||||
}
|
||||
|
||||
void DiagnosticsMatcher::ExplicitOperatorBoolChecker::run(
|
||||
|
|
|
@ -19,40 +19,76 @@ void take(...);
|
|||
void foo() {
|
||||
R* ptr;
|
||||
SmartPtr<R> sp;
|
||||
take([&]() {
|
||||
ptr->method(); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be used inside a lambda}} expected-note{{Please consider using a smart pointer}}
|
||||
take([&](R* argptr) {
|
||||
R* localptr;
|
||||
ptr->method(); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}}
|
||||
argptr->method();
|
||||
localptr->method();
|
||||
});
|
||||
take([&]() {
|
||||
take([&](SmartPtr<R> argsp) {
|
||||
SmartPtr<R> localsp;
|
||||
sp->method();
|
||||
argsp->method();
|
||||
localsp->method();
|
||||
});
|
||||
take([&]() {
|
||||
take(ptr); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be used inside a lambda}} expected-note{{Please consider using a smart pointer}}
|
||||
take([&](R* argptr) {
|
||||
R* localptr;
|
||||
take(ptr); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}}
|
||||
take(argptr);
|
||||
take(localptr);
|
||||
});
|
||||
take([&]() {
|
||||
take([&](SmartPtr<R> argsp) {
|
||||
SmartPtr<R> localsp;
|
||||
take(sp);
|
||||
take(argsp);
|
||||
take(localsp);
|
||||
});
|
||||
take([=]() {
|
||||
ptr->method(); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be used inside a lambda}} expected-note{{Please consider using a smart pointer}}
|
||||
take([=](R* argptr) {
|
||||
R* localptr;
|
||||
ptr->method(); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}}
|
||||
argptr->method();
|
||||
localptr->method();
|
||||
});
|
||||
take([=]() {
|
||||
take([=](SmartPtr<R> argsp) {
|
||||
SmartPtr<R> localsp;
|
||||
sp->method();
|
||||
argsp->method();
|
||||
localsp->method();
|
||||
});
|
||||
take([=]() {
|
||||
take(ptr); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be used inside a lambda}} expected-note{{Please consider using a smart pointer}}
|
||||
take([=](R* argptr) {
|
||||
R* localptr;
|
||||
take(ptr); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}}
|
||||
take(argptr);
|
||||
take(localptr);
|
||||
});
|
||||
take([=]() {
|
||||
take([=](SmartPtr<R> argsp) {
|
||||
SmartPtr<R> localsp;
|
||||
take(sp);
|
||||
take(argsp);
|
||||
take(localsp);
|
||||
});
|
||||
take([ptr]() {
|
||||
ptr->method(); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be used inside a lambda}} expected-note{{Please consider using a smart pointer}}
|
||||
take([ptr](R* argptr) {
|
||||
R* localptr;
|
||||
ptr->method(); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}}
|
||||
argptr->method();
|
||||
localptr->method();
|
||||
});
|
||||
take([sp]() {
|
||||
take([sp](SmartPtr<R> argsp) {
|
||||
SmartPtr<R> localsp;
|
||||
sp->method();
|
||||
argsp->method();
|
||||
localsp->method();
|
||||
});
|
||||
take([ptr]() {
|
||||
take(ptr); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be used inside a lambda}} expected-note{{Please consider using a smart pointer}}
|
||||
take([ptr](R* argptr) {
|
||||
R* localptr;
|
||||
take(ptr); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}}
|
||||
take(argptr);
|
||||
take(localptr);
|
||||
});
|
||||
take([sp]() {
|
||||
take([sp](SmartPtr<R> argsp) {
|
||||
SmartPtr<R> localsp;
|
||||
take(sp);
|
||||
take(argsp);
|
||||
take(localsp);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -460,17 +460,20 @@ IsOnFullDomainWhitelist(nsIURI* aURI)
|
|||
NS_LITERAL_CSTRING("m.video.baidu.com"),
|
||||
NS_LITERAL_CSTRING("m.video.baidu.com"),
|
||||
NS_LITERAL_CSTRING("imgcache.gtimg.cn"), // for m.v.qq.com
|
||||
NS_LITERAL_CSTRING("s.tabelog.jp"),
|
||||
NS_LITERAL_CSTRING("s.yimg.jp"), // for s.tabelog.jp
|
||||
NS_LITERAL_CSTRING("i.yimg.jp"), // for *.yahoo.co.jp
|
||||
NS_LITERAL_CSTRING("ai.yimg.jp"), // for *.yahoo.co.jp
|
||||
NS_LITERAL_CSTRING("m.finance.yahoo.co.jp"),
|
||||
NS_LITERAL_CSTRING("daily.c.yimg.jp"), // for sp.daily.co.jp
|
||||
NS_LITERAL_CSTRING("stat100.ameba.jp"), // for ameblo.jp
|
||||
NS_LITERAL_CSTRING("user.ameba.jp"), // for ameblo.jp
|
||||
NS_LITERAL_CSTRING("www.goo.ne.jp"),
|
||||
NS_LITERAL_CSTRING("s.tabelog.jp"),
|
||||
NS_LITERAL_CSTRING("x.gnst.jp"), // for mobile.gnavi.co.jp
|
||||
NS_LITERAL_CSTRING("c.x.gnst.jp"), // for mobile.gnavi.co.jp
|
||||
NS_LITERAL_CSTRING("www.smbc-card.com"),
|
||||
NS_LITERAL_CSTRING("static.card.jp.rakuten-static.com"), // for rakuten-card.co.jp
|
||||
NS_LITERAL_CSTRING("img.travel.rakuten.co.jp"), // for travel.rakuten.co.jp
|
||||
NS_LITERAL_CSTRING("img.mixi.net"), // for mixi.jp
|
||||
NS_LITERAL_CSTRING("girlschannel.net"),
|
||||
NS_LITERAL_CSTRING("www.fancl.co.jp"),
|
||||
|
@ -488,13 +491,14 @@ IsOnFullDomainWhitelist(nsIURI* aURI)
|
|||
NS_LITERAL_CSTRING("www.tokyo-sports.co.jp"),
|
||||
NS_LITERAL_CSTRING("www.bellemaison.jp"),
|
||||
NS_LITERAL_CSTRING("www.kuronekoyamato.co.jp"),
|
||||
NS_LITERAL_CSTRING("s.tsite.jp"),
|
||||
NS_LITERAL_CSTRING("formassist.jp"), // for orico.jp
|
||||
NS_LITERAL_CSTRING("sp.m.reuters.co.jp"),
|
||||
NS_LITERAL_CSTRING("www.atre.co.jp"),
|
||||
NS_LITERAL_CSTRING("www.jtb.co.jp"),
|
||||
NS_LITERAL_CSTRING("www.sharp.co.jp"),
|
||||
NS_LITERAL_CSTRING("www.biccamera.com"),
|
||||
NS_LITERAL_CSTRING("weathernews.jp"),
|
||||
NS_LITERAL_CSTRING("cache.ymail.jp"), // for www.yamada-denkiweb.com
|
||||
};
|
||||
static const size_t sNumFullDomainsOnWhitelist =
|
||||
MOZ_ARRAY_LENGTH(sFullDomainsOnWhitelist);
|
||||
|
@ -524,6 +528,7 @@ IsOnBaseDomainWhitelist(nsIURI* aURI)
|
|||
NS_LITERAL_CSTRING("dpfile.com"), // for m.dianping.com
|
||||
NS_LITERAL_CSTRING("hao123img.com"), // for hao123.com
|
||||
NS_LITERAL_CSTRING("tabelog.k-img.com"), // for s.tabelog.com
|
||||
NS_LITERAL_CSTRING("tsite.jp"), // for *.tsite.jp
|
||||
};
|
||||
static const size_t sNumBaseDomainsOnWhitelist =
|
||||
MOZ_ARRAY_LENGTH(sBaseDomainsOnWhitelist);
|
||||
|
|
|
@ -13867,7 +13867,9 @@ nsDocShell::GetAsyncPanZoomEnabled(bool* aOut)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
*aOut = false;
|
||||
// If we don't have a presShell, fall back to the default platform value of
|
||||
// whether or not APZ is enabled.
|
||||
*aOut = gfxPlatform::AsyncPanZoomEnabled();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,8 @@
|
|||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/ShadowRoot.h"
|
||||
#include "mozilla/dom/EncodingUtils.h"
|
||||
#include "nsComputedDOMStyle.h"
|
||||
#include "nsContainerFrame.h"
|
||||
#include "nsBlockFrame.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -324,7 +325,45 @@ nsDocumentEncoder::IncludeInContext(nsINode *aNode)
|
|||
|
||||
static
|
||||
bool
|
||||
IsInvisibleBreak(nsINode *aNode) {
|
||||
LineHasNonEmptyContentWorker(nsIFrame* aFrame)
|
||||
{
|
||||
// Look for non-empty frames, but ignore inline and br frames.
|
||||
// For inline frames, descend into the children, if any.
|
||||
if (aFrame->GetType() == nsGkAtoms::inlineFrame) {
|
||||
nsIFrame* child = aFrame->GetFirstPrincipalChild();
|
||||
while (child) {
|
||||
if (LineHasNonEmptyContentWorker(child)) {
|
||||
return true;
|
||||
}
|
||||
child = child->GetNextSibling();
|
||||
}
|
||||
} else {
|
||||
if (aFrame->GetType() != nsGkAtoms::brFrame &&
|
||||
!aFrame->IsEmpty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static
|
||||
bool
|
||||
LineHasNonEmptyContent(nsLineBox* aLine)
|
||||
{
|
||||
int32_t count = aLine->GetChildCount();
|
||||
for (nsIFrame* frame = aLine->mFirstChild; count > 0;
|
||||
--count, frame = frame->GetNextSibling()) {
|
||||
if (LineHasNonEmptyContentWorker(frame)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static
|
||||
bool
|
||||
IsInvisibleBreak(nsINode *aNode)
|
||||
{
|
||||
if (!aNode->IsElement() || !aNode->IsEditable()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -333,14 +372,36 @@ IsInvisibleBreak(nsINode *aNode) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// If the BRFrame has caused a visible line break, it should have a next
|
||||
// sibling, or otherwise no siblings (or immediately after a br) and a
|
||||
// non-zero height.
|
||||
bool visible = frame->GetNextSibling() ||
|
||||
((!frame->GetPrevSibling() ||
|
||||
frame->GetPrevSibling()->GetType() == nsGkAtoms::brFrame) &&
|
||||
frame->GetRect().Height() != 0);
|
||||
return !visible;
|
||||
nsContainerFrame* f = frame->GetParent();
|
||||
while (f && f->IsFrameOfType(nsBox::eLineParticipant)) {
|
||||
f = f->GetParent();
|
||||
}
|
||||
nsBlockFrame* blockAncestor = do_QueryFrame(f);
|
||||
if (!blockAncestor) {
|
||||
// The container frame doesn't support line breaking.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool valid = false;
|
||||
nsBlockInFlowLineIterator iter(blockAncestor, frame, &valid);
|
||||
if (!valid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool lineNonEmpty = LineHasNonEmptyContent(iter.GetLine());
|
||||
|
||||
while (iter.Next()) {
|
||||
auto currentLine = iter.GetLine();
|
||||
// Completely skip empty lines.
|
||||
if (!currentLine->IsEmpty()) {
|
||||
// If we come across an inline line, the BR has caused a visible line break.
|
||||
if (currentLine->IsInline()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lineNonEmpty;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
|
|
@ -28,6 +28,22 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=116083
|
|||
<div data-result="foo bar ! baz" style="white-space: pre-line" contenteditable><div>foo </div><div> bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
|
||||
<div data-result="foo bar ! baz" style="white-space: -moz-pre-space"><div>foo </div><div> bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
|
||||
<div data-result="foo bar ! baz" style="white-space: -moz-pre-space" contenteditable><div>foo </div><div> bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
|
||||
<div data-result="foo bar baz qux"><div>foo<br></div><span>bar<br>baz<br>qux</span></div>
|
||||
<div data-result="foo bar baz qux" contenteditable><div>foo<br></div><span>bar<br>baz<br>qux</span></div>
|
||||
<div data-result="foo "><div>foo</div><span><br></span></div>
|
||||
<div data-result="foo " contenteditable><div>foo</div><span><br></span></div>
|
||||
<div data-result="foo bar"><div>foo</div><span><br></span><div>bar</div></div>
|
||||
<div data-result="foo bar" contenteditable><div>foo</div><span><br></span><div>bar</div></div>
|
||||
<div data-result="foo bar "><div>foo</div><span>bar<br></span></div>
|
||||
<div data-result="foo bar" contenteditable><div>foo</div><span>bar<br></span></div>
|
||||
<div data-result="foo bar baz"><div>foo</div><span>bar<br></span><div>baz</div></div>
|
||||
<div data-result="foo bar baz" contenteditable><div>foo</div><span>bar<br></span><div>baz</div></div>
|
||||
<div data-result=" foo"><div><br><br><div>foo</div></div></div>
|
||||
<div data-result=" foo" contenteditable><div><br><br><div>foo</div></div></div>
|
||||
<div data-result="foo bar"><div>foo<br>bar</div></div>
|
||||
<div data-result="foo bar" contenteditable><div>foo<br>bar</div></div>
|
||||
<div data-result="foo bar "><div>foo<br>bar<br></div></div>
|
||||
<div data-result="foo bar" contenteditable><div>foo<br>bar<br></div></div>
|
||||
<div data-result=" foo bar ">foo bar</div>
|
||||
</div>
|
||||
<script type="application/javascript">
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
<!DOCTYPE html>
|
||||
<!-- This is only used to give us access to the caches global after setting the pref. -->
|
|
@ -22,6 +22,7 @@ support-files =
|
|||
test_cache_put_reorder.js
|
||||
test_cache_https.js
|
||||
large_url_list.js
|
||||
empty.html
|
||||
|
||||
[test_cache.html]
|
||||
[test_cache_add.html]
|
||||
|
|
|
@ -9,6 +9,18 @@
|
|||
</head>
|
||||
<body>
|
||||
<script class="testbody" type="text/javascript">
|
||||
function setupTestIframe() {
|
||||
return new Promise(function(resolve) {
|
||||
var iframe = document.createElement("iframe");
|
||||
iframe.src = "empty.html";
|
||||
iframe.onload = function() {
|
||||
window.caches = iframe.contentWindow.caches;
|
||||
resolve();
|
||||
};
|
||||
document.body.appendChild(iframe);
|
||||
});
|
||||
}
|
||||
|
||||
function resetStorage() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var principal = SpecialPowers.wrap(document).nodePrincipal;
|
||||
|
@ -32,7 +44,9 @@ SpecialPowers.pushPrefEnv({
|
|||
var name = 'foo';
|
||||
var url = './test_cache_add.js';
|
||||
var cache;
|
||||
caches.open(name).then(function(c) {
|
||||
setupTestIframe().then(function() {
|
||||
return caches.open(name);
|
||||
}).then(function(c) {
|
||||
cache = c;
|
||||
return cache.add(url);
|
||||
}).then(function() {
|
||||
|
|
|
@ -10,6 +10,18 @@
|
|||
</head>
|
||||
<body>
|
||||
<script class="testbody" type="text/javascript">
|
||||
function setupTestIframe() {
|
||||
return new Promise(function(resolve) {
|
||||
var iframe = document.createElement("iframe");
|
||||
iframe.src = "empty.html";
|
||||
iframe.onload = function() {
|
||||
window.caches = iframe.contentWindow.caches;
|
||||
resolve();
|
||||
};
|
||||
document.body.appendChild(iframe);
|
||||
});
|
||||
}
|
||||
|
||||
function clearStorage() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var principal = SpecialPowers.wrap(document).nodePrincipal;
|
||||
|
@ -73,7 +85,9 @@ SpecialPowers.pushPrefEnv({
|
|||
var endUsage = 0;
|
||||
// start from a fresh origin directory so other tests do not influence our
|
||||
// results
|
||||
clearStorage().then(function() {
|
||||
setupTestIframe().then(function() {
|
||||
return clearStorage();
|
||||
}).then(function() {
|
||||
return storageUsage();
|
||||
}).then(function(usage) {
|
||||
is(0, usage, 'disk usage should be zero to start');
|
||||
|
|
|
@ -3640,9 +3640,11 @@ SetDefaultPragmas(mozIStorageConnection* aConnection)
|
|||
|
||||
#ifndef IDB_MOBILE
|
||||
if (kSQLiteGrowthIncrement) {
|
||||
// This is just an optimization so ignore the failure if the disk is
|
||||
// currently too full.
|
||||
rv = aConnection->SetGrowthIncrement(kSQLiteGrowthIncrement,
|
||||
EmptyCString());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
if (rv != NS_ERROR_FILE_TOO_BIG && NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,10 +10,13 @@ EXTRA_COMPONENTS += [
|
|||
]
|
||||
|
||||
EXTRA_PP_JS_MODULES += [
|
||||
'PushServiceWebSocket.jsm',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'PushDB.jsm',
|
||||
'PushService.jsm',
|
||||
'PushServiceHttp2.jsm',
|
||||
'PushServiceWebSocket.jsm',
|
||||
]
|
||||
|
||||
MOCHITEST_MANIFESTS += [
|
||||
|
|
|
@ -2546,6 +2546,11 @@ ServiceWorkerManager::GetServiceWorkerRegistrationInfo(nsIPrincipal* aPrincipal,
|
|||
MOZ_ASSERT(aPrincipal);
|
||||
MOZ_ASSERT(aURI);
|
||||
|
||||
//XXXnsm Temporary fix until Bug 1171432 is fixed.
|
||||
if (NS_WARN_IF(BasePrincipal::Cast(aPrincipal)->AppId() == nsIScriptSecurityManager::UNKNOWN_APP_ID)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsAutoCString originAttributesSuffix;
|
||||
nsresult rv = PrincipalToScopeKey(aPrincipal, originAttributesSuffix);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
|
|
|
@ -1695,6 +1695,7 @@ nsEventStatus AsyncPanZoomController::OnPanEnd(const PanGestureInput& aEvent) {
|
|||
|
||||
mX.EndTouch(aEvent.mTime);
|
||||
mY.EndTouch(aEvent.mTime);
|
||||
SetState(NOTHING);
|
||||
RequestContentRepaint();
|
||||
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
|
|
|
@ -607,6 +607,8 @@ CompositorOGL::BeginFrame(const nsIntRegion& aInvalidRegion,
|
|||
*aRenderBoundsOut = rect;
|
||||
}
|
||||
|
||||
mRenderBoundsOut = rect;
|
||||
|
||||
GLint width = rect.width;
|
||||
GLint height = rect.height;
|
||||
|
||||
|
@ -950,6 +952,19 @@ CompositorOGL::DrawQuad(const Rect& aRect,
|
|||
DrawVRDistortion(aRect, aClipRect, aEffectChain, aOpacity, aTransform);
|
||||
return;
|
||||
}
|
||||
|
||||
// XXX: This doesn't handle 3D transforms. It also doesn't handled rotated
|
||||
// quads. Fix me.
|
||||
Rect destRect = aTransform.TransformBounds(aRect);
|
||||
mPixelsFilled += destRect.width * destRect.height;
|
||||
|
||||
// Do a simple culling if this rect is out of target buffer.
|
||||
// Inflate a small size to avoid some numerical imprecision issue.
|
||||
destRect.Inflate(1, 1);
|
||||
if (!mRenderBoundsOut.Intersects(destRect)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LayerScope::DrawBegin();
|
||||
|
||||
Rect clipRect = aClipRect;
|
||||
|
@ -999,13 +1014,6 @@ CompositorOGL::DrawQuad(const Rect& aRect,
|
|||
maskType = MaskType::MaskNone;
|
||||
}
|
||||
|
||||
{
|
||||
// XXX: This doesn't handle 3D transforms. It also doesn't handled rotated
|
||||
// quads. Fix me.
|
||||
const Rect destRect = aTransform.TransformBounds(aRect);
|
||||
mPixelsFilled += destRect.width * destRect.height;
|
||||
}
|
||||
|
||||
// Determine the color if this is a color shader and fold the opacity into
|
||||
// the color since color shaders don't have an opacity uniform.
|
||||
Color color;
|
||||
|
|
|
@ -457,6 +457,8 @@ private:
|
|||
FenceHandle mReleaseFenceHandle;
|
||||
ShaderProgramOGL *mCurrentProgram;
|
||||
|
||||
gfx::Rect mRenderBoundsOut;
|
||||
|
||||
CompositorOGLVRObjects mVR;
|
||||
};
|
||||
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
#include <servers/bootstrap.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <CoreServices/CoreServices.h>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
|
||||
//==============================================================================
|
||||
|
@ -145,16 +143,12 @@ class MachMessage {
|
|||
return GetDataLength() > 0 ? GetDataPacket()->data : NULL;
|
||||
}
|
||||
|
||||
u_int32_t GetDataLength() {
|
||||
return EndianU32_LtoN(GetDataPacket()->data_length);
|
||||
}
|
||||
u_int32_t GetDataLength();
|
||||
|
||||
// The message ID may be used as a code identifying the type of message
|
||||
void SetMessageID(int32_t message_id) {
|
||||
GetDataPacket()->id = EndianU32_NtoL(message_id);
|
||||
}
|
||||
void SetMessageID(int32_t message_id);
|
||||
|
||||
int32_t GetMessageID() { return EndianU32_LtoN(GetDataPacket()->id); }
|
||||
int32_t GetMessageID();
|
||||
|
||||
// Adds a descriptor (typically a mach port) to be translated
|
||||
// returns true if successful, otherwise not enough space
|
||||
|
@ -280,6 +274,9 @@ class ReceivePort {
|
|||
kern_return_t WaitForMessage(MachReceiveMessage *out_message,
|
||||
mach_msg_timeout_t timeout);
|
||||
|
||||
kern_return_t SendMessageToSelf(MachSendMessage& msg,
|
||||
mach_msg_timeout_t timeout);
|
||||
|
||||
// The underlying mach port that we wrap
|
||||
mach_port_t GetPort() const { return port_; }
|
||||
|
||||
|
|
|
@ -59,6 +59,21 @@ MachMessage::~MachMessage() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
u_int32_t MachMessage::GetDataLength() {
|
||||
return EndianU32_LtoN(GetDataPacket()->data_length);
|
||||
}
|
||||
|
||||
// The message ID may be used as a code identifying the type of message
|
||||
void MachMessage::SetMessageID(int32_t message_id) {
|
||||
GetDataPacket()->id = EndianU32_NtoL(message_id);
|
||||
}
|
||||
|
||||
int32_t MachMessage::GetMessageID() {
|
||||
return EndianU32_LtoN(GetDataPacket()->id);
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// returns true if successful
|
||||
bool MachMessage::SetData(const void* data,
|
||||
|
@ -251,7 +266,7 @@ kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message,
|
|||
out_message->Head()->msgh_id = 0;
|
||||
|
||||
kern_return_t result = mach_msg(out_message->Head(),
|
||||
MACH_RCV_MSG | MACH_RCV_TIMEOUT,
|
||||
MACH_RCV_MSG | (timeout == MACH_MSG_TIMEOUT_NONE ? 0 : MACH_RCV_TIMEOUT),
|
||||
0,
|
||||
out_message->MaxSize(),
|
||||
port_,
|
||||
|
@ -261,6 +276,34 @@ kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message,
|
|||
return result;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// send a message to this port
|
||||
kern_return_t ReceivePort::SendMessageToSelf(MachSendMessage& message, mach_msg_timeout_t timeout) {
|
||||
if (message.Head()->msgh_size == 0) {
|
||||
NOTREACHED();
|
||||
return KERN_INVALID_VALUE; // just for safety -- never should occur
|
||||
};
|
||||
|
||||
if (init_result_ != KERN_SUCCESS)
|
||||
return init_result_;
|
||||
|
||||
message.Head()->msgh_remote_port = port_;
|
||||
message.Head()->msgh_bits
|
||||
= MACH_MSGH_BITS (MACH_MSG_TYPE_MAKE_SEND,
|
||||
MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||
kern_return_t result = mach_msg(message.Head(),
|
||||
MACH_SEND_MSG | (timeout == MACH_MSG_TIMEOUT_NONE ? 0 : MACH_SEND_TIMEOUT),
|
||||
message.Head()->msgh_size,
|
||||
0,
|
||||
MACH_PORT_NULL,
|
||||
timeout, // timeout in ms
|
||||
MACH_PORT_NULL);
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
|
||||
//==============================================================================
|
||||
|
@ -297,7 +340,7 @@ kern_return_t MachPortSender::SendMessage(MachSendMessage &message,
|
|||
message.Head()->msgh_remote_port = send_port_;
|
||||
|
||||
kern_return_t result = mach_msg(message.Head(),
|
||||
MACH_SEND_MSG | MACH_SEND_TIMEOUT,
|
||||
MACH_SEND_MSG | (timeout == MACH_MSG_TIMEOUT_NONE ? 0 : MACH_SEND_TIMEOUT),
|
||||
message.Head()->msgh_size,
|
||||
0,
|
||||
MACH_PORT_NULL,
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "chrome/common/mach_ipc_mac.h"
|
||||
#include "base/rand_util.h"
|
||||
#include "nsILocalFileMac.h"
|
||||
#include "SharedMemoryBasic.h"
|
||||
#endif
|
||||
|
||||
#include "MainThreadUtils.h"
|
||||
|
@ -119,12 +120,16 @@ GeckoChildProcessHost::~GeckoChildProcessHost()
|
|||
|
||||
MOZ_COUNT_DTOR(GeckoChildProcessHost);
|
||||
|
||||
if (mChildProcessHandle > 0)
|
||||
if (mChildProcessHandle > 0) {
|
||||
#if defined(MOZ_WIDGET_COCOA)
|
||||
SharedMemoryBasic::CleanupForPid(mChildProcessHandle);
|
||||
#endif
|
||||
ProcessWatcher::EnsureProcessTerminated(mChildProcessHandle
|
||||
#if defined(NS_BUILD_REFCNT_LOGGING)
|
||||
, false // don't "force"
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
#if defined(MOZ_WIDGET_COCOA)
|
||||
if (mChildTask != MACH_PORT_NULL)
|
||||
|
@ -783,18 +788,44 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
|
|||
}
|
||||
MachPortSender parent_sender(child_message.GetTranslatedPort(1));
|
||||
|
||||
if (child_message.GetTranslatedPort(2) == MACH_PORT_NULL) {
|
||||
CHROMIUM_LOG(ERROR) << "parent GetTranslatedPort(2) failed.";
|
||||
}
|
||||
MachPortSender* parent_recv_port_memory_ack = new MachPortSender(child_message.GetTranslatedPort(2));
|
||||
|
||||
if (child_message.GetTranslatedPort(3) == MACH_PORT_NULL) {
|
||||
CHROMIUM_LOG(ERROR) << "parent GetTranslatedPort(3) failed.";
|
||||
}
|
||||
MachPortSender* parent_send_port_memory = new MachPortSender(child_message.GetTranslatedPort(3));
|
||||
|
||||
MachSendMessage parent_message(/* id= */0);
|
||||
if (!parent_message.AddDescriptor(MachMsgPortDescriptor(bootstrap_port))) {
|
||||
CHROMIUM_LOG(ERROR) << "parent AddDescriptor(" << bootstrap_port << ") failed.";
|
||||
return false;
|
||||
}
|
||||
|
||||
ReceivePort* parent_recv_port_memory = new ReceivePort();
|
||||
if (!parent_message.AddDescriptor(MachMsgPortDescriptor(parent_recv_port_memory->GetPort()))) {
|
||||
CHROMIUM_LOG(ERROR) << "parent AddDescriptor(" << parent_recv_port_memory->GetPort() << ") failed.";
|
||||
return false;
|
||||
}
|
||||
|
||||
ReceivePort* parent_send_port_memory_ack = new ReceivePort();
|
||||
if (!parent_message.AddDescriptor(MachMsgPortDescriptor(parent_send_port_memory_ack->GetPort()))) {
|
||||
CHROMIUM_LOG(ERROR) << "parent AddDescriptor(" << parent_send_port_memory_ack->GetPort() << ") failed.";
|
||||
return false;
|
||||
}
|
||||
|
||||
err = parent_sender.SendMessage(parent_message, kTimeoutMs);
|
||||
if (err != KERN_SUCCESS) {
|
||||
std::string errString = StringPrintf("0x%x %s", err, mach_error_string(err));
|
||||
CHROMIUM_LOG(ERROR) << "parent SendMessage() failed: " << errString;
|
||||
return false;
|
||||
}
|
||||
|
||||
SharedMemoryBasic::SetupMachMemory(process, parent_recv_port_memory, parent_recv_port_memory_ack,
|
||||
parent_send_port_memory, parent_send_port_memory_ack, false);
|
||||
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
#ifdef ANDROID
|
||||
# include "mozilla/ipc/SharedMemoryBasic_android.h"
|
||||
#elif defined(XP_MACOSX)
|
||||
# include "mozilla/ipc/SharedMemoryBasic_mach.h"
|
||||
#else
|
||||
# include "mozilla/ipc/SharedMemoryBasic_chromium.h"
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,692 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=8 et :
|
||||
*/
|
||||
/* 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 <map>
|
||||
|
||||
#include <mach/vm_map.h>
|
||||
#include <mach/mach_port.h>
|
||||
#include <mach/mach_vm.h>
|
||||
#include "SharedMemoryBasic.h"
|
||||
#include "chrome/common/mach_ipc_mac.h"
|
||||
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/StaticMutex.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsXPCOMPrivate.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define LOG_ERROR(str, args...) \
|
||||
PR_BEGIN_MACRO \
|
||||
char *msg = PR_smprintf(str, ## args); \
|
||||
NS_WARNING(msg); \
|
||||
PR_smprintf_free(msg); \
|
||||
PR_END_MACRO
|
||||
#else
|
||||
#define LOG_ERROR(str, args...) do { /* nothing */ } while(0)
|
||||
#endif
|
||||
|
||||
#define CHECK_MACH_ERROR(kr, msg) \
|
||||
PR_BEGIN_MACRO \
|
||||
if (kr != KERN_SUCCESS) { \
|
||||
LOG_ERROR("%s %s (%x)\n", msg, mach_error_string(kr), kr); \
|
||||
return false; \
|
||||
} \
|
||||
PR_END_MACRO
|
||||
|
||||
/*
|
||||
* This code is responsible for sharing memory between processes. Memory can be
|
||||
* shared between parent and child or between two children. Each memory region is
|
||||
* referenced via a Mach port. Mach ports are also used for messaging when
|
||||
* sharing a memory region.
|
||||
*
|
||||
* When the parent starts a child, it starts a thread whose only purpose is to
|
||||
* communicate with the child about shared memory. Once the child has started,
|
||||
* it starts a similar thread for communicating with the parent. Each side can
|
||||
* communicate with the thread on the other side via Mach ports. When either
|
||||
* side wants to share memory with the other, it sends a Mach message to the
|
||||
* other side. Attached to the message is the port that references the shared
|
||||
* memory region. When the other side receives the message, it automatically
|
||||
* gets access to the region. It sends a reply (also via a Mach port) so that
|
||||
* the originating side can continue.
|
||||
*
|
||||
* The two sides communicate using four ports. Two ports are used when the
|
||||
* parent shares memory with the child. The other two are used when the child
|
||||
* shares memory with the parent. One of these two ports is used for sending the
|
||||
* "share" message and the other is used for the reply.
|
||||
*
|
||||
* If a child wants to share memory with another child, it sends a "GetPorts"
|
||||
* message to the parent. The parent forwards this GetPorts message to the
|
||||
* target child. The message includes some ports so that the children can talk
|
||||
* directly. Both children start up a thread to communicate with the other child,
|
||||
* similar to the way parent and child communicate. In the future, when these
|
||||
* two children want to communicate, they re-use the channels that were created.
|
||||
*
|
||||
* When a child shuts down, the parent notifies all other children. Those
|
||||
* children then have the opportunity to shut down any threads they might have
|
||||
* been using to communicate directly with that child.
|
||||
*/
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
struct MemoryPorts {
|
||||
MachPortSender* mSender;
|
||||
ReceivePort* mReceiver;
|
||||
|
||||
MemoryPorts() {}
|
||||
MemoryPorts(MachPortSender* sender, ReceivePort* receiver)
|
||||
: mSender(sender), mReceiver(receiver) {}
|
||||
};
|
||||
|
||||
// Protects gMemoryCommPorts and gThreads.
|
||||
static StaticMutex gMutex;
|
||||
|
||||
static std::map<pid_t, MemoryPorts> gMemoryCommPorts;
|
||||
|
||||
enum {
|
||||
kGetPortsMsg = 1,
|
||||
kSharePortsMsg,
|
||||
kReturnIdMsg,
|
||||
kReturnPortsMsg,
|
||||
kShutdownMsg,
|
||||
kCleanupMsg,
|
||||
};
|
||||
|
||||
const int kTimeout = 1000;
|
||||
|
||||
pid_t gParentPid = 0;
|
||||
|
||||
struct PIDPair {
|
||||
pid_t mRequester;
|
||||
pid_t mRequested;
|
||||
|
||||
PIDPair(pid_t requester, pid_t requested)
|
||||
: mRequester(requester), mRequested(requested) {}
|
||||
};
|
||||
|
||||
struct ListeningThread {
|
||||
pthread_t mThread;
|
||||
MemoryPorts* mPorts;
|
||||
|
||||
ListeningThread() {}
|
||||
ListeningThread(pthread_t thread, MemoryPorts* ports)
|
||||
: mThread(thread), mPorts(ports) {}
|
||||
};
|
||||
|
||||
std::map<pid_t, ListeningThread> gThreads;
|
||||
|
||||
static void *
|
||||
PortServerThread(void *argument);
|
||||
|
||||
namespace {
|
||||
|
||||
class ShutdownObserver final : public nsIObserver
|
||||
{
|
||||
~ShutdownObserver() {}
|
||||
|
||||
static bool sInitialized;
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
void InitializeOnMainThread()
|
||||
{
|
||||
if (sInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
if (!observerService) {
|
||||
return;
|
||||
}
|
||||
nsCOMPtr<nsIObserver> obs = new ShutdownObserver();
|
||||
observerService->AddObserver(obs, "xpcom-shutdown-threads", false);
|
||||
sInitialized = true;
|
||||
}
|
||||
|
||||
static void Initialize(bool onMainThread)
|
||||
{
|
||||
if (sInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<ShutdownObserver> obs = new ShutdownObserver();
|
||||
if (onMainThread) {
|
||||
obs->InitializeOnMainThread();
|
||||
} else {
|
||||
NS_DispatchToMainThread(NS_NewRunnableMethod(obs, &ShutdownObserver::InitializeOnMainThread));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool ShutdownObserver::sInitialized = false;
|
||||
|
||||
} // namespace
|
||||
|
||||
NS_IMPL_ISUPPORTS(ShutdownObserver, nsIObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
ShutdownObserver::Observe(nsISupports* subject,
|
||||
const char* topic,
|
||||
const char16_t* data)
|
||||
{
|
||||
MOZ_ASSERT(strcmp(topic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID) == 0);
|
||||
|
||||
StaticMutexAutoLock smal(gMutex);
|
||||
|
||||
for (auto it = gThreads.begin(); it != gThreads.end(); ++it) {
|
||||
MachSendMessage shutdownMsg(kShutdownMsg);
|
||||
it->second.mPorts->mReceiver->SendMessageToSelf(shutdownMsg, kTimeout);
|
||||
pthread_join(it->second.mThread, nullptr);
|
||||
}
|
||||
gThreads.clear();
|
||||
|
||||
for (auto it = gMemoryCommPorts.begin(); it != gMemoryCommPorts.end(); ++it) {
|
||||
delete it->second.mSender;
|
||||
delete it->second.mReceiver;
|
||||
}
|
||||
gMemoryCommPorts.clear();
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
if (observerService) {
|
||||
observerService->RemoveObserver(this, "xpcom-shutdown-threads");
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
SetupMachMemory(pid_t pid,
|
||||
ReceivePort* listen_port,
|
||||
MachPortSender* listen_port_ack,
|
||||
MachPortSender* send_port,
|
||||
ReceivePort* send_port_ack,
|
||||
bool pidIsParent)
|
||||
{
|
||||
if (pidIsParent) {
|
||||
gParentPid = pid;
|
||||
}
|
||||
MemoryPorts* listen_ports = new MemoryPorts(listen_port_ack, listen_port);
|
||||
pthread_t thread;
|
||||
int err = pthread_create(&thread, nullptr, PortServerThread, listen_ports);
|
||||
if (err) {
|
||||
LOG_ERROR("pthread_create failed with %x\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
gMutex.AssertCurrentThreadOwns();
|
||||
gThreads[pid] = ListeningThread(thread, listen_ports);
|
||||
gMemoryCommPorts[pid] = MemoryPorts(send_port, send_port_ack);
|
||||
}
|
||||
|
||||
// Send two communication ports to another process along with the pid of the process that is
|
||||
// listening on them.
|
||||
bool
|
||||
SendPortsMessage(MachPortSender* sender,
|
||||
mach_port_t ports_in_receiver,
|
||||
mach_port_t ports_out_receiver,
|
||||
PIDPair pid_pair)
|
||||
{
|
||||
MachSendMessage getPortsMsg(kGetPortsMsg);
|
||||
if (!getPortsMsg.AddDescriptor(MachMsgPortDescriptor(ports_in_receiver))) {
|
||||
LOG_ERROR("Adding descriptor to message failed");
|
||||
return false;
|
||||
}
|
||||
if (!getPortsMsg.AddDescriptor(MachMsgPortDescriptor(ports_out_receiver))) {
|
||||
LOG_ERROR("Adding descriptor to message failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
getPortsMsg.SetData(&pid_pair, sizeof(PIDPair));
|
||||
kern_return_t err = sender->SendMessage(getPortsMsg, kTimeout);
|
||||
if (KERN_SUCCESS != err) {
|
||||
LOG_ERROR("Error sending get ports message %s (%x)\n", mach_error_string(err), err);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Receive two communication ports from another process
|
||||
bool
|
||||
RecvPortsMessage(ReceivePort* receiver, mach_port_t* ports_in_sender, mach_port_t* ports_out_sender)
|
||||
{
|
||||
MachReceiveMessage rcvPortsMsg;
|
||||
kern_return_t err = receiver->WaitForMessage(&rcvPortsMsg, kTimeout);
|
||||
if (KERN_SUCCESS != err) {
|
||||
LOG_ERROR("Error receiving get ports message %s (%x)\n", mach_error_string(err), err);
|
||||
}
|
||||
if (rcvPortsMsg.GetTranslatedPort(0) == MACH_PORT_NULL) {
|
||||
LOG_ERROR("GetTranslatedPort(0) failed");
|
||||
return false;
|
||||
}
|
||||
*ports_in_sender = rcvPortsMsg.GetTranslatedPort(0);
|
||||
|
||||
if (rcvPortsMsg.GetTranslatedPort(1) == MACH_PORT_NULL) {
|
||||
LOG_ERROR("GetTranslatedPort(1) failed");
|
||||
return false;
|
||||
}
|
||||
*ports_out_sender = rcvPortsMsg.GetTranslatedPort(1);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Send two communication ports to another process and receive two back
|
||||
bool
|
||||
RequestPorts(const MemoryPorts& request_ports,
|
||||
mach_port_t ports_in_receiver,
|
||||
mach_port_t* ports_in_sender,
|
||||
mach_port_t* ports_out_sender,
|
||||
mach_port_t ports_out_receiver,
|
||||
PIDPair pid_pair)
|
||||
{
|
||||
if (!SendPortsMessage(request_ports.mSender, ports_in_receiver, ports_out_receiver, pid_pair)) {
|
||||
return false;
|
||||
}
|
||||
return RecvPortsMessage(request_ports.mReceiver, ports_in_sender, ports_out_sender);
|
||||
}
|
||||
|
||||
MemoryPorts*
|
||||
GetMemoryPortsForPid(pid_t pid)
|
||||
{
|
||||
gMutex.AssertCurrentThreadOwns();
|
||||
|
||||
if (gMemoryCommPorts.find(pid) == gMemoryCommPorts.end()) {
|
||||
// We don't have the ports open to communicate with that pid, so we're going to
|
||||
// ask our parent process over IPC to set them up for us.
|
||||
if (gParentPid == 0) {
|
||||
// If we're the top level parent process, we have no parent to ask.
|
||||
LOG_ERROR("request for ports for pid %d, but we're the chrome process\n", pid);
|
||||
return nullptr;
|
||||
}
|
||||
const MemoryPorts& parent = gMemoryCommPorts[gParentPid];
|
||||
|
||||
// Create two receiving ports in this process to send to the parent. One will be used for
|
||||
// for listening for incoming memory to be shared, the other for getting the Handle of
|
||||
// memory we share to the other process.
|
||||
ReceivePort* ports_in_receiver = new ReceivePort();
|
||||
ReceivePort* ports_out_receiver = new ReceivePort();
|
||||
mach_port_t raw_ports_in_sender, raw_ports_out_sender;
|
||||
if (!RequestPorts(parent,
|
||||
ports_in_receiver->GetPort(),
|
||||
&raw_ports_in_sender,
|
||||
&raw_ports_out_sender,
|
||||
ports_out_receiver->GetPort(),
|
||||
PIDPair(getpid(), pid))) {
|
||||
LOG_ERROR("failed to request ports\n");
|
||||
return nullptr;
|
||||
}
|
||||
// Our parent process sent us two ports, one is for sending new memory to, the other
|
||||
// is for replying with the Handle when we receive new memory.
|
||||
MachPortSender* ports_in_sender = new MachPortSender(raw_ports_in_sender);
|
||||
MachPortSender* ports_out_sender = new MachPortSender(raw_ports_out_sender);
|
||||
SetupMachMemory(pid,
|
||||
ports_in_receiver,
|
||||
ports_in_sender,
|
||||
ports_out_sender,
|
||||
ports_out_receiver,
|
||||
false);
|
||||
MOZ_ASSERT(gMemoryCommPorts.find(pid) != gMemoryCommPorts.end());
|
||||
}
|
||||
return &gMemoryCommPorts.at(pid);
|
||||
}
|
||||
|
||||
// We just received a port representing a region of shared memory, reply to
|
||||
// the process that set it with the mach_port_t that represents it in this process.
|
||||
// That will be the Handle to be shared over normal IPC
|
||||
void
|
||||
HandleSharePortsMessage(MachReceiveMessage* rmsg, MemoryPorts* ports)
|
||||
{
|
||||
mach_port_t port = rmsg->GetTranslatedPort(0);
|
||||
MachSendMessage msg(kReturnIdMsg);
|
||||
msg.SetData(&port, sizeof(port));
|
||||
kern_return_t err = ports->mSender->SendMessage(msg, kTimeout);
|
||||
if (KERN_SUCCESS != err) {
|
||||
LOG_ERROR("SendMessage failed 0x%x %s\n", err, mach_error_string(err));
|
||||
}
|
||||
}
|
||||
|
||||
// We were asked by another process to get communications ports to some process. Return
|
||||
// those ports via an IPC message.
|
||||
bool
|
||||
SendReturnPortsMsg(MachPortSender* sender,
|
||||
mach_port_t raw_ports_in_sender,
|
||||
mach_port_t raw_ports_out_sender)
|
||||
{
|
||||
MachSendMessage getPortsMsg(kReturnPortsMsg);
|
||||
if (!getPortsMsg.AddDescriptor(MachMsgPortDescriptor(raw_ports_in_sender))) {
|
||||
LOG_ERROR("Adding descriptor to message failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!getPortsMsg.AddDescriptor(MachMsgPortDescriptor(raw_ports_out_sender))) {
|
||||
LOG_ERROR("Adding descriptor to message failed");
|
||||
return false;
|
||||
}
|
||||
kern_return_t err = sender->SendMessage(getPortsMsg, kTimeout);
|
||||
if (KERN_SUCCESS != err) {
|
||||
LOG_ERROR("Error sending get ports message %s (%x)\n", mach_error_string(err), err);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// We were asked for communcations ports to a process that isn't us. Assuming that process
|
||||
// is one of our children, forward that request on.
|
||||
void
|
||||
ForwardGetPortsMessage(MachReceiveMessage* rmsg, MemoryPorts* ports, PIDPair* pid_pair)
|
||||
{
|
||||
if (rmsg->GetTranslatedPort(0) == MACH_PORT_NULL) {
|
||||
LOG_ERROR("GetTranslatedPort(0) failed");
|
||||
return;
|
||||
}
|
||||
if (rmsg->GetTranslatedPort(1) == MACH_PORT_NULL) {
|
||||
LOG_ERROR("GetTranslatedPort(1) failed");
|
||||
return;
|
||||
}
|
||||
mach_port_t raw_ports_in_sender, raw_ports_out_sender;
|
||||
MemoryPorts* requestedPorts = GetMemoryPortsForPid(pid_pair->mRequested);
|
||||
if (!requestedPorts) {
|
||||
LOG_ERROR("failed to find port for process\n");
|
||||
return;
|
||||
}
|
||||
if (!RequestPorts(*requestedPorts, rmsg->GetTranslatedPort(0), &raw_ports_in_sender,
|
||||
&raw_ports_out_sender, rmsg->GetTranslatedPort(1), *pid_pair)) {
|
||||
LOG_ERROR("failed to request ports\n");
|
||||
return;
|
||||
}
|
||||
SendReturnPortsMsg(ports->mSender, raw_ports_in_sender, raw_ports_out_sender);
|
||||
}
|
||||
|
||||
// We receieved a message asking us to get communications ports for another process
|
||||
void
|
||||
HandleGetPortsMessage(MachReceiveMessage* rmsg, MemoryPorts* ports)
|
||||
{
|
||||
PIDPair* pid_pair;
|
||||
if (rmsg->GetDataLength() != sizeof(PIDPair)) {
|
||||
LOG_ERROR("Improperly formatted message\n");
|
||||
return;
|
||||
}
|
||||
pid_pair = reinterpret_cast<PIDPair*>(rmsg->GetData());
|
||||
if (pid_pair->mRequested != getpid()) {
|
||||
// This request is for ports to a process that isn't us, forward it to that process
|
||||
ForwardGetPortsMessage(rmsg, ports, pid_pair);
|
||||
} else {
|
||||
if (rmsg->GetTranslatedPort(0) == MACH_PORT_NULL) {
|
||||
LOG_ERROR("GetTranslatedPort(0) failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (rmsg->GetTranslatedPort(1) == MACH_PORT_NULL) {
|
||||
LOG_ERROR("GetTranslatedPort(1) failed");
|
||||
return;
|
||||
}
|
||||
|
||||
MachPortSender* ports_in_sender = new MachPortSender(rmsg->GetTranslatedPort(0));
|
||||
MachPortSender* ports_out_sender = new MachPortSender(rmsg->GetTranslatedPort(1));
|
||||
|
||||
ReceivePort* ports_in_receiver = new ReceivePort();
|
||||
ReceivePort* ports_out_receiver = new ReceivePort();
|
||||
if (SendReturnPortsMsg(ports->mSender, ports_in_receiver->GetPort(), ports_out_receiver->GetPort())) {
|
||||
SetupMachMemory(pid_pair->mRequester,
|
||||
ports_out_receiver,
|
||||
ports_out_sender,
|
||||
ports_in_sender,
|
||||
ports_in_receiver,
|
||||
false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void *
|
||||
PortServerThread(void *argument)
|
||||
{
|
||||
MemoryPorts* ports = static_cast<MemoryPorts*>(argument);
|
||||
MachReceiveMessage child_message;
|
||||
while (true) {
|
||||
MachReceiveMessage rmsg;
|
||||
kern_return_t err = ports->mReceiver->WaitForMessage(&rmsg, MACH_MSG_TIMEOUT_NONE);
|
||||
if (err != KERN_SUCCESS) {
|
||||
LOG_ERROR("Wait for message failed 0x%x %s\n", err, mach_error_string(err));
|
||||
continue;
|
||||
}
|
||||
|
||||
StaticMutexAutoLock smal(gMutex);
|
||||
|
||||
switch (rmsg.GetMessageID()) {
|
||||
case kSharePortsMsg:
|
||||
HandleSharePortsMessage(&rmsg, ports);
|
||||
break;
|
||||
case kGetPortsMsg:
|
||||
HandleGetPortsMessage(&rmsg, ports);
|
||||
break;
|
||||
case kShutdownMsg:
|
||||
delete ports->mSender;
|
||||
delete ports->mReceiver;
|
||||
delete ports;
|
||||
return nullptr;
|
||||
case kCleanupMsg:
|
||||
if (gParentPid == 0) {
|
||||
LOG_ERROR("Cleanup message not valid for parent process");
|
||||
continue;
|
||||
}
|
||||
|
||||
pid_t* pid;
|
||||
if (rmsg.GetDataLength() != sizeof(pid_t)) {
|
||||
LOG_ERROR("Improperly formatted message\n");
|
||||
continue;
|
||||
}
|
||||
pid = reinterpret_cast<pid_t*>(rmsg.GetData());
|
||||
SharedMemoryBasic::CleanupForPid(*pid);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Unknown message\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SharedMemoryBasic::SetupMachMemory(pid_t pid,
|
||||
ReceivePort* listen_port,
|
||||
MachPortSender* listen_port_ack,
|
||||
MachPortSender* send_port,
|
||||
ReceivePort* send_port_ack,
|
||||
bool pidIsParent)
|
||||
{
|
||||
StaticMutexAutoLock smal(gMutex);
|
||||
mozilla::ipc::SetupMachMemory(pid, listen_port, listen_port_ack, send_port, send_port_ack, pidIsParent);
|
||||
|
||||
ShutdownObserver::Initialize(pidIsParent);
|
||||
}
|
||||
|
||||
void
|
||||
SharedMemoryBasic::CleanupForPid(pid_t pid)
|
||||
{
|
||||
if (gThreads.find(pid) == gThreads.end()) {
|
||||
return;
|
||||
}
|
||||
const ListeningThread& listeningThread = gThreads[pid];
|
||||
MachSendMessage shutdownMsg(kShutdownMsg);
|
||||
kern_return_t ret = listeningThread.mPorts->mReceiver->SendMessageToSelf(shutdownMsg, kTimeout);
|
||||
if (ret != KERN_SUCCESS) {
|
||||
LOG_ERROR("sending shutdown msg failed %s %x\n", mach_error_string(ret), ret);
|
||||
}
|
||||
gThreads.erase(pid);
|
||||
|
||||
if (gParentPid == 0) {
|
||||
// We're the parent. Broadcast the cleanup message to everyone else.
|
||||
for (auto it = gMemoryCommPorts.begin(); it != gMemoryCommPorts.end(); ++it) {
|
||||
MachSendMessage msg(kCleanupMsg);
|
||||
msg.SetData(&pid, sizeof(pid));
|
||||
// We don't really care if this fails, we could be trying to send to an already shut down proc
|
||||
it->second.mSender->SendMessage(msg, kTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
MemoryPorts& ports = gMemoryCommPorts[pid];
|
||||
delete ports.mSender;
|
||||
delete ports.mReceiver;
|
||||
gMemoryCommPorts.erase(pid);
|
||||
}
|
||||
|
||||
SharedMemoryBasic::SharedMemoryBasic()
|
||||
: mPort(MACH_PORT_NULL)
|
||||
, mMemory(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
SharedMemoryBasic::SharedMemoryBasic(Handle aHandle)
|
||||
: mPort(MACH_PORT_NULL)
|
||||
, mMemory(nullptr)
|
||||
{
|
||||
mPort = aHandle;
|
||||
}
|
||||
|
||||
SharedMemoryBasic::~SharedMemoryBasic()
|
||||
{
|
||||
Unmap();
|
||||
Destroy();
|
||||
}
|
||||
|
||||
static inline void*
|
||||
toPointer(mach_vm_address_t address)
|
||||
{
|
||||
return reinterpret_cast<void*>(static_cast<uintptr_t>(address));
|
||||
}
|
||||
|
||||
static inline mach_vm_address_t
|
||||
toVMAddress(void* pointer)
|
||||
{
|
||||
return static_cast<mach_vm_address_t>(reinterpret_cast<uintptr_t>(pointer));
|
||||
}
|
||||
|
||||
bool
|
||||
SharedMemoryBasic::Create(size_t size)
|
||||
{
|
||||
mach_vm_address_t address;
|
||||
|
||||
kern_return_t kr = mach_vm_allocate(mach_task_self(), &address, round_page(size), VM_FLAGS_ANYWHERE);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
LOG_ERROR("Failed to allocate mach_vm_allocate shared memory (%zu bytes). %s (%x)\n",
|
||||
size, mach_error_string(kr), kr);
|
||||
return false;
|
||||
}
|
||||
|
||||
memory_object_size_t memoryObjectSize = round_page(size);
|
||||
|
||||
kr = mach_make_memory_entry_64(mach_task_self(),
|
||||
&memoryObjectSize,
|
||||
address,
|
||||
VM_PROT_DEFAULT,
|
||||
&mPort,
|
||||
MACH_PORT_NULL);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
LOG_ERROR("Failed to make memory entry (%zu bytes). %s (%x)\n",
|
||||
size, mach_error_string(kr), kr);
|
||||
return false;
|
||||
}
|
||||
|
||||
mMemory = toPointer(address);
|
||||
Mapped(size);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SharedMemoryBasic::Map(size_t size)
|
||||
{
|
||||
if (mMemory) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (MACH_PORT_NULL == mPort) {
|
||||
return false;
|
||||
}
|
||||
|
||||
kern_return_t kr;
|
||||
mach_vm_address_t address = 0;
|
||||
|
||||
vm_prot_t vmProtection = VM_PROT_READ | VM_PROT_WRITE;
|
||||
|
||||
kr = mach_vm_map(mach_task_self(), &address, round_page(size), 0, VM_FLAGS_ANYWHERE,
|
||||
mPort, 0, false, vmProtection, vmProtection, VM_INHERIT_NONE);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
LOG_ERROR("Failed to map shared memory (%zu bytes) into %x, port %x. %s (%x)\n",
|
||||
size, mach_task_self(), mPort, mach_error_string(kr), kr);
|
||||
return false;
|
||||
}
|
||||
|
||||
mMemory = toPointer(address);
|
||||
Mapped(size);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SharedMemoryBasic::ShareToProcess(base::ProcessId pid,
|
||||
Handle* aNewHandle)
|
||||
{
|
||||
StaticMutexAutoLock smal(gMutex);
|
||||
|
||||
MemoryPorts* ports = GetMemoryPortsForPid(pid);
|
||||
if (!ports) {
|
||||
LOG_ERROR("Unable to get ports for process.\n");
|
||||
return false;
|
||||
}
|
||||
MachSendMessage smsg(kSharePortsMsg);
|
||||
smsg.AddDescriptor(MachMsgPortDescriptor(mPort, MACH_MSG_TYPE_COPY_SEND));
|
||||
kern_return_t err = ports->mSender->SendMessage(smsg, kTimeout);
|
||||
if (err != KERN_SUCCESS) {
|
||||
LOG_ERROR("sending port failed %s %x\n", mach_error_string(err), err);
|
||||
return false;
|
||||
}
|
||||
MachReceiveMessage msg;
|
||||
err = ports->mReceiver->WaitForMessage(&msg, kTimeout);
|
||||
if (err != KERN_SUCCESS) {
|
||||
LOG_ERROR("didn't get an id %s %x\n", mach_error_string(err), err);
|
||||
return false;
|
||||
}
|
||||
if (msg.GetDataLength() != sizeof(mach_port_t)) {
|
||||
LOG_ERROR("Improperly formatted reply\n");
|
||||
return false;
|
||||
}
|
||||
mach_port_t *id = reinterpret_cast<mach_port_t*>(msg.GetData());
|
||||
*aNewHandle = *id;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
SharedMemoryBasic::Unmap()
|
||||
{
|
||||
if (!mMemory) {
|
||||
return;
|
||||
}
|
||||
vm_address_t address = toVMAddress(mMemory);
|
||||
kern_return_t kr = vm_deallocate(mach_task_self(), address, vm_page_size);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
LOG_ERROR("Failed to deallocate shared memory. %s (%x)\n", mach_error_string(kr), kr);
|
||||
return;
|
||||
}
|
||||
mMemory = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
SharedMemoryBasic::Destroy()
|
||||
{
|
||||
mach_port_deallocate(mach_task_self(), mPort);
|
||||
}
|
||||
|
||||
bool
|
||||
SharedMemoryBasic::IsHandleValid(const Handle& aHandle)
|
||||
{
|
||||
return aHandle > 0;
|
||||
}
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,84 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=8 et :
|
||||
*/
|
||||
/* 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_ipc_SharedMemoryBasic_mach_h
|
||||
#define mozilla_ipc_SharedMemoryBasic_mach_h
|
||||
|
||||
#include "base/file_descriptor_posix.h"
|
||||
#include "base/process.h"
|
||||
|
||||
#include "SharedMemory.h"
|
||||
#include <mach/port.h>
|
||||
|
||||
//
|
||||
// This is a low-level wrapper around platform shared memory. Don't
|
||||
// use it directly; use Shmem allocated through IPDL interfaces.
|
||||
//
|
||||
|
||||
class MachPortSender;
|
||||
class ReceivePort;
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
class SharedMemoryBasic final : public SharedMemory
|
||||
{
|
||||
public:
|
||||
typedef mach_port_t Handle;
|
||||
|
||||
static void SetupMachMemory(pid_t pid,
|
||||
ReceivePort* listen_port,
|
||||
MachPortSender* listen_port_ack,
|
||||
MachPortSender* send_port,
|
||||
ReceivePort* send_port_ack,
|
||||
bool pidIsParent);
|
||||
|
||||
static void CleanupForPid(pid_t pid);
|
||||
|
||||
SharedMemoryBasic();
|
||||
|
||||
explicit SharedMemoryBasic(Handle aHandle);
|
||||
|
||||
virtual bool Create(size_t aNbytes) override;
|
||||
|
||||
virtual bool Map(size_t nBytes) override;
|
||||
|
||||
virtual void* memory() const override
|
||||
{
|
||||
return mMemory;
|
||||
}
|
||||
|
||||
virtual SharedMemoryType Type() const override
|
||||
{
|
||||
return TYPE_BASIC;
|
||||
}
|
||||
|
||||
static Handle NULLHandle()
|
||||
{
|
||||
return Handle();
|
||||
}
|
||||
|
||||
|
||||
static bool IsHandleValid(const Handle &aHandle);
|
||||
|
||||
bool ShareToProcess(base::ProcessId aProcessId,
|
||||
Handle* aNewHandle);
|
||||
|
||||
private:
|
||||
~SharedMemoryBasic();
|
||||
|
||||
void Unmap();
|
||||
void Destroy();
|
||||
mach_port_t mPort;
|
||||
// Pointer to mapped region, null if unmapped.
|
||||
void *mMemory;
|
||||
};
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // ifndef mozilla_ipc_SharedMemoryBasic_mach_h
|
|
@ -76,6 +76,11 @@ if CONFIG['OS_TARGET'] == 'Android':
|
|||
UNIFIED_SOURCES += [
|
||||
'SharedMemoryBasic_android.cpp',
|
||||
]
|
||||
elif CONFIG['OS_ARCH'] == 'Darwin':
|
||||
EXPORTS.mozilla.ipc += ['SharedMemoryBasic_mach.h']
|
||||
SOURCES += [
|
||||
'SharedMemoryBasic_mach.cpp',
|
||||
]
|
||||
else:
|
||||
EXPORTS.mozilla.ipc += ['SharedMemoryBasic_chromium.h']
|
||||
|
||||
|
|
|
@ -1902,182 +1902,430 @@ BytecodeEmitter::bindNameToSlot(ParseNode* pn)
|
|||
bool
|
||||
BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
||||
{
|
||||
if (!pn || *answer)
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
|
||||
switch (pn->getKind()) {
|
||||
// Trivial cases with no side effects.
|
||||
case PNK_NEWTARGET:
|
||||
case PNK_NOP:
|
||||
case PNK_STRING:
|
||||
case PNK_TEMPLATE_STRING:
|
||||
case PNK_REGEXP:
|
||||
case PNK_TRUE:
|
||||
case PNK_FALSE:
|
||||
case PNK_NULL:
|
||||
case PNK_THIS:
|
||||
case PNK_ELISION:
|
||||
case PNK_GENERATOR:
|
||||
case PNK_NUMBER:
|
||||
case PNK_OBJECT_PROPERTY_NAME:
|
||||
MOZ_ASSERT(pn->isArity(PN_NULLARY));
|
||||
*answer = false;
|
||||
return true;
|
||||
|
||||
switch (pn->getArity()) {
|
||||
case PN_CODE:
|
||||
case PNK_BREAK:
|
||||
case PNK_CONTINUE:
|
||||
case PNK_DEBUGGER:
|
||||
MOZ_ASSERT(pn->isArity(PN_NULLARY));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
// Watch out for getters!
|
||||
case PNK_SUPERPROP:
|
||||
MOZ_ASSERT(pn->isArity(PN_NULLARY));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
// Again, getters.
|
||||
case PNK_DOT:
|
||||
MOZ_ASSERT(pn->isArity(PN_NAME));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
// Unary cases with side effects only if the child has them.
|
||||
case PNK_TYPEOFEXPR:
|
||||
case PNK_VOID:
|
||||
case PNK_NOT:
|
||||
case PNK_COMPUTED_NAME:
|
||||
MOZ_ASSERT(pn->isArity(PN_UNARY));
|
||||
return checkSideEffects(pn->pn_kid, answer);
|
||||
|
||||
// Looking up or evaluating the associated name could throw.
|
||||
case PNK_TYPEOFNAME:
|
||||
MOZ_ASSERT(pn->isArity(PN_UNARY));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
// These unary cases have side effects on the enclosing object/array,
|
||||
// sure. But that's not the question this function answers: it's
|
||||
// whether the operation may have a side effect on something *other* than
|
||||
// the result of the overall operation in which it's embedded. The
|
||||
// answer to that is no, for an object literal having a mutated prototype
|
||||
// and an array comprehension containing no other effectful operations
|
||||
// only produce a value, without affecting anything else.
|
||||
case PNK_MUTATEPROTO:
|
||||
case PNK_ARRAYPUSH:
|
||||
MOZ_ASSERT(pn->isArity(PN_UNARY));
|
||||
return checkSideEffects(pn->pn_kid, answer);
|
||||
|
||||
// Unary cases with obvious side effects.
|
||||
case PNK_PREINCREMENT:
|
||||
case PNK_POSTINCREMENT:
|
||||
case PNK_PREDECREMENT:
|
||||
case PNK_POSTDECREMENT:
|
||||
case PNK_THROW:
|
||||
MOZ_ASSERT(pn->isArity(PN_UNARY));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
// These might invoke valueOf/toString, even with a subexpression without
|
||||
// side effects! Consider |+{ valueOf: null, toString: null }|.
|
||||
case PNK_BITNOT:
|
||||
case PNK_POS:
|
||||
case PNK_NEG:
|
||||
MOZ_ASSERT(pn->isArity(PN_UNARY));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
// This invokes the (user-controllable) iterator protocol.
|
||||
case PNK_SPREAD:
|
||||
MOZ_ASSERT(pn->isArity(PN_UNARY));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
case PNK_YIELD_STAR:
|
||||
case PNK_YIELD:
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
// Deletion generally has side effects, even if isolated cases have none.
|
||||
case PNK_DELETENAME:
|
||||
case PNK_DELETEPROP:
|
||||
case PNK_DELETESUPERPROP:
|
||||
case PNK_DELETEELEM:
|
||||
case PNK_DELETESUPERELEM:
|
||||
MOZ_ASSERT(pn->isArity(PN_UNARY));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
// Deletion of a non-Reference expression has side effects only through
|
||||
// evaluating the expression.
|
||||
case PNK_DELETEEXPR: {
|
||||
MOZ_ASSERT(pn->isArity(PN_UNARY));
|
||||
ParseNode* expr = pn->pn_kid;
|
||||
return checkSideEffects(expr, answer);
|
||||
}
|
||||
|
||||
case PNK_SEMI:
|
||||
MOZ_ASSERT(pn->isArity(PN_UNARY));
|
||||
if (ParseNode* expr = pn->pn_kid)
|
||||
return checkSideEffects(expr, answer);
|
||||
*answer = false;
|
||||
return true;
|
||||
|
||||
// Binary cases with obvious side effects.
|
||||
case PNK_ASSIGN:
|
||||
case PNK_ADDASSIGN:
|
||||
case PNK_SUBASSIGN:
|
||||
case PNK_BITORASSIGN:
|
||||
case PNK_BITXORASSIGN:
|
||||
case PNK_BITANDASSIGN:
|
||||
case PNK_LSHASSIGN:
|
||||
case PNK_RSHASSIGN:
|
||||
case PNK_URSHASSIGN:
|
||||
case PNK_MULASSIGN:
|
||||
case PNK_DIVASSIGN:
|
||||
case PNK_MODASSIGN:
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
case PNK_STATEMENTLIST:
|
||||
case PNK_CATCHLIST:
|
||||
// Strict equality operations and logical operators are well-behaved and
|
||||
// perform no conversions.
|
||||
case PNK_OR:
|
||||
case PNK_AND:
|
||||
case PNK_STRICTEQ:
|
||||
case PNK_STRICTNE:
|
||||
// Any subexpression of a comma expression could be effectful.
|
||||
case PNK_COMMA:
|
||||
MOZ_ASSERT(pn->pn_count > 0);
|
||||
// Subcomponents of a literal may be effectful.
|
||||
case PNK_ARRAY:
|
||||
case PNK_OBJECT:
|
||||
MOZ_ASSERT(pn->isArity(PN_LIST));
|
||||
for (ParseNode* item = pn->pn_head; item; item = item->pn_next) {
|
||||
if (!checkSideEffects(item, answer))
|
||||
return false;
|
||||
if (*answer)
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
||||
// Most other binary operations (parsed as lists in SpiderMonkey) may
|
||||
// perform conversions triggering side effects. Math operations perform
|
||||
// ToNumber and may fail invoking invalid user-defined toString/valueOf:
|
||||
// |5 < { toString: null }|. |instanceof| throws if provided a
|
||||
// non-object constructor: |null instanceof null|. |in| throws if given
|
||||
// a non-object RHS: |5 in null|.
|
||||
case PNK_BITOR:
|
||||
case PNK_BITXOR:
|
||||
case PNK_BITAND:
|
||||
case PNK_EQ:
|
||||
case PNK_NE:
|
||||
case PNK_LT:
|
||||
case PNK_LE:
|
||||
case PNK_GT:
|
||||
case PNK_GE:
|
||||
case PNK_INSTANCEOF:
|
||||
case PNK_IN:
|
||||
case PNK_LSH:
|
||||
case PNK_RSH:
|
||||
case PNK_URSH:
|
||||
case PNK_ADD:
|
||||
case PNK_SUB:
|
||||
case PNK_STAR:
|
||||
case PNK_DIV:
|
||||
case PNK_MOD:
|
||||
MOZ_ASSERT(pn->isArity(PN_LIST));
|
||||
MOZ_ASSERT(pn->pn_count >= 2);
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
case PNK_DEFAULT:
|
||||
case PNK_COLON:
|
||||
case PNK_CASE:
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
if (!checkSideEffects(pn->pn_left, answer))
|
||||
return false;
|
||||
if (*answer)
|
||||
return true;
|
||||
return checkSideEffects(pn->pn_right, answer);
|
||||
|
||||
// More getters.
|
||||
case PNK_ELEM:
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
// Again, getters.
|
||||
case PNK_SUPERELEM:
|
||||
MOZ_ASSERT(pn->isArity(PN_UNARY));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
// These affect visible names in this code, or in other code.
|
||||
case PNK_IMPORT:
|
||||
case PNK_EXPORT_FROM:
|
||||
case PNK_EXPORT_DEFAULT:
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
// Likewise.
|
||||
case PNK_EXPORT:
|
||||
MOZ_ASSERT(pn->isArity(PN_UNARY));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
// Every part of a loop might be effect-free, but looping infinitely *is*
|
||||
// an effect. (Language lawyer trivia: C++ says threads can be assumed
|
||||
// to exit or have side effects, C++14 [intro.multithread]p27, so a C++
|
||||
// implementation's equivalent of the below could set |*answer = false;|
|
||||
// if all loop sub-nodes set |*answer = false|!)
|
||||
case PNK_DOWHILE:
|
||||
case PNK_WHILE:
|
||||
case PNK_FOR:
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
// Declarations affect the name set of the relevant scope.
|
||||
case PNK_VAR:
|
||||
case PNK_CONST:
|
||||
case PNK_LET:
|
||||
case PNK_GLOBALCONST:
|
||||
MOZ_ASSERT(pn->isArity(PN_LIST));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
case PNK_CONDITIONAL:
|
||||
MOZ_ASSERT(pn->isArity(PN_TERNARY));
|
||||
if (!checkSideEffects(pn->pn_kid1, answer))
|
||||
return false;
|
||||
if (*answer)
|
||||
return true;
|
||||
if (!checkSideEffects(pn->pn_kid2, answer))
|
||||
return false;
|
||||
if (*answer)
|
||||
return true;
|
||||
return checkSideEffects(pn->pn_kid3, answer);
|
||||
|
||||
case PNK_IF:
|
||||
MOZ_ASSERT(pn->isArity(PN_TERNARY));
|
||||
if (!checkSideEffects(pn->pn_kid1, answer))
|
||||
return false;
|
||||
if (*answer)
|
||||
return true;
|
||||
if (!checkSideEffects(pn->pn_kid2, answer))
|
||||
return false;
|
||||
if (*answer)
|
||||
return true;
|
||||
if (ParseNode* elseNode = pn->pn_kid3) {
|
||||
if (!checkSideEffects(elseNode, answer))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
// Function calls can invoke non-local code.
|
||||
case PNK_NEW:
|
||||
case PNK_CALL:
|
||||
case PNK_TAGGED_TEMPLATE:
|
||||
MOZ_ASSERT(pn->isArity(PN_LIST));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
// Classes typically introduce names. Even if no name is introduced,
|
||||
// the heritage and/or class body (through computed property names)
|
||||
// usually have effects.
|
||||
case PNK_CLASS:
|
||||
MOZ_ASSERT(pn->isArity(PN_TERNARY));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
// |with| calls |ToObject| on its expression and so throws if that value
|
||||
// is null/undefined.
|
||||
case PNK_WITH:
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
case PNK_RETURN:
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
case PNK_NAME:
|
||||
MOZ_ASSERT(pn->isArity(PN_NAME));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
// Shorthands could trigger getters: the |x| in the object literal in
|
||||
// |with ({ get x() { throw 42; } }) ({ x });|, for example, triggers
|
||||
// one. (Of course, it isn't necessary to use |with| for a shorthand to
|
||||
// trigger a getter.)
|
||||
case PNK_SHORTHAND:
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
case PNK_FUNCTION:
|
||||
MOZ_ASSERT(pn->isArity(PN_CODE));
|
||||
/*
|
||||
* A named function, contrary to ES3, is no longer useful, because we
|
||||
* bind its name lexically (using JSOP_CALLEE) instead of creating an
|
||||
* Object instance and binding a readonly, permanent property in it
|
||||
* A named function, contrary to ES3, is no longer effectful, because
|
||||
* we bind its name lexically (using JSOP_CALLEE) instead of creating
|
||||
* an Object instance and binding a readonly, permanent property in it
|
||||
* (the object and binding can be detected and hijacked or captured).
|
||||
* This is a bug fix to ES3; it is fixed in ES3.1 drafts.
|
||||
*/
|
||||
MOZ_ASSERT(*answer == false);
|
||||
*answer = false;
|
||||
return true;
|
||||
|
||||
case PN_LIST:
|
||||
if (pn->isOp(JSOP_NOP) || pn->isOp(JSOP_OR) || pn->isOp(JSOP_AND) ||
|
||||
pn->isOp(JSOP_STRICTEQ) || pn->isOp(JSOP_STRICTNE)) {
|
||||
/*
|
||||
* Non-operators along with ||, &&, ===, and !== never invoke
|
||||
* toString or valueOf.
|
||||
*/
|
||||
bool ok = true;
|
||||
for (ParseNode* pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
|
||||
ok &= checkSideEffects(pn2, answer);
|
||||
return ok;
|
||||
}
|
||||
// Generator expressions have no side effects on their own.
|
||||
case PNK_GENEXP:
|
||||
MOZ_ASSERT(pn->isArity(PN_LIST));
|
||||
*answer = false;
|
||||
return true;
|
||||
|
||||
if (pn->isKind(PNK_GENEXP)) {
|
||||
/* Generator-expressions are harmless if the result is ignored. */
|
||||
MOZ_ASSERT(*answer == false);
|
||||
case PNK_TRY:
|
||||
MOZ_ASSERT(pn->isArity(PN_TERNARY));
|
||||
if (!checkSideEffects(pn->pn_kid1, answer))
|
||||
return false;
|
||||
if (*answer)
|
||||
return true;
|
||||
if (ParseNode* catchList = pn->pn_kid2) {
|
||||
MOZ_ASSERT(catchList->isKind(PNK_CATCHLIST));
|
||||
if (!checkSideEffects(catchList, answer))
|
||||
return false;
|
||||
if (*answer)
|
||||
return true;
|
||||
}
|
||||
if (ParseNode* finallyBlock = pn->pn_kid3) {
|
||||
if (!checkSideEffects(finallyBlock, answer))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
/*
|
||||
* All invocation operations (construct: PNK_NEW, call: PNK_CALL)
|
||||
* are presumed to be useful, because they may have side effects
|
||||
* even if their main effect (their return value) is discarded.
|
||||
*
|
||||
* PNK_ELEM binary trees of 3+ nodes are flattened into lists to
|
||||
* avoid too much recursion. All such lists must be presumed to be
|
||||
* useful because each index operation could invoke a getter.
|
||||
*
|
||||
* Likewise, array and object initialisers may call prototype
|
||||
* setters (the __defineSetter__ built-in, and writable __proto__
|
||||
* on Array.prototype create this hazard). Initialiser list nodes
|
||||
* have JSOP_NEWINIT in their pn_op.
|
||||
*/
|
||||
case PNK_CATCH:
|
||||
MOZ_ASSERT(pn->isArity(PN_TERNARY));
|
||||
if (!checkSideEffects(pn->pn_kid1, answer))
|
||||
return false;
|
||||
if (*answer)
|
||||
return true;
|
||||
if (ParseNode* cond = pn->pn_kid2) {
|
||||
if (!checkSideEffects(cond, answer))
|
||||
return false;
|
||||
if (*answer)
|
||||
return true;
|
||||
}
|
||||
return checkSideEffects(pn->pn_kid3, answer);
|
||||
|
||||
case PNK_SWITCH:
|
||||
case PNK_LETBLOCK:
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
if (!checkSideEffects(pn->pn_left, answer))
|
||||
return false;
|
||||
return *answer || checkSideEffects(pn->pn_right, answer);
|
||||
|
||||
case PNK_LABEL:
|
||||
case PNK_LEXICALSCOPE:
|
||||
MOZ_ASSERT(pn->isArity(PN_NAME));
|
||||
return checkSideEffects(pn->expr(), answer);
|
||||
|
||||
// We could methodically check every interpolated expression, but it's
|
||||
// probably not worth the trouble. Treat template strings as effect-free
|
||||
// only if they don't contain any substitutions.
|
||||
case PNK_TEMPLATE_STRING_LIST:
|
||||
MOZ_ASSERT(pn->isArity(PN_LIST));
|
||||
MOZ_ASSERT(pn->pn_count > 0);
|
||||
MOZ_ASSERT((pn->pn_count % 2) == 1,
|
||||
"template strings must alternate template and substitution "
|
||||
"parts");
|
||||
*answer = pn->pn_count > 1;
|
||||
return true;
|
||||
|
||||
case PNK_ARRAYCOMP:
|
||||
MOZ_ASSERT(pn->isArity(PN_LIST));
|
||||
MOZ_ASSERT(pn->pn_count == 1);
|
||||
return checkSideEffects(pn->pn_head, answer);
|
||||
|
||||
case PNK_ARGSBODY:
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
case PN_TERNARY:
|
||||
return checkSideEffects(pn->pn_kid1, answer) &&
|
||||
checkSideEffects(pn->pn_kid2, answer) &&
|
||||
checkSideEffects(pn->pn_kid3, answer);
|
||||
case PNK_FORIN: // by PNK_FOR
|
||||
case PNK_FOROF: // by PNK_FOR
|
||||
case PNK_FORHEAD: // by PNK_FOR
|
||||
case PNK_FRESHENBLOCK: // by PNK_FOR
|
||||
case PNK_CLASSMETHOD: // by PNK_CLASS
|
||||
case PNK_CLASSNAMES: // by PNK_CLASS
|
||||
case PNK_CLASSMETHODLIST: // by PNK_CLASS
|
||||
case PNK_IMPORT_SPEC_LIST: // by PNK_IMPORT
|
||||
case PNK_IMPORT_SPEC: // by PNK_IMPORT
|
||||
case PNK_EXPORT_BATCH_SPEC:// by PNK_EXPORT
|
||||
case PNK_EXPORT_SPEC_LIST: // by PNK_EXPORT
|
||||
case PNK_EXPORT_SPEC: // by PNK_EXPORT
|
||||
case PNK_CALLSITEOBJ: // by PNK_TAGGED_TEMPLATE
|
||||
MOZ_CRASH("handled by parent nodes");
|
||||
|
||||
case PN_BINARY:
|
||||
case PN_BINARY_OBJ:
|
||||
if (pn->isAssignment()) {
|
||||
/*
|
||||
* Assignment is presumed to be useful, even if the next operation
|
||||
* is another assignment overwriting this one's ostensible effect,
|
||||
* because the left operand may be a property with a setter that
|
||||
* has side effects.
|
||||
*
|
||||
* The only exception is assignment of a useless value to a const
|
||||
* declared in the function currently being compiled.
|
||||
*/
|
||||
ParseNode* pn2 = pn->pn_left;
|
||||
if (!pn2->isKind(PNK_NAME)) {
|
||||
*answer = true;
|
||||
} else {
|
||||
if (!bindNameToSlot(pn2))
|
||||
return false;
|
||||
if (!checkSideEffects(pn->pn_right, answer))
|
||||
return false;
|
||||
if (!*answer && (!pn->isOp(JSOP_NOP) || !pn2->isConst()))
|
||||
*answer = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!pn->isOp(JSOP_OR), "|| produces a list now");
|
||||
MOZ_ASSERT(!pn->isOp(JSOP_AND), "&& produces a list now");
|
||||
MOZ_ASSERT(!pn->isOp(JSOP_STRICTEQ), "=== produces a list now");
|
||||
MOZ_ASSERT(!pn->isOp(JSOP_STRICTNE), "!== produces a list now");
|
||||
|
||||
/*
|
||||
* We can't easily prove that neither operand ever denotes an
|
||||
* object with a toString or valueOf method.
|
||||
*/
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
case PN_UNARY:
|
||||
switch (pn->getKind()) {
|
||||
case PNK_DELETENAME: {
|
||||
ParseNode* nameExpr = pn->pn_kid;
|
||||
MOZ_ASSERT(nameExpr->isKind(PNK_NAME));
|
||||
if (!bindNameToSlot(nameExpr))
|
||||
return false;
|
||||
*answer = !nameExpr->isConst();
|
||||
return true;
|
||||
}
|
||||
|
||||
case PNK_DELETEPROP:
|
||||
case PNK_DELETESUPERPROP:
|
||||
case PNK_DELETEELEM:
|
||||
case PNK_DELETESUPERELEM:
|
||||
// All these delete addressing modes have effects, too.
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
case PNK_DELETEEXPR:
|
||||
return checkSideEffects(pn->pn_kid, answer);
|
||||
|
||||
case PNK_TYPEOFNAME:
|
||||
case PNK_TYPEOFEXPR:
|
||||
case PNK_VOID:
|
||||
case PNK_NOT:
|
||||
case PNK_BITNOT:
|
||||
if (pn->isOp(JSOP_NOT)) {
|
||||
/* ! does not convert its operand via toString or valueOf. */
|
||||
return checkSideEffects(pn->pn_kid, answer);
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
|
||||
default:
|
||||
/*
|
||||
* All of PNK_INC, PNK_DEC and PNK_THROW have direct effects. Of
|
||||
* the remaining unary-arity node types, we can't easily prove that
|
||||
* the operand never denotes an object with a toString or valueOf
|
||||
* method.
|
||||
*/
|
||||
*answer = true;
|
||||
return true;
|
||||
}
|
||||
MOZ_CRASH("We have a returning default case");
|
||||
|
||||
case PN_NAME:
|
||||
/*
|
||||
* Take care to avoid trying to bind a label name (labels, both for
|
||||
* statements and property values in object initialisers, have pn_op
|
||||
* defaulted to JSOP_NOP).
|
||||
*/
|
||||
if (pn->isKind(PNK_NAME) && !pn->isOp(JSOP_NOP)) {
|
||||
if (!bindNameToSlot(pn))
|
||||
return false;
|
||||
if (!pn->isOp(JSOP_CALLEE) && pn->pn_cookie.isFree()) {
|
||||
/*
|
||||
* Not a use of an unshadowed named function expression's given
|
||||
* name, so this expression could invoke a getter that has side
|
||||
* effects.
|
||||
*/
|
||||
*answer = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (pn->isHoistedLexicalUse()) {
|
||||
// Hoisted uses of lexical bindings throw on access.
|
||||
*answer = true;
|
||||
}
|
||||
|
||||
if (pn->isKind(PNK_DOT)) {
|
||||
/* Dotted property references in general can call getters. */
|
||||
*answer = true;
|
||||
}
|
||||
return checkSideEffects(pn->maybeExpr(), answer);
|
||||
|
||||
case PN_NULLARY:
|
||||
if (pn->isKind(PNK_DEBUGGER) ||
|
||||
pn->isKind(PNK_SUPERPROP))
|
||||
*answer = true;
|
||||
return true;
|
||||
case PNK_LIMIT: // invalid sentinel value
|
||||
MOZ_CRASH("invalid node kind");
|
||||
}
|
||||
return true;
|
||||
|
||||
MOZ_CRASH("invalid, unenumerated ParseNodeKind value encountered in "
|
||||
"BytecodeEmitter::checkSideEffects");
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -175,7 +175,9 @@ class NameResolver
|
|||
* assign to the function's displayAtom field
|
||||
*/
|
||||
bool resolveFun(ParseNode* pn, HandleAtom prefix, MutableHandleAtom retAtom) {
|
||||
MOZ_ASSERT(pn != nullptr && pn->isKind(PNK_FUNCTION));
|
||||
MOZ_ASSERT(pn != nullptr);
|
||||
MOZ_ASSERT(pn->isKind(PNK_FUNCTION));
|
||||
MOZ_ASSERT(pn->isArity(PN_CODE));
|
||||
RootedFunction fun(cx, pn->pn_funbox->function());
|
||||
|
||||
StringBuffer buf(cx);
|
||||
|
@ -335,7 +337,8 @@ class NameResolver
|
|||
if (cur == nullptr)
|
||||
return true;
|
||||
|
||||
if (cur->isKind(PNK_FUNCTION) && cur->isArity(PN_CODE)) {
|
||||
MOZ_ASSERT(cur->isKind(PNK_FUNCTION) == cur->isArity(PN_CODE));
|
||||
if (cur->isKind(PNK_FUNCTION)) {
|
||||
RootedAtom prefix2(cx);
|
||||
if (!resolveFun(cur, prefix, &prefix2))
|
||||
return false;
|
||||
|
|
|
@ -946,10 +946,6 @@ struct NullaryNode : public ParseNode
|
|||
pn_atom = atom;
|
||||
}
|
||||
|
||||
static bool test(const ParseNode& node) {
|
||||
return node.isArity(PN_NULLARY);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void dump();
|
||||
#endif
|
||||
|
@ -963,10 +959,6 @@ struct UnaryNode : public ParseNode
|
|||
pn_kid = kid;
|
||||
}
|
||||
|
||||
static bool test(const ParseNode& node) {
|
||||
return node.isArity(PN_UNARY);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void dump(int indent);
|
||||
#endif
|
||||
|
@ -988,10 +980,6 @@ struct BinaryNode : public ParseNode
|
|||
pn_right = right;
|
||||
}
|
||||
|
||||
static bool test(const ParseNode& node) {
|
||||
return node.isArity(PN_BINARY);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void dump(int indent);
|
||||
#endif
|
||||
|
@ -1008,10 +996,6 @@ struct BinaryObjNode : public ParseNode
|
|||
pn_binary_obj = objbox;
|
||||
}
|
||||
|
||||
static bool test(const ParseNode& node) {
|
||||
return node.isArity(PN_BINARY_OBJ);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void dump(int indent);
|
||||
#endif
|
||||
|
@ -1038,10 +1022,6 @@ struct TernaryNode : public ParseNode
|
|||
pn_kid3 = kid3;
|
||||
}
|
||||
|
||||
static bool test(const ParseNode& node) {
|
||||
return node.isArity(PN_TERNARY);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void dump(int indent);
|
||||
#endif
|
||||
|
@ -1087,10 +1067,6 @@ struct CodeNode : public ParseNode
|
|||
pn_cookie.makeFree();
|
||||
}
|
||||
|
||||
static bool test(const ParseNode& node) {
|
||||
return node.isArity(PN_CODE);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void dump(int indent);
|
||||
#endif
|
||||
|
@ -1110,10 +1086,6 @@ struct NameNode : public ParseNode
|
|||
MOZ_ASSERT(pn_blockid == blockid); // check for bitfield overflow
|
||||
}
|
||||
|
||||
static bool test(const ParseNode& node) {
|
||||
return node.isArity(PN_NAME);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void dump(int indent);
|
||||
#endif
|
||||
|
|
|
@ -1,12 +1,29 @@
|
|||
// |jit-test| error: InternalError
|
||||
|
||||
// This test case creates an array, with no third element. When we iterate over
|
||||
// the third element of the array (GetElement) see that it is "undefined" and
|
||||
// start looking for __noSuchMethod__. Iterating over the protoype chain end on
|
||||
// the Proxy defined as the __proto__ of Array.
|
||||
//
|
||||
// When looking for the __noSuchMethod__ property (BaseProxyHandler::get) on the
|
||||
// proxy, the function getPropertyDescriptor is called which cause an error.
|
||||
//
|
||||
// Unfortunately, IonMonkey GetElementIC do not have supports for
|
||||
// __noSuchMethod__ (Bug 964574) at the moment, which cause a differential
|
||||
// behaviour.
|
||||
//
|
||||
// As we hope to remote __noSuchMethod__ soon (Bug 683218), we disable the mode
|
||||
// which force the selection of IonMonkey ICs.
|
||||
if (getJitCompilerOptions()["ion.forceinlineCaches"])
|
||||
setJitCompilerOption("ion.forceinlineCaches", 0);
|
||||
|
||||
Array.prototype.__proto__ = Proxy.create({
|
||||
getPropertyDescriptor: function(name) {
|
||||
return (560566);
|
||||
},
|
||||
}, null);
|
||||
function f() {}
|
||||
function g() { }
|
||||
function g() {}
|
||||
var x = [f,f,f,undefined,g];
|
||||
for (var i = 0; i < 5; ++i)
|
||||
y = x[i]("x");
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
setJitCompilerOption("ion.warmup.trigger", 50);
|
||||
for (var i = 0; i < 150; i++)
|
||||
Infinity;
|
|
@ -4,6 +4,12 @@
|
|||
if (getJitCompilerOptions()["ion.warmup.trigger"] <= 100)
|
||||
setJitCompilerOption("ion.warmup.trigger", 100);
|
||||
|
||||
// This test checks that we are able to remove the getelem & setelem with scalar
|
||||
// replacement, so we should not force inline caches, as this would skip the
|
||||
// generation of getelem & setelem instructions.
|
||||
if (getJitCompilerOptions()["ion.forceinlineCaches"])
|
||||
setJitCompilerOption("ion.forceinlineCaches", 0);
|
||||
|
||||
// This function is used to force a bailout when it is inlined, and to recover
|
||||
// the frame which is inlining this function.
|
||||
var resumeHere = function (i) { if (i >= 99) bailout(); };
|
||||
|
|
|
@ -4,6 +4,12 @@
|
|||
if (getJitCompilerOptions()["ion.warmup.trigger"] <= 90)
|
||||
setJitCompilerOption("ion.warmup.trigger", 90);
|
||||
|
||||
// This test checks that we are able to remove the getprop & setprop with scalar
|
||||
// replacement, so we should not force inline caches, as this would skip the
|
||||
// generation of getprop & setprop instructions.
|
||||
if (getJitCompilerOptions()["ion.forceinlineCaches"])
|
||||
setJitCompilerOption("ion.forceinlineCaches", 0);
|
||||
|
||||
var uceFault = function (i) {
|
||||
if (i > 98)
|
||||
uceFault = function (i) { return true; };
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
// This test checks that we are able to optimize float32 inputs. As
|
||||
// GetElementIC (float32 array accesses) output is not specialized with Float32
|
||||
// output types, we should not force inline caches.
|
||||
if (getJitCompilerOptions()["ion.forceinlineCaches"])
|
||||
setJitCompilerOption("ion.forceinlineCaches", 0);
|
||||
|
||||
// Fuzz tests
|
||||
(function(){
|
||||
//
|
||||
|
|
|
@ -7460,6 +7460,8 @@ bool
|
|||
IonBuilder::getStaticName(JSObject* staticObject, PropertyName* name, bool* psucceeded,
|
||||
MDefinition* lexicalCheck)
|
||||
{
|
||||
MOZ_ASSERT(*psucceeded == false);
|
||||
|
||||
jsid id = NameToId(name);
|
||||
|
||||
MOZ_ASSERT(staticObject->is<GlobalObject>() || staticObject->is<CallObject>());
|
||||
|
@ -7541,8 +7543,13 @@ IonBuilder::getStaticName(JSObject* staticObject, PropertyName* name, bool* psuc
|
|||
if (barrier != BarrierKind::NoBarrier)
|
||||
rvalType = MIRType_Value;
|
||||
|
||||
return loadSlot(obj, property.maybeTypes()->definiteSlot(), NumFixedSlots(staticObject),
|
||||
rvalType, barrier, types);
|
||||
if (!loadSlot(obj, property.maybeTypes()->definiteSlot(), NumFixedSlots(staticObject),
|
||||
rvalType, barrier, types)) {
|
||||
*psucceeded = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Whether a write of the given value may need a post-write barrier for GC purposes.
|
||||
|
@ -7606,19 +7613,21 @@ bool
|
|||
IonBuilder::jsop_getgname(PropertyName* name)
|
||||
{
|
||||
JSObject* obj = &script()->global();
|
||||
bool succeeded;
|
||||
if (!getStaticName(obj, name, &succeeded))
|
||||
return false;
|
||||
if (succeeded)
|
||||
return true;
|
||||
bool emitted = false;
|
||||
if (!getStaticName(obj, name, &emitted) || emitted)
|
||||
return emitted;
|
||||
|
||||
TemporaryTypeSet* types = bytecodeTypes(pc);
|
||||
MDefinition* globalObj = constant(ObjectValue(*obj));
|
||||
if (!getPropTryCommonGetter(&succeeded, globalObj, name, types))
|
||||
return false;
|
||||
if (succeeded)
|
||||
return true;
|
||||
if (MOZ_UNLIKELY(js_JitOptions.forceInlineCaches))
|
||||
goto do_InlineCache;
|
||||
|
||||
{
|
||||
TemporaryTypeSet* types = bytecodeTypes(pc);
|
||||
MDefinition* globalObj = constant(ObjectValue(*obj));
|
||||
if (!getPropTryCommonGetter(&emitted, globalObj, name, types) || emitted)
|
||||
return emitted;
|
||||
}
|
||||
|
||||
do_InlineCache:
|
||||
return jsop_getname(name);
|
||||
}
|
||||
|
||||
|
@ -7742,6 +7751,8 @@ IonBuilder::jsop_getelem()
|
|||
obj = maybeUnboxForPropertyAccess(obj);
|
||||
|
||||
bool emitted = false;
|
||||
if (MOZ_UNLIKELY(js_JitOptions.forceInlineCaches))
|
||||
goto do_InlineCache;
|
||||
|
||||
trackOptimizationAttempt(TrackedStrategy::GetElem_TypedObject);
|
||||
if (!getElemTryTypedObject(&emitted, obj, index) || emitted)
|
||||
|
@ -7771,6 +7782,7 @@ IonBuilder::jsop_getelem()
|
|||
if (!getElemTryArgumentsInlined(&emitted, obj, index) || emitted)
|
||||
return emitted;
|
||||
|
||||
do_InlineCache:
|
||||
if (script()->argumentsHasVarBinding() && obj->mightBeType(MIRType_MagicOptimizedArguments))
|
||||
return abort("Type is not definitely lazy arguments.");
|
||||
|
||||
|
@ -8798,6 +8810,9 @@ IonBuilder::jsop_setelem()
|
|||
return resumeAfter(ins);
|
||||
}
|
||||
|
||||
if (MOZ_UNLIKELY(js_JitOptions.forceInlineCaches))
|
||||
goto do_InlineCache;
|
||||
|
||||
trackOptimizationAttempt(TrackedStrategy::SetElem_TypedObject);
|
||||
if (!setElemTryTypedObject(&emitted, object, index, value) || emitted)
|
||||
return emitted;
|
||||
|
@ -8818,6 +8833,7 @@ IonBuilder::jsop_setelem()
|
|||
if (!setElemTryArguments(&emitted, object, index, value) || emitted)
|
||||
return emitted;
|
||||
|
||||
do_InlineCache:
|
||||
if (script()->argumentsHasVarBinding() &&
|
||||
object->mightBeType(MIRType_MagicOptimizedArguments) &&
|
||||
info().analysisMode() != Analysis_ArgumentsUsage)
|
||||
|
@ -10144,6 +10160,9 @@ IonBuilder::jsop_getprop(PropertyName* name)
|
|||
if (!getPropTryInnerize(&emitted, obj, name, types) || emitted)
|
||||
return emitted;
|
||||
|
||||
if (MOZ_UNLIKELY(js_JitOptions.forceInlineCaches))
|
||||
goto do_InlineCache;
|
||||
|
||||
// Try to hardcode known constants.
|
||||
trackOptimizationAttempt(TrackedStrategy::GetProp_Constant);
|
||||
if (!getPropTryConstant(&emitted, obj, name, types) || emitted)
|
||||
|
@ -10179,6 +10198,7 @@ IonBuilder::jsop_getprop(PropertyName* name)
|
|||
if (!getPropTryInlineAccess(&emitted, obj, name, barrier, types) || emitted)
|
||||
return emitted;
|
||||
|
||||
do_InlineCache:
|
||||
// Try to emit a polymorphic cache.
|
||||
trackOptimizationAttempt(TrackedStrategy::GetProp_InlineCache);
|
||||
if (!getPropTryCache(&emitted, obj, name, barrier, types) || emitted)
|
||||
|
@ -11168,6 +11188,9 @@ IonBuilder::getPropTryInnerize(bool* emitted, MDefinition* obj, PropertyName* na
|
|||
if (inner == obj)
|
||||
return true;
|
||||
|
||||
if (MOZ_UNLIKELY(js_JitOptions.forceInlineCaches))
|
||||
goto do_InlineCache;
|
||||
|
||||
// Note: the Baseline ICs don't know about this optimization, so it's
|
||||
// possible the global property's HeapTypeSet has not been initialized
|
||||
// yet. In this case we'll fall back to getPropTryCache for now.
|
||||
|
@ -11189,6 +11212,7 @@ IonBuilder::getPropTryInnerize(bool* emitted, MDefinition* obj, PropertyName* na
|
|||
if (!getPropTryCommonGetter(emitted, inner, name, types) || *emitted)
|
||||
return *emitted;
|
||||
|
||||
do_InlineCache:
|
||||
// Passing the inner object to GetProperty IC is safe, see the
|
||||
// needsOuterizedThisObject check in IsCacheableGetPropCallNative.
|
||||
BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
|
||||
|
@ -11222,6 +11246,9 @@ IonBuilder::jsop_setprop(PropertyName* name)
|
|||
return resumeAfter(ins);
|
||||
}
|
||||
|
||||
if (MOZ_UNLIKELY(js_JitOptions.forceInlineCaches))
|
||||
goto do_InlineCache_step1;
|
||||
|
||||
// Try to inline a common property setter, or make a call.
|
||||
trackOptimizationAttempt(TrackedStrategy::SetProp_CommonSetter);
|
||||
if (!setPropTryCommonSetter(&emitted, obj, name, value) || emitted)
|
||||
|
@ -11232,20 +11259,28 @@ IonBuilder::jsop_setprop(PropertyName* name)
|
|||
if (!setPropTryTypedObject(&emitted, obj, name, value) || emitted)
|
||||
return emitted;
|
||||
|
||||
do_InlineCache_step1:
|
||||
TemporaryTypeSet* objTypes = obj->resultTypeSet();
|
||||
bool barrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, name, &value,
|
||||
/* canModify = */ true);
|
||||
|
||||
if (MOZ_UNLIKELY(js_JitOptions.forceInlineCaches))
|
||||
goto do_InlineCache_step2;
|
||||
|
||||
// Try to emit stores to unboxed objects.
|
||||
trackOptimizationAttempt(TrackedStrategy::SetProp_Unboxed);
|
||||
if (!setPropTryUnboxed(&emitted, obj, name, value, barrier, objTypes) || emitted)
|
||||
return emitted;
|
||||
|
||||
do_InlineCache_step2:
|
||||
// Add post barrier if needed. The instructions above manage any post
|
||||
// barriers they need directly.
|
||||
if (NeedsPostBarrier(info(), value))
|
||||
current->add(MPostWriteBarrier::New(alloc(), obj, value));
|
||||
|
||||
if (MOZ_UNLIKELY(js_JitOptions.forceInlineCaches))
|
||||
goto do_InlineCache_step3;
|
||||
|
||||
// Try to emit store from definite slots.
|
||||
trackOptimizationAttempt(TrackedStrategy::SetProp_DefiniteSlot);
|
||||
if (!setPropTryDefiniteSlot(&emitted, obj, name, value, barrier, objTypes) || emitted)
|
||||
|
@ -11256,6 +11291,7 @@ IonBuilder::jsop_setprop(PropertyName* name)
|
|||
if (!setPropTryInlineAccess(&emitted, obj, name, value, barrier, objTypes) || emitted)
|
||||
return emitted;
|
||||
|
||||
do_InlineCache_step3:
|
||||
// Emit a polymorphic cache.
|
||||
trackOptimizationAttempt(TrackedStrategy::SetProp_InlineCache);
|
||||
return setPropTryCache(&emitted, obj, name, value, barrier, objTypes);
|
||||
|
@ -12288,11 +12324,9 @@ IonBuilder::jsop_getaliasedvar(ScopeCoordinate sc)
|
|||
JSObject* call = nullptr;
|
||||
if (hasStaticScopeObject(sc, &call) && call) {
|
||||
PropertyName* name = ScopeCoordinateName(scopeCoordinateNameCache, script(), pc);
|
||||
bool succeeded;
|
||||
if (!getStaticName(call, name, &succeeded, takeLexicalCheck()))
|
||||
return false;
|
||||
if (succeeded)
|
||||
return true;
|
||||
bool emitted = false;
|
||||
if (!getStaticName(call, name, &emitted, takeLexicalCheck()) || emitted)
|
||||
return emitted;
|
||||
}
|
||||
|
||||
// See jsop_checkaliasedlet.
|
||||
|
|
|
@ -113,6 +113,9 @@ JitOptions::JitOptions()
|
|||
// Whether functions are compiled immediately.
|
||||
SET_DEFAULT(eagerCompilation, false);
|
||||
|
||||
// Whether IonBuilder should prefer IC generation above specialized MIR.
|
||||
SET_DEFAULT(forceInlineCaches, false);
|
||||
|
||||
// Force how many invocation or loop iterations are needed before compiling
|
||||
// a function with the highest ionmonkey optimization level.
|
||||
// (i.e. OptimizationLevel_Normal)
|
||||
|
|
|
@ -56,6 +56,7 @@ struct JitOptions
|
|||
bool disableEaa;
|
||||
bool disableAma;
|
||||
bool eagerCompilation;
|
||||
bool forceInlineCaches;
|
||||
mozilla::Maybe<uint32_t> forcedDefaultIonWarmUpThreshold;
|
||||
mozilla::Maybe<IonRegisterAllocator> forcedRegisterAllocator;
|
||||
bool limitScriptSize;
|
||||
|
|
|
@ -5699,6 +5699,15 @@ JS_SetGlobalJitCompilerOption(JSRuntime* rt, JSJitCompilerOption opt, uint32_t v
|
|||
JitSpew(js::jit::JitSpew_IonScripts, "Enable ion's GVN");
|
||||
}
|
||||
break;
|
||||
case JSJITCOMPILER_ION_FORCE_IC:
|
||||
if (value == 0) {
|
||||
jit::js_JitOptions.forceInlineCaches = false;
|
||||
JitSpew(js::jit::JitSpew_IonScripts, "IonBuilder: Enable non-IC optimizations.");
|
||||
} else {
|
||||
jit::js_JitOptions.forceInlineCaches = true;
|
||||
JitSpew(js::jit::JitSpew_IonScripts, "IonBuilder: Disable non-IC optimizations.");
|
||||
}
|
||||
break;
|
||||
case JSJITCOMPILER_ION_ENABLE:
|
||||
if (value == 1) {
|
||||
JS::RuntimeOptionsRef(rt).setIon(true);
|
||||
|
@ -5753,6 +5762,8 @@ JS_GetGlobalJitCompilerOption(JSRuntime* rt, JSJitCompilerOption opt)
|
|||
return jit::js_JitOptions.forcedDefaultIonWarmUpThreshold.isSome()
|
||||
? jit::js_JitOptions.forcedDefaultIonWarmUpThreshold.ref()
|
||||
: jit::OptimizationInfo::CompilerWarmupThreshold;
|
||||
case JSJITCOMPILER_ION_FORCE_IC:
|
||||
return jit::js_JitOptions.forceInlineCaches;
|
||||
case JSJITCOMPILER_ION_ENABLE:
|
||||
return JS::RuntimeOptionsRef(rt).ion();
|
||||
case JSJITCOMPILER_BASELINE_ENABLE:
|
||||
|
|
|
@ -4927,6 +4927,7 @@ JS_SetOffthreadIonCompilationEnabled(JSRuntime* rt, bool enabled);
|
|||
Register(BASELINE_WARMUP_TRIGGER, "baseline.warmup.trigger") \
|
||||
Register(ION_WARMUP_TRIGGER, "ion.warmup.trigger") \
|
||||
Register(ION_GVN_ENABLE, "ion.gvn.enable") \
|
||||
Register(ION_FORCE_IC, "ion.forceinlineCaches") \
|
||||
Register(ION_ENABLE, "ion.enable") \
|
||||
Register(BASELINE_ENABLE, "baseline.enable") \
|
||||
Register(OFFTHREAD_COMPILATION_ENABLE, "offthread-compilation.enable") \
|
||||
|
|
|
@ -7350,7 +7350,7 @@ NewMemoryInfoObject(JSContext* cx)
|
|||
RootedObject obj(cx, JS_NewObject(cx, nullptr));
|
||||
|
||||
using namespace MemInfo;
|
||||
struct {
|
||||
struct NamedGetter {
|
||||
const char* name;
|
||||
JSNative getter;
|
||||
} getters[] = {
|
||||
|
@ -7364,13 +7364,13 @@ NewMemoryInfoObject(JSContext* cx)
|
|||
{ "minorGCCount", MinorGCCountGetter }
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < mozilla::ArrayLength(getters); i++) {
|
||||
#ifdef JS_MORE_DETERMINISTIC
|
||||
for (auto pair : getters) {
|
||||
#ifdef JS_MORE_DETERMINISTIC
|
||||
JSNative getter = DummyGetter;
|
||||
#else
|
||||
JSNative getter = getters[i].getter;
|
||||
JSNative getter = pair.getter;
|
||||
#endif
|
||||
if (!JS_DefineProperty(cx, obj, getters[i].name, UndefinedHandleValue,
|
||||
if (!JS_DefineProperty(cx, obj, pair.name, UndefinedHandleValue,
|
||||
JSPROP_ENUMERATE | JSPROP_SHARED,
|
||||
getter, nullptr))
|
||||
{
|
||||
|
@ -7385,7 +7385,7 @@ NewMemoryInfoObject(JSContext* cx)
|
|||
if (!JS_DefineProperty(cx, obj, "zone", zoneObj, JSPROP_ENUMERATE))
|
||||
return nullptr;
|
||||
|
||||
struct {
|
||||
struct NamedZoneGetter {
|
||||
const char* name;
|
||||
JSNative getter;
|
||||
} zoneGetters[] = {
|
||||
|
@ -7399,13 +7399,13 @@ NewMemoryInfoObject(JSContext* cx)
|
|||
{ "gcNumber", ZoneGCNumberGetter }
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < mozilla::ArrayLength(zoneGetters); i++) {
|
||||
for (auto pair : zoneGetters) {
|
||||
#ifdef JS_MORE_DETERMINISTIC
|
||||
JSNative getter = DummyGetter;
|
||||
#else
|
||||
JSNative getter = zoneGetters[i].getter;
|
||||
JSNative getter = pair.getter;
|
||||
#endif
|
||||
if (!JS_DefineProperty(cx, zoneObj, zoneGetters[i].name, UndefinedHandleValue,
|
||||
if (!JS_DefineProperty(cx, zoneObj, pair.name, UndefinedHandleValue,
|
||||
JSPROP_ENUMERATE | JSPROP_SHARED,
|
||||
getter, nullptr))
|
||||
{
|
||||
|
|
|
@ -3125,14 +3125,9 @@ nsDisplayLayerEventRegions::AddFrame(nsDisplayListBuilder* aBuilder,
|
|||
}
|
||||
if (!aFrame->GetParent()) {
|
||||
MOZ_ASSERT(aFrame->GetType() == nsGkAtoms::viewportFrame);
|
||||
nsSubDocumentFrame* subdoc = static_cast<nsSubDocumentFrame*>(
|
||||
nsLayoutUtils::GetCrossDocParentFrame(aFrame));
|
||||
if (subdoc && subdoc->PassPointerEventsToChildren()) {
|
||||
// If this viewport frame is for a subdocument with
|
||||
// mozpasspointerevents, then we don't want to add the viewport itself
|
||||
// to the event regions. Instead we want to add only subframes.
|
||||
return;
|
||||
}
|
||||
// Viewport frames are never event targets, other frames, like canvas frames,
|
||||
// are the event targets for any regions viewport frames may cover.
|
||||
return;
|
||||
}
|
||||
uint8_t pointerEvents = aFrame->StyleVisibility()->GetEffectivePointerEvents(aFrame);
|
||||
if (pointerEvents == NS_STYLE_POINTER_EVENTS_NONE) {
|
||||
|
|
|
@ -260,6 +260,6 @@ support-files = bug1080361_inner.html
|
|||
[test_bug1093686.html]
|
||||
support-files = bug1093686_inner.html
|
||||
[test_bug1120705.html]
|
||||
skip-if = buildapp == 'android' || buildapp == 'b2g' || buildapp == 'b2g-debug' || os == 'mac' # android and b2g do not have clickable scrollbars, mac does not have scrollbar down and up buttons
|
||||
skip-if = buildapp == 'android' || buildapp == 'b2g' || buildapp == 'b2g-debug' || os == 'mac' || toolkit == 'gtk2' || toolkit == 'gtk3' # android and b2g do not have clickable scrollbars, mac does not have scrollbar down and up buttons, gtk may or may not have scrollbar buttons depending on theme
|
||||
[test_bug1153130.html]
|
||||
support-files = bug1153130_inner.html
|
||||
|
|
|
@ -136,7 +136,7 @@ var testCases = [
|
|||
"mouseOffset" : { "x" : 0, "y" : 0 },
|
||||
"duration" : "0",
|
||||
"runMac" : false, // OSX does not have have line-scroll buttons
|
||||
"runGtk" : true,
|
||||
"runGtk" : false, // Some GTK themes may not have scroll buttons
|
||||
"runWin" : true
|
||||
},
|
||||
{
|
||||
|
@ -148,7 +148,7 @@ var testCases = [
|
|||
"mouseOffset" : { "x" : 0, "y" : 0 },
|
||||
"duration" : "500",
|
||||
"runMac" : false, // OSX does not have have line-scroll buttons
|
||||
"runGtk" : true,
|
||||
"runGtk" : false, // Some GTK themes may not have scroll buttons
|
||||
"runWin" : true
|
||||
},
|
||||
{
|
||||
|
@ -160,7 +160,7 @@ var testCases = [
|
|||
"mouseOffset" : { "x" : 0, "y" : 0 },
|
||||
"duration" : "0",
|
||||
"runMac" : false, // OSX does not have have line-scroll buttons
|
||||
"runGtk" : true,
|
||||
"runGtk" : false, // Some GTK themes may not have scroll buttons
|
||||
"runWin" : true
|
||||
},
|
||||
{
|
||||
|
@ -172,7 +172,7 @@ var testCases = [
|
|||
"mouseOffset" : { "x" : 0, "y" : 0 },
|
||||
"duration" : "500",
|
||||
"runMac" : false, // OSX does not have have line-scroll buttons
|
||||
"runGtk" : true,
|
||||
"runGtk" : false, // Some GTK themes may not have scroll buttons
|
||||
"runWin" : true
|
||||
},
|
||||
{
|
||||
|
@ -184,7 +184,7 @@ var testCases = [
|
|||
"mouseOffset" : { "x" : 0, "y" : 0 },
|
||||
"duration" : "0",
|
||||
"runMac" : false, // OSX does not have have line-scroll buttons
|
||||
"runGtk" : true,
|
||||
"runGtk" : false, // Some GTK themes may not have scroll buttons
|
||||
"runWin" : true
|
||||
},
|
||||
{
|
||||
|
@ -196,7 +196,7 @@ var testCases = [
|
|||
"mouseOffset" : { "x" : 0, "y" : 0 },
|
||||
"duration" : "500",
|
||||
"runMac" : false, // OSX does not have have line-scroll buttons
|
||||
"runGtk" : true,
|
||||
"runGtk" : false, // Some GTK themes may not have scroll buttons
|
||||
"runWin" : true
|
||||
},
|
||||
{
|
||||
|
@ -208,7 +208,7 @@ var testCases = [
|
|||
"mouseOffset" : { "x" : 0, "y" : 0 },
|
||||
"duration" : "0",
|
||||
"runMac" : false, // OSX does not have have line-scroll buttons
|
||||
"runGtk" : true,
|
||||
"runGtk" : false, // Some GTK themes may not have scroll buttons
|
||||
"runWin" : true
|
||||
},
|
||||
{
|
||||
|
@ -220,7 +220,7 @@ var testCases = [
|
|||
"mouseOffset" : { "x" : 0, "y" : 0 },
|
||||
"duration" : "500",
|
||||
"runMac" : false, // OSX does not have have line-scroll buttons
|
||||
"runGtk" : true,
|
||||
"runGtk" : false, // Some GTK themes may not have scroll buttons
|
||||
"runWin" : true
|
||||
},
|
||||
|
||||
|
|
|
@ -5359,6 +5359,7 @@ nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
|
|||
if (line->Contains(child)) {
|
||||
*aFoundValidLine = true;
|
||||
mLine = line;
|
||||
aFrame->SetLineCursor(line.get());
|
||||
return;
|
||||
}
|
||||
++line;
|
||||
|
@ -5367,6 +5368,7 @@ nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
|
|||
if (rline->Contains(child)) {
|
||||
*aFoundValidLine = true;
|
||||
mLine = rline;
|
||||
aFrame->SetLineCursor(rline.get());
|
||||
return;
|
||||
}
|
||||
++rline;
|
||||
|
@ -6524,9 +6526,8 @@ void nsBlockFrame::SetupLineCursor()
|
|||
|| mLines.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Properties().Set(LineCursorProperty(), mLines.front());
|
||||
AddStateBits(NS_BLOCK_HAS_LINE_CURSOR);
|
||||
|
||||
SetLineCursor(mLines.front());
|
||||
}
|
||||
|
||||
nsLineBox* nsBlockFrame::GetFirstLineContaining(nscoord y)
|
||||
|
@ -6554,7 +6555,7 @@ nsLineBox* nsBlockFrame::GetFirstLineContaining(nscoord y)
|
|||
}
|
||||
|
||||
if (cursor.get() != property) {
|
||||
props.Set(LineCursorProperty(), cursor.get());
|
||||
SetLineCursor(cursor.get());
|
||||
}
|
||||
|
||||
return cursor.get();
|
||||
|
|
|
@ -377,6 +377,10 @@ protected:
|
|||
return (GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR) ?
|
||||
static_cast<nsLineBox*>(Properties().Get(LineCursorProperty())) : nullptr;
|
||||
}
|
||||
void SetLineCursor(nsLineBox* aCursor) {
|
||||
Properties().Set(LineCursorProperty(), aCursor);
|
||||
AddStateBits(NS_BLOCK_HAS_LINE_CURSOR);
|
||||
}
|
||||
|
||||
nsLineBox* NewLineBox(nsIFrame* aFrame, bool aIsBlock) {
|
||||
return NS_NewLineBox(PresContext()->PresShell(), aFrame, aIsBlock);
|
||||
|
|
|
@ -237,6 +237,7 @@ public:
|
|||
RefPtr(const RefPtr& aOther) : mPtr(ref(aOther.mPtr)) {}
|
||||
MOZ_IMPLICIT RefPtr(const TemporaryRef<T>& aOther) : mPtr(aOther.take()) {}
|
||||
MOZ_IMPLICIT RefPtr(already_AddRefed<T>& aOther) : mPtr(aOther.take()) {}
|
||||
MOZ_IMPLICIT RefPtr(already_AddRefed<T>&& aOther) : mPtr(aOther.take()) {}
|
||||
MOZ_IMPLICIT RefPtr(T* aVal) : mPtr(ref(aVal)) {}
|
||||
|
||||
template<typename U>
|
||||
|
@ -259,6 +260,11 @@ public:
|
|||
assign(aOther.take());
|
||||
return *this;
|
||||
}
|
||||
RefPtr& operator=(already_AddRefed<T>&& aOther)
|
||||
{
|
||||
assign(aOther.take());
|
||||
return *this;
|
||||
}
|
||||
RefPtr& operator=(T* aVal)
|
||||
{
|
||||
assign(ref(aVal));
|
||||
|
|
|
@ -474,6 +474,9 @@ pref("security.mixed_content.block_active_content", true);
|
|||
// Enable pinning
|
||||
pref("security.cert_pinning.enforcement_level", 1);
|
||||
|
||||
// Only fetch OCSP for EV certificates
|
||||
pref("security.OCSP.enabled", 2);
|
||||
|
||||
// Override some named colors to avoid inverse OS themes
|
||||
pref("ui.-moz-dialog", "#efebe7");
|
||||
pref("ui.-moz-dialogtext", "#101010");
|
||||
|
|
|
@ -571,9 +571,8 @@ pref("apz.test.logging_enabled", false);
|
|||
pref("gfx.hidpi.enabled", 2);
|
||||
#endif
|
||||
|
||||
#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID)
|
||||
// Containerless scrolling for root frames does not yet pass tests on Android
|
||||
// or B2G.
|
||||
#if !defined(MOZ_WIDGET_ANDROID)
|
||||
// Containerless scrolling for root frames does not yet pass tests on Android.
|
||||
pref("layout.scroll.root-frame-containers", false);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -175,6 +175,9 @@ static DllBlockInfo sWindowsDllBlocklist[] = {
|
|||
// Old version of WebcamMax crashes WebRTC, bug 1130061
|
||||
{ "vwcsource.ax", MAKE_VERSION(1, 5, 0, 0) },
|
||||
|
||||
// NetOp School, discontinued product, bug 763395
|
||||
{ "nlsp.dll", MAKE_VERSION(6, 23, 2012, 19) },
|
||||
|
||||
{ nullptr, 0 }
|
||||
};
|
||||
|
||||
|
|
|
@ -5001,12 +5001,17 @@ nsHttpChannel::BeginConnect()
|
|||
mLoadFlags |= LOAD_FROM_CACHE;
|
||||
mLoadFlags &= ~VALIDATE_ALWAYS;
|
||||
nsCOMPtr<nsIPackagedAppService> pas =
|
||||
do_GetService("@mozilla.org/network/packaged-app-service;1", &rv);
|
||||
do_GetService("@mozilla.org/network/packaged-app-service;1", &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
AsyncAbort(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
return pas->RequestURI(mURI, GetLoadContextInfo(this), this);
|
||||
rv = pas->RequestURI(mURI, GetLoadContextInfo(this), this);
|
||||
if (NS_FAILED(rv)) {
|
||||
AsyncAbort(rv);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
// when proxying only use the pipeline bit if ProxyPipelining() allows it.
|
||||
|
|
|
@ -34,7 +34,7 @@ CertVerifier::CertVerifier(OcspDownloadConfig odc,
|
|||
OcspGetConfig ogc,
|
||||
uint32_t certShortLifetimeInDays,
|
||||
PinningMode pinningMode)
|
||||
: mOCSPDownloadEnabled(odc == ocspOn)
|
||||
: mOCSPDownloadConfig(odc)
|
||||
, mOCSPStrict(osc == ocspStrict)
|
||||
, mOCSPGETEnabled(ogc == ocspGetEnabled)
|
||||
, mCertShortLifetimeInDays(certShortLifetimeInDays)
|
||||
|
@ -168,8 +168,11 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
|
|||
return SECFailure;
|
||||
}
|
||||
|
||||
NSSCertDBTrustDomain::OCSPFetching ocspFetching
|
||||
= !mOCSPDownloadEnabled ||
|
||||
// We configure the OCSP fetching modes separately for EV and non-EV
|
||||
// verifications.
|
||||
NSSCertDBTrustDomain::OCSPFetching defaultOCSPFetching
|
||||
= (mOCSPDownloadConfig == ocspOff) ||
|
||||
(mOCSPDownloadConfig == ocspEVOnly) ||
|
||||
(flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::NeverFetchOCSP
|
||||
: !mOCSPStrict ? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail
|
||||
: NSSCertDBTrustDomain::FetchOCSPForDVHardFail;
|
||||
|
@ -194,7 +197,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
|
|||
case certificateUsageSSLClient: {
|
||||
// XXX: We don't really have a trust bit for SSL client authentication so
|
||||
// just use trustEmail as it is the closest alternative.
|
||||
NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
|
||||
NSSCertDBTrustDomain trustDomain(trustEmail, defaultOCSPFetching, mOCSPCache,
|
||||
pinArg, ocspGETConfig,
|
||||
mCertShortLifetimeInDays,
|
||||
pinningDisabled,
|
||||
|
@ -214,15 +217,17 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
|
|||
|
||||
#ifndef MOZ_NO_EV_CERTS
|
||||
// Try to validate for EV first.
|
||||
NSSCertDBTrustDomain::OCSPFetching evOCSPFetching
|
||||
= (mOCSPDownloadConfig == ocspOff) ||
|
||||
(flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
|
||||
: NSSCertDBTrustDomain::FetchOCSPForEV;
|
||||
|
||||
CertPolicyId evPolicy;
|
||||
SECOidTag evPolicyOidTag;
|
||||
SECStatus srv = GetFirstEVPolicy(cert, evPolicy, evPolicyOidTag);
|
||||
if (srv == SECSuccess) {
|
||||
NSSCertDBTrustDomain
|
||||
trustDomain(trustSSL,
|
||||
ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP
|
||||
? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
|
||||
: NSSCertDBTrustDomain::FetchOCSPForEV,
|
||||
trustDomain(trustSSL, evOCSPFetching,
|
||||
mOCSPCache, pinArg, ocspGETConfig,
|
||||
mCertShortLifetimeInDays, mPinningMode, MIN_RSA_BITS,
|
||||
hostname, builtChain);
|
||||
|
@ -248,8 +253,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
|
|||
}
|
||||
|
||||
// Now try non-EV.
|
||||
NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
|
||||
pinArg, ocspGETConfig,
|
||||
NSSCertDBTrustDomain trustDomain(trustSSL, defaultOCSPFetching,
|
||||
mOCSPCache, pinArg, ocspGETConfig,
|
||||
mCertShortLifetimeInDays, mPinningMode,
|
||||
MIN_RSA_BITS, hostname, builtChain);
|
||||
rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time,
|
||||
|
@ -268,8 +273,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
|
|||
}
|
||||
|
||||
// If that failed, try again with a smaller minimum key size.
|
||||
NSSCertDBTrustDomain trustDomainWeak(trustSSL, ocspFetching, mOCSPCache,
|
||||
pinArg, ocspGETConfig,
|
||||
NSSCertDBTrustDomain trustDomainWeak(trustSSL, defaultOCSPFetching,
|
||||
mOCSPCache, pinArg, ocspGETConfig,
|
||||
mCertShortLifetimeInDays,
|
||||
mPinningMode, MIN_RSA_BITS_WEAK,
|
||||
hostname, builtChain);
|
||||
|
@ -293,8 +298,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
|
|||
}
|
||||
|
||||
case certificateUsageSSLCA: {
|
||||
NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
|
||||
pinArg, ocspGETConfig,
|
||||
NSSCertDBTrustDomain trustDomain(trustSSL, defaultOCSPFetching,
|
||||
mOCSPCache, pinArg, ocspGETConfig,
|
||||
mCertShortLifetimeInDays,
|
||||
pinningDisabled, MIN_RSA_BITS_WEAK,
|
||||
nullptr, builtChain);
|
||||
|
@ -306,8 +311,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
|
|||
}
|
||||
|
||||
case certificateUsageEmailSigner: {
|
||||
NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
|
||||
pinArg, ocspGETConfig,
|
||||
NSSCertDBTrustDomain trustDomain(trustEmail, defaultOCSPFetching,
|
||||
mOCSPCache, pinArg, ocspGETConfig,
|
||||
mCertShortLifetimeInDays,
|
||||
pinningDisabled, MIN_RSA_BITS_WEAK,
|
||||
nullptr, builtChain);
|
||||
|
@ -330,8 +335,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
|
|||
// TODO: The higher level S/MIME processing should pass in which key
|
||||
// usage it is trying to verify for, and base its algorithm choices
|
||||
// based on the result of the verification(s).
|
||||
NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
|
||||
pinArg, ocspGETConfig,
|
||||
NSSCertDBTrustDomain trustDomain(trustEmail, defaultOCSPFetching,
|
||||
mOCSPCache, pinArg, ocspGETConfig,
|
||||
mCertShortLifetimeInDays,
|
||||
pinningDisabled, MIN_RSA_BITS_WEAK,
|
||||
nullptr, builtChain);
|
||||
|
@ -351,7 +356,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
|
|||
}
|
||||
|
||||
case certificateUsageObjectSigner: {
|
||||
NSSCertDBTrustDomain trustDomain(trustObjectSigning, ocspFetching,
|
||||
NSSCertDBTrustDomain trustDomain(trustObjectSigning, defaultOCSPFetching,
|
||||
mOCSPCache, pinArg, ocspGETConfig,
|
||||
mCertShortLifetimeInDays,
|
||||
pinningDisabled, MIN_RSA_BITS_WEAK,
|
||||
|
@ -382,16 +387,16 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
|
|||
eku = KeyPurposeId::id_kp_OCSPSigning;
|
||||
}
|
||||
|
||||
NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, mOCSPCache, pinArg,
|
||||
ocspGETConfig, mCertShortLifetimeInDays,
|
||||
NSSCertDBTrustDomain sslTrust(trustSSL, defaultOCSPFetching, mOCSPCache,
|
||||
pinArg, ocspGETConfig, mCertShortLifetimeInDays,
|
||||
pinningDisabled, MIN_RSA_BITS_WEAK,
|
||||
nullptr, builtChain);
|
||||
rv = BuildCertChain(sslTrust, certDER, time, endEntityOrCA,
|
||||
keyUsage, eku, CertPolicyId::anyPolicy,
|
||||
stapledOCSPResponse);
|
||||
if (rv == Result::ERROR_UNKNOWN_ISSUER) {
|
||||
NSSCertDBTrustDomain emailTrust(trustEmail, ocspFetching, mOCSPCache,
|
||||
pinArg, ocspGETConfig,
|
||||
NSSCertDBTrustDomain emailTrust(trustEmail, defaultOCSPFetching,
|
||||
mOCSPCache, pinArg, ocspGETConfig,
|
||||
mCertShortLifetimeInDays,
|
||||
pinningDisabled, MIN_RSA_BITS_WEAK,
|
||||
nullptr, builtChain);
|
||||
|
@ -400,7 +405,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
|
|||
stapledOCSPResponse);
|
||||
if (rv == Result::ERROR_UNKNOWN_ISSUER) {
|
||||
NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
|
||||
ocspFetching, mOCSPCache,
|
||||
defaultOCSPFetching, mOCSPCache,
|
||||
pinArg, ocspGETConfig,
|
||||
mCertShortLifetimeInDays,
|
||||
pinningDisabled,
|
||||
|
|
|
@ -75,12 +75,14 @@ public:
|
|||
pinningEnforceTestMode = 3
|
||||
};
|
||||
|
||||
enum OcspDownloadConfig { ocspOff = 0, ocspOn };
|
||||
enum OcspDownloadConfig {
|
||||
ocspOff = 0,
|
||||
ocspOn = 1,
|
||||
ocspEVOnly = 2
|
||||
};
|
||||
enum OcspStrictConfig { ocspRelaxed = 0, ocspStrict };
|
||||
enum OcspGetConfig { ocspGetDisabled = 0, ocspGetEnabled = 1 };
|
||||
|
||||
bool IsOCSPDownloadEnabled() const { return mOCSPDownloadEnabled; }
|
||||
|
||||
CertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc,
|
||||
OcspGetConfig ogc, uint32_t certShortLifetimeInDays,
|
||||
PinningMode pinningMode);
|
||||
|
@ -88,7 +90,7 @@ public:
|
|||
|
||||
void ClearOCSPCache() { mOCSPCache.Clear(); }
|
||||
|
||||
const bool mOCSPDownloadEnabled;
|
||||
const OcspDownloadConfig mOCSPDownloadConfig;
|
||||
const bool mOCSPStrict;
|
||||
const bool mOCSPGETEnabled;
|
||||
const uint32_t mCertShortLifetimeInDays;
|
||||
|
|
|
@ -193,10 +193,15 @@ GetRevocationBehaviorFromPrefs(/*out*/ CertVerifier::OcspDownloadConfig* odc,
|
|||
MOZ_ASSERT(ogc);
|
||||
MOZ_ASSERT(certShortLifetimeInDays);
|
||||
|
||||
// 0 = disabled, otherwise enabled
|
||||
*odc = Preferences::GetInt("security.OCSP.enabled", 1)
|
||||
? CertVerifier::ocspOn
|
||||
: CertVerifier::ocspOff;
|
||||
// 0 = disabled
|
||||
// 1 = enabled for everything (default)
|
||||
// 2 = enabled for EV certificates only
|
||||
int32_t ocspLevel = Preferences::GetInt("security.OCSP.enabled", 1);
|
||||
switch (ocspLevel) {
|
||||
case 0: *odc = CertVerifier::ocspOff; break;
|
||||
case 2: *odc = CertVerifier::ocspEVOnly; break;
|
||||
default: *odc = CertVerifier::ocspOn; break;
|
||||
}
|
||||
|
||||
*osc = Preferences::GetBool("security.OCSP.require", false)
|
||||
? CertVerifier::ocspStrict
|
||||
|
@ -266,6 +271,8 @@ nsNSSComponent::createBackgroundThreads()
|
|||
nsNSSComponent::~nsNSSComponent()
|
||||
{
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsNSSComponent::dtor\n"));
|
||||
NS_ASSERTION(!mCertVerificationThread,
|
||||
"Cert verification thread should have been cleaned up.");
|
||||
|
||||
deleteBackgroundThreads();
|
||||
|
||||
|
@ -1326,6 +1333,8 @@ nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
bec->DontForward();
|
||||
}
|
||||
}
|
||||
|
||||
deleteBackgroundThreads();
|
||||
}
|
||||
else if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
|
|
|
@ -81,6 +81,7 @@ function check_telemetry() {
|
|||
}
|
||||
|
||||
function run_test() {
|
||||
Services.prefs.setIntPref("security.OCSP.enabled", 1);
|
||||
add_tls_server_setup("BadCertServer");
|
||||
|
||||
let fakeOCSPResponder = new HttpServer();
|
||||
|
|
|
@ -68,6 +68,7 @@ function run_test() {
|
|||
// setup and start ocsp responder
|
||||
Services.prefs.setCharPref("network.dns.localDomains",
|
||||
'www.example.com, crl.example.com');
|
||||
Services.prefs.setIntPref("security.OCSP.enabled", 1);
|
||||
|
||||
add_test(function () {
|
||||
clearOCSPCache();
|
||||
|
|
|
@ -132,6 +132,7 @@ function checkRSAChains(inadequateKeySize, adequateKeySize) {
|
|||
|
||||
function run_test() {
|
||||
Services.prefs.setCharPref("network.dns.localDomains", "www.example.com");
|
||||
Services.prefs.setIntPref("security.OCSP.enabled", 1);
|
||||
|
||||
checkRSAChains(2040, 2048);
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ function generateGoodOCSPResponse() {
|
|||
function run_test() {
|
||||
do_get_profile();
|
||||
Services.prefs.setBoolPref("security.ssl.enable_ocsp_stapling", true);
|
||||
Services.prefs.setIntPref("security.OCSP.enabled", 1);
|
||||
add_tls_server_setup("OCSPStaplingServer");
|
||||
|
||||
let ocspResponder = new HttpServer();
|
||||
|
|
|
@ -37,6 +37,7 @@ function run_test() {
|
|||
|
||||
Services.prefs.setCharPref("network.dns.localDomains",
|
||||
"www.example.com");
|
||||
Services.prefs.setIntPref("security.OCSP.enabled", 1);
|
||||
|
||||
add_test(function() {
|
||||
clearOCSPCache();
|
||||
|
|
|
@ -14,6 +14,7 @@ let gOCSPRequestCount = 0;
|
|||
function run_test() {
|
||||
do_get_profile();
|
||||
Services.prefs.setBoolPref("security.OCSP.require", true);
|
||||
Services.prefs.setIntPref("security.OCSP.enabled", 1);
|
||||
|
||||
// We don't actually make use of stapling in this test. This is just how we
|
||||
// get a TLS connection.
|
||||
|
|
|
@ -27,6 +27,7 @@ function add_ocsp_test(aHost, aExpectedResult, aOCSPResponseToServe) {
|
|||
|
||||
do_get_profile();
|
||||
Services.prefs.setBoolPref("security.ssl.enable_ocsp_stapling", true);
|
||||
Services.prefs.setIntPref("security.OCSP.enabled", 1);
|
||||
let args = [["good", "localhostAndExampleCom", "unused"],
|
||||
["expiredresponse", "localhostAndExampleCom", "unused"],
|
||||
["oldvalidperiod", "localhostAndExampleCom", "unused"],
|
||||
|
|
|
@ -26,6 +26,7 @@ let gSocketListener = {
|
|||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
Services.prefs.setIntPref("security.OCSP.enabled", 1);
|
||||
|
||||
add_tls_server_setup("OCSPStaplingServer");
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ function run_test() {
|
|||
|
||||
Services.prefs.setCharPref("network.dns.localDomains",
|
||||
"www.example.com");
|
||||
Services.prefs.setIntPref("security.OCSP.enabled", 1);
|
||||
|
||||
add_test(function() {
|
||||
clearOCSPCache();
|
||||
|
|
|
@ -22,6 +22,7 @@ import mozdebug
|
|||
import mozinfo
|
||||
import mozprocess
|
||||
import mozrunner
|
||||
import numbers
|
||||
import re
|
||||
import shutil
|
||||
import signal
|
||||
|
@ -2551,8 +2552,9 @@ class Mochitest(MochitestUtilsMixin):
|
|||
"MOZ_HIDE_RESULTS_TABLE"] == "1":
|
||||
options.hideResultsTable = True
|
||||
|
||||
d = dict((k, v) for k, v in options.__dict__.items() if (not k.startswith(
|
||||
'log_') or not any([k.endswith(fmt) for fmt in commandline.log_formatters.keys()])))
|
||||
# strip certain unnecessary items to avoid serialization errors in json.dumps()
|
||||
d = dict((k, v) for k, v in options.__dict__.items() if (v is None) or
|
||||
isinstance(v,(basestring,numbers.Number)))
|
||||
d['testRoot'] = self.testRoot
|
||||
content = json.dumps(d)
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
/* 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 "BreakpadLogging.h"
|
||||
|
||||
#include <ostream>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// An output stream that acts like /dev/null and drops all output directed to it
|
||||
// Passing 0 here causes the ostream to enter an error state, and so it silently
|
||||
// drops all output directed to it.
|
||||
std::ostream gNullStream(0);
|
||||
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,20 @@
|
|||
/* 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 BreakpadLogging_h
|
||||
#define BreakpadLogging_h
|
||||
|
||||
#include <ostream>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// An output stream that acts like /dev/null and drops all output directed to it
|
||||
extern std::ostream gNullStream;
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
// Override the breakpad info stream to disable INFO logs
|
||||
#define BPLOG_INFO_STREAM mozilla::gNullStream
|
||||
|
||||
#endif // BreakpadLogging_h
|
|
@ -0,0 +1,18 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'BreakpadLogging.cpp',
|
||||
]
|
||||
|
||||
Library('breakpad_logging')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Toolkit', 'Breakpad Integration')
|
|
@ -42,6 +42,7 @@ elif CONFIG['OS_ARCH'] == 'Darwin':
|
|||
OS_LIBS += ['-framework Cocoa']
|
||||
USE_LIBS += [
|
||||
'breakpad_common_s',
|
||||
'breakpad_logging',
|
||||
'breakpad_mac_common_s',
|
||||
]
|
||||
elif CONFIG['OS_ARCH'] == 'SunOS':
|
||||
|
|
|
@ -86,9 +86,11 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
|||
if CONFIG['OS_TARGET'] == 'Android':
|
||||
DEFINES['NO_STABS_SUPPORT'] = True
|
||||
|
||||
DEFINES['BP_LOGGING_INCLUDE'] = '"BreakpadLogging.h"'
|
||||
|
||||
include('/toolkit/crashreporter/crashreporter.mozbuild')
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'..',
|
||||
'../../../breakpad-logging',
|
||||
]
|
||||
|
||||
|
|
|
@ -23,9 +23,12 @@ UNIFIED_SOURCES += [
|
|||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
DEFINES['BP_LOGGING_INCLUDE'] = '"BreakpadLogging.h"'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'..',
|
||||
'../..',
|
||||
'../../../breakpad-logging',
|
||||
]
|
||||
|
||||
include('/toolkit/crashreporter/crashreporter.mozbuild')
|
||||
|
|
|
@ -48,7 +48,10 @@ elif CONFIG['OS_ARCH'] == 'SunOS':
|
|||
'google-breakpad/src/tools/solaris/dump_syms',
|
||||
]
|
||||
|
||||
DIRS += ['client']
|
||||
DIRS += [
|
||||
'breakpad-logging',
|
||||
'client',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_CRASHREPORTER_INJECTOR']:
|
||||
DIRS += ['injector']
|
||||
|
|
|
@ -56,6 +56,7 @@ elif CONFIG['MOZ_ENABLE_PROFILER_SPS']:
|
|||
# Profiler requires some crashreporter code,
|
||||
# so build it even if crashreporter is disabled.
|
||||
DIRS += [
|
||||
'crashreporter/breakpad-logging',
|
||||
'crashreporter/google-breakpad/src/common',
|
||||
'crashreporter/google-breakpad/src/processor',
|
||||
]
|
||||
|
|
|
@ -375,6 +375,18 @@ XRE_InitChildProcess(int aArgc,
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
ReceivePort* ports_out_receiver = new ReceivePort();
|
||||
if (!child_message.AddDescriptor(MachMsgPortDescriptor(ports_out_receiver->GetPort()))) {
|
||||
NS_WARNING("Adding descriptor to message failed");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
ReceivePort* ports_in_receiver = new ReceivePort();
|
||||
if (!child_message.AddDescriptor(MachMsgPortDescriptor(ports_in_receiver->GetPort()))) {
|
||||
NS_WARNING("Adding descriptor to message failed");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
MachPortSender child_sender(mach_port_name);
|
||||
kern_return_t err = child_sender.SendMessage(child_message, kTimeoutMs);
|
||||
if (err != KERN_SUCCESS) {
|
||||
|
@ -393,12 +405,27 @@ XRE_InitChildProcess(int aArgc,
|
|||
NS_WARNING("child GetTranslatedPort(0) failed");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
err = task_set_bootstrap_port(mach_task_self(),
|
||||
parent_message.GetTranslatedPort(0));
|
||||
|
||||
if (parent_message.GetTranslatedPort(1) == MACH_PORT_NULL) {
|
||||
NS_WARNING("child GetTranslatedPort(1) failed");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
MachPortSender* ports_out_sender = new MachPortSender(parent_message.GetTranslatedPort(1));
|
||||
|
||||
if (parent_message.GetTranslatedPort(2) == MACH_PORT_NULL) {
|
||||
NS_WARNING("child GetTranslatedPort(2) failed");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
MachPortSender* ports_in_sender = new MachPortSender(parent_message.GetTranslatedPort(2));
|
||||
|
||||
if (err != KERN_SUCCESS) {
|
||||
NS_WARNING("child task_set_bootstrap_port() failed");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
SetupErrorHandling(aArgv[0]);
|
||||
|
@ -467,6 +494,11 @@ XRE_InitChildProcess(int aArgc,
|
|||
base::ProcessId parentPID = strtol(parentPIDString, &end, 10);
|
||||
MOZ_ASSERT(!*end, "invalid parent PID");
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
mozilla::ipc::SharedMemoryBasic::SetupMachMemory(parentPID, ports_in_receiver, ports_in_sender,
|
||||
ports_out_sender, ports_out_receiver, true);
|
||||
#endif
|
||||
|
||||
#if defined(XP_WIN)
|
||||
// On Win7+, register the application user model id passed in by
|
||||
// parent. This insures windows created by the container properly
|
||||
|
|
|
@ -220,8 +220,33 @@ JSObject* TableTicker::ToJSObject(JSContext *aCx, float aSinceTime)
|
|||
{
|
||||
UniquePtr<char[]> buf = ToJSON(aSinceTime);
|
||||
NS_ConvertUTF8toUTF16 js_string(nsDependentCString(buf.get()));
|
||||
MOZ_ALWAYS_TRUE(JS_ParseJSON(aCx, static_cast<const char16_t*>(js_string.get()),
|
||||
js_string.Length(), &val));
|
||||
bool rv = JS_ParseJSON(aCx, static_cast<const char16_t*>(js_string.get()),
|
||||
js_string.Length(), &val);
|
||||
if (!rv) {
|
||||
#ifdef NIGHTLY_BUILD
|
||||
// XXXshu: Temporary code to help debug malformed JSON. See bug 1172157.
|
||||
nsCOMPtr<nsIFile> path;
|
||||
nsresult rv = NS_GetSpecialDirectory("TmpD", getter_AddRefs(path));
|
||||
if (!NS_FAILED(rv)) {
|
||||
rv = path->Append(NS_LITERAL_STRING("bad-profile.json"));
|
||||
if (!NS_FAILED(rv)) {
|
||||
nsCString cpath;
|
||||
rv = path->GetNativePath(cpath);
|
||||
if (!NS_FAILED(rv)) {
|
||||
std::ofstream stream;
|
||||
stream.open(cpath.get());
|
||||
if (stream.is_open()) {
|
||||
stream << buf.get();
|
||||
stream.close();
|
||||
printf_stderr("Malformed profiler JSON dumped to %s! "
|
||||
"Please upload to https://bugzil.la/1172157\n",
|
||||
cpath.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return &val.toObject();
|
||||
}
|
||||
|
|
|
@ -1304,14 +1304,13 @@ void nsWindow::ConfigureAPZCTreeManager()
|
|||
// have code that can deal with them properly. If APZ is not enabled, this
|
||||
// function doesn't get called, and |mGesture| will take care of touch-based
|
||||
// scrolling. Note that RegisterTouchWindow may still do nothing depending
|
||||
// on touch/pointer events prefs, and so it is possible to enable APZ without
|
||||
// on touch events prefs, and so it is possible to enable APZ without
|
||||
// also enabling touch support.
|
||||
RegisterTouchWindow();
|
||||
}
|
||||
|
||||
void nsWindow::RegisterTouchWindow() {
|
||||
if (Preferences::GetInt("dom.w3c_touch_events.enabled", 0) ||
|
||||
gIsPointerEventsEnabled) {
|
||||
if (Preferences::GetInt("dom.w3c_touch_events.enabled", 0)) {
|
||||
mTouchWindow = true;
|
||||
mGesture.RegisterTouchWindow(mWnd);
|
||||
::EnumChildWindows(mWnd, nsWindow::RegisterTouchForDescendants, 0);
|
||||
|
@ -5321,10 +5320,6 @@ nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam,
|
|||
// A GestureNotify event is dispatched to decide which single-finger panning
|
||||
// direction should be active (including none) and if pan feedback should
|
||||
// be displayed. Java and plugin windows can make their own calls.
|
||||
if (gIsPointerEventsEnabled) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
||||
GESTURENOTIFYSTRUCT * gestureinfo = (GESTURENOTIFYSTRUCT*)lParam;
|
||||
nsPointWin touchPoint;
|
||||
|
@ -6274,10 +6269,6 @@ static int32_t RoundDown(double aDouble)
|
|||
// Gesture event processing. Handles WM_GESTURE events.
|
||||
bool nsWindow::OnGesture(WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if (gIsPointerEventsEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Treatment for pan events which translate into scroll events:
|
||||
if (mGesture.IsPanEvent(lParam)) {
|
||||
if ( !mGesture.ProcessPanMessage(mWnd, wParam, lParam) )
|
||||
|
|
Загрузка…
Ссылка в новой задаче