mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-02 22:37:50 +00:00
Merge inbound to m-c. a=merge
This commit is contained in:
commit
cbdddb04cd
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 || "",
|
||||
|
@ -165,7 +165,6 @@
|
||||
},
|
||||
{
|
||||
"name": "getRecent",
|
||||
"unsupported": true,
|
||||
"type": "function",
|
||||
"description": "Retrieves the recently added bookmarks.",
|
||||
"async": "callback",
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
72
dom/animation/AnimationPerformanceWarning.cpp
Normal file
72
dom/animation/AnimationPerformanceWarning.cpp
Normal 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
|
74
dom/animation/AnimationPerformanceWarning.h
Normal file
74
dom/animation/AnimationPerformanceWarning.h
Normal 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
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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',
|
||||
|
@ -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'
|
||||
|
330
dom/animation/test/chrome/test_animation_property_state.html
Normal file
330
dom/animation/test/chrome/test_animation_property_state.html
Normal 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>
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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(">");
|
||||
} 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(">");
|
||||
} 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);
|
||||
|
@ -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())
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
268
dom/base/test/test_websocket1.html
Normal file
268
dom/base/test/test_websocket1.html
Normal 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>
|
272
dom/base/test/test_websocket2.html
Normal file
272
dom/base/test/test_websocket2.html
Normal 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>
|
225
dom/base/test/test_websocket3.html
Normal file
225
dom/base/test/test_websocket3.html
Normal 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>
|
290
dom/base/test/test_websocket4.html
Normal file
290
dom/base/test/test_websocket4.html
Normal 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>
|
293
dom/base/test/test_websocket5.html
Normal file
293
dom/base/test/test_websocket5.html
Normal 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>
|
62
dom/base/test/websocket_helpers.js
Normal file
62
dom/base/test/websocket_helpers.js
Normal 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();
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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--) {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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';
|
||||
|
||||
|
@ -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);
|
||||
|
@ -503,7 +503,8 @@ UDPSocket::InitRemote(const nsAString& aLocalAddress,
|
||||
NS_ConvertUTF16toUTF8(aLocalAddress),
|
||||
aLocalPort,
|
||||
mAddressReuse,
|
||||
mLoopback);
|
||||
mLoopback,
|
||||
0);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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() {
|
||||
|
@ -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}`);
|
||||
});
|
||||
});
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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:
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
@ -15,6 +15,9 @@
|
||||
#include "Units.h" // for ScreenPoint
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class InputData;
|
||||
|
||||
namespace layers {
|
||||
|
||||
class AsyncPanZoomController;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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(¶ms, 0, sizeof(params));
|
||||
params.Flags = ovrInit_RequestVersion;
|
||||
params.RequestedMinorVersion = OVR_MINOR_VERSION;
|
||||
params.LogCallback = nullptr;
|
||||
|
@ -543,6 +543,7 @@ VRHMDManagerOculus050::Init()
|
||||
mOculusThread = already_AddRefed<nsIThread>(thread);
|
||||
|
||||
ovrInitParams params;
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
params.Flags = ovrInit_RequestVersion;
|
||||
params.RequestedMinorVersion = LIBOVR_MINOR_VERSION;
|
||||
params.LogCallback = nullptr;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user