mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-07 16:28:38 +09:00
Merge remote-tracking branch 'origin/tracking' into custom
This commit is contained in:
commit
eb8748c537
119 changed files with 3277 additions and 1496 deletions
|
|
@ -25,10 +25,8 @@
|
|||
<script>
|
||||
"use strict";
|
||||
var host = document.querySelector("#shadow");
|
||||
if (host.createShadowRoot) {
|
||||
var root = host.createShadowRoot();
|
||||
root.innerHTML = "<h3>Shadow DOM</h3><select multiple></select>";
|
||||
}
|
||||
var root = host.attachShadow({ mode: "open" });
|
||||
root.innerHTML = "<h3>Shadow DOM</h3><select multiple></select>";
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -21,8 +21,8 @@
|
|||
|
||||
// Set up a basic shadow DOM
|
||||
var host = document.querySelector('#shadow');
|
||||
if (host.createShadowRoot) {
|
||||
var root = host.createShadowRoot();
|
||||
if (host.attachShadow) {
|
||||
let root = host.attachShadow({ mode: "open" });
|
||||
root.innerHTML = '<h3>Shadow <em>DOM</em></h3><select multiple></select>';
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6913,6 +6913,27 @@ exports.CSS_PROPERTIES = {
|
|||
"unset"
|
||||
]
|
||||
},
|
||||
"inset": {
|
||||
"isInherited": false,
|
||||
"subproperties": [
|
||||
"top",
|
||||
"right",
|
||||
"bottom",
|
||||
"left"
|
||||
],
|
||||
"supports": [
|
||||
6,
|
||||
8
|
||||
],
|
||||
"values": [
|
||||
"-moz-calc",
|
||||
"auto",
|
||||
"calc",
|
||||
"inherit",
|
||||
"initial",
|
||||
"unset"
|
||||
]
|
||||
},
|
||||
"inset-block-end": {
|
||||
"isInherited": false,
|
||||
"subproperties": [
|
||||
|
|
|
|||
|
|
@ -536,23 +536,5 @@ StyleChildrenIterator::IsNeeded(const Element* aElement)
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
nsIContent*
|
||||
StyleChildrenIterator::GetNextChild()
|
||||
{
|
||||
while (nsIContent* child = AllChildrenIterator::GetNextChild()) {
|
||||
if (IsNativeAnonymousImplementationOfPseudoElement(child)) {
|
||||
// Skip any native-anonymous children that are used to implement pseudo-
|
||||
// elements. These match pseudo-element selectors instead of being
|
||||
// considered a child of their host, and thus the style system needs to
|
||||
// handle them separately.
|
||||
} else {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
#define ChildIterator_h
|
||||
|
||||
#include "nsIContent.h"
|
||||
#include "nsIContentInlines.h"
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Iterates over the children on a node. If a child is an insertion point,
|
||||
|
|
@ -17,9 +19,6 @@
|
|||
* binding's <xbl:content> element.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
class nsIContent;
|
||||
|
||||
namespace mozilla {
|
||||
|
|
@ -189,20 +188,33 @@ protected:
|
|||
class AllChildrenIterator : private FlattenedChildIterator
|
||||
{
|
||||
public:
|
||||
AllChildrenIterator(const nsIContent* aNode, uint32_t aFlags,
|
||||
bool aStartAtBeginning = true) :
|
||||
FlattenedChildIterator(aNode, aFlags, aStartAtBeginning),
|
||||
mAnonKidsIdx(aStartAtBeginning ? UINT32_MAX : 0),
|
||||
mFlags(aFlags), mPhase(aStartAtBeginning ? eAtBegin : eAtEnd) { }
|
||||
AllChildrenIterator(const nsIContent* aNode,
|
||||
uint32_t aFlags,
|
||||
bool aStartAtBeginning = true)
|
||||
: FlattenedChildIterator(aNode, aFlags, aStartAtBeginning)
|
||||
, mAnonKidsIdx(aStartAtBeginning ? UINT32_MAX : 0)
|
||||
, mFlags(aFlags), mPhase(aStartAtBeginning ? eAtBegin : eAtEnd)
|
||||
{
|
||||
}
|
||||
|
||||
AllChildrenIterator(AllChildrenIterator&& aOther)
|
||||
: FlattenedChildIterator(Move(aOther)),
|
||||
mAnonKids(Move(aOther.mAnonKids)), mAnonKidsIdx(aOther.mAnonKidsIdx),
|
||||
mFlags(aOther.mFlags), mPhase(aOther.mPhase)
|
||||
: FlattenedChildIterator(Move(aOther))
|
||||
, mAnonKids(Move(aOther.mAnonKids))
|
||||
, mAnonKidsIdx(aOther.mAnonKidsIdx)
|
||||
, mFlags(aOther.mFlags)
|
||||
, mPhase(aOther.mPhase)
|
||||
#ifdef DEBUG
|
||||
, mMutationGuard(aOther.mMutationGuard)
|
||||
, mMutationGuard(aOther.mMutationGuard)
|
||||
#endif
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
AllChildrenIterator& operator=(AllChildrenIterator&& aOther)
|
||||
{
|
||||
this->~AllChildrenIterator();
|
||||
new (this)AllChildrenIterator(Move(aOther));
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
~AllChildrenIterator() { MOZ_ASSERT(!mMutationGuard.Mutated(0)); }
|
||||
|
|
@ -268,20 +280,43 @@ private:
|
|||
*/
|
||||
class StyleChildrenIterator : private AllChildrenIterator {
|
||||
public:
|
||||
explicit StyleChildrenIterator(const nsIContent* aContent)
|
||||
StyleChildrenIterator(const nsIContent* aContent,
|
||||
bool aStartAtBeginning = true)
|
||||
: AllChildrenIterator(aContent,
|
||||
nsIContent::eAllChildren |
|
||||
nsIContent::eSkipDocumentLevelNativeAnonymousContent)
|
||||
nsIContent::eSkipDocumentLevelNativeAnonymousContent,
|
||||
aStartAtBeginning)
|
||||
{
|
||||
MOZ_COUNT_CTOR(StyleChildrenIterator);
|
||||
}
|
||||
|
||||
StyleChildrenIterator(StyleChildrenIterator&& aOther)
|
||||
: AllChildrenIterator(Move(aOther))
|
||||
{
|
||||
MOZ_COUNT_CTOR(StyleChildrenIterator);
|
||||
}
|
||||
|
||||
StyleChildrenIterator& operator=(StyleChildrenIterator&& aOther)
|
||||
{
|
||||
AllChildrenIterator::operator=(Move(aOther));
|
||||
return *this;
|
||||
}
|
||||
|
||||
~StyleChildrenIterator() { MOZ_COUNT_DTOR(StyleChildrenIterator); }
|
||||
|
||||
nsIContent* GetNextChild();
|
||||
static nsIContent* GetParent(const nsIContent& aContent)
|
||||
{
|
||||
nsINode* node = aContent.GetFlattenedTreeParentNodeForStyle();
|
||||
return node && node->IsContent() ? node->AsContent() : nullptr;
|
||||
}
|
||||
|
||||
// Returns true if we cannot find all the children we need to style by
|
||||
// traversing the siblings of the first child.
|
||||
static bool IsNeeded(const Element* aParent);
|
||||
|
||||
using AllChildrenIterator::GetNextChild;
|
||||
using AllChildrenIterator::GetPreviousChild;
|
||||
using AllChildrenIterator::Seek;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
|||
|
|
@ -4,11 +4,14 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DocumentOrShadowRoot.h"
|
||||
#include "mozilla/EventStateManager.h"
|
||||
#include "mozilla/dom/StyleSheetList.h"
|
||||
#include "nsDocument.h"
|
||||
#include "nsFocusManager.h"
|
||||
#include "ShadowRoot.h"
|
||||
#include "XULDocument.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsSVGUtils.h"
|
||||
|
||||
class nsINode;
|
||||
class nsIDocument;
|
||||
|
|
@ -144,5 +147,158 @@ DocumentOrShadowRoot::GetRetargetedFocusedElement()
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Element*
|
||||
DocumentOrShadowRoot::GetPointerLockElement()
|
||||
{
|
||||
nsCOMPtr<Element> pointerLockedElement =
|
||||
do_QueryReferent(EventStateManager::sPointerLockedElement);
|
||||
if (!pointerLockedElement) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIContent* retargetedPointerLockedElement = Retarget(pointerLockedElement);
|
||||
return
|
||||
retargetedPointerLockedElement && retargetedPointerLockedElement->IsElement() ?
|
||||
retargetedPointerLockedElement->AsElement() : nullptr;
|
||||
}
|
||||
|
||||
Element*
|
||||
DocumentOrShadowRoot::GetFullscreenElement()
|
||||
{
|
||||
if (!AsNode().IsInComposedDoc()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Element* element = AsNode().OwnerDoc()->FullScreenStackTop();
|
||||
NS_ASSERTION(!element ||
|
||||
element->State().HasState(NS_EVENT_STATE_FULL_SCREEN),
|
||||
"Fullscreen element should have fullscreen styles applied");
|
||||
|
||||
nsIContent* retargeted = Retarget(element);
|
||||
if (retargeted && retargeted->IsElement()) {
|
||||
return retargeted->AsElement();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Element*
|
||||
DocumentOrShadowRoot::ElementFromPoint(float aX, float aY)
|
||||
{
|
||||
return ElementFromPointHelper(aX, aY, false, true);
|
||||
}
|
||||
|
||||
void
|
||||
DocumentOrShadowRoot::ElementsFromPoint(float aX, float aY,
|
||||
nsTArray<RefPtr<Element>>& aElements)
|
||||
{
|
||||
ElementsFromPointHelper(aX, aY, nsIDocument::FLUSH_LAYOUT, aElements);
|
||||
}
|
||||
|
||||
Element*
|
||||
DocumentOrShadowRoot::ElementFromPointHelper(float aX, float aY,
|
||||
bool aIgnoreRootScrollFrame,
|
||||
bool aFlushLayout)
|
||||
{
|
||||
AutoTArray<RefPtr<Element>, 1> elementArray;
|
||||
ElementsFromPointHelper(aX, aY,
|
||||
((aIgnoreRootScrollFrame ? nsIDocument::IGNORE_ROOT_SCROLL_FRAME : 0) |
|
||||
(aFlushLayout ? nsIDocument::FLUSH_LAYOUT : 0) |
|
||||
nsIDocument::IS_ELEMENT_FROM_POINT),
|
||||
elementArray);
|
||||
if (elementArray.IsEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
return elementArray[0];
|
||||
}
|
||||
|
||||
void
|
||||
DocumentOrShadowRoot::ElementsFromPointHelper(float aX, float aY,
|
||||
uint32_t aFlags,
|
||||
nsTArray<RefPtr<mozilla::dom::Element>>& aElements)
|
||||
{
|
||||
// As per the the spec, we return null if either coord is negative
|
||||
if (!(aFlags & nsIDocument::IGNORE_ROOT_SCROLL_FRAME) && (aX < 0 || aY < 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
|
||||
nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
|
||||
nsPoint pt(x, y);
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = AsNode().OwnerDoc();
|
||||
|
||||
// Make sure the layout information we get is up-to-date, and
|
||||
// ensure we get a root frame (for everything but XUL)
|
||||
if (aFlags & nsIDocument::FLUSH_LAYOUT) {
|
||||
doc->FlushPendingNotifications(Flush_Layout);
|
||||
}
|
||||
|
||||
nsIPresShell* ps = doc->GetShell();
|
||||
if (!ps) {
|
||||
return;
|
||||
}
|
||||
nsIFrame* rootFrame = ps->GetRootFrame();
|
||||
|
||||
// XUL docs, unlike HTML, have no frame tree until everything's done loading
|
||||
if (!rootFrame) {
|
||||
return; // return null to premature XUL callers as a reminder to wait
|
||||
}
|
||||
|
||||
nsTArray<nsIFrame*> outFrames;
|
||||
// Emulate what GetFrameAtPoint does, since we want all the frames under our
|
||||
// point.
|
||||
nsLayoutUtils::GetFramesForArea(rootFrame, nsRect(pt, nsSize(1, 1)), outFrames,
|
||||
nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC |
|
||||
((aFlags & nsIDocument::IGNORE_ROOT_SCROLL_FRAME) ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0));
|
||||
|
||||
// Dunno when this would ever happen, as we should at least have a root frame under us?
|
||||
if (outFrames.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Used to filter out repeated elements in sequence.
|
||||
nsIContent* lastAdded = nullptr;
|
||||
|
||||
for (uint32_t i = 0; i < outFrames.Length(); i++) {
|
||||
nsIContent* node = doc->GetContentInThisDocument(outFrames[i]);
|
||||
|
||||
if (!node || !node->IsElement()) {
|
||||
// If this helper is called via ElementsFromPoint, we need to make sure
|
||||
// our frame is an element. Otherwise return whatever the top frame is
|
||||
// even if it isn't the top-painted element.
|
||||
// SVG 'text' element's SVGTextFrame doesn't respond to hit-testing, so
|
||||
// if 'node' is a child of such an element then we need to manually defer
|
||||
// to the parent here.
|
||||
if (!(aFlags & nsIDocument::IS_ELEMENT_FROM_POINT) &&
|
||||
!outFrames[i]->IsSVGText()) {
|
||||
continue;
|
||||
}
|
||||
node = node->GetParent();
|
||||
if (node) {
|
||||
ShadowRoot* shadow = ShadowRoot::FromNode(node);
|
||||
if (shadow) {
|
||||
node = shadow->Host();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//XXXsmaug There is plenty of unspec'ed behavior here
|
||||
// https://github.com/w3c/webcomponents/issues/735
|
||||
// https://github.com/w3c/webcomponents/issues/736
|
||||
node = Retarget(node);
|
||||
|
||||
if (node && node != lastAdded) {
|
||||
aElements.AppendElement(node->AsElement());
|
||||
lastAdded = node;
|
||||
// If this helper is called via ElementFromPoint, just return the first
|
||||
// element we find.
|
||||
if (aFlags & nsIDocument::IS_ELEMENT_FROM_POINT) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,6 +114,32 @@ public:
|
|||
|
||||
~DocumentOrShadowRoot() = default;
|
||||
|
||||
Element* GetPointerLockElement();
|
||||
Element* GetFullscreenElement();
|
||||
|
||||
Element* ElementFromPoint(float aX, float aY);
|
||||
void ElementsFromPoint(float aX, float aY,
|
||||
nsTArray<RefPtr<mozilla::dom::Element>>& aElements);
|
||||
|
||||
/**
|
||||
* Helper for nsIDOMDocument::elementFromPoint implementation that allows
|
||||
* ignoring the scroll frame and/or avoiding layout flushes.
|
||||
*
|
||||
* @see nsIDOMWindowUtils::elementFromPoint
|
||||
*/
|
||||
Element* ElementFromPointHelper(float aX, float aY,
|
||||
bool aIgnoreRootScrollFrame,
|
||||
bool aFlushLayout);
|
||||
enum ElementsFromPointFlags
|
||||
{
|
||||
IGNORE_ROOT_SCROLL_FRAME = 1,
|
||||
FLUSH_LAYOUT = 2,
|
||||
IS_ELEMENT_FROM_POINT = 4
|
||||
};
|
||||
|
||||
void ElementsFromPointHelper(float aX, float aY, uint32_t aFlags,
|
||||
nsTArray<RefPtr<mozilla::dom::Element>>& aElements);
|
||||
|
||||
protected:
|
||||
nsIContent* Retarget(nsIContent* aContent) const;
|
||||
|
||||
|
|
|
|||
|
|
@ -1126,18 +1126,6 @@ Element::AttachShadow(const ShadowRootInit& aInit, ErrorResult& aError)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
return AttachShadowInternal(aInit.mMode == ShadowRootMode::Closed, aError);
|
||||
}
|
||||
|
||||
already_AddRefed<ShadowRoot>
|
||||
Element::CreateShadowRoot(ErrorResult& aError)
|
||||
{
|
||||
return AttachShadowInternal(false, aError);
|
||||
}
|
||||
|
||||
already_AddRefed<ShadowRoot>
|
||||
Element::AttachShadowInternal(bool aClosed, ErrorResult& aError)
|
||||
{
|
||||
/**
|
||||
* 3. If context object is a shadow host, then throw
|
||||
* an "InvalidStateError" DOMException.
|
||||
|
|
@ -1183,8 +1171,9 @@ Element::AttachShadowInternal(bool aClosed, ErrorResult& aError)
|
|||
* context object???s node document, host is context object,
|
||||
* and mode is init???s mode.
|
||||
*/
|
||||
bool isClosed = (aInit.mMode == ShadowRootMode::Closed);
|
||||
RefPtr<ShadowRoot> shadowRoot =
|
||||
new ShadowRoot(this, aClosed, nodeInfo.forget(), protoBinding);
|
||||
new ShadowRoot(this, isClosed, nodeInfo.forget(), protoBinding);
|
||||
|
||||
shadowRoot->SetIsComposedDocParticipant(IsInComposedDoc());
|
||||
|
||||
|
|
@ -1725,10 +1714,11 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
|||
if (!hadParent) {
|
||||
uint32_t editableDescendantChange = EditableInclusiveDescendantCount(this);
|
||||
if (editableDescendantChange != 0) {
|
||||
// If we are binding a subtree root to the document, we need to update
|
||||
// the editable descendant count of all the ancestors.
|
||||
// If we are binding a subtree root to the document, we need to update
|
||||
// the editable descendant count of all the ancestors. However, we don't
|
||||
// cross the Shadow DOM boundary (expected behavior is unclear).
|
||||
nsIContent* parent = GetParent();
|
||||
while (parent) {
|
||||
while (parent && parent->IsElement()) {
|
||||
parent->ChangeEditableDescendantCount(editableDescendantChange);
|
||||
parent = parent->GetParent();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -470,9 +470,6 @@ protected:
|
|||
mState &= ~aStates;
|
||||
}
|
||||
|
||||
already_AddRefed<ShadowRoot> AttachShadowInternal(bool aClosed,
|
||||
ErrorResult& aError);
|
||||
|
||||
private:
|
||||
// Need to allow the ESM, nsGlobalWindow, and the focus manager to
|
||||
// set our state
|
||||
|
|
@ -930,9 +927,6 @@ public:
|
|||
void SetSlot(const nsAString& aName, ErrorResult& aError);
|
||||
void GetSlot(nsAString& aName);
|
||||
|
||||
// [deprecated] Shadow DOM v0
|
||||
already_AddRefed<ShadowRoot> CreateShadowRoot(ErrorResult& aError);
|
||||
|
||||
ShadowRoot *FastGetShadowRoot() const
|
||||
{
|
||||
nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
|
||||
|
|
|
|||
|
|
@ -801,6 +801,22 @@ FindChromeAccessOnlySubtreeOwner(nsIContent* aContent)
|
|||
return aContent;
|
||||
}
|
||||
|
||||
already_AddRefed<nsINode>
|
||||
FindChromeAccessOnlySubtreeOwner(EventTarget* aTarget)
|
||||
{
|
||||
nsCOMPtr<nsINode> node = do_QueryInterface(aTarget);
|
||||
if (!node || !node->ChromeOnlyAccess()) {
|
||||
return node.forget();
|
||||
}
|
||||
|
||||
if (!node->IsContent()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
node = FindChromeAccessOnlySubtreeOwner(node->AsContent());
|
||||
return node.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
||||
{
|
||||
|
|
@ -821,24 +837,11 @@ nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
|||
// chrome access only subtree or if we are about to propagate out of
|
||||
// a shadow root to a shadow root host.
|
||||
((this == aVisitor.mEvent->mOriginalTarget &&
|
||||
!ChromeOnlyAccess()) || isAnonForEvents || GetShadowRoot())) {
|
||||
!ChromeOnlyAccess()) || isAnonForEvents)) {
|
||||
nsCOMPtr<nsIContent> relatedTarget =
|
||||
do_QueryInterface(aVisitor.mEvent->AsMouseEvent()->mRelatedTarget);
|
||||
if (relatedTarget &&
|
||||
relatedTarget->OwnerDoc() == OwnerDoc()) {
|
||||
|
||||
// In the web components case, we may need to stop propagation of events
|
||||
// at shadow root host.
|
||||
if (GetShadowRoot()) {
|
||||
nsIContent* adjustedTarget =
|
||||
Event::GetShadowRelatedTarget(this, relatedTarget);
|
||||
if (this == adjustedTarget) {
|
||||
aVisitor.SetParentTarget(nullptr, false);
|
||||
aVisitor.mCanHandle = false;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// If current target is anonymous for events or we know that related
|
||||
// target is descendant of an element which is anonymous for events,
|
||||
// we may want to stop event propagation.
|
||||
|
|
@ -954,6 +957,104 @@ nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
|||
} else {
|
||||
aVisitor.SetParentTarget(GetComposedDoc(), false);
|
||||
}
|
||||
|
||||
if (!ChromeOnlyAccess() && !aVisitor.mRelatedTargetRetargetedInCurrentScope) {
|
||||
// We don't support Shadow DOM in native anonymous content yet.
|
||||
aVisitor.mRelatedTargetRetargetedInCurrentScope = true;
|
||||
if (aVisitor.mEvent->mOriginalRelatedTarget) {
|
||||
// https://dom.spec.whatwg.org/#concept-event-dispatch
|
||||
// Step 3.
|
||||
// "Let relatedTarget be the result of retargeting event's relatedTarget
|
||||
// against target if event's relatedTarget is non-null, and null
|
||||
// otherwise."
|
||||
//
|
||||
// This is a bit complicated because the event might be from native
|
||||
// anonymous content, but we need to deal with non-native anonymous
|
||||
// content there.
|
||||
bool initialTarget = this == aVisitor.mEvent->mOriginalTarget;
|
||||
nsCOMPtr<nsINode> originalTargetAsNode;
|
||||
// Use of mOriginalTargetIsInAnon is an optimization here.
|
||||
if (!initialTarget && aVisitor.mOriginalTargetIsInAnon) {
|
||||
originalTargetAsNode =
|
||||
FindChromeAccessOnlySubtreeOwner(aVisitor.mEvent->mOriginalTarget);
|
||||
initialTarget = originalTargetAsNode == this;
|
||||
}
|
||||
if (initialTarget) {
|
||||
nsCOMPtr<nsINode> relatedTargetAsNode =
|
||||
FindChromeAccessOnlySubtreeOwner(aVisitor.mEvent->mOriginalRelatedTarget);
|
||||
if (!originalTargetAsNode) {
|
||||
originalTargetAsNode =
|
||||
do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
|
||||
}
|
||||
|
||||
if (relatedTargetAsNode && originalTargetAsNode) {
|
||||
nsINode* retargetedRelatedTarget =
|
||||
nsContentUtils::Retarget(relatedTargetAsNode, originalTargetAsNode);
|
||||
if (originalTargetAsNode == retargetedRelatedTarget &&
|
||||
retargetedRelatedTarget != relatedTargetAsNode) {
|
||||
// Step 4.
|
||||
// "If target is relatedTarget and target is not event's
|
||||
// relatedTarget, then return true."
|
||||
aVisitor.IgnoreCurrentTargetBecauseOfShadowDOMRetargeting();
|
||||
// Old code relies on mTarget to point to the first element which
|
||||
// was not added to the event target chain because of mCanHandle
|
||||
// being false, but in Shadow DOM case mTarget really should
|
||||
// point to a node in Shadow DOM.
|
||||
aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Part of step 5. Retargeting target has happened already higher
|
||||
// up in this method.
|
||||
// "Append to an event path with event, target, targetOverride,
|
||||
// relatedTarget, and false."
|
||||
aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
|
||||
}
|
||||
} else {
|
||||
nsCOMPtr<nsINode> relatedTargetAsNode =
|
||||
FindChromeAccessOnlySubtreeOwner(aVisitor.mEvent->mOriginalRelatedTarget);
|
||||
if (relatedTargetAsNode) {
|
||||
// Step 11.3.
|
||||
// "Let relatedTarget be the result of retargeting event's
|
||||
// relatedTarget against parent if event's relatedTarget is non-null,
|
||||
// and null otherwise.".
|
||||
nsINode* retargetedRelatedTarget =
|
||||
nsContentUtils::Retarget(relatedTargetAsNode, this);
|
||||
nsCOMPtr<nsINode> targetInKnownToBeHandledScope =
|
||||
FindChromeAccessOnlySubtreeOwner(aVisitor.mTargetInKnownToBeHandledScope);
|
||||
if (nsContentUtils::ContentIsShadowIncludingDescendantOf(
|
||||
this, targetInKnownToBeHandledScope->SubtreeRoot())) {
|
||||
// Part of step 11.4.
|
||||
// "If target's root is a shadow-including inclusive ancestor of
|
||||
// parent, then"
|
||||
// "...Append to an event path with event, parent, null, relatedTarget,
|
||||
// " and slot-in-closed-tree."
|
||||
aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
|
||||
} else if (this == retargetedRelatedTarget) {
|
||||
// Step 11.5
|
||||
// "Otherwise, if parent and relatedTarget are identical, then set
|
||||
// parent to null."
|
||||
aVisitor.IgnoreCurrentTargetBecauseOfShadowDOMRetargeting();
|
||||
// Old code relies on mTarget to point to the first element which
|
||||
// was not added to the event target chain because of mCanHandle
|
||||
// being false, but in Shadow DOM case mTarget really should
|
||||
// point to a node in Shadow DOM.
|
||||
aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope;
|
||||
return NS_OK;
|
||||
} else {
|
||||
// Step 11.6
|
||||
aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (slot) {
|
||||
// Inform that we're about to exit the current scope.
|
||||
aVisitor.mRelatedTargetRetargetedInCurrentScope = false;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -101,6 +101,23 @@ ShadowRoot::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
|||
return mozilla::dom::ShadowRootBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
ShadowRoot::CloneInternalDataFrom(ShadowRoot* aOther)
|
||||
{
|
||||
size_t sheetCount = aOther->SheetCount();
|
||||
for (size_t i = 0; i < sheetCount; ++i) {
|
||||
StyleSheet* sheet = aOther->SheetAt(i);
|
||||
if (sheet && sheet->IsApplicable()) {
|
||||
// TODO: Remove AsGecko() call once Stylo is removed.
|
||||
RefPtr<CSSStyleSheet> clonedSheet =
|
||||
sheet->AsGecko()->Clone(nullptr, nullptr, nullptr, nullptr);
|
||||
if (clonedSheet) {
|
||||
AppendStyleSheet(*clonedSheet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ShadowRoot*
|
||||
ShadowRoot::FromNode(nsINode* aNode)
|
||||
{
|
||||
|
|
@ -311,6 +328,8 @@ ShadowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
|||
{
|
||||
aVisitor.mCanHandle = true;
|
||||
aVisitor.mRootOfClosedTree = IsClosed();
|
||||
// Inform that we're about to exit the current scope.
|
||||
aVisitor.mRelatedTargetRetargetedInCurrentScope = false;
|
||||
|
||||
// https://dom.spec.whatwg.org/#ref-for-get-the-parent%E2%91%A6
|
||||
if (!aVisitor.mEvent->mFlags.mComposed) {
|
||||
|
|
@ -333,12 +352,10 @@ ShadowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
|||
nsIContent* shadowHost = GetHost();
|
||||
aVisitor.SetParentTarget(shadowHost, false);
|
||||
|
||||
if (aVisitor.mOriginalTargetIsInAnon) {
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->mTarget));
|
||||
if (content && content->GetBindingParent() == shadowHost) {
|
||||
aVisitor.mEventTargetAtParent = shadowHost;
|
||||
}
|
||||
}
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->mTarget));
|
||||
if (content && content->GetBindingParent() == shadowHost) {
|
||||
aVisitor.mEventTargetAtParent = shadowHost;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
@ -488,25 +505,6 @@ ShadowRoot::Host()
|
|||
return host->AsElement();
|
||||
}
|
||||
|
||||
bool
|
||||
ShadowRoot::ApplyAuthorStyles()
|
||||
{
|
||||
return mProtoBinding->InheritsStyle();
|
||||
}
|
||||
|
||||
void
|
||||
ShadowRoot::SetApplyAuthorStyles(bool aApplyAuthorStyles)
|
||||
{
|
||||
mProtoBinding->SetInheritsStyle(aApplyAuthorStyles);
|
||||
|
||||
nsIPresShell* shell = OwnerDoc()->GetShell();
|
||||
if (shell) {
|
||||
OwnerDoc()->BeginUpdate(UPDATE_STYLE);
|
||||
shell->RecordShadowStyleChange(this);
|
||||
OwnerDoc()->EndUpdate(UPDATE_STYLE);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ShadowRoot::AttributeChanged(nsIDocument* aDocument,
|
||||
Element* aElement,
|
||||
|
|
|
|||
|
|
@ -59,13 +59,16 @@ public:
|
|||
// [deprecated] Shadow DOM v0
|
||||
void InsertSheet(StyleSheet* aSheet, nsIContent* aLinkingContent);
|
||||
void RemoveSheet(StyleSheet* aSheet);
|
||||
bool ApplyAuthorStyles();
|
||||
void SetApplyAuthorStyles(bool aApplyAuthorStyles);
|
||||
StyleSheetList* StyleSheets()
|
||||
{
|
||||
return &DocumentOrShadowRoot::EnsureDOMStyleSheets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones internal state, for example stylesheets, of aOther to 'this'.
|
||||
*/
|
||||
void CloneInternalDataFrom(ShadowRoot* aOther);
|
||||
|
||||
/**
|
||||
* Distributes all the explicit children of the pool host to the content
|
||||
* insertion points in this ShadowRoot.
|
||||
|
|
|
|||
|
|
@ -5,8 +5,7 @@
|
|||
<body>
|
||||
<div id="host"></div>
|
||||
<script>
|
||||
var s = host.createShadowRoot();
|
||||
s.innerHTML = '<input type="range" />';
|
||||
host.attachShadow({ mode: "open" }).innerHTML = '<input type="range" />';
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<body>
|
||||
<script>
|
||||
var x = document.createElement('span');
|
||||
x.createShadowRoot();
|
||||
x.attachShadow({ mode: "open" });
|
||||
x.id = 'a';
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<style>
|
||||
#foo {
|
||||
overflow: scroll;
|
||||
height: 100px;
|
||||
}
|
||||
</style>
|
||||
<div id="foo">
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
<script>
|
||||
foo.createShadowRoot().innerHTML = "<content></content>";
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
17
dom/base/crashtests/1393806.html
Normal file
17
dom/base/crashtests/1393806.html
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<html>
|
||||
<head>
|
||||
<script>
|
||||
function fun_0() {
|
||||
document.implementation.createDocument('', '', null).adoptNode(o2);
|
||||
}
|
||||
|
||||
o1 = document.createElement('map');
|
||||
o2 = document.createElement('iframe');
|
||||
document.documentElement.appendChild(o1);
|
||||
document.documentElement.appendChild(o2);
|
||||
o1.textContent = 'x';
|
||||
document.addEventListener('DOMNodeRemoved', fun_0, false);
|
||||
o1.innerText = 'x';
|
||||
</script>
|
||||
</head>
|
||||
</html>
|
||||
|
|
@ -209,6 +209,7 @@ load 1230422.html
|
|||
load 1251361.html
|
||||
load 1304437.html
|
||||
pref(clipboard.autocopy,true) load 1385272-1.html
|
||||
load 1393806.html
|
||||
pref(dom.webcomponents.enabled,true) load 1341693.html
|
||||
pref(dom.webcomponents.enabled,true) load 1419799.html
|
||||
pref(dom.webcomponents.enabled,false) load 1422931.html
|
||||
|
|
|
|||
|
|
@ -2248,6 +2248,34 @@ nsContentUtils::ContentIsHostIncludingDescendantOf(
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
nsContentUtils::ContentIsShadowIncludingDescendantOf(
|
||||
const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor)
|
||||
{
|
||||
MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
|
||||
MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
|
||||
|
||||
if (aPossibleAncestor == aPossibleDescendant->GetComposedDoc()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
do {
|
||||
if (aPossibleDescendant == aPossibleAncestor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (aPossibleDescendant->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
|
||||
ShadowRoot* shadowRoot =
|
||||
ShadowRoot::FromNode(const_cast<nsINode*>(aPossibleDescendant));
|
||||
aPossibleDescendant = shadowRoot ? shadowRoot->GetHost() : nullptr;
|
||||
} else {
|
||||
aPossibleDescendant = aPossibleDescendant->GetParentNode();
|
||||
}
|
||||
} while (aPossibleDescendant);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
|
||||
|
|
@ -2266,6 +2294,30 @@ nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
|
|||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
nsINode*
|
||||
nsContentUtils::Retarget(nsINode* aTargetA, nsINode* aTargetB)
|
||||
{
|
||||
while (true && aTargetA) {
|
||||
// If A's root is not a shadow root...
|
||||
nsINode* root = aTargetA->SubtreeRoot();
|
||||
// TODO: replace with IsShadowRoot() check once bug 1427511 lands.
|
||||
if (!ShadowRoot::FromNode(root)) {
|
||||
// ...then return A.
|
||||
return aTargetA;
|
||||
}
|
||||
|
||||
// or A's root is a shadow-including inclusive ancestor of B...
|
||||
if (nsContentUtils::ContentIsShadowIncludingDescendantOf(aTargetB, root)) {
|
||||
// ...then return A.
|
||||
return aTargetA;
|
||||
}
|
||||
|
||||
aTargetA = ShadowRoot::FromNode(root)->GetHost();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
|
|
@ -6384,9 +6436,7 @@ nsContentUtils::IsFocusedContent(const nsIContent* aContent)
|
|||
bool
|
||||
nsContentUtils::IsSubDocumentTabbable(nsIContent* aContent)
|
||||
{
|
||||
//XXXsmaug Shadow DOM spec issue!
|
||||
// We may need to change this to GetComposedDoc().
|
||||
nsIDocument* doc = aContent->GetUncomposedDoc();
|
||||
nsIDocument* doc = aContent->GetComposedDoc();
|
||||
if (!doc) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,6 +279,13 @@ public:
|
|||
static bool ContentIsHostIncludingDescendantOf(
|
||||
const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor);
|
||||
|
||||
/**
|
||||
* Similar to above, but does special case only ShadowRoot,
|
||||
* not HTMLTemplateElement.
|
||||
*/
|
||||
static bool ContentIsShadowIncludingDescendantOf(
|
||||
const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor);
|
||||
|
||||
/**
|
||||
* Similar to ContentIsDescendantOf except it crosses document boundaries,
|
||||
* this function uses ancestor/descendant relations in the composed document
|
||||
|
|
@ -287,6 +294,13 @@ public:
|
|||
static bool ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
|
||||
nsINode* aPossibleAncestor);
|
||||
|
||||
/**
|
||||
* Retarget an object A against an object B
|
||||
* @see https://dom.spec.whatwg.org/#retarget
|
||||
*/
|
||||
static nsINode*
|
||||
Retarget(nsINode* aTargetA, nsINode* aTargetB);
|
||||
|
||||
/*
|
||||
* This method fills the |aArray| with all ancestor nodes of |aNode|
|
||||
* including |aNode| at the zero index.
|
||||
|
|
|
|||
|
|
@ -3120,106 +3120,6 @@ nsDocument::ElementFromPoint(float aX, float aY, nsIDOMElement** aReturn)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
Element*
|
||||
nsIDocument::ElementFromPoint(float aX, float aY)
|
||||
{
|
||||
return ElementFromPointHelper(aX, aY, false, true);
|
||||
}
|
||||
|
||||
void
|
||||
nsIDocument::ElementsFromPoint(float aX, float aY,
|
||||
nsTArray<RefPtr<Element>>& aElements)
|
||||
{
|
||||
ElementsFromPointHelper(aX, aY, nsIDocument::FLUSH_LAYOUT, aElements);
|
||||
}
|
||||
|
||||
Element*
|
||||
nsDocument::ElementFromPointHelper(float aX, float aY,
|
||||
bool aIgnoreRootScrollFrame,
|
||||
bool aFlushLayout)
|
||||
{
|
||||
AutoTArray<RefPtr<Element>, 1> elementArray;
|
||||
ElementsFromPointHelper(aX, aY,
|
||||
((aIgnoreRootScrollFrame ? nsIDocument::IGNORE_ROOT_SCROLL_FRAME : 0) |
|
||||
(aFlushLayout ? nsIDocument::FLUSH_LAYOUT : 0) |
|
||||
nsIDocument::IS_ELEMENT_FROM_POINT),
|
||||
elementArray);
|
||||
if (elementArray.IsEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
return elementArray[0];
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::ElementsFromPointHelper(float aX, float aY,
|
||||
uint32_t aFlags,
|
||||
nsTArray<RefPtr<mozilla::dom::Element>>& aElements)
|
||||
{
|
||||
// As per the the spec, we return null if either coord is negative
|
||||
if (!(aFlags & nsIDocument::IGNORE_ROOT_SCROLL_FRAME) && (aX < 0 || aY < 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
|
||||
nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
|
||||
nsPoint pt(x, y);
|
||||
|
||||
// Make sure the layout information we get is up-to-date, and
|
||||
// ensure we get a root frame (for everything but XUL)
|
||||
if (aFlags & nsIDocument::FLUSH_LAYOUT) {
|
||||
FlushPendingNotifications(Flush_Layout);
|
||||
}
|
||||
|
||||
nsIPresShell *ps = GetShell();
|
||||
if (!ps) {
|
||||
return;
|
||||
}
|
||||
nsIFrame *rootFrame = ps->GetRootFrame();
|
||||
|
||||
// XUL docs, unlike HTML, have no frame tree until everything's done loading
|
||||
if (!rootFrame) {
|
||||
return; // return null to premature XUL callers as a reminder to wait
|
||||
}
|
||||
|
||||
nsTArray<nsIFrame*> outFrames;
|
||||
// Emulate what GetFrameAtPoint does, since we want all the frames under our
|
||||
// point.
|
||||
nsLayoutUtils::GetFramesForArea(rootFrame, nsRect(pt, nsSize(1, 1)), outFrames,
|
||||
nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC |
|
||||
((aFlags & nsIDocument::IGNORE_ROOT_SCROLL_FRAME) ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0));
|
||||
|
||||
// Dunno when this would ever happen, as we should at least have a root frame under us?
|
||||
if (outFrames.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Used to filter out repeated elements in sequence.
|
||||
nsIContent* lastAdded = nullptr;
|
||||
|
||||
for (uint32_t i = 0; i < outFrames.Length(); i++) {
|
||||
nsIContent* node = GetContentInThisDocument(outFrames[i]);
|
||||
|
||||
if (!node || !node->IsElement()) {
|
||||
// If this helper is called via ElementsFromPoint, we need to make sure
|
||||
// our frame is an element. Otherwise return whatever the top frame is
|
||||
// even if it isn't the top-painted element.
|
||||
if (!(aFlags & nsIDocument::IS_ELEMENT_FROM_POINT)) {
|
||||
continue;
|
||||
}
|
||||
node = node->GetParent();
|
||||
}
|
||||
if (node && node != lastAdded) {
|
||||
aElements.AppendElement(node->AsElement());
|
||||
lastAdded = node;
|
||||
// If this helper is called via ElementFromPoint, just return the first
|
||||
// element we find.
|
||||
if (aFlags & nsIDocument::IS_ELEMENT_FROM_POINT) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDocument::NodesFromRectHelper(float aX, float aY,
|
||||
float aTopSize, float aRightSize,
|
||||
|
|
@ -5923,14 +5823,9 @@ nsIDocument::ImportNode(nsINode& aNode, bool aDeep, ErrorResult& rv) const
|
|||
case nsIDOMNode::COMMENT_NODE:
|
||||
case nsIDOMNode::DOCUMENT_TYPE_NODE:
|
||||
{
|
||||
nsCOMPtr<nsINode> newNode;
|
||||
nsCOMArray<nsINode> nodesWithProperties;
|
||||
rv = nsNodeUtils::Clone(imported, aDeep, mNodeInfoManager,
|
||||
nodesWithProperties, getter_AddRefs(newNode));
|
||||
if (rv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
return newNode.forget();
|
||||
return nsNodeUtils::Clone(imported, aDeep, mNodeInfoManager,
|
||||
&nodesWithProperties, rv);
|
||||
}
|
||||
default:
|
||||
{
|
||||
|
|
@ -7096,8 +6991,8 @@ nsIDocument::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv)
|
|||
}
|
||||
|
||||
nsCOMArray<nsINode> nodesWithProperties;
|
||||
rv = nsNodeUtils::Adopt(adoptedNode, sameDocument ? nullptr : mNodeInfoManager,
|
||||
newScope, nodesWithProperties);
|
||||
nsNodeUtils::Adopt(adoptedNode, sameDocument ? nullptr : mNodeInfoManager,
|
||||
newScope, nodesWithProperties, rv);
|
||||
if (rv.Failed()) {
|
||||
// Disconnect all nodes from their parents, since some have the old document
|
||||
// as their ownerDocument and some have this as their ownerDocument.
|
||||
|
|
@ -8392,7 +8287,7 @@ nsDocument::DoUnblockOnload()
|
|||
}
|
||||
|
||||
nsIContent*
|
||||
nsDocument::GetContentInThisDocument(nsIFrame* aFrame) const
|
||||
nsIDocument::GetContentInThisDocument(nsIFrame* aFrame) const
|
||||
{
|
||||
for (nsIFrame* f = aFrame; f;
|
||||
f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
|
||||
|
|
@ -8603,7 +8498,7 @@ nsDocument::OnPageHide(bool aPersisted,
|
|||
EnumerateActivityObservers(NotifyActivityChanged, nullptr);
|
||||
|
||||
ClearPendingFullscreenRequests(this);
|
||||
if (GetFullscreenElement()) {
|
||||
if (FullScreenStackTop()) {
|
||||
// If this document was fullscreen, we should exit fullscreen in this
|
||||
// doctree branch. This ensures that if the user navigates while in
|
||||
// fullscreen mode we don't leave its still visible ancestor documents
|
||||
|
|
@ -10307,7 +10202,7 @@ nsIDocument::AsyncExitFullscreen(nsIDocument* aDoc)
|
|||
static bool
|
||||
CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData)
|
||||
{
|
||||
if (aDoc->GetFullscreenElement()) {
|
||||
if (aDoc->FullScreenStackTop()) {
|
||||
uint32_t* count = static_cast<uint32_t*>(aData);
|
||||
(*count)++;
|
||||
}
|
||||
|
|
@ -10327,7 +10222,7 @@ nsDocument::IsFullscreenLeaf()
|
|||
{
|
||||
// A fullscreen leaf document is fullscreen, and has no fullscreen
|
||||
// subdocuments.
|
||||
if (!GetFullscreenElement()) {
|
||||
if (!FullScreenStackTop()) {
|
||||
return false;
|
||||
}
|
||||
return CountFullscreenSubDocuments(this) == 0;
|
||||
|
|
@ -10336,11 +10231,11 @@ nsDocument::IsFullscreenLeaf()
|
|||
static bool
|
||||
ResetFullScreen(nsIDocument* aDocument, void* aData)
|
||||
{
|
||||
if (aDocument->GetFullscreenElement()) {
|
||||
if (aDocument->FullScreenStackTop()) {
|
||||
NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1,
|
||||
"Should have at most 1 fullscreen subdocument.");
|
||||
static_cast<nsDocument*>(aDocument)->CleanupFullscreenState();
|
||||
NS_ASSERTION(!aDocument->GetFullscreenElement(),
|
||||
NS_ASSERTION(!aDocument->FullScreenStackTop(),
|
||||
"Should reset full-screen");
|
||||
auto changed = reinterpret_cast<nsCOMArray<nsIDocument>*>(aData);
|
||||
changed->AppendElement(aDocument);
|
||||
|
|
@ -10388,7 +10283,7 @@ nsIDocument::ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc)
|
|||
UnlockPointer();
|
||||
|
||||
nsCOMPtr<nsIDocument> root = aMaybeNotARootDoc->GetFullscreenRoot();
|
||||
if (!root || !root->GetFullscreenElement()) {
|
||||
if (!root || !root->FullScreenStackTop()) {
|
||||
// If a document was detached before exiting from fullscreen, it is
|
||||
// possible that the root had left fullscreen state. In this case,
|
||||
// we would not get anything from the ResetFullScreen() call. Root's
|
||||
|
|
@ -10417,7 +10312,7 @@ nsIDocument::ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc)
|
|||
DispatchFullScreenChange(changed[changed.Length() - i - 1]);
|
||||
}
|
||||
|
||||
NS_ASSERTION(!root->GetFullscreenElement(),
|
||||
NS_ASSERTION(!root->FullScreenStackTop(),
|
||||
"Fullscreen root should no longer be a fullscreen doc...");
|
||||
|
||||
// Move the top-level window out of fullscreen mode.
|
||||
|
|
@ -10434,7 +10329,7 @@ GetFullscreenLeaf(nsIDocument* aDoc, void* aData)
|
|||
nsIDocument** result = static_cast<nsIDocument**>(aData);
|
||||
*result = aDoc;
|
||||
return false;
|
||||
} else if (aDoc->GetFullscreenElement()) {
|
||||
} else if (aDoc->FullScreenStackTop()) {
|
||||
aDoc->EnumerateSubDocuments(GetFullscreenLeaf, aData);
|
||||
}
|
||||
return true;
|
||||
|
|
@ -10453,7 +10348,7 @@ GetFullscreenLeaf(nsIDocument* aDoc)
|
|||
nsIDocument* root = nsContentUtils::GetRootDocument(aDoc);
|
||||
// Check that the root is actually fullscreen so we don't waste time walking
|
||||
// around its descendants.
|
||||
if (!root->GetFullscreenElement()) {
|
||||
if (!root->FullScreenStackTop()) {
|
||||
return nullptr;
|
||||
}
|
||||
GetFullscreenLeaf(root, &leaf);
|
||||
|
|
@ -10463,10 +10358,10 @@ GetFullscreenLeaf(nsIDocument* aDoc)
|
|||
void
|
||||
nsDocument::RestorePreviousFullScreenState()
|
||||
{
|
||||
NS_ASSERTION(!GetFullscreenElement() || !FullscreenRoots::IsEmpty(),
|
||||
NS_ASSERTION(!FullScreenStackTop() || !FullscreenRoots::IsEmpty(),
|
||||
"Should have at least 1 fullscreen root when fullscreen!");
|
||||
|
||||
if (!GetFullscreenElement() || !GetWindow() || FullscreenRoots::IsEmpty()) {
|
||||
if (!FullScreenStackTop() || !GetWindow() || FullscreenRoots::IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -10641,7 +10536,7 @@ nsDocument::FullScreenStackPush(Element* aElement)
|
|||
}
|
||||
EventStateManager::SetFullScreenState(aElement, true);
|
||||
mFullScreenStack.AppendElement(do_GetWeakReference(aElement));
|
||||
NS_ASSERTION(GetFullscreenElement() == aElement, "Should match");
|
||||
NS_ASSERTION(FullScreenStackTop() == aElement, "Should match");
|
||||
UpdateViewportScrollbarOverrideForFullscreen(this);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -10689,7 +10584,7 @@ nsDocument::FullScreenStackTop()
|
|||
uint32_t last = mFullScreenStack.Length() - 1;
|
||||
nsCOMPtr<Element> element(do_QueryReferent(mFullScreenStack[last]));
|
||||
NS_ASSERTION(element, "Should have full-screen element!");
|
||||
NS_ASSERTION(element->IsInUncomposedDoc(), "Full-screen element should be in doc");
|
||||
NS_ASSERTION(element->IsInComposedDoc(), "Full-screen element should be in doc");
|
||||
NS_ASSERTION(element->OwnerDoc() == this, "Full-screen element should be in this doc");
|
||||
return element;
|
||||
}
|
||||
|
|
@ -10767,7 +10662,7 @@ nsresult nsDocument::RemoteFrameFullscreenReverted()
|
|||
}
|
||||
|
||||
/* static */ bool
|
||||
nsDocument::IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject)
|
||||
nsIDocument::IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return nsContentUtils::IsCallerChrome() ||
|
||||
|
|
@ -10815,10 +10710,10 @@ nsDocument::FullscreenElementReadyCheck(Element* aElement,
|
|||
{
|
||||
NS_ASSERTION(aElement,
|
||||
"Must pass non-null element to nsDocument::RequestFullScreen");
|
||||
if (!aElement || aElement == GetFullscreenElement()) {
|
||||
if (!aElement || aElement == FullScreenStackTop()) {
|
||||
return false;
|
||||
}
|
||||
if (!aElement->IsInUncomposedDoc()) {
|
||||
if (!aElement->IsInComposedDoc()) {
|
||||
DispatchFullscreenError("FullscreenDeniedNotInDocument");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -10842,8 +10737,11 @@ nsDocument::FullscreenElementReadyCheck(Element* aElement,
|
|||
DispatchFullscreenError("FullscreenDeniedSubDocFullScreen");
|
||||
return false;
|
||||
}
|
||||
if (GetFullscreenElement() &&
|
||||
!nsContentUtils::ContentIsDescendantOf(aElement, GetFullscreenElement())) {
|
||||
//XXXsmaug Note, we don't follow the latest fullscreen spec here.
|
||||
// This whole check could be probably removed.
|
||||
if (FullScreenStackTop() &&
|
||||
!nsContentUtils::ContentIsHostIncludingDescendantOf(aElement,
|
||||
FullScreenStackTop())) {
|
||||
// If this document is full-screen, only grant full-screen requests from
|
||||
// a descendant of the current full-screen element.
|
||||
DispatchFullscreenError("FullscreenDeniedNotDescendant");
|
||||
|
|
@ -11203,16 +11101,6 @@ nsDocument::GetMozFullScreenElement(nsIDOMElement **aFullScreenElement)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
Element*
|
||||
nsDocument::GetFullscreenElement()
|
||||
{
|
||||
Element* element = FullScreenStackTop();
|
||||
NS_ASSERTION(!element ||
|
||||
element->State().HasState(NS_EVENT_STATE_FULL_SCREEN),
|
||||
"Fullscreen element should have fullscreen styles applied");
|
||||
return element;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocument::GetMozFullScreen(bool *aFullScreen)
|
||||
{
|
||||
|
|
@ -11331,7 +11219,7 @@ GetPointerLockError(Element* aElement, Element* aCurrentLock,
|
|||
return "PointerLockDeniedInUse";
|
||||
}
|
||||
|
||||
if (!aElement->IsInUncomposedDoc()) {
|
||||
if (!aElement->IsInComposedDoc()) {
|
||||
return "PointerLockDeniedNotInDocument";
|
||||
}
|
||||
|
||||
|
|
@ -11382,11 +11270,11 @@ ChangePointerLockedElement(Element* aElement, nsIDocument* aDocument,
|
|||
MOZ_ASSERT(aDocument);
|
||||
MOZ_ASSERT(aElement != aPointerLockedElement);
|
||||
if (aPointerLockedElement) {
|
||||
MOZ_ASSERT(aPointerLockedElement->GetUncomposedDoc() == aDocument);
|
||||
MOZ_ASSERT(aPointerLockedElement->GetComposedDoc() == aDocument);
|
||||
aPointerLockedElement->ClearPointerLock();
|
||||
}
|
||||
if (aElement) {
|
||||
MOZ_ASSERT(aElement->GetUncomposedDoc() == aDocument);
|
||||
MOZ_ASSERT(aElement->GetComposedDoc() == aDocument);
|
||||
aElement->SetPointerLock();
|
||||
EventStateManager::sPointerLockedElement = do_GetWeakReference(aElement);
|
||||
EventStateManager::sPointerLockedDoc = do_GetWeakReference(aDocument);
|
||||
|
|
@ -11410,9 +11298,9 @@ PointerLockRequest::Run()
|
|||
nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
|
||||
nsDocument* d = static_cast<nsDocument*>(doc.get());
|
||||
const char* error = nullptr;
|
||||
if (!e || !d || !e->GetUncomposedDoc()) {
|
||||
if (!e || !d || !e->GetComposedDoc()) {
|
||||
error = "PointerLockDeniedNotInDocument";
|
||||
} else if (e->GetUncomposedDoc() != d) {
|
||||
} else if (e->GetComposedDoc() != d) {
|
||||
error = "PointerLockDeniedMovedDocument";
|
||||
}
|
||||
if (!error) {
|
||||
|
|
@ -11578,25 +11466,6 @@ nsDocument::GetMozPointerLockElement(nsIDOMElement** aPointerLockedElement)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
Element*
|
||||
nsIDocument::GetPointerLockElement()
|
||||
{
|
||||
nsCOMPtr<Element> pointerLockedElement =
|
||||
do_QueryReferent(EventStateManager::sPointerLockedElement);
|
||||
if (!pointerLockedElement) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Make sure pointer locked element is in the same document.
|
||||
nsCOMPtr<nsIDocument> pointerLockedDoc =
|
||||
do_QueryReferent(EventStateManager::sPointerLockedDoc);
|
||||
if (pointerLockedDoc != this) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return pointerLockedElement;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDocument::Observe(nsISupports *aSubject,
|
||||
const char *aTopic,
|
||||
|
|
|
|||
|
|
@ -692,14 +692,6 @@ public:
|
|||
nsIAtom* aAttrName,
|
||||
const nsAString& aAttrValue) const override;
|
||||
|
||||
virtual Element* ElementFromPointHelper(float aX, float aY,
|
||||
bool aIgnoreRootScrollFrame,
|
||||
bool aFlushLayout) override;
|
||||
|
||||
virtual void ElementsFromPointHelper(float aX, float aY,
|
||||
uint32_t aFlags,
|
||||
nsTArray<RefPtr<mozilla::dom::Element>>& aElements) override;
|
||||
|
||||
virtual nsresult NodesFromRectHelper(float aX, float aY,
|
||||
float aTopSize, float aRightSize,
|
||||
float aBottomSize, float aLeftSize,
|
||||
|
|
@ -896,8 +888,6 @@ public:
|
|||
//
|
||||
already_AddRefed<nsSimpleContentList> BlockedTrackingNodes() const;
|
||||
|
||||
static bool IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject);
|
||||
|
||||
// Do the "fullscreen element ready check" from the fullscreen spec.
|
||||
// It returns true if the given element is allowed to go into fullscreen.
|
||||
bool FullscreenElementReadyCheck(Element* aElement, bool aWasCallerChrome);
|
||||
|
|
@ -921,11 +911,10 @@ public:
|
|||
void FullScreenStackPop();
|
||||
|
||||
// Returns the top element from the full-screen stack.
|
||||
Element* FullScreenStackTop();
|
||||
Element* FullScreenStackTop() override;
|
||||
|
||||
// DOM-exposed fullscreen API
|
||||
bool FullscreenEnabled() override;
|
||||
Element* GetFullscreenElement() override;
|
||||
|
||||
void RequestPointerLock(Element* aElement) override;
|
||||
bool SetPointerLock(Element* aElement, int aCursorStyle);
|
||||
|
|
@ -1315,16 +1304,6 @@ private:
|
|||
|
||||
nsresult InitCSP(nsIChannel* aChannel);
|
||||
|
||||
/**
|
||||
* Find the (non-anonymous) content in this document for aFrame. It will
|
||||
* be aFrame's content node if that content is in this document and not
|
||||
* anonymous. Otherwise, when aFrame is in a subdocument, we use the frame
|
||||
* element containing the subdocument containing aFrame, and/or find the
|
||||
* nearest non-anonymous ancestor in this document.
|
||||
* Returns null if there is no such element.
|
||||
*/
|
||||
nsIContent* GetContentInThisDocument(nsIFrame* aFrame) const;
|
||||
|
||||
// Just like EnableStyleSheetsForSet, but doesn't check whether
|
||||
// aSheetSet is null and allows the caller to control whether to set
|
||||
// aSheetSet as the preferred set in the CSSLoader.
|
||||
|
|
|
|||
|
|
@ -8,10 +8,12 @@
|
|||
#include "nsFocusManager.h"
|
||||
|
||||
#include "AccessibleCaretEventHub.h"
|
||||
#include "ChildIterator.h"
|
||||
#include "nsAttrValueInlines.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsDocument.h"
|
||||
#include "nsIEditor.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsIDOMChromeWindow.h"
|
||||
|
|
@ -46,7 +48,9 @@
|
|||
|
||||
#include "mozilla/ContentEvents.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/ShadowRoot.h"
|
||||
#include "mozilla/dom/HTMLInputElement.h"
|
||||
#include "mozilla/dom/HTMLSlotElement.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/EventStateManager.h"
|
||||
#include "mozilla/EventStates.h"
|
||||
|
|
@ -2978,6 +2982,378 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindowOuter* aWindow,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsHostOrSlot(nsIContent* aContent)
|
||||
{
|
||||
return aContent && (aContent->GetShadowRoot() ||
|
||||
aContent->IsHTMLElement(nsGkAtoms::slot));
|
||||
}
|
||||
|
||||
// Helper class to iterate contents in scope by traversing flattened tree
|
||||
// in tree order
|
||||
class MOZ_STACK_CLASS ScopedContentTraversal
|
||||
{
|
||||
public:
|
||||
ScopedContentTraversal(nsIContent* aStartContent, nsIContent* aOwner)
|
||||
: mCurrent(aStartContent)
|
||||
, mOwner(aOwner)
|
||||
{
|
||||
MOZ_ASSERT(aStartContent);
|
||||
}
|
||||
|
||||
void Next();
|
||||
void Prev();
|
||||
|
||||
void Reset()
|
||||
{
|
||||
SetCurrent(mOwner);
|
||||
}
|
||||
|
||||
nsIContent* GetCurrent()
|
||||
{
|
||||
return mCurrent;
|
||||
}
|
||||
|
||||
private:
|
||||
void SetCurrent(nsIContent* aContent)
|
||||
{
|
||||
mCurrent = aContent;
|
||||
}
|
||||
|
||||
nsIContent* mCurrent;
|
||||
nsIContent* mOwner;
|
||||
};
|
||||
|
||||
void
|
||||
ScopedContentTraversal::Next()
|
||||
{
|
||||
MOZ_ASSERT(mCurrent);
|
||||
|
||||
// Get mCurrent's first child if it's in the same scope.
|
||||
if (!IsHostOrSlot(mCurrent) || mCurrent == mOwner) {
|
||||
StyleChildrenIterator iter(mCurrent);
|
||||
nsIContent* child = iter.GetNextChild();
|
||||
if (child) {
|
||||
SetCurrent(child);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If mOwner has no children, END traversal
|
||||
if (mCurrent == mOwner) {
|
||||
SetCurrent(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
nsIContent* current = mCurrent;
|
||||
while (1) {
|
||||
// Create parent's iterator and move to current
|
||||
nsIContent* parent = current->GetFlattenedTreeParent();
|
||||
StyleChildrenIterator parentIter(parent);
|
||||
parentIter.Seek(current);
|
||||
|
||||
// Get next sibling of current
|
||||
if (nsIContent* next = parentIter.GetNextChild()) {
|
||||
SetCurrent(next);
|
||||
return;
|
||||
}
|
||||
|
||||
// If no next sibling and parent is mOwner, END traversal
|
||||
if (parent == mOwner) {
|
||||
SetCurrent(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
current = parent;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ScopedContentTraversal::Prev()
|
||||
{
|
||||
MOZ_ASSERT(mCurrent);
|
||||
|
||||
nsIContent* parent;
|
||||
nsIContent* last;
|
||||
if (mCurrent == mOwner) {
|
||||
// Get last child of mOwner
|
||||
StyleChildrenIterator ownerIter(mOwner, false /* aStartAtBeginning */);
|
||||
last = ownerIter.GetPreviousChild();
|
||||
|
||||
parent = last;
|
||||
} else {
|
||||
// Create parent's iterator and move to mCurrent
|
||||
parent = mCurrent->GetFlattenedTreeParent();
|
||||
StyleChildrenIterator parentIter(parent);
|
||||
parentIter.Seek(mCurrent);
|
||||
|
||||
// Get previous sibling
|
||||
last = parentIter.GetPreviousChild();
|
||||
}
|
||||
|
||||
while (last) {
|
||||
parent = last;
|
||||
if (parent->GetShadowRoot() ||
|
||||
parent->IsHTMLElement(nsGkAtoms::slot)) {
|
||||
// Skip contents in other scopes
|
||||
break;
|
||||
}
|
||||
|
||||
// Find last child
|
||||
StyleChildrenIterator iter(parent, false /* aStartAtBeginning */);
|
||||
last = iter.GetPreviousChild();
|
||||
}
|
||||
|
||||
// If parent is mOwner and no previous sibling remains, END traversal
|
||||
SetCurrent(parent == mOwner ? nullptr : parent);
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsFocusManager::FindOwner(nsIContent* aContent)
|
||||
{
|
||||
nsIContent* currentContent = aContent;
|
||||
while (currentContent) {
|
||||
nsIContent* parent = currentContent->GetFlattenedTreeParent();
|
||||
|
||||
// Shadow host / Slot
|
||||
if (IsHostOrSlot(parent)) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
currentContent = parent;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Host and Slot elements need to be handled as if they had tabindex 0 even
|
||||
* when they don't have the attribute. This is a helper method to get the
|
||||
* right value for focus navigation. If aIsFocusable is passed, it is set to
|
||||
* true if the element itself is focusable.
|
||||
*/
|
||||
static int32_t
|
||||
HostOrSlotTabIndexValue(nsIContent* aContent,
|
||||
bool* aIsFocusable = nullptr)
|
||||
{
|
||||
MOZ_ASSERT(IsHostOrSlot(aContent));
|
||||
|
||||
if (aIsFocusable) {
|
||||
*aIsFocusable = false;
|
||||
nsIFrame* frame = aContent->GetPrimaryFrame();
|
||||
if (frame) {
|
||||
int32_t tabIndex;
|
||||
frame->IsFocusable(&tabIndex, 0);
|
||||
*aIsFocusable = tabIndex >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
const nsAttrValue* attrVal =
|
||||
aContent->AsElement()->GetParsedAttr(nsGkAtoms::tabindex);
|
||||
if (!attrVal) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (attrVal->Type() == nsAttrValue::eInteger) {
|
||||
return attrVal->GetIntegerValue();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsFocusManager::GetNextTabbableContentInScope(nsIContent* aOwner,
|
||||
nsIContent* aStartContent,
|
||||
nsIContent* aOriginalStartContent,
|
||||
bool aForward,
|
||||
int32_t aCurrentTabIndex,
|
||||
bool aIgnoreTabIndex,
|
||||
bool aForDocumentNavigation,
|
||||
bool aSkipOwner)
|
||||
{
|
||||
MOZ_ASSERT(IsHostOrSlot(aOwner), "Scope owner should be host or slot");
|
||||
|
||||
if (!aSkipOwner && (aForward && aOwner == aStartContent)) {
|
||||
int32_t tabIndex = -1;
|
||||
nsIFrame* frame = aOwner->GetPrimaryFrame();
|
||||
if (frame && frame->IsFocusable(&tabIndex, false) && tabIndex >= 0) {
|
||||
return aOwner;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Iterate contents in scope
|
||||
//
|
||||
ScopedContentTraversal contentTraversal(aStartContent, aOwner);
|
||||
nsCOMPtr<nsIContent> iterContent;
|
||||
nsIContent* firstNonChromeOnly = aStartContent->IsInNativeAnonymousSubtree() ?
|
||||
aStartContent->FindFirstNonChromeOnlyAccessContent() : nullptr;
|
||||
while (1) {
|
||||
// Iterate tab index to find corresponding contents in scope
|
||||
|
||||
while (1) {
|
||||
// Iterate remaining contents in scope to find next content to focus
|
||||
|
||||
// Get next content
|
||||
aForward ? contentTraversal.Next() : contentTraversal.Prev();
|
||||
iterContent = contentTraversal.GetCurrent();
|
||||
|
||||
if (firstNonChromeOnly && firstNonChromeOnly == iterContent) {
|
||||
// We just broke out from the native anonymous content, so move
|
||||
// to the previous/next node of the native anonymous owner.
|
||||
if (aForward) {
|
||||
contentTraversal.Next();
|
||||
} else {
|
||||
contentTraversal.Prev();
|
||||
}
|
||||
iterContent = contentTraversal.GetCurrent();
|
||||
}
|
||||
|
||||
if (!iterContent) {
|
||||
// Reach the end
|
||||
break;
|
||||
}
|
||||
|
||||
// Get the tab index of the next element. For NAC we rely on frames.
|
||||
//XXXsmaug we should probably use frames also for Shadow DOM and special
|
||||
// case only display:contents elements.
|
||||
int32_t tabIndex = 0;
|
||||
if (iterContent->IsInNativeAnonymousSubtree() &&
|
||||
iterContent->GetPrimaryFrame()) {
|
||||
iterContent->GetPrimaryFrame()->IsFocusable(&tabIndex);
|
||||
} else if (IsHostOrSlot(iterContent)) {
|
||||
tabIndex = HostOrSlotTabIndexValue(iterContent);
|
||||
} else {
|
||||
iterContent->IsFocusable(&tabIndex);
|
||||
}
|
||||
if (tabIndex < 0 || !(aIgnoreTabIndex || tabIndex == aCurrentTabIndex)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IsHostOrSlot(iterContent)) {
|
||||
nsCOMPtr<nsIContent> elementInFrame;
|
||||
bool checkSubDocument = true;
|
||||
if (aForDocumentNavigation &&
|
||||
TryDocumentNavigation(iterContent, &checkSubDocument,
|
||||
getter_AddRefs(elementInFrame))) {
|
||||
return elementInFrame;
|
||||
}
|
||||
if (!checkSubDocument) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (TryToMoveFocusToSubDocument(iterContent, aOriginalStartContent,
|
||||
aForward, aForDocumentNavigation,
|
||||
getter_AddRefs(elementInFrame))) {
|
||||
return elementInFrame;
|
||||
}
|
||||
|
||||
// Found content to focus
|
||||
return iterContent;
|
||||
}
|
||||
|
||||
// Search in scope owned by iterContent
|
||||
nsIContent* contentToFocus =
|
||||
GetNextTabbableContentInScope(iterContent, iterContent,
|
||||
aOriginalStartContent, aForward,
|
||||
aForward ? 1 : 0, aIgnoreTabIndex,
|
||||
aForDocumentNavigation,
|
||||
false /* aSkipOwner */);
|
||||
if (contentToFocus) {
|
||||
return contentToFocus;
|
||||
}
|
||||
};
|
||||
|
||||
// If already at lowest priority tab (0), end search completely.
|
||||
// A bit counterintuitive but true, tabindex order goes 1, 2, ... 32767, 0
|
||||
if (aCurrentTabIndex == (aForward ? 0 : 1)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Continue looking for next highest priority tabindex
|
||||
aCurrentTabIndex = GetNextTabIndex(aOwner, aCurrentTabIndex, aForward);
|
||||
contentTraversal.Reset();
|
||||
}
|
||||
|
||||
// Return scope owner at last for backward navigation if its tabindex
|
||||
// is non-negative
|
||||
if (!aSkipOwner && !aForward) {
|
||||
int32_t tabIndex = -1;
|
||||
nsIFrame* frame = aOwner->GetPrimaryFrame();
|
||||
if (frame && frame->IsFocusable(&tabIndex, false) && tabIndex >= 0) {
|
||||
return aOwner;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsFocusManager::GetNextTabbableContentInAncestorScopes(
|
||||
nsIContent* aStartOwner,
|
||||
nsIContent** aStartContent,
|
||||
nsIContent* aOriginalStartContent,
|
||||
bool aForward,
|
||||
int32_t* aCurrentTabIndex,
|
||||
bool aIgnoreTabIndex,
|
||||
bool aForDocumentNavigation)
|
||||
{
|
||||
MOZ_ASSERT(aStartOwner == FindOwner(*aStartContent),
|
||||
"aStartOwner should be the scope owner of aStartContent");
|
||||
MOZ_ASSERT(IsHostOrSlot(aStartOwner), "scope owner should be host or slot");
|
||||
|
||||
nsIContent* owner = aStartOwner;
|
||||
nsIContent* startContent = *aStartContent;
|
||||
|
||||
while (IsHostOrSlot(owner)) {
|
||||
int32_t tabIndex = 0;
|
||||
if (IsHostOrSlot(startContent)) {
|
||||
tabIndex = HostOrSlotTabIndexValue(startContent);
|
||||
} else {
|
||||
startContent->IsFocusable(&tabIndex);
|
||||
}
|
||||
nsIContent* contentToFocus =
|
||||
GetNextTabbableContentInScope(owner, startContent, aOriginalStartContent,
|
||||
aForward, tabIndex, aIgnoreTabIndex,
|
||||
aForDocumentNavigation,
|
||||
false /* aSkipOwner */);
|
||||
if (contentToFocus) {
|
||||
return contentToFocus;
|
||||
}
|
||||
|
||||
startContent = owner;
|
||||
owner = FindOwner(startContent);
|
||||
}
|
||||
|
||||
// If not found in shadow DOM, search from the top level shadow host in light DOM
|
||||
*aStartContent = startContent;
|
||||
*aCurrentTabIndex = HostOrSlotTabIndexValue(startContent);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static nsIContent*
|
||||
GetTopLevelScopeOwner(nsIContent* aContent)
|
||||
{
|
||||
nsIContent* topLevelScopeOwner = nullptr;
|
||||
while (aContent) {
|
||||
if (HTMLSlotElement* slot = aContent->GetAssignedSlot()) {
|
||||
aContent = slot;
|
||||
} else if (ShadowRoot* shadowRoot = aContent->GetContainingShadow()) {
|
||||
aContent = shadowRoot->Host();
|
||||
topLevelScopeOwner = aContent;
|
||||
} else {
|
||||
// TODO: replaced with FromNode.
|
||||
if (HTMLSlotElement::FromContentOrNull(aContent)) {
|
||||
topLevelScopeOwner = aContent;
|
||||
}
|
||||
aContent = aContent->GetParent();
|
||||
}
|
||||
}
|
||||
|
||||
return topLevelScopeOwner;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
nsIContent* aRootContent,
|
||||
|
|
@ -2995,70 +3371,163 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
|||
if (!startContent)
|
||||
return NS_OK;
|
||||
|
||||
nsIContent* currentTopLevelScopeOwner = GetTopLevelScopeOwner(aStartContent);
|
||||
|
||||
LOGCONTENTNAVIGATION("GetNextTabbable: %s", aStartContent);
|
||||
LOGFOCUSNAVIGATION((" tabindex: %d", aCurrentTabIndex));
|
||||
|
||||
if (nsDocument::IsWebComponentsEnabled(aRootContent)) {
|
||||
// If aStartContent is a shadow host or slot in forward navigation,
|
||||
// search in scope owned by aStartContent.
|
||||
if (aForward && IsHostOrSlot(aStartContent)) {
|
||||
nsIContent* contentToFocus =
|
||||
GetNextTabbableContentInScope(aStartContent, aStartContent,
|
||||
aOriginalStartContent, aForward,
|
||||
aForward ? 1 : 0, aIgnoreTabIndex,
|
||||
aForDocumentNavigation,
|
||||
true /* aSkipOwner */);
|
||||
if (contentToFocus) {
|
||||
NS_ADDREF(*aResultContent = contentToFocus);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// If aStartContent is in a scope owned by Shadow DOM search from scope
|
||||
// including aStartContent.
|
||||
if (nsIContent* owner = FindOwner(aStartContent)) {
|
||||
nsIContent* contentToFocus =
|
||||
GetNextTabbableContentInAncestorScopes(owner,
|
||||
&aStartContent,
|
||||
aOriginalStartContent,
|
||||
aForward,
|
||||
&aCurrentTabIndex,
|
||||
aIgnoreTabIndex,
|
||||
aForDocumentNavigation);
|
||||
if (contentToFocus) {
|
||||
NS_ADDREF(*aResultContent = contentToFocus);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// If we reach here, it means no next tabbable content in shadow DOM.
|
||||
// We need to continue searching in light DOM, starting at the top level
|
||||
// shadow host in light DOM (updated aStartContent) and its tabindex
|
||||
// (updated aCurrentTabIndex).
|
||||
MOZ_ASSERT(!FindOwner(aStartContent),
|
||||
"aStartContent should not be owned by Shadow DOM at this point");
|
||||
}
|
||||
|
||||
nsPresContext* presContext = aPresShell->GetPresContext();
|
||||
|
||||
bool getNextFrame = true;
|
||||
nsCOMPtr<nsIContent> iterStartContent = aStartContent;
|
||||
// Iterate tab index to find corresponding contents
|
||||
while (1) {
|
||||
nsIFrame* startFrame = iterStartContent->GetPrimaryFrame();
|
||||
nsIFrame* frame = iterStartContent->GetPrimaryFrame();
|
||||
// if there is no frame, look for another content node that has a frame
|
||||
if (!startFrame) {
|
||||
while (!frame) {
|
||||
// if the root content doesn't have a frame, just return
|
||||
if (iterStartContent == aRootContent)
|
||||
if (iterStartContent == aRootContent) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// look for the next or previous content node in tree order
|
||||
iterStartContent = aForward ? iterStartContent->GetNextNode() : iterStartContent->GetPreviousContent();
|
||||
iterStartContent = aForward ? iterStartContent->GetNextNode()
|
||||
: iterStartContent->GetPreviousContent();
|
||||
if (!iterStartContent) {
|
||||
break;
|
||||
}
|
||||
|
||||
frame = iterStartContent->GetPrimaryFrame();
|
||||
// Host without frame, enter its scope.
|
||||
if (nsDocument::IsWebComponentsEnabled(aRootContent) &&
|
||||
(!frame && iterStartContent->GetShadowRoot())) {
|
||||
int32_t tabIndex = HostOrSlotTabIndexValue(iterStartContent);
|
||||
if (tabIndex >= 0 &&
|
||||
(aIgnoreTabIndex || aCurrentTabIndex == tabIndex)) {
|
||||
nsIContent* contentToFocus = GetNextTabbableContentInScope(
|
||||
iterStartContent, iterStartContent, aOriginalStartContent,
|
||||
aForward, aForward ? 1 : 0, aIgnoreTabIndex,
|
||||
aForDocumentNavigation, true /* aSkipOwner */);
|
||||
if (contentToFocus) {
|
||||
NS_ADDREF(*aResultContent = contentToFocus);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
// we've already skipped over the initial focused content, so we
|
||||
// don't want to traverse frames.
|
||||
getNextFrame = false;
|
||||
if (iterStartContent)
|
||||
continue;
|
||||
|
||||
// otherwise, as a last attempt, just look at the root content
|
||||
iterStartContent = aRootContent;
|
||||
continue;
|
||||
}
|
||||
|
||||
// For tab navigation, pass false for aSkipPopupChecks so that we don't
|
||||
// iterate into or out of a popup. For document naviation pass true to
|
||||
// ignore these boundaries.
|
||||
nsCOMPtr<nsIFrameEnumerator> frameTraversal;
|
||||
nsresult rv = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
|
||||
presContext, startFrame,
|
||||
ePreOrder,
|
||||
false, // aVisual
|
||||
false, // aLockInScrollView
|
||||
true, // aFollowOOFs
|
||||
aForDocumentNavigation // aSkipPopupChecks
|
||||
);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (frame) {
|
||||
// For tab navigation, pass false for aSkipPopupChecks so that we don't
|
||||
// iterate into or out of a popup. For document navigation, pass true to
|
||||
// ignore these boundaries.
|
||||
nsresult rv = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
|
||||
presContext,
|
||||
frame,
|
||||
ePreOrder,
|
||||
false, // aVisual
|
||||
false, // aLockInScrollView
|
||||
true, // aFollowOOFs
|
||||
aForDocumentNavigation // aSkipPopupChecks
|
||||
);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (iterStartContent == aRootContent) {
|
||||
if (!aForward) {
|
||||
frameTraversal->Last();
|
||||
} else if (aRootContent->IsFocusable()) {
|
||||
frameTraversal->Next();
|
||||
if (iterStartContent == aRootContent) {
|
||||
if (!aForward) {
|
||||
frameTraversal->Last();
|
||||
} else if (aRootContent->IsFocusable()) {
|
||||
frameTraversal->Next();
|
||||
}
|
||||
frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
|
||||
} else if (getNextFrame &&
|
||||
(!iterStartContent ||
|
||||
!iterStartContent->IsHTMLElement(nsGkAtoms::area))) {
|
||||
// Need to do special check in case we're in an imagemap which has multiple
|
||||
// content nodes per frame, so don't skip over the starting frame.
|
||||
if (aForward) {
|
||||
frameTraversal->Next();
|
||||
} else {
|
||||
frameTraversal->Prev();
|
||||
}
|
||||
|
||||
frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
|
||||
}
|
||||
}
|
||||
else if (getNextFrame &&
|
||||
(!iterStartContent ||
|
||||
!iterStartContent->IsHTMLElement(nsGkAtoms::area))) {
|
||||
// Need to do special check in case we're in an imagemap which has multiple
|
||||
// content nodes per frame, so don't skip over the starting frame.
|
||||
if (aForward)
|
||||
frameTraversal->Next();
|
||||
else
|
||||
frameTraversal->Prev();
|
||||
}
|
||||
|
||||
// Walk frames to find something tabbable matching mCurrentTabIndex
|
||||
nsIFrame* frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
|
||||
while (frame) {
|
||||
nsIContent* currentContent = frame->GetContent();
|
||||
if (nsDocument::IsWebComponentsEnabled(currentContent)) {
|
||||
// Try to find the topmost scope owner, since we want to skip the node
|
||||
// that is not owned by document in frame traversal.
|
||||
nsIContent* oldTopLevelScopeOwner = currentTopLevelScopeOwner;
|
||||
if (!aForward || oldTopLevelScopeOwner != currentContent) {
|
||||
currentTopLevelScopeOwner = GetTopLevelScopeOwner(currentContent);
|
||||
} else {
|
||||
currentTopLevelScopeOwner = currentContent;
|
||||
}
|
||||
if (currentTopLevelScopeOwner) {
|
||||
if (currentTopLevelScopeOwner == oldTopLevelScopeOwner) {
|
||||
// We're within non-document scope, continue.
|
||||
do {
|
||||
if (aForward) {
|
||||
frameTraversal->Next();
|
||||
} else {
|
||||
frameTraversal->Prev();
|
||||
}
|
||||
frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
|
||||
// For the usage of GetPrevContinuation, see the comment
|
||||
// at the end of while (frame) loop.
|
||||
} while (frame && frame->GetPrevContinuation());
|
||||
continue;
|
||||
}
|
||||
currentContent = currentTopLevelScopeOwner;
|
||||
}
|
||||
}
|
||||
|
||||
// For document navigation, check if this element is an open panel. Since
|
||||
// panels aren't focusable (tabIndex would be -1), we'll just assume that
|
||||
|
|
@ -3092,7 +3561,7 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
|||
// and root content, so that we only find content within the panel.
|
||||
// Note also that we pass false for aForDocumentNavigation since we
|
||||
// want to locate the first content, not the first document.
|
||||
rv = GetNextTabbableContent(aPresShell, currentContent,
|
||||
nsresult rv = GetNextTabbableContent(aPresShell, currentContent,
|
||||
nullptr, currentContent,
|
||||
true, 1, false, false,
|
||||
aResultContent);
|
||||
|
|
@ -3103,6 +3572,34 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
|||
}
|
||||
}
|
||||
|
||||
// As of now, 2018/04/12, sequential focus navigation is still
|
||||
// in the obsolete Shadow DOM specification.
|
||||
// http://w3c.github.io/webcomponents/spec/shadow/#sequential-focus-navigation
|
||||
// "if ELEMENT is focusable, a shadow host, or a slot element,
|
||||
// append ELEMENT to NAVIGATION-ORDER."
|
||||
// and later in "For each element ELEMENT in NAVIGATION-ORDER: "
|
||||
// hosts and slots are handled before other elements.
|
||||
if (nsDocument::IsWebComponentsEnabled(currentContent) &&
|
||||
IsHostOrSlot(currentContent)) {
|
||||
bool focusableHostSlot;
|
||||
int32_t tabIndex =
|
||||
HostOrSlotTabIndexValue(currentContent, &focusableHostSlot);
|
||||
// Host or slot itself isn't focusable or going backwards, enter its scope.
|
||||
if ((!aForward || !focusableHostSlot) && tabIndex >= 0 &&
|
||||
(aIgnoreTabIndex || aCurrentTabIndex == tabIndex)) {
|
||||
nsIContent* contentToFocus =
|
||||
GetNextTabbableContentInScope(currentContent, currentContent,
|
||||
aOriginalStartContent, aForward,
|
||||
aForward ? 1 : 0, aIgnoreTabIndex,
|
||||
aForDocumentNavigation,
|
||||
true /* aSkipOwner */);
|
||||
if (contentToFocus) {
|
||||
NS_ADDREF(*aResultContent = contentToFocus);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TabIndex not set defaults to 0 for form elements, anchors and other
|
||||
// elements that are normally focusable. Tabindex defaults to -1
|
||||
// for elements that are not normally focusable.
|
||||
|
|
@ -3151,54 +3648,22 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
|||
|
||||
// Next, for document navigation, check if this a non-remote child document.
|
||||
bool checkSubDocument = true;
|
||||
if (aForDocumentNavigation) {
|
||||
nsIContent* docRoot = GetRootForChildDocument(currentContent);
|
||||
if (docRoot) {
|
||||
// If GetRootForChildDocument returned something then call
|
||||
// FocusFirst to find the root or first element to focus within
|
||||
// the child document. If this is a frameset though, skip this and
|
||||
// fall through to the checkSubDocument block below to iterate into
|
||||
// the frameset's frames and locate the first focusable frame.
|
||||
if (!docRoot->IsHTMLElement(nsGkAtoms::frameset)) {
|
||||
return FocusFirst(docRoot, aResultContent);
|
||||
}
|
||||
} else {
|
||||
// Set checkSubDocument to false, as this was neither a frame
|
||||
// type element or a child document that was focusable.
|
||||
checkSubDocument = false;
|
||||
}
|
||||
if (aForDocumentNavigation &&
|
||||
TryDocumentNavigation(currentContent, &checkSubDocument,
|
||||
aResultContent)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (checkSubDocument) {
|
||||
// found a node with a matching tab index. Check if it is a child
|
||||
// frame. If so, navigate into the child frame instead.
|
||||
nsIDocument* doc = currentContent->GetComposedDoc();
|
||||
NS_ASSERTION(doc, "content not in document");
|
||||
nsIDocument* subdoc = doc->GetSubDocumentFor(currentContent);
|
||||
if (subdoc && !subdoc->EventHandlingSuppressed()) {
|
||||
if (aForward) {
|
||||
// when tabbing forward into a frame, return the root
|
||||
// frame so that the canvas becomes focused.
|
||||
nsCOMPtr<nsPIDOMWindowOuter> subframe = subdoc->GetWindow();
|
||||
if (subframe) {
|
||||
*aResultContent = GetRootForFocus(subframe, subdoc, false, true);
|
||||
if (*aResultContent) {
|
||||
NS_ADDREF(*aResultContent);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
Element* rootElement = subdoc->GetRootElement();
|
||||
nsIPresShell* subShell = subdoc->GetShell();
|
||||
if (rootElement && subShell) {
|
||||
rv = GetNextTabbableContent(subShell, rootElement,
|
||||
aOriginalStartContent, rootElement,
|
||||
aForward, (aForward ? 1 : 0),
|
||||
false, aForDocumentNavigation, aResultContent);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (*aResultContent)
|
||||
return NS_OK;
|
||||
}
|
||||
if (TryToMoveFocusToSubDocument(currentContent,
|
||||
aOriginalStartContent,
|
||||
aForward,
|
||||
aForDocumentNavigation,
|
||||
aResultContent)) {
|
||||
MOZ_ASSERT(*aResultContent);
|
||||
return NS_OK;
|
||||
}
|
||||
// otherwise, use this as the next content node to tab to, unless
|
||||
// this was the element we started on. This would happen for
|
||||
|
|
@ -3269,6 +3734,77 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsFocusManager::TryDocumentNavigation(nsIContent* aCurrentContent,
|
||||
bool* aCheckSubDocument,
|
||||
nsIContent** aResultContent)
|
||||
{
|
||||
*aCheckSubDocument = true;
|
||||
nsIContent* docRoot = GetRootForChildDocument(aCurrentContent);
|
||||
if (docRoot) {
|
||||
// If GetRootForChildDocument returned something then call
|
||||
// FocusFirst to find the root or first element to focus within
|
||||
// the child document. If this is a frameset though, skip this and
|
||||
// fall through to normal tab navigation to iterate into
|
||||
// the frameset's frames and locate the first focusable frame.
|
||||
if (!docRoot->IsHTMLElement(nsGkAtoms::frameset)) {
|
||||
*aCheckSubDocument = false;
|
||||
Unused << FocusFirst(docRoot, aResultContent);
|
||||
return *aResultContent != nullptr;
|
||||
}
|
||||
} else {
|
||||
// Set aCheckSubDocument to false, as this was neither a frame
|
||||
// type element or a child document that was focusable.
|
||||
*aCheckSubDocument = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
nsFocusManager::TryToMoveFocusToSubDocument(nsIContent* aCurrentContent,
|
||||
nsIContent* aOriginalStartContent,
|
||||
bool aForward,
|
||||
bool aForDocumentNavigation,
|
||||
nsIContent** aResultContent)
|
||||
{
|
||||
nsIDocument* doc = aCurrentContent->GetComposedDoc();
|
||||
NS_ASSERTION(doc, "content not in document");
|
||||
nsIDocument* subdoc = doc->GetSubDocumentFor(aCurrentContent);
|
||||
if (subdoc && !subdoc->EventHandlingSuppressed()) {
|
||||
if (aForward) {
|
||||
// when tabbing forward into a frame, return the root
|
||||
// frame so that the canvas becomes focused.
|
||||
nsCOMPtr<nsPIDOMWindowOuter> subframe = subdoc->GetWindow();
|
||||
if (subframe) {
|
||||
*aResultContent = GetRootForFocus(subframe, subdoc, false, true);
|
||||
if (*aResultContent) {
|
||||
NS_ADDREF(*aResultContent);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Element* rootElement = subdoc->GetRootElement();
|
||||
nsIPresShell* subShell = subdoc->GetShell();
|
||||
if (rootElement && subShell) {
|
||||
nsresult rv = GetNextTabbableContent(subShell,
|
||||
rootElement,
|
||||
aOriginalStartContent,
|
||||
rootElement,
|
||||
aForward,
|
||||
(aForward ? 1 : 0),
|
||||
false,
|
||||
aForDocumentNavigation,
|
||||
aResultContent);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
if (*aResultContent) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsFocusManager::GetNextTabbableMapArea(bool aForward,
|
||||
int32_t aCurrentTabIndex,
|
||||
|
|
@ -3315,15 +3851,20 @@ nsFocusManager::GetNextTabIndex(nsIContent* aParent,
|
|||
bool aForward)
|
||||
{
|
||||
int32_t tabIndex, childTabIndex;
|
||||
StyleChildrenIterator iter(aParent);
|
||||
|
||||
if (aForward) {
|
||||
tabIndex = 0;
|
||||
for (nsIContent* child = aParent->GetFirstChild();
|
||||
for (nsIContent* child = iter.GetNextChild();
|
||||
child;
|
||||
child = child->GetNextSibling()) {
|
||||
childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
|
||||
if (childTabIndex > aCurrentTabIndex && childTabIndex != tabIndex) {
|
||||
tabIndex = (tabIndex == 0 || childTabIndex < tabIndex) ? childTabIndex : tabIndex;
|
||||
child = iter.GetNextChild()) {
|
||||
// Skip child's descendants if child is a shadow host, as they are
|
||||
// in the focus navigation scope owned by child's shadow root
|
||||
if (!(nsDocument::IsWebComponentsEnabled(aParent) && IsHostOrSlot(child))) {
|
||||
childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
|
||||
if (childTabIndex > aCurrentTabIndex && childTabIndex != tabIndex) {
|
||||
tabIndex = (tabIndex == 0 || childTabIndex < tabIndex) ? childTabIndex : tabIndex;
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoString tabIndexStr;
|
||||
|
|
@ -3337,13 +3878,17 @@ nsFocusManager::GetNextTabIndex(nsIContent* aParent,
|
|||
}
|
||||
else { /* !aForward */
|
||||
tabIndex = 1;
|
||||
for (nsIContent* child = aParent->GetFirstChild();
|
||||
for (nsIContent* child = iter.GetNextChild();
|
||||
child;
|
||||
child = child->GetNextSibling()) {
|
||||
childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
|
||||
if ((aCurrentTabIndex == 0 && childTabIndex > tabIndex) ||
|
||||
(childTabIndex < aCurrentTabIndex && childTabIndex > tabIndex)) {
|
||||
tabIndex = childTabIndex;
|
||||
child = iter.GetNextChild()) {
|
||||
// Skip child's descendants if child is a shadow host, as they are
|
||||
// in the focus navigation scope owned by child's shadow root
|
||||
if (!(nsDocument::IsWebComponentsEnabled(aParent) && IsHostOrSlot(child))) {
|
||||
childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
|
||||
if ((aCurrentTabIndex == 0 && childTabIndex > tabIndex) ||
|
||||
(childTabIndex < aCurrentTabIndex && childTabIndex > tabIndex)) {
|
||||
tabIndex = childTabIndex;
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoString tabIndexStr;
|
||||
|
|
|
|||
|
|
@ -384,6 +384,91 @@ protected:
|
|||
int32_t aType, bool aNoParentTraversal,
|
||||
nsIContent** aNextContent);
|
||||
|
||||
/**
|
||||
* Returns scope owner of aContent.
|
||||
* A scope owner is either a document root, shadow host, or slot.
|
||||
*/
|
||||
nsIContent* FindOwner(nsIContent* aContent);
|
||||
|
||||
/**
|
||||
* Retrieve the next tabbable element in scope owned by aOwner, using
|
||||
* focusability and tabindex to determine the tab order.
|
||||
*
|
||||
* aOwner is the owner of scope to search in.
|
||||
*
|
||||
* aStartContent is the starting point for this call of this method.
|
||||
*
|
||||
* aOriginalStartContent is the initial starting point for sequential
|
||||
* navigation.
|
||||
*
|
||||
* aForward should be true for forward navigation or false for backward
|
||||
* navigation.
|
||||
*
|
||||
* aCurrentTabIndex is the current tabindex.
|
||||
*
|
||||
* aIgnoreTabIndex to ignore the current tabindex and find the element
|
||||
* irrespective or the tab index.
|
||||
*
|
||||
* aForDocumentNavigation informs whether we're navigating only through
|
||||
* documents.
|
||||
*
|
||||
* aSkipOwner to skip owner while searching. The flag is set when caller is
|
||||
* |GetNextTabbableContent| in order to let caller handle owner.
|
||||
*
|
||||
* NOTE:
|
||||
* Consider the method searches downwards in flattened subtree
|
||||
* rooted at aOwner.
|
||||
*/
|
||||
nsIContent* GetNextTabbableContentInScope(nsIContent* aOwner,
|
||||
nsIContent* aStartContent,
|
||||
nsIContent* aOriginalStartContent,
|
||||
bool aForward,
|
||||
int32_t aCurrentTabIndex,
|
||||
bool aIgnoreTabIndex,
|
||||
bool aForDocumentNavigation,
|
||||
bool aSkipOwner);
|
||||
|
||||
/**
|
||||
* Retrieve the next tabbable element in scope including aStartContent
|
||||
* and the scope's ancestor scopes, using focusability and tabindex to
|
||||
* determine the tab order.
|
||||
*
|
||||
* aStartOwner is the scope owner of the aStartContent.
|
||||
*
|
||||
* aStartContent an in/out paremeter. It as input is the starting point
|
||||
* for this call of this method; as output it is the shadow host in
|
||||
* light DOM if the next tabbable element is not found in shadow DOM,
|
||||
* in order to continue searching in light DOM.
|
||||
*
|
||||
* aOriginalStartContent is the initial starting point for sequential
|
||||
* navigation.
|
||||
*
|
||||
* aForward should be true for forward navigation or false for backward
|
||||
* navigation.
|
||||
*
|
||||
* aCurrentTabIndex returns tab index of shadow host in light DOM if the
|
||||
* next tabbable element is not found in shadow DOM, in order to continue
|
||||
* searching in light DOM.
|
||||
*
|
||||
* aIgnoreTabIndex to ignore the current tabindex and find the element
|
||||
* irrespective or the tab index.
|
||||
*
|
||||
* aForDocumentNavigation informs whether we're navigating only through
|
||||
* documents.
|
||||
*
|
||||
* NOTE:
|
||||
* Consider the method searches upwards in all shadow host- or slot-rooted
|
||||
* flattened subtrees that contains aStartContent as non-root, except
|
||||
* the flattened subtree rooted at shadow host in light DOM.
|
||||
*/
|
||||
nsIContent* GetNextTabbableContentInAncestorScopes(nsIContent* aStartOwner,
|
||||
nsIContent** aStartContent,
|
||||
nsIContent* aOriginalStartContent,
|
||||
bool aForward,
|
||||
int32_t* aCurrentTabIndex,
|
||||
bool aIgnoreTabIndex,
|
||||
bool aForDocumentNavigation);
|
||||
|
||||
/**
|
||||
* Retrieve the next tabbable element within a document, using focusability
|
||||
* and tabindex to determine the tab order. The element is returned in
|
||||
|
|
@ -503,6 +588,16 @@ private:
|
|||
|
||||
void SetFocusedWindowInternal(nsPIDOMWindowOuter* aWindow);
|
||||
|
||||
bool TryDocumentNavigation(nsIContent* aCurrentContent,
|
||||
bool* aCheckSubDocument,
|
||||
nsIContent** aResultContent);
|
||||
|
||||
bool TryToMoveFocusToSubDocument(nsIContent* aCurrentContent,
|
||||
nsIContent* aOriginalStartContent,
|
||||
bool aForward,
|
||||
bool aForDocumentNavigation,
|
||||
nsIContent** aResultContent);
|
||||
|
||||
// the currently active and front-most top-most window
|
||||
nsCOMPtr<nsPIDOMWindowOuter> mActiveWindow;
|
||||
|
||||
|
|
|
|||
|
|
@ -1811,26 +1811,6 @@ public:
|
|||
nsIAtom* aAttrName,
|
||||
const nsAString& aAttrValue) const = 0;
|
||||
|
||||
/**
|
||||
* Helper for nsIDOMDocument::elementFromPoint implementation that allows
|
||||
* ignoring the scroll frame and/or avoiding layout flushes.
|
||||
*
|
||||
* @see nsIDOMWindowUtils::elementFromPoint
|
||||
*/
|
||||
virtual Element* ElementFromPointHelper(float aX, float aY,
|
||||
bool aIgnoreRootScrollFrame,
|
||||
bool aFlushLayout) = 0;
|
||||
|
||||
enum ElementsFromPointFlags {
|
||||
IGNORE_ROOT_SCROLL_FRAME = 1,
|
||||
FLUSH_LAYOUT = 2,
|
||||
IS_ELEMENT_FROM_POINT = 4
|
||||
};
|
||||
|
||||
virtual void ElementsFromPointHelper(float aX, float aY,
|
||||
uint32_t aFlags,
|
||||
nsTArray<RefPtr<mozilla::dom::Element>>& aElements) = 0;
|
||||
|
||||
virtual nsresult NodesFromRectHelper(float aX, float aY,
|
||||
float aTopSize, float aRightSize,
|
||||
float aBottomSize, float aLeftSize,
|
||||
|
|
@ -2637,17 +2617,17 @@ public:
|
|||
nsIURI* GetDocumentURIObject() const;
|
||||
// Not const because all the full-screen goop is not const
|
||||
virtual bool FullscreenEnabled() = 0;
|
||||
virtual Element* GetFullscreenElement() = 0;
|
||||
virtual Element* FullScreenStackTop() = 0;
|
||||
bool Fullscreen()
|
||||
{
|
||||
return !!GetFullscreenElement();
|
||||
}
|
||||
void ExitFullscreen();
|
||||
Element* GetPointerLockElement();
|
||||
void ExitPointerLock()
|
||||
{
|
||||
UnlockPointer(this);
|
||||
}
|
||||
static bool IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject);
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
bool Hidden() const
|
||||
{
|
||||
|
|
@ -2664,10 +2644,6 @@ public:
|
|||
void GetPreferredStyleSheetSet(nsAString& aSheetSet);
|
||||
virtual mozilla::dom::DOMStringList* StyleSheetSets() = 0;
|
||||
virtual void EnableStyleSheetsForSet(const nsAString& aSheetSet) = 0;
|
||||
Element* ElementFromPoint(float aX, float aY);
|
||||
void ElementsFromPoint(float aX,
|
||||
float aY,
|
||||
nsTArray<RefPtr<mozilla::dom::Element>>& aElements);
|
||||
|
||||
/**
|
||||
* Retrieve the location of the caret position (DOM node and character
|
||||
|
|
@ -2835,6 +2811,16 @@ public:
|
|||
|
||||
bool ModuleScriptsEnabled();
|
||||
|
||||
/**
|
||||
* Find the (non-anonymous) content in this document for aFrame. It will
|
||||
* be aFrame's content node if that content is in this document and not
|
||||
* anonymous. Otherwise, when aFrame is in a subdocument, we use the frame
|
||||
* element containing the subdocument containing aFrame, and/or find the
|
||||
* nearest non-anonymous ancestor in this document.
|
||||
* Returns null if there is no such element.
|
||||
*/
|
||||
nsIContent* GetContentInThisDocument(nsIFrame* aFrame) const;
|
||||
|
||||
virtual void AddResizeObserver(mozilla::dom::ResizeObserver* aResizeObserver) = 0;
|
||||
|
||||
virtual void ScheduleResizeObserversNotification() const = 0;
|
||||
|
|
|
|||
|
|
@ -273,6 +273,18 @@ nsINode* nsINode::GetRootNode(const GetRootNodeOptions& aOptions)
|
|||
return SubtreeRoot();
|
||||
}
|
||||
|
||||
// TODO: was marked as constant.
|
||||
nsINode*
|
||||
nsINode::GetParentOrHostNode()
|
||||
{
|
||||
if (mParent) {
|
||||
return mParent;
|
||||
}
|
||||
|
||||
ShadowRoot* shadowRoot = ShadowRoot::FromNode(this);
|
||||
return shadowRoot ? shadowRoot->GetHost() : nullptr;
|
||||
}
|
||||
|
||||
nsINode*
|
||||
nsINode::SubtreeRoot() const
|
||||
{
|
||||
|
|
@ -1543,32 +1555,25 @@ nsINode::SetExplicitBaseURI(nsIURI* aURI)
|
|||
return rv;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
AdoptNodeIntoOwnerDoc(nsINode *aParent, nsINode *aNode)
|
||||
static void
|
||||
AdoptNodeIntoOwnerDoc(nsINode *aParent, nsINode *aNode, ErrorResult& aError)
|
||||
{
|
||||
NS_ASSERTION(!aNode->GetParentNode(),
|
||||
"Should have removed from parent already");
|
||||
|
||||
nsIDocument *doc = aParent->OwnerDoc();
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
DebugOnly<nsINode*> adoptedNode = doc->AdoptNode(*aNode, aError);
|
||||
|
||||
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIDOMNode> adoptedNode;
|
||||
rv = domDoc->AdoptNode(node, getter_AddRefs(adoptedNode));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ASSERTION(aParent->OwnerDoc() == doc,
|
||||
"ownerDoc chainged while adopting");
|
||||
NS_ASSERTION(adoptedNode == node, "Uh, adopt node changed nodes?");
|
||||
NS_ASSERTION(aParent->OwnerDoc() == aNode->OwnerDoc(),
|
||||
#ifdef DEBUG
|
||||
if (!aError.Failed()) {
|
||||
MOZ_ASSERT(aParent->OwnerDoc() == doc,
|
||||
"ownerDoc changed while adopting");
|
||||
MOZ_ASSERT(adoptedNode == aNode, "Uh, adopt node changed nodes?");
|
||||
MOZ_ASSERT(aParent->OwnerDoc() == aNode->OwnerDoc(),
|
||||
"ownerDocument changed again after adopting!");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
#endif // DEBUG
|
||||
}
|
||||
|
||||
static nsresult
|
||||
|
|
@ -1593,19 +1598,20 @@ ReparentWrappersInSubtree(nsIContent* aRoot)
|
|||
|
||||
rootedGlobal = xpc::GetXBLScope(cx, rootedGlobal);
|
||||
|
||||
nsresult rv;
|
||||
ErrorResult rv;
|
||||
JS::Rooted<JSObject*> reflector(cx);
|
||||
for (nsIContent* cur = aRoot; cur; cur = cur->GetNextNode(aRoot)) {
|
||||
if ((reflector = cur->GetWrapper())) {
|
||||
JSAutoCompartment ac(cx, reflector);
|
||||
rv = ReparentWrapper(cx, reflector);
|
||||
if NS_FAILED(rv) {
|
||||
ReparentWrapper(cx, reflector, rv);
|
||||
rv.WouldReportJSException();
|
||||
if (rv.Failed()) {
|
||||
// We _could_ consider BlastSubtreeToPieces here, but it's not really
|
||||
// needed. Having some nodes in here accessible to content while others
|
||||
// are not is probably OK. We just need to fail out of the actual
|
||||
// insertion, so they're not in the DOM. Returning a failure here will
|
||||
// do that.
|
||||
return rv;
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1619,7 +1625,6 @@ nsINode::doInsertChildAt(nsIContent* aKid, uint32_t aIndex,
|
|||
{
|
||||
NS_PRECONDITION(!aKid->GetParentNode(),
|
||||
"Inserting node that already has parent");
|
||||
nsresult rv;
|
||||
|
||||
// The id-handling code, and in the future possibly other code, need to
|
||||
// react to unexpected attribute changes.
|
||||
|
|
@ -1630,15 +1635,23 @@ nsINode::doInsertChildAt(nsIContent* aKid, uint32_t aIndex,
|
|||
mozAutoDocUpdate updateBatch(GetComposedDoc(), UPDATE_CONTENT_MODEL, aNotify);
|
||||
|
||||
if (OwnerDoc() != aKid->OwnerDoc()) {
|
||||
rv = AdoptNodeIntoOwnerDoc(this, aKid);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
ErrorResult error;
|
||||
AdoptNodeIntoOwnerDoc(this, aKid, error);
|
||||
|
||||
// Need to WouldReportJSException() if our callee can throw a JS
|
||||
// exception (which it can) and we're neither propagating the
|
||||
// error out nor unconditionally suppressing it.
|
||||
error.WouldReportJSException();
|
||||
if (NS_WARN_IF(error.Failed())) {
|
||||
return error.StealNSResult();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t childCount = aChildArray.ChildCount();
|
||||
NS_ENSURE_TRUE(aIndex <= childCount, NS_ERROR_ILLEGAL_VALUE);
|
||||
bool isAppend = (aIndex == childCount);
|
||||
|
||||
rv = aChildArray.InsertChildAt(aKid, aIndex);
|
||||
nsresult rv = aChildArray.InsertChildAt(aKid, aIndex);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (aIndex == 0) {
|
||||
mFirstChild = aKid;
|
||||
|
|
@ -2475,7 +2488,7 @@ nsINode::ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild,
|
|||
// inserting them w/o calling AdoptNode().
|
||||
nsIDocument* doc = OwnerDoc();
|
||||
if (doc != newContent->OwnerDoc()) {
|
||||
aError = AdoptNodeIntoOwnerDoc(this, aNewChild);
|
||||
AdoptNodeIntoOwnerDoc(this, aNewChild, aError);
|
||||
if (aError.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -3072,9 +3085,7 @@ nsINode::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
|
|||
already_AddRefed<nsINode>
|
||||
nsINode::CloneNode(bool aDeep, ErrorResult& aError)
|
||||
{
|
||||
nsCOMPtr<nsINode> result;
|
||||
aError = nsNodeUtils::CloneNodeImpl(this, aDeep, getter_AddRefs(result));
|
||||
return result.forget();
|
||||
return nsNodeUtils::CloneNodeImpl(this, aDeep, aError);
|
||||
}
|
||||
|
||||
nsDOMAttributeMap*
|
||||
|
|
|
|||
|
|
@ -942,6 +942,12 @@ public:
|
|||
return mParent;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is similar to above, but in case 'this' is ShadowRoot, we return its
|
||||
* host element.
|
||||
*/
|
||||
nsINode* GetParentOrHostNode();
|
||||
|
||||
/**
|
||||
* Returns the node that is the parent of this node in the flattened
|
||||
* tree. This differs from the normal parent if the node is filtered
|
||||
|
|
|
|||
|
|
@ -1430,13 +1430,11 @@ nsImageLoadingContent::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
|||
nsIContent* aBindingParent,
|
||||
bool aCompileEventHandlers)
|
||||
{
|
||||
// We may be entering the document, so if our image should be tracked,
|
||||
// track it.
|
||||
if (!aDocument)
|
||||
return;
|
||||
|
||||
TrackImage(mCurrentRequest);
|
||||
TrackImage(mPendingRequest);
|
||||
// We may be getting connected and our image should be tracked.
|
||||
if (GetOurCurrentDoc()) {
|
||||
TrackImage(mCurrentRequest);
|
||||
TrackImage(mPendingRequest);
|
||||
}
|
||||
|
||||
if (mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD)
|
||||
aDocument->BlockOnload();
|
||||
|
|
|
|||
|
|
@ -404,28 +404,20 @@ nsNodeUtils::TraverseUserData(nsINode* aNode,
|
|||
}
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
nsNodeUtils::CloneNodeImpl(nsINode *aNode, bool aDeep, nsINode **aResult)
|
||||
already_AddRefed<nsINode>
|
||||
nsNodeUtils::CloneNodeImpl(nsINode *aNode, bool aDeep, ErrorResult& aError)
|
||||
{
|
||||
*aResult = nullptr;
|
||||
|
||||
nsCOMPtr<nsINode> newNode;
|
||||
nsCOMArray<nsINode> nodesWithProperties;
|
||||
nsresult rv = Clone(aNode, aDeep, nullptr, nodesWithProperties,
|
||||
getter_AddRefs(newNode));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
newNode.forget(aResult);
|
||||
return NS_OK;
|
||||
return Clone(aNode, aDeep, nullptr, &nodesWithProperties, aError);
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
already_AddRefed<nsINode>
|
||||
nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
||||
nsNodeInfoManager *aNewNodeInfoManager,
|
||||
JS::Handle<JSObject*> aReparentScope,
|
||||
nsCOMArray<nsINode> &aNodesWithProperties,
|
||||
nsINode *aParent, nsINode **aResult)
|
||||
nsCOMArray<nsINode> *aNodesWithProperties,
|
||||
nsINode *aParent, ErrorResult& aError)
|
||||
{
|
||||
NS_PRECONDITION((!aClone && aNewNodeInfoManager) || !aReparentScope,
|
||||
"If cloning or not getting a new nodeinfo we shouldn't "
|
||||
|
|
@ -433,14 +425,11 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
|||
NS_PRECONDITION(!aParent || aNode->IsNodeOfType(nsINode::eCONTENT),
|
||||
"Can't insert document or attribute nodes into a parent");
|
||||
|
||||
*aResult = nullptr;
|
||||
|
||||
// First deal with aNode and walk its attributes (and their children). Then,
|
||||
// if aDeep is true, deal with aNode's children (and recurse into their
|
||||
// attributes and children).
|
||||
|
||||
nsAutoScriptBlocker scriptBlocker;
|
||||
nsresult rv;
|
||||
|
||||
nsNodeInfoManager *nodeInfoManager = aNewNodeInfoManager;
|
||||
|
||||
|
|
@ -452,14 +441,20 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
|||
// Don't allow importing/adopting nodes from non-privileged "scriptable"
|
||||
// documents to "non-scriptable" documents.
|
||||
nsIDocument* newDoc = nodeInfoManager->GetDocument();
|
||||
NS_ENSURE_STATE(newDoc);
|
||||
if (NS_WARN_IF(!newDoc)) {
|
||||
aError.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
bool hasHadScriptHandlingObject = false;
|
||||
if (!newDoc->GetScriptHandlingObject(hasHadScriptHandlingObject) &&
|
||||
!hasHadScriptHandlingObject) {
|
||||
nsIDocument* currentDoc = aNode->OwnerDoc();
|
||||
NS_ENSURE_STATE((nsContentUtils::IsChromeDoc(currentDoc) ||
|
||||
(!currentDoc->GetScriptHandlingObject(hasHadScriptHandlingObject) &&
|
||||
!hasHadScriptHandlingObject)));
|
||||
if (NS_WARN_IF(!nsContentUtils::IsChromeDoc(currentDoc) &&
|
||||
(currentDoc->GetScriptHandlingObject(hasHadScriptHandlingObject) ||
|
||||
hasHadScriptHandlingObject))) {
|
||||
aError.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
newNodeInfo = nodeInfoManager->GetNodeInfo(nodeInfo->NameAtom(),
|
||||
|
|
@ -475,8 +470,11 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
|||
|
||||
nsCOMPtr<nsINode> clone;
|
||||
if (aClone) {
|
||||
rv = aNode->Clone(nodeInfo, getter_AddRefs(clone));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsresult rv = aNode->Clone(nodeInfo, getter_AddRefs(clone));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aError.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (CustomElementRegistry::IsCustomElementEnabled() &&
|
||||
clone->IsHTMLElement()) {
|
||||
|
|
@ -514,7 +512,10 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
|||
// parent.
|
||||
rv = aParent->AppendChildTo(static_cast<nsIContent*>(clone.get()),
|
||||
false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aError.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else if (aDeep && clone->IsNodeOfType(nsINode::eDOCUMENT)) {
|
||||
// After cloning the document itself, we want to clone the children into
|
||||
|
|
@ -613,8 +614,8 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
|||
if ((wrapper = aNode->GetWrapper())) {
|
||||
MOZ_ASSERT(IsDOMObject(wrapper));
|
||||
JSAutoCompartment ac(cx, wrapper);
|
||||
rv = ReparentWrapper(cx, wrapper);
|
||||
if (NS_FAILED(rv)) {
|
||||
ReparentWrapper(cx, wrapper, aError);
|
||||
if (aError.Failed()) {
|
||||
if (wasRegistered) {
|
||||
aNode->OwnerDoc()->UnregisterActivityObserver(aNode->AsElement());
|
||||
}
|
||||
|
|
@ -622,17 +623,17 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
|||
if (wasRegistered) {
|
||||
aNode->OwnerDoc()->RegisterActivityObserver(aNode->AsElement());
|
||||
}
|
||||
return rv;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aNode->HasProperties()) {
|
||||
bool ok = aNodesWithProperties.AppendObject(aNode);
|
||||
bool ok = aNodesWithProperties->AppendObject(aNode);
|
||||
MOZ_RELEASE_ASSERT(ok, "Out of memory");
|
||||
if (aClone) {
|
||||
ok = aNodesWithProperties.AppendObject(clone);
|
||||
ok = aNodesWithProperties->AppendObject(clone);
|
||||
MOZ_RELEASE_ASSERT(ok, "Out of memory");
|
||||
}
|
||||
}
|
||||
|
|
@ -642,11 +643,47 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
|||
for (nsIContent* cloneChild = aNode->GetFirstChild();
|
||||
cloneChild;
|
||||
cloneChild = cloneChild->GetNextSibling()) {
|
||||
nsCOMPtr<nsINode> child;
|
||||
rv = CloneAndAdopt(cloneChild, aClone, true, nodeInfoManager,
|
||||
aReparentScope, aNodesWithProperties, clone,
|
||||
getter_AddRefs(child));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsINode> child =
|
||||
CloneAndAdopt(cloneChild, aClone, true, nodeInfoManager,
|
||||
aReparentScope, aNodesWithProperties, clone,
|
||||
aError);
|
||||
if (NS_WARN_IF(aError.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aDeep && aNode->IsElement()) {
|
||||
ShadowRoot* shadowRoot = aNode->AsElement()->GetShadowRoot();
|
||||
if (!shadowRoot) {
|
||||
// Nothing to do here.
|
||||
} else if (aClone && clone->OwnerDoc()->IsStaticDocument()) {
|
||||
ShadowRootInit init;
|
||||
init.mMode = shadowRoot->Mode();
|
||||
RefPtr<ShadowRoot> newShadowRoot =
|
||||
clone->AsElement()->AttachShadow(init, aError);
|
||||
if (NS_WARN_IF(aError.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
newShadowRoot->CloneInternalDataFrom(shadowRoot);
|
||||
for (nsIContent* originalChild = shadowRoot->GetFirstChild();
|
||||
originalChild;
|
||||
originalChild = originalChild->GetNextSibling()) {
|
||||
nsCOMPtr<nsINode> child = CloneAndAdopt(originalChild, aClone, aDeep, nodeInfoManager,
|
||||
aReparentScope, aNodesWithProperties, newShadowRoot,
|
||||
aError);
|
||||
if (NS_WARN_IF(aError.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
} else if (!aClone) {
|
||||
nsCOMPtr<nsINode> child = CloneAndAdopt(shadowRoot, aClone, aDeep, nodeInfoManager,
|
||||
aReparentScope, aNodesWithProperties, clone,
|
||||
aError);
|
||||
if (NS_WARN_IF(aError.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -665,11 +702,13 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
|||
for (nsIContent* cloneChild = origContent->GetFirstChild();
|
||||
cloneChild;
|
||||
cloneChild = cloneChild->GetNextSibling()) {
|
||||
nsCOMPtr<nsINode> child;
|
||||
rv = CloneAndAdopt(cloneChild, aClone, aDeep, ownerNodeInfoManager,
|
||||
aReparentScope, aNodesWithProperties, cloneContent,
|
||||
getter_AddRefs(child));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsINode> child =
|
||||
CloneAndAdopt(cloneChild, aClone, aDeep, ownerNodeInfoManager,
|
||||
aReparentScope, aNodesWithProperties, cloneContent,
|
||||
aError);
|
||||
if (NS_WARN_IF(aError.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -692,9 +731,7 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
|||
}
|
||||
#endif
|
||||
|
||||
clone.forget(aResult);
|
||||
|
||||
return NS_OK;
|
||||
return clone.forget();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ template<class E> class nsCOMArray;
|
|||
class nsCycleCollectionTraversalCallback;
|
||||
namespace mozilla {
|
||||
struct NonOwningAnimationTarget;
|
||||
class ErrorResult;
|
||||
namespace dom {
|
||||
class Animation;
|
||||
} // namespace dom
|
||||
|
|
@ -181,25 +182,17 @@ public:
|
|||
* @param aNodesWithProperties All nodes (from amongst aNode and its
|
||||
* descendants) with properties. Every node will
|
||||
* be followed by its clone.
|
||||
* @param aResult *aResult will contain the cloned node.
|
||||
* @param aError The error, if any.
|
||||
*
|
||||
* @return The newly created node. Null in error conditions.
|
||||
*/
|
||||
static nsresult Clone(nsINode *aNode, bool aDeep,
|
||||
nsNodeInfoManager *aNewNodeInfoManager,
|
||||
nsCOMArray<nsINode> &aNodesWithProperties,
|
||||
nsINode **aResult)
|
||||
static already_AddRefed<nsINode> Clone(nsINode *aNode, bool aDeep,
|
||||
nsNodeInfoManager *aNewNodeInfoManager,
|
||||
nsCOMArray<nsINode> *aNodesWithProperties,
|
||||
mozilla::ErrorResult& aError)
|
||||
{
|
||||
return CloneAndAdopt(aNode, true, aDeep, aNewNodeInfoManager,
|
||||
nullptr, aNodesWithProperties, nullptr, aResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones aNode, its attributes and, if aDeep is true, its descendant nodes
|
||||
*/
|
||||
static nsresult Clone(nsINode *aNode, bool aDeep, nsINode **aResult)
|
||||
{
|
||||
nsCOMArray<nsINode> dummyNodeWithProperties;
|
||||
return CloneAndAdopt(aNode, true, aDeep, nullptr, nullptr,
|
||||
dummyNodeWithProperties, aNode->GetParent(), aResult);
|
||||
nullptr, aNodesWithProperties, nullptr, aError);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -218,19 +211,20 @@ public:
|
|||
* should be done.
|
||||
* @param aNodesWithProperties All nodes (from amongst aNode and its
|
||||
* descendants) with properties.
|
||||
* @param aError The error, if any.
|
||||
*/
|
||||
static nsresult Adopt(nsINode *aNode, nsNodeInfoManager *aNewNodeInfoManager,
|
||||
JS::Handle<JSObject*> aReparentScope,
|
||||
nsCOMArray<nsINode> &aNodesWithProperties)
|
||||
static void Adopt(nsINode *aNode, nsNodeInfoManager *aNewNodeInfoManager,
|
||||
JS::Handle<JSObject*> aReparentScope,
|
||||
nsCOMArray<nsINode>& aNodesWithProperties,
|
||||
mozilla::ErrorResult& aError)
|
||||
{
|
||||
nsCOMPtr<nsINode> node;
|
||||
nsresult rv = CloneAndAdopt(aNode, false, true, aNewNodeInfoManager,
|
||||
aReparentScope, aNodesWithProperties,
|
||||
nullptr, getter_AddRefs(node));
|
||||
// Just need to store the return value of CloneAndAdopt in a
|
||||
// temporary nsCOMPtr to make sure we release it.
|
||||
nsCOMPtr<nsINode> node = CloneAndAdopt(aNode, false, true, aNewNodeInfoManager,
|
||||
aReparentScope, &aNodesWithProperties,
|
||||
nullptr, aError);
|
||||
|
||||
nsMutationGuard::DidMutate();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -248,9 +242,12 @@ public:
|
|||
*
|
||||
* @param aNode the node to clone
|
||||
* @param aDeep if true all descendants will be cloned too
|
||||
* @param aResult the clone
|
||||
* @param aError the error, if any.
|
||||
*
|
||||
* @return the clone, or null if an error occurs.
|
||||
*/
|
||||
static nsresult CloneNodeImpl(nsINode *aNode, bool aDeep, nsINode **aResult);
|
||||
static already_AddRefed<nsINode> CloneNodeImpl(nsINode *aNode, bool aDeep,
|
||||
mozilla::ErrorResult& aError);
|
||||
|
||||
/**
|
||||
* Release the UserData for aNode.
|
||||
|
|
@ -286,7 +283,7 @@ private:
|
|||
*
|
||||
* @param aNode Node to adopt/clone.
|
||||
* @param aClone If true the node will be cloned and the cloned node will
|
||||
* be in aResult.
|
||||
* be returned.
|
||||
* @param aDeep If true the function will be called recursively on
|
||||
* descendants of the node
|
||||
* @param aNewNodeInfoManager The nodeinfo manager to use to create new
|
||||
|
|
@ -302,14 +299,18 @@ private:
|
|||
* @param aParent If aClone is true the cloned node will be appended to
|
||||
* aParent's children. May be null. If not null then aNode
|
||||
* must be an nsIContent.
|
||||
* @param aResult If aClone is true then *aResult will contain the cloned
|
||||
* node.
|
||||
* @param aError The error, if any.
|
||||
*
|
||||
* @return If aClone is true then the cloned node will be returned,
|
||||
* unless an error occurred. In error conditions, null
|
||||
* will be returned.
|
||||
*/
|
||||
static nsresult CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
||||
nsNodeInfoManager *aNewNodeInfoManager,
|
||||
JS::Handle<JSObject*> aReparentScope,
|
||||
nsCOMArray<nsINode> &aNodesWithProperties,
|
||||
nsINode *aParent, nsINode **aResult);
|
||||
static already_AddRefed<nsINode>
|
||||
CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
||||
nsNodeInfoManager* aNewNodeInfoManager,
|
||||
JS::Handle<JSObject*> aReparentScope,
|
||||
nsCOMArray<nsINode> *aNodesWithProperties,
|
||||
nsINode *aParent, mozilla::ErrorResult& aError);
|
||||
|
||||
enum class AnimationMutationType
|
||||
{
|
||||
|
|
|
|||
|
|
@ -304,14 +304,6 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// Check for a ShadowRoot because link elements are inert in a
|
||||
// ShadowRoot.
|
||||
ShadowRoot* containingShadow = thisContent->GetContainingShadow();
|
||||
if (thisContent->IsHTMLElement(nsGkAtoms::link) &&
|
||||
(aOldShadowRoot || containingShadow)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// XXXheycam ServoStyleSheets do not support <style scoped>.
|
||||
Element* oldScopeElement = nullptr;
|
||||
if (mStyleSheet) {
|
||||
|
|
@ -346,15 +338,16 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
|
|||
}
|
||||
}
|
||||
|
||||
// When static documents are created, stylesheets are cloned manually.
|
||||
if (mDontLoadStyle || !mUpdatesEnabled ||
|
||||
thisContent->OwnerDoc()->IsStaticDocument()) {
|
||||
nsCOMPtr<nsIDocument> doc = thisContent->IsInShadowTree() ?
|
||||
thisContent->OwnerDoc() : thisContent->GetUncomposedDoc();
|
||||
|
||||
// Loader could be null during unlink, see bug 1425866.
|
||||
if (!doc || !doc->CSSLoader() || !doc->CSSLoader()->GetEnabled()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = thisContent->IsInShadowTree() ?
|
||||
thisContent->OwnerDoc() : thisContent->GetUncomposedDoc();
|
||||
if (!doc || !doc->CSSLoader()->GetEnabled()) {
|
||||
// When static documents are created, stylesheets are cloned manually.
|
||||
if (mDontLoadStyle || !mUpdatesEnabled || doc->IsStaticDocument()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
@ -426,8 +419,7 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
|
|||
rv = doc->CSSLoader()->
|
||||
LoadInlineStyle(thisContent, text, mLineNumber, title, media,
|
||||
scopeElement, aObserver, &doneLoading, &isAlternate, &isExplicitlyEnabled);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
nsAutoString integrity;
|
||||
thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::integrity, integrity);
|
||||
if (!integrity.IsEmpty()) {
|
||||
|
|
|
|||
903
dom/base/test/file_bug1453693.html
Normal file
903
dom/base/test/file_bug1453693.html
Normal file
|
|
@ -0,0 +1,903 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Test for Bug 1453693</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script>
|
||||
|
||||
class TestNode extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
const styles = "<style>:focus{background-color:yellow;}</style>";
|
||||
this.attachShadow({ mode: 'open' });
|
||||
this.shadowRoot.innerHTML =
|
||||
`${styles}<div tabindex='-1'>test node</div> <slot></slot>`;
|
||||
}}
|
||||
|
||||
window.customElements.define('test-node', TestNode);
|
||||
|
||||
var lastFocusTarget;
|
||||
function focusLogger(event) {
|
||||
lastFocusTarget = event.target;
|
||||
console.log(event.target + " under " + event.target.parentNode);
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
function testTabbingThroughShadowDOMWithTabIndexes() {
|
||||
var anchor = document.createElement("a");
|
||||
anchor.onfocus = focusLogger;
|
||||
anchor.href = "#";
|
||||
anchor.textContent = "in light DOM";
|
||||
document.body.appendChild(anchor);
|
||||
|
||||
var host = document.createElement("div");
|
||||
document.body.appendChild(host);
|
||||
|
||||
var sr = host.attachShadow({mode: "open"});
|
||||
var shadowAnchor = anchor.cloneNode(false);
|
||||
shadowAnchor.onfocus = focusLogger;
|
||||
shadowAnchor.textContent = "in shadow DOM";
|
||||
sr.appendChild(shadowAnchor);
|
||||
var shadowInput = document.createElement("input");
|
||||
shadowInput.onfocus = focusLogger;
|
||||
shadowInput.tabIndex = 1;
|
||||
sr.appendChild(shadowInput);
|
||||
|
||||
var shadowDate = document.createElement("input");
|
||||
shadowDate.type = "date";
|
||||
shadowDate.onfocus = focusLogger;
|
||||
shadowDate.tabIndex = 1;
|
||||
sr.appendChild(shadowDate);
|
||||
|
||||
var shadowIframe = document.createElement("iframe");
|
||||
shadowIframe.tabIndex = 1;
|
||||
sr.appendChild(shadowIframe);
|
||||
shadowIframe.contentDocument.body.innerHTML = "<input>";
|
||||
|
||||
var input = document.createElement("input");
|
||||
input.onfocus = focusLogger;
|
||||
input.tabIndex = 1;
|
||||
document.body.appendChild(input);
|
||||
|
||||
var input2 = document.createElement("input");
|
||||
input2.onfocus = focusLogger;
|
||||
document.body.appendChild(input2);
|
||||
|
||||
document.body.offsetLeft;
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input, "Should have focused input element. (3)");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, anchor, "Should have focused anchor element. (3)");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (3)");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (3)");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (3)");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (3)");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(shadowIframe.contentDocument.activeElement,
|
||||
shadowIframe.contentDocument.documentElement,
|
||||
"Should have focused document element in shadow iframe. (3)");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(shadowIframe.contentDocument.activeElement,
|
||||
shadowIframe.contentDocument.body.firstChild,
|
||||
"Should have focused input element in shadow iframe. (3)");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowAnchor, "Should have focused anchor element in shadow DOM. (3)");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input2, "Should have focused input[2] element. (3)");
|
||||
|
||||
// Backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowAnchor, "Should have focused anchor element in shadow DOM. (4)");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(shadowIframe.contentDocument.activeElement,
|
||||
shadowIframe.contentDocument.body.firstChild,
|
||||
"Should have focused input element in shadow iframe. (4)");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(shadowIframe.contentDocument.activeElement,
|
||||
shadowIframe.contentDocument.documentElement,
|
||||
"Should have focused document element in shadow iframe. (4)");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (4)");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (4)");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (4)");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (4)");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, anchor, "Should have focused anchor element. (4)");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input, "Should have focused input element. (4)");
|
||||
|
||||
document.body.innerHTML = null;
|
||||
}
|
||||
|
||||
function testTabbingThroughSimpleShadowDOM() {
|
||||
var anchor = document.createElement("a");
|
||||
anchor.onfocus = focusLogger;
|
||||
anchor.href = "#";
|
||||
anchor.textContent = "in light DOM";
|
||||
document.body.appendChild(anchor);
|
||||
anchor.focus();
|
||||
|
||||
var host = document.createElement("div");
|
||||
document.body.appendChild(host);
|
||||
|
||||
var sr = host.attachShadow({mode: "open"});
|
||||
var shadowAnchor = anchor.cloneNode(false);
|
||||
shadowAnchor.onfocus = focusLogger;
|
||||
shadowAnchor.textContent = "in shadow DOM";
|
||||
sr.appendChild(shadowAnchor);
|
||||
var shadowInput = document.createElement("input");
|
||||
shadowInput.onfocus = focusLogger;
|
||||
sr.appendChild(shadowInput);
|
||||
|
||||
var hiddenShadowButton = document.createElement("button");
|
||||
hiddenShadowButton.setAttribute("style", "display: none;");
|
||||
sr.appendChild(hiddenShadowButton);
|
||||
|
||||
var input = document.createElement("input");
|
||||
input.onfocus = focusLogger;
|
||||
document.body.appendChild(input);
|
||||
|
||||
var input2 = document.createElement("input");
|
||||
input2.onfocus = focusLogger;
|
||||
document.body.appendChild(input2);
|
||||
|
||||
document.body.offsetLeft;
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowAnchor, "Should have focused anchor element in shadow DOM.");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM.");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input, "Should have focused input element.");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input2, "Should have focused input[2] element.");
|
||||
|
||||
// Backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input, "Should have focused input element. (2)");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (2)");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowAnchor, "Should have focused anchor element in shadow DOM. (2)");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, anchor, "Should have focused anchor element. (2)");
|
||||
|
||||
host.remove();
|
||||
input.remove();
|
||||
input2.remove();
|
||||
}
|
||||
|
||||
function testTabbingThroughNestedShadowDOM() {
|
||||
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus. (1)");
|
||||
|
||||
var host = document.createElement("div");
|
||||
host.id = "host";
|
||||
document.body.appendChild(host);
|
||||
|
||||
var sr0 = host.attachShadow({mode: "open"});
|
||||
sr0.innerHTML = "<button id='button'>X</button><br id='br'><div id='h1'></div><div id='h2'></div>";
|
||||
var button = sr0.getElementById("button");
|
||||
button.onfocus = focusLogger;
|
||||
|
||||
var h1 = sr0.getElementById("h1");
|
||||
var sr1 = h1.attachShadow({mode: "open"});
|
||||
sr1.innerHTML = "h1 <input id='h11' placeholder='click me and press tab'><input id='h12' placeholder='and then tab again'>";
|
||||
var input11 = sr1.getElementById("h11");
|
||||
input11.onfocus = focusLogger;
|
||||
var input12 = sr1.getElementById("h12");
|
||||
input12.onfocus = focusLogger;
|
||||
|
||||
var h2 = sr0.getElementById("h2");
|
||||
var sr2 = h2.attachShadow({mode: "open"});
|
||||
sr2.innerHTML = "h2 <input id='h21'><input id='h22'>";
|
||||
var input21 = sr2.getElementById("h21");
|
||||
input21.onfocus = focusLogger;
|
||||
var input22 = sr2.getElementById("h22");
|
||||
input22.onfocus = focusLogger;
|
||||
|
||||
document.body.offsetLeft;
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, button, "[nested shadow] Should have focused button element. (1)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input11, "[nested shadow] Should have focused input element. (1)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input12, "[nested shadow] Should have focused input element. (2)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input21, "[nested shadow] Should have focused input element. (3)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input22, "[nested shadow] Should have focused input element. (4)");
|
||||
|
||||
// Backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input21, "[nested shadow] Should have focused input element. (5)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input12, "[nested shadow] Should have focused input element. (6)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input11, "[nested shadow] Should have focused input element. (7)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, button, "[nested shadow] Should have focused button element. (8)");
|
||||
|
||||
// Back to beginning, outside of Shadow DOM.
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus. (2)");
|
||||
|
||||
host.remove();
|
||||
}
|
||||
|
||||
function testTabbingThroughDisplayContentsHost() {
|
||||
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus. (1)");
|
||||
|
||||
var host = document.createElement("div");
|
||||
host.id = "host";
|
||||
host.setAttribute("style", "display: contents; border: 1px solid black;");
|
||||
document.body.appendChild(host);
|
||||
|
||||
var sr0 = host.attachShadow({mode: "open"});
|
||||
sr0.innerHTML = "<input id='shadowInput1'><input id='shadowInput2'>";
|
||||
var shadowInput1 = sr0.getElementById("shadowInput1");
|
||||
shadowInput1.onfocus = focusLogger;
|
||||
var shadowInput2 = sr0.getElementById("shadowInput2");
|
||||
shadowInput2.onfocus = focusLogger;
|
||||
|
||||
var host1 = document.createElement("div");
|
||||
host1.id = "host";
|
||||
host1.tabIndex = 0;
|
||||
host1.setAttribute("style", "display: contents; border: 1px solid black;");
|
||||
document.body.appendChild(host1);
|
||||
|
||||
var sr1 = host1.attachShadow({mode: "open"});
|
||||
sr1.innerHTML = "<input id='shadowInput1'><input id='shadowInput2'>";
|
||||
var shadowInput3 = sr1.getElementById("shadowInput1");
|
||||
shadowInput3.onfocus = focusLogger;
|
||||
var shadowInput4 = sr1.getElementById("shadowInput2");
|
||||
shadowInput4.onfocus = focusLogger;
|
||||
|
||||
document.body.offsetLeft;
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowInput1, "Should have focused input element. (1)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowInput2, "Should have focused input element. (2)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowInput3, "Should have focused input element. (3)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowInput4, "Should have focused input element. (4)");
|
||||
|
||||
// Backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowInput3, "Should have focused input element. (5)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowInput2, "Should have focused input element. (6)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowInput1, "Should have focused input element. (7)");
|
||||
|
||||
// Back to beginning, outside of Shadow DOM.
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus. (2)");
|
||||
|
||||
host.remove();
|
||||
host1.remove();
|
||||
}
|
||||
|
||||
function testTabbingThroughLightDOMShadowDOMLightDOM() {
|
||||
opener.is(document.activeElement, document.body.firstChild,
|
||||
"body's first child should have focus.");
|
||||
|
||||
var host = document.createElement("span");
|
||||
host.innerHTML = "\n";
|
||||
host.id = "host";
|
||||
document.body.appendChild(host);
|
||||
|
||||
var sr0 = host.attachShadow({mode: "open"});
|
||||
sr0.innerHTML = document.getElementById("template").innerHTML;
|
||||
var p1 = sr0.getElementById("p1");
|
||||
p1.onfocus = focusLogger;
|
||||
var p2 = sr0.getElementById("p2");
|
||||
p2.onfocus = focusLogger;
|
||||
|
||||
var p = document.createElement("p");
|
||||
p.innerHTML = " <a href='#p'>link 1</a> ";
|
||||
var a = p.firstElementChild;
|
||||
a.onfocus = focusLogger;
|
||||
document.body.appendChild(p);
|
||||
|
||||
document.body.offsetLeft;
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, p1, "Should have focused p1.");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, p2, "Should have focused p2.");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, a, "Should have focused a.");
|
||||
|
||||
// Backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, p2, "Should have focused p2.");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, p1, "Should have focused p1.");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, document.body.firstChild,
|
||||
"body's first child should have focus.");
|
||||
|
||||
host.remove();
|
||||
p.remove();
|
||||
}
|
||||
|
||||
function testFocusableHost() {
|
||||
opener.is(document.activeElement, document.body.firstChild,
|
||||
"body's first child should have focus.");
|
||||
|
||||
var host = document.createElement("div");
|
||||
host.id = "host";
|
||||
host.tabIndex = 0;
|
||||
host.onfocus = focusLogger;
|
||||
document.body.appendChild(host);
|
||||
|
||||
var slotted = document.createElement("div");
|
||||
slotted.tabIndex = 0;
|
||||
slotted.onfocus = focusLogger;
|
||||
host.appendChild(slotted);
|
||||
|
||||
var sr0 = host.attachShadow({mode: "open"});
|
||||
sr0.appendChild(document.createElement("slot"));
|
||||
|
||||
var p = document.createElement("p");
|
||||
p.innerHTML = " <a href='#p'>link 1</a> ";
|
||||
var a = p.firstElementChild;
|
||||
a.onfocus = focusLogger;
|
||||
document.body.appendChild(p);
|
||||
|
||||
document.body.offsetLeft;
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, host, "Should have focused host.");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, slotted, "Should have focused slotted.");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, a, "Should have focused a.");
|
||||
|
||||
// Backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, slotted, "Should have focused slotted.");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, host, "Should have focused host.");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, document.body.firstChild,
|
||||
"body's first child should have focus.");
|
||||
|
||||
host.remove();
|
||||
p.remove();
|
||||
}
|
||||
|
||||
function testShiftTabbingThroughFocusableHost() {
|
||||
opener.is(document.activeElement, document.body.firstChild,
|
||||
"body's first child should have focus.");
|
||||
|
||||
var host = document.createElement("div");
|
||||
host.id = "host";
|
||||
host.tabIndex = 0;
|
||||
host.onfocus = focusLogger;
|
||||
document.body.appendChild(host);
|
||||
|
||||
var sr = host.attachShadow({mode: "open"});
|
||||
var shadowButton = document.createElement("button");
|
||||
shadowButton.innerText = "X";
|
||||
shadowButton.onfocus = focusLogger;
|
||||
sr.appendChild(shadowButton);
|
||||
|
||||
var shadowInput = document.createElement("input");
|
||||
shadowInput.onfocus = focusLogger;
|
||||
sr.appendChild(shadowInput);
|
||||
sr.appendChild(document.createElement("br"));
|
||||
|
||||
var input = document.createElement("input");
|
||||
input.onfocus = focusLogger;
|
||||
document.body.appendChild(input);
|
||||
|
||||
document.body.offsetLeft;
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, host, "Should have focused host element. (1)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowButton, "Should have focused button element in shadow DOM. (2)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (3)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input, "Should have focused input element. (4)");
|
||||
|
||||
// Backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (5)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowButton, "Should have focused button element in shadow DOM. (6)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
// focus is already on host
|
||||
opener.is(sr.activeElement, null,
|
||||
"Focus should have left button element in shadow DOM. (7)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, document.body.firstChild,
|
||||
"body's first child should have focus.");
|
||||
|
||||
host.remove();
|
||||
input.remove();
|
||||
}
|
||||
|
||||
function testTabbingThroughNestedSlot() {
|
||||
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
|
||||
|
||||
var host0 = document.createElement("div");
|
||||
var sr0 = host0.attachShadow({mode: "open"});
|
||||
sr0.innerHTML = "<slot></slot>";
|
||||
document.body.appendChild(host0);
|
||||
|
||||
// focusable
|
||||
var host00 = document.createElement("div");
|
||||
var sr00 = host00.attachShadow({mode: "open"});
|
||||
var div00 = document.createElement("div");
|
||||
div00.tabIndex = 0;
|
||||
div00.onfocus = focusLogger;
|
||||
sr00.appendChild(div00);
|
||||
host0.appendChild(host00);
|
||||
|
||||
// not focusable
|
||||
var host01 = document.createElement("div");
|
||||
var sr01 = host01.attachShadow({mode: "open"});
|
||||
sr01.innerHTML = "<div></div>";
|
||||
host0.appendChild(host01);
|
||||
|
||||
// focusable
|
||||
var host02 = document.createElement("div");
|
||||
var sr02 = host02.attachShadow({mode: "open"});
|
||||
var div02 = document.createElement("div");
|
||||
div02.tabIndex = 0;
|
||||
div02.onfocus = focusLogger;
|
||||
sr02.appendChild(div02);
|
||||
host0.appendChild(host02);
|
||||
|
||||
var host1 = document.createElement("div");
|
||||
var sr1 = host1.attachShadow({mode: "open"});
|
||||
sr1.innerHTML = "<slot></slot>";
|
||||
document.body.appendChild(host1);
|
||||
|
||||
var host10 = document.createElement("div");
|
||||
var sr10 = host10.attachShadow({mode: "open"});
|
||||
sr10.innerHTML = "<slot></slot>";
|
||||
host1.appendChild(host10);
|
||||
|
||||
var input10 = document.createElement("input");
|
||||
input10.onfocus = focusLogger;
|
||||
host10.appendChild(input10);
|
||||
|
||||
var host11 = document.createElement("div");
|
||||
var sr11 = host11.attachShadow({mode: "open"});
|
||||
sr11.innerHTML = "<slot></slot>";
|
||||
host1.appendChild(host11);
|
||||
|
||||
var input11 = document.createElement("input");
|
||||
input11.onfocus = focusLogger;
|
||||
host11.appendChild(input11);
|
||||
|
||||
document.body.offsetLeft;
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, div00, "Should have focused div element in shadow DOM. (1)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, div02, "Should have focused div element in shadow DOM. (2)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input10, "Should have focused input element in shadow DOM. (3)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input11, "Should have focused button element in shadow DOM. (4)");
|
||||
|
||||
// Backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input10, "Should have focused input element in shadow DOM. (5)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, div02, "Should have focused input element in shadow DOM. (6)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, div00, "Should have focused input element in shadow DOM. (7)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, document.body.firstChild,
|
||||
"body's first child should have focus.");
|
||||
|
||||
host0.remove();
|
||||
host1.remove();
|
||||
}
|
||||
|
||||
function testTabbingThroughSlotInLightDOM() {
|
||||
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
|
||||
|
||||
var input0 = document.createElement("input");
|
||||
input0.onfocus = focusLogger;
|
||||
document.body.appendChild(input0);
|
||||
|
||||
var slot1 = document.createElement("slot");
|
||||
document.body.appendChild(slot1);
|
||||
|
||||
var input10 = document.createElement("input");
|
||||
input10.onfocus = focusLogger;
|
||||
slot1.appendChild(input10);
|
||||
|
||||
var input11 = document.createElement("input");
|
||||
input11.onfocus = focusLogger;
|
||||
slot1.appendChild(input11);
|
||||
|
||||
var input2 = document.createElement("input");
|
||||
input2.onfocus = focusLogger;
|
||||
document.body.appendChild(input2);
|
||||
|
||||
document.body.offsetLeft;
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input0, "Should have focused input element. (1)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input10, "Should have focused input element in slot. (2)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input11, "Should have focused input element in slot. (3)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input2, "Should have focused input element. (4)");
|
||||
|
||||
// Backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input11, "Should have focused input element in slot. (5)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input10, "Should have focused input element in slot. (6)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input0, "Should have focused input element. (7)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, document.body.firstChild,
|
||||
"body's first child should have focus.");
|
||||
|
||||
input0.remove();
|
||||
slot1.remove();
|
||||
input2.remove();
|
||||
}
|
||||
|
||||
function testTabbingThroughFocusableSlotInLightDOM() {
|
||||
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
|
||||
|
||||
var slot0 = document.createElement("slot");
|
||||
slot0.tabIndex = 0;
|
||||
slot0.setAttribute("style", "display: inline;");
|
||||
slot0.onfocus = focusLogger;
|
||||
document.body.appendChild(slot0);
|
||||
|
||||
var slot00 = document.createElement("slot");
|
||||
slot00.tabIndex = 0;
|
||||
slot00.setAttribute("style", "display: inline;");
|
||||
slot00.onfocus = focusLogger;
|
||||
slot0.appendChild(slot00);
|
||||
|
||||
var input000 = document.createElement("input");
|
||||
input000.onfocus = focusLogger;
|
||||
slot00.appendChild(input000);
|
||||
|
||||
var input01 = document.createElement("input");
|
||||
input01.onfocus = focusLogger;
|
||||
slot0.appendChild(input01);
|
||||
|
||||
var input1 = document.createElement("input");
|
||||
input1.onfocus = focusLogger;
|
||||
document.body.appendChild(input1);
|
||||
|
||||
document.body.offsetLeft;
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, slot0, "Should have focused slot element. (1)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, slot00, "Should have focused slot element. (2)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input000, "Should have focused input element in slot. (3)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input01, "Should have focused input element in slot. (4)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input1, "Should have focused input element. (5)");
|
||||
|
||||
// Backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input01, "Should have focused input element in slot. (6)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input000, "Should have focused input element in slot. (7)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, slot00, "Should have focused slot element. (8)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, slot0, "Should have focused slot element. (9)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, document.body.firstChild,
|
||||
"body's first child should have focus.");
|
||||
|
||||
slot0.remove();
|
||||
input1.remove();
|
||||
}
|
||||
|
||||
function testTabbingThroughScrollableShadowDOM() {
|
||||
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
|
||||
|
||||
var host0 = document.createElement("div");
|
||||
host0.setAttribute("style", "height: 50px; overflow: auto;");
|
||||
host0.onfocus = focusLogger;
|
||||
document.body.appendChild(host0);
|
||||
|
||||
var sr0 = host0.attachShadow({mode: "open"});
|
||||
sr0.innerHTML = `
|
||||
<style>
|
||||
div,slot {
|
||||
height: 30px;
|
||||
display: block;
|
||||
overflow: auto;
|
||||
}
|
||||
input {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
|
||||
var input00 = document.createElement("input");
|
||||
input00.setAttribute("style", "background-color: red;");
|
||||
input00.onfocus = focusLogger;
|
||||
sr0.appendChild(input00);
|
||||
|
||||
var container01 = document.createElement("div");
|
||||
container01.onfocus = focusLogger;
|
||||
sr0.appendChild(container01);
|
||||
|
||||
var input010 = document.createElement("input");
|
||||
input010.onfocus = focusLogger;
|
||||
container01.appendChild(input010);
|
||||
|
||||
var input011 = document.createElement("input");
|
||||
input011.onfocus = focusLogger;
|
||||
container01.appendChild(input011);
|
||||
|
||||
var slot02 = document.createElement("slot");
|
||||
slot02.onfocus = focusLogger;
|
||||
sr0.appendChild(slot02);
|
||||
|
||||
var input020 = document.createElement("input");
|
||||
input020.setAttribute("style", "display: block;");
|
||||
input020.onfocus = focusLogger;
|
||||
host0.appendChild(input020);
|
||||
|
||||
var input021 = document.createElement("input");
|
||||
input021.setAttribute("style", "display: block;");
|
||||
input021.onfocus = focusLogger;
|
||||
host0.appendChild(input021);
|
||||
|
||||
var input1 = document.createElement("input");
|
||||
input1.onfocus = focusLogger;
|
||||
document.body.appendChild(input1);
|
||||
|
||||
document.body.offsetLeft;
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, host0, "Should have focused shadow host element. (1)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input00, "Should have focused input element in shadow dom. (2)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, container01, "Should have focused scrollable element in shadow dom. (3)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input010, "Should have focused input element in shadow dom. (4)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input011, "Should have focused input element in shadow dom. (5)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, slot02, "Should have focused slot element in shadow dom. (6)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input020, "Should have focused input element in slot. (7)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input021, "Should have focused input element in slot. (8)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input1, "Should have focused input element in light dom. (9)");
|
||||
|
||||
// Backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input021, "Should have focused input element in slot. (10)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input020, "Should have focused input element in slot. (11)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, slot02, "Should have focused slot element in shadow dom. (12)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input011, "Should have focused input element in shadow dom. (13)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input010, "Should have focused input element in shadow dom. (14)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, container01, "Should have focused scrollable element in shadow dom. (15)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input00, "Should have focused input element in shadow dom. (16)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
// focus is already on host
|
||||
opener.is(sr0.activeElement, null,
|
||||
"Focus should have left input element in shadow DOM. (7)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, document.body.firstChild,
|
||||
"body's first child should have focus.");
|
||||
|
||||
host0.remove();
|
||||
input1.remove();
|
||||
}
|
||||
|
||||
function testDeeplyNestedShadowTree() {
|
||||
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
|
||||
var host1 = document.createElement("test-node");
|
||||
var lastHost = host1;
|
||||
for (var i = 0; i < 20; ++i) {
|
||||
lastHost.appendChild(document.createElement("test-node"));
|
||||
lastHost = lastHost.firstChild;
|
||||
}
|
||||
|
||||
var input = document.createElement("input");
|
||||
document.body.appendChild(host1);
|
||||
document.body.appendChild(input);
|
||||
document.body.offsetLeft;
|
||||
|
||||
// Test shadow tree which doesn't have anything tab-focusable.
|
||||
host1.shadowRoot.querySelector("div").focus();
|
||||
synthesizeKey("KEY_Tab");
|
||||
is(document.activeElement, input, "Should have focused input element.");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
|
||||
|
||||
// Same test but with focusable elements in the tree...
|
||||
var input2 = document.createElement("input");
|
||||
var host2 = host1.firstChild;
|
||||
var host3 = host2.firstChild;
|
||||
host2.insertBefore(input2, host3);
|
||||
var input3 = document.createElement("input");
|
||||
lastHost.appendChild(input3);
|
||||
document.body.offsetLeft;
|
||||
host3.shadowRoot.querySelector("div").focus();
|
||||
synthesizeKey("KEY_Tab");
|
||||
is(document.activeElement, input3, "Should have focused input3 element.");
|
||||
|
||||
// ...and backwards
|
||||
host3.shadowRoot.querySelector("div").focus();
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
is(document.activeElement, input2, "Should have focused input2 element.");
|
||||
|
||||
// Remove elements added to body element.
|
||||
host1.remove();
|
||||
input.remove();
|
||||
|
||||
// Tests expect body.firstChild to have focus.
|
||||
document.body.firstChild.focus();
|
||||
}
|
||||
|
||||
// Bug 1558393
|
||||
function testBackwardsTabbingWithSlotsWithoutFocusableContent() {
|
||||
let first = document.createElement("div");
|
||||
first.tabIndex = 0;
|
||||
let host = document.createElement("div");
|
||||
host.tabIndex = 0;
|
||||
let second = document.createElement("div");
|
||||
second.tabIndex = 0;
|
||||
host.appendChild(document.createTextNode("foo"));
|
||||
host.attachShadow({ mode: "open" }).innerHTML = `<slot></slot>`;
|
||||
|
||||
document.body.appendChild(first);
|
||||
document.body.appendChild(host);
|
||||
document.body.appendChild(second);
|
||||
document.body.offsetLeft;
|
||||
|
||||
first.focus();
|
||||
opener.is(document.activeElement, first, "First light div should have focus");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(document.activeElement, host, "Host should be focused");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(document.activeElement, second, "Second light div should be focused");
|
||||
|
||||
// Now backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, host, "Focus should return to host");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, first, "Focus should return to first light div");
|
||||
|
||||
second.remove();
|
||||
host.remove();
|
||||
first.remove();
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
|
||||
testTabbingThroughShadowDOMWithTabIndexes();
|
||||
testTabbingThroughSimpleShadowDOM();
|
||||
testTabbingThroughNestedShadowDOM();
|
||||
testTabbingThroughDisplayContentsHost();
|
||||
testTabbingThroughLightDOMShadowDOMLightDOM();
|
||||
testFocusableHost();
|
||||
testShiftTabbingThroughFocusableHost();
|
||||
testTabbingThroughNestedSlot();
|
||||
testTabbingThroughSlotInLightDOM();
|
||||
testTabbingThroughFocusableSlotInLightDOM();
|
||||
testTabbingThroughScrollableShadowDOM();
|
||||
testDeeplyNestedShadowTree();
|
||||
testBackwardsTabbingWithSlotsWithoutFocusableContent();
|
||||
|
||||
opener.didRunTests();
|
||||
window.close();
|
||||
}
|
||||
|
||||
function init() {
|
||||
SimpleTest.waitForFocus(runTest);
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
</style>
|
||||
<template id="template">
|
||||
<div style="overflow: hidden">
|
||||
<p tabindex="0" id="p1">component</p>
|
||||
<p tabindex="0" id="p2">/component</p>
|
||||
</div>
|
||||
</template>
|
||||
</head>
|
||||
<body onload="init()">
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -129,6 +129,7 @@ support-files =
|
|||
file_bug1263696_frame_pass.html
|
||||
file_bug1263696_frame_fail.html
|
||||
file_bug1274806.html
|
||||
file_bug1453693.html
|
||||
file_general_document.html
|
||||
file_htmlserializer_1.html
|
||||
file_htmlserializer_1_bodyonly.html
|
||||
|
|
@ -609,6 +610,8 @@ skip-if = toolkit == 'android'
|
|||
[test_bug1308069.html]
|
||||
[test_bug1314032.html]
|
||||
[test_bug1375050.html]
|
||||
[test_bug1453693.html]
|
||||
skip-if = os == "mac" # Different tab focus behavior on mac
|
||||
[test_caretPositionFromPoint.html]
|
||||
[test_change_policy.html]
|
||||
[test_classList.html]
|
||||
|
|
@ -648,6 +651,8 @@ skip-if = toolkit == 'android' #bug 904183
|
|||
[test_fileapi.html]
|
||||
[test_fileapi_slice.html]
|
||||
skip-if = (toolkit == 'android') # Android: Bug 775227
|
||||
[test_focus_shadow_dom_root.html]
|
||||
skip-if = os == "mac" # Different tab focus behavior on mac
|
||||
[test_getAttribute_after_createAttribute.html]
|
||||
[test_getElementById.html]
|
||||
[test_getTranslationNodes.html]
|
||||
|
|
|
|||
32
dom/base/test/test_bug1453693.html
Normal file
32
dom/base/test/test_bug1453693.html
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1453693
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1453693</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 1453693 **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function runTests() {
|
||||
win = window.open("file_bug1453693.html", "", "width=300, height=300");
|
||||
}
|
||||
|
||||
function didRunTests() {
|
||||
setTimeout("SimpleTest.finish()");
|
||||
}
|
||||
|
||||
;
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="SimpleTest.waitForFocus(runTests);">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1453693">Mozilla Bug 1453693</a>
|
||||
</body>
|
||||
</html>
|
||||
36
dom/base/test/test_focus_shadow_dom_root.html
Normal file
36
dom/base/test/test_focus_shadow_dom_root.html
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<!doctype html>
|
||||
<title>Test for bug 1544826</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<div id="host"><a href="#" id="slotted">This is focusable too</a></div>
|
||||
<script>
|
||||
const host = document.getElementById("host");
|
||||
const shadow = host.attachShadow({ mode: "open" });
|
||||
shadow.innerHTML = `
|
||||
<a id="shadow-1" href="#">This is focusable</a>
|
||||
<slot></slot>
|
||||
<a id="shadow-2" href="#">So is this</a>
|
||||
`;
|
||||
document.documentElement.remove();
|
||||
document.appendChild(host);
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.waitForFocus(function() {
|
||||
is(document.documentElement, host, "Host is the document element");
|
||||
host.offsetTop;
|
||||
synthesizeKey("KEY_Tab");
|
||||
is(shadow.activeElement.id, "shadow-1", "First link in Shadow DOM is focused");
|
||||
synthesizeKey("KEY_Tab");
|
||||
is(document.activeElement.id, "slotted", "Slotted link is focused");
|
||||
synthesizeKey("KEY_Tab");
|
||||
is(shadow.activeElement.id, "shadow-2", "Second link in Shadow DOM is focused");
|
||||
|
||||
// Now backwards.
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
is(document.activeElement.id, "slotted", "Backwards: Slotted link is focused");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
is(shadow.activeElement.id, "shadow-1", "Backwards: First slotted link is focused");
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
</script>
|
||||
|
|
@ -2106,16 +2106,19 @@ DictionaryBase::AppendJSONToString(const char16_t* aJSONData,
|
|||
return true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
|
||||
void
|
||||
ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg, ErrorResult& aError)
|
||||
{
|
||||
js::AssertSameCompartment(aCx, aObjArg);
|
||||
|
||||
aError.MightThrowJSException();
|
||||
|
||||
// Check if we're anywhere near the stack limit before we reach the
|
||||
// transplanting code, since it has no good way to handle errors. This uses
|
||||
// the untrusted script limit, which is not strictly necessary since no
|
||||
// actual script should run.
|
||||
JS_CHECK_RECURSION_CONSERVATIVE(aCx, return NS_ERROR_FAILURE);
|
||||
// TODO: Make sure to retain 'onerror' if bug 1342439 lands.
|
||||
JS_CHECK_RECURSION_CONSERVATIVE(aCx, aError.StealExceptionFromJSContext(aCx); return);
|
||||
|
||||
JS::Rooted<JSObject*> aObj(aCx, aObjArg);
|
||||
const DOMJSClass* domClass = GetDOMClass(aObj);
|
||||
|
|
@ -2135,12 +2138,12 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
|
|||
JSCompartment* newCompartment = js::GetObjectCompartment(newParent);
|
||||
if (oldCompartment == newCompartment) {
|
||||
MOZ_ASSERT(oldParent == newParent);
|
||||
return NS_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
nsISupports* native = UnwrapDOMObjectToISupports(aObj);
|
||||
if (!native) {
|
||||
return NS_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
bool isProxy = js::IsProxy(aObj);
|
||||
|
|
@ -2156,12 +2159,14 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
|
|||
|
||||
JS::Handle<JSObject*> proto = (domClass->mGetProto)(aCx);
|
||||
if (!proto) {
|
||||
return NS_ERROR_FAILURE;
|
||||
aError.StealExceptionFromJSContext(aCx);
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> newobj(aCx, JS_CloneObject(aCx, aObj, proto));
|
||||
if (!newobj) {
|
||||
return NS_ERROR_FAILURE;
|
||||
aError.StealExceptionFromJSContext(aCx);
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> propertyHolder(aCx);
|
||||
|
|
@ -2169,11 +2174,13 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
|
|||
if (copyFrom) {
|
||||
propertyHolder = JS_NewObjectWithGivenProto(aCx, nullptr, nullptr);
|
||||
if (!propertyHolder) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
aError.StealExceptionFromJSContext(aCx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!JS_CopyPropertiesFrom(aCx, propertyHolder, copyFrom)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
aError.StealExceptionFromJSContext(aCx);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
propertyHolder = nullptr;
|
||||
|
|
@ -2189,7 +2196,8 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
|
|||
// if expandos are present then the wrapper will already have been preserved
|
||||
// for this native.
|
||||
if (!xpc::XrayUtils::CloneExpandoChain(aCx, newobj, aObj)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
aError.StealExceptionFromJSContext(aCx);
|
||||
return;
|
||||
}
|
||||
|
||||
// We've set up |newobj|, so we make it own the native by setting its reserved
|
||||
|
|
@ -2244,9 +2252,6 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
|
|||
if (htmlobject) {
|
||||
htmlobject->SetupProtoChain(aCx, aObj);
|
||||
}
|
||||
|
||||
// Now we can just return the wrapper
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
GlobalObject::GlobalObject(JSContext* aCx, JSObject* aObject)
|
||||
|
|
|
|||
|
|
@ -2758,8 +2758,8 @@ const nsAString& NonNullHelper(const binding_detail::FakeString& aArg)
|
|||
|
||||
// Reparent the wrapper of aObj to whatever its native now thinks its
|
||||
// parent should be.
|
||||
nsresult
|
||||
ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObj);
|
||||
void
|
||||
ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObj, ErrorResult& aError);
|
||||
|
||||
/**
|
||||
* Used to implement the Symbol.hasInstance property of an interface object.
|
||||
|
|
|
|||
|
|
@ -116,10 +116,9 @@ ContentEventHandler::InitBasic()
|
|||
|
||||
// If text frame which has overflowing selection underline is dirty,
|
||||
// we need to flush the pending reflow here.
|
||||
mPresShell->FlushPendingNotifications(Flush_Layout);
|
||||
|
||||
// Flushing notifications can cause mPresShell to be destroyed (bug 577963).
|
||||
NS_ENSURE_TRUE(!mPresShell->IsDestroying(), NS_ERROR_FAILURE);
|
||||
if (nsIDocument* doc = mPresShell->GetDocument()) {
|
||||
doc->FlushPendingNotifications(Flush_Layout);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
@ -170,11 +169,11 @@ ContentEventHandler::InitRootContent(Selection* aNormalSelection)
|
|||
}
|
||||
|
||||
// See bug 537041 comment 5, the range could have removed node.
|
||||
if (NS_WARN_IF(startNode->GetUncomposedDoc() != mPresShell->GetDocument())) {
|
||||
if (NS_WARN_IF(startNode->GetComposedDoc() != mPresShell->GetDocument())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_ASSERTION(startNode->GetUncomposedDoc() == endNode->GetUncomposedDoc(),
|
||||
NS_ASSERTION(startNode->GetComposedDoc() == endNode->GetComposedDoc(),
|
||||
"firstNormalSelectionRange crosses the document boundary");
|
||||
|
||||
mRootContent = startNode->GetSelectionRootContent(mPresShell);
|
||||
|
|
@ -2882,31 +2881,6 @@ ContentEventHandler::AdjustCollapsedRangeMaybeIntoTextNode(nsRange* aRange)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ContentEventHandler::GetStartFrameAndOffset(const nsRange* aRange,
|
||||
nsIFrame*& aFrame,
|
||||
int32_t& aOffsetInFrame)
|
||||
{
|
||||
MOZ_ASSERT(aRange);
|
||||
|
||||
aFrame = nullptr;
|
||||
aOffsetInFrame = -1;
|
||||
|
||||
nsINode* node = aRange->GetStartParent();
|
||||
if (NS_WARN_IF(!node) ||
|
||||
NS_WARN_IF(!node->IsNodeOfType(nsINode::eCONTENT))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsIContent* content = static_cast<nsIContent*>(node);
|
||||
RefPtr<nsFrameSelection> fs = mPresShell->FrameSelection();
|
||||
aFrame = fs->GetFrameForNodeOffset(content, aRange->StartOffset(),
|
||||
fs->GetHint(), &aOffsetInFrame);
|
||||
if (NS_WARN_IF(!aFrame)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ContentEventHandler::ConvertToRootRelativeOffset(nsIFrame* aFrame,
|
||||
nsRect& aRect)
|
||||
|
|
|
|||
|
|
@ -287,10 +287,6 @@ protected:
|
|||
// If the aRange isn't in text node but next to a text node, this method
|
||||
// modifies it in the text node. Otherwise, not modified.
|
||||
nsresult AdjustCollapsedRangeMaybeIntoTextNode(nsRange* aCollapsedRange);
|
||||
// Find the first frame for the range and get the start offset in it.
|
||||
nsresult GetStartFrameAndOffset(const nsRange* aRange,
|
||||
nsIFrame*& aFrame,
|
||||
int32_t& aOffsetInFrame);
|
||||
// Convert the frame relative offset to be relative to the root frame of the
|
||||
// root presContext (but still measured in appUnits of aFrame's presContext).
|
||||
nsresult ConvertToRootRelativeOffset(nsIFrame* aFrame,
|
||||
|
|
|
|||
|
|
@ -148,6 +148,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Event)
|
|||
tmp->mEvent->mCurrentTarget = nullptr;
|
||||
tmp->mEvent->mOriginalTarget = nullptr;
|
||||
tmp->mEvent->mRelatedTarget = nullptr;
|
||||
tmp->mEvent->mOriginalRelatedTarget = nullptr;
|
||||
switch (tmp->mEvent->mClass) {
|
||||
case eDragEventClass: {
|
||||
WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent();
|
||||
|
|
@ -176,6 +177,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Event)
|
|||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mCurrentTarget)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mOriginalTarget)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mRelatedTarget)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mOriginalRelatedTarget)
|
||||
switch (tmp->mEvent->mClass) {
|
||||
case eDragEventClass: {
|
||||
WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent();
|
||||
|
|
@ -555,6 +557,26 @@ Event::SetEventType(const nsAString& aEventTypeArg)
|
|||
mEvent->SetDefaultComposedInNativeAnonymousContent();
|
||||
}
|
||||
|
||||
already_AddRefed<EventTarget>
|
||||
Event::EnsureWebAccessibleRelatedTarget(EventTarget* aRelatedTarget)
|
||||
{
|
||||
nsCOMPtr<EventTarget> relatedTarget = aRelatedTarget;
|
||||
if (relatedTarget) {
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(relatedTarget);
|
||||
|
||||
if (content && content->ChromeOnlyAccess() &&
|
||||
!nsContentUtils::CanAccessNativeAnon()) {
|
||||
content = content->FindFirstNonChromeOnlyAccessContent();
|
||||
relatedTarget = do_QueryInterface(content);
|
||||
}
|
||||
|
||||
if (relatedTarget) {
|
||||
relatedTarget = relatedTarget->GetTargetForDOMEvent();
|
||||
}
|
||||
}
|
||||
return relatedTarget.forget();
|
||||
}
|
||||
|
||||
void
|
||||
Event::InitEvent(const nsAString& aEventTypeArg,
|
||||
bool aCanBubbleArg,
|
||||
|
|
@ -1227,44 +1249,6 @@ Event::SetOwner(EventTarget* aOwner)
|
|||
#endif
|
||||
}
|
||||
|
||||
// static
|
||||
nsIContent*
|
||||
Event::GetShadowRelatedTarget(nsIContent* aCurrentTarget,
|
||||
nsIContent* aRelatedTarget)
|
||||
{
|
||||
if (!aCurrentTarget || !aRelatedTarget) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Walk up the ancestor node trees of the related target until
|
||||
// we encounter the node tree of the current target in order
|
||||
// to find the adjusted related target. Walking up the tree may
|
||||
// not find a common ancestor node tree if the related target is in
|
||||
// an ancestor tree, but in that case it does not need to be adjusted.
|
||||
ShadowRoot* currentTargetShadow = aCurrentTarget->GetContainingShadow();
|
||||
if (!currentTargetShadow) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIContent* relatedTarget = aCurrentTarget;
|
||||
while (relatedTarget) {
|
||||
ShadowRoot* ancestorShadow = relatedTarget->GetContainingShadow();
|
||||
if (currentTargetShadow == ancestorShadow) {
|
||||
return relatedTarget;
|
||||
}
|
||||
|
||||
// Didn't find the ancestor tree, thus related target does not have to
|
||||
// adjusted.
|
||||
if (!ancestorShadow) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
relatedTarget = ancestorShadow->GetHost();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Event::GetCancelBubble(bool* aCancelBubble)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -251,14 +251,6 @@ public:
|
|||
return mIsMainThreadEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given current target, returns the related target adjusted with
|
||||
* shadow DOM retargeting rules. Returns nullptr if related target
|
||||
* is not adjusted.
|
||||
*/
|
||||
static nsIContent* GetShadowRelatedTarget(nsIContent* aCurrentTarget,
|
||||
nsIContent* aRelatedTarget);
|
||||
|
||||
void MarkUninitialized()
|
||||
{
|
||||
mEvent->mMessage = eVoidEvent;
|
||||
|
|
@ -295,6 +287,9 @@ protected:
|
|||
mEvent->SetComposed(aComposed);
|
||||
}
|
||||
|
||||
already_AddRefed<EventTarget>
|
||||
EnsureWebAccessibleRelatedTarget(EventTarget* aRelatedTarget);
|
||||
|
||||
mozilla::WidgetEvent* mEvent;
|
||||
RefPtr<nsPresContext> mPresContext;
|
||||
nsCOMPtr<EventTarget> mExplicitOriginalTarget;
|
||||
|
|
|
|||
|
|
@ -204,6 +204,16 @@ public:
|
|||
mNewTarget = aNewTarget;
|
||||
}
|
||||
|
||||
EventTarget* GetRetargetedRelatedTarget()
|
||||
{
|
||||
return mRetargetedRelatedTarget;
|
||||
}
|
||||
|
||||
void SetRetargetedRelatedTarget(EventTarget* aTarget)
|
||||
{
|
||||
mRetargetedRelatedTarget = aTarget;
|
||||
}
|
||||
|
||||
void SetForceContentDispatch(bool aForce)
|
||||
{
|
||||
mFlags.mForceContentDispatch = aForce;
|
||||
|
|
@ -354,6 +364,7 @@ public:
|
|||
|
||||
private:
|
||||
nsCOMPtr<EventTarget> mTarget;
|
||||
nsCOMPtr<EventTarget> mRetargetedRelatedTarget;
|
||||
|
||||
class EventTargetChainFlags
|
||||
{
|
||||
|
|
@ -422,6 +433,7 @@ EventTargetChainItem::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
|||
SetWantsPreHandleEvent(aVisitor.mWantsPreHandleEvent);
|
||||
SetPreHandleEventOnly(aVisitor.mWantsPreHandleEvent && !aVisitor.mCanHandle);
|
||||
SetRootOfClosedTree(aVisitor.mRootOfClosedTree);
|
||||
SetRetargetedRelatedTarget(aVisitor.mRetargetedRelatedTarget);
|
||||
mItemFlags = aVisitor.mItemFlags;
|
||||
mItemData = aVisitor.mItemData;
|
||||
}
|
||||
|
|
@ -454,6 +466,7 @@ EventTargetChainItem::HandleEventTargetChain(
|
|||
{
|
||||
// Save the target so that it can be restored later.
|
||||
nsCOMPtr<EventTarget> firstTarget = aVisitor.mEvent->mTarget;
|
||||
nsCOMPtr<EventTarget> firstRelatedTarget = aVisitor.mEvent->mRelatedTarget;
|
||||
uint32_t chainLength = aChain.Length();
|
||||
uint32_t firstCanHandleEventTargetIdx =
|
||||
EventTargetChainItem::GetFirstCanHandleEventTargetIdx(aChain);
|
||||
|
|
@ -483,6 +496,30 @@ EventTargetChainItem::HandleEventTargetChain(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dispatching-events
|
||||
// Step 14.2
|
||||
// "Set event's relatedTarget to tuple's relatedTarget."
|
||||
// Note, the initial retargeting was done already when creating
|
||||
// event target chain, so we need to do this only after calling
|
||||
// HandleEvent, not before, like in the specification.
|
||||
if (item.GetRetargetedRelatedTarget()) {
|
||||
bool found = false;
|
||||
for (uint32_t j = i; j > 0; --j) {
|
||||
uint32_t childIndex = j - 1;
|
||||
EventTarget* relatedTarget =
|
||||
aChain[childIndex].GetRetargetedRelatedTarget();
|
||||
if (relatedTarget) {
|
||||
found = true;
|
||||
aVisitor.mEvent->mRelatedTarget = relatedTarget;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
aVisitor.mEvent->mRelatedTarget =
|
||||
aVisitor.mEvent->mOriginalRelatedTarget;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Target
|
||||
|
|
@ -511,6 +548,14 @@ EventTargetChainItem::HandleEventTargetChain(
|
|||
aVisitor.mEvent->mTarget = newTarget;
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dispatching-events
|
||||
// Step 15.2
|
||||
// "Set event's relatedTarget to tuple's relatedTarget."
|
||||
EventTarget* relatedTarget = item.GetRetargetedRelatedTarget();
|
||||
if (relatedTarget) {
|
||||
aVisitor.mEvent->mRelatedTarget = relatedTarget;
|
||||
}
|
||||
|
||||
if (aVisitor.mEvent->mFlags.mBubbles || newTarget) {
|
||||
if ((!aVisitor.mEvent->mFlags.mNoContentDispatch ||
|
||||
item.ForceContentDispatch()) &&
|
||||
|
|
@ -542,6 +587,7 @@ EventTargetChainItem::HandleEventTargetChain(
|
|||
// Retarget for system event group (which does the default handling too).
|
||||
// Setting back the target which was used also for default event group.
|
||||
aVisitor.mEvent->mTarget = firstTarget;
|
||||
aVisitor.mEvent->mRelatedTarget = aVisitor.mEvent->mOriginalRelatedTarget;
|
||||
aVisitor.mEvent->mFlags.mInSystemGroup = true;
|
||||
HandleEventTargetChain(aChain,
|
||||
aVisitor,
|
||||
|
|
@ -599,6 +645,7 @@ MayRetargetToChromeIfCanNotHandleEvent(
|
|||
EventTargetChainItem::DestroyLast(aChain, aTargetEtci);
|
||||
}
|
||||
if (aPreVisitor.mAutomaticChromeDispatch && aContent) {
|
||||
aPreVisitor.mRelatedTargetRetargetedInCurrentScope = false;
|
||||
// Event target couldn't handle the event. Try to propagate to chrome.
|
||||
EventTargetChainItem* chromeTargetEtci =
|
||||
EventTargetChainItemForChromeTarget(aChain, aContent, aChildEtci);
|
||||
|
|
@ -762,9 +809,10 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
|
|||
aEvent->mOriginalTarget = aEvent->mTarget;
|
||||
}
|
||||
|
||||
aEvent->mOriginalRelatedTarget = aEvent->mRelatedTarget;
|
||||
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(aEvent->mOriginalTarget);
|
||||
bool isInAnon = (content && (content->IsInAnonymousSubtree() ||
|
||||
content->IsInShadowTree()));
|
||||
bool isInAnon = (content && content->IsInAnonymousSubtree());
|
||||
|
||||
aEvent->mFlags.mIsBeingDispatched = true;
|
||||
|
||||
|
|
@ -772,7 +820,7 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
|
|||
// GetEventTargetParent for the original target.
|
||||
nsEventStatus status = aEventStatus ? *aEventStatus : nsEventStatus_eIgnore;
|
||||
EventChainPreVisitor preVisitor(aPresContext, aEvent, aDOMEvent, status,
|
||||
isInAnon);
|
||||
isInAnon, aEvent->mTarget);
|
||||
targetEtci->GetEventTargetParent(preVisitor);
|
||||
|
||||
if (!preVisitor.mCanHandle) {
|
||||
|
|
@ -810,14 +858,21 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
|
|||
if (preVisitor.mEventTargetAtParent) {
|
||||
// Need to set the target of the event
|
||||
// so that also the next retargeting works.
|
||||
preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
|
||||
preVisitor.mEvent->mTarget = preVisitor.mEventTargetAtParent;
|
||||
parentEtci->SetNewTarget(preVisitor.mEventTargetAtParent);
|
||||
}
|
||||
|
||||
if (preVisitor.mRetargetedRelatedTarget) {
|
||||
preVisitor.mEvent->mRelatedTarget = preVisitor.mRetargetedRelatedTarget;
|
||||
}
|
||||
|
||||
parentEtci->GetEventTargetParent(preVisitor);
|
||||
if (preVisitor.mCanHandle) {
|
||||
preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
|
||||
topEtci = parentEtci;
|
||||
} else {
|
||||
bool ignoreBecauseOfShadowDOM = preVisitor.mIgnoreBecauseOfShadowDOM;
|
||||
nsCOMPtr<nsINode> disabledTarget = do_QueryInterface(parentTarget);
|
||||
parentEtci = MayRetargetToChromeIfCanNotHandleEvent(chain,
|
||||
preVisitor,
|
||||
|
|
@ -825,9 +880,14 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
|
|||
topEtci,
|
||||
disabledTarget);
|
||||
if (parentEtci && preVisitor.mCanHandle) {
|
||||
preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
|
||||
EventTargetChainItem* item =
|
||||
EventTargetChainItem::GetFirstCanHandleEventTarget(chain);
|
||||
item->SetNewTarget(parentTarget);
|
||||
if (!ignoreBecauseOfShadowDOM) {
|
||||
// If we ignored the target because of Shadow DOM retargeting, we
|
||||
// shouldn't treat the target to be in the event path at all.
|
||||
item->SetNewTarget(parentTarget);
|
||||
}
|
||||
topEtci = parentEtci;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -871,6 +931,19 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
|
|||
aEvent->mFlags.mIsBeingDispatched = false;
|
||||
aEvent->mFlags.mDispatchedAtLeastOnce = true;
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-event-dispatch
|
||||
// Step 18
|
||||
// "If target's root is a shadow root, then set event's target attribute and
|
||||
// event's relatedTarget to null."
|
||||
nsCOMPtr<nsIContent> finalTarget = do_QueryInterface(aEvent->mTarget);
|
||||
// TODO: replace with IsShadowRoot() check once bug 1427511 lands.
|
||||
if (finalTarget && ShadowRoot::FromNode(finalTarget->SubtreeRoot())) {
|
||||
aEvent->mTarget = nullptr;
|
||||
aEvent->mOriginalTarget = nullptr;
|
||||
aEvent->mRelatedTarget = nullptr;
|
||||
aEvent->mOriginalRelatedTarget = nullptr;
|
||||
}
|
||||
|
||||
if (!externalDOMEvent && preVisitor.mDOMEvent) {
|
||||
// An dom::Event was created while dispatching the event.
|
||||
// Duplicate private data if someone holds a pointer to it.
|
||||
|
|
|
|||
|
|
@ -113,7 +113,8 @@ public:
|
|||
WidgetEvent* aEvent,
|
||||
nsIDOMEvent* aDOMEvent,
|
||||
nsEventStatus aEventStatus,
|
||||
bool aIsInAnon)
|
||||
bool aIsInAnon,
|
||||
dom::EventTarget* aTargetInKnownToBeHandledScope)
|
||||
: EventChainVisitor(aPresContext, aEvent, aDOMEvent, aEventStatus)
|
||||
, mCanHandle(true)
|
||||
, mAutomaticChromeDispatch(true)
|
||||
|
|
@ -126,8 +127,12 @@ public:
|
|||
, mRootOfClosedTree(false)
|
||||
, mParentIsSlotInClosedTree(false)
|
||||
, mParentIsChromeHandler(false)
|
||||
, mRelatedTargetRetargetedInCurrentScope(false)
|
||||
, mIgnoreBecauseOfShadowDOM(false)
|
||||
, mParentTarget(nullptr)
|
||||
, mEventTargetAtParent(nullptr)
|
||||
, mRetargetedRelatedTarget(nullptr)
|
||||
, mTargetInKnownToBeHandledScope(aTargetInKnownToBeHandledScope)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -144,8 +149,13 @@ public:
|
|||
mRootOfClosedTree = false;
|
||||
mParentIsSlotInClosedTree = false;
|
||||
mParentIsChromeHandler = false;
|
||||
// Note, we don't clear mRelatedTargetRetargetedInCurrentScope explicitly,
|
||||
// since it is used during event path creation to indicate whether
|
||||
// relatedTarget may need to be retargeted.
|
||||
mIgnoreBecauseOfShadowDOM = false;
|
||||
mParentTarget = nullptr;
|
||||
mEventTargetAtParent = nullptr;
|
||||
mRetargetedRelatedTarget = nullptr;
|
||||
}
|
||||
|
||||
dom::EventTarget* GetParentTarget()
|
||||
|
|
@ -161,6 +171,14 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void IgnoreCurrentTargetBecauseOfShadowDOMRetargeting()
|
||||
{
|
||||
mCanHandle = false;
|
||||
mIgnoreBecauseOfShadowDOM = true;
|
||||
SetParentTarget(nullptr, false);
|
||||
mEventTargetAtParent = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Member that must be set in GetEventTargetParent by event targets. If set to
|
||||
* false, indicates that this event target will not be handling the event and
|
||||
|
|
@ -229,6 +247,19 @@ public:
|
|||
*/
|
||||
bool mParentIsChromeHandler;
|
||||
|
||||
/**
|
||||
* True if event's related target has been already retargeted in the
|
||||
* current 'scope'. This should be set to false initially and whenever
|
||||
* event path creation crosses shadow boundary.
|
||||
*/
|
||||
bool mRelatedTargetRetargetedInCurrentScope;
|
||||
|
||||
/**
|
||||
* True if Shadow DOM relatedTarget retargeting causes the current item
|
||||
* to not show up in the event path.
|
||||
*/
|
||||
bool mIgnoreBecauseOfShadowDOM;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Parent item in the event target chain.
|
||||
|
|
@ -241,6 +272,19 @@ public:
|
|||
* which should be used when the event is handled at mParentTarget.
|
||||
*/
|
||||
dom::EventTarget* mEventTargetAtParent;
|
||||
|
||||
/**
|
||||
* If the related target of the event needs to be retargeted, set this
|
||||
* to a new EventTarget.
|
||||
*/
|
||||
dom::EventTarget* mRetargetedRelatedTarget;
|
||||
|
||||
/**
|
||||
* Set to the value of mEvent->mTarget of the previous scope in case of
|
||||
* Shadow DOM or such, and if there is no anonymous content this just points
|
||||
* to the initial target.
|
||||
*/
|
||||
dom::EventTarget* mTargetInKnownToBeHandledScope;
|
||||
};
|
||||
|
||||
class EventChainPostVisitor : public mozilla::EventChainVisitor
|
||||
|
|
|
|||
|
|
@ -30,14 +30,14 @@ NS_IMETHODIMP
|
|||
FocusEvent::GetRelatedTarget(nsIDOMEventTarget** aRelatedTarget)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aRelatedTarget);
|
||||
NS_IF_ADDREF(*aRelatedTarget = GetRelatedTarget());
|
||||
*aRelatedTarget = GetRelatedTarget().take();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
EventTarget*
|
||||
already_AddRefed<EventTarget>
|
||||
FocusEvent::GetRelatedTarget()
|
||||
{
|
||||
return mEvent->AsFocusEvent()->mRelatedTarget;
|
||||
return EnsureWebAccessibleRelatedTarget(mEvent->AsFocusEvent()->mRelatedTarget);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ public:
|
|||
nsPresContext* aPresContext,
|
||||
InternalFocusEvent* aEvent);
|
||||
|
||||
EventTarget* GetRelatedTarget();
|
||||
already_AddRefed<EventTarget> GetRelatedTarget();
|
||||
|
||||
static already_AddRefed<FocusEvent> Constructor(const GlobalObject& aGlobal,
|
||||
const nsAString& aType,
|
||||
|
|
|
|||
|
|
@ -300,27 +300,7 @@ MouseEvent::GetRelatedTarget()
|
|||
break;
|
||||
}
|
||||
|
||||
if (relatedTarget) {
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(relatedTarget);
|
||||
nsCOMPtr<nsIContent> currentTarget =
|
||||
do_QueryInterface(mEvent->mCurrentTarget);
|
||||
|
||||
nsIContent* shadowRelatedTarget = GetShadowRelatedTarget(currentTarget, content);
|
||||
if (shadowRelatedTarget) {
|
||||
relatedTarget = shadowRelatedTarget;
|
||||
}
|
||||
|
||||
if (content && content->ChromeOnlyAccess() &&
|
||||
!nsContentUtils::CanAccessNativeAnon()) {
|
||||
relatedTarget = do_QueryInterface(content->FindFirstNonChromeOnlyAccessContent());
|
||||
}
|
||||
|
||||
if (relatedTarget) {
|
||||
relatedTarget = relatedTarget->GetTargetForDOMEvent();
|
||||
}
|
||||
return relatedTarget.forget();
|
||||
}
|
||||
return nullptr;
|
||||
return EnsureWebAccessibleRelatedTarget(relatedTarget);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -623,8 +623,9 @@ HTMLImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
|||
}
|
||||
|
||||
if (HaveSrcsetOrInPicture()) {
|
||||
if (aDocument && !mInDocResponsiveContent) {
|
||||
aDocument->AddResponsiveContent(this);
|
||||
nsIDocument* doc = GetComposedDoc();
|
||||
if (doc && !mInDocResponsiveContent) {
|
||||
doc->AddResponsiveContent(this);
|
||||
mInDocResponsiveContent = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -162,7 +162,8 @@ HTMLLinkElement::HasDeferredDNSPrefetchRequest()
|
|||
}
|
||||
|
||||
nsresult
|
||||
HTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
HTMLLinkElement::BindToTree(nsIDocument* aDocument,
|
||||
nsIContent* aParent,
|
||||
nsIContent* aBindingParent,
|
||||
bool aCompileEventHandlers)
|
||||
{
|
||||
|
|
@ -173,12 +174,8 @@ HTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
|||
aCompileEventHandlers);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Link must be inert in ShadowRoot.
|
||||
if (aDocument && !GetContainingShadow()) {
|
||||
aDocument->RegisterPendingLinkUpdate(this);
|
||||
}
|
||||
|
||||
if (IsInComposedDoc()) {
|
||||
if (nsIDocument* doc = GetComposedDoc()) {
|
||||
doc->RegisterPendingLinkUpdate(this);
|
||||
TryDNSPrefetchPreconnectOrPrefetch();
|
||||
}
|
||||
|
||||
|
|
@ -222,11 +219,7 @@ HTMLLinkElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
|||
// If this is reinserted back into the document it will not be
|
||||
// from the parser.
|
||||
nsCOMPtr<nsIDocument> oldDoc = GetUncomposedDoc();
|
||||
|
||||
// Check for a ShadowRoot because link elements are inert in a
|
||||
// ShadowRoot.
|
||||
ShadowRoot* oldShadowRoot = GetBindingParent() ?
|
||||
GetBindingParent()->GetShadowRoot() : nullptr;
|
||||
ShadowRoot* oldShadowRoot = GetContainingShadow();
|
||||
|
||||
OwnerDoc()->UnregisterPendingLinkUpdate(this);
|
||||
|
||||
|
|
|
|||
|
|
@ -463,7 +463,7 @@ nsGenericHTMLElement::IntrinsicState() const
|
|||
uint32_t
|
||||
nsGenericHTMLElement::EditableInclusiveDescendantCount()
|
||||
{
|
||||
bool isEditable = IsInUncomposedDoc() && HasFlag(NODE_IS_EDITABLE) &&
|
||||
bool isEditable = IsInComposedDoc() && HasFlag(NODE_IS_EDITABLE) &&
|
||||
GetContentEditableValue() == eTrue;
|
||||
return EditableDescendantCount() + isEditable;
|
||||
}
|
||||
|
|
@ -484,12 +484,14 @@ nsGenericHTMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
|||
aDocument->
|
||||
AddToNameTable(this, GetParsedAttr(nsGkAtoms::name)->GetAtomValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (HasFlag(NODE_IS_EDITABLE) && GetContentEditableValue() == eTrue) {
|
||||
nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(aDocument);
|
||||
if (htmlDocument) {
|
||||
htmlDocument->ChangeContentEditableCount(this, +1);
|
||||
}
|
||||
if (HasFlag(NODE_IS_EDITABLE) && GetContentEditableValue() == eTrue &&
|
||||
IsInComposedDoc()) {
|
||||
nsCOMPtr<nsIHTMLDocument> htmlDocument =
|
||||
do_QueryInterface(GetComposedDoc());
|
||||
if (htmlDocument) {
|
||||
htmlDocument->ChangeContentEditableCount(this, +1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -514,8 +516,7 @@ nsGenericHTMLElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
|||
RemoveFromNameTable();
|
||||
|
||||
if (GetContentEditableValue() == eTrue) {
|
||||
//XXXsmaug Fix this for Shadow DOM, bug 1066965.
|
||||
nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(GetUncomposedDoc());
|
||||
nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(GetComposedDoc());
|
||||
if (htmlDocument) {
|
||||
htmlDocument->ChangeContentEditableCount(this, -1);
|
||||
}
|
||||
|
|
@ -2774,8 +2775,7 @@ MakeContentDescendantsEditable(nsIContent *aContent, nsIDocument *aDocument)
|
|||
void
|
||||
nsGenericHTMLElement::ChangeEditableState(int32_t aChange)
|
||||
{
|
||||
//XXXsmaug Fix this for Shadow DOM, bug 1066965.
|
||||
nsIDocument* document = GetUncomposedDoc();
|
||||
nsIDocument* document = GetComposedDoc();
|
||||
if (!document) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -2788,7 +2788,8 @@ nsGenericHTMLElement::ChangeEditableState(int32_t aChange)
|
|||
}
|
||||
|
||||
nsIContent* parent = GetParent();
|
||||
while (parent) {
|
||||
// Don't update across Shadow DOM boundary.
|
||||
while (parent && parent->IsElement()) {
|
||||
parent->ChangeEditableDescendantCount(aChange);
|
||||
parent = parent->GetParent();
|
||||
}
|
||||
|
|
|
|||
52
dom/html/test/file_fullscreen-shadowdom.html
Normal file
52
dom/html/test/file_fullscreen-shadowdom.html
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1430305
|
||||
Bug 1430305 - Implement ShadowRoot.fullscreenElement
|
||||
-->
|
||||
<head>
|
||||
<title>Bug 1430305</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js">
|
||||
</script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js">
|
||||
</script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=1430305">
|
||||
Mozilla Bug 1430305</a>
|
||||
|
||||
<div id="host"></div>
|
||||
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
function begin() {
|
||||
var host = document.getElementById("host");
|
||||
var shadowRoot = host.attachShadow({mode: "open"});
|
||||
shadowRoot.innerHTML = "<div>test</div>";
|
||||
var elem = shadowRoot.firstChild;
|
||||
var gotFullscreenEvent = false;
|
||||
|
||||
document.addEventListener("fullscreenchange", function (e) {
|
||||
if (document.fullscreenElement === host) {
|
||||
is(shadowRoot.fullscreenElement, elem,
|
||||
"Expected element entered fullsceen");
|
||||
gotFullscreenEvent = true;
|
||||
document.exitFullscreen();
|
||||
} else {
|
||||
opener.ok(gotFullscreenEvent, "Entered fullscreen as expected");
|
||||
is(shadowRoot.fullscreenElement, null,
|
||||
"Shouldn't have fullscreenElement anymore.");
|
||||
is(document.fullscreenElement, null,
|
||||
"Shouldn't have fullscreenElement anymore.");
|
||||
opener.nextTest();
|
||||
}
|
||||
});
|
||||
elem.requestFullscreen();
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -72,6 +72,7 @@ support-files =
|
|||
file_fullscreen-rollback.html
|
||||
file_fullscreen-scrollbar.html
|
||||
file_fullscreen-selector.html
|
||||
file_fullscreen-shadowdom.html
|
||||
file_fullscreen-svg-element.html
|
||||
file_fullscreen-top-layer.html
|
||||
file_fullscreen-unprefix-disabled-inner.html
|
||||
|
|
@ -444,6 +445,7 @@ support-files =
|
|||
[test_bug1146116.html]
|
||||
[test_bug1264157.html]
|
||||
[test_bug1287321.html]
|
||||
[test_bug1323815.html]
|
||||
[test_change_crossorigin.html]
|
||||
[test_checked.html]
|
||||
[test_dir_attributes_reflection.html]
|
||||
|
|
|
|||
50
dom/html/test/test_bug1323815.html
Normal file
50
dom/html/test/test_bug1323815.html
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1323815
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1323815</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 1323815 **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
function test() {
|
||||
var n = document.getElementById("number");
|
||||
var t = document.getElementById("text");
|
||||
t.focus();
|
||||
var gotBlur = false;
|
||||
t.onblur = function(e) {
|
||||
try {
|
||||
is(e.relatedTarget.localName, "input");
|
||||
} catch(ex) {
|
||||
ok(false, "Accessing properties on the relatedTarget shouldn't throw! " + ex);
|
||||
}
|
||||
gotBlur = true;
|
||||
}
|
||||
|
||||
n.focus();
|
||||
ok(gotBlur);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForFocus(test);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1323815">Mozilla Bug 1323815</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<input type="number" id="number"><input type="text" id="text">
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -39,6 +39,7 @@ var gTestWindows = [
|
|||
"file_fullscreen-navigation.html",
|
||||
"file_fullscreen-scrollbar.html",
|
||||
"file_fullscreen-selector.html",
|
||||
"file_fullscreen-shadowdom.html",
|
||||
"file_fullscreen-top-layer.html",
|
||||
"file_fullscreen-backdrop.html",
|
||||
"file_fullscreen-nested.html",
|
||||
|
|
@ -96,7 +97,8 @@ addLoadEvent(function() {
|
|||
["full-screen-api.unprefix.enabled", true],
|
||||
["full-screen-api.allow-trusted-requests-only", false],
|
||||
["full-screen-api.transition-duration.enter", "0 0"],
|
||||
["full-screen-api.transition-duration.leave", "0 0"]
|
||||
["full-screen-api.transition-duration.leave", "0 0"],
|
||||
["dom.webcomponents.enabled", true]
|
||||
]}, nextTest);
|
||||
});
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ public:
|
|||
MOZ_ASSERT(mLoggingInfo.nextVersionChangeTransactionSerialNumber() >
|
||||
INT64_MIN);
|
||||
|
||||
if (aMode == IDBTransaction::VERSION_CHANGE) {
|
||||
if (aMode == IDBTransaction::Mode::VersionChange) {
|
||||
return mLoggingInfo.nextVersionChangeTransactionSerialNumber()--;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9005,7 +9005,7 @@ public:
|
|||
MOZ_ASSERT(mLoggingInfo.nextVersionChangeTransactionSerialNumber() >
|
||||
INT64_MIN);
|
||||
|
||||
if (aMode == IDBTransaction::VERSION_CHANGE) {
|
||||
if (aMode == IDBTransaction::Mode::VersionChange) {
|
||||
return mLoggingInfo.nextVersionChangeTransactionSerialNumber()--;
|
||||
}
|
||||
|
||||
|
|
@ -11330,11 +11330,11 @@ AutoSavepoint::~AutoSavepoint()
|
|||
if (mConnection) {
|
||||
mConnection->AssertIsOnConnectionThread();
|
||||
MOZ_ASSERT(mDEBUGTransaction);
|
||||
MOZ_ASSERT(mDEBUGTransaction->GetMode() == IDBTransaction::READ_WRITE ||
|
||||
MOZ_ASSERT(mDEBUGTransaction->GetMode() == IDBTransaction::Mode::ReadWrite ||
|
||||
mDEBUGTransaction->GetMode() ==
|
||||
IDBTransaction::READ_WRITE_FLUSH ||
|
||||
mDEBUGTransaction->GetMode() == IDBTransaction::CLEANUP ||
|
||||
mDEBUGTransaction->GetMode() == IDBTransaction::VERSION_CHANGE);
|
||||
IDBTransaction::Mode::ReadWriteFlush ||
|
||||
mDEBUGTransaction->GetMode() == IDBTransaction::Mode::Cleanup ||
|
||||
mDEBUGTransaction->GetMode() == IDBTransaction::Mode::VersionChange);
|
||||
|
||||
if (NS_FAILED(mConnection->RollbackSavepoint())) {
|
||||
NS_WARNING("Failed to rollback savepoint!");
|
||||
|
|
@ -11347,10 +11347,10 @@ DatabaseConnection::
|
|||
AutoSavepoint::Start(const TransactionBase* aTransaction)
|
||||
{
|
||||
MOZ_ASSERT(aTransaction);
|
||||
MOZ_ASSERT(aTransaction->GetMode() == IDBTransaction::READ_WRITE ||
|
||||
aTransaction->GetMode() == IDBTransaction::READ_WRITE_FLUSH ||
|
||||
aTransaction->GetMode() == IDBTransaction::CLEANUP ||
|
||||
aTransaction->GetMode() == IDBTransaction::VERSION_CHANGE);
|
||||
MOZ_ASSERT(aTransaction->GetMode() == IDBTransaction::Mode::ReadWrite ||
|
||||
aTransaction->GetMode() == IDBTransaction::Mode::ReadWriteFlush ||
|
||||
aTransaction->GetMode() == IDBTransaction::Mode::Cleanup ||
|
||||
aTransaction->GetMode() == IDBTransaction::Mode::VersionChange);
|
||||
|
||||
DatabaseConnection* connection = aTransaction->GetDatabase()->GetConnection();
|
||||
MOZ_ASSERT(connection);
|
||||
|
|
@ -14478,19 +14478,19 @@ Database::AllocPBackgroundIDBTransactionParent(
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(aMode != IDBTransaction::READ_ONLY &&
|
||||
aMode != IDBTransaction::READ_WRITE &&
|
||||
aMode != IDBTransaction::READ_WRITE_FLUSH &&
|
||||
aMode != IDBTransaction::CLEANUP)) {
|
||||
if (NS_WARN_IF(aMode != IDBTransaction::Mode::ReadOnly &&
|
||||
aMode != IDBTransaction::Mode::ReadWrite &&
|
||||
aMode != IDBTransaction::Mode::ReadWriteFlush &&
|
||||
aMode != IDBTransaction::Mode::Cleanup)) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If this is a readwrite transaction to a chrome database make sure the child
|
||||
// has write access.
|
||||
if (NS_WARN_IF((aMode == IDBTransaction::READ_WRITE ||
|
||||
aMode == IDBTransaction::READ_WRITE_FLUSH ||
|
||||
aMode == IDBTransaction::CLEANUP) &&
|
||||
if (NS_WARN_IF((aMode == IDBTransaction::Mode::ReadWrite ||
|
||||
aMode == IDBTransaction::Mode::ReadWriteFlush ||
|
||||
aMode == IDBTransaction::Mode::Cleanup) &&
|
||||
mPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo &&
|
||||
!mChromeWriteAccessAllowed)) {
|
||||
return nullptr;
|
||||
|
|
@ -14553,10 +14553,10 @@ Database::RecvPBackgroundIDBTransactionConstructor(
|
|||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(aActor);
|
||||
MOZ_ASSERT(!aObjectStoreNames.IsEmpty());
|
||||
MOZ_ASSERT(aMode == IDBTransaction::READ_ONLY ||
|
||||
aMode == IDBTransaction::READ_WRITE ||
|
||||
aMode == IDBTransaction::READ_WRITE_FLUSH ||
|
||||
aMode == IDBTransaction::CLEANUP);
|
||||
MOZ_ASSERT(aMode == IDBTransaction::Mode::ReadOnly ||
|
||||
aMode == IDBTransaction::Mode::ReadWrite ||
|
||||
aMode == IDBTransaction::Mode::ReadWriteFlush ||
|
||||
aMode == IDBTransaction::Mode::Cleanup);
|
||||
MOZ_ASSERT(!mClosed);
|
||||
|
||||
if (IsInvalidated()) {
|
||||
|
|
@ -14578,7 +14578,7 @@ Database::RecvPBackgroundIDBTransactionConstructor(
|
|||
mMetadata->mDatabaseId,
|
||||
transaction->LoggingSerialNumber(),
|
||||
aObjectStoreNames,
|
||||
aMode != IDBTransaction::READ_ONLY);
|
||||
aMode != IDBTransaction::Mode::ReadOnly);
|
||||
|
||||
transaction->SetActive(transactionId);
|
||||
|
||||
|
|
@ -14715,14 +14715,14 @@ StartTransactionOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
|||
|
||||
Transaction()->SetActiveOnConnectionThread();
|
||||
|
||||
if (Transaction()->GetMode() == IDBTransaction::CLEANUP) {
|
||||
if (Transaction()->GetMode() == IDBTransaction::Mode::Cleanup) {
|
||||
nsresult rv = aConnection->DisableQuotaChecks();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
if (Transaction()->GetMode() != IDBTransaction::READ_ONLY) {
|
||||
if (Transaction()->GetMode() != IDBTransaction::Mode::ReadOnly) {
|
||||
nsresult rv = aConnection->BeginWriteTransaction();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
|
|
@ -15032,10 +15032,10 @@ TransactionBase::VerifyRequestParams(const RequestParams& aParams) const
|
|||
}
|
||||
|
||||
case RequestParams::TObjectStoreDeleteParams: {
|
||||
if (NS_WARN_IF(mMode != IDBTransaction::READ_WRITE &&
|
||||
mMode != IDBTransaction::READ_WRITE_FLUSH &&
|
||||
mMode != IDBTransaction::CLEANUP &&
|
||||
mMode != IDBTransaction::VERSION_CHANGE)) {
|
||||
if (NS_WARN_IF(mMode != IDBTransaction::Mode::ReadWrite &&
|
||||
mMode != IDBTransaction::Mode::ReadWriteFlush &&
|
||||
mMode != IDBTransaction::Mode::Cleanup &&
|
||||
mMode != IDBTransaction::Mode::VersionChange)) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -15056,10 +15056,10 @@ TransactionBase::VerifyRequestParams(const RequestParams& aParams) const
|
|||
}
|
||||
|
||||
case RequestParams::TObjectStoreClearParams: {
|
||||
if (NS_WARN_IF(mMode != IDBTransaction::READ_WRITE &&
|
||||
mMode != IDBTransaction::READ_WRITE_FLUSH &&
|
||||
mMode != IDBTransaction::CLEANUP &&
|
||||
mMode != IDBTransaction::VERSION_CHANGE)) {
|
||||
if (NS_WARN_IF(mMode != IDBTransaction::Mode::ReadWrite &&
|
||||
mMode != IDBTransaction::Mode::ReadWriteFlush &&
|
||||
mMode != IDBTransaction::Mode::Cleanup &&
|
||||
mMode != IDBTransaction::Mode::VersionChange)) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -15243,9 +15243,9 @@ TransactionBase::VerifyRequestParams(const ObjectStoreAddPutParams& aParams)
|
|||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
if (NS_WARN_IF(mMode != IDBTransaction::READ_WRITE &&
|
||||
mMode != IDBTransaction::READ_WRITE_FLUSH &&
|
||||
mMode != IDBTransaction::VERSION_CHANGE)) {
|
||||
if (NS_WARN_IF(mMode != IDBTransaction::Mode::ReadWrite &&
|
||||
mMode != IDBTransaction::Mode::ReadWriteFlush &&
|
||||
mMode != IDBTransaction::Mode::VersionChange)) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -15864,7 +15864,7 @@ NormalTransaction::DeallocPBackgroundIDBCursorParent(
|
|||
VersionChangeTransaction::VersionChangeTransaction(
|
||||
OpenDatabaseOp* aOpenDatabaseOp)
|
||||
: TransactionBase(aOpenDatabaseOp->mDatabase,
|
||||
IDBTransaction::VERSION_CHANGE)
|
||||
IDBTransaction::Mode::VersionChange)
|
||||
, mOpenDatabaseOp(aOpenDatabaseOp)
|
||||
, mActorWasAlive(false)
|
||||
{
|
||||
|
|
@ -22234,7 +22234,7 @@ OpenDatabaseOp::DispatchToWorkThread()
|
|||
MOZ_ASSERT(mState == State::WaitingForTransactionsToComplete);
|
||||
MOZ_ASSERT(mVersionChangeTransaction);
|
||||
MOZ_ASSERT(mVersionChangeTransaction->GetMode() ==
|
||||
IDBTransaction::VERSION_CHANGE);
|
||||
IDBTransaction::Mode::VersionChange);
|
||||
MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
|
||||
|
||||
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
|
||||
|
|
@ -23739,10 +23739,10 @@ CommitOp::WriteAutoIncrementCounts()
|
|||
{
|
||||
MOZ_ASSERT(mTransaction);
|
||||
mTransaction->AssertIsOnConnectionThread();
|
||||
MOZ_ASSERT(mTransaction->GetMode() == IDBTransaction::READ_WRITE ||
|
||||
mTransaction->GetMode() == IDBTransaction::READ_WRITE_FLUSH ||
|
||||
mTransaction->GetMode() == IDBTransaction::CLEANUP ||
|
||||
mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE);
|
||||
MOZ_ASSERT(mTransaction->GetMode() == IDBTransaction::Mode::ReadWrite ||
|
||||
mTransaction->GetMode() == IDBTransaction::Mode::ReadWriteFlush ||
|
||||
mTransaction->GetMode() == IDBTransaction::Mode::Cleanup ||
|
||||
mTransaction->GetMode() == IDBTransaction::Mode::VersionChange);
|
||||
|
||||
const nsTArray<RefPtr<FullObjectStoreMetadata>>& metadataArray =
|
||||
mTransaction->mModifiedAutoIncrementObjectStoreMetadataArray;
|
||||
|
|
@ -23807,10 +23807,10 @@ CommitOp::CommitOrRollbackAutoIncrementCounts()
|
|||
{
|
||||
MOZ_ASSERT(mTransaction);
|
||||
mTransaction->AssertIsOnConnectionThread();
|
||||
MOZ_ASSERT(mTransaction->GetMode() == IDBTransaction::READ_WRITE ||
|
||||
mTransaction->GetMode() == IDBTransaction::READ_WRITE_FLUSH ||
|
||||
mTransaction->GetMode() == IDBTransaction::CLEANUP ||
|
||||
mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE);
|
||||
MOZ_ASSERT(mTransaction->GetMode() == IDBTransaction::Mode::ReadWrite ||
|
||||
mTransaction->GetMode() == IDBTransaction::Mode::ReadWriteFlush ||
|
||||
mTransaction->GetMode() == IDBTransaction::Mode::Cleanup ||
|
||||
mTransaction->GetMode() == IDBTransaction::Mode::VersionChange);
|
||||
|
||||
nsTArray<RefPtr<FullObjectStoreMetadata>>& metadataArray =
|
||||
mTransaction->mModifiedAutoIncrementObjectStoreMetadataArray;
|
||||
|
|
@ -23841,7 +23841,7 @@ CommitOp::AssertForeignKeyConsistency(DatabaseConnection* aConnection)
|
|||
MOZ_ASSERT(aConnection);
|
||||
MOZ_ASSERT(mTransaction);
|
||||
mTransaction->AssertIsOnConnectionThread();
|
||||
MOZ_ASSERT(mTransaction->GetMode() != IDBTransaction::READ_ONLY);
|
||||
MOZ_ASSERT(mTransaction->GetMode() != IDBTransaction::Mode::ReadOnly);
|
||||
|
||||
DatabaseConnection::CachedStatement pragmaStmt;
|
||||
MOZ_ALWAYS_SUCCEEDS(
|
||||
|
|
@ -23891,7 +23891,7 @@ CommitOp::Run()
|
|||
mTransaction->LoggingSerialNumber(),
|
||||
mLoggingSerialNumber);
|
||||
|
||||
if (mTransaction->GetMode() != IDBTransaction::READ_ONLY &&
|
||||
if (mTransaction->GetMode() != IDBTransaction::Mode::ReadOnly &&
|
||||
mTransaction->mHasBeenActiveOnConnectionThread) {
|
||||
Database* database = mTransaction->GetDatabase();
|
||||
MOZ_ASSERT(database);
|
||||
|
|
@ -23920,7 +23920,7 @@ CommitOp::Run()
|
|||
NS_WARNING_ASSERTION(NS_SUCCEEDED(mResultCode), "Commit failed!");
|
||||
|
||||
if (NS_SUCCEEDED(mResultCode) &&
|
||||
mTransaction->GetMode() == IDBTransaction::READ_WRITE_FLUSH) {
|
||||
mTransaction->GetMode() == IDBTransaction::Mode::ReadWriteFlush) {
|
||||
mResultCode = connection->Checkpoint();
|
||||
}
|
||||
|
||||
|
|
@ -23943,7 +23943,7 @@ CommitOp::Run()
|
|||
|
||||
connection->FinishWriteTransaction();
|
||||
|
||||
if (mTransaction->GetMode() == IDBTransaction::CLEANUP) {
|
||||
if (mTransaction->GetMode() == IDBTransaction::Mode::Cleanup) {
|
||||
connection->DoIdleProcessing(/* aNeedsCheckpoint */ true);
|
||||
|
||||
connection->EnableQuotaChecks();
|
||||
|
|
@ -25789,7 +25789,7 @@ NormalTransactionOp::ObjectStoreHasIndexes(NormalTransactionOp* aOp,
|
|||
MOZ_ASSERT(aHasIndexes);
|
||||
|
||||
bool hasIndexes;
|
||||
if (aOp->Transaction()->GetMode() == IDBTransaction::VERSION_CHANGE &&
|
||||
if (aOp->Transaction()->GetMode() == IDBTransaction::Mode::VersionChange &&
|
||||
aMayHaveIndexes) {
|
||||
// If this is a version change transaction then mObjectStoreMayHaveIndexes
|
||||
// could be wrong (e.g. if a unique index failed to be created due to a
|
||||
|
|
|
|||
|
|
@ -685,7 +685,7 @@ IDBCursor::Update(JSContext* aCx, JS::Handle<JS::Value> aValue,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (mTransaction->GetMode() == IDBTransaction::CLEANUP ||
|
||||
if (mTransaction->GetMode() == IDBTransaction::Mode::Cleanup ||
|
||||
IsSourceDeleted() ||
|
||||
!mHaveValue ||
|
||||
mType == Type_ObjectStoreKey ||
|
||||
|
|
|
|||
|
|
@ -425,7 +425,7 @@ IDBDatabase::CreateObjectStore(
|
|||
IDBTransaction* transaction = IDBTransaction::GetCurrent();
|
||||
if (!transaction ||
|
||||
transaction->Database() != this ||
|
||||
transaction->GetMode() != IDBTransaction::VERSION_CHANGE) {
|
||||
transaction->GetMode() != IDBTransaction::Mode::VersionChange) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -503,7 +503,7 @@ IDBDatabase::DeleteObjectStore(const nsAString& aName, ErrorResult& aRv)
|
|||
IDBTransaction* transaction = IDBTransaction::GetCurrent();
|
||||
if (!transaction ||
|
||||
transaction->Database() != this ||
|
||||
transaction->GetMode() != IDBTransaction::VERSION_CHANGE) {
|
||||
transaction->GetMode() != IDBTransaction::Mode::VersionChange) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
|
||||
return;
|
||||
}
|
||||
|
|
@ -666,21 +666,21 @@ IDBDatabase::Transaction(JSContext* aCx,
|
|||
IDBTransaction::Mode mode;
|
||||
switch (aMode) {
|
||||
case IDBTransactionMode::Readonly:
|
||||
mode = IDBTransaction::READ_ONLY;
|
||||
mode = IDBTransaction::Mode::ReadOnly;
|
||||
break;
|
||||
case IDBTransactionMode::Readwrite:
|
||||
if (mQuotaExceeded) {
|
||||
mode = IDBTransaction::CLEANUP;
|
||||
mode = IDBTransaction::Mode::Cleanup;
|
||||
mQuotaExceeded = false;
|
||||
} else {
|
||||
mode = IDBTransaction::READ_WRITE;
|
||||
mode = IDBTransaction::Mode::ReadWrite;
|
||||
}
|
||||
break;
|
||||
case IDBTransactionMode::Readwriteflush:
|
||||
mode = IDBTransaction::READ_WRITE_FLUSH;
|
||||
mode = IDBTransaction::Mode::ReadWriteFlush;
|
||||
break;
|
||||
case IDBTransactionMode::Cleanup:
|
||||
mode = IDBTransaction::CLEANUP;
|
||||
mode = IDBTransaction::Mode::Cleanup;
|
||||
mQuotaExceeded = false;
|
||||
break;
|
||||
case IDBTransactionMode::Versionchange:
|
||||
|
|
@ -715,7 +715,7 @@ IDBDatabase::Transaction(JSContext* aCx,
|
|||
|
||||
transaction->SetBackgroundActor(actor);
|
||||
|
||||
if (mode == IDBTransaction::CLEANUP) {
|
||||
if (mode == IDBTransaction::Mode::Cleanup) {
|
||||
ExpireFileActors(/* aExpireAll */ true);
|
||||
}
|
||||
|
||||
|
|
@ -859,14 +859,14 @@ IDBDatabase::AbortTransactions(bool aShouldWarn)
|
|||
if (aShouldWarn) {
|
||||
switch (transaction->GetMode()) {
|
||||
// We ignore transactions that could not have written any data.
|
||||
case IDBTransaction::READ_ONLY:
|
||||
case IDBTransaction::Mode::ReadOnly:
|
||||
break;
|
||||
|
||||
// We warn for any transactions that could have written data.
|
||||
case IDBTransaction::READ_WRITE:
|
||||
case IDBTransaction::READ_WRITE_FLUSH:
|
||||
case IDBTransaction::CLEANUP:
|
||||
case IDBTransaction::VERSION_CHANGE:
|
||||
case IDBTransaction::Mode::ReadWrite:
|
||||
case IDBTransaction::Mode::ReadWriteFlush:
|
||||
case IDBTransaction::Mode::Cleanup:
|
||||
case IDBTransaction::Mode::VersionChange:
|
||||
transactionsThatNeedWarning.AppendElement(transaction);
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ IDBIndex::SetName(const nsAString& aName, ErrorResult& aRv)
|
|||
|
||||
IDBTransaction* transaction = mObjectStore->Transaction();
|
||||
|
||||
if (transaction->GetMode() != IDBTransaction::VERSION_CHANGE ||
|
||||
if (transaction->GetMode() != IDBTransaction::Mode::VersionChange ||
|
||||
mDeletedMetadata) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1407,7 +1407,7 @@ IDBObjectStore::AddOrPut(JSContext* aCx,
|
|||
MOZ_ASSERT(aCx);
|
||||
MOZ_ASSERT_IF(aFromCursor, aOverwrite);
|
||||
|
||||
if (mTransaction->GetMode() == IDBTransaction::CLEANUP ||
|
||||
if (mTransaction->GetMode() == IDBTransaction::Mode::Cleanup ||
|
||||
mDeletedSpec) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
|
||||
return nullptr;
|
||||
|
|
@ -1991,7 +1991,7 @@ IDBObjectStore::CreateIndex(const nsAString& aName,
|
|||
{
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
if (mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE ||
|
||||
if (mTransaction->GetMode() != IDBTransaction::Mode::VersionChange ||
|
||||
mDeletedSpec) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
|
||||
return nullptr;
|
||||
|
|
@ -2101,7 +2101,7 @@ IDBObjectStore::DeleteIndex(const nsAString& aName, ErrorResult& aRv)
|
|||
{
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
if (mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE ||
|
||||
if (mTransaction->GetMode() != IDBTransaction::Mode::VersionChange ||
|
||||
mDeletedSpec) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
|
||||
return;
|
||||
|
|
@ -2424,7 +2424,7 @@ IDBObjectStore::SetName(const nsAString& aName, ErrorResult& aRv)
|
|||
{
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
if (mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE ||
|
||||
if (mTransaction->GetMode() != IDBTransaction::Mode::VersionChange ||
|
||||
mDeletedSpec) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ IDBTransaction::IDBTransaction(IDBDatabase* aDatabase,
|
|||
, mPendingRequestCount(0)
|
||||
, mLineNo(0)
|
||||
, mColumn(0)
|
||||
, mReadyState(IDBTransaction::INITIAL)
|
||||
, mReadyState(IDBTransaction::ReadyState::Initial)
|
||||
, mMode(aMode)
|
||||
, mCreating(false)
|
||||
, mRegistered(false)
|
||||
|
|
@ -134,10 +134,10 @@ IDBTransaction::~IDBTransaction()
|
|||
MOZ_ASSERT(!mPendingRequestCount);
|
||||
MOZ_ASSERT(!mCreating);
|
||||
MOZ_ASSERT(mSentCommitOrAbort);
|
||||
MOZ_ASSERT_IF(mMode == VERSION_CHANGE &&
|
||||
MOZ_ASSERT_IF(mMode == Mode::VersionChange &&
|
||||
mBackgroundActor.mVersionChangeBackgroundActor,
|
||||
mFiredCompleteOrAbort);
|
||||
MOZ_ASSERT_IF(mMode != VERSION_CHANGE &&
|
||||
MOZ_ASSERT_IF(mMode != Mode::VersionChange &&
|
||||
mBackgroundActor.mNormalBackgroundActor,
|
||||
mFiredCompleteOrAbort);
|
||||
|
||||
|
|
@ -148,7 +148,7 @@ IDBTransaction::~IDBTransaction()
|
|||
#endif
|
||||
}
|
||||
|
||||
if (mMode == VERSION_CHANGE) {
|
||||
if (mMode == Mode::VersionChange) {
|
||||
if (auto* actor = mBackgroundActor.mVersionChangeBackgroundActor) {
|
||||
actor->SendDeleteMeInternal(/* aFailedConstructor */ false);
|
||||
|
||||
|
|
@ -184,7 +184,7 @@ IDBTransaction::CreateVersionChange(
|
|||
RefPtr<IDBTransaction> transaction =
|
||||
new IDBTransaction(aDatabase,
|
||||
emptyObjectStoreNames,
|
||||
VERSION_CHANGE);
|
||||
Mode::VersionChange);
|
||||
aOpenRequest->GetCallerLocation(transaction->mFilename,
|
||||
&transaction->mLineNo, &transaction->mColumn);
|
||||
|
||||
|
|
@ -213,10 +213,10 @@ IDBTransaction::Create(JSContext* aCx, IDBDatabase* aDatabase,
|
|||
MOZ_ASSERT(aDatabase);
|
||||
aDatabase->AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(!aObjectStoreNames.IsEmpty());
|
||||
MOZ_ASSERT(aMode == READ_ONLY ||
|
||||
aMode == READ_WRITE ||
|
||||
aMode == READ_WRITE_FLUSH ||
|
||||
aMode == CLEANUP);
|
||||
MOZ_ASSERT(aMode == Mode::ReadOnly ||
|
||||
aMode == Mode::ReadWrite ||
|
||||
aMode == Mode::ReadWriteFlush ||
|
||||
aMode == Mode::Cleanup);
|
||||
|
||||
RefPtr<IDBTransaction> transaction =
|
||||
new IDBTransaction(aDatabase, aObjectStoreNames, aMode);
|
||||
|
|
@ -281,7 +281,7 @@ IDBTransaction::SetBackgroundActor(indexedDB::BackgroundTransactionChild* aBackg
|
|||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(aBackgroundActor);
|
||||
MOZ_ASSERT(!mBackgroundActor.mNormalBackgroundActor);
|
||||
MOZ_ASSERT(mMode != VERSION_CHANGE);
|
||||
MOZ_ASSERT(mMode != Mode::VersionChange);
|
||||
|
||||
mBackgroundActor.mNormalBackgroundActor = aBackgroundActor;
|
||||
}
|
||||
|
|
@ -295,7 +295,7 @@ IDBTransaction::StartRequest(IDBRequest* aRequest, const RequestParams& aParams)
|
|||
|
||||
BackgroundRequestChild* actor = new BackgroundRequestChild(aRequest);
|
||||
|
||||
if (mMode == VERSION_CHANGE) {
|
||||
if (mMode == Mode::VersionChange) {
|
||||
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
|
||||
|
||||
mBackgroundActor.mVersionChangeBackgroundActor->
|
||||
|
|
@ -321,7 +321,7 @@ IDBTransaction::OpenCursor(BackgroundCursorChild* aBackgroundActor,
|
|||
MOZ_ASSERT(aBackgroundActor);
|
||||
MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None);
|
||||
|
||||
if (mMode == VERSION_CHANGE) {
|
||||
if (mMode == Mode::VersionChange) {
|
||||
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
|
||||
|
||||
mBackgroundActor.mVersionChangeBackgroundActor->
|
||||
|
|
@ -361,8 +361,8 @@ IDBTransaction::OnNewRequest()
|
|||
AssertIsOnOwningThread();
|
||||
|
||||
if (!mPendingRequestCount) {
|
||||
MOZ_ASSERT(INITIAL == mReadyState);
|
||||
mReadyState = LOADING;
|
||||
MOZ_ASSERT(ReadyState::Initial == mReadyState);
|
||||
mReadyState = ReadyState::Loading;
|
||||
}
|
||||
|
||||
++mPendingRequestCount;
|
||||
|
|
@ -377,7 +377,7 @@ IDBTransaction::OnRequestFinished(bool aActorDestroyedNormally)
|
|||
--mPendingRequestCount;
|
||||
|
||||
if (!mPendingRequestCount) {
|
||||
mReadyState = COMMITTING;
|
||||
mReadyState = ReadyState::Committing;
|
||||
|
||||
if (aActorDestroyedNormally) {
|
||||
if (NS_SUCCEEDED(mAbortCode)) {
|
||||
|
|
@ -421,7 +421,7 @@ IDBTransaction::SendCommit()
|
|||
LoggingSerialNumber(),
|
||||
requestSerialNumber);
|
||||
|
||||
if (mMode == VERSION_CHANGE) {
|
||||
if (mMode == Mode::VersionChange) {
|
||||
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
|
||||
mBackgroundActor.mVersionChangeBackgroundActor->SendCommit();
|
||||
} else {
|
||||
|
|
@ -454,7 +454,7 @@ IDBTransaction::SendAbort(nsresult aResultCode)
|
|||
requestSerialNumber,
|
||||
aResultCode);
|
||||
|
||||
if (mMode == VERSION_CHANGE) {
|
||||
if (mMode == Mode::VersionChange) {
|
||||
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
|
||||
mBackgroundActor.mVersionChangeBackgroundActor->SendAbort(aResultCode);
|
||||
} else {
|
||||
|
|
@ -473,7 +473,7 @@ IDBTransaction::IsOpen() const
|
|||
AssertIsOnOwningThread();
|
||||
|
||||
// If we haven't started anything then we're open.
|
||||
if (mReadyState == IDBTransaction::INITIAL) {
|
||||
if (mReadyState == IDBTransaction::ReadyState::Initial) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -482,7 +482,7 @@ IDBTransaction::IsOpen() const
|
|||
// from the time we were created) then we are open. Otherwise check the
|
||||
// currently running transaction to see if it's the same. We only allow other
|
||||
// requests to be made if this transaction is currently running.
|
||||
if (mReadyState == IDBTransaction::LOADING &&
|
||||
if (mReadyState == IDBTransaction::ReadyState::Loading &&
|
||||
(mCreating || GetCurrent() == this)) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -508,7 +508,7 @@ IDBTransaction::CreateObjectStore(const ObjectStoreSpec& aSpec)
|
|||
{
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(aSpec.metadata().id());
|
||||
MOZ_ASSERT(VERSION_CHANGE == mMode);
|
||||
MOZ_ASSERT(Mode::VersionChange == mMode);
|
||||
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
|
||||
MOZ_ASSERT(IsOpen());
|
||||
|
||||
|
|
@ -540,7 +540,7 @@ IDBTransaction::DeleteObjectStore(int64_t aObjectStoreId)
|
|||
{
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(aObjectStoreId);
|
||||
MOZ_ASSERT(VERSION_CHANGE == mMode);
|
||||
MOZ_ASSERT(Mode::VersionChange == mMode);
|
||||
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
|
||||
MOZ_ASSERT(IsOpen());
|
||||
|
||||
|
|
@ -571,7 +571,7 @@ IDBTransaction::RenameObjectStore(int64_t aObjectStoreId,
|
|||
{
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(aObjectStoreId);
|
||||
MOZ_ASSERT(VERSION_CHANGE == mMode);
|
||||
MOZ_ASSERT(Mode::VersionChange == mMode);
|
||||
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
|
||||
MOZ_ASSERT(IsOpen());
|
||||
|
||||
|
|
@ -586,7 +586,7 @@ IDBTransaction::CreateIndex(IDBObjectStore* aObjectStore,
|
|||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(aObjectStore);
|
||||
MOZ_ASSERT(aMetadata.id());
|
||||
MOZ_ASSERT(VERSION_CHANGE == mMode);
|
||||
MOZ_ASSERT(Mode::VersionChange == mMode);
|
||||
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
|
||||
MOZ_ASSERT(IsOpen());
|
||||
|
||||
|
|
@ -601,7 +601,7 @@ IDBTransaction::DeleteIndex(IDBObjectStore* aObjectStore,
|
|||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(aObjectStore);
|
||||
MOZ_ASSERT(aIndexId);
|
||||
MOZ_ASSERT(VERSION_CHANGE == mMode);
|
||||
MOZ_ASSERT(Mode::VersionChange == mMode);
|
||||
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
|
||||
MOZ_ASSERT(IsOpen());
|
||||
|
||||
|
|
@ -617,7 +617,7 @@ IDBTransaction::RenameIndex(IDBObjectStore* aObjectStore,
|
|||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(aObjectStore);
|
||||
MOZ_ASSERT(aIndexId);
|
||||
MOZ_ASSERT(VERSION_CHANGE == mMode);
|
||||
MOZ_ASSERT(Mode::VersionChange == mMode);
|
||||
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
|
||||
MOZ_ASSERT(IsOpen());
|
||||
|
||||
|
|
@ -637,12 +637,12 @@ IDBTransaction::AbortInternal(nsresult aAbortCode,
|
|||
|
||||
RefPtr<DOMError> error = aError;
|
||||
|
||||
const bool isVersionChange = mMode == VERSION_CHANGE;
|
||||
const bool isVersionChange = mMode == Mode::VersionChange;
|
||||
const bool isInvalidated = mDatabase->IsInvalidated();
|
||||
bool needToSendAbort = mReadyState == INITIAL;
|
||||
bool needToSendAbort = mReadyState == ReadyState::Initial;
|
||||
|
||||
mAbortCode = aAbortCode;
|
||||
mReadyState = DONE;
|
||||
mReadyState = ReadyState::Done;
|
||||
mError = error.forget();
|
||||
|
||||
if (isVersionChange) {
|
||||
|
|
@ -775,7 +775,7 @@ IDBTransaction::FireCompleteOrAbortEvents(nsresult aResult)
|
|||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(!mFiredCompleteOrAbort);
|
||||
|
||||
mReadyState = DONE;
|
||||
mReadyState = ReadyState::Done;
|
||||
|
||||
#ifdef DEBUG
|
||||
mFiredCompleteOrAbort = true;
|
||||
|
|
@ -834,7 +834,7 @@ int64_t
|
|||
IDBTransaction::NextObjectStoreId()
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(VERSION_CHANGE == mMode);
|
||||
MOZ_ASSERT(Mode::VersionChange == mMode);
|
||||
|
||||
return mNextObjectStoreId++;
|
||||
}
|
||||
|
|
@ -843,7 +843,7 @@ int64_t
|
|||
IDBTransaction::NextIndexId()
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(VERSION_CHANGE == mMode);
|
||||
MOZ_ASSERT(Mode::VersionChange == mMode);
|
||||
|
||||
return mNextIndexId++;
|
||||
}
|
||||
|
|
@ -862,22 +862,22 @@ IDBTransaction::GetMode(ErrorResult& aRv) const
|
|||
AssertIsOnOwningThread();
|
||||
|
||||
switch (mMode) {
|
||||
case READ_ONLY:
|
||||
case Mode::ReadOnly:
|
||||
return IDBTransactionMode::Readonly;
|
||||
|
||||
case READ_WRITE:
|
||||
case Mode::ReadWrite:
|
||||
return IDBTransactionMode::Readwrite;
|
||||
|
||||
case READ_WRITE_FLUSH:
|
||||
case Mode::ReadWriteFlush:
|
||||
return IDBTransactionMode::Readwriteflush;
|
||||
|
||||
case CLEANUP:
|
||||
case Mode::Cleanup:
|
||||
return IDBTransactionMode::Cleanup;
|
||||
|
||||
case VERSION_CHANGE:
|
||||
case Mode::VersionChange:
|
||||
return IDBTransactionMode::Versionchange;
|
||||
|
||||
case MODE_INVALID:
|
||||
case Mode::Invalid:
|
||||
default:
|
||||
MOZ_CRASH("Bad mode!");
|
||||
}
|
||||
|
|
@ -896,7 +896,7 @@ IDBTransaction::ObjectStoreNames() const
|
|||
{
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
if (mMode == IDBTransaction::VERSION_CHANGE) {
|
||||
if (mMode == IDBTransaction::Mode::VersionChange) {
|
||||
return mDatabase->ObjectStoreNames();
|
||||
}
|
||||
|
||||
|
|
@ -917,7 +917,7 @@ IDBTransaction::ObjectStore(const nsAString& aName, ErrorResult& aRv)
|
|||
|
||||
const ObjectStoreSpec* spec = nullptr;
|
||||
|
||||
if (IDBTransaction::VERSION_CHANGE == mMode ||
|
||||
if (IDBTransaction::Mode::VersionChange == mMode ||
|
||||
mObjectStoreNames.Contains(aName)) {
|
||||
const nsTArray<ObjectStoreSpec>& objectStores =
|
||||
mDatabase->Spec()->objectStores();
|
||||
|
|
@ -1014,8 +1014,8 @@ IDBTransaction::Run()
|
|||
mCreating = false;
|
||||
|
||||
// Maybe commit if there were no requests generated.
|
||||
if (mReadyState == IDBTransaction::INITIAL) {
|
||||
mReadyState = DONE;
|
||||
if (mReadyState == IDBTransaction::ReadyState::Initial) {
|
||||
mReadyState = ReadyState::Done;
|
||||
|
||||
SendCommit();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,25 +53,18 @@ class IDBTransaction final
|
|||
friend class WorkerHolder;
|
||||
|
||||
public:
|
||||
enum Mode
|
||||
{
|
||||
READ_ONLY = 0,
|
||||
READ_WRITE,
|
||||
READ_WRITE_FLUSH,
|
||||
CLEANUP,
|
||||
VERSION_CHANGE,
|
||||
enum struct Mode {
|
||||
ReadOnly = 0,
|
||||
ReadWrite,
|
||||
ReadWriteFlush,
|
||||
Cleanup,
|
||||
VersionChange,
|
||||
|
||||
// Only needed for IPC serialization helper, should never be used in code.
|
||||
MODE_INVALID
|
||||
Invalid
|
||||
};
|
||||
|
||||
enum ReadyState
|
||||
{
|
||||
INITIAL = 0,
|
||||
LOADING,
|
||||
COMMITTING,
|
||||
DONE
|
||||
};
|
||||
enum struct ReadyState { Initial = 0, Loading, Inactive, Committing, Done };
|
||||
|
||||
private:
|
||||
RefPtr<IDBDatabase> mDatabase;
|
||||
|
|
@ -81,7 +74,7 @@ private:
|
|||
nsTArray<RefPtr<IDBObjectStore>> mDeletedObjectStores;
|
||||
nsAutoPtr<WorkerHolder> mWorkerHolder;
|
||||
|
||||
// Tagged with mMode. If mMode is VERSION_CHANGE then mBackgroundActor will be
|
||||
// Tagged with mMode. If mMode is Mode::VersionChange then mBackgroundActor will be
|
||||
// a BackgroundVersionChangeTransactionChild. Otherwise it will be a
|
||||
// BackgroundTransactionChild.
|
||||
union {
|
||||
|
|
@ -91,7 +84,7 @@ private:
|
|||
|
||||
const int64_t mLoggingSerialNumber;
|
||||
|
||||
// Only used for VERSION_CHANGE transactions.
|
||||
// Only used for Mode::VersionChange transactions.
|
||||
int64_t mNextObjectStoreId;
|
||||
int64_t mNextIndexId;
|
||||
|
||||
|
|
@ -146,7 +139,7 @@ public:
|
|||
{
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
if (mMode == VERSION_CHANGE) {
|
||||
if (mMode == Mode::VersionChange) {
|
||||
mBackgroundActor.mVersionChangeBackgroundActor = nullptr;
|
||||
} else {
|
||||
mBackgroundActor.mNormalBackgroundActor = nullptr;
|
||||
|
|
@ -171,7 +164,7 @@ public:
|
|||
{
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
return mReadyState == COMMITTING || mReadyState == DONE;
|
||||
return mReadyState == ReadyState::Committing || mReadyState == ReadyState::Done;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -179,17 +172,17 @@ public:
|
|||
{
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
return mReadyState == DONE;
|
||||
return mReadyState == ReadyState::Done;
|
||||
}
|
||||
|
||||
bool
|
||||
IsWriteAllowed() const
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
return mMode == READ_WRITE ||
|
||||
mMode == READ_WRITE_FLUSH ||
|
||||
mMode == CLEANUP ||
|
||||
mMode == VERSION_CHANGE;
|
||||
return mMode == Mode::ReadWrite ||
|
||||
mMode == Mode::ReadWriteFlush ||
|
||||
mMode == Mode::Cleanup ||
|
||||
mMode == Mode::VersionChange;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -295,11 +288,11 @@ public:
|
|||
void
|
||||
FireCompleteOrAbortEvents(nsresult aResult);
|
||||
|
||||
// Only for VERSION_CHANGE transactions.
|
||||
// Only for Mode::VersionChange transactions.
|
||||
int64_t
|
||||
NextObjectStoreId();
|
||||
|
||||
// Only for VERSION_CHANGE transactions.
|
||||
// Only for Mode::VersionChange transactions.
|
||||
int64_t
|
||||
NextIndexId();
|
||||
|
||||
|
|
|
|||
|
|
@ -119,19 +119,19 @@ public:
|
|||
Append(kCommaSpace);
|
||||
|
||||
switch (aTransaction->GetMode()) {
|
||||
case IDBTransaction::READ_ONLY:
|
||||
case IDBTransaction::Mode::ReadOnly:
|
||||
AppendLiteral("\"readonly\"");
|
||||
break;
|
||||
case IDBTransaction::READ_WRITE:
|
||||
case IDBTransaction::Mode::ReadWrite:
|
||||
AppendLiteral("\"readwrite\"");
|
||||
break;
|
||||
case IDBTransaction::READ_WRITE_FLUSH:
|
||||
case IDBTransaction::Mode::ReadWriteFlush:
|
||||
AppendLiteral("\"readwriteflush\"");
|
||||
break;
|
||||
case IDBTransaction::CLEANUP:
|
||||
case IDBTransaction::Mode::Cleanup:
|
||||
AppendLiteral("\"cleanup\"");
|
||||
break;
|
||||
case IDBTransaction::VERSION_CHANGE:
|
||||
case IDBTransaction::Mode::VersionChange:
|
||||
AppendLiteral("\"versionchange\"");
|
||||
break;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -86,8 +86,8 @@ template <>
|
|||
struct ParamTraits<mozilla::dom::IDBTransaction::Mode> :
|
||||
public ContiguousEnumSerializer<
|
||||
mozilla::dom::IDBTransaction::Mode,
|
||||
mozilla::dom::IDBTransaction::READ_ONLY,
|
||||
mozilla::dom::IDBTransaction::MODE_INVALID>
|
||||
mozilla::dom::IDBTransaction::Mode::ReadOnly,
|
||||
mozilla::dom::IDBTransaction::Mode::Invalid>
|
||||
{ };
|
||||
|
||||
} // namespace IPC
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
const READ_WRITE = "readwrite";
|
||||
const Mode::ReadWrite = "readwrite";
|
||||
|
||||
const databaseInfo = [
|
||||
{ name: window.location.pathname + "1" },
|
||||
|
|
@ -72,7 +72,7 @@
|
|||
for (let i = 1; i < databases.length; i++) {
|
||||
let db = databases[i];
|
||||
|
||||
let objectStore = db.transaction([objectStoreName], READ_WRITE)
|
||||
let objectStore = db.transaction([objectStoreName], Mode::ReadWrite)
|
||||
.objectStore(objectStoreName);
|
||||
|
||||
request = objectStore.add(refResult, 2);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
const READ_WRITE = "readwrite";
|
||||
const Mode::ReadWrite = "readwrite";
|
||||
|
||||
const name = window.location.pathname;
|
||||
|
||||
|
|
@ -48,7 +48,7 @@
|
|||
|
||||
is(event.type, "success", "Got correct event type");
|
||||
|
||||
let trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
let trans = db.transaction([objectStoreName], Mode::ReadWrite);
|
||||
trans.objectStore(objectStoreName).delete(fileData1.key);
|
||||
trans.oncomplete = grabEventAndContinueHandler;
|
||||
event = yield undefined;
|
||||
|
|
@ -80,7 +80,7 @@
|
|||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
trans = db.transaction([objectStoreName], Mode::ReadWrite);
|
||||
objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
request = objectStore.get(fileData2.key);
|
||||
|
|
@ -100,7 +100,7 @@
|
|||
is(getFileDBRefCount(name, 2), 0, "Correct db ref count");
|
||||
|
||||
|
||||
trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
trans = db.transaction([objectStoreName], Mode::ReadWrite);
|
||||
objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
objectStore.delete(fileData3.key);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
const READ_WRITE = "readwrite";
|
||||
const Mode::ReadWrite = "readwrite";
|
||||
|
||||
const name = window.location.pathname;
|
||||
|
||||
|
|
@ -67,7 +67,7 @@
|
|||
is(usage, startUsage + fileData1.obj.file.size + fileData2.obj.file.size,
|
||||
"Correct file usage");
|
||||
|
||||
let trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
let trans = db.transaction([objectStoreName], Mode::ReadWrite);
|
||||
trans.objectStore(objectStoreName).delete(fileData1.key);
|
||||
trans.oncomplete = grabEventAndContinueHandler;
|
||||
event = yield undefined;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
*/
|
||||
function testSteps()
|
||||
{
|
||||
const READ_WRITE = "readwrite";
|
||||
const Mode::ReadWrite = "readwrite";
|
||||
|
||||
const databaseInfo = [
|
||||
{ name: window.location.pathname + "1", source: true },
|
||||
|
|
@ -106,7 +106,7 @@
|
|||
for (let i = 1; i < databases.length; i++) {
|
||||
let db = databases[i];
|
||||
|
||||
let trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
let trans = db.transaction([objectStoreName], Mode::ReadWrite);
|
||||
let objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
request = objectStore.add(fileBackedFile, 2);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
const READ_WRITE = "readwrite";
|
||||
const Mode::ReadWrite = "readwrite";
|
||||
|
||||
const name = window.location.pathname;
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
const READ_WRITE = "readwrite";
|
||||
const Mode::ReadWrite = "readwrite";
|
||||
|
||||
const name = window.location.pathname;
|
||||
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
|
||||
is(event.type, "success", "Got correct event type");
|
||||
|
||||
let objectStore = db.transaction([objectStoreName], READ_WRITE)
|
||||
let objectStore = db.transaction([objectStoreName], Mode::ReadWrite)
|
||||
.objectStore(objectStoreName);
|
||||
request = objectStore.add(blobData.blob, blobData.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
|
|
@ -65,7 +65,7 @@
|
|||
verifyBlob(event.target.result, blobData.blob, 1);
|
||||
yield undefined;
|
||||
|
||||
objectStore = db.transaction([objectStoreName], READ_WRITE)
|
||||
objectStore = db.transaction([objectStoreName], Mode::ReadWrite)
|
||||
.objectStore(objectStoreName);
|
||||
request = objectStore.add(fileData.file, fileData.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
const READ_WRITE = "readwrite";
|
||||
const Mode::ReadWrite = "readwrite";
|
||||
|
||||
const name = window.location.pathname;
|
||||
|
||||
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
is(event.type, "success", "Got correct event type");
|
||||
|
||||
let trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
let trans = db.transaction([objectStoreName], Mode::ReadWrite);
|
||||
objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
objectStore.delete(fileData.key);
|
||||
|
|
@ -50,7 +50,7 @@
|
|||
|
||||
is(getFileDBRefCount(name, 1), 0, "Correct db ref count");
|
||||
|
||||
trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
trans = db.transaction([objectStoreName], Mode::ReadWrite);
|
||||
objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
request = objectStore.add(fileData.file, fileData.key);
|
||||
|
|
@ -81,7 +81,7 @@
|
|||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
let trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
let trans = db.transaction([objectStoreName], Mode::ReadWrite);
|
||||
objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
request = objectStore.get(fileData.key);
|
||||
|
|
@ -98,7 +98,7 @@
|
|||
|
||||
is(getFileDBRefCount(name, 1), 0, "Correct db ref count");
|
||||
|
||||
trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
trans = db.transaction([objectStoreName], Mode::ReadWrite);
|
||||
objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
request = objectStore.add(result, fileData.key);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
const READ_WRITE = "readwrite";
|
||||
const Mode::ReadWrite = "readwrite";
|
||||
|
||||
const name = window.location.pathname;
|
||||
|
||||
|
|
@ -38,7 +38,7 @@
|
|||
|
||||
is(event.type, "success", "Got correct event type");
|
||||
|
||||
let trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
let trans = db.transaction([objectStoreName], Mode::ReadWrite);
|
||||
objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
request = objectStore.add(fileData.file, fileData.key);
|
||||
|
|
@ -58,7 +58,7 @@
|
|||
|
||||
is(getFileDBRefCount(name, 1), 0, "Correct db ref count");
|
||||
|
||||
trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
trans = db.transaction([objectStoreName], Mode::ReadWrite);
|
||||
objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
request = objectStore.add(result, fileData.key);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
const READ_WRITE = "readwrite";
|
||||
const Mode::ReadWrite = "readwrite";
|
||||
|
||||
const name = window.location.pathname;
|
||||
|
||||
|
|
@ -67,7 +67,7 @@
|
|||
for (let i = 1; i < objectStoreInfo.length; i++) {
|
||||
let info = objectStoreInfo[i];
|
||||
|
||||
let objectStore = db.transaction([info.name], READ_WRITE)
|
||||
let objectStore = db.transaction([info.name], Mode::ReadWrite)
|
||||
.objectStore(info.name);
|
||||
|
||||
request = objectStore.add(refResult, 2);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
const READ_WRITE = "readwrite";
|
||||
const Mode::ReadWrite = "readwrite";
|
||||
|
||||
const name = window.location.pathname;
|
||||
|
||||
|
|
@ -38,7 +38,7 @@
|
|||
|
||||
is(event.type, "success", "Got correct event type");
|
||||
|
||||
let trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
let trans = db.transaction([objectStoreName], Mode::ReadWrite);
|
||||
objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
request = objectStore.add(fileData.file, fileData.key);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
const READ_WRITE = "readwrite";
|
||||
const Mode::ReadWrite = "readwrite";
|
||||
|
||||
const databaseInfo = [
|
||||
{ name: window.location.pathname + "1" },
|
||||
|
|
@ -56,7 +56,7 @@
|
|||
is(mutableFile.name, "random.bin", "Correct name");
|
||||
is(mutableFile.type, "binary/random", "Correct type");
|
||||
|
||||
let trans = db1.transaction([objectStoreName], READ_WRITE);
|
||||
let trans = db1.transaction([objectStoreName], Mode::ReadWrite);
|
||||
let objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
request = objectStore.add(mutableFile, 42);
|
||||
|
|
@ -74,7 +74,7 @@
|
|||
|
||||
let db2 = databases[1];
|
||||
|
||||
trans = db2.transaction([objectStoreName], READ_WRITE);
|
||||
trans = db2.transaction([objectStoreName], Mode::ReadWrite);
|
||||
objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
const READ_WRITE = "readwrite";
|
||||
const Mode::ReadWrite = "readwrite";
|
||||
|
||||
const name = window.location.pathname;
|
||||
|
||||
|
|
@ -58,7 +58,7 @@
|
|||
|
||||
let file = event.target.result;
|
||||
|
||||
let trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
let trans = db.transaction([objectStoreName], Mode::ReadWrite);
|
||||
objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
request = objectStore.add(file, 42);
|
||||
|
|
|
|||
|
|
@ -55,10 +55,10 @@ function testSteps()
|
|||
|
||||
try {
|
||||
db.transaction("foo").objectStore("foo").clear();
|
||||
ok(false, "clear should throw on READ_ONLY transactions");
|
||||
ok(false, "clear should throw on Mode::ReadOnly transactions");
|
||||
}
|
||||
catch (e) {
|
||||
ok(true, "clear should throw on READ_ONLY transactions");
|
||||
ok(true, "clear should throw on Mode::ReadOnly transactions");
|
||||
}
|
||||
|
||||
request = db.transaction("foo", "readwriteflush")
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ function testSteps()
|
|||
is(db.version, 1, "Database has correct version");
|
||||
|
||||
db.onupgradeneeded = function() {
|
||||
ok(false, "our ongoing VERSION_CHANGE transaction should exclude any others!");
|
||||
ok(false, "our ongoing Mode::VersionChange transaction should exclude any others!");
|
||||
}
|
||||
|
||||
db.createObjectStore("foo");
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ function testSteps()
|
|||
abortEventCount = 0;
|
||||
let expectedAbortEventCount = 0;
|
||||
|
||||
// During INITIAL
|
||||
// During ReadyState::Initial
|
||||
transaction = db.transaction("foo");
|
||||
transaction.abort();
|
||||
try {
|
||||
|
|
@ -269,7 +269,7 @@ function testSteps()
|
|||
ok(true, "second abort should throw an error");
|
||||
}
|
||||
|
||||
// During LOADING
|
||||
// During ReadyState::Loading
|
||||
transaction = db.transaction("foo");
|
||||
transaction.objectStore("foo").get(1).onerror = abortErrorHandler;
|
||||
expectedAbortEventCount++;
|
||||
|
|
@ -282,7 +282,7 @@ function testSteps()
|
|||
ok(true, "second abort should throw an error");
|
||||
}
|
||||
|
||||
// During LOADING from callback
|
||||
// During ReadyState::Loading from callback
|
||||
transaction = db.transaction("foo");
|
||||
transaction.objectStore("foo").get(1).onsuccess = grabEventAndContinueHandler;
|
||||
event = yield undefined;
|
||||
|
|
@ -297,7 +297,7 @@ function testSteps()
|
|||
ok(true, "second abort should throw an error");
|
||||
}
|
||||
|
||||
// During LOADING from error callback
|
||||
// During ReadyState::Loading from error callback
|
||||
transaction = db.transaction("foo", "readwrite");
|
||||
transaction.objectStore("foo").add({}, 1).onerror = function(event) {
|
||||
event.preventDefault();
|
||||
|
|
@ -327,7 +327,7 @@ function testSteps()
|
|||
};
|
||||
yield undefined;
|
||||
|
||||
// During COMMITTING
|
||||
// During ReadyState::Committing
|
||||
transaction = db.transaction("foo", "readwrite");
|
||||
transaction.objectStore("foo").put({hello: "world"}, 1).onsuccess = function(event) {
|
||||
continueToNextStep();
|
||||
|
|
|
|||
|
|
@ -259,14 +259,13 @@ SVGUseElement::CreateAnonymousContent()
|
|||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsINode> newnode;
|
||||
nsCOMArray<nsINode> unused;
|
||||
nsNodeInfoManager* nodeInfoManager =
|
||||
targetContent->OwnerDoc() == OwnerDoc() ?
|
||||
nullptr : OwnerDoc()->NodeInfoManager();
|
||||
nsNodeUtils::Clone(targetContent, true, nodeInfoManager, unused,
|
||||
getter_AddRefs(newnode));
|
||||
|
||||
IgnoredErrorResult rv;
|
||||
nsCOMPtr<nsINode> newnode =
|
||||
nsNodeUtils::Clone(targetContent, true, nodeInfoManager, &unused, rv);
|
||||
nsCOMPtr<nsIContent> newcontent = do_QueryInterface(newnode);
|
||||
|
||||
if (!newcontent)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,110 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--https://bugzilla.mozilla.org/show_bug.cgi?id=633602-->
|
||||
<head>
|
||||
<title>Bug 633602</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js">
|
||||
</script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js">
|
||||
</script>
|
||||
<script type="application/javascript" src="pointerlock_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=633602">
|
||||
Mozilla Bug 633602
|
||||
</a>
|
||||
<div id="host"></div>
|
||||
<pre id="test">
|
||||
<script type="text/javascript">
|
||||
/*
|
||||
* Test for Bug 1430303
|
||||
* Make sure DOM API is correct.
|
||||
*/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var div,
|
||||
host,
|
||||
hasRequestPointerLock = false,
|
||||
pointerLockChangeEventFired = false,
|
||||
pointerUnlocked = false,
|
||||
pointerLocked = false,
|
||||
hasExitPointerLock = false,
|
||||
pointerLockElement = false,
|
||||
hasMovementX = false,
|
||||
hasMovementY = false;
|
||||
gotContextMenuEvent = false;
|
||||
|
||||
function runTests () {
|
||||
ok(hasRequestPointerLock, "Element should have requestPointerLock.");
|
||||
ok(pointerLockChangeEventFired, "pointerlockchange event should fire.");
|
||||
ok(pointerUnlocked, "Should be able to unlock pointer locked element.");
|
||||
ok(pointerLocked, "Requested element should be able to lock.");
|
||||
ok(hasExitPointerLock, "Document should have exitPointerLock");
|
||||
ok(pointerLockElement, "Document should keep track of correct pointer locked element");
|
||||
ok(hasMovementX, "Mouse Event should have movementX.");
|
||||
ok(hasMovementY, "Mouse Event should have movementY.");
|
||||
ok(!gotContextMenuEvent, "Shouldn't have got a contextmenu event.");
|
||||
}
|
||||
|
||||
function mouseMoveHandler(e) {
|
||||
info("Got mousemove");
|
||||
document.removeEventListener("mousemove", mouseMoveHandler);
|
||||
|
||||
hasMovementX = "movementX" in e;
|
||||
hasMovementY = "movementY" in e;
|
||||
|
||||
hasExitPointerLock = "exitPointerLock" in document;
|
||||
document.exitPointerLock();
|
||||
}
|
||||
|
||||
document.addEventListener("pointerlockchange", function (e) {
|
||||
pointerLockChangeEventFired = true;
|
||||
|
||||
if (document.pointerLockElement) {
|
||||
info("Got pointerlockchange for entering");
|
||||
pointerLocked = true;
|
||||
pointerLockElement = document.pointerLockElement === host;
|
||||
is(host.shadowRoot.firstChild, host.shadowRoot.pointerLockElement,
|
||||
"ShadowRoot's pointerLockElement shouldn't be retargeted.");
|
||||
|
||||
window.addEventListener("contextmenu",
|
||||
function() { gotContextMenuEvent = true; },
|
||||
true);
|
||||
synthesizeMouse(document.body, 4, 4,
|
||||
{ type: "contextmenu", button: 2 },
|
||||
window);
|
||||
|
||||
document.addEventListener("mousemove", mouseMoveHandler);
|
||||
synthesizeMouseAtCenter(host, {type: "mousemove"}, window);
|
||||
} else {
|
||||
info("Got pointerlockchange for exiting");
|
||||
pointerUnlocked = true;
|
||||
addFullscreenChangeContinuation("exit", function() {
|
||||
info("Got fullscreenchange for exiting");
|
||||
runTests();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
document.exitFullscreen();
|
||||
}
|
||||
});
|
||||
|
||||
function start() {
|
||||
host = document.getElementById("host");
|
||||
var sr = host.attachShadow({mode: "open"});
|
||||
sr.innerHTML = "<div><div>";
|
||||
div = sr.firstChild;
|
||||
info("Requesting fullscreen on parent");
|
||||
addFullscreenChangeContinuation("enter", function() {
|
||||
info("Got fullscreenchange for entering");
|
||||
hasRequestPointerLock = "requestPointerLock" in div;
|
||||
div.requestPointerLock();
|
||||
});
|
||||
host.requestFullscreen();
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -8,6 +8,7 @@ tags = fullscreen
|
|||
support-files =
|
||||
pointerlock_utils.js
|
||||
file_pointerlock-api.html
|
||||
file_pointerlock-api-with-shadow.html
|
||||
file_pointerlockerror.html
|
||||
file_escapeKey.html
|
||||
file_withoutDOM.html
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
|
|||
["full-screen-api.unprefix.enabled", true],
|
||||
["full-screen-api.allow-trusted-requests-only", false],
|
||||
["full-screen-api.transition-duration.enter", "0 0"],
|
||||
["full-screen-api.transition-duration.leave", "0 0"]
|
||||
["full-screen-api.transition-duration.leave", "0 0"],
|
||||
["dom.webcomponents.enabled", true]
|
||||
]}, nextTest);
|
||||
|
||||
// Run the tests which go full-screen in new window, as Mochitests
|
||||
|
|
@ -49,6 +50,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
|
|||
"file_movementXY.html",
|
||||
"file_nestedFullScreen.html",
|
||||
"file_pointerlock-api.html",
|
||||
"file_pointerlock-api-with-shadow.html",
|
||||
"file_pointerlockerror.html",
|
||||
"file_pointerLockPref.html",
|
||||
"file_removedFromDOM.html",
|
||||
|
|
|
|||
|
|
@ -1,173 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=887538
|
||||
-->
|
||||
<head>
|
||||
<title>Test for HTMLShadowElement</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="grabme"></div>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887538">Bug 887538</a>
|
||||
<script>
|
||||
var host = document.createElement("span");
|
||||
|
||||
// Create three shadow roots on a single host and make sure that shadow elements
|
||||
// are associated with the correct shadow root.
|
||||
var firstShadow = host.createShadowRoot();
|
||||
firstShadow.innerHTML = '<shadow id="shadowone"></shadow>';
|
||||
var secondShadow = host.createShadowRoot();
|
||||
secondShadow.innerHTML = '<shadow id="shadowtwo"></shadow>';
|
||||
var thirdShadow = host.createShadowRoot();
|
||||
thirdShadow.innerHTML = '<shadow id="shadowthree"></shadow>';
|
||||
|
||||
is(firstShadow.getElementById("shadowone").olderShadowRoot, null, "Shadow element in oldest ShadowRoot should not be associated with a ShadowRoot.");
|
||||
is(secondShadow.getElementById("shadowtwo").olderShadowRoot, firstShadow, "Shadow element should be associated with older ShadowRoot.");
|
||||
is(thirdShadow.getElementById("shadowthree").olderShadowRoot, secondShadow, "Shadow element should be associated with older ShadowRoot.");
|
||||
|
||||
// Only the first ShadowRoot in tree order is an insertion point.
|
||||
host = document.createElement("span");
|
||||
firstShadow = host.createShadowRoot();
|
||||
secondShadow = host.createShadowRoot();
|
||||
secondShadow.innerHTML = '<shadow id="shadowone"></shadow><shadow id="shadowtwo"></shadow>';
|
||||
var shadowElemOne = secondShadow.getElementById("shadowone");
|
||||
var shadowElemTwo = secondShadow.getElementById("shadowtwo");
|
||||
|
||||
is(shadowElemOne.olderShadowRoot, firstShadow, "First <shadow> in tree order should be an insertion point.");
|
||||
is(shadowElemTwo.olderShadowRoot, null, "Second <shadow> in tree order should not be an insertion point.");
|
||||
|
||||
// Remove the first <shadow> element and make sure the second <shadow> element becomes an insertion point.
|
||||
secondShadow.removeChild(shadowElemOne);
|
||||
is(shadowElemOne.olderShadowRoot, null, "<shadow> element not in a ShadowRoot is not an insertion point.");
|
||||
is(shadowElemTwo.olderShadowRoot, firstShadow, "Second <shadow> element should become insertion point after first is removed.");
|
||||
|
||||
// Insert a <shadow> element before the current shadow insertion point and make sure that it becomes an insertion point.
|
||||
secondShadow.insertBefore(shadowElemOne, shadowElemTwo);
|
||||
is(shadowElemOne.olderShadowRoot, firstShadow, "<shadow> element inserted as first in tree order should become an insertion point.");
|
||||
is(shadowElemTwo.olderShadowRoot, null, "<shadow> element should no longer be an insertion point it another is inserted before.");
|
||||
|
||||
// <shadow> element in fallback content is not an insertion point.
|
||||
host = document.createElement("span");
|
||||
firstShadow = host.createShadowRoot();
|
||||
secondShadow = host.createShadowRoot();
|
||||
secondShadow.innerHTML = '<content><shadow id="shadowone"></shadow></content><shadow id="shadowtwo"></shadow>';
|
||||
shadowElemOne = secondShadow.getElementById("shadowone");
|
||||
shadowElemTwo = secondShadow.getElementById("shadowtwo");
|
||||
|
||||
is(shadowElemOne.olderShadowRoot, null, "<shadow> element in fallback content is not an insertion point.");
|
||||
is(shadowElemTwo.olderShadowRoot, null, "<shadow> element preceeded by another <shadow> element is not an insertion point.");
|
||||
|
||||
// <shadow> element that is descendant of shadow element is not an insertion point.
|
||||
host = document.createElement("span");
|
||||
firstShadow = host.createShadowRoot();
|
||||
secondShadow = host.createShadowRoot();
|
||||
secondShadow.innerHTML = '<shadow><shadow id="shadowone"></shadow></shadow>';
|
||||
shadowElemOne = secondShadow.getElementById("shadowone");
|
||||
is(shadowElemOne.olderShadowRoot, null, "<shadow> element that is descendant of shadow element is not an insertion point.");
|
||||
|
||||
// Check projection of <content> elements through <shadow> elements.
|
||||
host = document.createElement("span");
|
||||
firstShadow = host.createShadowRoot();
|
||||
secondShadow = host.createShadowRoot();
|
||||
firstShadow.innerHTML = '<content id="firstcontent"></content>';
|
||||
secondShadow.innerHTML = '<shadow><span id="one"></span><content id="secondcontent"></content><span id="four"></span></shadow>';
|
||||
host.innerHTML = '<span id="two"></span><span id="three"></span>';
|
||||
var firstContent = firstShadow.getElementById("firstcontent");
|
||||
var secondContent = secondShadow.getElementById("secondcontent");
|
||||
var firstDistNodes = firstContent.getDistributedNodes();
|
||||
var secondDistNodes = secondContent.getDistributedNodes();
|
||||
|
||||
is(secondDistNodes.length, 2, "There should be two distributed nodes from the host.");
|
||||
ok(secondDistNodes[0].id == "two" &&
|
||||
secondDistNodes[1].id == "three", "Nodes projected from host should preserve order.");
|
||||
|
||||
is(firstDistNodes.length, 4, "There should be four distributed nodes, two from the first shadow, two from the second shadow.");
|
||||
ok(firstDistNodes[0].id == "one" &&
|
||||
firstDistNodes[1].id == "two" &&
|
||||
firstDistNodes[2].id == "three" &&
|
||||
firstDistNodes[3].id == "four", "Reprojection through shadow should preserve node order.");
|
||||
|
||||
// Remove a node from the host and make sure that it is removed from all insertion points.
|
||||
host.removeChild(host.firstChild);
|
||||
firstDistNodes = firstContent.getDistributedNodes();
|
||||
secondDistNodes = secondContent.getDistributedNodes();
|
||||
|
||||
is(secondDistNodes.length, 1, "There should be one distriubted node remaining after removing node from host.");
|
||||
ok(secondDistNodes[0].id == "three", "Span with id=two should have been removed from content element.");
|
||||
is(firstDistNodes.length, 3, "There should be three distributed nodes remaining after removing node from host.");
|
||||
ok(firstDistNodes[0].id == "one" &&
|
||||
firstDistNodes[1].id == "three" &&
|
||||
firstDistNodes[2].id == "four", "Reprojection through shadow should preserve node order.");
|
||||
|
||||
// Check projection of <shadow> elements to <content> elements.
|
||||
host = document.createElement("span");
|
||||
firstShadow = host.createShadowRoot();
|
||||
secondShadow = host.createShadowRoot();
|
||||
secondShadow.innerHTML = '<span id="firstspan"><shadow></shadow></span>';
|
||||
thirdShadow = secondShadow.getElementById("firstspan").createShadowRoot();
|
||||
thirdShadow.innerHTML = '<content id="firstcontent"></content>';
|
||||
firstContent = thirdShadow.getElementById("firstcontent");
|
||||
var shadowChild = document.createElement("span");
|
||||
firstShadow.appendChild(shadowChild);
|
||||
|
||||
is(firstContent.getDistributedNodes()[0], shadowChild, "Elements in shadow insertioin point should be projected into content insertion points.");
|
||||
|
||||
// Remove child of ShadowRoot and check that projected node is removed from insertion point.
|
||||
firstShadow.removeChild(firstShadow.firstChild);
|
||||
|
||||
is(firstContent.getDistributedNodes().length, 0, "Reprojected element was removed from ShadowRoot, thus it should be removed from content insertion point.");
|
||||
|
||||
// Check deeply nested projection of <shadow> elements.
|
||||
host = document.createElement("span");
|
||||
firstShadow = host.createShadowRoot();
|
||||
firstShadow.innerHTML = '<content></content>';
|
||||
secondShadow = host.createShadowRoot();
|
||||
secondShadow.innerHTML = '<shadow><content></content></shadow>';
|
||||
thirdShadow = host.createShadowRoot();
|
||||
thirdShadow.innerHTML = '<span id="firstspan"><shadow><content></content></shadow></span>';
|
||||
var fourthShadow = thirdShadow.getElementById("firstspan").createShadowRoot();
|
||||
fourthShadow.innerHTML = '<content id="firstcontent"></content>';
|
||||
firstContent = fourthShadow.getElementById("firstcontent");
|
||||
host.innerHTML = '<span></span>';
|
||||
|
||||
is(firstContent.getDistributedNodes()[0], host.firstChild, "Child of host should be projected to insertion point.");
|
||||
|
||||
// Remove node and make sure that it is removed from distributed nodes.
|
||||
host.removeChild(host.firstChild);
|
||||
|
||||
is(firstContent.getDistributedNodes().length, 0, "Node removed from host should be removed from insertion point.");
|
||||
|
||||
// Check projection of fallback content through <shadow> elements.
|
||||
host = document.createElement("span");
|
||||
firstShadow = host.createShadowRoot();
|
||||
firstShadow.innerHTML = '<content><span id="firstspan"></span></content>';
|
||||
secondShadow = host.createShadowRoot();
|
||||
secondShadow.innerHTML = '<span id="secondspan"><shadow id="firstshadow"></shadow></span>';
|
||||
firstShadowElem = secondShadow.getElementById("firstshadow");
|
||||
thirdShadow = secondShadow.getElementById("secondspan").createShadowRoot();
|
||||
thirdShadow.innerHTML = '<content id="firstcontent"></content>';
|
||||
firstContent = thirdShadow.getElementById("firstcontent");
|
||||
|
||||
is(firstContent.getDistributedNodes().length, 1, "There should be one node distributed from fallback content.");
|
||||
is(firstContent.getDistributedNodes()[0], firstShadow.getElementById("firstspan"), "Fallback content should be distributed.");
|
||||
|
||||
// Add some content to the host (causing the fallback content to be dropped) and make sure distribution nodes are updated.
|
||||
|
||||
var newElem = document.createElement("div");
|
||||
firstShadowElem.appendChild(newElem);
|
||||
|
||||
is(firstContent.getDistributedNodes().length, 1, "There should be one node distributed from the host.");
|
||||
is(firstContent.getDistributedNodes()[0], newElem, "Distributed node should be from host, not fallback content.");
|
||||
|
||||
// Remove the distribution node and check that fallback content is used.
|
||||
firstShadowElem.removeChild(newElem);
|
||||
|
||||
is(firstContent.getDistributedNodes().length, 1, "There should be one node distributed from fallback content.");
|
||||
is(firstContent.getDistributedNodes()[0], firstShadow.getElementById("firstspan"), "Fallback content should be distributed after removing node from host.");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -22,22 +22,7 @@ isnot(document.baseURI, "http://www.example.org/", "Base element should be inert
|
|||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Check that <link> is inert.
|
||||
var numStyleBeforeLoad = document.styleSheets.length;
|
||||
|
||||
shadow.innerHTML = '<link id="shadowlink" rel="stylesheet" type="text/css" href="inert_style.css" /><span id="shadowspan"></span>';
|
||||
shadow.applyAuthorStyles = true;
|
||||
var shadowSpan = shadow.getElementById("shadowspan");
|
||||
var shadowStyle = shadow.getElementById("shadowlink");
|
||||
|
||||
function runChecks() {
|
||||
isnot(getComputedStyle(shadowSpan, null).getPropertyValue("padding-top"), "10px", "Link element should be inert.");
|
||||
is(document.styleSheets.length, numStyleBeforeLoad, "Document style count should remain the same because the style should not be in the doucment.");
|
||||
is(shadow.styleSheets.length, 0, "Inert link should not add style to ShadowRoot.");
|
||||
// Remove link to make sure we don't get assertions.
|
||||
shadow.removeChild(shadowStyle);
|
||||
SimpleTest.finish();
|
||||
};
|
||||
SimpleTest.finish();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -48,19 +48,12 @@ isnot(getComputedStyle(document.getElementById("bodydiv"), null).getPropertyValu
|
|||
// Make sure that elements in the ShadowRoot are styled according to the ShadowRoot style.
|
||||
is(getComputedStyle(divToStyle, null).getPropertyValue("height"), "100px", "ShadowRoot style sheets should apply to elements in ShadowRoot.");
|
||||
|
||||
// Tests for applyAuthorStyles.
|
||||
// Tests for author styles not applying in a ShadowRoot.
|
||||
var authorStyle = document.createElement("style");
|
||||
authorStyle.innerHTML = ".fat { padding-right: 20px; padding-left: 30px; }";
|
||||
document.body.appendChild(authorStyle);
|
||||
|
||||
is(root.applyAuthorStyles, false, "applyAuthorStyles defaults to false.");
|
||||
isnot(getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should not apply to ShadowRoot when ShadowRoot.applyAuthorStyles is false.");
|
||||
root.applyAuthorStyles = true;
|
||||
is(root.applyAuthorStyles, true, "applyAuthorStyles was set to true.");
|
||||
is(getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should apply to ShadowRoot when ShadowRoot.applyAuthorStyles is true.");
|
||||
root.applyAuthorStyles = false;
|
||||
is(root.applyAuthorStyles, false, "applyAuthorStyles was set to false.");
|
||||
isnot(getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should not apply to ShadowRoot when ShadowRoot.applyAuthorStyles is false.");
|
||||
isnot(iframeWin.getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should not apply to ShadowRoot.");
|
||||
|
||||
// Test dynamic changes to style in ShadowRoot.
|
||||
root.innerHTML = '<div id="divtostyle" class="dummy"></div>';
|
||||
|
|
|
|||
|
|
@ -238,10 +238,6 @@ partial interface Document {
|
|||
readonly attribute boolean fullscreenEnabled;
|
||||
[BinaryName="fullscreenEnabled"]
|
||||
readonly attribute boolean mozFullScreenEnabled;
|
||||
[LenientSetter, Func="nsDocument::IsUnprefixedFullscreenEnabled"]
|
||||
readonly attribute Element? fullscreenElement;
|
||||
[BinaryName="fullscreenElement"]
|
||||
readonly attribute Element? mozFullScreenElement;
|
||||
|
||||
[Func="nsDocument::IsUnprefixedFullscreenEnabled"]
|
||||
void exitFullscreen();
|
||||
|
|
@ -258,7 +254,6 @@ partial interface Document {
|
|||
// https://w3c.github.io/pointerlock/#extensions-to-the-document-interface
|
||||
// https://w3c.github.io/pointerlock/#extensions-to-the-documentorshadowroot-mixin
|
||||
partial interface Document {
|
||||
readonly attribute Element? pointerLockElement;
|
||||
[BinaryName="pointerLockElement", Pref="pointer-lock-api.prefixed.enabled"]
|
||||
readonly attribute Element? mozPointerLockElement;
|
||||
void exitPointerLock();
|
||||
|
|
@ -292,8 +287,6 @@ partial interface Document {
|
|||
|
||||
// http://dev.w3.org/csswg/cssom-view/#extensions-to-the-document-interface
|
||||
partial interface Document {
|
||||
Element? elementFromPoint (float x, float y);
|
||||
sequence<Element> elementsFromPoint (float x, float y);
|
||||
CaretPosition? caretPositionFromPoint (float x, float y);
|
||||
|
||||
readonly attribute Element? scrollingElement;
|
||||
|
|
|
|||
|
|
@ -12,18 +12,17 @@
|
|||
interface DocumentOrShadowRoot {
|
||||
// Not implemented yet: bug 1430308.
|
||||
// Selection? getSelection();
|
||||
// Not implemented yet: bug 1430301.
|
||||
// Element? elementFromPoint (float x, float y);
|
||||
// Not implemented yet: bug 1430301.
|
||||
// sequence<Element> elementsFromPoint (float x, float y);
|
||||
Element? elementFromPoint (float x, float y);
|
||||
sequence<Element> elementsFromPoint (float x, float y);
|
||||
// Not implemented yet: bug 1430307.
|
||||
// CaretPosition? caretPositionFromPoint (float x, float y);
|
||||
|
||||
readonly attribute Element? activeElement;
|
||||
readonly attribute StyleSheetList styleSheets;
|
||||
|
||||
// Not implemented yet: bug 1430303.
|
||||
// readonly attribute Element? pointerLockElement;
|
||||
// Not implemented yet: bug 1430305.
|
||||
// readonly attribute Element? fullscreenElement;
|
||||
readonly attribute Element? pointerLockElement;
|
||||
[LenientSetter, Func="nsDocument::IsUnprefixedFullscreenEnabled"]
|
||||
readonly attribute Element? fullscreenElement;
|
||||
[BinaryName="fullscreenElement"]
|
||||
readonly attribute Element? mozFullScreenElement;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -244,10 +244,6 @@ partial interface Element {
|
|||
readonly attribute HTMLSlotElement? assignedSlot;
|
||||
[CEReactions, Unscopable, SetterThrows, Func="nsDocument::IsWebComponentsEnabled"]
|
||||
attribute DOMString slot;
|
||||
|
||||
// [deprecated] Shadow DOM v0
|
||||
[Throws, Func="nsDocument::IsWebComponentsEnabled"]
|
||||
ShadowRoot createShadowRoot();
|
||||
};
|
||||
|
||||
Element implements ChildNode;
|
||||
|
|
|
|||
|
|
@ -31,6 +31,5 @@ interface ShadowRoot : DocumentFragment
|
|||
HTMLCollection getElementsByClassName(DOMString classNames);
|
||||
[CEReactions, SetterThrows, TreatNullAs=EmptyString]
|
||||
attribute DOMString innerHTML;
|
||||
attribute boolean applyAuthorStyles;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -321,10 +321,11 @@ nsXBLBinding::GenerateAnonymousContent()
|
|||
if (hasContent) {
|
||||
nsIDocument* doc = mBoundElement->OwnerDoc();
|
||||
|
||||
nsCOMPtr<nsINode> clonedNode;
|
||||
nsCOMArray<nsINode> nodesWithProperties;
|
||||
nsNodeUtils::Clone(content, true, doc->NodeInfoManager(),
|
||||
nodesWithProperties, getter_AddRefs(clonedNode));
|
||||
IgnoredErrorResult rv;
|
||||
nsCOMPtr<nsINode> clonedNode =
|
||||
nsNodeUtils::Clone(content, true, doc->NodeInfoManager(), nullptr, rv);
|
||||
// FIXME: Bug 1399558, Why is this code OK assuming that nsNodeUtils::Clone
|
||||
// never fails?
|
||||
mContent = clonedNode->AsElement();
|
||||
|
||||
// Search for <xbl:children> elements in the XBL content. In the presence
|
||||
|
|
|
|||
|
|
@ -155,6 +155,8 @@ NS_NewDOMDocument(nsIDOMDocument** aInstancePtrResult,
|
|||
if (aDoctype) {
|
||||
nsCOMPtr<nsIDOMNode> tmpNode;
|
||||
rv = doc->AppendChild(aDoctype, getter_AddRefs(tmpNode));
|
||||
// TODO: if we choose to land bug 1318479, make sure to call
|
||||
// result.WouldReportJSException() before stealing the NSResult.
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
|
|
@ -167,6 +169,8 @@ NS_NewDOMDocument(nsIDOMDocument** aInstancePtrResult,
|
|||
nsCOMPtr<nsIDOMNode> tmpNode;
|
||||
|
||||
rv = doc->AppendChild(root, getter_AddRefs(tmpNode));
|
||||
// TODO: if we choose to land bug 1318479, make sure to call
|
||||
// result.WouldReportJSException() before stealing the NSResult.
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -682,7 +682,7 @@ nsEditorSpellCheck::UpdateCurrentDictionary(nsIEditorSpellCheckCallback* aCallba
|
|||
RefPtr<DictionaryFetcher> fetcher =
|
||||
new DictionaryFetcher(this, aCallback, mDictionaryFetcherGroup);
|
||||
rootContent->GetLang(fetcher->mRootContentLang);
|
||||
nsCOMPtr<nsIDocument> doc = rootContent->GetUncomposedDoc();
|
||||
nsCOMPtr<nsIDocument> doc = rootContent->GetComposedDoc();
|
||||
NS_ENSURE_STATE(doc);
|
||||
doc->GetContentLanguage(fetcher->mRootDocContentLang);
|
||||
|
||||
|
|
|
|||
|
|
@ -1114,7 +1114,7 @@ EditorEventListener::Focus(WidgetEvent* aFocusEvent)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMEventTarget> target = aFocusEvent->GetDOMEventTarget();
|
||||
nsCOMPtr<nsIDOMEventTarget> target = aFocusEvent->GetOriginalDOMEventTarget();
|
||||
nsCOMPtr<nsINode> node = do_QueryInterface(target);
|
||||
NS_ENSURE_TRUE(node, NS_ERROR_UNEXPECTED);
|
||||
|
||||
|
|
@ -1126,13 +1126,15 @@ EditorEventListener::Focus(WidgetEvent* aFocusEvent)
|
|||
}
|
||||
|
||||
if (node->IsNodeOfType(nsINode::eCONTENT)) {
|
||||
nsIContent* content =
|
||||
node->AsContent()->FindFirstNonChromeOnlyAccessContent();
|
||||
// XXX If the focus event target is a form control in contenteditable
|
||||
// element, perhaps, the parent HTML editor should do nothing by this
|
||||
// handler. However, FindSelectionRoot() returns the root element of the
|
||||
// contenteditable editor. So, the editableRoot value is invalid for
|
||||
// the plain text editor, and it will be set to the wrong limiter of
|
||||
// the selection. However, fortunately, actual bugs are not found yet.
|
||||
nsCOMPtr<nsIContent> editableRoot = editorBase->FindSelectionRoot(node);
|
||||
nsCOMPtr<nsIContent> editableRoot = editorBase->FindSelectionRoot(content);
|
||||
|
||||
// make sure that the element is really focused in case an earlier
|
||||
// listener in the chain changed the focus.
|
||||
|
|
|
|||
|
|
@ -7986,16 +7986,20 @@ HTMLEditRules::ConfirmSelectionInBody()
|
|||
{
|
||||
// get the body
|
||||
NS_ENSURE_STATE(mHTMLEditor);
|
||||
nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(mHTMLEditor->GetRoot());
|
||||
NS_ENSURE_TRUE(rootElement, NS_ERROR_UNEXPECTED);
|
||||
RefPtr<Element> rootElement = mHTMLEditor->GetRoot();
|
||||
if (NS_WARN_IF(!rootElement)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// get the selection
|
||||
NS_ENSURE_STATE(mHTMLEditor);
|
||||
RefPtr<Selection> selection = mHTMLEditor->GetSelection();
|
||||
NS_ENSURE_STATE(selection);
|
||||
if (NS_WARN_IF(!selection)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// get the selection start location
|
||||
nsCOMPtr<nsIDOMNode> selNode, temp, parent;
|
||||
nsCOMPtr<nsINode> selNode;
|
||||
int32_t selOffset;
|
||||
NS_ENSURE_STATE(mHTMLEditor);
|
||||
nsresult rv =
|
||||
|
|
@ -8005,12 +8009,11 @@ HTMLEditRules::ConfirmSelectionInBody()
|
|||
return rv;
|
||||
}
|
||||
|
||||
temp = selNode;
|
||||
nsINode* temp = selNode;
|
||||
|
||||
// check that selNode is inside body
|
||||
while (temp && !TextEditUtils::IsBody(temp)) {
|
||||
temp->GetParentNode(getter_AddRefs(parent));
|
||||
temp = parent;
|
||||
while (temp && !temp->IsHTMLElement(nsGkAtoms::body)) {
|
||||
temp = temp->GetParentOrHostNode();
|
||||
}
|
||||
|
||||
// if we aren't in the body, force the issue
|
||||
|
|
@ -8018,6 +8021,7 @@ HTMLEditRules::ConfirmSelectionInBody()
|
|||
// uncomment this to see when we get bad selections
|
||||
// NS_NOTREACHED("selection not in body");
|
||||
selection->Collapse(rootElement, 0);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// get the selection end location
|
||||
|
|
@ -8028,9 +8032,8 @@ HTMLEditRules::ConfirmSelectionInBody()
|
|||
temp = selNode;
|
||||
|
||||
// check that selNode is inside body
|
||||
while (temp && !TextEditUtils::IsBody(temp)) {
|
||||
rv = temp->GetParentNode(getter_AddRefs(parent));
|
||||
temp = parent;
|
||||
while (temp && !temp->IsHTMLElement(nsGkAtoms::body)) {
|
||||
temp = temp->GetParentOrHostNode();
|
||||
}
|
||||
|
||||
// if we aren't in the body, force the issue
|
||||
|
|
@ -8040,9 +8043,7 @@ HTMLEditRules::ConfirmSelectionInBody()
|
|||
selection->Collapse(rootElement, 0);
|
||||
}
|
||||
|
||||
// XXX This is the result of the last call of GetParentNode(), it doesn't
|
||||
// make sense...
|
||||
return rv;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
|
|
|||
|
|
@ -381,13 +381,14 @@ HTMLEditor::FindSelectionRoot(nsINode* aNode)
|
|||
aNode->IsNodeOfType(nsINode::eCONTENT),
|
||||
"aNode must be content or document node");
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = aNode->GetUncomposedDoc();
|
||||
nsCOMPtr<nsIDocument> doc = aNode->GetComposedDoc();
|
||||
if (!doc) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContent> content;
|
||||
if (doc->HasFlag(NODE_IS_EDITABLE) || !aNode->IsContent()) {
|
||||
if (aNode->IsInUncomposedDoc() &&
|
||||
doc->HasFlag(NODE_IS_EDITABLE) || !aNode->IsContent()) {
|
||||
content = doc->GetRootElement();
|
||||
return content.forget();
|
||||
}
|
||||
|
|
@ -5135,7 +5136,11 @@ HTMLEditor::IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent)
|
|||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMEventTarget> target = aGUIEvent->GetDOMEventTarget();
|
||||
nsCOMPtr<nsIDOMEventTarget> target = aGUIEvent->GetOriginalDOMEventTarget();
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(target);
|
||||
if (content) {
|
||||
target = content->FindFirstNonChromeOnlyAccessContent();
|
||||
}
|
||||
NS_ENSURE_TRUE(target, false);
|
||||
|
||||
nsCOMPtr<nsIDocument> document = GetDocument();
|
||||
|
|
|
|||
|
|
@ -1483,8 +1483,9 @@ nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
|||
// Now check that we're still looking at a range that's under
|
||||
// aWordUtil.GetRootNode()
|
||||
nsINode* rootNode = aWordUtil.GetRootNode();
|
||||
if (!nsContentUtils::ContentIsDescendantOf(beginNode, rootNode) ||
|
||||
!nsContentUtils::ContentIsDescendantOf(endNode, rootNode)) {
|
||||
if (!beginNode->IsInComposedDoc() || !endNode->IsInComposedDoc() ||
|
||||
!nsContentUtils::ContentIsShadowIncludingDescendantOf(beginNode, rootNode) ||
|
||||
!nsContentUtils::ContentIsShadowIncludingDescendantOf(endNode, rootNode)) {
|
||||
// Just bail out and don't try to spell-check this
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ bool
|
|||
js::intl_FormatRelativeTime(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 3);
|
||||
MOZ_ASSERT(args.length() == 4);
|
||||
|
||||
RootedObject relativeTimeFormat(cx, &args[0].toObject());
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
}
|
||||
|
||||
connectedCallback() {
|
||||
let shadow = this.createShadowRoot();
|
||||
let shadow = this.attachShadow({ mode: "open" });
|
||||
if (this.template) {
|
||||
let te = document.createElement('template');
|
||||
te.innerHTML = this.template;
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
<!doctype html>
|
||||
<script>
|
||||
// Test for content redistribution outside of the document.
|
||||
// Passes if it doesn't assert.
|
||||
let host = document.createElement('div');
|
||||
host.innerHTML = "<div id='foo'></div>";
|
||||
|
||||
let shadowRoot = host.createShadowRoot();
|
||||
shadowRoot.innerHTML = "<content select='#foo'></content>";
|
||||
|
||||
host.firstElementChild.removeAttribute('id');
|
||||
|
||||
// Move to the document, do the same.
|
||||
document.documentElement.appendChild(host);
|
||||
host.firstElementChild.setAttribute('id', 'foo');
|
||||
</script>
|
||||
|
|
@ -484,6 +484,5 @@ load 1308793.svg
|
|||
load 1308848-1.html
|
||||
load 1308848-2.html
|
||||
asserts(0-1) load 1343606.html # bug 1343948
|
||||
load 1404789-1.html
|
||||
load 1404789-2.html
|
||||
load 1419762.html
|
||||
|
|
|
|||
|
|
@ -796,21 +796,23 @@ public:
|
|||
|
||||
nsCOMArray<nsIContent> mGeneratedTextNodesWithInitializer;
|
||||
|
||||
TreeMatchContext mTreeMatchContext;
|
||||
TreeMatchContext& mTreeMatchContext;
|
||||
|
||||
// Constructor
|
||||
// Use the passed-in history state.
|
||||
nsFrameConstructorState(
|
||||
nsIPresShell* aPresShell,
|
||||
TreeMatchContext& aTreeMatchContext,
|
||||
nsContainerFrame* aFixedContainingBlock,
|
||||
nsContainerFrame* aAbsoluteContainingBlock,
|
||||
nsContainerFrame* aFloatContainingBlock,
|
||||
already_AddRefed<nsILayoutHistoryState> aHistoryState);
|
||||
// Get the history state from the pres context's pres shell.
|
||||
nsFrameConstructorState(nsIPresShell* aPresShell,
|
||||
nsContainerFrame* aFixedContainingBlock,
|
||||
nsContainerFrame* aAbsoluteContainingBlock,
|
||||
nsContainerFrame* aFloatContainingBlock);
|
||||
nsFrameConstructorState(nsIPresShell* aPresShell,
|
||||
TreeMatchContext& aTreeMatchContext,
|
||||
nsContainerFrame* aFixedContainingBlock,
|
||||
nsContainerFrame* aAbsoluteContainingBlock,
|
||||
nsContainerFrame* aFloatContainingBlock);
|
||||
|
||||
~nsFrameConstructorState();
|
||||
|
||||
|
|
@ -963,6 +965,7 @@ protected:
|
|||
|
||||
nsFrameConstructorState::nsFrameConstructorState(
|
||||
nsIPresShell* aPresShell,
|
||||
TreeMatchContext& aTreeMatchContext,
|
||||
nsContainerFrame* aFixedContainingBlock,
|
||||
nsContainerFrame* aAbsoluteContainingBlock,
|
||||
nsContainerFrame* aFloatContainingBlock,
|
||||
|
|
@ -989,8 +992,7 @@ nsFrameConstructorState::nsFrameConstructorState(
|
|||
mFixedPosIsAbsPos(aFixedContainingBlock == aAbsoluteContainingBlock),
|
||||
mHavePendingPopupgroup(false),
|
||||
mCreatingExtraFrames(false),
|
||||
mTreeMatchContext(true, nsRuleWalker::eRelevantLinkUnvisited,
|
||||
aPresShell->GetDocument()),
|
||||
mTreeMatchContext(aTreeMatchContext),
|
||||
mCurrentPendingBindingInsertionPoint(nullptr)
|
||||
{
|
||||
#ifdef MOZ_XUL
|
||||
|
|
@ -1003,10 +1005,13 @@ nsFrameConstructorState::nsFrameConstructorState(
|
|||
}
|
||||
|
||||
nsFrameConstructorState::nsFrameConstructorState(nsIPresShell* aPresShell,
|
||||
TreeMatchContext& aTreeMatchContext,
|
||||
nsContainerFrame* aFixedContainingBlock,
|
||||
nsContainerFrame* aAbsoluteContainingBlock,
|
||||
nsContainerFrame* aFloatContainingBlock)
|
||||
: nsFrameConstructorState(aPresShell, aFixedContainingBlock,
|
||||
: nsFrameConstructorState(aPresShell,
|
||||
aTreeMatchContext,
|
||||
aFixedContainingBlock,
|
||||
aAbsoluteContainingBlock,
|
||||
aFloatContainingBlock,
|
||||
aPresShell->GetDocument()->GetLayoutHistoryState())
|
||||
|
|
@ -2417,13 +2422,15 @@ nsCSSFrameConstructor::ConstructDocElementFrame(Element* aDocEle
|
|||
|
||||
NS_ASSERTION(mDocElementContainingBlock, "Should have parent by now");
|
||||
|
||||
TreeMatchContext matchContext(mDocument, TreeMatchContext::ForFrameConstruction);
|
||||
// Initialize the ancestor filter with null for now; we'll push
|
||||
// aDocElement once we finish resolving style for it.
|
||||
matchContext.InitAncestors(nullptr);
|
||||
nsFrameConstructorState state(mPresShell,
|
||||
matchContext,
|
||||
GetAbsoluteContainingBlock(mDocElementContainingBlock, FIXED_POS),
|
||||
nullptr,
|
||||
nullptr, do_AddRef(aFrameState));
|
||||
// Initialize the ancestor filter with null for now; we'll push
|
||||
// aDocElement once we finish resolving style for it.
|
||||
state.mTreeMatchContext.InitAncestors(nullptr);
|
||||
|
||||
// XXXbz why, exactly?
|
||||
if (!mTempFrameTreeState)
|
||||
|
|
@ -2856,7 +2863,8 @@ nsCSSFrameConstructor::SetUpDocElementContainingBlock(nsIContent* aDocElement)
|
|||
RefPtr<nsStyleContext> rootPseudoStyle;
|
||||
// we must create a state because if the scrollbars are GFX it needs the
|
||||
// state to build the scrollbar frames.
|
||||
nsFrameConstructorState state(mPresShell, nullptr, nullptr, nullptr);
|
||||
TreeMatchContext matchContext(mDocument, TreeMatchContext::ForFrameConstruction);
|
||||
nsFrameConstructorState state(mPresShell, matchContext, nullptr, nullptr, nullptr);
|
||||
|
||||
// Start off with the viewport as parent; we'll adjust it as needed.
|
||||
nsContainerFrame* parentFrame = viewportFrame;
|
||||
|
|
@ -7023,12 +7031,16 @@ nsCSSFrameConstructor::MaybeConstructLazily(Operation aOperation,
|
|||
}
|
||||
|
||||
void
|
||||
nsCSSFrameConstructor::CreateNeededFrames(nsIContent* aContent)
|
||||
nsCSSFrameConstructor::CreateNeededFrames(nsIContent* aContent,
|
||||
TreeMatchContext& aTreeMatchContext)
|
||||
{
|
||||
NS_ASSERTION(!aContent->HasFlag(NODE_NEEDS_FRAME),
|
||||
"shouldn't get here with a content node that has needs frame bit set");
|
||||
NS_ASSERTION(aContent->HasFlag(NODE_DESCENDANTS_NEED_FRAMES),
|
||||
"should only get here with a content node that has descendants needing frames");
|
||||
NS_ASSERTION(aTreeMatchContext.mAncestorFilter.HasFilter(),
|
||||
"The whole point of having the tree match context is optimizing "
|
||||
"the ancestor filter usage!");
|
||||
|
||||
aContent->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES);
|
||||
|
||||
|
|
@ -7064,19 +7076,31 @@ nsCSSFrameConstructor::CreateNeededFrames(nsIContent* aContent)
|
|||
inRun = false;
|
||||
// generate a ContentRangeInserted for [startOfRun,i)
|
||||
ContentRangeInserted(aContent, firstChildInRun, child, nullptr,
|
||||
false);
|
||||
false, &aTreeMatchContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inRun) {
|
||||
ContentAppended(aContent, firstChildInRun, false);
|
||||
ContentAppended(aContent, firstChildInRun, false, &aTreeMatchContext);
|
||||
}
|
||||
|
||||
// Now descend.
|
||||
FlattenedChildIterator iter(aContent);
|
||||
for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
|
||||
if (child->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
|
||||
CreateNeededFrames(child);
|
||||
TreeMatchContext::AutoAncestorPusher insertionPointPusher(
|
||||
aTreeMatchContext);
|
||||
|
||||
// Handle stuff like xbl:children.
|
||||
if (child->GetParent() != aContent && child->GetParent()->IsElement()) {
|
||||
insertionPointPusher.PushAncestorAndStyleScope(
|
||||
child->GetParent()->AsElement());
|
||||
}
|
||||
|
||||
TreeMatchContext::AutoAncestorPusher pusher(aTreeMatchContext);
|
||||
pusher.PushAncestorAndStyleScope(child);
|
||||
|
||||
CreateNeededFrames(child, aTreeMatchContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7091,7 +7115,9 @@ void nsCSSFrameConstructor::CreateNeededFrames()
|
|||
"root element should not have frame created lazily");
|
||||
if (rootElement && rootElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
|
||||
BeginUpdate();
|
||||
CreateNeededFrames(rootElement);
|
||||
TreeMatchContext treeMatchContext(mDocument, TreeMatchContext::ForFrameConstruction);
|
||||
treeMatchContext.InitAncestors(rootElement);
|
||||
CreateNeededFrames(rootElement, treeMatchContext);
|
||||
EndUpdate();
|
||||
}
|
||||
}
|
||||
|
|
@ -7208,10 +7234,13 @@ nsCSSFrameConstructor::MaybeRecreateForFrameset(nsIFrame* aParentFrame,
|
|||
}
|
||||
|
||||
void
|
||||
nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
|
||||
nsIContent* aFirstNewContent,
|
||||
bool aAllowLazyConstruction)
|
||||
nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
|
||||
nsIContent* aFirstNewContent,
|
||||
bool aAllowLazyConstruction,
|
||||
TreeMatchContext* aProvidedTreeMatchContext)
|
||||
{
|
||||
MOZ_ASSERT_IF(aProvidedTreeMatchContext, !aAllowLazyConstruction);
|
||||
|
||||
AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
|
||||
NS_PRECONDITION(mUpdateCount != 0,
|
||||
"Should be in an update while creating frames");
|
||||
|
|
@ -7391,13 +7420,23 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
|
|||
}
|
||||
|
||||
// Create some new frames
|
||||
//
|
||||
// We use the provided tree match context, or create a new one on the fly
|
||||
// otherwise.
|
||||
Maybe<TreeMatchContext> matchContext;
|
||||
if (!aProvidedTreeMatchContext) {
|
||||
matchContext.emplace(mDocument, TreeMatchContext::ForFrameConstruction);
|
||||
// We use GetParentElementCrossingShadowRoot to handle the case where
|
||||
// aContainer is a ShadowRoot.
|
||||
matchContext->InitAncestors(aFirstNewContent->GetParentElementCrossingShadowRoot());
|
||||
}
|
||||
nsFrameConstructorState state(mPresShell,
|
||||
aProvidedTreeMatchContext
|
||||
? *aProvidedTreeMatchContext
|
||||
: *matchContext,
|
||||
GetAbsoluteContainingBlock(parentFrame, FIXED_POS),
|
||||
GetAbsoluteContainingBlock(parentFrame, ABS_POS),
|
||||
containingBlock);
|
||||
// We use GetParentElementCrossingShadowRoot to handle the case where
|
||||
// aContainer is a ShadowRoot.
|
||||
state.mTreeMatchContext.InitAncestors(aFirstNewContent->GetParentElementCrossingShadowRoot());
|
||||
|
||||
nsIAtom* frameType = parentFrame->GetType();
|
||||
|
||||
|
|
@ -7606,7 +7645,8 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
|
|||
nsIContent* aStartChild,
|
||||
nsIContent* aEndChild,
|
||||
nsILayoutHistoryState* aFrameState,
|
||||
bool aAllowLazyConstruction)
|
||||
bool aAllowLazyConstruction,
|
||||
TreeMatchContext* aProvidedTreeMatchContext)
|
||||
{
|
||||
AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
|
||||
NS_PRECONDITION(mUpdateCount != 0,
|
||||
|
|
@ -7839,14 +7879,21 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
|
|||
return;
|
||||
}
|
||||
|
||||
Maybe<TreeMatchContext> matchContext;
|
||||
if (!aProvidedTreeMatchContext) {
|
||||
matchContext.emplace(mDocument, TreeMatchContext::ForFrameConstruction);
|
||||
// We use GetParentElementCrossingShadowRoot to handle the case where
|
||||
// aContainer is a ShadowRoot.
|
||||
matchContext->InitAncestors(aStartChild->GetParentElementCrossingShadowRoot());
|
||||
}
|
||||
nsFrameConstructorState state(mPresShell,
|
||||
aProvidedTreeMatchContext
|
||||
? *aProvidedTreeMatchContext
|
||||
: *matchContext,
|
||||
GetAbsoluteContainingBlock(insertion.mParentFrame, FIXED_POS),
|
||||
GetAbsoluteContainingBlock(insertion.mParentFrame, ABS_POS),
|
||||
GetFloatContainingBlock(insertion.mParentFrame),
|
||||
do_AddRef(aFrameState));
|
||||
// We use GetParentElementCrossingShadowRoot to handle the case where
|
||||
// aContainer is a ShadowRoot.
|
||||
state.mTreeMatchContext.InitAncestors(aStartChild->GetParentElementCrossingShadowRoot());
|
||||
|
||||
// Recover state for the containing block - we need to know if
|
||||
// it has :first-letter or :first-line style applied to it. The
|
||||
|
|
@ -8695,7 +8742,9 @@ nsCSSFrameConstructor::CreateContinuingTableFrame(nsIPresShell* aPresShell,
|
|||
// Replicate the header/footer frame.
|
||||
nsTableRowGroupFrame* headerFooterFrame;
|
||||
nsFrameItems childItems;
|
||||
TreeMatchContext matchContext(mDocument, TreeMatchContext::ForFrameConstruction);
|
||||
nsFrameConstructorState state(mPresShell,
|
||||
matchContext,
|
||||
GetAbsoluteContainingBlock(newFrame, FIXED_POS),
|
||||
GetAbsoluteContainingBlock(newFrame, ABS_POS),
|
||||
nullptr);
|
||||
|
|
@ -8969,7 +9018,10 @@ nsCSSFrameConstructor::ReplicateFixedFrames(nsPageContentFrame* aParentFrame)
|
|||
// This should not normally be possible (because fixed-pos elements should
|
||||
// be absolute containers) but fixed-pos tables currently aren't abs-pos
|
||||
// containers.
|
||||
nsFrameConstructorState state(mPresShell, aParentFrame,
|
||||
TreeMatchContext matchContext(mDocument, TreeMatchContext::ForFrameConstruction);
|
||||
nsFrameConstructorState state(mPresShell,
|
||||
matchContext,
|
||||
aParentFrame,
|
||||
nullptr,
|
||||
mRootElementFrame);
|
||||
state.mCreatingExtraFrames = true;
|
||||
|
|
@ -11109,7 +11161,10 @@ nsCSSFrameConstructor::CreateLetterFrame(nsContainerFrame* aBlockFrame,
|
|||
|
||||
NS_ASSERTION(aBlockContinuation == GetFloatContainingBlock(aParentFrame),
|
||||
"Containing block is confused");
|
||||
TreeMatchContext matchContext(mDocument,
|
||||
TreeMatchContext::ForFrameConstruction);
|
||||
nsFrameConstructorState state(mPresShell,
|
||||
matchContext,
|
||||
GetAbsoluteContainingBlock(aParentFrame, FIXED_POS),
|
||||
GetAbsoluteContainingBlock(aParentFrame, ABS_POS),
|
||||
aBlockContinuation);
|
||||
|
|
@ -11485,7 +11540,10 @@ nsCSSFrameConstructor::CreateListBoxContent(nsContainerFrame* aParentFrame,
|
|||
// Construct a new frame
|
||||
if (nullptr != aParentFrame) {
|
||||
nsFrameItems frameItems;
|
||||
nsFrameConstructorState state(mPresShell, GetAbsoluteContainingBlock(aParentFrame, FIXED_POS),
|
||||
TreeMatchContext matchContext(mDocument, TreeMatchContext::ForFrameConstruction);
|
||||
nsFrameConstructorState state(mPresShell,
|
||||
matchContext,
|
||||
GetAbsoluteContainingBlock(aParentFrame, FIXED_POS),
|
||||
GetAbsoluteContainingBlock(aParentFrame, ABS_POS),
|
||||
GetFloatContainingBlock(aParentFrame),
|
||||
do_AddRef(mTempFrameTreeState.get()));
|
||||
|
|
@ -12376,7 +12434,8 @@ nsCSSFrameConstructor::GenerateChildFrames(nsContainerFrame* aFrame)
|
|||
BeginUpdate();
|
||||
|
||||
nsFrameItems childItems;
|
||||
nsFrameConstructorState state(mPresShell, nullptr, nullptr, nullptr);
|
||||
TreeMatchContext matchContext(mDocument, TreeMatchContext::ForFrameConstruction);
|
||||
nsFrameConstructorState state(mPresShell, matchContext, nullptr, nullptr, nullptr);
|
||||
// We don't have a parent frame with a pending binding constructor here,
|
||||
// so no need to worry about ordering of the kids' constructors with it.
|
||||
// Pass null for the PendingBinding.
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue