mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-26 20:30:41 +00:00
Merge inbound to mozilla-central. a=merge
This commit is contained in:
commit
b1522a7bf0
@ -327,6 +327,9 @@ class TabTracker extends TabTrackerBase {
|
||||
}
|
||||
|
||||
setId(nativeTab, id) {
|
||||
if (!nativeTab.parentNode) {
|
||||
throw new Error("Cannot attach ID to a destroyed tab.");
|
||||
}
|
||||
this._tabs.set(nativeTab, id);
|
||||
if (nativeTab.linkedBrowser) {
|
||||
this._browsers.set(nativeTab.linkedBrowser, id);
|
||||
|
@ -10,6 +10,7 @@ import subprocess
|
||||
import platform
|
||||
import json
|
||||
import argparse
|
||||
import fnmatch
|
||||
import glob
|
||||
import errno
|
||||
import re
|
||||
@ -222,6 +223,9 @@ def build_one_stage(cc, cxx, asm, ld, ar, ranlib, libtool,
|
||||
if is_windows():
|
||||
cmake_args.insert(-1, "-DLLVM_EXPORT_SYMBOLS_FOR_PLUGINS=ON")
|
||||
cmake_args.insert(-1, "-DLLVM_USE_CRT_RELEASE=MT")
|
||||
else:
|
||||
# libllvm as a shared library is not supported on Windows
|
||||
cmake_args += ["-DLLVM_LINK_LLVM_DYLIB=ON"]
|
||||
if ranlib is not None:
|
||||
cmake_args += ["-DCMAKE_RANLIB=%s" % slashify_path(ranlib)]
|
||||
if libtool is not None:
|
||||
@ -366,11 +370,17 @@ def prune_final_dir_for_clang_tidy(final_dir):
|
||||
|
||||
# Keep include/ intact.
|
||||
|
||||
# In lib/, only keep lib/clang/N.M.O/include.
|
||||
# In lib/, only keep lib/clang/N.M.O/include and the LLVM shared library.
|
||||
re_ver_num = re.compile(r"^\d+\.\d+\.\d+$", re.I)
|
||||
for f in glob.glob("%s/lib/*" % final_dir):
|
||||
if os.path.basename(f) != "clang":
|
||||
delete(f)
|
||||
name = os.path.basename(f)
|
||||
if name == "clang":
|
||||
continue
|
||||
if is_darwin() and name == 'libLLVM.dylib':
|
||||
continue
|
||||
if is_linux() and fnmatch.fnmatch(name, 'libLLVM*.so'):
|
||||
continue
|
||||
delete(f)
|
||||
for f in glob.glob("%s/lib/clang/*" % final_dir):
|
||||
if re_ver_num.search(os.path.basename(f)) is None:
|
||||
delete(f)
|
||||
@ -600,11 +610,11 @@ if __name__ == "__main__":
|
||||
extra_asmflags = []
|
||||
extra_ldflags = []
|
||||
elif is_linux():
|
||||
extra_cflags = ["-static-libgcc"]
|
||||
extra_cxxflags = ["-static-libgcc", "-static-libstdc++"]
|
||||
extra_cflags = []
|
||||
extra_cxxflags = []
|
||||
extra_cflags2 = ["-fPIC"]
|
||||
# Silence clang's warnings about arguments not being used in compilation.
|
||||
extra_cxxflags2 = ["-fPIC", '-Qunused-arguments', "-static-libstdc++"]
|
||||
extra_cxxflags2 = ["-fPIC", '-Qunused-arguments']
|
||||
extra_asmflags = []
|
||||
extra_ldflags = []
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
"cxx": "/builds/worker/workspace/build/src/gcc/bin/g++",
|
||||
"as": "/builds/worker/workspace/build/src/gcc/bin/gcc",
|
||||
"patches": [
|
||||
"static-llvm-symbolizer.patch",
|
||||
"find_symbolizer_linux.patch",
|
||||
"r322325.patch",
|
||||
"r322401.patch",
|
||||
|
@ -21,6 +21,7 @@
|
||||
"libtool": "/builds/worker/workspace/build/src/cctools/bin/x86_64-apple-darwin11-libtool",
|
||||
"ld": "/builds/worker/workspace/build/src/clang/bin/clang",
|
||||
"patches": [
|
||||
"static-llvm-symbolizer.patch",
|
||||
"compiler-rt-cross-compile.patch",
|
||||
"compiler-rt-no-codesign.patch",
|
||||
"r322401.patch",
|
||||
|
@ -16,6 +16,7 @@
|
||||
"cxx": "/builds/worker/workspace/build/src/gcc/bin/g++",
|
||||
"as": "/builds/worker/workspace/build/src/gcc/bin/gcc",
|
||||
"patches": [
|
||||
"static-llvm-symbolizer.patch",
|
||||
"find_symbolizer_linux.patch",
|
||||
"rename_gcov_flush.patch"
|
||||
]
|
||||
|
12
build/build-clang/static-llvm-symbolizer.patch
Normal file
12
build/build-clang/static-llvm-symbolizer.patch
Normal file
@ -0,0 +1,12 @@
|
||||
diff --git a/llvm/tools/llvm-symbolizer/CMakeLists.txt b/llvm/tools/llvm-symbolizer/CMakeLists.txt
|
||||
index 8185c296c50..13c7419fa47 100644
|
||||
--- a/llvm/tools/llvm-symbolizer/CMakeLists.txt
|
||||
+++ b/llvm/tools/llvm-symbolizer/CMakeLists.txt
|
||||
@@ -13,6 +13,7 @@ set(LLVM_LINK_COMPONENTS
|
||||
)
|
||||
|
||||
add_llvm_tool(llvm-symbolizer
|
||||
+ DISABLE_LLVM_LINK_LLVM_DYLIB
|
||||
llvm-symbolizer.cpp
|
||||
)
|
||||
|
@ -6,10 +6,13 @@
|
||||
|
||||
#include "DocumentOrShadowRoot.h"
|
||||
#include "mozilla/EventStateManager.h"
|
||||
#include "mozilla/dom/HTMLInputElement.h"
|
||||
#include "mozilla/dom/ShadowRoot.h"
|
||||
#include "mozilla/dom/StyleSheetList.h"
|
||||
#include "nsDocument.h"
|
||||
#include "nsFocusManager.h"
|
||||
#include "nsIRadioVisitor.h"
|
||||
#include "nsIFormControl.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsSVGUtils.h"
|
||||
#include "nsWindowSizes.h"
|
||||
@ -398,5 +401,205 @@ DocumentOrShadowRoot::ReportEmptyGetElementByIdArg()
|
||||
nsContentUtils::ReportEmptyGetElementByIdArg(AsNode().OwnerDoc());
|
||||
}
|
||||
|
||||
/**
|
||||
* A struct that holds all the information about a radio group.
|
||||
*/
|
||||
struct nsRadioGroupStruct
|
||||
{
|
||||
nsRadioGroupStruct()
|
||||
: mRequiredRadioCount(0)
|
||||
, mGroupSuffersFromValueMissing(false)
|
||||
{}
|
||||
|
||||
/**
|
||||
* A strong pointer to the currently selected radio button.
|
||||
*/
|
||||
RefPtr<HTMLInputElement> mSelectedRadioButton;
|
||||
nsCOMArray<nsIFormControl> mRadioButtons;
|
||||
uint32_t mRequiredRadioCount;
|
||||
bool mGroupSuffersFromValueMissing;
|
||||
};
|
||||
|
||||
nsresult
|
||||
DocumentOrShadowRoot::WalkRadioGroup(const nsAString& aName,
|
||||
nsIRadioVisitor* aVisitor,
|
||||
bool aFlushContent)
|
||||
{
|
||||
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
||||
|
||||
for (int i = 0; i < radioGroup->mRadioButtons.Count(); i++) {
|
||||
if (!aVisitor->Visit(radioGroup->mRadioButtons[i])) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
DocumentOrShadowRoot::SetCurrentRadioButton(const nsAString& aName,
|
||||
HTMLInputElement* aRadio)
|
||||
{
|
||||
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
||||
radioGroup->mSelectedRadioButton = aRadio;
|
||||
}
|
||||
|
||||
HTMLInputElement*
|
||||
DocumentOrShadowRoot::GetCurrentRadioButton(const nsAString& aName)
|
||||
{
|
||||
return GetOrCreateRadioGroup(aName)->mSelectedRadioButton;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DocumentOrShadowRoot::GetNextRadioButton(const nsAString& aName,
|
||||
const bool aPrevious,
|
||||
HTMLInputElement* aFocusedRadio,
|
||||
HTMLInputElement** aRadioOut)
|
||||
{
|
||||
// XXX Can we combine the HTML radio button method impls of
|
||||
// nsDocument and nsHTMLFormControl?
|
||||
// XXX Why is HTML radio button stuff in nsDocument, as
|
||||
// opposed to nsHTMLDocument?
|
||||
*aRadioOut = nullptr;
|
||||
|
||||
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
||||
|
||||
// Return the radio button relative to the focused radio button.
|
||||
// If no radio is focused, get the radio relative to the selected one.
|
||||
RefPtr<HTMLInputElement> currentRadio;
|
||||
if (aFocusedRadio) {
|
||||
currentRadio = aFocusedRadio;
|
||||
} else {
|
||||
currentRadio = radioGroup->mSelectedRadioButton;
|
||||
if (!currentRadio) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
int32_t index = radioGroup->mRadioButtons.IndexOf(currentRadio);
|
||||
if (index < 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
int32_t numRadios = radioGroup->mRadioButtons.Count();
|
||||
RefPtr<HTMLInputElement> radio;
|
||||
do {
|
||||
if (aPrevious) {
|
||||
if (--index < 0) {
|
||||
index = numRadios -1;
|
||||
}
|
||||
} else if (++index >= numRadios) {
|
||||
index = 0;
|
||||
}
|
||||
NS_ASSERTION(static_cast<nsGenericHTMLFormElement*>(radioGroup->mRadioButtons[index])->IsHTMLElement(nsGkAtoms::input),
|
||||
"mRadioButtons holding a non-radio button");
|
||||
radio = static_cast<HTMLInputElement*>(radioGroup->mRadioButtons[index]);
|
||||
} while (radio->Disabled() && radio != currentRadio);
|
||||
|
||||
radio.forget(aRadioOut);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
DocumentOrShadowRoot::AddToRadioGroup(const nsAString& aName,
|
||||
HTMLInputElement* aRadio)
|
||||
{
|
||||
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
||||
radioGroup->mRadioButtons.AppendObject(aRadio);
|
||||
|
||||
if (aRadio->IsRequired()) {
|
||||
radioGroup->mRequiredRadioCount++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DocumentOrShadowRoot::RemoveFromRadioGroup(const nsAString& aName,
|
||||
HTMLInputElement* aRadio)
|
||||
{
|
||||
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
||||
radioGroup->mRadioButtons.RemoveObject(aRadio);
|
||||
|
||||
if (aRadio->IsRequired()) {
|
||||
NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
|
||||
"mRequiredRadioCount about to wrap below 0!");
|
||||
radioGroup->mRequiredRadioCount--;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
DocumentOrShadowRoot::GetRequiredRadioCount(const nsAString& aName) const
|
||||
{
|
||||
nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
|
||||
return radioGroup ? radioGroup->mRequiredRadioCount : 0;
|
||||
}
|
||||
|
||||
void
|
||||
DocumentOrShadowRoot::RadioRequiredWillChange(const nsAString& aName,
|
||||
bool aRequiredAdded)
|
||||
{
|
||||
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
||||
|
||||
if (aRequiredAdded) {
|
||||
radioGroup->mRequiredRadioCount++;
|
||||
} else {
|
||||
NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
|
||||
"mRequiredRadioCount about to wrap below 0!");
|
||||
radioGroup->mRequiredRadioCount--;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
DocumentOrShadowRoot::GetValueMissingState(const nsAString& aName) const
|
||||
{
|
||||
nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
|
||||
return radioGroup && radioGroup->mGroupSuffersFromValueMissing;
|
||||
}
|
||||
|
||||
void
|
||||
DocumentOrShadowRoot::SetValueMissingState(const nsAString& aName, bool aValue)
|
||||
{
|
||||
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
||||
radioGroup->mGroupSuffersFromValueMissing = aValue;
|
||||
}
|
||||
|
||||
nsRadioGroupStruct*
|
||||
DocumentOrShadowRoot::GetRadioGroup(const nsAString& aName) const
|
||||
{
|
||||
nsRadioGroupStruct* radioGroup = nullptr;
|
||||
mRadioGroups.Get(aName, &radioGroup);
|
||||
return radioGroup;
|
||||
}
|
||||
|
||||
nsRadioGroupStruct*
|
||||
DocumentOrShadowRoot::GetOrCreateRadioGroup(const nsAString& aName)
|
||||
{
|
||||
return mRadioGroups.LookupForAdd(aName).OrInsert(
|
||||
[] () { return new nsRadioGroupStruct(); });
|
||||
}
|
||||
|
||||
void
|
||||
DocumentOrShadowRoot::Traverse(DocumentOrShadowRoot* tmp,
|
||||
nsCycleCollectionTraversalCallback &cb)
|
||||
{
|
||||
for (auto iter = tmp->mRadioGroups.Iter(); !iter.Done(); iter.Next()) {
|
||||
nsRadioGroupStruct* radioGroup = iter.UserData();
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
|
||||
cb, "mRadioGroups entry->mSelectedRadioButton");
|
||||
cb.NoteXPCOMChild(ToSupports(radioGroup->mSelectedRadioButton));
|
||||
|
||||
uint32_t i, count = radioGroup->mRadioButtons.Count();
|
||||
for (i = 0; i < count; ++i) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
|
||||
cb, "mRadioGroups entry->mRadioButtons[i]");
|
||||
cb.NoteXPCOMChild(radioGroup->mRadioButtons[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DocumentOrShadowRoot::Unlink(DocumentOrShadowRoot* tmp)
|
||||
{
|
||||
tmp->mRadioGroups.Clear();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -8,13 +8,16 @@
|
||||
#define mozilla_dom_DocumentOrShadowRoot_h__
|
||||
|
||||
#include "mozilla/dom/NameSpaceConstants.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsContentListDeclarations.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsIdentifierMapEntry.h"
|
||||
|
||||
class nsContentList;
|
||||
class nsCycleCollectionTraversalCallback;
|
||||
class nsIDocument;
|
||||
class nsINode;
|
||||
class nsIRadioVisitor;
|
||||
class nsWindowSizes;
|
||||
|
||||
namespace mozilla {
|
||||
@ -23,6 +26,9 @@ class StyleSheet;
|
||||
namespace dom {
|
||||
|
||||
class Element;
|
||||
class DocumentOrShadowRoot;
|
||||
class HTMLInputElement;
|
||||
struct nsRadioGroupStruct;
|
||||
class StyleSheetList;
|
||||
class ShadowRoot;
|
||||
|
||||
@ -45,6 +51,11 @@ public:
|
||||
explicit DocumentOrShadowRoot(nsIDocument&);
|
||||
explicit DocumentOrShadowRoot(mozilla::dom::ShadowRoot&);
|
||||
|
||||
// Unusual argument naming is because of cycle collection macros.
|
||||
static void Traverse(DocumentOrShadowRoot* tmp,
|
||||
nsCycleCollectionTraversalCallback &cb);
|
||||
static void Unlink(DocumentOrShadowRoot* tmp);
|
||||
|
||||
nsINode& AsNode()
|
||||
{
|
||||
return mAsNode;
|
||||
@ -186,6 +197,31 @@ public:
|
||||
|
||||
void ReportEmptyGetElementByIdArg();
|
||||
|
||||
// nsIRadioGroupContainer
|
||||
NS_IMETHOD WalkRadioGroup(const nsAString& aName,
|
||||
nsIRadioVisitor* aVisitor,
|
||||
bool aFlushContent);
|
||||
void SetCurrentRadioButton(const nsAString& aName,
|
||||
HTMLInputElement* aRadio);
|
||||
HTMLInputElement* GetCurrentRadioButton(const nsAString& aName);
|
||||
nsresult GetNextRadioButton(const nsAString& aName,
|
||||
const bool aPrevious,
|
||||
HTMLInputElement* aFocusedRadio,
|
||||
HTMLInputElement** aRadioOut);
|
||||
void AddToRadioGroup(const nsAString& aName,
|
||||
HTMLInputElement* aRadio);
|
||||
void RemoveFromRadioGroup(const nsAString& aName,
|
||||
HTMLInputElement* aRadio);
|
||||
uint32_t GetRequiredRadioCount(const nsAString& aName) const;
|
||||
void RadioRequiredWillChange(const nsAString& aName,
|
||||
bool aRequiredAdded);
|
||||
bool GetValueMissingState(const nsAString& aName) const;
|
||||
void SetValueMissingState(const nsAString& aName, bool aValue);
|
||||
|
||||
// for radio group
|
||||
nsRadioGroupStruct* GetRadioGroup(const nsAString& aName) const;
|
||||
nsRadioGroupStruct* GetOrCreateRadioGroup(const nsAString& aName);
|
||||
|
||||
protected:
|
||||
// Returns the reference to the sheet, if found in mStyleSheets.
|
||||
already_AddRefed<StyleSheet> RemoveSheet(StyleSheet& aSheet);
|
||||
@ -218,6 +254,8 @@ protected:
|
||||
*/
|
||||
nsTHashtable<nsIdentifierMapEntry> mIdentifierMap;
|
||||
|
||||
nsClassHashtable<nsStringHashKey, nsRadioGroupStruct> mRadioGroups;
|
||||
|
||||
nsINode& mAsNode;
|
||||
const Kind mKind;
|
||||
};
|
||||
|
@ -41,6 +41,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ShadowRoot, DocumentFragment)
|
||||
iter.Next()) {
|
||||
iter.Get()->Traverse(&cb);
|
||||
}
|
||||
DocumentOrShadowRoot::Traverse(tmp, cb);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ShadowRoot)
|
||||
@ -49,11 +50,13 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ShadowRoot)
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets)
|
||||
tmp->mIdentifierMap.Clear();
|
||||
DocumentOrShadowRoot::Unlink(tmp);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(DocumentFragment)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ShadowRoot)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIRadioGroupContainer)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DocumentFragment)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(ShadowRoot, DocumentFragment)
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsIdentifierMapEntry.h"
|
||||
#include "nsIRadioGroupContainer.h"
|
||||
#include "nsStubMutationObserver.h"
|
||||
#include "nsTHashtable.h"
|
||||
|
||||
@ -35,10 +36,12 @@ class Rule;
|
||||
namespace dom {
|
||||
|
||||
class Element;
|
||||
class HTMLInputElement;
|
||||
|
||||
class ShadowRoot final : public DocumentFragment,
|
||||
public DocumentOrShadowRoot,
|
||||
public nsStubMutationObserver
|
||||
public nsStubMutationObserver,
|
||||
public nsIRadioGroupContainer
|
||||
{
|
||||
public:
|
||||
NS_IMPL_FROMNODE_HELPER(ShadowRoot, IsShadowRoot());
|
||||
@ -207,6 +210,61 @@ public:
|
||||
|
||||
void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
|
||||
|
||||
// nsIRadioGroupContainer
|
||||
NS_IMETHOD WalkRadioGroup(const nsAString& aName,
|
||||
nsIRadioVisitor* aVisitor,
|
||||
bool aFlushContent) override
|
||||
{
|
||||
return DocumentOrShadowRoot::WalkRadioGroup(aName, aVisitor, aFlushContent);
|
||||
}
|
||||
virtual void
|
||||
SetCurrentRadioButton(const nsAString& aName,
|
||||
HTMLInputElement* aRadio) override
|
||||
{
|
||||
DocumentOrShadowRoot::SetCurrentRadioButton(aName, aRadio);
|
||||
}
|
||||
virtual HTMLInputElement*
|
||||
GetCurrentRadioButton(const nsAString& aName) override
|
||||
{
|
||||
return DocumentOrShadowRoot::GetCurrentRadioButton(aName);
|
||||
}
|
||||
NS_IMETHOD
|
||||
GetNextRadioButton(const nsAString& aName,
|
||||
const bool aPrevious,
|
||||
HTMLInputElement* aFocusedRadio,
|
||||
HTMLInputElement** aRadioOut) override
|
||||
{
|
||||
return DocumentOrShadowRoot::GetNextRadioButton(aName, aPrevious,
|
||||
aFocusedRadio, aRadioOut);
|
||||
}
|
||||
virtual void AddToRadioGroup(const nsAString& aName,
|
||||
HTMLInputElement* aRadio) override
|
||||
{
|
||||
DocumentOrShadowRoot::AddToRadioGroup(aName, aRadio);
|
||||
}
|
||||
virtual void RemoveFromRadioGroup(const nsAString& aName,
|
||||
HTMLInputElement* aRadio) override
|
||||
{
|
||||
DocumentOrShadowRoot::RemoveFromRadioGroup(aName, aRadio);
|
||||
}
|
||||
virtual uint32_t GetRequiredRadioCount(const nsAString& aName) const override
|
||||
{
|
||||
return DocumentOrShadowRoot::GetRequiredRadioCount(aName);
|
||||
}
|
||||
virtual void RadioRequiredWillChange(const nsAString& aName,
|
||||
bool aRequiredAdded) override
|
||||
{
|
||||
DocumentOrShadowRoot::RadioRequiredWillChange(aName, aRequiredAdded);
|
||||
}
|
||||
virtual bool GetValueMissingState(const nsAString& aName) const override
|
||||
{
|
||||
return DocumentOrShadowRoot::GetValueMissingState(aName);
|
||||
}
|
||||
virtual void SetValueMissingState(const nsAString& aName, bool aValue) override
|
||||
{
|
||||
return DocumentOrShadowRoot::SetValueMissingState(aName, aValue);
|
||||
}
|
||||
|
||||
protected:
|
||||
// FIXME(emilio): This will need to become more fine-grained.
|
||||
void ApplicableRulesChanged();
|
||||
|
@ -119,10 +119,6 @@
|
||||
#include "nsFocusManager.h"
|
||||
#include "nsICookieService.h"
|
||||
|
||||
// for radio group stuff
|
||||
#include "nsIRadioVisitor.h"
|
||||
#include "nsIFormControl.h"
|
||||
|
||||
#include "nsBidiUtils.h"
|
||||
|
||||
#include "nsContentCreatorFunctions.h"
|
||||
@ -724,26 +720,6 @@ public:
|
||||
nsIDocument *mSubDocument;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A struct that holds all the information about a radio group.
|
||||
*/
|
||||
struct nsRadioGroupStruct
|
||||
{
|
||||
nsRadioGroupStruct()
|
||||
: mRequiredRadioCount(0)
|
||||
, mGroupSuffersFromValueMissing(false)
|
||||
{}
|
||||
|
||||
/**
|
||||
* A strong pointer to the currently selected radio button.
|
||||
*/
|
||||
RefPtr<HTMLInputElement> mSelectedRadioButton;
|
||||
nsCOMArray<nsIFormControl> mRadioButtons;
|
||||
uint32_t mRequiredRadioCount;
|
||||
bool mGroupSuffersFromValueMissing;
|
||||
};
|
||||
|
||||
// nsOnloadBlocker implementation
|
||||
NS_IMPL_ISUPPORTS(nsOnloadBlocker, nsIRequest)
|
||||
|
||||
@ -1924,19 +1900,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader)
|
||||
|
||||
for (auto iter = tmp->mRadioGroups.Iter(); !iter.Done(); iter.Next()) {
|
||||
nsRadioGroupStruct* radioGroup = iter.UserData();
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
|
||||
cb, "mRadioGroups entry->mSelectedRadioButton");
|
||||
cb.NoteXPCOMChild(ToSupports(radioGroup->mSelectedRadioButton));
|
||||
|
||||
uint32_t i, count = radioGroup->mRadioButtons.Count();
|
||||
for (i = 0; i < count; ++i) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
|
||||
cb, "mRadioGroups entry->mRadioButtons[i]");
|
||||
cb.NoteXPCOMChild(radioGroup->mRadioButtons[i]);
|
||||
}
|
||||
}
|
||||
DocumentOrShadowRoot::Traverse(tmp, cb);
|
||||
|
||||
// The boxobject for an element will only exist as long as it's in the
|
||||
// document, so we'll traverse the table here instead of from the element.
|
||||
@ -2092,7 +2056,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
|
||||
"How did we get here without our presshell going away "
|
||||
"first?");
|
||||
|
||||
tmp->mRadioGroups.Clear();
|
||||
DocumentOrShadowRoot::Unlink(tmp);
|
||||
|
||||
// nsDocument has a pretty complex destructor, so we're going to
|
||||
// assume that *most* cycles you actually want to break somewhere
|
||||
@ -7713,163 +7677,6 @@ nsIDocument::IsScriptEnabled()
|
||||
return xpc::Scriptability::Get(globalObject->GetGlobalJSObject()).Allowed();
|
||||
}
|
||||
|
||||
nsRadioGroupStruct*
|
||||
nsDocument::GetRadioGroup(const nsAString& aName) const
|
||||
{
|
||||
nsRadioGroupStruct* radioGroup = nullptr;
|
||||
mRadioGroups.Get(aName, &radioGroup);
|
||||
return radioGroup;
|
||||
}
|
||||
|
||||
nsRadioGroupStruct*
|
||||
nsDocument::GetOrCreateRadioGroup(const nsAString& aName)
|
||||
{
|
||||
return mRadioGroups.LookupForAdd(aName).OrInsert(
|
||||
[] () { return new nsRadioGroupStruct(); });
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::SetCurrentRadioButton(const nsAString& aName,
|
||||
HTMLInputElement* aRadio)
|
||||
{
|
||||
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
||||
radioGroup->mSelectedRadioButton = aRadio;
|
||||
}
|
||||
|
||||
HTMLInputElement*
|
||||
nsDocument::GetCurrentRadioButton(const nsAString& aName)
|
||||
{
|
||||
return GetOrCreateRadioGroup(aName)->mSelectedRadioButton;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocument::GetNextRadioButton(const nsAString& aName,
|
||||
const bool aPrevious,
|
||||
HTMLInputElement* aFocusedRadio,
|
||||
HTMLInputElement** aRadioOut)
|
||||
{
|
||||
// XXX Can we combine the HTML radio button method impls of
|
||||
// nsDocument and nsHTMLFormControl?
|
||||
// XXX Why is HTML radio button stuff in nsDocument, as
|
||||
// opposed to nsHTMLDocument?
|
||||
*aRadioOut = nullptr;
|
||||
|
||||
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
||||
|
||||
// Return the radio button relative to the focused radio button.
|
||||
// If no radio is focused, get the radio relative to the selected one.
|
||||
RefPtr<HTMLInputElement> currentRadio;
|
||||
if (aFocusedRadio) {
|
||||
currentRadio = aFocusedRadio;
|
||||
}
|
||||
else {
|
||||
currentRadio = radioGroup->mSelectedRadioButton;
|
||||
if (!currentRadio) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
int32_t index = radioGroup->mRadioButtons.IndexOf(currentRadio);
|
||||
if (index < 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
int32_t numRadios = radioGroup->mRadioButtons.Count();
|
||||
RefPtr<HTMLInputElement> radio;
|
||||
do {
|
||||
if (aPrevious) {
|
||||
if (--index < 0) {
|
||||
index = numRadios -1;
|
||||
}
|
||||
}
|
||||
else if (++index >= numRadios) {
|
||||
index = 0;
|
||||
}
|
||||
NS_ASSERTION(static_cast<nsGenericHTMLFormElement*>(radioGroup->mRadioButtons[index])->IsHTMLElement(nsGkAtoms::input),
|
||||
"mRadioButtons holding a non-radio button");
|
||||
radio = static_cast<HTMLInputElement*>(radioGroup->mRadioButtons[index]);
|
||||
} while (radio->Disabled() && radio != currentRadio);
|
||||
|
||||
radio.forget(aRadioOut);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::AddToRadioGroup(const nsAString& aName,
|
||||
HTMLInputElement* aRadio)
|
||||
{
|
||||
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
||||
radioGroup->mRadioButtons.AppendObject(aRadio);
|
||||
|
||||
if (aRadio->IsRequired()) {
|
||||
radioGroup->mRequiredRadioCount++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::RemoveFromRadioGroup(const nsAString& aName,
|
||||
HTMLInputElement* aRadio)
|
||||
{
|
||||
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
||||
radioGroup->mRadioButtons.RemoveObject(aRadio);
|
||||
|
||||
if (aRadio->IsRequired()) {
|
||||
NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
|
||||
"mRequiredRadioCount about to wrap below 0!");
|
||||
radioGroup->mRequiredRadioCount--;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocument::WalkRadioGroup(const nsAString& aName,
|
||||
nsIRadioVisitor* aVisitor,
|
||||
bool aFlushContent)
|
||||
{
|
||||
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
||||
|
||||
for (int i = 0; i < radioGroup->mRadioButtons.Count(); i++) {
|
||||
if (!aVisitor->Visit(radioGroup->mRadioButtons[i])) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsDocument::GetRequiredRadioCount(const nsAString& aName) const
|
||||
{
|
||||
nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
|
||||
return radioGroup ? radioGroup->mRequiredRadioCount : 0;
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::RadioRequiredWillChange(const nsAString& aName, bool aRequiredAdded)
|
||||
{
|
||||
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
||||
|
||||
if (aRequiredAdded) {
|
||||
radioGroup->mRequiredRadioCount++;
|
||||
} else {
|
||||
NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
|
||||
"mRequiredRadioCount about to wrap below 0!");
|
||||
radioGroup->mRequiredRadioCount--;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsDocument::GetValueMissingState(const nsAString& aName) const
|
||||
{
|
||||
nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
|
||||
return radioGroup && radioGroup->mGroupSuffersFromValueMissing;
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::SetValueMissingState(const nsAString& aName, bool aValue)
|
||||
{
|
||||
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
||||
radioGroup->mGroupSuffersFromValueMissing = aValue;
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::RetrieveRelevantHeaders(nsIChannel *aChannel)
|
||||
{
|
||||
|
@ -66,9 +66,7 @@
|
||||
|
||||
class nsDOMStyleSheetSetList;
|
||||
class nsDocument;
|
||||
class nsIRadioVisitor;
|
||||
class nsIFormControl;
|
||||
struct nsRadioGroupStruct;
|
||||
class nsOnloadBlocker;
|
||||
class nsDOMNavigationTiming;
|
||||
class nsWindowSizes;
|
||||
@ -147,30 +145,57 @@ public:
|
||||
// nsIRadioGroupContainer
|
||||
NS_IMETHOD WalkRadioGroup(const nsAString& aName,
|
||||
nsIRadioVisitor* aVisitor,
|
||||
bool aFlushContent) override;
|
||||
bool aFlushContent) override
|
||||
{
|
||||
return DocumentOrShadowRoot::WalkRadioGroup(aName, aVisitor, aFlushContent);
|
||||
}
|
||||
virtual void
|
||||
SetCurrentRadioButton(const nsAString& aName,
|
||||
mozilla::dom::HTMLInputElement* aRadio) override;
|
||||
mozilla::dom::HTMLInputElement* aRadio) override
|
||||
{
|
||||
DocumentOrShadowRoot::SetCurrentRadioButton(aName, aRadio);
|
||||
}
|
||||
virtual mozilla::dom::HTMLInputElement*
|
||||
GetCurrentRadioButton(const nsAString& aName) override;
|
||||
GetCurrentRadioButton(const nsAString& aName) override
|
||||
{
|
||||
return DocumentOrShadowRoot::GetCurrentRadioButton(aName);
|
||||
}
|
||||
NS_IMETHOD
|
||||
GetNextRadioButton(const nsAString& aName,
|
||||
const bool aPrevious,
|
||||
mozilla::dom::HTMLInputElement* aFocusedRadio,
|
||||
mozilla::dom::HTMLInputElement** aRadioOut) override;
|
||||
mozilla::dom::HTMLInputElement* aFocusedRadio,
|
||||
mozilla::dom::HTMLInputElement** aRadioOut) override
|
||||
{
|
||||
return DocumentOrShadowRoot::GetNextRadioButton(aName, aPrevious,
|
||||
aFocusedRadio, aRadioOut);
|
||||
}
|
||||
virtual void AddToRadioGroup(const nsAString& aName,
|
||||
mozilla::dom::HTMLInputElement* aRadio) override;
|
||||
mozilla::dom::HTMLInputElement* aRadio) override
|
||||
{
|
||||
DocumentOrShadowRoot::AddToRadioGroup(aName, aRadio);
|
||||
}
|
||||
virtual void RemoveFromRadioGroup(const nsAString& aName,
|
||||
mozilla::dom::HTMLInputElement* aRadio) override;
|
||||
virtual uint32_t GetRequiredRadioCount(const nsAString& aName) const override;
|
||||
mozilla::dom::HTMLInputElement* aRadio) override
|
||||
{
|
||||
DocumentOrShadowRoot::RemoveFromRadioGroup(aName, aRadio);
|
||||
}
|
||||
virtual uint32_t GetRequiredRadioCount(const nsAString& aName) const override
|
||||
{
|
||||
return DocumentOrShadowRoot::GetRequiredRadioCount(aName);
|
||||
}
|
||||
virtual void RadioRequiredWillChange(const nsAString& aName,
|
||||
bool aRequiredAdded) override;
|
||||
virtual bool GetValueMissingState(const nsAString& aName) const override;
|
||||
virtual void SetValueMissingState(const nsAString& aName, bool aValue) override;
|
||||
|
||||
// for radio group
|
||||
nsRadioGroupStruct* GetRadioGroup(const nsAString& aName) const;
|
||||
nsRadioGroupStruct* GetOrCreateRadioGroup(const nsAString& aName);
|
||||
bool aRequiredAdded) override
|
||||
{
|
||||
DocumentOrShadowRoot::RadioRequiredWillChange(aName, aRequiredAdded);
|
||||
}
|
||||
virtual bool GetValueMissingState(const nsAString& aName) const override
|
||||
{
|
||||
return DocumentOrShadowRoot::GetValueMissingState(aName);
|
||||
}
|
||||
virtual void SetValueMissingState(const nsAString& aName, bool aValue) override
|
||||
{
|
||||
return DocumentOrShadowRoot::SetValueMissingState(aName, aValue);
|
||||
}
|
||||
|
||||
// Check whether shadow DOM is enabled for aGlobal.
|
||||
static bool IsShadowDOMEnabled(JSContext* aCx, JSObject* aGlobal);
|
||||
@ -262,8 +287,6 @@ public:
|
||||
// include https://github.com/rust-lang-nursery/rust-bindgen/pull/1271.
|
||||
js::ExpandoAndGeneration mExpandoAndGeneration;
|
||||
|
||||
nsClassHashtable<nsStringHashKey, nsRadioGroupStruct> mRadioGroups;
|
||||
|
||||
friend class nsCallRequestFullscreen;
|
||||
|
||||
// The application cache that this document is associated with, if
|
||||
|
@ -66,6 +66,10 @@
|
||||
using namespace mozilla::tasktracer;
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
#include "ProfilerMarkerPayload.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace dom;
|
||||
@ -1109,8 +1113,49 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
|
||||
EventChainPostVisitor postVisitor(preVisitor);
|
||||
MOZ_RELEASE_ASSERT(!aEvent->mPath);
|
||||
aEvent->mPath = &chain;
|
||||
EventTargetChainItem::HandleEventTargetChain(chain, postVisitor,
|
||||
aCallback, cd);
|
||||
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
if (profiler_is_active()) {
|
||||
// Add a profiler label and a profiler marker for the actual
|
||||
// dispatch of the event.
|
||||
// This is a very hot code path, so we need to make sure not to
|
||||
// do this extra work when we're not profiling.
|
||||
if (!postVisitor.mDOMEvent) {
|
||||
// This is tiny bit slow, but happens only once per event.
|
||||
// Similar code also in EventListenerManager.
|
||||
nsCOMPtr<EventTarget> et = aEvent->mOriginalTarget;
|
||||
RefPtr<Event> event = EventDispatcher::CreateEvent(et, aPresContext,
|
||||
aEvent,
|
||||
EmptyString());
|
||||
event.swap(postVisitor.mDOMEvent);
|
||||
}
|
||||
nsAutoString typeStr;
|
||||
postVisitor.mDOMEvent->GetType(typeStr);
|
||||
AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
|
||||
"EventDispatcher::Dispatch", OTHER, typeStr);
|
||||
|
||||
profiler_add_marker(
|
||||
"DOMEvent",
|
||||
MakeUnique<DOMEventMarkerPayload>(typeStr,
|
||||
aEvent->mTimeStamp,
|
||||
"DOMEvent",
|
||||
TRACING_INTERVAL_START));
|
||||
|
||||
EventTargetChainItem::HandleEventTargetChain(chain, postVisitor,
|
||||
aCallback, cd);
|
||||
|
||||
profiler_add_marker(
|
||||
"DOMEvent",
|
||||
MakeUnique<DOMEventMarkerPayload>(typeStr,
|
||||
aEvent->mTimeStamp,
|
||||
"DOMEvent",
|
||||
TRACING_INTERVAL_END));
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
EventTargetChainItem::HandleEventTargetChain(chain, postVisitor,
|
||||
aCallback, cd);
|
||||
}
|
||||
aEvent->mPath = nullptr;
|
||||
|
||||
preVisitor.mEventStatus = postVisitor.mEventStatus;
|
||||
|
@ -29,7 +29,6 @@
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
||||
#include "EventListenerService.h"
|
||||
#include "GeckoProfiler.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsContentUtils.h"
|
||||
@ -52,10 +51,6 @@
|
||||
#include "nsIFrame.h"
|
||||
#include "nsDisplayList.h"
|
||||
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
#include "ProfilerMarkerPayload.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace dom;
|
||||
@ -1249,6 +1244,7 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
|
||||
(aEvent->IsTrusted() || listener->mFlags.mAllowUntrustedEvents)) {
|
||||
if (!*aDOMEvent) {
|
||||
// This is tiny bit slow, but happens only once per event.
|
||||
// Similar code also in EventDispatcher.
|
||||
nsCOMPtr<EventTarget> et = aEvent->mOriginalTarget;
|
||||
RefPtr<Event> event = EventDispatcher::CreateEvent(et, aPresContext,
|
||||
aEvent,
|
||||
@ -1301,46 +1297,16 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
|
||||
hasRemovedListener = true;
|
||||
}
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
nsCOMPtr<nsPIDOMWindowInner> innerWindow =
|
||||
WindowFromListener(listener, aItemInShadowTree);
|
||||
mozilla::dom::Event* oldWindowEvent = nullptr;
|
||||
if (innerWindow) {
|
||||
oldWindowEvent = innerWindow->SetEvent(*aDOMEvent);
|
||||
}
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
if (profiler_is_active()) {
|
||||
// Add a profiler label and a profiler marker for the actual
|
||||
// dispatch of the event.
|
||||
// This is a very hot code path, so we need to make sure not to
|
||||
// do this extra work when we're not profiling.
|
||||
nsAutoString typeStr;
|
||||
(*aDOMEvent)->GetType(typeStr);
|
||||
AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
|
||||
"EventListenerManager::HandleEventInternal", OTHER, typeStr);
|
||||
|
||||
uint16_t phase = (*aDOMEvent)->EventPhase();
|
||||
profiler_add_marker(
|
||||
"DOMEvent",
|
||||
MakeUnique<DOMEventMarkerPayload>(typeStr, phase,
|
||||
aEvent->mTimeStamp,
|
||||
"DOMEvent",
|
||||
TRACING_INTERVAL_START));
|
||||
nsresult rv =
|
||||
HandleEventSubType(listener, *aDOMEvent, aCurrentTarget);
|
||||
|
||||
rv = HandleEventSubType(listener, *aDOMEvent, aCurrentTarget);
|
||||
|
||||
phase = (*aDOMEvent)->EventPhase();
|
||||
profiler_add_marker(
|
||||
"DOMEvent",
|
||||
MakeUnique<DOMEventMarkerPayload>(typeStr, phase,
|
||||
aEvent->mTimeStamp,
|
||||
"DOMEvent",
|
||||
TRACING_INTERVAL_END));
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
rv = HandleEventSubType(listener, *aDOMEvent, aCurrentTarget);
|
||||
}
|
||||
if (innerWindow) {
|
||||
Unused << innerWindow->SetEvent(oldWindowEvent);
|
||||
}
|
||||
|
@ -3052,8 +3052,14 @@ HTMLInputElement::GetRadioGroupContainer() const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//XXXsmaug It isn't clear how this should work in Shadow DOM.
|
||||
return static_cast<nsDocument*>(GetUncomposedDoc());
|
||||
DocumentOrShadowRoot* docOrShadow = GetUncomposedDocOrConnectedShadowRoot();
|
||||
if (!docOrShadow) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRadioGroupContainer> container =
|
||||
do_QueryInterface(&(docOrShadow->AsNode()));
|
||||
return container;
|
||||
}
|
||||
|
||||
HTMLInputElement*
|
||||
@ -4632,7 +4638,8 @@ HTMLInputElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
|
||||
// Add radio to document if we don't have a form already (if we do it's
|
||||
// already been added into that group)
|
||||
if (aDocument && !mForm && mType == NS_FORM_INPUT_RADIO) {
|
||||
if (!mForm && mType == NS_FORM_INPUT_RADIO &&
|
||||
GetUncomposedDocOrConnectedShadowRoot()) {
|
||||
AddedToRadioGroup();
|
||||
}
|
||||
|
||||
@ -6567,7 +6574,7 @@ HTMLInputElement::AddedToRadioGroup()
|
||||
{
|
||||
// If the element is neither in a form nor a document, there is no group so we
|
||||
// should just stop here.
|
||||
if (!mForm && (!IsInUncomposedDoc() || IsInAnonymousSubtree())) {
|
||||
if (!mForm && (!GetUncomposedDocOrConnectedShadowRoot() || IsInAnonymousSubtree())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -621,7 +621,16 @@ AsyncImagePipelineManager::ProcessPipelineRemoved(const wr::PipelineId& aPipelin
|
||||
return;
|
||||
}
|
||||
if (auto entry = mPipelineTexturesHolders.Lookup(wr::AsUint64(aPipelineId))) {
|
||||
if (entry.Data()->mDestroyedEpoch.isSome()) {
|
||||
PipelineTexturesHolder* holder = entry.Data();
|
||||
if (holder->mDestroyedEpoch.isSome()) {
|
||||
// Explicitly release all of the shared surfaces.
|
||||
while (!holder->mExternalImages.empty()) {
|
||||
DebugOnly<bool> released =
|
||||
SharedSurfacesParent::Release(holder->mExternalImages.front().mImageId);
|
||||
MOZ_ASSERT(released);
|
||||
holder->mExternalImages.pop();
|
||||
}
|
||||
|
||||
// Remove Pipeline
|
||||
entry.Remove();
|
||||
}
|
||||
|
@ -546,6 +546,7 @@ private:
|
||||
DECL_GFX_PREF(Live, "image.animated.generate-full-frames", ImageAnimatedGenerateFullFrames, bool, false);
|
||||
DECL_GFX_PREF(Live, "image.animated.resume-from-last-displayed", ImageAnimatedResumeFromLastDisplayed, bool, false);
|
||||
DECL_GFX_PREF(Live, "image.cache.factor2.threshold-surfaces", ImageCacheFactor2ThresholdSurfaces, int32_t, -1);
|
||||
DECL_GFX_PREF(Live, "image.cache.max-rasterized-svg-threshold-kb", ImageCacheMaxRasterizedSVGThresholdKB, int32_t, 90*1024);
|
||||
DECL_GFX_PREF(Once, "image.cache.size", ImageCacheSize, int32_t, 5*1024*1024);
|
||||
DECL_GFX_PREF(Once, "image.cache.timeweight", ImageCacheTimeWeight, int32_t, 500);
|
||||
DECL_GFX_PREF(Live, "image.decode-immediately.enabled", ImageDecodeImmediatelyEnabled, bool, false);
|
||||
|
@ -160,6 +160,7 @@ ImageResource::GetImageContainerImpl(LayerManager* aManager,
|
||||
case ImgDrawResult::SUCCESS:
|
||||
case ImgDrawResult::BAD_IMAGE:
|
||||
case ImgDrawResult::BAD_ARGS:
|
||||
case ImgDrawResult::NOT_SUPPORTED:
|
||||
container.forget(aOutContainer);
|
||||
return entry->mLastDrawResult;
|
||||
case ImgDrawResult::NOT_READY:
|
||||
@ -218,6 +219,7 @@ ImageResource::GetImageContainerImpl(LayerManager* aManager,
|
||||
case ImgDrawResult::SUCCESS:
|
||||
case ImgDrawResult::BAD_IMAGE:
|
||||
case ImgDrawResult::BAD_ARGS:
|
||||
case ImgDrawResult::NOT_SUPPORTED:
|
||||
container.forget(aOutContainer);
|
||||
return entry->mLastDrawResult;
|
||||
case ImgDrawResult::NOT_READY:
|
||||
|
@ -337,6 +337,24 @@ protected:
|
||||
bool mAnimating:1; // Are we currently animating?
|
||||
bool mError:1; // Error handling
|
||||
|
||||
/**
|
||||
* Attempt to find a matching cached surface in the SurfaceCache, and if not
|
||||
* available, request the production of such a surface (either synchronously
|
||||
* or asynchronously).
|
||||
*
|
||||
* If the draw result is BAD_IMAGE, BAD_ARGS or NOT_READY, the size will be
|
||||
* the same as aSize. If it is TEMPORARY_ERROR, INCOMPLETE, or SUCCESS, the
|
||||
* size is a hint as to what we expect the surface size to be, once the best
|
||||
* fitting size is available. It may or may not match the size of the surface
|
||||
* returned at this moment. This is useful for choosing how to store the final
|
||||
* result (e.g. if going into an ImageContainer, ideally we would share the
|
||||
* same container for many requested sizes, if they all end up with the same
|
||||
* best fit size in the end).
|
||||
*
|
||||
* A valid surface should only be returned for SUCCESS and INCOMPLETE.
|
||||
*
|
||||
* Any other draw result is invalid.
|
||||
*/
|
||||
virtual Tuple<ImgDrawResult, gfx::IntSize, RefPtr<gfx::SourceSurface>>
|
||||
GetFrameInternal(const gfx::IntSize& aSize,
|
||||
const Maybe<SVGImageContext>& aSVGContext,
|
||||
|
@ -109,8 +109,11 @@ private:
|
||||
DrawableSurface mSurface;
|
||||
MatchType mMatchType;
|
||||
|
||||
/// If given, the size the caller should request a decode at. This may or may
|
||||
/// not match the size the caller requested from the cache.
|
||||
/// mSuggestedSize will be the size of the returned surface if the result is
|
||||
/// SUBSTITUTE_BECAUSE_BEST. It will be empty for EXACT, and can contain a
|
||||
/// non-empty size possibly different from the returned surface (if any) for
|
||||
/// all other results. If non-empty, it will always be the size the caller
|
||||
/// should request any decodes at.
|
||||
gfx::IntSize mSuggestedSize;
|
||||
};
|
||||
|
||||
|
@ -24,7 +24,8 @@ struct SVGDrawingParameters
|
||||
typedef mozilla::gfx::SamplingFilter SamplingFilter;
|
||||
|
||||
SVGDrawingParameters(gfxContext* aContext,
|
||||
const nsIntSize& aSize,
|
||||
const nsIntSize& aRasterSize,
|
||||
const nsIntSize& aDrawSize,
|
||||
const ImageRegion& aRegion,
|
||||
SamplingFilter aSamplingFilter,
|
||||
const Maybe<SVGImageContext>& aSVGContext,
|
||||
@ -32,11 +33,12 @@ struct SVGDrawingParameters
|
||||
uint32_t aFlags,
|
||||
float aOpacity)
|
||||
: context(aContext)
|
||||
, size(aSize.width, aSize.height)
|
||||
, size(aRasterSize)
|
||||
, drawSize(aDrawSize)
|
||||
, region(aRegion)
|
||||
, samplingFilter(aSamplingFilter)
|
||||
, svgContext(aSVGContext)
|
||||
, viewportSize(aSize)
|
||||
, viewportSize(aRasterSize)
|
||||
, animationTime(aAnimationTime)
|
||||
, flags(aFlags)
|
||||
, opacity(aOpacity)
|
||||
@ -50,7 +52,8 @@ struct SVGDrawingParameters
|
||||
}
|
||||
|
||||
gfxContext* context;
|
||||
IntSize size;
|
||||
IntSize size; // Size to rasterize a surface at.
|
||||
IntSize drawSize; // Size to draw the given surface at.
|
||||
ImageRegion region;
|
||||
SamplingFilter samplingFilter;
|
||||
const Maybe<SVGImageContext>& svgContext;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <algorithm>
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/Move.h"
|
||||
@ -251,10 +252,11 @@ class ImageSurfaceCache
|
||||
{
|
||||
~ImageSurfaceCache() { }
|
||||
public:
|
||||
ImageSurfaceCache()
|
||||
explicit ImageSurfaceCache(const ImageKey aImageKey)
|
||||
: mLocked(false)
|
||||
, mFactor2Mode(false)
|
||||
, mFactor2Pruned(false)
|
||||
, mIsVectorImage(aImageKey->GetType() == imgIContainer::TYPE_VECTOR)
|
||||
{ }
|
||||
|
||||
MOZ_DECLARE_REFCOUNTED_TYPENAME(ImageSurfaceCache)
|
||||
@ -330,7 +332,7 @@ public:
|
||||
|
||||
// Try for a best match second, if using compact.
|
||||
IntSize suggestedSize = SuggestedSize(aIdealKey.Size());
|
||||
if (mFactor2Mode) {
|
||||
if (suggestedSize != aIdealKey.Size()) {
|
||||
if (!exactMatch) {
|
||||
SurfaceKey compactKey = aIdealKey.CloneWithSize(suggestedSize);
|
||||
mSurfaces.Get(compactKey, getter_AddRefs(exactMatch));
|
||||
@ -401,7 +403,7 @@ public:
|
||||
} else if (aIdealKey.Size() != bestMatch->GetSurfaceKey().Size()) {
|
||||
// The best factor of 2 match is still decoding, but the best we've got.
|
||||
MOZ_ASSERT(suggestedSize != aIdealKey.Size());
|
||||
MOZ_ASSERT(mFactor2Mode);
|
||||
MOZ_ASSERT(mFactor2Mode || mIsVectorImage);
|
||||
matchType = MatchType::SUBSTITUTE_BECAUSE_BEST;
|
||||
} else {
|
||||
// The exact match is still decoding, but it's the best we've got.
|
||||
@ -433,20 +435,18 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine how many native surfaces this image has. Zero means we either
|
||||
// don't know yet (in which case do nothing), or we don't want to limit the
|
||||
// number of surfaces for this image.
|
||||
//
|
||||
// XXX(aosmond): Vector images have zero native sizes. This is because they
|
||||
// are regenerated at the given size. There isn't an equivalent concept to
|
||||
// the native size (and w/h ratio) to provide a frame of reference to what
|
||||
// are "good" sizes. While it is desirable to have a similar mechanism as
|
||||
// that for raster images, it will need a different approach.
|
||||
// Determine how many native surfaces this image has. If it is zero, and it
|
||||
// is a vector image, then we should impute a single native size. Otherwise,
|
||||
// it may be zero because we don't know yet, or the image has an error, or
|
||||
// it isn't supported.
|
||||
auto first = ConstIter();
|
||||
NotNull<CachedSurface*> current = WrapNotNull(first.UserData());
|
||||
Image* image = static_cast<Image*>(current->GetImageKey());
|
||||
size_t nativeSizes = image->GetNativeSizesLength();
|
||||
if (nativeSizes == 0) {
|
||||
if (mIsVectorImage) {
|
||||
MOZ_ASSERT(nativeSizes == 0);
|
||||
nativeSizes = 1;
|
||||
} else if (nativeSizes == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -530,6 +530,33 @@ public:
|
||||
}
|
||||
|
||||
IntSize SuggestedSize(const IntSize& aSize) const
|
||||
{
|
||||
IntSize suggestedSize = SuggestedSizeInternal(aSize);
|
||||
if (mIsVectorImage) {
|
||||
// Whether or not we are in factor of 2 mode, vector image rasterization is
|
||||
// clamped at a configured maximum if the caller is willing to accept
|
||||
// substitutes.
|
||||
MOZ_ASSERT(SurfaceCache::IsLegalSize(suggestedSize));
|
||||
|
||||
// If we exceed the maximum, we need to scale the size downwards to fit.
|
||||
// It shouldn't get here if it is significantly larger because
|
||||
// VectorImage::UseSurfaceCacheForSize should prevent us from requesting
|
||||
// a rasterized version of a surface greater than 4x the maximum.
|
||||
int32_t maxSizeKB = gfxPrefs::ImageCacheMaxRasterizedSVGThresholdKB();
|
||||
int32_t proposedKB = suggestedSize.width * suggestedSize.height / 256;
|
||||
if (maxSizeKB >= proposedKB) {
|
||||
return suggestedSize;
|
||||
}
|
||||
|
||||
double scale = sqrt(double(maxSizeKB) / proposedKB);
|
||||
suggestedSize.width = int32_t(scale * suggestedSize.width);
|
||||
suggestedSize.height = int32_t(scale * suggestedSize.height);
|
||||
}
|
||||
|
||||
return suggestedSize;
|
||||
}
|
||||
|
||||
IntSize SuggestedSizeInternal(const IntSize& aSize) const
|
||||
{
|
||||
// When not in factor of 2 mode, we can always decode at the given size.
|
||||
if (!mFactor2Mode) {
|
||||
@ -552,11 +579,48 @@ public:
|
||||
NS_FAILED(image->GetHeight(&factorSize.height)) ||
|
||||
factorSize.IsEmpty()) {
|
||||
// We should not have entered factor of 2 mode without a valid size, and
|
||||
// several successfully decoded surfaces.
|
||||
// several successfully decoded surfaces. Note that valid vector images
|
||||
// may have a default size of 0x0, and those are not yet supported.
|
||||
MOZ_ASSERT_UNREACHABLE("Expected valid native size!");
|
||||
return aSize;
|
||||
}
|
||||
|
||||
if (mIsVectorImage) {
|
||||
// Ensure the aspect ratio matches the native size before forcing the
|
||||
// caller to accept a factor of 2 size. The difference between the aspect
|
||||
// ratios is:
|
||||
//
|
||||
// delta = nativeWidth/nativeHeight - desiredWidth/desiredHeight
|
||||
//
|
||||
// delta*nativeHeight*desiredHeight = nativeWidth*desiredHeight
|
||||
// - desiredWidth*nativeHeight
|
||||
//
|
||||
// Using the maximum accepted delta as a constant, we can avoid the
|
||||
// floating point division and just compare after some integer ops.
|
||||
int32_t delta = factorSize.width * aSize.height - aSize.width * factorSize.height;
|
||||
int32_t maxDelta = (factorSize.height * aSize.height) >> 4;
|
||||
if (delta > maxDelta || delta < -maxDelta) {
|
||||
return aSize;
|
||||
}
|
||||
|
||||
// If the requested size is bigger than the native size, we actually need
|
||||
// to grow the native size instead of shrinking it.
|
||||
if (factorSize.width < aSize.width) {
|
||||
do {
|
||||
IntSize candidate(factorSize.width * 2, factorSize.height * 2);
|
||||
if (!SurfaceCache::IsLegalSize(candidate)) {
|
||||
break;
|
||||
}
|
||||
|
||||
factorSize = candidate;
|
||||
} while (factorSize.width < aSize.width);
|
||||
|
||||
return factorSize;
|
||||
}
|
||||
|
||||
// Otherwise we can find the best fit as normal.
|
||||
}
|
||||
|
||||
// Start with the native size as the best first guess.
|
||||
IntSize bestSize = factorSize;
|
||||
factorSize.width /= 2;
|
||||
@ -674,6 +738,10 @@ private:
|
||||
// True if all non-factor of 2 surfaces have been removed from the cache. Note
|
||||
// that this excludes unsubstitutable sizes.
|
||||
bool mFactor2Pruned;
|
||||
|
||||
// True if the surfaces are produced from a vector image. If so, it must match
|
||||
// the aspect ratio when using factor of 2 mode.
|
||||
bool mIsVectorImage;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -760,9 +828,10 @@ public:
|
||||
|
||||
// Locate the appropriate per-image cache. If there's not an existing cache
|
||||
// for this image, create it.
|
||||
RefPtr<ImageSurfaceCache> cache = GetImageCache(aProvider->GetImageKey());
|
||||
const ImageKey imageKey = aProvider->GetImageKey();
|
||||
RefPtr<ImageSurfaceCache> cache = GetImageCache(imageKey);
|
||||
if (!cache) {
|
||||
cache = new ImageSurfaceCache;
|
||||
cache = new ImageSurfaceCache(imageKey);
|
||||
mImageCaches.Put(aProvider->GetImageKey(), cache);
|
||||
}
|
||||
|
||||
@ -1014,7 +1083,7 @@ public:
|
||||
{
|
||||
RefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
|
||||
if (!cache) {
|
||||
cache = new ImageSurfaceCache;
|
||||
cache = new ImageSurfaceCache(aImageKey);
|
||||
mImageCaches.Put(aImageKey, cache);
|
||||
}
|
||||
|
||||
@ -1630,5 +1699,30 @@ SurfaceCache::MaximumCapacity()
|
||||
return sInstance->MaximumCapacity();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SurfaceCache::IsLegalSize(const IntSize& aSize)
|
||||
{
|
||||
// reject over-wide or over-tall images
|
||||
const int32_t k64KLimit = 0x0000FFFF;
|
||||
if (MOZ_UNLIKELY(aSize.width > k64KLimit || aSize.height > k64KLimit )) {
|
||||
NS_WARNING("image too big");
|
||||
return false;
|
||||
}
|
||||
|
||||
// protect against invalid sizes
|
||||
if (MOZ_UNLIKELY(aSize.height <= 0 || aSize.width <= 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check to make sure we don't overflow a 32-bit
|
||||
CheckedInt32 requiredBytes = CheckedInt32(aSize.width) *
|
||||
CheckedInt32(aSize.height) * 4;
|
||||
if (MOZ_UNLIKELY(!requiredBytes.isValid())) {
|
||||
NS_WARNING("width or height too large");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
|
@ -443,6 +443,11 @@ struct SurfaceCache
|
||||
*/
|
||||
static size_t MaximumCapacity();
|
||||
|
||||
/**
|
||||
* @return true if the given size is valid.
|
||||
*/
|
||||
static bool IsLegalSize(const IntSize& aSize);
|
||||
|
||||
private:
|
||||
virtual ~SurfaceCache() = 0; // Forbid instantiation.
|
||||
};
|
||||
|
@ -807,15 +807,21 @@ VectorImage::GetFrameInternal(const IntSize& aSize,
|
||||
RefPtr<SourceSurface>());
|
||||
}
|
||||
|
||||
RefPtr<SourceSurface> sourceSurface =
|
||||
// We don't allow large surfaces to be rasterized on the Draw and
|
||||
// GetImageContainerAtSize paths, because those have alternatives. If we get
|
||||
// here however, then we know it came from GetFrame(AtSize) and that path does
|
||||
// not have any fallback method, so we don't check UseSurfaceCacheForSize.
|
||||
RefPtr<SourceSurface> sourceSurface;
|
||||
IntSize decodeSize;
|
||||
Tie(sourceSurface, decodeSize) =
|
||||
LookupCachedSurface(aSize, aSVGContext, aFlags);
|
||||
if (sourceSurface) {
|
||||
return MakeTuple(ImgDrawResult::SUCCESS, aSize, std::move(sourceSurface));
|
||||
return MakeTuple(ImgDrawResult::SUCCESS, decodeSize, std::move(sourceSurface));
|
||||
}
|
||||
|
||||
if (mIsDrawing) {
|
||||
NS_WARNING("Refusing to make re-entrant call to VectorImage::Draw");
|
||||
return MakeTuple(ImgDrawResult::TEMPORARY_ERROR, aSize,
|
||||
return MakeTuple(ImgDrawResult::TEMPORARY_ERROR, decodeSize,
|
||||
RefPtr<SourceSurface>());
|
||||
}
|
||||
|
||||
@ -824,7 +830,8 @@ VectorImage::GetFrameInternal(const IntSize& aSize,
|
||||
// flags, having an animation, etc). Otherwise CreateSurface will assume that
|
||||
// the caller is capable of drawing directly to its own draw target if we
|
||||
// cannot cache.
|
||||
SVGDrawingParameters params(nullptr, aSize, ImageRegion::Create(aSize),
|
||||
SVGDrawingParameters params(nullptr, decodeSize, aSize,
|
||||
ImageRegion::Create(decodeSize),
|
||||
SamplingFilter::POINT, aSVGContext,
|
||||
mSVGDocumentWrapper->GetCurrentTime(),
|
||||
aFlags, 1.0);
|
||||
@ -840,12 +847,12 @@ VectorImage::GetFrameInternal(const IntSize& aSize,
|
||||
CreateSurface(params, svgDrawable, didCache);
|
||||
if (!surface) {
|
||||
MOZ_ASSERT(!didCache);
|
||||
return MakeTuple(ImgDrawResult::TEMPORARY_ERROR, aSize,
|
||||
return MakeTuple(ImgDrawResult::TEMPORARY_ERROR, decodeSize,
|
||||
RefPtr<SourceSurface>());
|
||||
}
|
||||
|
||||
SendFrameComplete(didCache, params.flags);
|
||||
return MakeTuple(ImgDrawResult::SUCCESS, aSize, std::move(surface));
|
||||
return MakeTuple(ImgDrawResult::SUCCESS, decodeSize, std::move(surface));
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
@ -904,7 +911,9 @@ VectorImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
|
||||
{
|
||||
// Since we only support image containers with WebRender, and it can handle
|
||||
// textures larger than the hw max texture size, we don't need to check aSize.
|
||||
return !aSize.IsEmpty() && IsImageContainerAvailable(aManager, aFlags);
|
||||
return !aSize.IsEmpty() &&
|
||||
UseSurfaceCacheForSize(aSize) &&
|
||||
IsImageContainerAvailable(aManager, aFlags);
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
@ -915,15 +924,16 @@ VectorImage::GetImageContainerAtSize(layers::LayerManager* aManager,
|
||||
uint32_t aFlags,
|
||||
layers::ImageContainer** aOutContainer)
|
||||
{
|
||||
if (!UseSurfaceCacheForSize(aSize)) {
|
||||
return ImgDrawResult::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
Maybe<SVGImageContext> newSVGContext;
|
||||
MaybeRestrictSVGContext(newSVGContext, aSVGContext, aFlags);
|
||||
|
||||
// Since we do not support high quality scaling with SVG, we mask it off so
|
||||
// that container requests with and without it map to the same container.
|
||||
// Similarly the aspect ratio flag was already handled as part of the SVG
|
||||
// context restriction above.
|
||||
uint32_t flags = aFlags & ~(FLAG_HIGH_QUALITY_SCALING |
|
||||
FLAG_FORCE_PRESERVEASPECTRATIO_NONE);
|
||||
// The aspect ratio flag was already handled as part of the SVG context
|
||||
// restriction above.
|
||||
uint32_t flags = aFlags & ~(FLAG_FORCE_PRESERVEASPECTRATIO_NONE);
|
||||
return GetImageContainerImpl(aManager, aSize,
|
||||
newSVGContext ? newSVGContext : aSVGContext,
|
||||
flags, aOutContainer);
|
||||
@ -1002,10 +1012,13 @@ VectorImage::Draw(gfxContext* aContext,
|
||||
SendOnUnlockedDraw(aFlags);
|
||||
}
|
||||
|
||||
// We should always bypass the cache when using DrawTargetRecording because
|
||||
// we prefer the drawing commands in general to the rasterized surface. This
|
||||
// allows blob images to avoid rasterized SVGs with WebRender.
|
||||
if (aContext->GetDrawTarget()->GetBackendType() == BackendType::RECORDING) {
|
||||
// We should bypass the cache when:
|
||||
// - We are using a DrawTargetRecording because we prefer the drawing commands
|
||||
// in general to the rasterized surface. This allows blob images to avoid
|
||||
// rasterized SVGs with WebRender.
|
||||
// - The size exceeds what we are will to cache as a rasterized surface.
|
||||
if (aContext->GetDrawTarget()->GetBackendType() == BackendType::RECORDING ||
|
||||
!UseSurfaceCacheForSize(aSize)) {
|
||||
aFlags |= FLAG_BYPASS_SURFACE_CACHE;
|
||||
}
|
||||
|
||||
@ -1021,18 +1034,19 @@ VectorImage::Draw(gfxContext* aContext,
|
||||
bool contextPaint =
|
||||
MaybeRestrictSVGContext(newSVGContext, aSVGContext, aFlags);
|
||||
|
||||
SVGDrawingParameters params(aContext, aSize, aRegion, aSamplingFilter,
|
||||
SVGDrawingParameters params(aContext, aSize, aSize, aRegion, aSamplingFilter,
|
||||
newSVGContext ? newSVGContext : aSVGContext,
|
||||
animTime, aFlags, aOpacity);
|
||||
|
||||
// If we have an prerasterized version of this image that matches the
|
||||
// drawing parameters, use that.
|
||||
RefPtr<SourceSurface> sourceSurface =
|
||||
RefPtr<SourceSurface> sourceSurface;
|
||||
Tie(sourceSurface, params.size) =
|
||||
LookupCachedSurface(aSize, params.svgContext, aFlags);
|
||||
if (sourceSurface) {
|
||||
RefPtr<gfxDrawable> svgDrawable =
|
||||
new gfxSurfaceDrawable(sourceSurface, sourceSurface->GetSize());
|
||||
Show(svgDrawable, params);
|
||||
RefPtr<gfxDrawable> drawable =
|
||||
new gfxSurfaceDrawable(sourceSurface, params.size);
|
||||
Show(drawable, params);
|
||||
return ImgDrawResult::SUCCESS;
|
||||
}
|
||||
|
||||
@ -1076,29 +1090,56 @@ VectorImage::CreateSVGDrawable(const SVGDrawingParameters& aParams)
|
||||
return svgDrawable.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<SourceSurface>
|
||||
bool
|
||||
VectorImage::UseSurfaceCacheForSize(const IntSize& aSize) const
|
||||
{
|
||||
int32_t maxSizeKB = gfxPrefs::ImageCacheMaxRasterizedSVGThresholdKB();
|
||||
if (maxSizeKB <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!SurfaceCache::IsLegalSize(aSize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// With factor of 2 mode, we should be willing to use a surface which is up
|
||||
// to twice the width, and twice the height, of the maximum sized surface
|
||||
// before switching to drawing to the target directly. That means the size in
|
||||
// KB works out to be:
|
||||
// width * height * 4 [bytes/pixel] * / 1024 [bytes/KB] <= 2 * 2 * maxSizeKB
|
||||
return aSize.width * aSize.height / 1024 <= maxSizeKB;
|
||||
}
|
||||
|
||||
Tuple<RefPtr<SourceSurface>, IntSize>
|
||||
VectorImage::LookupCachedSurface(const IntSize& aSize,
|
||||
const Maybe<SVGImageContext>& aSVGContext,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
// If we're not allowed to use a cached surface, don't attempt a lookup.
|
||||
if (aFlags & FLAG_BYPASS_SURFACE_CACHE) {
|
||||
return nullptr;
|
||||
return MakeTuple(RefPtr<SourceSurface>(), aSize);
|
||||
}
|
||||
|
||||
// We don't do any caching if we have animation, so don't bother with a lookup
|
||||
// in this case either.
|
||||
if (mHaveAnimations) {
|
||||
return nullptr;
|
||||
return MakeTuple(RefPtr<SourceSurface>(), aSize);
|
||||
}
|
||||
|
||||
LookupResult result =
|
||||
SurfaceCache::Lookup(ImageKey(this),
|
||||
VectorSurfaceKey(aSize, aSVGContext));
|
||||
LookupResult result(MatchType::NOT_FOUND);
|
||||
SurfaceKey surfaceKey = VectorSurfaceKey(aSize, aSVGContext);
|
||||
if ((aFlags & FLAG_SYNC_DECODE) || !(aFlags & FLAG_HIGH_QUALITY_SCALING)) {
|
||||
result = SurfaceCache::Lookup(ImageKey(this), surfaceKey);
|
||||
} else {
|
||||
result = SurfaceCache::LookupBestMatch(ImageKey(this), surfaceKey);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(result.SuggestedSize().IsEmpty(), "SVG should not substitute!");
|
||||
if (!result) {
|
||||
return nullptr; // No matching surface, or the OS freed the volatile buffer.
|
||||
IntSize rasterSize = result.SuggestedSize().IsEmpty()
|
||||
? aSize : result.SuggestedSize();
|
||||
MOZ_ASSERT(result.Type() != MatchType::SUBSTITUTE_BECAUSE_PENDING);
|
||||
if (!result || result.Type() == MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND) {
|
||||
// No matching surface, or the OS freed the volatile buffer.
|
||||
return MakeTuple(RefPtr<SourceSurface>(), rasterSize);
|
||||
}
|
||||
|
||||
RefPtr<SourceSurface> sourceSurface = result.Surface()->GetSourceSurface();
|
||||
@ -1106,10 +1147,10 @@ VectorImage::LookupCachedSurface(const IntSize& aSize,
|
||||
// Something went wrong. (Probably a GPU driver crash or device reset.)
|
||||
// Attempt to recover.
|
||||
RecoverFromLossOfSurfaces();
|
||||
return nullptr;
|
||||
return MakeTuple(RefPtr<SourceSurface>(), rasterSize);
|
||||
}
|
||||
|
||||
return sourceSurface.forget();
|
||||
return MakeTuple(std::move(sourceSurface), rasterSize);
|
||||
}
|
||||
|
||||
already_AddRefed<SourceSurface>
|
||||
@ -1189,7 +1230,15 @@ VectorImage::CreateSurface(const SVGDrawingParameters& aParams,
|
||||
SurfaceKey surfaceKey = VectorSurfaceKey(aParams.size, aParams.svgContext);
|
||||
NotNull<RefPtr<ISurfaceProvider>> provider =
|
||||
MakeNotNull<SimpleSurfaceProvider*>(ImageKey(this), surfaceKey, frame);
|
||||
SurfaceCache::Insert(provider);
|
||||
|
||||
if (SurfaceCache::Insert(provider) == InsertOutcome::SUCCESS &&
|
||||
aParams.size != aParams.drawSize) {
|
||||
// We created a new surface that wasn't the size we requested, which means
|
||||
// we entered factor-of-2 mode. We should purge any surfaces we no longer
|
||||
// need rather than waiting for the cache to expire them.
|
||||
SurfaceCache::PruneImage(ImageKey(this));
|
||||
}
|
||||
|
||||
return surface.forget();
|
||||
}
|
||||
|
||||
@ -1224,10 +1273,21 @@ VectorImage::SendFrameComplete(bool aDidCache, uint32_t aFlags)
|
||||
void
|
||||
VectorImage::Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams)
|
||||
{
|
||||
// The surface size may differ from the size at which we wish to draw. As
|
||||
// such, we may need to adjust the context/region to take this into account.
|
||||
gfxContextMatrixAutoSaveRestore saveMatrix(aParams.context);
|
||||
ImageRegion region(aParams.region);
|
||||
if (aParams.drawSize != aParams.size) {
|
||||
gfx::Size scale(double(aParams.drawSize.width) / aParams.size.width,
|
||||
double(aParams.drawSize.height) / aParams.size.height);
|
||||
aParams.context->Multiply(gfxMatrix::Scaling(scale.width, scale.height));
|
||||
region.Scale(1.0 / scale.width, 1.0 / scale.height);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aDrawable, "Should have a gfxDrawable by now");
|
||||
gfxUtils::DrawPixelSnapped(aParams.context, aDrawable,
|
||||
SizeDouble(aParams.size),
|
||||
aParams.region,
|
||||
region,
|
||||
SurfaceFormat::B8G8R8A8,
|
||||
aParams.samplingFilter,
|
||||
aParams.flags, aParams.opacity, false);
|
||||
|
@ -92,8 +92,12 @@ private:
|
||||
const IntSize& aSize,
|
||||
uint32_t aFlags) override;
|
||||
|
||||
/// Attempt to find a matching cached surface in the SurfaceCache.
|
||||
already_AddRefed<SourceSurface>
|
||||
/**
|
||||
* Attempt to find a matching cached surface in the SurfaceCache. Returns the
|
||||
* cached surface, if found, and the size to rasterize at, if applicable.
|
||||
* If we cannot rasterize, it will be the requested size to draw at (aSize).
|
||||
*/
|
||||
Tuple<RefPtr<SourceSurface>, IntSize>
|
||||
LookupCachedSurface(const IntSize& aSize,
|
||||
const Maybe<SVGImageContext>& aSVGContext,
|
||||
uint32_t aFlags);
|
||||
@ -106,6 +110,9 @@ private:
|
||||
already_AddRefed<gfxDrawable>
|
||||
CreateSVGDrawable(const SVGDrawingParameters& aParams);
|
||||
|
||||
/// Returns true if we use the surface cache to store rasterized copies.
|
||||
bool UseSurfaceCacheForSize(const IntSize& aSize) const;
|
||||
|
||||
/// Rasterize the SVG into a surface. aWillCache will be set to whether or
|
||||
/// not the new surface was put into the cache.
|
||||
already_AddRefed<SourceSurface>
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "imgFrame.h"
|
||||
#include "ImageRegion.h"
|
||||
#include "ShutdownTracker.h"
|
||||
#include "SurfaceCache.h"
|
||||
|
||||
#include "prenv.h"
|
||||
|
||||
@ -157,38 +158,13 @@ ClearSurface(DataSourceSurface* aSurface, const IntSize& aSize, SurfaceFormat aF
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns true if an image of aWidth x aHeight is allowed and legal.
|
||||
static bool
|
||||
AllowedImageSize(int32_t aWidth, int32_t aHeight)
|
||||
{
|
||||
// reject over-wide or over-tall images
|
||||
const int32_t k64KLimit = 0x0000FFFF;
|
||||
if (MOZ_UNLIKELY(aWidth > k64KLimit || aHeight > k64KLimit )) {
|
||||
NS_WARNING("image too big");
|
||||
return false;
|
||||
}
|
||||
|
||||
// protect against invalid sizes
|
||||
if (MOZ_UNLIKELY(aHeight <= 0 || aWidth <= 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check to make sure we don't overflow a 32-bit
|
||||
CheckedInt32 requiredBytes = CheckedInt32(aWidth) * CheckedInt32(aHeight) * 4;
|
||||
if (MOZ_UNLIKELY(!requiredBytes.isValid())) {
|
||||
NS_WARNING("width or height too large");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool AllowedImageAndFrameDimensions(const nsIntSize& aImageSize,
|
||||
const nsIntRect& aFrameRect)
|
||||
{
|
||||
if (!AllowedImageSize(aImageSize.width, aImageSize.height)) {
|
||||
if (!SurfaceCache::IsLegalSize(aImageSize)) {
|
||||
return false;
|
||||
}
|
||||
if (!AllowedImageSize(aFrameRect.Width(), aFrameRect.Height())) {
|
||||
if (!SurfaceCache::IsLegalSize(aFrameRect.Size())) {
|
||||
return false;
|
||||
}
|
||||
nsIntRect imageRect(0, 0, aImageSize.width, aImageSize.height);
|
||||
@ -337,7 +313,7 @@ imgFrame::InitWithDrawable(gfxDrawable* aDrawable,
|
||||
{
|
||||
// Assert for properties that should be verified by decoders,
|
||||
// warn for properties related to bad content.
|
||||
if (!AllowedImageSize(aSize.width, aSize.height)) {
|
||||
if (!SurfaceCache::IsLegalSize(aSize)) {
|
||||
NS_WARNING("Should have legal image size");
|
||||
mAborted = true;
|
||||
return NS_ERROR_FAILURE;
|
||||
|
@ -4620,6 +4620,10 @@ pref("image.animated.resume-from-last-displayed", true);
|
||||
// same data at different sizes.
|
||||
pref("image.cache.factor2.threshold-surfaces", 4);
|
||||
|
||||
// Maximum number of pixels in either dimension that we are willing to upscale
|
||||
// an SVG to when we are in "factor of 2" mode.
|
||||
pref("image.cache.max-rasterized-svg-threshold-kb", 92160);
|
||||
|
||||
// The maximum size, in bytes, of the decoded images we cache
|
||||
pref("image.cache.size", 5242880);
|
||||
|
||||
|
@ -64,7 +64,7 @@ jobs:
|
||||
PERFHERDER_EXTRA_OPTIONS: searchfox
|
||||
run:
|
||||
using: mozharness
|
||||
actions: [get-secrets build update]
|
||||
actions: [get-secrets build]
|
||||
config:
|
||||
- builds/releng_base_firefox.py
|
||||
- builds/releng_base_mac_64_cross_builds.py
|
||||
|
@ -1,4 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
# cctools sometimes needs to be rebuilt when clang is modified.
|
||||
# Until bug 1471905 is addressed, increase the following number
|
||||
# when a forced rebuild of cctools is necessary: 1
|
||||
|
||||
set -x -e -v
|
||||
|
||||
# This script is for building cctools (Apple's binutils) for Mac OS X on
|
||||
|
@ -1,4 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
# cctools sometimes needs to be rebuilt when clang is modified.
|
||||
# Until bug 1471905 is addressed, increase the following number
|
||||
# when a forced rebuild of cctools is necessary: 1
|
||||
|
||||
set -x -e -v
|
||||
|
||||
# This script is for building cctools (Apple's binutils) for Linux using
|
||||
@ -35,7 +40,7 @@ cd $WORKSPACE/build/src
|
||||
cd $CROSSTOOLS_CCTOOLS_DIR
|
||||
export CC=$CLANG_DIR/bin/clang
|
||||
export CXX=$CLANG_DIR/bin/clang++
|
||||
export LDFLAGS=-lpthread
|
||||
export LDFLAGS="-lpthread -Wl,-rpath-link,$CLANG_DIR/lib"
|
||||
./autogen.sh
|
||||
./configure --prefix=$CROSSTOOLS_BUILD_DIR --target=x86_64-apple-darwin11 --with-llvm-config=$CLANG_DIR/bin/llvm-config
|
||||
|
||||
|
@ -397866,6 +397866,12 @@
|
||||
{}
|
||||
]
|
||||
],
|
||||
"shadow-dom/input-type-radio.html": [
|
||||
[
|
||||
"/shadow-dom/input-type-radio.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"shadow-dom/leaktests/get-elements.html": [
|
||||
[
|
||||
"/shadow-dom/leaktests/get-elements.html",
|
||||
@ -646387,6 +646393,10 @@
|
||||
"b571534eb0d6f3f57cfbec3e706648b19848b6d6",
|
||||
"testharness"
|
||||
],
|
||||
"shadow-dom/input-type-radio.html": [
|
||||
"bd5d8e43b0fd9d0c9f1e078ed97a1bbd18b7b0be",
|
||||
"testharness"
|
||||
],
|
||||
"shadow-dom/layout-slot-no-longer-assigned.html": [
|
||||
"dfcac99da023ec2bbd94835f71efaef952a62341",
|
||||
"reftest"
|
||||
|
@ -2,4 +2,4 @@
|
||||
disabled:
|
||||
if os == "linux": https://bugzilla.mozilla.org/show_bug.cgi?id=1477342
|
||||
if os == "mac": https://bugzilla.mozilla.org/show_bug.cgi?id=1477342
|
||||
if (os == "win") and (version == "10.0.15603"): https://bugzilla.mozilla.org/show_bug.cgi?id=1477342
|
||||
if os == "win": https://bugzilla.mozilla.org/show_bug.cgi?id=1477342
|
||||
|
73
testing/web-platform/tests/shadow-dom/input-type-radio.html
Normal file
73
testing/web-platform/tests/shadow-dom/input-type-radio.html
Normal file
@ -0,0 +1,73 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title></title>
|
||||
<script src=/resources/testharness.js></script>
|
||||
<script src=/resources/testharnessreport.js></script>
|
||||
<input type="radio" name="group" id="lightRadio1">
|
||||
<input type="radio" name="group" id="lightRadio2">
|
||||
<div id="host"></div>
|
||||
<script>
|
||||
|
||||
test(() => {
|
||||
var lightRadio1 = document.getElementById("lightRadio1");
|
||||
var lightRadio2 = document.getElementById("lightRadio2");
|
||||
|
||||
var host = document.getElementById("host");
|
||||
var sr = host.attachShadow({mode: "closed"});
|
||||
var shadowRadio1 = document.createElement("input");
|
||||
shadowRadio1.name = "group";
|
||||
shadowRadio1.id = "shadowRadio1";
|
||||
shadowRadio1.type = "radio";
|
||||
sr.appendChild(shadowRadio1);
|
||||
var shadowRadio2 = document.createElement("input");
|
||||
shadowRadio2.name = "group";
|
||||
shadowRadio2.id = "shadowRadio2";
|
||||
shadowRadio2.type = "radio";
|
||||
sr.appendChild(shadowRadio2);
|
||||
|
||||
assert_false(lightRadio1.checked);
|
||||
assert_false(lightRadio2.checked);
|
||||
assert_false(shadowRadio1.checked);
|
||||
assert_false(shadowRadio2.checked);
|
||||
|
||||
lightRadio1.click();
|
||||
assert_true(lightRadio1.checked);
|
||||
assert_false(lightRadio2.checked);
|
||||
assert_false(shadowRadio1.checked);
|
||||
assert_false(shadowRadio2.checked);
|
||||
|
||||
lightRadio2.click();
|
||||
assert_false(lightRadio1.checked);
|
||||
assert_true(lightRadio2.checked);
|
||||
assert_false(shadowRadio1.checked);
|
||||
assert_false(shadowRadio2.checked);
|
||||
|
||||
shadowRadio1.click();
|
||||
assert_false(lightRadio1.checked);
|
||||
assert_true(lightRadio2.checked);
|
||||
assert_true(shadowRadio1.checked);
|
||||
assert_false(shadowRadio2.checked);
|
||||
|
||||
shadowRadio2.click();
|
||||
assert_false(lightRadio1.checked);
|
||||
assert_true(lightRadio2.checked);
|
||||
assert_false(shadowRadio1.checked);
|
||||
assert_true(shadowRadio2.checked);
|
||||
|
||||
// Ensure radio groups work even when modifying shadow DOM.
|
||||
shadowRadio2.remove();
|
||||
sr.appendChild(shadowRadio2);
|
||||
shadowRadio2.click();
|
||||
assert_false(lightRadio1.checked);
|
||||
assert_true(lightRadio2.checked);
|
||||
assert_false(shadowRadio1.checked);
|
||||
assert_true(shadowRadio2.checked);
|
||||
|
||||
shadowRadio1.click();
|
||||
assert_false(lightRadio1.checked);
|
||||
assert_true(lightRadio2.checked);
|
||||
assert_true(shadowRadio1.checked);
|
||||
assert_false(shadowRadio2.checked);
|
||||
}, "input type=radio elements should form a group inside shadow DOM.");
|
||||
|
||||
</script>
|
@ -112,7 +112,6 @@ DOMEventMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
|
||||
|
||||
WriteTime(aWriter, aProcessStartTime, mTimeStamp, "timeStamp");
|
||||
aWriter.StringProperty("eventType", NS_ConvertUTF16toUTF8(mEventType).get());
|
||||
aWriter.IntProperty("phase", mPhase);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -125,13 +125,12 @@ private:
|
||||
class DOMEventMarkerPayload : public TracingMarkerPayload
|
||||
{
|
||||
public:
|
||||
DOMEventMarkerPayload(const nsAString& aEventType, uint16_t aPhase,
|
||||
DOMEventMarkerPayload(const nsAString& aEventType,
|
||||
const mozilla::TimeStamp& aTimeStamp,
|
||||
const char* aCategory, TracingKind aKind)
|
||||
: TracingMarkerPayload(aCategory, aKind)
|
||||
, mTimeStamp(aTimeStamp)
|
||||
, mEventType(aEventType)
|
||||
, mPhase(aPhase)
|
||||
{}
|
||||
|
||||
DECL_STREAM_PAYLOAD
|
||||
@ -139,7 +138,6 @@ public:
|
||||
private:
|
||||
mozilla::TimeStamp mTimeStamp;
|
||||
nsString mEventType;
|
||||
uint16_t mPhase;
|
||||
};
|
||||
|
||||
class UserTimingMarkerPayload : public ProfilerMarkerPayload
|
||||
|
Loading…
x
Reference in New Issue
Block a user