зеркало из https://github.com/mozilla/pjs.git
Merge mozilla-inbound and mozilla-central
This commit is contained in:
Коммит
f3c1fffe53
|
@ -9,7 +9,7 @@ pref("app.update.interval", 28800);
|
|||
pref("app.update.download.backgroundInterval", 60);
|
||||
// URL user can browse to manually if for some reason all update installation
|
||||
// attempts fail.
|
||||
pref("app.update.url.manual", "http://nightly.mozilla.org/");
|
||||
pref("app.update.url.manual", "http://www.mozilla.com/firefox/channel/");
|
||||
// A default value for the "More information about this update" link
|
||||
// supplied in the "An update is available" page of the update wizard.
|
||||
pref("app.update.url.details", "http://www.mozilla.org/projects/%APP%/");
|
||||
|
|
|
@ -39,6 +39,7 @@ class DeviceManagerADB(DeviceManager):
|
|||
def mkDir(self, name):
|
||||
try:
|
||||
self.checkCmd(["shell", "mkdir", name])
|
||||
self.chmodDir(name)
|
||||
return name
|
||||
except:
|
||||
return None
|
||||
|
|
|
@ -515,7 +515,6 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, PRBool aClone, PRBool aDeep,
|
|||
nsnull;
|
||||
|
||||
nsCOMPtr<nsINode> clone;
|
||||
PRBool isDeepDocumentClone = PR_FALSE;
|
||||
if (aClone) {
|
||||
rv = aNode->Clone(nodeInfo, getter_AddRefs(clone));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -528,7 +527,6 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, PRBool aClone, PRBool aDeep,
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
else if (aDeep && clone->IsNodeOfType(nsINode::eDOCUMENT)) {
|
||||
isDeepDocumentClone = PR_TRUE;
|
||||
// After cloning the document itself, we want to clone the children into
|
||||
// the cloned document (somewhat like cloning and importing them into the
|
||||
// cloned document).
|
||||
|
|
|
@ -502,6 +502,7 @@ _TEST_FILES2 = \
|
|||
somedatas.resource \
|
||||
somedatas.resource^headers^ \
|
||||
delayedServerEvents.sjs \
|
||||
test_bug664916.html \
|
||||
test_bug666604.html \
|
||||
$(NULL)
|
||||
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=664916
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 664916</title>
|
||||
<script type="application/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=664916">Mozilla Bug 664916</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
|
||||
/** Test for Bug 664916 **/
|
||||
var div = document.createElement("div");
|
||||
var textNode = document.createTextNode("x")
|
||||
var tagNameGetter = div.__lookupGetter__("tagName");
|
||||
|
||||
var tagName = "";
|
||||
try {
|
||||
tagName = tagNameGetter.call(textNode);
|
||||
ok(false, "Should throw when calling tagname getter on text node");
|
||||
} catch(e) {
|
||||
ok(true, "Should throw when calling tagname getter on text node");
|
||||
}
|
||||
is(tagName, "", "Should not have changed tagName yet");
|
||||
tagName = tagNameGetter.call(div);
|
||||
is(tagName, "DIV", "Should get the right tag name");
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -649,9 +649,9 @@ ImageDocument::CreateSyntheticDocument()
|
|||
// This is bad during printing, it means tall image frames won't know
|
||||
// the size of the paper and cannot break into continuations along
|
||||
// multiple pages.
|
||||
Element* head = GetHeadElement();
|
||||
if (!head) {
|
||||
NS_WARNING("no head on image document!");
|
||||
Element* body = GetBodyElement();
|
||||
if (!body) {
|
||||
NS_WARNING("no body on image document!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -665,16 +665,10 @@ ImageDocument::CreateSyntheticDocument()
|
|||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
styleContent->SetInnerHTML(NS_LITERAL_STRING("img { display: block; }"));
|
||||
head->AppendChildTo(styleContent, PR_FALSE);
|
||||
styleContent->SetTextContent(NS_LITERAL_STRING("img { display: block; }"));
|
||||
body->AppendChildTo(styleContent, PR_FALSE);
|
||||
|
||||
// Add the image element
|
||||
Element* body = GetBodyElement();
|
||||
if (!body) {
|
||||
NS_WARNING("no body on image document!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::img, nsnull,
|
||||
kNameSpaceID_XHTML,
|
||||
nsIDOMNode::ELEMENT_NODE);
|
||||
|
|
|
@ -2170,16 +2170,8 @@ nsHTMLDocument::GetEmbeds(nsIDOMHTMLCollection** aEmbeds)
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLDocument::GetSelection(nsAString& aReturn)
|
||||
nsHTMLDocument::GetSelection(nsISelection** aReturn)
|
||||
{
|
||||
aReturn.Truncate();
|
||||
|
||||
nsCOMPtr<nsIJSContextStack> stack = do_GetService("@mozilla.org/js/xpc/ContextStack;1");
|
||||
JSContext* ccx = nsnull;
|
||||
if (stack && NS_SUCCEEDED(stack->Peek(&ccx)) && ccx) {
|
||||
JS_ReportWarning(ccx, "Deprecated method document.getSelection() called. Please use window.getSelection() instead.");
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(GetScopeObject());
|
||||
nsCOMPtr<nsPIDOMWindow> pwin = do_QueryInterface(window);
|
||||
NS_ENSURE_TRUE(pwin, NS_OK);
|
||||
|
@ -2188,17 +2180,8 @@ nsHTMLDocument::GetSelection(nsAString& aReturn)
|
|||
pwin->GetOuterWindow()->GetCurrentInnerWindow() == pwin,
|
||||
NS_OK);
|
||||
|
||||
nsCOMPtr<nsISelection> selection;
|
||||
nsresult rv = window->GetSelection(getter_AddRefs(selection));
|
||||
NS_ENSURE_TRUE(selection && NS_SUCCEEDED(rv), rv);
|
||||
|
||||
nsXPIDLString str;
|
||||
|
||||
rv = selection->ToString(getter_Copies(str));
|
||||
|
||||
aReturn.Assign(str);
|
||||
|
||||
return rv;
|
||||
return window->GetSelection(aReturn);
|
||||
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -124,7 +124,7 @@ DOMSVGAnimatedLengthList::InternalBaseValListWillChangeTo(const SVGLengthList& a
|
|||
|
||||
nsRefPtr<DOMSVGAnimatedLengthList> kungFuDeathGrip;
|
||||
if (mBaseVal) {
|
||||
if (!aNewValue.Length()) {
|
||||
if (aNewValue.Length() < mBaseVal->Length()) {
|
||||
// InternalListLengthWillChange might clear last reference to |this|.
|
||||
// Retain a temporary reference to keep from dying before returning.
|
||||
kungFuDeathGrip = this;
|
||||
|
|
|
@ -123,7 +123,7 @@ DOMSVGAnimatedNumberList::InternalBaseValListWillChangeTo(const SVGNumberList& a
|
|||
|
||||
nsRefPtr<DOMSVGAnimatedNumberList> kungFuDeathGrip;
|
||||
if (mBaseVal) {
|
||||
if (!aNewValue.Length()) {
|
||||
if (aNewValue.Length() < mBaseVal->Length()) {
|
||||
// InternalListLengthWillChange might clear last reference to |this|.
|
||||
// Retain a temporary reference to keep from dying before returning.
|
||||
kungFuDeathGrip = this;
|
||||
|
|
|
@ -119,7 +119,7 @@ DOMSVGLengthList::InternalListLengthWillChange(PRUint32 aNewLength)
|
|||
}
|
||||
|
||||
nsRefPtr<DOMSVGLengthList> kungFuDeathGrip;
|
||||
if (oldLength && !aNewLength) {
|
||||
if (aNewLength < oldLength) {
|
||||
// RemovingFromList() might clear last reference to |this|.
|
||||
// Retain a temporary reference to keep from dying before returning.
|
||||
kungFuDeathGrip = this;
|
||||
|
|
|
@ -119,7 +119,7 @@ DOMSVGNumberList::InternalListLengthWillChange(PRUint32 aNewLength)
|
|||
}
|
||||
|
||||
nsRefPtr<DOMSVGNumberList> kungFuDeathGrip;
|
||||
if (oldLength && !aNewLength) {
|
||||
if (aNewLength < oldLength) {
|
||||
// RemovingFromList() might clear last reference to |this|.
|
||||
// Retain a temporary reference to keep from dying before returning.
|
||||
kungFuDeathGrip = this;
|
||||
|
|
|
@ -156,7 +156,7 @@ DOMSVGPathSegList::InternalListWillChangeTo(const SVGPathData& aNewValue)
|
|||
PRUint32 newSegType;
|
||||
|
||||
nsRefPtr<DOMSVGPathSegList> kungFuDeathGrip;
|
||||
if (length && aNewValue.IsEmpty()) {
|
||||
if (aNewValue.Length() < length) {
|
||||
// RemovingFromList() might clear last reference to |this|.
|
||||
// Retain a temporary reference to keep from dying before returning.
|
||||
kungFuDeathGrip = this;
|
||||
|
|
|
@ -148,7 +148,7 @@ DOMSVGPointList::InternalListWillChangeTo(const SVGPointList& aNewValue)
|
|||
}
|
||||
|
||||
nsRefPtr<DOMSVGPointList> kungFuDeathGrip;
|
||||
if (oldLength && !newLength) {
|
||||
if (newLength < oldLength) {
|
||||
// RemovingFromList() might clear last reference to |this|.
|
||||
// Retain a temporary reference to keep from dying before returning.
|
||||
kungFuDeathGrip = this;
|
||||
|
|
|
@ -9192,7 +9192,17 @@ nsresult nsDocShell::DoChannelLoad(nsIChannel * aChannel,
|
|||
// Load attributes depend on load type...
|
||||
switch (mLoadType) {
|
||||
case LOAD_HISTORY:
|
||||
loadFlags |= nsIRequest::VALIDATE_NEVER;
|
||||
{
|
||||
// Only send VALIDATE_NEVER if mLSHE's URI was never changed via
|
||||
// push/replaceState (bug 669671).
|
||||
PRBool uriModified = PR_FALSE;
|
||||
if (mLSHE) {
|
||||
mLSHE->GetURIWasModified(&uriModified);
|
||||
}
|
||||
|
||||
if (!uriModified)
|
||||
loadFlags |= nsIRequest::VALIDATE_NEVER;
|
||||
}
|
||||
break;
|
||||
|
||||
case LOAD_RELOAD_CHARSET_CHANGE:
|
||||
|
@ -9854,6 +9864,15 @@ nsDocShell::AddState(nsIVariant *aData, const nsAString& aTitle,
|
|||
newSHEntry->SetStateData(scContainer);
|
||||
newSHEntry->SetPostData(nsnull);
|
||||
|
||||
// If this push/replaceState changed the document's current URI and the new
|
||||
// URI differs from the old URI in more than the hash, or if the old
|
||||
// SHEntry's URI was modified in this way by a push/replaceState call
|
||||
// set URIWasModified to true for the current SHEntry (bug 669671).
|
||||
PRBool sameExceptHashes = PR_TRUE, oldURIWasModified = PR_FALSE;
|
||||
newURI->EqualsExceptRef(mCurrentURI, &sameExceptHashes);
|
||||
oldOSHE->GetURIWasModified(&oldURIWasModified);
|
||||
newSHEntry->SetURIWasModified(!sameExceptHashes || oldURIWasModified);
|
||||
|
||||
// Step 5: If aReplace is false, indicating that we're doing a pushState
|
||||
// rather than a replaceState, notify bfcache that we've added a page to
|
||||
// the history so it can evict content viewers if appropriate.
|
||||
|
|
|
@ -59,7 +59,7 @@ class nsDocShellEditorData;
|
|||
[ptr] native nsDocShellEditorDataPtr(nsDocShellEditorData);
|
||||
|
||||
|
||||
[scriptable, uuid(5f3ebf43-6944-45fb-b1b1-78a05bf9370b)]
|
||||
[scriptable, uuid(b92d403e-f5ec-4b81-b0e3-6e6c241cef2d)]
|
||||
interface nsISHEntry : nsIHistoryEntry
|
||||
{
|
||||
/** URI for the document */
|
||||
|
@ -169,6 +169,19 @@ interface nsISHEntry : nsIHistoryEntry
|
|||
* is a session history entry for
|
||||
*/
|
||||
attribute ACString contentType;
|
||||
|
||||
/**
|
||||
* If we created this SHEntry via history.pushState or modified it via
|
||||
* history.replaceState, and if we changed the SHEntry's URI via the
|
||||
* push/replaceState call, and if the SHEntry's new URI differs from its
|
||||
* old URI by more than just the hash, then we set this field to true.
|
||||
*
|
||||
* Additionally, if this SHEntry was created by calling pushState from a
|
||||
* SHEntry whose URI was modified, this SHEntry's URIWasModified field is
|
||||
* true.
|
||||
*
|
||||
*/
|
||||
attribute boolean URIWasModified;
|
||||
|
||||
/** Set/Get scrollers' positon in anchored pages */
|
||||
void setScrollPosition(in long x, in long y);
|
||||
|
|
|
@ -108,6 +108,7 @@ nsSHEntry::nsSHEntry()
|
|||
, mDocIdentifier(gEntryDocIdentifier++)
|
||||
, mScrollPositionX(0)
|
||||
, mScrollPositionY(0)
|
||||
, mURIWasModified(PR_FALSE)
|
||||
, mIsFrameNavigation(PR_FALSE)
|
||||
, mSaveLayoutState(PR_TRUE)
|
||||
, mExpired(PR_FALSE)
|
||||
|
@ -132,6 +133,7 @@ nsSHEntry::nsSHEntry(const nsSHEntry &other)
|
|||
, mDocIdentifier(other.mDocIdentifier)
|
||||
, mScrollPositionX(0) // XXX why not copy?
|
||||
, mScrollPositionY(0) // XXX why not copy?
|
||||
, mURIWasModified(other.mURIWasModified)
|
||||
, mIsFrameNavigation(other.mIsFrameNavigation)
|
||||
, mSaveLayoutState(other.mSaveLayoutState)
|
||||
, mExpired(other.mExpired)
|
||||
|
@ -208,6 +210,18 @@ NS_IMETHODIMP nsSHEntry::GetScrollPosition(PRInt32 *x, PRInt32 *y)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsSHEntry::GetURIWasModified(PRBool* aOut)
|
||||
{
|
||||
*aOut = mURIWasModified;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsSHEntry::SetURIWasModified(PRBool aIn)
|
||||
{
|
||||
mURIWasModified = aIn;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsSHEntry::GetURI(nsIURI** aURI)
|
||||
{
|
||||
*aURI = mURI;
|
||||
|
|
|
@ -102,6 +102,7 @@ private:
|
|||
PRInt64 mDocIdentifier;
|
||||
PRInt32 mScrollPositionX;
|
||||
PRInt32 mScrollPositionY;
|
||||
PRPackedBool mURIWasModified;
|
||||
PRPackedBool mIsFrameNavigation;
|
||||
PRPackedBool mSaveLayoutState;
|
||||
PRPackedBool mExpired;
|
||||
|
|
|
@ -116,6 +116,8 @@ _TEST_FILES = \
|
|||
test_bug668513.html \
|
||||
bug668513_redirect.html \
|
||||
bug668513_redirect.html^headers^ \
|
||||
test_bug669671.html \
|
||||
file_bug669671.sjs \
|
||||
$(NULL)
|
||||
|
||||
ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
function handleRequest(request, response)
|
||||
{
|
||||
var count = parseInt(getState('count'));
|
||||
if (!count)
|
||||
count = 0;
|
||||
setState('count', count + 1 + '');
|
||||
|
||||
response.setHeader('Content-Type', 'text/html', false);
|
||||
response.setHeader('Cache-Control', 'max-age=0');
|
||||
response.write('<html><body onload="opener.onChildLoad()" ' +
|
||||
'onunload="parseInt(\'0\')">' +
|
||||
count + '</body></html>');
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=669671
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 669671</title>
|
||||
<script type="application/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=669671">Mozilla Bug 669671</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
/**
|
||||
* Test for Bug 669671.
|
||||
*
|
||||
* This is a bit complicated. We have a script, file_bug669671.sjs, which counts
|
||||
* how many times it's loaded and returns that count in the body of an HTML
|
||||
* document. For brevity, call this page X.
|
||||
*
|
||||
* X is sent with Cache-Control: max-age=0 and can't be bfcached (it has an
|
||||
* onunload handler). Our test does the following in a popup:
|
||||
*
|
||||
* 1) Load X?pushed, to prime the cache.
|
||||
* 2) Navigate to X.
|
||||
* 3) Call pushState and navigate from X to X?pushed.
|
||||
* 4) Navigate to X?navigated.
|
||||
* 5) Go back (to X?pushed).
|
||||
*
|
||||
* We do all this work so we can check that in step 5, we fetch X?pushed from
|
||||
* the network -- we shouldn't use our cached copy, because of the
|
||||
* cache-control header X sends.
|
||||
*
|
||||
* Then we go back and repeat the whole process but call history.replaceState
|
||||
* instead of pushState. And for good measure, we test once more, this time
|
||||
* modifying only the hash of the URI using replaceState. In this case, we
|
||||
* *should* load from the cache.
|
||||
*
|
||||
**/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function onChildLoad()
|
||||
{
|
||||
SimpleTest.executeSoon(function() { gGen.next() });
|
||||
}
|
||||
|
||||
var _loadCount = 0;
|
||||
function checkPopupLoadCount()
|
||||
{
|
||||
is(popup.document.body.innerHTML, _loadCount + '', 'Load count');
|
||||
|
||||
// We normally want to increment _loadCount here. But if the test fails
|
||||
// because we didn't do a load we should have, let's not cause a cascade of
|
||||
// failures by incrementing _loadCount.
|
||||
var origCount = _loadCount;
|
||||
if (popup.document.body.innerHTML >= _loadCount + '')
|
||||
_loadCount++;
|
||||
return origCount;
|
||||
}
|
||||
|
||||
function test()
|
||||
{
|
||||
// Step 1 - The popup's body counts how many times we've requested the
|
||||
// resource. This is the first time we've requested it, so it should be '0'.
|
||||
checkPopupLoadCount();
|
||||
|
||||
// Step 2 - We'll get another onChildLoad when this finishes.
|
||||
popup.location = 'file_bug669671.sjs';
|
||||
yield;
|
||||
|
||||
// Step 3 - Call pushState and change the URI back to ?pushed.
|
||||
checkPopupLoadCount();
|
||||
popup.history.pushState('', '', '?pushed');
|
||||
|
||||
// Step 4 - Navigate away. This should trigger another onChildLoad.
|
||||
popup.location = 'file_bug669671.sjs?navigated-1';
|
||||
yield;
|
||||
|
||||
// Step 5 - Go back. This should result in another onload (because the file is
|
||||
// not in bfcache) and should be the fourth time we've requested the sjs file.
|
||||
checkPopupLoadCount();
|
||||
popup.back();
|
||||
yield;
|
||||
|
||||
// This is the check which was failing before we fixed the bug.
|
||||
checkPopupLoadCount();
|
||||
|
||||
popup.close();
|
||||
|
||||
// Do the whole thing again, but with replaceState.
|
||||
popup = window.open('file_bug669671.sjs?replaced');
|
||||
yield;
|
||||
checkPopupLoadCount();
|
||||
popup.location = 'file_bug669671.sjs';
|
||||
yield;
|
||||
checkPopupLoadCount();
|
||||
popup.history.replaceState('', '', '?replaced');
|
||||
popup.location = 'file_bug669671.sjs?navigated-2';
|
||||
yield;
|
||||
checkPopupLoadCount();
|
||||
popup.back();
|
||||
yield;
|
||||
checkPopupLoadCount();
|
||||
popup.close();
|
||||
|
||||
// Once more, with feeling. Notice that we don't have to prime the cache
|
||||
// with an extra load here, because X and X#hash share the same cache entry.
|
||||
popup = window.open('file_bug669671.sjs?hash-test');
|
||||
yield;
|
||||
var initialCount = checkPopupLoadCount();
|
||||
popup.history.replaceState('', '', '#hash');
|
||||
popup.location = 'file_bug669671.sjs?navigated-3';
|
||||
yield;
|
||||
checkPopupLoadCount();
|
||||
popup.back();
|
||||
yield;
|
||||
is(popup.document.body.innerHTML, initialCount + '',
|
||||
'Load count (should be cached)');
|
||||
popup.close();
|
||||
|
||||
SimpleTest.finish();
|
||||
yield;
|
||||
}
|
||||
|
||||
// This will call into onChildLoad once it loads.
|
||||
var popup = window.open('file_bug669671.sjs?pushed');
|
||||
|
||||
var gGen = test();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -54,31 +54,31 @@ nsDOMMemoryReporter::Init()
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMMemoryReporter::GetProcess(char** aProcess)
|
||||
nsDOMMemoryReporter::GetProcess(nsACString &aProcess)
|
||||
{
|
||||
// "" means the main process.
|
||||
*aProcess = strdup("");
|
||||
aProcess.Truncate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMMemoryReporter::GetPath(char** aMemoryPath)
|
||||
nsDOMMemoryReporter::GetPath(nsACString &aMemoryPath)
|
||||
{
|
||||
*aMemoryPath = strdup("explicit/dom");
|
||||
aMemoryPath.AssignLiteral("explicit/dom");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMMemoryReporter::GetKind(int* aKind)
|
||||
nsDOMMemoryReporter::GetKind(PRInt32* aKind)
|
||||
{
|
||||
*aKind = KIND_HEAP;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMMemoryReporter::GetDescription(char** aDescription)
|
||||
nsDOMMemoryReporter::GetDescription(nsACString &aDescription)
|
||||
{
|
||||
*aDescription = strdup("Memory used by the DOM.");
|
||||
aDescription.AssignLiteral("Memory used by the DOM.");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,16 +44,10 @@
|
|||
class nsDOMMemoryReporter: public nsIMemoryReporter {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIMEMORYREPORTER
|
||||
|
||||
static void Init();
|
||||
|
||||
NS_IMETHOD GetProcess(char** aProcess);
|
||||
NS_IMETHOD GetPath(char** aMemoryPath);
|
||||
NS_IMETHOD GetKind(int* aKnd);
|
||||
NS_IMETHOD GetDescription(char** aDescription);
|
||||
NS_IMETHOD GetUnits(PRInt32* aUnits);
|
||||
NS_IMETHOD GetAmount(PRInt64* aAmount);
|
||||
|
||||
private:
|
||||
// Protect ctor, use Init() instead.
|
||||
nsDOMMemoryReporter();
|
||||
|
|
|
@ -185,7 +185,10 @@ static PRTime sMaxChromeScriptRunTime;
|
|||
static nsIScriptSecurityManager *sSecurityManager;
|
||||
|
||||
// nsMemoryPressureObserver observes the memory-pressure notifications
|
||||
// and forces a garbage collection and cycle collection when it happens.
|
||||
// and forces a garbage collection and cycle collection when it happens, if
|
||||
// the appropriate pref is set.
|
||||
|
||||
static PRBool sGCOnMemoryPressure;
|
||||
|
||||
class nsMemoryPressureObserver : public nsIObserver
|
||||
{
|
||||
|
@ -200,8 +203,10 @@ NS_IMETHODIMP
|
|||
nsMemoryPressureObserver::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const PRUnichar* aData)
|
||||
{
|
||||
nsJSContext::GarbageCollectNow();
|
||||
nsJSContext::CycleCollectNow();
|
||||
if (sGCOnMemoryPressure) {
|
||||
nsJSContext::GarbageCollectNow();
|
||||
nsJSContext::CycleCollectNow();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -3795,6 +3800,10 @@ nsJSRuntime::Init()
|
|||
if (!obs)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
Preferences::AddBoolVarCache(&sGCOnMemoryPressure,
|
||||
"javascript.options.gc_on_memory_pressure",
|
||||
PR_TRUE);
|
||||
|
||||
nsIObserver* memPressureObserver = new nsMemoryPressureObserver();
|
||||
NS_ENSURE_TRUE(memPressureObserver, NS_ERROR_OUT_OF_MEMORY);
|
||||
obs->AddObserver(memPressureObserver, "memory-pressure", PR_FALSE);
|
||||
|
|
|
@ -49,8 +49,9 @@
|
|||
*
|
||||
* @see <http://www.whatwg.org/html/>
|
||||
*/
|
||||
interface nsISelection;
|
||||
|
||||
[scriptable, uuid(3c0ca40f-72c5-4d15-935e-ccaff7953f2c)]
|
||||
[scriptable, uuid(3ab3e856-361d-435a-8a4d-b462799945cd)]
|
||||
interface nsIDOMHTMLDocument : nsIDOMDocument
|
||||
{
|
||||
readonly attribute DOMString URL;
|
||||
|
@ -132,7 +133,7 @@ interface nsIDOMHTMLDocument : nsIDOMDocument
|
|||
|
||||
|
||||
// DOM Range
|
||||
DOMString getSelection();
|
||||
nsISelection getSelection();
|
||||
|
||||
|
||||
// Mozilla extensions
|
||||
|
|
|
@ -363,11 +363,11 @@ ContentChild::RecvPMemoryReportRequestConstructor(PMemoryReportRequestChild* chi
|
|||
PRInt32 units;
|
||||
PRInt64 amount;
|
||||
nsCString desc;
|
||||
r->GetPath(getter_Copies(path));
|
||||
r->GetPath(path);
|
||||
r->GetKind(&kind);
|
||||
r->GetUnits(&units);
|
||||
r->GetAmount(&amount);
|
||||
r->GetDescription(getter_Copies(desc));
|
||||
r->GetDescription(desc);
|
||||
|
||||
MemoryReport memreport(process, path, kind, units, amount, desc);
|
||||
reports.AppendElement(memreport);
|
||||
|
|
|
@ -89,6 +89,7 @@
|
|||
<action android:name="org.mozilla.gecko.restart"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
#if MOZ_CRASHREPORTER
|
||||
<activity android:name="CrashReporter"
|
||||
android:label="@string/crash_reporter_title"
|
||||
|
@ -98,5 +99,14 @@
|
|||
</intent-filter>
|
||||
</activity>
|
||||
#endif
|
||||
|
||||
<activity android:name="LauncherShortcuts"
|
||||
android:label="@string/launcher_shortcuts_title">
|
||||
<!-- This intent-filter allows your shortcuts to be created in the launcher. -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.CREATE_SHORTCUT" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
|
@ -303,7 +303,7 @@ abstract public class GeckoApp
|
|||
Log.i("GeckoApp", "Intent : ACTION_MAIN");
|
||||
GeckoAppShell.sendEventToGecko(new GeckoEvent(""));
|
||||
}
|
||||
else if (action.equals("org.mozilla.fennec.WEBAPP")) {
|
||||
else if (action.equals("org.mozilla.gecko.WEBAPP")) {
|
||||
String uri = intent.getStringExtra("args");
|
||||
GeckoAppShell.sendEventToGecko(new GeckoEvent(uri));
|
||||
Log.i("GeckoApp","Intent : WEBAPP - " + uri);
|
||||
|
|
|
@ -678,7 +678,7 @@ public class GeckoAppShell
|
|||
Log.w("GeckoAppJava", "installWebApplication for " + aURI + " [" + aTitle + "]");
|
||||
|
||||
// the intent to be launched by the shortcut
|
||||
Intent shortcutIntent = new Intent("org.mozilla.fennec.WEBAPP");
|
||||
Intent shortcutIntent = new Intent("org.mozilla.gecko.WEBAPP");
|
||||
shortcutIntent.setClassName(GeckoApp.mAppContext,
|
||||
GeckoApp.mAppContext.getPackageName() + ".App");
|
||||
shortcutIntent.putExtra("args", "--webapp=" + aURI);
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** 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 Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Vladimir Vukicevic <vladimir@pobox.com>
|
||||
* Wes Johnston <wjohnston@mozilla.com>
|
||||
* Mark Finkle <mfinkle@mozilla.com>
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
#filter substitution
|
||||
package @ANDROID_PACKAGE_NAME@;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import org.mozilla.gecko.*;
|
||||
|
||||
import android.os.*;
|
||||
import android.content.*;
|
||||
import android.app.*;
|
||||
import android.text.*;
|
||||
import android.util.*;
|
||||
import android.widget.*;
|
||||
import android.database.sqlite.*;
|
||||
import android.database.*;
|
||||
import android.view.*;
|
||||
import android.net.Uri;
|
||||
import android.graphics.*;
|
||||
|
||||
|
||||
public class LauncherShortcuts extends ListActivity {
|
||||
public static final String CREATE_SHORTCUT = "org.mozilla.gecko.CREATE_SHORTCUT";
|
||||
|
||||
public class LauncherCursorAdapter extends SimpleCursorAdapter {
|
||||
private Cursor _cursor;
|
||||
private Context _context;
|
||||
|
||||
public LauncherCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
|
||||
// Using the older, deprecated constructor so we can work on API < 11
|
||||
super(context, layout, c, from, to);
|
||||
_cursor = c;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
ImageView imageView = (ImageView) view.findViewById(R.id.favicon);
|
||||
|
||||
String favicon = cursor.getString(3);
|
||||
byte[] raw = Base64.decode(favicon.substring(22), Base64.DEFAULT);
|
||||
Bitmap bitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeByteArray(raw, 0, raw.length), 48, 48, true);
|
||||
imageView.setImageBitmap(bitmap);
|
||||
|
||||
super.bindView(view, context, cursor);
|
||||
}
|
||||
}
|
||||
|
||||
private Cursor getCursor(Context context) {
|
||||
File home = new File(context.getFilesDir(), "mozilla");
|
||||
if (!home.exists())
|
||||
return null;
|
||||
|
||||
File profile = null;
|
||||
String[] files = home.list();
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
if (files[i].endsWith(".default")) {
|
||||
profile = new File(home, files[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (profile == null)
|
||||
return null;
|
||||
|
||||
File webapps = new File(profile, "webapps.sqlite");
|
||||
if (!webapps.exists())
|
||||
return null;
|
||||
|
||||
Log.i("LauncherShortcuts", "Opening: " + webapps.getPath());
|
||||
mDb = SQLiteDatabase.openDatabase(webapps.getPath(), null, SQLiteDatabase.OPEN_READONLY | SQLiteDatabase.NO_LOCALIZED_COLLATORS);
|
||||
return mDb.rawQuery("SELECT rowid as _id, title, uri, icon FROM webapps", null);
|
||||
}
|
||||
|
||||
private Cursor mCursor;
|
||||
private SQLiteDatabase mDb;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.launch_app_list);
|
||||
|
||||
final Intent intent = getIntent();
|
||||
final String action = intent.getAction();
|
||||
|
||||
if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) {
|
||||
mCursor = getCursor(this);
|
||||
if (mCursor != null) {
|
||||
// After selecting an item, the empty view can flash on screen. Clear
|
||||
// the text so we don't see it.
|
||||
TextView emptyText = (TextView)findViewById(android.R.id.empty);
|
||||
emptyText.setText("");
|
||||
|
||||
// Load the list using a custom adapter so we can create the bitmaps
|
||||
ListAdapter adapter = new LauncherCursorAdapter(
|
||||
this,
|
||||
R.layout.launch_app_listitem,
|
||||
mCursor,
|
||||
new String[] {"title"},
|
||||
new int[] {R.id.title}
|
||||
);
|
||||
setListAdapter(adapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onListItemClick(ListView l, View v, int position, long id) {
|
||||
mCursor.moveToPosition(position);
|
||||
|
||||
Intent shortcutintent = new Intent("org.mozilla.gecko.WEBAPP");
|
||||
shortcutintent.setClass(this, App.class);
|
||||
shortcutintent.putExtra("args", "--webapp=" + mCursor.getString(2));
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, mCursor.getString(1));
|
||||
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutintent);
|
||||
|
||||
String favicon = mCursor.getString(3);
|
||||
byte[] raw = Base64.decode(favicon.substring(22), Base64.DEFAULT);
|
||||
|
||||
DisplayMetrics dm = new DisplayMetrics();
|
||||
getWindowManager().getDefaultDisplay().getMetrics(dm);
|
||||
int size;
|
||||
switch (dm.densityDpi) {
|
||||
case DisplayMetrics.DENSITY_MEDIUM:
|
||||
size = 48;
|
||||
break;
|
||||
case DisplayMetrics.DENSITY_HIGH:
|
||||
size = 72;
|
||||
break;
|
||||
default:
|
||||
size = 72;
|
||||
}
|
||||
|
||||
Bitmap bitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeByteArray(raw, 0, raw.length), size, size, true);
|
||||
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, bitmap);
|
||||
|
||||
// Now, return the result to the launcher
|
||||
setResult(RESULT_OK, intent);
|
||||
mDb.close();
|
||||
mCursor.close();
|
||||
finish();
|
||||
}
|
||||
}
|
|
@ -59,6 +59,7 @@ PROCESSEDJAVAFILES = \
|
|||
App.java \
|
||||
Restarter.java \
|
||||
NotificationHandler.java \
|
||||
LauncherShortcuts.java \
|
||||
$(NULL)
|
||||
|
||||
|
||||
|
@ -116,6 +117,8 @@ RES_LAYOUT = \
|
|||
res/layout/notification_progress.xml \
|
||||
res/layout/notification_progress_text.xml \
|
||||
res/layout/notification_icon_text.xml \
|
||||
res/layout/launch_app_list.xml \
|
||||
res/layout/launch_app_listitem.xml \
|
||||
$(NULL)
|
||||
|
||||
RES_VALUES = res/values/colors.xml res/values/themes.xml
|
||||
|
|
|
@ -16,3 +16,6 @@
|
|||
<!ENTITY sending_crash_report "Sending crash report\u2026">
|
||||
<!ENTITY exit_label "Exit">
|
||||
<!ENTITY continue_label "Continue">
|
||||
|
||||
<!ENTITY launcher_shortcuts_title "&brandShortName; Web Apps">
|
||||
<!ENTITY launcher_shortcuts_empty "No web apps were found">
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="3dip"
|
||||
android:orientation="vertical"
|
||||
android:windowIsFloating="true">
|
||||
|
||||
<ListView android:id="@android:id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:drawSelectorOnTop="false"/>
|
||||
|
||||
<TextView android:id="@android:id/empty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:text="@string/launcher_shortcuts_empty"/>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:attr/listPreferredItemHeight"
|
||||
android:padding="6dip"
|
||||
android:orientation="horizontal">
|
||||
<ImageView
|
||||
android:id="@+id/favicon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginRight="6dip"/>
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"/>
|
||||
</LinearLayout>
|
|
@ -21,4 +21,7 @@
|
|||
<string name="sending_crash_report">&sending_crash_report;</string>
|
||||
<string name="exit_label">&exit_label;</string>
|
||||
<string name="continue_label">&continue_label;</string>
|
||||
|
||||
<string name="launcher_shortcuts_title">&launcher_shortcuts_title;</string>
|
||||
<string name="launcher_shortcuts_empty">&launcher_shortcuts_empty;</string>
|
||||
</resources>
|
||||
|
|
|
@ -600,13 +600,13 @@ public:
|
|||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD GetProcess(char **process) {
|
||||
*process = strdup("");
|
||||
NS_IMETHOD GetProcess(nsACString &process) {
|
||||
process.Truncate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetPath(char **memoryPath) {
|
||||
*memoryPath = strdup(SurfaceMemoryReporterPathForType(mType));
|
||||
NS_IMETHOD GetPath(nsACString &path) {
|
||||
path.Assign(SurfaceMemoryReporterPathForType(mType));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -625,8 +625,8 @@ public:
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetDescription(char **desc) {
|
||||
*desc = strdup("Memory used by gfx surface of the given type.");
|
||||
NS_IMETHOD GetDescription(nsACString &desc) {
|
||||
desc.AssignLiteral("Memory used by gfx surface of the given type.");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -481,8 +481,10 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() {
|
|||
|
||||
// Process messages from input buffer.
|
||||
const char *p;
|
||||
const char *overflowp;
|
||||
const char *end;
|
||||
if (input_overflow_buf_.empty()) {
|
||||
overflowp = NULL;
|
||||
p = input_buf_;
|
||||
end = p + bytes_read;
|
||||
} else {
|
||||
|
@ -493,7 +495,7 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() {
|
|||
return false;
|
||||
}
|
||||
input_overflow_buf_.append(input_buf_, bytes_read);
|
||||
p = input_overflow_buf_.data();
|
||||
overflowp = p = input_overflow_buf_.data();
|
||||
end = p + input_overflow_buf_.size();
|
||||
}
|
||||
|
||||
|
@ -571,7 +573,15 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
input_overflow_buf_.assign(p, end - p);
|
||||
if (end == p) {
|
||||
input_overflow_buf_.clear();
|
||||
} else if (!overflowp) {
|
||||
// p is from input_buf_
|
||||
input_overflow_buf_.assign(p, end - p);
|
||||
} else if (p > overflowp) {
|
||||
// p is from input_overflow_buf_
|
||||
input_overflow_buf_.erase(0, p - overflowp);
|
||||
}
|
||||
input_overflow_fds_ = std::vector<int>(&fds[fds_i], &fds[num_fds]);
|
||||
|
||||
// When the input data buffer is empty, the overflow fds should be too. If
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
|
||||
#include <math.h>
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "mozilla/ipc/SharedMemory.h"
|
||||
|
||||
|
|
|
@ -150,6 +150,7 @@ CPPSRCS = \
|
|||
jsgcmark.cpp \
|
||||
jsgcchunk.cpp \
|
||||
jsgcstats.cpp \
|
||||
jscrashreport.cpp \
|
||||
jshash.cpp \
|
||||
jsinterp.cpp \
|
||||
jsinvoke.cpp \
|
||||
|
@ -206,6 +207,7 @@ INSTALLED_HEADERS = \
|
|||
jsclone.h \
|
||||
jscntxt.h \
|
||||
jscompat.h \
|
||||
jscrashreport.h \
|
||||
jsdate.h \
|
||||
jsdbgapi.h \
|
||||
jsdhash.h \
|
||||
|
|
|
@ -1183,6 +1183,16 @@ extern JS_PUBLIC_API(JSBool)
|
|||
JS_SetCTypesCallbacks(JSContext *cx, JSObject *ctypesObj, JSCTypesCallbacks *callbacks);
|
||||
#endif
|
||||
|
||||
typedef JSBool
|
||||
(* JSEnumerateDiagnosticMemoryCallback)(void *ptr, size_t length);
|
||||
|
||||
/*
|
||||
* Enumerate memory regions that contain diagnostic information
|
||||
* intended to be included in crash report minidumps.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_EnumerateDiagnosticMemoryRegions(JSEnumerateDiagnosticMemoryCallback callback);
|
||||
|
||||
/*
|
||||
* Macros to hide interpreter stack layout details from a JSFastNative using
|
||||
* its jsval *vp parameter. The stack layout underlying invocation can't change
|
||||
|
|
|
@ -412,6 +412,12 @@ struct JSRuntime {
|
|||
/* Compartment that is currently involved in per-compartment GC */
|
||||
JSCompartment *gcCurrentCompartment;
|
||||
|
||||
/*
|
||||
* If this is non-NULL, all marked objects must belong to this compartment.
|
||||
* This is used to look for compartment bugs.
|
||||
*/
|
||||
JSCompartment *gcCheckCompartment;
|
||||
|
||||
/*
|
||||
* We can pack these flags as only the GC thread writes to them. Atomic
|
||||
* updates to packed bytes are not guaranteed, so stores issued by one
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=4 sw=4 et tw=99:
|
||||
*
|
||||
* ***** 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 SpiderMonkey JavaScript 1.9 code, released
|
||||
* May 28, 2008.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation
|
||||
* 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 of 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 jscrashformat_h___
|
||||
#define jscrashformat_h___
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace js {
|
||||
namespace crash {
|
||||
|
||||
const static int crash_cookie_len = 16;
|
||||
const static char crash_cookie[crash_cookie_len] = "*J*S*CRASHDATA*";
|
||||
|
||||
/* These values are used for CrashHeader::id. */
|
||||
enum {
|
||||
JS_CRASH_STACK_GC = 0x400,
|
||||
JS_CRASH_STACK_ERROR = 0x401,
|
||||
JS_CRASH_RING = 0x800
|
||||
};
|
||||
|
||||
/*
|
||||
* All the data here will be stored directly in the minidump, so we use
|
||||
* platform-independent types. We also ensure that the size of every field is a
|
||||
* multiple of 8 bytes, to guarantee that they won't be padded.
|
||||
*/
|
||||
|
||||
struct CrashHeader
|
||||
{
|
||||
char cookie[crash_cookie_len];
|
||||
|
||||
/* id of the crash data, chosen from the enum above. */
|
||||
uint64 id;
|
||||
|
||||
CrashHeader(uint64 id) : id(id) { memcpy(cookie, crash_cookie, crash_cookie_len); }
|
||||
};
|
||||
|
||||
struct CrashRegisters
|
||||
{
|
||||
uint64 ip, sp, bp;
|
||||
};
|
||||
|
||||
const static int crash_buffer_size = 32 * 1024;
|
||||
|
||||
struct CrashStack
|
||||
{
|
||||
CrashStack(uint64 id) : header(id) {}
|
||||
|
||||
CrashHeader header;
|
||||
uint64 snaptime; /* Unix time when the stack was snapshotted. */
|
||||
CrashRegisters regs; /* Register contents for the snapshot. */
|
||||
uint64 stack_base; /* Base address of stack at the time of snapshot. */
|
||||
uint64 stack_len; /* Extent of the stack. */
|
||||
char stack[crash_buffer_size]; /* Contents of the stack. */
|
||||
};
|
||||
|
||||
struct CrashRing
|
||||
{
|
||||
CrashRing(uint64 id) : header(id), offset(0) { memset(buffer, 0, sizeof(buffer)); }
|
||||
|
||||
CrashHeader header;
|
||||
uint64 offset; /* Next byte to be written in the buffer. */
|
||||
char buffer[crash_buffer_size];
|
||||
};
|
||||
|
||||
/* These are the tag values for each entry in the CrashRing. */
|
||||
enum {
|
||||
JS_CRASH_TAG_GC = 0x200
|
||||
};
|
||||
|
||||
} /* namespace crash */
|
||||
} /* namespace js */
|
||||
|
||||
#endif
|
|
@ -0,0 +1,280 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=4 sw=4 et tw=99:
|
||||
*
|
||||
* ***** 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 SpiderMonkey JavaScript 1.9 code, released
|
||||
* May 28, 2008.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation
|
||||
* 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 of 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 ***** */
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jscrashreport.h"
|
||||
#include "jscrashformat.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
namespace js {
|
||||
namespace crash {
|
||||
|
||||
const static int stack_snapshot_max_size = 32768;
|
||||
|
||||
#if defined(XP_WIN)
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
static bool
|
||||
GetStack(uint64 *stack, uint64 *stack_len, CrashRegisters *regs, char *buffer, size_t size)
|
||||
{
|
||||
/* Try to figure out how big the stack is. */
|
||||
char dummy;
|
||||
MEMORY_BASIC_INFORMATION info;
|
||||
if (VirtualQuery(reinterpret_cast<LPCVOID>(&dummy), &info, sizeof(info)) == 0)
|
||||
return false;
|
||||
if (info.State != MEM_COMMIT)
|
||||
return false;
|
||||
|
||||
/* 256 is a fudge factor to account for the rest of GetStack's frame. */
|
||||
uint64 p = uint64(&dummy) - 256;
|
||||
uint64 len = stack_snapshot_max_size;
|
||||
|
||||
if (p + len > uint64(info.BaseAddress) + info.RegionSize)
|
||||
len = uint64(info.BaseAddress) + info.RegionSize - p;
|
||||
|
||||
if (len > size)
|
||||
len = size;
|
||||
|
||||
*stack = p;
|
||||
*stack_len = len;
|
||||
|
||||
/* Get the register state. */
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
uint32 vip, vsp, vbp;
|
||||
__asm {
|
||||
Label:
|
||||
mov [vbp], ebp;
|
||||
mov [vsp], esp;
|
||||
mov eax, [Label];
|
||||
mov [vip], eax;
|
||||
}
|
||||
regs->ip = vip;
|
||||
regs->sp = vsp;
|
||||
regs->bp = vbp;
|
||||
#else
|
||||
CONTEXT context;
|
||||
RtlCaptureContext(&context);
|
||||
regs->ip = context.Rip;
|
||||
regs->sp = context.Rsp;
|
||||
regs->bp = context.Rbp;
|
||||
#endif
|
||||
|
||||
memcpy(buffer, (void *)p, len);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#elif defined(__linux__) && (defined(__x86_64__) || defined(__i386__))
|
||||
|
||||
#include <unistd.h>
|
||||
#include <ucontext.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
static bool
|
||||
GetStack(uint64 *stack, uint64 *stack_len, CrashRegisters *regs, char *buffer, size_t size)
|
||||
{
|
||||
/* 256 is a fudge factor to account for the rest of GetStack's frame. */
|
||||
char dummy;
|
||||
uint64 p = uint64(&dummy) - 256;
|
||||
uint64 pgsz = getpagesize();
|
||||
uint64 len = stack_snapshot_max_size;
|
||||
p &= ~(pgsz - 1);
|
||||
|
||||
/* Try to figure out how big the stack is. */
|
||||
while (len > 0) {
|
||||
if (mlock((const void *)p, len) == 0) {
|
||||
munlock((const void *)p, len);
|
||||
break;
|
||||
}
|
||||
len -= pgsz;
|
||||
}
|
||||
|
||||
if (len > size)
|
||||
len = size;
|
||||
|
||||
*stack = p;
|
||||
*stack_len = len;
|
||||
|
||||
/* Get the register state. */
|
||||
ucontext_t context;
|
||||
if (getcontext(&context) != 0)
|
||||
return false;
|
||||
|
||||
#if JS_BITS_PER_WORD == 64
|
||||
regs->sp = (uint64)context.uc_mcontext.gregs[REG_RSP];
|
||||
regs->bp = (uint64)context.uc_mcontext.gregs[REG_RBP];
|
||||
regs->ip = (uint64)context.uc_mcontext.gregs[REG_RIP];
|
||||
#elif JS_BITS_PER_WORD == 32
|
||||
regs->sp = (uint64)context.uc_mcontext.gregs[REG_ESP];
|
||||
regs->bp = (uint64)context.uc_mcontext.gregs[REG_EBP];
|
||||
regs->ip = (uint64)context.uc_mcontext.gregs[REG_EIP];
|
||||
#endif
|
||||
|
||||
memcpy(buffer, (void *)p, len);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static bool
|
||||
GetStack(uint64 *stack, uint64 *stack_len, CrashRegisters *regs, char *buffer, size_t size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
class Stack : private CrashStack
|
||||
{
|
||||
public:
|
||||
Stack(uint64 id);
|
||||
|
||||
bool snapshot();
|
||||
};
|
||||
|
||||
Stack::Stack(uint64 id)
|
||||
: CrashStack(id)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
Stack::snapshot()
|
||||
{
|
||||
snaptime = time(NULL);
|
||||
return GetStack(&stack_base, &stack_len, ®s, stack, sizeof(stack));
|
||||
}
|
||||
|
||||
class Ring : private CrashRing
|
||||
{
|
||||
public:
|
||||
Ring(uint64 id);
|
||||
|
||||
void push(uint64 tag, void *data, size_t size);
|
||||
|
||||
private:
|
||||
size_t bufferSize() { return crash_buffer_size; }
|
||||
void copyBytes(void *data, size_t size);
|
||||
};
|
||||
|
||||
Ring::Ring(uint64 id)
|
||||
: CrashRing(id)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Ring::push(uint64 tag, void *data, size_t size)
|
||||
{
|
||||
uint64 t = time(NULL);
|
||||
|
||||
copyBytes(&tag, sizeof(uint64));
|
||||
copyBytes(&t, sizeof(uint64));
|
||||
copyBytes(data, size);
|
||||
uint64 mysize = size;
|
||||
copyBytes(&mysize, sizeof(uint64));
|
||||
}
|
||||
|
||||
void
|
||||
Ring::copyBytes(void *data, size_t size)
|
||||
{
|
||||
if (size >= bufferSize())
|
||||
size = bufferSize();
|
||||
|
||||
if (offset + size > bufferSize()) {
|
||||
size_t first = bufferSize() - offset;
|
||||
size_t second = size - first;
|
||||
memcpy(&buffer[offset], data, first);
|
||||
memcpy(buffer, (char *)data + first, second);
|
||||
offset = second;
|
||||
} else {
|
||||
memcpy(&buffer[offset], data, size);
|
||||
offset += size;
|
||||
}
|
||||
}
|
||||
|
||||
static bool gInitialized;
|
||||
|
||||
static Stack gGCStack(JS_CRASH_STACK_GC);
|
||||
static Stack gErrorStack(JS_CRASH_STACK_ERROR);
|
||||
static Ring gRingBuffer(JS_CRASH_RING);
|
||||
|
||||
} /* namespace crash */
|
||||
} /* namespace js */
|
||||
|
||||
using namespace js;
|
||||
using namespace js::crash;
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js_SnapshotGCStack()
|
||||
{
|
||||
if (gInitialized)
|
||||
gGCStack.snapshot();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js_SnapshotErrorStack()
|
||||
{
|
||||
if (gInitialized)
|
||||
gErrorStack.snapshot();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js_SaveCrashData(uint64 tag, void *ptr, size_t size)
|
||||
{
|
||||
if (gInitialized)
|
||||
gRingBuffer.push(tag, ptr, size);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_EnumerateDiagnosticMemoryRegions(JSEnumerateDiagnosticMemoryCallback callback)
|
||||
{
|
||||
#if 1
|
||||
if (!gInitialized) {
|
||||
gInitialized = true;
|
||||
(*callback)(&gGCStack, sizeof(gGCStack));
|
||||
(*callback)(&gErrorStack, sizeof(gErrorStack));
|
||||
(*callback)(&gRingBuffer, sizeof(gRingBuffer));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=4 sw=4 et tw=99:
|
||||
*
|
||||
* ***** 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 SpiderMonkey JavaScript 1.9 code, released
|
||||
* May 28, 2008.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation
|
||||
* 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 of 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 jscrashreport_h___
|
||||
#define jscrashreport_h___
|
||||
|
||||
#include "jstypes.h"
|
||||
|
||||
JS_BEGIN_EXTERN_C
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js_SnapshotGCStack();
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js_SnapshotErrorStack();
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js_SaveCrashData(uint64 tag, void *ptr, size_t size);
|
||||
|
||||
JS_END_EXTERN_C
|
||||
|
||||
#endif /* jscrashreport_h___ */
|
|
@ -60,6 +60,8 @@
|
|||
#include "jsapi.h"
|
||||
#include "jsatom.h"
|
||||
#include "jscompartment.h"
|
||||
#include "jscrashreport.h"
|
||||
#include "jscrashformat.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jsversion.h"
|
||||
#include "jsdbgapi.h"
|
||||
|
@ -239,9 +241,7 @@ Arena::finalize(JSContext *cx)
|
|||
if (!newFreeSpanStart)
|
||||
newFreeSpanStart = thing;
|
||||
t->finalize(cx);
|
||||
#ifdef DEBUG
|
||||
memset(t, JS_FREE_PATTERN, sizeof(T));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2662,6 +2662,12 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIMER_P
|
|||
(*c)->setGCLastBytes((*c)->gcBytes, gckind);
|
||||
}
|
||||
|
||||
struct GCCrashData
|
||||
{
|
||||
int isRegen;
|
||||
int isCompartment;
|
||||
};
|
||||
|
||||
void
|
||||
js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
|
||||
{
|
||||
|
@ -2683,6 +2689,11 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
|
|||
|
||||
RecordNativeStackTopForGC(cx);
|
||||
|
||||
GCCrashData crashData;
|
||||
crashData.isRegen = rt->shapeGen & SHAPE_OVERFLOW_BIT;
|
||||
crashData.isCompartment = !!comp;
|
||||
js_SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData));
|
||||
|
||||
GCTIMER_BEGIN(rt, comp);
|
||||
|
||||
do {
|
||||
|
@ -2721,6 +2732,8 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
|
|||
|
||||
rt->gcChunkAllocationSinceLastGC = false;
|
||||
GCTIMER_END(gckind == GC_LAST_CONTEXT);
|
||||
|
||||
js_SnapshotGCStack();
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
|
|
@ -115,6 +115,12 @@ Mark(JSTracer *trc, T *thing)
|
|||
JS_ASSERT(thing->arenaHeader()->compartment);
|
||||
JS_ASSERT(thing->arenaHeader()->compartment->rt == rt);
|
||||
|
||||
if (rt->gcCheckCompartment && thing->compartment() != rt->gcCheckCompartment &&
|
||||
thing->compartment() != rt->atomsCompartment)
|
||||
{
|
||||
JS_Assert("compartment mismatch in GC", __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't mark things outside a compartment if we are in a per-compartment
|
||||
* GC.
|
||||
|
@ -158,6 +164,16 @@ MarkObject(JSTracer *trc, JSObject &obj, const char *name)
|
|||
Mark(trc, &obj);
|
||||
}
|
||||
|
||||
void
|
||||
MarkCrossCompartmentObject(JSTracer *trc, JSObject &obj, const char *name)
|
||||
{
|
||||
JSRuntime *rt = trc->context->runtime;
|
||||
if (rt->gcCurrentCompartment && rt->gcCurrentCompartment != obj.compartment())
|
||||
return;
|
||||
|
||||
MarkObject(trc, obj, name);
|
||||
}
|
||||
|
||||
void
|
||||
MarkObjectWithPrinter(JSTracer *trc, JSObject &obj, JSTraceNamePrinter printer,
|
||||
const void *arg, size_t index)
|
||||
|
@ -351,6 +367,22 @@ MarkValue(JSTracer *trc, const js::Value &v, const char *name)
|
|||
MarkValueRaw(trc, v);
|
||||
}
|
||||
|
||||
void
|
||||
MarkCrossCompartmentValue(JSTracer *trc, const js::Value &v, const char *name)
|
||||
{
|
||||
if (v.isMarkable()) {
|
||||
js::gc::Cell *cell = (js::gc::Cell *)v.toGCThing();
|
||||
unsigned kind = v.gcKind();
|
||||
if (kind == JSTRACE_STRING && ((JSString *)cell)->isStaticAtom())
|
||||
return;
|
||||
JSRuntime *rt = trc->context->runtime;
|
||||
if (rt->gcCurrentCompartment && cell->compartment() != rt->gcCurrentCompartment)
|
||||
return;
|
||||
|
||||
MarkValue(trc, v, name);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MarkValueRange(JSTracer *trc, Value *beg, Value *end, const char *name)
|
||||
{
|
||||
|
@ -748,6 +780,9 @@ MarkChildren(JSTracer *trc, JSXML *xml)
|
|||
void
|
||||
GCMarker::drainMarkStack()
|
||||
{
|
||||
JSRuntime *rt = context->runtime;
|
||||
rt->gcCheckCompartment = rt->gcCurrentCompartment;
|
||||
|
||||
while (!isMarkStackEmpty()) {
|
||||
while (!ropeStack.isEmpty())
|
||||
ScanRope(this, ropeStack.pop());
|
||||
|
@ -772,6 +807,8 @@ GCMarker::drainMarkStack()
|
|||
markDelayedChildren();
|
||||
}
|
||||
}
|
||||
|
||||
rt->gcCheckCompartment = NULL;
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
|
|
@ -62,6 +62,13 @@ MarkString(JSTracer *trc, JSString *str, const char *name);
|
|||
void
|
||||
MarkObject(JSTracer *trc, JSObject &obj, const char *name);
|
||||
|
||||
/*
|
||||
* Mark an object that may be in a different compartment from the compartment
|
||||
* being GC'd. (Although it won't be marked if it's in the wrong compartment.)
|
||||
*/
|
||||
void
|
||||
MarkCrossCompartmentObject(JSTracer *trc, JSObject &obj, const char *name);
|
||||
|
||||
void
|
||||
MarkObjectWithPrinter(JSTracer *trc, JSObject &obj, JSTraceNamePrinter printer,
|
||||
const void *arg, size_t index);
|
||||
|
@ -102,6 +109,13 @@ MarkValueRaw(JSTracer *trc, const js::Value &v);
|
|||
void
|
||||
MarkValue(JSTracer *trc, const js::Value &v, const char *name);
|
||||
|
||||
/*
|
||||
* Mark a value that may be in a different compartment from the compartment
|
||||
* being GC'd. (Although it won't be marked if it's in the wrong compartment.)
|
||||
*/
|
||||
void
|
||||
MarkCrossCompartmentValue(JSTracer *trc, const js::Value &v, const char *name);
|
||||
|
||||
void
|
||||
MarkValueRange(JSTracer *trc, Value *beg, Value *end, const char *name);
|
||||
|
||||
|
|
|
@ -980,11 +980,11 @@ static void
|
|||
proxy_TraceObject(JSTracer *trc, JSObject *obj)
|
||||
{
|
||||
obj->getProxyHandler()->trace(trc, obj);
|
||||
MarkValue(trc, obj->getProxyPrivate(), "private");
|
||||
MarkValue(trc, obj->getProxyExtra(), "extra");
|
||||
MarkCrossCompartmentValue(trc, obj->getProxyPrivate(), "private");
|
||||
MarkCrossCompartmentValue(trc, obj->getProxyExtra(), "extra");
|
||||
if (obj->isFunctionProxy()) {
|
||||
MarkValue(trc, GetCall(obj), "call");
|
||||
MarkValue(trc, GetConstruct(obj), "construct");
|
||||
MarkCrossCompartmentValue(trc, GetCall(obj), "call");
|
||||
MarkCrossCompartmentValue(trc, GetConstruct(obj), "construct");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -992,8 +992,8 @@ static void
|
|||
proxy_TraceFunction(JSTracer *trc, JSObject *obj)
|
||||
{
|
||||
proxy_TraceObject(trc, obj);
|
||||
MarkValue(trc, GetCall(obj), "call");
|
||||
MarkValue(trc, GetConstruct(obj), "construct");
|
||||
MarkCrossCompartmentValue(trc, GetCall(obj), "call");
|
||||
MarkCrossCompartmentValue(trc, GetConstruct(obj), "construct");
|
||||
}
|
||||
|
||||
static JSBool
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#define jsutil_h___
|
||||
|
||||
#include "jstypes.h"
|
||||
#include "jscrashreport.h"
|
||||
#include "mozilla/Util.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -59,6 +60,8 @@ JS_BEGIN_EXTERN_C
|
|||
} \
|
||||
JS_END_MACRO
|
||||
|
||||
#define JS_FREE_PATTERN 0xDA
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#define JS_ASSERT(expr) \
|
||||
|
@ -80,8 +83,6 @@ JS_BEGIN_EXTERN_C
|
|||
# define JS_THREADSAFE_ASSERT(expr) ((void) 0)
|
||||
# endif
|
||||
|
||||
#define JS_FREE_PATTERN 0xDA
|
||||
|
||||
#else
|
||||
|
||||
#define JS_ASSERT(expr) ((void) 0)
|
||||
|
@ -220,6 +221,12 @@ extern JS_PUBLIC_DATA(JSUint32) OOM_counter; /* data race, who cares. */
|
|||
#define JS_OOM_POSSIBLY_FAIL() do {} while(0)
|
||||
#endif
|
||||
|
||||
static JS_INLINE void *js_record_oom(void *p) {
|
||||
if (!p)
|
||||
js_SnapshotErrorStack();
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* SpiderMonkey code should not be calling these allocation functions directly.
|
||||
* Instead, all calls should go through JSRuntime, JSContext or OffTheBooks.
|
||||
|
@ -227,17 +234,17 @@ extern JS_PUBLIC_DATA(JSUint32) OOM_counter; /* data race, who cares. */
|
|||
*/
|
||||
static JS_INLINE void* js_malloc(size_t bytes) {
|
||||
JS_OOM_POSSIBLY_FAIL();
|
||||
return malloc(bytes);
|
||||
return js_record_oom(malloc(bytes));
|
||||
}
|
||||
|
||||
static JS_INLINE void* js_calloc(size_t bytes) {
|
||||
JS_OOM_POSSIBLY_FAIL();
|
||||
return calloc(bytes, 1);
|
||||
return js_record_oom(calloc(bytes, 1));
|
||||
}
|
||||
|
||||
static JS_INLINE void* js_realloc(void* p, size_t bytes) {
|
||||
JS_OOM_POSSIBLY_FAIL();
|
||||
return realloc(p, bytes);
|
||||
return js_record_oom(realloc(p, bytes));
|
||||
}
|
||||
|
||||
static JS_INLINE void js_free(void* p) {
|
||||
|
|
|
@ -768,4 +768,10 @@ JSCrossCompartmentWrapper::defaultValue(JSContext *cx, JSObject *wrapper, JSType
|
|||
return call.origin->wrap(cx, vp);
|
||||
}
|
||||
|
||||
void
|
||||
JSCrossCompartmentWrapper::trace(JSTracer *trc, JSObject *wrapper)
|
||||
{
|
||||
MarkCrossCompartmentObject(trc, *wrappedObject(wrapper), "wrappedObject");
|
||||
}
|
||||
|
||||
JSCrossCompartmentWrapper JSCrossCompartmentWrapper::singleton(0u);
|
||||
|
|
|
@ -153,6 +153,8 @@ class JS_FRIEND_API(JSCrossCompartmentWrapper) : public JSWrapper {
|
|||
virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, uintN indent);
|
||||
virtual bool defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, js::Value *vp);
|
||||
|
||||
virtual void trace(JSTracer *trc, JSObject *wrapper);
|
||||
|
||||
static JSCrossCompartmentWrapper singleton;
|
||||
};
|
||||
|
||||
|
|
|
@ -876,7 +876,7 @@ customMethodCalls = {
|
|||
'thisType': 'nsGenericElement'
|
||||
},
|
||||
'nsIDOMElement_GetTagName': {
|
||||
'thisType': 'nsINode',
|
||||
'thisType': 'nsGenericElement',
|
||||
'code': 'nsString result = self->NodeName();',
|
||||
'canFail': False
|
||||
},
|
||||
|
|
|
@ -49,6 +49,10 @@
|
|||
#include "mozilla/FunctionTimer.h"
|
||||
#include "prsystem.h"
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
#include "nsExceptionHandler.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
/***************************************************************************/
|
||||
|
@ -1667,6 +1671,14 @@ NS_IMPL_THREADSAFE_ISUPPORTS1(
|
|||
, nsIMemoryMultiReporter
|
||||
)
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
static JSBool
|
||||
DiagnosticMemoryCallback(void *ptr, size_t size)
|
||||
{
|
||||
return CrashReporter::RegisterAppMemory(ptr, size) == NS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
|
||||
: mXPConnect(aXPConnect),
|
||||
mJSRuntime(nsnull),
|
||||
|
@ -1724,6 +1736,9 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
|
|||
JS_SetWrapObjectCallbacks(mJSRuntime,
|
||||
xpc::WrapperFactory::Rewrap,
|
||||
xpc::WrapperFactory::PrepareForWrapping);
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
JS_EnumerateDiagnosticMemoryRegions(DiagnosticMemoryCallback);
|
||||
#endif
|
||||
mWatchdogWakeup = JS_NEW_CONDVAR(mJSRuntime->gcLock);
|
||||
if (!mWatchdogWakeup)
|
||||
NS_RUNTIMEABORT("JS_NEW_CONDVAR failed.");
|
||||
|
|
|
@ -6629,7 +6629,7 @@ nsFrame::GetParentStyleContextFrame(nsPresContext* aPresContext,
|
|||
* is needed because the split inline's style context is the parent of the
|
||||
* anonymous block's style context.
|
||||
*
|
||||
* If aFrame is not ananonymous block, null is returned.
|
||||
* If aFrame is not an anonymous block, null is returned.
|
||||
*/
|
||||
static nsIFrame*
|
||||
GetIBSpecialSiblingForAnonymousBlock(nsIFrame* aFrame)
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
# include <windows.h> // for DebugBreak
|
||||
#elif defined(XP_UNIX)
|
||||
# include <unistd.h> // for _exit
|
||||
# include <signal.h>
|
||||
#endif
|
||||
|
||||
#if defined(XP_WIN) || defined(XP_OS2)
|
||||
|
@ -72,19 +73,39 @@ mozalloc_abort(const char* const msg)
|
|||
fputs(msg, stderr);
|
||||
fputs("\n", stderr);
|
||||
|
||||
#if defined(XP_UNIX) && !defined(XP_MACOSX)
|
||||
abort();
|
||||
#elif defined(_MSC_VER)
|
||||
#if defined(_MSC_VER)
|
||||
__debugbreak();
|
||||
#elif defined(XP_WIN)
|
||||
DebugBreak();
|
||||
#endif
|
||||
// abort() doesn't trigger breakpad on Mac, "fall through" to the
|
||||
// fail-safe code
|
||||
|
||||
// Still haven't aborted? Try dereferencing null.
|
||||
// On *NIX platforms the prefered way to abort is by touching bad memory,
|
||||
// since this generates a stack trace inside our own code (avoiding
|
||||
// problems with starting the trace inside libc, where we might not have
|
||||
// symbols and can get lost).
|
||||
|
||||
TouchBadMemory();
|
||||
|
||||
// If we haven't aborted yet, we can try to raise SIGABRT which might work
|
||||
// on some *NIXs, but not OS X (it doesn't trigger breakpad there).
|
||||
// Note that we don't call abort(), since raise is likelier to give us
|
||||
// useful stack data, and also since abort() is redirected to call this
|
||||
// function (see below).
|
||||
#if defined(XP_UNIX) && !defined(XP_MACOSX)
|
||||
raise(SIGABRT);
|
||||
#endif
|
||||
|
||||
// Still haven't aborted? Try _exit().
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
#if defined(XP_UNIX)
|
||||
// Define abort() here, so that it is used instead of the system abort(). This
|
||||
// lets us control the behavior when aborting, in order to get better results
|
||||
// on *NIX platfrorms. See mozalloc_abort for details.
|
||||
void abort(void)
|
||||
{
|
||||
mozalloc_abort("Redirecting call to abort() to mozalloc_abort\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -384,6 +384,10 @@ pref("content.sink.perf_parse_time", 50000000);
|
|||
|
||||
pref("javascript.options.mem.high_water_mark", 32);
|
||||
|
||||
// Disable the JS engine's gc on memory pressure, since we do one in the mobile
|
||||
// browser (bug 669346).
|
||||
pref("javascript.options.gc_on_memory_pressure", false);
|
||||
|
||||
pref("dom.max_chrome_script_run_time", 0); // disable slow script dialog for chrome
|
||||
pref("dom.max_script_run_time", 20);
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ XPCOMUtils.defineLazyGetter(this, "CommonUI", function() {
|
|||
|
||||
[
|
||||
["FullScreenVideo"],
|
||||
["WebappsUI"],
|
||||
["BadgeHandlers"],
|
||||
["ContextHelper"],
|
||||
["SelectionHelper"],
|
||||
|
|
|
@ -1038,6 +1038,21 @@ var BrowserUI = {
|
|||
return this._domWindowClose(browser);
|
||||
break;
|
||||
case "DOMLinkAdded":
|
||||
// checks for an icon to use for a web app
|
||||
// priority is : icon < apple-touch-icon
|
||||
let rel = json.rel.toLowerCase().split(" ");
|
||||
if ((rel.indexOf("icon") != -1) && !browser.appIcon) {
|
||||
// We should also use the sizes attribute if available
|
||||
// see http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#rel-icon
|
||||
browser.appIcon = json.href;
|
||||
}
|
||||
else if (rel.indexOf("apple-touch-icon") != -1) {
|
||||
// XXX should we support apple-touch-icon-precomposed ?
|
||||
// see http://developer.apple.com/safari/library/documentation/appleapplications/reference/safariwebcontent/configuringwebapplications/configuringwebapplications.html
|
||||
browser.appIcon = json.href;
|
||||
}
|
||||
|
||||
// Handle favicon changes
|
||||
if (Browser.selectedBrowser == browser)
|
||||
this._updateIcon(Browser.selectedBrowser.mIconURL);
|
||||
break;
|
||||
|
@ -1238,7 +1253,8 @@ var BrowserUI = {
|
|||
this.activePanel = RemoteTabsList;
|
||||
break;
|
||||
case "cmd_quit":
|
||||
GlobalOverlay.goQuitApplication();
|
||||
// Only close one window
|
||||
this._closeOrQuit();
|
||||
break;
|
||||
case "cmd_close":
|
||||
this._closeOrQuit();
|
||||
|
|
|
@ -378,13 +378,14 @@ var Browser = {
|
|||
messageManager.addMessageListener("Browser:CertException", this);
|
||||
messageManager.addMessageListener("Browser:BlockedSite", this);
|
||||
|
||||
// broadcast a UIReady message so add-ons know we are finished with startup
|
||||
// Broadcast a UIReady message so add-ons know we are finished with startup
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("UIReady", true, false);
|
||||
window.dispatchEvent(event);
|
||||
|
||||
// if we have an opener this was not the first window opened and will not
|
||||
// If we have an opener this was not the first window opened and will not
|
||||
// receive an initial resize event. instead we fire the resize handler manually
|
||||
// Bug 610834
|
||||
if (window.opener)
|
||||
resizeHandler({ target: window });
|
||||
},
|
||||
|
@ -941,7 +942,7 @@ var Browser = {
|
|||
function visibility(aSidebarRect, aVisibleRect) {
|
||||
let width = aSidebarRect.width;
|
||||
aSidebarRect.restrictTo(aVisibleRect);
|
||||
return (aSidebarRect.width ? aSidebarRect.width / width : 0);
|
||||
return (width ? aSidebarRect.width / width : 0);
|
||||
}
|
||||
|
||||
if (!dx) dx = 0;
|
||||
|
@ -1492,6 +1493,7 @@ Browser.WebProgress.prototype = {
|
|||
tab.hostChanged = true;
|
||||
tab.browser.lastLocation = location;
|
||||
tab.browser.userTypedValue = "";
|
||||
tab.browser.appIcon = null;
|
||||
|
||||
#ifdef MOZ_CRASH_REPORTER
|
||||
if (CrashReporter.enabled)
|
||||
|
@ -1576,6 +1578,8 @@ Browser.WebProgress.prototype = {
|
|||
};
|
||||
|
||||
|
||||
const OPEN_APPTAB = 100; // Hack until we get a real API
|
||||
|
||||
function nsBrowserAccess() { }
|
||||
|
||||
nsBrowserAccess.prototype = {
|
||||
|
@ -1618,13 +1622,31 @@ nsBrowserAccess.prototype = {
|
|||
tab.closeOnExit = true;
|
||||
browser = tab.browser;
|
||||
BrowserUI.hidePanel();
|
||||
} else if (aWhere == OPEN_APPTAB) {
|
||||
Browser.tabs.forEach(function(aTab) {
|
||||
if ("appURI" in aTab.browser && aTab.browser.appURI.spec == aURI.spec) {
|
||||
Browser.selectedTab = aTab;
|
||||
browser = aTab.browser;
|
||||
}
|
||||
});
|
||||
|
||||
if (!browser) {
|
||||
// Make a new tab to hold the app
|
||||
let tab = Browser.addTab("about:blank", true, null, { getAttention: true });
|
||||
browser = tab.browser;
|
||||
browser.appURI = aURI;
|
||||
} else {
|
||||
// Just use the existing browser, but return null to keep the system from trying to load the URI again
|
||||
browser = null;
|
||||
}
|
||||
BrowserUI.hidePanel();
|
||||
} else { // OPEN_CURRENTWINDOW and illegal values
|
||||
browser = Browser.selectedBrowser;
|
||||
}
|
||||
|
||||
try {
|
||||
let referrer;
|
||||
if (aURI) {
|
||||
if (aURI && browser) {
|
||||
if (aOpener) {
|
||||
location = aOpener.location;
|
||||
referrer = Services.io.newURI(location, null, null);
|
||||
|
@ -2518,8 +2540,16 @@ var ContentCrashObserver = {
|
|||
|
||||
var MemoryObserver = {
|
||||
observe: function mo_observe(aSubject, aTopic, aData) {
|
||||
function gc() {
|
||||
window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils).garbageCollect();
|
||||
Cu.forceGC();
|
||||
};
|
||||
|
||||
if (aData == "heap-minimize") {
|
||||
// do non-destructive stuff here.
|
||||
// The JS engine would normally GC on this notification, but since we
|
||||
// disabled that in favor of this method (bug 669346), we should gc here.
|
||||
gc();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2530,9 +2560,9 @@ var MemoryObserver = {
|
|||
tab.resurrect();
|
||||
}
|
||||
|
||||
window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils).garbageCollect();
|
||||
Cu.forceGC();
|
||||
// gc *after* throwing out the tabs so we can reclaim that memory.
|
||||
gc();
|
||||
|
||||
// Bug 637582 - The low memory condition throws out some stuff that we still
|
||||
// need, re-selecting the active tab gets us back to where we need to be.
|
||||
let sTab = Browser.selectedTab;
|
||||
|
|
|
@ -68,10 +68,9 @@
|
|||
title="&brandShortName;"
|
||||
#ifdef MOZ_PLATFORM_MAEMO
|
||||
sizemode="fullscreen"
|
||||
#else
|
||||
#endif
|
||||
width="480"
|
||||
height="800"
|
||||
#endif
|
||||
onkeypress="onDebugKeyPress(event);"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml">
|
||||
|
@ -349,6 +348,7 @@
|
|||
onclick="PageActions.clearPagePermissions(event);"/>
|
||||
<pageaction id="pageaction-search" title="&pageactions.search.addNew;"/>
|
||||
<pageaction id="pageaction-charset" title="&pageactions.charEncoding;" onclick="CharsetMenu.show();"/>
|
||||
<pageaction id="pageaction-webapps-install" title="&pageactions.webapps.install;" onclick="WebappsUI.show();"/>
|
||||
</hbox>
|
||||
</arrowbox>
|
||||
|
||||
|
|
|
@ -174,6 +174,7 @@ var PageActions = {
|
|||
#endif
|
||||
this.register("pageaction-share", this.updateShare, this);
|
||||
this.register("pageaction-search", BrowserSearch.updatePageSearchEngines, BrowserSearch);
|
||||
this.register("pageaction-webapps-install", WebappsUI.updateWebappsInstall, WebappsUI);
|
||||
},
|
||||
|
||||
handleEvent: function handleEvent(aEvent) {
|
||||
|
@ -1658,3 +1659,128 @@ var CharsetMenu = {
|
|||
}
|
||||
|
||||
};
|
||||
|
||||
var WebappsUI = {
|
||||
_dialog: null,
|
||||
_manifest: null,
|
||||
|
||||
checkBox: function(aEvent) {
|
||||
let elem = aEvent.originalTarget;
|
||||
let perm = elem.getAttribute("perm");
|
||||
if (this._manifest.capabilities && this._manifest.capabilities.indexOf(perm) != -1) {
|
||||
if (elem.checked) {
|
||||
elem.classList.remove("webapps-noperm");
|
||||
elem.classList.add("webapps-perm");
|
||||
} else {
|
||||
elem.classList.remove("webapps-perm");
|
||||
elem.classList.add("webapps-noperm");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
show: function show(aManifest) {
|
||||
if (!aManifest) {
|
||||
// Try every way to get an icon
|
||||
let browser = Browser.selectedBrowser;
|
||||
let icon = browser.appIcon;
|
||||
if (!icon)
|
||||
icon = browser.mIconURL;
|
||||
if (!icon)
|
||||
icon = gFaviconService.getFaviconImageForPage(browser.currentURI).spec;
|
||||
|
||||
// Create a simple manifest
|
||||
aManifest = {
|
||||
uri: browser.currentURI.spec,
|
||||
name: browser.contentTitle,
|
||||
icon: icon
|
||||
};
|
||||
}
|
||||
|
||||
this._manifest = aManifest;
|
||||
this._dialog = importDialog(window, "chrome://browser/content/webapps.xul", null);
|
||||
|
||||
if (aManifest.name)
|
||||
document.getElementById("webapps-title").value = aManifest.name;
|
||||
if (aManifest.icon)
|
||||
document.getElementById("webapps-icon").src = aManifest.icon;
|
||||
|
||||
let uri = Services.io.newURI(aManifest.uri, null, null);
|
||||
|
||||
let perms = [["offline", "offline-app"], ["geoloc", "geo"], ["notifications", "desktop-notifications"]];
|
||||
perms.forEach(function(tuple) {
|
||||
let elem = document.getElementById("webapps-" + tuple[0] + "-checkbox");
|
||||
let currentPerm = Services.perms.testExactPermission(uri, tuple[1]);
|
||||
if ((aManifest.capabilities && (aManifest.capabilities.indexOf(tuple[1]) != -1)) || (currentPerm == Ci.nsIPermissionManager.ALLOW_ACTION))
|
||||
elem.checked = true;
|
||||
else
|
||||
elem.checked = (currentPerm == Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
elem.classList.remove("webapps-noperm");
|
||||
elem.classList.add("webapps-perm");
|
||||
});
|
||||
|
||||
BrowserUI.pushPopup(this, this._dialog);
|
||||
|
||||
// Force a modal dialog
|
||||
this._dialog.waitForClose();
|
||||
},
|
||||
|
||||
hide: function hide() {
|
||||
this._dialog.close();
|
||||
this._dialog = null;
|
||||
BrowserUI.popPopup(this);
|
||||
},
|
||||
|
||||
_updatePermission: function updatePermission(aId, aPerm) {
|
||||
try {
|
||||
let uri = Services.io.newURI(this._manifest.uri, null, null);
|
||||
Services.perms.add(uri, aPerm, document.getElementById(aId).checked ? Ci.nsIPermissionManager.ALLOW_ACTION : Ci.nsIPermissionManager.DENY_ACTION);
|
||||
} catch(e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
},
|
||||
|
||||
launch: function launch() {
|
||||
let title = document.getElementById("webapps-title").value;
|
||||
if (!title)
|
||||
return;
|
||||
|
||||
this._updatePermission("webapps-offline-checkbox", "offline-app");
|
||||
this._updatePermission("webapps-geoloc-checkbox", "geo");
|
||||
this._updatePermission("webapps-notifications-checkbox", "desktop-notification");
|
||||
|
||||
this.hide();
|
||||
this.install(this._manifest.uri, title, this._manifest.icon);
|
||||
},
|
||||
|
||||
updateWebappsInstall: function updateWebappsInstall(aNode) {
|
||||
return !document.getElementById("main-window").hasAttribute("webapp");
|
||||
},
|
||||
|
||||
install: function(aURI, aTitle, aIcon) {
|
||||
const kIconSize = 64;
|
||||
|
||||
let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
||||
canvas.setAttribute("style", "display: none");
|
||||
|
||||
let self = this;
|
||||
let image = new Image();
|
||||
image.onload = function() {
|
||||
canvas.width = canvas.height = kIconSize; // clears the canvas
|
||||
let ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(image, 0, 0, kIconSize, kIconSize);
|
||||
let data = canvas.toDataURL("image/png", "");
|
||||
canvas = null;
|
||||
try {
|
||||
let webapp = Cc["@mozilla.org/webapps/support;1"].getService(Ci.nsIWebappsSupport);
|
||||
webapp.installApplication(aTitle, aURI, aIcon, data);
|
||||
} catch(e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
image.onerror = function() {
|
||||
// can't load the icon (bad URI) : fallback to the default one from chrome
|
||||
self.install(aURI, aTitle, "chrome://browser/skin/images/favicon-default-30.png");
|
||||
}
|
||||
image.src = aIcon;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- ***** 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 Mobile Browser.
|
||||
-
|
||||
- The Initial Developer of the Original Code is
|
||||
- Mozilla Corporation.
|
||||
- Portions created by the Initial Developer are Copyright (C) 2008
|
||||
- the Initial Developer. All Rights Reserved.
|
||||
-
|
||||
- Contributor(s):
|
||||
- Fabrice Desré <fabrice@mozilla.com>
|
||||
-
|
||||
- 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 LGPL or the GPL. 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 ***** -->
|
||||
<!DOCTYPE dialog [
|
||||
<!ENTITY % promptDTD SYSTEM "chrome://browser/locale/prompt.dtd">
|
||||
%promptDTD;
|
||||
<!ENTITY % webappsDTD SYSTEM "chrome://browser/locale/webapps.dtd">
|
||||
%webappsDTD;
|
||||
]>
|
||||
<dialog id="webapp-dialog" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<keyset>
|
||||
<key keycode="VK_ESCAPE" command="cmd_cancel"/>
|
||||
<key keycode="VK_RETURN" command="cmd_ok"/>
|
||||
</keyset>
|
||||
|
||||
<commandset>
|
||||
<command id="cmd_cancel" oncommand="WebappsUI.hide();"/>
|
||||
<command id="cmd_ok" oncommand="WebappsUI.launch();"/>
|
||||
</commandset>
|
||||
|
||||
<vbox class="prompt-title" id="webapps-title-box">
|
||||
<hbox align="center">
|
||||
<image id="webapps-icon"/>
|
||||
<vbox flex="1">
|
||||
<textbox id="webapps-title" placeholder="&webapps.title.placeholder;" flex="1"/>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</vbox>
|
||||
<separator class="prompt-line"/>
|
||||
<scrollbox class="prompt-message prompt-header" id="webapps-perm-box" orient="vertical" oncommand="WebappsUI.checkBox(event)" flex="1">
|
||||
<label crop="center" flex="1" value="&webapps.permissions;"/>
|
||||
<button id="webapps-geoloc-checkbox" perm="geo" type="checkbox" class="button-checkbox webapps-perm" flex="1">
|
||||
<image class="button-image-icon"/>
|
||||
<vbox flex="1">
|
||||
<description class="prompt-checkbox-label" flex="1">&webapps.perm.geolocation;</description>
|
||||
<description class="prompt-checkbox-label webapps-perm-requested-hint" id="webapps-geoloc-app">&webapps.perm.requested;</description>
|
||||
</vbox>
|
||||
</button>
|
||||
<button id="webapps-offline-checkbox" perm="offline-app" type="checkbox" class="button-checkbox webapps-perm" flex="1">
|
||||
<image class="button-image-icon"/>
|
||||
<vbox flex="1">
|
||||
<description class="prompt-checkbox-label" flex="1">&webapps.perm.offline;</description>
|
||||
<description class="prompt-checkbox-label webapps-perm-requested-hint" id="webapps-offline-app">&webapps.perm.requested;</description>
|
||||
</vbox>
|
||||
</button>
|
||||
<button id="webapps-notifications-checkbox" perm="desktop-notifications" type="checkbox" class="button-checkbox webapps-perm" flex="1">
|
||||
<image class="button-image-icon"/>
|
||||
<vbox flex="1">
|
||||
<description class="prompt-checkbox-label" flex="1">&webapps.perm.notifications;</description>
|
||||
<description class="prompt-checkbox-label webapps-perm-requested-hint" id="webapps-notifications-app">&webapps.perm.requested;</description>
|
||||
</vbox>
|
||||
</button>
|
||||
</scrollbox>
|
||||
<hbox pack="center" class="prompt-buttons">
|
||||
<button class="prompt-button" command="cmd_ok" label="&ok.label;"/>
|
||||
<separator/>
|
||||
<button class="prompt-button" command="cmd_cancel" label="&cancel.label;"/>
|
||||
</hbox>
|
||||
</dialog>
|
||||
|
|
@ -64,6 +64,7 @@ chrome.jar:
|
|||
content/prompt/select.xul (content/prompt/select.xul)
|
||||
content/prompt/prompt.js (content/prompt/prompt.js)
|
||||
content/share.xul (content/share.xul)
|
||||
content/webapps.xul (content/webapps.xul)
|
||||
content/AnimatedZoom.js (content/AnimatedZoom.js)
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
content/sync.js (content/sync.js)
|
||||
|
|
|
@ -44,12 +44,12 @@ Cu.import("resource://gre/modules/Services.jsm");
|
|||
|
||||
function openWindow(aParent, aURL, aTarget, aFeatures, aArgs) {
|
||||
let argString = null;
|
||||
if (aArgs) {
|
||||
if (aArgs && !(aArgs instanceof Ci.nsISupportsArray)) {
|
||||
argString = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
|
||||
argString.data = aArgs;
|
||||
}
|
||||
|
||||
return Services.ww.openWindow(aParent, aURL, aTarget, aFeatures, argString);
|
||||
return Services.ww.openWindow(aParent, aURL, aTarget, aFeatures, argString || aArgs);
|
||||
}
|
||||
|
||||
function resolveURIInternal(aCmdLine, aArgument) {
|
||||
|
@ -61,16 +61,14 @@ function resolveURIInternal(aCmdLine, aArgument) {
|
|||
try {
|
||||
if (uri.file.exists())
|
||||
return uri;
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
|
||||
try {
|
||||
let urifixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
|
||||
uri = urifixup.createFixupURI(aArgument, 0);
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
|
||||
|
@ -155,8 +153,7 @@ BrowserCLH.prototype = {
|
|||
// Stop the normal commandline processing from continuing
|
||||
aCmdLine.preventDefault = true;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
return;
|
||||
|
@ -165,6 +162,12 @@ BrowserCLH.prototype = {
|
|||
// Check and remove the alert flag here, but we'll handle it a bit later - see below
|
||||
let alertFlag = aCmdLine.handleFlagWithParam("alert", false);
|
||||
|
||||
// Check and remove the webapp param
|
||||
let appFlag = aCmdLine.handleFlagWithParam("webapp", false);
|
||||
let appURI;
|
||||
if (appFlag)
|
||||
appURI = resolveURIInternal(aCmdLine, appFlag);
|
||||
|
||||
// Keep an array of possible URL arguments
|
||||
let uris = [];
|
||||
|
||||
|
@ -187,16 +190,17 @@ BrowserCLH.prototype = {
|
|||
}
|
||||
|
||||
// Open the main browser window, if we don't already have one
|
||||
let win;
|
||||
let localePickerWin;
|
||||
let browserWin;
|
||||
try {
|
||||
win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
localePickerWin = Services.wm.getMostRecentWindow("navigator:localepicker");
|
||||
if (localePickerWin) {
|
||||
localePickerWin.focus();
|
||||
let localeWin = Services.wm.getMostRecentWindow("navigator:localepicker");
|
||||
if (localeWin) {
|
||||
localeWin.focus();
|
||||
aCmdLine.preventDefault = true;
|
||||
return;
|
||||
} else if (!win) {
|
||||
}
|
||||
|
||||
browserWin = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (!browserWin) {
|
||||
// Default to the saved homepage
|
||||
let defaultURL = getHomePage();
|
||||
|
||||
|
@ -208,30 +212,35 @@ BrowserCLH.prototype = {
|
|||
|
||||
// Show the locale selector if we have a new profile
|
||||
if (needHomepageOverride() == "new profile" && Services.prefs.getBoolPref("browser.firstrun.show.localepicker")) {
|
||||
win = openWindow(null, "chrome://browser/content/localePicker.xul", "_blank", "chrome,dialog=no,all", defaultURL);
|
||||
browserWin = openWindow(null, "chrome://browser/content/localePicker.xul", "_blank", "chrome,dialog=no,all", defaultURL);
|
||||
aCmdLine.preventDefault = true;
|
||||
return;
|
||||
}
|
||||
|
||||
win = openWindow(null, "chrome://browser/content/browser.xul", "_blank", "chrome,dialog=no,all", defaultURL);
|
||||
browserWin = openWindow(null, "chrome://browser/content/browser.xul", "_blank", "chrome,dialog=no,all", defaultURL);
|
||||
}
|
||||
|
||||
win.focus();
|
||||
browserWin.focus();
|
||||
|
||||
// Stop the normal commandline processing from continuing. We just opened the main browser window
|
||||
aCmdLine.preventDefault = true;
|
||||
} catch (e) { }
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
|
||||
// Assumption: All remaining command line arguments have been sent remotely (browser is already running)
|
||||
// Action: Open any URLs we find into an existing browser window
|
||||
|
||||
// First, get a browserDOMWindow object
|
||||
while (!win.browserDOMWindow)
|
||||
while (!browserWin.browserDOMWindow)
|
||||
Services.tm.currentThread.processNextEvent(true);
|
||||
|
||||
// Open any URIs into new tabs
|
||||
for (let i = 0; i < uris.length; i++)
|
||||
win.browserDOMWindow.openURI(uris[i], null, Ci.nsIBrowserDOMWindow.OPEN_NEWTAB, Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
|
||||
browserWin.browserDOMWindow.openURI(uris[i], null, Ci.nsIBrowserDOMWindow.OPEN_NEWTAB, Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
|
||||
|
||||
if (appURI)
|
||||
browserWin.browserDOMWindow.openURI(appURI, null, browserWin.OPEN_APPTAB, Ci.nsIBrowserDOMWindow.OPEN_NEW);
|
||||
|
||||
// Handle the notification, if called from it
|
||||
if (alertFlag) {
|
||||
|
@ -243,9 +252,9 @@ BrowserCLH.prototype = {
|
|||
var updateTimerCallback = updateService.QueryInterface(Ci.nsITimerCallback);
|
||||
updateTimerCallback.notify(null);
|
||||
} else if (alertFlag.length >= 9 && alertFlag.substr(0, 9) == "download:") {
|
||||
showPanelWhenReady(win, "downloads-container");
|
||||
showPanelWhenReady(browserWin, "downloads-container");
|
||||
} else if (alertFlag == "addons") {
|
||||
showPanelWhenReady(win, "addons-container");
|
||||
showPanelWhenReady(browserWin, "addons-container");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -75,6 +75,7 @@ EXTRA_COMPONENTS = \
|
|||
LoginManager.js \
|
||||
LoginManagerPrompter.js \
|
||||
BlocklistPrompt.js \
|
||||
WebappsSupport.js \
|
||||
$(NULL)
|
||||
|
||||
ifdef MOZ_SAFE_BROWSING
|
||||
|
|
|
@ -120,3 +120,7 @@ category app-startup SafeBrowsing service,@mozilla.org/safebrowsing/application;
|
|||
component {88b3eb21-d072-4e3b-886d-f89d8c49fe59} UpdatePrompt.js
|
||||
contract @mozilla.org/updates/update-prompt;1 {88b3eb21-d072-4e3b-886d-f89d8c49fe59}
|
||||
#endif
|
||||
|
||||
# webapps
|
||||
component {cb1107c1-1e15-4f11-99c8-27b9ec221a2a} WebappsSupport.js
|
||||
contract @mozilla.org/webapps/support;1 {cb1107c1-1e15-4f11-99c8-27b9ec221a2a}
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
/* ***** 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 Mobile Browser.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Fabrice Desré <fabrice@mozilla.com>
|
||||
* Mark Finkle <mfinkle@mozilla.com>
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const DB_VERSION = 1;
|
||||
|
||||
function WebappsSupport() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
WebappsSupport.prototype = {
|
||||
db: null,
|
||||
|
||||
init: function() {
|
||||
let file = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties).get("ProfD", Ci.nsIFile);
|
||||
file.append("webapps.sqlite");
|
||||
this.db = Services.storage.openDatabase(file);
|
||||
let version = this.db.schemaVersion;
|
||||
|
||||
if (version == 0) {
|
||||
this.db.executeSimpleSQL("CREATE TABLE webapps (title TEXT, uri TEXT PRIMARY KEY, icon TEXT)");
|
||||
this.db.schemaVersion = DB_VERSION;
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "_installQuery", function() {
|
||||
return this.db.createAsyncStatement("INSERT INTO webapps (title, uri, icon) VALUES(:title, :uri, :icon)");
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "_findQuery", function() {
|
||||
return this.db.createStatement("SELECT uri FROM webapps where uri = :uri");
|
||||
});
|
||||
|
||||
Services.obs.addObserver(this, "quit-application-granted", false);
|
||||
},
|
||||
|
||||
// entry point
|
||||
installApplication: function(aTitle, aURI, aIconURI, aIconData) {
|
||||
let stmt = this._installQuery;
|
||||
stmt.params.title = aTitle;
|
||||
stmt.params.uri = aURI;
|
||||
stmt.params.icon = aIconData;
|
||||
stmt.executeAsync();
|
||||
},
|
||||
|
||||
isApplicationInstalled: function(aURI) {
|
||||
let stmt = this._findQuery;
|
||||
let found = false;
|
||||
try {
|
||||
stmt.params.uri = aURI;
|
||||
found = stmt.executeStep();
|
||||
} finally {
|
||||
stmt.reset();
|
||||
}
|
||||
return found;
|
||||
},
|
||||
|
||||
// nsIObserver
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
Services.obs.removeObserver(this, "quit-application-granted");
|
||||
|
||||
// Finalize the statements that we have used
|
||||
let stmts = [
|
||||
"_installQuery",
|
||||
"_findQuery"
|
||||
];
|
||||
for (let i = 0; i < stmts.length; i++) {
|
||||
// We do not want to create any query we haven't already created, so
|
||||
// see if it is a getter first.
|
||||
if (Object.getOwnPropertyDescriptor(this, stmts[i]).value !== undefined) {
|
||||
this[stmts[i]].finalize();
|
||||
}
|
||||
}
|
||||
|
||||
this.db.asyncClose();
|
||||
},
|
||||
|
||||
// QI
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebappsSupport]),
|
||||
|
||||
// XPCOMUtils factory
|
||||
classID: Components.ID("{cb1107c1-1e15-4f11-99c8-27b9ec221a2a}")
|
||||
};
|
||||
|
||||
const NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsSupport]);
|
||||
|
|
@ -275,6 +275,7 @@
|
|||
@BINPATH@/components/xuldoc.xpt
|
||||
@BINPATH@/components/xultmpl.xpt
|
||||
@BINPATH@/components/zipwriter.xpt
|
||||
@BINPATH@/components/webapps.xpt
|
||||
|
||||
; JavaScript components
|
||||
@BINPATH@/components/ConsoleAPI.manifest
|
||||
|
@ -330,6 +331,7 @@
|
|||
@BINPATH@/components/amContentHandler.js
|
||||
@BINPATH@/components/amWebInstallListener.js
|
||||
@BINPATH@/components/nsBlocklistService.js
|
||||
|
||||
#ifdef MOZ_UPDATER
|
||||
@BINPATH@/components/nsUpdateService.manifest
|
||||
@BINPATH@/components/nsUpdateService.js
|
||||
|
@ -610,6 +612,7 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
|
|||
#ifdef MOZ_UPDATER
|
||||
@BINPATH@/components/UpdatePrompt.js
|
||||
#endif
|
||||
@BINPATH@/components/WebappsSupport.js
|
||||
@BINPATH@/components/XPIDialogService.js
|
||||
@BINPATH@/components/browsercomps.xpt
|
||||
@BINPATH@/extensions/feedback@mobile.mozilla.org.xpi
|
||||
|
|
|
@ -111,5 +111,6 @@
|
|||
<!ENTITY pageactions.findInPage "Find In Page">
|
||||
<!ENTITY pageactions.search.addNew "Add Search Engine">
|
||||
<!ENTITY pageactions.charEncoding "Character Encoding">
|
||||
<!ENTITY pageactions.webapps.install "Install as App">
|
||||
|
||||
<!ENTITY appMenu.siteOptions "Site Options">
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<!ENTITY webapps.title.placeholder "Enter a title">
|
||||
<!ENTITY webapps.permissions "Allow access:">
|
||||
<!ENTITY webapps.perm.geolocation "Location-aware browsing">
|
||||
<!ENTITY webapps.perm.offline "Offline data storage">
|
||||
<!ENTITY webapps.perm.notifications "Desktop notifications">
|
||||
<!ENTITY webapps.perm.requested "requested">
|
|
@ -16,6 +16,7 @@
|
|||
locale/@AB_CD@/browser/sync.dtd (%chrome/sync.dtd)
|
||||
locale/@AB_CD@/browser/sync.properties (%chrome/sync.properties)
|
||||
locale/@AB_CD@/browser/prompt.dtd (%chrome/prompt.dtd)
|
||||
locale/@AB_CD@/browser/webapps.dtd (%chrome/webapps.dtd)
|
||||
locale/@AB_CD@/browser/feedback.dtd (%chrome/feedback.dtd)
|
||||
locale/@AB_CD@/browser/phishing.dtd (%chrome/phishing.dtd)
|
||||
locale/@AB_CD@/browser/bookmarks.json (bookmarks.json)
|
||||
|
|
|
@ -1329,6 +1329,25 @@ setting {
|
|||
z-index: 500;
|
||||
}
|
||||
|
||||
/* openwebapps capabilities ------------------------------------------------------------ */
|
||||
.webapps-noperm description.webapps-perm-requested-hint {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.webapps-perm description.webapps-perm-requested-hint {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#webapps-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin: @margin_normal@;
|
||||
}
|
||||
|
||||
#webapps-title {
|
||||
-moz-margin-end: @margin_normal@;
|
||||
}
|
||||
|
||||
/* Android menu ------------------------------------------------------------ */
|
||||
#appmenu {
|
||||
background: rgba(255,255,255,0.95);
|
||||
|
|
|
@ -1311,6 +1311,25 @@ setting {
|
|||
z-index: 500;
|
||||
}
|
||||
|
||||
/* openwebapps capabilities ------------------------------------------------------------ */
|
||||
.webapps-noperm description.webapps-perm-requested-hint {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.webapps-perm description.webapps-perm-requested-hint {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#webapps-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin: @margin_normal@;
|
||||
}
|
||||
|
||||
#webapps-title {
|
||||
-moz-margin-end: @margin_normal@;
|
||||
}
|
||||
|
||||
/* Android menu ------------------------------------------------------------ */
|
||||
#appmenu {
|
||||
background: @color_background_default@;
|
||||
|
|
|
@ -287,12 +287,13 @@ toolbarbutton[open="true"] {
|
|||
list-style-image: url("chrome://browser/skin/images/check-selected-hdpi.png");
|
||||
}
|
||||
|
||||
.button-checkbox > .button-box,
|
||||
.button-checkbox:hover:active > .button-box,
|
||||
.button-checkbox[checked="true"] > .button-box {
|
||||
padding-top: @padding_tiny@;
|
||||
padding-bottom: @padding_xsmall@;
|
||||
-moz-padding-start: @margin_small@;
|
||||
-moz-padding-end: @margin_small@;
|
||||
-moz-padding-start: @padding_small@;
|
||||
-moz-padding-end: @padding_small@;
|
||||
}
|
||||
|
||||
/* radio buttons ----------------------------------------------------------- */
|
||||
|
|
|
@ -158,30 +158,30 @@ public:
|
|||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD GetProcess(char **process)
|
||||
NS_IMETHOD GetProcess(nsACString &process)
|
||||
{
|
||||
*process = strdup("");
|
||||
process.Truncate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetPath(char **memoryPath)
|
||||
NS_IMETHOD GetPath(nsACString &path)
|
||||
{
|
||||
if (mType == ChromeUsedRaw) {
|
||||
*memoryPath = strdup("explicit/images/chrome/used/raw");
|
||||
path.AssignLiteral("explicit/images/chrome/used/raw");
|
||||
} else if (mType == ChromeUsedUncompressed) {
|
||||
*memoryPath = strdup("explicit/images/chrome/used/uncompressed");
|
||||
path.AssignLiteral("explicit/images/chrome/used/uncompressed");
|
||||
} else if (mType == ChromeUnusedRaw) {
|
||||
*memoryPath = strdup("explicit/images/chrome/unused/raw");
|
||||
path.AssignLiteral("explicit/images/chrome/unused/raw");
|
||||
} else if (mType == ChromeUnusedUncompressed) {
|
||||
*memoryPath = strdup("explicit/images/chrome/unused/uncompressed");
|
||||
path.AssignLiteral("explicit/images/chrome/unused/uncompressed");
|
||||
} else if (mType == ContentUsedRaw) {
|
||||
*memoryPath = strdup("explicit/images/content/used/raw");
|
||||
path.AssignLiteral("explicit/images/content/used/raw");
|
||||
} else if (mType == ContentUsedUncompressed) {
|
||||
*memoryPath = strdup("explicit/images/content/used/uncompressed");
|
||||
path.AssignLiteral("explicit/images/content/used/uncompressed");
|
||||
} else if (mType == ContentUnusedRaw) {
|
||||
*memoryPath = strdup("explicit/images/content/unused/raw");
|
||||
path.AssignLiteral("explicit/images/content/unused/raw");
|
||||
} else if (mType == ContentUnusedUncompressed) {
|
||||
*memoryPath = strdup("explicit/images/content/unused/uncompressed");
|
||||
path.AssignLiteral("explicit/images/content/unused/uncompressed");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -249,24 +249,24 @@ public:
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetDescription(char **desc)
|
||||
NS_IMETHOD GetDescription(nsACString &desc)
|
||||
{
|
||||
if (mType == ChromeUsedRaw) {
|
||||
*desc = strdup("Memory used by in-use chrome images (compressed data).");
|
||||
desc.AssignLiteral("Memory used by in-use chrome images (compressed data).");
|
||||
} else if (mType == ChromeUsedUncompressed) {
|
||||
*desc = strdup("Memory used by in-use chrome images (uncompressed data).");
|
||||
desc.AssignLiteral("Memory used by in-use chrome images (uncompressed data).");
|
||||
} else if (mType == ChromeUnusedRaw) {
|
||||
*desc = strdup("Memory used by not in-use chrome images (compressed data).");
|
||||
desc.AssignLiteral("Memory used by not in-use chrome images (compressed data).");
|
||||
} else if (mType == ChromeUnusedUncompressed) {
|
||||
*desc = strdup("Memory used by not in-use chrome images (uncompressed data).");
|
||||
desc.AssignLiteral("Memory used by not in-use chrome images (uncompressed data).");
|
||||
} else if (mType == ContentUsedRaw) {
|
||||
*desc = strdup("Memory used by in-use content images (compressed data).");
|
||||
desc.AssignLiteral("Memory used by in-use content images (compressed data).");
|
||||
} else if (mType == ContentUsedUncompressed) {
|
||||
*desc = strdup("Memory used by in-use content images (uncompressed data).");
|
||||
desc.AssignLiteral("Memory used by in-use content images (uncompressed data).");
|
||||
} else if (mType == ContentUnusedRaw) {
|
||||
*desc = strdup("Memory used by not in-use content images (compressed data).");
|
||||
desc.AssignLiteral("Memory used by not in-use content images (compressed data).");
|
||||
} else if (mType == ContentUnusedUncompressed) {
|
||||
*desc = strdup("Memory used by not in-use content images (uncompressed data).");
|
||||
desc.AssignLiteral("Memory used by not in-use content images (uncompressed data).");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -628,6 +628,7 @@ pref("javascript.options.mem.high_water_mark", 128);
|
|||
pref("javascript.options.mem.max", -1);
|
||||
pref("javascript.options.mem.gc_per_compartment", true);
|
||||
pref("javascript.options.mem.log", false);
|
||||
pref("javascript.options.gc_on_memory_pressure", true);
|
||||
|
||||
// advanced prefs
|
||||
pref("advanced.mailftp", false);
|
||||
|
|
|
@ -649,8 +649,12 @@ nsHttpChannel::CallOnStartRequest()
|
|||
mTracingEnabled = PR_FALSE;
|
||||
|
||||
if (mResponseHead && mResponseHead->ContentType().IsEmpty()) {
|
||||
NS_ASSERTION(mConnectionInfo, "Should have connection info here");
|
||||
if (!mContentTypeHint.IsEmpty())
|
||||
mResponseHead->SetContentType(mContentTypeHint);
|
||||
else if (mResponseHead->Version() == NS_HTTP_VERSION_0_9 &&
|
||||
mConnectionInfo->Port() != mConnectionInfo->DefaultPort())
|
||||
mResponseHead->SetContentType(NS_LITERAL_CSTRING(TEXT_PLAIN));
|
||||
else {
|
||||
// Uh-oh. We had better find out what type we are!
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
|
@ -568,6 +568,12 @@ WebSocketChannel::~WebSocketChannel()
|
|||
mContext.forget(&forgettableContext);
|
||||
NS_ProxyRelease(mainThread, forgettableContext, PR_FALSE);
|
||||
}
|
||||
|
||||
if (mLoadGroup) {
|
||||
nsILoadGroup *forgettableGroup;
|
||||
mLoadGroup.forget(&forgettableGroup);
|
||||
NS_ProxyRelease(mainThread, forgettableGroup, PR_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
|
@ -209,7 +209,6 @@ private:
|
|||
nsCOMPtr<nsIEventTarget> mSocketThread;
|
||||
nsCOMPtr<nsIHttpChannelInternal> mChannel;
|
||||
nsCOMPtr<nsIHttpChannel> mHttpChannel;
|
||||
nsCOMPtr<nsILoadGroup> mLoadGroup;
|
||||
nsCOMPtr<nsICancelable> mDNSRequest;
|
||||
nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
|
||||
nsCOMPtr<nsIRandomGenerator> mRandomGenerator;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set sw=4 ts=4 et tw=80 : */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set sw=4 ts=4 et tw=80 : */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
do_load_httpd_js();
|
||||
|
||||
var httpserver = null;
|
||||
var simplePath = "/simple";
|
||||
var normalPath = "/normal";
|
||||
var httpbody = "<html></html>";
|
||||
var uri1 = "http://localhost:4444" + simplePath;
|
||||
var uri2 = "http://localhost:4444" + normalPath;
|
||||
|
||||
function make_channel(url) {
|
||||
var ios = Cc["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService);
|
||||
return ios.newChannel(url, "", null);
|
||||
}
|
||||
|
||||
var listener_proto = {
|
||||
QueryInterface: function(iid) {
|
||||
if (iid.equals(Components.interfaces.nsIStreamListener) ||
|
||||
iid.equals(Components.interfaces.nsIRequestObserver) ||
|
||||
iid.equals(Components.interfaces.nsISupports))
|
||||
return this;
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
|
||||
onStartRequest: function(request, context) {
|
||||
do_check_eq(request.QueryInterface(Ci.nsIChannel).contentType,
|
||||
this.contentType);
|
||||
request.cancel(Cr.NS_BINDING_ABORTED);
|
||||
},
|
||||
|
||||
onDataAvailable: function(request, context, stream, offset, count) {
|
||||
do_throw("Unexpected onDataAvailable");
|
||||
},
|
||||
|
||||
onStopRequest: function(request, context, status) {
|
||||
do_check_eq(status, Cr.NS_BINDING_ABORTED);
|
||||
this.termination_func();
|
||||
}
|
||||
};
|
||||
|
||||
function listener(contentType, termination_func) {
|
||||
this.contentType = contentType;
|
||||
this.termination_func = termination_func;
|
||||
}
|
||||
listener.prototype = listener_proto;
|
||||
|
||||
function run_test()
|
||||
{
|
||||
httpserver = new nsHttpServer();
|
||||
httpserver.registerPathHandler(simplePath, simpleHandler);
|
||||
httpserver.registerPathHandler(normalPath, normalHandler);
|
||||
httpserver.start(4444);
|
||||
|
||||
var channel = make_channel(uri1);
|
||||
channel.asyncOpen(new listener("text/plain", function() {
|
||||
run_test2();
|
||||
}), null);
|
||||
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function run_test2()
|
||||
{
|
||||
var channel = make_channel(uri2);
|
||||
channel.asyncOpen(new listener("text/html", function() {
|
||||
httpserver.stop(do_test_finished);
|
||||
}), null);
|
||||
}
|
||||
|
||||
function simpleHandler(metadata, response)
|
||||
{
|
||||
response.seizePower();
|
||||
response.bodyOutputStream.write(httpbody, httpbody.length);
|
||||
response.finish();
|
||||
}
|
||||
|
||||
function normalHandler(metadata, response)
|
||||
{
|
||||
response.bodyOutputStream.write(httpbody, httpbody.length);
|
||||
response.finish();
|
||||
}
|
|
@ -68,6 +68,7 @@ tail =
|
|||
[test_bug659569.js]
|
||||
[test_bug660066.js]
|
||||
[test_bug651185.js]
|
||||
[test_bug667907.js]
|
||||
[test_cacheflags.js]
|
||||
[test_channel_close.js]
|
||||
[test_compareURIs.js]
|
||||
|
|
|
@ -348,17 +348,15 @@ public:
|
|||
}
|
||||
|
||||
|
||||
NS_IMETHOD GetProcess(char **process)
|
||||
NS_IMETHOD GetProcess(nsACString &process)
|
||||
{
|
||||
*process = strdup("");
|
||||
process.Truncate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetPath(char **memoryPath)
|
||||
NS_IMETHOD GetPath(nsACString &path)
|
||||
{
|
||||
nsCString path;
|
||||
|
||||
path.AppendLiteral("explicit/storage/sqlite/");
|
||||
path.AssignLiteral("explicit/storage/sqlite/");
|
||||
path.Append(mDBConn.getFilename());
|
||||
|
||||
if (mType == Cache_Used) {
|
||||
|
@ -370,8 +368,6 @@ public:
|
|||
else if (mType == Stmt_Used) {
|
||||
path.AppendLiteral("/stmt-used");
|
||||
}
|
||||
|
||||
*memoryPath = ::ToNewCString(path);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -406,17 +402,17 @@ public:
|
|||
return convertResultCode(rc);
|
||||
}
|
||||
|
||||
NS_IMETHOD GetDescription(char **desc)
|
||||
NS_IMETHOD GetDescription(nsACString &desc)
|
||||
{
|
||||
if (mType == Cache_Used) {
|
||||
*desc = ::strdup("Memory (approximate) used by all pager caches.");
|
||||
desc.AssignLiteral("Memory (approximate) used by all pager caches.");
|
||||
}
|
||||
else if (mType == Schema_Used) {
|
||||
*desc = ::strdup("Memory (approximate) used to store the schema "
|
||||
"for all databases associated with the connection");
|
||||
desc.AssignLiteral("Memory (approximate) used to store the schema "
|
||||
"for all databases associated with the connection");
|
||||
}
|
||||
else if (mType == Stmt_Used) {
|
||||
*desc = ::strdup("Memory (approximate) used by all prepared statements");
|
||||
desc.AssignLiteral("Memory (approximate) used by all prepared statements");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -136,7 +136,7 @@ class RemoteOptions(MochitestOptions):
|
|||
return None
|
||||
|
||||
if (options.remoteLogFile == None):
|
||||
options.remoteLogFile = options.remoteTestRoot + '/mochitest.log'
|
||||
options.remoteLogFile = options.remoteTestRoot + '/logs/mochitest.log'
|
||||
|
||||
if (options.remoteLogFile.count('/') < 1):
|
||||
options.remoteLogFile = options.remoteTestRoot + '/' + options.remoteLogFile
|
||||
|
@ -336,6 +336,8 @@ def main():
|
|||
if (options == None):
|
||||
sys.exit(1)
|
||||
|
||||
logParent = os.path.dirname(options.remoteLogFile)
|
||||
dm.mkDir(logParent);
|
||||
auto.setRemoteLog(options.remoteLogFile)
|
||||
auto.setServerInfo(options.webServer, options.httpPort, options.sslPort)
|
||||
|
||||
|
|
|
@ -61,6 +61,13 @@ RUN_MOCHITEST = \
|
|||
--console-level=INFO --log-file=./$@.log --file-level=INFO \
|
||||
$(SYMBOLS_PATH) $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS)
|
||||
|
||||
RUN_MOCHITEST_REMOTE = \
|
||||
rm -f ./$@.log && \
|
||||
$(PYTHON) _tests/testing/mochitest/runtestsremote.py --autorun --close-when-done \
|
||||
--console-level=INFO --log-file=./$@.log --file-level=INFO $(DM_FLAGS) \
|
||||
--app=$(ANDROID_PACKAGE_NAME) --deviceIP=${TEST_DEVICE} --xre-path=${MOZ_HOST_BIN} \
|
||||
$(SYMBOLS_PATH) $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS)
|
||||
|
||||
ifndef NO_FAIL_ON_TEST_ERRORS
|
||||
define CHECK_TEST_ERROR
|
||||
@errors=`grep "TEST-UNEXPECTED-" $@.log` ;\
|
||||
|
@ -74,6 +81,13 @@ define CHECK_TEST_ERROR
|
|||
endef
|
||||
endif
|
||||
|
||||
mochitest-remote:
|
||||
if test -f ${MOZ_HOST_BIN}/xpcshell && [ "${TEST_DEVICE}" != "" ]; \
|
||||
then $(RUN_MOCHITEST_REMOTE); \
|
||||
else \
|
||||
@echo "please prepare your host with environment variables for TEST_DEVICE and MOZ_HOST_BIN"; \
|
||||
fi
|
||||
|
||||
mochitest-plain:
|
||||
$(RUN_MOCHITEST)
|
||||
$(CHECK_TEST_ERROR)
|
||||
|
|
|
@ -190,8 +190,6 @@ _BROWSER_TEST_PAGES = \
|
|||
test-bug-595934-malformedxml-external.xml \
|
||||
test-bug-595934-empty-getelementbyid.html \
|
||||
test-bug-595934-empty-getelementbyid.js \
|
||||
test-bug-595934-getselection.html \
|
||||
test-bug-595934-getselection.js \
|
||||
test-bug-595934-image.html \
|
||||
test-bug-595934-image.jpg \
|
||||
test-bug-597136-external-script-errors.html \
|
||||
|
|
|
@ -89,11 +89,6 @@ const TESTS = [
|
|||
category: "CSS Parser",
|
||||
matchString: "foobarCanvasCssParser",
|
||||
},
|
||||
{ // #15
|
||||
file: "test-bug-595934-getselection.html",
|
||||
category: "content javascript",
|
||||
matchString: "getSelection",
|
||||
},
|
||||
{ // #16
|
||||
file: "test-bug-595934-image.html",
|
||||
category: "Image",
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Web Console test for bug 595934 - category: content javascript.
|
||||
(getSelection())</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<script type="text/javascript"
|
||||
src="test-bug-595934-getselection.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p>Web Console test for bug 595934 - category "content javascript"
|
||||
(getSelection()).</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
window.addEventListener("load", function() {
|
||||
document.getSelection();
|
||||
}, false);
|
||||
|
|
@ -445,7 +445,8 @@ bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
|
|||
crashing_process,
|
||||
context,
|
||||
context_size,
|
||||
mapping_info_);
|
||||
mapping_info_,
|
||||
app_memory_info_);
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -539,4 +540,19 @@ void ExceptionHandler::AddMappingInfo(const std::string& name,
|
|||
mapping_info_.push_back(mapping);
|
||||
}
|
||||
|
||||
void ExceptionHandler::RegisterAppMemory(void *ptr, size_t length) {
|
||||
app_memory_info_.push_back(AppMemory(ptr, length));
|
||||
}
|
||||
|
||||
void ExceptionHandler::UnregisterAppMemory(void *ptr) {
|
||||
for (AppMemoryList::iterator iter = app_memory_info_.begin();
|
||||
iter != app_memory_info_.end();
|
||||
++iter) {
|
||||
if (iter->ptr == ptr) {
|
||||
app_memory_info_.erase(iter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
|
|
@ -219,6 +219,11 @@ class ExceptionHandler {
|
|||
size_t mapping_size,
|
||||
size_t file_offset);
|
||||
|
||||
// Calling RegisterAppMemory(p, len) causes len bytes starting
|
||||
// at address p to be copied to the minidump when a crash happens.
|
||||
void RegisterAppMemory(void *ptr, size_t length);
|
||||
void UnregisterAppMemory(void *ptr);
|
||||
|
||||
private:
|
||||
void Init(const std::string &dump_path,
|
||||
const int server_fd);
|
||||
|
@ -278,6 +283,10 @@ class ExceptionHandler {
|
|||
// Callers can add extra info about mappings for cases where the
|
||||
// dumper code cannot extract enough information from /proc/<pid>/maps.
|
||||
MappingList mapping_info_;
|
||||
|
||||
// Callers can request additional memory regions to be included in
|
||||
// the dump.
|
||||
AppMemoryList app_memory_info_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
|
|
@ -418,7 +418,8 @@ class MinidumpWriter {
|
|||
MinidumpWriter(const char* filename,
|
||||
pid_t crashing_pid,
|
||||
const ExceptionHandler::CrashContext* context,
|
||||
const MappingList& mappings)
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appmem)
|
||||
: filename_(filename),
|
||||
siginfo_(&context->siginfo),
|
||||
ucontext_(&context->context),
|
||||
|
@ -432,14 +433,16 @@ class MinidumpWriter {
|
|||
crashing_tid_pc_(0),
|
||||
dumper_(crashing_pid),
|
||||
memory_blocks_(dumper_.allocator()),
|
||||
mapping_info_(mappings) {
|
||||
mapping_info_(mappings),
|
||||
app_memory_info_(appmem) {
|
||||
}
|
||||
|
||||
// case (2) above
|
||||
MinidumpWriter(const char* filename,
|
||||
pid_t pid,
|
||||
pid_t blame_thread,
|
||||
const MappingList& mappings)
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appmem)
|
||||
: filename_(filename),
|
||||
siginfo_(NULL), // we fill this in if we find blame_thread
|
||||
ucontext_(NULL),
|
||||
|
@ -448,7 +451,8 @@ class MinidumpWriter {
|
|||
crashing_tid_pc_(0), // set if we find blame_thread
|
||||
dumper_(pid),
|
||||
memory_blocks_(dumper_.allocator()),
|
||||
mapping_info_(mappings) {
|
||||
mapping_info_(mappings),
|
||||
app_memory_info_(appmem) {
|
||||
}
|
||||
|
||||
bool Init() {
|
||||
|
@ -515,6 +519,9 @@ class MinidumpWriter {
|
|||
return false;
|
||||
dir.CopyIndex(dir_index++, &dirent);
|
||||
|
||||
if (!WriteAppMemory())
|
||||
return false;
|
||||
|
||||
if (!WriteMemoryListStream(&dirent))
|
||||
return false;
|
||||
dir.CopyIndex(dir_index++, &dirent);
|
||||
|
@ -824,6 +831,28 @@ class MinidumpWriter {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool WriteAppMemory() {
|
||||
for (AppMemoryList::const_iterator iter = app_memory_info_.begin();
|
||||
iter != app_memory_info_.end();
|
||||
++iter) {
|
||||
uint8_t* data_copy =
|
||||
(uint8_t*) dumper_.allocator()->Alloc(iter->length);
|
||||
dumper_.CopyFromProcess(data_copy, crashing_tid_, iter->ptr,
|
||||
iter->length);
|
||||
|
||||
UntypedMDRVA memory(&minidump_writer_);
|
||||
if (!memory.Allocate(iter->length))
|
||||
return false;
|
||||
memory.Copy(data_copy, iter->length);
|
||||
MDMemoryDescriptor desc;
|
||||
desc.start_of_memory_range = (uintptr_t)iter->ptr;
|
||||
desc.memory = memory.location();
|
||||
memory_blocks_.push_back(desc);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ShouldIncludeMapping(const MappingInfo& mapping) {
|
||||
if (mapping.name[0] == 0 || // we only want modules with filenames.
|
||||
mapping.offset || // we only want to include one mapping per shared lib.
|
||||
|
@ -1362,22 +1391,27 @@ popline:
|
|||
wasteful_vector<MDMemoryDescriptor> memory_blocks_;
|
||||
// Additional information about some mappings provided by the caller.
|
||||
const MappingList& mapping_info_;
|
||||
// Callers can request additional memory regions to be included in
|
||||
// the dump.
|
||||
const AppMemoryList& app_memory_info_;
|
||||
};
|
||||
|
||||
bool WriteMinidump(const char* filename, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size) {
|
||||
MappingList m;
|
||||
return WriteMinidump(filename, crashing_process, blob, blob_size, m);
|
||||
AppMemoryList a;
|
||||
return WriteMinidump(filename, crashing_process, blob, blob_size, m, a);
|
||||
}
|
||||
|
||||
bool WriteMinidump(const char* filename, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings) {
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appmem) {
|
||||
if (blob_size != sizeof(ExceptionHandler::CrashContext))
|
||||
return false;
|
||||
const ExceptionHandler::CrashContext* context =
|
||||
reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
|
||||
MinidumpWriter writer(filename, crashing_process, context, mappings);
|
||||
MinidumpWriter writer(filename, crashing_process, context, mappings, appmem);
|
||||
if (!writer.Init())
|
||||
return false;
|
||||
return writer.Dump();
|
||||
|
@ -1387,7 +1421,8 @@ bool WriteMinidump(const char* filename, pid_t process,
|
|||
pid_t process_blamed_thread) {
|
||||
//TODO: support mappings here
|
||||
MappingList m;
|
||||
MinidumpWriter writer(filename, process, process_blamed_thread, m);
|
||||
AppMemoryList a;
|
||||
MinidumpWriter writer(filename, process, process_blamed_thread, m, a);
|
||||
if (!writer.Init())
|
||||
return false;
|
||||
return writer.Dump();
|
||||
|
|
|
@ -44,6 +44,16 @@ namespace google_breakpad {
|
|||
typedef std::pair<struct MappingInfo, u_int8_t[sizeof(MDGUID)]> MappingEntry;
|
||||
typedef std::list<MappingEntry> MappingList;
|
||||
|
||||
// These entries store a list of memory regions that the client wants included
|
||||
// in the minidump.
|
||||
struct AppMemory {
|
||||
AppMemory(void *ptr, size_t length) : ptr(ptr), length(length) {}
|
||||
|
||||
void *ptr;
|
||||
size_t length;
|
||||
};
|
||||
typedef std::list<AppMemory> AppMemoryList;
|
||||
|
||||
// Write a minidump to the filesystem. This function does not malloc nor use
|
||||
// libc functions which may. Thus, it can be used in contexts where the state
|
||||
// of the heap may be corrupt.
|
||||
|
@ -65,10 +75,12 @@ bool WriteMinidump(const char* filename, pid_t crashing_process,
|
|||
bool WriteMinidump(const char* filename, pid_t process,
|
||||
pid_t process_blamed_thread);
|
||||
|
||||
// This overload also allows passing a list of known mappings.
|
||||
// This overload also allows passing a list of known mappings and
|
||||
// a list of additional memory regions to be included in the minidump.
|
||||
bool WriteMinidump(const char* filename, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings);
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appdata);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
|
|
|
@ -133,9 +133,7 @@ static const int kExceptionHandlerThreadInitialStackSize = 64 * 1024;
|
|||
|
||||
// This is passed as the context to the MinidumpWriteDump callback.
|
||||
typedef struct {
|
||||
ULONG64 memory_base;
|
||||
ULONG memory_size;
|
||||
bool finished;
|
||||
AppMemoryList::const_iterator iter, end;
|
||||
} MinidumpCallbackContext;
|
||||
|
||||
vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL;
|
||||
|
@ -926,13 +924,13 @@ BOOL CALLBACK ExceptionHandler::MinidumpWriteDumpCallback(
|
|||
case MemoryCallback: {
|
||||
MinidumpCallbackContext* callback_context =
|
||||
reinterpret_cast<MinidumpCallbackContext*>(context);
|
||||
if (callback_context->finished)
|
||||
if (callback_context->iter == callback_context->end)
|
||||
return FALSE;
|
||||
|
||||
// Include the specified memory region.
|
||||
callback_output->MemoryBase = callback_context->memory_base;
|
||||
callback_output->MemorySize = callback_context->memory_size;
|
||||
callback_context->finished = true;
|
||||
callback_output->MemoryBase = callback_context->iter->ptr;
|
||||
callback_output->MemorySize = callback_context->iter->length;
|
||||
callback_context->iter++;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@ -1015,9 +1013,6 @@ bool ExceptionHandler::WriteMinidumpWithExceptionForProcess(
|
|||
++user_streams.UserStreamCount;
|
||||
}
|
||||
|
||||
MINIDUMP_CALLBACK_INFORMATION callback;
|
||||
MinidumpCallbackContext context;
|
||||
MINIDUMP_CALLBACK_INFORMATION* callback_pointer = NULL;
|
||||
// Older versions of DbgHelp.dll don't correctly put the memory around
|
||||
// the faulting instruction pointer into the minidump. This
|
||||
// callback will ensure that it gets included.
|
||||
|
@ -1042,23 +1037,26 @@ bool ExceptionHandler::WriteMinidumpWithExceptionForProcess(
|
|||
// pointer, but settle for whatever's available up to the
|
||||
// boundaries of the memory region.
|
||||
const ULONG64 kIPMemorySize = 256;
|
||||
context.memory_base =
|
||||
ULONG64 base =
|
||||
std::max(reinterpret_cast<ULONG64>(info.BaseAddress),
|
||||
instruction_pointer - (kIPMemorySize / 2));
|
||||
ULONG64 end_of_range =
|
||||
std::min(instruction_pointer + (kIPMemorySize / 2),
|
||||
reinterpret_cast<ULONG64>(info.BaseAddress)
|
||||
+ info.RegionSize);
|
||||
context.memory_size =
|
||||
static_cast<ULONG>(end_of_range - context.memory_base);
|
||||
|
||||
context.finished = false;
|
||||
callback.CallbackRoutine = MinidumpWriteDumpCallback;
|
||||
callback.CallbackParam = reinterpret_cast<void*>(&context);
|
||||
callback_pointer = &callback;
|
||||
ULONG size = static_cast<ULONG>(end_of_range - base);
|
||||
app_memory_info_.push_back(AppMemory(base, size));
|
||||
}
|
||||
}
|
||||
|
||||
MinidumpCallbackContext context;
|
||||
context.iter = app_memory_info_.begin();
|
||||
context.end = app_memory_info_.end();
|
||||
|
||||
MINIDUMP_CALLBACK_INFORMATION callback;
|
||||
callback.CallbackRoutine = MinidumpWriteDumpCallback;
|
||||
callback.CallbackParam = reinterpret_cast<void*>(&context);
|
||||
|
||||
// The explicit comparison to TRUE avoids a warning (C4800).
|
||||
success = (minidump_write_dump_(process,
|
||||
processId,
|
||||
|
@ -1066,7 +1064,7 @@ bool ExceptionHandler::WriteMinidumpWithExceptionForProcess(
|
|||
dump_type_,
|
||||
exinfo ? &except_info : NULL,
|
||||
&user_streams,
|
||||
callback_pointer) == TRUE);
|
||||
&callback) == TRUE);
|
||||
|
||||
CloseHandle(dump_file);
|
||||
}
|
||||
|
@ -1095,4 +1093,20 @@ void ExceptionHandler::UpdateNextID() {
|
|||
next_minidump_path_c_ = next_minidump_path_.c_str();
|
||||
}
|
||||
|
||||
void ExceptionHandler::RegisterAppMemory(void *ptr, size_t length) {
|
||||
app_memory_info_.push_back(AppMemory(reinterpret_cast<ULONG64>(ptr),
|
||||
static_cast<ULONG>(length)));
|
||||
}
|
||||
|
||||
void ExceptionHandler::UnregisterAppMemory(void *ptr) {
|
||||
for (AppMemoryList::iterator iter = app_memory_info_.begin();
|
||||
iter != app_memory_info_.end();
|
||||
++iter) {
|
||||
if (iter->ptr == reinterpret_cast<ULONG64>(ptr)) {
|
||||
app_memory_info_.erase(iter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
|
|
@ -67,6 +67,7 @@
|
|||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#include "client/windows/common/ipc_protocol.h"
|
||||
#include "client/windows/crash_generation/crash_generation_client.h"
|
||||
|
@ -78,6 +79,16 @@ namespace google_breakpad {
|
|||
using std::vector;
|
||||
using std::wstring;
|
||||
|
||||
// These entries store a list of memory regions that the client wants included
|
||||
// in the minidump.
|
||||
struct AppMemory {
|
||||
AppMemory(ULONG64 ptr, ULONG length) : ptr(ptr), length(length) {}
|
||||
|
||||
ULONG64 ptr;
|
||||
ULONG length;
|
||||
};
|
||||
typedef std::list<AppMemory> AppMemoryList;
|
||||
|
||||
class ExceptionHandler {
|
||||
public:
|
||||
// A callback function to run before Breakpad performs any substantial
|
||||
|
@ -222,6 +233,11 @@ class ExceptionHandler {
|
|||
// Returns whether out-of-process dump generation is used or not.
|
||||
bool IsOutOfProcess() const { return crash_generation_client_.get() != NULL; }
|
||||
|
||||
// Calling RegisterAppMemory(p, len) causes len bytes starting
|
||||
// at address p to be copied to the minidump when a crash happens.
|
||||
void RegisterAppMemory(void *ptr, size_t length);
|
||||
void UnregisterAppMemory(void *ptr);
|
||||
|
||||
private:
|
||||
friend class AutoExceptionHandler;
|
||||
|
||||
|
@ -422,6 +438,10 @@ class ExceptionHandler {
|
|||
// to not interfere with debuggers.
|
||||
bool handle_debug_exceptions_;
|
||||
|
||||
// Callers can request additional memory regions to be included in
|
||||
// the dump.
|
||||
AppMemoryList app_memory_info_;
|
||||
|
||||
// A stack of ExceptionHandler objects that have installed unhandled
|
||||
// exception filters. This vector is used by HandleException to determine
|
||||
// which ExceptionHandler object to route an exception to. When an
|
||||
|
|
|
@ -1153,6 +1153,32 @@ bool GetAnnotation(const nsACString& key, nsACString& data)
|
|||
return true;
|
||||
}
|
||||
|
||||
nsresult RegisterAppMemory(void* ptr, size_t length)
|
||||
{
|
||||
if (!GetEnabled())
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
#if defined(XP_LINUX) || defined(XP_WIN32)
|
||||
gExceptionHandler->RegisterAppMemory(ptr, length);
|
||||
return NS_OK;
|
||||
#else
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
nsresult UnregisterAppMemory(void* ptr)
|
||||
{
|
||||
if (!GetEnabled())
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
#if defined(XP_LINUX) || defined(XP_WIN32)
|
||||
gExceptionHandler->UnregisterAppMemory(ptr);
|
||||
return NS_OK;
|
||||
#else
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool GetServerURL(nsACString& aServerURL)
|
||||
{
|
||||
if (!gExceptionHandler)
|
||||
|
@ -1689,6 +1715,7 @@ OnChildProcessDumpRequested(void* aContext,
|
|||
// have access to the library mappings.
|
||||
MappingMap::const_iterator iter =
|
||||
child_library_mappings.find(aClientInfo->pid_);
|
||||
google_breakpad::AppMemoryList a;
|
||||
if (iter == child_library_mappings.end()) {
|
||||
NS_WARNING("No library mappings found for child, can't write minidump!");
|
||||
return;
|
||||
|
@ -1698,7 +1725,8 @@ OnChildProcessDumpRequested(void* aContext,
|
|||
aClientInfo->pid_,
|
||||
aClientInfo->crash_context,
|
||||
aClientInfo->crash_context_size,
|
||||
iter->second))
|
||||
iter->second,
|
||||
a))
|
||||
return;
|
||||
#endif
|
||||
|
||||
|
|
|
@ -70,6 +70,10 @@ nsresult SetRestartArgs(int argc, char** argv);
|
|||
nsresult SetupExtraData(nsILocalFile* aAppDataDirectory,
|
||||
const nsACString& aBuildID);
|
||||
|
||||
// Registers an additional memory region to be included in the minidump
|
||||
nsresult RegisterAppMemory(void* ptr, size_t length);
|
||||
nsresult UnregisterAppMemory(void* ptr);
|
||||
|
||||
// Functions for working with minidumps and .extras
|
||||
typedef nsDataHashtable<nsCStringHashKey, nsCString> AnnotationTable;
|
||||
|
||||
|
|
|
@ -29,6 +29,9 @@ CrashTestUtils.crash = lib.declare("Crash",
|
|||
ctypes.default_abi,
|
||||
ctypes.void_t,
|
||||
ctypes.int16_t);
|
||||
CrashTestUtils.saveAppMemory = lib.declare("SaveAppMemory",
|
||||
ctypes.default_abi,
|
||||
ctypes.uint64_t);
|
||||
|
||||
CrashTestUtils.lockDir = lib.declare("LockDir",
|
||||
ctypes.default_abi,
|
||||
|
@ -47,3 +50,8 @@ CrashTestUtils.dumpHasInstructionPointerMemory =
|
|||
ctypes.default_abi,
|
||||
ctypes.bool,
|
||||
ctypes.char.ptr);
|
||||
|
||||
CrashTestUtils.dumpCheckMemory = lib.declare("DumpCheckMemory",
|
||||
ctypes.default_abi,
|
||||
ctypes.bool,
|
||||
ctypes.char.ptr);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#include <stdio.h>
|
||||
|
||||
#include "google_breakpad/processor/minidump.h"
|
||||
#include "nscore.h"
|
||||
|
||||
|
@ -58,3 +60,48 @@ DumpHasInstructionPointerMemory(const char* dump_file)
|
|||
memory_list->GetMemoryRegionForAddress(instruction_pointer);
|
||||
return region != NULL;
|
||||
}
|
||||
|
||||
// This function tests for a very specific condition. It finds
|
||||
// an address in a file, "crash-addr", in the CWD. It checks
|
||||
// that the minidump has a memory region starting at that
|
||||
// address. The region must be 32 bytes long and contain the
|
||||
// values 0 to 31 as bytes, in ascending order.
|
||||
extern "C"
|
||||
NS_EXPORT bool
|
||||
DumpCheckMemory(const char* dump_file)
|
||||
{
|
||||
Minidump dump(dump_file);
|
||||
if (!dump.Read())
|
||||
return false;
|
||||
|
||||
MinidumpMemoryList* memory_list = dump.GetMemoryList();
|
||||
if (!memory_list) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void *addr;
|
||||
FILE *fp = fopen("crash-addr", "r");
|
||||
if (!fp)
|
||||
return false;
|
||||
if (fscanf(fp, "%p", &addr) != 1)
|
||||
return false;
|
||||
fclose(fp);
|
||||
|
||||
remove("crash-addr");
|
||||
|
||||
MinidumpMemoryRegion* region =
|
||||
memory_list->GetMemoryRegionForAddress(u_int64_t(addr));
|
||||
if(!region)
|
||||
return false;
|
||||
|
||||
const u_int8_t* chars = region->GetMemory();
|
||||
if (region->GetSize() != 32)
|
||||
return false;
|
||||
|
||||
for (int i=0; i<32; i++) {
|
||||
if (chars[i] != i)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#include <stdio.h>
|
||||
|
||||
#include "nscore.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "nsExceptionHandler.h"
|
||||
|
||||
/*
|
||||
* This pure virtual call example is from MSDN
|
||||
|
@ -67,3 +70,20 @@ nsISupports* LockDir(nsILocalFile *directory)
|
|||
XRE_LockProfileDirectory(directory, &lockfile);
|
||||
return lockfile;
|
||||
}
|
||||
|
||||
char testData[32];
|
||||
|
||||
extern "C" NS_EXPORT
|
||||
PRUint64 SaveAppMemory()
|
||||
{
|
||||
for (size_t i=0; i<sizeof(testData); i++)
|
||||
testData[i] = i;
|
||||
|
||||
FILE *fp = fopen("crash-addr", "w");
|
||||
if (!fp)
|
||||
return 0;
|
||||
fprintf(fp, "%p\n", (void *)testData);
|
||||
fclose(fp);
|
||||
|
||||
return (PRInt64)testData;
|
||||
}
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче