Bug 1020244 - Ability to insert AnonymousContent nodes in the canvasFrame via a chrome-only Document API; r=smaug; r=roc; r=ehsan

This commit is contained in:
Patrick Brosset 2014-10-28 11:15:25 +01:00
parent f510bc6ce1
commit 159aa067df
10 changed files with 183 additions and 4 deletions

View File

@ -33,6 +33,12 @@ AnonymousContent::GetContentNode()
return mContentNode;
}
void
AnonymousContent::SetContentNode(Element* aContentNode)
{
mContentNode = aContentNode;
}
void
AnonymousContent::SetTextContentForElement(const nsAString& aElementId,
const nsAString& aText,

View File

@ -26,6 +26,7 @@ public:
explicit AnonymousContent(Element* aContentNode);
nsCOMPtr<Element> GetContentNode();
void SetContentNode(Element* aContentNode);
JSObject* WrapObject(JSContext* aCx);
// WebIDL methods

View File

@ -68,6 +68,7 @@
#include "nsIServiceManager.h"
#include "nsIServiceWorkerManager.h"
#include "nsCanvasFrame.h"
#include "nsContentCID.h"
#include "nsError.h"
#include "nsPresShell.h"
@ -182,6 +183,7 @@
#include "nsWrapperCacheInlines.h"
#include "nsSandboxFlags.h"
#include "nsIAppsService.h"
#include "mozilla/dom/AnonymousContent.h"
#include "mozilla/dom/AnimationTimeline.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/DocumentFragment.h"
@ -1986,6 +1988,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRegistry)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContents)
// Traverse all our nsCOMArrays.
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets)
@ -5138,6 +5141,78 @@ nsDocument::StyleRuleRemoved(nsIStyleSheet* aSheet,
#undef DO_STYLESHEET_NOTIFICATION
already_AddRefed<AnonymousContent>
nsIDocument::InsertAnonymousContent(Element& aElement, ErrorResult& aRv)
{
nsIPresShell* shell = GetShell();
if (!shell || !shell->GetCanvasFrame()) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
nsCOMPtr<Element> container = shell->GetCanvasFrame()
->GetCustomContentContainer();
if (!container) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
// Clone the node to avoid returning a direct reference
nsCOMPtr<nsINode> clonedElement = aElement.CloneNode(true, aRv);
if (aRv.Failed()) {
return nullptr;
}
// Insert the element into the container
nsresult rv;
rv = container->AppendChildTo(clonedElement->AsContent(), true);
if (NS_FAILED(rv)) {
return nullptr;
}
nsRefPtr<AnonymousContent> anonymousContent =
new AnonymousContent(clonedElement->AsElement());
mAnonymousContents.AppendElement(anonymousContent);
return anonymousContent.forget();
}
void
nsIDocument::RemoveAnonymousContent(AnonymousContent& aContent,
ErrorResult& aRv)
{
nsIPresShell* shell = GetShell();
if (!shell) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return;
}
nsCOMPtr<Element> container = shell->GetCanvasFrame()
->GetCustomContentContainer();
if (!container) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return;
}
// Iterate over know customContents to get and remove the right one
for (int32_t i = mAnonymousContents.Length() - 1; i >= 0; --i) {
if (mAnonymousContents[i] == &aContent) {
// Get the node from the customContent
nsCOMPtr<Element> node = aContent.GetContentNode();
// Remove the entry in mAnonymousContents
mAnonymousContents.RemoveElementAt(i);
// Remove the node from its container
container->RemoveChild(*node, aRv);
if (aRv.Failed()) {
return;
}
break;
}
}
}
//
// nsIDOMDocument interface

View File

@ -92,6 +92,7 @@ class ImageLoader;
namespace dom {
class AnimationTimeline;
class AnonymousContent;
class Attr;
class BoxObject;
class CDATASection;
@ -715,6 +716,15 @@ public:
return mDidDocumentOpen;
}
already_AddRefed<mozilla::dom::AnonymousContent>
InsertAnonymousContent(mozilla::dom::Element& aElement,
mozilla::ErrorResult& aError);
void RemoveAnonymousContent(mozilla::dom::AnonymousContent& aContent,
mozilla::ErrorResult& aError);
nsTArray<nsRefPtr<mozilla::dom::AnonymousContent>>& GetAnonymousContents() {
return mAnonymousContents;
}
protected:
virtual Element *GetRootElementInternal() const = 0;
@ -2747,6 +2757,8 @@ protected:
nsRefPtr<mozilla::dom::XPathEvaluator> mXPathEvaluator;
nsTArray<nsRefPtr<mozilla::dom::AnonymousContent>> mAnonymousContents;
uint32_t mBlockDOMContentLoaded;
bool mDidFireDOMContentLoaded:1;
};

View File

@ -355,6 +355,30 @@ partial interface Document {
[ChromeOnly] readonly attribute boolean isSrcdocDocument;
};
/**
* Chrome document anonymous content management.
* This is a Chrome-only API that allows inserting fixed positioned anonymous
* content on top of the current page displayed in the document.
* The supplied content is cloned and inserted into the document's CanvasFrame.
* Note that this only works for HTML documents.
*/
partial interface Document {
/**
* Deep-clones the provided element and inserts it into the CanvasFrame.
* Returns an AnonymousContent instance that can be used to manipulate the
* inserted element.
*/
[ChromeOnly, NewObject, Throws]
AnonymousContent insertAnonymousContent(Element aElement);
/**
* Removes the element inserted into the CanvasFrame given an AnonymousContent
* instance.
*/
[ChromeOnly, Throws]
void removeAnonymousContent(AnonymousContent aContent);
};
Document implements XPathEvaluator;
Document implements GlobalEventHandlers;
Document implements TouchEventHandlers;

View File

@ -21,6 +21,7 @@
#include "nsFrameManager.h"
#include "gfxPlatform.h"
#include "nsPrintfCString.h"
#include "mozilla/dom/AnonymousContent.h"
// for touchcaret
#include "nsContentList.h"
#include "nsContentCreatorFunctions.h"
@ -35,6 +36,7 @@
//#define DEBUG_CANVAS_FOCUS
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::layout;
using namespace mozilla::gfx;
@ -63,7 +65,7 @@ nsCanvasFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
ErrorResult er;
// We won't create touch caret element if preference is not enabled.
if (PresShell::TouchCaretPrefEnabled()) {
nsRefPtr<dom::NodeInfo> nodeInfo;
nsRefPtr<NodeInfo> nodeInfo;
// Create and append touch caret frame.
nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::div, nullptr,
@ -72,7 +74,7 @@ nsCanvasFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
rv = NS_NewHTMLElement(getter_AddRefs(mTouchCaretElement), nodeInfo.forget(),
mozilla::dom::NOT_FROM_PARSER);
NOT_FROM_PARSER);
NS_ENSURE_SUCCESS(rv, rv);
aElements.AppendElement(mTouchCaretElement);
@ -103,6 +105,23 @@ nsCanvasFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
NS_ENSURE_SUCCESS(rv, rv);
}
// Create the custom content container.
mCustomContentContainer = doc->CreateHTMLElement(nsGkAtoms::div);
aElements.AppendElement(mCustomContentContainer);
// XXX add :moz-native-anonymous or will that be automatically set?
rv = mCustomContentContainer->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
NS_LITERAL_STRING("moz-custom-content-container"),
true);
NS_ENSURE_SUCCESS(rv, rv);
// Append all existing AnonymousContent nodes stored at document level if any.
int32_t anonymousContentCount = doc->GetAnonymousContents().Length();
for (int32_t i = 0; i < anonymousContentCount; ++i) {
nsCOMPtr<Element> node = doc->GetAnonymousContents()[i]->GetContentNode();
mCustomContentContainer->AppendChildTo(node->AsContent(), true);
}
return NS_OK;
}
@ -120,6 +139,8 @@ nsCanvasFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements, uint32
if (mSelectionCaretsEndElement) {
aElements.AppendElement(mSelectionCaretsEndElement);
}
aElements.AppendElement(mCustomContentContainer);
}
void
@ -134,6 +155,22 @@ nsCanvasFrame::DestroyFrom(nsIFrame* aDestructRoot)
nsContentUtils::DestroyAnonymousContent(&mTouchCaretElement);
nsContentUtils::DestroyAnonymousContent(&mSelectionCaretsStartElement);
nsContentUtils::DestroyAnonymousContent(&mSelectionCaretsEndElement);
// Elements inserted in the custom content container have the same lifetime as
// the document, so before destroying the container, make sure to keep a clone
// of each of them at document level so they can be re-appended on reframe.
if (mCustomContentContainer) {
nsCOMPtr<nsIDocument> doc = mContent->OwnerDoc();
ErrorResult rv;
for (int32_t i = doc->GetAnonymousContents().Length() - 1; i >= 0; --i) {
AnonymousContent* content = doc->GetAnonymousContents()[i];
nsCOMPtr<nsINode> clonedElement = content->GetContentNode()->CloneNode(true, rv);
content->SetContentNode(clonedElement->AsElement());
}
}
nsContentUtils::DestroyAnonymousContent(&mCustomContentContainer);
nsContainerFrame::DestroyFrom(aDestructRoot);
}

View File

@ -87,6 +87,11 @@ public:
return mSelectionCaretsEndElement;
}
mozilla::dom::Element* GetCustomContentContainer() const
{
return mCustomContentContainer;
}
/** SetHasFocus tells the CanvasFrame to draw with focus ring
* @param aHasFocus true to show focus ring, false to hide it
*/
@ -138,6 +143,7 @@ protected:
nsCOMPtr<mozilla::dom::Element> mTouchCaretElement;
nsCOMPtr<mozilla::dom::Element> mSelectionCaretsStartElement;
nsCOMPtr<mozilla::dom::Element> mSelectionCaretsEndElement;
nsCOMPtr<mozilla::dom::Element> mCustomContentContainer;
};
/**

View File

@ -6914,8 +6914,12 @@ nsIFrame::GetFrameFromDirection(nsDirection aDirection, bool aVisual,
frameTraversal->Prev();
traversedFrame = frameTraversal->CurrentItem();
if (!traversedFrame)
// Skip anonymous elements
if (!traversedFrame ||
traversedFrame->GetContent()->IsRootOfNativeAnonymousSubtree())
return NS_ERROR_FAILURE;
traversedFrame->IsSelectable(&selectable, nullptr);
} // while (!selectable)

View File

@ -2410,7 +2410,7 @@ public:
virtual nsresult PeekOffset(nsPeekOffsetStruct *aPos);
/**
* called to find the previous/next selectable leaf frame.
* called to find the previous/next non-anonymous selectable leaf frame.
* @param aDirection [in] the direction to move in (eDirPrevious or eDirNext)
* @param aVisual [in] whether bidi caret behavior is visual (true) or logical (false)
* @param aJumpLines [in] whether to allow jumping across line boundaries

View File

@ -390,3 +390,17 @@ div:-moz-native-anonymous.moz-selectioncaret-right.hidden {
margin: 0px;
visibility: hidden;
}
/* Custom content container in the CanvasFrame, fixed positioned on top of
everything else, not reacting to pointer events. */
div:-moz-native-anonymous.moz-custom-content-container {
pointer-events: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 2147483648;
}