mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 22:25:30 +00:00
Merge m-c to elm
This commit is contained in:
commit
73b0ef826b
2
CLOBBER
2
CLOBBER
@ -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).
|
||||
|
@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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]
|
||||
|
@ -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>
|
||||
|
67
accessible/tests/mochitest/events/test_textselchange.html
Normal file
67
accessible/tests/mochitest/events/test_textselchange.html
Normal 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>
|
@ -16,6 +16,8 @@
|
||||
function doTest()
|
||||
{
|
||||
// Hit testing. See bug #726097
|
||||
getNode("hittest").scrollIntoView(true);
|
||||
|
||||
var hititem = getAccessible("hititem");
|
||||
var hittest = getAccessible("hittest");
|
||||
|
||||
|
@ -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"));
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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]);
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"revision": "c09f78f6bdd9c8c3cea3943f8a6fe96c760d7de7",
|
||||
"revision": "166023c341cca944563c410f05dff37a992cb05b",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -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);
|
||||
},
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;"
|
||||
|
@ -1277,7 +1277,7 @@ nsContextMenu.prototype = {
|
||||
},
|
||||
|
||||
playPlugin: function() {
|
||||
gPluginHandler._showClickToPlayNotification(this.browser, this.target);
|
||||
gPluginHandler._showClickToPlayNotification(this.browser, this.target, true);
|
||||
},
|
||||
|
||||
hidePlugin: function() {
|
||||
|
@ -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]
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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>
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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"));
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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"
|
||||
|
@ -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) {
|
||||
|
@ -79,7 +79,7 @@
|
||||
#include "nsIBaseWindow.h"
|
||||
#include "nsIWidget.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "js/GCAPI.h"
|
||||
|
||||
#include "nsNodeInfoManager.h"
|
||||
#include "nsICategoryManager.h"
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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; }
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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"
|
||||
|
@ -24,7 +24,6 @@
|
||||
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsIXPConnect.h"
|
||||
#include "jsapi.h"
|
||||
|
||||
#include "nsITimer.h"
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
82
content/media/encoder/EncodedFrameContainer.h
Normal file
82
content/media/encoder/EncodedFrameContainer.h
Normal 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
|
@ -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)) {
|
||||
|
@ -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)
|
||||
{}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
29
content/media/encoder/TrackMetadataBase.h
Normal file
29
content/media/encoder/TrackMetadataBase.h
Normal 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
|
@ -8,8 +8,10 @@ MODULE = 'content'
|
||||
|
||||
EXPORTS += [
|
||||
'ContainerWriter.h',
|
||||
'EncodedFrameContainer.h',
|
||||
'MediaEncoder.h',
|
||||
'TrackEncoder.h',
|
||||
'TrackMetadataBase.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -33,7 +33,6 @@
|
||||
#include "nsRuleProcessorData.h"
|
||||
#include "nsIWeakReference.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "nsIXPConnect.h"
|
||||
#include "nsDOMCID.h"
|
||||
#include "nsIDOMScriptObjectFactory.h"
|
||||
|
@ -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;
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user