mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge central to inbound
This commit is contained in:
commit
63f2e7c2e5
@ -35,51 +35,18 @@
|
|||||||
*
|
*
|
||||||
* ***** END LICENSE BLOCK ***** */
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
#include "nsDOMBlobBuilder.h"
|
||||||
#include "jstypedarray.h"
|
#include "jstypedarray.h"
|
||||||
#include "nsAutoPtr.h"
|
#include "nsAutoPtr.h"
|
||||||
#include "nsDOMClassInfoID.h"
|
#include "nsDOMClassInfoID.h"
|
||||||
#include "nsDOMFile.h"
|
|
||||||
#include "nsIMultiplexInputStream.h"
|
#include "nsIMultiplexInputStream.h"
|
||||||
#include "nsStringStream.h"
|
#include "nsStringStream.h"
|
||||||
#include "nsTArray.h"
|
#include "nsTArray.h"
|
||||||
#include "nsJSUtils.h"
|
#include "nsJSUtils.h"
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
#include "CheckedInt.h"
|
|
||||||
|
|
||||||
#include "mozilla/StdInt.h"
|
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
|
|
||||||
class nsDOMMultipartFile : public nsDOMFileBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// Create as a file
|
|
||||||
nsDOMMultipartFile(nsTArray<nsCOMPtr<nsIDOMBlob> > aBlobs,
|
|
||||||
const nsAString& aName,
|
|
||||||
const nsAString& aContentType)
|
|
||||||
: nsDOMFileBase(aName, aContentType, UINT64_MAX),
|
|
||||||
mBlobs(aBlobs)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create as a blob
|
|
||||||
nsDOMMultipartFile(nsTArray<nsCOMPtr<nsIDOMBlob> > aBlobs,
|
|
||||||
const nsAString& aContentType)
|
|
||||||
: nsDOMFileBase(aContentType, UINT64_MAX),
|
|
||||||
mBlobs(aBlobs)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
already_AddRefed<nsIDOMBlob>
|
|
||||||
CreateSlice(PRUint64 aStart, PRUint64 aLength, const nsAString& aContentType);
|
|
||||||
|
|
||||||
NS_IMETHOD GetSize(PRUint64*);
|
|
||||||
NS_IMETHOD GetInternalStream(nsIInputStream**);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
nsTArray<nsCOMPtr<nsIDOMBlob> > mBlobs;
|
|
||||||
};
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsDOMMultipartFile::GetSize(PRUint64* aLength)
|
nsDOMMultipartFile::GetSize(PRUint64* aLength)
|
||||||
{
|
{
|
||||||
@ -199,67 +166,6 @@ nsDOMMultipartFile::CreateSlice(PRUint64 aStart, PRUint64 aLength,
|
|||||||
return blob.forget();
|
return blob.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
class nsDOMBlobBuilder : public nsIDOMMozBlobBuilder
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
nsDOMBlobBuilder()
|
|
||||||
: mData(nsnull), mDataLen(0), mDataBufferLen(0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
NS_DECL_ISUPPORTS
|
|
||||||
NS_DECL_NSIDOMMOZBLOBBUILDER
|
|
||||||
protected:
|
|
||||||
nsresult AppendVoidPtr(void* aData, PRUint32 aLength);
|
|
||||||
nsresult AppendString(JSString* aString, JSContext* aCx);
|
|
||||||
nsresult AppendBlob(nsIDOMBlob* aBlob);
|
|
||||||
nsresult AppendArrayBuffer(JSObject* aBuffer);
|
|
||||||
|
|
||||||
bool ExpandBufferSize(PRUint64 aSize)
|
|
||||||
{
|
|
||||||
if (mDataBufferLen >= mDataLen + aSize) {
|
|
||||||
mDataLen += aSize;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start at 1 or we'll loop forever.
|
|
||||||
CheckedUint32 bufferLen = NS_MAX<PRUint32>(mDataBufferLen, 1);
|
|
||||||
while (bufferLen.valid() && bufferLen.value() < mDataLen + aSize)
|
|
||||||
bufferLen *= 2;
|
|
||||||
|
|
||||||
if (!bufferLen.valid())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// PR_ memory functions are still fallible
|
|
||||||
void* data = PR_Realloc(mData, bufferLen.value());
|
|
||||||
if (!data)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
mData = data;
|
|
||||||
mDataBufferLen = bufferLen.value();
|
|
||||||
mDataLen += aSize;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Flush() {
|
|
||||||
if (mData) {
|
|
||||||
// If we have some data, create a blob for it
|
|
||||||
// and put it on the stack
|
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMBlob> blob =
|
|
||||||
new nsDOMMemoryFile(mData, mDataLen, EmptyString(), EmptyString());
|
|
||||||
mBlobs.AppendElement(blob);
|
|
||||||
mData = nsnull; // The nsDOMMemoryFile takes ownership of the buffer
|
|
||||||
mDataLen = 0;
|
|
||||||
mDataBufferLen = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nsTArray<nsCOMPtr<nsIDOMBlob> > mBlobs;
|
|
||||||
void* mData;
|
|
||||||
PRUint64 mDataLen;
|
|
||||||
PRUint64 mDataBufferLen;
|
|
||||||
};
|
|
||||||
|
|
||||||
DOMCI_DATA(MozBlobBuilder, nsDOMBlobBuilder)
|
DOMCI_DATA(MozBlobBuilder, nsDOMBlobBuilder)
|
||||||
|
|
||||||
NS_IMPL_ADDREF(nsDOMBlobBuilder)
|
NS_IMPL_ADDREF(nsDOMBlobBuilder)
|
||||||
@ -271,7 +177,7 @@ NS_INTERFACE_MAP_BEGIN(nsDOMBlobBuilder)
|
|||||||
NS_INTERFACE_MAP_END
|
NS_INTERFACE_MAP_END
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsDOMBlobBuilder::AppendVoidPtr(void* aData, PRUint32 aLength)
|
nsDOMBlobBuilder::AppendVoidPtr(const void* aData, PRUint32 aLength)
|
||||||
{
|
{
|
||||||
NS_ENSURE_ARG_POINTER(aData);
|
NS_ENSURE_ARG_POINTER(aData);
|
||||||
|
|
||||||
@ -319,6 +225,14 @@ nsDOMBlobBuilder::AppendArrayBuffer(JSObject* aBuffer)
|
|||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsDOMBlobBuilder::GetBlob(const nsAString& aContentType,
|
nsDOMBlobBuilder::GetBlob(const nsAString& aContentType,
|
||||||
nsIDOMBlob** aBlob)
|
nsIDOMBlob** aBlob)
|
||||||
|
{
|
||||||
|
return GetBlobInternal(aContentType, true, aBlob);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsDOMBlobBuilder::GetBlobInternal(const nsAString& aContentType,
|
||||||
|
bool aClearBuffer,
|
||||||
|
nsIDOMBlob** aBlob)
|
||||||
{
|
{
|
||||||
NS_ENSURE_ARG(aBlob);
|
NS_ENSURE_ARG(aBlob);
|
||||||
|
|
||||||
@ -332,7 +246,9 @@ nsDOMBlobBuilder::GetBlob(const nsAString& aContentType,
|
|||||||
// the existing contents of the BlobBuilder should be included
|
// the existing contents of the BlobBuilder should be included
|
||||||
// in the next blob produced. This seems silly and has been raised
|
// in the next blob produced. This seems silly and has been raised
|
||||||
// on the WHATWG listserv.
|
// on the WHATWG listserv.
|
||||||
|
if (aClearBuffer) {
|
||||||
mBlobs.Clear();
|
mBlobs.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
143
content/base/src/nsDOMBlobBuilder.h
Normal file
143
content/base/src/nsDOMBlobBuilder.h
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Mozilla File API.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* Kyle Huey <me@kylehuey.com>
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
#ifndef nsDOMBlobBuilder_h
|
||||||
|
#define nsDOMBlobBuilder_h
|
||||||
|
|
||||||
|
#include "nsDOMFile.h"
|
||||||
|
#include "CheckedInt.h"
|
||||||
|
|
||||||
|
#include "mozilla/StdInt.h"
|
||||||
|
|
||||||
|
using namespace mozilla;
|
||||||
|
|
||||||
|
class nsDOMMultipartFile : public nsDOMFileBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Create as a file
|
||||||
|
nsDOMMultipartFile(nsTArray<nsCOMPtr<nsIDOMBlob> > aBlobs,
|
||||||
|
const nsAString& aName,
|
||||||
|
const nsAString& aContentType)
|
||||||
|
: nsDOMFileBase(aName, aContentType, UINT64_MAX),
|
||||||
|
mBlobs(aBlobs)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create as a blob
|
||||||
|
nsDOMMultipartFile(nsTArray<nsCOMPtr<nsIDOMBlob> > aBlobs,
|
||||||
|
const nsAString& aContentType)
|
||||||
|
: nsDOMFileBase(aContentType, UINT64_MAX),
|
||||||
|
mBlobs(aBlobs)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
already_AddRefed<nsIDOMBlob>
|
||||||
|
CreateSlice(PRUint64 aStart, PRUint64 aLength, const nsAString& aContentType);
|
||||||
|
|
||||||
|
NS_IMETHOD GetSize(PRUint64*);
|
||||||
|
NS_IMETHOD GetInternalStream(nsIInputStream**);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
nsTArray<nsCOMPtr<nsIDOMBlob> > mBlobs;
|
||||||
|
};
|
||||||
|
|
||||||
|
class nsDOMBlobBuilder : public nsIDOMMozBlobBuilder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
nsDOMBlobBuilder()
|
||||||
|
: mData(nsnull), mDataLen(0), mDataBufferLen(0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
NS_DECL_ISUPPORTS
|
||||||
|
NS_DECL_NSIDOMMOZBLOBBUILDER
|
||||||
|
|
||||||
|
nsresult GetBlobInternal(const nsAString& aContentType,
|
||||||
|
bool aClearBuffer, nsIDOMBlob** aBlob);
|
||||||
|
nsresult AppendVoidPtr(const void* aData, PRUint32 aLength);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
nsresult AppendString(JSString* aString, JSContext* aCx);
|
||||||
|
nsresult AppendBlob(nsIDOMBlob* aBlob);
|
||||||
|
nsresult AppendArrayBuffer(JSObject* aBuffer);
|
||||||
|
|
||||||
|
bool ExpandBufferSize(PRUint64 aSize)
|
||||||
|
{
|
||||||
|
if (mDataBufferLen >= mDataLen + aSize) {
|
||||||
|
mDataLen += aSize;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start at 1 or we'll loop forever.
|
||||||
|
CheckedUint32 bufferLen = NS_MAX<PRUint32>(mDataBufferLen, 1);
|
||||||
|
while (bufferLen.valid() && bufferLen.value() < mDataLen + aSize)
|
||||||
|
bufferLen *= 2;
|
||||||
|
|
||||||
|
if (!bufferLen.valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// PR_ memory functions are still fallible
|
||||||
|
void* data = PR_Realloc(mData, bufferLen.value());
|
||||||
|
if (!data)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
mData = data;
|
||||||
|
mDataBufferLen = bufferLen.value();
|
||||||
|
mDataLen += aSize;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Flush() {
|
||||||
|
if (mData) {
|
||||||
|
// If we have some data, create a blob for it
|
||||||
|
// and put it on the stack
|
||||||
|
|
||||||
|
nsCOMPtr<nsIDOMBlob> blob =
|
||||||
|
new nsDOMMemoryFile(mData, mDataLen, EmptyString(), EmptyString());
|
||||||
|
mBlobs.AppendElement(blob);
|
||||||
|
mData = nsnull; // The nsDOMMemoryFile takes ownership of the buffer
|
||||||
|
mDataLen = 0;
|
||||||
|
mDataBufferLen = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nsTArray<nsCOMPtr<nsIDOMBlob> > mBlobs;
|
||||||
|
void* mData;
|
||||||
|
PRUint64 mDataLen;
|
||||||
|
PRUint64 mDataBufferLen;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -582,6 +582,8 @@ nsXMLHttpRequest::ResetResponse()
|
|||||||
mResponseBody.Truncate();
|
mResponseBody.Truncate();
|
||||||
mResponseText.Truncate();
|
mResponseText.Truncate();
|
||||||
mResponseBlob = nsnull;
|
mResponseBlob = nsnull;
|
||||||
|
mDOMFile = nsnull;
|
||||||
|
mBuilder = nsnull;
|
||||||
mResultArrayBuffer = nsnull;
|
mResultArrayBuffer = nsnull;
|
||||||
mResultJSON = JSVAL_VOID;
|
mResultJSON = JSVAL_VOID;
|
||||||
mLoadTransferred = 0;
|
mLoadTransferred = 0;
|
||||||
@ -967,6 +969,28 @@ nsXMLHttpRequest::CreateResponseParsedJSON(JSContext* aCx)
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsXMLHttpRequest::CreatePartialBlob()
|
||||||
|
{
|
||||||
|
if (mDOMFile) {
|
||||||
|
if (mLoadTotal == mLoadTransferred) {
|
||||||
|
mResponseBlob = mDOMFile;
|
||||||
|
} else {
|
||||||
|
mResponseBlob =
|
||||||
|
mDOMFile->CreateSlice(0, mLoadTransferred, EmptyString());
|
||||||
|
}
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCAutoString contentType;
|
||||||
|
if (mLoadTotal == mLoadTransferred) {
|
||||||
|
mChannel->GetContentType(contentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mBuilder->GetBlobInternal(NS_ConvertASCIItoUTF16(contentType),
|
||||||
|
false, getter_AddRefs(mResponseBlob));
|
||||||
|
}
|
||||||
|
|
||||||
/* attribute AString responseType; */
|
/* attribute AString responseType; */
|
||||||
NS_IMETHODIMP nsXMLHttpRequest::GetResponseType(nsAString& aResponseType)
|
NS_IMETHODIMP nsXMLHttpRequest::GetResponseType(nsAString& aResponseType)
|
||||||
{
|
{
|
||||||
@ -995,6 +1019,9 @@ NS_IMETHODIMP nsXMLHttpRequest::GetResponseType(nsAString& aResponseType)
|
|||||||
case XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER:
|
case XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER:
|
||||||
aResponseType.AssignLiteral("moz-chunked-arraybuffer");
|
aResponseType.AssignLiteral("moz-chunked-arraybuffer");
|
||||||
break;
|
break;
|
||||||
|
case XML_HTTP_RESPONSE_TYPE_MOZ_BLOB:
|
||||||
|
aResponseType.AssignLiteral("moz-blob");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
NS_ERROR("Should not happen");
|
NS_ERROR("Should not happen");
|
||||||
}
|
}
|
||||||
@ -1041,6 +1068,8 @@ NS_IMETHODIMP nsXMLHttpRequest::SetResponseType(const nsAString& aResponseType)
|
|||||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||||
}
|
}
|
||||||
mResponseType = XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER;
|
mResponseType = XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER;
|
||||||
|
} else if (aResponseType.EqualsLiteral("moz-blob")) {
|
||||||
|
mResponseType = XML_HTTP_RESPONSE_TYPE_MOZ_BLOB;
|
||||||
}
|
}
|
||||||
// If the given value is not the empty string, "arraybuffer",
|
// If the given value is not the empty string, "arraybuffer",
|
||||||
// "blob", "document", or "text" terminate these steps.
|
// "blob", "document", or "text" terminate these steps.
|
||||||
@ -1053,7 +1082,8 @@ NS_IMETHODIMP nsXMLHttpRequest::SetResponseType(const nsAString& aResponseType)
|
|||||||
if (mState & XML_HTTP_REQUEST_HEADERS_RECEIVED) {
|
if (mState & XML_HTTP_REQUEST_HEADERS_RECEIVED) {
|
||||||
nsCOMPtr<nsICachingChannel> cc(do_QueryInterface(mChannel));
|
nsCOMPtr<nsICachingChannel> cc(do_QueryInterface(mChannel));
|
||||||
if (cc) {
|
if (cc) {
|
||||||
cc->SetCacheAsFile(mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB);
|
cc->SetCacheAsFile(mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
|
||||||
|
mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1097,12 +1127,22 @@ NS_IMETHODIMP nsXMLHttpRequest::GetResponse(JSContext *aCx, jsval *aResult)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case XML_HTTP_RESPONSE_TYPE_BLOB:
|
case XML_HTTP_RESPONSE_TYPE_BLOB:
|
||||||
if (mState & XML_HTTP_REQUEST_DONE && mResponseBlob) {
|
case XML_HTTP_RESPONSE_TYPE_MOZ_BLOB:
|
||||||
|
*aResult = JSVAL_NULL;
|
||||||
|
if (mState & XML_HTTP_REQUEST_DONE) {
|
||||||
|
// do nothing here
|
||||||
|
} else if (mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
|
||||||
|
if (!mResponseBlob) {
|
||||||
|
rv = CreatePartialBlob();
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
if (mResponseBlob) {
|
||||||
JSObject* scope = JS_GetGlobalForScopeChain(aCx);
|
JSObject* scope = JS_GetGlobalForScopeChain(aCx);
|
||||||
rv = nsContentUtils::WrapNative(aCx, scope, mResponseBlob, aResult,
|
rv = nsContentUtils::WrapNative(aCx, scope, mResponseBlob, aResult,
|
||||||
nsnull, true);
|
nsnull, true);
|
||||||
} else {
|
|
||||||
*aResult = JSVAL_NULL;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1712,16 +1752,29 @@ nsXMLHttpRequest::StreamReaderFunc(nsIInputStream* in,
|
|||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB &&
|
nsresult rv = NS_OK;
|
||||||
xmlHttpRequest->mResponseBlob) {
|
|
||||||
|
if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
|
||||||
|
xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
|
||||||
|
if (!xmlHttpRequest->mDOMFile) {
|
||||||
|
if (!xmlHttpRequest->mBuilder) {
|
||||||
|
xmlHttpRequest->mBuilder = new nsDOMBlobBuilder();
|
||||||
|
}
|
||||||
|
rv = xmlHttpRequest->mBuilder->AppendVoidPtr(fromRawSegment, count);
|
||||||
|
}
|
||||||
|
// Clear the cache so that the blob size is updated.
|
||||||
|
if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
|
||||||
|
xmlHttpRequest->mResponseBlob = nsnull;
|
||||||
|
}
|
||||||
|
if (NS_SUCCEEDED(rv)) {
|
||||||
*writeCount = count;
|
*writeCount = count;
|
||||||
return NS_OK;
|
}
|
||||||
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_DEFAULT &&
|
if ((xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_DEFAULT &&
|
||||||
xmlHttpRequest->mResponseXML) ||
|
xmlHttpRequest->mResponseXML) ||
|
||||||
xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER ||
|
xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER ||
|
||||||
xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
|
|
||||||
xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER) {
|
xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER) {
|
||||||
// Copy for our own use
|
// Copy for our own use
|
||||||
PRUint32 previousLength = xmlHttpRequest->mResponseBody.Length();
|
PRUint32 previousLength = xmlHttpRequest->mResponseBody.Length();
|
||||||
@ -1738,8 +1791,6 @@ nsXMLHttpRequest::StreamReaderFunc(nsIInputStream* in,
|
|||||||
xmlHttpRequest->AppendToResponseText(fromRawSegment, count);
|
xmlHttpRequest->AppendToResponseText(fromRawSegment, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult rv = NS_OK;
|
|
||||||
|
|
||||||
if (xmlHttpRequest->mState & XML_HTTP_REQUEST_PARSEBODY) {
|
if (xmlHttpRequest->mState & XML_HTTP_REQUEST_PARSEBODY) {
|
||||||
// Give the same data to the parser.
|
// Give the same data to the parser.
|
||||||
|
|
||||||
@ -1773,7 +1824,7 @@ nsXMLHttpRequest::StreamReaderFunc(nsIInputStream* in,
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nsXMLHttpRequest::CreateResponseBlob(nsIRequest *request)
|
bool nsXMLHttpRequest::CreateDOMFile(nsIRequest *request)
|
||||||
{
|
{
|
||||||
nsCOMPtr<nsIFile> file;
|
nsCOMPtr<nsIFile> file;
|
||||||
nsCOMPtr<nsICachingChannel> cc(do_QueryInterface(request));
|
nsCOMPtr<nsICachingChannel> cc(do_QueryInterface(request));
|
||||||
@ -1801,9 +1852,10 @@ bool nsXMLHttpRequest::CreateResponseBlob(nsIRequest *request)
|
|||||||
fromFile = true;
|
fromFile = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
mResponseBlob =
|
mDOMFile =
|
||||||
new nsDOMFileFile(file, NS_ConvertASCIItoUTF16(contentType), cacheToken);
|
new nsDOMFileFile(file, NS_ConvertASCIItoUTF16(contentType), cacheToken);
|
||||||
mResponseBody.Truncate();
|
mBuilder = nsnull;
|
||||||
|
NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
|
||||||
}
|
}
|
||||||
return fromFile;
|
return fromFile;
|
||||||
}
|
}
|
||||||
@ -1822,8 +1874,9 @@ nsXMLHttpRequest::OnDataAvailable(nsIRequest *request,
|
|||||||
mProgressSinceLastProgressEvent = true;
|
mProgressSinceLastProgressEvent = true;
|
||||||
|
|
||||||
bool cancelable = false;
|
bool cancelable = false;
|
||||||
if (mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB && !mResponseBlob) {
|
if ((mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
|
||||||
cancelable = CreateResponseBlob(request);
|
mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) && !mDOMFile) {
|
||||||
|
cancelable = CreateDOMFile(request);
|
||||||
// The nsIStreamListener contract mandates us
|
// The nsIStreamListener contract mandates us
|
||||||
// to read from the stream before returning.
|
// to read from the stream before returning.
|
||||||
}
|
}
|
||||||
@ -1835,7 +1888,7 @@ nsXMLHttpRequest::OnDataAvailable(nsIRequest *request,
|
|||||||
|
|
||||||
if (cancelable) {
|
if (cancelable) {
|
||||||
// We don't have to read from the local file for the blob response
|
// We don't have to read from the local file for the blob response
|
||||||
mResponseBlob->GetSize(&mLoadTransferred);
|
mDOMFile->GetSize(&mLoadTransferred);
|
||||||
ChangeState(XML_HTTP_REQUEST_LOADING);
|
ChangeState(XML_HTTP_REQUEST_LOADING);
|
||||||
return request->Cancel(NS_OK);
|
return request->Cancel(NS_OK);
|
||||||
}
|
}
|
||||||
@ -1936,7 +1989,8 @@ nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
|
|||||||
mState &= ~XML_HTTP_REQUEST_MPART_HEADERS;
|
mState &= ~XML_HTTP_REQUEST_MPART_HEADERS;
|
||||||
ChangeState(XML_HTTP_REQUEST_HEADERS_RECEIVED);
|
ChangeState(XML_HTTP_REQUEST_HEADERS_RECEIVED);
|
||||||
|
|
||||||
if (mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB) {
|
if (mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
|
||||||
|
mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
|
||||||
nsCOMPtr<nsICachingChannel> cc(do_QueryInterface(mChannel));
|
nsCOMPtr<nsICachingChannel> cc(do_QueryInterface(mChannel));
|
||||||
if (cc) {
|
if (cc) {
|
||||||
cc->SetCacheAsFile(true);
|
cc->SetCacheAsFile(true);
|
||||||
@ -2132,33 +2186,30 @@ nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult
|
|||||||
MaybeDispatchProgressEvents(true);
|
MaybeDispatchProgressEvents(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
|
if (NS_SUCCEEDED(status) &&
|
||||||
NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
|
(mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
|
||||||
|
mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB)) {
|
||||||
if (NS_SUCCEEDED(status) && mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB) {
|
if (!mDOMFile) {
|
||||||
if (!mResponseBlob) {
|
CreateDOMFile(request);
|
||||||
CreateResponseBlob(request);
|
|
||||||
}
|
}
|
||||||
if (!mResponseBlob) {
|
if (mDOMFile) {
|
||||||
|
mResponseBlob = mDOMFile;
|
||||||
|
mDOMFile = nsnull;
|
||||||
|
} else {
|
||||||
// Smaller files may be written in cache map instead of separate files.
|
// Smaller files may be written in cache map instead of separate files.
|
||||||
// Also, no-store response cannot be written in persistent cache.
|
// Also, no-store response cannot be written in persistent cache.
|
||||||
nsCAutoString contentType;
|
nsCAutoString contentType;
|
||||||
mChannel->GetContentType(contentType);
|
mChannel->GetContentType(contentType);
|
||||||
// XXX We should change mResponseBody to be a raw malloc'ed buffer
|
mBuilder->GetBlobInternal(NS_ConvertASCIItoUTF16(contentType),
|
||||||
// to avoid copying the data.
|
false, getter_AddRefs(mResponseBlob));
|
||||||
PRUint32 blobLen = mResponseBody.Length();
|
mBuilder = nsnull;
|
||||||
void *blobData = PR_Malloc(blobLen);
|
|
||||||
if (blobData) {
|
|
||||||
memcpy(blobData, mResponseBody.BeginReading(), blobLen);
|
|
||||||
|
|
||||||
mResponseBlob =
|
|
||||||
new nsDOMMemoryFile(blobData, blobLen,
|
|
||||||
NS_ConvertASCIItoUTF16(contentType));
|
|
||||||
mResponseBody.Truncate();
|
|
||||||
}
|
}
|
||||||
|
NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
|
||||||
NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");
|
NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
|
||||||
|
NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
|
||||||
|
|
||||||
channel->SetNotificationCallbacks(nsnull);
|
channel->SetNotificationCallbacks(nsnull);
|
||||||
mNotificationCallbacks = nsnull;
|
mNotificationCallbacks = nsnull;
|
||||||
|
@ -65,6 +65,8 @@
|
|||||||
#include "nsDOMProgressEvent.h"
|
#include "nsDOMProgressEvent.h"
|
||||||
#include "nsDOMEventTargetWrapperCache.h"
|
#include "nsDOMEventTargetWrapperCache.h"
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
|
#include "nsDOMFile.h"
|
||||||
|
#include "nsDOMBlobBuilder.h"
|
||||||
|
|
||||||
class nsILoadGroup;
|
class nsILoadGroup;
|
||||||
class AsyncVerifyRedirectCallbackForwarder;
|
class AsyncVerifyRedirectCallbackForwarder;
|
||||||
@ -217,7 +219,8 @@ protected:
|
|||||||
PRUint32 count,
|
PRUint32 count,
|
||||||
PRUint32 *writeCount);
|
PRUint32 *writeCount);
|
||||||
nsresult CreateResponseParsedJSON(JSContext* aCx);
|
nsresult CreateResponseParsedJSON(JSContext* aCx);
|
||||||
bool CreateResponseBlob(nsIRequest *request);
|
nsresult CreatePartialBlob(void);
|
||||||
|
bool CreateDOMFile(nsIRequest *request);
|
||||||
// Change the state of the object with this. The broadcast argument
|
// Change the state of the object with this. The broadcast argument
|
||||||
// determines if the onreadystatechange listener should be called.
|
// determines if the onreadystatechange listener should be called.
|
||||||
nsresult ChangeState(PRUint32 aState, bool aBroadcast = true);
|
nsresult ChangeState(PRUint32 aState, bool aBroadcast = true);
|
||||||
@ -309,10 +312,20 @@ protected:
|
|||||||
XML_HTTP_RESPONSE_TYPE_TEXT,
|
XML_HTTP_RESPONSE_TYPE_TEXT,
|
||||||
XML_HTTP_RESPONSE_TYPE_JSON,
|
XML_HTTP_RESPONSE_TYPE_JSON,
|
||||||
XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT,
|
XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT,
|
||||||
XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER
|
XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER,
|
||||||
|
XML_HTTP_RESPONSE_TYPE_MOZ_BLOB
|
||||||
} mResponseType;
|
} mResponseType;
|
||||||
|
|
||||||
|
// It is either a cached blob-response from the last call to GetResponse,
|
||||||
|
// but is also explicitly set in OnStopRequest.
|
||||||
nsCOMPtr<nsIDOMBlob> mResponseBlob;
|
nsCOMPtr<nsIDOMBlob> mResponseBlob;
|
||||||
|
// Non-null only when we are able to get a os-file representation of the
|
||||||
|
// response, i.e. when loading from a file, or when the http-stream
|
||||||
|
// caches into a file or is reading from a cached file.
|
||||||
|
nsRefPtr<nsDOMFileBase> mDOMFile;
|
||||||
|
// We stream data to mBuilder when response type is "blob" or "moz-blob"
|
||||||
|
// and mDOMFile is null.
|
||||||
|
nsRefPtr<nsDOMBlobBuilder> mBuilder;
|
||||||
|
|
||||||
nsCString mOverrideMimeType;
|
nsCString mOverrideMimeType;
|
||||||
|
|
||||||
|
@ -212,13 +212,16 @@ is(xhr.response, null, "Bad JSON should result in null response even 2nd time.")
|
|||||||
// test response (responseType='blob')
|
// test response (responseType='blob')
|
||||||
var onloadCount = 0;
|
var onloadCount = 0;
|
||||||
function checkOnloadCount() {
|
function checkOnloadCount() {
|
||||||
if (++onloadCount >= 3) SimpleTest.finish();
|
if (++onloadCount >= 6) SimpleTest.finish();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var responseTypes = ['blob', 'moz-blob'];
|
||||||
|
for (var i = 0; i < responseTypes.length; i++) {
|
||||||
|
var t = responseTypes[i];
|
||||||
// with a simple text file
|
// with a simple text file
|
||||||
xhr = new XMLHttpRequest();
|
xhr = new XMLHttpRequest();
|
||||||
xhr.open("GET", 'file_XHR_pass2.txt');
|
xhr.open("GET", 'file_XHR_pass2.txt');
|
||||||
xhr.responseType = 'blob';
|
xhr.responseType = t;
|
||||||
xhr.onloadend = continueTest;
|
xhr.onloadend = continueTest;
|
||||||
xhr.send(null);
|
xhr.send(null);
|
||||||
yield;
|
yield;
|
||||||
@ -245,7 +248,7 @@ xhr.onreadystatechange = function() {
|
|||||||
switch (xhr.readyState) {
|
switch (xhr.readyState) {
|
||||||
case 2:
|
case 2:
|
||||||
is(xhr.status, 200, "wrong status");
|
is(xhr.status, 200, "wrong status");
|
||||||
xhr.responseType = 'blob';
|
xhr.responseType = t;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
b = xhr.response;
|
b = xhr.response;
|
||||||
@ -293,9 +296,10 @@ xhr.onreadystatechange = function() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
xhr.open("GET", 'file_XHR_binary2.bin', true);
|
xhr.open("GET", 'file_XHR_binary2.bin', true);
|
||||||
xhr.responseType = 'blob';
|
xhr.responseType = t;
|
||||||
xhr.send(null);
|
xhr.send(null);
|
||||||
})();
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
var client = new XMLHttpRequest();
|
var client = new XMLHttpRequest();
|
||||||
client.onreadystatechange = function() {
|
client.onreadystatechange = function() {
|
||||||
|
@ -39,13 +39,21 @@ function updateProgress(e, data, testName) {
|
|||||||
is(typeof e.target.response, "string", "response should be a string" + test);
|
is(typeof e.target.response, "string", "response should be a string" + test);
|
||||||
response = e.target.response;
|
response = e.target.response;
|
||||||
}
|
}
|
||||||
|
else if (data.blob) {
|
||||||
|
ok(e.target.response instanceof Blob, "response should be a Blob" + test);
|
||||||
|
response = e.target.response;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
ok(e.target.response instanceof ArrayBuffer, "response should be a ArrayBuffer" + test);
|
ok(e.target.response instanceof ArrayBuffer, "response should be an ArrayBuffer" + test);
|
||||||
response = bufferToString(e.target.response);
|
response = bufferToString(e.target.response);
|
||||||
}
|
}
|
||||||
|
is(e.target.response, e.target.response, "reflexivity should hold" + test);
|
||||||
|
|
||||||
if (!data.nodata && !data.encoded) {
|
if (!data.nodata && !data.encoded) {
|
||||||
if (!data.chunked) {
|
if (data.blob) {
|
||||||
|
is(e.loaded, response.size, "event.loaded matches response size" + test);
|
||||||
|
}
|
||||||
|
else if (!data.chunked) {
|
||||||
is(e.loaded, response.length, "event.loaded matches response size" + test);
|
is(e.loaded, response.length, "event.loaded matches response size" + test);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -57,7 +65,7 @@ function updateProgress(e, data, testName) {
|
|||||||
ok(e.loaded - data.receivedBytes <= data.pendingBytes,
|
ok(e.loaded - data.receivedBytes <= data.pendingBytes,
|
||||||
"event.loaded didn't increase too much" + test);
|
"event.loaded didn't increase too much" + test);
|
||||||
|
|
||||||
if (!data.nodata) {
|
if (!data.nodata && !data.blob) {
|
||||||
var newData;
|
var newData;
|
||||||
ok(startsWith(response, data.receivedResult),
|
ok(startsWith(response, data.receivedResult),
|
||||||
"response strictly grew" + test);
|
"response strictly grew" + test);
|
||||||
@ -74,7 +82,7 @@ function updateProgress(e, data, testName) {
|
|||||||
is(e.total, data.total, "total" + test);
|
is(e.total, data.total, "total" + test);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data.nodata) {
|
if (!data.nodata && !data.blob) {
|
||||||
data.pendingResult = data.pendingResult.substr(newData.length);
|
data.pendingResult = data.pendingResult.substr(newData.length);
|
||||||
}
|
}
|
||||||
data.pendingBytes -= e.loaded - data.receivedBytes;
|
data.pendingBytes -= e.loaded - data.receivedBytes;
|
||||||
@ -113,7 +121,8 @@ function runTests() {
|
|||||||
|
|
||||||
var responseTypes = [{ type: "text", text: true },
|
var responseTypes = [{ type: "text", text: true },
|
||||||
{ type: "arraybuffer", text: false, nodata: true },
|
{ type: "arraybuffer", text: false, nodata: true },
|
||||||
{ type: "blob", text: false, nodata: true },
|
{ type: "blob", text: false, nodata: true, blob: true },
|
||||||
|
{ type: "moz-blob", text: false, nodata: false, blob: true },
|
||||||
{ type: "document", text: true, nodata: true },
|
{ type: "document", text: true, nodata: true },
|
||||||
{ type: "json", text: true, nodata: true },
|
{ type: "json", text: true, nodata: true },
|
||||||
{ type: "", text: true },
|
{ type: "", text: true },
|
||||||
@ -153,7 +162,7 @@ function runTests() {
|
|||||||
{ data: utf8encode("a\u867Eb").substr(4), utf16: "b" },
|
{ data: utf8encode("a\u867Eb").substr(4), utf16: "b" },
|
||||||
{ close: true },
|
{ close: true },
|
||||||
];
|
];
|
||||||
if (responseType.type === "blob") {
|
if (responseType.blob) {
|
||||||
tests.push({ file: "file_XHR_binary2.bin", name: "cacheable data", total: 65536 },
|
tests.push({ file: "file_XHR_binary2.bin", name: "cacheable data", total: 65536 },
|
||||||
{ close: true },
|
{ close: true },
|
||||||
{ file: "file_XHR_binary2.bin", name: "cached data", total: 65536 },
|
{ file: "file_XHR_binary2.bin", name: "cached data", total: 65536 },
|
||||||
@ -177,6 +186,7 @@ function runTests() {
|
|||||||
nodata: responseType.nodata,
|
nodata: responseType.nodata,
|
||||||
chunked: responseType.chunked,
|
chunked: responseType.chunked,
|
||||||
text: responseType.text,
|
text: responseType.text,
|
||||||
|
blob: responseType.blob,
|
||||||
file: test.file };
|
file: test.file };
|
||||||
|
|
||||||
xhr.onreadystatechange = null;
|
xhr.onreadystatechange = null;
|
||||||
@ -235,7 +245,7 @@ function runTests() {
|
|||||||
is(xhr.response, null, "chunked data has null response for " + testState.name);
|
is(xhr.response, null, "chunked data has null response for " + testState.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!testState.nodata || responseType.chunked) {
|
if (!testState.nodata && !responseType.blob || responseType.chunked) {
|
||||||
// This branch intentionally left blank
|
// This branch intentionally left blank
|
||||||
// Under these conditions we check the response during updateProgress
|
// Under these conditions we check the response during updateProgress
|
||||||
}
|
}
|
||||||
@ -243,7 +253,7 @@ function runTests() {
|
|||||||
is(bufferToString(xhr.response), testState.pendingResult,
|
is(bufferToString(xhr.response), testState.pendingResult,
|
||||||
"full response for " + testState.name);
|
"full response for " + testState.name);
|
||||||
}
|
}
|
||||||
else if (responseType.type === "blob") {
|
else if (responseType.blob) {
|
||||||
let reader = new FileReader;
|
let reader = new FileReader;
|
||||||
reader.readAsBinaryString(xhr.response);
|
reader.readAsBinaryString(xhr.response);
|
||||||
reader.onloadend = getEvent;
|
reader.onloadend = getEvent;
|
||||||
@ -280,7 +290,7 @@ function runTests() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!testState.nodata) {
|
if (!testState.nodata && !testState.blob) {
|
||||||
is(testState.pendingResult, "",
|
is(testState.pendingResult, "",
|
||||||
"should have consumed the expected result");
|
"should have consumed the expected result");
|
||||||
}
|
}
|
||||||
|
@ -127,6 +127,7 @@ _TEST_FILES = \
|
|||||||
test_loop.html \
|
test_loop.html \
|
||||||
test_media_selection.html \
|
test_media_selection.html \
|
||||||
test_mozLoadFrom.html \
|
test_mozLoadFrom.html \
|
||||||
|
test_no_load_event.html \
|
||||||
test_networkState.html \
|
test_networkState.html \
|
||||||
test_new_audio.html \
|
test_new_audio.html \
|
||||||
test_paused.html \
|
test_paused.html \
|
||||||
|
59
content/media/test/test_no_load_event.html
Normal file
59
content/media/test/test_no_load_event.html
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<!--
|
||||||
|
https://bugzilla.mozilla.org/show_bug.cgi?id=715469
|
||||||
|
-->
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Test for Bug 715469</title>
|
||||||
|
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
<script type="text/javascript" src="manifest.js"></script>
|
||||||
|
</head>
|
||||||
|
<body onload="start();">
|
||||||
|
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=715469">Mozilla Bug 715469</a>
|
||||||
|
<p id="display"></p>
|
||||||
|
<div id="content" style="display: none">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<pre id="test">
|
||||||
|
|
||||||
|
<script type="application/javascript">
|
||||||
|
|
||||||
|
/** Test for Bug 715469 **/
|
||||||
|
|
||||||
|
var gotLoadEvent = false;
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
var resource = getPlayableVideo(gSmallTests);
|
||||||
|
if (resource == null) {
|
||||||
|
todo(false, "No types supported");
|
||||||
|
} else {
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
var v = document.createElement("video");
|
||||||
|
v.src = resource.name;
|
||||||
|
v.addEventListener("loadeddata", function(){v.play();}, false);
|
||||||
|
v.controls = "true";
|
||||||
|
|
||||||
|
v.addEventListener("load",
|
||||||
|
function(){
|
||||||
|
gotLoadEvent = true;
|
||||||
|
},
|
||||||
|
false);
|
||||||
|
|
||||||
|
v.addEventListener("ended", finished, false);
|
||||||
|
|
||||||
|
document.body.appendChild(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function finished() {
|
||||||
|
is(gotLoadEvent, false, "Should not receive a load on the video element");
|
||||||
|
SimpleTest.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -953,6 +953,7 @@ RasterImage::GetImageContainer(LayerManager* aManager,
|
|||||||
(!mImageContainer->Manager() &&
|
(!mImageContainer->Manager() &&
|
||||||
(mImageContainer->GetBackendType() == aManager->GetBackendType())))) {
|
(mImageContainer->GetBackendType() == aManager->GetBackendType())))) {
|
||||||
*_retval = mImageContainer;
|
*_retval = mImageContainer;
|
||||||
|
NS_ADDREF(*_retval);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -978,6 +979,7 @@ RasterImage::GetImageContainer(LayerManager* aManager,
|
|||||||
mImageContainer->SetCurrentImage(image);
|
mImageContainer->SetCurrentImage(image);
|
||||||
|
|
||||||
*_retval = mImageContainer;
|
*_retval = mImageContainer;
|
||||||
|
NS_ADDREF(*_retval);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,7 +219,7 @@ protected:
|
|||||||
* supports being optimized to an ImageLayer (TYPE_RASTER only) returns
|
* supports being optimized to an ImageLayer (TYPE_RASTER only) returns
|
||||||
* an ImageContainer for the image.
|
* an ImageContainer for the image.
|
||||||
*/
|
*/
|
||||||
nsRefPtr<ImageContainer> CanOptimizeImageLayer(LayerManager* aManager);
|
already_AddRefed<ImageContainer> CanOptimizeImageLayer(LayerManager* aManager);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The region of visible content in the layer, relative to the
|
* The region of visible content in the layer, relative to the
|
||||||
@ -968,7 +968,7 @@ ContainerState::FindOpaqueBackgroundColorFor(PRInt32 aThebesLayerIndex)
|
|||||||
return NS_RGBA(0,0,0,0);
|
return NS_RGBA(0,0,0,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsRefPtr<ImageContainer>
|
already_AddRefed<ImageContainer>
|
||||||
ContainerState::ThebesLayerData::CanOptimizeImageLayer(LayerManager* aManager)
|
ContainerState::ThebesLayerData::CanOptimizeImageLayer(LayerManager* aManager)
|
||||||
{
|
{
|
||||||
if (!mImage || !mImageClip.mRoundedClipRects.IsEmpty()) {
|
if (!mImage || !mImageClip.mRoundedClipRects.IsEmpty()) {
|
||||||
|
@ -1210,19 +1210,13 @@ nsDisplayImage::Paint(nsDisplayListBuilder* aBuilder,
|
|||||||
: (PRUint32) imgIContainer::FLAG_NONE);
|
: (PRUint32) imgIContainer::FLAG_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<imgIContainer>
|
already_AddRefed<ImageContainer>
|
||||||
nsDisplayImage::GetImage()
|
|
||||||
{
|
|
||||||
return mImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsRefPtr<ImageContainer>
|
|
||||||
nsDisplayImage::GetContainer(LayerManager* aManager)
|
nsDisplayImage::GetContainer(LayerManager* aManager)
|
||||||
{
|
{
|
||||||
ImageContainer* container;
|
nsRefPtr<ImageContainer> container;
|
||||||
nsresult rv = mImage->GetImageContainer(aManager, &container);
|
nsresult rv = mImage->GetImageContainer(aManager, getter_AddRefs(container));
|
||||||
NS_ENSURE_SUCCESS(rv, NULL);
|
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||||
return container;
|
return container.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -386,13 +386,12 @@ public:
|
|||||||
}
|
}
|
||||||
virtual void Paint(nsDisplayListBuilder* aBuilder,
|
virtual void Paint(nsDisplayListBuilder* aBuilder,
|
||||||
nsRenderingContext* aCtx);
|
nsRenderingContext* aCtx);
|
||||||
nsCOMPtr<imgIContainer> GetImage();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an ImageContainer for this image if the image type
|
* Returns an ImageContainer for this image if the image type
|
||||||
* supports it (TYPE_RASTER only).
|
* supports it (TYPE_RASTER only).
|
||||||
*/
|
*/
|
||||||
nsRefPtr<ImageContainer> GetContainer(LayerManager* aManager);
|
already_AddRefed<ImageContainer> GetContainer(LayerManager* aManager);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure an ImageLayer for this display item.
|
* Configure an ImageLayer for this display item.
|
||||||
|
@ -73,7 +73,7 @@ body.non-verbose pre.tree {
|
|||||||
color: #004;
|
color: #004;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mrStar {
|
.mrNote {
|
||||||
color: #604;
|
color: #604;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,3 +98,9 @@ body.non-verbose pre.tree {
|
|||||||
.hidden {
|
.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.invalid {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #f00;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,30 @@ const UNITS_PERCENTAGE = Ci.nsIMemoryReporter.UNITS_PERCENTAGE;
|
|||||||
|
|
||||||
const kUnknown = -1; // used for _amount if a memory reporter failed
|
const kUnknown = -1; // used for _amount if a memory reporter failed
|
||||||
|
|
||||||
const kTreeDescriptions = {
|
// Paths, names and descriptions all need to be sanitized before being
|
||||||
|
// displayed, because the user has some control over them via names of
|
||||||
|
// compartments, windows, etc. Also, forward slashes in URLs in paths are
|
||||||
|
// represented with backslashes to avoid being mistaken for path separators, so
|
||||||
|
// we need to undo that as well.
|
||||||
|
|
||||||
|
function escapeAll(aStr)
|
||||||
|
{
|
||||||
|
return aStr.replace(/\&/g, '&').replace(/'/g, ''').
|
||||||
|
replace(/\</g, '<').replace(/>/g, '>').
|
||||||
|
replace(/\"/g, '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
function flipBackslashes(aStr)
|
||||||
|
{
|
||||||
|
return aStr.replace(/\\/g, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeSafe(aUnsafeStr)
|
||||||
|
{
|
||||||
|
return escapeAll(flipBackslashes(aUnsafeStr));
|
||||||
|
}
|
||||||
|
|
||||||
|
const kTreeUnsafeDescriptions = {
|
||||||
'explicit' :
|
'explicit' :
|
||||||
"This tree covers explicit memory allocations by the application, " +
|
"This tree covers explicit memory allocations by the application, " +
|
||||||
"both at the operating system level (via calls to functions such as " +
|
"both at the operating system level (via calls to functions such as " +
|
||||||
@ -199,13 +222,13 @@ function sendHeapMinNotifications()
|
|||||||
sendHeapMinNotificationsInner();
|
sendHeapMinNotificationsInner();
|
||||||
}
|
}
|
||||||
|
|
||||||
function Reporter(aPath, aKind, aUnits, aAmount, aDescription)
|
function Reporter(aUnsafePath, aKind, aUnits, aAmount, aUnsafeDesc)
|
||||||
{
|
{
|
||||||
this._path = aPath;
|
this._unsafePath = aUnsafePath;
|
||||||
this._kind = aKind;
|
this._kind = aKind;
|
||||||
this._units = aUnits;
|
this._units = aUnits;
|
||||||
this._amount = aAmount;
|
this._amount = aAmount;
|
||||||
this._description = aDescription;
|
this._unsafeDescription = aUnsafeDesc;
|
||||||
// this._nMerged is only defined if > 1
|
// this._nMerged is only defined if > 1
|
||||||
// this._done is defined when getBytes is called
|
// this._done is defined when getBytes is called
|
||||||
}
|
}
|
||||||
@ -228,7 +251,7 @@ Reporter.prototype = {
|
|||||||
// Nb: the '/' must be present, because we have a KIND_OTHER reporter
|
// Nb: the '/' must be present, because we have a KIND_OTHER reporter
|
||||||
// called "explicit" which is not part of the "explicit" tree.
|
// called "explicit" which is not part of the "explicit" tree.
|
||||||
aTreeName += "/";
|
aTreeName += "/";
|
||||||
return this._path.slice(0, aTreeName.length) === aTreeName;
|
return this._unsafePath.slice(0, aTreeName.length) === aTreeName;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -246,21 +269,22 @@ function getReportersByProcess(aMgr)
|
|||||||
// allocated.
|
// allocated.
|
||||||
var reportersByProcess = {};
|
var reportersByProcess = {};
|
||||||
|
|
||||||
function addReporter(aProcess, aPath, aKind, aUnits, aAmount, aDescription)
|
function addReporter(aProcess, aUnsafePath, aKind, aUnits, aAmount,
|
||||||
|
aUnsafeDesc)
|
||||||
{
|
{
|
||||||
var process = aProcess === "" ? "Main" : aProcess;
|
var process = aProcess === "" ? "Main" : aProcess;
|
||||||
var r = new Reporter(aPath, aKind, aUnits, aAmount, aDescription);
|
var r = new Reporter(aUnsafePath, aKind, aUnits, aAmount, aUnsafeDesc);
|
||||||
if (!reportersByProcess[process]) {
|
if (!reportersByProcess[process]) {
|
||||||
reportersByProcess[process] = {};
|
reportersByProcess[process] = {};
|
||||||
}
|
}
|
||||||
var reporters = reportersByProcess[process];
|
var reporters = reportersByProcess[process];
|
||||||
var reporter = reporters[r._path];
|
var reporter = reporters[r._unsafePath];
|
||||||
if (reporter) {
|
if (reporter) {
|
||||||
// Already an entry; must be a duplicated reporter. This can happen
|
// Already an entry; must be a duplicated reporter. This can happen
|
||||||
// legitimately. Merge them.
|
// legitimately. Merge them.
|
||||||
reporter.merge(r);
|
reporter.merge(r);
|
||||||
} else {
|
} else {
|
||||||
reporters[r._path] = r;
|
reporters[r._unsafePath] = r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,28 +395,28 @@ function update()
|
|||||||
// - Leaf TreeNodes correspond to Reporters and have more properties.
|
// - Leaf TreeNodes correspond to Reporters and have more properties.
|
||||||
// - Non-leaf TreeNodes are just scaffolding nodes for the tree; their values
|
// - Non-leaf TreeNodes are just scaffolding nodes for the tree; their values
|
||||||
// are derived from their children.
|
// are derived from their children.
|
||||||
function TreeNode(aName)
|
function TreeNode(aUnsafeName)
|
||||||
{
|
{
|
||||||
// Nb: _units is not needed, it's always UNITS_BYTES.
|
// Nb: _units is not needed, it's always UNITS_BYTES.
|
||||||
this._name = aName;
|
this._unsafeName = aUnsafeName;
|
||||||
this._kids = [];
|
this._kids = [];
|
||||||
// All TreeNodes have these properties added later:
|
// All TreeNodes have these properties added later:
|
||||||
// - _amount (which is never |kUnknown|)
|
// - _amount (which is never |kUnknown|)
|
||||||
// - _description
|
// - _unsafeDescription
|
||||||
//
|
//
|
||||||
// Leaf TreeNodes have these properties added later:
|
// Leaf TreeNodes have these properties added later:
|
||||||
// - _kind
|
// - _kind
|
||||||
// - _nMerged (if > 1)
|
// - _nMerged (if > 1)
|
||||||
// - _hasProblem (only defined if true)
|
// - _isUnknown (only defined if true)
|
||||||
//
|
//
|
||||||
// Non-leaf TreeNodes have these properties added later:
|
// Non-leaf TreeNodes have these properties added later:
|
||||||
// - _hideKids (only defined if true)
|
// - _hideKids (only defined if true)
|
||||||
}
|
}
|
||||||
|
|
||||||
TreeNode.prototype = {
|
TreeNode.prototype = {
|
||||||
findKid: function(aName) {
|
findKid: function(aUnsafeName) {
|
||||||
for (var i = 0; i < this._kids.length; i++) {
|
for (var i = 0; i < this._kids.length; i++) {
|
||||||
if (this._kids[i]._name === aName) {
|
if (this._kids[i]._unsafeName === aUnsafeName) {
|
||||||
return this._kids[i];
|
return this._kids[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -413,7 +437,7 @@ TreeNode.compare = function(a, b) {
|
|||||||
* structure that will be shown as output.
|
* structure that will be shown as output.
|
||||||
*
|
*
|
||||||
* @param aReporters
|
* @param aReporters
|
||||||
* The table of Reporters, indexed by path.
|
* The table of Reporters, indexed by _unsafePath.
|
||||||
* @param aTreeName
|
* @param aTreeName
|
||||||
* The name of the tree being built.
|
* The name of the tree being built.
|
||||||
* @return The built tree.
|
* @return The built tree.
|
||||||
@ -428,8 +452,8 @@ function buildTree(aReporters, aTreeName)
|
|||||||
// "explicit". But there may be zero for "map" trees; if that happens,
|
// "explicit". But there may be zero for "map" trees; if that happens,
|
||||||
// bail.
|
// bail.
|
||||||
var foundReporter = false;
|
var foundReporter = false;
|
||||||
for (var path in aReporters) {
|
for (var unsafePath in aReporters) {
|
||||||
if (aReporters[path].treeNameMatches(aTreeName)) {
|
if (aReporters[unsafePath].treeNameMatches(aTreeName)) {
|
||||||
foundReporter = true;
|
foundReporter = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -440,22 +464,22 @@ function buildTree(aReporters, aTreeName)
|
|||||||
}
|
}
|
||||||
|
|
||||||
var t = new TreeNode("falseRoot");
|
var t = new TreeNode("falseRoot");
|
||||||
for (var path in aReporters) {
|
for (var unsafePath in aReporters) {
|
||||||
// Add any missing nodes in the tree implied by the path.
|
// Add any missing nodes in the tree implied by the unsafePath.
|
||||||
var r = aReporters[path];
|
var r = aReporters[unsafePath];
|
||||||
if (r.treeNameMatches(aTreeName)) {
|
if (r.treeNameMatches(aTreeName)) {
|
||||||
assert(r._kind === KIND_HEAP || r._kind === KIND_NONHEAP,
|
assert(r._kind === KIND_HEAP || r._kind === KIND_NONHEAP,
|
||||||
"reporters in the tree must have KIND_HEAP or KIND_NONHEAP");
|
"reporters in the tree must have KIND_HEAP or KIND_NONHEAP");
|
||||||
assert(r._units === UNITS_BYTES, "r._units === UNITS_BYTES");
|
assert(r._units === UNITS_BYTES, "r._units === UNITS_BYTES");
|
||||||
var names = r._path.split('/');
|
var unsafeNames = r._unsafePath.split('/');
|
||||||
var u = t;
|
var u = t;
|
||||||
for (var i = 0; i < names.length; i++) {
|
for (var i = 0; i < unsafeNames.length; i++) {
|
||||||
var name = names[i];
|
var unsafeName = unsafeNames[i];
|
||||||
var uMatch = u.findKid(name);
|
var uMatch = u.findKid(unsafeName);
|
||||||
if (uMatch) {
|
if (uMatch) {
|
||||||
u = uMatch;
|
u = uMatch;
|
||||||
} else {
|
} else {
|
||||||
var v = new TreeNode(name);
|
var v = new TreeNode(unsafeName);
|
||||||
u._kids.push(v);
|
u._kids.push(v);
|
||||||
u = v;
|
u = v;
|
||||||
}
|
}
|
||||||
@ -474,19 +498,20 @@ function buildTree(aReporters, aTreeName)
|
|||||||
|
|
||||||
// Next, fill in the remaining properties bottom-up.
|
// Next, fill in the remaining properties bottom-up.
|
||||||
// Note that this function never returns kUnknown.
|
// Note that this function never returns kUnknown.
|
||||||
function fillInTree(aT, aPrepath)
|
function fillInTree(aT, aUnsafePrePath)
|
||||||
{
|
{
|
||||||
var path = aPrepath ? aPrepath + '/' + aT._name : aT._name;
|
var unsafePath =
|
||||||
|
aUnsafePrePath ? aUnsafePrePath + '/' + aT._unsafeName : aT._unsafeName;
|
||||||
if (aT._kids.length === 0) {
|
if (aT._kids.length === 0) {
|
||||||
// Leaf node. Must have a reporter.
|
// Leaf node. Must have a reporter.
|
||||||
assert(aT._kind !== undefined, "aT._kind is undefined for leaf node");
|
assert(aT._kind !== undefined, "aT._kind is undefined for leaf node");
|
||||||
aT._description = getDescription(aReporters, path);
|
aT._unsafeDescription = getUnsafeDescription(aReporters, unsafePath);
|
||||||
var amount = getBytes(aReporters, path);
|
var amount = getBytes(aReporters, unsafePath);
|
||||||
if (amount !== kUnknown) {
|
if (amount !== kUnknown) {
|
||||||
aT._amount = amount;
|
aT._amount = amount;
|
||||||
} else {
|
} else {
|
||||||
aT._amount = 0;
|
aT._amount = 0;
|
||||||
aT._hasProblem = true;
|
aT._isUnknown = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Non-leaf node. Derive its size and description entirely from its
|
// Non-leaf node. Derive its size and description entirely from its
|
||||||
@ -494,10 +519,11 @@ function buildTree(aReporters, aTreeName)
|
|||||||
assert(aT._kind === undefined, "aT._kind is defined for non-leaf node");
|
assert(aT._kind === undefined, "aT._kind is defined for non-leaf node");
|
||||||
var childrenBytes = 0;
|
var childrenBytes = 0;
|
||||||
for (var i = 0; i < aT._kids.length; i++) {
|
for (var i = 0; i < aT._kids.length; i++) {
|
||||||
childrenBytes += fillInTree(aT._kids[i], path);
|
childrenBytes += fillInTree(aT._kids[i], unsafePath);
|
||||||
}
|
}
|
||||||
aT._amount = childrenBytes;
|
aT._amount = childrenBytes;
|
||||||
aT._description = "The sum of all entries below '" + aT._name + "'.";
|
aT._unsafeDescription =
|
||||||
|
"The sum of all entries below '" + aT._unsafeName + "'.";
|
||||||
}
|
}
|
||||||
assert(aT._amount !== kUnknown, "aT._amount !== kUnknown");
|
assert(aT._amount !== kUnknown, "aT._amount !== kUnknown");
|
||||||
return aT._amount;
|
return aT._amount;
|
||||||
@ -515,8 +541,8 @@ function buildTree(aReporters, aTreeName)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the description on the root node.
|
// Set the (unsafe) description on the root node.
|
||||||
t._description = kTreeDescriptions[t._name];
|
t._unsafeDescription = kTreeUnsafeDescriptions[t._unsafeName];
|
||||||
|
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
@ -526,16 +552,16 @@ function buildTree(aReporters, aTreeName)
|
|||||||
* explicitly marking them as done.
|
* explicitly marking them as done.
|
||||||
*
|
*
|
||||||
* @param aReporters
|
* @param aReporters
|
||||||
* The table of Reporters, indexed by path.
|
* The table of Reporters, indexed by _unsafePath.
|
||||||
* @param aTreeName
|
* @param aTreeName
|
||||||
* The name of the tree being built.
|
* The name of the tree being built.
|
||||||
*/
|
*/
|
||||||
function ignoreTree(aReporters, aTreeName)
|
function ignoreTree(aReporters, aTreeName)
|
||||||
{
|
{
|
||||||
for (var path in aReporters) {
|
for (var unsafePath in aReporters) {
|
||||||
var r = aReporters[path];
|
var r = aReporters[unsafePath];
|
||||||
if (r.treeNameMatches(aTreeName)) {
|
if (r.treeNameMatches(aTreeName)) {
|
||||||
var dummy = getBytes(aReporters, path);
|
var dummy = getBytes(aReporters, unsafePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -546,7 +572,7 @@ function ignoreTree(aReporters, aTreeName)
|
|||||||
* @param aT
|
* @param aT
|
||||||
* The tree.
|
* The tree.
|
||||||
* @param aReporters
|
* @param aReporters
|
||||||
* Table of Reporters for this process, indexed by _path.
|
* Table of Reporters for this process, indexed by _unsafePath.
|
||||||
* @return A boolean indicating if "heap-allocated" is known for the process.
|
* @return A boolean indicating if "heap-allocated" is known for the process.
|
||||||
*/
|
*/
|
||||||
function fixUpExplicitTree(aT, aReporters)
|
function fixUpExplicitTree(aT, aReporters)
|
||||||
@ -579,15 +605,14 @@ function fixUpExplicitTree(aT, aReporters)
|
|||||||
heapAllocatedBytes - getKnownHeapUsedBytes(aT);
|
heapAllocatedBytes - getKnownHeapUsedBytes(aT);
|
||||||
} else {
|
} else {
|
||||||
heapUnclassifiedT._amount = 0;
|
heapUnclassifiedT._amount = 0;
|
||||||
heapUnclassifiedT._hasProblem = true;
|
heapUnclassifiedT._isUnknown = true;
|
||||||
}
|
}
|
||||||
// This kindToString() ensures the "(Heap)" prefix is set without having to
|
// This kindToString() ensures the "(Heap)" prefix is set without having to
|
||||||
// set the _kind property, which would mean that there is a corresponding
|
// set the _kind property, which would mean that there is a corresponding
|
||||||
// Reporter for this TreeNode (which isn't true).
|
// Reporter for this TreeNode (which isn't true)
|
||||||
heapUnclassifiedT._description =
|
heapUnclassifiedT._unsafeDescription = kindToString(KIND_HEAP) +
|
||||||
kindToString(KIND_HEAP) +
|
|
||||||
"Memory not classified by a more specific reporter. This includes " +
|
"Memory not classified by a more specific reporter. This includes " +
|
||||||
"slop bytes due to internal fragmentation in the heap allocator "
|
"slop bytes due to internal fragmentation in the heap allocator " +
|
||||||
"(caused when the allocator rounds up request sizes).";
|
"(caused when the allocator rounds up request sizes).";
|
||||||
|
|
||||||
aT._kids.push(heapUnclassifiedT);
|
aT._kids.push(heapUnclassifiedT);
|
||||||
@ -649,7 +674,7 @@ function sortTreeAndInsertAggregateNodes(aTotalBytes, aT)
|
|||||||
}
|
}
|
||||||
aggT._hideKids = true;
|
aggT._hideKids = true;
|
||||||
aggT._amount = aggBytes;
|
aggT._amount = aggBytes;
|
||||||
aggT._description =
|
aggT._unsafeDescription =
|
||||||
nAgg + " sub-trees that are below the " + kSignificanceThresholdPerc +
|
nAgg + " sub-trees that are below the " + kSignificanceThresholdPerc +
|
||||||
"% significance threshold.";
|
"% significance threshold.";
|
||||||
aT._kids.splice(i0, nAgg, aggT);
|
aT._kids.splice(i0, nAgg, aggT);
|
||||||
@ -671,6 +696,11 @@ function sortTreeAndInsertAggregateNodes(aTotalBytes, aT)
|
|||||||
sortTreeAndInsertAggregateNodes(aTotalBytes, aT._kids[i]);
|
sortTreeAndInsertAggregateNodes(aTotalBytes, aT._kids[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Global variable indicating if we've seen any invalid values for this
|
||||||
|
// process; it holds the unsafePaths of any such reporters. It is reset for
|
||||||
|
// each new process.
|
||||||
|
var gUnsafePathsWithInvalidValuesForThisProcess = [];
|
||||||
|
|
||||||
function genWarningText(aHasKnownHeapAllocated, aHasMozMallocUsableSize)
|
function genWarningText(aHasKnownHeapAllocated, aHasMozMallocUsableSize)
|
||||||
{
|
{
|
||||||
var warningText = "";
|
var warningText = "";
|
||||||
@ -698,6 +728,31 @@ function genWarningText(aHasKnownHeapAllocated, aHasMozMallocUsableSize)
|
|||||||
"by individual memory reporters and so will fall under " +
|
"by individual memory reporters and so will fall under " +
|
||||||
"'heap-unclassified'.</p>\n\n";
|
"'heap-unclassified'.</p>\n\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gUnsafePathsWithInvalidValuesForThisProcess.length > 0) {
|
||||||
|
warningText +=
|
||||||
|
"<div class='accuracyWarning'>" +
|
||||||
|
"<p>WARNING: the following values are negative or unreasonably " +
|
||||||
|
"large.</p>\n" +
|
||||||
|
"<ul>";
|
||||||
|
for (var i = 0;
|
||||||
|
i < gUnsafePathsWithInvalidValuesForThisProcess.length;
|
||||||
|
i++)
|
||||||
|
{
|
||||||
|
warningText +=
|
||||||
|
" <li>" +
|
||||||
|
makeSafe(gUnsafePathsWithInvalidValuesForThisProcess[i]) +
|
||||||
|
"</li>\n";
|
||||||
|
}
|
||||||
|
warningText +=
|
||||||
|
"</ul>" +
|
||||||
|
"<p>This indicates a defect in one or more memory reporters. The " +
|
||||||
|
"invalid values are highlighted, but you may need to expand one " +
|
||||||
|
"or more sub-trees to see them.</p>\n\n" +
|
||||||
|
"</div>";
|
||||||
|
gUnsafePathsWithInvalidValuesForThisProcess = []; // reset for the next process
|
||||||
|
}
|
||||||
|
|
||||||
return warningText;
|
return warningText;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -707,7 +762,7 @@ function genWarningText(aHasKnownHeapAllocated, aHasMozMallocUsableSize)
|
|||||||
* @param aProcess
|
* @param aProcess
|
||||||
* The name of the process.
|
* The name of the process.
|
||||||
* @param aReporters
|
* @param aReporters
|
||||||
* Table of Reporters for this process, indexed by _path.
|
* Table of Reporters for this process, indexed by _unsafePath.
|
||||||
* @param aHasMozMallocUsableSize
|
* @param aHasMozMallocUsableSize
|
||||||
* Boolean indicating if moz_malloc_usable_size works.
|
* Boolean indicating if moz_malloc_usable_size works.
|
||||||
* @return The generated text.
|
* @return The generated text.
|
||||||
@ -719,20 +774,14 @@ function genProcessText(aProcess, aReporters, aHasMozMallocUsableSize)
|
|||||||
sortTreeAndInsertAggregateNodes(explicitTree._amount, explicitTree);
|
sortTreeAndInsertAggregateNodes(explicitTree._amount, explicitTree);
|
||||||
var explicitText = genTreeText(explicitTree, aProcess);
|
var explicitText = genTreeText(explicitTree, aProcess);
|
||||||
|
|
||||||
// Generate any warnings about inaccuracies due to platform limitations.
|
|
||||||
// The newlines give nice spacing if we cut+paste into a text buffer.
|
|
||||||
var warningText = "";
|
|
||||||
var accuracyTagText = "<p class='accuracyWarning'>";
|
|
||||||
var warningText =
|
|
||||||
genWarningText(hasKnownHeapAllocated, aHasMozMallocUsableSize);
|
|
||||||
|
|
||||||
// We only show these breakdown trees in verbose mode.
|
// We only show these breakdown trees in verbose mode.
|
||||||
var mapTreeText = "";
|
var mapTreeText = "";
|
||||||
kMapTreePaths.forEach(function(t) {
|
kMapTreePaths.forEach(function(t) {
|
||||||
if (gVerbose) {
|
if (gVerbose) {
|
||||||
var tree = buildTree(aReporters, t);
|
var tree = buildTree(aReporters, t);
|
||||||
|
|
||||||
// |tree| will be null if we don't have any reporters for the given path.
|
// |tree| will be null if we don't have any reporters for the given
|
||||||
|
// unsafePath.
|
||||||
if (tree) {
|
if (tree) {
|
||||||
sortTreeAndInsertAggregateNodes(tree._amount, tree);
|
sortTreeAndInsertAggregateNodes(tree._amount, tree);
|
||||||
tree._hideKids = true; // map trees are always initially collapsed
|
tree._hideKids = true; // map trees are always initially collapsed
|
||||||
@ -747,12 +796,35 @@ function genProcessText(aProcess, aReporters, aHasMozMallocUsableSize)
|
|||||||
// looks at all the reporters which aren't part of a tree.
|
// looks at all the reporters which aren't part of a tree.
|
||||||
var otherText = genOtherText(aReporters, aProcess);
|
var otherText = genOtherText(aReporters, aProcess);
|
||||||
|
|
||||||
|
// Generate any warnings about inaccuracies due to platform limitations.
|
||||||
|
// This must come after generating all the text. The newlines give nice
|
||||||
|
// spacing if we cut+paste into a text buffer.
|
||||||
|
var warningText = "";
|
||||||
|
var warningText =
|
||||||
|
genWarningText(hasKnownHeapAllocated, aHasMozMallocUsableSize);
|
||||||
|
|
||||||
// The newlines give nice spacing if we cut+paste into a text buffer.
|
// The newlines give nice spacing if we cut+paste into a text buffer.
|
||||||
return "<h1>" + aProcess + " Process</h1>\n\n" +
|
return "<h1>" + aProcess + " Process</h1>\n\n" +
|
||||||
warningText + explicitText + mapTreeText + otherText +
|
warningText + explicitText + mapTreeText + otherText +
|
||||||
"<hr></hr>";
|
"<hr></hr>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if a number has a negative sign when converted to a string.
|
||||||
|
* Works even for -0.
|
||||||
|
*
|
||||||
|
* @param aN
|
||||||
|
* The number.
|
||||||
|
* @return A boolean.
|
||||||
|
*/
|
||||||
|
function hasNegativeSign(aN)
|
||||||
|
{
|
||||||
|
if (aN === 0) { // this succeeds for 0 and -0
|
||||||
|
return 1 / aN === -Infinity; // this succeeds for -0
|
||||||
|
}
|
||||||
|
return aN < 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats an int as a human-readable string.
|
* Formats an int as a human-readable string.
|
||||||
*
|
*
|
||||||
@ -763,7 +835,7 @@ function genProcessText(aProcess, aReporters, aHasMozMallocUsableSize)
|
|||||||
function formatInt(aN)
|
function formatInt(aN)
|
||||||
{
|
{
|
||||||
var neg = false;
|
var neg = false;
|
||||||
if (aN < 0) {
|
if (hasNegativeSign(aN)) {
|
||||||
neg = true;
|
neg = true;
|
||||||
aN = -aN;
|
aN = -aN;
|
||||||
}
|
}
|
||||||
@ -804,7 +876,8 @@ function formatBytes(aBytes)
|
|||||||
} else {
|
} else {
|
||||||
var mbytes = (aBytes / (1024 * 1024)).toFixed(2);
|
var mbytes = (aBytes / (1024 * 1024)).toFixed(2);
|
||||||
var a = String(mbytes).split(".");
|
var a = String(mbytes).split(".");
|
||||||
s = formatInt(a[0]) + "." + a[1] + " " + unit;
|
// If the argument to formatInt() is -0, it will print the negative sign.
|
||||||
|
s = formatInt(Number(a[0])) + "." + a[1] + " " + unit;
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
@ -847,17 +920,17 @@ function pad(aS, aN, aC)
|
|||||||
* property.
|
* property.
|
||||||
*
|
*
|
||||||
* @param aReporters
|
* @param aReporters
|
||||||
* Table of Reporters for this process, indexed by _path.
|
* Table of Reporters for this process, indexed by _unsafePath.
|
||||||
* @param aPath
|
* @param aUnsafePath
|
||||||
* The path of the R.
|
* The unsafePath of the R.
|
||||||
* @param aDoNotMark
|
* @param aDoNotMark
|
||||||
* If true, the _done property is not set.
|
* If true, the _done property is not set.
|
||||||
* @return The byte count.
|
* @return The byte count.
|
||||||
*/
|
*/
|
||||||
function getBytes(aReporters, aPath, aDoNotMark)
|
function getBytes(aReporters, aUnsafePath, aDoNotMark)
|
||||||
{
|
{
|
||||||
var r = aReporters[aPath];
|
var r = aReporters[aUnsafePath];
|
||||||
assert(r, "getBytes: no such Reporter: " + aPath);
|
assert(r, "getBytes: no such Reporter: " + makeSafe(aUnsafePath));
|
||||||
if (!aDoNotMark) {
|
if (!aDoNotMark) {
|
||||||
r._done = true;
|
r._done = true;
|
||||||
}
|
}
|
||||||
@ -865,19 +938,19 @@ function getBytes(aReporters, aPath, aDoNotMark)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the description for a particular Reporter.
|
* Gets the (unsafe) description for a particular Reporter.
|
||||||
*
|
*
|
||||||
* @param aReporters
|
* @param aReporters
|
||||||
* Table of Reporters for this process, indexed by _path.
|
* Table of Reporters for this process, indexed by _unsafePath.
|
||||||
* @param aPath
|
* @param aUnsafePath
|
||||||
* The path of the Reporter.
|
* The unsafePath of the Reporter.
|
||||||
* @return The description.
|
* @return The description.
|
||||||
*/
|
*/
|
||||||
function getDescription(aReporters, aPath)
|
function getUnsafeDescription(aReporters, aUnsafePath)
|
||||||
{
|
{
|
||||||
var r = aReporters[aPath];
|
var r = aReporters[aUnsafePath];
|
||||||
assert(r, "getDescription: no such Reporter: " + aPath);
|
assert(r, "getUnsafeDescription: no such Reporter: " + makeSafe(aUnsafePath));
|
||||||
return r._description;
|
return r._unsafeDescription;
|
||||||
}
|
}
|
||||||
|
|
||||||
// There's a subset of the Unicode "light" box-drawing chars that are widely
|
// There's a subset of the Unicode "light" box-drawing chars that are widely
|
||||||
@ -889,9 +962,11 @@ const kHorizontal = "\u2500",
|
|||||||
kUpAndRight = "\u2514",
|
kUpAndRight = "\u2514",
|
||||||
kVerticalAndRight = "\u251c";
|
kVerticalAndRight = "\u251c";
|
||||||
|
|
||||||
function genMrValueText(aValue)
|
function genMrValueText(aValue, aIsInvalid)
|
||||||
{
|
{
|
||||||
return "<span class='mrValue'>" + aValue + " </span>";
|
return aIsInvalid ?
|
||||||
|
"<span class='mrValue invalid'>" + aValue + "</span>" :
|
||||||
|
"<span class='mrValue'>" + aValue + "</span>";
|
||||||
}
|
}
|
||||||
|
|
||||||
function kindToString(aKind)
|
function kindToString(aKind)
|
||||||
@ -905,34 +980,8 @@ function kindToString(aKind)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For user-controlled strings.
|
function genMrNameText(aKind, aShowSubtrees, aHasKids, aUnsafeDesc,
|
||||||
function escapeAll(aStr)
|
aUnsafeName, aIsUnknown, aIsInvalid, aNMerged)
|
||||||
{
|
|
||||||
return aStr.replace(/\&/g, '&').replace(/'/g, ''').
|
|
||||||
replace(/\</g, '<').replace(/>/g, '>').
|
|
||||||
replace(/\"/g, '"');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compartment reporter names are URLs and so can include forward slashes. But
|
|
||||||
// forward slash is the memory reporter path separator. So the memory
|
|
||||||
// reporters change them to backslashes. Undo that here.
|
|
||||||
function flipBackslashes(aStr)
|
|
||||||
{
|
|
||||||
return aStr.replace(/\\/g, '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
function prepName(aStr)
|
|
||||||
{
|
|
||||||
return escapeAll(flipBackslashes(aStr));
|
|
||||||
}
|
|
||||||
|
|
||||||
function prepDesc(aStr)
|
|
||||||
{
|
|
||||||
return escapeAll(flipBackslashes(aStr));
|
|
||||||
}
|
|
||||||
|
|
||||||
function genMrNameText(aKind, aShowSubtrees, aHasKids, aDesc, aName,
|
|
||||||
aHasProblem, aNMerged)
|
|
||||||
{
|
{
|
||||||
var text = "";
|
var text = "";
|
||||||
if (aHasKids) {
|
if (aHasKids) {
|
||||||
@ -947,35 +996,41 @@ function genMrNameText(aKind, aShowSubtrees, aHasKids, aDesc, aName,
|
|||||||
text += "<span class='mrSep'> " + kHorizontal + kHorizontal + " </span>";
|
text += "<span class='mrSep'> " + kHorizontal + kHorizontal + " </span>";
|
||||||
}
|
}
|
||||||
text += "<span class='mrName' title='" +
|
text += "<span class='mrName' title='" +
|
||||||
kindToString(aKind) + prepDesc(aDesc) + "'>" +
|
kindToString(aKind) + makeSafe(aUnsafeDesc) + "'>" +
|
||||||
prepName(aName) + "</span>";
|
makeSafe(aUnsafeName) + "</span>";
|
||||||
if (aHasProblem) {
|
if (aIsUnknown) {
|
||||||
const problemDesc =
|
const problemDesc =
|
||||||
"Warning: this memory reporter was unable to compute a useful value. ";
|
"Warning: this memory reporter was unable to compute a useful value. ";
|
||||||
text += "<span class='mrStar' title=\"" + problemDesc + "\"> [*]</span>";
|
text += "<span class='mrNote' title=\"" + problemDesc + "\"> [*]</span>";
|
||||||
|
}
|
||||||
|
if (aIsInvalid) {
|
||||||
|
const invalidDesc =
|
||||||
|
"Warning: this value is invalid and indicates a bug in one or more " +
|
||||||
|
"memory reporters. ";
|
||||||
|
text += "<span class='mrNote' title=\"" + invalidDesc + "\"> [?!]</span>";
|
||||||
}
|
}
|
||||||
if (aNMerged) {
|
if (aNMerged) {
|
||||||
const dupDesc = "This value is the sum of " + aNMerged +
|
const dupDesc = "This value is the sum of " + aNMerged +
|
||||||
" memory reporters that all have the same path.";
|
" memory reporters that all have the same path.";
|
||||||
text += "<span class='mrStar' title=\"" + dupDesc + "\"> [" +
|
text += "<span class='mrNote' title=\"" + dupDesc + "\"> [" +
|
||||||
aNMerged + "]</span>";
|
aNMerged + "]</span>";
|
||||||
}
|
}
|
||||||
return text + '\n';
|
return text + '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is used to record which sub-trees have been toggled, so the
|
// This is used to record the (safe) IDs of which sub-trees have been toggled,
|
||||||
// collapsed/expanded state can be replicated when the page is regenerated.
|
// so the collapsed/expanded state can be replicated when the page is
|
||||||
// It can end up holding IDs of nodes that no longer exist, e.g. for
|
// regenerated. It can end up holding IDs of nodes that no longer exist, e.g.
|
||||||
// compartments that have been closed. This doesn't seem like a big deal,
|
// for compartments that have been closed. This doesn't seem like a big deal,
|
||||||
// because the number is limited by the number of entries the user has changed
|
// because the number is limited by the number of entries the user has changed
|
||||||
// from their original state.
|
// from their original state.
|
||||||
var gToggles = {};
|
var gTogglesBySafeTreeId = {};
|
||||||
|
|
||||||
function toggle(aEvent)
|
function toggle(aEvent)
|
||||||
{
|
{
|
||||||
// This relies on each line being a span that contains at least five spans:
|
// This relies on each line being a span that contains at least five spans:
|
||||||
// mrValue, mrPerc, mrSep ('++'), mrSep ('--'), mrName, and then zero or more
|
// mrValue, mrPerc, mrSep ('++'), mrSep ('--'), mrName, and then zero or more
|
||||||
// mrStars. All whitespace must be within one of these spans for this
|
// mrNotes. All whitespace must be within one of these spans for this
|
||||||
// function to find the right nodes. And the span containing the children of
|
// function to find the right nodes. And the span containing the children of
|
||||||
// this line must immediately follow. Assertions check this.
|
// this line must immediately follow. Assertions check this.
|
||||||
|
|
||||||
@ -1003,11 +1058,11 @@ function toggle(aEvent)
|
|||||||
subTreeSpan.classList.toggle("hidden");
|
subTreeSpan.classList.toggle("hidden");
|
||||||
|
|
||||||
// Record/unrecord that this sub-tree was toggled.
|
// Record/unrecord that this sub-tree was toggled.
|
||||||
var treeId = outerSpan.id;
|
var safeTreeId = outerSpan.id;
|
||||||
if (gToggles[treeId]) {
|
if (gTogglesBySafeTreeId[safeTreeId]) {
|
||||||
delete gToggles[treeId];
|
delete gTogglesBySafeTreeId[safeTreeId];
|
||||||
} else {
|
} else {
|
||||||
gToggles[treeId] = true;
|
gTogglesBySafeTreeId[safeTreeId] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1024,13 +1079,13 @@ function genTreeText(aT, aProcess)
|
|||||||
{
|
{
|
||||||
var treeBytes = aT._amount;
|
var treeBytes = aT._amount;
|
||||||
var rootStringLength = aT.toString().length;
|
var rootStringLength = aT.toString().length;
|
||||||
var isExplicitTree = aT._name == 'explicit';
|
var isExplicitTree = aT._unsafeName == 'explicit';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the text for a particular tree, without a heading.
|
* Generates the text for a particular tree, without a heading.
|
||||||
*
|
*
|
||||||
* @param aPrePath
|
* @param aUnsafePrePath
|
||||||
* The partial path leading up to this node.
|
* The partial unsafePath leading up to this node.
|
||||||
* @param aT
|
* @param aT
|
||||||
* The tree.
|
* The tree.
|
||||||
* @param aIndentGuide
|
* @param aIndentGuide
|
||||||
@ -1042,7 +1097,7 @@ function genTreeText(aT, aProcess)
|
|||||||
* The length of the formatted byte count of the top node in the tree.
|
* The length of the formatted byte count of the top node in the tree.
|
||||||
* @return The generated text.
|
* @return The generated text.
|
||||||
*/
|
*/
|
||||||
function genTreeText2(aPrePath, aT, aIndentGuide, aParentStringLength)
|
function genTreeText2(aUnsafePrePath, aT, aIndentGuide, aParentStringLength)
|
||||||
{
|
{
|
||||||
function repeatStr(aC, aN)
|
function repeatStr(aC, aN)
|
||||||
{
|
{
|
||||||
@ -1053,6 +1108,15 @@ function genTreeText(aT, aProcess)
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine if we should show the sub-tree below this entry; this
|
||||||
|
// involves reinstating any previous toggling of the sub-tree.
|
||||||
|
var unsafePath = aUnsafePrePath + aT._unsafeName;
|
||||||
|
var safeTreeId = escapeAll(aProcess + ":" + unsafePath);
|
||||||
|
var showSubtrees = !aT._hideKids;
|
||||||
|
if (gTogglesBySafeTreeId[safeTreeId]) {
|
||||||
|
showSubtrees = !showSubtrees;
|
||||||
|
}
|
||||||
|
|
||||||
// Generate the indent.
|
// Generate the indent.
|
||||||
var indent = "<span class='treeLine'>";
|
var indent = "<span class='treeLine'>";
|
||||||
if (aIndentGuide.length > 0) {
|
if (aIndentGuide.length > 0) {
|
||||||
@ -1075,24 +1139,24 @@ function genTreeText(aT, aProcess)
|
|||||||
}
|
}
|
||||||
indent += "</span>";
|
indent += "</span>";
|
||||||
|
|
||||||
// Generate the percentage, and determine if we should show subtrees.
|
// Generate the percentage; detect and record invalid values at the same
|
||||||
|
// time.
|
||||||
var percText = "";
|
var percText = "";
|
||||||
var showSubtrees = !aT._hideKids;
|
var tIsInvalid = false;
|
||||||
if (aT._amount === treeBytes) {
|
if (aT._amount === treeBytes) {
|
||||||
percText = "100.0";
|
percText = "100.0";
|
||||||
} else {
|
} else {
|
||||||
var perc = (100 * aT._amount / treeBytes);
|
var perc = (100 * aT._amount / treeBytes);
|
||||||
|
if (!(0 <= perc && perc <= 100)) {
|
||||||
|
tIsInvalid = true;
|
||||||
|
gUnsafePathsWithInvalidValuesForThisProcess.push(unsafePath);
|
||||||
|
}
|
||||||
percText = (100 * aT._amount / treeBytes).toFixed(2);
|
percText = (100 * aT._amount / treeBytes).toFixed(2);
|
||||||
percText = pad(percText, 5, '0');
|
percText = pad(percText, 5, '0');
|
||||||
}
|
}
|
||||||
percText = "<span class='mrPerc'>(" + percText + "%) </span>";
|
percText = tIsInvalid ?
|
||||||
|
"<span class='mrPerc invalid'> (" + percText + "%)</span>" :
|
||||||
// Reinstate any previous toggling of this sub-tree.
|
"<span class='mrPerc'> (" + percText + "%)</span>";
|
||||||
var path = aPrePath + aT._name;
|
|
||||||
var treeId = escapeAll(aProcess + ":" + path);
|
|
||||||
if (gToggles[treeId]) {
|
|
||||||
showSubtrees = !showSubtrees;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't want to show '(nonheap)' on a tree like 'map/vsize', since the
|
// We don't want to show '(nonheap)' on a tree like 'map/vsize', since the
|
||||||
// whole tree is non-heap.
|
// whole tree is non-heap.
|
||||||
@ -1106,12 +1170,13 @@ function genTreeText(aT, aProcess)
|
|||||||
}
|
}
|
||||||
var text = indent;
|
var text = indent;
|
||||||
if (hasKids) {
|
if (hasKids) {
|
||||||
text +=
|
text += "<span onclick='toggle(event)' class='hasKids' id='" +
|
||||||
"<span onclick='toggle(event)' class='hasKids' id='" + treeId + "'>";
|
safeTreeId + "'>";
|
||||||
}
|
}
|
||||||
text += genMrValueText(tString) + percText;
|
text += genMrValueText(tString, tIsInvalid) + percText;
|
||||||
text += genMrNameText(kind, showSubtrees, hasKids, aT._description,
|
text += genMrNameText(kind, showSubtrees, hasKids, aT._unsafeDescription,
|
||||||
aT._name, aT._hasProblem, aT._nMerged);
|
aT._unsafeName, aT._isUnknown, tIsInvalid,
|
||||||
|
aT._nMerged);
|
||||||
if (hasKids) {
|
if (hasKids) {
|
||||||
var hiddenText = showSubtrees ? "" : " hidden";
|
var hiddenText = showSubtrees ? "" : " hidden";
|
||||||
// The 'kids' class is just used for sanity checking in toggle().
|
// The 'kids' class is just used for sanity checking in toggle().
|
||||||
@ -1121,7 +1186,7 @@ function genTreeText(aT, aProcess)
|
|||||||
for (var i = 0; i < aT._kids.length; i++) {
|
for (var i = 0; i < aT._kids.length; i++) {
|
||||||
// 3 is the standard depth, the callee adjusts it if necessary.
|
// 3 is the standard depth, the callee adjusts it if necessary.
|
||||||
aIndentGuide.push({ _isLastKid: (i === aT._kids.length - 1), _depth: 3 });
|
aIndentGuide.push({ _isLastKid: (i === aT._kids.length - 1), _depth: 3 });
|
||||||
text += genTreeText2(path + "/", aT._kids[i], aIndentGuide,
|
text += genTreeText2(unsafePath + "/", aT._kids[i], aIndentGuide,
|
||||||
tString.length);
|
tString.length);
|
||||||
aIndentGuide.pop();
|
aIndentGuide.pop();
|
||||||
}
|
}
|
||||||
@ -1131,23 +1196,22 @@ function genTreeText(aT, aProcess)
|
|||||||
|
|
||||||
var text = genTreeText2(/* prePath = */"", aT, [], rootStringLength);
|
var text = genTreeText2(/* prePath = */"", aT, [], rootStringLength);
|
||||||
|
|
||||||
return genSectionMarkup(aT._name, text);
|
return genSectionMarkup(aT._unsafeName, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
function OtherReporter(aPath, aUnits, aAmount, aDescription,
|
function OtherReporter(aUnsafePath, aUnits, aAmount, aUnsafeDesc, aNMerged)
|
||||||
aNMerged)
|
|
||||||
{
|
{
|
||||||
// Nb: _kind is not needed, it's always KIND_OTHER.
|
// Nb: _kind is not needed, it's always KIND_OTHER.
|
||||||
this._path = aPath;
|
this._unsafePath = aUnsafePath;
|
||||||
this._units = aUnits;
|
this._units = aUnits;
|
||||||
if (aAmount === kUnknown) {
|
if (aAmount === kUnknown) {
|
||||||
this._amount = 0;
|
this._amount = 0;
|
||||||
this._hasProblem = true;
|
this._isUnknown = true;
|
||||||
} else {
|
} else {
|
||||||
this._amount = aAmount;
|
this._amount = aAmount;
|
||||||
}
|
}
|
||||||
this._description = aDescription;
|
this._unsafeDescription = aUnsafeDesc;
|
||||||
this.asString = this.toString();
|
this._asString = this.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
OtherReporter.prototype = {
|
OtherReporter.prototype = {
|
||||||
@ -1160,12 +1224,25 @@ OtherReporter.prototype = {
|
|||||||
default:
|
default:
|
||||||
assert(false, "bad units in OtherReporter.toString");
|
assert(false, "bad units in OtherReporter.toString");
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
isInvalid: function() {
|
||||||
|
var n = this._amount;
|
||||||
|
switch (this._units) {
|
||||||
|
case UNITS_BYTES:
|
||||||
|
case UNITS_COUNT:
|
||||||
|
case UNITS_COUNT_CUMULATIVE: return (n !== kUnknown && n < 0);
|
||||||
|
case UNITS_PERCENTAGE: return (n !== kUnknown &&
|
||||||
|
!(0 <= n && n <= 10000));
|
||||||
|
default:
|
||||||
|
assert(false, "bad units in OtherReporter.isInvalid");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
OtherReporter.compare = function(a, b) {
|
OtherReporter.compare = function(a, b) {
|
||||||
return a._path < b._path ? -1 :
|
return a._unsafePath < b._unsafePath ? -1 :
|
||||||
a._path > b._path ? 1 :
|
a._unsafePath > b._unsafePath ? 1 :
|
||||||
0;
|
0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1173,7 +1250,7 @@ OtherReporter.compare = function(a, b) {
|
|||||||
* Generates the text for the "Other Measurements" section.
|
* Generates the text for the "Other Measurements" section.
|
||||||
*
|
*
|
||||||
* @param aReportersByProcess
|
* @param aReportersByProcess
|
||||||
* Table of Reporters for this process, indexed by _path.
|
* Table of Reporters for this process, indexed by _unsafePath.
|
||||||
* @param aProcess
|
* @param aProcess
|
||||||
* The process these reporters correspond to.
|
* The process these reporters correspond to.
|
||||||
* @return The generated text.
|
* @return The generated text.
|
||||||
@ -1185,19 +1262,17 @@ function genOtherText(aReportersByProcess, aProcess)
|
|||||||
// widest element, so we can format things nicely.
|
// widest element, so we can format things nicely.
|
||||||
var maxStringLength = 0;
|
var maxStringLength = 0;
|
||||||
var otherReporters = [];
|
var otherReporters = [];
|
||||||
for (var path in aReportersByProcess) {
|
for (var unsafePath in aReportersByProcess) {
|
||||||
var r = aReportersByProcess[path];
|
var r = aReportersByProcess[unsafePath];
|
||||||
if (!r._done) {
|
if (!r._done) {
|
||||||
assert(r._kind === KIND_OTHER, "_kind !== KIND_OTHER for " + r._path);
|
assert(r._kind === KIND_OTHER,
|
||||||
assert(r.nMerged === undefined); // we don't allow dup'd OTHER reporters
|
"_kind !== KIND_OTHER for " + makeSafe(r._unsafePath));
|
||||||
var hasProblem = false;
|
assert(r._nMerged === undefined); // we don't allow dup'd OTHER reporters
|
||||||
if (r._amount === kUnknown) {
|
var o = new OtherReporter(r._unsafePath, r._units, r._amount,
|
||||||
hasProblem = true;
|
r._unsafeDescription);
|
||||||
}
|
|
||||||
var o = new OtherReporter(r._path, r._units, r._amount, r._description);
|
|
||||||
otherReporters.push(o);
|
otherReporters.push(o);
|
||||||
if (o.asString.length > maxStringLength) {
|
if (o._asString.length > maxStringLength) {
|
||||||
maxStringLength = o.asString.length;
|
maxStringLength = o._asString.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1207,10 +1282,14 @@ function genOtherText(aReportersByProcess, aProcess)
|
|||||||
var text = "";
|
var text = "";
|
||||||
for (var i = 0; i < otherReporters.length; i++) {
|
for (var i = 0; i < otherReporters.length; i++) {
|
||||||
var o = otherReporters[i];
|
var o = otherReporters[i];
|
||||||
text += genMrValueText(pad(o.asString, maxStringLength, ' '));
|
var oIsInvalid = o.isInvalid();
|
||||||
|
if (oIsInvalid) {
|
||||||
|
gUnsafePathsWithInvalidValuesForThisProcess.push(o._unsafePath);
|
||||||
|
}
|
||||||
|
text += genMrValueText(pad(o._asString, maxStringLength, ' '), oIsInvalid);
|
||||||
text += genMrNameText(KIND_OTHER, /* showSubtrees = */true,
|
text += genMrNameText(KIND_OTHER, /* showSubtrees = */true,
|
||||||
/* hasKids = */false, o._description, o._path,
|
/* hasKids = */false, o._unsafeDescription,
|
||||||
o._hasProblem);
|
o._unsafePath, o._isUnknown, oIsInvalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
return genSectionMarkup('other', text);
|
return genSectionMarkup('other', text);
|
||||||
|
@ -166,7 +166,36 @@
|
|||||||
f("3rd", "explicit/a/d", HEAP, kUnknown),
|
f("3rd", "explicit/a/d", HEAP, kUnknown),
|
||||||
f("3rd", "explicit/a/d", HEAP, kUnknown), // dup: merge
|
f("3rd", "explicit/a/d", HEAP, kUnknown), // dup: merge
|
||||||
f("3rd", "explicit/b", NONHEAP, kUnknown),
|
f("3rd", "explicit/b", NONHEAP, kUnknown),
|
||||||
f("3rd", "other1", OTHER, kUnknown)
|
f2("3rd", "other1", OTHER, BYTES, kUnknown),
|
||||||
|
f2("3rd", "other2", OTHER, COUNT, kUnknown),
|
||||||
|
f2("3rd", "other3", OTHER, COUNT_CUMULATIVE, kUnknown),
|
||||||
|
f2("3rd", "other4", OTHER, PERCENTAGE, kUnknown),
|
||||||
|
|
||||||
|
// Invalid values (negative, too-big) should be identified.
|
||||||
|
f("4th", "heap-allocated", OTHER, 100 * MB),
|
||||||
|
f("4th", "explicit/js/compartment(http:\\\\too-big.com\\)/stuff",
|
||||||
|
HEAP, 150 * MB),
|
||||||
|
f("4th", "explicit/ok", HEAP, 5 * MB),
|
||||||
|
f("4th", "explicit/neg1", NONHEAP, -2 * MB),
|
||||||
|
// -111 becomes "-0.00MB" in non-verbose mode, and getting the negative
|
||||||
|
// sign in there correctly is non-trivial.
|
||||||
|
f2("4th", "other1", OTHER, BYTES, -111),
|
||||||
|
f2("4th", "other2", OTHER, BYTES, -222 * MB),
|
||||||
|
f2("4th", "other3", OTHER, COUNT, -333),
|
||||||
|
f2("4th", "other4", OTHER, COUNT_CUMULATIVE, -444),
|
||||||
|
f2("4th", "other5", OTHER, PERCENTAGE, -555),
|
||||||
|
// Escaping should again prevent this script from running when the name
|
||||||
|
// is printed in the warning.
|
||||||
|
f2("4th", "other6-danger<script>window.alert(1)</script>",
|
||||||
|
OTHER, PERCENTAGE, 66666),
|
||||||
|
|
||||||
|
// If a negative value is within a collapsed sub-tree in non-verbose mode,
|
||||||
|
// we should still get the warning at the top.
|
||||||
|
f("5th", "heap-allocated", OTHER, 100 * MB),
|
||||||
|
f("5th", "explicit/big", HEAP, 99 * MB),
|
||||||
|
f("5th", "explicit/a/pos", HEAP, 40 * KB),
|
||||||
|
f("5th", "explicit/a/neg1", NONHEAP, -20 * KB),
|
||||||
|
f("5th", "explicit/a/neg2", NONHEAP, -10 * KB)
|
||||||
];
|
];
|
||||||
for (var i = 0; i < fakeReporters2.length; i++) {
|
for (var i = 0; i < fakeReporters2.length; i++) {
|
||||||
mgr.registerReporter(fakeReporters2[i]);
|
mgr.registerReporter(fakeReporters2[i]);
|
||||||
@ -175,8 +204,8 @@
|
|||||||
]]>
|
]]>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<iframe id="amFrame" height="300" src="about:memory"></iframe>
|
<iframe id="amFrame" height="400" src="about:memory"></iframe>
|
||||||
<iframe id="amvFrame" height="300" src="about:memory?verbose"></iframe>
|
<iframe id="amvFrame" height="400" src="about:memory?verbose"></iframe>
|
||||||
|
|
||||||
<script type="application/javascript">
|
<script type="application/javascript">
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
@ -247,6 +276,58 @@ Explicit Allocations\n\
|
|||||||
Other Measurements\n\
|
Other Measurements\n\
|
||||||
0.00 MB ── heap-allocated [*]\n\
|
0.00 MB ── heap-allocated [*]\n\
|
||||||
0.00 MB ── other1 [*]\n\
|
0.00 MB ── other1 [*]\n\
|
||||||
|
0 ── other2 [*]\n\
|
||||||
|
0 ── other3 [*]\n\
|
||||||
|
0.00% ── other4 [*]\n\
|
||||||
|
\n\
|
||||||
|
4th Process\n\
|
||||||
|
\n\
|
||||||
|
WARNING: the following values are negative or unreasonably large.\n\
|
||||||
|
explicit/js\n\
|
||||||
|
explicit/js/compartment(http://too-big.com/)\n\
|
||||||
|
explicit/js/compartment(http://too-big.com/)/stuff\n\
|
||||||
|
explicit/(2 tiny)\n\
|
||||||
|
explicit/(2 tiny)/neg1\n\
|
||||||
|
explicit/(2 tiny)/heap-unclassified\n\
|
||||||
|
other1\n\
|
||||||
|
other2\n\
|
||||||
|
other3\n\
|
||||||
|
other4\n\
|
||||||
|
other5\n\
|
||||||
|
other6-danger<script>window.alert(1)</script>\n\
|
||||||
|
This indicates a defect in one or more memory reporters. The invalid values are highlighted, but you may need to expand one or more sub-trees to see them.\n\
|
||||||
|
\n\
|
||||||
|
Explicit Allocations\n\
|
||||||
|
98.00 MB (100.0%) -- explicit\n\
|
||||||
|
├──150.00 MB (153.06%) -- js [?!]\n\
|
||||||
|
│ └──150.00 MB (153.06%) -- compartment(http://too-big.com/) [?!]\n\
|
||||||
|
│ └──150.00 MB (153.06%) ── stuff [?!]\n\
|
||||||
|
├───5.00 MB (05.10%) ── ok\n\
|
||||||
|
└──-57.00 MB (-58.16%) ++ (2 tiny) [?!]\n\
|
||||||
|
\n\
|
||||||
|
Other Measurements\n\
|
||||||
|
100.00 MB ── heap-allocated\n\
|
||||||
|
-0.00 MB ── other1 [?!]\n\
|
||||||
|
-222.00 MB ── other2 [?!]\n\
|
||||||
|
-333 ── other3 [?!]\n\
|
||||||
|
-444 ── other4 [?!]\n\
|
||||||
|
-5.55% ── other5 [?!]\n\
|
||||||
|
666.66% ── other6-danger<script>window.alert(1)</script> [?!]\n\
|
||||||
|
\n\
|
||||||
|
5th Process\n\
|
||||||
|
\n\
|
||||||
|
WARNING: the following values are negative or unreasonably large.\n\
|
||||||
|
explicit/(2 tiny)/a/neg2\n\
|
||||||
|
explicit/(2 tiny)/a/neg1\n\
|
||||||
|
This indicates a defect in one or more memory reporters. The invalid values are highlighted, but you may need to expand one or more sub-trees to see them.\n\
|
||||||
|
\n\
|
||||||
|
Explicit Allocations\n\
|
||||||
|
99.97 MB (100.0%) -- explicit\n\
|
||||||
|
├──99.00 MB (99.03%) ── big\n\
|
||||||
|
└───0.97 MB (00.97%) ++ (2 tiny)\n\
|
||||||
|
\n\
|
||||||
|
Other Measurements\n\
|
||||||
|
100.00 MB ── heap-allocated\n\
|
||||||
\n\
|
\n\
|
||||||
";
|
";
|
||||||
|
|
||||||
@ -334,6 +415,62 @@ Explicit Allocations\n\
|
|||||||
Other Measurements\n\
|
Other Measurements\n\
|
||||||
0 B ── heap-allocated [*]\n\
|
0 B ── heap-allocated [*]\n\
|
||||||
0 B ── other1 [*]\n\
|
0 B ── other1 [*]\n\
|
||||||
|
0 ── other2 [*]\n\
|
||||||
|
0 ── other3 [*]\n\
|
||||||
|
0.00% ── other4 [*]\n\
|
||||||
|
\n\
|
||||||
|
4th Process\n\
|
||||||
|
\n\
|
||||||
|
WARNING: the following values are negative or unreasonably large.\n\
|
||||||
|
explicit/js\n\
|
||||||
|
explicit/js/compartment(http://too-big.com/)\n\
|
||||||
|
explicit/js/compartment(http://too-big.com/)/stuff\n\
|
||||||
|
explicit/neg1\n\
|
||||||
|
explicit/heap-unclassified\n\
|
||||||
|
other1\n\
|
||||||
|
other2\n\
|
||||||
|
other3\n\
|
||||||
|
other4\n\
|
||||||
|
other5\n\
|
||||||
|
other6-danger<script>window.alert(1)</script>\n\
|
||||||
|
This indicates a defect in one or more memory reporters. The invalid values are highlighted, but you may need to expand one or more sub-trees to see them.\n\
|
||||||
|
\n\
|
||||||
|
Explicit Allocations\n\
|
||||||
|
102,760,448 B (100.0%) -- explicit\n\
|
||||||
|
├──157,286,400 B (153.06%) -- js [?!]\n\
|
||||||
|
│ └──157,286,400 B (153.06%) -- compartment(http://too-big.com/) [?!]\n\
|
||||||
|
│ └──157,286,400 B (153.06%) ── stuff [?!]\n\
|
||||||
|
├────5,242,880 B (05.10%) ── ok\n\
|
||||||
|
├───-2,097,152 B (-2.04%) ── neg1 [?!]\n\
|
||||||
|
└──-57,671,680 B (-56.12%) ── heap-unclassified [?!]\n\
|
||||||
|
\n\
|
||||||
|
Other Measurements\n\
|
||||||
|
104,857,600 B ── heap-allocated\n\
|
||||||
|
-111 B ── other1 [?!]\n\
|
||||||
|
-232,783,872 B ── other2 [?!]\n\
|
||||||
|
-333 ── other3 [?!]\n\
|
||||||
|
-444 ── other4 [?!]\n\
|
||||||
|
-5.55% ── other5 [?!]\n\
|
||||||
|
666.66% ── other6-danger<script>window.alert(1)</script> [?!]\n\
|
||||||
|
\n\
|
||||||
|
5th Process\n\
|
||||||
|
\n\
|
||||||
|
WARNING: the following values are negative or unreasonably large.\n\
|
||||||
|
explicit/a/neg2\n\
|
||||||
|
explicit/a/neg1\n\
|
||||||
|
This indicates a defect in one or more memory reporters. The invalid values are highlighted, but you may need to expand one or more sub-trees to see them.\n\
|
||||||
|
\n\
|
||||||
|
Explicit Allocations\n\
|
||||||
|
104,826,880 B (100.0%) -- explicit\n\
|
||||||
|
├──103,809,024 B (99.03%) ── big\n\
|
||||||
|
├────1,007,616 B (00.96%) ── heap-unclassified\n\
|
||||||
|
└───────10,240 B (00.01%) -- a\n\
|
||||||
|
├──40,960 B (00.04%) ── pos\n\
|
||||||
|
├──-10,240 B (-0.01%) ── neg2 [?!]\n\
|
||||||
|
└──-20,480 B (-0.02%) ── neg1 [?!]\n\
|
||||||
|
\n\
|
||||||
|
Other Measurements\n\
|
||||||
|
104,857,600 B ── heap-allocated\n\
|
||||||
\n\
|
\n\
|
||||||
"
|
"
|
||||||
|
|
||||||
|
@ -184,6 +184,9 @@ TelemetryPing.prototype = {
|
|||||||
// duplicate submissions.
|
// duplicate submissions.
|
||||||
_uuid: generateUUID(),
|
_uuid: generateUUID(),
|
||||||
_prevSession: null,
|
_prevSession: null,
|
||||||
|
// Regex that matches histograms we care about during startup.
|
||||||
|
_startupHistogramRegex: /SQLITE|HTTP|SPDY|CACHE|DNS/,
|
||||||
|
_slowSQLStartup: {},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a set of histograms that can be converted into JSON
|
* Returns a set of histograms that can be converted into JSON
|
||||||
@ -384,15 +387,25 @@ TelemetryPing.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a copy of sqlite histograms on startup
|
* Return true if we're interested in having a STARTUP_* histogram for
|
||||||
|
* the given histogram name.
|
||||||
*/
|
*/
|
||||||
gatherStartupSqlite: function gatherStartupSqlite() {
|
isInterestingStartupHistogram: function isInterestingStartupHistogram(name) {
|
||||||
|
return this._startupHistogramRegex.test(name);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a copy of interesting histograms at startup.
|
||||||
|
*/
|
||||||
|
gatherStartupInformation: function gatherStartupInformation() {
|
||||||
let info = Telemetry.registeredHistograms;
|
let info = Telemetry.registeredHistograms;
|
||||||
let sqlite_re = /SQLITE/;
|
let snapshots = Telemetry.histogramSnapshots;
|
||||||
for (let name in info) {
|
for (let name in info) {
|
||||||
if (sqlite_re.test(name))
|
// Only duplicate interesting histograms with actual data.
|
||||||
|
if (this.isInterestingStartupHistogram(name) && name in snapshots)
|
||||||
Telemetry.histogramFrom("STARTUP_" + name, name);
|
Telemetry.histogramFrom("STARTUP_" + name, name);
|
||||||
}
|
}
|
||||||
|
this._slowSQLStartup = Telemetry.slowSQL;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -436,6 +449,10 @@ TelemetryPing.prototype = {
|
|||||||
payloadObj.simpleMeasurements = getSimpleMeasurements();
|
payloadObj.simpleMeasurements = getSimpleMeasurements();
|
||||||
payloadObj.histograms = this.getHistograms(Telemetry.histogramSnapshots);
|
payloadObj.histograms = this.getHistograms(Telemetry.histogramSnapshots);
|
||||||
payloadObj.slowSQL = Telemetry.slowSQL;
|
payloadObj.slowSQL = Telemetry.slowSQL;
|
||||||
|
if (Object.keys(this._slowSQLStartup.mainThread).length
|
||||||
|
|| Object.keys(this._slowSQLStartup.otherThreads).length) {
|
||||||
|
payloadObj.slowSQLStartup = this._slowSQLStartup;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return { previous: !!havePreviousSession, slug: slug, payload: JSON.stringify(payloadObj) };
|
return { previous: !!havePreviousSession, slug: slug, payload: JSON.stringify(payloadObj) };
|
||||||
},
|
},
|
||||||
@ -602,7 +619,7 @@ TelemetryPing.prototype = {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "sessionstore-windows-restored":
|
case "sessionstore-windows-restored":
|
||||||
this.gatherStartupSqlite();
|
this.gatherStartupInformation();
|
||||||
break;
|
break;
|
||||||
case "idle-daily":
|
case "idle-daily":
|
||||||
// Enqueue to main-thread, otherwise components may be inited by the
|
// Enqueue to main-thread, otherwise components may be inited by the
|
||||||
|
@ -35,18 +35,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.fullscreenButton {
|
.fullscreenButton {
|
||||||
background-color: transparent;
|
background: -moz-image-rect(url("chrome://global/skin/media/fullscreenButton.png"), 0, 16, 16, 0) no-repeat center;
|
||||||
list-style-image: url("chrome://global/skin/media/fullscreenButton.png");
|
|
||||||
-moz-image-region: rect(0px, 16px, 16px, 0px);
|
|
||||||
-moz-appearance: none;
|
-moz-appearance: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
min-height: 28px;
|
min-height: 28px;
|
||||||
min-width: 28px;
|
min-width: 28px;
|
||||||
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fullscreenButton[fullscreened] {
|
.fullscreenButton[fullscreened] {
|
||||||
-moz-image-region: rect(0px, 32px, 16px, 16px);
|
background-image: -moz-image-rect(url("chrome://global/skin/media/fullscreenButton.png"), 0, 32, 16, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
.volumeStack {
|
.volumeStack {
|
||||||
|
@ -37,9 +37,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.fullscreenButton {
|
.fullscreenButton {
|
||||||
background-color: transparent;
|
background: -moz-image-rect(url("chrome://global/skin/media/fullscreenButton.png"), 0, 16, 16, 0) no-repeat center;
|
||||||
list-style-image: url("chrome://global/skin/media/fullscreenButton.png");
|
|
||||||
-moz-image-region: rect(0px, 16px, 16px, 0px);
|
|
||||||
-moz-appearance: none;
|
-moz-appearance: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@ -49,7 +47,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.fullscreenButton[fullscreened] {
|
.fullscreenButton[fullscreened] {
|
||||||
-moz-image-region: rect(0px, 32px, 16px, 16px);
|
background-image: -moz-image-rect(url("chrome://global/skin/media/fullscreenButton.png"), 0, 32, 16, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
.volumeStack {
|
.volumeStack {
|
||||||
|
Loading…
Reference in New Issue
Block a user