--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -52,17 +52,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
XPCOMUtils.defineLazyServiceGetter(this, "gDNSService",
"@mozilla.org/network/dns-service;1",
"nsIDNSService");
XPCOMUtils.defineLazyServiceGetter(this, "WindowsUIUtils",
"@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils");
XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
"resource://gre/modules/LightweightThemeManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
- "resource:///modules/ContextualIdentityService.jsm");
+ "resource://gre/modules/ContextualIdentityService.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "gAboutNewTabService",
"@mozilla.org/browser/aboutnewtab-service;1",
"nsIAboutNewTabService");
XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
return Services.strings.createBundle('chrome://browser/locale/browser.properties');
});
XPCOMUtils.defineLazyModuleGetter(this, "AddonWatcher",
"resource://gre/modules/AddonWatcher.jsm");
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -7,17 +7,17 @@
Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
Components.utils.import("resource://gre/modules/InlineSpellChecker.jsm");
Components.utils.import("resource://gre/modules/LoginManagerContextMenu.jsm");
Components.utils.import("resource://gre/modules/BrowserUtils.jsm");
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
- "resource:///modules/ContextualIdentityService.jsm");
+ "resource://gre/modules/ContextualIdentityService.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper",
"resource://gre/modules/LoginHelper.jsm");
var gContextMenuContentData = null;
function nsContextMenu(aXulMenu, aIsShift) {
this.shouldDisplay = true;
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -9,17 +9,17 @@ Components.utils.import("resource://gre/
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
Components.utils.import("resource:///modules/RecentWindow.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ShellService",
"resource:///modules/ShellService.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
- "resource:///modules/ContextualIdentityService.jsm");
+ "resource://gre/modules/ContextualIdentityService.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
"@mozilla.org/browser/aboutnewtab-service;1",
"nsIAboutNewTabService");
this.__defineGetter__("BROWSER_NEW_TAB_URL", () => {
if (PrivateBrowsingUtils.isWindowPrivate(window) &&
!PrivateBrowsingUtils.permanentPrivateBrowsing &&
--- a/browser/components/contextualidentity/moz.build
+++ b/browser/components/contextualidentity/moz.build
@@ -3,16 +3,12 @@
# 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/.
BROWSER_CHROME_MANIFESTS += [
'test/browser/browser.ini',
]
-EXTRA_JS_MODULES += [
- 'ContextualIdentityService.jsm',
-]
-
JAR_MANIFESTS += ['jar.mn']
with Files('**'):
BUG_COMPONENT = ('Firefox', 'Contextual Identity')
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -23,17 +23,17 @@ XPCOMUtils.defineLazyModuleGetter(this,
"resource://gre/modules/ShortcutUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CharsetMenu",
"resource://gre/modules/CharsetMenu.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SyncedTabs",
"resource://services-sync/SyncedTabs.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
- "resource:///modules/ContextualIdentityService.jsm");
+ "resource://gre/modules/ContextualIdentityService.jsm");
XPCOMUtils.defineLazyGetter(this, "CharsetBundle", function() {
const kCharsetBundle = "chrome://global/locale/charsetMenu.properties";
return Services.strings.createBundle(kCharsetBundle);
});
XPCOMUtils.defineLazyGetter(this, "BrandBundle", function() {
const kBrandBundle = "chrome://branding/locale/brand.properties";
return Services.strings.createBundle(kBrandBundle);
--- a/browser/components/preferences/cookies.js
+++ b/browser/components/preferences/cookies.js
@@ -5,17 +5,17 @@
const nsICookie = Components.interfaces.nsICookie;
Components.utils.import("resource://gre/modules/PluralForm.jsm");
Components.utils.import("resource://gre/modules/Services.jsm")
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
- "resource:///modules/ContextualIdentityService.jsm");
+ "resource://gre/modules/ContextualIdentityService.jsm");
var gCookiesWindow = {
_cm : Components.classes["@mozilla.org/cookiemanager;1"]
.getService(Components.interfaces.nsICookieManager),
_ds : Components.classes["@mozilla.org/intl/scriptabledateformat;1"]
.getService(Components.interfaces.nsIScriptableDateFormat),
_hosts : {},
_hostOrder : [],
--- a/devtools/client/inspector/rules/test/head.js
+++ b/devtools/client/inspector/rules/test/head.js
@@ -212,34 +212,16 @@ var waitForSuccess = Task.async(function
ok(false, "Failure: " + desc);
break;
}
yield new Promise(r => setTimeout(r, 200));
}
});
/**
- * Get the dataURL for the font family tooltip.
- *
- * @param {String} font
- * The font family value.
- * @param {object} nodeFront
- * The NodeActor that will used to retrieve the dataURL for the
- * font family tooltip contents.
- */
-var getFontFamilyDataURL = Task.async(function* (font, nodeFront) {
- let fillStyle = (Services.prefs.getCharPref("devtools.theme") === "light") ?
- "black" : "white";
-
- let {data} = yield nodeFront.getFontFamilyDataURL(font, fillStyle);
- let dataURL = yield data.string();
- return dataURL;
-});
-
-/**
* Get the DOMNode for a css rule in the rule-view that corresponds to the given
* selector
*
* @param {CssRuleView} view
* The instance of the rule-view panel
* @param {String} selectorText
* The selector in the rule-view for which the rule
* object is wanted
--- a/devtools/client/inspector/shared/style-inspector-overlays.js
+++ b/devtools/client/inspector/shared/style-inspector-overlays.js
@@ -7,17 +7,17 @@
"use strict";
// The style-inspector overlays are:
// - tooltips that appear when hovering over property values
// - editor tooltips that appear when clicking color swatches, etc.
// - in-content highlighters that appear when hovering over property values
// - etc.
-const {getTheme} = require("devtools/client/shared/theme");
+const {getColor} = require("devtools/client/shared/theme");
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const {
getImageDimensions,
setImageTooltip,
setBrokenImageTooltip,
} = require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
const {
SwatchColorPickerTooltip,
@@ -467,17 +467,17 @@ TooltipsOverlay.prototype = {
if (!font || !nodeFront || typeof nodeFront.getFontFamilyDataURL !== "function") {
throw new Error("Unable to create font preview tooltip content.");
}
font = font.replace(/"/g, "'");
font = font.replace("!important", "");
font = font.trim();
- let fillStyle = getTheme() === "light" ? "black" : "white";
+ let fillStyle = getColor("body-color");
let {data, size: maxDim} = yield nodeFront.getFontFamilyDataURL(font, fillStyle);
let imageUrl = yield data.string();
let doc = this.view.inspector.panelDoc;
let {naturalWidth, naturalHeight} = yield getImageDimensions(doc, imageUrl);
yield setImageTooltip(this.previewTooltip, doc, imageUrl,
{hideDimensionLabel: true, maxDim, naturalWidth, naturalHeight});
--- a/devtools/client/inspector/shared/test/head.js
+++ b/devtools/client/inspector/shared/test/head.js
@@ -1,24 +1,24 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint no-unused-vars: [2, {"vars": "local"}] */
/* import-globals-from ../../test/head.js */
-
"use strict";
// Import the inspector's head.js first (which itself imports shared-head.js).
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/inspector/test/head.js",
this);
var {CssRuleView} = require("devtools/client/inspector/rules/rules");
var {getInplaceEditorForSpan: inplaceEditor} =
require("devtools/client/shared/inplace-editor");
+const {getColor: getThemeColor} = require("devtools/client/shared/theme");
const TEST_URL_ROOT =
"http://example.com/browser/devtools/client/inspector/shared/test/";
const TEST_URL_ROOT_SSL =
"https://example.com/browser/devtools/client/inspector/shared/test/";
const ROOT_TEST_DIR = getRootDirectory(gTestPath);
const FRAME_SCRIPT_URL = ROOT_TEST_DIR + "doc_frame_script.js";
const _STRINGS = Services.strings.createBundle(
@@ -235,18 +235,17 @@ function waitForSuccess(validatorFn, nam
*
* @param {String} font
* The font family value.
* @param {object} nodeFront
* The NodeActor that will used to retrieve the dataURL for the
* font family tooltip contents.
*/
var getFontFamilyDataURL = Task.async(function* (font, nodeFront) {
- let fillStyle = (Services.prefs.getCharPref("devtools.theme") === "light") ?
- "black" : "white";
+ let fillStyle = getThemeColor("body-color");
let {data} = yield nodeFront.getFontFamilyDataURL(font, fillStyle);
let dataURL = yield data.string();
return dataURL;
});
/* *********************************************
* RULE-VIEW
--- a/dom/base/DOMImplementation.cpp
+++ b/dom/base/DOMImplementation.cpp
@@ -134,18 +134,24 @@ DOMImplementation::CreateDocument(const
true, scriptHandlingObject,
DocumentFlavorLegacyGuess);
NS_ENSURE_SUCCESS(rv, rv);
// When DOMImplementation's createDocument method is invoked with
// namespace set to HTML Namespace use the registry of the associated
// document to the new instance.
nsCOMPtr<nsIDocument> doc = do_QueryInterface(document);
+
if (aNamespaceURI.EqualsLiteral("http://www.w3.org/1999/xhtml")) {
+ doc->SetContentType(NS_LITERAL_STRING("application/xhtml+xml"));
doc->UseRegistryFromDocument(mOwner);
+ } else if (aNamespaceURI.EqualsLiteral("http://www.w3.org/2000/svg")) {
+ doc->SetContentType(NS_LITERAL_STRING("image/svg+xml"));
+ } else {
+ doc->SetContentType(NS_LITERAL_STRING("application/xml"));
}
doc->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE);
doc.forget(aDocument);
document.forget(aDOMDocument);
return NS_OK;
}
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1521,17 +1521,17 @@ Element::BindToTree(nsIDocument* aDocume
// aDocument->BindingManager()->ChangeDocumentFor(this, nullptr,
// aDocument);
// We no longer need to track the subtree pointer (and in fact we'll assert
// if we do this any later).
ClearSubtreeRootPointer();
// Being added to a document.
- SetInDocument();
+ SetIsInDocument();
// Unset this flag since we now really are in a document.
UnsetFlags(NODE_FORCE_XBL_BINDINGS |
// And clear the lazy frame construction bits.
NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES |
// And the restyle bits
ELEMENT_ALL_RESTYLE_FLAGS);
} else if (IsInShadowTree()) {
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -70,37 +70,37 @@ NS_GetContentList(nsINode* aRootNode,
int32_t aMatchNameSpaceId,
const nsAString& aTagname);
#define ELEMENT_FLAG_BIT(n_) NODE_FLAG_BIT(NODE_TYPE_SPECIFIC_BITS_OFFSET + (n_))
// Element-specific flags
enum {
// Set if the element has a pending style change.
- ELEMENT_HAS_PENDING_RESTYLE = ELEMENT_FLAG_BIT(0),
+ ELEMENT_HAS_PENDING_RESTYLE = NODE_SHARED_RESTYLE_BIT_1,
// Set if the element is a potential restyle root (that is, has a style
// change pending _and_ that style change will attempt to restyle
// descendants).
- ELEMENT_IS_POTENTIAL_RESTYLE_ROOT = ELEMENT_FLAG_BIT(1),
+ ELEMENT_IS_POTENTIAL_RESTYLE_ROOT = NODE_SHARED_RESTYLE_BIT_2,
// Set if the element has a pending animation-only style change as
// part of an animation-only style update (where we update styles from
// animation to the current refresh tick, but leave everything else as
// it was).
- ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE = ELEMENT_FLAG_BIT(2),
+ ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE = ELEMENT_FLAG_BIT(0),
// Set if the element is a potential animation-only restyle root (that
// is, has an animation-only style change pending _and_ that style
// change will attempt to restyle descendants).
- ELEMENT_IS_POTENTIAL_ANIMATION_ONLY_RESTYLE_ROOT = ELEMENT_FLAG_BIT(3),
+ ELEMENT_IS_POTENTIAL_ANIMATION_ONLY_RESTYLE_ROOT = ELEMENT_FLAG_BIT(1),
// Set if this element has a pending restyle with an eRestyle_SomeDescendants
// restyle hint.
- ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR = ELEMENT_FLAG_BIT(4),
+ ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR = ELEMENT_FLAG_BIT(2),
// Just the HAS_PENDING bits, for convenience
ELEMENT_PENDING_RESTYLE_FLAGS =
ELEMENT_HAS_PENDING_RESTYLE |
ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE,
// Just the IS_POTENTIAL bits, for convenience
ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS =
@@ -108,20 +108,20 @@ enum {
ELEMENT_IS_POTENTIAL_ANIMATION_ONLY_RESTYLE_ROOT,
// All of the restyle bits together, for convenience.
ELEMENT_ALL_RESTYLE_FLAGS = ELEMENT_PENDING_RESTYLE_FLAGS |
ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS |
ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR,
// Set if this element is marked as 'scrollgrab' (see bug 912666)
- ELEMENT_HAS_SCROLLGRAB = ELEMENT_FLAG_BIT(5),
+ ELEMENT_HAS_SCROLLGRAB = ELEMENT_FLAG_BIT(3),
// Remaining bits are for subclasses
- ELEMENT_TYPE_SPECIFIC_BITS_OFFSET = NODE_TYPE_SPECIFIC_BITS_OFFSET + 6
+ ELEMENT_TYPE_SPECIFIC_BITS_OFFSET = NODE_TYPE_SPECIFIC_BITS_OFFSET + 4
};
#undef ELEMENT_FLAG_BIT
// Make sure we have space for our bits
ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET);
namespace mozilla {
@@ -1445,16 +1445,23 @@ inline mozilla::dom::Element* nsINode::A
}
inline const mozilla::dom::Element* nsINode::AsElement() const
{
MOZ_ASSERT(IsElement());
return static_cast<const mozilla::dom::Element*>(this);
}
+inline void nsINode::UnsetRestyleFlagsIfGecko()
+{
+ if (IsElement() && !IsStyledByServo()) {
+ UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
+ }
+}
+
/**
* Macros to implement Clone(). _elementName is the class for which to implement
* Clone.
*/
#define NS_IMPL_ELEMENT_CLONE(_elementName) \
nsresult \
_elementName::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const \
{ \
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1449,20 +1449,19 @@ Navigator::SendBeacon(const nsAString& a
nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
nsCOMPtr<nsIInterfaceRequestor> callbacks =
do_QueryInterface(mWindow->GetDocShell());
loadGroup->SetNotificationCallbacks(callbacks);
channel->SetLoadGroup(loadGroup);
RefPtr<BeaconStreamListener> beaconListener = new BeaconStreamListener();
rv = channel->AsyncOpen2(beaconListener);
- if (NS_FAILED(rv)) {
- aRv.Throw(rv);
- return false;
- }
+ // do not throw if security checks fail within asyncOpen2
+ NS_ENSURE_SUCCESS(rv, false);
+
// make the beaconListener hold a strong reference to the loadgroup
// which is released in ::OnStartRequest
beaconListener->SetLoadGroup(loadGroup);
return true;
}
MediaDevices*
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1455,17 +1455,20 @@ nsIDocument::nsIDocument()
mFontFaceSetDirty(true),
mGetUserFontSetCalled(false),
mPostedFlushUserFontSet(false),
mPartID(0),
mDidFireDOMContentLoaded(true),
mHasScrollLinkedEffect(false),
mUserHasInteracted(false)
{
- SetInDocument();
+ SetIsDocument();
+ if (IsStyledByServo()) {
+ SetFlags(NODE_IS_DIRTY_FOR_SERVO | NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
+ }
PR_INIT_CLIST(&mDOMMediaQueryLists);
}
// NOTE! nsDocument::operator new() zeroes out all members, so don't
// bother initializing members to 0.
nsDocument::nsDocument(const char* aContentType)
@@ -3168,20 +3171,16 @@ nsDocument::GetContentType(nsAString& aC
CopyUTF8toUTF16(GetContentTypeInternal(), aContentType);
return NS_OK;
}
void
nsDocument::SetContentType(const nsAString& aContentType)
{
- NS_ASSERTION(GetContentTypeInternal().IsEmpty() ||
- GetContentTypeInternal().Equals(NS_ConvertUTF16toUTF8(aContentType)),
- "Do you really want to change the content-type?");
-
SetContentTypeInternal(NS_ConvertUTF16toUTF8(aContentType));
}
nsresult
nsDocument::GetAllowPlugins(bool * aAllowPlugins)
{
// First, we ask our docshell if it allows plugins.
nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
@@ -13645,19 +13644,32 @@ nsIDocument::ReportHasScrollLinkedEffect
}
mHasScrollLinkedEffect = true;
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
NS_LITERAL_CSTRING("Async Pan/Zoom"),
this, nsContentUtils::eLAYOUT_PROPERTIES,
"ScrollLinkedEffectFound2");
}
-mozilla::StyleBackendType
-nsIDocument::GetStyleBackendType() const
-{
- if (!mPresShell) {
+void
+nsIDocument::UpdateStyleBackendType()
+{
+ MOZ_ASSERT(mStyleBackendType == StyleBackendType(0),
+ "no need to call UpdateStyleBackendType now");
#ifdef MOZ_STYLO
- NS_WARNING("GetStyleBackendType() called on document without a pres shell");
+ // XXX For now we use a Servo-backed style set only for (X)HTML documents
+ // in content docshells. This should let us avoid implementing XUL-specific
+ // CSS features. And apart from not supporting SVG properties in Servo
+ // yet, the root SVG element likes to create a style sheet for an SVG
+ // document before we have a pres shell (i.e. before we make the decision
+ // here about whether to use a Gecko- or Servo-backed style system), so
+ // we avoid Servo-backed style sets for SVG documents.
+ NS_ASSERTION(mDocumentContainer, "stylo: calling UpdateStyleBackendType "
+ "before we have a docshell");
+ mStyleBackendType =
+ nsLayoutUtils::SupportsServoStyleBackend(this) &&
+ mDocumentContainer ?
+ StyleBackendType::Servo :
+ StyleBackendType::Gecko;
+#else
+ mStyleBackendType = StyleBackendType::Gecko;
#endif
- return StyleBackendType::Gecko;
- }
- return mPresShell->StyleSet()->BackendType();
-}
+}
--- a/dom/base/nsGenericDOMDataNode.cpp
+++ b/dom/base/nsGenericDOMDataNode.cpp
@@ -306,17 +306,17 @@ nsGenericDOMDataNode::SetTextInternal(ui
nsContentUtils::HasMutationListeners(this,
NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED,
this);
nsCOMPtr<nsIAtom> oldValue;
if (haveMutationListeners) {
oldValue = GetCurrentValueAtom();
}
-
+
if (aNotify) {
CharacterDataChangeInfo info = {
aOffset == textLength,
aOffset,
endOffset,
aLength,
aDetails
};
@@ -479,17 +479,17 @@ nsGenericDOMDataNode::BindToTree(nsIDocu
"Already have a parent. Unbind first!");
NS_PRECONDITION(!GetBindingParent() ||
aBindingParent == GetBindingParent() ||
(!aBindingParent && aParent &&
aParent->GetBindingParent() == GetBindingParent()),
"Already have a binding parent. Unbind first!");
NS_PRECONDITION(aBindingParent != this,
"Content must not be its own binding parent");
- NS_PRECONDITION(!IsRootOfNativeAnonymousSubtree() ||
+ NS_PRECONDITION(!IsRootOfNativeAnonymousSubtree() ||
aBindingParent == aParent,
"Native anonymous content must have its parent as its "
"own binding parent");
if (!aBindingParent && aParent) {
aBindingParent = aParent->GetBindingParent();
}
@@ -535,17 +535,17 @@ nsGenericDOMDataNode::BindToTree(nsIDocu
// Set document
if (aDocument) {
// We no longer need to track the subtree pointer (and in fact we'll assert
// if we do this any later).
ClearSubtreeRootPointer();
// XXX See the comment in Element::BindToTree
- SetInDocument();
+ SetIsInDocument();
if (mText.IsBidi()) {
aDocument->SetBidiEnabled();
}
// Clear the lazy frame construction bits.
UnsetFlags(NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES);
} else if (!IsInShadowTree()) {
// If we're not in the doc and not in a shadow tree,
// update our subtree pointer.
@@ -800,17 +800,17 @@ nsGenericDOMDataNode::SaveSubtreeState()
#ifdef DEBUG
void
nsGenericDOMDataNode::List(FILE* out, int32_t aIndent) const
{
}
void
nsGenericDOMDataNode::DumpContent(FILE* out, int32_t aIndent,
- bool aDumpAll) const
+ bool aDumpAll) const
{
}
#endif
bool
nsGenericDOMDataNode::IsLink(nsIURI** aURI) const
{
*aURI = nullptr;
--- a/dom/base/nsHostObjectProtocolHandler.cpp
+++ b/dom/base/nsHostObjectProtocolHandler.cpp
@@ -346,30 +346,24 @@ nsHostObjectProtocolHandler::AddDataEntr
gDataTable->Put(aUri, info);
return NS_OK;
}
void
nsHostObjectProtocolHandler::RemoveDataEntry(const nsACString& aUri)
{
- if (gDataTable) {
- nsCString uriIgnoringRef;
- int32_t hashPos = aUri.FindChar('#');
- if (hashPos < 0) {
- uriIgnoringRef = aUri;
- }
- else {
- uriIgnoringRef = StringHead(aUri, hashPos);
- }
- gDataTable->Remove(uriIgnoringRef);
- if (gDataTable->Count() == 0) {
- delete gDataTable;
- gDataTable = nullptr;
- }
+ if (!gDataTable) {
+ return;
+ }
+
+ gDataTable->Remove(aUri);
+ if (gDataTable->Count() == 0) {
+ delete gDataTable;
+ gDataTable = nullptr;
}
}
nsresult
nsHostObjectProtocolHandler::GenerateURIString(const nsACString &aScheme,
nsIPrincipal* aPrincipal,
nsACString& aUri)
{
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -1075,17 +1075,29 @@ public:
/**
* Get this document's CSSLoader. This is guaranteed to not return null.
*/
mozilla::css::Loader* CSSLoader() const {
return mCSSLoader;
}
- mozilla::StyleBackendType GetStyleBackendType() const;
+ mozilla::StyleBackendType GetStyleBackendType() const {
+ if (mStyleBackendType == mozilla::StyleBackendType(0)) {
+ const_cast<nsIDocument*>(this)->UpdateStyleBackendType();
+ }
+ MOZ_ASSERT(mStyleBackendType != mozilla::StyleBackendType(0));
+ return mStyleBackendType;
+ }
+
+ void UpdateStyleBackendType();
+
+ bool IsStyledByServo() const {
+ return GetStyleBackendType() == mozilla::StyleBackendType::Servo;
+ }
/**
* Get this document's StyleImageLoader. This is guaranteed to not return null.
*/
mozilla::css::ImageLoader* StyleImageLoader() const {
return mStyleImageLoader;
}
@@ -2907,16 +2919,20 @@ protected:
nsCompatibility mCompatMode;
// Our readyState
ReadyState mReadyState;
// Our visibility state
mozilla::dom::VisibilityState mVisibilityState;
+ // Whether this document has (or will have, once we have a pres shell) a
+ // Gecko- or Servo-backed style system.
+ mozilla::StyleBackendType mStyleBackendType;
+
// True if BIDI is enabled.
bool mBidiEnabled : 1;
// True if a MathML element has ever been owned by this document.
bool mMathMLEnabled : 1;
// True if this document is the initial document for a window. This should
// basically be true only for documents that exist in newly-opened windows or
// documents created to satisfy a GetDocument() on a window when there's no
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -3059,8 +3059,16 @@ nsINode::IsApzAware() const
return IsNodeApzAware();
}
bool
nsINode::IsNodeApzAwareInternal() const
{
return EventTarget::IsApzAware();
}
+
+#ifdef MOZ_STYLO
+bool
+nsINode::IsStyledByServo() const
+{
+ return OwnerDoc()->IsStyledByServo();
+}
+#endif
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -176,18 +176,30 @@ enum {
NODE_ALL_DIRECTION_FLAGS = NODE_HAS_DIRECTION_LTR |
NODE_HAS_DIRECTION_RTL,
NODE_CHROME_ONLY_ACCESS = NODE_FLAG_BIT(19),
NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS = NODE_FLAG_BIT(20),
+ // These two bits are shared by Gecko's and Servo's restyle systems for
+ // different purposes. They should not be accessed directly, and access to
+ // them should be properly guarded by asserts.
+ NODE_SHARED_RESTYLE_BIT_1 = NODE_FLAG_BIT(21),
+ NODE_SHARED_RESTYLE_BIT_2 = NODE_FLAG_BIT(22),
+
+ // Whether this node is dirty for Servo's style system.
+ NODE_IS_DIRTY_FOR_SERVO = NODE_SHARED_RESTYLE_BIT_1,
+
+ // Whether this node has dirty descendants for Servo's style system.
+ NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO = NODE_SHARED_RESTYLE_BIT_2,
+
// Remaining bits are node type specific.
- NODE_TYPE_SPECIFIC_BITS_OFFSET = 21
+ NODE_TYPE_SPECIFIC_BITS_OFFSET = 23
};
// Make sure we have space for our bits
#define ASSERT_NODE_FLAGS_SPACE(n) \
static_assert(WRAPPER_CACHE_FLAGS_BITS_USED + (n) <= \
sizeof(nsWrapperCache::FlagsType) * 8, \
"Not enough space for our bits")
ASSERT_NODE_FLAGS_SPACE(NODE_TYPE_SPECIFIC_BITS_OFFSET);
@@ -950,16 +962,55 @@ public:
using nsIDOMEventTarget::AddSystemEventListener;
virtual bool IsApzAware() const override;
virtual nsPIDOMWindowOuter* GetOwnerGlobalForBindings() override;
virtual nsIGlobalObject* GetOwnerGlobal() const override;
/**
+ * Returns true if this is a node belonging to a document that uses the Servo
+ * style system.
+ */
+#ifdef MOZ_STYLO
+ bool IsStyledByServo() const;
+#else
+ bool IsStyledByServo() const { return false; }
+#endif
+
+ inline bool IsDirtyForServo() const
+ {
+ MOZ_ASSERT(IsStyledByServo());
+ return HasFlag(NODE_IS_DIRTY_FOR_SERVO);
+ }
+
+ inline bool HasDirtyDescendantsForServo() const
+ {
+ MOZ_ASSERT(IsStyledByServo());
+ return HasFlag(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
+ }
+
+ inline void SetIsDirtyForServo() {
+ MOZ_ASSERT(IsStyledByServo());
+ SetFlags(NODE_IS_DIRTY_FOR_SERVO);
+ }
+
+ inline void SetHasDirtyDescendantsForServo() {
+ MOZ_ASSERT(IsStyledByServo());
+ SetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
+ }
+
+ inline void SetIsDirtyAndHasDirtyDescendantsForServo() {
+ MOZ_ASSERT(IsStyledByServo());
+ SetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO | NODE_IS_DIRTY_FOR_SERVO);
+ }
+
+ inline void UnsetRestyleFlagsIfGecko();
+
+ /**
* Adds a mutation observer to be notified when this node, or any of its
* descendants, are modified. The node will hold a weak reference to the
* observer, which means that it is the responsibility of the observer to
* remove itself in case it dies before the node. If an observer is added
* while observers are being notified, it may also be notified. In general,
* adding observers while inside a notification is not a good idea. An
* observer that is already observing the node must not be added without
* being removed first.
@@ -1671,17 +1722,27 @@ public:
void SetMayBeApzAware() { SetBoolFlag(MayBeApzAware); }
bool NodeMayBeApzAware() const
{
return GetBoolFlag(MayBeApzAware);
}
protected:
void SetParentIsContent(bool aValue) { SetBoolFlag(ParentIsContent, aValue); }
- void SetInDocument() { SetBoolFlag(IsInDocument); }
+ /**
+ * This is a special case of SetIsInDocument used to special-case it for the
+ * document constructor (which can't do the IsStyledByServo() check).
+ */
+ void SetIsDocument() { SetBoolFlag(IsInDocument); }
+ void SetIsInDocument() {
+ if (IsStyledByServo()) {
+ SetIsDirtyAndHasDirtyDescendantsForServo();
+ }
+ SetBoolFlag(IsInDocument);
+ }
void SetNodeIsContent() { SetBoolFlag(NodeIsContent); }
void ClearInDocument() { ClearBoolFlag(IsInDocument); }
void SetIsElement() { SetBoolFlag(NodeIsElement); }
void SetHasID() { SetBoolFlag(ElementHasID); }
void ClearHasID() { ClearBoolFlag(ElementHasID); }
void SetMayHaveStyle() { SetBoolFlag(ElementMayHaveStyle); }
void SetHasName() { SetBoolFlag(ElementHasName); }
void ClearHasName() { ClearBoolFlag(ElementHasName); }
@@ -2003,18 +2064,18 @@ public:
return mServoNodeData;
#else
MOZ_CRASH("Accessing servo node data in non-stylo build");
#endif
}
void SetServoNodeData(ServoNodeData* aData) {
#ifdef MOZ_STYLO
- MOZ_ASSERT(!mServoNodeData);
- mServoNodeData = aData;
+ MOZ_ASSERT(!mServoNodeData);
+ mServoNodeData = aData;
#else
MOZ_CRASH("Setting servo node data in non-stylo build");
#endif
}
protected:
static bool Traverse(nsINode *tmp, nsCycleCollectionTraversalCallback &cb);
static void Unlink(nsINode *tmp);
--- a/dom/base/test/test_blob_fragment_and_query.html
+++ b/dom/base/test/test_blob_fragment_and_query.html
@@ -7,42 +7,53 @@
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script>
var blob = new Blob(['hello world']);
ok(blob, "We have a blob.");
-var url = URL.createObjectURL(blob);
-ok(url, "We have a URI");
-
var tests = [
- url,
- url + "?aa",
- url + "#bb",
- url + "?cc#dd",
- url + "#ee?ff",
+ { part: "", revoke: true },
+ { part: "?aa", revoke: false },
+ { part: "#bb", revoke: false },
+ { part: "?cc#dd", revoke: false },
+ { part: "#ee?ff", revoke: false }
];
function runTest() {
if (!tests.length) {
SimpleTest.finish();
return;
}
+ var url = URL.createObjectURL(blob);
+ ok(url, "We have a URI");
+
var test = tests.shift();
+ URL.revokeObjectURL(url + test.part);
+
var xhr = new XMLHttpRequest();
- xhr.open('GET', test);
+ try {
+ xhr.open('GET', url + test.part);
+ } catch(e) {
+ ok(test.revoke, "This should fail!");
+ runTest();
+ return;
+ }
+
xhr.onload = function() {
- is(xhr.responseText, 'hello world', 'URL: ' + test);
+ is(xhr.responseText, 'hello world', 'URL: ' + url + test.part);
runTest();
}
+
xhr.send();
+ ok(!test.revoke, "This should succeed!");
}
SimpleTest.waitForExplicitFinish();
runTest();
</script>
</body>
</html>
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -6776,30 +6776,30 @@ HTMLInputElement::IntrinsicState() const
// 4. The element has already been modified or the user tried to submit the
// form owner while invalid.
if ((!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) &&
(mCanShowValidUI && ShouldShowValidityUI() &&
(IsValid() || (!state.HasState(NS_EVENT_STATE_MOZ_UI_INVALID) &&
!mCanShowInvalidUI)))) {
state |= NS_EVENT_STATE_MOZ_UI_VALID;
}
+
+ // :in-range and :out-of-range only apply if the element currently has a range
+ if (mHasRange) {
+ state |= (GetValidityState(VALIDITY_STATE_RANGE_OVERFLOW) ||
+ GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW))
+ ? NS_EVENT_STATE_OUTOFRANGE
+ : NS_EVENT_STATE_INRANGE;
+ }
}
if (mForm && !mForm->GetValidity() && IsSubmitControl()) {
state |= NS_EVENT_STATE_MOZ_SUBMITINVALID;
}
- // :in-range and :out-of-range only apply if the element currently has a range.
- if (mHasRange) {
- state |= (GetValidityState(VALIDITY_STATE_RANGE_OVERFLOW) ||
- GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW))
- ? NS_EVENT_STATE_OUTOFRANGE
- : NS_EVENT_STATE_INRANGE;
- }
-
return state;
}
void
HTMLInputElement::AddStates(EventStates aStates)
{
if (mType == NS_FORM_INPUT_TEXT) {
EventStates focusStates(aStates & (NS_EVENT_STATE_FOCUS |
--- a/dom/html/HTMLTrackElement.cpp
+++ b/dom/html/HTMLTrackElement.cpp
@@ -234,20 +234,16 @@ HTMLTrackElement::BindToTree(nsIDocument
bool aCompileEventHandlers)
{
nsresult rv = nsGenericHTMLElement::BindToTree(aDocument,
aParent,
aBindingParent,
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv, rv);
- if (!aDocument) {
- return NS_OK;
- }
-
LOG(LogLevel::Debug, ("Track Element bound to tree."));
if (!aParent || !aParent->IsNodeOfType(nsINode::eMEDIA)) {
return NS_OK;
}
// Store our parent so we can look up its frame for display.
if (!mMediaParent) {
mMediaParent = static_cast<HTMLMediaElement*>(aParent);
@@ -258,17 +254,17 @@ HTMLTrackElement::BindToTree(nsIDocument
// We may already have a TextTrack at this point if GetTrack() has already
// been called. This happens, for instance, if script tries to get the
// TextTrack before its mTrackElement has been bound to the DOM tree.
if (!mTrack) {
CreateTextTrack();
}
RefPtr<Runnable> r = NewRunnableMethod(this, &HTMLTrackElement::LoadResource);
- mMediaParent->RunInStableState(r);
+ nsContentUtils::RunInStableState(r.forget());
}
return NS_OK;
}
void
HTMLTrackElement::UnbindFromTree(bool aDeep, bool aNullParent)
{
--- a/dom/html/test/mochitest.ini
+++ b/dom/html/test/mochitest.ini
@@ -455,16 +455,17 @@ skip-if = (toolkit == 'gonk' && debug) #
[test_bug885024.html]
[test_bug893537.html]
[test_bug95530.html]
[test_bug969346.html]
[test_bug982039.html]
[test_bug1003539.html]
[test_bug1045270.html]
[test_bug1146116.html]
+[test_bug1264157.html]
[test_change_crossorigin.html]
[test_checked.html]
[test_dir_attributes_reflection.html]
[test_dl_attributes_reflection.html]
[test_element_prototype.html]
[test_embed_attributes_reflection.html]
[test_formData.html]
[test_formSubmission.html]
copy from dom/html/test/test_bug535043.html
copy to dom/html/test/test_bug1264157.html
--- a/dom/html/test/test_bug535043.html
+++ b/dom/html/test/test_bug1264157.html
@@ -3,86 +3,86 @@
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=535043
-->
<head>
<title>Test for Bug 535043</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ input {
+ outline: 2px solid lime;
+ }
+ input:in-range {
+ outline: 2px solid red;
+ }
+ input:out-of-range {
+ outline: 2px solid orange;
+ }
+ </style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=535043">Mozilla Bug 535043</a>
<p id="display"></p>
<div id="content">
- <textarea></textarea>
- <textarea maxlength="-1"></textarea>
- <textarea maxlength="0"></textarea>
- <textarea maxlength="2"></textarea>
+
+</head>
+<body>
+ <input type="number" value=0 min=0 max=10> Active in-range
+ <br><br>
+ <input type="number" value=0 min=0 max=10 disabled> Disabled in-range
+ <br><br>
+ <input type="number" value=0 min=0 max=10 readonly> Read-only in-range
+ <br><br>
+ <input type="number" value=11 min=0 max=10> Active out-of-range
+ <br><br>
+ <input type="number" value=11 min=0 max=10 disabled> Disabled out-of-range
+ <br><br>
+ <input type="number" value=11 min=0 max=10 readonly> Read-only out-of-range
</div>
<pre id="test">
<script type="text/javascript">
-/** Test for Bug 535043 **/
-function checkTextArea(textArea) {
- textArea.value = '';
- textArea.focus();
- for (var j = 0; j < 3; j++) {
- synthesizeKey('x', {});
- }
- var htmlMaxLength = textArea.getAttribute('maxlength');
- var domMaxLength = textArea.maxLength;
- if (htmlMaxLength == null) {
- is(domMaxLength, -1,
- 'maxlength is unset but maxLength DOM attribute is not -1');
- } else if (htmlMaxLength < 0) {
- // Per the HTML5 spec, out-of-range values are supposed to translate to -1,
- // not 0, but they don't?
- is(domMaxLength, -1,
- 'maxlength is out of range but maxLength DOM attribute is not -1');
- } else {
- is(domMaxLength, parseInt(htmlMaxLength),
- 'maxlength in DOM does not match provided value');
- }
- if (textArea.maxLength == -1) {
- is(textArea.value.length, 3,
- 'textarea with maxLength -1 should have no length limit');
- } else {
- is(textArea.value.length, textArea.maxLength, 'textarea has maxLength ' +
- textArea.maxLength + ' but length ' + textArea.value.length );
- }
-}
+/** Test for Bug 1264157 **/
+SimpleTest.waitForFocus(function() {
+ // Check the initial values.
+ let active = [].slice.call(document.querySelectorAll("input:not(:disabled):not(:-moz-read-only)"));
+ let disabled = [].slice.call(document.querySelectorAll("input:disabled"));
+ let readonly = [].slice.call(document.querySelectorAll("input:-moz-read-only"));
+ ok(active.length == 2, "Test is messed up: missing non-disabled/non-readonly inputs");
+ ok(disabled.length == 2, "Test is messed up: missing disabled inputs");
+ ok(readonly.length == 2, "Test is messed up: missing readonly inputs");
+
+ is(document.querySelectorAll("input:in-range").length, 1,
+ "Wrong number of in-range elements selected.");
+ is(document.querySelectorAll("input:out-of-range").length, 1,
+ "Wrong number of out-of-range elements selected.");
-SimpleTest.waitForFocus(function() {
- var textAreas = document.getElementsByTagName('textarea');
- for (var i = 0; i < textAreas.length; i++) {
- checkTextArea(textAreas[i]);
- }
-
- textArea = textAreas[0];
- testNums = [-42, -1, 0, 2];
- for (var i = 0; i < testNums.length; i++) {
- textArea.removeAttribute('maxlength');
+ // Dynamically change the values to see if that works too.
+ active[0].value = -1;
+ is(document.querySelectorAll("input:in-range").length, 0,
+ "Wrong number of in-range elements selected after value changed.");
+ is(document.querySelectorAll("input:out-of-range").length, 2,
+ "Wrong number of out-of-range elements selected after value changed.");
+ active[0].value = 0;
+ is(document.querySelectorAll("input:in-range").length, 1,
+ "Wrong number of in-range elements selected after value changed back.");
+ is(document.querySelectorAll("input:out-of-range").length, 1,
+ "Wrong number of out-of-range elements selected after value changed back.");
- var caught = false;
- try {
- textArea.maxLength = testNums[i];
- } catch (e) {
- caught = true;
- }
- if (testNums[i] < 0) {
- ok(caught, 'Setting negative maxLength should throw exception');
- } else {
- ok(!caught, 'Setting nonnegative maxLength should not throw exception');
- }
- checkTextArea(textArea);
+ // Dynamically change the attributes to see if that works too.
+ disabled.forEach(function(e) { e.removeAttribute("disabled"); });
+ readonly.forEach(function(e) { e.removeAttribute("readonly"); });
+ active.forEach(function(e) { e.setAttribute("readonly", true); });
- textArea.setAttribute('maxlength', testNums[i]);
- checkTextArea(textArea);
- }
+ is(document.querySelectorAll("input:in-range").length, 2,
+ "Wrong number of in-range elements selected after attribute changed.");
+ is(document.querySelectorAll("input:out-of-range").length, 2,
+ "Wrong number of out-of-range elements selected after attribute changed.");
SimpleTest.finish();
});
SimpleTest.waitForExplicitFinish();
</script>
</pre>
--- a/dom/manifest/test/test_window_oninstall_event.html
+++ b/dom/manifest/test/test_window_oninstall_event.html
@@ -15,17 +15,17 @@ https://bugzilla.mozilla.org/show_bug.cg
const finish = SimpleTest.finish.bind(SimpleTest);
enableOnInstallPref()
.then(createIframe)
.then(checkImplementation)
.then(checkOnInstallEventFires)
.then(checkAddEventListenerFires)
.then(finish)
.catch(err => {
- dump(`${err}: ${err.stack}`);
+ ok(false, err.stack);
finish();
});
function enableOnInstallPref() {
const ops = {
"set": [
["dom.manifest.oninstall", true],
],
@@ -54,17 +54,17 @@ https://bugzilla.mozilla.org/show_bug.cg
// no point in continuing
if (!hasOnInstallProp) {
const err = new Error("No 'oninstall' IDL attribute. Aborting early.");
return reject(err);
}
is(ifrWindow.oninstall, null, "window install is initially set to null");
// Check that enumerable, configurable, and has a getter and setter.
- const objDescriptor = Object.getOwnPropertyDescriptor(window, "oninstall");
+ const objDescriptor = Object.getOwnPropertyDescriptor(ifrWindow, "oninstall");
ok(objDescriptor.enumerable, "is enumerable");
ok(objDescriptor.configurable, "is configurable");
ok(objDescriptor.hasOwnProperty("get"), "has getter");
ok(objDescriptor.hasOwnProperty("set"), "has setter");
resolve(ifrWindow);
});
}
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -665,21 +665,41 @@ WMFVideoMFTManager::CreateBasicVideoFram
b.mPlanes[2].mWidth = halfWidth;
b.mPlanes[2].mOffset = 0;
b.mPlanes[2].mSkip = 0;
media::TimeUnit pts = GetSampleTime(aSample);
NS_ENSURE_TRUE(pts.IsValid(), E_FAIL);
media::TimeUnit duration = GetSampleDuration(aSample);
NS_ENSURE_TRUE(duration.IsValid(), E_FAIL);
+ nsIntRect pictureRegion = mVideoInfo.ScaledImageRect(videoWidth, videoHeight);
+
+ if (mLayersBackend != LayersBackend::LAYERS_D3D9 &&
+ mLayersBackend != LayersBackend::LAYERS_D3D11) {
+ RefPtr<VideoData> v = VideoData::Create(mVideoInfo,
+ mImageContainer,
+ aStreamOffset,
+ pts.ToMicroseconds(),
+ duration.ToMicroseconds(),
+ b,
+ false,
+ -1,
+ pictureRegion);
+ if (twoDBuffer) {
+ twoDBuffer->Unlock2D();
+ } else {
+ buffer->Unlock();
+ }
+ v.forget(aOutVideoData);
+ return S_OK;
+ }
RefPtr<layers::PlanarYCbCrImage> image =
new IMFYCbCrImage(buffer, twoDBuffer);
- nsIntRect pictureRegion = mVideoInfo.ScaledImageRect(videoWidth, videoHeight);
VideoData::SetVideoDataToImage(image,
mVideoInfo,
b,
pictureRegion,
false);
RefPtr<VideoData> v =
VideoData::CreateFromImage(mVideoInfo,
--- a/dom/presentation/PresentationDataChannelSessionTransport.js
+++ b/dom/presentation/PresentationDataChannelSessionTransport.js
@@ -216,18 +216,18 @@ PresentationTransportBuilder.prototype =
},
onIceCandidate: function(aCandidate) {
log("onIceCandidate: " + aCandidate + " with role " + this._role);
let candidate = new this._window.RTCIceCandidate(JSON.parse(aCandidate));
this._peerConnection.addIceCandidate(candidate).catch(e => this._reportError(e));
},
- notifyClosed: function(aReason) {
- log("notifyClosed reason: " + aReason);
+ notifyDisconnected: function(aReason) {
+ log("notifyDisconnected reason: " + aReason);
if (aReason != Cr.NS_OK) {
this._cleanup(aReason);
} else if (this._isControlChannelNeeded) {
this._cleanup(Cr.NS_ERROR_FAILURE);
}
},
};
--- a/dom/presentation/PresentationService.cpp
+++ b/dom/presentation/PresentationService.cpp
@@ -125,17 +125,17 @@ PresentationDeviceRequest::CreateSession
if (NS_WARN_IF(!info)) {
return NS_ERROR_NOT_AVAILABLE;
}
info->SetDevice(aDevice);
// Establish a control channel. If we failed to do so, the callback is called
// with an error message.
nsCOMPtr<nsIPresentationControlChannel> ctrlChannel;
- nsresult rv = aDevice->EstablishControlChannel(mRequestUrl, mId, getter_AddRefs(ctrlChannel));
+ nsresult rv = aDevice->EstablishControlChannel(getter_AddRefs(ctrlChannel));
if (NS_WARN_IF(NS_FAILED(rv))) {
return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
}
// Initialize the session info with the control channel.
rv = info->Init(ctrlChannel);
if (NS_WARN_IF(NS_FAILED(rv))) {
return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
@@ -278,85 +278,85 @@ PresentationService::HandleSessionReques
nsresult rv = aRequest->GetControlChannel(getter_AddRefs(ctrlChannel));
if (NS_WARN_IF(NS_FAILED(rv) || !ctrlChannel)) {
return rv;
}
nsAutoString url;
rv = aRequest->GetUrl(url);
if (NS_WARN_IF(NS_FAILED(rv))) {
- ctrlChannel->Close(rv);
+ ctrlChannel->Disconnect(rv);
return rv;
}
nsAutoString sessionId;
rv = aRequest->GetPresentationId(sessionId);
if (NS_WARN_IF(NS_FAILED(rv))) {
- ctrlChannel->Close(rv);
+ ctrlChannel->Disconnect(rv);
return rv;
}
nsCOMPtr<nsIPresentationDevice> device;
rv = aRequest->GetDevice(getter_AddRefs(device));
if (NS_WARN_IF(NS_FAILED(rv))) {
- ctrlChannel->Close(rv);
+ ctrlChannel->Disconnect(rv);
return rv;
}
#ifdef MOZ_WIDGET_GONK
// Verify the existence of the app if necessary.
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri), url);
if (NS_WARN_IF(NS_FAILED(rv))) {
- ctrlChannel->Close(NS_ERROR_DOM_BAD_URI);
+ ctrlChannel->Disconnect(NS_ERROR_DOM_BAD_URI);
return rv;
}
bool isApp;
rv = uri->SchemeIs("app", &isApp);
if (NS_WARN_IF(NS_FAILED(rv))) {
- ctrlChannel->Close(rv);
+ ctrlChannel->Disconnect(rv);
return rv;
}
if (NS_WARN_IF(isApp && !IsAppInstalled(uri))) {
- ctrlChannel->Close(NS_ERROR_DOM_NOT_FOUND_ERR);
+ ctrlChannel->Disconnect(NS_ERROR_DOM_NOT_FOUND_ERR);
return NS_OK;
}
#endif
// 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->Close(NS_ERROR_DOM_OPERATION_ERR);
+ ctrlChannel->Disconnect(NS_ERROR_DOM_OPERATION_ERR);
return NS_ERROR_DOM_ABORT_ERR;
}
info = new PresentationPresentingInfo(url, sessionId, device);
rv = info->Init(ctrlChannel);
if (NS_WARN_IF(NS_FAILED(rv))) {
- ctrlChannel->Close(rv);
+ ctrlChannel->Disconnect(rv);
return rv;
}
mSessionInfoAtReceiver.Put(sessionId, info);
// Notify the receiver to launch.
nsCOMPtr<nsIPresentationRequestUIGlue> glue =
do_CreateInstance(PRESENTATION_REQUEST_UI_GLUE_CONTRACTID);
if (NS_WARN_IF(!glue)) {
- ctrlChannel->Close(NS_ERROR_DOM_OPERATION_ERR);
+ ctrlChannel->Disconnect(NS_ERROR_DOM_OPERATION_ERR);
return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
}
nsCOMPtr<nsISupports> promise;
rv = glue->SendRequest(url, sessionId, device, getter_AddRefs(promise));
if (NS_WARN_IF(NS_FAILED(rv))) {
- ctrlChannel->Close(rv);
+ ctrlChannel->Disconnect(rv);
return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
}
nsCOMPtr<Promise> realPromise = do_QueryInterface(promise);
static_cast<PresentationPresentingInfo*>(info.get())->SetPromise(realPromise);
return NS_OK;
}
--- a/dom/presentation/PresentationSessionInfo.cpp
+++ b/dom/presentation/PresentationSessionInfo.cpp
@@ -221,17 +221,17 @@ PresentationSessionInfo::Init(nsIPresent
/* virtual */ void
PresentationSessionInfo::Shutdown(nsresult aReason)
{
NS_WARN_IF(NS_FAILED(aReason));
// Close the control channel if any.
if (mControlChannel) {
- NS_WARN_IF(NS_FAILED(mControlChannel->Close(aReason)));
+ NS_WARN_IF(NS_FAILED(mControlChannel->Disconnect(aReason)));
}
// Close the data transport channel if any.
if (mTransport) {
// |mIsTransportReady| will be unset once |NotifyTransportClosed| is called.
NS_WARN_IF(NS_FAILED(mTransport->Close(aReason)));
}
@@ -462,28 +462,28 @@ NS_IMETHODIMP
PresentationSessionInfo::SendIceCandidate(const nsAString& candidate)
{
return mControlChannel->SendIceCandidate(candidate);
}
NS_IMETHODIMP
PresentationSessionInfo::Close(nsresult reason)
{
- return mControlChannel->Close(reason);
+ return mControlChannel->Disconnect(reason);
}
/**
* Implementation of PresentationControllingInfo
*
* During presentation session establishment, the sender expects the following
* after trying to establish the control channel: (The order between step 3 and
* 4 is not guaranteed.)
* 1. |Init| is called to open a socket |mServerSocket| for data transport
* channel.
- * 2. |NotifyOpened| of |nsIPresentationControlChannelListener| is called to
+ * 2. |NotifyConnected| of |nsIPresentationControlChannelListener| is called to
* indicate the control channel is ready to use. Then send the offer to the
* receiver via the control channel.
* 3.1 |OnSocketAccepted| of |nsIServerSocketListener| is called to indicate the
* data transport channel is connected. Then initialize |mTransport|.
* 3.2 |NotifyTransportReady| of |nsIPresentationSessionTransportCallback| is
* called.
* 4. |OnAnswer| of |nsIPresentationControlChannelListener| is called to
* indicate the receiver is ready. Close the control channel since it's no
@@ -668,35 +668,40 @@ PresentationControllingInfo::OnAnswer(ns
}
return builder->OnAnswer(aDescription);
}
mIsResponderReady = true;
// Close the control channel since it's no longer needed.
- nsresult rv = mControlChannel->Close(NS_OK);
+ nsresult rv = mControlChannel->Disconnect(NS_OK);
if (NS_WARN_IF(NS_FAILED(rv))) {
return ReplyError(NS_ERROR_DOM_OPERATION_ERR);
}
// Session might not be ready at this moment (waiting for the establishment of
// the data transport channel).
if (IsSessionReady()){
return ReplySuccess();
}
return NS_OK;
}
NS_IMETHODIMP
-PresentationControllingInfo::NotifyOpened()
+PresentationControllingInfo::NotifyConnected()
{
MOZ_ASSERT(NS_IsMainThread());
+ nsresult rv = mControlChannel->Launch(GetSessionId(), GetUrl());
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
if (!Preferences::GetBool("dom.presentation.session_transport.data_channel.enable")) {
// Build TCP session transport
return GetAddress();
}
nsPIDOMWindowInner* window = nullptr;
/**
* Generally transport is maintained by the chrome process. However, data
@@ -726,36 +731,36 @@ PresentationControllingInfo::NotifyOpene
// OOP case
mTransportType = nsIPresentationChannelDescription::TYPE_DATACHANNEL;
nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder>
dataChannelBuilder(do_QueryInterface(mBuilder));
if (NS_WARN_IF(!dataChannelBuilder)) {
return NS_ERROR_NOT_AVAILABLE;
}
- nsresult rv = dataChannelBuilder->
- BuildDataChannelTransport(nsIPresentationService::ROLE_CONTROLLER,
- window,
- this);
+ rv = dataChannelBuilder->
+ BuildDataChannelTransport(nsIPresentationService::ROLE_CONTROLLER,
+ window,
+ this);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
NS_IMETHODIMP
-PresentationControllingInfo::NotifyClosed(nsresult aReason)
+PresentationControllingInfo::NotifyDisconnected(nsresult aReason)
{
MOZ_ASSERT(NS_IsMainThread());
if (mTransportType == nsIPresentationChannelDescription::TYPE_DATACHANNEL) {
nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder>
builder = do_QueryInterface(mBuilder);
if (builder) {
- NS_WARN_IF(NS_FAILED(builder->NotifyClosed(aReason)));
+ NS_WARN_IF(NS_FAILED(builder->NotifyDisconnected(aReason)));
}
}
// Unset control channel here so it won't try to re-close it in potential
// subsequent |Shutdown| calls.
SetControlChannel(nullptr);
if (NS_WARN_IF(NS_FAILED(aReason) || !mIsResponderReady)) {
@@ -1127,32 +1132,32 @@ PresentationPresentingInfo::OnIceCandida
nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder>
builder = do_QueryInterface(mBuilder);
return builder->OnIceCandidate(aCandidate);
}
NS_IMETHODIMP
-PresentationPresentingInfo::NotifyOpened()
+PresentationPresentingInfo::NotifyConnected()
{
// Do nothing.
return NS_OK;
}
NS_IMETHODIMP
-PresentationPresentingInfo::NotifyClosed(nsresult aReason)
+PresentationPresentingInfo::NotifyDisconnected(nsresult aReason)
{
MOZ_ASSERT(NS_IsMainThread());
if (mTransportType == nsIPresentationChannelDescription::TYPE_DATACHANNEL) {
nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder>
builder = do_QueryInterface(mBuilder);
if (builder) {
- NS_WARN_IF(NS_FAILED(builder->NotifyClosed(aReason)));
+ NS_WARN_IF(NS_FAILED(builder->NotifyDisconnected(aReason)));
}
}
// Unset control channel here so it won't try to re-close it in potential
// subsequent |Shutdown| calls.
SetControlChannel(nullptr);
if (NS_WARN_IF(NS_FAILED(aReason))) {
--- a/dom/presentation/interfaces/nsIPresentationControlChannel.idl
+++ b/dom/presentation/interfaces/nsIPresentationControlChannel.idl
@@ -49,26 +49,26 @@ interface nsIPresentationControlChannelL
/*
* Callback for receiving ICE candidate from remote endpoint.
* @param answer The received answer.
*/
void onIceCandidate(in DOMString candidate);
/*
- * The callback for notifying channel opened. This should be async called
+ * The callback for notifying channel connected. This should be async called
* after nsIPresentationDevice::establishControlChannel.
*/
- void notifyOpened();
+ void notifyConnected();
/*
- * The callback for notifying channel closed.
+ * The callback for notifying channel disconnected.
* @param reason The reason of channel close, NS_OK represents normal close.
*/
- void notifyClosed(in nsresult reason);
+ void notifyDisconnected(in nsresult reason);
};
/*
* The control channel for establishing RTCPeerConnection for a presentation
* session. SDP Offer/Answer will be exchanged through this interface. The
* control channel should be in-order.
*/
[scriptable, uuid(e60e208c-a9f5-4bc6-9a3e-47f3e4ae9c57)]
@@ -98,13 +98,21 @@ interface nsIPresentationControlChannel:
* Send ICE candidate to remote endpoint. |onIceCandidate| should be invoked
* on remote endpoint.
* @param candidate The candidate to send
* @throws NS_ERROR_FAILURE on failure
*/
void sendIceCandidate(in DOMString candidate);
/*
- * Close the transport channel.
- * @param reason The reason of channel close; NS_OK represents normal.
+ * Launch a presentation on remote endpoint.
+ * @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 close(in nsresult reason);
+ void launch(in DOMString presentationId, in DOMString url);
+
+ /*
+ * Disconnect the control channel.
+ * @param reason The reason of disconnecting channel; NS_OK represents normal.
+ */
+ void disconnect(in nsresult reason);
};
--- a/dom/presentation/interfaces/nsIPresentationControlService.idl
+++ b/dom/presentation/interfaces/nsIPresentationControlService.idl
@@ -60,42 +60,48 @@ interface nsIPresentationControlService:
* The port of the server socket. Pass 0 or opt-out to indicate no
* preference, and a port will be selected automatically.
* @throws NS_ERROR_FAILURE if the server socket has been inited or the
* server socket can not be inited.
*/
void startServer([optional] in uint16_t aPort);
/**
- * Request session to designated remote presentation control receiver.
+ * Request connection to designated remote presentation control receiver.
* @param aDeviceInfo
* The remtoe device info for establish connection.
- * @param aUrl
- * The URL requested to open by remote device.
- * @param aPresentationId
- * The Id for representing this session.
* @returns The control channel for this session.
* @throws NS_ERROR_FAILURE if the Id hasn't been inited.
*/
- nsIPresentationControlChannel requestSession(in nsITCPDeviceInfo aDeviceInfo,
- in DOMString aUrl,
- in DOMString aPresentationId);
+ nsIPresentationControlChannel connect(in nsITCPDeviceInfo aDeviceInfo);
+
+ /**
+ * Check the compatibility to remote presentation control server.
+ * @param aVersion
+ * The version of remote server.
+ */
+ boolean isCompatibleServer(in uint32_t aVersion);
/**
* Close server socket and call |listener.onClose(NS_OK)|
*/
void close();
/**
* Get the listen port of the TCP socket, valid after |init|. 0 indicates
* the server socket is not inited or closed.
*/
readonly attribute uint16_t port;
/**
+ * The protocol version implemented by this server.
+ */
+ readonly attribute uint32_t version;
+
+ /**
* The id of the TCP presentation server. |requestSession| won't
* work until the |id| is set.
*/
attribute AUTF8String id;
/**
* the listener for handling events of this presentation control server
*/
--- a/dom/presentation/interfaces/nsIPresentationDevice.idl
+++ b/dom/presentation/interfaces/nsIPresentationDevice.idl
@@ -19,21 +19,18 @@ interface nsIPresentationDevice : nsISup
readonly attribute AUTF8String name;
// TODO expose more info in order to fulfill UX spec
// The category of this device, could be "wifi", "bluetooth", "hdmi", etc.
readonly attribute AUTF8String type;
/*
* Establish a control channel to this device.
- * @param url The URL requested to open by remote device.
- * @param presentationId The Id for representing this session.
* @returns The control channel for this session.
* @throws NS_ERROR_FAILURE if the establishment fails
*/
- nsIPresentationControlChannel establishControlChannel(in DOMString url,
- in DOMString presentationId);
+ nsIPresentationControlChannel establishControlChannel();
// Do something when presentation session is disconnected.
void disconnect();
};
--- a/dom/presentation/interfaces/nsIPresentationSessionTransportBuilder.idl
+++ b/dom/presentation/interfaces/nsIPresentationSessionTransportBuilder.idl
@@ -50,22 +50,22 @@ interface nsIPresentationTCPSessionTrans
* Builder for WebRTC data channel session transport
*/
[scriptable, uuid(8131c4e0-3a8c-4bc1-a92a-8431473d2fe8)]
interface nsIPresentationDataChannelSessionTransportBuilder : nsIPresentationSessionTransportBuilder
{
/**
* The following creation function will trigger |listener.onSessionTransport|
* if the session transport is successfully built, |listener.onError| if some
- * error occurs during creating session transport. The |notifyOpened| of
+ * error occurs during creating session transport. The |notifyConnected| of
* |aControlChannel| should be called before calling
* |buildDataChannelTransport|.
*/
void buildDataChannelTransport(in uint8_t aRole,
in mozIDOMWindow aWindow,
in nsIPresentationSessionTransportBuilderListener aListener);
// Bug 1275150 - Merge TCP builder with the following APIs
void onOffer(in nsIPresentationChannelDescription offer);
void onAnswer(in nsIPresentationChannelDescription answer);
void onIceCandidate(in DOMString candidate);
- void notifyClosed(in nsresult reason);
+ void notifyDisconnected(in nsresult reason);
};
--- a/dom/presentation/ipc/PresentationBuilderParent.cpp
+++ b/dom/presentation/ipc/PresentationBuilderParent.cpp
@@ -84,17 +84,17 @@ PresentationBuilderParent::OnAnswer(nsIP
if (NS_WARN_IF(!SendOnAnswer(SDP))){
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
-PresentationBuilderParent::NotifyClosed(nsresult aReason)
+PresentationBuilderParent::NotifyDisconnected(nsresult aReason)
{
return NS_OK;
}
void
PresentationBuilderParent::ActorDestroy(ActorDestroyReason aWhy)
{
mNeedDestroyActor = false;
new file mode 100644
--- /dev/null
+++ b/dom/presentation/provider/ControllerStateMachine.jsm
@@ -0,0 +1,187 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+/* jshint esnext:true, globalstrict:true, moz:true, undef:true, unused:true */
+/* globals Components, dump */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["ControllerStateMachine"]; // jshint ignore:line
+
+const { utils: Cu } = Components;
+
+/* globals State, CommandType */
+Cu.import("resource://gre/modules/presentation/StateMachineHelper.jsm");
+
+const DEBUG = false;
+function debug(str) {
+ dump("-*- ControllerStateMachine: " + str + "\n");
+}
+
+var handlers = [
+ function _initHandler(stateMachine, command) {
+ // shouldn't receive any command at init state.
+ DEBUG && debug("unexpected command: " + JSON.stringify(command)); // jshint ignore:line
+ },
+ function _connectingHandler(stateMachine, command) {
+ switch (command.type) {
+ case CommandType.CONNECT_ACK:
+ stateMachine.state = State.CONNECTED;
+ stateMachine._notifyDeviceConnected();
+ break;
+ case CommandType.DISCONNECT:
+ stateMachine.state = State.CLOSED;
+ stateMachine._notifyDisconnected(command.reason);
+ break;
+ default:
+ debug("unexpected command: " + JSON.stringify(command));
+ // ignore unexpected command.
+ break;
+ }
+ },
+ function _connectedHandler(stateMachine, command) {
+ switch (command.type) {
+ case CommandType.DISCONNECT:
+ stateMachine.state = State.CLOSED;
+ stateMachine._notifyDisconnected(command.reason);
+ break;
+ case CommandType.LAUNCH_ACK:
+ stateMachine._notifyLaunch(command.presentationId);
+ break;
+ case CommandType.ANSWER:
+ case CommandType.ICE_CANDIDATE:
+ stateMachine._notifyChannelDescriptor(command);
+ break;
+ default:
+ debug("unexpected command: " + JSON.stringify(command));
+ // ignore unexpected command.
+ break;
+ }
+ },
+ function _closingHandler(stateMachine, command) {
+ // ignore every command in closing state.
+ DEBUG && debug("unexpected command: " + JSON.stringify(command)); // jshint ignore:line
+ },
+ function _closedHandler(stateMachine, command) {
+ // ignore every command in closed state.
+ DEBUG && debug("unexpected command: " + JSON.stringify(command)); // jshint ignore:line
+ },
+];
+
+function ControllerStateMachine(channel, deviceId) {
+ this.state = State.INIT;
+ this._channel = channel;
+ this._deviceId = deviceId;
+}
+
+ControllerStateMachine.prototype = {
+ launch: function _launch(presentationId, url) {
+ if (this.state === State.CONNECTED) {
+ this._sendCommand({
+ type: CommandType.LAUNCH,
+ presentationId: presentationId,
+ url: url,
+ });
+ }
+ },
+
+ sendOffer: function _sendOffer(offer) {
+ if (this.state === State.CONNECTED) {
+ this._sendCommand({
+ type: CommandType.OFFER,
+ offer: offer,
+ });
+ }
+ },
+
+ sendAnswer: function _sendAnswer() {
+ // answer can only be sent by presenting UA.
+ debug("controller shouldn't generate answer");
+ },
+
+ updateIceCandidate: function _updateIceCandidate(candidate) {
+ if (this.state === State.CONNECTED) {
+ this._sendCommand({
+ type: CommandType.ICE_CANDIDATE,
+ candidate: candidate,
+ });
+ }
+ },
+
+ onCommand: function _onCommand(command) {
+ handlers[this.state](this, command);
+ },
+
+ onChannelReady: function _onChannelReady() {
+ if (this.state === State.INIT) {
+ this._sendCommand({
+ type: CommandType.CONNECT,
+ deviceId: this._deviceId
+ });
+ this.state = State.CONNECTING;
+ }
+ },
+
+ onChannelClosed: function _onChannelClose(reason, isByRemote) {
+ switch (this.state) {
+ case State.CONNECTED:
+ if (isByRemote) {
+ this.state = State.CLOSED;
+ this._notifyDisconnected(reason);
+ } else {
+ this._sendCommand({
+ type: CommandType.DISCONNECT,
+ reason: reason
+ });
+ this.state = State.CLOSING;
+ this._closeReason = reason;
+ }
+ break;
+ case State.CLOSING:
+ if (isByRemote) {
+ this.state = State.CLOSED;
+ if (this._closeReason) {
+ reason = this._closeReason;
+ delete this._closeReason;
+ }
+ this._notifyDisconnected(reason);
+ }
+ break;
+ default:
+ DEBUG && debug("unexpected channel close: " + reason + ", " + isByRemote); // jshint ignore:line
+ break;
+ }
+ },
+
+ _sendCommand: function _sendCommand(command) {
+ this._channel.sendCommand(command);
+ },
+
+ _notifyDeviceConnected: function _notifyDeviceConnected() {
+ //XXX trigger following command
+ this._channel.notifyDeviceConnected();
+ },
+
+ _notifyDisconnected: function _notifyDisconnected(reason) {
+ this._channel.notifyDisconnected(reason);
+ },
+
+ _notifyLaunch: function _notifyLaunch(presentationId) {
+ this._channel.notifyLaunch(presentationId);
+ },
+
+ _notifyChannelDescriptor: function _notifyChannelDescriptor(command) {
+ switch (command.type) {
+ case CommandType.ANSWER:
+ this._channel.notifyAnswer(command.answer);
+ break;
+ case CommandType.ICE_CANDIDATE:
+ this._channel.notifyIceCandidate(command.candidate);
+ break;
+ }
+ },
+};
+
+this.ControllerStateMachine = ControllerStateMachine; // jshint ignore:line
--- a/dom/presentation/provider/DisplayDeviceProvider.cpp
+++ b/dom/presentation/provider/DisplayDeviceProvider.cpp
@@ -87,30 +87,28 @@ NS_IMETHODIMP
DisplayDeviceProvider::HDMIDisplayDevice::GetWindowId(nsACString& aWindowId)
{
aWindowId = mWindowId;
return NS_OK;
}
NS_IMETHODIMP
DisplayDeviceProvider::HDMIDisplayDevice
- ::EstablishControlChannel(const nsAString& aUrl,
- const nsAString& aPresentationId,
- nsIPresentationControlChannel** aControlChannel)
+ ::EstablishControlChannel(nsIPresentationControlChannel** aControlChannel)
{
nsresult rv = OpenTopLevelWindow();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
RefPtr<DisplayDeviceProvider> provider = mProvider.get();
if (NS_WARN_IF(!provider)) {
return NS_ERROR_FAILURE;
}
- return provider->RequestSession(this, aUrl, aPresentationId, aControlChannel);
+ return provider->Connect(this, aControlChannel);
}
NS_IMETHODIMP
DisplayDeviceProvider::HDMIDisplayDevice::Disconnect()
{
nsresult rv = CloseTopLevelWindow();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
@@ -431,31 +429,26 @@ DisplayDeviceProvider::Observe(nsISuppor
}
}
}
return NS_OK;
}
nsresult
-DisplayDeviceProvider::RequestSession(HDMIDisplayDevice* aDevice,
- const nsAString& aUrl,
- const nsAString& aPresentationId,
- nsIPresentationControlChannel** aControlChannel)
+DisplayDeviceProvider::Connect(HDMIDisplayDevice* aDevice,
+ nsIPresentationControlChannel** aControlChannel)
{
MOZ_ASSERT(aDevice);
MOZ_ASSERT(mPresentationService);
NS_ENSURE_ARG_POINTER(aControlChannel);
*aControlChannel = nullptr;
nsCOMPtr<nsITCPDeviceInfo> deviceInfo = new TCPDeviceInfo(aDevice->Id(),
aDevice->Address(),
mPort);
- return mPresentationService->RequestSession(deviceInfo,
- aUrl,
- aPresentationId,
- aControlChannel);
+ return mPresentationService->Connect(deviceInfo, aControlChannel);
}
} // namespace presentation
} // namespace dom
} // namespace mozilla
--- a/dom/presentation/provider/DisplayDeviceProvider.h
+++ b/dom/presentation/provider/DisplayDeviceProvider.h
@@ -88,20 +88,18 @@ private:
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
NS_DECL_NSIPRESENTATIONDEVICEPROVIDER
NS_DECL_NSIPRESENTATIONCONTROLSERVERLISTENER
// For using WeakPtr when MOZ_REFCOUNTED_LEAK_CHECKING defined
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(DisplayDeviceProvider)
- nsresult RequestSession(HDMIDisplayDevice* aDevice,
- const nsAString& aUrl,
- const nsAString& aPresentationId,
- nsIPresentationControlChannel** aControlChannel);
+ nsresult Connect(HDMIDisplayDevice* aDevice,
+ nsIPresentationControlChannel** aControlChannel);
private:
virtual ~DisplayDeviceProvider();
nsresult Init();
nsresult Uninit();
nsresult AddExternalScreen();
nsresult RemoveExternalScreen();
copy from dom/presentation/provider/MulticastDNSDeviceProvider.cpp
copy to dom/presentation/provider/LegacyMDNSDeviceProvider.cpp
--- a/dom/presentation/provider/MulticastDNSDeviceProvider.cpp
+++ b/dom/presentation/provider/LegacyMDNSDeviceProvider.cpp
@@ -1,122 +1,111 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#include "MulticastDNSDeviceProvider.h"
+#include "LegacyMDNSDeviceProvider.h"
#include "MainThreadUtils.h"
#include "mozilla/Logging.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/unused.h"
#include "nsComponentManagerUtils.h"
#include "nsIObserverService.h"
+#include "nsIWritablePropertyBag2.h"
#include "nsServiceManagerUtils.h"
#include "nsTCPDeviceInfo.h"
#include "nsThreadUtils.h"
+#include "nsIPropertyBag2.h"
-#ifdef MOZ_WIDGET_ANDROID
-#include "nsIPropertyBag2.h"
-#endif // MOZ_WIDGET_ANDROID
-
-#define PREF_PRESENTATION_DISCOVERY "dom.presentation.discovery.enabled"
+#define PREF_PRESENTATION_DISCOVERY_LEGACY "dom.presentation.discovery.legacy.enabled"
#define PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS "dom.presentation.discovery.timeout_ms"
-#define PREF_PRESENTATION_DISCOVERABLE "dom.presentation.discoverable"
#define PREF_PRESENTATION_DEVICE_NAME "dom.presentation.device.name"
-#define SERVICE_TYPE "_mozilla_papi._tcp."
+#define LEGACY_SERVICE_TYPE "_mozilla_papi._tcp"
-static mozilla::LazyLogModule sMulticastDNSProviderLogModule("MulticastDNSDeviceProvider");
+#define LEGACY_PRESENTATION_CONTROL_SERVICE_CONTACT_ID "@mozilla.org/presentation/legacy-control-service;1"
+
+static mozilla::LazyLogModule sLegacyMDNSProviderLogModule("LegacyMDNSDeviceProvider");
#undef LOG_I
-#define LOG_I(...) MOZ_LOG(sMulticastDNSProviderLogModule, mozilla::LogLevel::Debug, (__VA_ARGS__))
+#define LOG_I(...) MOZ_LOG(sLegacyMDNSProviderLogModule, mozilla::LogLevel::Debug, (__VA_ARGS__))
#undef LOG_E
-#define LOG_E(...) MOZ_LOG(sMulticastDNSProviderLogModule, mozilla::LogLevel::Error, (__VA_ARGS__))
+#define LOG_E(...) MOZ_LOG(sLegacyMDNSProviderLogModule, mozilla::LogLevel::Error, (__VA_ARGS__))
namespace mozilla {
namespace dom {
namespace presentation {
+namespace legacy {
static const char* kObservedPrefs[] = {
- PREF_PRESENTATION_DISCOVERY,
+ PREF_PRESENTATION_DISCOVERY_LEGACY,
PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS,
- PREF_PRESENTATION_DISCOVERABLE,
PREF_PRESENTATION_DEVICE_NAME,
nullptr
};
namespace {
-#ifdef MOZ_WIDGET_ANDROID
static void
GetAndroidDeviceName(nsACString& aRetVal)
{
nsCOMPtr<nsIPropertyBag2> infoService = do_GetService("@mozilla.org/system-info;1");
MOZ_ASSERT(infoService, "Could not find a system info service");
Unused << NS_WARN_IF(NS_FAILED(infoService->GetPropertyAsACString(
NS_LITERAL_STRING("device"), aRetVal)));
}
-#endif // MOZ_WIDGET_ANDROID
} //anonymous namespace
/**
* This wrapper is used to break circular-reference problem.
*/
class DNSServiceWrappedListener final
: public nsIDNSServiceDiscoveryListener
- , public nsIDNSRegistrationListener
, public nsIDNSServiceResolveListener
- , public nsIPresentationControlServerListener
{
public:
NS_DECL_ISUPPORTS
NS_FORWARD_SAFE_NSIDNSSERVICEDISCOVERYLISTENER(mListener)
- NS_FORWARD_SAFE_NSIDNSREGISTRATIONLISTENER(mListener)
NS_FORWARD_SAFE_NSIDNSSERVICERESOLVELISTENER(mListener)
- NS_FORWARD_SAFE_NSIPRESENTATIONCONTROLSERVERLISTENER(mListener)
explicit DNSServiceWrappedListener() = default;
- nsresult SetListener(MulticastDNSDeviceProvider* aListener)
+ nsresult SetListener(LegacyMDNSDeviceProvider* aListener)
{
mListener = aListener;
return NS_OK;
}
private:
virtual ~DNSServiceWrappedListener() = default;
- MulticastDNSDeviceProvider* mListener = nullptr;
+ LegacyMDNSDeviceProvider* mListener = nullptr;
};
NS_IMPL_ISUPPORTS(DNSServiceWrappedListener,
nsIDNSServiceDiscoveryListener,
- nsIDNSRegistrationListener,
- nsIDNSServiceResolveListener,
- nsIPresentationControlServerListener)
+ nsIDNSServiceResolveListener)
-NS_IMPL_ISUPPORTS(MulticastDNSDeviceProvider,
+NS_IMPL_ISUPPORTS(LegacyMDNSDeviceProvider,
nsIPresentationDeviceProvider,
nsIDNSServiceDiscoveryListener,
- nsIDNSRegistrationListener,
nsIDNSServiceResolveListener,
- nsIPresentationControlServerListener,
nsIObserver)
-MulticastDNSDeviceProvider::~MulticastDNSDeviceProvider()
+LegacyMDNSDeviceProvider::~LegacyMDNSDeviceProvider()
{
Uninit();
}
nsresult
-MulticastDNSDeviceProvider::Init()
+LegacyMDNSDeviceProvider::Init()
{
MOZ_ASSERT(NS_IsMainThread());
if (mInitialized) {
return NS_OK;
}
nsresult rv;
@@ -126,165 +115,76 @@ MulticastDNSDeviceProvider::Init()
return rv;
}
mWrappedListener = new DNSServiceWrappedListener();
if (NS_WARN_IF(NS_FAILED(rv = mWrappedListener->SetListener(this)))) {
return rv;
}
- mPresentationService = do_CreateInstance(PRESENTATION_CONTROL_SERVICE_CONTACT_ID, &rv);
+ mPresentationService = do_CreateInstance(LEGACY_PRESENTATION_CONTROL_SERVICE_CONTACT_ID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mDiscoveryTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
Preferences::AddStrongObservers(this, kObservedPrefs);
- mDiscoveryEnabled = Preferences::GetBool(PREF_PRESENTATION_DISCOVERY);
+ mDiscoveryEnabled = Preferences::GetBool(PREF_PRESENTATION_DISCOVERY_LEGACY);
mDiscoveryTimeoutMs = Preferences::GetUint(PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS);
- mDiscoverable = Preferences::GetBool(PREF_PRESENTATION_DISCOVERABLE);
mServiceName = Preferences::GetCString(PREF_PRESENTATION_DEVICE_NAME);
-#ifdef MOZ_WIDGET_ANDROID
// FIXME: Bug 1185806 - Provide a common device name setting.
if (mServiceName.IsEmpty()) {
GetAndroidDeviceName(mServiceName);
Unused << Preferences::SetCString(PREF_PRESENTATION_DEVICE_NAME, mServiceName);
}
-#endif // MOZ_WIDGET_ANDROID
Unused << mPresentationService->SetId(mServiceName);
if (mDiscoveryEnabled && NS_WARN_IF(NS_FAILED(rv = ForceDiscovery()))) {
return rv;
}
- if (mDiscoverable && NS_WARN_IF(NS_FAILED(rv = RegisterService()))) {
- return rv;
- }
-
mInitialized = true;
return NS_OK;
}
nsresult
-MulticastDNSDeviceProvider::Uninit()
+LegacyMDNSDeviceProvider::Uninit()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mInitialized) {
return NS_OK;
}
ClearDevices();
Preferences::RemoveObservers(this, kObservedPrefs);
StopDiscovery(NS_OK);
- UnregisterService(NS_OK);
mMulticastDNS = nullptr;
if (mWrappedListener) {
mWrappedListener->SetListener(nullptr);
mWrappedListener = nullptr;
}
mInitialized = false;
return NS_OK;
}
nsresult
-MulticastDNSDeviceProvider::RegisterService()
-{
- LOG_I("RegisterService: %s (%d)", mServiceName.get(), mDiscoverable);
- MOZ_ASSERT(NS_IsMainThread());
-
- if (!mDiscoverable) {
- return NS_OK;
- }
-
- nsresult rv;
-
- uint16_t servicePort;
- if (NS_WARN_IF(NS_FAILED(rv = mPresentationService->GetPort(&servicePort)))) {
- return rv;
- }
-
- /**
- * If |servicePort| is non-zero, it means PresentationServer is running.
- * Otherwise, we should make it start serving.
- */
- if (!servicePort) {
- if (NS_WARN_IF(NS_FAILED(rv = mPresentationService->SetListener(mWrappedListener)))) {
- return rv;
- }
- if (NS_WARN_IF(NS_FAILED(rv = mPresentationService->StartServer(0)))) {
- return rv;
- }
- if (NS_WARN_IF(NS_FAILED(rv = mPresentationService->GetPort(&servicePort)))) {
- return rv;
- }
- }
-
- // Cancel on going service registration.
- if (mRegisterRequest) {
- mRegisterRequest->Cancel(NS_OK);
- mRegisterRequest = nullptr;
- }
-
- /**
- * Register the presentation control channel server as an mDNS service.
- */
- nsCOMPtr<nsIDNSServiceInfo> serviceInfo =
- do_CreateInstance(DNSSERVICEINFO_CONTRACT_ID, &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (NS_WARN_IF(NS_FAILED(rv = serviceInfo->SetServiceType(
- NS_LITERAL_CSTRING(SERVICE_TYPE))))) {
- return rv;
- }
- if (NS_WARN_IF(NS_FAILED(rv = serviceInfo->SetServiceName(mServiceName)))) {
- return rv;
- }
- if (NS_WARN_IF(NS_FAILED(rv = serviceInfo->SetPort(servicePort)))) {
- return rv;
- }
-
- return mMulticastDNS->RegisterService(serviceInfo,
- mWrappedListener,
- getter_AddRefs(mRegisterRequest));
-}
-
-nsresult
-MulticastDNSDeviceProvider::UnregisterService(nsresult aReason)
-{
- MOZ_ASSERT(NS_IsMainThread());
-
- if (mRegisterRequest) {
- mRegisterRequest->Cancel(aReason);
- mRegisterRequest = nullptr;
- }
-
- if (mPresentationService) {
- mPresentationService->SetListener(nullptr);
- mPresentationService->Close();
- }
-
- return NS_OK;
-}
-
-nsresult
-MulticastDNSDeviceProvider::StopDiscovery(nsresult aReason)
+LegacyMDNSDeviceProvider::StopDiscovery(nsresult aReason)
{
LOG_I("StopDiscovery (0x%08x)", aReason);
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mDiscoveryTimer);
Unused << mDiscoveryTimer->Cancel();
@@ -292,37 +192,35 @@ MulticastDNSDeviceProvider::StopDiscover
mDiscoveryRequest->Cancel(aReason);
mDiscoveryRequest = nullptr;
}
return NS_OK;
}
nsresult
-MulticastDNSDeviceProvider::RequestSession(Device* aDevice,
- const nsAString& aUrl,
- const nsAString& aPresentationId,
- nsIPresentationControlChannel** aRetVal)
+LegacyMDNSDeviceProvider::Connect(Device* aDevice,
+ nsIPresentationControlChannel** aRetVal)
{
MOZ_ASSERT(aDevice);
MOZ_ASSERT(mPresentationService);
RefPtr<TCPDeviceInfo> deviceInfo = new TCPDeviceInfo(aDevice->Id(),
aDevice->Address(),
aDevice->Port());
- return mPresentationService->RequestSession(deviceInfo, aUrl, aPresentationId, aRetVal);
+ return mPresentationService->Connect(deviceInfo, aRetVal);
}
nsresult
-MulticastDNSDeviceProvider::AddDevice(const nsACString& aId,
- const nsACString& aServiceName,
- const nsACString& aServiceType,
- const nsACString& aAddress,
- const uint16_t aPort)
+LegacyMDNSDeviceProvider::AddDevice(const nsACString& aId,
+ const nsACString& aServiceName,
+ const nsACString& aServiceType,
+ const nsACString& aAddress,
+ const uint16_t aPort)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mPresentationService);
RefPtr<Device> device = new Device(aId, /* ID */
aServiceName,
aServiceType,
aAddress,
@@ -336,21 +234,21 @@ MulticastDNSDeviceProvider::AddDevice(co
}
mDevices.AppendElement(device);
return NS_OK;
}
nsresult
-MulticastDNSDeviceProvider::UpdateDevice(const uint32_t aIndex,
- const nsACString& aServiceName,
- const nsACString& aServiceType,
- const nsACString& aAddress,
- const uint16_t aPort)
+LegacyMDNSDeviceProvider::UpdateDevice(const uint32_t aIndex,
+ const nsACString& aServiceName,
+ const nsACString& aServiceType,
+ const nsACString& aAddress,
+ const uint16_t aPort)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mPresentationService);
if (NS_WARN_IF(aIndex >= mDevices.Length())) {
return NS_ERROR_INVALID_ARG;
}
@@ -362,17 +260,17 @@ MulticastDNSDeviceProvider::UpdateDevice
if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) {
Unused << listener->UpdateDevice(device);
}
return NS_OK;
}
nsresult
-MulticastDNSDeviceProvider::RemoveDevice(const uint32_t aIndex)
+LegacyMDNSDeviceProvider::RemoveDevice(const uint32_t aIndex)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mPresentationService);
if (NS_WARN_IF(aIndex >= mDevices.Length())) {
return NS_ERROR_INVALID_ARG;
}
@@ -385,18 +283,18 @@ MulticastDNSDeviceProvider::RemoveDevice
if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) {
Unused << listener->RemoveDevice(device);
}
return NS_OK;
}
bool
-MulticastDNSDeviceProvider::FindDeviceById(const nsACString& aId,
- uint32_t& aIndex)
+LegacyMDNSDeviceProvider::FindDeviceById(const nsACString& aId,
+ uint32_t& aIndex)
{
MOZ_ASSERT(NS_IsMainThread());
RefPtr<Device> device = new Device(aId,
/* aName = */ EmptyCString(),
/* aType = */ EmptyCString(),
/* aHost = */ EmptyCString(),
/* aPort = */ 0,
@@ -408,18 +306,18 @@ MulticastDNSDeviceProvider::FindDeviceBy
return false;
}
aIndex = index;
return true;
}
bool
-MulticastDNSDeviceProvider::FindDeviceByAddress(const nsACString& aAddress,
- uint32_t& aIndex)
+LegacyMDNSDeviceProvider::FindDeviceByAddress(const nsACString& aAddress,
+ uint32_t& aIndex)
{
MOZ_ASSERT(NS_IsMainThread());
RefPtr<Device> device = new Device(/* aId = */ EmptyCString(),
/* aName = */ EmptyCString(),
/* aType = */ EmptyCString(),
aAddress,
/* aPort = */ 0,
@@ -431,54 +329,54 @@ MulticastDNSDeviceProvider::FindDeviceBy
return false;
}
aIndex = index;
return true;
}
void
-MulticastDNSDeviceProvider::MarkAllDevicesUnknown()
+LegacyMDNSDeviceProvider::MarkAllDevicesUnknown()
{
MOZ_ASSERT(NS_IsMainThread());
for (auto& device : mDevices) {
device->ChangeState(DeviceState::eUnknown);
}
}
void
-MulticastDNSDeviceProvider::ClearUnknownDevices()
+LegacyMDNSDeviceProvider::ClearUnknownDevices()
{
MOZ_ASSERT(NS_IsMainThread());
size_t i = mDevices.Length();
while (i > 0) {
--i;
if (mDevices[i]->State() == DeviceState::eUnknown) {
NS_WARN_IF(NS_FAILED(RemoveDevice(i)));
}
}
}
void
-MulticastDNSDeviceProvider::ClearDevices()
+LegacyMDNSDeviceProvider::ClearDevices()
{
MOZ_ASSERT(NS_IsMainThread());
size_t i = mDevices.Length();
while (i > 0) {
--i;
NS_WARN_IF(NS_FAILED(RemoveDevice(i)));
}
}
// nsIPresentationDeviceProvider
NS_IMETHODIMP
-MulticastDNSDeviceProvider::GetListener(nsIPresentationDeviceListener** aListener)
+LegacyMDNSDeviceProvider::GetListener(nsIPresentationDeviceListener** aListener)
{
MOZ_ASSERT(NS_IsMainThread());
if (NS_WARN_IF(!aListener)) {
return NS_ERROR_INVALID_POINTER;
}
nsresult rv;
@@ -489,17 +387,17 @@ MulticastDNSDeviceProvider::GetListener(
}
listener.forget(aListener);
return NS_OK;
}
NS_IMETHODIMP
-MulticastDNSDeviceProvider::SetListener(nsIPresentationDeviceListener* aListener)
+LegacyMDNSDeviceProvider::SetListener(nsIPresentationDeviceListener* aListener)
{
MOZ_ASSERT(NS_IsMainThread());
mDeviceListener = do_GetWeakReference(aListener);
nsresult rv;
if (mDeviceListener) {
if (NS_WARN_IF(NS_FAILED(rv = Init()))) {
@@ -510,17 +408,17 @@ MulticastDNSDeviceProvider::SetListener(
return rv;
}
}
return NS_OK;
}
NS_IMETHODIMP
-MulticastDNSDeviceProvider::ForceDiscovery()
+LegacyMDNSDeviceProvider::ForceDiscovery()
{
LOG_I("ForceDiscovery (%d)", mDiscoveryEnabled);
MOZ_ASSERT(NS_IsMainThread());
if (!mDiscoveryEnabled) {
return NS_OK;
}
@@ -538,28 +436,28 @@ MulticastDNSDeviceProvider::ForceDiscove
return rv;
}
return NS_OK;
}
StopDiscovery(NS_OK);
if (NS_WARN_IF(NS_FAILED(rv = mMulticastDNS->StartDiscovery(
- NS_LITERAL_CSTRING(SERVICE_TYPE),
+ NS_LITERAL_CSTRING(LEGACY_SERVICE_TYPE),
mWrappedListener,
getter_AddRefs(mDiscoveryRequest))))) {
return rv;
}
return NS_OK;
}
// nsIDNSServiceDiscoveryListener
NS_IMETHODIMP
-MulticastDNSDeviceProvider::OnDiscoveryStarted(const nsACString& aServiceType)
+LegacyMDNSDeviceProvider::OnDiscoveryStarted(const nsACString& aServiceType)
{
LOG_I("OnDiscoveryStarted");
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mDiscoveryTimer);
MarkAllDevicesUnknown();
nsresult rv;
@@ -570,30 +468,30 @@ MulticastDNSDeviceProvider::OnDiscoveryS
}
mIsDiscovering = true;
return NS_OK;
}
NS_IMETHODIMP
-MulticastDNSDeviceProvider::OnDiscoveryStopped(const nsACString& aServiceType)
+LegacyMDNSDeviceProvider::OnDiscoveryStopped(const nsACString& aServiceType)
{
LOG_I("OnDiscoveryStopped");
MOZ_ASSERT(NS_IsMainThread());
ClearUnknownDevices();
mIsDiscovering = false;
return NS_OK;
}
NS_IMETHODIMP
-MulticastDNSDeviceProvider::OnServiceFound(nsIDNSServiceInfo* aServiceInfo)
+LegacyMDNSDeviceProvider::OnServiceFound(nsIDNSServiceInfo* aServiceInfo)
{
MOZ_ASSERT(NS_IsMainThread());
if (NS_WARN_IF(!aServiceInfo)) {
return NS_ERROR_INVALID_ARG;
}
nsresult rv ;
@@ -611,17 +509,17 @@ MulticastDNSDeviceProvider::OnServiceFou
return rv;
}
}
return NS_OK;
}
NS_IMETHODIMP
-MulticastDNSDeviceProvider::OnServiceLost(nsIDNSServiceInfo* aServiceInfo)
+LegacyMDNSDeviceProvider::OnServiceLost(nsIDNSServiceInfo* aServiceInfo)
{
MOZ_ASSERT(NS_IsMainThread());
if (NS_WARN_IF(!aServiceInfo)) {
return NS_ERROR_INVALID_ARG;
}
nsresult rv;
@@ -647,103 +545,38 @@ MulticastDNSDeviceProvider::OnServiceLos
if (NS_WARN_IF(NS_FAILED(rv = RemoveDevice(index)))) {
return rv;
}
return NS_OK;
}
NS_IMETHODIMP
-MulticastDNSDeviceProvider::OnStartDiscoveryFailed(const nsACString& aServiceType,
- int32_t aErrorCode)
+LegacyMDNSDeviceProvider::OnStartDiscoveryFailed(const nsACString& aServiceType,
+ int32_t aErrorCode)
{
LOG_E("OnStartDiscoveryFailed: %d", aErrorCode);
MOZ_ASSERT(NS_IsMainThread());
return NS_OK;
}
NS_IMETHODIMP
-MulticastDNSDeviceProvider::OnStopDiscoveryFailed(const nsACString& aServiceType,
- int32_t aErrorCode)
+LegacyMDNSDeviceProvider::OnStopDiscoveryFailed(const nsACString& aServiceType,
+ int32_t aErrorCode)
{
LOG_E("OnStopDiscoveryFailed: %d", aErrorCode);
MOZ_ASSERT(NS_IsMainThread());
return NS_OK;
}
-// nsIDNSRegistrationListener
-NS_IMETHODIMP
-MulticastDNSDeviceProvider::OnServiceRegistered(nsIDNSServiceInfo* aServiceInfo)
-{
- MOZ_ASSERT(NS_IsMainThread());
-
- if (NS_WARN_IF(!aServiceInfo)) {
- return NS_ERROR_INVALID_ARG;
- }
- nsresult rv;
-
- nsAutoCString name;
- if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetServiceName(name)))) {
- return rv;
- }
-
- LOG_I("OnServiceRegistered (%s)", name.get());
- mRegisteredName = name;
-
- if (mMulticastDNS) {
- if (NS_WARN_IF(NS_FAILED(rv = mMulticastDNS->ResolveService(
- aServiceInfo, mWrappedListener)))) {
- return rv;
- }
- }
-
- return NS_OK;
-}
-
-NS_IMETHODIMP
-MulticastDNSDeviceProvider::OnServiceUnregistered(nsIDNSServiceInfo* aServiceInfo)
-{
- LOG_I("OnServiceUnregistered");
- MOZ_ASSERT(NS_IsMainThread());
-
- return NS_OK;
-}
-
-NS_IMETHODIMP
-MulticastDNSDeviceProvider::OnRegistrationFailed(nsIDNSServiceInfo* aServiceInfo,
- int32_t aErrorCode)
-{
- LOG_E("OnRegistrationFailed: %d", aErrorCode);
- MOZ_ASSERT(NS_IsMainThread());
-
- mRegisterRequest = nullptr;
-
- if (aErrorCode == nsIDNSRegistrationListener::ERROR_SERVICE_NOT_RUNNING) {
- return NS_DispatchToMainThread(
- NewRunnableMethod(this, &MulticastDNSDeviceProvider::RegisterService));
- }
-
- return NS_OK;
-}
-
-NS_IMETHODIMP
-MulticastDNSDeviceProvider::OnUnregistrationFailed(nsIDNSServiceInfo* aServiceInfo,
- int32_t aErrorCode)
-{
- LOG_E("OnUnregistrationFailed: %d", aErrorCode);
- MOZ_ASSERT(NS_IsMainThread());
-
- return NS_OK;
-}
-
// nsIDNSServiceResolveListener
NS_IMETHODIMP
-MulticastDNSDeviceProvider::OnServiceResolved(nsIDNSServiceInfo* aServiceInfo)
+LegacyMDNSDeviceProvider::OnServiceResolved(nsIDNSServiceInfo* aServiceInfo)
{
MOZ_ASSERT(NS_IsMainThread());
if (NS_WARN_IF(!aServiceInfo)) {
return NS_ERROR_INVALID_ARG;
}
nsresult rv;
@@ -755,26 +588,16 @@ MulticastDNSDeviceProvider::OnServiceRes
LOG_I("OnServiceResolved: %s", serviceName.get());
nsAutoCString host;
if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetHost(host)))) {
return rv;
}
- if (mRegisteredName == serviceName) {
- LOG_I("ignore self");
-
- if (NS_WARN_IF(NS_FAILED(rv = mPresentationService->SetId(host)))) {
- return rv;
- }
-
- return NS_OK;
- }
-
nsAutoCString address;
if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetAddress(address)))) {
return rv;
}
uint16_t port;
if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetPort(&port)))) {
return rv;
@@ -799,218 +622,135 @@ MulticastDNSDeviceProvider::OnServiceRes
address,
port);
}
return NS_OK;
}
NS_IMETHODIMP
-MulticastDNSDeviceProvider::OnResolveFailed(nsIDNSServiceInfo* aServiceInfo,
- int32_t aErrorCode)
+LegacyMDNSDeviceProvider::OnResolveFailed(nsIDNSServiceInfo* aServiceInfo,
+ int32_t aErrorCode)
{
LOG_E("OnResolveFailed: %d", aErrorCode);
MOZ_ASSERT(NS_IsMainThread());
return NS_OK;
}
-// nsIPresentationControlServerListener
-NS_IMETHODIMP
-MulticastDNSDeviceProvider::OnPortChange(uint16_t aPort)
-{
- LOG_I("OnPortChange: %d", aPort);
- MOZ_ASSERT(NS_IsMainThread());
-
- if (mDiscoverable) {
- RegisterService();
- }
-
- return NS_OK;
-}
-
-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;
- 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;
-}
-
// nsIObserver
NS_IMETHODIMP
-MulticastDNSDeviceProvider::Observe(nsISupports* aSubject,
- const char* aTopic,
- const char16_t* aData)
+LegacyMDNSDeviceProvider::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ConvertUTF16toUTF8 data(aData);
LOG_I("Observe: topic = %s, data = %s", aTopic, data.get());
if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
- if (data.EqualsLiteral(PREF_PRESENTATION_DISCOVERY)) {
- OnDiscoveryChanged(Preferences::GetBool(PREF_PRESENTATION_DISCOVERY));
+ if (data.EqualsLiteral(PREF_PRESENTATION_DISCOVERY_LEGACY)) {
+ OnDiscoveryChanged(Preferences::GetBool(PREF_PRESENTATION_DISCOVERY_LEGACY));
} else if (data.EqualsLiteral(PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS)) {
OnDiscoveryTimeoutChanged(Preferences::GetUint(PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS));
- } else if (data.EqualsLiteral(PREF_PRESENTATION_DISCOVERABLE)) {
- OnDiscoverableChanged(Preferences::GetBool(PREF_PRESENTATION_DISCOVERABLE));
} else if (data.EqualsLiteral(PREF_PRESENTATION_DEVICE_NAME)) {
nsAdoptingCString newServiceName = Preferences::GetCString(PREF_PRESENTATION_DEVICE_NAME);
if (!mServiceName.Equals(newServiceName)) {
OnServiceNameChanged(newServiceName);
}
}
} else if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) {
StopDiscovery(NS_OK);
}
return NS_OK;
}
nsresult
-MulticastDNSDeviceProvider::OnDiscoveryChanged(bool aEnabled)
+LegacyMDNSDeviceProvider::OnDiscoveryChanged(bool aEnabled)
{
LOG_I("DiscoveryEnabled = %d\n", aEnabled);
MOZ_ASSERT(NS_IsMainThread());
mDiscoveryEnabled = aEnabled;
if (mDiscoveryEnabled) {
return ForceDiscovery();
}
return StopDiscovery(NS_OK);
}
nsresult
-MulticastDNSDeviceProvider::OnDiscoveryTimeoutChanged(uint32_t aTimeoutMs)
+LegacyMDNSDeviceProvider::OnDiscoveryTimeoutChanged(uint32_t aTimeoutMs)
{
LOG_I("OnDiscoveryTimeoutChanged = %d\n", aTimeoutMs);
MOZ_ASSERT(NS_IsMainThread());
mDiscoveryTimeoutMs = aTimeoutMs;
return NS_OK;
}
nsresult
-MulticastDNSDeviceProvider::OnDiscoverableChanged(bool aEnabled)
-{
- LOG_I("Discoverable = %d\n", aEnabled);
- MOZ_ASSERT(NS_IsMainThread());
-
- mDiscoverable = aEnabled;
-
- if (mDiscoverable) {
- return RegisterService();
- }
-
- return UnregisterService(NS_OK);
-}
-
-nsresult
-MulticastDNSDeviceProvider::OnServiceNameChanged(const nsACString& aServiceName)
+LegacyMDNSDeviceProvider::OnServiceNameChanged(const nsACString& aServiceName)
{
LOG_I("serviceName = %s\n", PromiseFlatCString(aServiceName).get());
MOZ_ASSERT(NS_IsMainThread());
mServiceName = aServiceName;
-
- nsresult rv;
- if (NS_WARN_IF(NS_FAILED(rv = UnregisterService(NS_OK)))) {
- return rv;
- }
-
- if (mDiscoverable) {
- return RegisterService();
- }
+ mPresentationService->SetId(mServiceName);
return NS_OK;
}
-// MulticastDNSDeviceProvider::Device
-NS_IMPL_ISUPPORTS(MulticastDNSDeviceProvider::Device,
+// LegacyMDNSDeviceProvider::Device
+NS_IMPL_ISUPPORTS(LegacyMDNSDeviceProvider::Device,
nsIPresentationDevice)
// nsIPresentationDevice
NS_IMETHODIMP
-MulticastDNSDeviceProvider::Device::GetId(nsACString& aId)
+LegacyMDNSDeviceProvider::Device::GetId(nsACString& aId)
{
aId = mId;
return NS_OK;
}
NS_IMETHODIMP
-MulticastDNSDeviceProvider::Device::GetName(nsACString& aName)
+LegacyMDNSDeviceProvider::Device::GetName(nsACString& aName)
{
aName = mName;
return NS_OK;
}
NS_IMETHODIMP
-MulticastDNSDeviceProvider::Device::GetType(nsACString& aType)
+LegacyMDNSDeviceProvider::Device::GetType(nsACString& aType)
{
aType = mType;
return NS_OK;
}
NS_IMETHODIMP
-MulticastDNSDeviceProvider::Device::EstablishControlChannel(const nsAString& aUrl,
- const nsAString& aPresentationId,
- nsIPresentationControlChannel** aRetVal)
+LegacyMDNSDeviceProvider::Device::EstablishControlChannel(
+ nsIPresentationControlChannel** aRetVal)
{
if (!mProvider) {
return NS_ERROR_FAILURE;
}
- return mProvider->RequestSession(this, aUrl, aPresentationId, aRetVal);
+ return mProvider->Connect(this, aRetVal);
}
NS_IMETHODIMP
-MulticastDNSDeviceProvider::Device::Disconnect()
+LegacyMDNSDeviceProvider::Device::Disconnect()
{
// No need to do anything when disconnect.
return NS_OK;
}
+} // namespace legacy
} // namespace presentation
} // namespace dom
} // namespace mozilla
copy from dom/presentation/provider/MulticastDNSDeviceProvider.h
copy to dom/presentation/provider/LegacyMDNSDeviceProvider.h
--- a/dom/presentation/provider/MulticastDNSDeviceProvider.h
+++ b/dom/presentation/provider/LegacyMDNSDeviceProvider.h
@@ -1,15 +1,15 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#ifndef mozilla_dom_presentation_provider_MulticastDNSDeviceProvider_h
-#define mozilla_dom_presentation_provider_MulticastDNSDeviceProvider_h
+#ifndef mozilla_dom_presentation_provider_LegacyMDNSDeviceProvider_h
+#define mozilla_dom_presentation_provider_LegacyMDNSDeviceProvider_h
#include "mozilla/RefPtr.h"
#include "nsCOMPtr.h"
#include "nsICancelable.h"
#include "nsIDNSServiceDiscovery.h"
#include "nsIObserver.h"
#include "nsIPresentationDevice.h"
#include "nsIPresentationDeviceProvider.h"
@@ -17,38 +17,35 @@
#include "nsITimer.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsWeakPtr.h"
namespace mozilla {
namespace dom {
namespace presentation {
+namespace legacy {
class DNSServiceWrappedListener;
class MulticastDNSService;
-class MulticastDNSDeviceProvider final
+class LegacyMDNSDeviceProvider final
: public nsIPresentationDeviceProvider
, public nsIDNSServiceDiscoveryListener
- , public nsIDNSRegistrationListener
, public nsIDNSServiceResolveListener
- , public nsIPresentationControlServerListener
, public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPRESENTATIONDEVICEPROVIDER
NS_DECL_NSIDNSSERVICEDISCOVERYLISTENER
- NS_DECL_NSIDNSREGISTRATIONLISTENER
NS_DECL_NSIDNSSERVICERESOLVELISTENER
- NS_DECL_NSIPRESENTATIONCONTROLSERVERLISTENER
NS_DECL_NSIOBSERVER
- explicit MulticastDNSDeviceProvider() = default;
+ explicit LegacyMDNSDeviceProvider() = default;
nsresult Init();
nsresult Uninit();
private:
enum class DeviceState : uint32_t {
eUnknown,
eActive
};
@@ -60,17 +57,17 @@ private:
NS_DECL_NSIPRESENTATIONDEVICE
explicit Device(const nsACString& aId,
const nsACString& aName,
const nsACString& aType,
const nsACString& aAddress,
const uint16_t aPort,
DeviceState aState,
- MulticastDNSDeviceProvider* aProvider)
+ LegacyMDNSDeviceProvider* aProvider)
: mId(aId)
, mName(aName)
, mType(aType)
, mAddress(aAddress)
, mPort(aPort)
, mState(aState)
, mProvider(aProvider)
{
@@ -116,39 +113,35 @@ private:
virtual ~Device() = default;
nsCString mId;
nsCString mName;
nsCString mType;
nsCString mAddress;
uint16_t mPort;
DeviceState mState;
- MulticastDNSDeviceProvider* mProvider;
+ LegacyMDNSDeviceProvider* mProvider;
};
struct DeviceIdComparator {
bool Equals(const RefPtr<Device>& aA, const RefPtr<Device>& aB) const {
return aA->Id() == aB->Id();
}
};
struct DeviceAddressComparator {
bool Equals(const RefPtr<Device>& aA, const RefPtr<Device>& aB) const {
return aA->Address() == aB->Address();
}
};
- virtual ~MulticastDNSDeviceProvider();
- nsresult RegisterService();
- nsresult UnregisterService(nsresult aReason);
+ virtual ~LegacyMDNSDeviceProvider();
nsresult StopDiscovery(nsresult aReason);
- nsresult RequestSession(Device* aDevice,
- const nsAString& aUrl,
- const nsAString& aPresentationId,
- nsIPresentationControlChannel** aRetVal);
+ nsresult Connect(Device* aDevice,
+ nsIPresentationControlChannel** aRetVal);
// device manipulation
nsresult AddDevice(const nsACString& aId,
const nsACString& aServiceName,
const nsACString& aServiceType,
const nsACString& aAddress,
const uint16_t aPort);
nsresult UpdateDevice(const uint32_t aIndex,
@@ -165,38 +158,34 @@ private:
void MarkAllDevicesUnknown();
void ClearUnknownDevices();
void ClearDevices();
// preferences
nsresult OnDiscoveryChanged(bool aEnabled);
nsresult OnDiscoveryTimeoutChanged(uint32_t aTimeoutMs);
- nsresult OnDiscoverableChanged(bool aEnabled);
nsresult OnServiceNameChanged(const nsACString& aServiceName);
bool mInitialized = false;
nsWeakPtr mDeviceListener;
nsCOMPtr<nsIPresentationControlService> mPresentationService;
nsCOMPtr<nsIDNSServiceDiscovery> mMulticastDNS;
RefPtr<DNSServiceWrappedListener> mWrappedListener;
nsCOMPtr<nsICancelable> mDiscoveryRequest;
- nsCOMPtr<nsICancelable> mRegisterRequest;
nsTArray<RefPtr<Device>> mDevices;
bool mDiscoveryEnabled = false;
bool mIsDiscovering = false;
uint32_t mDiscoveryTimeoutMs;
nsCOMPtr<nsITimer> mDiscoveryTimer;
- bool mDiscoverable = false;
-
nsCString mServiceName;
- nsCString mRegisteredName;
};
+} // namespace legacy
} // namespace presentation
} // namespace dom
} // namespace mozilla
-#endif // mozilla_dom_presentation_provider_MulticastDNSDeviceProvider_h
+#endif // mozilla_dom_presentation_provider_LegacyMDNSDeviceProvider_h
new file mode 100644
--- /dev/null
+++ b/dom/presentation/provider/LegacyPresentationControlService.js
@@ -0,0 +1,476 @@
+/* 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/. */
+/* jshint esnext:true, globalstrict:true, moz:true, undef:true, unused:true */
+/* globals Components, dump */
+"use strict";
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+/* globals XPCOMUtils */
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+/* globals Services */
+Cu.import("resource://gre/modules/Services.jsm");
+/* globals NetUtil */
+Cu.import("resource://gre/modules/NetUtil.jsm");
+
+const DEBUG = Services.prefs.getBoolPref("dom.presentation.tcp_server.debug");
+function log(aMsg) {
+ dump("-*- LegacyPresentationControlService.js: " + aMsg + "\n");
+}
+
+function LegacyPresentationControlService() {
+ DEBUG && log("LegacyPresentationControlService - ctor"); //jshint ignore:line
+ this._id = null;
+}
+
+LegacyPresentationControlService.prototype = {
+ startServer: function() {
+ DEBUG && log("LegacyPresentationControlService - doesn't support receiver mode"); //jshint ignore:line
+ throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ get id() {
+ return this._id;
+ },
+
+ set id(aId) {
+ this._id = aId;
+ },
+
+ get port() {
+ return 0;
+ },
+
+ get version() {
+ return 0;
+ },
+
+ set listener(aListener) { //jshint ignore:line
+ DEBUG && log("LegacyPresentationControlService - doesn't support receiver mode"); //jshint ignore:line
+ throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ get listener() {
+ return null;
+ },
+
+ connect: function(aDeviceInfo) {
+ if (!this.id) {
+ DEBUG && log("LegacyPresentationControlService - Id has not initialized; requestSession fails"); //jshint ignore:line
+ return null;
+ }
+ DEBUG && log("LegacyPresentationControlService - requestSession to " + aDeviceInfo.id); //jshint ignore:line
+
+ let sts = Cc["@mozilla.org/network/socket-transport-service;1"]
+ .getService(Ci.nsISocketTransportService);
+
+ let socketTransport;
+ try {
+ socketTransport = sts.createTransport(null,
+ 0,
+ aDeviceInfo.address,
+ aDeviceInfo.port,
+ null);
+ } catch (e) {
+ DEBUG && log("LegacyPresentationControlService - createTransport throws: " + e); //jshint ignore:line
+ // Pop the exception to |TCPDevice.establishControlChannel|
+ throw Cr.NS_ERROR_FAILURE;
+ }
+ return new LegacyTCPControlChannel(this.id,
+ socketTransport,
+ aDeviceInfo);
+ },
+
+ close: function() {
+ DEBUG && log("LegacyPresentationControlService - close"); //jshint ignore:line
+ },
+
+ classID: Components.ID("{b21816fe-8aff-4811-86d2-85a7444c557e}"),
+ QueryInterface : XPCOMUtils.generateQI([Ci.nsIPresentationControlService]),
+};
+
+function ChannelDescription(aInit) {
+ this._type = aInit.type;
+ switch (this._type) {
+ case Ci.nsIPresentationChannelDescription.TYPE_TCP:
+ this._tcpAddresses = Cc["@mozilla.org/array;1"]
+ .createInstance(Ci.nsIMutableArray);
+ for (let address of aInit.tcpAddress) {
+ let wrapper = Cc["@mozilla.org/supports-cstring;1"]
+ .createInstance(Ci.nsISupportsCString);
+ wrapper.data = address;
+ this._tcpAddresses.appendElement(wrapper, false);
+ }
+
+ this._tcpPort = aInit.tcpPort;
+ break;
+ case Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL:
+ this._dataChannelSDP = aInit.dataChannelSDP;
+ break;
+ }
+}
+
+ChannelDescription.prototype = {
+ _type: 0,
+ _tcpAddresses: null,
+ _tcpPort: 0,
+ _dataChannelSDP: "",
+
+ get type() {
+ return this._type;
+ },
+
+ get tcpAddress() {
+ return this._tcpAddresses;
+ },
+
+ get tcpPort() {
+ return this._tcpPort;
+ },
+
+ get dataChannelSDP() {
+ return this._dataChannelSDP;
+ },
+
+ classID: Components.ID("{d69fc81c-4f40-47a3-97e6-b4cf5db2294e}"),
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationChannelDescription]),
+};
+
+// Helper function: transfer nsIPresentationChannelDescription to json
+function discriptionAsJson(aDescription) {
+ let json = {};
+ json.type = aDescription.type;
+ switch(aDescription.type) {
+ case Ci.nsIPresentationChannelDescription.TYPE_TCP:
+ let addresses = aDescription.tcpAddress.QueryInterface(Ci.nsIArray);
+ json.tcpAddress = [];
+ for (let idx = 0; idx < addresses.length; idx++) {
+ let address = addresses.queryElementAt(idx, Ci.nsISupportsCString);
+ json.tcpAddress.push(address.data);
+ }
+ json.tcpPort = aDescription.tcpPort;
+ break;
+ case Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL:
+ json.dataChannelSDP = aDescription.dataChannelSDP;
+ break;
+ }
+ return json;
+}
+
+function LegacyTCPControlChannel(id,
+ transport,
+ deviceInfo) {
+ DEBUG && log("create LegacyTCPControlChannel"); //jshint ignore:line
+ this._deviceInfo = deviceInfo;
+ this._transport = transport;
+
+ this._id = id;
+
+ let currentThread = Services.tm.currentThread;
+ transport.setEventSink(this, currentThread);
+
+ this._input = this._transport.openInputStream(0, 0, 0)
+ .QueryInterface(Ci.nsIAsyncInputStream);
+ this._input.asyncWait(this.QueryInterface(Ci.nsIStreamListener),
+ Ci.nsIAsyncInputStream.WAIT_CLOSURE_ONLY,
+ 0,
+ currentThread);
+
+ this._output = this._transport
+ .openOutputStream(Ci.nsITransport.OPEN_UNBUFFERED, 0, 0);
+}
+
+LegacyTCPControlChannel.prototype = {
+ _connected: false,
+ _pendingOpen: false,
+ _pendingAnswer: null,
+ _pendingClose: null,
+ _pendingCloseReason: null,
+
+ _sendMessage: function(aJSONData, aOnThrow) {
+ if (!aOnThrow) {
+ aOnThrow = function(e) {throw e.result;};
+ }
+
+ if (!aJSONData) {
+ aOnThrow();
+ return;
+ }
+
+ if (!this._connected) {
+ DEBUG && log("LegacyTCPControlChannel - send" + aJSONData.type + " fails"); //jshint ignore:line
+ throw Cr.NS_ERROR_FAILURE;
+ }
+
+ try {
+ this._send(aJSONData);
+ } catch (e) {
+ aOnThrow(e);
+ }
+ },
+
+ _sendInit: function() {
+ let msg = {
+ type: "requestSession:Init",
+ presentationId: this._presentationId,
+ url: this._url,
+ id: this._id,
+ };
+
+ this._sendMessage(msg, function(e) {
+ this.disconnect();
+ this._notifyDisconnected(e.result);
+ });
+ },
+
+ launch: function(aPresentationId, aUrl) {
+ this._presentationId = aPresentationId;
+ this._url = aUrl;
+
+ this._sendInit();
+ },
+
+ sendOffer: function(aOffer) {
+ let msg = {
+ type: "requestSession:Offer",
+ presentationId: this._presentationId,
+ offer: discriptionAsJson(aOffer),
+ };
+ this._sendMessage(msg);
+ },
+
+ sendAnswer: function(aAnswer) { //jshint ignore:line
+ throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ sendIceCandidate: function(aCandidate) {
+ let msg = {
+ type: "requestSession:IceCandidate",
+ presentationId: this._presentationId,
+ iceCandidate: aCandidate,
+ };
+ this._sendMessage(msg);
+ },
+ // may throw an exception
+ _send: function(aMsg) {
+ DEBUG && log("LegacyTCPControlChannel - Send: " + JSON.stringify(aMsg, null, 2)); //jshint ignore:line
+
+ /**
+ * XXX In TCP streaming, it is possible that more than one message in one
+ * TCP packet. We use line delimited JSON to identify where one JSON encoded
+ * object ends and the next begins. Therefore, we do not allow newline
+ * characters whithin the whole message, and add a newline at the end.
+ * Please see the parser code in |onDataAvailable|.
+ */
+ let message = JSON.stringify(aMsg).replace(["\n"], "") + "\n";
+ try {
+ this._output.write(message, message.length);
+ } catch(e) {
+ DEBUG && log("LegacyTCPControlChannel - Failed to send message: " + e.name); //jshint ignore:line
+ throw e;
+ }
+ },
+
+ // nsIAsyncInputStream (Triggered by nsIInputStream.asyncWait)
+ // Only used for detecting connection refused
+ onInputStreamReady: function(aStream) {
+ try {
+ aStream.available();
+ } catch (e) {
+ DEBUG && log("LegacyTCPControlChannel - onInputStreamReady error: " + e.name); //jshint ignore:line
+ // NS_ERROR_CONNECTION_REFUSED
+ this._listener.notifyDisconnected(e.result);
+ }
+ },
+
+ // nsITransportEventSink (Triggered by nsISocketTransport.setEventSink)
+ onTransportStatus: function(aTransport, aStatus, aProg, aProgMax) { //jshint ignore:line
+ DEBUG && log("LegacyTCPControlChannel - onTransportStatus: "
+ + aStatus.toString(16)); //jshint ignore:line
+ if (aStatus === Ci.nsISocketTransport.STATUS_CONNECTED_TO) {
+ this._connected = true;
+
+ if (!this._pump) {
+ this._createInputStreamPump();
+ }
+
+ this._notifyConnected();
+ }
+ },
+
+ // nsIRequestObserver (Triggered by nsIInputStreamPump.asyncRead)
+ onStartRequest: function() {
+ DEBUG && log("LegacyTCPControlChannel - onStartRequest"); //jshint ignore:line
+ },
+
+ // nsIRequestObserver (Triggered by nsIInputStreamPump.asyncRead)
+ onStopRequest: function(aRequest, aContext, aStatus) {
+ DEBUG && log("LegacyTCPControlChannel - onStopRequest: " + aStatus); //jshint ignore:line
+ this.disconnect(aStatus);
+ this._notifyDisconnected(aStatus);
+ },
+
+ // nsIStreamListener (Triggered by nsIInputStreamPump.asyncRead)
+ onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, aCount) { //jshint ignore:line
+ let data = NetUtil.readInputStreamToString(aInputStream,
+ aInputStream.available());
+ DEBUG && log("LegacyTCPControlChannel - onDataAvailable: " + data); //jshint ignore:line
+
+ // Parser of line delimited JSON. Please see |_send| for more informaiton.
+ let jsonArray = data.split("\n");
+ jsonArray.pop();
+ for (let json of jsonArray) {
+ let msg;
+ try {
+ msg = JSON.parse(json);
+ } catch (e) {
+ DEBUG && log("LegacyTCPSignalingChannel - error in parsing json: " + e); //jshint ignore:line
+ }
+
+ this._handleMessage(msg);
+ }
+ },
+
+ _createInputStreamPump: function() {
+ DEBUG && log("LegacyTCPControlChannel - create pump"); //jshint ignore:line
+ this._pump = Cc["@mozilla.org/network/input-stream-pump;1"].
+ createInstance(Ci.nsIInputStreamPump);
+ this._pump.init(this._input, -1, -1, 0, 0, false);
+ this._pump.asyncRead(this, null);
+ },
+
+ // Handle command from remote side
+ _handleMessage: function(aMsg) {
+ DEBUG && log("LegacyTCPControlChannel - handleMessage from "
+ + JSON.stringify(this._deviceInfo) + ": " + JSON.stringify(aMsg)); //jshint ignore:line
+ switch (aMsg.type) {
+ case "requestSession:Answer": {
+ this._onAnswer(aMsg.answer);
+ break;
+ }
+ case "requestSession:IceCandidate": {
+ this._listener.onIceCandidate(aMsg.iceCandidate);
+ break;
+ }
+ case "requestSession:CloseReason": {
+ this._pendingCloseReason = aMsg.reason;
+ break;
+ }
+ }
+ },
+
+ get listener() {
+ return this._listener;
+ },
+
+ set listener(aListener) {
+ DEBUG && log("LegacyTCPControlChannel - set listener: " + aListener); //jshint ignore:line
+ if (!aListener) {
+ this._listener = null;
+ return;
+ }
+
+ this._listener = aListener;
+ if (this._pendingOpen) {
+ this._pendingOpen = false;
+ DEBUG && log("LegacyTCPControlChannel - notify pending opened"); //jshint ignore:line
+ this._listener.notifyConnected();
+ }
+
+ if (this._pendingAnswer) {
+ let answer = this._pendingAnswer;
+ DEBUG && log("LegacyTCPControlChannel - notify pending answer: " +
+ JSON.stringify(answer)); // jshint ignore:line
+ this._listener.onAnswer(new ChannelDescription(answer));
+ this._pendingAnswer = null;
+ }
+
+ if (this._pendingClose) {
+ DEBUG && log("LegacyTCPControlChannel - notify pending closed"); //jshint ignore:line
+ this._notifyDisconnected(this._pendingCloseReason);
+ this._pendingClose = null;
+ }
+ },
+
+ /**
+ * These functions are designed to handle the interaction with listener
+ * appropriately. |_FUNC| is to handle |this._listener.FUNC|.
+ */
+ _onAnswer: function(aAnswer) {
+ if (!this._connected) {
+ return;
+ }
+ if (!this._listener) {
+ this._pendingAnswer = aAnswer;
+ return;
+ }
+ DEBUG && log("LegacyTCPControlChannel - notify answer: " + JSON.stringify(aAnswer)); //jshint ignore:line
+ this._listener.onAnswer(new ChannelDescription(aAnswer));
+ },
+
+ _notifyConnected: function() {
+ this._connected = true;
+ this._pendingClose = false;
+ this._pendingCloseReason = Cr.NS_OK;
+
+ if (!this._listener) {
+ this._pendingOpen = true;
+ return;
+ }
+
+ DEBUG && log("LegacyTCPControlChannel - notify opened"); //jshint ignore:line
+ this._listener.notifyConnected();
+ },
+
+ _notifyDisconnected: function(aReason) {
+ this._connected = false;
+ this._pendingOpen = false;
+ this._pendingAnswer = null;
+
+ // Remote endpoint closes the control channel with abnormal reason.
+ if (aReason == Cr.NS_OK && this._pendingCloseReason != Cr.NS_OK) {
+ aReason = this._pendingCloseReason;
+ }
+
+ if (!this._listener) {
+ this._pendingClose = true;
+ this._pendingCloseReason = aReason;
+ return;
+ }
+
+ DEBUG && log("LegacyTCPControlChannel - notify closed"); //jshint ignore:line
+ this._listener.notifyDisconnected(aReason);
+ },
+
+ disconnect: function(aReason) {
+ DEBUG && log("LegacyTCPControlChannel - close with reason: " + aReason); //jshint ignore:line
+
+ if (this._connected) {
+ // default reason is NS_OK
+ if (typeof aReason !== "undefined" && aReason !== Cr.NS_OK) {
+ let msg = {
+ type: "requestSession:CloseReason",
+ presentationId: this._presentationId,
+ reason: aReason,
+ };
+ this._sendMessage(msg);
+ this._pendingCloseReason = aReason;
+ }
+
+ this._transport.setEventSink(null, null);
+ this._pump = null;
+
+ this._input.close();
+ this._output.close();
+
+ this._connected = false;
+ }
+ },
+
+ classID: Components.ID("{4027ce3d-06e3-4d06-a235-df329cb0d411}"),
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel,
+ Ci.nsIStreamListener]),
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([LegacyPresentationControlService]); //jshint ignore:line
new file mode 100644
--- /dev/null
+++ b/dom/presentation/provider/LegacyProviders.manifest
@@ -0,0 +1,2 @@
+component {b21816fe-8aff-4811-86d2-85a7444c557e} LegacyPresentationControlService.js
+contract @mozilla.org/presentation/legacy-control-service;1 {b21816fe-8aff-4811-86d2-85a7444c557e}
--- a/dom/presentation/provider/MulticastDNSDeviceProvider.cpp
+++ b/dom/presentation/provider/MulticastDNSDeviceProvider.cpp
@@ -6,30 +6,32 @@
#include "MulticastDNSDeviceProvider.h"
#include "MainThreadUtils.h"
#include "mozilla/Logging.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/unused.h"
#include "nsComponentManagerUtils.h"
#include "nsIObserverService.h"
+#include "nsIWritablePropertyBag2.h"
#include "nsServiceManagerUtils.h"
#include "nsTCPDeviceInfo.h"
#include "nsThreadUtils.h"
#ifdef MOZ_WIDGET_ANDROID
#include "nsIPropertyBag2.h"
#endif // MOZ_WIDGET_ANDROID
#define PREF_PRESENTATION_DISCOVERY "dom.presentation.discovery.enabled"
#define PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS "dom.presentation.discovery.timeout_ms"
#define PREF_PRESENTATION_DISCOVERABLE "dom.presentation.discoverable"
#define PREF_PRESENTATION_DEVICE_NAME "dom.presentation.device.name"
-#define SERVICE_TYPE "_mozilla_papi._tcp."
+#define SERVICE_TYPE "_presentation-ctrl._tcp"
+#define PROTOCOL_VERSION_TAG "version"
static mozilla::LazyLogModule sMulticastDNSProviderLogModule("MulticastDNSDeviceProvider");
#undef LOG_I
#define LOG_I(...) MOZ_LOG(sMulticastDNSProviderLogModule, mozilla::LogLevel::Debug, (__VA_ARGS__))
#undef LOG_E
#define LOG_E(...) MOZ_LOG(sMulticastDNSProviderLogModule, mozilla::LogLevel::Error, (__VA_ARGS__))
@@ -250,16 +252,31 @@ MulticastDNSDeviceProvider::RegisterServ
}
if (NS_WARN_IF(NS_FAILED(rv = serviceInfo->SetServiceName(mServiceName)))) {
return rv;
}
if (NS_WARN_IF(NS_FAILED(rv = serviceInfo->SetPort(servicePort)))) {
return rv;
}
+ nsCOMPtr<nsIWritablePropertyBag2> propBag =
+ do_CreateInstance("@mozilla.org/hash-property-bag;1");
+ MOZ_ASSERT(propBag);
+
+ uint32_t version;
+ rv = mPresentationService->GetVersion(&version);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+ rv = propBag->SetPropertyAsUint32(NS_LITERAL_STRING(PROTOCOL_VERSION_TAG),
+ version);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ if (NS_WARN_IF(NS_FAILED(rv = serviceInfo->SetAttributes(propBag)))) {
+ return rv;
+ }
+
return mMulticastDNS->RegisterService(serviceInfo,
mWrappedListener,
getter_AddRefs(mRegisterRequest));
}
nsresult
MulticastDNSDeviceProvider::UnregisterService(nsresult aReason)
{
@@ -292,29 +309,53 @@ MulticastDNSDeviceProvider::StopDiscover
mDiscoveryRequest->Cancel(aReason);
mDiscoveryRequest = nullptr;
}
return NS_OK;
}
nsresult
-MulticastDNSDeviceProvider::RequestSession(Device* aDevice,
- const nsAString& aUrl,
- const nsAString& aPresentationId,
- nsIPresentationControlChannel** aRetVal)
+MulticastDNSDeviceProvider::Connect(Device* aDevice,
+ nsIPresentationControlChannel** aRetVal)
{
MOZ_ASSERT(aDevice);
MOZ_ASSERT(mPresentationService);
RefPtr<TCPDeviceInfo> deviceInfo = new TCPDeviceInfo(aDevice->Id(),
aDevice->Address(),
aDevice->Port());
- return mPresentationService->RequestSession(deviceInfo, aUrl, aPresentationId, aRetVal);
+ return mPresentationService->Connect(deviceInfo, aRetVal);
+}
+
+bool
+MulticastDNSDeviceProvider::IsCompatibleServer(nsIDNSServiceInfo* aServiceInfo)
+{
+ MOZ_ASSERT(aServiceInfo);
+
+ nsCOMPtr<nsIPropertyBag2> propBag;
+ if (NS_WARN_IF(NS_FAILED(
+ aServiceInfo->GetAttributes(getter_AddRefs(propBag)))) || !propBag) {
+ return false;
+ }
+
+ uint32_t remoteVersion;
+ if (NS_WARN_IF(NS_FAILED(
+ propBag->GetPropertyAsUint32(NS_LITERAL_STRING(PROTOCOL_VERSION_TAG),
+ &remoteVersion)))) {
+ return false;
+ }
+
+ bool isCompatible = false;
+ Unused << NS_WARN_IF(NS_FAILED(
+ mPresentationService->IsCompatibleServer(remoteVersion,
+ &isCompatible)));
+
+ return isCompatible;
}
nsresult
MulticastDNSDeviceProvider::AddDevice(const nsACString& aId,
const nsACString& aServiceName,
const nsACString& aServiceType,
const nsACString& aAddress,
const uint16_t aPort)
@@ -765,16 +806,21 @@ MulticastDNSDeviceProvider::OnServiceRes
if (NS_WARN_IF(NS_FAILED(rv = mPresentationService->SetId(host)))) {
return rv;
}
return NS_OK;
}
+ if (!IsCompatibleServer(aServiceInfo)) {
+ LOG_I("ignore incompatible service: %s", serviceName.get());
+ return NS_OK;
+ }
+
nsAutoCString address;
if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetAddress(address)))) {
return rv;
}
uint16_t port;
if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetPort(&port)))) {
return rv;
@@ -988,25 +1034,24 @@ NS_IMETHODIMP
MulticastDNSDeviceProvider::Device::GetType(nsACString& aType)
{
aType = mType;
return NS_OK;
}
NS_IMETHODIMP
-MulticastDNSDeviceProvider::Device::EstablishControlChannel(const nsAString& aUrl,
- const nsAString& aPresentationId,
- nsIPresentationControlChannel** aRetVal)
+MulticastDNSDeviceProvider::Device::EstablishControlChannel(
+ nsIPresentationControlChannel** aRetVal)
{
if (!mProvider) {
return NS_ERROR_FAILURE;
}
- return mProvider->RequestSession(this, aUrl, aPresentationId, aRetVal);
+ return mProvider->Connect(this, aRetVal);
}
NS_IMETHODIMP
MulticastDNSDeviceProvider::Device::Disconnect()
{
// No need to do anything when disconnect.
return NS_OK;
}
--- a/dom/presentation/provider/MulticastDNSDeviceProvider.h
+++ b/dom/presentation/provider/MulticastDNSDeviceProvider.h
@@ -135,20 +135,19 @@ private:
return aA->Address() == aB->Address();
}
};
virtual ~MulticastDNSDeviceProvider();
nsresult RegisterService();
nsresult UnregisterService(nsresult aReason);
nsresult StopDiscovery(nsresult aReason);
- nsresult RequestSession(Device* aDevice,
- const nsAString& aUrl,
- const nsAString& aPresentationId,
- nsIPresentationControlChannel** aRetVal);
+ nsresult Connect(Device* aDevice,
+ nsIPresentationControlChannel** aRetVal);
+ bool IsCompatibleServer(nsIDNSServiceInfo* aServiceInfo);
// device manipulation
nsresult AddDevice(const nsACString& aId,
const nsACString& aServiceName,
const nsACString& aServiceType,
const nsACString& aAddress,
const uint16_t aPort);
nsresult UpdateDevice(const uint32_t aIndex,
--- a/dom/presentation/provider/PresentationControlService.js
+++ b/dom/presentation/provider/PresentationControlService.js
@@ -1,19 +1,33 @@
/* 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/. */
+/* jshint esnext:true, globalstrict:true, moz:true, undef:true, unused:true */
+/* globals Components, dump */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+/* globals XPCOMUtils */
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+/* globals Services */
Cu.import("resource://gre/modules/Services.jsm");
+/* globals NetUtil */
Cu.import("resource://gre/modules/NetUtil.jsm");
+/* globals ControllerStateMachine */
+XPCOMUtils.defineLazyModuleGetter(this, "ControllerStateMachine", // jshint ignore:line
+ "resource://gre/modules/presentation/ControllerStateMachine.jsm");
+/* global ReceiverStateMachine */
+XPCOMUtils.defineLazyModuleGetter(this, "ReceiverStateMachine", // jshint ignore:line
+ "resource://gre/modules/presentation/ReceiverStateMachine.jsm");
+
+const kProtocolVersion = 1; // need to review isCompatibleServer while fiddling the version number.
+
const DEBUG = Services.prefs.getBoolPref("dom.presentation.tcp_server.debug");
function log(aMsg) {
dump("-*- PresentationControlService.js: " + aMsg + "\n");
}
function TCPDeviceInfo(aAddress, aPort, aId) {
this.address = aAddress;
this.port = aPort;
@@ -31,140 +45,143 @@ PresentationControlService.prototype = {
* If a user agent connects to this server, we create a control channel but
* hand it to |TCPDevice.listener| when the initial information exchange
* finishes. Therefore, we hold the control channels in this period.
*/
_controlChannels: [],
startServer: function(aPort) {
if (this._isServiceInit()) {
- DEBUG && log("PresentationControlService - server socket has been initialized");
+ DEBUG && log("PresentationControlService - server socket has been initialized"); // jshint ignore:line
throw Cr.NS_ERROR_FAILURE;
}
/**
* 0 or undefined indicates opt-out parameter, and a port will be selected
* automatically.
*/
let serverSocketPort = (typeof aPort !== "undefined" && aPort !== 0) ? aPort : -1;
this._serverSocket = Cc["@mozilla.org/network/server-socket;1"]
.createInstance(Ci.nsIServerSocket);
if (!this._serverSocket) {
- DEBUG && log("PresentationControlService - create server socket fail.");
+ DEBUG && log("PresentationControlService - create server socket fail."); // jshint ignore:line
throw Cr.NS_ERROR_FAILURE;
}
try {
this._serverSocket.init(serverSocketPort, false, -1);
this._serverSocket.asyncListen(this);
} catch (e) {
// NS_ERROR_SOCKET_ADDRESS_IN_USE
- DEBUG && log("PresentationControlService - init server socket fail: " + e);
+ DEBUG && log("PresentationControlService - init server socket fail: " + e); // jshint ignore:line
throw Cr.NS_ERROR_FAILURE;
}
this._port = this._serverSocket.port;
- DEBUG && log("PresentationControlService - service start on port: " + this._port);
+ DEBUG && log("PresentationControlService - service start on port: " + this._port); // jshint ignore:line
// Monitor network interface change to restart server socket.
// Only B2G has nsINetworkManager
Services.obs.addObserver(this, "network-active-changed", false);
Services.obs.addObserver(this, "network:offline-status-changed", false);
},
+ isCompatibleServer: function(aVersion) {
+ // No compatibility issue for the first version of control protocol
+ return this.version === aVersion;
+ },
+
get id() {
return this._id;
},
set id(aId) {
this._id = aId;
},
get port() {
return this._port;
},
+ get version() {
+ return kProtocolVersion;
+ },
+
set listener(aListener) {
this._listener = aListener;
},
get listener() {
return this._listener;
},
_isServiceInit: function() {
return this._serverSocket !== null;
},
- requestSession: function(aDeviceInfo, aUrl, aPresentationId) {
+ connect: function(aDeviceInfo) {
if (!this.id) {
- DEBUG && log("PresentationControlService - Id has not initialized; requestSession fails");
+ DEBUG && log("PresentationControlService - Id has not initialized; connect fails"); // jshint ignore:line
return null;
}
- DEBUG && log("PresentationControlService - requestSession to " + aDeviceInfo.id
- + ": " + aUrl + ", " + aPresentationId);
+ DEBUG && log("PresentationControlService - connect to " + aDeviceInfo.id); // jshint ignore:line
let sts = Cc["@mozilla.org/network/socket-transport-service;1"]
- .getService(Ci.nsISocketTransportService)
+ .getService(Ci.nsISocketTransportService);
let socketTransport;
try {
socketTransport = sts.createTransport(null,
0,
aDeviceInfo.address,
aDeviceInfo.port,
null);
} catch (e) {
- DEBUG && log("PresentationControlService - createTransport throws: " + e);
+ DEBUG && log("PresentationControlService - createTransport throws: " + e); // jshint ignore:line
// Pop the exception to |TCPDevice.establishControlChannel|
throw Cr.NS_ERROR_FAILURE;
}
return new TCPControlChannel(this,
socketTransport,
aDeviceInfo,
- aPresentationId,
- "sender",
- aUrl);
+ "sender");
},
responseSession: function(aDeviceInfo, aSocketTransport) {
if (!this._isServiceInit()) {
DEBUG && log("PresentationControlService - should never receive remote " +
- "session request before server socket initialization");
+ "session request before server socket initialization"); // jshint ignore:line
return null;
}
- DEBUG && log("PresentationControlService - responseSession to "
- + JSON.stringify(aDeviceInfo));
+ DEBUG && log("PresentationControlService - responseSession to " +
+ JSON.stringify(aDeviceInfo)); // jshint ignore:line
return new TCPControlChannel(this,
aSocketTransport,
aDeviceInfo,
- null, // presentation ID
- "receiver",
- null // url
- );
+ "receiver");
},
// Triggered by TCPControlChannel
onSessionRequest: function(aDeviceInfo, aUrl, aPresentationId, aControlChannel) {
- DEBUG && log("PresentationControlService - onSessionRequest: "
- + aDeviceInfo.address + ":" + aDeviceInfo.port);
+ DEBUG && log("PresentationControlService - onSessionRequest: " +
+ aDeviceInfo.address + ":" + aDeviceInfo.port); // jshint ignore:line
this.listener.onSessionRequest(aDeviceInfo,
aUrl,
aPresentationId,
aControlChannel);
this.releaseControlChannel(aControlChannel);
},
// nsIServerSocketListener (Triggered by nsIServerSocket.init)
onSocketAccepted: function(aServerSocket, aClientSocket) {
- DEBUG && log("PresentationControlService - onSocketAccepted: "
- + aClientSocket.host + ":" + aClientSocket.port);
+ DEBUG && log("PresentationControlService - onSocketAccepted: " +
+ aClientSocket.host + ":" + aClientSocket.port); // jshint ignore:line
let deviceInfo = new TCPDeviceInfo(aClientSocket.host, aClientSocket.port);
this.holdControlChannel(this.responseSession(deviceInfo, aClientSocket));
},
holdControlChannel: function(aControlChannel) {
this._controlChannels.push(aControlChannel);
},
@@ -172,77 +189,77 @@ PresentationControlService.prototype = {
let index = this._controlChannels.indexOf(aControlChannel);
if (index !== -1) {
delete this._controlChannels[index];
}
},
// nsIServerSocketListener (Triggered by nsIServerSocket.init)
onStopListening: function(aServerSocket, aStatus) {
- DEBUG && log("PresentationControlService - onStopListening: " + aStatus);
+ DEBUG && log("PresentationControlService - onStopListening: " + aStatus); // jshint ignore:line
},
close: function() {
- DEBUG && log("PresentationControlService - close");
+ DEBUG && log("PresentationControlService - close"); // jshint ignore:line
if (this._isServiceInit()) {
- DEBUG && log("PresentationControlService - close server socket");
+ DEBUG && log("PresentationControlService - close server socket"); // jshint ignore:line
this._serverSocket.close();
this._serverSocket = null;
Services.obs.removeObserver(this, "network-active-changed");
Services.obs.removeObserver(this, "network:offline-status-changed");
}
this._port = 0;
},
// nsIObserver
observe: function(aSubject, aTopic, aData) {
- DEBUG && log("PresentationControlService - observe: " + aTopic);
+ DEBUG && log("PresentationControlService - observe: " + aTopic); // jshint ignore:line
switch (aTopic) {
case "network-active-changed": {
if (!aSubject) {
- DEBUG && log("No active network");
+ DEBUG && log("No active network"); // jshint ignore:line
return;
}
/**
* Restart service only when original status is online because other
* cases will be handled by "network:offline-status-changed".
*/
if (!Services.io.offline) {
this._restartServer();
}
break;
}
case "network:offline-status-changed": {
if (aData == "offline") {
- DEBUG && log("network offline");
+ DEBUG && log("network offline"); // jshint ignore:line
return;
}
this._restartServer();
break;
}
}
},
_restartServer: function() {
- DEBUG && log("PresentationControlService - restart service");
+ DEBUG && log("PresentationControlService - restart service"); // jshint ignore:line
// restart server socket
if (this._isServiceInit()) {
let port = this._port;
this.close();
try {
this.startServer();
if (this._listener && this._port !== port) {
this._listener.onPortChange(this._port);
}
} catch (e) {
- DEBUG && log("PresentationControlService - restart service fail: " + e);
+ DEBUG && log("PresentationControlService - restart service fail: " + e); // jshint ignore:line
}
}
},
classID: Components.ID("{f4079b8b-ede5-4b90-a112-5b415a931deb}"),
QueryInterface : XPCOMUtils.generateQI([Ci.nsIServerSocketListener,
Ci.nsIPresentationControlService,
Ci.nsIObserver]),
@@ -314,387 +331,340 @@ function discriptionAsJson(aDescription)
break;
}
return json;
}
function TCPControlChannel(presentationService,
transport,
deviceInfo,
- presentationId,
- direction,
- url) {
- DEBUG && log("create TCPControlChannel: " + presentationId + " with role: "
- + direction);
+ direction) {
+ DEBUG && log("create TCPControlChannel for : " + direction); // jshint ignore:line
this._deviceInfo = deviceInfo;
- this._presentationId = presentationId;
this._direction = direction;
this._transport = transport;
- this._url = url;
- this._presentationService = presentationService;
+ this._presentationService = presentationService;
let currentThread = Services.tm.currentThread;
transport.setEventSink(this, currentThread);
this._input = this._transport.openInputStream(0, 0, 0)
.QueryInterface(Ci.nsIAsyncInputStream);
this._input.asyncWait(this.QueryInterface(Ci.nsIStreamListener),
Ci.nsIAsyncInputStream.WAIT_CLOSURE_ONLY,
0,
currentThread);
this._output = this._transport
.openOutputStream(Ci.nsITransport.OPEN_UNBUFFERED, 0, 0);
+ this._stateMachine =
+ (direction === "sender") ? new ControllerStateMachine(this, presentationService.id)
+ : new ReceiverStateMachine(this);
// Since the transport created by server socket is already CONNECTED_TO
if (this._direction === "receiver") {
this._createInputStreamPump();
}
}
TCPControlChannel.prototype = {
_connected: false,
_pendingOpen: false,
_pendingOffer: null,
_pendingAnswer: null,
_pendingClose: null,
_pendingCloseReason: null,
- _sendingMessageType: null,
-
- _sendMessage: function(aType, aJSONData, aOnThrow) {
- if (!aOnThrow) {
- aOnThrow = function(e) {throw e.result;}
- }
-
- if (!aType || !aJSONData) {
- aOnThrow();
- return;
- }
-
- if (!this._connected) {
- DEBUG && log("TCPControlChannel - send" + aType + " fails");
- throw Cr.NS_ERROR_FAILURE;
- }
-
- DEBUG && log("TCPControlChannel - send" + aType + ": "
- + JSON.stringify(aJSONData));
- try {
- this._send(aJSONData);
- } catch (e) {
- aOnThrow(e);
- }
- this._sendingMessageType = aType;
- },
-
- _sendInit: function() {
- let msg = {
- type: "requestSession:Init",
- presentationId: this._presentationId,
- url: this._url,
- id: this._presentationService.id,
- };
-
- this._sendMessage("init", msg, function(e) {
- this.close();
- this._notifyClosed(e.result);
- });
- },
sendOffer: function(aOffer) {
- let msg = {
- type: "requestSession:Offer",
- presentationId: this.presentationId,
- offer: discriptionAsJson(aOffer),
- };
- this._sendMessage("offer", msg);
+ this._stateMachine.sendOffer(discriptionAsJson(aOffer));
},
sendAnswer: function(aAnswer) {
- let msg = {
- type: "requestSession:Answer",
- presentationId: this.presentationId,
- answer: discriptionAsJson(aAnswer),
- };
- this._sendMessage("answer", msg);
+ this._stateMachine.sendAnswer(discriptionAsJson(aAnswer));
},
sendIceCandidate: function(aCandidate) {
- let msg = {
- type: "requestSession:IceCandidate",
- presentationId: this.presentationId,
- iceCandidate: aCandidate,
- };
- this._sendMessage("iceCandidate", msg);
+ this._stateMachine.updateIceCandidate(aCandidate);
},
+
+ launch: function(aPresentationId, aUrl) {
+ this._stateMachine.launch(aPresentationId, aUrl);
+ },
+
// may throw an exception
_send: function(aMsg) {
- DEBUG && log("TCPControlChannel - Send: " + JSON.stringify(aMsg, null, 2));
+ DEBUG && log("TCPControlChannel - Send: " + JSON.stringify(aMsg, null, 2)); // jshint ignore:line
/**
* XXX In TCP streaming, it is possible that more than one message in one
* TCP packet. We use line delimited JSON to identify where one JSON encoded
* object ends and the next begins. Therefore, we do not allow newline
* characters whithin the whole message, and add a newline at the end.
* Please see the parser code in |onDataAvailable|.
*/
let message = JSON.stringify(aMsg).replace(["\n"], "") + "\n";
try {
this._output.write(message, message.length);
} catch(e) {
- DEBUG && log("TCPControlChannel - Failed to send message: " + e.name);
+ DEBUG && log("TCPControlChannel - Failed to send message: " + e.name); // jshint ignore:line
throw e;
}
},
// nsIAsyncInputStream (Triggered by nsIInputStream.asyncWait)
// Only used for detecting connection refused
onInputStreamReady: function(aStream) {
try {
aStream.available();
} catch (e) {
- DEBUG && log("TCPControlChannel - onInputStreamReady error: " + e.name);
+ DEBUG && log("TCPControlChannel - onInputStreamReady error: " + e.name); // jshint ignore:line
// NS_ERROR_CONNECTION_REFUSED
- this._listener.notifyClosed(e.result);
+ this._listener.notifyDisconnected(e.result);
}
},
// nsITransportEventSink (Triggered by nsISocketTransport.setEventSink)
- onTransportStatus: function(aTransport, aStatus, aProg, aProgMax) {
- DEBUG && log("TCPControlChannel - onTransportStatus: "
- + aStatus.toString(16) + " with role: " + this._direction);
+ onTransportStatus: function(aTransport, aStatus) {
+ DEBUG && log("TCPControlChannel - onTransportStatus: " + aStatus.toString(16) +
+ " with role: " + this._direction); // jshint ignore:line
if (aStatus === Ci.nsISocketTransport.STATUS_CONNECTED_TO) {
this._connected = true;
if (!this._pump) {
this._createInputStreamPump();
}
-
- if (this._direction === "sender") {
- this._sendInit();
- }
- } else if (aStatus === Ci.nsISocketTransport.STATUS_SENDING_TO) {
- if (this._sendingMessageType === "init") {
- this._notifyOpened();
- }
- this._sendingMessageType = null;
}
},
// nsIRequestObserver (Triggered by nsIInputStreamPump.asyncRead)
onStartRequest: function() {
- DEBUG && log("TCPControlChannel - onStartRequest with role: "
- + this._direction);
+ DEBUG && log("TCPControlChannel - onStartRequest with role: " +
+ this._direction); // jshint ignore:line
},
// nsIRequestObserver (Triggered by nsIInputStreamPump.asyncRead)
onStopRequest: function(aRequest, aContext, aStatus) {
- DEBUG && log("TCPControlChannel - onStopRequest: " + aStatus
- + " with role: " + this._direction);
- this.close(aStatus);
- this._notifyClosed(aStatus);
+ DEBUG && log("TCPControlChannel - onStopRequest: " + aStatus +
+ " with role: " + this._direction); // jshint ignore:line
+ this._stateMachine.onChannelClosed(aStatus, true);
},
// nsIStreamListener (Triggered by nsIInputStreamPump.asyncRead)
- onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, aCount) {
+ onDataAvailable: function(aRequest, aContext, aInputStream) {
let data = NetUtil.readInputStreamToString(aInputStream,
aInputStream.available());
- DEBUG && log("TCPControlChannel - onDataAvailable: " + data);
+ DEBUG && log("TCPControlChannel - onDataAvailable: " + data); // jshint ignore:line
// Parser of line delimited JSON. Please see |_send| for more informaiton.
let jsonArray = data.split("\n");
jsonArray.pop();
for (let json of jsonArray) {
let msg;
try {
msg = JSON.parse(json);
} catch (e) {
- DEBUG && log("TCPSignalingChannel - error in parsing json: " + e);
+ DEBUG && log("TCPSignalingChannel - error in parsing json: " + e); // jshint ignore:line
}
this._handleMessage(msg);
}
},
_createInputStreamPump: function() {
- DEBUG && log("TCPControlChannel - create pump with role: "
- + this._direction);
+ DEBUG && log("TCPControlChannel - create pump with role: " +
+ this._direction); // jshint ignore:line
this._pump = Cc["@mozilla.org/network/input-stream-pump;1"].
createInstance(Ci.nsIInputStreamPump);
this._pump.init(this._input, -1, -1, 0, 0, false);
this._pump.asyncRead(this, null);
+ this._stateMachine.onChannelReady();
},
// Handle command from remote side
_handleMessage: function(aMsg) {
- DEBUG && log("TCPControlChannel - handleMessage from "
- + JSON.stringify(this._deviceInfo) + ": " + JSON.stringify(aMsg));
- switch (aMsg.type) {
- case "requestSession:Init": {
- this._deviceInfo.id = aMsg.id;
- this._url = aMsg.url;
- this._presentationId = aMsg.presentationId;
- this._presentationService.onSessionRequest(this._deviceInfo,
- aMsg.url,
- aMsg.presentationId,
- this);
- this._notifyOpened();
- break;
- }
- case "requestSession:Offer": {
- this._onOffer(aMsg.offer);
- break;
- }
- case "requestSession:Answer": {
- this._onAnswer(aMsg.answer);
- break;
- }
- case "requestSession:IceCandidate": {
- this._listener.onIceCandidate(aMsg.iceCandidate);
- break;
- }
- case "requestSession:CloseReason": {
- this._pendingCloseReason = aMsg.reason;
- break;
- }
- }
+ DEBUG && log("TCPControlChannel - handleMessage from " +
+ JSON.stringify(this._deviceInfo) + ": " + JSON.stringify(aMsg)); // jshint ignore:line
+ this._stateMachine.onCommand(aMsg);
},
get listener() {
return this._listener;
},
set listener(aListener) {
- DEBUG && log("TCPControlChannel - set listener: " + aListener);
+ DEBUG && log("TCPControlChannel - set listener: " + aListener); // jshint ignore:line
if (!aListener) {
this._listener = null;
return;
}
this._listener = aListener;
if (this._pendingOpen) {
this._pendingOpen = false;
- DEBUG && log("TCPControlChannel - notify pending opened");
- this._listener.notifyOpened();
+ DEBUG && log("TCPControlChannel - notify pending opened"); // jshint ignore:line
+ this._listener.notifyConnected();
}
if (this._pendingOffer) {
let offer = this._pendingOffer;
- DEBUG && log("TCPControlChannel - notify pending offer: "
- + JSON.stringify(offer));
+ DEBUG && log("TCPControlChannel - notify pending offer: " +
+ JSON.stringify(offer)); // jshint ignore:line
this._listener.onOffer(new ChannelDescription(offer));
this._pendingOffer = null;
}
if (this._pendingAnswer) {
let answer = this._pendingAnswer;
- DEBUG && log("TCPControlChannel - notify pending answer: "
- + JSON.stringify(answer));
+ DEBUG && log("TCPControlChannel - notify pending answer: " +
+ JSON.stringify(answer)); // jshint ignore:line
this._listener.onAnswer(new ChannelDescription(answer));
this._pendingAnswer = null;
}
if (this._pendingClose) {
- DEBUG && log("TCPControlChannel - notify pending closed");
- this._notifyClosed(this._pendingCloseReason);
+ DEBUG && log("TCPControlChannel - notify pending closed"); // jshint ignore:line
+ this._notifyDisconnected(this._pendingCloseReason);
this._pendingClose = null;
}
},
/**
* These functions are designed to handle the interaction with listener
* appropriately. |_FUNC| is to handle |this._listener.FUNC|.
*/
_onOffer: function(aOffer) {
if (!this._connected) {
return;
}
if (!this._listener) {
this._pendingOffer = aOffer;
return;
}
- DEBUG && log("TCPControlChannel - notify offer: "
- + JSON.stringify(aOffer));
+ DEBUG && log("TCPControlChannel - notify offer: " +
+ JSON.stringify(aOffer)); // jshint ignore:line
this._listener.onOffer(new ChannelDescription(aOffer));
},
_onAnswer: function(aAnswer) {
if (!this._connected) {
return;
}
if (!this._listener) {
this._pendingAnswer = aAnswer;
return;
}
- DEBUG && log("TCPControlChannel - notify answer: "
- + JSON.stringify(aAnswer));
+ DEBUG && log("TCPControlChannel - notify answer: " +
+ JSON.stringify(aAnswer)); // jshint ignore:line
this._listener.onAnswer(new ChannelDescription(aAnswer));
},
- _notifyOpened: function() {
+ _notifyConnected: function() {
this._connected = true;
this._pendingClose = false;
this._pendingCloseReason = Cr.NS_OK;
if (!this._listener) {
this._pendingOpen = true;
return;
}
- DEBUG && log("TCPControlChannel - notify opened with role: "
- + this._direction);
- this._listener.notifyOpened();
+ DEBUG && log("TCPControlChannel - notify opened with role: " +
+ this._direction); // jshint ignore:line
+ this._listener.notifyConnected();
},
- _notifyClosed: function(aReason) {
+ _notifyDisconnected: function(aReason) {
this._connected = false;
this._pendingOpen = false;
this._pendingOffer = null;
this._pendingAnswer = null;
// Remote endpoint closes the control channel with abnormal reason.
if (aReason == Cr.NS_OK && this._pendingCloseReason != Cr.NS_OK) {
aReason = this._pendingCloseReason;
}
if (!this._listener) {
this._pendingClose = true;
this._pendingCloseReason = aReason;
return;
}
- DEBUG && log("TCPControlChannel - notify closed with role: "
- + this._direction);
- this._listener.notifyClosed(aReason);
+ DEBUG && log("TCPControlChannel - notify closed with role: " +
+ this._direction); // jshint ignore:line
+ this._listener.notifyDisconnected(aReason);
},
- close: function(aReason) {
- DEBUG && log("TCPControlChannel - close with reason: " + aReason);
-
+ _closeTransport: function() {
if (this._connected) {
- // default reason is NS_OK
- if (typeof aReason !== "undefined" && aReason !== Cr.NS_OK) {
- let msg = {
- type: "requestSession:CloseReason",
- presentationId: this.presentationId,
- reason: aReason,
- };
- this._sendMessage("close", msg);
- this._pendingCloseReason = aReason;
- }
-
this._transport.setEventSink(null, null);
this._pump = null;
this._input.close();
this._output.close();
this._presentationService.releaseControlChannel(this);
+ }
+ },
+
+ disconnect: function(aReason) {
+ DEBUG && log("TCPControlChannel - disconnect with reason: " + aReason); // jshint ignore:line
+
+ if (this._connected) {
+ // default reason is NS_OK
+ aReason = !aReason ? Cr.NS_OK : aReason;
+ this._stateMachine.onChannelClosed(aReason, false);
+
+ this._closeTransport();
this._connected = false;
}
},
+ // callback from state machine
+ sendCommand: function(command) {
+ this._send(command);
+ },
+
+ notifyDeviceConnected: function(deviceId) {
+ switch (this._direction) {
+ case "receiver":
+ this._deviceInfo.id = deviceId;
+ break;
+ }
+ this._notifyConnected();
+ },
+
+ notifyDisconnected: function(reason) {
+ this._notifyDisconnected(reason);
+ this._closeTransport();
+ this._connected = false;
+ },
+
+ notifyLaunch: function(presentationId, url) {
+ switch (this._direction) {
+ case "receiver":
+ this._presentationService.onSessionRequest(this._deviceInfo,
+ url,
+ presentationId,
+ this);
+ break;
+ }
+ },
+
+ notifyOffer: function(offer) {
+ this._onOffer(offer);
+ },
+
+ notifyAnswer: function(answer) {
+ this._onAnswer(answer);
+ },
+
+ notifyIceCandidate: function(candidate) {
+ this._listener.onIceCandidate(candidate);
+ },
+
classID: Components.ID("{fefb8286-0bdc-488b-98bf-0c11b485c955}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel,
Ci.nsIStreamListener]),
};
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PresentationControlService]);
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PresentationControlService]); // jshint ignore:line
--- a/dom/presentation/provider/PresentationDeviceProviderModule.cpp
+++ b/dom/presentation/provider/PresentationDeviceProviderModule.cpp
@@ -2,55 +2,86 @@
/* 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 "DisplayDeviceProvider.h"
#include "MulticastDNSDeviceProvider.h"
#include "mozilla/ModuleUtils.h"
+#ifdef MOZ_WIDGET_ANDROID
+#include "LegacyMDNSDeviceProvider.h"
+#endif //MOZ_WIDGET_ANDROID
+
#define MULTICAST_DNS_PROVIDER_CID \
{0x814f947a, 0x52f7, 0x41c9, \
{ 0x94, 0xa1, 0x36, 0x84, 0x79, 0x72, 0x84, 0xac }}
#define DISPLAY_DEVICE_PROVIDER_CID \
{ 0x515d9879, 0xfe0b, 0x4d9f, \
{ 0x89, 0x49, 0x7f, 0xa7, 0x65, 0x6c, 0x01, 0x0e } }
+#ifdef MOZ_WIDGET_ANDROID
+#define LEGACY_MDNS_PROVIDER_CID \
+ { 0x6885ff39, 0xd98c, 0x4356, \
+ { 0x9e, 0xb3, 0x56, 0x56, 0x31, 0x63, 0x0a, 0xf6 } }
+#endif //MOZ_WIDGET_ANDROID
#define DISPLAY_DEVICE_PROVIDER_CONTRACT_ID "@mozilla.org/presentation-device/displaydevice-provider;1"
#define MULTICAST_DNS_PROVIDER_CONTRACT_ID "@mozilla.org/presentation-device/multicastdns-provider;1"
+#ifdef MOZ_WIDGET_ANDROID
+#define LEGACY_MDNS_PROVIDER_CONTRACT_ID "@mozilla.org/presentation-device/legacy-mdns-provider;1"
+#endif //MOZ_WIDGET_ANDROID
+
using mozilla::dom::presentation::MulticastDNSDeviceProvider;
using mozilla::dom::presentation::DisplayDeviceProvider;
+#ifdef MOZ_WIDGET_ANDROID
+using mozilla::dom::presentation::legacy::LegacyMDNSDeviceProvider;
+#endif //MOZ_WIDGET_ANDROID
+
NS_GENERIC_FACTORY_CONSTRUCTOR(MulticastDNSDeviceProvider)
NS_DEFINE_NAMED_CID(MULTICAST_DNS_PROVIDER_CID);
NS_GENERIC_FACTORY_CONSTRUCTOR(DisplayDeviceProvider)
NS_DEFINE_NAMED_CID(DISPLAY_DEVICE_PROVIDER_CID);
+#ifdef MOZ_WIDGET_ANDROID
+NS_GENERIC_FACTORY_CONSTRUCTOR(LegacyMDNSDeviceProvider)
+NS_DEFINE_NAMED_CID(LEGACY_MDNS_PROVIDER_CID);
+#endif //MOZ_WIDGET_ANDROID
+
static const mozilla::Module::CIDEntry kPresentationDeviceProviderCIDs[] = {
{ &kMULTICAST_DNS_PROVIDER_CID, false, nullptr, MulticastDNSDeviceProviderConstructor },
{ &kDISPLAY_DEVICE_PROVIDER_CID, false, nullptr, DisplayDeviceProviderConstructor },
+#ifdef MOZ_WIDGET_ANDROID
+ { &kLEGACY_MDNS_PROVIDER_CID, false, nullptr, LegacyMDNSDeviceProviderConstructor },
+#endif //MOZ_WIDGET_ANDROID
{ nullptr }
};
static const mozilla::Module::ContractIDEntry kPresentationDeviceProviderContracts[] = {
{ MULTICAST_DNS_PROVIDER_CONTRACT_ID, &kMULTICAST_DNS_PROVIDER_CID },
{ DISPLAY_DEVICE_PROVIDER_CONTRACT_ID, &kDISPLAY_DEVICE_PROVIDER_CID },
+#ifdef MOZ_WIDGET_ANDROID
+ { LEGACY_MDNS_PROVIDER_CONTRACT_ID, &kLEGACY_MDNS_PROVIDER_CID },
+#endif //MOZ_WIDGET_ANDROID
{ nullptr }
};
static const mozilla::Module::CategoryEntry kPresentationDeviceProviderCategories[] = {
#if defined(MOZ_WIDGET_COCOA) || defined(MOZ_WIDGET_ANDROID) || (defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 16)
{ PRESENTATION_DEVICE_PROVIDER_CATEGORY, "MulticastDNSDeviceProvider", MULTICAST_DNS_PROVIDER_CONTRACT_ID },
#endif
#if defined(MOZ_WIDGET_GONK)
{ PRESENTATION_DEVICE_PROVIDER_CATEGORY, "DisplayDeviceProvider", DISPLAY_DEVICE_PROVIDER_CONTRACT_ID },
#endif
+#ifdef MOZ_WIDGET_ANDROID
+ { PRESENTATION_DEVICE_PROVIDER_CATEGORY, "LegacyMDNSDeviceProvider", LEGACY_MDNS_PROVIDER_CONTRACT_ID },
+#endif //MOZ_WIDGET_ANDROID
{ nullptr }
};
static const mozilla::Module kPresentationDeviceProviderModule = {
mozilla::Module::kVersion,
kPresentationDeviceProviderCIDs,
kPresentationDeviceProviderContracts,
kPresentationDeviceProviderCategories
new file mode 100644
--- /dev/null
+++ b/dom/presentation/provider/ReceiverStateMachine.jsm
@@ -0,0 +1,186 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+/* jshint esnext:true, globalstrict:true, moz:true, undef:true, unused:true */
+/* globals Components, dump */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["ReceiverStateMachine"]; // jshint ignore:line
+
+const { utils: Cu } = Components;
+
+/* globals State, CommandType */
+Cu.import("resource://gre/modules/presentation/StateMachineHelper.jsm");
+
+const DEBUG = false;
+function debug(str) {
+ dump("-*- ReceiverStateMachine: " + str + "\n");
+}
+
+var handlers = [
+ function _initHandler(stateMachine, command) {
+ // shouldn't receive any command at init state
+ DEBUG && debug("unexpected command: " + JSON.stringify(command)); // jshint ignore:line
+ },
+ function _connectingHandler(stateMachine, command) {
+ switch (command.type) {
+ case CommandType.CONNECT:
+ stateMachine._sendCommand({
+ type: CommandType.CONNECT_ACK
+ });
+ stateMachine.state = State.CONNECTED;
+ stateMachine._notifyDeviceConnected(command.deviceId);
+ break;
+ case CommandType.DISCONNECT:
+ stateMachine.state = State.CLOSED;
+ stateMachine._notifyDisconnected(command.reason);
+ break;
+ default:
+ debug("unexpected command: " + JSON.stringify(command));
+ // ignore unexpected command
+ break;
+ }
+ },
+ function _connectedHandler(stateMachine, command) {
+ switch (command.type) {
+ case CommandType.DISCONNECT:
+ stateMachine.state = State.CLOSED;
+ stateMachine._notifyDisconnected(command.reason);
+ break;
+ case CommandType.LAUNCH:
+ stateMachine._notifyLaunch(command.presentationId,
+ command.url);
+ stateMachine._sendCommand({
+ type: CommandType.LAUNCH_ACK,
+ presentationId: command.presentationId
+ });
+ break;
+ case CommandType.OFFER:
+ case CommandType.ICE_CANDIDATE:
+ stateMachine._notifyChannelDescriptor(command);
+ break;
+ default:
+ debug("unexpected command: " + JSON.stringify(command));
+ // ignore unexpected command
+ break;
+ }
+ },
+ function _closingHandler(stateMachine, command) {
+ // ignore every command in closing state.
+ DEBUG && debug("unexpected command: " + JSON.stringify(command)); // jshint ignore:line
+ },
+ function _closedHandler(stateMachine, command) {
+ // ignore every command in closed state.
+ DEBUG && debug("unexpected command: " + JSON.stringify(command)); // jshint ignore:line
+ },
+];
+
+function ReceiverStateMachine(channel) {
+ this.state = State.INIT;
+ this._channel = channel;
+}
+
+ReceiverStateMachine.prototype = {
+ launch: function _launch() {
+ // presentation session can only be launched by controlling UA.
+ debug("receiver shouldn't trigger launch");
+ },
+
+ sendOffer: function _sendOffer() {
+ // offer can only be sent by controlling UA.
+ debug("receiver shouldn't generate offer");
+ },
+
+ sendAnswer: function _sendAnswer(answer) {
+ if (this.state === State.CONNECTED) {
+ this._sendCommand({
+ type: CommandType.ANSWER,
+ answer: answer,
+ });
+ }
+ },
+
+ updateIceCandidate: function _updateIceCandidate(candidate) {
+ if (this.state === State.CONNECTED) {
+ this._sendCommand({
+ type: CommandType.ICE_CANDIDATE,
+ candidate: candidate,
+ });
+ }
+ },
+
+ onCommand: function _onCommand(command) {
+ handlers[this.state](this, command);
+ },
+
+ onChannelReady: function _onChannelReady() {
+ if (this.state === State.INIT) {
+ this.state = State.CONNECTING;
+ }
+ },
+
+ onChannelClosed: function _onChannelClose(reason, isByRemote) {
+ switch (this.state) {
+ case State.CONNECTED:
+ if (isByRemote) {
+ this.state = State.CLOSED;
+ this._notifyDisconnected(reason);
+ } else {
+ this._sendCommand({
+ type: CommandType.DISCONNECT,
+ reason: reason
+ });
+ this.state = State.CLOSING;
+ this._closeReason = reason;
+ }
+ break;
+ case State.CLOSING:
+ if (isByRemote) {
+ this.state = State.CLOSED;
+ if (this._closeReason) {
+ reason = this._closeReason;
+ delete this._closeReason;
+ }
+ this._notifyDisconnected(reason);
+ } else {
+ // do nothing and wait for remote channel closed.
+ }
+ break;
+ default:
+ DEBUG && debug("unexpected channel close: " + reason + ", " + isByRemote); // jshint ignore:line
+ break;
+ }
+ },
+
+ _sendCommand: function _sendCommand(command) {
+ this._channel.sendCommand(command);
+ },
+
+ _notifyDeviceConnected: function _notifyDeviceConnected(deviceName) {
+ this._channel.notifyDeviceConnected(deviceName);
+ },
+
+ _notifyDisconnected: function _notifyDisconnected(reason) {
+ this._channel.notifyDisconnected(reason);
+ },
+
+ _notifyLaunch: function _notifyLaunch(presentationId, url) {
+ this._channel.notifyLaunch(presentationId, url);
+ },
+
+ _notifyChannelDescriptor: function _notifyChannelDescriptor(command) {
+ switch (command.type) {
+ case CommandType.OFFER:
+ this._channel.notifyOffer(command.offer);
+ break;
+ case CommandType.ICE_CANDIDATE:
+ this._channel.notifyIceCandidate(command.candidate);
+ break;
+ }
+ },
+};
+
+this.ReceiverStateMachine = ReceiverStateMachine; // jshint ignore:line
new file mode 100644
--- /dev/null
+++ b/dom/presentation/provider/StateMachineHelper.jsm
@@ -0,0 +1,35 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+/* jshint esnext:true, globalstrict:true, moz:true, undef:true, unused:true */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["State", "CommandType"]; // jshint ignore:line
+
+const State = Object.freeze({
+ INIT: 0,
+ CONNECTING: 1,
+ CONNECTED: 2,
+ CLOSING: 3,
+ CLOSED: 4,
+});
+
+const CommandType = Object.freeze({
+ // control channel life cycle
+ CONNECT: "connect", // { deviceId: <string> }
+ CONNECT_ACK: "connect-ack", // { presentationId: <string> }
+ DISCONNECT: "disconnect", // { reason: <int> }
+ // presentation session life cycle
+ LAUNCH: "launch", // { presentationId: <string>, url: <string> }
+ LAUNCH_ACK: "launch-ack", // { presentationId: <string> }
+ // session transport establishment
+ OFFER: "offer", // { offer: <json> }
+ ANSWER: "answer", // { answer: <json> }
+ ICE_CANDIDATE: "ice-candidate", // { candidate: <string> }
+});
+
+this.State = State; // jshint ignore:line
+this.CommandType = CommandType; // jshint ignore:line
--- a/dom/presentation/provider/moz.build
+++ b/dom/presentation/provider/moz.build
@@ -10,10 +10,27 @@ EXTRA_COMPONENTS += [
]
UNIFIED_SOURCES += [
'DisplayDeviceProvider.cpp',
'MulticastDNSDeviceProvider.cpp',
'PresentationDeviceProviderModule.cpp',
]
+EXTRA_JS_MODULES.presentation += [
+ 'ControllerStateMachine.jsm',
+ 'ReceiverStateMachine.jsm',
+ 'StateMachineHelper.jsm',
+]
+
+# for TV 2.5 device backward capability
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
+ EXTRA_COMPONENTS += [
+ 'LegacyPresentationControlService.js',
+ 'LegacyProviders.manifest',
+ ]
+
+ UNIFIED_SOURCES += [
+ 'LegacyMDNSDeviceProvider.cpp',
+ ]
+
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
--- a/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js
+++ b/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js
@@ -136,34 +136,36 @@ const mockedControlChannel = {
} catch (e) {
isValid = false;
}
} else if (aSDP.type == Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL) {
isValid = (aSDP.dataChannelSDP == "test-sdp");
}
return isValid;
},
- close: function(reason) {
+ launch: function(presentationId, url) {
+ },
+ disconnect: function(reason) {
sendAsyncMessage('control-channel-closed', reason);
- this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).notifyClosed(reason);
+ this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).notifyDisconnected(reason);
},
simulateReceiverReady: function() {
this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).notifyReceiverReady();
},
simulateOnOffer: function() {
sendAsyncMessage('offer-received');
this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).onOffer(mockedChannelDescription);
},
simulateOnAnswer: function() {
sendAsyncMessage('answer-received');
this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).onAnswer(mockedChannelDescription);
},
- simulateNotifyOpened: function() {
+ simulateNotifyConnected: function() {
sendAsyncMessage('control-channel-opened');
- this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).notifyOpened();
+ this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).notifyConnected();
},
};
const mockedDevice = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]),
id: 'id',
name: 'name',
type: 'type',
@@ -398,21 +400,21 @@ addMessageListener('trigger-incoming-ans
mockedControlChannel.simulateOnAnswer();
});
addMessageListener('trigger-incoming-transport', function() {
mockedServerSocket.simulateOnSocketAccepted(mockedServerSocket, mockedSocketTransport);
});
addMessageListener('trigger-control-channel-open', function(reason) {
- mockedControlChannel.simulateNotifyOpened();
+ mockedControlChannel.simulateNotifyConnected();
});
addMessageListener('trigger-control-channel-close', function(reason) {
- mockedControlChannel.close(reason);
+ mockedControlChannel.disconnect(reason);
});
addMessageListener('trigger-data-transport-close', function(reason) {
mockedSessionTransport.close(reason);
});
addMessageListener('trigger-incoming-message', function(message) {
mockedSessionTransport.simulateIncomingMessage(message);
--- a/dom/presentation/tests/mochitest/PresentationSessionChromeScript1UA.js
+++ b/dom/presentation/tests/mochitest/PresentationSessionChromeScript1UA.js
@@ -164,74 +164,86 @@ const mockControlChannelOfSender = {
} else {
debug('set listener for mockControlChannelOfSender with null');
}
this._listener = listener;
},
get listener() {
return this._listener;
},
- notifyOpened: function() {
- // send offer after notifyOpened immediately
+ notifyConnected: function() {
+ // send offer after notifyConnected immediately
this._listener
.QueryInterface(Ci.nsIPresentationControlChannelListener)
- .notifyOpened();
+ .notifyConnected();
},
sendOffer: function(offer) {
sendAsyncMessage('offer-sent');
},
onAnswer: function(answer) {
this._listener
.QueryInterface(Ci.nsIPresentationControlChannelListener)
.onAnswer(answer);
},
- close: function(reason) {
+ launch: function(presentationId, url) {
+ sendAsyncMessage('sender-launch', url);
+ },
+ disconnect: function(reason) {
this._listener
.QueryInterface(Ci.nsIPresentationControlChannelListener)
- .notifyClosed(reason);
- mockControlChannelOfReceiver.close();
+ .notifyDisconnected(reason);
+ mockControlChannelOfReceiver.disconnect();
}
};
// control channel of receiver
const mockControlChannelOfReceiver = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]),
set listener(listener) {
// PresentationPresentingInfo::SetControlChannel
if (listener) {
debug('set listener for mockControlChannelOfReceiver without null');
} else {
debug('set listener for mockControlChannelOfReceiver with null');
}
this._listener = listener;
+
+ if (this._pendingOpened) {
+ this._pendingOpened = false;
+ this.notifyConnected();
+ }
},
get listener() {
return this._listener;
},
- notifyOpened: function() {
+ notifyConnected: function() {
// do nothing
+ if (!this._listener) {
+ this._pendingOpened = true;
+ return;
+ }
this._listener
.QueryInterface(Ci.nsIPresentationControlChannelListener)
- .notifyOpened();
+ .notifyConnected();
},
onOffer: function(offer) {
this._listener
.QueryInterface(Ci.nsIPresentationControlChannelListener)
.onOffer(offer);
},
sendAnswer: function(answer) {
this._listener
.QueryInterface(Ci.nsIPresentationSessionTransportCallback)
.notifyTransportReady();
sendAsyncMessage('answer-sent');
},
- close: function(reason) {
+ disconnect: function(reason) {
this._listener
.QueryInterface(Ci.nsIPresentationControlChannelListener)
- .notifyClosed(reason);
+ .notifyDisconnected(reason);
sendAsyncMessage('control-channel-receiver-closed', reason);
}
};
const mockDevice = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]),
id: 'id',
name: 'name',
@@ -352,18 +364,18 @@ function initMockAndListener() {
.onSessionRequest(mockDevice,
url,
sessionId,
mockControlChannelOfReceiver);
});
addMessageListener('trigger-control-channel-open', function(reason) {
debug('Got message: trigger-control-channel-open');
- mockControlChannelOfSender.notifyOpened();
- mockControlChannelOfReceiver.notifyOpened();
+ mockControlChannelOfSender.notifyConnected();
+ mockControlChannelOfReceiver.notifyConnected();
});
addMessageListener('trigger-on-offer', function() {
debug('Got message: trigger-on-offer');
mockControlChannelOfReceiver.onOffer(mockChannelDescriptionOfSender);
mockServerSocket.onSocketAccepted(mockServerSocket, mockSocketTransport);
});
--- a/dom/presentation/tests/mochitest/test_presentation_1ua_connection_wentaway.js
+++ b/dom/presentation/tests/mochitest/test_presentation_1ua_connection_wentaway.js
@@ -21,30 +21,35 @@ function setup() {
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
debug('Got message: device-prompt');
gScript.removeMessageListener('device-prompt', devicePromptHandler);
gScript.sendAsyncMessage('trigger-device-prompt-select');
});
gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() {
- debug('Got message: control-channel-established');
gScript.removeMessageListener('control-channel-established',
controlChannelEstablishedHandler);
+ gScript.sendAsyncMessage("trigger-control-channel-open");
+ });
+
+ gScript.addMessageListener('sender-launch', function senderLaunchHandler(url) {
+ debug('Got message: sender-launch');
+ gScript.removeMessageListener('sender-launch', senderLaunchHandler);
+ is(url, receiverUrl, 'Receiver: should receive the same url');
receiverIframe = document.createElement('iframe');
receiverIframe.setAttribute("mozbrowser", "true");
receiverIframe.setAttribute("mozpresentation", receiverUrl);
var oop = location.pathname.indexOf('_inproc') == -1;
receiverIframe.setAttribute("remote", oop);
receiverIframe.setAttribute('src', receiverUrl);
receiverIframe.addEventListener("mozbrowserloadend", function mozbrowserloadendHander() {
receiverIframe.removeEventListener("mozbrowserloadend", mozbrowserloadendHander);
info("Receiver loaded.");
- gScript.sendAsyncMessage("trigger-control-channel-open");
});
// This event is triggered when the iframe calls "alert".
receiverIframe.addEventListener("mozbrowsershowmodalprompt", function receiverListener(evt) {
var message = evt.detail.message;
if (/^OK /.exec(message)) {
ok(true, message.replace(/^OK /, ""));
} else if (/^KO /.exec(message)) {
@@ -91,17 +96,17 @@ function setup() {
});
return Promise.resolve();
}
function testCreateRequest() {
return new Promise(function(aResolve, aReject) {
info('Sender: --- testCreateRequest ---');
- request = new PresentationRequest("http://example.com");
+ request = new PresentationRequest(receiverUrl);
request.getAvailability().then((aAvailability) => {
aAvailability.onchange = function() {
aAvailability.onchange = null;
ok(aAvailability.value, "Sender: Device should be available.");
aResolve();
}
}).catch((aError) => {
ok(false, "Sender: Error occurred when getting availability: " + aError);
--- a/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver.html
+++ b/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver.html
@@ -35,26 +35,29 @@ function setup() {
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
debug('Got message: device-prompt');
gScript.removeMessageListener('device-prompt', devicePromptHandler);
gScript.sendAsyncMessage('trigger-device-prompt-select');
});
gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() {
- debug('Got message: control-channel-established');
- gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler);
+ gScript.removeMessageListener('control-channel-established',
+ controlChannelEstablishedHandler);
+ gScript.sendAsyncMessage("trigger-control-channel-open");
+ });
+
+ gScript.addMessageListener('sender-launch', function senderLaunchHandler(url) {
+ debug('Got message: sender-launch');
+ gScript.removeMessageListener('sender-launch', senderLaunchHandler);
+ is(url, receiverUrl, 'Receiver: should receive the same url');
receiverIframe = document.createElement('iframe');
receiverIframe.setAttribute('src', receiverUrl);
receiverIframe.setAttribute("mozbrowser", "true");
receiverIframe.setAttribute("mozpresentation", receiverUrl);
- receiverIframe.onload = function() {
- info('Receiver loaded.');
- gScript.sendAsyncMessage('trigger-control-channel-open');
- };
// This event is triggered when the iframe calls "alert".
receiverIframe.addEventListener("mozbrowsershowmodalprompt", function receiverListener(evt) {
var message = evt.detail.message;
debug('Got iframe message: ' + message);
if (/^OK /.exec(message)) {
ok(true, message.replace(/^OK /, ""));
} else if (/^KO /.exec(message)) {
@@ -100,17 +103,17 @@ function setup() {
});
return Promise.resolve();
}
function testCreateRequest() {
return new Promise(function(aResolve, aReject) {
info('Sender: --- testCreateRequest ---');
- request = new PresentationRequest("http://example.com");
+ request = new PresentationRequest(receiverUrl);
request.getAvailability().then((aAvailability) => {
aAvailability.onchange = function() {
aAvailability.onchange = null;
ok(aAvailability.value, "Sender: Device should be available.");
aResolve();
}
}).catch((aError) => {
ok(false, "Sender: Error occurred when getting availability: " + aError);
--- a/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver_oop.html
+++ b/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver_oop.html
@@ -35,28 +35,33 @@ function setup() {
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
debug('Got message: device-prompt');
gScript.removeMessageListener('device-prompt', devicePromptHandler);
gScript.sendAsyncMessage('trigger-device-prompt-select');
});
gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() {
- debug('Got message: control-channel-established');
gScript.removeMessageListener('control-channel-established',
controlChannelEstablishedHandler);
+ gScript.sendAsyncMessage("trigger-control-channel-open");
+ });
+
+ gScript.addMessageListener('sender-launch', function senderLaunchHandler(url) {
+ debug('Got message: sender-launch');
+ gScript.removeMessageListener('sender-launch', senderLaunchHandler);
+ is(url, receiverUrl, 'Receiver: should receive the same url');
receiverIframe = document.createElement('iframe');
receiverIframe.setAttribute("remote", "true");
receiverIframe.setAttribute("mozbrowser", "true");
receiverIframe.setAttribute("mozpresentation", receiverUrl);
receiverIframe.setAttribute('src', receiverUrl);
receiverIframe.addEventListener("mozbrowserloadend", function mozbrowserloadendHander() {
receiverIframe.removeEventListener("mozbrowserloadend", mozbrowserloadendHander);
info("Receiver loaded.");
- gScript.sendAsyncMessage("trigger-control-channel-open");
});
// This event is triggered when the iframe calls "alert".
receiverIframe.addEventListener("mozbrowsershowmodalprompt", function receiverListener(evt) {
var message = evt.detail.message;
if (/^OK /.exec(message)) {
ok(true, message.replace(/^OK /, ""));
} else if (/^KO /.exec(message)) {
@@ -103,17 +108,17 @@ function setup() {
});
return Promise.resolve();
}
function testCreateRequest() {
return new Promise(function(aResolve, aReject) {
info('Sender: --- testCreateRequest ---');
- request = new PresentationRequest("http://example.com");
+ request = new PresentationRequest(receiverUrl);
request.getAvailability()
.then((aAvailability) => {
aAvailability.onchange = function() {
aAvailability.onchange = null;
ok(aAvailability.value, "Sender: Device should be available.");
aResolve();
}
})
--- a/dom/presentation/tests/mochitest/test_presentation_datachannel_sessiontransport.html
+++ b/dom/presentation/tests/mochitest/test_presentation_datachannel_sessiontransport.html
@@ -104,19 +104,19 @@ const clientListener = {
setTimeout(()=>this._remoteBuilder.onOffer(aOffer), 0);
},
sendAnswer: function(aAnswer) {
setTimeout(()=>this._remoteBuilder.onAnswer(aAnswer), 0);
},
sendIceCandidate: function(aCandidate) {
setTimeout(()=>this._remoteBuilder.onIceCandidate(aCandidate), 0);
},
- close: function(aReason) {
- setTimeout(()=>this._localBuilder.notifyClosed(aReason), 0);
- setTimeout(()=>this._remoteBuilder.notifyClosed(aReason), 0);
+ disconnect: function(aReason) {
+ setTimeout(()=>this._localBuilder.notifyDisconnected(aReason), 0);
+ setTimeout(()=>this._remoteBuilder.notifyDisconnected(aReason), 0);
},
set remoteBuilder(aRemoteBuilder) {
this._remoteBuilder = aRemoteBuilder;
},
set localBuilder(aLocalBuilder) {
this._localBuilder = aLocalBuilder;
},
}
@@ -136,19 +136,19 @@ const serverListener = {
setTimeout(()=>this._remoteBuilder.onOffer(aOffer), 0);
},
sendAnswer: function(aAnswer) {
setTimeout(()=>this._remoteBuilder.onAnswer(aAnswer), 0);
},
sendIceCandidate: function(aCandidate) {
setTimeout(()=>this._remoteBuilder.onIceCandidate(aCandidate), 0);
},
- close: function(aReason) {
- setTimeout(()=>this._localBuilder.notifyClosed(aReason), 0);
- setTimeout(()=>this._remoteBuilder.notifyClosed(aReason), 0);
+ disconnect: function(aReason) {
+ setTimeout(()=>this._localBuilder.notifyDisconnected(aReason), 0);
+ setTimeout(()=>this._remoteBuilder.notifyDisconnected(aReason), 0);
},
set remoteBuilder(aRemoteBuilder) {
this._remoteBuilder = aRemoteBuilder;
},
set localBuilder(aLocalBuilder) {
this._localBuilder = aLocalBuilder;
},
}
--- a/dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js
+++ b/dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js
@@ -15,16 +15,22 @@ const PROVIDER_CONTRACT_ID = "@mozilla.o
const SD_CONTRACT_ID = "@mozilla.org/toolkit/components/mdnsresponder/dns-sd;1";
const UUID_CONTRACT_ID = "@mozilla.org/uuid-generator;1";
const SERVER_CONTRACT_ID = "@mozilla.org/presentation/control-service;1";
const PREF_DISCOVERY = "dom.presentation.discovery.enabled";
const PREF_DISCOVERABLE = "dom.presentation.discoverable";
const PREF_DEVICENAME= "dom.presentation.device.name";
+const LATEST_VERSION = 1;
+const SERVICE_TYPE = "_presentation-ctrl._tcp";
+const versionAttr = Cc["@mozilla.org/hash-property-bag;1"]
+ .createInstance(Ci.nsIWritablePropertyBag2);
+versionAttr.setPropertyAsUint32("version", LATEST_VERSION);
+
var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
function MockFactory(aClass) {
this._cls = aClass;
}
MockFactory.prototype = {
createInstance: function(aOuter, aIID) {
if (aOuter) {
@@ -195,17 +201,17 @@ TestPresentationDeviceListener.prototype
function createDevice(host, port, serviceName, serviceType, domainName, attributes) {
let device = new MockDNSServiceInfo();
device.host = host || "";
device.port = port || 0;
device.address = host || "";
device.serviceName = serviceName || "";
device.serviceType = serviceType || "";
device.domainName = domainName || "";
- device.attributes = attributes || null;
+ device.attributes = attributes || versionAttr;
return device;
}
function registerService() {
Services.prefs.setBoolPref(PREF_DISCOVERABLE, true);
let mockObj = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
@@ -331,17 +337,17 @@ function registerServiceDynamically() {
}
function addDevice() {
Services.prefs.setBoolPref(PREF_DISCOVERY, true);
let mockDevice = createDevice("device.local",
12345,
"service.name",
- "_mozilla_papi._tcp");
+ SERVICE_TYPE);
let mockObj = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
startDiscovery: function(serviceType, listener) {
listener.onDiscoveryStarted(serviceType);
listener.onServiceFound(createDevice("",
0,
mockDevice.serviceName,
mockDevice.serviceType));
@@ -388,20 +394,21 @@ function handleSessionRequest() {
const testPresentationId = "test-presentation-id";
const testDeviceName = "test-device-name";
Services.prefs.setCharPref(PREF_DEVICENAME, testDeviceName);
let mockDevice = createDevice("device.local",
12345,
"service.name",
- "_mozilla_papi._tcp");
+ SERVICE_TYPE);
let mockSDObj = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
startDiscovery: function(serviceType, listener) {
+ do_print('xxx start discovery');
listener.onDiscoveryStarted(serviceType);
listener.onServiceFound(createDevice("",
0,
mockDevice.serviceName,
mockDevice.serviceType));
return {
QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
cancel: function() {}
@@ -413,64 +420,64 @@ function handleSessionRequest() {
mockDevice.port,
mockDevice.serviceName,
mockDevice.serviceType));
}
};
let mockServerObj = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]),
- requestSession: function(deviceInfo, url, presentationId) {
+ connect: function(deviceInfo) {
this.request = {
deviceInfo: deviceInfo,
- url: url,
- presentationId: presentationId,
};
return {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]),
};
},
id: "",
+ version: LATEST_VERSION,
+ isCompatibleServer: function(version) {
+ return this.version === version;
+ }
};
let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj);
let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj);
let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider);
let listener = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener,
Ci.nsISupportsWeakReference]),
addDevice: function(device) {
this.device = device;
},
};
provider.listener = listener;
- let controlChannel = listener.device.establishControlChannel(testUrl, testPresentationId);
+ let controlChannel = listener.device.establishControlChannel();
Assert.equal(mockServerObj.request.deviceInfo.id, mockDevice.host);
Assert.equal(mockServerObj.request.deviceInfo.address, mockDevice.host);
Assert.equal(mockServerObj.request.deviceInfo.port, mockDevice.port);
- Assert.equal(mockServerObj.request.url, testUrl);
- Assert.equal(mockServerObj.request.presentationId, testPresentationId);
Assert.equal(mockServerObj.id, testDeviceName);
provider.listener = null;
run_next_test();
}
function handleOnSessionRequest() {
Services.prefs.setBoolPref(PREF_DISCOVERY, true);
Services.prefs.setBoolPref(PREF_DISCOVERABLE, true);
let mockDevice = createDevice("device.local",
12345,
"service.name",
- "_mozilla_papi._tcp");
+ SERVICE_TYPE);
let mockSDObj = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
startDiscovery: function(serviceType, listener) {
listener.onDiscoveryStarted(serviceType);
listener.onServiceFound(createDevice("",
0,
mockDevice.serviceName,
mockDevice.serviceType));
@@ -489,16 +496,17 @@ function handleOnSessionRequest() {
};
let mockServerObj = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]),
startServer: function() {},
sessionRequest: function() {},
close: function() {},
id: '',
+ version: LATEST_VERSION,
port: 0,
listener: null,
};
let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj);
let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj);
let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider);
let listener = {
@@ -555,16 +563,17 @@ function handleOnSessionRequestFromUnkno
};
let mockServerObj = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]),
startServer: function() {},
sessionRequest: function() {},
close: function() {},
id: '',
+ version: LATEST_VERSION,
port: 0,
listener: null,
};
let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj);
let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj);
let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider);
let listener = {
@@ -613,17 +622,17 @@ function handleOnSessionRequestFromUnkno
provider.listener = null;
run_next_test();
}
function noAddDevice() {
Services.prefs.setBoolPref(PREF_DISCOVERY, false);
- let mockDevice = createDevice("device.local", 12345, "service.name", "_mozilla_papi._tcp");
+ let mockDevice = createDevice("device.local", 12345, "service.name", SERVICE_TYPE);
let mockObj = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
startDiscovery: function(serviceType, listener) {
Assert.ok(false, "shouldn't perform any device discovery");
},
registerService: function(serviceInfo, listener) {},
resolveService: function(serviceInfo, listener) {
}
@@ -640,24 +649,24 @@ function noAddDevice() {
};
provider.listener = listener;
provider.forceDiscovery();
provider.listener = null;
run_next_test();
}
-function ignoreSelfDevice() {
+function ignoreIncompatibleDevice() {
Services.prefs.setBoolPref(PREF_DISCOVERY, false);
Services.prefs.setBoolPref(PREF_DISCOVERABLE, true);
let mockDevice = createDevice("device.local",
12345,
"service.name",
- "_mozilla_papi._tcp");
+ SERVICE_TYPE);
let mockSDObj = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
startDiscovery: function(serviceType, listener) {
listener.onDiscoveryStarted(serviceType);
listener.onServiceFound(createDevice("",
0,
mockDevice.serviceName,
mockDevice.serviceType));
@@ -687,16 +696,20 @@ function ignoreSelfDevice() {
};
let mockServerObj = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]),
startServer: function() {},
sessionRequest: function() {},
close: function() {},
id: '',
+ version: LATEST_VERSION,
+ isCompatibleServer: function(version) {
+ return false;
+ },
port: 0,
listener: null,
};
let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj);
let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj);
let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider);
let listener = new TestPresentationDeviceListener();
@@ -708,23 +721,97 @@ function ignoreSelfDevice() {
// Start discovery
Services.prefs.setBoolPref(PREF_DISCOVERY, true);
Assert.equal(listener.count(), 0);
provider.listener = null;
run_next_test();
}
+
+function ignoreSelfDevice() {
+ Services.prefs.setBoolPref(PREF_DISCOVERY, false);
+ Services.prefs.setBoolPref(PREF_DISCOVERABLE, true);
+
+ let mockDevice = createDevice("device.local",
+ 12345,
+ "service.name",
+ SERVICE_TYPE);
+ let mockSDObj = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
+ startDiscovery: function(serviceType, listener) {
+ listener.onDiscoveryStarted(serviceType);
+ listener.onServiceFound(createDevice("",
+ 0,
+ mockDevice.serviceName,
+ mockDevice.serviceType));
+ return {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
+ cancel: function() {}
+ };
+ },
+ registerService: function(serviceInfo, listener) {
+ listener.onServiceRegistered(createDevice("",
+ 0,
+ mockDevice.serviceName,
+ mockDevice.serviceType));
+ return {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
+ cancel: function() {}
+ };
+ },
+ resolveService: function(serviceInfo, listener) {
+ Assert.equal(serviceInfo.serviceName, mockDevice.serviceName);
+ Assert.equal(serviceInfo.serviceType, mockDevice.serviceType);
+ listener.onServiceResolved(createDevice(mockDevice.host,
+ mockDevice.port,
+ mockDevice.serviceName,
+ mockDevice.serviceType));
+ }
+ };
+
+ let mockServerObj = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]),
+ startServer: function() {},
+ sessionRequest: function() {},
+ close: function() {},
+ id: '',
+ version: LATEST_VERSION,
+ isCompatibleServer: function(version) {
+ return this.version === version;
+ },
+ port: 0,
+ listener: null,
+ };
+
+ let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj);
+ let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj);
+ let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider);
+ let listener = new TestPresentationDeviceListener();
+
+ // Register service
+ provider.listener = listener;
+ Assert.equal(mockServerObj.id, mockDevice.host);
+
+ // Start discovery
+ Services.prefs.setBoolPref(PREF_DISCOVERY, true);
+ Assert.equal(listener.count(), 0);
+
+ provider.listener = null;
+
+ run_next_test();
+}
+
function addDeviceDynamically() {
Services.prefs.setBoolPref(PREF_DISCOVERY, false);
let mockDevice = createDevice("device.local",
12345,
"service.name",
- "_mozilla_papi._tcp");
+ SERVICE_TYPE);
let mockObj = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
startDiscovery: function(serviceType, listener) {
listener.onDiscoveryStarted(serviceType);
listener.onServiceFound(createDevice("",
0,
mockDevice.serviceName,
mockDevice.serviceType));
@@ -767,18 +854,18 @@ function addDeviceDynamically() {
provider.listener = null;
run_next_test();
}
function updateDevice() {
Services.prefs.setBoolPref(PREF_DISCOVERY, true);
- let mockDevice1 = createDevice("A.local", 12345, "N1", "_mozilla_papi._tcp");
- let mockDevice2 = createDevice("A.local", 23456, "N2", "_mozilla_papi._tcp");
+ let mockDevice1 = createDevice("A.local", 12345, "N1", SERVICE_TYPE);
+ let mockDevice2 = createDevice("A.local", 23456, "N2", SERVICE_TYPE);
let mockObj = {
discovered: false,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
startDiscovery: function(serviceType, listener) {
listener.onDiscoveryStarted(serviceType);
@@ -793,17 +880,17 @@ function updateDevice() {
QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
cancel: function() {
listener.onDiscoveryStopped(serviceType);
}
};
},
registerService: function(serviceInfo, listener) {},
resolveService: function(serviceInfo, listener) {
- Assert.equal(serviceInfo.serviceType, "_mozilla_papi._tcp");
+ Assert.equal(serviceInfo.serviceType, SERVICE_TYPE);
if (serviceInfo.serviceName == "N1") {
listener.onServiceResolved(mockDevice1);
} else if (serviceInfo.serviceName == "N2") {
listener.onServiceResolved(mockDevice2);
} else {
Assert.ok(false);
}
}
@@ -853,19 +940,19 @@ function updateDevice() {
provider.listener = null;
run_next_test();
}
function diffDiscovery() {
Services.prefs.setBoolPref(PREF_DISCOVERY, true);
- let mockDevice1 = createDevice("A.local", 12345, "N1", "_mozilla_papi._tcp");
- let mockDevice2 = createDevice("B.local", 23456, "N2", "_mozilla_papi._tcp");
- let mockDevice3 = createDevice("C.local", 45678, "N3", "_mozilla_papi._tcp");
+ let mockDevice1 = createDevice("A.local", 12345, "N1", SERVICE_TYPE);
+ let mockDevice2 = createDevice("B.local", 23456, "N2", SERVICE_TYPE);
+ let mockDevice3 = createDevice("C.local", 45678, "N3", SERVICE_TYPE);
let mockObj = {
discovered: false,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
startDiscovery: function(serviceType, listener) {
listener.onDiscoveryStarted(serviceType);
@@ -882,17 +969,17 @@ function diffDiscovery() {
QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
cancel: function() {
listener.onDiscoveryStopped(serviceType);
}
};
},
registerService: function(serviceInfo, listener) {},
resolveService: function(serviceInfo, listener) {
- Assert.equal(serviceInfo.serviceType, "_mozilla_papi._tcp");
+ Assert.equal(serviceInfo.serviceType, SERVICE_TYPE);
if (serviceInfo.serviceName == "N1") {
listener.onServiceResolved(mockDevice1);
} else if (serviceInfo.serviceName == "N2") {
listener.onServiceResolved(mockDevice2);
} else if (serviceInfo.serviceName == "N3") {
listener.onServiceResolved(mockDevice3);
} else {
Assert.ok(false);
@@ -939,17 +1026,17 @@ function diffDiscovery() {
function serverClosed() {
Services.prefs.setBoolPref(PREF_DISCOVERABLE, true);
Services.prefs.setBoolPref(PREF_DISCOVERY, true);
let mockDevice = createDevice("device.local",
12345,
"service.name",
- "_mozilla_papi._tcp");
+ SERVICE_TYPE);
let mockObj = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
startDiscovery: function(serviceType, listener) {
listener.onDiscoveryStarted(serviceType);
listener.onServiceFound(createDevice("",
0,
mockDevice.serviceName,
@@ -1029,16 +1116,17 @@ function run_test() {
add_test(registerService);
add_test(noRegisterService);
add_test(registerServiceDynamically);
add_test(addDevice);
add_test(handleSessionRequest);
add_test(handleOnSessionRequest);
add_test(handleOnSessionRequestFromUnknownDevice);
add_test(noAddDevice);
+ add_test(ignoreIncompatibleDevice);
add_test(ignoreSelfDevice);
add_test(addDeviceDynamically);
add_test(updateDevice);
add_test(diffDiscovery);
add_test(serverClosed);
run_next_test();
}
--- a/dom/presentation/tests/xpcshell/test_presentation_device_manager.js
+++ b/dom/presentation/tests/xpcshell/test_presentation_device_manager.js
@@ -16,17 +16,18 @@ function TestPresentationDevice() {}
function TestPresentationControlChannel() {}
TestPresentationControlChannel.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]),
sendOffer: function(offer) {},
sendAnswer: function(answer) {},
- close: function() {},
+ disconnect: function() {},
+ launch: function() {},
set listener(listener) {},
get listener() {},
};
var testProvider = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceProvider]),
forceDiscovery: function() {
new file mode 100644
--- /dev/null
+++ b/dom/presentation/tests/xpcshell/test_presentation_state_machine.js
@@ -0,0 +1,195 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+/* jshint esnext:true, globalstrict:true, moz:true, undef:true, unused:true */
+/* globals Components,Assert,run_next_test,add_test,do_execute_soon */
+
+'use strict';
+
+const { utils: Cu, results: Cr } = Components;
+
+/* globals ControllerStateMachine */
+Cu.import('resource://gre/modules/presentation/ControllerStateMachine.jsm');
+/* globals ReceiverStateMachine */
+Cu.import('resource://gre/modules/presentation/ReceiverStateMachine.jsm');
+/* globals State */
+Cu.import('resource://gre/modules/presentation/StateMachineHelper.jsm');
+
+const testControllerId = 'test-controller-id';
+const testPresentationId = 'test-presentation-id';
+const testUrl = 'http://example.org';
+
+let mockControllerChannel = {};
+let mockReceiverChannel = {};
+
+let controllerState = new ControllerStateMachine(mockControllerChannel, testControllerId);
+let receiverState = new ReceiverStateMachine(mockReceiverChannel);
+
+mockControllerChannel.sendCommand = function(command) {
+ do_execute_soon(function() {
+ receiverState.onCommand(command);
+ });
+};
+
+mockReceiverChannel.sendCommand = function(command) {
+ do_execute_soon(function() {
+ controllerState.onCommand(command);
+ });
+};
+
+function connect() {
+ Assert.equal(controllerState.state, State.INIT, 'controller in init state');
+ Assert.equal(receiverState.state, State.INIT, 'receiver in init state');
+ // step 1: underlying connection is ready
+ controllerState.onChannelReady();
+ Assert.equal(controllerState.state, State.CONNECTING, 'controller in connecting state');
+ receiverState.onChannelReady();
+ Assert.equal(receiverState.state, State.CONNECTING, 'receiver in connecting state');
+
+ // step 2: receiver reply to connect command
+ mockReceiverChannel.notifyDeviceConnected = function(deviceId) {
+ Assert.equal(deviceId, testControllerId, 'receiver connect to mock controller');
+ Assert.equal(receiverState.state, State.CONNECTED, 'receiver in connected state');
+
+ // step 3: controller receive connect-ack command
+ mockControllerChannel.notifyDeviceConnected = function() {
+ Assert.equal(controllerState.state, State.CONNECTED, 'controller in connected state');
+ run_next_test();
+ };
+ };
+}
+
+function launch() {
+ Assert.equal(controllerState.state, State.CONNECTED, 'controller in connected state');
+ Assert.equal(receiverState.state, State.CONNECTED, 'receiver in connected state');
+
+ controllerState.launch(testPresentationId, testUrl);
+ mockReceiverChannel.notifyLaunch = function(presentationId, url) {
+ Assert.equal(receiverState.state, State.CONNECTED, 'receiver in connected state');
+ Assert.equal(presentationId, testPresentationId, 'expected presentationId received');
+ Assert.equal(url, testUrl, 'expected url received');
+
+ mockControllerChannel.notifyLaunch = function(presentationId) {
+ Assert.equal(controllerState.state, State.CONNECTED, 'controller in connected state');
+ Assert.equal(presentationId, testPresentationId, 'expected presentationId received from ack');
+
+ run_next_test();
+ };
+ };
+}
+
+function exchangeSDP() {
+ Assert.equal(controllerState.state, State.CONNECTED, 'controller in connected state');
+ Assert.equal(receiverState.state, State.CONNECTED, 'receiver in connected state');
+
+ const testOffer = 'test-offer';
+ const testAnswer = 'test-answer';
+ const testIceCandidate = 'test-ice-candidate';
+ controllerState.sendOffer(testOffer);
+ mockReceiverChannel.notifyOffer = function(offer) {
+ Assert.equal(offer, testOffer, 'expected offer received');
+
+ receiverState.sendAnswer(testAnswer);
+ mockControllerChannel.notifyAnswer = function(answer) {
+ Assert.equal(answer, testAnswer, 'expected answer received');
+
+ controllerState.updateIceCandidate(testIceCandidate);
+ mockReceiverChannel.notifyIceCandidate = function(candidate) {
+ Assert.equal(candidate, testIceCandidate, 'expected ice candidate received in receiver');
+
+ receiverState.updateIceCandidate(testIceCandidate);
+ mockControllerChannel.notifyIceCandidate = function(candidate) {
+ Assert.equal(candidate, testIceCandidate, 'expected ice candidate received in controller');
+
+ run_next_test();
+ };
+ };
+ };
+ };
+}
+
+function disconnect() {
+ // step 1: controller send disconnect command
+ controllerState.onChannelClosed(Cr.NS_OK, false);
+ Assert.equal(controllerState.state, State.CLOSING, 'controller in closing state');
+
+ mockReceiverChannel.notifyDisconnected = function(reason) {
+ Assert.equal(reason, Cr.NS_OK, 'receive close reason');
+ Assert.equal(receiverState.state, State.CLOSED, 'receiver in closed state');
+
+ receiverState.onChannelClosed(Cr.NS_OK, true);
+ Assert.equal(receiverState.state, State.CLOSED, 'receiver in closed state');
+
+ mockControllerChannel.notifyDisconnected = function(reason) {
+ Assert.equal(reason, Cr.NS_OK, 'receive close reason');
+ Assert.equal(controllerState.state, State.CLOSED, 'controller in closed state');
+
+ run_next_test();
+ };
+ controllerState.onChannelClosed(Cr.NS_OK, true);
+ };
+}
+
+function receiverDisconnect() {
+ // initial state: controller and receiver are connected
+ controllerState.state = State.CONNECTED;
+ receiverState.state = State.CONNECTED;
+
+ // step 1: controller send disconnect command
+ receiverState.onChannelClosed(Cr.NS_OK, false);
+ Assert.equal(receiverState.state, State.CLOSING, 'receiver in closing state');
+
+ mockControllerChannel.notifyDisconnected = function(reason) {
+ Assert.equal(reason, Cr.NS_OK, 'receive close reason');
+ Assert.equal(controllerState.state, State.CLOSED, 'controller in closed state');
+
+ controllerState.onChannelClosed(Cr.NS_OK, true);
+ Assert.equal(controllerState.state, State.CLOSED, 'controller in closed state');
+
+ mockReceiverChannel.notifyDisconnected = function(reason) {
+ Assert.equal(reason, Cr.NS_OK, 'receive close reason');
+ Assert.equal(receiverState.state, State.CLOSED, 'receiver in closed state');
+
+ run_next_test();
+ };
+ receiverState.onChannelClosed(Cr.NS_OK, true);
+ };
+}
+
+function abnormalDisconnect() {
+ // initial state: controller and receiver are connected
+ controllerState.state = State.CONNECTED;
+ receiverState.state = State.CONNECTED;
+
+ const testErrorReason = Cr.NS_ERROR_FAILURE;
+ // step 1: controller send disconnect command
+ controllerState.onChannelClosed(testErrorReason, false);
+ Assert.equal(controllerState.state, State.CLOSING, 'controller in closing state');
+
+ mockReceiverChannel.notifyDisconnected = function(reason) {
+ Assert.equal(reason, testErrorReason, 'receive abnormal close reason');
+ Assert.equal(receiverState.state, State.CLOSED, 'receiver in closed state');
+
+ receiverState.onChannelClosed(Cr.NS_OK, true);
+ Assert.equal(receiverState.state, State.CLOSED, 'receiver in closed state');
+
+ mockControllerChannel.notifyDisconnected = function(reason) {
+ Assert.equal(reason, testErrorReason, 'receive abnormal close reason');
+ Assert.equal(controllerState.state, State.CLOSED, 'controller in closed state');
+
+ run_next_test();
+ };
+ controllerState.onChannelClosed(Cr.NS_OK, true);
+ };
+}
+
+add_test(connect);
+add_test(launch);
+add_test(exchangeSDP);
+add_test(disconnect);
+add_test(receiverDisconnect);
+add_test(abnormalDisconnect);
+
+function run_test() { // jshint ignore:line
+ run_next_test();
+}
--- a/dom/presentation/tests/xpcshell/test_tcp_control_channel.js
+++ b/dom/presentation/tests/xpcshell/test_tcp_control_channel.js
@@ -4,17 +4,17 @@
'use strict';
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/Services.jsm');
-var tps;
+var pcs;
// Call |run_next_test| if all functions in |names| are called
function makeJointSuccess(names) {
let funcs = {}, successCount = 0;
names.forEach(function(name) {
funcs[name] = function() {
do_print('got expected: ' + name);
if (++successCount === names.length)
@@ -51,35 +51,35 @@ var candidate;
const OFFER_ADDRESS = '192.168.123.123';
const OFFER_PORT = 123;
// controller's presentation channel description
const ANSWER_ADDRESS = '192.168.321.321';
const ANSWER_PORT = 321;
function loopOfferAnser() {
- tps = Cc["@mozilla.org/presentation/control-service;1"]
+ pcs = Cc["@mozilla.org/presentation/control-service;1"]
.createInstance(Ci.nsIPresentationControlService);
- tps.id = 'controllerID';
- tps.startServer(PRESENTER_CONTROL_CHANNEL_PORT);
+ pcs.id = 'controllerID';
+ pcs.startServer(PRESENTER_CONTROL_CHANNEL_PORT);
testPresentationServer();
}
function testPresentationServer() {
let yayFuncs = makeJointSuccess(['controllerControlChannelClose',
'presenterControlChannelClose']);
let controllerControlChannel;
- tps.listener = {
+ pcs.listener = {
onSessionRequest: function(deviceInfo, url, presentationId, controlChannel) {
controllerControlChannel = controlChannel;
- Assert.equal(deviceInfo.id, tps.id, 'expected device id');
+ Assert.equal(deviceInfo.id, pcs.id, 'expected device id');
Assert.equal(deviceInfo.address, '127.0.0.1', 'expected device address');
Assert.equal(url, 'http://example.com', 'expected url');
Assert.equal(presentationId, 'testPresentationId', 'expected presentation id');
controllerControlChannel.listener = {
status: 'created',
onOffer: function(aOffer) {
Assert.equal(this.status, 'opened', '1. controllerControlChannel: get offer, send answer');
@@ -104,25 +104,25 @@ function testPresentationServer() {
onIceCandidate: function(aCandidate) {
Assert.ok(true, '3. controllerControlChannel: get ice candidate, close channel');
let recvCandidate = JSON.parse(aCandidate);
for (let key in recvCandidate) {
if (typeof(recvCandidate[key]) !== "function") {
Assert.equal(recvCandidate[key], candidate[key], "key " + key + " should match.");
}
}
- controllerControlChannel.close(CLOSE_CONTROL_CHANNEL_REASON);
+ controllerControlChannel.disconnect(CLOSE_CONTROL_CHANNEL_REASON);
},
- notifyOpened: function() {
+ notifyConnected: function() {
Assert.equal(this.status, 'created', '0. controllerControlChannel: opened');
this.status = 'opened';
},
- notifyClosed: function(aReason) {
+ notifyDisconnected: function(aReason) {
Assert.equal(this.status, 'onOffer', '4. controllerControlChannel: closed');
- Assert.equal(aReason, CLOSE_CONTROL_CHANNEL_REASON, 'presenterControlChannel notify closed');
+ Assert.equal(aReason, CLOSE_CONTROL_CHANNEL_REASON, 'controllerControlChannel notify closed');
this.status = 'closed';
yayFuncs.controllerControlChannelClose();
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
};
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlServerListener]),
@@ -130,19 +130,17 @@ function testPresentationServer() {
let presenterDeviceInfo = {
id: 'presentatorID',
address: '127.0.0.1',
port: PRESENTER_CONTROL_CHANNEL_PORT,
QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPDeviceInfo]),
};
- let presenterControlChannel = tps.requestSession(presenterDeviceInfo,
- 'http://example.com',
- 'testPresentationId');
+ let presenterControlChannel = pcs.connect(presenterDeviceInfo);
presenterControlChannel.listener = {
status: 'created',
onOffer: function(offer) {
Assert.ok(false, 'get offer');
},
onAnswer: function(aAnswer) {
Assert.equal(this.status, 'opened', '2. presenterControlChannel: get answer, send ICE candidate');
@@ -157,70 +155,71 @@ function testPresentationServer() {
sdpMid: "helloworld",
sdpMLineIndex: 1
};
presenterControlChannel.sendIceCandidate(JSON.stringify(candidate));
},
onIceCandidate: function(aCandidate) {
Assert.ok(false, 'get ICE candidate');
},
- notifyOpened: function() {
+ notifyConnected: function() {
Assert.equal(this.status, 'created', '0. presenterControlChannel: opened, send offer');
+ presenterControlChannel.launch('testPresentationId', 'http://example.com');
this.status = 'opened';
try {
let tcpType = Ci.nsIPresentationChannelDescription.TYPE_TCP;
let offer = new TestDescription(tcpType, [OFFER_ADDRESS], OFFER_PORT)
presenterControlChannel.sendOffer(offer);
} catch (e) {
Assert.ok(false, 'sending offer fails:' + e);
}
},
- notifyClosed: function(aReason) {
+ notifyDisconnected: function(aReason) {
this.status = 'closed';
Assert.equal(aReason, CLOSE_CONTROL_CHANNEL_REASON, '4. presenterControlChannel notify closed');
yayFuncs.presenterControlChannelClose();
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]),
};
}
function setOffline() {
- tps.listener = {
+ pcs.listener = {
onPortChange: function(aPort) {
Assert.notEqual(aPort, 0, 'TCPPresentationServer port changed and the port should be valid');
- tps.close();
+ pcs.close();
run_next_test();
},
};
// Let the server socket restart automatically.
Services.io.offline = true;
Services.io.offline = false;
}
function oneMoreLoop() {
try {
- tps.startServer(PRESENTER_CONTROL_CHANNEL_PORT);
+ pcs.startServer(PRESENTER_CONTROL_CHANNEL_PORT);
testPresentationServer();
} catch (e) {
Assert.ok(false, 'TCP presentation init fail:' + e);
run_next_test();
}
}
function shutdown()
{
- tps.listener = {
+ pcs.listener = {
onPortChange: function(aPort) {
Assert.ok(false, 'TCPPresentationServer port changed');
},
};
- tps.close();
- Assert.equal(tps.port, 0, "TCPPresentationServer closed");
+ pcs.close();
+ Assert.equal(pcs.port, 0, "TCPPresentationServer closed");
run_next_test();
}
// Test manually close control channel with NS_ERROR_FAILURE
function changeCloseReason() {
CLOSE_CONTROL_CHANNEL_REASON = Cr.NS_ERROR_FAILURE;
run_next_test();
}
--- a/dom/presentation/tests/xpcshell/xpcshell.ini
+++ b/dom/presentation/tests/xpcshell/xpcshell.ini
@@ -1,8 +1,9 @@
[DEFAULT]
head =
tail =
[test_multicast_dns_device_provider.js]
[test_presentation_device_manager.js]
[test_presentation_session_transport.js]
[test_tcp_control_channel.js]
+[test_presentation_state_machine.js]
--- a/dom/push/test/xpcshell/xpcshell.ini
+++ b/dom/push/test/xpcshell/xpcshell.ini
@@ -1,12 +1,11 @@
[DEFAULT]
head = head.js head-http2.js
tail =
-firefox-appdir = browser
# Push notifications and alarms are currently disabled on Android.
skip-if = toolkit == 'android'
[test_clear_forgetAboutSite.js]
[test_clear_origin_data.js]
[test_crypto.js]
[test_drop_expired.js]
[test_handler_service.js]
--- a/dom/security/test/csp/file_main.js
+++ b/dom/security/test/csp/file_main.js
@@ -10,19 +10,17 @@ function doXHR(uri, callback) {
} catch(ex) {}
}
doXHR("http://mochi.test:8888/tests/dom/security/test/csp/file_CSP.sjs?testid=xhr_good");
doXHR("http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=xhr_bad");
fetch("http://mochi.test:8888/tests/dom/security/test/csp/file_CSP.sjs?testid=fetch_good");
fetch("http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=fetch_bad");
navigator.sendBeacon("http://mochi.test:8888/tests/dom/security/test/csp/file_CSP.sjs?testid=beacon_good");
-try {
- navigator.sendBeacon("http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=beacon_bad");
-} catch(ex) {}
+navigator.sendBeacon("http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=beacon_bad");
var topWorkerBlob;
var nestedWorkerBlob;
doXHR("file_main_worker.js", function (topResponse) {
topWorkerBlob = URL.createObjectURL(topResponse);
doXHR("file_child_worker.js", function (response) {
nestedWorkerBlob = URL.createObjectURL(response);
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/file_sendbeacon.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="Content-Security-Policy" content= "connect-src 'none'">
+ <title>Bug 1234813 - sendBeacon should not throw if blocked by Content Policy</title>
+</head>
+<body>
+
+<script type="application/javascript">
+try {
+ navigator.sendBeacon("http://example.com/sendbeaconintonirvana");
+ window.parent.postMessage({result: "blocked-beacon-does-not-throw"}, "*");
+ }
+ catch (e) {
+ window.parent.postMessage({result: "blocked-beacon-throws"}, "*");
+ }
+</script>
+
+</body>
+</html>
--- a/dom/security/test/csp/mochitest.ini
+++ b/dom/security/test/csp/mochitest.ini
@@ -179,16 +179,17 @@ support-files =
file_sandbox_7.html
file_sandbox_8.html
file_sandbox_9.html
file_sandbox_10.html
file_sandbox_11.html
file_sandbox_12.html
file_require_sri_meta.sjs
file_require_sri_meta.js
+ file_sendbeacon.html
[test_base-uri.html]
[test_blob_data_schemes.html]
[test_connect-src.html]
[test_CSP.html]
[test_allow_https_schemes.html]
skip-if = buildapp == 'b2g' #no ssl support
[test_bug663567.html]
@@ -270,8 +271,9 @@ tags = mcb
tags = mcb
[test_form_action_blocks_url.html]
[test_meta_whitespace_skipping.html]
[test_iframe_sandbox.html]
[test_iframe_sandbox_top_1.html]
[test_sandbox.html]
[test_ping.html]
[test_require_sri_meta.html]
+[test_sendbeacon.html]
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/test_sendbeacon.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1234813 - sendBeacon should not throw if blocked by Content Policy</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<iframe style="width:100%;" id="testframe" src="file_sendbeacon.html"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * Let's try to fire a sendBeacon which gets blocked by CSP. Let's make sure
+ * sendBeacon does not throw an exception.
+ */
+SimpleTest.waitForExplicitFinish();
+
+// a postMessage handler used to bubble up the
+// result from within the iframe.
+window.addEventListener("message", receiveMessage, false);
+function receiveMessage(event) {
+ var result = event.data.result;
+ is(result, "blocked-beacon-does-not-throw", "sendBeacon should not throw");
+ window.removeEventListener("message", receiveMessage, false);
+ SimpleTest.finish();
+}
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/security/test/general/file_contentpolicytype_targeted_link_iframe.sjs
@@ -0,0 +1,46 @@
+// custom *.sjs for Bug 1255240
+
+const TEST_FRAME = `
+ <!DOCTYPE HTML>
+ <html>
+ <head><meta charset='utf-8'></head>
+ <body>
+ <a id='testlink' target='innerframe' href='file_contentpolicytype_targeted_link_iframe.sjs?innerframe'>click me</a>
+ <iframe name='innerframe'></iframe>
+ <script type='text/javascript'>
+ var link = document.getElementById('testlink');
+ testlink.click();
+ </script>
+ </body>
+ </html> `;
+
+const INNER_FRAME = `
+ <!DOCTYPE HTML>
+ <html>
+ <head><meta charset='utf-8'></head>
+ hello world!
+ </body>
+ </html>`;
+
+function handleRequest(request, response)
+{
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/html", false);
+
+ var queryString = request.queryString;
+
+ if (queryString === "testframe") {
+ response.write(TEST_FRAME);
+ return;
+ }
+
+ if (queryString === "innerframe") {
+ response.write(INNER_FRAME);
+ return;
+ }
+
+ // we should never get here, but just in case
+ // return something unexpected
+ response.write("do'h");
+}
new file mode 100644
--- /dev/null
+++ b/dom/security/test/general/mochitest.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+support-files =
+ file_contentpolicytype_targeted_link_iframe.sjs
+
+[test_contentpolicytype_targeted_link_iframe.html]
new file mode 100644
--- /dev/null
+++ b/dom/security/test/general/test_contentpolicytype_targeted_link_iframe.html
@@ -0,0 +1,100 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1255240 - Test content policy types within content policies for targeted links in iframes</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * Let's load a link into a targeted iframe and make sure the content policy
+ * type used for content policy checks is of TYPE_SUBDOCUMENT both times.
+ */
+
+const Cc = SpecialPowers.Cc;
+const Ci = SpecialPowers.Ci;
+
+const EXPECTED_CONTENT_TYPE = Ci.nsIContentPolicy.TYPE_SUBDOCUMENT;
+const EXPECTED_URL =
+ "http://mochi.test:8888/tests/dom/security/test/general/file_contentpolicytype_targeted_link_iframe.sjs?innerframe";
+const TEST_FRAME_URL =
+ "file_contentpolicytype_targeted_link_iframe.sjs?testframe";
+
+// we should get two content policy chcks with the same content policy type
+const EXPECTED_RESULTS = 2;
+var testCounter = 0;
+
+// ----- START Content Policy implementation for the test
+var categoryManager = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
+
+const POLICYNAME = "@mozilla.org/iframetestpolicy;1";
+const POLICYID = SpecialPowers.wrap(SpecialPowers.Components)
+ .ID("{6cc95ef3-40e1-4d59-87f0-86f100373227}");
+
+var policy = {
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ iid = SpecialPowers.wrap(iid);
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIFactory) ||
+ iid.equals(Ci.nsIContentPolicy))
+ return this;
+
+ throw SpecialPowers.Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+ // nsIFactory implementation
+ createInstance: function(outer, iid) {
+ return this.QueryInterface(iid);
+ },
+
+ // nsIContentPolicy implementation
+ shouldLoad: function(contentType, contentLocation, requestOrigin,
+ context, mimeTypeGuess, extra) {
+
+ // make sure we get the right amount of content policy calls
+ // e.g. about:blank also gets chrcked by content policies
+ if (contentLocation.asciiSpec === EXPECTED_URL) {
+ is(contentType, EXPECTED_CONTENT_TYPE,
+ "content policy type should TYPESUBDOCUMENT");
+ testCounter++;
+ }
+
+ if (testCounter === EXPECTED_RESULTS) {
+ categoryManager.deleteCategoryEntry("content-policy", POLICYNAME, false);
+ SimpleTest.finish();
+ }
+ return Ci.nsIContentPolicy.ACCEPT;
+ },
+
+ shouldProcess: function(contentType, contentLocation, requestOrigin,
+ context, mimeTypeGuess, extra) {
+ return Ci.nsIContentPolicy.ACCEPT;
+ }
+}
+policy = SpecialPowers.wrapCallbackObject(policy);
+
+// Register content policy
+var componentManager = SpecialPowers.wrap(SpecialPowers.Components).manager
+ .QueryInterface(Ci.nsIComponentRegistrar);
+
+componentManager.registerFactory(POLICYID, "Test content policy", POLICYNAME, policy);
+categoryManager.addCategoryEntry("content-policy", POLICYNAME, POLICYNAME, false, true);
+
+// ----- END Content Policy implementation for the test
+
+
+// start the test
+SimpleTest.waitForExplicitFinish();
+var testframe = document.getElementById("testframe");
+testframe.src = TEST_FRAME_URL;
+
+</script>
+</body>
+</html>
--- a/dom/security/test/moz.build
+++ b/dom/security/test/moz.build
@@ -10,16 +10,17 @@ XPCSHELL_TESTS_MANIFESTS += [
GeckoCppUnitTests([
'TestCSPParser',
])
MOCHITEST_MANIFESTS += [
'cors/mochitest.ini',
'csp/mochitest.ini',
+ 'general/mochitest.ini',
'mixedcontentblocker/mochitest.ini',
'sri/mochitest.ini',
]
MOCHITEST_CHROME_MANIFESTS += [
'csp/chrome.ini',
]
--- a/image/SourceBuffer.cpp
+++ b/image/SourceBuffer.cpp
@@ -98,16 +98,17 @@ SourceBuffer::CreateChunk(size_t aCapaci
size_t finalCapacity = aRoundUp ? RoundedUpCapacity(aCapacity)
: aCapacity;
// Use the size of the SurfaceCache as an additional heuristic to avoid
// allocating huge buffers. Generally images do not get smaller when decoded,
// so if we could store the source data in the SurfaceCache, we assume that
// there's no way we'll be able to store the decoded version.
if (MOZ_UNLIKELY(!SurfaceCache::CanHold(finalCapacity))) {
+ NS_WARNING("SourceBuffer refused to create chunk too large for SurfaceCache");
return Nothing();
}
return Some(Chunk(finalCapacity));
}
nsresult
SourceBuffer::Compact()
--- a/js/public/UbiNode.h
+++ b/js/public/UbiNode.h
@@ -676,16 +676,18 @@ class Node {
mozilla::AlignedStorage2<Base> storage;
Base* base() { return storage.addr(); }
const Base* base() const { return storage.addr(); }
template<typename T>
void construct(T* ptr) {
static_assert(sizeof(Concrete<T>) == sizeof(*base()),
"ubi::Base specializations must be the same size as ubi::Base");
+ static_assert(mozilla::IsBaseOf<Base, Concrete<T>>::value,
+ "ubi::Concrete<T> must inherit from ubi::Base");
Concrete<T>::construct(base(), ptr);
}
struct ConstructFunctor;
public:
Node() { construct<void>(nullptr); }
template<typename T>
--- a/js/src/builtin/Object.js
+++ b/js/src/builtin/Object.js
@@ -83,21 +83,16 @@ function Object_toLocaleString() {
var O = this;
// Step 2.
return callContentFunction(O.toString, O);
}
// ES7 draft (2016 March 8) B.2.2.3
function ObjectDefineSetter(name, setter) {
- if (this === null || this === undefined)
- AddContentTelemetry(TELEMETRY_DEFINE_GETTER_SETTER_THIS_NULL_UNDEFINED, 1);
- else
- AddContentTelemetry(TELEMETRY_DEFINE_GETTER_SETTER_THIS_NULL_UNDEFINED, 0);
-
// Step 1.
var object = ToObject(this);
// Step 2.
if (!IsCallable(setter))
ThrowTypeError(JSMSG_BAD_GETTER_OR_SETTER, "setter");
// Step 3.
@@ -114,21 +109,16 @@ function ObjectDefineSetter(name, setter
// Step 5.
std_Object_defineProperty(object, key, desc);
// Step 6. (implicit)
}
// ES7 draft (2016 March 8) B.2.2.2
function ObjectDefineGetter(name, getter) {
- if (this === null || this === undefined)
- AddContentTelemetry(TELEMETRY_DEFINE_GETTER_SETTER_THIS_NULL_UNDEFINED, 1);
- else
- AddContentTelemetry(TELEMETRY_DEFINE_GETTER_SETTER_THIS_NULL_UNDEFINED, 0);
-
// Step 1.
var object = ToObject(this);
// Step 2.
if (!IsCallable(getter))
ThrowTypeError(JSMSG_BAD_GETTER_OR_SETTER, "getter");
// Step 3.
--- a/js/src/builtin/SelfHostingDefines.h
+++ b/js/src/builtin/SelfHostingDefines.h
@@ -85,17 +85,16 @@
#define PROMISE_REJECTION_TRACKER_OPERATION_HANDLE true
// NB: keep these in sync with the copy in jsfriendapi.h.
#define JSITER_OWNONLY 0x8 /* iterate over obj's own properties only */
#define JSITER_HIDDEN 0x10 /* also enumerate non-enumerable properties */
#define JSITER_SYMBOLS 0x20 /* also include symbol property keys */
#define JSITER_SYMBOLSONLY 0x40 /* exclude string property keys */
-#define TELEMETRY_DEFINE_GETTER_SETTER_THIS_NULL_UNDEFINED 25
#define REGEXP_FLAGS_SLOT 2
#define REGEXP_IGNORECASE_FLAG 0x01
#define REGEXP_GLOBAL_FLAG 0x02
#define REGEXP_MULTILINE_FLAG 0x04
#define REGEXP_STICKY_FLAG 0x08
#define REGEXP_UNICODE_FLAG 0x10
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -129,23 +129,19 @@ enum {
JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS,
JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS,
JS_TELEMETRY_GC_MINOR_REASON,
JS_TELEMETRY_GC_MINOR_REASON_LONG,
JS_TELEMETRY_GC_MINOR_US,
JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT,
JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS,
JS_TELEMETRY_ADDON_EXCEPTIONS,
- JS_TELEMETRY_DEFINE_GETTER_SETTER_THIS_NULL_UNDEFINED,
JS_TELEMETRY_END
};
-static_assert(JS_TELEMETRY_DEFINE_GETTER_SETTER_THIS_NULL_UNDEFINED == 25,
- "This value needs to be kept in sync with SelfHostingDefines.h");
-
typedef void
(*JSAccumulateTelemetryDataCallback)(int id, uint32_t sample, const char* key);
extern JS_FRIEND_API(void)
JS_SetAccumulateTelemetryCallback(JSContext* cx, JSAccumulateTelemetryDataCallback callback);
extern JS_FRIEND_API(bool)
JS_GetIsSecureContext(JSCompartment* compartment);
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -3227,20 +3227,16 @@ AccumulateTelemetryCallback(int id, uint
Telemetry::Accumulate(Telemetry::JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, sample);
break;
case JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS:
Telemetry::Accumulate(Telemetry::JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS, sample);
break;
case JS_TELEMETRY_ADDON_EXCEPTIONS:
Telemetry::Accumulate(Telemetry::JS_TELEMETRY_ADDON_EXCEPTIONS, nsDependentCString(key), sample);
break;
- case JS_TELEMETRY_DEFINE_GETTER_SETTER_THIS_NULL_UNDEFINED:
- MOZ_ASSERT(sample == 0 || sample == 1);
- Telemetry::Accumulate(Telemetry::JS_DEFINE_GETTER_SETTER_THIS_NULL_UNDEFINED, sample);
- break;
default:
MOZ_ASSERT_UNREACHABLE("Unexpected JS_TELEMETRY id");
}
}
static void
CompartmentNameCallback(JSRuntime* rt, JSCompartment* comp,
char* buf, size_t bufsize)
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -78,25 +78,22 @@ ElementTagToString(dom::Element* aElemen
nsCString result;
nsDependentAtomString buf(aElement->NodeInfo()->NameAtom());
result.AppendPrintf("(%s@%p)", NS_ConvertUTF16toUTF8(buf).get(), aElement);
return result;
}
#endif
RestyleManager::RestyleManager(nsPresContext* aPresContext)
- : mPresContext(aPresContext)
+ : RestyleManagerBase(aPresContext)
, mDoRebuildAllStyleData(false)
, mInRebuildAllStyleData(false)
- , mObservingRefreshDriver(false)
, mInStyleRefresh(false)
, mSkipAnimationRules(false)
, mHavePendingNonAnimationRestyles(false)
- , mRestyleGeneration(1)
- , mHoverGeneration(0)
, mRebuildAllExtraHint(nsChangeHint(0))
, mRebuildAllRestyleHint(nsRestyleHint(0))
, mAnimationGeneration(0)
, mReframingStyleContexts(nullptr)
, mAnimationsWithDestroyedFrame(nullptr)
, mPendingRestyles(ELEMENT_HAS_PENDING_RESTYLE |
ELEMENT_IS_POTENTIAL_RESTYLE_ROOT |
ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR)
@@ -215,17 +212,17 @@ SyncViewsAndInvalidateDescendants(nsIFra
* the restyled element's principle frame to one of its ancestor frames based
* on what nsCSSRendering::FindBackground returns, since the background style
* may have been propagated up to an ancestor frame. Processing hints using an
* ancestor frame is fine in general, but nsChangeHint_ChildrenOnlyTransform is
* a special case since it is intended to update the children of a specific
* frame.
*/
static nsIFrame*
-GetFrameForChildrenOnlyTransformHint(nsIFrame *aFrame)
+GetFrameForChildrenOnlyTransformHint(nsIFrame* aFrame)
{
if (aFrame->GetType() == nsGkAtoms::viewportFrame) {
// This happens if the root-<svg> is fixed positioned, in which case we
// can't use aFrame->GetContent() to find the primary frame, since
// GetContent() returns nullptr for ViewportFrame.
aFrame = aFrame->PrincipalChildList().FirstChild();
}
// For an nsHTMLScrollFrame, this will get the SVG frame that has the
@@ -352,34 +349,34 @@ ApplyRenderingChangeToTree(nsPresContext
// We check StyleDisplay()->HasTransformStyle() in addition to checking
// IsTransformed() since we can get here for some frames that don't support
// CSS transforms.
NS_ASSERTION(!(aChange & nsChangeHint_UpdateTransformLayer) ||
aFrame->IsTransformed() ||
aFrame->StyleDisplay()->HasTransformStyle(),
"Unexpected UpdateTransformLayer hint");
- nsIPresShell *shell = aPresContext->PresShell();
+ nsIPresShell* shell = aPresContext->PresShell();
if (shell->IsPaintingSuppressed()) {
// Don't allow synchronous rendering changes when painting is turned off.
aChange &= ~nsChangeHint_RepaintFrame;
if (!aChange) {
return;
}
}
// Trigger rendering updates by damaging this frame and any
// continuations of this frame.
#ifdef DEBUG
gInApplyRenderingChangeToTree = true;
#endif
if (aChange & nsChangeHint_RepaintFrame) {
// If the frame's background is propagated to an ancestor, walk up to
// that ancestor and apply the RepaintFrame change hint to it.
- nsStyleContext *bgSC;
+ nsStyleContext* bgSC;
nsIFrame* propagatedFrame = aFrame;
while (!nsCSSRendering::FindBackground(propagatedFrame, &bgSC)) {
propagatedFrame = propagatedFrame->GetParent();
NS_ASSERTION(aFrame, "root frame must paint");
}
if (propagatedFrame != aFrame) {
DoApplyRenderingChangeToTree(propagatedFrame, nsChangeHint_RepaintFrame);
@@ -437,29 +434,29 @@ RestyleManager::RecomputePosition(nsIFra
}
// Update sticky positioning for an entire element at once, starting with
// the first continuation or ib-split sibling.
// It's rare that the frame we already have isn't already the first
// continuation or ib-split sibling, but it can happen when styles differ
// across continuations such as ::first-line or ::first-letter, and in
// those cases we will generally (but maybe not always) do the work twice.
- nsIFrame *firstContinuation =
+ nsIFrame* firstContinuation =
nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
StickyScrollContainer::ComputeStickyOffsets(firstContinuation);
StickyScrollContainer* ssc =
StickyScrollContainer::GetStickyScrollContainerForFrame(firstContinuation);
if (ssc) {
ssc->PositionContinuations(firstContinuation);
}
} else {
MOZ_ASSERT(NS_STYLE_POSITION_RELATIVE == display->mPosition,
"Unexpected type of positioning");
- for (nsIFrame *cont = aFrame; cont;
+ for (nsIFrame* cont = aFrame; cont;
cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
nsIFrame* cb = cont->GetContainingBlock();
nsMargin newOffsets;
WritingMode wm = cb->GetWritingMode();
const LogicalSize size(wm, cb->GetContentRectRelativeToSelf().Size());
nsHTMLReflowState::ComputeRelativeOffsets(wm, cont, size, newOffsets);
NS_ASSERTION(newOffsets.left == -newOffsets.right &&
@@ -637,24 +634,24 @@ RestyleManager::StyleChangeReflow(nsIFra
nsIPresShell::ReflowRootHandling rootHandling;
if (aHint & nsChangeHint_ReflowChangesSizeOrPosition) {
rootHandling = nsIPresShell::ePositionOrSizeChange;
} else {
rootHandling = nsIPresShell::eNoPositionOrSizeChange;
}
do {
- mPresContext->PresShell()->FrameNeedsReflow(aFrame, dirtyType, dirtyBits,
+ PresContext()->PresShell()->FrameNeedsReflow(aFrame, dirtyType, dirtyBits,
rootHandling);
aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
} while (aFrame);
}
void
-RestyleManager::AddSubtreeToOverflowTracker(nsIFrame* aFrame)
+RestyleManager::AddSubtreeToOverflowTracker(nsIFrame* aFrame)
{
if (aFrame->FrameMaintainsOverflow()) {
mOverflowChangedTracker.AddFrame(aFrame,
OverflowChangedTracker::CHILDREN_CHANGED);
}
nsIFrame::ChildListIterator lists(aFrame);
for (; !lists.IsDone(); lists.Next()) {
for (nsIFrame* child : lists.CurrentList()) {
@@ -740,17 +737,17 @@ RestyleManager::ProcessRestyledFrames(ns
PROFILER_LABEL("RestyleManager", "ProcessRestyledFrames",
js::ProfileEntry::Category::CSS);
// Make sure to not rebuild quote or counter lists while we're
// processing restyles
FrameConstructor()->BeginUpdate();
- FramePropertyTable* propTable = mPresContext->PropertyTable();
+ FramePropertyTable* propTable = PresContext()->PropertyTable();
// Mark frames so that we skip frames that die along the way, bug 123049.
// A frame can be in the list multiple times with different hints. Further
// optmization is possible if nsStyleChangeList::AppendChange could coalesce
int32_t index = count;
while (0 <= --index) {
const nsStyleChangeData* changeData;
@@ -793,17 +790,17 @@ RestyleManager::ProcessRestyledFrames(ns
!(hint & nsChangeHint_ReconstructFrame)) {
if (NeedToReframeForAddingOrRemovingTransform(frame) ||
frame->GetType() == nsGkAtoms::fieldSetFrame ||
frame->GetContentInsertionFrame() != frame) {
// The frame has positioned children that need to be reparented, or
// it can't easily be converted to/from being an abs-pos container correctly.
hint |= nsChangeHint_ReconstructFrame;
} else {
- for (nsIFrame *cont = frame; cont;
+ for (nsIFrame* cont = frame; cont;
cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
// Normally frame construction would set state bits as needed,
// but we're not going to reconstruct the frame so we need to set them.
// It's because we need to set this state on each affected frame
// that we can't coalesce nsChangeHint_UpdateContainingBlock hints up
// to ancestors (i.e. it can't be an inherited change hint).
if (cont->IsAbsPosContaininingBlock()) {
if (cont->StyleDisplay()->HasTransform(cont)) {
@@ -851,17 +848,17 @@ RestyleManager::ProcessRestyledFrames(ns
if (!(frame->GetStateBits() & NS_FRAME_MAY_BE_TRANSFORMED)) {
// Frame can not be transformed, and thus a change in transform will
// have no effect and we should not use the
// nsChangeHint_UpdatePostTransformOverflow hint.
hint &= ~nsChangeHint_UpdatePostTransformOverflow;
}
if (hint & nsChangeHint_UpdateEffects) {
- for (nsIFrame *cont = frame; cont;
+ for (nsIFrame* cont = frame; cont;
cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
nsSVGEffects::UpdateEffects(cont);
}
}
if ((hint & nsChangeHint_InvalidateRenderingObservers) ||
((hint & nsChangeHint_UpdateOpacityLayer) &&
frame->IsFrameOfType(nsIFrame::eSVG) &&
!(frame->GetStateBits() & NS_STATE_IS_OUTER_SVG))) {
@@ -898,17 +895,17 @@ RestyleManager::ProcessRestyledFrames(ns
// these frames. Repaint the whole frame.
hint |= nsChangeHint_RepaintFrame;
}
}
if (hint & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView |
nsChangeHint_UpdateOpacityLayer | nsChangeHint_UpdateTransformLayer |
nsChangeHint_ChildrenOnlyTransform | nsChangeHint_SchedulePaint)) {
- ApplyRenderingChangeToTree(mPresContext, frame, hint);
+ ApplyRenderingChangeToTree(PresContext(), frame, hint);
}
if ((hint & nsChangeHint_RecomputePosition) && !didReflowThisFrame) {
ActiveLayerTracker::NotifyOffsetRestyle(frame);
// It is possible for this to fall back to a reflow
if (!RecomputePosition(frame)) {
didReflowThisFrame = true;
}
}
@@ -916,17 +913,17 @@ RestyleManager::ProcessRestyledFrames(ns
(hint & nsChangeHint_UpdateOverflow),
"nsChangeHint_UpdateOverflow should be passed too");
if (!didReflowThisFrame &&
(hint & (nsChangeHint_UpdateOverflow |
nsChangeHint_UpdatePostTransformOverflow |
nsChangeHint_UpdateParentOverflow |
nsChangeHint_UpdateSubtreeOverflow))) {
if (hint & nsChangeHint_UpdateSubtreeOverflow) {
- for (nsIFrame *cont = frame; cont; cont =
+ for (nsIFrame* cont = frame; cont; cont =
nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
AddSubtreeToOverflowTracker(cont);
}
// The work we just did in AddSubtreeToOverflowTracker
// subsumes some of the other hints:
hint &= ~(nsChangeHint_UpdateOverflow |
nsChangeHint_UpdatePostTransformOverflow);
}
@@ -968,38 +965,38 @@ RestyleManager::ProcessRestyledFrames(ns
// nsChangeHint_UpdatePostTransformOverflow,
// CHILDREN_CHANGED is selected as it is
// strictly stronger.
if (hint & nsChangeHint_UpdateOverflow) {
changeKind = OverflowChangedTracker::CHILDREN_CHANGED;
} else {
changeKind = OverflowChangedTracker::TRANSFORM_CHANGED;
}
- for (nsIFrame *cont = frame; cont; cont =
+ for (nsIFrame* cont = frame; cont; cont =
nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
mOverflowChangedTracker.AddFrame(cont, changeKind);
}
}
// UpdateParentOverflow hints need to be processed in addition
// to the above, since if the processing of the above hints
// yields no change, the update will not propagate to the
// parent.
if (hint & nsChangeHint_UpdateParentOverflow) {
MOZ_ASSERT(frame->GetParent(),
"shouldn't get style hints for the root frame");
- for (nsIFrame *cont = frame; cont; cont =
+ for (nsIFrame* cont = frame; cont; cont =
nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
mOverflowChangedTracker.AddFrame(cont->GetParent(),
OverflowChangedTracker::CHILDREN_CHANGED);
}
}
}
}
if ((hint & nsChangeHint_UpdateCursor) && !didUpdateCursor) {
- mPresContext->PresShell()->SynthesizeMouseMove(false);
+ PresContext()->PresShell()->SynthesizeMouseMove(false);
didUpdateCursor = true;
}
}
}
FrameConstructor()->EndUpdate();
// cleanup references and verify the style tree. Note that the latter needs
@@ -1048,19 +1045,19 @@ RestyleManager::RestyleElement(Element*
// of <area>s. See bug 135040. We can remove this block once that's fixed.
aPrimaryFrame = nullptr;
}
NS_ASSERTION(!aPrimaryFrame || aPrimaryFrame->GetContent() == aElement,
"frame/content mismatch");
// If we're restyling the root element and there are 'rem' units in
// use, handle dynamic changes to the definition of a 'rem' here.
- if (mPresContext->UsesRootEMUnits() && aPrimaryFrame &&
+ if (PresContext()->UsesRootEMUnits() && aPrimaryFrame &&
!mInRebuildAllStyleData) {
- nsStyleContext *oldContext = aPrimaryFrame->StyleContext();
+ nsStyleContext* oldContext = aPrimaryFrame->StyleContext();
if (!oldContext->GetParent()) { // check that we're the root element
RefPtr<nsStyleContext> newContext = StyleSet()->
ResolveStyleFor(aElement, nullptr /* == oldContext->GetParent() */);
if (oldContext->StyleFont()->mFont.size !=
newContext->StyleFont()->mFont.size) {
// The basis for 'rem' units has changed.
mRebuildAllRestyleHint |= aRestyleHint;
if (aRestyleHint & eRestyle_SomeDescendants) {
@@ -1112,17 +1109,17 @@ RestyleManager::ReframingStyleContexts::
RestyleManager::ReframingStyleContexts::~ReframingStyleContexts()
{
// Before we go away, we need to flush out any frame construction that
// was enqueued, so that we start transitions.
// Note that this is a little bit evil in that we're calling into code
// that calls our member functions from our destructor, but it's at
// the beginning of our destructor, so it shouldn't be too bad.
- mRestyleManager->mPresContext->FrameConstructor()->CreateNeededFrames();
+ mRestyleManager->PresContext()->FrameConstructor()->CreateNeededFrames();
}
RestyleManager::AnimationsWithDestroyedFrame::AnimationsWithDestroyedFrame(
RestyleManager* aRestyleManager)
: mRestyleManager(aRestyleManager)
, mRestorePointer(mRestyleManager->mAnimationsWithDestroyedFrame)
{
MOZ_ASSERT(!mRestyleManager->mAnimationsWithDestroyedFrame,
@@ -1181,85 +1178,21 @@ RestyleManager::ContentStateChanged(nsIC
// XXXbz it would be good if this function only took Elements, but
// we'd have to make ESM guarantee that usefully.
if (!aContent->IsElement()) {
return NS_OK;
}
Element* aElement = aContent->AsElement();
- nsStyleSet* styleSet = StyleSet();
- NS_ASSERTION(styleSet, "couldn't get style set");
-
- nsChangeHint hint = NS_STYLE_HINT_NONE;
- // Any change to a content state that affects which frames we construct
- // must lead to a frame reconstruct here if we already have a frame.
- // Note that we never decide through non-CSS means to not create frames
- // based on content states, so if we already don't have a frame we don't
- // need to force a reframe -- if it's needed, the HasStateDependentStyle
- // call will handle things.
- nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
- CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo;
- if (primaryFrame) {
- // If it's generated content, ignore LOADING/etc state changes on it.
- if (!primaryFrame->IsGeneratedContentFrame() &&
- aStateMask.HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
- NS_EVENT_STATE_USERDISABLED |
- NS_EVENT_STATE_SUPPRESSED |
- NS_EVENT_STATE_LOADING)) {
- hint = nsChangeHint_ReconstructFrame;
- } else {
- uint8_t app = primaryFrame->StyleDisplay()->mAppearance;
- if (app) {
- nsITheme *theme = mPresContext->GetTheme();
- if (theme && theme->ThemeSupportsWidget(mPresContext,
- primaryFrame, app)) {
- bool repaint = false;
- theme->WidgetStateChanged(primaryFrame, app, nullptr, &repaint, nullptr);
- if (repaint) {
- hint |= nsChangeHint_RepaintFrame;
- }
- }
- }
- }
-
- pseudoType = primaryFrame->StyleContext()->GetPseudoType();
-
- primaryFrame->ContentStatesChanged(aStateMask);
- }
-
-
- nsRestyleHint rshint;
-
- if (pseudoType >= CSSPseudoElementType::Count) {
- rshint = styleSet->HasStateDependentStyle(aElement, aStateMask);
- } else if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(
- pseudoType)) {
- // If aElement is a pseudo-element, we want to check to see whether there
- // are any state-dependent rules applying to that pseudo.
- Element* ancestor = ElementForStyleContext(nullptr, primaryFrame,
- pseudoType);
- rshint = styleSet->HasStateDependentStyle(ancestor, pseudoType, aElement,
- aStateMask);
- } else {
- rshint = nsRestyleHint(0);
- }
-
- if (aStateMask.HasState(NS_EVENT_STATE_HOVER) && rshint != 0) {
- ++mHoverGeneration;
- }
-
- if (aStateMask.HasState(NS_EVENT_STATE_VISITED)) {
- // Exposing information to the page about whether the link is
- // visited or not isn't really something we can worry about here.
- // FIXME: We could probably do this a bit better.
- hint |= nsChangeHint_RepaintFrame;
- }
-
- PostRestyleEvent(aElement, rshint, hint);
+ nsChangeHint changeHint;
+ nsRestyleHint restyleHint;
+ ContentStateChangedInternal(aElement, aStateMask, &changeHint, &restyleHint);
+
+ PostRestyleEvent(aElement, restyleHint, changeHint);
return NS_OK;
}
// Forwarded nsIMutationObserver method, to handle restyling.
void
RestyleManager::AttributeWillChange(Element* aElement,
int32_t aNameSpaceID,
nsIAtom* aAttribute,
@@ -1285,17 +1218,17 @@ RestyleManager::AttributeChanged(Element
int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType,
const nsAttrValue* aOldValue)
{
// Hold onto the PresShell to prevent ourselves from being destroyed.
// XXXbz how, exactly, would this attribute change cause us to be
// destroyed from inside this function?
- nsCOMPtr<nsIPresShell> shell = mPresContext->GetPresShell();
+ nsCOMPtr<nsIPresShell> shell = PresContext()->GetPresShell();
// Get the frame associated with the content which is the highest in the frame tree
nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
#if 0
NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
("RestyleManager::AttributeChanged: content=%p[%s] frame=%p",
aContent, ContentTag(aElement, 0), frame));
@@ -1307,45 +1240,45 @@ RestyleManager::AttributeChanged(Element
bool reframe = (hint & nsChangeHint_ReconstructFrame) != 0;
#ifdef MOZ_XUL
// The following listbox widget trap prevents offscreen listbox widget
// content from being removed and re-inserted (which is what would
// happen otherwise).
if (!primaryFrame && !reframe) {
int32_t namespaceID;
- nsIAtom* tag = mPresContext->Document()->BindingManager()->
+ nsIAtom* tag = PresContext()->Document()->BindingManager()->
ResolveTag(aElement, &namespaceID);
if (namespaceID == kNameSpaceID_XUL &&
(tag == nsGkAtoms::listitem ||
tag == nsGkAtoms::listcell))
return;
}
if (aAttribute == nsGkAtoms::tooltiptext ||
aAttribute == nsGkAtoms::tooltip)
{
- nsIRootBox* rootBox = nsIRootBox::GetRootBox(mPresContext->GetPresShell());
+ nsIRootBox* rootBox = nsIRootBox::GetRootBox(PresContext()->GetPresShell());
if (rootBox) {
if (aModType == nsIDOMMutationEvent::REMOVAL)
rootBox->RemoveTooltipSupport(aElement);
if (aModType == nsIDOMMutationEvent::ADDITION)
rootBox->AddTooltipSupport(aElement);
}
}
#endif // MOZ_XUL
if (primaryFrame) {
// See if we have appearance information for a theme.
const nsStyleDisplay* disp = primaryFrame->StyleDisplay();
if (disp->mAppearance) {
- nsITheme *theme = mPresContext->GetTheme();
- if (theme && theme->ThemeSupportsWidget(mPresContext, primaryFrame, disp->mAppearance)) {
+ nsITheme* theme = PresContext()->GetTheme();
+ if (theme && theme->ThemeSupportsWidget(PresContext(), primaryFrame, disp->mAppearance)) {
bool repaint = false;
theme->WidgetStateChanged(primaryFrame, disp->mAppearance, aAttribute,
&repaint, aOldValue);
if (repaint)
hint |= nsChangeHint_RepaintFrame;
}
}
@@ -1453,17 +1386,17 @@ RestyleManager::RestyleForAppend(Element
// Needed since we can't use PostRestyleEvent on non-elements (with
// eRestyle_LaterSiblings or nsRestyleHint(eRestyle_Subtree |
// eRestyle_LaterSiblings) as appropriate).
static void
RestyleSiblingsStartingWith(RestyleManager* aRestyleManager,
nsIContent* aStartingSibling /* may be null */)
{
- for (nsIContent *sibling = aStartingSibling; sibling;
+ for (nsIContent* sibling = aStartingSibling; sibling;
sibling = sibling->GetNextSibling()) {
if (sibling->IsElement()) {
aRestyleManager->
PostRestyleEvent(sibling->AsElement(),
nsRestyleHint(eRestyle_Subtree | eRestyle_LaterSiblings),
NS_STYLE_HINT_NONE);
break;
}
@@ -1656,17 +1589,17 @@ RestyleManager::RebuildAllStyleData(nsCh
"eRestyle_ForceDescendants");
mRebuildAllExtraHint |= aExtraHint;
mRebuildAllRestyleHint |= aRestyleHint;
// Processing the style changes could cause a flush that propagates to
// the parent frame and thus destroys the pres shell, so we must hold
// a reference.
- nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
+ nsCOMPtr<nsIPresShell> presShell = PresContext()->GetPresShell();
if (!presShell || !presShell->GetRootFrame()) {
mDoRebuildAllStyleData = false;
return;
}
// Make sure that the viewmanager will outlive the presshell
RefPtr<nsViewManager> vm = presShell->GetViewManager();
@@ -1681,17 +1614,17 @@ RestyleManager::RebuildAllStyleData(nsCh
ProcessPendingRestyles();
}
void
RestyleManager::StartRebuildAllStyleData(RestyleTracker& aRestyleTracker)
{
MOZ_ASSERT(mIsProcessingRestyles);
- nsIFrame* rootFrame = mPresContext->PresShell()->GetRootFrame();
+ nsIFrame* rootFrame = PresContext()->PresShell()->GetRootFrame();
if (!rootFrame) {
// No need to do anything.
return;
}
mInRebuildAllStyleData = true;
// Tell the style set to get the old rule tree out of the way
@@ -1712,17 +1645,17 @@ RestyleManager::StartRebuildAllStyleData
(restyleHint & ~(eRestyle_Force | eRestyle_ForceDescendants))) {
// We want this hint to apply to the root node's primary frame
// rather than the root frame, since it's the primary frame that has
// the styles for the root element (rather than the ancestors of the
// primary frame whose mContent is the root node but which have
// different styles). If we use up the hint for one of the
// ancestors that we hit first, then we'll fail to do the restyling
// we need to do.
- Element* root = mPresContext->Document()->GetRootElement();
+ Element* root = PresContext()->Document()->GetRootElement();
if (root) {
// If the root element is gone, dropping the hint on the floor
// should be fine.
aRestyleTracker.AddPendingRestyle(root, restyleHint, nsChangeHint(0));
}
restyleHint = nsRestyleHint(0);
}
@@ -1752,23 +1685,23 @@ RestyleManager::FinishRebuildAllStyleDat
StyleSet()->EndReconstruct();
mInRebuildAllStyleData = false;
}
void
RestyleManager::ProcessPendingRestyles()
{
- NS_PRECONDITION(mPresContext->Document(), "No document? Pshaw!");
+ NS_PRECONDITION(PresContext()->Document(), "No document? Pshaw!");
NS_PRECONDITION(!nsContentUtils::IsSafeToRunScript(),
"Missing a script blocker!");
// First do any queued-up frame creation. (We should really
// merge this into the rest of the process, though; see bug 827239.)
- mPresContext->FrameConstructor()->CreateNeededFrames();
+ PresContext()->FrameConstructor()->CreateNeededFrames();
// Process non-animation restyles...
MOZ_ASSERT(!mIsProcessingRestyles,
"Nesting calls to ProcessPendingRestyles?");
mIsProcessingRestyles = true;
// Before we process any restyles, we need to ensure that style
// resulting from any animations is up-to-date, so that if any style
@@ -1796,23 +1729,23 @@ RestyleManager::ProcessPendingRestyles()
// want to tell the transition manager to act as though we're in
// UpdateOnlyAnimationStyles.
//
// FIXME: In the future, we might want to refactor the way the
// animation and transition manager do their refresh driver ticks so
// that we can use UpdateOnlyAnimationStyles, with a different
// boolean argument, for this update as well, instead of having them
// post style updates in their WillRefresh methods.
- mPresContext->TransitionManager()->SetInAnimationOnlyStyleUpdate(true);
+ PresContext()->TransitionManager()->SetInAnimationOnlyStyleUpdate(true);
}
ProcessRestyles(mPendingRestyles);
if (!haveNonAnimation) {
- mPresContext->TransitionManager()->SetInAnimationOnlyStyleUpdate(false);
+ PresContext()->TransitionManager()->SetInAnimationOnlyStyleUpdate(false);
}
mIsProcessingRestyles = false;
NS_ASSERTION(haveNonAnimation || !mHavePendingNonAnimationRestyles,
"should not have added restyles");
mHavePendingNonAnimationRestyles = false;
@@ -1830,17 +1763,17 @@ RestyleManager::ProcessPendingRestyles()
"should have called FinishRebuildAllStyleData");
}
void
RestyleManager::BeginProcessingRestyles(RestyleTracker& aRestyleTracker)
{
// Make sure to not rebuild quote or counter lists while we're
// processing restyles
- mPresContext->FrameConstructor()->BeginUpdate();
+ PresContext()->FrameConstructor()->BeginUpdate();
mInStyleRefresh = true;
if (ShouldStartRebuildAllFor(aRestyleTracker)) {
mDoRebuildAllStyleData = false;
StartRebuildAllStyleData(aRestyleTracker);
}
}
@@ -1857,54 +1790,54 @@ RestyleManager::EndProcessingRestyles()
// Set mInStyleRefresh to false now, since the EndUpdate call might
// add more restyles.
mInStyleRefresh = false;
if (mInRebuildAllStyleData) {
FinishRebuildAllStyleData();
}
- mPresContext->FrameConstructor()->EndUpdate();
+ PresContext()->FrameConstructor()->EndUpdate();
#ifdef DEBUG
- mPresContext->PresShell()->VerifyStyleTree();
+ PresContext()->PresShell()->VerifyStyleTree();
#endif
}
void
RestyleManager::UpdateOnlyAnimationStyles()
{
- bool doCSS = mPresContext->EffectCompositor()->HasPendingStyleUpdates();
-
- nsIDocument* document = mPresContext->Document();
+ bool doCSS = PresContext()->EffectCompositor()->HasPendingStyleUpdates();
+
+ nsIDocument* document = PresContext()->Document();
nsSMILAnimationController* animationController =
document->HasAnimationController() ?
document->GetAnimationController() :
nullptr;
bool doSMIL = animationController &&
animationController->MightHavePendingStyleUpdates();
if (!doCSS && !doSMIL) {
return;
}
- nsTransitionManager* transitionManager = mPresContext->TransitionManager();
+ nsTransitionManager* transitionManager = PresContext()->TransitionManager();
transitionManager->SetInAnimationOnlyStyleUpdate(true);
RestyleTracker tracker(ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE |
ELEMENT_IS_POTENTIAL_ANIMATION_ONLY_RESTYLE_ROOT);
tracker.Init(this);
if (doCSS) {
// FIXME: We should have the transition manager and animation manager
// add only the elements for which animations are currently throttled
// (i.e., animating on the compositor with main-thread style updates
// suppressed).
- mPresContext->EffectCompositor()->AddStyleUpdatesTo(tracker);
+ PresContext()->EffectCompositor()->AddStyleUpdatesTo(tracker);
}
if (doSMIL) {
animationController->AddStyleUpdatesTo(tracker);
}
ProcessRestyles(tracker);
@@ -1912,18 +1845,18 @@ RestyleManager::UpdateOnlyAnimationStyle
}
void
RestyleManager::PostRestyleEvent(Element* aElement,
nsRestyleHint aRestyleHint,
nsChangeHint aMinChangeHint,
const RestyleHintData* aRestyleHintData)
{
- if (MOZ_UNLIKELY(!mPresContext) ||
- MOZ_UNLIKELY(mPresContext->PresShell()->IsDestroying())) {
+ if (MOZ_UNLIKELY(IsDisconnected()) ||
+ MOZ_UNLIKELY(PresContext()->PresShell()->IsDestroying())) {
return;
}
if (aRestyleHint == 0 && !aMinChangeHint) {
// Nothing to do here
return;
}
@@ -1943,20 +1876,20 @@ RestyleManager::PostRestyleEvent(Element
void
RestyleManager::PostRestyleEventInternal(bool aForLazyConstruction)
{
// Make sure we're not in a style refresh; if we are, we still have
// a call to ProcessPendingRestyles coming and there's no need to
// add ourselves as a refresh observer until then.
bool inRefresh = !aForLazyConstruction && mInStyleRefresh;
- nsIPresShell* presShell = mPresContext->PresShell();
- if (!mObservingRefreshDriver && !inRefresh) {
- mObservingRefreshDriver = mPresContext->RefreshDriver()->
- AddStyleFlushObserver(presShell);
+ nsIPresShell* presShell = PresContext()->PresShell();
+ if (!ObservingRefreshDriver() && !inRefresh) {
+ SetObservingRefreshDriver(PresContext()->RefreshDriver()->
+ AddStyleFlushObserver(presShell));
}
// Unconditionally flag our document as needing a flush. The other
// option here would be a dedicated boolean to track whether we need
// to do so (set here and unset in ProcessPendingRestyles).
presShell->GetDocument()->SetNeedStyleFlush();
}
@@ -2410,33 +2343,33 @@ RestyleManager::ReparentStyleContext(nsI
// Check that our assumption that continuations of the same
// pseudo-type and with the same style context parent have the
// same style context is valid before the reresolution. (We need
// to check the pseudo-type and style context parent because of
// :first-letter and :first-line, where we create styled and
// unstyled letter/line frames distinguished by pseudo-type, and
// then need to distinguish their descendants based on having
// different parents.)
- nsIFrame *nextContinuation = aFrame->GetNextContinuation();
+ nsIFrame* nextContinuation = aFrame->GetNextContinuation();
if (nextContinuation) {
- nsStyleContext *nextContinuationContext =
+ nsStyleContext* nextContinuationContext =
nextContinuation->StyleContext();
NS_ASSERTION(oldContext == nextContinuationContext ||
oldContext->GetPseudo() !=
nextContinuationContext->GetPseudo() ||
oldContext->GetParent() !=
nextContinuationContext->GetParent(),
"continuations should have the same style context");
}
}
#endif
- nsIFrame *prevContinuation =
+ nsIFrame* prevContinuation =
GetPrevContinuationWithPossiblySameStyle(aFrame);
- nsStyleContext *prevContinuationContext;
+ nsStyleContext* prevContinuationContext;
bool copyFromContinuation =
prevContinuation &&
(prevContinuationContext = prevContinuation->StyleContext())
->GetPseudo() == oldContext->GetPseudo() &&
prevContinuationContext->GetParent() == newParentContext;
if (copyFromContinuation) {
// Just use the style context from the frame's previous
// continuation (see assertion about aFrame->GetNextContinuation()
@@ -2600,16 +2533,17 @@ ElementRestyler::ElementRestyler(nsPresC
, mKidsDesiredA11yNotifications(mDesiredA11yNotifications)
, mOurA11yNotification(eDontNotify)
, mVisibleKidsOfHiddenElement(aVisibleKidsOfHiddenElement)
#endif
#ifdef RESTYLE_LOGGING
, mLoggingDepth(aRestyleTracker.LoggingDepth() + 1)
#endif
{
+ MOZ_ASSERT_IF(mContent, !mContent->IsStyledByServo());
}
ElementRestyler::ElementRestyler(const ElementRestyler& aParentRestyler,
nsIFrame* aFrame,
uint32_t aConstructorFlags)
: mPresContext(aParentRestyler.mPresContext)
, mFrame(aFrame)
, mParentContent(aParentRestyler.mContent)
@@ -2634,16 +2568,17 @@ ElementRestyler::ElementRestyler(const E
, mKidsDesiredA11yNotifications(mDesiredA11yNotifications)
, mOurA11yNotification(eDontNotify)
, mVisibleKidsOfHiddenElement(aParentRestyler.mVisibleKidsOfHiddenElement)
#endif
#ifdef RESTYLE_LOGGING
, mLoggingDepth(aParentRestyler.mLoggingDepth + 1)
#endif
{
+ MOZ_ASSERT_IF(mContent, !mContent->IsStyledByServo());
if (aConstructorFlags & FOR_OUT_OF_FLOW_CHILD) {
// Note that the out-of-flow may not be a geometric descendant of
// the frame where we started the reresolve. Therefore, even if
// mHintsHandled already includes nsChangeHint_AllReflowHints we
// don't want to pass that on to the out-of-flow reresolve, since
// that can lead to the out-of-flow not getting reflowed when it
// should be (eg a reresolve starting at <body> that involves
// reflowing the <body> would miss reflowing fixed-pos nodes that
@@ -2682,16 +2617,17 @@ ElementRestyler::ElementRestyler(ParentC
, mKidsDesiredA11yNotifications(mDesiredA11yNotifications)
, mOurA11yNotification(eDontNotify)
, mVisibleKidsOfHiddenElement(aParentRestyler.mVisibleKidsOfHiddenElement)
#endif
#ifdef RESTYLE_LOGGING
, mLoggingDepth(aParentRestyler.mLoggingDepth + 1)
#endif
{
+ MOZ_ASSERT_IF(mContent, !mContent->IsStyledByServo());
}
ElementRestyler::ElementRestyler(nsPresContext* aPresContext,
nsIContent* aContent,
nsStyleChangeList* aChangeList,
nsChangeHint aHintsHandledByAncestors,
RestyleTracker& aRestyleTracker,
nsTArray<nsCSSSelector*>& aSelectorsForDescendants,
@@ -2899,29 +2835,31 @@ ElementRestyler::ConditionallyRestyleChi
}
void
ElementRestyler::ConditionallyRestyleChildren(nsIFrame* aFrame,
Element* aRestyleRoot)
{
MOZ_ASSERT(aFrame->GetContent());
MOZ_ASSERT(aFrame->GetContent()->IsElement());
+ MOZ_ASSERT(!aFrame->GetContent()->IsStyledByServo());
ConditionallyRestyleUndisplayedDescendants(aFrame, aRestyleRoot);
ConditionallyRestyleContentChildren(aFrame, aRestyleRoot);
}
// The structure of this method parallels RestyleContentChildren.
// If you update this method, you probably want to update that one too.
void
ElementRestyler::ConditionallyRestyleContentChildren(nsIFrame* aFrame,
Element* aRestyleRoot)
{
MOZ_ASSERT(aFrame->GetContent());
MOZ_ASSERT(aFrame->GetContent()->IsElement());
+ MOZ_ASSERT(!aFrame->GetContent()->IsStyledByServo());
if (aFrame->GetContent()->HasFlag(mRestyleTracker.RootBit())) {
aRestyleRoot = aFrame->GetContent()->AsElement();
}
for (nsIFrame* f = aFrame; f;
f = GetNextContinuationWithSameStyle(f, f->StyleContext())) {
nsIFrame::ChildListIterator lists(f);
@@ -2996,24 +2934,24 @@ void
ElementRestyler::ConditionallyRestyleUndisplayedNodes(
UndisplayedNode* aUndisplayed,
nsIContent* aUndisplayedParent,
const uint8_t aDisplay,
Element* aRestyleRoot)
{
MOZ_ASSERT(aDisplay == NS_STYLE_DISPLAY_NONE ||
aDisplay == NS_STYLE_DISPLAY_CONTENTS);
-
if (!aUndisplayed) {
return;
}
if (aUndisplayedParent &&
aUndisplayedParent->IsElement() &&
aUndisplayedParent->HasFlag(mRestyleTracker.RootBit())) {
+ MOZ_ASSERT(!aUndisplayedParent->IsStyledByServo());
aRestyleRoot = aUndisplayedParent->AsElement();
}
for (UndisplayedNode* undisplayed = aUndisplayed; undisplayed;
undisplayed = undisplayed->mNext) {
if (!undisplayed->mContent->IsElement()) {
continue;
@@ -3030,16 +2968,17 @@ ElementRestyler::ConditionallyRestyleUnd
}
}
}
void
ElementRestyler::ConditionallyRestyleContentDescendants(Element* aElement,
Element* aRestyleRoot)
{
+ MOZ_ASSERT(!aElement->IsStyledByServo());
if (aElement->HasFlag(mRestyleTracker.RootBit())) {
aRestyleRoot = aElement;
}
FlattenedChildIterator it(aElement);
for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
if (n->IsElement()) {
Element* e = n->AsElement();
@@ -3060,16 +2999,17 @@ ElementRestyler::ConditionallyRestyle(ns
}
return ConditionallyRestyle(aFrame->GetContent()->AsElement(), aRestyleRoot);
}
bool
ElementRestyler::ConditionallyRestyle(Element* aElement, Element* aRestyleRoot)
{
+ MOZ_ASSERT(!aElement->IsStyledByServo());
LOG_RESTYLE("considering element %s for eRestyle_SomeDescendants",
ElementTagToString(aElement).get());
LOG_RESTYLE_INDENT();
if (aElement->HasFlag(mRestyleTracker.RootBit())) {
aRestyleRoot = aElement;
}
@@ -3961,19 +3901,19 @@ ElementRestyler::RestyleSelf(nsIFrame* a
mParentFrameHintsNotHandledForDescendants =
nsChangeHint_Hints_NotHandledForDescendants;
}
LOG_RESTYLE("parentContext = %p", parentContext);
// do primary context
RefPtr<nsStyleContext> newContext;
- nsIFrame *prevContinuation =
+ nsIFrame* prevContinuation =
GetPrevContinuationWithPossiblySameStyle(aSelf);
- nsStyleContext *prevContinuationContext;
+ nsStyleContext* prevContinuationContext;
bool copyFromContinuation =
prevContinuation &&
(prevContinuationContext = prevContinuation->StyleContext())
->GetPseudo() == oldContext->GetPseudo() &&
prevContinuationContext->GetParent() == parentContext;
if (copyFromContinuation) {
// Just use the style context from the frame's previous
// continuation.
@@ -4428,17 +4368,17 @@ ElementRestyler::RestyleChildren(nsResty
// on a frame change. The act of reconstructing frames will force
// new style contexts to be resolved on all of this frame's
// descendants anyway, so we want to avoid wasting time processing
// style contexts that we're just going to throw away anyway. - dwh
// It's also important to check mHintsHandled since reresolving the
// kids would use mFrame->StyleContext(), which is out of date if
// mHintsHandled has a ReconstructFrame hint; doing this could trigger
// assertions about mismatched rule trees.
- nsIFrame *lastContinuation;
+ nsIFrame* lastContinuation;
if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
InitializeAccessibilityNotifications(mFrame->StyleContext());
for (nsIFrame* f = mFrame; f;
f = GetNextContinuationWithSameStyle(f, f->StyleContext())) {
lastContinuation = f;
RestyleContentChildren(f, aChildRestyleHint);
}
@@ -5037,25 +4977,16 @@ RestyleManager::ComputeAndProcessStyleCh
r.RestyleChildrenOfDisplayContentsElement(frame, aNewContext, aMinChange,
aRestyleTracker,
aRestyleHint, aRestyleHintData);
ProcessRestyledFrames(changeList);
ClearCachedInheritedStyleDataOnDescendants(contextsToClear);
}
nsStyleSet*
-RestyleManager::StyleSet() const
-{
- MOZ_ASSERT(mPresContext->StyleSet()->IsGecko(),
- "RestyleManager should only be used with a Gecko-flavored "
- "style backend");
- return mPresContext->StyleSet()->AsGecko();
-}
-
-nsStyleSet*
ElementRestyler::StyleSet() const
{
MOZ_ASSERT(mPresContext->StyleSet()->IsGecko(),
"ElementRestyler should only be used with a Gecko-flavored "
"style backend");
return mPresContext->StyleSet()->AsGecko();
}
--- a/layout/base/RestyleManager.h
+++ b/layout/base/RestyleManager.h
@@ -7,16 +7,17 @@
* Code responsible for managing style changes: tracking what style
* changes need to happen, scheduling them, and doing them.
*/
#ifndef mozilla_RestyleManager_h
#define mozilla_RestyleManager_h
#include "mozilla/RestyleLogging.h"
+#include "mozilla/RestyleManagerBase.h"
#include "nsISupportsImpl.h"
#include "nsChangeHint.h"
#include "RestyleTracker.h"
#include "nsPresContext.h"
#include "nsRefreshDriver.h"
#include "nsRefPtrHashtable.h"
#include "nsTransitionManager.h"
@@ -28,48 +29,36 @@ namespace mozilla {
enum class CSSPseudoElementType : uint8_t;
class EventStates;
struct UndisplayedNode;
namespace dom {
class Element;
} // namespace dom
-class RestyleManager final
+class RestyleManager final : public RestyleManagerBase
{
public:
- friend class ::nsRefreshDriver;
friend class RestyleTracker;
- typedef mozilla::dom::Element Element;
-
explicit RestyleManager(nsPresContext* aPresContext);
private:
// Private destructor, to discourage deletion outside of Release():
~RestyleManager()
{
MOZ_ASSERT(!mReframingStyleContexts,
"temporary member should be nulled out before destruction");
MOZ_ASSERT(!mAnimationsWithDestroyedFrame,
"leaving dangling pointers from AnimationsWithDestroyedFrame");
}
public:
NS_INLINE_DECL_REFCOUNTING(mozilla::RestyleManager)
- void Disconnect() {
- mPresContext = nullptr;
- }
-
- nsPresContext* PresContext() const {
- MOZ_ASSERT(mPresContext);
- return mPresContext;
- }
-
// Should be called when a frame is going to be destroyed and
// WillDestroyFrameTree hasn't been called yet.
void NotifyDestroyingFrame(nsIFrame* aFrame);
// Forwarded nsIDocumentObserver method, to handle restyling (and
// passing the notification to the frame).
nsresult ContentStateChanged(nsIContent* aContent,
EventStates aStateMask);
@@ -83,24 +72,16 @@ public:
// Forwarded nsIMutationObserver method, to handle restyling (and
// passing the notification to the frame).
void AttributeChanged(Element* aElement,
int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType,
const nsAttrValue* aOldValue);
- // Get an integer that increments every time we process pending restyles.
- // The value is never 0.
- uint32_t GetRestyleGeneration() const { return mRestyleGeneration; }
-
- // Get an integer that increments every time there is a style change
- // as a result of a change to the :hover content state.
- uint32_t GetHoverGeneration() const { return mHoverGeneration; }
-
// Get a counter that increments on every style change, that we use to
// track whether off-main-thread animations are up-to-date.
uint64_t GetAnimationGeneration() const { return mAnimationGeneration; }
static uint64_t GetAnimationGenerationForFrame(nsIFrame* aFrame);
// Update the animation generation count to mark that animation state
// has changed.
@@ -134,25 +115,23 @@ public:
*/
nsresult ReparentStyleContext(nsIFrame* aFrame);
void ClearSelectors() {
mPendingRestyles.ClearSelectors();
}
private:
- nsCSSFrameConstructor* FrameConstructor() const
- { return PresContext()->FrameConstructor(); }
-
// Used when restyling an element with a frame.
void ComputeAndProcessStyleChange(nsIFrame* aFrame,
nsChangeHint aMinChange,
RestyleTracker& aRestyleTracker,
nsRestyleHint aRestyleHint,
const RestyleHintData& aRestyleHintData);
+
// Used when restyling a display:contents element.
void ComputeAndProcessStyleChange(nsStyleContext* aNewContext,
Element* aElement,
nsChangeHint aMinChange,
RestyleTracker& aRestyleTracker,
nsRestyleHint aRestyleHint,
const RestyleHintData& aRestyleHintData);
@@ -451,17 +430,17 @@ public:
#endif
#ifdef RESTYLE_LOGGING
/**
* Returns whether a restyle event currently being processed by this
* RestyleManager should be logged.
*/
bool ShouldLogRestyle() {
- return ShouldLogRestyle(mPresContext);
+ return ShouldLogRestyle(PresContext());
}
/**
* Returns whether a restyle event currently being processed for the
* document with the specified nsPresContext should be logged.
*/
static bool ShouldLogRestyle(nsPresContext* aPresContext) {
return aPresContext->RestyleLoggingEnabled() &&
@@ -487,17 +466,22 @@ public:
// environment variable.
static uint32_t StructsToLog();
static nsCString StructNamesToString(uint32_t aSIDs);
int32_t& LoggingDepth() { return mLoggingDepth; }
#endif
private:
- inline nsStyleSet* StyleSet() const;
+ inline nsStyleSet* StyleSet() const {
+ MOZ_ASSERT(PresContext()->StyleSet()->IsGecko(),
+ "RestyleManager should only be used with a Gecko-flavored "
+ "style backend");
+ return PresContext()->StyleSet()->AsGecko();
+ }
/* aMinHint is the minimal change that should be made to the element */
// XXXbz do we really need the aPrimaryFrame argument here?
void RestyleElement(Element* aElement,
nsIFrame* aPrimaryFrame,
nsChangeHint aMinHint,
RestyleTracker& aRestyleTracker,
nsRestyleHint aRestyleHint,
@@ -522,44 +506,33 @@ private:
return mDoRebuildAllStyleData &&
&aRestyleTracker == &mPendingRestyles;
}
void ProcessRestyles(RestyleTracker& aRestyleTracker) {
// Fast-path the common case (esp. for the animation restyle
// tracker) of not having anything to do.
if (aRestyleTracker.Count() || ShouldStartRebuildAllFor(aRestyleTracker)) {
- if (++mRestyleGeneration == 0) {
- // Keep mRestyleGeneration from being 0, since that's what
- // nsPresContext::GetRestyleGeneration returns when it no
- // longer has a RestyleManager.
- ++mRestyleGeneration;
- }
+ IncrementRestyleGeneration();
aRestyleTracker.DoProcessRestyles();
}
}
private:
- nsPresContext* mPresContext; // weak, disconnected in Disconnect
-
// True if we need to reconstruct the rule tree the next time we
// process restyles.
bool mDoRebuildAllStyleData : 1;
// True if we're currently in the process of reconstructing the rule tree.
bool mInRebuildAllStyleData : 1;
- // True if we're already waiting for a refresh notification
- bool mObservingRefreshDriver : 1;
// True if we're in the middle of a nsRefreshDriver refresh
bool mInStyleRefresh : 1;
// Whether rule matching should skip styles associated with animation
bool mSkipAnimationRules : 1;
bool mHavePendingNonAnimationRestyles : 1;
- uint32_t mRestyleGeneration;
- uint32_t mHoverGeneration;
nsChangeHint mRebuildAllExtraHint;
nsRestyleHint mRebuildAllRestyleHint;
OverflowChangedTracker mOverflowChangedTracker;
// The total number of animation flushes by this frame constructor.
// Used to keep the layer and animation manager in sync.
uint64_t mAnimationGeneration;
new file mode 100644
--- /dev/null
+++ b/layout/base/RestyleManagerBase.cpp
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "mozilla/RestyleManagerBase.h"
+#include "mozilla/StyleSetHandle.h"
+#include "nsIFrame.h"
+
+namespace mozilla {
+
+RestyleManagerBase::RestyleManagerBase(nsPresContext* aPresContext)
+ : mPresContext(aPresContext)
+ , mRestyleGeneration(1)
+ , mHoverGeneration(0)
+ , mObservingRefreshDriver(false)
+{
+ MOZ_ASSERT(mPresContext);
+}
+
+/**
+ * Calculates the change hint and the restyle hint for a given content state
+ * change.
+ *
+ * This is called from both Restyle managers.
+ */
+void
+RestyleManagerBase::ContentStateChangedInternal(Element* aElement,
+ EventStates aStateMask,
+ nsChangeHint* aOutChangeHint,
+ nsRestyleHint* aOutRestyleHint)
+{
+ MOZ_ASSERT(aOutChangeHint);
+ MOZ_ASSERT(aOutRestyleHint);
+
+ StyleSetHandle styleSet = PresContext()->StyleSet();
+ NS_ASSERTION(styleSet, "couldn't get style set");
+
+ *aOutChangeHint = NS_STYLE_HINT_NONE;
+ // Any change to a content state that affects which frames we construct
+ // must lead to a frame reconstruct here if we already have a frame.
+ // Note that we never decide through non-CSS means to not create frames
+ // based on content states, so if we already don't have a frame we don't
+ // need to force a reframe -- if it's needed, the HasStateDependentStyle
+ // call will handle things.
+ nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
+ CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo;
+ if (primaryFrame) {
+ // If it's generated content, ignore LOADING/etc state changes on it.
+ if (!primaryFrame->IsGeneratedContentFrame() &&
+ aStateMask.HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
+ NS_EVENT_STATE_USERDISABLED |
+ NS_EVENT_STATE_SUPPRESSED |
+ NS_EVENT_STATE_LOADING)) {
+ *aOutChangeHint = nsChangeHint_ReconstructFrame;
+ } else {
+ uint8_t app = primaryFrame->StyleDisplay()->mAppearance;
+ if (app) {
+ nsITheme *theme = PresContext()->GetTheme();
+ if (theme && theme->ThemeSupportsWidget(PresContext(),
+ primaryFrame, app)) {
+ bool repaint = false;
+ theme->WidgetStateChanged(primaryFrame, app, nullptr, &repaint, nullptr);
+ if (repaint) {
+ *aOutChangeHint |= nsChangeHint_RepaintFrame;
+ }
+ }
+ }
+ }
+
+ pseudoType = primaryFrame->StyleContext()->GetPseudoType();
+
+ primaryFrame->ContentStatesChanged(aStateMask);
+ }
+
+ if (pseudoType >= CSSPseudoElementType::Count) {
+ *aOutRestyleHint = styleSet->HasStateDependentStyle(aElement, aStateMask);
+ } else if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(
+ pseudoType)) {
+ // If aElement is a pseudo-element, we want to check to see whether there
+ // are any state-dependent rules applying to that pseudo.
+ Element* ancestor = ElementForStyleContext(nullptr, primaryFrame,
+ pseudoType);
+ *aOutRestyleHint = styleSet->HasStateDependentStyle(ancestor, pseudoType, aElement,
+ aStateMask);
+ } else {
+ *aOutRestyleHint = nsRestyleHint(0);
+ }
+
+ if (aStateMask.HasState(NS_EVENT_STATE_HOVER) && *aOutRestyleHint != 0) {
+ IncrementHoverGeneration();
+ }
+
+ if (aStateMask.HasState(NS_EVENT_STATE_VISITED)) {
+ // Exposing information to the page about whether the link is
+ // visited or not isn't really something we can worry about here.
+ // FIXME: We could probably do this a bit better.
+ *aOutChangeHint |= nsChangeHint_RepaintFrame;
+ }
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/layout/base/RestyleManagerBase.h
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_RestyleManagerBase_h
+#define mozilla_RestyleManagerBase_h
+
+#include "nsChangeHint.h"
+
+namespace mozilla {
+
+class ServoRestyleManager;
+class RestyleManager;
+
+/**
+ * Class for sharing data and logic common to both RestyleManager and
+ * ServoRestyleManager.
+ */
+class RestyleManagerBase
+{
+protected:
+ explicit RestyleManagerBase(nsPresContext* aPresContext);
+
+public:
+ typedef mozilla::dom::Element Element;
+
+ // Get an integer that increments every time we process pending restyles.
+ // The value is never 0.
+ uint32_t GetRestyleGeneration() const { return mRestyleGeneration; }
+
+ // Get an integer that increments every time there is a style change
+ // as a result of a change to the :hover content state.
+ uint32_t GetHoverGeneration() const { return mHoverGeneration; }
+
+ bool ObservingRefreshDriver() const { return mObservingRefreshDriver; }
+
+ void SetObservingRefreshDriver(bool aObserving) {
+ mObservingRefreshDriver = aObserving;
+ }
+
+ void Disconnect() { mPresContext = nullptr; }
+
+protected:
+ void ContentStateChangedInternal(Element* aElement,
+ EventStates aStateMask,
+ nsChangeHint* aOutChangeHint,
+ nsRestyleHint* aOutRestyleHint);
+
+ bool IsDisconnected() { return mPresContext == nullptr; }
+
+ void IncrementHoverGeneration() {
+ ++mHoverGeneration;
+ }
+
+ void IncrementRestyleGeneration() {
+ if (++mRestyleGeneration == 0) {
+ // Keep mRestyleGeneration from being 0, since that's what
+ // nsPresContext::GetRestyleGeneration returns when it no
+ // longer has a RestyleManager.
+ ++mRestyleGeneration;
+ }
+ }
+
+ nsPresContext* PresContext() const {
+ MOZ_ASSERT(mPresContext);
+ return mPresContext;
+ }
+
+ nsCSSFrameConstructor* FrameConstructor() const {
+ return PresContext()->FrameConstructor();
+ }
+
+ inline bool IsGecko() const {
+ return !IsServo();
+ }
+
+ inline bool IsServo() const {
+#ifdef MOZ_STYLO
+ return PresContext()->StyleSet()->IsServo();
+#else
+ return false;
+#endif
+ }
+
+private:
+ nsPresContext* mPresContext; // weak, can be null after Disconnect().
+ uint32_t mRestyleGeneration;
+ uint32_t mHoverGeneration;
+ // True if we're already waiting for a refresh notification.
+ bool mObservingRefreshDriver;
+};
+
+} // namespace mozilla
+
+#endif
--- a/layout/base/RestyleManagerHandle.h
+++ b/layout/base/RestyleManagerHandle.h
@@ -130,16 +130,18 @@ public:
inline void AttributeChanged(dom::Element* aElement,
int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType,
const nsAttrValue* aOldValue);
inline nsresult ReparentStyleContext(nsIFrame* aFrame);
inline bool HasPendingRestyles();
inline uint64_t GetRestyleGeneration() const;
+ inline uint32_t GetHoverGeneration() const;
+ inline void SetObservingRefreshDriver(bool aObserving);
private:
// Stores a pointer to an RestyleManager or a ServoRestyleManager. The least
// significant bit is 0 for the former, 1 for the latter. This is
// valid as the least significant bit will never be used for a pointer
// value on platforms we care about.
uintptr_t mValue;
};
--- a/layout/base/RestyleManagerHandleInlines.h
+++ b/layout/base/RestyleManagerHandleInlines.h
@@ -138,14 +138,27 @@ RestyleManagerHandle::Ptr::HasPendingRes
}
uint64_t
RestyleManagerHandle::Ptr::GetRestyleGeneration() const
{
FORWARD(GetRestyleGeneration, ());
}
+uint32_t
+RestyleManagerHandle::Ptr::GetHoverGeneration() const
+{
+ FORWARD(GetHoverGeneration, ());
+}
+
+void
+RestyleManagerHandle::Ptr::SetObservingRefreshDriver(bool aObserving)
+{
+ FORWARD(SetObservingRefreshDriver, (aObserving));
+}
+
+
} // namespace mozilla
#undef FORWARD
#undef FORWARD_CONCRETE
#endif // mozilla_RestyleManagerHandleInlines_h
--- a/layout/base/RestyleTracker.cpp
+++ b/layout/base/RestyleTracker.cpp
@@ -163,28 +163,30 @@ RestyleTracker::DoProcessRestyles()
// loop so that we process any restyle events generated by processing
while (mPendingRestyles.Count()) {
if (mHaveLaterSiblingRestyles) {
// Convert them to individual restyles on all the later siblings
AutoTArray<RefPtr<Element>, RESTYLE_ARRAY_STACKSIZE> laterSiblingArr;
for (auto iter = mPendingRestyles.Iter(); !iter.Done(); iter.Next()) {
auto element = static_cast<dom::Element*>(iter.Key());
+ MOZ_ASSERT(!element->IsStyledByServo(), "Should not have Servo-styled elements here");
// Only collect the entries that actually need restyling by us (and
// haven't, for example, already been restyled).
// It's important to not mess with the flags on entries not in our
// document.
if (element->GetComposedDoc() == Document() &&
element->HasFlag(RestyleBit()) &&
(iter.Data()->mRestyleHint & eRestyle_LaterSiblings)) {
laterSiblingArr.AppendElement(element);
}
}
for (uint32_t i = 0; i < laterSiblingArr.Length(); ++i) {
Element* element = laterSiblingArr[i];
+ MOZ_ASSERT(!element->IsStyledByServo());
for (nsIContent* sibling = element->GetNextSibling();
sibling;
sibling = sibling->GetNextSibling()) {
if (sibling->IsElement()) {
LOG_RESTYLE("adding pending restyle for %s due to "
"eRestyle_LaterSiblings hint on %s",
FrameTagToString(sibling->AsElement()).get(),
FrameTagToString(element->AsElement()).get());
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -1,37 +1,57 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "mozilla/ServoRestyleManager.h"
+#include "mozilla/ServoStyleSet.h"
using namespace mozilla::dom;
namespace mozilla {
-ServoRestyleManager::ServoRestyleManager()
- : mRestyleGeneration(1)
+ServoRestyleManager::ServoRestyleManager(nsPresContext* aPresContext)
+ : RestyleManagerBase(aPresContext)
{
}
void
ServoRestyleManager::Disconnect()
{
NS_ERROR("stylo: ServoRestyleManager::Disconnect not implemented");
}
void
ServoRestyleManager::PostRestyleEvent(Element* aElement,
nsRestyleHint aRestyleHint,
nsChangeHint aMinChangeHint)
{
- NS_ERROR("stylo: ServoRestyleManager::PostRestyleEvent not implemented");
+ if (MOZ_UNLIKELY(IsDisconnected()) ||
+ MOZ_UNLIKELY(PresContext()->PresShell()->IsDestroying())) {
+ return;
+ }
+
+ nsIPresShell* presShell = PresContext()->PresShell();
+ if (!ObservingRefreshDriver()) {
+ SetObservingRefreshDriver(PresContext()->RefreshDriver()->
+ AddStyleFlushObserver(presShell));
+ }
+
+ aElement->SetIsDirtyForServo();
+ nsINode* cur = aElement;
+ while ((cur = cur->GetParentNode())) {
+ if (cur->HasDirtyDescendantsForServo())
+ break;
+ cur->SetHasDirtyDescendantsForServo();
+ }
+
+ presShell->GetDocument()->SetNeedStyleFlush();
}
void
ServoRestyleManager::PostRestyleEventForLazyConstruction()
{
NS_ERROR("stylo: ServoRestyleManager::PostRestyleEventForLazyConstruction not implemented");
}
@@ -48,17 +68,17 @@ ServoRestyleManager::PostRebuildAllStyle
{
MOZ_CRASH("stylo: ServoRestyleManager::PostRebuildAllStyleDataEvent not implemented");
}
void
ServoRestyleManager::ProcessPendingRestyles()
{
// XXXheycam Do nothing for now.
- mRestyleGeneration++;
+ IncrementRestyleGeneration();
}
void
ServoRestyleManager::RestyleForInsertOrChange(Element* aContainer,
nsIContent* aChild)
{
NS_ERROR("stylo: ServoRestyleManager::RestyleForInsertOrChange not implemented");
}
@@ -77,17 +97,27 @@ ServoRestyleManager::RestyleForRemove(El
{
NS_ERROR("stylo: ServoRestyleManager::RestyleForRemove not implemented");
}
nsresult
ServoRestyleManager::ContentStateChanged(nsIContent* aContent,
EventStates aStateMask)
{
- NS_ERROR("stylo: ServoRestyleManager::ContentStateChanged not implemented");
+ if (!aContent->IsElement()) {
+ return NS_OK;
+ }
+
+ Element* aElement = aContent->AsElement();
+ nsChangeHint changeHint;
+ nsRestyleHint restyleHint;
+ ContentStateChangedInternal(aElement, aStateMask, &changeHint, &restyleHint);
+
+ // TODO(emilio): Post a restyle here, and make it effective.
+ // PostRestyleEvent(aElement, restyleHint, changeHint);
return NS_OK;
}
void
ServoRestyleManager::AttributeWillChange(Element* aElement,
int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType,
@@ -114,15 +144,9 @@ ServoRestyleManager::ReparentStyleContex
bool
ServoRestyleManager::HasPendingRestyles()
{
NS_ERROR("stylo: ServoRestyleManager::HasPendingRestyles not implemented");
return false;
}
-uint64_t
-ServoRestyleManager::GetRestyleGeneration() const
-{
- return mRestyleGeneration;
-}
-
} // namespace mozilla
--- a/layout/base/ServoRestyleManager.h
+++ b/layout/base/ServoRestyleManager.h
@@ -3,40 +3,43 @@
/* 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_ServoRestyleManager_h
#define mozilla_ServoRestyleManager_h
#include "mozilla/EventStates.h"
+#include "mozilla/RestyleManagerBase.h"
#include "nsChangeHint.h"
#include "nsISupportsImpl.h"
+#include "nsPresContext.h"
+#include "nsINode.h"
namespace mozilla {
namespace dom {
class Element;
} // namespace dom
} // namespace mozilla
class nsAttrValue;
class nsIAtom;
class nsIContent;
class nsIFrame;
namespace mozilla {
/**
* Restyle manager for a Servo-backed style system.
*/
-class ServoRestyleManager
+class ServoRestyleManager : public RestyleManagerBase
{
public:
NS_INLINE_DECL_REFCOUNTING(ServoRestyleManager)
- ServoRestyleManager();
+ explicit ServoRestyleManager(nsPresContext* aPresContext);
void Disconnect();
void PostRestyleEvent(dom::Element* aElement,
nsRestyleHint aRestyleHint,
nsChangeHint aMinChangeHint);
void PostRestyleEventForLazyConstruction();
void RebuildAllStyleData(nsChangeHint aExtraHint,
nsRestyleHint aRestyleHint);
@@ -59,19 +62,24 @@ public:
const nsAttrValue* aNewValue);
void AttributeChanged(dom::Element* aElement,
int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType,
const nsAttrValue* aOldValue);
nsresult ReparentStyleContext(nsIFrame* aFrame);
bool HasPendingRestyles();
- uint64_t GetRestyleGeneration() const;
protected:
~ServoRestyleManager() {}
- uint64_t mRestyleGeneration;
+private:
+ inline ServoStyleSet* StyleSet() const {
+ MOZ_ASSERT(PresContext()->StyleSet()->IsServo(),
+ "ServoRestyleManager should only be used with a Servo-flavored "
+ "style backend");
+ return PresContext()->StyleSet()->AsServo();
+ }
};
} // namespace mozilla
#endif // mozilla_ServoRestyleManager_h
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -102,16 +102,17 @@ EXPORTS += [
EXPORTS.mozilla += [
'ArenaObjectID.h',
'ArenaRefPtr.h',
'ArenaRefPtrInlines.h',
'GeometryUtils.h',
'PaintTracker.h',
'RestyleLogging.h',
'RestyleManager.h',
+ 'RestyleManagerBase.h',
'RestyleManagerHandle.h',
'RestyleManagerHandleInlines.h',
'ServoRestyleManager.h',
'StaticPresData.h',
]
UNIFIED_SOURCES += [
'AccessibleCaret.cpp',
@@ -149,16 +150,17 @@ UNIFIED_SOURCES += [
'nsPresContext.cpp',
'nsPresShell.cpp',
'nsQuoteList.cpp',
'nsStyleChangeList.cpp',
'nsStyleSheetService.cpp',
'PaintTracker.cpp',
'PositionedEventTargeting.cpp',
'RestyleManager.cpp',
+ 'RestyleManagerBase.cpp',
'RestyleTracker.cpp',
'ScrollbarStyles.cpp',
'ServoRestyleManager.cpp',
'StackArena.cpp',
'StaticPresData.cpp',
'TouchManager.cpp',
'ZoomConstraintsClient.cpp',
]
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -1975,17 +1975,17 @@ nsCSSFrameConstructor::GetParentType(nsI
if (aFrameType == nsGkAtoms::tableColGroupFrame) {
return eTypeColGroup;
}
if (aFrameType == nsGkAtoms::rubyBaseContainerFrame) {
return eTypeRubyBaseContainer;
}
if (aFrameType == nsGkAtoms::rubyTextContainerFrame) {
return eTypeRubyTextContainer;
- }
+ }
if (aFrameType == nsGkAtoms::rubyFrame) {
return eTypeRuby;
}
return eTypeBlock;
}
static nsContainerFrame*
@@ -2425,17 +2425,17 @@ nsCSSFrameConstructor::ConstructDocEleme
state.mPresShell->CaptureHistoryState(getter_AddRefs(mTempFrameTreeState));
// Make sure that we'll handle restyles for this document element in
// the future. We need this, because the document element might
// have stale restyle bits from a previous frame constructor for
// this document. Unlike in AddFrameConstructionItems, it's safe to
// unset all element restyle flags, since we don't have any
// siblings.
- aDocElement->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
+ aDocElement->UnsetRestyleFlagsIfGecko();
// --------- CREATE AREA OR BOX FRAME -------
// FIXME: Should this use ResolveStyleContext? (The calls in this
// function are the only case in nsCSSFrameConstructor where we don't
// do so for the construction of a style context for an element.)
RefPtr<nsStyleContext> styleContext;
styleContext = mPresShell->StyleSet()->ResolveStyleFor(aDocElement,
nullptr);
@@ -4135,17 +4135,17 @@ nsCSSFrameConstructor::CreateAnonymousFr
if (newFrame) {
NS_ASSERTION(content->GetPrimaryFrame(),
"Content must have a primary frame now");
newFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT);
aChildItems.AddChild(newFrame);
} else {
FrameConstructionItemList items;
{
- // Skip parent display based style-fixup during our
+ // Skip parent display based style-fixup during our
// AddFrameConstructionItems() call:
TreeMatchContext::AutoParentDisplayBasedStyleFixupSkipper
parentDisplayBasedStyleFixupSkipper(aState.mTreeMatchContext);
AddFrameConstructionItems(aState, content, true, insertion, items);
}
ConstructFramesFromItemList(aState, items, aParentFrame, aChildItems);
}
@@ -5508,17 +5508,17 @@ nsCSSFrameConstructor::AddPageBreakItem(
}
bool
nsCSSFrameConstructor::ShouldCreateItemsForChild(nsFrameConstructorState& aState,
nsIContent* aContent,
nsContainerFrame* aParentFrame)
{
aContent->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
- if (aContent->IsElement()) {
+ if (aContent->IsElement() && !aContent->IsStyledByServo()) {
// We can't just remove our pending restyle flags, since we may
// have restyle-later-siblings set on us. But we _can_ remove the
// "is possible restyle root" flags, and need to. Otherwise we can
// end up with stale such flags (e.g. if we used to have a
// display:none parent when our last restyle was posted and
// processed and now no longer do).
aContent->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS &
~ELEMENT_PENDING_RESTYLE_FLAGS);
@@ -9751,17 +9751,17 @@ nsCSSFrameConstructor::sPseudoParentData
{ // Ruby
FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT |
FCDATA_USE_CHILD_ITEMS |
FCDATA_SKIP_FRAMESET,
NS_NewRubyFrame),
&nsCSSAnonBoxes::ruby
},
{ // Ruby Base
- FCDATA_DECL(FCDATA_USE_CHILD_ITEMS |
+ FCDATA_DECL(FCDATA_USE_CHILD_ITEMS |
FCDATA_IS_LINE_PARTICIPANT |
FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyBaseContainer) |
FCDATA_SKIP_FRAMESET,
NS_NewRubyBaseFrame),
&nsCSSAnonBoxes::rubyBase
},
{ // Ruby Base Container
FCDATA_DECL(FCDATA_USE_CHILD_ITEMS |
@@ -10321,17 +10321,17 @@ nsCSSFrameConstructor::CreateNeededPseud
break;
case eTypeTable:
// Either colgroup or rowgroup, depending on what we're grouping.
wrapperType = groupingParentType == eTypeColGroup ?
eTypeColGroup : eTypeRowGroup;
break;
case eTypeColGroup:
MOZ_CRASH("Colgroups should be suppresing non-col child items");
- default:
+ default:
NS_ASSERTION(ourParentType == eTypeBlock, "Unrecognized parent type");
if (IsRubyParentType(groupingParentType)) {
wrapperType = eTypeRuby;
} else {
NS_ASSERTION(IsTableParentType(groupingParentType),
"groupingParentType should be either Ruby or table");
wrapperType = eTypeTable;
}
@@ -10537,23 +10537,26 @@ nsCSSFrameConstructor::AddFCItemsForAnon
nsIContent* content = aAnonymousItems[i].mContent;
#ifdef DEBUG
nsIAnonymousContentCreator* creator = do_QueryFrame(aFrame);
NS_ASSERTION(!creator || !creator->CreateFrameFor(content),
"If you need to use CreateFrameFor, you need to call "
"CreateAnonymousFrames manually and not follow the standard "
"ProcessChildren() codepath for this frame");
#endif
+ // Anything restyled by servo should already have the style data.
+ MOZ_ASSERT_IF(content->IsStyledByServo(), !!content->GetServoNodeData());
+ // Gecko-styled nodes should have no pending restyle flags.
+ MOZ_ASSERT_IF(!content->IsStyledByServo(),
+ !content->IsElement() ||
+ !(content->GetFlags() & ELEMENT_ALL_RESTYLE_FLAGS));
// Assert some things about this content
MOZ_ASSERT(!(content->GetFlags() &
(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME)),
"Should not be marked as needing frames");
- MOZ_ASSERT(!content->IsElement() ||
- !(content->GetFlags() & ELEMENT_ALL_RESTYLE_FLAGS),
- "Should have no pending restyle flags");
MOZ_ASSERT(!content->GetPrimaryFrame(),
"Should have no existing frame");
MOZ_ASSERT(!content->IsNodeOfType(nsINode::eCOMMENT) &&
!content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION),
"Why is someone creating garbage anonymous content");
RefPtr<nsStyleContext> styleContext;
TreeMatchContext::AutoParentDisplayBasedStyleFixupSkipper
@@ -10691,19 +10694,17 @@ nsCSSFrameConstructor::ProcessChildren(n
ancestorPusher.PushAncestorAndStyleScope(parent->AsElement());
} else {
ancestorPusher.PushStyleScope(parent->AsElement());
}
}
// Frame construction item construction should not post
// restyles, so removing restyle flags here is safe.
- if (child->IsElement()) {
- child->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
- }
+ child->UnsetRestyleFlagsIfGecko();
if (addChildItems) {
AddFrameConstructionItems(aState, child, iter.XBLInvolved(), insertion,
itemsToConstruct);
} else {
ClearLazyBits(child, child->GetNextSibling());
}
}
itemsToConstruct.SetParentHasNoXBLChildren(!iter.XBLInvolved());
@@ -12003,23 +12004,22 @@ nsCSSFrameConstructor::BuildInlineChildI
// Manually check for comments/PIs, since we don't have a frame to pass to
// AddFrameConstructionItems. We know our parent is a non-replaced inline,
// so there is no need to do the NeedFrameFor check.
content->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
if (content->IsNodeOfType(nsINode::eCOMMENT) ||
content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
continue;
}
- if (content->IsElement()) {
- // See comment explaining why we need to remove the "is possible
- // restyle root" flags in AddFrameConstructionItems. But note
- // that we can remove all restyle flags, just like in
- // ProcessChildren and for the same reason.
- content->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
- }
+
+ // See comment explaining why we need to remove the "is possible
+ // restyle root" flags in AddFrameConstructionItems. But note
+ // that we can remove all restyle flags, just like in
+ // ProcessChildren and for the same reason.
+ content->UnsetRestyleFlagsIfGecko();
RefPtr<nsStyleContext> childContext =
ResolveStyleContext(parentStyleContext, content, &aState);
AddFrameConstructionItemsInternal(aState, content, nullptr,
content->NodeInfo()->NameAtom(),
content->GetNameSpaceID(),
iter.XBLInvolved(), childContext,
--- a/layout/base/nsCSSRenderingBorders.cpp
+++ b/layout/base/nsCSSRenderingBorders.cpp
@@ -16,16 +16,17 @@
#include "DottedCornerFinder.h"
#include "nsLayoutUtils.h"
#include "nsStyleConsts.h"
#include "nsContentUtils.h"
#include "nsCSSColorUtils.h"
#include "GeckoProfiler.h"
#include "nsExpirationTracker.h"
#include "RoundedRect.h"
+#include "nsIScriptError.h"
#include "nsClassHashtable.h"
#include "nsPresContext.h"
#include "nsStyleStruct.h"
#include "mozilla/gfx/2D.h"
#include "gfx2DGlue.h"
#include "gfxGradientCache.h"
#include <algorithm>
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -739,17 +739,18 @@ nsDisplayListBuilder::nsDisplayListBuild
mIsPaintingToWindow(false),
mIsCompositingCheap(false),
mContainsPluginItem(false),
mAncestorHasApzAwareEventHandler(false),
mHaveScrollableDisplayPort(false),
mWindowDraggingAllowed(false),
mIsBuildingForPopup(nsLayoutUtils::IsPopup(aReferenceFrame)),
mForceLayerForScrollParent(false),
- mAsyncPanZoomEnabled(nsLayoutUtils::AsyncPanZoomEnabled(aReferenceFrame))
+ mAsyncPanZoomEnabled(nsLayoutUtils::AsyncPanZoomEnabled(aReferenceFrame)),
+ mBuildingInvisibleItems(false)
{
MOZ_COUNT_CTOR(nsDisplayListBuilder);
PL_InitArenaPool(&mPool, "displayListArena", 4096,
std::max(NS_ALIGNMENT_OF(void*),NS_ALIGNMENT_OF(double))-1);
nsPresContext* pc = aReferenceFrame->PresContext();
nsIPresShell *shell = pc->PresShell();
if (pc->IsRenderingOnlySelection()) {
@@ -1639,21 +1640,27 @@ nsDisplayList::ComputeVisibilityForSubli
bool anyVisible = false;
AutoTArray<nsDisplayItem*, 512> elements;
MoveListTo(this, &elements);
for (int32_t i = elements.Length() - 1; i >= 0; --i) {
nsDisplayItem* item = elements[i];
- nsRect bounds = item->GetClippedBounds(aBuilder);
-
- nsRegion itemVisible;
- itemVisible.And(*aVisibleRegion, bounds);
- item->mVisibleRect = itemVisible.GetBounds();
+
+ if (item->mForceNotVisible) {
+ NS_ASSERTION(item->mVisibleRect.IsEmpty(),
+ "invisible items should have empty vis rect");
+ } else {
+ nsRect bounds = item->GetClippedBounds(aBuilder);
+
+ nsRegion itemVisible;
+ itemVisible.And(*aVisibleRegion, bounds);
+ item->mVisibleRect = itemVisible.GetBounds();
+ }
if (item->ComputeVisibility(aBuilder, aVisibleRegion)) {
anyVisible = true;
nsRegion opaque = TreatAsOpaque(item, aBuilder);
// Subtract opaque item from the visible region
aBuilder->SubtractFromVisibleRegion(aVisibleRegion, opaque);
}
@@ -2239,16 +2246,17 @@ nsDisplayItem::nsDisplayItem(nsDisplayLi
{}
nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
const DisplayItemScrollClip* aScrollClip)
: mFrame(aFrame)
, mClip(aBuilder->ClipState().GetCurrentCombinedClip(aBuilder))
, mScrollClip(aScrollClip)
, mAnimatedGeometryRoot(nullptr)
+ , mForceNotVisible(aBuilder->IsBuildingInvisibleItems())
#ifdef MOZ_DUMP_PAINTING
, mPainted(false)
#endif
{
mReferenceFrame = aBuilder->FindReferenceFrameFor(aFrame, &mToReferenceFrame);
// This can return the wrong result if the item override ShouldFixToViewport(),
// the item needs to set it again in its constructor.
mAnimatedGeometryRoot = aBuilder->FindAnimatedGeometryRootFor(aFrame);
@@ -2301,21 +2309,26 @@ nsDisplayItem::ComputeVisibility(nsDispl
{
return !mVisibleRect.IsEmpty() &&
!IsInvisibleInRect(aVisibleRegion->GetBounds());
}
bool
nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder,
nsRegion* aVisibleRegion) {
- nsRect bounds = GetClippedBounds(aBuilder);
-
- nsRegion itemVisible;
- itemVisible.And(*aVisibleRegion, bounds);
- mVisibleRect = itemVisible.GetBounds();
+ if (mForceNotVisible) {
+ NS_ASSERTION(mVisibleRect.IsEmpty(),
+ "invisible items should have empty vis rect");
+ } else {
+ nsRect bounds = GetClippedBounds(aBuilder);
+
+ nsRegion itemVisible;
+ itemVisible.And(*aVisibleRegion, bounds);
+ mVisibleRect = itemVisible.GetBounds();
+ }
// When we recompute visibility within layers we don't need to
// expand the visible region for content behind plugins (the plugin
// is not in the layer).
if (!ComputeVisibility(aBuilder, aVisibleRegion)) {
mVisibleRect = nsRect();
return false;
}
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -714,17 +714,18 @@ public:
: mBuilder(aBuilder),
mPrevFrame(aBuilder->mCurrentFrame),
mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame),
mPrevLayerEventRegions(aBuilder->mLayerEventRegions),
mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame),
mPrevDirtyRect(aBuilder->mDirtyRect),
mPrevAGR(aBuilder->mCurrentAGR),
mPrevIsAtRootOfPseudoStackingContext(aBuilder->mIsAtRootOfPseudoStackingContext),
- mPrevAncestorHasApzAwareEventHandler(aBuilder->mAncestorHasApzAwareEventHandler)
+ mPrevAncestorHasApzAwareEventHandler(aBuilder->mAncestorHasApzAwareEventHandler),
+ mPrevBuildingInvisibleItems(aBuilder->mBuildingInvisibleItems)
{
if (aForChild->IsTransformed()) {
aBuilder->mCurrentOffsetToReferenceFrame = nsPoint();
aBuilder->mCurrentReferenceFrame = aForChild;
} else if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
aBuilder->mCurrentOffsetToReferenceFrame += aForChild->GetPosition();
} else {
aBuilder->mCurrentReferenceFrame =
@@ -754,37 +755,42 @@ public:
// current frame is an immediate descendant.
const nsIFrame* GetPrevAnimatedGeometryRoot() const {
return mPrevAnimatedGeometryRoot;
}
bool IsAnimatedGeometryRoot() const {
return *mBuilder->mCurrentAGR == mBuilder->mCurrentFrame;
}
+ void RestoreBuildingInvisibleItemsValue() {
+ mBuilder->mBuildingInvisibleItems = mPrevBuildingInvisibleItems;
+ }
~AutoBuildingDisplayList() {
mBuilder->mCurrentFrame = mPrevFrame;
mBuilder->mCurrentReferenceFrame = mPrevReferenceFrame;
mBuilder->mLayerEventRegions = mPrevLayerEventRegions;
mBuilder->mCurrentOffsetToReferenceFrame = mPrevOffset;
mBuilder->mDirtyRect = mPrevDirtyRect;
mBuilder->mCurrentAGR = mPrevAGR;
mBuilder->mIsAtRootOfPseudoStackingContext = mPrevIsAtRootOfPseudoStackingContext;
mBuilder->mAncestorHasApzAwareEventHandler = mPrevAncestorHasApzAwareEventHandler;
+ mBuilder->mBuildingInvisibleItems = mPrevBuildingInvisibleItems;
}
private:
nsDisplayListBuilder* mBuilder;
const nsIFrame* mPrevFrame;
const nsIFrame* mPrevReferenceFrame;
nsIFrame* mPrevAnimatedGeometryRoot;
nsDisplayLayerEventRegions* mPrevLayerEventRegions;
nsPoint mPrevOffset;
nsRect mPrevDirtyRect;
AnimatedGeometryRoot* mPrevAGR;
bool mPrevIsAtRootOfPseudoStackingContext;
bool mPrevAncestorHasApzAwareEventHandler;
+ bool mPrevBuildingInvisibleItems;
};
/**
* A helper class to temporarily set the value of mInTransform.
*/
class AutoInTransformSetter;
friend class AutoInTransformSetter;
class AutoInTransformSetter {
@@ -1124,16 +1130,21 @@ public:
const nsRect GetPreserves3DDirtyRect(const nsIFrame *aFrame) const {
return mPreserves3DCtx.mDirtyRect;
}
void SetPreserves3DDirtyRect(const nsRect &aDirtyRect) {
mPreserves3DCtx.mDirtyRect = aDirtyRect;
}
+ bool IsBuildingInvisibleItems() const { return mBuildingInvisibleItems; }
+ void SetBuildingInvisibleItems(bool aBuildingInvisibleItems) {
+ mBuildingInvisibleItems = aBuildingInvisibleItems;
+ }
+
private:
void MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame, nsIFrame* aFrame,
const nsRect& aDirtyRect);
/**
* Returns whether a frame acts as an animated geometry root, optionally
* returning the next ancestor to check.
*/
@@ -1273,16 +1284,17 @@ private:
// True when the first async-scrollable scroll frame for which we build a
// display list has a display port. An async-scrollable scroll frame is one
// which WantsAsyncScroll().
bool mHaveScrollableDisplayPort;
bool mWindowDraggingAllowed;
bool mIsBuildingForPopup;
bool mForceLayerForScrollParent;
bool mAsyncPanZoomEnabled;
+ bool mBuildingInvisibleItems;
};
class nsDisplayItem;
class nsDisplayList;
/**
* nsDisplayItems are put in singly-linked lists rooted in an nsDisplayList.
* nsDisplayItemLink holds the link. The lists are linked from lowest to
* highest in z-order.
@@ -1872,16 +1884,17 @@ protected:
nsPoint mToReferenceFrame;
// This is the rectangle that needs to be painted.
// Display item construction sets this to the dirty rect.
// nsDisplayList::ComputeVisibility sets this to the visible region
// of the item by intersecting the current visible region with the bounds
// of the item. Paint implementations can use this to limit their drawing.
// Guaranteed to be contained in GetBounds().
nsRect mVisibleRect;
+ bool mForceNotVisible;
#ifdef MOZ_DUMP_PAINTING
// True if this frame has been painted.
bool mPainted;
#endif
};
/**
* Manages a singly-linked list of display list items.
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -2160,47 +2160,25 @@ nsDocumentViewer::RequestWindowClose(boo
mDeferredWindowClose = true;
} else
#endif
*aCanClose = true;
return NS_OK;
}
-static StyleBackendType
-StyleBackendTypeForDocument(nsIDocument* aDocument, nsIDocShell* aContainer)
-{
- MOZ_ASSERT(aDocument);
-
- // XXX For now we use a Servo-backed style set only for (X)HTML documents
- // in content docshells. This should let us avoid implementing XUL-specific
- // CSS features. And apart from not supporting SVG properties in Servo
- // yet, the root SVG element likes to create a style sheet for an SVG
- // document before we have a pres shell (i.e. before we make the decision
- // here about whether to use a Gecko- or Servo-backed style system), so
- // we avoid Servo-backed style sets for SVG documents.
-
- return nsPresContext::StyloEnabled() &&
- aDocument->IsHTMLOrXHTML() &&
- aContainer &&
- aContainer->ItemType() == nsIDocShell::typeContent ?
- StyleBackendType::Servo :
- StyleBackendType::Gecko;
-}
-
StyleSetHandle
nsDocumentViewer::CreateStyleSet(nsIDocument* aDocument)
{
// Make sure this does the same thing as PresShell::AddSheet wrt ordering.
// this should eventually get expanded to allow for creating
// different sets for different media
- StyleBackendType backendType =
- StyleBackendTypeForDocument(aDocument, mContainer);
+ StyleBackendType backendType = aDocument->GetStyleBackendType();
StyleSetHandle styleSet;
if (backendType == StyleBackendType::Gecko) {
styleSet = new nsStyleSet();
} else {
styleSet = new ServoStyleSet();
}
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -9360,8 +9360,16 @@ nsLayoutUtils::GetCumulativeApzCallbackT
}
}
frame = GetCrossDocParentFrame(frame);
lastContent = content;
content = frame ? frame->GetContent() : nullptr;
}
return delta;
}
+
+/* static */ bool
+nsLayoutUtils::SupportsServoStyleBackend(nsIDocument* aDocument)
+{
+ return nsPresContext::StyloEnabled() &&
+ aDocument->IsHTMLOrXHTML() &&
+ static_cast<nsDocument*>(aDocument)->IsContentDocument();
+}
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -2849,16 +2849,24 @@ public:
* transforms on the content elements encountered along the way. Return the
* accumulated value.
* XXX: Note that this does not take into account CSS transforms, nor
* differences in structure between the frame tree and the layer tree (which
* is probably what we *want* to be computing).
*/
static CSSPoint GetCumulativeApzCallbackTransform(nsIFrame* aFrame);
+ /*
+ * Returns whether the given document supports being rendered with a
+ * Servo-backed style system. This checks whether Stylo is enabled
+ * globally, that the document is an HTML document, and that it is
+ * being presented in a content docshell.
+ */
+ static bool SupportsServoStyleBackend(nsIDocument* aDocument);
+
private:
static uint32_t sFontSizeInflationEmPerLine;
static uint32_t sFontSizeInflationMinTwips;
static uint32_t sFontSizeInflationLineThreshold;
static int32_t sFontSizeInflationMappingIntercept;
static uint32_t sFontSizeInflationMaxRatio;
static bool sFontSizeInflationForceEnabled;
static bool sFontSizeInflationDisabledInMasterProcess;
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -3677,33 +3677,28 @@ FlushLayoutRecursive(nsIDocument* aDocum
return true;
}
void
PresShell::DispatchSynthMouseMove(WidgetGUIEvent* aEvent,
bool aFlushOnHoverChange)
{
RestyleManagerHandle restyleManager = mPresContext->RestyleManager();
- if (restyleManager->IsServo()) {
- NS_ERROR("stylo: cannot dispatch synthetic mouse moves when using a "
- "ServoRestyleManager yet");
- return;
- }
uint32_t hoverGenerationBefore =
- restyleManager->AsGecko()->GetHoverGeneration();
+ restyleManager->GetHoverGeneration();
nsEventStatus status;
nsView* targetView = nsView::GetViewFor(aEvent->mWidget);
if (!targetView)
return;
targetView->GetViewManager()->DispatchEvent(aEvent, targetView, &status);
if (MOZ_UNLIKELY(mIsDestroying)) {
return;
}
if (aFlushOnHoverChange &&
- hoverGenerationBefore != restyleManager->AsGecko()->GetHoverGeneration()) {
+ hoverGenerationBefore != restyleManager->GetHoverGeneration()) {
// Flush so that the resulting reflow happens now so that our caller
// can suppress any synthesized mouse moves caused by that reflow.
// This code only ever runs for the root document, but :hover changes
// can happen in descendant documents too, so make sure we flush
// all of them.
FlushLayoutRecursive(mDocument);
}
}
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -1741,20 +1741,17 @@ nsRefreshDriver::Tick(int64_t aNowEpoch,
profiler_tracing("Paint", "Styles", mStyleCause, TRACING_INTERVAL_START);
mStyleCause = nullptr;
}
NS_ADDREF(shell);
mStyleFlushObservers.RemoveElement(shell);
RestyleManagerHandle restyleManager =
shell->GetPresContext()->RestyleManager();
- // XXX stylo: ServoRestyleManager does not observer the refresh driver yet.
- if (restyleManager->IsGecko()) {
- restyleManager->AsGecko()->mObservingRefreshDriver = false;
- }
+ restyleManager->SetObservingRefreshDriver(false);
shell->FlushPendingNotifications(ChangesToFlush(Flush_Style, false));
// Inform the FontFaceSet that we ticked, so that it can resolve its
// ready promise if it needs to (though it might still be waiting on
// a layout flush).
nsPresContext* presContext = shell->GetPresContext();
if (presContext) {
presContext->NotifyFontFaceSetOnRefresh();
}
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2794,22 +2794,30 @@ nsIFrame::BuildDisplayListForChild(nsDis
"Stacking contexts must also be pseudo-stacking-contexts");
nsDisplayListBuilder::AutoBuildingDisplayList
buildingForChild(aBuilder, child, dirty, pseudoStackingContext);
DisplayListClipState::AutoClipMultiple clipState(aBuilder);
CheckForApzAwareEventHandlers(aBuilder, child);
if (savedOutOfFlowData) {
+ aBuilder->SetBuildingInvisibleItems(false);
+
clipState.SetClipForContainingBlockDescendants(
&savedOutOfFlowData->mContainingBlockClip);
clipState.SetScrollClipForContainingBlockDescendants(
savedOutOfFlowData->mContainingBlockScrollClip);
} else if (GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO &&
isPlaceholder) {
+ NS_ASSERTION(dirty.IsEmpty(), "should have empty dirty rect");
+ // Every item we build from now until we descent into an out of flow that
+ // does have saved out of flow data should be invisible. This state gets
+ // restored when AutoBuildingDisplayList gets out of scope.
+ aBuilder->SetBuildingInvisibleItems(true);
+
// If we have nested out-of-flow frames and the outer one isn't visible
// then we won't have stored clip data for it. We can just clear the clip
// instead since we know we won't render anything, and the inner out-of-flow
// frame will setup the correct clip for itself.
clipState.SetClipForContainingBlockDescendants(nullptr);
clipState.SetScrollClipForContainingBlockDescendants(nullptr);
}
@@ -2893,17 +2901,19 @@ nsIFrame::BuildDisplayListForChild(nsDis
list.AppendToTop(pseudoStack.Floats());
list.AppendToTop(pseudoStack.Content());
list.AppendToTop(pseudoStack.Outlines());
extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants());
#ifdef DEBUG
DisplayDebugBorders(aBuilder, child, aLists);
#endif
}
-
+
+ buildingForChild.RestoreBuildingInvisibleItemsValue();
+
// Clear clip rect for the construction of the items below. Since we're
// clipping all their contents, they themselves don't need to be clipped.
clipState.Clear();
if (isPositioned || isVisuallyAtomic ||
(aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
// Genuine stacking contexts, and positioned pseudo-stacking-contexts,
// go in this level.
--- a/layout/generic/nsFrameSetFrame.cpp
+++ b/layout/generic/nsFrameSetFrame.cpp
@@ -274,29 +274,25 @@ nsHTMLFramesetFrame::Init(nsIContent*
for (uint32_t childX = 0; childX < numChildren; childX++) {
if (mChildCount == numCells) { // we have more <frame> or <frameset> than cells
// Clear the lazy bits in the remaining children. Also clear
// the restyle flags, like nsCSSFrameConstructor::ProcessChildren does.
for (uint32_t i = childX; i < numChildren; i++) {
nsIContent *child = mContent->GetChildAt(i);
child->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
- if (child->IsElement()) {
- child->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
- }
+ child->UnsetRestyleFlagsIfGecko();
}
break;
}
nsIContent *child = mContent->GetChildAt(childX);
child->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
// Also clear the restyle flags in the child like
// nsCSSFrameConstructor::ProcessChildren does.
- if (child->IsElement()) {
- child->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
- }
+ child->UnsetRestyleFlagsIfGecko();
// IMPORTANT: This must match the conditions in
// nsCSSFrameConstructor::ContentAppended/Inserted/Removed
if (!child->IsHTMLElement())
continue;
if (child->IsAnyOfHTMLElements(nsGkAtoms::frameset, nsGkAtoms::frame)) {
RefPtr<nsStyleContext> kidSC;
@@ -637,22 +633,22 @@ nsresult nsHTMLFramesetFrame::HandleEven
nsEventStatus* aEventStatus)
{
NS_ENSURE_ARG_POINTER(aEventStatus);
if (mDragger) {
// the nsFramesetBorderFrame has captured NS_MOUSE_DOWN
switch (aEvent->mMessage) {
case eMouseMove:
MouseDrag(aPresContext, aEvent);
- break;
+ break;
case eMouseUp:
if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
EndMouseDrag(aPresContext);
}
- break;
+ break;
default:
break;
}
*aEventStatus = nsEventStatus_eConsumeNoDefault;
} else {
*aEventStatus = nsEventStatus_eIgnore;
}
return NS_OK;
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -537,22 +537,34 @@ GlyphObserver::NotifyGlyphsChanged()
nsIPresShell* shell = mFrame->PresContext()->PresShell();
for (nsIFrame* f = mFrame; f;
f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
if (f != mFrame && f->HasAnyStateBits(TEXT_IN_TEXTRUN_USER_DATA)) {
// f will have its own GlyphObserver (if needed) so we can stop here.
break;
}
f->InvalidateFrame();
- // Theoretically we could just update overflow areas, perhaps using
- // OverflowChangedTracker, but that would do a bunch of work eagerly that
- // we should probably do lazily here since there could be a lot
- // of text frames affected and we'd like to coalesce the work. So that's
- // not easy to do well.
- shell->FrameNeedsReflow(f, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
+
+ // If this is a non-display text frame within SVG <text>, we need
+ // to reflow the SVGTextFrame. (This is similar to reflowing the
+ // SVGTextFrame in response to style changes, in
+ // SVGTextFrame::DidSetStyleContext.)
+ if (f->IsSVGText() && f->GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
+ auto svgTextFrame = static_cast<SVGTextFrame*>(
+ nsLayoutUtils::GetClosestFrameOfType(f,
+ nsGkAtoms::svgTextFrame));
+ svgTextFrame->ScheduleReflowSVGNonDisplayText();
+ } else {
+ // Theoretically we could just update overflow areas, perhaps using
+ // OverflowChangedTracker, but that would do a bunch of work eagerly that
+ // we should probably do lazily here since there could be a lot
+ // of text frames affected and we'd like to coalesce the work. So that's
+ // not easy to do well.
+ shell->FrameNeedsReflow(f, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
+ }
}
}
int32_t nsTextFrame::GetContentEnd() const {
nsTextFrame* next = static_cast<nsTextFrame*>(GetNextContinuation());
return next ? next->GetContentOffset() : mContent->GetText()->GetLength();
}
--- a/layout/generic/nsVideoFrame.cpp
+++ b/layout/generic/nsVideoFrame.cpp
@@ -630,25 +630,29 @@ nsVideoFrame::GetVideoIntrinsicSize(nsRe
}
void
nsVideoFrame::UpdatePosterSource(bool aNotify)
{
NS_ASSERTION(HasVideoElement(), "Only call this on <video> elements.");
HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
- if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::poster)) {
+ if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::poster) &&
+ !element->AttrValueIs(kNameSpaceID_None,
+ nsGkAtoms::poster,
+ nsGkAtoms::_empty,
+ eIgnoreCase)) {
nsAutoString posterStr;
element->GetPoster(posterStr);
mPosterImage->SetAttr(kNameSpaceID_None,
nsGkAtoms::src,
posterStr,
aNotify);
} else {
- mPosterImage->UnsetAttr(kNameSpaceID_None, nsGkAtoms::poster, aNotify);
+ mPosterImage->UnsetAttr(kNameSpaceID_None, nsGkAtoms::src, aNotify);
}
}
nsresult
nsVideoFrame::AttributeChanged(int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType)
{
--- a/layout/reftests/text-svgglyphs/reftest.list
+++ b/layout/reftests/text-svgglyphs/reftest.list
@@ -12,12 +12,12 @@ pref(gfx.font_rendering.opentype_svg.ena
pref(gfx.font_rendering.opentype_svg.enabled,true) fuzzy-if(cocoaWidget,1,7028) fuzzy-if(gtkWidget&&/^Linux\x20x86_64/.test(http.oscpu),1,23) fuzzy-if(skiaContent,1,250) == svg-glyph-objectgradient-zoom.svg svg-glyph-objectgradient-zoom-ref.svg
pref(gfx.font_rendering.opentype_svg.enabled,true) fuzzy-if(gtkWidget,1,1438) fuzzy-if(winWidget,1,1954) fuzzy-if(Android||B2G,8,3795) == svg-glyph-objectpattern.svg svg-glyph-objectpattern-ref.svg
pref(gfx.font_rendering.opentype_svg.enabled,true) == clip.html clip-ref.html
pref(gfx.font_rendering.opentype_svg.enabled,true) fuzzy(1,13) fuzzy-if(gtkWidget&&/^Linux\x20x86_64/.test(http.oscpu),1,62) fuzzy-if(B2G,1,25) fuzzy-if(skiaContent,1,350) == svg-glyph-objectopacity.svg svg-glyph-objectopacity-ref.svg # see bug 871961#c5
pref(gfx.font_rendering.opentype_svg.enabled,true) fuzzy-if(gtkWidget,1,2268) fuzzy-if(winWidget,1,3074) fuzzy-if(Android||B2G,5,4715) == svg-glyph-objectopacity2.svg svg-glyph-objectopacity2-ref.svg
pref(gfx.font_rendering.opentype_svg.enabled,true) fuzzy-if(skiaContent,2,200) == svg-glyph-paintnone.svg svg-glyph-paintnone-ref.svg
pref(gfx.font_rendering.opentype_svg.enabled,true) fuzzy-if(skiaContent,2,200) == svg-glyph-cachedopacity.svg svg-glyph-cachedopacity-ref.svg
pref(gfx.font_rendering.opentype_svg.enabled,true) fuzzy-if(cocoaWidget,255,100) == svg-glyph-objectvalue.svg svg-glyph-objectvalue-ref.svg
-#pref(gfx.font_rendering.opentype_svg.enabled,true) fails == svg-glyph-mask.svg svg-glyph-mask-ref.svg # bug 872483, 1135329
+pref(gfx.font_rendering.opentype_svg.enabled,true) fails == svg-glyph-mask.svg svg-glyph-mask-ref.svg # bug 872483
pref(gfx.font_rendering.opentype_svg.enabled,true) == svg-glyph-paint-server.svg svg-glyph-paint-server-ref.svg
pref(gfx.font_rendering.opentype_svg.enabled,true) == svg-glyph-transform.svg svg-glyph-transform-ref.svg
pref(gfx.font_rendering.opentype_svg.enabled,true) == svg-glyph-extents.html svg-glyph-extents-ref.html
--- a/layout/style/Loader.h
+++ b/layout/style/Loader.h
@@ -196,29 +196,16 @@ public:
private:
// Private destructor, to discourage deletion outside of Release():
~Loader();
public:
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(Loader)
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(Loader)
- /**
- * Returns the StyleBackendType that will be used for style sheets created
- * by this Loader. If SetStyleBackendType has been called, that value
- * will be returned by GetStyleBackendType. For Loaders created with the
- * nsIDocument* constructor and which haven't had SetStyleBackendType
- * called on them, the document's StyleBackendType will be returned.
- */
- StyleBackendType GetStyleBackendType() const;
-
- void SetStyleBackendType(StyleBackendType aType) {
- mStyleBackendType = Some(aType);
- }
-
void DropDocumentReference(); // notification that doc is going away
void SetCompatibilityMode(nsCompatibility aCompatMode)
{ mCompatMode = aCompatMode; }
nsCompatibility GetCompatibilityMode() { return mCompatMode; }
nsresult SetPreferredSheet(const nsAString& aTitle);
// XXXbz sort out what the deal is with events! When should they fire?
@@ -547,16 +534,18 @@ private:
void SheetComplete(SheetLoadData* aLoadData, nsresult aStatus);
// The guts of SheetComplete. This may be called recursively on parent datas
// or datas that had glommed on to a single load. The array is there so load
// datas whose observers need to be notified can be added to it.
void DoSheetComplete(SheetLoadData* aLoadData, nsresult aStatus,
LoadDataArray& aDatasToNotify);
+ StyleBackendType GetStyleBackendType() const;
+
struct Sheets {
nsBaseHashtable<URIPrincipalReferrerPolicyAndCORSModeHashKey,
StyleSheetHandle::RefPtr,
StyleSheetHandle> mCompleteSheets;
nsDataHashtable<URIPrincipalReferrerPolicyAndCORSModeHashKey, SheetLoadData*>
mLoadingDatas; // weak refs
nsDataHashtable<URIPrincipalReferrerPolicyAndCORSModeHashKey, SheetLoadData*>
mPendingDatas; // weak refs
@@ -583,16 +572,18 @@ private:
// whole bunch at once (e.g. in one of the stop methods). This is used to
// make sure that HasPendingLoads() won't return false until we're notifying
// on the last data we're working with.
uint32_t mDatasToNotifyOn;
nsCompatibility mCompatMode;
nsString mPreferredSheet; // title of preferred sheet
+ // Set explicitly when the Loader(StyleBackendType) constructor is used, or
+ // taken from the document when the Loader(nsIDocument*) constructor is used.
mozilla::Maybe<StyleBackendType> mStyleBackendType;
bool mEnabled; // is enabled to load new styles
#ifdef DEBUG
bool mSyncCallback;
#endif
};
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -159,16 +159,35 @@ Gecko_Namespace(RawGeckoElement* aElemen
nsIAtom*
Gecko_GetElementId(RawGeckoElement* aElement)
{
const nsAttrValue* attr = aElement->GetParsedAttr(nsGkAtoms::id);
return attr ? attr->GetAtomValue() : nullptr;
}
+// Dirtiness tracking.
+uint32_t
+Gecko_GetNodeFlags(RawGeckoNode* aNode)
+{
+ return aNode->GetFlags();
+}
+
+void
+Gecko_SetNodeFlags(RawGeckoNode* aNode, uint32_t aFlags)
+{
+ aNode->SetFlags(aFlags);
+}
+
+void
+Gecko_UnsetNodeFlags(RawGeckoNode* aNode, uint32_t aFlags)
+{
+ aNode->UnsetFlags(aFlags);
+}
+
template<class MatchFn>
bool
DoMatch(RawGeckoElement* aElement, nsIAtom* aNS, nsIAtom* aName, MatchFn aMatch)
{
if (aNS) {
int32_t ns = nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNS);
NS_ENSURE_TRUE(ns != kNameSpaceID_Unknown, false);
const nsAttrValue* value = aElement->GetParsedAttr(aName, ns);
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -171,16 +171,21 @@ NS_DECL_HOLDER_FFI_REFCOUNTING(nsIURI, U
// Display style.
void Gecko_SetMozBinding(nsStyleDisplay* style_struct,
const uint8_t* string_bytes, uint32_t string_length,
ThreadSafeURIHolder* base_uri,
ThreadSafeURIHolder* referrer,
ThreadSafePrincipalHolder* principal);
void Gecko_CopyMozBindingFrom(nsStyleDisplay* des, const nsStyleDisplay* src);
+// Dirtiness tracking.
+uint32_t Gecko_GetNodeFlags(RawGeckoNode* node);
+void Gecko_SetNodeFlags(RawGeckoNode* node, uint32_t flags);
+void Gecko_UnsetNodeFlags(RawGeckoNode* node, uint32_t flags);
+
// Styleset and Stylesheet management.
//
// TODO: Make these return already_AddRefed and UniquePtr when the binding
// generator is smart enough to handle them.
RawServoStyleSheet* Servo_StylesheetFromUTF8Bytes(
const uint8_t* bytes, uint32_t length,
mozilla::css::SheetParsingMode parsing_mode,
ThreadSafeURIHolder* base,
--- a/layout/style/StyleBackendType.h
+++ b/layout/style/StyleBackendType.h
@@ -9,15 +9,15 @@
namespace mozilla {
/**
* Enumeration that represents one of the two supported style system backends.
*/
enum class StyleBackendType : int
{
- Gecko,
+ Gecko = 1,
Servo
};
} // namespace mozilla
#endif // mozilla_StyleBackendType_h
--- a/layout/svg/SVGTextFrame.h
+++ b/layout/svg/SVGTextFrame.h
@@ -394,18 +394,18 @@ public:
* but which will skip over any ancestor non-display container frames on the
* way to the nsSVGOuterSVGFrame. It exists for the situation where a
* non-display <text> element has changed and needs to ensure ReflowSVG will
* be called on its closest display container frame, so that
* nsSVGDisplayContainerFrame::ReflowSVG will call ReflowSVGNonDisplayText on
* it.
*
* The only case where we have to do this is in response to a style change on
- * a non-display <text>; the only caller of ScheduleReflowSVGNonDisplayText
- * currently is SVGTextFrame::DidSetStyleContext.
+ * a non-display <text>. It is done in response to glyphs changes on
+ * non-display <text> (i.e., animated SVG-in-OpenType glyphs).
*/
void ScheduleReflowSVGNonDisplayText();
/**
* Updates the mFontSizeScaleFactor value by looking at the range of
* font-sizes used within the <text>.
*
* @return Whether mFontSizeScaleFactor changed.
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -911,13 +911,14 @@ pref("identity.fxaccounts.remote.profile
pref("identity.fxaccounts.remote.oauth.uri", "https://oauth.accounts.firefox.com/v1");
// Token server used by Firefox Account-authenticated Sync.
pref("identity.sync.tokenserver.uri", "https://token.services.mozilla.com/1.0/sync/1.5");
// Enable Presentation API
pref("dom.presentation.enabled", true);
pref("dom.presentation.discovery.enabled", true);
+pref("dom.presentation.discovery.legacy.enabled", true); // for TV 2.5 backward capability
pref("dom.audiochannel.audioCompeting", true);
pref("dom.audiochannel.mediaControl", true);
pref("media.openUnsupportedTypeWithExternalApp", true);
--- a/mobile/android/base/java/org/mozilla/gecko/toolbar/ToolbarDisplayLayout.java
+++ b/mobile/android/base/java/org/mozilla/gecko/toolbar/ToolbarDisplayLayout.java
@@ -273,16 +273,21 @@ public class ToolbarDisplayLayout extend
String strippedURL = stripAboutReaderURL(url);
final boolean isHttpOrHttps = StringUtils.isHttpOrHttps(strippedURL);
if (mPrefs.shouldTrimUrls()) {
strippedURL = StringUtils.stripCommonSubdomains(StringUtils.stripScheme(strippedURL));
}
+ // The URL bar does not support RTL currently (See bug 928688 and meta bug 702845).
+ // Displaying a URL using RTL (or mixed) characters can lead to an undesired reordering
+ // of elements of the URL. That's why we are forcing the URL to use LTR (bug 1284372).
+ strippedURL = StringUtils.forceLTR(strippedURL);
+
// This value is not visible to screen readers but we rely on it when running UI tests. Screen
// readers will instead focus BrowserToolbar and read the "base domain" from there. UI tests
// will read the content description to obtain the full URL for performing assertions.
setContentDescription(strippedURL);
final SiteIdentity siteIdentity = tab.getSiteIdentity();
if (siteIdentity.hasOwner() && SwitchBoard.isInExperiment(mActivity, Experiments.URLBAR_SHOW_EV_CERT_OWNER)) {
// Show Owner of EV certificate as title
--- a/mobile/android/base/java/org/mozilla/gecko/util/StringUtils.java
+++ b/mobile/android/base/java/org/mozilla/gecko/util/StringUtils.java
@@ -270,9 +270,37 @@ public class StringUtils {
return Collections.unmodifiableSet(names);
}
public static String safeSubstring(@NonNull final String str, final int start, final int end) {
return str.substring(
Math.max(0, start),
Math.min(end, str.length()));
}
+
+ /**
+ * Check if this might be a RTL (right-to-left) text by looking at the first character.
+ */
+ public static boolean isRTL(String text) {
+ if (TextUtils.isEmpty(text)) {
+ return false;
+ }
+
+ final char character = text.charAt(0);
+ final byte directionality = Character.getDirectionality(character);
+
+ return directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT
+ || directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC
+ || directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING
+ || directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE;
+ }
+
+ /**
+ * Force LTR (left-to-right) by prepending the text with the "left-to-right mark" (U+200E) if needed.
+ */
+ public static String forceLTR(String text) {
+ if (!isRTL(text)) {
+ return text;
+ }
+
+ return "\u200E" + text;
+ }
}
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -410,16 +410,18 @@
@BINPATH@/components/PresentationDeviceInfoManager.manifest
@BINPATH@/components/PresentationDeviceInfoManager.js
@BINPATH@/components/BuiltinProviders.manifest
@BINPATH@/components/PresentationControlService.js
@BINPATH@/components/PresentationNetworkHelper.js
@BINPATH@/components/PresentationNetworkHelper.manifest
@BINPATH@/components/PresentationDataChannelSessionTransport.js
@BINPATH@/components/PresentationDataChannelSessionTransport.manifest
+@BINPATH@/components/LegacyProviders.manifest
+@BINPATH@/components/LegacyPresentationControlService.js
@BINPATH@/components/PACGenerator.js
@BINPATH@/components/PACGenerator.manifest
@BINPATH@/components/TVSimulatorService.js
@BINPATH@/components/TVSimulatorService.manifest
; Modules
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/util/TestStringUtils.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/util/TestStringUtils.java
@@ -7,36 +7,38 @@ package org.mozilla.gecko.util;
import junit.framework.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
@RunWith(TestRunner.class)
public class TestStringUtils {
@Test
public void testIsHttpOrHttps() {
// No value
Assert.assertFalse(StringUtils.isHttpOrHttps(null));
Assert.assertFalse(StringUtils.isHttpOrHttps(""));
// Garbage
Assert.assertFalse(StringUtils.isHttpOrHttps("lksdjflasuf"));
// URLs with http/https
- Assert.assertTrue(StringUtils.isHttpOrHttps("https://www.google.com"));
- Assert.assertTrue(StringUtils.isHttpOrHttps("http://www.facebook.com"));
- Assert.assertTrue(StringUtils.isHttpOrHttps("https://mozilla.org/en-US/firefox/products/"));
+ assertTrue(StringUtils.isHttpOrHttps("https://www.google.com"));
+ assertTrue(StringUtils.isHttpOrHttps("http://www.facebook.com"));
+ assertTrue(StringUtils.isHttpOrHttps("https://mozilla.org/en-US/firefox/products/"));
// IP addresses
- Assert.assertTrue(StringUtils.isHttpOrHttps("https://192.168.0.1"));
- Assert.assertTrue(StringUtils.isHttpOrHttps("http://63.245.215.20/en-US/firefox/products"));
+ assertTrue(StringUtils.isHttpOrHttps("https://192.168.0.1"));
+ assertTrue(StringUtils.isHttpOrHttps("http://63.245.215.20/en-US/firefox/products"));
// Other protocols
Assert.assertFalse(StringUtils.isHttpOrHttps("ftp://people.mozilla.org"));
Assert.assertFalse(StringUtils.isHttpOrHttps("javascript:window.google.com"));
Assert.assertFalse(StringUtils.isHttpOrHttps("tel://1234567890"));
// No scheme
Assert.assertFalse(StringUtils.isHttpOrHttps("google.com"));
@@ -48,9 +50,40 @@ public class TestStringUtils {
assertEquals(StringUtils.stripRef(null), null);
assertEquals(StringUtils.stripRef(""), "");
assertEquals(StringUtils.stripRef("??AAABBBCCC"), "??AAABBBCCC");
assertEquals(StringUtils.stripRef("https://mozilla.org"), "https://mozilla.org");
assertEquals(StringUtils.stripRef("https://mozilla.org#BBBB"), "https://mozilla.org");
assertEquals(StringUtils.stripRef("https://mozilla.org/#BBBB"), "https://mozilla.org/");
}
+
+ @Test
+ public void testIsRTL() {
+ assertFalse(StringUtils.isRTL("mozilla.org"));
+ assertFalse(StringUtils.isRTL("something.عربي"));
+
+ assertTrue(StringUtils.isRTL("عربي"));
+ assertTrue(StringUtils.isRTL("عربي.org"));
+
+ // Text with LTR mark
+ assertFalse(StringUtils.isRTL("\u200EHello"));
+ assertFalse(StringUtils.isRTL("\u200Eعربي"));
+ }
+
+ @Test
+ public void testForceLTR() {
+ assertFalse(StringUtils.isRTL(StringUtils.forceLTR("عربي")));
+ assertFalse(StringUtils.isRTL(StringUtils.forceLTR("عربي.org")));
+
+ // Strings that are already LTR are not modified
+ final String someLtrString = "HelloWorld";
+ assertEquals(someLtrString, StringUtils.forceLTR(someLtrString));
+
+ // We add the LTR mark only once
+ final String someRtlString = "عربي";
+ assertEquals(4, someRtlString.length());
+ final String forcedLtrString = StringUtils.forceLTR(someRtlString);
+ assertEquals(5, forcedLtrString.length());
+ final String forcedAgainLtrString = StringUtils.forceLTR(forcedLtrString);
+ assertEquals(5, forcedAgainLtrString.length());
+ }
}
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1551,17 +1551,17 @@ pref("network.http.enable-packaged-apps"
// Enable this to bring in the signature verification if the signature exists.
// Set to false if you don't need the signed packaged web app support (i.e. NSec).
pref("network.http.signed-packages.enabled", false);
// If it is set to false, headers with empty value will not appear in the header
// array - behavior as it used to be. If it is true: empty headers coming from
// the network will exist in header array as empty string. Call SetHeader with
// an empty value will still delete the header.(Bug 6699259)
-pref("network.http.keep_empty_response_headers_as_empty_string", false);
+pref("network.http.keep_empty_response_headers_as_empty_string", true);
// default values for FTP
// in a DSCP environment this should be 40 (0x28, or AF11), per RFC-4594,
// Section 4.8 "High-Throughput Data Service Class", and 80 (0x50, or AF22)
// per Section 4.7 "Low-Latency Data Service Class".
pref("network.ftp.data.qos", 0);
pref("network.ftp.control.qos", 0);
@@ -5238,16 +5238,17 @@ pref("dom.udpsocket.enabled", false);
// Disable before keyboard events and after keyboard events by default.
pref("dom.beforeAfterKeyboardEvent.enabled", false);
// Presentation API
pref("dom.presentation.enabled", false);
pref("dom.presentation.tcp_server.debug", false);
pref("dom.presentation.discovery.enabled", false);
+pref("dom.presentation.discovery.legacy.enabled", false);
pref("dom.presentation.discovery.timeout_ms", 10000);
pref("dom.presentation.discoverable", false);
pref("dom.presentation.session_transport.data_channel.enable", false);
#ifdef XP_MACOSX
#if !defined(RELEASE_BUILD) || defined(DEBUG)
// In non-release builds we crash by default on insecure text input (when a
// password editor has focus but secure event input isn't enabled). The
--- a/security/sandbox/linux/SandboxFilter.cpp
+++ b/security/sandbox/linux/SandboxFilter.cpp
@@ -182,16 +182,17 @@ public:
// Metadata of opened files
CASES_FOR_fstat:
return Allow();
// Simple I/O
case __NR_write:
case __NR_read:
+ case __NR_readv:
case __NR_writev: // see SandboxLogging.cpp
CASES_FOR_lseek:
return Allow();
// Memory mapping
CASES_FOR_mmap:
case __NR_munmap:
return Allow();
@@ -433,16 +434,17 @@ public:
case SYS_SOCKET:
return Some(Error(EACCES));
#else // #ifdef DESKTOP
case SYS_RECV:
case SYS_SEND:
case SYS_SOCKET: // DANGEROUS
case SYS_CONNECT: // DANGEROUS
case SYS_ACCEPT:
+ case SYS_ACCEPT4:
case SYS_BIND:
case SYS_LISTEN:
case SYS_GETSOCKOPT:
case SYS_SETSOCKOPT:
case SYS_GETSOCKNAME:
case SYS_GETPEERNAME:
case SYS_SHUTDOWN:
return Some(Allow());
@@ -512,16 +514,17 @@ public:
case __NR_getcwd:
CASES_FOR_statfs:
CASES_FOR_fstatfs:
case __NR_chmod:
case __NR_rename:
case __NR_symlink:
case __NR_quotactl:
case __NR_utimes:
+ case __NR_link:
case __NR_unlink:
case __NR_fchown:
case __NR_fchmod:
#endif
return Allow();
case __NR_readlink:
case __NR_readlinkat:
@@ -639,16 +642,19 @@ public:
}
#endif
#ifdef __NR_semget
case __NR_semget:
return Allow();
#endif
+ case __NR_mlock:
+ return Allow();
+
#endif // DESKTOP
#ifdef __NR_getrandom
case __NR_getrandom:
return Allow();
#endif
// nsSystemInfo uses uname (and we cache an instance, so
deleted file mode 100644
--- a/testing/web-platform/meta/XMLHttpRequest/getresponseheader-cookies-and-more.htm.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[getresponseheader-cookies-and-more.htm]
- type: testharness
- [XMLHttpRequest: getResponseHeader() custom/non-existent headers and cookies]
- expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/cors/response-headers.htm.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[response-headers.htm]
- type: testharness
- [getResponseHeader: Combined testing of cors response headers]
- expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/custom-elements/v0/creating-and-passing-registries/share-registry-create-document.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[share-registry-create-document.html]
- type: testharness
- [Document created by createDocument with HTML namespace should share an existing registry]
- expected: FAIL
-
--- a/testing/web-platform/meta/dom/nodes/DOMImplementation-createDocument.html.ini
+++ b/testing/web-platform/meta/dom/nodes/DOMImplementation-createDocument.html.ini
@@ -4,20 +4,14 @@
expected: FAIL
[createDocument test 172: metadata for null,null,DocumentType node]
expected: FAIL
[createDocument test 172: null,null,DocumentType node,null]
expected: FAIL
- [createDocument test 179: metadata for "http://www.w3.org/1999/xhtml","",null]
- expected: FAIL
-
- [createDocument test 180: metadata for "http://www.w3.org/2000/svg","",null]
- expected: FAIL
-
[createDocument test 185: null,"",DocumentType node]
expected: FAIL
[createDocument test 186: null,"",DocumentType node]
expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/dom/nodes/Document-contentType/contentType/createDocument.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[createDocument.html]
- type: testharness
- [document.implementation.createDocument: document.contentType === 'application/xhtml+xml']
- expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/dom/nodes/Document-createElement-namespace.html.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[Document-createElement-namespace.html]
- type: testharness
- [Created element's namespace in created XHTML document]
- expected: FAIL
-
- [Created element's namespace in created SVG document]
- expected: FAIL
-
--- a/testing/web-platform/meta/html/infrastructure/urls/resolving-urls/query-encoding/utf-16be.html.ini
+++ b/testing/web-platform/meta/html/infrastructure/urls/resolving-urls/query-encoding/utf-16be.html.ini
@@ -7,19 +7,16 @@
expected: TIMEOUT
[hyperlink auditing <area ping>]
expected: TIMEOUT
[loading image <video poster>]
disabled: true
- [loading webvtt <track>]
- expected: TIMEOUT
-
[history.pushState]
expected: FAIL
[history.replaceState]
expected: FAIL
[SharedWorker() in a dedicated worker]
expected: FAIL
--- a/testing/web-platform/meta/html/infrastructure/urls/resolving-urls/query-encoding/utf-16le.html.ini
+++ b/testing/web-platform/meta/html/infrastructure/urls/resolving-urls/query-encoding/utf-16le.html.ini
@@ -7,19 +7,16 @@
expected: TIMEOUT
[hyperlink auditing <area ping>]
expected: TIMEOUT
[loading image <video poster>]
disabled: true
- [loading webvtt <track>]
- expected: TIMEOUT
-
[history.pushState]
expected: FAIL
[history.replaceState]
expected: FAIL
[SharedWorker() in a dedicated worker]
expected: FAIL
--- a/testing/web-platform/meta/html/infrastructure/urls/resolving-urls/query-encoding/utf-8.html.ini
+++ b/testing/web-platform/meta/html/infrastructure/urls/resolving-urls/query-encoding/utf-8.html.ini
@@ -7,19 +7,16 @@
expected: TIMEOUT
[hyperlink auditing <area ping>]
expected: TIMEOUT
[loading image <video poster>]
disabled: true
- [loading webvtt <track>]
- expected: TIMEOUT
-
[history.pushState]
expected: FAIL
[history.replaceState]
expected: FAIL
[SharedWorker() in a dedicated worker]
expected: FAIL
--- a/testing/web-platform/meta/html/infrastructure/urls/resolving-urls/query-encoding/windows-1251.html.ini
+++ b/testing/web-platform/meta/html/infrastructure/urls/resolving-urls/query-encoding/windows-1251.html.ini
@@ -134,17 +134,17 @@
[loading video <audio>]
expected: FAIL
[loading video <audio><source>]
expected: FAIL
[loading webvtt <track>]
- expected: TIMEOUT
+ expected: FAIL
[submit form <form action>]
expected: FAIL
[submit form <input formaction>]
expected: FAIL
[submit form <button formaction>]
--- a/testing/web-platform/meta/html/infrastructure/urls/resolving-urls/query-encoding/windows-1252.html.ini
+++ b/testing/web-platform/meta/html/infrastructure/urls/resolving-urls/query-encoding/windows-1252.html.ini
@@ -4,19 +4,16 @@
disabled:
if os == "mac": https://bugzilla.mozilla.org/show_bug.cgi?id=1034063
[hyperlink auditing <a ping>]
expected: TIMEOUT
[hyperlink auditing <area ping>]
expected: TIMEOUT
- [loading webvtt <track>]
- expected: TIMEOUT
-
[EventSource constructor]
expected: FAIL
[EventSource#url]
expected: FAIL
[window.open()]
expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/interfaces/TextTrack/cues.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[cues.html]
- type: testharness
- [TextTrack.cues, default attribute]
- expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/getter.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[getter.html]
- type: testharness
- [TextTrackList getter]
- expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/interfaces/TextTrackList/length.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[length.html]
- type: testharness
- [TextTrackList.length]
- expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/cloneNode.html.ini
+++ /dev/null
@@ -1,12 +0,0 @@
-[cloneNode.html]
- type: testharness
- expected: TIMEOUT
- [track element cloneNode, loading]
- expected: FAIL
-
- [track element cloneNode, loaded]
- expected: TIMEOUT
-
- [track element cloneNode, failed to load]
- expected: TIMEOUT
-
deleted file mode 100644
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-template-element/additions-to-serializing-xhtml-documents/outerhtml.html.ini
+++ /dev/null
@@ -1,11 +0,0 @@
-[outerhtml.html]
- type: testharness
- [Template contents should be serialized instead of template element if serializing template element]
- expected: FAIL
-
- [Template contents should be serialized instead of template element if serializing template element. Test nested template]
- expected: FAIL
-
- [Template contents should be serialized instead of template element if serializing template element. Test serializing whole document]
- expected: FAIL
-
--- a/testing/web-platform/tests/content-security-policy/blink-contrib/connect-src-beacon-blocked.sub.html
+++ b/testing/web-platform/tests/content-security-policy/blink-contrib/connect-src-beacon-blocked.sub.html
@@ -17,19 +17,19 @@ connect-src 'self' http://{{host}}:{{por
<script>
if (typeof navigator.sendBeacon != 'function') {
t_log.set_status(t_log.NOTRUN, "No navigator.sendBeacon, cannot run test.");
t_log.phase = t_log.phases.HAS_RESULT;
t_log.done();
} else {
try {
var es = navigator.sendBeacon("http://www1.{{host}}:{{ports[http][0]}}/security/contentSecurityPolicy/echo-report.php");
- log("Fail");
+ log("Pass");
} catch (e) {
- log("Pass");
+ log("Fail");
}
var report = document.createElement("script");
report.src = "../support/checkReport.sub.js?reportExists=true&reportField=violated-directive&reportValue=connect-src%20'self'";
report.async = true;
report.defer = true;
document.body.appendChild(report);
}
rename from browser/components/contextualidentity/ContextualIdentityService.jsm
rename to toolkit/components/contextualidentity/ContextualIdentityService.jsm
new file mode 100644
--- /dev/null
+++ b/toolkit/components/contextualidentity/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXTRA_JS_MODULES += [
+ 'ContextualIdentityService.jsm',
+]
--- a/toolkit/components/moz.build
+++ b/toolkit/components/moz.build
@@ -14,16 +14,17 @@ DIRS += [
'aboutmemory',
'aboutperformance',
'addoncompat',
'alerts',
'apppicker',
'asyncshutdown',
'commandlines',
'contentprefs',
+ 'contextualidentity',
'cookie',
'crashmonitor',
'diskspacewatcher',
'downloads',
'extensions',
'exthelper',
'filepicker',
'filewatcher',
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -613,24 +613,16 @@
},
"JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS": {
"alert_emails": ["jdemooij@mozilla.com"],
"expires_in_version": "never",
"kind": "enumerated",
"n_values": 10,
"description": "Use of SpiderMonkey's deprecated language extensions in add-ons: ForEach=0, DestructuringForIn=1 (obsolete), LegacyGenerator=2, ExpressionClosure=3, LetBlock=4 (obsolete), LetExpression=5 (obsolete), NoSuchMethod=6 (obsolete), FlagsArgument=7 (obsolete), RegExpSourceProp=8 (obsolete), RestoredRegExpStatics=9 (obsolete), BlockScopeFunRedecl=10"
},
- "JS_DEFINE_GETTER_SETTER_THIS_NULL_UNDEFINED": {
- "alert_emails": ["jdemooij@mozilla.com"],
- "bug_numbers": [1249123],
- "expires_in_version": "55",
- "kind": "boolean",
- "releaseChannelCollection": "opt-out",
- "description": "__defineGetter__ or __defineSetter__ invoked with 1 = null/undefined or 0 = everything else as |this| value"
- },
"XUL_CACHE_DISABLED": {
"expires_in_version": "default",
"kind": "flag",
"description": "XUL cache was disabled"
},
"MEMORY_RESIDENT_FAST": {
"alert_emails": ["memshrink-telemetry-alerts@mozilla.com"],
"expires_in_version": "never",
--- a/toolkit/components/thumbnails/BackgroundPageThumbs.jsm
+++ b/toolkit/components/thumbnails/BackgroundPageThumbs.jsm
@@ -31,17 +31,17 @@ const TEL_CAPTURE_DONE_BAD_URI = 5;
// These are looked up on the global as properties below.
XPCOMUtils.defineConstant(this, "TEL_CAPTURE_DONE_OK", TEL_CAPTURE_DONE_OK);
XPCOMUtils.defineConstant(this, "TEL_CAPTURE_DONE_TIMEOUT", TEL_CAPTURE_DONE_TIMEOUT);
XPCOMUtils.defineConstant(this, "TEL_CAPTURE_DONE_CRASHED", TEL_CAPTURE_DONE_CRASHED);
XPCOMUtils.defineConstant(this, "TEL_CAPTURE_DONE_BAD_URI", TEL_CAPTURE_DONE_BAD_URI);
XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
- "resource:///modules/ContextualIdentityService.jsm");
+ "resource://gre/modules/ContextualIdentityService.jsm");
const global = this;
const BackgroundPageThumbs = {
/**
* Asynchronously captures a thumbnail of the given URL.
*
* The page is loaded anonymously, and plug-ins are disabled.
--- a/toolkit/forgetaboutsite/ForgetAboutSite.jsm
+++ b/toolkit/forgetaboutsite/ForgetAboutSite.jsm
@@ -8,17 +8,17 @@ Components.utils.import("resource://gre/
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/NetUtil.jsm");
Components.utils.import("resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
"resource://gre/modules/Downloads.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
- "resource:///modules/ContextualIdentityService.jsm");
+ "resource://gre/modules/ContextualIdentityService.jsm");
this.EXPORTED_SYMBOLS = ["ForgetAboutSite"];
/**
* Returns true if the string passed in is part of the root domain of the
* current string. For example, if this is "www.mozilla.org", and we pass in
* "mozilla.org", this will return true. It would return false the other way
* around.
--- a/toolkit/forgetaboutsite/test/unit/xpcshell.ini
+++ b/toolkit/forgetaboutsite/test/unit/xpcshell.ini
@@ -1,9 +1,8 @@
[DEFAULT]
head = head_forgetaboutsite.js ../../../../dom/push/test/xpcshell/head.js
tail =
-firefox-appdir = browser
skip-if = toolkit == 'android' || toolkit == 'gonk'
support-files =
!/dom/push/test/xpcshell/head.js
[test_removeDataFromDomain.js]
--- a/widget/gtk/nsNativeThemeGTK.cpp
+++ b/widget/gtk/nsNativeThemeGTK.cpp
@@ -302,16 +302,18 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u
// the slider to the actual scrollbar object
nsIFrame *tmpFrame = aFrame->GetParent()->GetParent();
aState->curpos = CheckIntAttr(tmpFrame, nsGkAtoms::curpos, 0);
aState->maxpos = CheckIntAttr(tmpFrame, nsGkAtoms::maxpos, 100);
if (CheckBooleanAttr(aFrame, nsGkAtoms::active)) {
aState->active = TRUE;
+ // Set hover state to emulate Gtk style of active scrollbar thumb
+ aState->inHover = TRUE;
}
}
if (aWidgetType == NS_THEME_SCROLLBARBUTTON_UP ||
aWidgetType == NS_THEME_SCROLLBARBUTTON_DOWN ||
aWidgetType == NS_THEME_SCROLLBARBUTTON_LEFT ||
aWidgetType == NS_THEME_SCROLLBARBUTTON_RIGHT) {
// set the state to disabled when the scrollbar is scrolled to
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -1346,41 +1346,45 @@ SetUserTimeAndStartupIDForActivatedWindo
if (timestamp) {
gdk_window_focus(gtk_widget_get_window(aWindow), timestamp);
GTKToolkit->SetFocusTimestamp(0);
}
return;
}
#if defined(MOZ_ENABLE_STARTUP_NOTIFICATION)
- GdkWindow* gdkWindow = gtk_widget_get_window(aWindow);
-
- GdkScreen* screen = gdk_window_get_screen(gdkWindow);
- SnDisplay* snd =
- sn_display_new(gdk_x11_display_get_xdisplay(gdk_window_get_display(gdkWindow)),
- nullptr, nullptr);
- if (!snd)
- return;
- SnLauncheeContext* ctx =
- sn_launchee_context_new(snd, gdk_screen_get_number(screen),
- desktopStartupID.get());
- if (!ctx) {
+ // TODO - Implement for non-X11 Gtk backends (Bug 726479)
+ if (mIsX11Display) {
+ GdkWindow* gdkWindow = gtk_widget_get_window(aWindow);
+
+ GdkScreen* screen = gdk_window_get_screen(gdkWindow);
+ SnDisplay* snd =
+ sn_display_new(gdk_x11_display_get_xdisplay(gdk_window_get_display(gdkWindow)),
+ nullptr, nullptr);
+ if (!snd)
+ return;
+ SnLauncheeContext* ctx =
+ sn_launchee_context_new(snd, gdk_screen_get_number(screen),
+ desktopStartupID.get());
+ if (!ctx) {
+ sn_display_unref(snd);
+ return;
+ }
+
+ if (sn_launchee_context_get_id_has_timestamp(ctx)) {
+ gdk_x11_window_set_user_time(gdkWindow,
+ sn_launchee_context_get_timestamp(ctx));
+ }
+
+ sn_launchee_context_setup_window(ctx, gdk_x11_window_get_xid(gdkWindow));
+ sn_launchee_context_complete(ctx);
+
+ sn_launchee_context_unref(ctx);
sn_display_unref(snd);
- return;
- }
-
- if (sn_launchee_context_get_id_has_timestamp(ctx)) {
- gdk_x11_window_set_user_time(gdkWindow, sn_launchee_context_get_timestamp(ctx));
- }
-
- sn_launchee_context_setup_window(ctx, gdk_x11_window_get_xid(gdkWindow));
- sn_launchee_context_complete(ctx);
-
- sn_launchee_context_unref(ctx);
- sn_display_unref(snd);
+ }
#endif
// If we used the startup ID, that already contains the focus timestamp;
// we don't want to reuse the timestamp next time we raise the window
GTKToolkit->SetFocusTimestamp(0);
GTKToolkit->SetDesktopStartupID(EmptyCString());
}