diff --git a/accessible/public/nsIAccessibleEditableText.idl b/accessible/public/nsIAccessibleEditableText.idl index 14669159025..36b72a880bf 100644 --- a/accessible/public/nsIAccessibleEditableText.idl +++ b/accessible/public/nsIAccessibleEditableText.idl @@ -43,7 +43,7 @@ interface nsIEditor; -[scriptable, uuid(52837507-202d-4e72-a482-5f068a1fd720)] +[scriptable, uuid(e242d495-5cde-4b1c-8c84-2525b14939f5)] interface nsIAccessibleEditableText : nsISupports { /** @@ -103,19 +103,4 @@ interface nsIAccessibleEditableText : nsISupports * clipboard into the text represented by this object. */ void pasteText (in long position); - - /** - * Returns an editor associated with the accessible. - */ - [noscript] readonly attribute nsIEditor associatedEditor; }; - -/* - Assumptions: - - selectAttributes method takes an nsISupports parameter. - 'set' methods throw exception on failure. - 'wstring' inputs are potentially multibyte (UTF-16 for - instance); 'string' and UTF-8 may be a better choice. - -*/ diff --git a/accessible/src/base/NotificationController.cpp b/accessible/src/base/NotificationController.cpp index 5a1db68a8d2..f0c99cb5cb8 100644 --- a/accessible/src/base/NotificationController.cpp +++ b/accessible/src/base/NotificationController.cpp @@ -680,8 +680,7 @@ NotificationController::CreateTextChangeEventFor(AccMutationEvent* aEvent) // Don't fire event for the first html:br in an editor. if (aEvent->mAccessible->Role() == roles::WHITESPACE) { - nsCOMPtr editor; - textAccessible->GetAssociatedEditor(getter_AddRefs(editor)); + nsCOMPtr editor = textAccessible->GetEditor(); if (editor) { bool isEmpty = false; editor->GetDocumentIsEmpty(&isEmpty); diff --git a/accessible/src/base/nsAccessible.h b/accessible/src/base/nsAccessible.h index 5b638e4623d..5a40ed68e83 100644 --- a/accessible/src/base/nsAccessible.h +++ b/accessible/src/base/nsAccessible.h @@ -214,6 +214,11 @@ public: */ virtual PRUint64 NativeState(); + /** + * Return bit set of invisible and offscreen states. + */ + PRUint64 VisibilityState(); + /** * Returns attributes for accessible without explicitly setted ARIA * attributes. @@ -702,8 +707,6 @@ protected: virtual nsIFrame* GetBoundsFrame(); virtual void GetBoundsRect(nsRect& aRect, nsIFrame** aRelativeFrame); - PRUint64 VisibilityState(); - ////////////////////////////////////////////////////////////////////////////// // Name helpers diff --git a/accessible/src/base/nsDocAccessible.cpp b/accessible/src/base/nsDocAccessible.cpp index 483f642c457..8eeb28aa06c 100644 --- a/accessible/src/base/nsDocAccessible.cpp +++ b/accessible/src/base/nsDocAccessible.cpp @@ -333,8 +333,7 @@ nsDocAccessible::NativeState() state |= states::INVISIBLE | states::OFFSCREEN; } - nsCOMPtr editor; - GetAssociatedEditor(getter_AddRefs(editor)); + nsCOMPtr editor = GetEditor(); state |= editor ? states::EDITABLE : states::READONLY; return state; @@ -553,37 +552,32 @@ nsDocAccessible::GetVirtualCursor(nsIAccessiblePivot** aVirtualCursor) return NS_OK; } -// nsIAccessibleHyperText method -NS_IMETHODIMP nsDocAccessible::GetAssociatedEditor(nsIEditor **aEditor) +// nsHyperTextAccessible method +already_AddRefed +nsDocAccessible::GetEditor() const { - NS_ENSURE_ARG_POINTER(aEditor); - *aEditor = nsnull; - - if (IsDefunct()) - return NS_ERROR_FAILURE; - // Check if document is editable (designMode="on" case). Otherwise check if // the html:body (for HTML document case) or document element is editable. if (!mDocument->HasFlag(NODE_IS_EDITABLE) && !mContent->HasFlag(NODE_IS_EDITABLE)) - return NS_OK; + return nsnull; nsCOMPtr container = mDocument->GetContainer(); nsCOMPtr editingSession(do_GetInterface(container)); if (!editingSession) - return NS_OK; // No editing session interface + return nsnull; // No editing session interface nsCOMPtr editor; editingSession->GetEditorForWindow(mDocument->GetWindow(), getter_AddRefs(editor)); - if (!editor) { - return NS_OK; - } - bool isEditable; + if (!editor) + return nsnull; + + bool isEditable = false; editor->GetIsDocumentEditable(&isEditable); - if (isEditable) { - NS_ADDREF(*aEditor = editor); - } - return NS_OK; + if (isEditable) + return editor.forget(); + + return nsnull; } // nsDocAccessible public method diff --git a/accessible/src/base/nsDocAccessible.h b/accessible/src/base/nsDocAccessible.h index 47cc3e1da3f..5368be297b6 100644 --- a/accessible/src/base/nsDocAccessible.h +++ b/accessible/src/base/nsDocAccessible.h @@ -133,8 +133,8 @@ public: virtual nsresult HandleAccEvent(AccEvent* aAccEvent); #endif - // nsIAccessibleText - NS_IMETHOD GetAssociatedEditor(nsIEditor **aEditor); + // nsHyperTextAccessible + virtual already_AddRefed GetEditor() const; // nsDocAccessible diff --git a/accessible/src/html/nsHTMLFormControlAccessible.cpp b/accessible/src/html/nsHTMLFormControlAccessible.cpp index 98c8771c4e0..defd4c5208f 100644 --- a/accessible/src/html/nsHTMLFormControlAccessible.cpp +++ b/accessible/src/html/nsHTMLFormControlAccessible.cpp @@ -544,11 +544,12 @@ NS_IMETHODIMP nsHTMLTextFieldAccessible::DoAction(PRUint8 index) return NS_ERROR_INVALID_ARG; } -NS_IMETHODIMP nsHTMLTextFieldAccessible::GetAssociatedEditor(nsIEditor **aEditor) +already_AddRefed +nsHTMLTextFieldAccessible::GetEditor() const { - *aEditor = nsnull; nsCOMPtr editableElt(do_QueryInterface(mContent)); - NS_ENSURE_TRUE(editableElt, NS_ERROR_FAILURE); + if (!editableElt) + return nsnull; // nsGenericHTMLElement::GetEditor has a security check. // Make sure we're not restricted by the permissions of @@ -558,7 +559,7 @@ NS_IMETHODIMP nsHTMLTextFieldAccessible::GetAssociatedEditor(nsIEditor **aEditor bool pushed = stack && NS_SUCCEEDED(stack->Push(nsnull)); nsCOMPtr editor; - nsresult rv = editableElt->GetEditor(aEditor); + editableElt->GetEditor(getter_AddRefs(editor)); if (pushed) { JSContext* cx; @@ -566,7 +567,7 @@ NS_IMETHODIMP nsHTMLTextFieldAccessible::GetAssociatedEditor(nsIEditor **aEditor NS_ASSERTION(!cx, "context should be null"); } - return rv; + return editor.forget(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/accessible/src/html/nsHTMLFormControlAccessible.h b/accessible/src/html/nsHTMLFormControlAccessible.h index 0dd3a0fb321..8bc5ba49238 100644 --- a/accessible/src/html/nsHTMLFormControlAccessible.h +++ b/accessible/src/html/nsHTMLFormControlAccessible.h @@ -138,8 +138,8 @@ public: NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName); NS_IMETHOD DoAction(PRUint8 index); - // nsIAccessibleEditableText - NS_IMETHOD GetAssociatedEditor(nsIEditor **aEditor); + // nsHyperTextAccessible + virtual already_AddRefed GetEditor() const; // nsAccessible virtual void ApplyARIAState(PRUint64* aState); diff --git a/accessible/src/html/nsHyperTextAccessible.cpp b/accessible/src/html/nsHyperTextAccessible.cpp index 31426a26ef2..b51741676ba 100644 --- a/accessible/src/html/nsHyperTextAccessible.cpp +++ b/accessible/src/html/nsHyperTextAccessible.cpp @@ -166,8 +166,7 @@ nsHyperTextAccessible::NativeState() { PRUint64 states = nsAccessibleWrap::NativeState(); - nsCOMPtr editor; - GetAssociatedEditor(getter_AddRefs(editor)); + nsCOMPtr editor = GetEditor(); if (editor) { PRUint32 flags; editor->GetFlags(&flags); @@ -711,8 +710,7 @@ nsHyperTextAccessible::HypertextOffsetsToDOMRange(PRInt32 aStartHTOffset, // If the given offsets are 0 and associated editor is empty then return // collapsed range with editor root element as range container. if (aStartHTOffset == 0 && aEndHTOffset == 0) { - nsCOMPtr editor; - GetAssociatedEditor(getter_AddRefs(editor)); + nsCOMPtr editor = GetEditor(); if (editor) { bool isEmpty = false; editor->GetDocumentIsEmpty(&isEmpty); @@ -1455,8 +1453,10 @@ NS_IMETHODIMP nsHyperTextAccessible::SetTextContents(const nsAString &aText) NS_IMETHODIMP nsHyperTextAccessible::InsertText(const nsAString &aText, PRInt32 aPosition) { - nsCOMPtr editor; - GetAssociatedEditor(getter_AddRefs(editor)); + if (IsDefunct()) + return NS_ERROR_FAILURE; + + nsCOMPtr editor = GetEditor(); nsCOMPtr peditor(do_QueryInterface(editor)); NS_ENSURE_STATE(peditor); @@ -1470,8 +1470,10 @@ nsHyperTextAccessible::InsertText(const nsAString &aText, PRInt32 aPosition) NS_IMETHODIMP nsHyperTextAccessible::CopyText(PRInt32 aStartPos, PRInt32 aEndPos) { - nsCOMPtr editor; - GetAssociatedEditor(getter_AddRefs(editor)); + if (IsDefunct()) + return NS_ERROR_FAILURE; + + nsCOMPtr editor = GetEditor(); NS_ENSURE_STATE(editor); nsresult rv = SetSelectionRange(aStartPos, aEndPos); @@ -1483,8 +1485,10 @@ nsHyperTextAccessible::CopyText(PRInt32 aStartPos, PRInt32 aEndPos) NS_IMETHODIMP nsHyperTextAccessible::CutText(PRInt32 aStartPos, PRInt32 aEndPos) { - nsCOMPtr editor; - GetAssociatedEditor(getter_AddRefs(editor)); + if (IsDefunct()) + return NS_ERROR_FAILURE; + + nsCOMPtr editor = GetEditor(); NS_ENSURE_STATE(editor); nsresult rv = SetSelectionRange(aStartPos, aEndPos); @@ -1496,8 +1500,10 @@ nsHyperTextAccessible::CutText(PRInt32 aStartPos, PRInt32 aEndPos) NS_IMETHODIMP nsHyperTextAccessible::DeleteText(PRInt32 aStartPos, PRInt32 aEndPos) { - nsCOMPtr editor; - GetAssociatedEditor(getter_AddRefs(editor)); + if (IsDefunct()) + return NS_ERROR_FAILURE; + + nsCOMPtr editor = GetEditor(); NS_ENSURE_STATE(editor); nsresult rv = SetSelectionRange(aStartPos, aEndPos); @@ -1509,8 +1515,10 @@ nsHyperTextAccessible::DeleteText(PRInt32 aStartPos, PRInt32 aEndPos) NS_IMETHODIMP nsHyperTextAccessible::PasteText(PRInt32 aPosition) { - nsCOMPtr editor; - GetAssociatedEditor(getter_AddRefs(editor)); + if (IsDefunct()) + return NS_ERROR_FAILURE; + + nsCOMPtr editor = GetEditor(); NS_ENSURE_STATE(editor); nsresult rv = SetSelectionRange(aPosition, aPosition); @@ -1519,44 +1527,37 @@ nsHyperTextAccessible::PasteText(PRInt32 aPosition) return editor->Paste(nsIClipboard::kGlobalClipboard); } -NS_IMETHODIMP -nsHyperTextAccessible::GetAssociatedEditor(nsIEditor **aEditor) +already_AddRefed +nsHyperTextAccessible::GetEditor() const { - NS_ENSURE_ARG_POINTER(aEditor); - *aEditor = nsnull; - - if (IsDefunct()) - return NS_ERROR_FAILURE; - if (!mContent->HasFlag(NODE_IS_EDITABLE)) { // If we're inside an editable container, then return that container's editor - nsCOMPtr ancestor, current = this; - while (NS_SUCCEEDED(current->GetParent(getter_AddRefs(ancestor))) && ancestor) { - nsRefPtr ancestorTextAccessible; - ancestor->QueryInterface(NS_GET_IID(nsHyperTextAccessible), - getter_AddRefs(ancestorTextAccessible)); - if (ancestorTextAccessible) { + nsAccessible* ancestor = Parent(); + while (ancestor) { + nsHyperTextAccessible* hyperText = ancestor->AsHyperText(); + if (hyperText) { // Recursion will stop at container doc because it has its own impl - // of GetAssociatedEditor() - return ancestorTextAccessible->GetAssociatedEditor(aEditor); + // of GetEditor() + return hyperText->GetEditor(); } - current = ancestor; + + ancestor = ancestor->Parent(); } - return NS_OK; + + return nsnull; } nsCOMPtr docShellTreeItem = nsCoreUtils::GetDocShellTreeItemFor(mContent); nsCOMPtr editingSession(do_GetInterface(docShellTreeItem)); if (!editingSession) - return NS_OK; // No editing session interface - - NS_ENSURE_TRUE(mDoc, NS_ERROR_FAILURE); - nsIDocument* docNode = mDoc->GetDocumentNode(); - NS_ENSURE_TRUE(docNode, NS_ERROR_FAILURE); + return nsnull; // No editing session interface nsCOMPtr editor; - return editingSession->GetEditorForWindow(docNode->GetWindow(), aEditor); + nsIDocument* docNode = mDoc->GetDocumentNode(); + editingSession->GetEditorForWindow(docNode->GetWindow(), + getter_AddRefs(editor)); + return editor.forget(); } /** @@ -1770,8 +1771,7 @@ nsHyperTextAccessible::GetSelectionDOMRanges(PRInt16 aType, nsCOMPtr startNode = GetNode(); - nsCOMPtr editor; - GetAssociatedEditor(getter_AddRefs(editor)); + nsCOMPtr editor = GetEditor(); if (editor) { nsCOMPtr editorRoot; editor->GetRootElement(getter_AddRefs(editorRoot)); diff --git a/accessible/src/html/nsHyperTextAccessible.h b/accessible/src/html/nsHyperTextAccessible.h index a53d20bb7b7..8c42d12d7a6 100644 --- a/accessible/src/html/nsHyperTextAccessible.h +++ b/accessible/src/html/nsHyperTextAccessible.h @@ -264,6 +264,14 @@ public: return GetChildAt(GetChildIndexAtOffset(aOffset)); } + ////////////////////////////////////////////////////////////////////////////// + // EditableTextAccessible + + /** + * Return the editor associated with the accessible. + */ + virtual already_AddRefed GetEditor() const; + protected: // nsHyperTextAccessible diff --git a/accessible/src/xforms/nsXFormsAccessible.cpp b/accessible/src/xforms/nsXFormsAccessible.cpp index fd65c81a64a..2cc0c1e9e94 100644 --- a/accessible/src/xforms/nsXFormsAccessible.cpp +++ b/accessible/src/xforms/nsXFormsAccessible.cpp @@ -273,8 +273,7 @@ nsXFormsEditableAccessible::NativeState() } } - nsCOMPtr editor; - GetAssociatedEditor(getter_AddRefs(editor)); + nsCOMPtr editor = GetEditor(); NS_ENSURE_TRUE(editor, state); PRUint32 flags; editor->GetFlags(&flags); @@ -286,11 +285,14 @@ nsXFormsEditableAccessible::NativeState() return state; } -NS_IMETHODIMP -nsXFormsEditableAccessible::GetAssociatedEditor(nsIEditor **aEditor) +already_AddRefed +nsXFormsEditableAccessible::GetEditor() const { nsCOMPtr DOMNode(do_QueryInterface(mContent)); - return sXFormsService->GetEditor(DOMNode, aEditor); + + nsCOMPtr editor; + sXFormsService->GetEditor(DOMNode, getter_AddRefs(editor)); + return editor.forget(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/accessible/src/xforms/nsXFormsAccessible.h b/accessible/src/xforms/nsXFormsAccessible.h index 512ea7fefac..76350d639b8 100644 --- a/accessible/src/xforms/nsXFormsAccessible.h +++ b/accessible/src/xforms/nsXFormsAccessible.h @@ -144,8 +144,8 @@ class nsXFormsEditableAccessible : public nsXFormsAccessible public: nsXFormsEditableAccessible(nsIContent* aContent, nsDocAccessible* aDoc); - // nsIAccessibleEditableText - NS_IMETHOD GetAssociatedEditor(nsIEditor **aEditor); + // nsHyperTextAccessible + virtual already_AddRefed GetEditor() const; // nsAccessible virtual PRUint64 NativeState(); diff --git a/accessible/src/xul/nsXULFormControlAccessible.cpp b/accessible/src/xul/nsXULFormControlAccessible.cpp index 8ff7c46d2fa..671a515dda7 100644 --- a/accessible/src/xul/nsXULFormControlAccessible.cpp +++ b/accessible/src/xul/nsXULFormControlAccessible.cpp @@ -850,14 +850,17 @@ nsXULTextFieldAccessible::CanHaveAnonChildren() return false; } -NS_IMETHODIMP nsXULTextFieldAccessible::GetAssociatedEditor(nsIEditor **aEditor) +already_AddRefed +nsXULTextFieldAccessible::GetEditor() const { - *aEditor = nsnull; - nsCOMPtr inputField = GetInputField(); nsCOMPtr editableElt(do_QueryInterface(inputField)); - NS_ENSURE_TRUE(editableElt, NS_ERROR_FAILURE); - return editableElt->GetEditor(aEditor); + if (!editableElt) + return nsnull; + + nsCOMPtr editor; + editableElt->GetEditor(getter_AddRefs(editor)); + return editor.forget(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/accessible/src/xul/nsXULFormControlAccessible.h b/accessible/src/xul/nsXULFormControlAccessible.h index 965119dd967..a7bb22aa47e 100644 --- a/accessible/src/xul/nsXULFormControlAccessible.h +++ b/accessible/src/xul/nsXULFormControlAccessible.h @@ -260,8 +260,8 @@ public: NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName); NS_IMETHOD DoAction(PRUint8 index); - // nsIAccessibleEditableText - NS_IMETHOD GetAssociatedEditor(nsIEditor **aEditor); + // nsHyperTextAccessible + virtual already_AddRefed GetEditor() const; // nsAccessible virtual void ApplyARIAState(PRUint64* aState); diff --git a/accessible/tests/mochitest/common.js b/accessible/tests/mochitest/common.js index d17b8c1721d..a80193e6fad 100644 --- a/accessible/tests/mochitest/common.js +++ b/accessible/tests/mochitest/common.js @@ -613,9 +613,9 @@ function getNodePrettyName(aNode) function getObjAddress(aObj) { var exp = /native\s*@\s*(0x[a-f0-9]+)/g; - var match = exp.exec(aObj.valueOf()); + var match = exp.exec(aObj.toString()); if (match) return match[1]; - return aObj.valueOf(); + return aObj.toString(); } diff --git a/accessible/tests/mochitest/events.js b/accessible/tests/mochitest/events.js index 8a2dd91c37d..f64bf4d779b 100644 --- a/accessible/tests/mochitest/events.js +++ b/accessible/tests/mochitest/events.js @@ -304,12 +304,20 @@ function eventQueue(aEventType) // Start processing of next invoker. invoker = this.getNextInvoker(); + this.setEventHandler(invoker); + if (gLogger.isEnabled()) { gLogger.logToConsole("Event queue: \n invoke: " + invoker.getID()); gLogger.logToDOM("EQ: invoke: " + invoker.getID(), true); } - this.setEventHandler(invoker); + var infoText = "Invoke the '" + invoker.getID() + "' test { "; + for (var idx = 0; idx < this.mEventSeq.length; idx++) { + infoText += this.isEventUnexpected(idx) ? "un" : ""; + infoText += "expected '" + this.getEventTypeAsString(idx) + "' event; "; + } + infoText += " }"; + info(infoText); if (invoker.invoke() == INVOKER_ACTION_FAILED) { // Invoker failed to prepare action, fail and finish tests. diff --git a/aclocal.m4 b/aclocal.m4 index 77f9452fb6c..c8a04e5737e 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -18,6 +18,7 @@ builtin(include, build/autoconf/lto.m4)dnl builtin(include, build/autoconf/gcc-pr49911.m4)dnl builtin(include, build/autoconf/frameptr.m4)dnl builtin(include, build/autoconf/compiler-opts.m4)dnl +builtin(include, build/autoconf/expandlibs.m4)dnl MOZ_PROG_CHECKMSYS() diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index ea78fb7a147..cea04b8f81a 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -46,7 +46,7 @@ pref("browser.homescreenURL", "file:///data/local/homescreen.html,file:///system #endif // URL for the dialer application. -pref("dom.telephony.app.phone.url", "http://localhost:7777/data/local/apps/dialer/dialer.html"); +pref("dom.telephony.app.phone.url", "http://localhost:7777/data/local/apps/dialer/dialer.html http://localhost:7777/data/local/apps/homescreen/homescreen.html http://localhost:7777/apps/dialer/dialer.html http://localhost:7777/apps/homescreen/homescreen.html"); // Device pixel to CSS px ratio, in percent. Set to -1 to calculate based on display density. pref("browser.viewport.scaleRatio", -1); diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index b47c149a259..3f1ccb08dc5 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -66,7 +66,7 @@ pref("extensions.getAddons.cache.enabled", true); pref("extensions.getAddons.maxResults", 15); pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/guid:%IDS%?src=firefox&appOS=%OS%&appVersion=%VERSION%"); pref("extensions.getAddons.getWithPerformance.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/guid:%IDS%?src=firefox&appOS=%OS%&appVersion=%VERSION%&tMain=%TIME_MAIN%&tFirstPaint=%TIME_FIRST_PAINT%&tSessionRestored=%TIME_SESSION_RESTORED%"); -pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/firefox/search?q=%TERMS%"); +pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/firefox/search?q=%TERMS%&platform=%OS%&appver=%VERSION%"); pref("extensions.getAddons.search.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/%TERMS%/all/%MAX_RESULTS%/%OS%/%VERSION%/%COMPATIBILITY_MODE%?src=firefox"); pref("extensions.webservice.discoverURL", "https://services.addons.mozilla.org/%LOCALE%/firefox/discovery/pane/%VERSION%/%OS%/%COMPATIBILITY_MODE%"); @@ -214,6 +214,7 @@ pref("app.update.service.enabled", true); // pref("extensions.update.enabled", true); pref("extensions.update.url", "https://versioncheck.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%¤tAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%"); +pref("extensions.update.background.url", "https://versioncheck-bg.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%¤tAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%"); pref("extensions.update.interval", 86400); // Check for updates to Extensions and // Themes every day // Non-symmetric (not shared by extensions) extension-specific [update] preferences @@ -1041,6 +1042,9 @@ pref("devtools.debugger.enabled", false); // The default Debugger UI height pref("devtools.debugger.ui.height", 250); +// Disable remote debugging protocol logging +pref("devtools.debugger.log", false); + // Enable the style inspector pref("devtools.styleinspector.enabled", true); diff --git a/browser/base/content/newtab/dropTargetShim.js b/browser/base/content/newtab/dropTargetShim.js index 5e6ea5c3c1e..a2c708f66e1 100644 --- a/browser/base/content/newtab/dropTargetShim.js +++ b/browser/base/content/newtab/dropTargetShim.js @@ -40,10 +40,12 @@ let gDropTargetShim = { * @param aEvent The 'dragstart' event. */ _start: function DropTargetShim_start(aEvent) { - gGrid.lock(); + if (aEvent.target.classList.contains("site")) { + gGrid.lock(); - // XXX bug 505521 - Listen for dragover on the document. - document.documentElement.addEventListener("dragover", this._dragover, false); + // XXX bug 505521 - Listen for dragover on the document. + document.documentElement.addEventListener("dragover", this._dragover, false); + } }, /** diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js index e54cadbe2f6..f6f44210dfd 100644 --- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -850,7 +850,7 @@ nsContextMenu.prototype = { let uri = makeURI(this.mediaURL); let url = uri.QueryInterface(Ci.nsIURL); if (url.fileBaseName) - name = url.fileBaseName + ".jpg"; + name = decodeURI(url.fileBaseName) + ".jpg"; } catch (e) { } if (!name) name = "snapshot.jpg"; diff --git a/browser/base/content/test/newtab/Makefile.in b/browser/base/content/test/newtab/Makefile.in index 9718eaf4931..00d30c17b34 100644 --- a/browser/base/content/test/newtab/Makefile.in +++ b/browser/base/content/test/newtab/Makefile.in @@ -20,7 +20,9 @@ _BROWSER_FILES = \ browser_newtab_reset.js \ browser_newtab_tabsync.js \ browser_newtab_unpin.js \ + browser_newtab_bug722273.js \ browser_newtab_bug723102.js \ + browser_newtab_bug723121.js \ head.js \ $(NULL) diff --git a/browser/base/content/test/newtab/browser_newtab_bug722273.js b/browser/base/content/test/newtab/browser_newtab_bug722273.js new file mode 100644 index 00000000000..a929aa8db90 --- /dev/null +++ b/browser/base/content/test/newtab/browser_newtab_bug722273.js @@ -0,0 +1,62 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const NOW = Date.now() * 1000; +const URL = "http://fake-site.com/"; + +let tmp = {}; +Cu.import("resource:///modules/NewTabUtils.jsm", tmp); +Cc["@mozilla.org/moz/jssubscript-loader;1"] + .getService(Ci.mozIJSSubScriptLoader) + .loadSubScript("chrome://browser/content/sanitize.js", tmp); + +let {NewTabUtils, Sanitizer} = tmp; + +let bhist = Cc["@mozilla.org/browser/global-history;2"] + .getService(Ci.nsIBrowserHistory); + +function runTests() { + clearHistory(); + fillHistory(); + yield addNewTabPageTab(); + + is(cells[0].site.url, URL, "first site is our fake site"); + + let page = { + update: function () { + executeSoon(TestRunner.next); + }, + + observe: function () {} + }; + + NewTabUtils.allPages.register(page); + yield clearHistory(); + + NewTabUtils.allPages.unregister(page); + ok(!cells[0].site, "the fake site is gone"); +} + +function fillHistory() { + let uri = makeURI(URL); + for (let i = 59; i > 0; i--) + bhist.addPageWithDetails(uri, "fake site", NOW - i * 60 * 1000000); +} + +function clearHistory() { + let s = new Sanitizer(); + s.prefDomain = "privacy.cpd."; + + let prefs = gPrefService.getBranch(s.prefDomain); + prefs.setBoolPref("history", true); + prefs.setBoolPref("downloads", false); + prefs.setBoolPref("cache", false); + prefs.setBoolPref("cookies", false); + prefs.setBoolPref("formdata", false); + prefs.setBoolPref("offlineApps", false); + prefs.setBoolPref("passwords", false); + prefs.setBoolPref("sessions", false); + prefs.setBoolPref("siteSettings", false); + + s.sanitize(); +} diff --git a/browser/base/content/test/newtab/browser_newtab_bug723121.js b/browser/base/content/test/newtab/browser_newtab_bug723121.js new file mode 100644 index 00000000000..d71a7396616 --- /dev/null +++ b/browser/base/content/test/newtab/browser_newtab_bug723121.js @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function runTests() { + setLinks("0,1,2,3,4,5,6,7,8"); + setPinnedLinks(""); + + yield addNewTabPageTab(); + checkGridLocked(false, "grid is unlocked"); + + let cell = cells[0].node; + let site = cells[0].site.node; + + sendDragEvent(site, "dragstart"); + checkGridLocked(true, "grid is now locked"); + + sendDragEvent(site, "dragend"); + checkGridLocked(false, "grid isn't locked anymore"); + + sendDragEvent(cell, "dragstart"); + checkGridLocked(false, "grid isn't locked - dragstart was ignored"); +} + +function checkGridLocked(aLocked, aMessage) { + is(cw.gGrid.node.hasAttribute("locked"), aLocked, aMessage); +} + +function sendDragEvent(aNode, aType) { + let ifaceReq = cw.QueryInterface(Ci.nsIInterfaceRequestor); + let windowUtils = ifaceReq.getInterface(Ci.nsIDOMWindowUtils); + + let dataTransfer = { + mozUserCancelled: false, + setData: function () null, + setDragImage: function () null, + getData: function () "about:blank" + }; + + let event = cw.document.createEvent("DragEvents"); + event.initDragEvent(aType, true, true, cw, 0, 0, 0, 0, 0, + false, false, false, false, 0, null, dataTransfer); + + windowUtils.dispatchDOMEventViaPresShell(aNode, event, true); +} diff --git a/browser/base/content/test/newtab/head.js b/browser/base/content/test/newtab/head.js index 1695c90e22d..0800f1ca823 100644 --- a/browser/base/content/test/newtab/head.js +++ b/browser/base/content/test/newtab/head.js @@ -129,10 +129,11 @@ function addNewTabPageTab() { cw = browser.contentWindow; if (NewTabUtils.allPages.enabled) { - cells = cw.gGrid.cells; - // Continue when the link cache has been populated. - NewTabUtils.links.populateCache(TestRunner.next); + NewTabUtils.links.populateCache(function () { + cells = cw.gGrid.cells; + executeSoon(TestRunner.next); + }); } else { TestRunner.next(); } @@ -246,6 +247,8 @@ function unpinCell(aCell) { */ function simulateDrop(aDropTarget, aDragSource) { let event = { + clientX: 0, + clientY: 0, dataTransfer: { mozUserCancelled: false, setData: function () null, diff --git a/browser/components/certerror/content/aboutCertError.css b/browser/components/certerror/content/aboutCertError.css index c0f87365e67..e20977b8e18 100644 --- a/browser/components/certerror/content/aboutCertError.css +++ b/browser/components/certerror/content/aboutCertError.css @@ -47,20 +47,6 @@ white-space: pre-wrap; } -#technicalContent > h2, #expertContent > h2 { - cursor: pointer; - -moz-padding-start: 20px; - position: relative; - left: -20px; -} - -body[dir="rtl"] #technicalContent > h2, -body[dir="rtl"] #expertContent > h2 { - left: auto; - right: -20px; -} - -div[collapsed] > p, -div[collapsed] > div { +.expander[collapsed] + * { display: none; } diff --git a/browser/components/certerror/content/aboutCertError.xhtml b/browser/components/certerror/content/aboutCertError.xhtml index 64ec4201d5d..e54aba69596 100644 --- a/browser/components/certerror/content/aboutCertError.xhtml +++ b/browser/components/certerror/content/aboutCertError.xhtml @@ -260,18 +260,18 @@ -
-

&certerror.technical.heading;

-

-

+

+ +

+

-

-

&certerror.expert.heading;

-
-

&certerror.expert.content;

-

&certerror.expert.contentPara2;

- -
+

+ +

+
+

&certerror.expert.content;

+

&certerror.expert.contentPara2;

+
diff --git a/browser/components/dirprovider/DirectoryProvider.cpp b/browser/components/dirprovider/DirectoryProvider.cpp index 1373adc6c51..5df1419c206 100644 --- a/browser/components/dirprovider/DirectoryProvider.cpp +++ b/browser/components/dirprovider/DirectoryProvider.cpp @@ -56,6 +56,7 @@ #include "nsServiceManagerUtils.h" #include "nsStringAPI.h" #include "nsXULAppAPI.h" +#include "nsIPrefLocalizedString.h" namespace mozilla { namespace browser { @@ -200,7 +201,18 @@ AppendDistroSearchDirs(nsIProperties* aDirSvc, nsCOMArray &array) localePlugins->AppendNative(NS_LITERAL_CSTRING("locale")); nsCString locale; - rv = prefs->GetCharPref("general.useragent.locale", getter_Copies(locale)); + nsCOMPtr prefString; + rv = prefs->GetComplexValue("general.useragent.locale", + NS_GET_IID(nsIPrefLocalizedString), + getter_AddRefs(prefString)); + if (NS_SUCCEEDED(rv)) { + nsAutoString wLocale; + prefString->GetData(getter_Copies(wLocale)); + CopyUTF16toUTF8(wLocale, locale); + } else { + rv = prefs->GetCharPref("general.useragent.locale", getter_Copies(locale)); + } + if (NS_SUCCEEDED(rv)) { nsCOMPtr curLocalePlugins; diff --git a/browser/components/migration/Makefile.in b/browser/components/migration/Makefile.in index f606260619e..80d21a5d580 100644 --- a/browser/components/migration/Makefile.in +++ b/browser/components/migration/Makefile.in @@ -43,6 +43,8 @@ include $(DEPTH)/config/autoconf.mk DIRS = public src +TEST_DIRS += tests + include $(topsrcdir)/config/rules.mk # Needed for preprocessor removal of IE Profile Migrator label - bug 236901 diff --git a/browser/components/migration/content/migration.js b/browser/components/migration/content/migration.js index 3bc34c76e87..ffb0c345eb7 100644 --- a/browser/components/migration/content/migration.js +++ b/browser/components/migration/content/migration.js @@ -65,6 +65,7 @@ var MigrationWizard = { this._source = window.arguments[0]; this._migrator = window.arguments[1].QueryInterface(kIMig); this._autoMigrate = window.arguments[2].QueryInterface(kIPStartup); + this._skipImportSourcePage = window.arguments[3]; if (this._autoMigrate) { // Show the "nothing" option in the automigrate case to provide an @@ -94,7 +95,7 @@ var MigrationWizard = { // Reference to the "From File" radio button var fromfile = null; - //XXXquark This function is called before init, so check for bookmarks here + // init is not called when openDialog opens the wizard, so check for bookmarks here. if ("arguments" in window && window.arguments[0] == "bookmarks") { this._bookmarks = true; @@ -151,6 +152,12 @@ var MigrationWizard = { document.getElementById("importBookmarks").hidden = true; document.getElementById("importAll").hidden = true; } + + // Advance to the next page if the caller told us to. + if (this._migrator && this._skipImportSourcePage) { + this._wiz.advance(); + this._wiz.canRewind = false; + } }, onImportSourcePageAdvanced: function () diff --git a/browser/components/migration/src/FirefoxProfileMigrator.js b/browser/components/migration/src/FirefoxProfileMigrator.js index dad4217347b..7b4a6e4b772 100644 --- a/browser/components/migration/src/FirefoxProfileMigrator.js +++ b/browser/components/migration/src/FirefoxProfileMigrator.js @@ -227,6 +227,12 @@ FirefoxProfileMigrator.prototype = { this._replaceBookmarks = true; } + // Ensure that aProfile is not the current profile. + if (this._paths.currentProfile.path === this._sourceProfile.path) { + throw new Exception("Source and destination profiles are the same"); + return; + } + Services.obs.notifyObservers(null, "Migration:Started", null); // Reset pending count. If this count becomes 0, "Migration:Ended" @@ -278,6 +284,11 @@ FirefoxProfileMigrator.prototype = { this._sourceProfile.initWithPath(aProfile); let result = 0; + + // Ensure that aProfile is not the current profile. + if (this._paths.currentProfile.path === this._sourceProfile.path) + return result; + if (!this._sourceProfile.exists() || !this._sourceProfile.isReadable()) { Cu.reportError("source profile directory doesn't exist or is not readable"); return result; diff --git a/browser/components/migration/src/ProfileMigrator.js b/browser/components/migration/src/ProfileMigrator.js index b713068cba3..ea666753193 100644 --- a/browser/components/migration/src/ProfileMigrator.js +++ b/browser/components/migration/src/ProfileMigrator.js @@ -16,10 +16,25 @@ function ProfileMigrator() { } ProfileMigrator.prototype = { - migrate: function PM_migrate(aStartup) { + migrate: function PM_migrate(aStartup, aKey) { // By opening the wizard with a supplied migrator, it will automatically // migrate from it. - let [key, migrator] = this._getDefaultMigrator(); + let key = null, migrator = null; + let skipImportSourcePage = Cc["@mozilla.org/supports-PRBool;1"] + .createInstance(Ci.nsISupportsPRBool); + if (aKey) { + key = aKey; + migrator = this._getMigratorIfSourceExists(key); + if (!migrator) { + Cu.reportError("Invalid migrator key specified or source does not exist."); + return; + } + // If the migrator was passed to us from the caller, use that migrator + // and skip the import source page. + skipImportSourcePage.data = true; + } else { + [key, migrator] = this._getDefaultMigrator(); + } if (!key) return; @@ -27,6 +42,7 @@ ProfileMigrator.prototype = { params.appendElement(this._toCString(key), false); params.appendElement(migrator, false); params.appendElement(aStartup, false); + params.appendElement(skipImportSourcePage, false); Services.ww.openWindow(null, "chrome://browser/content/migration/migration.xul", @@ -43,10 +59,14 @@ ProfileMigrator.prototype = { }, _getMigratorIfSourceExists: function PM__getMigratorIfSourceExists(aKey) { - let cid = "@mozilla.org/profile/migrator;1?app=browser&type=" + aKey; - let migrator = Cc[cid].createInstance(Ci.nsIBrowserProfileMigrator); - if (migrator.sourceExists) - return migrator; + try { + let cid = "@mozilla.org/profile/migrator;1?app=browser&type=" + aKey; + let migrator = Cc[cid].createInstance(Ci.nsIBrowserProfileMigrator); + if (migrator.sourceExists) + return migrator; + } catch (ex) { + Cu.reportError("Could not get migrator: " + ex); + } return null; }, diff --git a/browser/components/migration/src/nsIEProfileMigrator.cpp b/browser/components/migration/src/nsIEProfileMigrator.cpp index f63b230f25d..636623bb2c3 100644 --- a/browser/components/migration/src/nsIEProfileMigrator.cpp +++ b/browser/components/migration/src/nsIEProfileMigrator.cpp @@ -1412,6 +1412,9 @@ nsIEProfileMigrator::CopyFavoritesBatched(bool aReplace) // Locate the Links toolbar folder, we want to replace the Personal Toolbar // content with Favorites in this folder. + // On versions minor or equal to IE6 the folder name is stored in the + // LinksFolderName registry key, but in newer versions it may be just a + // Links subfolder inside the default Favorites folder. nsCOMPtr regKey = do_CreateInstance("@mozilla.org/windows-registry-key;1"); if (regKey && @@ -1421,9 +1424,14 @@ nsIEProfileMigrator::CopyFavoritesBatched(bool aReplace) nsAutoString linksFolderName; if (NS_SUCCEEDED(regKey->ReadStringValue( NS_LITERAL_STRING("LinksFolderName"), - linksFolderName))) + linksFolderName))) { personalToolbarFolderName = linksFolderName; + } + else { + personalToolbarFolderName.AssignLiteral("Links"); + } } + folder = bookmarksMenuFolderId; } diff --git a/browser/components/migration/tests/Makefile.in b/browser/components/migration/tests/Makefile.in new file mode 100644 index 00000000000..9e62100e0b3 --- /dev/null +++ b/browser/components/migration/tests/Makefile.in @@ -0,0 +1,15 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. + +DEPTH = ../../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +relativesrcdir = browser/components/migration/tests + +include $(DEPTH)/config/autoconf.mk + +XPCSHELL_TESTS = unit + +include $(topsrcdir)/config/rules.mk diff --git a/browser/components/migration/tests/unit/bookmarks.html b/browser/components/migration/tests/unit/bookmarks.html new file mode 100644 index 00000000000..07b22e9b3fc --- /dev/null +++ b/browser/components/migration/tests/unit/bookmarks.html @@ -0,0 +1,16 @@ + + + +Bookmarks +

Bookmarks Menu

+ +

+

example +

Bookmarks Toolbar

+
Add bookmarks to this folder to see them displayed on the Bookmarks Toolbar +

+

example +

+

diff --git a/browser/components/migration/tests/unit/head_migration.js b/browser/components/migration/tests/unit/head_migration.js new file mode 100644 index 00000000000..f24123ffde9 --- /dev/null +++ b/browser/components/migration/tests/unit/head_migration.js @@ -0,0 +1,28 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +const IMIGRATOR = Ci.nsIBrowserProfileMigrator; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", + "resource://gre/modules/PlacesUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", + "resource://gre/modules/FileUtils.jsm"); + +// Initialize profile. +let gProfD = do_get_profile(); + +function newMigratorFor(aKey) { + let cid = "@mozilla.org/profile/migrator;1?app=browser&type=" + aKey; + return Cc[cid].createInstance(Ci.nsIBrowserProfileMigrator); +} + +let (bookmarkshtml = do_get_file("bookmarks.html")) { + bookmarkshtml.copyTo(gProfD, "bookmarks.html"); +} diff --git a/browser/components/migration/tests/unit/test_IE_bookmarks.js b/browser/components/migration/tests/unit/test_IE_bookmarks.js new file mode 100644 index 00000000000..6a3fd8a9b9b --- /dev/null +++ b/browser/components/migration/tests/unit/test_IE_bookmarks.js @@ -0,0 +1,28 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function run_test() { + let migrator = newMigratorFor("ie"); + + // Sanity check for the source. + do_check_true(migrator.sourceExists); + + // Ensure bookmarks migration is available. + let availableSources = migrator.getMigrateData("FieldOfFlowers", false); + do_check_true((availableSources & IMIGRATOR.BOOKMARKS) > 0); + + // Needed to enforce bookmarks replacing. + let startup = { + doStartup: function () {}, + get directory() do_get_profile() + } + migrator.migrate(IMIGRATOR.BOOKMARKS, startup, "FieldOfFlowers"); + + // Check that at least two bookmark have been added to the menu and the + // toolbar. The first one comes from bookmarks.html, the others from IE. + do_check_true(PlacesUtils.bookmarks + .getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 1) > 0); + do_check_true(PlacesUtils.bookmarks + .getIdForItemAt(PlacesUtils.toolbarFolderId, 1) > 0); +} diff --git a/browser/components/migration/tests/unit/xpcshell.ini b/browser/components/migration/tests/unit/xpcshell.ini new file mode 100644 index 00000000000..2704bc2cfbc --- /dev/null +++ b/browser/components/migration/tests/unit/xpcshell.ini @@ -0,0 +1,6 @@ +[DEFAULT] +head = head_migration.js +tail = + +[test_IE_bookmarks.js] +skip-if = os != "win" diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index 18117ba39c9..e029167ad4e 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -411,18 +411,20 @@ BrowserGlue.prototype = { // For any add-ons that were installed disabled and can be enabled offer // them to the user - var win = this.getMostRecentBrowserWindow(); - var browser = win.gBrowser; var changedIDs = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED); - AddonManager.getAddonsByIDs(changedIDs, function(aAddons) { - aAddons.forEach(function(aAddon) { - // If the add-on isn't user disabled or can't be enabled then skip it - if (!aAddon.userDisabled || !(aAddon.permissions & AddonManager.PERM_CAN_ENABLE)) - return; + if (changedIDs.length > 0) { + AddonManager.getAddonsByIDs(changedIDs, function(aAddons) { + var win = this.getMostRecentBrowserWindow(); + var browser = win.gBrowser; + aAddons.forEach(function(aAddon) { + // If the add-on isn't user disabled or can't be enabled then skip it. + if (!aAddon.userDisabled || !(aAddon.permissions & AddonManager.PERM_CAN_ENABLE)) + return; - browser.selectedTab = browser.addTab("about:newaddon?id=" + aAddon.id); - }) - }); + browser.selectedTab = browser.addTab("about:newaddon?id=" + aAddon.id); + }) + }); + } let keywordURLUserSet = Services.prefs.prefHasUserValue("keyword.URL"); Services.telemetry.getHistogramById("FX_KEYWORD_URL_USERSET").add(keywordURLUserSet); diff --git a/browser/components/places/content/browserPlacesViews.js b/browser/components/places/content/browserPlacesViews.js index 0cf3c274c0f..7ff3a4f66f7 100644 --- a/browser/components/places/content/browserPlacesViews.js +++ b/browser/components/places/content/browserPlacesViews.js @@ -657,7 +657,9 @@ PlacesViewBase.prototype = { aPlacesNode._siteURI = aLivemark.siteURI; if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED) { aLivemark.registerForUpdates(aPlacesNode, this); + // Prioritize the current livemark. aLivemark.reload(); + PlacesUtils.livemarks.reloadLivemarks(); if (shouldInvalidate) this.invalidateContainer(aPlacesNode); } @@ -900,11 +902,6 @@ PlacesToolbar.prototype = { __proto__: PlacesViewBase.prototype, _cbEvents: ["dragstart", "dragover", "dragexit", "dragend", "drop", -#ifdef XP_UNIX -#ifndef XP_MACOSX - "mousedown", "mouseup", -#endif -#endif "mousemove", "mouseover", "mouseout"], QueryInterface: function PT_QueryInterface(aIID) { @@ -1101,16 +1098,6 @@ PlacesToolbar.prototype = { case "mouseout": this._onMouseOut(aEvent); break; -#ifdef XP_UNIX -#ifndef XP_MACOSX - case "mouseup": - this._onMouseUp(aEvent); - break; - case "mousedown": - this._onMouseDown(aEvent); - break; -#endif -#endif case "popupshowing": this._onPopupShowing(aEvent); break; @@ -1538,14 +1525,6 @@ PlacesToolbar.prototype = { draggedElt.getAttribute("type") == "menu") { // If the drag gesture on a container is toward down we open instead // of dragging. -#ifdef XP_UNIX -#ifndef XP_MACOSX - if (this._mouseDownTimer) { - this._mouseDownTimer.cancel(); - this._mouseDownTimer = null; - } -#endif -#endif let translateY = this._cachedMouseMoveEvent.clientY - aEvent.clientY; let translateX = this._cachedMouseMoveEvent.clientX - aEvent.clientX; if ((translateY) >= Math.abs(translateX/2)) { @@ -1718,47 +1697,6 @@ PlacesToolbar.prototype = { } }, -#ifdef XP_UNIX -#ifndef XP_MACOSX - _onMouseDown: function PT__onMouseDown(aEvent) { - let target = aEvent.target; - if (aEvent.button == 0 && - target.localName == "toolbarbutton" && - target.getAttribute("type") == "menu") { - this._allowPopupShowing = false; - // On Linux we can open the popup only after a delay. - // Indeed as soon as the menupopup opens we are unable to start a - // drag aEvent. See bug 500081 for details. - this._mouseDownTimer = Cc["@mozilla.org/timer;1"]. - createInstance(Ci.nsITimer); - let callback = { - _self: this, - _target: target, - notify: function(timer) { - this._target.open = true; - this._mouseDownTimer = null; - } - }; - - this._mouseDownTimer.initWithCallback(callback, 300, - Ci.nsITimer.TYPE_ONE_SHOT); - } - }, - - _onMouseUp: function PT__onMouseUp(aEvent) { - if (aEvent.button != 0) - return; - - if (this._mouseDownTimer) { - // On a click (down/up), we should open the menu popup. - this._mouseDownTimer.cancel(); - this._mouseDownTimer = null; - aEvent.target.open = true; - } - }, -#endif -#endif - _onMouseMove: function PT__onMouseMove(aEvent) { // Used in dragStart to prevent dragging folders when dragging down. this._cachedMouseMoveEvent = aEvent; diff --git a/browser/components/places/content/editBookmarkOverlay.xul b/browser/components/places/content/editBookmarkOverlay.xul index 09796c72f83..5d09a876b4d 100644 --- a/browser/components/places/content/editBookmarkOverlay.xul +++ b/browser/components/places/content/editBookmarkOverlay.xul @@ -90,7 +90,6 @@ observes="paneElementsBroadcaster"/> @@ -102,7 +101,6 @@ observes="paneElementsBroadcaster"/> diff --git a/browser/components/places/content/treeView.js b/browser/components/places/content/treeView.js index f5ceb51db7f..c484cf35a98 100644 --- a/browser/components/places/content/treeView.js +++ b/browser/components/places/content/treeView.js @@ -896,7 +896,9 @@ PlacesTreeView.prototype = { aNode._feedURI = aLivemark.feedURI; if (aNewState == Components.interfaces.nsINavHistoryContainerResultNode.STATE_OPENED) { aLivemark.registerForUpdates(aNode, this); + // Prioritize the current livemark. aLivemark.reload(); + PlacesUtils.livemarks.reloadLivemarks(); if (shouldInvalidate) this.invalidateContainer(aNode); } diff --git a/browser/devtools/debugger/DebuggerUI.jsm b/browser/devtools/debugger/DebuggerUI.jsm index 44154297cc9..b1d01de7bc3 100644 --- a/browser/devtools/debugger/DebuggerUI.jsm +++ b/browser/devtools/debugger/DebuggerUI.jsm @@ -24,6 +24,7 @@ * Dave Camp (original author) * Panos Astithas * Victor Porof + * Mihai Sucan * * 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 @@ -60,9 +61,37 @@ function DebuggerPane(aTab) { this._tab = aTab; this._close = this.close.bind(this); this._debugTab = this.debugTab.bind(this); + this.breakpoints = {}; } DebuggerPane.prototype = { + /** + * Skip editor breakpoint change events. + * + * This property tells the source editor event handler to skip handling of + * the BREAKPOINT_CHANGE events. This is used when the debugger adds/removes + * breakpoints from the editor. Typically, the BREAKPOINT_CHANGE event handler + * adds/removes events from the debugger, but when breakpoints are added from + * the public debugger API, we need to do things in reverse. + * + * This implementation relies on the fact that the source editor fires the + * BREAKPOINT_CHANGE events synchronously. + * + * @private + * @type boolean + */ + _skipEditorBreakpointChange: false, + + /** + * The list of breakpoints in the debugger as tracked by the current + * DebuggerPane instance. This an object where the values are BreakpointActor + * objects received from the client, while the keys are actor names, for + * example "conn0.breakpoint3". + * + * @type object + */ + breakpoints: null, + /** * Creates and initializes the widgets contained in the debugger UI. */ @@ -87,11 +116,15 @@ DebuggerPane.prototype = { self.frame.removeEventListener("DOMContentLoaded", initPane, true); // Initialize the source editor. self.frame.contentWindow.editor = self.editor = new SourceEditor(); + self.frame.contentWindow.updateEditorBreakpoints = + self._updateEditorBreakpoints.bind(self); let config = { mode: SourceEditor.MODES.JAVASCRIPT, showLineNumbers: true, - readOnly: true + readOnly: true, + showAnnotationRuler: true, + showOverviewRuler: true, }; let editorPlaceholder = self.frame.contentDocument.getElementById("editor"); @@ -107,14 +140,217 @@ DebuggerPane.prototype = { * editor initialization. */ _onEditorLoad: function DP__onEditorLoad() { + this.editor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, + this._onEditorBreakpointChange.bind(this)); // Connect to the debugger server. this.connect(); }, + /** + * Event handler for breakpoint changes that happen in the editor. This + * function syncs the breakpoint changes in the editor to those in the + * debugger. + * + * @private + * @param object aEvent + * The SourceEditor.EVENTS.BREAKPOINT_CHANGE event object. + */ + _onEditorBreakpointChange: function DP__onEditorBreakpointChange(aEvent) { + if (this._skipEditorBreakpointChange) { + return; + } + + aEvent.added.forEach(this._onEditorBreakpointAdd, this); + aEvent.removed.forEach(this._onEditorBreakpointRemove, this); + }, + + /** + * Retrieve the URL of the selected script in the debugger view. + * + * @private + * @return string + * The URL of the selected script. + */ + _selectedScript: function DP__selectedScript() { + return this.debuggerWindow ? + this.debuggerWindow.DebuggerView.Scripts.selected : null; + }, + + /** + * Event handler for new breakpoints that come from the editor. + * + * @private + * @param object aBreakpoint + * The breakpoint object coming from the editor. + */ + _onEditorBreakpointAdd: function DP__onEditorBreakpointAdd(aBreakpoint) { + let location = { + url: this._selectedScript(), + line: aBreakpoint.line + 1, + }; + + if (location.url) { + let callback = function (aClient, aError) { + if (aError) { + this._skipEditorBreakpointChange = true; + let result = this.editor.removeBreakpoint(aBreakpoint.line); + this._skipEditorBreakpointChange = false; + } + }.bind(this); + this.addBreakpoint(location, callback, true); + } + }, + + /** + * Event handler for breakpoints that are removed from the editor. + * + * @private + * @param object aBreakpoint + * The breakpoint object that was removed from the editor. + */ + _onEditorBreakpointRemove: function DP__onEditorBreakpointRemove(aBreakpoint) { + let url = this._selectedScript(); + let line = aBreakpoint.line + 1; + if (!url) { + return; + } + + let breakpoint = this.getBreakpoint(url, line); + if (breakpoint) { + this.removeBreakpoint(breakpoint, null, true); + } + }, + + /** + * Update the breakpoints in the editor view. This function takes the list of + * breakpoints in the debugger and adds them back into the editor view. This + * is invoked when the selected script is changed. + * + * @private + */ + _updateEditorBreakpoints: function DP__updateEditorBreakpoints() + { + let url = this._selectedScript(); + if (!url) { + return; + } + + this._skipEditorBreakpointChange = true; + for each (let breakpoint in this.breakpoints) { + if (breakpoint.location.url == url) { + this.editor.addBreakpoint(breakpoint.location.line - 1); + } + } + this._skipEditorBreakpointChange = false; + }, + + /** + * Add a breakpoint. + * + * @param object aLocation + * The location where you want the breakpoint. This object must have + * two properties: + * - url - the URL of the script. + * - line - the line number (starting from 1). + * @param function [aCallback] + * Optional function to invoke once the breakpoint is added. The + * callback is invoked with two arguments: + * - aBreakpointClient - the BreakpointActor client object, if the + * breakpoint has been added successfully. + * - aResponseError - if there was any error. + * @param boolean [aNoEditorUpdate=false] + * Tells if you want to skip editor updates. Typically the editor is + * updated to visually indicate that a breakpoint has been added. + */ + addBreakpoint: + function DP_addBreakpoint(aLocation, aCallback, aNoEditorUpdate) { + let breakpoint = this.getBreakpoint(aLocation.url, aLocation.line); + if (breakpoint) { + aCallback && aCallback(breakpoint); + return; + } + + this.activeThread.setBreakpoint(aLocation, function(aResponse, aBpClient) { + if (!aResponse.error) { + this.breakpoints[aBpClient.actor] = aBpClient; + + if (!aNoEditorUpdate) { + let url = this._selectedScript(); + if (url == aLocation.url) { + this._skipEditorBreakpointChange = true; + this.editor.addBreakpoint(aLocation.line - 1); + this._skipEditorBreakpointChange = false; + } + } + } + + aCallback && aCallback(aBpClient, aResponse.error); + }.bind(this)); + }, + + /** + * Remove a breakpoint. + * + * @param object aBreakpoint + * The breakpoint you want to remove. + * @param function [aCallback] + * Optional function to invoke once the breakpoint is removed. The + * callback is invoked with one argument: the breakpoint location + * object which holds the url and line properties. + * @param boolean [aNoEditorUpdate=false] + * Tells if you want to skip editor updates. Typically the editor is + * updated to visually indicate that a breakpoint has been removed. + */ + removeBreakpoint: + function DP_removeBreakpoint(aBreakpoint, aCallback, aNoEditorUpdate) { + if (!(aBreakpoint.actor in this.breakpoints)) { + aCallback && aCallback(aBreakpoint.location); + return; + } + + aBreakpoint.remove(function() { + delete this.breakpoints[aBreakpoint.actor]; + + if (!aNoEditorUpdate) { + let url = this._selectedScript(); + if (url == aBreakpoint.location.url) { + this._skipEditorBreakpointChange = true; + this.editor.removeBreakpoint(aBreakpoint.location.line - 1); + this._skipEditorBreakpointChange = false; + } + } + + aCallback && aCallback(aBreakpoint.location); + }.bind(this)); + }, + + /** + * Get the breakpoint object at the given location. + * + * @param string aUrl + * The URL of where the breakpoint is. + * @param number aLine + * The line number where the breakpoint is. + * @return object + * The BreakpointActor object. + */ + getBreakpoint: function DP_getBreakpoint(aUrl, aLine) { + for each (let breakpoint in this.breakpoints) { + if (breakpoint.location.url == aUrl && breakpoint.location.line == aLine) { + return breakpoint; + } + } + return null; + }, + /** * Closes the debugger UI removing child nodes and event listeners. */ close: function DP_close() { + for each (let breakpoint in this.breakpoints) { + this.removeBreakpoint(breakpoint); + } + if (this._tab) { this._tab._scriptDebugger = null; this._tab = null; @@ -192,7 +428,7 @@ DebuggerPane.prototype = { }, get debuggerWindow() { - return this.frame.contentWindow; + return this.frame ? this.frame.contentWindow : null; }, get debuggerClient() { @@ -340,6 +576,7 @@ DebuggerUI.prototype = { script.text = aSourceText; script.contentType = aContentType; elt.setUserData("sourceScript", script, null); + dbg._updateEditorBreakpoints(); } }; diff --git a/browser/devtools/debugger/debugger-view.js b/browser/devtools/debugger/debugger-view.js index 888a122d889..ea4d3bca2e2 100644 --- a/browser/devtools/debugger/debugger-view.js +++ b/browser/devtools/debugger/debugger-view.js @@ -22,6 +22,7 @@ * * Contributor(s): * Victor Porof (original author) + * Mihai Sucan * * 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 @@ -199,7 +200,6 @@ DebuggerView.Stackframes = { // the list item wasn't found in the stackframe container if (!frame) { - dump("The frame list item wasn't found in the stackframes container."); return; } @@ -356,7 +356,6 @@ DebuggerView.Properties = { // make sure the element was created successfully if (!element) { - dump("The debugger scope container wasn't created properly: " + aId); return null; } @@ -398,7 +397,6 @@ DebuggerView.Properties = { // make sure the element was created successfully if (!element) { - dump("The debugger variable container wasn't created properly: " + aId); return null; } @@ -466,7 +464,6 @@ DebuggerView.Properties = { // make sure the info node exists if (!info) { - dump("Could not set the grip for the corresponding variable: " + aVar.id); return null; } @@ -569,7 +566,6 @@ DebuggerView.Properties = { // make sure the element was created successfully if (!element) { - dump("The debugger property container wasn't created properly."); return null; } @@ -710,11 +706,9 @@ DebuggerView.Properties = { _createPropertyElement: function DVP__createPropertyElement(aName, aId, aClass, aParent) { // make sure we don't duplicate anything and the parent exists if (document.getElementById(aId)) { - dump("Duplicating a property element id is not allowed."); return null; } if (!aParent) { - dump("A property element must have a valid parent node specified."); return null; } @@ -1127,6 +1121,15 @@ DebuggerView.Scripts = { } }, + /** + * Retrieve the URL of the selected script. + * @return string|null + */ + get selected() { + return this._scripts.selectedItem ? + this._scripts.selectedItem.value : null; + }, + /** * Adds a script to the scripts container. * If the script already exists (was previously added), null is returned. diff --git a/browser/devtools/debugger/debugger.js b/browser/devtools/debugger/debugger.js index 9875ae4ea41..fb9beb4c210 100644 --- a/browser/devtools/debugger/debugger.js +++ b/browser/devtools/debugger/debugger.js @@ -78,7 +78,8 @@ function startDebuggingTab(aClient, aTabGrip) gTabClient = aTabClient; gClient.attachThread(aResponse.threadActor, function(aResponse, aThreadClient) { if (!aThreadClient) { - dump("Couldn't attach to thread: "+aResponse.error+"\n"); + Components.utils.reportError("Couldn't attach to thread: " + + aResponse.error); return; } ThreadState.connect(aThreadClient, function() { @@ -612,6 +613,7 @@ var SourceScripts = { window.editor.setText(DebuggerView.getStr("loadingText")); } else { window.editor.setText(aScript.text); + window.updateEditorBreakpoints(); } } }; diff --git a/browser/devtools/debugger/test/Makefile.in b/browser/devtools/debugger/test/Makefile.in index d85d6668285..bb3e29eb67b 100644 --- a/browser/devtools/debugger/test/Makefile.in +++ b/browser/devtools/debugger/test/Makefile.in @@ -73,6 +73,7 @@ _BROWSER_TEST_FILES = \ browser_dbg_update-editor-mode.js \ browser_dbg_select-line.js \ browser_dbg_clean-exit.js \ + browser_dbg_bug723069_editor-breakpoints.js \ head.js \ $(NULL) diff --git a/browser/devtools/debugger/test/browser_dbg_bug723069_editor-breakpoints.js b/browser/devtools/debugger/test/browser_dbg_bug723069_editor-breakpoints.js new file mode 100644 index 00000000000..f2066d16226 --- /dev/null +++ b/browser/devtools/debugger/test/browser_dbg_bug723069_editor-breakpoints.js @@ -0,0 +1,274 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Bug 723069: test the debugger breakpoint API and connection to the source + * editor. + */ + +const TAB_URL = EXAMPLE_URL + "browser_dbg_script-switching.html"; + +let gPane = null; +let gTab = null; +let gDebuggee = null; +let gDebugger = null; +let gScripts = null; +let gEditor = null; +let gBreakpoints = null; + +function test() +{ + let tempScope = {}; + Cu.import("resource:///modules/source-editor.jsm", tempScope); + let SourceEditor = tempScope.SourceEditor; + + debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) { + gTab = aTab; + gDebuggee = aDebuggee; + gPane = aPane; + gDebugger = gPane.debuggerWindow; + + gPane.activeThread.addOneTimeListener("scriptsadded", function() { + Services.tm.currentThread.dispatch({ run: onScriptsAdded }, 0); + }); + gDebuggee.firstCall(); + }); + + function onScriptsAdded() + { + gScripts = gDebugger.DebuggerView.Scripts; + + is(gDebugger.StackFrames.activeThread.state, "paused", + "Should only be getting stack frames while paused."); + + is(gScripts._scripts.itemCount, 2, "Found the expected number of scripts."); + + gEditor = gDebugger.editor; + + isnot(gEditor.getText().indexOf("debugger"), -1, + "The correct script was loaded initially."); + isnot(gScripts.selected, gScripts.scriptLocations()[0], + "the correct sccript is selected"); + + gBreakpoints = gPane.breakpoints; + is(Object.keys(gBreakpoints), 0, "no breakpoints"); + ok(!gPane.getBreakpoint("foo", 3), "getBreakpoint('foo', 3) returns falsey"); + + is(gEditor.getBreakpoints().length, 0, "no breakpoints in the editor"); + + + info("add the first breakpoint"); + gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, + onEditorBreakpointAddFirst); + let location = {url: gScripts.selected, line: 6}; + executeSoon(function() { + gPane.addBreakpoint(location, onBreakpointAddFirst); + }); + } + + let breakpointsAdded = 0; + let breakpointsRemoved = 0; + let editorBreakpointChanges = 0; + + function onEditorBreakpointAddFirst(aEvent) + { + gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, + onEditorBreakpointAddFirst); + editorBreakpointChanges++; + + ok(aEvent, "breakpoint1 added to the editor"); + is(aEvent.added.length, 1, "one breakpoint added to the editor"); + is(aEvent.removed.length, 0, "no breakpoint was removed from the editor"); + is(aEvent.added[0].line, 5, "editor breakpoint line is correct"); + + is(gEditor.getBreakpoints().length, 1, + "editor.getBreakpoints().length is correct"); + } + + function onBreakpointAddFirst(aBreakpointClient, aResponseError) + { + breakpointsAdded++; + + ok(aBreakpointClient, "breakpoint1 added, client received"); + ok(!aResponseError, "breakpoint1 added without errors"); + is(aBreakpointClient.location.url, gScripts.selected, + "breakpoint1 client url is correct"); + is(aBreakpointClient.location.line, 6, + "breakpoint1 client line is correct"); + + executeSoon(function() { + ok(aBreakpointClient.actor in gBreakpoints, + "breakpoint1 client found in the list of debugger breakpoints"); + is(Object.keys(gBreakpoints).length, 1, + "the list of debugger breakpoints holds only one breakpoint"); + is(gPane.getBreakpoint(gScripts.selected, 6), aBreakpointClient, + "getBreakpoint(selectedScript, 2) returns the correct breakpoint"); + + info("remove the first breakpoint"); + gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, + onEditorBreakpointRemoveFirst); + gPane.removeBreakpoint(aBreakpointClient, onBreakpointRemoveFirst); + }); + } + + function onBreakpointRemoveFirst(aLocation) + { + breakpointsRemoved++; + + ok(aLocation, "breakpoint1 removed"); + is(aLocation.url, gScripts.selected, "breakpoint1 remove: url is correct"); + is(aLocation.line, 6, "breakpoint1 remove: line is correct"); + + executeSoon(testBreakpointAddBackground); + } + + function onEditorBreakpointRemoveFirst(aEvent) + { + gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, + onEditorBreakpointRemoveFirst); + editorBreakpointChanges++; + + ok(aEvent, "breakpoint1 removed from the editor"); + is(aEvent.added.length, 0, "no breakpoint was added to the editor"); + is(aEvent.removed.length, 1, "one breakpoint was removed from the editor"); + is(aEvent.removed[0].line, 5, "editor breakpoint line is correct"); + + is(gEditor.getBreakpoints().length, 0, "editor.getBreakpoints().length is correct"); + } + + function testBreakpointAddBackground() + { + info("add a breakpoint to the second script which is not selected"); + + is(Object.keys(gBreakpoints).length, 0, "no breakpoints in the debugger"); + ok(!gPane.getBreakpoint(gScripts.selected, 6), + "getBreakpoint(selectedScript, 6) returns no breakpoint"); + + let script0 = gScripts.scriptLocations()[0]; + isnot(script0, gScripts.selected, + "first script location is not the currently selected script"); + + let location = {url: script0, line: 5}; + gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, + onEditorBreakpointAddBackgroundTrap); + gPane.addBreakpoint(location, onBreakpointAddBackground); + } + + function onEditorBreakpointAddBackgroundTrap(aEvent) + { + // trap listener: no breakpoint must be added to the editor when a breakpoint + // is added to a script that is not currently selected. + gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, + onEditorBreakpointAddBackgroundTrap); + editorBreakpointChanges++; + ok(false, "breakpoint2 must not be added to the editor"); + } + + function onBreakpointAddBackground(aBreakpointClient, aResponseError) + { + breakpointsAdded++; + + ok(aBreakpointClient, "breakpoint2 added, client received"); + ok(!aResponseError, "breakpoint2 added without errors"); + is(aBreakpointClient.location.url, gScripts.scriptLocations()[0], + "breakpoint2 client url is correct"); + is(aBreakpointClient.location.line, 5, + "breakpoint2 client line is correct"); + + executeSoon(function() { + ok(aBreakpointClient.actor in gBreakpoints, + "breakpoint2 client found in the list of debugger breakpoints"); + is(Object.keys(gBreakpoints).length, 1, "one breakpoint in the debugger"); + is(gPane.getBreakpoint(gScripts.scriptLocations()[0], 5), aBreakpointClient, + "getBreakpoint(scriptLocations[0], 5) returns the correct breakpoint"); + + // remove the trap listener + gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, + onEditorBreakpointAddBackgroundTrap); + + gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, + onEditorBreakpointAddSwitch); + gEditor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, + onEditorTextChanged); + + info("switch to the second script"); + + gScripts._scripts.selectedIndex = 0; + gDebugger.SourceScripts.onChange({ target: gScripts._scripts }); + }); + } + + function onEditorBreakpointAddSwitch(aEvent) + { + gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, + onEditorBreakpointAddSwitch); + editorBreakpointChanges++; + + ok(aEvent, "breakpoint2 added to the editor"); + is(aEvent.added.length, 1, "one breakpoint added to the editor"); + is(aEvent.removed.length, 0, "no breakpoint was removed from the editor"); + is(aEvent.added[0].line, 4, "editor breakpoint line is correct"); + + is(gEditor.getBreakpoints().length, 1, + "editor.getBreakpoints().length is correct"); + } + + function onEditorTextChanged() + { + gEditor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED, + onEditorTextChanged); + + is(gEditor.getText().indexOf("debugger"), -1, + "The second script is no longer displayed."); + + isnot(gEditor.getText().indexOf("firstCall"), -1, + "The first script is displayed."); + + executeSoon(function() { + info("remove the second breakpoint using the mouse"); + + gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, + onEditorBreakpointRemoveSecond); + + let testWin = gEditor.editorElement.ownerDocument.defaultView; + EventUtils.synthesizeMouse(gEditor.editorElement, 10, 70, {}, testWin); + }); + + } + + function onEditorBreakpointRemoveSecond(aEvent) + { + gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, + onEditorBreakpointRemoveSecond); + editorBreakpointChanges++; + + ok(aEvent, "breakpoint2 removed from the editor"); + is(aEvent.added.length, 0, "no breakpoint was added to the editor"); + is(aEvent.removed.length, 1, "one breakpoint was removed from the editor"); + is(aEvent.removed[0].line, 4, "editor breakpoint line is correct"); + + is(gEditor.getBreakpoints().length, 0, "editor.getBreakpoints().length is correct"); + + executeSoon(function() { + gDebugger.StackFrames.activeThread.resume(finish); + }); + } + + registerCleanupFunction(function() { + is(Object.keys(gBreakpoints).length, 0, "no breakpoint in the debugger"); + ok(!gPane.getBreakpoint(gScripts.scriptLocations()[0], 5), + "getBreakpoint(scriptLocations[0], 5) returns no breakpoint"); + + removeTab(gTab); + is(breakpointsAdded, 2, "correct number of breakpoints have been added"); + is(breakpointsRemoved, 1, "correct number of breakpoints have been removed"); + is(editorBreakpointChanges, 4, "correct number of editor breakpoint changes"); + gPane = null; + gTab = null; + gDebuggee = null; + gDebugger = null; + gScripts = null; + gEditor = null; + gBreakpoints = null; + }); +} diff --git a/browser/devtools/debugger/test/browser_dbg_clean-exit.js b/browser/devtools/debugger/test/browser_dbg_clean-exit.js index 26eea1d3c2c..a152a8b5d84 100644 --- a/browser/devtools/debugger/test/browser_dbg_clean-exit.js +++ b/browser/devtools/debugger/test/browser_dbg_clean-exit.js @@ -7,7 +7,6 @@ var gPane = null; var gTab = null; -var gDebuggee = null; var gDebugger = null; const DEBUGGER_TAB_URL = EXAMPLE_URL + "browser_dbg_debuggerstatement.html"; @@ -15,7 +14,6 @@ const DEBUGGER_TAB_URL = EXAMPLE_URL + "browser_dbg_debuggerstatement.html"; function test() { debug_tab_pane(DEBUGGER_TAB_URL, function(aTab, aDebuggee, aPane) { gTab = aTab; - gDebuggee = aDebuggee; gPane = aPane; gDebugger = gPane.debuggerWindow; @@ -29,12 +27,16 @@ function testCleanExit() { is(gDebugger.StackFrames.activeThread.paused, true, "Should be paused after the debugger statement."); - gPane._client.addOneTimeListener("tabDetached", function () { - finish(); - }); - removeTab(gTab); + closeDebuggerAndFinish(gTab); }}, 0); }); gTab.linkedBrowser.contentWindow.wrappedJSObject.runDebuggerStatement(); } + +registerCleanupFunction(function() { + removeTab(gTab); + gPane = null; + gTab = null; + gDebugger = null; +}); diff --git a/browser/devtools/debugger/test/browser_dbg_location-changes.js b/browser/devtools/debugger/test/browser_dbg_location-changes.js index c01e5e4c870..1132bd6a79f 100644 --- a/browser/devtools/debugger/test/browser_dbg_location-changes.js +++ b/browser/devtools/debugger/test/browser_dbg_location-changes.js @@ -55,10 +55,17 @@ function testLocationChange() gPane._client.addOneTimeListener("tabAttached", function(aEvent, aPacket) { ok(true, "Successfully reattached to the tab again."); - removeTab(gTab); - finish(); + closeDebuggerAndFinish(gTab); }); }); content.location = TAB1_URL; }); } + +registerCleanupFunction(function() { + removeTab(gTab); + gPane = null; + gTab = null; + gDebuggee = null; + gDebugger = null; +}); diff --git a/browser/devtools/debugger/test/browser_dbg_pause-resume.js b/browser/devtools/debugger/test/browser_dbg_pause-resume.js index 4a549ae9b0e..3c7d11bfca5 100644 --- a/browser/devtools/debugger/test/browser_dbg_pause-resume.js +++ b/browser/devtools/debugger/test/browser_dbg_pause-resume.js @@ -6,13 +6,11 @@ var gPane = null; var gTab = null; -var gDebuggee = null; var gDebugger = null; function test() { debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) { gTab = aTab; - gDebuggee = aDebuggee; gPane = aPane; gDebugger = gPane.debuggerWindow; @@ -63,8 +61,7 @@ function testResume() { is(button.label, gDebugger.DebuggerView.getStr("pauseLabel"), "Button label should be pause when running."); - removeTab(gTab); - finish(); + closeDebuggerAndFinish(gTab); }}, 0); }); @@ -72,3 +69,9 @@ function testResume() { gDebugger.document.getElementById("resume"), gDebugger); } + +registerCleanupFunction(function() { + removeTab(gTab); + gPane = null; + gTab = null; +}); diff --git a/browser/devtools/debugger/test/browser_dbg_propertyview-01.js b/browser/devtools/debugger/test/browser_dbg_propertyview-01.js index 4d5b014e3fd..f1923f7a8cc 100644 --- a/browser/devtools/debugger/test/browser_dbg_propertyview-01.js +++ b/browser/devtools/debugger/test/browser_dbg_propertyview-01.js @@ -134,9 +134,15 @@ function resumeAndFinish() { is(vs._scripts.itemCount, 6, "Got too many script items in the list!"); - - removeTab(gTab); - finish(); + closeDebuggerAndFinish(gTab); }); }); } + +registerCleanupFunction(function() { + removeTab(gTab); + gPane = null; + gTab = null; + gDebuggee = null; + gDebugger = null; +}); diff --git a/browser/devtools/debugger/test/browser_dbg_propertyview-02.js b/browser/devtools/debugger/test/browser_dbg_propertyview-02.js index 284ecef58dd..37ed42054e3 100644 --- a/browser/devtools/debugger/test/browser_dbg_propertyview-02.js +++ b/browser/devtools/debugger/test/browser_dbg_propertyview-02.js @@ -116,16 +116,19 @@ function testSimpleCall() { ok(!testScope.expanded, "Clicking again the testScope tilte should collapse it."); - resumeAndFinish(); + gDebugger.StackFrames.activeThread.resume(function() { + closeDebuggerAndFinish(gTab); + }); }}, 0); }); gDebuggee.simpleCall(); } -function resumeAndFinish() { - gDebugger.StackFrames.activeThread.resume(function() { - removeTab(gTab); - finish(); - }); -} +registerCleanupFunction(function() { + removeTab(gTab); + gPane = null; + gTab = null; + gDebuggee = null; + gDebugger = null; +}); diff --git a/browser/devtools/debugger/test/browser_dbg_propertyview-03.js b/browser/devtools/debugger/test/browser_dbg_propertyview-03.js index d61692be32d..c48b2ce001b 100644 --- a/browser/devtools/debugger/test/browser_dbg_propertyview-03.js +++ b/browser/devtools/debugger/test/browser_dbg_propertyview-03.js @@ -118,16 +118,19 @@ function testSimpleCall() { is(gDebugger.DebuggerView.Properties._vars.childNodes.length, 4, "The scope should have been removed from the parent container tree."); - resumeAndFinish(); + gDebugger.StackFrames.activeThread.resume(function() { + closeDebuggerAndFinish(gTab); + }); }}, 0); }); gDebuggee.simpleCall(); } -function resumeAndFinish() { - gDebugger.StackFrames.activeThread.resume(function() { - removeTab(gTab); - finish(); - }); -} +registerCleanupFunction(function() { + removeTab(gTab); + gPane = null; + gTab = null; + gDebuggee = null; + gDebugger = null; +}); diff --git a/browser/devtools/debugger/test/browser_dbg_propertyview-04.js b/browser/devtools/debugger/test/browser_dbg_propertyview-04.js index a0be8a00703..c653f5abec1 100644 --- a/browser/devtools/debugger/test/browser_dbg_propertyview-04.js +++ b/browser/devtools/debugger/test/browser_dbg_propertyview-04.js @@ -73,16 +73,19 @@ function testSimpleCall() { is(testScope.querySelector(".details").childNodes.length, 0, "The var should have been removed from the parent container tree."); - resumeAndFinish(); + gDebugger.StackFrames.activeThread.resume(function() { + closeDebuggerAndFinish(gTab); + }); }}, 0); }); gDebuggee.simpleCall(); } -function resumeAndFinish() { - gDebugger.StackFrames.activeThread.resume(function() { - removeTab(gTab); - finish(); - }); -} +registerCleanupFunction(function() { + removeTab(gTab); + gPane = null; + gTab = null; + gDebuggee = null; + gDebugger = null; +}); diff --git a/browser/devtools/debugger/test/browser_dbg_propertyview-05.js b/browser/devtools/debugger/test/browser_dbg_propertyview-05.js index 73301399959..34cb3dbbe81 100644 --- a/browser/devtools/debugger/test/browser_dbg_propertyview-05.js +++ b/browser/devtools/debugger/test/browser_dbg_propertyview-05.js @@ -81,16 +81,19 @@ function testSimpleCall() { is(testScope.querySelector(".details").childNodes.length, 0, "The var should have been removed from the parent container tree."); - resumeAndFinish(); + gDebugger.StackFrames.activeThread.resume(function() { + closeDebuggerAndFinish(gTab); + }); }}, 0); }); gDebuggee.simpleCall(); } -function resumeAndFinish() { - gDebugger.StackFrames.activeThread.resume(function() { - removeTab(gTab); - finish(); - }); -} +registerCleanupFunction(function() { + removeTab(gTab); + gPane = null; + gTab = null; + gDebuggee = null; + gDebugger = null; +}); diff --git a/browser/devtools/debugger/test/browser_dbg_propertyview-06.js b/browser/devtools/debugger/test/browser_dbg_propertyview-06.js index 0c6b601397c..62fa96dbd6b 100644 --- a/browser/devtools/debugger/test/browser_dbg_propertyview-06.js +++ b/browser/devtools/debugger/test/browser_dbg_propertyview-06.js @@ -117,17 +117,19 @@ function testSimpleCall() { is(localVar5.querySelector(".info").textContent, "[object Object]", "The grip information for the localVar5 wasn't set correctly."); - - resumeAndFinish(); + gDebugger.StackFrames.activeThread.resume(function() { + closeDebuggerAndFinish(gTab); + }); }}, 0); }); gDebuggee.simpleCall(); } -function resumeAndFinish() { - gDebugger.StackFrames.activeThread.resume(function() { - removeTab(gTab); - finish(); - }); -} +registerCleanupFunction(function() { + removeTab(gTab); + gPane = null; + gTab = null; + gDebuggee = null; + gDebugger = null; +}); diff --git a/browser/devtools/debugger/test/browser_dbg_propertyview-07.js b/browser/devtools/debugger/test/browser_dbg_propertyview-07.js index 3df26725b01..4c1b9f1e3a2 100644 --- a/browser/devtools/debugger/test/browser_dbg_propertyview-07.js +++ b/browser/devtools/debugger/test/browser_dbg_propertyview-07.js @@ -10,14 +10,12 @@ const TAB_URL = EXAMPLE_URL + "browser_dbg_frame-parameters.html"; var gPane = null; var gTab = null; -var gDebuggee = null; var gDebugger = null; function test() { debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) { gTab = aTab; - gDebuggee = aDebuggee; gPane = aPane; gDebugger = gPane.debuggerWindow; @@ -87,10 +85,16 @@ function resumeAndFinish() { is(frames.querySelectorAll(".dbg-stackframe").length, 0, "Should have no frames."); - removeTab(gTab); - finish(); + closeDebuggerAndFinish(gTab); }}, 0); }); gDebugger.StackFrames.activeThread.resume(); } + +registerCleanupFunction(function() { + removeTab(gTab); + gPane = null; + gTab = null; + gDebugger = null; +}); diff --git a/browser/devtools/debugger/test/browser_dbg_propertyview-08.js b/browser/devtools/debugger/test/browser_dbg_propertyview-08.js index f7f49773006..59765da70fc 100644 --- a/browser/devtools/debugger/test/browser_dbg_propertyview-08.js +++ b/browser/devtools/debugger/test/browser_dbg_propertyview-08.js @@ -10,14 +10,12 @@ const TAB_URL = EXAMPLE_URL + "browser_dbg_frame-parameters.html"; var gPane = null; var gTab = null; -var gDebuggee = null; var gDebugger = null; function test() { debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) { gTab = aTab; - gDebuggee = aDebuggee; gPane = aPane; gDebugger = gPane.debuggerWindow; @@ -103,10 +101,16 @@ function resumeAndFinish() { is(frames.querySelectorAll(".dbg-stackframe").length, 0, "Should have no frames."); - removeTab(gTab); - finish(); + closeDebuggerAndFinish(gTab); }}, 0); }); gDebugger.StackFrames.activeThread.resume(); } + +registerCleanupFunction(function() { + removeTab(gTab); + gPane = null; + gTab = null; + gDebugger = null; +}); diff --git a/browser/devtools/debugger/test/browser_dbg_script-switching.js b/browser/devtools/debugger/test/browser_dbg_script-switching.js index 90c71223d76..000b93d326d 100644 --- a/browser/devtools/debugger/test/browser_dbg_script-switching.js +++ b/browser/devtools/debugger/test/browser_dbg_script-switching.js @@ -103,6 +103,13 @@ function testSwitchRunning() ok(gDebugger.editor.getText().search(/firstCall/) == -1, "The first script is no longer displayed."); - removeTab(gTab); - finish(); + closeDebuggerAndFinish(gTab); } + +registerCleanupFunction(function() { + removeTab(gTab); + gPane = null; + gTab = null; + gDebuggee = null; + gDebugger = null; +}); diff --git a/browser/devtools/debugger/test/browser_dbg_select-line.js b/browser/devtools/debugger/test/browser_dbg_select-line.js index b33b3eea3cc..0b246593b32 100644 --- a/browser/devtools/debugger/test/browser_dbg_select-line.js +++ b/browser/devtools/debugger/test/browser_dbg_select-line.js @@ -68,14 +68,22 @@ function testSelectLine() { "The correct line is selected."); gDebugger.StackFrames.activeThread.resume(function() { - removeTab(gTab); - finish(); + closeDebuggerAndFinish(gTab); }); }); }); + // Scroll all the way down to ensure stackframe-3 is visible. + let stackframes = gDebugger.document.getElementById("stackframes"); + stackframes.scrollTop = stackframes.scrollHeight; + // Click the oldest stack frame. + let frames = gDebugger.DebuggerView.Stackframes._frames; + is(frames.querySelectorAll(".dbg-stackframe").length, 4, + "Should have four frames."); + let element = gDebugger.document.getElementById("stackframe-3"); + isnot(element, null, "Found the third stack frame."); EventUtils.synthesizeMouseAtCenter(element, {}, gDebugger); }); }}, 0); @@ -83,3 +91,11 @@ function testSelectLine() { gDebuggee.firstCall(); } + +registerCleanupFunction(function() { + removeTab(gTab); + gPane = null; + gTab = null; + gDebuggee = null; + gDebugger = null; +}); diff --git a/browser/devtools/debugger/test/browser_dbg_stack-01.js b/browser/devtools/debugger/test/browser_dbg_stack-01.js index f38997398e7..6ad23121b24 100644 --- a/browser/devtools/debugger/test/browser_dbg_stack-01.js +++ b/browser/devtools/debugger/test/browser_dbg_stack-01.js @@ -36,16 +36,19 @@ function testSimpleCall() { is(childNodes.length, frames.querySelectorAll(".dbg-stackframe").length, "All children should be frames."); - resumeAndFinish(); + gDebugger.StackFrames.activeThread.resume(function() { + closeDebuggerAndFinish(gTab); + }); }}, 0); }); gDebuggee.simpleCall(); } -function resumeAndFinish() { - gDebugger.StackFrames.activeThread.resume(function() { - removeTab(gTab); - finish(); - }); -} +registerCleanupFunction(function() { + removeTab(gTab); + gPane = null; + gTab = null; + gDebuggee = null; + gDebugger = null; +}); diff --git a/browser/devtools/debugger/test/browser_dbg_stack-02.js b/browser/devtools/debugger/test/browser_dbg_stack-02.js index 47af89e7234..2b6c223c705 100644 --- a/browser/devtools/debugger/test/browser_dbg_stack-02.js +++ b/browser/devtools/debugger/test/browser_dbg_stack-02.js @@ -67,17 +67,19 @@ function testEvalCall() { ok(!frames.querySelector("#stackframe-1").classList.contains("selected"), "Second frame should not be selected after click inside the first frame."); - resumeAndFinish(); + gDebugger.StackFrames.activeThread.resume(function() { + closeDebuggerAndFinish(gTab); + }); }}, 0); }); gDebuggee.evalCall(); } -function resumeAndFinish() { - gDebugger.StackFrames.activeThread.resume(function() { - removeTab(gTab); - finish(); - }); -} - +registerCleanupFunction(function() { + removeTab(gTab); + gPane = null; + gTab = null; + gDebuggee = null; + gDebugger = null; +}); diff --git a/browser/devtools/debugger/test/browser_dbg_stack-03.js b/browser/devtools/debugger/test/browser_dbg_stack-03.js index bd48cfb8c3f..1475fc70c62 100644 --- a/browser/devtools/debugger/test/browser_dbg_stack-03.js +++ b/browser/devtools/debugger/test/browser_dbg_stack-03.js @@ -47,7 +47,9 @@ function testRecurse() { is(frames.querySelectorAll(".dbg-stackframe").length, recurseLimit, "Should have reached the recurse limit."); - resumeAndFinish(); + gDebugger.StackFrames.activeThread.resume(function() { + closeDebuggerAndFinish(gTab); + }); }); frames.scrollTop = frames.scrollHeight; @@ -60,9 +62,10 @@ function testRecurse() { gDebuggee.recurse(); } -function resumeAndFinish() { - gDebugger.StackFrames.activeThread.resume(function() { - removeTab(gTab); - finish(); - }); -} +registerCleanupFunction(function() { + removeTab(gTab); + gPane = null; + gTab = null; + gDebuggee = null; + gDebugger = null; +}); diff --git a/browser/devtools/debugger/test/browser_dbg_stack-04.js b/browser/devtools/debugger/test/browser_dbg_stack-04.js index 246d5548a19..2585135087a 100644 --- a/browser/devtools/debugger/test/browser_dbg_stack-04.js +++ b/browser/devtools/debugger/test/browser_dbg_stack-04.js @@ -48,8 +48,7 @@ function testEvalCallResume() { is(frames.querySelectorAll(".empty").length, 1, "Should have the empty list explanation."); - removeTab(gTab); - finish(); + closeDebuggerAndFinish(gTab); }); gPane.activeThread.resume(); @@ -58,3 +57,11 @@ function testEvalCallResume() { gDebuggee.evalCall(); } + +registerCleanupFunction(function() { + removeTab(gTab); + gPane = null; + gTab = null; + gDebuggee = null; + gDebugger = null; +}); diff --git a/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js b/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js index 6588e7d01eb..7a1a44400a1 100644 --- a/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js +++ b/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js @@ -72,7 +72,15 @@ function testSwitchPaused() "Found the expected editor mode."); gDebugger.StackFrames.activeThread.resume(function() { - removeTab(gTab); - finish(); + closeDebuggerAndFinish(gTab); }); } + +registerCleanupFunction(function() { + removeTab(gTab); + gPane = null; + gTab = null; + gDebuggee = null; + gDebugger = null; + gScripts = null; +}); diff --git a/browser/devtools/debugger/test/head.js b/browser/devtools/debugger/test/head.js index 2c5da2f5594..dced2c347ab 100644 --- a/browser/devtools/debugger/test/head.js +++ b/browser/devtools/debugger/test/head.js @@ -49,6 +49,14 @@ function removeTab(aTab) { gBrowser.removeTab(aTab); } +function closeDebuggerAndFinish(aTab) { + DebuggerUI.aWindow.addEventListener("Debugger:Shutdown", function cleanup() { + DebuggerUI.aWindow.removeEventListener("Debugger:Shutdown", cleanup, false); + finish(); + }, false); + DebuggerUI.getDebugger(aTab).close(); +} + function get_tab_actor_for_url(aClient, aURL, aCallback) { aClient.listTabs(function(aResponse) { for each (let tab in aResponse.tabs) { diff --git a/browser/devtools/sourceeditor/source-editor-orion.jsm b/browser/devtools/sourceeditor/source-editor-orion.jsm index 3728ed2680f..e7dc16e24e1 100644 --- a/browser/devtools/sourceeditor/source-editor-orion.jsm +++ b/browser/devtools/sourceeditor/source-editor-orion.jsm @@ -23,6 +23,7 @@ * Mihai Sucan (original author) * Kenny Heaton * Spyros Livathinos + * Allen Eubank * * 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 @@ -124,6 +125,18 @@ const DEFAULT_KEYBINDINGS = [ code: Ci.nsIDOMKeyEvent.DOM_VK_TAB, shift: true, }, + { + action: "Move Lines Up", + code: Ci.nsIDOMKeyEvent.DOM_VK_UP, + ctrl: Services.appinfo.OS == "Darwin", + alt: true, + }, + { + action: "Move Lines Down", + code: Ci.nsIDOMKeyEvent.DOM_VK_DOWN, + ctrl: Services.appinfo.OS == "Darwin", + alt: true, + }, ]; var EXPORTED_SYMBOLS = ["SourceEditor"]; @@ -367,6 +380,7 @@ SourceEditor.prototype = { "Find Next Occurrence": [this.ui.findNext, this.ui], "Find Previous Occurrence": [this.ui.findPrevious, this.ui], "Goto Line...": [this.ui.gotoLine, this.ui], + "Move Lines Down": [this._moveLines, this], }; for (let name in actions) { @@ -374,9 +388,17 @@ SourceEditor.prototype = { this._view.setAction(name, action[0].bind(action[1])); } + this._view.setAction("Move Lines Up", this._moveLines.bind(this, true)); + let keys = (config.keys || []).concat(DEFAULT_KEYBINDINGS); keys.forEach(function(aKey) { - let binding = new KeyBinding(aKey.code, aKey.accel, aKey.shift, aKey.alt); + // In Orion mod1 refers to Cmd on Macs and Ctrl on Windows and Linux. + // So, if ctrl is in aKey we use it on Windows and Linux, otherwise + // we use aKey.accel for mod1. + let mod1 = Services.appinfo.OS != "Darwin" && + "ctrl" in aKey ? aKey.ctrl : aKey.accel; + let binding = new KeyBinding(aKey.code, mod1, aKey.shift, aKey.alt, + aKey.ctrl); this._view.setKeyBinding(binding, aKey.action); if (aKey.callback) { @@ -578,6 +600,78 @@ SourceEditor.prototype = { return true; }, + /** + * Move lines upwards or downwards, relative to the current caret location. + * + * @private + * @param boolean aLineAbove + * True if moving lines up, false to move lines down. + */ + _moveLines: function SE__moveLines(aLineAbove) + { + if (this.readOnly) { + return false; + } + + let model = this._model; + let selection = this.getSelection(); + let firstLine = model.getLineAtOffset(selection.start); + if (firstLine == 0 && aLineAbove) { + return true; + } + + let lastLine = model.getLineAtOffset(selection.end); + let firstLineStart = model.getLineStart(firstLine); + let lastLineStart = model.getLineStart(lastLine); + if (selection.start != selection.end && lastLineStart == selection.end) { + lastLine--; + } + if (!aLineAbove && (lastLine + 1) == this.getLineCount()) { + return true; + } + + let lastLineEnd = model.getLineEnd(lastLine, true); + let text = this.getText(firstLineStart, lastLineEnd); + + if (aLineAbove) { + let aboveLine = firstLine - 1; + let aboveLineStart = model.getLineStart(aboveLine); + + this.startCompoundChange(); + if (lastLine == (this.getLineCount() - 1)) { + let delimiterStart = model.getLineEnd(aboveLine); + let delimiterEnd = model.getLineEnd(aboveLine, true); + let lineDelimiter = this.getText(delimiterStart, delimiterEnd); + text += lineDelimiter; + this.setText("", firstLineStart - lineDelimiter.length, lastLineEnd); + } else { + this.setText("", firstLineStart, lastLineEnd); + } + this.setText(text, aboveLineStart, aboveLineStart); + this.endCompoundChange(); + this.setSelection(aboveLineStart, aboveLineStart + text.length); + } else { + let belowLine = lastLine + 1; + let belowLineEnd = model.getLineEnd(belowLine, true); + + let insertAt = belowLineEnd - lastLineEnd + firstLineStart; + let lineDelimiter = ""; + if (belowLine == this.getLineCount() - 1) { + let delimiterStart = model.getLineEnd(lastLine); + lineDelimiter = this.getText(delimiterStart, lastLineEnd); + text = lineDelimiter + text.substr(0, text.length - + lineDelimiter.length); + } + this.startCompoundChange(); + this.setText("", firstLineStart, lastLineEnd); + this.setText(text, insertAt, insertAt); + this.endCompoundChange(); + this.setSelection(insertAt + lineDelimiter.length, + insertAt + text.length); + } + return true; + }, + /** * The Orion Selection event handler. The current caret line is * highlighted and for Linux users the selected text is copied into the X11 diff --git a/browser/devtools/sourceeditor/source-editor.jsm b/browser/devtools/sourceeditor/source-editor.jsm index ae02faea5a5..7eb31982c65 100644 --- a/browser/devtools/sourceeditor/source-editor.jsm +++ b/browser/devtools/sourceeditor/source-editor.jsm @@ -192,6 +192,7 @@ SourceEditor.DEFAULTS = { * - action - name of the editor action to invoke. * - code - keyCode for the shortcut. * - accel - boolean for the Accel key (Cmd on Macs, Ctrl on Linux/Windows). + * - ctrl - boolean for the Control key * - shift - boolean for the Shift key. * - alt - boolean for the Alt key. * - callback - optional function to invoke, if the action is not predefined diff --git a/browser/devtools/sourceeditor/test/Makefile.in b/browser/devtools/sourceeditor/test/Makefile.in index eff7bde8d1a..94aaf9beea0 100644 --- a/browser/devtools/sourceeditor/test/Makefile.in +++ b/browser/devtools/sourceeditor/test/Makefile.in @@ -58,6 +58,7 @@ _BROWSER_TEST_FILES = \ browser_bug725388_mouse_events.js \ browser_bug707987_debugger_breakpoints.js \ browser_bug712982_line_ruler_click.js \ + browser_bug725618_moveLines_shortcut.js \ browser_bug700893_dirty_state.js \ head.js \ diff --git a/browser/devtools/sourceeditor/test/browser_bug725618_moveLines_shortcut.js b/browser/devtools/sourceeditor/test/browser_bug725618_moveLines_shortcut.js new file mode 100644 index 00000000000..c86a436c699 --- /dev/null +++ b/browser/devtools/sourceeditor/test/browser_bug725618_moveLines_shortcut.js @@ -0,0 +1,117 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +let tempScope = {}; +Cu.import("resource:///modules/source-editor.jsm", tempScope); +let SourceEditor = tempScope.SourceEditor; + +let editor; +let testWin; + +function test() +{ + waitForExplicitFinish(); + + const windowUrl = "data:application/vnd.mozilla.xul+xml," + + "" + + ""; + const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no"; + + testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null); + testWin.addEventListener("load", function onWindowLoad() { + testWin.removeEventListener("load", onWindowLoad, false); + waitForFocus(initEditor, testWin); + }, false); +} + +function initEditor() +{ + let box = testWin.document.querySelector("box"); + + let text = "target\nfoo\nbar" + let config = { + initialText: text, + }; + + editor = new SourceEditor(); + editor.init(box, config, editorLoaded); +} + +function editorLoaded() +{ + editor.focus(); + + editor.setCaretOffset(0); + + let modifiers = {altKey: true, ctrlKey: Services.appinfo.OS == "Darwin"}; + + EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin); + is(editor.getText(), "foo\ntarget\nbar", "Move lines down works"); + is(editor.getSelectedText(), "target\n", "selection is correct"); + + EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin); + is(editor.getText(), "foo\nbar\ntarget", "Move lines down works"); + is(editor.getSelectedText(), "target", "selection is correct"); + + EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin); + is(editor.getText(), "foo\nbar\ntarget", "Check for bottom of editor works"); + is(editor.getSelectedText(), "target", "selection is correct"); + + EventUtils.synthesizeKey("VK_UP", modifiers, testWin); + is(editor.getText(), "foo\ntarget\nbar", "Move lines up works"); + is(editor.getSelectedText(), "target\n", "selection is correct"); + + EventUtils.synthesizeKey("VK_UP", modifiers, testWin); + is(editor.getText(), "target\nfoo\nbar", "Move lines up works"); + is(editor.getSelectedText(), "target\n", "selection is correct"); + + EventUtils.synthesizeKey("VK_UP", modifiers, testWin); + is(editor.getText(), "target\nfoo\nbar", "Check for top of editor works"); + is(editor.getSelectedText(), "target\n", "selection is correct"); + + editor.setSelection(0, 10); + info("text within selection =" + editor.getSelectedText()); + + EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin); + is(editor.getText(), "bar\ntarget\nfoo", "Multiple line move down works"); + is(editor.getSelectedText(), "target\nfoo", "selection is correct"); + + EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin); + is(editor.getText(), "bar\ntarget\nfoo", + "Check for bottom of editor works with multiple line selection"); + is(editor.getSelectedText(), "target\nfoo", "selection is correct"); + + EventUtils.synthesizeKey("VK_UP", modifiers, testWin); + is(editor.getText(), "target\nfoo\nbar", "Multiple line move up works"); + is(editor.getSelectedText(), "target\nfoo\n", "selection is correct"); + + EventUtils.synthesizeKey("VK_UP", modifiers, testWin); + is(editor.getText(), "target\nfoo\nbar", + "Check for top of editor works with multiple line selection"); + is(editor.getSelectedText(), "target\nfoo\n", "selection is correct"); + + editor.readOnly = true; + + editor.setCaretOffset(0); + + EventUtils.synthesizeKey("VK_UP", modifiers, testWin); + is(editor.getText(), "target\nfoo\nbar", + "Check for readOnly mode works with move lines up"); + + EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin); + is(editor.getText(), "target\nfoo\nbar", + "Check for readOnly mode works with move lines down"); + + finish(); +} + +registerCleanupFunction(function() +{ + editor.destroy(); + testWin.close(); + testWin = editor = null; +}); diff --git a/browser/devtools/styleinspector/CssLogic.jsm b/browser/devtools/styleinspector/CssLogic.jsm index d450536fc27..0a431db857d 100644 --- a/browser/devtools/styleinspector/CssLogic.jsm +++ b/browser/devtools/styleinspector/CssLogic.jsm @@ -1198,11 +1198,19 @@ function CssRule(aCssSheet, aDomRule, aElement) this._cssSheet = aCssSheet; this._domRule = aDomRule; + let parentRule = aDomRule.parentRule; + if (parentRule && parentRule.type == Ci.nsIDOMCSSRule.MEDIA_RULE) { + this.mediaText = parentRule.media.mediaText; + } + if (this._cssSheet) { // parse _domRule.selectorText on call to this.selectors this._selectors = null; this.line = this._cssSheet._cssLogic.domUtils.getRuleLine(this._domRule); this.source = this._cssSheet.shortSource + ":" + this.line; + if (this.mediaText) { + this.source += " @media " + this.mediaText; + } this.href = this._cssSheet.href; this.contentRule = this._cssSheet.contentSheet; } else if (aElement) { @@ -1218,6 +1226,13 @@ function CssRule(aCssSheet, aDomRule, aElement) CssRule.prototype = { _passId: null, + mediaText: "", + + get isMediaRule() + { + return !!this.mediaText; + }, + /** * Check if the parent stylesheet is allowed by the CssLogic.sourceFilter. * diff --git a/browser/devtools/styleinspector/CssRuleView.jsm b/browser/devtools/styleinspector/CssRuleView.jsm index 9d179a30dbe..26330951327 100644 --- a/browser/devtools/styleinspector/CssRuleView.jsm +++ b/browser/devtools/styleinspector/CssRuleView.jsm @@ -356,10 +356,20 @@ function Rule(aElementStyle, aOptions) this.style = aOptions.style || this.domRule.style; this.selectorText = aOptions.selectorText || this.domRule.selectorText; this.inherited = aOptions.inherited || null; + + if (this.domRule) { + let parentRule = this.domRule.parentRule; + if (parentRule && parentRule.type == Ci.nsIDOMCSSRule.MEDIA_RULE) { + this.mediaText = parentRule.media.mediaText; + } + } + this._getTextProperties(); } Rule.prototype = { + mediaText: "", + get title() { if (this._title) { @@ -380,7 +390,7 @@ Rule.prototype = { args, args.length); } - return this._title; + return this._title + (this.mediaText ? " @media " + this.mediaText : ""); }, /** @@ -722,8 +732,26 @@ CssRuleView.prototype = { }.bind(this); this._createEditors(); + + // When creating a new property, we fake the normal property + // editor behavior (focusing a property's value after entering its + // name) by responding to the name's blur event, creating the + // value editor, and grabbing focus to the value editor. But if + // focus has already moved to another document, we won't be able + // to move focus to the new editor. + // Create a focusable item at the end of the editors to catch these + // cases. + this._focusBackstop = createChild(this.element, "div", { + tabindex: 0, + }); + this._backstopHandler = function() { + // If this item is actually focused long enough to get the focus + // event, allow focus to move on out of this document. + moveFocus(this.doc.defaultView, FOCUS_FORWARD); + }.bind(this); + this._focusBackstop.addEventListener("focus", this._backstopHandler, false); }, - + /** * Update the rules for the currently highlighted element. */ @@ -752,6 +780,12 @@ CssRuleView.prototype = { this._clearRules(); this._viewedElement = null; this._elementStyle = null; + + if (this._focusBackstop) { + this._focusBackstop.removeEventListener("focus", this._backstopHandler, false); + this._backstopHandler = null; + this._focusBackstop = null; + } }, /** @@ -835,7 +869,6 @@ RuleEditor.prototype = { this.openBrace = createChild(header, "span", { class: "ruleview-ruleopen", - tabindex: "0", textContent: " {" }); diff --git a/browser/devtools/styleinspector/test/Makefile.in b/browser/devtools/styleinspector/test/Makefile.in index a313cb6b630..104a4a4f0f2 100644 --- a/browser/devtools/styleinspector/test/Makefile.in +++ b/browser/devtools/styleinspector/test/Makefile.in @@ -60,7 +60,10 @@ _BROWSER_TEST_FILES = \ browser_ruleview_manipulation.js \ browser_ruleview_override.js \ browser_ruleview_ui.js \ + browser_ruleview_focus.js \ browser_bug705707_is_content_stylesheet.js \ + browser_bug722196_property_view_media_queries.js \ + browser_bug722196_rule_view_media_queries.js \ browser_bug_592743_specificity.js \ head.js \ $(NULL) @@ -74,6 +77,7 @@ _BROWSER_TEST_PAGES = \ browser_bug705707_is_content_stylesheet_script.css \ browser_bug705707_is_content_stylesheet.xul \ browser_bug705707_is_content_stylesheet_xul.css \ + browser_bug722196_identify_media_queries.html \ $(NULL) libs:: $(_BROWSER_TEST_FILES) diff --git a/browser/devtools/styleinspector/test/browser_bug722196_identify_media_queries.html b/browser/devtools/styleinspector/test/browser_bug722196_identify_media_queries.html new file mode 100644 index 00000000000..1adb8bc7a73 --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_bug722196_identify_media_queries.html @@ -0,0 +1,24 @@ + + + test + + + + +

+ + diff --git a/browser/devtools/styleinspector/test/browser_bug722196_property_view_media_queries.js b/browser/devtools/styleinspector/test/browser_bug722196_property_view_media_queries.js new file mode 100644 index 00000000000..fa1602b23ec --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_bug722196_property_view_media_queries.js @@ -0,0 +1,68 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that we correctly display appropriate media query titles in the +// property view. + +let doc; +let stylePanel; + +const TEST_URI = "http://example.com/browser/browser/devtools/styleinspector/" + + "test/browser_bug722196_identify_media_queries.html"; + +function test() +{ + waitForExplicitFinish(); + addTab(TEST_URI); + browser.addEventListener("load", docLoaded, true); +} + +function docLoaded() +{ + browser.removeEventListener("load", docLoaded, true); + doc = content.document; + stylePanel = new StyleInspector(window); + Services.obs.addObserver(checkSheets, "StyleInspector-opened", false); + stylePanel.createPanel(false, function() { + stylePanel.open(doc.body); + }); +} + +function checkSheets() +{ + Services.obs.removeObserver(checkSheets, "StyleInspector-opened", false); + + ok(stylePanel.isOpen(), "style inspector is open"); + + var div = doc.querySelector("div"); + ok(div, "captain, we have the div"); + + stylePanel.selectNode(div); + + let cssLogic = stylePanel.cssLogic; + cssLogic.processMatchedSelectors(); + + let _strings = Services.strings + .createBundle("chrome://browser/locale/devtools/styleinspector.properties"); + + let inline = _strings.GetStringFromName("rule.sourceInline"); + + let source1 = inline + ":8"; + let source2 = inline + ":15 @media screen and (min-width: 1px)"; + is(cssLogic._matchedRules[0][0].source, source1, + "rule.source gives correct output for rule 1"); + is(cssLogic._matchedRules[1][0].source, source2, + "rule.source gives correct output for rule 2"); + + Services.obs.addObserver(finishUp, "StyleInspector-closed", false); + stylePanel.close(); +} + +function finishUp() +{ + Services.obs.removeObserver(finishUp, "StyleInspector-closed", false); + doc = null; + gBrowser.removeCurrentTab(); + finish(); +} diff --git a/browser/devtools/styleinspector/test/browser_bug722196_rule_view_media_queries.js b/browser/devtools/styleinspector/test/browser_bug722196_rule_view_media_queries.js new file mode 100644 index 00000000000..9ba647ce959 --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_bug722196_rule_view_media_queries.js @@ -0,0 +1,55 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that we correctly display appropriate media query titles in the +// rule view. + +let tempScope = {}; +Cu.import("resource:///modules/devtools/CssRuleView.jsm", tempScope); +let _ElementStyle = tempScope._ElementStyle; +let doc; + +const TEST_URI = "http://example.com/browser/browser/devtools/styleinspector/" + + "test/browser_bug722196_identify_media_queries.html"; + +function test() +{ + waitForExplicitFinish(); + addTab(TEST_URI); + browser.addEventListener("load", docLoaded, true); +} + +function docLoaded() +{ + browser.removeEventListener("load", docLoaded, true); + doc = content.document; + checkSheets(); +} + +function checkSheets() +{ + var div = doc.querySelector("div"); + ok(div, "captain, we have the div"); + + let elementStyle = new _ElementStyle(div); + is(elementStyle.rules.length, 3, "Should have 3 rules."); + + let _strings = Services.strings + .createBundle("chrome://browser/locale/devtools/styleinspector.properties"); + + let inline = _strings.GetStringFromName("rule.sourceInline"); + + is(elementStyle.rules[0].title, inline, "check rule 0 title"); + is(elementStyle.rules[1].title, inline + + ":15 @media screen and (min-width: 1px)", "check rule 1 title"); + is(elementStyle.rules[2].title, inline + ":8", "check rule 2 title"); + finishUp(); +} + +function finishUp() +{ + doc = null; + gBrowser.removeCurrentTab(); + finish(); +} diff --git a/browser/devtools/styleinspector/test/browser_ruleview_focus.js b/browser/devtools/styleinspector/test/browser_ruleview_focus.js new file mode 100644 index 00000000000..188255e4b73 --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_ruleview_focus.js @@ -0,0 +1,106 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that focus doesn't leave the style editor when adding a property +// (bug 719916) + +let doc; +let stylePanel; + +function waitForRuleView(aCallback) +{ + if (InspectorUI.ruleView) { + aCallback(); + return; + } + + let ruleViewFrame = InspectorUI.getToolIframe(InspectorUI.ruleViewObject); + ruleViewFrame.addEventListener("load", function(evt) { + ruleViewFrame.removeEventListener(evt.type, arguments.callee, true); + executeSoon(function() { + aCallback(); + }); + }, true); +} + +function waitForEditorFocus(aParent, aCallback) +{ + aParent.addEventListener("focus", function onFocus(evt) { + if (evt.target.inplaceEditor) { + aParent.removeEventListener("focus", onFocus, true); + let editor = evt.target.inplaceEditor; + executeSoon(function() { + aCallback(editor); + }); + } + }, true); +} + +function openRuleView() +{ + Services.obs.addObserver(function onOpened() { + Services.obs.removeObserver(onOpened, + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); + + // Highlight a node. + let node = content.document.getElementsByTagName("h1")[0]; + InspectorUI.inspectNode(node); + InspectorUI.stopInspecting(); + + // Open the rule view sidebar. + waitForRuleView(testFocus); + + InspectorUI.showSidebar(); + InspectorUI.ruleButton.click(); + + testFocus(); + }, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.openInspectorUI(); +} + +function testFocus() +{ + let ruleViewFrame = InspectorUI.getToolIframe(InspectorUI.ruleViewObject); + let brace = ruleViewFrame.contentDocument.querySelectorAll(".ruleview-ruleclose")[0]; + waitForEditorFocus(brace.parentNode, function onNewElement(aEditor) { + aEditor.input.value = "color"; + waitForEditorFocus(brace.parentNode, function onEditingValue(aEditor) { + // If we actually get this focus we're ok. + ok(true, "We got focus."); + aEditor.input.value = "green"; + + // If we've retained focus, pressing return will start a new editor. + // If not, we'll wait here until we time out. + waitForEditorFocus(brace.parentNode, function onNewEditor(aEditor) { + aEditor.input.blur(); + finishTest(); + }); + EventUtils.sendKey("return"); + }); + EventUtils.sendKey("return"); + }); + + brace.focus(); +} + +function finishUp() +{ + doc = stylePanel = null; + gBrowser.removeCurrentTab(); + finish(); +} + +function test() +{ + waitForExplicitFinish(); + gBrowser.selectedTab = gBrowser.addTab(); + gBrowser.selectedBrowser.addEventListener("load", function(evt) { + gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true); + doc = content.document; + doc.title = "Rule View Test"; + waitForFocus(openRuleView, content); + }, true); + + content.location = "data:text/html,

Some header text

"; +} diff --git a/browser/devtools/webconsole/GcliCommands.jsm b/browser/devtools/webconsole/GcliCommands.jsm index 07dbf13d82d..92ba54fc773 100644 --- a/browser/devtools/webconsole/GcliCommands.jsm +++ b/browser/devtools/webconsole/GcliCommands.jsm @@ -20,6 +20,7 @@ * * Contributor(s): * Joe Walker (original author) + * Mihai Sucan * * 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 @@ -160,8 +161,6 @@ gcli.addCommand({ } }); -let breakpoints = []; - /** * 'break' command */ @@ -180,17 +179,25 @@ gcli.addCommand({ description: gcli.lookup("breaklistDesc"), returnType: "html", exec: function(args, context) { - if (breakpoints.length === 0) { + let win = HUDService.currentContext(); + let dbg = win.DebuggerUI.getDebugger(win.gBrowser.selectedTab); + if (!dbg) { + return gcli.lookup("breakaddDebuggerStopped"); + } + let breakpoints = dbg.breakpoints; + + if (Object.keys(breakpoints).length === 0) { return gcli.lookup("breaklistNone"); } let reply = gcli.lookup("breaklistIntro"); reply += "
    "; - breakpoints.forEach(function(breakpoint) { + for each (let breakpoint in breakpoints) { let text = gcli.lookupFormat("breaklistLineEntry", - [breakpoint.file, breakpoint.line]); + [breakpoint.location.url, + breakpoint.location.line]); reply += "
  1. " + text + "
  2. "; - }); + }; reply += "
"; return reply; } @@ -248,14 +255,11 @@ gcli.addCommand({ } var promise = context.createPromise(); let position = { url: args.file, line: args.line }; - dbg.activeThread.setBreakpoint(position, function(aResponse, aBpClient) { - if (aResponse.error) { - promise.resolve(gcli.lookupFormat("breakaddFailed", - [ aResponse.error ])); + dbg.addBreakpoint(position, function(aBreakpoint, aError) { + if (aError) { + promise.resolve(gcli.lookupFormat("breakaddFailed", [aError])); return; } - args.client = aBpClient; - breakpoints.push(args); promise.resolve(gcli.lookup("breakaddAdded")); }); return promise; @@ -275,19 +279,37 @@ gcli.addCommand({ type: { name: "number", min: 0, - max: function() { return breakpoints.length - 1; } + max: function() { + let win = HUDService.currentContext(); + let dbg = win.DebuggerUI.getDebugger(win.gBrowser.selectedTab); + if (!dbg) { + return gcli.lookup("breakaddDebuggerStopped"); + } + return Object.keys(dbg.breakpoints).length - 1; + }, }, description: gcli.lookup("breakdelBreakidDesc") } ], returnType: "html", exec: function(args, context) { - let breakpoint = breakpoints.splice(args.breakid, 1)[0]; - var promise = context.createPromise(); + let win = HUDService.currentContext(); + let dbg = win.DebuggerUI.getDebugger(win.gBrowser.selectedTab); + if (!dbg) { + return gcli.lookup("breakaddDebuggerStopped"); + } + + let breakpoints = dbg.breakpoints; + let id = Object.keys(dbg.breakpoints)[args.breakid]; + if (!id || !(id in breakpoints)) { + return gcli.lookup("breakNotFound"); + } + + let promise = context.createPromise(); try { - breakpoint.client.remove(function(aResponse) { - promise.resolve(gcli.lookup("breakdelRemoved")); - }); + dbg.removeBreakpoint(breakpoints[id], function() { + promise.resolve(gcli.lookup("breakdelRemoved")); + }); } catch (ex) { // If the debugger has been closed already, don't scare the user. promise.resolve(gcli.lookup("breakdelRemoved")); diff --git a/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties index e5fd687186e..52bda769868 100644 --- a/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties +++ b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties @@ -255,6 +255,10 @@ breakdelBreakidDesc=Index of breakpoint # command to explain that a breakpoint was removed. breakdelRemoved=Breakpoint removed +# LOCALIZATION NOTE (breakNotFound) Used in the output of the 'break del' +# command to explain that the breakpoint was not found. +breakNotFound=Breakpoint was not found + # LOCALIZATION NOTE (consolecloseDesc) A very short description of the # 'console close' command. This string is designed to be shown in a menu # alongside the command name, which is why it should be as short as possible. diff --git a/browser/makefiles.sh b/browser/makefiles.sh index d76045f8cfa..663e677165d 100644 --- a/browser/makefiles.sh +++ b/browser/makefiles.sh @@ -139,6 +139,7 @@ if [ "$ENABLE_TESTS" ]; then browser/components/shell/test/Makefile browser/components/feeds/test/Makefile browser/components/feeds/test/chrome/Makefile + browser/components/migration/tests/Makefile browser/components/places/tests/Makefile browser/components/places/tests/chrome/Makefile browser/components/places/tests/browser/Makefile diff --git a/browser/modules/NewTabUtils.jsm b/browser/modules/NewTabUtils.jsm index 549c64434b2..072b970a370 100644 --- a/browser/modules/NewTabUtils.jsm +++ b/browser/modules/NewTabUtils.jsm @@ -121,8 +121,6 @@ let Storage = { // want any data from private browsing to show up. PinnedLinks.resetCache(); BlockedLinks.resetCache(); - - Pages.update(); } }, @@ -187,11 +185,6 @@ let AllPages = { */ _pages: [], - /** - * Tells whether we already added a preference observer. - */ - _observing: false, - /** * Cached value that tells whether the New Tab Page feature is enabled. */ @@ -203,12 +196,7 @@ let AllPages = { */ register: function AllPages_register(aPage) { this._pages.push(aPage); - - // Add the preference observer if we haven't already. - if (!this._observing) { - this._observing = true; - Services.prefs.addObserver(PREF_NEWTAB_ENABLED, this, true); - } + this._addObserver(); }, /** @@ -238,6 +226,14 @@ let AllPages = { Services.prefs.setBoolPref(PREF_NEWTAB_ENABLED, !!aEnabled); }, + /** + * Returns the number of registered New Tab Pages (i.e. the number of open + * about:newtab instances). + */ + get length() { + return this._pages.length; + }, + /** * Updates all currently active pages but the given one. * @param aExceptPage The page to exclude from updating. @@ -264,6 +260,15 @@ let AllPages = { }, this); }, + /** + * Adds a preference observer and turns itself into a no-op after the first + * invokation. + */ + _addObserver: function AllPages_addObserver() { + Services.prefs.addObserver(PREF_NEWTAB_ENABLED, this, true); + this._addObserver = function () {}; + }, + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]) }; @@ -512,6 +517,8 @@ let Links = { this._links = aLinks; executeCallbacks(); }.bind(this)); + + this._addObserver(); } }, @@ -544,7 +551,32 @@ let Links = { */ resetCache: function Links_resetCache() { this._links = []; - } + }, + + /** + * Implements the nsIObserver interface to get notified about browser history + * sanitization. + */ + observe: function Links_observe(aSubject, aTopic, aData) { + // Make sure to update open about:newtab instances. If there are no opened + // pages we can just wait for the next new tab to populate the cache again. + if (AllPages.length && AllPages.enabled) + this.populateCache(function () { AllPages.update() }, true); + else + this._links = null; + }, + + /** + * Adds a sanitization observer and turns itself into a no-op after the first + * invokation. + */ + _addObserver: function Links_addObserver() { + Services.obs.addObserver(this, "browser:purge-session-history", true); + this._addObserver = function () {}; + }, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, + Ci.nsISupportsWeakReference]) }; /** diff --git a/browser/themes/gnomestripe/aboutCertError.css b/browser/themes/gnomestripe/aboutCertError.css index e0d9e9b9347..17343b1c782 100644 --- a/browser/themes/gnomestripe/aboutCertError.css +++ b/browser/themes/gnomestripe/aboutCertError.css @@ -86,21 +86,24 @@ body[dir="rtl"] #errorPageContainer { -moz-margin-start: 80px; } -#technicalContent > h2, #expertContent > h2 { - background : url("chrome://browser/skin/section_expanded.png") left 0 no-repeat; +.expander > button { + -moz-padding-start: 20px; + -moz-margin-start: -20px; + background: url("chrome://browser/skin/aboutCertError_sectionExpanded.png") left center no-repeat; + border: none; + font: inherit; + color: inherit; + cursor: pointer; } -body[dir="rtl"] #technicalContent > h2, -body[dir="rtl"] #expertContent > h2 { - background-position: right 0; +body[dir="rtl"] .expander > button { + background-position: right center; } -#technicalContent[collapsed] > h2, -#expertContent[collapsed] > h2{ - background-image: url("chrome://browser/skin/section_collapsed.png"); +.expander[collapsed] > button { + background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed.png"); } -body[dir="rtl"] #technicalContent[collapsed] > h2, -body[dir="rtl"] #expertContent[collapsed] > h2 { - background-image: url("chrome://browser/skin/section_collapsed-rtl.png"); +body[dir="rtl"] .expander[collapsed] > button { + background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed-rtl.png"); } diff --git a/browser/themes/gnomestripe/section_collapsed-rtl.png b/browser/themes/gnomestripe/aboutCertError_sectionCollapsed-rtl.png similarity index 100% rename from browser/themes/gnomestripe/section_collapsed-rtl.png rename to browser/themes/gnomestripe/aboutCertError_sectionCollapsed-rtl.png diff --git a/browser/themes/gnomestripe/section_collapsed.png b/browser/themes/gnomestripe/aboutCertError_sectionCollapsed.png similarity index 100% rename from browser/themes/gnomestripe/section_collapsed.png rename to browser/themes/gnomestripe/aboutCertError_sectionCollapsed.png diff --git a/browser/themes/gnomestripe/section_expanded.png b/browser/themes/gnomestripe/aboutCertError_sectionExpanded.png similarity index 100% rename from browser/themes/gnomestripe/section_expanded.png rename to browser/themes/gnomestripe/aboutCertError_sectionExpanded.png diff --git a/browser/themes/gnomestripe/devtools/csshtmltree.css b/browser/themes/gnomestripe/devtools/csshtmltree.css index 738130a17f5..0ee9c461260 100644 --- a/browser/themes/gnomestripe/devtools/csshtmltree.css +++ b/browser/themes/gnomestripe/devtools/csshtmltree.css @@ -146,6 +146,7 @@ direction: ltr; padding: 0; -moz-padding-start: 20px; + vertical-align: text-bottom; } .bestmatch { diff --git a/browser/themes/gnomestripe/fullscreen-video.css b/browser/themes/gnomestripe/fullscreen-video.css deleted file mode 100644 index 2a090def7d3..00000000000 --- a/browser/themes/gnomestripe/fullscreen-video.css +++ /dev/null @@ -1,8 +0,0 @@ -#close { - position: absolute; - top: 0; - right: 0; - width: 32px; - height: 32px; - background: url(KUI-close.png) center center no-repeat; -} diff --git a/browser/themes/gnomestripe/jar.mn b/browser/themes/gnomestripe/jar.mn index 2772429a8e1..aaabfdde100 100644 --- a/browser/themes/gnomestripe/jar.mn +++ b/browser/themes/gnomestripe/jar.mn @@ -5,14 +5,16 @@ browser.jar: * skin/classic/browser/aboutPrivateBrowsing.css (aboutPrivateBrowsing.css) * skin/classic/browser/aboutSessionRestore.css (aboutSessionRestore.css) skin/classic/browser/aboutSessionRestore-window-icon.png - skin/classic/browser/aboutCertError.css (aboutCertError.css) + skin/classic/browser/aboutCertError.css + skin/classic/browser/aboutCertError_sectionCollapsed.png + skin/classic/browser/aboutCertError_sectionCollapsed-rtl.png + skin/classic/browser/aboutCertError_sectionExpanded.png #ifdef MOZ_SERVICES_SYNC skin/classic/browser/aboutSyncTabs.css #endif skin/classic/browser/actionicon-tab.png * skin/classic/browser/browser.css (browser.css) * skin/classic/browser/engineManager.css (engineManager.css) - skin/classic/browser/fullscreen-video.css skin/classic/browser/Geolocation-16.png skin/classic/browser/Geolocation-64.png skin/classic/browser/Go-arrow.png @@ -27,9 +29,6 @@ browser.jar: skin/classic/browser/Privacy-16.png skin/classic/browser/Privacy-48.png skin/classic/browser/searchbar.css (searchbar.css) - skin/classic/browser/section_collapsed.png - skin/classic/browser/section_collapsed-rtl.png - skin/classic/browser/section_expanded.png skin/classic/browser/Secure.png skin/classic/browser/Security-broken.png skin/classic/browser/setDesktopBackground.css diff --git a/browser/themes/pinstripe/aboutCertError.css b/browser/themes/pinstripe/aboutCertError.css index e0d9e9b9347..17343b1c782 100644 --- a/browser/themes/pinstripe/aboutCertError.css +++ b/browser/themes/pinstripe/aboutCertError.css @@ -86,21 +86,24 @@ body[dir="rtl"] #errorPageContainer { -moz-margin-start: 80px; } -#technicalContent > h2, #expertContent > h2 { - background : url("chrome://browser/skin/section_expanded.png") left 0 no-repeat; +.expander > button { + -moz-padding-start: 20px; + -moz-margin-start: -20px; + background: url("chrome://browser/skin/aboutCertError_sectionExpanded.png") left center no-repeat; + border: none; + font: inherit; + color: inherit; + cursor: pointer; } -body[dir="rtl"] #technicalContent > h2, -body[dir="rtl"] #expertContent > h2 { - background-position: right 0; +body[dir="rtl"] .expander > button { + background-position: right center; } -#technicalContent[collapsed] > h2, -#expertContent[collapsed] > h2{ - background-image: url("chrome://browser/skin/section_collapsed.png"); +.expander[collapsed] > button { + background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed.png"); } -body[dir="rtl"] #technicalContent[collapsed] > h2, -body[dir="rtl"] #expertContent[collapsed] > h2 { - background-image: url("chrome://browser/skin/section_collapsed-rtl.png"); +body[dir="rtl"] .expander[collapsed] > button { + background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed-rtl.png"); } diff --git a/browser/themes/pinstripe/section_collapsed-rtl.png b/browser/themes/pinstripe/aboutCertError_sectionCollapsed-rtl.png similarity index 100% rename from browser/themes/pinstripe/section_collapsed-rtl.png rename to browser/themes/pinstripe/aboutCertError_sectionCollapsed-rtl.png diff --git a/browser/themes/pinstripe/section_collapsed.png b/browser/themes/pinstripe/aboutCertError_sectionCollapsed.png similarity index 100% rename from browser/themes/pinstripe/section_collapsed.png rename to browser/themes/pinstripe/aboutCertError_sectionCollapsed.png diff --git a/browser/themes/pinstripe/section_expanded.png b/browser/themes/pinstripe/aboutCertError_sectionExpanded.png similarity index 100% rename from browser/themes/pinstripe/section_expanded.png rename to browser/themes/pinstripe/aboutCertError_sectionExpanded.png diff --git a/browser/themes/pinstripe/devtools/csshtmltree.css b/browser/themes/pinstripe/devtools/csshtmltree.css index 8593a585845..e40f46af724 100644 --- a/browser/themes/pinstripe/devtools/csshtmltree.css +++ b/browser/themes/pinstripe/devtools/csshtmltree.css @@ -148,6 +148,7 @@ direction: ltr; padding: 0; -moz-padding-start: 20px; + vertical-align: text-bottom; } .bestmatch { diff --git a/browser/themes/pinstripe/fullscreen-video.css b/browser/themes/pinstripe/fullscreen-video.css deleted file mode 100644 index ee39ad1bf67..00000000000 --- a/browser/themes/pinstripe/fullscreen-video.css +++ /dev/null @@ -1,8 +0,0 @@ -#close { - position: absolute; - top: 0; - left: 0; - width: 32px; - height: 32px; - background: url(KUI-close.png) center center no-repeat; -} diff --git a/browser/themes/pinstripe/jar.mn b/browser/themes/pinstripe/jar.mn index dec60186dec..95987071c55 100644 --- a/browser/themes/pinstripe/jar.mn +++ b/browser/themes/pinstripe/jar.mn @@ -4,14 +4,16 @@ browser.jar: * skin/classic/browser/aboutPrivateBrowsing.css (aboutPrivateBrowsing.css) * skin/classic/browser/aboutSessionRestore.css (aboutSessionRestore.css) skin/classic/browser/aboutSessionRestore-window-icon.png - skin/classic/browser/aboutCertError.css (aboutCertError.css) + skin/classic/browser/aboutCertError.css + skin/classic/browser/aboutCertError_sectionCollapsed.png + skin/classic/browser/aboutCertError_sectionCollapsed-rtl.png + skin/classic/browser/aboutCertError_sectionExpanded.png #ifdef MOZ_SERVICES_SYNC skin/classic/browser/aboutSyncTabs.css #endif skin/classic/browser/actionicon-tab.png * skin/classic/browser/browser.css (browser.css) * skin/classic/browser/engineManager.css (engineManager.css) - skin/classic/browser/fullscreen-video.css skin/classic/browser/Geolocation-16.png skin/classic/browser/Geolocation-64.png skin/classic/browser/home.png @@ -36,9 +38,6 @@ browser.jar: skin/classic/browser/searchbar-dropmarker.png skin/classic/browser/searchbar.css skin/classic/browser/Search.png - skin/classic/browser/section_collapsed.png - skin/classic/browser/section_collapsed-rtl.png - skin/classic/browser/section_expanded.png skin/classic/browser/Secure-Glyph-White.png skin/classic/browser/keyhole-circle.png skin/classic/browser/Toolbar.png diff --git a/browser/themes/winstripe/aboutCertError.css b/browser/themes/winstripe/aboutCertError.css index f67a7b9590b..17343b1c782 100644 --- a/browser/themes/winstripe/aboutCertError.css +++ b/browser/themes/winstripe/aboutCertError.css @@ -86,21 +86,24 @@ body[dir="rtl"] #errorPageContainer { -moz-margin-start: 80px; } -#technicalContent > h2, #expertContent > h2 { - background : url("chrome://browser/skin/section_expanded.png") left center no-repeat; +.expander > button { + -moz-padding-start: 20px; + -moz-margin-start: -20px; + background: url("chrome://browser/skin/aboutCertError_sectionExpanded.png") left center no-repeat; + border: none; + font: inherit; + color: inherit; + cursor: pointer; } -body[dir="rtl"] #technicalContent > h2, -body[dir="rtl"] #expertContent > h2 { +body[dir="rtl"] .expander > button { background-position: right center; } -#technicalContent[collapsed] > h2, -#expertContent[collapsed] > h2{ - background-image: url("chrome://browser/skin/section_collapsed.png"); +.expander[collapsed] > button { + background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed.png"); } -body[dir="rtl"] #technicalContent[collapsed] > h2, -body[dir="rtl"] #expertContent[collapsed] > h2 { - background-image: url("chrome://browser/skin/section_collapsed-rtl.png"); +body[dir="rtl"] .expander[collapsed] > button { + background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed-rtl.png"); } diff --git a/browser/themes/winstripe/section_collapsed-rtl.png b/browser/themes/winstripe/aboutCertError_sectionCollapsed-rtl.png similarity index 100% rename from browser/themes/winstripe/section_collapsed-rtl.png rename to browser/themes/winstripe/aboutCertError_sectionCollapsed-rtl.png diff --git a/browser/themes/winstripe/section_collapsed.png b/browser/themes/winstripe/aboutCertError_sectionCollapsed.png similarity index 100% rename from browser/themes/winstripe/section_collapsed.png rename to browser/themes/winstripe/aboutCertError_sectionCollapsed.png diff --git a/browser/themes/winstripe/section_expanded.png b/browser/themes/winstripe/aboutCertError_sectionExpanded.png similarity index 100% rename from browser/themes/winstripe/section_expanded.png rename to browser/themes/winstripe/aboutCertError_sectionExpanded.png diff --git a/browser/themes/winstripe/devtools/csshtmltree.css b/browser/themes/winstripe/devtools/csshtmltree.css index 3448e8f6076..42dce67568c 100644 --- a/browser/themes/winstripe/devtools/csshtmltree.css +++ b/browser/themes/winstripe/devtools/csshtmltree.css @@ -146,6 +146,7 @@ direction: ltr; padding: 0; -moz-padding-start: 20px; + vertical-align: text-bottom; } .bestmatch { diff --git a/browser/themes/winstripe/fullscreen-video.css b/browser/themes/winstripe/fullscreen-video.css deleted file mode 100644 index 2a090def7d3..00000000000 --- a/browser/themes/winstripe/fullscreen-video.css +++ /dev/null @@ -1,8 +0,0 @@ -#close { - position: absolute; - top: 0; - right: 0; - width: 32px; - height: 32px; - background: url(KUI-close.png) center center no-repeat; -} diff --git a/browser/themes/winstripe/jar.mn b/browser/themes/winstripe/jar.mn index 83c61045eda..e5d860334d2 100644 --- a/browser/themes/winstripe/jar.mn +++ b/browser/themes/winstripe/jar.mn @@ -7,7 +7,10 @@ browser.jar: * skin/classic/browser/aboutPrivateBrowsing.css (aboutPrivateBrowsing.css) * skin/classic/browser/aboutSessionRestore.css (aboutSessionRestore.css) skin/classic/browser/aboutSessionRestore-window-icon.png (preferences/application.png) - skin/classic/browser/aboutCertError.css (aboutCertError.css) + skin/classic/browser/aboutCertError.css + skin/classic/browser/aboutCertError_sectionCollapsed.png + skin/classic/browser/aboutCertError_sectionCollapsed-rtl.png + skin/classic/browser/aboutCertError_sectionExpanded.png #ifdef MOZ_SERVICES_SYNC skin/classic/browser/aboutSyncTabs.css #endif @@ -16,7 +19,6 @@ browser.jar: skin/classic/browser/appmenu-dropmarker.png * skin/classic/browser/browser.css (browser.css) * skin/classic/browser/engineManager.css (engineManager.css) - skin/classic/browser/fullscreen-video.css skin/classic/browser/Geolocation-16.png skin/classic/browser/Geolocation-64.png skin/classic/browser/Info.png (Info.png) @@ -38,9 +40,6 @@ browser.jar: skin/classic/browser/toolbarbutton-dropdown-arrow-inverted.png * skin/classic/browser/searchbar.css (searchbar.css) skin/classic/browser/searchbar-dropdown-arrow.png - skin/classic/browser/section_collapsed.png - skin/classic/browser/section_collapsed-rtl.png - skin/classic/browser/section_expanded.png skin/classic/browser/setDesktopBackground.css skin/classic/browser/menu-back.png (menu-back.png) skin/classic/browser/menu-forward.png (menu-forward.png) @@ -177,7 +176,10 @@ browser.jar: * skin/classic/aero/browser/aboutPrivateBrowsing.css (aboutPrivateBrowsing.css) * skin/classic/aero/browser/aboutSessionRestore.css (aboutSessionRestore.css) skin/classic/aero/browser/aboutSessionRestore-window-icon.png (aboutSessionRestore-window-icon-aero.png) - skin/classic/aero/browser/aboutCertError.css (aboutCertError.css) + skin/classic/aero/browser/aboutCertError.css + skin/classic/aero/browser/aboutCertError_sectionCollapsed.png + skin/classic/aero/browser/aboutCertError_sectionCollapsed-rtl.png + skin/classic/aero/browser/aboutCertError_sectionExpanded.png #ifdef MOZ_SERVICES_SYNC skin/classic/aero/browser/aboutSyncTabs.css #endif @@ -186,7 +188,6 @@ browser.jar: skin/classic/aero/browser/appmenu-icons.png * skin/classic/aero/browser/browser.css (browser-aero.css) * skin/classic/aero/browser/engineManager.css (engineManager.css) - skin/classic/aero/browser/fullscreen-video.css skin/classic/aero/browser/Geolocation-16.png skin/classic/aero/browser/Geolocation-64.png skin/classic/aero/browser/Info.png (Info-aero.png) @@ -208,9 +209,6 @@ browser.jar: skin/classic/aero/browser/toolbarbutton-dropdown-arrow-inverted.png * skin/classic/aero/browser/searchbar.css (searchbar.css) skin/classic/aero/browser/searchbar-dropdown-arrow.png (searchbar-dropdown-arrow-aero.png) - skin/classic/aero/browser/section_collapsed.png - skin/classic/aero/browser/section_collapsed-rtl.png - skin/classic/aero/browser/section_expanded.png skin/classic/aero/browser/setDesktopBackground.css skin/classic/aero/browser/menu-back.png (menu-back-aero.png) skin/classic/aero/browser/menu-forward.png (menu-forward-aero.png) diff --git a/build/unix/check_debug_ranges.py b/build/autoconf/check_debug_ranges.py similarity index 100% rename from build/unix/check_debug_ranges.py rename to build/autoconf/check_debug_ranges.py diff --git a/build/autoconf/compiler-opts.m4 b/build/autoconf/compiler-opts.m4 index 081d8e4f5d8..e4f75b0eee1 100644 --- a/build/autoconf/compiler-opts.m4 +++ b/build/autoconf/compiler-opts.m4 @@ -9,5 +9,76 @@ if test "$CLANG_CXX"; then ## from C. _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-unknown-warning-option -Wno-return-type-c-linkage" fi -]) +if test "$GNU_CC"; then + CFLAGS="$CFLAGS -ffunction-sections -fdata-sections" + CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections" +fi + +dnl ======================================================== +dnl = Identical Code Folding +dnl ======================================================== + +MOZ_ARG_DISABLE_BOOL(icf, +[ --disable-icf Disable Identical Code Folding], + MOZ_DISABLE_ICF=1, + MOZ_DISABLE_ICF= ) + +if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -z "$MOZ_DISABLE_ICF"; then + AC_CACHE_CHECK([whether the linker supports Identical Code Folding], + LD_SUPPORTS_ICF, + [echo 'int foo() {return 42;}' \ + 'int bar() {return 42;}' \ + 'int main() {return foo() - bar();}' > conftest.${ac_ext} + # If the linker supports ICF, foo and bar symbols will have + # the same address + if AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS -Wl,--icf=safe -ffunction-sections conftest.${ac_ext} $LIBS 1>&2]) && + test -s conftest${ac_exeext} && + objdump -t conftest${ac_exeext} | awk changequote(<<, >>)'{a[<<$>>6] = <<$>>1} END {if (a["foo"] && (a["foo"] != a["bar"])) { exit 1 }}'changequote([, ]); then + LD_SUPPORTS_ICF=yes + else + LD_SUPPORTS_ICF=no + fi + rm -rf conftest*]) + if test "$LD_SUPPORTS_ICF" = yes; then + _SAVE_LDFLAGS="$LDFLAGS -Wl,--icf=safe" + LDFLAGS="$LDFLAGS -Wl,--icf=safe -Wl,--print-icf-sections" + AC_TRY_LINK([], [], + [LD_PRINT_ICF_SECTIONS=-Wl,--print-icf-sections], + [LD_PRINT_ICF_SECTIONS=]) + AC_SUBST([LD_PRINT_ICF_SECTIONS]) + LDFLAGS="$_SAVE_LDFLAGS" + fi +fi + +dnl ======================================================== +dnl = Automatically remove dead symbols +dnl ======================================================== + +if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -n "$MOZ_DEBUG_FLAGS"; then + dnl See bug 670659 + AC_CACHE_CHECK([whether removing dead symbols breaks debugging], + GC_SECTIONS_BREAKS_DEBUG_RANGES, + [echo 'int foo() {return 42;}' \ + 'int bar() {return 1;}' \ + 'int main() {return foo();}' > conftest.${ac_ext} + if AC_TRY_COMMAND([${CC-cc} -o conftest.${ac_objext} $CFLAGS $MOZ_DEBUG_FLAGS -c conftest.${ac_ext} 1>&2]) && + AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS $MOZ_DEBUG_FLAGS -Wl,--gc-sections conftest.${ac_objext} $LIBS 1>&2]) && + test -s conftest${ac_exeext} -a -s conftest.${ac_objext}; then + if test "`$PYTHON "$_topsrcdir"/build/autoconf/check_debug_ranges.py conftest.${ac_objext} conftest.${ac_ext}`" = \ + "`$PYTHON "$_topsrcdir"/build/autoconf/check_debug_ranges.py conftest${ac_exeext} conftest.${ac_ext}`"; then + GC_SECTIONS_BREAKS_DEBUG_RANGES=no + else + GC_SECTIONS_BREAKS_DEBUG_RANGES=yes + fi + else + dnl We really don't expect to get here, but just in case + GC_SECTIONS_BREAKS_DEBUG_RANGES="no, but it's broken in some other way" + fi + rm -rf conftest*]) + if test "$GC_SECTIONS_BREAKS_DEBUG_RANGES" = no; then + DSO_LDOPTS="$DSO_LDOPTS -Wl,--gc-sections" + fi +fi + +]) diff --git a/build/autoconf/expandlibs.m4 b/build/autoconf/expandlibs.m4 new file mode 100644 index 00000000000..ef228abf708 --- /dev/null +++ b/build/autoconf/expandlibs.m4 @@ -0,0 +1,56 @@ +AC_DEFUN([MOZ_EXPAND_LIBS], +[ +dnl ======================================================== +dnl = +dnl = Check what kind of list files are supported by the +dnl = linker +dnl = +dnl ======================================================== + +AC_CACHE_CHECK(what kind of list files are supported by the linker, + EXPAND_LIBS_LIST_STYLE, + [echo "int main() {return 0;}" > conftest.${ac_ext} + if AC_TRY_COMMAND(${CC-cc} -o conftest.${OBJ_SUFFIX} -c $CFLAGS $CPPFLAGS conftest.${ac_ext} 1>&5) && test -s conftest.${OBJ_SUFFIX}; then + echo "INPUT(conftest.${OBJ_SUFFIX})" > conftest.list + if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS conftest.list $LIBS 1>&5) && test -s conftest${ac_exeext}; then + EXPAND_LIBS_LIST_STYLE=linkerscript + else + echo "conftest.${OBJ_SUFFIX}" > conftest.list + if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS @conftest.list $LIBS 1>&5) && test -s conftest${ac_exeext}; then + EXPAND_LIBS_LIST_STYLE=list + else + EXPAND_LIBS_LIST_STYLE=none + fi + fi + else + dnl We really don't expect to get here, but just in case + AC_ERROR([couldn't compile a simple C file]) + fi + rm -rf conftest*]) + +LIBS_DESC_SUFFIX=desc +AC_SUBST(LIBS_DESC_SUFFIX) +AC_SUBST(EXPAND_LIBS_LIST_STYLE) + +if test "$GCC_USE_GNU_LD"; then + AC_CACHE_CHECK(what kind of ordering can be done with the linker, + EXPAND_LIBS_ORDER_STYLE, + [> conftest.order + _SAVE_LDFLAGS="$LDFLAGS" + LDFLAGS="${LDFLAGS} -Wl,--section-ordering-file,conftest.order" + AC_TRY_LINK([], [], + EXPAND_LIBS_ORDER_STYLE=section-ordering-file, + EXPAND_LIBS_ORDER_STYLE=) + LDFLAGS="$_SAVE_LDFLAGS" + if test -z "$EXPAND_LIBS_ORDER_STYLE"; then + if AC_TRY_COMMAND(${CC-cc} ${DSO_LDOPTS} ${LDFLAGS} -o ${DLL_PREFIX}conftest${DLL_SUFFIX} -Wl,--verbose 2> /dev/null | sed -n '/^===/,/^===/p' | grep '\.text'); then + EXPAND_LIBS_ORDER_STYLE=linkerscript + else + EXPAND_LIBS_ORDER_STYLE=none + fi + rm -f ${DLL_PREFIX}conftest${DLL_SUFFIX} + fi]) +fi +AC_SUBST(EXPAND_LIBS_ORDER_STYLE) + +]) diff --git a/build/automation.py.in b/build/automation.py.in index ca9c8ce4c33..5b4a5272aed 100644 --- a/build/automation.py.in +++ b/build/automation.py.in @@ -395,6 +395,7 @@ user_pref("browser.safebrowsing.provider.0.keyURL", "http://%(server)s/safebrows user_pref("browser.safebrowsing.provider.0.updateURL", "http://%(server)s/safebrowsing-dummy/update"); // Point update checks to the local testing server for fast failures user_pref("extensions.update.url", "http://%(server)s/extensions-dummy/updateURL"); +user_pref("extensions.update.background.url", "http://%(server)s/extensions-dummy/updateBackgroundURL"); user_pref("extensions.blocklist.url", "http://%(server)s/extensions-dummy/blocklistURL"); user_pref("extensions.hotfix.url", "http://%(server)s/extensions-dummy/hotfixURL"); // Make sure opening about:addons won't hit the network @@ -737,8 +738,11 @@ user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless t def killAndGetStack(self, proc, utilityPath, debuggerInfo): """Kill the process, preferrably in a way that gets us a stack trace.""" - if not debuggerInfo and not self.haveDumpedScreen: - self.dumpScreen(utilityPath) + if not debuggerInfo: + if self.haveDumpedScreen: + self.log.info("Not taking screenshot here: see the one that was previously logged") + else: + self.dumpScreen(utilityPath) if self.CRASHREPORTER and not debuggerInfo: if self.UNIXISH: @@ -795,8 +799,11 @@ user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless t if stackFixerFunction: line = stackFixerFunction(line) self.log.info(line.rstrip().decode("UTF-8", "ignore")) - if not debuggerInfo and not self.haveDumpedScreen and "TEST-UNEXPECTED-FAIL" in line and "Test timed out" in line: - self.dumpScreen(utilityPath) + if not debuggerInfo and "TEST-UNEXPECTED-FAIL" in line and "Test timed out" in line: + if self.haveDumpedScreen: + self.log.info("Not taking screenshot here: see the one that was previously logged") + else: + self.dumpScreen(utilityPath) (line, didTimeout) = self.readWithTimeout(logsource, timeout) if not hitMaxTime and maxTime and datetime.now() - startTime > timedelta(seconds = maxTime): diff --git a/build/automationutils.py b/build/automationutils.py index c8067bd0347..fa72bdc0d1a 100644 --- a/build/automationutils.py +++ b/build/automationutils.py @@ -459,7 +459,7 @@ class ShutdownLeakLogger(object): DOM windows (that are still around after test suite shutdown, despite running the GC) to the tests that created them and prints leak statistics. """ - MAX_LEAK_COUNT = 123 + MAX_LEAK_COUNT = 120 def __init__(self, logger): self.logger = logger diff --git a/build/mobile/robocop/Makefile.in b/build/mobile/robocop/Makefile.in index 47e3aca33d8..6488befce41 100644 --- a/build/mobile/robocop/Makefile.in +++ b/build/mobile/robocop/Makefile.in @@ -66,7 +66,10 @@ _JAVA_HARNESS = \ _JAVA_TESTS = $(patsubst $(TESTPATH)/%.in,%,$(wildcard $(TESTPATH)/*.java.in)) -_TEST_FILES = $(wildcard $(TESTPATH)/*.html) +_TEST_FILES = \ + $(wildcard $(TESTPATH)/*.html) \ + $(wildcard $(TESTPATH)/*.sjs) \ + $(NULL) _ROBOCOP_TOOLS = \ $(TESTPATH)/robocop.ini \ diff --git a/caps/idl/nsIPrincipal.idl b/caps/idl/nsIPrincipal.idl index 3a09ca8db51..cfc2bac4dfa 100644 --- a/caps/idl/nsIPrincipal.idl +++ b/caps/idl/nsIPrincipal.idl @@ -52,7 +52,7 @@ interface nsIContentSecurityPolicy; [ptr] native JSContext(JSContext); [ptr] native JSPrincipals(JSPrincipals); -[scriptable, uuid(1f83b0e0-6b63-4bdc-a50a-b9afe256bd25)] +[scriptable, uuid(f8c4c89a-d726-421b-8415-3e34b241175b)] interface nsIPrincipal : nsISerializable { /** @@ -96,12 +96,6 @@ interface nsIPrincipal : nsISerializable */ [noscript] readonly attribute unsigned long hashValue; - /** - * Returns the JS equivalent of the principal. - * @see JSPrincipals.h - */ - [noscript] JSPrincipals getJSPrincipals(in JSContext cx); - /** * The domain security policy of the principal. */ diff --git a/caps/include/nsJSPrincipals.h b/caps/include/nsJSPrincipals.h index 782ac029294..f983532cecb 100644 --- a/caps/include/nsJSPrincipals.h +++ b/caps/include/nsJSPrincipals.h @@ -43,14 +43,47 @@ class nsCString; -struct nsJSPrincipals : JSPrincipals +struct nsJSPrincipals : nsIPrincipal, JSPrincipals { - static nsresult Startup(); - nsJSPrincipals(); - nsresult Init(nsIPrincipal* aPrincipal, const nsCString& aCodebase); - ~nsJSPrincipals(void); + static JSBool Subsume(JSPrincipals *jsprin, JSPrincipals *other); + static void Destroy(JSPrincipals *jsprin); + static JSBool Transcode(JSXDRState *xdr, JSPrincipals **jsprinp); - nsIPrincipal *nsIPrincipalPtr; // [WEAK] it owns us. + /* + * Get a weak reference to nsIPrincipal associated with the given JS + * principal. + */ + static nsJSPrincipals* get(JSPrincipals *principals) { + nsJSPrincipals *self = static_cast(principals); + MOZ_ASSERT_IF(self, self->debugToken == DEBUG_TOKEN); + return self; + } + + static nsJSPrincipals* get(nsIPrincipal *principal) { + nsJSPrincipals *self = static_cast(principal); + MOZ_ASSERT_IF(self, self->debugToken == DEBUG_TOKEN); + return self; + } + + nsJSPrincipals() { + refcount = 0; + setDebugToken(DEBUG_TOKEN); + } + + virtual ~nsJSPrincipals() { + setDebugToken(0); + } + + /** + * Return a string that can be used as JS script filename in error reports. + */ + virtual void GetScriptLocation(nsACString &aStr) = 0; + +#ifdef DEBUG + virtual void dumpImpl() = 0; +#endif + + static const uint32_t DEBUG_TOKEN = 0x0bf41760; }; #endif /* nsJSPrincipals_h__ */ diff --git a/caps/include/nsNullPrincipal.h b/caps/include/nsNullPrincipal.h index e342d287bb7..a2cbe139b0e 100644 --- a/caps/include/nsNullPrincipal.h +++ b/caps/include/nsNullPrincipal.h @@ -59,16 +59,16 @@ class nsIURI; #define NS_NULLPRINCIPAL_SCHEME "moz-nullprincipal" -class nsNullPrincipal : public nsIPrincipal +class nsNullPrincipal : public nsJSPrincipals { public: nsNullPrincipal(); - // Our refcount is managed by mJSPrincipals. Use this macro to avoid an + // Our refcount is managed by nsJSPrincipals. Use this macro to avoid an // extra refcount member. // FIXME: bug 327245 -- I sorta wish there were a clean way to share the - // mJSPrincipals munging code between the various principal classes without + // nsJSPrincipals munging code between the various principal classes without // giving up the NS_DECL_NSIPRINCIPAL goodness. NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIPRINCIPAL @@ -76,10 +76,15 @@ public: nsresult Init(); -protected: + virtual void GetScriptLocation(nsACString &aStr) MOZ_OVERRIDE; + +#ifdef DEBUG + virtual void dumpImpl() MOZ_OVERRIDE; +#endif + + protected: virtual ~nsNullPrincipal(); - nsJSPrincipals mJSPrincipals; nsCOMPtr mURI; }; diff --git a/caps/include/nsPrincipal.h b/caps/include/nsPrincipal.h index 8f78db43ef6..1542dc6ba7d 100644 --- a/caps/include/nsPrincipal.h +++ b/caps/include/nsPrincipal.h @@ -51,7 +51,7 @@ class nsIObjectInputStream; class nsIObjectOutputStream; -class nsPrincipal : public nsIPrincipal +class nsPrincipal : public nsJSPrincipals { public: nsPrincipal(); @@ -60,7 +60,7 @@ protected: virtual ~nsPrincipal(); public: - // Our refcount is managed by mJSPrincipals. Use this macro to avoid + // Our refcount is managed by nsJSPrincipals. Use this macro to avoid // an extra refcount member. NS_DECL_ISUPPORTS_INHERITED public: @@ -100,8 +100,13 @@ public: static const char sInvalid[]; + virtual void GetScriptLocation(nsACString &aStr) MOZ_OVERRIDE; + +#ifdef DEBUG + virtual void dumpImpl() MOZ_OVERRIDE; +#endif + protected: - nsJSPrincipals mJSPrincipals; nsTArray< nsAutoPtr > mAnnotations; nsHashtable* mCapabilities; nsCString mPrefName; diff --git a/caps/include/nsScriptSecurityManager.h b/caps/include/nsScriptSecurityManager.h index ca0a9fb7079..94e5e469a15 100644 --- a/caps/include/nsScriptSecurityManager.h +++ b/caps/include/nsScriptSecurityManager.h @@ -428,6 +428,9 @@ private: jsid id, JSAccessMode mode, jsval *vp); + static JSPrincipals * + ObjectPrincipalFinder(JSObject *obj); + // Decides, based on CSP, whether or not eval() and stuff can be executed. static JSBool ContentSecurityPolicyPermitsJSAction(JSContext *cx); diff --git a/caps/include/nsSystemPrincipal.h b/caps/include/nsSystemPrincipal.h index d9154a6c4b1..25eaee25427 100644 --- a/caps/include/nsSystemPrincipal.h +++ b/caps/include/nsSystemPrincipal.h @@ -50,23 +50,26 @@ #define NS_SYSTEMPRINCIPAL_CONTRACTID "@mozilla.org/systemprincipal;1" -class nsSystemPrincipal : public nsIPrincipal +class nsSystemPrincipal : public nsJSPrincipals { public: - // Our refcount is managed by mJSPrincipals. Use this macro to avoid + // Our refcount is managed by nsJSPrincipals. Use this macro to avoid // an extra refcount member. NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIPRINCIPAL NS_DECL_NSISERIALIZABLE - nsresult Init(JSPrincipals **jsprin); - nsSystemPrincipal(); + virtual void GetScriptLocation(nsACString &aStr) MOZ_OVERRIDE; + +#ifdef DEBUG + virtual void dumpImpl() MOZ_OVERRIDE; +#endif + protected: virtual ~nsSystemPrincipal(void); - nsJSPrincipals mJSPrincipals; // XXX Probably unnecessary. See bug 143559. NS_DECL_OWNINGTHREAD }; diff --git a/caps/src/nsJSPrincipals.cpp b/caps/src/nsJSPrincipals.cpp index f86f0d38a33..5907b35ffea 100644 --- a/caps/src/nsJSPrincipals.cpp +++ b/caps/src/nsJSPrincipals.cpp @@ -50,46 +50,44 @@ #include "nsMemory.h" #include "nsStringBuffer.h" -static JSBool -nsJSPrincipalsSubsume(JSPrincipals *jsprin, JSPrincipals *other) -{ - nsJSPrincipals *nsjsprin = static_cast(jsprin); - nsJSPrincipals *nsother = static_cast(other); +// for mozilla::dom::workers::kJSPrincipalsDebugToken +#include "mozilla/dom/workers/Workers.h" +/* static */ JSBool +nsJSPrincipals::Subsume(JSPrincipals *jsprin, JSPrincipals *other) +{ bool result; - nsresult rv = nsjsprin->nsIPrincipalPtr->Subsumes(nsother->nsIPrincipalPtr, - &result); + nsresult rv = nsJSPrincipals::get(jsprin)->Subsumes(nsJSPrincipals::get(other), &result); return NS_SUCCEEDED(rv) && result; } -static void -nsDestroyJSPrincipals(JSContext *cx, struct JSPrincipals *jsprin) +/* static */ void +nsJSPrincipals::Destroy(JSPrincipals *jsprin) { - nsJSPrincipals *nsjsprin = static_cast(jsprin); + // The JS runtime can call this method during the last GC when + // nsScriptSecurityManager is destroyed. So we must not assume here that + // the security manager still exists. + + nsJSPrincipals *nsjsprin = nsJSPrincipals::get(jsprin); // We need to destroy the nsIPrincipal. We'll do this by adding // to the refcount and calling release - // Note that we don't want to use NS_IF_RELEASE because it will try - // to set nsjsprin->nsIPrincipalPtr to nsnull *after* nsjsprin has - // already been destroyed. #ifdef NS_BUILD_REFCNT_LOGGING // The refcount logging considers AddRef-to-1 to indicate creation, // so trick it into thinking it's otherwise, but balance the // Release() we do below. nsjsprin->refcount++; - nsjsprin->nsIPrincipalPtr->AddRef(); + nsjsprin->AddRef(); nsjsprin->refcount--; #else nsjsprin->refcount++; #endif - nsjsprin->nsIPrincipalPtr->Release(); - // The nsIPrincipal that we release owns the JSPrincipal struct, - // so we don't need to worry about "codebase" + nsjsprin->Release(); } -static JSBool -nsTranscodeJSPrincipals(JSXDRState *xdr, JSPrincipals **jsprinp) +/* static */ JSBool +nsJSPrincipals::Transcode(JSXDRState *xdr, JSPrincipals **jsprinp) { nsresult rv; @@ -107,12 +105,7 @@ nsTranscodeJSPrincipals(JSXDRState *xdr, JSPrincipals **jsprinp) if (NS_SUCCEEDED(rv)) { ::JS_XDRMemResetData(xdr); - // Require that GetJSPrincipals has been called already by the - // code that compiled the script that owns the principals. - nsJSPrincipals *nsjsprin = - static_cast(*jsprinp); - - rv = stream->WriteObject(nsjsprin->nsIPrincipalPtr, true); + rv = stream->WriteObject(nsJSPrincipals::get(*jsprinp), true); } } } else { @@ -141,7 +134,8 @@ nsTranscodeJSPrincipals(JSXDRState *xdr, JSPrincipals **jsprinp) nsMemory::Free(olddata); ::JS_XDRMemSetData(xdr, data, size); - prin->GetJSPrincipals(xdr->cx, jsprinp); + *jsprinp = nsJSPrincipals::get(prin); + JS_HoldPrincipals(*jsprinp); } } } @@ -156,69 +150,22 @@ nsTranscodeJSPrincipals(JSXDRState *xdr, JSPrincipals **jsprinp) return JS_TRUE; } -nsresult -nsJSPrincipals::Startup() +#ifdef DEBUG + +// Defined here so one can do principals->dump() in the debugger +JS_EXPORT_API(void) +JSPrincipals::dump() { - nsCOMPtr rtsvc = nsXPConnect::GetXPConnect(); - if (!rtsvc) - return NS_ERROR_FAILURE; - - JSRuntime *rt; - rtsvc->GetRuntime(&rt); - NS_ASSERTION(rt != nsnull, "no JSRuntime?!"); - - 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; -} - -nsJSPrincipals::nsJSPrincipals() -{ - codebase = nsnull; - refcount = 0; - destroy = nsDestroyJSPrincipals; - subsume = nsJSPrincipalsSubsume; - nsIPrincipalPtr = nsnull; -} - -nsresult -nsJSPrincipals::Init(nsIPrincipal *aPrincipal, const nsCString& aCodebase) -{ - if (nsIPrincipalPtr) { - NS_ERROR("Init called twice!"); - return NS_ERROR_UNEXPECTED; - } - - nsIPrincipalPtr = aPrincipal; - nsStringBuffer* buf = nsStringBuffer::FromString(aCodebase); - char* data; - if (buf) { - buf->AddRef(); - data = static_cast(buf->Data()); + if (debugToken == nsJSPrincipals::DEBUG_TOKEN) { + static_cast(this)->dumpImpl(); + } else if (debugToken == mozilla::dom::workers::kJSPrincipalsDebugToken) { + fprintf(stderr, "Web Worker principal singleton (%p)\n", this); } else { - PRUint32 len = aCodebase.Length(); - buf = nsStringBuffer::Alloc(len + 1); // addrefs - if (!buf) { - return NS_ERROR_OUT_OF_MEMORY; - } - data = static_cast(buf->Data()); - memcpy(data, aCodebase.get(), len); - data[len] = '\0'; - } - - codebase = data; - - return NS_OK; -} - -nsJSPrincipals::~nsJSPrincipals() -{ - if (codebase) { - nsStringBuffer::FromData(codebase)->Release(); + fprintf(stderr, + "!!! JSPrincipals (%p) is not nsJSPrincipals instance - bad token: " + "actual=0x%x expected=0x%x\n", + this, unsigned(debugToken), unsigned(nsJSPrincipals::DEBUG_TOKEN)); } } + +#endif diff --git a/caps/src/nsNullPrincipal.cpp b/caps/src/nsNullPrincipal.cpp index 8851652d71c..8f69ade01b5 100644 --- a/caps/src/nsNullPrincipal.cpp +++ b/caps/src/nsNullPrincipal.cpp @@ -69,8 +69,8 @@ NS_IMPL_CI_INTERFACE_GETTER2(nsNullPrincipal, NS_IMETHODIMP_(nsrefcnt) nsNullPrincipal::AddRef() { - NS_PRECONDITION(PRInt32(mJSPrincipals.refcount) >= 0, "illegal refcnt"); - nsrefcnt count = PR_ATOMIC_INCREMENT(&mJSPrincipals.refcount); + NS_PRECONDITION(PRInt32(refcount) >= 0, "illegal refcnt"); + nsrefcnt count = PR_ATOMIC_INCREMENT(&refcount); NS_LOG_ADDREF(this, count, "nsNullPrincipal", sizeof(*this)); return count; } @@ -78,8 +78,8 @@ nsNullPrincipal::AddRef() NS_IMETHODIMP_(nsrefcnt) nsNullPrincipal::Release() { - NS_PRECONDITION(0 != mJSPrincipals.refcount, "dup release"); - nsrefcnt count = PR_ATOMIC_DECREMENT(&mJSPrincipals.refcount); + NS_PRECONDITION(0 != refcount, "dup release"); + nsrefcnt count = PR_ATOMIC_DECREMENT(&refcount); NS_LOG_RELEASE(this, count, "nsNullPrincipal"); if (count == 0) { delete this; @@ -133,9 +133,24 @@ nsNullPrincipal::Init() mURI = new nsNullPrincipalURI(str); NS_ENSURE_TRUE(mURI, NS_ERROR_OUT_OF_MEMORY); - return mJSPrincipals.Init(this, str); + return NS_OK; } +void +nsNullPrincipal::GetScriptLocation(nsACString &aStr) +{ + mURI->GetSpec(aStr); +} + +#ifdef DEBUG +void nsNullPrincipal::dumpImpl() +{ + nsCAutoString str; + mURI->GetSpec(str); + fprintf(stderr, "nsNullPrincipal (%p) = %s\n", this, str.get()); +} +#endif + /** * nsIPrincipal implementation */ @@ -179,17 +194,6 @@ nsNullPrincipal::GetHashValue(PRUint32 *aResult) return NS_OK; } -NS_IMETHODIMP -nsNullPrincipal::GetJSPrincipals(JSContext *cx, JSPrincipals **aJsprin) -{ - NS_PRECONDITION(mJSPrincipals.nsIPrincipalPtr, - "mJSPrincipals is uninitalized!"); - - JSPRINCIPALS_HOLD(cx, &mJSPrincipals); - *aJsprin = &mJSPrincipals; - return NS_OK; -} - NS_IMETHODIMP nsNullPrincipal::GetSecurityPolicy(void** aSecurityPolicy) { diff --git a/caps/src/nsPrincipal.cpp b/caps/src/nsPrincipal.cpp index 97005d838ad..e7b0897005e 100644 --- a/caps/src/nsPrincipal.cpp +++ b/caps/src/nsPrincipal.cpp @@ -93,9 +93,9 @@ NS_IMPL_CI_INTERFACE_GETTER2(nsPrincipal, NS_IMETHODIMP_(nsrefcnt) nsPrincipal::AddRef() { - NS_PRECONDITION(PRInt32(mJSPrincipals.refcount) >= 0, "illegal refcnt"); + NS_PRECONDITION(PRInt32(refcount) >= 0, "illegal refcnt"); // XXXcaa does this need to be threadsafe? See bug 143559. - nsrefcnt count = PR_ATOMIC_INCREMENT(&mJSPrincipals.refcount); + nsrefcnt count = PR_ATOMIC_INCREMENT(&refcount); NS_LOG_ADDREF(this, count, "nsPrincipal", sizeof(*this)); return count; } @@ -103,8 +103,8 @@ nsPrincipal::AddRef() NS_IMETHODIMP_(nsrefcnt) nsPrincipal::Release() { - NS_PRECONDITION(0 != mJSPrincipals.refcount, "dup release"); - nsrefcnt count = PR_ATOMIC_DECREMENT(&mJSPrincipals.refcount); + NS_PRECONDITION(0 != refcount, "dup release"); + nsrefcnt count = PR_ATOMIC_DECREMENT(&refcount); NS_LOG_RELEASE(this, count, "nsPrincipal"); if (count == 0) { delete this; @@ -147,24 +147,10 @@ nsPrincipal::Init(const nsACString& aCertFingerprint, mCodebase = NS_TryToMakeImmutable(aCodebase); mCodebaseImmutable = URIIsImmutable(mCodebase); - nsresult rv; - if (!aCertFingerprint.IsEmpty()) { - rv = SetCertificate(aCertFingerprint, aSubjectName, aPrettyName, aCert); - if (NS_SUCCEEDED(rv)) { - rv = mJSPrincipals.Init(this, mCert->fingerprint); - } - } - else { - nsCAutoString spec; - rv = mCodebase->GetSpec(spec); - if (NS_SUCCEEDED(rv)) { - rv = mJSPrincipals.Init(this, spec); - } - } + if (aCertFingerprint.IsEmpty()) + return NS_OK; - NS_ASSERTION(NS_SUCCEEDED(rv), "nsPrincipal::Init() failed"); - - return rv; + return SetCertificate(aCertFingerprint, aSubjectName, aPrettyName, aCert); } nsPrincipal::~nsPrincipal(void) @@ -173,16 +159,25 @@ nsPrincipal::~nsPrincipal(void) delete mCapabilities; } -NS_IMETHODIMP -nsPrincipal::GetJSPrincipals(JSContext *cx, JSPrincipals **jsprin) +void +nsPrincipal::GetScriptLocation(nsACString &aStr) { - NS_PRECONDITION(mJSPrincipals.nsIPrincipalPtr, "mJSPrincipals is uninitialized!"); - - JSPRINCIPALS_HOLD(cx, &mJSPrincipals); - *jsprin = &mJSPrincipals; - return NS_OK; + if (mCert) { + aStr.Assign(mCert->fingerprint); + } else { + mCodebase->GetSpec(aStr); + } } +#ifdef DEBUG +void nsPrincipal::dumpImpl() +{ + nsCAutoString str; + GetScriptLocation(str); + fprintf(stderr, "nsPrincipal (%p) = %s\n", this, str.get()); +} +#endif + NS_IMETHODIMP nsPrincipal::GetOrigin(char **aOrigin) { @@ -883,9 +878,6 @@ nsPrincipal::InitFromPersistent(const char* aPrefName, mTrusted = aTrusted; } - rv = mJSPrincipals.Init(this, aToken); - NS_ENSURE_SUCCESS(rv, rv); - //-- Save the preference name mPrefName = aPrefName; diff --git a/caps/src/nsScriptSecurityManager.cpp b/caps/src/nsScriptSecurityManager.cpp index bf2b8424a49..169edef4f24 100644 --- a/caps/src/nsScriptSecurityManager.cpp +++ b/caps/src/nsScriptSecurityManager.cpp @@ -517,6 +517,13 @@ NS_IMPL_ISUPPORTS4(nsScriptSecurityManager, /////////////////////////////////////////////////// ///////////////// Security Checks ///////////////// + +/* static */ JSPrincipals * +nsScriptSecurityManager::ObjectPrincipalFinder(JSObject *aObj) +{ + return nsJSPrincipals::get(doGetObjectPrincipal(aObj)); +} + JSBool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(JSContext *cx) { @@ -537,7 +544,7 @@ nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(JSContext *cx) if (!subjectPrincipal) { // See bug 553448 for discussion of this case. - NS_ASSERTION(!JS_GetSecurityCallbacks(cx)->findObjectPrincipals, + NS_ASSERTION(!JS_GetSecurityCallbacks(js::GetRuntime(cx))->findObjectPrincipals, "CSP: Should have been able to find subject principal. " "Reluctantly granting access."); return JS_TRUE; @@ -2179,11 +2186,7 @@ nsScriptSecurityManager::GetScriptPrincipal(JSContext *cx, NS_ERROR("Script compiled without principals!"); return nsnull; } - nsJSPrincipals *nsJSPrin = static_cast(jsp); - nsIPrincipal* result = nsJSPrin->nsIPrincipalPtr; - if (!result) - *rv = NS_ERROR_FAILURE; - return result; + return nsJSPrincipals::get(jsp); } // static @@ -3330,7 +3333,6 @@ nsScriptSecurityManager::nsScriptSecurityManager(void) mPrincipals.Init(31); } - nsresult nsScriptSecurityManager::Init() { nsXPConnect* xpconnect = nsXPConnect::GetXPConnect(); @@ -3365,10 +3367,6 @@ nsresult nsScriptSecurityManager::Init() nsRefPtr system = new nsSystemPrincipal(); NS_ENSURE_TRUE(system, NS_ERROR_OUT_OF_MEMORY); - JSPrincipals *jsprin; - rv = system->Init(&jsprin); - NS_ENSURE_SUCCESS(rv, rv); - mSystemPrincipal = system; //-- Register security check callback in the JS engine @@ -3380,20 +3378,19 @@ nsresult nsScriptSecurityManager::Init() rv = runtimeService->GetRuntime(&sRuntime); NS_ENSURE_SUCCESS(rv, rv); - static JSSecurityCallbacks securityCallbacks = { + static const JSSecurityCallbacks securityCallbacks = { CheckObjectAccess, - NULL, - NULL, + nsJSPrincipals::Subsume, + nsJSPrincipals::Transcode, + ObjectPrincipalFinder, ContentSecurityPolicyPermitsJSAction }; -#ifdef DEBUG - JSSecurityCallbacks *oldcallbacks = -#endif - JS_SetRuntimeSecurityCallbacks(sRuntime, &securityCallbacks); - NS_ASSERTION(!oldcallbacks, "Someone else set security callbacks!"); + MOZ_ASSERT(!JS_GetSecurityCallbacks(sRuntime)); + JS_SetSecurityCallbacks(sRuntime, &securityCallbacks); + JS_InitDestroyPrincipalsCallback(sRuntime, nsJSPrincipals::Destroy); - JS_SetTrustedPrincipals(sRuntime, jsprin); + JS_SetTrustedPrincipals(sRuntime, system); return NS_OK; } @@ -3417,7 +3414,7 @@ void nsScriptSecurityManager::Shutdown() { if (sRuntime) { - JS_SetRuntimeSecurityCallbacks(sRuntime, NULL); + JS_SetSecurityCallbacks(sRuntime, NULL); JS_SetTrustedPrincipals(sRuntime, NULL); sRuntime = nsnull; } @@ -3445,13 +3442,6 @@ nsScriptSecurityManager::GetScriptSecurityManager() return nsnull; } - rv = nsJSPrincipals::Startup(); - if (NS_FAILED(rv)) { - NS_WARNING("can't initialize JS engine security protocol glue!"); - delete ssManager; - return nsnull; - } - rv = sXPConnect->SetDefaultSecurityManager(ssManager, nsIXPCSecurityManager::HOOK_ALL); if (NS_FAILED(rv)) { diff --git a/caps/src/nsSystemPrincipal.cpp b/caps/src/nsSystemPrincipal.cpp index c223d07cfb6..2b3f5b499bc 100644 --- a/caps/src/nsSystemPrincipal.cpp +++ b/caps/src/nsSystemPrincipal.cpp @@ -62,8 +62,8 @@ NS_IMPL_CI_INTERFACE_GETTER2(nsSystemPrincipal, NS_IMETHODIMP_(nsrefcnt) nsSystemPrincipal::AddRef() { - NS_PRECONDITION(PRInt32(mJSPrincipals.refcount) >= 0, "illegal refcnt"); - nsrefcnt count = PR_ATOMIC_INCREMENT(&mJSPrincipals.refcount); + NS_PRECONDITION(PRInt32(refcount) >= 0, "illegal refcnt"); + nsrefcnt count = PR_ATOMIC_INCREMENT(&refcount); NS_LOG_ADDREF(this, count, "nsSystemPrincipal", sizeof(*this)); return count; } @@ -71,8 +71,8 @@ nsSystemPrincipal::AddRef() NS_IMETHODIMP_(nsrefcnt) nsSystemPrincipal::Release() { - NS_PRECONDITION(0 != mJSPrincipals.refcount, "dup release"); - nsrefcnt count = PR_ATOMIC_DECREMENT(&mJSPrincipals.refcount); + NS_PRECONDITION(0 != refcount, "dup release"); + nsrefcnt count = PR_ATOMIC_DECREMENT(&refcount); NS_LOG_RELEASE(this, count, "nsSystemPrincipal"); if (count == 0) { delete this; @@ -81,13 +81,26 @@ nsSystemPrincipal::Release() return count; } +static const char SYSTEM_PRINCIPAL_SPEC[] = "[System Principal]"; + +void +nsSystemPrincipal::GetScriptLocation(nsACString &aStr) +{ + aStr.Assign(SYSTEM_PRINCIPAL_SPEC); +} + +#ifdef DEBUG +void nsSystemPrincipal::dumpImpl() +{ + fprintf(stderr, "nsSystemPrincipal (%p)\n", this); +} +#endif + /////////////////////////////////////// // Methods implementing nsIPrincipal // /////////////////////////////////////// -#define SYSTEM_PRINCIPAL_SPEC "[System Principal]" - NS_IMETHODIMP nsSystemPrincipal::GetPreferences(char** aPrefName, char** aID, char** aSubjectName, @@ -280,16 +293,6 @@ nsSystemPrincipal::SetSecurityPolicy(void* aSecurityPolicy) return NS_OK; } -NS_IMETHODIMP -nsSystemPrincipal::GetJSPrincipals(JSContext *cx, JSPrincipals **jsprin) -{ - NS_PRECONDITION(mJSPrincipals.nsIPrincipalPtr, "mJSPrincipals is uninitialized!"); - - JSPRINCIPALS_HOLD(cx, &mJSPrincipals); - *jsprin = &mJSPrincipals; - return NS_OK; -} - ////////////////////////////////////////// // Methods implementing nsISerializable // @@ -317,24 +320,6 @@ nsSystemPrincipal::nsSystemPrincipal() { } -nsresult -nsSystemPrincipal::Init(JSPrincipals **jsprin) -{ - // Use an nsCString so we only do the allocation once here and then - // share with nsJSPrincipals - nsCString str(SYSTEM_PRINCIPAL_SPEC); - if (!str.EqualsLiteral(SYSTEM_PRINCIPAL_SPEC)) { - NS_WARNING("Out of memory initializing system principal"); - return NS_ERROR_OUT_OF_MEMORY; - } - - nsresult rv = mJSPrincipals.Init(this, str); - NS_ENSURE_SUCCESS(rv, rv); - - *jsprin = &mJSPrincipals; - return NS_OK; -} - -nsSystemPrincipal::~nsSystemPrincipal(void) +nsSystemPrincipal::~nsSystemPrincipal() { } diff --git a/config/autoconf.mk.in b/config/autoconf.mk.in index d0eeb0c7897..84233fbb2c9 100644 --- a/config/autoconf.mk.in +++ b/config/autoconf.mk.in @@ -301,6 +301,10 @@ MOZ_NATIVE_NSS = @MOZ_NATIVE_NSS@ MOZ_B2G_RIL = @MOZ_B2G_RIL@ MOZ_B2G_BT = @MOZ_B2G_BT@ +MOZ_ASAN = @MOZ_ASAN@ +MOZ_CFLAGS_NSS = @MOZ_CFLAGS_NSS@ +MOZ_NO_WLZDEFS = @MOZ_NO_WLZDEFS@ + BUILD_CTYPES = @BUILD_CTYPES@ COMPILE_ENVIRONMENT = @COMPILE_ENVIRONMENT@ diff --git a/config/config.mk b/config/config.mk index 21d37ea5bd1..a830bfe969b 100644 --- a/config/config.mk +++ b/config/config.mk @@ -789,8 +789,12 @@ EXPAND_LIBS_GEN = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config EXPAND_AR = $(EXPAND_LIBS_EXEC) --extract -- $(AR) EXPAND_CC = $(EXPAND_LIBS_EXEC) --uselist -- $(CC) EXPAND_CCC = $(EXPAND_LIBS_EXEC) --uselist -- $(CCC) -EXPAND_LD = $(EXPAND_LIBS_EXEC) --uselist $(if $(REORDER),--reorder $(REORDER))-- $(LD) -EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) --uselist $(if $(REORDER),--reorder $(REORDER))-- $(MKSHLIB) +EXPAND_LD = $(EXPAND_LIBS_EXEC) --uselist -- $(LD) +EXPAND_MKSHLIB_ARGS = --uselist +ifdef SYMBOL_ORDER +EXPAND_MKSHLIB_ARGS += --symbol-order $(SYMBOL_ORDER) +endif +EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) $(EXPAND_MKSHLIB_ARGS) -- $(MKSHLIB) ifdef STDCXX_COMPAT ifneq ($(OS_ARCH),Darwin) diff --git a/config/expandlibs_config.py.in b/config/expandlibs_config.py.in index e204bb9e85e..32a48ac5fd4 100644 --- a/config/expandlibs_config.py.in +++ b/config/expandlibs_config.py.in @@ -54,3 +54,5 @@ DLL_SUFFIX = normalize_suffix("@DLL_SUFFIX@") IMPORT_LIB_SUFFIX = normalize_suffix("@IMPORT_LIB_SUFFIX@") LIBS_DESC_SUFFIX = normalize_suffix("@LIBS_DESC_SUFFIX@") EXPAND_LIBS_LIST_STYLE = "@EXPAND_LIBS_LIST_STYLE@" +EXPAND_LIBS_ORDER_STYLE = "@EXPAND_LIBS_ORDER_STYLE@" +LD_PRINT_ICF_SECTIONS = "@LD_PRINT_ICF_SECTIONS@" diff --git a/config/expandlibs_exec.py b/config/expandlibs_exec.py index b6671cfdef7..afc0fc8fd60 100644 --- a/config/expandlibs_exec.py +++ b/config/expandlibs_exec.py @@ -49,9 +49,9 @@ EXPAND_LIBS_LIST_STYLE variable: 'list' for MSVC style lists (@file.list) or 'linkerscript' for GNU ld linker scripts. See https://bugzilla.mozilla.org/show_bug.cgi?id=584474#c59 for more details. -With the --reorder argument, followed by a file name, it will reorder the -object files from the command line according to the order given in the file. -Implies --extract. +With the --symbol-order argument, followed by a file name, it will add the +relevant linker options to change the order in which the linker puts the +symbols appear in the resulting binary. Only works for ELF targets. ''' from __future__ import with_statement import sys @@ -62,6 +62,18 @@ from optparse import OptionParser import subprocess import tempfile import shutil +import subprocess +import re + +# The are the insert points for a GNU ld linker script, assuming a more +# or less "standard" default linker script. This is not a dict because +# order is important. +SECTION_INSERT_BEFORE = [ + ('.text', '.fini'), + ('.rodata', '.rodata1'), + ('.data.rel.ro', '.dynamic'), + ('.data', '.data1'), +] class ExpandArgsMore(ExpandArgs): ''' Meant to be used as 'with ExpandArgsMore(args) as ...: ''' @@ -119,6 +131,7 @@ class ExpandArgsMore(ExpandArgs): content = ["%s\n" % obj for obj in objs] ref = "@" + tmp else: + os.close(fd) os.remove(tmp) return self.tmp.append(tmp) @@ -129,21 +142,163 @@ class ExpandArgsMore(ExpandArgs): newlist = self[0:idx] + [ref] + [item for item in self[idx:] if item not in objs] self[0:] = newlist - def reorder(self, order_list): - '''Given a list of file names without OBJ_SUFFIX, rearrange self - so that the object file names it contains are ordered according to - that list. - ''' - objs = [o for o in self if isObject(o)] - if not objs: return - idx = self.index(objs[0]) - # Keep everything before the first object, then the ordered objects, - # then any other objects, then any non-objects after the first object - objnames = dict([(os.path.splitext(os.path.basename(o))[0], o) for o in objs]) - self[0:] = self[0:idx] + [objnames[o] for o in order_list if o in objnames] + \ - [o for o in objs if os.path.splitext(os.path.basename(o))[0] not in order_list] + \ - [x for x in self[idx:] if not isObject(x)] + def _getFoldedSections(self): + '''Returns a dict about folded sections. + When section A and B are folded into section C, the dict contains: + { 'A': 'C', + 'B': 'C', + 'C': ['A', 'B'] }''' + if not conf.LD_PRINT_ICF_SECTIONS: + return {} + proc = subprocess.Popen(self + [conf.LD_PRINT_ICF_SECTIONS], stdout = subprocess.PIPE, stderr = subprocess.PIPE) + (stdout, stderr) = proc.communicate() + result = {} + # gold's --print-icf-sections output looks like the following: + # ld: ICF folding section '.section' in file 'file.o'into '.section' in file 'file.o' + # In terms of words, chances are this will change in the future, + # especially considering "into" is misplaced. Splitting on quotes + # seems safer. + for l in stderr.split('\n'): + quoted = l.split("'") + if len(quoted) > 5 and quoted[1] != quoted[5]: + result[quoted[1]] = quoted[5] + if quoted[5] in result: + result[quoted[5]].append(quoted[1]) + else: + result[quoted[5]] = [quoted[1]] + return result + + def _getOrderedSections(self, ordered_symbols): + '''Given an ordered list of symbols, returns the corresponding list + of sections following the order.''' + if not conf.EXPAND_LIBS_ORDER_STYLE in ['linkerscript', 'section-ordering-file']: + raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE) + finder = SectionFinder([arg for arg in self if isObject(arg) or os.path.splitext(arg)[1] == conf.LIB_SUFFIX]) + folded = self._getFoldedSections() + sections = set() + ordered_sections = [] + for symbol in ordered_symbols: + symbol_sections = finder.getSections(symbol) + all_symbol_sections = [] + for section in symbol_sections: + if section in folded: + if isinstance(folded[section], str): + section = folded[section] + all_symbol_sections.append(section) + all_symbol_sections.extend(folded[section]) + else: + all_symbol_sections.append(section) + for section in all_symbol_sections: + if not section in sections: + ordered_sections.append(section) + sections.add(section) + return ordered_sections + + def orderSymbols(self, order): + '''Given a file containing a list of symbols, adds the appropriate + argument to make the linker put the symbols in that order.''' + with open(order) as file: + sections = self._getOrderedSections([l.strip() for l in file.readlines() if l.strip()]) + split_sections = {} + linked_sections = [s[0] for s in SECTION_INSERT_BEFORE] + for s in sections: + for linked_section in linked_sections: + if s.startswith(linked_section): + if linked_section in split_sections: + split_sections[linked_section].append(s) + else: + split_sections[linked_section] = [s] + break + content = [] + # Order is important + linked_sections = [s for s in linked_sections if s in split_sections] + + if conf.EXPAND_LIBS_ORDER_STYLE == 'section-ordering-file': + option = '-Wl,--section-ordering-file,%s' + content = sections + for linked_section in linked_sections: + content.extend(split_sections[linked_section]) + content.append('%s.*' % linked_section) + content.append(linked_section) + + elif conf.EXPAND_LIBS_ORDER_STYLE == 'linkerscript': + option = '-Wl,-T,%s' + section_insert_before = dict(SECTION_INSERT_BEFORE) + for linked_section in linked_sections: + content.append('SECTIONS {') + content.append(' %s : {' % linked_section) + content.extend(' *(%s)' % s for s in split_sections[linked_section]) + content.append(' }') + content.append('}') + content.append('INSERT BEFORE %s' % section_insert_before[linked_section]) + else: + raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE) + + fd, tmp = tempfile.mkstemp(dir=os.curdir) + f = os.fdopen(fd, "w") + f.write('\n'.join(content)+'\n') + f.close() + self.tmp.append(tmp) + self.append(option % tmp) + +class SectionFinder(object): + '''Instances of this class allow to map symbol names to sections in + object files.''' + + def __init__(self, objs): + '''Creates an instance, given a list of object files.''' + if not conf.EXPAND_LIBS_ORDER_STYLE in ['linkerscript', 'section-ordering-file']: + raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE) + self.mapping = {} + for obj in objs: + if not isObject(obj) and os.path.splitext(obj)[1] != conf.LIB_SUFFIX: + raise Exception('%s is not an object nor a static library' % obj) + for symbol, section in SectionFinder._getSymbols(obj): + sym = SectionFinder._normalize(symbol) + if sym in self.mapping: + if not section in self.mapping[sym]: + self.mapping[sym].append(section) + else: + self.mapping[sym] = [section] + + def getSections(self, symbol): + '''Given a symbol, returns a list of sections containing it or the + corresponding thunks. When the given symbol is a thunk, returns the + list of sections containing its corresponding normal symbol and the + other thunks for that symbol.''' + sym = SectionFinder._normalize(symbol) + if sym in self.mapping: + return self.mapping[sym] + return [] + + @staticmethod + def _normalize(symbol): + '''For normal symbols, return the given symbol. For thunks, return + the corresponding normal symbol.''' + if re.match('^_ZThn[0-9]+_', symbol): + return re.sub('^_ZThn[0-9]+_', '_Z', symbol) + return symbol + + @staticmethod + def _getSymbols(obj): + '''Returns a list of (symbol, section) contained in the given object + file.''' + proc = subprocess.Popen(['objdump', '-t', obj], stdout = subprocess.PIPE, stderr = subprocess.PIPE) + (stdout, stderr) = proc.communicate() + syms = [] + for line in stdout.splitlines(): + # Each line has the following format: + # [lgu!][w ][C ][W ][Ii ][dD ][FfO ]
\t + tmp = line.split(' ',1) + # This gives us ["", "[lgu!][w ][C ][W ][Ii ][dD ][FfO ]
\t "] + # We only need to consider cases where "
\t " is present, + # and where the [FfO] flag is either F (function) or O (object). + if len(tmp) > 1 and len(tmp[1]) > 6 and tmp[1][6] in ['O', 'F']: + tmp = tmp[1][8:].split() + # That gives us ["
","", ""] + syms.append((tmp[-1], tmp[0])) + return syms def main(): parser = OptionParser() @@ -153,17 +308,16 @@ def main(): help="use a list file for objects when executing a command") parser.add_option("--verbose", action="store_true", dest="verbose", help="display executed command and temporary files content") - parser.add_option("--reorder", dest="reorder", - help="reorder the objects according to the given list", metavar="FILE") + parser.add_option("--symbol-order", dest="symbol_order", metavar="FILE", + help="use the given list of symbols to order symbols in the resulting binary when using with a linker") (options, args) = parser.parse_args() with ExpandArgsMore(args) as args: - if options.extract or options.reorder: + if options.extract: args.extract() - if options.reorder: - with open(options.reorder) as file: - args.reorder([l.strip() for l in file.readlines()]) + if options.symbol_order: + args.orderSymbols(options.symbol_order) if options.uselist: args.makelist() diff --git a/config/tests/unit-expandlibs.py b/config/tests/unit-expandlibs.py index 0582d70c05d..491c95448b1 100644 --- a/config/tests/unit-expandlibs.py +++ b/config/tests/unit-expandlibs.py @@ -38,7 +38,7 @@ config = sys.modules['expandlibs_config'] = imp.new_module('expandlibs_config') from expandlibs import LibDescriptor, ExpandArgs, relativize from expandlibs_gen import generate -from expandlibs_exec import ExpandArgsMore +from expandlibs_exec import ExpandArgsMore, SectionFinder def Lib(name): return config.LIB_PREFIX + name + config.LIB_SUFFIX @@ -267,28 +267,101 @@ class TestExpandArgsMore(TestExpandInit): # Restore subprocess.call subprocess.call = subprocess_call - def test_reorder(self): - '''Test object reordering''' - # We don't care about AR_EXTRACT testing, which is done in test_extract - config.AR_EXTRACT = '' +class FakeProcess(object): + def __init__(self, out, err = ''): + self.out = out + self.err = err - # ExpandArgsMore does the same as ExpandArgs - with ExpandArgsMore(['foo', '-bar'] + self.arg_files + [self.tmpfile('liby', Lib('y'))]) as args: - self.assertRelEqual(args, ['foo', '-bar'] + self.files + self.liby_files + self.libx_files) + def communicate(self): + return (self.out, self.err) - # Use an order containing object files from libraries - order_files = [self.libx_files[1], self.libx_files[0], self.liby_files[2], self.files[1]] - order = [os.path.splitext(os.path.basename(f))[0] for f in order_files] - args.reorder(order[:2] + ['unknown'] + order[2:]) +OBJDUMPS = { +'foo.o': ''' +00000000 g F .text\t00000001 foo +00000000 g F .text._Z6foobarv\t00000001 _Z6foobarv +00000000 g F .text.hello\t00000001 hello +00000000 g F .text._ZThn4_6foobarv\t00000001 _ZThn4_6foobarv +''', +'bar.o': ''' +00000000 g F .text.hi\t00000001 hi +00000000 g F .text.hot._Z6barbazv\t00000001 .hidden _Z6barbazv +''', +} + +PRINT_ICF = ''' +ld: ICF folding section '.text.hello' in file 'foo.o'into '.text.hi' in file 'bar.o' +ld: ICF folding section '.foo' in file 'foo.o'into '.foo' in file 'bar.o' +''' + +class SubprocessPopen(object): + def __init__(self, test): + self.test = test + + def __call__(self, args, stdout = None, stderr = None): + self.test.assertEqual(stdout, subprocess.PIPE) + self.test.assertEqual(stderr, subprocess.PIPE) + if args[0] == 'objdump': + self.test.assertEqual(args[1], '-t') + self.test.assertTrue(args[2] in OBJDUMPS) + return FakeProcess(OBJDUMPS[args[2]]) + else: + return FakeProcess('', PRINT_ICF) + +class TestSectionFinder(unittest.TestCase): + def test_getSections(self): + '''Test SectionFinder''' + # Divert subprocess.Popen + subprocess_popen = subprocess.Popen + subprocess.Popen = SubprocessPopen(self) + config.EXPAND_LIBS_ORDER_STYLE = 'linkerscript' + config.OBJ_SUFFIX = '.o' + config.LIB_SUFFIX = '.a' + finder = SectionFinder(['foo.o', 'bar.o']) + self.assertEqual(finder.getSections('foobar'), []) + self.assertEqual(finder.getSections('_Z6barbazv'), ['.text.hot._Z6barbazv']) + self.assertEqual(finder.getSections('_Z6foobarv'), ['.text._Z6foobarv', '.text._ZThn4_6foobarv']) + self.assertEqual(finder.getSections('_ZThn4_6foobarv'), ['.text._Z6foobarv', '.text._ZThn4_6foobarv']) + subprocess.Popen = subprocess_popen + +class TestSymbolOrder(unittest.TestCase): + def test_getOrderedSections(self): + '''Test ExpandMoreArgs' _getOrderedSections''' + # Divert subprocess.Popen + subprocess_popen = subprocess.Popen + subprocess.Popen = SubprocessPopen(self) + config.EXPAND_LIBS_ORDER_STYLE = 'linkerscript' + config.OBJ_SUFFIX = '.o' + config.LIB_SUFFIX = '.a' + config.LD_PRINT_ICF_SECTIONS = '' + args = ExpandArgsMore(['foo', '-bar', 'bar.o', 'foo.o']) + self.assertEqual(args._getOrderedSections(['_Z6foobarv', '_Z6barbazv']), ['.text._Z6foobarv', '.text._ZThn4_6foobarv', '.text.hot._Z6barbazv']) + self.assertEqual(args._getOrderedSections(['_ZThn4_6foobarv', '_Z6barbazv']), ['.text._Z6foobarv', '.text._ZThn4_6foobarv', '.text.hot._Z6barbazv']) + subprocess.Popen = subprocess_popen + + def test_getFoldedSections(self): + '''Test ExpandMoreArgs' _getFoldedSections''' + # Divert subprocess.Popen + subprocess_popen = subprocess.Popen + subprocess.Popen = SubprocessPopen(self) + config.LD_PRINT_ICF_SECTIONS = '-Wl,--print-icf-sections' + args = ExpandArgsMore(['foo', '-bar', 'bar.o', 'foo.o']) + self.assertEqual(args._getFoldedSections(), {'.text.hello': '.text.hi', '.text.hi': ['.text.hello']}) + subprocess.Popen = subprocess_popen + + def test_getOrderedSectionsWithICF(self): + '''Test ExpandMoreArgs' _getOrderedSections, with ICF''' + # Divert subprocess.Popen + subprocess_popen = subprocess.Popen + subprocess.Popen = SubprocessPopen(self) + config.EXPAND_LIBS_ORDER_STYLE = 'linkerscript' + config.OBJ_SUFFIX = '.o' + config.LIB_SUFFIX = '.a' + config.LD_PRINT_ICF_SECTIONS = '-Wl,--print-icf-sections' + args = ExpandArgsMore(['foo', '-bar', 'bar.o', 'foo.o']) + self.assertEqual(args._getOrderedSections(['hello', '_Z6barbazv']), ['.text.hi', '.text.hello', '.text.hot._Z6barbazv']) + self.assertEqual(args._getOrderedSections(['_ZThn4_6foobarv', 'hi', '_Z6barbazv']), ['.text._Z6foobarv', '.text._ZThn4_6foobarv', '.text.hi', '.text.hello', '.text.hot._Z6barbazv']) + subprocess.Popen = subprocess_popen - # self.files has objects at #1, #2, #4 - self.assertRelEqual(args[:3], ['foo', '-bar'] + self.files[:1]) - self.assertRelEqual(args[3:7], order_files) - self.assertRelEqual(args[7:9], [self.files[2], self.files[4]]) - self.assertRelEqual(args[9:11], self.liby_files[:2]) - self.assertRelEqual(args[11:12], [self.libx_files[2]]) - self.assertRelEqual(args[12:14], [self.files[3], self.files[5]]) - self.assertRelEqual(args[14:], [self.liby_files[3]]) if __name__ == '__main__': unittest.main(testRunner=MozTestRunner()) diff --git a/configure.in b/configure.in index 091841e731d..d1c61263d28 100644 --- a/configure.in +++ b/configure.in @@ -1837,6 +1837,33 @@ if test -n "$CLANG_CXX"; then _WARNINGS_CXXFLAGS="-Qunused-arguments ${_WARNINGS_CXXFLAGS}" fi +dnl ======================================================== +dnl = Use Address Sanitizer +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(address-sanitizer, +[ --enable-address-sanitizer Enable Address Sanitizer (default=no)], + MOZ_ASAN=1, + MOZ_ASAN= ) +if test -n "$MOZ_ASAN"; then + MOZ_LLVM_HACKS=1 + AC_DEFINE(MOZ_ASAN) +fi +AC_SUBST(MOZ_ASAN) + +dnl ======================================================== +dnl = Enable hacks required for LLVM instrumentations +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(llvm-hacks, +[ --enable-llvm-hacks Enable workarounds required for several LLVM instrumentations (default=no)], + MOZ_LLVM_HACKS=1, + MOZ_LLVM_HACKS= ) +if test -n "$MOZ_LLVM_HACKS"; then + MOZ_NO_WLZDEFS=1 + MOZ_CFLAGS_NSS=1 +fi +AC_SUBST(MOZ_NO_WLZDEFS) +AC_SUBST(MOZ_CFLAGS_NSS) + dnl ======================================================== dnl GNU specific defaults dnl ======================================================== @@ -1847,8 +1874,13 @@ if test "$GNU_CC"; then MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@' DSO_LDOPTS='-shared' if test "$GCC_USE_GNU_LD"; then - # Don't allow undefined symbols in libraries - DSO_LDOPTS="$DSO_LDOPTS -Wl,-z,defs" + # Some tools like ASan use a runtime library that is only + # linked against executables, so we must allow undefined + # symbols for shared objects in some cases. + if test -z "$MOZ_NO_WLZDEFS"; then + # Don't allow undefined symbols in libraries + DSO_LDOPTS="$DSO_LDOPTS -Wl,-z,defs" + fi fi WARNINGS_AS_ERRORS='-Werror -Wno-error=uninitialized' DSO_CFLAGS='' @@ -2492,6 +2524,8 @@ ia64*-hpux*) no_x=yes if test -n "$gonkdir"; then _PLATFORM_DEFAULT_TOOLKIT=cairo-gonk + MOZ_B2G_RIL=1 + MOZ_B2G_BT=1 else _PLATFORM_DEFAULT_TOOLKIT=cairo-android MOZ_LINKER=1 @@ -5006,7 +5040,6 @@ cairo-gonk) TK_LIBS='$(MOZ_CAIRO_LIBS)' MOZ_WEBGL=1 MOZ_PDF_PRINTING=1 - MOZ_B2G_RIL=1 MOZ_TOUCH=1 ;; @@ -7147,78 +7180,6 @@ if test -n "$MOZ_DEBUG" -o -n "$MOZ_DEBUG_SYMBOLS"; then export MOZ_DEBUG_SYMBOLS fi -dnl ======================================================== -dnl = Identical Code Folding -dnl ======================================================== - -MOZ_ARG_DISABLE_BOOL(icf, -[ --disable-icf Disable Identical Code Folding], - MOZ_DISABLE_ICF=1, - MOZ_DISABLE_ICF= ) - -if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -z "$MOZ_DISABLE_ICF"; then - AC_CACHE_CHECK([whether the linker supports Identical Code Folding], - LD_SUPPORTS_ICF, - [echo 'int foo() {return 42;}' \ - 'int bar() {return 42;}' \ - 'int main() {return foo() - bar();}' > conftest.${ac_ext} - # If the linker supports ICF, foo and bar symbols will have - # the same address - if AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS -Wl,--icf=safe -ffunction-sections conftest.${ac_ext} $LIBS 1>&2]) && - test -s conftest${ac_exeext} && - objdump -t conftest${ac_exeext} | awk '{a[[$6]] = $1} END {if (a[["foo"]] && (a[["foo"]] != a[["bar"]])) { exit 1 }}'; then - LD_SUPPORTS_ICF=yes - else - LD_SUPPORTS_ICF=no - fi - rm -rf conftest*]) - if test "$LD_SUPPORTS_ICF" = yes; then - LDFLAGS="$LDFLAGS -Wl,--icf=safe" - CFLAGS="$CFLAGS -ffunction-sections" - CXXFLAGS="$CXXFLAGS -ffunction-sections" - fi -fi - -dnl ======================================================== -dnl = Automatically remove dead symbols -dnl ======================================================== - -if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -n "$MOZ_DEBUG_FLAGS"; then - dnl See bug 670659 - AC_CACHE_CHECK([whether removing dead symbols breaks debugging], - GC_SECTIONS_BREAKS_DEBUG_RANGES, - [echo 'int foo() {return 42;}' \ - 'int bar() {return 1;}' \ - 'int main() {return foo();}' > conftest.${ac_ext} - if AC_TRY_COMMAND([${CC-cc} -o conftest.${ac_objext} $CFLAGS $MOZ_DEBUG_FLAGS -ffunction-sections -c conftest.${ac_ext} 1>&2]) && - AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS $MOZ_DEBUG_FLAGS -Wl,--gc-sections conftest.${ac_objext} $LIBS 1>&2]) && - test -s conftest${ac_exeext} -a -s conftest.${ac_objext}; then - if test "`$PYTHON "$_topsrcdir"/build/unix/check_debug_ranges.py conftest.${ac_objext} conftest.${ac_ext}`" = \ - "`$PYTHON "$_topsrcdir"/build/unix/check_debug_ranges.py conftest${ac_exeext} conftest.${ac_ext}`"; then - GC_SECTIONS_BREAKS_DEBUG_RANGES=no - else - GC_SECTIONS_BREAKS_DEBUG_RANGES=yes - fi - else - dnl We really don't expect to get here, but just in case - GC_SECTIONS_BREAKS_DEBUG_RANGES="no, but it's broken in some other way" - fi - rm -rf conftest*]) - if test "$GC_SECTIONS_BREAKS_DEBUG_RANGES" = no; then - DSO_LDOPTS="$DSO_LDOPTS -Wl,--gc-sections" - case "$CFLAGS" in - *-ffunction-sections*) - CFLAGS="$CFLAGS -fdata-sections" - CXXFLAGS="$CXXFLAGS -fdata-sections" - ;; - *) - CFLAGS="$CFLAGS -ffunction-sections -fdata-sections" - CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections" - ;; - esac - fi -fi - dnl ======================================================== dnl = Enable any treating of compile warnings as errors dnl ======================================================== @@ -8040,37 +8001,7 @@ fi # ! SKIP_COMPILER_CHECKS AC_DEFINE(CPP_THROW_NEW, [throw()]) AC_LANG_C -dnl ======================================================== -dnl = -dnl = Check what kind of list files are supported by the -dnl = linker -dnl = -dnl ======================================================== - -AC_CACHE_CHECK(what kind of list files are supported by the linker, - EXPAND_LIBS_LIST_STYLE, - [echo "int main() {return 0;}" > conftest.${ac_ext} - if AC_TRY_COMMAND(${CC-cc} -o conftest.${OBJ_SUFFIX} -c $CFLAGS $CPPFLAGS conftest.${ac_ext} 1>&2) && test -s conftest.${OBJ_SUFFIX}; then - echo "INPUT(conftest.${OBJ_SUFFIX})" > conftest.list - if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS conftest.list $LIBS 1>&2) && test -s conftest${ac_exeext}; then - EXPAND_LIBS_LIST_STYLE=linkerscript - else - echo "conftest.${OBJ_SUFFIX}" > conftest.list - if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS @conftest.list $LIBS 1>&2) && test -s conftest${ac_exeext}; then - EXPAND_LIBS_LIST_STYLE=list - else - EXPAND_LIBS_LIST_STYLE=none - fi - fi - else - dnl We really don't expect to get here, but just in case - AC_ERROR([couldn't compile a simple C file]) - fi - rm -rf conftest*]) - -LIBS_DESC_SUFFIX=desc -AC_SUBST(LIBS_DESC_SUFFIX) -AC_SUBST(EXPAND_LIBS_LIST_STYLE) +MOZ_EXPAND_LIBS dnl ======================================================== dnl = diff --git a/content/base/public/Makefile.in b/content/base/public/Makefile.in index 5a8e717f0c4..3a9f08e5275 100644 --- a/content/base/public/Makefile.in +++ b/content/base/public/Makefile.in @@ -65,7 +65,6 @@ nsINodeList.h \ nsIScriptElement.h \ nsIStyleSheetLinkingElement.h \ nsIContentSerializer.h \ -nsIHTMLToTextSink.h \ nsIXPathEvaluatorInternal.h \ mozISanitizingSerializer.h \ nsCaseTreatment.h \ diff --git a/content/base/public/nsContentUtils.h b/content/base/public/nsContentUtils.h index 89fe3a6e400..4fdfaad807a 100644 --- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -441,6 +441,16 @@ public: */ static bool IsHTMLWhitespace(PRUnichar aChar); + /** + * Is the HTML local name a block element? + */ + static bool IsHTMLBlock(nsIAtom* aLocalName); + + /** + * Is the HTML local name a void element? + */ + static bool IsHTMLVoid(nsIAtom* aLocalName); + /** * Parse a margin string of format 'top, right, bottom, left' into * an nsIntMargin. @@ -838,11 +848,22 @@ public: * Fill (with the parameters given) the localized string named |aKey| in * properties file |aFile|. */ +private: static nsresult FormatLocalizedString(PropertiesFile aFile, const char* aKey, - const PRUnichar **aParams, + const PRUnichar** aParams, PRUint32 aParamsLength, nsXPIDLString& aResult); + +public: + template + static nsresult FormatLocalizedString(PropertiesFile aFile, + const char* aKey, + const PRUnichar* (&aParams)[N], + nsXPIDLString& aResult) + { + return FormatLocalizedString(aFile, aKey, aParams, N, aResult); + } /** * Returns true if aDocument is a chrome document diff --git a/content/base/public/nsIHTMLToTextSink.h b/content/base/public/nsIHTMLToTextSink.h deleted file mode 100644 index 13c7c9d6dae..00000000000 --- a/content/base/public/nsIHTMLToTextSink.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef _nsIPlainTextSink_h__ -#define _nsIPlainTextSink_h__ - -#include "nsISupports.h" -#include "nsStringGlue.h" - -#define NS_PLAINTEXTSINK_CONTRACTID "@mozilla.org/layout/plaintextsink;1" - -/* starting interface: nsIContentSerializer */ -#define NS_IHTMLTOTEXTSINK_IID_STR "b12b5643-07cb-401e-aabb-64b2dcd2717f" - -#define NS_IHTMLTOTEXTSINK_IID \ - {0xb12b5643, 0x07cb, 0x401e, \ - { 0xaa, 0xbb, 0x64, 0xb2, 0xdc, 0xd2, 0x71, 0x7f }} - - -class nsIHTMLToTextSink : public nsISupports { - public: - - NS_DECLARE_STATIC_IID_ACCESSOR(NS_IHTMLTOTEXTSINK_IID) - - NS_IMETHOD Initialize(nsAString* aOutString, - PRUint32 aFlags, PRUint32 aWrapCol) = 0; - // This function violates string ownership rules, see impl. -}; - -NS_DEFINE_STATIC_IID_ACCESSOR(nsIHTMLToTextSink, NS_IHTMLTOTEXTSINK_IID) - -#endif diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 9c103340543..3c764c9c32f 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -1190,6 +1190,72 @@ nsContentUtils::IsHTMLWhitespace(PRUnichar aChar) aChar == PRUnichar(0x0020); } +/* static */ +bool +nsContentUtils::IsHTMLBlock(nsIAtom* aLocalName) +{ + return + (aLocalName == nsGkAtoms::address) || + (aLocalName == nsGkAtoms::article) || + (aLocalName == nsGkAtoms::aside) || + (aLocalName == nsGkAtoms::blockquote) || + (aLocalName == nsGkAtoms::center) || + (aLocalName == nsGkAtoms::dir) || + (aLocalName == nsGkAtoms::div) || + (aLocalName == nsGkAtoms::dl) || // XXX why not dt and dd? + (aLocalName == nsGkAtoms::fieldset) || + (aLocalName == nsGkAtoms::figure) || // XXX shouldn't figcaption be on this list + (aLocalName == nsGkAtoms::footer) || + (aLocalName == nsGkAtoms::form) || + (aLocalName == nsGkAtoms::h1) || + (aLocalName == nsGkAtoms::h2) || + (aLocalName == nsGkAtoms::h3) || + (aLocalName == nsGkAtoms::h4) || + (aLocalName == nsGkAtoms::h5) || + (aLocalName == nsGkAtoms::h6) || + (aLocalName == nsGkAtoms::header) || + (aLocalName == nsGkAtoms::hgroup) || + (aLocalName == nsGkAtoms::hr) || + (aLocalName == nsGkAtoms::li) || + (aLocalName == nsGkAtoms::listing) || + (aLocalName == nsGkAtoms::menu) || + (aLocalName == nsGkAtoms::multicol) || // XXX get rid of this one? + (aLocalName == nsGkAtoms::nav) || + (aLocalName == nsGkAtoms::ol) || + (aLocalName == nsGkAtoms::p) || + (aLocalName == nsGkAtoms::pre) || + (aLocalName == nsGkAtoms::section) || + (aLocalName == nsGkAtoms::table) || + (aLocalName == nsGkAtoms::ul) || + (aLocalName == nsGkAtoms::xmp); +} + +/* static */ +bool +nsContentUtils::IsHTMLVoid(nsIAtom* aLocalName) +{ + return + (aLocalName == nsGkAtoms::area) || + (aLocalName == nsGkAtoms::base) || + (aLocalName == nsGkAtoms::basefont) || + (aLocalName == nsGkAtoms::bgsound) || + (aLocalName == nsGkAtoms::br) || + (aLocalName == nsGkAtoms::col) || + (aLocalName == nsGkAtoms::command) || + (aLocalName == nsGkAtoms::embed) || + (aLocalName == nsGkAtoms::frame) || + (aLocalName == nsGkAtoms::hr) || + (aLocalName == nsGkAtoms::img) || + (aLocalName == nsGkAtoms::input) || + (aLocalName == nsGkAtoms::keygen) || + (aLocalName == nsGkAtoms::link) || + (aLocalName == nsGkAtoms::meta) || + (aLocalName == nsGkAtoms::param) || + (aLocalName == nsGkAtoms::source) || + (aLocalName == nsGkAtoms::track) || + (aLocalName == nsGkAtoms::wbr); +} + /* static */ bool nsContentUtils::ParseIntMarginValue(const nsAString& aString, nsIntMargin& result) diff --git a/content/base/src/nsFrameMessageManager.cpp b/content/base/src/nsFrameMessageManager.cpp index 179d9af871e..7cc0d44a874 100644 --- a/content/base/src/nsFrameMessageManager.cpp +++ b/content/base/src/nsFrameMessageManager.cpp @@ -43,6 +43,7 @@ #include "nsIXPConnect.h" #include "jsapi.h" #include "nsJSUtils.h" +#include "nsJSPrincipals.h" #include "nsNetUtil.h" #include "nsScriptLoader.h" #include "nsIJSContextStack.h" @@ -811,15 +812,13 @@ nsFrameScriptExecutor::LoadFrameScriptInternal(const nsAString& aURL) JSObject* global = nsnull; mGlobal->GetJSObject(&global); if (global) { - JSPrincipals* jsprin = nsnull; - mPrincipal->GetJSPrincipals(mCx, &jsprin); - uint32 oldopts = JS_GetOptions(mCx); JS_SetOptions(mCx, oldopts | JSOPTION_NO_SCRIPT_RVAL); JSScript* script = - JS_CompileUCScriptForPrincipals(mCx, nsnull, jsprin, - (jschar*)dataString.get(), + JS_CompileUCScriptForPrincipals(mCx, nsnull, + nsJSPrincipals::get(mPrincipal), + static_cast(dataString.get()), dataString.Length(), url.get(), 1); @@ -839,8 +838,6 @@ nsFrameScriptExecutor::LoadFrameScriptInternal(const nsAString& aURL) } (void) JS_ExecuteScript(mCx, global, script, nsnull); } - //XXX Argh, JSPrincipals are manually refcounted! - JSPRINCIPALS_DROP(mCx, jsprin); } } JSContext* unused; diff --git a/content/base/src/nsGenericElement.cpp b/content/base/src/nsGenericElement.cpp index e03df4da2b1..88f1167a93d 100644 --- a/content/base/src/nsGenericElement.cpp +++ b/content/base/src/nsGenericElement.cpp @@ -2736,6 +2736,9 @@ nsGenericElement::RemoveAttribute(const nsAString& aName) const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName); if (!name) { + // If there is no canonical nsAttrName for this attribute name, then the + // attribute does not exist and we can't get its namespace ID and + // local name below, so we return early. return NS_OK; } @@ -2889,8 +2892,9 @@ nsGenericElement::RemoveAttributeNS(const nsAString& aNamespaceURI, nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI); if (nsid == kNameSpaceID_Unknown) { - // Unknown namespace means no attr... - + // If the namespace ID is unknown, it means there can't possibly be an + // existing attribute. We would need a known namespace ID to pass into + // UnsetAttr, so we return early if we don't have one. return NS_OK; } diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index 1208809ffad..18578f3a162 100644 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -142,6 +142,7 @@ GK_ATOM(before_end, "before_end") GK_ATOM(before_start, "before_start") GK_ATOM(below, "below") GK_ATOM(bgcolor, "bgcolor") +GK_ATOM(bgsound, "bgsound") GK_ATOM(big, "big") GK_ATOM(binding, "binding") GK_ATOM(bindings, "bindings") @@ -493,6 +494,7 @@ GK_ATOM(keepcurrentinview, "keepcurrentinview") GK_ATOM(key, "key") GK_ATOM(keycode, "keycode") GK_ATOM(keydown, "keydown") +GK_ATOM(keygen, "keygen") GK_ATOM(keypress, "keypress") GK_ATOM(keyset, "keyset") GK_ATOM(keytext, "keytext") diff --git a/content/base/src/nsGkAtoms.cpp b/content/base/src/nsGkAtoms.cpp index c0c70357f01..30294e21069 100644 --- a/content/base/src/nsGkAtoms.cpp +++ b/content/base/src/nsGkAtoms.cpp @@ -65,6 +65,6 @@ static const nsStaticAtom GkAtoms_info[] = { void nsGkAtoms::AddRefAtoms() { - NS_RegisterStaticAtoms(GkAtoms_info, ArrayLength(GkAtoms_info)); + NS_RegisterStaticAtoms(GkAtoms_info); } diff --git a/content/base/src/nsPlainTextSerializer.cpp b/content/base/src/nsPlainTextSerializer.cpp index c6799f73032..27ccb56fa27 100644 --- a/content/base/src/nsPlainTextSerializer.cpp +++ b/content/base/src/nsPlainTextSerializer.cpp @@ -53,7 +53,6 @@ #include "nsReadableUtils.h" #include "nsUnicharUtils.h" #include "nsCRT.h" -#include "nsIParserService.h" #include "mozilla/dom/Element.h" #include "mozilla/Preferences.h" @@ -79,7 +78,7 @@ static const PRInt32 kIndentSizeDD = kTabSize; // Indention of
static const PRUnichar kNBSP = 160; static const PRUnichar kSPACE = ' '; -static PRInt32 HeaderLevel(eHTMLTags aTag); +static PRInt32 HeaderLevel(nsIAtom* aTag); static PRInt32 GetUnicharWidth(PRUnichar ucs); static PRInt32 GetUnicharStringWidth(const PRUnichar* pwcs, PRInt32 n); @@ -126,7 +125,9 @@ nsPlainTextSerializer::nsPlainTextSerializer() mStartedOutput = false; // initialize the tag stack to zero: - mTagStack = new nsHTMLTag[TagStackSize]; + // The stack only ever contains pointers to static atoms, so they don't + // need refcounting. + mTagStack = new nsIAtom*[TagStackSize]; mTagStackIndex = 0; mIgnoreAboveIndex = (PRUint32)kNotFound; @@ -144,11 +145,8 @@ nsPlainTextSerializer::~nsPlainTextSerializer() NS_WARN_IF_FALSE(mHeadLevel == 0, "Wrong head level!"); } -NS_IMPL_ISUPPORTS4(nsPlainTextSerializer, - nsIContentSerializer, - nsIContentSink, - nsIHTMLContentSink, - nsIHTMLToTextSink) +NS_IMPL_ISUPPORTS1(nsPlainTextSerializer, + nsIContentSerializer) NS_IMETHODIMP @@ -158,20 +156,18 @@ nsPlainTextSerializer::Init(PRUint32 aFlags, PRUint32 aWrapColumn, { #ifdef DEBUG // Check if the major control flags are set correctly. - if(aFlags & nsIDocumentEncoder::OutputFormatFlowed) { + if (aFlags & nsIDocumentEncoder::OutputFormatFlowed) { NS_ASSERTION(aFlags & nsIDocumentEncoder::OutputFormatted, "If you want format=flowed, you must combine it with " "nsIDocumentEncoder::OutputFormatted"); } - if(aFlags & nsIDocumentEncoder::OutputFormatted) { + if (aFlags & nsIDocumentEncoder::OutputFormatted) { NS_ASSERTION(!(aFlags & nsIDocumentEncoder::OutputPreformatted), "Can't do formatted and preformatted output at the same time!"); } #endif - NS_ENSURE_TRUE(nsContentUtils::GetParserService(), NS_ERROR_UNEXPECTED); - mFlags = aFlags; mWrapColumn = aWrapColumn; @@ -270,21 +266,6 @@ nsPlainTextSerializer::PopBool(nsTArray& aStack) return returnValue; } -NS_IMETHODIMP -nsPlainTextSerializer::Initialize(nsAString* aOutString, - PRUint32 aFlags, PRUint32 aWrapCol) -{ - nsresult rv = Init(aFlags, aWrapCol, nsnull, false, false); - NS_ENSURE_SUCCESS(rv, rv); - - // XXX This is wrong. It violates XPCOM string ownership rules. - // We're only getting away with this because instances of this - // class are restricted to single function scope. - mOutputString = aOutString; - - return NS_OK; -} - NS_IMETHODIMP nsPlainTextSerializer::AppendText(nsIContent* aText, PRInt32 aStartOffset, @@ -335,31 +316,26 @@ nsPlainTextSerializer::AppendText(nsIContent* aText, PRInt32 offset = textstr.FindCharInSet("\n\r"); while (offset != kNotFound) { - if(offset>start) { + if (offset>start) { // Pass in the line - rv = DoAddLeaf(nsnull, - eHTMLTag_text, - Substring(textstr, start, offset-start)); - if (NS_FAILED(rv)) break; + DoAddText(false, + Substring(textstr, start, offset-start)); } // Pass in a newline - rv = DoAddLeaf(nsnull, eHTMLTag_newline, mLineBreak); - if (NS_FAILED(rv)) break; + DoAddText(true, mLineBreak); start = offset+1; offset = textstr.FindCharInSet("\n\r", start); } // Consume the last bit of the string if there's any left - if (NS_SUCCEEDED(rv) && start < length) { + if (start < length) { if (start) { - rv = DoAddLeaf(nsnull, - eHTMLTag_text, - Substring(textstr, start, length-start)); + DoAddText(false, Substring(textstr, start, length - start)); } else { - rv = DoAddLeaf(nsnull, eHTMLTag_text, textstr); + DoAddText(false, textstr); } } @@ -387,23 +363,23 @@ nsPlainTextSerializer::AppendElementStart(Element* aElement, mElement = aElement; nsresult rv; - PRInt32 id = GetIdForContent(mElement); + nsIAtom* id = GetIdForContent(mElement); - bool isContainer = IsContainer(id); + bool isContainer = !nsContentUtils::IsHTMLVoid(id); mOutputString = &aStr; if (isContainer) { - rv = DoOpenContainer(nsnull, id); + rv = DoOpenContainer(id); } else { - rv = DoAddLeaf(nsnull, id, EmptyString()); + rv = DoAddLeaf(id); } mElement = nsnull; mOutputString = nsnull; - if (id == eHTMLTag_head) { + if (id == nsGkAtoms::head) { ++mHeadLevel; } @@ -419,9 +395,9 @@ nsPlainTextSerializer::AppendElementEnd(Element* aElement, mElement = aElement; nsresult rv; - PRInt32 id = GetIdForContent(mElement); + nsIAtom* id = GetIdForContent(mElement); - bool isContainer = IsContainer(id); + bool isContainer = !nsContentUtils::IsHTMLVoid(id); mOutputString = &aStr; @@ -433,7 +409,7 @@ nsPlainTextSerializer::AppendElementEnd(Element* aElement, mElement = nsnull; mOutputString = nsnull; - if (id == eHTMLTag_head) { + if (id == nsGkAtoms::head) { --mHeadLevel; NS_ASSERTION(mHeadLevel >= 0, "mHeadLevel < 0"); } @@ -457,89 +433,8 @@ nsPlainTextSerializer::AppendDocumentStart(nsIDocument *aDocument, return NS_OK; } -NS_IMETHODIMP -nsPlainTextSerializer::OpenContainer(const nsIParserNode& aNode) -{ - PRInt32 type = aNode.GetNodeType(); - - if (type == eHTMLTag_head) { - ++mHeadLevel; - return NS_OK; - } - - return DoOpenContainer(&aNode, type); -} - -NS_IMETHODIMP -nsPlainTextSerializer::CloseContainer(const nsHTMLTag aTag) -{ - if (aTag == eHTMLTag_head) { - --mHeadLevel; - NS_ASSERTION(mHeadLevel >= 0, "mHeadLevel < 0"); - return NS_OK; - } - - return DoCloseContainer(aTag); -} - -NS_IMETHODIMP -nsPlainTextSerializer::AddLeaf(const nsIParserNode& aNode) -{ - if (mIgnoreAboveIndex != (PRUint32)kNotFound) { - return NS_OK; - } - - eHTMLTags type = (eHTMLTags)aNode.GetNodeType(); - const nsAString& text = aNode.GetText(); - - if ((type == eHTMLTag_text) || - (type == eHTMLTag_whitespace) || - (type == eHTMLTag_newline)) { - // Copy the text out, stripping out CRs - nsAutoString str; - PRUint32 length; - str.SetCapacity(text.Length()); - nsReadingIterator srcStart, srcEnd; - length = nsContentUtils::CopyNewlineNormalizedUnicodeTo(text.BeginReading(srcStart), text.EndReading(srcEnd), str); - str.SetLength(length); - return DoAddLeaf(&aNode, type, str); - } - else { - return DoAddLeaf(&aNode, type, text); - } -} - -NS_IMETHODIMP -nsPlainTextSerializer::OpenHead() -{ - ++mHeadLevel; - return NS_OK; -} - -NS_IMETHODIMP -nsPlainTextSerializer::IsEnabled(PRInt32 aTag, bool* aReturn) -{ - nsHTMLTag theHTMLTag = nsHTMLTag(aTag); - - if (theHTMLTag == eHTMLTag_script) { - *aReturn = !(mFlags & nsIDocumentEncoder::OutputNoScriptContent); - } - else if (theHTMLTag == eHTMLTag_frameset) { - *aReturn = !(mFlags & nsIDocumentEncoder::OutputNoFramesContent); - } - else { - *aReturn = false; - } - - return NS_OK; -} - -/** - * aNode may be null when we're working with the DOM, but then mElement is - * useable instead. - */ nsresult -nsPlainTextSerializer::DoOpenContainer(const nsIParserNode* aNode, PRInt32 aTag) +nsPlainTextSerializer::DoOpenContainer(nsIAtom* aTag) { if (mFlags & nsIDocumentEncoder::OutputRaw) { // Raw means raw. Don't even think about doing anything fancy @@ -550,10 +445,8 @@ nsPlainTextSerializer::DoOpenContainer(const nsIParserNode* aNode, PRInt32 aTag) return NS_OK; } - eHTMLTags type = (eHTMLTags)aTag; - if (mTagStackIndex < TagStackSize) { - mTagStack[mTagStackIndex++] = type; + mTagStack[mTagStackIndex++] = aTag; } if (mIgnoreAboveIndex != (PRUint32)kNotFound) { @@ -562,15 +455,16 @@ nsPlainTextSerializer::DoOpenContainer(const nsIParserNode* aNode, PRInt32 aTag) // Reset this so that
doesn't affect the whitespace // above random
s below it.
-  mHasWrittenCiteBlockquote = mHasWrittenCiteBlockquote && aTag == eHTMLTag_pre;
+  mHasWrittenCiteBlockquote = mHasWrittenCiteBlockquote &&
+                              aTag == nsGkAtoms::pre;
 
   bool isInCiteBlockquote = false;
 
   // XXX special-case 
so that we don't add additional // newlines before the text. - if (aTag == eHTMLTag_blockquote) { + if (aTag == nsGkAtoms::blockquote) { nsAutoString value; - nsresult rv = GetAttributeValue(aNode, nsGkAtoms::type, value); + nsresult rv = GetAttributeValue(nsGkAtoms::type, value); isInCiteBlockquote = NS_SUCCEEDED(rv) && value.EqualsIgnoreCase("cite"); } @@ -578,9 +472,9 @@ nsPlainTextSerializer::DoOpenContainer(const nsIParserNode* aNode, PRInt32 aTag) EnsureVerticalSpace(mFloatingLines); // Check if this tag's content that should not be output - if ((type == eHTMLTag_noscript && + if ((aTag == nsGkAtoms::noscript && !(mFlags & nsIDocumentEncoder::OutputNoScriptContent)) || - ((type == eHTMLTag_iframe || type == eHTMLTag_noframes) && + ((aTag == nsGkAtoms::iframe || aTag == nsGkAtoms::noframes) && !(mFlags & nsIDocumentEncoder::OutputNoFramesContent))) { // Ignore everything that follows the current tag in // question until a matching end tag is encountered. @@ -588,7 +482,7 @@ nsPlainTextSerializer::DoOpenContainer(const nsIParserNode* aNode, PRInt32 aTag) return NS_OK; } - if (type == eHTMLTag_body) { + if (aTag == nsGkAtoms::body) { // Try to figure out here whether we have a // preformatted style attribute. // @@ -599,7 +493,7 @@ nsPlainTextSerializer::DoOpenContainer(const nsIParserNode* aNode, PRInt32 aTag) // (which arguably we should only do if told to do so). nsAutoString style; PRInt32 whitespace; - if(NS_SUCCEEDED(GetAttributeValue(aNode, nsGkAtoms::style, style)) && + if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::style, style)) && (kNotFound != (whitespace = style.Find("white-space:")))) { if (kNotFound != style.Find("pre-wrap", true, whitespace)) { @@ -652,9 +546,9 @@ nsPlainTextSerializer::DoOpenContainer(const nsIParserNode* aNode, PRInt32 aTag) return NS_OK; } - if (type == eHTMLTag_p) + if (aTag == nsGkAtoms::p) EnsureVerticalSpace(1); - else if (type == eHTMLTag_pre) { + else if (aTag == nsGkAtoms::pre) { if (GetLastBool(mIsInCiteBlockquote)) EnsureVerticalSpace(0); else if (mHasWrittenCiteBlockquote) { @@ -664,10 +558,10 @@ nsPlainTextSerializer::DoOpenContainer(const nsIParserNode* aNode, PRInt32 aTag) else EnsureVerticalSpace(1); } - else if (type == eHTMLTag_tr) { + else if (aTag == nsGkAtoms::tr) { PushBool(mHasWrittenCellsForRow, false); } - else if (type == eHTMLTag_td || type == eHTMLTag_th) { + else if (aTag == nsGkAtoms::td || aTag == nsGkAtoms::th) { // We must make sure that the content of two table cells get a // space between them. @@ -687,21 +581,21 @@ nsPlainTextSerializer::DoOpenContainer(const nsIParserNode* aNode, PRInt32 aTag) SetLastBool(mHasWrittenCellsForRow, true); } } - else if (type == eHTMLTag_ul) { + else if (aTag == nsGkAtoms::ul) { // Indent here to support nested lists, which aren't included in li :-( EnsureVerticalSpace(mULCount + mOLStackIndex == 0 ? 1 : 0); // Must end the current line before we change indention mIndent += kIndentSizeList; mULCount++; } - else if (type == eHTMLTag_ol) { + else if (aTag == nsGkAtoms::ol) { EnsureVerticalSpace(mULCount + mOLStackIndex == 0 ? 1 : 0); if (mFlags & nsIDocumentEncoder::OutputFormatted) { // Must end the current line before we change indention if (mOLStackIndex < OLStackSize) { nsAutoString startAttr; PRInt32 startVal = 1; - if(NS_SUCCEEDED(GetAttributeValue(aNode, nsGkAtoms::start, startAttr))){ + if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::start, startAttr))) { PRInt32 rv = 0; startVal = startAttr.ToInteger(&rv); if (NS_FAILED(rv)) @@ -714,12 +608,12 @@ nsPlainTextSerializer::DoOpenContainer(const nsIParserNode* aNode, PRInt32 aTag) } mIndent += kIndentSizeList; // see ul } - else if (type == eHTMLTag_li && + else if (aTag == nsGkAtoms::li && (mFlags & nsIDocumentEncoder::OutputFormatted)) { if (mTagStackIndex > 1 && IsInOL()) { if (mOLStackIndex > 0) { nsAutoString valueAttr; - if(NS_SUCCEEDED(GetAttributeValue(aNode, nsGkAtoms::value, valueAttr))){ + if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::value, valueAttr))) { PRInt32 rv = 0; PRInt32 valueAttrVal = valueAttr.ToInteger(&rv); if (NS_SUCCEEDED(rv)) @@ -744,20 +638,20 @@ nsPlainTextSerializer::DoOpenContainer(const nsIParserNode* aNode, PRInt32 aTag) mInIndentString.Append(PRUnichar(' ')); } - else if (type == eHTMLTag_dl) { + else if (aTag == nsGkAtoms::dl) { EnsureVerticalSpace(1); } - else if (type == eHTMLTag_dt) { + else if (aTag == nsGkAtoms::dt) { EnsureVerticalSpace(0); } - else if (type == eHTMLTag_dd) { + else if (aTag == nsGkAtoms::dd) { EnsureVerticalSpace(0); mIndent += kIndentSizeDD; } - else if (type == eHTMLTag_span) { + else if (aTag == nsGkAtoms::span) { ++mSpanLevel; } - else if (type == eHTMLTag_blockquote) { + else if (aTag == nsGkAtoms::blockquote) { // Push PushBool(mIsInCiteBlockquote, isInCiteBlockquote); if (isInCiteBlockquote) { @@ -769,13 +663,13 @@ nsPlainTextSerializer::DoOpenContainer(const nsIParserNode* aNode, PRInt32 aTag) mIndent += kTabSize; // Check for some maximum value? } } - else if (type == eHTMLTag_q) { + else if (aTag == nsGkAtoms::q) { Write(NS_LITERAL_STRING("\"")); } // Else make sure we'll separate block level tags, // even if we're about to leave, before doing any other formatting. - else if (IsBlockLevel(aTag)) { + else if (nsContentUtils::IsHTMLBlock(aTag)) { EnsureVerticalSpace(0); } @@ -789,18 +683,17 @@ nsPlainTextSerializer::DoOpenContainer(const nsIParserNode* aNode, PRInt32 aTag) ////////////////////////////////////////////////////////////// // Push on stack - bool currentNodeIsConverted = IsCurrentNodeConverted(aNode); - PushBool(mCurrentNodeIsConverted, currentNodeIsConverted); + bool currentNodeIsConverted = IsCurrentNodeConverted(); - if (type == eHTMLTag_h1 || type == eHTMLTag_h2 || - type == eHTMLTag_h3 || type == eHTMLTag_h4 || - type == eHTMLTag_h5 || type == eHTMLTag_h6) + if (aTag == nsGkAtoms::h1 || aTag == nsGkAtoms::h2 || + aTag == nsGkAtoms::h3 || aTag == nsGkAtoms::h4 || + aTag == nsGkAtoms::h5 || aTag == nsGkAtoms::h6) { EnsureVerticalSpace(2); if (mHeaderStrategy == 2) { // numbered mIndent += kIndentSizeHeaders; // Caching - PRInt32 level = HeaderLevel(type); + PRInt32 level = HeaderLevel(aTag); // Increase counter for current level mHeaderCounter[level]++; // Reset all lower levels @@ -821,37 +714,37 @@ nsPlainTextSerializer::DoOpenContainer(const nsIParserNode* aNode, PRInt32 aTag) } else if (mHeaderStrategy == 1) { // indent increasingly mIndent += kIndentSizeHeaders; - for (PRInt32 i = HeaderLevel(type); i > 1; i--) { + for (PRInt32 i = HeaderLevel(aTag); i > 1; i--) { // for h(x), run x-1 times mIndent += kIndentIncrementHeaders; } } } - else if (type == eHTMLTag_a && !currentNodeIsConverted) { + else if (aTag == nsGkAtoms::a && !currentNodeIsConverted) { nsAutoString url; - if (NS_SUCCEEDED(GetAttributeValue(aNode, nsGkAtoms::href, url)) + if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::href, url)) && !url.IsEmpty()) { mURL = url; } } - else if (type == eHTMLTag_sup && mStructs && !currentNodeIsConverted) { + else if (aTag == nsGkAtoms::sup && mStructs && !currentNodeIsConverted) { Write(NS_LITERAL_STRING("^")); } - else if (type == eHTMLTag_sub && mStructs && !currentNodeIsConverted) { + else if (aTag == nsGkAtoms::sub && mStructs && !currentNodeIsConverted) { Write(NS_LITERAL_STRING("_")); } - else if (type == eHTMLTag_code && mStructs && !currentNodeIsConverted) { + else if (aTag == nsGkAtoms::code && mStructs && !currentNodeIsConverted) { Write(NS_LITERAL_STRING("|")); } - else if ((type == eHTMLTag_strong || type == eHTMLTag_b) + else if ((aTag == nsGkAtoms::strong || aTag == nsGkAtoms::b) && mStructs && !currentNodeIsConverted) { Write(NS_LITERAL_STRING("*")); } - else if ((type == eHTMLTag_em || type == eHTMLTag_i) + else if ((aTag == nsGkAtoms::em || aTag == nsGkAtoms::i) && mStructs && !currentNodeIsConverted) { Write(NS_LITERAL_STRING("/")); } - else if (type == eHTMLTag_u && mStructs && !currentNodeIsConverted) { + else if (aTag == nsGkAtoms::u && mStructs && !currentNodeIsConverted) { Write(NS_LITERAL_STRING("_")); } @@ -866,7 +759,7 @@ nsPlainTextSerializer::DoOpenContainer(const nsIParserNode* aNode, PRInt32 aTag) } nsresult -nsPlainTextSerializer::DoCloseContainer(PRInt32 aTag) +nsPlainTextSerializer::DoCloseContainer(nsIAtom* aTag) { if (mFlags & nsIDocumentEncoder::OutputRaw) { // Raw means raw. Don't even think about doing anything fancy @@ -891,9 +784,8 @@ nsPlainTextSerializer::DoCloseContainer(PRInt32 aTag) return NS_OK; } - eHTMLTags type = (eHTMLTags)aTag; // End current line if we're ending a block level tag - if((type == eHTMLTag_body) || (type == eHTMLTag_html)) { + if ((aTag == nsGkAtoms::body) || (aTag == nsGkAtoms::html)) { // We want the output to end with a new line, // but in preformatted areas like text fields, // we can't emit newlines that weren't there. @@ -914,26 +806,26 @@ nsPlainTextSerializer::DoCloseContainer(PRInt32 aTag) return NS_OK; } - if (type == eHTMLTag_tr) { + if (aTag == nsGkAtoms::tr) { PopBool(mHasWrittenCellsForRow); // Should always end a line, but get no more whitespace if (mFloatingLines < 0) mFloatingLines = 0; mLineBreakDue = true; } - else if (((type == eHTMLTag_li) || - (type == eHTMLTag_dt)) && + else if (((aTag == nsGkAtoms::li) || + (aTag == nsGkAtoms::dt)) && (mFlags & nsIDocumentEncoder::OutputFormatted)) { // Items that should always end a line, but get no more whitespace if (mFloatingLines < 0) mFloatingLines = 0; mLineBreakDue = true; } - else if (type == eHTMLTag_pre) { + else if (aTag == nsGkAtoms::pre) { mFloatingLines = GetLastBool(mIsInCiteBlockquote) ? 0 : 1; mLineBreakDue = true; } - else if (type == eHTMLTag_ul) { + else if (aTag == nsGkAtoms::ul) { FlushLine(); mIndent -= kIndentSizeList; if (--mULCount + mOLStackIndex == 0) { @@ -941,7 +833,7 @@ nsPlainTextSerializer::DoCloseContainer(PRInt32 aTag) mLineBreakDue = true; } } - else if (type == eHTMLTag_ol) { + else if (aTag == nsGkAtoms::ol) { FlushLine(); // Doing this after decreasing OLStackIndex would be wrong. mIndent -= kIndentSizeList; NS_ASSERTION(mOLStackIndex, "Wrong OLStack level!"); @@ -951,24 +843,24 @@ nsPlainTextSerializer::DoCloseContainer(PRInt32 aTag) mLineBreakDue = true; } } - else if (type == eHTMLTag_dl) { + else if (aTag == nsGkAtoms::dl) { mFloatingLines = 1; mLineBreakDue = true; } - else if (type == eHTMLTag_dd) { + else if (aTag == nsGkAtoms::dd) { FlushLine(); mIndent -= kIndentSizeDD; } - else if (type == eHTMLTag_span) { + else if (aTag == nsGkAtoms::span) { NS_ASSERTION(mSpanLevel, "Span level will be negative!"); --mSpanLevel; } - else if (type == eHTMLTag_div) { + else if (aTag == nsGkAtoms::div) { if (mFloatingLines < 0) mFloatingLines = 0; mLineBreakDue = true; } - else if (type == eHTMLTag_blockquote) { + else if (aTag == nsGkAtoms::blockquote) { FlushLine(); // Is this needed? // Pop @@ -986,13 +878,11 @@ nsPlainTextSerializer::DoCloseContainer(PRInt32 aTag) } mLineBreakDue = true; } - else if (type == eHTMLTag_q) { + else if (aTag == nsGkAtoms::q) { Write(NS_LITERAL_STRING("\"")); } - else if (IsBlockLevel(aTag) - && type != eHTMLTag_script - && type != eHTMLTag_doctypeDecl - && type != eHTMLTag_markupDecl) { + else if (nsContentUtils::IsHTMLBlock(aTag) + && aTag != nsGkAtoms::script) { // All other blocks get 1 vertical space after them // in formatted mode, otherwise 0. // This is hard. Sometimes 0 is a better number, but @@ -1016,24 +906,24 @@ nsPlainTextSerializer::DoCloseContainer(PRInt32 aTag) ////////////////////////////////////////////////////////////// // Pop the currentConverted stack - bool currentNodeIsConverted = PopBool(mCurrentNodeIsConverted); + bool currentNodeIsConverted = IsCurrentNodeConverted(); - if (type == eHTMLTag_h1 || type == eHTMLTag_h2 || - type == eHTMLTag_h3 || type == eHTMLTag_h4 || - type == eHTMLTag_h5 || type == eHTMLTag_h6) { + if (aTag == nsGkAtoms::h1 || aTag == nsGkAtoms::h2 || + aTag == nsGkAtoms::h3 || aTag == nsGkAtoms::h4 || + aTag == nsGkAtoms::h5 || aTag == nsGkAtoms::h6) { if (mHeaderStrategy) { /*numbered or indent increasingly*/ mIndent -= kIndentSizeHeaders; } if (mHeaderStrategy == 1 /*indent increasingly*/ ) { - for (PRInt32 i = HeaderLevel(type); i > 1; i--) { + for (PRInt32 i = HeaderLevel(aTag); i > 1; i--) { // for h(x), run x-1 times mIndent -= kIndentIncrementHeaders; } } EnsureVerticalSpace(1); } - else if (type == eHTMLTag_a && !currentNodeIsConverted && !mURL.IsEmpty()) { + else if (aTag == nsGkAtoms::a && !currentNodeIsConverted && !mURL.IsEmpty()) { nsAutoString temp; temp.AssignLiteral(" <"); temp += mURL; @@ -1041,122 +931,124 @@ nsPlainTextSerializer::DoCloseContainer(PRInt32 aTag) Write(temp); mURL.Truncate(); } - else if ((type == eHTMLTag_sup || type == eHTMLTag_sub) + else if ((aTag == nsGkAtoms::sup || aTag == nsGkAtoms::sub) && mStructs && !currentNodeIsConverted) { Write(kSpace); } - else if (type == eHTMLTag_code && mStructs && !currentNodeIsConverted) { + else if (aTag == nsGkAtoms::code && mStructs && !currentNodeIsConverted) { Write(NS_LITERAL_STRING("|")); } - else if ((type == eHTMLTag_strong || type == eHTMLTag_b) + else if ((aTag == nsGkAtoms::strong || aTag == nsGkAtoms::b) && mStructs && !currentNodeIsConverted) { Write(NS_LITERAL_STRING("*")); } - else if ((type == eHTMLTag_em || type == eHTMLTag_i) + else if ((aTag == nsGkAtoms::em || aTag == nsGkAtoms::i) && mStructs && !currentNodeIsConverted) { Write(NS_LITERAL_STRING("/")); } - else if (type == eHTMLTag_u && mStructs && !currentNodeIsConverted) { + else if (aTag == nsGkAtoms::u && mStructs && !currentNodeIsConverted) { Write(NS_LITERAL_STRING("_")); } return NS_OK; } -/** - * aNode may be null when we're working with the DOM, but then mElement is - * useable instead. - */ -nsresult -nsPlainTextSerializer::DoAddLeaf(const nsIParserNode *aNode, PRInt32 aTag, - const nsAString& aText) +bool +nsPlainTextSerializer::MustSuppressLeaf() { - // If we don't want any output, just return - if (!DoOutput()) { - return NS_OK; - } - - if (aTag != eHTMLTag_whitespace && aTag != eHTMLTag_newline) { - // Make sure to reset this, since it's no longer true. - mHasWrittenCiteBlockquote = false; - } - - if (mLineBreakDue) - EnsureVerticalSpace(mFloatingLines); - - eHTMLTags type = (eHTMLTags)aTag; - if ((mTagStackIndex > 1 && - mTagStack[mTagStackIndex-2] == eHTMLTag_select) || + mTagStack[mTagStackIndex-2] == nsGkAtoms::select) || (mTagStackIndex > 0 && - mTagStack[mTagStackIndex-1] == eHTMLTag_select)) { + mTagStack[mTagStackIndex-1] == nsGkAtoms::select)) { // Don't output the contents of SELECT elements; // Might be nice, eventually, to output just the selected element. // Read more in bug 31994. - return NS_OK; + return true; } - else if (mTagStackIndex > 0 && - (mTagStack[mTagStackIndex-1] == eHTMLTag_script || - mTagStack[mTagStackIndex-1] == eHTMLTag_style)) { + + if (mTagStackIndex > 0 && + (mTagStack[mTagStackIndex-1] == nsGkAtoms::script || + mTagStack[mTagStackIndex-1] == nsGkAtoms::style)) { // Don't output the contents of diff --git a/dom/tests/mochitest/general/test_frameElementWrapping.html b/dom/tests/mochitest/general/test_frameElementWrapping.html index 1cc5b18f8c2..33bc7fdde85 100644 --- a/dom/tests/mochitest/general/test_frameElementWrapping.html +++ b/dom/tests/mochitest/general/test_frameElementWrapping.html @@ -1,7 +1,7 @@ - Test for location object behaviors + Test for same-origin and cross-origin wrapping of frameElement @@ -14,6 +14,14 @@
 
+  
+  
+  
+
+
+

+ + + + \ No newline at end of file diff --git a/layout/build/nsLayoutModule.cpp b/layout/build/nsLayoutModule.cpp index d2dc08f232f..a7e24e95325 100644 --- a/layout/build/nsLayoutModule.cpp +++ b/layout/build/nsLayoutModule.cpp @@ -62,7 +62,6 @@ #include "nsIFactory.h" #include "nsIFrameUtil.h" #include "nsHTMLStyleSheet.h" -#include "nsIHTMLToTextSink.h" #include "nsILayoutDebugger.h" #include "nsINameSpaceManager.h" #include "nsINodeInfo.h" @@ -1136,7 +1135,6 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = { { NS_CONTENTSERIALIZER_CONTRACTID_PREFIX "text/html", &kNS_HTMLCONTENTSERIALIZER_CID }, { NS_CONTENTSERIALIZER_CONTRACTID_PREFIX "application/vnd.mozilla.xul+xml", &kNS_XMLCONTENTSERIALIZER_CID }, { NS_CONTENTSERIALIZER_CONTRACTID_PREFIX "text/plain", &kNS_PLAINTEXTSERIALIZER_CID }, - { NS_PLAINTEXTSINK_CONTRACTID, &kNS_PLAINTEXTSERIALIZER_CID }, { MOZ_SANITIZINGHTMLSERIALIZER_CONTRACTID, &kMOZ_SANITIZINGHTMLSERIALIZER_CID }, { NS_PARSERUTILS_CONTRACTID, &kNS_PARSERUTILS_CID }, { NS_SCRIPTABLEUNESCAPEHTML_CONTRACTID, &kNS_SCRIPTABLEUNESCAPEHTML_CID }, diff --git a/layout/forms/nsComboboxControlFrame.cpp b/layout/forms/nsComboboxControlFrame.cpp index 3f605eae2f6..5a61ab784f4 100644 --- a/layout/forms/nsComboboxControlFrame.cpp +++ b/layout/forms/nsComboboxControlFrame.cpp @@ -1324,7 +1324,7 @@ nsComboboxControlFrame::DestroyFrom(nsIFrame* aDestructRoot) nsBlockFrame::DestroyFrom(aDestructRoot); } -nsFrameList +const nsFrameList& nsComboboxControlFrame::GetChildList(ChildListID aListID) const { if (kSelectPopupList == aListID) { diff --git a/layout/forms/nsComboboxControlFrame.h b/layout/forms/nsComboboxControlFrame.h index ea412a8a4fc..91cd8b6a3d2 100644 --- a/layout/forms/nsComboboxControlFrame.h +++ b/layout/forms/nsComboboxControlFrame.h @@ -141,7 +141,7 @@ public: virtual void DestroyFrom(nsIFrame* aDestructRoot); NS_IMETHOD SetInitialChildList(ChildListID aListID, nsFrameList& aChildList); - virtual nsFrameList GetChildList(ChildListID aListID) const; + virtual const nsFrameList& GetChildList(ChildListID aListID) const; virtual void GetChildLists(nsTArray* aLists) const; virtual nsIFrame* GetContentInsertionFrame(); diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index c6a384e6c1b..6b0f5812a1e 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -285,6 +285,11 @@ NS_DECLARE_FRAME_PROPERTY(LineCursorProperty, nsnull) NS_DECLARE_FRAME_PROPERTY(OverflowLinesProperty, DestroyOverflowLines) NS_DECLARE_FRAME_PROPERTY(OverflowOutOfFlowsProperty, nsContainerFrame::DestroyFrameList) +NS_DECLARE_FRAME_PROPERTY(PushedFloatProperty, + nsContainerFrame::DestroyFrameList) +NS_DECLARE_FRAME_PROPERTY(OutsideBulletProperty, + nsContainerFrame::DestroyFrameList) +NS_DECLARE_FRAME_PROPERTY(InsideBulletProperty, nsnull) //---------------------------------------------------------------------- @@ -308,12 +313,6 @@ void nsBlockFrame::DestroyFrom(nsIFrame* aDestructRoot) { DestroyAbsoluteFrames(aDestructRoot); - // Outside bullets are not in our child-list so check for them here - // and delete them when present. - if (mBullet && HaveOutsideBullet()) { - mBullet->DestroyFrom(aDestructRoot); - mBullet = nsnull; - } mFloats.DestroyFramesFrom(aDestructRoot); @@ -329,9 +328,10 @@ nsBlockFrame::DestroyFrom(nsIFrame* aDestructRoot) } // destroy overflow lines now - nsLineList* overflowLines = RemoveOverflowLines(); + FrameLines* overflowLines = RemoveOverflowLines(); if (overflowLines) { - nsLineBox::DeleteLineList(presContext, *overflowLines, aDestructRoot); + nsLineBox::DeleteLineList(presContext, overflowLines->mLines, + aDestructRoot); delete overflowLines; } @@ -454,12 +454,12 @@ nsBlockFrame::List(FILE* out, PRInt32 aIndent) const } // Output the overflow lines. - const nsLineList* overflowLines = GetOverflowLines(); - if (overflowLines && !overflowLines->empty()) { + const FrameLines* overflowLines = GetOverflowLines(); + if (overflowLines && !overflowLines->mLines.empty()) { IndentBy(out, aIndent); fputs("Overflow-lines<\n", out); - const_line_iterator line = overflowLines->begin(), - line_end = overflowLines->end(); + const_line_iterator line = overflowLines->mLines.begin(), + line_end = overflowLines->mLines.end(); for ( ; line != line_end; ++line) { line->List(out, aIndent + 1); } @@ -574,19 +574,15 @@ nsBlockFrame::GetCaretBaseline() const ///////////////////////////////////////////////////////////////////////////// // Child frame enumeration -nsFrameList +const nsFrameList& nsBlockFrame::GetChildList(ChildListID aListID) const { switch (aListID) { case kPrincipalList: return mFrames; case kOverflowList: { - // XXXbz once we start using nsFrameList for our overflow list, we - // could switch GetChildList to returning a |const nsFrameList&|. - nsLineList* overflowLines = GetOverflowLines(); - return overflowLines ? nsFrameList(overflowLines->front()->mFirstChild, - overflowLines->back()->LastChild()) - : nsFrameList::EmptyList(); + FrameLines* overflowLines = GetOverflowLines(); + return overflowLines ? overflowLines->mFrames : nsFrameList::EmptyList(); } case kFloatList: return mFloats; @@ -598,9 +594,10 @@ nsBlockFrame::GetChildList(ChildListID aListID) const const nsFrameList* list = GetPushedFloats(); return list ? *list : nsFrameList::EmptyList(); } - case kBulletList: - return HaveOutsideBullet() ? nsFrameList(mBullet, mBullet) - : nsFrameList::EmptyList(); + case kBulletList: { + const nsFrameList* list = GetOutsideBulletList(); + return list ? *list : nsFrameList::EmptyList(); + } default: return nsContainerFrame::GetChildList(aListID); } @@ -610,20 +607,18 @@ void nsBlockFrame::GetChildLists(nsTArray* aLists) const { nsContainerFrame::GetChildLists(aLists); - nsLineList* overflowLines = GetOverflowLines(); - if (overflowLines && overflowLines->front()->mFirstChild) { - nsFrameList overflowList(overflowLines->front()->mFirstChild, - overflowLines->back()->LastChild()); - overflowList.AppendIfNonempty(aLists, kOverflowList); + FrameLines* overflowLines = GetOverflowLines(); + if (overflowLines) { + overflowLines->mFrames.AppendIfNonempty(aLists, kOverflowList); } const nsFrameList* list = GetOverflowOutOfFlows(); if (list) { list->AppendIfNonempty(aLists, kOverflowOutOfFlowList); } mFloats.AppendIfNonempty(aLists, kFloatList); - if (HaveOutsideBullet()) { - nsFrameList bullet(mBullet, mBullet); - bullet.AppendIfNonempty(aLists, kBulletList); + list = GetOutsideBulletList(); + if (list) { + list->AppendIfNonempty(aLists, kBulletList); } list = GetPushedFloats(); if (list) { @@ -637,8 +632,9 @@ nsBlockFrame::IsFloatContainingBlock() const return true; } -static void ReparentFrame(nsIFrame* aFrame, nsIFrame* aOldParent, - nsIFrame* aNewParent) { +static void +ReparentFrame(nsIFrame* aFrame, nsIFrame* aOldParent, nsIFrame* aNewParent) +{ NS_ASSERTION(aOldParent == aFrame->GetParent(), "Parent not consistent with expectations"); @@ -650,8 +646,37 @@ static void ReparentFrame(nsIFrame* aFrame, nsIFrame* aOldParent, aOldParent, aNewParent); } -////////////////////////////////////////////////////////////////////// -// Frame structure methods +static void +ReparentFrames(nsFrameList& aFrameList, nsIFrame* aOldParent, + nsIFrame* aNewParent) +{ + for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) { + ReparentFrame(e.get(), aOldParent, aNewParent); + } +} + +/** + * Remove the first line from aFromLines and adjust the associated frame list + * aFromFrames accordingly. The removed line is assigned to *aOutLine and + * a frame list with its frames is assigned to *aOutFrames, i.e. the frames + * that were extracted from the head of aFromFrames. + * aFromLines must contain at least one line, the line may be empty. + * @return true if aFromLines becomes empty + */ +static bool +RemoveFirstLine(nsLineList& aFromLines, nsFrameList& aFromFrames, + nsLineBox** aOutLine, nsFrameList* aOutFrames) +{ + nsLineList_iterator removedLine = aFromLines.begin(); + *aOutLine = removedLine; + nsLineList_iterator next = aFromLines.erase(removedLine); + bool isLastLine = next == aFromLines.end(); + nsIFrame* lastFrame = isLastLine ? aFromFrames.LastChild() + : next->mFirstChild->GetPrevSibling(); + nsFrameList::FrameLinkEnumerator linkToBreak(aFromFrames, lastFrame); + *aOutFrames = aFromFrames.ExtractHead(linkToBreak); + return isLastLine; +} ////////////////////////////////////////////////////////////////////// // Reflow methods @@ -1059,7 +1084,7 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, } if (!NS_FRAME_IS_FULLY_COMPLETE(state.mReflowStatus)) { - if (GetOverflowLines() || GetPushedFloats()) { + if (HasOverflowLines() || HasPushedFloats()) { state.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW; } @@ -1082,7 +1107,7 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, // rare case: an empty first line followed by a second line that // contains a block (example:
  • \n

    ... ). This is where // the second case can happen. - if (mBullet && HaveOutsideBullet() && !mLines.empty() && + if (HasOutsideBullet() && !mLines.empty() && (mLines.front()->IsBlock() || (0 == mLines.front()->mBounds.height && mLines.front() != mLines.back() && @@ -1094,7 +1119,8 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, bool havePosition = nsLayoutUtils::GetFirstLinePosition(this, &position); nscoord lineTop = havePosition ? position.mTop : reflowState->mComputedBorderPadding.top; - ReflowBullet(state, metrics, lineTop); + nsIFrame* bullet = GetOutsideBullet(); + ReflowBullet(bullet, state, metrics, lineTop); NS_ASSERTION(!BulletIsEmpty() || metrics.height == 0, "empty bullet took up space"); @@ -1105,9 +1131,9 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, // bullets that are placed next to a child block (bug 92896) // Tall bullets won't look particularly nice here... - nsRect bbox = mBullet->GetRect(); + nsRect bbox = bullet->GetRect(); bbox.y = position.mBaseline - metrics.ascent; - mBullet->SetRect(bbox); + bullet->SetRect(bbox); } // Otherwise just leave the bullet where it is, up against our top padding. } @@ -1463,13 +1489,14 @@ nsBlockFrame::ComputeOverflowAreas(const nsRect& aBounds, areas.UnionWith(line->GetOverflowAreas()); } - // Factor the bullet in; normally the bullet will be factored into + // Factor an outside bullet in; normally the bullet will be factored into // the line-box's overflow areas. However, if the line is a block // line then it won't; if there are no lines, it won't. So just // factor it in anyway (it can't hurt if it was already done). // XXXldb Can we just fix GetOverflowArea instead? - if (mBullet) { - areas.UnionAllWith(mBullet->GetRect()); + nsIFrame* outsideBullet = GetOutsideBullet(); + if (outsideBullet) { + areas.UnionAllWith(outsideBullet->GetRect()); } // Factor in the bottom edge of the children. Child frames will be added @@ -2190,7 +2217,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) line_iterator lineIter = this->end_lines(); if (lineIter != this->begin_lines()) { lineIter--; // I have lines; step back from dummy iterator to last line. - nsBlockInFlowLineIterator bifLineIter(this, lineIter, false); + nsBlockInFlowLineIterator bifLineIter(this, lineIter); // Check for next-in-flow-chain's first line. // (First, see if there is such a line, and second, see if it's clean) @@ -2212,92 +2239,59 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) if (!skipPull && aState.mNextInFlow) { // Pull data from a next-in-flow if there's still room for more // content here. - while (keepGoing && (nsnull != aState.mNextInFlow)) { + while (keepGoing && aState.mNextInFlow) { // Grab first line from our next-in-flow nsBlockFrame* nextInFlow = aState.mNextInFlow; - line_iterator nifLine = nextInFlow->begin_lines(); - nsLineBox *toMove; - bool toMoveIsOverflowLine; - if (nifLine != nextInFlow->end_lines()) { - toMove = nifLine; - nextInFlow->mLines.erase(nifLine); - toMoveIsOverflowLine = false; + nsLineBox* pulledLine; + nsFrameList pulledFrames; + bool isOverflowLine = false; + if (!nextInFlow->mLines.empty()) { + RemoveFirstLine(nextInFlow->mLines, nextInFlow->mFrames, + &pulledLine, &pulledFrames); } else { // Grab an overflow line if there are any - nsLineList* overflowLines = nextInFlow->GetOverflowLines(); + FrameLines* overflowLines = nextInFlow->GetOverflowLines(); if (!overflowLines) { aState.mNextInFlow = static_cast(nextInFlow->GetNextInFlow()); continue; } - nifLine = overflowLines->begin(); - NS_ASSERTION(nifLine != overflowLines->end(), - "Stored overflow line list should not be empty"); - toMove = nifLine; - nextInFlow->RemoveOverflowLines(); - nifLine = overflowLines->erase(nifLine); - if (nifLine != overflowLines->end()) { - // We need to this remove-and-put-back dance because we want - // to avoid making the overflow line list empty while it's - // stored in the property (because the property has the - // invariant that the list is never empty). - nextInFlow->SetOverflowLines(overflowLines); - } else { - delete overflowLines; + bool last = + RemoveFirstLine(overflowLines->mLines, overflowLines->mFrames, + &pulledLine, &pulledFrames); + if (last) { + nextInFlow->DestroyOverflowLines(); } - toMoveIsOverflowLine = true; + isOverflowLine = true; } - if (0 == toMove->GetChildCount()) { + if (pulledFrames.IsEmpty()) { // The line is empty. Try the next one. - NS_ASSERTION(nsnull == toMove->mFirstChild, "bad empty line"); - aState.FreeLineBox(toMove); + NS_ASSERTION(pulledLine->GetChildCount() == 0 && + !pulledLine->mFirstChild, "bad empty line"); + aState.FreeLineBox(pulledLine); continue; } - // XXX move to a subroutine: run-in, overflow, pullframe and this do this - // Make the children in the line ours. - nsIFrame* frame = toMove->mFirstChild; - nsIFrame* lastFrame = nsnull; - PRInt32 n = toMove->GetChildCount(); - while (--n >= 0) { - ReparentFrame(frame, nextInFlow, this); - lastFrame = frame; - frame = frame->GetNextSibling(); - } - - NS_ASSERTION(lastFrame == toMove->LastChild(), "Unexpected lastFrame"); + ReparentFrames(pulledFrames, nextInFlow, this); + NS_ASSERTION(pulledFrames.LastChild() == pulledLine->LastChild(), + "Unexpected last frame"); NS_ASSERTION(aState.mPrevChild || mLines.empty(), "should have a prevchild here"); - NS_ASSERTION(aState.mPrevChild == mFrames.LastChild(), "Incorrect aState.mPrevChild before inserting line at end"); - // Shift toMove's frames into our mFrames list. - if (toMoveIsOverflowLine) { - // Pulling from an overflow list - // XXXbz If we switch overflow lines to nsFrameList, we should - // change this SetNextSibling call. - lastFrame->SetNextSibling(nsnull); - } else { - // Pulling from nextInFlow->mFrames - nsFrameList::FrameLinkEnumerator linkToBreak(nextInFlow->mFrames, lastFrame); - nextInFlow->mFrames.ExtractHead(linkToBreak); - } - nsFrameList newFrames(toMove->mFirstChild, lastFrame); - mFrames.AppendFrames(nsnull, newFrames); + // Shift pulledLine's frames into our mFrames list. + mFrames.AppendFrames(nsnull, pulledFrames); // Add line to our line list, and set its last child as our new prev-child - line = mLines.before_insert(end_lines(), toMove); - aState.mPrevChild = lastFrame; - - NS_ASSERTION(aState.mPrevChild == mFrames.LastChild(), - "Incorrect aState.mPrevChild after inserting line at end"); + line = mLines.before_insert(end_lines(), pulledLine); + aState.mPrevChild = mFrames.LastChild(); // Reparent floats whose placeholders are in the line. - ReparentFloats(toMove->mFirstChild, nextInFlow, toMoveIsOverflowLine, true); + ReparentFloats(pulledLine->mFirstChild, nextInFlow, isOverflowLine, true); - DumpLine(aState, toMove, deltaY, 0); + DumpLine(aState, pulledLine, deltaY, 0); #ifdef DEBUG AutoNoisyIndenter indent2(gNoisyReflow); #endif @@ -2350,9 +2344,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) } // Handle an odd-ball case: a list-item with no lines - if (mBullet && HaveOutsideBullet() && mLines.empty()) { + if (HasOutsideBullet() && mLines.empty()) { nsHTMLReflowMetrics metrics; - ReflowBullet(aState, metrics, + nsIFrame* bullet = GetOutsideBullet(); + ReflowBullet(bullet, aState, metrics, aState.mReflowState.mComputedBorderPadding.top); NS_ASSERTION(!BulletIsEmpty() || metrics.height == 0, "empty bullet took up space"); @@ -2362,7 +2357,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) // we end up with *some* height. if (metrics.ascent == nsHTMLReflowMetrics::ASK_FOR_BASELINE && - !nsLayoutUtils::GetFirstLineBaseline(mBullet, &metrics.ascent)) { + !nsLayoutUtils::GetFirstLineBaseline(bullet, &metrics.ascent)) { metrics.ascent = metrics.height; } @@ -2380,7 +2375,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) nscoord offset = minAscent - metrics.ascent; if (offset > 0) { - mBullet->SetRect(mBullet->GetRect() + nsPoint(0, offset)); + bullet->SetRect(bullet->GetRect() + nsPoint(0, offset)); } } } @@ -2594,7 +2589,7 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState, { // First check our remaining lines. if (end_lines() != aLine.next()) { - return PullFrameFrom(aState, aLine, this, false, aLine.next()); + return PullFrameFrom(aState, aLine, this, false, mFrames, aLine.next()); } NS_ASSERTION(!GetOverflowLines(), @@ -2606,13 +2601,15 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState, // first normal lines, then overflow lines if (!nextInFlow->mLines.empty()) { return PullFrameFrom(aState, aLine, nextInFlow, false, + nextInFlow->mFrames, nextInFlow->mLines.begin()); } - nsLineList* overflowLines = nextInFlow->GetOverflowLines(); + FrameLines* overflowLines = nextInFlow->GetOverflowLines(); if (overflowLines) { return PullFrameFrom(aState, aLine, nextInFlow, true, - overflowLines->begin()); + overflowLines->mFrames, + overflowLines->mLines.begin()); } nextInFlow = static_cast(nextInFlow->GetNextInFlow()); @@ -2627,6 +2624,7 @@ nsBlockFrame::PullFrameFrom(nsBlockReflowState& aState, nsLineBox* aLine, nsBlockFrame* aFromContainer, bool aFromOverflowLine, + nsFrameList& aFromFrameList, nsLineList::iterator aFromLine) { nsLineBox* fromLine = aFromLine; @@ -2653,16 +2651,12 @@ nsBlockFrame::PullFrameFrom(nsBlockReflowState& aState, // The frame is being pulled from a next-in-flow; therefore we // need to add it to our sibling list. if (NS_LIKELY(!aFromOverflowLine)) { + NS_ASSERTION(&aFromFrameList == &aFromContainer->mFrames, + "must be normal flow if not overflow line"); NS_ASSERTION(aFromLine == aFromContainer->mLines.begin(), "should only pull from first line"); - // Pulling from the next-in-flow's normal line list - aFromContainer->mFrames.RemoveFrame(frame); - } else { - // Pulling from the next-in-flow's overflow list - // XXXbz If we switch overflow lines to nsFrameList, we should - // change this SetNextSibling call. - frame->SetNextSibling(nsnull); } + aFromFrameList.RemoveFrame(frame); // When pushing and pulling frames we need to check for whether any // views need to be reparented @@ -2692,9 +2686,10 @@ nsBlockFrame::PullFrameFrom(nsBlockReflowState& aState, // XXX WHY do we invalidate the bounds AND the combined area? doesn't // the combined area always enclose the bounds? Invalidate(fromLine->mBounds); - nsLineList* fromLineList = aFromOverflowLine - ? aFromContainer->RemoveOverflowLines() - : &aFromContainer->mLines; + FrameLines* overflowLines = + aFromOverflowLine ? aFromContainer->RemoveOverflowLines() : nsnull; + nsLineList* fromLineList = + aFromOverflowLine ? &overflowLines->mLines : &aFromContainer->mLines; if (aFromLine.next() != fromLineList->end()) aFromLine.next()->MarkPreviousMarginDirty(); @@ -2706,9 +2701,9 @@ nsBlockFrame::PullFrameFrom(nsBlockReflowState& aState, // Put any remaining overflow lines back. if (aFromOverflowLine) { if (!fromLineList->empty()) { - aFromContainer->SetOverflowLines(fromLineList); + aFromContainer->SetOverflowLines(overflowLines); } else { - delete fromLineList; + delete overflowLines; // Now any iterators into fromLineList are invalid (but // aFromLine already was invalidated above) } @@ -2866,7 +2861,7 @@ nsBlockFrame::IsSelfEmpty() return false; } - if (HaveOutsideBullet() && !BulletIsEmpty()) { + if (HasOutsideBullet() && !BulletIsEmpty()) { return false; } @@ -3780,15 +3775,15 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, if (aLineLayout.GetDirtyNextLine()) { // aLine may have been pushed to the overflow lines. - nsLineList* overflowLines = GetOverflowLines(); + FrameLines* overflowLines = GetOverflowLines(); // We can't just compare iterators front() to aLine here, since they may be in // different lists. bool pushedToOverflowLines = overflowLines && - overflowLines->front() == aLine.get(); + overflowLines->mLines.front() == aLine.get(); if (pushedToOverflowLines) { // aLine is stale, it's associated with the main line list but it should // be associated with the overflow line list now - aLine = overflowLines->begin(); + aLine = overflowLines->mLines.begin(); } nsBlockInFlowLineIterator iter(this, aLine, pushedToOverflowLines); if (iter.Next() && iter.GetLine()->IsInline()) { @@ -4192,17 +4187,18 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // first or second line. It's only placed on the second line in a // rare case: when the first line is empty. bool addedBullet = false; - if (mBullet && HaveOutsideBullet() && + if (HasOutsideBullet() && ((aLine == mLines.front() && (!aLineLayout.IsZeroHeight() || (aLine == mLines.back()))) || (mLines.front() != mLines.back() && 0 == mLines.front()->mBounds.height && aLine == mLines.begin().next()))) { nsHTMLReflowMetrics metrics; - ReflowBullet(aState, metrics, aState.mY); + nsIFrame* bullet = GetOutsideBullet(); + ReflowBullet(bullet, aState, metrics, aState.mY); NS_ASSERTION(!BulletIsEmpty() || metrics.height == 0, "empty bullet took up space"); - aLineLayout.AddBulletFrame(mBullet, metrics); + aLineLayout.AddBulletFrame(bullet, metrics); addedBullet = true; } aLineLayout.VerticalAlignLine(); @@ -4285,7 +4281,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, aLineLayout.RelativePositionFrames(overflowAreas); aLine->SetOverflowAreas(overflowAreas); if (addedBullet) { - aLineLayout.RemoveBulletFrame(mBullet); + aLineLayout.RemoveBulletFrame(GetOutsideBullet()); } // Inline lines do not have margins themselves; however they are @@ -4386,6 +4382,10 @@ void nsBlockFrame::PushLines(nsBlockReflowState& aState, nsLineList::iterator aLineBefore) { + // NOTE: aLineBefore is always a normal line, not an overflow line. + // The following expression will assert otherwise. + DebugOnly check = aLineBefore == mLines.begin(); + nsLineList::iterator overBegin(aLineBefore.next()); // PushTruncatedPlaceholderLine sometimes pushes the first line. Ugh. @@ -4407,32 +4407,27 @@ nsBlockFrame::PushLines(nsBlockReflowState& aState, // the height of some child block to grow which creates additional // overflowing content. In such cases we must prepend the new // overflow to the existing overflow. - nsLineList* overflowLines = RemoveOverflowLines(); + FrameLines* overflowLines = RemoveOverflowLines(); if (!overflowLines) { // XXXldb use presshell arena! - overflowLines = new nsLineList(); + overflowLines = new FrameLines(); } if (overflowLines) { - // First, remove the frames we're pushing from mFrames - nsIFrame* oldLastChild = mFrames.LastChild(); + nsIFrame* lineBeforeLastFrame; if (firstLine) { - mFrames.Clear(); + lineBeforeLastFrame = nsnull; // removes all frames } else { nsIFrame* f = overBegin->mFirstChild; - nsIFrame* lineBeforeLastFrame = - f ? f->GetPrevSibling() : aLineBefore->LastChild(); + lineBeforeLastFrame = f ? f->GetPrevSibling() : mFrames.LastChild(); NS_ASSERTION(!f || lineBeforeLastFrame == aLineBefore->LastChild(), "unexpected line frames"); - mFrames.RemoveFramesAfter(lineBeforeLastFrame); } - if (!overflowLines->empty()) { - // XXXbz If we switch overflow lines to nsFrameList, we should - // change this SetNextSibling call. - oldLastChild->SetNextSibling(overflowLines->front()->mFirstChild); - } - overflowLines->splice(overflowLines->begin(), mLines, overBegin, - end_lines()); - NS_ASSERTION(!overflowLines->empty(), "should not be empty"); + nsFrameList pushedFrames = mFrames.RemoveFramesAfter(lineBeforeLastFrame); + overflowLines->mFrames.InsertFrames(nsnull, nsnull, pushedFrames); + + overflowLines->mLines.splice(overflowLines->mLines.begin(), mLines, + overBegin, end_lines()); + NS_ASSERTION(!overflowLines->mLines.empty(), "should not be empty"); // this takes ownership but it won't delete it immediately so we // can keep using it. SetOverflowLines(overflowLines); @@ -4441,18 +4436,18 @@ nsBlockFrame::PushLines(nsBlockReflowState& aState, // they are pulled up by our next-in-flow. // XXXldb Can this get called O(N) times making the whole thing O(N^2)? - for (line_iterator line = overflowLines->begin(), - line_end = overflowLines->end(); + for (line_iterator line = overflowLines->mLines.begin(), + line_end = overflowLines->mLines.end(); line != line_end; ++line) - { - line->MarkDirty(); - line->MarkPreviousMarginDirty(); - line->mBounds.SetRect(0, 0, 0, 0); - if (line->HasFloats()) { - line->FreeFloats(aState.mFloatCacheFreeList); - } + { + line->MarkDirty(); + line->MarkPreviousMarginDirty(); + line->mBounds.SetRect(0, 0, 0, 0); + if (line->HasFloats()) { + line->FreeFloats(aState.mFloatCacheFreeList); } + } } } @@ -4471,31 +4466,23 @@ nsBlockFrame::DrainOverflowLines() #ifdef DEBUG VerifyOverflowSituation(); #endif - nsLineList* overflowLines = nsnull; - nsLineList* ourOverflowLines = nsnull; + FrameLines* overflowLines = nsnull; + FrameLines* ourOverflowLines = nsnull; // First grab the prev-in-flows overflow lines nsBlockFrame* prevBlock = (nsBlockFrame*) GetPrevInFlow(); if (prevBlock) { overflowLines = prevBlock->RemoveOverflowLines(); if (overflowLines) { - NS_ASSERTION(! overflowLines->empty(), + NS_ASSERTION(!overflowLines->mLines.empty(), "overflow lines should never be set and empty"); - // Make all the frames on the overflow line list mine - nsIFrame* frame = overflowLines->front()->mFirstChild; - while (nsnull != frame) { - ReparentFrame(frame, prevBlock, this); + // Make all the frames on the overflow line list mine. + ReparentFrames(overflowLines->mFrames, prevBlock, this); - // Get the next frame - frame = frame->GetNextSibling(); - } - - // make the overflow out-of-flow frames mine too + // Make the overflow out-of-flow frames mine too. nsAutoOOFFrameList oofs(prevBlock); if (oofs.mList.NotEmpty()) { - for (nsIFrame* f = oofs.mList.FirstChild(); f; f = f->GetNextSibling()) { - ReparentFrame(f, prevBlock, this); - } + ReparentFrames(oofs.mList, prevBlock, this); mFloats.InsertFrames(nsnull, nsnull, oofs.mList); } } @@ -4522,7 +4509,7 @@ nsBlockFrame::DrainOverflowLines() // Now join the line lists into mLines if (overflowLines) { - if (!overflowLines->empty()) { + if (!overflowLines->mLines.empty()) { // Join the line lists if (!mLines.empty()) { // Remember to recompute the margins on the first line. This will @@ -4531,26 +4518,18 @@ nsBlockFrame::DrainOverflowLines() } // Join the sibling lists together - nsIFrame* firstFrame = overflowLines->front()->mFirstChild; - nsIFrame* lastFrame = overflowLines->back()->LastChild(); - nsFrameList framesToInsert(firstFrame, lastFrame); - mFrames.InsertFrames(nsnull, nsnull, framesToInsert); + mFrames.InsertFrames(nsnull, nsnull, overflowLines->mFrames); // Place overflow lines at the front of our line list - mLines.splice(mLines.begin(), *overflowLines); - NS_ASSERTION(overflowLines->empty(), "splice should empty list"); + mLines.splice(mLines.begin(), overflowLines->mLines); + NS_ASSERTION(overflowLines->mLines.empty(), "splice should empty list"); } delete overflowLines; } if (ourOverflowLines) { - if (!ourOverflowLines->empty()) { - nsIFrame* firstFrame = ourOverflowLines->front()->mFirstChild; - nsIFrame* lastFrame = ourOverflowLines->back()->LastChild(); - nsFrameList framesToAppend(firstFrame, lastFrame); - mFrames.AppendFrames(nsnull, framesToAppend); - - // append the overflow to mLines - mLines.splice(mLines.end(), *ourOverflowLines); + if (!ourOverflowLines->mLines.empty()) { + mFrames.AppendFrames(nsnull, ourOverflowLines->mFrames); + mLines.splice(mLines.end(), ourOverflowLines->mLines); } delete ourOverflowLines; } @@ -4586,40 +4565,57 @@ nsBlockFrame::DrainPushedFloats(nsBlockReflowState& aState) } } -nsLineList* +nsBlockFrame::FrameLines* nsBlockFrame::GetOverflowLines() const { - if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_LINES)) { + if (!HasOverflowLines()) { return nsnull; } - nsLineList* lines = static_cast - (Properties().Get(OverflowLinesProperty())); - NS_ASSERTION(lines && !lines->empty(), + FrameLines* prop = + static_cast(Properties().Get(OverflowLinesProperty())); + NS_ASSERTION(prop && !prop->mLines.empty() && + prop->mLines.front()->mFirstChild == prop->mFrames.FirstChild(), "value should always be stored and non-empty when state set"); - return lines; + return prop; } -nsLineList* +nsBlockFrame::FrameLines* nsBlockFrame::RemoveOverflowLines() { - if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_LINES)) { + if (!HasOverflowLines()) { return nsnull; } - nsLineList* lines = static_cast - (Properties().Remove(OverflowLinesProperty())); - NS_ASSERTION(lines && !lines->empty(), + FrameLines* prop = + static_cast(Properties().Remove(OverflowLinesProperty())); + NS_ASSERTION(prop && !prop->mLines.empty() && + prop->mLines.front()->mFirstChild == prop->mFrames.FirstChild(), "value should always be stored and non-empty when state set"); RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES); - return lines; + return prop; +} + +void +nsBlockFrame::DestroyOverflowLines() +{ + NS_ASSERTION(HasOverflowLines(), "huh?"); + FrameLines* prop = + static_cast(Properties().Remove(OverflowLinesProperty())); + NS_ASSERTION(prop && prop->mLines.empty(), + "value should always be stored but empty when destroying"); + RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES); + delete prop; } // This takes ownership of aOverflowLines. // XXX We should allocate overflowLines from presShell arena! -nsresult -nsBlockFrame::SetOverflowLines(nsLineList* aOverflowLines) +void +nsBlockFrame::SetOverflowLines(FrameLines* aOverflowLines) { NS_ASSERTION(aOverflowLines, "null lines"); - NS_ASSERTION(!aOverflowLines->empty(), "empty lines"); + NS_ASSERTION(!aOverflowLines->mLines.empty(), "empty lines"); + NS_ASSERTION(aOverflowLines->mLines.front()->mFirstChild == + aOverflowLines->mFrames.FirstChild(), + "invalid overflow lines / frames"); NS_ASSERTION(!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_LINES), "Overwriting existing overflow lines"); @@ -4628,7 +4624,6 @@ nsBlockFrame::SetOverflowLines(nsLineList* aOverflowLines) NS_ASSERTION(!props.Get(OverflowLinesProperty()), "existing overflow list"); props.Set(OverflowLinesProperty(), aOverflowLines); AddStateBits(NS_BLOCK_HAS_OVERFLOW_LINES); - return NS_OK; } nsFrameList* @@ -4675,10 +4670,47 @@ nsBlockFrame::SetOverflowOutOfFlows(const nsFrameList& aList, } } +nsBulletFrame* +nsBlockFrame::GetInsideBullet() const +{ + if (!HasInsideBullet()) { + return nsnull; + } + NS_ASSERTION(!HasOutsideBullet(), "invalid bullet state"); + nsBulletFrame* frame = + static_cast(Properties().Get(InsideBulletProperty())); + NS_ASSERTION(frame && frame->GetType() == nsGkAtoms::bulletFrame, + "bogus inside bullet frame"); + return frame; +} + +nsBulletFrame* +nsBlockFrame::GetOutsideBullet() const +{ + nsFrameList* list = GetOutsideBulletList(); + return list ? static_cast(list->FirstChild()) + : nsnull; +} + +nsFrameList* +nsBlockFrame::GetOutsideBulletList() const +{ + if (!HasOutsideBullet()) { + return nsnull; + } + NS_ASSERTION(!HasInsideBullet(), "invalid bullet state"); + nsFrameList* list = + static_cast(Properties().Get(OutsideBulletProperty())); + NS_ASSERTION(list && list->GetLength() == 1 && + list->FirstChild()->GetType() == nsGkAtoms::bulletFrame, + "bogus outside bullet list"); + return list; +} + nsFrameList* nsBlockFrame::GetPushedFloats() const { - if (!(GetStateBits() & NS_BLOCK_HAS_PUSHED_FLOATS)) { + if (!HasPushedFloats()) { return nsnull; } nsFrameList* result = @@ -4704,10 +4736,9 @@ nsBlockFrame::EnsurePushedFloats() nsFrameList* nsBlockFrame::RemovePushedFloats() { - if (!(GetStateBits() & NS_BLOCK_HAS_PUSHED_FLOATS)) { + if (!HasPushedFloats()) { return nsnull; } - nsFrameList *result = static_cast(Properties().Remove(PushedFloatProperty())); RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS); @@ -4839,16 +4870,14 @@ nsBlockFrame::AddFrames(nsFrameList& aFrameList, nsIFrame* aPrevSibling) // If we're inserting at the beginning of our list and we have an // inside bullet, insert after that bullet. - if (!aPrevSibling && mBullet && !HaveOutsideBullet()) { - NS_ASSERTION(!aFrameList.ContainsFrame(mBullet), - "Trying to make mBullet prev sibling to itself"); - aPrevSibling = mBullet; + if (!aPrevSibling && HasInsideBullet()) { + aPrevSibling = GetInsideBullet(); } nsIPresShell *presShell = PresContext()->PresShell(); // Attempt to find the line that contains the previous sibling - nsFrameList overflowFrames; + FrameLines* overflowLines; nsLineList* lineList = &mLines; nsLineList::iterator prevSibLine = lineList->end(); PRInt32 prevSiblingIndex = -1; @@ -4862,15 +4891,14 @@ nsBlockFrame::AddFrames(nsFrameList& aFrameList, nsIFrame* aPrevSibling) prevSibLine, mFrames.LastChild(), &prevSiblingIndex)) { // Not in mLines - try overflow lines. - lineList = GetOverflowLines(); - if (lineList) { - prevSibLine = lineList->end(); + overflowLines = GetOverflowLines(); + lineList = overflowLines ? &overflowLines->mLines : nsnull; + if (overflowLines) { + prevSibLine = overflowLines->mLines.end(); prevSiblingIndex = -1; - overflowFrames = nsFrameList(lineList->front()->mFirstChild, - lineList->back()->LastChild()); if (!nsLineBox::RFindLineContaining(aPrevSibling, lineList->begin(), prevSibLine, - overflowFrames.LastChild(), + overflowLines->mFrames.LastChild(), &prevSiblingIndex)) { lineList = nsnull; } @@ -4914,7 +4942,7 @@ nsBlockFrame::AddFrames(nsFrameList& aFrameList, nsIFrame* aPrevSibling) lineList->front()->MarkDirty(); lineList->front()->SetInvalidateTextRuns(true); } - nsFrameList& frames = lineList == &mLines ? mFrames : overflowFrames; + nsFrameList& frames = lineList == &mLines ? mFrames : overflowLines->mFrames; const nsFrameList::Slice& newFrames = frames.InsertFrames(nsnull, aPrevSibling, aFrameList); @@ -5129,28 +5157,38 @@ void nsBlockFrame::TryAllLines(nsLineList::iterator* aIterator, nsLineList::iterator* aStartIterator, nsLineList::iterator* aEndIterator, - bool* aInOverflowLines) { + bool* aInOverflowLines, + FrameLines** aOverflowLines) +{ if (*aIterator == *aEndIterator) { if (!*aInOverflowLines) { - *aInOverflowLines = true; // Try the overflow lines - nsLineList* overflowLines = GetOverflowLines(); - if (overflowLines) { - *aStartIterator = overflowLines->begin(); + *aInOverflowLines = true; + FrameLines* lines = GetOverflowLines(); + if (lines) { + *aStartIterator = lines->mLines.begin(); *aIterator = *aStartIterator; - *aEndIterator = overflowLines->end(); + *aEndIterator = lines->mLines.end(); + *aOverflowLines = lines; } } } } +nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame, + line_iterator aLine) + : mFrame(aFrame), mLine(aLine), mInOverflowLines(nsnull) +{ + // This will assert if aLine isn't in mLines of aFrame: + DebugOnly check = aLine == mFrame->begin_lines(); +} + nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame, line_iterator aLine, bool aInOverflow) : mFrame(aFrame), mLine(aLine), mInOverflowLines(nsnull) { if (aInOverflow) { - mInOverflowLines = aFrame->GetOverflowLines(); - NS_ASSERTION(mInOverflowLines, "How can we be in overflow if there isn't any?"); + mInOverflowLines = &aFrame->GetOverflowLines()->mLines; } } @@ -5297,7 +5335,8 @@ nsBlockInFlowLineIterator::Prev() mFrame = static_cast(mFrame->GetPrevInFlow()); if (!mFrame) return false; - mInOverflowLines = mFrame->GetOverflowLines(); + nsBlockFrame::FrameLines* overflowLines = mFrame->GetOverflowLines(); + mInOverflowLines = overflowLines ? &overflowLines->mLines : nsnull; if (mInOverflowLines) { mLine = mInOverflowLines->end(); NS_ASSERTION(mLine != mInOverflowLines->begin(), "empty overflow line list?"); @@ -5326,7 +5365,8 @@ nsBlockInFlowLineIterator::FindValidLine() if (mLine != mFrame->end_lines()) return true; } else { - mInOverflowLines = mFrame->GetOverflowLines(); + nsBlockFrame::FrameLines* overflowLines = mFrame->GetOverflowLines(); + mInOverflowLines = overflowLines ? &overflowLines->mLines : nsnull; if (mInOverflowLines) { mLine = mInOverflowLines->begin(); NS_ASSERTION(mLine != mInOverflowLines->end(), "empty overflow line list?"); @@ -5384,16 +5424,19 @@ nsBlockFrame::DoRemoveFrame(nsIFrame* aDeletedFrame, PRUint32 aFlags) nsLineList::iterator line_start = mLines.begin(), line_end = mLines.end(); nsLineList::iterator line = line_start; + FrameLines* overflowLines = nsnull; bool searchingOverflowList = false; // Make sure we look in the overflow lines even if the normal line // list is empty - TryAllLines(&line, &line_start, &line_end, &searchingOverflowList); + TryAllLines(&line, &line_start, &line_end, &searchingOverflowList, + &overflowLines); while (line != line_end) { if (line->Contains(aDeletedFrame)) { break; } ++line; - TryAllLines(&line, &line_start, &line_end, &searchingOverflowList); + TryAllLines(&line, &line_start, &line_end, &searchingOverflowList, + &overflowLines); } if (line == line_end) { @@ -5412,7 +5455,7 @@ nsBlockFrame::DoRemoveFrame(nsIFrame* aDeletedFrame, PRUint32 aFlags) } } - while ((line != line_end) && (nsnull != aDeletedFrame)) { + while (line != line_end && aDeletedFrame) { NS_ASSERTION(this == aDeletedFrame->GetParent(), "messed up delete code"); NS_ASSERTION(line->Contains(aDeletedFrame), "frame not in line"); @@ -5428,19 +5471,19 @@ nsBlockFrame::DoRemoveFrame(nsIFrame* aDeletedFrame, PRUint32 aFlags) line_iterator next = line.next(); nsIFrame* lastFrame = next != line_end ? next->mFirstChild->GetPrevSibling() : - (searchingOverflowList ? line->LastChild() : mFrames.LastChild()); + (searchingOverflowList ? overflowLines->mFrames.LastChild() : + mFrames.LastChild()); NS_ASSERTION(next == line_end || lastFrame == line->LastChild(), "unexpected line frames"); isLastFrameOnLine = lastFrame == aDeletedFrame; } // Remove aDeletedFrame from the line - nsIFrame* nextFrame = aDeletedFrame->GetNextSibling(); if (line->mFirstChild == aDeletedFrame) { // We should be setting this to null if aDeletedFrame // is the only frame on the line. HOWEVER in that case // we will be removing the line anyway, see below. - line->mFirstChild = nextFrame; + line->mFirstChild = aDeletedFrame->GetNextSibling(); } // Hmm, this won't do anything if we're removing a frame in the first @@ -5457,13 +5500,7 @@ nsBlockFrame::DoRemoveFrame(nsIFrame* aDeletedFrame, PRUint32 aFlags) // prevSibling will only be nsnull when we are deleting the very // first frame in the main or overflow list. if (searchingOverflowList) { - nsIFrame* prevSibling = aDeletedFrame->GetPrevSibling(); - if (prevSibling) { - // XXXbz If we switch overflow lines to nsFrameList, we should - // change this SetNextSibling call. - prevSibling->SetNextSibling(nextFrame); - } - aDeletedFrame->SetNextSibling(nsnull); + overflowLines->mFrames.RemoveFrame(aDeletedFrame); } else { mFrames.RemoveFrame(aDeletedFrame); } @@ -5518,12 +5555,13 @@ nsBlockFrame::DoRemoveFrame(nsIFrame* aDeletedFrame, PRUint32 aFlags) #endif Invalidate(visOverflow); } else { - nsLineList* lineList = RemoveOverflowLines(); - line = lineList->erase(line); - if (!lineList->empty()) { - SetOverflowLines(lineList); + // XXX update searchingOverflowList directly, remove only when empty + FrameLines* overflowLines = RemoveOverflowLines(); + line = overflowLines->mLines.erase(line); + if (!overflowLines->mLines.empty()) { + SetOverflowLines(overflowLines); } else { - delete lineList; + delete overflowLines; // We just invalidated our iterators. Since we were in // the overflow lines list, which is now empty, set them // so we're at the end of the regular line list. @@ -5577,7 +5615,8 @@ nsBlockFrame::DoRemoveFrame(nsIFrame* aDeletedFrame, PRUint32 aFlags) line = line_end; } - TryAllLines(&line, &line_start, &line_end, &searchingOverflowList); + TryAllLines(&line, &line_start, &line_end, &searchingOverflowList, + &overflowLines); #ifdef NOISY_REMOVE_FRAME printf("DoRemoveFrame: now on %s line=%p\n", searchingOverflowList?"overflow":"normal", line.get()); @@ -5628,25 +5667,22 @@ nsBlockFrame::StealFrame(nsPresContext* aPresContext, line_start = line, line_end = mLines.end(); bool searchingOverflowList = false; + FrameLines* overflowLines = nsnull; nsIFrame* prevSibling = nsnull; // Make sure we look in the overflow lines even if the normal line // list is empty - TryAllLines(&line, &line_start, &line_end, &searchingOverflowList); + TryAllLines(&line, &line_start, &line_end, &searchingOverflowList, + &overflowLines); while (line != line_end) { nsIFrame* frame = line->mFirstChild; PRInt32 n = line->GetChildCount(); while (--n >= 0) { if (frame == aChild) { - // Disconnect from sibling list if (frame == line->mFirstChild) { line->mFirstChild = frame->GetNextSibling(); } if (searchingOverflowList) { - // XXXbz If we switch overflow lines to nsFrameList, we should - // change this SetNextSibling call. - if (prevSibling) - prevSibling->SetNextSibling(frame->GetNextSibling()); - frame->SetNextSibling(nsnull); + overflowLines->mFrames.RemoveFrame(frame); } else { mFrames.RemoveFrame(frame); } @@ -5662,13 +5698,13 @@ nsBlockFrame::StealFrame(nsPresContext* aPresContext, nsLineBox* lineBox = line; if (searchingOverflowList) { // Erase line, but avoid making the overflow line list empty - nsLineList* lineList = RemoveOverflowLines(); - line = lineList->erase(line); - if (!lineList->empty()) { - nsresult rv = SetOverflowLines(lineList); - NS_ENSURE_SUCCESS(rv, rv); + // XXX update overflowLines directly, remove only when empty + RemoveOverflowLines(); + line = overflowLines->mLines.erase(line); + if (!overflowLines->mLines.empty()) { + SetOverflowLines(overflowLines); } else { - delete lineList; + delete overflowLines; // We just invalidated our iterators. Since we were in // the overflow lines list, which is now empty, set them // so we're at the end of the regular line list. @@ -5694,7 +5730,8 @@ nsBlockFrame::StealFrame(nsPresContext* aPresContext, frame = frame->GetNextSibling(); } ++line; - TryAllLines(&line, &line_start, &line_end, &searchingOverflowList); + TryAllLines(&line, &line_start, &line_end, &searchingOverflowList, + &overflowLines); if (prevSibling && !prevSibling->GetNextSibling()) { // We just switched to the overflow list. Null out prevSibling prevSibling = nsnull; @@ -6303,9 +6340,10 @@ nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, } } - if (NS_SUCCEEDED(rv) && (nsnull != mBullet) && HaveOutsideBullet()) { + if (NS_SUCCEEDED(rv) && HasOutsideBullet()) { // Display outside bullets manually - rv = BuildDisplayListForChild(aBuilder, mBullet, aDirtyRect, aLists); + nsIFrame* bullet = GetOutsideBullet(); + rv = BuildDisplayListForChild(aBuilder, bullet, aDirtyRect, aLists); } #ifdef DEBUG @@ -6349,7 +6387,7 @@ nsBlockFrame::CreateAccessible() presContext->PresShell()); } - if (!mBullet || !presContext) { + if (!HasBullet() || !presContext) { if (!mContent->GetParent()) { // Don't create accessible objects for the root content node, they are redundant with // the nsDocAccessible object created with the document node @@ -6437,7 +6475,7 @@ nsBlockFrame::ChildIsDirty(nsIFrame* aChild) if (aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW && aChild->GetStyleDisplay()->IsAbsolutelyPositioned()) { // do nothing - } else if (aChild == mBullet && HaveOutsideBullet()) { + } else if (aChild == GetOutsideBullet()) { // The bullet lives in the first line, unless the first line has // height 0 and there is a second line, in which case it lives // in the second line. @@ -6472,15 +6510,9 @@ nsBlockFrame::Init(nsIContent* aContent, nsIFrame* aPrevInFlow) { if (aPrevInFlow) { - // Copy over the block frame type flags - nsBlockFrame* blockFrame = (nsBlockFrame*)aPrevInFlow; - - // Don't copy NS_BLOCK_HAS_FIRST_LETTER_CHILD as that is set on the first - // continuation only. - SetFlags(blockFrame->mState & - (NS_BLOCK_FLAGS_MASK & - (~NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET & - ~NS_BLOCK_HAS_FIRST_LETTER_CHILD))); + // Copy over the inherited block frame bits from the prev-in-flow. + SetFlags(aPrevInFlow->GetStateBits() & + (NS_BLOCK_FLAGS_MASK & ~NS_BLOCK_FLAGS_NON_INHERITED_MASK)); } nsresult rv = nsBlockFrameSuper::Init(aContent, aParent, aPrevInFlow); @@ -6496,6 +6528,11 @@ NS_IMETHODIMP nsBlockFrame::SetInitialChildList(ChildListID aListID, nsFrameList& aChildList) { + NS_ASSERTION(aListID != kPrincipalList || + (GetStateBits() & (NS_BLOCK_FRAME_HAS_INSIDE_BULLET | + NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET)) == 0, + "how can we have a bullet already?"); + nsresult rv = NS_OK; if (kAbsoluteList == aListID) { @@ -6554,10 +6591,9 @@ nsBlockFrame::SetInitialChildList(ChildListID aListID, } possibleListItem = parent; } - if ((nsnull == GetPrevInFlow()) && - (NS_STYLE_DISPLAY_LIST_ITEM == - possibleListItem->GetStyleDisplay()->mDisplay) && - (nsnull == mBullet)) { + if (NS_STYLE_DISPLAY_LIST_ITEM == + possibleListItem->GetStyleDisplay()->mDisplay && + !GetPrevInFlow()) { // Resolve style for the bullet frame const nsStyleList* styleList = GetStyleList(); nsCSSPseudoElements::Type pseudoType; @@ -6583,7 +6619,7 @@ nsBlockFrame::SetInitialChildList(ChildListID aListID, // Create bullet frame nsBulletFrame* bullet = new (shell) nsBulletFrame(kidSC); - if (nsnull == bullet) { + if (!bullet) { return NS_ERROR_OUT_OF_MEMORY; } bullet->Init(mContent, this, nsnull); @@ -6591,16 +6627,16 @@ nsBlockFrame::SetInitialChildList(ChildListID aListID, // If the list bullet frame should be positioned inside then add // it to the flow now. if (NS_STYLE_LIST_STYLE_POSITION_INSIDE == - styleList->mListStylePosition) { + styleList->mListStylePosition) { nsFrameList bulletList(bullet, bullet); AddFrames(bulletList, nsnull); - mState &= ~NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET; + Properties().Set(InsideBulletProperty(), bullet); + AddStateBits(NS_BLOCK_FRAME_HAS_INSIDE_BULLET); + } else { + nsFrameList* bulletList = new nsFrameList(bullet, bullet); + Properties().Set(OutsideBulletProperty(), bulletList); + AddStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET); } - else { - mState |= NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET; - } - - mBullet = bullet; } } @@ -6611,8 +6647,7 @@ bool nsBlockFrame::BulletIsEmpty() const { NS_ASSERTION(mContent->GetPrimaryFrame()->GetStyleDisplay()->mDisplay == - NS_STYLE_DISPLAY_LIST_ITEM && - HaveOutsideBullet(), + NS_STYLE_DISPLAY_LIST_ITEM && HasOutsideBullet(), "should only care when we have an outside bullet"); const nsStyleList* list = GetStyleList(); return list->mListStyleType == NS_STYLE_LIST_STYLE_NONE && @@ -6636,24 +6671,15 @@ nsBlockFrame::GetBulletText(nsAString& aText) const aText.Assign(kSquareCharacter); } else if (myList->mListStyleType != NS_STYLE_LIST_STYLE_NONE) { - nsAutoString text; - mBullet->GetListItemText(*myList, text); - aText = text; + nsBulletFrame* bullet = GetBullet(); + if (bullet) { + nsAutoString text; + bullet->GetListItemText(*myList, text); + aText = text; + } } } -bool -nsBlockFrame::HasBullet() const -{ - if (mBullet) { - const nsStyleList* styleList = GetStyleList(); - return styleList->GetListStyleImage() || - styleList->mListStyleType != NS_STYLE_LIST_STYLE_NONE; - } - - return false; -} - // static bool nsBlockFrame::FrameStartsCounterScope(nsIFrame* aFrame) @@ -6761,15 +6787,15 @@ nsBlockFrame::RenumberListsFor(nsPresContext* aPresContext, // something foreign has crept in. nsBlockFrame* listItem = nsLayoutUtils::GetAsBlock(kid); if (listItem) { - if (nsnull != listItem->mBullet) { + nsBulletFrame* bullet = listItem->GetBullet(); + if (bullet) { bool changed; - *aOrdinal = listItem->mBullet->SetListItemOrdinal(*aOrdinal, - &changed); + *aOrdinal = bullet->SetListItemOrdinal(*aOrdinal, &changed); if (changed) { kidRenumberedABullet = true; // The ordinal changed - mark the bullet frame dirty. - listItem->ChildIsDirty(listItem->mBullet); + listItem->ChildIsDirty(bullet); } } @@ -6801,7 +6827,8 @@ nsBlockFrame::RenumberListsFor(nsPresContext* aPresContext, } void -nsBlockFrame::ReflowBullet(nsBlockReflowState& aState, +nsBlockFrame::ReflowBullet(nsIFrame* aBulletFrame, + nsBlockReflowState& aState, nsHTMLReflowMetrics& aMetrics, nscoord aLineTop) { @@ -6817,10 +6844,10 @@ nsBlockFrame::ReflowBullet(nsBlockReflowState& aState, // XXXwaterson Should this look just like the logic in // nsBlockReflowContext::ReflowBlock and nsLineLayout::ReflowFrame? nsHTMLReflowState reflowState(aState.mPresContext, rs, - mBullet, availSize); + aBulletFrame, availSize); nsReflowStatus status; - mBullet->WillReflow(aState.mPresContext); - mBullet->Reflow(aState.mPresContext, aMetrics, reflowState, status); + aBulletFrame->WillReflow(aState.mPresContext); + aBulletFrame->Reflow(aState.mPresContext, aMetrics, reflowState, status); // Get the float available space using our saved state from before we // started reflowing the block, so that we ignore any floats inside @@ -6860,8 +6887,9 @@ nsBlockFrame::ReflowBullet(nsBlockReflowState& aState, // Approximate the bullets position; vertical alignment will provide // the final vertical location. nscoord y = aState.mContentArea.y; - mBullet->SetRect(nsRect(x, y, aMetrics.width, aMetrics.height)); - mBullet->DidReflow(aState.mPresContext, &aState.mReflowState, NS_FRAME_REFLOW_FINISHED); + aBulletFrame->SetRect(nsRect(x, y, aMetrics.width, aMetrics.height)); + aBulletFrame->DidReflow(aState.mPresContext, &aState.mReflowState, + NS_FRAME_REFLOW_FINISHED); } // This is used to scan frames for any float placeholders, add their @@ -7175,14 +7203,19 @@ nsBlockFrame::VerifyLines(bool aFinalCheckOK) void nsBlockFrame::VerifyOverflowSituation() { - nsBlockFrame* flow = (nsBlockFrame*) GetFirstInFlow(); - while (nsnull != flow) { - nsLineList* overflowLines = GetOverflowLines(); - if (nsnull != overflowLines) { - NS_ASSERTION(! overflowLines->empty(), "should not be empty if present"); - NS_ASSERTION(overflowLines->front()->mFirstChild, "bad overflow list"); + nsBlockFrame* flow = static_cast(GetFirstInFlow()); + while (flow) { + FrameLines* overflowLines = GetOverflowLines(); + if (overflowLines) { + NS_ASSERTION(!overflowLines->mLines.empty(), + "should not be empty if present"); + NS_ASSERTION(overflowLines->mLines.front()->mFirstChild, + "bad overflow lines"); + NS_ASSERTION(overflowLines->mLines.front()->mFirstChild == + overflowLines->mFrames.FirstChild(), + "bad overflow frames / lines"); } - flow = (nsBlockFrame*) flow->GetNextInFlow(); + flow = static_cast(flow->GetNextInFlow()); } } diff --git a/layout/generic/nsBlockFrame.h b/layout/generic/nsBlockFrame.h index 836e3a10d7f..b61207fdc3b 100644 --- a/layout/generic/nsBlockFrame.h +++ b/layout/generic/nsBlockFrame.h @@ -154,11 +154,6 @@ public: friend nsIFrame* NS_NewBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, PRUint32 aFlags); - // This is a child list too, but we let nsBlockReflowState get to it - // directly too. - NS_DECLARE_FRAME_PROPERTY(PushedFloatProperty, - nsContainerFrame::DestroyFrameList) - // nsQueryFrame NS_DECL_QUERYFRAME @@ -175,7 +170,7 @@ public: nsFrameList& aFrameList); NS_IMETHOD RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame); - virtual nsFrameList GetChildList(ChildListID aListID) const; + virtual const nsFrameList& GetChildList(ChildListID aListID) const; virtual void GetChildLists(nsTArray* aLists) const; virtual nscoord GetBaseline() const; virtual nscoord GetCaretBaseline() const; @@ -246,12 +241,14 @@ public: /** * Return the bullet text equivalent. */ - virtual void GetBulletText(nsAString& aText) const; + void GetBulletText(nsAString& aText) const; /** * Return true if there's a bullet. */ - virtual bool HasBullet() const; + bool HasBullet() const { + return HasOutsideBullet() || HasInsideBullet(); + } virtual void MarkIntrinsicWidthsDirty(); virtual nscoord GetMinWidth(nsRenderingContext *aRenderingContext); @@ -328,6 +325,11 @@ public: */ static nsBlockFrame* GetNearestAncestorBlock(nsIFrame* aCandidate); + struct FrameLines { + nsLineList mLines; + nsFrameList mFrames; + }; + protected: nsBlockFrame(nsStyleContext* aContext) : nsContainerFrame(aContext) @@ -355,22 +357,14 @@ protected: void TryAllLines(nsLineList::iterator* aIterator, nsLineList::iterator* aStartIterator, nsLineList::iterator* aEndIterator, - bool* aInOverflowLines); + bool* aInOverflowLines, + FrameLines** aOverflowLines); void SetFlags(nsFrameState aFlags) { mState &= ~NS_BLOCK_FLAGS_MASK; mState |= aFlags; } - bool HaveOutsideBullet() const { -#if defined(DEBUG) && !defined(DEBUG_rods) - if(mState & NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET) { - NS_ASSERTION(mBullet,"NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET flag set and no mBullet"); - } -#endif - return 0 != (mState & NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET); - } - /** move the frames contained by aLine by aDY * if aLine is a block, its child floats are added to the state manager */ @@ -646,8 +640,14 @@ protected: nsLineBox* aLine, nsBlockFrame* aFromContainer, bool aFromOverflowLine, + nsFrameList& aFromFrameList, nsLineList::iterator aFromLine); + /** + * Push the line after aLineBefore to the overflow line list. + * @param aLineBefore a line in 'mLines' (or begin_lines() when + * pushing the first line) + */ void PushLines(nsBlockReflowState& aState, nsLineList::iterator aLineBefore); @@ -675,7 +675,8 @@ protected: static bool FrameStartsCounterScope(nsIFrame* aFrame); - void ReflowBullet(nsBlockReflowState& aState, + void ReflowBullet(nsIFrame* aBulletFrame, + nsBlockReflowState& aState, nsHTMLReflowMetrics& aMetrics, nscoord aLineTop); @@ -684,10 +685,14 @@ protected: virtual nsILineIterator* GetLineIterator(); public: - nsLineList* GetOverflowLines() const; + bool HasOverflowLines() const { + return 0 != (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_LINES); + } + FrameLines* GetOverflowLines() const; protected: - nsLineList* RemoveOverflowLines(); - nsresult SetOverflowLines(nsLineList* aOverflowLines); + FrameLines* RemoveOverflowLines(); + void SetOverflowLines(FrameLines* aOverflowLines); + void DestroyOverflowLines(); // Determine the computed height that's in effect for this block // frame (that is, our computed height minus the heights of our @@ -724,6 +729,50 @@ protected: nsFrameList* GetOverflowOutOfFlows() const; void SetOverflowOutOfFlows(const nsFrameList& aList, nsFrameList* aPropValue); + /** + * @return true if this frame has an inside bullet frame. + */ + bool HasInsideBullet() const { + return 0 != (mState & NS_BLOCK_FRAME_HAS_INSIDE_BULLET); + } + + /** + * @return the inside bullet frame or nsnull if we don't have one. + */ + nsBulletFrame* GetInsideBullet() const; + + /** + * @return true if this frame has an outside bullet frame. + */ + bool HasOutsideBullet() const { + return 0 != (mState & NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET); + } + + /** + * @return the outside bullet frame or nsnull if we don't have one. + */ + nsBulletFrame* GetOutsideBullet() const; + + /** + * @return the outside bullet frame list frame property. + */ + nsFrameList* GetOutsideBulletList() const; + + /** + * @return the bullet frame or nsnull if we don't have one. + */ + nsBulletFrame* GetBullet() const { + nsBulletFrame* outside = GetOutsideBullet(); + return outside ? outside : GetInsideBullet(); + } + + /** + * @return true if this frame has pushed floats. + */ + bool HasPushedFloats() const { + return 0 != (GetStateBits() & NS_BLOCK_HAS_PUSHED_FLOATS); + } + // Get the pushed floats list nsFrameList* GetPushedFloats() const; // Get the pushed floats list, or if there is not currently one, @@ -743,12 +792,9 @@ protected: nsLineList mLines; // List of all floats in this block + // XXXmats blocks rarely have floats, make it a frame property nsFrameList mFloats; - // XXX_fix_me: subclass one more time! - // For list-item frames, this is the bullet frame. - nsBulletFrame* mBullet; - friend class nsBlockReflowState; friend class nsBlockInFlowLineIterator; @@ -798,7 +844,11 @@ private: class nsBlockInFlowLineIterator { public: typedef nsBlockFrame::line_iterator line_iterator; - nsBlockInFlowLineIterator(nsBlockFrame* aFrame, line_iterator aLine, bool aInOverflow); + /** + * Set up the iterator to point to aLine which must be a normal line + * in aFrame (not an overflow line). + */ + nsBlockInFlowLineIterator(nsBlockFrame* aFrame, line_iterator aLine); /** * Set up the iterator to point to the first line found starting from * aFrame. Sets aFoundValidLine to false if there is no such line. @@ -845,6 +895,10 @@ public: bool Prev(); private: + friend class nsBlockFrame; + // XXX nsBlockFrame uses this internally in one place. Try to remove it. + nsBlockInFlowLineIterator(nsBlockFrame* aFrame, line_iterator aLine, bool aInOverflow); + nsBlockFrame* mFrame; line_iterator mLine; nsLineList* mInOverflowLines; diff --git a/layout/generic/nsBlockReflowContext.cpp b/layout/generic/nsBlockReflowContext.cpp index 3ed89cc2ce1..abd16f83301 100644 --- a/layout/generic/nsBlockReflowContext.cpp +++ b/layout/generic/nsBlockReflowContext.cpp @@ -124,7 +124,8 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS, nsBlockFrame::line_iterator line_end; bool anyLines = true; if (overflowLines) { - nsLineList* lines = block->GetOverflowLines(); + nsBlockFrame::FrameLines* frames = block->GetOverflowLines(); + nsLineList* lines = frames ? &frames->mLines : nsnull; if (!lines) { anyLines = false; } else { diff --git a/layout/generic/nsContainerFrame.cpp b/layout/generic/nsContainerFrame.cpp index 8a4193b3257..8efb9538e6d 100644 --- a/layout/generic/nsContainerFrame.cpp +++ b/layout/generic/nsContainerFrame.cpp @@ -273,7 +273,7 @@ nsContainerFrame::DestroyFrom(nsIFrame* aDestructRoot) ///////////////////////////////////////////////////////////////////////////// // Child frame enumeration -nsFrameList +const nsFrameList& nsContainerFrame::GetChildList(ChildListID aListID) const { // We only know about the principal child list and the overflow lists. diff --git a/layout/generic/nsContainerFrame.h b/layout/generic/nsContainerFrame.h index b8e5282a216..b3b559d6e0a 100644 --- a/layout/generic/nsContainerFrame.h +++ b/layout/generic/nsContainerFrame.h @@ -95,7 +95,7 @@ public: NS_IMETHOD RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame); - virtual nsFrameList GetChildList(ChildListID aList) const; + virtual const nsFrameList& GetChildList(ChildListID aList) const; virtual void GetChildLists(nsTArray* aLists) const; virtual void DestroyFrom(nsIFrame* aDestructRoot); virtual void ChildIsDirty(nsIFrame* aChild); diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index fc486698fc5..a82bc6afde0 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -1187,7 +1187,7 @@ nsFrame::GetBaseline() const return mRect.height + GetUsedMargin().bottom; } -nsFrameList +const nsFrameList& nsFrame::GetChildList(ChildListID aListID) const { if (IsAbsoluteContainer() && diff --git a/layout/generic/nsFrame.h b/layout/generic/nsFrame.h index d75e62af473..fbb922bc29b 100644 --- a/layout/generic/nsFrame.h +++ b/layout/generic/nsFrame.h @@ -190,7 +190,7 @@ public: nsStyleContext* aStyleContext); virtual void SetParent(nsIFrame* aParent); virtual nscoord GetBaseline() const; - virtual nsFrameList GetChildList(ChildListID aListID) const; + virtual const nsFrameList& GetChildList(ChildListID aListID) const; virtual void GetChildLists(nsTArray* aLists) const; NS_IMETHOD HandleEvent(nsPresContext* aPresContext, diff --git a/layout/generic/nsHTMLParts.h b/layout/generic/nsHTMLParts.h index 7c5dbd8163b..4a282fc6d29 100644 --- a/layout/generic/nsHTMLParts.h +++ b/layout/generic/nsHTMLParts.h @@ -73,6 +73,10 @@ class nsTableColFrame; * frame among the block's descendants. If there is a floating first-letter * frame, or the block has first-letter style but has no first letter, this * bit is not set. This bit is set on the first continuation only. + * + * NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET and NS_BLOCK_FRAME_HAS_INSIDE_BULLET + * means the block has an associated bullet frame, they are mutually exclusive. + * */ #define NS_BLOCK_MARGIN_ROOT NS_FRAME_STATE_BIT(22) #define NS_BLOCK_FLOAT_MGR NS_FRAME_STATE_BIT(23) @@ -80,14 +84,25 @@ class nsTableColFrame; #define NS_BLOCK_HAS_FIRST_LETTER_STYLE NS_FRAME_STATE_BIT(29) #define NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET NS_FRAME_STATE_BIT(30) #define NS_BLOCK_HAS_FIRST_LETTER_CHILD NS_FRAME_STATE_BIT(31) -// These are the bits that get inherited from a block frame to its -// next-in-flows and are not private to blocks -#define NS_BLOCK_FLAGS_MASK (NS_BLOCK_MARGIN_ROOT | \ - NS_BLOCK_FLOAT_MGR | \ - NS_BLOCK_CLIP_PAGINATED_OVERFLOW | \ - NS_BLOCK_HAS_FIRST_LETTER_STYLE | \ - NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET | \ - NS_BLOCK_HAS_FIRST_LETTER_CHILD) +#define NS_BLOCK_FRAME_HAS_INSIDE_BULLET NS_FRAME_STATE_BIT(63) +// These are all the block specific frame bits, they are copied from +// the prev-in-flow to a newly created next-in-flow, except for the +// NS_BLOCK_FLAGS_NON_INHERITED_MASK bits below. +#define NS_BLOCK_FLAGS_MASK (NS_BLOCK_MARGIN_ROOT | \ + NS_BLOCK_FLOAT_MGR | \ + NS_BLOCK_CLIP_PAGINATED_OVERFLOW | \ + NS_BLOCK_HAS_FIRST_LETTER_STYLE | \ + NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET | \ + NS_BLOCK_HAS_FIRST_LETTER_CHILD | \ + NS_BLOCK_FRAME_HAS_INSIDE_BULLET) + +// This is the subset of NS_BLOCK_FLAGS_MASK that is NOT inherited +// by default. They should only be set on the first-in-flow. +// See nsBlockFrame::Init. +#define NS_BLOCK_FLAGS_NON_INHERITED_MASK \ + (NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET | \ + NS_BLOCK_HAS_FIRST_LETTER_CHILD | \ + NS_BLOCK_FRAME_HAS_INSIDE_BULLET) // Factory methods for creating html layout objects diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index ab8112bc05e..ced7251601d 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -1057,12 +1057,8 @@ public: * @return the child list. If the requested list is unsupported by this * frame type, an empty list will be returned. */ - // XXXbz if all our frame storage were actually backed by nsFrameList, we - // could make this return a const reference... nsBlockFrame is the only real - // culprit here. Make sure to assign the return value of this function into - // a |const nsFrameList&|, not an nsFrameList. - virtual nsFrameList GetChildList(ChildListID aListID) const = 0; - nsFrameList PrincipalChildList() { return GetChildList(kPrincipalList); } + virtual const nsFrameList& GetChildList(ChildListID aListID) const = 0; + const nsFrameList& PrincipalChildList() { return GetChildList(kPrincipalList); } virtual void GetChildLists(nsTArray* aLists) const = 0; // XXXbz this method should go away nsIFrame* GetFirstChild(ChildListID aListID) const { diff --git a/layout/generic/nsLineBox.cpp b/layout/generic/nsLineBox.cpp index 6e4010c8c62..6ef3a1fc40b 100644 --- a/layout/generic/nsLineBox.cpp +++ b/layout/generic/nsLineBox.cpp @@ -235,6 +235,7 @@ nsLineBox::List(FILE* out, PRInt32 aIndent) const } #endif +#ifdef DEBUG nsIFrame* nsLineBox::LastChild() const { @@ -245,13 +246,7 @@ nsLineBox::LastChild() const } return frame; } - -bool -nsLineBox::IsLastChild(nsIFrame* aFrame) const -{ - nsIFrame* lastFrame = LastChild(); - return aFrame == lastFrame; -} +#endif PRInt32 nsLineBox::IndexOf(nsIFrame* aFrame) const @@ -373,7 +368,7 @@ nsLineBox::RFindLineContaining(nsIFrame* aFrame, nsIFrame* curFrame = aLastFrameBeforeEnd; while (aBegin != aEnd) { --aEnd; - NS_ASSERTION(aEnd->IsLastChild(curFrame), "Unexpected curFrame"); + NS_ASSERTION(aEnd->LastChild() == curFrame, "Unexpected curFrame"); // i is the index of curFrame in aEnd PRInt32 i = aEnd->GetChildCount() - 1; while (i >= 0) { diff --git a/layout/generic/nsLineBox.h b/layout/generic/nsLineBox.h index ef88e822bd8..f20023e3087 100644 --- a/layout/generic/nsLineBox.h +++ b/layout/generic/nsLineBox.h @@ -470,11 +470,8 @@ public: char* StateToString(char* aBuf, PRInt32 aBufSize) const; void List(FILE* out, PRInt32 aIndent) const; -#endif - nsIFrame* LastChild() const; - - bool IsLastChild(nsIFrame* aFrame) const; +#endif PRInt32 IndexOf(nsIFrame* aFrame) const; diff --git a/layout/generic/nsObjectFrame.cpp b/layout/generic/nsObjectFrame.cpp index d24cdb12400..24c494c3c09 100644 --- a/layout/generic/nsObjectFrame.cpp +++ b/layout/generic/nsObjectFrame.cpp @@ -351,6 +351,9 @@ nsObjectFrame::DestroyFrom(nsIFrame* aDestructRoot) mBackgroundSink->Destroy(); } + if (mInstanceOwner) { + mInstanceOwner->SetFrame(nsnull); + } SetInstanceOwner(nsnull); nsObjectFrameSuper::DestroyFrom(aDestructRoot); diff --git a/layout/generic/nsTextFrameThebes.cpp b/layout/generic/nsTextFrameThebes.cpp index b57d5af5193..c3a7acede07 100644 --- a/layout/generic/nsTextFrameThebes.cpp +++ b/layout/generic/nsTextFrameThebes.cpp @@ -1212,7 +1212,7 @@ BuildTextRuns(gfxContext* aContext, nsTextFrame* aForFrame, bool isValid = true; nsBlockInFlowLineIterator backIterator(block, &isValid); if (aForFrameLine) { - backIterator = nsBlockInFlowLineIterator(block, *aForFrameLine, false); + backIterator = nsBlockInFlowLineIterator(block, *aForFrameLine); } else { backIterator = nsBlockInFlowLineIterator(block, lineContainerChild, &isValid); NS_ASSERTION(isValid, "aForFrame not found in block, someone lied to us"); diff --git a/layout/inspector/src/inDOMView.cpp b/layout/inspector/src/inDOMView.cpp index bbe38562524..0998b9a1a8f 100644 --- a/layout/inspector/src/inDOMView.cpp +++ b/layout/inspector/src/inDOMView.cpp @@ -142,7 +142,7 @@ inDOMView::~inDOMView() /* static */ void inDOMView::InitAtoms() { - NS_RegisterStaticAtoms(Atoms_info, ArrayLength(Atoms_info)); + NS_RegisterStaticAtoms(Atoms_info); } //////////////////////////////////////////////////////////////////////// diff --git a/layout/mathml/nsMathMLContainerFrame.cpp b/layout/mathml/nsMathMLContainerFrame.cpp index 2f2c665d6bd..4b0231ac7c0 100644 --- a/layout/mathml/nsMathMLContainerFrame.cpp +++ b/layout/mathml/nsMathMLContainerFrame.cpp @@ -1107,7 +1107,7 @@ static PRInt32 kInterFrameSpacingTable[eMathMLFrameType_COUNT][eMathMLFrameType_ /*OpUsr*/ {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01}, /*Inner*/ {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01}, /*Italic*/ {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01}, - /*Upright*/ {0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01} + /*Upright*/ {0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00} }; #define GET_INTERSPACE(scriptlevel_, frametype1_, frametype2_, space_) \ diff --git a/layout/reftests/bidi/reftest.list b/layout/reftests/bidi/reftest.list index 5d635c0162d..bcf98e6e4cd 100644 --- a/layout/reftests/bidi/reftest.list +++ b/layout/reftests/bidi/reftest.list @@ -7,8 +7,8 @@ == bidi-004.html bidi-004-ref.html == bidi-004-j.html bidi-004-ref.html == bidi-005.html bidi-005-ref.html -== bidi-006.html bidi-006-ref.html -== bidi-006-j.html bidi-006-ref.html +random-if(cocoaWidget) == bidi-006.html bidi-006-ref.html # bug 734313 +random-if(cocoaWidget) == bidi-006-j.html bidi-006-ref.html # bug 734313 == bidiSVG-01.svg bidiSVG-01-ref.svg == bidiSVG-02.svg bidiSVG-02-ref.svg == bidiSVG-03.svg bidiSVG-03-ref.svg @@ -34,8 +34,8 @@ random-if(cocoaWidget) == mirroring-02.html mirroring-02-ref.html == unicode-bidi-anonymous-002.html unicode-bidi-anonymous-002-ref.html == with-first-letter-1a.html with-first-letter-1-ref.html == with-first-letter-1b.html with-first-letter-1-ref.html -== with-first-letter-2a.html with-first-letter-2-ref.html -== with-first-letter-2b.html with-first-letter-2-ref.html +random-if(cocoaWidget) == with-first-letter-2a.html with-first-letter-2-ref.html # bug 734313 +random-if(cocoaWidget) == with-first-letter-2b.html with-first-letter-2-ref.html # bug 734313 == 83958-1a.html 83958-1-ref.html == 83958-1b.html 83958-1-ref.html == 83958-1c.html 83958-1-ref.html diff --git a/layout/reftests/bugs/731726-1-ref.html b/layout/reftests/bugs/731726-1-ref.html new file mode 100644 index 00000000000..34a58f1a9ef --- /dev/null +++ b/layout/reftests/bugs/731726-1-ref.html @@ -0,0 +1,13 @@ + + + + + + + +

    + + + diff --git a/layout/reftests/bugs/731726-1.html b/layout/reftests/bugs/731726-1.html new file mode 100644 index 00000000000..a016e62ae8a --- /dev/null +++ b/layout/reftests/bugs/731726-1.html @@ -0,0 +1,17 @@ + + + + + + +
    + +
    + + + diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 88a08c269cd..f1a137827b3 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1694,3 +1694,4 @@ fuzzy-if(d2d,1,19) fuzzy-if(cocoaWidget,1,170) == 718521.html 718521-ref.html == 720987.html 720987-ref.html == 722923-1.html 722923-1-ref.html == 729143-1.html 729143-1-ref.html +needs-focus == 731726-1.html 731726-1-ref.html diff --git a/layout/style/nsCSSAnonBoxes.cpp b/layout/style/nsCSSAnonBoxes.cpp index a96b43ef52b..b346ae0f1b4 100644 --- a/layout/style/nsCSSAnonBoxes.cpp +++ b/layout/style/nsCSSAnonBoxes.cpp @@ -68,8 +68,7 @@ static const nsStaticAtom CSSAnonBoxes_info[] = { void nsCSSAnonBoxes::AddRefAtoms() { - NS_RegisterStaticAtoms(CSSAnonBoxes_info, - ArrayLength(CSSAnonBoxes_info)); + NS_RegisterStaticAtoms(CSSAnonBoxes_info); } bool nsCSSAnonBoxes::IsAnonBox(nsIAtom *aAtom) diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 8c36e65af73..2a22ad214c9 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -735,7 +735,7 @@ static void AppendRuleToSheet(css::Rule* aRule, void* aParser) mScanner.ReportUnexpected(#msg_) #define REPORT_UNEXPECTED_P(msg_, params_) \ - mScanner.ReportUnexpectedParams(#msg_, params_, ArrayLength(params_)) + mScanner.ReportUnexpectedParams(#msg_, params_) #define REPORT_UNEXPECTED_EOF(lf_) \ mScanner.ReportUnexpectedEOF(#lf_) diff --git a/layout/style/nsCSSPseudoClasses.cpp b/layout/style/nsCSSPseudoClasses.cpp index 7ea0e69fd9c..3f6b600c1dd 100644 --- a/layout/style/nsCSSPseudoClasses.cpp +++ b/layout/style/nsCSSPseudoClasses.cpp @@ -66,8 +66,7 @@ static const nsStaticAtom CSSPseudoClasses_info[] = { void nsCSSPseudoClasses::AddRefAtoms() { - NS_RegisterStaticAtoms(CSSPseudoClasses_info, - ArrayLength(CSSPseudoClasses_info)); + NS_RegisterStaticAtoms(CSSPseudoClasses_info); } bool diff --git a/layout/style/nsCSSPseudoElements.cpp b/layout/style/nsCSSPseudoElements.cpp index 4dd1172d903..bbaf46facf1 100644 --- a/layout/style/nsCSSPseudoElements.cpp +++ b/layout/style/nsCSSPseudoElements.cpp @@ -80,8 +80,7 @@ static const PRUint32 CSSPseudoElements_flags[] = { void nsCSSPseudoElements::AddRefAtoms() { - NS_RegisterStaticAtoms(CSSPseudoElements_info, - ArrayLength(CSSPseudoElements_info)); + NS_RegisterStaticAtoms(CSSPseudoElements_info); } bool nsCSSPseudoElements::IsPseudoElement(nsIAtom *aAtom) diff --git a/layout/style/nsCSSScanner.cpp b/layout/style/nsCSSScanner.cpp index b8fb0b55655..d157e91a605 100644 --- a/layout/style/nsCSSScanner.cpp +++ b/layout/style/nsCSSScanner.cpp @@ -547,7 +547,7 @@ nsCSSScanner::ReportUnexpectedToken(nsCSSToken& tok, tokenString.get() }; - ReportUnexpectedParams(aMessage, params, ArrayLength(params)); + ReportUnexpectedParams(aMessage, params); } // aParams's first entry must be null, and we'll fill in the token diff --git a/layout/style/nsCSSScanner.h b/layout/style/nsCSSScanner.h index 6871c10b887..b9897a6065b 100644 --- a/layout/style/nsCSSScanner.h +++ b/layout/style/nsCSSScanner.h @@ -160,9 +160,19 @@ class nsCSSScanner { // aMessage must take no parameters void ReportUnexpected(const char* aMessage); + +private: void ReportUnexpectedParams(const char* aMessage, - const PRUnichar **aParams, + const PRUnichar** aParams, PRUint32 aParamsLength); + +public: + template + void ReportUnexpectedParams(const char* aMessage, + const PRUnichar* (&aParams)[N]) + { + return ReportUnexpectedParams(aMessage, aParams, N); + } // aLookingFor is a plain string, not a format string void ReportUnexpectedEOF(const char* aLookingFor); // aLookingFor is a single character diff --git a/layout/svg/base/src/nsSVGUtils.cpp b/layout/svg/base/src/nsSVGUtils.cpp index 984cb86b18d..9603735e899 100644 --- a/layout/svg/base/src/nsSVGUtils.cpp +++ b/layout/svg/base/src/nsSVGUtils.cpp @@ -1215,6 +1215,11 @@ nsSVGUtils::HitTestChildren(nsIFrame *aFrame, const nsPoint &aPoint) current = current->GetPrevSibling()) { nsISVGChildFrame* SVGFrame = do_QueryFrame(current); if (SVGFrame) { + const nsIContent* content = current->GetContent(); + if (content->IsSVG() && + !static_cast(content)->HasValidDimensions()) { + continue; + } result = SVGFrame->GetFrameForPoint(aPoint); if (result) break; diff --git a/layout/svg/crashtests/732836-1.svg b/layout/svg/crashtests/732836-1.svg new file mode 100644 index 00000000000..a9abb46668c --- /dev/null +++ b/layout/svg/crashtests/732836-1.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/layout/svg/crashtests/crashtests.list b/layout/svg/crashtests/crashtests.list index ed07648dd11..467879c3006 100644 --- a/layout/svg/crashtests/crashtests.list +++ b/layout/svg/crashtests/crashtests.list @@ -124,3 +124,4 @@ load 709920-2.svg load 713413-1.svg load 722003-1.svg load 725918-1.svg +load 732836-1.svg diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp index 53062d11451..66436822ce0 100644 --- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -1045,7 +1045,7 @@ nsTableFrame::InsertRowGroups(const nsFrameList::Slice& aRowGroups) ///////////////////////////////////////////////////////////////////////////// // Child frame enumeration -nsFrameList +const nsFrameList& nsTableFrame::GetChildList(ChildListID aListID) const { if (aListID == kColGroupList) { diff --git a/layout/tables/nsTableFrame.h b/layout/tables/nsTableFrame.h index 872ca469b92..e52d8f868e3 100644 --- a/layout/tables/nsTableFrame.h +++ b/layout/tables/nsTableFrame.h @@ -244,7 +244,7 @@ public: NS_IMETHOD SetInitialChildList(ChildListID aListID, nsFrameList& aChildList); - virtual nsFrameList GetChildList(ChildListID aListID) const; + virtual const nsFrameList& GetChildList(ChildListID aListID) const; virtual void GetChildLists(nsTArray* aLists) const; NS_IMETHOD BuildDisplayList(nsDisplayListBuilder* aBuilder, diff --git a/layout/tables/nsTableOuterFrame.cpp b/layout/tables/nsTableOuterFrame.cpp index c1bc6f30d01..49206de5e3c 100644 --- a/layout/tables/nsTableOuterFrame.cpp +++ b/layout/tables/nsTableOuterFrame.cpp @@ -215,7 +215,7 @@ nsTableOuterFrame::DestroyFrom(nsIFrame* aDestructRoot) nsContainerFrame::DestroyFrom(aDestructRoot); } -nsFrameList +const nsFrameList& nsTableOuterFrame::GetChildList(ChildListID aListID) const { if (aListID == kCaptionList) { diff --git a/layout/tables/nsTableOuterFrame.h b/layout/tables/nsTableOuterFrame.h index fa33ce7015f..d7be9e5ba84 100644 --- a/layout/tables/nsTableOuterFrame.h +++ b/layout/tables/nsTableOuterFrame.h @@ -102,7 +102,7 @@ public: NS_IMETHOD SetInitialChildList(ChildListID aListID, nsFrameList& aChildList); - virtual nsFrameList GetChildList(ChildListID aListID) const; + virtual const nsFrameList& GetChildList(ChildListID aListID) const; virtual void GetChildLists(nsTArray* aLists) const; NS_IMETHOD AppendFrames(ChildListID aListID, diff --git a/layout/xul/base/src/nsBoxFrame.h b/layout/xul/base/src/nsBoxFrame.h index bb58524c590..3938e84dbf4 100644 --- a/layout/xul/base/src/nsBoxFrame.h +++ b/layout/xul/base/src/nsBoxFrame.h @@ -61,7 +61,7 @@ class nsBoxLayoutState; #define NS_STATE_CURRENTLY_IN_DEBUG NS_FRAME_STATE_BIT(25) //#define NS_STATE_SET_TO_DEBUG NS_FRAME_STATE_BIT(26) moved to nsBox.h //#define NS_STATE_DEBUG_WAS_SET NS_FRAME_STATE_BIT(27) moved to nsBox.h -// NS_FRAME_STATE_BIT(28) not used anymore +#define NS_STATE_MENU_HAS_POPUP_LIST NS_FRAME_STATE_BIT(28) /* used on nsMenuFrame */ #define NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK NS_FRAME_STATE_BIT(29) #define NS_STATE_EQUAL_SIZE NS_FRAME_STATE_BIT(30) //#define NS_STATE_IS_DIRECTION_NORMAL NS_FRAME_STATE_BIT(31) moved to nsIFrame.h diff --git a/layout/xul/base/src/nsMenuFrame.cpp b/layout/xul/base/src/nsMenuFrame.cpp index 65efb99b610..9b15135f371 100644 --- a/layout/xul/base/src/nsMenuFrame.cpp +++ b/layout/xul/base/src/nsMenuFrame.cpp @@ -87,6 +87,13 @@ using namespace mozilla; #define NSCONTEXTMENUISMOUSEUP 1 #endif +static void +AssertNotCalled(void* aPropertyValue) +{ + NS_ERROR("popup list should never be destroyed by the FramePropertyTable"); +} +NS_DECLARE_FRAME_PROPERTY(PopupListProperty, AssertNotCalled) + static PRInt32 gEatMouseMove = false; const PRInt32 kBlinkDelay = 67; // milliseconds @@ -207,9 +214,6 @@ NS_QUERYFRAME_HEAD(nsMenuFrame) NS_QUERYFRAME_ENTRY(nsMenuFrame) NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame) -// -// nsMenuFrame cntr -// nsMenuFrame::nsMenuFrame(nsIPresShell* aShell, nsStyleContext* aContext): nsBoxFrame(aShell, aContext), mIsMenu(false), @@ -217,11 +221,9 @@ nsMenuFrame::nsMenuFrame(nsIPresShell* aShell, nsStyleContext* aContext): mIgnoreAccelTextChange(false), mType(eMenuType_Normal), mMenuParent(nsnull), - mPopupFrame(nsnull), mBlinkState(0) { - -} // cntr +} void nsMenuFrame::SetParent(nsIFrame* aParent) @@ -298,11 +300,12 @@ nsMenuFrame::Init(nsIContent* aContent, return rv; } -nsFrameList +const nsFrameList& nsMenuFrame::GetChildList(ChildListID aListID) const { if (kPopupList == aListID) { - return nsFrameList(mPopupFrame, mPopupFrame); + nsFrameList* list = GetPopupList(); + return list ? *list : nsFrameList::EmptyList(); } return nsBoxFrame::GetChildList(aListID); } @@ -311,8 +314,44 @@ void nsMenuFrame::GetChildLists(nsTArray* aLists) const { nsBoxFrame::GetChildLists(aLists); - nsFrameList popupList(mPopupFrame, mPopupFrame); - popupList.AppendIfNonempty(aLists, kPopupList); + nsFrameList* list = GetPopupList(); + if (list) { + list->AppendIfNonempty(aLists, kPopupList); + } +} + +nsMenuPopupFrame* +nsMenuFrame::GetPopup() +{ + nsFrameList* popupList = GetPopupList(); + return popupList ? static_cast(popupList->FirstChild()) : + nsnull; +} + +nsFrameList* +nsMenuFrame::GetPopupList() const +{ + if (!HasPopup()) { + return nsnull; + } + nsFrameList* prop = + static_cast(Properties().Get(PopupListProperty())); + NS_ASSERTION(prop && prop->GetLength() == 1 && + prop->FirstChild()->GetType() == nsGkAtoms::menuPopupFrame, + "popup list should have exactly one nsMenuPopupFrame"); + return prop; +} + +void +nsMenuFrame::DestroyPopupList() +{ + NS_ASSERTION(HasPopup(), "huh?"); + nsFrameList* prop = + static_cast(Properties().Remove(PopupListProperty())); + NS_ASSERTION(prop && prop->IsEmpty(), + "popup list must exist and be empty when destroying"); + RemoveStateBits(NS_STATE_MENU_HAS_POPUP_LIST); + delete prop; } void @@ -320,9 +359,12 @@ nsMenuFrame::SetPopupFrame(nsFrameList& aFrameList) { for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) { if (e.get()->GetType() == nsGkAtoms::menuPopupFrame) { - // Remove this frame from the list and set it as mPopupFrame - mPopupFrame = (nsMenuPopupFrame *)e.get(); - aFrameList.RemoveFrame(e.get()); + // Remove the frame from the list and store it in a nsFrameList* property. + nsIFrame* popupFrame = e.get(); + aFrameList.RemoveFrame(popupFrame); + nsFrameList* popupList = new nsFrameList(popupFrame, popupFrame); + Properties().Set(PopupListProperty(), popupList); + AddStateBits(NS_STATE_MENU_HAS_POPUP_LIST); break; } } @@ -332,7 +374,7 @@ NS_IMETHODIMP nsMenuFrame::SetInitialChildList(ChildListID aListID, nsFrameList& aChildList) { - NS_ASSERTION(!mPopupFrame, "already have a popup frame set"); + NS_ASSERTION(!HasPopup(), "SetInitialChildList called twice?"); if (aListID == kPrincipalList || aListID == kPopupList) { SetPopupFrame(aChildList); } @@ -365,8 +407,11 @@ nsMenuFrame::DestroyFrom(nsIFrame* aDestructRoot) mMenuParent->CurrentMenuIsBeingDestroyed(); } - if (mPopupFrame) - mPopupFrame->DestroyFrom(aDestructRoot); + nsFrameList* popupList = GetPopupList(); + if (popupList) { + popupList->DestroyFramesFrom(aDestructRoot); + DestroyPopupList(); + } nsBoxFrame::DestroyFrom(aDestructRoot); } @@ -675,8 +720,8 @@ nsMenuFrame::CloseMenu(bool aDeselectMenu) // Close the menu asynchronously nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); - if (pm && mPopupFrame) - pm->HidePopup(mPopupFrame->GetContent(), false, aDeselectMenu, true); + if (pm && HasPopup()) + pm->HidePopup(GetPopup()->GetContent(), false, aDeselectMenu, true); } bool @@ -713,9 +758,10 @@ nsMenuFrame::DoLayout(nsBoxLayoutState& aState) // lay us out nsresult rv = nsBoxFrame::DoLayout(aState); - if (mPopupFrame) { + nsMenuPopupFrame* popupFrame = GetPopup(); + if (popupFrame) { bool sizeToPopup = IsSizedToPopup(mContent, false); - mPopupFrame->LayoutPopup(aState, this, sizeToPopup); + popupFrame->LayoutPopup(aState, this, sizeToPopup); } return rv; @@ -733,8 +779,9 @@ nsMenuFrame::SetDebug(nsBoxLayoutState& aState, bool aDebug) if (debugChanged) { nsBoxFrame::SetDebug(aState, aDebug); - if (mPopupFrame) - SetDebug(aState, mPopupFrame, aDebug); + nsMenuPopupFrame* popupFrame = GetPopup(); + if (popupFrame) + SetDebug(aState, popupFrame, aDebug); } return NS_OK; @@ -797,7 +844,8 @@ nsMenuFrame::Enter(nsGUIEvent *aEvent) bool nsMenuFrame::IsOpen() { - return mPopupFrame && mPopupFrame->IsOpen(); + nsMenuPopupFrame* popupFrame = GetPopup(); + return popupFrame && popupFrame->IsOpen(); } bool @@ -1248,21 +1296,15 @@ NS_IMETHODIMP nsMenuFrame::RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) { - nsresult rv = NS_OK; - - if (mPopupFrame == aOldFrame) { - // Go ahead and remove this frame. - mPopupFrame->Destroy(); - mPopupFrame = nsnull; + nsFrameList* popupList = GetPopupList(); + if (popupList && popupList->DestroyFrameIfPresent(aOldFrame)) { + DestroyPopupList(); PresContext()->PresShell()-> FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_HAS_DIRTY_CHILDREN); - rv = NS_OK; - } else { - rv = nsBoxFrame::RemoveFrame(aListID, aOldFrame); + return NS_OK; } - - return rv; + return nsBoxFrame::RemoveFrame(aListID, aOldFrame); } NS_IMETHODIMP @@ -1270,9 +1312,9 @@ nsMenuFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame, nsFrameList& aFrameList) { - if (!mPopupFrame && (aListID == kPrincipalList || aListID == kPopupList)) { + if (!HasPopup() && (aListID == kPrincipalList || aListID == kPopupList)) { SetPopupFrame(aFrameList); - if (mPopupFrame) { + if (HasPopup()) { #ifdef DEBUG_LAYOUT nsBoxLayoutState state(PresContext()); SetDebug(state, aFrameList, mState & NS_STATE_CURRENTLY_IN_DEBUG); @@ -1287,7 +1329,7 @@ nsMenuFrame::InsertFrames(ChildListID aListID, if (aFrameList.IsEmpty()) return NS_OK; - if (NS_UNLIKELY(aPrevFrame == mPopupFrame)) { + if (NS_UNLIKELY(aPrevFrame && aPrevFrame == GetPopup())) { aPrevFrame = nsnull; } @@ -1298,9 +1340,9 @@ NS_IMETHODIMP nsMenuFrame::AppendFrames(ChildListID aListID, nsFrameList& aFrameList) { - if (!mPopupFrame && (aListID == kPrincipalList || aListID == kPopupList)) { + if (!HasPopup() && (aListID == kPrincipalList || aListID == kPopupList)) { SetPopupFrame(aFrameList); - if (mPopupFrame) { + if (HasPopup()) { #ifdef DEBUG_LAYOUT nsBoxLayoutState state(PresContext()); @@ -1326,9 +1368,10 @@ nsMenuFrame::SizeToPopup(nsBoxLayoutState& aState, nsSize& aSize) nsSize tmpSize(-1, 0); nsIBox::AddCSSPrefSize(this, tmpSize, widthSet, heightSet); if (!widthSet && GetFlex(aState) == 0) { - if (!mPopupFrame) + nsMenuPopupFrame* popupFrame = GetPopup(); + if (!popupFrame) return false; - tmpSize = mPopupFrame->GetPrefSize(aState); + tmpSize = popupFrame->GetPrefSize(aState); // Produce a size such that: // (1) the menu and its popup can be the same width @@ -1340,7 +1383,7 @@ nsMenuFrame::SizeToPopup(nsBoxLayoutState& aState, nsSize& aSize) GetBorderAndPadding(borderPadding); // if there is a scroll frame, add the desired width of the scrollbar as well - nsIScrollableFrame* scrollFrame = do_QueryFrame(mPopupFrame->GetFirstPrincipalChild()); + nsIScrollableFrame* scrollFrame = do_QueryFrame(popupFrame->GetFirstPrincipalChild()); nscoord scrollbarWidth = 0; if (scrollFrame) { scrollbarWidth = @@ -1380,10 +1423,11 @@ nsMenuFrame::GetPrefSize(nsBoxLayoutState& aState) NS_IMETHODIMP nsMenuFrame::GetActiveChild(nsIDOMElement** aResult) { - if (!mPopupFrame) + nsMenuPopupFrame* popupFrame = GetPopup(); + if (!popupFrame) return NS_ERROR_FAILURE; - nsMenuFrame* menuFrame = mPopupFrame->GetCurrentMenuItem(); + nsMenuFrame* menuFrame = popupFrame->GetCurrentMenuItem(); if (!menuFrame) { *aResult = nsnull; } @@ -1399,12 +1443,13 @@ nsMenuFrame::GetActiveChild(nsIDOMElement** aResult) NS_IMETHODIMP nsMenuFrame::SetActiveChild(nsIDOMElement* aChild) { - if (!mPopupFrame) + nsMenuPopupFrame* popupFrame = GetPopup(); + if (!popupFrame) return NS_ERROR_FAILURE; if (!aChild) { // Remove the current selection - mPopupFrame->ChangeMenuItem(nsnull, false); + popupFrame->ChangeMenuItem(nsnull, false); return NS_OK; } @@ -1412,17 +1457,18 @@ nsMenuFrame::SetActiveChild(nsIDOMElement* aChild) nsIFrame* kid = child->GetPrimaryFrame(); if (kid && kid->GetType() == nsGkAtoms::menuFrame) - mPopupFrame->ChangeMenuItem(static_cast(kid), false); + popupFrame->ChangeMenuItem(static_cast(kid), false); return NS_OK; } nsIScrollableFrame* nsMenuFrame::GetScrollTargetFrame() { - if (!mPopupFrame) + nsMenuPopupFrame* popupFrame = GetPopup(); + if (!popupFrame) return nsnull; - nsIFrame* childFrame = mPopupFrame->GetFirstPrincipalChild(); + nsIFrame* childFrame = popupFrame->GetFirstPrincipalChild(); if (childFrame) - return mPopupFrame->GetScrollFrame(childFrame); + return popupFrame->GetScrollFrame(childFrame); return nsnull; } diff --git a/layout/xul/base/src/nsMenuFrame.h b/layout/xul/base/src/nsMenuFrame.h index 9731c41bf9d..dc5476dd913 100644 --- a/layout/xul/base/src/nsMenuFrame.h +++ b/layout/xul/base/src/nsMenuFrame.h @@ -130,7 +130,7 @@ public: // The following methods are all overridden so that the menupopup // can be stored in a separate list, so that it doesn't impact reflow of the // actual menu item at all. - virtual nsFrameList GetChildList(ChildListID aList) const; + virtual const nsFrameList& GetChildList(ChildListID aList) const; virtual void GetChildLists(nsTArray* aLists) const; NS_IMETHOD SetInitialChildList(ChildListID aListID, nsFrameList& aChildList); @@ -185,7 +185,16 @@ public: virtual nsMenuParent *GetMenuParent() { return mMenuParent; } const nsAString& GetRadioGroupName() { return mGroupName; } nsMenuType GetMenuType() { return mType; } - nsMenuPopupFrame* GetPopup() { return mPopupFrame; } + nsMenuPopupFrame* GetPopup(); + + /** + * @return true if this frame has a popup child frame. + */ + bool HasPopup() const + { + return (GetStateBits() & NS_STATE_MENU_HAS_POPUP_LIST) != 0; + } + // nsMenuFrame methods @@ -226,10 +235,23 @@ protected: friend class nsASyncMenuInitialization; friend class nsMenuAttributeChangedEvent; - // initialize mPopupFrame to the first popup frame within - // aChildList. Removes the popup, if any, from aChildList. + /** + * Initialize the popup list to the first popup frame within + * aChildList. Removes the popup, if any, from aChildList. + */ void SetPopupFrame(nsFrameList& aChildList); + /** + * Get the popup frame list from the frame property. + * @return the property value if it exists, nsnull otherwise. + */ + nsFrameList* GetPopupList() const; + + /** + * Destroy the popup list property. The list must exist and be empty. + */ + void DestroyPopupList(); + // set mMenuParent to the nearest enclosing menu bar or menupopup frame of // aParent (or aParent itself). This is called when initializing the frame, // so aParent should be the expected parent of this frame. @@ -275,9 +297,6 @@ protected: nsMenuParent* mMenuParent; // Our parent menu. - // the popup for this menu, owned - nsMenuPopupFrame* mPopupFrame; - // Reference to the mediator which wraps this frame. nsRefPtr mTimerMediator; diff --git a/media/libpng/mozpngconf.h b/media/libpng/mozpngconf.h index 0f5f4cf1dd5..f67897ed334 100644 --- a/media/libpng/mozpngconf.h +++ b/media/libpng/mozpngconf.h @@ -41,7 +41,6 @@ #define PNG_API_RULE 0 #define PNG_COST_SHIFT 3 -#define PNG_DEFAULT_READ_MACROS 1 #define PNG_GAMMA_THRESHOLD_FIXED 5000 #define PNG_MAX_GAMMA_8 11 #define PNG_USER_CHUNK_MALLOC_MAX 4000000L @@ -72,7 +71,6 @@ #define PNG_READ_EXPAND_SUPPORTED #define PNG_READ_GAMMA_SUPPORTED #define PNG_READ_GRAY_TO_RGB_SUPPORTED -#define PNG_READ_INT_FUNCTIONS_SUPPORTED #define PNG_READ_INTERLACING_SUPPORTED #define PNG_READ_SCALE_16_TO_8_SUPPORTED #define PNG_READ_TEXT_SUPPORTED @@ -86,8 +84,8 @@ #define PNG_WRITE_16BIT_SUPPORTED #define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED #define PNG_WRITE_FLUSH_SUPPORTED -#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED #define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED +#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED #endif #define PNG_APNG_SUPPORTED @@ -144,7 +142,6 @@ /* The following may have changed, or can be affected by conditional compilation choices, and will be mangled. */ #define png_64bit_product MOZ_PNG_64bit_product -#define png_benign_error MOZ_PNG_benign_error #define png_build_gamma_table MOZ_PNG_build_gamma_tab #define png_build_grayscale_palette MOZ_PNG_build_g_p #define png_calculate_crc MOZ_PNG_calc_crc @@ -153,7 +150,6 @@ #define png_check_chunk_name MOZ_PNG_ck_chunk_name #define png_check_IHDR MOZ_PNG_ck_IHDR #define png_check_keyword MOZ_PNG_ck_keyword -#define png_chunk_benign_error MOZ_PNG_ck_benign_error #define png_combine_row MOZ_PNG_combine_row #define png_convert_from_struct_tm MOZ_PNG_cv_from_struct_tm #define png_convert_from_time_t MOZ_PNG_cv_from_time_t @@ -234,7 +230,6 @@ #define png_get_IHDR MOZ_PNG_get_IHDR #define png_get_image_height MOZ_PNG_get_image_h #define png_get_image_width MOZ_PNG_get_image_w -#define png_get_int_32 MOZ_PNG_get_int_32 #define png_get_interlace_type MOZ_PNG_get_interlace_type #define png_get_libpng_ver MOZ_PNG_get_libpng_ver #define png_get_mem_ptr MOZ_PNG_get_mem_ptr @@ -259,9 +254,6 @@ #define png_get_text MOZ_PNG_get_text #define png_get_tIME MOZ_PNG_get_tIME #define png_get_tRNS MOZ_PNG_get_tRNS -#define png_get_uint_16 MOZ_PNG_get_uint_16 -#define png_get_uint_31 MOZ_PNG_get_uint_31 -#define png_get_uint_32 MOZ_PNG_get_uint_32 #define png_get_unknown_chunks MOZ_PNG_get_unk_chunks #define png_get_user_chunk_ptr MOZ_PNG_get_user_chunk_ptr #define png_get_user_height_max MOZ_PNG_get_user_height_max @@ -545,10 +537,8 @@ #define png_exp MOZ_PNG_exp #define png_exp16bit MOZ_PNG_exp16bit #define png_exp8bit MOZ_PNG_exp8bit -#define png_fixed_error MOZ_PNG_fixed_error #define png_fixed_inches_from_microns MOZ_PNG_fixed_inch_from_micr #define png_format_number MOZ_PNG_format_number -#define png_formatted_warning MOZ_PNG_formatted_warning #define png_gamma_16bit_correct MOZ_PNG_gamma_16bit_correct #define png_gamma_8bit_correct MOZ_PNG_gamma_8bit_correct #define png_gamma_correct MOZ_PNG_gamma_correct @@ -610,9 +600,6 @@ #define png_set_text_compression_strategy MOZ_PNG_set_text_c_strategy #define png_set_text_compression_window_bits MOZ_PNG_set_text_c_wnd_bits #define png_user_version_check MOZ_PNG_user_version_check -#define png_warning_parameter MOZ_PNG_warn_param -#define png_warning_parameter_signed MOZ_PNG_warn_param_signed -#define png_warning_parameter_unsigned MOZ_PNG_warn_param_unsigned #define png_write_chunk_header MOZ_PNG_write_chunk_header #define png_write_complete_chunk MOZ_PNG_write_complete_chunk #define png_xy_from_XYZ MOZ_PNG_xy_from_XYZ @@ -624,11 +611,16 @@ #define ppi_from_ppm MOZ_ppi_from_ppm #define translate_gamma_flags MOZ_translate_gamma_flags -#ifdef PR_LOGGING +#if defined(PR_LOGGING) && defined(PNG_WARNINGS_SUPPORTED) #define png_warning MOZ_PNG_warning #define png_error MOZ_PNG_error #define png_chunk_error MOZ_PNG_chunk_err +#define png_fixed_error MOZ_PNG_fixed_err +#define png_formatted_warning MOZ_PNG_formatted_warning #define png_chunk_warning MOZ_PNG_chunk_warn +#define png_warning_parameter MOZ_PNG_warn_param +#define png_warning_parameter_signed MOZ_PNG_warn_param_signed +#define png_warning_parameter_unsigned MOZ_PNG_warn_param_unsigned #endif #endif /* MOZPNGCONF_H */ diff --git a/mfbt/STYLE b/mfbt/STYLE new file mode 100644 index 00000000000..31b3ceec138 --- /dev/null +++ b/mfbt/STYLE @@ -0,0 +1,273 @@ += mfbt style rules = + +== Line length == + +The line limit is 80 characters, except that excessively long blocks of preprocessor directives may exceed this if it makes the code more readable (e.g. MOZ_STATIC_ASSERT in Assertions.h.), and unbreakable text in comments (e.g. URLs) may exceed this as well. Wrap expressions after binary operators. + +== Capitalization == + +Standalone functions, classes, structs, and template parameters are named InterCaps-style. Member functions and fields in classes and structs are named camelCaps-style. + +== Indentation == + +Indentation is two spaces, never tabs. + + if (x == 2) + return 17; + +== Whitespace == + +Surround binary operators with a single space on either side. + + if (x == 2) + return 17; + +When describing pointer types, the * shall be adjacent to the type name. (Same goes for references -- & goes by the type name.) + + int + Foo(int* p) + { + typedef void* VoidPtr; + int& i = *p; + } + +A corollary: don't mix declaration types by declaring a T and a T* (or a T**, &c.) in the same declaration. + + T* foo, bar; // BAD + +== Bracing == + +Don't brace single statements. + + if (y == 7) + return 3; + for (size_t i = 0; i < 5; i++) + frob(i); + +But do brace them if the statement (or condition(s) or any additional consequents, if the braces would be associated with an if statement) occupies multiple lines. + + if (cond1 || + cond2) + { + action(); + } + if (cond1) { + consequent(); + } else { + alternative(arg1, + arg2); + } + if (cond1 || cond2) { + callMethod(arg1, + arg2); + } + for (size_t j = 0; + j < 17; + j++) + { + action(); + } + +Braces in control flow go at the end of the line except when associated with an |if| or loop-head where the condition covers multiple lines + +== Classes and structs == + +Inside class and structure definitions, public/private consume one level of indentation. + + class Baz + { + public: + Baz() { } + }; + +The absence of public/private in structs in which all members are public still consumes a level. + + struct Foo + { + int field; + }; + +Braces delimiting a class or struct go on their own lines. + +Member initialization in constructors should be formatted as follows: + + class Fnord + { + size_t s1, s2, s3, s4, s5; + + public: + Fnord(size_t s) : s1(s), s2(s), s3(s), s4(s), s5(s) { } + Fnord() + : s1(0), /* member initialization can be compressed if desired */ + s2(0), + s3(0), + s4(0), + s5(0) + { + ... + } + }; + +Fields should go first in the class so that the basic structure is all in one place, consistently. + +Use the inline keyword to annotate functions defined inline in a header. (If the function is defined inline in the class, don't bother adding it redundantly.) + +Explicitly delete (using Attributes.h's MOZ_DELETE) the copy constructor and assignment operator from classes not intended to be copied or assigned to avoid mistakes. + + class Funky + { + public: + Funky() { } + + private: + Funky(const Funky& other) MOZ_DELETE; + void operator=(const Funky& other) MOZ_DELETE; + }; + +Include a blank line between sections of structs and classes with different access control. + +The "get" prefix is used when a method is fallible. If it's infallible, don't use it. + + class String + { + public: + size_t length() const; // not getLength() + }; + +== Templates == + +Capitalize template parameter names to distinguish them from fields. + + template + class BloomFilter + { + }; + +Use single-letter names if it makes sense (T for an arbitrary type, K for key type, V for value type, &c.). Otherwise use InterCaps-style names. + +When declaring or defining a function, template<...> goes on one line, the return type and other specifiers go on another line, and the function name and argument list go on a third line. + + template + inline bool + Vector::add(T t) + { + } + +== Namespaces == + +All C++ code shall be in the mozilla namespace, except that functionality only used to implement external-facing API should be in the mozilla::detail namespace, indicating that it should not be directly used. + +Namespace opening braces go on the same line as the namespace declaration. Namespace closing braces shall be commented. Namespace contents are not indented. + + namespace mozilla { + ... + } // namespace mozilla + +Don't use |using| in a header unless it's confined to a class or method. Implementation files for out-of-line functionality may use |using|. + +== #includes == + +Headers that include mfbt headers use a fully-qualified include path, even if full qualification is not strictly necessary. + + #include "mozilla/Assertions.h" + +mfbt headers should be included first, alphabetically. Standard includes should follow, separated from mfbt includes by a blank line. + + #include "mozilla/Assertions.h" + #include "mozilla/Attributes.h" + + #include + +If a header dependency is limited simply to the existence of a class, forward-declare it rather than #include that header. + + namespace mozilla { + + class BloomFilter; + extern bool + Test(BloomFilter* bf); + + } // namespace mozilla + +== Preprocessor == + +Include guards should be named by determining the fully-qualified include path, then substituting _ for / and . in it, and finally appending a trailing _. For example, "mozilla/Assertions.h" becomes mozilla_Assertions_h_. + +Nested preprocessor directives indent the directive name (but not the #) by two spaces. + + #ifdef __clang__ + # define FOO ... + #else + # define FOO ... + #endif + +Comments within nested preprocessor directives align with directive names at that nesting depth. + + #if defined(__GNUC__) + /* gcc supports C++11 override syntax. */ + # define MOZ_OVERRIDE override + #else + # define MOZ_OVERRIDE /* unsupported */ + #endif + +Feature-testing macros may be defined to nothing. Macros intended to be textually expanded should be defined to a comment indicating non-support, as above or as appropriate to the situation. + +No particular preference is expressed between testing for a macro being defined using defined(...) and using #ifdef. + +When defining a macro with different expansions for different compilers, the top level of distinction should be the compiler, and the next nested level should be the compiler version. Clang seems likely to be around for awhile, so to reduce confusion test for it separately from gcc even when it's not strictly necessary. + + #if defined(__clang__) + #elif defined(__GNUC__) + # if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) + # else + # endif + #elif defined(_MSC_VER) + #endif + +But don't distinguish clang's feature support using version checks: use the __has_feature() and __has_extension() macros instead, because vendors may customize clang's version numbers. + +Prefer inline functions to macros whenever possible. + +== Comments == + +Header files shall have a short descriptive comment underneath license boilerplate indicating what functionality the file implements, to be picked up by MXR and displayed in directory listings. (But see bug 717196, which currently prevents MXR from doing this if the MPL2 boilerplate is used.) + + Assertions.h: + ...license boilerplate... + + /* Implementations of runtime and static assertion macros for C and C++. */ + +Classes intended for public use shall have interface comments explaining their functionality from the user's perspective. These comments shall include examples of how the relevant functionality might be used. These interface comments use /** */ doxygen/Javadoc-style comments. + + /** + * The Frobber class simplifies the process of frobbing. + */ + class Frobber + { + }; + +Comments describing implementation details (tradeoffs considered, assumptions made, mathematical background, &c.) occur separately from interface comments so that users need not consider them. They should go inside the class definition or inside the appropriate method, depending on the specificity of the comment. + +Headers which are intended to be C-compatible shall use only /**/-style comments. (Code examples nested inside documentation comments may use //-style comments.) Headers which are C++-compatible may also use //-style comments. + +Non-interface comments that are /**/-style shall not also be doxygen-style. + +Use Python-style ** to denote exponentiation inside comments, not ^ (which can be confused with C-style bitwise xor). If you're writing sufficiently complex math, feel free to descend into LaTeX math mode ;-) inside implementation comments if you need to. (But keep it out of interface comments, because most people probably haven't seen LaTeX.) + +== Miscellaneous == + +Enclose C-compatible code in |extern "C"| blocks, and #ifdef __cplusplus the block start/end as needed. The contents of these blocks should not be indented. + +Add new functionality to new headers unless an existing header makes sense. Err on the side of more headers rather than fewer, as this helps to minimize dependencies. Don't add anything to Util.h, which will be split into multiple headers at some point (bug 713082). + +Don't use bool for argument types unless the method is a "set" or "enable"-style method where the method name and bool value together indicate the sense of its effect. Use well-named enums in all other places, so that the semantics of the argument are clear at a glance and do not require knowing how the method interprets that argument. + + void + setVisible(bool visible); // true clearly means visible, false clearly not + enum Enumerability { + Enumerable, + NonEnumerable + }; + bool + DefineProperty(JSObject* obj, const char* name, Value v, Enumerability e); + +Use NULL for the null pointer constant. diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js index 25a57c1a793..f4950117307 100644 --- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -221,13 +221,14 @@ pref("extensions.strictCompatibility", false); pref("extensions.minCompatibleAppVersion", "11.0"); pref("extensions.update.url", "https://versioncheck.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%¤tAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%"); +pref("extensions.update.background.url", "https://versioncheck-bg.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%¤tAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%"); /* preferences for the Get Add-ons pane */ pref("extensions.getAddons.cache.enabled", true); pref("extensions.getAddons.maxResults", 15); pref("extensions.getAddons.recommended.browseURL", "https://addons.mozilla.org/%LOCALE%/android/recommended/"); pref("extensions.getAddons.recommended.url", "https://services.addons.mozilla.org/%LOCALE%/android/api/%API_VERSION%/list/featured/all/%MAX_RESULTS%/%OS%/%VERSION%"); -pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/android/search?q=%TERMS%"); +pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/android/search?q=%TERMS%&platform=%OS%&appver=%VERSION%"); pref("extensions.getAddons.search.url", "https://services.addons.mozilla.org/%LOCALE%/android/api/%API_VERSION%/search/%TERMS%/all/%MAX_RESULTS%/%OS%/%VERSION%/%COMPATIBILITY_MODE%"); pref("extensions.getAddons.browseAddons", "https://addons.mozilla.org/%LOCALE%/android/"); pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/%LOCALE%/android/api/%API_VERSION%/search/guid:%IDS%?src=mobile&appOS=%OS%&appVersion=%VERSION%"); @@ -247,6 +248,9 @@ pref("extensions.blocklist.detailsURL", "https://www.mozilla.com/%LOCALE%/blockl pref("dom.disable_open_during_load", true); pref("privacy.popups.showBrowserMessage", true); +/* disable opening windows with the dialog feature */ +pref("dom.disable_window_open_dialog_feature", true); + pref("keyword.enabled", true); pref("keyword.URL", "http://www.google.com/m?ie=UTF-8&oe=UTF-8&sourceid=navclient&gfns=1&q="); diff --git a/mobile/android/base/AboutHomeContent.java b/mobile/android/base/AboutHomeContent.java index ef77b9e03e8..e1075fd57fe 100644 --- a/mobile/android/base/AboutHomeContent.java +++ b/mobile/android/base/AboutHomeContent.java @@ -112,6 +112,7 @@ public class AboutHomeContent extends ScrollView { private LayoutInflater mInflater; private AccountManager mAccountManager; + private OnAccountsUpdateListener mAccountListener = null; protected SimpleCursorAdapter mTopSitesAdapter; protected GridView mTopSitesGrid; @@ -139,7 +140,7 @@ public class AboutHomeContent extends ScrollView { mAccountManager = AccountManager.get(context); // The listener will run on the background thread (see 2nd argument) - mAccountManager.addOnAccountsUpdatedListener(new OnAccountsUpdateListener() { + mAccountManager.addOnAccountsUpdatedListener(mAccountListener = new OnAccountsUpdateListener() { public void onAccountsUpdated(Account[] accounts) { final GeckoApp.StartupMode startupMode = GeckoApp.mAppContext.getStartupMode(); final boolean syncIsSetup = isSyncSetup(); @@ -210,6 +211,13 @@ public class AboutHomeContent extends ScrollView { }); } + public void onDestroy() { + if (mAccountListener != null) { + mAccountManager.removeOnAccountsUpdatedListener(mAccountListener); + mAccountListener = null; + } + } + void setLastTabsVisibility(boolean visible) { int visibility = visible ? View.VISIBLE : View.GONE; findViewById(R.id.last_tabs_title).setVisibility(visibility); diff --git a/mobile/android/base/AwesomeBarTabs.java b/mobile/android/base/AwesomeBarTabs.java index 1662f153edc..4d3a5f62307 100644 --- a/mobile/android/base/AwesomeBarTabs.java +++ b/mobile/android/base/AwesomeBarTabs.java @@ -202,6 +202,7 @@ public class AwesomeBarTabs extends TabHost { private static final int VIEW_TYPE_COUNT = 2; private LayoutInflater mInflater; + private Resources mResources; private LinkedList> mParentStack; private RefreshBookmarkCursorTask mRefreshTask = null; private TextView mBookmarksTitleView; @@ -210,6 +211,7 @@ public class AwesomeBarTabs extends TabHost { super(context, layout, c, from, to); mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mResources = mContext.getResources(); // mParentStack holds folder id/title pairs that allow us to navigate // back up the folder heirarchy @@ -268,6 +270,24 @@ public class AwesomeBarTabs extends TabHost { if (!c.moveToPosition(position)) return ""; + String guid = c.getString(c.getColumnIndexOrThrow(Bookmarks.GUID)); + + // If we don't have a special GUID, just return the folder title from the DB. + if (guid == null || guid.length() == 12) + return c.getString(c.getColumnIndexOrThrow(Bookmarks.TITLE)); + + // Use localized strings for special folder names. + if (guid.equals(Bookmarks.MOBILE_FOLDER_GUID)) + return mResources.getString(R.string.bookmarks_folder_mobile); + else if (guid.equals(Bookmarks.MENU_FOLDER_GUID)) + return mResources.getString(R.string.bookmarks_folder_menu); + else if (guid.equals(Bookmarks.TOOLBAR_FOLDER_GUID)) + return mResources.getString(R.string.bookmarks_folder_toolbar); + else if (guid.equals(Bookmarks.UNFILED_FOLDER_GUID)) + return mResources.getString(R.string.bookmarks_folder_unfiled); + + // If for some reason we have a folder with a special GUID, but it's not one of + // the special folders we expect in the UI, just return the title from the DB. return c.getString(c.getColumnIndexOrThrow(Bookmarks.TITLE)); } diff --git a/mobile/android/base/BrowserToolbar.java b/mobile/android/base/BrowserToolbar.java index 6638c927b23..1658797cec5 100644 --- a/mobile/android/base/BrowserToolbar.java +++ b/mobile/android/base/BrowserToolbar.java @@ -300,7 +300,7 @@ public class BrowserToolbar extends LinearLayout { } public void setFavicon(Drawable image) { - if (Tabs.getInstance().getSelectedTab().isLoading()) + if (Tabs.getInstance().getSelectedTab().getState() == Tab.STATE_LOADING) return; if (image != null) @@ -343,7 +343,7 @@ public class BrowserToolbar extends LinearLayout { setTitle(tab.getDisplayTitle()); setFavicon(tab.getFavicon()); setSecurityMode(tab.getSecurityMode()); - setProgressVisibility(tab.isLoading()); + setProgressVisibility(tab.getState() == Tab.STATE_LOADING); setShadowVisibility((url == null) || !url.startsWith("about:")); updateTabCount(Tabs.getInstance().getCount()); } diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index a9fb20554eb..36f69a352e4 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -121,7 +121,6 @@ abstract public class GeckoApp public static SurfaceView cameraView; public static GeckoApp mAppContext; public static boolean mDOMFullScreen = false; - public static File sGREDir = null; public static Menu sMenu; private static GeckoThread sGeckoThread = null; public GeckoAppHandler mMainHandler; @@ -174,7 +173,6 @@ abstract public class GeckoApp public enum LaunchState {Launching, WaitForDebugger, Launched, GeckoRunning, GeckoExiting}; private static LaunchState sLaunchState = LaunchState.Launching; - private static boolean sTryCatchAttached = false; private static final int FILE_PICKER_REQUEST = 1; private static final int AWESOMEBAR_REQUEST = 2; @@ -610,7 +608,7 @@ abstract public class GeckoApp bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos); processThumbnail(tab, bitmap, bos.toByteArray()); } else { - if (!tab.hasLoaded()) { + if (tab.getState() == Tab.STATE_DELAYED) { byte[] thumbnail = BrowserDB.getThumbnailForUrl(getContentResolver(), tab.getURL()); if (thumbnail != null) processThumbnail(tab, null, thumbnail); @@ -783,7 +781,7 @@ abstract public class GeckoApp mBrowserToolbar.setTitle(tab.getDisplayTitle()); mBrowserToolbar.setFavicon(tab.getFavicon()); mBrowserToolbar.setSecurityMode(tab.getSecurityMode()); - mBrowserToolbar.setProgressVisibility(tab.isLoading()); + mBrowserToolbar.setProgressVisibility(tab.getState() == Tab.STATE_LOADING); } } }); @@ -925,6 +923,7 @@ abstract public class GeckoApp handleSecurityChange(tabId, mode); } else if (event.equals("Content:StateChange")) { final int tabId = message.getInt("tabID"); + final boolean success = message.getBoolean("success"); int state = message.getInt("state"); Log.i(LOGTAG, "State - " + state); if ((state & GeckoAppShell.WPL_STATE_IS_NETWORK) != 0) { @@ -934,7 +933,7 @@ abstract public class GeckoApp handleDocumentStart(tabId, showProgress); } else if ((state & GeckoAppShell.WPL_STATE_STOP) != 0) { Log.i(LOGTAG, "Got a document stop"); - handleDocumentStop(tabId); + handleDocumentStop(tabId, success); } } } else if (event.equals("Content:LoadError")) { @@ -972,6 +971,13 @@ abstract public class GeckoApp mBrowserToolbar.show(); } }); + } else if (event.equals("ToggleChrome:Focus")) { + mMainHandler.post(new Runnable() { + public void run() { + mBrowserToolbar.setVisibility(View.VISIBLE); + mBrowserToolbar.requestFocusFromTouch(); + } + }); } else if (event.equals("DOMFullScreen:Start")) { mDOMFullScreen = true; } else if (event.equals("DOMFullScreen:Stop")) { @@ -1218,7 +1224,7 @@ abstract public class GeckoApp if (tab == null) return; - tab.setLoading(true); + tab.setState(Tab.STATE_LOADING); tab.updateSecurityMode("unknown"); mMainHandler.post(new Runnable() { @@ -1233,12 +1239,12 @@ abstract public class GeckoApp }); } - void handleDocumentStop(int tabId) { + void handleDocumentStop(int tabId, boolean success) { final Tab tab = Tabs.getInstance().getTab(tabId); if (tab == null) return; - tab.setLoading(false); + tab.setState(success ? Tab.STATE_SUCCESS : Tab.STATE_ERROR); mMainHandler.post(new Runnable() { public void run() { @@ -1273,7 +1279,6 @@ abstract public class GeckoApp return; tab.updateTitle(title); - tab.setHasLoaded(true); // Make the UI changes mMainHandler.post(new Runnable() { @@ -1314,7 +1319,7 @@ abstract public class GeckoApp // want to load the image straight away. If tab is still // loading, we only load the favicon once the page's content // is fully loaded (see handleContentLoaded()). - if (!tab.isLoading()) { + if (tab.getState() != tab.STATE_LOADING) { mMainHandler.post(new Runnable() { public void run() { loadFavicon(tab); @@ -1602,6 +1607,8 @@ abstract public class GeckoApp @Override public void onCreate(Bundle savedInstanceState) { + GeckoAppShell.registerGlobalExceptionHandler(); + mAppContext = this; // StrictMode is set by defaults resource flag |enableStrictMode|. @@ -1609,7 +1616,7 @@ abstract public class GeckoApp enableStrictMode(); } - System.loadLibrary("mozglue"); + GeckoAppShell.loadMozGlue(); mMainHandler = new GeckoAppHandler(); Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - onCreate"); if (savedInstanceState != null) { @@ -1644,6 +1651,7 @@ abstract public class GeckoApp mInitialized = true; Intent intent = getIntent(); + String action = intent.getAction(); String args = intent.getStringExtra("args"); if (args != null && args.contains("-profile")) { Pattern p = Pattern.compile("(?:-profile\\s*)(\\w*)(\\s*)"); @@ -1656,7 +1664,7 @@ abstract public class GeckoApp } } - if (ACTION_UPDATE.equals(intent.getAction()) || args != null && args.contains("-alert update-app")) { + if (ACTION_UPDATE.equals(action) || args != null && args.contains("-alert update-app")) { Log.i(LOGTAG,"onCreate: Update request"); checkAndLaunchUpdate(); } @@ -1679,9 +1687,6 @@ abstract public class GeckoApp mBrowserToolbar.updateTabCount(1); } - if (sGREDir == null) - sGREDir = new File(this.getApplicationInfo().dataDir); - Uri data = intent.getData(); if (data != null && "http".equals(data.getScheme()) && isHostOnPrefetchWhitelist(data.getHost())) { @@ -1697,9 +1702,20 @@ abstract public class GeckoApp } sGeckoThread = new GeckoThread(intent, passedUri, mRestoreSession); - if (!ACTION_DEBUG.equals(intent.getAction()) && - checkAndSetLaunchState(LaunchState.Launching, LaunchState.Launched)) + if (!ACTION_DEBUG.equals(action) && + checkAndSetLaunchState(LaunchState.Launching, LaunchState.Launched)) { sGeckoThread.start(); + } else if (ACTION_DEBUG.equals(action) && + checkAndSetLaunchState(LaunchState.Launching, LaunchState.WaitForDebugger)) { + mMainHandler.postDelayed(new Runnable() { + public void run() { + Log.i(LOGTAG, "Launching from debug intent after 5s wait"); + setLaunchState(LaunchState.Launching); + sGeckoThread.start(); + } + }, 1000 * 5 /* 5 seconds */); + Log.i(LOGTAG, "Intent : ACTION_DEBUG - waiting 5s before launching"); + } mFavicons = new Favicons(this); @@ -1740,21 +1756,6 @@ abstract public class GeckoApp Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - UI almost up"); - if (!sTryCatchAttached) { - sTryCatchAttached = true; - mMainHandler.post(new Runnable() { - public void run() { - try { - Looper.loop(); - } catch (Exception e) { - GeckoAppShell.reportJavaCrash(e); - } - // resetting this is kinda pointless, but oh well - sTryCatchAttached = false; - } - }); - } - //register for events GeckoAppShell.registerGeckoEventListener("DOMContentLoaded", GeckoApp.mAppContext); GeckoAppShell.registerGeckoEventListener("DOMTitleChanged", GeckoApp.mAppContext); @@ -1776,6 +1777,7 @@ abstract public class GeckoApp GeckoAppShell.registerGeckoEventListener("DOMFullScreen:Stop", GeckoApp.mAppContext); GeckoAppShell.registerGeckoEventListener("ToggleChrome:Hide", GeckoApp.mAppContext); GeckoAppShell.registerGeckoEventListener("ToggleChrome:Show", GeckoApp.mAppContext); + GeckoAppShell.registerGeckoEventListener("ToggleChrome:Focus", GeckoApp.mAppContext); GeckoAppShell.registerGeckoEventListener("Permissions:Data", GeckoApp.mAppContext); GeckoAppShell.registerGeckoEventListener("Downloads:Done", GeckoApp.mAppContext); GeckoAppShell.registerGeckoEventListener("CharEncoding:Data", GeckoApp.mAppContext); @@ -1946,6 +1948,10 @@ abstract public class GeckoApp return; } + // don't perform any actions if launching from recent apps + if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) + return; + if (checkLaunchState(LaunchState.Launched)) { Uri data = intent.getData(); Bundle bundle = intent.getExtras(); @@ -2148,6 +2154,7 @@ abstract public class GeckoApp GeckoAppShell.unregisterGeckoEventListener("Toast:Show", GeckoApp.mAppContext); GeckoAppShell.unregisterGeckoEventListener("ToggleChrome:Hide", GeckoApp.mAppContext); GeckoAppShell.unregisterGeckoEventListener("ToggleChrome:Show", GeckoApp.mAppContext); + GeckoAppShell.unregisterGeckoEventListener("ToggleChrome:Focus", GeckoApp.mAppContext); GeckoAppShell.unregisterGeckoEventListener("Permissions:Data", GeckoApp.mAppContext); GeckoAppShell.unregisterGeckoEventListener("Downloads:Done", GeckoApp.mAppContext); GeckoAppShell.unregisterGeckoEventListener("CharEncoding:Data", GeckoApp.mAppContext); @@ -2169,6 +2176,10 @@ abstract public class GeckoApp super.onDestroy(); unregisterReceiver(mBatteryReceiver); + + if (mAboutHomeContent != null) { + mAboutHomeContent.onDestroy(); + } } @Override @@ -2488,7 +2499,7 @@ abstract public class GeckoApp fileExt = name.substring(period); fileName = name.substring(0, period); } - File file = File.createTempFile(fileName, fileExt, sGREDir); + File file = File.createTempFile(fileName, fileExt, GeckoAppShell.getGREDir(GeckoApp.mAppContext)); FileOutputStream fos = new FileOutputStream(file); InputStream is = cr.openInputStream(uri); diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 99a66ac39db..2ffb9d04afe 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -112,6 +112,8 @@ public class GeckoAppShell static File sHomeDir = null; static private int sDensityDpi = 0; private static Boolean sSQLiteLibsLoaded = false; + private static Boolean sLibsSetup = false; + private static File sGREDir = null; private static HashMap> mEventListeners; @@ -141,13 +143,22 @@ public class GeckoAppShell public static native void loadSQLiteLibsNative(String apkName, boolean shouldExtract); public static native void onChangeNetworkLinkStatus(String status); - public static void reportJavaCrash(Throwable e) { - Log.e(LOGTAG, "top level exception", e); + public static void registerGlobalExceptionHandler() { + Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { + public void uncaughtException(Thread thread, Throwable e) { + Log.e(LOGTAG, ">>> REPORTING UNCAUGHT EXCEPTION FROM THREAD " + + thread.getId() + " (\"" + thread.getName() + "\")", e); + reportJavaCrash(getStackTraceString(e)); + } + }); + } + + private static String getStackTraceString(Throwable e) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); pw.flush(); - reportJavaCrash(sw.toString()); + return sw.toString(); } private static native void reportJavaCrash(String stackTrace); @@ -212,16 +223,16 @@ public class GeckoAppShell return GeckoBackgroundThread.getHandler(); } - public static File getCacheDir() { + public static File getCacheDir(Context context) { if (sCacheFile == null) - sCacheFile = GeckoApp.mAppContext.getCacheDir(); + sCacheFile = context.getCacheDir(); return sCacheFile; } - public static long getFreeSpace() { + public static long getFreeSpace(Context context) { try { if (sFreeSpace == -1) { - File cacheDir = getCacheDir(); + File cacheDir = getCacheDir(context); if (cacheDir != null) { StatFs cacheStats = new StatFs(cacheDir.getPath()); sFreeSpace = cacheStats.getFreeBlocks() * @@ -236,17 +247,46 @@ public class GeckoAppShell return sFreeSpace; } + public static File getGREDir(Context context) { + if (sGREDir == null) + sGREDir = new File(context.getApplicationInfo().dataDir); + return sGREDir; + } + // java-side stuff - public static boolean loadLibsSetup(String apkName) { + public static void loadLibsSetup(Context context) { + if (sLibsSetup) + return; + // The package data lib directory isn't placed in ld.so's // search path, so we have to manually load libraries that // libxul will depend on. Not ideal. - GeckoApp geckoApp = GeckoApp.mAppContext; - GeckoProfile profile = geckoApp.getProfile(); - profile.moveProfilesToAppInstallLocation(); + GeckoProfile profile = GeckoProfile.get(context); + File cacheFile = getCacheDir(context); + putenv("GRE_HOME=" + getGREDir(context).getPath()); + File[] files = cacheFile.listFiles(); + if (files != null) { + Iterator cacheFiles = Arrays.asList(files).iterator(); + while (cacheFiles.hasNext()) { + File libFile = cacheFiles.next(); + if (libFile.getName().endsWith(".so")) + libFile.delete(); + } + } + + // setup the libs cache + String linkerCache = System.getenv("MOZ_LINKER_CACHE"); + if (System.getenv("MOZ_LINKER_CACHE") == null) { + GeckoAppShell.putenv("MOZ_LINKER_CACHE=" + cacheFile.getPath()); + } + sLibsSetup = true; + } + + private static void setupPluginEnvironment(GeckoApp context) { + // setup plugin path directories try { - String[] dirs = GeckoApp.mAppContext.getPluginDirectories(); + String[] dirs = context.getPluginDirectories(); StringBuffer pluginSearchPath = new StringBuffer(); for (int i = 0; i < dirs.length; i++) { Log.i(LOGTAG, "dir: " + dirs[i]); @@ -254,49 +294,22 @@ public class GeckoAppShell pluginSearchPath.append(":"); } GeckoAppShell.putenv("MOZ_PLUGIN_PATH="+pluginSearchPath); + + File pluginDataDir = context.getDir("plugins", 0); + GeckoAppShell.putenv("ANDROID_PLUGIN_DATADIR=" + pluginDataDir.getPath()); + } catch (Exception ex) { Log.i(LOGTAG, "exception getting plugin dirs", ex); } + } - GeckoAppShell.putenv("HOME=" + profile.getFilesDir().getPath()); - GeckoAppShell.putenv("GRE_HOME=" + GeckoApp.sGREDir.getPath()); - Intent i = geckoApp.getIntent(); - String env = i.getStringExtra("env0"); - Log.i(LOGTAG, "env0: "+ env); - for (int c = 1; env != null; c++) { - GeckoAppShell.putenv(env); - env = i.getStringExtra("env" + c); - Log.i(LOGTAG, "env"+ c +": "+ env); - } - - File f = geckoApp.getDir("tmp", Context.MODE_WORLD_READABLE | - Context.MODE_WORLD_WRITEABLE ); - - if (!f.exists()) - f.mkdirs(); - - GeckoAppShell.putenv("TMPDIR=" + f.getPath()); - - f = Environment.getDownloadCacheDirectory(); - GeckoAppShell.putenv("EXTERNAL_STORAGE=" + f.getPath()); - - File cacheFile = getCacheDir(); - String linkerCache = System.getenv("MOZ_LINKER_CACHE"); - if (System.getenv("MOZ_LINKER_CACHE") == null) { - GeckoAppShell.putenv("MOZ_LINKER_CACHE=" + cacheFile.getPath()); - } - - File pluginDataDir = GeckoApp.mAppContext.getDir("plugins", 0); - GeckoAppShell.putenv("ANDROID_PLUGIN_DATADIR=" + pluginDataDir.getPath()); - - // gingerbread introduces File.getUsableSpace(). We should use that. - long freeSpace = getFreeSpace(); + private static void setupDownloadEnvironment(GeckoApp context) { try { File downloadDir = null; File updatesDir = null; if (Build.VERSION.SDK_INT >= 8) { downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); - updatesDir = GeckoApp.mAppContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS); + updatesDir = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS); } else { updatesDir = downloadDir = new File(Environment.getExternalStorageDirectory().getPath(), "download"); } @@ -306,38 +319,63 @@ public class GeckoAppShell catch (Exception e) { Log.i(LOGTAG, "No download directory has been found: " + e); } - - putLocaleEnv(); - - boolean extractLibs = GeckoApp.ACTION_DEBUG.equals(i.getAction()); - if (!extractLibs) { - // remove any previously extracted libs - File[] files = cacheFile.listFiles(); - if (files != null) { - Iterator cacheFiles = Arrays.asList(files).iterator(); - while (cacheFiles.hasNext()) { - File libFile = cacheFiles.next(); - if (libFile.getName().endsWith(".so")) - libFile.delete(); - } - } - } - return extractLibs; } - public static void ensureSQLiteLibsLoaded(String apkName) { + public static void setupGeckoEnvironment(Context context) { + GeckoProfile profile = GeckoProfile.get(context); + profile.moveProfilesToAppInstallLocation(); + + setupPluginEnvironment((GeckoApp) context); + setupDownloadEnvironment((GeckoApp) context); + + // profile home path + GeckoAppShell.putenv("HOME=" + profile.getFilesDir().getPath()); + + Intent i = null; + i = ((Activity)context).getIntent(); + + // if we have an intent (we're being launched by an activity) + // read in any environmental variables from it here + String env = i.getStringExtra("env0"); + Log.i(LOGTAG, "env0: "+ env); + for (int c = 1; env != null; c++) { + GeckoAppShell.putenv(env); + env = i.getStringExtra("env" + c); + Log.i(LOGTAG, "env"+ c +": "+ env); + } + // setup the tmp path + File f = context.getDir("tmp", Context.MODE_WORLD_READABLE | + Context.MODE_WORLD_WRITEABLE ); + if (!f.exists()) + f.mkdirs(); + GeckoAppShell.putenv("TMPDIR=" + f.getPath()); + + // setup the downloads path + f = Environment.getDownloadCacheDirectory(); + GeckoAppShell.putenv("EXTERNAL_STORAGE=" + f.getPath()); + + putLocaleEnv(); + } + + public static void loadSQLiteLibs(Context context, String apkName) { if (sSQLiteLibsLoaded) return; synchronized(sSQLiteLibsLoaded) { if (sSQLiteLibsLoaded) return; - loadSQLiteLibsNative(apkName, loadLibsSetup(apkName)); + loadMozGlue(); + // the extract libs parameter is being removed in bug 732069 + loadSQLiteLibsNative(apkName, false); sSQLiteLibsLoaded = true; } } + public static void loadMozGlue() { + System.loadLibrary("mozglue"); + } + public static void loadGeckoLibs(String apkName) { - boolean extractLibs = loadLibsSetup(apkName); + loadLibsSetup(GeckoApp.mAppContext); loadGeckoLibsNative(apkName); } @@ -1773,7 +1811,7 @@ public class GeckoAppShell return false; } - public static void emitGeckoAccessibilityEvent (int eventType, String role, String text, String description, boolean enabled, boolean checked, boolean password) { + public static void emitGeckoAccessibilityEvent (int eventType, String[] textList, String description, boolean enabled, boolean checked, boolean password) { AccessibilityManager accessibilityManager = (AccessibilityManager) GeckoApp.mAppContext.getSystemService(Context.ACCESSIBILITY_SERVICE); @@ -1784,13 +1822,14 @@ public class GeckoAppShell LayerView layerView = layerController.getView(); AccessibilityEvent event = AccessibilityEvent.obtain(eventType); - event.setClassName(layerView.getClass().getName() + "$" + role); + event.setClassName(layerView.getClass().getName()); event.setPackageName(GeckoApp.mAppContext.getPackageName()); event.setEnabled(enabled); event.setChecked(checked); event.setPassword(password); event.setContentDescription(description); - event.getText().add(text); + for (String text: textList) + event.getText().add(text); accessibilityManager.sendAccessibilityEvent(event); } diff --git a/mobile/android/base/GeckoThread.java b/mobile/android/base/GeckoThread.java index 3276ed219aa..2f339278703 100644 --- a/mobile/android/base/GeckoThread.java +++ b/mobile/android/base/GeckoThread.java @@ -66,7 +66,7 @@ public class GeckoThread extends Thread { public void run() { final GeckoApp app = GeckoApp.mAppContext; - File cacheFile = GeckoAppShell.getCacheDir(); + File cacheFile = GeckoAppShell.getCacheDir(app); File libxulFile = new File(cacheFile, "libxul.so"); if ((!libxulFile.exists() || @@ -86,9 +86,12 @@ public class GeckoThread extends Thread { // At some point while loading the gecko libs our default locale gets set // so just save it to locale here and reset it as default after the join Locale locale = Locale.getDefault(); + String resourcePath = app.getApplication().getPackageResourcePath(); - GeckoAppShell.ensureSQLiteLibsLoaded(resourcePath); + GeckoAppShell.setupGeckoEnvironment(app); + GeckoAppShell.loadSQLiteLibs(app, resourcePath); GeckoAppShell.loadGeckoLibs(resourcePath); + Locale.setDefault(locale); Resources res = app.getBaseContext().getResources(); Configuration config = res.getConfiguration(); @@ -98,15 +101,10 @@ public class GeckoThread extends Thread { Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - runGecko"); // and then fire us up - try { - Log.w(LOGTAG, "RunGecko - URI = " + mUri); - - GeckoAppShell.runGecko(app.getApplication().getPackageResourcePath(), - mIntent.getStringExtra("args"), - mUri, - mRestoreSession); - } catch (Exception e) { - GeckoAppShell.reportJavaCrash(e); - } + Log.w(LOGTAG, "RunGecko - URI = " + mUri); + GeckoAppShell.runGecko(app.getApplication().getPackageResourcePath(), + mIntent.getStringExtra("args"), + mUri, + mRestoreSession); } } diff --git a/mobile/android/base/ProfileMigrator.java b/mobile/android/base/ProfileMigrator.java index 7c956d47e8c..2c0544ba95c 100644 --- a/mobile/android/base/ProfileMigrator.java +++ b/mobile/android/base/ProfileMigrator.java @@ -640,7 +640,7 @@ public class ProfileMigrator { File dbFileShm = new File(dbPathShm); SQLiteBridge db = null; - GeckoAppShell.ensureSQLiteLibsLoaded(GeckoApp.mAppContext.getApplication().getPackageResourcePath()); + GeckoAppShell.loadSQLiteLibs(GeckoApp.mAppContext, GeckoApp.mAppContext.getApplication().getPackageResourcePath()); try { db = new SQLiteBridge(dbPath); calculateReroot(db); @@ -664,7 +664,7 @@ public class ProfileMigrator { } protected void cleanupXULLibCache() { - File cacheFile = GeckoAppShell.getCacheDir(); + File cacheFile = GeckoAppShell.getCacheDir(GeckoApp.mAppContext); File[] files = cacheFile.listFiles(); if (files != null) { Iterator cacheFiles = Arrays.asList(files).iterator(); diff --git a/mobile/android/base/Tab.java b/mobile/android/base/Tab.java index 0386316ab45..26795b293f4 100644 --- a/mobile/android/base/Tab.java +++ b/mobile/android/base/Tab.java @@ -78,7 +78,6 @@ public final class Tab { private int mHistoryIndex; private int mParentId; private boolean mExternal; - private boolean mLoading; private boolean mBookmark; private HashMap mDoorHangers; private long mFaviconLoadId; @@ -88,9 +87,14 @@ public final class Tab { private boolean mHasTouchListeners; private ArrayList mPluginViews; private HashMap mPluginLayers; - private boolean mHasLoaded; private ContentResolver mContentResolver; private ContentObserver mContentObserver; + private int mState; + + public static final int STATE_DELAYED = 0; + public static final int STATE_LOADING = 1; + public static final int STATE_SUCCESS = 2; + public static final int STATE_ERROR = 3; public static final class HistoryEntry { public String mUri; // must never be null @@ -121,7 +125,7 @@ public final class Tab { mContentType = ""; mPluginViews = new ArrayList(); mPluginLayers = new HashMap(); - mHasLoaded = false; + mState = STATE_LOADING; mContentResolver = Tabs.getInstance().getContentResolver(); mContentObserver = new ContentObserver(GeckoAppShell.getHandler()) { public void onChange(boolean selfChange) { @@ -232,7 +236,9 @@ public final class Tab { b.getHeight()); Bitmap bitmap = Bitmap.createScaledBitmap(cropped, getThumbnailWidth(), getThumbnailHeight(), false); - saveThumbnailToDB(new BitmapDrawable(bitmap)); + + if (mState == Tab.STATE_SUCCESS) + saveThumbnailToDB(new BitmapDrawable(bitmap)); if (!cropped.equals(b)) b.recycle(); @@ -262,10 +268,6 @@ public final class Tab { return mSecurityMode; } - public boolean isLoading() { - return mLoading; - } - public boolean isBookmark() { return mBookmark; } @@ -321,8 +323,12 @@ public final class Tab { } } - public void setLoading(boolean loading) { - mLoading = loading; + public void setState(int state) { + mState = state; + } + + public int getState() { + return mState; } public void setHasTouchListeners(boolean aValue) { @@ -467,14 +473,6 @@ public final class Tab { return mDoorHangers; } - public void setHasLoaded(boolean hasLoaded) { - mHasLoaded = hasLoaded; - } - - public boolean hasLoaded() { - return mHasLoaded; - } - void handleSessionHistoryMessage(String event, JSONObject message) throws JSONException { if (event.equals("New")) { final String uri = message.getString("uri"); diff --git a/mobile/android/base/Tabs.java b/mobile/android/base/Tabs.java index 1626b59dcd7..43a52ddae74 100644 --- a/mobile/android/base/Tabs.java +++ b/mobile/android/base/Tabs.java @@ -136,7 +136,7 @@ public class Tabs implements GeckoEventListener { GeckoApp.mBrowserToolbar.setTitle(tab.getDisplayTitle()); GeckoApp.mBrowserToolbar.setFavicon(tab.getFavicon()); GeckoApp.mBrowserToolbar.setSecurityMode(tab.getSecurityMode()); - GeckoApp.mBrowserToolbar.setProgressVisibility(tab.isLoading()); + GeckoApp.mBrowserToolbar.setProgressVisibility(tab.getState() == Tab.STATE_LOADING); GeckoApp.mDoorHangerPopup.updatePopup(); GeckoApp.mBrowserToolbar.setShadowVisibility((url == null) || !url.startsWith("about:")); notifyListeners(tab, TabEvents.SELECTED); @@ -283,7 +283,7 @@ public class Tabs implements GeckoEventListener { if (message.getBoolean("selected")) selectTab(tab.getId()); if (message.getBoolean("delayLoad")) - tab.setHasLoaded(false); + tab.setState(Tab.STATE_DELAYED); } else if (event.equals("Tab:Close")) { Tab tab = getTab(message.getInt("tabID")); closeTab(tab); diff --git a/mobile/android/base/db/GeckoProvider.java.in b/mobile/android/base/db/GeckoProvider.java.in index 4cbb30cdf2f..6944efc18ff 100644 --- a/mobile/android/base/db/GeckoProvider.java.in +++ b/mobile/android/base/db/GeckoProvider.java.in @@ -88,6 +88,8 @@ public abstract class GeckoProvider extends ContentProvider { boolean dbNeedsSetup = true; try { + String resourcePath = context.getPackageResourcePath(); + GeckoAppShell.loadSQLiteLibs(context, resourcePath); bridge = new SQLiteBridge(databasePath); int version = bridge.getVersion(); Log.i(mLogTag, version + " == " + mDBVersion); diff --git a/mobile/android/base/db/LocalBrowserDB.java b/mobile/android/base/db/LocalBrowserDB.java index 8eeed44d3c9..ff734c51e0a 100644 --- a/mobile/android/base/db/LocalBrowserDB.java +++ b/mobile/android/base/db/LocalBrowserDB.java @@ -89,6 +89,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { private static final String[] DEFAULT_BOOKMARK_COLUMNS = new String[] { Bookmarks._ID, + Bookmarks.GUID, Bookmarks.URL, Bookmarks.TITLE, Bookmarks.IS_FOLDER, diff --git a/mobile/android/base/gfx/ViewportMetrics.java b/mobile/android/base/gfx/ViewportMetrics.java index 5afd9df5e48..eee9ead3df9 100644 --- a/mobile/android/base/gfx/ViewportMetrics.java +++ b/mobile/android/base/gfx/ViewportMetrics.java @@ -64,6 +64,7 @@ public class ViewportMetrics { private RectF mViewportRect; private PointF mViewportOffset; private float mZoomFactor; + private boolean mAllowZoom; // A scale from -1,-1 to 1,1 that represents what edge of the displayport // we want the viewport to be biased towards. @@ -79,6 +80,7 @@ public class ViewportMetrics { mViewportOffset = new PointF(0, 0); mZoomFactor = 1.0f; mViewportBias = new PointF(0.0f, 0.0f); + mAllowZoom = true; } public ViewportMetrics(ViewportMetrics viewport) { @@ -88,6 +90,7 @@ public class ViewportMetrics { mViewportOffset = new PointF(offset.x, offset.y); mZoomFactor = viewport.getZoomFactor(); mViewportBias = viewport.mViewportBias; + mAllowZoom = viewport.mAllowZoom; } public ViewportMetrics(JSONObject json) throws JSONException { @@ -101,6 +104,8 @@ public class ViewportMetrics { float offsetY = (float)json.getDouble("offsetY"); float zoom = (float)json.getDouble("zoom"); + mAllowZoom = json.getBoolean("allowZoom"); + mPageSize = new FloatSize(pageWidth, pageHeight); mViewportRect = new RectF(x, y, x + width, y + height); mViewportOffset = new PointF(offsetX, offsetY); @@ -186,6 +191,10 @@ public class ViewportMetrics { return mZoomFactor; } + public boolean getAllowZoom() { + return mAllowZoom; + } + public void setPageSize(FloatSize pageSize) { mPageSize = pageSize; } diff --git a/mobile/android/base/resources/layout/awesomebar_search.xml b/mobile/android/base/resources/layout/awesomebar_search.xml index d66e21e24f6..38ca20d3f40 100644 --- a/mobile/android/base/resources/layout/awesomebar_search.xml +++ b/mobile/android/base/resources/layout/awesomebar_search.xml @@ -14,6 +14,7 @@ android:hint="@string/awesomebar_default_text" android:inputType="textUri|textNoSuggestions" android:imeOptions="actionSearch" + android:selectAllOnFocus="true" android:singleLine="true" android:gravity="center_vertical|left"> diff --git a/mobile/android/base/sync/CollectionKeys.java b/mobile/android/base/sync/CollectionKeys.java index 8a8f0903c9f..f3a7a77f602 100644 --- a/mobile/android/base/sync/CollectionKeys.java +++ b/mobile/android/base/sync/CollectionKeys.java @@ -42,7 +42,6 @@ import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Map.Entry; -import org.json.JSONException; import org.json.simple.JSONArray; import org.json.simple.parser.ParseException; import org.mozilla.apache.commons.codec.binary.Base64; @@ -101,11 +100,6 @@ public class CollectionKeys { /** * Take a pair of values in a JSON array, handing them off to KeyBundle to * produce a usable keypair. - * - * @param array - * @return - * @throws JSONException - * @throws UnsupportedEncodingException */ private static KeyBundle arrayToKeyBundle(JSONArray array) throws UnsupportedEncodingException { String encKeyStr = (String) array.get(0); @@ -168,14 +162,6 @@ public class CollectionKeys { /** * Take a downloaded record, and the Sync Key, decrypting the record and * setting our own keys accordingly. - * - * @param keys - * @param syncKeyBundle - * @throws CryptoException - * @throws IOException - * @throws ParseException - * @throws NonObjectJSONException - * @throws JSONException */ public void setKeyPairsFromWBO(CryptoRecord keys, KeyBundle syncKeyBundle) throws CryptoException, diff --git a/mobile/android/base/sync/HTTPFailureException.java b/mobile/android/base/sync/HTTPFailureException.java index cb7e5845c15..6fbb2a300e1 100644 --- a/mobile/android/base/sync/HTTPFailureException.java +++ b/mobile/android/base/sync/HTTPFailureException.java @@ -51,11 +51,12 @@ public class HTTPFailureException extends SyncException { @Override public String toString() { - String errorMessage = "[unknown error message]"; + String errorMessage; try { errorMessage = this.response.getErrorMessage(); } catch (Exception e) { // Oh well. + errorMessage = "[unknown error message]"; } return ""; diff --git a/mobile/android/base/sync/SyncConfiguration.java b/mobile/android/base/sync/SyncConfiguration.java index b0019136965..145ae1a5865 100644 --- a/mobile/android/base/sync/SyncConfiguration.java +++ b/mobile/android/base/sync/SyncConfiguration.java @@ -193,9 +193,6 @@ public class SyncConfiguration implements CredentialsSource { /** * Create a new SyncConfiguration instance. Pass in a PrefsSource to * provide access to preferences. - * - * @param prefsPath - * @param prefsSource */ public SyncConfiguration(String prefsPath, PrefsSource prefsSource) { this.prefsPath = prefsPath; @@ -210,7 +207,6 @@ public class SyncConfiguration implements CredentialsSource { /** * Return a convenient accessor for part of prefs. - * @param prefix * @return * A ConfigurationBranch object representing this * section of the preferences space. diff --git a/mobile/android/base/sync/Utils.java b/mobile/android/base/sync/Utils.java index 66e3c4bba1f..d4d16b88bb2 100644 --- a/mobile/android/base/sync/Utils.java +++ b/mobile/android/base/sync/Utils.java @@ -69,10 +69,11 @@ public class Utils { return new String(encodedBytes).replace("+", "-").replace("/", "_"); } - /* + /** * Helper to generate secure random bytes. * - * @param length Number of bytes to generate. + * @param length + * Number of bytes to generate. */ public static byte[] generateRandomBytes(int length) { byte[] bytes = new byte[length]; @@ -80,10 +81,11 @@ public class Utils { return bytes; } - /* + /** * Helper to generate a random integer in a specified range. * - * @param r Generate an integer between 0 and r-1 inclusive. + * @param r + * Generate an integer between 0 and r-1 inclusive. */ public static BigInteger generateBigIntegerLessThan(BigInteger r) { int maxBytes = (int) Math.ceil(((double) r.bitLength()) / 8); @@ -91,17 +93,15 @@ public class Utils { return randInt.mod(r); } - /* + /** * Helper to reseed the shared secure random number generator. */ public static void reseedSharedRandom() { sharedSecureRandom.setSeed(sharedSecureRandom.generateSeed(8)); } - /* - * Helper to convert Byte Array to a Hex String - * Input: byte[] array - * Output: Hex string + /** + * Helper to convert a byte array to a hex-encoded string */ public static String byte2hex(byte[] b) { // StringBuffer should be used instead. @@ -125,11 +125,6 @@ public class Utils { return hs; } - /* - * Helper for array concatenation. - * Input: At least two byte[] - * Output: A concatenated version of them - */ public static byte[] concatAll(byte[] first, byte[]... rest) { int totalLength = first.length; for (byte[] array : rest) { @@ -163,20 +158,12 @@ public class Utils { return Base64.decodeBase64(base64.getBytes("UTF-8")); } - /* - * Decode a friendly base32 string. - */ public static byte[] decodeFriendlyBase32(String base32) { Base32 converter = new Base32(); final String translated = base32.replace('8', 'l').replace('9', 'o'); return converter.decode(translated.toUpperCase()); } - /* - * Helper to convert Hex String to Byte Array - * Input: Hex string - * Output: byte[] version of hex string - */ public static byte[] hex2Byte(String str) { if (str.length() % 2 == 1) { str = "0" + str; @@ -247,10 +234,6 @@ public class Utils { /** * Yes, an equality method that's null-safe. - * - * @param a - * @param b - * @return */ private static boolean same(Object a, Object b) { if (a == b) { @@ -265,10 +248,6 @@ public class Utils { /** * Return true if the two arrays are both null, or are both arrays * containing the same elements in the same order. - * - * @param a - * @param b - * @return */ public static boolean sameArrays(JSONArray a, JSONArray b) { if (a == b) { diff --git a/mobile/android/base/sync/crypto/KeyBundle.java b/mobile/android/base/sync/crypto/KeyBundle.java index ce149993a3f..c05fa787029 100644 --- a/mobile/android/base/sync/crypto/KeyBundle.java +++ b/mobile/android/base/sync/crypto/KeyBundle.java @@ -46,7 +46,6 @@ import javax.crypto.Mac; import org.mozilla.apache.commons.codec.binary.Base64; import org.mozilla.gecko.sync.Utils; -import org.mozilla.gecko.sync.crypto.CryptoException; import java.security.InvalidKeyException; import java.util.Locale; diff --git a/mobile/android/base/sync/jpake/JPakeCrypto.java b/mobile/android/base/sync/jpake/JPakeCrypto.java index d3f5104b71e..2c4bb6bc4cd 100644 --- a/mobile/android/base/sync/jpake/JPakeCrypto.java +++ b/mobile/android/base/sync/jpake/JPakeCrypto.java @@ -98,10 +98,6 @@ public class JPakeCrypto { * * Round 1 of J-PAKE protocol. * Generate x1, x2, and ZKP for other party. - * - * @param mySignerId - * @param valuesOut - * @throws NoSuchAlgorithmException */ public static void round1(JPakeParty jp, JPakeNumGenerator gen) throws NoSuchAlgorithmException, UnsupportedEncodingException { // Randomly select x1 from [0,q), x2 from [1,q). @@ -124,16 +120,6 @@ public class JPakeCrypto { * Round 2 of J-PAKE protocol. * Generate A and ZKP for A. * Verify ZKP from other party. Does not check for replay ZKP. - * - * @param mySignerId - * @param valuesOut - * @param secret - * @param gx3 - * @param gx4 - * @param zkp3 - * @param zkp4 - * @throws IncorrectZkpException - * @throws NoSuchAlgorithmException */ public static void round2(BigInteger secretValue, JPakeParty jp, JPakeNumGenerator gen) throws IncorrectZkpException, NoSuchAlgorithmException, @@ -164,13 +150,6 @@ public class JPakeCrypto { /** * Final round of J-PAKE protocol. - * - * @param b - * @param zkp - * @param secret - * - * @return KeyBundle - * @throws IncorrectZkpException */ public static KeyBundle finalRound(BigInteger secretValue, JPakeParty jp) throws IncorrectZkpException, NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException { diff --git a/mobile/android/base/sync/jpake/stage/GetChannelStage.java b/mobile/android/base/sync/jpake/stage/GetChannelStage.java index 7efd92dd370..5ac2ccfed72 100644 --- a/mobile/android/base/sync/jpake/stage/GetChannelStage.java +++ b/mobile/android/base/sync/jpake/stage/GetChannelStage.java @@ -54,14 +54,12 @@ public class GetChannelStage extends JPakeStage { public void handleFailure(String error) { Logger.error(LOG_TAG, "Got HTTP failure: " + error); jClient.abort(error); - return; } @Override public void handleError(Exception e) { Logger.error(LOG_TAG, "Threw HTTP exception.", e); jClient.abort(Constants.JPAKE_ERROR_CHANNEL); - return; } }; diff --git a/mobile/android/base/sync/jpake/stage/GetRequestStage.java b/mobile/android/base/sync/jpake/stage/GetRequestStage.java index d697e6e624b..c571e157a78 100644 --- a/mobile/android/base/sync/jpake/stage/GetRequestStage.java +++ b/mobile/android/base/sync/jpake/stage/GetRequestStage.java @@ -75,14 +75,12 @@ public class GetRequestStage extends JPakeStage { public void handleFailure(String error) { Logger.error(LOG_TAG, "Got HTTP failure: " + error); jClient.abort(error); - return; } @Override public void handleError(Exception e) { Logger.error(LOG_TAG, "Threw HTTP exception.", e); jClient.abort(Constants.JPAKE_ERROR_NETWORK); - return; } }; diff --git a/mobile/android/base/sync/jpake/stage/PutRequestStage.java b/mobile/android/base/sync/jpake/stage/PutRequestStage.java index bdf0c2e7fa6..6264906f857 100644 --- a/mobile/android/base/sync/jpake/stage/PutRequestStage.java +++ b/mobile/android/base/sync/jpake/stage/PutRequestStage.java @@ -60,14 +60,12 @@ public class PutRequestStage extends JPakeStage { public void handleFailure(String error) { Logger.error(LOG_TAG, "Got HTTP failure: " + error); jClient.abort(error); - return; } @Override public void handleError(Exception e) { Logger.error(LOG_TAG, "HTTP exception.", e); jClient.abort(Constants.JPAKE_ERROR_NETWORK); - return; } }; diff --git a/mobile/android/base/sync/net/BaseResource.java b/mobile/android/base/sync/net/BaseResource.java index 999094df150..53884ec4aa0 100644 --- a/mobile/android/base/sync/net/BaseResource.java +++ b/mobile/android/base/sync/net/BaseResource.java @@ -82,6 +82,8 @@ import ch.boye.httpclientandroidlib.protocol.HttpContext; * Exposes simple get/post/put/delete methods. */ public class BaseResource implements Resource { + private static final String ANDROID_LOOPBACK_IP = "10.0.2.2"; + public static boolean rewriteLocalhost = true; private static final String LOG_TAG = "BaseResource"; @@ -107,9 +109,9 @@ public class BaseResource implements Resource { public BaseResource(URI uri, boolean rewrite) { if (rewrite && uri.getHost().equals("localhost")) { // Rewrite localhost URIs to refer to the special Android emulator loopback passthrough interface. - Log.d(LOG_TAG, "Rewriting " + uri + " to point to 10.0.2.2."); + Log.d(LOG_TAG, "Rewriting " + uri + " to point to " + ANDROID_LOOPBACK_IP + "."); try { - this.uri = new URI(uri.getScheme(), uri.getUserInfo(), "10.0.2.2", uri.getPort(), uri.getPath(), uri.getQuery(), uri.getFragment()); + this.uri = new URI(uri.getScheme(), uri.getUserInfo(), ANDROID_LOOPBACK_IP, uri.getPort(), uri.getPath(), uri.getQuery(), uri.getFragment()); } catch (URISyntaxException e) { Log.e(LOG_TAG, "Got error rewriting URI for Android emulator.", e); } @@ -167,7 +169,6 @@ public class BaseResource implements Resource { /** * This method exists for test code. - * @return */ public static ClientConnectionManager enablePlainHTTPConnectionManager() { synchronized (connManagerMonitor) { diff --git a/mobile/android/base/sync/net/SyncResponse.java b/mobile/android/base/sync/net/SyncResponse.java index 5a6a8ea2ffa..4d7bb197675 100644 --- a/mobile/android/base/sync/net/SyncResponse.java +++ b/mobile/android/base/sync/net/SyncResponse.java @@ -148,9 +148,9 @@ public class SyncResponse { } /** - * @return A number of seconds, or -1 if the header was not present. + * @return A number of seconds, or -1 if the 'Retry-After' header was not present. */ - public int retryAfter() throws NumberFormatException { + public int retryAfterInSeconds() throws NumberFormatException { if (!this.hasHeader(HEADER_RETRY_AFTER)) { return -1; } @@ -178,10 +178,39 @@ public class SyncResponse { } } - public int weaveBackoff() throws NumberFormatException { + /** + * @return A number of seconds, or -1 if the 'X-Weave-Backoff' header was not + * present. + */ + public int weaveBackoffInSeconds() throws NumberFormatException { return this.getIntegerHeader("x-weave-backoff"); } + /** + * @return A number of milliseconds, or -1 if neither the 'Retry-After' or + * 'X-Weave-Backoff' header was present. + */ + public int totalBackoffInMilliseconds() { + int retryAfterInSeconds = -1; + try { + retryAfterInSeconds = retryAfterInSeconds(); + } catch (NumberFormatException e) { + } + + int weaveBackoffInSeconds = -1; + try { + weaveBackoffInSeconds = weaveBackoffInSeconds(); + } catch (NumberFormatException e) { + } + + int totalBackoff = Math.max(retryAfterInSeconds, weaveBackoffInSeconds); + if (totalBackoff < 0) { + return -1; + } else { + return 1000 * totalBackoff; + } + } + /** * The timestamp returned from a Sync server is a decimal number of seconds, * e.g., 1323393518.04. diff --git a/mobile/android/base/sync/net/SyncStorageRecordRequest.java b/mobile/android/base/sync/net/SyncStorageRecordRequest.java index bb303249e20..5987ee46aaf 100644 --- a/mobile/android/base/sync/net/SyncStorageRecordRequest.java +++ b/mobile/android/base/sync/net/SyncStorageRecordRequest.java @@ -90,8 +90,6 @@ public class SyncStorageRecordRequest extends SyncStorageRequest { /** * Helper for turning a JSON object into a payload. - * @param body - * @return * @throws UnsupportedEncodingException */ protected StringEntity jsonEntity(JSONObject body) throws UnsupportedEncodingException { diff --git a/mobile/android/base/sync/repositories/RepositorySession.java b/mobile/android/base/sync/repositories/RepositorySession.java index 443b763c029..205735b2689 100644 --- a/mobile/android/base/sync/repositories/RepositorySession.java +++ b/mobile/android/base/sync/repositories/RepositorySession.java @@ -209,9 +209,6 @@ public abstract class RepositorySession { * * The Synchronizer most likely wants to bump the bundle timestamp to be a value * return from a fetch call. - * - * @param optional - * @return */ protected RepositorySessionBundle getBundle(RepositorySessionBundle optional) { Logger.debug(LOG_TAG, "RepositorySession.getBundle(optional)."); @@ -225,8 +222,6 @@ public abstract class RepositorySession { /** * Just like finish(), but doesn't do any work that should only be performed * at the end of a successful sync, and can be called any time. - * - * @param delegate */ public void abort(RepositorySessionFinishDelegate delegate) { this.abort(); @@ -267,9 +262,6 @@ public abstract class RepositorySession { /** * Run the provided command if we're active and our delegate queue * is not shut down. - * - * @param command - * @throws InactiveSessionException */ protected synchronized void executeDelegateCommand(Runnable command) throws InactiveSessionException { @@ -389,8 +381,6 @@ public abstract class RepositorySession { * redundantly. * * The default implementation does nothing. - * - * @param record */ protected synchronized void trackRecord(Record record) { } diff --git a/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java b/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java index 35c21e4518d..e9b623ab1bb 100644 --- a/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java +++ b/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java @@ -100,10 +100,6 @@ public class AndroidBrowserBookmarksDataAccessor extends AndroidBrowserRepositor /** * Bump the modified time of a record by ID. - * - * @param id - * @param modified - * @return */ public int bumpModified(long id, long modified) { Logger.debug(LOG_TAG, "Bumping modified for " + id + " to " + modified); diff --git a/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java b/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java index 8f0044c04e9..3fa15fbe581 100644 --- a/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java +++ b/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java @@ -172,9 +172,6 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo /** * Return true if the provided record GUID should be skipped * in child lists or fetch results. - * - * @param recordGUID - * @return */ public static boolean forbiddenGUID(String recordGUID) { return recordGUID == null || diff --git a/mobile/android/base/sync/repositories/android/AndroidBrowserRepositorySession.java b/mobile/android/base/sync/repositories/android/AndroidBrowserRepositorySession.java index 37beaa02972..8505f423b75 100644 --- a/mobile/android/base/sync/repositories/android/AndroidBrowserRepositorySession.java +++ b/mobile/android/base/sync/repositories/android/AndroidBrowserRepositorySession.java @@ -101,8 +101,6 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos * * Return null if this record should not be processed. * - * @param cur - * @return * @throws NoGuidForIdException * @throws NullCursorException * @throws ParentNotFoundException @@ -114,8 +112,6 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos * updated to match the record that we're constructing: for example, the children * of a folder might be repositioned as we generate the folder's record. * - * @param cur - * @return * @throws NoGuidForIdException * @throws NullCursorException * @throws ParentNotFoundException @@ -555,8 +551,6 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos * Retrieve a record from the store by GUID, without writing unnecessarily to the * database. * - * @param guid - * @return * @throws NoGuidForIdException * @throws NullCursorException * @throws ParentNotFoundException diff --git a/mobile/android/base/sync/repositories/android/ClientsDatabaseAccessor.java b/mobile/android/base/sync/repositories/android/ClientsDatabaseAccessor.java index d1346a81ff8..102a2fec3eb 100644 --- a/mobile/android/base/sync/repositories/android/ClientsDatabaseAccessor.java +++ b/mobile/android/base/sync/repositories/android/ClientsDatabaseAccessor.java @@ -88,11 +88,17 @@ public class ClientsDatabaseAccessor { } public int clientsCount() { + Cursor cur; try { - return db.fetchAll().getCount(); + cur = db.fetchAll(); } catch (NullCursorException e) { return 0; } + try { + return cur.getCount(); + } finally { + cur.close(); + } } private String getProfileId() { diff --git a/mobile/android/base/sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java b/mobile/android/base/sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java index 5983f13d279..a27f5a2d4bc 100644 --- a/mobile/android/base/sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java +++ b/mobile/android/base/sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java @@ -1,40 +1,6 @@ -/* ***** 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 Android Sync Client. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Jason Voll - * Richard Newman - * - * 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 ***** */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.sync.repositories.delegates; @@ -49,7 +15,7 @@ public interface RepositorySessionFetchRecordsDelegate { /** * Called when all records in this fetch have been returned. * - * @param end + * @param fetchEnd * A millisecond-resolution timestamp indicating the *remote* timestamp * at the end of the range of records. Usually this is the timestamp at * which the request was received. @@ -57,8 +23,19 @@ public interface RepositorySessionFetchRecordsDelegate { */ public void onFetchCompleted(final long fetchEnd); - // Shorthand for calling onFetchedRecord for each record in turn, then - // calling onFetchCompleted. + /** + * Shorthand for calling onFetchedRecord for each record in turn, then + * calling onFetchCompleted. + * + * @param records + * An array of fetched records. Can be empty. + * + * @param fetchEnd + * A millisecond-resolution timestamp indicating the *remote* timestamp + * at the end of the range of records. Usually this is the timestamp at + * which the request was received. + * E.g., the (normalized) value of the X-Weave-Timestamp header. + */ public void onFetchSucceeded(Record[] records, final long fetchEnd); public RepositorySessionFetchRecordsDelegate deferredFetchDelegate(ExecutorService executor); diff --git a/mobile/android/base/sync/repositories/domain/Record.java b/mobile/android/base/sync/repositories/domain/Record.java index 4ce59b4b6c1..c0eae5ab7fc 100644 --- a/mobile/android/base/sync/repositories/domain/Record.java +++ b/mobile/android/base/sync/repositories/domain/Record.java @@ -118,9 +118,6 @@ public abstract class Record { /** * Return true iff the input is a Record and has the same * collection and guid as this object. - * - * @param o - * @return */ public boolean equalIdentifiers(Object o) { if (o == null || !(o instanceof Record)) { diff --git a/mobile/android/base/sync/repositories/domain/TabsRecord.java b/mobile/android/base/sync/repositories/domain/TabsRecord.java index 4b9b7c47410..e2726da1958 100644 --- a/mobile/android/base/sync/repositories/domain/TabsRecord.java +++ b/mobile/android/base/sync/repositories/domain/TabsRecord.java @@ -12,7 +12,6 @@ import org.mozilla.gecko.sync.ExtendedJSONObject; import org.mozilla.gecko.sync.Logger; import org.mozilla.gecko.sync.NonArrayJSONException; import org.mozilla.gecko.sync.Utils; -import org.mozilla.gecko.sync.repositories.domain.Record; /** * Represents a client's collection of tabs. diff --git a/mobile/android/base/sync/setup/activities/AccountActivity.java b/mobile/android/base/sync/setup/activities/AccountActivity.java index 03c749f4ac4..9809683095e 100644 --- a/mobile/android/base/sync/setup/activities/AccountActivity.java +++ b/mobile/android/base/sync/setup/activities/AccountActivity.java @@ -213,11 +213,25 @@ public class AccountActivity extends AccountAuthenticatorActivity { userbundle.putString(Constants.OPTION_SERVER, DEFAULT_SERVER); } Log.d(LOG_TAG, "Adding account for " + Constants.ACCOUNTTYPE_SYNC); - boolean result = accountManager.addAccountExplicitly(account, password, userbundle); + boolean result = false; + try { + result = accountManager.addAccountExplicitly(account, password, userbundle); + } catch (SecurityException e) { + final String message = e.getMessage(); + if (message != null && (message.indexOf("is different than the authenticator's uid") > 0)) { + Log.wtf("FirefoxSync", + "Unable to create account. " + + "If you have more than one version of " + + "Firefox/Beta/Aurora/Nightly/Fennec installed, that's why.", + e); + } else { + Log.e("FirefoxSync", "Unable to create account.", e); + } + } Log.d(LOG_TAG, "Account: " + account + " added successfully? " + result); if (!result) { - Log.e(LOG_TAG, "Error adding account!"); + Log.e(LOG_TAG, "Failed to add account!"); } // Set components to sync (default: all). diff --git a/mobile/android/base/sync/stage/AndroidBrowserBookmarksServerSyncStage.java b/mobile/android/base/sync/stage/AndroidBrowserBookmarksServerSyncStage.java index 4ab79d634c7..be0697f01fc 100644 --- a/mobile/android/base/sync/stage/AndroidBrowserBookmarksServerSyncStage.java +++ b/mobile/android/base/sync/stage/AndroidBrowserBookmarksServerSyncStage.java @@ -52,11 +52,6 @@ public class AndroidBrowserBookmarksServerSyncStage extends ServerSyncStage { private static final String BOOKMARKS_SORT = "index"; private static final long BOOKMARKS_REQUEST_LIMIT = 5000; // Sanity limit. - @Override - public void execute(org.mozilla.gecko.sync.GlobalSession session) throws NoSuchStageException { - super.execute(session); - } - @Override protected String getCollection() { return "bookmarks"; diff --git a/mobile/android/base/sync/stage/AndroidBrowserHistoryServerSyncStage.java b/mobile/android/base/sync/stage/AndroidBrowserHistoryServerSyncStage.java index ab52ff13060..13bdc81dde6 100644 --- a/mobile/android/base/sync/stage/AndroidBrowserHistoryServerSyncStage.java +++ b/mobile/android/base/sync/stage/AndroidBrowserHistoryServerSyncStage.java @@ -52,11 +52,6 @@ public class AndroidBrowserHistoryServerSyncStage extends ServerSyncStage { private static final String HISTORY_SORT = "index"; private static final long HISTORY_REQUEST_LIMIT = 250; - @Override - public void execute(org.mozilla.gecko.sync.GlobalSession session) throws NoSuchStageException { - super.execute(session); - } - @Override protected String getCollection() { return "history"; diff --git a/mobile/android/base/sync/stage/ServerSyncStage.java b/mobile/android/base/sync/stage/ServerSyncStage.java index 6679ed088ae..1f04c35f359 100644 --- a/mobile/android/base/sync/stage/ServerSyncStage.java +++ b/mobile/android/base/sync/stage/ServerSyncStage.java @@ -95,10 +95,6 @@ public abstract class ServerSyncStage implements /** * Return a Crypto5Middleware-wrapped Server11Repository. * - * @param clusterURI - * @param data.username - * @param collection - * @return * @throws NoCollectionKeysSetException * @throws URISyntaxException */ diff --git a/mobile/android/base/ui/PanZoomController.java b/mobile/android/base/ui/PanZoomController.java index 44fcb51d6ac..81ccfdfafdb 100644 --- a/mobile/android/base/ui/PanZoomController.java +++ b/mobile/android/base/ui/PanZoomController.java @@ -770,6 +770,12 @@ public class PanZoomController public boolean onScale(SimpleScaleGestureDetector detector) { Log.d(LOGTAG, "onScale in state " + mState); + if (GeckoApp.mDOMFullScreen) + return false; + + if (!mController.getViewportMetrics().getAllowZoom()) + return false; + if (mState == PanZoomState.ANIMATED_ZOOM) return false; diff --git a/mobile/android/chrome/content/aboutAddons.js b/mobile/android/chrome/content/aboutAddons.js index d1efd95a902..bd81f999d3f 100644 --- a/mobile/android/chrome/content/aboutAddons.js +++ b/mobile/android/chrome/content/aboutAddons.js @@ -208,6 +208,22 @@ var Addons = { }, showDetails: function showDetails(aListItem) { + // This function removes and returns the text content of aNode without + // removing any child elements. Removing the text nodes ensures any XBL + // bindings apply properly. + function stripTextNodes(aNode) { + var text = ""; + for (var i = 0; i < aNode.childNodes.length; i++) { + if (aNode.childNodes[i].nodeType != document.ELEMENT_NODE) { + text += aNode.childNodes[i].textContent; + aNode.removeChild(aNode.childNodes[i--]); + } else { + text += stripTextNodes(aNode.childNodes[i]); + } + } + return text; + } + let detailItem = document.querySelector("#addons-details > .addon-item"); detailItem.setAttribute("isDisabled", aListItem.getAttribute("isDisabled")); detailItem.setAttribute("opType", aListItem.getAttribute("opType")); @@ -247,22 +263,6 @@ var Addons = { xhr.open("GET", optionsURL, false); xhr.send(); if (xhr.responseXML) { - // This function removes and returns the text content of aNode without - // removing any child elements. Removing the text nodes ensures any XBL - // bindings apply properly. - function stripTextNodes(aNode) { - var text = ''; - for (var i = 0; i < aNode.childNodes.length; i++) { - if (aNode.childNodes[i].nodeType != document.ELEMENT_NODE) { - text += aNode.childNodes[i].textContent; - aNode.removeChild(aNode.childNodes[i--]); - } else { - text += stripTextNodes(aNode.childNodes[i]); - } - } - return text; - } - // Only allow for now let settings = xhr.responseXML.querySelectorAll(":root > setting"); for (let i = 0; i < settings.length; i++) { diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 513dc11c115..ebaa113d08b 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -507,7 +507,7 @@ var BrowserApp = { aParams = aParams || {}; let flags = "flags" in aParams ? aParams.flags : Ci.nsIWebNavigation.LOAD_FLAGS_NONE; - let postData = ("postData" in aParams && aParams.postData) ? aParams.postData.value : null; + let postData = ("postData" in aParams && aParams.postData) ? aParams.postData : null; let referrerURI = "referrerURI" in aParams ? aParams.referrerURI : null; let charset = "charset" in aParams ? aParams.charset : null; @@ -830,16 +830,6 @@ var BrowserApp = { }); }, - getSearchOrURI: function getSearchOrURI(aParams) { - let uri; - if (aParams.engine) { - let engine = Services.search.getEngineByName(aParams.engine); - if (engine) - uri = engine.getSubmission(aParams.url).uri; - } - return uri ? uri.spec : aParams.url; - }, - scrollToFocusedInput: function(aBrowser) { let doc = aBrowser.contentDocument; if (!doc) @@ -955,7 +945,15 @@ var BrowserApp = { flags: flags }; - let url = this.getSearchOrURI(data); + let url = data.url; + if (data.engine) { + let engine = Services.search.getEngineByName(data.engine); + if (engine) { + let submission = engine.getSubmission(url); + url = submission.uri.spec; + params.postData = submission.postData; + } + } // Don't show progress throbber for about:home if (url == "about:home") @@ -1563,6 +1561,10 @@ Tab.prototype = { }; sendMessageToJava(message); + this.overscrollController = new OverscrollController(this); + this.browser.contentWindow.controllers + .insertControllerAt(0, this.overscrollController); + let flags = Ci.nsIWebProgress.NOTIFY_STATE_ALL | Ci.nsIWebProgress.NOTIFY_LOCATION | Ci.nsIWebProgress.NOTIFY_SECURITY; @@ -1611,6 +1613,9 @@ Tab.prototype = { if (!this.browser) return; + this.browser.controllers.contentWindow + .removeController(this.overscrollController); + this.browser.removeProgressListener(this); this.browser.removeEventListener("DOMContentLoaded", this, true); this.browser.removeEventListener("DOMLinkAdded", this, true); @@ -1739,6 +1744,8 @@ Tab.prototype = { pageWidth *= this._viewport.zoom; pageHeight *= this._viewport.zoom; + this._viewport.allowZoom = this.metadata.allowZoom; + /* * Avoid sending page sizes of less than screen size before we hit DOMContentLoaded, because * this causes the page size to jump around wildly during page load. After the page is loaded, @@ -1973,13 +1980,20 @@ Tab.prototype = { let restoring = aStateFlags & Ci.nsIWebProgressListener.STATE_RESTORING; let showProgress = restoring ? false : this.showProgress; + // true if the page loaded successfully (i.e., no 404s or other errors) + let success = false; + try { + success = aRequest.QueryInterface(Components.interfaces.nsIHttpChannel).requestSucceeded; + } catch (e) { } + let message = { gecko: { type: "Content:StateChange", tabID: this.id, uri: uri, state: aStateFlags, - showProgress: showProgress + showProgress: showProgress, + success: success } }; sendMessageToJava(message); @@ -2913,7 +2927,13 @@ var FormAssistant = { switch (aEvent.type) { case "focus": let currentElement = aEvent.target; - this._showValidationMessage(currentElement); + + // Prioritize a form validation message over autocomplete suggestions + // when the element is first focused (a form validation message will + // only be available if an invalid form was submitted) + if (this._showValidationMessage(currentElement)) + break; + this._showAutoCompleteSuggestions(currentElement) break; case "input": @@ -3500,7 +3520,8 @@ var PopupBlockerObserver = { let popupFeatures = pageReport[i].popupWindowFeatures; let popupName = pageReport[i].popupWindowName; - BrowserApp.addTab(popupURIspec); + let parent = BrowserApp.selectedTab; + BrowserApp.addTab(popupURIspec, { parentId: parent.id }); } } } @@ -4309,3 +4330,25 @@ var CharacterEncoding = { } }; +function OverscrollController(aTab) { + this.tab = aTab; +} + +OverscrollController.prototype = { + supportsCommand : function supportsCommand(aCommand) { + if (aCommand != "cmd_linePrevious" && aCommand != "cmd_scrollPageUp") + return false; + + return (this.tab.viewport.y == 0); + }, + + isCommandEnabled : function isCommandEnabled(aCommand) { + return this.supportsCommand(aCommand); + }, + + doCommand : function doCommand(aCommand){ + sendMessageToJava({ gecko: { type: "ToggleChrome:Focus" } }); + }, + + onEvent : function onEvent(aEvent) { } +}; diff --git a/mobile/xul/app/mobile.js b/mobile/xul/app/mobile.js index 27b106108d5..c2c2fc6f6e4 100644 --- a/mobile/xul/app/mobile.js +++ b/mobile/xul/app/mobile.js @@ -216,13 +216,14 @@ pref("extensions.strictCompatibility", false); pref("extensions.minCompatibleAppVersion", "4.0"); pref("extensions.update.url", "https://versioncheck.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%¤tAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%"); +pref("extensions.update.background.url", "https://versioncheck-bg.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%¤tAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%"); /* preferences for the Get Add-ons pane */ pref("extensions.getAddons.cache.enabled", true); pref("extensions.getAddons.maxResults", 15); pref("extensions.getAddons.recommended.browseURL", "https://addons.mozilla.org/%LOCALE%/mobile/recommended/"); pref("extensions.getAddons.recommended.url", "https://services.addons.mozilla.org/%LOCALE%/mobile/api/%API_VERSION%/list/featured/all/%MAX_RESULTS%/%OS%/%VERSION%"); -pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/mobile/search?q=%TERMS%"); +pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/mobile/search?q=%TERMS%&platform=%OS%&appver=%VERSION%"); pref("extensions.getAddons.search.url", "https://services.addons.mozilla.org/%LOCALE%/mobile/api/%API_VERSION%/search/%TERMS%/all/%MAX_RESULTS%/%OS%/%VERSION%/%COMPATIBILITY_MODE%"); pref("extensions.getAddons.browseAddons", "https://addons.mozilla.org/%LOCALE%/mobile/"); pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/%LOCALE%/mobile/api/%API_VERSION%/search/guid:%IDS%?src=mobile&appOS=%OS%&appVersion=%VERSION%"); @@ -242,6 +243,9 @@ pref("extensions.blocklist.detailsURL", "https://www.mozilla.com/%LOCALE%/blockl pref("dom.disable_open_during_load", true); pref("privacy.popups.showBrowserMessage", true); +/* disable opening windows with the dialog feature */ +pref("dom.disable_window_open_dialog_feature", true); + pref("keyword.enabled", true); pref("keyword.URL", "http://www.google.com/m?ie=UTF-8&oe=UTF-8&sourceid=navclient&gfns=1&q="); diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 32ab27737b2..d279ae13853 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -203,6 +203,9 @@ pref("gfx.downloadable_fonts.enabled", true); pref("gfx.downloadable_fonts.fallback_delay", 3000); pref("gfx.downloadable_fonts.sanitize", true); +// whether to always search all font cmaps during system font fallback +pref("gfx.font_rendering.fallback.always_use_cmaps", false); + #ifdef MOZ_GRAPHITE pref("gfx.font_rendering.graphite.enabled", false); #endif @@ -1514,14 +1517,14 @@ pref("plugins.click_to_play", false); #ifndef DEBUG // How long a plugin is allowed to process a synchronous IPC message // before we consider it "hung". -pref("dom.ipc.plugins.timeoutSecs", 25); +pref("dom.ipc.plugins.timeoutSecs", 45); // How long a plugin process will wait for a response from the parent // to a synchronous request before terminating itself. After this // point the child assumes the parent is hung. Currently disabled. pref("dom.ipc.plugins.parentTimeoutSecs", 0); // How long a plugin launch is allowed to take before // we consider it failed. -pref("dom.ipc.plugins.processLaunchTimeoutSecs", 25); +pref("dom.ipc.plugins.processLaunchTimeoutSecs", 45); #else // No timeout in DEBUG builds pref("dom.ipc.plugins.timeoutSecs", 0); @@ -1627,9 +1630,9 @@ pref("font.name-list.cursive.he", "Guttman Yad, Ktav, Arial"); pref("font.name.serif.ja", "MS PMincho"); pref("font.name.sans-serif.ja", "MS PGothic"); pref("font.name.monospace.ja", "MS Gothic"); -pref("font.name-list.serif.ja", "MS PMincho, MS Mincho, MS PGothic, MS Gothic"); -pref("font.name-list.sans-serif.ja", "MS PGothic, MS Gothic, MS PMincho, MS Mincho"); -pref("font.name-list.monospace.ja", "MS Gothic, MS Mincho, MS PGothic, MS PMincho"); +pref("font.name-list.serif.ja", "MS PMincho, MS Mincho, MS PGothic, MS Gothic,Meiryo"); +pref("font.name-list.sans-serif.ja", "MS PGothic, MS Gothic, MS PMincho, MS Mincho,Meiryo"); +pref("font.name-list.monospace.ja", "MS Gothic, MS Mincho, MS PGothic, MS PMincho,Meiryo"); pref("font.name.serif.ko", "Batang"); pref("font.name.sans-serif.ko", "Gulim"); @@ -1679,27 +1682,27 @@ pref("font.name.cursive.x-western", "Comic Sans MS"); pref("font.name.serif.zh-CN", "SimSun"); pref("font.name.sans-serif.zh-CN", "SimSun"); pref("font.name.monospace.zh-CN", "SimSun"); -pref("font.name-list.serif.zh-CN", "MS Song, SimSun"); -pref("font.name-list.sans-serif.zh-CN", "MS Song, SimSun"); -pref("font.name-list.monospace.zh-CN", "MS Song, SimSun"); +pref("font.name-list.serif.zh-CN", "MS Song, SimSun, SimSun-ExtB"); +pref("font.name-list.sans-serif.zh-CN", "MS Song, SimSun, SimSun-ExtB"); +pref("font.name-list.monospace.zh-CN", "MS Song, SimSun, SimSun-ExtB"); // Per Taiwanese users' demand. They don't want to use TC fonts for // rendering Latin letters. (bug 88579) pref("font.name.serif.zh-TW", "Times New Roman"); pref("font.name.sans-serif.zh-TW", "Arial"); pref("font.name.monospace.zh-TW", "MingLiU"); -pref("font.name-list.serif.zh-TW", "PMingLiu, MingLiU"); -pref("font.name-list.sans-serif.zh-TW", "PMingLiU, MingLiU"); -pref("font.name-list.monospace.zh-TW", "MingLiU"); +pref("font.name-list.serif.zh-TW", "PMingLiu, MingLiU, MingLiU-ExtB"); +pref("font.name-list.sans-serif.zh-TW", "PMingLiU, MingLiU, MingLiU-ExtB"); +pref("font.name-list.monospace.zh-TW", "MingLiU, MingLiU-ExtB"); // hkscsm3u.ttf (HKSCS-2001) : http://www.microsoft.com/hk/hkscs // Hong Kong users have the same demand about glyphs for Latin letters (bug 88579) pref("font.name.serif.zh-HK", "Times New Roman"); pref("font.name.sans-serif.zh-HK", "Arial"); pref("font.name.monospace.zh-HK", "MingLiu_HKSCS"); -pref("font.name-list.serif.zh-HK", "MingLiu_HKSCS, Ming(for ISO10646), MingLiU"); -pref("font.name-list.sans-serif.zh-HK", "MingLiU_HKSCS, Ming(for ISO10646), MingLiU"); -pref("font.name-list.monospace.zh-HK", "MingLiU_HKSCS, Ming(for ISO10646), MingLiU"); +pref("font.name-list.serif.zh-HK", "MingLiu_HKSCS, Ming(for ISO10646), MingLiU, MingLiU_HKSCS-ExtB"); +pref("font.name-list.sans-serif.zh-HK", "MingLiU_HKSCS, Ming(for ISO10646), MingLiU, MingLiU_HKSCS-ExtB"); +pref("font.name-list.monospace.zh-HK", "MingLiU_HKSCS, Ming(for ISO10646), MingLiU, MingLiU_HKSCS-ExtB"); pref("font.name.serif.x-devanagari", "Mangal"); pref("font.name.sans-serif.x-devanagari", "Raghindi"); @@ -2095,16 +2098,16 @@ pref("font.name-list.monospace.ar", "Geeza Pro"); pref("font.name-list.cursive.ar", "DecoType Naskh"); pref("font.name-list.fantasy.ar", "KufiStandardGK"); -pref("font.name.serif.el", "Lucida Grande"); -pref("font.name.sans-serif.el", "Lucida Grande"); -pref("font.name.monospace.el", "Lucida Grande"); +pref("font.name.serif.el", "Times"); +pref("font.name.sans-serif.el", "Helvetica"); +pref("font.name.monospace.el", "Courier New"); pref("font.name.cursive.el", "Lucida Grande"); pref("font.name.fantasy.el", "Lucida Grande"); -pref("font.name-list.serif.el", "Lucida Grande"); -pref("font.name-list.sans-serif.el", "Lucida Grande"); -pref("font.name-list.monospace.el", "Lucida Grande"); -pref("font.name-list.cursive.el", "Lucida Grande"); -pref("font.name-list.fantasy.el", "Lucida Grande"); +pref("font.name-list.serif.el", "Times,Times New Roman"); +pref("font.name-list.sans-serif.el", "Helvetica,Lucida Grande"); +pref("font.name-list.monospace.el", "Courier New,Lucida Grande"); +pref("font.name-list.cursive.el", "Times,Lucida Grande"); +pref("font.name-list.fantasy.el", "Times,Lucida Grande"); pref("font.name.serif.he", "Times New Roman"); pref("font.name.sans-serif.he", "Arial"); @@ -2117,11 +2120,11 @@ pref("font.name-list.monospace.he", "Courier New"); pref("font.name-list.cursive.he", "Times New Roman"); pref("font.name-list.fantasy.he", "Times New Roman"); -pref("font.name.serif.ja", "Hiragino Mincho Pro"); -pref("font.name.sans-serif.ja", "Hiragino Kaku Gothic Pro"); +pref("font.name.serif.ja", "Hiragino Mincho ProN"); +pref("font.name.sans-serif.ja", "Hiragino Kaku Gothic ProN"); pref("font.name.monospace.ja", "Osaka-Mono"); -pref("font.name-list.serif.ja", "Hiragino Mincho Pro"); -pref("font.name-list.sans-serif.ja", "Hiragino Kaku Gothic Pro"); +pref("font.name-list.serif.ja", "Hiragino Mincho ProN,Hiragino Mincho Pro"); +pref("font.name-list.sans-serif.ja", "Hiragino Kaku Gothic ProN,Hiragino Kaku Gothic Pro"); pref("font.name-list.monospace.ja", "Osaka-Mono"); pref("font.name.serif.ko", "AppleMyungjo"); @@ -2200,9 +2203,9 @@ pref("font.name.sans-serif.x-cyrillic", "Helvetica"); pref("font.name.monospace.x-cyrillic", "Monaco"); pref("font.name.cursive.x-cyrillic", "Geneva"); pref("font.name.fantasy.x-cyrillic", "Charcoal CY"); -pref("font.name-list.serif.x-cyrillic", "Times"); -pref("font.name-list.sans-serif.x-cyrillic", "Helvetica"); -pref("font.name-list.monospace.x-cyrillic", "Monaco"); +pref("font.name-list.serif.x-cyrillic", "Times,Times New Roman"); +pref("font.name-list.sans-serif.x-cyrillic", "Helvetica,Arial"); +pref("font.name-list.monospace.x-cyrillic", "Monaco,Courier New"); pref("font.name-list.cursive.x-cyrillic", "Geneva"); pref("font.name-list.fantasy.x-cyrillic", "Charcoal CY"); @@ -2311,32 +2314,32 @@ pref("font.name.sans-serif.x-western", "Helvetica"); pref("font.name.monospace.x-western", "Courier"); pref("font.name.cursive.x-western", "Apple Chancery"); pref("font.name.fantasy.x-western", "Papyrus"); -pref("font.name-list.serif.x-western", "Times"); -pref("font.name-list.sans-serif.x-western", "Helvetica"); -pref("font.name-list.monospace.x-western", "Courier"); +pref("font.name-list.serif.x-western", "Times,Times New Roman"); +pref("font.name-list.sans-serif.x-western", "Helvetica,Arial"); +pref("font.name-list.monospace.x-western", "Courier,Courier New"); pref("font.name-list.cursive.x-western", "Apple Chancery"); pref("font.name-list.fantasy.x-western", "Papyrus"); pref("font.name.serif.zh-CN", "STSong"); pref("font.name.sans-serif.zh-CN", "STHeiti"); pref("font.name.monospace.zh-CN", "STHeiti"); -pref("font.name-list.serif.zh-CN", "STSong"); -pref("font.name-list.sans-serif.zh-CN", "STHeiti"); -pref("font.name-list.monospace.zh-CN", "STHeiti"); +pref("font.name-list.serif.zh-CN", "STSong,Heiti SC"); +pref("font.name-list.sans-serif.zh-CN", "STHeiti,Heiti SC"); +pref("font.name-list.monospace.zh-CN", "STHeiti,Heiti SC"); pref("font.name.serif.zh-TW", "Apple LiSung"); pref("font.name.sans-serif.zh-TW", "Apple LiGothic"); pref("font.name.monospace.zh-TW", "Apple LiGothic"); -pref("font.name-list.serif.zh-TW", "Apple LiSung"); -pref("font.name-list.sans-serif.zh-TW", "Apple LiGothic"); -pref("font.name-list.monospace.zh-TW", "Apple LiGothic"); +pref("font.name-list.serif.zh-TW", "Apple LiSung,Heiti TC"); +pref("font.name-list.sans-serif.zh-TW", "Apple LiGothic,Heiti TC"); +pref("font.name-list.monospace.zh-TW", "Apple LiGothic,Heiti TC"); pref("font.name.serif.zh-HK", "LiSong Pro"); pref("font.name.sans-serif.zh-HK", "LiHei Pro"); pref("font.name.monospace.zh-HK", "LiHei Pro"); -pref("font.name-list.serif.zh-HK", "LiSong Pro"); -pref("font.name-list.sans-serif.zh-HK", "LiHei Pro"); -pref("font.name-list.monospace.zh-HK", "LiHei Pro"); +pref("font.name-list.serif.zh-HK", "LiSong Pro,Heiti TC"); +pref("font.name-list.sans-serif.zh-HK", "LiHei Pro,Heiti TC"); +pref("font.name-list.monospace.zh-HK", "LiHei Pro,Heiti TC"); pref("font.default.ar", "sans-serif"); pref("font.size.variable.ar", 16); @@ -3461,12 +3464,16 @@ pref("dom.network.metered", false); // many mb of virtual address space available. pref("memory.low_virtual_memory_threshold_mb", 128); +// On Windows 32- or 64-bit, fire a low-memory notification if we have less +// than this many mb of commit space (physical memory plus page file) left. +pref("memory.low_commit_space_threshold_mb", 128); + // On Windows 32- or 64-bit, fire a low-memory notification if we have less // than this many mb of physical memory available on the whole machine. pref("memory.low_physical_memory_threshold_mb", 0); // On Windows 32- or 64-bit, don't fire a low-memory notification because of -// low available physical memory more than once every -// low_physical_memory_notification_interval_ms. -pref("memory.low_physical_memory_notification_interval_ms", 10000); +// low available physical memory or low commit space more than once every +// low_memory_notification_interval_ms. +pref("memory.low_memory_notification_interval_ms", 10000); #endif diff --git a/mozglue/linker/CustomElf.cpp b/mozglue/linker/CustomElf.cpp index 949e441b669..10b8f228b35 100644 --- a/mozglue/linker/CustomElf.cpp +++ b/mozglue/linker/CustomElf.cpp @@ -679,9 +679,12 @@ CustomElf::RelocateJumps() symptr = GetSymbolPtrInDeps(strtab.GetStringAt(sym.st_name)); if (symptr == NULL) { - log("%s: Error: relocation to NULL @0x%08" PRIxAddr " for symbol \"%s\"", - GetPath(), rel->r_offset, strtab.GetStringAt(sym.st_name)); - return false; + log("%s: %s: relocation to NULL @0x%08" PRIxAddr " for symbol \"%s\"", + GetPath(), + (ELF_ST_BIND(sym.st_info) == STB_WEAK) ? "Warning" : "Error", + rel->r_offset, strtab.GetStringAt(sym.st_name)); + if (ELF_ST_BIND(sym.st_info) != STB_WEAK) + return false; } /* Apply relocation */ *(void **) ptr = symptr; diff --git a/mozglue/linker/CustomElf.h b/mozglue/linker/CustomElf.h index 86906328d83..0efe96ac5f8 100644 --- a/mozglue/linker/CustomElf.h +++ b/mozglue/linker/CustomElf.h @@ -26,12 +26,18 @@ #define ELFCLASS ELFCLASS64 #define ELF_R_TYPE ELF64_R_TYPE #define ELF_R_SYM ELF64_R_SYM +#ifndef ELF_ST_BIND +#define ELF_ST_BIND ELF64_ST_BIND +#endif #define PRIxAddr "lx" #else #define Elf_(type) Elf32_ ## type #define ELFCLASS ELFCLASS32 #define ELF_R_TYPE ELF32_R_TYPE #define ELF_R_SYM ELF32_R_SYM +#ifndef ELF_ST_BIND +#define ELF_ST_BIND ELF32_ST_BIND +#endif #define PRIxAddr "x" #endif diff --git a/netwerk/base/src/nsIOService.cpp b/netwerk/base/src/nsIOService.cpp index d6bc5b62135..bf7b8551b75 100644 --- a/netwerk/base/src/nsIOService.cpp +++ b/netwerk/base/src/nsIOService.cpp @@ -176,7 +176,7 @@ PRUint32 nsIOService::gDefaultSegmentCount = 24; nsIOService::nsIOService() : mOffline(true) , mOfflineForProfileChange(false) - , mManageOfflineStatus(true) + , mManageOfflineStatus(false) , mSettingOffline(false) , mSetOfflineValue(false) , mShutdown(false) @@ -1119,10 +1119,19 @@ NS_IMETHODIMP nsIOService::SetManageOfflineStatus(bool aManage) { nsresult rv = NS_OK; - InitializeNetworkLinkService(); + // SetManageOfflineStatus must throw when we fail to go from non-managed + // to managed. Usually because there is no link monitoring service + // available. Failure to do this switch is detected by a failure of + // TrackNetworkLinkStatusForOffline(). When there is no network link + // available during call to InitializeNetworkLinkService(), application is + // put to offline mode. And when we change mMangeOfflineStatus to false + // on the next line we get stuck on being offline even though the link + // becomes later available. bool wasManaged = mManageOfflineStatus; mManageOfflineStatus = aManage; + InitializeNetworkLinkService(); + if (mManageOfflineStatus && !wasManaged) { rv = TrackNetworkLinkStatusForOffline(); if (NS_FAILED(rv)) diff --git a/parser/html/nsHtml5Atoms.cpp b/parser/html/nsHtml5Atoms.cpp index 2494ebebf64..5ffbc00dfb3 100644 --- a/parser/html/nsHtml5Atoms.cpp +++ b/parser/html/nsHtml5Atoms.cpp @@ -66,5 +66,5 @@ static const nsStaticAtom Html5Atoms_info[] = { void nsHtml5Atoms::AddRefAtoms() { - NS_RegisterStaticAtoms(Html5Atoms_info, ArrayLength(Html5Atoms_info)); + NS_RegisterStaticAtoms(Html5Atoms_info); } diff --git a/parser/html/nsHtml5TreeOperation.cpp b/parser/html/nsHtml5TreeOperation.cpp index 1b8040577ea..d03180b9f2f 100644 --- a/parser/html/nsHtml5TreeOperation.cpp +++ b/parser/html/nsHtml5TreeOperation.cpp @@ -795,12 +795,12 @@ nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder, const PRUnichar* params[] = { atom->GetUTF16String(), otherAtom->GetUTF16String() }; rv = nsContentUtils::FormatLocalizedString( - nsContentUtils::eHTMLPARSER_PROPERTIES, msgId, params, 2, message); + nsContentUtils::eHTMLPARSER_PROPERTIES, msgId, params, message); NS_ENSURE_SUCCESS(rv, rv); } else if (atom) { const PRUnichar* params[] = { atom->GetUTF16String() }; rv = nsContentUtils::FormatLocalizedString( - nsContentUtils::eHTMLPARSER_PROPERTIES, msgId, params, 1, message); + nsContentUtils::eHTMLPARSER_PROPERTIES, msgId, params, message); NS_ENSURE_SUCCESS(rv, rv); } else { rv = nsContentUtils::GetLocalizedString( diff --git a/parser/htmlparser/src/nsHTMLTags.cpp b/parser/htmlparser/src/nsHTMLTags.cpp index 85755f0e261..883fe939539 100644 --- a/parser/htmlparser/src/nsHTMLTags.cpp +++ b/parser/htmlparser/src/nsHTMLTags.cpp @@ -374,7 +374,7 @@ nsHTMLTags::AddRefTable(void) if (gTableRefCount++ == 0) { // Fill in our static atom pointers - NS_RegisterStaticAtoms(sTagAtoms_info, ArrayLength(sTagAtoms_info)); + NS_RegisterStaticAtoms(sTagAtoms_info); NS_ASSERTION(!gTagTable && !gTagAtomTable, "pre existing hash!"); diff --git a/parser/htmlparser/tests/mochitest/file_bug594730-4.html b/parser/htmlparser/tests/mochitest/file_bug594730-4.html index 2cbf7745cb9..bdce353a59f 100644 --- a/parser/htmlparser/tests/mochitest/file_bug594730-4.html +++ b/parser/htmlparser/tests/mochitest/file_bug594730-4.html @@ -1,3 +1,3 @@ - + diff --git a/rdf/base/src/nsRDFContentSink.cpp b/rdf/base/src/nsRDFContentSink.cpp index 561c3e6be6b..c08289cd226 100644 --- a/rdf/base/src/nsRDFContentSink.cpp +++ b/rdf/base/src/nsRDFContentSink.cpp @@ -337,7 +337,7 @@ RDFContentSinkImpl::RDFContentSinkImpl() rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils); - NS_RegisterStaticAtoms(rdf_atoms, ArrayLength(rdf_atoms)); + NS_RegisterStaticAtoms(rdf_atoms); } mNodeIDMap.Init(); diff --git a/security/manager/Makefile.in b/security/manager/Makefile.in index 1da94ccd215..535be9db069 100644 --- a/security/manager/Makefile.in +++ b/security/manager/Makefile.in @@ -278,6 +278,13 @@ ifeq ($(OS_TARGET),Linux) DEFAULT_GMAKE_FLAGS += FREEBL_LOWHASH=1 endif +ifdef MOZ_NO_WLZDEFS +DEFAULT_GMAKE_FLAGS += ZDEFS_FLAG= +endif +ifdef MOZ_CFLAGS_NSS +DEFAULT_GMAKE_FLAGS += XCFLAGS="$(CFLAGS)" +endif + SUBMAKEFILES = boot/Makefile ssl/Makefile pki/Makefile locales/Makefile include $(topsrcdir)/config/rules.mk diff --git a/security/manager/ssl/src/nsCrypto.cpp b/security/manager/ssl/src/nsCrypto.cpp index 1f532dc77f9..3a5a7898864 100644 --- a/security/manager/ssl/src/nsCrypto.cpp +++ b/security/manager/ssl/src/nsCrypto.cpp @@ -2164,7 +2164,6 @@ NS_IMETHODIMP nsCryptoRunnable::Run() { nsNSSShutDownPreventionLock locker; - JSPrincipals *principals; JSContext *cx = m_args->m_cx; JSAutoRequest ar(cx); @@ -2174,29 +2173,20 @@ nsCryptoRunnable::Run() return NS_ERROR_FAILURE; } - nsresult rv = m_args->m_principals->GetJSPrincipals(cx, &principals); - if (NS_FAILED(rv)) - return NS_ERROR_FAILURE; - // make sure the right context is on the stack. must not return w/out popping nsCOMPtr stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1")); if (!stack || NS_FAILED(stack->Push(cx))) { - JSPRINCIPALS_DROP(cx, principals); return NS_ERROR_FAILURE; } - jsval retval; - if (JS_EvaluateScriptForPrincipals(cx, m_args->m_scope, principals, - m_args->m_jsCallback, - strlen(m_args->m_jsCallback), - nsnull, 0, - &retval) != JS_TRUE) { - rv = NS_ERROR_FAILURE; - } - + JSBool ok = + JS_EvaluateScriptForPrincipals(cx, m_args->m_scope, + nsJSPrincipals::get(m_args->m_principals), + m_args->m_jsCallback, + strlen(m_args->m_jsCallback), + nsnull, 0, nsnull); stack->Pop(nsnull); - JSPRINCIPALS_DROP(cx, principals); - return rv; + return ok ? NS_OK : NS_ERROR_FAILURE; } //Quick helper function to check if a newly issued cert diff --git a/security/manager/ssl/src/nsNSSCertHelper.cpp b/security/manager/ssl/src/nsNSSCertHelper.cpp index 2101f6fd1e4..811aedba4a7 100644 --- a/security/manager/ssl/src/nsNSSCertHelper.cpp +++ b/security/manager/ssl/src/nsNSSCertHelper.cpp @@ -2125,7 +2125,7 @@ nsNSSCertificate::CreateTBSCertificateASN1Struct(nsIASN1Sequence **retSequence, // length to be in bytes, so let's convert the // length in a temporary SECItem data.data = mCert->issuerID.data; - data.len = mCert->issuerID.len / 8; + data.len = (mCert->issuerID.len + 7) / 8; ProcessRawBytes(nssComponent, &data, text); printableItem = new nsNSSASN1PrintableItem(); @@ -2143,8 +2143,8 @@ nsNSSCertificate::CreateTBSCertificateASN1Struct(nsIASN1Sequence **retSequence, // The function ProcessRawBytes expects the // length to be in bytes, so let's convert the // length in a temporary SECItem - data.data = mCert->issuerID.data; - data.len = mCert->issuerID.len / 8; + data.data = mCert->subjectID.data; + data.len = (mCert->subjectID.len + 7) / 8; ProcessRawBytes(nssComponent, &data, text); printableItem = new nsNSSASN1PrintableItem(); diff --git a/storage/src/mozStorageService.cpp b/storage/src/mozStorageService.cpp index 4988ede95de..0999d7faa03 100644 --- a/storage/src/mozStorageService.cpp +++ b/storage/src/mozStorageService.cpp @@ -194,9 +194,10 @@ public: // main thread! But at the time of writing this function is only called when // about:memory is loaded (not, for example, when telemetry pings occur) and // any delays in that case aren't so bad. - NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *aCallback, + NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *aCb, nsISupports *aClosure) { + nsresult rv; size_t totalConnSize = 0; { nsTArray > connections; @@ -218,29 +219,32 @@ public: SQLiteMutexAutoLock lockedScope(conn->sharedDBMutex); - totalConnSize += - doConnMeasurement(aCallback, aClosure, *conn.get(), pathHead, - NS_LITERAL_CSTRING("stmt"), mStmtDesc, - SQLITE_DBSTATUS_STMT_USED); - totalConnSize += - doConnMeasurement(aCallback, aClosure, *conn.get(), pathHead, - NS_LITERAL_CSTRING("cache"), mCacheDesc, - SQLITE_DBSTATUS_CACHE_USED); - totalConnSize += - doConnMeasurement(aCallback, aClosure, *conn.get(), pathHead, - NS_LITERAL_CSTRING("schema"), mSchemaDesc, - SQLITE_DBSTATUS_SCHEMA_USED); + rv = reportConn(aCb, aClosure, *conn.get(), pathHead, + NS_LITERAL_CSTRING("stmt"), mStmtDesc, + SQLITE_DBSTATUS_STMT_USED, &totalConnSize); + NS_ENSURE_SUCCESS(rv, rv); + + rv = reportConn(aCb, aClosure, *conn.get(), pathHead, + NS_LITERAL_CSTRING("cache"), mCacheDesc, + SQLITE_DBSTATUS_CACHE_USED, &totalConnSize); + NS_ENSURE_SUCCESS(rv, rv); + + rv = reportConn(aCb, aClosure, *conn.get(), pathHead, + NS_LITERAL_CSTRING("schema"), mSchemaDesc, + SQLITE_DBSTATUS_SCHEMA_USED, &totalConnSize); + NS_ENSURE_SUCCESS(rv, rv); } } PRInt64 other = ::sqlite3_memory_used() - totalConnSize; - aCallback->Callback(NS_LITERAL_CSTRING(""), - NS_LITERAL_CSTRING("explicit/storage/sqlite/other"), - nsIMemoryReporter::KIND_HEAP, - nsIMemoryReporter::UNITS_BYTES, other, - NS_LITERAL_CSTRING("All unclassified sqlite memory."), - aClosure); + rv = aCb->Callback(NS_LITERAL_CSTRING(""), + NS_LITERAL_CSTRING("explicit/storage/sqlite/other"), + nsIMemoryReporter::KIND_HEAP, + nsIMemoryReporter::UNITS_BYTES, other, + NS_LITERAL_CSTRING("All unclassified sqlite memory."), + aClosure); + NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } @@ -272,14 +276,17 @@ private: * The memory report description. * @param aOption * The SQLite constant for getting the measurement. + * @param aTotal + * The accumulator for the measurement. */ - size_t doConnMeasurement(nsIMemoryMultiReporterCallback *aCallback, - nsISupports *aClosure, - sqlite3 *aConn, - const nsACString &aPathHead, - const nsACString &aKind, - const nsACString &aDesc, - int aOption) + nsresult reportConn(nsIMemoryMultiReporterCallback *aCb, + nsISupports *aClosure, + sqlite3 *aConn, + const nsACString &aPathHead, + const nsACString &aKind, + const nsACString &aDesc, + int aOption, + size_t *aTotal) { nsCString path(aPathHead); path.Append(aKind); @@ -290,11 +297,14 @@ private: nsresult rv = convertResultCode(rc); NS_ENSURE_SUCCESS(rv, rv); - aCallback->Callback(NS_LITERAL_CSTRING(""), path, - nsIMemoryReporter::KIND_HEAP, - nsIMemoryReporter::UNITS_BYTES, PRInt64(curr), - aDesc, aClosure); - return curr; + rv = aCb->Callback(NS_LITERAL_CSTRING(""), path, + nsIMemoryReporter::KIND_HEAP, + nsIMemoryReporter::UNITS_BYTES, PRInt64(curr), + aDesc, aClosure); + NS_ENSURE_SUCCESS(rv, rv); + *aTotal += curr; + + return NS_OK; } }; diff --git a/testing/talos/talos.json b/testing/talos/talos.json index 4f1c0326bc3..7ee379adc17 100644 --- a/testing/talos/talos.json +++ b/testing/talos/talos.json @@ -2,9 +2,5 @@ "talos.zip": { "url": "http://build.mozilla.org/talos/zips/talos.bug732835.zip", "path": "" - }, - "pageloader.xpi": { - "url": "http://build.mozilla.org/talos/xpis/pageloader.xpi", - "path": "talos/page_load_test" } } diff --git a/testing/talos/talos_from_code.py b/testing/talos/talos_from_code.py index 574dd2767d6..25d9334f0b0 100644 --- a/testing/talos/talos_from_code.py +++ b/testing/talos/talos_from_code.py @@ -44,18 +44,18 @@ def main(): # 3) download the necessary files print "INFO: talos.json URL: %s" % options.talos_json_url try: - for key in ('talos.zip', 'pageloader.xpi',): - entity = get_value(jsonFilename, key) - if passesRestrictions(options.talos_json_url, entity["url"]): - # the key is at the same time the filename e.g. talos.zip - download_file(entity["url"], entity["path"], key) - print "INFO: %s -> %s" % (entity["url"], os.path.join(entity["path"], key)) - else: - print "ERROR: You have tried to download a file " + \ - "from: %s " % fileUrl + \ - "which is a location different than http://build.mozilla.org/talos/" - print "ERROR: This is only allowed for the certain branches." - sys.exit(1) + key = 'talos.zip' + entity = get_value(jsonFilename, key) + if passesRestrictions(options.talos_json_url, entity["url"]): + # the key is at the same time the filename e.g. talos.zip + download_file(entity["url"], entity["path"], key) + print "INFO: %s -> %s" % (entity["url"], os.path.join(entity["path"], key)) + else: + print "ERROR: You have tried to download a file " + \ + "from: %s " % fileUrl + \ + "which is a location different than http://build.mozilla.org/talos/" + print "ERROR: This is only allowed for the certain branches." + sys.exit(1) except Exception, e: print "ERROR: %s" % str(e) sys.exit(1) diff --git a/testing/xpcshell/head.js b/testing/xpcshell/head.js index c3d4707fab0..b33a066b614 100644 --- a/testing/xpcshell/head.js +++ b/testing/xpcshell/head.js @@ -843,7 +843,9 @@ function run_test_in_child(testFile, optionalCallback) var testPath = do_get_file(testFile).path.replace(/\\/g, "/"); do_test_pending(); - sendCommand("const _TEST_FILE=['" + testPath + "']; _execute_test();", + sendCommand("_dump('CHILD-TEST-STARTED'); " + + "const _TEST_FILE=['" + testPath + "']; _execute_test(); " + + "_dump('CHILD-TEST-COMPLETED');", callback); } diff --git a/testing/xpcshell/runxpcshelltests.py b/testing/xpcshell/runxpcshelltests.py index ba6699e55f6..114807eb656 100644 --- a/testing/xpcshell/runxpcshelltests.py +++ b/testing/xpcshell/runxpcshelltests.py @@ -679,10 +679,17 @@ class XPCShellTests(object): self.log.info("<<<<<<<") result = not ((self.getReturnCode(proc) != 0) or + # if do_throw or do_check failed (stdout and re.search("^((parent|child): )?TEST-UNEXPECTED-", stdout, re.MULTILINE)) or + # if syntax error in xpcshell file (stdout and re.search(": SyntaxError:", stdout, - re.MULTILINE))) + re.MULTILINE)) or + # if e10s test started but never finished (child process crash) + (stdout and re.search("^child: CHILD-TEST-STARTED", + stdout, re.MULTILINE) + and not re.search("^child: CHILD-TEST-COMPLETED", + stdout, re.MULTILINE))) if result != expected: failureType = "TEST-UNEXPECTED-%s" % ("FAIL" if expected else "PASS") diff --git a/testing/xpcshell/xpcshell.ini b/testing/xpcshell/xpcshell.ini index 0221cd8e02d..2af59639ded 100644 --- a/testing/xpcshell/xpcshell.ini +++ b/testing/xpcshell/xpcshell.ini @@ -72,6 +72,7 @@ skip-if = os == "android" skip-if = os == "android" [include:browser/components/dirprovider/tests/unit/xpcshell.ini] [include:browser/components/feeds/test/unit/xpcshell.ini] +[include:browser/components/migration/tests/unit/xpcshell.ini] [include:browser/components/places/tests/unit/xpcshell.ini] [include:browser/components/privatebrowsing/test/unit/xpcshell.ini] [include:browser/components/shell/test/unit/xpcshell.ini] diff --git a/toolkit/components/aboutmemory/content/aboutMemory.js b/toolkit/components/aboutmemory/content/aboutMemory.js index f2dc882a98e..54c55def0dc 100644 --- a/toolkit/components/aboutmemory/content/aboutMemory.js +++ b/toolkit/components/aboutmemory/content/aboutMemory.js @@ -124,7 +124,7 @@ function onLoad() } else if (document.title === "about:compartments") { onLoadAboutCompartments(); } else { - assert(false, "Unknown location"); + assert(false, "Unknown location: " + document.title); } } diff --git a/toolkit/components/alerts/nsAlertsService.cpp b/toolkit/components/alerts/nsAlertsService.cpp index 2a0905e4981..e197dcd3f07 100644 --- a/toolkit/components/alerts/nsAlertsService.cpp +++ b/toolkit/components/alerts/nsAlertsService.cpp @@ -74,6 +74,33 @@ nsAlertsService::nsAlertsService() nsAlertsService::~nsAlertsService() {} +bool nsAlertsService::ShouldShowAlert() +{ + bool result = true; + +#ifdef XP_WIN + HMODULE shellDLL = ::LoadLibraryW(L"shell32.dll"); + if (!shellDLL) + return result; + + SHQueryUserNotificationStatePtr pSHQueryUserNotificationState = + (SHQueryUserNotificationStatePtr) ::GetProcAddress(shellDLL, "SHQueryUserNotificationState"); + + if (pSHQueryUserNotificationState) { + MOZ_QUERY_USER_NOTIFICATION_STATE qstate; + if (SUCCEEDED(pSHQueryUserNotificationState(&qstate))) { + if (qstate != QUNS_ACCEPTS_NOTIFICATIONS) { + result = false; + } + } + } + + ::FreeLibrary(shellDLL); +#endif + + return result; +} + NS_IMETHODIMP nsAlertsService::ShowAlertNotification(const nsAString & aImageUrl, const nsAString & aAlertTitle, const nsAString & aAlertText, bool aAlertTextClickable, const nsAString & aAlertCookie, @@ -110,6 +137,13 @@ NS_IMETHODIMP nsAlertsService::ShowAlertNotification(const nsAString & aImageUrl return rv; } + if (!ShouldShowAlert()) { + // Do not display the alert. Instead call alertfinished and get out. + if (aAlertListener) + aAlertListener->Observe(NULL, "alertfinished", PromiseFlatString(aAlertCookie).get()); + return NS_OK; + } + nsCOMPtr wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID)); nsCOMPtr newWindow; diff --git a/toolkit/components/alerts/nsAlertsService.h b/toolkit/components/alerts/nsAlertsService.h index 7153ac75cea..c3895633db1 100644 --- a/toolkit/components/alerts/nsAlertsService.h +++ b/toolkit/components/alerts/nsAlertsService.h @@ -42,6 +42,23 @@ #include "nsIAlertsService.h" #include "nsCOMPtr.h" +#ifdef XP_WIN +typedef enum tagMOZ_QUERY_USER_NOTIFICATION_STATE { + QUNS_NOT_PRESENT = 1, + QUNS_BUSY = 2, + QUNS_RUNNING_D3D_FULL_SCREEN = 3, + QUNS_PRESENTATION_MODE = 4, + QUNS_ACCEPTS_NOTIFICATIONS = 5, + QUNS_QUIET_TIME = 6, + QUNS_IMMERSIVE = 7 +} MOZ_QUERY_USER_NOTIFICATION_STATE; + +extern "C" { +// This function is Windows Vista or later +typedef HRESULT (__stdcall *SHQueryUserNotificationStatePtr)(MOZ_QUERY_USER_NOTIFICATION_STATE *pquns); +} +#endif // defined(XP_WIN) + class nsAlertsService : public nsIAlertsService, public nsIAlertsProgressListener { @@ -54,6 +71,7 @@ public: virtual ~nsAlertsService(); protected: + bool ShouldShowAlert(); }; #endif /* nsAlertsService_h__ */ diff --git a/toolkit/components/alerts/nsIAlertsService.idl b/toolkit/components/alerts/nsIAlertsService.idl index 7318b0f4026..8726438a9d1 100644 --- a/toolkit/components/alerts/nsIAlertsService.idl +++ b/toolkit/components/alerts/nsIAlertsService.idl @@ -70,6 +70,11 @@ interface nsIAlertsService : nsISupports * topic - "alertfinished" when the alert goes away * "alertclickcallback" when the text is clicked * data - the value of the cookie parameter passed to showAlertNotification. + * + * @note Depending on current circumstances (if the user's in a fullscreen + * application, for instance), the alert might not be displayed at all. + * In that case, if an alert listener is passed in it will receive the + * "alertfinished" notification immediately. */ void showAlertNotification(in AString imageUrl, in AString title, diff --git a/toolkit/components/passwordmgr/test/test_bug_627616.html b/toolkit/components/passwordmgr/test/test_bug_627616.html index e4f5f0892f5..eaa2d8982d9 100644 --- a/toolkit/components/passwordmgr/test/test_bug_627616.html +++ b/toolkit/components/passwordmgr/test/test_bug_627616.html @@ -49,8 +49,8 @@ "realm=mochirealm&" + extra || ""); xhr.onloadend = function() { - is(xhr.status, expectedStatus); - is(xhr.statusText, expectedText); + is(xhr.status, expectedStatus, "xhr.status"); + is(xhr.statusText, expectedText, "xhr.statusText"); runNextTest(); } return xhr; diff --git a/toolkit/components/places/mozIAsyncLivemarks.idl b/toolkit/components/places/mozIAsyncLivemarks.idl index 15c6f43fe53..b49a63973f2 100644 --- a/toolkit/components/places/mozIAsyncLivemarks.idl +++ b/toolkit/components/places/mozIAsyncLivemarks.idl @@ -16,7 +16,7 @@ interface mozILivemark; interface nsINavHistoryResultObserver; -[scriptable, uuid(addaa7c5-bd85-4c83-9c21-81c8a825c358)] +[scriptable, uuid(1dbf174c-696e-4d9b-af0f-350da50d2249)] interface mozIAsyncLivemarks : nsISupports { /** @@ -67,9 +67,15 @@ interface mozIAsyncLivemarks : nsISupports in mozILivemarkCallback aCallback); /** - * Forces a reload of all livemarks, whether or not they've expired. + * Reloads all livemarks if they are expired or if forced to do so. + * + * @param [optional]aForceUpdate + * If set to true forces a reload even if contents are still valid. + * + * @note The update process is asynchronous, observers registered through + * registerForUpdates will be notified of updated contents. */ - void reloadLivemarks(); + void reloadLivemarks([optional]in boolean aForceUpdate); }; [scriptable, function, uuid(62a426f9-39a6-42f0-ad48-b7404d48188f)] diff --git a/toolkit/components/places/nsLivemarkService.js b/toolkit/components/places/nsLivemarkService.js index f6f258f0378..da44f4f5a51 100644 --- a/toolkit/components/places/nsLivemarkService.js +++ b/toolkit/components/places/nsLivemarkService.js @@ -151,6 +151,7 @@ LivemarkService.prototype = { } }, + _reloading: false, _startReloadTimer: function LS__startReloadTimer() { if (this._reloadTimer) { @@ -160,6 +161,7 @@ LivemarkService.prototype = { this._reloadTimer = Cc["@mozilla.org/timer;1"] .createInstance(Ci.nsITimer); } + this._reloading = true; this._reloadTimer.initWithCallback(this._reloadNextLivemark.bind(this), RELOAD_DELAY_MS, Ci.nsITimer.TYPE_ONE_SHOT); @@ -179,6 +181,7 @@ LivemarkService.prototype = { } if (this._reloadTimer) { + this._reloading = false; this._reloadTimer.cancel(); delete this._reloadTimer; } @@ -363,7 +366,7 @@ LivemarkService.prototype = { { this._reportDeprecatedMethod(); - this._reloadLivemarks(); + this._reloadLivemarks(true); }, ////////////////////////////////////////////////////////////////////////////// @@ -393,7 +396,15 @@ LivemarkService.prototype = { throw new Components.Exception("", Cr.NS_ERROR_INVALID_ARG); } - livemark = new Livemark(aLivemarkInfo); + // Don't pass unexpected input data to the livemark constructor. + livemark = new Livemark({ title: aLivemarkInfo.title + , parentId: aLivemarkInfo.parentId + , index: aLivemarkInfo.index + , feedURI: aLivemarkInfo.feedURI + , siteURI: aLivemarkInfo.siteURI + , guid: aLivemarkInfo.guid + , lastModified: aLivemarkInfo.lastModified + }); if (this._itemAdded && this._itemAdded.id == livemark.id) { livemark.index = this._itemAdded.index; if (!aLivemarkInfo.guid) { @@ -469,20 +480,31 @@ LivemarkService.prototype = { _reloaded: [], _reloadNextLivemark: function LS__reloadNextLivemark() { + this._reloading = false; // Find first livemark to be reloaded. for (let id in this._livemarks) { if (this._reloaded.indexOf(id) == -1) { this._reloaded.push(id); - this._livemarks[id].reload(); + this._livemarks[id].reload(this._forceUpdate); this._startReloadTimer(); break; } } }, - reloadLivemarks: function LS_reloadLivemarks() + reloadLivemarks: function LS_reloadLivemarks(aForceUpdate) { + // Check if there's a currently running reload, to save some useless work. + let notWorthRestarting = + this._forceUpdate || // We're already forceUpdating. + !aForceUpdate; // The caller didn't request a forced update. + if (this._reloading && notWorthRestarting) { + // Ignore this call. + return; + } + this._onCacheReady((function LS_reloadAllLivemarks_ETAT() { + this._forceUpdate = !!aForceUpdate; this._reloaded = []; // Livemarks reloads happen on a timer, and are delayed for performance // reasons. @@ -841,6 +863,10 @@ Livemark.prototype = { this.status = Ci.mozILivemark.STATUS_LOADING; + // Setting the status notifies observers that may remove the livemark. + if (this._terminated) + return; + try { // Create a load group for the request. This will allow us to // automatically keep track of redirects, so we can always @@ -851,7 +877,7 @@ Livemark.prototype = { QueryInterface(Ci.nsIHttpChannel); channel.loadGroup = loadgroup; channel.loadFlags |= Ci.nsIRequest.LOAD_BACKGROUND | - Ci.nsIRequest.VALIDATE_ALWAYS; + Ci.nsIRequest.LOAD_BYPASS_CACHE; channel.requestMethod = "GET"; channel.setRequestHeader("X-Moz", "livebookmarks", false); @@ -1003,6 +1029,8 @@ Livemark.prototype = { */ terminate: function LM_terminate() { + // Avoid handling any updateChildren request from now on. + this._terminated = true; // Clear the list before aborting, since abort() would try to set the // status and notify about it, but that's not really useful at this point. this._resultObserversList = []; diff --git a/toolkit/components/places/tests/chrome/Makefile.in b/toolkit/components/places/tests/chrome/Makefile.in index 12e9f579d26..d29f83e5f08 100644 --- a/toolkit/components/places/tests/chrome/Makefile.in +++ b/toolkit/components/places/tests/chrome/Makefile.in @@ -61,6 +61,7 @@ _CHROME_FILES = \ test_favicon_annotations.xul \ test_303567.xul \ test_381357.xul \ + test_reloadLivemarks.xul \ $(NULL) libs:: $(_HTTP_FILES) diff --git a/toolkit/components/places/tests/chrome/test_reloadLivemarks.xul b/toolkit/components/places/tests/chrome/test_reloadLivemarks.xul new file mode 100644 index 00000000000..8b0a66261bd --- /dev/null +++ b/toolkit/components/places/tests/chrome/test_reloadLivemarks.xul @@ -0,0 +1,148 @@ + + + + + + + + + + diff --git a/toolkit/components/places/tests/unit/test_mozIAsyncLivemarks.js b/toolkit/components/places/tests/unit/test_mozIAsyncLivemarks.js index 3a57d19317f..3fcc1a3167f 100644 --- a/toolkit/components/places/tests/unit/test_mozIAsyncLivemarks.js +++ b/toolkit/components/places/tests/unit/test_mozIAsyncLivemarks.js @@ -243,6 +243,24 @@ add_test(function test_addLivemark_callback_succeeds() }); }); +add_test(function test_addLivemark_bogusid_callback_succeeds() +{ + PlacesUtils.livemarks.addLivemark({ id: 100 // Should be ignored. + , title: "test" + , parentId: PlacesUtils.unfiledBookmarksFolderId + , index: PlacesUtils.bookmarks.DEFAULT_INDEX + , feedURI: FEED_URI + , siteURI: SITE_URI + }, function (aStatus, aLivemark) + { + do_check_true(Components.isSuccessCode(aStatus)); + do_check_true(aLivemark.id > 0); + do_check_neq(aLivemark.id, 100); + + run_next_test(); + }); +}); + add_test(function test_addLivemark_bogusParent_callback_fails() { PlacesUtils.livemarks.addLivemark({ title: "test" diff --git a/toolkit/components/places/tests/unit/test_null_interfaces.js b/toolkit/components/places/tests/unit/test_null_interfaces.js index 23fab10fbf0..f40efb9d1f9 100644 --- a/toolkit/components/places/tests/unit/test_null_interfaces.js +++ b/toolkit/components/places/tests/unit/test_null_interfaces.js @@ -56,7 +56,7 @@ let testServices = [ ["browser/nav-bookmarks-service;1","nsINavBookmarksService", ["createFolder", "getItemIdForGUID"]], ["browser/livemark-service;2","nsILivemarkService", []], - ["browser/livemark-service;2","mozIAsyncLivemarks", []], + ["browser/livemark-service;2","mozIAsyncLivemarks", ["reloadLivemarks"]], ["browser/annotation-service;1","nsIAnnotationService", []], ["browser/favicon-service;1","nsIFaviconService", []], ["browser/tagging-service;1","nsITaggingService", []], diff --git a/toolkit/components/telemetry/Telemetry.cpp b/toolkit/components/telemetry/Telemetry.cpp index 46f97c14387..2b0ab6331fe 100644 --- a/toolkit/components/telemetry/Telemetry.cpp +++ b/toolkit/components/telemetry/Telemetry.cpp @@ -162,13 +162,11 @@ private: typedef AutoHashtable AddonHistogramMapType; typedef nsBaseHashtableET AddonEntryType; typedef AutoHashtable AddonMapType; - struct AddonEnumeratorArgs { - JSContext *cx; - JSObject *obj; - }; static bool AddonHistogramReflector(AddonHistogramEntryType *entry, JSContext *cx, JSObject *obj); static bool AddonReflector(AddonEntryType *entry, JSContext *cx, JSObject *obj); + static bool CreateHistogramForAddon(const nsACString &name, + AddonHistogramInfo &info); AddonMapType mAddonMap; // This is used for speedy string->Telemetry::ID conversions @@ -205,6 +203,7 @@ struct TelemetryHistogram { // errors. #define HISTOGRAM(id, min, max, bucket_count, histogram_type, b) \ PR_STATIC_ASSERT(nsITelemetry::HISTOGRAM_ ## histogram_type == nsITelemetry::HISTOGRAM_BOOLEAN || \ + nsITelemetry::HISTOGRAM_ ## histogram_type == nsITelemetry::HISTOGRAM_FLAG || \ (min < max && bucket_count > 2 && min >= 1)); #include "TelemetryHistograms.h" @@ -235,6 +234,8 @@ TelemetryHistogramType(Histogram *h, PRUint32 *result) case Histogram::BOOLEAN_HISTOGRAM: *result = nsITelemetry::HISTOGRAM_BOOLEAN; break; + case Histogram::FLAG_HISTOGRAM: + *result = nsITelemetry::HISTOGRAM_FLAG; default: return false; } @@ -245,7 +246,8 @@ nsresult HistogramGet(const char *name, PRUint32 min, PRUint32 max, PRUint32 bucketCount, PRUint32 histogramType, Histogram **result) { - if (histogramType != nsITelemetry::HISTOGRAM_BOOLEAN) { + if (histogramType != nsITelemetry::HISTOGRAM_BOOLEAN + && histogramType != nsITelemetry::HISTOGRAM_FLAG) { // Sanity checks for histogram parameters. if (min >= max) return NS_ERROR_ILLEGAL_VALUE; @@ -267,6 +269,9 @@ HistogramGet(const char *name, PRUint32 min, PRUint32 max, PRUint32 bucketCount, case nsITelemetry::HISTOGRAM_BOOLEAN: *result = BooleanHistogram::FactoryGet(name, Histogram::kUmaTargetedHistogramFlag); break; + case nsITelemetry::HISTOGRAM_FLAG: + *result = FlagHistogram::FactoryGet(name, Histogram::kUmaTargetedHistogramFlag); + break; default: return NS_ERROR_INVALID_ARG; } @@ -735,25 +740,14 @@ TelemetryImpl::GetAddonHistogram(const nsACString &id, const nsACString &name, } AddonHistogramInfo &info = histogramEntry->mData; - Histogram *h; - if (info.h) { - h = info.h; - } else { + if (!info.h) { nsCAutoString actualName; AddonHistogramName(id, name, actualName); - nsresult rv = HistogramGet(PromiseFlatCString(actualName).get(), - info.min, info.max, info.bucketCount, - info.histogramType, &h); - if (NS_FAILED(rv)) { - return rv; + if (!CreateHistogramForAddon(actualName, info)) { + return NS_ERROR_FAILURE; } - // Don't let this histogram be reported via the normal means - // (e.g. Telemetry.registeredHistograms); we'll make it available in - // other ways. - h->ClearFlags(Histogram::kUmaTargetedHistogramFlag); - info.h = h; } - return WrapAndReturnHistogram(h, cx, ret); + return WrapAndReturnHistogram(info.h, cx, ret); } NS_IMETHODIMP @@ -780,6 +774,16 @@ TelemetryImpl::GetHistogramSnapshots(JSContext *cx, jsval *ret) return NS_ERROR_FAILURE; *ret = OBJECT_TO_JSVAL(root_obj); + // Ensure that all the HISTOGRAM_FLAG histograms have been created, so + // that their values are snapshotted. + for (size_t i = 0; i < Telemetry::HistogramCount; ++i) { + if (gHistograms[i].histogramType == nsITelemetry::HISTOGRAM_FLAG) { + Histogram *h; + DebugOnly rv = GetHistogramByEnumId(Telemetry::ID(i), &h); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + } + }; + StatisticsRecorder::Histograms hs; StatisticsRecorder::GetHistograms(&hs); @@ -821,13 +825,40 @@ TelemetryImpl::GetHistogramSnapshots(JSContext *cx, jsval *ret) return NS_OK; } +bool +TelemetryImpl::CreateHistogramForAddon(const nsACString &name, + AddonHistogramInfo &info) +{ + Histogram *h; + nsresult rv = HistogramGet(PromiseFlatCString(name).get(), + info.min, info.max, info.bucketCount, + info.histogramType, &h); + if (NS_FAILED(rv)) { + return false; + } + // Don't let this histogram be reported via the normal means + // (e.g. Telemetry.registeredHistograms); we'll make it available in + // other ways. + h->ClearFlags(Histogram::kUmaTargetedHistogramFlag); + info.h = h; + return true; +} + bool TelemetryImpl::AddonHistogramReflector(AddonHistogramEntryType *entry, JSContext *cx, JSObject *obj) { + AddonHistogramInfo &info = entry->mData; + // Never even accessed the histogram. - if (!entry->mData.h) { - return true; + if (!info.h) { + // Have to force creation of HISTOGRAM_FLAG histograms. + if (info.histogramType != nsITelemetry::HISTOGRAM_FLAG) + return true; + + if (!CreateHistogramForAddon(entry->GetKey(), info)) { + return false; + } } JSObject *snapshot = JS_NewObject(cx, NULL, NULL, NULL); @@ -836,7 +867,7 @@ TelemetryImpl::AddonHistogramReflector(AddonHistogramEntryType *entry, return true; } JS::AutoObjectRooter r(cx, snapshot); - switch (ReflectHistogramSnapshot(cx, snapshot, entry->mData.h)) { + switch (ReflectHistogramSnapshot(cx, snapshot, info.h)) { case REFLECT_FAILURE: case REFLECT_CORRUPT: return false; diff --git a/toolkit/components/telemetry/TelemetryHistograms.h b/toolkit/components/telemetry/TelemetryHistograms.h index 8a8f923acfb..87f2739d59c 100644 --- a/toolkit/components/telemetry/TelemetryHistograms.h +++ b/toolkit/components/telemetry/TelemetryHistograms.h @@ -51,6 +51,8 @@ /* Convenience macro for BOOLEAN histograms. */ #define HISTOGRAM_BOOLEAN(id, message) HISTOGRAM(id, 0, 1, 2, BOOLEAN, message) +/* Likewise for FLAG histograms. */ +#define HISTOGRAM_FLAG(id, message) HISTOGRAM(id, 0, 1, 2, FLAG, message) /** * a11y telemetry @@ -102,6 +104,7 @@ HISTOGRAM(MEMORY_FREE_PURGED_PAGES_MS, 1, 1024, 10, EXPONENTIAL, "Time(ms) to pu #elif defined(XP_WIN) HISTOGRAM(LOW_MEMORY_EVENTS_VIRTUAL, 1, 1024, 21, EXPONENTIAL, "Number of low-virtual-memory events fired since last ping") HISTOGRAM(LOW_MEMORY_EVENTS_PHYSICAL, 1, 1024, 21, EXPONENTIAL, "Number of low-physical-memory events fired since last ping") +HISTOGRAM(LOW_MEMORY_EVENTS_COMMIT_SPACE, 1, 1024, 21, EXPONENTIAL, "Number of low-commit-space events fired since last ping") #endif #if defined(XP_WIN) @@ -132,6 +135,8 @@ HISTOGRAM(MAC_INITFONTLIST_TOTAL, 1, 30000, 10, EXPONENTIAL, "gfxMacPlatformFont HISTOGRAM(SYSTEM_FONT_FALLBACK, 1, 100000, 50, EXPONENTIAL, "System font fallback (us)") HISTOGRAM(SYSTEM_FONT_FALLBACK_FIRST, 1, 40000, 20, EXPONENTIAL, "System font fallback, first call (ms)") +HISTOGRAM(SYSTEM_FONT_FALLBACK_SCRIPT, 1, 110, 111, LINEAR, "System font fallback script") + HISTOGRAM(STARTUP_CACHE_AGE_HOURS, 1, 3000, 20, EXPONENTIAL, "Startup cache age (hours)") /** @@ -144,6 +149,7 @@ HISTOGRAM(WORD_CACHE_LOOKUP_SCRIPT, 1, 110, 111, LINEAR, "Word cache lookup (scr HISTOGRAM(WORD_CACHE_HIT_SCRIPT, 1, 110, 111, LINEAR, "Word cache hit (script)") HISTOGRAM_BOOLEAN(FONT_CACHE_HIT, "font cache hit") +HISTOGRAM_BOOLEAN(BAD_FALLBACK_FONT, "system fallback font can't be used") HISTOGRAM_BOOLEAN(SHUTDOWN_OK, "Did the browser start after a successful shutdown") @@ -412,5 +418,7 @@ HISTOGRAM(RANGE_CHECKSUM_ERRORS, 1, 3000, 10, EXPONENTIAL, "Number of histograms HISTOGRAM(BUCKET_ORDER_ERRORS, 1, 3000, 10, EXPONENTIAL, "Number of histograms with bucket order errors") HISTOGRAM(TOTAL_COUNT_HIGH_ERRORS, 1, 3000, 10, EXPONENTIAL, "Number of histograms with total count high errors") HISTOGRAM(TOTAL_COUNT_LOW_ERRORS, 1, 3000, 10, EXPONENTIAL, "Number of histograms with total count low errors") +HISTOGRAM_FLAG(TELEMETRY_TEST_FLAG, "a testing histogram; not meant to be touched") #undef HISTOGRAM_BOOLEAN +#undef HISTOGRAM_FLAG diff --git a/toolkit/components/telemetry/TelemetryPing.js b/toolkit/components/telemetry/TelemetryPing.js index 6ff1661c92d..cccf36fcfbd 100644 --- a/toolkit/components/telemetry/TelemetryPing.js +++ b/toolkit/components/telemetry/TelemetryPing.js @@ -67,6 +67,7 @@ const MEM_HISTOGRAMS = { "heap-allocated": "MEMORY_HEAP_ALLOCATED", "page-faults-hard": "PAGE_FAULTS_HARD", "low-memory-events-virtual": "LOW_MEMORY_EVENTS_VIRTUAL", + "low-memory-events-commit-space": "LOW_MEMORY_EVENTS_COMMIT_SPACE", "low-memory-events-physical": "LOW_MEMORY_EVENTS_PHYSICAL" }; // Seconds of idle time before pinging. @@ -634,7 +635,11 @@ TelemetryPing.prototype = { */ uninstall: function uninstall() { this.detachObservers() - Services.obs.removeObserver(this, "sessionstore-windows-restored"); + try { + Services.obs.removeObserver(this, "sessionstore-windows-restored"); + } catch (e) { + // Already observed this event. + } Services.obs.removeObserver(this, "profile-before-change"); Services.obs.removeObserver(this, "private-browsing"); Services.obs.removeObserver(this, "quit-application-granted"); @@ -674,6 +679,9 @@ TelemetryPing.prototype = { } break; case "sessionstore-windows-restored": + Services.obs.removeObserver(this, "sessionstore-windows-restored"); + // fall through + case "test-gather-startup": this.gatherStartupInformation(); break; case "idle-daily": diff --git a/toolkit/components/telemetry/nsITelemetry.idl b/toolkit/components/telemetry/nsITelemetry.idl index 6cb6aa7c331..9714cab73f5 100644 --- a/toolkit/components/telemetry/nsITelemetry.idl +++ b/toolkit/components/telemetry/nsITelemetry.idl @@ -78,10 +78,12 @@ interface nsITelemetry : nsISupports * HISTOGRAM_EXPONENTIAL - buckets increase exponentially * HISTOGRAM_LINEAR - buckets increase linearly * HISTOGRAM_BOOLEAN - For storing 0/1 values + * HISTOGRAM_FLAG - For storing a single value; its count is always == 1. */ const unsigned long HISTOGRAM_EXPONENTIAL = 0; const unsigned long HISTOGRAM_LINEAR = 1; const unsigned long HISTOGRAM_BOOLEAN = 2; + const unsigned long HISTOGRAM_FLAG = 3; /* * An object containing a snapshot from all of the currently registered histograms. diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js index 4b8c6aeef5c..6683d554486 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js @@ -29,7 +29,7 @@ var gFinished = false; function telemetry_ping () { const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsIObserver); - TelemetryPing.observe(null, "sessionstore-windows-restored", null); + TelemetryPing.observe(null, "test-gather-startup", null); TelemetryPing.observe(null, "test-ping", SERVER); } @@ -121,6 +121,7 @@ function checkHistograms(request, response) { const TELEMETRY_PING = "TELEMETRY_PING"; const TELEMETRY_SUCCESS = "TELEMETRY_SUCCESS"; + const TELEMETRY_TEST_FLAG = "TELEMETRY_TEST_FLAG"; do_check_true(TELEMETRY_PING in payload.histograms); let rh = Telemetry.registeredHistograms; for (let name in rh) { @@ -131,6 +132,17 @@ function checkHistograms(request, response) { do_check_false(IGNORE_HISTOGRAM in payload.histograms); do_check_false(IGNORE_CLONED_HISTOGRAM in payload.histograms); + // Flag histograms should automagically spring to life. + const expected_flag = { + range: [1, 2], + bucket_count: 3, + histogram_type: 3, + values: {0:1, 1:0}, + sum: 1 + }; + let flag = payload.histograms[TELEMETRY_TEST_FLAG]; + do_check_eq(uneval(flag), uneval(expected_flag)); + // There should be one successful report from the previous telemetry ping. const expected_tc = { range: [1, 2], diff --git a/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js b/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js index 844f0471d43..91846219872 100644 --- a/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js +++ b/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js @@ -85,6 +85,32 @@ function test_boolean_histogram() do_check_eq(s.counts[0], 2); } +function test_flag_histogram() +{ + var h = Telemetry.newHistogram("test::flag histogram", 130, 4, 5, Telemetry.HISTOGRAM_FLAG); + var r = h.snapshot().ranges; + // Flag histograms ignore numeric parameters. + do_check_eq(uneval(r), uneval([0, 1, 2])) + // Should already have a 0 counted. + var c = h.snapshot().counts; + var s = h.snapshot().sum; + do_check_eq(uneval(c), uneval([1, 0, 0])); + do_check_eq(s, 1); + // Should switch counts. + h.add(2); + var c2 = h.snapshot().counts; + var s2 = h.snapshot().sum; + do_check_eq(uneval(c2), uneval([0, 1, 0])); + do_check_eq(s, 1); + // Should only switch counts once. + h.add(3); + var c3 = h.snapshot().counts; + var s3 = h.snapshot().sum; + do_check_eq(uneval(c3), uneval([0, 1, 0])); + do_check_eq(s3, 1); + do_check_eq(h.snapshot().histogram_type, Telemetry.FLAG_HISTOGRAM); +} + function test_getHistogramById() { try { Telemetry.getHistogramById("nonexistent"); @@ -148,10 +174,19 @@ function test_addons() { expect_success(function () register(extra_addon, name1, 0, 1, 2, Telemetry.HISTOGRAM_BOOLEAN)); + // Check that we can register flag histograms. + var flag_addon = "testing-flag-addon"; + var flag_histogram = "flag-histogram"; + expect_success(function() + register(flag_addon, flag_histogram, 0, 1, 2, Telemetry.HISTOGRAM_FLAG)) + expect_success(function() + register(flag_addon, name2, 2, 4, 4, Telemetry.HISTOGRAM_LINEAR)); + // Check that we reflect registered addons and histograms. snapshots = Telemetry.addonHistogramSnapshots; do_check_true(addon_id in snapshots) do_check_true(extra_addon in snapshots); + do_check_true(flag_addon in snapshots); // Check that we have data for our created histograms. do_check_true(name1 in snapshots[addon_id]); @@ -168,6 +203,10 @@ function test_addons() { // Even though we've registered it, it shouldn't show up until data is added to it. do_check_false(name1 in snapshots[extra_addon]); + // Flag histograms should show up automagically. + do_check_true(flag_histogram in snapshots[flag_addon]); + do_check_false(name2 in snapshots[flag_addon]); + // Check that we can remove addon histograms. Telemetry.unregisterAddonHistograms(addon_id); snapshots = Telemetry.addonHistogramSnapshots; diff --git a/toolkit/content/Makefile.in b/toolkit/content/Makefile.in index 11c1ab5d00e..50d633c9b5f 100644 --- a/toolkit/content/Makefile.in +++ b/toolkit/content/Makefile.in @@ -55,6 +55,8 @@ DEFINES += \ -DCXX_VERSION="$(CXX_VERSION)" \ -DCXXFLAGS="$(CXXFLAGS)" \ -DCPPFLAGS="$(CPPFLAGS)" \ + -DMOZ_APP_NAME=$(MOZ_APP_NAME) \ + -DMOZ_BUILD_APP=$(MOZ_BUILD_APP) \ $(NULL) MOZ_SOURCE_STAMP ?= $(shell hg -R $(topsrcdir) parent --template="{node|short}\n" 2>/dev/null) diff --git a/toolkit/content/aboutSupport.js b/toolkit/content/aboutSupport.js index 1e648dad65d..8fbb2eb198a 100644 --- a/toolkit/content/aboutSupport.js +++ b/toolkit/content/aboutSupport.js @@ -38,6 +38,7 @@ const Cc = Components.classes; const Ci = Components.interfaces; +const Cu = Components.utils; Components.utils.import("resource://gre/modules/AddonManager.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); @@ -113,6 +114,7 @@ window.onload = function () { document.getElementById("version-box").textContent = version; // Update the other sections. + populateResetBox(); populatePreferencesSection(); populateExtensionsSection(); populateGraphicsSection(); @@ -561,3 +563,51 @@ function openProfileDirectory() { "nsILocalFile", "initWithPath"); new nsLocalFile(profileDir).reveal(); } + +/** + * Profile reset is only supported for the default profile if the appropriate migrator exists. + */ +function populateResetBox() { + let profileService = Cc["@mozilla.org/toolkit/profile-service;1"] + .getService(Ci.nsIToolkitProfileService); + let currentProfileDir = Services.dirsvc.get("ProfD", Ci.nsIFile); + +#expand const MOZ_APP_NAME = "__MOZ_APP_NAME__"; +#expand const MOZ_BUILD_APP = "__MOZ_BUILD_APP__"; + + // Only show the reset box for the default profile if the self-migrator used for reset exists. + try { + if (!currentProfileDir.equals(profileService.selectedProfile.rootDir) || + !("@mozilla.org/profile/migrator;1?app=" + MOZ_BUILD_APP + "&type=" + MOZ_APP_NAME in Cc)) + return; + document.getElementById("reset-box").style.visibility = "visible"; + } catch (e) { + // Catch exception when there is no selected profile. + Cu.reportError(e); + } +} + +/** + * Restart the application to reset the profile. + */ +function resetProfileAndRestart() { + let branding = Services.strings.createBundle("chrome://branding/locale/brand.properties"); + let brandShortName = branding.GetStringFromName("brandShortName"); + + // Prompt the user to confirm. + let retVals = { + reset: false, + }; + window.openDialog("chrome://global/content/resetProfile.xul", null, + "chrome,modal,centerscreen,titlebar,dialog=yes", retVals); + if (!retVals.reset) + return; + + // Set the reset profile environment variable. + let env = Cc["@mozilla.org/process/environment;1"] + .getService(Ci.nsIEnvironment); + env.set("MOZ_RESET_PROFILE_RESTART", "1"); + + let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup); + appStartup.quit(Ci.nsIAppStartup.eForceQuit | Ci.nsIAppStartup.eRestart); +} diff --git a/toolkit/content/aboutSupport.xhtml b/toolkit/content/aboutSupport.xhtml index e92194bdcc6..a392b9a1424 100644 --- a/toolkit/content/aboutSupport.xhtml +++ b/toolkit/content/aboutSupport.xhtml @@ -42,6 +42,7 @@ %globalDTD; %brandDTD; %aboutSupportDTD; + %resetProfileDTD; ]> @@ -57,6 +58,14 @@ + +

    &aboutSupport.pageTitle;

    diff --git a/toolkit/content/jar.mn b/toolkit/content/jar.mn index afc8549acd0..6f0d2d6367f 100644 --- a/toolkit/content/jar.mn +++ b/toolkit/content/jar.mn @@ -37,6 +37,9 @@ toolkit.jar: *+ content/global/globalOverlay.js (globalOverlay.js) + content/global/mozilla.xhtml (mozilla.xhtml) *+ content/global/nsDragAndDrop.js (nsDragAndDrop.js) + content/global/resetProfile.css (resetProfile.css) +* content/global/resetProfile.js (resetProfile.js) +* content/global/resetProfile.xul (resetProfile.xul) * content/global/treeUtils.js (treeUtils.js) *+ content/global/viewZoomOverlay.js (viewZoomOverlay.js) *+ content/global/bindings/autocomplete.xml (widgets/autocomplete.xml) diff --git a/toolkit/content/resetProfile.css b/toolkit/content/resetProfile.css new file mode 100644 index 00000000000..2a8114801a6 --- /dev/null +++ b/toolkit/content/resetProfile.css @@ -0,0 +1,11 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#migratedItems { + -moz-margin-start: 1.5em; +} + +#resetProfileFooter { + font-weight: bold; +} diff --git a/toolkit/content/resetProfile.js b/toolkit/content/resetProfile.js new file mode 100644 index 00000000000..975a9edcb64 --- /dev/null +++ b/toolkit/content/resetProfile.js @@ -0,0 +1,35 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +Components.utils.import("resource://gre/modules/Services.jsm"); + +// based on onImportItemsPageShow from migration.js +function onResetProfileLoad() { +#expand const MOZ_BUILD_APP = "__MOZ_BUILD_APP__"; +#expand const MOZ_APP_NAME = "__MOZ_APP_NAME__"; + const MAX_MIGRATED_TYPES = 16; + + var migratedItems = document.getElementById("migratedItems"); + var bundle = Services.strings.createBundle("chrome://" + MOZ_BUILD_APP + + "/locale/migration/migration.properties"); + + // Loop over possible data to migrate to give the user a list of what will be preserved. This + // assumes that if the string for the data exists then it will be migrated since calling + // getMigrateData now won't give the correct result. + for (var i = 1; i < MAX_MIGRATED_TYPES; ++i) { + var itemID = Math.pow(2, i); + try { + var checkbox = document.createElement("label"); + checkbox.setAttribute("value", bundle.GetStringFromName(itemID + "_" + MOZ_APP_NAME)); + migratedItems.appendChild(checkbox); + } catch (x) { + // Catch exceptions when the string for a data type doesn't exist because it's not migrated + } + } +} + +function onResetProfileAccepted() { + var retVals = window.arguments[0]; + retVals.reset = true; +} diff --git a/toolkit/content/resetProfile.xul b/toolkit/content/resetProfile.xul new file mode 100644 index 00000000000..bd2ee2627b1 --- /dev/null +++ b/toolkit/content/resetProfile.xul @@ -0,0 +1,36 @@ + + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. + + +%brandDTD; + +%resetProfileDTD; +]> + + + + + + +