Merge m-c to elm

This commit is contained in:
Nick Alexander 2013-10-28 09:23:22 -07:00
commit 73b0ef826b
477 changed files with 8397 additions and 7169 deletions

View File

@ -18,4 +18,4 @@
# Modifying this file will now automatically clobber the buildbot machines \o/
#
Bug 914270 needs a clobber since moving variables to moz.build always requires a clobber (bug 852814)
Bug 918345 needs a clobber due to WebIDL binding dependency issues (bug 928195).

View File

@ -16,6 +16,10 @@
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
////////////////////////////////////////////////////////////////////////////////
@ -117,6 +121,18 @@ AccShowEvent::
}
////////////////////////////////////////////////////////////////////////////////
// AccTextSelChangeEvent
////////////////////////////////////////////////////////////////////////////////
AccTextSelChangeEvent::AccTextSelChangeEvent(HyperTextAccessible* aTarget,
nsISelection* aSelection) :
AccEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED, aTarget,
eAutoDetect, eCoalesceTextSelChange),
mSel(aSelection) {}
AccTextSelChangeEvent::~AccTextSelChangeEvent() { }
////////////////////////////////////////////////////////////////////////////////
// AccSelChangeEvent
////////////////////////////////////////////////////////////////////////////////

View File

@ -10,6 +10,8 @@
#include "mozilla/a11y/Accessible.h"
class nsISelection;
namespace mozilla {
namespace a11y {
@ -59,6 +61,9 @@ public:
// 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,
@ -77,6 +82,8 @@ public:
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(); }
@ -93,6 +100,7 @@ public:
eHideEvent,
eShowEvent,
eCaretMoveEvent,
eTextSelChangeEvent,
eSelectionChangeEvent,
eTableChangeEvent,
eVirtualCursorChangeEvent
@ -330,9 +338,11 @@ protected:
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
@ -347,8 +357,30 @@ public:
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;
};

View File

@ -152,6 +152,26 @@ EventQueue::CoalesceEvents()
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
@ -458,21 +478,8 @@ EventQueue::ProcessEventQueue()
}
// 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;
}

View File

@ -265,9 +265,6 @@ 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();
@ -299,7 +296,7 @@ FocusManager::ProcessFocusEvent(AccEvent* aEvent)
if (mActiveARIAMenubar) {
nsRefPtr<AccEvent> menuEndEvent =
new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar,
fromUserInputFlag);
aEvent->FromUserInput());
nsEventShell::FireEvent(menuEndEvent);
}
@ -309,7 +306,7 @@ FocusManager::ProcessFocusEvent(AccEvent* aEvent)
if (mActiveARIAMenubar) {
nsRefPtr<AccEvent> menuStartEvent =
new AccEvent(nsIAccessibleEvent::EVENT_MENU_START,
mActiveARIAMenubar, fromUserInputFlag);
mActiveARIAMenubar, aEvent->FromUserInput());
nsEventShell::FireEvent(menuStartEvent);
}
}
@ -317,7 +314,7 @@ FocusManager::ProcessFocusEvent(AccEvent* aEvent)
// 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;
@ -329,7 +326,7 @@ FocusManager::ProcessFocusEvent(AccEvent* aEvent)
#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
@ -342,7 +339,7 @@ FocusManager::ProcessFocusEvent(AccEvent* aEvent)
// 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);
}

View File

@ -9,12 +9,10 @@
#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"
@ -23,14 +21,6 @@
using namespace mozilla;
using namespace mozilla::a11y;
void
SelectionManager::Shutdown()
{
ClearControlSelectionListener();
mLastTextAccessible = nullptr;
mLastUsedSelection = nullptr;
}
void
SelectionManager::ClearControlSelectionListener()
{
@ -64,8 +54,6 @@ SelectionManager::SetControlSelectionListener(dom::Element* aFocusedElm)
// the current focus.
ClearControlSelectionListener();
mLastTextAccessible = nullptr;
mCurrCtrlFrame = aFocusedElm->GetPrimaryFrame();
if (!mCurrCtrlFrame)
return;
@ -119,6 +107,37 @@ SelectionManager::RemoveDocSelectionListener(nsIPresShell* aPresShell)
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,
@ -149,128 +168,34 @@ SelectionManager::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
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
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!");
}
}
HyperTextAccessible* textAcc =
nsAccUtils::GetTextAccessibleFromSelection(aSelection);
if (!textAcc)
HyperTextAccessible* text = nsAccUtils::GetTextContainer(cntrNode);
if (!text) {
NS_NOTREACHED("We must reach document accessible implementing text interface!");
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
}
mLastCaretOffset = caretOffset;
mLastTextAccessible = textAcc;
if (selection->GetType() == nsISelectionController::SELECTION_NORMAL) {
nsRefPtr<AccEvent> event = new AccTextSelChangeEvent(text, aSelection);
text->Document()->FireDelayedEvent(event);
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);
} 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);
}
}
nsIntRect
SelectionManager::GetCaretRect(nsIWidget** aWidget)
{
nsIntRect caretRect;
NS_ENSURE_TRUE(aWidget, caretRect);
*aWidget = nullptr;
if (!mLastTextAccessible) {
return caretRect; // Return empty rect
}
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
}
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;
}
return caretRect;
}

View File

@ -6,15 +6,10 @@
#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 {
@ -24,7 +19,7 @@ class Element;
namespace a11y {
class HyperTextAccessible;
class AccEvent;
/**
* This special accessibility class is for the caret and selection management.
@ -53,7 +48,7 @@ public:
NS_DECL_NSISELECTIONLISTENER
// SelectionManager
void Shutdown();
void Shutdown() { ClearControlSelectionListener(); }
/**
* Listen to selection events on the focused control.
@ -79,9 +74,10 @@ public:
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:
/**
@ -89,27 +85,9 @@ protected:
*/
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

View File

@ -252,32 +252,15 @@ nsAccUtils::IsARIASelected(Accessible* aAccessible)
}
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)
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!");
DocAccessible* doc =
GetAccService()->GetDocAccessible(aNode->OwnerDoc());
Accessible* accessible =
doc ? doc->GetAccessibleOrContainer(aNode) : nullptr;
if (!accessible)
return nullptr;
}
do {
HyperTextAccessible* textAcc = accessible->AsHyperText();
@ -287,7 +270,6 @@ nsAccUtils::GetTextAccessibleFromSelection(nsISelection* aSelection)
accessible = accessible->Parent();
} while (accessible);
NS_NOTREACHED("We must reach document accessible implementing nsIAccessibleText!");
return nullptr;
}

View File

@ -136,22 +136,17 @@ public:
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.
*

View File

@ -9,12 +9,14 @@
#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"
@ -1742,6 +1744,57 @@ HyperTextAccessible::CaretLineNumber()
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()
{

View File

@ -262,6 +262,15 @@ public:
*/
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

View File

@ -24,7 +24,6 @@
#include "nsINameSpaceManager.h"
#include "nsIPersistentProperties2.h"
#include "nsISelectionController.h"
#include "jsapi.h"
#include "nsIServiceManager.h"
#include "nsITextControlFrame.h"
@ -533,9 +532,7 @@ HTMLFileInputAccessible::HandleAccEvent(AccEvent* aEvent)
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);
}
}

View File

@ -1132,7 +1132,7 @@ AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
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
@ -1286,20 +1286,19 @@ AccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild)
}
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;
}

View File

@ -164,7 +164,7 @@ public: // construction, destruction
* 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.

View File

@ -1209,6 +1209,14 @@ function synthUpKey(aNodeOrID, aCheckerOrEventSeq, aArgs)
aCheckerOrEventSeq);
}
/**
* Left arrow key invoker.
*/
function synthLeftKey(aNodeOrID, aCheckerOrEventSeq)
{
this.__proto__ = new synthKey(aNodeOrID, "VK_LEFT", null, aCheckerOrEventSeq);
}
/**
* Right arrow key invoker.
*/
@ -1657,6 +1665,17 @@ function invokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg, aIsAsync)
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.
*/
@ -1736,6 +1755,19 @@ function caretMoveChecker(aCaretOffset, aTargetOrFunc, aTargetFuncArg)
}
}
/**
* 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.
*/

View File

@ -46,5 +46,6 @@ support-files =
[test_text.html]
[test_text_alg.html]
[test_textattrchange.html]
[test_textselchange.html]
[test_tree.xul]
[test_valuechange.html]

View File

@ -62,6 +62,10 @@
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)));
@ -104,6 +108,11 @@
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">
@ -124,6 +133,8 @@
<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>

View File

@ -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>

View File

@ -16,6 +16,8 @@
function doTest()
{
// Hit testing. See bug #726097
getNode("hittest").scrollIntoView(true);
var hititem = getAccessible("hititem");
var hittest = getAccessible("hittest");

View File

@ -81,6 +81,8 @@
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"));

View File

@ -89,7 +89,7 @@
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()
@ -133,8 +133,7 @@
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));
@ -155,7 +154,12 @@
<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>

View File

@ -728,6 +728,7 @@ pref("font.size.inflation.disabledInMasterProcess", true);
pref("memory.free_dirty_pages", true);
pref("layout.imagevisibility.enabled", false);
pref("layout.imagevisibility.enabled_for_browser_elements_only", true);
pref("layout.imagevisibility.numscrollportwidths", 1);
pref("layout.imagevisibility.numscrollportheights", 1);

View File

@ -5,7 +5,7 @@
"use strict"
function debug(str) {
//dump("-*- ContentPermissionPrompt: " + s + "\n");
//dump("-*- ContentPermissionPrompt: " + str + "\n");
}
const Ci = Components.interfaces;
@ -13,11 +13,14 @@ const Cr = Components.results;
const Cu = Components.utils;
const Cc = Components.classes;
const PROMPT_FOR_UNKNOWN = ["geolocation", "desktop-notification",
"audio-capture"];
const PROMPT_FOR_UNKNOWN = ["audio-capture",
"desktop-notification",
"geolocation",
"video-capture"];
// Due to privary issue, permission requests like GetUserMedia should prompt
// every time instead of providing session persistence.
const PERMISSION_NO_SESSION = ["audio-capture"];
const PERMISSION_NO_SESSION = ["audio-capture", "video-capture"];
const ALLOW_MULTIPLE_REQUESTS = ["audio-capture", "video-capture"];
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
@ -41,7 +44,21 @@ XPCOMUtils.defineLazyServiceGetter(this,
"@mozilla.org/telephony/audiomanager;1",
"nsIAudioManager");
function rememberPermission(aPermission, aPrincipal, aSession)
/**
* aTypesInfo is an array of {permission, access, action, deny} which keeps
* the information of each permission. This arrary is initialized in
* ContentPermissionPrompt.prompt and used among functions.
*
* aTypesInfo[].permission : permission name
* aTypesInfo[].access : permission name + request.access
* aTypesInfo[].action : the default action of this permission
* aTypesInfo[].deny : true if security manager denied this app's origin
* principal.
* Note:
* aTypesInfo[].permission will be sent to prompt only when
* aTypesInfo[].action is PROMPT_ACTION and aTypesInfo[].deny is false.
*/
function rememberPermission(aTypesInfo, aPrincipal, aSession)
{
function convertPermToAllow(aPerm, aPrincipal)
{
@ -49,12 +66,13 @@ function rememberPermission(aPermission, aPrincipal, aSession)
permissionManager.testExactPermissionFromPrincipal(aPrincipal, aPerm);
if (type == Ci.nsIPermissionManager.PROMPT_ACTION ||
(type == Ci.nsIPermissionManager.UNKNOWN_ACTION &&
PROMPT_FOR_UNKNOWN.indexOf(aPermission) >= 0)) {
PROMPT_FOR_UNKNOWN.indexOf(aPerm) >= 0)) {
debug("add " + aPerm + " to permission manager with ALLOW_ACTION");
if (!aSession) {
permissionManager.addFromPrincipal(aPrincipal,
aPerm,
Ci.nsIPermissionManager.ALLOW_ACTION);
} else if (PERMISSION_NO_SESSION.indexOf(aPermission) < 0) {
} else if (PERMISSION_NO_SESSION.indexOf(aPerm) < 0) {
permissionManager.addFromPrincipal(aPrincipal,
aPerm,
Ci.nsIPermissionManager.ALLOW_ACTION,
@ -63,14 +81,18 @@ function rememberPermission(aPermission, aPrincipal, aSession)
}
}
// Expand the permission to see if we have multiple access properties to convert
let access = PermissionsTable[aPermission].access;
if (access) {
for (let idx in access) {
convertPermToAllow(aPermission + "-" + access[idx], aPrincipal);
for (let i in aTypesInfo) {
// Expand the permission to see if we have multiple access properties
// to convert
let perm = aTypesInfo[i].permission;
let access = PermissionsTable[perm].access;
if (access) {
for (let idx in access) {
convertPermToAllow(perm + "-" + access[idx], aPrincipal);
}
} else {
convertPermToAllow(perm, aPrincipal);
}
} else {
convertPermToAllow(aPermission, aPrincipal);
}
}
@ -78,23 +100,63 @@ function ContentPermissionPrompt() {}
ContentPermissionPrompt.prototype = {
handleExistingPermission: function handleExistingPermission(request) {
let access = (request.access && request.access !== "unused") ? request.type + "-" + request.access :
request.type;
let result = Services.perms.testExactPermissionFromPrincipal(request.principal, access);
if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
handleExistingPermission: function handleExistingPermission(request,
typesInfo) {
typesInfo.forEach(function(type) {
type.action =
Services.perms.testExactPermissionFromPrincipal(request.principal,
type.access);
});
// If all permissions are allowed already, call allow() without prompting.
let checkAllowPermission = function(type) {
if (type.action == Ci.nsIPermissionManager.ALLOW_ACTION) {
return true;
}
return false;
}
if (typesInfo.every(checkAllowPermission)) {
debug("all permission requests are allowed");
request.allow();
return true;
}
if (result == Ci.nsIPermissionManager.DENY_ACTION ||
result == Ci.nsIPermissionManager.UNKNOWN_ACTION && PROMPT_FOR_UNKNOWN.indexOf(access) < 0) {
// If all permissions are DENY_ACTION or UNKNOWN_ACTION, call cancel()
// without prompting.
let checkDenyPermission = function(type) {
if (type.action == Ci.nsIPermissionManager.DENY_ACTION ||
type.action == Ci.nsIPermissionManager.UNKNOWN_ACTION &&
PROMPT_FOR_UNKNOWN.indexOf(type.access) < 0) {
return true;
}
return false;
}
if (typesInfo.every(checkDenyPermission)) {
debug("all permission requests are denied");
request.cancel();
return true;
}
return false;
},
handledByApp: function handledByApp(request) {
// multiple requests should be audio and video
checkMultipleRequest: function checkMultipleRequest(typesInfo) {
if (typesInfo.length == 1) {
return true;
} else if (typesInfo.length > 1) {
let checkIfAllowMultiRequest = function(type) {
return (ALLOW_MULTIPLE_REQUESTS.indexOf(type.access) !== -1);
}
if (typesInfo.every(checkIfAllowMultiRequest)) {
debug("legal multiple requests");
return true;
}
}
return false;
},
handledByApp: function handledByApp(request, typesInfo) {
if (request.principal.appId == Ci.nsIScriptSecurityManager.NO_APP_ID ||
request.principal.appId == Ci.nsIScriptSecurityManager.UNKNOWN_APP_ID) {
// This should not really happen
@ -106,49 +168,94 @@ ContentPermissionPrompt.prototype = {
.getService(Ci.nsIAppsService);
let app = appsService.getAppByLocalId(request.principal.appId);
let url = Services.io.newURI(app.origin, null, null);
let principal = secMan.getAppCodebasePrincipal(url, request.principal.appId,
/*mozbrowser*/false);
let access = (request.access && request.access !== "unused") ? request.type + "-" + request.access :
request.type;
let result = Services.perms.testExactPermissionFromPrincipal(principal, access);
// Check each permission if it's denied by permission manager with app's
// URL.
let checkIfDenyAppPrincipal = function(type) {
let url = Services.io.newURI(app.origin, null, null);
let principal = secMan.getAppCodebasePrincipal(url,
request.principal.appId,
/*mozbrowser*/false);
let result = Services.perms.testExactPermissionFromPrincipal(principal,
type.access);
if (result == Ci.nsIPermissionManager.ALLOW_ACTION ||
result == Ci.nsIPermissionManager.PROMPT_ACTION) {
return false;
if (result == Ci.nsIPermissionManager.ALLOW_ACTION ||
result == Ci.nsIPermissionManager.PROMPT_ACTION) {
type.deny = false;
}
return type.deny;
}
if (typesInfo.every(checkIfDenyAppPrincipal)) {
request.cancel();
return true;
}
request.cancel();
return true;
return false;
},
handledByPermissionType: function handledByPermissionType(request) {
return permissionSpecificChecker.hasOwnProperty(request.type)
? permissionSpecificChecker[request.type](request)
: false;
handledByPermissionType: function handledByPermissionType(request, typesInfo) {
for (let i in typesInfo) {
if (permissionSpecificChecker.hasOwnProperty(typesInfo[i].permission) &&
permissionSpecificChecker[typesInfo[i].permission](request)) {
return true;
}
}
return false;
},
_id: 0,
prompt: function(request) {
if (secMan.isSystemPrincipal(request.principal)) {
request.allow();
return true;
return;
}
if (this.handledByApp(request) ||
this.handledByPermissionType(request)) {
// Initialize the typesInfo and set the default value.
let typesInfo = [];
let perms = request.types.QueryInterface(Ci.nsIArray);
for (let idx = 0; idx < perms.length; idx++) {
let perm = perms.queryElementAt(idx, Ci.nsIContentPermissionType);
let tmp = {
permission: perm.type,
access: (perm.access && perm.access !== "unused") ?
perm.type + "-" + perm.access : perm.type,
deny: true,
action: Ci.nsIPermissionManager.UNKNOWN_ACTION
};
typesInfo.push(tmp);
}
if (typesInfo.length == 0) {
request.cancel();
return;
}
if(!this.checkMultipleRequest(typesInfo)) {
request.cancel();
return;
}
if (this.handledByApp(request, typesInfo) ||
this.handledByPermissionType(request, typesInfo)) {
return;
}
// returns true if the request was handled
if (this.handleExistingPermission(request))
if (this.handleExistingPermission(request, typesInfo)) {
return;
}
// prompt PROMPT_ACTION request only.
typesInfo.forEach(function(aType, aIndex) {
if (aType.action != Ci.nsIPermissionManager.PROMPT_ACTION || aType.deny) {
typesInfo.splice(aIndex);
}
});
let frame = request.element;
let requestId = this._id++;
if (!frame) {
this.delegatePrompt(request, requestId);
this.delegatePrompt(request, requestId, typesInfo);
return;
}
@ -163,7 +270,7 @@ ContentPermissionPrompt.prototype = {
if (evt.detail.visible === true)
return;
self.cancelPrompt(request, requestId);
self.cancelPrompt(request, requestId, typesInfo);
cancelRequest();
}
@ -180,7 +287,7 @@ ContentPermissionPrompt.prototype = {
// away but the request is still here.
frame.addEventListener("mozbrowservisibilitychange", onVisibilityChange);
self.delegatePrompt(request, requestId, function onCallback() {
self.delegatePrompt(request, requestId, typesInfo, function onCallback() {
frame.removeEventListener("mozbrowservisibilitychange", onVisibilityChange);
});
};
@ -191,22 +298,17 @@ ContentPermissionPrompt.prototype = {
}
},
cancelPrompt: function(request, requestId) {
this.sendToBrowserWindow("cancel-permission-prompt", request, requestId);
cancelPrompt: function(request, requestId, typesInfo) {
this.sendToBrowserWindow("cancel-permission-prompt", request, requestId,
typesInfo);
},
delegatePrompt: function(request, requestId, callback) {
let access = (request.access && request.access !== "unused") ? request.type + "-" + request.access :
request.type;
let principal = request.principal;
delegatePrompt: function(request, requestId, typesInfo, callback) {
this._permission = access;
this._uri = principal.URI.spec;
this._origin = principal.origin;
this.sendToBrowserWindow("permission-prompt", request, requestId, function(type, remember) {
this.sendToBrowserWindow("permission-prompt", request, requestId, typesInfo,
function(type, remember) {
if (type == "permission-allow") {
rememberPermission(request.type, principal, !remember);
rememberPermission(typesInfo, request.principal, !remember);
if (callback) {
callback();
}
@ -214,14 +316,20 @@ ContentPermissionPrompt.prototype = {
return;
}
if (remember) {
Services.perms.addFromPrincipal(principal, access,
Ci.nsIPermissionManager.DENY_ACTION);
} else {
Services.perms.addFromPrincipal(principal, access,
Ci.nsIPermissionManager.DENY_ACTION,
Ci.nsIPermissionManager.EXPIRE_SESSION, 0);
let addDenyPermission = function(type) {
debug("add " + type.permission +
" to permission manager with DENY_ACTION");
if (remember) {
Services.perms.addFromPrincipal(request.principal, type.access,
Ci.nsIPermissionManager.DENY_ACTION);
} else {
Services.perms.addFromPrincipal(request.principal, type.access,
Ci.nsIPermissionManager.DENY_ACTION,
Ci.nsIPermissionManager.EXPIRE_SESSION,
0);
}
}
typesInfo.forEach(addDenyPermission);
if (callback) {
callback();
@ -230,7 +338,7 @@ ContentPermissionPrompt.prototype = {
});
},
sendToBrowserWindow: function(type, request, requestId, callback) {
sendToBrowserWindow: function(type, request, requestId, typesInfo, callback) {
let browser = Services.wm.getMostRecentWindow("navigator:browser");
let content = browser.getContentWindow();
if (!content)
@ -253,10 +361,15 @@ ContentPermissionPrompt.prototype = {
principal.appStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED)
? true
: request.remember;
let permissions = [];
for (let i in typesInfo) {
debug("prompt " + typesInfo[i].permission);
permissions.push(typesInfo[i].permission);
}
let details = {
type: type,
permission: request.type,
permissions: permissions,
id: requestId,
origin: principal.origin,
isApp: isApp,
@ -289,6 +402,5 @@ ContentPermissionPrompt.prototype = {
};
})();
//module initialization
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentPermissionPrompt]);

View File

@ -1,4 +1,4 @@
{
"revision": "c09f78f6bdd9c8c3cea3943f8a6fe96c760d7de7",
"revision": "166023c341cca944563c410f05dff37a992cb05b",
"repo_path": "/integration/gaia-central"
}

View File

@ -213,20 +213,12 @@ var gPluginHandler = {
},
handleEvent : function(event) {
let plugin;
let doc;
let eventType = event.type;
if (eventType === "PluginRemoved") {
doc = event.target;
}
else {
plugin = event.target;
doc = plugin.ownerDocument;
let plugin = event.target;
let doc = plugin.ownerDocument;
if (!(plugin instanceof Ci.nsIObjectLoadingContent))
return;
}
if (!(plugin instanceof Ci.nsIObjectLoadingContent))
return;
if (eventType == "PluginBindingAttached") {
// The plugin binding fires this event when it is created.
@ -308,13 +300,12 @@ var gPluginHandler = {
break;
case "PluginInstantiated":
case "PluginRemoved":
shouldShowNotification = true;
break;
}
// Show the in-content UI if it's not too big. The crashed plugin handler already did this.
if (eventType != "PluginCrashed" && eventType != "PluginRemoved") {
if (eventType != "PluginCrashed") {
let overlay = this.getPluginUI(plugin, "main");
if (overlay != null) {
if (!this.isTooSmall(plugin, overlay))
@ -336,7 +327,7 @@ var gPluginHandler = {
// Only show the notification after we've done the isTooSmall check, so
// that the notification can decide whether to show the "alert" icon
if (shouldShowNotification) {
this._showClickToPlayNotification(browser);
this._showClickToPlayNotification(browser, plugin, false);
}
},
@ -562,7 +553,7 @@ var gPluginHandler = {
if (!(aEvent.originalTarget instanceof HTMLAnchorElement) &&
(aEvent.originalTarget.getAttribute('anonid') != 'closeIcon') &&
aEvent.button == 0 && aEvent.isTrusted) {
gPluginHandler._showClickToPlayNotification(browser, plugin);
gPluginHandler._showClickToPlayNotification(browser, plugin, true);
aEvent.stopPropagation();
aEvent.preventDefault();
}
@ -607,7 +598,7 @@ var gPluginHandler = {
}, true);
if (!playPreviewInfo.ignoreCTP) {
gPluginHandler._showClickToPlayNotification(browser);
gPluginHandler._showClickToPlayNotification(browser, aPlugin, false);
}
},
@ -625,14 +616,20 @@ var gPluginHandler = {
if (gPluginHandler.canActivatePlugin(objLoadingContent))
gPluginHandler._handleClickToPlayEvent(plugin);
}
gPluginHandler._showClickToPlayNotification(browser);
gPluginHandler._showClickToPlayNotification(browser, null, false);
},
_clickToPlayNotificationEventCallback: function PH_ctpEventCallback(event) {
if (event == "showing") {
gPluginHandler._makeCenterActions(this);
Services.telemetry.getHistogramById("PLUGINS_NOTIFICATION_SHOWN")
.add(!this.options.primaryPlugin);
// Histograms always start at 0, even though our data starts at 1
let histogramCount = this.options.centerActions.size - 1;
if (histogramCount > 4) {
histogramCount = 4;
}
Services.telemetry.getHistogramById("PLUGINS_NOTIFICATION_PLUGIN_COUNT")
.add(histogramCount);
}
else if (event == "dismissed") {
// Once the popup is dismissed, clicking the icon should show the full
@ -655,86 +652,6 @@ var gPluginHandler = {
return principal.origin;
},
_makeCenterActions: function PH_makeCenterActions(notification) {
let contentWindow = notification.browser.contentWindow;
let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let principal = contentWindow.document.nodePrincipal;
// This matches the behavior of nsPermssionManager, used for display purposes only
let principalHost = this._getHostFromPrincipal(principal);
let centerActions = [];
let pluginsFound = new Set();
for (let plugin of cwu.plugins) {
plugin.QueryInterface(Ci.nsIObjectLoadingContent);
if (plugin.getContentTypeForMIMEType(plugin.actualType) != Ci.nsIObjectLoadingContent.TYPE_PLUGIN) {
continue;
}
let pluginInfo = this._getPluginInfo(plugin);
if (pluginInfo.permissionString === null) {
Components.utils.reportError("No permission string for active plugin.");
continue;
}
if (pluginsFound.has(pluginInfo.permissionString)) {
continue;
}
pluginsFound.add(pluginInfo.permissionString);
// Add the per-site permissions and details URLs to pluginInfo here
// because they are more expensive to compute and so we avoid it in
// the tighter loop above.
let permissionObj = Services.perms.
getPermissionObject(principal, pluginInfo.permissionString, false);
if (permissionObj) {
pluginInfo.pluginPermissionHost = permissionObj.host;
pluginInfo.pluginPermissionType = permissionObj.expireType;
}
else {
pluginInfo.pluginPermissionHost = principalHost;
pluginInfo.pluginPermissionType = undefined;
}
let url;
// TODO: allow the blocklist to specify a better link, bug 873093
if (pluginInfo.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE) {
url = Services.urlFormatter.formatURLPref("plugins.update.url");
}
else if (pluginInfo.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
url = Services.blocklist.getPluginBlocklistURL(pluginInfo.pluginTag);
}
else {
url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "clicktoplay";
}
pluginInfo.detailsLink = url;
centerActions.push(pluginInfo);
}
if (centerActions.length == 0) {
// TODO: this is a temporary band-aid to avoid broken doorhangers
// until bug 926605 is landed.
notification.options.centerActions = [];
setTimeout(() => PopupNotifications.remove(notification), 0);
return;
}
centerActions.sort(function(a, b) {
return a.pluginName.localeCompare(b.pluginName);
});
notification.options.centerActions = centerActions;
// Histograms always start at 0, even though our data starts at 1
let histogramCount = centerActions.length - 1;
if (histogramCount > 4) {
histogramCount = 4;
}
Services.telemetry.getHistogramById("PLUGINS_NOTIFICATION_PLUGIN_COUNT")
.add(histogramCount);
},
/**
* Called from the plugin doorhanger to set the new permissions for a plugin
* and activate plugins if necessary.
@ -798,6 +715,7 @@ var gPluginHandler = {
let plugins = cwu.plugins;
let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
let pluginFound = false;
for (let plugin of plugins) {
plugin.QueryInterface(Ci.nsIObjectLoadingContent);
// canActivatePlugin will return false if this isn't a known plugin type,
@ -809,60 +727,141 @@ var gPluginHandler = {
overlay.removeEventListener("click", gPluginHandler._overlayClickListener, true);
}
plugin.playPlugin();
pluginFound = true;
}
}
// If there are no instances of the plugin on the page any more, what the
// user probably needs is for us to allow and then refresh.
if (!pluginFound) {
browser.reload();
}
},
_showClickToPlayNotification: function PH_showClickToPlayNotification(aBrowser, aPrimaryPlugin) {
_showClickToPlayNotification: function PH_showClickToPlayNotification(aBrowser, aPlugin, aShowNow) {
let notification = PopupNotifications.getNotification("click-to-play-plugins", aBrowser);
let plugins = [];
let contentWindow = aBrowser.contentWindow;
let contentDoc = aBrowser.contentDocument;
let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
// cwu.plugins may contain non-plugin <object>s, filter them out
let plugins = cwu.plugins.filter((plugin) =>
plugin.getContentTypeForMIMEType(plugin.actualType) == Ci.nsIObjectLoadingContent.TYPE_PLUGIN);
if (plugins.length == 0) {
if (notification) {
PopupNotifications.remove(notification);
// if aPlugin is null, that means the user has navigated back to a page with plugins, and we need
// to collect all the plugins
if (aPlugin === null) {
let contentWindow = aBrowser.contentWindow;
let contentDoc = aBrowser.contentDocument;
let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
// cwu.plugins may contain non-plugin <object>s, filter them out
plugins = cwu.plugins.filter((plugin) =>
plugin.getContentTypeForMIMEType(plugin.actualType) == Ci.nsIObjectLoadingContent.TYPE_PLUGIN);
if (plugins.length == 0) {
if (notification) {
PopupNotifications.remove(notification);
}
return;
}
} else {
plugins = [aPlugin];
}
// If this is a new notification, create a centerActions map, otherwise append
let centerActions;
if (notification) {
centerActions = notification.options.centerActions;
} else {
centerActions = new Map();
}
let principal = aBrowser.contentDocument.nodePrincipal;
let principalHost = this._getHostFromPrincipal(principal);
for (var plugin of plugins) {
let pluginInfo = this._getPluginInfo(plugin);
if (pluginInfo.permissionString === null) {
Cu.reportError("No permission string for active plugin.");
continue;
}
if (centerActions.has(pluginInfo.permissionString)) {
continue;
}
// Assume that plugins are hidden and then set override later
pluginInfo.hidden = true;
let overlay = this.getPluginUI(plugin, "main");
if (overlay && overlay.style.visibility != "hidden" && overlay.style.visibility != "") {
pluginInfo.hidden = false;
}
let permissionObj = Services.perms.
getPermissionObject(principal, pluginInfo.permissionString, false);
if (permissionObj) {
pluginInfo.pluginPermissionHost = permissionObj.host;
pluginInfo.pluginPermissionType = permissionObj.expireType;
}
else {
pluginInfo.pluginPermissionHost = principalHost;
pluginInfo.pluginPermissionType = undefined;
}
let url;
// TODO: allow the blocklist to specify a better link, bug 873093
if (pluginInfo.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE) {
url = Services.urlFormatter.formatURLPref("plugins.update.url");
}
else if (pluginInfo.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
url = Services.blocklist.getPluginBlocklistURL(pluginInfo.pluginTag);
}
else {
url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "clicktoplay";
}
pluginInfo.detailsLink = url;
centerActions.set(pluginInfo.permissionString, pluginInfo);
}
let pluginBlocked = false;
let pluginHidden = false;
for (let pluginInfo of centerActions.values()) {
let fallbackType = pluginInfo.fallbackType;
if (fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE ||
fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE ||
fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_BLOCKLISTED) {
pluginBlocked = true;
pluginHidden = false;
break;
}
if (fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY && pluginInfo.hidden) {
pluginHidden = true;
}
}
let iconClasses = document.getElementById("plugins-notification-icon").classList;
iconClasses.toggle("plugin-blocked", pluginBlocked);
iconClasses.toggle("plugin-hidden", pluginHidden);
let primaryPluginPermission = null;
if (aShowNow) {
primaryPluginPermission = this._getPluginInfo(aPlugin).permissionString;
}
if (notification) {
// Don't modify the notification UI while it's on the screen, that would be
// jumpy and might allow clickjacking.
if (aShowNow) {
notification.options.primaryPlugin = primaryPluginPermission;
notification.reshow();
}
return;
}
let icon = 'plugins-notification-icon';
for (let plugin of plugins) {
let fallbackType = plugin.pluginFallbackType;
if (fallbackType == plugin.PLUGIN_VULNERABLE_UPDATABLE ||
fallbackType == plugin.PLUGIN_VULNERABLE_NO_UPDATE ||
fallbackType == plugin.PLUGIN_BLOCKLISTED) {
icon = 'blocked-plugins-notification-icon';
break;
}
if (fallbackType == plugin.PLUGIN_CLICK_TO_PLAY) {
let overlay = this.getPluginUI(plugin, "main");
if (!overlay || overlay.style.visibility == 'hidden') {
icon = 'alert-plugins-notification-icon';
}
}
}
let dismissed = notification ? notification.dismissed : true;
if (aPrimaryPlugin)
dismissed = false;
let primaryPluginPermission = null;
if (aPrimaryPlugin) {
primaryPluginPermission = this._getPluginInfo(aPrimaryPlugin).permissionString;
}
let options = {
dismissed: dismissed,
dismissed: !aShowNow,
eventCallback: this._clickToPlayNotificationEventCallback,
primaryPlugin: primaryPluginPermission
primaryPlugin: primaryPluginPermission,
centerActions: centerActions
};
PopupNotifications.show(aBrowser, "click-to-play-plugins",
"", icon,
"", "plugins-notification-icon",
null, null, options);
},

View File

@ -756,7 +756,6 @@ var gBrowserInit = {
gBrowser.addEventListener("PluginCrashed", gPluginHandler, true);
gBrowser.addEventListener("PluginOutdated", gPluginHandler, true);
gBrowser.addEventListener("PluginInstantiated", gPluginHandler, true);
gBrowser.addEventListener("PluginRemoved", gPluginHandler, true);
gBrowser.addEventListener("NewPluginInstalled", gPluginHandler.newPluginInstalled, true);

View File

@ -534,8 +534,6 @@
<image id="webapps-notification-icon" class="notification-anchor-icon" role="button"/>
<image id="plugins-notification-icon" class="notification-anchor-icon" role="button"/>
<image id="web-notifications-notification-icon" class="notification-anchor-icon" role="button"/>
<image id="alert-plugins-notification-icon" class="notification-anchor-icon" role="button"/>
<image id="blocked-plugins-notification-icon" class="notification-anchor-icon" role="button"/>
<image id="plugin-install-notification-icon" class="notification-anchor-icon" role="button"/>
<image id="mixed-content-blocked-notification-icon" class="notification-anchor-icon" role="button"/>
<image id="webRTC-shareDevices-notification-icon" class="notification-anchor-icon" role="button"/>
@ -571,7 +569,7 @@
onclick="gPopupBlockerObserver.onReportButtonClick(event);"/>
<image id="star-button"
class="urlbar-icon"
onclick="BookmarkingUI.onCommand(event);"/>
onclick="if (event.button === 0) BookmarkingUI.onCommand(event);"/>
<image id="go-button"
class="urlbar-icon"
tooltiptext="&goEndCap.tooltip;"

View File

@ -1277,7 +1277,7 @@ nsContextMenu.prototype = {
},
playPlugin: function() {
gPluginHandler._showClickToPlayNotification(this.browser, this.target);
gPluginHandler._showClickToPlayNotification(this.browser, this.target, true);
},
hidePlugin: function() {

View File

@ -61,7 +61,6 @@ support-files =
plugin_clickToPlayDeny.html
plugin_data_url.html
plugin_hidden_to_visible.html
plugin_iframe.html
plugin_small.html
plugin_test.html
plugin_test2.html
@ -88,7 +87,6 @@ support-files =
[browser_CTP_data_urls.js]
[browser_CTP_drag_drop.js]
[browser_CTP_iframe.js]
[browser_CTP_nonplugins.js]
[browser_CTP_resize.js]
[browser_URLBarSetURI.js]

View File

@ -71,32 +71,43 @@ function test1() {
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
ok(!objLoadingContent.activated, "Test 1, Plugin should not be activated");
window.document.addEventListener("popupshown", test2, false);
// When the popupshown DOM event is fired, the actual showing of the popup
// may still be pending. Clear the event loop before continuing so that
// subsequently-opened popups aren't cancelled by accident.
let goToNext = function() {
window.document.removeEventListener("popupshown", goToNext, false);
executeSoon(test2);
};
window.document.addEventListener("popupshown", goToNext, false);
EventUtils.synthesizeMouseAtCenter(plugin,
{ type: "contextmenu", button: 2 },
gTestBrowser.contentWindow);
}
function test2() {
window.document.removeEventListener("popupshown", test2, false);
let activate = window.document.getElementById("context-ctp-play");
ok(activate, "Test 2, Should have a context menu entry for activating the plugin");
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "Test 2, Should have a click-to-play notification");
ok(notification.dismissed, "Test 2, notification should be dismissed");
// Trigger the click-to-play popup
activate.doCommand();
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "Test 2, Should have a click-to-play notification");
ok(!notification.dismissed, "Test 2, The click-to-play notification should not be dismissed");
waitForCondition(() => !notification.dismissed,
test3, "Test 2, waited too long for context activation");
}
function test3() {
// Activate the plugin
PopupNotifications.panel.firstChild._primaryButton.click();
let plugin = gTestBrowser.contentDocument.getElementById("test");
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
waitForCondition(() => objLoadingContent.activated, test3, "Waited too long for plugin to activate");
waitForCondition(() => objLoadingContent.activated, test4, "Waited too long for plugin to activate");
}
function test3() {
function test4() {
finishTest();
}

View File

@ -161,10 +161,10 @@ function test2b() {
// Simulate choosing "Allow now" for the test plugin
notification.reshow();
is(notification.options.centerActions.length, 2, "Test 2b, Should have two types of plugin in the notification");
is(notification.options.centerActions.size, 2, "Test 2b, Should have two types of plugin in the notification");
var centerAction = null;
for (var action of notification.options.centerActions) {
for (var action of notification.options.centerActions.values()) {
if (action.pluginName == "Test") {
centerAction = action;
break;

View File

@ -64,13 +64,11 @@ function part5() {
gBrowser.selectedBrowser.removeEventListener("PluginBindingAttached", handleEvent);
ok(PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser), "Should have a click-to-play notification in the initial tab");
gNextTest = part6;
gNewWindow = gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
gNewWindow.addEventListener("load", handleEvent, true);
waitForFocus(part6, gNewWindow);
}
function part6() {
gNewWindow.removeEventListener("load", handleEvent);
let condition = function() PopupNotifications.getNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser);
waitForCondition(condition, part7, "Waited too long for click-to-play notification");
}

View File

@ -1,135 +0,0 @@
var rootDir = getRootDirectory(gTestPath);
const gTestRoot = rootDir;
const gHttpTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
var gTestBrowser = null;
var gNextTest = null;
var gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
var gPageLoads = 0;
Components.utils.import("resource://gre/modules/Services.jsm");
// This listens for the next opened tab and checks it is of the right url.
// opencallback is called when the new tab is fully loaded
// closecallback is called when the tab is closed
function TabOpenListener(url, opencallback, closecallback) {
this.url = url;
this.opencallback = opencallback;
this.closecallback = closecallback;
gBrowser.tabContainer.addEventListener("TabOpen", this, false);
}
TabOpenListener.prototype = {
url: null,
opencallback: null,
closecallback: null,
tab: null,
browser: null,
handleEvent: function(event) {
if (event.type == "TabOpen") {
gBrowser.tabContainer.removeEventListener("TabOpen", this, false);
this.tab = event.originalTarget;
this.browser = this.tab.linkedBrowser;
gBrowser.addEventListener("pageshow", this, false);
} else if (event.type == "pageshow") {
if (event.target.location.href != this.url)
return;
gBrowser.removeEventListener("pageshow", this, false);
this.tab.addEventListener("TabClose", this, false);
var url = this.browser.contentDocument.location.href;
is(url, this.url, "Should have opened the correct tab");
this.opencallback(this.tab, this.browser.contentWindow);
} else if (event.type == "TabClose") {
if (event.originalTarget != this.tab)
return;
this.tab.removeEventListener("TabClose", this, false);
this.opencallback = null;
this.tab = null;
this.browser = null;
// Let the window close complete
executeSoon(this.closecallback);
this.closecallback = null;
}
}
};
function test() {
waitForExplicitFinish();
registerCleanupFunction(function() {
clearAllPluginPermissions();
Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
});
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
var newTab = gBrowser.addTab();
gBrowser.selectedTab = newTab;
gTestBrowser = gBrowser.selectedBrowser;
gTestBrowser.addEventListener("load", pageLoad, true);
Services.prefs.setBoolPref("plugins.click_to_play", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
prepareTest(runAfterPluginBindingAttached(test1), gHttpTestRoot + "plugin_iframe.html");
}
function finishTest() {
clearAllPluginPermissions();
gTestBrowser.removeEventListener("load", pageLoad, true);
gBrowser.removeCurrentTab();
window.focus();
finish();
}
function pageLoad() {
// Wait for the iframe to be loaded as well.
if (gPageLoads++ < 1)
return;
// The plugin events are async dispatched and can come after the load event
// This just allows the events to fire before we then go on to test the states.
executeSoon(gNextTest);
}
function prepareTest(nextTest, url) {
gNextTest = nextTest;
gTestBrowser.contentWindow.location = url;
}
// Due to layout being async, "PluginBindAttached" may trigger later.
// This wraps a function to force a layout flush, thus triggering it,
// and schedules the function execution so they're definitely executed
// afterwards.
function runAfterPluginBindingAttached(func) {
return function() {
let doc = gTestBrowser.contentDocument.getElementById('frame').contentDocument;
let elems = doc.getElementsByTagName('embed');
if (elems.length < 1) {
elems = doc.getElementsByTagName('object');
}
elems[0].clientTop;
executeSoon(func);
};
}
// Test that we don't show a doorhanger after removing the last plugin
// when the plugin was in an iframe.
function test1() {
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(popupNotification, "Test 1, Should have a click-to-play notification");
let frame = gTestBrowser.contentDocument.getElementById("frame");
frame.parentElement.removeChild(frame);
let condition = () => {
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
if (notification) {
notification.reshow();
}
return !notification;
}
waitForCondition(condition, finishTest, "Test1, Waited too long for notification too be removed");
}

View File

@ -149,12 +149,12 @@ function test2() {
let plugin = gTestBrowser.contentDocument.getElementById("test");
plugin.parentNode.removeChild(plugin);
runAfterPluginRemoved(() => executeSoon(test3));
executeSoon(test3);
}
function test3() {
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(!popupNotification, "Test 3, Should not have a click-to-play notification");
ok(popupNotification, "Test 3, Should still have a click-to-play notification");
finishTest();
}

View File

@ -40,7 +40,7 @@ function pluginBindingAttached() {
ok(notification, "should have popup notification");
// We don't set up the action list until the notification is shown
notification.reshow();
is(notification.options.centerActions.length, 1, "should be 1 type of plugin in the popup notification");
is(notification.options.centerActions.size, 1, "should be 1 type of plugin in the popup notification");
XPCNativeWrapper.unwrap(gTestBrowser.contentWindow).addSecondPlugin();
} else if (gNumPluginBindingsAttached == 2) {
var doc = gTestBrowser.contentDocument;
@ -51,7 +51,7 @@ function pluginBindingAttached() {
var notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "should have popup notification");
notification.reshow();
is(notification.options.centerActions.length, 2, "should be 2 types of plugin in the popup notification");
is(notification.options.centerActions.size, 2, "should be 2 types of plugin in the popup notification");
finish();
} else {
ok(false, "if we've gotten here, something is quite wrong");

View File

@ -179,7 +179,7 @@ function test5() {
ok(notification.dismissed, "Test 5: The plugin notification should be dismissed by default");
notification.reshow();
is(notification.options.centerActions.length, 1, "Test 5: Only the blocked plugin should be present in the notification");
is(notification.options.centerActions.size, 1, "Test 5: Only the blocked plugin should be present in the notification");
ok(PopupNotifications.panel.firstChild._buttonContainer.hidden, "Part 5: The blocked plugins notification should not have any buttons visible.");
ok(!gTestBrowser.missingPlugins, "Test 5, Should not be a missing plugin list");
@ -441,10 +441,11 @@ function test18f() {
var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
ok(!objLoadingContent.activated, "Test 18f, Plugin should not be activated");
// XXXBAD: this code doesn't do what you think it does! it is actually
// observing the "removed" event of the old notification, since we create
// a *new* one when the plugin is clicked.
notification.options.eventCallback = function() { executeSoon(test18g); };
var oldEventCallback = notification.options.eventCallback;
notification.options.eventCallback = function() {
oldEventCallback();
executeSoon(test18g);
};
EventUtils.synthesizeMouseAtCenter(plugin, {}, gTestBrowser.contentWindow);
}
@ -603,10 +604,10 @@ function test21a() {
// we have to actually show the panel to get the bindings to instantiate
notification.reshow();
is(notification.options.centerActions.length, 2, "Test 21a, Should have two types of plugin in the notification");
is(notification.options.centerActions.size, 2, "Test 21a, Should have two types of plugin in the notification");
var centerAction = null;
for (var action of notification.options.centerActions) {
for (var action of notification.options.centerActions.values()) {
if (action.pluginName == "Test") {
centerAction = action;
break;
@ -640,7 +641,7 @@ function test21c() {
ok(notification, "Test 21c, Should have a click-to-play notification");
notification.reshow();
ok(notification.options.centerActions.length == 2, "Test 21c, Should have one type of plugin in the notification");
ok(notification.options.centerActions.size == 2, "Test 21c, Should have one type of plugin in the notification");
var doc = gTestBrowser.contentDocument;
var plugin = doc.getElementById("test");
@ -661,7 +662,7 @@ function test21c() {
}
var centerAction = null;
for (var action of notification.options.centerActions) {
for (var action of notification.options.centerActions.values()) {
if (action.pluginName == "Second Test") {
centerAction = action;
break;

View File

@ -68,7 +68,7 @@ function testActivateAddSameTypePart2() {
function testActivateAddSameTypePart3() {
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
let centerAction = null;
for (let action of popupNotification.options.centerActions) {
for (let action of popupNotification.options.centerActions.values()) {
if (action.pluginName == "Test") {
centerAction = action;
break;
@ -142,7 +142,7 @@ function testActivateAddDifferentTypePart2() {
function testActivateAddDifferentTypePart3() {
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
is(popupNotification.options.centerActions.length, 1, "Should be one plugin action");
is(popupNotification.options.centerActions.size, 1, "Should be one plugin action");
let plugin = gTestBrowser.contentDocument.getElementsByTagName("embed")[0];
ok(!plugin.activated, "testActivateAddDifferentTypePart3: plugin should not be activated");

View File

@ -1,11 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<iframe src="plugin_test.html" id="frame" width="300" height="300">
This should load plugin_test.html
</iframe>
</body>
</html>

View File

@ -1567,14 +1567,20 @@
<field name="_items">[]</field>
<constructor><![CDATA[
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
for (let action of this.notification.options.centerActions) {
let sortedActions = [];
for (let action of this.notification.options.centerActions.values()) {
sortedActions.push(action);
}
sortedActions.sort((a, b) => a.pluginName.localeCompare(b.pluginName));
for (let action of sortedActions) {
let item = document.createElementNS(XUL_NS, "row");
item.setAttribute("class", "plugin-popupnotification-centeritem");
item.action = action;
this.appendChild(item);
this._items.push(item);
}
switch (this.notification.options.centerActions.length) {
switch (this._items.length) {
case 0:
PopupNotifications._dismiss();
break;
@ -1639,7 +1645,7 @@
</method>
<method name="_setupSingleState">
<body><![CDATA[
var action = this.notification.options.centerActions[0];
var action = this._items[0].action;
var host = action.pluginPermissionHost;
let label, linkLabel, linkUrl, button1, button2;
@ -1803,7 +1809,7 @@
<method name="_singleActivateNow">
<body><![CDATA[
gPluginHandler._updatePluginPermission(this.notification,
this.notification.options.centerActions[0],
this._items[0].action,
"allownow");
this._cancel();
]]></body>
@ -1811,7 +1817,7 @@
<method name="_singleBlock">
<body><![CDATA[
gPluginHandler._updatePluginPermission(this.notification,
this.notification.options.centerActions[0],
this._items[0].action,
"block");
this._cancel();
]]></body>
@ -1819,7 +1825,7 @@
<method name="_singleActivateAlways">
<body><![CDATA[
gPluginHandler._updatePluginPermission(this.notification,
this.notification.options.centerActions[0],
this._items[0].action,
"allowalways");
this._cancel();
]]></body>
@ -1827,7 +1833,7 @@
<method name="_singleContinue">
<body><![CDATA[
gPluginHandler._updatePluginPermission(this.notification,
this.notification.options.centerActions[0],
this._items[0].action,
"continue");
this._cancel();
]]></body>

View File

@ -1964,13 +1964,21 @@ ContentPermissionPrompt.prototype = {
prompt: function CPP_prompt(request) {
// Only allow exactly one permission rquest here.
let types = request.types.QueryInterface(Ci.nsIArray);
if (types.length != 1) {
request.cancel();
return;
}
let perm = types.queryElementAt(0, Ci.nsIContentPermissionType);
const kFeatureKeys = { "geolocation" : "geo",
"desktop-notification" : "desktop-notification",
"pointerLock" : "pointerLock",
};
// Make sure that we support the request.
if (!(request.type in kFeatureKeys)) {
if (!(perm.type in kFeatureKeys)) {
return;
}
@ -1982,7 +1990,7 @@ ContentPermissionPrompt.prototype = {
return;
var autoAllow = false;
var permissionKey = kFeatureKeys[request.type];
var permissionKey = kFeatureKeys[perm.type];
var result = Services.perms.testExactPermissionFromPrincipal(requestingPrincipal, permissionKey);
if (result == Ci.nsIPermissionManager.DENY_ACTION) {
@ -1993,14 +2001,14 @@ ContentPermissionPrompt.prototype = {
if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
autoAllow = true;
// For pointerLock, we still want to show a warning prompt.
if (request.type != "pointerLock") {
if (perm.type != "pointerLock") {
request.allow();
return;
}
}
// Show the prompt.
switch (request.type) {
switch (perm.type) {
case "geolocation":
this._promptGeo(request);
break;

View File

@ -64,7 +64,7 @@ function test() {
MockFilePicker.returnFiles = [aFile];
MockFilePicker.displayDirectory = null;
aWin.getTargetFile(params, function() {
aWin.promiseTargetFile(params).then(function() {
// File picker should start with expected display dir.
is(MockFilePicker.displayDirectory.path, aDisplayDir.path,
"File picker should start with browser.download.lastDir");
@ -78,7 +78,7 @@ function test() {
gDownloadLastDir.cleanupPrivateFile();
aWin.close();
aCallback();
});
}).then(null, function() { ok(false); });
}
testOnWindow(false, function(win, downloadDir) {

View File

@ -33,6 +33,7 @@ ShaderEditorPanel.prototype = {
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();

View File

@ -39,7 +39,7 @@ const DEFAULT_EDITOR_CONFIG = {
/**
* 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.
@ -71,8 +71,10 @@ 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);
@ -82,10 +84,20 @@ let EventsHandler = {
* 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.
*/
@ -172,6 +184,12 @@ let ShadersListView = Heritage.extend(WidgetMethods, {
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);
}
},
/**

View File

@ -40,10 +40,10 @@
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"
@ -57,8 +57,8 @@
class="plain editor-label"
value="&shaderEditorUI.fragmentShader;"/>
</vbox>
</hbox>
</hbox>
</box>
</box>
</vbox>
</window>

View File

@ -135,7 +135,7 @@ function once(aTarget, aEventName, aUseCapture = false) {
function waitForFrame(aDebuggee) {
let deferred = promise.defer();
aDebuggee.mozRequestAnimationFrame(deferred.resolve);
aDebuggee.requestAnimationFrame(deferred.resolve);
return deferred.promise;
}

View File

@ -487,7 +487,6 @@ function SideMenuGroup(aWidget, aName, aOptions={}) {
if (aOptions.showCheckbox) {
let checkbox = this._checkbox = makeCheckbox(title, { description: aName });
checkbox.className = "side-menu-widget-group-checkbox";
checkbox.setAttribute("align", "start");
}
title.appendChild(name);
@ -592,7 +591,6 @@ function SideMenuItem(aGroup, aContents, aTooltip, aAttachment={}, aOptions={})
if (aOptions.showCheckbox) {
let checkbox = this._checkbox = makeCheckbox(container, aAttachment);
checkbox.className = "side-menu-widget-item-checkbox";
checkbox.setAttribute("align", "start");
}
container.appendChild(target);

View File

@ -24,6 +24,11 @@
-moz-user-focus: normal;
}
.side-menu-widget-group-checkbox .checkbox-label-box,
.side-menu-widget-item-checkbox .checkbox-label-box {
display: none; /* See bug 669507 */
}
/* VariablesView */
.variables-view-container {

View File

@ -31,8 +31,10 @@ function testPropertyProvider() {
completion = JSPropertyProvider(content, strComplete);
ok(completion.matches.length == 2, "two matches found");
ok(completion.matchProp == "locatio", "matching part is 'test'");
ok(completion.matches[0] == "location", "the first match is 'location'");
ok(completion.matches[1] == "locationbar", "the second match is 'locationbar'");
var matches = completion.matches;
matches.sort();
ok(matches[0] == "location", "the first match is 'location'");
ok(matches[1] == "locationbar", "the second match is 'locationbar'");
finishTest();
}

View File

@ -1,4 +1,4 @@
This is the pdf.js project output, https://github.com/mozilla/pdf.js
Current extension version is: 0.8.629
Current extension version is: 0.8.641

View File

@ -20,8 +20,8 @@ if (typeof PDFJS === 'undefined') {
(typeof window !== 'undefined' ? window : this).PDFJS = {};
}
PDFJS.version = '0.8.629';
PDFJS.build = 'b16b3be';
PDFJS.version = '0.8.641';
PDFJS.build = '19485c3';
(function pdfjsWrapper() {
// Use strict in our context only - users might not want it
@ -6575,6 +6575,11 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
var bounds = Util.getAxialAlignedBoundingBox(
group.bbox,
currentCtx.mozCurrentTransform);
// Clip the bounding box to the current canvas.
bounds = Util.intersect(bounds, [0,
0,
currentCtx.canvas.width,
currentCtx.canvas.height]);
// Use ceil in case we're between sizes so we don't create canvas that is
// too small and make the canvas at least 1x1 pixels.
var drawnWidth = Math.max(Math.ceil(bounds[2] - bounds[0]), 1);

View File

@ -20,8 +20,8 @@ if (typeof PDFJS === 'undefined') {
(typeof window !== 'undefined' ? window : this).PDFJS = {};
}
PDFJS.version = '0.8.629';
PDFJS.build = 'b16b3be';
PDFJS.version = '0.8.641';
PDFJS.build = '19485c3';
(function pdfjsWrapper() {
// Use strict in our context only - users might not want it

View File

@ -223,7 +223,7 @@ limitations under the License.
data-l10n-id="page_rotate_ccw"></menuitem>
</menu>
<div id="viewerContainer">
<div id="viewerContainer" tabindex="0">
<div id="viewer"></div>
</div>

View File

@ -1299,11 +1299,12 @@ var SecondaryToolbar = {
initialize: function secondaryToolbarInitialize(options) {
this.toolbar = options.toolbar;
this.presentationMode = options.presentationMode;
this.buttonContainer = this.toolbar.firstElementChild;
// Define the toolbar buttons.
this.toggleButton = options.toggleButton;
this.presentationMode = options.presentationMode;
this.presentationModeButton = options.presentationModeButton;
this.openFile = options.openFile;
this.print = options.print;
this.download = options.download;
@ -1315,7 +1316,8 @@ var SecondaryToolbar = {
// Attach the event listeners.
var elements = [
{ element: this.toggleButton, handler: this.toggle },
{ element: this.presentationMode, handler: this.presentationModeClick },
{ element: this.presentationModeButton,
handler: this.presentationModeClick },
{ element: this.openFile, handler: this.openFileClick },
{ element: this.print, handler: this.printClick },
{ element: this.download, handler: this.downloadClick },
@ -1335,7 +1337,7 @@ var SecondaryToolbar = {
// Event handling functions.
presentationModeClick: function secondaryToolbarPresentationModeClick(evt) {
PresentationMode.request();
this.presentationMode.request();
this.close();
},
@ -1372,7 +1374,7 @@ var SecondaryToolbar = {
// Misc. functions for interacting with the toolbar.
setMaxHeight: function secondaryToolbarSetMaxHeight(container) {
if (!container) {
if (!container || !this.buttonContainer) {
return;
}
this.newContainerHeight = container.clientHeight;
@ -1410,10 +1412,6 @@ var SecondaryToolbar = {
} else {
this.open();
}
},
get isOpen() {
return this.opened;
}
};
@ -1506,6 +1504,8 @@ var PresentationMode = {
this.container = options.container;
this.secondaryToolbar = options.secondaryToolbar;
this.viewer = this.container.firstElementChild;
this.firstPage = options.firstPage;
this.lastPage = options.lastPage;
this.pageRotateCw = options.pageRotateCw;
@ -1538,7 +1538,8 @@ var PresentationMode = {
},
request: function presentationModeRequest() {
if (!PDFView.supportsFullscreen || this.isFullscreen) {
if (!PDFView.supportsFullscreen || this.isFullscreen ||
!this.viewer.hasChildNodes()) {
return false;
}
@ -1720,8 +1721,10 @@ var PDFView = {
SecondaryToolbar.initialize({
toolbar: document.getElementById('secondaryToolbar'),
presentationMode: PresentationMode,
toggleButton: document.getElementById('secondaryToolbarToggle'),
presentationMode: document.getElementById('secondaryPresentationMode'),
presentationModeButton:
document.getElementById('secondaryPresentationMode'),
openFile: document.getElementById('secondaryOpenFile'),
print: document.getElementById('secondaryPrint'),
download: document.getElementById('secondaryDownload'),
@ -1968,8 +1971,8 @@ var PDFView = {
},
get isHorizontalScrollbarEnabled() {
var div = document.getElementById('viewerContainer');
return div.scrollWidth > div.clientWidth;
return (PresentationMode.active ? false :
(this.container.scrollWidth > this.container.clientWidth));
},
initPassiveLoading: function pdfViewInitPassiveLoading() {
@ -3230,6 +3233,7 @@ var PageView = function pageView(container, id, scale,
this.scrollIntoView = function pageViewScrollIntoView(dest) {
if (PresentationMode.active) { // Avoid breaking presentation mode.
dest = null;
PDFView.setScale(PDFView.currentScaleValue, true, true);
}
if (!dest) {
scrollIntoView(div);
@ -4554,7 +4558,7 @@ window.addEventListener('DOMMouseScroll', function(evt) {
window.addEventListener('click', function click(evt) {
if (!PresentationMode.active) {
if (SecondaryToolbar.isOpen && PDFView.container.contains(evt.target)) {
if (SecondaryToolbar.opened && PDFView.container.contains(evt.target)) {
SecondaryToolbar.close();
}
} else if (evt.button === 0) {
@ -4649,6 +4653,9 @@ window.addEventListener('keydown', function keydown(evt) {
return; // ignoring if the 'toolbar' element is focused
curElement = curElement.parentNode;
}
// Workaround for issue in Firefox, that prevents scroll keys from working
// when elements with 'tabindex' are focused.
PDFView.container.blur();
if (cmd === 0) { // no control key pressed at all.
switch (evt.keyCode) {
@ -4673,7 +4680,7 @@ window.addEventListener('keydown', function keydown(evt) {
handled = true;
break;
case 27: // esc key
if (SecondaryToolbar.isOpen) {
if (SecondaryToolbar.opened) {
SecondaryToolbar.close();
handled = true;
}

View File

@ -1,11 +1,11 @@
chrome.manifest
components/PdfRedirector.js
components/PdfStreamConverter.js
content/PdfJs.jsm
content/PdfJsTelemetry.jsm
content/build/pdf.js
content/build/pdf.worker.js
content/network.js
content/PdfJs.jsm
content/PdfJsTelemetry.jsm
content/web/debugger.js
content/web/images/annotation-check.svg
content/web/images/annotation-comment.svg

View File

@ -44,6 +44,8 @@ let IndexedDB = {
}
let prompt = Cc["@mozilla.org/content-permission/prompt;1"].createInstance(Ci.nsIContentPermissionPrompt);
let types = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
types.appendElement({type: type, access: "unused"}, false);
// If the user waits a long time before responding, we default to UNKNOWN_ACTION.
let timeoutId = setTimeout(function() {
@ -60,7 +62,7 @@ let IndexedDB = {
}
prompt.prompt({
type: type,
types: types,
uri: Services.io.newURI(payload.location, null, null),
window: null,
element: aMessage.target,

View File

@ -56,8 +56,8 @@ ContentPermissionPrompt.prototype = {
return chromeWin.Browser.getNotificationBox(request.element);
},
handleExistingPermission: function handleExistingPermission(request) {
let result = Services.perms.testExactPermissionFromPrincipal(request.principal, request.type);
handleExistingPermission: function handleExistingPermission(request, type) {
let result = Services.perms.testExactPermissionFromPrincipal(request.principal, type);
if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
request.allow();
return true;
@ -70,20 +70,28 @@ ContentPermissionPrompt.prototype = {
},
prompt: function(request) {
// Only allow exactly one permission rquest here.
let types = request.types.QueryInterface(Ci.nsIArray);
if (types.length != 1) {
request.cancel();
return;
}
let perm = types.queryElementAt(0, Ci.nsIContentPermissionType);
// returns true if the request was handled
if (this.handleExistingPermission(request))
if (this.handleExistingPermission(request, perm.type))
return;
let pm = Services.perms;
let notificationBox = this.getNotificationBoxForRequest(request);
let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
let notification = notificationBox.getNotificationWithValue(request.type);
let notification = notificationBox.getNotificationWithValue(perm.type);
if (notification)
return;
let entityName = kEntities[request.type];
let icon = kIcons[request.type] || "";
let entityName = kEntities[perm.type];
let icon = kIcons[perm.type] || "";
let buttons = [{
label: browserBundle.GetStringFromName(entityName + ".allow"),
@ -96,7 +104,7 @@ ContentPermissionPrompt.prototype = {
label: browserBundle.GetStringFromName("contentPermissions.alwaysForSite"),
accessKey: "",
callback: function(notification) {
Services.perms.addFromPrincipal(request.principal, request.type, Ci.nsIPermissionManager.ALLOW_ACTION);
Services.perms.addFromPrincipal(request.principal, perm.type, Ci.nsIPermissionManager.ALLOW_ACTION);
request.allow();
}
},
@ -104,7 +112,7 @@ ContentPermissionPrompt.prototype = {
label: browserBundle.GetStringFromName("contentPermissions.neverForSite"),
accessKey: "",
callback: function(notification) {
Services.perms.addFromPrincipal(request.principal, request.type, Ci.nsIPermissionManager.DENY_ACTION);
Services.perms.addFromPrincipal(request.principal, perm.type, Ci.nsIPermissionManager.DENY_ACTION);
request.cancel();
}
}];
@ -112,12 +120,12 @@ ContentPermissionPrompt.prototype = {
let message = browserBundle.formatStringFromName(entityName + ".wantsTo",
[request.principal.URI.host], 1);
let newBar = notificationBox.appendNotification(message,
request.type,
perm.type,
icon,
notificationBox.PRIORITY_WARNING_MEDIUM,
buttons);
if (request.type == "geolocation") {
if (perm.type == "geolocation") {
// Add the "learn more" link.
let link = newBar.ownerDocument.createElement("label");
link.setAttribute("value", browserBundle.GetStringFromName("geolocation.learnMore"));

View File

@ -1296,30 +1296,22 @@ toolbar[iconsize="small"] #webrtc-status-button {
#plugins-notification-icon {
list-style-image: url(chrome://browser/skin/notification-pluginNormal.png);
}
#alert-plugins-notification-icon {
#plugins-notification-icon.plugin-hidden {
list-style-image: url(chrome://browser/skin/notification-pluginAlert.png);
}
#blocked-plugins-notification-icon {
#plugins-notification-icon.plugin-blocked {
list-style-image: url(chrome://browser/skin/notification-pluginBlocked.png);
}
#plugins-notification-icon,
#alert-plugins-notification-icon,
#blocked-plugins-notification-icon {
#plugins-notification-icon {
-moz-image-region: rect(0, 16px, 16px, 0);
}
#plugins-notification-icon:hover,
#alert-plugins-notification-icon:hover,
#blocked-plugins-notification-icon:hover {
#plugins-notification-icon:hover {
-moz-image-region: rect(0, 32px, 16px, 16px);
}
#plugins-notification-icon:active,
#alert-plugins-notification-icon:active,
#blocked-plugins-notification-icon:active {
#plugins-notification-icon:active {
-moz-image-region: rect(0, 48px, 16px, 32px);
}
@ -1334,7 +1326,7 @@ toolbar[iconsize="small"] #webrtc-status-button {
visibility: collapse;
}
#blocked-plugins-notification-icon[showing] {
#plugins-notification-icon.plugin-blocked[showing] {
animation: pluginBlockedNotification 500ms ease 0s 5 alternate both;
}

View File

@ -24,8 +24,11 @@
-moz-border-end: 1px solid #222426; /* Match the sources list's dark margin. */
}
#sources-toolbar .devtools-toolbarbutton {
min-width: 32px;
}
#pretty-print {
min-width: 0;
font-weight: bold;
}

View File

@ -3,3 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
%include ../../shared/devtools/shadereditor.inc.css
.side-menu-widget-item-checkbox > .checkbox-spacer-box {
-moz-appearance: none;
}

View File

@ -317,11 +317,6 @@
background-image: linear-gradient(#fff, #eee);
}
.side-menu-widget-group-checkbox {
margin: 0;
padding: 0;
}
/* SideMenuWidget items */
.side-menu-widget-item[theme="dark"] {
@ -379,11 +374,6 @@
width: 8px;
}
.side-menu-widget-item-checkbox {
-moz-margin-start: 4px;
-moz-margin-end: -6px;
}
.side-menu-widget-item-other {
background: url(background-noise-toolbar.png), hsla(208,11%,27%, 0.65);
}
@ -403,6 +393,25 @@
text-shadow: 0 1px 1px #111;
}
/* SideMenuWidget checkboxes */
.side-menu-widget-group-checkbox {
margin: 0;
-moz-margin-end: 4px;
}
.side-menu-widget-item-checkbox {
margin: 0;
-moz-margin-start: 4px;
-moz-margin-end: -4px;
}
.side-menu-widget-group-checkbox .checkbox-spacer-box,
.side-menu-widget-item-checkbox .checkbox-spacer-box {
margin: 0;
border: none;
}
/* SideMenuWidget misc */
.side-menu-widget-empty-notice-container {

View File

@ -3207,29 +3207,23 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
list-style-image: url(chrome://browser/skin/notification-pluginNormal.png);
}
#alert-plugins-notification-icon {
#plugins-notification-icon.plugin-hidden {
list-style-image: url(chrome://browser/skin/notification-pluginAlert.png);
}
#blocked-plugins-notification-icon {
#plugins-notification-icon.plugin-blocked {
list-style-image: url(chrome://browser/skin/notification-pluginBlocked.png);
}
#plugins-notification-icon,
#alert-plugins-notification-icon,
#blocked-plugins-notification-icon {
#plugins-notification-icon {
-moz-image-region: rect(0, 16px, 16px, 0);
}
#plugins-notification-icon:hover,
#alert-plugins-notification-icon:hover,
#blocked-plugins-notification-icon:hover {
#plugins-notification-icon:hover {
-moz-image-region: rect(0, 32px, 16px, 16px);
}
#plugins-notification-icon:active,
#alert-plugins-notification-icon:active,
#blocked-plugins-notification-icon:active {
#plugins-notification-icon:active {
-moz-image-region: rect(0, 48px, 16px, 32px);
}
@ -3238,29 +3232,23 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
list-style-image: url(chrome://browser/skin/notification-pluginNormal@2x.png);
}
#alert-plugins-notification-icon {
#plugins-notification-icon.plugin-hidden {
list-style-image: url(chrome://browser/skin/notification-pluginAlert@2x.png);
}
#blocked-plugins-notification-icon {
#plugins-notification-icon.plugin-blocked {
list-style-image: url(chrome://browser/skin/notification-pluginBlocked@2x.png);
}
#plugins-notification-icon,
#alert-plugins-notification-icon,
#blocked-plugins-notification-icon {
#plugins-notification-icon {
-moz-image-region: rect(0, 32px, 32px, 0);
}
#plugins-notification-icon:hover,
#alert-plugins-notification-icon:hover,
#blocked-plugins-notification-icon:hover {
#plugins-notification-icon:hover {
-moz-image-region: rect(0, 64px, 32px, 32px);
}
#plugins-notification-icon:active,
#alert-plugins-notification-icon:active,
#blocked-plugins-notification-icon:active {
#plugins-notification-icon:active {
-moz-image-region: rect(0, 96px, 32px, 64px);
}
}
@ -3281,7 +3269,7 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
visibility: collapse;
}
#blocked-plugins-notification-icon[showing] {
#plugins-notification-icon.plugin-blocked[showing] {
animation: pluginBlockedNotification 500ms ease 0s 5 alternate both;
}

View File

@ -26,8 +26,11 @@
-moz-border-end: 1px solid #222426; /* Match the sources list's dark margin. */
}
#sources-toolbar .devtools-toolbarbutton {
min-width: 32px;
}
#pretty-print {
min-width: 0;
font-weight: bold;
}

View File

@ -317,11 +317,6 @@
background-image: linear-gradient(#fff, #eee);
}
.side-menu-widget-group-checkbox {
margin: 0;
padding: 0;
}
/* SideMenuWidget items */
.side-menu-widget-item[theme="dark"] {
@ -379,11 +374,6 @@
width: 8px;
}
.side-menu-widget-item-checkbox {
-moz-margin-start: 4px;
-moz-margin-end: -6px;
}
.side-menu-widget-item-other {
background: url(background-noise-toolbar.png), hsla(208,11%,27%, 0.65);
}
@ -403,6 +393,19 @@
text-shadow: 0 1px 1px #111;
}
/* SideMenuWidget checkboxes */
.side-menu-widget-group-checkbox {
margin: 0;
-moz-margin-end: 4px;
}
.side-menu-widget-item-checkbox {
margin: 0;
-moz-margin-start: 4px;
-moz-margin-end: -4px;
}
/* SideMenuWidget misc */
.side-menu-widget-empty-notice-container {

View File

@ -51,8 +51,6 @@
.side-menu-widget-item-checkbox {
-moz-appearance: none;
-moz-margin-end: -6px;
padding: 0;
opacity: 0;
transition: opacity .15s ease-out 0s;
}
@ -66,10 +64,10 @@
transition: opacity .15s ease-out 0s;
}
.side-menu-widget-item-checkbox > .checkbox-check {
.side-menu-widget-item-checkbox .checkbox-check {
-moz-appearance: none;
background: none;
background-image: url("chrome://browser/skin/devtools/itemToggle.png");
background-image: url(itemToggle.png);
background-repeat: no-repeat;
background-clip: content-box;
background-size: 32px 16px;
@ -79,7 +77,7 @@
border: 0;
}
.side-menu-widget-item-checkbox[checked] > .checkbox-check {
.side-menu-widget-item-checkbox[checked] .checkbox-check {
background-position: 0 0;
}
@ -105,3 +103,32 @@
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);
}
}

View File

@ -2568,30 +2568,23 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
#plugins-notification-icon {
list-style-image: url(chrome://browser/skin/notification-pluginNormal.png);
}
#alert-plugins-notification-icon {
#plugins-notification-icon.plugin-hidden {
list-style-image: url(chrome://browser/skin/notification-pluginAlert.png);
}
#blocked-plugins-notification-icon {
#plugins-notification-icon.plugin-blocked {
list-style-image: url(chrome://browser/skin/notification-pluginBlocked.png);
}
#plugins-notification-icon,
#alert-plugins-notification-icon,
#blocked-plugins-notification-icon {
#plugins-notification-icon {
-moz-image-region: rect(0, 16px, 16px, 0);
}
#plugins-notification-icon:hover,
#alert-plugins-notification-icon:hover,
#blocked-plugins-notification-icon:hover {
#plugins-notification-icon:hover {
-moz-image-region: rect(0, 32px, 16px, 16px);
}
#plugins-notification-icon:active,
#alert-plugins-notification-icon:active,
#blocked-plugins-notification-icon:active {
#plugins-notification-icon:active {
-moz-image-region: rect(0, 48px, 16px, 32px);
}
@ -2606,7 +2599,7 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
visibility: collapse;
}
#blocked-plugins-notification-icon[showing] {
#plugins-notification-icon.plugin-blocked[showing] {
animation: pluginBlockedNotification 500ms ease 0s 5 alternate both;
}

View File

@ -24,8 +24,11 @@
-moz-border-end: 1px solid #222426; /* Match the sources list's dark margin. */
}
#sources-toolbar .devtools-toolbarbutton {
min-width: 32px;
}
#pretty-print {
min-width: 0;
font-weight: bold;
}

View File

@ -321,11 +321,6 @@
background-image: linear-gradient(#fff, #eee);
}
.side-menu-widget-group-checkbox {
margin: 0;
padding: 0;
}
/* SideMenuWidget items */
.side-menu-widget-item[theme="dark"] {
@ -383,11 +378,6 @@
width: 8px;
}
.side-menu-widget-item-checkbox {
-moz-margin-start: 4px;
-moz-margin-end: -6px;
}
.side-menu-widget-item-other {
background: url(background-noise-toolbar.png), hsla(208,11%,27%, 0.65);
}
@ -406,6 +396,19 @@
color: #f5f7fa;
}
/* SideMenuWidget checkboxes */
.side-menu-widget-group-checkbox {
margin: 0;
-moz-margin-end: 4px;
}
.side-menu-widget-item-checkbox {
margin: 0;
-moz-margin-start: 4px;
-moz-margin-end: -4px;
}
/* SideMenuWidget misc */
.side-menu-widget-empty-notice-container {

View File

@ -11,7 +11,6 @@
#include "plstr.h"
#include "nsXPIDLString.h"
#include "nsCOMPtr.h"
#include "jsapi.h"
#include "nsIJSRuntimeService.h"
#include "nsIServiceManager.h"
#include "nsMemory.h"

View File

@ -84,8 +84,6 @@
#include "nsIBaseWindow.h"
#include "nsIWidget.h"
#include "jsapi.h"
#include "nsNodeInfoManager.h"
#include "nsICategoryManager.h"
#include "nsIDOMDocumentType.h"
@ -2247,11 +2245,11 @@ Element::PostHandleEventForLinks(nsEventChainPostVisitor& aVisitor)
}
break;
case NS_MOUSE_CLICK:
if (aVisitor.mEvent->IsLeftClickEvent()) {
WidgetInputEvent* inputEvent = aVisitor.mEvent->AsInputEvent();
if (inputEvent->IsControl() || inputEvent->IsMeta() ||
inputEvent->IsAlt() ||inputEvent->IsShift()) {
case NS_MOUSE_CLICK: {
WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
if (mouseEvent->IsLeftClickEvent()) {
if (mouseEvent->IsControl() || mouseEvent->IsMeta() ||
mouseEvent->IsAlt() ||mouseEvent->IsShift()) {
break;
}
@ -2260,7 +2258,7 @@ Element::PostHandleEventForLinks(nsEventChainPostVisitor& aVisitor)
if (shell) {
// single-click
nsEventStatus status = nsEventStatus_eIgnore;
InternalUIEvent actEvent(aVisitor.mEvent->mFlags.mIsTrusted,
InternalUIEvent actEvent(mouseEvent->mFlags.mIsTrusted,
NS_UI_ACTIVATE, 1);
rv = shell->HandleDOMEventWithTarget(this, &actEvent, &status);
@ -2270,7 +2268,7 @@ Element::PostHandleEventForLinks(nsEventChainPostVisitor& aVisitor)
}
}
break;
}
case NS_UI_ACTIVATE:
{
if (aVisitor.mEvent->originalTarget == this) {

View File

@ -79,7 +79,7 @@
#include "nsIBaseWindow.h"
#include "nsIWidget.h"
#include "jsapi.h"
#include "js/GCAPI.h"
#include "nsNodeInfoManager.h"
#include "nsICategoryManager.h"

View File

@ -184,7 +184,7 @@ nsDOMFileReader::GetReadyState(uint16_t *aReadyState)
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;
}
@ -192,21 +192,21 @@ nsDOMFileReader::GetResult(JSContext* aCx, ErrorResult& aRv)
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;
}

View File

@ -215,6 +215,8 @@
#include "mozilla/dom/XPathEvaluator.h"
#include "nsIDocumentEncoder.h"
#include "nsIStructuredCloneContainer.h"
#include "nsIMutableArray.h"
#include "nsContentPermissionHelper.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -10647,17 +10649,11 @@ NS_IMPL_ISUPPORTS_INHERITED1(nsPointerLockPermissionRequest,
nsIContentPermissionRequest)
NS_IMETHODIMP
nsPointerLockPermissionRequest::GetType(nsACString& aType)
nsPointerLockPermissionRequest::GetTypes(nsIArray** aTypes)
{
aType = "pointerLock";
return NS_OK;
}
NS_IMETHODIMP
nsPointerLockPermissionRequest::GetAccess(nsACString& aAccess)
{
aAccess = "unused";
return NS_OK;
return CreatePermissionArray(NS_LITERAL_CSTRING("pointerLock"),
NS_LITERAL_CSTRING("unused"),
aTypes);
}
NS_IMETHODIMP
@ -11091,26 +11087,30 @@ nsDocument::GetVisibilityState(nsAString& aState)
/* virtual */ void
nsIDocument::DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const
{
aWindowSizes->mDOMOther +=
aWindowSizes->mDOMOtherSize +=
nsINode::SizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
if (mPresShell) {
mPresShell->AddSizeOfIncludingThis(aWindowSizes->mMallocSizeOf,
&aWindowSizes->mArenaStats,
&aWindowSizes->mLayoutPresShell,
&aWindowSizes->mLayoutStyleSets,
&aWindowSizes->mLayoutTextRuns,
&aWindowSizes->mLayoutPresContext);
&aWindowSizes->mLayoutPresShellSize,
&aWindowSizes->mLayoutStyleSetsSize,
&aWindowSizes->mLayoutTextRunsSize,
&aWindowSizes->mLayoutPresContextSize);
}
aWindowSizes->mPropertyTables +=
aWindowSizes->mPropertyTablesSize +=
mPropertyTable.SizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
for (uint32_t i = 0, count = mExtraPropertyTables.Length();
i < count; ++i) {
aWindowSizes->mPropertyTables +=
aWindowSizes->mPropertyTablesSize +=
mExtraPropertyTables[i]->SizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
}
if (nsEventListenerManager* elm = GetExistingListenerManager()) {
aWindowSizes->mDOMEventListenersCount += elm->ListenerCount();
}
// Measurement of the following members may be added later if DMD finds it
// is worthwhile:
// - many!
@ -11119,7 +11119,7 @@ nsIDocument::DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const
void
nsIDocument::DocAddSizeOfIncludingThis(nsWindowSizes* aWindowSizes) const
{
aWindowSizes->mDOMOther += aWindowSizes->mMallocSizeOf(this);
aWindowSizes->mDOMOtherSize += aWindowSizes->mMallocSizeOf(this);
DocAddSizeOfExcludingThis(aWindowSizes);
}
@ -11155,58 +11155,62 @@ nsDocument::DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const
switch (node->NodeType()) {
case nsIDOMNode::ELEMENT_NODE:
p = &aWindowSizes->mDOMElementNodes;
p = &aWindowSizes->mDOMElementNodesSize;
break;
case nsIDOMNode::TEXT_NODE:
p = &aWindowSizes->mDOMTextNodes;
p = &aWindowSizes->mDOMTextNodesSize;
break;
case nsIDOMNode::CDATA_SECTION_NODE:
p = &aWindowSizes->mDOMCDATANodes;
p = &aWindowSizes->mDOMCDATANodesSize;
break;
case nsIDOMNode::COMMENT_NODE:
p = &aWindowSizes->mDOMCommentNodes;
p = &aWindowSizes->mDOMCommentNodesSize;
break;
default:
p = &aWindowSizes->mDOMOther;
p = &aWindowSizes->mDOMOtherSize;
break;
}
*p += nodeSize;
if (nsEventListenerManager* elm = node->GetExistingListenerManager()) {
aWindowSizes->mDOMEventListenersCount += elm->ListenerCount();
}
}
aWindowSizes->mStyleSheets +=
aWindowSizes->mStyleSheetsSize +=
mStyleSheets.SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
aWindowSizes->mMallocSizeOf);
aWindowSizes->mStyleSheets +=
aWindowSizes->mStyleSheetsSize +=
mCatalogSheets.SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
aWindowSizes->mMallocSizeOf);
aWindowSizes->mStyleSheets +=
aWindowSizes->mStyleSheetsSize +=
mAdditionalSheets[eAgentSheet].
SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
aWindowSizes->mMallocSizeOf);
aWindowSizes->mStyleSheets +=
aWindowSizes->mStyleSheetsSize +=
mAdditionalSheets[eUserSheet].
SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
aWindowSizes->mMallocSizeOf);
aWindowSizes->mStyleSheets +=
aWindowSizes->mStyleSheetsSize +=
mAdditionalSheets[eAuthorSheet].
SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
aWindowSizes->mMallocSizeOf);
// Lumping in the loader with the style-sheets size is not ideal,
// but most of the things in there are in fact stylesheets, so it
// doesn't seem worthwhile to separate it out.
aWindowSizes->mStyleSheets +=
aWindowSizes->mStyleSheetsSize +=
CSSLoader()->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
aWindowSizes->mDOMOther +=
aWindowSizes->mDOMOtherSize +=
mAttrStyleSheet ?
mAttrStyleSheet->DOMSizeOfIncludingThis(aWindowSizes->mMallocSizeOf) :
0;
aWindowSizes->mDOMOther +=
aWindowSizes->mDOMOtherSize +=
mStyledLinks.SizeOfExcludingThis(nullptr, aWindowSizes->mMallocSizeOf);
aWindowSizes->mDOMOther +=
aWindowSizes->mDOMOtherSize +=
mIdentifierMap.SizeOfExcludingThis(nsIdentifierMapEntry::SizeOfExcludingThis,
aWindowSizes->mMallocSizeOf);

View File

@ -954,7 +954,7 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
JS::Rooted<JSObject*> thisObject(ctx, thisValue.toObjectOrNull());
JSAutoCompartment tac(ctx, thisObject);
if (!JS_WrapValue(ctx, argv.address())) {
if (!JS_WrapValue(ctx, &argv)) {
return NS_ERROR_UNEXPECTED;
}

View File

@ -3146,17 +3146,15 @@ nsObjectLoadingContent::LegacyCall(JSContext* aCx,
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();
}
@ -3189,7 +3187,7 @@ nsObjectLoadingContent::LegacyCall(JSContext* aCx,
}
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);

View File

@ -88,6 +88,7 @@
#include "mozilla/dom/HTMLVideoElement.h"
#include "mozilla/dom/TextMetrics.h"
#include "mozilla/dom/UnionTypes.h"
#include "nsGlobalWindow.h"
#ifdef USE_SKIA_GPU
#undef free // apparently defined by some windows header, clashing with a free()
@ -2858,27 +2859,27 @@ CanvasRenderingContext2D::SetMozDashOffset(double mozDashOffset)
}
void
CanvasRenderingContext2D::SetLineDash(const mozilla::dom::AutoSequence<double>& mSegments) {
CanvasRenderingContext2D::SetLineDash(const mozilla::dom::AutoSequence<double>& aSegments) {
FallibleTArray<mozilla::gfx::Float>& dash = CurrentState().dash;
dash.Clear();
for(mozilla::dom::AutoSequence<double>::index_type x = 0; x < mSegments.Length(); x++) {
dash.AppendElement(mSegments[x]);
for (uint32_t x = 0; x < aSegments.Length(); x++) {
dash.AppendElement(aSegments[x]);
}
if(mSegments.Length()%2) { // If the number of elements is odd, concatenate again
for(mozilla::dom::AutoSequence<double>::index_type x = 0; x < mSegments.Length(); x++) {
dash.AppendElement(mSegments[x]);
if (aSegments.Length() % 2) { // If the number of elements is odd, concatenate again
for (uint32_t x = 0; x < aSegments.Length(); x++) {
dash.AppendElement(aSegments[x]);
}
}
}
void
CanvasRenderingContext2D::GetLineDash(nsTArray<double>& mSegments) const {
CanvasRenderingContext2D::GetLineDash(nsTArray<double>& aSegments) const {
const FallibleTArray<mozilla::gfx::Float>& dash = CurrentState().dash;
mSegments.Clear();
aSegments.Clear();
for(FallibleTArray<mozilla::gfx::Float>::index_type x = 0; x < dash.Length(); x++) {
mSegments.AppendElement(dash[x]);
for (uint32_t x = 0; x < dash.Length(); x++) {
aSegments.AppendElement(dash[x]);
}
}
@ -3224,7 +3225,7 @@ CanvasRenderingContext2D::GetGlobalCompositeOperation(nsAString& op,
}
void
CanvasRenderingContext2D::DrawWindow(nsIDOMWindow* window, double x,
CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& window, double x,
double y, double w, double h,
const nsAString& bgColor,
uint32_t flags, ErrorResult& error)
@ -3253,16 +3254,13 @@ CanvasRenderingContext2D::DrawWindow(nsIDOMWindow* window, double x,
// Flush layout updates
if (!(flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH)) {
nsContentUtils::FlushLayoutForTree(window);
nsContentUtils::FlushLayoutForTree(&window);
}
nsRefPtr<nsPresContext> presContext;
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(window);
if (win) {
nsIDocShell* docshell = win->GetDocShell();
if (docshell) {
docshell->GetPresContext(getter_AddRefs(presContext));
}
nsIDocShell* docshell = window.GetDocShell();
if (docshell) {
docshell->GetPresContext(getter_AddRefs(presContext));
}
if (!presContext) {
error.Throw(NS_ERROR_FAILURE);

View File

@ -24,6 +24,7 @@
#include "gfx2DGlue.h"
#include "imgIEncoder.h"
class nsGlobalWindow;
class nsXULElement;
namespace mozilla {
@ -369,7 +370,7 @@ public:
}
}
void DrawWindow(nsIDOMWindow* window, double x, double y, double w, double h,
void DrawWindow(nsGlobalWindow& window, double x, double y, double w, double h,
const nsAString& bgColor, uint32_t flags,
mozilla::ErrorResult& error);
void AsyncDrawXULElement(nsXULElement& elem, double x, double y, double w,

View File

@ -67,7 +67,7 @@ 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;

View File

@ -400,6 +400,11 @@ public:
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
uint32_t ListenerCount() const
{
return mListeners.Length();
}
void MarkForCC();
mozilla::dom::EventTarget* GetTarget() { return mTarget; }

View File

@ -1009,22 +1009,23 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
#endif
// Store last known screenPoint and clientPoint so pointer lock
// can use these values as constants.
WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
if (aEvent->mFlags.mIsTrusted &&
((aEvent->IsMouseDerivedEvent() && IsMouseEventReal(aEvent)) ||
aEvent->eventStructType == NS_WHEEL_EVENT)) {
if (!sIsPointerLocked) {
sLastScreenPoint = nsDOMUIEvent::CalculateScreenPoint(aPresContext, aEvent);
sLastClientPoint = nsDOMUIEvent::CalculateClientPoint(aPresContext, aEvent, nullptr);
}
((mouseEvent && IsMouseEventReal(mouseEvent)) ||
aEvent->eventStructType == NS_WHEEL_EVENT) &&
!sIsPointerLocked) {
sLastScreenPoint =
nsDOMUIEvent::CalculateScreenPoint(aPresContext, aEvent);
sLastClientPoint =
nsDOMUIEvent::CalculateClientPoint(aPresContext, aEvent, nullptr);
}
// Do not take account NS_MOUSE_ENTER/EXIT so that loading a page
// when user is not active doesn't change the state to active.
if (aEvent->mFlags.mIsTrusted &&
((aEvent->eventStructType == NS_MOUSE_EVENT &&
IsMouseEventReal(aEvent) &&
aEvent->message != NS_MOUSE_ENTER &&
aEvent->message != NS_MOUSE_EXIT) ||
((mouseEvent && IsMouseEventReal(mouseEvent) &&
mouseEvent->message != NS_MOUSE_ENTER &&
mouseEvent->message != NS_MOUSE_EXIT) ||
aEvent->eventStructType == NS_WHEEL_EVENT ||
aEvent->eventStructType == NS_KEY_EVENT)) {
if (gMouseOrKeyboardEventCounter == 0) {
@ -1044,7 +1045,6 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
switch (aEvent->message) {
case NS_MOUSE_BUTTON_DOWN: {
WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
switch (mouseEvent->button) {
case WidgetMouseEvent::eLeftButton:
#ifndef XP_OS2
@ -1069,7 +1069,6 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
break;
}
case NS_MOUSE_BUTTON_UP: {
WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
switch (mouseEvent->button) {
case WidgetMouseEvent::eLeftButton:
if (Prefs::ClickHoldContextMenu()) {
@ -1095,21 +1094,18 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
// If the event is not a top-level window exit, then it's not
// really an exit --- we may have traversed widget boundaries but
// we're still in our toplevel window.
{
WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
if (mouseEvent->exit != WidgetMouseEvent::eTopLevel) {
// Treat it as a synthetic move so we don't generate spurious
// "exit" or "move" events. Any necessary "out" or "over" events
// will be generated by GenerateMouseEnterExit
mouseEvent->message = NS_MOUSE_MOVE;
mouseEvent->reason = WidgetMouseEvent::eSynthesized;
// then fall through...
} else {
GenerateMouseEnterExit(mouseEvent);
//This is a window level mouse exit event and should stop here
aEvent->message = 0;
break;
}
if (mouseEvent->exit != WidgetMouseEvent::eTopLevel) {
// Treat it as a synthetic move so we don't generate spurious
// "exit" or "move" events. Any necessary "out" or "over" events
// will be generated by GenerateMouseEnterExit
mouseEvent->message = NS_MOUSE_MOVE;
mouseEvent->reason = WidgetMouseEvent::eSynthesized;
// then fall through...
} else {
GenerateMouseEnterExit(mouseEvent);
//This is a window level mouse exit event and should stop here
aEvent->message = 0;
break;
}
case NS_MOUSE_MOVE: {
// on the Mac, GenerateDragGesture() may not return until the drag
@ -1118,7 +1114,6 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
// that ClearFrameRefs() has been called and it cleared out
// |mCurrentTarget|. As a result, we should pass |mCurrentTarget|
// into UpdateCursor().
WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
GenerateDragGesture(aPresContext, mouseEvent);
UpdateCursor(aPresContext, aEvent, mCurrentTarget, aStatus);
GenerateMouseEnterExit(mouseEvent);

View File

@ -198,8 +198,9 @@ HTMLButtonElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
// cause activation of the input. That is, if we're a click event, or a
// DOMActivate that was dispatched directly, this will be set, but if we're
// a DOMActivate dispatched from click handling, it will not be set.
WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
bool outerActivateEvent =
(aVisitor.mEvent->IsLeftClickEvent() ||
((mouseEvent && mouseEvent->IsLeftClickEvent()) ||
(aVisitor.mEvent->message == NS_UI_ACTIVATE &&
!mInInternalActivate));
@ -225,22 +226,25 @@ HTMLButtonElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
return rv;
}
if (aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault &&
aVisitor.mEvent->IsLeftClickEvent()) {
InternalUIEvent actEvent(aVisitor.mEvent->mFlags.mIsTrusted,
NS_UI_ACTIVATE, 1);
if (aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault) {
WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
if (mouseEvent && mouseEvent->IsLeftClickEvent()) {
InternalUIEvent actEvent(aVisitor.mEvent->mFlags.mIsTrusted,
NS_UI_ACTIVATE, 1);
nsCOMPtr<nsIPresShell> shell = aVisitor.mPresContext->GetPresShell();
if (shell) {
nsEventStatus status = nsEventStatus_eIgnore;
mInInternalActivate = true;
shell->HandleDOMEventWithTarget(this, &actEvent, &status);
mInInternalActivate = false;
nsCOMPtr<nsIPresShell> shell = aVisitor.mPresContext->GetPresShell();
if (shell) {
nsEventStatus status = nsEventStatus_eIgnore;
mInInternalActivate = true;
shell->HandleDOMEventWithTarget(this, &actEvent, &status);
mInInternalActivate = false;
// If activate is cancelled, we must do the same as when click is
// cancelled (revert the checkbox to its original value).
if (status == nsEventStatus_eConsumeNoDefault)
aVisitor.mEventStatus = status;
// If activate is cancelled, we must do the same as when click is
// cancelled (revert the checkbox to its original value).
if (status == nsEventStatus_eConsumeNoDefault) {
aVisitor.mEventStatus = status;
}
}
}
}

View File

@ -373,7 +373,8 @@ HTMLImageElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
// the Generic Element as this could cause a click event to fire
// twice, once by the image frame for the map and once by the Anchor
// element. (bug 39723)
if (aVisitor.mEvent->IsLeftClickEvent()) {
WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
if (mouseEvent && mouseEvent->IsLeftClickEvent()) {
bool isMap = false;
GetIsMap(&isMap);
if (isMap) {

View File

@ -3112,8 +3112,9 @@ HTMLInputElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
// cause activation of the input. That is, if we're a click event, or a
// DOMActivate that was dispatched directly, this will be set, but if we're
// a DOMActivate dispatched from click handling, it will not be set.
WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
bool outerActivateEvent =
(aVisitor.mEvent->IsLeftClickEvent() ||
((mouseEvent && mouseEvent->IsLeftClickEvent()) ||
(aVisitor.mEvent->message == NS_UI_ACTIVATE && !mInInternalActivate));
if (outerActivateEvent) {
@ -3342,14 +3343,18 @@ HTMLInputElement::MaybeInitPickers(nsEventChainPostVisitor& aVisitor)
// - it's the left mouse button.
// We do not prevent non-trusted click because authors can already use
// .click(). However, the pickers will follow the rules of popup-blocking.
if (aVisitor.mEvent->IsLeftClickEvent() &&
!aVisitor.mEvent->mFlags.mDefaultPrevented) {
if (mType == NS_FORM_INPUT_FILE) {
return InitFilePicker(FILE_PICKER_FILE);
}
if (mType == NS_FORM_INPUT_COLOR) {
return InitColorPicker();
}
if (aVisitor.mEvent->mFlags.mDefaultPrevented) {
return NS_OK;
}
WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
if (!(mouseEvent && mouseEvent->IsLeftClickEvent())) {
return NS_OK;
}
if (mType == NS_FORM_INPUT_FILE) {
return InitFilePicker(FILE_PICKER_FILE);
}
if (mType == NS_FORM_INPUT_COLOR) {
return InitColorPicker();
}
return NS_OK;
}
@ -3395,23 +3400,26 @@ HTMLInputElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
// the click event handling, and allow cancellation of DOMActivate to cancel
// the click.
if (aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault &&
!IsSingleLineTextControl(true) &&
aVisitor.mEvent->IsLeftClickEvent() &&
!ShouldPreventDOMActivateDispatch(aVisitor.mEvent->originalTarget)) {
InternalUIEvent actEvent(aVisitor.mEvent->mFlags.mIsTrusted,
NS_UI_ACTIVATE, 1);
!IsSingleLineTextControl(true)) {
WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
if (mouseEvent && mouseEvent->IsLeftClickEvent() &&
!ShouldPreventDOMActivateDispatch(aVisitor.mEvent->originalTarget)) {
InternalUIEvent actEvent(aVisitor.mEvent->mFlags.mIsTrusted,
NS_UI_ACTIVATE, 1);
nsCOMPtr<nsIPresShell> shell = aVisitor.mPresContext->GetPresShell();
if (shell) {
nsEventStatus status = nsEventStatus_eIgnore;
mInInternalActivate = true;
rv = shell->HandleDOMEventWithTarget(this, &actEvent, &status);
mInInternalActivate = false;
nsCOMPtr<nsIPresShell> shell = aVisitor.mPresContext->GetPresShell();
if (shell) {
nsEventStatus status = nsEventStatus_eIgnore;
mInInternalActivate = true;
rv = shell->HandleDOMEventWithTarget(this, &actEvent, &status);
mInInternalActivate = false;
// If activate is cancelled, we must do the same as when click is
// cancelled (revert the checkbox to its original value).
if (status == nsEventStatus_eConsumeNoDefault)
aVisitor.mEventStatus = status;
// If activate is cancelled, we must do the same as when click is
// cancelled (revert the checkbox to its original value).
if (status == nsEventStatus_eConsumeNoDefault) {
aVisitor.mEventStatus = status;
}
}
}
}

View File

@ -114,8 +114,9 @@ DestroyMouseDownPoint(void * /*aObject*/,
nsresult
HTMLLabelElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
{
WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
if (mHandlingEvent ||
(!aVisitor.mEvent->IsLeftClickEvent() &&
(!(mouseEvent && mouseEvent->IsLeftClickEvent()) &&
aVisitor.mEvent->message != NS_MOUSE_BUTTON_DOWN) ||
aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault ||
!aVisitor.mPresContext ||
@ -131,12 +132,11 @@ HTMLLabelElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
mHandlingEvent = true;
switch (aVisitor.mEvent->message) {
case NS_MOUSE_BUTTON_DOWN:
if (aVisitor.mEvent->AsMouseEvent()->button ==
WidgetMouseEvent::eLeftButton) {
if (mouseEvent->button == WidgetMouseEvent::eLeftButton) {
// We reset the mouse-down point on every event because there is
// no guarantee we will reach the NS_MOUSE_CLICK code below.
LayoutDeviceIntPoint* curPoint =
new LayoutDeviceIntPoint(aVisitor.mEvent->refPoint);
new LayoutDeviceIntPoint(mouseEvent->refPoint);
SetProperty(nsGkAtoms::labelMouseDownPtProperty,
static_cast<void*>(curPoint),
DestroyMouseDownPoint);
@ -144,8 +144,7 @@ HTMLLabelElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
break;
case NS_MOUSE_CLICK:
if (aVisitor.mEvent->IsLeftClickEvent()) {
WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
if (mouseEvent->IsLeftClickEvent()) {
LayoutDeviceIntPoint* mouseDownPoint =
static_cast<LayoutDeviceIntPoint*>(
GetProperty(nsGkAtoms::labelMouseDownPtProperty));

View File

@ -11,7 +11,6 @@
#include "nsNetUtil.h"
#include "nsContentUtils.h"
#include "nsUnicharUtils.h" // for nsCaseInsensitiveStringComparator()
#include "jsapi.h"
#include "nsIScriptContext.h"
#include "nsIScriptGlobalObject.h"
#include "nsIXPConnect.h"

View File

@ -24,7 +24,6 @@
#include "nsIScriptSecurityManager.h"
#include "nsIXPConnect.h"
#include "jsapi.h"
#include "nsITimer.h"

View File

@ -1156,7 +1156,7 @@ UndoManager::DispatchTransactionEvent(JSContext* aCx, const nsAString& aType,
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;
}

View File

@ -8,6 +8,8 @@
#include "nsTArray.h"
#include "nsAutoPtr.h"
#include "EncodedFrameContainer.h"
#include "TrackMetadataBase.h"
namespace mozilla {
/**
@ -31,11 +33,20 @@ public:
* END_OF_STREAM if this is the last packet of track.
* Currently, WriteEncodedTrack doesn't support multiple tracks.
*/
virtual nsresult WriteEncodedTrack(const nsTArray<uint8_t>& aBuffer,
int aDuration, uint32_t aFlags = 0) = 0;
virtual nsresult WriteEncodedTrack(const EncodedFrameContainer& aData,
uint32_t aFlags = 0) = 0;
/**
* Set the meta data pointer into muxer
* This function will check the integrity of aMetadata.
* If the meta data isn't well format, this function will return NS_ERROR_FAILURE to caller,
* else save the pointer to mMetadata and return NS_OK.
*/
virtual nsresult SetMetadata(nsRefPtr<TrackMetadataBase> aMetadata) = 0;
enum {
FLUSH_NEEDED = 1 << 0
FLUSH_NEEDED = 1 << 0,
GET_HEADER = 1 << 1
};
/**
@ -50,6 +61,7 @@ public:
uint32_t aFlags = 0) = 0;
protected:
nsRefPtr<TrackMetadataBase> mMetadata;
bool mInitialized;
};
}

View File

@ -0,0 +1,82 @@
/* -*- Mode: C++; tab-width: 2; 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 EncodedFrameContainer_H_
#define EncodedFrameContainer_H_
#include "nsTArray.h"
namespace mozilla {
class EncodedFrame;
/*
* This container is used to carry video or audio encoded data from encoder to muxer.
* The media data object is created by encoder and recycle by the destructor.
* Only allow to store audio or video encoded data in EncodedData.
*/
class EncodedFrameContainer
{
public:
// Append encoded frame data
void AppendEncodedFrame(EncodedFrame* aEncodedFrame)
{
mEncodedFrames.AppendElement(aEncodedFrame);
}
// Retrieve all of the encoded frames
const nsTArray<nsAutoPtr<EncodedFrame> >& GetEncodedFrames() const
{
return mEncodedFrames;
}
private:
// This container is used to store the video or audio encoded packets.
// Muxer should check mFrameType and get the encoded data type from mEncodedFrames.
nsTArray<nsAutoPtr<EncodedFrame> > mEncodedFrames;
};
// Represent one encoded frame
class EncodedFrame
{
public:
EncodedFrame() :
mTimeStamp(0),
mDuration(0),
mFrameType(UNKNOW)
{}
enum FrameType {
I_FRAME, // intraframe
P_FRAME, // predicted frame
B_FRAME, // bidirectionally predicted frame
AUDIO_FRAME, // audio frame
UNKNOW // FrameType not set
};
const nsTArray<uint8_t>& GetFrameData() const
{
return mFrameData;
}
void SetFrameData(nsTArray<uint8_t> *aData)
{
mFrameData.SwapElements(*aData);
}
uint64_t GetTimeStamp() const { return mTimeStamp; }
void SetTimeStamp(uint64_t aTimeStamp) { mTimeStamp = aTimeStamp; }
uint64_t GetDuration() const { return mDuration; }
void SetDuration(uint64_t aDuration) { mDuration = aDuration; }
FrameType GetFrameType() const { return mFrameType; }
void SetFrameType(FrameType aFrameType) { mFrameType = aFrameType; }
private:
// Encoded data
nsTArray<uint8_t> mFrameData;
uint64_t mTimeStamp;
// The playback duration of this packet in number of samples
uint64_t mDuration;
// Represent what is in the FrameData
FrameType mFrameType;
};
}
#endif

View File

@ -5,6 +5,7 @@
#include "MediaEncoder.h"
#include "MediaDecoder.h"
#include "nsIPrincipal.h"
#ifdef MOZ_OGG
#include "OggWriter.h"
#endif
@ -128,17 +129,15 @@ MediaEncoder::CreateEncoder(const nsAString& aMIMEType)
/**
* GetEncodedData() runs as a state machine, starting with mState set to
* ENCODE_HEADER, the procedure should be as follow:
* GET_METADDATA, the procedure should be as follow:
*
* While non-stop
* If mState is ENCODE_HEADER
* Create the header from audio/video encoder
* If a header is generated
* Insert header data into the container stream of writer
* Force copied the final container data from writer
* Return the copy of final container data
* Else
* If mState is GET_METADDATA
* Get the meta data from audio/video encoder
* If a meta data is generated
* Get meta data from audio/video encoder
* Set mState to ENCODE_TRACK
* Return the final container data
*
* If mState is ENCODE_TRACK
* Get encoded track data from audio/video encoder
@ -163,48 +162,36 @@ MediaEncoder::GetEncodedData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
bool reloop = true;
while (reloop) {
switch (mState) {
case ENCODE_HEADER: {
nsTArray<uint8_t> buffer;
nsresult rv = mAudioEncoder->GetHeader(&buffer);
case ENCODE_METADDATA: {
nsRefPtr<TrackMetadataBase> meta = mAudioEncoder->GetMetadata();
MOZ_ASSERT(meta);
nsresult rv = mWriter->SetMetadata(meta);
if (NS_FAILED(rv)) {
// Encoding might be canceled.
mState = ENCODE_DONE;
break;
mState = ENCODE_DONE;
break;
}
if (!buffer.IsEmpty()) {
rv = mWriter->WriteEncodedTrack(buffer, 0);
if (NS_FAILED(rv)) {
LOG("ERROR! Fail to write header to the media container.");
mState = ENCODE_DONE;
break;
}
rv = mWriter->GetContainerData(aOutputBufs,
ContainerWriter::FLUSH_NEEDED);
if (NS_SUCCEEDED(rv)) {
// Successfully get the copy of final container data from writer.
reloop = false;
}
} else {
// No more headers, starts to encode tracks.
mState = ENCODE_TRACK;
rv = mWriter->GetContainerData(aOutputBufs,
ContainerWriter::GET_HEADER);
if (NS_FAILED(rv)) {
mState = ENCODE_DONE;
break;
}
mState = ENCODE_TRACK;
break;
}
case ENCODE_TRACK: {
nsTArray<uint8_t> buffer;
int encodedDuration = 0;
nsresult rv = mAudioEncoder->GetEncodedTrack(&buffer, encodedDuration);
EncodedFrameContainer encodedData;
nsresult rv = mAudioEncoder->GetEncodedTrack(encodedData);
if (NS_FAILED(rv)) {
// Encoding might be canceled.
LOG("ERROR! Fail to get encoded data from encoder.");
mState = ENCODE_DONE;
break;
}
rv = mWriter->WriteEncodedTrack(buffer, encodedDuration,
rv = mWriter->WriteEncodedTrack(encodedData,
mAudioEncoder->IsEncodingComplete() ?
ContainerWriter::END_OF_STREAM : 0);
if (NS_FAILED(rv)) {

View File

@ -51,7 +51,7 @@ class MediaEncoder : public MediaStreamListener
{
public :
enum {
ENCODE_HEADER,
ENCODE_METADDATA,
ENCODE_TRACK,
ENCODE_DONE,
};
@ -64,7 +64,7 @@ public :
, mAudioEncoder(aAudioEncoder)
, mVideoEncoder(aVideoEncoder)
, mMIMEType(aMIMEType)
, mState(MediaEncoder::ENCODE_HEADER)
, mState(MediaEncoder::ENCODE_METADDATA)
, mShutdown(false)
{}

View File

@ -115,7 +115,6 @@ SerializeOpusCommentHeader(const nsCString& aVendor,
OpusTrackEncoder::OpusTrackEncoder()
: AudioTrackEncoder()
, mEncoderState(ID_HEADER)
, mEncoder(nullptr)
, mSourceSegment(new AudioSegment())
, mLookahead(0)
@ -187,8 +186,8 @@ OpusTrackEncoder::GetPacketDuration()
return GetOutputSampleRate() * kFrameDurationMs / 1000;
}
nsresult
OpusTrackEncoder::GetHeader(nsTArray<uint8_t>* aOutput)
nsRefPtr<TrackMetadataBase>
OpusTrackEncoder::GetMetadata()
{
{
// Wait if mEncoder is not initialized.
@ -199,50 +198,35 @@ OpusTrackEncoder::GetHeader(nsTArray<uint8_t>* aOutput)
}
if (mCanceled || mDoneEncoding) {
return NS_ERROR_FAILURE;
return nullptr;
}
switch (mEncoderState) {
case ID_HEADER:
{
OpusMetadata* meta = new OpusMetadata();
mLookahead = 0;
int error = opus_encoder_ctl(mEncoder, OPUS_GET_LOOKAHEAD(&mLookahead));
if (error != OPUS_OK) {
mLookahead = 0;
int error = opus_encoder_ctl(mEncoder, OPUS_GET_LOOKAHEAD(&mLookahead));
if (error != OPUS_OK) {
mLookahead = 0;
}
// The ogg time stamping and pre-skip is always timed at 48000.
SerializeOpusIdHeader(mChannels, mLookahead*(kOpusSamplingRate/mSamplingRate),
mSamplingRate, aOutput);
mEncoderState = COMMENT_HEADER;
break;
}
case COMMENT_HEADER:
{
nsCString vendor;
vendor.AppendASCII(opus_get_version_string());
nsTArray<nsCString> comments;
comments.AppendElement(NS_LITERAL_CSTRING("ENCODER=Mozilla" MOZ_APP_UA_VERSION));
// The ogg time stamping and pre-skip is always timed at 48000.
SerializeOpusIdHeader(mChannels, mLookahead*(kOpusSamplingRate/mSamplingRate),
mSamplingRate, &meta->mIdHeader);
SerializeOpusCommentHeader(vendor, comments, aOutput);
nsCString vendor;
vendor.AppendASCII(opus_get_version_string());
mEncoderState = DATA;
break;
}
case DATA:
// No more headers.
break;
default:
MOZ_CRASH("Invalid state");
}
return NS_OK;
nsTArray<nsCString> comments;
comments.AppendElement(NS_LITERAL_CSTRING("ENCODER=Mozilla" MOZ_APP_UA_VERSION));
SerializeOpusCommentHeader(vendor, comments,
&meta->mCommentHeader);
return meta;
}
nsresult
OpusTrackEncoder::GetEncodedTrack(nsTArray<uint8_t>* aOutput,
int &aOutputDuration)
OpusTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
{
{
// Move all the samples from mRawSegment to mSourceSegment. We only hold
@ -297,6 +281,8 @@ OpusTrackEncoder::GetEncodedTrack(nsTArray<uint8_t>* aOutput,
iter.Next();
}
EncodedFrame* audiodata = new EncodedFrame();
audiodata->SetFrameType(EncodedFrame::AUDIO_FRAME);
if (mResampler) {
nsAutoTArray<AudioDataValue, 9600> resamplingDest;
// We want to consume all the input data, so we slightly oversize the
@ -321,10 +307,10 @@ OpusTrackEncoder::GetEncodedTrack(nsTArray<uint8_t>* aOutput,
pcm = resamplingDest;
// This is always at 48000Hz.
aOutputDuration = outframes;
audiodata->SetDuration(outframes);
} else {
// The ogg time stamping and pre-skip is always timed at 48000.
aOutputDuration = frameCopied * (kOpusSamplingRate / mSamplingRate);
audiodata->SetDuration(frameCopied * (kOpusSamplingRate / mSamplingRate));
}
// Remove the raw data which has been pulled to pcm buffer.
@ -348,26 +334,28 @@ OpusTrackEncoder::GetEncodedTrack(nsTArray<uint8_t>* aOutput,
memset(pcm.Elements() + frameCopied * mChannels, 0,
(GetPacketDuration()-frameCopied)*mChannels*sizeof(AudioDataValue));
}
nsTArray<uint8_t> frameData;
// Encode the data with Opus Encoder.
aOutput->SetLength(MAX_DATA_BYTES);
frameData.SetLength(MAX_DATA_BYTES);
// result is returned as opus error code if it is negative.
int result = 0;
#ifdef MOZ_SAMPLE_TYPE_S16
const opus_int16* pcmBuf = static_cast<opus_int16*>(pcm.Elements());
result = opus_encode(mEncoder, pcmBuf, GetPacketDuration(),
aOutput->Elements(), MAX_DATA_BYTES);
frameData.Elements(), MAX_DATA_BYTES);
#else
const float* pcmBuf = static_cast<float*>(pcm.Elements());
result = opus_encode_float(mEncoder, pcmBuf, GetPacketDuration(),
aOutput->Elements(), MAX_DATA_BYTES);
frameData.Elements(), MAX_DATA_BYTES);
#endif
aOutput->SetLength(result >= 0 ? result : 0);
frameData.SetLength(result >= 0 ? result : 0);
if (result < 0) {
LOG("[Opus] Fail to encode data! Result: %s.", opus_strerror(result));
}
audiodata->SetFrameData(&frameData);
aData.AppendEncodedFrame(audiodata);
return result >= 0 ? NS_OK : NS_ERROR_FAILURE;
}

View File

@ -9,21 +9,32 @@
#include <stdint.h>
#include <speex/speex_resampler.h>
#include "TrackEncoder.h"
#include "nsCOMPtr.h"
struct OpusEncoder;
namespace mozilla {
// Opus meta data structure
class OpusMetadata : public TrackMetadataBase
{
public:
// The ID Header of OggOpus. refer to http://wiki.xiph.org/OggOpus.
nsTArray<uint8_t> mIdHeader;
// The Comment Header of OggOpus.
nsTArray<uint8_t> mCommentHeader;
MetadataKind GetKind() const MOZ_OVERRIDE { return METADATA_OPUS; }
};
class OpusTrackEncoder : public AudioTrackEncoder
{
public:
OpusTrackEncoder();
virtual ~OpusTrackEncoder();
nsresult GetHeader(nsTArray<uint8_t>* aOutput) MOZ_OVERRIDE;
nsRefPtr<TrackMetadataBase> GetMetadata() MOZ_OVERRIDE;
nsresult GetEncodedTrack(nsTArray<uint8_t>* aOutput, int &aOutputDuration) MOZ_OVERRIDE;
nsresult GetEncodedTrack(EncodedFrameContainer& aData) MOZ_OVERRIDE;
protected:
int GetPacketDuration() MOZ_OVERRIDE;
@ -31,12 +42,6 @@ protected:
nsresult Init(int aChannels, int aSamplingRate) MOZ_OVERRIDE;
private:
enum {
ID_HEADER,
COMMENT_HEADER,
DATA
} mEncoderState;
/**
* Get the samplerate of the data to be fed to the Opus encoder. This might be
* different from the intput samplerate if resampling occurs.

View File

@ -10,6 +10,8 @@
#include "AudioSegment.h"
#include "StreamBuffer.h"
#include "TrackMetadataBase.h"
#include "EncodedFrameContainer.h"
namespace mozilla {
@ -48,17 +50,14 @@ public:
virtual void NotifyRemoved(MediaStreamGraph* aGraph) = 0;
/**
* Creates and sets up header for a specific codec. Result data is returned
* in aOutput.
* Creates and sets up meta data for a specific codec
*/
virtual nsresult GetHeader(nsTArray<uint8_t>* aOutput) = 0;
virtual nsRefPtr<TrackMetadataBase> GetMetadata() = 0;
/**
* Encodes raw segments. Result data is returned in aOutput. aOutputDuration
* is the playback duration of this packet in number of samples.
* Encodes raw segments. Result data is returned in aData.
*/
virtual nsresult GetEncodedTrack(nsTArray<uint8_t>* aOutput,
int &aOutputDuration) = 0;
virtual nsresult GetEncodedTrack(EncodedFrameContainer& aData) = 0;
};
class AudioTrackEncoder : public TrackEncoder

View File

@ -0,0 +1,29 @@
/* -*- Mode: C++; tab-width: 2; 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 TrackMetadataBase_h_
#define TrackMetadataBase_h_
#include "nsTArray.h"
#include "nsCOMPtr.h"
namespace mozilla {
// A class represent meta data for various codec format. Only support one track information.
class TrackMetadataBase
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TrackMetadataBase)
enum MetadataKind {
METADATA_OPUS, // Represent the Opus metadata
METADATA_VP8,
METADATA_UNKNOW // Metadata Kind not set
};
virtual ~TrackMetadataBase() {}
// Return the specific metadata kind
virtual MetadataKind GetKind() const = 0;
};
}
#endif

View File

@ -8,8 +8,10 @@ MODULE = 'content'
EXPORTS += [
'ContainerWriter.h',
'EncodedFrameContainer.h',
'MediaEncoder.h',
'TrackEncoder.h',
'TrackMetadataBase.h',
]
SOURCES += [

View File

@ -4,6 +4,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "OggWriter.h"
#include "prtime.h"
#include "OpusTrackEncoder.h"
#undef LOG
#ifdef MOZ_WIDGET_GONK
@ -46,8 +47,29 @@ OggWriter::Init()
}
nsresult
OggWriter::WriteEncodedTrack(const nsTArray<uint8_t>& aBuffer, int aDuration,
OggWriter::WriteEncodedTrack(const EncodedFrameContainer& aData,
uint32_t aFlags)
{
for (uint32_t i = 0; i < aData.GetEncodedFrames().Length(); i++) {
if (aData.GetEncodedFrames()[i]->GetFrameType() != EncodedFrame::AUDIO_FRAME) {
LOG("[OggWriter] wrong encoded data type!");
return NS_ERROR_FAILURE;
}
nsresult rv = WriteEncodedData(aData.GetEncodedFrames()[i]->GetFrameData(),
aData.GetEncodedFrames()[i]->GetDuration(),
aFlags);
if (NS_FAILED(rv)) {
LOG("%p Failed to WriteEncodedTrack!", this);
return rv;
}
}
return NS_OK;
}
nsresult
OggWriter::WriteEncodedData(const nsTArray<uint8_t>& aBuffer, int aDuration,
uint32_t aFlags)
{
if (!mInitialized) {
LOG("[OggWriter] OggWriter has not initialized!");
@ -87,14 +109,49 @@ OggWriter::WriteEncodedTrack(const nsTArray<uint8_t>& aBuffer, int aDuration,
return NS_OK;
}
void
OggWriter::ProduceOggPage(nsTArray<nsTArray<uint8_t> >* aOutputBufs)
{
aOutputBufs->AppendElement();
aOutputBufs->LastElement().SetLength(mOggPage.header_len +
mOggPage.body_len);
memcpy(aOutputBufs->LastElement().Elements(), mOggPage.header,
mOggPage.header_len);
memcpy(aOutputBufs->LastElement().Elements() + mOggPage.header_len,
mOggPage.body, mOggPage.body_len);
}
nsresult
OggWriter::GetContainerData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
uint32_t aFlags)
{
int rc = -1;
// Generate the oggOpus Header
if (aFlags & ContainerWriter::GET_HEADER) {
OpusMetadata* meta = static_cast<OpusMetadata*>(mMetadata.get());
NS_ASSERTION(meta, "should have meta data");
NS_ASSERTION(meta->GetKind() == TrackMetadataBase::METADATA_OPUS,
"should have Opus meta data");
nsresult rv = WriteEncodedData(meta->mIdHeader, 0);
NS_ENSURE_SUCCESS(rv, rv);
rc = ogg_stream_flush(&mOggStreamState, &mOggPage);
NS_ENSURE_TRUE(rc > 0, NS_ERROR_FAILURE);
ProduceOggPage(aOutputBufs);
rv = WriteEncodedData(meta->mCommentHeader, 0);
NS_ENSURE_SUCCESS(rv, rv);
rc = ogg_stream_flush(&mOggStreamState, &mOggPage);
NS_ENSURE_TRUE(rc > 0, NS_ERROR_FAILURE);
ProduceOggPage(aOutputBufs);
return NS_OK;
// Force generate a page even if the amount of packet data is not enough.
// Usually do so after a header packet.
if (aFlags & ContainerWriter::FLUSH_NEEDED) {
} else if (aFlags & ContainerWriter::FLUSH_NEEDED) {
// rc = 0 means no packet to put into a page, or an internal error.
rc = ogg_stream_flush(&mOggStreamState, &mOggPage);
} else {
@ -104,16 +161,32 @@ OggWriter::GetContainerData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
}
if (rc) {
aOutputBufs->AppendElement();
aOutputBufs->LastElement().SetLength(mOggPage.header_len +
mOggPage.body_len);
memcpy(aOutputBufs->LastElement().Elements(), mOggPage.header,
mOggPage.header_len);
memcpy(aOutputBufs->LastElement().Elements() + mOggPage.header_len,
mOggPage.body, mOggPage.body_len);
ProduceOggPage(aOutputBufs);
}
return (rc > 0) ? NS_OK : NS_ERROR_FAILURE;
}
nsresult
OggWriter::SetMetadata(nsRefPtr<TrackMetadataBase> aMetadata)
{
if (aMetadata->GetKind() != TrackMetadataBase::METADATA_OPUS) {
LOG("wrong meta data type!");
return NS_ERROR_FAILURE;
}
// Validate each field of METADATA
OpusMetadata* meta = static_cast<OpusMetadata*>(aMetadata.get());
if (meta->mIdHeader.Length() == 0) {
LOG("miss mIdHeader!");
return NS_ERROR_FAILURE;
}
if (meta->mCommentHeader.Length() == 0) {
LOG("miss mCommentHeader!");
return NS_ERROR_FAILURE;
}
mMetadata = aMetadata;
return NS_OK;
}
}

View File

@ -22,15 +22,23 @@ class OggWriter : public ContainerWriter
public:
OggWriter();
nsresult WriteEncodedTrack(const nsTArray<uint8_t>& aBuffer, int aDuration,
nsresult WriteEncodedTrack(const EncodedFrameContainer &aData,
uint32_t aFlags = 0) MOZ_OVERRIDE;
nsresult GetContainerData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
uint32_t aFlags = 0) MOZ_OVERRIDE;
// Check metadata type integrity and reject unacceptable track encoder.
nsresult SetMetadata(nsRefPtr<TrackMetadataBase> aMetadata) MOZ_OVERRIDE;
private:
nsresult Init();
nsresult WriteEncodedData(const nsTArray<uint8_t>& aBuffer, int aDuration,
uint32_t aFlags = 0);
void ProduceOggPage(nsTArray<nsTArray<uint8_t> >* aOutputBufs);
ogg_stream_state mOggStreamState;
ogg_page mOggPage;
ogg_packet mPacket;

View File

@ -33,7 +33,6 @@
#include "nsRuleProcessorData.h"
#include "nsIWeakReference.h"
#include "jsapi.h"
#include "nsIXPConnect.h"
#include "nsDOMCID.h"
#include "nsIDOMScriptObjectFactory.h"

View File

@ -345,7 +345,7 @@ nsXBLProtoImplField::InstallAccessors(JSContext* aCx,
// 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,
@ -445,9 +445,9 @@ nsXBLProtoImplField::InstallField(nsIScriptContext* aContext,
// 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;

View File

@ -14,7 +14,6 @@
#include "nsXULContentSink.h"
#include "jsapi.h"
#include "jsfriendapi.h"
#include "nsCOMPtr.h"

Some files were not shown because too many files have changed in this diff Show More