author | Phil Ringnalda <philringnalda@gmail.com> |
Sat, 26 Oct 2013 18:51:32 -0700 | |
changeset 166264 | d5219c34bfb14328b26ec11cdfd77003835ab35b |
parent 166263 | 2db069bf99081e67d2678001a7486e36fed60b35 (current diff) |
parent 166178 | a80dce1126db109fdbf19a3bde050c2bd580c834 (diff) |
child 166265 | 795b14516359f38351feb052fd475d3f6cec2811 |
push id | 3066 |
push user | akeybl@mozilla.com |
push date | Mon, 09 Dec 2013 19:58:46 +0000 |
treeherder | mozilla-beta@a31a0dce83aa [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 27.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/accessible/src/base/AccEvent.cpp +++ b/accessible/src/base/AccEvent.cpp @@ -11,16 +11,20 @@ #include "xpcAccEvents.h" #include "States.h" #include "nsEventStateManager.h" using namespace mozilla; using namespace mozilla::a11y; +static_assert(static_cast<bool>(eNoUserInput) == false && + static_cast<bool>(eFromUserInput) == true, + "EIsFromUserInput cannot be casted to bool"); + //////////////////////////////////////////////////////////////////////////////// // AccEvent //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // AccEvent constructors AccEvent::AccEvent(uint32_t aEventType, Accessible* aAccessible, @@ -113,16 +117,28 @@ AccHideEvent:: AccShowEvent:: AccShowEvent(Accessible* aTarget, nsINode* aTargetNode) : AccMutationEvent(::nsIAccessibleEvent::EVENT_SHOW, aTarget, aTargetNode) { } //////////////////////////////////////////////////////////////////////////////// +// AccTextSelChangeEvent +//////////////////////////////////////////////////////////////////////////////// + +AccTextSelChangeEvent::AccTextSelChangeEvent(HyperTextAccessible* aTarget, + nsISelection* aSelection) : + AccEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED, aTarget, + eAutoDetect, eCoalesceTextSelChange), + mSel(aSelection) {} + +AccTextSelChangeEvent::~AccTextSelChangeEvent() { } + +//////////////////////////////////////////////////////////////////////////////// // AccSelChangeEvent //////////////////////////////////////////////////////////////////////////////// AccSelChangeEvent:: AccSelChangeEvent(Accessible* aWidget, Accessible* aItem, SelChangeType aSelChangeType) : AccEvent(0, aItem, eAutoDetect, eCoalesceSelectionChange), mWidget(aWidget), mItem(aItem), mSelChangeType(aSelChangeType),
--- a/accessible/src/base/AccEvent.h +++ b/accessible/src/base/AccEvent.h @@ -5,16 +5,18 @@ #ifndef _AccEvent_H_ #define _AccEvent_H_ #include "nsIAccessibleEvent.h" #include "mozilla/a11y/Accessible.h" +class nsISelection; + namespace mozilla { namespace a11y { class DocAccessible; // Constants used to point whether the event is from user input. enum EIsFromUserInput { @@ -54,16 +56,19 @@ public: eCoalesceOfSameType, // eCoalesceSelectionChange: coalescence of selection change events. eCoalesceSelectionChange, // eCoalesceStateChange: coalesce state change events. eCoalesceStateChange, + // eCoalesceTextSelChange: coalescence of text selection change events. + eCoalesceTextSelChange, + // eRemoveDupes : For repeat events, only the newest event in queue // will be emitted. eRemoveDupes, // eDoNotEmit : This event is confirmed as a duplicate, do not emit it. eDoNotEmit }; @@ -72,32 +77,35 @@ public: EIsFromUserInput aIsFromUserInput = eAutoDetect, EEventRule aEventRule = eRemoveDupes); virtual ~AccEvent() {} // AccEvent uint32_t GetEventType() const { return mEventType; } EEventRule GetEventRule() const { return mEventRule; } bool IsFromUserInput() const { return mIsFromUserInput; } + EIsFromUserInput FromUserInput() const + { return static_cast<EIsFromUserInput>(mIsFromUserInput); } Accessible* GetAccessible() const { return mAccessible; } DocAccessible* GetDocAccessible() const { return mAccessible->Document(); } /** * Down casting. */ enum EventGroup { eGenericEvent, eStateChangeEvent, eTextChangeEvent, eMutationEvent, eReorderEvent, eHideEvent, eShowEvent, eCaretMoveEvent, + eTextSelChangeEvent, eSelectionChangeEvent, eTableChangeEvent, eVirtualCursorChangeEvent }; static const EventGroup kEventGroup = eGenericEvent; virtual unsigned int GetEventGroups() const { @@ -325,35 +333,59 @@ protected: /** * Accessible caret move event. */ class AccCaretMoveEvent: public AccEvent { public: - AccCaretMoveEvent(Accessible* aAccessible) : - AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aAccessible), - mCaretOffset(-1) { } + AccCaretMoveEvent(Accessible* aAccessible, int32_t aCaretOffset, + EIsFromUserInput aIsFromUserInput = eAutoDetect) : + AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aAccessible, + aIsFromUserInput), + mCaretOffset(aCaretOffset) { } virtual ~AccCaretMoveEvent() { } // AccEvent static const EventGroup kEventGroup = eCaretMoveEvent; virtual unsigned int GetEventGroups() const { return AccEvent::GetEventGroups() | (1U << eCaretMoveEvent); } // AccCaretMoveEvent int32_t GetCaretOffset() const { return mCaretOffset; } private: int32_t mCaretOffset; +}; + + +/** + * Accessible text selection change event. + */ +class AccTextSelChangeEvent : public AccEvent +{ +public: + AccTextSelChangeEvent(HyperTextAccessible* aTarget, nsISelection* aSelection); + virtual ~AccTextSelChangeEvent(); + + // AccEvent + static const EventGroup kEventGroup = eTextSelChangeEvent; + virtual unsigned int GetEventGroups() const + { + return AccEvent::GetEventGroups() | (1U << eTextSelChangeEvent); + } + +private: + nsCOMPtr<nsISelection> mSel; friend class EventQueue; + friend class SelectionManager; }; /** * Accessible widget selection change event. */ class AccSelChangeEvent : public AccEvent {
--- a/accessible/src/base/EventQueue.cpp +++ b/accessible/src/base/EventQueue.cpp @@ -147,16 +147,36 @@ EventQueue::CoalesceEvents() if (thisSCEvent->mIsEnabled != tailSCEvent->mIsEnabled) tailEvent->mEventRule = AccEvent::eDoNotEmit; } } } break; // eCoalesceStateChange } + case AccEvent::eCoalesceTextSelChange: + { + // Coalesce older event by newer event for the same selection or target. + // Events for same selection may have different targets and vice versa one + // target may be pointed by different selections (for latter see + // bug 927159). + for (uint32_t index = tail - 1; index < tail; index--) { + AccEvent* thisEvent = mEvents[index]; + if (thisEvent->mEventRule != AccEvent::eDoNotEmit && + thisEvent->mEventType == tailEvent->mEventType) { + AccTextSelChangeEvent* thisTSCEvent = downcast_accEvent(thisEvent); + AccTextSelChangeEvent* tailTSCEvent = downcast_accEvent(tailEvent); + if (thisTSCEvent->mSel == tailTSCEvent->mSel || + thisEvent->mAccessible == tailEvent->mAccessible) + thisEvent->mEventRule = AccEvent::eDoNotEmit; + } + + } + } break; // eCoalesceTextSelChange + case AccEvent::eRemoveDupes: { // Check for repeat events, coalesce newly appended event by more older // event. for (uint32_t index = tail - 1; index < tail; index--) { AccEvent* accEvent = mEvents[index]; if (accEvent->mEventType == tailEvent->mEventType && accEvent->mEventRule == tailEvent->mEventRule && @@ -453,31 +473,18 @@ EventQueue::ProcessEventQueue() // Dispatch the focus event if target is still focused. if (event->mEventType == nsIAccessibleEvent::EVENT_FOCUS) { FocusMgr()->ProcessFocusEvent(event); continue; } // Dispatch caret moved and text selection change events. - if (event->mEventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED) { - AccCaretMoveEvent* caretMoveEvent = downcast_accEvent(event); - HyperTextAccessible* hyperText = target->AsHyperText(); - if (hyperText && - NS_SUCCEEDED(hyperText->GetCaretOffset(&caretMoveEvent->mCaretOffset))) { - - nsEventShell::FireEvent(caretMoveEvent); - - // There's a selection so fire selection change as well. - int32_t selectionCount; - hyperText->GetSelectionCount(&selectionCount); - if (selectionCount) - nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED, - hyperText); - } + if (event->mEventType == nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED) { + SelectionMgr()->ProcessTextSelChangeEvent(event); continue; } // Fire selected state change events in support to selection events. if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_ADD) { nsEventShell::FireEvent(event->mAccessible, states::SELECTED, true, event->mIsFromUserInput);
--- a/accessible/src/base/FocusManager.cpp +++ b/accessible/src/base/FocusManager.cpp @@ -260,19 +260,16 @@ FocusManager::ProcessDOMFocus(nsINode* a } void FocusManager::ProcessFocusEvent(AccEvent* aEvent) { NS_PRECONDITION(aEvent->GetEventType() == nsIAccessibleEvent::EVENT_FOCUS, "Focus event is expected!"); - EIsFromUserInput fromUserInputFlag = aEvent->IsFromUserInput() ? - eFromUserInput : eNoUserInput; - // Emit focus event if event target is the active item. Otherwise then check // if it's still focused and then update active item and emit focus event. Accessible* target = aEvent->GetAccessible(); if (target != mActiveItem) { // Check if still focused. Otherwise we can end up with storing the active // item for control that isn't focused anymore. DocAccessible* document = aEvent->GetDocAccessible(); @@ -294,60 +291,60 @@ FocusManager::ProcessFocusEvent(AccEvent Accessible* ARIAMenubar = nsAccUtils::GetAncestorWithRole(target, roles::MENUBAR); if (ARIAMenubar != mActiveARIAMenubar) { // Leaving ARIA menu. Fire menu_end event on current menubar. if (mActiveARIAMenubar) { nsRefPtr<AccEvent> menuEndEvent = new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar, - fromUserInputFlag); + aEvent->FromUserInput()); nsEventShell::FireEvent(menuEndEvent); } mActiveARIAMenubar = ARIAMenubar; // Entering ARIA menu. Fire menu_start event. if (mActiveARIAMenubar) { nsRefPtr<AccEvent> menuStartEvent = new AccEvent(nsIAccessibleEvent::EVENT_MENU_START, - mActiveARIAMenubar, fromUserInputFlag); + mActiveARIAMenubar, aEvent->FromUserInput()); nsEventShell::FireEvent(menuStartEvent); } } } else if (mActiveARIAMenubar) { // Focus left a menu. Fire menu_end event. nsRefPtr<AccEvent> menuEndEvent = new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar, - fromUserInputFlag); + aEvent->FromUserInput()); nsEventShell::FireEvent(menuEndEvent); mActiveARIAMenubar = nullptr; } #ifdef A11Y_LOG if (logging::IsEnabled(logging::eFocus)) logging::FocusNotificationTarget("fire focus event", "Target", target); #endif nsRefPtr<AccEvent> focusEvent = - new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, target, fromUserInputFlag); + new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, target, aEvent->FromUserInput()); nsEventShell::FireEvent(focusEvent); // Fire scrolling_start event when the document receives the focus if it has // an anchor jump. If an accessible within the document receive the focus // then null out the anchor jump because it no longer applies. DocAccessible* targetDocument = target->Document(); Accessible* anchorJump = targetDocument->AnchorJump(); if (anchorJump) { if (target == targetDocument) { // XXX: bug 625699, note in some cases the node could go away before we // we receive focus event, for example if the node is removed from DOM. nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SCROLLING_START, - anchorJump, fromUserInputFlag); + anchorJump, aEvent->FromUserInput()); } targetDocument->SetAnchorJump(nullptr); } } nsINode* FocusManager::FocusedDOMNode() const {
--- a/accessible/src/base/SelectionManager.cpp +++ b/accessible/src/base/SelectionManager.cpp @@ -4,39 +4,29 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/a11y/SelectionManager.h" #include "DocAccessible-inl.h" #include "nsAccessibilityService.h" #include "nsAccUtils.h" #include "nsCoreUtils.h" -#include "nsIAccessibleEvent.h" +#include "nsEventShell.h" -#include "nsCaret.h" #include "nsIAccessibleTypes.h" #include "nsIDOMDocument.h" -#include "nsIFrame.h" #include "nsIPresShell.h" #include "nsISelectionPrivate.h" #include "mozilla/Selection.h" #include "mozilla/dom/Element.h" using namespace mozilla; using namespace mozilla::a11y; void -SelectionManager::Shutdown() -{ - ClearControlSelectionListener(); - mLastTextAccessible = nullptr; - mLastUsedSelection = nullptr; -} - -void SelectionManager::ClearControlSelectionListener() { if (!mCurrCtrlFrame) return; const nsFrameSelection* frameSel = mCurrCtrlFrame->GetConstFrameSelection(); NS_ASSERTION(frameSel, "No frame selection for the element!"); @@ -59,18 +49,16 @@ SelectionManager::ClearControlSelectionL void SelectionManager::SetControlSelectionListener(dom::Element* aFocusedElm) { // When focus moves such that the caret is part of a new frame selection // this removes the old selection listener and attaches a new one for // the current focus. ClearControlSelectionListener(); - mLastTextAccessible = nullptr; - mCurrCtrlFrame = aFocusedElm->GetPrimaryFrame(); if (!mCurrCtrlFrame) return; const nsFrameSelection* frameSel = mCurrCtrlFrame->GetConstFrameSelection(); NS_ASSERTION(frameSel, "No frame selection for focused element!"); if (!frameSel) return; @@ -114,16 +102,47 @@ SelectionManager::RemoveDocSelectionList // Remove 'this' registered as selection listener for the spellcheck // selection. Selection* spellSel = frameSel->GetSelection(nsISelectionController::SELECTION_SPELLCHECK); spellSel->RemoveSelectionListener(this); } +void +SelectionManager::ProcessTextSelChangeEvent(AccEvent* aEvent) +{ + AccTextSelChangeEvent* event = downcast_accEvent(aEvent); + Selection* sel = static_cast<Selection*>(event->mSel.get()); + + // Fire selection change event if it's not pure caret-move selection change. + if (sel->GetRangeCount() != 1 || !sel->IsCollapsed()) + nsEventShell::FireEvent(aEvent); + + // Fire caret move event if there's a caret in the selection. + nsINode* caretCntrNode = + nsCoreUtils::GetDOMNodeFromDOMPoint(sel->GetFocusNode(), + sel->GetFocusOffset()); + if (!caretCntrNode) + return; + + HyperTextAccessible* caretCntr = nsAccUtils::GetTextContainer(caretCntrNode); + NS_ASSERTION(caretCntr, + "No text container for focus while there's one for common ancestor?!"); + if (!caretCntr) + return; + + int32_t caretOffset = -1; + if (NS_SUCCEEDED(caretCntr->GetCaretOffset(&caretOffset)) && caretOffset != -1) { + nsRefPtr<AccCaretMoveEvent> caretMoveEvent = + new AccCaretMoveEvent(caretCntr, caretOffset, aEvent->FromUserInput()); + nsEventShell::FireEvent(caretMoveEvent); + } +} + NS_IMETHODIMP SelectionManager::NotifySelectionChanged(nsIDOMDocument* aDOMDocument, nsISelection* aSelection, int16_t aReason) { NS_ENSURE_ARG(aDOMDocument); nsCOMPtr<nsIDocument> documentNode(do_QueryInterface(aDOMDocument)); @@ -144,133 +163,39 @@ SelectionManager::NotifySelectionChanged } return NS_OK; } void SelectionManager::ProcessSelectionChanged(nsISelection* aSelection) { - nsCOMPtr<nsISelectionPrivate> privSel(do_QueryInterface(aSelection)); - - int16_t type = 0; - privSel->GetType(&type); - - if (type == nsISelectionController::SELECTION_NORMAL) - NormalSelectionChanged(aSelection); - - else if (type == nsISelectionController::SELECTION_SPELLCHECK) - SpellcheckSelectionChanged(aSelection); -} - -void -SelectionManager::NormalSelectionChanged(nsISelection* aSelection) -{ - mLastUsedSelection = do_GetWeakReference(aSelection); - - int32_t rangeCount = 0; - aSelection->GetRangeCount(&rangeCount); - if (rangeCount == 0) { - mLastTextAccessible = nullptr; - return; // No selection - } - - HyperTextAccessible* textAcc = - nsAccUtils::GetTextAccessibleFromSelection(aSelection); - if (!textAcc) - return; - - int32_t caretOffset = -1; - nsresult rv = textAcc->GetCaretOffset(&caretOffset); - if (NS_FAILED(rv)) - return; - - if (textAcc == mLastTextAccessible && caretOffset == mLastCaretOffset) { - int32_t selectionCount = 0; - textAcc->GetSelectionCount(&selectionCount); // Don't swallow similar events when selecting text - if (!selectionCount) - return; // Swallow duplicate caret event + Selection* selection = static_cast<Selection*>(aSelection); + const nsRange* range = selection->GetAnchorFocusRange(); + nsINode* cntrNode = nullptr; + if (range) + cntrNode = range->GetCommonAncestor(); + if (!cntrNode) { + cntrNode = selection->GetFrameSelection()->GetAncestorLimiter(); + if (!cntrNode) { + cntrNode = selection->GetPresShell()->GetDocument(); + NS_ASSERTION(selection->GetPresShell()->ConstFrameSelection() == selection->GetFrameSelection(), + "Wrong selection container was used!"); + } } - mLastCaretOffset = caretOffset; - mLastTextAccessible = textAcc; - - nsRefPtr<AccEvent> event = new AccCaretMoveEvent(mLastTextAccessible); - mLastTextAccessible->Document()->FireDelayedEvent(event); -} - -void -SelectionManager::SpellcheckSelectionChanged(nsISelection* aSelection) -{ - // XXX: fire an event for accessible of focus node of the selection. If - // spellchecking is enabled then we will fire the number of events for - // the same accessible for newly appended range of the selection (for every - // misspelled word). If spellchecking is disabled (for example, - // @spellcheck="false" on html:body) then we won't fire any event. - - HyperTextAccessible* hyperText = - nsAccUtils::GetTextAccessibleFromSelection(aSelection); - if (hyperText) { - hyperText->Document()-> - FireDelayedEvent(nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED, - hyperText); - } -} - -nsIntRect -SelectionManager::GetCaretRect(nsIWidget** aWidget) -{ - nsIntRect caretRect; - NS_ENSURE_TRUE(aWidget, caretRect); - *aWidget = nullptr; - - if (!mLastTextAccessible) { - return caretRect; // Return empty rect + HyperTextAccessible* text = nsAccUtils::GetTextContainer(cntrNode); + if (!text) { + NS_NOTREACHED("We must reach document accessible implementing text interface!"); + return; } - nsINode *lastNodeWithCaret = mLastTextAccessible->GetNode(); - NS_ENSURE_TRUE(lastNodeWithCaret, caretRect); - - nsIPresShell *presShell = nsCoreUtils::GetPresShellFor(lastNodeWithCaret); - NS_ENSURE_TRUE(presShell, caretRect); - - nsRefPtr<nsCaret> caret = presShell->GetCaret(); - NS_ENSURE_TRUE(caret, caretRect); - - nsCOMPtr<nsISelection> caretSelection(do_QueryReferent(mLastUsedSelection)); - NS_ENSURE_TRUE(caretSelection, caretRect); - - bool isVisible; - caret->GetCaretVisible(&isVisible); - if (!isVisible) { - return nsIntRect(); // Return empty rect - } - - nsRect rect; - nsIFrame* frame = caret->GetGeometry(caretSelection, &rect); - if (!frame || rect.IsEmpty()) { - return nsIntRect(); // Return empty rect - } + if (selection->GetType() == nsISelectionController::SELECTION_NORMAL) { + nsRefPtr<AccEvent> event = new AccTextSelChangeEvent(text, aSelection); + text->Document()->FireDelayedEvent(event); - nsPoint offset; - // Offset from widget origin to the frame origin, which includes chrome - // on the widget. - *aWidget = frame->GetNearestWidget(offset); - NS_ENSURE_TRUE(*aWidget, nsIntRect()); - rect.MoveBy(offset); - - caretRect = rect.ToOutsidePixels(frame->PresContext()->AppUnitsPerDevPixel()); - // ((content screen origin) - (content offset in the widget)) = widget origin on the screen - caretRect.MoveBy((*aWidget)->WidgetToScreenOffset() - (*aWidget)->GetClientOffset()); - - // Correct for character size, so that caret always matches the size of the character - // This is important for font size transitions, and is necessary because the Gecko caret uses the - // previous character's size as the user moves forward in the text by character. - int32_t charX, charY, charWidth, charHeight; - if (NS_SUCCEEDED(mLastTextAccessible->GetCharacterExtents(mLastCaretOffset, &charX, &charY, - &charWidth, &charHeight, - nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE))) { - caretRect.height -= charY - caretRect.y; - caretRect.y = charY; + } else if (selection->GetType() == nsISelectionController::SELECTION_SPELLCHECK) { + // XXX: fire an event for container accessible of the focus/anchor range + // of the spelcheck selection. + text->Document()->FireDelayedEvent(nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED, + text); } - - return caretRect; }
--- a/accessible/src/base/SelectionManager.h +++ b/accessible/src/base/SelectionManager.h @@ -1,35 +1,30 @@ /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_a11y_SelectionManager_h__ #define mozilla_a11y_SelectionManager_h__ -#include "nsAutoPtr.h" #include "nsIFrame.h" #include "nsISelectionListener.h" -class nsIContent; -class nsIntRect; class nsIPresShell; -class nsIWeakReference; -class nsIWidget; namespace mozilla { namespace dom { class Element; } namespace a11y { -class HyperTextAccessible; +class AccEvent; /** * This special accessibility class is for the caret and selection management. * There is only 1 visible caret per top level window. However, there may be * several visible selections. * * The important selections are the one owned by each document, and the one in * the currently focused control. @@ -48,17 +43,17 @@ class SelectionManager : public nsISelec public: // nsISupports // implemented by derived nsAccessibilityService // nsISelectionListener NS_DECL_NSISELECTIONLISTENER // SelectionManager - void Shutdown(); + void Shutdown() { ClearControlSelectionListener(); } /** * Listen to selection events on the focused control. * * Note: only one control's selection events are listened to at a time. This * will remove the previous control's selection listener. */ void SetControlSelectionListener(dom::Element* aFocusedElm); @@ -74,45 +69,28 @@ public: void AddDocSelectionListener(nsIPresShell* aPresShell); /** * Stop listening to selection events for a given document */ void RemoveDocSelectionListener(nsIPresShell* aShell); /** - * Return the caret rect and the widget containing the caret. + * Process delayed event, results in caret move and text selection change + * events. */ - nsIntRect GetCaretRect(nsIWidget** aWidget); + void ProcessTextSelChangeEvent(AccEvent* aEvent); protected: /** * Process DOM selection change. Fire selection and caret move events. */ void ProcessSelectionChanged(nsISelection* aSelection); - /** - * Process normal selection change and fire caret move event. - */ - void NormalSelectionChanged(nsISelection* aSelection); - - /** - * Process spellcheck selection change and fire text attribute changed event - * for invalid text attribute. - */ - void SpellcheckSelectionChanged(nsISelection* aSelection); - private: // Currently focused control. nsWeakFrame mCurrCtrlFrame; - - // Info for the the last selection event. - // If it was on a control, then its control's selection. Otherwise, it's for - // a document where the selection changed. - nsCOMPtr<nsIWeakReference> mLastUsedSelection; // Weak ref to nsISelection - nsRefPtr<HyperTextAccessible> mLastTextAccessible; - int32_t mLastCaretOffset; }; } // namespace a11y } // namespace mozilla #endif
--- a/accessible/src/base/nsAccUtils.cpp +++ b/accessible/src/base/nsAccUtils.cpp @@ -247,52 +247,34 @@ bool nsAccUtils::IsARIASelected(Accessible* aAccessible) { return aAccessible->GetContent()-> AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_selected, nsGkAtoms::_true, eCaseMatters); } HyperTextAccessible* -nsAccUtils::GetTextAccessibleFromSelection(nsISelection* aSelection) +nsAccUtils::GetTextContainer(nsINode* aNode) { - // Get accessible from selection's focus DOM point (the DOM point where - // selection is ended). - - nsCOMPtr<nsIDOMNode> focusDOMNode; - aSelection->GetFocusNode(getter_AddRefs(focusDOMNode)); - if (!focusDOMNode) + // Get text accessible containing the result node. + DocAccessible* doc = + GetAccService()->GetDocAccessible(aNode->OwnerDoc()); + Accessible* accessible = + doc ? doc->GetAccessibleOrContainer(aNode) : nullptr; + if (!accessible) return nullptr; - int32_t focusOffset = 0; - aSelection->GetFocusOffset(&focusOffset); - - nsCOMPtr<nsINode> focusNode(do_QueryInterface(focusDOMNode)); - nsCOMPtr<nsINode> resultNode = - nsCoreUtils::GetDOMNodeFromDOMPoint(focusNode, focusOffset); - - // Get text accessible containing the result node. - DocAccessible* doc = - GetAccService()->GetDocAccessible(resultNode->OwnerDoc()); - Accessible* accessible = doc ? - doc->GetAccessibleOrContainer(resultNode) : nullptr; - if (!accessible) { - NS_NOTREACHED("No nsIAccessibleText for selection change event!"); - return nullptr; - } - do { HyperTextAccessible* textAcc = accessible->AsHyperText(); if (textAcc) return textAcc; accessible = accessible->Parent(); } while (accessible); - NS_NOTREACHED("We must reach document accessible implementing nsIAccessibleText!"); return nullptr; } nsIntPoint nsAccUtils::ConvertToScreenCoords(int32_t aX, int32_t aY, uint32_t aCoordinateType, Accessible* aAccessible) {
--- a/accessible/src/base/nsAccUtils.h +++ b/accessible/src/base/nsAccUtils.h @@ -132,32 +132,27 @@ public: * * @param aAccessible [in] the item accessible * @param aState [in] the state of the item accessible */ static Accessible* GetSelectableContainer(Accessible* aAccessible, uint64_t aState); /** + * Return a text container accessible for the given node. + */ + static HyperTextAccessible* GetTextContainer(nsINode* aNode); + + /** * Return true if the DOM node of given accessible has aria-selected="true" * attribute. */ static bool IsARIASelected(Accessible* aAccessible); /** - * Return text accessible containing focus point of the given selection. - * Used for normal and misspelling selection changes processing. - * - * @param aSelection [in] the given selection - * @return text accessible - */ - static HyperTextAccessible* - GetTextAccessibleFromSelection(nsISelection* aSelection); - - /** * Converts the given coordinates to coordinates relative screen. * * @param aX [in] the given x coord * @param aY [in] the given y coord * @param aCoordinateType [in] specifies coordinates origin (refer to * nsIAccessibleCoordinateType) * @param aAccessible [in] the accessible if coordinates are given * relative it.
--- a/accessible/src/generic/HyperTextAccessible.cpp +++ b/accessible/src/generic/HyperTextAccessible.cpp @@ -4,22 +4,24 @@ * 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 "HyperTextAccessible.h" #include "Accessible-inl.h" #include "nsAccessibilityService.h" #include "nsAccUtils.h" +#include "nsIAccessibleTypes.h" #include "DocAccessible.h" #include "Role.h" #include "States.h" #include "TextAttrs.h" #include "TreeWalker.h" +#include "nsCaret.h" #include "nsIClipboard.h" #include "nsContentUtils.h" #include "nsFocusManager.h" #include "nsIDOMRange.h" #include "nsIEditingSession.h" #include "nsIEditor.h" #include "nsIFrame.h" #include "nsFrameSelection.h" @@ -1737,16 +1739,67 @@ HyperTextAccessible::CaretLineNumber() caretFrame = parentFrame; } NS_NOTREACHED("DOM ancestry had this hypertext but frame ancestry didn't"); return lineNumber; } +nsIntRect +HyperTextAccessible::GetCaretRect(nsIWidget** aWidget) +{ + *aWidget = nullptr; + + nsRefPtr<nsCaret> caret = mDoc->PresShell()->GetCaret(); + NS_ENSURE_TRUE(caret, nsIntRect()); + + nsISelection* caretSelection = caret->GetCaretDOMSelection(); + NS_ENSURE_TRUE(caretSelection, nsIntRect()); + + bool isVisible = false; + caret->GetCaretVisible(&isVisible); + if (!isVisible) + return nsIntRect(); + + nsRect rect; + nsIFrame* frame = caret->GetGeometry(caretSelection, &rect); + if (!frame || rect.IsEmpty()) + return nsIntRect(); + + nsPoint offset; + // Offset from widget origin to the frame origin, which includes chrome + // on the widget. + *aWidget = frame->GetNearestWidget(offset); + NS_ENSURE_TRUE(*aWidget, nsIntRect()); + rect.MoveBy(offset); + + nsIntRect caretRect; + caretRect = rect.ToOutsidePixels(frame->PresContext()->AppUnitsPerDevPixel()); + // ((content screen origin) - (content offset in the widget)) = widget origin on the screen + caretRect.MoveBy((*aWidget)->WidgetToScreenOffset() - (*aWidget)->GetClientOffset()); + + int32_t caretOffset = -1; + GetCaretOffset(&caretOffset); + + // Correct for character size, so that caret always matches the size of + // the character. This is important for font size transitions, and is + // necessary because the Gecko caret uses the previous character's size as + // the user moves forward in the text by character. + int32_t charX = 0, charY = 0, charWidth = 0, charHeight = 0; + if (NS_SUCCEEDED(GetCharacterExtents(caretOffset, &charX, &charY, + &charWidth, &charHeight, + nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE))) { + caretRect.height -= charY - caretRect.y; + caretRect.y = charY; + } + + return caretRect; +} + already_AddRefed<nsFrameSelection> HyperTextAccessible::FrameSelection() { nsIFrame* frame = GetFrame(); return frame ? frame->GetFrameSelection() : nullptr; } void
--- a/accessible/src/generic/HyperTextAccessible.h +++ b/accessible/src/generic/HyperTextAccessible.h @@ -257,16 +257,25 @@ public: } /** * Provide the line number for the caret. * @return 1-based index for the line number with the caret */ int32_t CaretLineNumber(); + /** + * Return the caret rect and the widget containing the caret within this + * text accessible. + * + * @param [out] the widget containing the caret + * @return the caret rect + */ + nsIntRect GetCaretRect(nsIWidget** aWidget); + ////////////////////////////////////////////////////////////////////////////// // EditableTextAccessible /** * Return the editor associated with the accessible. */ virtual already_AddRefed<nsIEditor> GetEditor() const;
--- a/accessible/src/html/HTMLFormControlAccessible.cpp +++ b/accessible/src/html/HTMLFormControlAccessible.cpp @@ -528,19 +528,17 @@ HTMLFileInputAccessible::HandleAccEvent( (event->GetState() == states::BUSY || event->GetState() == states::REQUIRED || event->GetState() == states::HASPOPUP || event->GetState() == states::INVALID)) { Accessible* button = GetChildAt(0); if (button && button->Role() == roles::PUSHBUTTON) { nsRefPtr<AccStateChangeEvent> childEvent = new AccStateChangeEvent(button, event->GetState(), - event->IsStateEnabled(), - (event->IsFromUserInput() ? eFromUserInput - : eNoUserInput)); + event->IsStateEnabled(), event->FromUserInput()); nsEventShell::FireEvent(childEvent); } } return NS_OK; }
--- a/accessible/src/windows/msaa/AccessibleWrap.cpp +++ b/accessible/src/windows/msaa/AccessibleWrap.cpp @@ -1127,17 +1127,17 @@ AccessibleWrap::HandleAccEvent(AccEvent* NS_ENSURE_TRUE(!IsDefunct(), NS_ERROR_FAILURE); Accessible* accessible = aEvent->GetAccessible(); if (!accessible) return NS_OK; if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED || eventType == nsIAccessibleEvent::EVENT_FOCUS) { - UpdateSystemCaret(); + UpdateSystemCaretFor(accessible); } int32_t childID = GetChildIDFor(accessible); // get the id for the accessible if (!childID) return NS_OK; // Can't fire an event without a child ID HWND hWnd = GetHWNDFor(accessible); NS_ENSURE_TRUE(hWnd, NS_ERROR_FAILURE); @@ -1281,30 +1281,29 @@ AccessibleWrap::GetXPAccessibleFor(const return nullptr; } // Gecko child indices are 0-based in contrast to indices used in MSAA. return GetChildAt(aVarChild.lVal - 1); } void -AccessibleWrap::UpdateSystemCaret() +AccessibleWrap::UpdateSystemCaretFor(Accessible* aAccessible) { // Move the system caret so that Windows Tablet Edition and tradional ATs with // off-screen model can follow the caret ::DestroyCaret(); - a11y::RootAccessible* rootAccessible = RootAccessible(); - if (!rootAccessible) { + HyperTextAccessible* text = aAccessible->AsHyperText(); + if (!text) return; - } nsIWidget* widget = nullptr; - nsIntRect caretRect = SelectionMgr()->GetCaretRect(&widget); - HWND caretWnd; + nsIntRect caretRect = text->GetCaretRect(&widget); + HWND caretWnd; if (caretRect.IsEmpty() || !(caretWnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW))) { return; } // Create invisible bitmap for caret, otherwise its appearance interferes // with Gecko caret HBITMAP caretBitMap = CreateBitmap(1, caretRect.height, 1, 1, nullptr); if (::CreateCaret(caretWnd, caretBitMap, 1, caretRect.height)) { // Also destroys the last caret
--- a/accessible/src/windows/msaa/AccessibleWrap.h +++ b/accessible/src/windows/msaa/AccessibleWrap.h @@ -159,17 +159,17 @@ public: // construction, destruction /** * System caret support: update the Windows caret position. * The system caret works more universally than the MSAA caret * For example, Window-Eyes, JAWS, ZoomText and Windows Tablet Edition use it * We will use an invisible system caret. * Gecko is still responsible for drawing its own caret */ - void UpdateSystemCaret(); + void UpdateSystemCaretFor(Accessible* aAccessible); /** * Find an accessible by the given child ID in cached documents. */ Accessible* GetXPAccessibleFor(const VARIANT& aVarChild); NS_IMETHOD GetNativeInterface(void **aOutAccessible);
--- a/accessible/tests/mochitest/events.js +++ b/accessible/tests/mochitest/events.js @@ -1205,16 +1205,24 @@ function synthDownKey(aNodeOrID, aChecke */ function synthUpKey(aNodeOrID, aCheckerOrEventSeq, aArgs) { this.__proto__ = new synthKey(aNodeOrID, "VK_UP", aArgs, aCheckerOrEventSeq); } /** + * Left arrow key invoker. + */ +function synthLeftKey(aNodeOrID, aCheckerOrEventSeq) +{ + this.__proto__ = new synthKey(aNodeOrID, "VK_LEFT", null, aCheckerOrEventSeq); +} + +/** * Right arrow key invoker. */ function synthRightKey(aNodeOrID, aCheckerOrEventSeq) { this.__proto__ = new synthKey(aNodeOrID, "VK_RIGHT", null, aCheckerOrEventSeq); } /** @@ -1653,16 +1661,27 @@ function invokerChecker(aEventType, aTar return prettyName(this.mTarget); } this.mTarget = aTargetOrFunc; this.mTargetFuncArg = aTargetFuncArg; } /** + * Generic invoker checker for unexpected events. + */ +function unexpectedInvokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg) +{ + this.__proto__ = new invokerChecker(aEventType, aTargetOrFunc, + aTargetFuncArg, true); + + this.unexpected = true; +} + +/** * Common invoker checker for async events. */ function asyncInvokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg) { this.__proto__ = new invokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg, true); } @@ -1732,16 +1751,29 @@ function caretMoveChecker(aCaretOffset, { is(aEvent.QueryInterface(nsIAccessibleCaretMoveEvent).caretOffset, aCaretOffset, "Wrong caret offset for " + prettyName(aEvent.accessible)); } } /** + * Text selection change checker. + */ +function textSelectionChecker(aID, aStartOffset, aEndOffset) +{ + this.__proto__ = new invokerChecker(EVENT_TEXT_SELECTION_CHANGED, aID); + + this.check = function textSelectionChecker_check(aEvent) + { + testTextGetSelection(aID, aStartOffset, aEndOffset, 0); + } +} + +/** * State change checker. */ function stateChangeChecker(aState, aIsExtraState, aIsEnabled, aTargetOrFunc, aTargetFuncArg, aIsAsync, aSkipCurrentStateCheck) { this.__proto__ = new invokerChecker(EVENT_STATE_CHANGE, aTargetOrFunc, aTargetFuncArg, aIsAsync);
--- a/accessible/tests/mochitest/events/a11y.ini +++ b/accessible/tests/mochitest/events/a11y.ini @@ -41,10 +41,11 @@ support-files = [test_scroll.xul] [test_selection.html] [test_selection.xul] [test_selection_aria.html] [test_statechange.html] [test_text.html] [test_text_alg.html] [test_textattrchange.html] +[test_textselchange.html] [test_tree.xul] [test_valuechange.html]
--- a/accessible/tests/mochitest/events/test_caretmove.html +++ b/accessible/tests/mochitest/events/test_caretmove.html @@ -57,16 +57,20 @@ gQueue.push(new synthClick(id, new caretMoveChecker(0, id))); gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id))); id = "textarea"; gQueue.push(new synthClick(id, new caretMoveChecker(0, id))); gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id))); gQueue.push(new synthDownKey(id, new caretMoveChecker(12, id))); + id = "textarea_wrapped"; + gQueue.push(new setCaretOffset(id, 4, id)); + gQueue.push(new synthLeftKey(id, new caretMoveChecker(4, id))); + id = "p"; gQueue.push(new synthClick(id, new caretMoveChecker(0, id))); gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id))); gQueue.push(new synthDownKey(id, new caretMoveChecker(6, id))); id = "p1_in_div"; gQueue.push(new synthClick(id, new clickChecker(0, id, "p2_in_div", -1))); @@ -100,16 +104,21 @@ <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=454377" title="Accessible caret move events testing"> Bug 454377 </a> <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=567571" + title="caret-moved events missing at the end of a wrapped line of text"> + Bug 567571 + </a> + <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=824901" title="HyperTextAccessible::DOMPointToHypertextOffset fails for node and offset equal to node child count"> Bug 824901 </a> <p id="display"></p> <div id="content" style="display: none"></div> <pre id="test"> </pre> @@ -119,11 +128,13 @@ <p id="p" contentEditable="true"><span>text</span><br/>text</p> <div id="div" contentEditable="true"><p id="p1_in_div">text</p><p id="p2_in_div">text</p></div> <p contentEditable="true" id="test1"><span id="test1_span">text</span>ohoho</p> <p contentEditable="true" id="test2"><span><span id="test2_span">text</span></span>ohoho</p> <p contentEditable="true" id="test3"></p> <p contentEditable="true" id="test4"><span id="test4_span"></span></p> + <textarea id="textarea_wrapped" cols="5">hey friend</textarea> + <div id="eventdump"></div> </body> </html>
new file mode 100644 --- /dev/null +++ b/accessible/tests/mochitest/events/test_textselchange.html @@ -0,0 +1,67 @@ +<html> + +<head> + <title>Accessible text selection change events testing</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../text.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + var gQueue = null; + + // gA11yEventDumpID = "eventdump"; // debug stuff + //gA11yEventDumpToConsole = true; + + function doTests() + { + // test caret move events and caret offsets + gQueue = new eventQueue(); + + var onclickSeq = [ + new caretMoveChecker(0, "c1_p1"), + new unexpectedInvokerChecker(EVENT_TEXT_SELECTION_CHANGED, "c1_p1") + ]; + gQueue.push(new synthClick("c1_p1", onclickSeq)); + gQueue.push(new synthDownKey("c1", new textSelectionChecker("c1", 0, 1), { shiftKey: true })); + gQueue.push(new synthDownKey("c1", new textSelectionChecker("c1", 0, 2), { shiftKey: true })); + + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTests); + </script> +</head> + +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=762934" + title="Text selection change event has a wrong target when selection is spanned through several objects"> + Bug 762934 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="c1" contentEditable="true"> + <p id="c1_p1">paragraph</p> + <p id="c1_p2">paragraph</p> + </div> + + <div id="eventdump"></div> +</body> +</html>
--- a/accessible/tests/mochitest/hittest/test_browser.html +++ b/accessible/tests/mochitest/hittest/test_browser.html @@ -11,16 +11,18 @@ src="../common.js"></script> <script type="application/javascript" src="../layout.js"></script> <script type="application/javascript"> function doTest() { // Hit testing. See bug #726097 + getNode("hittest").scrollIntoView(true); + var hititem = getAccessible("hititem"); var hittest = getAccessible("hittest"); var [hitX, hitY, hitWidth, hitHeight] = getBounds(hititem); var tgtX = hitX + hitWidth / 2; var tgtY = hitY + hitHeight / 2; var rootAcc = getRootAccessible();
--- a/accessible/tests/mochitest/hittest/test_menu.xul +++ b/accessible/tests/mochitest/hittest/test_menu.xul @@ -76,16 +76,18 @@ function doTest() { if (LINUX) { ok(true, "No tests is running on Linux"); SimpleTest.finish(); return; } + getNode("mi_file1").scrollIntoView(true); + gQueue = new eventQueue(); gQueue.push(new openMenu("mi_file1", "mp_file1", "mi_file1.1")); gQueue.push(new openMenu("mi_file1.2", "mp_file1.2", "mi_file1.2.1")); gQueue.push(new closeMenu("mi_file1", "mi_file1.1", "mi_file1.2.1")); gQueue.invoke(); // Will call SimpleTest.finish(); } SimpleTest.waitForExplicitFinish();
--- a/accessible/tests/mochitest/textselection/test_general.html +++ b/accessible/tests/mochitest/textselection/test_general.html @@ -84,17 +84,17 @@ } } function removeSelection(aID) { this.hyperText = getAccessible(aID, [ nsIAccessibleText ]); this.eventSeq = [ - new invokerChecker(EVENT_TEXT_SELECTION_CHANGED, aID) + new invokerChecker(EVENT_TEXT_SELECTION_CHANGED, document) ]; this.invoke = function removeSelection_invoke() { this.hyperText.removeSelection(0); } this.finalCheck = function removeSelection_finalCheck() @@ -128,18 +128,17 @@ var gQueue = null; function doTests() { gQueue = new eventQueue(); gQueue.push(new addSelection("paragraph", 1, 3)); gQueue.push(new changeSelection("paragraph", 2, 4)); - //gQueue.push(new removeSelection("paragraph")); - todo(false, "removeSelection doesn't fire text selection changed events, see bug bug 688124."); + gQueue.push(new removeSelection("paragraph")); gQueue.push(new synthFocus("textbox", onfocusEventSeq("textbox"))); gQueue.push(new changeSelection("textbox", 1, 3)); gQueue.push(new synthFocus("textarea", onfocusEventSeq("textarea"))); gQueue.push(new changeSelection("textarea", 1, 3)); gQueue.invoke(); // Will call SimpleTest.finish(); @@ -150,17 +149,22 @@ </script> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=688126" title="nsIAccessibleText::setSelectionBounds doesn't fire text selection changed events in some cases"> - Mozilla Bug 688126 + Bug 688126 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=688124" + title="no text selection changed event when selection is removed"> + Bug 688124 </a> <p id="display"></p> <div id="content" style="display: none"></div> <pre id="test"> </pre> <p id="paragraph">hello</p> <input id="textbox" value="hello"/>
--- a/b2g/chrome/content/shell.js +++ b/b2g/chrome/content/shell.js @@ -1254,70 +1254,89 @@ window.addEventListener('ContentStart', type: 'visible-audio-channel-changed', channel: aData }); shell.visibleNormalAudioActive = (aData == 'normal'); }, "visible-audio-channel-changed", false); })(); (function recordingStatusTracker() { - let gRecordingActiveCount = 0; + // Recording status is tracked per process with following data structure: + // {<processId>: {count: <N>, + // requestURL: <requestURL>, + // isApp: <isApp>, + // audioCount: <N>, + // videoCount: <N>}} let gRecordingActiveProcesses = {}; let recordingHandler = function(aSubject, aTopic, aData) { - let oldCount = gRecordingActiveCount; - - let processId = (!aSubject) ? 'main' - : aSubject.QueryInterface(Ci.nsIPropertyBag2).get('childID'); + let props = aSubject.QueryInterface(Ci.nsIPropertyBag2); + let processId = (props.hasKey('childID')) ? props.get('childID') + : 'main'; if (processId && !gRecordingActiveProcesses.hasOwnProperty(processId)) { - gRecordingActiveProcesses[processId] = 0; + gRecordingActiveProcesses[processId] = {count: 0, + requestURL: props.get('requestURL'), + isApp: props.get('isApp'), + audioCount: 0, + videoCount: 0 }; } let currentActive = gRecordingActiveProcesses[processId]; + let wasActive = (currentActive['count'] > 0); + let wasAudioActive = (currentActive['audioCount'] > 0); + let wasVideoActive = (currentActive['videoCount'] > 0); + switch (aData) { case 'starting': - gRecordingActiveCount++; - currentActive++; + currentActive['count']++; + currentActive['audioCount'] += (props.get('isAudio')) ? 1 : 0; + currentActive['videoCount'] += (props.get('isVideo')) ? 1 : 0; break; case 'shutdown': - // Bug 928206 will make shutdown be sent even if no starting. - if (currentActive > 0) { - gRecordingActiveCount--; - currentActive--; - } + currentActive['count']--; + currentActive['audioCount'] -= (props.get('isAudio')) ? 1 : 0; + currentActive['videoCount'] -= (props.get('isVideo')) ? 1 : 0; break; case 'content-shutdown': - gRecordingActiveCount -= currentActive; - currentActive = 0; + currentActive['count'] = 0; + currentActive['audioCount'] = 0; + currentActive['videoCount'] = 0; break; } - if (currentActive > 0) { + if (currentActive['count'] > 0) { gRecordingActiveProcesses[processId] = currentActive; } else { delete gRecordingActiveProcesses[processId]; } - // We need to track changes from N <-> 0 - if ((oldCount === 0 && gRecordingActiveCount > 0) || - (gRecordingActiveCount === 0 && oldCount > 0)) { + // We need to track changes if any active state is changed. + let isActive = (currentActive['count'] > 0); + let isAudioActive = (currentActive['audioCount'] > 0); + let isVideoActive = (currentActive['videoCount'] > 0); + if ((isActive != wasActive) || + (isAudioActive != wasAudioActive) || + (isVideoActive != wasVideoActive)) { shell.sendChromeEvent({ type: 'recording-status', - active: (gRecordingActiveCount > 0) + active: isActive, + requestURL: currentActive['requestURL'], + isApp: currentActive['isApp'], + isAudio: isAudioActive, + isVideo: isVideoActive }); } }; Services.obs.addObserver(recordingHandler, 'recording-device-events', false); Services.obs.addObserver(recordingHandler, 'recording-device-ipc-events', false); Services.obs.addObserver(function(aSubject, aTopic, aData) { // send additional recording events if content process is being killed - let props = aSubject.QueryInterface(Ci.nsIPropertyBag2); - let childId = aSubject.get('childID'); - if (gRecordingActiveProcesses.hasOwnProperty(childId) >= 0) { + let processId = aSubject.QueryInterface(Ci.nsIPropertyBag2).get('childID'); + if (gRecordingActiveProcesses.hasOwnProperty(processId)) { Services.obs.notifyObservers(aSubject, 'recording-device-ipc-events', 'content-shutdown'); } }, 'ipc:content-shutdown', false); })(); (function volumeStateTracker() { Services.obs.addObserver(function(aSubject, aTopic, aData) { shell.sendChromeEvent({
--- a/browser/app/blocklist.xml +++ b/browser/app/blocklist.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> -<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1382113575000"> +<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1382112975000"> <emItems> <emItem blockID="i454" id="sqlmoz@facebook.com"> <versionRange minVersion="0" maxVersion="*" severity="3"> </versionRange> <versionRange minVersion="0" maxVersion="*" severity="3"> </versionRange> </emItem> <emItem blockID="i58" id="webmaster@buzzzzvideos.info"> @@ -1186,37 +1186,16 @@ </pluginItem> <pluginItem blockID="p459"> <match name="filename" exp="JavaAppletPlugin\.plugin" /> <versionRange minVersion="Java 7 Update 25" maxVersion="Java 7 Update 44" severity="0" vulnerabilitystatus="1"> <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"> <versionRange minVersion="17.0" maxVersion="*" /> </targetApplication> </versionRange> </pluginItem> - <pluginItem blockID="p462"> - <match name="filename" exp="JavaAppletPlugin\.plugin" /> <versionRange minVersion="Java 7 Update 45" maxVersion="Java 7 Update 45" severity="0" vulnerabilitystatus="2"> - <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"> - <versionRange minVersion="24.0" maxVersion="*" /> - </targetApplication> - </versionRange> - </pluginItem> - <pluginItem blockID="p463"> - <match name="name" exp="Java\(TM\) Platform SE 7 U45(\s[^\d\._U]|$)" /> <match name="filename" exp="npjp2\.dll" /> <versionRange severity="0" vulnerabilitystatus="2"> - <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"> - <versionRange minVersion="24.0" maxVersion="*" /> - </targetApplication> - </versionRange> - </pluginItem> - <pluginItem blockID="p464"> - <match name="name" exp="Java(\(TM\))? Plug-in 10\.45(\.[0-9]+)?([^\d\._]|$)" /> <match name="filename" exp="libnpjp2\.so" /> <versionRange severity="0" vulnerabilitystatus="2"> - <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"> - <versionRange minVersion="24.0" maxVersion="*" /> - </targetApplication> - </versionRange> - </pluginItem> </pluginItems> <gfxItems> <gfxBlacklistEntry blockID="g35"> <os>WINNT 6.1</os> <vendor>0x10de</vendor> <devices> <device>0x0a6c</device> </devices> <feature>DIRECT2D</feature> <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus> <driverVersion>8.17.12.5896</driverVersion> <driverVersionComparator>LESS_THAN_OR_EQUAL</driverVersionComparator> </gfxBlacklistEntry> <gfxBlacklistEntry blockID="g36"> <os>WINNT 6.1</os> <vendor>0x10de</vendor> <devices>
--- a/browser/base/content/content.js +++ b/browser/base/content/content.js @@ -94,17 +94,17 @@ let AboutHomeListener = { // Inject search engine and snippets URL. let docElt = doc.documentElement; // set the following attributes BEFORE searchEngineName, which triggers to // show the snippets when it's set. docElt.setAttribute("snippetsURL", aData.snippetsURL); if (aData.showKnowYourRights) docElt.setAttribute("showKnowYourRights", "true"); docElt.setAttribute("snippetsVersion", aData.snippetsVersion); - docElt.setAttribute("searchEngineName", Services.search.defaultEngine.name); + docElt.setAttribute("searchEngineName", aData.defaultEngineName); }, onPageLoad: function() { let doc = content.document; if (doc.documentURI.toLowerCase() != "about:home" || doc.documentElement.hasAttribute("hasBrowserHandlers")) { return; }
--- a/browser/base/content/sanitize.js +++ b/browser/base/content/sanitize.js @@ -222,16 +222,22 @@ Sanitizer.prototype = { // Clear last URL of the Open Web Location dialog var prefs = Components.classes["@mozilla.org/preferences-service;1"] .getService(Components.interfaces.nsIPrefBranch); try { prefs.clearUserPref("general.open_location.last_url"); } catch (e) { } + + try { + var seer = Components.classes["@mozilla.org/network/seer;1"] + .getService(Components.interfaces.nsINetworkSeer); + seer.reset(); + } catch (e) { } }, get canClear() { // bug 347231: Always allow clearing history due to dependencies on // the browser:purge-session-history notification. (like error console) return true; }
--- a/browser/devtools/profiler/cleopatra/js/ui.js +++ b/browser/devtools/profiler/cleopatra/js/ui.js @@ -535,26 +535,16 @@ HistogramView.prototype = { ctx.fillStyle = "green"; } else if (isInRangeSelector) { ctx.fillStyle = "blue"; } else { ctx.fillStyle = step.color; } var roundedHeight = Math.round(step.value * height); ctx.fillRect(step.x, height - roundedHeight, step.width, roundedHeight); - if (step.marker) { - var x = step.x + step.width + 2; - var endPoint = x + ctx.measureText(step.marker).width; - var lastDataPoint = this._histogramData[this._histogramData.length-1]; - if (endPoint >= lastDataPoint.x + lastDataPoint.width) { - x -= endPoint - (lastDataPoint.x + lastDataPoint.width) - 1; - } - ctx.fillText(step.marker, x, 15 + ((markerCount % 2) == 0 ? 0 : 20)); - markerCount++; - } } this._finishedRendering = true; }, highlightedCallstackChanged: function HistogramView_highlightedCallstackChanged(highlightedCallstack) { this._scheduleRender(highlightedCallstack); }, _isInRangeSelector: function HistogramView_isInRangeSelector(index) {
--- a/browser/devtools/shadereditor/panel.js +++ b/browser/devtools/shadereditor/panel.js @@ -28,16 +28,17 @@ ShaderEditorPanel.prototype = { if (!this.target.isRemote) { targetPromise = this.target.makeRemote(); } else { targetPromise = promise.resolve(this.target); } return targetPromise .then(() => { + this.panelWin.gToolbox = this._toolbox; this.panelWin.gTarget = this.target; this.panelWin.gFront = new WebGLFront(this.target.client, this.target.form); return this.panelWin.startupShaderEditor(); }) .then(() => { this.isReady = true; this.emit("ready"); return this;
--- a/browser/devtools/shadereditor/shadereditor.js +++ b/browser/devtools/shadereditor/shadereditor.js @@ -34,17 +34,17 @@ const DEFAULT_EDITOR_CONFIG = { mode: Editor.modes.text, lineNumbers: true, showAnnotationRuler: true }; /** * The current target and the WebGL Editor front, set by this tool's host. */ -let gTarget, gFront; +let gToolbox, gTarget, gFront; /** * Initializes the shader editor controller and views. */ function startupShaderEditor() { return promise.all([ EventsHandler.initialize(), ShadersListView.initialize(), @@ -66,32 +66,44 @@ function shutdownShaderEditor() { /** * Functions handling target-related lifetime events. */ let EventsHandler = { /** * Listen for events emitted by the current tab target. */ initialize: function() { + this._onHostChanged = this._onHostChanged.bind(this); this._onWillNavigate = this._onWillNavigate.bind(this); this._onProgramLinked = this._onProgramLinked.bind(this); + gToolbox.on("host-changed", this._onHostChanged); gTarget.on("will-navigate", this._onWillNavigate); gFront.on("program-linked", this._onProgramLinked); }, /** * Remove events emitted by the current tab target. */ destroy: function() { + gToolbox.off("host-changed", this._onHostChanged); gTarget.off("will-navigate", this._onWillNavigate); gFront.off("program-linked", this._onProgramLinked); }, /** + * Handles a host change event on the parent toolbox. + */ + _onHostChanged: function() { + if (gToolbox.hostType == "side") { + $("#shaders-pane").removeAttribute("height"); + } + }, + + /** * Called for each location change in the debugged tab. */ _onWillNavigate: function() { gFront.setup(); ShadersListView.empty(); ShadersEditorsView.setText({ vs: "", fs: "" }); $("#reload-notice").hidden = true; @@ -167,16 +179,22 @@ let ShadersListView = Heritage.extend(Wi checkboxTooltip: L10N.getStr("shadersList.blackboxLabel") } }); // Make sure there's always a selected item available. if (!this.selectedItem) { this.selectedIndex = 0; } + + // Prevent this container from growing indefinitely in height when the + // toolbox is docked to the side. + if (gToolbox.hostType == "side" && this.itemCount == SHADERS_AUTOGROW_ITEMS) { + this._pane.setAttribute("height", this._pane.getBoundingClientRect().height); + } }, /** * The select listener for the sources container. */ _onShaderSelect: function({ detail: sourceItem }) { if (!sourceItem) { return;
--- a/browser/devtools/shadereditor/shadereditor.xul +++ b/browser/devtools/shadereditor/shadereditor.xul @@ -35,30 +35,30 @@ pack="center" flex="1" hidden="true"> <label id="requests-menu-waiting-notice-label" class="plain" value="&shaderEditorUI.emptyNotice;"/> </hbox> - <hbox id="content" flex="1" hidden="true"> + <box id="content" class="devtools-responsive-container" flex="1" hidden="true"> <vbox id="shaders-pane"/> <splitter class="devtools-side-splitter"/> - <hbox id="shaders-editors" flex="1"> + <box id="shaders-editors" class="devtools-responsive-container" flex="1"> <vbox flex="1"> <vbox id="vs-editor" flex="1"/> <label id="vs-editor-label" class="plain editor-label" value="&shaderEditorUI.vertexShader;"/> </vbox> <splitter id="editors-splitter" class="devtools-side-splitter"/> <vbox flex="1"> <vbox id="fs-editor" flex="1"/> <label id="fs-editor-label" class="plain editor-label" value="&shaderEditorUI.fragmentShader;"/> </vbox> - </hbox> - </hbox> + </box> + </box> </vbox> </window>
--- a/browser/metro/theme/platform.css +++ b/browser/metro/theme/platform.css @@ -682,17 +682,17 @@ arrowbox { .meta-section-container[viewstate="snapped"] .meta-section-title, .meta-section-container[viewstate="snapped"] richgrid { margin-top: @metro_spacing_xnormal@; padding: 0; } .meta-section > richgrid { opacity: 1; - transform: translateX(0) scale(1); + transform: translateX(0) translateY(0); transition-duration: 367ms; transition-delay: 500ms; transition-timing-function: @metro_animation_easing@; } .meta-section:nth-child(2) > richgrid { transition-delay: 600ms; } @@ -700,17 +700,17 @@ arrowbox { transition-delay: 700ms; } .meta-section:nth-child(4) > richgrid { transition-delay: 800ms; } .meta-section > richgrid[fade] { opacity: 0; - transform: translateX(150px) scale(.9); + transform: translateX(150px) translateY(25px); } #start-container[viewstate="snapped"] .meta-section > richgrid { transition-property: none; } /* App bars ----------------------------------------------------------------- */
--- a/browser/modules/AboutHome.jsm +++ b/browser/modules/AboutHome.jsm @@ -182,17 +182,18 @@ let AboutHome = { Components.utils.import("resource:///modules/sessionstore/SessionStore.jsm", wrapper); let ss = wrapper.SessionStore; ss.promiseInitialized.then(function() { let data = { showRestoreLastSession: ss.canRestoreLastSession, snippetsURL: AboutHomeUtils.snippetsURL, showKnowYourRights: AboutHomeUtils.showKnowYourRights, - snippetsVersion: AboutHomeUtils.snippetsVersion + snippetsVersion: AboutHomeUtils.snippetsVersion, + defaultEngineName: Services.search.defaultEngine.name }; if (AboutHomeUtils.showKnowYourRights) { // Set pref to indicate we've shown the notification. let currentVersion = Services.prefs.getIntPref("browser.rights.version"); Services.prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true); }
--- a/browser/themes/shared/devtools/shadereditor.inc.css +++ b/browser/themes/shared/devtools/shadereditor.inc.css @@ -100,8 +100,37 @@ color: #fff; } .editor-label[selected] { background: linear-gradient(hsl(206,61%,40%), hsl(206,61%,31%)) repeat-x top left; box-shadow: inset 0 1px 0 hsla(210,40%,83%,.15), inset 0 -1px 0 hsla(210,40%,83%,.05); } + +/* Responsive sidebar */ + +@media (max-width: 700px) { + #shaders-pane { + max-height: 60vh; + } + + .side-menu-widget-container { + box-shadow: none !important; + } + + .side-menu-widget-item-arrow { + background-image: none !important; + } + + .devtools-side-splitter { + border-top-color: transparent !important; + } + + .editor-label { + -moz-box-ordinal-group: 0; + } + + .editor-label:not([selected]) { + border-top: 1px solid hsla(210,8%,5%,.25); + box-shadow: inset 0 1px 0 hsla(210,40%,83%,.15); + } +}
--- a/content/base/src/nsDOMFileReader.cpp +++ b/content/base/src/nsDOMFileReader.cpp @@ -179,39 +179,39 @@ nsDOMFileReader::GetReadyState(uint16_t { *aReadyState = ReadyState(); return NS_OK; } JS::Value nsDOMFileReader::GetResult(JSContext* aCx, ErrorResult& aRv) { - JS::Rooted<JS::Value> result(aCx, JS::UndefinedValue()); + JS::Rooted<JS::Value> result(aCx); aRv = GetResult(aCx, result.address()); return result; } NS_IMETHODIMP nsDOMFileReader::GetResult(JSContext* aCx, JS::Value* aResult) { + JS::Rooted<JS::Value> result(aCx); if (mDataFormat == FILE_AS_ARRAYBUFFER) { if (mReadyState == nsIDOMFileReader::DONE && mResultArrayBuffer) { - JSObject* tmp = mResultArrayBuffer; - *aResult = OBJECT_TO_JSVAL(tmp); + result.setObject(*mResultArrayBuffer); } else { - *aResult = JSVAL_NULL; + result.setNull(); } - if (!JS_WrapValue(aCx, aResult)) { + if (!JS_WrapValue(aCx, &result)) { return NS_ERROR_FAILURE; } + *aResult = result; return NS_OK; } - + nsString tmpResult = mResult; - JS::Rooted<JS::Value> result(aCx); if (!xpc::StringToJsval(aCx, tmpResult, &result)) { return NS_ERROR_FAILURE; } *aResult = result; return NS_OK; } NS_IMETHODIMP
--- a/content/base/src/nsFrameLoader.h +++ b/content/base/src/nsFrameLoader.h @@ -305,16 +305,18 @@ public: /** * Applies a new set of sandbox flags. These are merged with the sandbox * flags from our owning content's owning document with a logical OR, this * ensures that we can only add restrictions and never remove them. */ void ApplySandboxFlags(uint32_t sandboxFlags); + void GetURL(nsString& aURL); + private: void SetOwnerContent(mozilla::dom::Element* aContent); bool ShouldUseRemoteProcess(); /** * Is this a frameloader for a bona fide <iframe mozbrowser> or @@ -353,17 +355,16 @@ private: already_AddRefed<mozIApplication> GetContainingApp(); /** * If we are an IPC frame, set mRemoteFrame. Otherwise, create and * initialize mDocShell. */ nsresult MaybeCreateDocShell(); nsresult EnsureMessageManager(); - NS_HIDDEN_(void) GetURL(nsString& aURL); // Properly retrieves documentSize of any subdocument type. nsresult GetWindowDimensions(nsRect& aRect); // Updates the subdocument position and size. This gets called only // when we have our own in-process DocShell. NS_HIDDEN_(nsresult) UpdateBaseWindowPositionAndSize(nsSubDocumentFrame *aIFrame); nsresult CheckURILoad(nsIURI* aURI);
--- a/content/base/src/nsFrameMessageManager.cpp +++ b/content/base/src/nsFrameMessageManager.cpp @@ -46,24 +46,36 @@ # undef SendMessage # endif #endif using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::dom::ipc; +static PLDHashOperator +CycleCollectorTraverseListeners(const nsAString& aKey, + nsTArray<nsMessageListenerInfo>* aListeners, + void* aCb) +{ + nsCycleCollectionTraversalCallback* cb = + static_cast<nsCycleCollectionTraversalCallback*> (aCb); + uint32_t count = aListeners->Length(); + for (uint32_t i = 0; i < count; ++i) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "listeners[i] mStrongListener"); + cb->NoteXPCOMChild((*aListeners)[i].mStrongListener.get()); + } + return PL_DHASH_NEXT; +} + NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameMessageManager) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameMessageManager) - uint32_t count = tmp->mListeners.Length(); - for (uint32_t i = 0; i < count; i++) { - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mListeners[i] mStrongListener"); - cb.NoteXPCOMChild(tmp->mListeners[i].mStrongListener.get()); - } + tmp->mListeners.EnumerateRead(CycleCollectorTraverseListeners, + static_cast<void*>(&cb)); NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildManagers) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameMessageManager) tmp->mListeners.Clear(); for (int32_t i = tmp->mChildManagers.Count(); i > 0; --i) { static_cast<nsFrameMessageManager*>(tmp->mChildManagers[i - 1])-> Disconnect(false); @@ -241,100 +253,136 @@ SameProcessCpowHolder::ToObject(JSContex } // nsIMessageListenerManager NS_IMETHODIMP nsFrameMessageManager::AddMessageListener(const nsAString& aMessage, nsIMessageListener* aListener) { - nsCOMPtr<nsIAtom> message = do_GetAtom(aMessage); - uint32_t len = mListeners.Length(); - for (uint32_t i = 0; i < len; ++i) { - if (mListeners[i].mMessage == message && - mListeners[i].mStrongListener == aListener) { - return NS_OK; + nsTArray<nsMessageListenerInfo>* listeners = mListeners.Get(aMessage); + if (!listeners) { + listeners = new nsTArray<nsMessageListenerInfo>(); + mListeners.Put(aMessage, listeners); + } else { + uint32_t len = listeners->Length(); + for (uint32_t i = 0; i < len; ++i) { + if ((*listeners)[i].mStrongListener == aListener) { + return NS_OK; + } } } - nsMessageListenerInfo* entry = mListeners.AppendElement(); + + nsMessageListenerInfo* entry = listeners->AppendElement(); NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY); - entry->mMessage = message; entry->mStrongListener = aListener; return NS_OK; } NS_IMETHODIMP nsFrameMessageManager::RemoveMessageListener(const nsAString& aMessage, nsIMessageListener* aListener) { - nsCOMPtr<nsIAtom> message = do_GetAtom(aMessage); - uint32_t len = mListeners.Length(); + nsTArray<nsMessageListenerInfo>* listeners = mListeners.Get(aMessage); + if (!listeners) { + return NS_OK; + } + + uint32_t len = listeners->Length(); for (uint32_t i = 0; i < len; ++i) { - if (mListeners[i].mMessage == message && - mListeners[i].mStrongListener == aListener) { - mListeners.RemoveElementAt(i); + if ((*listeners)[i].mStrongListener == aListener) { + listeners->RemoveElementAt(i); return NS_OK; } } return NS_OK; } +#ifdef DEBUG +typedef struct +{ + nsCOMPtr<nsISupports> mCanonical; + nsWeakPtr mWeak; +} CanonicalCheckerParams; + +static PLDHashOperator +CanonicalChecker(const nsAString& aKey, + nsTArray<nsMessageListenerInfo>* aListeners, + void* aParams) +{ + CanonicalCheckerParams* params = + static_cast<CanonicalCheckerParams*> (aParams); + + uint32_t count = aListeners->Length(); + for (uint32_t i = 0; i < count; i++) { + if (!(*aListeners)[i].mWeakListener) { + continue; + } + nsCOMPtr<nsISupports> otherCanonical = + do_QueryReferent((*aListeners)[i].mWeakListener); + MOZ_ASSERT((params->mCanonical == otherCanonical) == + (params->mWeak == (*aListeners)[i].mWeakListener)); + } + return PL_DHASH_NEXT; +} +#endif + NS_IMETHODIMP nsFrameMessageManager::AddWeakMessageListener(const nsAString& aMessage, nsIMessageListener* aListener) { nsWeakPtr weak = do_GetWeakReference(aListener); NS_ENSURE_TRUE(weak, NS_ERROR_NO_INTERFACE); #ifdef DEBUG // It's technically possible that one object X could give two different // nsIWeakReference*'s when you do_GetWeakReference(X). We really don't want // this to happen; it will break e.g. RemoveWeakMessageListener. So let's // check that we're not getting ourselves into that situation. nsCOMPtr<nsISupports> canonical = do_QueryInterface(aListener); - for (uint32_t i = 0; i < mListeners.Length(); ++i) { - if (!mListeners[i].mWeakListener) { - continue; - } - - nsCOMPtr<nsISupports> otherCanonical = - do_QueryReferent(mListeners[i].mWeakListener); - MOZ_ASSERT((canonical == otherCanonical) == - (weak == mListeners[i].mWeakListener)); - } + CanonicalCheckerParams params; + params.mCanonical = canonical; + params.mWeak = weak; + mListeners.EnumerateRead(CanonicalChecker, (void*)¶ms); #endif - nsCOMPtr<nsIAtom> message = do_GetAtom(aMessage); - uint32_t len = mListeners.Length(); - for (uint32_t i = 0; i < len; ++i) { - if (mListeners[i].mMessage == message && - mListeners[i].mWeakListener == weak) { - return NS_OK; + nsTArray<nsMessageListenerInfo>* listeners = mListeners.Get(aMessage); + if (!listeners) { + listeners = new nsTArray<nsMessageListenerInfo>(); + mListeners.Put(aMessage, listeners); + } else { + uint32_t len = listeners->Length(); + for (uint32_t i = 0; i < len; ++i) { + if ((*listeners)[i].mWeakListener == weak) { + return NS_OK; + } } } - nsMessageListenerInfo* entry = mListeners.AppendElement(); - entry->mMessage = message; + nsMessageListenerInfo* entry = listeners->AppendElement(); entry->mWeakListener = weak; return NS_OK; } NS_IMETHODIMP nsFrameMessageManager::RemoveWeakMessageListener(const nsAString& aMessage, nsIMessageListener* aListener) { nsWeakPtr weak = do_GetWeakReference(aListener); NS_ENSURE_TRUE(weak, NS_OK); - nsCOMPtr<nsIAtom> message = do_GetAtom(aMessage); - uint32_t len = mListeners.Length(); + nsTArray<nsMessageListenerInfo>* listeners = mListeners.Get(aMessage); + if (!listeners) { + return NS_OK; + } + + uint32_t len = listeners->Length(); for (uint32_t i = 0; i < len; ++i) { - if (mListeners[i].mMessage == message && - mListeners[i].mWeakListener == weak) { - mListeners.RemoveElementAt(i); + if ((*listeners)[i].mWeakListener == weak) { + listeners->RemoveElementAt(i); return NS_OK; } } return NS_OK; } // nsIFrameScriptLoader @@ -787,143 +835,141 @@ nsresult nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget, const nsAString& aMessage, bool aIsSync, const StructuredCloneData* aCloneData, CpowHolder* aCpows, InfallibleTArray<nsString>* aJSONRetVal) { AutoSafeJSContext ctx; + nsTArray<nsMessageListenerInfo>* listeners = mListeners.Get(aMessage); + if (listeners) { - if (mListeners.Length()) { - nsCOMPtr<nsIAtom> name = do_GetAtom(aMessage); MMListenerRemover lr(this); - for (uint32_t i = 0; i < mListeners.Length(); ++i) { + for (uint32_t i = 0; i < listeners->Length(); ++i) { // Remove mListeners[i] if it's an expired weak listener. nsCOMPtr<nsISupports> weakListener; - if (mListeners[i].mWeakListener) { - weakListener = do_QueryReferent(mListeners[i].mWeakListener); + if ((*listeners)[i].mWeakListener) { + weakListener = do_QueryReferent((*listeners)[i].mWeakListener); if (!weakListener) { - mListeners.RemoveElementAt(i--); + listeners->RemoveElementAt(i--); continue; } } - if (mListeners[i].mMessage == name) { - nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS; - if (weakListener) { - wrappedJS = do_QueryInterface(weakListener); - } else { - wrappedJS = do_QueryInterface(mListeners[i].mStrongListener); - } + nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS; + if (weakListener) { + wrappedJS = do_QueryInterface(weakListener); + } else { + wrappedJS = do_QueryInterface((*listeners)[i].mStrongListener); + } + + if (!wrappedJS) { + continue; + } + JS::Rooted<JSObject*> object(ctx, wrappedJS->GetJSObject()); + if (!object) { + continue; + } + JSAutoCompartment ac(ctx, object); + + // The parameter for the listener function. + JS::Rooted<JSObject*> param(ctx, + JS_NewObject(ctx, nullptr, nullptr, nullptr)); + NS_ENSURE_TRUE(param, NS_ERROR_OUT_OF_MEMORY); - if (!wrappedJS) { - continue; + JS::Rooted<JS::Value> targetv(ctx); + JS::Rooted<JSObject*> global(ctx, JS_GetGlobalForObject(ctx, object)); + nsContentUtils::WrapNative(ctx, global, aTarget, &targetv, + nullptr, true); + + JS::RootedObject cpows(ctx); + if (aCpows) { + if (!aCpows->ToObject(ctx, &cpows)) { + return NS_ERROR_UNEXPECTED; } - JS::Rooted<JSObject*> object(ctx, wrappedJS->GetJSObject()); - if (!object) { - continue; + } + + if (!cpows) { + cpows = JS_NewObject(ctx, nullptr, nullptr, nullptr); + if (!cpows) { + return NS_ERROR_UNEXPECTED; } - JSAutoCompartment ac(ctx, object); + } + + JS::RootedValue cpowsv(ctx, JS::ObjectValue(*cpows)); - // The parameter for the listener function. - JS::Rooted<JSObject*> param(ctx, - JS_NewObject(ctx, nullptr, nullptr, nullptr)); - NS_ENSURE_TRUE(param, NS_ERROR_OUT_OF_MEMORY); + JS::Rooted<JS::Value> json(ctx, JS::NullValue()); + if (aCloneData && aCloneData->mDataLength && + !ReadStructuredClone(ctx, *aCloneData, &json)) { + JS_ClearPendingException(ctx); + return NS_OK; + } + JS::Rooted<JSString*> jsMessage(ctx, + JS_NewUCStringCopyN(ctx, + static_cast<const jschar*>(aMessage.BeginReading()), + aMessage.Length())); + NS_ENSURE_TRUE(jsMessage, NS_ERROR_OUT_OF_MEMORY); + JS_DefineProperty(ctx, param, "target", targetv, nullptr, nullptr, JSPROP_ENUMERATE); + JS_DefineProperty(ctx, param, "name", + STRING_TO_JSVAL(jsMessage), nullptr, nullptr, JSPROP_ENUMERATE); + JS_DefineProperty(ctx, param, "sync", + BOOLEAN_TO_JSVAL(aIsSync), nullptr, nullptr, JSPROP_ENUMERATE); + JS_DefineProperty(ctx, param, "json", json, nullptr, nullptr, JSPROP_ENUMERATE); // deprecated + JS_DefineProperty(ctx, param, "data", json, nullptr, nullptr, JSPROP_ENUMERATE); + JS_DefineProperty(ctx, param, "objects", cpowsv, nullptr, nullptr, JSPROP_ENUMERATE); - JS::Rooted<JS::Value> targetv(ctx); - JS::Rooted<JSObject*> global(ctx, JS_GetGlobalForObject(ctx, object)); - nsContentUtils::WrapNative(ctx, global, aTarget, &targetv, - nullptr, true); + JS::Rooted<JS::Value> thisValue(ctx, JS::UndefinedValue()); + + JS::Rooted<JS::Value> funval(ctx); + if (JS_ObjectIsCallable(ctx, object)) { + // If the listener is a JS function: + funval.setObject(*object); - JS::RootedObject cpows(ctx); - if (aCpows) { - if (!aCpows->ToObject(ctx, &cpows)) { - return NS_ERROR_UNEXPECTED; - } + // A small hack to get 'this' value right on content side where + // messageManager is wrapped in TabChildGlobal. + nsCOMPtr<nsISupports> defaultThisValue; + if (mChrome) { + defaultThisValue = do_QueryObject(this); + } else { + defaultThisValue = aTarget; } + JS::Rooted<JSObject*> global(ctx, JS_GetGlobalForObject(ctx, object)); + nsContentUtils::WrapNative(ctx, global, defaultThisValue, + &thisValue, nullptr, true); + } else { + // If the listener is a JS object which has receiveMessage function: + if (!JS_GetProperty(ctx, object, "receiveMessage", &funval) || + !funval.isObject()) + return NS_ERROR_UNEXPECTED; - if (!cpows) { - cpows = JS_NewObject(ctx, nullptr, nullptr, nullptr); - if (!cpows) { - return NS_ERROR_UNEXPECTED; - } + // Check if the object is even callable. + NS_ENSURE_STATE(JS_ObjectIsCallable(ctx, &funval.toObject())); + thisValue.setObject(*object); + } + + JS::Rooted<JS::Value> rval(ctx, JSVAL_VOID); + JS::Rooted<JS::Value> argv(ctx, JS::ObjectValue(*param)); + + { + JS::Rooted<JSObject*> thisObject(ctx, thisValue.toObjectOrNull()); + + JSAutoCompartment tac(ctx, thisObject); + if (!JS_WrapValue(ctx, &argv)) { + return NS_ERROR_UNEXPECTED; } - JS::RootedValue cpowsv(ctx, JS::ObjectValue(*cpows)); - - JS::Rooted<JS::Value> json(ctx, JS::NullValue()); - if (aCloneData && aCloneData->mDataLength && - !ReadStructuredClone(ctx, *aCloneData, &json)) { - JS_ClearPendingException(ctx); - return NS_OK; - } - JS::Rooted<JSString*> jsMessage(ctx, - JS_NewUCStringCopyN(ctx, - static_cast<const jschar*>(aMessage.BeginReading()), - aMessage.Length())); - NS_ENSURE_TRUE(jsMessage, NS_ERROR_OUT_OF_MEMORY); - JS_DefineProperty(ctx, param, "target", targetv, nullptr, nullptr, JSPROP_ENUMERATE); - JS_DefineProperty(ctx, param, "name", - STRING_TO_JSVAL(jsMessage), nullptr, nullptr, JSPROP_ENUMERATE); - JS_DefineProperty(ctx, param, "sync", - BOOLEAN_TO_JSVAL(aIsSync), nullptr, nullptr, JSPROP_ENUMERATE); - JS_DefineProperty(ctx, param, "json", json, nullptr, nullptr, JSPROP_ENUMERATE); // deprecated - JS_DefineProperty(ctx, param, "data", json, nullptr, nullptr, JSPROP_ENUMERATE); - JS_DefineProperty(ctx, param, "objects", cpowsv, nullptr, nullptr, JSPROP_ENUMERATE); - - JS::Rooted<JS::Value> thisValue(ctx, JS::UndefinedValue()); - - JS::Rooted<JS::Value> funval(ctx); - if (JS_ObjectIsCallable(ctx, object)) { - // If the listener is a JS function: - funval.setObject(*object); - - // A small hack to get 'this' value right on content side where - // messageManager is wrapped in TabChildGlobal. - nsCOMPtr<nsISupports> defaultThisValue; - if (mChrome) { - defaultThisValue = do_QueryObject(this); - } else { - defaultThisValue = aTarget; - } - JS::Rooted<JSObject*> global(ctx, JS_GetGlobalForObject(ctx, object)); - nsContentUtils::WrapNative(ctx, global, defaultThisValue, - &thisValue, nullptr, true); - } else { - // If the listener is a JS object which has receiveMessage function: - if (!JS_GetProperty(ctx, object, "receiveMessage", &funval) || - !funval.isObject()) - return NS_ERROR_UNEXPECTED; - - // Check if the object is even callable. - NS_ENSURE_STATE(JS_ObjectIsCallable(ctx, &funval.toObject())); - thisValue.setObject(*object); - } - - JS::Rooted<JS::Value> rval(ctx, JSVAL_VOID); - JS::Rooted<JS::Value> argv(ctx, JS::ObjectValue(*param)); - - { - JS::Rooted<JSObject*> thisObject(ctx, thisValue.toObjectOrNull()); - - JSAutoCompartment tac(ctx, thisObject); - if (!JS_WrapValue(ctx, argv.address())) { - return NS_ERROR_UNEXPECTED; - } - - JS_CallFunctionValue(ctx, thisObject, - funval, 1, argv.address(), rval.address()); - if (aJSONRetVal) { - nsString json; - if (JS_Stringify(ctx, &rval, JS::NullPtr(), JS::NullHandleValue, - JSONCreator, &json)) { - aJSONRetVal->AppendElement(json); - } + JS_CallFunctionValue(ctx, thisObject, + funval, 1, argv.address(), rval.address()); + if (aJSONRetVal) { + nsString json; + if (JS_Stringify(ctx, &rval, JS::NullPtr(), JS::NullHandleValue, + JSONCreator, &json)) { + aJSONRetVal->AppendElement(json); } } } } } nsRefPtr<nsFrameMessageManager> kungfuDeathGrip = mParentManager; return mParentManager ? mParentManager->ReceiveMessage(aTarget, aMessage, aIsSync, aCloneData, @@ -1006,77 +1052,97 @@ nsFrameMessageManager::Disconnect(bool a mOwnedCallback = nullptr; if (!mHandlingMessage) { mListeners.Clear(); } } namespace { -struct MessageManagerReferentCount { - MessageManagerReferentCount() : strong(0), weakAlive(0), weakDead(0) {} - size_t strong; - size_t weakAlive; - size_t weakDead; - nsCOMArray<nsIAtom> suspectMessages; - nsDataHashtable<nsPtrHashKey<nsIAtom>, uint32_t> messageCounter; +struct MessageManagerReferentCount +{ + MessageManagerReferentCount() : mStrong(0), mWeakAlive(0), mWeakDead(0) {} + size_t mStrong; + size_t mWeakAlive; + size_t mWeakDead; + nsTArray<nsString> mSuspectMessages; + nsDataHashtable<nsStringHashKey, uint32_t> mMessageCounter; }; } // anonymous namespace namespace mozilla { namespace dom { class MessageManagerReporter MOZ_FINAL : public nsIMemoryReporter { public: NS_DECL_ISUPPORTS NS_DECL_NSIMEMORYREPORTER + + static const size_t kSuspectReferentCount = 300; protected: - static const size_t kSuspectReferentCount = 300; void CountReferents(nsFrameMessageManager* aMessageManager, MessageManagerReferentCount* aReferentCount); }; NS_IMPL_ISUPPORTS1(MessageManagerReporter, nsIMemoryReporter) -void -MessageManagerReporter::CountReferents(nsFrameMessageManager* aMessageManager, - MessageManagerReferentCount* aReferentCount) +static PLDHashOperator +CollectMessageListenerData(const nsAString& aKey, + nsTArray<nsMessageListenerInfo>* aListeners, + void* aData) { - for (uint32_t i = 0; i < aMessageManager->mListeners.Length(); i++) { - const nsMessageListenerInfo& listenerInfo = aMessageManager->mListeners[i]; + MessageManagerReferentCount* referentCount = + static_cast<MessageManagerReferentCount*>(aData); + + uint32_t listenerCount = aListeners->Length(); + if (!listenerCount) { + return PL_DHASH_NEXT; + } + nsString key(aKey); + uint32_t oldCount = 0; + referentCount->mMessageCounter.Get(key, &oldCount); + uint32_t currentCount = oldCount + listenerCount; + referentCount->mMessageCounter.Put(key, currentCount); + + // Keep track of messages that have a suspiciously large + // number of referents (symptom of leak). + if (currentCount == MessageManagerReporter::kSuspectReferentCount) { + referentCount->mSuspectMessages.AppendElement(key); + } + + for (uint32_t i = 0; i < listenerCount; ++i) { + const nsMessageListenerInfo& listenerInfo = (*aListeners)[i]; if (listenerInfo.mWeakListener) { nsCOMPtr<nsISupports> referent = do_QueryReferent(listenerInfo.mWeakListener); if (referent) { - aReferentCount->weakAlive++; + referentCount->mWeakAlive++; } else { - aReferentCount->weakDead++; + referentCount->mWeakDead++; } } else { - aReferentCount->strong++; - } - - uint32_t oldCount = 0; - aReferentCount->messageCounter.Get(listenerInfo.mMessage, &oldCount); - uint32_t currentCount = oldCount + 1; - aReferentCount->messageCounter.Put(listenerInfo.mMessage, currentCount); - - // Keep track of messages that have a suspiciously large - // number of referents (symptom of leak). - if (currentCount == kSuspectReferentCount) { - aReferentCount->suspectMessages.AppendElement(listenerInfo.mMessage); + referentCount->mStrong++; } } + return PL_DHASH_NEXT; +} + +void +MessageManagerReporter::CountReferents(nsFrameMessageManager* aMessageManager, + MessageManagerReferentCount* aReferentCount) +{ + aMessageManager->mListeners.EnumerateRead(CollectMessageListenerData, + aReferentCount); // Add referent count in child managers because the listeners // participate in messages dispatched from parent message manager. - for (uint32_t i = 0; i < aMessageManager->mChildManagers.Length(); i++) { + for (uint32_t i = 0; i < aMessageManager->mChildManagers.Length(); ++i) { nsRefPtr<nsFrameMessageManager> mm = static_cast<nsFrameMessageManager*>(aMessageManager->mChildManagers[i]); CountReferents(mm, aReferentCount); } } NS_IMETHODIMP MessageManagerReporter::GetName(nsACString& aName) @@ -1097,35 +1163,35 @@ ReportReferentCount(const char* aManager rv = aCb->Callback(EmptyCString(), _path, \ nsIMemoryReporter::KIND_OTHER, \ nsIMemoryReporter::UNITS_COUNT, _amount, \ _desc, aClosure); \ NS_ENSURE_SUCCESS(rv, rv); \ } while (0) REPORT(nsPrintfCString("message-manager/referent/%s/strong", aManagerType), - aReferentCount.strong, + aReferentCount.mStrong, nsPrintfCString("The number of strong referents held by the message " "manager in the %s manager.", aManagerType)); REPORT(nsPrintfCString("message-manager/referent/%s/weak/alive", aManagerType), - aReferentCount.weakAlive, + aReferentCount.mWeakAlive, nsPrintfCString("The number of weak referents that are still alive " "held by the message manager in the %s manager.", aManagerType)); REPORT(nsPrintfCString("message-manager/referent/%s/weak/dead", aManagerType), - aReferentCount.weakDead, + aReferentCount.mWeakDead, nsPrintfCString("The number of weak referents that are dead " "held by the message manager in the %s manager.", aManagerType)); - for (uint32_t i = 0; i < aReferentCount.suspectMessages.Length(); i++) { + for (uint32_t i = 0; i < aReferentCount.mSuspectMessages.Length(); i++) { uint32_t totalReferentCount = 0; - aReferentCount.messageCounter.Get(aReferentCount.suspectMessages[i], - &totalReferentCount); - nsAtomCString suspect(aReferentCount.suspectMessages[i]); + aReferentCount.mMessageCounter.Get(aReferentCount.mSuspectMessages[i], + &totalReferentCount); + NS_ConvertUTF16toUTF8 suspect(aReferentCount.mSuspectMessages[i]); REPORT(nsPrintfCString("message-manager-suspect/%s/referent(message=%s)", aManagerType, suspect.get()), totalReferentCount, nsPrintfCString("A message in the %s message manager with a " "suspiciously large number of referents (symptom " "of a leak).", aManagerType)); } #undef REPORT @@ -1706,22 +1772,32 @@ NS_NewChildProcessMessageManager(nsISync } nsFrameMessageManager* mm = new nsFrameMessageManager(cb, nullptr, MM_PROCESSMANAGER | MM_OWNSCALLBACK); nsFrameMessageManager::sChildProcessManager = mm; return CallQueryInterface(mm, aResult); } +static PLDHashOperator +CycleCollectorMarkListeners(const nsAString& aKey, + nsTArray<nsMessageListenerInfo>* aListeners, + void* aData) +{ + uint32_t count = aListeners->Length(); + for (uint32_t i = 0; i < count; i++) { + if ((*aListeners)[i].mStrongListener) { + xpc_TryUnmarkWrappedGrayObject((*aListeners)[i].mStrongListener); + } + } + return PL_DHASH_NEXT; +} + bool nsFrameMessageManager::MarkForCC() { - uint32_t len = mListeners.Length(); - for (uint32_t i = 0; i < len; ++i) { - if (mListeners[i].mStrongListener) { - xpc_TryUnmarkWrappedGrayObject(mListeners[i].mStrongListener); - } - } + mListeners.EnumerateRead(CycleCollectorMarkListeners, nullptr); + if (mRefCnt.IsPurple()) { mRefCnt.RemovePurple(); } return true; }
--- a/content/base/src/nsFrameMessageManager.h +++ b/content/base/src/nsFrameMessageManager.h @@ -13,16 +13,17 @@ #include "nsCOMArray.h" #include "nsTArray.h" #include "nsIAtom.h" #include "nsCycleCollectionParticipant.h" #include "nsTArray.h" #include "nsIPrincipal.h" #include "nsIXPConnect.h" #include "nsDataHashtable.h" +#include "nsClassHashtable.h" #include "mozilla/Services.h" #include "nsIObserverService.h" #include "nsThreadUtils.h" #include "nsWeakPtr.h" #include "mozilla/Attributes.h" #include "js/RootingAPI.h" namespace mozilla { @@ -111,17 +112,16 @@ StructuredCloneData UnpackClonedMessageD class nsAXPCNativeCallContext; struct nsMessageListenerInfo { // Exactly one of mStrongListener and mWeakListener must be non-null. nsCOMPtr<nsIMessageListener> mStrongListener; nsWeakPtr mWeakListener; - nsCOMPtr<nsIAtom> mMessage; }; class CpowHolder { public: virtual bool ToObject(JSContext* cx, JS::MutableHandleObject objp) = 0; }; @@ -263,17 +263,19 @@ private: const JS::Value& aJSON, const JS::Value& aObjects, JSContext* aCx, uint8_t aArgc, JS::Value* aRetval, bool aIsSync); protected: friend class MMListenerRemover; - nsTArray<nsMessageListenerInfo> mListeners; + // We keep the message listeners as arrays in a hastable indexed by the + // message name. That gives us fast lookups in ReceiveMessage(). + nsClassHashtable<nsStringHashKey, nsTArray<nsMessageListenerInfo>> mListeners; nsCOMArray<nsIContentFrameMessageManager> mChildManagers; bool mChrome; // true if we're in the chrome process bool mGlobal; // true if we're the global frame message manager bool mIsProcessManager; // true if the message manager belongs to the process realm bool mIsBroadcaster; // true if the message manager is a broadcaster bool mOwnsCallback; bool mHandlingMessage; bool mDisconnected;
--- a/content/base/src/nsObjectLoadingContent.cpp +++ b/content/base/src/nsObjectLoadingContent.cpp @@ -3141,27 +3141,25 @@ nsObjectLoadingContent::LegacyCall(JSCon return JS::UndefinedValue(); } obj = thisContent->GetWrapper(); // Now wrap things up into the compartment of "obj" JSAutoCompartment ac(aCx, obj); nsTArray<JS::Value> args(aArguments); JS::AutoArrayRooter rooter(aCx, args.Length(), args.Elements()); - for (JS::Value *arg = args.Elements(), *arg_end = arg + args.Length(); - arg != arg_end; - ++arg) { - if (!JS_WrapValue(aCx, arg)) { + for (size_t i = 0; i < args.Length(); i++) { + if (!JS_WrapValue(aCx, rooter.handleAt(i))) { aRv.Throw(NS_ERROR_UNEXPECTED); return JS::UndefinedValue(); } } JS::Rooted<JS::Value> thisVal(aCx, aThisVal); - if (!JS_WrapValue(aCx, thisVal.address())) { + if (!JS_WrapValue(aCx, &thisVal)) { aRv.Throw(NS_ERROR_UNEXPECTED); return JS::UndefinedValue(); } nsRefPtr<nsNPAPIPluginInstance> pi; nsresult rv = ScriptRequestPluginInstance(aCx, getter_AddRefs(pi)); if (NS_FAILED(rv)) { aRv.Throw(rv); @@ -3184,17 +3182,17 @@ nsObjectLoadingContent::LegacyCall(JSCon } if (!pi_obj) { aRv.Throw(NS_ERROR_NOT_AVAILABLE); return JS::UndefinedValue(); } JS::Rooted<JS::Value> retval(aCx); - bool ok = JS::Call(aCx, thisVal, pi_obj, args.Length(), args.Elements(), + bool ok = JS::Call(aCx, thisVal, pi_obj, args.Length(), rooter.array, &retval); if (!ok) { aRv.Throw(NS_ERROR_FAILURE); return JS::UndefinedValue(); } Telemetry::Accumulate(Telemetry::PLUGIN_CALLED_DIRECTLY, true); return retval;
--- a/content/base/src/nsScriptLoader.cpp +++ b/content/base/src/nsScriptLoader.cpp @@ -42,16 +42,17 @@ #include "nsIChannelPolicy.h" #include "nsChannelPolicy.h" #include "nsCRT.h" #include "nsContentCreatorFunctions.h" #include "mozilla/dom/Element.h" #include "nsCrossSiteListenerProxy.h" #include "nsSandboxFlags.h" #include "nsContentTypeParser.h" +#include "nsINetworkSeer.h" #include "mozilla/CORSMode.h" #include "mozilla/Attributes.h" #ifdef PR_LOGGING static PRLogModuleInfo* gCspPRLog; #endif @@ -324,16 +325,20 @@ nsScriptLoader::StartLoad(nsScriptLoadRe if (httpChannel) { // HTTP content negotation has little value in this context. httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), NS_LITERAL_CSTRING("*/*"), false); httpChannel->SetReferrer(mDocument->GetDocumentURI()); } + nsCOMPtr<nsILoadContext> loadContext(do_QueryInterface(docshell)); + mozilla::net::SeerLearn(aRequest->mURI, mDocument->GetDocumentURI(), + nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, loadContext); + nsCOMPtr<nsIStreamLoader> loader; rv = NS_NewStreamLoader(getter_AddRefs(loader), this); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIStreamListener> listener = loader.get(); if (aRequest->mCORSMode != CORS_NONE) { bool withCredentials = (aRequest->mCORSMode == CORS_USE_CREDENTIALS);
--- a/content/events/src/nsDOMMessageEvent.cpp +++ b/content/events/src/nsDOMMessageEvent.cpp @@ -62,17 +62,17 @@ nsDOMMessageEvent::GetData(JSContext* aC *aData = GetData(aCx, rv); return rv.ErrorCode(); } JS::Value nsDOMMessageEvent::GetData(JSContext* aCx, ErrorResult& aRv) { JS::Rooted<JS::Value> data(aCx, mData); - if (!JS_WrapValue(aCx, data.address())) { + if (!JS_WrapValue(aCx, &data)) { aRv.Throw(NS_ERROR_FAILURE); } return data; } NS_IMETHODIMP nsDOMMessageEvent::GetOrigin(nsAString& aOrigin) {
--- a/content/html/content/src/UndoManager.cpp +++ b/content/html/content/src/UndoManager.cpp @@ -1151,17 +1151,17 @@ UndoManager::DispatchTransactionEvent(JS nsCOMPtr<nsIWritableVariant> transactions = new nsVariant(); // Unwrap the DOMTransactions into jsvals, then convert // to nsIVariant then put into a nsIVariant array. Arrays in XPIDL suck. nsCOMArray<nsIVariant> keepAlive; nsTArray<nsIVariant*> transactionItems; for (uint32_t i = 0; i < items.Length(); i++) { JS::Rooted<JS::Value> txVal(aCx, JS::ObjectValue(*items[i]->Callback())); - if (!JS_WrapValue(aCx, txVal.address())) { + if (!JS_WrapValue(aCx, &txVal)) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return; } nsCOMPtr<nsIVariant> txVariant; nsresult rv = nsContentUtils::XPConnect()->JSToVariant(aCx, txVal, getter_AddRefs(txVariant)); if (NS_SUCCEEDED(rv)) {
--- a/content/media/AudioSegment.cpp +++ b/content/media/AudioSegment.cpp @@ -150,19 +150,22 @@ AudioSegment::WriteTo(uint64_t aID, Audi duration, c.mVolume, outputChannels, buf.Elements()); } } else { // Assumes that a bit pattern of zeroes == 0.0f memset(buf.Elements(), 0, buf.Length()*sizeof(AudioDataValue)); } - aOutput->Write(buf.Elements(), int32_t(duration)); - if(!c.mTimeStamp.IsNull()) - LogLatency(AsyncLatencyLogger::AudioMediaStreamTrack, aID, - (mozilla::TimeStamp::Now() - c.mTimeStamp).ToMilliseconds()); + aOutput->Write(buf.Elements(), int32_t(duration), &(c.mTimeStamp)); + if(!c.mTimeStamp.IsNull()) { + TimeStamp now = TimeStamp::Now(); + // would be more efficient to c.mTimeStamp to ms on create time then pass here + LogTime(AsyncLatencyLogger::AudioMediaStreamTrack, aID, + (now - c.mTimeStamp).ToMilliseconds(), c.mTimeStamp); + } offset += duration; } } aOutput->Start(); } }
--- a/content/media/AudioStream.cpp +++ b/content/media/AudioStream.cpp @@ -138,17 +138,18 @@ static cubeb_stream_type ConvertChannelT } #endif AudioStream::AudioStream() : mInRate(0), mOutRate(0), mChannels(0), mWritten(0), - mAudioClock(MOZ_THIS_IN_INITIALIZER_LIST()) + mAudioClock(MOZ_THIS_IN_INITIALIZER_LIST()), + mReadPoint(0) {} void AudioStream::InitLibrary() { #ifdef PR_LOGGING gAudioStreamLog = PR_NewLogModule("AudioStream"); #endif gAudioPrefsLock = new Mutex("AudioStream::gAudioPrefsLock"); @@ -323,28 +324,29 @@ class BufferedAudioStream : public Audio public: BufferedAudioStream(); ~BufferedAudioStream(); nsresult Init(int32_t aNumChannels, int32_t aRate, const dom::AudioChannelType aAudioChannelType, AudioStream::LatencyRequest aLatencyRequest); void Shutdown(); - nsresult Write(const AudioDataValue* aBuf, uint32_t aFrames); + nsresult Write(const AudioDataValue* aBuf, uint32_t aFrames, TimeStamp *aTime = nullptr); uint32_t Available(); void SetVolume(double aVolume); void Drain(); void Start(); void Pause(); void Resume(); int64_t GetPosition(); int64_t GetPositionInFrames(); int64_t GetPositionInFramesInternal(); int64_t GetLatencyInFrames(); bool IsPaused(); + void GetBufferInsertTime(int64_t &aTimeMs); // This method acquires the monitor and forward the call to the base // class, to prevent a race on |mTimeStretcher|, in // |AudioStream::EnsureTimeStretcherInitialized|. nsresult EnsureTimeStretcherInitialized(); private: static long DataCallback_S(cubeb_stream*, void* aThis, void* aBuffer, long aFrames) { @@ -354,20 +356,19 @@ private: static void StateCallback_S(cubeb_stream*, void* aThis, cubeb_state aState) { static_cast<BufferedAudioStream*>(aThis)->StateCallback(aState); } long DataCallback(void* aBuffer, long aFrames); void StateCallback(cubeb_state aState); - long GetUnprocessed(void* aBuffer, long aFrames); - - long GetTimeStretched(void* aBuffer, long aFrames); - + // aTime is the time in ms the samples were inserted into MediaStreamGraph + long GetUnprocessed(void* aBuffer, long aFrames, int64_t &aTime); + long GetTimeStretched(void* aBuffer, long aFrames, int64_t &aTime); // Shared implementation of underflow adjusted position calculation. // Caller must own the monitor. int64_t GetPositionInFramesUnlocked(); void StartUnlocked(); // The monitor is held to protect all access to member variables. Write() @@ -537,26 +538,26 @@ WriteDumpFile(FILE* aDumpFile, AudioStre fwrite(output, 2, samples, aDumpFile); fflush(aDumpFile); } BufferedAudioStream::BufferedAudioStream() : mMonitor("BufferedAudioStream"), mLostFrames(0), mDumpFile(nullptr), mVolume(1.0), mBytesPerFrame(0), mState(INITIALIZED) { - AsyncLatencyLogger::Get(true)->AddRef(); + // keep a ref in case we shut down later than nsLayoutStatics + mLatencyLog = AsyncLatencyLogger::Get(true); } BufferedAudioStream::~BufferedAudioStream() { Shutdown(); if (mDumpFile) { fclose(mDumpFile); } - AsyncLatencyLogger::Get()->Release(); } nsresult BufferedAudioStream::EnsureTimeStretcherInitialized() { MonitorAutoLock mon(mMonitor); return AudioStream::EnsureTimeStretcherInitialized(); } @@ -567,16 +568,18 @@ BufferedAudioStream::Init(int32_t aNumCh AudioStream::LatencyRequest aLatencyRequest) { cubeb* cubebContext = GetCubebContext(); if (!cubebContext || aNumChannels < 0 || aRate < 0) { return NS_ERROR_FAILURE; } + PR_LOG(gAudioStreamLog, PR_LOG_DEBUG, + ("%s channels: %d, rate: %d", __FUNCTION__, aNumChannels, aRate)); mInRate = mOutRate = aRate; mChannels = aNumChannels; mDumpFile = OpenDumpFile(this); cubeb_stream_params params; params.rate = aRate; params.channels = aNumChannels; @@ -640,29 +643,46 @@ BufferedAudioStream::Shutdown() if (mState == STARTED) { Pause(); } if (mCubebStream) { mCubebStream.reset(); } } +// aTime is the time in ms the samples were inserted into MediaStreamGraph nsresult -BufferedAudioStream::Write(const AudioDataValue* aBuf, uint32_t aFrames) +BufferedAudioStream::Write(const AudioDataValue* aBuf, uint32_t aFrames, TimeStamp *aTime) { MonitorAutoLock mon(mMonitor); if (!mCubebStream || mState == ERRORED) { return NS_ERROR_FAILURE; } NS_ASSERTION(mState == INITIALIZED || mState == STARTED, "Stream write in unexpected state."); const uint8_t* src = reinterpret_cast<const uint8_t*>(aBuf); uint32_t bytesToCopy = FramesToBytes(aFrames); + // XXX this will need to change if we want to enable this on-the-fly! + if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) { + // Record the position and time this data was inserted + int64_t timeMs; + if (aTime && !aTime->IsNull()) { + if (mStartTime.IsNull()) { + AsyncLatencyLogger::Get(true)->GetStartTime(mStartTime); + } + timeMs = (*aTime - mStartTime).ToMilliseconds(); + } else { + timeMs = 0; + } + struct Inserts insert = { timeMs, aFrames}; + mInserts.AppendElement(insert); + } + while (bytesToCopy > 0) { uint32_t available = std::min(bytesToCopy, mBuffer.Available()); NS_ABORT_IF_FALSE(available % mBytesPerFrame == 0, "Must copy complete frames."); mBuffer.AppendElements(src, available); src += available; bytesToCopy -= available; @@ -841,18 +861,35 @@ BufferedAudioStream::GetLatencyInFrames( bool BufferedAudioStream::IsPaused() { MonitorAutoLock mon(mMonitor); return mState == STOPPED; } +void +BufferedAudioStream::GetBufferInsertTime(int64_t &aTimeMs) +{ + if (mInserts.Length() > 0) { + // Find the right block, but don't leave the array empty + while (mInserts.Length() > 1 && mReadPoint >= mInserts[0].mFrames) { + mReadPoint -= mInserts[0].mFrames; + mInserts.RemoveElementAt(0); + } + // offset for amount already read + // XXX Note: could misreport if we couldn't find a block in the right timeframe + aTimeMs = mInserts[0].mTimeMs + ((mReadPoint * 1000) / mOutRate); + } else { + aTimeMs = INT64_MAX; + } +} + long -BufferedAudioStream::GetUnprocessed(void* aBuffer, long aFrames) +BufferedAudioStream::GetUnprocessed(void* aBuffer, long aFrames, int64_t &aTimeMs) { uint8_t* wpos = reinterpret_cast<uint8_t*>(aBuffer); // Flush the timestretcher pipeline, if we were playing using a playback rate // other than 1.0. uint32_t flushedFrames = 0; if (mTimeStretcher && mTimeStretcher->numSamples()) { flushedFrames = mTimeStretcher->receiveSamples(reinterpret_cast<AudioDataValue*>(wpos), aFrames); @@ -862,21 +899,26 @@ BufferedAudioStream::GetUnprocessed(void uint32_t available = std::min(toPopBytes, mBuffer.Length()); void* input[2]; uint32_t input_size[2]; mBuffer.PopElements(available, &input[0], &input_size[0], &input[1], &input_size[1]); memcpy(wpos, input[0], input_size[0]); wpos += input_size[0]; memcpy(wpos, input[1], input_size[1]); + + // First time block now has our first returned sample + mReadPoint += BytesToFrames(available); + GetBufferInsertTime(aTimeMs); + return BytesToFrames(available) + flushedFrames; } long -BufferedAudioStream::GetTimeStretched(void* aBuffer, long aFrames) +BufferedAudioStream::GetTimeStretched(void* aBuffer, long aFrames, int64_t &aTimeMs) { long processedFrames = 0; // We need to call the non-locking version, because we already have the lock. if (AudioStream::EnsureTimeStretcherInitialized() != NS_OK) { return 0; } @@ -891,77 +933,87 @@ BufferedAudioStream::GetTimeStretched(vo void* input[2]; uint32_t input_size[2]; available = std::min(mBuffer.Length(), toPopBytes); if (available != toPopBytes) { lowOnBufferedData = true; } mBuffer.PopElements(available, &input[0], &input_size[0], &input[1], &input_size[1]); + mReadPoint += BytesToFrames(available); for(uint32_t i = 0; i < 2; i++) { mTimeStretcher->putSamples(reinterpret_cast<AudioDataValue*>(input[i]), BytesToFrames(input_size[i])); } } uint32_t receivedFrames = mTimeStretcher->receiveSamples(reinterpret_cast<AudioDataValue*>(wpos), aFrames - processedFrames); wpos += FramesToBytes(receivedFrames); processedFrames += receivedFrames; } while (processedFrames < aFrames && !lowOnBufferedData); + GetBufferInsertTime(aTimeMs); + return processedFrames; } long BufferedAudioStream::DataCallback(void* aBuffer, long aFrames) { MonitorAutoLock mon(mMonitor); uint32_t available = std::min(static_cast<uint32_t>(FramesToBytes(aFrames)), mBuffer.Length()); NS_ABORT_IF_FALSE(available % mBytesPerFrame == 0, "Must copy complete frames"); + AudioDataValue* output = reinterpret_cast<AudioDataValue*>(aBuffer); uint32_t underrunFrames = 0; uint32_t servicedFrames = 0; + int64_t insertTime; if (available) { - AudioDataValue* output = reinterpret_cast<AudioDataValue*>(aBuffer); if (mInRate == mOutRate) { - servicedFrames = GetUnprocessed(output, aFrames); + servicedFrames = GetUnprocessed(output, aFrames, insertTime); } else { - servicedFrames = GetTimeStretched(output, aFrames); + servicedFrames = GetTimeStretched(output, aFrames, insertTime); } float scaled_volume = float(GetVolumeScale() * mVolume); ScaleAudioSamples(output, aFrames * mChannels, scaled_volume); NS_ABORT_IF_FALSE(mBuffer.Length() % mBytesPerFrame == 0, "Must copy complete frames"); // Notify any blocked Write() call that more space is available in mBuffer. mon.NotifyAll(); + } else { + GetBufferInsertTime(insertTime); } underrunFrames = aFrames - servicedFrames; if (mState != DRAINING) { uint8_t* rpos = static_cast<uint8_t*>(aBuffer) + FramesToBytes(aFrames - underrunFrames); memset(rpos, 0, FramesToBytes(underrunFrames)); -#ifdef PR_LOGGING if (underrunFrames) { PR_LOG(gAudioStreamLog, PR_LOG_WARNING, ("AudioStream %p lost %d frames", this, underrunFrames)); } -#endif mLostFrames += underrunFrames; servicedFrames += underrunFrames; } WriteDumpFile(mDumpFile, this, aFrames, aBuffer); - if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) { + // Don't log if we're not interested or if the stream is inactive + if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG) && + insertTime != INT64_MAX && servicedFrames > underrunFrames) { uint32_t latency = UINT32_MAX; if (cubeb_stream_get_latency(mCubebStream, &latency)) { NS_WARNING("Could not get latency from cubeb."); } - mLatencyLog->Log(AsyncLatencyLogger::AudioStream, 0, (mBuffer.Length() * 1000) / mOutRate); - mLatencyLog->Log(AsyncLatencyLogger::Cubeb, 0, (latency * 1000) / mOutRate); + TimeStamp now = TimeStamp::Now(); + + mLatencyLog->Log(AsyncLatencyLogger::AudioStream, reinterpret_cast<uint64_t>(this), + insertTime, now); + mLatencyLog->Log(AsyncLatencyLogger::Cubeb, reinterpret_cast<uint64_t>(mCubebStream.get()), + (latency * 1000) / mOutRate, now); } mAudioClock.UpdateWritePosition(servicedFrames); return servicedFrames; } void BufferedAudioStream::StateCallback(cubeb_state aState)
--- a/content/media/AudioStream.h +++ b/content/media/AudioStream.h @@ -129,18 +129,19 @@ public: LatencyRequest aLatencyRequest) = 0; // Closes the stream. All future use of the stream is an error. virtual void Shutdown() = 0; // Write audio data to the audio hardware. aBuf is an array of AudioDataValues // AudioDataValue of length aFrames*mChannels. If aFrames is larger // than the result of Available(), the write will block until sufficient - // buffer space is available. - virtual nsresult Write(const mozilla::AudioDataValue* aBuf, uint32_t aFrames) = 0; + // buffer space is available. aTime is the time in ms associated with the first sample + // for latency calculations + virtual nsresult Write(const mozilla::AudioDataValue* aBuf, uint32_t aFrames, TimeStamp *aTime = nullptr) = 0; // Return the number of audio frames that can be written without blocking. virtual uint32_t Available() = 0; // Set the current volume of the audio playback. This is a value from // 0 (meaning muted) to 1 (meaning full volume). Thread-safe. virtual void SetVolume(double aVolume) = 0; @@ -198,13 +199,26 @@ protected: // Output rate in Hz (characteristic of the playback rate) int mOutRate; int mChannels; // Number of frames written to the buffers. int64_t mWritten; AudioClock mAudioClock; nsAutoPtr<soundtouch::SoundTouch> mTimeStretcher; nsRefPtr<AsyncLatencyLogger> mLatencyLog; + + // copy of Latency logger's starting time for offset calculations + TimeStamp mStartTime; + // Where in the current mInserts[0] block cubeb has read to + int64_t mReadPoint; + // Keep track of each inserted block of samples and the time it was inserted + // so we can estimate the clock time for a specific sample's insertion (for when + // we send data to cubeb). Blocks are aged out as needed. + struct Inserts { + int64_t mTimeMs; + int64_t mFrames; + }; + nsAutoTArray<Inserts,8> mInserts; }; } // namespace mozilla #endif
--- a/content/media/Latency.cpp +++ b/content/media/Latency.cpp @@ -19,17 +19,28 @@ using namespace mozilla; const char* LatencyLogIndex2Strings[] = { "Audio MediaStreamTrack", "Video MediaStreamTrack", "Cubeb", "AudioStream", - "NetStat" + "NetEQ", + "AudioCapture Base", + "AudioCapture Samples", + "AudioTrackInsertion", + "MediaPipeline Audio Insertion", + "AudioTransmit", + "AudioReceive", + "MediaPipelineAudioPlayout", + "MediaStream Create", + "AudioStream Create", + "AudioSendRTP", + "AudioRecvRTP" }; static StaticRefPtr<AsyncLatencyLogger> gAsyncLogger; PRLogModuleInfo* GetLatencyLog() { static PRLogModuleInfo* sLog; @@ -38,39 +49,72 @@ GetLatencyLog() } return sLog; } class LogEvent : public nsRunnable { public: + LogEvent(AsyncLatencyLogger::LatencyLogIndex aIndex, uint64_t aID, int64_t aValue, + TimeStamp aTimeStamp) : + mIndex(aIndex), + mID(aID), + mValue(aValue), + mTimeStamp(aTimeStamp) + {} LogEvent(AsyncLatencyLogger::LatencyLogIndex aIndex, uint64_t aID, int64_t aValue) : mIndex(aIndex), mID(aID), - mValue(aValue) + mValue(aValue), + mTimeStamp(TimeStamp()) {} ~LogEvent() {} NS_IMETHOD Run() { - AsyncLatencyLogger::Get(true)->WriteLog(mIndex, mID, mValue); + AsyncLatencyLogger::Get(true)->WriteLog(mIndex, mID, mValue, mTimeStamp); return NS_OK; } protected: AsyncLatencyLogger::LatencyLogIndex mIndex; uint64_t mID; int64_t mValue; + TimeStamp mTimeStamp; }; void LogLatency(AsyncLatencyLogger::LatencyLogIndex aIndex, uint64_t aID, int64_t aValue) { AsyncLatencyLogger::Get()->Log(aIndex, aID, aValue); } +void LogTime(AsyncLatencyLogger::LatencyLogIndex aIndex, uint64_t aID, int64_t aValue) +{ + TimeStamp now = TimeStamp::Now(); + AsyncLatencyLogger::Get()->Log(aIndex, aID, aValue, now); +} + +void LogTime(AsyncLatencyLogger::LatencyLogIndex aIndex, uint64_t aID, int64_t aValue, TimeStamp &aTime) +{ + AsyncLatencyLogger::Get()->Log(aIndex, aID, aValue, aTime); +} + +void LogTime(uint32_t aIndex, uint64_t aID, int64_t aValue) +{ + LogTime(static_cast<AsyncLatencyLogger::LatencyLogIndex>(aIndex), aID, aValue); +} +void LogTime(uint32_t aIndex, uint64_t aID, int64_t aValue, TimeStamp &aTime) +{ + LogTime(static_cast<AsyncLatencyLogger::LatencyLogIndex>(aIndex), aID, aValue, aTime); +} +void LogLatency(uint32_t aIndex, uint64_t aID, int64_t aValue) +{ + LogLatency(static_cast<AsyncLatencyLogger::LatencyLogIndex>(aIndex), aID, aValue); +} + /* static */ void AsyncLatencyLogger::InitializeStatics() { NS_ASSERTION(NS_IsMainThread(), "Main thread only"); GetLatencyLog(); gAsyncLogger = new AsyncLatencyLogger(); } @@ -123,42 +167,62 @@ void AsyncLatencyLogger::Init() MutexAutoLock lock(mMutex); if (mStart.IsNull()) { nsresult rv = NS_NewNamedThread("Latency Logger", getter_AddRefs(mThread)); NS_ENSURE_SUCCESS_VOID(rv); mStart = TimeStamp::Now(); } } +void AsyncLatencyLogger::GetStartTime(TimeStamp &aStart) +{ + MutexAutoLock lock(mMutex); + aStart = mStart; +} + nsresult AsyncLatencyLogger::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData) { MOZ_ASSERT(NS_IsMainThread()); if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { Shutdown(); } return NS_OK; } // aID is a sub-identifier (in particular a specific MediaStramTrack) -void AsyncLatencyLogger::WriteLog(LatencyLogIndex aIndex, uint64_t aID, int64_t aValue) +void AsyncLatencyLogger::WriteLog(LatencyLogIndex aIndex, uint64_t aID, int64_t aValue, + TimeStamp aTimeStamp) { - PR_LOG(GetLatencyLog(), PR_LOG_DEBUG, - ("%s,%llu,%lld.,%lld.", - LatencyLogIndex2Strings[aIndex], aID, GetTimeStamp(), aValue)); + if (aTimeStamp.IsNull()) { + PR_LOG(GetLatencyLog(), PR_LOG_DEBUG, + ("Latency: %s,%llu,%lld,%lld", + LatencyLogIndex2Strings[aIndex], aID, GetTimeStamp(), aValue)); + } else { + PR_LOG(GetLatencyLog(), PR_LOG_DEBUG, + ("Latency: %s,%llu,%lld,%lld,%lld", + LatencyLogIndex2Strings[aIndex], aID, GetTimeStamp(), aValue, + static_cast<int64_t>((aTimeStamp - gAsyncLogger->mStart).ToMilliseconds()))); + } } int64_t AsyncLatencyLogger::GetTimeStamp() { TimeDuration t = TimeStamp::Now() - mStart; return t.ToMilliseconds(); } void AsyncLatencyLogger::Log(LatencyLogIndex aIndex, uint64_t aID, int64_t aValue) { + TimeStamp null; + Log(aIndex, aID, aValue, null); +} + +void AsyncLatencyLogger::Log(LatencyLogIndex aIndex, uint64_t aID, int64_t aValue, TimeStamp &aTime) +{ if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) { - nsCOMPtr<nsIRunnable> event = new LogEvent(aIndex, aID, aValue); + nsCOMPtr<nsIRunnable> event = new LogEvent(aIndex, aID, aValue, aTime); if (mThread) { mThread->Dispatch(event, NS_DISPATCH_NORMAL); } } }
--- a/content/media/Latency.h +++ b/content/media/Latency.h @@ -3,17 +3,17 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef MOZILLA_LATENCY_H #define MOZILLA_LATENCY_H #include "mozilla/TimeStamp.h" -#include "prlog.h" +#include "nspr/prlog.h" #include "nsCOMPtr.h" #include "nsIThread.h" #include "mozilla/Monitor.h" #include "nsISupportsImpl.h" #include "nsObserverService.h" class AsyncLatencyLogger; class LogEvent; @@ -24,25 +24,44 @@ PRLogModuleInfo* GetLatencyLog(); class AsyncLatencyLogger : public nsIObserver { NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIOBSERVER public: enum LatencyLogIndex { - AudioMediaStreamTrack, + AudioMediaStreamTrack = 0, VideoMediaStreamTrack, Cubeb, AudioStream, NetEQ, + AudioCaptureBase, // base time for capturing an audio stream + AudioCapture, // records number of samples captured and the time + AudioTrackInsertion, // # of samples inserted into a mediastreamtrack and the time + MediaPipelineAudioInsertion, // Timestamp and time of timestamp + AudioTransmit, // Timestamp and socket send time + AudioReceive, // Timestamp and receive time + MediaPipelineAudioPlayout, // Timestamp and playout into MST time + MediaStreamCreate, // Source and TrackUnion streams + AudioStreamCreate, // TrackUnion stream and AudioStream + AudioSendRTP, + AudioRecvRTP, _MAX_INDEX }; - void Log(LatencyLogIndex index, uint64_t aID, int64_t value); - void WriteLog(LatencyLogIndex index, uint64_t aID, int64_t value); + // Log with a null timestamp + void Log(LatencyLogIndex index, uint64_t aID, int64_t aValue); + // Log with a timestamp + void Log(LatencyLogIndex index, uint64_t aID, int64_t aValue, + mozilla::TimeStamp &aTime); + // Write a log message to NSPR + void WriteLog(LatencyLogIndex index, uint64_t aID, int64_t aValue, + mozilla::TimeStamp timestamp); + // Get the base time used by the logger for delta calculations + void GetStartTime(mozilla::TimeStamp &aStart); static AsyncLatencyLogger* Get(bool aStartTimer = false); static void InitializeStatics(); // After this is called, the global log object may go away static void ShutdownLogger(); private: AsyncLatencyLogger(); virtual ~AsyncLatencyLogger(); @@ -58,11 +77,24 @@ private: // thread only. mozilla::TimeStamp mStart; // This monitor protects mStart and mMediaLatencyLog for the // initialization sequence. It is initialized at layout startup, and // destroyed at layout shutdown. mozilla::Mutex mMutex; }; -void LogLatency(AsyncLatencyLogger::LatencyLogIndex index, uint64_t aID, int64_t value); +// need uint32_t versions for access from webrtc/trunk code +// Log without a time delta +void LogLatency(AsyncLatencyLogger::LatencyLogIndex index, uint64_t aID, int64_t aValue); +void LogLatency(uint32_t index, uint64_t aID, int64_t aValue); +// Log TimeStamp::Now() (as delta) +void LogTime(AsyncLatencyLogger::LatencyLogIndex index, uint64_t aID, int64_t aValue); +void LogTime(uint32_t index, uint64_t aID, int64_t aValue); +// Log the specified time (as delta) +void LogTime(AsyncLatencyLogger::LatencyLogIndex index, uint64_t aID, int64_t aValue, + mozilla::TimeStamp &aTime); + +// For generating unique-ish ids for logged sources +#define LATENCY_STREAM_ID(source, trackID) \ + ((((uint64_t) (source)) & ~0x0F) | (trackID)) #endif
--- a/content/media/MediaSegment.h +++ b/content/media/MediaSegment.h @@ -228,16 +228,23 @@ public: MediaSegmentBase<C, Chunk>& mSegment; uint32_t mIndex; }; void RemoveLeading(TrackTicks aDuration) { RemoveLeading(aDuration, 0); } + +#ifdef MOZILLA_INTERNAL_API + void GetStartTime(TimeStamp &aTime) { + aTime = mChunks[0].mTimeStamp; + } +#endif + protected: MediaSegmentBase(Type aType) : MediaSegment(aType) {} /** * Appends the contents of aSource to this segment, clearing aSource. */ void AppendFromInternal(MediaSegmentBase<C, Chunk>* aSource) {
--- a/content/media/MediaStreamGraph.cpp +++ b/content/media/MediaStreamGraph.cpp @@ -767,16 +767,20 @@ MediaStreamGraphImpl::CreateOrDestroyAud aStream->mAudioOutputStreams.AppendElement(); audioOutputStream->mAudioPlaybackStartTime = aAudioOutputStartTime; audioOutputStream->mBlockedAudioTime = 0; audioOutputStream->mStream = AudioStream::AllocateStream(); // XXX for now, allocate stereo output. But we need to fix this to // match the system's ideal channel configuration. audioOutputStream->mStream->Init(2, tracks->GetRate(), AUDIO_CHANNEL_NORMAL, AudioStream::LowLatency); audioOutputStream->mTrackID = tracks->GetID(); + + LogLatency(AsyncLatencyLogger::AudioStreamCreate, + reinterpret_cast<uint64_t>(aStream), + reinterpret_cast<int64_t>(audioOutputStream->mStream.get())); } } } for (int32_t i = audioOutputStreamsFound.Length() - 1; i >= 0; --i) { if (!audioOutputStreamsFound[i]) { aStream->mAudioOutputStreams[i].mStream->Shutdown(); aStream->mAudioOutputStreams.RemoveElementAt(i); @@ -849,18 +853,19 @@ MediaStreamGraphImpl::PlayAudio(MediaStr output.AppendNullData(endTicks - sliceEnd); NS_ASSERTION(endTicks == sliceEnd || track->IsEnded(), "Ran out of data but track not ended?"); output.ApplyVolume(volume); LOG(PR_LOG_DEBUG+1, ("MediaStream %p writing samples for %f to %f (samples %lld to %lld)", aStream, MediaTimeToSeconds(t), MediaTimeToSeconds(end), startTicks, endTicks)); } - // XXX need unique id for stream & track - output.WriteTo((((uint64_t)i) << 32) | track->GetID(), audioOutput.mStream); + // Need unique id for stream & track - and we want it to match the inserter + output.WriteTo(LATENCY_STREAM_ID(aStream, track->GetID()), + audioOutput.mStream); t = end; } } } static void SetImageToBlackPixel(PlanarYCbCrImage* aImage) {
--- a/content/media/webrtc/MediaEngineWebRTCAudio.cpp +++ b/content/media/webrtc/MediaEngineWebRTCAudio.cpp @@ -151,23 +151,27 @@ MediaEngineWebRTCAudioSource::Start(Sour { MonitorAutoLock lock(mMonitor); mSources.AppendElement(aStream); } AudioSegment* segment = new AudioSegment(); aStream->AddTrack(aID, SAMPLE_FREQUENCY, 0, segment); aStream->AdvanceKnownTracksTime(STREAM_TIME_MAX); - LOG(("Initial audio")); - mTrackID = aID; + LOG(("Start audio for stream %p", aStream)); if (mState == kStarted) { + MOZ_ASSERT(aID == mTrackID); return NS_OK; } mState = kStarted; + mTrackID = aID; + + // Make sure logger starts before capture + AsyncLatencyLogger::Get(true); // Configure audio processing in webrtc code Config(mEchoOn, webrtc::kEcUnchanged, mAgcOn, webrtc::kAgcUnchanged, mNoiseOn, webrtc::kNsUnchanged); if (mVoEBase->StartReceive(mChannel)) { return NS_ERROR_FAILURE; @@ -365,21 +369,28 @@ MediaEngineWebRTCAudioSource::Process(in sample* dest = static_cast<sample*>(buffer->Data()); memcpy(dest, audio10ms, length * sizeof(sample)); AudioSegment segment; nsAutoTArray<const sample*,1> channels; channels.AppendElement(dest); segment.AppendFrames(buffer.forget(), channels, length); + TimeStamp insertTime; + segment.GetStartTime(insertTime); SourceMediaStream *source = mSources[i]; if (source) { // This is safe from any thread, and is safe if the track is Finished - // or Destroyed + // or Destroyed. + // Make sure we include the stream and the track. + // The 0:1 is a flag to note when we've done the final insert for a given input block. + LogTime(AsyncLatencyLogger::AudioTrackInsertion, LATENCY_STREAM_ID(source, mTrackID), + (i+1 < len) ? 0 : 1, insertTime); + source->AppendToTrack(mTrackID, &segment); } } return; } }
--- a/content/media/wmf/WMF.h +++ b/content/media/wmf/WMF.h @@ -26,16 +26,23 @@ which makes Windows Media Foundation una #include <mfobjects.h> #include <stdio.h> #include <mferror.h> #include <propvarutil.h> #include <wmcodecdsp.h> #include <initguid.h> #include <d3d9.h> #include <dxva2api.h> +#include <wmcodecdsp.h> + +// Some SDK versions don't define the AAC decoder CLSID. +#ifndef CLSID_CMSAACDecMFT +extern const CLSID CLSID_CMSAACDecMFT; +#define WMF_MUST_DEFINE_AAC_MFT_CLSID +#endif namespace mozilla { namespace wmf { // Loads/Unloads all the DLLs in which the WMF functions are located. // The DLLs must be loaded before any of the WMF functions below will work. // All the function definitions below are wrappers which locate the // corresponding WMF function in the appropriate DLL (hence why LoadDLL() @@ -99,16 +106,23 @@ HRESULT MFTEnumEx(GUID guidCategory, HRESULT MFGetService(IUnknown *punkObject, REFGUID guidService, REFIID riid, LPVOID *ppvObject); HRESULT DXVA2CreateDirect3DDeviceManager9(UINT *pResetToken, IDirect3DDeviceManager9 **ppDXVAManager); + +HRESULT MFCreateSample(IMFSample **ppIMFSample); + +HRESULT MFCreateAlignedMemoryBuffer(DWORD cbMaxLength, + DWORD fAlignmentFlags, + IMFMediaBuffer **ppBuffer); + } // end namespace wmf } // end namespace mozilla #pragma pop_macro("WINVER") #endif
--- a/content/media/wmf/WMFReader.cpp +++ b/content/media/wmf/WMFReader.cpp @@ -279,103 +279,16 @@ GetSourceReaderCanSeek(IMFSourceReader* hr = wmf::PropVariantToUInt32(var, &flags); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); aOutCanSeek = ((flags & MFMEDIASOURCE_CAN_SEEK) == MFMEDIASOURCE_CAN_SEEK); return S_OK; } -static HRESULT -GetDefaultStride(IMFMediaType *aType, uint32_t* aOutStride) -{ - // Try to get the default stride from the media type. - HRESULT hr = aType->GetUINT32(MF_MT_DEFAULT_STRIDE, aOutStride); - if (SUCCEEDED(hr)) { - return S_OK; - } - - // Stride attribute not set, calculate it. - GUID subtype = GUID_NULL; - uint32_t width = 0; - uint32_t height = 0; - - hr = aType->GetGUID(MF_MT_SUBTYPE, &subtype); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - - hr = MFGetAttributeSize(aType, MF_MT_FRAME_SIZE, &width, &height); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - - hr = wmf::MFGetStrideForBitmapInfoHeader(subtype.Data1, width, (LONG*)(aOutStride)); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - - return hr; -} - -static int32_t -MFOffsetToInt32(const MFOffset& aOffset) -{ - return int32_t(aOffset.value + (aOffset.fract / 65536.0f)); -} - -// Gets the sub-region of the video frame that should be displayed. -// See: http://msdn.microsoft.com/en-us/library/windows/desktop/bb530115(v=vs.85).aspx -static HRESULT -GetPictureRegion(IMFMediaType* aMediaType, nsIntRect& aOutPictureRegion) -{ - // Determine if "pan and scan" is enabled for this media. If it is, we - // only display a region of the video frame, not the entire frame. - BOOL panScan = MFGetAttributeUINT32(aMediaType, MF_MT_PAN_SCAN_ENABLED, FALSE); - - // If pan and scan mode is enabled. Try to get the display region. - HRESULT hr = E_FAIL; - MFVideoArea videoArea; - memset(&videoArea, 0, sizeof(MFVideoArea)); - if (panScan) { - hr = aMediaType->GetBlob(MF_MT_PAN_SCAN_APERTURE, - (UINT8*)&videoArea, - sizeof(MFVideoArea), - nullptr); - } - - // If we're not in pan-and-scan mode, or the pan-and-scan region is not set, - // check for a minimimum display aperture. - if (!panScan || hr == MF_E_ATTRIBUTENOTFOUND) { - hr = aMediaType->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE, - (UINT8*)&videoArea, - sizeof(MFVideoArea), - nullptr); - } - - if (hr == MF_E_ATTRIBUTENOTFOUND) { - // Minimum display aperture is not set, for "backward compatibility with - // some components", check for a geometric aperture. - hr = aMediaType->GetBlob(MF_MT_GEOMETRIC_APERTURE, - (UINT8*)&videoArea, - sizeof(MFVideoArea), - nullptr); - } - - if (SUCCEEDED(hr)) { - // The media specified a picture region, return it. - aOutPictureRegion = nsIntRect(MFOffsetToInt32(videoArea.OffsetX), - MFOffsetToInt32(videoArea.OffsetY), - videoArea.Area.cx, - videoArea.Area.cy); - return S_OK; - } - - // No picture region defined, fall back to using the entire video area. - UINT32 width = 0, height = 0; - hr = MFGetAttributeSize(aMediaType, MF_MT_FRAME_SIZE, &width, &height); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - aOutPictureRegion = nsIntRect(0, 0, width, height); - return S_OK; -} - HRESULT WMFReader::ConfigureVideoFrameGeometry(IMFMediaType* aMediaType) { NS_ENSURE_TRUE(aMediaType != nullptr, E_POINTER); HRESULT hr; // Verify that the video subtype is what we expect it to be. // When using hardware acceleration/DXVA2 the video format should @@ -634,49 +547,16 @@ WMFReader::ReadMetadata(MediaInfo* aInfo *aInfo = mInfo; *aTags = nullptr; // aTags can be retrieved using techniques like used here: // http://blogs.msdn.com/b/mf/archive/2010/01/12/mfmediapropdump.aspx return NS_OK; } -static int64_t -GetSampleDuration(IMFSample* aSample) -{ - int64_t duration = 0; - aSample->GetSampleDuration(&duration); - return HNsToUsecs(duration); -} - -HRESULT -HNsToFrames(int64_t aHNs, uint32_t aRate, int64_t* aOutFrames) -{ - MOZ_ASSERT(aOutFrames); - const int64_t HNS_PER_S = USECS_PER_S * 10; - CheckedInt<int64_t> i = aHNs; - i *= aRate; - i /= HNS_PER_S; - NS_ENSURE_TRUE(i.isValid(), E_FAIL); - *aOutFrames = i.value(); - return S_OK; -} - -HRESULT -FramesToUsecs(int64_t aSamples, uint32_t aRate, int64_t* aOutUsecs) -{ - MOZ_ASSERT(aOutUsecs); - CheckedInt<int64_t> i = aSamples; - i *= USECS_PER_S; - i /= aRate; - NS_ENSURE_TRUE(i.isValid(), E_FAIL); - *aOutUsecs = i.value(); - return S_OK; -} - bool WMFReader::DecodeAudioData() { NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread."); HRESULT hr; hr = mSourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, // control flags
--- a/content/media/wmf/WMFUtils.cpp +++ b/content/media/wmf/WMFUtils.cpp @@ -6,19 +6,27 @@ #include "WMFUtils.h" #include <stdint.h> #include "mozilla/RefPtr.h" #include "prlog.h" #include "nsThreadUtils.h" #include "WinUtils.h" #include "nsWindowsHelpers.h" +#include "mozilla/CheckedInt.h" +#include "VideoUtils.h" using namespace mozilla::widget; +#ifdef WMF_MUST_DEFINE_AAC_MFT_CLSID +// Some SDK versions don't define the AAC decoder CLSID. +// {32D186A7-218F-4C75-8876-DD77273A8999} +DEFINE_GUID(CLSID_CMSAACDecMFT, 0x32D186A7, 0x218F, 0x4C75, 0x88, 0x76, 0xDD, 0x77, 0x27, 0x3A, 0x89, 0x99); +#endif + namespace mozilla { struct GuidToName { GUID guid; const char* name; }; #define GUID_TO_NAME_ENTRY(g) { g, #g } @@ -208,23 +216,148 @@ DoGetInterface(IUnknown* aUnknown, void* { if (!aInterface) return E_POINTER; *aInterface = aUnknown; aUnknown->AddRef(); return S_OK; } -namespace wmf { +HRESULT +HNsToFrames(int64_t aHNs, uint32_t aRate, int64_t* aOutFrames) +{ + MOZ_ASSERT(aOutFrames); + const int64_t HNS_PER_S = USECS_PER_S * 10; + CheckedInt<int64_t> i = aHNs; + i *= aRate; + i /= HNS_PER_S; + NS_ENSURE_TRUE(i.isValid(), E_FAIL); + *aOutFrames = i.value(); + return S_OK; +} + +HRESULT +FramesToUsecs(int64_t aSamples, uint32_t aRate, int64_t* aOutUsecs) +{ + MOZ_ASSERT(aOutUsecs); + CheckedInt<int64_t> i = aSamples; + i *= USECS_PER_S; + i /= aRate; + NS_ENSURE_TRUE(i.isValid(), E_FAIL); + *aOutUsecs = i.value(); + return S_OK; +} + +HRESULT +GetDefaultStride(IMFMediaType *aType, uint32_t* aOutStride) +{ + // Try to get the default stride from the media type. + HRESULT hr = aType->GetUINT32(MF_MT_DEFAULT_STRIDE, aOutStride); + if (SUCCEEDED(hr)) { + return S_OK; + } + + // Stride attribute not set, calculate it. + GUID subtype = GUID_NULL; + uint32_t width = 0; + uint32_t height = 0; + + hr = aType->GetGUID(MF_MT_SUBTYPE, &subtype); + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); + + hr = MFGetAttributeSize(aType, MF_MT_FRAME_SIZE, &width, &height); + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); + + hr = wmf::MFGetStrideForBitmapInfoHeader(subtype.Data1, width, (LONG*)(aOutStride)); + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); + + return hr; +} + +int32_t +MFOffsetToInt32(const MFOffset& aOffset) +{ + return int32_t(aOffset.value + (aOffset.fract / 65536.0f)); +} + +int64_t +GetSampleDuration(IMFSample* aSample) +{ + NS_ENSURE_TRUE(aSample, -1); + int64_t duration = 0; + aSample->GetSampleDuration(&duration); + return HNsToUsecs(duration); +} -// Some SDK versions don't define the AAC decoder CLSID. -#ifndef CLSID_CMSAACDecMFT -// {32D186A7-218F-4C75-8876-DD77273A8999} -DEFINE_GUID(CLSID_CMSAACDecMFT, 0x32D186A7, 0x218F, 0x4C75, 0x88, 0x76, 0xDD, 0x77, 0x27, 0x3A, 0x89, 0x99); -#endif +int64_t +GetSampleTime(IMFSample* aSample) +{ + NS_ENSURE_TRUE(aSample, -1); + LONGLONG timestampHns = 0; + HRESULT hr = aSample->GetSampleTime(×tampHns); + NS_ENSURE_TRUE(SUCCEEDED(hr), -1); + return HNsToUsecs(timestampHns); +} + +// Gets the sub-region of the video frame that should be displayed. +// See: http://msdn.microsoft.com/en-us/library/windows/desktop/bb530115(v=vs.85).aspx +HRESULT +GetPictureRegion(IMFMediaType* aMediaType, nsIntRect& aOutPictureRegion) +{ + // Determine if "pan and scan" is enabled for this media. If it is, we + // only display a region of the video frame, not the entire frame. + BOOL panScan = MFGetAttributeUINT32(aMediaType, MF_MT_PAN_SCAN_ENABLED, FALSE); + + // If pan and scan mode is enabled. Try to get the display region. + HRESULT hr = E_FAIL; + MFVideoArea videoArea; + memset(&videoArea, 0, sizeof(MFVideoArea)); + if (panScan) { + hr = aMediaType->GetBlob(MF_MT_PAN_SCAN_APERTURE, + (UINT8*)&videoArea, + sizeof(MFVideoArea), + nullptr); + } + + // If we're not in pan-and-scan mode, or the pan-and-scan region is not set, + // check for a minimimum display aperture. + if (!panScan || hr == MF_E_ATTRIBUTENOTFOUND) { + hr = aMediaType->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE, + (UINT8*)&videoArea, + sizeof(MFVideoArea), + nullptr); + } + + if (hr == MF_E_ATTRIBUTENOTFOUND) { + // Minimum display aperture is not set, for "backward compatibility with + // some components", check for a geometric aperture. + hr = aMediaType->GetBlob(MF_MT_GEOMETRIC_APERTURE, + (UINT8*)&videoArea, + sizeof(MFVideoArea), + nullptr); + } + + if (SUCCEEDED(hr)) { + // The media specified a picture region, return it. + aOutPictureRegion = nsIntRect(MFOffsetToInt32(videoArea.OffsetX), + MFOffsetToInt32(videoArea.OffsetY), + videoArea.Area.cx, + videoArea.Area.cy); + return S_OK; + } + + // No picture region defined, fall back to using the entire video area. + UINT32 width = 0, height = 0; + hr = MFGetAttributeSize(aMediaType, MF_MT_FRAME_SIZE, &width, &height); + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); + aOutPictureRegion = nsIntRect(0, 0, width, height); + return S_OK; +} + +namespace wmf { static bool IsSupportedDecoder(const GUID& aDecoderGUID) { return aDecoderGUID == CLSID_CMSH264DecoderMFT || aDecoderGUID == CLSID_CMSAACDecMFT || aDecoderGUID == CLSID_CMP3DecMediaObject; } @@ -528,10 +661,28 @@ HRESULT DXVA2CreateDirect3DDeviceManager9(UINT *pResetToken, IDirect3DDeviceManager9 **ppDXVAManager) { DECL_FUNCTION_PTR(DXVA2CreateDirect3DDeviceManager9, UINT*, IDirect3DDeviceManager9 **); ENSURE_FUNCTION_PTR(DXVA2CreateDirect3DDeviceManager9, dxva2.dll) return (DXVA2CreateDirect3DDeviceManager9Ptr)(pResetToken, ppDXVAManager); } +HRESULT +MFCreateSample(IMFSample **ppIMFSample) +{ + DECL_FUNCTION_PTR(MFCreateSample, IMFSample **); + ENSURE_FUNCTION_PTR(MFCreateSample, mfplat.dll) + return (MFCreateSamplePtr)(ppIMFSample); +} + +HRESULT +MFCreateAlignedMemoryBuffer(DWORD cbMaxLength, + DWORD fAlignmentFlags, + IMFMediaBuffer **ppBuffer) +{ + DECL_FUNCTION_PTR(MFCreateAlignedMemoryBuffer, DWORD, DWORD, IMFMediaBuffer**); + ENSURE_FUNCTION_PTR(MFCreateAlignedMemoryBuffer, mfplat.dll) + return (MFCreateAlignedMemoryBufferPtr)(cbMaxLength, fAlignmentFlags, ppBuffer); +} + } // end namespace wmf } // end namespace mozilla
--- a/content/media/wmf/WMFUtils.h +++ b/content/media/wmf/WMFUtils.h @@ -1,16 +1,18 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "WMF.h" #include "nsString.h" +#include "nsRect.h" +#include "VideoUtils.h" // Various utilities shared by WMF backend files. namespace mozilla { nsCString GetGUIDName(const GUID& guid); @@ -59,9 +61,36 @@ HNsToUsecs(int64_t hNanoSecs) { return hNanoSecs / 10; } // Assigns aUnknown to *aInterface, and AddRef's it. // Helper for MSCOM QueryInterface implementations. HRESULT DoGetInterface(IUnknown* aUnknown, void** aInterface); +HRESULT +HNsToFrames(int64_t aHNs, uint32_t aRate, int64_t* aOutFrames); + +HRESULT +FramesToUsecs(int64_t aSamples, uint32_t aRate, int64_t* aOutUsecs); + +HRESULT +GetDefaultStride(IMFMediaType *aType, uint32_t* aOutStride); + +int32_t +MFOffsetToInt32(const MFOffset& aOffset); + +// Gets the sub-region of the video frame that should be displayed. +// See: http://msdn.microsoft.com/en-us/library/windows/desktop/bb530115(v=vs.85).aspx +HRESULT +GetPictureRegion(IMFMediaType* aMediaType, nsIntRect& aOutPictureRegion); + +// Returns the duration of a IMFSample in microseconds. +// Returns -1 on failure. +int64_t +GetSampleDuration(IMFSample* aSample); + +// Returns the presentation time of a IMFSample in microseconds. +// Returns -1 on failure. +int64_t +GetSampleTime(IMFSample* aSample); + } // namespace mozilla
--- a/content/media/wmf/moz.build +++ b/content/media/wmf/moz.build @@ -5,16 +5,17 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. MODULE = 'content' EXPORTS += [ 'WMF.h', 'WMFDecoder.h', 'WMFReader.h', + 'WMFUtils.h', ] SOURCES += [ 'DXVA2Manager.cpp', 'WMFByteStream.cpp', 'WMFDecoder.cpp', 'WMFReader.cpp', 'WMFSourceReaderCallback.cpp',
--- a/content/xbl/src/nsXBLProtoImplField.cpp +++ b/content/xbl/src/nsXBLProtoImplField.cpp @@ -340,17 +340,17 @@ nsXBLProtoImplField::InstallAccessors(JS return NS_OK; // FieldGetter and FieldSetter need to run in the XBL scope so that they can // see through any SOWs on their targets. // First, enter the XBL scope, and compile the functions there. JSAutoCompartment ac(aCx, scopeObject); JS::Rooted<JS::Value> wrappedClassObj(aCx, JS::ObjectValue(*aTargetClassObject)); - if (!JS_WrapValue(aCx, wrappedClassObj.address()) || !JS_WrapId(aCx, id.address())) + if (!JS_WrapValue(aCx, &wrappedClassObj) || !JS_WrapId(aCx, id.address())) return NS_ERROR_OUT_OF_MEMORY; JS::Rooted<JSObject*> get(aCx, JS_GetFunctionObject(js::NewFunctionByIdWithReserved(aCx, FieldGetter, 0, 0, scopeObject, id))); if (!get) { return NS_ERROR_OUT_OF_MEMORY; } @@ -440,19 +440,19 @@ nsXBLProtoImplField::InstallField(nsIScr return rv; } // Now, enter the node's compartment, wrap the eval result, and define it on // the bound node. JSAutoCompartment ac2(cx, aBoundNode); nsDependentString name(mName); - if (!JS_WrapValue(cx, result.address()) || + if (!JS_WrapValue(cx, &result) || !::JS_DefineUCProperty(cx, aBoundNode, - reinterpret_cast<const jschar*>(mName), + reinterpret_cast<const jschar*>(mName), name.Length(), result, nullptr, nullptr, mJSAttributes)) { return NS_ERROR_OUT_OF_MEMORY; } *aDidInstall = true; return NS_OK; }
--- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -111,16 +111,17 @@ #include "nsITabChild.h" #include "nsISiteSecurityService.h" #include "nsStructuredCloneContainer.h" #include "nsIStructuredCloneContainer.h" #ifdef MOZ_PLACES #include "nsIFaviconService.h" #include "mozIAsyncFavicons.h" #endif +#include "nsINetworkSeer.h" // Editor-related #include "nsIEditingSession.h" #include "nsPIDOMWindow.h" #include "nsGlobalWindow.h" #include "nsPIWindowRoot.h" #include "nsICachingChannel.h" @@ -4582,25 +4583,19 @@ nsDocShell::LoadErrorPage(nsIURI *aURI, errorPageUrl.AppendASCII(escapedCSSClass.get()); } errorPageUrl.AppendLiteral("&c="); errorPageUrl.AppendASCII(escapedCharset.get()); errorPageUrl.AppendLiteral("&d="); errorPageUrl.AppendASCII(escapedDescription.get()); // Append the manifest URL if the error comes from an app. - uint32_t appId; - nsresult rv = GetAppId(&appId); - if (appId != nsIScriptSecurityManager::NO_APP_ID && - appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) { - nsCOMPtr<nsIAppsService> appsService = - do_GetService(APPS_SERVICE_CONTRACTID); - NS_ASSERTION(appsService, "No AppsService available"); - nsAutoString manifestURL; - appsService->GetManifestURLByLocalId(appId, manifestURL); + nsString manifestURL; + nsresult rv = GetAppManifestURL(manifestURL); + if (manifestURL.Length() > 0) { nsCString manifestParam; SAFE_ESCAPE(manifestParam, NS_ConvertUTF16toUTF8(manifestURL).get(), url_Path); errorPageUrl.AppendLiteral("&m="); errorPageUrl.AppendASCII(manifestParam.get()); } @@ -7014,19 +7009,22 @@ nsDocShell::EndPageLoad(nsIWebProgress * else if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) { // Non-caching channels will simply return NS_ERROR_OFFLINE. // Caching channels would have to look at their flags to work // out which error to return. Or we can fix up the error here. if (!(mLoadType & LOAD_CMD_HISTORY)) aStatus = NS_ERROR_OFFLINE; DisplayLoadError(aStatus, url, nullptr, aChannel); } - } // if we have a host - - return NS_OK; + } // if we have a host + else if (url && NS_SUCCEEDED(aStatus)) { + mozilla::net::SeerLearnRedirect(url, aChannel, this); + } + + return NS_OK; } //***************************************************************************** // nsDocShell: Content Viewer Management //***************************************************************************** NS_IMETHODIMP @@ -9403,16 +9401,19 @@ nsDocShell::InternalLoad(nsIURI * aURI, } nsAutoString srcdoc; if (aFlags & INTERNAL_LOAD_FLAGS_IS_SRCDOC) srcdoc = aSrcdoc; else srcdoc = NullString(); + mozilla::net::SeerPredict(aURI, nullptr, nsINetworkSeer::PREDICT_LOAD, + this, nullptr); + nsCOMPtr<nsIRequest> req; rv = DoURILoad(aURI, aReferrer, !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER), owner, aTypeHint, aFileName, aPostData, aHeadersData, aFirstParty, aDocShell, getter_AddRefs(req), (aFlags & INTERNAL_LOAD_FLAGS_FIRST_LOAD) != 0, (aFlags & INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER) != 0, (aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES) != 0, @@ -12459,16 +12460,19 @@ nsDocShell::OnOverLink(nsIContent* aCont nsAutoCString spec; rv = aURI->GetSpec(spec); NS_ENSURE_SUCCESS(rv, rv); nsAutoString uStr; rv = textToSubURI->UnEscapeURIForUI(charset, spec, uStr); NS_ENSURE_SUCCESS(rv, rv); + mozilla::net::SeerPredict(aURI, mCurrentURI, nsINetworkSeer::PREDICT_LINK, + this, nullptr); + if (browserChrome2) { nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aContent); rv = browserChrome2->SetStatusWithContext(nsIWebBrowserChrome::STATUS_LINK, uStr, element); } else { rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK, uStr.get()); } return rv; @@ -12727,16 +12731,35 @@ nsDocShell::GetAppId(uint32_t* aAppId) *aAppId = nsIScriptSecurityManager::NO_APP_ID; return NS_OK; } return parent->GetAppId(aAppId); } NS_IMETHODIMP +nsDocShell::GetAppManifestURL(nsAString& aAppManifestURL) +{ + uint32_t appId; + GetAppId(&appId); + + if (appId != nsIScriptSecurityManager::NO_APP_ID && + appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) { + nsCOMPtr<nsIAppsService> appsService = + do_GetService(APPS_SERVICE_CONTRACTID); + NS_ASSERTION(appsService, "No AppsService available"); + appsService->GetManifestURLByLocalId(appId, aAppManifestURL); + } else { + aAppManifestURL.SetLength(0); + } + + return NS_OK; +} + +NS_IMETHODIMP nsDocShell::GetAsyncPanZoomEnabled(bool* aOut) { if (TabChild* tabChild = TabChild::GetFrom(this)) { *aOut = tabChild->IsAsyncPanZoomEnabled(); return NS_OK; } *aOut = false; return NS_OK;
--- a/docshell/base/nsIDocShell.idl +++ b/docshell/base/nsIDocShell.idl @@ -38,17 +38,17 @@ interface nsIDOMStorage; interface nsIPrincipal; interface nsIWebBrowserPrint; interface nsIVariant; interface nsIPrivacyTransitionObserver; interface nsIReflowObserver; typedef unsigned long nsLoadFlags; -[scriptable, builtinclass, uuid(4c8cd9da-4e93-42ed-9901-3781e90458d9)] +[scriptable, builtinclass, uuid(1470A132-99B2-44C3-B37D-D8093B2E29BF)] interface nsIDocShell : nsIDocShellTreeItem { /** * Loads a given URI. This will give priority to loading the requested URI * in the object implementing this interface. If it can't be loaded here * however, the URL dispatcher will go through its normal process of content * loading. * @@ -738,16 +738,23 @@ interface nsIDocShell : nsIDocShellTreeI * return NO_APP_ID. We never return UNKNOWN_APP_ID. * * Notice that a docshell may have an associated app even if it returns true * for isBrowserElement! */ [infallible] readonly attribute unsigned long appId; /** + * Return the manifest URL of the app associated with this docshell. + * + * If there is no associated app in our hierarchy, we return empty string. + */ + readonly attribute DOMString appManifestURL; + + /** * Like nsIDocShellTreeItem::GetSameTypeParent, except this ignores <iframe * mozbrowser> and <iframe mozapp> boundaries. */ nsIDocShell getSameTypeParentIgnoreBrowserAndAppBoundaries(); /** * True iff asynchronous panning and zooming is enabled for this * docshell.
--- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -1586,17 +1586,17 @@ Navigator::DoNewResolve(JSContext* aCx, rv = nsContentUtils::WrapNative(aCx, aObject, native, &prop_val, getter_AddRefs(holder), true); if (NS_FAILED(rv)) { return Throw(aCx, rv); } } - if (!JS_WrapValue(aCx, prop_val.address())) { + if (!JS_WrapValue(aCx, &prop_val)) { return Throw(aCx, NS_ERROR_UNEXPECTED); } aValue.set(prop_val); return true; } static PLDHashOperator
--- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -2224,22 +2224,22 @@ BaseStubConstructor(nsIWeakReference* aW unsigned argc = args.length() + 1; JS::AutoValueVector argv(cx); argv.resize(argc); nsCOMPtr<nsIXPConnectJSObjectHolder> holder; nsCOMPtr<nsIDOMWindow> currentWin(do_GetInterface(currentInner)); rv = WrapNative(cx, obj, currentWin, &NS_GET_IID(nsIDOMWindow), true, argv.handleAt(0), getter_AddRefs(holder)); - if (!JS_WrapValue(cx, &argv[0])) + if (!JS_WrapValue(cx, argv.handleAt(0))) return NS_ERROR_FAILURE; for (size_t i = 1; i < argc; ++i) { argv[i] = args[i - 1]; - if (!JS_WrapValue(cx, &argv[i])) + if (!JS_WrapValue(cx, argv.handleAt(i))) return NS_ERROR_FAILURE; } JS::Rooted<JS::Value> frval(cx); bool ret = JS_CallFunctionValue(cx, thisObject, funval, argc, argv.begin(), frval.address()); @@ -2366,17 +2366,17 @@ public: JS::Handle<JSObject*> obj, const jsval &val, bool *bp, bool *_retval); nsresult Install(JSContext *cx, JS::Handle<JSObject*> target, JS::Handle<JS::Value> aThisAsVal) { JS::Rooted<JS::Value> thisAsVal(cx, aThisAsVal); // The 'attrs' argument used to be JSPROP_PERMANENT. See bug 628612. - bool ok = JS_WrapValue(cx, thisAsVal.address()) && + bool ok = JS_WrapValue(cx, &thisAsVal) && ::JS_DefineUCProperty(cx, target, reinterpret_cast<const jschar *>(mClassName), NS_strlen(mClassName), thisAsVal, JS_PropertyStub, JS_StrictPropertyStub, 0); return ok ? NS_OK : NS_ERROR_UNEXPECTED; } @@ -2940,17 +2940,17 @@ ResolvePrototype(nsIXPConnect *aXPConnec } } v = OBJECT_TO_JSVAL(dot_prototype); JSAutoCompartment ac(cx, class_obj); // Per ECMA, the prototype property is {DontEnum, DontDelete, ReadOnly} - if (!JS_WrapValue(cx, v.address()) || + if (!JS_WrapValue(cx, &v) || !JS_DefineProperty(cx, class_obj, "prototype", v, JS_PropertyStub, JS_StrictPropertyStub, JSPROP_PERMANENT | JSPROP_READONLY)) { return NS_ERROR_UNEXPECTED; } *did_resolve = true; @@ -3252,17 +3252,17 @@ nsWindowSH::GlobalResolve(nsGlobalWindow } rv = WrapNative(cx, scope, native, true, &prop_val, getter_AddRefs(holder)); } NS_ENSURE_SUCCESS(rv, rv); - if (!JS_WrapValue(cx, prop_val.address())) { + if (!JS_WrapValue(cx, &prop_val)) { return NS_ERROR_UNEXPECTED; } bool ok = ::JS_DefinePropertyById(cx, obj, id, prop_val, JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE); *did_resolve = true; @@ -3563,17 +3563,17 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp JS::Rooted<JSObject*> scope(cx, wrapper->GetJSObject()); nsCOMPtr<nsIXPConnectJSObjectHolder> holder; JS::Rooted<JS::Value> v(cx); rv = WrapNative(cx, scope, location, &NS_GET_IID(nsIDOMLocation), true, &v, getter_AddRefs(holder)); NS_ENSURE_SUCCESS(rv, rv); - bool ok = JS_WrapValue(cx, v.address()) && + bool ok = JS_WrapValue(cx, &v) && JS_DefinePropertyById(cx, obj, id, v, JS_PropertyStub, LocationSetterUnwrapper, JSPROP_PERMANENT | JSPROP_ENUMERATE); if (!ok) { return NS_ERROR_FAILURE; } @@ -3687,17 +3687,17 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp // property *objp = obj; // NB: We need to do this for any Xray wrapper. if (xpc::WrapperFactory::IsXrayWrapper(obj)) { // Unless our object is a native wrapper, in which case we have to // define it ourselves. - *_retval = JS_WrapValue(cx, v.address()) && + *_retval = JS_WrapValue(cx, &v) && JS_DefineProperty(cx, obj, "document", v, JS_PropertyStub, JS_StrictPropertyStub, JSPROP_READONLY | JSPROP_ENUMERATE); if (!*_retval) { return NS_ERROR_UNEXPECTED; } }
--- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -617,17 +617,18 @@ protected: return nsGlobalWindow::FromSupports( static_cast<nsISupports*>(js::GetProxyExtra(proxy, 0).toPrivate())); } // False return value means we threw an exception. True return value // but false "found" means we didn't have a subframe at that index. bool GetSubframeWindow(JSContext *cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, - JS::Value *vp, bool &found); + JS::MutableHandle<JS::Value> vp, + bool &found); // Returns a non-null window only if id is an index and we have a // window at that index. already_AddRefed<nsIDOMWindow> GetSubframeWindow(JSContext *cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id); bool AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy, @@ -703,17 +704,17 @@ nsOuterWindowProxy::getPropertyDescripto bool nsOuterWindowProxy::getOwnPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, JS::MutableHandle<JSPropertyDescriptor> desc, unsigned flags) { bool found; - if (!GetSubframeWindow(cx, proxy, id, desc.value().address(), found)) { + if (!GetSubframeWindow(cx, proxy, id, desc.value(), found)) { return false; } if (found) { FillPropertyDescriptor(desc, proxy, true); return true; } // else fall through to js::Wrapper @@ -824,17 +825,17 @@ nsOuterWindowProxy::get(JSContext *cx, J { if (id == nsDOMClassInfo::sWrappedJSObject_id && xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) { vp.set(JS::ObjectValue(*proxy)); return true; } bool found; - if (!GetSubframeWindow(cx, proxy, id, vp.address(), found)) { + if (!GetSubframeWindow(cx, proxy, id, vp, found)) { return false; } if (found) { return true; } // Else fall through to js::Wrapper return js::Wrapper::get(cx, proxy, receiver, id, vp); @@ -875,17 +876,18 @@ nsOuterWindowProxy::iterate(JSContext *c // BaseProxyHandler::iterate seems to do what we want here: fall // back on the property names returned from keys() and enumerate(). return js::BaseProxyHandler::iterate(cx, proxy, flags, vp); } bool nsOuterWindowProxy::GetSubframeWindow(JSContext *cx, JS::Handle<JSObject*> proxy, - JS::Handle<jsid> id, JS::Value* vp, + JS::Handle<jsid> id, + JS::MutableHandle<JS::Value> vp, bool& found) { nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id); if (!frame) { found = false; return true; } @@ -895,17 +897,18 @@ nsOuterWindowProxy::GetSubframeWindow(JS global->EnsureInnerWindow(); JSObject* obj = global->FastGetGlobalJSObject(); // This null check fixes a hard-to-reproduce crash that occurs when we // get here when we're mid-call to nsDocShell::Destroy. See bug 640904 // comment 105. if (MOZ_UNLIKELY(!obj)) { return xpc::Throw(cx, NS_ERROR_FAILURE); } - *vp = JS::ObjectValue(*obj); + + vp.setObject(*obj); return JS_WrapValue(cx, vp); } already_AddRefed<nsIDOMWindow> nsOuterWindowProxy::GetSubframeWindow(JSContext *cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id) {
--- a/dom/base/nsHistory.cpp +++ b/dom/base/nsHistory.cpp @@ -126,17 +126,17 @@ nsHistory::GetState(JSContext* aCx, Erro if (variant) { JS::Rooted<JS::Value> jsData(aCx); aRv = variant->GetAsJSVal(jsData.address()); if (aRv.Failed()) { return JS::UndefinedValue(); } - if (!JS_WrapValue(aCx, jsData.address())) { + if (!JS_WrapValue(aCx, &jsData)) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return JS::UndefinedValue(); } return jsData; } return JS::UndefinedValue();
--- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -1201,25 +1201,30 @@ nsJSContext::SetProperty(JS::Handle<JSOb Maybe<nsRootedJSValueArray> tempStorage; JS::Rooted<JSObject*> global(mContext, GetWindowProxy()); nsresult rv = ConvertSupportsTojsvals(aArgs, global, &argc, &argv, tempStorage); NS_ENSURE_SUCCESS(rv, rv); + JS::AutoArrayRooter array(mContext, argc, argv); + // got the arguments, now attach them. for (uint32_t i = 0; i < argc; ++i) { - if (!JS_WrapValue(mContext, &argv[i])) { + if (!JS_WrapValue(mContext, array.handleAt(i))) { return NS_ERROR_FAILURE; } } - JSObject *args = ::JS_NewArrayObject(mContext, argc, argv); + JSObject *args = ::JS_NewArrayObject(mContext, argc, array.array); + if (!args) { + return NS_ERROR_FAILURE; + } JS::Value vargs = OBJECT_TO_JSVAL(args); return JS_DefineProperty(mContext, aTarget, aPropName, vargs, NULL, NULL, 0) ? NS_OK : NS_ERROR_FAILURE; } nsresult
--- a/dom/base/nsJSUtils.cpp +++ b/dom/base/nsJSUtils.cpp @@ -301,18 +301,23 @@ nsJSUtils::EvaluateString(JSContext* aCx if (aRetValue) { *aRetValue = exn; } JS_ClearPendingException(aCx); } } // Wrap the return value into whatever compartment aCx was in. - if (aRetValue && !JS_WrapValue(aCx, aRetValue)) - return NS_ERROR_OUT_OF_MEMORY; + if (aRetValue) { + JS::Rooted<JS::Value> v(aCx, *aRetValue); + if (!JS_WrapValue(aCx, &v)) { + return NS_ERROR_OUT_OF_MEMORY; + } + *aRetValue = v; + } return rv; } // // nsDOMJSUtils.h // JSObject* GetDefaultScopeFromJSContext(JSContext *cx)
--- a/dom/base/nsStructuredCloneContainer.cpp +++ b/dom/base/nsStructuredCloneContainer.cpp @@ -43,22 +43,23 @@ nsStructuredCloneContainer::InitFromJSVa JSContext* aCx) { NS_ENSURE_STATE(!mData); NS_ENSURE_ARG_POINTER(aCx); // Make sure that we serialize in the right context. MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext()); JS::Rooted<JS::Value> jsData(aCx, aData); - JS_WrapValue(aCx, jsData.address()); + bool success = JS_WrapValue(aCx, &jsData); + NS_ENSURE_STATE(success); uint64_t* jsBytes = nullptr; - bool success = JS_WriteStructuredClone(aCx, jsData, &jsBytes, &mSize, - nullptr, nullptr, - JS::UndefinedHandleValue); + success = JS_WriteStructuredClone(aCx, jsData, &jsBytes, &mSize, + nullptr, nullptr, + JS::UndefinedHandleValue); NS_ENSURE_STATE(success); NS_ENSURE_STATE(jsBytes); // Copy jsBytes into our own buffer. mData = (uint64_t*) malloc(mSize); if (!mData) { mSize = 0; mVersion = 0;
--- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -176,20 +176,22 @@ ErrorResult::ThrowJSException(JSContext* } } void ErrorResult::ReportJSException(JSContext* cx) { MOZ_ASSERT(!mMightHaveUnreportedJSException, "Why didn't you tell us you planned to handle JS exceptions?"); - if (JS_WrapValue(cx, &mJSException)) { - JS::RootedValue exception(cx, mJSException); + + JS::Rooted<JS::Value> exception(cx, mJSException); + if (JS_WrapValue(cx, &exception)) { JS_SetPendingException(cx, exception); } + mJSException = exception; // If JS_WrapValue failed, not much we can do about it... No matter // what, go ahead and unroot mJSException. JS_RemoveValueRoot(cx, &mJSException); } void ErrorResult::ReportJSExceptionFromJSImplementation(JSContext* aCx) { @@ -1558,17 +1560,18 @@ ConcatJSString(JSContext* cx, const char } return JS_ConcatStrings(cx, preString, postString); } bool NativeToString(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> obj, const char* pre, - const char* post, JS::Value* v) + const char* post, + JS::MutableHandle<JS::Value> v) { JS::Rooted<JSPropertyDescriptor> toStringDesc(cx); toStringDesc.object().set(nullptr); toStringDesc.setAttributes(0); toStringDesc.setShortId(0); toStringDesc.setGetter(nullptr); toStringDesc.setSetter(nullptr); toStringDesc.value().set(JS::UndefinedValue()); @@ -1578,17 +1581,17 @@ NativeToString(JSContext* cx, JS::Handle return false; } JS::Rooted<JSString*> str(cx); { JSAutoCompartment ac(cx, obj); if (toStringDesc.object()) { JS::Rooted<JS::Value> toString(cx, toStringDesc.value()); - if (!JS_WrapValue(cx, toString.address())) { + if (!JS_WrapValue(cx, &toString)) { return false; } MOZ_ASSERT(JS_ObjectIsCallable(cx, &toString.toObject())); JS::Rooted<JS::Value> toStringResult(cx); if (JS_CallFunctionValue(cx, obj, toString, 0, nullptr, toStringResult.address())) { str = toStringResult.toString(); } else { @@ -1614,17 +1617,17 @@ NativeToString(JSContext* cx, JS::Handle str = ConcatJSString(cx, pre, str, post); } } if (!str) { return false; } - v->setString(str); + v.setString(str); return JS_WrapValue(cx, v); } // Dynamically ensure that two objects don't end up with the same reserved slot. class MOZ_STACK_CLASS AutoCloneDOMObjectSlotGuard { public: AutoCloneDOMObjectSlotGuard(JSContext* aCx, JSObject* aOld, JSObject* aNew)
--- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -506,45 +506,45 @@ SetSystemOnlyWrapper(JSObject* obj, nsWr // needed. MOZ_ALWAYS_INLINE bool MaybeWrapStringValue(JSContext* cx, JS::MutableHandle<JS::Value> rval) { MOZ_ASSERT(rval.isString()); JSString* str = rval.toString(); if (JS::GetGCThingZone(str) != js::GetContextZone(cx)) { - return JS_WrapValue(cx, rval.address()); + return JS_WrapValue(cx, rval); } return true; } // Make sure to wrap the given object value into the right compartment as // needed. This will work correctly, but possibly slowly, on all objects. MOZ_ALWAYS_INLINE bool MaybeWrapObjectValue(JSContext* cx, JS::MutableHandle<JS::Value> rval) { MOZ_ASSERT(rval.isObject()); JSObject* obj = &rval.toObject(); if (js::GetObjectCompartment(obj) != js::GetContextCompartment(cx)) { - return JS_WrapValue(cx, rval.address()); + return JS_WrapValue(cx, rval); } // We're same-compartment, but even then we might need to wrap // objects specially. Check for that. if (GetSameCompartmentWrapperForDOMBinding(obj)) { // We're a new-binding object, and "obj" now points to the right thing rval.set(JS::ObjectValue(*obj)); return true; } // It's not a WebIDL object. But it might be an XPConnect one, in which case // we may need to outerize here, so make sure to call JS_WrapValue. - return JS_WrapValue(cx, rval.address()); + return JS_WrapValue(cx, rval); } // Like MaybeWrapObjectValue, but also allows null MOZ_ALWAYS_INLINE bool MaybeWrapObjectOrNullValue(JSContext* cx, JS::MutableHandle<JS::Value> rval) { MOZ_ASSERT(rval.isObjectOrNull()); @@ -563,17 +563,17 @@ MaybeWrapNonDOMObjectValue(JSContext* cx MOZ_ASSERT(!GetDOMClass(&rval.toObject())); MOZ_ASSERT(!(js::GetObjectClass(&rval.toObject())->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS)); JSObject* obj = &rval.toObject(); if (js::GetObjectCompartment(obj) == js::GetContextCompartment(cx)) { return true; } - return JS_WrapValue(cx, rval.address()); + return JS_WrapValue(cx, rval); } // Like MaybeWrapNonDOMObjectValue but allows null MOZ_ALWAYS_INLINE bool MaybeWrapNonDOMObjectOrNullValue(JSContext* cx, JS::MutableHandle<JS::Value> rval) { MOZ_ASSERT(rval.isObjectOrNull()); @@ -677,17 +677,17 @@ WrapNewBindingObject(JSContext* cx, JS:: bool sameCompartment = js::GetObjectCompartment(obj) == js::GetContextCompartment(cx); if (sameCompartment && couldBeDOMBinding) { WrapNewBindingForSameCompartment(cx, obj, value, rval); return true; } rval.set(JS::ObjectValue(*obj)); - return JS_WrapValue(cx, rval.address()); + return JS_WrapValue(cx, rval); } // Create a JSObject wrapping "value", for cases when "value" is a // non-wrapper-cached object using WebIDL bindings. "value" must implement a // WrapObject() method taking a JSContext and a scope. template <class T> inline bool WrapNewBindingNonWrapperCachedObject(JSContext* cx, @@ -718,17 +718,17 @@ WrapNewBindingNonWrapperCachedObject(JSC if (!obj) { return false; } // We can end up here in all sorts of compartments, per above. Make // sure to JS_WrapValue! rval.set(JS::ObjectValue(*obj)); - return JS_WrapValue(cx, rval.address()); + return JS_WrapValue(cx, rval); } // Create a JSObject wrapping "value", for cases when "value" is a // non-wrapper-cached owned object using WebIDL bindings. "value" must implement a // WrapObject() method taking a JSContext, a scope, and a boolean outparam that // is true if the JSObject took ownership template <class T> inline bool @@ -769,17 +769,17 @@ WrapNewBindingNonWrapperCachedOwnedObjec if (!obj) { return false; } // We can end up here in all sorts of compartments, per above. Make // sure to JS_WrapValue! rval.set(JS::ObjectValue(*obj)); - return JS_WrapValue(cx, rval.address()); + return JS_WrapValue(cx, rval); } // Helper for smart pointers (nsAutoPtr/nsRefPtr/nsCOMPtr). template <template <typename> class SmartPtr, typename T> inline bool WrapNewBindingNonWrapperCachedObject(JSContext* cx, JS::Handle<JSObject*> scope, const SmartPtr<T>& value, JS::MutableHandle<JS::Value> rval) @@ -1938,17 +1938,18 @@ void SetXrayExpandoChain(JSObject *obj, * interface or interface prototype object. * pre is a string that should be prefixed to the value. * post is a string that should be prefixed to the value. * v contains the JSString for the value if the function returns true. */ bool NativeToString(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> obj, const char* pre, - const char* post, JS::Value* v); + const char* post, + JS::MutableHandle<JS::Value> v); HAS_MEMBER(JSBindingFinalized) template<class T, bool hasCallback=HasJSBindingFinalizedMember<T>::Value> struct JSBindingFinalized { static void Finalized(T* self) {
--- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -4600,19 +4600,19 @@ def wrapTypeIntoCurrentCompartment(type, """ Take the thing named by "value" and if it contains "any", "object", or spidermonkey-interface types inside return a CGThing that will wrap them into the current compartment. """ if type.isAny(): assert not type.nullable() if isMember: + value = "JS::MutableHandleValue::fromMarkedLocation(&%s)" % value + else: value = "&" + value - else: - value = value + ".address()" return CGGeneric("if (!JS_WrapValue(cx, %s)) {\n" " return false;\n" "}" % value) if type.isObject(): if isMember: value = "JS::MutableHandleObject::fromMarkedLocation(&%s)" % value else:
--- a/dom/bindings/Exceptions.cpp +++ b/dom/bindings/Exceptions.cpp @@ -75,17 +75,17 @@ ThrowExceptionObject(JSContext* aCx, Exc // If we stored the original thrown JS value in the exception // (see XPCConvert::ConstructException) and we are in a web context // (i.e., not chrome), rethrow the original value. This only applies to JS // implemented components so we only need to check for this on the main // thread. if (NS_IsMainThread() && !IsCallerChrome() && aException->StealJSVal(thrown.address())) { - if (!JS_WrapValue(aCx, thrown.address())) { + if (!JS_WrapValue(aCx, &thrown)) { return false; } JS_SetPendingException(aCx, thrown); return true; } JS::Rooted<JSObject*> glob(aCx, JS::CurrentGlobalOrNull(aCx)); if (!glob) {
--- a/dom/camera/DOMCameraControl.cpp +++ b/dom/camera/DOMCameraControl.cpp @@ -1,24 +1,27 @@ /* 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 "base/basictypes.h" #include "nsCOMPtr.h" #include "nsDOMClassInfo.h" +#include "nsHashPropertyBag.h" #include "jsapi.h" #include "nsThread.h" #include "DeviceStorage.h" #include "mozilla/dom/CameraControlBinding.h" -#include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/TabChild.h" #include "mozilla/Services.h" #include "mozilla/unused.h" +#include "nsIAppsService.h" #include "nsIObserverService.h" #include "nsIDOMDeviceStorage.h" +#include "nsIScriptSecurityManager.h" #include "nsXULAppAPI.h" #include "DOMCameraManager.h" #include "DOMCameraCapabilities.h" #include "DOMCameraControl.h" #include "CameraCommon.h" #include "mozilla/dom/CameraManagerBinding.h" #include "mozilla/dom/BindingUtils.h" @@ -273,23 +276,26 @@ nsDOMCameraControl::StartRecording(JSCon nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); if (!obs) { NS_WARNING("Could not get the Observer service for CameraControl::StartRecording."); aRv.Throw(NS_ERROR_FAILURE); return; } - obs->NotifyObservers(nullptr, + nsRefPtr<nsHashPropertyBag> props = CreateRecordingDeviceEventsSubject(); + obs->NotifyObservers(static_cast<nsIPropertyBag2*>(props), "recording-device-events", NS_LITERAL_STRING("starting").get()); // Forward recording events to parent process. // The events are gathered in chrome process and used for recording indicator if (XRE_GetProcessType() != GeckoProcessType_Default) { - unused << ContentChild::GetSingleton()->SendRecordingDeviceEvents(NS_LITERAL_STRING("starting")); + unused << TabChild::GetFrom(mWindow)->SendRecordingDeviceEvents(NS_LITERAL_STRING("starting"), + true /* isAudio */, + true /* isVideo */); } #ifdef MOZ_B2G if (!mAudioChannelAgent) { mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1"); if (mAudioChannelAgent) { // Camera app will stop recording when it falls to the background, so no callback is necessary. mAudioChannelAgent->Init(AUDIO_CHANNEL_CONTENT, nullptr); @@ -313,23 +319,26 @@ void nsDOMCameraControl::StopRecording(ErrorResult& aRv) { nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); if (!obs) { NS_WARNING("Could not get the Observer service for CameraControl::StopRecording."); aRv.Throw(NS_ERROR_FAILURE); } - obs->NotifyObservers(nullptr, + nsRefPtr<nsHashPropertyBag> props = CreateRecordingDeviceEventsSubject(); + obs->NotifyObservers(static_cast<nsIPropertyBag2*>(props) , "recording-device-events", NS_LITERAL_STRING("shutdown").get()); // Forward recording events to parent process. // The events are gathered in chrome process and used for recording indicator if (XRE_GetProcessType() != GeckoProcessType_Default) { - unused << ContentChild::GetSingleton()->SendRecordingDeviceEvents(NS_LITERAL_STRING("shutdown")); + unused << TabChild::GetFrom(mWindow)->SendRecordingDeviceEvents(NS_LITERAL_STRING("shutdown"), + true /* isAudio */, + true /* isVideo */); } #ifdef MOZ_B2G if (mAudioChannelAgent) { mAudioChannelAgent->StopPlaying(); mAudioChannelAgent = nullptr; } #endif @@ -519,8 +528,44 @@ nsDOMCameraControl::Shutdown() mCameraControl->Shutdown(); } nsRefPtr<ICameraControl> nsDOMCameraControl::GetNativeCameraControl() { return mCameraControl; } + +already_AddRefed<nsHashPropertyBag> +nsDOMCameraControl::CreateRecordingDeviceEventsSubject() +{ + MOZ_ASSERT(mWindow); + + nsRefPtr<nsHashPropertyBag> props = new nsHashPropertyBag(); + props->SetPropertyAsBool(NS_LITERAL_STRING("isAudio"), true); + props->SetPropertyAsBool(NS_LITERAL_STRING("isVideo"), true); + + nsCOMPtr<nsIDocShell> docShell = mWindow->GetDocShell(); + if (docShell) { + bool isApp; + DebugOnly<nsresult> rv = docShell->GetIsApp(&isApp); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + nsString requestURL; + if (isApp) { + rv = docShell->GetAppManifestURL(requestURL); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + } else { + nsCString pageURL; + nsCOMPtr<nsIURI> docURI = mWindow->GetDocumentURI(); + MOZ_ASSERT(docURI); + + rv = docURI->GetSpec(pageURL); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + requestURL = NS_ConvertUTF8toUTF16(pageURL); + } + props->SetPropertyAsBool(NS_LITERAL_STRING("isApp"), isApp); + props->SetPropertyAsAString(NS_LITERAL_STRING("requestURL"), requestURL); + } + + return props.forget(); +}
--- a/dom/camera/DOMCameraControl.h +++ b/dom/camera/DOMCameraControl.h @@ -10,16 +10,17 @@ #include "nsCycleCollectionParticipant.h" #include "DictionaryHelpers.h" #include "ICameraControl.h" #include "DOMCameraPreview.h" #include "nsIDOMCameraManager.h" #include "CameraCommon.h" #include "AudioChannelAgent.h" #include "nsProxyRelease.h" +#include "nsHashPropertyBag.h" class nsDOMDeviceStorage; class nsPIDOMWindow; namespace mozilla { namespace dom { class CameraPictureOptions; template<typename T> class Optional; @@ -94,16 +95,17 @@ public: protected: virtual ~nsDOMCameraControl(); private: nsDOMCameraControl(const nsDOMCameraControl&) MOZ_DELETE; nsDOMCameraControl& operator=(const nsDOMCameraControl&) MOZ_DELETE; virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE; + already_AddRefed<nsHashPropertyBag> CreateRecordingDeviceEventsSubject(); protected: /* additional members */ nsRefPtr<ICameraControl> mCameraControl; // non-DOM camera control nsCOMPtr<nsICameraCapabilities> mDOMCapabilities; // An agent used to join audio channel service. nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent; nsCOMPtr<nsPIDOMWindow> mWindow;
--- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -1931,33 +1931,16 @@ ContentParent::RecvBroadcastVolume(const return true; #else NS_WARNING("ContentParent::RecvBroadcastVolume shouldn't be called when MOZ_WIDGET_GONK is not defined"); return false; #endif } bool -ContentParent::RecvRecordingDeviceEvents(const nsString& aRecordingStatus) -{ - nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); - if (obs) { - // recording-device-ipc-events needs to gather more information from content process - nsRefPtr<nsHashPropertyBag> props = new nsHashPropertyBag(); - props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), mChildID); - obs->NotifyObservers((nsIPropertyBag2*) props, - "recording-device-ipc-events", - aRecordingStatus.get()); - } else { - NS_WARNING("Could not get the Observer service for ContentParent::RecvRecordingDeviceEvents."); - } - return true; -} - -bool ContentParent::SendNuwaFork() { if (this != sNuwaProcess) { return false; } CancelableTask* nuwaForkTimeoutTask = NewRunnableMethod( this, &ContentParent::OnNuwaForkTimeout);
--- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -159,16 +159,18 @@ public: /** * Kill our subprocess and make sure it dies. Should only be used * in emergency situations since it bypasses the normal shutdown * process. */ void KillHard(); uint64_t ChildID() { return mChildID; } + const nsString& AppManifestURL() const { return mAppManifestURL; } + bool IsPreallocated(); /** * Get a user-friendly name for this ContentParent. We make no guarantees * about this name: It might not be unique, apps can spoof special names, * etc. So please don't use this name to make any decisions about the * ContentParent based on the value returned here. */ @@ -466,18 +468,16 @@ private: virtual bool RecvAudioChannelChangedNotification(); virtual bool RecvAudioChannelChangeDefVolChannel( const AudioChannelType& aType, const bool& aHidden); virtual bool RecvBroadcastVolume(const nsString& aVolumeName); - virtual bool RecvRecordingDeviceEvents(const nsString& aRecordingStatus); - virtual bool RecvSystemMessageHandled() MOZ_OVERRIDE; virtual bool RecvNuwaReady() MOZ_OVERRIDE; virtual bool RecvAddNewProcess(const uint32_t& aPid, const InfallibleTArray<ProtocolFdMapping>& aFds) MOZ_OVERRIDE; virtual bool RecvCreateFakeVolume(const nsString& fsName, const nsString& mountPoint) MOZ_OVERRIDE;
--- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -299,16 +299,24 @@ parent: /** * Notifies the parent about a scroll event. The pres shell ID and * view ID identify which scrollable (sub-)frame was scrolled, and * the new scroll offset for that frame is sent. */ UpdateScrollOffset(uint32_t aPresShellId, ViewID aViewId, CSSIntPoint aScrollOffset); + /** + * Notifies the parent about a recording device is starting or shutdown. + * @param recordingStatus starting or shutdown + * @param isAudio recording start with microphone + * @param isVideo recording start with camera + */ + async RecordingDeviceEvents(nsString recordingStatus, bool isAudio, bool isVideo); + __delete__(); child: /** * Notify the remote browser that it has been Show()n on this * side, with the given |visibleRect|. This message is expected * to trigger creation of the remote browser's "widget". *
--- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -460,18 +460,16 @@ parent: async FilePathUpdateNotify(nsString aType, nsString aStorageName, nsString aFilepath, nsCString aReason); // get nsIVolumeService to broadcast volume information async BroadcastVolume(nsString volumeName); - async RecordingDeviceEvents(nsString recordingStatus); - // Notify the parent that the child has finished handling a system message. async SystemMessageHandled(); NuwaReady(); sync AddNewProcess(uint32_t pid, ProtocolFdMapping[] aFds); // called by the child (test code only) to propagate volume changes to the parent
--- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -25,16 +25,17 @@ #include "mozilla/unused.h" #include "nsCOMPtr.h" #include "nsContentPermissionHelper.h" #include "nsContentUtils.h" #include "nsDebug.h" #include "nsEventStateManager.h" #include "nsFocusManager.h" #include "nsFrameLoader.h" +#include "nsHashPropertyBag.h" #include "nsIContent.h" #include "nsIDocShell.h" #include "nsIDocShellTreeOwner.h" #include "nsIDOMElement.h" #include "nsIDOMEvent.h" #include "nsIDOMWindow.h" #include "nsIDialogCreator.h" #include "nsIInterfaceRequestorUtils.h" @@ -1620,10 +1621,44 @@ bool TabParent::RecvContentReceivedTouch(const bool& aPreventDefault) { if (RenderFrameParent* rfp = GetRenderFrame()) { rfp->ContentReceivedTouch(aPreventDefault); } return true; } +bool +TabParent::RecvRecordingDeviceEvents(const nsString& aRecordingStatus, + const bool& aIsAudio, + const bool& aIsVideo) +{ + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + // recording-device-ipc-events needs to gather more information from content process + nsRefPtr<nsHashPropertyBag> props = new nsHashPropertyBag(); + props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), Manager()->ChildID()); + props->SetPropertyAsBool(NS_LITERAL_STRING("isApp"), Manager()->IsForApp()); + props->SetPropertyAsBool(NS_LITERAL_STRING("isAudio"), aIsAudio); + props->SetPropertyAsBool(NS_LITERAL_STRING("isVideo"), aIsVideo); + + nsString requestURL; + if (Manager()->IsForApp()) { + requestURL = Manager()->AppManifestURL(); + } else { + nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader(); + NS_ENSURE_TRUE(frameLoader, true); + + frameLoader->GetURL(requestURL); + } + props->SetPropertyAsAString(NS_LITERAL_STRING("requestURL"), requestURL); + + obs->NotifyObservers((nsIPropertyBag2*) props, + "recording-device-ipc-events", + aRecordingStatus.get()); + } else { + NS_WARNING("Could not get the Observer service for ContentParent::RecvRecordingDeviceEvents."); + } + return true; +} + } // namespace tabs } // namespace mozilla
--- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -157,16 +157,19 @@ public: virtual bool RecvGetDefaultScale(double* aValue); virtual bool RecvGetWidgetNativeData(WindowsHandle* aValue); virtual bool RecvZoomToRect(const CSSRect& aRect); virtual bool RecvUpdateZoomConstraints(const bool& aAllowZoom, const CSSToScreenScale& aMinZoom, const CSSToScreenScale& aMaxZoom); virtual bool RecvUpdateScrollOffset(const uint32_t& aPresShellId, const ViewID& aViewId, const CSSIntPoint& aScrollOffset); virtual bool RecvContentReceivedTouch(const bool& aPreventDefault); + virtual bool RecvRecordingDeviceEvents(const nsString& aRecordingStatus, + const bool& aIsAudio, + const bool& aIsVideo); virtual PContentDialogParent* AllocPContentDialogParent(const uint32_t& aType, const nsCString& aName, const nsCString& aFeatures, const InfallibleTArray<int>& aIntParams, const InfallibleTArray<nsString>& aStringParams); virtual bool DeallocPContentDialogParent(PContentDialogParent* aDialog) { delete aDialog;
--- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -1,33 +1,38 @@ /* 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 "MediaManager.h" #include "MediaStreamGraph.h" #include "GetUserMediaRequest.h" +#include "nsHashPropertyBag.h" #ifdef MOZ_WIDGET_GONK #include "nsIAudioManager.h" #endif +#include "nsIAppsService.h" #include "nsIDOMFile.h" #include "nsIEventTarget.h" #include "nsIUUIDGenerator.h" #include "nsIScriptGlobalObject.h" #include "nsIPopupWindowManager.h" #include "nsISupportsArray.h" #include "nsIDocShell.h" #include "nsIDocument.h" #include "nsISupportsPrimitives.h" #include "nsIInterfaceRequestorUtils.h" -#include "mozilla/dom/ContentChild.h" +#include "nsIScriptSecurityManager.h" +#include "mozilla/dom/TabChild.h" #include "mozilla/dom/MediaStreamTrackBinding.h" #include "mozilla/dom/GetUserMediaRequestBinding.h" +#include "Latency.h" + // For PR_snprintf #include "prprf.h" #include "nsJSUtils.h" #include "nsDOMFile.h" #include "nsGlobalWindow.h" #include "mozilla/Preferences.h" @@ -133,16 +138,55 @@ static nsresult ValidateTrackConstraints nsresult rv = CompareDictionaries(aCx, track.mMandatory.Value(), aNormalized.mMandatory, aOutUnknownConstraint); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; } +static already_AddRefed<nsHashPropertyBag> +CreateRecordingDeviceEventsSubject(nsPIDOMWindow* aWindow, + const bool aIsAudio, + const bool aIsVideo) +{ + MOZ_ASSERT(aWindow); + + nsRefPtr<nsHashPropertyBag> props = new nsHashPropertyBag(); + props->SetPropertyAsBool(NS_LITERAL_STRING("isAudio"), aIsAudio); + props->SetPropertyAsBool(NS_LITERAL_STRING("isVideo"), aIsVideo); + + nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell(); + if (docShell) { + bool isApp; + DebugOnly<nsresult> rv = docShell->GetIsApp(&isApp); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + nsString requestURL; + if (isApp) { + rv = docShell->GetAppManifestURL(requestURL); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + } else { + nsCString pageURL; + nsCOMPtr<nsIURI> docURI = aWindow->GetDocumentURI(); + MOZ_ASSERT(docURI); + + rv = docURI->GetSpec(pageURL); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + requestURL = NS_ConvertUTF8toUTF16(pageURL); + } + + props->SetPropertyAsAString(NS_LITERAL_STRING("requestURL"), requestURL); + props->SetPropertyAsBool(NS_LITERAL_STRING("isApp"), isApp); + } + + return props.forget(); +} + /** * Send an error back to content. The error is the form a string. * Do this only on the main thread. The success callback is also passed here * so it can be released correctly. */ class ErrorCallbackRunnable : public nsRunnable { public: @@ -559,16 +603,22 @@ public: nsRefPtr<SourceMediaStream> stream = gm->CreateSourceStream(nullptr); // connect the source stream to the track union stream to avoid us blocking trackunion->GetStream()->AsProcessedStream()->SetAutofinish(true); nsRefPtr<MediaInputPort> port = trackunion->GetStream()->AsProcessedStream()-> AllocateInputPort(stream, MediaInputPort::FLAG_BLOCK_OUTPUT); trackunion->mSourceStream = stream; trackunion->mPort = port.forget(); + // Log the relationship between SourceMediaStream and TrackUnion stream + // Make sure logger starts before capture + AsyncLatencyLogger::Get(true); + LogLatency(AsyncLatencyLogger::MediaStreamCreate, + reinterpret_cast<uint64_t>(stream.get()), + reinterpret_cast<int64_t>(trackunion->GetStream())); trackunion->CombineWithPrincipal(window->GetExtantDoc()->NodePrincipal()); // The listener was added at the begining in an inactive state. // Activate our listener. We'll call Start() on the source when get a callback // that the MediaStream has started consuming. The listener is freed // when the page is invalidated (on navigation or close). mListener->Activate(stream.forget(), mAudioSource, mVideoSource); @@ -579,17 +629,17 @@ public: // Dispatch to the media thread to ask it to start the sources, // because that can take a while. // Pass ownership of trackunion to the MediaOperationRunnable // to ensure it's kept alive until the MediaOperationRunnable runs (at least). nsIThread *mediaThread = MediaManager::GetThread(); nsRefPtr<MediaOperationRunnable> runnable( new MediaOperationRunnable(MEDIA_START, mListener, trackunion, tracksAvailableCallback, - mAudioSource, mVideoSource, false)); + mAudioSource, mVideoSource, false, mWindowID)); mediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL); #ifdef MOZ_WEBRTC // Right now these configs are only of use if webrtc is available nsresult rv; nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs); @@ -1725,17 +1775,17 @@ GetUserMediaCallbackMediaStreamListener: nsRefPtr<MediaOperationRunnable> runnable; // We can't take a chance on blocking here, so proxy this to another // thread. // Pass a ref to us (which is threadsafe) so it can query us for the // source stream info. runnable = new MediaOperationRunnable(MEDIA_STOP, this, nullptr, nullptr, mAudioSource, mVideoSource, - mFinished); + mFinished, mWindowID); mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL); } // Called from the MediaStreamGraph thread void GetUserMediaCallbackMediaStreamListener::NotifyFinished(MediaStreamGraph* aGraph) { mFinished = true; @@ -1782,20 +1832,27 @@ GetUserMediaNotificationEvent::Run() break; case STOPPING: msg = NS_LITERAL_STRING("shutdown"); if (mListener) { mListener->SetStopped(); } break; } - obs->NotifyObservers(nullptr, + + nsCOMPtr<nsPIDOMWindow> window = nsGlobalWindow::GetInnerWindowWithId(mWindowID); + MOZ_ASSERT(window); + + nsRefPtr<nsHashPropertyBag> props = + CreateRecordingDeviceEventsSubject(window, mIsAudio, mIsVideo); + + obs->NotifyObservers(static_cast<nsIPropertyBag2*>(props), "recording-device-events", msg.get()); // Forward recording events to parent process. // The events are gathered in chrome process and used for recording indicator if (XRE_GetProcessType() != GeckoProcessType_Default) { - unused << dom::ContentChild::GetSingleton()->SendRecordingDeviceEvents(msg); + unused << dom::TabChild::GetFrom(window)->SendRecordingDeviceEvents(msg, mIsAudio, mIsVideo); } return NS_OK; } } // namespace mozilla
--- a/dom/media/MediaManager.h +++ b/dom/media/MediaManager.h @@ -199,36 +199,42 @@ private: class GetUserMediaNotificationEvent: public nsRunnable { public: enum GetUserMediaStatus { STARTING, STOPPING }; GetUserMediaNotificationEvent(GetUserMediaCallbackMediaStreamListener* aListener, - GetUserMediaStatus aStatus) - : mListener(aListener), mStatus(aStatus) {} + GetUserMediaStatus aStatus, + bool aIsAudio, bool aIsVideo, uint64_t aWindowID) + : mListener(aListener) , mStatus(aStatus) , mIsAudio(aIsAudio) + , mIsVideo(aIsVideo), mWindowID(aWindowID) {} GetUserMediaNotificationEvent(GetUserMediaStatus aStatus, already_AddRefed<DOMMediaStream> aStream, - DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback) + DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback, + bool aIsAudio, bool aIsVideo, uint64_t aWindowID) : mStream(aStream), mOnTracksAvailableCallback(aOnTracksAvailableCallback), - mStatus(aStatus) {} + mStatus(aStatus), mIsAudio(aIsAudio), mIsVideo(aIsVideo), mWindowID(aWindowID) {} virtual ~GetUserMediaNotificationEvent() { } NS_IMETHOD Run() MOZ_OVERRIDE; protected: nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe nsRefPtr<DOMMediaStream> mStream; nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback; GetUserMediaStatus mStatus; + bool mIsAudio; + bool mIsVideo; + uint64_t mWindowID; }; typedef enum { MEDIA_START, MEDIA_STOP } MediaOperation; // Generic class for running long media operations like Start off the main @@ -239,24 +245,26 @@ class MediaOperationRunnable : public ns public: // so we can send Stop without AddRef()ing from the MSG thread MediaOperationRunnable(MediaOperation aType, GetUserMediaCallbackMediaStreamListener* aListener, DOMMediaStream* aStream, DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback, MediaEngineSource* aAudioSource, MediaEngineSource* aVideoSource, - bool aNeedsFinish) + bool aNeedsFinish, + uint64_t aWindowID) : mType(aType) , mStream(aStream) , mOnTracksAvailableCallback(aOnTracksAvailableCallback) , mAudioSource(aAudioSource) , mVideoSource(aVideoSource) , mListener(aListener) , mFinish(aNeedsFinish) + , mWindowID(aWindowID) {} ~MediaOperationRunnable() { // MediaStreams can be released on any thread. } NS_IMETHOD @@ -298,17 +306,20 @@ public: MM_LOG(("started all sources")); // Forward mOnTracksAvailableCallback to GetUserMediaNotificationEvent, // because mOnTracksAvailableCallback needs to be added to mStream // on the main thread. nsRefPtr<GetUserMediaNotificationEvent> event = new GetUserMediaNotificationEvent(GetUserMediaNotificationEvent::STARTING, mStream.forget(), - mOnTracksAvailableCallback.forget()); + mOnTracksAvailableCallback.forget(), + mAudioSource != nullptr, + mVideoSource != nullptr, + mWindowID); NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); } break; case MEDIA_STOP: { NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread"); if (mAudioSource) { @@ -319,17 +330,21 @@ public: mVideoSource->Stop(source, kVideoTrack); mVideoSource->Deallocate(); } // Do this after stopping all tracks with EndTrack() if (mFinish) { source->Finish(); } nsRefPtr<GetUserMediaNotificationEvent> event = - new GetUserMediaNotificationEvent(mListener, GetUserMediaNotificationEvent::STOPPING); + new GetUserMediaNotificationEvent(mListener, + GetUserMediaNotificationEvent::STOPPING, + mAudioSource != nullptr, + mVideoSource != nullptr, + mWindowID); NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); } break; default: MOZ_ASSERT(false,"invalid MediaManager operation"); break; @@ -340,16 +355,17 @@ public: private: MediaOperation mType; nsRefPtr<DOMMediaStream> mStream; nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback; nsRefPtr<MediaEngineSource> mAudioSource; // threadsafe nsRefPtr<MediaEngineSource> mVideoSource; // threadsafe nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe bool mFinish; + uint64_t mWindowID; }; typedef nsTArray<nsRefPtr<GetUserMediaCallbackMediaStreamListener> > StreamListeners; typedef nsClassHashtable<nsUint64HashKey, StreamListeners> WindowTable; class MediaDevice : public nsIMediaDevice { public:
--- a/dom/src/jsurl/nsJSProtocolHandler.cpp +++ b/dom/src/jsurl/nsJSProtocolHandler.cpp @@ -324,17 +324,17 @@ nsresult nsJSThunk::EvaluateScript(nsICh // lose the error), or it might be JS that then proceeds to // cause an error of its own (which will also make us lose // this error). ::JS_ReportPendingException(cx); } // If we took the sandbox path above, v might be in the sandbox // compartment. - if (!JS_WrapValue(cx, v.address())) { + if (!JS_WrapValue(cx, &v)) { return NS_ERROR_OUT_OF_MEMORY; } if (NS_FAILED(rv) || !(v.isString() || v.isUndefined())) { return NS_ERROR_MALFORMED_URI; } else if (v.isUndefined()) { return NS_ERROR_DOM_RETVAL_UNDEFINED; } else {
new file mode 100644 --- /dev/null +++ b/dom/tests/mochitest/bugs/file_bug927901.html @@ -0,0 +1,18 @@ +<html> + <head> + <title></title> + <script> + var ret = "pass"; + try { + window.foo = window.crypto.getRandomValues; + } catch(ex) { + ret = "" + ex; + } + parent.postMessage(ret, "*"); + </script> + <style> + </style> + </head> + <body onload="document.body.textContent = 'Crypto test file on ' + location"> + </body> +</html>
--- a/dom/tests/mochitest/bugs/mochitest.ini +++ b/dom/tests/mochitest/bugs/mochitest.ini @@ -27,16 +27,17 @@ support-files = iframe_bug430276-2.html iframe_bug430276.html iframe_bug440572.html iframe_bug49312.html iframe_domparser_after_blank.html utils_bug260264.js utils_bug743615.js worker_bug743615.js + file_bug927901.html [test_DOMWindowCreated_chromeonly.html] [test_bug132255.html] [test_bug159849.html] [test_bug260264.html] [test_bug260264_nested.html] [test_bug265203.html] [test_bug291377.html] @@ -123,16 +124,17 @@ support-files = [test_bug809290.html] [test_bug817476.html] [test_bug823173.html] [test_bug848088.html] [test_bug850517.html] [test_bug857555.html] [test_bug862540.html] [test_bug876098.html] +[test_bug927901.html] [test_devicemotion_multiple_listeners.html] [test_domparser_after_blank.html] [test_onerror_message.html] [test_protochains.html] [test_resize_move_windows.html] [test_sizetocontent_clamp.html] [test_toJSON.html] [test_window_bar.html]
new file mode 100644 --- /dev/null +++ b/dom/tests/mochitest/bugs/test_bug927901.html @@ -0,0 +1,40 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=927901 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 927901</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + + /** Test for Bug 927901 **/ + SimpleTest.waitForExplicitFinish(); + + var counter = 0; + window.onmessage = function(e) { + ++counter; + is(e.data, "pass", "Accessing window.crypto.getRandomValues in the iframe should have succeeded!"); + if (counter == 1) { + document.getElementById("testiframe").src = + "http://mochi.test:8888/tests/dom/tests/mochitest/bugs/file_bug927901.html " + } else if (counter == 2) { + SimpleTest.finish(); + } + } + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=927901">Mozilla Bug 927901</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +<iframe id="testiframe" src="http://test1.example.org:8000/tests/dom/tests/mochitest/bugs/file_bug927901.html"></iframe> +</body> +</html>
--- a/dom/tests/mochitest/general/test_offsets.html +++ b/dom/tests/mochitest/general/test_offsets.html @@ -11,51 +11,51 @@ -moz-box-sizing: content-box; } </style> </head> <body id="body" onload="setTimeout(testElements, 0, 'testelements', SimpleTest.finish);" style="margin: 1px; border: 2px solid black; padding: 4px;"> <div id="testelements" style="margin: 0; border: 0; padding: 0;"> - <input id="input1" style="margin: 0; margin-left: 6px; margin-top: 2px; border: 1px solid green; padding: 6px; width: 50px; height: 20px" + <div id="div1" style="margin: 0; margin-left: 6px; margin-top: 2px; border: 1px solid green; padding: 6px; width: 50px; height: 20px" _offsetLeft="13" _offsetTop="9" _offsetWidth="64" _offsetHeight="34" _scrollWidth="62" _scrollHeight="32" - _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32"> + _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32"></div> <div id="noscroll" style="margin: 2px; border: 1px solid blue; padding: 3px;" _offsetLeft="10" _offsetTop="12" _offsetWidth="64" _offsetHeight="34" _scrollWidth="62" _scrollHeight="32" _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32"> <div id="inner">Inner Text</div> </div> <div id="absolute" style="position: absolute; margin: 5px; border: 2px solid blue; padding: 0;"> <div id="absolute-block" _offsetParent="absolute"> - <input id="absolute-replaced" _offsetParent="absolute" style="margin: 1px; border: 0; padding: 3px;"> + <div id="absolute-replaced" _offsetParent="absolute" style="margin: 1px; border: 0; padding: 3px;"></div> </div> </div> <div id="absolutelr" style="position: absolute; margin: 5px; border: 2px solid blue; padding: 0; left: 90px; top: 130px;"> This is some absolute positioned text. <div id="absolutelr-block" _offsetParent="absolutelr"> - <input id="absolutelr-replaced" _offsetParent="absolutelr" style="margin: 1px; border: 0; padding: 3px;"> + <div id="absolutelr-replaced" _offsetParent="absolutelr" style="margin: 1px; border: 0; padding: 3px;"></div> </div> </div> <div id="relative" style="position: relative; margin: 2px; border: 1px solid orange; padding: 7px; left: 10px; top: 5px;"> This is some relative positioned text. <div id="relative-block" _offsetParent="relative"> - <input id="relative-replaced" _offsetParent="relative" style="margin: 1px; border: 0; padding: 3px;"> + <div id="relative-replaced" _offsetParent="relative" style="margin: 1px; border: 0; padding: 3px;"></div> </div> </div> <div id="fixed" style="position: fixed; margin: 2px; border: 1px solid orange; padding: 7px; left: 87px; top: 12px;"> This is some fixed positioned text. <div id="fixed-block" _offsetParent="fixed"> - <input id="fixed-replaced" _offsetParent="fixed" style="margin: 1px; border: 0; padding: 3px;"> + <div id="fixed-replaced" _offsetParent="fixed" style="margin: 1px; border: 0; padding: 3px;"></div> </div> </div> <div id="scrollbox" style="overflow: scroll; padding-left: 0px; margin: 3px; border: 4px solid green; max-width: 80px; max-height: 70px;" _scrollWidth="62" _scrollHeight="32" _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32"><p id="p1" style="margin: 0; padding: 0;">One</p> <p id="p2">Two</p> @@ -65,23 +65,23 @@ value="This button is much longer than the others"> </p></div> <div id="overflow-visible" style="width:100px; height:100px;"> <div id="overflow-visible-1" style="width:200px; height:1px; background:yellow;"></div> <div id="overflow-visible-2" style="height:200px; background:lime;"></div> </div> - <input id="input-displaynone" style="display: none; border: 0; padding: 0;" - _offsetParent="null"> + <div id="div-displaynone" style="display: none; border: 0; padding: 0;" + _offsetParent="null"></div> <p id="p3" style="margin: 2px; border: 0; padding: 1px;" _offsetLeft="9" _offsetTop="9" _offsetWidth="64" _offsetHeight="34" _scrollWidth="62" _scrollHeight="32" _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32"> - <input id="input-nosize" style="width: 0; height: 0; margin: 0; border: 0; padding: 0;"> + <div id="div-nosize" style="width: 0; height: 0; margin: 0; border: 0; padding: 0;"></div> </p> </div> <div id="scrollbox-test" style="float: left; overflow: scroll; margin: 0; border: 0; padding: 0"></div> <script type="application/javascript"> SimpleTest.waitForExplicitFinish();
--- a/dom/tests/mochitest/general/test_offsets.js +++ b/dom/tests/mochitest/general/test_offsets.js @@ -105,17 +105,17 @@ function testElement(element) isEqualAppunits(boundingrect.height, borderTop + paddingTop + height + paddingBottom + borderBottom, element.id + " bounding rect height"); isEqualAppunits(boundingrect.right - boundingrect.left, boundingrect.width, element.id + " bounding rect right"); isEqualAppunits(boundingrect.bottom - boundingrect.top, boundingrect.height, element.id + " bounding rect bottom"); var rects = element.getClientRects(); - if (element.id == "input-displaynone" || element.id == "nonappended") { + if (element.id == "div-displaynone" || element.id == "nonappended") { is(rects.length, 0, element.id + " getClientRects empty"); } else { is(rects[0].left, boundingrect.left, element.id + " getClientRects left"); is(rects[0].top, boundingrect.top, element.id + " getClientRects top"); is(rects[0].right, boundingrect.right, element.id + " getClientRects right"); is(rects[0].bottom, boundingrect.bottom, element.id + " getClientRects bottom"); } @@ -189,22 +189,19 @@ function checkCoords(element, type, left checkCoord(element, type + "Height", height, testname); if (element instanceof SVGElement) return; if (element.id == "outerpopup" && !element.parentNode.open) // closed popup return; - if (element.id == "input-displaynone" || element.id == "nonappended") // hidden elements + if (element.id == "div-displaynone" || element.id == "nonappended") // hidden elements ok(element[type + "Width"] == 0 && element[type + "Height"] == 0, element.id + " has zero " + type + " width and height"); - else if (element.id != "input-nosize") // for some reason, this element has a width of 2 - ok(element[type + "Width"] > 0 && element[type + "Height"] > 0, - element.id + " has non-zero " + type + " width and height"); } function gcs(element, prop) { var propVal = (element instanceof SVGElement && (prop == "width" || prop == "height")) ? element.getAttribute(prop) : getComputedStyle(element, "")[prop]; if (propVal == "auto") return 0;
--- a/image/src/imgLoader.cpp +++ b/image/src/imgLoader.cpp @@ -24,16 +24,17 @@ #include "nsICachingChannel.h" #include "nsIInterfaceRequestor.h" #include "nsIProgressEventSink.h" #include "nsIChannelEventSink.h" #include "nsIAsyncVerifyRedirectCallback.h" #include "nsIFileURL.h" #include "nsCRT.h" #include "nsIDocument.h" +#include "nsINetworkSeer.h" #include "nsIApplicationCache.h" #include "nsIApplicationCacheContainer.h" #include "nsIMemoryReporter.h" #include "Image.h" #include "DiscardTracker.h" @@ -1233,16 +1234,19 @@ bool imgLoader::ValidateRequestWithNewCh // In the mean time, we must defer notifications because we are added to // the imgRequest's proxy list, and we can get extra notifications // resulting from methods such as RequestDecode(). See bug 579122. proxy->SetNotificationsDeferred(true); // Add the proxy without notifying hvc->AddProxy(proxy); + mozilla::net::SeerLearn(aURI, aInitialDocumentURI, + nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, aLoadGroup); + rv = newChannel->AsyncOpen(listener, nullptr); if (NS_SUCCEEDED(rv)) NS_ADDREF(*aProxyRequest = req.get()); return NS_SUCCEEDED(rv); } } @@ -1730,16 +1734,19 @@ nsresult imgLoader::LoadImage(nsIURI *aU } listener = corsproxy; } PR_LOG(GetImgLog(), PR_LOG_DEBUG, ("[this=%p] imgLoader::LoadImage -- Calling channel->AsyncOpen()\n", this)); + mozilla::net::SeerLearn(aURI, aInitialDocumentURI, + nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, aLoadGroup); + nsresult openRes = newChannel->AsyncOpen(listener, nullptr); if (NS_FAILED(openRes)) { PR_LOG(GetImgLog(), PR_LOG_DEBUG, ("[this=%p] imgLoader::LoadImage -- AsyncOpen() failed: 0x%x\n", this, openRes)); request->CancelAndAbort(openRes); return openRes;
--- a/js/jsd/jsd_val.cpp +++ b/js/jsd/jsd_val.cpp @@ -191,17 +191,17 @@ jsd_GetValueString(JSDContext* jsdc, JSD AutoSaveExceptionState as(cx); string = JS_ValueToString(cx, jsdval->val); } JSAutoCompartment ac2(cx, jsdc->glob); if(string) { stringval = STRING_TO_JSVAL(string); } - if(!string || !JS_WrapValue(cx, stringval.address())) { + if(!string || !JS_WrapValue(cx, &stringval)) { return nullptr; } jsdval->string = JSVAL_TO_STRING(stringval); if(!JS_AddNamedStringRoot(cx, &jsdval->string, "ValueString")) jsdval->string = nullptr; return jsdval->string; @@ -248,17 +248,17 @@ jsd_NewValue(JSDContext* jsdc, jsval val if(JSVAL_IS_GCTHING(val)) { bool ok; JSAutoCompartment ac(cx, jsdc->glob); ok = JS_AddNamedValueRoot(cx, &jsdval->val, "JSDValue"); if(ok && JSVAL_IS_STRING(val)) { - if(!JS_WrapValue(cx, val.address())) { + if(!JS_WrapValue(cx, &val)) { ok = false; } } if(!ok) { free(jsdval); return nullptr;
--- a/js/jsd/jsd_xpc.cpp +++ b/js/jsd/jsd_xpc.cpp @@ -2315,21 +2315,22 @@ jsdValue::Refresh() return NS_OK; } NS_IMETHODIMP jsdValue::GetWrappedValue(JSContext* aCx, JS::Value* aRetval) { ASSERT_VALID_EPHEMERAL; - *aRetval = JSD_GetValueWrappedJSVal(mCx, mValue); - if (!JS_WrapValue(aCx, aRetval)) { + JS::RootedValue value(aCx, JSD_GetValueWrappedJSVal(mCx, mValue)); + if (!JS_WrapValue(aCx, &value)) { return NS_ERROR_FAILURE; } + *aRetval = value; return NS_OK; } NS_IMETHODIMP jsdValue::GetScript(jsdIScript **_rval) { ASSERT_VALID_EPHEMERAL; JSDScript *script = JSD_GetScriptForValue(mCx, mValue);
--- a/js/src/jit-test/tests/basic/testOOMInAutoEnterCompartment.js +++ b/js/src/jit-test/tests/basic/testOOMInAutoEnterCompartment.js @@ -1,8 +1,9 @@ +// |jit-test| slow // This test is too slow to run with ASan in a debug configuration if (getBuildConfiguration()['asan'] && getBuildConfiguration()['debug']) quit(0); function fatty() { try { fatty(); } catch (e) { foo();
--- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -1,16 +1,17 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * 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 "jit/BaselineIC.h" +#include "mozilla/DebugOnly.h" #include "mozilla/TemplateLib.h" #include "jsautooplen.h" #include "jslibmath.h" #include "jstypes.h" #include "builtin/Eval.h" #include "jit/BaselineHelpers.h" @@ -26,16 +27,18 @@ #include "jsboolinlines.h" #include "jsscriptinlines.h" #include "jit/IonFrames-inl.h" #include "vm/Interpreter-inl.h" #include "vm/ScopeObject-inl.h" #include "vm/StringObject-inl.h" +using mozilla::DebugOnly; + namespace js { namespace jit { #ifdef DEBUG void FallbackICSpew(JSContext *cx, ICFallbackStub *stub, const char *fmt, ...) { if (IonSpewEnabled(IonSpew_BaselineICFallback)) { @@ -4133,18 +4136,18 @@ ICGetElemNativeCompiler::generateStubCod JS_ASSERT(R0 == JSReturnOperand); leaveStubFrame(masm); masm.moveValue(JSReturnOperand, R1); // Unstow R0 EmitUnstowICValues(masm, 1); // Extract string from R1 again. - Register strExtract2 = masm.extractString(R1, ExtractTemp1); - JS_ASSERT(strExtract2 == strExtract); + DebugOnly<Register> strExtract2 = masm.extractString(R1, ExtractTemp1); + JS_ASSERT(Register(strExtract2) == strExtract); masm.bind(&skipAtomize); } // Since this stub sometimes enter a stub frame, we manually set this to true (lie). entersStubFrame_ = true; // Key has been atomized if necessary. Do identity check on string pointer.
--- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -2126,17 +2126,17 @@ jit::SetEnterJitData(JSContext *cx, Ente data.calleeToken = CalleeToToken(iter.callee()); } } return true; } IonExecStatus -jit::Cannon(JSContext *cx, RunState &state) +jit::IonCannon(JSContext *cx, RunState &state) { IonScript *ion = state.script()->ionScript(); EnterJitData data(cx); data.jitcode = ion->method()->raw(); AutoValueVector vals(cx); if (!SetEnterJitData(cx, data, state, vals))
--- a/js/src/jit/Ion.h +++ b/js/src/jit/Ion.h @@ -333,17 +333,17 @@ IsErrorStatus(IonExecStatus status) { return status == IonExec_Error || status == IonExec_Aborted; } struct EnterJitData; bool SetEnterJitData(JSContext *cx, EnterJitData &data, RunState &state, AutoValueVector &vals); -IonExecStatus Cannon(JSContext *cx, RunState &state); +IonExecStatus IonCannon(JSContext *cx, RunState &state); // Used to enter Ion from C++ natives like Array.map. Called from FastInvokeGuard. IonExecStatus FastInvoke(JSContext *cx, HandleFunction fun, CallArgs &args); // Walk the stack and invalidate active Ion frames for the invalid scripts. void Invalidate(types::TypeCompartment &types, FreeOp *fop, const Vector<types::RecompileInfo> &invalid, bool resetUses = true); void Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses = true);
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -275,17 +275,17 @@ IonBuilder::canInlineTarget(JSFunction * if (target->getParent() != &script()->global()) { IonSpew(IonSpew_Inlining, "Cannot inline due to scope mismatch"); return false; } // Allow constructing lazy scripts when performing the definite properties // analysis, as baseline has not been used to warm the caller up yet. - if (target->isInterpretedLazy() && info().executionMode() == DefinitePropertiesAnalysis) { + if (target->isInterpreted() && info().executionMode() == DefinitePropertiesAnalysis) { if (!target->getOrCreateScript(context())) return false; RootedScript script(context(), target->nonLazyScript()); if (!script->hasBaselineScript()) { MethodStatus status = BaselineCompile(context(), script); if (status != Method_Compiled) return false;
--- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -1727,27 +1727,20 @@ LIRGenerator::visitToInt32(MToInt32 *con case MIRType_Double: { LDoubleToInt32 *lir = new LDoubleToInt32(useRegister(opd)); return assignSnapshot(lir) && define(lir, convert); } case MIRType_String: - // Strings are complicated - we don't handle them yet. - IonSpew(IonSpew_Abort, "String to Int32 not supported yet."); - return false; - case MIRType_Object: - // Objects might be effectful. - IonSpew(IonSpew_Abort, "Object to Int32 not supported yet."); - return false; - case MIRType_Undefined: - IonSpew(IonSpew_Abort, "Undefined coerces to NaN, not int32_t."); + // Objects might be effectful. Undefined coerces to NaN, not int32. + MOZ_ASSUME_UNREACHABLE("ToInt32 invalid input type"); return false; default: MOZ_ASSUME_UNREACHABLE("unexpected type"); } } bool
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -2914,17 +2914,19 @@ class MAsmJSUnsignedToFloat32 } bool canProduceFloat32() const { return true; } }; // Converts a primitive (either typed or untyped) to an int32. If the input is // not primitive at runtime, a bailout occurs. If the input cannot be converted // to an int32 without loss (i.e. "5.5" or undefined) then a bailout occurs. -class MToInt32 : public MUnaryInstruction +class MToInt32 + : public MUnaryInstruction, + public ToInt32Policy { bool canBeNegativeZero_; MToInt32(MDefinition *def) : MUnaryInstruction(def), canBeNegativeZero_(true) { setResultType(MIRType_Int32); @@ -2945,16 +2947,20 @@ class MToInt32 : public MUnaryInstructio bool canBeNegativeZero() { return canBeNegativeZero_; } void setCanBeNegativeZero(bool negativeZero) { canBeNegativeZero_ = negativeZero; } + TypePolicy *typePolicy() { + return this; + } + bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { return AliasSet::None(); } void computeRange();
--- a/js/src/jit/TypePolicy.cpp +++ b/js/src/jit/TypePolicy.cpp @@ -531,16 +531,37 @@ ToDoublePolicy::staticAdjustInputs(MInst if (in->type() != MIRType_Object && in->type() != MIRType_String) return true; in = boxAt(ins, in); ins->replaceOperand(0, in); return true; } +bool +ToInt32Policy::staticAdjustInputs(MInstruction *ins) +{ + JS_ASSERT(ins->isToInt32()); + + MDefinition *in = ins->getOperand(0); + switch (in->type()) { + case MIRType_Object: + case MIRType_String: + case MIRType_Undefined: + // Objects might be effectful. Undefined coerces to NaN, not int32. + in = boxAt(ins, in); + ins->replaceOperand(0, in); + break; + default: + break; + } + + return true; +} + template <unsigned Op> bool ObjectPolicy<Op>::staticAdjustInputs(MInstruction *ins) { MDefinition *in = ins->getOperand(Op); if (in->type() == MIRType_Object || in->type() == MIRType_Slots || in->type() == MIRType_Elements) {
--- a/js/src/jit/TypePolicy.h +++ b/js/src/jit/TypePolicy.h @@ -189,16 +189,26 @@ class ToDoublePolicy : public BoxInputsP { public: static bool staticAdjustInputs(MInstruction *def); bool adjustInputs(MInstruction *def) { return staticAdjustInputs(def); } }; +// Box objects, strings and undefined as input to a ToInt32 instruction. +class ToInt32Policy : public BoxInputsPolicy +{ + public: + static bool staticAdjustInputs(MInstruction *def); + bool adjustInputs(MInstruction *def) { + return staticAdjustInputs(def); + } +}; + template <unsigned Op> class ObjectPolicy : public BoxInputsPolicy { public: static bool staticAdjustInputs(MInstruction *ins); bool adjustInputs(MInstruction *ins) { return staticAdjustInputs(ins); }
--- a/js/src/jsapi-tests/testArgumentsObject.cpp +++ b/js/src/jsapi-tests/testArgumentsObject.cpp @@ -2,16 +2,18 @@ * vim: set ts=8 sts=4 et sw=4 tw=99: */ /* 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 "jsapi-tests/tests.h" +#include "jsobjinlines.h" + #include "vm/ArgumentsObject-inl.h" using namespace js; static const char NORMAL_ZERO[] = "function f() { return arguments; }"; static const char NORMAL_ONE[] = "function f(a) { return arguments; }";
--- a/js/src/jsapi-tests/testChromeBuffer.cpp +++ b/js/src/jsapi-tests/testChromeBuffer.cpp @@ -68,17 +68,17 @@ BEGIN_TEST(testChromeBuffer) "trusted", 1, ¶mName, bytes, strlen(bytes), "", 0)); trusted_fun = JS_GetFunctionObject(fun); if (!JS_AddNamedObjectRoot(cx, &trusted_fun, "trusted-function")) return false; } JS::RootedValue v(cx, JS::ObjectValue(*trusted_fun)); - CHECK(JS_WrapValue(cx, v.address())); + CHECK(JS_WrapValue(cx, &v)); const char *paramName = "trusted"; const char *bytes = "try { " " return untrusted(trusted); " "} catch (e) { " " try { " " return trusted(100); " " } catch(e) { " @@ -109,17 +109,17 @@ BEGIN_TEST(testChromeBuffer) JS::HandleObject global = JS::HandleObject::fromMarkedLocation(&trusted_glob); CHECK(fun = JS_CompileFunctionForPrincipals(cx, global, &system_principals, "trusted", 1, ¶mName, bytes, strlen(bytes), "", 0)); trusted_fun = JS_GetFunctionObject(fun); } JS::RootedValue v(cx, JS::ObjectValue(*trusted_fun)); - CHECK(JS_WrapValue(cx, v.address())); + CHECK(JS_WrapValue(cx, &v)); const char *paramName = "trusted"; const char *bytes = "try { " " return untrusted(trusted); " "} catch (e) { " " return trusted(untrusted); " "} "; CHECK(fun = JS_CompileFunction(cx, global, "untrusted", 1, ¶mName,
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1150,26 +1150,22 @@ JS_WrapObject(JSContext *cx, MutableHand AssertHeapIsIdle(cx); CHECK_REQUEST(cx); if (objp) JS::ExposeGCThingToActiveJS(objp, JSTRACE_OBJECT); return cx->compartment()->wrap(cx, objp); } JS_PUBLIC_API(bool) -JS_WrapValue(JSContext *cx, jsval *vp) +JS_WrapValue(JSContext *cx, MutableHandleValue vp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); - if (vp) - JS::ExposeValueToActiveJS(*vp); - RootedValue value(cx, *vp); - bool ok = cx->compartment()->wrap(cx, &value); - *vp = value.get(); - return ok; + JS::ExposeValueToActiveJS(vp); + return cx->compartment()->wrap(cx, vp); } JS_PUBLIC_API(bool) JS_WrapId(JSContext *cx, jsid *idp) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); if (idp) { @@ -4542,16 +4538,21 @@ JS::CompileOffThread(JSContext *cx, Hand #endif } JS_PUBLIC_API(JSScript *) JS::FinishOffThreadScript(JSContext *maybecx, JSRuntime *rt, void *token) { #ifdef JS_WORKER_THREADS JS_ASSERT(CurrentThreadCanAccessRuntime(rt)); + + Maybe<AutoLastFrameCheck> lfc; + if (maybecx) + lfc.construct(maybecx); + return rt->workerThreadState->finishParseTask(maybecx, rt, token); #else MOZ_ASSUME_UNREACHABLE("Off thread compilation is not available."); #endif } JS_PUBLIC_API(JSScript *) JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *objArg, JSPrincipals *principals,
--- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1756,17 +1756,17 @@ JS_SetCompartmentPrivate(JSCompartment * extern JS_PUBLIC_API(void *) JS_GetCompartmentPrivate(JSCompartment *compartment); extern JS_PUBLIC_API(bool) JS_WrapObject(JSContext *cx, JS::MutableHandleObject objp); extern JS_PUBLIC_API(bool) -JS_WrapValue(JSContext *cx, jsval *vp); +JS_WrapValue(JSContext *cx, JS::MutableHandleValue vp); extern JS_PUBLIC_API(bool) JS_WrapId(JSContext *cx, jsid *idp); extern JS_PUBLIC_API(JSObject *) JS_TransplantObject(JSContext *cx, JS::Handle<JSObject*> origobj, JS::Handle<JSObject*> target); extern JS_PUBLIC_API(bool)
--- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -1099,17 +1099,17 @@ Evaluate(JSContext *cx, unsigned argc, j return false; args.rval().setString(str); return true; } return false; } } - return JS_WrapValue(cx, vp); + return JS_WrapValue(cx, args.rval()); } static JSString * FileAsString(JSContext *cx, const char *pathname) { FILE *file; RootedString str(cx); size_t len, cc; @@ -3772,17 +3772,17 @@ DecompileThisScript(JSContext *cx, unsig { JSAutoCompartment ac(cx, script); JSString *result = JS_DecompileScript(cx, script, "test", 0); if (!result) return false; args.rval().setString(result); } - return JS_WrapValue(cx, vp); + return JS_WrapValue(cx, args.rval()); } static bool ThisFilename(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); RootedScript script (cx); if (!JS_DescribeScriptedCaller(cx, &script, nullptr) || !script->filename()) {
--- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -391,17 +391,17 @@ js::RunScript(JSContext *cx, RunState &s SPSEntryMarker marker(cx->runtime()); #ifdef JS_ION if (jit::IsIonEnabled(cx)) { jit::MethodStatus status = jit::CanEnter(cx, state); if (status == jit::Method_Error) return false; if (status == jit::Method_Compiled) { - jit::IonExecStatus status = jit::Cannon(cx, state); + jit::IonExecStatus status = jit::IonCannon(cx, state); return !IsErrorStatus(status); } } if (jit::IsBaselineEnabled(cx)) { jit::MethodStatus status = jit::CanEnterBaselineMethod(cx, state); if (status == jit::Method_Error) return false; @@ -2484,17 +2484,17 @@ BEGIN_CASE(JSOP_FUNCALL) if (newType) state.setUseNewType(); if (!newType && jit::IsIonEnabled(cx)) { jit::MethodStatus status = jit::CanEnter(cx, state); if (status == jit::Method_Error) goto error; if (status == jit::Method_Compiled) { - jit::IonExecStatus exec = jit::Cannon(cx, state); + jit::IonExecStatus exec = jit::IonCannon(cx, state); CHECK_BRANCH(); regs.sp = args.spAfterCall(); interpReturnOK = !IsErrorStatus(exec); goto jit_return; } } if (jit::IsBaselineEnabled(cx)) {
--- a/js/src/vm/OldDebugAPI.cpp +++ b/js/src/vm/OldDebugAPI.cpp @@ -1024,16 +1024,21 @@ class AutoPropertyDescArray }; } /* anonymous namespace */ static const char * FormatValue(JSContext *cx, const Value &vArg, JSAutoByteString &bytes) { RootedValue v(cx, vArg); + + mozilla::Maybe<AutoCompartment> ac; + if (v.isObject()) + ac.construct(cx, &v.toObject()); + JSString *str = ToString<CanGC>(cx, v); if (!str) return nullptr; const char *buf = bytes.encodeLatin1(cx, str); if (!buf) return nullptr; const char *found = strstr(buf, "function "); if (found && (found - buf <= 2)) @@ -1054,26 +1059,16 @@ FormatFrame(JSContext *cx, const NonBuil const char *filename = script->filename(); unsigned lineno = PCToLineNumber(script, pc); RootedFunction fun(cx, iter.maybeCallee()); RootedString funname(cx); if (fun) funname = fun->atom(); - RootedObject callObj(cx); - AutoPropertyDescArray callProps(cx); - - if (!iter.isJit() && (showArgs || showLocals)) { - JSAbstractFramePtr frame(Jsvalify(iter.abstractFramePtr())); - callObj = frame.callObject(cx); - if (callObj) - callProps.fetch(callObj); - } - RootedValue thisVal(cx); AutoPropertyDescArray thisProps(cx); if (iter.computeThis(cx)) { thisVal = iter.thisv(); if (showThisProps && !thisVal.isPrimitive()) thisProps.fetch(&thisVal.toObject()); } @@ -1084,118 +1079,85 @@ FormatFrame(JSContext *cx, const NonBuil } else if (fun) { buf = JS_sprintf_append(buf, "%d anonymous(", num); } else { buf = JS_sprintf_append(buf, "%d <TOP LEVEL>", num); } if (!buf) return buf; - // print the function arguments - if (showArgs && callObj) { - uint32_t namedArgCount = 0; - for (uint32_t i = 0; i < callProps->length; i++) { - JSPropertyDesc* desc = &callProps->array[i]; + if (showArgs && iter.hasArgs()) { + BindingVector bindings(cx); + if (fun && fun->isInterpreted()) { + if (!FillBindingVector(script, &bindings)) + return buf; + } + + + bool first = true; + for (unsigned i = 0; i < iter.numActualArgs(); i++) { + RootedValue arg(cx); + if (i < iter.numFormalArgs() && script->formalIsAliased(i)) { + for (AliasedFormalIter fi(script); ; fi++) { + if (fi.frameIndex() == i) { + arg = iter.callObj().aliasedVar(fi); + break; + } + } + } else if (script->argsObjAliasesFormals() && iter.hasArgsObj()) { + arg = iter.argsObj().arg(i); + } else { + arg = iter.unaliasedActual(i, DONT_CHECK_ALIASING); + } + + JSAutoByteString valueBytes; + const char *value = FormatValue(cx, arg, valueBytes); + JSAutoByteString nameBytes; const char *name = nullptr; - bool hasName = JSVAL_IS_STRING(desc->id); - if (hasName) - name = FormatValue(cx, desc->id, nameBytes); - JSAutoByteString valueBytes; - const char *value = FormatValue(cx, desc->value, valueBytes); - if (value && (name || !hasName)) { + if (i < bindings.length()) { + name = nameBytes.encodeLatin1(cx, bindings[i].name()); + if (!buf) + return NULL; + } + + if (value) { buf = JS_sprintf_append(buf, "%s%s%s%s%s%s", - namedArgCount ? ", " : "", + !first ? ", " : "", name ? name :"", name ? " = " : "", - desc->value.isString() ? "\"" : "", + arg.isString() ? "\"" : "", value ? value : "?unknown?", - desc->value.isString() ? "\"" : ""); + arg.isString() ? "\"" : ""); if (!buf) return buf; + + first = false; } else { - buf = JS_sprintf_append(buf, " <Failed to get named argument while inspecting stack frame>\n"); + buf = JS_sprintf_append(buf, " <Failed to get argument while inspecting stack frame>\n"); + if (!buf) + return buf; cx->clearPendingException(); } - namedArgCount++; - } - - // print any unnamed trailing args (found in 'arguments' object) - RootedValue val(cx); - if (JS_GetProperty(cx, callObj, "arguments", &val) && val.isObject()) { - uint32_t argCount; - RootedObject argsObj(cx, &val.toObject()); - if (JS_GetProperty(cx, argsObj, "length", &val) && - ToUint32(cx, val, &argCount) && - argCount > namedArgCount) - { - for (uint32_t k = namedArgCount; k < argCount; k++) { - char number[8]; - JS_snprintf(number, 8, "%d", (int) k); - - JSAutoByteString valueBytes; - const char *value = nullptr; - if (JS_GetProperty(cx, argsObj, number, &val) && - (value = FormatValue(cx, val, valueBytes))) - { - buf = JS_sprintf_append(buf, "%s%s%s%s", - k ? ", " : "", - val.isString() ? "\"" : "", - value ? value : "?unknown?", - val.isString() ? "\"" : ""); - if (!buf) - return buf; - } else { - buf = JS_sprintf_append(buf, " <Failed to get argument while inspecting stack frame>\n"); - cx->clearPendingException(); - } - } - } else { - buf = JS_sprintf_append(buf, " <Failed to get 'length' while inspecting stack frame>\n"); - cx->clearPendingException(); - } - } else { - buf = JS_sprintf_append(buf, " <Failed to get 'arguments' while inspecting stack frame>\n"); - cx->clearPendingException(); } } // print filename and line number buf = JS_sprintf_append(buf, "%s [\"%s\":%d]\n", fun ? ")" : "", filename ? filename : "<unknown>", lineno); if (!buf) return buf; - // print local variables - if (showLocals && callProps->array) { - for (uint32_t i = 0; i < callProps->length; i++) { - JSPropertyDesc* desc = &callProps->array[i]; - JSAutoByteString nameBytes; - JSAutoByteString valueBytes; - const char *name = FormatValue(cx, desc->id, nameBytes); - const char *value = FormatValue(cx, desc->value, valueBytes); - if (name && value) { - buf = JS_sprintf_append(buf, " %s = %s%s%s\n", - name, - desc->value.isString() ? "\"" : "", - value, - desc->value.isString() ? "\"" : ""); - if (!buf) - return buf; - } else { - buf = JS_sprintf_append(buf, " <Failed to get local while inspecting stack frame>\n"); - cx->clearPendingException(); - } - } - } + // Note: Right now we don't dump the local variables anymore, because + // that is hard to support across all the JITs etc. // print the value of 'this' if (showLocals) { if (!thisVal.isUndefined()) { JSAutoByteString thisValBytes; RootedString thisValStr(cx, ToString<CanGC>(cx, thisVal)); const char *str = nullptr; if (thisValStr &&
--- a/js/src/vm/Shape.cpp +++ b/js/src/vm/Shape.cpp @@ -1614,17 +1614,17 @@ NewObjectCache::invalidateEntriesForShap /* static */ void EmptyShape::insertInitialShape(ExclusiveContext *cx, HandleShape shape, HandleObject proto) { InitialShapeEntry::Lookup lookup(shape->getObjectClass(), TaggedProto(proto), shape->getObjectParent(), shape->getObjectMetadata(), shape->numFixedSlots(), shape->getObjectFlags()); /* Bug 929547 - we do not rekey based on metadata object moves */ - JSObject *metadata = shape->getObjectMetadata(); + DebugOnly<JSObject *> metadata = shape->getObjectMetadata(); JS_ASSERT_IF(metadata, !gc::IsInsideNursery(cx->compartment()->runtimeFromAnyThread(), metadata)); InitialShapeSet::Ptr p = cx->compartment()->initialShapes.lookup(lookup); JS_ASSERT(p); InitialShapeEntry &entry = const_cast<InitialShapeEntry &>(*p); /* The new shape had better be rooted at the old one. */
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp +++ b/js/xpconnect/loader/mozJSComponentLoader.cpp @@ -1069,17 +1069,17 @@ mozJSComponentLoader::Import(const nsACS if (targetVal.isObject()) { // If we're passing in something like a content DOM window, chances // are the caller expects the properties to end up on the object // proper and not on the Xray holder. This is dubious, but can be used // during testing. Given that dumb callers can already leak JSMs into // content by passing a raw content JS object (where Xrays aren't // possible), we aim for consistency here. Waive xray. if (WrapperFactory::IsXrayWrapper(&targetVal.toObject()) && - !WrapperFactory::WaiveXrayAndWrap(cx, targetVal.address())) + !WrapperFactory::WaiveXrayAndWrap(cx, &targetVal)) { return NS_ERROR_FAILURE; } targetObject = &targetVal.toObject(); } else if (!targetVal.isNull()) { // If targetVal isNull(), we actually want to leave targetObject null. // Not doing so breaks |make package|. return ReportOnCaller(cx, ERROR_SCOPE_OBJ, @@ -1202,17 +1202,17 @@ mozJSComponentLoader::ImportInto(const n &newEntry->location, true, &exception); mInProgressImports.Remove(key); if (NS_FAILED(rv)) { if (!exception.isUndefined()) { // An exception was thrown during compilation. Propagate it // out to our caller so they can report it. - if (!JS_WrapValue(callercx, exception.address())) + if (!JS_WrapValue(callercx, &exception)) return NS_ERROR_OUT_OF_MEMORY; JS_SetPendingException(callercx, exception); return NS_OK; } // Something failed, but we don't know what it is, guess. return NS_ERROR_FILE_NOT_FOUND; } @@ -1276,17 +1276,17 @@ mozJSComponentLoader::ImportInto(const n return NS_ERROR_FAILURE; return ReportOnCaller(cxhelper, ERROR_GETTING_SYMBOL, PromiseFlatCString(aLocation).get(), bytes.ptr()); } JSAutoCompartment target_ac(mContext, targetObj); - if (!JS_WrapValue(mContext, value.address()) || + if (!JS_WrapValue(mContext, &value) || !JS_SetPropertyById(mContext, targetObj, symbolId, value)) { JSAutoByteString bytes(mContext, JSID_TO_STRING(symbolId)); if (!bytes) return NS_ERROR_FAILURE; return ReportOnCaller(cxhelper, ERROR_SETTING_SYMBOL, PromiseFlatCString(aLocation).get(), bytes.ptr()); }
--- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp +++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp @@ -305,27 +305,29 @@ mozJSSubScriptLoader::LoadSubScript(cons return rv; if (function) { script = JS_GetFunctionScript(cx, function); } loader->NoteSubScript(script, targetObj); + RootedValue rval(cx); bool ok = false; if (function) { - ok = JS_CallFunction(cx, targetObj, function, 0, nullptr, retval); + ok = JS_CallFunction(cx, targetObj, function, 0, nullptr, rval.address()); } else { - ok = JS_ExecuteScriptVersion(cx, targetObj, script, retval, version); + ok = JS_ExecuteScriptVersion(cx, targetObj, script, rval.address(), version); } if (ok) { JSAutoCompartment rac(cx, result_obj); - if (!JS_WrapValue(cx, retval)) + if (!JS_WrapValue(cx, &rval)) return NS_ERROR_UNEXPECTED; + *retval = rval; } if (cache && ok && writeScript) { WriteCachedScript(cache, cachePath, cx, mSystemPrincipal, script); } return NS_OK; }
--- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -300,17 +300,17 @@ ExportFunction(JSContext *cx, unsigned a // we have to add it to the target scope as a property. if (!JS_DefinePropertyById(cx, targetScope, id, args.rval(), JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE)) return false; } // Finally we have to re-wrap the exported function back to the caller compartment. - if (!JS_WrapValue(cx, args.rval().address())) + if (!JS_WrapValue(cx, args.rval())) return false; return true; } static bool GetFilenameAndLineNumber(JSContext *cx, nsACString &filename, unsigned &lineno) { @@ -977,17 +977,18 @@ xpc::GlobalProperties::Define(JSContext if (btoa && !JS_DefineFunction(cx, obj, "btoa", Btoa, 1, 0)) return false; return true; } nsresult -xpc::CreateSandboxObject(JSContext *cx, jsval *vp, nsISupports *prinOrSop, SandboxOptions& options) +xpc::CreateSandboxObject(JSContext *cx, MutableHandleValue vp, nsISupports *prinOrSop, + SandboxOptions& options) { // Create the sandbox global object nsresult rv; nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv)); if (NS_FAILED(rv)) return NS_ERROR_XPC_UNEXPECTED; nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(prinOrSop); @@ -1037,17 +1038,17 @@ xpc::CreateSandboxObject(JSContext *cx, if (options.proto) { bool ok = JS_WrapObject(cx, &options.proto); if (!ok) return NS_ERROR_XPC_UNEXPECTED; if (xpc::WrapperFactory::IsXrayWrapper(options.proto) && !options.wantXrays) { RootedValue v(cx, ObjectValue(*options.proto)); - if (!xpc::WrapperFactory::WaiveXrayAndWrap(cx, v.address())) + if (!xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v)) return NS_ERROR_FAILURE; options.proto = &v.toObject(); } // Now check what sort of thing we've got in |proto| JSObject *unwrappedProto = js::UncheckedUnwrap(options.proto, false); const js::Class *unwrappedClass = js::GetObjectClass(unwrappedProto); if (IS_WN_CLASS(unwrappedClass) || @@ -1089,26 +1090,25 @@ xpc::CreateSandboxObject(JSContext *cx, !JS_DefineFunction(cx, sandbox, "evalInWindow", EvalInWindow, 2, 0) || !JS_DefineFunction(cx, sandbox, "createObjectIn", CreateObjectIn, 2, 0))) return NS_ERROR_XPC_UNEXPECTED; if (!options.globalProperties.Define(cx, sandbox)) return NS_ERROR_XPC_UNEXPECTED; } - if (vp) { - // We have this crazy behavior where wantXrays=false also implies that the - // returned sandbox is implicitly waived. We've stopped advertising it, but - // keep supporting it for now. - *vp = OBJECT_TO_JSVAL(sandbox); - if (options.wantXrays && !JS_WrapValue(cx, vp)) - return NS_ERROR_UNEXPECTED; - if (!options.wantXrays && !xpc::WrapperFactory::WaiveXrayAndWrap(cx, vp)) - return NS_ERROR_UNEXPECTED; - } + + // We have this crazy behavior where wantXrays=false also implies that the + // returned sandbox is implicitly waived. We've stopped advertising it, but + // keep supporting it for now. + vp.setObject(*sandbox); + if (options.wantXrays && !JS_WrapValue(cx, vp)) + return NS_ERROR_UNEXPECTED; + if (!options.wantXrays && !xpc::WrapperFactory::WaiveXrayAndWrap(cx, vp)) + return NS_ERROR_UNEXPECTED; // Set the location information for the new global, so that tools like // about:memory may use that information xpc::SetLocationForGlobal(sandbox, options.sandboxName); xpc::SetSandboxMetadata(cx, sandbox, options.metadata); JS_FireOnNewGlobalObject(cx, sandbox); @@ -1504,17 +1504,17 @@ nsXPCComponents_utils_Sandbox::CallOrCon SandboxOptions options(cx, optionsObject); if (calledWithOptions && !options.Parse()) return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); if (NS_FAILED(AssembleSandboxMemoryReporterName(cx, options.sandboxName))) return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); - rv = CreateSandboxObject(cx, args.rval().address(), prinOrSop, options); + rv = CreateSandboxObject(cx, args.rval(), prinOrSop, options); if (NS_FAILED(rv)) return ThrowAndFail(rv, cx, _retval); *_retval = true; return NS_OK; } @@ -1642,29 +1642,29 @@ xpc::EvalInSandbox(JSContext *cx, Handle // // Alright, we're back on the caller's cx. If an error occured, try to // wrap and set the exception. Otherwise, wrap the return value. // if (!ok) { // If we end up without an exception, it was probably due to OOM along // the way, in which case we thow. Otherwise, wrap it. - if (exn.isUndefined() || !JS_WrapValue(cx, exn.address())) + if (exn.isUndefined() || !JS_WrapValue(cx, &exn)) return NS_ERROR_OUT_OF_MEMORY; // Set the exception on our caller's cx. JS_SetPendingException(cx, exn); return NS_ERROR_FAILURE; } // Transitively apply Xray waivers if |sb| was waived. if (waiveXray) { - ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, v.address()); + ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v); } else { - ok = JS_WrapValue(cx, v.address()); + ok = JS_WrapValue(cx, &v); } NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); // Whew! rval.set(v); return NS_OK; } @@ -1705,22 +1705,23 @@ CloningFunctionForwarder(JSContext *cx, } } // JS API does not support any JSObject to JSFunction conversion, // so let's use JS_CallFunctionValue instead. RootedValue functionVal(cx); functionVal.setObject(*origFunObj); - if (!JS_CallFunctionValue(cx, nullptr, functionVal, args.length(), args.array(), vp)) + if (!JS_CallFunctionValue(cx, nullptr, functionVal, args.length(), args.array(), + args.rval().address())) return false; } // Return value must be wrapped. - return JS_WrapValue(cx, vp); + return JS_WrapValue(cx, args.rval()); } bool xpc::NewFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable, bool doclone, MutableHandleValue vp) { JSFunction *fun = js::NewFunctionByIdWithReserved(cx, doclone ? CloningFunctionForwarder : NonCloningFunctionForwarder, @@ -1743,17 +1744,17 @@ xpc::GetSandboxMetadata(JSContext *cx, H MOZ_ASSERT(IsSandbox(sandbox)); RootedValue metadata(cx); { JSAutoCompartment ac(cx, sandbox); metadata = JS_GetReservedSlot(sandbox, XPCONNECT_SANDBOX_CLASS_METADATA_SLOT); } - if (!JS_WrapValue(cx, metadata.address())) + if (!JS_WrapValue(cx, &metadata)) return NS_ERROR_UNEXPECTED; rval.set(metadata); return NS_OK; } nsresult xpc::SetSandboxMetadata(JSContext *cx, HandleObject sandbox, HandleValue metadataArg)
--- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -3208,38 +3208,38 @@ nsXPCComponents_Utils::SetWantXrays(cons NS_IMETHODIMP nsXPCComponents_Utils::GetComponentsForScope(const jsval &vscope, JSContext *cx, jsval *rval) { if (!vscope.isObject()) return NS_ERROR_INVALID_ARG; JSObject *scopeObj = js::UncheckedUnwrap(&vscope.toObject()); XPCWrappedNativeScope *scope = GetObjectScope(scopeObj); - JSObject *components = scope->GetComponentsJSObject(); + RootedObject components(cx, scope->GetComponentsJSObject()); if (!components) return NS_ERROR_FAILURE; + if (!JS_WrapObject(cx, &components)) + return NS_ERROR_FAILURE; *rval = ObjectValue(*components); - if (!JS_WrapValue(cx, rval)) - return NS_ERROR_FAILURE; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::Dispatch(const jsval &runnableArg, const jsval &scope, JSContext *cx) { // Enter the given compartment, if any, and rewrap runnable. Maybe<JSAutoCompartment> ac; RootedValue runnable(cx, runnableArg); if (scope.isObject()) { JSObject *scopeObj = js::UncheckedUnwrap(&scope.toObject()); if (!scopeObj) return NS_ERROR_FAILURE; ac.construct(cx, scopeObj); - if (!JS_WrapValue(cx, runnable.address())) + if (!JS_WrapValue(cx, &runnable)) return NS_ERROR_FAILURE; } // Get an XPCWrappedJS for |runnable|. if (!runnable.isObject()) return NS_ERROR_INVALID_ARG; nsCOMPtr<nsIRunnable> run; @@ -3338,33 +3338,35 @@ nsXPCComponents_Utils::IsXrayWrapper(con *aRetval = obj.isObject() && xpc::WrapperFactory::IsXrayWrapper(&obj.toObject()); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::WaiveXrays(const Value &aVal, JSContext *aCx, jsval *aRetval) { - *aRetval = aVal; - if (!xpc::WrapperFactory::WaiveXrayAndWrap(aCx, aRetval)) + RootedValue value(aCx, aVal); + if (!xpc::WrapperFactory::WaiveXrayAndWrap(aCx, &value)) return NS_ERROR_FAILURE; + *aRetval = value; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::UnwaiveXrays(const Value &aVal, JSContext *aCx, jsval *aRetval) { if (!aVal.isObject()) { *aRetval = aVal; return NS_OK; } - *aRetval = ObjectValue(*js::UncheckedUnwrap(&aVal.toObject())); - if (!JS_WrapValue(aCx, aRetval)) + RootedObject obj(aCx, js::UncheckedUnwrap(&aVal.toObject())); + if (!JS_WrapObject(aCx, &obj)) return NS_ERROR_FAILURE; + *aRetval = ObjectValue(*obj); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::GetClassName(const Value &aObj, bool aUnwrap, JSContext *aCx, char **aRv) { if (!aObj.isObject()) return NS_ERROR_INVALID_ARG;
--- a/js/xpconnect/src/XPCConvert.cpp +++ b/js/xpconnect/src/XPCConvert.cpp @@ -150,17 +150,17 @@ XPCConvert::NativeData2JS(MutableHandleV return false; d.setString(str); break; } case nsXPTType::T_JSVAL : { d.set(*((Value*)s)); - if (!JS_WrapValue(cx, d.address())) + if (!JS_WrapValue(cx, d)) return false; break; } default: // set the default result d.setNull();
--- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -3373,20 +3373,17 @@ XPCJSRuntime::RemoveContextCallback(xpcC JSObject * XPCJSRuntime::GetJunkScope() { if (!mJunkScope) { AutoSafeJSContext cx; SandboxOptions options; options.sandboxName.AssignASCII("XPConnect Junk Compartment"); RootedValue v(cx); - nsresult rv = CreateSandboxObject(cx, v.address(), - nsContentUtils::GetSystemPrincipal(), - options); - + nsresult rv = CreateSandboxObject(cx, &v, nsContentUtils::GetSystemPrincipal(), options); NS_ENSURE_SUCCESS(rv, nullptr); mJunkScope = js::UncheckedUnwrap(&v.toObject()); JS_AddNamedObjectRoot(cx, &mJunkScope, "XPConnect Junk Compartment"); } return mJunkScope; }
--- a/js/xpconnect/src/XPCVariant.cpp +++ b/js/xpconnect/src/XPCVariant.cpp @@ -386,29 +386,29 @@ XPCVariant::VariantDataToJS(nsIVariant* RootedValue realVal(cx); nsresult rv = variant->GetAsJSVal(realVal.address()); if (NS_SUCCEEDED(rv) && (JSVAL_IS_PRIMITIVE(realVal) || type == nsIDataType::VTYPE_ARRAY || type == nsIDataType::VTYPE_EMPTY_ARRAY || type == nsIDataType::VTYPE_ID)) { - if (!JS_WrapValue(cx, realVal.address())) + if (!JS_WrapValue(cx, &realVal)) return false; pJSVal.set(realVal); return true; } nsCOMPtr<XPCVariant> xpcvariant = do_QueryInterface(variant); if (xpcvariant && xpcvariant->mReturnRawObject) { MOZ_ASSERT(type == nsIDataType::VTYPE_INTERFACE || type == nsIDataType::VTYPE_INTERFACE_IS, "Weird variant"); - if (!JS_WrapValue(cx, realVal.address())) + if (!JS_WrapValue(cx, &realVal)) return false; pJSVal.set(realVal); return true; } // else, it's an object and we really need to double wrap it if we've // already decided that its 'natural' type is as some sort of interface.
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp +++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp @@ -129,32 +129,34 @@ GetDoubleWrappedJSObject(XPCCallContext& } // This is the getter native function we use to handle 'wrappedJSObject' for // double wrapped JSObjects. static bool XPC_WN_DoubleWrappedGetter(JSContext *cx, unsigned argc, jsval *vp) { + CallArgs args = CallArgsFromVp(argc, vp); + RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); if (!obj) return false; XPCCallContext ccx(JS_CALLER, cx, obj); XPCWrappedNative* wrapper = ccx.GetWrapper(); THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); MOZ_ASSERT(JS_TypeOfValue(cx, JS_CALLEE(cx, vp)) == JSTYPE_FUNCTION, "bad function"); RootedObject realObject(cx, GetDoubleWrappedJSObject(ccx, wrapper)); if (!realObject) { // This is pretty unexpected at this point. The object originally // responded to this get property call and now gives no object. // XXX Should this throw something at the caller? - *vp = JSVAL_NULL; + args.rval().setNull(); return true; } // It is a double wrapped object. Figure out if the caller // is allowed to see it. nsIXPCSecurityManager* sm = nsXPConnect::XPConnect()->GetDefaultSecurityManager(); if (sm) { @@ -173,18 +175,19 @@ XPC_WN_DoubleWrappedGetter(JSContext *cx wrapper->GetIdentityObject(), wrapper->GetClassInfo(), id, wrapper->GetSecurityInfoAddr()))) { // The SecurityManager should have set an exception. return false; } } } - *vp = OBJECT_TO_JSVAL(realObject); - return JS_WrapValue(cx, vp); + + args.rval().setObject(*realObject); + return JS_WrapValue(cx, args.rval()); } /***************************************************************************/ // This is our shared function to define properties on our JSObjects. /* * NOTE:
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp +++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp @@ -255,18 +255,18 @@ XPCWrappedNativeScope::EnsureXBLScope(JS nsIPrincipal *principal = GetPrincipal(); nsCOMPtr<nsIExpandedPrincipal> ep; MOZ_ASSERT(!(ep = do_QueryInterface(principal))); nsTArray< nsCOMPtr<nsIPrincipal> > principalAsArray(1); principalAsArray.AppendElement(principal); ep = new nsExpandedPrincipal(principalAsArray); // Create the sandbox. - JS::RootedValue v(cx, JS::UndefinedValue()); - nsresult rv = CreateSandboxObject(cx, v.address(), ep, options); + RootedValue v(cx); + nsresult rv = CreateSandboxObject(cx, &v, ep, options); NS_ENSURE_SUCCESS(rv, nullptr); mXBLScope = &v.toObject(); // Tag it. EnsureCompartmentPrivate(js::UncheckedUnwrap(mXBLScope))->scope->mIsXBLScope = true; // Good to go! return mXBLScope;
--- a/js/xpconnect/src/XPCWrapper.cpp +++ b/js/xpconnect/src/XPCWrapper.cpp @@ -32,39 +32,39 @@ UnwrapNW(JSContext *cx, unsigned argc, j JS::RootedValue v(cx, JS_ARGV(cx, vp)[0]); if (!v.isObject() || !js::IsWrapper(&v.toObject())) { JS_SET_RVAL(cx, vp, v); return true; } if (AccessCheck::wrapperSubsumes(&v.toObject())) { - bool ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, v.address()); + bool ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v); NS_ENSURE_TRUE(ok, false); } JS_SET_RVAL(cx, vp, v); return true; } static bool XrayWrapperConstructor(JSContext *cx, unsigned argc, jsval *vp) { - if (argc == 0) { + JS::CallArgs args = CallArgsFromVp(argc, vp); + if (args.length() == 0) { return ThrowException(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx); } - JS::RootedValue v(cx, JS_ARGV(cx, vp)[0]); - if (!v.isObject()) { - JS_SET_RVAL(cx, vp, v); + if (!args[0].isObject()) { + args.rval().set(args[0]); return true; } - *vp = JS::ObjectValue(*js::UncheckedUnwrap(&v.toObject())); - return JS_WrapValue(cx, vp); + args.rval().setObject(*js::UncheckedUnwrap(&args[0].toObject())); + return JS_WrapValue(cx, args.rval()); } // static bool AttachNewConstructorObject(JSContext *aCx, JS::HandleObject aGlobalObject) { // Pushing a JSContext calls ActivateDebugger which calls this function, so // we can't use an AutoJSContext here until JSD is gone. JSAutoCompartment ac(aCx, aGlobalObject);
--- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -908,20 +908,19 @@ nsXPConnect::ClearAllWrappedNativeSecuri } NS_IMETHODIMP nsXPConnect::CreateSandbox(JSContext *cx, nsIPrincipal *principal, nsIXPConnectJSObjectHolder **_retval) { *_retval = nullptr; - RootedValue rval(cx, JSVAL_VOID); - + RootedValue rval(cx); SandboxOptions options; - nsresult rv = CreateSandboxObject(cx, rval.address(), principal, options); + nsresult rv = CreateSandboxObject(cx, &rval, principal, options); MOZ_ASSERT(NS_FAILED(rv) || !JSVAL_IS_PRIMITIVE(rval), "Bad return value from xpc_CreateSandboxObject()!"); if (NS_SUCCEEDED(rv) && !JSVAL_IS_PRIMITIVE(rval)) { *_retval = XPCJSObjectHolder::newHolder(JSVAL_TO_OBJECT(rval)); NS_ENSURE_TRUE(*_retval, NS_ERROR_OUT_OF_MEMORY); NS_ADDREF(*_retval);
--- a/js/xpconnect/src/qsgen.py +++ b/js/xpconnect/src/qsgen.py @@ -674,17 +674,17 @@ resultConvTemplates = { '[astring]': " return xpc::StringToJsval(cx, result, ${jsvalPtr});\n", '[domstring]': " return xpc::StringToJsval(cx, result, ${jsvalPtr});\n", '[jsval]': " ${jsvalPtr}.set(result);\n" - " return JS_WrapValue(cx, ${jsvalPtr}.address());\n" + " return JS_WrapValue(cx, ${jsvalPtr});\n" } def isVariantType(t): return isSpecificInterfaceType(t, 'nsIVariant') def writeResultConv(f, type, jsvalPtr, jsvalRef): """ Emit code to convert the C++ variable `result` to a jsval.
--- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -3687,17 +3687,17 @@ CreateGlobalObject(JSContext *cx, const // system using EvalInSandbox(). Takes the JSContext on which to // do setup etc on, puts the sandbox object in *vp (which must be // rooted by the caller), and uses the principal that's either // directly passed in prinOrSop or indirectly as an // nsIScriptObjectPrincipal holding the principal. If no principal is // reachable through prinOrSop, a new null principal will be created // and used. nsresult -CreateSandboxObject(JSContext *cx, jsval *vp, nsISupports *prinOrSop, +CreateSandboxObject(JSContext *cx, JS::MutableHandleValue vp, nsISupports *prinOrSop, xpc::SandboxOptions& options); // Helper for evaluating scripts in a sandbox object created with // CreateSandboxObject(). The caller is responsible of ensuring // that *rval doesn't get collected during the call or usage after the // call. This helper will use filename and lineNo for error reporting, // and if no filename is provided it will use the codebase from the // principal and line number 1 as a fallback. if returnStringOnly is // true, then the result in *rval, or the exception in cx->exception
--- a/js/xpconnect/tests/mochitest/mochitest.ini +++ b/js/xpconnect/tests/mochitest/mochitest.ini @@ -32,16 +32,18 @@ support-files = file_mozMatchesSelector.html file_nodelists.html file_wrappers-2.html inner.html test1_bug629331.html test2_bug629331.html [test_asmjs.html] +# bug 929498 +skip-if = os == 'android' [test_bug384632.html] [test_bug390488.html] [test_bug393269.html] [test_bug396851.html] [test_bug428021.html] [test_bug446584.html] [test_bug462428.html] [test_bug478438.html]
--- a/js/xpconnect/wrappers/WaiveXrayWrapper.cpp +++ b/js/xpconnect/wrappers/WaiveXrayWrapper.cpp @@ -13,24 +13,24 @@ using namespace JS; namespace xpc { static bool WaiveAccessors(JSContext *cx, JS::MutableHandle<JSPropertyDescriptor> desc) { if (desc.hasGetterObject() && desc.getterObject()) { RootedValue v(cx, JS::ObjectValue(*desc.getterObject())); - if (!WrapperFactory::WaiveXrayAndWrap(cx, v.address())) + if (!WrapperFactory::WaiveXrayAndWrap(cx, &v)) return false; desc.setGetterObject(&v.toObject()); } if (desc.hasSetterObject() && desc.setterObject()) { RootedValue v(cx, JS::ObjectValue(*desc.setterObject())); - if (!WrapperFactory::WaiveXrayAndWrap(cx, v.address())) + if (!WrapperFactory::WaiveXrayAndWrap(cx, &v)) return false; desc.setSetterObject(&v.toObject()); } return true; } WaiveXrayWrapper::WaiveXrayWrapper(unsigned flags) : js::CrossCompartmentWrapper(flags) { @@ -41,54 +41,54 @@ WaiveXrayWrapper::~WaiveXrayWrapper() } bool WaiveXrayWrapper::getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, JS::MutableHandle<JSPropertyDescriptor> desc, unsigned flags) { return CrossCompartmentWrapper::getPropertyDescriptor(cx, wrapper, id, desc, flags) && - WrapperFactory::WaiveXrayAndWrap(cx, desc.value().address()) && WaiveAccessors(cx, desc); + WrapperFactory::WaiveXrayAndWrap(cx, desc.value()) && WaiveAccessors(cx, desc); } bool WaiveXrayWrapper::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, JS::MutableHandle<JSPropertyDescriptor> desc, unsigned flags) { return CrossCompartmentWrapper::getOwnPropertyDescriptor(cx, wrapper, id, desc, flags) && - WrapperFactory::WaiveXrayAndWrap(cx, desc.value().address()) && WaiveAccessors(cx, desc); + WrapperFactory::WaiveXrayAndWrap(cx, desc.value()) && WaiveAccessors(cx, desc); } bool WaiveXrayWrapper::get(JSContext *cx, HandleObject wrapper, HandleObject receiver, HandleId id, MutableHandleValue vp) { return CrossCompartmentWrapper::get(cx, wrapper, receiver, id, vp) && - WrapperFactory::WaiveXrayAndWrap(cx, vp.address()); + WrapperFactory::WaiveXrayAndWrap(cx, vp); } bool WaiveXrayWrapper::call(JSContext *cx, HandleObject wrapper, const JS::CallArgs &args) { return CrossCompartmentWrapper::call(cx, wrapper, args) && - WrapperFactory::WaiveXrayAndWrap(cx, args.rval().address()); + WrapperFactory::WaiveXrayAndWrap(cx, args.rval()); } bool WaiveXrayWrapper::construct(JSContext *cx, HandleObject wrapper, const JS::CallArgs &args) { return CrossCompartmentWrapper::construct(cx, wrapper, args) && - WrapperFactory::WaiveXrayAndWrap(cx, args.rval().address()); + WrapperFactory::WaiveXrayAndWrap(cx, args.rval()); } // NB: This is important as the other side of a handshake with FieldGetter. See // nsXBLProtoImplField.cpp. bool WaiveXrayWrapper::nativeCall(JSContext *cx, JS::IsAcceptableThis test, JS::NativeImpl impl, JS::CallArgs args) { return CrossCompartmentWrapper::nativeCall(cx, test, impl, args) && - WrapperFactory::WaiveXrayAndWrap(cx, args.rval().address()); + WrapperFactory::WaiveXrayAndWrap(cx, args.rval()); } }
--- a/js/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/xpconnect/wrappers/WrapperFactory.cpp @@ -539,25 +539,25 @@ WrapperFactory::WrapForSameCompartment(J return wrapper; } // Call WaiveXrayAndWrap when you have a JS object that you don't want to be // wrapped in an Xray wrapper. cx->compartment is the compartment that will be // using the returned object. If the object to be wrapped is already in the // correct compartment, then this returns the unwrapped object. bool -WrapperFactory::WaiveXrayAndWrap(JSContext *cx, jsval *vp) +WrapperFactory::WaiveXrayAndWrap(JSContext *cx, MutableHandleValue vp) { - if (JSVAL_IS_PRIMITIVE(*vp)) + if (vp.isPrimitive()) return JS_WrapValue(cx, vp); - JSObject *obj = js::UncheckedUnwrap(JSVAL_TO_OBJECT(*vp)); + JSObject *obj = js::UncheckedUnwrap(&vp.toObject()); MOZ_ASSERT(!js::IsInnerObject(obj)); if (js::IsObjectInContextCompartment(obj, cx)) { - *vp = OBJECT_TO_JSVAL(obj); + vp.setObject(*obj); return true; } // Even though waivers have no effect on access by scopes that don't subsume // the underlying object, good defense-in-depth dictates that we should avoid // handing out waivers to callers that can't use them. The transitive waiving // machinery unconditionally calls WaiveXrayAndWrap on return values from // waived functions, even though the return value might be not be same-origin @@ -565,17 +565,17 @@ WrapperFactory::WaiveXrayAndWrap(JSConte // |cx|, we should check whether the caller has any business with waivers // to things in |obj|'s compartment. JSCompartment *target = js::GetContextCompartment(cx); JSCompartment *origin = js::GetObjectCompartment(obj); obj = AccessCheck::subsumes(target, origin) ? WaiveXray(cx, obj) : obj; if (!obj) return false; - *vp = OBJECT_TO_JSVAL(obj); + vp.setObject(*obj); return JS_WrapValue(cx, vp); } JSObject * WrapperFactory::WrapSOWObject(JSContext *cx, JSObject *objArg) { RootedObject obj(cx, objArg); RootedObject proto(cx);
--- a/js/xpconnect/wrappers/WrapperFactory.h +++ b/js/xpconnect/wrappers/WrapperFactory.h @@ -58,17 +58,17 @@ class WrapperFactory { JS::HandleObject parent, unsigned flags); // Wrap an object for same-compartment access. static JSObject *WrapForSameCompartment(JSContext *cx, JS::HandleObject obj); // Wrap wrapped object into a waiver wrapper and then re-wrap it. - static bool WaiveXrayAndWrap(JSContext *cx, jsval *vp); + static bool WaiveXrayAndWrap(JSContext *cx, JS::MutableHandleValue vp); // Wrap a (same compartment) object in a SOW. static JSObject *WrapSOWObject(JSContext *cx, JSObject *obj); // Return true if this is a Components object. static bool IsComponentsObject(JSObject *obj); // Wrap a (same compartment) Components object.
--- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -765,17 +765,17 @@ XPCWrappedNativeXrayTraits::resolveNativ // Without a wrapper the function would live on the prototype. Since we // don't have one, we have to avoid calling the scriptable helper's // GetProperty method for this property, so stub out the getter and // setter here explicitly. desc.setGetter(JS_PropertyStub); desc.setSetter(JS_StrictPropertyStub); } - if (!JS_WrapValue(cx, desc.value().address()) || !JS_WrapValue(cx, fval.address())) + if (!JS_WrapValue(cx, desc.value()) || !JS_WrapValue(cx, &fval)) return false; if (desc.hasGetterObject()) desc.setGetterObject(&fval.toObject()); if (desc.hasSetterObject()) desc.setSetterObject(&fval.toObject()); // Define the property. @@ -788,17 +788,17 @@ wrappedJSObject_getter(JSContext *cx, Ha { if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper)) { JS_ReportError(cx, "Unexpected object"); return false; } vp.set(OBJECT_TO_JSVAL(wrapper)); - return WrapperFactory::WaiveXrayAndWrap(cx, vp.address()); + return WrapperFactory::WaiveXrayAndWrap(cx, vp); } bool XrayTraits::resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, HandleObject wrapper, HandleObject holder, HandleId id, MutableHandle<JSPropertyDescriptor> desc, unsigned flags) { desc.object().set(nullptr); @@ -1100,17 +1100,17 @@ DOMXrayTraits::call(JSContext *cx, Handl if (!clasp->call(cx, args.length(), args.base())) return false; } else { // This is only reached for WebIDL instance objects, and in practice // only for plugins. Just call them on the content compartment. if (!baseInstance.call(cx, wrapper, args)) return false; } - return JS_WrapValue(cx, args.rval().address()); + return JS_WrapValue(cx, args.rval()); } bool DOMXrayTraits::construct(JSContext *cx, HandleObject wrapper, const JS::CallArgs &args, js::Wrapper& baseInstance) { RootedObject obj(cx, getTargetObject(wrapper)); MOZ_ASSERT(mozilla::dom::HasConstructor(obj)); @@ -1122,17 +1122,17 @@ DOMXrayTraits::construct(JSContext *cx, return false; } if (!clasp->construct(cx, args.length(), args.base())) return false; } else { if (!baseInstance.construct(cx, wrapper, args)) return false; } - if (!args.rval().isObject() || !JS_WrapValue(cx, args.rval().address())) + if (!args.rval().isObject() || !JS_WrapValue(cx, args.rval())) return false; return true; } void DOMXrayTraits::preserveWrapper(JSObject *target) { nsISupports *identity = mozilla::dom::UnwrapDOMObjectToISupports(target); @@ -1225,36 +1225,43 @@ HasNativeProperty(JSContext *cx, HandleO return false; *hasProp = !!desc.object(); return true; } } // namespace XrayUtils static bool -XrayToString(JSContext *cx, unsigned argc, jsval *vp) +XrayToString(JSContext *cx, unsigned argc, Value *vp) { - RootedObject wrapper(cx, JS_THIS_OBJECT(cx, vp)); + CallArgs args = CallArgsFromVp(argc, vp); + + if (!args.thisv().isObject()) { + JS_ReportError(cx, "XrayToString called on an incompatible object"); + return false; + } + + RootedObject wrapper(cx, &args.thisv().toObject()); if (!wrapper) return false; if (IsWrapper(wrapper) && GetProxyHandler(wrapper) == &sandboxCallableProxyHandler) { wrapper = xpc::SandboxCallableProxyHandler::wrappedObject(wrapper); } if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper)) { JS_ReportError(cx, "XrayToString called on an incompatible object"); return false; } RootedObject obj(cx, XrayTraits::getTargetObject(wrapper)); static const char start[] = "[object XrayWrapper "; static const char end[] = "]"; if (UseDOMXray(obj)) - return NativeToString(cx, wrapper, obj, start, end, vp); + return NativeToString(cx, wrapper, obj, start, end, args.rval()); nsAutoString result; result.AppendASCII(start); XPCCallContext ccx(JS_CALLER, cx, obj); XPCWrappedNative *wn = XPCWrappedNativeXrayTraits::getWN(wrapper); char *wrapperStr = wn->ToString(); if (!wrapperStr) { @@ -1266,17 +1273,17 @@ XrayToString(JSContext *cx, unsigned arg result.AppendASCII(end); JSString *str = JS_NewUCStringCopyN(cx, reinterpret_cast<const jschar *>(result.get()), result.Length()); if (!str) return false; - *vp = STRING_TO_JSVAL(str); + args.rval().setString(str); return true; } #ifdef DEBUG static void DEBUG_CheckXBLCallable(JSContext *cx, JSObject *obj) {
--- a/layout/forms/nsTextControlFrame.cpp +++ b/layout/forms/nsTextControlFrame.cpp @@ -1236,22 +1236,16 @@ nsTextControlFrame::SetInitialChildList( statefulFrame->RestoreState(&fakePresState); Properties().Remove(ContentScrollPos()); delete contentScrollPos; } } return rv; } -bool -nsTextControlFrame::IsScrollable() const -{ - return !IsSingleLineTextControl(); -} - void nsTextControlFrame::SetValueChanged(bool aValueChanged) { nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); NS_ASSERTION(txtCtrl, "Content not a text control element"); if (mUsePlaceholder) { nsWeakFrame weakFrame(this);
--- a/layout/forms/nsTextControlFrame.h +++ b/layout/forms/nsTextControlFrame.h @@ -34,18 +34,16 @@ public: NS_DECLARE_FRAME_PROPERTY(ContentScrollPos, DestroyPoint) nsTextControlFrame(nsIPresShell* aShell, nsStyleContext* aContext); virtual ~nsTextControlFrame(); virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE; virtual nsIScrollableFrame* GetScrollTargetFrame() MOZ_OVERRIDE { - if (!IsScrollable()) - return nullptr; return do_QueryFrame(GetFirstPrincipalChild()); } virtual nscoord GetMinWidth(nsRenderingContext* aRenderingContext) MOZ_OVERRIDE; virtual nscoord GetPrefWidth(nsRenderingContext* aRenderingContext) MOZ_OVERRIDE; virtual nsSize ComputeAutoSize(nsRenderingContext *aRenderingContext, nsSize aCBSize, nscoord aAvailableWidth, @@ -230,23 +228,16 @@ protected: private: nsTextControlFrame* mFrame; }; nsresult OffsetToDOMPoint(int32_t aOffset, nsIDOMNode** aResult, int32_t* aPosition); /** - * Find out whether this control is scrollable (i.e. if it is not a single - * line text control) - * @return whether this control is scrollable - */ - bool IsScrollable() const; - - /** * Update the textnode under our anonymous div to show the new * value. This should only be called when we have no editor yet. * @throws NS_ERROR_UNEXPECTED if the div has no text content */ nsresult UpdateValueDisplay(bool aNotify, bool aBeforeEditorInit = false, const nsAString *aValue = nullptr);
--- a/layout/forms/test/mochitest.ini +++ b/layout/forms/test/mochitest.ini @@ -27,13 +27,14 @@ support-files = [test_bug571352.html] [test_bug572406.html] [test_bug572649.html] [test_bug595310.html] [test_bug620936.html] [test_bug644542.html] [test_bug672810.html] [test_bug704049.html] +[test_bug717878_input_scroll.html] [test_bug869314.html] [test_bug903715.html] [test_listcontrol_search.html] [test_select_prevent_default.html] [test_textarea_resize.html]
new file mode 100644 --- /dev/null +++ b/layout/forms/test/test_bug717878_input_scroll.html @@ -0,0 +1,82 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=717878 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 717878</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717878">Mozilla Bug 717878</a> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<!-- size=10 and monospace font ensure there's no overflow in either direction --> +<input id="no-overflow" type="text" + size="10" + style=" + font-family: monospace; + font-size: 1em;" + value="Short"> +<!-- size=10, monospace font, and height=0.5em ensure overflow in both directions --> +<input id="overflow" type="text" + size="10" + style=" + font-family: monospace; + font-size: 1em; + height: 0.5em;" + value="This is a long string"> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 717878 **/ + +/** + * Test an element's scroll properties for correctness + * + * @param element Element to test + * @param prop Specify the property to test, + * i.e. "scrollLeft" or "scrollTop" + * @param propMax Specify the scrollMax property to test, + * i.e. "scrollLeftMax" or "scrollTopMax" + * @param is_overflow Specify whether the element is + * scrollable in the above direction + */ +function test_scroll(element, scroll, scrollMax, is_overflow) { + + is(element[scroll], 0, element.id + " initial " + scroll + " != 0"); + if (is_overflow) { + isnot(element[scrollMax], 0, element.id + " " + scrollMax + " == 0"); + } else { + is(element[scrollMax], 0, element.id + " " + scrollMax + " != 0"); + } + + element[scroll] = 10; + if (is_overflow) { + isnot(element[scroll], 0, element.id + " unable to scroll " + scroll); + } else { + is(element[scroll], 0, element.id + " able to scroll " + scroll); + } + + element[scroll] = element[scrollMax]; + is(element[scroll], element[scrollMax], element.id + " did not scroll to " + scrollMax); + + element[scroll] = element[scrollMax] + 10; + is(element[scroll], element[scrollMax], element.id + " scrolled past " + scrollMax); +} + +var no_overflow = document.getElementById("no-overflow"); +test_scroll(no_overflow, "scrollLeft", "scrollLeftMax", /* is_overflow */ false); +test_scroll(no_overflow, "scrollTop", "scrollTopMax", /* is_overflow */ false); + +var overflow = document.getElementById("overflow"); +test_scroll(overflow, "scrollLeft", "scrollLeftMax", /* is_overflow */ true); +test_scroll(overflow, "scrollTop", "scrollTopMax", /* is_overflow */ true); + +</script> +</pre> +</body> +</html>
--- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -5237,18 +5237,18 @@ nsFrame::UpdateOverflow() { MOZ_ASSERT(!(mState & NS_FRAME_SVG_LAYOUT) || !(mState & NS_FRAME_IS_NONDISPLAY), "Non-display SVG do not maintain visual overflow rects"); nsRect rect(nsPoint(0, 0), GetSize()); nsOverflowAreas overflowAreas(rect, rect); - bool isBox = IsBoxFrame() || IsBoxWrapped(); - if (!isBox || (!IsCollapsed() && !DoesClipChildren())) { + if (!DoesClipChildren() && + !(IsCollapsed() && (IsBoxFrame() || IsBoxWrapped()))) { nsLayoutUtils::UnionChildOverflow(this, overflowAreas); } if (FinishAndStoreOverflow(overflowAreas, GetSize())) { nsView* view = GetView(); if (view) { uint32_t flags = 0; GetLayoutFlags(flags);
new file mode 100644 --- /dev/null +++ b/layout/reftests/bugs/928607-1-ref.html @@ -0,0 +1,8 @@ +<DOCTYPE HTML> +<html> +<body> +<div style="overflow:hidden; border:1px solid black;"> + <div style="position:relative; left:50px; height:10px; background:blue;"></div> +</div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bugs/928607-1.html @@ -0,0 +1,15 @@ +<DOCTYPE HTML> +<html class="reftest-wait"> +<body> +<div style="overflow:hidden; border:1px solid black;"> + <div id="d" style="position:relative; left:10px; height:10px; background:blue;"></div> +</div> +<script> +function doTest() { + d.style.left = "50px"; + document.documentElement.removeAttribute("class"); +} +window.addEventListener("MozReftestInvalidate", doTest); +</script> +</body> +</html>
--- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1770,8 +1770,9 @@ test-pref(layout.css.flexbox.enabled,tru == 883987-1f.html 883987-1-ref.html == 890495-1.html 890495-1-ref.html == 894931-1.html 894931-1-ref.html == 897491-1.html 897491-1-ref.html == 897491-2.html 897491-2-ref.html fuzzy(1,10000) fuzzy-if(Android&&AndroidVersion>=15,5,10000) == 902330-1.html 902330-1-ref.html fuzzy-if(Android,8,400) == 906199-1.html 906199-1-ref.html == 921716-1.html 921716-1-ref.html +fuzzy-if(cocoaWidget,1,40) == 928607-1.html 928607-1-ref.html
--- a/layout/style/Loader.cpp +++ b/layout/style/Loader.cpp @@ -42,16 +42,17 @@ #include "nsIStyleSheetLinkingElement.h" #include "nsICSSLoaderObserver.h" #include "nsCSSParser.h" #include "mozilla/css/ImportRule.h" #include "nsThreadUtils.h" #include "nsGkAtoms.h" #include "nsIThreadInternal.h" #include "nsCrossSiteListenerProxy.h" +#include "nsINetworkSeer.h" #ifdef MOZ_XUL #include "nsXULPrototypeCache.h" #endif #include "nsIMediaList.h" #include "nsIDOMStyleSheet.h" #include "nsError.h" @@ -1428,16 +1429,22 @@ Loader::LoadSheet(SheetLoadData* aLoadDa nsCOMPtr<nsIUnicharStreamLoader> streamLoader; rv = NS_NewUnicharStreamLoader(getter_AddRefs(streamLoader), aLoadData); if (NS_FAILED(rv)) { LOG_ERROR((" Failed to create stream loader for sync load")); SheetComplete(aLoadData, rv); return rv; } + if (mDocument) { + mozilla::net::SeerLearn(aLoadData->mURI, mDocument->GetDocumentURI(), + nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, + mDocument); + } + // Just load it nsCOMPtr<nsIInputStream> stream; nsCOMPtr<nsIChannel> channel; rv = NS_OpenURI(getter_AddRefs(stream), aLoadData->mURI, nullptr, nullptr, nullptr, nsIRequest::LOAD_NORMAL, getter_AddRefs(channel)); if (NS_FAILED(rv)) { LOG_ERROR((" Failed to open URI synchronously")); @@ -1601,16 +1608,21 @@ Loader::LoadSheet(SheetLoadData* aLoadDa SheetComplete(aLoadData, rv); return rv; } channelListener = corsListener; } else { channelListener = streamLoader; } + if (mDocument) { + mozilla::net::SeerLearn(aLoadData->mURI, mDocument->GetDocumentURI(), + nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, mDocument); + } + rv = channel->AsyncOpen(channelListener, nullptr); #ifdef DEBUG mSyncCallback = false; #endif if (NS_FAILED(rv)) { LOG_ERROR((" Failed to create stream loader"));
--- a/layout/style/nsFontFaceLoader.cpp +++ b/layout/style/nsFontFaceLoader.cpp @@ -24,16 +24,17 @@ #include "nsIScriptSecurityManager.h" #include "nsIContentPolicy.h" #include "nsContentPolicyUtils.h" #include "nsCrossSiteListenerProxy.h" #include "nsIContentSecurityPolicy.h" #include "nsIDocShell.h" #include "nsIWebNavigation.h" +#include "nsINetworkSeer.h" #include "nsIConsoleService.h" #include "nsStyleSet.h" #include "nsPrintfCString.h" #include "mozilla/gfx/2D.h" using namespace mozilla; @@ -370,16 +371,20 @@ nsUserFontSet::StartLoad(gfxMixedFontFam #endif nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel)); if (httpChannel) httpChannel->SetReferrer(aFontFaceSrc->mReferrer); rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader); NS_ENSURE_SUCCESS(rv, rv); + nsIDocument *document = ps->GetDocument(); + mozilla::net::SeerLearn(aFontFaceSrc->mURI, document->GetDocumentURI(), + nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, loadGroup); + bool inherits = false; rv = NS_URIChainHasFlags(aFontFaceSrc->mURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, &inherits); if (NS_SUCCEEDED(rv) && inherits) { // allow data, javascript, etc URI's rv = channel->AsyncOpen(streamLoader, nullptr); } else {
--- a/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp +++ b/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp @@ -1,22 +1,31 @@ /* 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 "CSFLog.h" #include "nspr.h" +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#elif defined XP_WIN +#include <winsock2.h> +#endif + #include "AudioConduit.h" #include "nsCOMPtr.h" #include "mozilla/Services.h" #include "nsServiceManagerUtils.h" #include "nsIPrefService.h" #include "nsIPrefBranch.h" #include "nsThreadUtils.h" +#ifdef MOZILLA_INTERNAL_API +#include "Latency.h" +#endif #include "webrtc/voice_engine/include/voe_errors.h" #ifdef MOZ_WIDGET_ANDROID #include "AndroidJNIWrapper.h" #endif namespace mozilla { @@ -199,16 +208,22 @@ MediaConduitErrorCode WebrtcAudioConduit } if(!(mPtrVoEXmedia = VoEExternalMedia::GetInterface(mVoiceEngine))) { CSFLogError(logTag, "%s Unable to initialize VoEExternalMedia", __FUNCTION__); return kMediaConduitSessionNotInited; } + if(!(mPtrVoEVideoSync = VoEVideoSync::GetInterface(mVoiceEngine))) + { + CSFLogError(logTag, "%s Unable to initialize VoEVideoSync", __FUNCTION__); + return kMediaConduitSessionNotInited; + } + if (other) { mChannel = other->mChannel; } else { // init the engine with our audio device layer if(mPtrVoEBase->Init() == -1) { CSFLogError(logTag, "%s VoiceEngine Base Not Initialized", __FUNCTION__); return kMediaConduitSessionNotInited; @@ -505,16 +520,23 @@ WebrtcAudioConduit::SendAudioFrame(const // if transmission is not started .. conduit cannot insert frames if(!mEngineTransmitting) { CSFLogError(logTag, "%s Engine not transmitting ", __FUNCTION__); return kMediaConduitSessionNotInited; } +#ifdef MOZILLA_INTERNAL_API + if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) { + struct Processing insert = { TimeStamp::Now(), 0 }; + mProcessing.AppendElement(insert); + } +#endif + capture_delay = mCaptureDelay; //Insert the samples if(mPtrVoEXmedia->ExternalRecordingInsertData(audio_data, lengthSamples, samplingFreqHz, capture_delay) == -1) { int error = mPtrVoEBase->LastError(); @@ -583,29 +605,62 @@ WebrtcAudioConduit::GetAudioFrame(int16_ CSFLogError(logTag, "%s Getting audio data Failed %d", __FUNCTION__, error); if(error == VE_RUNTIME_PLAY_ERROR) { return kMediaConduitPlayoutError; } return kMediaConduitUnknownError; } +#ifdef MOZILLA_INTERNAL_API + if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) { + if (mProcessing.Length() > 0) { + unsigned int now; + mPtrVoEVideoSync->GetPlayoutTimestamp(mChannel, now); + if (static_cast<uint32_t>(now) != mLastTimestamp) { + mLastTimestamp = static_cast<uint32_t>(now); + // Find the block that includes this timestamp in the network input + while (mProcessing.Length() > 0) { + // FIX! assumes 20ms @ 48000Hz + // FIX handle wrap-around + if (mProcessing[0].mRTPTimeStamp + 20*(48000/1000) >= now) { + TimeDuration t = TimeStamp::Now() - mProcessing[0].mTimeStamp; + // Wrap-around? + int64_t delta = t.ToMilliseconds() + (now - mProcessing[0].mRTPTimeStamp)/(48000/1000); + LogTime(AsyncLatencyLogger::AudioRecvRTP, ((uint64_t) this), delta); + break; + } + mProcessing.RemoveElementAt(0); + } + } + } + } +#endif CSFLogDebug(logTag,"%s GetAudioFrame:Got samples: length %d ",__FUNCTION__, lengthSamples); return kMediaConduitNoError; } // Transport Layer Callbacks MediaConduitErrorCode WebrtcAudioConduit::ReceivedRTPPacket(const void *data, int len) { CSFLogDebug(logTag, "%s : channel %d", __FUNCTION__, mChannel); if(mEngineReceiving) { +#ifdef MOZILLA_INTERNAL_API + if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) { + // timestamp is at 32 bits in ([1]) + struct Processing insert = { TimeStamp::Now(), + ntohl(static_cast<const uint32_t *>(data)[1]) }; + mProcessing.AppendElement(insert); + } +#endif + if(mPtrVoENetwork->ReceivedRTPPacket(mChannel,data,len) == -1) { int error = mPtrVoEBase->LastError(); CSFLogError(logTag, "%s RTP Processing Error %d", __FUNCTION__, error); if(error == VE_RTP_RTCP_MODULE_ERROR) { return kMediaConduitRTPRTCPModuleError; } @@ -654,16 +709,28 @@ int WebrtcAudioConduit::SendPacket(int c if (mOtherDirection) { return mOtherDirection->SendPacket(channel, data, len); } CSFLogDebug(logTag, "%s : Asked to send RTP without an RTP sender on channel %d", __FUNCTION__, channel); return -1; } else { +#ifdef MOZILLA_INTERNAL_API + if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) { + if (mProcessing.Length() > 0) { + TimeStamp started = mProcessing[0].mTimeStamp; + mProcessing.RemoveElementAt(0); + mProcessing.RemoveElementAt(0); // 20ms packetization! Could automate this by watching sizes + TimeDuration t = TimeStamp::Now() - started; + int64_t delta = t.ToMilliseconds(); + LogTime(AsyncLatencyLogger::AudioSendRTP, ((uint64_t) this), delta); + } + } +#endif if(mTransport && (mTransport->SendRtpPacket(data, len) == NS_OK)) { CSFLogDebug(logTag, "%s Sent RTP Packet ", __FUNCTION__); return len; } else { CSFLogError(logTag, "%s RTP Packet Send Failed ", __FUNCTION__); return -1; }
--- a/media/webrtc/signaling/src/media-conduit/AudioConduit.h +++ b/media/webrtc/signaling/src/media-conduit/AudioConduit.h @@ -2,35 +2,39 @@ * 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 AUDIO_SESSION_H_ #define AUDIO_SESSION_H_ #include "mozilla/Attributes.h" +#include "mozilla/TimeStamp.h" +#include "nsTArray.h" #include "MediaConduitInterface.h" // Audio Engine Includes #include "webrtc/common_types.h" #include "webrtc/voice_engine/include/voe_base.h" #include "webrtc/voice_engine/include/voe_volume_control.h" #include "webrtc/voice_engine/include/voe_codec.h" #include "webrtc/voice_engine/include/voe_file.h" #include "webrtc/voice_engine/include/voe_network.h" #include "webrtc/voice_engine/include/voe_external_media.h" #include "webrtc/voice_engine/include/voe_audio_processing.h" +#include "webrtc/voice_engine/include/voe_video_sync.h" //Some WebRTC types for short notations using webrtc::VoEBase; using webrtc::VoENetwork; using webrtc::VoECodec; using webrtc::VoEExternalMedia; using webrtc::VoEAudioProcessing; + using webrtc::VoEVideoSync; /** This file hosts several structures identifying different aspects * of a RTP Session. */ namespace mozilla { /** @@ -142,16 +146,17 @@ public: WebrtcAudioConduit(): mOtherDirection(nullptr), mShutDown(false), mVoiceEngine(nullptr), mTransport(nullptr), mEngineTransmitting(false), mEngineReceiving(false), + mLastTimestamp(0), mChannel(-1), mCurSendCodecConfig(nullptr), mCaptureDelay(150), mEchoOn(true), mEchoCancel(webrtc::kEcAec) { } @@ -204,22 +209,33 @@ private: // conduit to die webrtc::VoiceEngine* mVoiceEngine; mozilla::RefPtr<TransportInterface> mTransport; webrtc::VoENetwork* mPtrVoENetwork; webrtc::VoEBase* mPtrVoEBase; webrtc::VoECodec* mPtrVoECodec; webrtc::VoEExternalMedia* mPtrVoEXmedia; webrtc::VoEAudioProcessing* mPtrVoEProcessing; + webrtc::VoEVideoSync* mPtrVoEVideoSync; //engine states of our interets bool mEngineTransmitting; // If true => VoiceEngine Send-subsystem is up bool mEngineReceiving; // If true => VoiceEngine Receive-subsystem is up // and playout is enabled + // Keep track of each inserted RTP block and the time it was inserted + // so we can estimate the clock time for a specific TimeStamp coming out + // (for when we send data to MediaStreamTracks). Blocks are aged out as needed. + struct Processing { + TimeStamp mTimeStamp; + uint32_t mRTPTimeStamp; // RTP timestamps received + }; + nsAutoTArray<Processing,8> mProcessing; + uint32_t mLastTimestamp; + int mChannel; RecvCodecList mRecvCodecList; AudioCodecConfig* mCurSendCodecConfig; // Current "capture" delay (really output plus input delay) int32_t mCaptureDelay; bool mEchoOn;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp @@ -19,16 +19,20 @@ #include "PeerConnectionCtx.h" #include "runnable_utils.h" #include "cpr_socket.h" #include "debug-psipcc-types.h" #include "prcvar.h" #include "mozilla/Telemetry.h" +#ifdef MOZILLA_INTERNAL_API +#include "mozilla/dom/RTCPeerConnectionBinding.h" +#endif + #include "nsIObserverService.h" #include "nsIObserver.h" #include "mozilla/Services.h" #include "StaticPtr.h" extern "C" { #include "../sipcc/core/common/thread_monitor.h" } @@ -36,16 +40,67 @@ static const char* logTag = "PeerConnect extern "C" { extern PRCondVar *ccAppReadyToStartCond; extern PRLock *ccAppReadyToStartLock; extern char ccAppReadyToStart; } namespace mozilla { + +using namespace dom; + +// Convert constraints to C structures + +#ifdef MOZILLA_INTERNAL_API +static void +Apply(const Optional<bool> &aSrc, cc_boolean_constraint_t *aDst, + bool mandatory = false) { + if (aSrc.WasPassed() && (mandatory || !aDst->was_passed)) { + aDst->was_passed = true; + aDst->value = aSrc.Value(); + aDst->mandatory = mandatory; + } +} +#endif + +MediaConstraintsExternal::MediaConstraintsExternal() { + memset(&mConstraints, 0, sizeof(mConstraints)); +} + +MediaConstraintsExternal::MediaConstraintsExternal( + const MediaConstraintsInternal &aSrc) { + cc_media_constraints_t* c = &mConstraints; + memset(c, 0, sizeof(*c)); +#ifdef MOZILLA_INTERNAL_API + Apply(aSrc.mMandatory.mOfferToReceiveAudio, &c->offer_to_receive_audio, true); + Apply(aSrc.mMandatory.mOfferToReceiveVideo, &c->offer_to_receive_video, true); + Apply(aSrc.mMandatory.mMozDontOfferDataChannel, &c->moz_dont_offer_datachannel, + true); + if (aSrc.mOptional.WasPassed()) { + const Sequence<MediaConstraintSet> &array = aSrc.mOptional.Value(); + for (uint32_t i = 0; i < array.Length(); i++) { + Apply(array[i].mOfferToReceiveAudio, &c->offer_to_receive_audio); + Apply(array[i].mOfferToReceiveVideo, &c->offer_to_receive_video); + Apply(array[i].mMozDontOfferDataChannel, &c->moz_dont_offer_datachannel); + } + } +#endif +} + +cc_media_constraints_t* +MediaConstraintsExternal::build() const { + cc_media_constraints_t* cc = (cc_media_constraints_t*) + cpr_malloc(sizeof(cc_media_constraints_t)); + if (cc) { + *cc = mConstraints; + } + return cc; +} + class PeerConnectionCtxShutdown : public nsIObserver { public: NS_DECL_ISUPPORTS PeerConnectionCtxShutdown() {} void Init()
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.h @@ -12,37 +12,40 @@ #include "CC_Device.h" #include "CC_DeviceInfo.h" #include "CC_Call.h" #include "CC_CallInfo.h" #include "CC_Line.h" #include "CC_LineInfo.h" #include "CC_Observer.h" #include "CC_FeatureInfo.h" +#include "cpr_stdlib.h" #include "StaticPtr.h" #include "PeerConnectionImpl.h" namespace mozilla { class PeerConnectionCtxShutdown; + +// Unit-test helper, because cc_media_constraints_t is hard to forward-declare + +class MediaConstraintsExternal { +public: + MediaConstraintsExternal(); + MediaConstraintsExternal(const dom::MediaConstraintsInternal &aOther); + cc_media_constraints_t* build() const; +protected: + cc_media_constraints_t mConstraints; +}; } namespace sipcc { using namespace mozilla; -// Unit-test helper, because cc_media_constraints_t is hard to forward-declare - -class MediaConstraintsExternal { -public: - MediaConstraintsExternal(cc_media_constraints_t *aConstraints) - : mConstraints(aConstraints) {} - cc_media_constraints_t *mConstraints; -}; - class OnCallEventArgs { public: OnCallEventArgs(ccapi_call_event_e aCallEvent, CSF::CC_CallInfoPtr aInfo) : mCallEvent(aCallEvent), mInfo(aInfo) {} ccapi_call_event_e mCallEvent; CSF::CC_CallInfoPtr mInfo; };
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -94,53 +94,16 @@ PRLogModuleInfo *signalingLogInfo() { logModuleInfo = PR_NewLogModule("signaling"); } return logModuleInfo; } namespace sipcc { -// Convert constraints to C structures - -#ifdef MOZILLA_INTERNAL_API -static void -Apply(const Optional<bool> &aSrc, cc_boolean_constraint_t *aDst, - bool mandatory = false) { - if (aSrc.WasPassed() && (mandatory || !aDst->was_passed)) { - aDst->was_passed = true; - aDst->value = aSrc.Value(); - aDst->mandatory = mandatory; - } -} -#endif - -static cc_media_constraints_t* -ConvertConstraints(const MediaConstraintsInternal& aSrc) { - cc_media_constraints_t* c = (cc_media_constraints_t*) - cpr_malloc(sizeof(cc_media_constraints_t)); - NS_ENSURE_TRUE(c,c); - memset(c, 0, sizeof(cc_media_constraints_t)); -#ifdef MOZILLA_INTERNAL_API - Apply(aSrc.mMandatory.mOfferToReceiveAudio, &c->offer_to_receive_audio, true); - Apply(aSrc.mMandatory.mOfferToReceiveVideo, &c->offer_to_receive_video, true); - Apply(aSrc.mMandatory.mMozDontOfferDataChannel, &c->moz_dont_offer_datachannel, - true); - if (aSrc.mOptional.WasPassed()) { - const Sequence<MediaConstraintSet> &array = aSrc.mOptional.Value(); - for (uint32_t i = 0; i < array.Length(); i++) { - Apply(array[i].mOfferToReceiveAudio, &c->offer_to_receive_audio); - Apply(array[i].mOfferToReceiveVideo, &c->offer_to_receive_video); - Apply(array[i].mMozDontOfferDataChannel, &c->moz_dont_offer_datachannel); - } - } -#endif - return c; -} - // Getting exceptions back down from PCObserver is generally not harmful. namespace { class JSErrorResult : public ErrorResult { public: ~JSErrorResult() { #ifdef MOZILLA_INTERNAL_API @@ -1049,51 +1012,53 @@ PeerConnectionImpl::NotifyDataChannel(al pco), NS_DISPATCH_NORMAL); #endif } NS_IMETHODIMP PeerConnectionImpl::CreateOffer(const MediaConstraintsInternal& aConstraints) { - return CreateOffer(MediaConstraintsExternal(ConvertConstraints(aConstraints))); + return CreateOffer(MediaConstraintsExternal (aConstraints)); } // Used by unit tests and the IDL CreateOffer. NS_IMETHODIMP PeerConnectionImpl::CreateOffer(const MediaConstraintsExternal& aConstraints) { PC_AUTO_ENTER_API_CALL(true); Timecard *tc = mTimeCard; mTimeCard = nullptr; STAMP_TIMECARD(tc, "Create Offer"); - NS_ENSURE_TRUE(aConstraints.mConstraints, NS_ERROR_UNEXPECTED); - mInternal->mCall->createOffer(aConstraints.mConstraints, tc); + cc_media_constraints_t* cc_constraints = aConstraints.build(); + NS_ENSURE_TRUE(cc_constraints, NS_ERROR_UNEXPECTED); + mInternal->mCall->createOffer(cc_constraints, tc); return NS_OK; } NS_IMETHODIMP PeerConnectionImpl::CreateAnswer(const MediaConstraintsInternal& aConstraints) { - return CreateAnswer(MediaConstraintsExternal(ConvertConstraints(aConstraints))); + return CreateAnswer(MediaConstraintsExternal (aConstraints)); } NS_IMETHODIMP PeerConnectionImpl::CreateAnswer(const MediaConstraintsExternal& aConstraints) { PC_AUTO_ENTER_API_CALL(true); Timecard *tc = mTimeCard; mTimeCard = nullptr; STAMP_TIMECARD(tc, "Create Answer"); - NS_ENSURE_TRUE(aConstraints.mConstraints, NS_ERROR_UNEXPECTED); - mInternal->mCall->createAnswer(aConstraints.mConstraints, tc); + cc_media_constraints_t* cc_constraints = aConstraints.build(); + NS_ENSURE_TRUE(cc_constraints, NS_ERROR_UNEXPECTED); + mInternal->mCall->createAnswer(cc_constraints, tc); return NS_OK; } NS_IMETHODIMP PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP) { PC_AUTO_ENTER_API_CALL(true);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h @@ -69,16 +69,17 @@ class RTCStatsReportInternal; #ifdef USE_FAKE_PCOBSERVER typedef test::AFakePCObserver PeerConnectionObserver; typedef const char *PCObserverString; #else class PeerConnectionObserver; typedef NS_ConvertUTF8toUTF16 PCObserverString; #endif } +class MediaConstraintsExternal; } #if defined(__cplusplus) && __cplusplus >= 201103L typedef struct Timecard Timecard; #else #include "timecard.h" #endif @@ -93,28 +94,28 @@ void func (__VA_ARGS__, rv) NS_IMETHODIMP func(__VA_ARGS__, resulttype **result); \ already_AddRefed<resulttype> func (__VA_ARGS__, rv) namespace sipcc { using mozilla::dom::PeerConnectionObserver; using mozilla::dom::RTCConfiguration; using mozilla::dom::MediaConstraintsInternal; +using mozilla::MediaConstraintsExternal; using mozilla::DOMMediaStream; using mozilla::NrIceCtx; using mozilla::NrIceMediaStream; using mozilla::DtlsIdentity; using mozilla::ErrorResult; using mozilla::NrIceStunServer; using mozilla::NrIceTurnServer; class PeerConnectionWrapper; class PeerConnectionMedia; class RemoteSourceStreamInfo; -class MediaConstraintsExternal; class OnCallEventArgs; class IceConfiguration { public: bool addStunServer(const std::string& addr, uint16_t port) { NrIceStunServer* server(NrIceStunServer::Create(addr, port));
--- a/media/webrtc/signaling/test/FakeMediaStreamsImpl.h +++ b/media/webrtc/signaling/test/FakeMediaStreamsImpl.h @@ -5,16 +5,19 @@ #ifndef FAKE_MEDIA_STREAMIMPL_H_ #define FAKE_MEDIA_STREAMIMPL_H_ #include "FakeMediaStreams.h" #include "nspr.h" #include "nsError.h" +void LogTime(AsyncLatencyLogger::LatencyLogIndex index, uint64_t b, int64_t c) {} +void LogLatency(AsyncLatencyLogger::LatencyLogIndex index, uint64_t b, int64_t c) {} + static const int AUDIO_BUFFER_SIZE = 1600; static const int NUM_CHANNELS = 2; NS_IMPL_ISUPPORTS1(Fake_DOMMediaStream, nsIDOMMediaStream) // Fake_SourceMediaStream nsresult Fake_SourceMediaStream::Start() { mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
--- a/media/webrtc/signaling/test/signaling_unittests.cpp +++ b/media/webrtc/signaling/test/signaling_unittests.cpp @@ -73,40 +73,32 @@ uint16_t kBogusSrflxPort(1001); namespace sipcc { // We can't use mozilla/dom/MediaConstraintsBinding.h here because it uses // nsString, so we pass constraints in using MediaConstraintsExternal instead class MediaConstraints : public MediaConstraintsExternal { public: - MediaConstraints() - : MediaConstraintsExternal((cc_media_constraints_t*) - cpr_malloc(sizeof(*mConstraints))) { - MOZ_ASSERT(mConstraints); - memset(mConstraints, 0, sizeof(*mConstraints)); - } - void setBooleanConstraint(const char *namePtr, bool value, bool mandatory) { cc_boolean_constraint_t &member (getMember(namePtr)); member.was_passed = true; member.value = value; member.mandatory = mandatory; } private: cc_boolean_constraint_t &getMember(const char *namePtr) { - MOZ_ASSERT(mConstraints); if (strcmp(namePtr, "OfferToReceiveAudio") == 0) { - return mConstraints->offer_to_receive_audio; + return mConstraints.offer_to_receive_audio; } if (strcmp(namePtr, "OfferToReceiveVideo") == 0) { - return mConstraints->offer_to_receive_video; + return mConstraints.offer_to_receive_video; } MOZ_ASSERT(false); - return mConstraints->moz_dont_offer_datachannel; + return mConstraints.moz_dont_offer_datachannel; } }; } using namespace mozilla; using namespace mozilla::dom; namespace test {
--- a/media/webrtc/trunk/webrtc/modules/audio_device/audio_device.gypi +++ b/media/webrtc/trunk/webrtc/modules/audio_device/audio_device.gypi @@ -39,16 +39,22 @@ 'audio_device_utility.h', 'audio_device_impl.cc', 'audio_device_impl.h', 'audio_device_config.h', 'dummy/audio_device_dummy.h', 'dummy/audio_device_utility_dummy.h', ], 'conditions': [ + ['build_with_mozilla==1', { + 'include_dirs': [ + '$(DIST)/include', + '$(DIST)/include/nspr', + ], + }], ['OS=="linux" or include_alsa_audio==1 or include_pulse_audio==1', { 'include_dirs': [ 'linux', ], }], # OS=="linux" or include_alsa_audio==1 or include_pulse_audio==1 ['OS=="ios"', { 'include_dirs': [ 'ios',
--- a/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_alsa_linux.cc +++ b/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_alsa_linux.cc @@ -14,16 +14,23 @@ #include "audio_device_alsa_linux.h" #include "audio_device_config.h" #include "event_wrapper.h" #include "system_wrappers/interface/sleep.h" #include "trace.h" #include "thread_wrapper.h" +#include "Latency.h" + +#define LOG_FIRST_CAPTURE(x) LogTime(AsyncLatencyLogger::AudioCaptureBase, \ + reinterpret_cast<uint64_t>(x), 0) +#define LOG_CAPTURE_FRAMES(x, frames) LogLatency(AsyncLatencyLogger::AudioCapture, \ + reinterpret_cast<uint64_t>(x), frames) + webrtc_adm_linux_alsa::AlsaSymbolTable AlsaSymbolTable; // Accesses ALSA functions through our late-binding symbol table instead of // directly. This way we don't have to link to libasound, which means our binary // will work on systems that don't have it. #define LATE(sym) \ LATESYM_GET(webrtc_adm_linux_alsa::AlsaSymbolTable, &AlsaSymbolTable, sym) @@ -91,16 +98,17 @@ AudioDeviceLinuxALSA::AudioDeviceLinuxAL _playChannels(ALSA_PLAYOUT_CH), _recordingBuffer(NULL), _playoutBuffer(NULL), _recordingFramesLeft(0), _playoutFramesLeft(0), _playBufType(AudioDeviceModule::kFixedBufferSize), _initialized(false), _recording(false), + _firstRecord(true), _playing(false), _recIsInitialized(false), _playIsInitialized(false), _AGC(false), _recordingDelay(0), _playoutDelay(0), _playWarning(0), _playError(0), @@ -1444,16 +1452,17 @@ int32_t AudioDeviceLinuxALSA::StartRecor { WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, " failed to alloc recording buffer"); _recording = false; return -1; } // RECORDING const char* threadName = "webrtc_audio_module_capture_thread"; + _firstRecord = true; _ptrThreadRec = ThreadWrapper::CreateThread(RecThreadFunc, this, kRealtimePriority, threadName); if (_ptrThreadRec == NULL) { WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, " failed to create the rec audio thread"); @@ -2262,16 +2271,21 @@ bool AudioDeviceLinuxALSA::RecThreadProc memcpy(&_recordingBuffer[_recordingBufferSizeIn10MS - left_size], buffer, size); _recordingFramesLeft -= frames; if (!_recordingFramesLeft) { // buf is full _recordingFramesLeft = _recordingFramesIn10MS; + if (_firstRecord) { + LOG_FIRST_CAPTURE(this); + _firstRecord = false; + } + LOG_CAPTURE_FRAMES(this, _recordingFramesIn10MS); // store the recorded buffer (no action will be taken if the // #recorded samples is not a full buffer) _ptrAudioBuffer->SetRecordedBuffer(_recordingBuffer, _recordingFramesIn10MS); uint32_t currentMicLevel = 0; uint32_t newMicLevel = 0;
--- a/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_alsa_linux.h +++ b/media/webrtc/trunk/webrtc/modules/audio_device/linux/audio_device_alsa_linux.h @@ -229,16 +229,17 @@ private: uint32_t _recordingFramesLeft; uint32_t _playoutFramesLeft; AudioDeviceModule::BufferType _playBufType; private: bool _initialized; bool _recording; + bool _firstRecord; bool _playing; bool _recIsInitialized; bool _playIsInitialized; bool _AGC; snd_pcm_sframes_t _recordingDelay; snd_pcm_sframes_t _playoutDelay;
--- a/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core.c +++ b/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core.c @@ -104,17 +104,27 @@ const float WebRtcAec_overDriveCurve[65] 1.8660f, 1.8750f, 1.8839f, 1.8927f, 1.9014f, 1.9100f, 1.9186f, 1.9270f, 1.9354f, 1.9437f, 1.9520f, 1.9601f, 1.9682f, 1.9763f, 1.9843f, 1.9922f, 2.0000f }; // Target suppression levels for nlp modes. // log{0.001, 0.00001, 0.00000001} static const float kTargetSupp[3] = { -6.9f, -11.5f, -18.4f }; -static const float kMinOverDrive[3] = { 1.0f, 2.0f, 5.0f }; + +// Two sets of parameters, one for the extended filter mode. +static const float kExtendedMinOverDrive[3] = { 3.0f, 6.0f, 15.0f }; +static const float kNormalMinOverDrive[3] = { 1.0f, 2.0f, 5.0f }; +static const float kExtendedSmoothingCoefficients[2][2] = + { { 0.9f, 0.1f }, { 0.92f, 0.08f } }; +static const float kNormalSmoothingCoefficients[2][2] = + { { 0.9f, 0.1f }, { 0.93f, 0.07f } }; + +// Number of partitions forming the NLP's "preferred" bands. +enum { kPrefBandSize = 24 }; #ifdef WEBRTC_AEC_DEBUG_DUMP extern int webrtc_aec_instance_count; #endif // "Private" function prototypes. static void ProcessBlock(AecCore* aec); @@ -276,89 +286,92 @@ int WebRtcAec_FreeAec(AecCore* aec) free(aec); return 0; } static void FilterFar(AecCore* aec, float yf[2][PART_LEN1]) { int i; - for (i = 0; i < NR_PART; i++) { + for (i = 0; i < aec->num_partitions; i++) { int j; int xPos = (i + aec->xfBufBlockPos) * PART_LEN1; int pos = i * PART_LEN1; // Check for wrap - if (i + aec->xfBufBlockPos >= NR_PART) { - xPos -= NR_PART*(PART_LEN1); + if (i + aec->xfBufBlockPos >= aec->num_partitions) { + xPos -= aec->num_partitions*(PART_LEN1); } for (j = 0; j < PART_LEN1; j++) { yf[0][j] += MulRe(aec->xfBuf[0][xPos + j], aec->xfBuf[1][xPos + j], aec->wfBuf[0][ pos + j], aec->wfBuf[1][ pos + j]); yf[1][j] += MulIm(aec->xfBuf[0][xPos + j], aec->xfBuf[1][xPos + j], aec->wfBuf[0][ pos + j], aec->wfBuf[1][ pos + j]); } } } static void ScaleErrorSignal(AecCore* aec, float ef[2][PART_LEN1]) { + const float mu = aec->extended_filter_enabled ? kExtendedMu : aec->normal_mu; + const float error_threshold = aec->extended_filter_enabled ? + kExtendedErrorThreshold : aec->normal_error_threshold; int i; - float absEf; + float abs_ef; for (i = 0; i < (PART_LEN1); i++) { ef[0][i] /= (aec->xPow[i] + 1e-10f); ef[1][i] /= (aec->xPow[i] + 1e-10f); - absEf = sqrtf(ef[0][i] * ef[0][i] + ef[1][i] * ef[1][i]); + abs_ef = sqrtf(ef[0][i] * ef[0][i] + ef[1][i] * ef[1][i]); - if (absEf > aec->errThresh) { - absEf = aec->errThresh / (absEf + 1e-10f); - ef[0][i] *= absEf; - ef[1][i] *= absEf; + if (abs_ef > error_threshold) { + abs_ef = error_threshold / (abs_ef + 1e-10f); + ef[0][i] *= abs_ef; + ef[1][i] *= abs_ef; } // Stepsize factor - ef[0][i] *= aec->mu; - ef[1][i] *= aec->mu; + ef[0][i] *= mu; + ef[1][i] *= mu; } } // Time-unconstrined filter adaptation. // TODO(andrew): consider for a low-complexity mode. //static void FilterAdaptationUnconstrained(AecCore* aec, float *fft, // float ef[2][PART_LEN1]) { // int i, j; -// for (i = 0; i < NR_PART; i++) { +// for (i = 0; i < aec->num_partitions; i++) { // int xPos = (i + aec->xfBufBlockPos)*(PART_LEN1); // int pos; // // Check for wrap -// if (i + aec->xfBufBlockPos >= NR_PART) { -// xPos -= NR_PART * PART_LEN1; +// if (i + aec->xfBufBlockPos >= aec->num_partitions) { +// xPos -= aec->num_partitions * PART_LEN1; // } // // pos = i * PART_LEN1; // // for (j = 0; j < PART_LEN1; j++) { -// aec->wfBuf[pos + j][0] += MulRe(aec->xfBuf[xPos + j][0], -// -aec->xfBuf[xPos + j][1], -// ef[j][0], ef[j][1]); -// aec->wfBuf[pos + j][1] += MulIm(aec->xfBuf[xPos + j][0], -// -aec->xfBuf[xPos + j][1], -//