Merge inbound to m-c. a=merge

This commit is contained in:
Ryan VanderMeulen 2016-03-05 19:12:44 -05:00
commit cbdddb04cd
490 changed files with 11232 additions and 5462 deletions

View File

@ -169,8 +169,12 @@ LogDocState(nsIDocument* aDocumentNode)
printf(", %svisible considering ancestors", aDocumentNode->IsVisibleConsideringAncestors() ? "" : "not ");
printf(", %sactive", aDocumentNode->IsActive() ? "" : "not ");
printf(", %sresource", aDocumentNode->IsResourceDoc() ? "" : "not ");
printf(", has %srole content",
nsCoreUtils::GetRoleContent(aDocumentNode) ? "" : "no ");
dom::Element* rootEl = aDocumentNode->GetBodyElement();
if (!rootEl) {
rootEl = aDocumentNode->GetRootElement();
}
printf(", has %srole content", rootEl ? "" : "no ");
}
static void

View File

@ -223,28 +223,6 @@ nsCoreUtils::GetDOMNodeFromDOMPoint(nsINode *aNode, uint32_t aOffset)
return aNode;
}
dom::Element*
nsCoreUtils::GetRoleContent(nsINode *aNode)
{
nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
if (!content) {
nsCOMPtr<nsIDocument> doc(do_QueryInterface(aNode));
if (doc) {
nsCOMPtr<nsIDOMHTMLDocument> htmlDoc(do_QueryInterface(aNode));
if (htmlDoc) {
nsCOMPtr<nsIDOMHTMLElement> bodyElement;
htmlDoc->GetBody(getter_AddRefs(bodyElement));
content = do_QueryInterface(bodyElement);
}
else {
return doc->GetDocumentElement();
}
}
}
return (content && content->IsElement()) ? content->AsElement() : nullptr;
}
bool
nsCoreUtils::IsAncestorOf(nsINode *aPossibleAncestorNode,
nsINode *aPossibleDescendantNode,

View File

@ -108,17 +108,6 @@ public:
*/
static nsINode *GetDOMNodeFromDOMPoint(nsINode *aNode, uint32_t aOffset);
/**
* Return the nsIContent* to check for ARIA attributes on -- this may not
* always be the DOM node for the accessible. Specifically, for doc
* accessibles, it is not the document node, but either the root element or
* <body> in HTML.
*
* @param aNode [in] DOM node for the accessible that may be affected by ARIA
* @return the nsIContent which may have ARIA markup
*/
static mozilla::dom::Element* GetRoleContent(nsINode *aNode);
/**
* Is the first passed in node an ancestor of the second?
* Note: A node is not considered to be the ancestor of itself.

View File

@ -81,6 +81,7 @@
#include "mozilla/dom/CanvasRenderingContext2D.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/dom/HTMLBodyElement.h"
#include "mozilla/dom/TreeWalker.h"
using namespace mozilla;
@ -1001,7 +1002,7 @@ Accessible::NativeAttributes()
break;
nsAccUtils::SetLiveContainerAttributes(attributes, startContent,
nsCoreUtils::GetRoleContent(doc));
doc->GetRootElement());
// Allow ARIA live region markup from outer documents to override
nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = doc->GetDocShell();

View File

@ -13,6 +13,7 @@
#include "NotificationController.h"
#include "States.h"
#include "nsIScrollableFrame.h"
#include "nsIDocumentInlines.h"
#ifdef A11Y_LOG
#include "Logging.h"
@ -77,6 +78,19 @@ DocAccessible::UpdateText(nsIContent* aTextNode)
mNotificationController->ScheduleTextUpdate(aTextNode);
}
inline void
DocAccessible::UpdateRootElIfNeeded()
{
dom::Element* rootEl = mDocumentNode->GetBodyElement();
if (!rootEl) {
rootEl = mDocumentNode->GetRootElement();
}
if (rootEl != mContent) {
mContent = rootEl;
SetRoleMapEntry(aria::GetRoleMap(rootEl));
}
}
inline void
DocAccessible::AddScrollListener()
{

View File

@ -1460,14 +1460,8 @@ DocAccessible::DoInitialUpdate()
mLoadState |= eTreeConstructed;
// The content element may be changed before the initial update and then we
// miss the notification (since content tree change notifications are ignored
// prior to initial update). Make sure the content element is valid.
dom::Element* rootEl = nsCoreUtils::GetRoleContent(mDocumentNode);
if (rootEl) {
mContent = rootEl;
SetRoleMapEntry(aria::GetRoleMap(rootEl));
}
// Set up a root element and ARIA role mapping.
UpdateRootElIfNeeded();
// Build initial tree. Since its the initial tree there's no group info to
// invalidate.
@ -1700,11 +1694,7 @@ DocAccessible::ProcessContentInserted(Accessible* aContainer,
if (container == this) {
// If new root content has been inserted then update it.
dom::Element* rootEl = nsCoreUtils::GetRoleContent(mDocumentNode);
if (rootEl != mContent) {
mContent = rootEl;
SetRoleMapEntry(aria::GetRoleMap(rootEl));
}
UpdateRootElIfNeeded();
// Continue to update the tree even if we don't have root content.
// For example, elements may be inserted under the document element while

View File

@ -376,6 +376,11 @@ protected:
*/
virtual void DoInitialUpdate();
/**
* Updates root element and picks up ARIA role on it if any.
*/
void UpdateRootElIfNeeded();
/**
* Process document load notification, fire document load and state busy
* events if applicable.

View File

@ -408,15 +408,23 @@ HyperTextAccessible::OffsetToDOMPoint(int32_t aOffset)
Accessible* child = GetChildAt(childIdx);
int32_t innerOffset = aOffset - GetChildOffset(childIdx);
// A text leaf case. The point is inside the text node.
// A text leaf case.
if (child->IsTextLeaf()) {
nsIContent* content = child->GetContent();
int32_t idx = 0;
if (NS_FAILED(RenderedToContentOffset(content->GetPrimaryFrame(),
innerOffset, &idx)))
return DOMPoint();
// The point is inside the text node. This is always true for any text leaf
// except a last child one. See assertion below.
if (aOffset < GetChildOffset(childIdx + 1)) {
nsIContent* content = child->GetContent();
int32_t idx = 0;
if (NS_FAILED(RenderedToContentOffset(content->GetPrimaryFrame(),
innerOffset, &idx)))
return DOMPoint();
return DOMPoint(content, idx);
return DOMPoint(content, idx);
}
// Set the DOM point right after the text node.
MOZ_ASSERT(static_cast<uint32_t>(aOffset) == CharacterCount());
innerOffset = 1;
}
// Case of embedded object. The point is either before or after the element.

View File

@ -1025,6 +1025,7 @@ pref("apz.fling_curve_threshold_inches_per_ms", "0.01");
pref("apz.fling_friction", "0.0019");
pref("apz.max_velocity_inches_per_ms", "0.07");
pref("apz.overscroll.enabled", true);
pref("apz.displayport_expiry_ms", 0); // causes issues on B2G, see bug 1250924
// For event-regions based hit-testing
pref("layout.event-regions.enabled", true);

View File

@ -140,12 +140,14 @@ var PositionHandler = {
let primaryScreen = Cc["@mozilla.org/gfx/screenmanager;1"]
.getService(Ci.nsIScreenManager)
.primaryScreen;
let width = {};
primaryScreen.GetRectDisplayPix({}, {}, width, {});
let availTop = {};
primaryScreen.GetAvailRectDisplayPix({}, availTop, {}, {});
window.moveTo((width.value - document.documentElement.clientWidth) / 2,
availTop.value);
let widthDevPix = {};
primaryScreen.GetRect({}, {}, widthDevPix, {});
let availTopDevPix = {};
primaryScreen.GetAvailRect({}, availTopDevPix, {}, {});
let scaleFactor = primaryScreen.defaultCSSScaleFactor;
let widthCss = widthDevPix.value / scaleFactor;
window.moveTo((widthCss - document.documentElement.clientWidth) / 2,
availTopDevPix.value / scaleFactor);
} else {
// This will ensure we're at y=0.
this.setXPosition(window.screenX);

View File

@ -115,6 +115,10 @@ extensions.registerSchemaAPI("bookmarks", "bookmarks", (extension, context) => {
return Bookmarks.search(query).then(result => result.map(convert));
},
getRecent: function(numberOfItems) {
return Bookmarks.getRecent(numberOfItems).then(result => result.map(convert));
},
create: function(bookmark) {
let info = {
title: bookmark.title || "",

View File

@ -165,7 +165,6 @@
},
{
"name": "getRecent",
"unsupported": true,
"type": "function",
"description": "Retrieves the recently added bookmarks.",
"async": "callback",

View File

@ -95,8 +95,8 @@ var gMainPane = {
let preffedOn = e10sPref.value || e10sTempPref.value || e10sForceEnable.value;
if (preffedOn) {
// The checkbox is checked if e10s is preffed on.
e10sCheckbox.checked = true;
// The checkbox is checked if e10s is preffed on and enabled.
e10sCheckbox.checked = Services.appinfo.browserTabsRemoteAutostart;
// but if it's force disabled, then the checkbox is disabled.
e10sCheckbox.disabled = !Services.appinfo.browserTabsRemoteAutostart;

View File

@ -375,8 +375,12 @@ def bootstrap(topsrcdir, mozilla_dir=None):
continue
with open(path, 'r') as f:
data = f.read()
r = session.post(BUILD_TELEMETRY_SERVER, data=data,
headers={'Content-Type': 'application/json'})
try:
r = session.post(BUILD_TELEMETRY_SERVER, data=data,
headers={'Content-Type': 'application/json'})
except Exception as e:
print('Exception posting to telemetry server: %s' % str(e))
break
# TODO: some of these errors are likely not recoverable, as
# written, we'll retry indefinitely
if r.status_code != 200:

View File

@ -2853,19 +2853,19 @@ private void CancelNotification()
String sPort = "";
int nEnd = 0;
int nStart = 0;
FileInputStream fis = null;
if ((sFileName == null) || (sFileName.length() == 0))
return(sRet);
Context ctx = contextWrapper.getApplicationContext();
try {
FileInputStream fis = ctx.openFileInput(sFileName);
fis = ctx.openFileInput(sFileName);
int nBytes = fis.available();
if (nBytes > 0)
{
byte [] buffer = new byte [nBytes + 1];
int nRead = fis.read(buffer, 0, nBytes);
fis.close();
ctx.deleteFile(sFileName);
if (nRead > 0)
{
@ -2898,6 +2898,19 @@ private void CancelNotification()
{
e.printStackTrace();
}
finally
{
if (fis != null)
{
try {
fis.close();
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
return(sRet);
}

View File

@ -299,7 +299,7 @@ DeveloperToolbar.prototype.createToolbar = function() {
let close = this._doc.createElement("toolbarbutton");
close.setAttribute("id", "developer-toolbar-closebutton");
close.setAttribute("class", "devtools-closebutton");
close.setAttribute("class", "close-icon");
close.setAttribute("oncommand", "DeveloperToolbar.hide();");
close.setAttribute("tooltiptext", "developerToolbarCloseButton.tooltiptext");
@ -333,10 +333,15 @@ DeveloperToolbar.prototype.createToolbar = function() {
toolbar.appendChild(close);
}
let bottomBox = this._doc.getElementById("browser-bottombox");
this._element = toolbar;
bottomBox.appendChild(this._element);
let bottomBox = this._doc.getElementById("browser-bottombox");
if (bottomBox) {
bottomBox.appendChild(this._element);
} else { // SeaMonkey does not have a "browser-bottombox".
let statusBar = this._doc.getElementById("status-bar");
if (statusBar)
statusBar.parentNode.insertBefore(this._element, statusBar);
}
this._errorCounterButton = toolboxBtn
this._errorCounterButton._defaultTooltipText =
this._errorCounterButton.getAttribute("tooltiptext");

View File

@ -9,32 +9,39 @@
#jit-optimizations-view {
width: 350px;
overflow-x: auto;
min-width: 200px;
white-space: nowrap;
--jit-tree-row-height: 14;
--jit-tree-header-height: 16;
}
#jit-optimizations-view > div {
flex: 1;
}
/* Override layout styles applied by minimal-xul.css */
#jit-optimizations-view div {
display: block;
}
.tree {
/**
* Flexing to fill out remaining vertical space.
*/
flex: 1;
overflow-y: auto;
height: 100%;
background-color: var(--theme-body-background);
#jit-optimizations-view span {
display: inline-block;
}
.optimization-header {
#jit-optimizations-view > div {
/* For elements that need to flex to fill the available space and/or
* scroll on overflow, we need to use the old flexbox model, since the
* parent nodes are in the XUL namespace. The new flexbox model can't
* properly compute dimensions and will ignore `flex: ${number}` properties,
* since no other parent node has a flex display. */
display: -moz-box;
-moz-box-flex: 1;
-moz-box-orient: vertical;
}
#jit-optimizations-view .optimization-header,
#jit-optimizations-view .tree * {
/* We can, however, display child nodes as flex to take advantage of
* horizontal/vertical inlining. */
display: flex;
}
#jit-optimizations-view .optimization-header {
height: var(--jit-tree-header-height);
padding: 2px 5px;
background-color: var(--theme-tab-toolbar-background);
@ -42,32 +49,25 @@
#jit-optimizations-view .header-title {
font-weight: bold;
padding-right: 7px;
padding-inline-end: 7px;
}
.tree-node {
#jit-optimizations-view .tree {
display: -moz-box;
-moz-box-flex: 1;
-moz-box-orient: vertical;
overflow: auto;
background-color: var(--theme-body-background);
}
#jit-optimizations-view .tree-node {
height: var(--jit-tree-row-height);
clear: both;
}
.tree-node button {
#jit-optimizations-view .tree-node button {
display: none;
}
#jit-optimizations-view .optimization-tree-item {
display: flex;
}
#jit-optimizations-view .arrow,
#jit-optimizations-view .optimization-site,
#jit-optimizations-view .optimization-attempts,
#jit-optimizations-view .optimization-attempt,
#jit-optimizations-view .optimization-types,
#jit-optimizations-view .optimization-ion-type,
#jit-optimizations-view .optimization-observed-type {
float: left;
}
#jit-optimizations-view .optimization-outcome.success {
color: var(--theme-highlight-green);
}
@ -75,31 +75,29 @@
color: var(--theme-highlight-red);
}
.opt-icon::before {
content: "";
.theme-dark .opt-icon::before {
background-image: url(chrome://devtools/skin/images/webconsole.svg);
}
.theme-light .opt-icon::before {
background-image: url(chrome://devtools/skin/images/webconsole.svg#light-icons);
}
.opt-icon::before {
display: inline-block;
content: "";
background-repeat: no-repeat;
background-size: 72px 60px;
/* show grey "i" bubble by default */
background-position: -36px -36px;
width: 10px;
height: 10px;
display: inline-block;
max-height: 12px;
}
#jit-optimizations-view .opt-icon {
float: left;
}
#jit-optimizations-view .opt-icon::before {
.opt-icon::before {
margin: 1px 6px 0 0;
}
.theme-light .opt-icon::before {
background-image: url(chrome://devtools/skin/images/webconsole.svg#light-icons);
}
.opt-icon.warning::before {
background-position: -24px -24px;
}
@ -114,7 +112,7 @@
}
.frame-link {
margin-left: 7px;
margin-inline-start: 7px;
}
.frame-link-filename {

View File

@ -647,27 +647,6 @@ menuitem.experimental-option::before {
display: none;
}
.opt-icon::before {
content: "";
background-image: url(chrome://devtools/skin/images/webconsole.svg);
background-repeat: no-repeat;
background-size: 72px 60px;
/* show grey "i" bubble by default */
background-position: -36px -36px;
width: 10px;
height: 10px;
display: inline-block;
max-height: 12px;
}
.theme-light .opt-icon::before {
background-image: url(chrome://devtools/skin/images/webconsole.svg#light-icons);
}
.opt-icon.warning::before {
background-position: -24px -24px;
}
/* for call tree */
description.opt-icon {
margin: 0px 0px 0px 0px;

View File

@ -10570,11 +10570,39 @@ nsDocShell::DoURILoad(nsIURI* aURI,
bool isSrcdoc = !aSrcdoc.IsVoid();
// There are three cases we care about:
// * Null mScriptGlobal: shouldn't happen but does (see bug 1240246). In this
// case, we create a loadingPrincipal as for a top-level load, but we leave
// requestingNode and requestingWindow null.
// * Top-level load (GetFrameElementInternal returns null). In this case,
// requestingNode is null, but requestingWindow is our mScriptGlobal.
// TODO we want to pass null for loadingPrincipal in this case.
// * Subframe load: requestingWindow is null, but requestingNode is the frame
// element for the load. loadingPrincipal is the NodePrincipal of the frame
// element.
nsCOMPtr<nsINode> requestingNode;
nsCOMPtr<nsPIDOMWindowOuter> requestingWindow;
nsCOMPtr<nsIPrincipal> loadingPrincipal;
if (mScriptGlobal) {
requestingNode = mScriptGlobal->AsOuter()->GetFrameElementInternal();
if (!requestingNode) {
requestingNode = mScriptGlobal->GetExtantDoc();
if (requestingNode) {
// If we have a requesting node, then use that as our loadingPrincipal.
loadingPrincipal = requestingNode->NodePrincipal();
} else {
MOZ_ASSERT(aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT);
requestingWindow = mScriptGlobal->AsOuter();
}
}
if (!loadingPrincipal) {
if (mItemType != typeChrome) {
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
ssm->GetDocShellCodebasePrincipal(aURI, this, getter_AddRefs(loadingPrincipal));
} else {
// This is a top-level chrome load, use a system principal for the
// loadingPrincipal.
loadingPrincipal = nsContentUtils::GetSystemPrincipal();
}
}
@ -10605,19 +10633,19 @@ nsDocShell::DoURILoad(nsIURI* aURI,
securityFlags |= nsILoadInfo::SEC_SANDBOXED;
}
nsCOMPtr<nsILoadInfo> loadInfo =
requestingWindow ?
new LoadInfo(requestingWindow, loadingPrincipal, triggeringPrincipal,
securityFlags) :
new LoadInfo(loadingPrincipal, triggeringPrincipal, requestingNode,
securityFlags, aContentPolicyType);
if (!isSrcdoc) {
rv = NS_NewChannelInternal(getter_AddRefs(channel),
aURI,
requestingNode,
requestingNode
? requestingNode->NodePrincipal()
: triggeringPrincipal.get(),
triggeringPrincipal,
securityFlags,
aContentPolicyType,
nullptr, // loadGroup
static_cast<nsIInterfaceRequestor*>(this),
loadFlags);
loadInfo,
nullptr, // loadGroup
static_cast<nsIInterfaceRequestor*>(this),
loadFlags);
if (NS_FAILED(rv)) {
if (rv == NS_ERROR_UNKNOWN_PROTOCOL) {
@ -10652,26 +10680,13 @@ nsDocShell::DoURILoad(nsIURI* aURI,
NS_ENSURE_TRUE(vsh, NS_ERROR_FAILURE);
rv = vsh->NewSrcdocChannel(aURI, aBaseURI, aSrcdoc,
requestingNode,
requestingNode
? requestingNode->NodePrincipal()
: triggeringPrincipal.get(),
triggeringPrincipal,
securityFlags,
aContentPolicyType,
getter_AddRefs(channel));
loadInfo, getter_AddRefs(channel));
} else {
rv = NS_NewInputStreamChannelInternal(getter_AddRefs(channel),
aURI,
aSrcdoc,
NS_LITERAL_CSTRING("text/html"),
requestingNode,
requestingNode ?
requestingNode->NodePrincipal() :
triggeringPrincipal.get(),
triggeringPrincipal,
securityFlags,
aContentPolicyType,
loadInfo,
true);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIInputStreamChannel> isc = do_QueryInterface(channel);

View File

@ -0,0 +1,72 @@
/* -*- 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 "AnimationPerformanceWarning.h"
#include "nsContentUtils.h"
namespace mozilla {
bool
AnimationPerformanceWarning::ToLocalizedString(
nsXPIDLString& aLocalizedString) const
{
const char* key = nullptr;
switch (mType) {
case Type::ContentTooLarge:
{
MOZ_ASSERT(mParams && mParams->Length() == 7,
"Parameter's length should be 7 for ContentTooLarge");
MOZ_ASSERT(mParams->Length() <= kMaxParamsForLocalization,
"Parameter's length should be less than "
"kMaxParamsForLocalization");
// We can pass an array of parameters whose length is greater than 7 to
// nsContentUtils::FormatLocalizedString because
// nsTextFormatter drops those extra parameters in the end.
nsAutoString strings[kMaxParamsForLocalization];
const char16_t* charParams[kMaxParamsForLocalization];
for (size_t i = 0, n = mParams->Length(); i < n; i++) {
strings[i].AppendInt((*mParams)[i]);
charParams[i] = strings[i].get();
}
nsresult rv = nsContentUtils::FormatLocalizedString(
nsContentUtils::eLAYOUT_PROPERTIES,
"AnimationWarningContentTooLarge",
charParams,
aLocalizedString);
return NS_SUCCEEDED(rv);
}
case Type::TransformBackfaceVisibilityHidden:
key = "AnimationWarningTransformBackfaceVisibilityHidden";
break;
case Type::TransformPreserve3D:
key = "AnimationWarningTransformPreserve3D";
break;
case Type::TransformSVG:
key = "AnimationWarningTransformSVG";
break;
case Type::TransformFrameInactive:
key = "AnimationWarningTransformFrameInactive";
break;
case Type::OpacityFrameInactive:
key = "AnimationWarningOpacityFrameInactive";
break;
case Type::WithGeometricProperties:
key = "AnimationWarningWithGeometricProperties";
break;
}
nsresult rv =
nsContentUtils::GetLocalizedString(nsContentUtils::eLAYOUT_PROPERTIES,
key, aLocalizedString);
return NS_SUCCEEDED(rv);
}
} // namespace mozilla

View File

@ -0,0 +1,74 @@
/* -*- 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_dom_AnimationPerformanceWarning_h
#define mozilla_dom_AnimationPerformanceWarning_h
#include "mozilla/InitializerList.h"
class nsXPIDLString;
namespace mozilla {
// Represents the reason why we can't run the CSS property on the compositor.
struct AnimationPerformanceWarning
{
enum class Type : uint8_t {
ContentTooLarge,
TransformBackfaceVisibilityHidden,
TransformPreserve3D,
TransformSVG,
TransformFrameInactive,
OpacityFrameInactive,
WithGeometricProperties
};
explicit AnimationPerformanceWarning(Type aType)
: mType(aType) { }
AnimationPerformanceWarning(Type aType,
std::initializer_list<int32_t> aParams)
: mType(aType)
{
// FIXME: Once std::initializer_list::size() become a constexpr function,
// we should use static_assert here.
MOZ_ASSERT(aParams.size() <= kMaxParamsForLocalization,
"The length of parameters should be less than "
"kMaxParamsForLocalization");
mParams.emplace(aParams);
}
// Maximum number of parameters passed to
// nsContentUtils::FormatLocalizedString to localize warning messages.
//
// NOTE: This constexpr can't be forward declared, so if you want to use
// this variable, please include this header file directly.
// This value is the same as the limit of nsStringBundle::FormatString.
// See the implementation of nsStringBundle::FormatString.
static MOZ_CONSTEXPR_VAR uint8_t kMaxParamsForLocalization = 10;
// Indicates why this property could not be animated on the compositor.
Type mType;
// Optional parameters that may be used for localization.
Maybe<nsTArray<int32_t>> mParams;
bool ToLocalizedString(nsXPIDLString& aLocalizedString) const;
bool operator==(const AnimationPerformanceWarning& aOther) const
{
return mType == aOther.mType &&
mParams == aOther.mParams;
}
bool operator!=(const AnimationPerformanceWarning& aOther) const
{
return !(*this == aOther);
}
};
} // namespace mozilla
#endif // mozilla_dom_AnimationPerformanceWarning_h

View File

@ -10,6 +10,7 @@
#include "mozilla/dom/Element.h"
#include "mozilla/dom/KeyframeEffect.h" // For KeyframeEffectReadOnly
#include "mozilla/AnimationUtils.h"
#include "mozilla/AnimationPerformanceWarning.h"
#include "mozilla/EffectSet.h"
#include "mozilla/InitializerList.h"
#include "mozilla/LayerAnimationInfo.h"
@ -109,10 +110,14 @@ FindAnimationsForCompositor(const nsIFrame* aFrame,
continue;
}
if (effect->ShouldBlockCompositorAnimations(aFrame)) {
AnimationPerformanceWarning::Type warningType;
if (effect->ShouldBlockCompositorAnimations(aFrame,
warningType)) {
if (aMatches) {
aMatches->Clear();
}
effect->SetPerformanceWarning(
aProperty, AnimationPerformanceWarning(warningType));
return false;
}
@ -716,6 +721,22 @@ EffectCompositor::GetPresContext(Element* aElement)
return shell->GetPresContext();
}
/* static */ void
EffectCompositor::SetPerformanceWarning(
const nsIFrame *aFrame,
nsCSSProperty aProperty,
const AnimationPerformanceWarning& aWarning)
{
EffectSet* effects = EffectSet::GetEffectSet(aFrame);
if (!effects) {
return;
}
for (KeyframeEffectReadOnly* effect : *effects) {
effect->SetPerformanceWarning(aProperty, aWarning);
}
}
// ---------------------------------------------------------
//
// Nested class: AnimationStyleRuleProcessor

View File

@ -30,6 +30,7 @@ namespace mozilla {
class EffectSet;
class RestyleTracker;
enum class CSSPseudoElementType : uint8_t;
struct AnimationPerformanceWarning;
namespace dom {
class Animation;
@ -187,6 +188,13 @@ public:
static Maybe<Pair<dom::Element*, CSSPseudoElementType>>
GetAnimationElementAndPseudoForFrame(const nsIFrame* aFrame);
// Associates a performance warning with effects on |aFrame| that animates
// |aProperty|.
static void SetPerformanceWarning(
const nsIFrame* aFrame,
nsCSSProperty aProperty,
const AnimationPerformanceWarning& aWarning);
private:
~EffectCompositor() = default;

View File

@ -633,6 +633,12 @@ KeyframeEffectReadOnly::SetIsRunningOnCompositor(nsCSSProperty aProperty,
for (AnimationProperty& property : mProperties) {
if (property.mProperty == aProperty) {
property.mIsRunningOnCompositor = aIsRunning;
// We currently only set a performance warning message when animations
// cannot be run on the compositor, so if this animation is running
// on the compositor we don't need a message.
if (aIsRunning) {
property.mPerformanceWarning.reset();
}
return;
}
}
@ -1871,6 +1877,32 @@ KeyframeEffectReadOnly::GetFrames(JSContext*& aCx,
}
}
void
KeyframeEffectReadOnly::GetPropertyState(
nsTArray<AnimationPropertyState>& aStates) const
{
for (const AnimationProperty& property : mProperties) {
// Bug 1252730: We should also expose this winsInCascade as well.
if (!property.mWinsInCascade) {
continue;
}
AnimationPropertyState state;
state.mProperty.Construct(
NS_ConvertASCIItoUTF16(nsCSSProps::GetStringValue(property.mProperty)));
state.mRunningOnCompositor.Construct(property.mIsRunningOnCompositor);
nsXPIDLString localizedString;
if (property.mPerformanceWarning &&
property.mPerformanceWarning->ToLocalizedString(localizedString)) {
state.mWarning.Construct(localizedString);
}
aStates.AppendElement(state);
}
}
/* static */ const TimeDuration
KeyframeEffectReadOnly::OverflowRegionRefreshInterval()
{
@ -2069,42 +2101,29 @@ KeyframeEffectReadOnly::IsGeometricProperty(
/* static */ bool
KeyframeEffectReadOnly::CanAnimateTransformOnCompositor(
const nsIFrame* aFrame,
const nsIContent* aContent)
AnimationPerformanceWarning::Type& aPerformanceWarning)
{
// Disallow OMTA for preserve-3d transform. Note that we check the style property
// rather than Extend3DContext() since that can recurse back into this function
// via HasOpacity().
// via HasOpacity(). See bug 779598.
if (aFrame->Combines3DTransformWithAncestors() ||
aFrame->StyleDisplay()->mTransformStyle == NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D) {
if (aContent) {
nsCString message;
message.AppendLiteral("Gecko bug: Async animation of 'preserve-3d' "
"transforms is not supported. See bug 779598");
AnimationUtils::LogAsyncAnimationFailure(message, aContent);
}
aPerformanceWarning = AnimationPerformanceWarning::Type::TransformPreserve3D;
return false;
}
// Note that testing BackfaceIsHidden() is not a sufficient test for
// what we need for animating backface-visibility correctly if we
// remove the above test for Extend3DContext(); that would require
// looking at backface-visibility on descendants as well.
// looking at backface-visibility on descendants as well. See bug 1186204.
if (aFrame->StyleDisplay()->BackfaceIsHidden()) {
if (aContent) {
nsCString message;
message.AppendLiteral("Gecko bug: Async animation of "
"'backface-visibility: hidden' transforms is not supported."
" See bug 1186204.");
AnimationUtils::LogAsyncAnimationFailure(message, aContent);
}
aPerformanceWarning =
AnimationPerformanceWarning::Type::TransformBackfaceVisibilityHidden;
return false;
}
// Async 'transform' animations of aFrames with SVG transforms is not
// supported. See bug 779599.
if (aFrame->IsSVGTransformed()) {
if (aContent) {
nsCString message;
message.AppendLiteral("Gecko bug: Async 'transform' animations of "
"aFrames with SVG transforms is not supported. See bug 779599");
AnimationUtils::LogAsyncAnimationFailure(message, aContent);
}
aPerformanceWarning = AnimationPerformanceWarning::Type::TransformSVG;
return false;
}
@ -2112,8 +2131,9 @@ KeyframeEffectReadOnly::CanAnimateTransformOnCompositor(
}
bool
KeyframeEffectReadOnly::ShouldBlockCompositorAnimations(const nsIFrame*
aFrame) const
KeyframeEffectReadOnly::ShouldBlockCompositorAnimations(
const nsIFrame* aFrame,
AnimationPerformanceWarning::Type& aPerformanceWarning) const
{
// We currently only expect this method to be called when this effect
// is attached to a playing Animation. If that ever changes we'll need
@ -2122,8 +2142,6 @@ KeyframeEffectReadOnly::ShouldBlockCompositorAnimations(const nsIFrame*
// running on the compositor.
MOZ_ASSERT(mAnimation && mAnimation->IsPlaying());
bool shouldLog = nsLayoutUtils::IsAnimationLoggingEnabled();
for (const AnimationProperty& property : mProperties) {
// If a property is overridden in the CSS cascade, it should not block other
// animations from running on the compositor.
@ -2132,20 +2150,15 @@ KeyframeEffectReadOnly::ShouldBlockCompositorAnimations(const nsIFrame*
}
// Check for geometric properties
if (IsGeometricProperty(property.mProperty)) {
if (shouldLog) {
nsCString message;
message.AppendLiteral("Performance warning: Async animation of "
"'transform' or 'opacity' not possible due to animation of geometric"
"properties on the same element");
AnimationUtils::LogAsyncAnimationFailure(message, aFrame->GetContent());
}
aPerformanceWarning =
AnimationPerformanceWarning::Type::WithGeometricProperties;
return true;
}
// Check for unsupported transform animations
if (property.mProperty == eCSSProperty_transform) {
if (!CanAnimateTransformOnCompositor(aFrame,
shouldLog ? aFrame->GetContent() : nullptr)) {
aPerformanceWarning)) {
return true;
}
}
@ -2154,6 +2167,28 @@ KeyframeEffectReadOnly::ShouldBlockCompositorAnimations(const nsIFrame*
return false;
}
void
KeyframeEffectReadOnly::SetPerformanceWarning(
nsCSSProperty aProperty,
const AnimationPerformanceWarning& aWarning)
{
for (AnimationProperty& property : mProperties) {
if (property.mProperty == aProperty &&
(!property.mPerformanceWarning ||
*property.mPerformanceWarning != aWarning)) {
property.mPerformanceWarning = Some(aWarning);
nsXPIDLString localizedString;
if (nsLayoutUtils::IsAnimationLoggingEnabled() &&
property.mPerformanceWarning->ToLocalizedString(localizedString)) {
nsAutoCString logMessage = NS_ConvertUTF16toUTF8(localizedString);
AnimationUtils::LogAsyncAnimationFailure(logMessage, mTarget);
}
return;
}
}
}
//---------------------------------------------------------------------
//
// KeyframeEffect

View File

@ -11,6 +11,7 @@
#include "nsCycleCollectionParticipant.h"
#include "nsIDocument.h"
#include "nsWrapperCache.h"
#include "mozilla/AnimationPerformanceWarning.h"
#include "mozilla/Attributes.h"
#include "mozilla/ComputedTimingFunction.h" // ComputedTimingFunction
#include "mozilla/LayerAnimationInfo.h" // LayerAnimations::kRecords
@ -43,6 +44,7 @@ class OwningElementOrCSSPseudoElement;
class UnrestrictedDoubleOrKeyframeEffectOptions;
enum class IterationCompositeOperation : uint32_t;
enum class CompositeOperation : uint32_t;
struct AnimationPropertyState;
}
/**
@ -145,6 +147,8 @@ struct AnimationProperty
// objects for equality.
bool mIsRunningOnCompositor = false;
Maybe<AnimationPerformanceWarning> mPerformanceWarning;
InfallibleTArray<AnimationPropertySegment> mSegments;
// NOTE: This operator does *not* compare the mWinsInCascade member *or* the
@ -303,6 +307,8 @@ public:
bool IsRunningOnCompositor() const;
void SetIsRunningOnCompositor(nsCSSProperty aProperty, bool aIsRunning);
void GetPropertyState(nsTArray<AnimationPropertyState>& aStates) const;
// Returns true if this effect, applied to |aFrame|, contains
// properties that mean we shouldn't run *any* compositor animations on this
// element.
@ -317,11 +323,24 @@ public:
//
// Bug 1218620 - It seems like we don't need to be this restrictive. Wouldn't
// it be ok to do 'opacity' animations on the compositor in either case?
bool ShouldBlockCompositorAnimations(const nsIFrame* aFrame) const;
//
// When returning true, |aOutPerformanceWarning| stores the reason why
// we shouldn't run the compositor animations.
bool ShouldBlockCompositorAnimations(
const nsIFrame* aFrame,
AnimationPerformanceWarning::Type& aPerformanceWarning) const;
nsIDocument* GetRenderedDocument() const;
nsPresContext* GetPresContext() const;
// Associates a warning with the animated property on the specified frame
// indicating why, for example, the property could not be animated on the
// compositor. |aParams| and |aParamsLength| are optional parameters which
// will be used to generate a localized message for devtools.
void SetPerformanceWarning(
nsCSSProperty aProperty,
const AnimationPerformanceWarning& aWarning);
protected:
KeyframeEffectReadOnly(nsIDocument* aDocument,
Element* aTarget,
@ -383,11 +402,11 @@ private:
bool CanThrottleTransformChanges(nsIFrame& aFrame) const;
// Returns true unless Gecko limitations prevent performing transform
// animations for |aFrame|. Any limitations that are encountered are
// logged using |aContent| to describe the affected content.
// If |aContent| is nullptr, no logging is performed
static bool CanAnimateTransformOnCompositor(const nsIFrame* aFrame,
const nsIContent* aContent);
// animations for |aFrame|. When returning true, the reason for the
// limitation is stored in |aOutPerformanceWarning|.
static bool CanAnimateTransformOnCompositor(
const nsIFrame* aFrame,
AnimationPerformanceWarning::Type& aPerformanceWarning);
static bool IsGeometricProperty(const nsCSSProperty aProperty);
static const TimeDuration OverflowRegionRefreshInterval();

View File

@ -20,6 +20,7 @@ EXPORTS.mozilla.dom += [
EXPORTS.mozilla += [
'AnimationComparator.h',
'AnimationPerformanceWarning.h',
'AnimationUtils.h',
'AnimValuesStyleRule.h',
'ComputedTimingFunction.h',
@ -34,6 +35,7 @@ UNIFIED_SOURCES += [
'AnimationEffectReadOnly.cpp',
'AnimationEffectTiming.cpp',
'AnimationEffectTimingReadOnly.cpp',
'AnimationPerformanceWarning.cpp',
'AnimationTimeline.cpp',
'AnimationUtils.cpp',
'AnimValuesStyleRule.cpp',

View File

@ -7,6 +7,7 @@ support-files =
# file_animate_xrays.html needs to go in mochitest.ini since it is served
# over HTTP
[chrome/test_animation_observers.html]
[chrome/test_animation_property_state.html]
[chrome/test_restyles.html]
[chrome/test_running_on_compositor.html]
skip-if = buildapp == 'b2g'

View File

@ -0,0 +1,330 @@
<!doctype html>
<head>
<meta charset=utf-8>
<title>Bug 1196114 - Animation property which indicates
running on the compositor or not</title>
<script type="application/javascript" src="../testharness.js"></script>
<script type="application/javascript" src="../testharnessreport.js"></script>
<script type="application/javascript" src="../testcommon.js"></script>
<style>
.compositable {
/* Element needs geometry to be eligible for layerization */
width: 100px;
height: 100px;
background-color: white;
}
</style>
</head>
<body>
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1196114"
target="_blank">Mozilla Bug 1196114</a>
<div id="log"></div>
<script>
'use strict';
// This is used for obtaining localized strings.
var gStringBundle;
SpecialPowers.pushPrefEnv({ "set": [["general.useragent.locale", "en-US"]] },
start);
function compare_property_state(a, b) {
if (a.property > b.property) {
return -1;
} else if (a.property < b.property) {
return 1;
}
if (a.runningOnCompositor != b.runningOnCompositor) {
return a.runningOnCompositor ? 1 : -1;
}
return a.warning > b.warning ? -1 : 1;
}
function assert_animation_property_state_equals(actual, expected) {
assert_equals(actual.length, expected.length);
var sortedActual = actual.sort(compare_property_state);
var sortedExpected = expected.sort(compare_property_state);
for (var i = 0; i < sortedActual.length; i++) {
assert_equals(sortedActual[i].property,
sortedExpected[i].property,
'CSS property name should match');
assert_equals(sortedActual[i].runningOnCompositor,
sortedExpected[i].runningOnCompositor,
'runningOnCompositor property should match');
if (sortedExpected[i].warning instanceof RegExp) {
assert_regexp_match(sortedActual[i].warning,
sortedExpected[i].warning,
'warning message should match');
} else if (sortedExpected[i].warning) {
assert_equals(sortedActual[i].warning,
gStringBundle.GetStringFromName(sortedExpected[i].warning),
'warning message should match');
}
}
}
// Check that the animation is running on compositor and
// warning property is not set for the CSS property regardless
// expected values.
function assert_property_state_on_compositor(actual, expected) {
assert_equals(actual.length, expected.length);
var sortedActual = actual.sort(compare_property_state);
var sortedExpected = expected.sort(compare_property_state);
for (var i = 0; i < sortedActual.length; i++) {
assert_equals(sortedActual[i].property,
sortedExpected[i].property,
'CSS property name should match');
assert_true(sortedActual[i].runningOnCompositor,
'runningOnCompositor property should be true');
assert_not_exists(sortedActual[i], 'warning',
'warning property should not be set');
}
}
var gAnimationsTests = [
{
desc: 'animations on compositor',
frames: {
opacity: [0, 1]
},
expected: [
{
property: 'opacity',
runningOnCompositor: true
}
]
},
{
desc: 'animations on main thread',
frames: {
backgroundColor: ['white', 'red']
},
expected: [
{
property: 'background-color',
runningOnCompositor: false
}
]
},
{
desc: 'animations on both threads',
frames: {
backgroundColor: ['white', 'red'],
transform: ['translate(0px)', 'translate(100px)']
},
expected: [
{
property: 'background-color',
runningOnCompositor: false
},
{
property: 'transform',
runningOnCompositor: true
}
]
},
{
desc: 'two animation properties on compositor thread',
frames: {
opacity: [0, 1],
transform: ['translate(0px)', 'translate(100px)']
},
expected: [
{
property: 'opacity',
runningOnCompositor: true
},
{
property: 'transform',
runningOnCompositor: true
}
]
},
{
// FIXME: Once we have KeyframeEffect.setFrames, we should rewrite
// this test case to check that runningOnCompositor is restored to true
// after 'width' keyframe is removed from the keyframes.
desc: 'animation on compositor with animation of geometric properties',
frames: {
width: ['100px', '200px'],
transform: ['translate(0px)', 'translate(100px)']
},
expected: [
{
property: 'width',
runningOnCompositor: false
},
{
property: 'transform',
runningOnCompositor: false,
warning: 'AnimationWarningWithGeometricProperties'
}
]
},
];
gAnimationsTests.forEach(function(subtest) {
promise_test(function(t) {
var div = addDiv(t, { class: 'compositable' });
var animation = div.animate(subtest.frames, 100000);
return animation.ready.then(t.step_func(function() {
assert_animation_property_state_equals(
animation.effect.getPropertyState(),
subtest.expected);
}));
}, subtest.desc);
});
var gPerformanceWarningTests = [
{
desc: 'preserve-3d transform',
frames: {
transform: ['translate(0px)', 'translate(100px)']
},
style: 'transform-style: preserve-3d',
expected: [
{
property: 'transform',
runningOnCompositor: false,
warning: 'AnimationWarningTransformPreserve3D'
}
]
},
{
desc: 'transform with backface-visibility:hidden',
frames: {
transform: ['translate(0px)', 'translate(100px)']
},
style: 'backface-visibility: hidden;',
expected: [
{
property: 'transform',
runningOnCompositor: false,
warning: 'AnimationWarningTransformBackfaceVisibilityHidden'
}
]
},
];
function start() {
var bundleService = SpecialPowers.Cc['@mozilla.org/intl/stringbundle;1']
.getService(SpecialPowers.Ci.nsIStringBundleService);
gStringBundle = bundleService
.createBundle("chrome://global/locale/layout_errors.properties");
gAnimationsTests.forEach(function(subtest) {
promise_test(function(t) {
var div = addDiv(t, { class: 'compositable' });
var animation = div.animate(subtest.frames, 100000);
return animation.ready.then(t.step_func(function() {
assert_animation_property_state_equals(
animation.effect.getPropertyState(),
subtest.expected);
}));
}, subtest.desc);
});
gPerformanceWarningTests.forEach(function(subtest) {
promise_test(function(t) {
var div = addDiv(t, { class: 'compositable' });
var animation = div.animate(subtest.frames, 100000);
return animation.ready.then(t.step_func(function() {
assert_property_state_on_compositor(
animation.effect.getPropertyState(),
subtest.expected);
div.style = subtest.style;
return waitForFrame();
})).then(t.step_func(function() {
assert_animation_property_state_equals(
animation.effect.getPropertyState(),
subtest.expected);
div.style = '';
return waitForFrame();
})).then(t.step_func(function() {
assert_property_state_on_compositor(
animation.effect.getPropertyState(),
subtest.expected);
}));
}, subtest.desc);
});
promise_test(function(t) {
var div = addDiv(t, { class: 'compositable' });
var animation = div.animate(
{ transform: ['translate(0px)', 'translate(100px)'] }, 100000);
return animation.ready.then(t.step_func(function() {
assert_animation_property_state_equals(
animation.effect.getPropertyState(),
[ { property: 'transform', runningOnCompositor: true } ]);
div.style = 'width: 10000px; height: 10000px';
return waitForFrame();
})).then(t.step_func(function() {
// viewport depends on test environment.
var expectedWarning = new RegExp(
"Async animation disabled because frame size \\(10000, 10000\\) is " +
"bigger than the viewport \\(\\d+, \\d+\\) or the visual rectangle " +
"\\(10000, 10000\\) is larger than the max allowed value \\(\\d+\\)");
assert_animation_property_state_equals(
animation.effect.getPropertyState(),
[ {
property: 'transform',
runningOnCompositor: false,
warning: expectedWarning
} ]);
div.style = 'width: 100px; height: 100px';
return waitForFrame();
})).then(t.step_func(function() {
// FIXME: Bug 1253164: the animation should get back on compositor.
assert_animation_property_state_equals(
animation.effect.getPropertyState(),
[ { property: 'transform', runningOnCompositor: false } ]);
}));
}, 'transform on too big element');
promise_test(function(t) {
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('width', '100');
svg.setAttribute('height', '100');
var rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('width', '100');
rect.setAttribute('height', '100');
rect.setAttribute('fill', 'red');
svg.appendChild(rect);
document.body.appendChild(svg);
t.add_cleanup(function() {
svg.remove();
});
var animation = svg.animate(
{ transform: ['translate(0px)', 'translate(100px)'] }, 100000);
return animation.ready.then(t.step_func(function() {
assert_animation_property_state_equals(
animation.effect.getPropertyState(),
[ { property: 'transform', runningOnCompositor: true } ]);
svg.setAttribute('transform', 'translate(10, 20)');
return waitForFrame();
})).then(t.step_func(function() {
assert_animation_property_state_equals(
animation.effect.getPropertyState(),
[ {
property: 'transform',
runningOnCompositor: false,
warning: 'AnimationWarningTransformSVG'
} ]);
svg.removeAttribute('transform');
return waitForFrame();
})).then(t.step_func(function() {
assert_animation_property_state_equals(
animation.effect.getPropertyState(),
[ { property: 'transform', runningOnCompositor: true } ]);
}));
}, 'transform of nsIFrame with SVG transform');
}
</script>
</body>

View File

@ -112,7 +112,7 @@
#include "mozAutoDocUpdate.h"
#include "prprf.h"
#include "mozilla/Snprintf.h"
#include "nsDOMMutationObserver.h"
#include "nsWrapperCacheInlines.h"
#include "nsCycleCollector.h"
@ -1883,13 +1883,13 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement)
}
const char* nsuri = nsid < ArrayLength(kNSURIs) ? kNSURIs[nsid] : "";
PR_snprintf(name, sizeof(name), "FragmentOrElement%s %s%s%s%s %s",
nsuri,
localName.get(),
NS_ConvertUTF16toUTF8(id).get(),
NS_ConvertUTF16toUTF8(classes).get(),
orphan.get(),
uri.get());
snprintf_literal(name, "FragmentOrElement%s %s%s%s%s %s",
nsuri,
localName.get(),
NS_ConvertUTF16toUTF8(id).get(),
NS_ConvertUTF16toUTF8(classes).get(),
orphan.get(),
uri.get());
cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
}
else {

View File

@ -25,7 +25,7 @@
#include "nsContentUtils.h"
#include "nsReadableUtils.h"
#include "nsAutoPtr.h"
#include "prprf.h"
#include "mozilla/Snprintf.h"
#include "nsIDocument.h"
#include "nsGkAtoms.h"
#include "nsCCUncollectableMarker.h"
@ -130,11 +130,11 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(NodeInfo)
uint32_t nsid = tmp->NamespaceID();
nsAtomCString localName(tmp->NameAtom());
if (nsid < ArrayLength(kNodeInfoNSURIs)) {
PR_snprintf(name, sizeof(name), "NodeInfo%s %s", kNodeInfoNSURIs[nsid],
localName.get());
snprintf_literal(name, "NodeInfo%s %s", kNodeInfoNSURIs[nsid],
localName.get());
}
else {
PR_snprintf(name, sizeof(name), "NodeInfo %s", localName.get());
snprintf_literal(name, "NodeInfo %s", localName.get());
}
cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);

View File

@ -23,7 +23,7 @@
#include "mozilla/Logging.h"
#include "plstr.h"
#include "prprf.h"
#include "mozilla/Snprintf.h"
#include "mozilla/Telemetry.h"
#include "nsIInterfaceRequestor.h"
@ -1777,12 +1777,12 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
if (tmp->mDocumentURI)
tmp->mDocumentURI->GetSpec(uri);
if (nsid < ArrayLength(kNSURIs)) {
PR_snprintf(name, sizeof(name), "nsDocument %s %s %s",
loadedAsData.get(), kNSURIs[nsid], uri.get());
snprintf_literal(name, "nsDocument %s %s %s",
loadedAsData.get(), kNSURIs[nsid], uri.get());
}
else {
PR_snprintf(name, sizeof(name), "nsDocument %s %s",
loadedAsData.get(), uri.get());
snprintf_literal(name, "nsDocument %s %s",
loadedAsData.get(), uri.get());
}
cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
}
@ -2951,10 +2951,9 @@ GetFormattedTimeString(PRTime aTime, nsAString& aFormattedTimeString)
PR_ExplodeTime(aTime, PR_LocalTimeParameters, &prtime);
// "MM/DD/YYYY hh:mm:ss"
char formatedTime[24];
if (PR_snprintf(formatedTime, sizeof(formatedTime),
"%02ld/%02ld/%04hd %02ld:%02ld:%02ld",
prtime.tm_month + 1, prtime.tm_mday, prtime.tm_year,
prtime.tm_hour , prtime.tm_min, prtime.tm_sec)) {
if (snprintf_literal(formatedTime, "%02d/%02d/%04d %02d:%02d:%02d",
prtime.tm_month + 1, prtime.tm_mday, int(prtime.tm_year),
prtime.tm_hour , prtime.tm_min, prtime.tm_sec)) {
CopyASCIItoUTF16(nsDependentCString(formatedTime), aFormattedTimeString);
} else {
// If we for whatever reason failed to find the last modified time

View File

@ -34,7 +34,7 @@
#include "mozAutoDocUpdate.h"
#include "PLDHashTable.h"
#include "prprf.h"
#include "mozilla/Snprintf.h"
#include "nsWrapperCacheInlines.h"
using namespace mozilla;
@ -90,8 +90,8 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGenericDOMDataNode)
if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
char name[40];
PR_snprintf(name, sizeof(name), "nsGenericDOMDataNode (len=%d)",
tmp->mText.GetLength());
snprintf_literal(name, "nsGenericDOMDataNode (len=%d)",
tmp->mText.GetLength());
cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
} else {
NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGenericDOMDataNode, tmp->mRefCnt.get())
@ -426,7 +426,7 @@ nsGenericDOMDataNode::ToCString(nsAString& aBuf, int32_t aOffset,
aBuf.AppendLiteral("&gt;");
} else if ((ch < ' ') || (ch >= 127)) {
char buf[10];
PR_snprintf(buf, sizeof(buf), "\\u%04x", ch);
snprintf_literal(buf, "\\u%04x", ch);
AppendASCIItoUTF16(buf, aBuf);
} else {
aBuf.Append(ch);
@ -446,7 +446,7 @@ nsGenericDOMDataNode::ToCString(nsAString& aBuf, int32_t aOffset,
aBuf.AppendLiteral("&gt;");
} else if ((ch < ' ') || (ch >= 127)) {
char buf[10];
PR_snprintf(buf, sizeof(buf), "\\u%04x", ch);
snprintf_literal(buf, "\\u%04x", ch);
AppendASCIItoUTF16(buf, aBuf);
} else {
aBuf.Append(ch);

View File

@ -20,6 +20,7 @@
#include "mozilla/dom/DOMStorage.h"
#include "mozilla/dom/StorageEvent.h"
#include "mozilla/dom/StorageEventBinding.h"
#include "mozilla/IntegerPrintfMacros.h"
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
#include "mozilla/dom/WindowOrientationObserver.h"
#endif
@ -1816,9 +1817,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindow)
if (tmp->mDoc && tmp->mDoc->GetDocumentURI()) {
tmp->mDoc->GetDocumentURI()->GetSpec(uri);
}
PR_snprintf(name, sizeof(name), "nsGlobalWindow #%llu %s %s",
tmp->mWindowID, tmp->IsInnerWindow() ? "inner" : "outer",
uri.get());
snprintf_literal(name, "nsGlobalWindow # %" PRIu64 " %s %s", tmp->mWindowID,
tmp->IsInnerWindow() ? "inner" : "outer", uri.get());
cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
} else {
NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindow, tmp->mRefCnt.get())

View File

@ -23,7 +23,7 @@
#include "nsNameSpaceManager.h"
#include "nsTextFragment.h"
#include "nsString.h"
#include "prprf.h"
#include "mozilla/Snprintf.h"
#include "nsUnicharUtils.h"
#include "nsCRT.h"
#include "nsContentUtils.h"
@ -608,7 +608,7 @@ nsXMLContentSerializer::GenerateNewPrefix(nsAString& aPrefix)
{
aPrefix.Assign('a');
char buf[128];
PR_snprintf(buf, sizeof(buf), "%d", mPrefixIndex++);
snprintf_literal(buf, "%d", mPrefixIndex++);
AppendASCIItoUTF16(buf, aPrefix);
}

View File

@ -38,7 +38,6 @@ function test() {
// Need to capture 2 loads, one for the main page and one for the iframe
function MixedTest1A() {
dump("XYZ\n");
BrowserTestUtils.browserLoaded(gTestBrowser, true /*includeSubFrames*/).then(MixedTest1B);
}
@ -62,9 +61,8 @@ function MixedTest1C() {
ContentTask.spawn(gTestBrowser, null, function() {
return content.location.href;
}).then(url => {
ok(gTestBrowser.contentWindow.location == "http://example.com/", "Navigating to insecure domain through target='_top' failed.")
is(url, "http://example.com/", "Navigating to insecure domain through target='_top' failed.")
MixedTestsCompleted();
});
}

View File

@ -260,6 +260,7 @@ support-files =
file_bug1198095.js
file_bug1250148.sjs
mozbrowser_api_utils.js
websocket_helpers.js
[test_anonymousContent_api.html]
[test_anonymousContent_append_after_reflow.html]
@ -808,7 +809,15 @@ skip-if = toolkit == 'android'
[test_w3element_traversal.html]
[test_w3element_traversal.xhtml]
[test_w3element_traversal_svg.html]
[test_websocket.html]
[test_websocket1.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' # TC: Bug 1144079 - Re-enable Mulet mochitests and reftests taskcluster-specific disables.
[test_websocket2.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' # TC: Bug 1144079 - Re-enable Mulet mochitests and reftests taskcluster-specific disables.
[test_websocket3.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' # TC: Bug 1144079 - Re-enable Mulet mochitests and reftests taskcluster-specific disables.
[test_websocket4.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' # TC: Bug 1144079 - Re-enable Mulet mochitests and reftests taskcluster-specific disables.
[test_websocket5.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' # TC: Bug 1144079 - Re-enable Mulet mochitests and reftests taskcluster-specific disables.
[test_websocket_basic.html]
skip-if = buildapp == 'b2g' || toolkit == 'android'

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,268 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"></meta>
<title>WebSocket test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="websocket_helpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="testWebSocket()">
<script class="testbody" type="text/javascript">
function test1() {
return new Promise(function(resolve, reject) {
try {
var ws = CreateTestWS("http://mochi.test:8888/tests/dom/base/test/file_websocket");
ok(false, "test1 failed");
} catch (e) {
ok(true, "test1 failed");
}
resolve();
});
}
// this test expects that the serialization list to connect to the proxy
// is empty.
function test2() {
return new Promise(function(resolve, reject) {
var waitTest2Part1 = true;
var waitTest2Part2 = true;
var ws1 = CreateTestWS("ws://sub2.test2.example.com/tests/dom/base/test/file_websocket", "test-2.1");
var ws2 = CreateTestWS("ws://sub2.test2.example.com/tests/dom/base/test/file_websocket", "test-2.2");
var ws2CanConnect = false;
function maybeFinished() {
if (!waitTest2Part1 && !waitTest2Part2) {
resolve();
}
}
ws1.onopen = function() {
ok(true, "ws1 open in test 2");
ws2CanConnect = true;
ws1.close();
}
ws1.onclose = function(e) {
waitTest2Part1 = false;
maybeFinished();
}
ws2.onopen = function() {
ok(ws2CanConnect, "shouldn't connect yet in test-2!");
ws2.close();
}
ws2.onclose = function(e) {
waitTest2Part2 = false;
maybeFinished();
}
});
}
function test3() {
return new Promise(function(resolve, reject) {
var hasError = false;
var ws = CreateTestWS("ws://this.websocket.server.probably.does.not.exist");
ws.onopen = shouldNotOpen;
ws.onerror = function (e) {
hasError = true;
}
ws.onclose = function(e) {
shouldCloseNotCleanly(e);
ok(hasError, "rcvd onerror event");
is(e.code, 1006, "test-3 close code should be 1006 but is:" + e.code);
resolve();
}
});
}
function test4() {
return new Promise(function(resolve, reject) {
try {
var ws = CreateTestWS("file_websocket");
ok(false, "test-4 failed");
} catch (e) {
ok(true, "test-4 failed");
}
resolve();
});
}
function test5() {
return new Promise(function(resolve, reject) {
try {
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "");
ok(false, "couldn't accept an empty string in the protocol parameter");
} catch (e) {
ok(true, "couldn't accept an empty string in the protocol parameter");
}
try {
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "\n");
ok(false, "couldn't accept any not printable ASCII character in the protocol parameter");
} catch (e) {
ok(true, "couldn't accept any not printable ASCII character in the protocol parameter");
}
try {
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test 5");
ok(false, "U+0020 not acceptable in protocol parameter");
} catch (e) {
ok(true, "U+0020 not acceptable in protocol parameter");
}
resolve();
});
}
function test6() {
return new Promise(function(resolve, reject) {
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-6");
var counter = 1;
ws.onopen = function() {
ws.send(counter);
}
ws.onmessage = function(e) {
if (counter == 5) {
is(e.data, "あいうえお", "test-6 counter 5 data ok");
ws.close();
} else {
is(parseInt(e.data), counter+1, "bad counter");
counter += 2;
ws.send(counter);
}
}
ws.onclose = function(e) {
shouldCloseCleanly(e);
resolve();
}
});
}
function test7() {
return new Promise(function(resolve, reject) {
var ws = CreateTestWS("ws://sub2.test2.example.org/tests/dom/base/test/file_websocket", "test-7");
var gotmsg = false;
ws.onopen = function() {
ok(true, "test 7 open");
}
ws.onmessage = function(e) {
ok(true, "test 7 message");
is(e.origin, "ws://sub2.test2.example.org", "onmessage origin set to ws:// host");
gotmsg = true;
ws.close();
}
ws.onclose = function(e) {
ok(gotmsg, "recvd message in test 7 before close");
shouldCloseCleanly(e);
resolve();
}
});
}
function test8() {
return new Promise(function(resolve, reject) {
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-8");
ws.onopen = function() {
is(ws.protocol, "test-8", "test-8 subprotocol selection");
ws.close();
}
ws.onclose = function(e) {
shouldCloseCleanly(e);
// We called close() with no close code: so pywebsocket will also send no
// close code, which translates to code 1005
is(e.code, 1005, "test-8 close code has wrong value:" + e.code);
is(e.reason, "", "test-8 close reason has wrong value:" + e.reason);
resolve();
}
});
}
function test9() {
return new Promise(function(resolve, reject) {
var ws = CreateTestWS("ws://test2.example.org/tests/dom/base/test/file_websocket", "test-9");
ws._receivedErrorEvent = false;
ws.onopen = shouldNotOpen;
ws.onerror = function(e) {
ws._receivedErrorEvent = true;
}
ws.onclose = function(e) {
ok(ws._receivedErrorEvent, "Didn't received the error event in test 9.");
shouldCloseNotCleanly(e);
resolve();
}
ws.close();
});
}
function test10() {
return new Promise(function(resolve, reject) {
var ws = CreateTestWS("ws://sub1.test1.example.com/tests/dom/base/test/file_websocket", "test-10");
ws.onclose = function(e) {
shouldCloseCleanly(e);
resolve();
}
try {
ws.send("client data");
ok(false, "Couldn't send data before connecting!");
} catch (e) {
ok(true, "Couldn't send data before connecting!");
}
ws.onopen = function()
{
ok(true, "test 10 opened");
ws.close();
}
});
}
var tests = [
test1, // client tries to connect to a http scheme location;
test2, // assure serialization of the connections;
test3, // client tries to connect to an non-existent ws server;
test4, // client tries to connect using a relative url;
test5, // client uses an invalid protocol value;
test6, // counter and encoding check;
test7, // onmessage event origin property check
test8, // client calls close() and the server sends the close frame (with no
// code or reason) in acknowledgement;
test9, // client closes the connection before the ws connection is established;
test10, // client sends a message before the ws connection is established;
];
function testWebSocket() {
doTest();
}
</script>
<div id="feedback">
</div>
</body>
</html>

View File

@ -0,0 +1,272 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"></meta>
<title>WebSocket test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="websocket_helpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="testWebSocket()">
<script class="testbody" type="text/javascript">
function test11() {
return new Promise(function(resolve, reject) {
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-11");
is(ws.readyState, 0, "create bad readyState in test-11!");
ws.onopen = function() {
is(ws.readyState, 1, "open bad readyState in test-11!");
ws.send("client data");
}
ws.onmessage = function(e) {
is(e.data, "server data", "bad received message in test-11!");
ws.close(1000, "Have a nice day");
// this ok() is disabled due to a race condition - it state may have
// advanced through 2 (closing) and into 3 (closed) before it is evald
// ok(ws.readyState == 2, "onmessage bad readyState in test-11!");
}
ws.onclose = function(e) {
is(ws.readyState, 3, "onclose bad readyState in test-11!");
shouldCloseCleanly(e);
is(e.code, 1000, "test 11 got wrong close code: " + e.code);
is(e.reason, "Have a nice day", "test 11 got wrong close reason: " + e.reason);
resolve();
}
});
}
function test12() {
return new Promise(function(resolve, reject) {
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-12");
ws.onopen = function() {
try {
// send an unpaired surrogate
ws._gotMessage = false;
ws.send("a\ud800b");
ok(true, "ok to send an unpaired surrogate");
} catch (e) {
ok(false, "shouldn't fail any more when sending an unpaired surrogate!");
}
}
ws.onmessage = function(msg) {
is(msg.data, "SUCCESS", "Unpaired surrogate in UTF-16 not converted in test-12");
ws._gotMessage = true;
// Must support unpaired surrogates in close reason, too
ws.close(1000, "a\ud800b");
}
ws.onclose = function(e) {
is(ws.readyState, 3, "onclose bad readyState in test-12!");
ok(ws._gotMessage, "didn't receive message!");
shouldCloseCleanly(e);
is(e.code, 1000, "test 12 got wrong close code: " + e.code);
is(e.reason, "a\ufffdb", "test 11 didn't get replacement char in close reason: " + e.reason);
resolve();
}
});
}
function test13() {
return new Promise(function(resolve, reject) {
// previous versions of this test counted the number of protocol errors
// returned, but the protocol stack typically closes down after reporting a
// protocol level error - trying to resync is too dangerous
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-13");
ws._timesCalledOnError = 0;
ws.onerror = function() {
ws._timesCalledOnError++;
}
ws.onclose = function(e) {
ok(ws._timesCalledOnError > 0, "no error events");
resolve();
}
});
}
function test14() {
return new Promise(function(resolve, reject) {
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-14");
ws.onmessage = function() {
ok(false, "shouldn't received message after the server sent the close frame");
}
ws.onclose = function(e) {
shouldCloseCleanly(e);
resolve();
};
});
}
function test15() {
return new Promise(function(resolve, reject) {
/*
* DISABLED: see comments for test-15 case in file_websocket_wsh.py
*/
resolve();
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-15");
ws.onclose = function(e) {
shouldCloseNotCleanly(e);
resolve();
}
// termination of the connection might cause an error event if it happens in OPEN
ws.onerror = function() {
}
});
}
function test16() {
return new Promise(function(resolve, reject) {
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-16");
ws.onopen = function() {
ws.close();
ok(!ws.send("client data"), "shouldn't send message after calling close()");
}
ws.onmessage = function() {
ok(false, "shouldn't send message after calling close()");
}
ws.onerror = function() {
}
ws.onclose = function() {
resolve();
}
});
}
function test17() {
return new Promise(function(resolve, reject) {
var status_test17 = "not started";
var test17func = function() {
var local_ws = new WebSocket("ws://sub1.test2.example.org/tests/dom/base/test/file_websocket", "test-17");
status_test17 = "started";
local_ws.onopen = function(e) {
status_test17 = "opened";
e.target.send("client data");
forcegc();
};
local_ws.onerror = function() {
ok(false, "onerror called on test " + current_test + "!");
};
local_ws.onmessage = function(e) {
ok(e.data == "server data", "Bad message in test-17");
status_test17 = "got message";
forcegc();
};
local_ws.onclose = function(e) {
ok(status_test17 == "got message", "Didn't got message in test-17!");
shouldCloseCleanly(e);
status_test17 = "closed";
forcegc();
resolve();
};
window._test17 = null;
forcegc();
}
window._test17 = test17func;
window._test17();
});
}
// The tests that expects that their websockets neither open nor close MUST
// be in the end of the tests, i.e. HERE, in order to prevent blocking the other
// tests.
function test18() {
return new Promise(function(resolve, reject) {
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket_http_resource.txt");
ws.onopen = shouldNotOpen;
ws.onerror = ignoreError;
ws.onclose = function(e)
{
shouldCloseNotCleanly(e);
resolve();
}
});
}
function test19() {
return new Promise(function(resolve, reject) {
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-19");
ws.onopen = shouldNotOpen;
ws.onerror = ignoreError;
ws.onclose = function(e)
{
shouldCloseNotCleanly(e);
resolve();
}
});
}
function test20() {
return new Promise(function(resolve, reject) {
var test20func = function() {
var local_ws = new WebSocket("ws://sub1.test1.example.org/tests/dom/base/test/file_websocket", "test-20");
local_ws.onerror = function() {
ok(false, "onerror called on test " + current_test + "!");
}
local_ws.onclose = function(e) {
ok(true, "test 20 closed despite gc");
resolve();
}
local_ws = null;
window._test20 = null;
forcegc();
}
window._test20 = test20func;
window._test20();
});
}
var tests = [
test11, // a simple hello echo;
test12, // client sends a message containing unpaired surrogates
test13, //server sends an invalid message;
test14, // server sends the close frame, it doesn't close the tcp connection
// and it keeps sending normal ws messages;
test15, // server closes the tcp connection, but it doesn't send the close
// frame;
test16, // client calls close() and tries to send a message;
test17, // see bug 572975 - all event listeners set
test18, // client tries to connect to an http resource;
test19, // server closes the tcp connection before establishing the ws
// connection;
test20, // see bug 572975 - only on error and onclose event listeners set
];
function testWebSocket() {
doTest();
}
</script>
<div id="feedback">
</div>
</body>
</html>

View File

@ -0,0 +1,225 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"></meta>
<title>WebSocket test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="websocket_helpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="testWebSocket()">
<script class="testbody" type="text/javascript">
function test21() {
return new Promise(function(resolve, reject) {
var test21func = function() {
var local_ws = new WebSocket("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-21");
var received_message = false;
local_ws.onopen = function(e) {
e.target.send("client data");
forcegc();
e.target.onopen = null;
forcegc();
}
local_ws.onerror = function() {
ok(false, "onerror called on test " + current_test + "!");
}
local_ws.onmessage = function(e) {
is(e.data, "server data", "Bad message in test-21");
received_message = true;
forcegc();
e.target.onmessage = null;
forcegc();
}
local_ws.onclose = function(e) {
shouldCloseCleanly(e);
ok(received_message, "close transitioned through onmessage");
resolve();
}
local_ws = null;
window._test21 = null;
forcegc();
}
window._test21 = test21func;
window._test21();
});
}
function test22() {
return new Promise(function(resolve, reject) {
const pref_open = "network.websocket.timeout.open";
SpecialPowers.setIntPref(pref_open, 5);
var ws = CreateTestWS("ws://sub2.test2.example.org/tests/dom/base/test/file_websocket", "test-22");
ws.onopen = shouldNotOpen;
ws.onerror = ignoreError;
ws.onclose = function(e) {
shouldCloseNotCleanly(e);
resolve();
}
SpecialPowers.clearUserPref(pref_open);
});
}
function test23() {
return new Promise(function(resolve, reject) {
ok("WebSocket" in window, "WebSocket should be available on window object");
resolve();
});
}
function test24() {
return new Promise(function(resolve, reject) {
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-does-not-exist");
ws.onopen = shouldNotOpen;
ws.onclose = function(e) {
shouldCloseNotCleanly(e);
resolve();
}
ws.onerror = function() {
}
});
}
function test25() {
return new Promise(function(resolve, reject) {
var prots=[];
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
// This test errors because the server requires a sub-protocol, but
// the test just wants to ensure that the ctor doesn't generate an
// exception
ws.onerror = ignoreError;
ws.onopen = shouldNotOpen;
ws.onclose = function(e) {
is(ws.protocol, "", "test25 subprotocol selection");
ok(true, "test 25 protocol array close");
resolve();
}
});
}
function test26() {
return new Promise(function(resolve, reject) {
var prots=[""];
try {
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
ok(false, "testing empty element sub protocol array");
} catch (e) {
ok(true, "testing empty sub element protocol array");
}
resolve();
});
}
function test27() {
return new Promise(function(resolve, reject) {
var prots=["test27", ""];
try {
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
ok(false, "testing empty element mixed sub protocol array");
} catch (e) {
ok(true, "testing empty element mixed sub protocol array");
}
resolve();
});
}
function test28() {
return new Promise(function(resolve, reject) {
var prots=["test28"];
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
ws.onopen = function(e) {
ok(true, "test 28 protocol array open");
ws.close();
}
ws.onclose = function(e) {
is(ws.protocol, "test28", "test28 subprotocol selection");
ok(true, "test 28 protocol array close");
resolve();
}
});
}
function test29() {
return new Promise(function(resolve, reject) {
var prots=["test29a", "test29b"];
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
ws.onopen = function(e) {
ok(true, "test 29 protocol array open");
ws.close();
}
ws.onclose = function(e) {
ok(true, "test 29 protocol array close");
resolve();
}
});
}
function test30() {
return new Promise(function(resolve, reject) {
var prots=["test-does-not-exist"];
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
ws.onopen = shouldNotOpen;
ws.onclose = function(e) {
shouldCloseNotCleanly(e);
resolve();
}
ws.onerror = function() {
}
});
}
var tests = [
test21, // see bug 572975 - same as test 17, but delete strong event listeners
// when receiving the message event;
test22, // server takes too long to establish the ws connection;
test23, // should detect WebSocket on window object;
test24, // server rejects sub-protocol string
test25, // ctor with valid empty sub-protocol array
test26, // ctor with invalid sub-protocol array containing 1 empty element
test27, // ctor with invalid sub-protocol array containing an empty element in
// list
test28, // ctor using valid 1 element sub-protocol array
test29, // ctor using all valid 5 element sub-protocol array
test30, // ctor using valid 1 element sub-protocol array with element server
// will reject
];
function testWebSocket() {
doTest();
}
</script>
<div id="feedback">
</div>
</body>
</html>

View File

@ -0,0 +1,290 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"></meta>
<title>WebSocket test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="websocket_helpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="testWebSocket()">
<script class="testbody" type="text/javascript">
function test31() {
return new Promise(function(resolve, reject) {
var prots=["test-does-not-exist", "test31"];
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
ws.onopen = function(e) {
ok(true, "test 31 protocol array open");
ws.close();
}
ws.onclose = function(e) {
is(ws.protocol, "test31", "test31 subprotocol selection");
ok(true, "test 31 protocol array close");
resolve();
}
});
}
function test32() {
return new Promise(function(resolve, reject) {
var prots=["test32","test32"];
try {
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
ok(false, "testing duplicated element sub protocol array");
} catch (e) {
ok(true, "testing duplicated sub element protocol array");
}
resolve();
});
}
function test33() {
return new Promise(function(resolve, reject) {
var prots=["test33"];
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
ws.onopen = function(e) {
ok(true, "test 33 open");
ws.close(3131); // pass code but not reason
}
ws.onclose = function(e) {
ok(true, "test 33 close");
shouldCloseCleanly(e);
is(e.code, 3131, "test 33 got wrong close code: " + e.code);
is(e.reason, "", "test 33 got wrong close reason: " + e.reason);
resolve();
}
});
}
function test34() {
return new Promise(function(resolve, reject) {
var prots=["test-34"];
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
ws.onopen = function(e) {
ok(true, "test 34 open");
ws.close();
}
ws.onclose = function(e)
{
ok(true, "test 34 close");
ok(e.wasClean, "test 34 closed cleanly");
is(e.code, 1001, "test 34 custom server code");
is(e.reason, "going away now", "test 34 custom server reason");
resolve();
}
});
}
function test35() {
return new Promise(function(resolve, reject) {
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-35a");
ws.onopen = function(e) {
ok(true, "test 35a open");
ws.close(3500, "my code");
}
ws.onclose = function(e) {
ok(true, "test 35a close");
ok(e.wasClean, "test 35a closed cleanly");
var wsb = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-35b");
wsb.onopen = function(e) {
ok(true, "test 35b open");
wsb.close();
}
wsb.onclose = function(e) {
ok(true, "test 35b close");
ok(e.wasClean, "test 35b closed cleanly");
is(e.code, 3501, "test 35 custom server code");
is(e.reason, "my code", "test 35 custom server reason");
resolve();
}
}
});
}
function test36() {
return new Promise(function(resolve, reject) {
var prots=["test-36"];
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
ws.onopen = function(e) {
ok(true, "test 36 open");
try {
ws.close(13200);
ok(false, "testing custom close code out of range");
} catch (e) {
ok(true, "testing custom close code out of range");
ws.close(3200);
}
}
ws.onclose = function(e) {
ok(true, "test 36 close");
ok(e.wasClean, "test 36 closed cleanly");
resolve();
}
});
}
function test37() {
return new Promise(function(resolve, reject) {
var prots=["test-37"];
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
ws.onopen = function(e) {
ok(true, "test 37 open");
try {
ws.close(3100,"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123");
ok(false, "testing custom close reason out of range");
} catch (e) {
ok(true, "testing custom close reason out of range");
ws.close(3100,"012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012");
}
}
ws.onclose = function(e) {
ok(true, "test 37 close");
ok(e.wasClean, "test 37 closed cleanly");
var wsb = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-37b");
wsb.onopen = function(e) {
// now test that a rejected close code and reason dont persist
ok(true, "test 37b open");
try {
wsb.close(3101,"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123");
ok(false, "testing custom close reason out of range 37b");
} catch (e) {
ok(true, "testing custom close reason out of range 37b");
wsb.close();
}
}
wsb.onclose = function(e) {
ok(true, "test 37b close");
ok(e.wasClean, "test 37b closed cleanly");
var wsc = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-37c");
wsc.onopen = function(e) {
ok(true, "test 37c open");
wsc.close();
}
wsc.onclose = function(e) {
isnot(e.code, 3101, "test 37c custom server code not present");
is(e.reason, "", "test 37c custom server reason not present");
resolve();
}
}
}
});
}
function test38() {
return new Promise(function(resolve, reject) {
var prots=["test-38"];
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
ws.onopen = function(e) {
ok(true, "test 38 open");
isnot(ws.extensions, undefined, "extensions attribute defined");
// is(ws.extensions, "deflate-stream", "extensions attribute deflate-stream");
ws.close();
}
ws.onclose = function(e) {
ok(true, "test 38 close");
resolve();
}
});
}
function test39() {
return new Promise(function(resolve, reject) {
var prots=["test-39"];
var ws = CreateTestWS("wss://example.com/tests/dom/base/test/file_websocket", prots);
status_test39 = "started";
ws.onopen = function(e) {
status_test39 = "opened";
ok(true, "test 39 open");
ws.close();
}
ws.onclose = function(e) {
ok(true, "test 39 close");
is(status_test39, "opened", "test 39 did open");
resolve();
}
});
}
function test40() {
return new Promise(function(resolve, reject) {
var prots=["test-40"];
var ws = CreateTestWS("wss://nocert.example.com/tests/dom/base/test/file_websocket", prots);
status_test40 = "started";
ws.onerror = ignoreError;
ws.onopen = function(e) {
status_test40 = "opened";
ok(false, "test 40 open");
ws.close();
}
ws.onclose = function(e) {
ok(true, "test 40 close");
is(status_test40, "started", "test 40 did not open");
resolve();
}
});
}
var tests = [
test31, // ctor using valid 2 element sub-protocol array with 1 element server
// will reject and one server will accept
test32, // ctor using invalid sub-protocol array that contains duplicate items
test33, // test for sending/receiving custom close code (but no close reason)
test34, // test for receiving custom close code and reason
test35, // test for sending custom close code and reason
test36, // negative test for sending out of range close code
test37, // negative test for too long of a close reason
test38, // ensure extensions attribute is defined
test39, // a basic wss:// connectivity test
test40, // negative test for wss:// with no cert
];
function testWebSocket() {
doTest();
}
</script>
<div id="feedback">
</div>
</body>
</html>

View File

@ -0,0 +1,293 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"></meta>
<title>WebSocket test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="websocket_helpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="testWebSocket()">
<script class="testbody" type="text/javascript">
function test41() {
return new Promise(function(resolve, reject) {
var ws = CreateTestWS("ws://example.com/tests/dom/base/test/file_websocket", "test-41a", 1);
ws.onopen = function(e) {
ok(true, "test 41a open");
is(ws.url, "ws://example.com/tests/dom/base/test/file_websocket",
"test 41a initial ws should not be redirected");
ws.close();
}
ws.onclose = function(e) {
ok(true, "test 41a close");
// establish a hsts policy for example.com
var wsb = CreateTestWS("wss://example.com/tests/dom/base/test/file_websocket", "test-41b", 1);
wsb.onopen = function(e) {
ok(true, "test 41b open");
wsb.close();
}
wsb.onclose = function(e) {
ok(true, "test 41b close");
// try ws:// again, it should be done over wss:// now due to hsts
var wsc = CreateTestWS("ws://example.com/tests/dom/base/test/file_websocket", "test-41c");
wsc.onopen = function(e) {
ok(true, "test 41c open");
is(wsc.url, "wss://example.com/tests/dom/base/test/file_websocket",
"test 41c ws should be redirected by hsts to wss");
wsc.close();
}
wsc.onclose = function(e) {
ok(true, "test 41c close");
// clean up the STS state
const Ci = SpecialPowers.Ci;
var loadContext = SpecialPowers.wrap(window)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsILoadContext);
var flags = 0;
if (loadContext.usePrivateBrowsing)
flags |= Ci.nsISocketProvider.NO_PERMANENT_STORAGE;
SpecialPowers.cleanUpSTSData("http://example.com", flags);
resolve();
}
}
}
});
}
function test42() {
return new Promise(function(resolve, reject) {
// test some utf-8 non-characters. They should be allowed in the
// websockets context. Test via round trip echo.
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-42");
var data = ["U+FFFE \ufffe",
"U+FFFF \uffff",
"U+10FFFF \udbff\udfff"];
var index = 0;
ws.onopen = function() {
ws.send(data[0]);
ws.send(data[1]);
ws.send(data[2]);
}
ws.onmessage = function(e) {
is(e.data, data[index], "bad received message in test-42! index="+index);
index++;
if (index == 3) {
ws.close();
}
}
ws.onclose = function(e) {
resolve();
}
});
}
function test43() {
return new Promise(function(resolve, reject) {
var prots=["test-43"];
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
ws.onopen = function(e) {
ok(true, "test 43 open");
// Test binaryType setting
ws.binaryType = "arraybuffer";
ws.binaryType = "blob";
ws.binaryType = ""; // illegal
is(ws.binaryType, "blob");
ws.binaryType = "ArrayBuffer"; // illegal
is(ws.binaryType, "blob");
ws.binaryType = "Blob"; // illegal
is(ws.binaryType, "blob");
ws.binaryType = "mcfoofluu"; // illegal
is(ws.binaryType, "blob");
ws.close();
}
ws.onclose = function(e) {
ok(true, "test 43 close");
resolve();
}
});
}
function test44() {
return new Promise(function(resolve, reject) {
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-44");
is(ws.readyState, 0, "bad readyState in test-44!");
ws.binaryType = "arraybuffer";
ws.onopen = function() {
is(ws.readyState, 1, "open bad readyState in test-44!");
var buf = new ArrayBuffer(3);
// create byte view
var view = new Uint8Array(buf);
view[0] = 5;
view[1] = 0; // null byte
view[2] = 7;
ws.send(buf);
}
ws.onmessage = function(e) {
ok(e.data instanceof ArrayBuffer, "Should receive an arraybuffer!");
var view = new Uint8Array(e.data);
ok(view.length == 2 && view[0] == 0 && view[1] ==4, "testing Reply arraybuffer" );
ws.close();
}
ws.onclose = function(e) {
is(ws.readyState, 3, "onclose bad readyState in test-44!");
shouldCloseCleanly(e);
resolve();
}
});
}
function test45()
{
return new Promise(function(resolve, reject) {
function test45Real(blobFile) {
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-45");
is(ws.readyState, 0, "bad readyState in test-45!");
// ws.binaryType = "blob"; // Don't need to specify: blob is the default
ws.onopen = function() {
is(ws.readyState, 1, "open bad readyState in test-45!");
ws.send(blobFile);
}
var test45blob;
ws.onmessage = function(e) {
test45blob = e.data;
ok(test45blob instanceof Blob, "We should be receiving a Blob");
ws.close();
}
ws.onclose = function(e) {
is(ws.readyState, 3, "onclose bad readyState in test-45!");
shouldCloseCleanly(e);
// check blob contents
var reader = new FileReader();
reader.onload = function(event) {
is(reader.result, "flob", "response should be 'flob': got '"
+ reader.result + "'");
}
reader.onerror = function(event) {
testFailed("Failed to read blob: error code = " + reader.error.code);
}
reader.onloadend = function(event) {
resolve();
}
reader.readAsBinaryString(test45blob);
}
}
SpecialPowers.createFiles([{name: "testBlobFile", data: "flob"}],
function(files) {
test45Real(files[0]);
},
function(msg) {
testFailed("Failed to create file for test45: " + msg);
resolve();
});
});
}
function test46() {
return new Promise(function(resolve, reject) {
var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-46");
is(ws.readyState, 0, "create bad readyState in test-46!");
ws.onopen = function() {
is(ws.readyState, 1, "open bad readyState in test-46!");
ws.close()
is(ws.readyState, 2, "close must set readyState to 2 in test-46!");
}
ws.onmessage = function(e) {
ok(false, "received message after calling close in test-46!");
}
ws.onclose = function(e) {
is(ws.readyState, 3, "onclose bad readyState in test-46!");
shouldCloseCleanly(e);
resolve();
}
});
}
function test47() {
return new Promise(function(resolve, reject) {
var hasError = false;
var ws = CreateTestWS("ws://another.websocket.server.that.probably.does.not.exist");
ws.onopen = shouldNotOpen;
ws.onerror = function (e) {
is(ws.readyState, 3, "test-47: readyState should be CLOSED(3) in onerror: got "
+ ws.readyState);
ok(!ws._withinClose, "onerror() called during close()!");
hasError = true;
}
ws.onclose = function(e) {
shouldCloseNotCleanly(e);
ok(hasError, "test-47: should have called onerror before onclose");
is(ws.readyState, 3, "test-47: readyState should be CLOSED(3) in onclose: got "
+ ws.readyState);
ok(!ws._withinClose, "onclose() called during close()!");
is(e.code, 1006, "test-47 close code should be 1006 but is:" + e.code);
resolve();
}
// Call close before we're connected: throws error
// Make sure we call onerror/onclose asynchronously
ws._withinClose = 1;
ws.close(3333, "Closed before we were open: error");
ws._withinClose = 0;
is(ws.readyState, 2, "test-47: readyState should be CLOSING(2) after close(): got "
+ ws.readyState);
});
}
var tests = [
test41, // HSTS
test42, // non-char utf-8 sequences
test43, // Test setting binaryType attribute
test44, // Test sending/receving binary ArrayBuffer
test45, // Test sending/receving binary Blob
test46, // Test that we don't dispatch incoming msgs once in CLOSING state
test47, // Make sure onerror/onclose aren't called during close()
];
function testWebSocket() {
doTest();
}
</script>
<div id="feedback">
</div>
</body>
</html>

View File

@ -0,0 +1,62 @@
var current_test = 0;
function shouldNotOpen(e) {
var ws = e.target;
ok(false, "onopen shouldn't be called on test " + ws._testNumber + "!");
}
function shouldCloseCleanly(e) {
var ws = e.target;
ok(e.wasClean, "the ws connection in test " + ws._testNumber + " should be closed cleanly");
}
function shouldCloseNotCleanly(e) {
var ws = e.target;
ok(!e.wasClean, "the ws connection in test " + ws._testNumber + " shouldn't be closed cleanly");
}
function ignoreError(e) {
}
function CreateTestWS(ws_location, ws_protocol) {
var ws;
try {
if (ws_protocol == undefined) {
ws = new WebSocket(ws_location);
} else {
ws = new WebSocket(ws_location, ws_protocol);
}
ws._testNumber = current_test;
ok(true, "Created websocket for test " + ws._testNumber +"\n");
ws.onerror = function(e) {
ok(false, "onerror called on test " + e.target._testNumber + "!");
}
} catch (e) {
throw e;
}
return ws;
}
function forcegc() {
SpecialPowers.forceGC();
SpecialPowers.gc();
}
function doTest() {
if (current_test >= tests.length) {
SimpleTest.finish();
return;
}
$("feedback").innerHTML = "executing test: " + (current_test+1) + " of " + tests.length + " tests.";
tests[current_test++]().then(doTest);
}
SimpleTest.requestFlakyTimeout("The web socket tests are really fragile, but avoiding timeouts might be hard, since it's testing stuff on the network. " +
"Expect all sorts of flakiness in this test...");
SimpleTest.waitForExplicitFinish();

View File

@ -30,7 +30,7 @@
#include "xpcprivate.h"
#include "XrayWrapper.h"
#include "nsPrintfCString.h"
#include "prprf.h"
#include "mozilla/Snprintf.h"
#include "nsGlobalWindow.h"
#include "mozilla/dom/ScriptSettings.h"
@ -57,7 +57,7 @@ namespace dom {
JSErrorFormatString ErrorFormatString[] = {
#define MSG_DEF(_name, _argc, _exn, _str) \
{ _str, _argc, _exn },
{ #_name, _str, _argc, _exn },
#include "mozilla/dom/Errors.msg"
#undef MSG_DEF
};
@ -2424,13 +2424,13 @@ ConvertJSValueToByteString(JSContext* cx, JS::Handle<JS::Value> v,
// 20 digits, plus one more for the null terminator.
char index[21];
static_assert(sizeof(size_t) <= 8, "index array too small");
PR_snprintf(index, sizeof(index), "%d", badCharIndex);
snprintf_literal(index, "%d", badCharIndex);
// A char16_t is 16 bits long. The biggest unsigned 16 bit
// number (65,535) has 5 digits, plus one more for the null
// terminator.
char badCharArray[6];
static_assert(sizeof(char16_t) <= 2, "badCharArray too small");
PR_snprintf(badCharArray, sizeof(badCharArray), "%d", badChar);
snprintf_literal(badCharArray, "%d", badChar);
ThrowErrorMessage(cx, MSG_INVALID_BYTESTRING, index, badCharArray);
return false;
}

View File

@ -24,6 +24,7 @@
#include "mozilla/dom/NonRefcountedDOMObject.h"
#include "mozilla/dom/Nullable.h"
#include "mozilla/dom/RootedDictionary.h"
#include "mozilla/SegmentedVector.h"
#include "mozilla/dom/workers/Workers.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/Likely.h"
@ -2849,27 +2850,27 @@ struct DeferredFinalizerImpl
typename Conditional<IsRefcounted<T>::value,
RefPtr<T>,
nsAutoPtr<T>>::Type>::Type SmartPtr;
typedef nsTArray<SmartPtr> SmartPtrArray;
typedef SegmentedVector<SmartPtr> SmartPtrArray;
static_assert(IsSame<T, nsISupports>::value || !IsBaseOf<nsISupports, T>::value,
"nsISupports classes should all use the nsISupports instantiation");
static inline void
AppendAndTake(nsTArray<nsCOMPtr<nsISupports>>& smartPtrArray, nsISupports* ptr)
AppendAndTake(SegmentedVector<nsCOMPtr<nsISupports>>& smartPtrArray, nsISupports* ptr)
{
smartPtrArray.AppendElement(dont_AddRef(ptr));
smartPtrArray.InfallibleAppend(dont_AddRef(ptr));
}
template<class U>
static inline void
AppendAndTake(nsTArray<RefPtr<U>>& smartPtrArray, U* ptr)
AppendAndTake(SegmentedVector<RefPtr<U>>& smartPtrArray, U* ptr)
{
smartPtrArray.AppendElement(dont_AddRef(ptr));
smartPtrArray.InfallibleAppend(dont_AddRef(ptr));
}
template<class U>
static inline void
AppendAndTake(nsTArray<nsAutoPtr<U>>& smartPtrArray, U* ptr)
AppendAndTake(SegmentedVector<nsAutoPtr<U>>& smartPtrArray, U* ptr)
{
smartPtrArray.AppendElement(ptr);
smartPtrArray.InfallibleAppend(ptr);
}
static void*
@ -2892,7 +2893,7 @@ struct DeferredFinalizerImpl
aSlice = oldLen;
}
uint32_t newLen = oldLen - aSlice;
pointers->RemoveElementsAt(newLen, aSlice);
pointers->PopLastN(aSlice);
if (newLen == 0) {
delete pointers;
return true;

View File

@ -615,7 +615,7 @@ BluetoothSocket::Listen(const nsAString& aServiceName,
bool aAuth, bool aEncrypt)
{
nsAutoPtr<BluetoothUnixSocketConnector> connector(
new BluetoothUnixSocketConnector(BluetoothAddress::ANY, aType,
new BluetoothUnixSocketConnector(BluetoothAddress::ANY(), aType,
aChannel, aAuth, aEncrypt));
nsresult rv = Listen(connector);

View File

@ -5,7 +5,7 @@
[DEFAULT]
subsuite = webgl
skip-if = e10s || os == 'b2g' || ((os == 'linux') && (buildapp == 'b2g')) || ((os == 'linux') && (buildapp == 'mulet'))
skip-if = os == 'b2g' || ((os == 'linux') && (buildapp == 'b2g')) || ((os == 'linux') && (buildapp == 'mulet'))
support-files = webgl-conformance/../webgl-mochitest/driver-info.js
webgl-conformance/always-fail.html

View File

@ -6,7 +6,7 @@
#include "nsContentUtils.h"
#include "nsIDocument.h"
#include "prprf.h"
#include "mozilla/Snprintf.h"
#include "nsGlobalWindow.h"
#include "ScriptSettings.h"
#include "mozilla/DOMEventTargetHelper.h"
@ -31,8 +31,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(DOMEventTargetHelper)
if (tmp->mOwnerWindow && tmp->mOwnerWindow->GetExtantDoc()) {
tmp->mOwnerWindow->GetExtantDoc()->GetDocumentURI(uri);
}
PR_snprintf(name, sizeof(name), "DOMEventTargetHelper %s",
NS_ConvertUTF16toUTF8(uri).get());
snprintf_literal(name, "DOMEventTargetHelper %s",
NS_ConvertUTF16toUTF8(uri).get());
cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
} else {
NS_IMPL_CYCLE_COLLECTION_DESCRIBE(DOMEventTargetHelper, tmp->mRefCnt.get())

View File

@ -95,15 +95,11 @@ function doTest1(expectedEventLog,focusAfterCloseId) {
}
function setPrefAndDoTest(expectedEventLog,focusAfterCloseId,prefValue) {
var origPrefValue = SpecialPowers.getIntPref("browser.link.open_newwindow");
var select1 = document.getElementById('Select1');
select1.blur();
result = "";
log({},"Test with browser.link.open_newwindow = "+prefValue);
try {
SpecialPowers.setIntPref("browser.link.open_newwindow", prefValue);
doTest1(expectedEventLog,focusAfterCloseId);
} finally {
SpecialPowers.setIntPref("browser.link.open_newwindow", origPrefValue);
}
SpecialPowers.pushPrefEnv({"set": [['browser.link.open_newwindow', prefValue]]}, function() {
doTest1(expectedEventLog,focusAfterCloseId);
});
}

View File

@ -53,7 +53,7 @@ INPUT(popupText1): blur \n\
setPrefAndDoTest(eventLogForNewWindow,'Body',2); // 2 = open new window as window
}
todo(false, "Please write a test for bug 299673 that actually works");
todo(false, "Please write a test for bug 299673 that actually works, see bug 553417");
</script>
</pre>

View File

@ -41,6 +41,7 @@ SELECT(Select1): focus \n\
SELECT(Select1): change \n\
: >>> OpenWindow\n\
: blur top-doc\n\
: focus popup-doc\n\
INPUT(popupText1): focus \n\
: <<< OpenWindow\n\
SELECT(Select1): blur \n\
@ -49,10 +50,9 @@ INPUT(popupText1): blur \n\
: focus top-doc\n\
'
setPrefAndDoTest(eventLogForNewTab,'Body',3); // 3 = open new window as tab
}
todo(false, "Please write a test for bug 299673 that actually works");
todo(false, "Please write a test for bug 299673 that actually works, see bug 553417");
</script>
</pre>

View File

@ -407,21 +407,17 @@ NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGeolocationRequest)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGeolocationRequest)
NS_IMPL_CYCLE_COLLECTION(nsGeolocationRequest, mCallback, mErrorCallback, mLocator)
void
nsGeolocationRequest::Notify()
{
StopTimeoutTimer();
SetTimeoutTimer();
NotifyErrorAndShutdown(nsIDOMGeoPositionError::TIMEOUT);
}
void
nsGeolocationRequest::NotifyErrorAndShutdown(uint16_t aErrorCode)
{
MOZ_ASSERT(!mShutdown, "timeout after shutdown");
if (!mIsWatchPositionRequest) {
Shutdown();
mLocator->RemoveRequest(this);
@ -718,16 +714,13 @@ nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition)
callback->Call(*wrapped);
} else {
nsIDOMGeoPositionCallback* callback = mCallback.GetXPCOMCallback();
MOZ_ASSERT(callback);
callback->HandleEvent(aPosition);
}
StopTimeoutTimer();
SetTimeoutTimer();
MOZ_ASSERT(mShutdown || mIsWatchPositionRequest,
"non-shutdown getCurrentPosition request after callback!");
}
nsIPrincipal*
nsGeolocationRequest::GetPrincipal()
{
@ -745,22 +738,10 @@ nsGeolocationRequest::Update(nsIDOMGeoPosition* aPosition)
NS_DispatchToMainThread(ev);
return NS_OK;
}
NS_IMETHODIMP
nsGeolocationRequest::LocationUpdatePending()
{
if (!mTimeoutTimer) {
SetTimeoutTimer();
}
return NS_OK;
}
NS_IMETHODIMP
nsGeolocationRequest::NotifyError(uint16_t aErrorCode)
{
MOZ_ASSERT(NS_IsMainThread());
RefPtr<PositionError> positionError = new PositionError(mLocator, aErrorCode);
positionError->NotifyCallback(mErrorCallback);
return NS_OK;
@ -1030,30 +1011,17 @@ nsGeolocationService::Update(nsIDOMGeoPosition *aSomewhere)
if (aSomewhere) {
SetCachedPosition(aSomewhere);
}
for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
mGeolocators[i]->Update(aSomewhere);
}
return NS_OK;
}
NS_IMETHODIMP
nsGeolocationService::LocationUpdatePending()
{
for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
mGeolocators[i]->LocationUpdatePending();
}
return NS_OK;
}
NS_IMETHODIMP
nsGeolocationService::NotifyError(uint16_t aErrorCode)
{
for (uint32_t i = 0; i < mGeolocators.Length(); i++) {
mGeolocators[i]->NotifyError(aErrorCode);
}
return NS_OK;
}
@ -1476,21 +1444,8 @@ Geolocation::Update(nsIDOMGeoPosition *aSomewhere)
for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
mWatchingCallbacks[i]->Update(aSomewhere);
}
return NS_OK;
}
NS_IMETHODIMP
Geolocation::LocationUpdatePending()
{
// this event is only really interesting for watch callbacks
for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
mWatchingCallbacks[i]->LocationUpdatePending();
}
return NS_OK;
}
NS_IMETHODIMP
Geolocation::NotifyError(uint16_t aErrorCode)
{
@ -1498,7 +1453,6 @@ Geolocation::NotifyError(uint16_t aErrorCode)
Shutdown();
return NS_OK;
}
mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_ERROR, true);
for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) {

View File

@ -6,6 +6,7 @@
#include "ImageDocument.h"
#include "mozilla/dom/ImageDocumentBinding.h"
#include "mozilla/dom/HTMLImageElement.h"
#include "nsRect.h"
#include "nsIImageLoadingContent.h"
#include "nsGenericHTMLElement.h"
@ -336,6 +337,23 @@ ImageDocument::ShrinkToFit()
}
if (GetZoomLevel() != mOriginalZoomLevel && mImageIsResized &&
!nsContentUtils::IsChildOfSameType(this)) {
// If we're zoomed, so that we don't maintain the invariant that
// mImageIsResized if and only if its displayed width/height fit in
// mVisibleWidth/mVisibleHeight, then we may need to switch to/from the
// overflowingVertical class here, because our viewport size may have
// changed and we don't plan to adjust the image size to compensate. Since
// mImageIsResized it has a "height" attribute set, and we can just get the
// displayed image height by getting .height on the HTMLImageElement.
HTMLImageElement* img = HTMLImageElement::FromContent(mImageContent);
uint32_t imageHeight = img->Height();
nsDOMTokenList* classList = img->ClassList();
ErrorResult ignored;
if (imageHeight > mVisibleHeight) {
classList->Add(NS_LITERAL_STRING("overflowingVertical"), ignored);
} else {
classList->Remove(NS_LITERAL_STRING("overflowingVertical"), ignored);
}
ignored.SuppressException();
return;
}
@ -501,7 +519,7 @@ void
ImageDocument::SetModeClass(eModeClasses mode)
{
nsDOMTokenList* classList = mImageContent->AsElement()->ClassList();
mozilla::ErrorResult rv;
ErrorResult rv;
if (mode == eShrinkToFit) {
classList->Add(NS_LITERAL_STRING("shrinkToFit"), rv);
@ -520,6 +538,8 @@ ImageDocument::SetModeClass(eModeClasses mode)
} else {
classList->Remove(NS_LITERAL_STRING("overflowingHorizontalOnly"), rv);
}
rv.SuppressException();
}
nsresult

View File

@ -1918,7 +1918,7 @@ nsTextEditorState::GetValue(nsAString& aValue, bool aIgnoreWrap) const
if (!mTextCtrlElement->ValueChanged() || !mValue) {
mTextCtrlElement->GetDefaultValueFromContent(aValue);
} else {
aValue = NS_ConvertUTF8toUTF16(*mValue);
aValue = *mValue;
}
}
}
@ -2139,7 +2139,7 @@ nsTextEditorState::SetValue(const nsAString& aValue, uint32_t aFlags)
}
} else {
if (!mValue) {
mValue = new nsCString;
mValue.emplace();
}
nsString value;
if (!value.Assign(newValue, fallible)) {
@ -2148,7 +2148,7 @@ nsTextEditorState::SetValue(const nsAString& aValue, uint32_t aFlags)
if (!nsContentUtils::PlatformToDOMLineBreaks(value, fallible)) {
return false;
}
if (!CopyUTF16toUTF8(value, *mValue, fallible)) {
if (!mValue->Assign(value, fallible)) {
return false;
}

View File

@ -14,6 +14,7 @@
#include "nsCycleCollectionParticipant.h"
#include "mozilla/dom/Element.h"
#include "mozilla/Attributes.h"
#include "mozilla/Maybe.h"
#include "mozilla/WeakPtr.h"
class nsTextInputListener;
@ -293,7 +294,7 @@ private:
nsCOMPtr<mozilla::dom::Element> mPlaceholderDiv;
nsTextControlFrame* mBoundFrame;
RefPtr<nsTextInputListener> mTextListener;
nsAutoPtr<nsCString> mValue;
mozilla::Maybe<nsString> mValue;
RefPtr<nsAnonDivObserver> mMutationObserver;
mutable nsString mCachedValue; // Caches non-hard-wrapped value on a multiline control.
// mValueBeingSet is available only while SetValue() is requesting to commit

View File

@ -5,6 +5,7 @@
* 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/. */
using base::ProcessId from "base/process.h";
using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
namespace mozilla {
@ -19,6 +20,7 @@ struct SlowScriptData
struct PluginHangData
{
uint32_t pluginId;
ProcessId contentProcessId;
};
union HangData

View File

@ -428,7 +428,8 @@ HangMonitorChild::NotifyPluginHangAsync(uint32_t aPluginId)
// bounce back to parent on background thread
if (mIPCOpen) {
Unused << SendHangEvidence(PluginHangData(aPluginId));
Unused << SendHangEvidence(PluginHangData(aPluginId,
base::GetCurrentProcId()));
}
}
@ -819,7 +820,8 @@ HangMonitoredProcess::TerminatePlugin()
// generates a crash report that includes a browser report taken here
// earlier, the content process, and any plugin process(es).
uint32_t id = mHangData.get_PluginHangData().pluginId();
plugins::TerminatePlugin(id, NS_LITERAL_CSTRING("HangMonitor"),
base::ProcessId contentPid = mHangData.get_PluginHangData().contentProcessId();
plugins::TerminatePlugin(id, contentPid, NS_LITERAL_CSTRING("HangMonitor"),
mBrowserDumpId);
if (mActor) {

View File

@ -10,3 +10,25 @@ ImageMapPolyOddNumberOfCoords=The "coords" attribute of the <area shape="poly">
TablePartRelPosWarning=Relative positioning of table rows and row groups is now supported. This site may need to be updated because it may depend on this feature having no effect.
ScrollLinkedEffectFound2=This site appears to use a scroll-linked positioning effect. This may not work well with asynchronous panning; see https://developer.mozilla.org/docs/Mozilla/Performance/ScrollLinkedEffects for further details and to join the discussion on related tools and features!
## LOCALIZATION NOTE(AnimationWarningContentTooLarge):
## (%1$S, %2$S) is a pair of integer values of the frame size
## (%3$S, %4$S) is a pair of integer values of the viewport size
## (%5$S, %6$S) is a pair of integer values of the visual rectangle size
## (%7$S) is an integer value
AnimationWarningContentTooLarge=Async animation disabled because frame size (%1$S, %2$S) is bigger than the viewport (%3$S, %4$S) or the visual rectangle (%5$S, %6$S) is larger than the max allowed value (%7$S)
## LOCALIZATION NOTE(AnimationWarningTransformBackfaceVisibilityHidde):
## 'backface-visibility: hidden' is a CSS property, don't translate it.
AnimationWarningTransformBackfaceVisibilityHidden=Async animation of 'backface-visibility: hidden' transforms is not supported
## LOCALIZATION NOTE(AnimationWarningTransformPreserve3D):
## 'transform-style: preserve-3d' is a CSS property, don't translate it.
AnimationWarningTransformPreserve3D=Async animation of 'transform-style: preserve-3d' transforms is not supported
## LOCALIZATION NOTE(AnimationWarningTransformSVG,
## AnimationWarningTransformFrameInactive,
## AnimationWarningOpacityFrameInactive,
## AnimationWarningWithGeometricProperties):
## 'transform' and 'opacity' mean CSS property names, don't translate it.
AnimationWarningTransformSVG=Async 'transform' animations of aFrames with SVG transforms is not supported
AnimationWarningTransformFrameInactive=Async animation disabled because frame was not marked active for 'transform' animation
AnimationWarningOpacityFrameInactive=Async animation disabled because frame was not marked active for 'opacity' animation
AnimationWarningWithGeometricProperties=Async animation of 'transform' or 'opacity' not possible due to animation of geometric properties on the same element

View File

@ -54,8 +54,8 @@
#include "nsNullPrincipal.h"
#include "nsVariant.h"
// For PR_snprintf
#include "prprf.h"
// For snprintf
#include "mozilla/Snprintf.h"
#include "nsJSUtils.h"
#include "nsGlobalWindow.h"
@ -2540,7 +2540,7 @@ MediaManager::RemoveWindowID(uint64_t aWindowId)
// Notify the UI that this window no longer has gUM active
char windowBuffer[32];
PR_snprintf(windowBuffer, sizeof(windowBuffer), "%llu", outerID);
snprintf_literal(windowBuffer, "%" PRIu64, outerID);
nsString data = NS_ConvertUTF8toUTF16(windowBuffer);
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();

View File

@ -28,6 +28,7 @@
#include "mozilla/Logging.h"
#include "nsServiceManagerUtils.h"
#include "gfxPlatform.h"
#include "mozilla/Snprintf.h"
#ifdef MOZ_WIDGET_ANDROID
#include "AndroidBridge.h"

View File

@ -9,6 +9,7 @@
#include "MediaData.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/Logging.h"
#include "mozilla/Snprintf.h"
extern mozilla::LogModule* GetSourceBufferResourceLog();
@ -164,7 +165,7 @@ ResourceQueue::Dump(const char* aPath)
ResourceItem* item = ResourceAt(i);
char buf[255];
PR_snprintf(buf, sizeof(buf), "%s/%08u.bin", aPath, i);
snprintf_literal(buf, "%s/%08u.bin", aPath, i);
FILE* fp = fopen(buf, "wb");
if (!fp) {
return;

View File

@ -18,7 +18,8 @@
#include "nsAutoRef.h"
#include "NesteggPacketHolder.h"
#include "XiphExtradata.h"
#include "prprf.h"
#include "prprf.h" // leaving it for PR_vsnprintf()
#include "mozilla/Snprintf.h"
#include <algorithm>
#include <stdint.h>
@ -117,7 +118,7 @@ static void webmdemux_log(nestegg* aContext,
va_start(args, aFormat);
PR_snprintf(msg, sizeof(msg), "%p [Nestegg-%s] ", aContext, sevStr);
snprintf_literal(msg, "%p [Nestegg-%s] ", aContext, sevStr);
PR_vsnprintf(msg+strlen(msg), sizeof(msg)-strlen(msg), aFormat, args);
MOZ_LOG(gNesteggLog, LogLevel::Debug, (msg));

View File

@ -13,6 +13,7 @@
#include "mozilla/dom/RTCCertificateBinding.h"
#include "mozilla/dom/WebCryptoCommon.h"
#include "mozilla/dom/WebCryptoTask.h"
#include "mozilla/Snprintf.h"
#include <cstdio>
@ -95,7 +96,7 @@ private:
char buf[sizeof(randomName) * 2 + 4];
PL_strncpy(buf, "CN=", 3);
for (size_t i = 0; i < sizeof(randomName); ++i) {
PR_snprintf(&buf[i * 2 + 3], 2, "%.2x", randomName[i]);
snprintf(&buf[i * 2 + 3], 2, "%.2x", randomName[i]);
}
buf[sizeof(buf) - 1] = '\0';

View File

@ -41,7 +41,7 @@ protocol PUDPSocket
manager PNecko or PBackground;
parent:
async Bind(UDPAddressInfo addressInfo, bool addressReuse, bool loopback);
async Bind(UDPAddressInfo addressInfo, bool addressReuse, bool loopback, uint32_t recvBufferSize);
async Connect(UDPAddressInfo addressInfo);
async OutgoingData(UDPData data, UDPSocketAddr addr);

View File

@ -503,7 +503,8 @@ UDPSocket::InitRemote(const nsAString& aLocalAddress,
NS_ConvertUTF16toUTF8(aLocalAddress),
aLocalPort,
mAddressReuse,
mLoopback);
mLoopback,
0);
if (NS_FAILED(rv)) {
return rv;

View File

@ -171,7 +171,8 @@ UDPSocketChild::Bind(nsIUDPSocketInternal* aSocket,
const nsACString& aHost,
uint16_t aPort,
bool aAddressReuse,
bool aLoopback)
bool aLoopback,
uint32_t recvBufferSize)
{
UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, PromiseFlatCString(aHost).get(), aPort));
@ -190,7 +191,7 @@ UDPSocketChild::Bind(nsIUDPSocketInternal* aSocket,
mFilterName);
}
SendBind(UDPAddressInfo(nsCString(aHost), aPort), aAddressReuse, aLoopback);
SendBind(UDPAddressInfo(nsCString(aHost), aPort), aAddressReuse, aLoopback, recvBufferSize);
return NS_OK;
}

View File

@ -161,11 +161,12 @@ UDPSocketParent::Init(const IPC::Principal& aPrincipal,
bool
UDPSocketParent::RecvBind(const UDPAddressInfo& aAddressInfo,
const bool& aAddressReuse, const bool& aLoopback)
const bool& aAddressReuse, const bool& aLoopback,
const uint32_t& recvBufferSize)
{
UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, aAddressInfo.addr().get(), aAddressInfo.port()));
if (NS_FAILED(BindInternal(aAddressInfo.addr(), aAddressInfo.port(), aAddressReuse, aLoopback))) {
if (NS_FAILED(BindInternal(aAddressInfo.addr(), aAddressInfo.port(), aAddressReuse, aLoopback, recvBufferSize))) {
FireInternalError(__LINE__);
return true;
}
@ -193,11 +194,12 @@ UDPSocketParent::RecvBind(const UDPAddressInfo& aAddressInfo,
nsresult
UDPSocketParent::BindInternal(const nsCString& aHost, const uint16_t& aPort,
const bool& aAddressReuse, const bool& aLoopback)
const bool& aAddressReuse, const bool& aLoopback,
const uint32_t& recvBufferSize)
{
nsresult rv;
UDPSOCKET_LOG(("%s: [this=%p] %s:%u addressReuse: %d loopback: %d", __FUNCTION__, this, nsCString(aHost).get(), aPort, aAddressReuse, aLoopback));
UDPSOCKET_LOG(("%s: [this=%p] %s:%u addressReuse: %d loopback: %d recvBufferSize: %lu", __FUNCTION__, this, nsCString(aHost).get(), aPort, aAddressReuse, aLoopback, recvBufferSize));
nsCOMPtr<nsIUDPSocket> sock =
do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv);
@ -243,6 +245,12 @@ UDPSocketParent::BindInternal(const nsCString& aHost, const uint16_t& aPort,
return rv;
}
}
if (recvBufferSize != 0) {
rv = sock->SetRecvBufferSize(recvBufferSize);
if (NS_WARN_IF(NS_FAILED(rv))) {
UDPSOCKET_LOG(("%s: [this=%p] %s:%u failed to set recv buffer size to: %lu", __FUNCTION__, this, nsCString(aHost).get(), aPort, recvBufferSize));
}
}
// register listener
rv = sock->AsyncListen(this);

View File

@ -35,7 +35,8 @@ public:
bool Init(const IPC::Principal& aPrincipal, const nsACString& aFilter);
virtual bool RecvBind(const UDPAddressInfo& aAddressInfo,
const bool& aAddressReuse, const bool& aLoopback) override;
const bool& aAddressReuse, const bool& aLoopback,
const uint32_t& recvBufferSize) override;
virtual bool RecvConnect(const UDPAddressInfo& aAddressInfo) override;
void DoSendConnectResponse(const UDPAddressInfo& aAddressInfo);
void SendConnectResponse(nsIEventTarget *aThread,
@ -62,7 +63,8 @@ private:
void Send(const InfallibleTArray<uint8_t>& aData, const UDPSocketAddr& aAddr);
void Send(const InputStreamParams& aStream, const UDPSocketAddr& aAddr);
nsresult BindInternal(const nsCString& aHost, const uint16_t& aPort,
const bool& aAddressReuse, const bool& aLoopback);
const bool& aAddressReuse, const bool& aLoopback,
const uint32_t& recvBufferSize);
nsresult ConnectInternal(const nsCString& aHost, const uint16_t& aPort);
void FireInternalError(uint32_t aLineNo);
void SendInternalError(nsIEventTarget *aThread,

View File

@ -32,7 +32,7 @@ interface nsIUDPSocketChild : nsISupports
// Tell the chrome process to bind the UDP socket to a given local host and port
void bind(in nsIUDPSocketInternal socket, in nsIPrincipal principal,
in AUTF8String host, in unsigned short port,
in bool addressReuse, in bool loopback);
in bool addressReuse, in bool loopback, in uint32_t recvBufferSize);
// Tell the chrome process to connect the UDP socket to a given remote host and port
void connect(in nsIUDPSocketInternal socket, in AUTF8String host, in unsigned short port);

View File

@ -73,7 +73,7 @@
#include "nsIDOMWindow.h"
#include "nsNetCID.h"
#include "prprf.h"
#include "mozilla/Snprintf.h"
#include "nsThreadUtils.h"
#include "nsIInputStreamTee.h"
#include "nsQueryObject.h"
@ -3785,8 +3785,8 @@ nsPluginHost::ParsePostBufferToFixHeaders(const char *inPostData, uint32_t inPos
newBufferLen = dataLen + l;
if (!(*outPostData = p = (char*)moz_xmalloc(newBufferLen)))
return NS_ERROR_OUT_OF_MEMORY;
headersLen = PR_snprintf(p, l,"%s: %ld%s", ContentLenHeader, dataLen, CRLFCRLF);
if (headersLen == l) { // if PR_snprintf has ate all extra space consider this as an error
headersLen = snprintf(p, l,"%s: %u%s", ContentLenHeader, dataLen, CRLFCRLF);
if (headersLen == l) { // if snprintf has ate all extra space consider this as an error
free(p);
*outPostData = 0;
return NS_ERROR_FAILURE;

View File

@ -7,6 +7,8 @@
#ifndef mozilla_plugins_PluginBridge_h
#define mozilla_plugins_PluginBridge_h
#include "base/process.h"
namespace mozilla {
namespace dom {
@ -26,6 +28,7 @@ FindPluginsForContent(uint32_t aPluginEpoch,
void
TerminatePlugin(uint32_t aPluginId,
base::ProcessId aContentProcessId,
const nsCString& aMonitorDescription,
const nsAString& aBrowserDumpId);

View File

@ -9,6 +9,7 @@
#include "PluginHangUIParent.h"
#include "mozilla/Telemetry.h"
#include "mozilla/ipc/ProtocolUtils.h"
#include "mozilla/plugins/PluginModuleParent.h"
#include "nsContentUtils.h"
@ -354,6 +355,7 @@ PluginHangUIParent::RecvUserResponse(const unsigned int& aResponse)
if (aResponse & HANGUI_USER_RESPONSE_STOP) {
// User clicked Stop
mModule->TerminateChildProcess(mMainThreadMessageLoop,
mozilla::ipc::kInvalidProcessId,
NS_LITERAL_CSTRING("ModalHangUI"),
EmptyString());
responseCode = 1;

View File

@ -17,6 +17,7 @@
#include "mozilla/dom/PCrashReporterParent.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/ipc/MessageChannel.h"
#include "mozilla/ipc/ProtocolUtils.h"
#include "mozilla/plugins/BrowserStreamParent.h"
#include "mozilla/plugins/PluginAsyncSurrogate.h"
#include "mozilla/plugins/PluginBridge.h"
@ -127,7 +128,9 @@ mozilla::plugins::SetupBridge(uint32_t aPluginId,
if (NS_FAILED(*rv)) {
return true;
}
chromeParent->SetContentParent(aContentParent);
if (chromeParent->IsStartingAsync()) {
chromeParent->SetContentParent(aContentParent);
}
if (!aForceBridgeNow && chromeParent->IsStartingAsync() &&
PluginModuleChromeParent::DidInstantiate()) {
// We'll handle the bridging asynchronously
@ -358,6 +361,7 @@ bool PluginModuleMapping::sIsLoadModuleOnStack = false;
void
mozilla::plugins::TerminatePlugin(uint32_t aPluginId,
base::ProcessId aContentProcessId,
const nsCString& aMonitorDescription,
const nsAString& aBrowserDumpId)
{
@ -372,6 +376,7 @@ mozilla::plugins::TerminatePlugin(uint32_t aPluginId,
PluginModuleChromeParent* chromeParent =
static_cast<PluginModuleChromeParent*>(plugin->GetLibrary());
chromeParent->TerminateChildProcess(MessageLoop::current(),
aContentProcessId,
aMonitorDescription,
aBrowserDumpId);
}
@ -473,7 +478,8 @@ PluginModuleContentParent::OnLoadPluginResult(const uint32_t& aPluginId,
void
PluginModuleChromeParent::SetContentParent(dom::ContentParent* aContentParent)
{
MOZ_ASSERT(aContentParent);
// mContentParent is to be used ONLY during async plugin init!
MOZ_ASSERT(aContentParent && mIsStartingAsync);
mContentParent = aContentParent;
}
@ -713,7 +719,7 @@ PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath,
, mPluginId(aPluginId)
, mChromeTaskFactory(this)
, mHangAnnotationFlags(0)
, mHangAnnotatorMutex("PluginModuleChromeParent::mHangAnnotatorMutex")
, mProtocolCallStackMutex("PluginModuleChromeParent::mProtocolCallStackMutex")
#ifdef XP_WIN
, mPluginCpuUsageOnHang()
, mHangUIParent(nullptr)
@ -985,14 +991,14 @@ PluginModuleChromeParent::OnEnteredCall()
{
mozilla::ipc::IProtocol* protocol = GetInvokingProtocol();
MOZ_ASSERT(protocol);
mozilla::MutexAutoLock lock(mHangAnnotatorMutex);
mozilla::MutexAutoLock lock(mProtocolCallStackMutex);
mProtocolCallStack.AppendElement(protocol);
}
void
PluginModuleChromeParent::OnExitedCall()
{
mozilla::MutexAutoLock lock(mHangAnnotatorMutex);
mozilla::MutexAutoLock lock(mProtocolCallStackMutex);
MOZ_ASSERT(!mProtocolCallStack.IsEmpty());
mProtocolCallStack.RemoveElementAt(mProtocolCallStack.Length() - 1);
}
@ -1002,14 +1008,14 @@ PluginModuleChromeParent::OnEnteredSyncSend()
{
mozilla::ipc::IProtocol* protocol = GetInvokingProtocol();
MOZ_ASSERT(protocol);
mozilla::MutexAutoLock lock(mHangAnnotatorMutex);
mozilla::MutexAutoLock lock(mProtocolCallStackMutex);
mProtocolCallStack.AppendElement(protocol);
}
void
PluginModuleChromeParent::OnExitedSyncSend()
{
mozilla::MutexAutoLock lock(mHangAnnotatorMutex);
mozilla::MutexAutoLock lock(mProtocolCallStackMutex);
MOZ_ASSERT(!mProtocolCallStack.IsEmpty());
mProtocolCallStack.RemoveElementAt(mProtocolCallStack.Length() - 1);
}
@ -1171,6 +1177,7 @@ PluginModuleChromeParent::ShouldContinueFromReplyTimeout()
FinishHangUI();
#endif // XP_WIN
TerminateChildProcess(MessageLoop::current(),
mozilla::ipc::kInvalidProcessId,
NS_LITERAL_CSTRING("ModalHangUI"),
EmptyString());
GetIPCChannel()->CloseWithTimeout();
@ -1196,6 +1203,7 @@ PluginModuleContentParent::OnExitedSyncSend()
void
PluginModuleChromeParent::TerminateChildProcess(MessageLoop* aMsgLoop,
base::ProcessId aContentPid,
const nsCString& aMonitorDescription,
const nsAString& aBrowserDumpId)
{
@ -1281,9 +1289,9 @@ PluginModuleChromeParent::TerminateChildProcess(MessageLoop* aMsgLoop,
additionalDumps.AppendLiteral(",flash2");
}
#endif
if (mContentParent) {
if (aContentPid != mozilla::ipc::kInvalidProcessId) {
// Include the content process minidump
if (CreatePluginMinidump(mContentParent->OtherPid(), 0,
if (CreatePluginMinidump(aContentPid, 0,
pluginDumpFile,
NS_LITERAL_CSTRING("content"))) {
additionalDumps.AppendLiteral(",content");
@ -2239,7 +2247,9 @@ PluginModuleChromeParent::RecvNP_InitializeResult(const NPError& aError)
}
}
mNPInitialized = initOk;
return mContentParent->SendLoadPluginResult(mPluginId, initOk);
bool result = mContentParent->SendLoadPluginResult(mPluginId, initOk);
mContentParent = nullptr;
return result;
}
#else
@ -2346,9 +2356,10 @@ PluginModuleChromeParent::RecvNP_InitializeResult(const NPError& aError)
if ((ok = SendAssociatePluginId())) {
ok = mContentParent->SendLoadPluginResult(mPluginId,
aError == NPERR_NO_ERROR);
mContentParent = nullptr;
}
} else if (aError == NPERR_NO_ERROR) {
// Initialization steps when e10s is disabled
// Initialization steps for (e10s && !asyncInit) || !e10s
#if defined XP_WIN
if (mIsStartingAsync) {
SetPluginFuncs(mNPPIface);

View File

@ -418,6 +418,8 @@ class PluginModuleChromeParent
*
* @param aMsgLoop the main message pump associated with the module
* protocol.
* @param aContentPid PID of the e10s content process from which a hang was
* reported. May be kInvalidProcessId if not applicable.
* @param aMonitorDescription a string describing the hang monitor that
* is making this call. This string is added to the crash reporter
* annotations for the plugin process.
@ -427,6 +429,7 @@ class PluginModuleChromeParent
* dump will be taken at the time of this call.
*/
void TerminateChildProcess(MessageLoop* aMsgLoop,
base::ProcessId aContentPid,
const nsCString& aMonitorDescription,
const nsAString& aBrowserDumpId);
@ -547,7 +550,7 @@ private:
kHangUIDontShow = (1u << 3)
};
Atomic<uint32_t> mHangAnnotationFlags;
mozilla::Mutex mHangAnnotatorMutex;
mozilla::Mutex mProtocolCallStackMutex;
InfallibleTArray<mozilla::ipc::IProtocol*> mProtocolCallStack;
#ifdef XP_WIN
InfallibleTArray<float> mPluginCpuUsageOnHang;
@ -625,6 +628,10 @@ private:
bool mInitOnAsyncConnect;
nsresult mAsyncInitRv;
NPError mAsyncInitError;
// mContentParent is to be used ONLY during the IPC dance that occurs
// when ContentParent::RecvLoadPlugin is called under async plugin init!
// In other contexts it is *unsafe*, as there might be multiple content
// processes in existence!
dom::ContentParent* mContentParent;
nsCOMPtr<nsIObserver> mOfflineObserver;
#ifdef MOZ_ENABLE_PROFILER_SPS

View File

@ -24,9 +24,6 @@ const {PushCrypto} = Cu.import("resource://gre/modules/PushCrypto.jsm");
// Currently supported protocols: WebSocket.
const CONNECTION_PROTOCOLS = [PushServiceWebSocket, PushServiceHttp2];
XPCOMUtils.defineLazyModuleGetter(this, "AlarmService",
"resource://gre/modules/AlarmService.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "gContentSecurityManager",
"@mozilla.org/contentsecuritymanager;1",
"nsIContentSecurityManager");
@ -96,7 +93,6 @@ this.PushService = {
_state: PUSH_SERVICE_UNINIT,
_db: null,
_options: null,
_alarmID: null,
_visibleNotifications: new Map(),
// Callback that is called after attempting to
@ -548,13 +544,11 @@ this.PushService = {
return;
}
this.stopAlarm();
this._stopObservers();
this._service.disconnect();
this._service.uninit();
this._service = null;
this.stopAlarm();
if (!this._db) {
return Promise.resolve();
@ -609,57 +603,6 @@ this.PushService = {
});
},
/** |delay| should be in milliseconds. */
setAlarm: function(delay) {
if (this._state <= PUSH_SERVICE_ACTIVATING) {
return;
}
// Bug 909270: Since calls to AlarmService.add() are async, calls must be
// 'queued' to ensure only one alarm is ever active.
if (this._settingAlarm) {
// onSuccess will handle the set. Overwriting the variable enforces the
// last-writer-wins semantics.
this._queuedAlarmDelay = delay;
this._waitingForAlarmSet = true;
return;
}
// Stop any existing alarm.
this.stopAlarm();
this._settingAlarm = true;
AlarmService.add(
{
date: new Date(Date.now() + delay),
ignoreTimezone: true
},
() => {
if (this._state > PUSH_SERVICE_ACTIVATING) {
this._service.onAlarmFired();
}
}, (alarmID) => {
this._alarmID = alarmID;
console.debug("setAlarm: Set alarm", delay, "in the future",
this._alarmID);
this._settingAlarm = false;
if (this._waitingForAlarmSet) {
this._waitingForAlarmSet = false;
this.setAlarm(this._queuedAlarmDelay);
}
}
);
},
stopAlarm: function() {
if (this._alarmID !== null) {
console.debug("stopAlarm: Stopped existing alarm", this._alarmID);
AlarmService.remove(this._alarmID);
this._alarmID = null;
}
},
/**
* Drops all active registrations and notifies the associated service
* workers. This function is called when the user switches Push servers,

View File

@ -465,7 +465,7 @@ this.PushServiceHttp2 = {
listener: null,
countUnableToConnect: 0,
lastStartListening: 0,
waitingForAlarm: false
retryTimerID: 0,
};
this._listenForMsgs(result.subscriptionUri);
return result;
@ -583,28 +583,20 @@ this.PushServiceHttp2 = {
if (retryAfter !== -1) {
// This is a 5xx response.
// To respect RetryAfter header, setTimeout is used. setAlarm sets a
// cumulative alarm so it will not always respect RetryAfter header.
this._conns[aSubscriptionUri].countUnableToConnect++;
setTimeout(_ => this._listenForMsgs(aSubscriptionUri), retryAfter);
this._conns[aSubscriptionUri].retryTimerID =
setTimeout(_ => this._listenForMsgs(aSubscriptionUri), retryAfter);
return;
}
// we set just one alarm because most probably all connection will go over
// a single TCP connection.
retryAfter = prefs.get("http2.retryInterval") *
Math.pow(2, this._conns[aSubscriptionUri].countUnableToConnect);
retryAfter = retryAfter * (0.8 + Math.random() * 0.4); // add +/-20%.
this._conns[aSubscriptionUri].countUnableToConnect++;
if (retryAfter === 0) {
setTimeout(_ => this._listenForMsgs(aSubscriptionUri), 0);
} else {
this._conns[aSubscriptionUri].waitingForAlarm = true;
this._mainPushService.setAlarm(retryAfter);
}
this._conns[aSubscriptionUri].retryTimerID =
setTimeout(_ => this._listenForMsgs(aSubscriptionUri), retryAfter);
console.debug("retryAfterBackoff: Retry in", retryAfter);
},
@ -626,7 +618,11 @@ this.PushServiceHttp2 = {
}
this._conns[subscriptionUri].listener = null;
this._conns[subscriptionUri].channel = null;
this._conns[subscriptionUri].waitingForAlarm = false;
if (this._conns[subscriptionUri].retryTimerID > 0) {
clearTimeout(this._conns[subscriptionUri].retryTimerID);
}
if (deleteInfo) {
delete this._conns[subscriptionUri];
}
@ -655,27 +651,13 @@ this.PushServiceHttp2 = {
this._conns[record.subscriptionUri] = {channel: null,
listener: null,
countUnableToConnect: 0,
waitingForAlarm: false};
retryTimerID: 0};
}
if (!this._conns[record.subscriptionUri].conn) {
this._conns[record.subscriptionUri].waitingForAlarm = false;
this._listenForMsgs(record.subscriptionUri);
}
},
// Start listening if subscriptions present.
_startConnectionsWaitingForAlarm: function() {
console.debug("startConnectionsWaitingForAlarm()");
for (let subscriptionUri in this._conns) {
if ((this._conns[subscriptionUri]) &&
!this._conns[subscriptionUri].conn &&
this._conns[subscriptionUri].waitingForAlarm) {
this._conns[subscriptionUri].waitingForAlarm = false;
this._listenForMsgs(subscriptionUri);
}
}
},
// Close connection and notify apps that subscription are gone.
_shutdownSubscription: function(aSubscriptionUri) {
console.debug("shutdownSubscriptions()");
@ -785,10 +767,6 @@ this.PushServiceHttp2 = {
err);
});
},
onAlarmFired: function() {
this._startConnectionsWaitingForAlarm();
},
};
function PushRecordHttp2(record) {

View File

@ -140,44 +140,112 @@ this.PushServiceWebSocket = {
},
observe: function(aSubject, aTopic, aData) {
if (aTopic == "nsPref:changed" && aData == "dom.push.userAgentID") {
this._onUAIDChanged();
} else if (aTopic == "timer-callback") {
this._onTimerFired(aSubject);
}
},
switch (aTopic) {
case "nsPref:changed":
if (aData == "dom.push.userAgentID") {
this._shutdownWS();
this._reconnectAfterBackoff();
/**
* Handles a UAID change. Unlike reconnects, we cancel all pending requests
* after disconnecting. Existing subscriptions stored in IndexedDB will be
* dropped on reconnect.
*/
_onUAIDChanged() {
console.debug("onUAIDChanged()");
this._shutdownWS();
this._startBackoffTimer();
},
/** Handles a ping, backoff, or request timeout timer event. */
_onTimerFired(timer) {
console.debug("onTimerFired()");
if (timer == this._pingTimer) {
this._sendPing();
return;
}
if (timer == this._backoffTimer) {
console.debug("onTimerFired: Reconnecting after backoff");
if (this._reconnectTestCallback) {
// Notify the test callback once the client reconnects.
let actualRetryTimeout = Date.now() - this._lastDisconnect;
this._reconnectTestCallback(actualRetryTimeout);
}
break;
case "timer-callback":
if (aSubject == this._requestTimeoutTimer) {
if (Object.keys(this._registerRequests).length === 0) {
this._requestTimeoutTimer.cancel();
}
this._beginWSSetup();
return;
}
// Set to true if at least one request timed out.
let requestTimedOut = false;
for (let channelID in this._registerRequests) {
let duration = Date.now() - this._registerRequests[channelID].ctime;
// If any of the registration requests time out, all the ones after it
// also made to fail, since we are going to be disconnecting the
// socket.
if (requestTimedOut || duration > this._requestTimeout) {
requestTimedOut = true;
this._registerRequests[channelID]
.reject(new Error("Register request timed out for channel ID " +
channelID));
if (timer == this._requestTimeoutTimer) {
this._timeOutRequests();
return;
}
},
delete this._registerRequests[channelID];
}
}
/**
* Sends a ping to the server. Bypasses the request queue, but starts the
* request timeout timer. If the socket is already closed, or the server
* does not respond within the timeout, the client will reconnect.
*/
_sendPing() {
console.debug("sendPing()");
// The most likely reason for a registration request timing out is
// that the socket has disconnected. Best to reconnect.
this._startRequestTimeoutTimer();
try {
this._wsSendMessage({});
this._lastPingTime = Date.now();
} catch (e) {
console.debug("sendPing: Error sending ping", e);
this._reconnect();
}
},
/** Times out any pending requests. */
_timeOutRequests() {
console.debug("timeOutRequests()");
if (!this._hasPendingRequests()) {
// Cancel the repeating timer and exit early if we aren't waiting for
// pongs or requests.
this._requestTimeoutTimer.cancel();
return;
}
let now = Date.now();
// Set to true if at least one request timed out, or we're still waiting
// for a pong after the request timeout.
let requestTimedOut = false;
if (this._lastPingTime > 0 &&
now - this._lastPingTime > this._requestTimeout) {
console.debug("timeOutRequests: Did not receive pong in time");
requestTimedOut = true;
} else {
for (let [channelID, request] of this._registerRequests) {
let duration = now - request.ctime;
// If any of the registration requests time out, all the ones after it
// also made to fail, since we are going to be disconnecting the
// socket.
requestTimedOut |= duration > this._requestTimeout;
if (requestTimedOut) {
this._reconnect();
request.reject(new Error(
"Register request timed out for channel ID " + channelID));
this._registerRequests.delete(channelID);
}
}
break;
}
// The most likely reason for a pong or registration request timing out is
// that the socket has disconnected. Best to reconnect.
if (requestTimedOut) {
this._reconnect();
}
},
@ -200,7 +268,7 @@ this.PushServiceWebSocket = {
},
_ws: null,
_registerRequests: {},
_registerRequests: new Map(),
_currentState: STATE_SHUT_DOWN,
_requestTimeout: 0,
_requestTimeoutTimer: null,
@ -256,6 +324,34 @@ this.PushServiceWebSocket = {
/** Indicates whether the server supports Web Push-style message delivery. */
_dataEnabled: false,
/**
* The last time the client sent a ping to the server. If non-zero, keeps the
* request timeout timer active. Reset to zero when the server responds with
* a pong or pending messages.
*/
_lastPingTime: 0,
/** The last time the connection was closed. */
_lastDisconnect: 0,
/**
* A one-shot timer used to ping the server, to avoid timing out idle
* connections. Reset to the ping interval on each incoming message.
*/
_pingTimer: null,
/** A one-shot timer fired after the reconnect backoff period. */
_backoffTimer: null,
/**
* A function called when the client reconnects after backing off.
*
* @param {Number} actualRetryTimeout The time elapsed between the last
* disconnect and reconnect time. This should be >= the backoff delay for
* that attempt.
*/
_reconnectTestCallback: null,
/**
* Sends a message to the Push Server through an open websocket.
* typeof(msg) shall be an object
@ -306,7 +402,7 @@ this.PushServiceWebSocket = {
_reconnect: function () {
console.debug("reconnect()");
this._shutdownWS(false);
this._reconnectAfterBackoff();
this._startBackoffTimer();
},
_shutdownWS: function(shouldCancelPending = true) {
@ -324,11 +420,10 @@ this.PushServiceWebSocket = {
} catch (e) {}
this._ws = null;
this._waitingForPong = false;
if (this._mainPushService) {
this._mainPushService.stopAlarm();
} else {
console.error("shutdownWS: Uninitialized push service");
this._lastPingTime = 0;
if (this._pingTimer) {
this._pingTimer.cancel();
}
if (shouldCancelPending) {
@ -339,6 +434,8 @@ this.PushServiceWebSocket = {
this._notifyRequestQueue();
this._notifyRequestQueue = null;
}
this._lastDisconnect = Date.now();
},
uninit: function() {
@ -352,6 +449,9 @@ this.PushServiceWebSocket = {
// or receiving notifications.
this._shutdownWS();
if (this._backoffTimer) {
this._backoffTimer.cancel();
}
if (this._requestTimeoutTimer) {
this._requestTimeoutTimer.cancel();
}
@ -365,12 +465,9 @@ this.PushServiceWebSocket = {
* How retries work: The goal is to ensure websocket is always up on
* networks not supporting UDP. So the websocket should only be shutdown if
* onServerClose indicates UDP wakeup. If WS is closed due to socket error,
* _reconnectAfterBackoff() is called. The retry alarm is started and when
* _startBackoffTimer() is called. The retry timer is started and when
* it times out, beginWSSetup() is called again.
*
* On a successful connection, the alarm is cancelled in
* wsOnMessageAvailable() when the ping alarm is started.
*
* If we are in the middle of a timeout (i.e. waiting), but
* a register/unregister is called, we don't want to wait around anymore.
* _sendRequest will automatically call beginWSSetup(), which will cancel the
@ -378,8 +475,8 @@ this.PushServiceWebSocket = {
* timer event comes in (because the timer fired the event before it was
* cancelled), so the connection won't be reset.
*/
_reconnectAfterBackoff: function() {
console.debug("reconnectAfterBackoff()");
_startBackoffTimer() {
console.debug("startBackoffTimer()");
//Calculate new ping interval
this._calculateAdaptivePing(true /* wsWentDown */);
@ -390,13 +487,46 @@ this.PushServiceWebSocket = {
this._retryFailCount++;
console.debug("reconnectAfterBackoff: Retry in", retryTimeout,
console.debug("startBackoffTimer: Retry in", retryTimeout,
"Try number", this._retryFailCount);
if (this._mainPushService) {
this._mainPushService.setAlarm(retryTimeout);
} else {
console.error("reconnectAfterBackoff: Uninitialized push service");
if (!this._backoffTimer) {
this._backoffTimer = Cc["@mozilla.org/timer;1"]
.createInstance(Ci.nsITimer);
}
this._backoffTimer.init(this, retryTimeout, Ci.nsITimer.TYPE_ONE_SHOT);
},
/** Indicates whether we're waiting for pongs or requests. */
_hasPendingRequests() {
return this._lastPingTime > 0 || this._registerRequests.size > 0;
},
/**
* Starts the request timeout timer unless we're already waiting for a pong
* or register request.
*/
_startRequestTimeoutTimer() {
if (this._hasPendingRequests()) {
return;
}
if (!this._requestTimeoutTimer) {
this._requestTimeoutTimer = Cc["@mozilla.org/timer;1"]
.createInstance(Ci.nsITimer);
}
this._requestTimeoutTimer.init(this,
this._requestTimeout,
Ci.nsITimer.TYPE_REPEATING_SLACK);
},
/** Starts or resets the ping timer. */
_startPingTimer() {
if (!this._pingTimer) {
this._pingTimer = Cc["@mozilla.org/timer;1"]
.createInstance(Ci.nsITimer);
}
this._pingTimer.init(this, prefs.get("pingInterval"),
Ci.nsITimer.TYPE_ONE_SHOT);
},
/**
@ -580,8 +710,8 @@ this.PushServiceWebSocket = {
}
// Stop any pending reconnects scheduled for the near future.
if (this._mainPushService) {
this._mainPushService.stopAlarm();
if (this._backoffTimer) {
this._backoffTimer.cancel();
}
let uri = this._serverURI;
@ -623,74 +753,6 @@ this.PushServiceWebSocket = {
return !!this._ws;
},
/**
* There is only one alarm active at any time. This alarm has 3 intervals
* corresponding to 3 tasks.
*
* 1) Reconnect on ping timeout.
* If we haven't received any messages from the server by the time this
* alarm fires, the connection is closed and PushService tries to
* reconnect, repurposing the alarm for (3).
*
* 2) Send a ping.
* The protocol sends a ping ({}) on the wire every pingInterval ms. Once
* it sends the ping, the alarm goes to task (1) which is waiting for
* a pong. If data is received after the ping is sent,
* _wsOnMessageAvailable() will reset the ping alarm (which cancels
* waiting for the pong). So as long as the connection is fine, pong alarm
* never fires.
*
* 3) Reconnect after backoff.
* The alarm is set by _reconnectAfterBackoff() and increases in duration
* every time we try and fail to connect. When it triggers, websocket
* setup begins again. On successful socket setup, the socket starts
* receiving messages. The alarm now goes to (2) where it monitors the
* WebSocket by sending a ping. Since incoming data is a sign of the
* connection being up, the ping alarm is reset every time data is
* received.
*/
onAlarmFired: function() {
// Conditions are arranged in decreasing specificity.
// i.e. when _waitingForPong is true, other conditions are also true.
if (this._waitingForPong) {
console.debug("onAlarmFired: Did not receive pong in time.",
"Reconnecting WebSocket");
this._reconnect();
}
else if (this._currentState == STATE_READY) {
// Send a ping.
// Bypass the queue; we don't want this to be kept pending.
// Watch out for exception in case the socket has disconnected.
// When this happens, we pretend the ping was sent and don't specially
// handle the exception, as the lack of a pong will lead to the socket
// being reset.
try {
this._wsSendMessage({});
} catch (e) {
}
this._waitingForPong = true;
this._mainPushService.setAlarm(prefs.get("requestTimeout"));
}
else if (this._mainPushService && this._mainPushService._alarmID !== null) {
console.debug("onAlarmFired: reconnect alarm fired");
// Reconnect after back-off.
// The check for a non-null _alarmID prevents a situation where the alarm
// fires, but _shutdownWS() is called from another code-path (e.g.
// network state change) and we don't want to reconnect.
//
// It also handles the case where _beginWSSetup() is called from another
// code-path.
//
// alarmID will be non-null only when no shutdown/connect is
// called between _reconnectAfterBackoff() setting the alarm and the
// alarm firing.
// Websocket is shut down. Backoff interval expired, try to connect.
this._beginWSSetup();
}
},
_acquireWakeLock: function() {
if (!AppConstants.MOZ_B2G) {
return;
@ -818,14 +880,13 @@ this.PushServiceWebSocket = {
_handleRegisterReply: function(reply) {
console.debug("handleRegisterReply()");
if (typeof reply.channelID !== "string" ||
typeof this._registerRequests[reply.channelID] !== "object") {
!this._registerRequests.has(reply.channelID)) {
return;
}
let tmp = this._registerRequests[reply.channelID];
delete this._registerRequests[reply.channelID];
if (Object.keys(this._registerRequests).length === 0 &&
this._requestTimeoutTimer) {
let tmp = this._registerRequests.get(reply.channelID);
this._registerRequests.delete(reply.channelID);
if (!this._hasPendingRequests()) {
this._requestTimeoutTimer.cancel();
}
@ -964,27 +1025,20 @@ this.PushServiceWebSocket = {
request: function(action, record) {
console.debug("request() ", action);
if (Object.keys(this._registerRequests).length === 0) {
// start the timer since we now have at least one request
if (!this._requestTimeoutTimer) {
this._requestTimeoutTimer = Cc["@mozilla.org/timer;1"]
.createInstance(Ci.nsITimer);
}
this._requestTimeoutTimer.init(this,
this._requestTimeout,
Ci.nsITimer.TYPE_REPEATING_SLACK);
}
// start the timer since we now have at least one request
this._startRequestTimeoutTimer();
if (action == "register") {
let data = {channelID: this._generateID(),
messageType: action};
return new Promise((resolve, reject) => {
this._registerRequests[data.channelID] = {record: record,
resolve: resolve,
reject: reject,
ctime: Date.now()
};
this._registerRequests.set(data.channelID, {
record: record,
resolve: resolve,
reject: reject,
ctime: Date.now(),
});
this._queueRequest(data);
}).then(record => {
if (!this._dataEnabled) {
@ -1021,7 +1075,7 @@ this.PushServiceWebSocket = {
_send(data) {
if (this._currentState == STATE_READY) {
if (data.messageType != "register" ||
typeof this._registerRequests[data.channelID] == "object") {
this._registerRequests.has(data.channelID)) {
// check if request has not been cancelled
this._wsSendMessage(data);
@ -1030,12 +1084,14 @@ this.PushServiceWebSocket = {
},
_sendRegisterRequests() {
this._enqueue(_ => Promise.all(Object.keys(this._registerRequests).map(channelID =>
this._send({
messageType: "register",
channelID: channelID,
})
)));
this._enqueue(_ => {
for (let channelID of this._registerRequests.keys()) {
this._send({
messageType: "register",
channelID: channelID,
});
}
});
},
_queueRequest(data) {
@ -1148,7 +1204,8 @@ this.PushServiceWebSocket = {
_wsOnMessageAvailable: function(context, message) {
console.debug("wsOnMessageAvailable()", message);
this._waitingForPong = false;
// Clearing the last ping time indicates we're no longer waiting for a pong.
this._lastPingTime = 0;
let reply;
try {
@ -1174,8 +1231,8 @@ this.PushServiceWebSocket = {
}
// Reset the ping timer. Note: This path is executed at every step of the
// handshake, so this alarm does not need to be set explicitly at startup.
this._mainPushService.setAlarm(prefs.get("pingInterval"));
// handshake, so this timer does not need to be set explicitly at startup.
this._startPingTimer();
// If it is a ping, do not handle the message.
if (doNotHandle) {
@ -1232,11 +1289,10 @@ this.PushServiceWebSocket = {
* Rejects all pending register requests with errors.
*/
_cancelRegisterRequests: function() {
for (let channelID in this._registerRequests) {
let request = this._registerRequests[channelID];
delete this._registerRequests[channelID];
for (let request of this._registerRequests.values()) {
request.reject(new Error("Register request aborted"));
}
this._registerRequests.clear();
},
_makeUDPSocket: function() {

View File

@ -31,10 +31,8 @@ add_task(function* test_ws_retry() {
});
let alarmDelays = [];
let setAlarm = PushService.setAlarm;
PushService.setAlarm = function(delay) {
PushServiceWebSocket._reconnectTestCallback = function(delay) {
alarmDelays.push(delay);
setAlarm.apply(this, arguments);
};
let handshakeDone;
@ -46,7 +44,6 @@ add_task(function* test_ws_retry() {
return new MockWebSocket(uri, {
onHello(request) {
if (alarmDelays.length == 10) {
PushService.setAlarm = setAlarm;
this.serverSendMsg(JSON.stringify({
messageType: 'hello',
status: 200,
@ -66,6 +63,8 @@ add_task(function* test_ws_retry() {
45000,
'Timed out waiting for successful handshake'
);
deepEqual(alarmDelays, [25, 50, 100, 200, 400, 800, 1600, 3200, 6400, 10000],
'Wrong reconnect alarm delays');
[25, 50, 100, 200, 400, 800, 1600, 3200, 6400, 10000].forEach(function(minDelay, index) {
ok(alarmDelays[index] >= minDelay, `Should wait at least ${
minDelay}ms before attempt ${index + 1}`);
});
});

View File

@ -964,23 +964,14 @@ GonkGPSGeolocationProvider::NetworkLocationUpdate::Update(nsIDOMGeoPosition *pos
provider->mLocationCallback->Update(provider->mLastGPSPosition);
}
}
provider->InjectLocation(lat, lon, acc);
return NS_OK;
}
NS_IMETHODIMP
GonkGPSGeolocationProvider::NetworkLocationUpdate::LocationUpdatePending()
{
return NS_OK;
}
NS_IMETHODIMP
GonkGPSGeolocationProvider::NetworkLocationUpdate::NotifyError(uint16_t error)
{
return NS_OK;
}
NS_IMETHODIMP
GonkGPSGeolocationProvider::Startup()
{

View File

@ -814,9 +814,7 @@ MozMtpDatabase::getObjectList(MtpStorageID aStorageID,
//TODO: Optimize
ScopedDeletePtr<MtpObjectHandleList> list;
list = new MtpObjectHandleList();
UniquePtr<MtpObjectHandleList> list(new MtpObjectHandleList());
MutexAutoLock lock(mMutex);
@ -832,7 +830,7 @@ MozMtpDatabase::getObjectList(MtpStorageID aStorageID,
}
}
MTP_LOG(" returning %d items", list->size());
return list.forget();
return list.release();
}
//virtual

View File

@ -15,7 +15,7 @@
#include "NetworkUtils.h"
#include "prprf.h"
#include "mozilla/Snprintf.h"
#include "SystemProperty.h"
#include <android/log.h>
@ -399,11 +399,11 @@ static void convertUTF8toUTF16(nsTArray<nsCString>& narrow,
static void getIFProperties(const char* ifname, IFProperties& prop)
{
char key[Property::KEY_MAX_LENGTH];
PR_snprintf(key, Property::KEY_MAX_LENGTH - 1, "net.%s.gw", ifname);
snprintf(key, Property::KEY_MAX_LENGTH - 1, "net.%s.gw", ifname);
Property::Get(key, prop.gateway, "");
PR_snprintf(key, Property::KEY_MAX_LENGTH - 1, "net.%s.dns1", ifname);
snprintf(key, Property::KEY_MAX_LENGTH - 1, "net.%s.dns1", ifname);
Property::Get(key, prop.dns1, "");
PR_snprintf(key, Property::KEY_MAX_LENGTH - 1, "net.%s.dns2", ifname);
snprintf(key, Property::KEY_MAX_LENGTH - 1, "net.%s.dns2", ifname);
Property::Get(key, prop.dns2, "");
}
@ -524,7 +524,7 @@ void NetworkUtils::nextNetdCommand()
gCurrentCommand.chain = GET_CURRENT_CHAIN;
gCurrentCommand.callback = GET_CURRENT_CALLBACK;
PR_snprintf(gCurrentCommand.command, MAX_COMMAND_SIZE - 1, "%s", GET_CURRENT_COMMAND);
snprintf(gCurrentCommand.command, MAX_COMMAND_SIZE - 1, "%s", GET_CURRENT_COMMAND);
NU_DBG("Sending \'%s\' command to netd.", gCurrentCommand.command);
SendNetdCommand(GET_CURRENT_NETD_COMMAND);
@ -549,9 +549,9 @@ void NetworkUtils::doCommand(const char* aCommand, CommandChain* aChain, Command
// Android JB version adds sequence number to netd command.
if (SDK_VERSION >= 16) {
PR_snprintf((char*)netdCommand->mData, MAX_COMMAND_SIZE - 1, "0 %s", aCommand);
snprintf((char*)netdCommand->mData, MAX_COMMAND_SIZE - 1, "0 %s", aCommand);
} else {
PR_snprintf((char*)netdCommand->mData, MAX_COMMAND_SIZE - 1, "%s", aCommand);
snprintf((char*)netdCommand->mData, MAX_COMMAND_SIZE - 1, "%s", aCommand);
}
netdCommand->mSize = strlen((char*)netdCommand->mData) + 1;
@ -571,7 +571,7 @@ void NetworkUtils::wifiFirmwareReload(CommandChain* aChain,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "softap fwreload %s %s", GET_CHAR(mIfname), GET_CHAR(mMode));
snprintf(command, MAX_COMMAND_SIZE - 1, "softap fwreload %s %s", GET_CHAR(mIfname), GET_CHAR(mMode));
doCommand(command, aChain, aCallback);
}
@ -589,7 +589,7 @@ void NetworkUtils::startAccessPointDriver(CommandChain* aChain,
}
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "softap start %s", GET_CHAR(mIfname));
snprintf(command, MAX_COMMAND_SIZE - 1, "softap start %s", GET_CHAR(mIfname));
doCommand(command, aChain, aCallback);
}
@ -607,7 +607,7 @@ void NetworkUtils::stopAccessPointDriver(CommandChain* aChain,
}
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "softap stop %s", GET_CHAR(mIfname));
snprintf(command, MAX_COMMAND_SIZE - 1, "softap stop %s", GET_CHAR(mIfname));
doCommand(command, aChain, aCallback);
}
@ -651,24 +651,24 @@ void NetworkUtils::setAccessPoint(CommandChain* aChain,
escapeQuote(key);
if (SDK_VERSION >= 19) {
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "softap set %s \"%s\" broadcast 6 %s \"%s\"",
GET_CHAR(mIfname),
ssid.get(),
GET_CHAR(mSecurity),
key.get());
snprintf(command, MAX_COMMAND_SIZE - 1, "softap set %s \"%s\" broadcast 6 %s \"%s\"",
GET_CHAR(mIfname),
ssid.get(),
GET_CHAR(mSecurity),
key.get());
} else if (SDK_VERSION >= 16) {
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "softap set %s \"%s\" %s \"%s\"",
GET_CHAR(mIfname),
ssid.get(),
GET_CHAR(mSecurity),
key.get());
snprintf(command, MAX_COMMAND_SIZE - 1, "softap set %s \"%s\" %s \"%s\"",
GET_CHAR(mIfname),
ssid.get(),
GET_CHAR(mSecurity),
key.get());
} else {
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "softap set %s %s \"%s\" %s \"%s\" 6 0 8",
GET_CHAR(mIfname),
GET_CHAR(mWifictrlinterfacename),
ssid.get(),
GET_CHAR(mSecurity),
key.get());
snprintf(command, MAX_COMMAND_SIZE - 1, "softap set %s %s \"%s\" %s \"%s\" 6 0 8",
GET_CHAR(mIfname),
GET_CHAR(mWifictrlinterfacename),
ssid.get(),
GET_CHAR(mSecurity),
key.get());
}
doCommand(command, aChain, aCallback);
@ -679,7 +679,7 @@ void NetworkUtils::cleanUpStream(CommandChain* aChain,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "nat disable %s %s 0", GET_CHAR(mPreInternalIfname), GET_CHAR(mPreExternalIfname));
snprintf(command, MAX_COMMAND_SIZE - 1, "nat disable %s %s 0", GET_CHAR(mPreInternalIfname), GET_CHAR(mPreExternalIfname));
doCommand(command, aChain, aCallback);
}
@ -689,7 +689,7 @@ void NetworkUtils::createUpStream(CommandChain* aChain,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "nat enable %s %s 0", GET_CHAR(mCurInternalIfname), GET_CHAR(mCurExternalIfname));
snprintf(command, MAX_COMMAND_SIZE - 1, "nat enable %s %s 0", GET_CHAR(mCurInternalIfname), GET_CHAR(mCurExternalIfname));
doCommand(command, aChain, aCallback);
}
@ -740,7 +740,7 @@ void NetworkUtils::setQuota(CommandChain* aChain,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth setiquota %s %lld", GET_CHAR(mIfname), LLONG_MAX);
snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth setiquota %s % " PRId64, GET_CHAR(mIfname), INT64_MAX);
doCommand(command, aChain, aCallback);
}
@ -750,7 +750,7 @@ void NetworkUtils::removeQuota(CommandChain* aChain,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth removeiquota %s", GET_CHAR(mIfname));
snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth removeiquota %s", GET_CHAR(mIfname));
doCommand(command, aChain, aCallback);
}
@ -760,7 +760,8 @@ void NetworkUtils::setAlarm(CommandChain* aChain,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth setinterfacealert %s %lld", GET_CHAR(mIfname), GET_FIELD(mThreshold));
snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth setinterfacealert %s %lld",
GET_CHAR(mIfname), GET_FIELD(mThreshold));
doCommand(command, aChain, aCallback);
}
@ -770,7 +771,7 @@ void NetworkUtils::removeAlarm(CommandChain* aChain,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth removeinterfacealert %s", GET_CHAR(mIfname));
snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth removeinterfacealert %s", GET_CHAR(mIfname));
doCommand(command, aChain, aCallback);
}
@ -781,7 +782,7 @@ void NetworkUtils::setGlobalAlarm(CommandChain* aChain,
{
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth setglobalalert %ld", GET_FIELD(mThreshold));
snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth setglobalalert %lld", GET_FIELD(mThreshold));
doCommand(command, aChain, aCallback);
}
@ -791,7 +792,7 @@ void NetworkUtils::removeGlobalAlarm(CommandChain* aChain,
{
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth removeglobalalert");
snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth removeglobalalert");
doCommand(command, aChain, aCallback);
}
@ -800,7 +801,7 @@ void NetworkUtils::tetherInterface(CommandChain* aChain,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface add %s", GET_CHAR(mIfname));
snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface add %s", GET_CHAR(mIfname));
doCommand(command, aChain, aCallback);
}
@ -818,8 +819,8 @@ void NetworkUtils::addInterfaceToLocalNetwork(CommandChain* aChain,
}
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "network interface add local %s",
GET_CHAR(mInternalIfname));
snprintf(command, MAX_COMMAND_SIZE - 1, "network interface add local %s",
GET_CHAR(mInternalIfname));
doCommand(command, aChain, aCallback);
}
@ -841,8 +842,8 @@ void NetworkUtils::addRouteToLocalNetwork(CommandChain* aChain,
uint32_t ip = inet_addr(GET_CHAR(mIp));
char* networkAddr = getNetworkAddr(ip, prefix);
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "network route add local %s %s/%s",
GET_CHAR(mInternalIfname), networkAddr, GET_CHAR(mPrefix));
snprintf(command, MAX_COMMAND_SIZE - 1, "network route add local %s %s/%s",
GET_CHAR(mInternalIfname), networkAddr, GET_CHAR(mPrefix));
doCommand(command, aChain, aCallback);
}
@ -853,9 +854,9 @@ void NetworkUtils::preTetherInterfaceList(CommandChain* aChain,
{
char command[MAX_COMMAND_SIZE];
if (SDK_VERSION >= 16) {
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface list");
snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface list");
} else {
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface list 0");
snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface list 0");
}
doCommand(command, aChain, aCallback);
@ -867,7 +868,7 @@ void NetworkUtils::postTetherInterfaceList(CommandChain* aChain,
{
// Send the dummy command to continue the function chain.
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "%s", DUMMY_COMMAND);
snprintf(command, MAX_COMMAND_SIZE - 1, "%s", DUMMY_COMMAND);
char buf[BUF_SIZE];
NS_ConvertUTF16toUTF8 reason(aResult.mResultReason);
@ -927,8 +928,8 @@ void NetworkUtils::addUpstreamInterface(CommandChain* aChain,
}
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface add_upstream %s",
interface.get());
snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface add_upstream %s",
interface.get());
doCommand(command, aChain, aCallback);
}
@ -947,8 +948,8 @@ void NetworkUtils::removeUpstreamInterface(CommandChain* aChain,
}
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface remove_upstream %s",
interface.get());
snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface remove_upstream %s",
interface.get());
doCommand(command, aChain, aCallback);
}
@ -959,14 +960,14 @@ void NetworkUtils::setIpForwardingEnabled(CommandChain* aChain,
char command[MAX_COMMAND_SIZE];
if (GET_FIELD(mEnable)) {
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "ipfwd enable");
snprintf(command, MAX_COMMAND_SIZE - 1, "ipfwd enable");
} else {
// Don't disable ip forwarding because others interface still need it.
// Send the dummy command to continue the function chain.
if (GET_FIELD(mInterfaceList).Length() > 1) {
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "%s", DUMMY_COMMAND);
snprintf(command, MAX_COMMAND_SIZE - 1, "%s", DUMMY_COMMAND);
} else {
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "ipfwd disable");
snprintf(command, MAX_COMMAND_SIZE - 1, "ipfwd disable");
}
}
@ -990,9 +991,9 @@ void NetworkUtils::stopTethering(CommandChain* aChain,
// Don't stop tethering because others interface still need it.
// Send the dummy to continue the function chain.
if (GET_FIELD(mInterfaceList).Length() > 1) {
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "%s", DUMMY_COMMAND);
snprintf(command, MAX_COMMAND_SIZE - 1, "%s", DUMMY_COMMAND);
} else {
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "tether stop");
snprintf(command, MAX_COMMAND_SIZE - 1, "tether stop");
}
doCommand(command, aChain, aCallback);
@ -1007,18 +1008,18 @@ void NetworkUtils::startTethering(CommandChain* aChain,
// We don't need to start tethering again.
// Send the dummy command to continue the function chain.
if (aResult.mResultReason.Find("started") != kNotFound) {
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "%s", DUMMY_COMMAND);
snprintf(command, MAX_COMMAND_SIZE - 1, "%s", DUMMY_COMMAND);
} else {
// If usbStartIp/usbEndIp is not valid, don't append them since
// the trailing white spaces will be parsed to extra empty args
// See: http://androidxref.com/4.3_r2.1/xref/system/core/libsysutils/src/FrameworkListener.cpp#78
if (!GET_FIELD(mUsbStartIp).IsEmpty() && !GET_FIELD(mUsbEndIp).IsEmpty()) {
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "tether start %s %s %s %s",
GET_CHAR(mWifiStartIp), GET_CHAR(mWifiEndIp),
GET_CHAR(mUsbStartIp), GET_CHAR(mUsbEndIp));
snprintf(command, MAX_COMMAND_SIZE - 1, "tether start %s %s %s %s",
GET_CHAR(mWifiStartIp), GET_CHAR(mWifiEndIp),
GET_CHAR(mUsbStartIp), GET_CHAR(mUsbEndIp));
} else {
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "tether start %s %s",
GET_CHAR(mWifiStartIp), GET_CHAR(mWifiEndIp));
snprintf(command, MAX_COMMAND_SIZE - 1, "tether start %s %s",
GET_CHAR(mWifiStartIp), GET_CHAR(mWifiEndIp));
}
}
@ -1030,7 +1031,7 @@ void NetworkUtils::untetherInterface(CommandChain* aChain,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface remove %s", GET_CHAR(mIfname));
snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface remove %s", GET_CHAR(mIfname));
doCommand(command, aChain, aCallback);
}
@ -1048,8 +1049,8 @@ void NetworkUtils::removeInterfaceFromLocalNetwork(CommandChain* aChain,
}
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "network interface remove local %s",
GET_CHAR(mIfname));
snprintf(command, MAX_COMMAND_SIZE - 1, "network interface remove local %s",
GET_CHAR(mIfname));
doCommand(command, aChain, aCallback);
}
@ -1061,11 +1062,11 @@ void NetworkUtils::setDnsForwarders(CommandChain* aChain,
char command[MAX_COMMAND_SIZE];
if (SDK_VERSION >= 20) {
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "tether dns set %d %s %s",
GET_FIELD(mNetId), GET_CHAR(mDns1), GET_CHAR(mDns2));
snprintf(command, MAX_COMMAND_SIZE - 1, "tether dns set %d %s %s",
GET_FIELD(mNetId), GET_CHAR(mDns1), GET_CHAR(mDns2));
} else {
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "tether dns set %s %s",
GET_CHAR(mDns1), GET_CHAR(mDns2));
snprintf(command, MAX_COMMAND_SIZE - 1, "tether dns set %s %s",
GET_CHAR(mDns1), GET_CHAR(mDns2));
}
doCommand(command, aChain, aCallback);
@ -1083,12 +1084,12 @@ void NetworkUtils::enableNat(CommandChain* aChain,
char* networkAddr = getNetworkAddr(ip, prefix);
// address/prefix will only take effect when secondary routing table exists.
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "nat enable %s %s 1 %s/%s",
GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname), networkAddr,
GET_CHAR(mPrefix));
snprintf(command, MAX_COMMAND_SIZE - 1, "nat enable %s %s 1 %s/%s",
GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname), networkAddr,
GET_CHAR(mPrefix));
} else {
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "nat enable %s %s 0",
GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname));
snprintf(command, MAX_COMMAND_SIZE - 1, "nat enable %s %s 0",
GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname));
}
doCommand(command, aChain, aCallback);
@ -1105,12 +1106,12 @@ void NetworkUtils::disableNat(CommandChain* aChain,
uint32_t ip = inet_addr(GET_CHAR(mIp));
char* networkAddr = getNetworkAddr(ip, prefix);
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "nat disable %s %s 1 %s/%s",
GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname), networkAddr,
GET_CHAR(mPrefix));
snprintf(command, MAX_COMMAND_SIZE - 1, "nat disable %s %s 1 %s/%s",
GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname), networkAddr,
GET_CHAR(mPrefix));
} else {
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "nat disable %s %s 0",
GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname));
snprintf(command, MAX_COMMAND_SIZE - 1, "nat disable %s %s 0",
GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname));
}
doCommand(command, aChain, aCallback);
@ -1121,7 +1122,7 @@ void NetworkUtils::setDefaultInterface(CommandChain* aChain,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "resolver setdefaultif %s", GET_CHAR(mIfname));
snprintf(command, MAX_COMMAND_SIZE - 1, "resolver setdefaultif %s", GET_CHAR(mIfname));
doCommand(command, aChain, aCallback);
}
@ -1140,9 +1141,9 @@ void NetworkUtils::removeDefaultRoute(CommandChain* aChain,
NS_ConvertUTF16toUTF8 autoGateway(gateways[GET_FIELD(mLoopIndex)]);
int type = getIpType(autoGateway.get());
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "network route remove %d %s %s/0 %s",
GET_FIELD(mNetId), GET_CHAR(mIfname),
type == AF_INET6 ? "::" : "0.0.0.0", autoGateway.get());
snprintf(command, MAX_COMMAND_SIZE - 1, "network route remove %d %s %s/0 %s",
GET_FIELD(mNetId), GET_CHAR(mIfname),
type == AF_INET6 ? "::" : "0.0.0.0", autoGateway.get());
struct MyCallback {
static void callback(CommandCallback::CallbackType aOriginalCallback,
@ -1173,11 +1174,11 @@ void NetworkUtils::setInterfaceDns(CommandChain* aChain,
int written;
if (SDK_VERSION >= 20) {
written = PR_snprintf(command, sizeof command, "resolver setnetdns %d %s",
GET_FIELD(mNetId), GET_CHAR(mDomain));
written = snprintf_literal(command, "resolver setnetdns %d %s",
GET_FIELD(mNetId), GET_CHAR(mDomain));
} else {
written = PR_snprintf(command, sizeof command, "resolver setifdns %s %s",
GET_CHAR(mIfname), GET_CHAR(mDomain));
written = snprintf_literal(command, "resolver setifdns %s %s",
GET_CHAR(mIfname), GET_CHAR(mDomain));
}
nsTArray<nsString>& dnses = GET_FIELD(mDnses);
@ -1186,7 +1187,7 @@ void NetworkUtils::setInterfaceDns(CommandChain* aChain,
for (uint32_t i = 0; i < length; i++) {
NS_ConvertUTF16toUTF8 autoDns(dnses[i]);
int ret = PR_snprintf(command + written, sizeof(command) - written, " %s", autoDns.get());
int ret = snprintf(command + written, sizeof(command) - written, " %s", autoDns.get());
if (ret <= 1) {
command[written] = '\0';
continue;
@ -1249,7 +1250,7 @@ void NetworkUtils::clearAddrForInterface(CommandChain* aChain,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "interface clearaddrs %s", GET_CHAR(mIfname));
snprintf(command, MAX_COMMAND_SIZE - 1, "interface clearaddrs %s", GET_CHAR(mIfname));
doCommand(command, aChain, aCallback);
}
@ -1259,7 +1260,7 @@ void NetworkUtils::createNetwork(CommandChain* aChain,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "network create %d", GET_FIELD(mNetId));
snprintf(command, MAX_COMMAND_SIZE - 1, "network create %d", GET_FIELD(mNetId));
doCommand(command, aChain, aCallback);
}
@ -1269,7 +1270,7 @@ void NetworkUtils::destroyNetwork(CommandChain* aChain,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "network destroy %d", GET_FIELD(mNetId));
snprintf(command, MAX_COMMAND_SIZE - 1, "network destroy %d", GET_FIELD(mNetId));
doCommand(command, aChain, aCallback);
}
@ -1279,8 +1280,8 @@ void NetworkUtils::addInterfaceToNetwork(CommandChain* aChain,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "network interface add %d %s",
GET_FIELD(mNetId), GET_CHAR(mIfname));
snprintf(command, MAX_COMMAND_SIZE - 1, "network interface add %d %s",
GET_FIELD(mNetId), GET_CHAR(mIfname));
doCommand(command, aChain, aCallback);
}
@ -1339,10 +1340,10 @@ void NetworkUtils::modifyRouteOnInterface(CommandChain* aChain,
const char* action = aDoAdd ? "add" : "remove";
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "network route %s%s %d %s %s/%d%s",
legacyOrEmpty, action,
GET_FIELD(mNetId), GET_CHAR(mIfname), ipOrSubnetIp.get(),
GET_FIELD(mPrefixLength), gatewayOrEmpty.get());
snprintf(command, MAX_COMMAND_SIZE - 1, "network route %s%s %d %s %s/%d%s",
legacyOrEmpty, action,
GET_FIELD(mNetId), GET_CHAR(mIfname), ipOrSubnetIp.get(),
GET_FIELD(mPrefixLength), gatewayOrEmpty.get());
doCommand(command, aChain, aCallback);
}
@ -1361,9 +1362,9 @@ void NetworkUtils::addDefaultRouteToNetwork(CommandChain* aChain,
NS_ConvertUTF16toUTF8 autoGateway(gateways[GET_FIELD(mLoopIndex)]);
int type = getIpType(autoGateway.get());
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "network route add %d %s %s/0 %s",
GET_FIELD(mNetId), GET_CHAR(mIfname),
type == AF_INET6 ? "::" : "0.0.0.0", autoGateway.get());
snprintf(command, MAX_COMMAND_SIZE - 1, "network route add %d %s %s/0 %s",
GET_FIELD(mNetId), GET_CHAR(mIfname),
type == AF_INET6 ? "::" : "0.0.0.0", autoGateway.get());
struct MyCallback {
static void callback(CommandCallback::CallbackType aOriginalCallback,
@ -1391,7 +1392,7 @@ void NetworkUtils::setDefaultNetwork(CommandChain* aChain,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "network default set %d", GET_FIELD(mNetId));
snprintf(command, MAX_COMMAND_SIZE - 1, "network default set %d", GET_FIELD(mNetId));
doCommand(command, aChain, aCallback);
}
@ -1403,20 +1404,20 @@ void NetworkUtils::addRouteToSecondaryTable(CommandChain* aChain,
char command[MAX_COMMAND_SIZE];
if (SDK_VERSION >= 20) {
PR_snprintf(command, MAX_COMMAND_SIZE - 1,
"network route add %d %s %s/%s %s",
GET_FIELD(mNetId),
GET_CHAR(mIfname),
GET_CHAR(mIp),
GET_CHAR(mPrefix),
GET_CHAR(mGateway));
snprintf(command, MAX_COMMAND_SIZE - 1,
"network route add %d %s %s/%s %s",
GET_FIELD(mNetId),
GET_CHAR(mIfname),
GET_CHAR(mIp),
GET_CHAR(mPrefix),
GET_CHAR(mGateway));
} else {
PR_snprintf(command, MAX_COMMAND_SIZE - 1,
"interface route add %s secondary %s %s %s",
GET_CHAR(mIfname),
GET_CHAR(mIp),
GET_CHAR(mPrefix),
GET_CHAR(mGateway));
snprintf(command, MAX_COMMAND_SIZE - 1,
"interface route add %s secondary %s %s %s",
GET_CHAR(mIfname),
GET_CHAR(mIp),
GET_CHAR(mPrefix),
GET_CHAR(mGateway));
}
doCommand(command, aChain, aCallback);
@ -1428,20 +1429,20 @@ void NetworkUtils::removeRouteFromSecondaryTable(CommandChain* aChain,
char command[MAX_COMMAND_SIZE];
if (SDK_VERSION >= 20) {
PR_snprintf(command, MAX_COMMAND_SIZE - 1,
"network route remove %d %s %s/%s %s",
GET_FIELD(mNetId),
GET_CHAR(mIfname),
GET_CHAR(mIp),
GET_CHAR(mPrefix),
GET_CHAR(mGateway));
snprintf(command, MAX_COMMAND_SIZE - 1,
"network route remove %d %s %s/%s %s",
GET_FIELD(mNetId),
GET_CHAR(mIfname),
GET_CHAR(mIp),
GET_CHAR(mPrefix),
GET_CHAR(mGateway));
} else {
PR_snprintf(command, MAX_COMMAND_SIZE - 1,
"interface route remove %s secondary %s %s %s",
GET_CHAR(mIfname),
GET_CHAR(mIp),
GET_CHAR(mPrefix),
GET_CHAR(mGateway));
snprintf(command, MAX_COMMAND_SIZE - 1,
"interface route remove %s secondary %s %s %s",
GET_CHAR(mIfname),
GET_CHAR(mIp),
GET_CHAR(mPrefix),
GET_CHAR(mGateway));
}
doCommand(command, aChain, aCallback);
@ -1453,8 +1454,8 @@ void NetworkUtils::setIpv6Enabled(CommandChain* aChain,
bool aEnabled)
{
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "interface ipv6 %s %s",
GET_CHAR(mIfname), aEnabled ? "enable" : "disable");
snprintf(command, MAX_COMMAND_SIZE - 1, "interface ipv6 %s %s",
GET_CHAR(mIfname), aEnabled ? "enable" : "disable");
struct MyCallback {
static void callback(CommandCallback::CallbackType aOriginalCallback,
@ -1489,8 +1490,8 @@ void NetworkUtils::setMtu(CommandChain* aChain,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
PR_snprintf(command, MAX_COMMAND_SIZE - 1, "interface setmtu %s %d",
GET_CHAR(mIfname), GET_FIELD(mMtu));
snprintf(command, MAX_COMMAND_SIZE - 1, "interface setmtu %s %ld",
GET_CHAR(mIfname), GET_FIELD(mMtu));
doCommand(command, aChain, aCallback);
}
@ -1876,9 +1877,9 @@ void NetworkUtils::onNetdMessage(NetdCommand* aCommand)
if (code == NETD_COMMAND_INTERFACE_CHANGE) {
if (gWifiTetheringParms) {
char linkdownReason[MAX_COMMAND_SIZE];
PR_snprintf(linkdownReason, MAX_COMMAND_SIZE - 1,
"Iface linkstate %s down",
NS_ConvertUTF16toUTF8(gWifiTetheringParms->mIfname).get());
snprintf(linkdownReason, MAX_COMMAND_SIZE - 1,
"Iface linkstate %s down",
NS_ConvertUTF16toUTF8(gWifiTetheringParms->mIfname).get());
if (!strcmp(reason, linkdownReason)) {
NU_DBG("Wifi link down, restarting tethering.");
@ -1955,7 +1956,7 @@ CommandResult NetworkUtils::setDNS(NetworkParams& aOptions)
NS_ConvertUTF16toUTF8 autoDns(aOptions.mDnses[i]);
char dns_prop_key[Property::VALUE_MAX_LENGTH];
PR_snprintf(dns_prop_key, sizeof dns_prop_key, "net.dns%d", i+1);
snprintf_literal(dns_prop_key, "net.dns%d", i+1);
Property::Set(dns_prop_key, autoDns.get());
}
} else {
@ -1972,7 +1973,7 @@ CommandResult NetworkUtils::setDNS(NetworkParams& aOptions)
Property::Get("net.dnschange", dnschange, "0");
char num[Property::VALUE_MAX_LENGTH];
PR_snprintf(num, Property::VALUE_MAX_LENGTH - 1, "%d", atoi(dnschange) + 1);
snprintf(num, Property::VALUE_MAX_LENGTH - 1, "%d", atoi(dnschange) + 1);
Property::Set("net.dnschange", num);
// DNS needs to be set through netd since JellyBean (4.3).
@ -2164,7 +2165,7 @@ CommandResult NetworkUtils::setDefaultRouteLegacy(NetworkParams& aOptions)
char key[Property::KEY_MAX_LENGTH];
char gateway[Property::KEY_MAX_LENGTH];
PR_snprintf(key, sizeof key - 1, "net.%s.gw", autoIfname.get());
snprintf(key, sizeof key - 1, "net.%s.gw", autoIfname.get());
Property::Get(key, gateway, "");
int type = getIpType(gateway);

View File

@ -125,25 +125,16 @@ CoreLocationLocationProvider::MLSUpdate::Update(nsIDOMGeoPosition *position)
if (!coords) {
return NS_ERROR_FAILURE;
}
mParentLocationProvider.Update(position);
Telemetry::Accumulate(Telemetry::GEOLOCATION_OSX_SOURCE_IS_MLS, true);
return NS_OK;
}
NS_IMETHODIMP
CoreLocationLocationProvider::MLSUpdate::LocationUpdatePending()
{
return NS_OK;
}
NS_IMETHODIMP
CoreLocationLocationProvider::MLSUpdate::NotifyError(uint16_t error)
{
mParentLocationProvider.NotifyError(error);
return NS_OK;
}
class CoreLocationObjects {
public:
NS_METHOD Init(CoreLocationLocationProvider* aProvider) {

View File

@ -34,18 +34,9 @@ WindowsLocationProvider::MLSUpdate::Update(nsIDOMGeoPosition *aPosition)
if (!coords) {
return NS_ERROR_FAILURE;
}
Telemetry::Accumulate(Telemetry::GEOLOCATION_WIN8_SOURCE_IS_MLS, true);
return mCallback->Update(aPosition);
}
NS_IMETHODIMP
WindowsLocationProvider::MLSUpdate::LocationUpdatePending()
{
return NS_OK;
}
NS_IMETHODIMP
WindowsLocationProvider::MLSUpdate::NotifyError(uint16_t aError)
{
@ -55,7 +46,6 @@ WindowsLocationProvider::MLSUpdate::NotifyError(uint16_t aError)
return mCallback->NotifyError(aError);
}
class LocationEvent final : public ILocationEvents
{
public:

View File

@ -45,6 +45,16 @@ interface KeyframeEffectReadOnly : AnimationEffectReadOnly {
[Throws] sequence<object> getFrames();
};
// Non-standard extensions
dictionary AnimationPropertyState {
DOMString property;
boolean runningOnCompositor;
DOMString? warning;
};
partial interface KeyframeEffectReadOnly {
[ChromeOnly] sequence<AnimationPropertyState> getPropertyState();
};
[Func="nsDocument::IsWebAnimationsEnabled",
Constructor ((Element or CSSPseudoElement)? target,

View File

@ -9,6 +9,7 @@
#include <errno.h>
#include <cutils/properties.h>
#include "prinit.h"
#include "mozilla/Snprintf.h"
#include "js/CharacterEncoding.h"
using namespace mozilla::dom;
@ -368,10 +369,10 @@ public:
char command[COMMAND_SIZE];
if (!strcmp(iface, "p2p0")) {
// Commands for p2p0 interface don't need prefix
PR_snprintf(command, COMMAND_SIZE, "%s", cmd);
snprintf_literal(command, "%s", cmd);
}
else {
PR_snprintf(command, COMMAND_SIZE, "IFNAME=%s %s", iface, cmd);
snprintf_literal(command, "IFNAME=%s %s", iface, cmd);
}
USE_DLFUNC(wifi_command)
return wifi_command(command, buf, len);

View File

@ -289,13 +289,19 @@ ImageContainer::ClearAllImages()
void
ImageContainer::SetCurrentImageInTransaction(Image *aImage)
{
AutoTArray<NonOwningImage,1> images;
images.AppendElement(NonOwningImage(aImage));
SetCurrentImagesInTransaction(images);
}
void
ImageContainer::SetCurrentImagesInTransaction(const nsTArray<NonOwningImage>& aImages)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
NS_ASSERTION(!mImageClient, "Should use async image transfer with ImageBridge.");
AutoTArray<NonOwningImage,1> images;
images.AppendElement(NonOwningImage(aImage));
SetCurrentImageInternal(images);
SetCurrentImageInternal(aImages);
}
bool ImageContainer::IsAsync() const

View File

@ -434,6 +434,7 @@ public:
* You won't get meaningful painted/dropped counts when using this method.
*/
void SetCurrentImageInTransaction(Image* aImage);
void SetCurrentImagesInTransaction(const nsTArray<NonOwningImage>& aImages);
/**
* Returns true if this ImageContainer uses the ImageBridge IPDL protocol.

View File

@ -2975,7 +2975,10 @@ bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime,
mAnimation = nullptr;
SetState(NOTHING);
}
if (wantsRepaints) {
// Request a repaint at the end of the animation in case something such as a
// call to NotifyLayersUpdated was invoked during the animation and Gecko's
// current state is some intermediate point of the animation.
if (!continueAnimation || wantsRepaints) {
RequestContentRepaint();
}
UpdateSharedCompositorFrameMetrics();

View File

@ -15,6 +15,9 @@
#include "Units.h" // for ScreenPoint
namespace mozilla {
class InputData;
namespace layers {
class AsyncPanZoomController;

View File

@ -139,8 +139,7 @@ template<class ContainerT> void
ContainerRenderVR(ContainerT* aContainer,
LayerManagerComposite* aManager,
const gfx::IntRect& aClipRect,
RefPtr<gfx::VRHMDInfo> aHMD,
int32_t aInputFrameID)
RefPtr<gfx::VRHMDInfo> aHMD)
{
int32_t inputFrameID = -1;
@ -271,11 +270,29 @@ ContainerRenderVR(ContainerT* aContainer,
surfaceRect.width, surfaceRect.height));
layerToRender->RenderLayer(surfaceRect);
CompositableHost *ch = layerToRender->GetCompositableHost();
if (ch) {
int32_t compositableInputFrameID = ch->GetLastInputFrameID();
if (compositableInputFrameID != -1) {
inputFrameID = compositableInputFrameID;
// Search all children recursively until we find the canvas with
// an inputFrameID
std::stack<LayerComposite*> searchLayers;
searchLayers.push(layerToRender);
while (!searchLayers.empty() && inputFrameID == -1) {
LayerComposite* searchLayer = searchLayers.top();
searchLayers.pop();
if (searchLayer) {
searchLayers.push(searchLayer->GetFirstChildComposite());
Layer* sibling = searchLayer->GetLayer();
if (sibling) {
sibling = sibling->GetNextSibling();
}
if (sibling) {
searchLayers.push(sibling->AsLayerComposite());
}
CompositableHost *ch = searchLayer->GetCompositableHost();
if (ch) {
int32_t compositableInputFrameID = ch->GetLastInputFrameID();
if (compositableInputFrameID != -1) {
inputFrameID = compositableInputFrameID;
}
}
}
}
@ -714,7 +731,7 @@ ContainerRender(ContainerT* aContainer,
RefPtr<gfx::VRHMDInfo> hmdInfo = gfx::VRManager::Get()->GetDevice(aContainer->GetVRDeviceID());
if (hmdInfo && hmdInfo->GetConfiguration().IsValid()) {
ContainerRenderVR(aContainer, aManager, aClipRect, hmdInfo, aContainer->GetInputFrameID());
ContainerRenderVR(aContainer, aManager, aClipRect, hmdInfo);
aContainer->mPrepared = nullptr;
return;
}

View File

@ -2350,15 +2350,8 @@ CompositorParent::UpdatePluginWindowState(uint64_t aId)
// Check if this layer tree has received any shadow layer updates
if (!lts.mUpdatedPluginDataAvailable) {
if (mLastPluginUpdateLayerTreeId != aId) {
lts.mUpdatedPluginDataAvailable = true;
mPluginsLayerOffset = nsIntPoint(0,0);
mPluginsLayerVisibleRegion.SetEmpty();
PLUGINS_LOG("[%" PRIu64 "] new layer id, refreshing", aId);
} else {
PLUGINS_LOG("[%" PRIu64 "] no plugin data", aId);
return false;
}
PLUGINS_LOG("[%" PRIu64 "] no plugin data", aId);
return false;
}
// pluginMetricsChanged tracks whether we need to send plugin update

View File

@ -364,6 +364,7 @@ private:
DECL_GFX_PREF(Live, "layers.low-precision-buffer", UseLowPrecisionBuffer, bool, false);
DECL_GFX_PREF(Live, "layers.low-precision-opacity", LowPrecisionOpacity, float, 1.0f);
DECL_GFX_PREF(Live, "layers.low-precision-resolution", LowPrecisionResolution, float, 0.25f);
DECL_GFX_PREF(Live, "layers.max-active", MaxActiveLayers, int32_t, -1);
DECL_GFX_PREF(Once, "layers.offmainthreadcomposition.enabled", LayersOffMainThreadCompositionEnabled, bool, false);
DECL_GFX_PREF(Once, "layers.offmainthreadcomposition.force-enabled", LayersOffMainThreadCompositionForceEnabled, bool, false);
DECL_GFX_PREF(Live, "layers.offmainthreadcomposition.frame-rate", LayersCompositionFrameRate, int32_t,-1);

View File

@ -526,6 +526,12 @@ HMDInfoOculus::SubmitFrame(RenderTargetSet *aRTSet, int32_t aInputFrameID)
RenderTargetSetOculus *rts = static_cast<RenderTargetSetOculus*>(aRTSet);
MOZ_ASSERT(rts->hmd != nullptr);
MOZ_ASSERT(rts->textureSet != nullptr);
MOZ_ASSERT(aInputFrameID >= 0);
if (aInputFrameID < 0) {
// Sanity check to prevent invalid memory access on builds with assertions
// disabled.
aInputFrameID = 0;
}
VRHMDSensorState sensorState = mLastSensorState[aInputFrameID % kMaxLatencyFrames];
// It is possible to get a cache miss on mLastSensorState if latency is
@ -535,6 +541,7 @@ HMDInfoOculus::SubmitFrame(RenderTargetSet *aRTSet, int32_t aInputFrameID)
// un-viewable and a more accurate pose prediction is not likely to
// compensate.
ovrLayerEyeFov layer;
memset(&layer, 0, sizeof(layer));
layer.Header.Type = ovrLayerType_EyeFov;
layer.Header.Flags = 0;
layer.ColorTexture[0] = rts->textureSet;
@ -607,6 +614,7 @@ VRHMDManagerOculus::Init()
mOculusThread = already_AddRefed<nsIThread>(thread);
ovrInitParams params;
memset(&params, 0, sizeof(params));
params.Flags = ovrInit_RequestVersion;
params.RequestedMinorVersion = OVR_MINOR_VERSION;
params.LogCallback = nullptr;

View File

@ -543,6 +543,7 @@ VRHMDManagerOculus050::Init()
mOculusThread = already_AddRefed<nsIThread>(thread);
ovrInitParams params;
memset(&params, 0, sizeof(params));
params.Flags = ovrInit_RequestVersion;
params.RequestedMinorVersion = LIBOVR_MINOR_VERSION;
params.LogCallback = nullptr;

View File

@ -58,6 +58,7 @@
#include "mozilla/StaticMutex.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Preferences.h"
#include "mozilla/UniquePtrExtensions.h"
#include "nsAlgorithm.h"
#include "nsPrintfCString.h"
#include "nsIObserver.h"
@ -1242,7 +1243,7 @@ protected:
private:
double mLastLineChecked;
ScopedFreePtr<regex_t> mRegexes;
UniqueFreePtr<regex_t> mRegexes;
};
NS_IMPL_ISUPPORTS(OomVictimLogger, nsIObserver);
@ -1276,9 +1277,13 @@ OomVictimLogger::Observe(
// Compile our regex just in time
if (!mRegexes) {
mRegexes = static_cast<regex_t*>(malloc(sizeof(regex_t) * regex_count));
UniqueFreePtr<regex_t> regexes(
static_cast<regex_t*>(malloc(sizeof(regex_t) * regex_count))
);
mRegexes.swap(regexes);
for (size_t i = 0; i < regex_count; i++) {
int compilation_err = regcomp(&(mRegexes[i]), regexes_raw[i], REG_NOSUB);
int compilation_err =
regcomp(&(mRegexes.get()[i]), regexes_raw[i], REG_NOSUB);
if (compilation_err) {
OOM_LOG(ANDROID_LOG_ERROR, "Cannot compile regex \"%s\"\n", regexes_raw[i]);
return NS_OK;
@ -1298,16 +1303,16 @@ OomVictimLogger::Observe(
#endif
// Retreive kernel log
int msg_buf_size = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
ScopedFreePtr<char> msg_buf(static_cast<char *>(malloc(msg_buf_size + 1)));
int read_size = klogctl(KLOG_READ_ALL, msg_buf.rwget(), msg_buf_size);
UniqueFreePtr<char> msg_buf(static_cast<char *>(malloc(msg_buf_size + 1)));
int read_size = klogctl(KLOG_READ_ALL, msg_buf.get(), msg_buf_size);
// Turn buffer into cstring
read_size = read_size > msg_buf_size ? msg_buf_size : read_size;
msg_buf.rwget()[read_size] = '\0';
msg_buf.get()[read_size] = '\0';
// Foreach line
char* line_end;
char* line_begin = msg_buf.rwget();
char* line_begin = msg_buf.get();
for (; (line_end = strchr(line_begin, '\n')); line_begin = line_end + 1) {
// make line into cstring
*line_end = '\0';
@ -1340,7 +1345,7 @@ OomVictimLogger::Observe(
// Log interesting lines
for (size_t i = 0; i < regex_count; i++) {
int matching = !regexec(&(mRegexes[i]), line_begin, 0, NULL, 0);
int matching = !regexec(&(mRegexes.get()[i]), line_begin, 0, NULL, 0);
if (matching) {
// Log content of kernel message. We try to skip the ], but if for
// some reason (most likely due to buffer overflow/wraparound), we

View File

@ -33,7 +33,7 @@ FrameAnimator::GetSingleLoopTime() const
return -1;
}
uint32_t looptime = 0;
int32_t looptime = 0;
for (uint32_t i = 0; i < mImage->GetNumFrames(); ++i) {
int32_t timeout = GetTimeoutForFrame(i);
if (timeout >= 0) {
@ -180,14 +180,19 @@ FrameAnimator::AdvanceFrame(TimeStamp aTime)
mCurrentAnimationFrameTime = GetCurrentImgFrameEndTime();
// If we can get closer to the current time by a multiple of the image's loop
// time, we should.
uint32_t loopTime = GetSingleLoopTime();
// time, we should. We need to be done decoding in order to know the full loop
// time though!
int32_t loopTime = GetSingleLoopTime();
if (loopTime > 0) {
// We shouldn't be advancing by a whole loop unless we are decoded and know
// what a full loop actually is. GetSingleLoopTime should return -1 so this
// never happens.
MOZ_ASSERT(mDoneDecoding);
TimeDuration delay = aTime - mCurrentAnimationFrameTime;
if (delay.ToMilliseconds() > loopTime) {
// Explicitly use integer division to get the floor of the number of
// loops.
uint32_t loops = static_cast<uint32_t>(delay.ToMilliseconds()) / loopTime;
uint64_t loops = static_cast<uint64_t>(delay.ToMilliseconds()) / loopTime;
mCurrentAnimationFrameTime +=
TimeDuration::FromMilliseconds(loops * loopTime);
}

View File

@ -78,6 +78,8 @@ RasterImage::RasterImage(ImageURL* aURI /* = nullptr */) :
mLockCount(0),
mDecodeCount(0),
mRequestedSampleSize(0),
mImageProducerID(ImageContainer::AllocateProducerID()),
mLastFrameID(0),
mLastImageContainerDrawResult(DrawResult::NOT_READY),
#ifdef DEBUG
mFramesNotified(0),
@ -691,7 +693,11 @@ RasterImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags)
// |image| holds a reference to a SourceSurface which in turn holds a lock on
// the current frame's VolatileBuffer, ensuring that it doesn't get freed as
// long as the layer system keeps this ImageContainer alive.
container->SetCurrentImageInTransaction(image);
AutoTArray<ImageContainer::NonOwningImage, 1> imageList;
imageList.AppendElement(ImageContainer::NonOwningImage(image, TimeStamp(),
mLastFrameID++,
mImageProducerID));
container->SetCurrentImagesInTransaction(imageList);
mLastImageContainerDrawResult = drawResult;
mImageContainer = container;
@ -718,7 +724,9 @@ RasterImage::UpdateImageContainer()
mLastImageContainerDrawResult = drawResult;
AutoTArray<ImageContainer::NonOwningImage, 1> imageList;
imageList.AppendElement(ImageContainer::NonOwningImage(image));
imageList.AppendElement(ImageContainer::NonOwningImage(image, TimeStamp(),
mLastFrameID++,
mImageProducerID));
container->SetCurrentImages(imageList);
}

View File

@ -36,6 +36,7 @@
#include "mozilla/TimeStamp.h"
#include "mozilla/WeakPtr.h"
#include "mozilla/UniquePtr.h"
#include "ImageContainer.h"
#ifdef DEBUG
#include "imgIContainerDebug.h"
#endif
@ -367,6 +368,9 @@ private: // data
// the layer system needs it.
WeakPtr<layers::ImageContainer> mImageContainer;
layers::ImageContainer::ProducerID mImageProducerID;
layers::ImageContainer::FrameID mLastFrameID;
// If mImageContainer is non-null, this contains the DrawResult we obtained
// the last time we updated it.
DrawResult mLastImageContainerDrawResult;

View File

@ -972,6 +972,10 @@ MessageChannel::Send(Message* aMsg, Message* aReply)
MOZ_RELEASE_ASSERT(mCurrentTransaction == transaction);
bool maybeTimedOut = !WaitForSyncNotify(handleWindowsMessages);
if (mListener->NeedArtificialSleep()) {
MonitorAutoUnlock unlock(*mMonitor);
mListener->ArtificialSleep();
}
if (!Connected()) {
ReportConnectionError("MessageChannel::SendAndWait");
@ -1376,21 +1380,27 @@ MessageChannel::DispatchMessage(const Message &aMsg)
MonitorAutoUnlock unlock(*mMonitor);
CxxStackFrame frame(*this, IN_MESSAGE, &aMsg);
mListener->ArtificialSleep();
if (aMsg.is_sync())
DispatchSyncMessage(aMsg, *getter_Transfers(reply));
else if (aMsg.is_interrupt())
DispatchInterruptMessage(aMsg, 0);
else
DispatchAsyncMessage(aMsg);
mListener->ArtificialSleep();
}
if (mCurrentTransaction != id) {
// The transaction has been canceled. Don't send a reply.
IPC_LOG("Nulling out reply due to cancellation, seqno=%d, xid=%d", aMsg.seqno(), id);
reply = nullptr;
}
}
if (reply && ChannelConnected == mChannelState) {
IPC_LOG("Sending reply seqno=%d, xid=%d", aMsg.seqno(), aMsg.transaction_id());
mLink->SendMessage(reply.forget());
}
}
@ -1634,6 +1644,15 @@ MessageChannel::WaitResponse(bool aWaitTimedOut)
bool
MessageChannel::WaitForSyncNotify(bool /* aHandleWindowsMessages */)
{
#ifdef DEBUG
// WARNING: We don't release the lock here. We can't because the link thread
// could signal at this time and we would miss it. Instead we require
// ArtificialTimeout() to be extremely simple.
if (mListener->ArtificialTimeout()) {
return false;
}
#endif
PRIntervalTime timeout = (kNoTimeout == mTimeoutMs) ?
PR_INTERVAL_NO_TIMEOUT :
PR_MillisecondsToInterval(mTimeoutMs);
@ -1670,6 +1689,7 @@ MessageChannel::ShouldContinueFromTimeout()
{
MonitorAutoUnlock unlock(*mMonitor);
cont = mListener->OnReplyTimeout();
mListener->ArtificialSleep();
}
static enum { UNKNOWN, NOT_DEBUGGING, DEBUGGING } sDebuggingChildren = UNKNOWN;

View File

@ -81,6 +81,33 @@ class MessageListener
MOZ_CRASH("Intentional IPDL crash");
}
// The code here is only useful for fuzzing. It should not be used for any
// other purpose.
#ifdef DEBUG
// Returns true if we should simulate a timeout.
// WARNING: This is a testing-only function that is called with the
// MessageChannel monitor held. Don't do anything fancy here or we could
// deadlock.
virtual bool ArtificialTimeout() {
return false;
}
// Returns true if we want to cause the worker thread to sleep with the
// monitor unlocked.
virtual bool NeedArtificialSleep() {
return false;
}
// This function should be implemented to sleep for some amount of time on
// the worker thread. Will only be called if NeedArtificialSleep() returns
// true.
virtual void ArtificialSleep() {}
#else
bool ArtificialTimeout() { return false; }
bool NeedArtificialSleep() { return false; }
void ArtificialSleep() {}
#endif
virtual void OnEnteredCxxStack() {
NS_RUNTIMEABORT("default impl shouldn't be invoked");
}

Some files were not shown because too many files have changed in this diff Show More