mirror of
https://github.com/roytam1/basilisk55.git
synced 2026-05-26 13:29:16 +00:00
cherry-picked mozilla upstream changes:
bug1355414, bug1313977, bug1357366, bug1362889, bug1152353, bug1345893, bug1343172, bug1352348, bug1356843, bug1354308, bug1355340, bug1360574, bug1358776, bug1304566, bug1334097, bug1338574
This commit is contained in:
@@ -978,6 +978,10 @@ RemoteInputStream::SetStream(nsIInputStream* aStream)
|
||||
nsresult
|
||||
RemoteInputStream::BlockAndWaitForStream()
|
||||
{
|
||||
if (mStream) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (IsOnOwningThread()) {
|
||||
if (NS_IsMainThread()) {
|
||||
NS_WARNING("Blocking the main thread is not supported!");
|
||||
|
||||
@@ -89,9 +89,17 @@ FileSystemSecurity::ContentProcessHasAccessTo(ContentParentId aId,
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
AssertIsInMainProcess();
|
||||
|
||||
if (FindInReadable(NS_LITERAL_STRING(".."), aPath)) {
|
||||
#if defined(XP_WIN)
|
||||
if (StringBeginsWith(aPath, NS_LITERAL_STRING("..\\")) ||
|
||||
FindInReadable(NS_LITERAL_STRING("\\..\\"), aPath)) {
|
||||
return false;
|
||||
}
|
||||
#elif defined(XP_UNIX)
|
||||
if (StringBeginsWith(aPath, NS_LITERAL_STRING("../")) ||
|
||||
FindInReadable(NS_LITERAL_STRING("/../"), aPath)) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
nsTArray<nsString>* paths;
|
||||
if (!mPaths.Get(aId, &paths)) {
|
||||
|
||||
@@ -28,7 +28,7 @@ addMessageListener("entries.open", function (e) {
|
||||
dir1.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o700);
|
||||
|
||||
var file2 = dir1.clone();
|
||||
file2.append('bar.txt');
|
||||
file2.append('bar..txt'); // Note the double ..
|
||||
file2.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o600);
|
||||
|
||||
var dir2 = dir1.clone();
|
||||
|
||||
@@ -176,9 +176,9 @@ function test_directoryEntry_getFile_simple() {
|
||||
}
|
||||
|
||||
function test_directoryEntry_getFile_deep() {
|
||||
directoryEntry.getFile("subdir/bar.txt", {},
|
||||
directoryEntry.getFile("subdir/bar..txt", {},
|
||||
function(e) {
|
||||
is(e.name, "bar.txt", "We have the right FileEntry.");
|
||||
is(e.name, "bar..txt", "We have the right FileEntry.");
|
||||
test_getParent(e, directoryEntry, /* nested */ true);
|
||||
}, function(e) {
|
||||
ok(false, "This should not happen.");
|
||||
@@ -316,9 +316,9 @@ function test_root_getFile_simple() {
|
||||
}
|
||||
|
||||
function test_root_getFile_deep() {
|
||||
fileEntry.filesystem.root.getFile(directoryEntry.name + "/subdir/bar.txt", {},
|
||||
fileEntry.filesystem.root.getFile(directoryEntry.name + "/subdir/bar..txt", {},
|
||||
function(e) {
|
||||
is(e.name, "bar.txt", "We have the right FileEntry.");
|
||||
is(e.name, "bar..txt", "We have the right FileEntry.");
|
||||
next();
|
||||
}, function(e) {
|
||||
ok(false, "This should not happen.");
|
||||
|
||||
@@ -74,14 +74,7 @@ MediaShutdownManager::InitStatics()
|
||||
sInstance, NS_LITERAL_STRING(__FILE__), __LINE__,
|
||||
NS_LITERAL_STRING("MediaShutdownManager shutdown"));
|
||||
if (NS_FAILED(rv)) {
|
||||
// Leak the buffer on the heap to make sure that it lives long enough,
|
||||
// as MOZ_CRASH_ANNOTATE expects the pointer passed to it to live to
|
||||
// the end of the program.
|
||||
const size_t CAPACITY = 256;
|
||||
auto buf = new char[CAPACITY];
|
||||
snprintf(buf, CAPACITY, "Failed to add shutdown blocker! rv=%x", uint32_t(rv));
|
||||
MOZ_CRASH_ANNOTATE(buf);
|
||||
MOZ_REALLY_CRASH();
|
||||
MOZ_CRASH_UNSAFE_PRINTF("Failed to add shutdown blocker! rv=%x", uint32_t(rv));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ private:
|
||||
char buf[sizeof(randomName) * 2 + 4];
|
||||
PL_strncpy(buf, "CN=", 3);
|
||||
for (size_t i = 0; i < sizeof(randomName); ++i) {
|
||||
snprintf(&buf[i * 2 + 3], 2, "%.2x", randomName[i]);
|
||||
snprintf(&buf[i * 2 + 3], 3, "%.2x", randomName[i]);
|
||||
}
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
|
||||
|
||||
@@ -2458,6 +2458,18 @@ ShutdownObserver::Observe(nsISupports* aSubject,
|
||||
MOZ_ASSERT(!strcmp(aTopic, PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID));
|
||||
MOZ_ASSERT(gInstance);
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
if (NS_WARN_IF(!observerService)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Unregister ourselves from the observer service first to make sure the
|
||||
// nested event loop below will not cause re-entrancy issues.
|
||||
Unused <<
|
||||
observerService->RemoveObserver(this,
|
||||
PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID);
|
||||
|
||||
QuotaManagerService* qms = QuotaManagerService::Get();
|
||||
MOZ_ASSERT(qms);
|
||||
|
||||
|
||||
@@ -226,7 +226,7 @@ nsXMLFragmentContentSink::CloseElement(nsIContent* aContent)
|
||||
{
|
||||
// don't do fancy stuff in nsXMLContentSink
|
||||
if (mPreventScriptExecution &&
|
||||
(aContent->IsHTMLElement(nsGkAtoms::script),
|
||||
(aContent->IsHTMLElement(nsGkAtoms::script) ||
|
||||
aContent->IsSVGElement(nsGkAtoms::script))) {
|
||||
nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(aContent);
|
||||
NS_ASSERTION(sele, "script did QI correctly!");
|
||||
|
||||
@@ -644,6 +644,10 @@ nsEditorSpellCheck::DeleteSuggestedWordList()
|
||||
NS_IMETHODIMP
|
||||
nsEditorSpellCheck::UpdateCurrentDictionary(nsIEditorSpellCheckCallback* aCallback)
|
||||
{
|
||||
if (NS_WARN_IF(!mSpellChecker)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
|
||||
RefPtr<nsEditorSpellCheck> kungFuDeathGrip = this;
|
||||
|
||||
@@ -157,6 +157,8 @@ public:
|
||||
*/
|
||||
virtual void NotifyFlushComplete() = 0;
|
||||
|
||||
virtual void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) = 0;
|
||||
|
||||
virtual void UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent) {}
|
||||
virtual void UpdateOverscrollOffset(float aX, float aY, bool aIsRootContent) {}
|
||||
virtual void SetScrollingRootContent(bool isRootContent) {}
|
||||
|
||||
@@ -53,10 +53,12 @@ typedef mozilla::gfx::Point Point;
|
||||
typedef mozilla::gfx::Point4D Point4D;
|
||||
typedef mozilla::gfx::Matrix4x4 Matrix4x4;
|
||||
|
||||
typedef CompositorBridgeParent::LayerTreeState LayerTreeState;
|
||||
|
||||
float APZCTreeManager::sDPI = 160.0;
|
||||
|
||||
struct APZCTreeManager::TreeBuildingState {
|
||||
TreeBuildingState(const CompositorBridgeParent::LayerTreeState* const aLayerTreeState,
|
||||
TreeBuildingState(const LayerTreeState* const aLayerTreeState,
|
||||
bool aIsFirstPaint, uint64_t aOriginatingLayersId,
|
||||
APZTestData* aTestData, uint32_t aPaintSequence)
|
||||
: mLayerTreeState(aLayerTreeState)
|
||||
@@ -67,7 +69,7 @@ struct APZCTreeManager::TreeBuildingState {
|
||||
}
|
||||
|
||||
// State that doesn't change as we recurse in the tree building
|
||||
const CompositorBridgeParent::LayerTreeState* const mLayerTreeState;
|
||||
const LayerTreeState* const mLayerTreeState;
|
||||
const bool mIsFirstPaint;
|
||||
const uint64_t mOriginatingLayersId;
|
||||
const APZPaintLogHelper mPaintLogger;
|
||||
@@ -226,13 +228,13 @@ APZCTreeManager::UpdateHitTestingTree(uint64_t aRootLayerTreeId,
|
||||
// the layers id that originated this update.
|
||||
APZTestData* testData = nullptr;
|
||||
if (gfxPrefs::APZTestLoggingEnabled()) {
|
||||
if (CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aOriginatingLayersId)) {
|
||||
if (LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aOriginatingLayersId)) {
|
||||
testData = &state->mApzTestData;
|
||||
testData->StartNewPaint(aPaintSequenceNumber);
|
||||
}
|
||||
}
|
||||
|
||||
const CompositorBridgeParent::LayerTreeState* treeState =
|
||||
const LayerTreeState* treeState =
|
||||
CompositorBridgeParent::GetIndirectShadowTree(aRootLayerTreeId);
|
||||
MOZ_ASSERT(treeState);
|
||||
TreeBuildingState state(treeState, aIsFirstPaint, aOriginatingLayersId,
|
||||
@@ -439,6 +441,7 @@ APZCTreeManager::StartScrollbarDrag(const ScrollableLayerGuid& aGuid,
|
||||
|
||||
RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
|
||||
if (!apzc) {
|
||||
NotifyScrollbarDragRejected(aGuid);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -446,6 +449,14 @@ APZCTreeManager::StartScrollbarDrag(const ScrollableLayerGuid& aGuid,
|
||||
mInputQueue->ConfirmDragBlock(inputBlockId, apzc, aDragMetrics);
|
||||
}
|
||||
|
||||
void
|
||||
APZCTreeManager::NotifyScrollbarDragRejected(const ScrollableLayerGuid& aGuid) const
|
||||
{
|
||||
const LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aGuid.mLayersId);
|
||||
MOZ_ASSERT(state && state->mController);
|
||||
state->mController->NotifyAsyncScrollbarDragRejected(aGuid.mScrollId);
|
||||
}
|
||||
|
||||
HitTestingTreeNode*
|
||||
APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
|
||||
const FrameMetrics& aMetrics,
|
||||
@@ -462,7 +473,7 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
|
||||
needsApzc = false;
|
||||
}
|
||||
|
||||
const CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
|
||||
const LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
|
||||
if (!(state && state->mController.get())) {
|
||||
needsApzc = false;
|
||||
}
|
||||
@@ -690,7 +701,7 @@ APZCTreeManager::FlushApzRepaints(uint64_t aLayersId)
|
||||
// ensure any pending paints were flushed. Now, paints are flushed
|
||||
// immediately, so it is safe to simply send a notification now.
|
||||
APZCTM_LOG("Flushing repaints for layers id %" PRIu64, aLayersId);
|
||||
const CompositorBridgeParent::LayerTreeState* state =
|
||||
const LayerTreeState* state =
|
||||
CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
|
||||
MOZ_ASSERT(state && state->mController);
|
||||
state->mController->DispatchToRepaintThread(NewRunnableMethod(
|
||||
|
||||
@@ -477,6 +477,8 @@ private:
|
||||
void PrintAPZCInfo(const LayerMetricsWrapper& aLayer,
|
||||
const AsyncPanZoomController* apzc);
|
||||
|
||||
void NotifyScrollbarDragRejected(const ScrollableLayerGuid& aGuid) const;
|
||||
|
||||
protected:
|
||||
/* The input queue where input events are held until we know enough to
|
||||
* figure out where they're going. Protected so gtests can access it.
|
||||
|
||||
@@ -49,6 +49,7 @@ protected:
|
||||
while (mcc->RunThroughDelayedTasks());
|
||||
apzc->Destroy();
|
||||
tm->ClearTree();
|
||||
tm->ClearContentController();
|
||||
}
|
||||
|
||||
void MakeApzcWaitForMainThread()
|
||||
|
||||
@@ -29,6 +29,7 @@ protected:
|
||||
virtual void TearDown() {
|
||||
while (mcc->RunThroughDelayedTasks());
|
||||
manager->ClearTree();
|
||||
manager->ClearContentController();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -91,6 +91,7 @@ public:
|
||||
}
|
||||
MOCK_METHOD3(NotifyAPZStateChange, void(const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg));
|
||||
MOCK_METHOD0(NotifyFlushComplete, void());
|
||||
MOCK_METHOD1(NotifyAsyncScrollbarDragRejected, void(const FrameMetrics::ViewID&));
|
||||
};
|
||||
|
||||
class MockContentControllerDelayed : public MockContentController {
|
||||
@@ -174,6 +175,10 @@ public:
|
||||
return mInputQueue;
|
||||
}
|
||||
|
||||
void ClearContentController() {
|
||||
mcc = nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
AsyncPanZoomController* NewAPZCInstance(uint64_t aLayersId,
|
||||
GoannaContentController* aController) override;
|
||||
|
||||
@@ -918,6 +918,15 @@ APZCCallbackHelper::IsScrollInProgress(nsIScrollableFrame* aFrame)
|
||||
|| aFrame->LastSmoothScrollOrigin();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
APZCCallbackHelper::NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (nsIScrollableFrame* scrollFrame = nsLayoutUtils::FindScrollableFrameFor(aScrollId)) {
|
||||
scrollFrame->AsyncScrollbarDragRejected();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
APZCCallbackHelper::NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
|
||||
LayoutDeviceCoord aSpanChange,
|
||||
|
||||
@@ -164,6 +164,8 @@ public:
|
||||
/* Notify content that the repaint flush is complete. */
|
||||
static void NotifyFlushComplete(nsIPresShell* aShell);
|
||||
|
||||
static void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId);
|
||||
|
||||
/* Temporarily ignore the Displayport for better paint performance. If at
|
||||
* all possible, pass in a presShell if you have one at the call site, we
|
||||
* use it to trigger a repaint once suppression is disabled. Without that
|
||||
|
||||
@@ -274,3 +274,16 @@ ChromeProcessController::NotifyFlushComplete()
|
||||
|
||||
APZCCallbackHelper::NotifyFlushComplete(GetPresShell());
|
||||
}
|
||||
|
||||
void
|
||||
ChromeProcessController::NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId)
|
||||
{
|
||||
if (MessageLoop::current() != mUILoop) {
|
||||
mUILoop->PostTask(NewRunnableMethod<FrameMetrics::ViewID>(this,
|
||||
&ChromeProcessController::NotifyAsyncScrollbarDragRejected,
|
||||
aScrollId));
|
||||
return;
|
||||
}
|
||||
|
||||
APZCCallbackHelper::NotifyAsyncScrollbarDragRejected(aScrollId);
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ public:
|
||||
virtual void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId,
|
||||
const nsString& aEvent) override;
|
||||
virtual void NotifyFlushComplete() override;
|
||||
virtual void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) override;
|
||||
private:
|
||||
nsCOMPtr<nsIWidget> mWidget;
|
||||
RefPtr<APZEventState> mAPZEventState;
|
||||
|
||||
@@ -85,6 +85,12 @@ ContentProcessController::NotifyFlushComplete()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ContentProcessController::NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId)
|
||||
{
|
||||
APZCCallbackHelper::NotifyAsyncScrollbarDragRejected(aScrollId);
|
||||
}
|
||||
|
||||
void
|
||||
ContentProcessController::PostDelayedTask(already_AddRefed<Runnable> aRunnable, int aDelayMs)
|
||||
{
|
||||
|
||||
@@ -62,6 +62,8 @@ public:
|
||||
|
||||
void NotifyFlushComplete() override;
|
||||
|
||||
void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) override;
|
||||
|
||||
void PostDelayedTask(already_AddRefed<Runnable> aRunnable, int aDelayMs) override;
|
||||
|
||||
bool IsRepaintThread() override;
|
||||
|
||||
@@ -85,6 +85,13 @@ APZChild::RecvNotifyFlushComplete()
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
APZChild::RecvNotifyAsyncScrollbarDragRejected(const ViewID& aScrollId)
|
||||
{
|
||||
mController->NotifyAsyncScrollbarDragRejected(aScrollId);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
APZChild::RecvDestroy()
|
||||
{
|
||||
|
||||
@@ -42,6 +42,8 @@ public:
|
||||
|
||||
mozilla::ipc::IPCResult RecvNotifyFlushComplete() override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvNotifyAsyncScrollbarDragRejected(const ViewID& aScrollId) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvDestroy() override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -64,6 +64,8 @@ child:
|
||||
|
||||
async NotifyFlushComplete();
|
||||
|
||||
async NotifyAsyncScrollbarDragRejected(ViewID aScrollId);
|
||||
|
||||
async Destroy();
|
||||
};
|
||||
|
||||
|
||||
@@ -244,6 +244,21 @@ RemoteContentController::NotifyFlushComplete()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RemoteContentController::NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId)
|
||||
{
|
||||
if (MessageLoop::current() != mCompositorThread) {
|
||||
// We have to send messages from the compositor thread
|
||||
mCompositorThread->PostTask(NewRunnableMethod<FrameMetrics::ViewID>(this,
|
||||
&RemoteContentController::NotifyAsyncScrollbarDragRejected, aScrollId));
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCanSend) {
|
||||
Unused << SendNotifyAsyncScrollbarDragRejected(aScrollId);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RemoteContentController::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
|
||||
@@ -74,6 +74,8 @@ public:
|
||||
|
||||
virtual void NotifyFlushComplete() override;
|
||||
|
||||
virtual void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) override;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
virtual void Destroy() override;
|
||||
|
||||
@@ -300,7 +300,7 @@ bool SkBitmapProcState::chooseScanlineProcs(bool trivialMatrix, bool clampClamp)
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !defined(SK_ARM_HAS_NEON)
|
||||
#if !defined(SK_ARM_HAS_NEON) || defined(SK_ARM_HAS_OPTIONAL_NEON)
|
||||
static const SampleProc32 gSkBitmapProcStateSample32[] = {
|
||||
S32_opaque_D32_nofilter_DXDY,
|
||||
S32_alpha_D32_nofilter_DXDY,
|
||||
|
||||
@@ -56,7 +56,7 @@ extern const SkBitmapProcState::MatrixProc RepeatX_RepeatY_Procs_neon[];
|
||||
#endif // defined(SK_ARM_HAS_NEON)
|
||||
|
||||
// Compile non-neon code path if needed
|
||||
#if !defined(SK_ARM_HAS_NEON)
|
||||
#if !defined(SK_ARM_HAS_NEON) || defined(SK_ARM_HAS_OPTIONAL_NEON)
|
||||
#define MAKENAME(suffix) ClampX_ClampY ## suffix
|
||||
#define TILEX_PROCF(fx, max) SkClampMax((fx) >> 16, max)
|
||||
#define TILEY_PROCF(fy, max) SkClampMax((fy) >> 16, max)
|
||||
|
||||
@@ -136,9 +136,7 @@ BackgroundChildImpl::ProcessingError(Result aCode, const char* aReason)
|
||||
MOZ_CRASH("Unknown error code!");
|
||||
}
|
||||
|
||||
// This is just MOZ_CRASH() un-inlined so that we can pass the result code as
|
||||
// a string. MOZ_CRASH() only supports string literals at the moment.
|
||||
MOZ_ReportCrash(abortMessage.get(), __FILE__, __LINE__); MOZ_REALLY_CRASH();
|
||||
MOZ_CRASH_UNSAFE_PRINTF("%s: %s", abortMessage.get(), aReason);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
+62
-50
@@ -1802,6 +1802,50 @@ MessageChannel::DispatchAsyncMessage(const Message& aMsg)
|
||||
MaybeHandleError(rv, aMsg, "DispatchAsyncMessage");
|
||||
}
|
||||
|
||||
bool
|
||||
MessageChannel::ShouldDeferInterruptMessage(const Message& aMsg, size_t aStackDepth)
|
||||
{
|
||||
AssertWorkerThread();
|
||||
|
||||
// We may or may not own the lock in this function, so don't access any
|
||||
// channel state.
|
||||
|
||||
IPC_ASSERT(aMsg.is_interrupt() && !aMsg.is_reply(), "wrong message type");
|
||||
|
||||
// Race detection: see the long comment near mRemoteStackDepthGuess in
|
||||
// MessageChannel.h. "Remote" stack depth means our side, and "local" means
|
||||
// the other side.
|
||||
if (aMsg.interrupt_remote_stack_depth_guess() == RemoteViewOfStackDepth(aStackDepth)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Interrupt in-calls have raced. The winner, if there is one, gets to defer
|
||||
// processing of the other side's in-call.
|
||||
bool defer;
|
||||
const char* winner;
|
||||
const MessageInfo parentMsgInfo =
|
||||
(mSide == ChildSide) ? MessageInfo(aMsg) : mInterruptStack.top();
|
||||
const MessageInfo childMsgInfo =
|
||||
(mSide == ChildSide) ? mInterruptStack.top() : MessageInfo(aMsg);
|
||||
switch (mListener->MediateInterruptRace(parentMsgInfo, childMsgInfo))
|
||||
{
|
||||
case RIPChildWins:
|
||||
winner = "child";
|
||||
defer = (mSide == ChildSide);
|
||||
break;
|
||||
case RIPParentWins:
|
||||
winner = "parent";
|
||||
defer = (mSide != ChildSide);
|
||||
break;
|
||||
case RIPError:
|
||||
MOZ_CRASH("NYI: 'Error' Interrupt race policy");
|
||||
default:
|
||||
MOZ_CRASH("not reached");
|
||||
}
|
||||
|
||||
return defer;
|
||||
}
|
||||
|
||||
void
|
||||
MessageChannel::DispatchInterruptMessage(Message&& aMsg, size_t stackDepth)
|
||||
{
|
||||
@@ -1810,56 +1854,18 @@ MessageChannel::DispatchInterruptMessage(Message&& aMsg, size_t stackDepth)
|
||||
|
||||
IPC_ASSERT(aMsg.is_interrupt() && !aMsg.is_reply(), "wrong message type");
|
||||
|
||||
// Race detection: see the long comment near mRemoteStackDepthGuess in
|
||||
// MessageChannel.h. "Remote" stack depth means our side, and "local" means
|
||||
// the other side.
|
||||
if (aMsg.interrupt_remote_stack_depth_guess() != RemoteViewOfStackDepth(stackDepth)) {
|
||||
// Interrupt in-calls have raced. The winner, if there is one, gets to defer
|
||||
// processing of the other side's in-call.
|
||||
bool defer;
|
||||
const char* winner;
|
||||
const MessageInfo parentMsgInfo =
|
||||
(mSide == ChildSide) ? MessageInfo(aMsg) : mInterruptStack.top();
|
||||
const MessageInfo childMsgInfo =
|
||||
(mSide == ChildSide) ? mInterruptStack.top() : MessageInfo(aMsg);
|
||||
switch (mListener->MediateInterruptRace(parentMsgInfo, childMsgInfo))
|
||||
{
|
||||
case RIPChildWins:
|
||||
winner = "child";
|
||||
defer = (mSide == ChildSide);
|
||||
break;
|
||||
case RIPParentWins:
|
||||
winner = "parent";
|
||||
defer = (mSide != ChildSide);
|
||||
break;
|
||||
case RIPError:
|
||||
MOZ_CRASH("NYI: 'Error' Interrupt race policy");
|
||||
return;
|
||||
default:
|
||||
MOZ_CRASH("not reached");
|
||||
return;
|
||||
}
|
||||
|
||||
if (LoggingEnabled()) {
|
||||
printf_stderr(" (%s: %s won, so we're%sdeferring)\n",
|
||||
(mSide == ChildSide) ? "child" : "parent",
|
||||
winner,
|
||||
defer ? " " : " not ");
|
||||
}
|
||||
|
||||
if (defer) {
|
||||
// We now know the other side's stack has one more frame
|
||||
// than we thought.
|
||||
++mRemoteStackDepthGuess; // decremented in MaybeProcessDeferred()
|
||||
mDeferred.push(Move(aMsg));
|
||||
return;
|
||||
}
|
||||
|
||||
// We "lost" and need to process the other side's in-call. Don't need
|
||||
// to fix up the mRemoteStackDepthGuess here, because we're just about
|
||||
// to increment it in DispatchCall(), which will make it correct again.
|
||||
if (ShouldDeferInterruptMessage(aMsg, stackDepth)) {
|
||||
// We now know the other side's stack has one more frame
|
||||
// than we thought.
|
||||
++mRemoteStackDepthGuess; // decremented in MaybeProcessDeferred()
|
||||
mDeferred.push(Move(aMsg));
|
||||
return;
|
||||
}
|
||||
|
||||
// If we "lost" a race and need to process the other side's in-call, we
|
||||
// don't need to fix up the mRemoteStackDepthGuess here, because we're just
|
||||
// about to increment it, which will make it correct again.
|
||||
|
||||
#ifdef OS_WIN
|
||||
SyncStackFrame frame(this, true);
|
||||
#endif
|
||||
@@ -1895,12 +1901,18 @@ MessageChannel::MaybeUndeferIncall()
|
||||
|
||||
size_t stackDepth = InterruptStackDepth();
|
||||
|
||||
Message& deferred = mDeferred.top();
|
||||
|
||||
// the other side can only *under*-estimate our actual stack depth
|
||||
IPC_ASSERT(mDeferred.top().interrupt_remote_stack_depth_guess() <= stackDepth,
|
||||
IPC_ASSERT(deferred.interrupt_remote_stack_depth_guess() <= stackDepth,
|
||||
"fatal logic error");
|
||||
|
||||
if (ShouldDeferInterruptMessage(deferred, stackDepth)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// maybe time to process this message
|
||||
Message call(Move(mDeferred.top()));
|
||||
Message call(Move(deferred));
|
||||
mDeferred.pop();
|
||||
|
||||
// fix up fudge factor we added to account for race
|
||||
|
||||
@@ -419,6 +419,7 @@ class MessageChannel : HasResultCodes
|
||||
|
||||
bool WasTransactionCanceled(int transaction);
|
||||
bool ShouldDeferMessage(const Message& aMsg);
|
||||
bool ShouldDeferInterruptMessage(const Message& aMsg, size_t aStackDepth);
|
||||
void OnMessageReceivedFromLink(Message&& aMsg);
|
||||
void OnChannelErrorFromLink();
|
||||
|
||||
|
||||
+6
-16
@@ -843,10 +843,6 @@ DeallocateMappedContent(void* p, size_t length)
|
||||
#error "Memory mapping functions are not defined for your OS."
|
||||
#endif
|
||||
|
||||
#ifdef XP_WIN
|
||||
static char sCrashReason[256];
|
||||
#endif
|
||||
|
||||
void
|
||||
ProtectPages(void* p, size_t size)
|
||||
{
|
||||
@@ -856,10 +852,8 @@ ProtectPages(void* p, size_t size)
|
||||
#if defined(XP_WIN)
|
||||
DWORD oldProtect;
|
||||
if (!VirtualProtect(p, size, PAGE_NOACCESS, &oldProtect)) {
|
||||
SprintfLiteral(sCrashReason,
|
||||
"MOZ_CRASH(VirtualProtect(PAGE_NOACCESS) failed! Error code: %u)", GetLastError());
|
||||
MOZ_CRASH_ANNOTATE(sCrashReason);
|
||||
MOZ_REALLY_CRASH();
|
||||
MOZ_CRASH_UNSAFE_PRINTF("VirtualProtect(PAGE_NOACCESS) failed! Error code: %u",
|
||||
GetLastError());
|
||||
}
|
||||
MOZ_ASSERT(oldProtect == PAGE_READWRITE);
|
||||
#else // assume Unix
|
||||
@@ -877,10 +871,8 @@ MakePagesReadOnly(void* p, size_t size)
|
||||
#if defined(XP_WIN)
|
||||
DWORD oldProtect;
|
||||
if (!VirtualProtect(p, size, PAGE_READONLY, &oldProtect)) {
|
||||
SprintfLiteral(sCrashReason,
|
||||
"MOZ_CRASH(VirtualProtect(PAGE_READONLY) failed! Error code: %u)", GetLastError());
|
||||
MOZ_CRASH_ANNOTATE(sCrashReason);
|
||||
MOZ_REALLY_CRASH();
|
||||
MOZ_CRASH_UNSAFE_PRINTF("VirtualProtect(PAGE_READONLY) failed! Error code: %u",
|
||||
GetLastError());
|
||||
}
|
||||
MOZ_ASSERT(oldProtect == PAGE_READWRITE);
|
||||
#else // assume Unix
|
||||
@@ -898,10 +890,8 @@ UnprotectPages(void* p, size_t size)
|
||||
#if defined(XP_WIN)
|
||||
DWORD oldProtect;
|
||||
if (!VirtualProtect(p, size, PAGE_READWRITE, &oldProtect)) {
|
||||
SprintfLiteral(sCrashReason,
|
||||
"MOZ_CRASH(VirtualProtect(PAGE_READWRITE) failed! Error code: %u)", GetLastError());
|
||||
MOZ_CRASH_ANNOTATE(sCrashReason);
|
||||
MOZ_REALLY_CRASH();
|
||||
MOZ_CRASH_UNSAFE_PRINTF("VirtualProtect(PAGE_READWRITE) failed! Error code: %u",
|
||||
GetLastError());
|
||||
}
|
||||
MOZ_ASSERT(oldProtect == PAGE_NOACCESS || oldProtect == PAGE_READONLY);
|
||||
#else // assume Unix
|
||||
|
||||
+1
-1
@@ -3689,7 +3689,7 @@ js::MaybeAnalyzeBeforeCreatingLargeArray(ExclusiveContext* cx, HandleObjectGroup
|
||||
ShouldUpdateTypes::Update);
|
||||
MOZ_ASSERT(result.value == DenseElementResult::Success);
|
||||
}
|
||||
objects->maybeAnalyze(cx, group, /* forceAnalyze = */ true);
|
||||
objects->maybeAnalyze(cx, group, /* force = */ true);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
+3
-2
@@ -3105,9 +3105,10 @@ Crash(JSContext* cx, unsigned argc, Value* vp)
|
||||
char* utf8chars = JS_EncodeStringToUTF8(cx, message);
|
||||
if (!utf8chars)
|
||||
return false;
|
||||
#ifndef DEBUG
|
||||
MOZ_ReportCrash(utf8chars, __FILE__, __LINE__);
|
||||
MOZ_CRASH_ANNOTATE("MOZ_CRASH(dynamic)");
|
||||
MOZ_REALLY_CRASH();
|
||||
#endif
|
||||
MOZ_CRASH_UNSAFE_OOL(utf8chars);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
#include "ScrollSnap.h"
|
||||
#include "UnitTransforms.h"
|
||||
#include "nsPluginFrame.h"
|
||||
#include "nsSliderFrame.h"
|
||||
#include "mozilla/layers/APZCCallbackHelper.h"
|
||||
#include <mozilla/layers/AxisPhysicsModel.h>
|
||||
#include <mozilla/layers/AxisPhysicsMSDModel.h>
|
||||
@@ -6245,3 +6246,30 @@ ScrollFrameHelper::DragScroll(WidgetEvent* aEvent)
|
||||
|
||||
return willScroll;
|
||||
}
|
||||
|
||||
static void
|
||||
AsyncScrollbarDragRejected(nsIFrame* aScrollbar)
|
||||
{
|
||||
if (!aScrollbar) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (nsIFrame::ChildListIterator childLists(aScrollbar);
|
||||
!childLists.IsDone();
|
||||
childLists.Next()) {
|
||||
for (nsIFrame* frame : childLists.CurrentList()) {
|
||||
if (nsSliderFrame* sliderFrame = do_QueryFrame(frame)) {
|
||||
sliderFrame->AsyncScrollbarDragRejected();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ScrollFrameHelper::AsyncScrollbarDragRejected()
|
||||
{
|
||||
// We don't get told which scrollbar requested the async drag,
|
||||
// so we notify both.
|
||||
::AsyncScrollbarDragRejected(mHScrollbarBox);
|
||||
::AsyncScrollbarDragRejected(mVScrollbarBox);
|
||||
}
|
||||
|
||||
@@ -462,6 +462,8 @@ public:
|
||||
|
||||
bool DragScroll(WidgetEvent* aEvent);
|
||||
|
||||
void AsyncScrollbarDragRejected();
|
||||
|
||||
// owning references to the nsIAnonymousContentCreator-built content
|
||||
nsCOMPtr<nsIContent> mHScrollbarContent;
|
||||
nsCOMPtr<nsIContent> mVScrollbarContent;
|
||||
@@ -1045,6 +1047,10 @@ public:
|
||||
return mHelper.DragScroll(aEvent);
|
||||
}
|
||||
|
||||
virtual void AsyncScrollbarDragRejected() override {
|
||||
return mHelper.AsyncScrollbarDragRejected();
|
||||
}
|
||||
|
||||
#ifdef DEBUG_FRAME_DUMP
|
||||
virtual nsresult GetFrameName(nsAString& aResult) const override;
|
||||
#endif
|
||||
@@ -1478,6 +1484,10 @@ public:
|
||||
return mHelper.DragScroll(aEvent);
|
||||
}
|
||||
|
||||
virtual void AsyncScrollbarDragRejected() override {
|
||||
return mHelper.AsyncScrollbarDragRejected();
|
||||
}
|
||||
|
||||
#ifdef DEBUG_FRAME_DUMP
|
||||
virtual nsresult GetFrameName(nsAString& aResult) const override;
|
||||
#endif
|
||||
|
||||
@@ -483,6 +483,8 @@ public:
|
||||
* caller should look for an ancestor to scroll.
|
||||
*/
|
||||
virtual bool DragScroll(mozilla::WidgetEvent* aEvent) = 0;
|
||||
|
||||
virtual void AsyncScrollbarDragRejected() = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -991,36 +991,36 @@ ScrollFrameWillBuildScrollInfoLayer(nsIFrame* aScrollFrame)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
void
|
||||
nsSliderFrame::StartAPZDrag(WidgetGUIEvent* aEvent)
|
||||
{
|
||||
if (!aEvent->mFlags.mHandledByAPZ) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gfxPlatform::GetPlatform()->SupportsApzDragInput()) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
nsContainerFrame* scrollFrame = GetScrollbar()->GetParent();
|
||||
if (!scrollFrame) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
nsIContent* scrollableContent = scrollFrame->GetContent();
|
||||
if (!scrollableContent) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
nsIScrollableFrame* scrollFrameAsScrollable = do_QueryFrame(scrollFrame);
|
||||
if (!scrollFrameAsScrollable) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// APZ dragging requires the scrollbar to be layerized, which doesn't
|
||||
// happen for scroll info layers.
|
||||
if (ScrollFrameWillBuildScrollInfoLayer(scrollFrame)) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
mozilla::layers::FrameMetrics::ViewID scrollTargetId;
|
||||
@@ -1028,7 +1028,7 @@ nsSliderFrame::StartAPZDrag(WidgetGUIEvent* aEvent)
|
||||
bool hasAPZView = hasID && (scrollTargetId != layers::FrameMetrics::NULL_SCROLL_ID);
|
||||
|
||||
if (!hasAPZView) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
nsIFrame* scrollbarBox = GetScrollbar();
|
||||
@@ -1053,9 +1053,15 @@ nsSliderFrame::StartAPZDrag(WidgetGUIEvent* aEvent)
|
||||
AsyncDragMetrics::VERTICAL);
|
||||
|
||||
if (!nsLayoutUtils::HasDisplayPort(scrollableContent)) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// It's important to set this before calling nsIWidget::StartAsyncScrollbarDrag(),
|
||||
// because in some configurations, that can call AsyncScrollbarDragRejected()
|
||||
// synchronously, which clears the flag (and we want it to stay cleared in
|
||||
// that case).
|
||||
mScrollingWithAPZ = true;
|
||||
|
||||
// When we start an APZ drag, we wont get mouse events for the drag.
|
||||
// APZ will consume them all and only notify us of the new scroll position.
|
||||
bool waitForRefresh = InputAPZContext::HavePendingLayerization();
|
||||
@@ -1067,7 +1073,6 @@ nsSliderFrame::StartAPZDrag(WidgetGUIEvent* aEvent)
|
||||
if (!waitForRefresh) {
|
||||
widget->StartAsyncScrollbarDrag(dragMetrics);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
@@ -1137,16 +1142,15 @@ nsSliderFrame::StartDrag(nsIDOMEvent* aEvent)
|
||||
|
||||
mDragStart = pos - mThumbStart;
|
||||
|
||||
mScrollingWithAPZ = StartAPZDrag(event);
|
||||
mScrollingWithAPZ = false;
|
||||
StartAPZDrag(event); // sets mScrollingWithAPZ=true if appropriate
|
||||
|
||||
#ifdef DEBUG_SLIDER
|
||||
printf("Pressed mDragStart=%d\n",mDragStart);
|
||||
#endif
|
||||
|
||||
if (!mScrollingWithAPZ && !mSuppressionActive) {
|
||||
MOZ_ASSERT(PresContext()->PresShell());
|
||||
APZCCallbackHelper::SuppressDisplayport(true, PresContext()->PresShell());
|
||||
mSuppressionActive = true;
|
||||
if (!mScrollingWithAPZ) {
|
||||
SuppressDisplayport();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@@ -1160,11 +1164,7 @@ nsSliderFrame::StopDrag()
|
||||
|
||||
mScrollingWithAPZ = false;
|
||||
|
||||
if (mSuppressionActive) {
|
||||
MOZ_ASSERT(PresContext()->PresShell());
|
||||
APZCCallbackHelper::SuppressDisplayport(false, PresContext()->PresShell());
|
||||
mSuppressionActive = false;
|
||||
}
|
||||
UnsuppressDisplayport();
|
||||
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
nsIFrame* thumbFrame = mFrames.FirstChild();
|
||||
@@ -1536,5 +1536,36 @@ nsSliderFrame::GetThumbRatio() const
|
||||
return mRatio / mozilla::AppUnitsPerCSSPixel();
|
||||
}
|
||||
|
||||
void
|
||||
nsSliderFrame::AsyncScrollbarDragRejected()
|
||||
{
|
||||
mScrollingWithAPZ = false;
|
||||
// Only suppress the displayport if we're still dragging the thumb.
|
||||
// Otherwise, no one will unsuppress it.
|
||||
if (isDraggingThumb()) {
|
||||
SuppressDisplayport();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsSliderFrame::SuppressDisplayport()
|
||||
{
|
||||
if (!mSuppressionActive) {
|
||||
MOZ_ASSERT(PresContext()->PresShell());
|
||||
APZCCallbackHelper::SuppressDisplayport(true, PresContext()->PresShell());
|
||||
mSuppressionActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsSliderFrame::UnsuppressDisplayport()
|
||||
{
|
||||
if (mSuppressionActive) {
|
||||
MOZ_ASSERT(PresContext()->PresShell());
|
||||
APZCCallbackHelper::SuppressDisplayport(false, PresContext()->PresShell());
|
||||
mSuppressionActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsSliderMediator,
|
||||
nsIDOMEventListener)
|
||||
|
||||
@@ -100,7 +100,7 @@ public:
|
||||
nsresult StartDrag(nsIDOMEvent* aEvent);
|
||||
nsresult StopDrag();
|
||||
|
||||
bool StartAPZDrag(WidgetGUIEvent* aEvent);
|
||||
void StartAPZDrag(mozilla::WidgetGUIEvent* aEvent);
|
||||
|
||||
static int32_t GetCurrentPosition(nsIContent* content);
|
||||
static int32_t GetMinPosition(nsIContent* content);
|
||||
@@ -137,6 +137,11 @@ public:
|
||||
// scrolled frame.
|
||||
float GetThumbRatio() const;
|
||||
|
||||
// Notify the slider frame than an async scrollbar drag requested in
|
||||
// StartAPZDrag() was rejected by APZ, and the slider frame should
|
||||
// fall back to main-thread dragging.
|
||||
void AsyncScrollbarDragRejected();
|
||||
|
||||
private:
|
||||
|
||||
bool GetScrollToClick();
|
||||
@@ -158,6 +163,9 @@ private:
|
||||
void RemoveListener();
|
||||
bool isDraggingThumb();
|
||||
|
||||
void SuppressDisplayport();
|
||||
void UnsuppressDisplayport();
|
||||
|
||||
void StartRepeat() {
|
||||
nsRepeatService::GetInstance()->Start(Notify, this);
|
||||
}
|
||||
|
||||
@@ -21,4 +21,7 @@ USE_LIBS += [
|
||||
'memory',
|
||||
]
|
||||
|
||||
# The memory library defines this, so it's needed here too.
|
||||
DEFINES['IMPL_MFBT'] = True
|
||||
|
||||
DISABLE_STL_WRAPPING = True
|
||||
|
||||
+52
-3
@@ -3,7 +3,12 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/Types.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
MOZ_BEGIN_EXTERN_C
|
||||
|
||||
/*
|
||||
* The crash reason is defined as a global variable here rather than in the
|
||||
@@ -11,7 +16,51 @@
|
||||
* JS that don't link with the crash reporter directly. This value will only
|
||||
* be consumed if the crash reporter is used by the target application.
|
||||
*/
|
||||
MFBT_DATA const char* gMozCrashReason = nullptr;
|
||||
|
||||
#ifndef DEBUG
|
||||
MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void
|
||||
MOZ_CrashOOL(int aLine, const char* aReason)
|
||||
#else
|
||||
MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void
|
||||
MOZ_CrashOOL(const char* aFilename, int aLine, const char* aReason)
|
||||
#endif
|
||||
{
|
||||
#ifdef DEBUG
|
||||
MOZ_ReportCrash(aReason, aFilename, aLine);
|
||||
#endif
|
||||
MOZ_CRASH_ANNOTATE(aReason);
|
||||
MOZ_REALLY_CRASH(aLine);
|
||||
}
|
||||
|
||||
static char sPrintfCrashReason[sPrintfCrashReasonSize] = {};
|
||||
static mozilla::Atomic<bool> sCrashing(false);
|
||||
|
||||
#ifndef DEBUG
|
||||
MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE MOZ_FORMAT_PRINTF(2, 3) void
|
||||
MOZ_CrashPrintf(int aLine, const char* aFormat, ...)
|
||||
#else
|
||||
MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE MOZ_FORMAT_PRINTF(3, 4) void
|
||||
MOZ_CrashPrintf(const char* aFilename, int aLine, const char* aFormat, ...)
|
||||
#endif
|
||||
{
|
||||
if (!sCrashing.compareExchange(false, true)) {
|
||||
// In the unlikely event of a race condition, skip
|
||||
// setting the crash reason and just crash safely.
|
||||
MOZ_REALLY_CRASH(aLine);
|
||||
}
|
||||
va_list aArgs;
|
||||
va_start(aArgs, aFormat);
|
||||
int ret = vsnprintf(sPrintfCrashReason, sPrintfCrashReasonSize,
|
||||
aFormat, aArgs);
|
||||
va_end(aArgs);
|
||||
MOZ_RELEASE_ASSERT(ret >= 0 && size_t(ret) < sPrintfCrashReasonSize,
|
||||
"Could not write the explanation string to the supplied buffer!");
|
||||
#ifdef DEBUG
|
||||
MOZ_ReportCrash(sPrintfCrashReason, aFilename, aLine);
|
||||
#endif
|
||||
MOZ_CRASH_ANNOTATE(sPrintfCrashReason);
|
||||
MOZ_REALLY_CRASH(aLine);
|
||||
}
|
||||
|
||||
MOZ_BEGIN_EXTERN_C
|
||||
MOZ_EXPORT const char* gMozCrashReason = nullptr;
|
||||
MOZ_END_EXTERN_C
|
||||
|
||||
+62
-10
@@ -214,22 +214,22 @@ static MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void MOZ_NoReturn(int aLine)
|
||||
TerminateProcess(GetCurrentProcess(), 3);
|
||||
}
|
||||
|
||||
# define MOZ_REALLY_CRASH() \
|
||||
# define MOZ_REALLY_CRASH(line) \
|
||||
do { \
|
||||
__debugbreak(); \
|
||||
MOZ_NoReturn(__LINE__); \
|
||||
MOZ_NoReturn(line); \
|
||||
} while (0)
|
||||
#else
|
||||
# ifdef __cplusplus
|
||||
# define MOZ_REALLY_CRASH() \
|
||||
# define MOZ_REALLY_CRASH(line) \
|
||||
do { \
|
||||
*((volatile int*) NULL) = __LINE__; \
|
||||
*((volatile int*) NULL) = line; \
|
||||
::abort(); \
|
||||
} while (0)
|
||||
# else
|
||||
# define MOZ_REALLY_CRASH() \
|
||||
# define MOZ_REALLY_CRASH(line) \
|
||||
do { \
|
||||
*((volatile int*) NULL) = __LINE__; \
|
||||
*((volatile int*) NULL) = line; \
|
||||
abort(); \
|
||||
} while (0)
|
||||
# endif
|
||||
@@ -260,17 +260,69 @@ static MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void MOZ_NoReturn(int aLine)
|
||||
# define MOZ_CRASH(...) \
|
||||
do { \
|
||||
MOZ_CRASH_ANNOTATE("MOZ_CRASH(" __VA_ARGS__ ")"); \
|
||||
MOZ_REALLY_CRASH(); \
|
||||
MOZ_REALLY_CRASH(__LINE__); \
|
||||
} while (0)
|
||||
#else
|
||||
# define MOZ_CRASH(...) \
|
||||
do { \
|
||||
MOZ_ReportCrash("" __VA_ARGS__, __FILE__, __LINE__); \
|
||||
MOZ_CRASH_ANNOTATE("MOZ_CRASH(" __VA_ARGS__ ")"); \
|
||||
MOZ_REALLY_CRASH(); \
|
||||
MOZ_REALLY_CRASH(__LINE__); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MOZ_CRASH_UNSAFE_OOL(explanation-string) can be used if the explanation
|
||||
* string cannot be a string literal (but no other processing needs to be done
|
||||
* on it). A regular MOZ_CRASH() is preferred wherever possible, as passing
|
||||
* arbitrary strings from a potentially compromised process is not without risk.
|
||||
* If the string being passed is the result of a printf-style function,
|
||||
* consider using MOZ_CRASH_UNSAFE_PRINTF instead.
|
||||
*/
|
||||
#ifndef DEBUG
|
||||
MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void
|
||||
MOZ_CrashOOL(int aLine, const char* aReason);
|
||||
# define MOZ_CRASH_UNSAFE_OOL(reason) MOZ_CrashOOL(__LINE__, reason)
|
||||
#else
|
||||
MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE void
|
||||
MOZ_CrashOOL(const char* aFilename, int aLine, const char* aReason);
|
||||
# define MOZ_CRASH_UNSAFE_OOL(reason) MOZ_CrashOOL(__FILE__, __LINE__, reason)
|
||||
#endif
|
||||
|
||||
static const size_t sPrintfMaxArgs = 4;
|
||||
static const size_t sPrintfCrashReasonSize = 1024;
|
||||
|
||||
#ifndef DEBUG
|
||||
MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE MOZ_FORMAT_PRINTF(2, 3) void
|
||||
MOZ_CrashPrintf(int aLine, const char* aFormat, ...);
|
||||
# define MOZ_CALL_CRASH_PRINTF(format, ...) \
|
||||
MOZ_CrashPrintf(__LINE__, format, __VA_ARGS__)
|
||||
#else
|
||||
MFBT_API MOZ_COLD MOZ_NORETURN MOZ_NEVER_INLINE MOZ_FORMAT_PRINTF(3, 4) void
|
||||
MOZ_CrashPrintf(const char* aFilename, int aLine, const char* aFormat, ...);
|
||||
# define MOZ_CALL_CRASH_PRINTF(format, ...) \
|
||||
MOZ_CrashPrintf(__FILE__, __LINE__, format, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MOZ_CRASH_UNSAFE_PRINTF(format, arg1 [, args]) can be used when more
|
||||
* information is desired than a string literal can supply. The caller provides
|
||||
* a printf-style format string, which must be a string literal and between
|
||||
* 1 and 4 additional arguments. A regular MOZ_CRASH() is preferred wherever
|
||||
* possible, as passing arbitrary strings to printf from a potentially
|
||||
* compromised process is not without risk.
|
||||
*/
|
||||
#define MOZ_CRASH_UNSAFE_PRINTF(format, ...) \
|
||||
do { \
|
||||
MOZ_STATIC_ASSERT_VALID_ARG_COUNT(__VA_ARGS__); \
|
||||
static_assert( \
|
||||
MOZ_PASTE_PREFIX_AND_ARG_COUNT(, __VA_ARGS__) <= sPrintfMaxArgs, \
|
||||
"Only up to 4 additional arguments are allowed!"); \
|
||||
static_assert(sizeof(format) <= sPrintfCrashReasonSize, \
|
||||
"The supplied format string is too long!"); \
|
||||
MOZ_CALL_CRASH_PRINTF("" format, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
MOZ_END_EXTERN_C
|
||||
|
||||
/*
|
||||
@@ -366,7 +418,7 @@ struct AssertionConditionType
|
||||
if (MOZ_UNLIKELY(!MOZ_CHECK_ASSERT_ASSIGNMENT(expr))) { \
|
||||
MOZ_REPORT_ASSERTION_FAILURE(#expr, __FILE__, __LINE__); \
|
||||
MOZ_CRASH_ANNOTATE("MOZ_RELEASE_ASSERT(" #expr ")"); \
|
||||
MOZ_REALLY_CRASH(); \
|
||||
MOZ_REALLY_CRASH(__LINE__); \
|
||||
} \
|
||||
} while (0)
|
||||
/* Now the two-argument form. */
|
||||
@@ -376,7 +428,7 @@ struct AssertionConditionType
|
||||
if (MOZ_UNLIKELY(!MOZ_CHECK_ASSERT_ASSIGNMENT(expr))) { \
|
||||
MOZ_REPORT_ASSERTION_FAILURE(#expr " (" explain ")", __FILE__, __LINE__); \
|
||||
MOZ_CRASH_ANNOTATE("MOZ_RELEASE_ASSERT(" #expr ") (" explain ")"); \
|
||||
MOZ_REALLY_CRASH(); \
|
||||
MOZ_REALLY_CRASH(__LINE__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
@@ -148,8 +148,7 @@ static MOZ_COLD
|
||||
void CrashWithReason(const char * reason)
|
||||
{
|
||||
#ifndef RELEASE_OR_BETA
|
||||
MOZ_CRASH_ANNOTATE(reason);
|
||||
MOZ_REALLY_CRASH();
|
||||
MOZ_CRASH_UNSAFE_OOL(reason);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -5660,25 +5660,6 @@ NS_IMETHODIMP nsHttpChannel::CloseStickyConnection()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsHttpChannel::ForceNoSpdy()
|
||||
{
|
||||
LOG(("nsHttpChannel::ForceNoSpdy this=%p", this));
|
||||
|
||||
MOZ_ASSERT(mTransaction);
|
||||
if (!mTransaction) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
mAllowSpdy = 0;
|
||||
mCaps |= NS_HTTP_DISALLOW_SPDY;
|
||||
|
||||
if (!(mTransaction->Caps() & NS_HTTP_DISALLOW_SPDY)) {
|
||||
mTransaction->DisableSpdy();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpChannel::nsISupports
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -6062,6 +6043,52 @@ nsHttpChannel::BeginConnect()
|
||||
|
||||
SetLoadGroupUserAgentOverride();
|
||||
|
||||
// Check if request was cancelled during on-modify-request or on-useragent.
|
||||
if (mCanceled) {
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
if (mSuspendCount) {
|
||||
LOG(("Waiting until resume BeginConnect [this=%p]\n", this));
|
||||
MOZ_ASSERT(!mCallOnResume);
|
||||
mCallOnResume = &nsHttpChannel::HandleBeginConnectContinue;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return BeginConnectContinue();
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpChannel::HandleBeginConnectContinue()
|
||||
{
|
||||
NS_PRECONDITION(!mCallOnResume, "How did that happen?");
|
||||
nsresult rv;
|
||||
|
||||
if (mSuspendCount) {
|
||||
LOG(("Waiting until resume BeginConnect [this=%p]\n", this));
|
||||
mCallOnResume = &nsHttpChannel::HandleBeginConnectContinue;
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(("nsHttpChannel::HandleBeginConnectContinue [this=%p]\n", this));
|
||||
rv = BeginConnectContinue();
|
||||
if (NS_FAILED(rv)) {
|
||||
CloseCacheEntry(false);
|
||||
Unused << AsyncAbort(rv);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpChannel::BeginConnectContinue()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// Check if request was cancelled during suspend AFTER on-modify-request or
|
||||
// on-useragent.
|
||||
if (mCanceled) {
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
// Check to see if we should redirect this channel elsewhere by
|
||||
// nsIHttpChannel.redirectTo API request
|
||||
if (mAPIRedirectToURI) {
|
||||
|
||||
@@ -111,7 +111,6 @@ public:
|
||||
NS_IMETHOD OnAuthAvailable() override;
|
||||
NS_IMETHOD OnAuthCancelled(bool userCancel) override;
|
||||
NS_IMETHOD CloseStickyConnection() override;
|
||||
NS_IMETHOD ForceNoSpdy() override;
|
||||
// Functions we implement from nsIHttpAuthenticableChannel but are
|
||||
// declared in HttpBaseChannel must be implemented in this class. We
|
||||
// just call the HttpBaseChannel:: impls.
|
||||
@@ -284,6 +283,8 @@ private:
|
||||
|
||||
bool RequestIsConditional();
|
||||
nsresult BeginConnect();
|
||||
void HandleBeginConnectContinue();
|
||||
MOZ_MUST_USE nsresult BeginConnectContinue();
|
||||
nsresult ContinueBeginConnectWithResult();
|
||||
void ContinueBeginConnect();
|
||||
nsresult Connect();
|
||||
|
||||
@@ -820,10 +820,6 @@ nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge,
|
||||
}
|
||||
|
||||
mConnectionBased = !!(authFlags & nsIHttpAuthenticator::CONNECTION_BASED);
|
||||
if (mConnectionBased) {
|
||||
rv = mAuthChannel->ForceNoSpdy();
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
}
|
||||
|
||||
if (identityInvalid) {
|
||||
if (entry) {
|
||||
|
||||
@@ -2031,11 +2031,6 @@ nsHttpTransaction::CheckForStickyAuthScheme()
|
||||
MOZ_ASSERT(mResponseHead);
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||
|
||||
if (mClosed) {
|
||||
LOG((" closed, not checking"));
|
||||
return;
|
||||
}
|
||||
|
||||
CheckForStickyAuthSchemeAt(nsHttp::WWW_Authenticate);
|
||||
CheckForStickyAuthSchemeAt(nsHttp::Proxy_Authenticate);
|
||||
}
|
||||
|
||||
@@ -134,8 +134,6 @@ public:
|
||||
void DispatchedAsBlocking();
|
||||
void RemoveDispatchedAsBlocking();
|
||||
|
||||
void DisableSpdy() override;
|
||||
|
||||
nsHttpTransaction *QueryHttpTransaction() override { return this; }
|
||||
|
||||
Http2PushedStream *GetPushedStream() { return mPushedStream; }
|
||||
@@ -203,6 +201,7 @@ private:
|
||||
|
||||
bool ResponseTimeoutEnabled() const final;
|
||||
|
||||
void DisableSpdy() override;
|
||||
void ReuseConnectionOnRestartOK(bool reuseOk) override { mReuseOnRestart = reuseOk; }
|
||||
|
||||
// Called right after we parsed the response head. Checks for connection based
|
||||
|
||||
@@ -112,10 +112,4 @@ interface nsIHttpAuthenticableChannel : nsIProxiedChannel
|
||||
* the same connection.
|
||||
*/
|
||||
void closeStickyConnection();
|
||||
|
||||
/**
|
||||
* Tells the channel to not use SPDY-like protocols, since this will be
|
||||
* using connection-oriented auth.
|
||||
*/
|
||||
void forceNoSpdy();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
// This file tests async handling of a channel suspended in http-on-modify-request.
|
||||
|
||||
var CC = Components.Constructor;
|
||||
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
var obs = Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(Ci.nsIObserverService);
|
||||
|
||||
var ios = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Components.interfaces.nsIIOService);
|
||||
|
||||
// baseUrl is always the initial connection attempt and is handled by
|
||||
// failResponseHandler since every test expects that request will either be
|
||||
// redirected or cancelled.
|
||||
var baseUrl;
|
||||
|
||||
function failResponseHandler(metadata, response)
|
||||
{
|
||||
var text = "failure response";
|
||||
response.setHeader("Content-Type", "text/plain", false);
|
||||
response.bodyOutputStream.write(text, text.length);
|
||||
do_check_true(false, "Received request when we shouldn't.");
|
||||
}
|
||||
|
||||
function successResponseHandler(metadata, response)
|
||||
{
|
||||
var text = "success response";
|
||||
response.setHeader("Content-Type", "text/plain", false);
|
||||
response.bodyOutputStream.write(text, text.length);
|
||||
do_check_true(true, "Received expected request.");
|
||||
}
|
||||
|
||||
function onModifyListener(callback) {
|
||||
obs.addObserver({
|
||||
observe: function(subject, topic, data) {
|
||||
var obs = Cc["@mozilla.org/observer-service;1"].getService();
|
||||
obs = obs.QueryInterface(Ci.nsIObserverService);
|
||||
obs.removeObserver(this, "http-on-modify-request");
|
||||
callback(subject.QueryInterface(Ci.nsIHttpChannel));
|
||||
}
|
||||
}, "http-on-modify-request", false);
|
||||
}
|
||||
|
||||
function startChannelRequest(baseUrl, flags, expectedResponse=null) {
|
||||
var chan = NetUtil.newChannel({
|
||||
uri: baseUrl,
|
||||
loadUsingSystemPrincipal: true
|
||||
});
|
||||
chan.asyncOpen2(new ChannelListener((request, data, context) => {
|
||||
if (expectedResponse) {
|
||||
do_check_eq(data, expectedResponse);
|
||||
} else {
|
||||
do_check_true(!!!data, "no response");
|
||||
}
|
||||
do_execute_soon(run_next_test)
|
||||
}, null, flags));
|
||||
}
|
||||
|
||||
|
||||
add_test(function testSimpleRedirect() {
|
||||
onModifyListener(chan => {
|
||||
chan.redirectTo(ios.newURI(`${baseUrl}/success`));
|
||||
});
|
||||
startChannelRequest(baseUrl, undefined, "success response");
|
||||
});
|
||||
|
||||
add_test(function testSimpleCancel() {
|
||||
onModifyListener(chan => {
|
||||
chan.cancel(Cr.NS_BINDING_ABORTED);
|
||||
});
|
||||
startChannelRequest(baseUrl, CL_EXPECT_FAILURE);
|
||||
});
|
||||
|
||||
add_test(function testSimpleCancelRedirect() {
|
||||
onModifyListener(chan => {
|
||||
chan.redirectTo(ios.newURI(`${baseUrl}/fail`));
|
||||
chan.cancel(Cr.NS_BINDING_ABORTED);
|
||||
});
|
||||
startChannelRequest(baseUrl, CL_EXPECT_FAILURE);
|
||||
});
|
||||
|
||||
// Test a request that will get redirected asynchronously. baseUrl should
|
||||
// not be requested, we should receive the request for the redirectedUrl.
|
||||
add_test(function testAsyncRedirect() {
|
||||
onModifyListener(chan => {
|
||||
// Suspend the channel then yield to make this async.
|
||||
chan.suspend();
|
||||
Promise.resolve().then(() => {
|
||||
chan.redirectTo(ios.newURI(`${baseUrl}/success`));
|
||||
chan.resume();
|
||||
});
|
||||
});
|
||||
startChannelRequest(baseUrl, undefined, "success response");
|
||||
});
|
||||
|
||||
add_test(function testSyncRedirect() {
|
||||
onModifyListener(chan => {
|
||||
chan.suspend();
|
||||
chan.redirectTo(ios.newURI(`${baseUrl}/success`));
|
||||
Promise.resolve().then(() => {
|
||||
chan.resume();
|
||||
});
|
||||
});
|
||||
startChannelRequest(baseUrl, undefined, "success response");
|
||||
});
|
||||
|
||||
add_test(function testAsyncCancel() {
|
||||
onModifyListener(chan => {
|
||||
// Suspend the channel then yield to make this async.
|
||||
chan.suspend();
|
||||
Promise.resolve().then(() => {
|
||||
chan.cancel(Cr.NS_BINDING_ABORTED);
|
||||
chan.resume();
|
||||
});
|
||||
});
|
||||
startChannelRequest(baseUrl, CL_EXPECT_FAILURE);
|
||||
});
|
||||
|
||||
add_test(function testSyncCancel() {
|
||||
onModifyListener(chan => {
|
||||
chan.suspend();
|
||||
chan.cancel(Cr.NS_BINDING_ABORTED);
|
||||
Promise.resolve().then(() => {
|
||||
chan.resume();
|
||||
});
|
||||
});
|
||||
startChannelRequest(baseUrl, CL_EXPECT_FAILURE);
|
||||
});
|
||||
|
||||
// Test request that will get redirected and cancelled asynchronously,
|
||||
// ensure no connection is made.
|
||||
add_test(function testAsyncCancelRedirect() {
|
||||
onModifyListener(chan => {
|
||||
// Suspend the channel then yield to make this async.
|
||||
chan.suspend();
|
||||
Promise.resolve().then(() => {
|
||||
chan.cancel(Cr.NS_BINDING_ABORTED);
|
||||
chan.redirectTo(ios.newURI(`${baseUrl}/fail`));
|
||||
chan.resume();
|
||||
});
|
||||
});
|
||||
startChannelRequest(baseUrl, CL_EXPECT_FAILURE);
|
||||
});
|
||||
|
||||
// Test a request that will get cancelled synchronously, ensure async redirect
|
||||
// is not made.
|
||||
add_test(function testSyncCancelRedirect() {
|
||||
onModifyListener(chan => {
|
||||
chan.suspend();
|
||||
chan.cancel(Cr.NS_BINDING_ABORTED);
|
||||
Promise.resolve().then(() => {
|
||||
chan.redirectTo(ios.newURI(`${baseUrl}/fail`));
|
||||
chan.resume();
|
||||
});
|
||||
});
|
||||
startChannelRequest(baseUrl, CL_EXPECT_FAILURE);
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
var httpServer = new HttpServer();
|
||||
httpServer.registerPathHandler("/", failResponseHandler);
|
||||
httpServer.registerPathHandler("/fail", failResponseHandler);
|
||||
httpServer.registerPathHandler("/success", successResponseHandler);
|
||||
httpServer.start(-1);
|
||||
|
||||
baseUrl = `http://localhost:${httpServer.identity.primaryPort}`;
|
||||
|
||||
run_next_test();
|
||||
|
||||
do_register_cleanup(function(){
|
||||
httpServer.stop(() => {});
|
||||
});
|
||||
}
|
||||
@@ -359,6 +359,7 @@ skip-if = os == "android"
|
||||
[test_1073747.js]
|
||||
[test_safeoutputstream_append.js]
|
||||
[test_suspend_channel_before_connect.js]
|
||||
[test_suspend_channel_on_modified.js]
|
||||
[test_inhibit_caching.js]
|
||||
[test_dns_disable_ipv4.js]
|
||||
[test_dns_disable_ipv6.js]
|
||||
|
||||
@@ -598,27 +598,49 @@ Database::BackupAndReplaceDatabaseFile(nsCOMPtr<mozIStorageService>& aStorage)
|
||||
profDir, getter_AddRefs(backup));
|
||||
}
|
||||
|
||||
// If anything fails from this point on, we have a stale connection or
|
||||
// database file, and there's not much more we can do.
|
||||
// The only thing we can try to do is to replace the database on the next
|
||||
// start, and enforce a crash, so it gets reported to us.
|
||||
|
||||
// Close database connection if open.
|
||||
if (mMainConn) {
|
||||
// If there's any not finalized statement or this fails for any reason
|
||||
// we won't be able to remove the database.
|
||||
rv = mMainConn->Close();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_SUCCESS(rv, ForceCrashAndReplaceDatabase(
|
||||
NS_LITERAL_CSTRING("Unable to close the corrupt database.")));
|
||||
}
|
||||
|
||||
// Remove the broken database.
|
||||
rv = databaseFile->Remove(false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
|
||||
return ForceCrashAndReplaceDatabase(
|
||||
NS_LITERAL_CSTRING("Unable to remove the corrupt database file."));
|
||||
}
|
||||
|
||||
// Create a new database file.
|
||||
// Use an unshared connection, it will consume more memory but avoid shared
|
||||
// cache contentions across threads.
|
||||
rv = aStorage->OpenUnsharedDatabase(databaseFile, getter_AddRefs(mMainConn));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_SUCCESS(rv, ForceCrashAndReplaceDatabase(
|
||||
NS_LITERAL_CSTRING("Unable to open a new database connection.")));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Database::ForceCrashAndReplaceDatabase(const nsCString& aReason)
|
||||
{
|
||||
Preferences::SetBool(PREF_FORCE_DATABASE_REPLACEMENT, true);
|
||||
// Ensure that prefs get saved, or we could crash before storing them.
|
||||
nsIPrefService* prefService = Preferences::GetService();
|
||||
if (prefService && NS_SUCCEEDED(prefService->SavePrefFile(nullptr))) {
|
||||
// We could force an application restart here, but we'd like to get these
|
||||
// cases reported to us, so let's force a crash instead.
|
||||
MOZ_CRASH_UNSAFE_OOL(aReason.get());
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Database::InitSchema(bool* aDatabaseMigrated)
|
||||
{
|
||||
@@ -1896,7 +1918,23 @@ Database::MigrateV35Up() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
int64_t mobileRootId = CreateMobileRoot();
|
||||
if (mobileRootId <= 0) return NS_ERROR_FAILURE;
|
||||
if (mobileRootId <= 0) {
|
||||
// Either the schema is broken or there isn't any root. The latter can
|
||||
// happen if a consumer, for example Thunderbird, never used bookmarks.
|
||||
// If there are no roots, this migration should not run.
|
||||
nsCOMPtr<mozIStorageStatement> checkRootsStmt;
|
||||
nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT id FROM moz_bookmarks WHERE parent = 0"
|
||||
), getter_AddRefs(checkRootsStmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mozStorageStatementScoper scoper(checkRootsStmt);
|
||||
bool hasResult = false;
|
||||
rv = checkRootsStmt->ExecuteStep(&hasResult);
|
||||
if (NS_SUCCEEDED(rv) && !hasResult) {
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// At this point, we should have no more than two folders with the mobile
|
||||
// bookmarks anno: the new root, and the old folder if one exists. If, for
|
||||
|
||||
@@ -224,6 +224,12 @@ protected:
|
||||
*/
|
||||
nsresult BackupAndReplaceDatabaseFile(nsCOMPtr<mozIStorageService>& aStorage);
|
||||
|
||||
/**
|
||||
* This should be used as a last resort in case the database is corrupt and
|
||||
* there's no way to fix it in-place.
|
||||
*/
|
||||
nsresult ForceCrashAndReplaceDatabase(const nsCString& aReason);
|
||||
|
||||
/**
|
||||
* Initializes the database. This performs any necessary migrations for the
|
||||
* database. All migration is done inside a transaction that is rolled back
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
add_task(function* setup() {
|
||||
yield setupPlacesDatabase("places_v34.sqlite");
|
||||
// Setup database contents to be migrated.
|
||||
let path = OS.Path.join(OS.Constants.Path.profileDir, DB_FILENAME);
|
||||
let db = yield Sqlite.openConnection({ path });
|
||||
// Remove all the roots.
|
||||
yield db.execute("DELETE FROM moz_bookmarks");
|
||||
yield db.close();
|
||||
});
|
||||
|
||||
add_task(function* database_is_valid() {
|
||||
// Accessing the database for the first time triggers migration.
|
||||
Assert.equal(PlacesUtils.history.databaseStatus,
|
||||
PlacesUtils.history.DATABASE_STATUS_UPGRADED);
|
||||
|
||||
let db = yield PlacesUtils.promiseDBConnection();
|
||||
Assert.equal((yield db.getSchemaVersion()), CURRENT_SCHEMA_VERSION);
|
||||
});
|
||||
@@ -33,4 +33,5 @@ support-files =
|
||||
[test_current_from_v27.js]
|
||||
[test_current_from_v31.js]
|
||||
[test_current_from_v34.js]
|
||||
[test_current_from_v34_no_roots.js]
|
||||
[test_current_from_v35.js]
|
||||
|
||||
@@ -2716,14 +2716,12 @@ nsExternalHelperAppService::GetTypeFromExtension(const nsACString& aFileExt,
|
||||
}
|
||||
|
||||
// Ask OS.
|
||||
bool found = false;
|
||||
nsCOMPtr<nsIMIMEInfo> mi = GetMIMEInfoFromOS(EmptyCString(), aFileExt, &found);
|
||||
if (mi && found) {
|
||||
return mi->GetMIMEType(aContentType);
|
||||
if (GetMIMETypeFromOSForExtension(aFileExt, aContentType)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Check extras array.
|
||||
found = GetTypeFromExtras(aFileExt, aContentType);
|
||||
bool found = GetTypeFromExtras(aFileExt, aContentType);
|
||||
if (found) {
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -2922,3 +2920,11 @@ bool nsExternalHelperAppService::GetTypeFromExtras(const nsACString& aExtension,
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
nsExternalHelperAppService::GetMIMETypeFromOSForExtension(const nsACString& aExtension, nsACString& aMIMEType)
|
||||
{
|
||||
bool found = false;
|
||||
nsCOMPtr<nsIMIMEInfo> mimeInfo = GetMIMEInfoFromOS(EmptyCString(), aExtension, &found);
|
||||
return found && mimeInfo && NS_SUCCEEDED(mimeInfo->GetMIMEType(aMIMEType));
|
||||
}
|
||||
|
||||
@@ -108,6 +108,15 @@ public:
|
||||
virtual nsresult OSProtocolHandlerExists(const char *aScheme,
|
||||
bool *aExists) = 0;
|
||||
|
||||
/**
|
||||
* Given an extension, get a MIME type string. If not overridden by
|
||||
* the OS-specific nsOSHelperAppService, will call into GetMIMEInfoFromOS
|
||||
* with an empty mimetype.
|
||||
* @return true if we successfully found a mimetype.
|
||||
*/
|
||||
virtual bool GetMIMETypeFromOSForExtension(const nsACString& aExtension,
|
||||
nsACString& aMIMEType);
|
||||
|
||||
protected:
|
||||
virtual ~nsExternalHelperAppService();
|
||||
|
||||
|
||||
@@ -395,43 +395,25 @@ already_AddRefed<nsMIMEInfoWin> nsOSHelperAppService::GetByExtension(const nsAFl
|
||||
if (aFileExt.IsEmpty())
|
||||
return nullptr;
|
||||
|
||||
// windows registry assumes your file extension is going to include the '.'.
|
||||
// so make sure it's there...
|
||||
// Determine the mime type.
|
||||
nsAutoCString typeToUse;
|
||||
if (aTypeHint && *aTypeHint) {
|
||||
typeToUse.Assign(aTypeHint);
|
||||
} else if (!GetMIMETypeFromOSForExtension(NS_ConvertUTF16toUTF8(aFileExt), typeToUse)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<nsMIMEInfoWin> mimeInfo = new nsMIMEInfoWin(typeToUse);
|
||||
|
||||
// windows registry assumes your file extension is going to include the '.',
|
||||
// but our APIs expect it to not be there, so make sure we normalize that bit.
|
||||
nsAutoString fileExtToUse;
|
||||
if (aFileExt.First() != char16_t('.'))
|
||||
fileExtToUse = char16_t('.');
|
||||
|
||||
fileExtToUse.Append(aFileExt);
|
||||
|
||||
// Try to get an entry from the windows registry.
|
||||
nsCOMPtr<nsIWindowsRegKey> regKey =
|
||||
do_CreateInstance("@mozilla.org/windows-registry-key;1");
|
||||
if (!regKey)
|
||||
return nullptr;
|
||||
|
||||
nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
|
||||
fileExtToUse,
|
||||
nsIWindowsRegKey::ACCESS_QUERY_VALUE);
|
||||
if (NS_FAILED(rv))
|
||||
return nullptr;
|
||||
|
||||
nsAutoCString typeToUse;
|
||||
if (aTypeHint && *aTypeHint) {
|
||||
typeToUse.Assign(aTypeHint);
|
||||
}
|
||||
else {
|
||||
nsAutoString temp;
|
||||
if (NS_FAILED(regKey->ReadStringValue(NS_LITERAL_STRING("Content Type"),
|
||||
temp)) || temp.IsEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
// Content-Type is always in ASCII
|
||||
LossyAppendUTF16toASCII(temp, typeToUse);
|
||||
}
|
||||
|
||||
RefPtr<nsMIMEInfoWin> mimeInfo = new nsMIMEInfoWin(typeToUse);
|
||||
|
||||
// don't append the '.'
|
||||
// don't append the '.' for our APIs.
|
||||
mimeInfo->AppendExtension(NS_ConvertUTF16toUTF8(Substring(fileExtToUse, 1)));
|
||||
mimeInfo->SetPreferredAction(nsIMIMEInfo::useSystemDefault);
|
||||
|
||||
@@ -458,8 +440,17 @@ already_AddRefed<nsMIMEInfoWin> nsOSHelperAppService::GetByExtension(const nsAFl
|
||||
}
|
||||
else
|
||||
{
|
||||
found = NS_SUCCEEDED(regKey->ReadStringValue(EmptyString(),
|
||||
appInfo));
|
||||
nsCOMPtr<nsIWindowsRegKey> regKey =
|
||||
do_CreateInstance("@mozilla.org/windows-registry-key;1");
|
||||
if (!regKey)
|
||||
return nullptr;
|
||||
nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
|
||||
fileExtToUse,
|
||||
nsIWindowsRegKey::ACCESS_QUERY_VALUE);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
found = NS_SUCCEEDED(regKey->ReadStringValue(EmptyString(),
|
||||
appInfo));
|
||||
}
|
||||
}
|
||||
|
||||
// Bug 358297 - ignore the default handler, force the user to choose app
|
||||
@@ -596,3 +587,40 @@ nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsOSHelperAppService::GetMIMETypeFromOSForExtension(const nsACString& aExtension,
|
||||
nsACString& aMIMEType)
|
||||
{
|
||||
if (aExtension.IsEmpty())
|
||||
return false;
|
||||
|
||||
// windows registry assumes your file extension is going to include the '.'.
|
||||
// so make sure it's there...
|
||||
nsAutoString fileExtToUse;
|
||||
if (aExtension.First() != '.')
|
||||
fileExtToUse = char16_t('.');
|
||||
|
||||
AppendUTF8toUTF16(aExtension, fileExtToUse);
|
||||
|
||||
// Try to get an entry from the windows registry.
|
||||
nsCOMPtr<nsIWindowsRegKey> regKey =
|
||||
do_CreateInstance("@mozilla.org/windows-registry-key;1");
|
||||
if (!regKey)
|
||||
return false;
|
||||
|
||||
nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
|
||||
fileExtToUse,
|
||||
nsIWindowsRegKey::ACCESS_QUERY_VALUE);
|
||||
if (NS_FAILED(rv))
|
||||
return false;
|
||||
|
||||
nsAutoString mimeType;
|
||||
if (NS_FAILED(regKey->ReadStringValue(NS_LITERAL_STRING("Content Type"),
|
||||
mimeType)) || mimeType.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// Content-Type is always in ASCII
|
||||
aMIMEType.Truncate();
|
||||
LossyAppendUTF16toASCII(mimeType, aMIMEType);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ public:
|
||||
NS_IMETHOD GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
|
||||
bool *found,
|
||||
nsIHandlerInfo **_retval);
|
||||
virtual bool GetMIMETypeFromOSForExtension(const nsACString& aExtension,
|
||||
nsACString& aMIMEType) override;
|
||||
|
||||
/** Get the string value of a registry value and store it in result.
|
||||
* @return true on success, false on failure
|
||||
|
||||
@@ -493,6 +493,9 @@ public:
|
||||
LayoutDeviceIntPoint CocoaPointsToDevPixels(const NSPoint& aPt) const {
|
||||
return nsCocoaUtils::CocoaPointsToDevPixels(aPt, BackingScaleFactor());
|
||||
}
|
||||
LayoutDeviceIntPoint CocoaPointsToDevPixelsRoundDown(const NSPoint& aPt) const {
|
||||
return nsCocoaUtils::CocoaPointsToDevPixelsRoundDown(aPt, BackingScaleFactor());
|
||||
}
|
||||
LayoutDeviceIntRect CocoaPointsToDevPixels(const NSRect& aRect) const {
|
||||
return nsCocoaUtils::CocoaPointsToDevPixels(aRect, BackingScaleFactor());
|
||||
}
|
||||
|
||||
@@ -195,6 +195,7 @@ static uint32_t gNumberOfWidgetsNeedingEventThread = 0;
|
||||
#endif
|
||||
|
||||
- (LayoutDeviceIntPoint)convertWindowCoordinates:(NSPoint)aPoint;
|
||||
- (LayoutDeviceIntPoint)convertWindowCoordinatesRoundDown:(NSPoint)aPoint;
|
||||
- (IAPZCTreeManager*)apzctm;
|
||||
|
||||
- (BOOL)inactiveWindowAcceptsMouseEvent:(NSEvent*)aEvent;
|
||||
@@ -4832,16 +4833,20 @@ AccumulateIntegerDelta(NSEvent* aEvent)
|
||||
static gfx::IntPoint
|
||||
GetIntegerDeltaForEvent(NSEvent* aEvent)
|
||||
{
|
||||
if (nsCocoaFeatures::OnSierraOrLater()) {
|
||||
if (nsCocoaFeatures::OnSierraOrLater() && [aEvent hasPreciseScrollingDeltas]) {
|
||||
// Pixel scroll events (events with hasPreciseScrollingDeltas == YES)
|
||||
// carry pixel deltas in the scrollingDeltaX/Y fields and line scroll
|
||||
// information in the deltaX/Y fields.
|
||||
// Prior to 10.12, these line scroll fields would be zero for most pixel
|
||||
// scroll events and non-zero for some, whenever at least a full line
|
||||
// worth of pixel scrolling had accumulated. That's the behavior we want.
|
||||
// Starting with 10.12 however, pixel scroll events no longer accumulate
|
||||
// deltaX and deltaY; they just report floating point values for every
|
||||
// single event. So we need to do our own accumulation.
|
||||
return AccumulateIntegerDelta(aEvent);
|
||||
}
|
||||
|
||||
// Pre-10.12, deltaX/deltaY had the accumulation behavior that we want, and
|
||||
// it worked more reliably than doing it on our own, so use it on pre-10.12
|
||||
// versions. For example, with a traditional USB mouse, the first wheel
|
||||
// "tick" would always senda line scroll of at least one line, but with our
|
||||
// own accumulation you sometimes need to do multiple wheel ticks before one
|
||||
// line has been accumulated.
|
||||
// For line scrolls, or pre-10.12, just use the rounded up value of deltaX / deltaY.
|
||||
return gfx::IntPoint(RoundUp([aEvent deltaX]), RoundUp([aEvent deltaY]));
|
||||
}
|
||||
|
||||
@@ -4889,8 +4894,12 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
|
||||
|
||||
NSPoint locationInWindow = nsCocoaUtils::EventLocationForWindow(theEvent, [self window]);
|
||||
|
||||
// Use convertWindowCoordinatesRoundDown when converting the position to
|
||||
// integer screen pixels in order to ensure that coordinates which are just
|
||||
// inside the right / bottom edges of the window don't end up outside of the
|
||||
// window after rounding.
|
||||
ScreenPoint position = ViewAs<ScreenPixel>(
|
||||
[self convertWindowCoordinates:locationInWindow],
|
||||
[self convertWindowCoordinatesRoundDown:locationInWindow],
|
||||
PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent);
|
||||
|
||||
bool usePreciseDeltas = nsCocoaUtils::HasPreciseScrollingDeltas(theEvent) &&
|
||||
@@ -5603,6 +5612,16 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
|
||||
return mGoannaChild->CocoaPointsToDevPixels(localPoint);
|
||||
}
|
||||
|
||||
- (LayoutDeviceIntPoint)convertWindowCoordinatesRoundDown:(NSPoint)aPoint
|
||||
{
|
||||
if (!mGoannaChild) {
|
||||
return LayoutDeviceIntPoint(0, 0);
|
||||
}
|
||||
|
||||
NSPoint localPoint = [self convertPoint:aPoint fromView:nil];
|
||||
return mGoannaChild->CocoaPointsToDevPixelsRoundDown(localPoint);
|
||||
}
|
||||
|
||||
- (IAPZCTreeManager*)apzctm
|
||||
{
|
||||
return mGoannaChild ? mGoannaChild->APZCTM() : nullptr;
|
||||
|
||||
@@ -144,6 +144,13 @@ public:
|
||||
NSToIntRound(aPt.y * aBackingScale));
|
||||
}
|
||||
|
||||
static LayoutDeviceIntPoint
|
||||
CocoaPointsToDevPixelsRoundDown(const NSPoint& aPt, CGFloat aBackingScale)
|
||||
{
|
||||
return LayoutDeviceIntPoint(NSToIntFloor(aPt.x * aBackingScale),
|
||||
NSToIntFloor(aPt.y * aBackingScale));
|
||||
}
|
||||
|
||||
static LayoutDeviceIntRect
|
||||
CocoaPointsToDevPixels(const NSRect& aRect, CGFloat aBackingScale)
|
||||
{
|
||||
|
||||
@@ -25,6 +25,12 @@
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsITimer.h"
|
||||
|
||||
// These are two messages that the code in winspool.drv on Windows 7 explicitly
|
||||
// waits for while it is pumping other Windows messages, during display of the
|
||||
// Printer Properties dialog.
|
||||
#define MOZ_WM_PRINTER_PROPERTIES_COMPLETION 0x5b7a
|
||||
#define MOZ_WM_PRINTER_PROPERTIES_FAILURE 0x5b7f
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::widget;
|
||||
|
||||
@@ -367,6 +373,15 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
|
||||
continue; // the message is consumed.
|
||||
}
|
||||
|
||||
// Store Printer Properties messages for reposting, because they are not
|
||||
// processed by a window procedure, but are explicitly waited for in the
|
||||
// winspool.drv code that will be further up the stack.
|
||||
if (msg.message == MOZ_WM_PRINTER_PROPERTIES_COMPLETION ||
|
||||
msg.message == MOZ_WM_PRINTER_PROPERTIES_FAILURE) {
|
||||
mMsgsToRepost.push_back(msg);
|
||||
continue;
|
||||
}
|
||||
|
||||
::TranslateMessage(&msg);
|
||||
::DispatchMessageW(&msg);
|
||||
}
|
||||
@@ -402,3 +417,16 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
|
||||
|
||||
return gotMessage;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsAppShell::AfterProcessNextEvent(nsIThreadInternal* /* unused */,
|
||||
bool /* unused */)
|
||||
{
|
||||
if (!mMsgsToRepost.empty()) {
|
||||
for (MSG msg : mMsgsToRepost) {
|
||||
::PostMessageW(msg.hwnd, msg.message, msg.wParam, msg.lParam);
|
||||
}
|
||||
mMsgsToRepost.clear();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "nsBaseAppShell.h"
|
||||
#include <windows.h>
|
||||
#include <vector>
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
@@ -34,6 +35,9 @@ public:
|
||||
|
||||
static UINT GetTaskbarButtonCreatedMessage();
|
||||
|
||||
NS_IMETHOD AfterProcessNextEvent(nsIThreadInternal* thread,
|
||||
bool eventWasProcessed) final;
|
||||
|
||||
protected:
|
||||
NS_IMETHOD Run();
|
||||
NS_IMETHOD Exit();
|
||||
@@ -49,6 +53,7 @@ protected:
|
||||
|
||||
Mutex mLastNativeEventScheduledMutex;
|
||||
TimeStamp mLastNativeEventScheduled;
|
||||
std::vector<MSG> mMsgsToRepost;
|
||||
};
|
||||
|
||||
#endif // nsAppShell_h__
|
||||
|
||||
@@ -634,13 +634,8 @@ RegisterStaticAtoms(const nsStaticAtom* aAtoms, uint32_t aAtomCount)
|
||||
if (!atom->IsStaticAtom()) {
|
||||
nsAutoCString name;
|
||||
atom->ToUTF8String(name);
|
||||
|
||||
static char sCrashReason[1024];
|
||||
SprintfLiteral(sCrashReason,
|
||||
"static atom registration for %s should be pushed back",
|
||||
name.get());
|
||||
MOZ_CRASH_ANNOTATE(sCrashReason);
|
||||
MOZ_REALLY_CRASH();
|
||||
MOZ_CRASH_UNSAFE_PRINTF(
|
||||
"Static atom registration for %s should be pushed back", name.get());
|
||||
}
|
||||
} else {
|
||||
atom = new StaticAtom(stringBuffer, stringLen, hash);
|
||||
|
||||
+4
-11
@@ -9,6 +9,7 @@
|
||||
#include "nsXPCOM.h"
|
||||
#include "nsDebug.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/IntegerPrintfMacros.h"
|
||||
|
||||
nsTArrayHeader nsTArrayHeader::sEmptyHdr = { 0, 0, 0 };
|
||||
|
||||
@@ -22,15 +23,7 @@ IsTwiceTheRequiredBytesRepresentableAsUint32(size_t aCapacity, size_t aElemSize)
|
||||
MOZ_NORETURN MOZ_COLD void
|
||||
InvalidArrayIndex_CRASH(size_t aIndex, size_t aLength)
|
||||
{
|
||||
const size_t CAPACITY = 512;
|
||||
// Leak the buffer on the heap to make sure that it lives long enough, as
|
||||
// MOZ_CRASH_ANNOTATE expects the pointer passed to it to live to the end of
|
||||
// the program.
|
||||
auto* buffer = new char[CAPACITY];
|
||||
snprintf(buffer, CAPACITY,
|
||||
"ElementAt(aIndex = %llu, aLength = %llu)",
|
||||
(long long unsigned) aIndex,
|
||||
(long long unsigned) aLength);
|
||||
MOZ_CRASH_ANNOTATE(buffer);
|
||||
MOZ_REALLY_CRASH();
|
||||
MOZ_CRASH_UNSAFE_PRINTF(
|
||||
"ElementAt(aIndex = %" PRIu64 ", aLength = %" PRIu64 ")",
|
||||
static_cast<uint64_t>(aIndex), static_cast<uint64_t>(aLength));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user