зеркало из https://github.com/mozilla/gecko-dev.git
merge mozilla-inbound to mozilla-central a=merge
--HG-- rename : mobile/android/base/java/org/mozilla/gecko/GeckoAppShell.java => mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java rename : mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java => mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java rename : mobile/android/base/java/org/mozilla/gecko/gfx/LayerRenderer.java => mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerRenderer.java rename : mobile/android/base/java/org/mozilla/gecko/gfx/PanningPerfAPI.java => mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanningPerfAPI.java
This commit is contained in:
Коммит
389a3e0817
|
@ -0,0 +1,26 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
if ! git remote -v | grep origin | grep -q cctools-port; then
|
||||
echo "must be in a cctools-port checkout"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir build-cctools
|
||||
cd build-cctools
|
||||
|
||||
CFLAGS='-mcpu=generic -mtune=generic' MACOSX_DEPLOYMENT_TARGET=10.7 ../cctools/configure --target=x86_64-apple-darwin11
|
||||
env MACOSX_DEPLOYMENT_TARGET=10.7 make -s -j4
|
||||
|
||||
if test ! -e ld64/src/ld/ld; then
|
||||
echo "ld did not get built"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
gtar jcf cctools.tar.bz2 ld64/src/ld/ld --transform 's#ld64/src/ld#cctools/bin#'
|
||||
|
||||
cd ../
|
||||
|
||||
echo "build from $(git show --pretty=format:%H -s HEAD) complete!"
|
||||
echo "upload the build-cctools/cctools.tar.bz2 file to tooltool"
|
|
@ -16,16 +16,18 @@ if [ -d "$topsrcdir/clang" ]; then
|
|||
export CXX=$topsrcdir/clang/bin/clang++
|
||||
export LLVMCONFIG=$topsrcdir/clang/bin/llvm-config
|
||||
export DSYMUTIL=$topsrcdir/clang/bin/llvm-dsymutil
|
||||
# Use an updated linker.
|
||||
ldflags="-B$topsrcdir/cctools/bin"
|
||||
elif [ -d "$topsrcdir/../clang" ]; then
|
||||
# comm-central based build
|
||||
export CC=$topsrcdir/../clang/bin/clang
|
||||
export CXX=$topsrcdir/../clang/bin/clang++
|
||||
export LLVMCONFIG=$topsrcdir/../clang/bin/llvm-config
|
||||
export DSYMUTIL=$topsrcdir/../clang/bin/llvm-dsymutil
|
||||
# Use an updated linker.
|
||||
ldflags="-B$topsrcdir/../cctools/bin"
|
||||
fi
|
||||
|
||||
# Use an updated linker.
|
||||
ldflags="-B$topsrcdir/cctools/bin"
|
||||
# Ensure the updated linker doesn't generate things our older build tools
|
||||
# don't understand.
|
||||
ldflags="$ldflags -Wl,-no_data_in_code_info"
|
||||
|
|
|
@ -72,8 +72,8 @@ def rust_target(rust_compiler, rustc, target, cross_compiling):
|
|||
os_or_kernel = target.kernel if target.kernel == 'Linux' and target.os != 'Android' else target.os
|
||||
rustc_target = {
|
||||
# DragonFly
|
||||
('x86_64', 'Dragonfly'): 'x86_64-unknown-dragonfly',
|
||||
# FreeBSD, GNU/kFreeBSD
|
||||
('x86_64', 'DragonFly'): 'x86_64-unknown-dragonfly',
|
||||
# FreeBSD
|
||||
('x86', 'FreeBSD'): 'i686-unknown-freebsd',
|
||||
('x86_64', 'FreeBSD'): 'x86_64-unknown-freebsd',
|
||||
# NetBSD
|
||||
|
|
|
@ -24,6 +24,9 @@ DEFINES.update(
|
|||
if not CONFIG['HAVE_LANGINFO_CODESET']:
|
||||
DEFINES['U_HAVE_NL_LANGINFO_CODESET'] = 0
|
||||
|
||||
if CONFIG['MOZ_DEBUG']:
|
||||
DEFINES['U_DEBUG'] = 1
|
||||
|
||||
# ICU requires RTTI
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-frtti']
|
||||
|
|
|
@ -17,9 +17,9 @@ function test() {
|
|||
"-H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'",
|
||||
"-H 'Accept-Language: " + navigator.language + "'",
|
||||
"--compressed",
|
||||
"-H 'X-Custom-Header-1: Custom value'",
|
||||
"-H 'X-Custom-Header-2: 8.8.8.8'",
|
||||
"-H 'X-Custom-Header-3: Mon, 3 Mar 2014 11:11:11 GMT'",
|
||||
"-H 'x-custom-header-1: Custom value'",
|
||||
"-H 'x-custom-header-2: 8.8.8.8'",
|
||||
"-H 'x-custom-header-3: Mon, 3 Mar 2014 11:11:11 GMT'",
|
||||
"-H 'Referer: " + CURL_URL + "'",
|
||||
"-H 'Connection: keep-alive'",
|
||||
"-H 'Pragma: no-cache'",
|
||||
|
@ -34,9 +34,9 @@ function test() {
|
|||
'-H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"',
|
||||
'-H "Accept-Language: ' + navigator.language + '"',
|
||||
"--compressed",
|
||||
'-H "X-Custom-Header-1: Custom value"',
|
||||
'-H "X-Custom-Header-2: 8.8.8.8"',
|
||||
'-H "X-Custom-Header-3: Mon, 3 Mar 2014 11:11:11 GMT"',
|
||||
'-H "x-custom-header-1: Custom value"',
|
||||
'-H "x-custom-header-2: 8.8.8.8"',
|
||||
'-H "x-custom-header-3: Mon, 3 Mar 2014 11:11:11 GMT"',
|
||||
'-H "Referer: ' + CURL_URL + '"',
|
||||
'-H "Connection: keep-alive"',
|
||||
'-H "Pragma: no-cache"',
|
||||
|
@ -56,7 +56,36 @@ function test() {
|
|||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||
RequestsMenu.selectedItem = requestItem;
|
||||
|
||||
waitForClipboard(EXPECTED_RESULT, function setup() {
|
||||
waitForClipboard(function validate(aResult) {
|
||||
if (typeof aResult !== "string") {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Different setups may produce the same command, but with the
|
||||
// parameters in a different order in the commandline (which is fine).
|
||||
// Here we confirm that the commands are the same even in that case.
|
||||
var expected = EXPECTED_RESULT.toString().match(/[-A-Za-z1-9]+ ([\"'])(?:\\\1|.)*?\1/g),
|
||||
actual = aResult.match(/[-A-Za-z1-9]+ ([\"'])(?:\\\1|.)*?\1/g);
|
||||
|
||||
// Must begin with the same "curl 'URL'" segment
|
||||
if (!actual || expected[0] != actual[0]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Must match each of the params in the middle (headers)
|
||||
var expectedSet = new Set(expected);
|
||||
var actualSet = new Set(actual);
|
||||
if (expected.size != actual.size) {
|
||||
return false;
|
||||
}
|
||||
for (let param of expectedSet) {
|
||||
if (!actualSet.has(param)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}, function setup() {
|
||||
RequestsMenu.copyAsCurl();
|
||||
}, function onSuccess() {
|
||||
ok(true, "Clipboard contains a cURL command for the currently selected item's url.");
|
||||
|
|
|
@ -154,7 +154,7 @@ function testSentRequest(aData, aOrigData) {
|
|||
is(aData.url, aOrigData.url + "&" + ADD_QUERY, "correct url in sent request");
|
||||
|
||||
let hasHeader = aData.requestHeaders.headers.some((header) => {
|
||||
return (header.name + ": " + header.value) == ADD_HEADER;
|
||||
return (header.name.toLowerCase() + ": " + header.value) == ADD_HEADER.toLowerCase();
|
||||
});
|
||||
ok(hasHeader, "new header added to sent request");
|
||||
|
||||
|
|
|
@ -142,6 +142,9 @@ var CanvasActor = exports.CanvasActor = protocol.ActorClassWithSpec(canvasSpec,
|
|||
*/
|
||||
setup: function ({ reload }) {
|
||||
if (this._initialized) {
|
||||
if (reload) {
|
||||
this.tabActor.window.location.reload();
|
||||
}
|
||||
return;
|
||||
}
|
||||
this._initialized = true;
|
||||
|
|
|
@ -451,6 +451,9 @@ var WebAudioActor = exports.WebAudioActor = protocol.ActorClassWithSpec(webAudio
|
|||
this._nativeToActorID.clear();
|
||||
|
||||
if (this._initialized) {
|
||||
if (reload) {
|
||||
this.tabActor.window.location.reload();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -715,11 +715,7 @@ nsSHEntry::AddChild(nsISHEntry* aChild, int32_t aOffset)
|
|||
}
|
||||
}
|
||||
|
||||
if (!mChildren.ReplaceObjectAt(aChild, aOffset)) {
|
||||
NS_WARNING("Adding a child failed!");
|
||||
aChild->SetParent(nullptr);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mChildren.ReplaceObjectAt(aChild, aOffset);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -737,7 +733,8 @@ nsSHEntry::RemoveChild(nsISHEntry* aChild)
|
|||
} else {
|
||||
int32_t index = mChildren.IndexOfObject(aChild);
|
||||
if (index >= 0) {
|
||||
childRemoved = mChildren.ReplaceObjectAt(nullptr, index);
|
||||
mChildren.ReplaceObjectAt(nullptr, index);
|
||||
childRemoved = true;
|
||||
}
|
||||
}
|
||||
if (childRemoved) {
|
||||
|
@ -780,9 +777,8 @@ nsSHEntry::ReplaceChild(nsISHEntry* aNewEntry)
|
|||
if (mChildren[i] && NS_SUCCEEDED(mChildren[i]->GetDocshellID(&otherID)) &&
|
||||
docshellID == otherID) {
|
||||
mChildren[i]->SetParent(nullptr);
|
||||
if (mChildren.ReplaceObjectAt(aNewEntry, i)) {
|
||||
return aNewEntry->SetParent(this);
|
||||
}
|
||||
mChildren.ReplaceObjectAt(aNewEntry, i);
|
||||
return aNewEntry->SetParent(this);
|
||||
}
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
|
|
|
@ -1258,7 +1258,8 @@ Element::GetAttributeNS(const nsAString& aNamespaceURI,
|
|||
nsAString& aReturn)
|
||||
{
|
||||
int32_t nsid =
|
||||
nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI);
|
||||
nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI,
|
||||
nsContentUtils::IsChromeDoc(OwnerDoc()));
|
||||
|
||||
if (nsid == kNameSpaceID_Unknown) {
|
||||
// Unknown namespace means no attribute.
|
||||
|
@ -1300,7 +1301,8 @@ Element::RemoveAttributeNS(const nsAString& aNamespaceURI,
|
|||
{
|
||||
nsCOMPtr<nsIAtom> name = NS_Atomize(aLocalName);
|
||||
int32_t nsid =
|
||||
nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI);
|
||||
nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI,
|
||||
nsContentUtils::IsChromeDoc(OwnerDoc()));
|
||||
|
||||
if (nsid == kNameSpaceID_Unknown) {
|
||||
// If the namespace ID is unknown, it means there can't possibly be an
|
||||
|
@ -1377,7 +1379,8 @@ Element::HasAttributeNS(const nsAString& aNamespaceURI,
|
|||
const nsAString& aLocalName) const
|
||||
{
|
||||
int32_t nsid =
|
||||
nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI);
|
||||
nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI,
|
||||
nsContentUtils::IsChromeDoc(OwnerDoc()));
|
||||
|
||||
if (nsid == kNameSpaceID_Unknown) {
|
||||
// Unknown namespace means no attr...
|
||||
|
|
|
@ -23,6 +23,7 @@ static const int32_t kNameSpaceID_None = 0;
|
|||
#define kNameSpaceID_RDF 8
|
||||
#define kNameSpaceID_XUL 9
|
||||
#define kNameSpaceID_SVG 10
|
||||
#define kNameSpaceID_LastBuiltin 10 // last 'built-in' namespace
|
||||
#define kNameSpaceID_disabled_MathML 11
|
||||
#define kNameSpaceID_LastBuiltin 11 // last 'built-in' namespace
|
||||
|
||||
#endif // mozilla_dom_NameSpaceConstants_h__
|
||||
|
|
|
@ -197,7 +197,8 @@ bool
|
|||
NodeInfo::NamespaceEquals(const nsAString& aNamespaceURI) const
|
||||
{
|
||||
int32_t nsid =
|
||||
nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI);
|
||||
nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI,
|
||||
nsContentUtils::IsChromeDoc(mOwnerManager->GetDocument()));
|
||||
|
||||
return mozilla::dom::NodeInfo::NamespaceEquals(nsid);
|
||||
}
|
||||
|
|
|
@ -281,6 +281,7 @@ static const DOMIfaceAndProtoJSClass WindowNamedPropertiesClass = {
|
|||
PROXY_CLASS_DEF("WindowProperties",
|
||||
JSCLASS_IS_DOMIFACEANDPROTOJSCLASS),
|
||||
eNamedPropertiesObject,
|
||||
false,
|
||||
prototypes::id::_ID_Count,
|
||||
0,
|
||||
sWindowNamedPropertiesNativePropertyHooks,
|
||||
|
|
|
@ -2892,7 +2892,8 @@ nsContentUtils::SplitQName(const nsIContent* aNamespaceResolver,
|
|||
nameSpace);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*aNamespace = NameSpaceManager()->GetNameSpaceID(nameSpace);
|
||||
*aNamespace = NameSpaceManager()->GetNameSpaceID(nameSpace,
|
||||
nsContentUtils::IsChromeDoc(aNamespaceResolver->OwnerDoc()));
|
||||
if (*aNamespace == kNameSpaceID_Unknown)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
@ -7071,9 +7072,8 @@ nsContentUtils::IsForbiddenSystemRequestHeader(const nsACString& aHeader)
|
|||
static const char *kInvalidHeaders[] = {
|
||||
"accept-charset", "accept-encoding", "access-control-request-headers",
|
||||
"access-control-request-method", "connection", "content-length",
|
||||
"cookie", "cookie2", "content-transfer-encoding", "date", "dnt",
|
||||
"expect", "host", "keep-alive", "origin", "referer", "te", "trailer",
|
||||
"transfer-encoding", "upgrade", "via"
|
||||
"cookie", "cookie2", "date", "dnt", "expect", "host", "keep-alive",
|
||||
"origin", "referer", "te", "trailer", "transfer-encoding", "upgrade", "via"
|
||||
};
|
||||
for (uint32_t i = 0; i < ArrayLength(kInvalidHeaders); ++i) {
|
||||
if (aHeader.LowerCaseEqualsASCII(kInvalidHeaders[i])) {
|
||||
|
|
|
@ -446,7 +446,8 @@ nsDOMAttributeMap::GetAttrNodeInfo(const nsAString& aNamespaceURI,
|
|||
|
||||
if (!aNamespaceURI.IsEmpty()) {
|
||||
nameSpaceID =
|
||||
nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI);
|
||||
nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI,
|
||||
nsContentUtils::IsChromeDoc(mContent->OwnerDoc()));
|
||||
|
||||
if (nameSpaceID == kNameSpaceID_Unknown) {
|
||||
return nullptr;
|
||||
|
|
|
@ -243,6 +243,12 @@ public:
|
|||
*/
|
||||
inline bool IsInHTMLDocument() const;
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if in a chrome document
|
||||
*/
|
||||
virtual bool IsInChromeDocument();
|
||||
|
||||
/**
|
||||
* Get the namespace that this element's tag is defined in
|
||||
* @return the namespace
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "nsIContent.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
inline bool
|
||||
nsIContent::IsInHTMLDocument() const
|
||||
|
@ -16,4 +17,10 @@ nsIContent::IsInHTMLDocument() const
|
|||
return OwnerDoc()->IsHTMLDocument();
|
||||
}
|
||||
|
||||
inline bool
|
||||
nsIContent::IsInChromeDocument()
|
||||
{
|
||||
return nsContentUtils::IsChromeDoc(OwnerDoc());
|
||||
}
|
||||
|
||||
#endif // nsIContentInlines_h
|
||||
|
|
|
@ -15,17 +15,25 @@
|
|||
#include "mozilla/dom/NodeInfo.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsContentCreatorFunctions.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/dom/NodeInfo.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/dom/XBLChildrenElement.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
StaticAutoPtr<nsNameSpaceManager> nsNameSpaceManager::sInstance;
|
||||
static const char* kPrefMathMLDisabled = "mathml.disabled";
|
||||
static const char* kObservedPrefs[] = {
|
||||
kPrefMathMLDisabled,
|
||||
nullptr
|
||||
};
|
||||
StaticRefPtr<nsNameSpaceManager> nsNameSpaceManager::sInstance;
|
||||
|
||||
/* static */ nsNameSpaceManager*
|
||||
nsNameSpaceManager::GetInstance() {
|
||||
|
@ -49,6 +57,14 @@ bool nsNameSpaceManager::Init()
|
|||
rv = AddNameSpace(dont_AddRef(uri), id); \
|
||||
NS_ENSURE_SUCCESS(rv, false)
|
||||
|
||||
#define REGISTER_DISABLED_NAMESPACE(uri, id) \
|
||||
rv = AddDisabledNameSpace(dont_AddRef(uri), id); \
|
||||
NS_ENSURE_SUCCESS(rv, false)
|
||||
|
||||
mozilla::Preferences::AddStrongObservers(this, kObservedPrefs);
|
||||
mMathMLDisabled = mozilla::Preferences::GetBool(kPrefMathMLDisabled);
|
||||
|
||||
|
||||
// Need to be ordered according to ID.
|
||||
REGISTER_NAMESPACE(nsGkAtoms::nsuri_xmlns, kNameSpaceID_XMLNS);
|
||||
REGISTER_NAMESPACE(nsGkAtoms::nsuri_xml, kNameSpaceID_XML);
|
||||
|
@ -60,8 +76,10 @@ bool nsNameSpaceManager::Init()
|
|||
REGISTER_NAMESPACE(nsGkAtoms::nsuri_rdf, kNameSpaceID_RDF);
|
||||
REGISTER_NAMESPACE(nsGkAtoms::nsuri_xul, kNameSpaceID_XUL);
|
||||
REGISTER_NAMESPACE(nsGkAtoms::nsuri_svg, kNameSpaceID_SVG);
|
||||
REGISTER_DISABLED_NAMESPACE(nsGkAtoms::nsuri_mathml, kNameSpaceID_disabled_MathML);
|
||||
|
||||
#undef REGISTER_NAMESPACE
|
||||
#undef REGISTER_DISABLED_NAMESPACE
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -110,24 +128,32 @@ nsNameSpaceManager::GetNameSpaceURI(int32_t aNameSpaceID, nsAString& aURI)
|
|||
}
|
||||
|
||||
int32_t
|
||||
nsNameSpaceManager::GetNameSpaceID(const nsAString& aURI)
|
||||
nsNameSpaceManager::GetNameSpaceID(const nsAString& aURI,
|
||||
bool aInChromeDoc)
|
||||
{
|
||||
if (aURI.IsEmpty()) {
|
||||
return kNameSpaceID_None; // xmlns="", see bug 75700 for details
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAtom> atom = NS_Atomize(aURI);
|
||||
return GetNameSpaceID(atom);
|
||||
return GetNameSpaceID(atom, aInChromeDoc);
|
||||
}
|
||||
|
||||
int32_t
|
||||
nsNameSpaceManager::GetNameSpaceID(nsIAtom* aURI)
|
||||
nsNameSpaceManager::GetNameSpaceID(nsIAtom* aURI,
|
||||
bool aInChromeDoc)
|
||||
{
|
||||
if (aURI == nsGkAtoms::_empty) {
|
||||
return kNameSpaceID_None; // xmlns="", see bug 75700 for details
|
||||
}
|
||||
|
||||
int32_t nameSpaceID;
|
||||
if (mMathMLDisabled &&
|
||||
mDisabledURIToIDTable.Get(aURI, &nameSpaceID) &&
|
||||
!aInChromeDoc) {
|
||||
NS_POSTCONDITION(nameSpaceID >= 0, "Bogus namespace ID");
|
||||
return nameSpaceID;
|
||||
}
|
||||
if (mURIToIDTable.Get(aURI, &nameSpaceID)) {
|
||||
NS_POSTCONDITION(nameSpaceID >= 0, "Bogus namespace ID");
|
||||
return nameSpaceID;
|
||||
|
@ -153,7 +179,19 @@ NS_NewElement(Element** aResult,
|
|||
}
|
||||
#endif
|
||||
if (ns == kNameSpaceID_MathML) {
|
||||
return NS_NewMathMLElement(aResult, ni.forget());
|
||||
// If the mathml.disabled pref. is true, convert all MathML nodes into
|
||||
// disabled MathML nodes by swapping the namespace.
|
||||
nsNameSpaceManager* nsmgr = nsNameSpaceManager::GetInstance();
|
||||
if ((nsmgr && !nsmgr->mMathMLDisabled) ||
|
||||
nsContentUtils::IsChromeDoc(ni->GetDocument())) {
|
||||
return NS_NewMathMLElement(aResult, ni.forget());
|
||||
}
|
||||
|
||||
RefPtr<mozilla::dom::NodeInfo> genericXMLNI =
|
||||
ni->NodeInfoManager()->
|
||||
GetNodeInfo(ni->NameAtom(), ni->GetPrefixAtom(),
|
||||
kNameSpaceID_disabled_MathML, ni->NodeType(), ni->GetExtraName());
|
||||
return NS_NewXMLElement(aResult, genericXMLNI.forget());
|
||||
}
|
||||
if (ns == kNameSpaceID_SVG) {
|
||||
return NS_NewSVGElement(aResult, ni.forget(), aFromParser);
|
||||
|
@ -195,3 +233,35 @@ nsresult nsNameSpaceManager::AddNameSpace(already_AddRefed<nsIAtom> aURI,
|
|||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsNameSpaceManager::AddDisabledNameSpace(already_AddRefed<nsIAtom> aURI,
|
||||
const int32_t aNameSpaceID)
|
||||
{
|
||||
nsCOMPtr<nsIAtom> uri = aURI;
|
||||
if (aNameSpaceID < 0) {
|
||||
// We've wrapped... Can't do anything else here; just bail.
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
NS_ASSERTION(aNameSpaceID - 1 == (int32_t) mURIArray.Length(),
|
||||
"BAD! AddDisabledNameSpace not called in right order!");
|
||||
|
||||
mURIArray.AppendElement(uri.forget());
|
||||
mDisabledURIToIDTable.Put(mURIArray.LastElement(), aNameSpaceID);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsISupports
|
||||
NS_IMPL_ISUPPORTS(nsNameSpaceManager,
|
||||
nsIObserver)
|
||||
|
||||
// nsIObserver
|
||||
NS_IMETHODIMP
|
||||
nsNameSpaceManager::Observe(nsISupports* aObject, const char* aTopic,
|
||||
const char16_t* aMessage)
|
||||
{
|
||||
mMathMLDisabled = mozilla::Preferences::GetBool(kPrefMathMLDisabled);
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include "nsDataHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsIAtom.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
#include "mozilla/StaticPtr.h"
|
||||
|
@ -30,34 +32,42 @@ class nsAString;
|
|||
*
|
||||
*/
|
||||
|
||||
class nsNameSpaceManager final
|
||||
class nsNameSpaceManager final : public nsIObserver
|
||||
{
|
||||
public:
|
||||
~nsNameSpaceManager() {}
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
virtual nsresult RegisterNameSpace(const nsAString& aURI,
|
||||
int32_t& aNameSpaceID);
|
||||
|
||||
nsresult RegisterNameSpace(const nsAString& aURI, int32_t& aNameSpaceID);
|
||||
|
||||
nsresult GetNameSpaceURI(int32_t aNameSpaceID, nsAString& aURI);
|
||||
virtual nsresult GetNameSpaceURI(int32_t aNameSpaceID, nsAString& aURI);
|
||||
|
||||
nsIAtom* NameSpaceURIAtom(int32_t aNameSpaceID) {
|
||||
MOZ_ASSERT(aNameSpaceID > 0 && (int64_t) aNameSpaceID <= (int64_t) mURIArray.Length());
|
||||
return mURIArray.ElementAt(aNameSpaceID - 1); // id is index + 1
|
||||
}
|
||||
|
||||
int32_t GetNameSpaceID(const nsAString& aURI);
|
||||
int32_t GetNameSpaceID(nsIAtom* aURI);
|
||||
int32_t GetNameSpaceID(const nsAString& aURI,
|
||||
bool aInChromeDoc);
|
||||
int32_t GetNameSpaceID(nsIAtom* aURI,
|
||||
bool aInChromeDoc);
|
||||
|
||||
bool HasElementCreator(int32_t aNameSpaceID);
|
||||
|
||||
static nsNameSpaceManager* GetInstance();
|
||||
bool mMathMLDisabled;
|
||||
|
||||
private:
|
||||
bool Init();
|
||||
nsresult AddNameSpace(already_AddRefed<nsIAtom> aURI, const int32_t aNameSpaceID);
|
||||
nsresult AddDisabledNameSpace(already_AddRefed<nsIAtom> aURI, const int32_t aNameSpaceID);
|
||||
~nsNameSpaceManager() {};
|
||||
|
||||
nsDataHashtable<nsISupportsHashKey, int32_t> mURIToIDTable;
|
||||
nsDataHashtable<nsISupportsHashKey, int32_t> mDisabledURIToIDTable;
|
||||
nsTArray<nsCOMPtr<nsIAtom>> mURIArray;
|
||||
|
||||
static mozilla::StaticAutoPtr<nsNameSpaceManager> sInstance;
|
||||
static mozilla::StaticRefPtr<nsNameSpaceManager> sInstance;
|
||||
};
|
||||
|
||||
#endif // nsNameSpaceManager_h___
|
||||
|
|
|
@ -317,7 +317,7 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
|
|||
Element* oldScopeElement = nullptr;
|
||||
if (mStyleSheet) {
|
||||
if (mStyleSheet->IsServo()) {
|
||||
NS_ERROR("stylo: ServoStyleSheets don't support <style scoped>");
|
||||
NS_WARNING("stylo: ServoStyleSheets don't support <style scoped>");
|
||||
} else {
|
||||
oldScopeElement = mStyleSheet->AsGecko()->GetScopeElement();
|
||||
}
|
||||
|
|
|
@ -1083,14 +1083,15 @@ nsTreeSanitizer::SanitizeStyleSheet(const nsAString& aOriginal,
|
|||
// -moz-binding is blacklisted.
|
||||
bool didSanitize = false;
|
||||
// Create a sheet to hold the parsed CSS
|
||||
RefPtr<CSSStyleSheet> sheet = new CSSStyleSheet(CORS_NONE, aDocument->GetReferrerPolicy());
|
||||
RefPtr<CSSStyleSheet> sheet =
|
||||
new CSSStyleSheet(mozilla::css::eAuthorSheetFeatures,
|
||||
CORS_NONE, aDocument->GetReferrerPolicy());
|
||||
sheet->SetURIs(aDocument->GetDocumentURI(), nullptr, aBaseURI);
|
||||
sheet->SetPrincipal(aDocument->NodePrincipal());
|
||||
// Create the CSS parser, and parse the CSS text.
|
||||
nsCSSParser parser(nullptr, sheet);
|
||||
rv = parser.ParseSheet(aOriginal, aDocument->GetDocumentURI(), aBaseURI,
|
||||
aDocument->NodePrincipal(), 0,
|
||||
mozilla::css::eAuthorSheetFeatures);
|
||||
aDocument->NodePrincipal(), 0);
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
// Mark the sheet as complete.
|
||||
MOZ_ASSERT(!sheet->IsModified(),
|
||||
|
|
|
@ -712,7 +712,6 @@ static JSObject*
|
|||
CreateInterfaceObject(JSContext* cx, JS::Handle<JSObject*> global,
|
||||
JS::Handle<JSObject*> constructorProto,
|
||||
const js::Class* constructorClass,
|
||||
const JSNativeHolder* constructorNative,
|
||||
unsigned ctorNargs, const NamedConstructor* namedConstructors,
|
||||
JS::Handle<JSObject*> proto,
|
||||
const NativeProperties* properties,
|
||||
|
@ -720,35 +719,38 @@ CreateInterfaceObject(JSContext* cx, JS::Handle<JSObject*> global,
|
|||
const char* name, bool defineOnGlobal)
|
||||
{
|
||||
JS::Rooted<JSObject*> constructor(cx);
|
||||
if (constructorClass) {
|
||||
MOZ_ASSERT(constructorProto);
|
||||
constructor = JS_NewObjectWithGivenProto(cx, Jsvalify(constructorClass),
|
||||
constructorProto);
|
||||
} else {
|
||||
MOZ_ASSERT(constructorNative);
|
||||
MOZ_ASSERT(constructorProto == JS_GetFunctionPrototype(cx, global));
|
||||
constructor = CreateConstructor(cx, global, name, constructorNative,
|
||||
ctorNargs);
|
||||
}
|
||||
MOZ_ASSERT(constructorProto);
|
||||
MOZ_ASSERT(constructorClass);
|
||||
constructor = JS_NewObjectWithGivenProto(cx, Jsvalify(constructorClass),
|
||||
constructorProto);
|
||||
if (!constructor) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (constructorClass) {
|
||||
if (!JS_DefineProperty(cx, constructor, "length", ctorNargs,
|
||||
JSPROP_READONLY)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!JS_DefineProperty(cx, constructor, "length", ctorNargs,
|
||||
JSPROP_READONLY)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Might as well intern, since we're going to need an atomized
|
||||
// version of name anyway when we stick our constructor on the
|
||||
// global.
|
||||
JS::Rooted<JSString*> nameStr(cx, JS_AtomizeAndPinString(cx, name));
|
||||
if (!nameStr) {
|
||||
return nullptr;
|
||||
}
|
||||
// Might as well intern, since we're going to need an atomized
|
||||
// version of name anyway when we stick our constructor on the
|
||||
// global.
|
||||
JS::Rooted<JSString*> nameStr(cx, JS_AtomizeAndPinString(cx, name));
|
||||
if (!nameStr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!JS_DefineProperty(cx, constructor, "name", nameStr, JSPROP_READONLY)) {
|
||||
if (!JS_DefineProperty(cx, constructor, "name", nameStr, JSPROP_READONLY)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (DOMIfaceAndProtoJSClass::FromJSClass(constructorClass)->wantsInterfaceHasInstance) {
|
||||
JS::Rooted<jsid> hasInstanceId(cx,
|
||||
SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::hasInstance)));
|
||||
if (!JS_DefineFunctionById(cx, constructor, hasInstanceId,
|
||||
InterfaceHasInstance, 1,
|
||||
// Flags match those of Function[Symbol.hasInstance]
|
||||
JSPROP_READONLY | JSPROP_PERMANENT)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -829,12 +831,16 @@ CreateInterfacePrototypeObject(JSContext* cx, JS::Handle<JSObject*> global,
|
|||
const js::Class* protoClass,
|
||||
const NativeProperties* properties,
|
||||
const NativeProperties* chromeOnlyProperties,
|
||||
const char* const* unscopableNames)
|
||||
const char* const* unscopableNames,
|
||||
bool isGlobal)
|
||||
{
|
||||
JS::Rooted<JSObject*> ourProto(cx,
|
||||
JS_NewObjectWithUniqueType(cx, Jsvalify(protoClass), parentProto));
|
||||
if (!ourProto ||
|
||||
!DefineProperties(cx, ourProto, properties, chromeOnlyProperties)) {
|
||||
// We don't try to define properties on the global's prototype; those
|
||||
// properties go on the global itself.
|
||||
(!isGlobal &&
|
||||
!DefineProperties(cx, ourProto, properties, chromeOnlyProperties))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -910,16 +916,17 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
|
|||
JS::Handle<JSObject*> protoProto,
|
||||
const js::Class* protoClass, JS::Heap<JSObject*>* protoCache,
|
||||
JS::Handle<JSObject*> constructorProto,
|
||||
const js::Class* constructorClass, const JSNativeHolder* constructor,
|
||||
const js::Class* constructorClass,
|
||||
unsigned ctorNargs, const NamedConstructor* namedConstructors,
|
||||
JS::Heap<JSObject*>* constructorCache,
|
||||
const NativeProperties* properties,
|
||||
const NativeProperties* chromeOnlyProperties,
|
||||
const char* name, bool defineOnGlobal,
|
||||
const char* const* unscopableNames)
|
||||
const char* const* unscopableNames,
|
||||
bool isGlobal)
|
||||
{
|
||||
MOZ_ASSERT(protoClass || constructorClass || constructor,
|
||||
"Need at least one class or a constructor!");
|
||||
MOZ_ASSERT(protoClass || constructorClass,
|
||||
"Need at least one class!");
|
||||
MOZ_ASSERT(!((properties &&
|
||||
(properties->HasMethods() || properties->HasAttributes())) ||
|
||||
(chromeOnlyProperties &&
|
||||
|
@ -932,18 +939,17 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
|
|||
(chromeOnlyProperties &&
|
||||
(chromeOnlyProperties->HasStaticMethods() ||
|
||||
chromeOnlyProperties->HasStaticAttributes()))) ||
|
||||
constructorClass || constructor,
|
||||
"Static methods but no constructorClass or constructor!");
|
||||
MOZ_ASSERT(bool(name) == bool(constructorClass || constructor),
|
||||
constructorClass,
|
||||
"Static methods but no constructorClass!");
|
||||
MOZ_ASSERT(bool(name) == bool(constructorClass),
|
||||
"Must have name precisely when we have an interface object");
|
||||
MOZ_ASSERT(!constructorClass || !constructor);
|
||||
MOZ_ASSERT(!protoClass == !protoCache,
|
||||
"If, and only if, there is an interface prototype object we need "
|
||||
"to cache it");
|
||||
MOZ_ASSERT(!(constructorClass || constructor) == !constructorCache,
|
||||
MOZ_ASSERT(bool(constructorClass) == bool(constructorCache),
|
||||
"If, and only if, there is an interface object we need to cache "
|
||||
"it");
|
||||
MOZ_ASSERT(constructorProto || (!constructorClass && !constructor),
|
||||
MOZ_ASSERT(constructorProto || !constructorClass,
|
||||
"Must have a constructor proto if we plan to create a constructor "
|
||||
"object");
|
||||
|
||||
|
@ -952,7 +958,7 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
|
|||
proto =
|
||||
CreateInterfacePrototypeObject(cx, global, protoProto, protoClass,
|
||||
properties, chromeOnlyProperties,
|
||||
unscopableNames);
|
||||
unscopableNames, isGlobal);
|
||||
if (!proto) {
|
||||
return;
|
||||
}
|
||||
|
@ -964,11 +970,11 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
|
|||
}
|
||||
|
||||
JSObject* interface;
|
||||
if (constructorClass || constructor) {
|
||||
if (constructorClass) {
|
||||
interface = CreateInterfaceObject(cx, global, constructorProto,
|
||||
constructorClass, constructor,
|
||||
ctorNargs, namedConstructors, proto,
|
||||
properties, chromeOnlyProperties, name,
|
||||
constructorClass, ctorNargs,
|
||||
namedConstructors, proto, properties,
|
||||
chromeOnlyProperties, name,
|
||||
defineOnGlobal);
|
||||
if (!interface) {
|
||||
if (protoCache) {
|
||||
|
@ -1237,7 +1243,15 @@ static JSObject*
|
|||
XrayCreateFunction(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JSNativeWrapper native, unsigned nargs, JS::Handle<jsid> id)
|
||||
{
|
||||
JSFunction* fun = js::NewFunctionByIdWithReserved(cx, native.op, nargs, 0, id);
|
||||
JSFunction* fun;
|
||||
if (JSID_IS_STRING(id)) {
|
||||
fun = js::NewFunctionByIdWithReserved(cx, native.op, nargs, 0, id);
|
||||
} else {
|
||||
// Can't pass this id (probably a symbol) to NewFunctionByIdWithReserved;
|
||||
// just use an empty name for lack of anything better.
|
||||
fun = js::NewFunctionWithReserved(cx, native.op, nargs, 0, nullptr);
|
||||
}
|
||||
|
||||
if (!fun) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1656,6 +1670,26 @@ XrayResolveOwnProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
|||
JSPROP_PERMANENT | JSPROP_READONLY,
|
||||
desc, cacheOnHolder);
|
||||
}
|
||||
|
||||
if (id == SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::hasInstance)) &&
|
||||
DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj))->
|
||||
wantsInterfaceHasInstance) {
|
||||
cacheOnHolder = true;
|
||||
JSNativeWrapper interfaceHasInstanceWrapper = { InterfaceHasInstance,
|
||||
nullptr };
|
||||
JSObject* funObj = XrayCreateFunction(cx, wrapper,
|
||||
interfaceHasInstanceWrapper, 1, id);
|
||||
if (!funObj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
desc.value().setObject(*funObj);
|
||||
desc.setAttributes(JSPROP_READONLY | JSPROP_PERMANENT);
|
||||
desc.object().set(wrapper);
|
||||
desc.setSetter(nullptr);
|
||||
desc.setGetter(nullptr);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(IsInterfacePrototype(type));
|
||||
|
||||
|
@ -1880,7 +1914,7 @@ const js::ClassOps sBoringInterfaceObjectClassClassOps = {
|
|||
nullptr, /* mayResolve */
|
||||
nullptr, /* finalize */
|
||||
ThrowingConstructor, /* call */
|
||||
InterfaceHasInstance, /* hasInstance */
|
||||
nullptr, /* hasInstance */
|
||||
ThrowingConstructor, /* construct */
|
||||
nullptr, /* trace */
|
||||
};
|
||||
|
@ -2220,24 +2254,69 @@ GlobalObject::GetAsSupports() const
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
InterfaceHasInstance(JSContext* cx, JS::Handle<JSObject*> obj,
|
||||
JS::Handle<JSObject*> instance,
|
||||
bool* bp)
|
||||
static bool
|
||||
CallOrdinaryHasInstance(JSContext* cx, JS::CallArgs& args)
|
||||
{
|
||||
const DOMIfaceAndProtoJSClass* clasp =
|
||||
DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj));
|
||||
JS::Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
|
||||
bool isInstance;
|
||||
if (!JS::OrdinaryHasInstance(cx, thisObj, args.get(0), &isInstance)) {
|
||||
return false;
|
||||
}
|
||||
args.rval().setBoolean(isInstance);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
InterfaceHasInstance(JSContext* cx, unsigned argc, JS::Value* vp)
|
||||
{
|
||||
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
||||
// If the thing we were passed is not an object, return false like
|
||||
// OrdinaryHasInstance does.
|
||||
if (!args.get(0).isObject()) {
|
||||
args.rval().setBoolean(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If "this" is not an object, likewise return false (again, like
|
||||
// OrdinaryHasInstance).
|
||||
if (!args.thisv().isObject()) {
|
||||
args.rval().setBoolean(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If "this" doesn't have a DOMIfaceAndProtoJSClass, it's not a DOM
|
||||
// constructor, so just fall back to OrdinaryHasInstance. But note that we
|
||||
// should CheckedUnwrap here, because otherwise we won't get the right
|
||||
// answers.
|
||||
JS::Rooted<JSObject*> thisObj(cx, js::CheckedUnwrap(&args.thisv().toObject()));
|
||||
if (!thisObj) {
|
||||
// Just fall back on the normal thing, in case it still happens to work.
|
||||
return CallOrdinaryHasInstance(cx, args);
|
||||
}
|
||||
|
||||
const js::Class* thisClass = js::GetObjectClass(thisObj);
|
||||
|
||||
if (!IsDOMIfaceAndProtoClass(thisClass)) {
|
||||
return CallOrdinaryHasInstance(cx, args);
|
||||
}
|
||||
|
||||
const DOMIfaceAndProtoJSClass* clasp =
|
||||
DOMIfaceAndProtoJSClass::FromJSClass(thisClass);
|
||||
|
||||
// If "this" isn't a DOM constructor or is a constructor for an interface
|
||||
// without a prototype, just fall back to OrdinaryHasInstance.
|
||||
if (clasp->mType != eInterface ||
|
||||
clasp->mPrototypeID == prototypes::id::_ID_Count) {
|
||||
return CallOrdinaryHasInstance(cx, args);
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> instance(cx, &args[0].toObject());
|
||||
const DOMJSClass* domClass =
|
||||
GetDOMClass(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false));
|
||||
|
||||
MOZ_ASSERT(!domClass || clasp->mPrototypeID != prototypes::id::_ID_Count,
|
||||
"Why do we have a hasInstance hook if we don't have a prototype "
|
||||
"ID?");
|
||||
|
||||
if (domClass &&
|
||||
domClass->mInterfaceChain[clasp->mDepth] == clasp->mPrototypeID) {
|
||||
*bp = true;
|
||||
args.rval().setBoolean(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2247,49 +2326,11 @@ InterfaceHasInstance(JSContext* cx, JS::Handle<JSObject*> obj,
|
|||
clasp->mDepth, &boolp)) {
|
||||
return false;
|
||||
}
|
||||
*bp = boolp;
|
||||
args.rval().setBoolean(boolp);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> protov(cx);
|
||||
DebugOnly<bool> ok = JS_GetProperty(cx, obj, "prototype", &protov);
|
||||
MOZ_ASSERT(ok, "Someone messed with our prototype property?");
|
||||
|
||||
JS::Rooted<JSObject*> interfacePrototype(cx, &protov.toObject());
|
||||
MOZ_ASSERT(IsDOMIfaceAndProtoClass(js::GetObjectClass(interfacePrototype)),
|
||||
"Someone messed with our prototype property?");
|
||||
|
||||
JS::Rooted<JSObject*> proto(cx);
|
||||
if (!JS_GetPrototype(cx, instance, &proto)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (proto) {
|
||||
if (proto == interfacePrototype) {
|
||||
*bp = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!JS_GetPrototype(cx, proto, &proto)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
*bp = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
InterfaceHasInstance(JSContext* cx, JS::Handle<JSObject*> obj, JS::MutableHandle<JS::Value> vp,
|
||||
bool* bp)
|
||||
{
|
||||
if (!vp.isObject()) {
|
||||
*bp = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> instanceObject(cx, &vp.toObject());
|
||||
return InterfaceHasInstance(cx, obj, instanceObject, bp);
|
||||
return CallOrdinaryHasInstance(cx, args);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -599,6 +599,9 @@ struct NamedConstructor
|
|||
* underlying global.
|
||||
* unscopableNames if not null it points to a null-terminated list of const
|
||||
* char* names of the unscopable properties for this interface.
|
||||
* isGlobal if true, we're creating interface objects for a [Global] or
|
||||
* [PrimaryGlobal] interface, and hence shouldn't define properties on
|
||||
* the prototype object.
|
||||
*
|
||||
* At least one of protoClass, constructorClass or constructor should be
|
||||
* non-null. If constructorClass or constructor are non-null, the resulting
|
||||
|
@ -610,13 +613,14 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
|
|||
JS::Handle<JSObject*> protoProto,
|
||||
const js::Class* protoClass, JS::Heap<JSObject*>* protoCache,
|
||||
JS::Handle<JSObject*> interfaceProto,
|
||||
const js::Class* constructorClass, const JSNativeHolder* constructor,
|
||||
const js::Class* constructorClass,
|
||||
unsigned ctorNargs, const NamedConstructor* namedConstructors,
|
||||
JS::Heap<JSObject*>* constructorCache,
|
||||
const NativeProperties* regularProperties,
|
||||
const NativeProperties* chromeOnlyProperties,
|
||||
const char* name, bool defineOnGlobal,
|
||||
const char* const* unscopableNames);
|
||||
const char* const* unscopableNames,
|
||||
bool isGlobal);
|
||||
|
||||
/**
|
||||
* Define the properties (regular and chrome-only) on obj.
|
||||
|
@ -2673,17 +2677,11 @@ nsresult
|
|||
ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObj);
|
||||
|
||||
/**
|
||||
* Used to implement the hasInstance hook of an interface object.
|
||||
*
|
||||
* instance should not be a security wrapper.
|
||||
* Used to implement the Symbol.hasInstance property of an interface object.
|
||||
*/
|
||||
bool
|
||||
InterfaceHasInstance(JSContext* cx, JS::Handle<JSObject*> obj,
|
||||
JS::Handle<JSObject*> instance,
|
||||
bool* bp);
|
||||
bool
|
||||
InterfaceHasInstance(JSContext* cx, JS::Handle<JSObject*> obj, JS::MutableHandle<JS::Value> vp,
|
||||
bool* bp);
|
||||
InterfaceHasInstance(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
|
||||
bool
|
||||
InterfaceHasInstance(JSContext* cx, int prototypeID, int depth,
|
||||
JS::Handle<JSObject*> instance,
|
||||
|
|
|
@ -602,6 +602,7 @@ class CGPrototypeJSClass(CGThing):
|
|||
JS_NULL_OBJECT_OPS
|
||||
},
|
||||
${type},
|
||||
false,
|
||||
${prototypeID},
|
||||
${depth},
|
||||
${hooks},
|
||||
|
@ -673,12 +674,10 @@ class CGInterfaceObjectJSClass(CGThing):
|
|||
ctorname = "nullptr"
|
||||
else:
|
||||
ctorname = "ThrowingConstructor"
|
||||
if NeedsGeneratedHasInstance(self.descriptor):
|
||||
hasinstance = HASINSTANCE_HOOK_NAME
|
||||
elif self.descriptor.interface.hasInterfacePrototypeObject():
|
||||
hasinstance = "InterfaceHasInstance"
|
||||
else:
|
||||
hasinstance = "nullptr"
|
||||
needsHasInstance = (
|
||||
not NeedsGeneratedHasInstance(self.descriptor) and
|
||||
self.descriptor.interface.hasInterfacePrototypeObject())
|
||||
|
||||
prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
|
||||
slotCount = "DOM_INTERFACE_SLOTS_BASE"
|
||||
if len(self.descriptor.interface.namedConstructors) > 0:
|
||||
|
@ -687,10 +686,10 @@ class CGInterfaceObjectJSClass(CGThing):
|
|||
(protoGetter, _) = InterfaceObjectProtoGetter(self.descriptor,
|
||||
forXrays=True)
|
||||
|
||||
if ctorname == "ThrowingConstructor" and hasinstance == "InterfaceHasInstance":
|
||||
if ctorname == "ThrowingConstructor":
|
||||
ret = ""
|
||||
classOpsPtr = "&sBoringInterfaceObjectClassClassOps"
|
||||
elif ctorname == "nullptr" and hasinstance == "nullptr":
|
||||
elif ctorname == "nullptr":
|
||||
ret = ""
|
||||
classOpsPtr = "JS_NULL_CLASS_OPS"
|
||||
else:
|
||||
|
@ -706,14 +705,13 @@ class CGInterfaceObjectJSClass(CGThing):
|
|||
nullptr, /* mayResolve */
|
||||
nullptr, /* finalize */
|
||||
${ctorname}, /* call */
|
||||
${hasInstance}, /* hasInstance */
|
||||
nullptr, /* hasInstance */
|
||||
${ctorname}, /* construct */
|
||||
nullptr, /* trace */
|
||||
};
|
||||
|
||||
""",
|
||||
ctorname=ctorname,
|
||||
hasInstance=hasinstance)
|
||||
ctorname=ctorname)
|
||||
classOpsPtr = "&sInterfaceObjectClassOps"
|
||||
|
||||
if self.descriptor.interface.isNamespace():
|
||||
|
@ -744,6 +742,7 @@ class CGInterfaceObjectJSClass(CGThing):
|
|||
${objectOps}
|
||||
},
|
||||
eInterface,
|
||||
${needsHasInstance},
|
||||
${prototypeID},
|
||||
${depth},
|
||||
${hooks},
|
||||
|
@ -753,11 +752,10 @@ class CGInterfaceObjectJSClass(CGThing):
|
|||
""",
|
||||
classString=classString,
|
||||
slotCount=slotCount,
|
||||
ctorname=ctorname,
|
||||
hasInstance=hasinstance,
|
||||
classOpsPtr=classOpsPtr,
|
||||
hooks=NativePropertyHooks(self.descriptor),
|
||||
objectOps=objectOps,
|
||||
needsHasInstance=toStringBool(needsHasInstance),
|
||||
prototypeID=prototypeID,
|
||||
depth=depth,
|
||||
toStringResult=toStringResult,
|
||||
|
@ -1716,24 +1714,6 @@ class CGConstructNavigatorObject(CGAbstractMethod):
|
|||
""") + genConstructorBody(self.descriptor)
|
||||
|
||||
|
||||
class CGClassConstructHookHolder(CGGeneric):
|
||||
def __init__(self, descriptor):
|
||||
if descriptor.interface.ctor():
|
||||
constructHook = CONSTRUCT_HOOK_NAME
|
||||
else:
|
||||
constructHook = "ThrowingConstructor"
|
||||
CGGeneric.__init__(self, fill(
|
||||
"""
|
||||
static const JSNativeHolder ${CONSTRUCT_HOOK_NAME}_holder = {
|
||||
${constructHook},
|
||||
${hooks}
|
||||
};
|
||||
""",
|
||||
CONSTRUCT_HOOK_NAME=CONSTRUCT_HOOK_NAME,
|
||||
constructHook=constructHook,
|
||||
hooks=NativePropertyHooks(descriptor)))
|
||||
|
||||
|
||||
def NamedConstructorName(m):
|
||||
return '_' + m.identifier.name
|
||||
|
||||
|
@ -1783,19 +1763,17 @@ class CGNamedConstructors(CGThing):
|
|||
namedConstructors=namedConstructors)
|
||||
|
||||
|
||||
class CGClassHasInstanceHook(CGAbstractStaticMethod):
|
||||
class CGHasInstanceHook(CGAbstractStaticMethod):
|
||||
def __init__(self, descriptor):
|
||||
args = [Argument('JSContext*', 'cx'),
|
||||
Argument('JS::Handle<JSObject*>', 'obj'),
|
||||
Argument('JS::MutableHandle<JS::Value>', 'vp'),
|
||||
Argument('bool*', 'bp')]
|
||||
Argument('unsigned', 'argc'),
|
||||
Argument('JS::Value*', 'vp')]
|
||||
assert descriptor.interface.hasInterfaceObject()
|
||||
assert NeedsGeneratedHasInstance(descriptor)
|
||||
CGAbstractStaticMethod.__init__(self, descriptor, HASINSTANCE_HOOK_NAME,
|
||||
'bool', args)
|
||||
|
||||
def define(self):
|
||||
if not NeedsGeneratedHasInstance(self.descriptor):
|
||||
return ""
|
||||
return CGAbstractStaticMethod.define(self)
|
||||
|
||||
def definition_body(self):
|
||||
|
@ -1803,12 +1781,13 @@ class CGClassHasInstanceHook(CGAbstractStaticMethod):
|
|||
|
||||
def generate_code(self):
|
||||
header = dedent("""
|
||||
if (!vp.isObject()) {
|
||||
*bp = false;
|
||||
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
||||
if (!args.get(0).isObject()) {
|
||||
args.rval().setBoolean(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> instance(cx, &vp.toObject());
|
||||
JS::Rooted<JSObject*> instance(cx, &args[0].toObject());
|
||||
""")
|
||||
if self.descriptor.interface.hasInterfacePrototypeObject():
|
||||
return (
|
||||
|
@ -1819,8 +1798,8 @@ class CGClassHasInstanceHook(CGAbstractStaticMethod):
|
|||
static_assert(IsBaseOf<nsISupports, ${nativeType}>::value,
|
||||
"HasInstance only works for nsISupports-based classes.");
|
||||
|
||||
bool ok = InterfaceHasInstance(cx, obj, instance, bp);
|
||||
if (!ok || *bp) {
|
||||
bool ok = InterfaceHasInstance(cx, argc, vp);
|
||||
if (!ok || args.rval().toBoolean()) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
@ -1829,7 +1808,7 @@ class CGClassHasInstanceHook(CGAbstractStaticMethod):
|
|||
nsContentUtils::XPConnect()->GetNativeOfWrapper(cx,
|
||||
js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false));
|
||||
nsCOMPtr<nsIDOM${name}> qiResult = do_QueryInterface(native);
|
||||
*bp = !!qiResult;
|
||||
args.rval().setBoolean(!!qiResult);
|
||||
return true;
|
||||
""",
|
||||
nativeType=self.descriptor.nativeType,
|
||||
|
@ -1838,16 +1817,16 @@ class CGClassHasInstanceHook(CGAbstractStaticMethod):
|
|||
hasInstanceCode = dedent("""
|
||||
|
||||
const DOMJSClass* domClass = GetDOMClass(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false));
|
||||
*bp = false;
|
||||
if (!domClass) {
|
||||
// Not a DOM object, so certainly not an instance of this interface
|
||||
args.rval().setBoolean(false);
|
||||
return true;
|
||||
}
|
||||
""")
|
||||
if self.descriptor.interface.identifier.name == "ChromeWindow":
|
||||
setBp = "*bp = UnwrapDOMObject<nsGlobalWindow>(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false))->IsChromeWindow()"
|
||||
setRval = "args.rval().setBoolean(UnwrapDOMObject<nsGlobalWindow>(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false))->IsChromeWindow())"
|
||||
else:
|
||||
setBp = "*bp = true"
|
||||
setRval = "args.rval().setBoolean(true)"
|
||||
# Sort interaces implementing self by name so we get stable output.
|
||||
for iface in sorted(self.descriptor.interface.interfacesImplementingSelf,
|
||||
key=lambda iface: iface.identifier.name):
|
||||
|
@ -1855,13 +1834,14 @@ class CGClassHasInstanceHook(CGAbstractStaticMethod):
|
|||
"""
|
||||
|
||||
if (domClass->mInterfaceChain[PrototypeTraits<prototypes::id::${name}>::Depth] == prototypes::id::${name}) {
|
||||
${setBp};
|
||||
${setRval};
|
||||
return true;
|
||||
}
|
||||
""",
|
||||
name=iface.identifier.name,
|
||||
setBp=setBp)
|
||||
hasInstanceCode += "return true;\n"
|
||||
setRval=setRval)
|
||||
hasInstanceCode += ("args.rval().setBoolean(false);\n"
|
||||
"return true;\n")
|
||||
return header + hasInstanceCode
|
||||
|
||||
|
||||
|
@ -2244,6 +2224,20 @@ class MethodDefiner(PropertyDefiner):
|
|||
"condition": MemberCondition()
|
||||
})
|
||||
|
||||
if (static and
|
||||
not unforgeable and
|
||||
descriptor.interface.hasInterfaceObject() and
|
||||
NeedsGeneratedHasInstance(descriptor)):
|
||||
self.regular.append({
|
||||
"name": "@@hasInstance",
|
||||
"methodInfo": False,
|
||||
"nativeName": HASINSTANCE_HOOK_NAME,
|
||||
"length": 1,
|
||||
# Flags match those of Function[Symbol.hasInstance]
|
||||
"flags": "JSPROP_READONLY | JSPROP_PERMANENT",
|
||||
"condition": MemberCondition()
|
||||
})
|
||||
|
||||
# Generate the keys/values/entries aliases for value iterables.
|
||||
maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
|
||||
if (not static and
|
||||
|
@ -2792,11 +2786,6 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
|||
else:
|
||||
prefCache = None
|
||||
|
||||
if (needInterfaceObject and
|
||||
self.descriptor.needsConstructHookHolder()):
|
||||
constructHookHolder = "&" + CONSTRUCT_HOOK_NAME + "_holder"
|
||||
else:
|
||||
constructHookHolder = "nullptr"
|
||||
if self.descriptor.interface.ctor():
|
||||
constructArgs = methodLength(self.descriptor.interface.ctor())
|
||||
else:
|
||||
|
@ -2831,11 +2820,11 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
|||
constructorProto = "nullptr"
|
||||
|
||||
isGlobal = self.descriptor.isGlobal() is not None
|
||||
if not isGlobal and self.properties.hasNonChromeOnly():
|
||||
if self.properties.hasNonChromeOnly():
|
||||
properties = "sNativeProperties.Upcast()"
|
||||
else:
|
||||
properties = "nullptr"
|
||||
if not isGlobal and self.properties.hasChromeOnly():
|
||||
if self.properties.hasChromeOnly():
|
||||
chromeProperties = "nsContentUtils::ThreadsafeIsCallerChrome() ? sChromeOnlyNativeProperties.Upcast() : nullptr"
|
||||
else:
|
||||
chromeProperties = "nullptr"
|
||||
|
@ -2846,26 +2835,27 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
|||
JS::Heap<JSObject*>* interfaceCache = ${interfaceCache};
|
||||
dom::CreateInterfaceObjects(aCx, aGlobal, ${parentProto},
|
||||
${protoClass}, protoCache,
|
||||
${constructorProto}, ${interfaceClass}, ${constructHookHolder}, ${constructArgs}, ${namedConstructors},
|
||||
${constructorProto}, ${interfaceClass}, ${constructArgs}, ${namedConstructors},
|
||||
interfaceCache,
|
||||
${properties},
|
||||
${chromeProperties},
|
||||
${name}, aDefineOnGlobal,
|
||||
${unscopableNames});
|
||||
${unscopableNames},
|
||||
${isGlobal});
|
||||
""",
|
||||
protoClass=protoClass,
|
||||
parentProto=parentProto,
|
||||
protoCache=protoCache,
|
||||
constructorProto=constructorProto,
|
||||
interfaceClass=interfaceClass,
|
||||
constructHookHolder=constructHookHolder,
|
||||
constructArgs=constructArgs,
|
||||
namedConstructors=namedConstructors,
|
||||
interfaceCache=interfaceCache,
|
||||
properties=properties,
|
||||
chromeProperties=chromeProperties,
|
||||
name='"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "nullptr",
|
||||
unscopableNames="unscopableNames" if self.haveUnscopables else "nullptr")
|
||||
unscopableNames="unscopableNames" if self.haveUnscopables else "nullptr",
|
||||
isGlobal=toStringBool(isGlobal))
|
||||
|
||||
# If we fail after here, we must clear interface and prototype caches
|
||||
# using this code: intermediate failure must not expose the interface in
|
||||
|
@ -11976,6 +11966,12 @@ class CGDescriptor(CGThing):
|
|||
for m in clearableCachedAttrs(descriptor):
|
||||
cgThings.append(CGJSImplClearCachedValueMethod(descriptor, m))
|
||||
|
||||
# Need to output our generated hasinstance bits before
|
||||
# PropertyArrays tries to use them.
|
||||
if (descriptor.interface.hasInterfaceObject() and
|
||||
NeedsGeneratedHasInstance(descriptor)):
|
||||
cgThings.append(CGHasInstanceHook(descriptor))
|
||||
|
||||
properties = PropertyArrays(descriptor)
|
||||
cgThings.append(CGGeneric(define=str(properties)))
|
||||
cgThings.append(CGNativeProperties(descriptor, properties))
|
||||
|
@ -11996,10 +11992,7 @@ class CGDescriptor(CGThing):
|
|||
if descriptor.interface.hasInterfaceObject():
|
||||
cgThings.append(CGClassConstructor(descriptor,
|
||||
descriptor.interface.ctor()))
|
||||
cgThings.append(CGClassHasInstanceHook(descriptor))
|
||||
cgThings.append(CGInterfaceObjectJSClass(descriptor, properties))
|
||||
if descriptor.needsConstructHookHolder():
|
||||
cgThings.append(CGClassConstructHookHolder(descriptor))
|
||||
cgThings.append(CGNamedConstructors(descriptor))
|
||||
|
||||
cgThings.append(CGLegacyCallHook(descriptor))
|
||||
|
|
|
@ -602,10 +602,6 @@ class Descriptor(DescriptorProvider):
|
|||
def hasNonOrdinaryGetPrototypeOf(self):
|
||||
return self.interface.getExtendedAttribute("NonOrdinaryGetPrototypeOf")
|
||||
|
||||
def needsConstructHookHolder(self):
|
||||
assert self.interface.hasInterfaceObject()
|
||||
return False
|
||||
|
||||
def needsHeaderInclude(self):
|
||||
"""
|
||||
An interface doesn't need a header file if it is not concrete, not
|
||||
|
|
|
@ -386,9 +386,14 @@ struct DOMIfaceAndProtoJSClass
|
|||
|
||||
// Either eInterface, eInterfacePrototype, eGlobalInterfacePrototype or
|
||||
// eNamedPropertiesObject.
|
||||
DOMObjectType mType;
|
||||
DOMObjectType mType; // uint8_t
|
||||
|
||||
const prototypes::ID mPrototypeID;
|
||||
// Boolean indicating whether this object wants a @@hasInstance property
|
||||
// pointing to InterfaceHasInstance defined on it. Only ever true for the
|
||||
// eInterface case.
|
||||
bool wantsInterfaceHasInstance;
|
||||
|
||||
const prototypes::ID mPrototypeID; // uint16_t
|
||||
const uint32_t mDepth;
|
||||
|
||||
const NativePropertyHooks* mNativeHooks;
|
||||
|
|
|
@ -1630,6 +1630,13 @@ WebGLContext::DummyReadFramebufferOperation(const char* funcName)
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLContext::HasTimestampBits() const
|
||||
{
|
||||
// 'sync' provides glGetInteger64v either by supporting ARB_sync, GL3+, or GLES3+.
|
||||
return gl->IsSupported(GLFeature::sync);
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckContextLost(GLContext* gl, bool* const out_isGuilty)
|
||||
{
|
||||
|
|
|
@ -1475,6 +1475,8 @@ protected:
|
|||
bool mNeedsFakeNoStencil;
|
||||
bool mNeedsEmulatedLoneDepthStencil;
|
||||
|
||||
bool HasTimestampBits() const;
|
||||
|
||||
struct ScopedMaskWorkaround {
|
||||
WebGLContext& mWebGL;
|
||||
const bool mFakeNoAlpha;
|
||||
|
|
|
@ -267,7 +267,11 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
|
|||
if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) {
|
||||
if (pname == LOCAL_GL_TIMESTAMP_EXT) {
|
||||
GLuint64 iv = 0;
|
||||
gl->fGetInteger64v(pname, (GLint64*) &iv);
|
||||
if (HasTimestampBits()) {
|
||||
gl->fGetInteger64v(pname, (GLint64*)&iv);
|
||||
} else {
|
||||
GenerateWarning("QUERY_COUNTER_BITS_EXT for TIMESTAMP_EXT is 0.");
|
||||
}
|
||||
// TODO: JS doesn't support 64-bit integers. Be lossy and
|
||||
// cast to double (53 bits)
|
||||
return JS::NumberValue(static_cast<double>(iv));
|
||||
|
|
|
@ -20,6 +20,7 @@ WebGLExtensionDepthTexture::WebGLExtensionDepthTexture(WebGLContext* webgl)
|
|||
GLenum unpackType)
|
||||
{
|
||||
auto usage = fua->EditUsage(effFormat);
|
||||
usage->isFilterable = true;
|
||||
usage->SetRenderable();
|
||||
|
||||
const webgl::PackingInfo pi = {unpackFormat, unpackType};
|
||||
|
|
|
@ -177,7 +177,9 @@ WebGLExtensionDisjointTimerQuery::GetQueryEXT(JSContext* cx, GLenum target,
|
|||
return;
|
||||
}
|
||||
GLint bits = 0;
|
||||
mContext->GL()->fGetQueryiv(target, pname, &bits);
|
||||
if (mContext->HasTimestampBits()) {
|
||||
mContext->GL()->fGetQueryiv(target, pname, &bits);
|
||||
}
|
||||
retval.set(JS::Int32Value(int32_t(bits)));
|
||||
break;
|
||||
}
|
||||
|
@ -242,11 +244,7 @@ WebGLExtensionDisjointTimerQuery::IsSupported(const WebGLContext* webgl)
|
|||
gl::GLContext* gl = webgl->GL();
|
||||
return gl->IsSupported(gl::GLFeature::query_objects) &&
|
||||
gl->IsSupported(gl::GLFeature::get_query_object_i64v) &&
|
||||
gl->IsSupported(gl::GLFeature::query_counter) && // provides GL_TIMESTAMP
|
||||
gl->IsSupported(gl::GLFeature::sync); // provides glGetInteger64v
|
||||
// 'sync' provides glGetInteger64v either by supporting ARB_sync, GL3+, or GLES3+.
|
||||
// Since there are no differences between support for glGetInteger64v and support for
|
||||
// 'sync', we just piggy-back off of 'sync'.
|
||||
gl->IsSupported(gl::GLFeature::query_counter); // provides GL_TIMESTAMP
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -338,18 +338,7 @@ WebGLTexture::IsComplete(uint32_t texUnit, const char** const out_reason) const
|
|||
auto formatUsage = baseImageInfo.mFormat;
|
||||
auto format = formatUsage->format;
|
||||
|
||||
// "* The effective internal format specified for the texture arrays is a sized
|
||||
// internal color format that is not texture-filterable, and either the
|
||||
// magnification filter is not NEAREST or the minification filter is neither
|
||||
// NEAREST nor NEAREST_MIPMAP_NEAREST."
|
||||
// Since all (GLES3) unsized color formats are filterable just like their sized
|
||||
// equivalents, we don't have to care whether its sized or not.
|
||||
if (format->IsColorFormat() && !formatUsage->isFilterable) {
|
||||
*out_reason = "Because minification or magnification filtering is not NEAREST"
|
||||
" or NEAREST_MIPMAP_NEAREST, and the texture's format is a"
|
||||
" color format, its format must be \"texture-filterable\".";
|
||||
return false;
|
||||
}
|
||||
bool isFilterable = formatUsage->isFilterable;
|
||||
|
||||
// "* The effective internal format specified for the texture arrays is a sized
|
||||
// internal depth or depth and stencil format, the value of
|
||||
|
@ -360,16 +349,21 @@ WebGLTexture::IsComplete(uint32_t texUnit, const char** const out_reason) const
|
|||
// 3.0.1:
|
||||
// "* Clarify that a texture is incomplete if it has a depth component, no
|
||||
// shadow comparison, and linear filtering (also Bug 9481)."
|
||||
// As of OES_packed_depth_stencil rev #3, the sample code explicitly samples from
|
||||
// a DEPTH_STENCIL_OES texture with a min-filter of LINEAR. Therefore we relax
|
||||
// this restriction if WEBGL_depth_texture is enabled.
|
||||
if (!mContext->IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture)) {
|
||||
if (format->d && mTexCompareMode != LOCAL_GL_NONE) {
|
||||
*out_reason = "A depth or depth-stencil format with TEXTURE_COMPARE_MODE"
|
||||
" of NONE must have minification or magnification filtering"
|
||||
" of NEAREST or NEAREST_MIPMAP_NEAREST.";
|
||||
return false;
|
||||
}
|
||||
if (format->d && mTexCompareMode != LOCAL_GL_NONE) {
|
||||
isFilterable = true;
|
||||
}
|
||||
|
||||
// "* The effective internal format specified for the texture arrays is a sized
|
||||
// internal color format that is not texture-filterable, and either the
|
||||
// magnification filter is not NEAREST or the minification filter is neither
|
||||
// NEAREST nor NEAREST_MIPMAP_NEAREST."
|
||||
// Since all (GLES3) unsized color formats are filterable just like their sized
|
||||
// equivalents, we don't have to care whether its sized or not.
|
||||
if (!isFilterable) {
|
||||
*out_reason = "Because minification or magnification filtering is not NEAREST"
|
||||
" or NEAREST_MIPMAP_NEAREST, and the texture's format must be"
|
||||
" \"texture-filterable\".";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ fail-if = (os == 'android')
|
|||
[ensure-exts/test_EXT_color_buffer_half_float.html]
|
||||
fail-if = (os == 'android')
|
||||
[ensure-exts/test_EXT_disjoint_timer_query.html]
|
||||
fail-if = (os == 'android') || (os == 'mac') || (os == 'win')
|
||||
fail-if = (os == 'android') || (os == 'mac') || (os == 'win' && os_version == '5.1')
|
||||
[ensure-exts/test_EXT_frag_depth.html]
|
||||
fail-if = (os == 'android')
|
||||
[ensure-exts/test_EXT_sRGB.html]
|
||||
|
@ -84,6 +84,7 @@ skip-if = android_version == '18' #Android 4.3 aws only; bug 1030942
|
|||
skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
|
||||
[test_webgl_compressed_texture_es3.html]
|
||||
[test_webgl_disjoint_timer_query.html]
|
||||
fail-if = (os == 'win' && (os_version == '6.1' || os_version == '6.2'))
|
||||
[test_webgl_force_enable.html]
|
||||
[test_webgl_request_context.html]
|
||||
skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
|
||||
|
|
|
@ -797,6 +797,17 @@ HTMLMediaElement::MozDumpDebugInfo()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
HTMLMediaElement::SetVisible(bool aVisible)
|
||||
{
|
||||
if (!mDecoder) {
|
||||
return;
|
||||
}
|
||||
|
||||
mDecoder->NotifyOwnerActivityChanged(aVisible);
|
||||
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMediaStream>
|
||||
HTMLMediaElement::GetSrcObject() const
|
||||
{
|
||||
|
|
|
@ -606,6 +606,8 @@ public:
|
|||
|
||||
void MozDumpDebugInfo();
|
||||
|
||||
void SetVisible(bool aVisible);
|
||||
|
||||
already_AddRefed<DOMMediaStream> GetSrcObject() const;
|
||||
void SetSrcObject(DOMMediaStream& aValue);
|
||||
void SetSrcObject(DOMMediaStream* aValue);
|
||||
|
|
|
@ -2491,6 +2491,10 @@ BackgroundRequestChild::Recv__delete__(const RequestResponse& aResponse)
|
|||
HandleResponse(aResponse.get_ObjectStoreGetResponse().cloneInfo());
|
||||
break;
|
||||
|
||||
case RequestResponse::TObjectStoreGetKeyResponse:
|
||||
HandleResponse(aResponse.get_ObjectStoreGetKeyResponse().key());
|
||||
break;
|
||||
|
||||
case RequestResponse::TObjectStoreGetAllResponse:
|
||||
HandleResponse(aResponse.get_ObjectStoreGetAllResponse().cloneInfos());
|
||||
break;
|
||||
|
@ -2643,8 +2647,7 @@ BackgroundCursorChild::AssertIsOnOwningThread() const
|
|||
#endif // DEBUG
|
||||
|
||||
void
|
||||
BackgroundCursorChild::SendContinueInternal(const CursorRequestParams& aParams,
|
||||
const Key& aKey)
|
||||
BackgroundCursorChild::SendContinueInternal(const CursorRequestParams& aParams)
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(mRequest);
|
||||
|
@ -2661,66 +2664,7 @@ BackgroundCursorChild::SendContinueInternal(const CursorRequestParams& aParams,
|
|||
|
||||
mTransaction->OnNewRequest();
|
||||
|
||||
CursorRequestParams params = aParams;
|
||||
Key key = aKey;
|
||||
|
||||
switch (params.type()) {
|
||||
case CursorRequestParams::TContinueParams: {
|
||||
if (key.IsUnset()) {
|
||||
break;
|
||||
}
|
||||
while (!mCachedResponses.IsEmpty()) {
|
||||
if (mCachedResponses[0].mKey == key) {
|
||||
break;
|
||||
}
|
||||
mCachedResponses.RemoveElementAt(0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CursorRequestParams::TAdvanceParams: {
|
||||
uint32_t& advanceCount = params.get_AdvanceParams().count();
|
||||
while (advanceCount > 1 && !mCachedResponses.IsEmpty()) {
|
||||
key = mCachedResponses[0].mKey;
|
||||
mCachedResponses.RemoveElementAt(0);
|
||||
--advanceCount;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Should never get here!");
|
||||
}
|
||||
|
||||
if (!mCachedResponses.IsEmpty()) {
|
||||
nsCOMPtr<nsIRunnable> continueRunnable = new DelayedActionRunnable(
|
||||
this, &BackgroundCursorChild::SendDelayedContinueInternal);
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(continueRunnable));
|
||||
} else {
|
||||
MOZ_ALWAYS_TRUE(PBackgroundIDBCursorChild::SendContinue(params, key));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BackgroundCursorChild::SendDelayedContinueInternal()
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(mTransaction);
|
||||
MOZ_ASSERT(mCursor);
|
||||
MOZ_ASSERT(mStrongCursor);
|
||||
MOZ_ASSERT(!mCachedResponses.IsEmpty());
|
||||
|
||||
RefPtr<IDBCursor> cursor;
|
||||
mStrongCursor.swap(cursor);
|
||||
|
||||
auto& item = mCachedResponses[0];
|
||||
mCursor->Reset(Move(item.mKey), Move(item.mCloneInfo));
|
||||
mCachedResponses.RemoveElementAt(0);
|
||||
|
||||
ResultHelper helper(mRequest, mTransaction, mCursor);
|
||||
DispatchSuccessEvent(&helper);
|
||||
|
||||
mTransaction->OnRequestFinished(/* aActorDestroyedNormally */ true);
|
||||
MOZ_ALWAYS_TRUE(PBackgroundIDBCursorChild::SendContinue(aParams));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2743,14 +2687,6 @@ BackgroundCursorChild::SendDeleteMeInternal()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
BackgroundCursorChild::InvalidateCachedResponses()
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
mCachedResponses.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
BackgroundCursorChild::HandleResponse(nsresult aResponse)
|
||||
{
|
||||
|
@ -2816,14 +2752,7 @@ BackgroundCursorChild::HandleResponse(
|
|||
RefPtr<IDBCursor> newCursor;
|
||||
|
||||
if (mCursor) {
|
||||
if (mCursor->IsContinueCalled()) {
|
||||
mCursor->Reset(Move(response.key()), Move(cloneReadInfo));
|
||||
} else {
|
||||
CachedResponse cachedResponse;
|
||||
cachedResponse.mKey = Move(response.key());
|
||||
cachedResponse.mCloneInfo = Move(cloneReadInfo);
|
||||
mCachedResponses.AppendElement(Move(cachedResponse));
|
||||
}
|
||||
mCursor->Reset(Move(response.key()), Move(cloneReadInfo));
|
||||
} else {
|
||||
newCursor = IDBCursor::Create(this,
|
||||
Move(response.key()),
|
||||
|
@ -3046,16 +2975,6 @@ DelayedActionRunnable::Cancel()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
BackgroundCursorChild::CachedResponse::CachedResponse()
|
||||
{
|
||||
}
|
||||
|
||||
BackgroundCursorChild::CachedResponse::CachedResponse(CachedResponse&& aOther)
|
||||
: mKey(Move(aOther.mKey))
|
||||
{
|
||||
mCloneInfo = Move(aOther.mCloneInfo);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* BackgroundUtilsChild
|
||||
******************************************************************************/
|
||||
|
|
|
@ -688,17 +688,6 @@ class BackgroundCursorChild final
|
|||
|
||||
class DelayedActionRunnable;
|
||||
|
||||
struct CachedResponse
|
||||
{
|
||||
CachedResponse();
|
||||
|
||||
CachedResponse(CachedResponse&& aOther);
|
||||
|
||||
Key mKey;
|
||||
Key mObjectKey;
|
||||
StructuredCloneReadInfo mCloneInfo;
|
||||
};
|
||||
|
||||
IDBRequest* mRequest;
|
||||
IDBTransaction* mTransaction;
|
||||
IDBObjectStore* mObjectStore;
|
||||
|
@ -715,8 +704,6 @@ class BackgroundCursorChild final
|
|||
PRThread* mOwningThread;
|
||||
#endif
|
||||
|
||||
nsTArray<CachedResponse> mCachedResponses;
|
||||
|
||||
public:
|
||||
BackgroundCursorChild(IDBRequest* aRequest,
|
||||
IDBObjectStore* aObjectStore,
|
||||
|
@ -735,14 +722,11 @@ public:
|
|||
#endif
|
||||
|
||||
void
|
||||
SendContinueInternal(const CursorRequestParams& aParams, const Key& aKey);
|
||||
SendContinueInternal(const CursorRequestParams& aParams);
|
||||
|
||||
void
|
||||
SendDeleteMeInternal();
|
||||
|
||||
void
|
||||
InvalidateCachedResponses();
|
||||
|
||||
IDBRequest*
|
||||
GetRequest() const
|
||||
{
|
||||
|
@ -780,9 +764,6 @@ private:
|
|||
// BackgroundVersionChangeTransactionChild.
|
||||
~BackgroundCursorChild();
|
||||
|
||||
void
|
||||
SendDelayedContinueInternal();
|
||||
|
||||
void
|
||||
HandleResponse(nsresult aResponse);
|
||||
|
||||
|
@ -810,7 +791,7 @@ private:
|
|||
|
||||
// Force callers to use SendContinueInternal.
|
||||
bool
|
||||
SendContinue(const CursorRequestParams& aParams, const Key& aKey) = delete;
|
||||
SendContinue(const CursorRequestParams& aParams) = delete;
|
||||
|
||||
bool
|
||||
SendDeleteMe() = delete;
|
||||
|
|
|
@ -8185,23 +8185,24 @@ private:
|
|||
GetResponse(RequestResponse& aResponse) override;
|
||||
};
|
||||
|
||||
class ObjectStoreGetAllKeysRequestOp final
|
||||
class ObjectStoreGetKeyRequestOp final
|
||||
: public NormalTransactionOp
|
||||
{
|
||||
friend class TransactionBase;
|
||||
|
||||
const ObjectStoreGetAllKeysParams mParams;
|
||||
const uint32_t mObjectStoreId;
|
||||
const OptionalKeyRange mOptionalKeyRange;
|
||||
const uint32_t mLimit;
|
||||
const bool mGetAll;
|
||||
FallibleTArray<Key> mResponse;
|
||||
|
||||
private:
|
||||
// Only created by TransactionBase.
|
||||
ObjectStoreGetAllKeysRequestOp(TransactionBase* aTransaction,
|
||||
const ObjectStoreGetAllKeysParams& aParams)
|
||||
: NormalTransactionOp(aTransaction)
|
||||
, mParams(aParams)
|
||||
{ }
|
||||
ObjectStoreGetKeyRequestOp(TransactionBase* aTransaction,
|
||||
const RequestParams& aParams,
|
||||
bool aGetAll);
|
||||
|
||||
~ObjectStoreGetAllKeysRequestOp()
|
||||
~ObjectStoreGetKeyRequestOp()
|
||||
{ }
|
||||
|
||||
virtual nsresult
|
||||
|
@ -8426,6 +8427,7 @@ private:
|
|||
|
||||
nsCString mContinueQuery;
|
||||
nsCString mContinueToQuery;
|
||||
nsCString mContinuePrimaryKeyQuery;
|
||||
nsCString mLocale;
|
||||
|
||||
Key mKey;
|
||||
|
@ -8483,7 +8485,7 @@ private:
|
|||
RecvDeleteMe() override;
|
||||
|
||||
virtual bool
|
||||
RecvContinue(const CursorRequestParams& aParams, const Key& key) override;
|
||||
RecvContinue(const CursorRequestParams& aParams) override;
|
||||
|
||||
bool
|
||||
IsLocaleAware() const {
|
||||
|
@ -8578,16 +8580,13 @@ class Cursor::ContinueOp final
|
|||
friend class Cursor;
|
||||
|
||||
const CursorRequestParams mParams;
|
||||
const Key mKey;
|
||||
|
||||
private:
|
||||
// Only created by Cursor.
|
||||
ContinueOp(Cursor* aCursor,
|
||||
const CursorRequestParams& aParams,
|
||||
const Key& aKey)
|
||||
const CursorRequestParams& aParams)
|
||||
: CursorOpBase(aCursor)
|
||||
, mParams(aParams)
|
||||
, mKey(aKey)
|
||||
{
|
||||
MOZ_ASSERT(aParams.type() != CursorRequestParams::T__None);
|
||||
}
|
||||
|
@ -14531,6 +14530,22 @@ TransactionBase::VerifyRequestParams(const RequestParams& aParams) const
|
|||
break;
|
||||
}
|
||||
|
||||
case RequestParams::TObjectStoreGetKeyParams: {
|
||||
const ObjectStoreGetKeyParams& params =
|
||||
aParams.get_ObjectStoreGetKeyParams();
|
||||
const RefPtr<FullObjectStoreMetadata> objectStoreMetadata =
|
||||
GetMetadataForObjectStoreId(params.objectStoreId());
|
||||
if (NS_WARN_IF(!objectStoreMetadata)) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return false;
|
||||
}
|
||||
if (NS_WARN_IF(!VerifyRequestParams(params.keyRange()))) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case RequestParams::TObjectStoreGetAllParams: {
|
||||
const ObjectStoreGetAllParams& params =
|
||||
aParams.get_ObjectStoreGetAllParams();
|
||||
|
@ -14993,10 +15008,14 @@ TransactionBase::AllocRequest(const RequestParams& aParams, bool aTrustParams)
|
|||
new ObjectStoreGetRequestOp(this, aParams, /* aGetAll */ true);
|
||||
break;
|
||||
|
||||
case RequestParams::TObjectStoreGetKeyParams:
|
||||
actor =
|
||||
new ObjectStoreGetKeyRequestOp(this, aParams, /* aGetAll */ false);
|
||||
break;
|
||||
|
||||
case RequestParams::TObjectStoreGetAllKeysParams:
|
||||
actor =
|
||||
new ObjectStoreGetAllKeysRequestOp(this,
|
||||
aParams.get_ObjectStoreGetAllKeysParams());
|
||||
new ObjectStoreGetKeyRequestOp(this, aParams, /* aGetAll */ true);
|
||||
break;
|
||||
|
||||
case RequestParams::TObjectStoreDeleteParams:
|
||||
|
@ -16164,6 +16183,34 @@ Cursor::VerifyRequestParams(const CursorRequestParams& aParams) const
|
|||
break;
|
||||
}
|
||||
|
||||
case CursorRequestParams::TContinuePrimaryKeyParams: {
|
||||
const Key& key = aParams.get_ContinuePrimaryKeyParams().key();
|
||||
const Key& primaryKey = aParams.get_ContinuePrimaryKeyParams().primaryKey();
|
||||
MOZ_ASSERT(!key.IsUnset());
|
||||
MOZ_ASSERT(!primaryKey.IsUnset());
|
||||
switch (mDirection) {
|
||||
case IDBCursor::NEXT:
|
||||
if (NS_WARN_IF(key < sortKey ||
|
||||
(key == sortKey && primaryKey <= mObjectKey))) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case IDBCursor::PREV:
|
||||
if (NS_WARN_IF(key > sortKey ||
|
||||
(key == sortKey && primaryKey >= mObjectKey))) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Should never get here!");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CursorRequestParams::TAdvanceParams:
|
||||
if (NS_WARN_IF(!aParams.get_AdvanceParams().count())) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
|
@ -16324,7 +16371,7 @@ Cursor::RecvDeleteMe()
|
|||
}
|
||||
|
||||
bool
|
||||
Cursor::RecvContinue(const CursorRequestParams& aParams, const Key& aKey)
|
||||
Cursor::RecvContinue(const CursorRequestParams& aParams)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(aParams.type() != CursorRequestParams::T__None);
|
||||
|
@ -16358,7 +16405,7 @@ Cursor::RecvContinue(const CursorRequestParams& aParams, const Key& aKey)
|
|||
return false;
|
||||
}
|
||||
|
||||
RefPtr<ContinueOp> continueOp = new ContinueOp(this, aParams, aKey);
|
||||
RefPtr<ContinueOp> continueOp = new ContinueOp(this, aParams);
|
||||
if (NS_WARN_IF(!continueOp->Init(mTransaction))) {
|
||||
continueOp->Cleanup();
|
||||
return false;
|
||||
|
@ -25866,31 +25913,54 @@ ObjectStoreGetRequestOp::GetResponse(RequestResponse& aResponse)
|
|||
}
|
||||
}
|
||||
|
||||
ObjectStoreGetKeyRequestOp::ObjectStoreGetKeyRequestOp(
|
||||
TransactionBase* aTransaction,
|
||||
const RequestParams& aParams,
|
||||
bool aGetAll)
|
||||
: NormalTransactionOp(aTransaction)
|
||||
, mObjectStoreId(aGetAll ?
|
||||
aParams.get_ObjectStoreGetAllKeysParams().objectStoreId() :
|
||||
aParams.get_ObjectStoreGetKeyParams().objectStoreId())
|
||||
, mOptionalKeyRange(aGetAll ?
|
||||
aParams.get_ObjectStoreGetAllKeysParams()
|
||||
.optionalKeyRange() :
|
||||
OptionalKeyRange(aParams.get_ObjectStoreGetKeyParams()
|
||||
.keyRange()))
|
||||
, mLimit(aGetAll ? aParams.get_ObjectStoreGetAllKeysParams().limit() : 1)
|
||||
, mGetAll(aGetAll)
|
||||
{
|
||||
MOZ_ASSERT(aParams.type() == RequestParams::TObjectStoreGetKeyParams ||
|
||||
aParams.type() == RequestParams::TObjectStoreGetAllKeysParams);
|
||||
MOZ_ASSERT(mObjectStoreId);
|
||||
MOZ_ASSERT_IF(!aGetAll,
|
||||
mOptionalKeyRange.type() ==
|
||||
OptionalKeyRange::TSerializedKeyRange);
|
||||
}
|
||||
|
||||
nsresult
|
||||
ObjectStoreGetAllKeysRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
||||
ObjectStoreGetKeyRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
||||
{
|
||||
MOZ_ASSERT(aConnection);
|
||||
aConnection->AssertIsOnConnectionThread();
|
||||
|
||||
PROFILER_LABEL("IndexedDB",
|
||||
"ObjectStoreGetAllKeysRequestOp::DoDatabaseWork",
|
||||
"ObjectStoreGetKeyRequestOp::DoDatabaseWork",
|
||||
js::ProfileEntry::Category::STORAGE);
|
||||
|
||||
const bool hasKeyRange =
|
||||
mParams.optionalKeyRange().type() == OptionalKeyRange::TSerializedKeyRange;
|
||||
mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange;
|
||||
|
||||
nsAutoCString keyRangeClause;
|
||||
if (hasKeyRange) {
|
||||
GetBindingClauseForKeyRange(
|
||||
mParams.optionalKeyRange().get_SerializedKeyRange(),
|
||||
NS_LITERAL_CSTRING("key"),
|
||||
keyRangeClause);
|
||||
GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(),
|
||||
NS_LITERAL_CSTRING("key"),
|
||||
keyRangeClause);
|
||||
}
|
||||
|
||||
nsAutoCString limitClause;
|
||||
if (uint32_t limit = mParams.limit()) {
|
||||
if (mLimit) {
|
||||
limitClause.AssignLiteral(" LIMIT ");
|
||||
limitClause.AppendInt(limit);
|
||||
limitClause.AppendInt(mLimit);
|
||||
}
|
||||
|
||||
nsCString query =
|
||||
|
@ -25907,16 +25977,14 @@ ObjectStoreGetAllKeysRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
|||
return rv;
|
||||
}
|
||||
|
||||
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
|
||||
mParams.objectStoreId());
|
||||
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStoreId);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (hasKeyRange) {
|
||||
rv = BindKeyRangeToStatement(
|
||||
mParams.optionalKeyRange().get_SerializedKeyRange(),
|
||||
stmt);
|
||||
rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(),
|
||||
stmt);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -25939,18 +26007,32 @@ ObjectStoreGetAllKeysRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
|||
return rv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
ObjectStoreGetAllKeysRequestOp::GetResponse(RequestResponse& aResponse)
|
||||
ObjectStoreGetKeyRequestOp::GetResponse(RequestResponse& aResponse)
|
||||
{
|
||||
aResponse = ObjectStoreGetAllKeysResponse();
|
||||
MOZ_ASSERT_IF(mLimit, mResponse.Length() <= mLimit);
|
||||
|
||||
if (mGetAll) {
|
||||
aResponse = ObjectStoreGetAllKeysResponse();
|
||||
|
||||
if (!mResponse.IsEmpty()) {
|
||||
nsTArray<Key>& response =
|
||||
aResponse.get_ObjectStoreGetAllKeysResponse().keys();
|
||||
mResponse.SwapElements(response);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
aResponse = ObjectStoreGetKeyResponse();
|
||||
|
||||
if (!mResponse.IsEmpty()) {
|
||||
nsTArray<Key>& response =
|
||||
aResponse.get_ObjectStoreGetAllKeysResponse().keys();
|
||||
mResponse.SwapElements(response);
|
||||
aResponse.get_ObjectStoreGetKeyResponse().key() = Move(mResponse[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27338,6 +27420,13 @@ OpenOp::DoIndexDatabaseWork(DatabaseConnection* aConnection)
|
|||
NS_LITERAL_CSTRING(" AND sort_column >= :current_key") +
|
||||
directionClause +
|
||||
openLimit;
|
||||
mCursor->mContinuePrimaryKeyQuery =
|
||||
queryStart +
|
||||
NS_LITERAL_CSTRING(" AND sort_column >= :current_key "
|
||||
"AND index_table.object_data_key >= :object_key "
|
||||
) +
|
||||
directionClause +
|
||||
openLimit;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -27383,6 +27472,13 @@ OpenOp::DoIndexDatabaseWork(DatabaseConnection* aConnection)
|
|||
NS_LITERAL_CSTRING(" AND sort_column <= :current_key") +
|
||||
directionClause +
|
||||
openLimit;
|
||||
mCursor->mContinuePrimaryKeyQuery =
|
||||
queryStart +
|
||||
NS_LITERAL_CSTRING(" AND sort_column <= :current_key "
|
||||
"AND index_table.object_data_key <= :object_key "
|
||||
) +
|
||||
directionClause +
|
||||
openLimit;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -27560,6 +27656,13 @@ OpenOp::DoIndexKeyDatabaseWork(DatabaseConnection* aConnection)
|
|||
NS_LITERAL_CSTRING(" AND sort_column >= :current_key ") +
|
||||
directionClause +
|
||||
openLimit;
|
||||
mCursor->mContinuePrimaryKeyQuery =
|
||||
queryStart +
|
||||
NS_LITERAL_CSTRING(" AND sort_column >= :current_key "
|
||||
"AND object_data_key >= :object_key "
|
||||
) +
|
||||
directionClause +
|
||||
openLimit;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -27605,6 +27708,13 @@ OpenOp::DoIndexKeyDatabaseWork(DatabaseConnection* aConnection)
|
|||
NS_LITERAL_CSTRING(" AND sort_column <= :current_key ") +
|
||||
directionClause +
|
||||
openLimit;
|
||||
mCursor->mContinuePrimaryKeyQuery =
|
||||
queryStart +
|
||||
NS_LITERAL_CSTRING(" AND sort_column <= :current_key "
|
||||
"AND object_data_key <= :object_key "
|
||||
) +
|
||||
directionClause +
|
||||
openLimit;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -27645,6 +27755,7 @@ OpenOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
|||
MOZ_ASSERT(mCursor);
|
||||
MOZ_ASSERT(mCursor->mContinueQuery.IsEmpty());
|
||||
MOZ_ASSERT(mCursor->mContinueToQuery.IsEmpty());
|
||||
MOZ_ASSERT(mCursor->mContinuePrimaryKeyQuery.IsEmpty());
|
||||
MOZ_ASSERT(mCursor->mKey.IsUnset());
|
||||
MOZ_ASSERT(mCursor->mRangeKey.IsUnset());
|
||||
|
||||
|
@ -27727,6 +27838,10 @@ ContinueOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
|||
mCursor->mType == OpenCursorParams::TIndexOpenCursorParams ||
|
||||
mCursor->mType == OpenCursorParams::TIndexOpenKeyCursorParams;
|
||||
|
||||
MOZ_ASSERT_IF(isIndex &&
|
||||
(mCursor->mDirection == IDBCursor::NEXT ||
|
||||
mCursor->mDirection == IDBCursor::PREV),
|
||||
!mCursor->mContinuePrimaryKeyQuery.IsEmpty());
|
||||
MOZ_ASSERT_IF(isIndex, mCursor->mIndexId);
|
||||
MOZ_ASSERT_IF(isIndex, !mCursor->mObjectKey.IsUnset());
|
||||
|
||||
|
@ -27744,21 +27859,35 @@ ContinueOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
|||
// Note: Changing the number or order of SELECT columns in the query will
|
||||
// require changes to CursorOpBase::PopulateResponseFromStatement.
|
||||
bool hasContinueKey = false;
|
||||
bool hasContinuePrimaryKey = false;
|
||||
uint32_t advanceCount = 1;
|
||||
Key& currentKey = mCursor->IsLocaleAware() ? mCursor->mSortKey : mCursor->mKey;
|
||||
|
||||
if (mParams.type() == CursorRequestParams::TContinueParams) {
|
||||
// Always go to the next result.
|
||||
if (mParams.get_ContinueParams().key().IsUnset()) {
|
||||
hasContinueKey = false;
|
||||
} else {
|
||||
switch (mParams.type()) {
|
||||
case CursorRequestParams::TContinueParams:
|
||||
if (!mParams.get_ContinueParams().key().IsUnset()) {
|
||||
hasContinueKey = true;
|
||||
currentKey = mParams.get_ContinueParams().key();
|
||||
}
|
||||
break;
|
||||
case CursorRequestParams::TContinuePrimaryKeyParams:
|
||||
MOZ_ASSERT(!mParams.get_ContinuePrimaryKeyParams().key().IsUnset());
|
||||
MOZ_ASSERT(!mParams.get_ContinuePrimaryKeyParams().primaryKey().IsUnset());
|
||||
MOZ_ASSERT(mCursor->mDirection == IDBCursor::NEXT ||
|
||||
mCursor->mDirection == IDBCursor::PREV);
|
||||
hasContinueKey = true;
|
||||
}
|
||||
} else {
|
||||
advanceCount = mParams.get_AdvanceParams().count();
|
||||
hasContinueKey = false;
|
||||
hasContinuePrimaryKey = true;
|
||||
currentKey = mParams.get_ContinuePrimaryKeyParams().key();
|
||||
break;
|
||||
case CursorRequestParams::TAdvanceParams:
|
||||
advanceCount = mParams.get_AdvanceParams().count();
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Should never get here!");
|
||||
}
|
||||
|
||||
const nsCString& continueQuery =
|
||||
hasContinuePrimaryKey ? mCursor->mContinuePrimaryKeyQuery :
|
||||
hasContinueKey ? mCursor->mContinueToQuery : mCursor->mContinueQuery;
|
||||
|
||||
MOZ_ASSERT(advanceCount > 0);
|
||||
|
@ -27771,15 +27900,6 @@ ContinueOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
|||
NS_NAMED_LITERAL_CSTRING(rangeKeyName, "range_key");
|
||||
NS_NAMED_LITERAL_CSTRING(objectKeyName, "object_key");
|
||||
|
||||
const bool localeAware = mCursor->IsLocaleAware();
|
||||
|
||||
Key& currentKey = mCursor->mKey;
|
||||
if (hasContinueKey) {
|
||||
currentKey = mParams.get_ContinueParams().key();
|
||||
} else if (localeAware) {
|
||||
currentKey = mCursor->mSortKey;
|
||||
}
|
||||
|
||||
const bool usingRangeKey = !mCursor->mRangeKey.IsUnset();
|
||||
|
||||
DatabaseConnection::CachedStatement stmt;
|
||||
|
@ -27821,6 +27941,16 @@ ContinueOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
|||
}
|
||||
}
|
||||
|
||||
// Bind object key if primaryKey is specified.
|
||||
if (hasContinuePrimaryKey) {
|
||||
rv = mParams.get_ContinuePrimaryKeyParams().primaryKey()
|
||||
.BindToStatement(stmt, objectKeyName);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool hasResult;
|
||||
for (uint32_t index = 0; index < advanceCount; index++) {
|
||||
rv = stmt->ExecuteStep(&hasResult);
|
||||
|
@ -27843,23 +27973,6 @@ ContinueOp::DoDatabaseWork(DatabaseConnection* aConnection)
|
|||
return rv;
|
||||
}
|
||||
|
||||
uint32_t extraCount = 1;
|
||||
for (uint32_t i = 0; i < extraCount; i++) {
|
||||
rv = stmt->ExecuteStep(&hasResult);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!hasResult) {
|
||||
break;
|
||||
}
|
||||
|
||||
rv = PopulateResponseFromStatement(stmt, false);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -510,7 +510,117 @@ IDBCursor::Continue(JSContext* aCx,
|
|||
IDB_LOG_STRINGIFY(key));
|
||||
}
|
||||
|
||||
mBackgroundActor->SendContinueInternal(ContinueParams(key), mKey);
|
||||
mBackgroundActor->SendContinueInternal(ContinueParams(key));
|
||||
|
||||
mContinueCalled = true;
|
||||
}
|
||||
|
||||
void
|
||||
IDBCursor::ContinuePrimaryKey(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aKey,
|
||||
JS::Handle<JS::Value> aPrimaryKey,
|
||||
ErrorResult &aRv)
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
if (!mTransaction->IsOpen()) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsSourceDeleted()) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((mType != Type_Index && mType != Type_IndexKey) ||
|
||||
(mDirection != NEXT && mDirection != PREV)) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mHaveValue || mContinueCalled) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
Key key;
|
||||
aRv = key.SetFromJSVal(aCx, aKey);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
if (IsLocaleAware() && !key.IsUnset()) {
|
||||
Key tmp;
|
||||
aRv = key.ToLocaleBasedKey(tmp, mSourceIndex->Locale());
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
key = tmp;
|
||||
}
|
||||
|
||||
const Key& sortKey = IsLocaleAware() ? mSortKey : mKey;
|
||||
#else
|
||||
const Key& sortKey = mKey;
|
||||
#endif
|
||||
|
||||
if (key.IsUnset()) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
Key primaryKey;
|
||||
aRv = primaryKey.SetFromJSVal(aCx, aPrimaryKey);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (primaryKey.IsUnset()) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (mDirection) {
|
||||
case NEXT:
|
||||
if (key < sortKey ||
|
||||
(key == sortKey && primaryKey <= mPrimaryKey)) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case PREV:
|
||||
if (key > sortKey ||
|
||||
(key == sortKey && primaryKey >= mPrimaryKey)) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Unknown direction type!");
|
||||
}
|
||||
|
||||
const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
|
||||
mRequest->SetLoggingSerialNumber(requestSerialNumber);
|
||||
|
||||
IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
|
||||
"database(%s).transaction(%s).objectStore(%s)."
|
||||
"index(%s).cursor(%s).continuePrimaryKey(%s, %s)",
|
||||
"IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.continuePrimaryKey()",
|
||||
IDB_LOG_ID_STRING(),
|
||||
mTransaction->LoggingSerialNumber(),
|
||||
requestSerialNumber,
|
||||
IDB_LOG_STRINGIFY(mTransaction->Database()),
|
||||
IDB_LOG_STRINGIFY(mTransaction),
|
||||
IDB_LOG_STRINGIFY(mSourceIndex->ObjectStore()),
|
||||
IDB_LOG_STRINGIFY(mSourceIndex),
|
||||
IDB_LOG_STRINGIFY(mDirection),
|
||||
IDB_LOG_STRINGIFY(key),
|
||||
IDB_LOG_STRINGIFY(primaryKey));
|
||||
|
||||
mBackgroundActor->SendContinueInternal(ContinuePrimaryKeyParams(key, primaryKey));
|
||||
|
||||
mContinueCalled = true;
|
||||
}
|
||||
|
@ -568,7 +678,7 @@ IDBCursor::Advance(uint32_t aCount, ErrorResult &aRv)
|
|||
aCount);
|
||||
}
|
||||
|
||||
mBackgroundActor->SendContinueInternal(AdvanceParams(aCount), mKey);
|
||||
mBackgroundActor->SendContinueInternal(AdvanceParams(aCount));
|
||||
|
||||
mContinueCalled = true;
|
||||
}
|
||||
|
@ -603,8 +713,6 @@ IDBCursor::Update(JSContext* aCx, JS::Handle<JS::Value> aValue,
|
|||
MOZ_ASSERT(!mKey.IsUnset());
|
||||
MOZ_ASSERT_IF(mType == Type_Index, !mPrimaryKey.IsUnset());
|
||||
|
||||
mBackgroundActor->InvalidateCachedResponses();
|
||||
|
||||
IDBObjectStore* objectStore;
|
||||
if (mType == Type_ObjectStore) {
|
||||
objectStore = mSourceObjectStore;
|
||||
|
@ -722,8 +830,6 @@ IDBCursor::Delete(JSContext* aCx, ErrorResult& aRv)
|
|||
MOZ_ASSERT(mType == Type_ObjectStore || mType == Type_Index);
|
||||
MOZ_ASSERT(!mKey.IsUnset());
|
||||
|
||||
mBackgroundActor->InvalidateCachedResponses();
|
||||
|
||||
IDBObjectStore* objectStore;
|
||||
if (mType == Type_ObjectStore) {
|
||||
objectStore = mSourceObjectStore;
|
||||
|
|
|
@ -135,8 +135,6 @@ public:
|
|||
IDBCursorDirection
|
||||
GetDirection() const;
|
||||
|
||||
bool IsContinueCalled() const { return mContinueCalled; }
|
||||
|
||||
void
|
||||
GetKey(JSContext* aCx,
|
||||
JS::MutableHandle<JS::Value> aResult,
|
||||
|
@ -155,6 +153,12 @@ public:
|
|||
void
|
||||
Continue(JSContext* aCx, JS::Handle<JS::Value> aKey, ErrorResult& aRv);
|
||||
|
||||
void
|
||||
ContinuePrimaryKey(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aKey,
|
||||
JS::Handle<JS::Value> aPrimaryKey,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void
|
||||
Advance(uint32_t aCount, ErrorResult& aRv);
|
||||
|
||||
|
|
|
@ -1702,9 +1702,10 @@ IDBObjectStore::IndexNames()
|
|||
}
|
||||
|
||||
already_AddRefed<IDBRequest>
|
||||
IDBObjectStore::Get(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aKey,
|
||||
ErrorResult& aRv)
|
||||
IDBObjectStore::GetInternal(bool aKeyOnly,
|
||||
JSContext* aCx,
|
||||
JS::Handle<JS::Value> aKey,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
|
@ -1730,9 +1731,17 @@ IDBObjectStore::Get(JSContext* aCx,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
ObjectStoreGetParams params;
|
||||
params.objectStoreId() = Id();
|
||||
keyRange->ToSerialized(params.keyRange());
|
||||
const int64_t id = Id();
|
||||
|
||||
SerializedKeyRange serializedKeyRange;
|
||||
keyRange->ToSerialized(serializedKeyRange);
|
||||
|
||||
RequestParams params;
|
||||
if (aKeyOnly) {
|
||||
params = ObjectStoreGetKeyParams(id, serializedKeyRange);
|
||||
} else {
|
||||
params = ObjectStoreGetParams(id, serializedKeyRange);
|
||||
}
|
||||
|
||||
RefPtr<IDBRequest> request = GenerateRequest(aCx, this);
|
||||
MOZ_ASSERT(request);
|
||||
|
|
|
@ -203,7 +203,24 @@ public:
|
|||
}
|
||||
|
||||
already_AddRefed<IDBRequest>
|
||||
Get(JSContext* aCx, JS::Handle<JS::Value> aKey, ErrorResult& aRv);
|
||||
Get(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aKey,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
return GetInternal(/* aKeyOnly */ false, aCx, aKey, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<IDBRequest>
|
||||
GetKey(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aKey,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
return GetInternal(/* aKeyOnly */ true, aCx, aKey, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<IDBRequest>
|
||||
Clear(JSContext* aCx, ErrorResult& aRv);
|
||||
|
@ -333,6 +350,12 @@ private:
|
|||
bool aFromCursor,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<IDBRequest>
|
||||
GetInternal(bool aKeyOnly,
|
||||
JSContext* aCx,
|
||||
JS::Handle<JS::Value> aKey,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<IDBRequest>
|
||||
GetAllInternal(bool aKeysOnly,
|
||||
JSContext* aCx,
|
||||
|
|
|
@ -26,6 +26,12 @@ struct ContinueParams
|
|||
Key key;
|
||||
};
|
||||
|
||||
struct ContinuePrimaryKeyParams
|
||||
{
|
||||
Key key;
|
||||
Key primaryKey;
|
||||
};
|
||||
|
||||
struct AdvanceParams
|
||||
{
|
||||
uint32_t count;
|
||||
|
@ -34,6 +40,7 @@ struct AdvanceParams
|
|||
union CursorRequestParams
|
||||
{
|
||||
ContinueParams;
|
||||
ContinuePrimaryKeyParams;
|
||||
AdvanceParams;
|
||||
};
|
||||
|
||||
|
@ -80,7 +87,7 @@ protocol PBackgroundIDBCursor
|
|||
parent:
|
||||
async DeleteMe();
|
||||
|
||||
async Continue(CursorRequestParams params, Key key);
|
||||
async Continue(CursorRequestParams params);
|
||||
|
||||
child:
|
||||
async __delete__();
|
||||
|
|
|
@ -36,6 +36,11 @@ struct ObjectStoreGetResponse
|
|||
SerializedStructuredCloneReadInfo cloneInfo;
|
||||
};
|
||||
|
||||
struct ObjectStoreGetKeyResponse
|
||||
{
|
||||
Key key;
|
||||
};
|
||||
|
||||
struct ObjectStoreGetAllResponse
|
||||
{
|
||||
SerializedStructuredCloneReadInfo[] cloneInfos;
|
||||
|
@ -86,6 +91,7 @@ union RequestResponse
|
|||
{
|
||||
nsresult;
|
||||
ObjectStoreGetResponse;
|
||||
ObjectStoreGetKeyResponse;
|
||||
ObjectStoreAddResponse;
|
||||
ObjectStorePutResponse;
|
||||
ObjectStoreDeleteResponse;
|
||||
|
|
|
@ -187,6 +187,12 @@ struct ObjectStoreGetParams
|
|||
SerializedKeyRange keyRange;
|
||||
};
|
||||
|
||||
struct ObjectStoreGetKeyParams
|
||||
{
|
||||
int64_t objectStoreId;
|
||||
SerializedKeyRange keyRange;
|
||||
};
|
||||
|
||||
struct ObjectStoreGetAllParams
|
||||
{
|
||||
int64_t objectStoreId;
|
||||
|
@ -260,6 +266,7 @@ union RequestParams
|
|||
ObjectStoreAddParams;
|
||||
ObjectStorePutParams;
|
||||
ObjectStoreGetParams;
|
||||
ObjectStoreGetKeyParams;
|
||||
ObjectStoreGetAllParams;
|
||||
ObjectStoreGetAllKeysParams;
|
||||
ObjectStoreDeleteParams;
|
||||
|
|
|
@ -317,6 +317,11 @@ public:
|
|||
return &mIsSuspended;
|
||||
}
|
||||
|
||||
// Switch the video decoder to BlankDecoderModule. It might takes effective
|
||||
// since a few samples later depends on how much demuxed samples are already
|
||||
// queued in the original video decoder.
|
||||
virtual void SetVideoBlankDecode(bool aIsBlankDecode) {}
|
||||
|
||||
protected:
|
||||
virtual ~MediaDecoderReader();
|
||||
|
||||
|
|
|
@ -410,4 +410,14 @@ MediaDecoderReaderWrapper::OnMetadataRead(MetadataHolder* aMetadata)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderReaderWrapper::SetVideoBlankDecode(bool aIsBlankDecode)
|
||||
{
|
||||
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
NewRunnableMethod<bool>(mReader, &MediaDecoderReader::SetVideoBlankDecode,
|
||||
aIsBlankDecode);
|
||||
mReader->OwnerThread()->Dispatch(r.forget());
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -121,6 +121,8 @@ public:
|
|||
void SetCDMProxy(CDMProxy* aProxy) { mReader->SetCDMProxy(aProxy); }
|
||||
#endif
|
||||
|
||||
void SetVideoBlankDecode(bool aIsBlankDecode);
|
||||
|
||||
private:
|
||||
~MediaDecoderReaderWrapper();
|
||||
|
||||
|
|
|
@ -1353,6 +1353,7 @@ void MediaDecoderStateMachine::VisibilityChanged()
|
|||
|
||||
if (mVideoDecodeSuspended) {
|
||||
mVideoDecodeSuspended = false;
|
||||
mReader->SetVideoBlankDecode(false);
|
||||
|
||||
if (mIsReaderSuspended) {
|
||||
return;
|
||||
|
@ -2631,7 +2632,7 @@ bool MediaDecoderStateMachine::IsStateMachineScheduled() const
|
|||
bool MediaDecoderStateMachine::IsVideoDecodeSuspended() const
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
return mVideoDecodeSuspended || mIsReaderSuspended;
|
||||
return mIsReaderSuspended;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2923,6 +2924,7 @@ MediaDecoderStateMachine::OnSuspendTimerResolved()
|
|||
DECODER_LOG("OnSuspendTimerResolved");
|
||||
mVideoDecodeSuspendTimer.CompleteRequest();
|
||||
mVideoDecodeSuspended = true;
|
||||
mReader->SetVideoBlankDecode(true);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -418,7 +418,8 @@ MediaFormatReader::EnsureDecoderCreated(TrackType aTrack)
|
|||
decoder.mInfo ? *decoder.mInfo->GetAsAudioInfo() : mInfo.mAudio,
|
||||
decoder.mTaskQueue,
|
||||
decoder.mCallback.get(),
|
||||
mCrashHelper
|
||||
mCrashHelper,
|
||||
decoder.mIsBlankDecode
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
@ -432,7 +433,8 @@ MediaFormatReader::EnsureDecoderCreated(TrackType aTrack)
|
|||
decoder.mCallback.get(),
|
||||
mLayersBackendType,
|
||||
GetImageContainer(),
|
||||
mCrashHelper
|
||||
mCrashHelper,
|
||||
decoder.mIsBlankDecode
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
@ -2060,4 +2062,32 @@ MediaFormatReader::GetMozDebugReaderData(nsAString& aString)
|
|||
aString += NS_ConvertUTF8toUTF16(result);
|
||||
}
|
||||
|
||||
void
|
||||
MediaFormatReader::SetVideoBlankDecode(bool aIsBlankDecode)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
return SetBlankDecode(TrackType::kVideoTrack, aIsBlankDecode);
|
||||
}
|
||||
|
||||
void
|
||||
MediaFormatReader::SetBlankDecode(TrackType aTrack, bool aIsBlankDecode)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
auto& decoder = GetDecoderData(aTrack);
|
||||
|
||||
LOG("%s, decoder.mIsBlankDecode = %d => aIsBlankDecode = %d",
|
||||
TrackTypeToStr(aTrack), decoder.mIsBlankDecode, aIsBlankDecode);
|
||||
|
||||
if (decoder.mIsBlankDecode == aIsBlankDecode) {
|
||||
return;
|
||||
}
|
||||
|
||||
decoder.mIsBlankDecode = aIsBlankDecode;
|
||||
decoder.Flush();
|
||||
decoder.ShutdownDecoder();
|
||||
NotifyDecodingRequested(TrackInfo::kVideoTrack); // Calls ScheduleUpdate().
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -101,6 +101,8 @@ public:
|
|||
// Used for debugging purposes.
|
||||
void GetMozDebugReaderData(nsAString& aString);
|
||||
|
||||
void SetVideoBlankDecode(bool aIsBlankDecode) override;
|
||||
|
||||
private:
|
||||
|
||||
bool HasVideo() { return mVideo.mTrackDemuxer; }
|
||||
|
@ -253,6 +255,7 @@ private:
|
|||
, mSizeOfQueue(0)
|
||||
, mIsHardwareAccelerated(false)
|
||||
, mLastStreamSourceID(UINT32_MAX)
|
||||
, mIsBlankDecode(false)
|
||||
{}
|
||||
|
||||
MediaFormatReader* mOwner;
|
||||
|
@ -427,6 +430,9 @@ private:
|
|||
Maybe<media::TimeUnit> mLastTimeRangesEnd;
|
||||
RefPtr<SharedTrackInfo> mInfo;
|
||||
Maybe<media::TimeUnit> mFirstDemuxedSampleTime;
|
||||
// Use BlankDecoderModule or not.
|
||||
bool mIsBlankDecode;
|
||||
|
||||
};
|
||||
|
||||
class DecoderDataWithPromise : public DecoderData {
|
||||
|
@ -571,6 +577,8 @@ private:
|
|||
RefPtr<CDMProxy> mCDMProxy;
|
||||
#endif
|
||||
RefPtr<GMPCrashHelper> mCrashHelper;
|
||||
|
||||
void SetBlankDecode(TrackType aTrack, bool aIsBlankDecode);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -81,6 +81,7 @@ PDMFactory::PDMFactory()
|
|||
{
|
||||
EnsureInit();
|
||||
CreatePDMs();
|
||||
CreateBlankPDM();
|
||||
}
|
||||
|
||||
PDMFactory::~PDMFactory()
|
||||
|
@ -120,6 +121,11 @@ PDMFactory::EnsureInit() const
|
|||
already_AddRefed<MediaDataDecoder>
|
||||
PDMFactory::CreateDecoder(const CreateDecoderParams& aParams)
|
||||
{
|
||||
if (aParams.mUseBlankDecoder) {
|
||||
MOZ_ASSERT(mBlankPDM);
|
||||
return CreateDecoderWithPDM(mBlankPDM, aParams);
|
||||
}
|
||||
|
||||
const TrackInfo& config = aParams.mConfig;
|
||||
bool isEncrypted = mEMEPDM && config.mCrypto.mValid;
|
||||
|
||||
|
@ -287,6 +293,13 @@ PDMFactory::CreatePDMs()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
PDMFactory::CreateBlankPDM()
|
||||
{
|
||||
mBlankPDM = CreateBlankDecoderModule();
|
||||
MOZ_ASSERT(mBlankPDM && NS_SUCCEEDED(mBlankPDM->Startup()));
|
||||
}
|
||||
|
||||
bool
|
||||
PDMFactory::StartupPDM(PlatformDecoderModule* aPDM)
|
||||
{
|
||||
|
|
|
@ -47,6 +47,7 @@ public:
|
|||
private:
|
||||
virtual ~PDMFactory();
|
||||
void CreatePDMs();
|
||||
void CreateBlankPDM();
|
||||
// Startup the provided PDM and add it to our list if successful.
|
||||
bool StartupPDM(PlatformDecoderModule* aPDM);
|
||||
// Returns the first PDM in our list supporting the mimetype.
|
||||
|
@ -60,6 +61,7 @@ private:
|
|||
|
||||
nsTArray<RefPtr<PlatformDecoderModule>> mCurrentPDMs;
|
||||
RefPtr<PlatformDecoderModule> mEMEPDM;
|
||||
RefPtr<PlatformDecoderModule> mBlankPDM;
|
||||
|
||||
bool mWMFFailedToLoad = false;
|
||||
bool mFFmpegFailedToLoad = false;
|
||||
|
|
|
@ -64,6 +64,7 @@ struct CreateDecoderParams {
|
|||
layers::ImageContainer* mImageContainer = nullptr;
|
||||
layers::LayersBackend mLayersBackend = layers::LayersBackend::LAYERS_NONE;
|
||||
RefPtr<GMPCrashHelper> mCrashHelper;
|
||||
bool mUseBlankDecoder = false;
|
||||
|
||||
private:
|
||||
void Set(TaskQueue* aTaskQueue) { mTaskQueue = aTaskQueue; }
|
||||
|
@ -72,6 +73,7 @@ private:
|
|||
void Set(layers::ImageContainer* aImageContainer) { mImageContainer = aImageContainer; }
|
||||
void Set(layers::LayersBackend aLayersBackend) { mLayersBackend = aLayersBackend; }
|
||||
void Set(GMPCrashHelper* aCrashHelper) { mCrashHelper = aCrashHelper; }
|
||||
void Set(bool aUseBlankDecoder) { mUseBlankDecoder = aUseBlankDecoder; }
|
||||
template <typename T1, typename T2, typename... Ts>
|
||||
void Set(T1&& a1, T2&& a2, Ts&&... args)
|
||||
{
|
||||
|
|
|
@ -11,9 +11,12 @@
|
|||
#include "mozilla/mozalloc.h" // for operator new, and new (fallible)
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/TaskQueue.h"
|
||||
#include "mp4_demuxer/H264.h"
|
||||
#include "MP4Decoder.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsRect.h"
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "ReorderQueue.h"
|
||||
#include "TimeUnits.h"
|
||||
#include "VideoUtils.h"
|
||||
|
||||
|
@ -29,6 +32,10 @@ public:
|
|||
const CreateDecoderParams& aParams)
|
||||
: mCreator(aCreator)
|
||||
, mCallback(aParams.mCallback)
|
||||
, mMaxRefFrames(aParams.mConfig.GetType() == TrackInfo::kVideoTrack &&
|
||||
MP4Decoder::IsH264(aParams.mConfig.mMimeType)
|
||||
? mp4_demuxer::H264::ComputeMaxRefFrames(aParams.VideoConfig().mExtraData)
|
||||
: 0)
|
||||
, mType(aParams.mConfig.GetType())
|
||||
{
|
||||
}
|
||||
|
@ -47,19 +54,25 @@ public:
|
|||
mCreator->Create(media::TimeUnit::FromMicroseconds(aSample->mTime),
|
||||
media::TimeUnit::FromMicroseconds(aSample->mDuration),
|
||||
aSample->mOffset);
|
||||
if (!data) {
|
||||
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
|
||||
} else {
|
||||
mCallback->Output(data);
|
||||
|
||||
OutputFrame(data);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult Flush() override
|
||||
{
|
||||
mReorderQueue.Clear();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult Drain() override
|
||||
{
|
||||
while (!mReorderQueue.IsEmpty()) {
|
||||
mCallback->Output(mReorderQueue.Pop().get());
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult Flush() override {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult Drain() override {
|
||||
mCallback->DrainComplete();
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -69,9 +82,32 @@ public:
|
|||
return "blank media data decoder";
|
||||
}
|
||||
|
||||
private:
|
||||
void OutputFrame(MediaData* aData)
|
||||
{
|
||||
if (!aData) {
|
||||
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Frames come out in DTS order but we need to output them in PTS order.
|
||||
mReorderQueue.Push(aData);
|
||||
|
||||
while (mReorderQueue.Length() > mMaxRefFrames) {
|
||||
mCallback->Output(mReorderQueue.Pop().get());
|
||||
}
|
||||
|
||||
if (mReorderQueue.Length() <= mMaxRefFrames) {
|
||||
mCallback->InputExhausted();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
nsAutoPtr<BlankMediaDataCreator> mCreator;
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
const uint32_t mMaxRefFrames;
|
||||
ReorderQueue mReorderQueue;
|
||||
TrackInfo::TrackType mType;
|
||||
};
|
||||
|
||||
|
@ -93,10 +129,10 @@ public:
|
|||
{
|
||||
// Create a fake YUV buffer in a 420 format. That is, an 8bpp Y plane,
|
||||
// with a U and V plane that are half the size of the Y plane, i.e 8 bit,
|
||||
// 2x2 subsampled. Have the data pointers of each frame point to the
|
||||
// first plane, they'll always be zero'd memory anyway.
|
||||
auto frame = MakeUnique<uint8_t[]>(mFrameWidth * mFrameHeight);
|
||||
memset(frame.get(), 0, mFrameWidth * mFrameHeight);
|
||||
// 2x2 subsampled.
|
||||
const int sizeY = mFrameWidth * mFrameHeight;
|
||||
const int sizeCbCr = ((mFrameWidth + 1) / 2) * ((mFrameHeight + 1) / 2);
|
||||
auto frame = MakeUnique<uint8_t[]>(sizeY + sizeCbCr);
|
||||
VideoData::YCbCrBuffer buffer;
|
||||
|
||||
// Y plane.
|
||||
|
@ -108,7 +144,7 @@ public:
|
|||
buffer.mPlanes[0].mSkip = 0;
|
||||
|
||||
// Cb plane.
|
||||
buffer.mPlanes[1].mData = frame.get();
|
||||
buffer.mPlanes[1].mData = frame.get() + sizeY;
|
||||
buffer.mPlanes[1].mStride = mFrameWidth / 2;
|
||||
buffer.mPlanes[1].mHeight = mFrameHeight / 2;
|
||||
buffer.mPlanes[1].mWidth = mFrameWidth / 2;
|
||||
|
@ -116,13 +152,17 @@ public:
|
|||
buffer.mPlanes[1].mSkip = 0;
|
||||
|
||||
// Cr plane.
|
||||
buffer.mPlanes[2].mData = frame.get();
|
||||
buffer.mPlanes[2].mData = frame.get() + sizeY;
|
||||
buffer.mPlanes[2].mStride = mFrameWidth / 2;
|
||||
buffer.mPlanes[2].mHeight = mFrameHeight / 2;
|
||||
buffer.mPlanes[2].mWidth = mFrameWidth / 2;
|
||||
buffer.mPlanes[2].mOffset = 0;
|
||||
buffer.mPlanes[2].mSkip = 0;
|
||||
|
||||
// Set to color white.
|
||||
memset(buffer.mPlanes[0].mData, 255, sizeY);
|
||||
memset(buffer.mPlanes[1].mData, 128, sizeCbCr);
|
||||
|
||||
return VideoData::Create(mInfo,
|
||||
mImageContainer,
|
||||
nullptr,
|
||||
|
@ -134,6 +174,7 @@ public:
|
|||
aDTS.ToMicroseconds(),
|
||||
mPicture);
|
||||
}
|
||||
|
||||
private:
|
||||
VideoInfo mInfo;
|
||||
gfx::IntRect mPicture;
|
||||
|
@ -142,7 +183,6 @@ private:
|
|||
RefPtr<layers::ImageContainer> mImageContainer;
|
||||
};
|
||||
|
||||
|
||||
class BlankAudioDataCreator {
|
||||
public:
|
||||
BlankAudioDataCreator(uint32_t aChannelCount, uint32_t aSampleRate)
|
||||
|
@ -229,7 +269,11 @@ public:
|
|||
ConversionRequired
|
||||
DecoderNeedsConversion(const TrackInfo& aConfig) const override
|
||||
{
|
||||
return kNeedNone;
|
||||
if (aConfig.IsVideo() && MP4Decoder::IsH264(aConfig.mMimeType)) {
|
||||
return kNeedAVCC;
|
||||
} else {
|
||||
return kNeedNone;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
#include "AppleUtils.h"
|
||||
#include "AppleVTDecoder.h"
|
||||
#include "AppleVTLinker.h"
|
||||
#include "mp4_demuxer/H264.h"
|
||||
#include "MediaData.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mp4_demuxer/H264.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/Logging.h"
|
||||
|
@ -24,22 +24,6 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
static uint32_t ComputeMaxRefFrames(const MediaByteBuffer* aExtraData)
|
||||
{
|
||||
uint32_t maxRefFrames = 4;
|
||||
// Retrieve video dimensions from H264 SPS NAL.
|
||||
mp4_demuxer::SPSData spsdata;
|
||||
if (mp4_demuxer::H264::DecodeSPSFromExtraData(aExtraData, spsdata)) {
|
||||
// max_num_ref_frames determines the size of the sliding window
|
||||
// we need to queue that many frames in order to guarantee proper
|
||||
// pts frames ordering. Use a minimum of 4 to ensure proper playback of
|
||||
// non compliant videos.
|
||||
maxRefFrames =
|
||||
std::min(std::max(maxRefFrames, spsdata.max_num_ref_frames + 1), 16u);
|
||||
}
|
||||
return maxRefFrames;
|
||||
}
|
||||
|
||||
AppleVTDecoder::AppleVTDecoder(const VideoInfo& aConfig,
|
||||
TaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback,
|
||||
|
@ -52,7 +36,7 @@ AppleVTDecoder::AppleVTDecoder(const VideoInfo& aConfig,
|
|||
, mDisplayHeight(aConfig.mDisplay.height)
|
||||
, mQueuedSamples(0)
|
||||
, mTaskQueue(aTaskQueue)
|
||||
, mMaxRefFrames(ComputeMaxRefFrames(aConfig.mExtraData))
|
||||
, mMaxRefFrames(mp4_demuxer::H264::ComputeMaxRefFrames(aConfig.mExtraData))
|
||||
, mImageContainer(aImageContainer)
|
||||
, mInputIncoming(0)
|
||||
, mIsShutDown(false)
|
||||
|
|
|
@ -786,9 +786,12 @@ function AudioStreamHelper() {
|
|||
|
||||
AudioStreamHelper.prototype = {
|
||||
checkAudio: function(stream, analyser, fun) {
|
||||
/*
|
||||
analyser.enableDebugCanvas();
|
||||
return analyser.waitForAnalysisSuccess(fun)
|
||||
.then(() => analyser.disableDebugCanvas());
|
||||
*/
|
||||
return analyser.waitForAnalysisSuccess(fun);
|
||||
},
|
||||
|
||||
checkAudioFlowing: function(stream) {
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#include "ControllerConnectionCollection.h"
|
||||
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "nsIPresentationService.h"
|
||||
#include "PresentationConnection.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/* static */
|
||||
StaticAutoPtr<ControllerConnectionCollection>
|
||||
ControllerConnectionCollection::sSingleton;
|
||||
|
||||
/* static */ ControllerConnectionCollection*
|
||||
ControllerConnectionCollection::GetSingleton()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!sSingleton) {
|
||||
sSingleton = new ControllerConnectionCollection();
|
||||
ClearOnShutdown(&sSingleton);
|
||||
}
|
||||
|
||||
return sSingleton;
|
||||
}
|
||||
|
||||
ControllerConnectionCollection::ControllerConnectionCollection()
|
||||
{
|
||||
MOZ_COUNT_CTOR(ControllerConnectionCollection);
|
||||
}
|
||||
|
||||
ControllerConnectionCollection::~ControllerConnectionCollection()
|
||||
{
|
||||
MOZ_COUNT_DTOR(ControllerConnectionCollection);
|
||||
}
|
||||
|
||||
void
|
||||
ControllerConnectionCollection::AddConnection(
|
||||
PresentationConnection* aConnection,
|
||||
const uint8_t aRole)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (aRole != nsIPresentationService::ROLE_CONTROLLER) {
|
||||
MOZ_ASSERT(false, "This is allowed only to be called at controller side.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aConnection) {
|
||||
return;
|
||||
}
|
||||
|
||||
WeakPtr<PresentationConnection> connection = aConnection;
|
||||
if (mConnections.Contains(connection)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mConnections.AppendElement(connection);
|
||||
}
|
||||
|
||||
void
|
||||
ControllerConnectionCollection::RemoveConnection(
|
||||
PresentationConnection* aConnection,
|
||||
const uint8_t aRole)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (aRole != nsIPresentationService::ROLE_CONTROLLER) {
|
||||
MOZ_ASSERT(false, "This is allowed only to be called at controller side.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aConnection) {
|
||||
return;
|
||||
}
|
||||
|
||||
WeakPtr<PresentationConnection> connection = aConnection;
|
||||
mConnections.RemoveElement(connection);
|
||||
}
|
||||
|
||||
already_AddRefed<PresentationConnection>
|
||||
ControllerConnectionCollection::FindConnection(
|
||||
uint64_t aWindowId,
|
||||
const nsAString& aId,
|
||||
const uint8_t aRole)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (aRole != nsIPresentationService::ROLE_CONTROLLER) {
|
||||
MOZ_ASSERT(false, "This is allowed only to be called at controller side.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Loop backwards to allow removing elements in the loop.
|
||||
for (int i = mConnections.Length() - 1; i >= 0; --i) {
|
||||
WeakPtr<PresentationConnection> connection = mConnections[i];
|
||||
if (!connection) {
|
||||
// The connection was destroyed. Remove it from the list.
|
||||
mConnections.RemoveElementAt(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (connection->Equals(aWindowId, aId)) {
|
||||
RefPtr<PresentationConnection> matchedConnection = connection.get();
|
||||
return matchedConnection.forget();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,49 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_ControllerConnectionCollection_h
|
||||
#define mozilla_dom_ControllerConnectionCollection_h
|
||||
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class PresentationConnection;
|
||||
|
||||
class ControllerConnectionCollection final
|
||||
{
|
||||
public:
|
||||
static ControllerConnectionCollection* GetSingleton();
|
||||
|
||||
void AddConnection(PresentationConnection* aConnection,
|
||||
const uint8_t aRole);
|
||||
|
||||
void RemoveConnection(PresentationConnection* aConnection,
|
||||
const uint8_t aRole);
|
||||
|
||||
already_AddRefed<PresentationConnection>
|
||||
FindConnection(uint64_t aWindowId,
|
||||
const nsAString& aId,
|
||||
const uint8_t aRole);
|
||||
|
||||
private:
|
||||
friend class StaticAutoPtr<ControllerConnectionCollection>;
|
||||
|
||||
ControllerConnectionCollection();
|
||||
virtual ~ControllerConnectionCollection();
|
||||
|
||||
static StaticAutoPtr<ControllerConnectionCollection> sSingleton;
|
||||
nsTArray<WeakPtr<PresentationConnection>> mConnections;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_ControllerConnectionCollection_h
|
|
@ -13,6 +13,7 @@
|
|||
#include "PresentationCallbacks.h"
|
||||
#include "PresentationRequest.h"
|
||||
#include "PresentationConnection.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -28,6 +29,7 @@ PresentationRequesterCallback::PresentationRequesterCallback(PresentationRequest
|
|||
const nsAString& aSessionId,
|
||||
Promise* aPromise)
|
||||
: mRequest(aRequest)
|
||||
, mUrl(aUrl)
|
||||
, mSessionId(aSessionId)
|
||||
, mPromise(aPromise)
|
||||
{
|
||||
|
@ -46,10 +48,8 @@ PresentationRequesterCallback::NotifySuccess()
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// At the sender side, this function must get called after the transport
|
||||
// channel is ready. So we simply set the connection state as connected.
|
||||
RefPtr<PresentationConnection> connection =
|
||||
PresentationConnection::Create(mRequest->GetOwner(), mSessionId,
|
||||
PresentationConnection::Create(mRequest->GetOwner(), mSessionId, mUrl,
|
||||
nsIPresentationService::ROLE_CONTROLLER);
|
||||
if (NS_WARN_IF(!connection)) {
|
||||
mPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
|
||||
|
@ -74,6 +74,74 @@ PresentationRequesterCallback::NotifyError(nsresult aError)
|
|||
* Implementation of PresentationRequesterCallback
|
||||
*/
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(PresentationReconnectCallback,
|
||||
PresentationRequesterCallback)
|
||||
|
||||
PresentationReconnectCallback::PresentationReconnectCallback(
|
||||
PresentationRequest* aRequest,
|
||||
const nsAString& aUrl,
|
||||
const nsAString& aSessionId,
|
||||
Promise* aPromise,
|
||||
PresentationConnection* aConnection)
|
||||
: PresentationRequesterCallback(aRequest, aUrl, aSessionId, aPromise)
|
||||
, mConnection(aConnection)
|
||||
{
|
||||
}
|
||||
|
||||
PresentationReconnectCallback::~PresentationReconnectCallback()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationReconnectCallback::NotifySuccess()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsCOMPtr<nsIPresentationService> service =
|
||||
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
|
||||
if (NS_WARN_IF(!service)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
// We found a matched connection with the same window ID, URL, and
|
||||
// the session ID. Resolve the promise with this connection and dispatch
|
||||
// the event.
|
||||
if (mConnection) {
|
||||
mPromise->MaybeResolve(mConnection);
|
||||
rv = mRequest->DispatchConnectionAvailableEvent(mConnection);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
// Use |PresentationRequesterCallback::NotifySuccess| to create a new
|
||||
// connection since we don't find one that can be reused.
|
||||
rv = PresentationRequesterCallback::NotifySuccess();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = service->UpdateWindowIdBySessionId(mSessionId,
|
||||
mRequest->GetOwner()->WindowID());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
nsString sessionId = nsString(mSessionId);
|
||||
return NS_DispatchToMainThread(
|
||||
NS_NewRunnableFunction([sessionId, service]() -> void {
|
||||
service->BuildTransport(sessionId,
|
||||
nsIPresentationService::ROLE_CONTROLLER);
|
||||
}));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationReconnectCallback::NotifyError(nsresult aError)
|
||||
{
|
||||
return PresentationRequesterCallback::NotifyError(aError);
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(PresentationResponderLoadingCallback,
|
||||
nsIWebProgressListener,
|
||||
nsISupportsWeakReference)
|
||||
|
|
|
@ -20,10 +20,11 @@ class nsIWebProgress;
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class PresentationConnection;
|
||||
class PresentationRequest;
|
||||
class Promise;
|
||||
|
||||
class PresentationRequesterCallback final : public nsIPresentationServiceCallback
|
||||
class PresentationRequesterCallback : public nsIPresentationServiceCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
@ -34,14 +35,33 @@ public:
|
|||
const nsAString& aSessionId,
|
||||
Promise* aPromise);
|
||||
|
||||
private:
|
||||
~PresentationRequesterCallback();
|
||||
protected:
|
||||
virtual ~PresentationRequesterCallback();
|
||||
|
||||
RefPtr<PresentationRequest> mRequest;
|
||||
nsString mUrl;
|
||||
nsString mSessionId;
|
||||
RefPtr<Promise> mPromise;
|
||||
};
|
||||
|
||||
class PresentationReconnectCallback final : public PresentationRequesterCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIPRESENTATIONSERVICECALLBACK
|
||||
|
||||
PresentationReconnectCallback(PresentationRequest* aRequest,
|
||||
const nsAString& aUrl,
|
||||
const nsAString& aSessionId,
|
||||
Promise* aPromise,
|
||||
PresentationConnection* aConnection);
|
||||
|
||||
private:
|
||||
virtual ~PresentationReconnectCallback();
|
||||
|
||||
RefPtr<PresentationConnection> mConnection;
|
||||
};
|
||||
|
||||
class PresentationResponderLoadingCallback final : public nsIWebProgressListener
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "PresentationConnection.h"
|
||||
|
||||
#include "ControllerConnectionCollection.h"
|
||||
#include "mozilla/AsyncEventDispatcher.h"
|
||||
#include "mozilla/dom/DOMException.h"
|
||||
#include "mozilla/dom/MessageEvent.h"
|
||||
|
@ -43,10 +44,12 @@ NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
|||
|
||||
PresentationConnection::PresentationConnection(nsPIDOMWindowInner* aWindow,
|
||||
const nsAString& aId,
|
||||
const nsAString& aUrl,
|
||||
const uint8_t aRole,
|
||||
PresentationConnectionList* aList)
|
||||
: DOMEventTargetHelper(aWindow)
|
||||
, mId(aId)
|
||||
, mUrl(aUrl)
|
||||
, mState(PresentationConnectionState::Connecting)
|
||||
, mOwningConnectionList(aList)
|
||||
{
|
||||
|
@ -62,14 +65,24 @@ PresentationConnection::PresentationConnection(nsPIDOMWindowInner* aWindow,
|
|||
/* static */ already_AddRefed<PresentationConnection>
|
||||
PresentationConnection::Create(nsPIDOMWindowInner* aWindow,
|
||||
const nsAString& aId,
|
||||
const nsAString& aUrl,
|
||||
const uint8_t aRole,
|
||||
PresentationConnectionList* aList)
|
||||
{
|
||||
MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER ||
|
||||
aRole == nsIPresentationService::ROLE_RECEIVER);
|
||||
RefPtr<PresentationConnection> connection =
|
||||
new PresentationConnection(aWindow, aId, aRole, aList);
|
||||
return NS_WARN_IF(!connection->Init()) ? nullptr : connection.forget();
|
||||
new PresentationConnection(aWindow, aId, aUrl, aRole, aList);
|
||||
if (NS_WARN_IF(!connection->Init())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aRole == nsIPresentationService::ROLE_CONTROLLER) {
|
||||
ControllerConnectionCollection::GetSingleton()->AddConnection(connection,
|
||||
aRole);
|
||||
}
|
||||
|
||||
return connection.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -112,6 +125,11 @@ PresentationConnection::Shutdown()
|
|||
|
||||
rv = RemoveFromLoadGroup();
|
||||
NS_WARN_IF(NS_FAILED(rv));
|
||||
|
||||
if (mRole == nsIPresentationService::ROLE_CONTROLLER) {
|
||||
ControllerConnectionCollection::GetSingleton()->RemoveConnection(this,
|
||||
mRole);
|
||||
}
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
|
@ -134,6 +152,12 @@ PresentationConnection::GetId(nsAString& aId) const
|
|||
aId = mId;
|
||||
}
|
||||
|
||||
void
|
||||
PresentationConnection::GetUrl(nsAString& aUrl) const
|
||||
{
|
||||
aUrl = mUrl;
|
||||
}
|
||||
|
||||
PresentationConnectionState
|
||||
PresentationConnection::State() const
|
||||
{
|
||||
|
@ -203,6 +227,15 @@ PresentationConnection::Terminate(ErrorResult& aRv)
|
|||
NS_WARN_IF(NS_FAILED(service->TerminateSession(mId, mRole)));
|
||||
}
|
||||
|
||||
bool
|
||||
PresentationConnection::Equals(uint64_t aWindowId,
|
||||
const nsAString& aId)
|
||||
{
|
||||
return GetOwner() &&
|
||||
aWindowId == GetOwner()->WindowID() &&
|
||||
mId.Equals(aId);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationConnection::NotifyStateChange(const nsAString& aSessionId,
|
||||
uint16_t aState,
|
||||
|
@ -252,6 +285,8 @@ nsresult
|
|||
PresentationConnection::ProcessStateChanged(nsresult aReason)
|
||||
{
|
||||
switch (mState) {
|
||||
case PresentationConnectionState::Connecting:
|
||||
return NS_OK;
|
||||
case PresentationConnectionState::Connected: {
|
||||
RefPtr<AsyncEventDispatcher> asyncDispatcher =
|
||||
new AsyncEventDispatcher(this, NS_LITERAL_STRING("connect"), false);
|
||||
|
@ -333,6 +368,14 @@ PresentationConnection::NotifyMessage(const nsAString& aSessionId,
|
|||
return DispatchMessageEvent(jsData);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationConnection::NotifyReplaced()
|
||||
{
|
||||
return NotifyStateChange(mId,
|
||||
nsIPresentationSessionListener::STATE_CLOSED,
|
||||
NS_OK);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationConnection::DispatchConnectionClosedEvent(
|
||||
PresentationConnectionClosedReason aReason,
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#define mozilla_dom_PresentationConnection_h
|
||||
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "mozilla/dom/PresentationConnectionBinding.h"
|
||||
#include "mozilla/dom/PresentationConnectionClosedEventBinding.h"
|
||||
#include "nsIPresentationListener.h"
|
||||
|
@ -22,6 +23,7 @@ class PresentationConnectionList;
|
|||
class PresentationConnection final : public DOMEventTargetHelper
|
||||
, public nsIPresentationSessionListener
|
||||
, public nsIRequest
|
||||
, public SupportsWeakPtr<PresentationConnection>
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
@ -29,10 +31,12 @@ public:
|
|||
DOMEventTargetHelper)
|
||||
NS_DECL_NSIPRESENTATIONSESSIONLISTENER
|
||||
NS_DECL_NSIREQUEST
|
||||
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(PresentationConnection)
|
||||
|
||||
static already_AddRefed<PresentationConnection>
|
||||
Create(nsPIDOMWindowInner* aWindow,
|
||||
const nsAString& aId,
|
||||
const nsAString& aUrl,
|
||||
const uint8_t aRole,
|
||||
PresentationConnectionList* aList = nullptr);
|
||||
|
||||
|
@ -44,6 +48,8 @@ public:
|
|||
// WebIDL (public APIs)
|
||||
void GetId(nsAString& aId) const;
|
||||
|
||||
void GetUrl(nsAString& aUrl) const;
|
||||
|
||||
PresentationConnectionState State() const;
|
||||
|
||||
void Send(const nsAString& aData,
|
||||
|
@ -53,6 +59,9 @@ public:
|
|||
|
||||
void Terminate(ErrorResult& aRv);
|
||||
|
||||
bool
|
||||
Equals(uint64_t aWindowId, const nsAString& aId);
|
||||
|
||||
IMPL_EVENT_HANDLER(connect);
|
||||
IMPL_EVENT_HANDLER(close);
|
||||
IMPL_EVENT_HANDLER(terminate);
|
||||
|
@ -61,6 +70,7 @@ public:
|
|||
private:
|
||||
PresentationConnection(nsPIDOMWindowInner* aWindow,
|
||||
const nsAString& aId,
|
||||
const nsAString& aUrl,
|
||||
const uint8_t aRole,
|
||||
PresentationConnectionList* aList);
|
||||
|
||||
|
@ -84,6 +94,7 @@ private:
|
|||
nsresult RemoveFromLoadGroup();
|
||||
|
||||
nsString mId;
|
||||
nsString mUrl;
|
||||
uint8_t mRole;
|
||||
PresentationConnectionState mState;
|
||||
RefPtr<PresentationConnectionList> mOwningConnectionList;
|
||||
|
|
|
@ -262,6 +262,27 @@ PresentationDeviceManager::OnTerminateRequest(nsIPresentationDevice* aDevice,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationDeviceManager::OnReconnectRequest(nsIPresentationDevice* aDevice,
|
||||
const nsAString& aUrl,
|
||||
const nsAString& aPresentationId,
|
||||
nsIPresentationControlChannel* aControlChannel)
|
||||
{
|
||||
NS_ENSURE_ARG(aDevice);
|
||||
NS_ENSURE_ARG(aControlChannel);
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
|
||||
|
||||
RefPtr<PresentationSessionRequest> request =
|
||||
new PresentationSessionRequest(aDevice, aUrl, aPresentationId, aControlChannel);
|
||||
obs->NotifyObservers(request,
|
||||
PRESENTATION_RECONNECT_REQUEST_TOPIC,
|
||||
nullptr);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIObserver
|
||||
NS_IMETHODIMP
|
||||
PresentationDeviceManager::Observe(nsISupports *aSubject,
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "mozilla/dom/PresentationReceiverBinding.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIPresentationService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
@ -56,7 +57,11 @@ PresentationReceiver::Init()
|
|||
}
|
||||
mWindowId = mOwner->WindowID();
|
||||
|
||||
return true;
|
||||
nsCOMPtr<nsIDocShell> docShell = mOwner->GetDocShell();
|
||||
MOZ_ASSERT(docShell);
|
||||
|
||||
nsContentUtils::GetPresentationURL(docShell, mUrl);
|
||||
return !mUrl.IsEmpty();
|
||||
}
|
||||
|
||||
void PresentationReceiver::Shutdown()
|
||||
|
@ -96,7 +101,7 @@ PresentationReceiver::NotifySessionConnect(uint64_t aWindowId,
|
|||
}
|
||||
|
||||
RefPtr<PresentationConnection> connection =
|
||||
PresentationConnection::Create(mOwner, aSessionId,
|
||||
PresentationConnection::Create(mOwner, aSessionId, mUrl,
|
||||
nsIPresentationService::ROLE_RECEIVER,
|
||||
mConnectionList);
|
||||
if (NS_WARN_IF(!connection)) {
|
||||
|
|
|
@ -54,6 +54,7 @@ private:
|
|||
uint64_t mWindowId;
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> mOwner;
|
||||
nsString mUrl;
|
||||
RefPtr<Promise> mGetConnectionListPromise;
|
||||
RefPtr<PresentationConnectionList> mConnectionList;
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "PresentationRequest.h"
|
||||
|
||||
#include "ControllerConnectionCollection.h"
|
||||
#include "mozilla/dom/PresentationRequestBinding.h"
|
||||
#include "mozilla/dom/PresentationConnectionAvailableEvent.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
|
@ -152,6 +153,118 @@ PresentationRequest::StartWithDevice(const nsAString& aDeviceId,
|
|||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
PresentationRequest::Reconnect(const nsAString& aPresentationId,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
// TODO: Before starting to reconnect, we have to run the prohibits
|
||||
// mixed security contexts algorithm first. This will be implemented
|
||||
// in bug 1254488.
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
|
||||
if (NS_WARN_IF(!global)) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = GetOwner()->GetExtantDoc();
|
||||
if (NS_WARN_IF(!doc)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (doc->GetSandboxFlags() & SANDBOXED_PRESENTATION) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsString presentationId = nsString(aPresentationId);
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
NewRunnableMethod<nsString, RefPtr<Promise>>(
|
||||
this,
|
||||
&PresentationRequest::FindOrCreatePresentationConnection,
|
||||
presentationId,
|
||||
promise);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(r)))) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
|
||||
}
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
void
|
||||
PresentationRequest::FindOrCreatePresentationConnection(
|
||||
const nsAString& aPresentationId,
|
||||
Promise* aPromise)
|
||||
{
|
||||
MOZ_ASSERT(aPromise);
|
||||
|
||||
if (NS_WARN_IF(!GetOwner())) {
|
||||
aPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<PresentationConnection> connection =
|
||||
ControllerConnectionCollection::GetSingleton()->FindConnection(
|
||||
GetOwner()->WindowID(),
|
||||
aPresentationId,
|
||||
nsIPresentationService::ROLE_CONTROLLER);
|
||||
|
||||
if (connection) {
|
||||
nsAutoString url;
|
||||
connection->GetUrl(url);
|
||||
if (url.Equals(mUrl)) {
|
||||
switch (connection->State()) {
|
||||
case PresentationConnectionState::Closed:
|
||||
// We found the matched connection.
|
||||
break;
|
||||
case PresentationConnectionState::Connecting:
|
||||
case PresentationConnectionState::Connected:
|
||||
aPromise->MaybeResolve(connection);
|
||||
return;
|
||||
case PresentationConnectionState::Terminated:
|
||||
// A terminated connection cannot be reused.
|
||||
connection = nullptr;
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unknown presentation session state.");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
connection = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPresentationService> service =
|
||||
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
|
||||
if(NS_WARN_IF(!service)) {
|
||||
aPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPresentationServiceCallback> callback =
|
||||
new PresentationReconnectCallback(this,
|
||||
mUrl,
|
||||
aPresentationId,
|
||||
aPromise,
|
||||
connection);
|
||||
|
||||
nsresult rv =
|
||||
service->ReconnectSession(mUrl,
|
||||
aPresentationId,
|
||||
nsIPresentationService::ROLE_CONTROLLER,
|
||||
callback);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
PresentationRequest::GetAvailability(ErrorResult& aRv)
|
||||
{
|
||||
|
|
|
@ -36,6 +36,9 @@ public:
|
|||
already_AddRefed<Promise> StartWithDevice(const nsAString& aDeviceId,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise> Reconnect(const nsAString& aPresentationId,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise> GetAvailability(ErrorResult& aRv);
|
||||
|
||||
IMPL_EVENT_HANDLER(connectionavailable);
|
||||
|
@ -50,6 +53,9 @@ private:
|
|||
|
||||
bool Init();
|
||||
|
||||
void FindOrCreatePresentationConnection(const nsAString& aPresentationId,
|
||||
Promise* aPromise);
|
||||
|
||||
nsString mUrl;
|
||||
RefPtr<PresentationAvailability> mAvailability;
|
||||
};
|
||||
|
|
|
@ -223,6 +223,10 @@ PresentationService::Init()
|
|||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
rv = obs->AddObserver(this, PRESENTATION_RECONNECT_REQUEST_TOPIC, false);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPresentationDeviceManager> deviceManager =
|
||||
do_GetService(PRESENTATION_DEVICE_MANAGER_CONTRACTID);
|
||||
|
@ -258,6 +262,13 @@ PresentationService::Observe(nsISupports* aSubject,
|
|||
}
|
||||
|
||||
return HandleTerminateRequest(request);
|
||||
} else if (!strcmp(aTopic, PRESENTATION_RECONNECT_REQUEST_TOPIC)) {
|
||||
nsCOMPtr<nsIPresentationSessionRequest> request(do_QueryInterface(aSubject));
|
||||
if (NS_WARN_IF(!request)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return HandleReconnectRequest(request);
|
||||
} else if (!strcmp(aTopic, "profile-after-change")) {
|
||||
// It's expected since we add and entry to |kLayoutCategories| in
|
||||
// |nsLayoutModule.cpp| to launch this service earlier.
|
||||
|
@ -285,6 +296,7 @@ PresentationService::HandleShutdown()
|
|||
obs->RemoveObserver(this, PRESENTATION_DEVICE_CHANGE_TOPIC);
|
||||
obs->RemoveObserver(this, PRESENTATION_SESSION_REQUEST_TOPIC);
|
||||
obs->RemoveObserver(this, PRESENTATION_TERMINATE_REQUEST_TOPIC);
|
||||
obs->RemoveObserver(this, PRESENTATION_RECONNECT_REQUEST_TOPIC);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -366,12 +378,18 @@ PresentationService::HandleSessionRequest(nsIPresentationSessionRequest* aReques
|
|||
// Create or reuse session info.
|
||||
RefPtr<PresentationSessionInfo> info =
|
||||
GetSessionInfo(sessionId, nsIPresentationService::ROLE_RECEIVER);
|
||||
if (NS_WARN_IF(info)) {
|
||||
// TODO Bug 1195605. Update here after session join/resume becomes supported.
|
||||
ctrlChannel->Disconnect(NS_ERROR_DOM_OPERATION_ERR);
|
||||
return NS_ERROR_DOM_ABORT_ERR;
|
||||
|
||||
// This is the case for reconnecting a session.
|
||||
// Update the control channel and device of the session info.
|
||||
// Call |NotifyResponderReady| to indicate the receiver page is already there.
|
||||
if (info) {
|
||||
info->SetControlChannel(ctrlChannel);
|
||||
info->SetDevice(device);
|
||||
return static_cast<PresentationPresentingInfo*>(
|
||||
info.get())->NotifyResponderReady();
|
||||
}
|
||||
|
||||
// This is the case for a new session.
|
||||
info = new PresentationPresentingInfo(url, sessionId, device);
|
||||
rv = info->Init(ctrlChannel);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
|
@ -452,6 +470,53 @@ PresentationService::HandleTerminateRequest(nsIPresentationTerminateRequest* aRe
|
|||
return info->OnTerminate(ctrlChannel);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationService::HandleReconnectRequest(nsIPresentationSessionRequest* aRequest)
|
||||
{
|
||||
nsCOMPtr<nsIPresentationControlChannel> ctrlChannel;
|
||||
nsresult rv = aRequest->GetControlChannel(getter_AddRefs(ctrlChannel));
|
||||
if (NS_WARN_IF(NS_FAILED(rv) || !ctrlChannel)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsAutoString sessionId;
|
||||
rv = aRequest->GetPresentationId(sessionId);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
ctrlChannel->Disconnect(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
uint64_t windowId;
|
||||
rv = GetWindowIdBySessionIdInternal(sessionId, &windowId);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
ctrlChannel->Disconnect(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
RefPtr<PresentationSessionInfo> info =
|
||||
GetSessionInfo(sessionId, nsIPresentationService::ROLE_RECEIVER);
|
||||
if (NS_WARN_IF(!info)) {
|
||||
// Cannot reconnect non-existed session
|
||||
ctrlChannel->Disconnect(NS_ERROR_DOM_OPERATION_ERR);
|
||||
return NS_ERROR_DOM_ABORT_ERR;
|
||||
}
|
||||
|
||||
nsAutoString url;
|
||||
rv = aRequest->GetUrl(url);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
ctrlChannel->Disconnect(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Make sure the url is the same as the previous one.
|
||||
if (NS_WARN_IF(!info->GetUrl().Equals(url))) {
|
||||
ctrlChannel->Disconnect(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
return HandleSessionRequest(aRequest);
|
||||
}
|
||||
|
||||
void
|
||||
PresentationService::NotifyAvailableChange(bool aIsAvailable)
|
||||
{
|
||||
|
@ -642,6 +707,57 @@ PresentationService::TerminateSession(const nsAString& aSessionId,
|
|||
return info->Close(NS_OK, nsIPresentationSessionListener::STATE_TERMINATED);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationService::ReconnectSession(const nsAString& aUrl,
|
||||
const nsAString& aSessionId,
|
||||
uint8_t aRole,
|
||||
nsIPresentationServiceCallback* aCallback)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!aSessionId.IsEmpty());
|
||||
MOZ_ASSERT(aCallback);
|
||||
|
||||
if (aRole != nsIPresentationService::ROLE_CONTROLLER) {
|
||||
MOZ_ASSERT(false, "Only controller can call ReconnectSession.");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!aCallback)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
|
||||
if (NS_WARN_IF(!info)) {
|
||||
return aCallback->NotifyError(NS_ERROR_DOM_NOT_FOUND_ERR);
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!info->GetUrl().Equals(aUrl))) {
|
||||
return aCallback->NotifyError(NS_ERROR_DOM_NOT_FOUND_ERR);
|
||||
}
|
||||
|
||||
return static_cast<PresentationControllingInfo*>(info.get())->Reconnect(aCallback);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationService::BuildTransport(const nsAString& aSessionId,
|
||||
uint8_t aRole)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!aSessionId.IsEmpty());
|
||||
|
||||
if (aRole != nsIPresentationService::ROLE_CONTROLLER) {
|
||||
MOZ_ASSERT(false, "Only controller can call BuildTransport.");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole);
|
||||
if (NS_WARN_IF(!info)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return static_cast<PresentationControllingInfo*>(info.get())->BuildTransport();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationService::RegisterAvailabilityListener(nsIPresentationAvailabilityListener* aListener)
|
||||
{
|
||||
|
@ -848,6 +964,13 @@ PresentationService::GetWindowIdBySessionId(const nsAString& aSessionId,
|
|||
return GetWindowIdBySessionIdInternal(aSessionId, aWindowId);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationService::UpdateWindowIdBySessionId(const nsAString& aSessionId,
|
||||
const uint64_t aWindowId)
|
||||
{
|
||||
return UpdateWindowIdBySessionIdInternal(aSessionId, aWindowId);
|
||||
}
|
||||
|
||||
bool
|
||||
PresentationService::IsSessionAccessible(const nsAString& aSessionId,
|
||||
const uint8_t aRole,
|
||||
|
|
|
@ -68,6 +68,7 @@ private:
|
|||
nsresult HandleDeviceChange();
|
||||
nsresult HandleSessionRequest(nsIPresentationSessionRequest* aRequest);
|
||||
nsresult HandleTerminateRequest(nsIPresentationTerminateRequest* aRequest);
|
||||
nsresult HandleReconnectRequest(nsIPresentationSessionRequest* aRequest);
|
||||
void NotifyAvailableChange(bool aIsAvailable);
|
||||
bool IsAppInstalled(nsIURI* aUri);
|
||||
|
||||
|
|
|
@ -36,6 +36,8 @@ PresentationServiceBase::GetWindowIdBySessionIdInternal(
|
|||
const nsAString& aSessionId,
|
||||
uint64_t* aWindowId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mRespondingWindowIds.Get(aSessionId, aWindowId)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -46,6 +48,8 @@ void
|
|||
PresentationServiceBase::AddRespondingSessionId(uint64_t aWindowId,
|
||||
const nsAString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (NS_WARN_IF(aWindowId == 0)) {
|
||||
return;
|
||||
}
|
||||
|
@ -63,6 +67,8 @@ PresentationServiceBase::AddRespondingSessionId(uint64_t aWindowId,
|
|||
void
|
||||
PresentationServiceBase::RemoveRespondingSessionId(const nsAString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
uint64_t windowId = 0;
|
||||
if (mRespondingWindowIds.Get(aSessionId, &windowId)) {
|
||||
mRespondingWindowIds.Remove(aSessionId);
|
||||
|
@ -76,5 +82,17 @@ PresentationServiceBase::RemoveRespondingSessionId(const nsAString& aSessionId)
|
|||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationServiceBase::UpdateWindowIdBySessionIdInternal(
|
||||
const nsAString& aSessionId,
|
||||
const uint64_t aWindowId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
RemoveRespondingSessionId(aSessionId);
|
||||
AddRespondingSessionId(aWindowId, aSessionId);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -37,6 +37,8 @@ protected:
|
|||
nsresult GetWindowIdBySessionIdInternal(const nsAString& aSessionId, uint64_t* aWindowId);
|
||||
void AddRespondingSessionId(uint64_t aWindowId, const nsAString& aSessionId);
|
||||
void RemoveRespondingSessionId(const nsAString& aSessionId);
|
||||
nsresult UpdateWindowIdBySessionIdInternal(const nsAString& aSessionId,
|
||||
const uint64_t aWindowId);
|
||||
|
||||
// Store the responding listener based on the window ID of the (in-process or
|
||||
// OOP) receiver page.
|
||||
|
|
|
@ -244,6 +244,10 @@ PresentationSessionInfo::Shutdown(nsresult aReason)
|
|||
nsresult
|
||||
PresentationSessionInfo::SetListener(nsIPresentationSessionListener* aListener)
|
||||
{
|
||||
if (mListener && aListener) {
|
||||
NS_WARN_IF(NS_FAILED(mListener->NotifyReplaced()));
|
||||
}
|
||||
|
||||
mListener = aListener;
|
||||
|
||||
if (mListener) {
|
||||
|
@ -425,7 +429,8 @@ PresentationSessionInfo::NotifyTransportClosed(nsresult aReason)
|
|||
// potential subsequent |Shutdown| calls.
|
||||
mTransport = nullptr;
|
||||
|
||||
if (NS_WARN_IF(!IsSessionReady())) {
|
||||
if (NS_WARN_IF(!IsSessionReady() &&
|
||||
mState == nsIPresentationSessionListener::STATE_CONNECTING)) {
|
||||
// It happens before the session is ready. Reply the callback.
|
||||
return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
|
||||
}
|
||||
|
@ -744,6 +749,10 @@ PresentationControllingInfo::NotifyConnected()
|
|||
|
||||
switch (mState) {
|
||||
case nsIPresentationSessionListener::STATE_CONNECTING: {
|
||||
nsresult rv = mControlChannel->Launch(GetSessionId(), GetUrl());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
Unused << NS_WARN_IF(NS_FAILED(BuildTransport()));
|
||||
break;
|
||||
}
|
||||
|
@ -758,14 +767,27 @@ PresentationControllingInfo::NotifyConnected()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationControllingInfo::NotifyReconnected()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mReconnectCallback);
|
||||
|
||||
if (NS_WARN_IF(mState == nsIPresentationSessionListener::STATE_TERMINATED)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
SetStateWithReason(nsIPresentationSessionListener::STATE_CONNECTING, NS_OK);
|
||||
return mReconnectCallback->NotifySuccess();
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationControllingInfo::BuildTransport()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsresult rv = mControlChannel->Launch(GetSessionId(), GetUrl());
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
if (mState != nsIPresentationSessionListener::STATE_CONNECTING) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!Preferences::GetBool("dom.presentation.session_transport.data_channel.enable")) {
|
||||
|
@ -807,7 +829,7 @@ PresentationControllingInfo::BuildTransport()
|
|||
if (NS_WARN_IF(!dataChannelBuilder)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
rv = dataChannelBuilder->
|
||||
nsresult rv = dataChannelBuilder->
|
||||
BuildDataChannelTransport(nsIPresentationService::ROLE_CONTROLLER,
|
||||
window,
|
||||
this);
|
||||
|
@ -893,6 +915,41 @@ PresentationControllingInfo::OnStopListening(nsIServerSocket* aServerSocket,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationControllingInfo::Reconnect(nsIPresentationServiceCallback* aCallback)
|
||||
{
|
||||
if (!aCallback) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
mReconnectCallback = aCallback;
|
||||
|
||||
if (NS_WARN_IF(mState == nsIPresentationSessionListener::STATE_TERMINATED)) {
|
||||
return mReconnectCallback->NotifyError(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
}
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
if (!mControlChannel) {
|
||||
nsCOMPtr<nsIPresentationControlChannel> ctrlChannel;
|
||||
rv = mDevice->EstablishControlChannel(getter_AddRefs(ctrlChannel));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return mReconnectCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
|
||||
}
|
||||
|
||||
rv = Init(ctrlChannel);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return mReconnectCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
rv = mControlChannel->Reconnect(mSessionId, GetUrl());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return mReconnectCallback->NotifyError(rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of PresentationPresentingInfo
|
||||
*
|
||||
|
@ -1217,6 +1274,13 @@ PresentationPresentingInfo::NotifyConnected()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationPresentingInfo::NotifyReconnected()
|
||||
{
|
||||
MOZ_ASSERT(false, "NotifyReconnected should not be called at receiver side.");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationPresentingInfo::NotifyDisconnected(nsresult aReason)
|
||||
{
|
||||
|
|
|
@ -186,6 +186,10 @@ public:
|
|||
|
||||
nsresult Init(nsIPresentationControlChannel* aControlChannel) override;
|
||||
|
||||
nsresult Reconnect(nsIPresentationServiceCallback* aCallback);
|
||||
|
||||
nsresult BuildTransport();
|
||||
|
||||
private:
|
||||
~PresentationControllingInfo()
|
||||
{
|
||||
|
@ -198,9 +202,8 @@ private:
|
|||
|
||||
nsresult OnGetAddress(const nsACString& aAddress);
|
||||
|
||||
nsresult BuildTransport();
|
||||
|
||||
nsCOMPtr<nsIServerSocket> mServerSocket;
|
||||
nsCOMPtr<nsIPresentationServiceCallback> mReconnectCallback;
|
||||
};
|
||||
|
||||
// Session info with presenting browsing context (receiver side) behaviors.
|
||||
|
|
|
@ -64,6 +64,11 @@ interface nsIPresentationControlChannelListener: nsISupports
|
|||
* @param reason The reason of channel close, NS_OK represents normal close.
|
||||
*/
|
||||
void notifyDisconnected(in nsresult reason);
|
||||
|
||||
/*
|
||||
* The callback for notifying the reconnect command is acknowledged.
|
||||
*/
|
||||
void notifyReconnected();
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -122,4 +127,13 @@ interface nsIPresentationControlChannel: nsISupports
|
|||
* @param reason The reason of disconnecting channel; NS_OK represents normal.
|
||||
*/
|
||||
void disconnect(in nsresult reason);
|
||||
|
||||
/*
|
||||
* Reconnect a presentation on remote endpoint.
|
||||
* Note that only controller is allowed to reconnect a session.
|
||||
* @param presentationId The Id for representing this session.
|
||||
* @param url The URL requested to open by remote device.
|
||||
* @throws NS_ERROR_FAILURE on failure
|
||||
*/
|
||||
void reconnect(in DOMString presentationId, in DOMString url);
|
||||
};
|
||||
|
|
|
@ -57,6 +57,18 @@ interface nsIPresentationControlServerListener: nsISupports
|
|||
in DOMString aPresentationId,
|
||||
in nsIPresentationControlChannel aControlChannel,
|
||||
in boolean aIsFromReceiver);
|
||||
|
||||
/**
|
||||
* Callback while the remote host is requesting to reconnect a presentation session.
|
||||
* @param aDeviceInfo The device information related to the remote host.
|
||||
* @param aUrl The URL requested to open by remote device.
|
||||
* @param aPresentationId The Id for representing this session.
|
||||
* @param aControlChannel The control channel for this session.
|
||||
*/
|
||||
void onReconnectRequest(in nsITCPDeviceInfo aDeviceInfo,
|
||||
in DOMString url,
|
||||
in DOMString aPresentationId,
|
||||
in nsIPresentationControlChannel aControlChannel);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -44,6 +44,18 @@ interface nsIPresentationDeviceListener: nsISupports
|
|||
in DOMString presentationId,
|
||||
in nsIPresentationControlChannel controlChannel,
|
||||
in boolean aIsFromReceiver);
|
||||
|
||||
/*
|
||||
* Callback while the remote device is requesting to reconnect a presentation session.
|
||||
* @param device The remote device that sent session request.
|
||||
* @param aUrl The URL requested to open by remote device.
|
||||
* @param presentationId The Id for representing this session.
|
||||
* @param controlChannel The control channel for this session.
|
||||
*/
|
||||
void onReconnectRequest(in nsIPresentationDevice device,
|
||||
in DOMString url,
|
||||
in DOMString presentationId,
|
||||
in nsIPresentationControlChannel controlChannel);
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -33,6 +33,11 @@ interface nsIPresentationSessionListener : nsISupports
|
|||
*/
|
||||
void notifyMessage(in DOMString sessionId,
|
||||
in ACString data);
|
||||
|
||||
/*
|
||||
* Called when this listener is replaced by another one.
|
||||
*/
|
||||
void notifyReplaced();
|
||||
};
|
||||
|
||||
[scriptable, uuid(27f101d7-9ed1-429e-b4f8-43b00e8e111c)]
|
||||
|
|
|
@ -98,6 +98,21 @@ interface nsIPresentationService : nsISupports
|
|||
void terminateSession(in DOMString sessionId,
|
||||
in uint8_t role);
|
||||
|
||||
/*
|
||||
* Reconnect the session.
|
||||
*
|
||||
* @param url: The url of presenting page.
|
||||
* @param sessionId: An ID to identify presentation session.
|
||||
* @param role: Identify the function called by controller or receiver.
|
||||
* @param callback: NotifySuccess() is called when a control channel
|
||||
* is opened successfully.
|
||||
* Otherwise, NotifyError() is called with a error message.
|
||||
*/
|
||||
void reconnectSession(in DOMString url,
|
||||
in DOMString sessionId,
|
||||
in uint8_t role,
|
||||
in nsIPresentationServiceCallback callback);
|
||||
|
||||
/*
|
||||
* Register an availability listener. Must be called from the main thread.
|
||||
*
|
||||
|
@ -191,4 +206,23 @@ interface nsIPresentationService : nsISupports
|
|||
* The windowId for building RTCDataChannel session transport
|
||||
*/
|
||||
unsigned long long getWindowIdBySessionId(in DOMString sessionId);
|
||||
|
||||
/*
|
||||
* Update the mapping of the session ID and window ID.
|
||||
*
|
||||
* @param sessionId: An ID to identify presentation session.
|
||||
* @param windowId: The inner window ID associated with the presentation
|
||||
* session.
|
||||
*/
|
||||
void updateWindowIdBySessionId(in DOMString sessionId,
|
||||
in unsigned long long windowId);
|
||||
|
||||
/*
|
||||
* To build the session transport.
|
||||
* NOTE: This function should be only called at controller side.
|
||||
*
|
||||
* @param sessionId: An ID to identify presentation session.
|
||||
* @param role: Identify the function called by controller or receiver.
|
||||
*/
|
||||
void buildTransport(in DOMString sessionId, in uint8_t role);
|
||||
};
|
||||
|
|
|
@ -9,11 +9,14 @@ interface nsIPresentationControlChannel;
|
|||
|
||||
%{C++
|
||||
#define PRESENTATION_SESSION_REQUEST_TOPIC "presentation-session-request"
|
||||
#define PRESENTATION_RECONNECT_REQUEST_TOPIC "presentation-reconnect-request"
|
||||
%}
|
||||
|
||||
/*
|
||||
* The event of a device requesting for a presentation session. User can
|
||||
* monitor the session request on every device by observing "presentation-sesion-request".
|
||||
* The event of a device requesting for starting or reconnecting
|
||||
* a presentation session. User can monitor the session request
|
||||
* on every device by observing "presentation-sesion-request" for a
|
||||
* new session and "presentation-reconnect-request" for reconnecting.
|
||||
*/
|
||||
[scriptable, uuid(d808a084-d0f8-455a-a8df-5879e05a755b)]
|
||||
interface nsIPresentationSessionRequest: nsISupports
|
||||
|
|
|
@ -42,12 +42,27 @@ struct TerminateSessionRequest
|
|||
uint8_t role;
|
||||
};
|
||||
|
||||
struct ReconnectSessionRequest
|
||||
{
|
||||
nsString url;
|
||||
nsString sessionId;
|
||||
uint8_t role;
|
||||
};
|
||||
|
||||
struct BuildTransportRequest
|
||||
{
|
||||
nsString sessionId;
|
||||
uint8_t role;
|
||||
};
|
||||
|
||||
union PresentationIPCRequest
|
||||
{
|
||||
StartSessionRequest;
|
||||
SendSessionMessageRequest;
|
||||
CloseSessionRequest;
|
||||
TerminateSessionRequest;
|
||||
ReconnectSessionRequest;
|
||||
BuildTransportRequest;
|
||||
};
|
||||
|
||||
sync protocol PPresentation
|
||||
|
|
|
@ -5,8 +5,11 @@
|
|||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DCPresentationChannelDescription.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "PresentationBuilderChild.h"
|
||||
#include "PresentationIPCService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
|
|
@ -28,12 +28,20 @@ PresentationContentSessionInfo::Init() {
|
|||
nsresult
|
||||
PresentationContentSessionInfo::Send(const nsAString& aData)
|
||||
{
|
||||
if (!mTransport) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return mTransport->Send(aData);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationContentSessionInfo::Close(nsresult aReason)
|
||||
{
|
||||
if (!mTransport) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return mTransport->Close(aReason);
|
||||
}
|
||||
|
||||
|
|
|
@ -131,6 +131,39 @@ PresentationIPCService::TerminateSession(const nsAString& aSessionId,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationIPCService::ReconnectSession(const nsAString& aUrl,
|
||||
const nsAString& aSessionId,
|
||||
uint8_t aRole,
|
||||
nsIPresentationServiceCallback* aCallback)
|
||||
{
|
||||
MOZ_ASSERT(!aSessionId.IsEmpty());
|
||||
|
||||
if (aRole != nsIPresentationService::ROLE_CONTROLLER) {
|
||||
MOZ_ASSERT(false, "Only controller can call ReconnectSession.");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return SendRequest(aCallback, ReconnectSessionRequest(nsString(aUrl),
|
||||
nsString(aSessionId),
|
||||
aRole));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationIPCService::BuildTransport(const nsAString& aSessionId,
|
||||
uint8_t aRole)
|
||||
{
|
||||
MOZ_ASSERT(!aSessionId.IsEmpty());
|
||||
|
||||
if (aRole != nsIPresentationService::ROLE_CONTROLLER) {
|
||||
MOZ_ASSERT(false, "Only controller can call ReconnectSession.");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return SendRequest(nullptr, BuildTransportRequest(nsString(aSessionId),
|
||||
aRole));
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationIPCService::SendRequest(nsIPresentationServiceCallback* aCallback,
|
||||
const PresentationIPCRequest& aRequest)
|
||||
|
@ -176,6 +209,13 @@ PresentationIPCService::RegisterSessionListener(const nsAString& aSessionId,
|
|||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aListener);
|
||||
|
||||
nsCOMPtr<nsIPresentationSessionListener> listener;
|
||||
if (mSessionListeners.Get(aSessionId, getter_AddRefs(listener))) {
|
||||
NS_WARN_IF(NS_FAILED(listener->NotifyReplaced()));
|
||||
mSessionListeners.Put(aSessionId, aListener);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mSessionListeners.Put(aSessionId, aListener);
|
||||
if (sPresentationChild) {
|
||||
NS_WARN_IF(!sPresentationChild->SendRegisterSessionHandler(nsString(aSessionId), aRole));
|
||||
|
@ -245,6 +285,13 @@ PresentationIPCService::GetWindowIdBySessionId(const nsAString& aSessionId,
|
|||
return GetWindowIdBySessionIdInternal(aSessionId, aWindowId);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationIPCService::UpdateWindowIdBySessionId(const nsAString& aSessionId,
|
||||
const uint64_t aWindowId)
|
||||
{
|
||||
return UpdateWindowIdBySessionIdInternal(aSessionId, aWindowId);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationIPCService::NotifySessionStateChange(const nsAString& aSessionId,
|
||||
uint16_t aState,
|
||||
|
|
|
@ -62,14 +62,6 @@ private:
|
|||
nsRefPtrHashtable<nsUint64HashKey,
|
||||
nsIPresentationRespondingListener> mRespondingListeners;
|
||||
RefPtr<PresentationResponderLoadingCallback> mCallback;
|
||||
|
||||
// Store the mapping between the window ID of the OOP page (in this process)
|
||||
// and the ID of the responding session. It's used for an OOP receiver page
|
||||
// to retrieve the correspondent session ID. Besides, also keep the mapping
|
||||
// between the responding session ID and the window ID to help look up the
|
||||
// window ID.
|
||||
nsClassHashtable<nsUint64HashKey, nsString> mRespondingSessionIds;
|
||||
nsDataHashtable<nsStringHashKey, uint64_t> mRespondingWindowIds;
|
||||
nsRefPtrHashtable<nsStringHashKey,
|
||||
PresentationContentSessionInfo> mSessionInfos;
|
||||
};
|
||||
|
|
|
@ -89,6 +89,12 @@ PresentationParent::RecvPPresentationRequestConstructor(
|
|||
case PresentationIPCRequest::TTerminateSessionRequest:
|
||||
rv = actor->DoRequest(aRequest.get_TerminateSessionRequest());
|
||||
break;
|
||||
case PresentationIPCRequest::TReconnectSessionRequest:
|
||||
rv = actor->DoRequest(aRequest.get_ReconnectSessionRequest());
|
||||
break;
|
||||
case PresentationIPCRequest::TBuildTransportRequest:
|
||||
rv = actor->DoRequest(aRequest.get_BuildTransportRequest());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unknown PresentationIPCRequest type");
|
||||
}
|
||||
|
@ -242,6 +248,14 @@ PresentationParent::NotifyStateChange(const nsAString& aSessionId,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationParent::NotifyReplaced()
|
||||
{
|
||||
// Do nothing here, since |PresentationIPCService::RegisterSessionListener|
|
||||
// already dealt with this in content process.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationParent::NotifyMessage(const nsAString& aSessionId,
|
||||
const nsACString& aData)
|
||||
|
@ -382,6 +396,48 @@ PresentationRequestParent::DoRequest(const TerminateSessionRequest& aRequest)
|
|||
return NotifySuccess();
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationRequestParent::DoRequest(const ReconnectSessionRequest& aRequest)
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
|
||||
// Validate the accessibility (primarily for receiver side) so that a
|
||||
// compromised child process can't fake the ID.
|
||||
if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())->
|
||||
IsSessionAccessible(aRequest.sessionId(), aRequest.role(), OtherPid()))) {
|
||||
|
||||
// NOTE: Return NS_ERROR_DOM_NOT_FOUND_ERR here to match the spec.
|
||||
// https://w3c.github.io/presentation-api/#reconnecting-to-a-presentation
|
||||
return NotifyError(NS_ERROR_DOM_NOT_FOUND_ERR);
|
||||
}
|
||||
|
||||
mNeedRegisterBuilder = true;
|
||||
mSessionId = aRequest.sessionId();
|
||||
return mService->ReconnectSession(aRequest.url(),
|
||||
aRequest.sessionId(),
|
||||
aRequest.role(),
|
||||
this);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationRequestParent::DoRequest(const BuildTransportRequest& aRequest)
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
|
||||
// Validate the accessibility (primarily for receiver side) so that a
|
||||
// compromised child process can't fake the ID.
|
||||
if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())->
|
||||
IsSessionAccessible(aRequest.sessionId(), aRequest.role(), OtherPid()))) {
|
||||
return NotifyError(NS_ERROR_DOM_SECURITY_ERR);
|
||||
}
|
||||
|
||||
nsresult rv = mService->BuildTransport(aRequest.sessionId(), aRequest.role());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NotifyError(rv);
|
||||
}
|
||||
return NotifySuccess();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationRequestParent::NotifySuccess()
|
||||
{
|
||||
|
|
|
@ -113,6 +113,10 @@ private:
|
|||
|
||||
nsresult DoRequest(const TerminateSessionRequest& aRequest);
|
||||
|
||||
nsresult DoRequest(const ReconnectSessionRequest& aRequest);
|
||||
|
||||
nsresult DoRequest(const BuildTransportRequest& aRequest);
|
||||
|
||||
bool mActorDestroyed = false;
|
||||
bool mNeedRegisterBuilder = false;
|
||||
nsString mSessionId;
|
||||
|
|
|
@ -32,6 +32,7 @@ EXPORTS.mozilla.dom += [
|
|||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'ControllerConnectionCollection.cpp',
|
||||
'DCPresentationChannelDescription.cpp',
|
||||
'ipc/PresentationBuilderChild.cpp',
|
||||
'ipc/PresentationBuilderParent.cpp',
|
||||
|
|
|
@ -60,6 +60,9 @@ var handlers = [
|
|||
case CommandType.ICE_CANDIDATE:
|
||||
stateMachine._notifyChannelDescriptor(command);
|
||||
break;
|
||||
case CommandType.RECONNECT_ACK:
|
||||
stateMachine._notifyReconnect(command.presentationId);
|
||||
break;
|
||||
default:
|
||||
debug("unexpected command: " + JSON.stringify(command));
|
||||
// ignore unexpected command.
|
||||
|
@ -111,6 +114,16 @@ ControllerStateMachine.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
reconnect: function _reconnect(presentationId, url) {
|
||||
if (this.state === State.CONNECTED) {
|
||||
this._sendCommand({
|
||||
type: CommandType.RECONNECT,
|
||||
presentationId: presentationId,
|
||||
url: url,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
sendOffer: function _sendOffer(offer) {
|
||||
if (this.state === State.CONNECTED) {
|
||||
this._sendCommand({
|
||||
|
@ -200,6 +213,10 @@ ControllerStateMachine.prototype = {
|
|||
this._channel.notifyTerminate(presentationId);
|
||||
},
|
||||
|
||||
_notifyReconnect: function _notifyReconnect(presentationId) {
|
||||
this._channel.notifyReconnect(presentationId);
|
||||
},
|
||||
|
||||
_notifyChannelDescriptor: function _notifyChannelDescriptor(command) {
|
||||
switch (command.type) {
|
||||
case CommandType.ANSWER:
|
||||
|
|
|
@ -436,6 +436,37 @@ DisplayDeviceProvider::OnTerminateRequest(nsITCPDeviceInfo* aDeviceInfo,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DisplayDeviceProvider::OnReconnectRequest(nsITCPDeviceInfo* aDeviceInfo,
|
||||
const nsAString& aUrl,
|
||||
const nsAString& aPresentationId,
|
||||
nsIPresentationControlChannel* aControlChannel)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aDeviceInfo);
|
||||
MOZ_ASSERT(aControlChannel);
|
||||
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIPresentationDeviceListener> listener;
|
||||
rv = GetListener(getter_AddRefs(listener));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!listener);
|
||||
|
||||
rv = listener->OnReconnectRequest(mDevice,
|
||||
aUrl,
|
||||
aPresentationId,
|
||||
aControlChannel);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIObserver
|
||||
NS_IMETHODIMP
|
||||
DisplayDeviceProvider::Observe(nsISupports* aSubject,
|
||||
|
|
|
@ -474,6 +474,12 @@ LegacyTCPControlChannel.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
reconnect: function() {
|
||||
// Legacy protocol doesn't support extra reconnect protocol.
|
||||
// Trigger error handling for browser to shutdown all the resource locally.
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
classID: Components.ID("{4027ce3d-06e3-4d06-a235-df329cb0d411}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel,
|
||||
Ci.nsIStreamListener]),
|
||||
|
|
|
@ -873,63 +873,13 @@ MulticastDNSDeviceProvider::OnPortChange(uint16_t aPort)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MulticastDNSDeviceProvider::OnSessionRequest(nsITCPDeviceInfo* aDeviceInfo,
|
||||
const nsAString& aUrl,
|
||||
const nsAString& aPresentationId,
|
||||
nsIPresentationControlChannel* aControlChannel)
|
||||
// Create a new device if we were unable to find one with the address.
|
||||
already_AddRefed<MulticastDNSDeviceProvider::Device>
|
||||
MulticastDNSDeviceProvider::GetOrCreateDevice(nsITCPDeviceInfo* aDeviceInfo)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsAutoCString address;
|
||||
Unused << aDeviceInfo->GetAddress(address);
|
||||
|
||||
LOG_I("OnSessionRequest: %s", address.get());
|
||||
|
||||
RefPtr<Device> device;
|
||||
uint32_t index;
|
||||
if (FindDeviceByAddress(address, index)) {
|
||||
device = mDevices[index];
|
||||
} else {
|
||||
// create a one-time device object for non-discoverable controller
|
||||
// this device will not be listed in available device list and cannot
|
||||
// be used for requesting session.
|
||||
nsAutoCString id;
|
||||
Unused << aDeviceInfo->GetId(id);
|
||||
uint16_t port;
|
||||
Unused << aDeviceInfo->GetPort(&port);
|
||||
|
||||
device = new Device(id,
|
||||
/* aName = */ id,
|
||||
/* aType = */ EmptyCString(),
|
||||
address,
|
||||
port,
|
||||
DeviceState::eActive,
|
||||
/* aProvider = */ nullptr);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPresentationDeviceListener> listener;
|
||||
if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) {
|
||||
Unused << listener->OnSessionRequest(device, aUrl, aPresentationId,
|
||||
aControlChannel);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MulticastDNSDeviceProvider::OnTerminateRequest(nsITCPDeviceInfo* aDeviceInfo,
|
||||
const nsAString& aPresentationId,
|
||||
nsIPresentationControlChannel* aControlChannel,
|
||||
bool aIsFromReceiver)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsAutoCString address;
|
||||
Unused << aDeviceInfo->GetAddress(address);
|
||||
|
||||
LOG_I("OnTerminateRequest: %s", address.get());
|
||||
|
||||
RefPtr<Device> device;
|
||||
uint32_t index;
|
||||
if (FindDeviceByAddress(address, index)) {
|
||||
|
@ -952,6 +902,46 @@ MulticastDNSDeviceProvider::OnTerminateRequest(nsITCPDeviceInfo* aDeviceInfo,
|
|||
/* aProvider = */ nullptr);
|
||||
}
|
||||
|
||||
return device.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MulticastDNSDeviceProvider::OnSessionRequest(nsITCPDeviceInfo* aDeviceInfo,
|
||||
const nsAString& aUrl,
|
||||
const nsAString& aPresentationId,
|
||||
nsIPresentationControlChannel* aControlChannel)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsAutoCString address;
|
||||
Unused << aDeviceInfo->GetAddress(address);
|
||||
|
||||
LOG_I("OnSessionRequest: %s", address.get());
|
||||
|
||||
RefPtr<Device> device = GetOrCreateDevice(aDeviceInfo);
|
||||
nsCOMPtr<nsIPresentationDeviceListener> listener;
|
||||
if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) {
|
||||
Unused << listener->OnSessionRequest(device, aUrl, aPresentationId,
|
||||
aControlChannel);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MulticastDNSDeviceProvider::OnTerminateRequest(nsITCPDeviceInfo* aDeviceInfo,
|
||||
const nsAString& aPresentationId,
|
||||
nsIPresentationControlChannel* aControlChannel,
|
||||
bool aIsFromReceiver)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsAutoCString address;
|
||||
Unused << aDeviceInfo->GetAddress(address);
|
||||
|
||||
LOG_I("OnTerminateRequest: %s", address.get());
|
||||
|
||||
RefPtr<Device> device = GetOrCreateDevice(aDeviceInfo);
|
||||
nsCOMPtr<nsIPresentationDeviceListener> listener;
|
||||
if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) {
|
||||
Unused << listener->OnTerminateRequest(device, aPresentationId,
|
||||
|
@ -961,6 +951,29 @@ MulticastDNSDeviceProvider::OnTerminateRequest(nsITCPDeviceInfo* aDeviceInfo,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MulticastDNSDeviceProvider::OnReconnectRequest(nsITCPDeviceInfo* aDeviceInfo,
|
||||
const nsAString& aUrl,
|
||||
const nsAString& aPresentationId,
|
||||
nsIPresentationControlChannel* aControlChannel)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsAutoCString address;
|
||||
Unused << aDeviceInfo->GetAddress(address);
|
||||
|
||||
LOG_I("OnReconnectRequest: %s", address.get());
|
||||
|
||||
RefPtr<Device> device = GetOrCreateDevice(aDeviceInfo);
|
||||
nsCOMPtr<nsIPresentationDeviceListener> listener;
|
||||
if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) {
|
||||
Unused << listener->OnReconnectRequest(device, aUrl, aPresentationId,
|
||||
aControlChannel);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIObserver
|
||||
NS_IMETHODIMP
|
||||
MulticastDNSDeviceProvider::Observe(nsISupports* aSubject,
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include "nsTArray.h"
|
||||
#include "nsWeakPtr.h"
|
||||
|
||||
class nsITCPDeviceInfo;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace presentation {
|
||||
|
@ -162,6 +164,9 @@ private:
|
|||
bool FindDeviceByAddress(const nsACString& aAddress,
|
||||
uint32_t& aIndex);
|
||||
|
||||
already_AddRefed<Device>
|
||||
GetOrCreateDevice(nsITCPDeviceInfo* aDeviceInfo);
|
||||
|
||||
void MarkAllDevicesUnknown();
|
||||
void ClearUnknownDevices();
|
||||
void ClearDevices();
|
||||
|
|
|
@ -193,6 +193,21 @@ PresentationControlService.prototype = {
|
|||
this.releaseControlChannel(aControlChannel);
|
||||
},
|
||||
|
||||
onSessionReconnect: function(aDeviceInfo, aUrl, aPresentationId, aControlChannel) {
|
||||
DEBUG && log("TCPPresentationServer - onSessionReconnect: " +
|
||||
aDeviceInfo.address + ":" + aDeviceInfo.port); // jshint ignore:line
|
||||
if (!this.listener) {
|
||||
this.releaseControlChannel(aControlChannel);
|
||||
return;
|
||||
}
|
||||
|
||||
this.listener.onReconnectRequest(aDeviceInfo,
|
||||
aUrl,
|
||||
aPresentationId,
|
||||
aControlChannel);
|
||||
this.releaseControlChannel(aControlChannel);
|
||||
},
|
||||
|
||||
// nsIServerSocketListener (Triggered by nsIServerSocket.init)
|
||||
onSocketAccepted: function(aServerSocket, aClientSocket) {
|
||||
DEBUG && log("PresentationControlService - onSocketAccepted: " +
|
||||
|
@ -393,6 +408,7 @@ TCPControlChannel.prototype = {
|
|||
_pendingAnswer: null,
|
||||
_pendingClose: null,
|
||||
_pendingCloseReason: null,
|
||||
_pendingReconnect: false,
|
||||
|
||||
sendOffer: function(aOffer) {
|
||||
this._stateMachine.sendOffer(discriptionAsJson(aOffer));
|
||||
|
@ -555,6 +571,12 @@ TCPControlChannel.prototype = {
|
|||
this._notifyDisconnected(this._pendingCloseReason);
|
||||
this._pendingClose = null;
|
||||
}
|
||||
|
||||
if (this._pendingReconnect) {
|
||||
DEBUG && log("TCPControlChannel - notify pending reconnected"); // jshint ignore:line
|
||||
this._notifyReconnected();
|
||||
this._pendingReconnect = false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -624,6 +646,17 @@ TCPControlChannel.prototype = {
|
|||
this._listener.notifyDisconnected(aReason);
|
||||
},
|
||||
|
||||
_notifyReconnected: function() {
|
||||
if (!this._listener) {
|
||||
this._pendingReconnect = true;
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG && log("TCPControlChannel - notify reconnected with role: " +
|
||||
this._direction); // jshint ignore:line
|
||||
this._listener.notifyReconnected();
|
||||
},
|
||||
|
||||
_closeTransport: function() {
|
||||
if (this._connected) {
|
||||
this._transport.setEventSink(null, null);
|
||||
|
@ -649,6 +682,16 @@ TCPControlChannel.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
reconnect: function(aPresentationId, aUrl) {
|
||||
DEBUG && log("TCPControlChannel - reconnect with role: " +
|
||||
this._direction); // jshint ignore:line
|
||||
if (this._direction != "sender") {
|
||||
return Cr.NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
this._stateMachine.reconnect(aPresentationId, aUrl);
|
||||
},
|
||||
|
||||
// callback from state machine
|
||||
sendCommand: function(command) {
|
||||
this._send(command);
|
||||
|
@ -684,9 +727,9 @@ TCPControlChannel.prototype = {
|
|||
if (!this._terminatingId) {
|
||||
this._terminatingId = presentationId;
|
||||
this._presentationService.onSessionTerminate(this._deviceInfo,
|
||||
presentationId,
|
||||
this,
|
||||
this._direction === "sender");
|
||||
presentationId,
|
||||
this,
|
||||
this._direction === "sender");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -700,6 +743,20 @@ TCPControlChannel.prototype = {
|
|||
delete this._terminatingId;
|
||||
},
|
||||
|
||||
notifyReconnect: function(presentationId, url) {
|
||||
switch (this._direction) {
|
||||
case "receiver":
|
||||
this._presentationService.onSessionReconnect(this._deviceInfo,
|
||||
url,
|
||||
presentationId,
|
||||
this);
|
||||
break;
|
||||
case "sender":
|
||||
this._notifyReconnected();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
notifyOffer: function(offer) {
|
||||
this._onOffer(offer);
|
||||
},
|
||||
|
|
|
@ -68,6 +68,14 @@ var handlers = [
|
|||
case CommandType.ICE_CANDIDATE:
|
||||
stateMachine._notifyChannelDescriptor(command);
|
||||
break;
|
||||
case CommandType.RECONNECT:
|
||||
stateMachine._notifyReconnect(command.presentationId,
|
||||
command.url);
|
||||
stateMachine._sendCommand({
|
||||
type: CommandType.RECONNECT_ACK,
|
||||
presentationId: command.presentationId
|
||||
});
|
||||
break;
|
||||
default:
|
||||
debug("unexpected command: " + JSON.stringify(command));
|
||||
// ignore unexpected command
|
||||
|
@ -113,6 +121,10 @@ ReceiverStateMachine.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
reconnect: function _reconnect() {
|
||||
debug("receiver shouldn't trigger reconnect");
|
||||
},
|
||||
|
||||
sendOffer: function _sendOffer() {
|
||||
// offer can only be sent by controlling UA.
|
||||
debug("receiver shouldn't generate offer");
|
||||
|
@ -199,6 +211,10 @@ ReceiverStateMachine.prototype = {
|
|||
this._channel.notifyTerminate(presentationId);
|
||||
},
|
||||
|
||||
_notifyReconnect: function _notifyReconnect(presentationId, url) {
|
||||
this._channel.notifyReconnect(presentationId, url);
|
||||
},
|
||||
|
||||
_notifyChannelDescriptor: function _notifyChannelDescriptor(command) {
|
||||
switch (command.type) {
|
||||
case CommandType.OFFER:
|
||||
|
|
|
@ -27,6 +27,8 @@ const CommandType = Object.freeze({
|
|||
LAUNCH_ACK: "launch-ack", // { presentationId: <string> }
|
||||
TERMINATE: "terminate", // { presentationId: <string> }
|
||||
TERMINATE_ACK: "terminate-ack", // { presentationId: <string> }
|
||||
RECONNECT: "reconnect", // { presentationId: <string> }
|
||||
RECONNECT_ACK: "reconnect-ack", // { presentationId: <string> }
|
||||
// session transport establishment
|
||||
OFFER: "offer", // { offer: <json> }
|
||||
ANSWER: "answer", // { answer: <json> }
|
||||
|
|
|
@ -147,6 +147,14 @@ const mockedControlChannel = {
|
|||
terminate: function(presentationId) {
|
||||
sendAsyncMessage('sender-terminate', presentationId);
|
||||
},
|
||||
reconnect: function(presentationId, url) {
|
||||
sendAsyncMessage('start-reconnect', url);
|
||||
},
|
||||
notifyReconnected: function() {
|
||||
this._listener
|
||||
.QueryInterface(Ci.nsIPresentationControlChannelListener)
|
||||
.notifyReconnected();
|
||||
},
|
||||
disconnect: function(reason) {
|
||||
sendAsyncMessage('control-channel-closed', reason);
|
||||
this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).notifyDisconnected(reason);
|
||||
|
@ -403,6 +411,10 @@ addMessageListener('trigger-incoming-terminate-request', function() {
|
|||
.onTerminateRequest(mockedDevice, sessionId, mockedControlChannel, true);
|
||||
});
|
||||
|
||||
addMessageListener('trigger-reconnected-acked', function(url) {
|
||||
mockedControlChannel.notifyReconnected();
|
||||
});
|
||||
|
||||
addMessageListener('trigger-incoming-offer', function() {
|
||||
mockedControlChannel.simulateOnOffer();
|
||||
});
|
||||
|
@ -435,6 +447,16 @@ addMessageListener('teardown', function() {
|
|||
tearDown();
|
||||
});
|
||||
|
||||
var controlChannelListener;
|
||||
addMessageListener('save-control-channel-listener', function() {
|
||||
controlChannelListener = mockedControlChannel.listener;
|
||||
});
|
||||
|
||||
addMessageListener('restore-control-channel-listener', function(message) {
|
||||
mockedControlChannel.listener = controlChannelListener;
|
||||
controlChannelListener = null;
|
||||
});
|
||||
|
||||
var obs = Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(Ci.nsIObserverService);
|
||||
obs.addObserver(function observer(aSubject, aTopic, aData) {
|
||||
|
|
|
@ -176,6 +176,12 @@ const mockControlChannelOfSender = {
|
|||
.QueryInterface(Ci.nsIPresentationControlChannelListener)
|
||||
.notifyConnected();
|
||||
},
|
||||
notifyReconnected: function() {
|
||||
// send offer after notifyOpened immediately
|
||||
this._listener
|
||||
.QueryInterface(Ci.nsIPresentationControlChannelListener)
|
||||
.notifyReconnected();
|
||||
},
|
||||
sendOffer: function(offer) {
|
||||
sendAsyncMessage('offer-sent');
|
||||
},
|
||||
|
@ -200,6 +206,9 @@ const mockControlChannelOfSender = {
|
|||
terminate: function(presentationId) {
|
||||
sendAsyncMessage('sender-terminate');
|
||||
},
|
||||
reconnect: function(presentationId, url) {
|
||||
sendAsyncMessage('start-reconnect', url);
|
||||
},
|
||||
};
|
||||
|
||||
// control channel of receiver
|
||||
|
@ -406,6 +415,18 @@ function initMockAndListener() {
|
|||
triggerControlChannelError = true;
|
||||
});
|
||||
|
||||
addMessageListener('trigger-reconnected-acked', function(url) {
|
||||
debug('Got message: trigger-reconnected-acked');
|
||||
mockControlChannelOfSender.notifyReconnected();
|
||||
var deviceManager = Cc['@mozilla.org/presentation-device/manager;1']
|
||||
.getService(Ci.nsIPresentationDeviceManager);
|
||||
deviceManager.QueryInterface(Ci.nsIPresentationDeviceListener)
|
||||
.onReconnectRequest(mockDevice,
|
||||
url,
|
||||
sessionId,
|
||||
mockControlChannelOfReceiver);
|
||||
});
|
||||
|
||||
addMessageListener('trigger-on-offer', function() {
|
||||
debug('Got message: trigger-on-offer');
|
||||
mockControlChannelOfReceiver.onOffer(mockChannelDescriptionOfSender);
|
||||
|
|
|
@ -118,11 +118,25 @@ function testConnectionClosed() {
|
|||
});
|
||||
}
|
||||
|
||||
function testReconnectConnection() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
info('Receiver: --- testReconnectConnection ---');
|
||||
command('forward-command', JSON.stringify({ name: 'ready-to-reconnect' }));
|
||||
connection.onconnect = function() {
|
||||
connection.onconnect = null;
|
||||
ok(true, "The connection is reconnected.")
|
||||
aResolve();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
testConnectionAvailable()
|
||||
.then(testConnectionReady)
|
||||
.then(testIncomingMessage)
|
||||
.then(testSendMessage)
|
||||
.then(testConnectionClosed)
|
||||
.then(testReconnectConnection)
|
||||
.then(testConnectionClosed);
|
||||
}
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче