diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index d017134e6c..3b35c651e6 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -496,76 +496,6 @@ bool ScriptErrorEvent::sHandlingScriptError = false; // soon. namespace xpc { -void -SystemErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) -{ - JS::Rooted exception(cx); - ::JS_GetPendingException(cx, &exception); - - // Note: we must do this before running any more code on cx. - ::JS_ClearPendingException(cx); - - MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext()); - nsCOMPtr globalObject; - - // The eventual plan is for error reporting to happen in the AutoJSAPI - // destructor using the global with which the AutoJSAPI was initialized. We - // can't _quite_ do that yet, so we take a sloppy stab at those semantics. If - // we have an nsIScriptContext, we'll get the right answer modulo - // non-current-inners. - // - // Otherwise, we just use the privileged junk scope. This has the effect of - // causing us to report the error as "chrome javascript" rather than "content - // javascript", and not invoking any error reporters. This is exactly what we - // want here. - if (nsIScriptContext* scx = GetScriptContextFromJSContext(cx)) { - nsCOMPtr outer = do_QueryInterface(scx->GetGlobalObject()); - if (outer) { - globalObject = static_cast(outer->GetCurrentInnerWindow()); - } - } - - // We run addons in a separate privileged compartment, but they still expect - // to trigger the onerror handler of their associated DOMWindow. - // - // Note that the way we do this right now is sloppy. Error reporters can - // theoretically be triggered at arbitrary times (not just immediately before - // an AutoJSAPI comes off the stack), so we don't really have a way of knowing - // that the global of the current compartment is the correct global with which - // to report the error. But in practice this is probably fine for the time - // being, and will get cleaned up soon when we fix bug 981187. - if (!globalObject && JS::CurrentGlobalOrNull(cx)) { - globalObject = xpc::AddonWindowOrNull(JS::CurrentGlobalOrNull(cx)); - } - - if (!globalObject) { - globalObject = xpc::NativeGlobal(xpc::PrivilegedJunkScope()); - } - - if (globalObject) { - RefPtr xpcReport = new xpc::ErrorReport(); - bool isChrome = nsContentUtils::IsSystemPrincipal(globalObject->PrincipalOrNull()); - nsCOMPtr win = do_QueryInterface(globalObject); - xpcReport->Init(report, message, isChrome, win ? win->WindowID() : 0); - - // If we can't dispatch an event to a window, report it to the console - // directly. This includes the case where the error was an OOM, because - // triggering a scripted event handler is likely to generate further OOMs. - if (!win || JSREPORT_IS_WARNING(xpcReport->mFlags) || - report->errorNumber == JSMSG_OUT_OF_MEMORY) - { - JS::Rooted stack(cx, - xpc::FindExceptionStackForConsoleReport(win, exception)); - xpcReport->LogToConsoleWithStack(stack); - return; - } - - // Otherwise, we need to asynchronously invoke onerror before we can decide - // whether or not to report the error to the console. - DispatchScriptErrorEvent(win, JS_GetRuntime(cx), xpcReport, exception); - } -} - void DispatchScriptErrorEvent(nsPIDOMWindow *win, JSRuntime *rt, xpc::ErrorReport *xpcReport, JS::Handle exception) diff --git a/gfx/layers/apz/reftests/reftest.list b/gfx/layers/apz/reftests/reftest.list deleted file mode 100644 index fc76ab5384..0000000000 --- a/gfx/layers/apz/reftests/reftest.list +++ /dev/null @@ -1,14 +0,0 @@ -# The following tests test the async positioning of the scrollbars. -# Basic root-frame scrollbar with async scrolling -chaos-mode skip-if(!asyncPan) == async-scrollbar-1-v.html async-scrollbar-1-v-ref.html -chaos-mode skip-if(!asyncPan) == async-scrollbar-1-h.html async-scrollbar-1-h-ref.html -chaos-mode skip-if(!asyncPan) == async-scrollbar-1-vh.html async-scrollbar-1-vh-ref.html -chaos-mode skip-if(!asyncPan) == async-scrollbar-1-v-rtl.html async-scrollbar-1-v-rtl-ref.html -chaos-mode skip-if(!asyncPan) == async-scrollbar-1-h-rtl.html async-scrollbar-1-h-rtl-ref.html -chaos-mode skip-if(!asyncPan) == async-scrollbar-1-vh-rtl.html async-scrollbar-1-vh-rtl-ref.html - -# Different async zoom levels. Since the scrollthumb gets async-scaled in the -# compositor, the border-radius ends of the scrollthumb are going to be a little -# off, hence the fuzzy-if clauses. -chaos-mode skip-if(!asyncZoom) fuzzy-if(B2G,98,82) == async-scrollbar-zoom-1.html async-scrollbar-zoom-1-ref.html -chaos-mode skip-if(!asyncZoom) fuzzy-if(B2G,94,146) == async-scrollbar-zoom-2.html async-scrollbar-zoom-2-ref.html diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp index 0dcfffe5aa..9eacdb2233 100644 --- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -1770,7 +1770,7 @@ APZCTreeManager::GetAPZCAtPoint(HitTestingTreeNode* aNode, MOZ_ASSERT(result); } APZCTM_LOG("Successfully matched APZC %p via node %p (hit result %d)\n", - result, aNode, *aOutHitResult); + result, resultNode, *aOutHitResult); return result; } diff --git a/gfx/layers/apz/src/InputBlockState.cpp b/gfx/layers/apz/src/InputBlockState.cpp index d57893ef82..a92c264ff9 100644 --- a/gfx/layers/apz/src/InputBlockState.cpp +++ b/gfx/layers/apz/src/InputBlockState.cpp @@ -25,7 +25,8 @@ static uint64_t sBlockCounter = InputBlockState::NO_BLOCK_ID + 1; InputBlockState::InputBlockState(const RefPtr& aTargetApzc, bool aTargetConfirmed) : mTargetApzc(aTargetApzc) - , mTargetConfirmed(aTargetConfirmed) + , mTargetConfirmed(aTargetConfirmed ? TargetConfirmationState::eConfirmed + : TargetConfirmationState::eUnconfirmed) , mBlockId(sBlockCounter++) , mTransformToApzc(aTargetApzc->GetTransformToThis()) { @@ -35,12 +36,23 @@ InputBlockState::InputBlockState(const RefPtr& aTargetAp } bool -InputBlockState::SetConfirmedTargetApzc(const RefPtr& aTargetApzc) +InputBlockState::SetConfirmedTargetApzc(const RefPtr& aTargetApzc, + TargetConfirmationState aState) { - if (mTargetConfirmed) { + MOZ_ASSERT(aState == TargetConfirmationState::eConfirmed + || aState == TargetConfirmationState::eTimedOut); + + if (mTargetConfirmed == TargetConfirmationState::eTimedOut && + aState == TargetConfirmationState::eConfirmed) { + // The main thread finally responded. We had already timed out the + // confirmation, but we want to update the state internally so that we + // can record the time for telemetry purposes. + mTargetConfirmed = TargetConfirmationState::eTimedOutAndMainThreadResponded; + } + if (mTargetConfirmed != TargetConfirmationState::eUnconfirmed) { return false; } - mTargetConfirmed = true; + mTargetConfirmed = aState; TBS_LOG("%p got confirmed target APZC %p\n", this, mTargetApzc.get()); if (mTargetApzc == aTargetApzc) { @@ -85,7 +97,14 @@ InputBlockState::GetBlockId() const bool InputBlockState::IsTargetConfirmed() const { - return mTargetConfirmed; + return mTargetConfirmed != TargetConfirmationState::eUnconfirmed; +} + +bool +InputBlockState::HasReceivedRealConfirmedTarget() const +{ + return mTargetConfirmed == TargetConfirmationState::eConfirmed || + mTargetConfirmed == TargetConfirmationState::eTimedOutAndMainThreadResponded; } bool @@ -192,7 +211,7 @@ CancelableBlockState::IsDefaultPrevented() const bool CancelableBlockState::HasReceivedAllContentNotifications() const { - return IsTargetConfirmed() && mContentResponded; + return HasReceivedRealConfirmedTarget() && mContentResponded; } bool @@ -359,7 +378,8 @@ WheelBlockState::SetContentResponse(bool aPreventDefault) } bool -WheelBlockState::SetConfirmedTargetApzc(const RefPtr& aTargetApzc) +WheelBlockState::SetConfirmedTargetApzc(const RefPtr& aTargetApzc, + TargetConfirmationState aState) { // The APZC that we find via APZCCallbackHelpers may not be the same APZC // ESM or OverscrollHandoff would have computed. Make sure we get the right @@ -370,7 +390,7 @@ WheelBlockState::SetConfirmedTargetApzc(const RefPtr& aT apzc = apzc->BuildOverscrollHandoffChain()->FindFirstScrollable(event); } - InputBlockState::SetConfirmedTargetApzc(apzc); + InputBlockState::SetConfirmedTargetApzc(apzc, aState); return true; } @@ -603,7 +623,8 @@ PanGestureBlockState::PanGestureBlockState(const RefPtr& } bool -PanGestureBlockState::SetConfirmedTargetApzc(const RefPtr& aTargetApzc) +PanGestureBlockState::SetConfirmedTargetApzc(const RefPtr& aTargetApzc, + TargetConfirmationState aState) { // The APZC that we find via APZCCallbackHelpers may not be the same APZC // ESM or OverscrollHandoff would have computed. Make sure we get the right @@ -618,7 +639,7 @@ PanGestureBlockState::SetConfirmedTargetApzc(const RefPtr& aTargetApzc, bool aTargetConfirmed); virtual ~InputBlockState() {} - virtual bool SetConfirmedTargetApzc(const RefPtr& aTargetApzc); + virtual bool SetConfirmedTargetApzc(const RefPtr& aTargetApzc, + TargetConfirmationState aState); const RefPtr& GetTargetApzc() const; const RefPtr& GetOverscrollHandoffChain() const; uint64_t GetBlockId() const; bool IsTargetConfirmed() const; + bool HasReceivedRealConfirmedTarget() const; void SetScrolledApzc(AsyncPanZoomController* aApzc); AsyncPanZoomController* GetScrolledApzc() const; @@ -65,7 +74,7 @@ private: private: RefPtr mTargetApzc; - bool mTargetConfirmed; + TargetConfirmationState mTargetConfirmed; const uint64_t mBlockId; // The APZC that was actually scrolled by events in this input block. @@ -227,7 +236,8 @@ public: void HandleEvents() override; bool MustStayActive() override; const char* Type() override; - bool SetConfirmedTargetApzc(const RefPtr& aTargetApzc) override; + bool SetConfirmedTargetApzc(const RefPtr& aTargetApzc, + TargetConfirmationState aState) override; void AddEvent(const ScrollWheelInput& aEvent); @@ -348,7 +358,8 @@ public: void HandleEvents() override; bool MustStayActive() override; const char* Type() override; - bool SetConfirmedTargetApzc(const RefPtr& aTargetApzc) override; + bool SetConfirmedTargetApzc(const RefPtr& aTargetApzc, + TargetConfirmationState aState) override; void AddEvent(const PanGestureInput& aEvent); diff --git a/gfx/layers/apz/src/InputQueue.cpp b/gfx/layers/apz/src/InputQueue.cpp index 321d1e183e..72180c3d7b 100644 --- a/gfx/layers/apz/src/InputQueue.cpp +++ b/gfx/layers/apz/src/InputQueue.cpp @@ -118,7 +118,8 @@ InputQueue::ReceiveTouchInput(const RefPtr& aTarget, // from a fast fling to a pinch state (i.e. second finger goes down while // the first finger is moving). block->SetDuringFastFling(); - block->SetConfirmedTargetApzc(aTarget); + block->SetConfirmedTargetApzc(aTarget, + InputBlockState::TargetConfirmationState::eConfirmed); if (gfxPrefs::TouchActionEnabled()) { block->SetAllowedTouchBehaviors(currentBehaviors); } @@ -334,22 +335,25 @@ InputQueue::ReceivePanGestureInput(const RefPtr& aTarget block = mInputBlockQueue.LastElement()->AsPanGestureBlock(); } + PanGestureInput event = aEvent; nsEventStatus result = nsEventStatus_eConsumeDoDefault; if (!block || block->WasInterrupted()) { - if (aEvent.mType != PanGestureInput::PANGESTURE_START) { - // Only PANGESTURE_START events are allowed to start a new pan gesture block. - INPQ_LOG("pangesture block %p was interrupted %d\n", block, - block ? block->WasInterrupted() : 0); - return nsEventStatus_eConsumeDoDefault; + if (event.mType != PanGestureInput::PANGESTURE_START) { + // Only PANGESTURE_START events are allowed to start a new pan gesture + // block, but we really want to start a new block here, so we magically + // turn this input into a PANGESTURE_START. + INPQ_LOG("transmogrifying pan input %d to PANGESTURE_START for new block\n", + event.mType); + event.mType = PanGestureInput::PANGESTURE_START; } - block = new PanGestureBlockState(aTarget, aTargetConfirmed, aEvent); + block = new PanGestureBlockState(aTarget, aTargetConfirmed, event); INPQ_LOG("started new pan gesture block %p id %" PRIu64 " for target %p\n", block, block->GetBlockId(), aTarget.get()); if (aTargetConfirmed && - aEvent.mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection && - !CanScrollTargetHorizontally(aEvent, block)) { + event.mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection && + !CanScrollTargetHorizontally(event, block)) { // This event may trigger a swipe gesture, depending on what our caller // wants to do it. We need to suspend handling of this block until we get // a content response which will tell us whether to proceed or abort the @@ -379,8 +383,8 @@ InputQueue::ReceivePanGestureInput(const RefPtr& aTarget // null) should take priority. This is equivalent to just always using the // target (confirmed or not) from the block, which is what // MaybeHandleCurrentBlock() does. - if (!MaybeHandleCurrentBlock(block, aEvent)) { - block->AddEvent(aEvent.AsPanGestureInput()); + if (!MaybeHandleCurrentBlock(block, event)) { + block->AddEvent(event.AsPanGestureInput()); } return result; @@ -583,7 +587,9 @@ InputQueue::MainThreadTimeout(const uint64_t& aInputBlockId) { // target apzc in the case where the main thread doesn't get back to us // fast enough. success = mInputBlockQueue[i]->TimeoutContentResponse(); - success |= mInputBlockQueue[i]->SetConfirmedTargetApzc(mInputBlockQueue[i]->GetTargetApzc()); + success |= mInputBlockQueue[i]->SetConfirmedTargetApzc( + mInputBlockQueue[i]->GetTargetApzc(), + InputBlockState::TargetConfirmationState::eTimedOut); break; } } @@ -621,7 +627,8 @@ InputQueue::SetConfirmedTargetApzc(uint64_t aInputBlockId, const RefPtrGetBlockId() == aInputBlockId) { - success = block->SetConfirmedTargetApzc(aTargetApzc); + success = block->SetConfirmedTargetApzc(aTargetApzc, + InputBlockState::TargetConfirmationState::eConfirmed); block->RecordContentResponseTime(); break; } @@ -644,7 +651,8 @@ InputQueue::ConfirmDragBlock(uint64_t aInputBlockId, const RefPtrAsDragBlock(); if (block && block->GetBlockId() == aInputBlockId) { block->SetDragMetrics(aDragMetrics); - success = block->SetConfirmedTargetApzc(aTargetApzc); + success = block->SetConfirmedTargetApzc(aTargetApzc, + InputBlockState::TargetConfirmationState::eConfirmed); block->RecordContentResponseTime(); break; } diff --git a/gfx/layers/apz/src/OverscrollHandoffState.h b/gfx/layers/apz/src/OverscrollHandoffState.h index 699965a6d6..1290309e65 100644 --- a/gfx/layers/apz/src/OverscrollHandoffState.h +++ b/gfx/layers/apz/src/OverscrollHandoffState.h @@ -16,43 +16,13 @@ #include "Units.h" // for ScreenPoint namespace mozilla { + +class InputData; + namespace layers { class AsyncPanZoomController; -/** - * A variant of NS_INLINE_DECL_THREADSAFE_REFCOUNTING which makes the refcount - * variable |mutable|, and the AddRef and Release methods |const|, to allow - * using an |RefPtr|, and thereby enforcing better const-correctness. - * This is currently here because OverscrollHandoffChain is the only thing - * currently using it. As a follow-up, we can move this to xpcom/glue, write - * a corresponding version for non-threadsafe refcounting, and perhaps - * transition other clients of NS_INLINE_DECL_[THREADSAFE_]REFCOUNTING to the - * mutable versions. - */ -#define NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING(_class) \ -public: \ - NS_METHOD_(MozExternalRefCountType) AddRef(void) const { \ - MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \ - MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \ - nsrefcnt count = ++mRefCnt; \ - NS_LOG_ADDREF(const_cast<_class*>(this), count, #_class, sizeof(*this)); \ - return (nsrefcnt) count; \ - } \ - NS_METHOD_(MozExternalRefCountType) Release(void) const { \ - MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \ - nsrefcnt count = --mRefCnt; \ - NS_LOG_RELEASE(const_cast<_class*>(this), count, #_class); \ - if (count == 0) { \ - delete (this); \ - return 0; \ - } \ - return count; \ - } \ -protected: \ - mutable ::mozilla::ThreadSafeAutoRefCnt mRefCnt; \ -public: - /** * This class represents the chain of APZCs along which overscroll is handed off. * It is created by APZCTreeManager by starting from an initial APZC which is @@ -71,7 +41,7 @@ public: // Mutable so that we can pass around the class by // RefPtr and thus enforce that, once built, // the chain is not modified. - NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING(OverscrollHandoffChain) + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OverscrollHandoffChain) /* * Methods for building the handoff chain. diff --git a/gfx/layers/apz/test/apz_test_native_event_utils.js b/gfx/layers/apz/test/apz_test_native_event_utils.js deleted file mode 100644 index 2c15897448..0000000000 --- a/gfx/layers/apz/test/apz_test_native_event_utils.js +++ /dev/null @@ -1,140 +0,0 @@ -// Utilities for synthesizing of native events. - -function getPlatform() { - if (navigator.platform.indexOf("Win") == 0) { - return "windows"; - } - if (navigator.platform.indexOf("Mac") == 0) { - return "mac"; - } - if (navigator.platform.indexOf("Linux") == 0) { - return "linux"; - } - return "unknown"; -} - -function nativeVerticalWheelEventMsg() { - switch (getPlatform()) { - case "windows": return 0x020A; // WM_MOUSEWHEEL - case "mac": return 0; // value is unused, can be anything - case "linux": return 4; // value is unused, pass GDK_SCROLL_SMOOTH anyway - } - throw "Native wheel events not supported on platform " + getPlatform(); -} - -function nativeHorizontalWheelEventMsg() { - switch (getPlatform()) { - case "windows": return 0x020E; // WM_MOUSEHWHEEL - case "mac": return 0; // value is unused, can be anything - case "linux": return 4; // value is unused, pass GDK_SCROLL_SMOOTH anyway - } - throw "Native wheel events not supported on platform " + getPlatform(); -} - -// Convert (aX, aY), in CSS pixels relative to aElement's bounding rect, -// to device pixels relative to aElement's containing window. -function coordinatesRelativeToWindow(aX, aY, aElement) { - var targetWindow = aElement.ownerDocument.defaultView; - var scale = targetWindow.devicePixelRatio; - var rect = aElement.getBoundingClientRect(); - return { - x: targetWindow.mozInnerScreenX + ((rect.left + aX) * scale), - y: targetWindow.mozInnerScreenY + ((rect.top + aY) * scale) - }; -} - -// Synthesizes a native mousewheel event and returns immediately. This does not -// guarantee anything; you probably want to use one of the other functions below -// which actually wait for results. -// aX and aY are relative to |window|'s top-left. aDeltaX and aDeltaY -// are pixel deltas, and aObserver can be left undefined if not needed. -function synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY, aObserver) { - var pt = coordinatesRelativeToWindow(aX, aY, aElement); - if (aDeltaX && aDeltaY) { - throw "Simultaneous wheeling of horizontal and vertical is not supported on all platforms."; - } - var msg = aDeltaX ? nativeHorizontalWheelEventMsg() : nativeVerticalWheelEventMsg(); - _getDOMWindowUtils().sendNativeMouseScrollEvent(pt.x, pt.y, msg, aDeltaX, aDeltaY, 0, 0, 0, aElement, aObserver); - return true; -} - -// Synthesizes a native mousewheel event and invokes the callback once the -// request has been successfully made to the OS. This does not necessarily -// guarantee that the OS generates the event we requested. See -// synthesizeNativeWheel for details on the parameters. -function synthesizeNativeWheelAndWaitForObserver(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) { - var observer = { - observe: function(aSubject, aTopic, aData) { - if (aCallback && aTopic == "mousescrollevent") { - setTimeout(aCallback, 0); - } - } - }; - return synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY, observer); -} - -// Synthesizes a native mousewheel event and invokes the callback once the -// wheel event is dispatched to the window. See synthesizeNativeWheel for -// details on the parameters. -function synthesizeNativeWheelAndWaitForWheelEvent(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) { - window.addEventListener("wheel", function wheelWaiter(e) { - window.removeEventListener("wheel", wheelWaiter); - setTimeout(aCallback, 0); - }); - return synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY); -} - -// Synthesizes a native mousewheel event and invokes the callback once the -// first resulting scroll event is dispatched to the window. -// See synthesizeNativeWheel for details on the parameters. -function synthesizeNativeWheelAndWaitForScrollEvent(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) { - var useCapture = true; // scroll events don't always bubble - window.addEventListener("scroll", function scrollWaiter(e) { - window.removeEventListener("scroll", scrollWaiter, useCapture); - setTimeout(aCallback, 0); - }, useCapture); - return synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY); -} - -// Synthesizes a native mouse move event and returns immediately. -// aX and aY are relative to the top-left of |aElement|'s containing window. -function synthesizeNativeMouseMove(aElement, aX, aY) { - var pt = coordinatesRelativeToWindow(aX, aY, aElement); - _getDOMWindowUtils().sendNativeMouseEvent(pt.x, pt.y, nativeMouseMoveEventMsg(), 0, aElement); - return true; -} - -// Synthesizes a native mouse move event and invokes the callback once the -// mouse move event is dispatched to |aElement|'s containing window. If the event -// targets content in a subdocument, |aElement| should be inside the -// subdocument. See synthesizeNativeMouseMove for details on the other -// parameters. -function synthesizeNativeMouseMoveAndWaitForMoveEvent(aElement, aX, aY, aCallback) { - var targetWindow = aElement.ownerDocument.defaultView; - targetWindow.addEventListener("mousemove", function mousemoveWaiter(e) { - targetWindow.removeEventListener("mousemove", mousemoveWaiter); - setTimeout(aCallback, 0); - }); - return synthesizeNativeMouseMove(aElement, aX, aY); -} - -// Synthesizes a native touch event and dispatches it. aX and aY in CSS pixels -// relative to the top-left of |aElement|'s bounding rect. -function synthesizeNativeTouch(aElement, aX, aY, aType, aObserver = null, aTouchId = 0) { - var pt = coordinatesRelativeToWindow(aX, aY, aElement); - var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView); - utils.sendNativeTouchPoint(aTouchId, aType, pt.x, pt.y, 1, 90, aObserver); - return true; -} - -function synthesizeNativeDrag(aElement, aX, aY, aDeltaX, aDeltaY, aObserver = null, aTouchId = 0) { - synthesizeNativeTouch(aElement, aX, aY, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, aTouchId); - var steps = Math.max(Math.abs(aDeltaX), Math.abs(aDeltaY)); - for (var i = 1; i < steps; i++) { - var dx = i * (aDeltaX / steps); - var dy = i * (aDeltaY / steps); - synthesizeNativeTouch(aElement, aX + dx, aY + dy, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, aTouchId); - } - synthesizeNativeTouch(aElement, aX + aDeltaX, aY + aDeltaY, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, aTouchId); - return synthesizeNativeTouch(aElement, aX + aDeltaX, aY + aDeltaY, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, aObserver, aTouchId); -} diff --git a/gfx/layers/apz/test/apz_test_utils.js b/gfx/layers/apz/test/apz_test_utils.js deleted file mode 100644 index bc9830b9b1..0000000000 --- a/gfx/layers/apz/test/apz_test_utils.js +++ /dev/null @@ -1,118 +0,0 @@ -// Utilities for writing APZ tests using the framework added in bug 961289 - -// ---------------------------------------------------------------------- -// Functions that convert the APZ test data into a more usable form. -// Every place we have a WebIDL sequence whose elements are dictionaries -// with two elements, a key, and a value, we convert this into a JS -// object with a property for each key/value pair. (This is the structure -// we really want, but we can't express in directly in WebIDL.) -// ---------------------------------------------------------------------- - -function convertEntries(entries) { - var result = {}; - for (var i = 0; i < entries.length; ++i) { - result[entries[i].key] = entries[i].value; - } - return result; -} - -function convertScrollFrameData(scrollFrames) { - var result = {}; - for (var i = 0; i < scrollFrames.length; ++i) { - result[scrollFrames[i].scrollId] = convertEntries(scrollFrames[i].entries); - } - return result; -} - -function convertBuckets(buckets) { - var result = {}; - for (var i = 0; i < buckets.length; ++i) { - result[buckets[i].sequenceNumber] = convertScrollFrameData(buckets[i].scrollFrames); - } - return result; -} - -function convertTestData(testData) { - var result = {}; - result.paints = convertBuckets(testData.paints); - result.repaintRequests = convertBuckets(testData.repaintRequests); - return result; -} - -// ---------------------------------------------------------------- -// Utilities for reconstructing the structure of the APZC tree from -// 'parentScrollId' entries in the APZ test data. -// ---------------------------------------------------------------- - -// Create a node with scroll id 'id' in the APZC tree. -function makeNode(id) { - return {scrollId: id, children: []}; -} - -// Find a node with scroll id 'id' in the APZC tree rooted at 'root'. -function findNode(root, id) { - if (root.scrollId == id) { - return root; - } - for (var i = 0; i < root.children.length; ++i) { - var subtreeResult = findNode(root.children[i], id); - if (subtreeResult != null) { - return subtreeResult; - } - } - return null; -} - -// Add a child -> parent link to the APZC tree rooted at 'root'. -function addLink(root, child, parent) { - var parentNode = findNode(root, parent); - if (parentNode == null) { - parentNode = makeNode(parent); - root.children.push(parentNode); - } - parentNode.children.push(makeNode(child)); -} - -// Add a root node to the APZC tree. It will become a direct -// child of 'root'. -function addRoot(root, id) { - root.children.push(makeNode(id)); -} - -// Given APZ test data for a single paint on the compositor side, -// reconstruct the APZC tree structure from the 'parentScrollId' -// entries that were logged. More specifically, the subset of the -// APZC tree structure corresponding to the layer subtree for the -// content process that triggered the paint, is reconstructed (as -// the APZ test data only contains information abot this subtree). -function buildApzcTree(paint) { - // The APZC tree can potentially have multiple root nodes, - // so we invent a node that is the parent of all roots. - // This 'root' does not correspond to an APZC. - var root = makeNode(-1); - for (var scrollId in paint) { - if ("isRootForLayersId" in paint[scrollId]) { - addRoot(root, scrollId); - } else if ("parentScrollId" in paint[scrollId]) { - addLink(root, scrollId, paint[scrollId]["parentScrollId"]); - } - } - return root; -} - -function flushApzRepaints(aCallback, aWindow = window) { - if (!aCallback) { - throw "A callback must be provided!"; - } - var repaintDone = function() { - SpecialPowers.Services.obs.removeObserver(repaintDone, "apz-repaints-flushed", false); - setTimeout(aCallback, 0); - }; - SpecialPowers.Services.obs.addObserver(repaintDone, "apz-repaints-flushed", false); - if (SpecialPowers.getDOMWindowUtils(aWindow).flushApzRepaints()) { - dump("Flushed APZ repaints, waiting for callback...\n"); - } else { - dump("Flushing APZ repaints was a no-op, triggering callback directly...\n"); - repaintDone(); - } -} diff --git a/gfx/layers/apz/test/helper_basic_pan.html b/gfx/layers/apz/test/helper_basic_pan.html deleted file mode 100644 index 8876521683..0000000000 --- a/gfx/layers/apz/test/helper_basic_pan.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - Sanity panning test - - - - - -
- This div makes the page scrollable. -
- - diff --git a/gfx/layers/apz/test/helper_bug982141.html b/gfx/layers/apz/test/helper_bug982141.html deleted file mode 100644 index a9fcfd010c..0000000000 --- a/gfx/layers/apz/test/helper_bug982141.html +++ /dev/null @@ -1,147 +0,0 @@ - - - - - - - Test for Bug 982141, helper page - - - - - Mozilla Bug 982141 - -
- Line 1
- Line 2
- Line 3
- Line 4
- Line 5
- Line 6
- Line 7
- Line 8
- Line 9
- Line 10
- Line 11
- Line 12
- Line 13
- Line 14
- Line 15
- Line 16
- Line 17
- Line 18
- Line 19
- Line 20
- Line 21
- Line 22
- Line 23
- Line 24
- Line 25
- Line 26
- Line 27
- Line 28
- Line 29
- Line 30
- Line 31
- Line 32
- Line 33
- Line 34
- Line 35
- Line 36
- Line 37
- Line 38
- Line 39
- Line 40
- Line 41
- Line 42
- Line 43
- Line 44
- Line 45
- Line 46
- Line 40
- Line 48
- Line 49
- Line 50
-
- - diff --git a/gfx/layers/apz/test/helper_div_pan.html b/gfx/layers/apz/test/helper_div_pan.html deleted file mode 100644 index 8807611f66..0000000000 --- a/gfx/layers/apz/test/helper_div_pan.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - Sanity panning test for scrollable div - - - - - -
-
- This div makes the |outer| div scrollable. -
-
-
- This div makes the top-level page scrollable. -
- - diff --git a/gfx/layers/apz/test/helper_iframe1.html b/gfx/layers/apz/test/helper_iframe1.html deleted file mode 100644 index 047da96bd4..0000000000 --- a/gfx/layers/apz/test/helper_iframe1.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - -
-
-
- - diff --git a/gfx/layers/apz/test/helper_iframe2.html b/gfx/layers/apz/test/helper_iframe2.html deleted file mode 100644 index fee3883e95..0000000000 --- a/gfx/layers/apz/test/helper_iframe2.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - -
-
-
- - diff --git a/gfx/layers/apz/test/helper_iframe_pan.html b/gfx/layers/apz/test/helper_iframe_pan.html deleted file mode 100644 index e74be49ca0..0000000000 --- a/gfx/layers/apz/test/helper_iframe_pan.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - Sanity panning test for scrollable div - - - - - - -
- This div makes the top-level page scrollable. -
- - diff --git a/gfx/layers/apz/test/helper_subframe_style.css b/gfx/layers/apz/test/helper_subframe_style.css deleted file mode 100644 index a911bea711..0000000000 --- a/gfx/layers/apz/test/helper_subframe_style.css +++ /dev/null @@ -1,15 +0,0 @@ -body { - height: 500px; -} - -.inner-frame { - margin-top: 25%; - height: 200%; - width: 75%; - overflow: scroll; -} -.inner-content { - height: 200%; - width: 200%; - background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px); -} diff --git a/gfx/layers/apz/test/mochitest.ini b/gfx/layers/apz/test/mochitest.ini deleted file mode 100644 index 2834fe4c9b..0000000000 --- a/gfx/layers/apz/test/mochitest.ini +++ /dev/null @@ -1,8 +0,0 @@ -[DEFAULT] -support-files = apz_test_utils.js -[test_bug982141.html] -skip-if = toolkit != 'gonk' # bug 991198 -support-files = helper_bug982141.html -[test_bug1151663.html] -skip-if = toolkit != 'gonk' # bug 991198 -support-files = helper_bug1151663.html diff --git a/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js b/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js index ea4fcb28d5..6b8a43df93 100644 --- a/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js +++ b/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js @@ -31,19 +31,56 @@ function nativeHorizontalWheelEventMsg() { throw "Native wheel events not supported on platform " + getPlatform(); } +// Given a pixel scrolling delta, converts it to the platform's native units. +function nativeScrollUnits(aElement, aDimen) { + switch (getPlatform()) { + case "linux": { + // GTK deltas are treated as line height divided by 3 by gecko. + var targetWindow = aElement.ownerDocument.defaultView; + var lineHeight = targetWindow.getComputedStyle(aElement)["font-size"]; + return aDimen / (parseInt(lineHeight) * 3); + } + } + return aDimen; +} + +function nativeMouseMoveEventMsg() { + switch (getPlatform()) { + case "windows": return 1; // MOUSEEVENTF_MOVE + case "mac": return 5; // NSMouseMoved + case "linux": return 3; // GDK_MOTION_NOTIFY + } + throw "Native wheel events not supported on platform " + getPlatform(); +} + +// Convert (aX, aY), in CSS pixels relative to aElement's bounding rect, +// to device pixels relative to aElement's containing window. +function coordinatesRelativeToWindow(aX, aY, aElement) { + var targetWindow = aElement.ownerDocument.defaultView; + var scale = targetWindow.devicePixelRatio; + var rect = aElement.getBoundingClientRect(); + return { + x: (targetWindow.mozInnerScreenX + rect.left + aX) * scale, + y: (targetWindow.mozInnerScreenY + rect.top + aY) * scale + }; +} + // Synthesizes a native mousewheel event and returns immediately. This does not // guarantee anything; you probably want to use one of the other functions below // which actually wait for results. -// aX and aY are relative to |window|'s top-left. aDeltaX and aDeltaY -// are pixel deltas, and aObserver can be left undefined if not needed. +// aX and aY are relative to the top-left of |aElement|'s containing window. +// aDeltaX and aDeltaY are pixel deltas, and aObserver can be left undefined +// if not needed. function synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY, aObserver) { - aX += window.mozInnerScreenX; - aY += window.mozInnerScreenY; + var pt = coordinatesRelativeToWindow(aX, aY, aElement); if (aDeltaX && aDeltaY) { throw "Simultaneous wheeling of horizontal and vertical is not supported on all platforms."; } + aDeltaX = nativeScrollUnits(aElement, aDeltaX); + aDeltaY = nativeScrollUnits(aElement, aDeltaY); var msg = aDeltaX ? nativeHorizontalWheelEventMsg() : nativeVerticalWheelEventMsg(); - _getDOMWindowUtils().sendNativeMouseScrollEvent(aX, aY, msg, aDeltaX, aDeltaY, 0, 0, 0, aElement, aObserver); + var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView); + utils.sendNativeMouseScrollEvent(pt.x, pt.y, msg, aDeltaX, aDeltaY, 0, 0, 0, aElement, aObserver); return true; } @@ -63,13 +100,80 @@ function synthesizeNativeWheelAndWaitForObserver(aElement, aX, aY, aDeltaX, aDel } // Synthesizes a native mousewheel event and invokes the callback once the -// wheel event is dispatched to the window. See synthesizeNativeWheel for -// details on the parameters. -function synthesizeNativeWheelAndWaitForEvent(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) { - window.addEventListener("wheel", function wheelWaiter(e) { - window.removeEventListener("wheel", wheelWaiter); +// wheel event is dispatched to |aElement|'s containing window. If the event +// targets content in a subdocument, |aElement| should be inside the +// subdocument. See synthesizeNativeWheel for details on the other parameters. +function synthesizeNativeWheelAndWaitForWheelEvent(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) { + var targetWindow = aElement.ownerDocument.defaultView; + targetWindow.addEventListener("wheel", function wheelWaiter(e) { + targetWindow.removeEventListener("wheel", wheelWaiter); setTimeout(aCallback, 0); }); return synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY); } +// Synthesizes a native mousewheel event and invokes the callback once the +// first resulting scroll event is dispatched to |aElement|'s containing window. +// If the event targets content in a subdocument, |aElement| should be inside +// the subdocument. See synthesizeNativeWheel for details on the other +// parameters. +function synthesizeNativeWheelAndWaitForScrollEvent(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) { + var targetWindow = aElement.ownerDocument.defaultView; + var useCapture = true; // scroll events don't always bubble + targetWindow.addEventListener("scroll", function scrollWaiter(e) { + targetWindow.removeEventListener("scroll", scrollWaiter, useCapture); + setTimeout(aCallback, 0); + }, useCapture); + return synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY); +} + +// Synthesizes a native mouse move event and returns immediately. +// aX and aY are relative to the top-left of |aElement|'s containing window. +function synthesizeNativeMouseMove(aElement, aX, aY) { + var pt = coordinatesRelativeToWindow(aX, aY, aElement); + var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView); + utils.sendNativeMouseEvent(pt.x, pt.y, nativeMouseMoveEventMsg(), 0, aElement); + return true; +} + +// Synthesizes a native mouse move event and invokes the callback once the +// mouse move event is dispatched to |aElement|'s containing window. If the event +// targets content in a subdocument, |aElement| should be inside the +// subdocument. See synthesizeNativeMouseMove for details on the other +// parameters. +function synthesizeNativeMouseMoveAndWaitForMoveEvent(aElement, aX, aY, aCallback) { + var targetWindow = aElement.ownerDocument.defaultView; + targetWindow.addEventListener("mousemove", function mousemoveWaiter(e) { + targetWindow.removeEventListener("mousemove", mousemoveWaiter); + setTimeout(aCallback, 0); + }); + return synthesizeNativeMouseMove(aElement, aX, aY); +} + +// Synthesizes a native touch event and dispatches it. aX and aY in CSS pixels +// relative to the top-left of |aElement|'s bounding rect. +function synthesizeNativeTouch(aElement, aX, aY, aType, aObserver = null, aTouchId = 0) { + var pt = coordinatesRelativeToWindow(aX, aY, aElement); + var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView); + utils.sendNativeTouchPoint(aTouchId, aType, pt.x, pt.y, 1, 90, aObserver); + return true; +} + +function synthesizeNativeDrag(aElement, aX, aY, aDeltaX, aDeltaY, aObserver = null, aTouchId = 0) { + synthesizeNativeTouch(aElement, aX, aY, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, aTouchId); + var steps = Math.max(Math.abs(aDeltaX), Math.abs(aDeltaY)); + for (var i = 1; i < steps; i++) { + var dx = i * (aDeltaX / steps); + var dy = i * (aDeltaY / steps); + synthesizeNativeTouch(aElement, aX + dx, aY + dy, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, aTouchId); + } + synthesizeNativeTouch(aElement, aX + aDeltaX, aY + aDeltaY, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, aTouchId); + return synthesizeNativeTouch(aElement, aX + aDeltaX, aY + aDeltaY, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, aObserver, aTouchId); +} + +function synthesizeNativeTap(aElement, aX, aY, aObserver = null) { + var pt = coordinatesRelativeToWindow(aX, aY, aElement); + var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView); + utils.sendNativeTouchTap(pt.x, pt.y, false, aObserver); + return true; +} diff --git a/gfx/layers/apz/test/mochitest/apz_test_utils.js b/gfx/layers/apz/test/mochitest/apz_test_utils.js index 06eb7984bd..e82eda8de1 100644 --- a/gfx/layers/apz/test/mochitest/apz_test_utils.js +++ b/gfx/layers/apz/test/mochitest/apz_test_utils.js @@ -97,3 +97,26 @@ function flushApzRepaints(aCallback, aWindow = window) { repaintDone(); } } + +// Flush repaints, APZ pending repaints, and any repaints resulting from that +// flush. This is particularly useful if the test needs to reach some sort of +// "idle" state in terms of repaints. Usually just doing waitForAllPaints +// followed by flushApzRepaints is sufficient to flush all APZ state back to +// the main thread, but it can leave a paint scheduled which will get triggered +// at some later time. For tests that specifically test for painting at +// specific times, this method is the way to go. Even if in doubt, this is the +// preferred method as the extra step is "safe" and shouldn't interfere with +// most tests. +function waitForApzFlushedRepaints(aCallback) { + // First flush the main-thread paints and send transactions to the APZ + waitForAllPaints(function() { + // Then flush the APZ to make sure any repaint requests have been sent + // back to the main thread + flushApzRepaints(function() { + // Then flush the main-thread again to process the repaint requests. + // Once this is done, we should be in a stable state with nothing + // pending, so we can trigger the callback. + waitForAllPaints(aCallback); + }); + }); +} diff --git a/gfx/layers/apz/test/helper_bug1151663.html b/gfx/layers/apz/test/mochitest/helper_bug1151663.html similarity index 88% rename from gfx/layers/apz/test/helper_bug1151663.html rename to gfx/layers/apz/test/mochitest/helper_bug1151663.html index 15b3dc011a..ef2fde9a95 100644 --- a/gfx/layers/apz/test/helper_bug1151663.html +++ b/gfx/layers/apz/test/mochitest/helper_bug1151663.html @@ -55,15 +55,17 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1151663 // Convert the test data into a representation that's easier to navigate. contentTestData = convertTestData(contentTestData); compositorTestData = convertTestData(compositorTestData); + var paint = compositorTestData.paints[lastCompositorPaintSeqNo]; // Reconstruct the APZC tree structure in the last paint. - var apzcTree = buildApzcTree(compositorTestData.paints[lastCompositorPaintSeqNo]); + var apzcTree = buildApzcTree(paint); // The apzc tree for this page should consist of a single root APZC, - // and no child APZCs. - SimpleTest.is(apzcTree.children.length, 1, "expected a single root APZC"); - var rootApzc = apzcTree.children[0]; - SimpleTest.is(rootApzc.children.length, 0, "expected no child APZCs"); + // which either is the RCD with no child APZCs (e10s/B2G case) or has a + // single child APZC which is for the RCD (fennec case). + var rcd = findRcdNode(apzcTree); + SimpleTest.ok(rcd != null, "found the RCD node"); + SimpleTest.is(rcd.children.length, 0, "expected no children on the RCD"); window.opener.finishTest(); } diff --git a/gfx/layers/apz/test/mochitest/helper_bug982141.html b/gfx/layers/apz/test/mochitest/helper_bug982141.html index d81f65fd03..4167e64935 100644 --- a/gfx/layers/apz/test/mochitest/helper_bug982141.html +++ b/gfx/layers/apz/test/mochitest/helper_bug982141.html @@ -5,6 +5,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=982141 --> + Test for Bug 982141, helper page - + Mozilla Bug 982141 -
+
+
+ Wide content so that the vertical scrollbar for the parent div + doesn't eat into the 50px width and reduce the width of the + displayport. +
Line 1
Line 2
Line 3
diff --git a/gfx/layers/apz/test/mochitest/helper_scrollto_tap.html b/gfx/layers/apz/test/mochitest/helper_scrollto_tap.html new file mode 100644 index 0000000000..0c021ce9cf --- /dev/null +++ b/gfx/layers/apz/test/mochitest/helper_scrollto_tap.html @@ -0,0 +1,70 @@ + + + + + + Sanity touch-tapping test + + + + + + +
spacer
+ + + diff --git a/gfx/layers/apz/test/mochitest/helper_subframe_style.css b/gfx/layers/apz/test/mochitest/helper_subframe_style.css index a911bea711..5af9640802 100644 --- a/gfx/layers/apz/test/mochitest/helper_subframe_style.css +++ b/gfx/layers/apz/test/mochitest/helper_subframe_style.css @@ -3,7 +3,7 @@ body { } .inner-frame { - margin-top: 25%; + margin-top: 50px; /* this should be at least 30px */ height: 200%; width: 75%; overflow: scroll; diff --git a/gfx/layers/apz/test/mochitest/helper_tap.html b/gfx/layers/apz/test/mochitest/helper_tap.html new file mode 100644 index 0000000000..c7cbc4a98c --- /dev/null +++ b/gfx/layers/apz/test/mochitest/helper_tap.html @@ -0,0 +1,42 @@ + + + + + + Sanity touch-tapping test + + + + + + + + + diff --git a/gfx/layers/apz/test/mochitest/mochitest.ini b/gfx/layers/apz/test/mochitest/mochitest.ini index ea876dfd4e..d1de52a8ea 100644 --- a/gfx/layers/apz/test/mochitest/mochitest.ini +++ b/gfx/layers/apz/test/mochitest/mochitest.ini @@ -1,17 +1,22 @@ -[test_bug982141.html] -skip-if = toolkit != 'gonk' # bug 991198 +[DEFAULT] support-files = apz_test_utils.js + apz_test_native_event_utils.js helper_bug982141.html + helper_bug1151663.html helper_iframe1.html helper_iframe2.html helper_subframe_style.css helper_basic_pan.html helper_div_pan.html helper_iframe_pan.html + helper_scrollto_tap.html + helper_tap.html tags = apz +[test_bug982141.html] +[test_bug1151663.html] [test_wheel_scroll.html] -skip-if = (os == 'android') || (os == 'b2g') # wheel events not supported +skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet [test_wheel_transactions.html] skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet [test_bug1151667.html] @@ -28,3 +33,8 @@ skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel ev skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet [test_scroll_subframe_scrollbar.html] skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet +[test_frame_reconstruction.html] +[test_tap.html] +# Windows touch injection doesn't work in automation, but this test can be run locally on a windows touch device. +# On OS X we don't support touch events at all. +skip-if = (toolkit == 'windows') || (toolkit == 'cocoa') diff --git a/gfx/layers/apz/test/mochitest/test_basic_pan.html b/gfx/layers/apz/test/mochitest/test_basic_pan.html index 455b60135e..d2b2e3267e 100644 --- a/gfx/layers/apz/test/mochitest/test_basic_pan.html +++ b/gfx/layers/apz/test/mochitest/test_basic_pan.html @@ -8,7 +8,6 @@ + + + + + + + + +Mozilla Bug 1235899 +

+
+

You should be able to fling this list without it stopping abruptly

+
+
+
    +
  1. Some text
  2. +
  3. Some text
  4. +
  5. Some text
  6. +
  7. Some text
  8. +
  9. Some text
  10. +
  11. Some text
  12. +
  13. Some text
  14. +
  15. Some text
  16. +
  17. Some text
  18. +
  19. Some text
  20. +
  21. Some text
  22. +
  23. Some text
  24. +
  25. Some text
  26. +
  27. Some text
  28. +
  29. Some text
  30. +
  31. Some text
  32. +
  33. Some text
  34. +
  35. Some text
  36. +
  37. Some text
  38. +
  39. Some text
  40. +
  41. Some text
  42. +
  43. Some text
  44. +
  45. Some text
  46. +
  47. Some text
  48. +
  49. Some text
  50. +
  51. Some text
  52. +
  53. Some text
  54. +
  55. Some text
  56. +
  57. Some text
  58. +
  59. Some text
  60. +
  61. Some text
  62. +
  63. Some text
  64. +
  65. Some text
  66. +
  67. Some text
  68. +
  69. Some text
  70. +
  71. Some text
  72. +
  73. Some text
  74. +
  75. Some text
  76. +
  77. Some text
  78. +
  79. Some text
  80. +
  81. Some text
  82. +
  83. Some text
  84. +
  85. Some text
  86. +
  87. Some text
  88. +
  89. Some text
  90. +
  91. Some text
  92. +
  93. Some text
  94. +
  95. Some text
  96. +
  97. Some text
  98. +
  99. Some text
  100. +
  101. Some text
  102. +
  103. Some text
  104. +
  105. Some text
  106. +
  107. Some text
  108. +
  109. Some text
  110. +
  111. Some text
  112. +
  113. Some text
  114. +
  115. Some text
  116. +
  117. Some text
  118. +
  119. Some text
  120. +
  121. Some text
  122. +
  123. Some text
  124. +
  125. Some text
  126. +
  127. Some text
  128. +
  129. Some text
  130. +
  131. Some text
  132. +
  133. Some text
  134. +
  135. Some text
  136. +
  137. Some text
  138. +
  139. Some text
  140. +
  141. Some text
  142. +
  143. Some text
  144. +
  145. Some text
  146. +
  147. Some text
  148. +
  149. Some text
  150. +
  151. Some text
  152. +
  153. Some text
  154. +
  155. Some text
  156. +
  157. Some text
  158. +
  159. Some text
  160. +
  161. Some text
  162. +
  163. Some text
  164. +
  165. Some text
  166. +
  167. Some text
  168. +
  169. Some text
  170. +
  171. Some text
  172. +
  173. Some text
  174. +
  175. Some text
  176. +
  177. Some text
  178. +
  179. Some text
  180. +
  181. Some text
  182. +
  183. Some text
  184. +
  185. Some text
  186. +
  187. Some text
  188. +
  189. Some text
  190. +
  191. Some text
  192. +
  193. Some text
  194. +
  195. Some text
  196. +
  197. Some text
  198. +
  199. Some text
  200. +
  201. Some text
  202. +
  203. Some text
  204. +
+
+
+
+ +
+
+
+
diff --git a/gfx/layers/apz/test/mochitest/test_layerization.html b/gfx/layers/apz/test/mochitest/test_layerization.html
index 99e25edceb..1ba093f1ec 100644
--- a/gfx/layers/apz/test/mochitest/test_layerization.html
+++ b/gfx/layers/apz/test/mochitest/test_layerization.html
@@ -54,7 +54,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1173580
 
 
diff --git a/gfx/layers/apz/test/mochitest/test_scroll_inactive_flattened_frame.html b/gfx/layers/apz/test/mochitest/test_scroll_inactive_flattened_frame.html
index 464e92bd67..e82b2252fb 100644
--- a/gfx/layers/apz/test/mochitest/test_scroll_inactive_flattened_frame.html
+++ b/gfx/layers/apz/test/mochitest/test_scroll_inactive_flattened_frame.html
@@ -5,6 +5,7 @@
   
   
   
+  
   
 
 
@@ -35,15 +36,22 @@ function test() {
   });
 }
 
+function startTest() {
+  waitForAllPaints(function() {
+    flushApzRepaints(test);
+  });
+}
+
 window.onload = function() {
   SpecialPowers.pushPrefEnv({
     'set': [['general.smoothScroll', false],
             ['mousewheel.transaction.timeout', 1000000]],
   }, function () {
-    SimpleTest.waitForFocus(test);
+    SimpleTest.waitForFocus(startTest);
   });
 }
 
+SimpleTest.testInChaosMode();
 SimpleTest.waitForExplicitFinish();
 
 
diff --git a/gfx/layers/apz/test/mochitest/test_scroll_subframe_scrollbar.html b/gfx/layers/apz/test/mochitest/test_scroll_subframe_scrollbar.html
index a605756c26..db39787ffb 100644
--- a/gfx/layers/apz/test/mochitest/test_scroll_subframe_scrollbar.html
+++ b/gfx/layers/apz/test/mochitest/test_scroll_subframe_scrollbar.html
@@ -5,6 +5,7 @@
   
   
   
+  
   
 
 
-
+
 
diff --git a/gfx/layers/apz/reftests/async-scrollbar-1-vh-rtl.html b/gfx/layers/apz/test/reftest/async-scrollbar-1-vh-rtl.html similarity index 88% rename from gfx/layers/apz/reftests/async-scrollbar-1-vh-rtl.html rename to gfx/layers/apz/test/reftest/async-scrollbar-1-vh-rtl.html index 5a7ae977d8..397d1cf9bc 100644 --- a/gfx/layers/apz/reftests/async-scrollbar-1-vh-rtl.html +++ b/gfx/layers/apz/test/reftest/async-scrollbar-1-vh-rtl.html @@ -1,7 +1,7 @@ + reftest-async-scroll-x="-440" reftest-async-scroll-y="7999"> diff --git a/gfx/layers/apz/reftests/async-scrollbar-1-vh.html b/gfx/layers/apz/test/reftest/async-scrollbar-1-vh.html similarity index 87% rename from gfx/layers/apz/reftests/async-scrollbar-1-vh.html rename to gfx/layers/apz/test/reftest/async-scrollbar-1-vh.html index 0e5dbd42e0..a1d1527ddc 100644 --- a/gfx/layers/apz/reftests/async-scrollbar-1-vh.html +++ b/gfx/layers/apz/test/reftest/async-scrollbar-1-vh.html @@ -1,7 +1,7 @@ + reftest-async-scroll-x="449" reftest-async-scroll-y="7999"> - - Test for Bug 1151667 - - - - - - - -Mozilla Bug 1151667 -

-
- -
-
- -
-
-
-
- - diff --git a/gfx/layers/apz/test/test_bug982141.html b/gfx/layers/apz/test/test_bug982141.html deleted file mode 100644 index 139f8486eb..0000000000 --- a/gfx/layers/apz/test/test_bug982141.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - Test for Bug 982141 - - - - - - Mozilla Bug 982141 - - diff --git a/gfx/layers/apz/test/test_layerization.html b/gfx/layers/apz/test/test_layerization.html deleted file mode 100644 index 0eeb7c1c65..0000000000 --- a/gfx/layers/apz/test/test_layerization.html +++ /dev/null @@ -1,168 +0,0 @@ - - - - - Test for layerization - - - - - - - - - -APZ layerization tests -

-
-
-
-
-
-
-
-
-
-
-
- - - -
-
-
-
-
- - diff --git a/gfx/layers/apz/test/test_scroll_inactive_flattened_frame.html b/gfx/layers/apz/test/test_scroll_inactive_flattened_frame.html deleted file mode 100644 index 464e92bd67..0000000000 --- a/gfx/layers/apz/test/test_scroll_inactive_flattened_frame.html +++ /dev/null @@ -1,50 +0,0 @@ - - - - Test scrolling flattened inactive frames - - - - - - -
-
-
-
-
-
- - - diff --git a/gfx/layers/apz/test/test_wheel_scroll.html b/gfx/layers/apz/test/test_wheel_scroll.html deleted file mode 100644 index 8fe8980bfe..0000000000 --- a/gfx/layers/apz/test/test_wheel_scroll.html +++ /dev/null @@ -1,117 +0,0 @@ - - - - - Test for Bug 1013412 - - - - - - - -Mozilla Bug 1161206 -

-
-

Scrolling the page should be async, but scrolling over the dark circle should not scroll the page and instead rotate the white ball.

-
-
-
-
-
-
-
-
-
- - - diff --git a/gfx/layers/apz/test/test_wheel_transactions.html b/gfx/layers/apz/test/test_wheel_transactions.html deleted file mode 100644 index eeba97a2ba..0000000000 --- a/gfx/layers/apz/test/test_wheel_transactions.html +++ /dev/null @@ -1,142 +0,0 @@ - - - - - Test for Bug 1175585 - - - - - - - -APZ wheel transactions test -

-
-
-
-
-
-
-
-
- - - diff --git a/gfx/layers/apz/util/APZCCallbackHelper.cpp b/gfx/layers/apz/util/APZCCallbackHelper.cpp index 5512608bc0..e68ad44c3f 100644 --- a/gfx/layers/apz/util/APZCCallbackHelper.cpp +++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp @@ -258,7 +258,6 @@ APZCCallbackHelper::UpdateRootFrame(FrameMetrics& aMetrics) // adjusts the display port margins, so do it before we set those. ScrollFrame(content, aMetrics); - MOZ_ASSERT(nsLayoutUtils::HasDisplayPort(content)); SetDisplayPortMargins(shell, content, aMetrics); SetPaintRequestTime(content, aMetrics.GetPaintRequestTime()); } diff --git a/gfx/layers/apz/util/ActiveElementManager.cpp b/gfx/layers/apz/util/ActiveElementManager.cpp index 95a6d94830..83e5db0e50 100644 --- a/gfx/layers/apz/util/ActiveElementManager.cpp +++ b/gfx/layers/apz/util/ActiveElementManager.cpp @@ -90,7 +90,10 @@ ActiveElementManager::TriggerElementActivation() if (!mCanBePan) { SetActive(mTarget); } else { + CancelTask(); // this is only needed because of bug 1169802. Fixing that + // bug properly should make this unnecessary. MOZ_ASSERT(mSetActiveTask == nullptr); + mSetActiveTask = NewRunnableMethod( this, &ActiveElementManager::SetActiveTask, mTarget); MessageLoop::current()->PostDelayedTask( diff --git a/gfx/layers/apz/util/ChromeProcessController.h b/gfx/layers/apz/util/ChromeProcessController.h index d0ab00eb6a..47b3d3535b 100644 --- a/gfx/layers/apz/util/ChromeProcessController.h +++ b/gfx/layers/apz/util/ChromeProcessController.h @@ -28,6 +28,7 @@ class APZEventState; // tree. class ChromeProcessController : public mozilla::layers::GeckoContentController { +protected: typedef mozilla::layers::FrameMetrics FrameMetrics; typedef mozilla::layers::ScrollableLayerGuid ScrollableLayerGuid; diff --git a/gfx/layers/basic/BasicLayerManager.cpp b/gfx/layers/basic/BasicLayerManager.cpp index 1555ef2b9e..9f1a0ffac5 100644 --- a/gfx/layers/basic/BasicLayerManager.cpp +++ b/gfx/layers/basic/BasicLayerManager.cpp @@ -864,7 +864,7 @@ BasicLayerManager::PaintLayer(gfxContext* aTarget, paintLayerContext.Apply2DTransform(); - const nsIntRegion visibleRegion = aLayer->GetLocalVisibleRegion().ToUnknownRegion(); + nsIntRegion visibleRegion = aLayer->GetLocalVisibleRegion().ToUnknownRegion(); // If needsGroup is true, we'll clip to the visible region after we've popped the group if (needsClipToVisibleRegion && !needsGroup) { gfxUtils::ClipToRegion(aTarget, visibleRegion); @@ -897,7 +897,7 @@ BasicLayerManager::PaintLayer(gfxContext* aTarget, return; } - const IntRect& bounds = visibleRegion.GetBounds(); + IntRect bounds = visibleRegion.GetBounds(); RefPtr untransformedDT = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(bounds.width, bounds.height), SurfaceFormat::B8G8R8A8); @@ -934,6 +934,7 @@ BasicLayerManager::PaintLayer(gfxContext* aTarget, ToRect(aTarget->GetClipExtents())); xformBounds.RoundOut(); effectiveTransform.PostTranslate(-xformBounds.x, -xformBounds.y, 0); + effectiveTransform.PreTranslate(bounds.x, bounds.y, 0); RefPtr untransformedSurf = untransformedDT->Snapshot(); RefPtr xformDT = diff --git a/gfx/layers/client/CanvasClient.cpp b/gfx/layers/client/CanvasClient.cpp index 986b80736d..c7ce73b7ab 100644 --- a/gfx/layers/client/CanvasClient.cpp +++ b/gfx/layers/client/CanvasClient.cpp @@ -71,14 +71,14 @@ void CanvasClient2D::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) { AutoRemoveTexture autoRemove(this); - if (mBuffer && - (mBuffer->IsImmutable() || mBuffer->GetSize() != aSize)) { - autoRemove.mTexture = mBuffer; - mBuffer = nullptr; + if (mBackBuffer && + (mBackBuffer->IsImmutable() || mBackBuffer->GetSize() != aSize)) { + autoRemove.mTexture = mBackBuffer; + mBackBuffer = nullptr; } bool bufferCreated = false; - if (!mBuffer) { + if (!mBackBuffer) { bool isOpaque = (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE); gfxContentType contentType = isOpaque ? gfxContentType::COLOR @@ -90,46 +90,48 @@ CanvasClient2D::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) flags |= TextureFlags::ORIGIN_BOTTOM_LEFT; } - mBuffer = CreateTextureClientForCanvas(surfaceFormat, aSize, flags, aLayer); - if (!mBuffer) { + mBackBuffer = CreateTextureClientForCanvas(surfaceFormat, aSize, flags, aLayer); + if (!mBackBuffer) { NS_WARNING("Failed to allocate the TextureClient"); return; } - MOZ_ASSERT(mBuffer->CanExposeDrawTarget()); + MOZ_ASSERT(mBackBuffer->CanExposeDrawTarget()); bufferCreated = true; } bool updated = false; { - TextureClientAutoLock autoLock(mBuffer, OpenMode::OPEN_WRITE_ONLY); + TextureClientAutoLock autoLock(mBackBuffer, OpenMode::OPEN_WRITE_ONLY); if (!autoLock.Succeeded()) { - mBuffer = nullptr; + mBackBuffer = nullptr; return; } - RefPtr target = mBuffer->BorrowDrawTarget(); + RefPtr target = mBackBuffer->BorrowDrawTarget(); if (target) { aLayer->UpdateTarget(target); updated = true; } } - if (bufferCreated && !AddTextureClient(mBuffer)) { - mBuffer = nullptr; + if (bufferCreated && !AddTextureClient(mBackBuffer)) { + mBackBuffer = nullptr; return; } if (updated) { AutoTArray textures; CompositableForwarder::TimedTextureClient* t = textures.AppendElement(); - t->mTextureClient = mBuffer; - t->mPictureRect = nsIntRect(nsIntPoint(0, 0), mBuffer->GetSize()); + t->mTextureClient = mBackBuffer; + t->mPictureRect = nsIntRect(nsIntPoint(0, 0), mBackBuffer->GetSize()); t->mFrameID = mFrameID; t->mInputFrameID = VRManagerChild::Get()->GetInputFrameID(); GetForwarder()->UseTextures(this, textures); - mBuffer->SyncWithObject(GetForwarder()->GetSyncObject()); + mBackBuffer->SyncWithObject(GetForwarder()->GetSyncObject()); } + + mBackBuffer.swap(mFrontBuffer); } already_AddRefed diff --git a/gfx/layers/client/CanvasClient.h b/gfx/layers/client/CanvasClient.h index 7a8a174851..93d13d2e75 100644 --- a/gfx/layers/client/CanvasClient.h +++ b/gfx/layers/client/CanvasClient.h @@ -99,7 +99,7 @@ public: virtual void Clear() override { - mBuffer = nullptr; + mBackBuffer = mFrontBuffer = nullptr; } virtual void Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) override; @@ -112,7 +112,7 @@ public: virtual void OnDetach() override { - mBuffer = nullptr; + mBackBuffer = mFrontBuffer = nullptr; } private: @@ -122,7 +122,8 @@ private: TextureFlags aFlags, ClientCanvasLayer* aLayer); - RefPtr mBuffer; + RefPtr mBackBuffer; + RefPtr mFrontBuffer; }; // Used for GL canvases where we don't need to do any readback, i.e., with a diff --git a/gfx/layers/client/ClientCanvasLayer.h b/gfx/layers/client/ClientCanvasLayer.h index 6e633ad4b7..8820923b93 100644 --- a/gfx/layers/client/ClientCanvasLayer.h +++ b/gfx/layers/client/ClientCanvasLayer.h @@ -62,6 +62,13 @@ public: } } + virtual void HandleMemoryPressure() override + { + if (mCanvasClient) { + mCanvasClient->HandleMemoryPressure(); + } + } + virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override { aAttrs = CanvasLayerAttributes(mFilter, mBounds); diff --git a/gfx/layers/client/ClientImageLayer.cpp b/gfx/layers/client/ClientImageLayer.cpp index ba9f444bdd..64c1c77702 100644 --- a/gfx/layers/client/ClientImageLayer.cpp +++ b/gfx/layers/client/ClientImageLayer.cpp @@ -60,6 +60,13 @@ protected: DestroyBackBuffer(); } + virtual void HandleMemoryPressure() override + { + if (mImageClient) { + mImageClient->HandleMemoryPressure(); + } + } + virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override { aAttrs = ImageLayerAttributes(mFilter, mScaleToSize, mScaleMode); diff --git a/gfx/layers/client/ClientLayerManager.cpp b/gfx/layers/client/ClientLayerManager.cpp index 75aee59908..20bbd9ea1b 100644 --- a/gfx/layers/client/ClientLayerManager.cpp +++ b/gfx/layers/client/ClientLayerManager.cpp @@ -762,6 +762,10 @@ ClientLayerManager::ClearCachedResources(Layer* aSubtree) void ClientLayerManager::HandleMemoryPressure() { + if (mRoot) { + HandleMemoryPressureLayer(mRoot); + } + for (size_t i = 0; i < mTexturePools.Length(); i++) { mTexturePools[i]->ShrinkToMinimumSize(); } @@ -777,6 +781,16 @@ ClientLayerManager::ClearLayer(Layer* aLayer) } } +void +ClientLayerManager::HandleMemoryPressureLayer(Layer* aLayer) +{ + ClientLayer::ToClientLayer(aLayer)->HandleMemoryPressure(); + for (Layer* child = aLayer->GetFirstChild(); child; + child = child->GetNextSibling()) { + HandleMemoryPressureLayer(child); + } +} + void ClientLayerManager::GetBackendName(nsAString& aName) { diff --git a/gfx/layers/client/ClientLayerManager.h b/gfx/layers/client/ClientLayerManager.h index a18f39f97a..4365871408 100644 --- a/gfx/layers/client/ClientLayerManager.h +++ b/gfx/layers/client/ClientLayerManager.h @@ -293,6 +293,8 @@ private: void ClearLayer(Layer* aLayer); + void HandleMemoryPressureLayer(Layer* aLayer); + bool EndTransactionInternal(DrawPaintedLayerCallback aCallback, void* aCallbackData, EndTransactionFlags); @@ -380,6 +382,10 @@ public: virtual void ClearCachedResources() { } + // Shrink memory usage. + // Called when "memory-pressure" is observed. + virtual void HandleMemoryPressure() { } + virtual void RenderLayer() = 0; virtual void RenderLayerWithReadback(ReadbackProcessor *aReadback) { RenderLayer(); } diff --git a/gfx/layers/client/ClientPaintedLayer.h b/gfx/layers/client/ClientPaintedLayer.h index 820745aafe..949f746aea 100644 --- a/gfx/layers/client/ClientPaintedLayer.h +++ b/gfx/layers/client/ClientPaintedLayer.h @@ -76,7 +76,14 @@ public: mValidRegion.SetEmpty(); DestroyBackBuffer(); } - + + virtual void HandleMemoryPressure() override + { + if (mContentClient) { + mContentClient->HandleMemoryPressure(); + } + } + virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override { aAttrs = PaintedLayerAttributes(GetValidRegion()); diff --git a/gfx/layers/client/ClientTiledPaintedLayer.h b/gfx/layers/client/ClientTiledPaintedLayer.h index fe4b4018db..9e1e003ee5 100644 --- a/gfx/layers/client/ClientTiledPaintedLayer.h +++ b/gfx/layers/client/ClientTiledPaintedLayer.h @@ -73,6 +73,13 @@ public: virtual void ClearCachedResources() override; + virtual void HandleMemoryPressure() override + { + if (mContentClient) { + mContentClient->HandleMemoryPressure(); + } + } + /** * Helper method to find the nearest ancestor layers which * scroll and have a displayport. The parameters are out-params diff --git a/gfx/layers/client/CompositableClient.cpp b/gfx/layers/client/CompositableClient.cpp index 96b54a8a75..6e875ab21c 100644 --- a/gfx/layers/client/CompositableClient.cpp +++ b/gfx/layers/client/CompositableClient.cpp @@ -243,7 +243,15 @@ void CompositableClient::ClearCachedResources() { if (mTextureClientRecycler) { - mTextureClientRecycler = nullptr; + mTextureClientRecycler->ShrinkToMinimumSize(); + } +} + +void +CompositableClient::HandleMemoryPressure() +{ + if (mTextureClientRecycler) { + mTextureClientRecycler->ShrinkToMinimumSize(); } } diff --git a/gfx/layers/client/CompositableClient.h b/gfx/layers/client/CompositableClient.h index 5631713287..eac59603da 100644 --- a/gfx/layers/client/CompositableClient.h +++ b/gfx/layers/client/CompositableClient.h @@ -195,6 +195,12 @@ public: */ virtual void ClearCachedResources(); + /** + * Shrink memory usage. + * Called when "memory-pressure" is observed. + */ + virtual void HandleMemoryPressure(); + /** * Should be called when deataching a TextureClient from a Compositable, because * some platforms need to do some extra book keeping when this happens (for diff --git a/gfx/layers/client/ContentClient.cpp b/gfx/layers/client/ContentClient.cpp index e911a3fd0f..3ede7dcb7c 100644 --- a/gfx/layers/client/ContentClient.cpp +++ b/gfx/layers/client/ContentClient.cpp @@ -357,7 +357,7 @@ ContentClientRemoteBuffer::GetUpdatedRegion(const nsIntRegion& aRegionToDraw, // changes and some changed buffer content isn't reflected in the // draw or invalidate region (on purpose!). When this happens, we // need to read back the entire buffer too. - updatedRegion = aVisibleRegion; + updatedRegion = aVisibleRegion.GetBounds(); mIsNewBuffer = false; } else { updatedRegion = aRegionToDraw; diff --git a/gfx/layers/client/ImageClient.cpp b/gfx/layers/client/ImageClient.cpp index b43847dbfe..142e9024b0 100644 --- a/gfx/layers/client/ImageClient.cpp +++ b/gfx/layers/client/ImageClient.cpp @@ -4,7 +4,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ImageClient.h" + #include // for uint32_t + +#include "ClientLayerManager.h" // for ClientLayer #include "ImageContainer.h" // for Image, PlanarYCbCrImage, etc #include "ImageTypes.h" // for ImageFormat::PLANAR_YCBCR, etc #include "GLImages.h" // for SurfaceTextureImage::Data, etc @@ -12,6 +15,7 @@ #include "gfxPlatform.h" // for gfxPlatform #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc #include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed +#include "mozilla/gfx/2D.h" #include "mozilla/gfx/BaseSize.h" // for BaseSize #include "mozilla/gfx/Point.h" // for IntSize #include "mozilla/gfx/Types.h" // for SurfaceFormat, etc @@ -29,8 +33,7 @@ #include "nsDebug.h" // for NS_WARNING, NS_ASSERTION #include "nsISupportsImpl.h" // for Image::Release, etc #include "nsRect.h" // for mozilla::gfx::IntRect -#include "mozilla/gfx/2D.h" -#include "ClientLayerManager.h" + #ifdef MOZ_WIDGET_GONK #include "GrallocImages.h" #endif @@ -174,11 +177,12 @@ ImageClientSingle::UpdateImage(ImageContainer* aContainer, uint32_t aContentFlag #endif RefPtr texture = image->GetTextureClient(this); + const bool hasTextureClient = !!texture; for (int32_t i = mBuffers.Length() - 1; i >= 0; --i) { if (mBuffers[i].mImageSerial == image->GetSerial()) { - if (texture) { - MOZ_ASSERT(texture == mBuffers[i].mTextureClient); + if (hasTextureClient) { + MOZ_ASSERT(image->GetTextureClient(this) == mBuffers[i].mTextureClient); } else { texture = mBuffers[i].mTextureClient; } diff --git a/gfx/layers/client/TextureClientRecycleAllocator.cpp b/gfx/layers/client/TextureClientRecycleAllocator.cpp index 0cc89e81e6..c572f5f037 100644 --- a/gfx/layers/client/TextureClientRecycleAllocator.cpp +++ b/gfx/layers/client/TextureClientRecycleAllocator.cpp @@ -144,6 +144,15 @@ TextureClientRecycleAllocator::Allocate(gfx::SurfaceFormat aFormat, aTextureFlags, aAllocFlags); } +void +TextureClientRecycleAllocator::ShrinkToMinimumSize() +{ + MutexAutoLock lock(mLock); + while (!mPooledClients.empty()) { + mPooledClients.pop(); + } +} + void TextureClientRecycleAllocator::RecycleTextureClient(TextureClient* aClient) { diff --git a/gfx/layers/client/TextureClientRecycleAllocator.h b/gfx/layers/client/TextureClientRecycleAllocator.h index 4fd35851de..ff584df706 100644 --- a/gfx/layers/client/TextureClientRecycleAllocator.h +++ b/gfx/layers/client/TextureClientRecycleAllocator.h @@ -49,6 +49,8 @@ public: TextureFlags aTextureFlags, TextureAllocationFlags flags = ALLOC_DEFAULT); + void ShrinkToMinimumSize(); + protected: virtual already_AddRefed Allocate(gfx::SurfaceFormat aFormat, diff --git a/gfx/layers/composite/ImageLayerComposite.cpp b/gfx/layers/composite/ImageLayerComposite.cpp index 162c24a431..fbcb921e35 100644 --- a/gfx/layers/composite/ImageLayerComposite.cpp +++ b/gfx/layers/composite/ImageLayerComposite.cpp @@ -168,6 +168,23 @@ ImageLayerComposite::IsOpaque() return false; } +nsIntRegion +ImageLayerComposite::GetFullyRenderedRegion() +{ + if (!mImageHost || + !mImageHost->IsAttached()) { + return GetShadowVisibleRegion().ToUnknownRegion(); + } + + if (mScaleMode == ScaleMode::STRETCH) { + nsIntRegion shadowVisibleRegion; + shadowVisibleRegion.And(GetShadowVisibleRegion().ToUnknownRegion(), nsIntRegion(gfx::IntRect(0, 0, mScaleToSize.width, mScaleToSize.height))); + return shadowVisibleRegion; + } + + return GetShadowVisibleRegion().ToUnknownRegion(); +} + CompositableHost* ImageLayerComposite::GetCompositableHost() { diff --git a/gfx/layers/composite/ImageLayerComposite.h b/gfx/layers/composite/ImageLayerComposite.h index 9aa226d57a..eda1e35dc1 100644 --- a/gfx/layers/composite/ImageLayerComposite.h +++ b/gfx/layers/composite/ImageLayerComposite.h @@ -61,6 +61,8 @@ public: virtual bool IsOpaque() override; + virtual nsIntRegion GetFullyRenderedRegion() override; + protected: virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override; diff --git a/gfx/layers/composite/LayerManagerComposite.h b/gfx/layers/composite/LayerManagerComposite.h index c3a4961a6c..e5f30143ac 100644 --- a/gfx/layers/composite/LayerManagerComposite.h +++ b/gfx/layers/composite/LayerManagerComposite.h @@ -553,7 +553,7 @@ public: * While progressive drawing is in progress this region will be * a subset of the shadow visible region. */ - nsIntRegion GetFullyRenderedRegion(); + virtual nsIntRegion GetFullyRenderedRegion(); protected: gfx::Matrix4x4 mShadowTransform; diff --git a/gfx/layers/composite/TiledContentHost.cpp b/gfx/layers/composite/TiledContentHost.cpp index 119f3d3505..281778586e 100644 --- a/gfx/layers/composite/TiledContentHost.cpp +++ b/gfx/layers/composite/TiledContentHost.cpp @@ -36,12 +36,6 @@ TiledLayerBufferComposite::~TiledLayerBufferComposite() Clear(); } -/* static */ void -TiledLayerBufferComposite::RecycleCallback(TextureHost* textureHost, void* aClosure) -{ - textureHost->CompositorRecycle(); -} - void TiledLayerBufferComposite::SetCompositor(Compositor* aCompositor) { diff --git a/gfx/layers/composite/TiledContentHost.h b/gfx/layers/composite/TiledContentHost.h index d61e94fd6c..df22473a70 100644 --- a/gfx/layers/composite/TiledContentHost.h +++ b/gfx/layers/composite/TiledContentHost.h @@ -146,10 +146,6 @@ public: void SetCompositor(Compositor* aCompositor); - // Recycle callback for TextureHost. - // Used when TiledContentClient is present in client side. - static void RecycleCallback(TextureHost* textureHost, void* aClosure); - protected: CSSToParentLayerScale2D mFrameResolution; diff --git a/gfx/layers/d3d11/TextureD3D11.cpp b/gfx/layers/d3d11/TextureD3D11.cpp index 80afad7e39..7f8ab65d21 100644 --- a/gfx/layers/d3d11/TextureD3D11.cpp +++ b/gfx/layers/d3d11/TextureD3D11.cpp @@ -642,7 +642,9 @@ static CompositorD3D11* AssertD3D11Compositor(Compositor* aCompositor) { CompositorD3D11* compositor = aCompositor ? aCompositor->AsCompositorD3D11() : nullptr; - MOZ_DIAGNOSTIC_ASSERT(!!compositor); + if (!compositor) { + gfxCriticalNote << "[D3D11] Attempt to set an incompatible compositor"; + } return compositor; } @@ -670,6 +672,7 @@ DXGITextureHostD3D11::Lock() } if (!mTextureSource) { if (!mTexture && !OpenSharedHandle()) { + gfxWindowsPlatform::GetPlatform()->ForceDeviceReset(ForcedDeviceResetReason::OPENSHAREDHANDLE); return false; } diff --git a/gfx/layers/d3d9/CompositorD3D9.cpp b/gfx/layers/d3d9/CompositorD3D9.cpp index 6e9ad3ddae..e0b5890f31 100644 --- a/gfx/layers/d3d9/CompositorD3D9.cpp +++ b/gfx/layers/d3d9/CompositorD3D9.cpp @@ -672,7 +672,7 @@ CompositorD3D9::FailedToResetDevice() { if (mFailedResetAttempts > 10) { mFailedResetAttempts = 0; gfxWindowsPlatform::GetPlatform()->D3D9DeviceReset(); - gfxWarning() << "[D3D9] Unable to get a working D3D9 Compositor"; + gfxCriticalNote << "[D3D9] Unable to get a working D3D9 Compositor"; } } diff --git a/gfx/layers/d3d9/TextureD3D9.cpp b/gfx/layers/d3d9/TextureD3D9.cpp index a27d29e395..f27a9bb6be 100644 --- a/gfx/layers/d3d9/TextureD3D9.cpp +++ b/gfx/layers/d3d9/TextureD3D9.cpp @@ -905,7 +905,7 @@ TextureHostD3D9::UpdatedInternal(const nsIntRegion* aRegion) } if (!mTextureSource->UpdateFromTexture(mTexture, regionToUpdate)) { - gfxWarning() << "[D3D9] DataTextureSourceD3D9::UpdateFromTexture failed"; + gfxCriticalNote << "[D3D9] DataTextureSourceD3D9::UpdateFromTexture failed"; } } diff --git a/ipc/chromium/atomics/moz.build b/ipc/chromium/atomics/moz.build new file mode 100644 index 0000000000..6e0a9e42f7 --- /dev/null +++ b/ipc/chromium/atomics/moz.build @@ -0,0 +1,17 @@ +# -*- 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/. + +include('/ipc/chromium/chromium-config.mozbuild') + +Library('chromium_atomics') + +# This test is copied from ../moz.build for atomicops_internals_mutex.cc +ost = CONFIG['OS_TEST'] +if '86' not in ost and 'arm' not in ost and 'aarch64' != ost and 'mips' not in ost: + SOURCES += [ + '../src/base/atomicops_internals_mutex.cc', + '../src/base/lock_impl_posix.cc', + ] diff --git a/ipc/chromium/moz.build b/ipc/chromium/moz.build index 97d00a281e..8512e00c81 100644 --- a/ipc/chromium/moz.build +++ b/ipc/chromium/moz.build @@ -9,6 +9,7 @@ include(libevent_path_prefix + '/libeventcommon.mozbuild') UNIFIED_SOURCES += [ 'src/base/at_exit.cc', + 'src/base/buffer.cc', 'src/base/command_line.cc', 'src/base/file_path.cc', 'src/base/file_util.cc', @@ -17,7 +18,6 @@ UNIFIED_SOURCES += [ 'src/base/logging.cc', 'src/base/message_loop.cc', 'src/base/message_pump_default.cc', - 'src/base/non_thread_safe.cc', 'src/base/pickle.cc', 'src/base/rand_util.cc', 'src/base/revocable_store.cc', @@ -34,11 +34,7 @@ UNIFIED_SOURCES += [ 'src/chrome/common/child_thread.cc', 'src/chrome/common/chrome_switches.cc', 'src/chrome/common/ipc_channel.cc', - 'src/chrome/common/ipc_channel_proxy.cc', 'src/chrome/common/ipc_message.cc', - 'src/chrome/common/ipc_sync_channel.cc', - 'src/chrome/common/ipc_sync_message.cc', - 'src/chrome/common/message_router.cc', 'src/chrome/common/notification_service.cc', ] @@ -165,3 +161,7 @@ CXXFLAGS += CONFIG['TK_CFLAGS'] include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' + +DIRS += [ + 'atomics', +] diff --git a/ipc/chromium/src/base/buffer.cc b/ipc/chromium/src/base/buffer.cc new file mode 100644 index 0000000000..35559e9513 --- /dev/null +++ b/ipc/chromium/src/base/buffer.cc @@ -0,0 +1,125 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "buffer.h" + +Buffer::Buffer() + : mBuffer(nullptr), + mSize(0), + mReserved(0) +{ +} + +Buffer::~Buffer() +{ + if (mBuffer) { + free(mBuffer); + } +} + +bool +Buffer::empty() const +{ + return mSize == 0; +} + +size_t +Buffer::size() const +{ + return mSize; +} + +const char* +Buffer::data() const +{ + return mBuffer; +} + +void +Buffer::clear() +{ + free(mBuffer); + mBuffer = nullptr; + mSize = 0; + mReserved = 0; +} + +void +Buffer::try_realloc(size_t newlength) +{ + char* buffer = (char*)realloc(mBuffer, newlength); + if (buffer) { + mBuffer = buffer; + mReserved = newlength; + return; + } + + // If we're growing the buffer, crash. If we're shrinking, then we continue to + // use the old (larger) buffer. + MOZ_RELEASE_ASSERT(newlength <= mReserved); +} + +void +Buffer::append(const char* bytes, size_t length) +{ + if (mSize + length > mReserved) { + try_realloc(mSize + length); + } + + memcpy(mBuffer + mSize, bytes, length); + mSize += length; +} + +void +Buffer::assign(const char* bytes, size_t length) +{ + if (bytes >= mBuffer && bytes < mBuffer + mReserved) { + MOZ_RELEASE_ASSERT(bytes + length <= mBuffer + mSize); + memmove(mBuffer, bytes, length); + mSize = length; + try_realloc(length); + } else { + try_realloc(length); + mSize = length; + memcpy(mBuffer, bytes, length); + } +} + +void +Buffer::erase(size_t start, size_t count) +{ + mSize -= count; + memmove(mBuffer + start, mBuffer + start + count, mSize - start); + try_realloc(mSize); +} + +void +Buffer::reserve(size_t size) +{ + if (mReserved < size) { + try_realloc(size); + } +} + +char* +Buffer::trade_bytes(size_t count) +{ + char* result = mBuffer; + mSize = mReserved = mSize - count; + mBuffer = mReserved ? (char*)malloc(mReserved) : nullptr; + MOZ_RELEASE_ASSERT(!mReserved || mBuffer); + if (mSize) { + memcpy(mBuffer, result + count, mSize); + } + + // Try to resize the buffer down, but ignore failure. This can cause extra + // copies, but so be it. + char* resized = (char*)realloc(result, count); + if (resized) { + return resized; + } + return result; +} diff --git a/ipc/chromium/src/base/buffer.h b/ipc/chromium/src/base/buffer.h new file mode 100644 index 0000000000..4e95a5bb3a --- /dev/null +++ b/ipc/chromium/src/base/buffer.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CHROME_BASE_BUFFER_H_ +#define CHROME_BASE_BUFFER_H_ + +// Buffer is a simple std::string-like class for buffering up IPC messages. Its +// main distinguishing characteristic is the trade_bytes function. +class Buffer { +public: + Buffer(); + ~Buffer(); + + bool empty() const; + const char* data() const; + size_t size() const; + + void clear(); + void append(const char* bytes, size_t length); + void assign(const char* bytes, size_t length); + void erase(size_t start, size_t count); + + void reserve(size_t size); + + // This function should be used by a caller who wants to extract the first + // |count| bytes from the buffer. Rather than copying the bytes out, this + // function returns the entire buffer. The bytes in range [count, size()) are + // copied out to a new buffer which becomes the current buffer. The + // presumption is that |count| is very large and approximately equal to size() + // so not much needs to be copied. + char* trade_bytes(size_t count); + +private: + void try_realloc(size_t newlength); + + char* mBuffer; + size_t mSize; + size_t mReserved; +}; + +#endif // CHROME_BASE_BUFFER_H_ diff --git a/ipc/chromium/src/base/non_thread_safe.cc b/ipc/chromium/src/base/non_thread_safe.cc deleted file mode 100644 index d8a91c6651..0000000000 --- a/ipc/chromium/src/base/non_thread_safe.cc +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/non_thread_safe.h" - -// These checks are only done in debug builds. -#ifndef NDEBUG - -#include "base/logging.h" - -NonThreadSafe::NonThreadSafe() - : valid_thread_id_(PlatformThread::CurrentId()) { -} - -bool NonThreadSafe::CalledOnValidThread() const { - return valid_thread_id_ == PlatformThread::CurrentId(); -} - -NonThreadSafe::~NonThreadSafe() { - DCHECK(CalledOnValidThread()); -} - -#endif // NDEBUG diff --git a/ipc/chromium/src/base/non_thread_safe.h b/ipc/chromium/src/base/non_thread_safe.h deleted file mode 100644 index 1da46aa688..0000000000 --- a/ipc/chromium/src/base/non_thread_safe.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_NON_THREAD_SAFE_H__ -#define BASE_NON_THREAD_SAFE_H__ - -#include "base/platform_thread.h" - -// A helper class used to help verify that methods of a class are -// called from the same thread. One can inherit from this class and use -// CalledOnValidThread() to verify. -// -// This is intended to be used with classes that appear to be thread safe, but -// aren't. For example, a service or a singleton like the preferences system. -// -// Example: -// class MyClass : public NonThreadSafe { -// public: -// void Foo() { -// DCHECK(CalledOnValidThread()); -// ... (do stuff) ... -// } -// } -// -// In Release mode, CalledOnValidThread will always return true. -// -#ifndef NDEBUG -class NonThreadSafe { - public: - NonThreadSafe(); - ~NonThreadSafe(); - - bool CalledOnValidThread() const; - - private: - PlatformThreadId valid_thread_id_; -}; -#else -// Do nothing in release mode. -class NonThreadSafe { - public: - NonThreadSafe() {} - ~NonThreadSafe() {} - - bool CalledOnValidThread() const { - return true; - } -}; -#endif // NDEBUG - -#endif // BASE_NON_THREAD_SAFE_H__ diff --git a/ipc/chromium/src/base/pickle.cc b/ipc/chromium/src/base/pickle.cc index 6da5c03735..cf0a653638 100644 --- a/ipc/chromium/src/base/pickle.cc +++ b/ipc/chromium/src/base/pickle.cc @@ -122,10 +122,10 @@ Pickle::Pickle(int header_size) header_->payload_size = 0; } -Pickle::Pickle(const char* data, int data_len) +Pickle::Pickle(const char* data, int data_len, Ownership ownership) : header_(reinterpret_cast(const_cast(data))), header_size_(0), - capacity_(kCapacityReadOnly), + capacity_(ownership == BORROWS ? kCapacityReadOnly : data_len), variable_buffer_offset_(0) { if (data_len >= static_cast(sizeof(Header))) header_size_ = data_len - header_->payload_size; @@ -670,3 +670,23 @@ const char* Pickle::FindNext(uint32_t header_size, return start + header_size + hdr->payload_size; } + +// static +uint32_t Pickle::GetLength(uint32_t header_size, + const char* start, + const char* end) { + DCHECK(header_size == AlignInt(header_size)); + DCHECK(header_size <= static_cast(kPayloadUnit)); + + if (end < start) + return 0; + size_t length = static_cast(end - start); + if (length < sizeof(Header)) + return 0; + + const Header* hdr = reinterpret_cast(start); + if (length < header_size) + return 0; + + return header_size + hdr->payload_size; +} diff --git a/ipc/chromium/src/base/pickle.h b/ipc/chromium/src/base/pickle.h index 519d8a70a6..ce0712ae2b 100644 --- a/ipc/chromium/src/base/pickle.h +++ b/ipc/chromium/src/base/pickle.h @@ -32,6 +32,11 @@ // class Pickle { public: + enum Ownership { + BORROWS, + OWNS, + }; + ~Pickle(); // Initialize a Pickle object using the default header size. @@ -42,11 +47,13 @@ class Pickle { // will be rounded up to ensure that the header size is 32bit-aligned. explicit Pickle(int header_size); - // Initializes a Pickle from a const block of data. The data is not copied; - // instead the data is merely referenced by this Pickle. Only const methods - // should be used on the Pickle when initialized this way. The header - // padding size is deduced from the data length. - Pickle(const char* data, int data_len); + // Initializes a Pickle from a const block of data. If ownership == BORROWS, + // the data is not copied; instead the data is merely referenced by this + // Pickle. Only const methods should be used on the Pickle when initialized + // this way. The header padding size is deduced from the data length. If + // ownership == OWNS, then again no copying takes place. However, the buffer + // is writable and will be freed when this Pickle is destroyed. + Pickle(const char* data, int data_len, Ownership ownership = BORROWS); // Initializes a Pickle as a deep copy of another Pickle. Pickle(const Pickle& other); @@ -62,6 +69,11 @@ class Pickle { int size() const { return static_cast(header_size_ + header_->payload_size); } + // Return the full size of the memory allocated for this Pickle's data. + uint32_t capacity() const { + return capacity_; + } + // Returns the data for this Pickle. const void* data() const { return header_; } @@ -234,10 +246,6 @@ class Pickle { return header_ ? payload() + payload_size() : nullptr; } - uint32_t capacity() const { - return capacity_; - } - // Resizes the buffer for use when writing the specified amount of data. The // location that the data should be written at is returned, or NULL if there // was an error. Call EndWrite with the returned offset and the given length @@ -282,6 +290,12 @@ class Pickle { const char* range_start, const char* range_end); + // If the given range contains at least header_size bytes, return the length + // of the pickled data including the header. + static uint32_t GetLength(uint32_t header_size, + const char* range_start, + const char* range_end); + // The allocation granularity of the payload. static const int kPayloadUnit; diff --git a/ipc/chromium/src/base/task.h b/ipc/chromium/src/base/task.h index 91bf0bf692..c8af4ae834 100644 --- a/ipc/chromium/src/base/task.h +++ b/ipc/chromium/src/base/task.h @@ -6,12 +6,12 @@ #ifndef BASE_TASK_H_ #define BASE_TASK_H_ -#include "base/non_thread_safe.h" #include "base/revocable_store.h" #include "base/tracked.h" #include "base/tuple.h" #include "mozilla/IndexSequence.h" #include "mozilla/Tuple.h" +#include "nsISupportsImpl.h" // Helper functions so that we can call a function a pass it arguments that come // from a Tuple. @@ -128,7 +128,7 @@ class ScopedTaskFactory : public RevocableStore { return new TaskWrapper(this); } - class TaskWrapper : public TaskType, public NonThreadSafe { + class TaskWrapper : public TaskType { public: explicit TaskWrapper(RevocableStore* store) : revocable_(store) { } @@ -137,9 +137,15 @@ class ScopedTaskFactory : public RevocableStore { TaskType::Run(); } + ~TaskWrapper() { + NS_ASSERT_OWNINGTHREAD(TaskWrapper); + } + private: Revocable revocable_; + NS_DECL_OWNINGTHREAD + DISALLOW_EVIL_CONSTRUCTORS(TaskWrapper); }; diff --git a/ipc/chromium/src/chrome/common/child_process_host.cc b/ipc/chromium/src/chrome/common/child_process_host.cc index 69e4cf4ebc..c6e302ee1f 100644 --- a/ipc/chromium/src/chrome/common/child_process_host.cc +++ b/ipc/chromium/src/chrome/common/child_process_host.cc @@ -154,13 +154,13 @@ ChildProcessHost::ListenerHook::ListenerHook(ChildProcessHost* host) } void ChildProcessHost::ListenerHook::OnMessageReceived( - const IPC::Message& msg) { + IPC::Message&& msg) { bool msg_is_ok = true; bool handled = false; if (!handled) { - host_->OnMessageReceived(msg); + host_->OnMessageReceived(mozilla::Move(msg)); } if (!msg_is_ok) diff --git a/ipc/chromium/src/chrome/common/child_process_host.h b/ipc/chromium/src/chrome/common/child_process_host.h index 66e1110a9d..58e434218e 100644 --- a/ipc/chromium/src/chrome/common/child_process_host.h +++ b/ipc/chromium/src/chrome/common/child_process_host.h @@ -75,7 +75,7 @@ class ChildProcessHost : void InstanceCreated(); // IPC::Channel::Listener implementation: - virtual void OnMessageReceived(const IPC::Message& msg) { } + virtual void OnMessageReceived(IPC::Message&& msg) { } virtual void OnChannelConnected(int32_t peer_pid) { } virtual void OnChannelError() { } @@ -102,7 +102,7 @@ class ChildProcessHost : class ListenerHook : public IPC::Channel::Listener { public: explicit ListenerHook(ChildProcessHost* host); - virtual void OnMessageReceived(const IPC::Message& msg); + virtual void OnMessageReceived(IPC::Message&& msg); virtual void OnChannelConnected(int32_t peer_pid); virtual void OnChannelError(); virtual void GetQueuedMessages(std::queue& queue); diff --git a/ipc/chromium/src/chrome/common/child_thread.cc b/ipc/chromium/src/chrome/common/child_thread.cc index ccf59da23a..5469db0ede 100644 --- a/ipc/chromium/src/chrome/common/child_thread.cc +++ b/ipc/chromium/src/chrome/common/child_thread.cc @@ -62,23 +62,9 @@ bool ChildThread::Send(IPC::Message* msg) { return channel_->Send(msg); } -void ChildThread::AddRoute(int32_t routing_id, IPC::Channel::Listener* listener) { - DCHECK(MessageLoop::current() == message_loop()); - - router_.AddRoute(routing_id, listener); -} - -void ChildThread::RemoveRoute(int32_t routing_id) { - DCHECK(MessageLoop::current() == message_loop()); - - router_.RemoveRoute(routing_id); -} - -void ChildThread::OnMessageReceived(const IPC::Message& msg) { +void ChildThread::OnMessageReceived(IPC::Message&& msg) { if (msg.routing_id() == MSG_ROUTING_CONTROL) { OnControlMessageReceived(msg); - } else { - router_.OnMessageReceived(msg); } } diff --git a/ipc/chromium/src/chrome/common/child_thread.h b/ipc/chromium/src/chrome/common/child_thread.h index 245fbe2af5..b57779d1e1 100644 --- a/ipc/chromium/src/chrome/common/child_thread.h +++ b/ipc/chromium/src/chrome/common/child_thread.h @@ -6,8 +6,7 @@ #define CHROME_COMMON_CHILD_THREAD_H_ #include "base/thread.h" -#include "chrome/common/ipc_sync_channel.h" -#include "chrome/common/message_router.h" +#include "chrome/common/ipc_channel.h" #include "mozilla/UniquePtr.h" class ResourceDispatcher; @@ -59,7 +58,7 @@ class ChildThread : public IPC::Channel::Listener, private: // IPC::Channel::Listener implementation: - virtual void OnMessageReceived(const IPC::Message& msg); + virtual void OnMessageReceived(IPC::Message&& msg); virtual void OnChannelError(); #ifdef MOZ_NUWA_PROCESS @@ -72,10 +71,6 @@ class ChildThread : public IPC::Channel::Listener, std::wstring channel_name_; mozilla::UniquePtr channel_; - // Used only on the background render thread to implement message routing - // functionality to the consumers of the ChildThread. - MessageRouter router_; - Thread::Options options_; // If true, checks with the browser process before shutdown. This avoids race diff --git a/ipc/chromium/src/chrome/common/ipc_channel.h b/ipc/chromium/src/chrome/common/ipc_channel.h index 4fae50d311..94bff82110 100644 --- a/ipc/chromium/src/chrome/common/ipc_channel.h +++ b/ipc/chromium/src/chrome/common/ipc_channel.h @@ -25,7 +25,7 @@ class Channel : public Message::Sender { virtual ~Listener() {} // Called when a message is received. - virtual void OnMessageReceived(const Message& message) = 0; + virtual void OnMessageReceived(Message&& message) = 0; // Called when the channel is connected and we have received the internal // Hello message from the peer. @@ -51,7 +51,10 @@ class Channel : public Message::Sender { kMaximumMessageSize = 256 * 1024 * 1024, // Ammount of data to read at once from the pipe. - kReadBufferSize = 4 * 1024 + kReadBufferSize = 4 * 1024, + + // Maximum size of a message that we allow to be copied (rather than moved). + kMaxCopySize = 32 * 1024, }; // Initialize a Channel. diff --git a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc index c585b0c534..84a0360199 100644 --- a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc +++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc @@ -263,18 +263,7 @@ bool Channel::ChannelImpl::EnqueueHelloMessage() { void Channel::ChannelImpl::ClearAndShrinkInputOverflowBuf() { - // If input_overflow_buf_ has grown, shrink it back to its normal size. - static size_t previousCapacityAfterClearing = 0; - if (input_overflow_buf_.capacity() > previousCapacityAfterClearing) { - // This swap trick is the closest thing C++ has to a guaranteed way - // to shrink the capacity of a string. - std::string tmp; - tmp.reserve(Channel::kReadBufferSize); - input_overflow_buf_.swap(tmp); - previousCapacityAfterClearing = input_overflow_buf_.capacity(); - } else { - input_overflow_buf_.clear(); - } + input_overflow_buf_.clear(); } bool Channel::ChannelImpl::Connect() { @@ -396,9 +385,23 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() { CHROMIUM_LOG(ERROR) << "IPC message is too big"; return false; } + input_overflow_buf_.append(input_buf_, bytes_read); overflowp = p = input_overflow_buf_.data(); end = p + input_overflow_buf_.size(); + + // If we've received the entire header, then we know the message + // length. In that case, reserve enough space to hold the entire + // message. This is more efficient than repeatedly enlarging the buffer as + // more data comes in. + uint32_t length = Message::GetLength(p, end); + if (length) { + input_overflow_buf_.reserve(length + kReadBufferSize); + + // Recompute these pointers in case the buffer moved. + overflowp = p = input_overflow_buf_.data(); + end = p + input_overflow_buf_.size(); + } } // A pointer to an array of |num_fds| file descriptors which includes any @@ -423,7 +426,31 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() { const char* message_tail = Message::FindNext(p, end); if (message_tail) { int len = static_cast(message_tail - p); - Message m(p, len); + char* buf; + + // The Message |m| allocated below needs to own its data. We can either + // copy the data out of the buffer or else steal the buffer and move the + // remaining data elsewhere. If len is large enough, we steal. Otherwise + // we copy. + if (len > kMaxCopySize) { + // Since len > kMaxCopySize > kReadBufferSize, we know that we must be + // using the overflow buffer. And since we always shift everything to + // the left at the end of a read, we must be at the start of the + // overflow buffer. + MOZ_RELEASE_ASSERT(p == overflowp); + buf = input_overflow_buf_.trade_bytes(len); + + // At this point the remaining data is at the front of + // input_overflow_buf_. p will get fixed up at the end of the + // loop. Set it to null here to make sure no one uses it. + p = nullptr; + overflowp = message_tail = input_overflow_buf_.data(); + end = overflowp + input_overflow_buf_.size(); + } else { + buf = (char*)malloc(len); + memcpy(buf, p, len); + } + Message m(buf, len, Message::OWNS); if (m.header()->num_fds) { // the message has file descriptors const char* error = NULL; @@ -492,7 +519,7 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() { CloseDescriptors(m.fd_cookie()); #endif } else { - listener_->OnMessageReceived(m); + listener_->OnMessageReceived(mozilla::Move(m)); } p = message_tail; } else { diff --git a/ipc/chromium/src/chrome/common/ipc_channel_posix.h b/ipc/chromium/src/chrome/common/ipc_channel_posix.h index 12f5c1382f..7e442b8103 100644 --- a/ipc/chromium/src/chrome/common/ipc_channel_posix.h +++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.h @@ -14,6 +14,7 @@ #include #include +#include "base/buffer.h" #include "base/message_loop.h" #include "chrome/common/file_descriptor_set_posix.h" @@ -127,7 +128,7 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher { // Large messages that span multiple pipe buffers, get built-up using // this buffer. - std::string input_overflow_buf_; + Buffer input_overflow_buf_; std::vector input_overflow_fds_; // In server-mode, we have to wait for the client to connect before we diff --git a/ipc/chromium/src/chrome/common/ipc_channel_proxy.cc b/ipc/chromium/src/chrome/common/ipc_channel_proxy.cc deleted file mode 100644 index 60ab8069f2..0000000000 --- a/ipc/chromium/src/chrome/common/ipc_channel_proxy.cc +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/message_loop.h" -#include "base/thread.h" -#include "chrome/common/ipc_channel_proxy.h" -#include "chrome/common/ipc_message_utils.h" - -namespace IPC { - -//----------------------------------------------------------------------------- - -ChannelProxy::Context::Context(Channel::Listener* listener, - MessageFilter* filter, - MessageLoop* ipc_message_loop) - : listener_message_loop_(MessageLoop::current()), - listener_(listener), - ipc_message_loop_(ipc_message_loop), - channel_(NULL), - peer_pid_(0), - channel_connected_called_(false) { - if (filter) - filters_.push_back(filter); -} - -void ChannelProxy::Context::CreateChannel(const std::wstring& id, - const Channel::Mode& mode) { - DCHECK(channel_ == NULL); - channel_id_ = id; - channel_ = new Channel(id, mode, this); -} - -bool ChannelProxy::Context::TryFilters(const Message& message) { - - for (size_t i = 0; i < filters_.size(); ++i) { - if (filters_[i]->OnMessageReceived(message)) { - return true; - } - } - return false; -} - -// Called on the IPC::Channel thread -void ChannelProxy::Context::OnMessageReceived(const Message& message) { - // First give a chance to the filters to process this message. - if (!TryFilters(message)) - OnMessageReceivedNoFilter(message); -} - -// Called on the IPC::Channel thread -void ChannelProxy::Context::OnMessageReceivedNoFilter(const Message& message) { - // NOTE: This code relies on the listener's message loop not going away while - // this thread is active. That should be a reasonable assumption, but it - // feels risky. We may want to invent some more indirect way of referring to - // a MessageLoop if this becomes a problem. - listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( - this, &Context::OnDispatchMessage, message)); -} - -// Called on the IPC::Channel thread -void ChannelProxy::Context::OnChannelConnected(int32_t peer_pid) { - peer_pid_ = peer_pid; - for (size_t i = 0; i < filters_.size(); ++i) - filters_[i]->OnChannelConnected(peer_pid); - - // See above comment about using listener_message_loop_ here. - listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( - this, &Context::OnDispatchConnected)); -} - -// Called on the IPC::Channel thread -void ChannelProxy::Context::OnChannelError() { - for (size_t i = 0; i < filters_.size(); ++i) - filters_[i]->OnChannelError(); - - // See above comment about using listener_message_loop_ here. - listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( - this, &Context::OnDispatchError)); -} - -// Called on the IPC::Channel thread -void ChannelProxy::Context::OnChannelOpened() { - DCHECK(channel_ != NULL); - - // Assume a reference to ourselves on behalf of this thread. This reference - // will be released when we are closed. - AddRef(); - - if (!channel_->Connect()) { - OnChannelError(); - return; - } - - for (size_t i = 0; i < filters_.size(); ++i) - filters_[i]->OnFilterAdded(channel_); -} - -// Called on the IPC::Channel thread -void ChannelProxy::Context::OnChannelClosed() { - // It's okay for IPC::ChannelProxy::Close to be called more than once, which - // would result in this branch being taken. - if (!channel_) - return; - - for (size_t i = 0; i < filters_.size(); ++i) { - filters_[i]->OnChannelClosing(); - filters_[i]->OnFilterRemoved(); - } - - // We don't need the filters anymore. - filters_.clear(); - - delete channel_; - channel_ = NULL; - - // Balance with the reference taken during startup. This may result in - // self-destruction. - Release(); -} - -// Called on the IPC::Channel thread -void ChannelProxy::Context::OnSendMessage(Message* message) { - if (!channel_->Send(message)) - OnChannelError(); -} - -// Called on the IPC::Channel thread -void ChannelProxy::Context::OnAddFilter(MessageFilter* filter) { - filters_.push_back(filter); - - // If the channel has already been created, then we need to send this message - // so that the filter gets access to the Channel. - if (channel_) - filter->OnFilterAdded(channel_); - - // Balances the AddRef in ChannelProxy::AddFilter. - filter->Release(); -} - -// Called on the IPC::Channel thread -void ChannelProxy::Context::OnRemoveFilter(MessageFilter* filter) { - for (size_t i = 0; i < filters_.size(); ++i) { - if (filters_[i].get() == filter) { - filter->OnFilterRemoved(); - filters_.erase(filters_.begin() + i); - return; - } - } - - NOTREACHED() << "filter to be removed not found"; -} - -// Called on the listener's thread -void ChannelProxy::Context::OnDispatchMessage(const Message& message) { - if (!listener_) - return; - - OnDispatchConnected(); - - - listener_->OnMessageReceived(message); - -} - -// Called on the listener's thread -void ChannelProxy::Context::OnDispatchConnected() { - if (channel_connected_called_) - return; - - channel_connected_called_ = true; - if (listener_) - listener_->OnChannelConnected(peer_pid_); -} - -// Called on the listener's thread -void ChannelProxy::Context::OnDispatchError() { - if (listener_) - listener_->OnChannelError(); -} - -//----------------------------------------------------------------------------- - -ChannelProxy::ChannelProxy(const std::wstring& channel_id, Channel::Mode mode, - Channel::Listener* listener, MessageFilter* filter, - MessageLoop* ipc_thread) - : context_(new Context(listener, filter, ipc_thread)) { - Init(channel_id, mode, ipc_thread, true); -} - -ChannelProxy::ChannelProxy(const std::wstring& channel_id, Channel::Mode mode, - MessageLoop* ipc_thread, Context* context, - bool create_pipe_now) - : context_(context) { - Init(channel_id, mode, ipc_thread, create_pipe_now); -} - -void ChannelProxy::Init(const std::wstring& channel_id, Channel::Mode mode, - MessageLoop* ipc_thread_loop, bool create_pipe_now) { - if (create_pipe_now) { - // Create the channel immediately. This effectively sets up the - // low-level pipe so that the client can connect. Without creating - // the pipe immediately, it is possible for a listener to attempt - // to connect and get an error since the pipe doesn't exist yet. - context_->CreateChannel(channel_id, mode); - } else { -#if defined(OS_POSIX) - // TODO(playmobil): On POSIX, IPC::Channel uses a socketpair(), one side of - // which needs to be mapped into the child process' address space. - // To know the value of the client side FD we need to have already - // created a socketpair which currently occurs in IPC::Channel's - // constructor. - // If we lazilly construct the IPC::Channel then the caller has no way - // of knowing the FD #. - // - // We can solve this either by having the Channel's creation launch the - // subprocess itself or by creating the socketpair() externally. - NOTIMPLEMENTED(); -#endif // defined(OS_POSIX) - context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( - context_.get(), &Context::CreateChannel, channel_id, mode)); - } - - // complete initialization on the background thread - context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( - context_.get(), &Context::OnChannelOpened)); -} - -void ChannelProxy::Close() { - // Clear the backpointer to the listener so that any pending calls to - // Context::OnDispatchMessage or OnDispatchError will be ignored. It is - // possible that the channel could be closed while it is receiving messages! - context_->Clear(); - - if (context_->ipc_message_loop()) { - context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( - context_.get(), &Context::OnChannelClosed)); - } -} - -bool ChannelProxy::Send(Message* message) { - - context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( - context_.get(), &Context::OnSendMessage, message)); - return true; -} - -void ChannelProxy::AddFilter(MessageFilter* filter) { - // We want to addref the filter to prevent it from - // being destroyed before the OnAddFilter call is invoked. - filter->AddRef(); - context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( - context_.get(), &Context::OnAddFilter, filter)); -} - -void ChannelProxy::RemoveFilter(MessageFilter* filter) { - context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( - context_.get(), &Context::OnRemoveFilter, filter)); -} - -#if defined(OS_POSIX) -// See the TODO regarding lazy initialization of the channel in -// ChannelProxy::Init(). -// We assume that IPC::Channel::GetClientFileDescriptorMapping() is thread-safe. -void ChannelProxy::GetClientFileDescriptorMapping(int *src_fd, - int *dest_fd) const { - Channel *channel = context_.get()->channel_; - DCHECK(channel); // Channel must have been created first. - channel->GetClientFileDescriptorMapping(src_fd, dest_fd); -} -#endif - -//----------------------------------------------------------------------------- - -} // namespace IPC diff --git a/ipc/chromium/src/chrome/common/ipc_channel_proxy.h b/ipc/chromium/src/chrome/common/ipc_channel_proxy.h deleted file mode 100644 index bb312d9295..0000000000 --- a/ipc/chromium/src/chrome/common/ipc_channel_proxy.h +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_COMMON_IPC_CHANNEL_PROXY_H__ -#define CHROME_COMMON_IPC_CHANNEL_PROXY_H__ - -#include -#include "base/lock.h" -#include "chrome/common/ipc_channel.h" -#include "nsISupportsImpl.h" -#include "nsAutoPtr.h" - -class MessageLoop; - -namespace IPC { - -//----------------------------------------------------------------------------- -// IPC::ChannelProxy -// -// This class is a helper class that is useful when you wish to run an IPC -// channel on a background thread. It provides you with the option of either -// handling IPC messages on that background thread or having them dispatched to -// your main thread (the thread on which the IPC::ChannelProxy is created). -// -// The API for an IPC::ChannelProxy is very similar to that of an IPC::Channel. -// When you send a message to an IPC::ChannelProxy, the message is routed to -// the background thread, where it is then passed to the IPC::Channel's Send -// method. This means that you can send a message from your thread and your -// message will be sent over the IPC channel when possible instead of being -// delayed until your thread returns to its message loop. (Often IPC messages -// will queue up on the IPC::Channel when there is a lot of traffic, and the -// channel will not get cycles to flush its message queue until the thread, on -// which it is running, returns to its message loop.) -// -// An IPC::ChannelProxy can have a MessageFilter associated with it, which will -// be notified of incoming messages on the IPC::Channel's thread. This gives -// the consumer of IPC::ChannelProxy the ability to respond to incoming -// messages on this background thread instead of on their own thread, which may -// be bogged down with other processing. The result can be greatly improved -// latency for messages that can be handled on a background thread. -// -// The consumer of IPC::ChannelProxy is responsible for allocating the Thread -// instance where the IPC::Channel will be created and operated. -// -class ChannelProxy : public Message::Sender { - public: - // A class that receives messages on the thread where the IPC channel is - // running. It can choose to prevent the default action for an IPC message. - class MessageFilter { - public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MessageFilter) - - // Called on the background thread to provide the filter with access to the - // channel. Called when the IPC channel is initialized or when AddFilter - // is called if the channel is already initialized. - virtual void OnFilterAdded(Channel* channel) {} - - // Called on the background thread when the filter has been removed from - // the ChannelProxy and when the Channel is closing. After a filter is - // removed, it will not be called again. - virtual void OnFilterRemoved() {} - - // Called to inform the filter that the IPC channel is connected and we - // have received the internal Hello message from the peer. - virtual void OnChannelConnected(int32_t peer_pid) {} - - // Called when there is an error on the channel, typically that the channel - // has been closed. - virtual void OnChannelError() {} - - // Called to inform the filter that the IPC channel will be destroyed. - // OnFilterRemoved is called immediately after this. - virtual void OnChannelClosing() {} - - // Return true to indicate that the message was handled, or false to let - // the message be handled in the default way. - virtual bool OnMessageReceived(const Message& message) { - return false; - } - protected: - virtual ~MessageFilter() {} - }; - - // Initializes a channel proxy. The channel_id and mode parameters are - // passed directly to the underlying IPC::Channel. The listener is called on - // the thread that creates the ChannelProxy. The filter's OnMessageReceived - // method is called on the thread where the IPC::Channel is running. The - // filter may be null if the consumer is not interested in handling messages - // on the background thread. Any message not handled by the filter will be - // dispatched to the listener. The given message loop indicates where the - // IPC::Channel should be created. - ChannelProxy(const std::wstring& channel_id, Channel::Mode mode, - Channel::Listener* listener, MessageFilter* filter, - MessageLoop* ipc_thread_loop); - - ~ChannelProxy() { - Close(); - } - - // Close the IPC::Channel. This operation completes asynchronously, once the - // background thread processes the command to close the channel. It is ok to - // call this method multiple times. Redundant calls are ignored. - // - // WARNING: The MessageFilter object held by the ChannelProxy is also - // released asynchronously, and it may in fact have its final reference - // released on the background thread. The caller should be careful to deal - // with / allow for this possibility. - void Close(); - - // Send a message asynchronously. The message is routed to the background - // thread where it is passed to the IPC::Channel's Send method. - virtual bool Send(Message* message); - - // Used to intercept messages as they are received on the background thread. - // - // Ordinarily, messages sent to the ChannelProxy are routed to the matching - // listener on the worker thread. This API allows code to intercept messages - // before they are sent to the worker thread. - void AddFilter(MessageFilter* filter); - void RemoveFilter(MessageFilter* filter); - -#if defined(OS_POSIX) - // Calls through to the underlying channel's methods. - // TODO(playmobil): For now this is only implemented in the case of - // create_pipe_now = true, we need to figure this out for the latter case. - void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const; -#endif // defined(OS_POSIX) - - protected: - class Context; - // A subclass uses this constructor if it needs to add more information - // to the internal state. If create_pipe_now is true, the pipe is created - // immediately. Otherwise it's created on the IO thread. - ChannelProxy(const std::wstring& channel_id, Channel::Mode mode, - MessageLoop* ipc_thread_loop, Context* context, - bool create_pipe_now); - - // Used internally to hold state that is referenced on the IPC thread. - class Context : public Channel::Listener { - public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Context) - Context(Channel::Listener* listener, MessageFilter* filter, - MessageLoop* ipc_thread); - MessageLoop* ipc_message_loop() const { return ipc_message_loop_; } - const std::wstring& channel_id() const { return channel_id_; } - - // Dispatches a message on the listener thread. - void OnDispatchMessage(const Message& message); - - protected: - virtual ~Context() {} - - // IPC::Channel::Listener methods: - virtual void OnMessageReceived(const Message& message); - virtual void OnChannelConnected(int32_t peer_pid); - virtual void OnChannelError(); - - // Like OnMessageReceived but doesn't try the filters. - void OnMessageReceivedNoFilter(const Message& message); - - // Gives the filters a chance at processing |message|. - // Returns true if the message was processed, false otherwise. - bool TryFilters(const Message& message); - - // Like Open and Close, but called on the IPC thread. - virtual void OnChannelOpened(); - virtual void OnChannelClosed(); - - // Called on the consumers thread when the ChannelProxy is closed. At that - // point the consumer is telling us that they don't want to receive any - // more messages, so we honor that wish by forgetting them! - virtual void Clear() { listener_ = NULL; } - - private: - friend class ChannelProxy; - // Create the Channel - void CreateChannel(const std::wstring& id, const Channel::Mode& mode); - - // Methods called via InvokeLater: - void OnSendMessage(Message* message_ptr); - void OnAddFilter(MessageFilter* filter); - void OnRemoveFilter(MessageFilter* filter); - void OnDispatchConnected(); - void OnDispatchError(); - - MessageLoop* listener_message_loop_; - Channel::Listener* listener_; - - // List of filters. This is only accessed on the IPC thread. - std::vector > filters_; - MessageLoop* ipc_message_loop_; - Channel* channel_; - std::wstring channel_id_; - int peer_pid_; - bool channel_connected_called_; - }; - - Context* context() { return context_; } - - private: - void Init(const std::wstring& channel_id, Channel::Mode mode, - MessageLoop* ipc_thread_loop, bool create_pipe_now); - - // By maintaining this indirection (ref-counted) to our internal state, we - // can safely be destroyed while the background thread continues to do stuff - // that involves this data. - RefPtr context_; -}; - -} // namespace IPC - -#endif // CHROME_COMMON_IPC_CHANNEL_PROXY_H__ diff --git a/ipc/chromium/src/chrome/common/ipc_channel_win.cc b/ipc/chromium/src/chrome/common/ipc_channel_win.cc index 533c7284bb..82e5797646 100644 --- a/ipc/chromium/src/chrome/common/ipc_channel_win.cc +++ b/ipc/chromium/src/chrome/common/ipc_channel_win.cc @@ -12,11 +12,22 @@ #include "base/process_util.h" #include "base/rand_util.h" #include "base/string_util.h" -#include "base/non_thread_safe.h" #include "base/win_util.h" #include "chrome/common/ipc_message_utils.h" #include "mozilla/ipc/ProtocolUtils.h" +// ChannelImpl is used on the IPC thread, but constructed on a different thread, +// so it has to hold the nsAutoOwningThread as a pointer, and we need a slightly +// different macro. +#ifdef DEBUG +#define ASSERT_OWNINGTHREAD(_class) \ + if (nsAutoOwningThread* owningThread = _mOwningThread.get()) { \ + NS_CheckThreadSafe(owningThread->GetThread(), #_class " not thread-safe"); \ + } +#else +#define ASSERT_OWNINGTHREAD(_class) ((void)0) +#endif + namespace IPC { //------------------------------------------------------------------------------ @@ -94,9 +105,7 @@ HANDLE Channel::ChannelImpl::GetServerPipeHandle() const { } void Channel::ChannelImpl::Close() { - if (thread_check_.get()) { - DCHECK(thread_check_->CalledOnValidThread()); - } + ASSERT_OWNINGTHREAD(ChannelImpl); bool waited = false; if (input_state_.is_pending || output_state_.is_pending) { @@ -121,16 +130,14 @@ void Channel::ChannelImpl::Close() { delete m; } - if (thread_check_.get()) - thread_check_.reset(); - +#ifdef DEBUG + _mOwningThread = nullptr; +#endif closed_ = true; } bool Channel::ChannelImpl::Send(Message* message) { - if (thread_check_.get()) { - DCHECK(thread_check_->CalledOnValidThread()); - } + ASSERT_OWNINGTHREAD(ChannelImpl); #ifdef IPC_MESSAGE_DEBUG_EXTRA DLOG(INFO) << "sending message @" << message << " on channel @" << this << " with type " << message->type() @@ -239,8 +246,11 @@ bool Channel::ChannelImpl::EnqueueHelloMessage() { } bool Channel::ChannelImpl::Connect() { - if (!thread_check_.get()) - thread_check_.reset(new NonThreadSafe()); +#ifdef DEBUG + if (!_mOwningThread) { + _mOwningThread = mozilla::MakeUnique(); + } +#endif if (pipe_ == INVALID_HANDLE_VALUE) return false; @@ -265,7 +275,7 @@ bool Channel::ChannelImpl::Connect() { } bool Channel::ChannelImpl::ProcessConnection() { - DCHECK(thread_check_->CalledOnValidThread()); + ASSERT_OWNINGTHREAD(ChannelImpl); if (input_state_.is_pending) input_state_.is_pending = false; @@ -301,7 +311,7 @@ bool Channel::ChannelImpl::ProcessConnection() { bool Channel::ChannelImpl::ProcessIncomingMessages( MessageLoopForIO::IOContext* context, DWORD bytes_read) { - DCHECK(thread_check_->CalledOnValidThread()); + ASSERT_OWNINGTHREAD(ChannelImpl); if (input_state_.is_pending) { input_state_.is_pending = false; DCHECK(context); @@ -350,16 +360,53 @@ bool Channel::ChannelImpl::ProcessIncomingMessages( CHROMIUM_LOG(ERROR) << "IPC message is too big"; return false; } + input_overflow_buf_.append(input_buf_, bytes_read); p = input_overflow_buf_.data(); end = p + input_overflow_buf_.size(); + + // If we've received the entire header, then we know the message + // length. In that case, reserve enough space to hold the entire + // message. This is more efficient than repeatedly enlarging the buffer as + // more data comes in. + uint32_t length = Message::GetLength(p, end); + if (length) { + input_overflow_buf_.reserve(length + kReadBufferSize); + + // Recompute these pointers in case the buffer moved. + p = input_overflow_buf_.data(); + end = p + input_overflow_buf_.size(); + } } while (p < end) { const char* message_tail = Message::FindNext(p, end); if (message_tail) { int len = static_cast(message_tail - p); - const Message m(p, len); + char* buf; + + // The Message |m| allocated below needs to own its data. We can either + // copy the data out of the buffer or else steal the buffer and move the + // remaining data elsewhere. If len is large enough, we steal. Otherwise + // we copy. + if (len > kMaxCopySize) { + // Since len > kMaxCopySize > kReadBufferSize, we know that we must be + // using the overflow buffer. And since we always shift everything to + // the left at the end of a read, we must be at the start of the + // overflow buffer. + buf = input_overflow_buf_.trade_bytes(len); + + // At this point the remaining data is at the front of + // input_overflow_buf_. p will get fixed up at the end of the + // loop. Set it to null here to make sure no one uses it. + p = nullptr; + message_tail = input_overflow_buf_.data(); + end = message_tail + input_overflow_buf_.size(); + } else { + buf = (char*)malloc(len); + memcpy(buf, p, len); + } + Message m(buf, len, Message::OWNS); #ifdef IPC_MESSAGE_DEBUG_EXTRA DLOG(INFO) << "received message on channel @" << this << " with type " << m.type(); @@ -380,7 +427,7 @@ bool Channel::ChannelImpl::ProcessIncomingMessages( waiting_for_shared_secret_ = false; listener_->OnChannelConnected(claimed_pid); } else { - listener_->OnMessageReceived(m); + listener_->OnMessageReceived(mozilla::Move(m)); } p = message_tail; } else { @@ -401,7 +448,7 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages( DWORD bytes_written) { DCHECK(!waiting_connect_); // Why are we trying to send messages if there's // no connection? - DCHECK(thread_check_->CalledOnValidThread()); + ASSERT_OWNINGTHREAD(ChannelImpl); if (output_state_.is_pending) { DCHECK(context); @@ -459,7 +506,7 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages( void Channel::ChannelImpl::OnIOCompleted(MessageLoopForIO::IOContext* context, DWORD bytes_transfered, DWORD error) { bool ok; - DCHECK(thread_check_->CalledOnValidThread()); + ASSERT_OWNINGTHREAD(ChannelImpl); if (context == &input_state_.context) { if (waiting_connect_) { if (!ProcessConnection()) diff --git a/ipc/chromium/src/chrome/common/ipc_channel_win.h b/ipc/chromium/src/chrome/common/ipc_channel_win.h index 67e2da405b..98e3f66da8 100644 --- a/ipc/chromium/src/chrome/common/ipc_channel_win.h +++ b/ipc/chromium/src/chrome/common/ipc_channel_win.h @@ -10,10 +10,9 @@ #include #include +#include "base/buffer.h" #include "base/message_loop.h" -#include "mozilla/UniquePtr.h" - -class NonThreadSafe; +#include "nsISupportsImpl.h" namespace IPC { @@ -23,7 +22,7 @@ class Channel::ChannelImpl : public MessageLoopForIO::IOHandler { ChannelImpl(const std::wstring& channel_id, Mode mode, Listener* listener); ChannelImpl(const std::wstring& channel_id, HANDLE server_pipe, Mode mode, Listener* listener); - ~ChannelImpl() { + ~ChannelImpl() { if (pipe_ != INVALID_HANDLE_VALUE) { Close(); } @@ -86,7 +85,7 @@ class Channel::ChannelImpl : public MessageLoopForIO::IOHandler { // Large messages that span multiple pipe buffers, get built-up using // this buffer. - std::string input_overflow_buf_; + Buffer input_overflow_buf_; // In server-mode, we have to wait for the client to connect before we // can begin reading. We make use of the input_state_ when performing @@ -119,7 +118,9 @@ class Channel::ChannelImpl : public MessageLoopForIO::IOHandler { // send us back our shared secret, if we are using one. bool waiting_for_shared_secret_; - mozilla::UniquePtr thread_check_; +#ifdef DEBUG + mozilla::UniquePtr _mOwningThread; +#endif DISALLOW_COPY_AND_ASSIGN(ChannelImpl); }; diff --git a/ipc/chromium/src/chrome/common/ipc_message.cc b/ipc/chromium/src/chrome/common/ipc_message.cc index ab68ea7f46..19df7c1250 100644 --- a/ipc/chromium/src/chrome/common/ipc_message.cc +++ b/ipc/chromium/src/chrome/common/ipc_message.cc @@ -71,7 +71,9 @@ Message::Message(int32_t routing_id, msgid_t type, PriorityValue priority, InitLoggingVariables(aName); } -Message::Message(const char* data, int data_len) : Pickle(data, data_len) { +Message::Message(const char* data, int data_len, Ownership ownership) + : Pickle(data, data_len, ownership) +{ MOZ_COUNT_CTOR(IPC::Message); InitLoggingVariables(); } diff --git a/ipc/chromium/src/chrome/common/ipc_message.h b/ipc/chromium/src/chrome/common/ipc_message.h index 4cf6c9e201..0fffe8c751 100644 --- a/ipc/chromium/src/chrome/common/ipc_message.h +++ b/ipc/chromium/src/chrome/common/ipc_message.h @@ -70,10 +70,12 @@ class Message : public Pickle { MessageCompression compression = COMPRESSION_NONE, const char* const name="???"); - // Initializes a message from a const block of data. The data is not copied; - // instead the data is merely referenced by this message. Only const methods - // should be used on the message when initialized this way. - Message(const char* data, int data_len); + // Initializes a message from a const block of data. If ownership == BORROWS, + // the data is not copied; instead the data is merely referenced by this + // message. Only const methods should be used on the message when initialized + // this way. If ownership == OWNS, then again no copying takes place. However, + // the buffer is writable and will be freed when the message is destroyed. + Message(const char* data, int data_len, Ownership ownership = BORROWS); Message(const Message& other); Message(Message&& other); @@ -242,6 +244,12 @@ class Message : public Pickle { return Pickle::FindNext(sizeof(Header), range_start, range_end); } + // If the given range contains at least header_size bytes, return the length + // of the message including the header. + static uint32_t GetLength(const char* range_start, const char* range_end) { + return Pickle::GetLength(sizeof(Header), range_start, range_end); + } + #if defined(OS_POSIX) // On POSIX, a message supports reading / writing FileDescriptor objects. // This is used to pass a file descriptor to the peer of an IPC channel. diff --git a/ipc/chromium/src/chrome/common/ipc_message_utils.h b/ipc/chromium/src/chrome/common/ipc_message_utils.h index b063867af8..fa3f5bb1c2 100644 --- a/ipc/chromium/src/chrome/common/ipc_message_utils.h +++ b/ipc/chromium/src/chrome/common/ipc_message_utils.h @@ -17,7 +17,7 @@ #if defined(OS_POSIX) #include "chrome/common/file_descriptor_set_posix.h" #endif -#include "chrome/common/ipc_sync_message.h" +#include "chrome/common/ipc_message.h" #include "chrome/common/transport_dib.h" namespace IPC { diff --git a/ipc/chromium/src/chrome/common/ipc_sync_channel.cc b/ipc/chromium/src/chrome/common/ipc_sync_channel.cc deleted file mode 100644 index 02920b3474..0000000000 --- a/ipc/chromium/src/chrome/common/ipc_sync_channel.cc +++ /dev/null @@ -1,452 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/common/ipc_sync_channel.h" - -#include "base/logging.h" -#include "base/thread_local.h" -#include "base/message_loop.h" -#include "base/waitable_event.h" -#include "base/waitable_event_watcher.h" -#include "chrome/common/ipc_sync_message.h" -#include "nsISupportsImpl.h" - -using base::TimeDelta; -using base::TimeTicks; -using base::WaitableEvent; - -namespace IPC { -// When we're blocked in a Send(), we need to process incoming synchronous -// messages right away because it could be blocking our reply (either -// directly from the same object we're calling, or indirectly through one or -// more other channels). That means that in SyncContext's OnMessageReceived, -// we need to process sync message right away if we're blocked. However a -// simple check isn't sufficient, because the listener thread can be in the -// process of calling Send. -// To work around this, when SyncChannel filters a sync message, it sets -// an event that the listener thread waits on during its Send() call. This -// allows us to dispatch incoming sync messages when blocked. The race -// condition is handled because if Send is in the process of being called, it -// will check the event. In case the listener thread isn't sending a message, -// we queue a task on the listener thread to dispatch the received messages. -// The messages are stored in this queue object that's shared among all -// SyncChannel objects on the same thread (since one object can receive a -// sync message while another one is blocked). - -class SyncChannel::ReceivedSyncMsgQueue { - public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SyncChannel::ReceivedSyncMsgQueue) - - static base::ThreadLocalPointer& get_tls_ptr() { - static base::ThreadLocalPointer tls_ptr; - return tls_ptr; - } - - // Returns the ReceivedSyncMsgQueue instance for this thread, creating one - // if necessary. Call RemoveContext on the same thread when done. - static ReceivedSyncMsgQueue* AddContext() { - // We want one ReceivedSyncMsgQueue per listener thread (i.e. since multiple - // SyncChannel objects can block the same thread). - ReceivedSyncMsgQueue* rv = get_tls_ptr().Get(); - if (!rv) { - rv = new ReceivedSyncMsgQueue(); - get_tls_ptr().Set(rv); - } - rv->listener_count_++; - return rv; - } - - // Called on IPC thread when a synchronous message or reply arrives. - void QueueMessage(const Message& msg, SyncChannel::SyncContext* context) { - bool was_task_pending; - { - AutoLock auto_lock(message_lock_); - - was_task_pending = task_pending_; - task_pending_ = true; - - // We set the event in case the listener thread is blocked (or is about - // to). In case it's not, the PostTask dispatches the messages. - message_queue_.push_back(QueuedMessage(new Message(msg), context)); - } - - dispatch_event_.Signal(); - if (!was_task_pending) { - listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( - this, &ReceivedSyncMsgQueue::DispatchMessagesTask)); - } - } - - void QueueReply(const Message &msg, SyncChannel::SyncContext* context) { - received_replies_.push_back(QueuedMessage(new Message(msg), context)); - } - - // Called on the listener's thread to process any queues synchronous - // messages. - void DispatchMessagesTask() { - { - AutoLock auto_lock(message_lock_); - task_pending_ = false; - } - DispatchMessages(); - } - - void DispatchMessages() { - while (true) { - Message* message; - RefPtr context; - { - AutoLock auto_lock(message_lock_); - if (message_queue_.empty()) - break; - - message = message_queue_.front().message; - context = message_queue_.front().context; - message_queue_.pop_front(); - } - - context->OnDispatchMessage(*message); - delete message; - } - } - - // SyncChannel calls this in its destructor. - void RemoveContext(SyncContext* context) { - AutoLock auto_lock(message_lock_); - - SyncMessageQueue::iterator iter = message_queue_.begin(); - while (iter != message_queue_.end()) { - if (iter->context == context) { - delete iter->message; - iter = message_queue_.erase(iter); - } else { - iter++; - } - } - - if (--listener_count_ == 0) { - DCHECK(get_tls_ptr().Get()); - get_tls_ptr().Set(NULL); - } - } - - WaitableEvent* dispatch_event() { return &dispatch_event_; } - MessageLoop* listener_message_loop() { return listener_message_loop_; } - - // Called on the ipc thread to check if we can unblock any current Send() - // calls based on a queued reply. - void DispatchReplies() { - for (size_t i = 0; i < received_replies_.size(); ++i) { - Message* message = received_replies_[i].message; - if (received_replies_[i].context->TryToUnblockListener(message)) { - delete message; - received_replies_.erase(received_replies_.begin() + i); - return; - } - } - } - - protected: - ~ReceivedSyncMsgQueue() {} - - private: - // See the comment in SyncChannel::SyncChannel for why this event is created - // as manual reset. - ReceivedSyncMsgQueue() : - dispatch_event_(true, false), - listener_message_loop_(MessageLoop::current()), - task_pending_(false), - listener_count_(0) { - } - - // Holds information about a queued synchronous message or reply. - struct QueuedMessage { - QueuedMessage(Message* m, SyncContext* c) : message(m), context(c) { } - Message* message; - RefPtr context; - }; - - typedef std::deque SyncMessageQueue; - SyncMessageQueue message_queue_; - - std::vector received_replies_; - - // Set when we got a synchronous message that we must respond to as the - // sender needs its reply before it can reply to our original synchronous - // message. - WaitableEvent dispatch_event_; - MessageLoop* listener_message_loop_; - Lock message_lock_; - bool task_pending_; - int listener_count_; -}; - -SyncChannel::SyncContext::SyncContext( - Channel::Listener* listener, - MessageFilter* filter, - MessageLoop* ipc_thread, - WaitableEvent* shutdown_event) - : ChannelProxy::Context(listener, filter, ipc_thread), - received_sync_msgs_(ReceivedSyncMsgQueue::AddContext()), - shutdown_event_(shutdown_event) { -} - -SyncChannel::SyncContext::~SyncContext() { - while (!deserializers_.empty()) - Pop(); -} - -// Adds information about an outgoing sync message to the context so that -// we know how to deserialize the reply. Returns a handle that's set when -// the reply has arrived. -void SyncChannel::SyncContext::Push(SyncMessage* sync_msg) { - // The event is created as manual reset because in between Signal and - // OnObjectSignalled, another Send can happen which would stop the watcher - // from being called. The event would get watched later, when the nested - // Send completes, so the event will need to remain set. - PendingSyncMsg pending(SyncMessage::GetMessageId(*sync_msg), - sync_msg->GetReplyDeserializer(), - new WaitableEvent(true, false)); - AutoLock auto_lock(deserializers_lock_); - deserializers_.push_back(pending); -} - -bool SyncChannel::SyncContext::Pop() { - bool result; - { - AutoLock auto_lock(deserializers_lock_); - PendingSyncMsg msg = deserializers_.back(); - delete msg.deserializer; - delete msg.done_event; - msg.done_event = NULL; - deserializers_.pop_back(); - result = msg.send_result; - } - - // We got a reply to a synchronous Send() call that's blocking the listener - // thread. However, further down the call stack there could be another - // blocking Send() call, whose reply we received after we made this last - // Send() call. So check if we have any queued replies available that - // can now unblock the listener thread. - ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( - received_sync_msgs_.get(), &ReceivedSyncMsgQueue::DispatchReplies)); - - return result; -} - -WaitableEvent* SyncChannel::SyncContext::GetSendDoneEvent() { - AutoLock auto_lock(deserializers_lock_); - return deserializers_.back().done_event; -} - -WaitableEvent* SyncChannel::SyncContext::GetDispatchEvent() { - return received_sync_msgs_->dispatch_event(); -} - -void SyncChannel::SyncContext::DispatchMessages() { - received_sync_msgs_->DispatchMessages(); -} - -bool SyncChannel::SyncContext::TryToUnblockListener(const Message* msg) { - AutoLock auto_lock(deserializers_lock_); - if (deserializers_.empty() || - !SyncMessage::IsMessageReplyTo(*msg, deserializers_.back().id)) { - return false; - } - - if (!msg->is_reply_error()) { - deserializers_.back().send_result = deserializers_.back().deserializer-> - SerializeOutputParameters(*msg); - } - deserializers_.back().done_event->Signal(); - - return true; -} - -void SyncChannel::SyncContext::Clear() { - CancelPendingSends(); - received_sync_msgs_->RemoveContext(this); - - Context::Clear(); -} - -void SyncChannel::SyncContext::OnMessageReceived(const Message& msg) { - // Give the filters a chance at processing this message. - if (TryFilters(msg)) - return; - - if (TryToUnblockListener(&msg)) - return; - - if (msg.should_unblock()) { - received_sync_msgs_->QueueMessage(msg, this); - return; - } - - if (msg.is_reply()) { - received_sync_msgs_->QueueReply(msg, this); - return; - } - - return Context::OnMessageReceivedNoFilter(msg); -} - -void SyncChannel::SyncContext::OnChannelError() { - CancelPendingSends(); - shutdown_watcher_.StopWatching(); - Context::OnChannelError(); -} - -void SyncChannel::SyncContext::OnChannelOpened() { - shutdown_watcher_.StartWatching(shutdown_event_, this); - Context::OnChannelOpened(); -} - -void SyncChannel::SyncContext::OnChannelClosed() { - shutdown_watcher_.StopWatching(); - Context::OnChannelClosed(); -} - -void SyncChannel::SyncContext::OnSendTimeout(int message_id) { - AutoLock auto_lock(deserializers_lock_); - PendingSyncMessageQueue::iterator iter; - for (iter = deserializers_.begin(); iter != deserializers_.end(); iter++) { - if (iter->id == message_id) { - iter->done_event->Signal(); - break; - } - } -} - -void SyncChannel::SyncContext::CancelPendingSends() { - AutoLock auto_lock(deserializers_lock_); - PendingSyncMessageQueue::iterator iter; - for (iter = deserializers_.begin(); iter != deserializers_.end(); iter++) - iter->done_event->Signal(); -} - -void SyncChannel::SyncContext::OnWaitableEventSignaled(WaitableEvent* event) { - DCHECK(event == shutdown_event_); - // Process shut down before we can get a reply to a synchronous message. - // Cancel pending Send calls, which will end up setting the send done event. - CancelPendingSends(); -} - - -SyncChannel::SyncChannel( - const std::wstring& channel_id, Channel::Mode mode, - Channel::Listener* listener, MessageFilter* filter, - MessageLoop* ipc_message_loop, bool create_pipe_now, - WaitableEvent* shutdown_event) - : ChannelProxy( - channel_id, mode, ipc_message_loop, - new SyncContext(listener, filter, ipc_message_loop, shutdown_event), - create_pipe_now), - sync_messages_with_no_timeout_allowed_(true) { - // Ideally we only want to watch this object when running a nested message - // loop. However, we don't know when it exits if there's another nested - // message loop running under it or not, so we wouldn't know whether to - // stop or keep watching. So we always watch it, and create the event as - // manual reset since the object watcher might otherwise reset the event - // when we're doing a WaitMany. - dispatch_watcher_.StartWatching(sync_context()->GetDispatchEvent(), this); -} - -SyncChannel::~SyncChannel() { -} - -bool SyncChannel::Send(Message* message) { - return SendWithTimeout(message, base::kNoTimeout); -} - -bool SyncChannel::SendWithTimeout(Message* message, int timeout_ms) { - if (!message->is_sync()) { - ChannelProxy::Send(message); - return true; - } - - // *this* might get deleted in WaitForReply. - RefPtr context(sync_context()); - if (context->shutdown_event()->IsSignaled()) { - delete message; - return false; - } - - DCHECK(sync_messages_with_no_timeout_allowed_ || - timeout_ms != base::kNoTimeout); - SyncMessage* sync_msg = static_cast(message); - context->Push(sync_msg); - int message_id = SyncMessage::GetMessageId(*sync_msg); - WaitableEvent* pump_messages_event = sync_msg->pump_messages_event(); - - ChannelProxy::Send(message); - - if (timeout_ms != base::kNoTimeout) { - // We use the sync message id so that when a message times out, we don't - // confuse it with another send that is either above/below this Send in - // the call stack. - context->ipc_message_loop()->PostDelayedTask(FROM_HERE, - NewRunnableMethod(context.get(), - &SyncContext::OnSendTimeout, message_id), timeout_ms); - } - - // Wait for reply, or for any other incoming synchronous messages. - WaitForReply(pump_messages_event); - - return context->Pop(); -} - -void SyncChannel::WaitForReply(WaitableEvent* pump_messages_event) { - while (true) { - WaitableEvent* objects[] = { - sync_context()->GetDispatchEvent(), - sync_context()->GetSendDoneEvent(), - pump_messages_event - }; - - unsigned count = pump_messages_event ? 3: 2; - unsigned result = WaitableEvent::WaitMany(objects, count); - if (result == 0 /* dispatch event */) { - // We're waiting for a reply, but we received a blocking synchronous - // call. We must process it or otherwise a deadlock might occur. - sync_context()->GetDispatchEvent()->Reset(); - sync_context()->DispatchMessages(); - continue; - } - - if (result == 2 /* pump_messages_event */) - WaitForReplyWithNestedMessageLoop(); // Start a nested message loop. - - break; - } -} - -void SyncChannel::WaitForReplyWithNestedMessageLoop() { - WaitableEvent* old_done_event = send_done_watcher_.GetWatchedEvent(); - send_done_watcher_.StopWatching(); - send_done_watcher_.StartWatching(sync_context()->GetSendDoneEvent(), this); - bool old_state = MessageLoop::current()->NestableTasksAllowed(); - MessageLoop::current()->SetNestableTasksAllowed(true); - MessageLoop::current()->Run(); - MessageLoop::current()->SetNestableTasksAllowed(old_state); - if (old_done_event) - send_done_watcher_.StartWatching(old_done_event, this); -} - -void SyncChannel::OnWaitableEventSignaled(WaitableEvent* event) { - WaitableEvent* dispatch_event = sync_context()->GetDispatchEvent(); - if (event == dispatch_event) { - // The call to DispatchMessages might delete this object, so reregister - // the object watcher first. - dispatch_event->Reset(); - dispatch_watcher_.StartWatching(dispatch_event, this); - sync_context()->DispatchMessages(); - } else { - // We got the reply, timed out or the process shutdown. - DCHECK(event == sync_context()->GetSendDoneEvent()); - MessageLoop::current()->Quit(); - } -} - -} // namespace IPC diff --git a/ipc/chromium/src/chrome/common/ipc_sync_channel.h b/ipc/chromium/src/chrome/common/ipc_sync_channel.h deleted file mode 100644 index 498375d465..0000000000 --- a/ipc/chromium/src/chrome/common/ipc_sync_channel.h +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_COMMON_IPC_SYNC_SENDER_H__ -#define CHROME_COMMON_IPC_SYNC_SENDER_H__ - -#include -#include -#include "base/basictypes.h" -#include "base/lock.h" -#include "base/scoped_handle.h" -#include "base/waitable_event.h" -#include "base/waitable_event_watcher.h" -#include "chrome/common/ipc_channel_proxy.h" - -#include "nsAutoPtr.h" - -namespace IPC { - -class SyncMessage; -class MessageReplyDeserializer; - -// This is similar to IPC::ChannelProxy, with the added feature of supporting -// sending synchronous messages. -// Note that care must be taken that the lifetime of the ipc_thread argument -// is more than this object. If the message loop goes away while this object -// is running and it's used to send a message, then it will use the invalid -// message loop pointer to proxy it to the ipc thread. -class SyncChannel : public ChannelProxy, - public base::WaitableEventWatcher::Delegate { - public: - SyncChannel(const std::wstring& channel_id, Channel::Mode mode, - Channel::Listener* listener, MessageFilter* filter, - MessageLoop* ipc_message_loop, bool create_pipe_now, - base::WaitableEvent* shutdown_event); - ~SyncChannel(); - - virtual bool Send(Message* message); - virtual bool SendWithTimeout(Message* message, int timeout_ms); - - // Whether we allow sending messages with no time-out. - void set_sync_messages_with_no_timeout_allowed(bool value) { - sync_messages_with_no_timeout_allowed_ = value; - } - - protected: - class ReceivedSyncMsgQueue; - friend class ReceivedSyncMsgQueue; - - // SyncContext holds the per object data for SyncChannel, so that SyncChannel - // can be deleted while it's being used in a different thread. See - // ChannelProxy::Context for more information. - class SyncContext : public Context, - public base::WaitableEventWatcher::Delegate { - public: - SyncContext(Channel::Listener* listener, - MessageFilter* filter, - MessageLoop* ipc_thread, - base::WaitableEvent* shutdown_event); - - ~SyncContext(); - - // Adds information about an outgoing sync message to the context so that - // we know how to deserialize the reply. - void Push(IPC::SyncMessage* sync_msg); - - // Cleanly remove the top deserializer (and throw it away). Returns the - // result of the Send call for that message. - bool Pop(); - - // Returns an event that's set when the send is complete, timed out or the - // process shut down. - base::WaitableEvent* GetSendDoneEvent(); - - // Returns an event that's set when an incoming message that's not the reply - // needs to get dispatched (by calling SyncContext::DispatchMessages). - base::WaitableEvent* GetDispatchEvent(); - - void DispatchMessages(); - - // Checks if the given message is blocking the listener thread because of a - // synchronous send. If it is, the thread is unblocked and true is - // returned. Otherwise the function returns false. - bool TryToUnblockListener(const Message* msg); - - // Called on the IPC thread when a sync send that runs a nested message loop - // times out. - void OnSendTimeout(int message_id); - - base::WaitableEvent* shutdown_event() { return shutdown_event_; } - - private: - // IPC::ChannelProxy methods that we override. - - // Called on the listener thread. - virtual void Clear(); - - // Called on the IPC thread. - virtual void OnMessageReceived(const Message& msg); - virtual void OnChannelError(); - virtual void OnChannelOpened(); - virtual void OnChannelClosed(); - - // Cancels all pending Send calls. - void CancelPendingSends(); - - // WaitableEventWatcher::Delegate implementation. - virtual void OnWaitableEventSignaled(base::WaitableEvent* arg); - - // When sending a synchronous message, this structure contains an object - // that knows how to deserialize the response. - struct PendingSyncMsg { - PendingSyncMsg(int id, IPC::MessageReplyDeserializer* d, - base::WaitableEvent* e) : - id(id), deserializer(d), done_event(e), send_result(false) { } - int id; - IPC::MessageReplyDeserializer* deserializer; - base::WaitableEvent* done_event; - bool send_result; - }; - - typedef std::deque PendingSyncMessageQueue; - PendingSyncMessageQueue deserializers_; - Lock deserializers_lock_; - - RefPtr received_sync_msgs_; - - base::WaitableEvent* shutdown_event_; - base::WaitableEventWatcher shutdown_watcher_; - }; - - private: - // WaitableEventWatcher::Delegate implementation. - virtual void OnWaitableEventSignaled(base::WaitableEvent* arg); - - SyncContext* sync_context() { - return reinterpret_cast(context()); - } - - // Both these functions wait for a reply, timeout or process shutdown. The - // latter one also runs a nested message loop in the meantime. - void WaitForReply(base::WaitableEvent* pump_messages_event); - - // Runs a nested message loop until a reply arrives, times out, or the process - // shuts down. - void WaitForReplyWithNestedMessageLoop(); - - bool sync_messages_with_no_timeout_allowed_; - - // Used to signal events between the IPC and listener threads. - base::WaitableEventWatcher send_done_watcher_; - base::WaitableEventWatcher dispatch_watcher_; - - DISALLOW_EVIL_CONSTRUCTORS(SyncChannel); -}; - -} // namespace IPC - -#endif // CHROME_COMMON_IPC_SYNC_SENDER_H__ diff --git a/ipc/chromium/src/chrome/common/ipc_sync_message.cc b/ipc/chromium/src/chrome/common/ipc_sync_message.cc deleted file mode 100644 index df079f2374..0000000000 --- a/ipc/chromium/src/chrome/common/ipc_sync_message.cc +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "build/build_config.h" - -#if defined(OS_WIN) -#include -#endif -#include - -#include "base/logging.h" -#include "base/waitable_event.h" -#include "chrome/common/ipc_sync_message.h" - -namespace IPC { - -uint32_t SyncMessage::next_id_ = 0; -#define kSyncMessageHeaderSize 4 - -SyncMessage::SyncMessage( - int32_t routing_id, - uint16_t type, - PriorityValue priority, - MessageReplyDeserializer* deserializer) - : Message(routing_id, type, priority), - deserializer_(deserializer), - pump_messages_event_(NULL) - { - set_sync(); - set_unblock(true); - - // Add synchronous message data before the message payload. - SyncHeader header; - header.message_id = ++next_id_; - WriteSyncHeader(this, header); -} - -MessageReplyDeserializer* SyncMessage::GetReplyDeserializer() { - MessageReplyDeserializer* rv = deserializer_; - DCHECK(rv); - deserializer_ = NULL; - return rv; -} - -void SyncMessage::EnableMessagePumping() { - static base::WaitableEvent* dummy_event = new base::WaitableEvent(true, true); - DCHECK(!pump_messages_event_); - set_pump_messages_event(dummy_event); -} - -bool SyncMessage::IsMessageReplyTo(const Message& msg, int request_id) { - if (!msg.is_reply()) - return false; - - return GetMessageId(msg) == request_id; -} - -void* SyncMessage::GetDataIterator(const Message* msg) { - void* iter = const_cast(msg->payload()); - UpdateIter(&iter, kSyncMessageHeaderSize); - return iter; -} - -int SyncMessage::GetMessageId(const Message& msg) { - if (!msg.is_sync() && !msg.is_reply()) - return 0; - - SyncHeader header; - if (!ReadSyncHeader(msg, &header)) - return 0; - - return header.message_id; -} - -Message* SyncMessage::GenerateReply(const Message* msg) { - DCHECK(msg->is_sync()); - - Message* reply = new Message(msg->routing_id(), IPC_REPLY_ID, - msg->priority()); - reply->set_reply(); - - SyncHeader header; - - // use the same message id, but this time reply bit is set - header.message_id = GetMessageId(*msg); - WriteSyncHeader(reply, header); - - return reply; -} - -bool SyncMessage::ReadSyncHeader(const Message& msg, SyncHeader* header) { - DCHECK(msg.is_sync() || msg.is_reply()); - - void* iter = NULL; - bool result = msg.ReadInt(&iter, &header->message_id); - if (!result) { - NOTREACHED(); - return false; - } - - return true; -} - -bool SyncMessage::WriteSyncHeader(Message* msg, const SyncHeader& header) { - DCHECK(msg->is_sync() || msg->is_reply()); - DCHECK(msg->payload_size() == 0); - bool result = msg->WriteInt(header.message_id); - if (!result) { - NOTREACHED(); - return false; - } - - // Note: if you add anything here, you need to update kSyncMessageHeaderSize. - DCHECK(kSyncMessageHeaderSize == msg->payload_size()); - - return true; -} - - -bool MessageReplyDeserializer::SerializeOutputParameters(const Message& msg) { - return SerializeOutputParameters(msg, SyncMessage::GetDataIterator(&msg)); -} - -} // namespace IPC diff --git a/ipc/chromium/src/chrome/common/ipc_sync_message.h b/ipc/chromium/src/chrome/common/ipc_sync_message.h deleted file mode 100644 index 9670d98bf2..0000000000 --- a/ipc/chromium/src/chrome/common/ipc_sync_message.h +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_COMMON_IPC_SYNC_MESSAGE_H__ -#define CHROME_COMMON_IPC_SYNC_MESSAGE_H__ - -#if defined(OS_WIN) -#include -#endif -#include -#include "base/basictypes.h" -#include "chrome/common/ipc_message.h" - -namespace base { -class WaitableEvent; -} - -namespace IPC { - -class MessageReplyDeserializer; - -class SyncMessage : public Message { - public: - SyncMessage(int32_t routing_id, uint16_t type, PriorityValue priority, - MessageReplyDeserializer* deserializer); - - // Call this to get a deserializer for the output parameters. - // Note that this can only be called once, and the caller is responsible - // for deleting the deserializer when they're done. - MessageReplyDeserializer* GetReplyDeserializer(); - - // If this message can cause the receiver to block while waiting for user - // input (i.e. by calling MessageBox), then the caller needs to pump window - // messages and dispatch asynchronous messages while waiting for the reply. - // If this event is passed in, then window messages will start being pumped - // when it's set. Note that this behavior will continue even if the event is - // later reset. The event must be valid until after the Send call returns. - void set_pump_messages_event(base::WaitableEvent* event) { - pump_messages_event_ = event; - if (event) { - header()->flags |= PUMPING_MSGS_BIT; - } else { - header()->flags &= ~PUMPING_MSGS_BIT; - } - } - - // Call this if you always want to pump messages. You can call this method - // or set_pump_messages_event but not both. - void EnableMessagePumping(); - - base::WaitableEvent* pump_messages_event() const { - return pump_messages_event_; - } - - // Returns true if the message is a reply to the given request id. - static bool IsMessageReplyTo(const Message& msg, int request_id); - - // Given a reply message, returns an iterator to the beginning of the data - // (i.e. skips over the synchronous specific data). - static void* GetDataIterator(const Message* msg); - - // Given a synchronous message (or its reply), returns its id. - static int GetMessageId(const Message& msg); - - // Generates a reply message to the given message. - static Message* GenerateReply(const Message* msg); - - private: - struct SyncHeader { - // unique ID (unique per sender) - int message_id; - }; - - static bool ReadSyncHeader(const Message& msg, SyncHeader* header); - static bool WriteSyncHeader(Message* msg, const SyncHeader& header); - - MessageReplyDeserializer* deserializer_; - base::WaitableEvent* pump_messages_event_; - - static uint32_t next_id_; // for generation of unique ids -}; - -// Used to deserialize parameters from a reply to a synchronous message -class MessageReplyDeserializer { - public: - bool SerializeOutputParameters(const Message& msg); - virtual ~MessageReplyDeserializer() {} - private: - // Derived classes need to implement this, using the given iterator (which - // is skipped past the header for synchronous messages). - virtual bool SerializeOutputParameters(const Message& msg, void* iter) = 0; -}; - -} // namespace IPC - -#endif // CHROME_COMMON_IPC_SYNC_MESSAGE_H__ diff --git a/ipc/chromium/src/chrome/common/message_router.cc b/ipc/chromium/src/chrome/common/message_router.cc deleted file mode 100644 index 9b49810442..0000000000 --- a/ipc/chromium/src/chrome/common/message_router.cc +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/common/message_router.h" - -void MessageRouter::OnControlMessageReceived(const IPC::Message& msg) { - NOTREACHED() << - "should override in subclass if you care about control messages"; -} - -bool MessageRouter::Send(IPC::Message* msg) { - NOTREACHED() << - "should override in subclass if you care about sending messages"; - return false; -} - -void MessageRouter::AddRoute(int32_t routing_id, - IPC::Channel::Listener* listener) { - routes_.AddWithID(listener, routing_id); -} - -void MessageRouter::RemoveRoute(int32_t routing_id) { - routes_.Remove(routing_id); -} - -void MessageRouter::OnMessageReceived(const IPC::Message& msg) { - if (msg.routing_id() == MSG_ROUTING_CONTROL) { - OnControlMessageReceived(msg); - } else { - RouteMessage(msg); - } -} - -bool MessageRouter::RouteMessage(const IPC::Message& msg) { - IPC::Channel::Listener* listener = routes_.Lookup(msg.routing_id()); - if (!listener) - return false; - - listener->OnMessageReceived(msg); - return true; -} diff --git a/ipc/chromium/src/chrome/common/message_router.h b/ipc/chromium/src/chrome/common/message_router.h deleted file mode 100644 index 2d4264e242..0000000000 --- a/ipc/chromium/src/chrome/common/message_router.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_COMMON_MESSAGE_ROUTER_H__ -#define CHROME_COMMON_MESSAGE_ROUTER_H__ - -#include "base/id_map.h" -#include "chrome/common/ipc_channel.h" - -// The MessageRouter handles all incoming messages sent to it by routing them -// to the correct listener. Routing is based on the Message's routing ID. -// Since routing IDs are typically assigned asynchronously by the browser -// process, the MessageRouter has the notion of pending IDs for listeners that -// have not yet been assigned a routing ID. -// -// When a message arrives, the routing ID is used to index the set of routes to -// find a listener. If a listener is found, then the message is passed to it. -// Otherwise, the message is ignored if its routing ID is not equal to -// MSG_ROUTING_CONTROL. -// -// The MessageRouter supports the IPC::Message::Sender interface for outgoing -// messages, but does not define a meaningful implementation of it. The -// subclass of MessageRouter is intended to provide that if appropriate. -// -// The MessageRouter can be used as a concrete class provided its Send method -// is not called and it does not receive any control messages. - -class MessageRouter : public IPC::Channel::Listener, - public IPC::Message::Sender { - public: - MessageRouter() {} - virtual ~MessageRouter() {} - - // Implemented by subclasses to handle control messages - virtual void OnControlMessageReceived(const IPC::Message& msg); - - // IPC::Channel::Listener implementation: - virtual void OnMessageReceived(const IPC::Message& msg); - - // Like OnMessageReceived, except it only handles routed messages. Returns - // true if the message was dispatched, or false if there was no listener for - // that route id. - virtual bool RouteMessage(const IPC::Message& msg); - - // IPC::Message::Sender implementation: - virtual bool Send(IPC::Message* msg); - - // Called to add/remove a listener for a particular message routing ID. - void AddRoute(int32_t routing_id, IPC::Channel::Listener* listener); - void RemoveRoute(int32_t routing_id); - - private: - // A list of all listeners with assigned routing IDs. - IDMap routes_; - - DISALLOW_EVIL_CONSTRUCTORS(MessageRouter); -}; - -#endif // CHROME_COMMON_MESSAGE_ROUTER_H__ diff --git a/ipc/chromium/src/third_party/libeventcommon.mozbuild b/ipc/chromium/src/third_party/libeventcommon.mozbuild index bb8f41bcf4..597e4705c7 100644 --- a/ipc/chromium/src/third_party/libeventcommon.mozbuild +++ b/ipc/chromium/src/third_party/libeventcommon.mozbuild @@ -32,6 +32,7 @@ if os_posix and not CONFIG['MOZ_NATIVE_LIBEVENT']: DEFINES['HAVE_CONFIG_H'] = True LOCAL_INCLUDES += sorted([ 'libevent', + 'libevent/compat', 'libevent/include', 'libevent/' + libevent_include_suffix, ]) diff --git a/ipc/glue/CrossProcessMutex.h b/ipc/glue/CrossProcessMutex.h index 2d6bb2ad0d..5b60cab333 100644 --- a/ipc/glue/CrossProcessMutex.h +++ b/ipc/glue/CrossProcessMutex.h @@ -9,7 +9,7 @@ #include "base/process.h" #include "mozilla/Mutex.h" -#if defined(OS_LINUX) || defined(XP_DARWIN) +#if !defined(OS_WIN) && !defined(OS_NETBSD) && !defined(OS_OPENBSD) #include #include "SharedMemoryBasic.h" #include "mozilla/Atomics.h" @@ -34,7 +34,7 @@ struct ParamTraits; namespace mozilla { #if defined(OS_WIN) typedef HANDLE CrossProcessMutexHandle; -#elif defined(OS_LINUX) || defined(OS_MACOSX) +#elif !defined(OS_NETBSD) && !defined(OS_OPENBSD) typedef mozilla::ipc::SharedMemoryBasic::Handle CrossProcessMutexHandle; #else // Stub for other platforms. We can't use uintptr_t here since different @@ -100,7 +100,7 @@ private: #if defined(OS_WIN) HANDLE mMutex; -#elif defined(OS_LINUX) || defined(OS_MACOSX) +#elif !defined(OS_NETBSD) && !defined(OS_OPENBSD) RefPtr mSharedBuffer; pthread_mutex_t* mMutex; mozilla::Atomic* mCount; diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp index 2635ab9805..9ada6a376a 100644 --- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -22,6 +22,8 @@ #include "prenv.h" #include "nsXPCOMPrivate.h" +#include "nsExceptionHandler.h" + #include "nsDirectoryServiceDefs.h" #include "nsIFile.h" #include "nsPrintfCString.h" @@ -1086,7 +1088,7 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector& aExt EnvironmentLog("MOZ_PROCESS_LOG").print( "==> process %d launched child process %d (%S)\n", base::GetCurrentProcId(), base::GetProcId(process), - cmdLine.command_line_string()); + cmdLine.command_line_string().c_str()); } } else #endif @@ -1162,11 +1164,11 @@ GeckoChildProcessHost::OnChannelConnected(int32_t peer_pid) } void -GeckoChildProcessHost::OnMessageReceived(const IPC::Message& aMsg) +GeckoChildProcessHost::OnMessageReceived(IPC::Message&& aMsg) { // We never process messages ourself, just save them up for the next // listener. - mQueue.push(aMsg); + mQueue.push(Move(aMsg)); } void diff --git a/ipc/glue/GeckoChildProcessHost.h b/ipc/glue/GeckoChildProcessHost.h index 07df09880c..7a52350427 100644 --- a/ipc/glue/GeckoChildProcessHost.h +++ b/ipc/glue/GeckoChildProcessHost.h @@ -83,7 +83,7 @@ public: base::ProcessArchitecture aArch=base::GetCurrentProcessArchitecture()); virtual void OnChannelConnected(int32_t peer_pid); - virtual void OnMessageReceived(const IPC::Message& aMsg); + virtual void OnMessageReceived(IPC::Message&& aMsg); virtual void OnChannelError(); virtual void GetQueuedMessages(std::queue& queue); diff --git a/ipc/glue/IPCMessageUtils.h b/ipc/glue/IPCMessageUtils.h index f3b6035132..202aa440cb 100644 --- a/ipc/glue/IPCMessageUtils.h +++ b/ipc/glue/IPCMessageUtils.h @@ -116,8 +116,17 @@ struct EnumSerializer { static bool Read(const Message* aMsg, void** aIter, paramType* aResult) { uintParamType value; - if(!ReadParam(aMsg, aIter, &value) || - !EnumValidator::IsLegalValue(paramType(value))) { + if (!ReadParam(aMsg, aIter, &value)) { +#ifdef MOZ_CRASHREPORTER + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCReadErrorReason"), + NS_LITERAL_CSTRING("Bad iter")); +#endif + return false; + } else if (!EnumValidator::IsLegalValue(paramType(value))) { +#ifdef MOZ_CRASHREPORTER + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCReadErrorReason"), + NS_LITERAL_CSTRING("Illegal value")); +#endif return false; } *aResult = paramType(value); diff --git a/ipc/glue/MessageChannel.cpp b/ipc/glue/MessageChannel.cpp index c1e6df0a48..24ca3406c9 100644 --- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -119,7 +119,7 @@ static MessageChannel* gParentProcessBlocker; namespace mozilla { namespace ipc { -static const int kMinTelemetryMessageSize = 8192; +static const uint32_t kMinTelemetryMessageSize = 8192; const int32_t MessageChannel::kNoTimeout = INT32_MIN; @@ -757,8 +757,9 @@ MessageChannel::Echo(Message* aMsg) bool MessageChannel::Send(Message* aMsg) { - if (aMsg->size() >= kMinTelemetryMessageSize) { - Telemetry::Accumulate(Telemetry::IPC_MESSAGE_SIZE, nsCString(aMsg->name()), aMsg->size()); + if (aMsg->capacity() >= kMinTelemetryMessageSize) { + Telemetry::Accumulate(Telemetry::IPC_MESSAGE_SIZE, + nsCString(aMsg->name()), aMsg->capacity()); } CxxStackFrame frame(*this, OUT_MESSAGE, aMsg); @@ -875,7 +876,7 @@ public: }; void -MessageChannel::OnMessageReceivedFromLink(const Message& aMsg) +MessageChannel::OnMessageReceivedFromLink(Message&& aMsg) { AssertLinkThread(); mMonitor->AssertCurrentThreadOwns(); @@ -972,7 +973,7 @@ MessageChannel::OnMessageReceivedFromLink(const Message& aMsg) // blocked. This is okay, since we always check for pending events before // blocking again. - mPending.push_back(aMsg); + mPending.push_back(Move(aMsg)); if (shouldWakeUp) { NotifyWorkerThread(); @@ -988,14 +989,13 @@ MessageChannel::OnMessageReceivedFromLink(const Message& aMsg) } void -MessageChannel::PeekMessages(msgid_t aMsgId, mozilla::function aInvoke) +MessageChannel::PeekMessages(mozilla::function aInvoke) { MonitorAutoLock lock(*mMonitor); for (MessageQueue::iterator it = mPending.begin(); it != mPending.end(); it++) { Message &msg = *it; - - if (msg.type() == aMsgId && !aInvoke(msg)) { + if (!aInvoke(msg)) { break; } } @@ -1057,8 +1057,9 @@ MessageChannel::ProcessPendingRequests(AutoEnterTransaction& aTransaction) bool MessageChannel::Send(Message* aMsg, Message* aReply) { - if (aMsg->size() >= kMinTelemetryMessageSize) { - Telemetry::Accumulate(Telemetry::IPC_MESSAGE_SIZE, nsCString(aMsg->name()), aMsg->size()); + if (aMsg->capacity() >= kMinTelemetryMessageSize) { + Telemetry::Accumulate(Telemetry::IPC_MESSAGE_SIZE, + nsCString(aMsg->name()), aMsg->capacity()); } nsAutoPtr msg(aMsg); @@ -1750,7 +1751,7 @@ MessageChannel::MaybeUndeferIncall() "fatal logic error"); // maybe time to process this message - Message call = mDeferred.top(); + Message call(Move(mDeferred.top())); mDeferred.pop(); // fix up fudge factor we added to account for race @@ -1758,7 +1759,7 @@ MessageChannel::MaybeUndeferIncall() --mRemoteStackDepthGuess; MOZ_RELEASE_ASSERT(call.priority() == IPC::Message::PRIORITY_NORMAL); - mPending.push_back(call); + mPending.push_back(Move(call)); } void diff --git a/ipc/glue/MessageChannel.h b/ipc/glue/MessageChannel.h index dbdf38ce31..5f0aa6bbed 100644 --- a/ipc/glue/MessageChannel.h +++ b/ipc/glue/MessageChannel.h @@ -110,10 +110,10 @@ class MessageChannel : HasResultCodes mAbortOnError = abort; } - // Call aInvoke for each pending message of type aId until it returns false. + // Call aInvoke for each pending message until it returns false. // XXX: You must get permission from an IPC peer to use this function // since it requires custom deserialization and re-orders events. - void PeekMessages(Message::msgid_t aId, mozilla::function aInvoke); + void PeekMessages(mozilla::function aInvoke); // Misc. behavioral traits consumers can request for this channel enum ChannelFlags { @@ -431,7 +431,7 @@ class MessageChannel : HasResultCodes bool WasTransactionCanceled(int transaction); bool ShouldDeferMessage(const Message& aMsg); - void OnMessageReceivedFromLink(const Message& aMsg); + void OnMessageReceivedFromLink(Message&& aMsg); void OnChannelErrorFromLink(); private: diff --git a/ipc/glue/MessageLink.cpp b/ipc/glue/MessageLink.cpp index 75e1250008..eb8f9a17f6 100644 --- a/ipc/glue/MessageLink.cpp +++ b/ipc/glue/MessageLink.cpp @@ -271,7 +271,7 @@ ThreadLink::EchoMessage(Message *msg) mChan->AssertWorkerThread(); mChan->mMonitor->AssertCurrentThreadOwns(); - mChan->OnMessageReceivedFromLink(*msg); + mChan->OnMessageReceivedFromLink(Move(*msg)); delete msg; } @@ -282,7 +282,7 @@ ThreadLink::SendMessage(Message *msg) mChan->mMonitor->AssertCurrentThreadOwns(); if (mTargetChan) - mTargetChan->OnMessageReceivedFromLink(*msg); + mTargetChan->OnMessageReceivedFromLink(Move(*msg)); delete msg; } @@ -322,19 +322,19 @@ ThreadLink::Unsound_NumQueuedMessages() const // void -ProcessLink::OnMessageReceived(const Message& msg) +ProcessLink::OnMessageReceived(Message&& msg) { AssertIOThread(); NS_ASSERTION(mChan->mChannelState != ChannelError, "Shouldn't get here!"); MonitorAutoLock lock(*mChan->mMonitor); - mChan->OnMessageReceivedFromLink(msg); + mChan->OnMessageReceivedFromLink(Move(msg)); } void ProcessLink::OnEchoMessage(Message* msg) { AssertIOThread(); - OnMessageReceived(*msg); + OnMessageReceived(Move(*msg)); delete msg; } @@ -381,7 +381,7 @@ ProcessLink::OnTakeConnectedChannel() // Dispatch whatever messages the previous listener had queued up. while (!pending.empty()) { - OnMessageReceived(pending.front()); + OnMessageReceived(Move(pending.front())); pending.pop(); } } diff --git a/ipc/glue/MessageLink.h b/ipc/glue/MessageLink.h index b3ff141c2f..d0ad978dbd 100644 --- a/ipc/glue/MessageLink.h +++ b/ipc/glue/MessageLink.h @@ -204,7 +204,7 @@ class ProcessLink // These methods acquire the monitor and forward to the // similarly named methods in AsyncChannel below // (OnMessageReceivedFromLink(), etc) - virtual void OnMessageReceived(const Message& msg) override; + virtual void OnMessageReceived(Message&& msg) override; virtual void OnChannelConnected(int32_t peer_pid) override; virtual void OnChannelError() override; diff --git a/ipc/glue/ProtocolUtils.cpp b/ipc/glue/ProtocolUtils.cpp index f6df3b0149..1afeebfef1 100644 --- a/ipc/glue/ProtocolUtils.cpp +++ b/ipc/glue/ProtocolUtils.cpp @@ -7,6 +7,10 @@ #include "base/process_util.h" +#ifdef OS_POSIX +#include +#endif + #include "mozilla/ipc/MessageChannel.h" #include "mozilla/ipc/ProtocolUtils.h" #include "mozilla/ipc/Transport.h" @@ -17,6 +21,13 @@ #include "mozilla/sandboxTarget.h" #endif +#if defined(MOZ_CRASHREPORTER) && defined(XP_WIN) +#include "aclapi.h" +#include "sddl.h" + +#include "mozilla/TypeTraits.h" +#endif + using namespace IPC; using base::GetCurrentProcId; @@ -24,6 +35,17 @@ using base::ProcessHandle; using base::ProcessId; namespace mozilla { + +#if defined(MOZ_CRASHREPORTER) && defined(XP_WIN) +// Generate RAII classes for LPTSTR and PSECURITY_DESCRIPTOR. +MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedLPTStr, \ + RemovePointer::Type, \ + ::LocalFree) +MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPSecurityDescriptor, \ + RemovePointer::Type, \ + ::LocalFree) +#endif + namespace ipc { ProtocolCloneContext::ProtocolCloneContext() @@ -284,8 +306,15 @@ bool DuplicateHandle(HANDLE aSourceHandle, #endif // Finally, see if we already have access to the process. - ScopedProcessHandle targetProcess; - if (!base::OpenProcessHandle(aTargetProcessId, &targetProcess.rwget())) { + ScopedProcessHandle targetProcess(OpenProcess(PROCESS_DUP_HANDLE, + FALSE, + aTargetProcessId)); + if (!targetProcess) { +#ifdef MOZ_CRASH_REPORTER + CrashReporter::AnnotateCrashReport( + NS_LITERAL_CSTRING("IPCTransportFailureReason"), + NS_LITERAL_CSTRING("Failed to open target process.")); +#endif return false; } @@ -295,6 +324,120 @@ bool DuplicateHandle(HANDLE aSourceHandle, } #endif +#ifdef MOZ_CRASHREPORTER +void +AnnotateSystemError() +{ + int64_t error = 0; +#if defined(XP_WIN) + error = ::GetLastError(); +#elif defined(OS_POSIX) + error = errno; +#endif + if (error) { + CrashReporter::AnnotateCrashReport( + NS_LITERAL_CSTRING("IPCSystemError"), + nsPrintfCString("%lld", error)); + } +} + +void +AnnotateProcessInformation(base::ProcessId aPid) +{ +#ifdef XP_WIN + ScopedProcessHandle processHandle( + OpenProcess(READ_CONTROL|PROCESS_QUERY_INFORMATION, FALSE, aPid)); + if (!processHandle) { + CrashReporter::AnnotateCrashReport( + NS_LITERAL_CSTRING("IPCExtraSystemError"), + nsPrintfCString("Failed to get information of process %d, error(%d)", + aPid, + ::GetLastError())); + return; + } + + DWORD exitCode = 0; + if (!::GetExitCodeProcess(processHandle, &exitCode)) { + CrashReporter::AnnotateCrashReport( + NS_LITERAL_CSTRING("IPCExtraSystemError"), + nsPrintfCString("Failed to get exit information of process %d, error(%d)", + aPid, + ::GetLastError())); + return; + } + + if (exitCode != STILL_ACTIVE) { + CrashReporter::AnnotateCrashReport( + NS_LITERAL_CSTRING("IPCExtraSystemError"), + nsPrintfCString("Process %d is not alive. Exit code: %d", + aPid, + exitCode)); + return; + } + + ScopedPSecurityDescriptor secDesc(nullptr); + PSID ownerSid = nullptr; + DWORD rv = ::GetSecurityInfo(processHandle, + SE_KERNEL_OBJECT, + OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, + &ownerSid, + nullptr, + nullptr, + nullptr, + &secDesc.rwget()); + if (rv != ERROR_SUCCESS) { + // GetSecurityInfo() failed. + CrashReporter::AnnotateCrashReport( + NS_LITERAL_CSTRING("IPCExtraSystemError"), + nsPrintfCString("Failed to get security information of process %d," + " error(%d)", + aPid, + rv)); + return; + } + + ScopedLPTStr ownerSidStr(nullptr); + nsString annotation{}; + annotation.AppendLiteral("Owner: "); + if (::ConvertSidToStringSid(ownerSid, &ownerSidStr.rwget())) { + annotation.Append(ownerSidStr.get()); + } + + ScopedLPTStr secDescStr(nullptr); + annotation.AppendLiteral(", Security Descriptor: "); + if (::ConvertSecurityDescriptorToStringSecurityDescriptor(secDesc, + SDDL_REVISION_1, + DACL_SECURITY_INFORMATION, + &secDescStr.rwget(), + nullptr)) { + annotation.Append(secDescStr.get()); + } + + CrashReporter::AnnotateCrashReport( + NS_LITERAL_CSTRING("IPCExtraSystemError"), + NS_ConvertUTF16toUTF8(annotation)); +#endif +} +#endif + +void +LogMessageForProtocol(const char* aTopLevelProtocol, base::ProcessId aOtherPid, + const char* aContextDescription, + const char* aMessageDescription, + MessageDirection aDirection) +{ + nsPrintfCString logMessage("[time: %" PRId64 "][%d%s%d] [%s] %s %s\n", + PR_Now(), base::GetCurrentProcId(), + aDirection == MessageDirection::eReceiving ? "<-" : "->", + aOtherPid, aTopLevelProtocol, + aContextDescription, + aMessageDescription); +#ifdef ANDROID + __android_log_write(ANDROID_LOG_INFO, "GeckoIPC", logMessage.get()); +#endif + fputs(logMessage.get(), stderr); +} + void ProtocolErrorBreakpoint(const char* aMsg) { @@ -305,8 +448,7 @@ ProtocolErrorBreakpoint(const char* aMsg) } void -FatalError(const char* aProtocolName, const char* aMsg, - ProcessId aOtherPid, bool aIsParent) +FatalError(const char* aProtocolName, const char* aMsg, bool aIsParent) { ProtocolErrorBreakpoint(aMsg); @@ -325,6 +467,7 @@ FatalError(const char* aProtocolName, const char* aMsg, nsDependentCString(aProtocolName)); CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCFatalErrorMsg"), nsDependentCString(aMsg)); + AnnotateSystemError(); #endif MOZ_CRASH("IPC FatalError in the parent process!"); } else { @@ -333,5 +476,11 @@ FatalError(const char* aProtocolName, const char* aMsg, } } +void +LogicError(const char* aMsg) +{ + NS_RUNTIMEABORT(aMsg); +} + } // namespace ipc } // namespace mozilla diff --git a/ipc/glue/ProtocolUtils.h b/ipc/glue/ProtocolUtils.h index 36bc3bf5b9..5c4645b80f 100644 --- a/ipc/glue/ProtocolUtils.h +++ b/ipc/glue/ProtocolUtils.h @@ -296,12 +296,32 @@ LoggingEnabledFor(const char *aTopLevelProtocol) #endif } +enum class MessageDirection { + eSending, + eReceiving, +}; + +MOZ_NEVER_INLINE void +LogMessageForProtocol(const char* aTopLevelProtocol, base::ProcessId aOtherPid, + const char* aContextDescription, + const char* aMessageDescription, + MessageDirection aDirection); + MOZ_NEVER_INLINE void ProtocolErrorBreakpoint(const char* aMsg); +// The code generator calls this function for errors which come from the +// methods of protocols. Doing this saves codesize by making the error +// cases significantly smaller. MOZ_NEVER_INLINE void -FatalError(const char* aProtocolName, const char* aMsg, - base::ProcessId aOtherPid, bool aIsParent); +FatalError(const char* aProtocolName, const char* aMsg, bool aIsParent); + +// The code generator calls this function for errors which are not +// protocol-specific: errors in generated struct methods or errors in +// transition functions, for instance. Doing this saves codesize by +// by making the error cases significantly smaller. +MOZ_NEVER_INLINE void +LogicError(const char* aMsg); struct PrivateIPDLInterface {}; @@ -333,6 +353,18 @@ DuplicateHandle(HANDLE aSourceHandle, DWORD aOptions); #endif +/** + * Annotate the crash reporter with the error code from the most recent system + * call. Returns the system error. + */ +#ifdef MOZ_CRASHREPORTER +void AnnotateSystemError(); +void AnnotateProcessInformation(base::ProcessId aPid); +#else +#define AnnotateSystemError() do { } while (0) +#define AnnotateProcessInformation(...) do { } while (0) +#endif + /** * An endpoint represents one end of a partially initialized IPDL channel. To * set up a new top-level protocol: diff --git a/ipc/glue/Transport_posix.cpp b/ipc/glue/Transport_posix.cpp index 95be9bb4ee..bd131a5e52 100644 --- a/ipc/glue/Transport_posix.cpp +++ b/ipc/glue/Transport_posix.cpp @@ -15,6 +15,7 @@ #include "mozilla/ipc/Transport.h" #include "mozilla/ipc/FileDescriptor.h" +#include "ProtocolUtils.h" using namespace std; @@ -70,6 +71,9 @@ DuplicateDescriptor(const TransportDescriptor& aTd) { TransportDescriptor result = aTd; result.mFd.fd = dup(aTd.mFd.fd); + if (result.mFd.fd == -1) { + AnnotateSystemError(); + } MOZ_RELEASE_ASSERT(result.mFd.fd != -1, "DuplicateDescriptor failed"); return result; } diff --git a/ipc/glue/Transport_win.cpp b/ipc/glue/Transport_win.cpp index a79b1bdb7f..2873aac975 100644 --- a/ipc/glue/Transport_win.cpp +++ b/ipc/glue/Transport_win.cpp @@ -64,6 +64,10 @@ TransferHandleToProcess(HANDLE source, base::ProcessId pid) DWORD access = 0; DWORD options = DUPLICATE_SAME_ACCESS; bool ok = DuplicateHandle(source, pid, &handleDup, access, options); + if (!ok) { + AnnotateSystemError(); + AnnotateProcessInformation(pid); + } MOZ_RELEASE_ASSERT(ok); // Now close our own copy of the handle (we're supposed to be transferring, @@ -103,6 +107,9 @@ DuplicateDescriptor(const TransportDescriptor& aTd) DWORD options = DUPLICATE_SAME_ACCESS; bool ok = DuplicateHandle(aTd.mServerPipeHandle, base::GetCurrentProcId(), &serverDup, access, options); + if (!ok) { + AnnotateSystemError(); + } MOZ_RELEASE_ASSERT(ok); TransportDescriptor desc = aTd; diff --git a/ipc/glue/moz.build b/ipc/glue/moz.build index 307e1773cd..1d00648167 100644 --- a/ipc/glue/moz.build +++ b/ipc/glue/moz.build @@ -61,7 +61,7 @@ if CONFIG['OS_ARCH'] == 'WINNT': SOURCES += [ 'CrossProcessMutex_windows.cpp', ] -elif CONFIG['OS_ARCH'] in ('Linux', 'Darwin'): +elif not CONFIG['OS_ARCH'] in ('NetBSD', 'OpenBSD'): UNIFIED_SOURCES += [ 'CrossProcessMutex_posix.cpp', ] @@ -176,6 +176,10 @@ for var in ('MOZ_CHILD_PROCESS_NAME', 'MOZ_CHILD_PROCESS_NAME_PIE', 'MOZ_CHILD_PROCESS_BUNDLE', 'DLL_PREFIX', 'DLL_SUFFIX'): DEFINES[var] = '"%s"' % CONFIG[var] +LOCAL_INCLUDES += [ + '/toolkit/crashreporter', +] + if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_ARCH'] == 'WINNT': LOCAL_INCLUDES += [ '/security/sandbox/chromium', diff --git a/ipc/ipdl/ipdl/builtin.py b/ipc/ipdl/ipdl/builtin.py index 0b23d045ae..46d5f16228 100644 --- a/ipc/ipdl/ipdl/builtin.py +++ b/ipc/ipdl/ipdl/builtin.py @@ -43,7 +43,6 @@ Types = ( HeaderIncludes = ( 'mozilla/Attributes.h', - 'prtime.h', 'IPCMessageStart.h', 'ipc/IPCMessageUtils.h', 'mozilla/RefPtr.h', diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py index 619663d5f3..af627ffdb8 100644 --- a/ipc/ipdl/ipdl/lower.py +++ b/ipc/ipdl/ipdl/lower.py @@ -314,12 +314,6 @@ def _abortIfFalse(cond, msg): ExprVar('MOZ_DIAGNOSTIC_ASSERT'), [ cond, ExprLiteral.String(msg) ])) -def _runtimeAbort(msg): - if isinstance(msg, str): - msg = ExprLiteral.String(msg) - return StmtExpr( - ExprCall(ExprVar('NS_RUNTIMEABORT'), args=[ msg ])) - def _refptr(T): return Type('RefPtr', T=T) @@ -399,13 +393,13 @@ def _protocolErrorBreakpoint(msg): return StmtExpr(ExprCall(ExprVar('mozilla::ipc::ProtocolErrorBreakpoint'), args=[ msg ])) -def _ipcFatalError(name, msg, otherpid, isparent): +def _ipcFatalError(name, msg, isparent): if isinstance(name, str): name = ExprLiteral.String(name) if isinstance(msg, str): msg = ExprLiteral.String(msg) return StmtExpr(ExprCall(ExprVar('mozilla::ipc::FatalError'), - args=[ name, msg, otherpid, isparent ])) + args=[ name, msg, isparent ])) def _printWarningMessage(msg): if isinstance(msg, str): @@ -417,6 +411,10 @@ def _fatalError(msg): return StmtExpr( ExprCall(ExprVar('FatalError'), args=[ ExprLiteral.String(msg) ])) +def _logicError(msg): + return StmtExpr( + ExprCall(ExprVar('mozilla::ipc::LogicError'), args=[ ExprLiteral.String(msg) ])) + def _killProcess(pid): return ExprCall( ExprVar('base::KillProcess'), @@ -915,41 +913,31 @@ class MessageDecl(ipdl.ast.MessageDecl): or self.decl.type.isCtor() or self.decl.type.isDtor()) - def msgClass(self): + def msgCtorFunc(self): return 'Msg_%s'% (self.decl.progname) def prettyMsgName(self, pfx=''): - return pfx + self.msgClass() + return pfx + self.msgCtorFunc() - def pqMsgClass(self): - return '%s::%s'% (self.namespace, self.msgClass()) + def pqMsgCtorFunc(self): + return '%s::%s'% (self.namespace, self.msgCtorFunc()) - def msgCast(self, msgexpr): - return ExprCast(msgexpr, self.msgCxxType(const=1, ptr=1), static=1) - - def msgCxxType(self, const=0, ref=0, ptr=0): - return Type(self.pqMsgClass(), const=const, ref=ref, ptr=ptr) - - def msgId(self): return self.msgClass()+ '__ID' + def msgId(self): return self.msgCtorFunc()+ '__ID' def pqMsgId(self): return '%s::%s'% (self.namespace, self.msgId()) - def replyClass(self): + def replyCtorFunc(self): return 'Reply_%s'% (self.decl.progname) - def pqReplyClass(self): - return '%s::%s'% (self.namespace, self.replyClass()) + def pqReplyCtorFunc(self): + return '%s::%s'% (self.namespace, self.replyCtorFunc()) - def replyCast(self, replyexpr): - return ExprCast(replyexpr, Type(self.pqReplyClass(), const=1, ptr=1), - static=1) - - def replyId(self): return self.replyClass()+ '__ID' + def replyId(self): return self.replyCtorFunc()+ '__ID' def pqReplyId(self): return '%s::%s'% (self.namespace, self.replyId()) def prettyReplyName(self, pfx=''): - return pfx + self.replyClass() + return pfx + self.replyCtorFunc() def actorDecl(self): return self.params[0] @@ -1662,20 +1650,28 @@ class _GenerateProtocolCode(ipdl.ast.Visitor): self.funcDefns.append(tfDefn) for md in p.messageDecls: - ns.addstmts([ - _generateMessageClass(md.msgClass(), md.msgId(), - md.decl.type.priority, - md.prettyMsgName(p.name+'::'), - md.decl.type.compress), - Whitespace.NL ]) + decls = [] + + mfDecl, mfDefn = _splitFuncDeclDefn( + _generateMessageConstructor(md.msgCtorFunc(), md.msgId(), + md.decl.type.priority, + md.prettyMsgName(p.name+'::'), + md.decl.type.compress)) + decls.append(mfDecl) + self.funcDefns.append(mfDefn) + if md.hasReply(): - ns.addstmts([ - _generateMessageClass( - md.replyClass(), md.replyId(), + rfDecl, rfDefn = _splitFuncDeclDefn( + _generateMessageConstructor( + md.replyCtorFunc(), md.replyId(), md.decl.type.priority, md.prettyReplyName(p.name+'::'), - md.decl.type.compress), - Whitespace.NL ]) + md.decl.type.compress)) + decls.append(rfDecl) + self.funcDefns.append(rfDefn) + + decls.append(Whitespace.NL) + ns.addstmts(decls) ns.addstmts([ Whitespace.NL, Whitespace.NL ]) @@ -1848,7 +1844,7 @@ class _GenerateProtocolCode(ipdl.ast.Visitor): # special case for Dead deadblock = Block() deadblock.addstmts([ - _runtimeAbort('__delete__()d actor'), + _logicError('__delete__()d actor'), StmtReturn(ExprLiteral.FALSE) ]) fromswitch.addcase(CaseLabel(_deadState().name), deadblock) @@ -1863,13 +1859,13 @@ class _GenerateProtocolCode(ipdl.ast.Visitor): StmtReturn(ExprLiteral.TRUE)) else: dyingblock.addstmts([ - _runtimeAbort('__delete__()d (and unexpectedly dying) actor'), + _logicError('__delete__()d (and unexpectedly dying) actor'), StmtReturn(ExprLiteral.FALSE) ]) fromswitch.addcase(CaseLabel(_dyingState().name), dyingblock) unreachedblock = Block() unreachedblock.addstmts([ - _runtimeAbort('corrupted actor state'), + _logicError('corrupted actor state'), StmtReturn(ExprLiteral.FALSE) ]) fromswitch.addcase(DefaultLabel(), unreachedblock) @@ -1897,22 +1893,21 @@ class _GenerateProtocolCode(ipdl.ast.Visitor): ##-------------------------------------------------- -def _generateMessageClass(clsname, msgid, priority, prettyName, compress): - cls = Class(name=clsname, inherits=[ Inherit(Type('IPC::Message')) ]) - cls.addstmt(Label.PUBLIC) +def _generateMessageConstructor(clsname, msgid, priority, prettyName, compress): + routingId = ExprVar('routingId') - idenum = TypeEnum() - idenum.addId('ID', msgid) - cls.addstmt(StmtDecl(Decl(idenum, ''))) + func = FunctionDefn(FunctionDecl( + clsname, + params=[ Decl(Type('int32_t'), routingId.name) ], + ret=Type('IPC::Message', ptr=1))) - # make the message constructor if compress == 'compress': - compression = ExprVar('COMPRESSION_ENABLED') + compression = ExprVar('IPC::Message::COMPRESSION_ENABLED') elif compress: assert compress == 'compressall' - compression = ExprVar('COMPRESSION_ALL') + compression = ExprVar('IPC::Message::COMPRESSION_ALL') else: - compression = ExprVar('COMPRESSION_NONE') + compression = ExprVar('IPC::Message::COMPRESSION_NONE') if priority == ipdl.ast.NORMAL_PRIORITY: priorityEnum = 'IPC::Message::PRIORITY_NORMAL' elif priority == ipdl.ast.HIGH_PRIORITY: @@ -1920,74 +1915,16 @@ def _generateMessageClass(clsname, msgid, priority, prettyName, compress): else: assert priority == ipdl.ast.URGENT_PRIORITY priorityEnum = 'IPC::Message::PRIORITY_URGENT' - routingId = ExprVar('routingId') - ctor = ConstructorDefn( - ConstructorDecl(clsname, params=[ Decl(Type('int32_t'), routingId.name) ]), - memberinits=[ ExprMemberInit(ExprVar('IPC::Message'), - [ routingId, - ExprVar('ID'), - ExprVar(priorityEnum), - compression, - ExprLiteral.String(prettyName) ]) ]) - cls.addstmts([ ctor, Whitespace.NL ]) - # generate a logging function - # 'pfx' will be something like "[FooParent] sent" - pfxvar = ExprVar('pfx__') - otherpid = ExprVar('otherPid__') - receiving = ExprVar('receiving__') - logger = MethodDefn(MethodDecl( - 'Log', - params=([ Decl(Type('std::string', const=1, ref=1), pfxvar.name), - Decl(Type('base::ProcessId'), otherpid.name), - Decl(Type('bool'), receiving.name) ]), - const=1)) - # TODO/cjones: allow selecting what information is printed to - # the log - msgvar = ExprVar('logmsg__') - logger.addstmt(StmtDecl(Decl(Type('std::string'), msgvar.name))) + func.addstmt( + StmtReturn(ExprNew(Type('IPC::Message'), + args=[ routingId, + ExprVar(msgid), + ExprVar(priorityEnum), + compression, + ExprLiteral.String(prettyName) ]))) - def appendToMsg(thing): - return StmtExpr(ExprCall(ExprSelect(msgvar, '.', 'append'), - args=[ thing ])) - logger.addstmts([ - StmtExpr(ExprCall( - ExprVar('StringAppendF'), - args=[ ExprAddrOf(msgvar), - ExprLiteral.String('[time:%" PRId64 "][%d%s%d]'), - ExprCall(ExprVar('PR_Now')), - ExprCall(ExprVar('base::GetCurrentProcId')), - ExprConditional(receiving, ExprLiteral.String('<-'), - ExprLiteral.String('->')), - otherpid ])), - appendToMsg(pfxvar), - appendToMsg(ExprLiteral.String(clsname +'(')), - Whitespace.NL - ]) - - # TODO turn this back on when string stuff is sorted - - logger.addstmt(appendToMsg(ExprLiteral.String('[TODO])\\n'))) - - logger.addstmts([ - CppDirective('ifdef', 'ANDROID'), - StmtExpr(ExprCall( - ExprVar('__android_log_write'), - args=[ ExprVar('ANDROID_LOG_INFO'), - ExprLiteral.String('GeckoIPC'), - ExprCall(ExprSelect(msgvar, '.', 'c_str')) ])), - CppDirective('endif') - ]) - - # and actually print the log message - logger.addstmt(StmtExpr(ExprCall( - ExprVar('fputs'), - args=[ ExprCall(ExprSelect(msgvar, '.', 'c_str')), - ExprVar('stderr') ]))) - - cls.addstmt(logger) - - return cls + return func ##-------------------------------------------------- @@ -2345,7 +2282,7 @@ def _generateCxxUnion(ud): StmtBreak() ])) dtorswitch.addcase( DefaultLabel(), - StmtBlock([ _runtimeAbort("not reached"), StmtBreak() ])) + StmtBlock([ _logicError("not reached"), StmtBreak() ])) maybedtor.addstmts([ ifnone, ifnochange, @@ -2416,7 +2353,7 @@ def _generateCxxUnion(ud): StmtBlock([ StmtBreak() ])) copyswitch.addcase( DefaultLabel(), - StmtBlock([ _runtimeAbort('unreached'), StmtReturn() ])) + StmtBlock([ _logicError('unreached'), StmtReturn() ])) copyctor.addstmts([ StmtExpr(callAssertSanity(uvar=othervar)), copyswitch, @@ -2472,7 +2409,7 @@ def _generateCxxUnion(ud): StmtBreak() ])) opeqswitch.addcase( DefaultLabel(), - StmtBlock([ _runtimeAbort('unreached'), StmtBreak() ])) + StmtBlock([ _logicError('unreached'), StmtBreak() ])) opeq.addstmts([ StmtExpr(callAssertSanity(uvar=rhsvar)), StmtDecl(Decl(typetype, rhstypevar.name), init=ud.callType(rhsvar)), @@ -2513,7 +2450,7 @@ def _generateCxxUnion(ud): opeqeqswitch.addcase(CaseLabel(c.enum()), case) opeqeqswitch.addcase( DefaultLabel(), - StmtBlock([ _runtimeAbort('unreached'), + StmtBlock([ _logicError('unreached'), StmtReturn.FALSE ])) opeqeq.addstmt(opeqeqswitch) @@ -3297,7 +3234,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): ExprCall(p.processingErrorVar(), args=[ codevar, reasonvar ]))) else: onprocessingerror.addstmt( - _runtimeAbort("`OnProcessingError' called on non-toplevel actor")) + _fatalError("`OnProcessingError' called on non-toplevel actor")) self.cls.addstmts([ onprocessingerror, Whitespace.NL ]) # int32_t GetProtocolTypeId() { return PFoo; } @@ -3316,7 +3253,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): ExprCall(p.shouldContinueFromTimeoutVar()))) else: ontimeout.addstmts([ - _runtimeAbort("`OnReplyTimeout' called on non-toplevel actor"), + _fatalError("`OnReplyTimeout' called on non-toplevel actor"), StmtReturn.FALSE ]) @@ -3366,7 +3303,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): ]) else: onclose.addstmt( - _runtimeAbort("`OnClose' called on non-toplevel actor")) + _fatalError("`OnClose' called on non-toplevel actor")) self.cls.addstmts([ onclose, Whitespace.NL ]) # OnChannelError() @@ -3380,7 +3317,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): ]) else: onerror.addstmt( - _runtimeAbort("`OnError' called on non-toplevel actor")) + _fatalError("`OnError' called on non-toplevel actor")) self.cls.addstmts([ onerror, Whitespace.NL ]) # OnChannelConnected() @@ -3388,7 +3325,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): params=[ Decl(Type.INT32, 'aPid') ])) if not ptype.isToplevel(): onconnected.addstmt( - _runtimeAbort("'OnConnected' called on non-toplevel actor")) + _fatalError("'OnConnected' called on non-toplevel actor")) self.cls.addstmts([ onconnected, Whitespace.NL ]) @@ -3406,7 +3343,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): ExprSelect(p.channelVar(), '.', 'ProcessNativeEventsInInterruptCall'))), CppDirective('else'), - _runtimeAbort('This method is Windows-only'), + _fatalError('This method is Windows-only'), CppDirective('endif'), ]) @@ -3459,13 +3396,11 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): params=[ Decl(Type('char', const=1, ptrconst=1), msgparam.name) ], const=1, never_inline=1)) if self.side is 'parent': - otherpid = p.callOtherPid() isparent = ExprLiteral.TRUE else: - otherpid = ExprLiteral.ZERO isparent = ExprLiteral.FALSE fatalerror.addstmts([ - _ipcFatalError(actorname, msgparam, otherpid, isparent) + _ipcFatalError(actorname, msgparam, isparent) ]) self.cls.addstmts([ fatalerror, Whitespace.NL ]) @@ -3858,7 +3793,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): else: abort = StmtBlock() abort.addstmts([ - _runtimeAbort('this protocol tree does not use shmem'), + _fatalError('this protocol tree does not use shmem'), StmtReturn(_Result.NotKnown) ]) self.asyncSwitch.addcase( @@ -3900,9 +3835,9 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): getchannel.addstmt(StmtReturn(p.channelVar())) cloneprotocol.addstmts([ - _runtimeAbort('Clone() for ' + - p.name + - ' has not yet been implemented'), + _fatalError('Clone() for ' + + p.name + + ' has not yet been implemented'), StmtReturn(ExprLiteral.NULL) ]) @@ -3935,7 +3870,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): manageearray = p.managedVar(manageeipdltype, self.side) abortstmt = StmtIf(ExprBinary(actorvar, '==', ExprLiteral.NULL)) abortstmt.addifstmts([ - _runtimeAbort('can not clone an ' + actortype.name() + ' actor'), + _fatalError('can not clone an ' + actortype.name() + ' actor'), StmtReturn()]) forstmt = StmtFor( init=Param(Type.UINT32, ivar.name, ExprLiteral.ZERO), @@ -4001,7 +3936,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): virtual=1)) if not len(p.managesStmts): - removemanagee.addstmts([ _runtimeAbort('unreached'), StmtReturn() ]) + removemanagee.addstmts([ _fatalError('unreached'), StmtReturn() ]) else: switchontype = StmtSwitch(pvar) for managee in p.managesStmts: @@ -4027,7 +3962,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): switchontype.addcase(CaseLabel(_protocolId(manageeipdltype).name), case) default = StmtBlock() - default.addstmts([ _runtimeAbort('unreached'), StmtReturn() ]) + default.addstmts([ _fatalError('unreached'), StmtReturn() ]) switchontype.addcase(DefaultLabel(), default) removemanagee.addstmt(switchontype) @@ -4118,7 +4053,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): ifbad = StmtIf(ExprNot(okvar)) badShmemActions = [] if (self.side == 'child'): - badShmemActions.append(_runtimeAbort('bad Shmem')); + badShmemActions.append(_fatalError('bad Shmem')); else: badShmemActions.append(_printWarningMessage('bad Shmem')); badShmemActions.append(StmtReturn.FALSE); @@ -4288,7 +4223,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): pswitch.addcase(label, case) die = Block() - die.addstmts([ _runtimeAbort('Invalid protocol'), + die.addstmts([ _fatalError('Invalid protocol'), StmtReturn(_Result.ValuError) ]) pswitch.addcase(DefaultLabel(), die) @@ -4389,7 +4324,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): ifnull = StmtIf(ExprNot(var)) ifnotnullable = StmtIf(ExprNot(nullablevar)) ifnotnullable.addifstmt( - _runtimeAbort("NULL actor value passed to non-nullable param")) + _fatalError("NULL actor value passed to non-nullable param")) ifnull.addifstmt(ifnotnullable) ifnull.addifstmt(StmtExpr(ExprAssn(idvar, _NULL_ACTOR_ID))) # else @@ -4402,7 +4337,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): # this is always a hard-abort, because it means that some C++ # code has a live pointer to a freed actor, so we're playing # Russian roulette with invalid memory - iffreed.addifstmt(_runtimeAbort("actor has been |delete|d")) + iffreed.addifstmt(_fatalError("actor has been |delete|d")) ifnull.addelsestmt(iffreed) write.addstmts([ @@ -4696,7 +4631,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): writecase = StmtBlock() if c.special and c.side != self.side: - writecase.addstmt(_runtimeAbort('wrong side!')) + writecase.addstmt(_fatalError('wrong side!')) else: wexpr = ExprCall(ExprSelect(var, '.', c.getTypeName())) writecase.addstmt(StmtExpr(self.write(ct, wexpr, msgvar))) @@ -4730,7 +4665,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): unknowntype = 'unknown union type' writeswitch.addcase(DefaultLabel(), - StmtBlock([ _runtimeAbort(unknowntype), + StmtBlock([ _fatalError(unknowntype), StmtReturn() ])) readswitch.addcase(DefaultLabel(), StmtBlock(errfnRead(unknowntype))) @@ -4938,7 +4873,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): if self.side=='child': # in the child process this should not fail - failif.addifstmt(_runtimeAbort('constructor for actor failed')) + failif.addifstmt(_fatalError('constructor for actor failed')) else: failif.addifstmts(self.destroyActor(md, actorvar, why=_DestroyReason.FailedConstructor)) @@ -5168,9 +5103,9 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): this = None if md.decl.type.isDtor(): this = md.actorDecl().var() - stmts = ([ StmtDecl(Decl(Type(md.pqMsgClass(), ptr=1), msgvar.name), - init=ExprNew(Type(md.pqMsgClass()), - args=[ routingId ])) ] + stmts = ([ StmtDecl(Decl(Type('IPC::Message', ptr=1), msgvar.name), + init=ExprCall(ExprVar(md.pqMsgCtorFunc()), + args=[ routingId ])) ] + [ Whitespace.NL ] + [ StmtExpr(self.write(p.ipdltype, p.var(), msgvar, this)) for p in md.params ] @@ -5189,12 +5124,12 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): replyvar = self.replyvar return ( [ StmtExpr(ExprAssn( - replyvar, ExprNew(Type(md.pqReplyClass()), args=[ routingId ]))), + replyvar, ExprCall(ExprVar(md.pqReplyCtorFunc()), args=[ routingId ]))), Whitespace.NL ] + [ StmtExpr(self.write(r.ipdltype, r.var(), replyvar)) for r in md.returns ] + self.setMessageFlags(md, replyvar, reply=1) - + [ self.logMessage(md, md.replyCast(replyvar), 'Sending reply ') ]) + + [ self.logMessage(md, replyvar, 'Sending reply ') ]) def setMessageFlags(self, md, var, reply): @@ -5229,7 +5164,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): '.', 'set_name'), args=[ ExprLiteral.String(md.prettyMsgName(self.protocol.name +'::')) ])), - self.logMessage(md, md.msgCast(msgexpr), 'Received ', + self.logMessage(md, msgexpr, 'Received ', receiving=True), self.profilerLabel('Recv', md.decl.progname), Whitespace.NL @@ -5267,7 +5202,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): def deserializeReply(self, md, replyexpr, side, errfn, actor=None): stmts = [ Whitespace.NL, - self.logMessage(md, md.replyCast(replyexpr), + self.logMessage(md, replyexpr, 'Received reply ', actor, receiving=True) ] if 0 == len(md.returns): return stmts @@ -5380,12 +5315,16 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): def logMessage(self, md, msgptr, pfx, actor=None, receiving=False): actorname = _actorName(self.protocol.name, self.side) - topLevel = self.protocol.decl.type.toplevel().name() - return _ifLogging(ExprLiteral.String(topLevel), [ StmtExpr(ExprCall( - ExprSelect(msgptr, '->', 'Log'), - args=[ ExprLiteral.String('['+ actorname +'] '+ pfx), - self.protocol.callOtherPid(actor), - ExprLiteral.TRUE if receiving else ExprLiteral.FALSE ])) ]) + return _ifLogging(ExprLiteral.String(actorname), + [ StmtExpr(ExprCall( + ExprVar('mozilla::ipc::LogMessageForProtocol'), + args=[ ExprLiteral.String(actorname), + self.protocol.callOtherPid(actor), + ExprLiteral.String(pfx), + ExprCall(ExprSelect(msgptr, '->', 'name')), + ExprVar('mozilla::ipc::MessageDirection::eReceiving' + if receiving + else 'mozilla::ipc::MessageDirection::eSending') ])) ]) def profilerLabel(self, tag, msgname): return StmtExpr(ExprCall(ExprVar('PROFILER_LABEL'), diff --git a/js/examples/jorendb.js b/js/examples/jorendb.js index f1e1291f26..7fb9bd3f44 100644 --- a/js/examples/jorendb.js +++ b/js/examples/jorendb.js @@ -25,20 +25,91 @@ var topFrame = null; var debuggeeValues = {}; var nextDebuggeeValueIndex = 1; var lastExc = null; +var todo = []; +var activeTask; +var options = { 'pretty': true, + 'emacs': (os.getenv('EMACS') == 't') }; +var rerun = true; // Cleanup functions to run when we next re-enter the repl. var replCleanups = []; +// Redirect debugger printing functions to go to the original output +// destination, unaffected by any redirects done by the debugged script. +var initialOut = os.file.redirect(); +var initialErr = os.file.redirectErr(); + +function wrap(global, name) { + var orig = global[name]; + global[name] = function(...args) { + + var oldOut = os.file.redirect(initialOut); + var oldErr = os.file.redirectErr(initialErr); + try { + return orig.apply(global, args); + } finally { + os.file.redirect(oldOut); + os.file.redirectErr(oldErr); + } + }; +} +wrap(this, 'print'); +wrap(this, 'printErr'); +wrap(this, 'putstr'); + // Convert a debuggee value v to a string. function dvToString(v) { return (typeof v !== 'object' || v === null) ? uneval(v) : "[object " + v.class + "]"; } -function showDebuggeeValue(dv) { +function summaryObject(dv) { + var obj = {}; + for (var name of dv.getOwnPropertyNames()) { + var v = dv.getOwnPropertyDescriptor(name).value; + if (v instanceof Debugger.Object) { + v = "(...)"; + } + obj[name] = v; + } + return obj; +} + +function debuggeeValueToString(dv, style) { var dvrepr = dvToString(dv); + if (!style.pretty || (typeof dv !== 'object')) + return [dvrepr, undefined]; + + if (dv.class == "Error") { + let errval = debuggeeGlobalWrapper.executeInGlobalWithBindings("$" + i + ".toString()", debuggeeValues); + return [dvrepr, errval.return]; + } + + if (style.brief) + return [dvrepr, JSON.stringify(summaryObject(dv), null, 4)]; + + let str = debuggeeGlobalWrapper.executeInGlobalWithBindings("JSON.stringify(v, null, 4)", {v: dv}); + if ('throw' in str) { + if (style.noerror) + return [dvrepr, undefined]; + + let substyle = {}; + Object.assign(substyle, style); + substyle.noerror = true; + return [dvrepr, debuggeeValueToString(str.throw, substyle)]; + } + + return [dvrepr, str.return]; +} + +// Problem! Used to do [object Object] followed by details. Now just details? + +function showDebuggeeValue(dv, style={pretty: options.pretty}) { var i = nextDebuggeeValueIndex++; debuggeeValues["$" + i] = dv; - print("$" + i + " = " + dvrepr); + let [brief, full] = debuggeeValueToString(dv, style); + print("$" + i + " = " + brief); + if (full !== undefined) + print(full); } Object.defineProperty(Debugger.Frame.prototype, "num", { @@ -70,6 +141,16 @@ Debugger.Frame.prototype.positionDescription = function positionDescription() { return null; } +Debugger.Frame.prototype.location = function () { + if (this.script) { + var { lineNumber, columnNumber, isEntryPoint } = this.script.getOffsetLocation(this.offset); + if (this.script.url) + return this.script.url + ":" + lineNumber; + return null; + } + return null; +} + Debugger.Frame.prototype.fullDescription = function fullDescription() { var fr = this.frameDescription(); var pos = this.positionDescription(); @@ -121,6 +202,50 @@ function saveExcursion(fn) { } } +function parseArgs(str) { + return str.split(" "); +} + +function describedRv(r, desc) { + desc = "[" + desc + "] "; + if (r === undefined) { + print(desc + "Returning undefined"); + } else if (r === null) { + print(desc + "Returning null"); + } else if (r.length === undefined) { + print(desc + "Returning object " + JSON.stringify(r)); + } else { + print(desc + "Returning length-" + r.length + " list"); + if (r.length > 0) { + print(" " + r[0]); + } + } + return r; +} + +// Rerun the program (reloading it from the file) +function runCommand(args) { + print("Restarting program"); + if (args) + activeTask.scriptArgs = parseArgs(args); + rerun = true; + for (var f = topFrame; f; f = f.older) { + print(f.script.url + ":" + f.script.getOffsetLine(f.offset) +" was " + f.onPop); + if (f.older) { + f.onPop = function() { + print("Resumifying " + this.script.url + ":" + this.script.getOffsetLine(this.offset)); + return null; + }; + } else { + f.onPop = function() { + return { 'return': 0 }; + }; + } + } + //return describedRv([{ 'return': 0 }], "runCommand"); + return null; +} + // Evaluate an expression in the Debugger global function evalCommand(expr) { eval(expr); @@ -138,12 +263,47 @@ function backtraceCommand() { showFrame(f, i); } -function printCommand(rest) { +function setCommand(rest) { + var space = rest.indexOf(' '); + if (space == -1) { + print("Invalid set