Merge tracemonkey -> mozilla-central

This commit is contained in:
Blake Kaplan 2008-09-09 23:38:21 -07:00
commit 9893654539
807 changed files with 19254 additions and 18843 deletions

View File

@ -213,6 +213,7 @@ ACCESSIBILITY_ATOM(aria_flowto, "aria-flowto")
ACCESSIBILITY_ATOM(aria_grab, "aria-grab")
ACCESSIBILITY_ATOM(aria_haspopup, "aria-haspopup")
ACCESSIBILITY_ATOM(aria_invalid, "aria-invalid")
ACCESSIBILITY_ATOM(aria_label, "aria-label")
ACCESSIBILITY_ATOM(aria_labelledby, "aria-labelledby")
ACCESSIBILITY_ATOM(aria_level, "aria-level")
ACCESSIBILITY_ATOM(aria_live, "aria-live")

View File

@ -1628,6 +1628,7 @@ nsAccessibilityService::HasUniversalAriaProperty(nsIContent *aContent,
aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_grab) ||
aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_haspopup) ||
aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_invalid) ||
aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_label) ||
aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_labelledby) ||
aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_live) ||
aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_owns) ||

View File

@ -1706,7 +1706,11 @@ nsAccessible::AppendFlatStringFromSubtreeRecurse(nsIContent *aContent,
// Append all the text into one flat string
PRUint32 numChildren = 0;
nsCOMPtr<nsIDOMXULSelectControlElement> selectControlEl(do_QueryInterface(aContent));
if (!selectControlEl) { // Don't walk children of elements with options, just get label directly
if (!selectControlEl && aContent->Tag() != nsAccessibilityAtoms::textarea) {
// Don't walk children of elements with options, just get label directly.
// Don't traverse the children of a textarea, we want the value, not the
// static text node.
numChildren = aContent->GetChildCount();
}
@ -1843,8 +1847,14 @@ nsresult nsAccessible::GetHTMLName(nsAString& aLabel, PRBool aCanAggregateSubtre
return NS_ERROR_FAILURE; // Node shut down
}
// Check for DHTML accessibility labelledby relationship property
// Check for aria-label property
nsAutoString label;
if (content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_label, label)) {
aLabel = label;
return NS_OK;
}
// Check for aria-labelledby relationship property
nsresult rv = GetTextFromRelationID(nsAccessibilityAtoms::aria_labelledby, label);
if (NS_SUCCEEDED(rv)) {
aLabel = label;
@ -1894,8 +1904,14 @@ nsresult nsAccessible::GetXULName(nsAString& aLabel, PRBool aCanAggregateSubtree
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
NS_ASSERTION(content, "No nsIContent for DOM node");
// First check for label override via accessibility labelledby relationship
// First check for label override via aria-label property
nsAutoString label;
if (content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_label, label)) {
aLabel = label;
return NS_OK;
}
// Second check for label override via aria-labelledby relationship
nsresult rv = GetTextFromRelationID(nsAccessibilityAtoms::aria_labelledby, label);
if (NS_SUCCEEDED(rv)) {
aLabel = label;

View File

@ -38,6 +38,7 @@
#include "nsAccessibleEventData.h"
#include "nsAccessibilityAtoms.h"
#include "nsAccessibilityUtils.h"
#include "nsIAccessibilityService.h"
#include "nsIAccessNode.h"
#include "nsIDocument.h"

View File

@ -71,7 +71,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsApplicationAccessible,
enumerator->GetNext(getter_AddRefs(childWeakRef));
accessible = do_QueryReferent(childWeakRef);
if (accessible) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, accessible);
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "nsApplicationAccessible child");
cb.NoteXPCOMChild(accessible);
}
}

View File

@ -145,7 +145,7 @@ ElementTraverser(const void *aKey, nsIAccessNode *aAccessNode,
nsCycleCollectionTraversalCallback *cb =
static_cast<nsCycleCollectionTraversalCallback*>(aUserArg);
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, aAccessNode);
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mAccessNodeCache entry");
cb->NoteXPCOMChild(aAccessNode);
return PL_DHASH_NEXT;
}

View File

@ -1094,6 +1094,7 @@ NS_IMETHODIMP nsRootAccessible::FireDocLoadEvents(PRUint32 aEventType)
return NS_OK;
}
#ifdef MOZ_XUL
nsresult
nsRootAccessible::HandleTreeRowCountChangedEvent(nsIDOMEvent *aEvent,
nsIAccessibleTreeCache *aAccessible)
@ -1157,4 +1158,5 @@ nsRootAccessible::HandleTreeInvalidatedEvent(nsIDOMEvent *aEvent,
return aAccessible->TreeViewInvalidated(startRow, endRow, startCol, endCol);
}
#endif

View File

@ -42,7 +42,9 @@
#include "nsDocAccessibleWrap.h"
#include "nsIAccessibleDocument.h"
#ifdef MOZ_XUL
#include "nsIAccessibleTreeCache.h"
#endif
#include "nsHashtable.h"
#include "nsCaretAccessible.h"
@ -131,6 +133,7 @@ class nsRootAccessible : public nsDocAccessibleWrap,
/**
* Handles 'TreeRowCountChanged' event. Used in HandleEventWithTarget().
*/
#ifdef MOZ_XUL
nsresult HandleTreeRowCountChangedEvent(nsIDOMEvent *aEvent,
nsIAccessibleTreeCache *aAccessible);
@ -140,7 +143,6 @@ class nsRootAccessible : public nsDocAccessibleWrap,
nsresult HandleTreeInvalidatedEvent(nsIDOMEvent *aEvent,
nsIAccessibleTreeCache *aAccessible);
#ifdef MOZ_XUL
PRUint32 GetChromeFlags();
#endif
already_AddRefed<nsIDocShellTreeItem>

View File

@ -288,6 +288,8 @@ nsHTMLButtonAccessible::GetName(nsAString& aName)
nsAutoString name;
// Prefer aria-labelledby attribute for name
if (content->HasAttr(kNameSpaceID_None,
nsAccessibilityAtoms::aria_label) ||
content->HasAttr(kNameSpaceID_None,
nsAccessibilityAtoms::aria_labelledby)) {
GetHTMLName(name, PR_FALSE);
}

View File

@ -143,8 +143,9 @@ NS_IMETHODIMP nsHTMLImageAccessible::GetName(nsAString& aName)
PRBool hasAltAttrib =
content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::alt, aName);
if (aName.IsEmpty()) {
if (content->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_labelledby)) {
// Use HTML label or DHTML accessibility's labelledby attribute for name
if (content->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_label) ||
content->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_labelledby)) {
// Use HTML label or DHTML accessibility's label or labelledby attribute for name
// GetHTMLName will also try title attribute as a last resort
GetHTMLName(aName, PR_FALSE);
}

View File

@ -224,7 +224,19 @@ nsXFormsAccessible::GetState(PRUint32 *aState, PRUint32 *aExtraState)
NS_IMETHODIMP
nsXFormsAccessible::GetName(nsAString& aName)
{
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
if (!content) {
return NS_ERROR_FAILURE; // Node shut down
}
// Check for ARIA label property
nsAutoString name;
if (content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_label, name)) {
aName = name;
return NS_OK;
}
// Check for ARIA labelledby relationship property
nsresult rv = GetTextFromRelationID(nsAccessibilityAtoms::aria_labelledby, name);
if (NS_SUCCEEDED(rv) && !name.IsEmpty()) {
aName = name;

View File

@ -58,6 +58,7 @@ nsXFormsLabelAccessible::GetRole(PRUint32 *aRole)
NS_IMETHODIMP
nsXFormsLabelAccessible::GetName(nsAString& aName)
{
// XXX Correct name calculation for this, see bug 453594.
nsAutoString name;
nsresult rv = GetTextFromRelationID(nsAccessibilityAtoms::aria_labelledby, name);
aName = name;

View File

@ -45,7 +45,7 @@ include $(DEPTH)/config/autoconf.mk
MODULE = test_accessibility
ifdef MOZ_MOCHITEST
ifdef ENABLE_TESTS
DIRS += mochitest
endif

View File

@ -51,6 +51,7 @@ _TEST_FILES =\
common.js \
nsIAccessible_actions.js \
nsIAccessible_name.css \
nsIAccessible_name.js \
nsIAccessible_name.xbl \
nsIAccessibleEditableText.js \
test_aria_activedescendant.html \

View File

@ -0,0 +1,10 @@
function testName(aID, aName)
{
var acc = getAccessible(aID);
if (!acc) {
ok(false, "No accessible for " + aID + "!");
}
is(acc.name, aName, "Wrong name of the accessible for " + aID);
return acc;
}

View File

@ -4,42 +4,25 @@
<title>nsIAccessible::name calculation</title>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/MochiKit/packed.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/a11y/accessible/common.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/a11y/accessible/nsIAccessible_name.js"></script>
<script type="application/javascript">
const nsIAccessibleRetrieval = Components.interfaces.nsIAccessibleRetrieval;
const nsIAccessibleRole = Components.interfaces.nsIAccessibleRole;
var gAccRetrieval = null;
function testName(aID, aName)
{
var elm = document.getElementById(aID);
if (!elm) {
ok(false, "There is no element with ID " + aID);
return;
}
var acc = null;
try {
acc = gAccRetrieval.getAccessibleFor(elm);
} catch(e) {
}
if (!acc) {
ok(false, "There is no accessible for " + aID);
return;
}
is(acc.name, aName, "Wrong name of the accessible for " + aID);
}
function doTest()
{
gAccRetrieval = Components.classes["@mozilla.org/accessibleRetrieval;1"].
getService(nsIAccessibleRetrieval);
// aria-label
// Simple label provided via ARIA
testName("btn_simple_aria_label", "I am a button");
// aria-label and aria-labelledby, expect aria-label
testName("btn_both_aria_labels", "I am a button, two");
//////////////////////////////////////////////////////////////////////////
// aria-labelledby
@ -132,6 +115,19 @@
// If nothing is left. Let's try title attribute.
testName("btn_title", "title");
//////////////////////////////////////////////////////////////////////////
// textarea name
// textarea's name should have the value, which initially is specified by
// a text child.
testName("textareawithchild", "Story: Foo");
// new textarea name should reflect the value change.
var elem = document.getElementById("textareawithchild");
elem.value = "Bar";
testName("textareawithchild", "Story: Bar");
SimpleTest.finish();
}
@ -153,6 +149,14 @@
<pre id="test">
</pre>
<!-- aria-label, simple label -->
<span id="btn_simple_aria_label" role="button" aria-label="I am a button"/>
<br/>
<!-- aria-label plus aria-labelledby -->
<span id="btn_both_aria_labels" role="button" aria-label="I am a button, two"
aria-labelledby="labelledby_text"/>
<br/>
<!-- aria-labelledby, single relation -->
<span id="labelledby_text">text</span>
<button id="btn_labelledby_text"
@ -269,5 +273,13 @@
<!-- name from title attribute -->
<span id="btn_title" role="group" title="title">15</span>
<!-- A textarea nested in a label with a text child (bug #453371). -->
<form>
<label>Story:
<textarea id="textareawithchild" name="name">Foo</textarea>
</label>
</form>
</body>
</html>

View File

@ -13,41 +13,21 @@
src="chrome://mochikit/content/MochiKit/packed.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<script type="application/javascript"
src="chrome://mochikit/content/a11y/accessible/common.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/a11y/accessible/nsIAccessible_name.js"></script>
<script type="application/javascript">
<![CDATA[
const nsIAccessibleRetrieval = Components.interfaces.nsIAccessibleRetrieval;
const nsIAccessibleRole = Components.interfaces.nsIAccessibleRole;
var gAccRetrieval = null;
function testName(aID, aName)
{
var elm = document.getElementById(aID);
if (!elm) {
ok(false, "There is no element with ID " + aID);
return null;
}
var acc = null;
try {
acc = gAccRetrieval.getAccessibleFor(elm);
} catch(e) {
}
if (!acc) {
ok(false, "There is no accessible for " + aID);
return null;
}
is(acc.name, aName, "Wrong name of the accessible for " + aID);
return acc;
}
function doTest()
{
gAccRetrieval = Components.classes["@mozilla.org/accessibleRetrieval;1"].
getService(nsIAccessibleRetrieval);
// aria-label
// Simple label provided via ARIA
testName("btn_simple_aria_label", "I am a button");
// aria-label and aria-labelledby, expect aria-label
testName("btn_both_aria_labels", "I am a button, two");
//////////////////////////////////////////////////////////////////////////
// aria-labelledby
@ -198,6 +178,12 @@
</pre>
</body>
<!-- aria-label, simple label -->
<button id="btn_simple_aria_label" aria-label="I am a button"/>
<!-- aria-label plus aria-labelledby -->
<button id="btn_both_aria_labels" aria-label="I am a button, two"
aria-labelledby="labelledby_text"/>
<!-- aria-labelledby, single relation -->
<description id="labelledby_text">text</description>
<button id="btn_labelledby_text"

View File

@ -117,18 +117,11 @@ ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT)))
TK_LIBS := $(TK_LIBS)
endif
ifdef MOZ_ENABLE_LIBXUL
APP_XPCOM_LIBS = $(XPCOM_GLUE_LDOPTS)
else
MOZILLA_INTERNAL_API = 1
APP_XPCOM_LIBS = $(XPCOM_LIBS)
endif
LIBS += \
$(STATIC_COMPONENTS_LINKER_PATH) \
$(EXTRA_DSO_LIBS) \
$(MOZ_JS_LIBS) \
$(APP_XPCOM_LIBS) \
$(XPCOM_GLUE_LDOPTS) \
$(NSPR_LIBS) \
$(TK_LIBS) \
$(NULL)

View File

@ -535,9 +535,6 @@ pref("browser.backspace_action", 0);
// 2 = check multi/single line controls
pref("layout.spellcheckDefault", 1);
pref("view_source.editor.path", "");
pref("view_source.editor.external", false);
pref("browser.send_pings", false);
/* initial web feed readers list */

View File

@ -48,7 +48,7 @@ abs_srcdir = $(shell cd $(srcdir) && pwd)
CHROME_DEPS += $(abs_srcdir)/content/overrides/app-license.html
ifdef MOZ_MOCHITEST
ifdef ENABLE_TESTS
DIRS += content/test
endif

View File

@ -73,7 +73,12 @@ function init(aEvent)
button.setAttribute("accesskey", document.documentElement.getAttribute("creditsaccesskey"));
button.addEventListener("command", switchPage, false);
document.documentElement.getButton("accept").focus();
var acceptButton = document.documentElement.getButton("accept");
#ifdef XP_UNIX
acceptButton.setAttribute("icon", "close");
#endif
acceptButton.focus();
#ifdef XP_MACOSX
// it may not be sized at this point, and we need its width to calculate its position
window.sizeToContent();

View File

@ -62,9 +62,17 @@
#else
title="&aboutDialog.title;"
buttons="accept,extra2"
#ifdef XP_UNIX
buttonlabelaccept="&closeCmdGNOME.label;"
buttonaccesskeyaccept="&closeCmdGNOME.accesskey;"
#endif
#endif
creditslabel="&copyright;"
#ifdef XP_UNIX
creditsaccesskey="&copyrightGNOME.accesskey;"
#else
creditsaccesskey="&copyright.accesskey;"
#endif
aboutlabel="&aboutLink;"
aboutaccesskey="&aboutLink.accesskey;">

View File

@ -862,7 +862,7 @@ function prepareForStartup() {
// initialize observers and listeners
// and give C++ access to gBrowser
window.XULBrowserWindow = new nsBrowserStatusHandler();
XULBrowserWindow.init();
window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
@ -1896,18 +1896,7 @@ function BrowserViewSourceOfDocument(aDocument)
// If no page descriptor is available, just use the view-source URL...
}
ViewSourceOfURL(webNav.currentURI.spec, pageCookie, aDocument);
}
function ViewSourceOfURL(aURL, aPageDescriptor, aDocument)
{
var utils = window.top.gViewSourceUtils;
if (getBoolPref("view_source.editor.external", false)) {
utils.openInExternalEditor(aURL, aPageDescriptor, aDocument);
}
else {
utils.openInInternalViewer(aURL, aPageDescriptor, aDocument);
}
top.gViewSourceUtils.viewSource(webNav.currentURI.spec, pageCookie, aDocument);
}
// doc - document to use for source, or null for this window's document
@ -2825,9 +2814,8 @@ const DOMLinkHandler = {
if (!rels.feed && rels.alternate && rels.stylesheet)
break;
var feed = { title: link.title, href: link.href, type: link.type };
if (isValidFeed(feed, link.ownerDocument.nodePrincipal, rels.feed)) {
FeedHandler.addFeed(feed, link.ownerDocument);
if (isValidFeed(link, link.ownerDocument.nodePrincipal, rels.feed)) {
FeedHandler.addFeed(link, link.ownerDocument);
feedAdded = true;
}
}
@ -3725,27 +3713,20 @@ function mimeTypeIsTextBased(aMimeType)
aMimeType == "mozilla.application/cached-xul";
}
function nsBrowserStatusHandler()
{
this.init();
}
nsBrowserStatusHandler.prototype =
{
var XULBrowserWindow = {
// Stored Status, Link and Loading values
status : "",
defaultStatus : "",
jsStatus : "",
jsDefaultStatus : "",
overLink : "",
startTime : 0,
status: "",
defaultStatus: "",
jsStatus: "",
jsDefaultStatus: "",
overLink: "",
startTime: 0,
statusText: "",
lastURI: null,
statusTimeoutInEffect : false,
statusTimeoutInEffect: false,
QueryInterface : function(aIID)
{
QueryInterface: function (aIID) {
if (aIID.equals(Ci.nsIWebProgressListener) ||
aIID.equals(Ci.nsIWebProgressListener2) ||
aIID.equals(Ci.nsISupportsWeakReference) ||
@ -3755,16 +3736,33 @@ nsBrowserStatusHandler.prototype =
throw Cr.NS_NOINTERFACE;
},
init : function()
{
get statusMeter () {
delete this.statusMeter;
return this.statusMeter = document.getElementById("statusbar-icon");
},
get stopCommand () {
delete this.stopCommand;
return this.stopCommand = document.getElementById("Browser:Stop");
},
get reloadCommand () {
delete this.reloadCommand;
return this.reloadCommand = document.getElementById("Browser:Reload");
},
get statusTextField () {
delete this.statusTextField;
return this.statusTextField = document.getElementById("statusbar-display");
},
get securityButton () {
delete this.securityButton;
return this.securityButton = document.getElementById("security-button");
},
get isImage () {
delete this.isImage;
return this.isImage = document.getElementById("isImage");
},
init: function () {
this.throbberElement = document.getElementById("navigator-throbber");
this.statusMeter = document.getElementById("statusbar-icon");
this.stopCommand = document.getElementById("Browser:Stop");
this.reloadCommand = document.getElementById("Browser:Reload");
this.statusTextField = document.getElementById("statusbar-display");
this.securityButton = document.getElementById("security-button");
this.urlBar = document.getElementById("urlbar");
this.isImage = document.getElementById("isImage");
// Initialize the security button's state and tooltip text. Remember to reset
// _hostChanged, otherwise onSecurityChange will short circuit.
@ -3773,40 +3771,34 @@ nsBrowserStatusHandler.prototype =
this.onSecurityChange(null, null, securityUI.state);
},
destroy : function()
{
destroy: function () {
// XXXjag to avoid leaks :-/, see bug 60729
this.throbberElement = null;
this.statusMeter = null;
this.stopCommand = null;
this.reloadCommand = null;
this.statusTextField = null;
this.securityButton = null;
this.urlBar = null;
this.statusText = null;
this.lastURI = null;
delete this.throbberElement;
delete this.statusMeter;
delete this.stopCommand;
delete this.reloadCommand;
delete this.statusTextField;
delete this.securityButton;
delete this.statusText;
delete this.lastURI;
},
setJSStatus : function(status)
{
setJSStatus: function (status) {
this.jsStatus = status;
this.updateStatusField();
},
setJSDefaultStatus : function(status)
{
setJSDefaultStatus: function (status) {
this.jsDefaultStatus = status;
this.updateStatusField();
},
setDefaultStatus : function(status)
{
setDefaultStatus: function (status) {
this.defaultStatus = status;
this.updateStatusField();
},
setOverLink : function(link, b)
{
setOverLink: function (link, b) {
// Encode bidirectional formatting characters.
// (RFC 3987 sections 3.2 and 4.1 paragraph 6)
this.overLink = link.replace(/[\u200e\u200f\u202a\u202b\u202c\u202d\u202e]/g,
@ -3814,8 +3806,7 @@ nsBrowserStatusHandler.prototype =
this.updateStatusField();
},
updateStatusField : function()
{
updateStatusField: function () {
var text = this.overLink || this.status || this.jsStatus || this.jsDefaultStatus || this.defaultStatus;
// check the current value so we don't trigger an attribute change
@ -3826,17 +3817,15 @@ nsBrowserStatusHandler.prototype =
}
},
onLinkIconAvailable : function(aBrowser)
{
onLinkIconAvailable: function (aBrowser) {
if (gProxyFavIcon && gBrowser.mCurrentBrowser == aBrowser &&
gBrowser.userTypedValue === null)
PageProxySetIcon(aBrowser.mIconURL); // update the favicon in the URL bar
},
onProgressChange : function (aWebProgress, aRequest,
aCurSelfProgress, aMaxSelfProgress,
aCurTotalProgress, aMaxTotalProgress)
{
onProgressChange: function (aWebProgress, aRequest,
aCurSelfProgress, aMaxSelfProgress,
aCurTotalProgress, aMaxTotalProgress) {
if (aMaxTotalProgress > 0) {
// This is highly optimized. Don't touch this code unless
// you are intimately familiar with the cost of setting
@ -3846,59 +3835,55 @@ nsBrowserStatusHandler.prototype =
}
},
onProgressChange64 : function (aWebProgress, aRequest,
aCurSelfProgress, aMaxSelfProgress,
aCurTotalProgress, aMaxTotalProgress)
{
onProgressChange64: function (aWebProgress, aRequest,
aCurSelfProgress, aMaxSelfProgress,
aCurTotalProgress, aMaxTotalProgress) {
return this.onProgressChange(aWebProgress, aRequest,
aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress,
aMaxTotalProgress);
},
onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus)
{
onStateChange: function (aWebProgress, aRequest, aStateFlags, aStatus) {
const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
const nsIChannel = Components.interfaces.nsIChannel;
if (aStateFlags & nsIWebProgressListener.STATE_START) {
// This (thanks to the filter) is a network start or the first
// stray request (the first request outside of the document load),
// initialize the throbber and his friends.
// This (thanks to the filter) is a network start or the first
// stray request (the first request outside of the document load),
// initialize the throbber and his friends.
// Call start document load listeners (only if this is a network load)
if (aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK &&
aRequest && aWebProgress.DOMWindow == content)
this.startDocumentLoad(aRequest);
// Call start document load listeners (only if this is a network load)
if (aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK &&
aRequest && aWebProgress.DOMWindow == content)
this.startDocumentLoad(aRequest);
if (this.throbberElement) {
// Turn the throbber on.
this.throbberElement.setAttribute("busy", "true");
}
if (this.throbberElement) {
// Turn the throbber on.
this.throbberElement.setAttribute("busy", "true");
}
// Turn the status meter on.
this.statusMeter.value = 0; // be sure to clear the progress bar
if (gProgressCollapseTimer) {
window.clearTimeout(gProgressCollapseTimer);
gProgressCollapseTimer = null;
}
else
this.statusMeter.parentNode.collapsed = false;
// Turn the status meter on.
this.statusMeter.value = 0; // be sure to clear the progress bar
if (gProgressCollapseTimer) {
window.clearTimeout(gProgressCollapseTimer);
gProgressCollapseTimer = null;
}
else
this.statusMeter.parentNode.collapsed = false;
// XXX: This needs to be based on window activity...
this.stopCommand.removeAttribute("disabled");
// XXX: This needs to be based on window activity...
this.stopCommand.removeAttribute("disabled");
}
else if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
if (aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
if (aWebProgress.DOMWindow == content) {
if (aRequest)
this.endDocumentLoad(aRequest, aStatus);
var browser = gBrowser.mCurrentBrowser;
if (!gBrowser.mTabbedMode && !browser.mIconURL)
if (!gBrowser.mTabbedMode && !gBrowser.mCurrentBrowser.mIconURL)
gBrowser.useDefaultIcon(gBrowser.mCurrentTab);
if (Components.isSuccessCode(aStatus) &&
content.document.documentElement.getAttribute("manifest")) {
content.document.documentElement.getAttribute("manifest"))
OfflineApps.offlineAppRequested(content);
}
}
}
@ -3906,59 +3891,55 @@ nsBrowserStatusHandler.prototype =
// request stop outside of loading the document, stop throbbers
// and progress bars and such
if (aRequest) {
var msg = "";
// Get the URI either from a channel or a pseudo-object
if (aRequest instanceof nsIChannel || "URI" in aRequest) {
var location = aRequest.URI;
let msg = "";
// Get the URI either from a channel or a pseudo-object
if (aRequest instanceof nsIChannel || "URI" in aRequest) {
let location = aRequest.URI;
// For keyword URIs clear the user typed value since they will be changed into real URIs
if (location.scheme == "keyword" && aWebProgress.DOMWindow == content)
gBrowser.userTypedValue = null;
// For keyword URIs clear the user typed value since they will be changed into real URIs
if (location.scheme == "keyword" && aWebProgress.DOMWindow == content)
gBrowser.userTypedValue = null;
if (location.spec != "about:blank") {
const kErrorBindingAborted = 0x804B0002;
const kErrorNetTimeout = 0x804B000E;
switch (aStatus) {
case kErrorBindingAborted:
msg = gNavigatorBundle.getString("nv_stopped");
break;
case kErrorNetTimeout:
msg = gNavigatorBundle.getString("nv_timeout");
break;
}
if (location.spec != "about:blank") {
switch (aStatus) {
case Components.results.NS_BINDING_ABORTED:
msg = gNavigatorBundle.getString("nv_stopped");
break;
case Components.results.NS_ERROR_NET_TIMEOUT:
msg = gNavigatorBundle.getString("nv_timeout");
break;
}
}
// If msg is false then we did not have an error (channel may have
// been null, in the case of a stray image load).
if (!msg && (!location || location.spec != "about:blank")) {
msg = gNavigatorBundle.getString("nv_done");
}
this.status = "";
this.setDefaultStatus(msg);
// Disable menu entries for images, enable otherwise
if (content.document && mimeTypeIsTextBased(content.document.contentType))
this.isImage.removeAttribute('disabled');
else
this.isImage.setAttribute('disabled', 'true');
}
// If msg is false then we did not have an error (channel may have
// been null, in the case of a stray image load).
if (!msg && (!location || location.spec != "about:blank"))
msg = gNavigatorBundle.getString("nv_done");
// Turn the progress meter and throbber off.
gProgressCollapseTimer = window.setTimeout(
function() {
gProgressMeterPanel.collapsed = true;
gProgressCollapseTimer = null;
}, 100);
this.status = "";
this.setDefaultStatus(msg);
if (this.throbberElement)
this.throbberElement.removeAttribute("busy");
// Disable menu entries for images, enable otherwise
if (content.document && mimeTypeIsTextBased(content.document.contentType))
this.isImage.removeAttribute('disabled');
else
this.isImage.setAttribute('disabled', 'true');
}
this.stopCommand.setAttribute("disabled", "true");
// Turn the progress meter and throbber off.
gProgressCollapseTimer = window.setTimeout(function () {
gProgressMeterPanel.collapsed = true;
gProgressCollapseTimer = null;
}, 100);
if (this.throbberElement)
this.throbberElement.removeAttribute("busy");
this.stopCommand.setAttribute("disabled", "true");
}
},
onLocationChange : function(aWebProgress, aRequest, aLocationURI)
{
onLocationChange: function (aWebProgress, aRequest, aLocationURI) {
var location = aLocationURI ? aLocationURI.spec : "";
this._hostChanged = true;
@ -3969,7 +3950,7 @@ nsBrowserStatusHandler.prototype =
document.tooltipNode = null;
}
else {
for (var tooltipWindow =
for (let tooltipWindow =
document.tooltipNode.ownerDocument.defaultView;
tooltipWindow != tooltipWindow.parent;
tooltipWindow = tooltipWindow.parent) {
@ -3992,18 +3973,18 @@ nsBrowserStatusHandler.prototype =
// - which fires a onLocationChange message to uri + '#'...
var selectedBrowser = gBrowser.selectedBrowser;
if (selectedBrowser.lastURI) {
var oldSpec = selectedBrowser.lastURI.spec;
var oldIndexOfHash = oldSpec.indexOf("#");
let oldSpec = selectedBrowser.lastURI.spec;
let oldIndexOfHash = oldSpec.indexOf("#");
if (oldIndexOfHash != -1)
oldSpec = oldSpec.substr(0, oldIndexOfHash);
var newSpec = location;
var newIndexOfHash = newSpec.indexOf("#");
let newSpec = location;
let newIndexOfHash = newSpec.indexOf("#");
if (newIndexOfHash != -1)
newSpec = newSpec.substr(0, newSpec.indexOf("#"));
if (newSpec != oldSpec) {
// Remove all the notifications, except for those which want to
// persist across the first location change.
var nBox = gBrowser.getNotificationBox(selectedBrowser);
let nBox = gBrowser.getNotificationBox(selectedBrowser);
nBox.removeTransientNotifications();
}
}
@ -4024,10 +4005,9 @@ nsBrowserStatusHandler.prototype =
var browser = gBrowser.selectedBrowser;
if (aWebProgress.DOMWindow == content) {
if ((location == "about:blank" && !content.opener) ||
location == "") { // Second condition is for new tabs, otherwise
// reload function is enabled until tab is refreshed.
location == "") { // Second condition is for new tabs, otherwise
// reload function is enabled until tab is refreshed.
this.reloadCommand.setAttribute("disabled", "true");
} else {
this.reloadCommand.removeAttribute("disabled");
@ -4038,9 +4018,7 @@ nsBrowserStatusHandler.prototype =
if (gURLBar) {
URLBarSetURI(aLocationURI);
// Update starring UI
PlacesStarButton.updateState();
PlacesStarButton.updateState(); // Update starring UI
}
FullZoom.onLocationChange(aLocationURI);
@ -4060,73 +4038,62 @@ nsBrowserStatusHandler.prototype =
// See bug 358202, when tabs are switched during a drag operation,
// timers don't fire on windows (bug 203573)
if (aRequest) {
var self = this;
setTimeout(function() { self.asyncUpdateUI(); }, 0);
}
if (aRequest)
setTimeout(function () { XULBrowserWindow.asyncUpdateUI(); }, 0);
else
this.asyncUpdateUI();
},
asyncUpdateUI : function () {
asyncUpdateUI: function () {
FeedHandler.updateFeeds();
BrowserSearch.updateSearchButton();
},
onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
{
onStatusChange: function (aWebProgress, aRequest, aStatus, aMessage) {
this.status = aMessage;
this.updateStatusField();
},
onRefreshAttempted : function(aWebProgress, aURI, aDelay, aSameURI)
{
onRefreshAttempted: function (aWebProgress, aURI, aDelay, aSameURI) {
if (gPrefService.getBoolPref("accessibility.blockautorefresh")) {
var brandBundle = document.getElementById("bundle_brand");
var brandShortName = brandBundle.getString("brandShortName");
var refreshButtonText =
let brandBundle = document.getElementById("bundle_brand");
let brandShortName = brandBundle.getString("brandShortName");
let refreshButtonText =
gNavigatorBundle.getString("refreshBlocked.goButton");
var refreshButtonAccesskey =
let refreshButtonAccesskey =
gNavigatorBundle.getString("refreshBlocked.goButton.accesskey");
var message;
if (aSameURI)
message = gNavigatorBundle.getFormattedString(
"refreshBlocked.refreshLabel", [brandShortName]);
else
message = gNavigatorBundle.getFormattedString(
"refreshBlocked.redirectLabel", [brandShortName]);
var topBrowser = getBrowserFromContentWindow(aWebProgress.DOMWindow.top);
var docShell = aWebProgress.DOMWindow
let message =
gNavigatorBundle.getFormattedString(aSameURI ? "refreshBlocked.refreshLabel"
: "refreshBlocked.redirectLabel",
[brandShortName]);
let topBrowser = getBrowserFromContentWindow(aWebProgress.DOMWindow.top);
let docShell = aWebProgress.DOMWindow
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
var notificationBox = gBrowser.getNotificationBox(topBrowser);
var notification = notificationBox.getNotificationWithValue(
"refresh-blocked");
let notificationBox = gBrowser.getNotificationBox(topBrowser);
let notification = notificationBox.getNotificationWithValue("refresh-blocked");
if (notification) {
notification.label = message;
notification.refreshURI = aURI;
notification.delay = aDelay;
notification.docShell = docShell;
}
else {
var buttons = [{
} else {
let buttons = [{
label: refreshButtonText,
accessKey: refreshButtonAccesskey,
callback: function(aNotification, aButton) {
callback: function (aNotification, aButton) {
var refreshURI = aNotification.docShell
.QueryInterface(Ci.nsIRefreshURI);
refreshURI.forceRefreshURI(aNotification.refreshURI,
aNotification.delay, true);
}
}];
const priority = notificationBox.PRIORITY_INFO_MEDIUM;
notification = notificationBox.appendNotification(
message,
"refresh-blocked",
"chrome://browser/skin/Info.png",
priority,
buttons);
notification =
notificationBox.appendNotification(message, "refresh-blocked",
"chrome://browser/skin/Info.png",
notificationBox.PRIORITY_INFO_MEDIUM,
buttons);
notification.refreshURI = aURI;
notification.delay = aDelay;
notification.docShell = docShell;
@ -4142,9 +4109,7 @@ nsBrowserStatusHandler.prototype =
_tooltipText: null,
_hostChanged: false, // onLocationChange will flip this bit
onSecurityChange : function browser_onSecChange(aWebProgress,
aRequest, aState)
{
onSecurityChange: function (aWebProgress, aRequest, aState) {
// Don't need to do anything if the data we use to update the UI hasn't
// changed
if (this._state == aState &&
@ -4183,7 +4148,7 @@ nsBrowserStatusHandler.prototype =
wpl.STATE_SECURE_HIGH |
wpl.STATE_SECURE_MED |
wpl.STATE_SECURE_LOW;
var level = null;
var level;
var setHost = false;
switch (this._state & wpl_security_bits) {
@ -4203,12 +4168,10 @@ nsBrowserStatusHandler.prototype =
if (level) {
this.securityButton.setAttribute("level", level);
if (this.urlBar)
this.urlBar.setAttribute("level", level);
this.securityButton.hidden = false;
} else {
this.securityButton.hidden = true;
this.securityButton.removeAttribute("level");
if (this.urlBar)
this.urlBar.removeAttribute("level");
}
if (setHost && this._host)
@ -4236,8 +4199,7 @@ nsBrowserStatusHandler.prototype =
},
// simulate all change notifications after switching tabs
onUpdateCurrentBrowser : function(aStateFlags, aStatus, aMessage, aTotalProgress)
{
onUpdateCurrentBrowser: function (aStateFlags, aStatus, aMessage, aTotalProgress) {
var nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
var loadingDone = aStateFlags & nsIWebProgressListener.STATE_STOP;
// use a pseudo-object instead of a (potentially non-existing) channel for getting
@ -4256,8 +4218,7 @@ nsBrowserStatusHandler.prototype =
this.onProgressChange(gBrowser.webProgress, 0, 0, aTotalProgress, 1);
},
startDocumentLoad : function(aRequest)
{
startDocumentLoad: function (aRequest) {
// clear out feed data
gBrowser.mCurrentBrowser.feeds = null;
@ -4274,8 +4235,7 @@ nsBrowserStatusHandler.prototype =
}
},
endDocumentLoad : function(aRequest, aStatus)
{
endDocumentLoad: function (aRequest, aStatus) {
const nsIChannel = Components.interfaces.nsIChannel;
var urlStr = aRequest.QueryInterface(nsIChannel).originalURI.spec;

View File

@ -99,19 +99,15 @@
<tooltip id="aHTMLTooltip" onpopupshowing="return FillInHTMLTooltip(document.tooltipNode);"/>
<!-- for search and content formfill/pw manager -->
<panel type="autocomplete" chromedir="&locale.dir;" id="PopupAutoComplete" noautofocus="true" hidden="true"/>
<panel type="autocomplete" chromedir="&locale.dir;" id="PopupAutoComplete" noautofocus="true" hidden="true" level="top"/>
<!-- for url bar autocomplete -->
<panel type="autocomplete-richlistbox" chromedir="&locale.dir;" id="PopupAutoCompleteRichResult" noautofocus="true" hidden="true"/>
<panel type="autocomplete-richlistbox" chromedir="&locale.dir;" id="PopupAutoCompleteRichResult" noautofocus="true" hidden="true" level="top"/>
<!-- XXX panel element that has one or more text fields should not be
top-most panel, for IME users. See bug 433340 comment 100. -->
<panel id="editBookmarkPanel"
orient="vertical"
ignorekeys="true"
hidden="true"
noautohide="true"
onpopupshowing="this.removeAttribute('noautohide');"
onpopupshown="StarUI.panelShown(event);"
aria-labelledby="editBookmarkPanelTitle">
<hbox flex="1" align="top">
@ -197,7 +193,7 @@
<!-- Popup for site identity information -->
<panel id="identity-popup" position="after_start" hidden="true" noautofocus="true"
onpopupshown="document.getElementById('identity-popup-more-info-button').focus();"
onpopuphidden="focusAndSelectUrlBar();" norestorefocus="true"
onpopuphidden="focusAndSelectUrlBar();" norestorefocus="true" level="top"
chromedir="&locale.dir;">
<hbox id="identity-popup-container" align="top">
<image id="identity-popup-icon"/>
@ -234,7 +230,7 @@
<label crop="center" flex="1"/>
</tooltip>
<panel id="ctrlTab-panel" class="KUI-panel" hidden="true" noautofocus="true">
<panel id="ctrlTab-panel" class="KUI-panel" hidden="true" noautofocus="true" level="top">
<label id="ctrlTab-label" flex="1"/>
<svg:svg id="ctrlTab-svgRoot">
<svg:defs>
@ -565,6 +561,7 @@
tooltiptext="&downloadMonitor2.tooltip;" hidden="true"
command="Tools:Downloads"/>
<statusbarpanel id="security-button" class="statusbarpanel-iconic-text"
hidden="true"
ondblclick="if (event.button == 0) displaySecurityInfo();"/>
<statusbarpanel id="page-report-button" type="menu"
class="statusbarpanel-menu-iconic"

View File

@ -62,9 +62,9 @@ function initFeedTab()
}
if (rels.feed || (link.type && rels.alternate && !rels.stylesheet)) {
var feed = { title: link.title, href: link.href, type: link.type || "" };
if (isValidFeed(feed, gDocument.nodePrincipal, rels.feed)) {
var type = feedTypes[feed.type] || feedTypes["application/rss+xml"];
var type = isValidFeed(link, gDocument.nodePrincipal, rels.feed);
if (type) {
type = feedTypes[type] || feedTypes["application/rss+xml"];
addRow(feed.title, type, feed.href);
}
}

View File

@ -54,6 +54,7 @@ _TEST_FILES = test_feed_discovery.html \
_BROWSER_FILES = browser_bug321000.js \
browser_bug405137.js \
browser_bug409481.js \
browser_bug413915.js \
browser_autodiscovery.js \
browser_bug420160.js \
browser_bug441778.js \

View File

@ -0,0 +1,62 @@
function test() {
var ioserv = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
var exampleUri = ioserv.newURI("http://example.com/", null, null);
var secman = Components.classes["@mozilla.org/scriptsecuritymanager;1"].
getService(Components.interfaces.nsIScriptSecurityManager);
var principal = secman.getCodebasePrincipal(exampleUri);
function testIsFeed(aTitle, aHref, aType, aKnown) {
var link = { title: aTitle, href: aHref, type: aType };
return isValidFeed(link, principal, aKnown);
}
var href = "http://example.com/feed/";
var atomType = "application/atom+xml";
var funkyAtomType = " aPPLICAtion/Atom+XML ";
var rssType = "application/rss+xml";
var funkyRssType = " Application/RSS+XML ";
var rdfType = "application/rdf+xml";
var texmlType = "text/xml";
var appxmlType = "application/xml";
var noRss = "Foo";
var rss = "RSS";
// things that should be valid
ok(testIsFeed(noRss, href, atomType, false) == atomType,
"detect Atom feed");
ok(testIsFeed(noRss, href, funkyAtomType, false) == atomType,
"clean up and detect Atom feed");
ok(testIsFeed(noRss, href, rssType, false) == rssType,
"detect RSS feed");
ok(testIsFeed(noRss, href, funkyRssType, false) == rssType,
"clean up and detect RSS feed");
// things that should not be feeds
ok(testIsFeed(noRss, href, rdfType, false) == null,
"should not detect RDF non-feed");
ok(testIsFeed(rss, href, rdfType, false) == null,
"should not detect RDF feed from type and title");
ok(testIsFeed(noRss, href, texmlType, false) == null,
"should not detect text/xml non-feed");
ok(testIsFeed(rss, href, texmlType, false) == null,
"should not detect text/xml feed from type and title");
ok(testIsFeed(noRss, href, appxmlType, false) == null,
"should not detect application/xml non-feed");
ok(testIsFeed(rss, href, appxmlType, false) == null,
"should not detect application/xml feed from type and title");
// security check only, returns cleaned up type or "application/rss+xml"
ok(testIsFeed(noRss, href, atomType, true) == atomType,
"feed security check should return Atom type");
ok(testIsFeed(noRss, href, funkyAtomType, true) == atomType,
"feed security check should return cleaned up Atom type");
ok(testIsFeed(noRss, href, rssType, true) == rssType,
"feed security check should return RSS type");
ok(testIsFeed(noRss, href, funkyRssType, true) == rssType,
"feed security check should return cleaned up RSS type");
ok(testIsFeed(noRss, href, "", true) == rssType,
"feed security check without type should return RSS type");
ok(testIsFeed(noRss, href, "garbage", true) == "garbage",
"feed security check with garbage type should return garbage");
}

View File

@ -28,17 +28,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=377611
<!-- type is case-insensitive -->
<link rel="alternate" type="aPPliCAtion/ATom+xML" title="12" href="/12.atom" />
<!-- tolerate application/rdf+xml if the title includes 'rss' -->
<link rel="alternate" type="application/rdf+xml" title="13 rss" href="/13.rdf" />
<!-- tolerate application/xml if the title includes 'rss' -->
<link rel="alternate" type="application/xml" title="14 rss" href="/14.xml" />
<!-- tolerate text/xml if the title includes 'rss' -->
<link rel="alternate" type="text/xml" title="15 rss" href="/15.xml" />
<!-- "feed stylesheet" is a feed, though "alternate stylesheet" isn't -->
<link rel="feed stylesheet" title="16" href="/16.atom" />
<link rel="feed stylesheet" title="13" href="/13.atom" />
<!-- hyphens or letters around rel not allowed -->
<link rel="disabled-alternate" type="application/atom+xml" title="Bogus1" href="/Bogus1" />
@ -63,8 +54,17 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=377611
<!-- don't find application/rss+xml by title -->
<link rel="goats" type="application/rss+xml" title="Bogus9 RSS and Atom" href="/Bogus9" />
<!-- don't find application/rdf+xml by title -->
<link rel="alternate" type="application/rdf+xml" title="Bogus10 RSS and Atom" href="/Bogus10" />
<!-- don't find application/xml by title -->
<link rel="alternate" type="application/xml" title="Bogus11 RSS and Atom" href="/Bogus11" />
<!-- don't find text/xml by title -->
<link rel="alternate" type="text/xml" title="Bogus12 RSS and Atom" href="/Bogus12" />
<!-- alternate and stylesheet isn't a feed -->
<link rel="alternate stylesheet" type="application/rss+xml" title="Bogus10 RSS" href="/Bogus10" />
<link rel="alternate stylesheet" type="application/rss+xml" title="Bogus13 RSS" href="/Bogus13" />
</head>
<body>
<script type="text/javascript">

View File

@ -25,8 +25,8 @@ addLoadEvent(function() {
// Need privs because the feed seems to have an about:feeds principal or some
// such. It's not same-origin with us in any case.
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
is($("testFrame").contentDocument.documentElement.id, "feedHandler",
"Text didn't get sniffed as a feed?");
is($("testFrame").contentDocument.documentElement.id, "",
"Text got sniffed as a feed?");
});
addLoadEvent(SimpleTest.finish);

View File

@ -631,7 +631,7 @@ function openNewWindowWith(aURL, aDocument, aPostData, aAllowThirdPartyFixup,
/**
* isValidFeed: checks whether the given data represents a valid feed.
*
* @param aData
* @param aLink
* An object representing a feed with title, href and type.
* @param aPrincipal
* The principal of the document, used for security check.
@ -639,40 +639,28 @@ function openNewWindowWith(aURL, aDocument, aPostData, aAllowThirdPartyFixup,
* Whether this is already a known feed or not, if true only a security
* check will be performed.
*/
function isValidFeed(aData, aPrincipal, aIsFeed)
function isValidFeed(aLink, aPrincipal, aIsFeed)
{
if (!aData || !aPrincipal)
if (!aLink || !aPrincipal)
return false;
var type = aLink.type.toLowerCase().replace(/^\s+|\s*(?:;.*)?$/g, "");
if (!aIsFeed) {
var type = aData.type && aData.type.toLowerCase();
type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
aIsFeed = (type == "application/rss+xml" ||
type == "application/atom+xml");
if (!aIsFeed) {
// really slimy: general XML types with magic letters in the title
const titleRegex = /(^|\s)rss($|\s)/i;
aIsFeed = ((type == "text/xml" || type == "application/rdf+xml" ||
type == "application/xml") && titleRegex.test(aData.title));
}
}
if (aIsFeed) {
try {
urlSecurityCheck(aData.href, aPrincipal,
urlSecurityCheck(aLink.href, aPrincipal,
Components.interfaces.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
return type || "application/rss+xml";
}
catch(ex) {
aIsFeed = false;
}
}
if (type)
aData.type = type;
return aIsFeed;
return null;
}
// aCalledFromModal is optional

View File

@ -43,7 +43,7 @@ include $(DEPTH)/config/autoconf.mk
DIRS = public src
ifdef MOZ_MOCHITEST
ifdef ENABLE_TESTS
DIRS += test
endif

View File

@ -60,6 +60,8 @@
#include "nsIHttpChannel.h"
#include "nsIMIMEHeaderParam.h"
#include "nsMimeTypes.h"
#define TYPE_ATOM "application/atom+xml"
#define TYPE_RSS "application/rss+xml"
#define TYPE_MAYBE_FEED "application/vnd.mozilla.maybe.feed"
@ -331,6 +333,17 @@ nsFeedSniffer::GetMIMETypeFromContent(nsIRequest* request,
return NS_OK;
}
// Don't sniff arbitrary types. Limit sniffing to situations that
// we think can reasonably arise.
if (!contentType.EqualsLiteral(TEXT_HTML) &&
!contentType.EqualsLiteral(APPLICATION_OCTET_STREAM) &&
// Same criterion as XMLHttpRequest. Should we be checking for "+xml"
// and check for text/xml and application/xml by hand instead?
contentType.Find("xml") == -1) {
sniffedType.Truncate();
return NS_OK;
}
// Now we need to potentially decompress data served with
// Content-Encoding: gzip
nsresult rv = ConvertEncodedData(request, data, length);

View File

@ -64,7 +64,7 @@ const nsIWebNavigationInfo = Components.interfaces.nsIWebNavigationInfo;
const nsIBrowserSearchService = Components.interfaces.nsIBrowserSearchService;
const nsICommandLineValidator = Components.interfaces.nsICommandLineValidator;
const NS_BINDING_ABORTED = 0x804b0002;
const NS_BINDING_ABORTED = Components.results.NS_BINDING_ABORTED;
const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001;
const NS_ERROR_ABORT = Components.results.NS_ERROR_ABORT;

View File

@ -1087,7 +1087,7 @@ var BookmarkPropertiesPanel = {
},
onMenuListFolderSelect: function BPP_onMenuListFolderSelect(aEvent) {
if (this._folderTree.hidden)
if (this._folderTree.collapsed)
return;
this._folderTree.selectItems([this._getFolderIdFromMenuList()]);

View File

@ -306,7 +306,7 @@ var PlacesOrganizer = {
// The command execution function will take care of seeing the
// selection is a folder/container and loading its contents in
// tabs for us.
PlacesUIUtils.openContainerNodeInTabs(selectedNode);
PlacesUIUtils.openContainerNodeInTabs(selectedNode, aEvent);
}
}
},

View File

@ -47,9 +47,6 @@ MODULE = test_browser_places
XPCSHELL_TESTS = unit
ifdef MOZ_MOCHITEST
DIRS += browser
DIRS += perf
endif
DIRS = browser perf
include $(topsrcdir)/config/rules.mk

View File

@ -48,5 +48,17 @@ _CHROME_FILES = \
perf_large_delete.xul \
$(NULL)
_BROWSER_TEST_FILES = \
browser_ui_000_data.js\
browser_ui_bookmarks_sidebar.js\
browser_ui_history_sidebar.js\
$(NULL)
# XXX disabled tests, not working properly yet
# browser_ui_history_menu.js\
# browser_ui_locationbar.js\
libs:: $(_CHROME_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
libs:: $(_BROWSER_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)

View File

@ -0,0 +1,163 @@
/* ***** 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.org code.
*
* 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):
* Dietrich Ayala <dietrich@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 ***** */
/*
Sets up the database for subsequent performance
tests, and test the speed of adding to history
and bookmarks.
- add XXX visits distributed over XXX days
- add XXX bookmarks distributed over XXX days
*/
/*********************** begin header **********************/
waitForExplicitFinish();
const TEST_IDENTIFIER = "ui-perf-test";
const TEST_SUITE = "places";
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
getService(Ci.nsIWindowMediator);
var win = wm.getMostRecentWindow("navigator:browser");
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
var hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
function add_visit(aURI, aDate) {
var placeID = hs.addVisit(aURI,
aDate,
null, // no referrer
hs.TRANSITION_TYPED, // user typed in URL bar
false, // not redirect
0);
return placeID;
}
function add_bookmark(aURI) {
var bId = bs.insertBookmark(bs.unfiledBookmarksFolder, aURI,
bs.DEFAULT_INDEX, "bookmark/" + aURI.spec);
return bId;
}
function make_test_report(testName, result, units) {
return [TEST_IDENTIFIER, TEST_SUITE, testName, result, units||"ms"].join(":");
}
// Each test is an obj w/ a name property and run method
var ptests = [];
/*********************** end header **********************/
// add visits and bookmarks
ptests.push({
run: function() {
bs.runInBatchMode({
runBatched: function(aUserData) {
// timespan - same as default history pref for now
var days = 90;
// add visits, distributed across the timespan
var total_visits = 300;
var visits_per_day = total_visits/days;
var visit_date_microsec = Date.now() * 1000;
var day_counter = 0;
var start = Date.now();
for (var i = 0; i < days; i++) {
visit_date_microsec -= 86400 * 1000 * 1000; // remove a day
var spec = "http://example.com/" + visit_date_microsec;
for (var j = 0; j < visits_per_day; j++) {
var uri = ios.newURI(spec + j, null, null);
add_visit(uri, visit_date_microsec);
}
}
var duration = Date.now() - start;
var report = make_test_report("add_visits", duration);
ok(true, report);
// add bookmarks
var bookmarks_total = total_visits/10; // bookmark a tenth of the URLs in history
var bookmarks_per_day = bookmarks_total/days;
// reset visit date counter
visit_date_microsec = Date.now() * 1000;
var bookmark_counter = 0;
start = Date.now();
for (var i = 0; i < days; i++) {
visit_date_microsec -= 86400 * 1000 * 1000; // remove a day
var spec = "http://example.com/" + visit_date_microsec;
for (var j = 0; j < visits_per_day; j++) {
var uri = ios.newURI(spec + j, null, null);
if (bookmark_counter < bookmarks_per_day) {
add_bookmark(uri);
bookmark_counter++;
}
else
bookmark_counter = 0;
}
}
duration = Date.now() - start;
report = make_test_report("add_bookmarks", duration);
ok(true, report);
runNextTest();
}
}, null);
}
});
function test() {
// kick off tests
runNextTest();
}
function runNextTest() {
if (ptests.length > 0)
ptests.shift().run();
else
finish();
}

View File

@ -0,0 +1,133 @@
/* ***** 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.org code.
*
* 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):
* Dietrich Ayala <dietrich@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 ***** */
/*
Tests the performance of opening the bookmarks sidebar
*/
/*********************** begin header **********************/
waitForExplicitFinish();
const TEST_IDENTIFIER = "ui-perf-test";
const TEST_SUITE = "places";
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
getService(Ci.nsIWindowMediator);
var win = wm.getMostRecentWindow("navigator:browser");
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
var hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
function add_visit(aURI, aDate) {
var placeID = hs.addVisit(aURI,
aDate,
null, // no referrer
hs.TRANSITION_TYPED, // user typed in URL bar
false, // not redirect
0);
return placeID;
}
function add_bookmark(aURI) {
var bId = bs.insertBookmark(bs.unfiledBookmarksFolder, aURI,
bs.DEFAULT_INDEX, "bookmark/" + aURI.spec);
return bId;
}
function make_test_report(testName, result, units) {
return [TEST_IDENTIFIER, TEST_SUITE, testName, result, units||"ms"].join(":");
}
// Each test is an obj w/ a name property and run method
var ptests = [];
/*********************** end header **********************/
const TEST_REPEAT_COUNT = 10;
// test duration of bookmarks sidebar opening
ptests.push({
name: "open_bookmarks_sidebar",
times: [],
run: function() {
var self = this;
var start = Date.now();
var sb = document.getElementById("sidebar");
sb.addEventListener("load", function() {
var duration = Date.now() - start;
sb.removeEventListener("load", arguments.callee, true);
toggleSidebar("viewBookmarksSidebar", false);
self.times.push(duration);
if (self.times.length == TEST_REPEAT_COUNT)
self.finish();
else
self.run();
}, true);
toggleSidebar("viewBookmarksSidebar", true);
},
finish: function() {
this.times.sort(); // sort the scores
this.times.pop(); // remove worst
this.times.shift(); // remove best
var totalDuration = this.times.reduce(function(time, total){ return time + total; });
var avgDuration = totalDuration/this.times.length;
var report = make_test_report("open_bookmarks_sidebar", avgDuration);
ok(true, report);
runNextTest();
}
});
function test() {
// kick off tests
runNextTest();
}
function runNextTest() {
if (ptests.length > 0)
ptests.shift().run();
else
finish();
}

View File

@ -0,0 +1,127 @@
/* ***** 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.org code.
*
* 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):
* Dietrich Ayala <dietrich@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 ***** */
/*
Tests the performance of opening the history menu.
*/
/*********************** begin header **********************/
waitForExplicitFinish();
const TEST_IDENTIFIER = "ui-perf-test";
const TEST_SUITE = "places";
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
getService(Ci.nsIWindowMediator);
var win = wm.getMostRecentWindow("navigator:browser");
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
var hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
function add_visit(aURI, aDate) {
var placeID = hs.addVisit(aURI,
aDate,
null, // no referrer
hs.TRANSITION_TYPED, // user typed in URL bar
false, // not redirect
0);
return placeID;
}
function add_bookmark(aURI) {
var bId = bs.insertBookmark(bs.unfiledBookmarksFolder, aURI,
bs.DEFAULT_INDEX, "bookmark/" + aURI.spec);
return bId;
}
function make_test_report(testName, result, units) {
return [TEST_IDENTIFIER, TEST_SUITE, testName, result, units||"ms"].join(":");
}
// Each test is an obj w/ a name property and run method
var ptests = [];
/*********************** end header **********************/
// test duration of history menu opening
ptests.push({
name: "open_history_menu",
run: function() {
var menu = document.getElementById("history-menu");
ok(menu, "history menu should exist!");
var start = Date.now();
var popup = document.getElementById("goPopup");
popup.addEventListener("popupshown", function() {
var duration = Date.now() - start;
var report = make_test_report("open_history_menu", duration);
ok(true, report);
// clean up
popup.removeEventListener("popupshown", arguments.callee, false);
menu.open = false;
runNextTest();
}, false);
// XXX does not work, is still open=false immediately after setting it to true
//menu.open = true;
// XXX does nada
//EventUtils.sendMouseEvent({type:"click"}, "history-menu");
}
});
function test() {
// kick off tests
runNextTest();
}
function runNextTest() {
if (ptests.length > 0)
ptests.shift().run();
else
finish();
}

View File

@ -0,0 +1,246 @@
/* ***** 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.org code.
*
* 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):
* Dietrich Ayala <dietrich@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 ***** */
/*
Tests the performance of opening the History
sidebar in all the available views.
*/
/*********************** begin header **********************/
waitForExplicitFinish();
const TEST_IDENTIFIER = "ui-perf-test";
const TEST_SUITE = "places";
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
getService(Ci.nsIWindowMediator);
var win = wm.getMostRecentWindow("navigator:browser");
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
var hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
function add_visit(aURI, aDate) {
var placeID = hs.addVisit(aURI,
aDate,
null, // no referrer
hs.TRANSITION_TYPED, // user typed in URL bar
false, // not redirect
0);
return placeID;
}
function add_bookmark(aURI) {
var bId = bs.insertBookmark(bs.unfiledBookmarksFolder, aURI,
bs.DEFAULT_INDEX, "bookmark/" + aURI.spec);
return bId;
}
function make_test_report(testName, result, units) {
return [TEST_IDENTIFIER, TEST_SUITE, testName, result, units||"ms"].join(":");
}
// Each test is an obj w/ a name property and run method
var ptests = [];
/*********************** end header **********************/
const TEST_REPEAT_COUNT = 10;
// test duration of history sidebar opening
// default: bydayandsite
ptests.push({
name: "open_history_sidebar_bydayandsite",
times: [],
run: function() {
var self = this;
var start = Date.now();
var sb = document.getElementById("sidebar");
sb.addEventListener("load", function(aEvent) {
sb.removeEventListener("load", arguments.callee, true);
var duration = Date.now() - start;
toggleSidebar("viewHistorySidebar", false);
self.times.push(duration);
if (self.times.length == TEST_REPEAT_COUNT)
self.finish();
else
self.run();
}, true);
toggleSidebar("viewHistorySidebar", true);
},
finish: function() {
processTestResult(this);
runNextTest();
}
});
// bysite
ptests.push({
name: "history_sidebar_bysite",
times: [],
run: function() {
var self = this;
var start = Date.now();
var sb = document.getElementById("sidebar");
sb.addEventListener("load", function() {
var duration = Date.now() - start;
sb.removeEventListener("load", arguments.callee, true);
sb.contentDocument.getElementById("bysite").doCommand();
toggleSidebar("viewHistorySidebar", false);
self.times.push(duration);
if (self.times.length == TEST_REPEAT_COUNT)
self.finish();
else
self.run();
}, true);
toggleSidebar("viewHistorySidebar", true);
},
finish: function() {
processTestResult(this);
runNextTest();
}
});
// byday
ptests.push({
name: "history_sidebar_byday",
times: [],
run: function() {
var self = this;
var start = Date.now();
var sb = document.getElementById("sidebar");
sb.addEventListener("load", function() {
var duration = Date.now() - start;
sb.removeEventListener("load", arguments.callee, true);
sb.contentDocument.getElementById("byday").doCommand();
toggleSidebar("viewHistorySidebar", false);
self.times.push(duration);
if (self.times.length == TEST_REPEAT_COUNT)
self.finish();
else
self.run();
}, true);
toggleSidebar("viewHistorySidebar", true);
},
finish: function() {
processTestResult(this);
runNextTest();
}
});
// byvisited
ptests.push({
name: "history_sidebar_byvisited",
times: [],
run: function() {
var self = this;
var start = Date.now();
var sb = document.getElementById("sidebar");
sb.addEventListener("load", function() {
var duration = Date.now() - start;
sb.removeEventListener("load", arguments.callee, true);
sb.contentDocument.getElementById("byvisited").doCommand();
toggleSidebar("viewHistorySidebar", false);
self.times.push(duration);
if (self.times.length == TEST_REPEAT_COUNT)
self.finish();
else
self.run();
}, true);
toggleSidebar("viewHistorySidebar", true);
},
finish: function() {
processTestResult(this);
runNextTest();
}
});
// bylastvisited
ptests.push({
name: "history_sidebar_bylastvisited",
times: [],
run: function() {
var self = this;
var start = Date.now();
var sb = document.getElementById("sidebar");
sb.addEventListener("load", function() {
var duration = Date.now() - start;
sb.removeEventListener("load", arguments.callee, true);
sb.contentDocument.getElementById("bylastvisited").doCommand();
toggleSidebar("viewHistorySidebar", false);
self.times.push(duration);
if (self.times.length == TEST_REPEAT_COUNT)
self.finish();
else
self.run();
}, true);
toggleSidebar("viewHistorySidebar", true);
},
finish: function() {
processTestResult(this);
runNextTest();
}
});
function processTestResult(aTest) {
aTest.times.sort(); // sort the scores
aTest.times.pop(); // remove worst
aTest.times.shift(); // remove best
var totalDuration = aTest.times.reduce(function(time, total){ return time + total; });
var avgDuration = totalDuration/aTest.times.length;
var report = make_test_report(aTest.name, avgDuration);
ok(true, report);
}
function test() {
// kick off tests
runNextTest();
}
function runNextTest() {
if (ptests.length > 0)
ptests.shift().run();
else
finish();
}

View File

@ -0,0 +1,114 @@
/* ***** 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.org code.
*
* 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):
* Dietrich Ayala <dietrich@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 ***** */
/*
Tests the performance of opening the location bar dropdown.
*/
/*********************** begin header **********************/
waitForExplicitFinish();
const TEST_IDENTIFIER = "ui-perf-test";
const TEST_SUITE = "places";
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
getService(Ci.nsIWindowMediator);
var win = wm.getMostRecentWindow("navigator:browser");
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
var hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
function add_visit(aURI, aDate) {
var placeID = hs.addVisit(aURI,
aDate,
null, // no referrer
hs.TRANSITION_TYPED, // user typed in URL bar
false, // not redirect
0);
return placeID;
}
function add_bookmark(aURI) {
var bId = bs.insertBookmark(bs.unfiledBookmarksFolder, aURI,
bs.DEFAULT_INDEX, "bookmark/" + aURI.spec);
return bId;
}
function make_test_report(testName, result, units) {
return [TEST_IDENTIFIER, TEST_SUITE, testName, result, units||"ms"].join(":");
}
// Each test is an obj w/ a name property and run method
var ptests = [];
/*********************** end header **********************/
// test duration of history menu opening
ptests.push({
name: "open_locationbar_default",
run: function() {
var urlbar = document.getElementById("urlbar");
urlbar.addEventListener("onsearchcomplete", function() {
urlbar.removeEventListener("onsearchcomplete", arguments.callee, false);
runNextTest();
}, false);
urlbar.value = "example";
urlbar.select();
EventUtils.synthesizeKey("VK_RETURN", {});
}
});
function test() {
// kick off tests
runNextTest();
}
function runNextTest() {
if (ptests.length > 0)
ptests.shift().run();
else
finish();
}

View File

@ -43,7 +43,7 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
ifdef MOZ_MOCHITEST
ifdef ENABLE_TESTS
DIRS += tests
endif

View File

@ -67,8 +67,11 @@ var gCookiesWindow = {
if ("arguments" in window && window.arguments[0] &&
window.arguments[0].filterString)
this.setFilter(window.arguments[0].filterString);
{
document.getElementById("filter").value = window.arguments[0].filterString;
this.filter();
}
this._saveState();
document.getElementById("filter").focus();
@ -790,8 +793,7 @@ var gCookiesWindow = {
// Revert to single-select in the tree
this._tree.setAttribute("seltype", "single");
// Clear the Filter and the Tree Display
document.getElementById("filter").value = "";
// Clear the Tree Display
this._view._filtered = false;
this._view._rowCount = 0;
this._tree.treeBoxObject.rowCountChanged(0, -this._view._filterSet.length);
@ -826,8 +828,6 @@ var gCookiesWindow = {
this._lastSelectedRanges = [];
document.getElementById("cookiesIntro").value = this._bundle.getString("cookiesAll");
document.getElementById("clearFilter").disabled = true;
document.getElementById("filter").focus();
},
_cookieMatchesFilter: function (aCookie)
@ -875,68 +875,44 @@ var gCookiesWindow = {
this._openIndices.push(i);
}
},
_filterTimeout: -1,
onFilterInput: function ()
{
if (this._filterTimeout != -1)
clearTimeout(this._filterTimeout);
function filterCookies()
{
var filter = document.getElementById("filter").value;
if (filter == "") {
gCookiesWindow.clearFilter();
return;
}
var view = gCookiesWindow._view;
view._filterSet = gCookiesWindow._filterCookies(filter);
if (!view._filtered) {
// Save Display Info for the Non-Filtered mode when we first
// enter Filtered mode.
gCookiesWindow._saveState();
view._filtered = true;
}
// Move to multi-select in the tree
gCookiesWindow._tree.setAttribute("seltype", "multiple");
// Clear the display
var oldCount = view._rowCount;
view._rowCount = 0;
gCookiesWindow._tree.treeBoxObject.rowCountChanged(0, -oldCount);
// Set up the filtered display
view._rowCount = view._filterSet.length;
gCookiesWindow._tree.treeBoxObject.rowCountChanged(0, view.rowCount);
// if the view is not empty then select the first item
if (view.rowCount > 0)
view.selection.select(0);
document.getElementById("cookiesIntro").value = gCookiesWindow._bundle.getString("cookiesFiltered");
document.getElementById("clearFilter").disabled = false;
}
window.filterCookies = filterCookies;
this._filterTimeout = setTimeout("filterCookies();", 500);
},
onFilterKeyPress: function (aEvent)
filter: function ()
{
var filter = document.getElementById("filter").value;
if (aEvent.keyCode == 27 && filter != "") // ESC key
this.clearFilter();
if (filter == "") {
gCookiesWindow.clearFilter();
return;
}
var view = gCookiesWindow._view;
view._filterSet = gCookiesWindow._filterCookies(filter);
if (!view._filtered) {
// Save Display Info for the Non-Filtered mode when we first
// enter Filtered mode.
gCookiesWindow._saveState();
view._filtered = true;
}
// Move to multi-select in the tree
gCookiesWindow._tree.setAttribute("seltype", "multiple");
// Clear the display
var oldCount = view._rowCount;
view._rowCount = 0;
gCookiesWindow._tree.treeBoxObject.rowCountChanged(0, -oldCount);
// Set up the filtered display
view._rowCount = view._filterSet.length;
gCookiesWindow._tree.treeBoxObject.rowCountChanged(0, view.rowCount);
// if the view is not empty then select the first item
if (view.rowCount > 0)
view.selection.select(0);
document.getElementById("cookiesIntro").value = gCookiesWindow._bundle.getString("cookiesFiltered");
},
focusFilterBox: function ()
{
{
var filter = document.getElementById("filter");
filter.focus();
filter.select();
},
setFilter: function (aFilterString)
{
document.getElementById("filter").value = aFilterString;
this.onFilterInput();
}
};

View File

@ -62,15 +62,11 @@
<key key="&focusSearch1.key;" modifiers="accel" oncommand="gCookiesWindow.focusFilterBox();"/>
<key key="&focusSearch2.key;" modifiers="accel" oncommand="gCookiesWindow.focusFilterBox();"/>
</keyset>
<vbox flex="1" class="contentPane">
<hbox align="center">
<label accesskey="&filter.accesskey;" control="filter">&filter.label;</label>
<textbox id="filter" flex="1" oninput="gCookiesWindow.onFilterInput();"
onkeypress="gCookiesWindow.onFilterKeyPress(event);"/>
<button id="clearFilter" icon="clear" label="&clear.label;"
accesskey="&clear.accesskey;"
oncommand="gCookiesWindow.clearFilter();" disabled="true"/>
<textbox type="search" id="filter" flex="1" oncommand="gCookiesWindow.filter();"/>
</hbox>
<separator class="thin"/>
<label control="cookiesList" id="cookiesIntro" value="&cookiesonsystem.label;"/>

View File

@ -45,7 +45,7 @@ include $(DEPTH)/config/autoconf.mk
DIRS = src
ifdef MOZ_MOCHITEST
ifdef ENABLE_TESTS
DIRS += content/test
endif

View File

@ -43,7 +43,7 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
ifdef MOZ_MOCHITEST
ifdef ENABLE_TESTS
DIRS = test
endif

View File

@ -1553,13 +1553,6 @@ Engine.prototype = {
* the engine object.
*/
_parseAsSherlock: function SRCH_ENG_parseAsSherlock() {
/**
* Trims leading and trailing whitespace from aStr.
*/
function sTrim(aStr) {
return aStr.replace(/^\s+/g, "").replace(/\s+$/g, "");
}
/**
* Extracts one Sherlock "section" from aSource. A section is essentially
* an HTML element with attributes, but each attribute must be on a new
@ -1616,11 +1609,11 @@ Engine.prototype = {
var section = {};
for (var i = 0; i < lines.length; i++) {
var line = sTrim(lines[i]);
var line = lines[i].trim();
var els = line.split("=");
var name = sTrim(els.shift().toLowerCase());
var value = sTrim(els.join("="));
var name = els.shift().trim().toLowerCase();
var value = els.join("=").trim();
if (!name || !value)
continue;
@ -1629,7 +1622,7 @@ Engine.prototype = {
// value, and remove any trailing slashes or ">" characters
value = value.replace(/^["']/, "")
.replace(/["']\s*[\\\/]?>?\s*$/, "") || "";
value = sTrim(value);
value = value.trim();
// Don't clobber existing attributes
if (!(name in section))
@ -1747,7 +1740,7 @@ Engine.prototype = {
lines.forEach(function (line) {
// Strip leading/trailing whitespace and remove the surrounding markup
// ("<input" and ">")
line = sTrim(line).replace(/^<input/i, "").replace(/>$/, "");
line = line.trim().replace(/^<input/i, "").replace(/>$/, "");
// If this is one of the "directional" inputs (<inputnext>/<inputprev>)
const directionalInput = /^(prev|next)/i;

View File

@ -50,7 +50,7 @@ XPIDLSRCS = \
DIRS = src
ifdef MOZ_MOCHITEST
ifdef ENABLE_TESTS
DIRS += test
endif

View File

@ -101,44 +101,41 @@ SessionStartup.prototype = {
// get file references
var dirService = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties);
this._sessionFile = dirService.get("ProfD", Ci.nsILocalFile);
this._sessionFile.append("sessionstore.js");
let sessionFile = dirService.get("ProfD", Ci.nsILocalFile);
sessionFile.append("sessionstore.js");
let doResumeSession = this._prefBranch.getBoolPref("sessionstore.resume_session_once") ||
this._prefBranch.getIntPref("startup.page") == 3;
// only read the session file if config allows possibility of restoring
var resumeFromCrash = this._prefBranch.getBoolPref("sessionstore.resume_from_crash");
if ((resumeFromCrash || this._doResumeSession()) && this._sessionFile.exists()) {
// get string containing session state
this._iniString = this._readStateFile(this._sessionFile);
if (this._iniString) {
try {
// parse the session state into JS objects
var s = new Components.utils.Sandbox("about:blank");
var initialState = Components.utils.evalInSandbox(this._iniString, s);
// set bool detecting crash
this._lastSessionCrashed =
initialState.session && initialState.session.state &&
initialState.session.state == STATE_RUNNING_STR;
// invalid .INI file - nothing can be restored
}
catch (ex) { debug("The session file is invalid: " + ex); }
}
}
// prompt and check prefs
if (this._iniString) {
if (this._lastSessionCrashed && this._doRecoverSession())
this._sessionType = Ci.nsISessionStartup.RECOVER_SESSION;
else if (!this._lastSessionCrashed && this._doResumeSession())
this._sessionType = Ci.nsISessionStartup.RESUME_SESSION;
else
this._iniString = null; // reset the state string
}
if (this._prefBranch.getBoolPref("sessionstore.resume_session_once")) {
this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);
}
if (!resumeFromCrash && !doResumeSession || !sessionFile.exists())
return;
// get string containing session state
this._iniString = this._readStateFile(sessionFile);
if (!this._iniString)
return;
try {
// parse the session state into JS objects
var s = new Components.utils.Sandbox("about:blank");
var initialState = Components.utils.evalInSandbox(this._iniString, s);
}
catch (ex) { debug("The session file is invalid: " + ex); }
let lastSessionCrashed =
initialState && initialState.session && initialState.session.state &&
initialState.session.state == STATE_RUNNING_STR;
// set the startup type
if (lastSessionCrashed && resumeFromCrash && this._doRecoverSession())
this._sessionType = Ci.nsISessionStartup.RECOVER_SESSION;
else if (!lastSessionCrashed && doResumeSession)
this._sessionType = Ci.nsISessionStartup.RESUME_SESSION;
else
this._iniString = null; // reset the state string
if (this._sessionType != Ci.nsISessionStartup.NO_SESSION) {
// wait for the first browser window to open
var observerService = Cc["@mozilla.org/observer-service;1"].
@ -165,8 +162,7 @@ SessionStartup.prototype = {
this.init();
break;
case "quit-application":
// make sure that we don't init at this point, as that might
// unwantedly discard the session (cf. bug 409115)
// no reason for initializing at this point (cf. bug 409115)
observerService.removeObserver(this, "final-ui-startup");
observerService.removeObserver(this, "quit-application");
break;
@ -241,25 +237,12 @@ SessionStartup.prototype = {
/* ........ Auxiliary Functions .............. */
/**
* Whether or not to resume session, if not recovering from a crash.
* @returns bool
*/
_doResumeSession: function sss_doResumeSession() {
return this._prefBranch.getIntPref("startup.page") == 3 ||
this._prefBranch.getBoolPref("sessionstore.resume_session_once");
},
/**
* prompt user whether or not to restore the previous session,
* if the browser crashed
* @returns bool
*/
_doRecoverSession: function sss_doRecoverSession() {
// do not prompt or resume, post-crash
if (!this._prefBranch.getBoolPref("sessionstore.resume_from_crash"))
return false;
// if the prompt fails, recover anyway
var recover = true;
@ -365,7 +348,7 @@ SessionStartup.prototype = {
return content.replace(/\r\n?/g, "\n");
}
catch (ex) { } // inexisting file?
catch (ex) { Components.utils.reportError(ex); }
return null;
},
@ -386,8 +369,5 @@ SessionStartup.prototype = {
};
//module initialization
function NSGetModule(aCompMgr, aFileSpec) {
return XPCOMUtils.generateModule([SessionStartup]);
}
function NSGetModule(aCompMgr, aFileSpec)
XPCOMUtils.generateModule([SessionStartup]);

View File

@ -144,14 +144,12 @@ SessionStoreService.prototype = {
_windows: {},
// in case the last closed window ain't a navigator:browser one
_lastWindowClosed: null,
// (also contains browser popup windows closed after the last non-popup one)
_lastClosedWindows: null,
// not-"dirty" windows usually don't need to have their data updated
_dirtyWindows: {},
// flag all windows as dirty
_dirty: false,
/* ........ Global Event Handlers .............. */
/**
@ -195,7 +193,7 @@ SessionStoreService.prototype = {
this._sessionFileBackup = this._sessionFile.clone();
this._sessionFile.append("sessionstore.js");
this._sessionFileBackup.append("sessionstore.bak");
// get string containing session state
var iniString;
try {
@ -210,29 +208,34 @@ SessionStoreService.prototype = {
try {
// parse the session state into JS objects
this._initialState = this._safeEval(iniString);
// set bool detecting crash
this._lastSessionCrashed =
// if last session crashed, backup the session
let lastSessionCrashed =
this._initialState.session && this._initialState.session.state &&
this._initialState.session.state == STATE_RUNNING_STR;
if (lastSessionCrashed) {
try {
this._writeFile(this._sessionFileBackup, iniString);
}
catch (ex) { } // nothing else we can do here
}
// make sure that at least the first window doesn't have anything hidden
delete this._initialState.windows[0].hidden;
}
catch (ex) { debug("The session file is invalid: " + ex); }
}
// if last session crashed, backup the session
if (this._lastSessionCrashed) {
try {
this._writeFile(this._sessionFileBackup, iniString);
}
catch (ex) { } // nothing else we can do here
}
// remove the session data files if crash recovery is disabled
if (!this._resume_from_crash)
this._clearDisk();
// at this point, we've as good as resumed the session, so we can
// clear the resume_session_once flag, if it's set
if (this._loadState != STATE_QUITTING &&
this._prefBranch.getBoolPref("sessionstore.resume_session_once"))
this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);
// As this is called at delayedStartup, restoration must be initiated here
this.onLoad(aWindow);
},
@ -278,7 +281,6 @@ SessionStoreService.prototype = {
this._collectWindowData(aWindow);
});
this._dirtyWindows = [];
this._dirty = false;
break;
case "quit-application-granted":
// freeze the data at what we've got (ignoring closing windows)
@ -291,22 +293,27 @@ SessionStoreService.prototype = {
this._uninit();
break;
case "browser:purge-session-history": // catch sanitization
let openWindows = {};
this._forEachBrowserWindow(function(aWindow) {
Array.forEach(aWindow.getBrowser().browsers, function(aBrowser) {
delete aBrowser.parentNode.__SS_data;
});
openWindows[aWindow.__SSi] = true;
});
this._lastWindowClosed = null;
this._clearDisk();
// also clear all data about closed tabs
// also clear all data about closed tabs and windows
for (ix in this._windows) {
this._windows[ix]._closedTabs = [];
if (ix in openWindows)
this._windows[ix]._closedTabs = [];
else
delete this._windows[ix];
}
this._lastClosedWindows = null;
this._clearDisk();
// give the tabbrowsers a chance to clear their histories first
var win = this._getMostRecentBrowserWindow();
if (win)
win.setTimeout(function() { _this.saveState(true); }, 0);
else
else if (this._loadState == STATE_RUNNING)
this.saveState(true);
break;
case "nsPref:changed": // catch pref changes
@ -357,9 +364,13 @@ SessionStoreService.prototype = {
case "pageshow":
this.onTabLoad(aEvent.currentTarget.ownerDocument.defaultView, aEvent.currentTarget, aEvent);
break;
case "change":
case "input":
case "DOMAutoComplete":
this.onTabInput(aEvent.currentTarget.ownerDocument.defaultView, aEvent.currentTarget, aEvent);
this.onTabInput(aEvent.currentTarget.ownerDocument.defaultView, aEvent.currentTarget);
break;
case "scroll":
this.onTabScroll(aEvent.currentTarget.ownerDocument.defaultView);
break;
case "TabOpen":
case "TabClose":
@ -404,6 +415,8 @@ SessionStoreService.prototype = {
// and create its data object
this._windows[aWindow.__SSi] = { tabs: [], selected: 0, _closedTabs: [] };
if (!aWindow.toolbar.visible)
this._windows[aWindow.__SSi].isPopup = true;
// perform additional initialization when the first window is loading
if (this._loadState == STATE_STOPPED) {
@ -472,9 +485,16 @@ SessionStoreService.prototype = {
this._collectWindowData(aWindow);
// preserve this window's data (in case it was the last navigator:browser)
this._lastWindowClosed = this._windows[aWindow.__SSi];
this._lastWindowClosed.title = aWindow.content.document.title;
this._updateCookies([this._lastWindowClosed]);
var winData = this._windows[aWindow.__SSi];
winData.title = aWindow.content.document.title;
// if this is a popup window, append it to what we've already got (cf. bug 368677)
if (!this._lastClosedWindows || !winData.isPopup)
this._lastClosedWindows = [winData];
else
this._lastClosedWindows.push(winData);
this._updateCookies(this._lastClosedWindows);
// clear this window from the list
delete this._windows[aWindow.__SSi];
@ -488,7 +508,7 @@ SessionStoreService.prototype = {
}
// cache the window state until the window is completely gone
aWindow.__SS_dyingCache = this._windows[aWindow.__SSi] || this._lastWindowClosed;
aWindow.__SS_dyingCache = this._windows[aWindow.__SSi] || winData;
// reset the _tab property to avoid keeping the tab's XUL element alive
// longer than we need it
@ -512,8 +532,10 @@ SessionStoreService.prototype = {
onTabAdd: function sss_onTabAdd(aWindow, aPanel, aNoNotification) {
aPanel.addEventListener("load", this, true);
aPanel.addEventListener("pageshow", this, true);
aPanel.addEventListener("change", this, true);
aPanel.addEventListener("input", this, true);
aPanel.addEventListener("DOMAutoComplete", this, true);
aPanel.addEventListener("scroll", this, true);
if (!aNoNotification) {
this.saveStateDelayed(aWindow);
@ -532,11 +554,12 @@ SessionStoreService.prototype = {
onTabRemove: function sss_onTabRemove(aWindow, aPanel, aNoNotification) {
aPanel.removeEventListener("load", this, true);
aPanel.removeEventListener("pageshow", this, true);
aPanel.removeEventListener("change", this, true);
aPanel.removeEventListener("input", this, true);
aPanel.removeEventListener("DOMAutoComplete", this, true);
aPanel.removeEventListener("scroll", this, true);
delete aPanel.__SS_data;
delete aPanel.__SS_text;
if (!aNoNotification) {
this.saveStateDelayed(aWindow);
@ -572,10 +595,18 @@ SessionStoreService.prototype = {
delete tabState._tab;
// store closed-tab data for undo
if (tabState.entries.length > 1 || tabState.entries[0].url != "about:blank") {
if (tabState.entries.length > 0) {
let tabTitle = aTab.label;
let tabbrowser = aWindow.gBrowser;
// replace "Loading..." with the document title (with minimal side-effects)
if (tabTitle == tabbrowser.mStringBundle.getString("tabs.loading")) {
tabbrowser.setTabTitle(aTab);
[tabTitle, aTab.label] = [aTab.label, tabTitle];
}
this._windows[aWindow.__SSi]._closedTabs.unshift({
state: tabState,
title: aTab.getAttribute("label"),
title: tabTitle,
image: aTab.getAttribute("image"),
pos: aTab._tPos
});
@ -602,7 +633,6 @@ SessionStoreService.prototype = {
}
delete aPanel.__SS_data;
delete aPanel.__SS_text;
this.saveStateDelayed(aWindow);
// attempt to update the current URL we send in a crash report
@ -611,18 +641,25 @@ SessionStoreService.prototype = {
/**
* Called when a tabpanel sends the "input" notification
* stores textarea data
* @param aWindow
* Window reference
* @param aPanel
* TabPanel reference
* @param aEvent
* Event obj
*/
onTabInput: function sss_onTabInput(aWindow, aPanel, aEvent) {
if (this._saveTextData(aPanel, aEvent.originalTarget)) {
this.saveStateDelayed(aWindow, 3000);
}
onTabInput: function sss_onTabInput(aWindow, aPanel) {
if (aPanel.__SS_data)
delete aPanel.__SS_data._formDataSaved;
this.saveStateDelayed(aWindow, 3000);
},
/**
* Called when a tabpanel sends a "scroll" notification
* @param aWindow
* Window reference
*/
onTabScroll: function sss_onTabScroll(aWindow) {
this.saveStateDelayed(aWindow, 3000);
},
/**
@ -688,7 +725,7 @@ SessionStoreService.prototype = {
setTabState: function sss_setTabState(aTab, aState) {
var tabState = this._safeEval("(" + aState + ")");
if (!tabState.entries || !tabState.entries.length) {
if (!tabState.entries) {
Components.returnCode = Cr.NS_ERROR_INVALID_ARG;
return;
}
@ -857,7 +894,7 @@ SessionStoreService.prototype = {
* @returns object
*/
_collectTabData: function sss_collectTabData(aTab, aFullData) {
var tabData = { entries: [], index: 0 };
var tabData = { entries: [] };
var browser = aTab.linkedBrowser;
if (!browser || !browser.currentURI)
@ -888,7 +925,8 @@ SessionStoreService.prototype = {
if (!aFullData)
browser.parentNode.__SS_data = tabData;
}
else {
else if (browser.currentURI.spec != "about:blank" ||
browser.contentDocument.body.hasChildNodes()) {
tabData.entries[0] = { url: browser.currentURI.spec };
tabData.index = 1;
}
@ -1031,56 +1069,6 @@ SessionStoreService.prototype = {
return entry;
},
/**
* Updates the current document's cache of user entered text data
* @param aPanel
* TabPanel reference
* @param aTextarea
* HTML content element
* @returns bool
*/
_saveTextData: function sss_saveTextData(aPanel, aTextarea) {
var id = aTextarea.id ? "#" + aTextarea.id :
aTextarea.name;
if (!id
|| !(aTextarea instanceof Ci.nsIDOMHTMLTextAreaElement
|| aTextarea instanceof Ci.nsIDOMHTMLInputElement)) {
return false; // nothing to save
}
if (!aPanel.__SS_text) {
aPanel.__SS_text = [];
aPanel.__SS_text._refs = [];
}
// get the index of the reference to the text element
var ix = aPanel.__SS_text._refs.indexOf(aTextarea);
if (ix == -1) {
// we haven't registered this text element yet - do so now
aPanel.__SS_text._refs.push(aTextarea);
ix = aPanel.__SS_text.length;
}
else if (!aPanel.__SS_text[ix].cache) {
// we've already marked this text element for saving (the cache is
// added during save operations and would have to be updated here)
return false;
}
// determine the frame we're in and encode it into the textarea's ID
var content = aTextarea.ownerDocument.defaultView;
while (content != content.top) {
var frames = content.parent.frames;
for (var i = 0; i < frames.length && frames[i] != content; i++);
id = i + "|" + id;
content = content.parent;
}
// mark this element for saving
aPanel.__SS_text[ix] = { id: id, element: aTextarea };
return true;
},
/**
* go through all tabs and store the current scroll positions
* and innerHTML content of WYSIWYG editors
@ -1092,8 +1080,7 @@ SessionStoreService.prototype = {
for (var i = 0; i < browsers.length; i++) {
try {
var tabData = this._windows[aWindow.__SSi].tabs[i];
if (tabData.entries.length == 0 ||
browsers[i].parentNode.__SS_data && browsers[i].parentNode.__SS_data._tab)
if (browsers[i].parentNode.__SS_data && browsers[i].parentNode.__SS_data._tab)
continue; // ignore incompletely initialized tabs
this._updateTextAndScrollDataForTab(aWindow, browsers[i], tabData);
}
@ -1115,66 +1102,102 @@ SessionStoreService.prototype = {
*/
_updateTextAndScrollDataForTab:
function sss_updateTextAndScrollDataForTab(aWindow, aBrowser, aTabData, aFullData) {
var text = [];
if (aBrowser.parentNode.__SS_text &&
(aFullData || this._checkPrivacyLevel(aBrowser.currentURI.schemeIs("https")))) {
for (var ix = aBrowser.parentNode.__SS_text.length - 1; ix >= 0; ix--) {
var data = aBrowser.parentNode.__SS_text[ix];
if (!data.cache)
// update the text element's value before adding it to the data structure
data.cache = encodeURI(data.element.value);
text.push(data.id + "=" + data.cache);
}
}
if (aBrowser.currentURI.spec == "about:config")
text = ["#textbox=" + encodeURI(aBrowser.contentDocument.getElementById("textbox").
wrappedJSObject.value)];
if (text.length > 0)
aTabData.text = text.join(" ");
else if (aTabData.text)
delete aTabData.text;
var tabIndex = (aTabData.index || aTabData.entries.length) - 1;
// entry data needn't exist for tabs just initialized with an incomplete session state
if (aTabData.entries[tabIndex])
this._updateTextAndScrollDataForFrame(aWindow, aBrowser.contentWindow,
aTabData.entries[tabIndex], aFullData);
if (!aTabData.entries[tabIndex])
return;
this._updateTextAndScrollDataForFrame(aWindow, aBrowser.contentWindow,
aTabData.entries[tabIndex],
!aTabData._formDataSaved, aFullData);
aTabData._formDataSaved = true;
if (aBrowser.currentURI.spec == "about:config")
aTabData.entries[tabIndex].formdata = {
"#textbox": aBrowser.contentDocument.getElementById("textbox").wrappedJSObject.value
};
},
/**
* go through all subframes and store the current scroll positions
* and innerHTML content of WYSIWYG editors
* go through all subframes and store all form data, the current
* scroll positions and innerHTML content of WYSIWYG editors
* @param aWindow
* Window reference
* @param aContent
* frame reference
* @param aData
* part of a tabData object to add the information to
* @param aUpdateFormData
* update all form data for this tab
* @param aFullData
* always return privacy sensitive data (use with care)
*/
_updateTextAndScrollDataForFrame:
function sss_updateTextAndScrollDataForFrame(aWindow, aContent, aData, aFullData) {
function sss_updateTextAndScrollDataForFrame(aWindow, aContent, aData,
aUpdateFormData, aFullData) {
for (var i = 0; i < aContent.frames.length; i++) {
if (aData.children && aData.children[i])
this._updateTextAndScrollDataForFrame(aWindow, aContent.frames[i], aData.children[i], aFullData);
this._updateTextAndScrollDataForFrame(aWindow, aContent.frames[i],
aData.children[i], aUpdateFormData, aFullData);
}
// designMode is undefined e.g. for XUL documents (as about:config)
var isHTTPS = this._getURIFromString((aContent.parent || aContent).
document.location.href).schemeIs("https");
if ((aContent.document.designMode || "") == "on" &&
(aFullData || this._checkPrivacyLevel(isHTTPS))) {
if (aData.innerHTML === undefined && !aFullData) {
// we get no "input" events from iframes - listen for keypress here
var _this = this;
aContent.addEventListener("keypress", function(aEvent) {
_this.saveStateDelayed(aWindow, 3000); }, true);
if (aFullData || this._checkPrivacyLevel(isHTTPS)) {
if (aFullData || aUpdateFormData) {
let formData = this._collectFormDataForFrame(aContent.document);
if (formData)
aData.formdata = formData;
else if (aData.formdata)
delete aData.formdata;
}
// designMode is undefined e.g. for XUL documents (as about:config)
if ((aContent.document.designMode || "") == "on") {
if (aData.innerHTML === undefined && !aFullData) {
// we get no "input" events from iframes - listen for keypress here
let _this = this;
aContent.addEventListener("keypress", function(aEvent) {
_this.saveStateDelayed(aWindow, 3000);
}, true);
}
aData.innerHTML = aContent.document.body.innerHTML;
}
aData.innerHTML = aContent.document.body.innerHTML;
}
aData.scroll = aContent.scrollX + "," + aContent.scrollY;
},
/**
* collect the state of all form elements
* @param aDocument
* document reference
*/
_collectFormDataForFrame: function sss_collectFormDataForFrame(aDocument) {
let formNodesXPath = "//textarea|//select|//xhtml:textarea|//xhtml:select|" +
"//input[not(@type) or @type='text' or @type='checkbox' or @type='radio' or @type='file']|" +
"//xhtml:input[not(@type) or @type='text' or @type='checkbox' or @type='radio' or @type='file']";
let formNodes = aDocument.evaluate(formNodesXPath, aDocument, XPathHelper.resolveNS,
Ci.nsIDOMXPathResult.UNORDERED_NODE_ITERATOR_TYPE, null);
let node = formNodes.iterateNext();
if (!node)
return null;
let data = {};
do {
let id = node.id ? "#" + node.id : XPathHelper.generate(node);
if (node instanceof Ci.nsIDOMHTMLInputElement)
data[id] = node.type == "checkbox" || node.type == "radio" ? node.checked : node.value;
else if (node instanceof Ci.nsIDOMHTMLTextAreaElement)
data[id] = node.value;
else if (!node.multiple)
data[id] = node.selectedIndex;
else {
let options = Array.map(node.options, function(aOpt, aIx) aOpt.selected ? aIx : -1);
data[id] = options.filter(function(aIx) aIx >= 0);
}
} while ((node = formNodes.iterateNext()));
return data;
},
/**
* store all hosts for a URL
* @param aWindow
@ -1277,15 +1300,17 @@ SessionStoreService.prototype = {
/**
* serialize session data as Ini-formatted string
* @param aUpdateAll
* Bool update all windows
* @returns string
*/
_getCurrentState: function sss_getCurrentState() {
_getCurrentState: function sss_getCurrentState(aUpdateAll) {
var activeWindow = this._getMostRecentBrowserWindow();
if (this._loadState == STATE_RUNNING) {
// update the data for all windows with activities since the last save operation
this._forEachBrowserWindow(function(aWindow) {
if (this._dirty || this._dirtyWindows[aWindow.__SSi] || aWindow == activeWindow) {
if (aUpdateAll || this._dirtyWindows[aWindow.__SSi] || aWindow == activeWindow) {
this._collectWindowData(aWindow);
}
else { // always update the window features (whose change alone never triggers a save operation)
@ -1293,21 +1318,25 @@ SessionStoreService.prototype = {
}
}, this);
this._dirtyWindows = [];
this._dirty = false;
}
// collect the data for all windows
var total = [], windows = [];
var nonPopupCount = 0;
var ix;
for (ix in this._windows) {
total.push(this._windows[ix]);
windows.push(ix);
if (!this._windows[ix].isPopup)
nonPopupCount++;
}
this._updateCookies(total);
// if no browser window remains open, return the state of the last closed window
if (total.length == 0 && this._lastWindowClosed) {
total.push(this._lastWindowClosed);
// if no non-popup browser window remains open, return the state of the last closed window(s)
if (nonPopupCount == 0 && this._lastClosedWindows) {
// prepend the last non-popup browser window, so that if the user loads more tabs
// at startup we don't accidentally add them to a popup window
total = this._lastClosedWindows.concat(total);
}
if (activeWindow) {
this.activeWindowSSiCache = activeWindow.__SSi || "";
@ -1407,11 +1436,9 @@ SessionStoreService.prototype = {
}
// don't restore a single blank tab when we've had an external
// URL passed in for loading at startup (cf. bug 357419)
else if (root._firstTabs && !aOverwriteTabs && winData.tabs.length == 1) {
let tabEntries = winData.tabs[0].entries || [];
if (tabEntries.length == 0 ||
tabEntries.length == 1 && tabEntries[0].url == "about:blank")
winData.tabs = [];
else if (root._firstTabs && !aOverwriteTabs && winData.tabs.length == 1 &&
(!winData.tabs[0].entries || winData.tabs[0].entries.length == 0)) {
winData.tabs = [];
}
var tabbrowser = aWindow.getBrowser();
@ -1490,17 +1517,28 @@ SessionStoreService.prototype = {
// mark the tabs as loading
for (t = 0; t < aTabs.length; t++) {
if (!aTabs[t].entries || !aTabs[t].entries[0])
continue; // there won't be anything to load
var tab = aTabs[t]._tab;
var browser = tabbrowser.getBrowserForTab(tab);
if (!aTabs[t].entries || aTabs[t].entries.length == 0) {
// make sure to blank out this tab's content
// (just purging the tab's history won't be enough)
browser.contentDocument.location = "about:blank";
continue;
}
browser.stop(); // in case about:blank isn't done yet
tab.setAttribute("busy", "true");
tabbrowser.updateIcon(tab);
tabbrowser.setTabTitleLoading(tab);
// wall-paper fix for bug 439675: make sure that the URL to be loaded
// is always visible in the address bar
let activeIndex = (aTabs[t].index || aTabs[t].entries.length) - 1;
let activePageData = aTabs[t].entries[activeIndex] || null;
browser.userTypedValue = activePageData ? activePageData.url || null : null;
// keep the data around to prevent dataloss in case
// a tab gets closed before it's been properly restored
browser.parentNode.__SS_data = aTabs[t];
@ -1581,20 +1619,28 @@ SessionStoreService.prototype = {
event.initEvent("SSTabRestoring", true, false);
tab.dispatchEvent(event);
var activeIndex = (tabData.index || tabData.entries.length) - 1;
let activeIndex = (tabData.index || tabData.entries.length) - 1;
if (activeIndex >= tabData.entries.length)
activeIndex = tabData.entries.length - 1;
try {
browser.webNavigation.gotoIndex(activeIndex);
if (activeIndex >= 0)
browser.webNavigation.gotoIndex(activeIndex);
}
catch (ex) {
// ignore page load errors
tab.removeAttribute("busy");
}
catch (ex) { } // ignore an invalid tabData.index
// restore those aspects of the currently active documents
// which are not preserved in the plain history entries
// (mainly scroll state and text data)
browser.__SS_restore_data = tabData.entries[activeIndex] || {};
browser.__SS_restore_text = tabData.text || "";
browser.__SS_restore_tab = tab;
browser.__SS_restore = this.restoreDocument_proxy;
browser.addEventListener("load", browser.__SS_restore, true);
if (tabData.entries.length > 0) {
// restore those aspects of the currently active documents
// which are not preserved in the plain history entries
// (mainly scroll state and text data)
browser.__SS_restore_data = tabData.entries[activeIndex] || {};
browser.__SS_restore_text = tabData.text || "";
browser.__SS_restore_tab = tab;
browser.__SS_restore = this.restoreDocument_proxy;
browser.addEventListener("load", browser.__SS_restore, true);
}
aWindow.setTimeout(function(){ _this.restoreHistory(aWindow, aTabs, aIdMap); }, 0);
},
@ -1696,6 +1742,7 @@ SessionStoreService.prototype = {
return;
}
// restore text data saved by Firefox 2.0/3.0
var textArray = this.__SS_restore_text ? this.__SS_restore_text.split(" ") : [];
function restoreTextData(aContent, aPrefix) {
textArray.forEach(function(aEntry) {
@ -1713,8 +1760,41 @@ SessionStoreService.prototype = {
});
}
function restoreFormData(aDocument, aData) {
for (let key in aData) {
let node = key.charAt(0) == "#" ? aDocument.getElementById(key.slice(1)) :
XPathHelper.resolve(aDocument, key);
if (!node)
continue;
let value = aData[key];
if (typeof value == "string") {
node.value = value;
let event = aDocument.createEvent("UIEvents");
event.initUIEvent("input", true, true, aDocument.defaultView, 0);
node.dispatchEvent(event);
}
else if (typeof value == "boolean")
node.checked = value;
else if (typeof value == "number")
try {
node.selectedIndex = value;
} catch (ex) { /* throws for invalid indices */ }
else if (value && typeof value.indexOf == "function" && node.options) {
Array.forEach(node.options, function(aOpt, aIx) {
aOpt.selected = value.indexOf(aIx) > -1;
});
}
// NB: dispatching "change" events might have unintended side-effects
}
}
function restoreTextDataAndScrolling(aContent, aData, aPrefix) {
restoreTextData(aContent, aPrefix);
if (aData.formdata)
restoreFormData(aContent.document, aData.formdata);
else
restoreTextData(aContent, aPrefix);
if (aData.innerHTML) {
aContent.setTimeout(function(aHTML) { if (this.document.designMode == "on") { this.document.body.innerHTML = aHTML; } }, 0, aData.innerHTML);
}
@ -1728,18 +1808,23 @@ SessionStoreService.prototype = {
}
}
var content = aEvent.originalTarget.defaultView;
if (this.currentURI.spec == "about:config") {
// unwrap the document for about:config because otherwise the properties
// of the XBL bindings - as the textbox - aren't accessible (see bug 350718)
content = content.wrappedJSObject;
// don't restore text data and scrolling state if the user has navigated
// away before the loading completed (except for in-page navigation)
if (!this.__SS_restore_data.url || this.currentURI.spec.replace(/#.*/, "") ==
this.__SS_restore_data.url.replace(/#.*/, "")) {
var content = aEvent.originalTarget.defaultView;
if (this.currentURI.spec == "about:config") {
// unwrap the document for about:config because otherwise the properties
// of the XBL bindings - as the textbox - aren't accessible (see bug 350718)
content = content.wrappedJSObject;
}
restoreTextDataAndScrolling(content, this.__SS_restore_data, "");
// notify the tabbrowser that this document has been completely restored
var event = this.ownerDocument.createEvent("Events");
event.initEvent("SSTabRestored", true, false);
this.__SS_restore_tab.dispatchEvent(event);
}
restoreTextDataAndScrolling(content, this.__SS_restore_data, "");
// notify the tabbrowser that this document has been completely restored
var event = this.ownerDocument.createEvent("Events");
event.initEvent("SSTabRestored", true, false);
this.__SS_restore_tab.dispatchEvent(event);
this.removeEventListener("load", this.__SS_restore, true);
delete this.__SS_restore_data;
@ -1761,6 +1846,11 @@ SessionStoreService.prototype = {
aWindow[aItem].visible = hidden.indexOf(aItem) == -1;
});
if (aWinData.isPopup)
this._windows[aWindow.__SSi].isPopup = true;
else
delete this._windows[aWindow.__SSi].isPopup;
var _this = this;
aWindow.setTimeout(function() {
_this.restoreDimensions.apply(_this, [aWindow, aWinData.width || 0,
@ -1810,7 +1900,7 @@ SessionStoreService.prototype = {
// since resizing/moving a window brings it to the foreground,
// we might want to re-focus the last focused window
if (this.windowToFocus) {
this.windowToFocus.focus();
this.windowToFocus.content.focus();
}
},
@ -1889,8 +1979,7 @@ SessionStoreService.prototype = {
if (!this._resume_from_crash && this._loadState == STATE_RUNNING)
return;
this._dirty = aUpdateAll;
var oState = this._getCurrentState();
var oState = this._getCurrentState(aUpdateAll);
oState.session = { state: ((this._loadState == STATE_RUNNING) ? STATE_RUNNING_STR : STATE_STOPPED_STR) };
var stateString = Cc["@mozilla.org/supports-string;1"].
@ -2119,7 +2208,7 @@ SessionStoreService.prototype = {
* @return the object's JSON representation
*/
_toJSONString: function sss_toJSONString(aJSObject) {
var str = JSON.toString(aJSObject, ["_tab", "_hosts"] /* keys to drop */);
let str = JSON.toString(aJSObject, ["_tab", "_hosts", "_formDataSaved"] /* keys to drop */);
// sanity check - so that API consumers can just eval this string
if (!JSON.isMostlyHarmless(str))
@ -2160,5 +2249,65 @@ SessionStoreService.prototype = {
}
};
let XPathHelper = {
// these two hashes should be kept in sync
namespaceURIs: { "xhtml": "http://www.w3.org/1999/xhtml" },
namespacePrefixes: { "http://www.w3.org/1999/xhtml": "xhtml" },
/**
* Generates an approximate XPath query to an (X)HTML node
*/
generate: function sss_xph_generate(aNode) {
// have we reached the document node already?
if (!aNode.parentNode)
return "";
let prefix = this.namespacePrefixes[aNode.namespaceURI] || null;
let tag = (prefix ? prefix + ":" : "") + aNode.localName;
// stop once we've found a tag with an ID
if (aNode.id)
return "//" + tag + "[@id=" + this.quoteArgument(aNode.id) + "]";
// count the number of previous sibling nodes of the same tag
// (and possible also the same name)
let count = 0;
let nName = aNode.name || null;
for (let n = aNode; (n = n.previousSibling); )
if (n.localName == aNode.localName && n.namespaceURI == aNode.namespaceURI &&
(!nName || n.name == nName))
count++;
// recurse until hitting either the document node or an ID'd node
return this.generate(aNode.parentNode) + "/" + tag +
(nName ? "[@name=" + this.quoteArgument(nName) + "]" : "") +
(count ? "[" + (count + 1) + "]" : "");
},
/**
* Resolves an XPath query generated by XPathHelper.generate
*/
resolve: function sss_xph_resolve(aDocument, aQuery) {
let xptype = Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE;
return aDocument.evaluate(aQuery, aDocument, this.resolveNS, xptype, null).singleNodeValue;
},
/**
* Namespace resolver for the above XPath resolver
*/
resolveNS: function sss_xph_resolveNS(aPrefix) {
return XPathHelper.namespaceURIs[aPrefix] || null;
},
/**
* @returns a properly quoted string to insert into an XPath query
*/
quoteArgument: function sss_xph_quoteArgument(aArg) {
return !/'/.test(aArg) ? "'" + aArg + "'" :
!/"/.test(aArg) ? '"' + aArg + '"' :
"concat('" + aArg.replace(/'+/g, "',\"$&\",'") + "')";
}
};
function NSGetModule(aComMgr, aFileSpec)
XPCOMUtils.generateModule([SessionStoreService]);

View File

@ -45,7 +45,9 @@ include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
_BROWSER_TEST_FILES = \
browser_346337.js \
browser_350525.js \
browser_367052.js \
browser_393716.js \
browser_448741.js \
$(NULL)

View File

@ -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 sessionstore test code.
*
* The Initial Developer of the Original Code is
* Simon Bünzli <zeniko@gmail.com>.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
function test() {
/** Test for Bug 346337 **/
let fieldList = {
"//input[@name='testinput']": Date.now().toString(),
"//input[@name='bad name']": Math.random().toString(),
"//input[@type='checkbox']": true,
"//input[@type='radio'][1]": false,
"//input[@type='radio'][2]": true,
"//select": 2,
"//select[@multiple]": [1, 3],
"//textarea[1]": "",
"//textarea[3]": "Some more test\n" + new Date()
};
function getElementByXPath(aTab, aQuery) {
let doc = aTab.linkedBrowser.contentDocument;
let xptype = Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE;
return doc.evaluate(aQuery, doc, null, xptype, null).singleNodeValue;
}
function setFormValue(aTab, aQuery, aValue) {
let node = getElementByXPath(aTab, aQuery);
if (typeof aValue == "string")
node.value = aValue;
else if (typeof aValue == "boolean")
node.checked = aValue;
else if (typeof aValue == "number")
node.selectedIndex = aValue;
else
Array.forEach(node.options, function(aOpt, aIx)
(aOpt.selected = aValue.indexOf(aIx) > -1));
}
function compareFormValue(aTab, aQuery, aValue) {
let node = getElementByXPath(aTab, aQuery);
if (!node)
return false;
if (node instanceof Ci.nsIDOMHTMLInputElement)
return aValue == (node.type == "checkbox" || node.type == "radio" ?
node.checked : node.value);
if (node instanceof Ci.nsIDOMHTMLTextAreaElement)
return aValue == node.value;
if (!node.multiple)
return aValue == node.selectedIndex;
return Array.every(node.options, function(aOpt, aIx)
(aValue.indexOf(aIx) > -1) == aOpt.selected);
}
// test setup
let tabbrowser = getBrowser();
waitForExplicitFinish();
// make sure we don't save form data at all (except for tab duplication)
let privacy_level = gPrefService.getIntPref("browser.sessionstore.privacy_level");
gPrefService.setIntPref("browser.sessionstore.privacy_level", 2);
todo(false, "test doesn't run from the harness's http server");
let tab = tabbrowser.addTab("https://bugzilla.mozilla.org/attachment.cgi?id=328502");
tab.linkedBrowser.addEventListener("load", function(aEvent) {
for (let xpath in fieldList)
setFormValue(tab, xpath, fieldList[xpath]);
let tab2 = tabbrowser.duplicateTab(tab);
tab2.linkedBrowser.addEventListener("load", function(aEvent) {
for (let xpath in fieldList)
ok(compareFormValue(tab2, xpath, fieldList[xpath]),
"The value for \"" + xpath + "\" was correctly restored");
// clean up
tabbrowser.removeTab(tab2);
tabbrowser.removeTab(tab);
undoCloseTab();
tab = tabbrowser.selectedTab;
tab.linkedBrowser.addEventListener("load", function(aEvent) {
for (let xpath in fieldList)
if (fieldList[xpath])
ok(!compareFormValue(tab, xpath, fieldList[xpath]),
"The value for \"" + xpath + "\" was correctly discarded");
gPrefService.setIntPref("browser.sessionstore.privacy_level", privacy_level);
tabbrowser.removeTab(tab);
finish();
}, true);
}, true);
}, true);
}

View File

@ -1,6 +1,4 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* ***** BEGIN LICENSE BLOCK *****
/* ***** 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
@ -13,15 +11,14 @@
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
* The Original Code is sessionstore test code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2001
* Simon Bünzli <zeniko@gmail.com>.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Stuart Parmenter <pavlov@netscape.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
@ -37,46 +34,39 @@
*
* ***** END LICENSE BLOCK ***** */
#include "imgICache.h"
#include "nsIObserver.h"
#include "nsWeakReference.h"
#include "prtypes.h"
class imgRequest;
class nsIURI;
class nsICacheEntryDescriptor;
#define NS_IMGCACHE_CID \
{ /* fb4fd28a-1dd1-11b2-8391-e14242c59a41 */ \
0xfb4fd28a, \
0x1dd1, \
0x11b2, \
{0x83, 0x91, 0xe1, 0x42, 0x42, 0xc5, 0x9a, 0x41} \
function test() {
/** Test for Bug 367052 **/
// test setup
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
let tabbrowser = gBrowser;
waitForExplicitFinish();
// make sure that the next closed tab will increase getClosedTabCount
let max_tabs_undo = gPrefService.getIntPref("browser.sessionstore.max_tabs_undo");
gPrefService.setIntPref("browser.sessionstore.max_tabs_undo", max_tabs_undo + 1);
let closedTabCount = ss.getClosedTabCount(window);
// restore a blank tab
let tab = tabbrowser.addTab("about:");
tab.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);
let browser = tabbrowser.getBrowserForTab(tab);
let history = browser.webNavigation.sessionHistory;
ok(history.count >= 1, "the new tab does have at least one history entry");
ss.setTabState(tab, "{ entries: [] }");
tab.linkedBrowser.addEventListener("load", function(aEvent) {
ok(history.count == 0, "the tab was restored without any history whatsoever");
tabbrowser.removeTab(tab);
ok(ss.getClosedTabCount(window) == closedTabCount,
"The closed blank tab wasn't added to Recently Closed Tabs");
// clean up
gPrefService.setIntPref("browser.sessionstore.max_tabs_undo", max_tabs_undo);
finish();
}, true);
}, true);
}
class imgCache : public imgICache,
public nsIObserver,
public nsSupportsWeakReference
{
public:
NS_DECL_ISUPPORTS
NS_DECL_IMGICACHE
NS_DECL_NSIOBSERVER
imgCache();
virtual ~imgCache();
static nsresult Init();
static void Shutdown(); // for use by the factory
/* additional members */
static PRBool Put(nsIURI *aKey, imgRequest *request, nsICacheEntryDescriptor **aEntry);
static PRBool Get(nsIURI *aKey, PRBool *aHasExpired, imgRequest **aRequest, nsICacheEntryDescriptor **aEntry);
static PRBool Remove(nsIURI *aKey);
static nsresult ClearChromeImageCache();
static nsresult ClearImageCache();
};

View File

@ -44,7 +44,7 @@ include $(DEPTH)/config/autoconf.mk
DIRS = public src
ifdef MOZ_MOCHITEST
ifdef ENABLE_TESTS
DIRS += test
endif

View File

@ -63,6 +63,8 @@
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtkversion.h>
#include <gdk/gdk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <limits.h>
#include <stdlib.h>
@ -446,58 +448,8 @@ nsGNOMEShellService::SetDesktopBackground(nsIDOMElement* aElement,
return rv;
}
// In: pointer to two characters CC
// Out: parsed color number
static PRUint8
HexToNum(char ch)
{
if ('0' <= ch && '9' >= ch)
return ch - '0';
if ('A' <= ch && 'F' >= ch)
return ch - 'A';
if ('a' <= ch && 'f' >= ch)
return ch - 'a';
return 0;
}
// In: 3 or 6-character RRGGBB hex string
// Out: component colors
static PRBool
HexToRGB(const nsCString& aColorSpec,
PRUint8 &aRed,
PRUint8 &aGreen,
PRUint8 &aBlue)
{
const char *buf = aColorSpec.get();
if (aColorSpec.Length() == 6) {
aRed = HexToNum(buf[0]) >> 4 |
HexToNum(buf[1]);
aGreen = HexToNum(buf[2]) >> 4 |
HexToNum(buf[3]);
aBlue = HexToNum(buf[4]) >> 4 |
HexToNum(buf[5]);
return PR_TRUE;
}
if (aColorSpec.Length() == 3) {
aRed = HexToNum(buf[0]);
aGreen = HexToNum(buf[1]);
aBlue = HexToNum(buf[2]);
aRed |= aRed >> 4;
aGreen |= aGreen >> 4;
aBlue |= aBlue >> 4;
return PR_TRUE;
}
return PR_FALSE;
}
#define COLOR_16_TO_8_BIT(_c) ((_c) >> 8)
#define COLOR_8_TO_16_BIT(_c) ((_c) << 8)
NS_IMETHODIMP
nsGNOMEShellService::GetDesktopBackgroundColor(PRUint32 *aColor)
@ -512,22 +464,34 @@ nsGNOMEShellService::GetDesktopBackgroundColor(PRUint32 *aColor)
return NS_OK;
}
// Chop off the leading '#' character
background.Cut(0, 1);
GdkColor color;
gboolean success = gdk_color_parse(background.get(), &color);
PRUint8 red, green, blue;
if (!HexToRGB(background, red, green, blue))
return NS_ERROR_FAILURE;
NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
// The result must be in RGB order with the high 8 bits zero.
*aColor = (red << 16 | green << 8 | blue);
*aColor = COLOR_16_TO_8_BIT(color.red) << 16 |
COLOR_16_TO_8_BIT(color.green) << 8 |
COLOR_16_TO_8_BIT(color.blue);
return NS_OK;
}
static void
ColorToHex(PRUint32 aColor, nsCString& aResult)
ColorToCString(PRUint32 aColor, nsCString& aResult)
{
char *buf = aResult.BeginWriting(7);
#if GTK_CHECK_VERSION(2,12,0)
GdkColor color;
color.red = COLOR_8_TO_16_BIT(aColor >> 16);
color.green = COLOR_8_TO_16_BIT((aColor >> 8) & 0xff);
color.blue = COLOR_8_TO_16_BIT(aColor & 0xff);
gchar *colorString = gdk_color_to_string(&color);
aResult.Assign(colorString);
g_free(colorString);
#else // GTK 2.12.0
// The #rrrrggggbbbb format is used to match gdk_color_to_string()
char *buf = aResult.BeginWriting(13);
if (!buf)
return;
@ -535,7 +499,8 @@ ColorToHex(PRUint32 aColor, nsCString& aResult)
PRUint8 green = (aColor >> 8) & 0xff;
PRUint8 blue = aColor & 0xff;
PR_snprintf(buf, 8, "#%02x%02x%02x", red, green, blue);
PR_snprintf(buf, 14, "#%02x00%02x00%02x00", red, green, blue);
#endif // GTK 2.12.0
}
NS_IMETHODIMP
@ -544,7 +509,7 @@ nsGNOMEShellService::SetDesktopBackgroundColor(PRUint32 aColor)
nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID);
nsCString colorString;
ColorToHex(aColor, colorString);
ColorToCString(aColor, colorString);
gconf->SetString(NS_LITERAL_CSTRING(kDesktopColorKey), colorString);

View File

@ -42,6 +42,9 @@ relativesrcdir = browser/components/shell/test
include $(DEPTH)/config/autoconf.mk
MODULE = test_browser_shell
XPCSHELL_TESTS = unit
include $(topsrcdir)/config/rules.mk
_BROWSER_TEST_FILES = browser_420786.js \

View File

@ -0,0 +1,114 @@
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const GCONF_BG_COLOR_KEY = "/desktop/gnome/background/primary_color";
var gShell;
var gGConf;
/**
* Converts from a rgb numerical color valule (r << 16 | g << 8 | b)
* into a hex string in #RRGGBB format.
*/
function colorToHex(aColor) {
const rMask = 4294901760;
const gMask = 65280;
const bMask = 255;
var r = (aColor & rMask) >> 16;
var g = (aColor & gMask) >> 8;
var b = (aColor & bMask);
return "#" + [r, g, b].map(function(aInt)
aInt.toString(16).replace(/^(.)$/, "0$1"))
.join("").toUpperCase();
}
/**
* Converts a color string in #RRGGBB format to a rgb numerical color value
* (r << 16 | g << 8 | b).
*/
function hexToColor(aString) {
return parseInt(aString.substring(1,3), 16) << 16 |
parseInt(aString.substring(3,5), 16) << 8 |
parseInt(aString.substring(5,7), 16);
}
/**
* Checks that setting the GConf background key to aGConfColor will
* result in the Shell component returning a background color equals
* to aExpectedShellColor in #RRGGBB format.
*/
function checkGConfToShellColor(aGConfColor, aExpectedShellColor) {
gGConf.setString(GCONF_BG_COLOR_KEY, aGConfColor);
var shellColor = colorToHex(gShell.desktopBackgroundColor);
do_check_eq(shellColor, aExpectedShellColor);
}
/**
* Checks that setting the background color (in #RRGGBB format) using the Shell
* component will result in having a GConf key for the background color set to
* aExpectedGConfColor.
*/
function checkShellToGConfColor(aShellColor, aExpectedGConfColor) {
gShell.desktopBackgroundColor = hexToColor(aShellColor);
var gconfColor = gGConf.getString(GCONF_BG_COLOR_KEY);
do_check_eq(gconfColor, aExpectedGConfColor);
}
function run_test() {
// This test is Linux specific for now
if (!("@mozilla.org/gnome-gconf-service;1" in Cc))
return;
gGConf = Cc["@mozilla.org/gnome-gconf-service;1"].
getService(Ci.nsIGConfService);
gShell = Cc["@mozilla.org/browser/shell-service;1"].
getService(Ci.nsIShellService);
// Save the original background color so that we can restore it
// after the test.
var origGConfColor = gGConf.getString(GCONF_BG_COLOR_KEY);
try {
checkGConfToShellColor("#000", "#000000");
checkGConfToShellColor("#00f", "#0000FF");
checkGConfToShellColor("#b2f", "#BB22FF");
checkGConfToShellColor("#fff", "#FFFFFF");
checkGConfToShellColor("#000000", "#000000");
checkGConfToShellColor("#0000ff", "#0000FF");
checkGConfToShellColor("#b002f0", "#B002F0");
checkGConfToShellColor("#ffffff", "#FFFFFF");
checkGConfToShellColor("#000000000", "#000000");
checkGConfToShellColor("#00f00f00f", "#000000");
checkGConfToShellColor("#aaabbbccc", "#AABBCC");
checkGConfToShellColor("#fffffffff", "#FFFFFF");
checkGConfToShellColor("#000000000000", "#000000");
checkGConfToShellColor("#000f000f000f", "#000000");
checkGConfToShellColor("#00ff00ff00ff", "#000000");
checkGConfToShellColor("#aaaabbbbcccc", "#AABBCC");
checkGConfToShellColor("#111122223333", "#112233");
checkGConfToShellColor("#ffffffffffff", "#FFFFFF");
checkShellToGConfColor("#000000", "#000000000000");
checkShellToGConfColor("#0000FF", "#00000000ff00");
checkShellToGConfColor("#FFFFFF", "#ff00ff00ff00");
checkShellToGConfColor("#0A0B0C", "#0a000b000c00");
checkShellToGConfColor("#A0B0C0", "#a000b000c000");
checkShellToGConfColor("#AABBCC", "#aa00bb00cc00");
} finally {
gGConf.setString(GCONF_BG_COLOR_KEY, origGConfColor);
}
}

View File

@ -98,11 +98,11 @@ function (aTitle, aContentURL, aCustomizeURL)
{
debug("addPanel(" + aTitle + ", " + aContentURL + ", " +
aCustomizeURL + ")");
return this.addPanelInternal(aTitle, aContentURL, aCustomizeURL, false);
}
nsSidebar.prototype.addPersistentPanel =
nsSidebar.prototype.addPersistentPanel =
function(aTitle, aContentURL, aCustomizeURL)
{
debug("addPersistentPanel(" + aTitle + ", " + aContentURL + ", " +
@ -272,12 +272,13 @@ nsSidebar.prototype.getHelperForLanguage = function(count) {return null;}
nsSidebar.prototype.QueryInterface =
function (iid) {
if (!iid.equals(nsISidebar) &&
!iid.equals(nsISidebarExternal) &&
!iid.equals(nsIClassInfo) &&
!iid.equals(nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
if (iid.equals(nsISidebar) ||
iid.equals(nsISidebarExternal) ||
iid.equals(nsIClassInfo) ||
iid.equals(nsISupports))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
}
var sidebarModule = new Object();
@ -288,24 +289,25 @@ function (compMgr, fileSpec, location, type)
debug("registering (all right -- a JavaScript module!)");
compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
compMgr.registerFactoryLocation(SIDEBAR_CID,
compMgr.registerFactoryLocation(SIDEBAR_CID,
"Sidebar JS Component",
SIDEBAR_CONTRACTID,
fileSpec,
SIDEBAR_CONTRACTID,
fileSpec,
location,
type);
const CATMAN_CONTRACTID = "@mozilla.org/categorymanager;1";
const nsICategoryManager = Components.interfaces.nsICategoryManager;
var catman = Components.classes[CATMAN_CONTRACTID].
getService(nsICategoryManager);
const JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY = "JavaScript global property";
catman.addCategoryEntry(JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY,
"sidebar",
SIDEBAR_CONTRACTID,
true,
true);
catman.addCategoryEntry(JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY,
"external",
SIDEBAR_CONTRACTID,
@ -317,10 +319,10 @@ sidebarModule.getClassObject =
function (compMgr, cid, iid) {
if (!cid.equals(SIDEBAR_CID))
throw Components.results.NS_ERROR_NO_INTERFACE;
if (!iid.equals(Components.interfaces.nsIFactory))
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
return sidebarFactory;
}
@ -330,7 +332,7 @@ function(compMgr)
debug("Unloading component.");
return true;
}
/* factory object */
var sidebarFactory = new Object();
@ -354,24 +356,15 @@ if (DEBUG)
else
debug = function (s) {}
var strBundleService = null;
// String bundle service
var gStrBundleService = null;
function srGetStrBundle(path)
{
var strBundle = null;
if (!strBundleService) {
try {
strBundleService =
Components.classes["@mozilla.org/intl/stringbundle;1"].getService();
strBundleService =
strBundleService.QueryInterface(Components.interfaces.nsIStringBundleService);
} catch (ex) {
dump("\n--** strBundleService failed: " + ex + "\n");
return null;
}
}
strBundle = strBundleService.createBundle(path);
if (!strBundle) {
dump("\n--** strBundle createInstance failed **--\n");
}
return strBundle;
if (!gStrBundleService)
gStrBundleService =
Components.classes["@mozilla.org/intl/stringbundle;1"]
.getService(Components.interfaces.nsIStringBundleService);
return gStrBundleService.createBundle(path);
}

View File

@ -2,5 +2,4 @@
# . $topsrcdir/browser/config/mozconfig
# to the top of your mozconfig file.
mk_add_options MOZ_CO_PROJECT=browser
ac_add_options --enable-application=browser

View File

@ -41,6 +41,7 @@ MOZ_UPDATER=1
MOZ_PHOENIX=1
MOZ_ENABLE_LIBXUL=1
MOZ_STATIC_BUILD_UNSUPPORTED=1
MOZ_PLACES=1
# always enabled for form history
MOZ_MORKREADER=1

View File

@ -44,7 +44,7 @@ include $(DEPTH)/config/autoconf.mk
DIRS = public src
ifdef MOZ_MOCHITEST
ifdef ENABLE_TESTS
DIRS += test
endif

View File

@ -47,6 +47,7 @@ include $(topsrcdir)/config/rules.mk
_BROWSER_FILES =browser_Application.js \
browser_ApplicationPrefs.js \
browser_ApplicationStorage.js \
browser_ApplicationQuitting.js \
browser_Browser.js \
browser_Bookmarks.js \
ContentA.html \

View File

@ -0,0 +1,21 @@
function test() {
let quitRequestObserver = {
observe: function(aSubject, aTopic, aData) {
ok(aTopic == "quit-application-requested" &&
aSubject instanceof Components.interfaces.nsISupportsPRBool,
"Received a quit request we're going to deny");
aSubject.data = true;
}
};
// ensure that we don't accidentally quit
let os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
os.addObserver(quitRequestObserver, "quit-application-requested", false);
ok(!Application.quit(), "Tried to quit - and didn't succeed");
ok(!Application.restart(), "Tried to restart - and didn't succeed");
// clean up
os.removeObserver(quitRequestObserver, "quit-application-requested", false);
}

View File

@ -672,7 +672,66 @@ Function leaveStartMenu
FunctionEnd
Function preSummary
!insertmacro createSummaryINI
; Setup the summary.ini file for the Custom Summary Page
WriteINIStr "$PLUGINSDIR\summary.ini" "Settings" NumFields "3"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Type "label"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Text "$(SUMMARY_INSTALLED_TO)"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Left "0"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Right "-1"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Top "5"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Bottom "15"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" Type "text"
; The contents of this control must be set as follows in the pre function
; ${MUI_INSTALLOPTIONS_READ} $1 "summary.ini" "Field 2" "HWND"
; SendMessage $1 ${WM_SETTEXT} 0 "STR:$INSTDIR"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" state ""
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" Left "0"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" Right "-1"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" Top "17"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" Bottom "30"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" flags "READONLY"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 3" Type "label"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 3" Text "$(SUMMARY_CLICK)"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 3" Left "0"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 3" Right "-1"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 3" Top "130"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 3" Bottom "150"
${If} "$TmpVal" == "true"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Type "label"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Text "$(SUMMARY_REBOOT_REQUIRED_INSTALL)"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Left "0"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Right "-1"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Top "35"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Bottom "45"
WriteINIStr "$PLUGINSDIR\summary.ini" "Settings" NumFields "4"
${EndIf}
ReadINIStr $0 "$PLUGINSDIR\options.ini" "Field 6" "State"
${If} "$0" == "1"
${If} "$TmpVal" == "true"
; To insert this control reset Top / Bottom for controls below this one
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Top "50"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Bottom "60"
StrCpy $0 "5"
${Else}
StrCpy $0 "4"
${EndIf}
WriteINIStr "$PLUGINSDIR\summary.ini" "Field $0" Type "label"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field $0" Text "$(SUMMARY_MAKE_DEFAULT)"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field $0" Left "0"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field $0" Right "-1"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field $0" Top "35"
WriteINIStr "$PLUGINSDIR\summary.ini" "Field $0" Bottom "45"
WriteINIStr "$PLUGINSDIR\summary.ini" "Settings" NumFields "$0"
${EndIf}
!insertmacro MUI_HEADER_TEXT "$(SUMMARY_PAGE_TITLE)" "$(SUMMARY_PAGE_SUBTITLE)"
; The Summary custom page has a textbox that will automatically receive
@ -717,8 +776,90 @@ Function .onInit
!insertmacro MUI_INSTALLOPTIONS_EXTRACT "options.ini"
!insertmacro MUI_INSTALLOPTIONS_EXTRACT "shortcuts.ini"
!insertmacro MUI_INSTALLOPTIONS_EXTRACT "summary.ini"
!insertmacro createBasicCustomSetAsDefaultOptionsINI
!insertmacro createShortcutsINI
; Setup the options.ini file for the Custom Options Page
WriteINIStr "$PLUGINSDIR\options.ini" "Settings" NumFields "6"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Type "label"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Text "$(OPTIONS_SUMMARY)"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Left "0"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Right "-1"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Top "0"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Bottom "10"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Type "RadioButton"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Text "$(OPTION_STANDARD_RADIO)"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Left "15"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Right "-1"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Top "25"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Bottom "35"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" State "1"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Flags "GROUP"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Type "RadioButton"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Text "$(OPTION_CUSTOM_RADIO)"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Left "15"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Right "-1"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Top "55"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Bottom "65"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" State "0"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Type "label"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Text "$(OPTION_STANDARD_DESC)"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Left "30"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Right "-1"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Top "37"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Bottom "57"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Type "label"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Text "$(OPTION_CUSTOM_DESC)"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Left "30"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Right "-1"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Top "67"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Bottom "87"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 6" Type "checkbox"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 6" Text "$(OPTIONS_MAKE_DEFAULT)"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 6" Left "0"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 6" Right "-1"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 6" Top "124"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 6" Bottom "145"
WriteINIStr "$PLUGINSDIR\options.ini" "Field 6" State "1"
; Setup the shortcuts.ini file for the Custom Shortcuts Page
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Settings" NumFields "4"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Type "label"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Text "$(CREATE_ICONS_DESC)"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Left "0"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Right "-1"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Top "5"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Bottom "15"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Type "checkbox"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Text "$(ICONS_DESKTOP)"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Left "15"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Right "-1"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Top "20"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Bottom "30"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" State "1"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Flags "GROUP"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Type "checkbox"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Text "$(ICONS_STARTMENU)"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Left "15"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Right "-1"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Top "40"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Bottom "50"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" State "1"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Type "checkbox"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Text "$(ICONS_QUICKLAUNCH)"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Left "15"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Right "-1"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Top "60"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Bottom "70"
WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" State "1"
; There must always be nonlocalized and localized directories.
${GetSize} "$EXEDIR\nonlocalized\" "/S=0K" $R5 $R7 $R8

View File

@ -157,7 +157,7 @@ ShowUnInstDetails nevershow
!define MUI_PAGE_CUSTOMFUNCTION_LEAVE un.leaveWelcome
!insertmacro MUI_UNPAGE_WELCOME
; Uninstall Confirm Page
; Custom Uninstall Confirm Page
UninstPage custom un.preConfirm un.leaveConfirm
; Remove Files Page
@ -409,7 +409,67 @@ Function un.preConfirm
${un.ChangeMUIHeaderImage} "$PLUGINSDIR\modern-header.bmp"
${EndIf}
!insertmacro un.createUnConfirmINI
; Setup the unconfirm.ini file for the Custom Uninstall Confirm Page
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Settings" NumFields "5"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Type "label"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Text "$(UN_CONFIRM_UNINSTALLED_FROM)"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Left "0"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Right "-1"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Top "5"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Bottom "15"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Type "text"
; The contents of this control must be set as follows in the pre function
; ${MUI_INSTALLOPTIONS_READ} $1 "unconfirm.ini" "Field 2" "HWND"
; SendMessage $1 ${WM_SETTEXT} 0 "STR:$INSTDIR"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" State ""
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Left "0"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Right "-1"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Top "17"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Bottom "30"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" flags "READONLY"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Type "checkbox"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Text "$(UN_REMOVE_PROFILES)"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Left "0"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Right "-1"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Top "40"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Bottom "50"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" State "0"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" flags "NOTIFY"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 4" Type "text"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 4" State "$(UN_REMOVE_PROFILES_DESC)"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 4" Left "0"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 4" Right "-1"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 4" Top "52"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 4" Bottom "120"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 4" flags "MULTILINE|READONLY"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 5" Type "label"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 5" Text "$(UN_CONFIRM_CLICK)"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 5" Left "0"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 5" Right "-1"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 5" Top "130"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 5" Bottom "150"
${If} "$TmpVal" == "true"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 6" Type "label"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 6" Text "$(SUMMARY_REBOOT_REQUIRED_UNINSTALL)"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 6" Left "0"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 6" Right "-1"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 6" Top "35"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 6" Bottom "45"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Settings" NumFields "6"
; To insert this control reset Top / Bottom for controls below this one
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Top "55"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Bottom "65"
WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 4" Top "67"
${EndIf}
!insertmacro MUI_HEADER_TEXT "$(UN_CONFIRM_PAGE_TITLE)" "$(UN_CONFIRM_PAGE_SUBTITLE)"
; The Summary custom page has a textbox that will automatically receive
; focus. This sets the focus to the Install button instead.

View File

@ -1,9 +1,12 @@
<!ENTITY aboutDialog.title "About &brandFullName;">
<!ENTITY copyright "Credits">
<!ENTITY copyright.accesskey "C">
<!ENTITY aboutLink "&lt; About &brandFullName;">
<!ENTITY aboutLink.accesskey "A">
<!ENTITY aboutVersion "version">
<!ENTITY copyrightInfo "&#169;1998-2008 Contributors. All Rights Reserved. Firefox and the
Firefox logos are trademarks of the Mozilla Foundation. All rights
reserved.">
<!ENTITY aboutDialog.title "About &brandFullName;">
<!ENTITY copyright "Credits">
<!ENTITY copyright.accesskey "C">
<!ENTITY copyrightGNOME.accesskey "r">
<!ENTITY aboutLink "&lt; About &brandFullName;">
<!ENTITY aboutLink.accesskey "A">
<!ENTITY aboutVersion "version">
<!ENTITY closeCmdGNOME.label "Close">
<!ENTITY closeCmdGNOME.accesskey "C">
<!ENTITY copyrightInfo "&#169;1998-2008 Contributors. All Rights Reserved. Firefox and the
Firefox logos are trademarks of the Mozilla Foundation. All rights
reserved.">

View File

@ -22,9 +22,6 @@
<!ENTITY filter.label "Search:">
<!ENTITY filter.accesskey "S">
<!ENTITY clear.label "Clear">
<!ENTITY clear.accesskey "l">
<!ENTITY button.close.label "Close">
<!ENTITY button.close.accesskey "C">

View File

@ -68,7 +68,6 @@ SUMMARY_MAKE_DEFAULT=$BrandShortName will be set as your default web browser.
SUMMARY_CLICK=Click Install to continue.
SURVEY_TEXT=&Tell us what you thought of $BrandShortName
LAUNCH_TEXT=&Launch $BrandShortName now
WARN_APP_RUNNING_INSTALL=$BrandShortName must be closed to proceed with the installation.\n\nClick "OK" to exit $BrandShortName automatically and continue.
CREATE_ICONS_DESC=Create icons for $BrandShortName:
ICONS_DESKTOP=On my &Desktop
ICONS_STARTMENU=In my &Start Menu Programs folder
@ -95,7 +94,6 @@ BANNER_CHECK_EXISTING=Checking existing installation…
STATUS_INSTALL_APP=Installing $BrandShortName…
STATUS_INSTALL_LANG=Installing Language Files (${AB_CD})…
STATUS_INSTALL_OPTIONAL=Installing Optional Components…
STATUS_UNINSTALL_MAIN=Uninstalling $BrandShortName…
STATUS_CLEANUP=A Little Housekeeping…
@ -105,9 +103,6 @@ OPTIONS_SUMMARY=Choose the type of setup you prefer, then click Next.
# One line
OPTION_STANDARD_DESC=$BrandShortName will be installed with the most common options.
OPTION_STANDARD_RADIO=&Standard
# One line
OPTION_COMPLETE_DESC=$BrandShortName will be installed with all available options.
OPTION_COMPLETE_RADIO=C&omplete
# Two lines
OPTION_CUSTOM_DESC=You may choose individual options to be installed. Recommended for experienced users.
OPTION_CUSTOM_RADIO=&Custom

View File

@ -1132,21 +1132,17 @@ statusbarpanel#statusbar-display {
}
#security-button {
list-style-image: none;
min-width: 20px;
-moz-box-direction: reverse;
display: none;
}
#security-button[level="high"],
#security-button[level="low"] {
list-style-image: url("chrome://browser/skin/Secure.png");
display: -moz-box;
}
#security-button[level="broken"] {
list-style-image: url("chrome://browser/skin/Security-broken.png");
display: -moz-box;
}
/* XXXsw prevent margins of a value-less label from shifting the image */

View File

@ -1493,23 +1493,16 @@ sidebarheader > .tabs-closebutton > .toolbarbutton-text {
/* ----- SECURITY DISPLAY ----- */
#security-button {
display: none;
-moz-box-direction: reverse;
}
#security-button[level="high"] {
list-style-image: url("chrome://browser/skin/Secure-statusbar.png");
display: -moz-box;
}
#security-button[level="high"] ,
#security-button[level="low"] {
list-style-image: url("chrome://browser/skin/Secure-statusbar.png");
display: -moz-box;
}
#security-button[level="broken"] {
list-style-image: url("chrome://browser/skin/Secure-statusbar-broken.png");
display: -moz-box;
}
/* ----- PAGE REPORT DISPLAY ----- */

View File

@ -1301,21 +1301,17 @@ statusbarpanel#statusbar-display {
}
#security-button {
list-style-image: none;
min-width: 20px;
-moz-box-direction: reverse;
display: none;
}
#security-button[level="high"],
#security-button[level="low"] {
list-style-image: url("chrome://browser/skin/Secure.png");
display: -moz-box;
}
#security-button[level="broken"] {
list-style-image: url("chrome://browser/skin/Security-broken.png");
display: -moz-box;
}
/* XXXsw prevent margins of a value-less label from shifting the image */

View File

@ -79,9 +79,13 @@ browser_path = \"$(DIST)/bin/$(PROGRAM)\"
endif
endif
_CERTS_DIR = _profile/pgo/certs
AUTOMATION_PPARGS = \
-DBROWSER_PATH=$(browser_path) \
-DXPC_BIN_PATH=\"$(DIST)/bin\" \
-DBIN_SUFFIX=\"$(BIN_SUFFIX)\" \
-DCERTS_DIR=\"../$(_CERTS_DIR)\" \
$(NULL)
ifeq ($(OS_ARCH),Darwin)

View File

@ -43,20 +43,27 @@ VPATH = @srcdir@
relativesrcdir = build/pgo
include $(DEPTH)/config/autoconf.mk
DIRS = \
certs \
$(NULL)
include $(topsrcdir)/config/rules.mk
# Stuff to make a build with a profile
_PROFILE_DIR = $(DEPTH)/_profile/pgo
_CERTS_DIR = $(_PROFILE_DIR)/certs
_CERTS_SRC_DIR = $(srcdir)/certs
_PGO_FILES = \
automation.py \
profileserver.py \
genpgocert.py \
index.html \
quit.js \
server-locations.txt \
$(NULL)
ifeq ($(USE_SHORT_LIBNAME), 1)
PROGRAM = $(MOZ_APP_NAME)$(BIN_SUFFIX)
else
@ -80,6 +87,9 @@ endif
AUTOMATION_PPARGS = \
-DBROWSER_PATH=$(browser_path) \
-DXPC_BIN_PATH=\"$(DIST)/bin\" \
-DBIN_SUFFIX=\"$(BIN_SUFFIX)\" \
-DCERTS_DIR=\"$(_CERTS_DIR)\" \
-DCERTS_SRC_DIR=\"$(_CERTS_SRC_DIR)\" \
$(NULL)
ifeq ($(OS_ARCH),Darwin)
@ -102,11 +112,15 @@ automation.py: automation.py.in
$(PYTHON) $(topsrcdir)/config/Preprocessor.py \
$(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $^ > $@
genpgocert.py: genpgocert.py.in
$(PYTHON) $(topsrcdir)/config/Preprocessor.py \
$(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $^ > $@
profileserver.py: profileserver.py.in
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $^ > $@
chmod +x $@
GARBAGE += automation.py profileserver.py
GARBAGE += automation.py profileserver.py genpgocert.py
libs:: $(_PGO_FILES)
$(INSTALL) $^ $(_PROFILE_DIR)

View File

@ -53,6 +53,8 @@ Runs the browser from a script, and provides useful utilities
for setting up the browser environment.
"""
SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))
__all__ = [
"UNIXISH",
"IS_WIN32",
@ -62,6 +64,7 @@ __all__ = [
"initializeProfile",
"DIST_BIN",
"DEFAULT_APP",
"environment",
]
# These are generated in mozilla/build/Makefile.in
@ -74,10 +77,12 @@ __all__ = [
IS_CYGWIN = False
#endif
#expand IS_CAMINO = __IS_CAMINO__ != 0
#expand BIN_SUFFIX = __BIN_SUFFIX__
UNIXISH = not IS_WIN32 and not IS_MAC
#expand DEFAULT_APP = "./" + __BROWSER_PATH__
#expand CERTS_DIR = __CERTS_DIR__
###########
# LOGGING #
@ -103,7 +108,7 @@ class Process:
non-Windows platforms. :-(
"""
def __init__(self, command, args, env):
def __init__(self, command, args, env, inputdata = None):
"""
Creates a process representing the execution of the given command, which
must be an absolute path, with the given arguments in the given environment.
@ -111,24 +116,40 @@ class Process:
"""
command = os.path.abspath(command)
if IS_WIN32:
import tempfile
import subprocess
if inputdata:
inputfile = tempfile.TemporaryFile()
inputfile.write(inputdata)
inputfile.seek(0)
else:
inputfile = None
cmd = [command]
cmd.extend(args)
p = subprocess.Popen(cmd, env = env,
stdout = subprocess.PIPE,
stderr = subprocess.STDOUT)
stderr = subprocess.STDOUT,
stdin = inputfile)
self._out = p.stdout
else:
import popen2
cmd = []
for (k, v) in env.iteritems():
cmd.append(k + "='" + v + "' ")
if env:
for (k, v) in env.iteritems():
cmd.append(k + "='" + v + "' ")
cmd.append("'" + command + "'")
cmd.extend(map(lambda x: "'" + x + "'", args))
cmd = " ".join(cmd)
p = popen2.Popen4(cmd)
self._out = p.fromchild
if inputdata:
p.tochild.write(inputdata)
p.tochild.close()
self._process = p
self.pid = p.pid
@ -165,8 +186,13 @@ class Process:
def kill(self):
"Kills this process."
try:
if not IS_WIN32: # XXX
if not IS_WIN32:
os.kill(self._process.pid, signal.SIGKILL)
else:
import subprocess
pid = "%i" % self.pid
process = subprocess.Popen(["taskkill", "/F", "/PID", pid])
process.wait()
except:
pass
@ -201,13 +227,13 @@ class Location:
self.options = options
def readLocations():
def readLocations(locationsPath = "server-locations.txt"):
"""
Reads the locations at which the Mochitest HTTP server is available from
server-locations.txt.
"""
locationFile = codecs.open("server-locations.txt", "r", "UTF-8")
locationFile = codecs.open(locationsPath, "r", "UTF-8")
# Perhaps more detail than necessary, but it's the easiest way to make sure
# we get exactly the format we want. See server-locations.txt for the exact
@ -224,7 +250,7 @@ def readLocations():
r"(?P<port>\d+)"
r"(?:"
r"\s+"
r"(?P<options>\w+(?:,\w+)*)"
r"(?P<options>\S+(?:,\S+)*)"
r")?$")
locations = []
lineno = 0
@ -274,12 +300,14 @@ user_pref("dom.max_script_run_time", 0); // no slow script dialogs
user_pref("signed.applets.codebase_principal_support", true);
user_pref("security.warn_submit_insecure", false);
user_pref("browser.shell.checkDefaultBrowser", false);
user_pref("shell.checkDefaultClient", false);
user_pref("browser.warnOnQuit", false);
user_pref("accessibility.typeaheadfind.autostart", false);
user_pref("javascript.options.showInConsole", true);
user_pref("layout.debug.enable_data_xbl", true);
user_pref("browser.EULA.override", true);
user_pref("javascript.options.jit.content", false);
user_pref("gfx.color_management.force_srgb", true);
user_pref("camino.warn_when_closing", false); // Camino-only, harmless to others
"""
@ -319,13 +347,20 @@ function FindProxyForURL(url, host)
if (!matches)
return 'DIRECT';
var isHttp = matches[1] == 'http';
var isHttps = matches[1] == 'https';
if (!matches[3])
matches[3] = isHttp ? '80' : '443';
{
if (isHttp) matches[3] = '80';
if (isHttps) matches[3] = '443';
}
var origin = matches[1] + '://' + matches[2] + ':' + matches[3];
if (origins.indexOf(origin) < 0)
return 'DIRECT';
if (isHttp)
return 'PROXY localhost:8888';
return 'PROXY 127.0.0.1:8888';
if (isHttps)
return 'PROXY 127.0.0.1:4443';
return 'DIRECT';
}""" % { "origins": origins }
pacURL = "".join(pacURL.splitlines())
@ -343,12 +378,81 @@ user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless t
prefsFile.write("".join(prefs))
prefsFile.close()
def fillCertificateDB(profileDir):
pwfilePath = os.path.join(profileDir, ".crtdbpw")
pwfile = open(pwfilePath, "w")
pwfile.write("\n")
pwfile.close()
# Create head of the ssltunnel configuration file
sslTunnelConfigPath = os.path.join(CERTS_DIR, "ssltunnel.cfg")
sslTunnelConfig = open(sslTunnelConfigPath, "w")
sslTunnelConfig.write("httpproxy:1\n")
sslTunnelConfig.write("certdbdir:%s\n" % CERTS_DIR)
sslTunnelConfig.write("forward:127.0.0.1:8888\n")
sslTunnelConfig.write("listen:*:4443:pgo server certificate\n")
# Generate automatic certificate and bond custom certificates
locations = readLocations()
locations.pop(0)
for loc in locations:
if loc.scheme == "https" and "nocert" not in loc.options:
customCertRE = re.compile("^cert=(?P<nickname>[0-9a-zA-Z_ ]+)")
for option in loc.options:
match = customCertRE.match(option)
if match:
customcert = match.group("nickname");
sslTunnelConfig.write("listen:%s:%s:4443:%s\n" % (loc.host, loc.port, customcert))
break
sslTunnelConfig.close()
# Pre-create the certification database for the profile
certutil = DIST_BIN + "/certutil" + BIN_SUFFIX
status = Process(certutil, ["-N", "-d", profileDir, "-f", pwfilePath], environment()).wait()
if status != 0:
return status
# Walk the cert directory and add custom CAs as trusted
files = os.listdir(CERTS_DIR)
for item in files:
root, ext = os.path.splitext(item)
if ext == ".ca":
Process(certutil, ["-A", "-i", os.path.join(CERTS_DIR, item), "-d", profileDir, "-f", pwfilePath, "-n", root, "-t", "CT,,"], environment())
os.unlink(pwfilePath)
return 0
def environment(env = None):
if env == None:
env = dict(os.environ)
if UNIXISH:
ldLibraryPath = os.path.join(SCRIPT_DIR, DIST_BIN)
if "LD_LIBRARY_PATH" in env:
ldLibraryPath = ldLibraryPath + ":" + env["LD_LIBRARY_PATH"]
env["LD_LIBRARY_PATH"] = ldLibraryPath
return env
###############
# RUN THE APP #
###############
def runApp(testURL, env, app, profileDir, extraArgs):
# create certificate database for the profile
certificateStatus = fillCertificateDB(profileDir)
if certificateStatus != 0:
log.info("ERROR FAIL Certificate integration")
return certificateStatus
ssltunnel = DIST_BIN + "/ssltunnel" + BIN_SUFFIX
ssltunnelProcess = Process(ssltunnel, [os.path.join(CERTS_DIR, "ssltunnel.cfg")], environment())
log.info("SSL tunnel pid: %d", ssltunnelProcess.pid)
"Run the app, returning the time at which it was started."
# mark the start
start = datetime.now()
@ -374,10 +478,12 @@ def runApp(testURL, env, app, profileDir, extraArgs):
else:
args.append((testURL))
args.extend(extraArgs)
proc = Process(cmd, args, env = env)
proc = Process(cmd, args, env = environment(env))
log.info("Application pid: %d", proc.pid)
status = proc.wait()
if status != 0:
log.info("ERROR FAIL Exited with code %d during test run", status)
ssltunnelProcess.kill()
return start

View File

@ -1,3 +1,4 @@
#
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
@ -11,15 +12,15 @@
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code
# The Original Code is Mozilla test code
#
# The Initial Developer of the Original Code is
# Rusty Lynch
# Portions created by the Initial Developer are Copyright (C) 2004
# Mozilla Foundation
# Portions created by the Initial Developer are Copyright (C) 2008
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Rusty Lynch <rusty.lynch@intel.com>
# Honza Bambas <honzab@firemni.cz>
#
# 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
@ -35,47 +36,30 @@
#
# ***** END LICENSE BLOCK *****
######################################
# Makefile.in (Generic SANE Plugin Tree)
######################################
DEPTH = ../../../..
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = SanePlugin
LIBRARY_NAME = Sane
EXPORT_LIBRARY = 1
IS_COMPONENT = 1
_PROFILE_DIR = $(DEPTH)/_profile/pgo
_CERTS_DIR = $(_PROFILE_DIR)/certs
# Extension of files must be '.server'
_SERVER_CERTS = \
$(NULL)
# Extension of files must be '.ca'
_CERT_AUTHORITIES = \
pgoca.ca \
$(NULL)
CPPSRCS = \
nsSanePluginFactory.cpp \
nsSanePlugin.cpp \
$(NULL)
LOCAL_INCLUDES = -I$(srcdir)/.. -I$(srcdir)/../../public \
-I/usr/lib/glib/include
EXTRA_DSO_LDOPTS += -L$(DIST)/lib -lgtksuperwin \
$(TK_LIBS) -lsane -ljpeg
XPIDLSRCS = nsSanePluginControl.idl
_SERV_FILES = \
pgoca.p12 \
$(NULL)
include $(topsrcdir)/config/rules.mk
libs:: $(TARGETS)
$(INSTALL) $(srcdir)/test/camera.html $(DIST)/bin/res/samples
$(INSTALL) $(srcdir)/test/scanner.html $(DIST)/bin/res/samples
$(INSTALL) $(srcdir)/test/camera.html /home/httpd/html/test
$(INSTALL) $(srcdir)/test/scanner.html /home/httpd/html/test
superclean: clean
rm -f *~ test/*~
libs:: $(_SERV_FILES) $(_SERVER_CERTS) $(_CERT_AUTHORITIES)
$(INSTALL) $^ $(_CERTS_DIR)

15
build/pgo/certs/pgoca.ca Normal file
View File

@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIICXTCCAcagAwIBAgIBATANBgkqhkiG9w0BAQUFADBqMSQwIgYDVQQLExtQcm9m
aWxlIEd1aWRlZCBPcHRpbWl6YXRpb24xGDAWBgNVBAoTD01vemlsbGEgVGVzdGlu
ZzEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0w
ODA1MjIwMDM4MDVaFw0xODA1MjIwMDM4MDVaMGoxJDAiBgNVBAsTG1Byb2ZpbGUg
R3VpZGVkIE9wdGltaXphdGlvbjEYMBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSgw
JgYDVQQDEx9UZW1wb3JhcnkgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MIGfMA0GCSqG
SIb3DQEBAQUAA4GNADCBiQKBgQDg6iipAXGZYmgTcHfx8M2hcLqmqDalcj7sZ1A7
a3LiCBb+1uHKKy9hUxRUe61aJF4NgMAF5oc+HpXN0hpvkiNHxqqD7R6hrkP3gAJ3
eczEFKsFUI6AqaCL0+xpyhaaZmmarcHxU+PL2h5zq6VssxfBAsO0DkzWzk6E8vM+
jrku7QIDAQABoxMwETAPBgNVHRMECDAGAQH/AgEAMA0GCSqGSIb3DQEBBQUAA4GB
ALPbn3Ztg0m8qDt8Vkf5You6HEqIxZe+ffDTrfq/L7ofHk/OXEpL7OWKRHU33pNG
QS8khBG+sO461C51s6u9giW+eq2PaQv2HGASBpDbvPqc/Hf+zupZsdsXzHv6rt0V
lu5B6nOpMse1nhA494i1ARSuBNzLv5mas38YWG8Rr6jR
-----END CERTIFICATE-----

BIN
build/pgo/certs/pgoca.p12 Normal file

Binary file not shown.

214
build/pgo/genpgocert.py.in Normal file
View File

@ -0,0 +1,214 @@
#
# ***** 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.org code.
#
# The Initial Developer of the Original Code is
# Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2008
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Honza Bambas <honzab@firemni.cz>
#
# 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 *****
import automation
import os
import re
import sys
#expand DIST_BIN = "./" + __XPC_BIN_PATH__
#expand BIN_SUFFIX = __BIN_SUFFIX__
#expand CERTS_DIR = __CERTS_DIR__
#expand CERTS_SRC_DIR = __CERTS_SRC_DIR__
dbFiles = [
re.compile("^cert[0-9]+\.db$"),
re.compile("^key[0-9]+\.db$"),
re.compile("^secmod\.db$")
]
def unlinkDbFiles(path):
for root, dirs, files in os.walk(path):
for name in files:
for dbFile in dbFiles:
if dbFile.match(name) and os.path.exists(os.path.join(root, name)):
os.unlink(os.path.join(root, name))
def runUtil(util, args, inputdata = None):
proc = automation.Process(util, args, automation.environment(), inputdata)
return proc.wait()
def createRandomFile(randomFile):
import random
file = open(randomFile, "wb");
for count in xrange(0, 2048):
file.write(chr(random.randint(0, 255)))
file.close()
def createCertificateAuthority(dbDir, srcDir):
certutil = DIST_BIN + "/certutil" + BIN_SUFFIX
pk12util = DIST_BIN + "/pk12util" + BIN_SUFFIX
tempDbDir = os.path.join(dbDir, ".temp")
if not os.path.exists(tempDbDir):
os.mkdir(tempDbDir)
pwfilePath = os.path.join(tempDbDir, ".crtdbpw")
rndfilePath = os.path.join(tempDbDir, ".rndfile")
pgoCAModulePathSrc = os.path.join(srcDir, "pgoca.p12")
pgoCAPathSrc = os.path.join(srcDir, "pgoca.ca")
pgoCAModulePath = os.path.join(srcDir, "pgoca.p12")
pgoCAPath = os.path.join(srcDir, "pgoca.ca")
pwfile = open(pwfilePath, "w")
pwfile.write("\n")
pwfile.close()
unlinkDbFiles(tempDbDir)
# Create temporary certification database for CA generation
status = runUtil(certutil, ["-N", "-d", tempDbDir, "-f", pwfilePath])
if status != 0:
return status
createRandomFile(rndfilePath);
status = runUtil(certutil, ["-S", "-d", tempDbDir, "-s", "CN=Temporary Certificate Authority, O=Mozilla Testing, OU=Profile Guided Optimization", "-t", "C,,", "-x", "-m", "1", "-v", "120", "-n", "pgo temporary ca", "-2", "-f", pwfilePath, "-z", rndfilePath], "Y\n0\nN\n")
if status != 0:
return status
status = runUtil(certutil, ["-L", "-d", tempDbDir, "-n", "pgo temporary ca", "-a", "-o", pgoCAPathSrc, "-f", pwfilePath])
if status != 0:
return status
status = runUtil(pk12util, ["-o", pgoCAModulePathSrc, "-n", "pgo temporary ca", "-d", tempDbDir, "-w", pwfilePath, "-k", pwfilePath])
if status != 0:
return status
unlinkDbFiles(tempDbDir)
os.unlink(pwfilePath)
os.unlink(rndfilePath)
os.rmdir(tempDbDir)
return 0
def createSSLServerCertificate(dbDir):
certutil = DIST_BIN + "/certutil" + BIN_SUFFIX
pk12util = DIST_BIN + "/pk12util" + BIN_SUFFIX
pwfilePath = os.path.join(dbDir, ".crtdbpw")
rndfilePath = os.path.join(dbDir, ".rndfile")
pgoCAPath = os.path.join(dbDir, "pgoca.p12")
pwfile = open(pwfilePath, "w")
pwfile.write("\n")
pwfile.close()
unlinkDbFiles(dbDir)
# Create certification database for ssltunnel
status = runUtil(certutil, ["-N", "-d", dbDir, "-f", pwfilePath])
if status != 0:
return status
status = runUtil(pk12util, ["-i", pgoCAPath, "-w", pwfilePath, "-d", dbDir, "-k", pwfilePath])
if status != 0:
return status
# Generate automatic certificate
locations = automation.readLocations(os.path.join(dbDir, "../server-locations.txt"))
locations.pop(0)
locationsParam = ""
firstLocation = ""
for loc in locations:
if loc.scheme == "https" and "nocert" not in loc.options:
customCertOption = False
customCertRE = re.compile("^cert=(?:\w+)")
for option in loc.options:
match = customCertRE.match(option)
if match:
customCertOption = True
break
if not customCertOption:
if len(locationsParam) > 0:
locationsParam += ","
locationsParam += loc.host
if firstLocation == "":
firstLocation = loc.host
if firstLocation == "":
print "Nothing to generate, no automatic secure hosts specified"
else:
createRandomFile(rndfilePath);
status = runUtil(certutil, ["-S", "-s", "CN=%s" % firstLocation, "-t", "Pu,,", "-c", "pgo temporary ca", "-m", "2", "-8", locationsParam, "-v", "12", "-n", "pgo server certificate", "-d", dbDir, "-z", rndfilePath, "-f", pwfilePath])
if status != 0:
return status
# Walk the cert directory and add what necessary
files = os.listdir(CERTS_DIR)
for item in files:
root, ext = os.path.splitext(item)
if ext == ".server":
runUtil(pk12util, ["-i", os.path.join(CERTS_DIR, item), "-d", dbDir, "-k", pwfilePath, "-w", pwfilePath])
os.unlink(pwfilePath)
os.unlink(rndfilePath)
return 0
if len(sys.argv) == 1:
print "Specify --gen-server or --gen-ca"
sys.exit(1)
if sys.argv[1] == "--gen-server":
certificateStatus = createSSLServerCertificate(CERTS_DIR)
if certificateStatus != 0:
print "ERROR FAIL: SSL Server Certificate generation"
sys.exit(certificateStatus)
if sys.argv[1] == "--gen-ca":
certificateStatus = createCertificateAuthority(CERTS_DIR, CERTS_SRC_DIR)
if certificateStatus != 0:
print "ERROR FAIL: Certificate Authority generation"
else:
print "\n\n"
print "==================================================="
print " IMPORTANT:"
print " To use this new certificate authority in tests"
print " run 'make' at testing/mochitest"
print "==================================================="
sys.exit(certificateStatus)
print "Invalid option specified"
sys.exit(1)

View File

@ -52,11 +52,24 @@
# number is the default for the protocol.
#
# Unrecognized options are ignored. Recognized options are "primary" and
# "privileged". "primary" denotes a location which is the canonical location of
# "privileged", "nocert", "cert=some_cert_nickname".
#
# "primary" denotes a location which is the canonical location of
# the server; this location is the one assumed for requests which don't
# otherwise identify a particular origin (e.g. HTTP/1.0 requests). "privileged"
# denotes a location which should have the ability to request elevated
# privileges; the default is no privileges.
# otherwise identify a particular origin (e.g. HTTP/1.0 requests).
#
# "privileged" denotes a location which should have the ability to request
# elevated privileges; the default is no privileges.
#
# "nocert" makes sense only for https:// hosts and means there is not
# any certificate automatically generated for this host.
#
# "cert=nickname" tells the pgo server to use a particular certificate
# for this host. The certificate is referenced by its nickname that must
# not contain any spaces. The certificate key files (PKCS12 modules)
# for custom certification are loaded from build/pgo/ssltunnel/certs
# directory. When new certificate is added to this dir pgo/ssltunnel
# must be builded then.
#
#
@ -90,6 +103,15 @@ http://sub1.test2.example.com:80 privileged
http://sub2.test1.example.com:80 privileged
http://sub2.test2.example.com:80 privileged
https://example.com:443 privileged
https://test1.example.com:443 privileged
https://test2.example.com:443 privileged
https://sub1.test1.example.com:443 privileged
https://sub1.test2.example.com:443 privileged
https://sub2.test1.example.com:443 privileged
https://sub2.test2.example.com:443 privileged
https://nocert.example.com:443 privileged,nocert
#
# These are subdomains of <ält.example.org>.
#
@ -98,6 +120,9 @@ http://sub2.xn--lt-uia.example.org:80 privileged
http://xn--exmple-cua.test:80 privileged
http://sub1.xn--exmple-cua.test:80 privileged
https://xn--hxajbheg2az3al.xn--jxalpdlp:443 privileged
https://sub1.xn--hxajbheg2az3al.xn--jxalpdlp:443 privileged
#
# These are subdomains of <παράδειγμα.δοκιμή>, the Greek IDN for example.test.
#
@ -114,3 +139,8 @@ http://sectest1.example.org:80 privileged
http://sub.sectest2.example.org:80 privileged
http://sectest2.example.org:80
http://sub.sectest1.example.org:80
https://sectest1.example.org:443 privileged
https://sub.sectest2.example.org:443 privileged
https://sectest2.example.org:443
https://sub.sectest1.example.org:443

View File

@ -423,7 +423,7 @@ private:
nsScriptSecurityManager();
virtual ~nsScriptSecurityManager();
static JSBool JS_DLL_CALLBACK
static JSBool
CheckObjectAccess(JSContext *cx, JSObject *obj,
jsval id, JSAccessMode mode,
jsval *vp);

View File

@ -49,19 +49,19 @@
#include "nsMemory.h"
#include "nsStringBuffer.h"
JS_STATIC_DLL_CALLBACK(void *)
static void *
nsGetPrincipalArray(JSContext *cx, JSPrincipals *prin)
{
return nsnull;
}
JS_STATIC_DLL_CALLBACK(JSBool)
static JSBool
nsGlobalPrivilegesEnabled(JSContext *cx, JSPrincipals *jsprin)
{
return JS_TRUE;
}
JS_STATIC_DLL_CALLBACK(JSBool)
static JSBool
nsJSPrincipalsSubsume(JSPrincipals *jsprin, JSPrincipals *other)
{
nsJSPrincipals *nsjsprin = static_cast<nsJSPrincipals *>(jsprin);
@ -73,7 +73,7 @@ nsJSPrincipalsSubsume(JSPrincipals *jsprin, JSPrincipals *other)
return NS_SUCCEEDED(rv) && result;
}
JS_STATIC_DLL_CALLBACK(void)
static void
nsDestroyJSPrincipals(JSContext *cx, struct JSPrincipals *jsprin)
{
nsJSPrincipals *nsjsprin = static_cast<nsJSPrincipals *>(jsprin);
@ -99,7 +99,7 @@ nsDestroyJSPrincipals(JSContext *cx, struct JSPrincipals *jsprin)
// so we don't need to worry about "codebase"
}
JS_STATIC_DLL_CALLBACK(JSBool)
static JSBool
nsTranscodeJSPrincipals(JSXDRState *xdr, JSPrincipals **jsprinp)
{
nsresult rv;
@ -179,10 +179,13 @@ nsJSPrincipals::Startup()
rtsvc->GetRuntime(&rt);
NS_ASSERTION(rt != nsnull, "no JSRuntime?!");
JSPrincipalsTranscoder oldpx;
oldpx = ::JS_SetPrincipalsTranscoder(rt, nsTranscodeJSPrincipals);
NS_ASSERTION(oldpx == nsnull, "oops, JS_SetPrincipalsTranscoder wars!");
JSSecurityCallbacks *callbacks = JS_GetRuntimeSecurityCallbacks(rt);
NS_ASSERTION(callbacks, "Need a callbacks struct by now!");
NS_ASSERTION(!callbacks->principalsTranscoder,
"oops, JS_SetPrincipalsTranscoder wars!");
callbacks->principalsTranscoder = nsTranscodeJSPrincipals;
return NS_OK;
}

View File

@ -451,7 +451,7 @@ NS_IMPL_ISUPPORTS5(nsScriptSecurityManager,
///////////////////////////////////////////////////
///////////////// Security Checks /////////////////
JSBool JS_DLL_CALLBACK
JSBool
nsScriptSecurityManager::CheckObjectAccess(JSContext *cx, JSObject *obj,
jsval id, JSAccessMode mode,
jsval *vp)
@ -2050,7 +2050,7 @@ nsScriptSecurityManager::GetScriptPrincipal(JSContext *cx,
JSPrincipals *jsp = JS_GetScriptPrincipals(cx, script);
if (!jsp) {
*rv = NS_ERROR_FAILURE;
// Script didn't have principals -- shouldn't happen.
NS_ERROR("Script compiled without principals!");
return nsnull;
}
nsJSPrincipals *nsJSPrin = static_cast<nsJSPrincipals *>(jsp);
@ -3225,17 +3225,21 @@ nsresult nsScriptSecurityManager::Init()
rv = runtimeService->GetRuntime(&sRuntime);
NS_ENSURE_SUCCESS(rv, rv);
static JSSecurityCallbacks securityCallbacks = {
CheckObjectAccess,
NULL,
NULL
};
#ifdef DEBUG
JSCheckAccessOp oldCallback =
JSSecurityCallbacks *oldcallbacks =
#endif
JS_SetCheckObjectAccessCallback(sRuntime, CheckObjectAccess);
JS_SetRuntimeSecurityCallbacks(sRuntime, &securityCallbacks);
NS_ASSERTION(!oldcallbacks, "Someone else set security callbacks!");
sXPConnect->GetXPCWrappedNativeJSClassInfo(&sXPCWrappedNativeJSClass,
&sXPCWrappedNativeGetObjOps1,
&sXPCWrappedNativeGetObjOps2);
// For now, assert that no callback was set previously
NS_ASSERTION(!oldCallback, "Someone already set a JS CheckObjectAccess callback");
return NS_OK;
}
@ -3256,11 +3260,7 @@ void
nsScriptSecurityManager::Shutdown()
{
if (sRuntime) {
#ifdef DEBUG
JSCheckAccessOp oldCallback =
#endif
JS_SetCheckObjectAccessCallback(sRuntime, nsnull);
NS_ASSERTION(oldCallback == CheckObjectAccess, "Oops, we just clobbered someone else, oh well.");
JS_SetRuntimeSecurityCallbacks(sRuntime, NULL);
sRuntime = nsnull;
}
sEnabledID = JSVAL_VOID;

View File

@ -69,11 +69,7 @@ LIBXUL_SDK = @LIBXUL_SDK@
L10NBASEDIR = @L10NBASEDIR@
ifdef LIBXUL_SDK
LIBXUL_DIST = $(LIBXUL_SDK)
else
LIBXUL_DIST = $(DIST)
endif
LIBXUL_DIST = @LIBXUL_DIST@
XULRUNNER_STUB_NAME = @XULRUNNER_STUB_NAME@
@ -165,7 +161,6 @@ MOZ_OGG = @MOZ_OGG@
MOZ_MEDIA = @MOZ_MEDIA@
NS_PRINTING = @NS_PRINTING@
MOZ_CRASHREPORTER = @MOZ_CRASHREPORTER@
MOZ_MOCHITEST = @MOZ_MOCHITEST@
MOZ_HELP_VIEWER = @MOZ_HELP_VIEWER@
MOC= @MOC@

View File

@ -511,6 +511,7 @@ DEHYDRA_MODULES = \
TREEHYDRA_MODULES = \
$(topsrcdir)/xpcom/analysis/outparams.js \
$(topsrcdir)/xpcom/analysis/stack.js \
$(topsrcdir)/xpcom/analysis/flow.js \
$(NULL)
DEHYDRA_ARGS = \

View File

@ -2955,6 +2955,11 @@ EOF
fi # have visibility(hidden) attribute
fi # GNU_CC
# visibility hidden flag for Sun Studio on Solaris
if test -z "$GNU_CC" && test -z "$GNU_CXX" && test "$OS_ARCH" = "SunOS"; then
VISIBILITY_FLAGS='-xldscope=hidden'
fi # Sun Studio on Solaris
AC_SUBST(WRAP_SYSTEM_INCLUDES)
AC_SUBST(VISIBILITY_FLAGS)
@ -4031,6 +4036,13 @@ elif test -n "$LIBXUL_SDK_DIR" -a "$LIBXUL_SDK_DIR" != "no"; then
fi
AC_SUBST(LIBXUL_SDK)
if test -n "$LIBXUL_SDK"; then
LIBXUL_DIST="$LIBXUL_SDK"
else
LIBXUL_DIST="$MOZ_BUILD_ROOT/dist"
fi
AC_SUBST(LIBXUL_DIST)
dnl ========================================================
dnl = If NSPR was not detected in the system,
dnl = use the one in the source tree (mozilla/nsprpub)
@ -4054,22 +4066,22 @@ if test -n "$MOZ_NATIVE_NSPR"; then
AC_MSG_ERROR([system NSPR does not support PR_STATIC_ASSERT]))
CFLAGS=$_SAVE_CFLAGS
else
NSPR_CFLAGS='`$(DEPTH)/nsprpub/config/nspr-config --prefix=$(LIBXUL_DIST) --includedir=$(LIBXUL_DIST)/include/nspr --cflags`'
NSPR_CFLAGS='`$(DEPTH)/nsprpub/config/nspr-config --prefix='${LIBXUL_DIST}' --includedir='${LIBXUL_DIST}'/include/nspr --cflags`'
# explicitly set libs for Visual Age C++ for OS/2
if test "$OS_ARCH" = "OS2" -a "$VACPP" = "yes"; then
NSPR_LIBS='$(LIBXUL_DIST)/lib/nspr'$NSPR_VERSION'.lib $(LIBXUL_DIST)/lib/plc'$NSPR_VERSION'.lib $(LIBXUL_DIST)/lib/plds'$NSPR_VERSION'.lib '$_PTHREAD_LDFLAGS''
NSPR_LIBS="${LIBXUL_DIST}/lib/nspr${NSPR_VERSION}.lib ${LIBXUL_DIST}/lib/plc${NSPR_VERSION}.lib ${LIBXUL_DIST}/lib/plds${NSPR_VERSION}.lib ${_PTHREAD_LDFLAGS}"
elif test "$OS_ARCH" = "WINCE"; then
NSPR_CFLAGS='-I$(LIBXUL_DIST)/include/nspr'
NSPR_LIBS='$(LIBXUL_DIST)/lib/nspr'$NSPR_VERSION'.lib $(LIBXUL_DIST)/lib/plc'$NSPR_VERSION'.lib $(LIBXUL_DIST)/lib/plds'$NSPR_VERSION'.lib '
NSPR_CFLAGS="-I${LIBXUL_DIST}/include/nspr"
NSPR_LIBS="${LIBXUL_DIST}/lib/nspr${NSPR_VERSION}.lib ${LIBXUL_DIST}/lib/plc${NSPR_VERSION}.lib ${LIBXUL_DIST}/lib/plds${NSPR_VERSION}.lib "
elif test "$OS_ARCH" = "WINNT"; then
NSPR_CFLAGS='-I$(LIBXUL_DIST)/include/nspr'
NSPR_CFLAGS="-I${LIBXUL_DIST}/include/nspr"
if test -n "$GNU_CC"; then
NSPR_LIBS="-L\$(LIBXUL_DIST)/lib -lnspr$NSPR_VERSION -lplc$NSPR_VERSION -lplds$NSPR_VERSION"
NSPR_LIBS="-L${LIBXUL_DIST}/lib -lnspr${NSPR_VERSION} -lplc${NSPR_VERSION} -lplds${NSPR_VERSION}"
else
NSPR_LIBS='$(LIBXUL_DIST)/lib/nspr'$NSPR_VERSION'.lib $(LIBXUL_DIST)/lib/plc'$NSPR_VERSION'.lib $(LIBXUL_DIST)/lib/plds'$NSPR_VERSION'.lib '
NSPR_LIBS="${LIBXUL_DIST}/lib/nspr${NSPR_VERSION}.lib ${LIBXUL_DIST}/lib/plc${NSPR_VERSION}.lib ${LIBXUL_DIST}/lib/plds${NSPR_VERSION}.lib "
fi
else
NSPR_LIBS='`$(DEPTH)/nsprpub/config/nspr-config --prefix=$(LIBXUL_DIST) --libdir=$(LIBXUL_DIST)/lib --libs`'
NSPR_LIBS='`$(DEPTH)/nsprpub/config/nspr-config --prefix='${LIBXUL_DIST}' --libdir='${LIBXUL_DIST}'/lib --libs`'
fi
fi
@ -4345,7 +4357,6 @@ MOZ_JSLOADER=1
MOZ_LIBART_CFLAGS=
MOZ_LIBART_LIBS=
MOZ_MATHML=1
MOZ_MOCHITEST=1
MOZ_MORK=1
MOZ_MORKREADER=
MOZ_AUTH_EXTENSION=1
@ -5517,15 +5528,6 @@ if test -z "$MOZ_CRASHREPORTER_ENABLE_PERCENT"; then
fi
AC_DEFINE_UNQUOTED(MOZ_CRASHREPORTER_ENABLE_PERCENT, $MOZ_CRASHREPORTER_ENABLE_PERCENT)
dnl ========================================================
dnl = Build mochitest JS/DOM tests (on by default)
dnl ========================================================
MOZ_ARG_DISABLE_BOOL(mochitest,
[ --disable-mochitest Disable mochitest harness],
MOZ_MOCHITEST=,
MOZ_MOCHITEST=1 )
dnl ========================================================
dnl = Enable compilation of specific extension modules
dnl ========================================================
@ -7149,6 +7151,10 @@ MOZ_ARG_ENABLE_BOOL(libxul,
MOZ_ENABLE_LIBXUL=1,
MOZ_ENABLE_LIBXUL=)
if test -n "$MOZ_STATIC_BUILD_UNSUPPORTED" -a -n "$BUILD_STATIC_LIBS"; then
AC_MSG_ERROR([--enable-static is not supported for building $MOZ_APP_NAME. You probably want --enable-libxul.])
fi
if test -n "$MOZ_ENABLE_LIBXUL" -a -n "$BUILD_STATIC_LIBS"; then
AC_MSG_ERROR([--enable-libxul is not compatible with --enable-static])
fi
@ -7899,7 +7905,6 @@ AC_SUBST(MOZ_SPELLCHECK)
AC_SUBST(MOZ_XPFE_COMPONENTS)
AC_SUBST(MOZ_USER_DIR)
AC_SUBST(MOZ_CRASHREPORTER)
AC_SUBST(MOZ_MOCHITEST)
AC_SUBST(ENABLE_STRIP)
AC_SUBST(PKG_SKIP_STRIP)
@ -8356,12 +8361,12 @@ if test -z "$MOZ_NATIVE_NSPR"; then
# Hack to deal with the fact that we use NSPR_CFLAGS everywhere
AC_MSG_WARN([Recreating autoconf.mk with updated nspr-config output])
if test ! "$VACPP" && test "$OS_ARCH" != "WINNT" && test "$OS_ARCH" != "WINCE"; then
_libs=`./nsprpub/config/nspr-config --prefix=$\(LIBXUL_DIST\) --exec-prefix=$\(DIST\) --libdir=$\(LIBXUL_DIST\)/lib --libs`
$PERL -pi.bak -e "s '^NSPR_LIBS\\s*=.*'NSPR_LIBS = $_libs'" config/autoconf.mk
NSPR_LIBS=`./nsprpub/config/nspr-config --prefix=$LIBXUL_DIST --exec-prefix=$MOZ_BUILD_ROOT/dist --libdir=$LIBXUL_DIST/lib --libs`
$PERL -pi.bak -e "s '^NSPR_LIBS\\s*=.*'NSPR_LIBS = $NSPR_LIBS'" config/autoconf.mk
fi
if test "$OS_ARCH" != "WINNT" && test "$OS_ARCH" != "WINCE" ; then
_cflags=`./nsprpub/config/nspr-config --prefix=$\(LIBXUL_DIST\) --exec-prefix=$\(DIST\) --includedir=$\(LIBXUL_DIST\)/include/nspr --cflags`
$PERL -pi.bak -e "s '^NSPR_CFLAGS\\s*=.*'NSPR_CFLAGS = $_cflags'" config/autoconf.mk
NSPR_CFLAGS=`./nsprpub/config/nspr-config --prefix=$LIBXUL_DIST --exec-prefix=$MOZ_BUILD_ROOT/dist --includedir=$LIBXUL_DIST/include/nspr --cflags`
$PERL -pi.bak -e "s '^NSPR_CFLAGS\\s*=.*'NSPR_CFLAGS = $NSPR_CFLAGS'" config/autoconf.mk
fi
rm -f config/autoconf.mk.bak
fi

View File

@ -94,6 +94,7 @@ class nsIJSRuntimeService;
class nsIEventListenerManager;
class nsIScriptContext;
class nsIRunnable;
class nsIInterfaceRequestor;
template<class E> class nsCOMArray;
class nsIPref;
class nsVoidArray;
@ -664,9 +665,7 @@ public:
* @param aContent The content node to test.
* @return whether it's draggable
*/
static PRBool ContentIsDraggable(nsIContent* aContent) {
return IsDraggableImage(aContent) || IsDraggableLink(aContent);
}
static PRBool ContentIsDraggable(nsIContent* aContent);
/**
* Method that decides whether a content node is a draggable image
@ -1323,6 +1322,12 @@ public:
static nsresult GetContextForEventHandlers(nsINode* aNode,
nsIScriptContext** aContext);
static JSContext *GetCurrentJSContext();
static nsIInterfaceRequestor* GetSameOriginChecker();
private:
static PRBool InitializeEventTable();
@ -1400,6 +1405,8 @@ private:
static PRUint32 sRemovableScriptBlockerCount;
static nsCOMArray<nsIRunnable>* sBlockedScriptRunners;
static PRUint32 sRunnersCountAtFirstBlocker;
static nsIInterfaceRequestor* sSameOriginChecker;
};
#define NS_HOLD_JS_OBJECTS(obj, clazz) \
@ -1505,6 +1512,78 @@ private:
PRUint32 mNestingLevel;
};
/**
* Class used to detect unexpected mutations. To use the class create an
* nsMutationGuard on the stack before unexpected mutations could occur.
* You can then at any time call Mutated to check if any unexpected mutations
* have occured.
*
* When a guard is instantiated sMutationCount is set to 300. It is then
* decremented by every mutation (capped at 0). This means that we can only
* detect 300 mutations during the lifetime of a single guard, however that
* should be more then we ever care about as we usually only care if more then
* one mutation has occured.
*
* When the guard goes out of scope it will adjust sMutationCount so that over
* the lifetime of the guard the guard itself has not affected sMutationCount,
* while mutations that happened while the guard was alive still will. This
* allows a guard to be instantiated even if there is another guard higher up
* on the callstack watching for mutations.
*
* The only thing that has to be avoided is for an outer guard to be used
* while an inner guard is alive. This can be avoided by only ever
* instantiating a single guard per scope and only using the guard in the
* current scope.
*/
class nsMutationGuard {
public:
nsMutationGuard()
{
mDelta = eMaxMutations - sMutationCount;
sMutationCount = eMaxMutations;
}
~nsMutationGuard()
{
sMutationCount =
mDelta > sMutationCount ? 0 : sMutationCount - mDelta;
}
/**
* Returns true if any unexpected mutations have occured. You can pass in
* an 8-bit ignore count to ignore a number of expected mutations.
*/
PRBool Mutated(PRUint8 aIgnoreCount)
{
return sMutationCount < static_cast<PRUint32>(eMaxMutations - aIgnoreCount);
}
// This function should be called whenever a mutation that we want to keep
// track of happen. For now this is only done when children are added or
// removed, but we might do it for attribute changes too in the future.
static void DidMutate()
{
if (sMutationCount) {
--sMutationCount;
}
}
private:
// mDelta is the amount sMutationCount was adjusted when the guard was
// initialized. It is needed so that we can undo that adjustment once
// the guard dies.
PRUint32 mDelta;
// The value 300 is not important, as long as it is bigger then anything
// ever passed to Mutated().
enum { eMaxMutations = 300 };
// sMutationCount is a global mutation counter which is decreased by one at
// every mutation. It is capped at 0 to avoid wrapping.
// It's value is always between 0 and 300, inclusive.
static PRUint32 sMutationCount;
};
#define NS_AUTO_GCROOT_PASTE2(tok,line) tok##line
#define NS_AUTO_GCROOT_PASTE(tok,line) \
NS_AUTO_GCROOT_PASTE2(tok,line)

View File

@ -128,14 +128,15 @@ interface nsIDOMParser : nsISupports
* The nsIDOMParserJS interface provides a scriptable way of calling init().
* Do NOT use this interface from languages other than JavaScript.
*/
[scriptable, uuid(dca92fe9-ae7a-44b7-80aa-d151216698ac)]
[scriptable, uuid(ba6bcd6c-63d8-49b3-bc8a-1e5e895645bc)]
interface nsIDOMParserJS : nsISupports
{
/**
* Just like nsIDOMParser.init, but callable from JS. It'll pick up the args
* from XPConnect.
* Just like nsIDOMParser.init, but callable from JS.
*/
void init();
void init(in nsIPrincipal principal,
in nsIURI documentURI,
in nsIURI baseURI);
};
%{ C++

View File

@ -894,7 +894,10 @@ nsTransferableFactory::Produce(nsDOMDataTransfer* aDataTransfer,
nsIContent* findFormParent = findFormNode->GetParent();
while (findFormParent) {
nsCOMPtr<nsIFormControl> form(do_QueryInterface(findFormParent));
if (form && form->GetType() != NS_FORM_OBJECT)
if (form && form->GetType() != NS_FORM_OBJECT &&
form->GetType() != NS_FORM_FIELDSET &&
form->GetType() != NS_FORM_LEGEND &&
form->GetType() != NS_FORM_LABEL)
return NS_OK;
findFormParent = findFormParent->GetParent();
}

View File

@ -89,6 +89,7 @@
#include "nsIDOMHTMLDocument.h"
#include "nsIDOMHTMLCollection.h"
#include "nsIDOMHTMLFormElement.h"
#include "nsIDOMNSHTMLElement.h"
#include "nsIForm.h"
#include "nsIFormControl.h"
#include "nsGkAtoms.h"
@ -155,6 +156,8 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_XTFSERVICE_CID);
#include "nsReferencedElement.h"
#include "nsIUGenCategory.h"
#include "nsIDragService.h"
#include "nsIChannelEventSink.h"
#include "nsIInterfaceRequestor.h"
#ifdef IBMBIDI
#include "nsIBidiKeyboard.h"
@ -208,10 +211,13 @@ PRUint32 nsContentUtils::sScriptBlockerCount = 0;
PRUint32 nsContentUtils::sRemovableScriptBlockerCount = 0;
nsCOMArray<nsIRunnable>* nsContentUtils::sBlockedScriptRunners = nsnull;
PRUint32 nsContentUtils::sRunnersCountAtFirstBlocker = 0;
nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nsnull;
nsIJSRuntimeService *nsAutoGCRoot::sJSRuntimeService;
JSRuntime *nsAutoGCRoot::sJSScriptRuntime;
PRUint32 nsMutationGuard::sMutationCount = 0;
PRBool nsContentUtils::sInitialized = PR_FALSE;
static PLDHashTable sEventListenerManagersHash;
@ -255,6 +261,14 @@ EventListenerManagerHashClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
lm->~EventListenerManagerMapEntry();
}
class nsSameOriginChecker : public nsIChannelEventSink,
public nsIInterfaceRequestor
{
NS_DECL_ISUPPORTS
NS_DECL_NSICHANNELEVENTSINK
NS_DECL_NSIINTERFACEREQUESTOR
};
// static
nsresult
nsContentUtils::Init()
@ -897,6 +911,8 @@ nsContentUtils::Shutdown()
delete sBlockedScriptRunners;
sBlockedScriptRunners = nsnull;
NS_IF_RELEASE(sSameOriginChecker);
nsAutoGCRoot::Shutdown();
}
@ -2438,6 +2454,26 @@ nsContentUtils::GetImageFromContent(nsIImageLoadingContent* aContent,
return image;
}
// static
PRBool
nsContentUtils::ContentIsDraggable(nsIContent* aContent)
{
nsCOMPtr<nsIDOMNSHTMLElement> htmlElement = do_QueryInterface(aContent);
if (htmlElement) {
PRBool draggable = PR_FALSE;
htmlElement->GetDraggable(&draggable);
if (draggable)
return PR_TRUE;
if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
nsGkAtoms::_false, eIgnoreCase))
return PR_FALSE;
}
// special handling for content area image and link dragging
return IsDraggableImage(aContent) || IsDraggableLink(aContent);
}
// static
PRBool
nsContentUtils::IsDraggableImage(nsIContent* aContent)
@ -4314,6 +4350,17 @@ nsContentUtils::GetContextForEventHandlers(nsINode* aNode,
return NS_OK;
}
/* static */
JSContext *
nsContentUtils::GetCurrentJSContext()
{
JSContext *cx = nsnull;
sThreadJSContextStack->Peek(&cx);
return cx;
}
/* static */
void
nsAutoGCRoot::Shutdown()
@ -4349,3 +4396,45 @@ nsContentUtils::IsNamedItem(nsIContent* aContent)
return nsnull;
}
/* static */
nsIInterfaceRequestor*
nsContentUtils::GetSameOriginChecker()
{
if (!sSameOriginChecker) {
sSameOriginChecker = new nsSameOriginChecker();
NS_IF_ADDREF(sSameOriginChecker);
}
return sSameOriginChecker;
}
NS_IMPL_ISUPPORTS2(nsSameOriginChecker,
nsIChannelEventSink,
nsIInterfaceRequestor)
NS_IMETHODIMP
nsSameOriginChecker::OnChannelRedirect(nsIChannel *aOldChannel,
nsIChannel *aNewChannel,
PRUint32 aFlags)
{
NS_PRECONDITION(aNewChannel, "Redirecting to null channel?");
nsCOMPtr<nsIURI> oldURI;
nsresult rv = aOldChannel->GetURI(getter_AddRefs(oldURI)); // The original URI
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> newURI;
rv = aNewChannel->GetURI(getter_AddRefs(newURI)); // The new URI
NS_ENSURE_SUCCESS(rv, rv);
return nsContentUtils::GetSecurityManager()->
CheckSameOriginURI(oldURI, newURI, PR_TRUE);
}
NS_IMETHODIMP
nsSameOriginChecker::GetInterface(const nsIID & aIID, void **aResult)
{
return QueryInterface(aIID, aResult);
}

View File

@ -506,39 +506,14 @@ nsDOMParser::Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj,
}
NS_IMETHODIMP
nsDOMParser::Init()
nsDOMParser::Init(nsIPrincipal *principal, nsIURI *documentURI, nsIURI *baseURI)
{
AttemptedInitMarker marker(&mAttemptedInit);
nsAXPCNativeCallContext *ncc = nsnull;
nsresult rv = nsContentUtils::XPConnect()->
GetCurrentNativeCallContext(&ncc);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(ncc, NS_ERROR_UNEXPECTED);
JSContext *cx = nsnull;
rv = ncc->GetJSContext(&cx);
NS_ENSURE_SUCCESS(rv, rv);
JSContext *cx = nsContentUtils::GetCurrentJSContext();
NS_ENSURE_TRUE(cx, NS_ERROR_UNEXPECTED);
PRUint32 argc;
jsval *argv = nsnull;
ncc->GetArgc(&argc);
ncc->GetArgvPtr(&argv);
if (argc != 3) {
return NS_ERROR_INVALID_ARG;
}
nsCOMPtr<nsIPrincipal> prin;
nsCOMPtr<nsIURI> documentURI;
nsCOMPtr<nsIURI> baseURI;
rv = GetInitArgs(cx, argc, argv, getter_AddRefs(prin),
getter_AddRefs(documentURI), getter_AddRefs(baseURI));
NS_ENSURE_SUCCESS(rv, rv);
nsIScriptContext* scriptContext = GetScriptContextFromJSContext(cx);
return Init(prin, documentURI, baseURI,
return Init(principal, documentURI, baseURI,
scriptContext ? scriptContext->GetGlobalObject() : nsnull);
}

View File

@ -301,9 +301,13 @@ nsIdentifierMapEntry::~nsIdentifierMapEntry()
void
nsIdentifierMapEntry::Traverse(nsCycleCollectionTraversalCallback* aCallback)
{
if (mNameContentList != NAME_NOT_VALID)
if (mNameContentList != NAME_NOT_VALID) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
"mIdentifierMap mNameContentList");
aCallback->NoteXPCOMChild(mNameContentList);
}
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, "mIdentifierMap mDocAllList");
aCallback->NoteXPCOMChild(static_cast<nsIDOMNodeList*>(mDocAllList));
}
@ -1227,6 +1231,7 @@ public:
nsCycleCollectionTraversalCallback *mCb;
virtual void Visit(nsIContent* aContent)
{
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*mCb, "mLinkMap entry");
mCb->NoteXPCOMChild(aContent);
}
};
@ -1236,7 +1241,6 @@ LinkMapTraverser(nsUint32ToContentHashEntry* aEntry, void* userArg)
{
LinkMapTraversalVisitor visitor;
visitor.mCb = static_cast<nsCycleCollectionTraversalCallback*>(userArg);
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*visitor.mCb, "mLinkMap entry");
aEntry->VisitContent(&visitor);
return PL_DHASH_NEXT;
}

View File

@ -611,7 +611,7 @@ nsGenericDOMDataNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
NS_ASSERTION(IsRootOfNativeAnonymousSubtree() ||
!HasFlag(NODE_IS_IN_ANONYMOUS_SUBTREE) ||
aBindingParent->IsInNativeAnonymousSubtree(),
"Trying to re-bind content from native anonymous subtree to"
"Trying to re-bind content from native anonymous subtree to "
"non-native anonymous parent!");
slots->mBindingParent = aBindingParent; // Weak, so no addref happens.
if (IsRootOfNativeAnonymousSubtree() ||

View File

@ -149,6 +149,8 @@
#include "mozAutoDocUpdate.h"
#include "nsICSSParser.h"
#ifdef MOZ_SVG
PRBool NS_SVG_HaveFeature(const nsAString &aFeature);
#endif /* MOZ_SVG */
@ -789,10 +791,12 @@ nsNSElementTearoff::GetFirstElementChild(nsIDOMElement** aResult)
{
*aResult = nsnull;
#ifdef MOZ_XUL
nsXULElement* xul = nsXULElement::FromContent(mContent);
if (xul) {
xul->EnsureContentsGenerated();
}
#endif
nsAttrAndChildArray& children = mContent->mAttrsAndChildren;
PRUint32 i, count = children.ChildCount();
@ -811,10 +815,12 @@ nsNSElementTearoff::GetLastElementChild(nsIDOMElement** aResult)
{
*aResult = nsnull;
#ifdef MOZ_XUL
nsXULElement* xul = nsXULElement::FromContent(mContent);
if (xul) {
xul->EnsureContentsGenerated();
}
#endif
nsAttrAndChildArray& children = mContent->mAttrsAndChildren;
PRUint32 i = children.ChildCount();
@ -838,10 +844,12 @@ nsNSElementTearoff::GetPreviousElementSibling(nsIDOMElement** aResult)
return NS_OK;
}
#ifdef MOZ_XUL
nsXULElement* xul = nsXULElement::FromContent(parent);
if (xul) {
xul->EnsureContentsGenerated();
}
#endif
NS_ASSERTION(parent->IsNodeOfType(nsINode::eELEMENT) ||
parent->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT),
@ -875,10 +883,12 @@ nsNSElementTearoff::GetNextElementSibling(nsIDOMElement** aResult)
return NS_OK;
}
#ifdef MOZ_XUL
nsXULElement* xul = nsXULElement::FromContent(parent);
if (xul) {
xul->EnsureContentsGenerated();
}
#endif
NS_ASSERTION(parent->IsNodeOfType(nsINode::eELEMENT) ||
parent->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT),
@ -1692,9 +1702,6 @@ nsStaticContentList::Item(PRUint32 aIndex, nsIDOMNode** aReturn)
}
//----------------------------------------------------------------------
PRUint32 nsMutationGuard::sMutationCount = 0;
nsGenericElement::nsDOMSlots::nsDOMSlots(PtrBits aFlags)
: nsINode::nsSlots(aFlags),
mBindingParent(nsnull)
@ -5153,6 +5160,10 @@ TryMatchingElementsInSubtree(nsINode* aRoot,
nsIContent * const * kidSlot = aRoot->GetChildArray();
nsIContent * const * end = kidSlot + count;
#ifdef DEBUG
nsMutationGuard debugMutationGuard;
#endif
PRBool continueIteration = PR_TRUE;
for (; kidSlot != end; ++kidSlot) {
nsIContent* kid = *kidSlot;
@ -5214,6 +5225,11 @@ TryMatchingElementsInSubtree(nsINode* aRoot,
/* Make sure to clean this up */
prevSibling->~RuleProcessorData();
}
#ifdef DEBUG
NS_ASSERTION(!debugMutationGuard.Mutated(0), "Unexpected mutations happened");
#endif
return continueIteration;
}

View File

@ -317,78 +317,6 @@ private:
nsCOMArray<nsIContent> mList;
};
/**
* Class used to detect unexpected mutations. To use the class create an
* nsMutationGuard on the stack before unexpected mutations could occur.
* You can then at any time call Mutated to check if any unexpected mutations
* have occured.
*
* When a guard is instantiated sMutationCount is set to 300. It is then
* decremented by every mutation (capped at 0). This means that we can only
* detect 300 mutations during the lifetime of a single guard, however that
* should be more then we ever care about as we usually only care if more then
* one mutation has occured.
*
* When the guard goes out of scope it will adjust sMutationCount so that over
* the lifetime of the guard the guard itself has not affected sMutationCount,
* while mutations that happened while the guard was alive still will. This
* allows a guard to be instantiated even if there is another guard higher up
* on the callstack watching for mutations.
*
* The only thing that has to be avoided is for an outer guard to be used
* while an inner guard is alive. This can be avoided by only ever
* instantiating a single guard per scope and only using the guard in the
* current scope.
*/
class nsMutationGuard {
public:
nsMutationGuard()
{
mDelta = eMaxMutations - sMutationCount;
sMutationCount = eMaxMutations;
}
~nsMutationGuard()
{
sMutationCount =
mDelta > sMutationCount ? 0 : sMutationCount - mDelta;
}
/**
* Returns true if any unexpected mutations have occured. You can pass in
* an 8-bit ignore count to ignore a number of expected mutations.
*/
PRBool Mutated(PRUint8 aIgnoreCount)
{
return sMutationCount < static_cast<PRUint32>(eMaxMutations - aIgnoreCount);
}
// This function should be called whenever a mutation that we want to keep
// track of happen. For now this is only done when children are added or
// removed, but we might do it for attribute changes too in the future.
static void DidMutate()
{
if (sMutationCount) {
--sMutationCount;
}
}
private:
// mDelta is the amount sMutationCount was adjusted when the guard was
// initialized. It is needed so that we can undo that adjustment once
// the guard dies.
PRUint32 mDelta;
// The value 300 is not important, as long as it is bigger then anything
// ever passed to Mutated().
enum { eMaxMutations = 300 };
// sMutationCount is a global mutation counter which is decreased by one at
// every mutation. It is capped at 0 to avoid wrapping.
// It's value is always between 0 and 300, inclusive.
static PRUint32 sMutationCount;
};
// Forward declare to allow being a friend
class nsNSElementTearoff;

View File

@ -53,6 +53,7 @@
#ifdef MOZ_XUL
#include "nsXULElement.h"
#endif
#include "nsBindingManager.h"
// This macro expects the ownerDocument of content_ to be in scope as
// |nsIDocument* doc|

View File

@ -163,11 +163,11 @@ nsAsyncInstantiateEvent::Run()
class nsPluginErrorEvent : public nsRunnable {
public:
nsCOMPtr<nsIContent> mContent;
PRBool mBlocklisted;
PluginSupportState mState;
nsPluginErrorEvent(nsIContent* aContent, PRBool aBlocklisted)
nsPluginErrorEvent(nsIContent* aContent, PluginSupportState aState)
: mContent(aContent),
mBlocklisted(aBlocklisted)
mState(aState)
{}
~nsPluginErrorEvent() {}
@ -180,14 +180,22 @@ nsPluginErrorEvent::Run()
{
LOG(("OBJLC []: Firing plugin not found event for content %p\n",
mContent.get()));
if (mBlocklisted)
nsContentUtils::DispatchTrustedEvent(mContent->GetDocument(), mContent,
NS_LITERAL_STRING("PluginBlocklisted"),
PR_TRUE, PR_TRUE);
else
nsContentUtils::DispatchTrustedEvent(mContent->GetDocument(), mContent,
NS_LITERAL_STRING("PluginNotFound"),
PR_TRUE, PR_TRUE);
nsString type;
switch (mState) {
case ePluginUnsupported:
type = NS_LITERAL_STRING("PluginNotFound");
break;
case ePluginDisabled:
type = NS_LITERAL_STRING("PluginDisabled");
break;
case ePluginBlocklisted:
type = NS_LITERAL_STRING("PluginBlocklisted");
break;
default:
return NS_OK;
}
nsContentUtils::DispatchTrustedEvent(mContent->GetDocument(), mContent,
type, PR_TRUE, PR_TRUE);
return NS_OK;
}
@ -232,28 +240,29 @@ class AutoNotifier {
class AutoFallback {
public:
AutoFallback(nsObjectLoadingContent* aContent, const nsresult* rv)
: mContent(aContent), mResult(rv), mTypeUnsupported(PR_FALSE) {}
: mContent(aContent), mResult(rv), mPluginState(ePluginOtherState) {}
~AutoFallback() {
if (NS_FAILED(*mResult)) {
LOG(("OBJLC [%p]: rv=%08x, falling back\n", mContent, *mResult));
mContent->Fallback(PR_FALSE);
if (mTypeUnsupported) {
mContent->mTypeUnsupported = PR_TRUE;
if (mPluginState != ePluginOtherState) {
mContent->mPluginState = mPluginState;
}
}
}
/**
* This function can be called to indicate that, after falling back,
* mTypeUnsupported should be set to true.
* This should be set to something other than ePluginOtherState to indicate
* a specific failure that should be passed on.
*/
void TypeUnsupported() {
mTypeUnsupported = PR_TRUE;
}
void SetPluginState(PluginSupportState aState) {
NS_ASSERTION(aState != ePluginOtherState, "Should not be setting ePluginOtherState");
mPluginState = aState;
}
private:
nsObjectLoadingContent* mContent;
const nsresult* mResult;
PRBool mTypeUnsupported;
PluginSupportState mPluginState;
};
/**
@ -340,7 +349,7 @@ nsObjectLoadingContent::nsObjectLoadingContent()
, mInstantiating(PR_FALSE)
, mUserDisabled(PR_FALSE)
, mSuppressed(PR_FALSE)
, mTypeUnsupported(PR_FALSE)
, mPluginState(ePluginOtherState)
{
}
@ -573,20 +582,16 @@ nsObjectLoadingContent::OnStartRequest(nsIRequest *aRequest,
case eType_Null:
LOG(("OBJLC [%p]: Unsupported type, falling back\n", this));
// Need to fallback here (instead of using the case below), so that we can
// set mTypeUnsupported without it being overwritten. This is also why we
// set mPluginState without it being overwritten. This is also why we
// return early.
Fallback(PR_FALSE);
PluginSupportState pluginState = GetPluginSupportState(thisContent,
mContentType);
// Do nothing, but fire the plugin not found event if needed
if (pluginState == ePluginUnsupported ||
pluginState == ePluginBlocklisted) {
FirePluginError(thisContent, pluginState == ePluginBlocklisted);
}
if (pluginState != ePluginDisabled &&
pluginState != ePluginBlocklisted) {
mTypeUnsupported = PR_TRUE;
if (pluginState != ePluginOtherState) {
FirePluginError(thisContent, pluginState);
mPluginState = pluginState;
}
return NS_BINDING_ABORTED;
}
@ -892,8 +897,16 @@ nsObjectLoadingContent::ObjectState() const
// Otherwise, broken
PRInt32 state = NS_EVENT_STATE_BROKEN;
if (mTypeUnsupported) {
state |= NS_EVENT_STATE_TYPE_UNSUPPORTED;
switch (mPluginState) {
case ePluginDisabled:
state |= NS_EVENT_STATE_HANDLER_DISABLED;
break;
case ePluginBlocklisted:
state |= NS_EVENT_STATE_HANDLER_BLOCKED;
break;
case ePluginUnsupported:
state |= NS_EVENT_STATE_TYPE_UNSUPPORTED;
break;
}
return state;
};
@ -961,16 +974,11 @@ nsObjectLoadingContent::UpdateFallbackState(nsIContent* aContent,
AutoFallback& fallback,
const nsCString& aTypeHint)
{
PluginSupportState pluginState = GetPluginDisabledState(aTypeHint);
if (pluginState == ePluginUnsupported) {
// For unknown plugins notify the UI and allow the unknown plugin binding
// to attach.
FirePluginError(aContent, PR_FALSE);
fallback.TypeUnsupported();
}
else if (pluginState == ePluginBlocklisted) {
// For blocklisted plugins just send a notification to the UI.
FirePluginError(aContent, PR_TRUE);
// Notify the UI and update the fallback state
PluginSupportState state = GetPluginSupportState(aContent, aTypeHint);
if (state != ePluginOtherState) {
fallback.SetPluginState(state);
FirePluginError(aContent, state);
}
}
@ -1448,7 +1456,8 @@ nsObjectLoadingContent::UnloadContent()
mFrameLoader = nsnull;
}
mType = eType_Null;
mUserDisabled = mSuppressed = mTypeUnsupported = PR_FALSE;
mUserDisabled = mSuppressed = PR_FALSE;
mPluginState = ePluginOtherState;
}
void
@ -1498,12 +1507,12 @@ nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType,
/* static */ void
nsObjectLoadingContent::FirePluginError(nsIContent* thisContent,
PRBool blocklisted)
PluginSupportState state)
{
LOG(("OBJLC []: Dispatching nsPluginErrorEvent for content %p\n",
thisContent));
nsCOMPtr<nsIRunnable> ev = new nsPluginErrorEvent(thisContent, blocklisted);
nsCOMPtr<nsIRunnable> ev = new nsPluginErrorEvent(thisContent, state);
nsresult rv = NS_DispatchToCurrentThread(ev);
if (NS_FAILED(rv)) {
NS_WARNING("failed to dispatch nsPluginErrorEvent");
@ -1766,7 +1775,7 @@ nsObjectLoadingContent::ShouldShowDefaultPlugin(nsIContent* aContent,
return GetPluginSupportState(aContent, aContentType) == ePluginUnsupported;
}
/* static */ nsObjectLoadingContent::PluginSupportState
/* static */ PluginSupportState
nsObjectLoadingContent::GetPluginSupportState(nsIContent* aContent,
const nsCString& aContentType)
{
@ -1803,7 +1812,7 @@ nsObjectLoadingContent::GetPluginSupportState(nsIContent* aContent,
GetPluginDisabledState(aContentType);
}
/* static */ nsObjectLoadingContent::PluginSupportState
/* static */ PluginSupportState
nsObjectLoadingContent::GetPluginDisabledState(const nsCString& aContentType)
{
nsCOMPtr<nsIPluginHost> host(do_GetService("@mozilla.org/plugin/host;1"));

View File

@ -59,6 +59,15 @@ class AutoNotifier;
class AutoFallback;
class AutoSetInstantiatingToFalse;
enum PluginSupportState {
ePluginUnsupported, // The plugin is not supported (not installed, say)
ePluginDisabled, // The plugin has been explicitly disabled by the
// user.
ePluginBlocklisted, // The plugin is blocklisted and disabled
ePluginOtherState // Something else (e.g. not a plugin at all as far
// as we can tell).
};
/**
* INVARIANTS OF THIS CLASS
* - mChannel is non-null between asyncOpen and onStopRequest (NOTE: Only needs
@ -252,7 +261,7 @@ class nsObjectLoadingContent : public nsImageLoadingContent
* Fires the "Plugin not found" event. This function doesn't do any checks
* whether it should be fired, the caller should do that.
*/
static void FirePluginError(nsIContent* thisContent, PRBool blocklisted);
static void FirePluginError(nsIContent* thisContent, PluginSupportState state);
ObjectType GetTypeOfContent(const nsCString& aMIMEType);
@ -332,15 +341,6 @@ class nsObjectLoadingContent : public nsImageLoadingContent
static PRBool ShouldShowDefaultPlugin(nsIContent* aContent,
const nsCString& aContentType);
enum PluginSupportState {
ePluginUnsupported, // The plugin is not supported (not installed, say)
ePluginDisabled, // The plugin has been explicitly disabled by the
// user.
ePluginBlocklisted, // The plugin is blocklisted and disabled
ePluginOtherState // Something else (e.g. not a plugin at all as far
// as we can tell).
};
/**
* Get the plugin support state for the given content node and MIME type.
* This is used for purposes of determining whether to fire PluginNotFound
@ -418,8 +418,8 @@ class nsObjectLoadingContent : public nsImageLoadingContent
// Blocking status from content policy
PRBool mUserDisabled : 1;
PRBool mSuppressed : 1;
// Whether we fell back because of an unsupported type
PRBool mTypeUnsupported:1;
// A specific state that caused us to fallback
PluginSupportState mPluginState;
friend class nsAsyncInstantiateEvent;
};

View File

@ -1157,6 +1157,10 @@ static nsresult SplitDataNode(nsIDOMCharacterData* aStartNode,
nsresult nsRange::CutContents(nsIDOMDocumentFragment** aFragment)
{
if (aFragment) {
*aFragment = nsnull;
}
if (IsDetached())
return NS_ERROR_DOM_INVALID_STATE_ERR;
@ -1196,7 +1200,11 @@ nsresult nsRange::CutContents(nsIDOMDocumentFragment** aFragment)
if (iter.IsDone())
{
// There's nothing for us to delete.
return CollapseRangeAfterDelete(this);
rv = CollapseRangeAfterDelete(this);
if (NS_SUCCEEDED(rv) && aFragment) {
NS_ADDREF(*aFragment = retval);
}
return rv;
}
// We delete backwards to avoid iterator problems!
@ -1355,11 +1363,11 @@ nsresult nsRange::CutContents(nsIDOMDocumentFragment** aFragment)
// XXX_kin: desired behavior. For now we don't merge anything!
// XXX ajvincent Filed as https://bugzilla.mozilla.org/show_bug.cgi?id=401276
if (aFragment) {
rv = CollapseRangeAfterDelete(this);
if (NS_SUCCEEDED(rv) && aFragment) {
NS_ADDREF(*aFragment = retval);
}
return CollapseRangeAfterDelete(this);
return rv;
}
nsresult nsRange::DeleteContents()

View File

@ -202,6 +202,10 @@ _TEST_FILES = test_bug5141.html \
test_bug28293.html \
file_bug28293.sjs \
test_title.html \
test_bug453521.html \
test_bug391728.html \
file_bug391728.html \
file_bug391728_2.html \
$(NULL)
libs:: $(_TEST_FILES)

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