mirror of
https://github.com/roytam1/UXP.git
synced 2026-05-26 14:54:25 +00:00
Merge remote-tracking branch 'origin/tracking' into custom
This commit is contained in:
@@ -2208,10 +2208,10 @@ exports.CSS_PROPERTIES = {
|
||||
"values": [
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"auto",
|
||||
"calc",
|
||||
"content",
|
||||
"fit-content",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
@@ -2231,10 +2231,10 @@ exports.CSS_PROPERTIES = {
|
||||
"values": [
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"auto",
|
||||
"calc",
|
||||
"content",
|
||||
"fit-content",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
@@ -3282,7 +3282,6 @@ exports.CSS_PROPERTIES = {
|
||||
"-moz-crisp-edges",
|
||||
"-moz-deck",
|
||||
"-moz-element",
|
||||
"-moz-fit-content",
|
||||
"-moz-grid",
|
||||
"-moz-grid-group",
|
||||
"-moz-grid-line",
|
||||
@@ -3424,6 +3423,7 @@ exports.CSS_PROPERTIES = {
|
||||
"extra-expanded",
|
||||
"fill",
|
||||
"fill-box",
|
||||
"fit-content",
|
||||
"fixed",
|
||||
"flat",
|
||||
"flex",
|
||||
@@ -6000,10 +6000,10 @@ exports.CSS_PROPERTIES = {
|
||||
"values": [
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"auto",
|
||||
"calc",
|
||||
"content",
|
||||
"fit-content",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
@@ -6023,10 +6023,10 @@ exports.CSS_PROPERTIES = {
|
||||
"values": [
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"auto",
|
||||
"calc",
|
||||
"content",
|
||||
"fit-content",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
@@ -6816,9 +6816,9 @@ exports.CSS_PROPERTIES = {
|
||||
"values": [
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"auto",
|
||||
"calc",
|
||||
"fit-content",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
@@ -6903,9 +6903,9 @@ exports.CSS_PROPERTIES = {
|
||||
"values": [
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"auto",
|
||||
"calc",
|
||||
"fit-content",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
@@ -7782,8 +7782,8 @@ exports.CSS_PROPERTIES = {
|
||||
"values": [
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"calc",
|
||||
"fit-content",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
@@ -7804,8 +7804,8 @@ exports.CSS_PROPERTIES = {
|
||||
"values": [
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"calc",
|
||||
"fit-content",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
@@ -7826,8 +7826,8 @@ exports.CSS_PROPERTIES = {
|
||||
"values": [
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"calc",
|
||||
"fit-content",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
@@ -7866,9 +7866,9 @@ exports.CSS_PROPERTIES = {
|
||||
"values": [
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"auto",
|
||||
"calc",
|
||||
"fit-content",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
@@ -7888,9 +7888,9 @@ exports.CSS_PROPERTIES = {
|
||||
"values": [
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"auto",
|
||||
"calc",
|
||||
"fit-content",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
@@ -7910,9 +7910,9 @@ exports.CSS_PROPERTIES = {
|
||||
"values": [
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"auto",
|
||||
"calc",
|
||||
"fit-content",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
@@ -9710,9 +9710,9 @@ exports.CSS_PROPERTIES = {
|
||||
"values": [
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"auto",
|
||||
"calc",
|
||||
"fit-content",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
@@ -9826,6 +9826,7 @@ exports.CSS_PROPERTIES = {
|
||||
exports.PSEUDO_ELEMENTS = [
|
||||
":after",
|
||||
":before",
|
||||
":slotted",
|
||||
":backdrop",
|
||||
":first-letter",
|
||||
":first-line",
|
||||
|
||||
@@ -169,7 +169,7 @@ DocumentOrShadowRoot::GetFullscreenElement()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Element* element = AsNode().OwnerDoc()->FullScreenStackTop();
|
||||
Element* element = AsNode().OwnerDoc()->GetUnretargetedFullScreenElement();
|
||||
NS_ASSERTION(!element ||
|
||||
element->State().HasState(NS_EVENT_STATE_FULL_SCREEN),
|
||||
"Fullscreen element should have fullscreen styles applied");
|
||||
|
||||
+85
-20
@@ -738,20 +738,40 @@ nsContentUtils::InitializeModifierStrings()
|
||||
sModifierSeparator = new nsString(modifierSeparator);
|
||||
}
|
||||
|
||||
mozilla::EventClassID
|
||||
nsContentUtils::GetEventClassIDFromMessage(EventMessage aEventMessage)
|
||||
{
|
||||
switch (aEventMessage) {
|
||||
#define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \
|
||||
case message_: return struct_;
|
||||
#include "mozilla/EventNameList.h"
|
||||
#undef MESSAGE_TO_EVENT
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid event message?");
|
||||
return eBasicEventClass;
|
||||
}
|
||||
}
|
||||
|
||||
static nsIAtom*
|
||||
GetEventTypeFromMessage(EventMessage aEventMessage)
|
||||
{
|
||||
switch (aEventMessage) {
|
||||
#define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \
|
||||
case message_: return nsGkAtoms::on##name_;
|
||||
#include "mozilla/EventNameList.h"
|
||||
#undef MESSAGE_TO_EVENT
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Because of SVG/SMIL we have several atoms mapped to the same
|
||||
// id, but we can rely on MESSAGE_TO_EVENT to map id to only one atom.
|
||||
static bool
|
||||
ShouldAddEventToStringEventTable(const EventNameMapping& aMapping)
|
||||
{
|
||||
switch(aMapping.mMessage) {
|
||||
#define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \
|
||||
case message_: return nsGkAtoms::on##name_ == aMapping.mAtom;
|
||||
#include "mozilla/EventNameList.h"
|
||||
#undef MESSAGE_TO_EVENT
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
MOZ_ASSERT(aMapping.mAtom);
|
||||
return GetEventTypeFromMessage(aMapping.mMessage) == aMapping.mAtom;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -4149,10 +4169,12 @@ nsContentUtils::DispatchEventForPreloadURI(nsIDOMNode* aNode,
|
||||
|
||||
// static
|
||||
nsresult
|
||||
nsContentUtils::DispatchTrustedEvent(nsIDocument* aDoc, nsISupports* aTarget,
|
||||
nsContentUtils::DispatchTrustedEvent(nsIDocument* aDoc,
|
||||
nsISupports* aTarget,
|
||||
const nsAString& aEventName,
|
||||
bool aCanBubble, bool aCancelable,
|
||||
bool *aDefaultAction)
|
||||
bool aCanBubble,
|
||||
bool aCancelable,
|
||||
bool* aDefaultAction)
|
||||
{
|
||||
return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
|
||||
true, aDefaultAction);
|
||||
@@ -4160,10 +4182,12 @@ nsContentUtils::DispatchTrustedEvent(nsIDocument* aDoc, nsISupports* aTarget,
|
||||
|
||||
// static
|
||||
nsresult
|
||||
nsContentUtils::DispatchUntrustedEvent(nsIDocument* aDoc, nsISupports* aTarget,
|
||||
nsContentUtils::DispatchUntrustedEvent(nsIDocument* aDoc,
|
||||
nsISupports* aTarget,
|
||||
const nsAString& aEventName,
|
||||
bool aCanBubble, bool aCancelable,
|
||||
bool *aDefaultAction)
|
||||
bool aCanBubble,
|
||||
bool aCancelable,
|
||||
bool* aDefaultAction)
|
||||
{
|
||||
return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
|
||||
false, aDefaultAction);
|
||||
@@ -4171,10 +4195,13 @@ nsContentUtils::DispatchUntrustedEvent(nsIDocument* aDoc, nsISupports* aTarget,
|
||||
|
||||
// static
|
||||
nsresult
|
||||
nsContentUtils::DispatchEvent(nsIDocument* aDoc, nsISupports* aTarget,
|
||||
nsContentUtils::DispatchEvent(nsIDocument* aDoc,
|
||||
nsISupports* aTarget,
|
||||
const nsAString& aEventName,
|
||||
bool aCanBubble, bool aCancelable,
|
||||
bool aTrusted, bool *aDefaultAction,
|
||||
bool aCanBubble,
|
||||
bool aCancelable,
|
||||
bool aTrusted,
|
||||
bool* aDefaultAction,
|
||||
bool aOnlyChromeDispatch)
|
||||
{
|
||||
nsCOMPtr<nsIDOMEvent> event;
|
||||
@@ -4189,12 +4216,50 @@ nsContentUtils::DispatchEvent(nsIDocument* aDoc, nsISupports* aTarget,
|
||||
return target->DispatchEvent(event, aDefaultAction ? aDefaultAction : &dummy);
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
nsContentUtils::DispatchEvent(nsIDocument* aDoc,
|
||||
nsISupports* aTarget,
|
||||
WidgetEvent& aEvent,
|
||||
EventMessage aEventMessage,
|
||||
bool aCanBubble,
|
||||
bool aCancelable,
|
||||
bool aTrusted,
|
||||
bool* aDefaultAction,
|
||||
bool aOnlyChromeDispatch)
|
||||
{
|
||||
MOZ_ASSERT_IF(aOnlyChromeDispatch, aTrusted);
|
||||
|
||||
nsCOMPtr<EventTarget> target(do_QueryInterface(aTarget));
|
||||
|
||||
aEvent.mTime = PR_Now();
|
||||
|
||||
aEvent.mSpecifiedEventType = GetEventTypeFromMessage(aEventMessage);
|
||||
aEvent.SetDefaultComposed();
|
||||
aEvent.SetDefaultComposedInNativeAnonymousContent();
|
||||
|
||||
aEvent.mFlags.mBubbles = aCanBubble;
|
||||
aEvent.mFlags.mCancelable = aCancelable;
|
||||
aEvent.mFlags.mOnlyChromeDispatch = aOnlyChromeDispatch;
|
||||
|
||||
aEvent.mTarget = target;
|
||||
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
nsresult rv = EventDispatcher::DispatchDOMEvent(target, &aEvent, nullptr,
|
||||
nullptr, &status);
|
||||
if (aDefaultAction) {
|
||||
*aDefaultAction = (status != nsEventStatus_eConsumeNoDefault);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentUtils::DispatchChromeEvent(nsIDocument *aDoc,
|
||||
nsISupports *aTarget,
|
||||
const nsAString& aEventName,
|
||||
bool aCanBubble, bool aCancelable,
|
||||
bool *aDefaultAction)
|
||||
bool aCanBubble,
|
||||
bool aCancelable,
|
||||
bool* aDefaultAction)
|
||||
{
|
||||
|
||||
nsCOMPtr<nsIDOMEvent> event;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "js/TypeDecls.h"
|
||||
#include "js/Value.h"
|
||||
#include "js/RootingAPI.h"
|
||||
#include "mozilla/BasicEvents.h"
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/GuardObjects.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
@@ -1208,6 +1209,33 @@ public:
|
||||
bool aCancelable,
|
||||
bool *aDefaultAction = nullptr);
|
||||
|
||||
/**
|
||||
* This method creates and dispatches a trusted event using an event message.
|
||||
* @param aDoc The document which will be used to create the event.
|
||||
* @param aTarget The target of the event, should be QIable to
|
||||
* EventTarget.
|
||||
* @param aEventMessage The event message.
|
||||
* @param aCanBubble Whether the event can bubble.
|
||||
* @param aCancelable Is the event cancelable.
|
||||
* @param aDefaultAction Set to true if default action should be taken,
|
||||
* see nsIDOMEventTarget::DispatchEvent.
|
||||
*/
|
||||
template <class WidgetEventType>
|
||||
static nsresult DispatchTrustedEvent(nsIDocument* aDoc,
|
||||
nsISupports* aTarget,
|
||||
mozilla::EventMessage aEventMessage,
|
||||
bool aCanBubble,
|
||||
bool aCancelable,
|
||||
bool *aDefaultAction = nullptr,
|
||||
bool aOnlyChromeDispatch = false)
|
||||
{
|
||||
WidgetEventType event(true, aEventMessage);
|
||||
MOZ_ASSERT(GetEventClassIDFromMessage(aEventMessage) == event.mClass);
|
||||
return DispatchEvent(aDoc, aTarget, event, aEventMessage,
|
||||
aCanBubble, aCancelable, true,
|
||||
aDefaultAction, aOnlyChromeDispatch);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method creates and dispatches a untrusted event.
|
||||
* Works only with events which can be created by calling
|
||||
@@ -1228,6 +1256,33 @@ public:
|
||||
bool aCancelable,
|
||||
bool *aDefaultAction = nullptr);
|
||||
|
||||
/**
|
||||
* This method creates and dispatches a untrusted event using an event message.
|
||||
* @param aDoc The document which will be used to create the event.
|
||||
* @param aTarget The target of the event, should be QIable to
|
||||
* EventTarget.
|
||||
* @param aEventMessage The event message.
|
||||
* @param aCanBubble Whether the event can bubble.
|
||||
* @param aCancelable Is the event cancelable.
|
||||
* @param aDefaultAction Set to true if default action should be taken,
|
||||
* see nsIDOMEventTarget::DispatchEvent.
|
||||
*/
|
||||
template <class WidgetEventType>
|
||||
static nsresult DispatchUntrustedEvent(nsIDocument* aDoc,
|
||||
nsISupports* aTarget,
|
||||
mozilla::EventMessage aEventMessage,
|
||||
bool aCanBubble,
|
||||
bool aCancelable,
|
||||
bool *aDefaultAction = nullptr,
|
||||
bool aOnlyChromeDispatch = false)
|
||||
{
|
||||
WidgetEventType event(false, aEventMessage);
|
||||
MOZ_ASSERT(GetEventClassIDFromMessage(aEventMessage) == event.mClass);
|
||||
return DispatchEvent(aDoc, aTarget, event, aEventMessage,
|
||||
aCanBubble, aCancelable, false,
|
||||
aDefaultAction, aOnlyChromeDispatch);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method creates and dispatches a trusted event to the chrome
|
||||
* event handler (the parent object of the DOM Window in the event target
|
||||
@@ -2883,6 +2938,16 @@ private:
|
||||
bool *aDefaultAction = nullptr,
|
||||
bool aOnlyChromeDispatch = false);
|
||||
|
||||
static nsresult DispatchEvent(nsIDocument* aDoc,
|
||||
nsISupports* aTarget,
|
||||
mozilla::WidgetEvent& aWidgetEvent,
|
||||
mozilla::EventMessage aEventMessage,
|
||||
bool aCanBubble,
|
||||
bool aCancelable,
|
||||
bool aTrusted,
|
||||
bool *aDefaultAction = nullptr,
|
||||
bool aOnlyChromeDispatch = false);
|
||||
|
||||
static void InitializeModifierStrings();
|
||||
|
||||
static void DropFragmentParsers();
|
||||
@@ -2894,6 +2959,9 @@ private:
|
||||
static void* AllocClassMatchingInfo(nsINode* aRootNode,
|
||||
const nsString* aClasses);
|
||||
|
||||
static mozilla::EventClassID
|
||||
GetEventClassIDFromMessage(mozilla::EventMessage aEventMessage);
|
||||
|
||||
// Fills in aInfo with the tokens from the supplied autocomplete attribute.
|
||||
static AutocompleteAttrState InternalSerializeAutocompleteAttribute(const nsAttrValue* aAttrVal,
|
||||
mozilla::dom::AutocompleteInfo& aInfo);
|
||||
|
||||
+141
-76
@@ -159,6 +159,7 @@
|
||||
#include "nsEscape.h"
|
||||
#include "nsObjectLoadingContent.h"
|
||||
#include "nsHtml5TreeOpExecutor.h"
|
||||
#include "mozilla/dom/HTMLDialogElement.h"
|
||||
#include "mozilla/dom/HTMLLinkElement.h"
|
||||
#include "mozilla/dom/HTMLMediaElement.h"
|
||||
#include "mozilla/dom/HTMLIFrameElement.h"
|
||||
@@ -8481,7 +8482,7 @@ nsDocument::OnPageHide(bool aPersisted,
|
||||
EnumerateActivityObservers(NotifyActivityChanged, nullptr);
|
||||
|
||||
ClearPendingFullscreenRequests(this);
|
||||
if (FullScreenStackTop()) {
|
||||
if (GetUnretargetedFullScreenElement()) {
|
||||
// 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
|
||||
@@ -10112,6 +10113,19 @@ nsDocument::GetFullscreenRoot()
|
||||
return root;
|
||||
}
|
||||
|
||||
size_t
|
||||
nsDocument::CountFullscreenElements() const {
|
||||
size_t count = 0;
|
||||
for (const nsWeakPtr& ptr : mTopLayer) {
|
||||
if (nsCOMPtr<Element> elem = do_QueryReferent(ptr)) {
|
||||
if (elem->State().HasState(NS_EVENT_STATE_FULL_SCREEN)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::SetFullscreenRoot(nsIDocument* aRoot)
|
||||
{
|
||||
@@ -10125,6 +10139,18 @@ nsDocument::MozCancelFullScreen()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void nsDocument::TryCancelDialog() {
|
||||
// Check if the document is blocked by modal dialog
|
||||
for (const nsWeakPtr& weakPtr : Reversed(mTopLayer)) {
|
||||
nsCOMPtr<Element> element(do_QueryReferent(weakPtr));
|
||||
if (HTMLDialogElement* dialog =
|
||||
HTMLDialogElement::FromContentOrNull(element)) {
|
||||
dialog->CancelDialog();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsIDocument::ExitFullscreen()
|
||||
{
|
||||
@@ -10175,7 +10201,7 @@ nsIDocument::AsyncExitFullscreen(nsIDocument* aDoc)
|
||||
static bool
|
||||
CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData)
|
||||
{
|
||||
if (aDoc->FullScreenStackTop()) {
|
||||
if (aDoc->GetUnretargetedFullScreenElement()) {
|
||||
uint32_t* count = static_cast<uint32_t*>(aData);
|
||||
(*count)++;
|
||||
}
|
||||
@@ -10195,7 +10221,7 @@ nsDocument::IsFullscreenLeaf()
|
||||
{
|
||||
// A fullscreen leaf document is fullscreen, and has no fullscreen
|
||||
// subdocuments.
|
||||
if (!FullScreenStackTop()) {
|
||||
if (!GetUnretargetedFullScreenElement()) {
|
||||
return false;
|
||||
}
|
||||
return CountFullscreenSubDocuments(this) == 0;
|
||||
@@ -10204,11 +10230,11 @@ nsDocument::IsFullscreenLeaf()
|
||||
static bool
|
||||
ResetFullScreen(nsIDocument* aDocument, void* aData)
|
||||
{
|
||||
if (aDocument->FullScreenStackTop()) {
|
||||
if (aDocument->GetUnretargetedFullScreenElement()) {
|
||||
NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1,
|
||||
"Should have at most 1 fullscreen subdocument.");
|
||||
static_cast<nsDocument*>(aDocument)->CleanupFullscreenState();
|
||||
NS_ASSERTION(!aDocument->FullScreenStackTop(),
|
||||
NS_ASSERTION(!aDocument->GetUnretargetedFullScreenElement(),
|
||||
"Should reset full-screen");
|
||||
auto changed = reinterpret_cast<nsCOMArray<nsIDocument>*>(aData);
|
||||
changed->AppendElement(aDocument);
|
||||
@@ -10256,7 +10282,7 @@ nsIDocument::ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc)
|
||||
UnlockPointer();
|
||||
|
||||
nsCOMPtr<nsIDocument> root = aMaybeNotARootDoc->GetFullscreenRoot();
|
||||
if (!root || !root->FullScreenStackTop()) {
|
||||
if (!root || !root->GetUnretargetedFullScreenElement()) {
|
||||
// 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
|
||||
@@ -10285,7 +10311,7 @@ nsIDocument::ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc)
|
||||
DispatchFullScreenChange(changed[changed.Length() - i - 1]);
|
||||
}
|
||||
|
||||
NS_ASSERTION(!root->FullScreenStackTop(),
|
||||
NS_ASSERTION(!root->GetUnretargetedFullScreenElement(),
|
||||
"Fullscreen root should no longer be a fullscreen doc...");
|
||||
|
||||
// Move the top-level window out of fullscreen mode.
|
||||
@@ -10302,7 +10328,7 @@ GetFullscreenLeaf(nsIDocument* aDoc, void* aData)
|
||||
nsIDocument** result = static_cast<nsIDocument**>(aData);
|
||||
*result = aDoc;
|
||||
return false;
|
||||
} else if (aDoc->FullScreenStackTop()) {
|
||||
} else if (aDoc->GetUnretargetedFullScreenElement()) {
|
||||
aDoc->EnumerateSubDocuments(GetFullscreenLeaf, aData);
|
||||
}
|
||||
return true;
|
||||
@@ -10321,7 +10347,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->FullScreenStackTop()) {
|
||||
if (!root->GetUnretargetedFullScreenElement()) {
|
||||
return nullptr;
|
||||
}
|
||||
GetFullscreenLeaf(root, &leaf);
|
||||
@@ -10331,10 +10357,10 @@ GetFullscreenLeaf(nsIDocument* aDoc)
|
||||
void
|
||||
nsDocument::RestorePreviousFullScreenState()
|
||||
{
|
||||
NS_ASSERTION(!FullScreenStackTop() || !FullscreenRoots::IsEmpty(),
|
||||
NS_ASSERTION(!GetUnretargetedFullScreenElement() || !FullscreenRoots::IsEmpty(),
|
||||
"Should have at least 1 fullscreen root when fullscreen!");
|
||||
|
||||
if (!FullScreenStackTop() || !GetWindow() || FullscreenRoots::IsEmpty()) {
|
||||
if (!GetUnretargetedFullScreenElement() || !GetWindow() || FullscreenRoots::IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10350,10 +10376,10 @@ nsDocument::RestorePreviousFullScreenState()
|
||||
// Collect all ancestor documents which we are going to change.
|
||||
for (; doc; doc = doc->GetParentDocument()) {
|
||||
nsDocument* theDoc = static_cast<nsDocument*>(doc);
|
||||
MOZ_ASSERT(!theDoc->mFullScreenStack.IsEmpty(),
|
||||
Element* top = theDoc->GetUnretargetedFullScreenElement();
|
||||
MOZ_ASSERT(!top,
|
||||
"Ancestor of fullscreen document must also be in fullscreen");
|
||||
if (doc != this) {
|
||||
Element* top = theDoc->FullScreenStackTop();
|
||||
if (top->IsHTMLElement(nsGkAtoms::iframe)) {
|
||||
if (static_cast<HTMLIFrameElement*>(top)->FullscreenFlag()) {
|
||||
// If this is an iframe, and it explicitly requested
|
||||
@@ -10363,14 +10389,14 @@ nsDocument::RestorePreviousFullScreenState()
|
||||
}
|
||||
}
|
||||
exitDocs.AppendElement(theDoc);
|
||||
if (theDoc->mFullScreenStack.Length() > 1) {
|
||||
if (theDoc->CountFullscreenElements() > 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nsDocument* lastDoc = exitDocs.LastElement();
|
||||
if (!lastDoc->GetParentDocument() &&
|
||||
lastDoc->mFullScreenStack.Length() == 1) {
|
||||
size_t fullscreenCount = lastDoc->CountFullscreenElements();
|
||||
if (!lastDoc->GetParentDocument() && fullscreenCount == 1) {
|
||||
// If we are fully exiting fullscreen, don't touch anything here,
|
||||
// just wait for the window to get out from fullscreen first.
|
||||
AskWindowToExitFullscreen(this);
|
||||
@@ -10387,8 +10413,8 @@ nsDocument::RestorePreviousFullScreenState()
|
||||
// The last document will either rollback one fullscreen element, or
|
||||
// completely exit from the fullscreen state as well.
|
||||
nsIDocument* newFullscreenDoc;
|
||||
if (lastDoc->mFullScreenStack.Length() > 1) {
|
||||
lastDoc->FullScreenStackPop();
|
||||
if (fullscreenCount > 1) {
|
||||
lastDoc->UnsetFullscreenElement();
|
||||
newFullscreenDoc = lastDoc;
|
||||
} else {
|
||||
lastDoc->CleanupFullscreenState();
|
||||
@@ -10482,93 +10508,131 @@ ClearFullscreenStateOnElement(Element* aElement)
|
||||
void
|
||||
nsDocument::CleanupFullscreenState()
|
||||
{
|
||||
// Iterate the fullscreen stack and clear the fullscreen states.
|
||||
// Iterate the top layer and clear the fullscreen states.
|
||||
// Since we also need to clear the fullscreen-ancestor state, and
|
||||
// currently fullscreen elements can only be placed in hierarchy
|
||||
// order in the stack, reversely iterating the stack could be more
|
||||
// efficient. NOTE that fullscreen-ancestor state would be removed
|
||||
// in bug 1199529, and the elements may not in hierarchy order
|
||||
// after bug 1195213.
|
||||
for (nsWeakPtr& weakPtr : Reversed(mFullScreenStack)) {
|
||||
if (nsCOMPtr<Element> element = do_QueryReferent(weakPtr)) {
|
||||
ClearFullscreenStateOnElement(element);
|
||||
mTopLayer.RemoveElementsBy([&](const nsWeakPtr& weakPtr) {
|
||||
nsCOMPtr<Element> element(do_QueryReferent(weakPtr));
|
||||
if (!element || !element->IsInComposedDoc() ||
|
||||
element->OwnerDoc() != this) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
mFullScreenStack.Clear();
|
||||
|
||||
if (element->State().HasState(NS_EVENT_STATE_FULL_SCREEN)) {
|
||||
ClearFullscreenStateOnElement(element);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
mFullscreenRoot = nullptr;
|
||||
UpdateViewportScrollbarOverrideForFullscreen(this);
|
||||
}
|
||||
|
||||
bool
|
||||
nsDocument::FullScreenStackPush(Element* aElement)
|
||||
{
|
||||
NS_ASSERTION(aElement, "Must pass non-null to FullScreenStackPush()");
|
||||
Element* top = FullScreenStackTop();
|
||||
if (top == aElement || !aElement) {
|
||||
return false;
|
||||
}
|
||||
EventStateManager::SetFullScreenState(aElement, true);
|
||||
mFullScreenStack.AppendElement(do_GetWeakReference(aElement));
|
||||
NS_ASSERTION(FullScreenStackTop() == aElement, "Should match");
|
||||
void
|
||||
nsDocument::UnsetFullscreenElement() {
|
||||
Element* removedElement = TopLayerPop([](Element* element) -> bool {
|
||||
return element->State().HasState(NS_EVENT_STATE_FULL_SCREEN);
|
||||
});
|
||||
|
||||
MOZ_ASSERT(removedElement->State().HasState(NS_EVENT_STATE_FULL_SCREEN));
|
||||
ClearFullscreenStateOnElement(removedElement);
|
||||
UpdateViewportScrollbarOverrideForFullscreen(this);
|
||||
}
|
||||
|
||||
bool
|
||||
nsDocument::SetFullscreenElement(Element* aElement) {
|
||||
if (TopLayerPush(aElement)) {
|
||||
EventStateManager::SetFullScreenState(aElement, true);
|
||||
UpdateViewportScrollbarOverrideForFullscreen(this);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
nsDocument::TopLayerPush(Element* aElement) {
|
||||
NS_ASSERTION(aElement, "Must pass non-null to TopLayerPush()");
|
||||
auto predictFunc = [&aElement](Element* element) {
|
||||
return element == aElement;
|
||||
};
|
||||
TopLayerPop(predictFunc);
|
||||
|
||||
mTopLayer.AppendElement(do_GetWeakReference(aElement));
|
||||
NS_ASSERTION(GetTopLayerTop() == aElement, "Should match");
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::FullScreenStackPop()
|
||||
{
|
||||
if (mFullScreenStack.IsEmpty()) {
|
||||
return;
|
||||
Element*
|
||||
nsDocument::TopLayerPop(FunctionRef<bool(Element*)> aPredicateFunc) {
|
||||
if (mTopLayer.IsEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ClearFullscreenStateOnElement(FullScreenStackTop());
|
||||
|
||||
// Remove top element. Note the remaining top element in the stack
|
||||
// will not have full-screen style bits set, so we will need to restore
|
||||
// them on the new top element before returning.
|
||||
uint32_t last = mFullScreenStack.Length() - 1;
|
||||
mFullScreenStack.RemoveElementAt(last);
|
||||
// Remove the topmost element that qualifies aPredicate; This
|
||||
// is required is because the top layer contains not only
|
||||
// fullscreen elements, but also dialog elements.
|
||||
Element* removedElement;
|
||||
for (auto i : Reversed(MakeRange(mTopLayer.Length()))) {
|
||||
nsCOMPtr<Element> element(do_QueryReferent(mTopLayer[i]));
|
||||
if (element && aPredicateFunc(element)) {
|
||||
removedElement = element;
|
||||
mTopLayer.RemoveElementAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Pop from the stack null elements (references to elements which have
|
||||
// been GC'd since they were added to the stack) and elements which are
|
||||
// no longer in this document.
|
||||
while (!mFullScreenStack.IsEmpty()) {
|
||||
Element* element = FullScreenStackTop();
|
||||
while (!mTopLayer.IsEmpty()) {
|
||||
Element* element = GetTopLayerTop();
|
||||
if (!element || !element->IsInUncomposedDoc() || element->OwnerDoc() != this) {
|
||||
NS_ASSERTION(!element->State().HasState(NS_EVENT_STATE_FULL_SCREEN),
|
||||
"Should have already removed full-screen styles");
|
||||
uint32_t last = mFullScreenStack.Length() - 1;
|
||||
mFullScreenStack.RemoveElementAt(last);
|
||||
mTopLayer.RemoveElementAt(mTopLayer.Length() - 1);
|
||||
} else {
|
||||
// The top element of the stack is now an in-doc element. Return here.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateViewportScrollbarOverrideForFullscreen(this);
|
||||
return removedElement;
|
||||
}
|
||||
|
||||
Element*
|
||||
nsDocument::FullScreenStackTop()
|
||||
{
|
||||
if (mFullScreenStack.IsEmpty()) {
|
||||
nsDocument::GetTopLayerTop() {
|
||||
if (mTopLayer.IsEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
uint32_t last = mFullScreenStack.Length() - 1;
|
||||
nsCOMPtr<Element> element(do_QueryReferent(mFullScreenStack[last]));
|
||||
NS_ASSERTION(element, "Should have full-screen element!");
|
||||
NS_ASSERTION(element->IsInComposedDoc(), "Full-screen element should be in doc");
|
||||
NS_ASSERTION(element->OwnerDoc() == this, "Full-screen element should be in this doc");
|
||||
uint32_t last = mTopLayer.Length() - 1;
|
||||
nsCOMPtr<Element> element(do_QueryReferent(mTopLayer[last]));
|
||||
NS_ASSERTION(element, "Should have a top layer element!");
|
||||
NS_ASSERTION(element->IsInComposedDoc(), "Top layer element should be in doc");
|
||||
NS_ASSERTION(element->OwnerDoc() == this, "Top layer element should be in this doc");
|
||||
return element;
|
||||
}
|
||||
|
||||
/* virtual */ nsTArray<Element*>
|
||||
nsDocument::GetFullscreenStack() const
|
||||
{
|
||||
Element*
|
||||
nsDocument::GetUnretargetedFullScreenElement() {
|
||||
for (const nsWeakPtr& weakPtr : Reversed(mTopLayer)) {
|
||||
nsCOMPtr<Element> element(do_QueryReferent(weakPtr));
|
||||
// Per spec, the fullscreen element is the topmost element in the document's
|
||||
// top layer of which the fullscreen flag is set, if any, and null otherwise.
|
||||
if (element && element->State().HasState(NS_EVENT_STATE_FULL_SCREEN)) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsTArray<Element*>
|
||||
nsDocument::GetTopLayer() const {
|
||||
nsTArray<Element*> elements;
|
||||
for (const nsWeakPtr& ptr : mFullScreenStack) {
|
||||
for (const nsWeakPtr& ptr : mTopLayer) {
|
||||
if (nsCOMPtr<Element> elem = do_QueryReferent(ptr)) {
|
||||
MOZ_ASSERT(elem->State().HasState(NS_EVENT_STATE_FULL_SCREEN));
|
||||
elements.AppendElement(elem);
|
||||
}
|
||||
}
|
||||
@@ -10683,7 +10747,8 @@ nsDocument::FullscreenElementReadyCheck(Element* aElement,
|
||||
{
|
||||
NS_ASSERTION(aElement,
|
||||
"Must pass non-null element to nsDocument::RequestFullScreen");
|
||||
if (!aElement || aElement == FullScreenStackTop()) {
|
||||
Element* fullscreenElement = GetUnretargetedFullScreenElement();
|
||||
if (!aElement || aElement == fullscreenElement) {
|
||||
return false;
|
||||
}
|
||||
if (!aElement->IsInComposedDoc()) {
|
||||
@@ -10712,9 +10777,9 @@ nsDocument::FullscreenElementReadyCheck(Element* aElement,
|
||||
}
|
||||
//XXXsmaug Note, we don't follow the latest fullscreen spec here.
|
||||
// This whole check could be probably removed.
|
||||
if (FullScreenStackTop() &&
|
||||
if (fullscreenElement &&
|
||||
!nsContentUtils::ContentIsHostIncludingDescendantOf(aElement,
|
||||
FullScreenStackTop())) {
|
||||
fullscreenElement)) {
|
||||
// If this document is full-screen, only grant full-screen requests from
|
||||
// a descendant of the current full-screen element.
|
||||
DispatchFullscreenError("FullscreenDeniedNotDescendant");
|
||||
@@ -10871,7 +10936,7 @@ ShouldApplyFullscreenDirectly(nsIDocument* aDoc,
|
||||
// If we are in the content process, we can apply the fullscreen
|
||||
// state directly only if we have been in DOM fullscreen, because
|
||||
// otherwise we always need to notify the chrome.
|
||||
return !!nsContentUtils::GetRootDocument(aDoc)->GetFullscreenElement();
|
||||
return !!nsContentUtils::GetRootDocument(aDoc)->GetUnretargetedFullScreenElement();
|
||||
} else {
|
||||
// If we are in the chrome process, and the window has not been in
|
||||
// fullscreen, we certainly need to make that fullscreen first.
|
||||
@@ -10994,8 +11059,8 @@ nsDocument::ApplyFullscreen(const FullscreenRequest& aRequest)
|
||||
// Set the full-screen element. This sets the full-screen style on the
|
||||
// element, and the full-screen-ancestor styles on ancestors of the element
|
||||
// in this document.
|
||||
DebugOnly<bool> x = FullScreenStackPush(elem);
|
||||
NS_ASSERTION(x, "Full-screen state of requesting doc should always change!");
|
||||
DebugOnly<bool> x = SetFullscreenElement(elem);
|
||||
MOZ_ASSERT(x, "Full-screen state of requesting doc should always change!");
|
||||
// Set the iframe fullscreen flag.
|
||||
if (elem->IsHTMLElement(nsGkAtoms::iframe)) {
|
||||
static_cast<HTMLIFrameElement*>(elem)->SetFullscreenFlag(true);
|
||||
@@ -11017,14 +11082,14 @@ nsDocument::ApplyFullscreen(const FullscreenRequest& aRequest)
|
||||
}
|
||||
nsIDocument* parent = child->GetParentDocument();
|
||||
Element* element = parent->FindContentForSubDocument(child)->AsElement();
|
||||
if (static_cast<nsDocument*>(parent)->FullScreenStackPush(element)) {
|
||||
if (static_cast<nsDocument*>(parent)->SetFullscreenElement(element)) {
|
||||
changed.AppendElement(parent);
|
||||
child = parent;
|
||||
} else {
|
||||
// We've reached either the root, or a point in the doctree where the
|
||||
// new full-screen element container is the same as the previous
|
||||
// full-screen element's container. No more changes need to be made
|
||||
// to the full-screen stacks of documents further up the tree.
|
||||
// to the top layer of documents further up the tree.
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -11294,7 +11359,7 @@ PointerLockRequest::Run()
|
||||
}
|
||||
// If it is neither user input initiated, nor requested in fullscreen,
|
||||
// it should be rejected.
|
||||
if (!error && !mUserInputOrChromeCaller && !doc->GetFullscreenElement()) {
|
||||
if (!error && !mUserInputOrChromeCaller && !doc->GetUnretargetedFullScreenElement()) {
|
||||
error = "PointerLockDeniedNotInputDriven";
|
||||
}
|
||||
if (!error && !d->SetPointerLock(e, NS_STYLE_CURSOR_NONE)) {
|
||||
|
||||
+28
-18
@@ -56,6 +56,7 @@
|
||||
#include "imgIRequest.h"
|
||||
#include "mozilla/EventListenerManager.h"
|
||||
#include "mozilla/EventStates.h"
|
||||
#include "mozilla/FunctionRef.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/PendingAnimationTracker.h"
|
||||
#include "mozilla/dom/DOMImplementation.h"
|
||||
@@ -77,6 +78,7 @@
|
||||
#define XML_DECLARATION_BITS_STANDALONE_EXISTS (1 << 2)
|
||||
#define XML_DECLARATION_BITS_STANDALONE_YES (1 << 3)
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
class nsDOMStyleSheetSetList;
|
||||
class nsDocument;
|
||||
@@ -638,7 +640,7 @@ public:
|
||||
using mozilla::dom::DocumentOrShadowRoot::GetElementsByTagName;
|
||||
using mozilla::dom::DocumentOrShadowRoot::GetElementsByTagNameNS;
|
||||
using mozilla::dom::DocumentOrShadowRoot::GetElementsByClassName;
|
||||
|
||||
|
||||
// nsIDOMEventTarget
|
||||
virtual nsresult GetEventTargetParent(
|
||||
mozilla::EventChainPreVisitor& aVisitor) override;
|
||||
@@ -850,7 +852,7 @@ public:
|
||||
|
||||
virtual Element* FindImageMap(const nsAString& aNormalizedMapName) override;
|
||||
|
||||
virtual nsTArray<Element*> GetFullscreenStack() const override;
|
||||
virtual nsTArray<Element*> GetTopLayer() const override;
|
||||
virtual void AsyncRequestFullScreen(
|
||||
mozilla::UniquePtr<FullscreenRequest>&& aRequest) override;
|
||||
virtual void RestorePreviousFullScreenState() override;
|
||||
@@ -860,6 +862,7 @@ public:
|
||||
|
||||
virtual nsresult RemoteFrameFullscreenReverted() override;
|
||||
virtual nsIDocument* GetFullscreenRoot() override;
|
||||
virtual size_t CountFullscreenElements() const override;
|
||||
virtual void SetFullscreenRoot(nsIDocument* aRoot) override;
|
||||
|
||||
// Returns the size of the mBlockedTrackingNodes array. (nsIDocument.h)
|
||||
@@ -896,23 +899,32 @@ public:
|
||||
// to move this document into full-screen mode if allowed.
|
||||
void RequestFullScreen(mozilla::UniquePtr<FullscreenRequest>&& aRequest);
|
||||
|
||||
// Removes all elements from the full-screen stack, removing full-scren
|
||||
// styles from the top element in the stack.
|
||||
// Removes all elements with the full-screen flag set from the top layer, and
|
||||
// clears their full-screen flag.
|
||||
void CleanupFullscreenState();
|
||||
|
||||
// Pushes aElement onto the full-screen stack, and removes full-screen styles
|
||||
// from the former full-screen stack top, and its ancestors, and applies the
|
||||
// styles to aElement. aElement becomes the new "full-screen element".
|
||||
bool FullScreenStackPush(Element* aElement);
|
||||
// Pushes aElement onto the top layer
|
||||
bool TopLayerPush(Element* aElement) override;
|
||||
|
||||
// Remove the top element from the full-screen stack. Removes the full-screen
|
||||
// styles from the former top element, and applies them to the new top
|
||||
// element, if there is one.
|
||||
void FullScreenStackPop();
|
||||
// Removes the topmost element which have aPredicate return true from the top
|
||||
// layer. The removed element, if any, is returned.
|
||||
Element* TopLayerPop(FunctionRef<bool(Element*)> aPredicateFunc) override;
|
||||
|
||||
// Pops the fullscreen element from the top layer and clears its
|
||||
// fullscreen flag.
|
||||
void UnsetFullscreenElement();
|
||||
|
||||
// Pushes the given element into the top of top layer and set fullscreen
|
||||
// flag.
|
||||
bool SetFullscreenElement(Element* aElement);
|
||||
|
||||
// Cancel the dialog element if the document is blocked by the dialog
|
||||
void TryCancelDialog();
|
||||
|
||||
// Returns the top element from the full-screen stack.
|
||||
Element* FullScreenStackTop() override;
|
||||
|
||||
Element* GetTopLayerTop() override;
|
||||
// Return the fullscreen element in the top layer
|
||||
Element* GetUnretargetedFullScreenElement() override;
|
||||
// DOM-exposed fullscreen API
|
||||
bool FullscreenEnabled() override;
|
||||
|
||||
@@ -1193,10 +1205,8 @@ protected:
|
||||
// is a weak reference to avoid leaks due to circular references.
|
||||
nsWeakPtr mScopeObject;
|
||||
|
||||
// Stack of full-screen elements. When we request full-screen we push the
|
||||
// full-screen element onto this stack, and when we cancel full-screen we
|
||||
// pop one off this stack, restoring the previous full-screen state
|
||||
nsTArray<nsWeakPtr> mFullScreenStack;
|
||||
// Stack of top layer elements.
|
||||
nsTArray<nsWeakPtr> mTopLayer;
|
||||
|
||||
// The root of the doc tree in which this document is in. This is only
|
||||
// non-null when this document is in fullscreen mode.
|
||||
|
||||
+21
-3
@@ -33,6 +33,7 @@
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/CORSMode.h"
|
||||
#include "mozilla/dom/DocumentOrShadowRoot.h"
|
||||
#include "mozilla/FunctionRef.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/StyleSheet.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
@@ -99,6 +100,8 @@ class nsIGlobalObject;
|
||||
class nsStyleSet;
|
||||
struct nsCSSSelectorList;
|
||||
|
||||
using mozilla::FunctionRef;
|
||||
|
||||
namespace mozilla {
|
||||
class CSSStyleSheet;
|
||||
class ErrorResult;
|
||||
@@ -1273,9 +1276,9 @@ public:
|
||||
virtual void RemoveFromNameTable(Element* aElement, nsIAtom* aName) = 0;
|
||||
|
||||
/**
|
||||
* Returns all elements in the fullscreen stack in the insertion order.
|
||||
* Returns all elements in the top layer in the insertion order.
|
||||
*/
|
||||
virtual nsTArray<Element*> GetFullscreenStack() const = 0;
|
||||
virtual nsTArray<Element*> GetTopLayer() const = 0;
|
||||
|
||||
/**
|
||||
* Asynchronously requests that the document make aElement the fullscreen
|
||||
@@ -1331,12 +1334,26 @@ public:
|
||||
*/
|
||||
virtual nsIDocument* GetFullscreenRoot() = 0;
|
||||
|
||||
virtual size_t CountFullscreenElements() const = 0;
|
||||
|
||||
/**
|
||||
* Sets the fullscreen root to aRoot. This stores a weak reference to aRoot
|
||||
* in this document.
|
||||
*/
|
||||
virtual void SetFullscreenRoot(nsIDocument* aRoot) = 0;
|
||||
|
||||
/**
|
||||
* Push elements to and pop elements from the top layer.
|
||||
* Currently in use for fullscreen and modal version of <dialog>.
|
||||
*/
|
||||
virtual bool TopLayerPush(Element* aElement) = 0;
|
||||
virtual Element* TopLayerPop(FunctionRef<bool(Element*)> aPredicateFunc) = 0;
|
||||
|
||||
/**
|
||||
* Cancel the dialog element if the document is blocked by the dialog.
|
||||
*/
|
||||
virtual void TryCancelDialog() = 0;
|
||||
|
||||
/**
|
||||
* Synchronously cleans up the fullscreen state on the given document.
|
||||
*
|
||||
@@ -2607,7 +2624,8 @@ public:
|
||||
nsIURI* GetDocumentURIObject() const;
|
||||
// Not const because all the full-screen goop is not const
|
||||
virtual bool FullscreenEnabled() = 0;
|
||||
virtual Element* FullScreenStackTop() = 0;
|
||||
virtual Element* GetTopLayerTop() = 0;
|
||||
virtual Element* GetUnretargetedFullScreenElement() = 0;
|
||||
bool Fullscreen()
|
||||
{
|
||||
return !!GetFullscreenElement();
|
||||
|
||||
@@ -291,6 +291,8 @@ private:
|
||||
#define NS_EVENT_STATE_PLACEHOLDERSHOWN NS_DEFINE_EVENT_STATE_MACRO(52)
|
||||
// Element has focus-within.
|
||||
#define NS_EVENT_STATE_FOCUS_WITHIN NS_DEFINE_EVENT_STATE_MACRO(53)
|
||||
// Modal <dialog> element
|
||||
#define NS_EVENT_STATE_MODAL_DIALOG NS_DEFINE_EVENT_STATE_MACRO(54)
|
||||
|
||||
#define DIR_ATTR_STATES (NS_EVENT_STATE_HAS_DIR_ATTR | \
|
||||
NS_EVENT_STATE_DIR_ATTR_LTR | \
|
||||
@@ -309,7 +311,8 @@ private:
|
||||
#define ESM_MANAGED_STATES (DIR_ATTR_STATES | NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS | \
|
||||
NS_EVENT_STATE_HOVER | NS_EVENT_STATE_DRAGOVER | \
|
||||
NS_EVENT_STATE_URLTARGET | NS_EVENT_STATE_FOCUSRING | \
|
||||
NS_EVENT_STATE_FULL_SCREEN | NS_EVENT_STATE_FOCUS_WITHIN)
|
||||
NS_EVENT_STATE_FULL_SCREEN | NS_EVENT_STATE_FOCUS_WITHIN | \
|
||||
NS_EVENT_STATE_MODAL_DIALOG)
|
||||
|
||||
#define INTRINSIC_STATES (~ESM_MANAGED_STATES)
|
||||
|
||||
|
||||
@@ -21,21 +21,8 @@
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/dom/TabParent.h"
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
// Some defiens will be conflict with OSX SDK
|
||||
#define TextRange _TextRange
|
||||
#define TextRangeArray _TextRangeArray
|
||||
#define Comment _Comment
|
||||
#endif
|
||||
|
||||
#include "nsPluginInstanceOwner.h"
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
#undef TextRange
|
||||
#undef TextRangeArray
|
||||
#undef Comment
|
||||
#endif
|
||||
|
||||
using namespace mozilla::widget;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@@ -56,6 +56,9 @@ HTMLDialogElement::Close(const mozilla::dom::Optional<nsAString>& aReturnValue)
|
||||
ErrorResult ignored;
|
||||
SetOpen(false, ignored);
|
||||
ignored.SuppressException();
|
||||
|
||||
RemoveFromTopLayerIfNeeded();
|
||||
|
||||
RefPtr<AsyncEventDispatcher> eventDispatcher =
|
||||
new AsyncEventDispatcher(this, NS_LITERAL_STRING("close"), false);
|
||||
eventDispatcher->PostDOMEvent();
|
||||
@@ -72,6 +75,26 @@ HTMLDialogElement::Show()
|
||||
ignored.SuppressException();
|
||||
}
|
||||
|
||||
bool HTMLDialogElement::IsInTopLayer() const {
|
||||
return State().HasState(NS_EVENT_STATE_MODAL_DIALOG);
|
||||
}
|
||||
|
||||
void HTMLDialogElement::RemoveFromTopLayerIfNeeded() {
|
||||
if (!IsInTopLayer()) {
|
||||
return;
|
||||
}
|
||||
auto predictFunc = [&](Element* element) { return element == this; };
|
||||
|
||||
DebugOnly<Element*> removedElement = OwnerDoc()->TopLayerPop(predictFunc);
|
||||
MOZ_ASSERT(removedElement == this);
|
||||
RemoveStates(NS_EVENT_STATE_MODAL_DIALOG);
|
||||
}
|
||||
|
||||
void HTMLDialogElement::UnbindFromTree(bool aNullParent) {
|
||||
RemoveFromTopLayerIfNeeded();
|
||||
nsGenericHTMLElement::UnbindFromTree(aNullParent);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLDialogElement::ShowModal(ErrorResult& aError)
|
||||
{
|
||||
@@ -79,11 +102,36 @@ HTMLDialogElement::ShowModal(ErrorResult& aError)
|
||||
aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsInTopLayer() && OwnerDoc()->TopLayerPush(this)) {
|
||||
AddStates(NS_EVENT_STATE_MODAL_DIALOG);
|
||||
}
|
||||
|
||||
SetOpen(true, aError);
|
||||
aError.SuppressException();
|
||||
}
|
||||
|
||||
void HTMLDialogElement::CancelDialog() {
|
||||
// 1) Let close be the result of firing an event named cancel at dialog, with
|
||||
// the cancelable attribute initialized to true.
|
||||
bool defaultAction = true;
|
||||
nsContentUtils::DispatchTrustedEvent(
|
||||
OwnerDoc(),
|
||||
static_cast<nsIContent*>(this),
|
||||
NS_LITERAL_STRING("cancel"),
|
||||
false, /* can bubble */
|
||||
true, /* can cancel */
|
||||
&defaultAction);
|
||||
|
||||
// 2) If close is true and dialog has an open attribute, then close the dialog
|
||||
// with no return value.
|
||||
if (defaultAction) {
|
||||
Optional<nsAString> retValue;
|
||||
Close(retValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
JSObject*
|
||||
HTMLDialogElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
|
||||
@@ -41,10 +41,15 @@ public:
|
||||
{
|
||||
mReturnValue = aReturnValue;
|
||||
}
|
||||
|
||||
void UnbindFromTree(bool aNullParent = true) /*override*/;
|
||||
|
||||
void Close(const mozilla::dom::Optional<nsAString>& aReturnValue);
|
||||
void Show();
|
||||
void ShowModal(ErrorResult& aError);
|
||||
|
||||
bool IsInTopLayer() const;
|
||||
void CancelDialog();
|
||||
|
||||
nsString mReturnValue;
|
||||
|
||||
@@ -52,6 +57,9 @@ protected:
|
||||
virtual ~HTMLDialogElement();
|
||||
JSObject* WrapNode(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
private:
|
||||
void RemoveFromTopLayerIfNeeded();
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
||||
@@ -34,7 +34,14 @@
|
||||
#endif
|
||||
|
||||
#if defined(XP_MACOSX)
|
||||
// Some defines will conflict with OSX SDK
|
||||
#define TextRange _TextRange
|
||||
#define TextRangeArray _TextRangeArray
|
||||
#define Comment _Comment
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#undef TextRange
|
||||
#undef TextRangeArray
|
||||
#undef Comment
|
||||
#include <OpenGL/OpenGL.h>
|
||||
#ifndef NP_NO_CARBON
|
||||
#include <Carbon/Carbon.h>
|
||||
|
||||
@@ -20,7 +20,14 @@
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
#include "mozilla/gfx/QuartzSupport.h"
|
||||
// Some defines will conflict with OSX SDK
|
||||
#define TextRange _TextRange
|
||||
#define TextRangeArray _TextRangeArray
|
||||
#define Comment _Comment
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#undef TextRange
|
||||
#undef TextRangeArray
|
||||
#undef Comment
|
||||
#endif
|
||||
|
||||
class nsIInputStream;
|
||||
|
||||
@@ -4726,8 +4726,8 @@ GetDefiniteSizeTakenByBoxSizing(StyleBoxSizing aBoxSizing,
|
||||
}
|
||||
|
||||
// Handles only max-content and min-content, and
|
||||
// -moz-fit-content for min-width and max-width, since the others
|
||||
// (-moz-fit-content for width, and -moz-available) have no effect on
|
||||
// fit-content for min-width and max-width, since the others
|
||||
// (fit-content for width, and -moz-available) have no effect on
|
||||
// intrinsic widths.
|
||||
enum eWidthProperty { PROP_WIDTH, PROP_MAX_WIDTH, PROP_MIN_WIDTH };
|
||||
static bool
|
||||
@@ -5038,7 +5038,7 @@ nsLayoutUtils::IntrinsicForAxis(PhysicalAxis aAxis,
|
||||
if (styleISize.GetUnit() == eStyleUnit_Enumerated &&
|
||||
(styleISize.GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT ||
|
||||
styleISize.GetIntValue() == NS_STYLE_WIDTH_MIN_CONTENT)) {
|
||||
// -moz-fit-content and -moz-available enumerated widths compute intrinsic
|
||||
// fit-content and -moz-available enumerated widths compute intrinsic
|
||||
// widths just like auto.
|
||||
// For max-content and min-content, we handle them like
|
||||
// specified widths, but ignore box-sizing.
|
||||
@@ -5054,7 +5054,7 @@ nsLayoutUtils::IntrinsicForAxis(PhysicalAxis aAxis,
|
||||
++gNoiseIndent;
|
||||
#endif
|
||||
if (aType != MIN_ISIZE) {
|
||||
// At this point, |styleISize| is auto/-moz-fit-content/-moz-available or
|
||||
// At this point, |styleISize| is auto/fit-content/-moz-available or
|
||||
// has a percentage. The intrinisic size for those under a max-content
|
||||
// constraint is the max-content contribution which we shouldn't clamp.
|
||||
aMarginBoxMinSizeClamp = NS_MAXSIZE;
|
||||
|
||||
@@ -8063,6 +8063,12 @@ PresShell::HandleEventInternal(WidgetEvent* aEvent,
|
||||
aEvent->mFlags.mDefaultPreventedByChrome) {
|
||||
mIsLastChromeOnlyEscapeKeyConsumed = true;
|
||||
}
|
||||
if (aEvent->mMessage == eKeyDown &&
|
||||
!aEvent->mFlags.mDefaultPrevented) {
|
||||
if (nsIDocument* doc = GetDocument()) {
|
||||
doc->TryCancelDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -123,8 +123,8 @@ ViewportFrame::BuildDisplayListForTopLayer(nsDisplayListBuilder* aBuilder,
|
||||
nsDisplayList* aList)
|
||||
{
|
||||
nsIDocument* doc = PresContext()->Document();
|
||||
nsTArray<Element*> fullscreenStack = doc->GetFullscreenStack();
|
||||
for (Element* elem : fullscreenStack) {
|
||||
nsTArray<Element*> toplayer = doc->GetTopLayer();
|
||||
for (Element* elem : toplayer) {
|
||||
if (nsIFrame* frame = elem->GetPrimaryFrame()) {
|
||||
// There are two cases where an element in fullscreen is not in
|
||||
// the top layer:
|
||||
|
||||
@@ -63,7 +63,6 @@ CSS_KEY(-moz-element, _moz_element)
|
||||
CSS_KEY(-moz-eventreerow, _moz_eventreerow)
|
||||
CSS_KEY(-moz-field, _moz_field)
|
||||
CSS_KEY(-moz-fieldtext, _moz_fieldtext)
|
||||
CSS_KEY(-moz-fit-content, _moz_fit_content)
|
||||
CSS_KEY(-moz-fixed, _moz_fixed)
|
||||
CSS_KEY(-moz-grabbing, _moz_grabbing)
|
||||
CSS_KEY(-moz-grab, _moz_grab)
|
||||
|
||||
@@ -2260,7 +2260,7 @@ const KTableEntry nsCSSProps::kWhitespaceKTable[] = {
|
||||
const KTableEntry nsCSSProps::kWidthKTable[] = {
|
||||
{ eCSSKeyword_max_content, NS_STYLE_WIDTH_MAX_CONTENT },
|
||||
{ eCSSKeyword_min_content, NS_STYLE_WIDTH_MIN_CONTENT },
|
||||
{ eCSSKeyword__moz_fit_content, NS_STYLE_WIDTH_FIT_CONTENT },
|
||||
{ eCSSKeyword_fit_content, NS_STYLE_WIDTH_FIT_CONTENT },
|
||||
{ eCSSKeyword__moz_available, NS_STYLE_WIDTH_AVAILABLE },
|
||||
{ eCSSKeyword_UNKNOWN, -1 }
|
||||
};
|
||||
@@ -2269,7 +2269,7 @@ const KTableEntry nsCSSProps::kWidthKTable[] = {
|
||||
const KTableEntry nsCSSProps::kFlexBasisKTable[] = {
|
||||
{ eCSSKeyword_max_content, NS_STYLE_WIDTH_MAX_CONTENT },
|
||||
{ eCSSKeyword_min_content, NS_STYLE_WIDTH_MIN_CONTENT },
|
||||
{ eCSSKeyword__moz_fit_content, NS_STYLE_WIDTH_FIT_CONTENT },
|
||||
{ eCSSKeyword_fit_content, NS_STYLE_WIDTH_FIT_CONTENT },
|
||||
{ eCSSKeyword__moz_available, NS_STYLE_WIDTH_AVAILABLE },
|
||||
{ eCSSKeyword_content, NS_STYLE_FLEX_BASIS_CONTENT },
|
||||
{ eCSSKeyword_UNKNOWN, -1 }
|
||||
|
||||
@@ -190,6 +190,12 @@ CSS_STATE_PSEUDO_CLASS(fullscreen, ":fullscreen",
|
||||
NS_EVENT_STATE_FULL_SCREEN)
|
||||
CSS_STATE_PSEUDO_CLASS(mozFullScreen, ":-moz-full-screen", 0, "", NS_EVENT_STATE_FULL_SCREEN)
|
||||
|
||||
// Matches if a <dialog> element is modal
|
||||
CSS_STATE_PSEUDO_CLASS(mozModalDialog, ":-moz-modal-dialog",
|
||||
CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS,
|
||||
"",
|
||||
NS_EVENT_STATE_MODAL_DIALOG)
|
||||
|
||||
// Matches if the element is focused and should show a focus ring
|
||||
CSS_STATE_PSEUDO_CLASS(mozFocusRing, ":-moz-focusring", 0, "", NS_EVENT_STATE_FOCUSRING)
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
fieldset > legend {
|
||||
padding-inline-start: 2px;
|
||||
padding-inline-end: 2px;
|
||||
inline-size: -moz-fit-content;
|
||||
inline-size: fit-content;
|
||||
}
|
||||
|
||||
legend {
|
||||
|
||||
@@ -811,21 +811,31 @@ dialog {
|
||||
position: absolute;
|
||||
inset-inline-start: 0;
|
||||
inset-inline-end: 0;
|
||||
color: black;
|
||||
margin: auto;
|
||||
border-width: initial;
|
||||
border-style: solid;
|
||||
border-color: initial;
|
||||
border-color: dimgrey;
|
||||
border-image: initial;
|
||||
border-radius: 5px;
|
||||
padding: 1em;
|
||||
background: white;
|
||||
width: -moz-fit-content;
|
||||
color: #222; /*CanvasText*/
|
||||
background: snow; /*Canvas*/
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
dialog:not([open]) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
dialog:-moz-modal-dialog {
|
||||
-moz-top-layer: top !important;
|
||||
}
|
||||
|
||||
/* https://html.spec.whatwg.org/#flow-content-3 */
|
||||
dialog::backdrop {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* emulation of non-standard HTML <marquee> tag */
|
||||
marquee {
|
||||
inline-size: -moz-available;
|
||||
|
||||
@@ -179,7 +179,7 @@ GetISizeInfo(nsRenderingContext *aRenderingContext,
|
||||
if (!aIsCell || maxISize.GetIntValue() == NS_STYLE_WIDTH_AVAILABLE) {
|
||||
maxISize.SetNoneValue();
|
||||
} else if (maxISize.GetIntValue() == NS_STYLE_WIDTH_FIT_CONTENT) {
|
||||
// for 'max-inline-size', '-moz-fit-content' is like
|
||||
// for 'max-inline-size', 'fit-content' is like
|
||||
// 'max-content'
|
||||
maxISize.SetIntValue(NS_STYLE_WIDTH_MAX_CONTENT,
|
||||
eStyleUnit_Enumerated);
|
||||
@@ -206,7 +206,7 @@ GetISizeInfo(nsRenderingContext *aRenderingContext,
|
||||
if (!aIsCell || minISize.GetIntValue() == NS_STYLE_WIDTH_AVAILABLE) {
|
||||
minISize.SetCoordValue(0);
|
||||
} else if (minISize.GetIntValue() == NS_STYLE_WIDTH_FIT_CONTENT) {
|
||||
// for 'min-inline-size', '-moz-fit-content' is like
|
||||
// for 'min-inline-size', 'fit-content' is like
|
||||
// 'min-content'
|
||||
minISize.SetIntValue(NS_STYLE_WIDTH_MIN_CONTENT,
|
||||
eStyleUnit_Enumerated);
|
||||
|
||||
@@ -111,7 +111,7 @@ FixedTableLayoutStrategy::GetMinISize(nsRenderingContext* aRenderingContext)
|
||||
result -= spacing * (colSpan - 1);
|
||||
}
|
||||
}
|
||||
// else, for 'auto', '-moz-available', '-moz-fit-content',
|
||||
// else, for 'auto', '-moz-available', 'fit-content',
|
||||
// and 'calc()' with percentages, do nothing
|
||||
}
|
||||
}
|
||||
@@ -266,7 +266,7 @@ FixedTableLayoutStrategy::ComputeColumnISizes(const ReflowInput& aReflowInput)
|
||||
colFrame->AddPrefPercent(pct);
|
||||
pctTotal += pct;
|
||||
} else {
|
||||
// 'auto', '-moz-available', '-moz-fit-content', and 'calc()'
|
||||
// 'auto', '-moz-available', 'fit-content', and 'calc()'
|
||||
// with percentages
|
||||
colISize = unassignedMarker;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/*
|
||||
* A generic callable type that can be initialized from any compatible callable,
|
||||
* suitable for use as a function argument for the duration of the function
|
||||
* call (and no longer).
|
||||
*/
|
||||
|
||||
#ifndef mozilla_FunctionRef_h
|
||||
#define mozilla_FunctionRef_h
|
||||
|
||||
#include "mozilla/OperatorNewExtensions.h" // mozilla::NotNull, ::operator new
|
||||
|
||||
#include <cstddef> // std::nullptr_t
|
||||
#include <type_traits> // std::{declval,integral_constant}, std::is_{convertible,same,void}_v, std::{enable_if,remove_reference,remove_cv}_t
|
||||
|
||||
// This concept and its implementation are substantially inspired by foonathan's prior art:
|
||||
//
|
||||
// https://foonathan.net/2017/01/function-ref-implementation/
|
||||
// https://github.com/foonathan/type_safe/blob/2017851053f8dd268372f1612865792c5c621570/include/type_safe/reference.hpp
|
||||
//
|
||||
// Note: this is C++ code voodoo. Be careful if you have to make any changes.
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Template helper to determine if |Returned| is a return type compatible with
|
||||
// |Required|: if the former converts to the latter, or if |Required| is |void|
|
||||
// and nothing is returned.
|
||||
template <typename Returned, typename Required>
|
||||
using CompatibleReturnType =
|
||||
std::integral_constant<bool, std::is_void_v<Required> ||
|
||||
std::is_convertible_v<Returned, Required>>;
|
||||
|
||||
// Template helper to check if |Func| called with |Params| arguments returns
|
||||
// a type compatible with |Ret|.
|
||||
template <typename Func, typename Ret, typename... Params>
|
||||
using EnableMatchingFunction = std::enable_if_t<
|
||||
CompatibleReturnType<
|
||||
decltype(std::declval<Func&>()(std::declval<Params>()...)), Ret>::value,
|
||||
int>;
|
||||
|
||||
struct MatchingFunctionPointerTag {};
|
||||
struct MatchingFunctorTag {};
|
||||
struct InvalidFunctorTag {};
|
||||
|
||||
// Template helper to determine the proper way to store |Callable|: as function
|
||||
// pointer, as pointer to object, or unstorable.
|
||||
template <typename Callable, typename Ret, typename... Params>
|
||||
struct GetCallableTag {
|
||||
// Match the case where |Callable| is a compatible function pointer or
|
||||
// converts to one. (|+obj| invokes such a conversion.)
|
||||
template <typename T>
|
||||
static MatchingFunctionPointerTag test(
|
||||
int, T& obj, EnableMatchingFunction<decltype(+obj), Ret, Params...> = 0);
|
||||
|
||||
// Match the case where |Callable| is callable but can't be converted to a
|
||||
// function pointer. (|short| is a worse match for 0 than |int|, causing the
|
||||
// function pointer match to be preferred if both apply.)
|
||||
template <typename T>
|
||||
static MatchingFunctorTag test(short, T& obj,
|
||||
EnableMatchingFunction<T, Ret, Params...> = 0);
|
||||
|
||||
// Match all remaining cases. (Any other match is preferred to an ellipsis
|
||||
// match.)
|
||||
static InvalidFunctorTag test(...);
|
||||
|
||||
using Type = decltype(test(0, std::declval<Callable&>()));
|
||||
};
|
||||
|
||||
// If the callable is |nullptr|, |std::declval<std::nullptr_t&>()| will be an
|
||||
// error. Provide a specialization for |nullptr| that will fail substitution.
|
||||
template <typename Ret, typename... Params>
|
||||
struct GetCallableTag<std::nullptr_t, Ret, Params...> {};
|
||||
|
||||
template <typename Result, typename Callable, typename Ret, typename... Params>
|
||||
using EnableFunctionTag = std::enable_if_t<
|
||||
std::is_same_v<typename GetCallableTag<Callable, Ret, Params...>::Type,
|
||||
Result>,
|
||||
int>;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* An efficient, type-erasing, non-owning reference to a callable. It is
|
||||
* intended for use as the type of a function parameter that is not used after
|
||||
* the function in question returns.
|
||||
*
|
||||
* This class does not own the callable, so in general it is unsafe to store a
|
||||
* FunctionRef.
|
||||
*/
|
||||
template <typename Fn>
|
||||
class FunctionRef;
|
||||
|
||||
template <typename Ret, typename... Params>
|
||||
class FunctionRef<Ret(Params...)> {
|
||||
union Payload;
|
||||
|
||||
// |FunctionRef| stores an adaptor function pointer, determined by the
|
||||
// arguments passed to the constructor. That adaptor will perform the steps
|
||||
// needed to invoke the callable passed at construction time.
|
||||
using Adaptor = Ret (*)(const Payload& aPayload, Params... aParams);
|
||||
|
||||
// If |FunctionRef|'s callable can be stored as a function pointer, that
|
||||
// function pointer is stored after being cast to this *different* function
|
||||
// pointer type. |mAdaptor| then casts back to the original type to call it.
|
||||
// ([expr.reinterpret.cast]p6 guarantees that A->B->A function pointer casts
|
||||
// produce the original function pointer value.) An outlandish signature is
|
||||
// used to emphasize that the exact function pointer type doesn't matter.
|
||||
using FuncPtr = Payload***** (*)(Payload*****);
|
||||
|
||||
/**
|
||||
* An adaptor function (used by this class's function call operator) that
|
||||
* invokes the callable in |mPayload|, forwarding arguments and converting
|
||||
* return type as needed.
|
||||
*/
|
||||
const Adaptor mAdaptor;
|
||||
|
||||
/** Storage for the wrapped callable value. */
|
||||
union Payload {
|
||||
// This arm is used if |FunctionRef| is passed a compatible function pointer
|
||||
// or a lambda/callable that converts to a compatible function pointer.
|
||||
FuncPtr mFuncPtr;
|
||||
|
||||
// This arm is used if |FunctionRef| is passed some other callable or
|
||||
// |nullptr|.
|
||||
void* mObject;
|
||||
} mPayload;
|
||||
|
||||
template <typename RealFuncPtr>
|
||||
static Ret CallFunctionPointer(const Payload& aPayload,
|
||||
Params... aParams) noexcept {
|
||||
auto func = reinterpret_cast<RealFuncPtr>(aPayload.mFuncPtr);
|
||||
return static_cast<Ret>(func(static_cast<Params>(aParams)...));
|
||||
}
|
||||
|
||||
template <typename Ret2, typename... Params2>
|
||||
FunctionRef(detail::MatchingFunctionPointerTag, Ret2 (*aFuncPtr)(Params2...))
|
||||
: mAdaptor(&CallFunctionPointer<Ret2 (*)(Params2...)>) {
|
||||
::new (KnownNotNull, &mPayload.mFuncPtr)
|
||||
FuncPtr(reinterpret_cast<FuncPtr>(aFuncPtr));
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Construct a |FunctionRef| that's like a null function pointer that can't be
|
||||
* called.
|
||||
*/
|
||||
MOZ_IMPLICIT FunctionRef(std::nullptr_t) noexcept : mAdaptor(nullptr) {
|
||||
// This is technically unnecessary, but it seems best to always initialize
|
||||
// a union arm.
|
||||
::new (KnownNotNull, &mPayload.mObject) void*(nullptr);
|
||||
}
|
||||
|
||||
FunctionRef() : FunctionRef(nullptr) {}
|
||||
|
||||
/**
|
||||
* Constructs a |FunctionRef| from an object callable with |Params| arguments,
|
||||
* that returns a type convertible to |Ret|, where the callable isn't
|
||||
* convertible to function pointer (often because it contains some internal
|
||||
* state). For example:
|
||||
*
|
||||
* int x = 5;
|
||||
* auto doSideEffect = [&x]{ x++; }; // state is captured reference to |x|
|
||||
* FunctionRef<void()> f(doSideEffect);
|
||||
*/
|
||||
template <
|
||||
typename Callable,
|
||||
typename = detail::EnableFunctionTag<detail::MatchingFunctorTag, Callable,
|
||||
Ret, Params...>,
|
||||
typename std::enable_if_t<!std::is_same_v<
|
||||
typename std::remove_reference_t<typename std::remove_cv_t<Callable>>,
|
||||
FunctionRef>>* = nullptr>
|
||||
MOZ_IMPLICIT FunctionRef(Callable& aCallable) noexcept
|
||||
: mAdaptor([](const Payload& aPayload, Params... aParams) {
|
||||
auto& func = *static_cast<Callable*>(aPayload.mObject);
|
||||
return static_cast<Ret>(func(static_cast<Params>(aParams)...));
|
||||
}) {
|
||||
::new (KnownNotNull, &mPayload.mObject) void*(&aCallable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a |FunctionRef| from an value callable with |Params| arguments,
|
||||
* that returns a type convertible to |Ret|, where the callable is stateless
|
||||
* and is (or is convertible to) a function pointer. For example:
|
||||
*
|
||||
* // Exact match
|
||||
* double twice(double d) { return d * 2; }
|
||||
* FunctionRef<double(double)> func1(&twice);
|
||||
*
|
||||
* // Compatible match
|
||||
* float thrice(long double d) { return static_cast<float>(d) * 3; }
|
||||
* FunctionRef<double(double)> func2(&thrice);
|
||||
*
|
||||
* // Non-generic lambdas that don't capture anything have a conversion
|
||||
* // function to the appropriate function pointer type.
|
||||
* FunctionRef<int(double)> f([](long double){ return 'c'; });
|
||||
*/
|
||||
template <typename Callable,
|
||||
typename = detail::EnableFunctionTag<
|
||||
detail::MatchingFunctionPointerTag, Callable, Ret, Params...>>
|
||||
MOZ_IMPLICIT FunctionRef(const Callable& aCallable) noexcept
|
||||
: FunctionRef(detail::MatchingFunctionPointerTag{}, +aCallable) {}
|
||||
|
||||
/** Call the callable stored in this with the given arguments. */
|
||||
Ret operator()(Params... params) const {
|
||||
return mAdaptor(mPayload, static_cast<Params>(params)...);
|
||||
}
|
||||
|
||||
/** Return true iff this wasn't created from |nullptr|. */
|
||||
explicit operator bool() const noexcept { return mAdaptor != nullptr; }
|
||||
};
|
||||
|
||||
} /* namespace mozilla */
|
||||
|
||||
#endif /* mozilla_FunctionRef_h */
|
||||
@@ -42,6 +42,7 @@ EXPORTS.mozilla = [
|
||||
'FastBernoulliTrial.h',
|
||||
'FloatingPoint.h',
|
||||
'Function.h',
|
||||
'FunctionRef.h',
|
||||
'GuardObjects.h',
|
||||
'HashFunctions.h',
|
||||
'IndexSequence.h',
|
||||
|
||||
Reference in New Issue
Block a user