diff --git a/accessible/src/base/nsAccessibilityService.cpp b/accessible/src/base/nsAccessibilityService.cpp index fd82e4e1161d..2981a0495c09 100644 --- a/accessible/src/base/nsAccessibilityService.cpp +++ b/accessible/src/base/nsAccessibilityService.cpp @@ -36,7 +36,7 @@ #ifdef XP_WIN #include "nsHTMLWin32ObjectAccessible.h" #endif -#include "TextLeafAccessible.h" +#include "TextLeafAccessibleWrap.h" #ifdef DEBUG #include "Logging.h" @@ -416,7 +416,7 @@ nsAccessibilityService::CreateTextLeafAccessible(nsIContent* aContent, nsIPresShell* aPresShell) { Accessible* accessible = - new TextLeafAccessible(aContent, GetDocAccessible(aPresShell)); + new TextLeafAccessibleWrap(aContent, GetDocAccessible(aPresShell)); NS_ADDREF(accessible); return accessible; } diff --git a/accessible/tests/mochitest/common.js b/accessible/tests/mochitest/common.js index 16718610f60a..fb6bfdee641e 100644 --- a/accessible/tests/mochitest/common.js +++ b/accessible/tests/mochitest/common.js @@ -554,6 +554,13 @@ function relationTypeToString(aRelationType) return gAccRetrieval.getStringRelationType(aRelationType); } +function getLoadContext() { + const Ci = Components.interfaces; + return window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsILoadContext); +} + /** * Return text from clipboard. */ @@ -566,6 +573,7 @@ function getTextFromClipboard() var trans = Components.classes["@mozilla.org/widget/transferable;1"]. createInstance(Components.interfaces.nsITransferable); + trans.init(getLoadContext()); if (!trans) return; diff --git a/accessible/tests/mochitest/events/test_fromUserInput.html b/accessible/tests/mochitest/events/test_fromUserInput.html index 64a33fbadfe7..1cfeedf0b360 100644 --- a/accessible/tests/mochitest/events/test_fromUserInput.html +++ b/accessible/tests/mochitest/events/test_fromUserInput.html @@ -68,7 +68,7 @@ var selection = window.getSelection(); var range = document.createRange(); range.setStart(this.textNode, aStart); - range.setEnd(this.textNode, aEnd-1); + range.setEnd(this.textNode, aEnd); selection.addRange(range); synthesizeKey("VK_DELETE", {}); diff --git a/b2g/chrome/content/shell.js b/b2g/chrome/content/shell.js index 8416f1ea38d4..155c8e3717cc 100644 --- a/b2g/chrome/content/shell.js +++ b/b2g/chrome/content/shell.js @@ -591,16 +591,15 @@ window.addEventListener('ContentStart', function(evt) { }); })(); -// This is the backend for Gaia's screenshot feature. -// Gaia requests a screenshot by sending a mozContentEvent with -// detail.type set to 'save-screenshot'. Then we take a screenshot -// save it in device storage (external) and send a mozChromeEvent with -// detail.type set to 'saved-screenshot' and detail.filename set to -// the filename. +// This is the backend for Gaia's screenshot feature. Gaia requests a +// screenshot by sending a mozContentEvent with detail.type set to +// 'take-screenshot'. Then we take a screenshot and send a +// mozChromeEvent with detail.type set to 'take-screenshot-success' +// and detail.file set to the an image/png blob window.addEventListener('ContentStart', function ss_onContentStart() { let content = shell.contentBrowser.contentWindow; content.addEventListener('mozContentEvent', function ss_onMozContentEvent(e) { - if (e.detail.type !== 'save-screenshot') + if (e.detail.type !== 'take-screenshot') return; try { @@ -619,44 +618,14 @@ window.addEventListener('ContentStart', function ss_onContentStart() { context.drawWindow(window, 0, 0, width, height, 'rgb(255,255,255)', flags); - var filename = 'screenshots/' + - new Date().toISOString().slice(0,-5).replace(/[:T]/g, '-') + - '.png'; - - var file = canvas.mozGetAsFile(filename, 'image/png'); - var storage = navigator.getDeviceStorage('pictures')[0]; - if (!storage) { // If we don't have an SD card to save to, send an error - shell.sendEvent(content, 'mozChromeEvent', { - type: 'save-screenshot-no-card' - }); - return; - } - - var saveRequest = storage.addNamed(file, filename); - saveRequest.onsuccess = function ss_onsuccess() { - try { - shell.sendEvent(content, 'mozChromeEvent', { - type: 'save-screenshot-success', - filename: filename - }); - } catch(e) { - dump('exception in onsuccess ' + e + '\n'); - } - }; - saveRequest.onerror = function ss_onerror() { - try { - shell.sendEvent(content, 'mozChromeEvent', { - type: 'save-screenshot-error', - error: saveRequest.error.name - }); - } catch(e) { - dump('exception in onerror ' + e + '\n'); - } - }; - } catch(e) { - dump('exception while saving screenshot: ' + e + '\n'); shell.sendEvent(content, 'mozChromeEvent', { - type: 'save-screenshot-error', + type: 'take-screenshot-success', + file: canvas.mozGetAsFile('screenshot', 'image/png') + }); + } catch (e) { + dump('exception while creating screenshot: ' + e + '\n'); + shell.sendEvent(content, 'mozChromeEvent', { + type: 'take-screenshot-error', error: String(e) }); } diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 120192362ce7..26b65e30dbaf 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -2227,6 +2227,12 @@ function getPostDataStream(aStringData, aKeyword, aEncKeyword, aType) { return mimeStream.QueryInterface(Ci.nsIInputStream); } +function getLoadContext() { + return window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsILoadContext); +} + function readFromClipboard() { var url; @@ -2239,6 +2245,7 @@ function readFromClipboard() // Create transferable that will transfer the text. var trans = Components.classes["@mozilla.org/widget/transferable;1"] .createInstance(Components.interfaces.nsITransferable); + trans.init(getLoadContext()); trans.addDataFlavor("text/unicode"); diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js index c50b66948725..3edfa6596870 100644 --- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -1117,7 +1117,7 @@ nsContextMenu.prototype = { var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]. getService(Ci.nsIClipboardHelper); - clipboard.copyString(addresses); + clipboard.copyString(addresses, document); }, /////////////// @@ -1453,7 +1453,7 @@ nsContextMenu.prototype = { copyMediaLocation : function () { var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]. getService(Ci.nsIClipboardHelper); - clipboard.copyString(this.mediaURL); + clipboard.copyString(this.mediaURL, document); }, get imageURL() { diff --git a/browser/base/content/pageinfo/pageInfo.js b/browser/base/content/pageinfo/pageInfo.js index aaa8e9d82dfc..3dcc3cc6fecb 100644 --- a/browser/base/content/pageinfo/pageInfo.js +++ b/browser/base/content/pageinfo/pageInfo.js @@ -1243,7 +1243,7 @@ function doCopy() elem.removeAttribute("copybuffer"); } } - gClipboardHelper.copyString(text.join("\n")); + gClipboardHelper.copyString(text.join("\n"), document); } } diff --git a/browser/base/content/sanitizeDialog.js b/browser/base/content/sanitizeDialog.js index 6fea403a41a8..68e33cca2b66 100644 --- a/browser/base/content/sanitizeDialog.js +++ b/browser/base/content/sanitizeDialog.js @@ -654,6 +654,7 @@ var gContiguousSelectionTreeHelper = { createInstance(Ci.nsISupportsArray); var trans = Cc["@mozilla.org/widget/transferable;1"]. createInstance(Ci.nsITransferable); + trans.init(null); trans.setTransferData('dummy-flavor', null, 0); arr.AppendElement(trans); var reg = Cc["@mozilla.org/gfx/region;1"]. diff --git a/browser/base/content/test/browser_bug321000.js b/browser/base/content/test/browser_bug321000.js index 88ccfe13cb92..8f0280a99a3e 100644 --- a/browser/base/content/test/browser_bug321000.js +++ b/browser/base/content/test/browser_bug321000.js @@ -32,7 +32,7 @@ function test() { // Put a multi-line string in the clipboard. // Setting the clipboard value is an async OS operation, so we need to poll // the clipboard for valid data before going on. - waitForClipboard(kTestString, function() { cbHelper.copyString(kTestString); }, + waitForClipboard(kTestString, function() { cbHelper.copyString(kTestString, document); }, next_test, finish); } diff --git a/browser/base/content/test/browser_middleMouse_inherit.js b/browser/base/content/test/browser_middleMouse_inherit.js index 121cb6da2e10..891ea2ed0918 100644 --- a/browser/base/content/test/browser_middleMouse_inherit.js +++ b/browser/base/content/test/browser_middleMouse_inherit.js @@ -25,7 +25,7 @@ function test() { function() { Components.classes["@mozilla.org/widget/clipboardhelper;1"] .getService(Components.interfaces.nsIClipboardHelper) - .copyString(url); + .copyString(url, document); }, function () { // Middle click on the content area diff --git a/browser/base/content/urlbarBindings.xml b/browser/base/content/urlbarBindings.xml index 783d4d84a0ce..6b8a902b4a81 100644 --- a/browser/base/content/urlbarBindings.xml +++ b/browser/base/content/urlbarBindings.xml @@ -550,7 +550,7 @@ Cc["@mozilla.org/widget/clipboardhelper;1"] .getService(Ci.nsIClipboardHelper) - .copyString(val); + .copyString(val, document); }, supportsCommand: function(aCommand) { switch (aCommand) { @@ -751,7 +751,7 @@ Cc["@mozilla.org/widget/clipboardhelper;1"] .getService(Ci.nsIClipboardHelper) - .copyStringToClipboard(val, Ci.nsIClipboard.kSelectionClipboard); + .copyStringToClipboard(val, document, Ci.nsIClipboard.kSelectionClipboard); ]]> diff --git a/browser/components/downloads/content/downloads.js b/browser/components/downloads/content/downloads.js index cca869e71e61..c79aebcb86d5 100644 --- a/browser/components/downloads/content/downloads.js +++ b/browser/components/downloads/content/downloads.js @@ -1167,7 +1167,7 @@ DownloadsViewItemController.prototype = { { let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"] .getService(Ci.nsIClipboardHelper); - clipboard.copyString(this.dataItem.uri); + clipboard.copyString(this.dataItem.uri, document); }, downloadsCmd_doDefault: function DVIC_downloadsCmd_doDefault() diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index 5ebe789ff5c8..6478cc38ed10 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -492,13 +492,6 @@ BrowserGlue.prototype = { if (!aQuitType) aQuitType = "quit"; - // Never show a prompt inside private browsing mode - var inPrivateBrowsing = Cc["@mozilla.org/privatebrowsing;1"]. - getService(Ci.nsIPrivateBrowsingService). - privateBrowsingEnabled; - if (inPrivateBrowsing) - return; - var showPrompt = false; var mostRecentBrowserWindow; @@ -530,6 +523,12 @@ BrowserGlue.prototype = { return; } + var inPrivateBrowsing = Cc["@mozilla.org/privatebrowsing;1"]. + getService(Ci.nsIPrivateBrowsingService). + privateBrowsingEnabled; + if (inPrivateBrowsing) + return; + if (!showPrompt) return; diff --git a/browser/components/places/content/controller.js b/browser/components/places/content/controller.js index e57cb18858ab..25a5570e2d1d 100644 --- a/browser/components/places/content/controller.js +++ b/browser/components/places/content/controller.js @@ -376,6 +376,7 @@ PlacesController.prototype = { // pasting of valid "text/unicode" and "text/x-moz-url" data var xferable = Cc["@mozilla.org/widget/transferable;1"]. createInstance(Ci.nsITransferable); + xferable.init(null); xferable.addDataFlavor(PlacesUtils.TYPE_X_MOZ_URL); xferable.addDataFlavor(PlacesUtils.TYPE_UNICODE); @@ -1061,6 +1062,7 @@ PlacesController.prototype = { try { let xferable = Cc["@mozilla.org/widget/transferable;1"]. createInstance(Ci.nsITransferable); + xferable.init(null); xferable.addDataFlavor(PlacesUtils.TYPE_X_MOZ_PLACE_ACTION) this.clipboard.getData(xferable, Ci.nsIClipboard.kGlobalClipboard); xferable.getTransferData(PlacesUtils.TYPE_X_MOZ_PLACE_ACTION, action, {}); @@ -1090,6 +1092,7 @@ PlacesController.prototype = { _clearClipboard: function PC__clearClipboard() { let xferable = Cc["@mozilla.org/widget/transferable;1"]. createInstance(Ci.nsITransferable); + xferable.init(null); // Empty transferables may cause crashes, so just add an unknown type. const TYPE = "text/x-moz-place-empty"; xferable.addDataFlavor(TYPE); @@ -1135,6 +1138,7 @@ PlacesController.prototype = { let xferable = Cc["@mozilla.org/widget/transferable;1"]. createInstance(Ci.nsITransferable); + xferable.init(null); let hasData = false; // This order matters here! It controls how this and other applications // select data to be inserted based on type. @@ -1224,6 +1228,7 @@ PlacesController.prototype = { let xferable = Cc["@mozilla.org/widget/transferable;1"]. createInstance(Ci.nsITransferable); + xferable.init(null); // This order matters here! It controls the preferred flavors for this // paste operation. [ PlacesUtils.TYPE_X_MOZ_PLACE, diff --git a/browser/components/search/content/search.xml b/browser/components/search/content/search.xml index fc4328a3dc1f..289b40c77ba0 100644 --- a/browser/components/search/content/search.xml +++ b/browser/components/search/content/search.xml @@ -67,25 +67,25 @@ + // Make sure we rebuild the popup in onpopupshowing + this._needToBuildPopup = true; - - - + this._addedObserver = true; + + this.searchService.init((function search_init_cb(aStatus) { + if (Components.isSuccessCode(aStatus)) { + // Refresh the display (updating icon, etc) + this.updateDisplay(); + } else { + Components.utils.reportError("Cannot initialize search service, bailing out: " + aStatus); + } + }).bind(this)); + ]]> container = GetContainer(); + if (container) { + nsCOMPtr loadContext = do_QueryInterface(container); + return loadContext; + } + return nsnull; + } + /** * Set and get XML declaration. If aVersion is null there is no declaration. * aStandalone takes values -1, 0 and 1 indicating respectively that there diff --git a/content/base/src/nsCCUncollectableMarker.cpp b/content/base/src/nsCCUncollectableMarker.cpp index e0987c47226d..e1e19347f1f9 100644 --- a/content/base/src/nsCCUncollectableMarker.cpp +++ b/content/base/src/nsCCUncollectableMarker.cpp @@ -265,6 +265,8 @@ nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData) { if (!strcmp(aTopic, "xpcom-shutdown")) { + nsGenericElement::ClearContentUnbinder(); + nsCOMPtr obs = mozilla::services::GetObserverService(); if (!obs) @@ -289,7 +291,9 @@ nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic, !strcmp(aTopic, "cycle-collector-forget-skippable"); bool prepareForCC = !strcmp(aTopic, "cycle-collector-begin"); - + if (prepareForCC) { + nsGenericElement::ClearContentUnbinder(); + } // Increase generation to effectivly unmark all current objects if (!++sGeneration) { diff --git a/content/base/src/nsCopySupport.cpp b/content/base/src/nsCopySupport.cpp index 90957d8289fb..9f10fac66afa 100644 --- a/content/base/src/nsCopySupport.cpp +++ b/content/base/src/nsCopySupport.cpp @@ -182,6 +182,7 @@ SelectionCopyHelper(nsISelection *aSel, nsIDocument *aDoc, // Create a transferable for putting data on the Clipboard nsCOMPtr trans = do_CreateInstance(kCTransferableCID); if (trans) { + trans->Init(aDoc->GetLoadContext()); if (bIsHTMLCopy) { // Set up a format converter so that clipboard flavor queries work. // This converter isn't really used for conversions. @@ -459,6 +460,7 @@ nsCopySupport::GetContents(const nsACString& aMimeType, PRUint32 aFlags, nsISele nsresult nsCopySupport::ImageCopy(nsIImageLoadingContent* aImageElement, + nsILoadContext* aLoadContext, PRInt32 aCopyFlags) { nsresult rv; @@ -466,6 +468,7 @@ nsCopySupport::ImageCopy(nsIImageLoadingContent* aImageElement, // create a transferable for putting data on the Clipboard nsCOMPtr trans(do_CreateInstance(kCTransferableCID, &rv)); NS_ENSURE_SUCCESS(rv, rv); + trans->Init(aLoadContext); if (aCopyFlags & nsIContentViewerEdit::COPY_IMAGE_TEXT) { // get the location from the element diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 7d7b1749649e..9e3979000322 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -8991,6 +8991,7 @@ nsDocument::RequestFullScreen(Element* aElement, bool aWasCallerChrome) // to the document's principal's host, if it has one. if (!mIsApprovedForFullscreen) { mIsApprovedForFullscreen = + GetWindow()->IsPartOfApp() || nsContentUtils::IsSitePermAllow(NodePrincipal(), "fullscreen"); } diff --git a/content/base/src/nsGenericDOMDataNode.h b/content/base/src/nsGenericDOMDataNode.h index 30d1df1ec78e..30e300ed8d70 100644 --- a/content/base/src/nsGenericDOMDataNode.h +++ b/content/base/src/nsGenericDOMDataNode.h @@ -309,6 +309,11 @@ protected: nsTextFragment mText; public: + virtual bool OwnedOnlyByTheDOMTree() + { + return GetParent() && mRefCnt.get() == 1; + } + virtual bool IsPurple() { return mRefCnt.IsPurple(); diff --git a/content/base/src/nsGenericElement.cpp b/content/base/src/nsGenericElement.cpp index 81e7e93f0bd8..69e802d382ae 100644 --- a/content/base/src/nsGenericElement.cpp +++ b/content/base/src/nsGenericElement.cpp @@ -1045,6 +1045,44 @@ nsGenericElement::GetScrollWidth(PRInt32 *aScrollWidth) return NS_OK; } +PRInt32 +nsGenericElement::GetScrollLeftMax() +{ + nsIScrollableFrame* sf = GetScrollFrame(); + if (!sf) { + return 0; + } + + return nsPresContext::AppUnitsToIntCSSPixels(sf->GetScrollRange().XMost()); +} + +NS_IMETHODIMP +nsGenericElement::GetScrollLeftMax(PRInt32 *aScrollLeftMax) +{ + *aScrollLeftMax = GetScrollLeftMax(); + + return NS_OK; +} + +PRInt32 +nsGenericElement::GetScrollTopMax() +{ + nsIScrollableFrame* sf = GetScrollFrame(); + if (!sf) { + return 0; + } + + return nsPresContext::AppUnitsToIntCSSPixels(sf->GetScrollRange().YMost()); +} + +NS_IMETHODIMP +nsGenericElement::GetScrollTopMax(PRInt32 *aScrollTopMax) +{ + *aScrollTopMax = GetScrollTopMax(); + + return NS_OK; +} + nsRect nsGenericElement::GetClientAreaRect() { @@ -2809,6 +2847,16 @@ public: return NS_OK; } + static void UnbindAll() + { + nsRefPtr ub = sContentUnbinder; + sContentUnbinder = nsnull; + while (ub) { + ub->Run(); + ub = ub->mNext; + } + } + static void Append(nsIContent* aSubtreeRoot) { if (!sContentUnbinder) { @@ -2835,6 +2883,12 @@ private: ContentUnbinder* ContentUnbinder::sContentUnbinder = nsnull; +void +nsGenericElement::ClearContentUnbinder() +{ + ContentUnbinder::UnbindAll(); +} + NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericElement) nsINode::Unlink(tmp); @@ -3078,19 +3132,29 @@ nsGenericElement::CanSkipInCC(nsINode* aNode) } nsAutoTArray* gPurpleRoots = nsnull; +nsAutoTArray* gNodesToUnbind = nsnull; -void ClearPurpleRoots() +void ClearCycleCollectorCleanupData() { - if (!gPurpleRoots) { - return; + if (gPurpleRoots) { + PRUint32 len = gPurpleRoots->Length(); + for (PRUint32 i = 0; i < len; ++i) { + nsINode* n = gPurpleRoots->ElementAt(i); + n->SetIsPurpleRoot(false); + } + delete gPurpleRoots; + gPurpleRoots = nsnull; } - PRUint32 len = gPurpleRoots->Length(); - for (PRUint32 i = 0; i < len; ++i) { - nsINode* n = gPurpleRoots->ElementAt(i); - n->SetIsPurpleRoot(false); + if (gNodesToUnbind) { + PRUint32 len = gNodesToUnbind->Length(); + for (PRUint32 i = 0; i < len; ++i) { + nsIContent* c = gNodesToUnbind->ElementAt(i); + c->SetIsPurpleRoot(false); + ContentUnbinder::Append(c); + } + delete gNodesToUnbind; + gNodesToUnbind = nsnull; } - delete gPurpleRoots; - gPurpleRoots = nsnull; } static bool @@ -3164,8 +3228,8 @@ nsGenericElement::CanSkip(nsINode* aNode, bool aRemovingAllowed) return false; } - // Subtree has been traversed already, and aNode - // wasn't removed from purple buffer. No need to do more here. + // Subtree has been traversed already, and aNode has + // been handled in a way that doesn't require revisiting it. if (root->IsPurpleRoot()) { return false; } @@ -3175,8 +3239,12 @@ nsGenericElement::CanSkip(nsINode* aNode, bool aRemovingAllowed) nsAutoTArray nodesToClear; bool foundBlack = root->IsBlack(); + bool domOnlyCycle = false; if (root != currentDoc) { currentDoc = nsnull; + if (!foundBlack) { + domOnlyCycle = static_cast(root)->OwnedOnlyByTheDOMTree(); + } if (ShouldClearPurple(static_cast(root))) { nodesToClear.AppendElement(static_cast(root)); } @@ -3190,6 +3258,7 @@ nsGenericElement::CanSkip(nsINode* aNode, bool aRemovingAllowed) node = node->GetNextNode(root)) { foundBlack = foundBlack || node->IsBlack(); if (foundBlack) { + domOnlyCycle = false; if (currentDoc) { // If we can mark the whole document black, no need to optimize // so much, since when the next purple node in the document will be @@ -3202,19 +3271,36 @@ nsGenericElement::CanSkip(nsINode* aNode, bool aRemovingAllowed) node->RemovePurple(); } MarkNodeChildren(node); - } else if (ShouldClearPurple(node)) { - // Collect interesting nodes which we can clear if we find that - // they are kept alive in a black tree. - nodesToClear.AppendElement(node); + } else { + domOnlyCycle = domOnlyCycle && node->OwnedOnlyByTheDOMTree(); + if (ShouldClearPurple(node)) { + // Collect interesting nodes which we can clear if we find that + // they are kept alive in a black tree or are in a DOM-only cycle. + nodesToClear.AppendElement(node); + } } } if (!currentDoc || !foundBlack) { - if (!gPurpleRoots) { - gPurpleRoots = new nsAutoTArray(); - } root->SetIsPurpleRoot(true); - gPurpleRoots->AppendElement(root); + if (domOnlyCycle) { + if (!gNodesToUnbind) { + gNodesToUnbind = new nsAutoTArray(); + } + gNodesToUnbind->AppendElement(static_cast(root)); + for (PRUint32 i = 0; i < nodesToClear.Length(); ++i) { + nsIContent* n = nodesToClear[i]; + if ((n != aNode || aRemovingAllowed) && n->IsPurple()) { + n->RemovePurple(); + } + } + return true; + } else { + if (!gPurpleRoots) { + gPurpleRoots = new nsAutoTArray(); + } + gPurpleRoots->AppendElement(root); + } } if (!foundBlack) { @@ -3261,7 +3347,7 @@ nsGenericElement::CanSkipThis(nsINode* aNode) void nsGenericElement::InitCCCallbacks() { - nsCycleCollector_setForgetSkippableCallback(ClearPurpleRoots); + nsCycleCollector_setForgetSkippableCallback(ClearCycleCollectorCleanupData); nsCycleCollector_setBeforeUnlinkCallback(ClearBlackMarkedNodes); } @@ -4676,6 +4762,11 @@ nsGenericElement::MozRequestPointerLock() static const char* GetFullScreenError(nsIDocument* aDoc) { + nsCOMPtr win = aDoc->GetWindow(); + if (win && win->IsPartOfApp()) { + return nsnull; + } + if (!nsContentUtils::IsRequestFullScreenAllowed()) { return "FullScreenDeniedNotInputDriven"; } diff --git a/content/base/src/nsGenericElement.h b/content/base/src/nsGenericElement.h index 6962c0c1b609..c86637fc6f2d 100644 --- a/content/base/src/nsGenericElement.h +++ b/content/base/src/nsGenericElement.h @@ -547,6 +547,8 @@ public: PRInt32 GetScrollLeft(); PRInt32 GetScrollHeight(); PRInt32 GetScrollWidth(); + PRInt32 GetScrollLeftMax(); + PRInt32 GetScrollTopMax(); PRInt32 GetClientTop() { return nsPresContext::AppUnitsToIntCSSPixels(GetClientAreaRect().y); @@ -591,6 +593,16 @@ public: */ void FireNodeRemovedForChildren(); + virtual bool OwnedOnlyByTheDOMTree() + { + PRUint32 rc = mRefCnt.get(); + if (GetParent()) { + --rc; + } + rc -= mAttrsAndChildren.ChildCount(); + return rc == 0; + } + virtual bool IsPurple() { return mRefCnt.IsPurple(); @@ -601,6 +613,7 @@ public: mRefCnt.RemovePurple(); } + static void ClearContentUnbinder(); static bool CanSkip(nsINode* aNode, bool aRemovingAllowed); static bool CanSkipInCC(nsINode* aNode); static bool CanSkipThis(nsINode* aNode); diff --git a/content/base/src/nsRange.cpp b/content/base/src/nsRange.cpp index 7d37e893b4fe..9c96de864793 100644 --- a/content/base/src/nsRange.cpp +++ b/content/base/src/nsRange.cpp @@ -2017,30 +2017,23 @@ nsRange::CloneContents(nsIDOMDocumentFragment** aReturn) return NS_OK; } -nsresult -nsRange::CloneRange(nsRange** aReturn) const +already_AddRefed +nsRange::CloneRange() const { - if (aReturn == 0) - return NS_ERROR_NULL_POINTER; - nsRefPtr range = new nsRange(); range->SetMaySpanAnonymousSubtrees(mMaySpanAnonymousSubtrees); range->DoSetRange(mStartParent, mStartOffset, mEndParent, mEndOffset, mRoot); - range.forget(aReturn); - - return NS_OK; + return range.forget(); } NS_IMETHODIMP nsRange::CloneRange(nsIDOMRange** aReturn) { - nsRefPtr range; - nsresult rv = CloneRange(getter_AddRefs(range)); - range.forget(aReturn); - return rv; + *aReturn = CloneRange().get(); + return NS_OK; } NS_IMETHODIMP diff --git a/content/base/src/nsRange.h b/content/base/src/nsRange.h index a82b2bfabe54..b4bacc785974 100644 --- a/content/base/src/nsRange.h +++ b/content/base/src/nsRange.h @@ -118,7 +118,7 @@ public: void Reset(); nsresult SetStart(nsINode* aParent, PRInt32 aOffset); nsresult SetEnd(nsINode* aParent, PRInt32 aOffset); - nsresult CloneRange(nsRange** aNewRange) const; + already_AddRefed CloneRange() const; nsresult Set(nsINode* aStartParent, PRInt32 aStartOffset, nsINode* aEndParent, PRInt32 aEndOffset) diff --git a/content/base/test/test_bug166235.html b/content/base/test/test_bug166235.html index 71fc1839c4df..e6c6e19df767 100644 --- a/content/base/test/test_bug166235.html +++ b/content/base/test/test_bug166235.html @@ -22,6 +22,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=166235 /** Test for Bug 166235 **/ var Cc = SpecialPowers.wrap(Components).classes; + var Ci = SpecialPowers.wrap(Components).interfaces; var webnav = SpecialPowers.wrap(window).QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIWebNavigation) @@ -36,6 +37,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=166235 var textarea = SpecialPowers.wrap(document).getElementById('input'); + function getLoadContext() { + return SpecialPowers.wrap(window).QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsILoadContext); + } + function copyChildrenToClipboard(id) { textarea.blur(); clipboard.emptyClipboard(1); @@ -48,6 +55,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=166235 function getClipboardData(mime) { var transferable = Cc['@mozilla.org/widget/transferable;1'] .createInstance(Components.interfaces.nsITransferable); + transferable.init(getLoadContext()); transferable.addDataFlavor(mime); clipboard.getData(transferable, 1); var data = SpecialPowers.wrap({}); diff --git a/content/base/test/test_copypaste.html b/content/base/test/test_copypaste.html index c1e8712c7357..dbdf2cc1b176 100644 --- a/content/base/test/test_copypaste.html +++ b/content/base/test/test_copypaste.html @@ -35,6 +35,13 @@ function modifySelection(s) { }, 0) } +function getLoadContext() { + var Ci = SpecialPowers.wrap(Components).interfaces; + return SpecialPowers.wrap(window).QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsILoadContext); +} + function testCopyPaste () { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); @@ -86,6 +93,7 @@ function testCopyPaste () { function getClipboardData(mime) { var transferable = Components.classes['@mozilla.org/widget/transferable;1'] .createInstance(Components.interfaces.nsITransferable); + transferable.init(getLoadContext()); transferable.addDataFlavor(mime); clipboard.getData(transferable, 1); var data = {}; diff --git a/content/canvas/src/WebGLContextGL.cpp b/content/canvas/src/WebGLContextGL.cpp index 6b0dd890f834..32b046e2902f 100644 --- a/content/canvas/src/WebGLContextGL.cpp +++ b/content/canvas/src/WebGLContextGL.cpp @@ -2447,7 +2447,7 @@ WebGLContext::GetParameter(JSContext* cx, WebGLenum pname, ErrorResult& rv) case LOCAL_GL_COMPRESSED_TEXTURE_FORMATS: { uint32_t length = mCompressedTextureFormats.Length(); - JSObject* obj = Uint32Array::Create(cx, length, mCompressedTextureFormats.Elements()); + JSObject* obj = Uint32Array::Create(cx, this, length, mCompressedTextureFormats.Elements()); if (!obj) { rv = NS_ERROR_OUT_OF_MEMORY; } @@ -2524,7 +2524,7 @@ WebGLContext::GetParameter(JSContext* cx, WebGLenum pname, ErrorResult& rv) { GLfloat fv[2] = { 0 }; gl->fGetFloatv(pname, fv); - JSObject* obj = Float32Array::Create(cx, 2, fv); + JSObject* obj = Float32Array::Create(cx, this, 2, fv); if (!obj) { rv = NS_ERROR_OUT_OF_MEMORY; } @@ -2536,7 +2536,7 @@ WebGLContext::GetParameter(JSContext* cx, WebGLenum pname, ErrorResult& rv) { GLfloat fv[4] = { 0 }; gl->fGetFloatv(pname, fv); - JSObject* obj = Float32Array::Create(cx, 4, fv); + JSObject* obj = Float32Array::Create(cx, this, 4, fv); if (!obj) { rv = NS_ERROR_OUT_OF_MEMORY; } @@ -2547,7 +2547,7 @@ WebGLContext::GetParameter(JSContext* cx, WebGLenum pname, ErrorResult& rv) { GLint iv[2] = { 0 }; gl->fGetIntegerv(pname, iv); - JSObject* obj = Int32Array::Create(cx, 2, iv); + JSObject* obj = Int32Array::Create(cx, this, 2, iv); if (!obj) { rv = NS_ERROR_OUT_OF_MEMORY; } @@ -2559,7 +2559,7 @@ WebGLContext::GetParameter(JSContext* cx, WebGLenum pname, ErrorResult& rv) { GLint iv[4] = { 0 }; gl->fGetIntegerv(pname, iv); - JSObject* obj = Int32Array::Create(cx, 4, iv); + JSObject* obj = Int32Array::Create(cx, this, 4, iv); if (!obj) { rv = NS_ERROR_OUT_OF_MEMORY; } @@ -3323,7 +3323,7 @@ WebGLContext::GetUniform(JSContext* cx, WebGLProgram *prog, if (unitSize == 1) { return JS::DoubleValue(fv[0]); } else { - JSObject* obj = Float32Array::Create(cx, unitSize, fv); + JSObject* obj = Float32Array::Create(cx, this, unitSize, fv); if (!obj) { rv.Throw(NS_ERROR_OUT_OF_MEMORY); } @@ -3335,7 +3335,7 @@ WebGLContext::GetUniform(JSContext* cx, WebGLProgram *prog, if (unitSize == 1) { return JS::Int32Value(iv[0]); } else { - JSObject* obj = Int32Array::Create(cx, unitSize, iv); + JSObject* obj = Int32Array::Create(cx, this, unitSize, iv); if (!obj) { rv.Throw(NS_ERROR_OUT_OF_MEMORY); } @@ -3462,7 +3462,7 @@ WebGLContext::GetVertexAttrib(JSContext* cx, WebGLuint index, WebGLenum pname, vec[2] = mVertexAttrib0Vector[2]; vec[3] = mVertexAttrib0Vector[3]; } - JSObject* obj = Float32Array::Create(cx, 4, vec); + JSObject* obj = Float32Array::Create(cx, this, 4, vec); if (!obj) { rv.Throw(NS_ERROR_OUT_OF_MEMORY); } diff --git a/content/events/src/nsDOMDataTransfer.cpp b/content/events/src/nsDOMDataTransfer.cpp index f11597332334..04eaae7c4484 100644 --- a/content/events/src/nsDOMDataTransfer.cpp +++ b/content/events/src/nsDOMDataTransfer.cpp @@ -627,8 +627,11 @@ nsDOMDataTransfer::Clone(PRUint32 aEventType, bool aUserCancelled, } void -nsDOMDataTransfer::GetTransferables(nsISupportsArray** aArray) +nsDOMDataTransfer::GetTransferables(nsISupportsArray** aArray, + nsIDOMNode* aDragTarget) { + MOZ_ASSERT(aDragTarget); + *aArray = nsnull; nsCOMPtr transArray = @@ -636,6 +639,14 @@ nsDOMDataTransfer::GetTransferables(nsISupportsArray** aArray) if (!transArray) return; + nsCOMPtr dragNode = do_QueryInterface(aDragTarget); + if (!dragNode) + return; + nsIDocument* doc = dragNode->GetCurrentDoc(); + if (!doc) + return; + nsILoadContext* loadContext = doc->GetLoadContext(); + bool added = false; PRUint32 count = mItems.Length(); for (PRUint32 i = 0; i < count; i++) { @@ -649,6 +660,7 @@ nsDOMDataTransfer::GetTransferables(nsISupportsArray** aArray) do_CreateInstance("@mozilla.org/widget/transferable;1"); if (!transferable) return; + transferable->Init(loadContext); for (PRUint32 f = 0; f < count; f++) { TransferItem& formatitem = item[f]; diff --git a/content/events/src/nsDOMDataTransfer.h b/content/events/src/nsDOMDataTransfer.h index 4249368ca77b..d946351d8be1 100644 --- a/content/events/src/nsDOMDataTransfer.h +++ b/content/events/src/nsDOMDataTransfer.h @@ -97,7 +97,8 @@ public: // converts the data into an array of nsITransferable objects to be used for // drag and drop or clipboard operations. - void GetTransferables(nsISupportsArray** transferables); + void GetTransferables(nsISupportsArray** transferables, + nsIDOMNode* aDragTarget); // converts the data in the variant to an nsISupportString if possible or // an nsISupports or null otherwise. diff --git a/content/events/src/nsEventStateManager.cpp b/content/events/src/nsEventStateManager.cpp index 85902028454b..387eeb343d26 100644 --- a/content/events/src/nsEventStateManager.cpp +++ b/content/events/src/nsEventStateManager.cpp @@ -2317,7 +2317,7 @@ nsEventStateManager::DoDefaultDragStart(nsPresContext* aPresContext, nsIDOMElement* dragImage = aDataTransfer->GetDragImage(&imageX, &imageY); nsCOMPtr transArray; - aDataTransfer->GetTransferables(getter_AddRefs(transArray)); + aDataTransfer->GetTransferables(getter_AddRefs(transArray), dragTarget); if (!transArray) return false; diff --git a/content/html/content/src/nsGenericHTMLElement.cpp b/content/html/content/src/nsGenericHTMLElement.cpp index 00dbc5641a77..fa7e7266b4aa 100644 --- a/content/html/content/src/nsGenericHTMLElement.cpp +++ b/content/html/content/src/nsGenericHTMLElement.cpp @@ -4286,3 +4286,41 @@ nsGenericHTMLElement::GetProperties(nsIDOMHTMLPropertiesCollection** aReturn) return NS_OK; } +nsSize +nsGenericHTMLElement::GetWidthHeightForImage(imgIRequest *aImageRequest) +{ + nsSize size(0,0); + + nsIFrame* frame = GetPrimaryFrame(Flush_Layout); + + if (frame) { + size = frame->GetContentRect().Size(); + + size.width = nsPresContext::AppUnitsToIntCSSPixels(size.width); + size.height = nsPresContext::AppUnitsToIntCSSPixels(size.height); + } else { + const nsAttrValue* value; + nsCOMPtr image; + if (aImageRequest) { + aImageRequest->GetImage(getter_AddRefs(image)); + } + + if ((value = GetParsedAttr(nsGkAtoms::width)) && + value->Type() == nsAttrValue::eInteger) { + size.width = value->GetIntegerValue(); + } else if (image) { + image->GetWidth(&size.width); + } + + if ((value = GetParsedAttr(nsGkAtoms::height)) && + value->Type() == nsAttrValue::eInteger) { + size.height = value->GetIntegerValue(); + } else if (image) { + image->GetHeight(&size.height); + } + } + + NS_ASSERTION(size.width >= 0, "negative width"); + NS_ASSERTION(size.height >= 0, "negative height"); + return size; +} diff --git a/content/html/content/src/nsGenericHTMLElement.h b/content/html/content/src/nsGenericHTMLElement.h index 015d37ef794a..844cd5c45965 100644 --- a/content/html/content/src/nsGenericHTMLElement.h +++ b/content/html/content/src/nsGenericHTMLElement.h @@ -147,6 +147,11 @@ public: nsresult ClearDataset(); nsresult GetContextMenu(nsIDOMHTMLMenuElement** aContextMenu); + /** + * Get width and height, using given image request if attributes are unset. + */ + nsSize GetWidthHeightForImage(imgIRequest *aImageRequest); + protected: nsresult GetMarkup(bool aIncludeSelf, nsAString& aMarkup); diff --git a/content/html/content/src/nsHTMLImageElement.cpp b/content/html/content/src/nsHTMLImageElement.cpp index 812334e48de6..cc611afd3c45 100644 --- a/content/html/content/src/nsHTMLImageElement.cpp +++ b/content/html/content/src/nsHTMLImageElement.cpp @@ -196,49 +196,10 @@ nsHTMLImageElement::GetY(PRInt32* aY) return NS_OK; } -nsSize -nsHTMLImageElement::GetWidthHeight() -{ - nsSize size(0,0); - - nsIFrame* frame = GetPrimaryFrame(Flush_Layout); - - if (frame) { - size = frame->GetContentRect().Size(); - - size.width = nsPresContext::AppUnitsToIntCSSPixels(size.width); - size.height = nsPresContext::AppUnitsToIntCSSPixels(size.height); - } else { - const nsAttrValue* value; - nsCOMPtr image; - if (mCurrentRequest) { - mCurrentRequest->GetImage(getter_AddRefs(image)); - } - - if ((value = GetParsedAttr(nsGkAtoms::width)) && - value->Type() == nsAttrValue::eInteger) { - size.width = value->GetIntegerValue(); - } else if (image) { - image->GetWidth(&size.width); - } - - if ((value = GetParsedAttr(nsGkAtoms::height)) && - value->Type() == nsAttrValue::eInteger) { - size.height = value->GetIntegerValue(); - } else if (image) { - image->GetHeight(&size.height); - } - } - - NS_ASSERTION(size.width >= 0, "negative width"); - NS_ASSERTION(size.height >= 0, "negative height"); - return size; -} - NS_IMETHODIMP nsHTMLImageElement::GetHeight(PRUint32* aHeight) { - *aHeight = GetWidthHeight().height; + *aHeight = GetWidthHeightForImage(mCurrentRequest).height; return NS_OK; } @@ -246,17 +207,13 @@ nsHTMLImageElement::GetHeight(PRUint32* aHeight) NS_IMETHODIMP nsHTMLImageElement::SetHeight(PRUint32 aHeight) { - nsAutoString val; - val.AppendInt(aHeight); - - return nsGenericHTMLElement::SetAttr(kNameSpaceID_None, nsGkAtoms::height, - val, true); + return nsGenericHTMLElement::SetUnsignedIntAttr(nsGkAtoms::height, aHeight); } NS_IMETHODIMP nsHTMLImageElement::GetWidth(PRUint32* aWidth) { - *aWidth = GetWidthHeight().width; + *aWidth = GetWidthHeightForImage(mCurrentRequest).width; return NS_OK; } @@ -264,11 +221,7 @@ nsHTMLImageElement::GetWidth(PRUint32* aWidth) NS_IMETHODIMP nsHTMLImageElement::SetWidth(PRUint32 aWidth) { - nsAutoString val; - val.AppendInt(aWidth); - - return nsGenericHTMLElement::SetAttr(kNameSpaceID_None, nsGkAtoms::width, - val, true); + return nsGenericHTMLElement::SetUnsignedIntAttr(nsGkAtoms::width, aWidth); } bool diff --git a/content/html/content/src/nsHTMLImageElement.h b/content/html/content/src/nsHTMLImageElement.h index 99c0a00a31a9..6aff140b5ed1 100644 --- a/content/html/content/src/nsHTMLImageElement.h +++ b/content/html/content/src/nsHTMLImageElement.h @@ -102,7 +102,6 @@ public: virtual nsIDOMNode* AsDOMNode() { return this; } protected: nsIntPoint GetXY(); - nsSize GetWidthHeight(); virtual void GetItemValueText(nsAString& text); virtual void SetItemValueText(const nsAString& text); }; diff --git a/content/html/content/src/nsHTMLInputElement.cpp b/content/html/content/src/nsHTMLInputElement.cpp index 9c5021ca75ea..8aa443f316ff 100644 --- a/content/html/content/src/nsHTMLInputElement.cpp +++ b/content/html/content/src/nsHTMLInputElement.cpp @@ -858,6 +858,19 @@ NS_IMPL_STRING_ATTR(nsHTMLInputElement, Placeholder, placeholder) NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsHTMLInputElement, Type, type, kInputDefaultType->tag) +NS_IMETHODIMP +nsHTMLInputElement::GetHeight(PRUint32 *aHeight) +{ + *aHeight = GetWidthHeightForImage(mCurrentRequest).height; + return NS_OK; +} + +NS_IMETHODIMP +nsHTMLInputElement::SetHeight(PRUint32 aHeight) +{ + return nsGenericHTMLElement::SetUnsignedIntAttr(nsGkAtoms::height, aHeight); +} + NS_IMETHODIMP nsHTMLInputElement::GetIndeterminate(bool* aValue) { @@ -889,6 +902,19 @@ nsHTMLInputElement::SetIndeterminate(bool aValue) return SetIndeterminateInternal(aValue, true); } +NS_IMETHODIMP +nsHTMLInputElement::GetWidth(PRUint32 *aWidth) +{ + *aWidth = GetWidthHeightForImage(mCurrentRequest).width; + return NS_OK; +} + +NS_IMETHODIMP +nsHTMLInputElement::SetWidth(PRUint32 aWidth) +{ + return nsGenericHTMLElement::SetUnsignedIntAttr(nsGkAtoms::width, aWidth); +} + NS_IMETHODIMP nsHTMLInputElement::GetValue(nsAString& aValue) { diff --git a/content/html/content/test/forms/test_input_attributes_reflection.html b/content/html/content/test/forms/test_input_attributes_reflection.html index dfa7e271c507..b15f305ef708 100644 --- a/content/html/content/test/forms/test_input_attributes_reflection.html +++ b/content/html/content/test/forms/test_input_attributes_reflection.html @@ -99,7 +99,12 @@ reflectString({ otherValues: [ "_blank", "_self", "_parent", "_top" ], }); -// TODO: height (non-negative integer) +// .height +reflectUnsignedInt({ + element: document.createElement("input"), + attribute: "height", + nonZero: false +}); // .indeterminate doesn't reflect a content attribute. @@ -204,7 +209,12 @@ todo("valueAsNumber" in document.createElement("input"), todo("selectedOption" in document.createElement("input"), "selectedOption isn't implemented yet"); -// TODO: width (non-negative integer) +// .width +reflectUnsignedInt({ + element: document.createElement("input"), + attribute: "width", + nonZero: false +}); // .willValidate doesn't reflect a content attribute. // .validity doesn't reflect a content attribute. diff --git a/content/media/test/seek_with_sound.ogg b/content/media/test/seek_with_sound.ogg new file mode 100644 index 000000000000..c86d9946bddc Binary files /dev/null and b/content/media/test/seek_with_sound.ogg differ diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 75c3ee515df8..2a931cebfb9a 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -470,12 +470,7 @@ #include "nsHTMLSelectElement.h" #include "nsHTMLLegendElement.h" -#include "DOMSVGLengthList.h" -#include "DOMSVGNumberList.h" -#include "DOMSVGPathSegList.h" -#include "DOMSVGPointList.h" #include "DOMSVGStringList.h" -#include "DOMSVGTransformList.h" #include "mozilla/dom/indexedDB/IDBWrapperCache.h" #include "mozilla/dom/indexedDB/IDBFactory.h" @@ -780,10 +775,10 @@ static nsDOMClassInfoData sClassInfoData[] = { DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(DOMException, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(DOMTokenList, nsDOMTokenListSH, - ARRAY_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(DOMSettableTokenList, nsDOMTokenListSH, - ARRAY_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(DOMTokenList, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(DOMSettableTokenList, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(DocumentFragment, nsNodeSH, NODE_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(Element, nsElementSH, ELEMENT_SCRIPTABLE_FLAGS) @@ -1262,14 +1257,14 @@ static nsDOMClassInfoData sClassInfoData[] = { DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(SVGLength, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(SVGLengthList, nsSVGLengthListSH, - ARRAY_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(SVGLengthList, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(SVGMatrix, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(SVGNumber, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(SVGNumberList, nsSVGNumberListSH, - ARRAY_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(SVGNumberList, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(SVGPathSegArcAbs, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(SVGPathSegArcRel, nsDOMGenericSH, @@ -1304,16 +1299,16 @@ static nsDOMClassInfoData sClassInfoData[] = { DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(SVGPathSegLinetoVerticalRel, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(SVGPathSegList, nsSVGPathSegListSH, - ARRAY_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(SVGPathSegList, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(SVGPathSegMovetoAbs, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(SVGPathSegMovetoRel, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(SVGPoint, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(SVGPointList, nsSVGPointListSH, - ARRAY_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(SVGPointList, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(SVGPreserveAspectRatio, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(SVGRect, nsDOMGenericSH, @@ -1322,8 +1317,8 @@ static nsDOMClassInfoData sClassInfoData[] = { ARRAY_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(SVGTransform, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(SVGTransformList, nsSVGTransformListSH, - ARRAY_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(SVGTransformList, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(SVGZoomEvent, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) @@ -1397,8 +1392,8 @@ static nsDOMClassInfoData sClassInfoData[] = { NS_DEFINE_CLASSINFO_DATA(ClientRect, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(ClientRectList, nsClientRectListSH, - ARRAY_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(ClientRectList, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(SVGForeignObjectElement, nsElementSH, ELEMENT_SCRIPTABLE_FLAGS) @@ -1411,8 +1406,8 @@ static nsDOMClassInfoData sClassInfoData[] = { NS_DEFINE_CLASSINFO_DATA(OfflineResourceList, nsOfflineResourceListSH, ARRAY_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(FileList, nsFileListSH, - ARRAY_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(FileList, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(Blob, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(File, nsDOMGenericSH, @@ -1578,8 +1573,8 @@ static nsDOMClassInfoData sClassInfoData[] = { NS_DEFINE_CLASSINFO_DATA(PaintRequest, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(PaintRequestList, nsPaintRequestListSH, - ARRAY_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(PaintRequestList, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(ScrollAreaEvent, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) @@ -1637,8 +1632,8 @@ static nsDOMClassInfoData sClassInfoData[] = { NS_DEFINE_CLASSINFO_DATA(Touch, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(TouchList, nsDOMTouchListSH, - ARRAY_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(TouchList, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(TouchEvent, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) @@ -8351,27 +8346,6 @@ nsStringListSH::GetStringAt(nsISupports *aNative, PRInt32 aIndex, } -// DOMTokenList scriptable helper - -nsresult -nsDOMTokenListSH::GetStringAt(nsISupports *aNative, PRInt32 aIndex, - nsAString& aResult) -{ - nsCOMPtr list(do_QueryInterface(aNative)); - NS_ENSURE_TRUE(list, NS_ERROR_UNEXPECTED); - - nsresult rv = list->Item(aIndex, aResult); -#ifdef DEBUG - if (DOMStringIsNull(aResult)) { - PRUint32 length = 0; - list->GetLength(&length); - NS_ASSERTION(PRUint32(aIndex) >= length, "Item should only return null for out-of-bounds access"); - } -#endif - return rv; -} - - // Named Array helper NS_IMETHODIMP @@ -10287,36 +10261,6 @@ nsCSSRuleListSH::GetItemAt(nsISupports *aNative, PRUint32 aIndex, return list->GetItemAt(aIndex, aResult); } -// ClientRectList scriptable helper - -nsISupports* -nsClientRectListSH::GetItemAt(nsISupports *aNative, PRUint32 aIndex, - nsWrapperCache **aCache, nsresult *aResult) -{ - nsClientRectList* list = nsClientRectList::FromSupports(aNative); - - return list->GetItemAt(aIndex); -} - -// PaintRequestList scriptable helper - -nsISupports* -nsPaintRequestListSH::GetItemAt(nsISupports *aNative, PRUint32 aIndex, - nsWrapperCache **aCache, nsresult *aResult) -{ - nsPaintRequestList* list = nsPaintRequestList::FromSupports(aNative); - - return list->GetItemAt(aIndex); -} - -nsISupports* -nsDOMTouchListSH::GetItemAt(nsISupports *aNative, PRUint32 aIndex, - nsWrapperCache **aCache, nsresult *aResult) -{ - nsDOMTouchList* list = static_cast(aNative); - return list->GetItemAt(aIndex); -} - #ifdef MOZ_XUL // TreeColumns helper @@ -10755,39 +10699,6 @@ nsOfflineResourceListSH::GetStringAt(nsISupports *aNative, PRInt32 aIndex, return rv; } -// nsFileListSH -nsISupports* -nsFileListSH::GetItemAt(nsISupports *aNative, PRUint32 aIndex, - nsWrapperCache **aCache, nsresult *aResult) -{ - nsDOMFileList* list = nsDOMFileList::FromSupports(aNative); - - return list->GetItemAt(aIndex); -} - -// Template for SVGXXXList helpers -template nsISupports* -nsSVGListSH::GetItemAt(nsISupports *aNative, - PRUint32 aIndex, - nsWrapperCache **aCache, - nsresult *aResult) -{ - ListType* list = static_cast(static_cast(aNative)); -#ifdef DEBUG - { - nsCOMPtr list_qi = do_QueryInterface(aNative); - - // If this assertion fires the QI implementation for the object in - // question doesn't use the nsIDOMSVGXXXList pointer as the nsISupports - // pointer. That must be fixed, or we'll crash... - NS_ABORT_IF_FALSE(list_qi == list, "Uh, fix QI!"); - } -#endif - - return list->GetItemAt(aIndex); -} - - // SVGStringList helper nsresult diff --git a/dom/base/nsDOMClassInfo.h b/dom/base/nsDOMClassInfo.h index cf0bda03ec72..ab87d49fbe28 100644 --- a/dom/base/nsDOMClassInfo.h +++ b/dom/base/nsDOMClassInfo.h @@ -1088,31 +1088,6 @@ public: }; -// DOMTokenList scriptable helper - -class nsDOMTokenListSH : public nsStringArraySH -{ -protected: - nsDOMTokenListSH(nsDOMClassInfoData* aData) : nsStringArraySH(aData) - { - } - - virtual ~nsDOMTokenListSH() - { - } - - virtual nsresult GetStringAt(nsISupports *aNative, PRInt32 aIndex, - nsAString& aResult); - -public: - - static nsIClassInfo *doCreate(nsDOMClassInfoData* aData) - { - return new nsDOMTokenListSH(aData); - } -}; - - // MediaList helper class nsMediaListSH : public nsStringArraySH @@ -1235,74 +1210,6 @@ public: } }; -// ClientRectList helper - -class nsClientRectListSH : public nsArraySH -{ -protected: - nsClientRectListSH(nsDOMClassInfoData* aData) : nsArraySH(aData) - { - } - - virtual ~nsClientRectListSH() - { - } - - virtual nsISupports* GetItemAt(nsISupports *aNative, PRUint32 aIndex, - nsWrapperCache **aCache, nsresult *aResult); - -public: - static nsIClassInfo *doCreate(nsDOMClassInfoData* aData) - { - return new nsClientRectListSH(aData); - } -}; - - -// PaintRequestList helper - -class nsPaintRequestListSH : public nsArraySH -{ -protected: - nsPaintRequestListSH(nsDOMClassInfoData* aData) : nsArraySH(aData) - { - } - - virtual ~nsPaintRequestListSH() - { - } - - virtual nsISupports* GetItemAt(nsISupports *aNative, PRUint32 aIndex, - nsWrapperCache **aCache, nsresult *aResult); - -public: - static nsIClassInfo *doCreate(nsDOMClassInfoData* aData) - { - return new nsPaintRequestListSH(aData); - } -}; - -class nsDOMTouchListSH : public nsArraySH -{ -protected: - nsDOMTouchListSH(nsDOMClassInfoData* aData) : nsArraySH(aData) - { - } - - virtual ~nsDOMTouchListSH() - { - } - - virtual nsISupports* GetItemAt(nsISupports *aNative, PRUint32 aIndex, - nsWrapperCache **aCache, nsresult *aResult); - -public: - static nsIClassInfo* doCreate(nsDOMClassInfoData* aData) - { - return new nsDOMTouchListSH(aData); - } -}; - #ifdef MOZ_XUL // TreeColumns helper @@ -1507,53 +1414,6 @@ public: } }; -class nsFileListSH : public nsArraySH -{ -protected: - nsFileListSH(nsDOMClassInfoData *aData) : nsArraySH(aData) - { - } - - virtual ~nsFileListSH() - { - } - - virtual nsISupports* GetItemAt(nsISupports *aNative, PRUint32 aIndex, - nsWrapperCache **aCache, nsresult *aResult); - -public: - static nsIClassInfo *doCreate(nsDOMClassInfoData* aData) - { - return new nsFileListSH(aData); - } -}; - -// Template for SVGXXXList helpers - -template -class nsSVGListSH : public nsArraySH -{ -protected: - nsSVGListSH(nsDOMClassInfoData* aData) : nsArraySH(aData) - { - } - -public: - virtual nsISupports* GetItemAt(nsISupports *aNative, PRUint32 aIndex, - nsWrapperCache **aCache, nsresult *aResult); - - static nsIClassInfo *doCreate(nsDOMClassInfoData* aData) - { - return new nsSVGListSH(aData); - } -}; - -typedef nsSVGListSH nsSVGLengthListSH; -typedef nsSVGListSH nsSVGNumberListSH; -typedef nsSVGListSH nsSVGPathSegListSH; -typedef nsSVGListSH nsSVGPointListSH; -typedef nsSVGListSH nsSVGTransformListSH; - // SVGStringList helper class nsSVGStringListSH : public nsStringArraySH diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 4c401d82d285..af159ec80d02 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -50,7 +50,7 @@ #include "nsIMarkupDocumentViewer.h" #include "nsClientRect.h" -#if defined(MOZ_X11) && defined(MOZ_WIDGET_GTK2) +#if defined(MOZ_X11) && defined(MOZ_WIDGET_GTK) #include #include #endif @@ -213,8 +213,8 @@ nsDOMWindowUtils::Redraw(PRUint32 aCount, PRUint32 *aDurationOut) for (PRUint32 i = 0; i < aCount; i++) rootFrame->InvalidateWithFlags(r, nsIFrame::INVALIDATE_IMMEDIATE); -#if defined(MOZ_X11) && defined(MOZ_WIDGET_GTK2) - XSync(GDK_DISPLAY(), False); +#if defined(MOZ_X11) && defined(MOZ_WIDGET_GTK) + XSync(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), False); #endif *aDurationOut = PR_IntervalToMilliseconds(PR_IntervalNow() - iStart); diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index de18e6b9ae16..0b77d008697f 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -340,6 +340,7 @@ public: virtual NS_HIDDEN_(bool) DispatchCustomEvent(const char *aEventName); virtual NS_HIDDEN_(void) RefreshCompartmentPrincipal(); virtual NS_HIDDEN_(nsresult) SetFullScreenInternal(bool aIsFullScreen, bool aRequireTrust); + virtual NS_HIDDEN_(bool) IsPartOfApp(); // nsIDOMStorageIndexedDB NS_DECL_NSIDOMSTORAGEINDEXEDDB @@ -545,13 +546,6 @@ public: void AddEventTargetObject(nsDOMEventTargetHelper* aObject); void RemoveEventTargetObject(nsDOMEventTargetHelper* aObject); - /** - * Returns if the window is part of an application. - * It will check for the window app state and its parents until a window has - * an app state different from |TriState_Unknown|. - */ - bool IsPartOfApp(); - protected: friend class HashchangeCallback; friend class nsBarProp; diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 3c02c24da634..0dcdebb50a5a 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -2960,10 +2960,70 @@ nsJSContext::ShrinkGCBuffersNow() JS_ShrinkGCBuffers(nsJSRuntime::sRuntime); } -//Static +// Return true if any JSContext has a "global object" with a gray +// parent. The intent is to look for JS Object windows. We don't merge +// system compartments, so we don't use them to trigger merging CCs. +static bool +AnyGrayGlobalParent() +{ + if (!nsJSRuntime::sRuntime) { + return false; + } + JSContext *iter = nsnull; + JSContext *cx; + while ((cx = JS_ContextIterator(nsJSRuntime::sRuntime, &iter))) { + if (JSObject *global = JS_GetGlobalObject(cx)) { + if (JSObject *parent = js::GetObjectParent(global)) { + if (js::GCThingIsMarkedGray(parent) && + !js::IsSystemCompartment(js::GetGCThingCompartment(parent))) { + return true; + } + } + } + } + return false; +} + +static bool +DoMergingCC(bool aForced) +{ + // Don't merge too many times in a row, and do at least a minimum + // number of unmerged CCs in a row. + static const PRInt32 kMinConsecutiveUnmerged = 3; + static const PRInt32 kMaxConsecutiveMerged = 3; + + static PRInt32 sUnmergedNeeded = 0; + static PRInt32 sMergedInARow = 0; + + MOZ_ASSERT(0 <= sUnmergedNeeded <= kMinConsecutiveUnmerged); + MOZ_ASSERT(0 <= sMergedInARow <= kMaxConsecutiveMerged); + + if (sMergedInARow == kMaxConsecutiveMerged) { + MOZ_ASSERT(sUnmergedNeeded == 0); + sUnmergedNeeded = kMinConsecutiveUnmerged; + } + + if (sUnmergedNeeded > 0) { + sUnmergedNeeded--; + sMergedInARow = 0; + return false; + } + + if (!aForced && AnyGrayGlobalParent()) { + sMergedInARow++; + return true; + } else { + sMergedInARow = 0; + return false; + } + +} + +//static void nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener, - PRInt32 aExtraForgetSkippableCalls) + PRInt32 aExtraForgetSkippableCalls, + bool aForced) { if (!NS_IsMainThread()) { return; @@ -2995,8 +3055,10 @@ nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener, ++sCleanupsSinceLastGC; } + bool mergingCC = DoMergingCC(aForced); + nsCycleCollectorResults ccResults; - nsCycleCollector_collect(&ccResults, aListener); + nsCycleCollector_collect(mergingCC, &ccResults, aListener); sCCollectedWaitingForGC += ccResults.mFreedRefCounted + ccResults.mFreedGCed; // If we collected a substantial amount of cycles, poke the GC since more objects @@ -3024,13 +3086,18 @@ nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener, sFirstCollectionTime = now; } + nsCString mergeMsg; + if (mergingCC) { + mergeMsg.AssignLiteral(" merged"); + } + nsCString gcMsg; if (ccResults.mForcedGC) { gcMsg.AssignLiteral(", forced a GC"); } NS_NAMED_MULTILINE_LITERAL_STRING(kFmt, - NS_LL("CC(T+%.1f) duration: %llums, suspected: %lu, visited: %lu RCed and %lu GCed, collected: %lu RCed and %lu GCed (%lu waiting for GC)%s\n") + NS_LL("CC(T+%.1f) duration: %llums, suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu RCed and %lu GCed (%lu waiting for GC)%s\n") NS_LL("ForgetSkippable %lu times before CC, min: %lu ms, max: %lu ms, avg: %lu ms, total: %lu ms, removed: %lu")); nsString msg; PRUint32 cleanups = sForgetSkippableBeforeCC ? sForgetSkippableBeforeCC : 1; @@ -3038,7 +3105,7 @@ nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener, ? 0 : sMinForgetSkippableTime; msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), double(delta) / PR_USEC_PER_SEC, (now - start) / PR_USEC_PER_MSEC, suspected, - ccResults.mVisitedRefCounted, ccResults.mVisitedGCed, + ccResults.mVisitedRefCounted, ccResults.mVisitedGCed, mergeMsg.get(), ccResults.mFreedRefCounted, ccResults.mFreedGCed, sCCollectedWaitingForGC, gcMsg.get(), sForgetSkippableBeforeCC, @@ -3191,7 +3258,7 @@ CCTimerFired(nsITimer *aTimer, void *aClosure) } else { // We are in the final timer fire and still meet the conditions for // triggering a CC. - nsJSContext::CycleCollectNow(); + nsJSContext::CycleCollectNow(nsnull, 0, false); } } else if ((sPreviousSuspectedCount + 100) <= suspected) { // Only do a forget skippable if there are more than a few new objects. diff --git a/dom/base/nsJSEnvironment.h b/dom/base/nsJSEnvironment.h index 073beed81fbc..a1bc0423889f 100644 --- a/dom/base/nsJSEnvironment.h +++ b/dom/base/nsJSEnvironment.h @@ -153,7 +153,8 @@ public: // If aExtraForgetSkippableCalls is -1, forgetSkippable won't be // called even if the previous collection was GC. static void CycleCollectNow(nsICycleCollectorListener *aListener = nsnull, - PRInt32 aExtraForgetSkippableCalls = 0); + PRInt32 aExtraForgetSkippableCalls = 0, + bool aForced = true); static void PokeGC(js::gcreason::Reason aReason, int aDelay = 0); static void KillGCTimer(); diff --git a/dom/base/nsPIDOMWindow.h b/dom/base/nsPIDOMWindow.h index 1b39640cae92..6a21b4b4c4f1 100644 --- a/dom/base/nsPIDOMWindow.h +++ b/dom/base/nsPIDOMWindow.h @@ -592,6 +592,13 @@ public: */ virtual void RefreshCompartmentPrincipal() = 0; + /** + * Returns if the window is part of an application. + * It will check for the window app state and its parents until a window has + * an app state different from |TriState_Unknown|. + */ + virtual bool IsPartOfApp() = 0; + protected: // The nsPIDOMWindow constructor. The aOuterWindow argument should // be null if and only if the created window itself is an outer diff --git a/dom/bindings/TypedArray.h b/dom/bindings/TypedArray.h index e1d4a8d78c87..b2fdffaf1ab9 100644 --- a/dom/bindings/TypedArray.h +++ b/dom/bindings/TypedArray.h @@ -44,7 +44,15 @@ struct TypedArray : public TypedArray_base { {} static inline JSObject* - Create(JSContext* cx, uint32_t length, T* data = NULL) { + Create(JSContext* cx, nsWrapperCache* creator, uint32_t length, + T* data = NULL) { + JSObject* creatorWrapper; + JSAutoEnterCompartment ac; + if (creator && (creatorWrapper = creator->GetWrapperPreserveColor())) { + if (!ac.enter(cx, creatorWrapper)) { + return NULL; + } + } JSObject* obj = CreateNew(cx, length); if (!obj) { return NULL; @@ -64,7 +72,7 @@ typedef TypedArray Uint8Array; typedef TypedArray + JS_GetTypedArrayLength, JS_NewUint8ClampedArray> Uint8ClampedArray; typedef TypedArray diff --git a/dom/devicestorage/Makefile.in b/dom/devicestorage/Makefile.in index be8b0f586607..23bce76d9daf 100644 --- a/dom/devicestorage/Makefile.in +++ b/dom/devicestorage/Makefile.in @@ -32,6 +32,8 @@ LOCAL_INCLUDES = \ -I$(topsrcdir)/content/events/src \ $(NULL) +TEST_DIRS += test + include $(topsrcdir)/config/config.mk include $(topsrcdir)/ipc/chromium/chromium-config.mk include $(topsrcdir)/config/rules.mk diff --git a/dom/devicestorage/nsDeviceStorage.cpp b/dom/devicestorage/nsDeviceStorage.cpp index 5a9c20b36256..2a877c60e542 100644 --- a/dom/devicestorage/nsDeviceStorage.cpp +++ b/dom/devicestorage/nsDeviceStorage.cpp @@ -28,9 +28,11 @@ public: nsCOMPtr mFile; nsString mPath; + bool mEditable; DeviceStorageFile(nsIFile* aFile, const nsAString& aPath) : mPath(aPath) + , mEditable(false) { NS_ASSERTION(aFile, "Must not create a DeviceStorageFile with a null nsIFile"); // always take a clone @@ -43,6 +45,7 @@ public: } DeviceStorageFile(nsIFile* aFile) + : mEditable(false) { NS_ASSERTION(aFile, "Must not create a DeviceStorageFile with a null nsIFile"); // always take a clone @@ -56,6 +59,11 @@ public: NormalizeFilePath(); } + void + setEditable(bool aEditable) { + mEditable = aEditable; + } + NS_DECL_ISUPPORTS // we want to make sure that the names of file can't reach @@ -220,12 +228,12 @@ nsDOMDeviceStorage::SetRootFileForType(const nsAString& aType, const PRInt32 aIn return typeResult; } -static jsval nsIFileToJsval(nsPIDOMWindow* aWindow, DeviceStorageFile* aFile, bool aEditable) +static jsval nsIFileToJsval(nsPIDOMWindow* aWindow, DeviceStorageFile* aFile) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(aWindow, "Null Window"); - if (aEditable) { + if (aFile->mEditable) { // TODO - needs janv's file handle support. return JSVAL_NULL; } @@ -309,7 +317,6 @@ public: nsDOMDeviceStorageCursor(nsIDOMWindow* aWindow, nsIURI* aURI, DeviceStorageFile* aFile, - bool aEditable, PRUint64 aSince); private: @@ -321,7 +328,6 @@ protected: bool mOkToCallContinue; nsRefPtr mFile; nsCOMPtr mURI; - bool mEditable; PRUint64 mSince; // to access mFiles @@ -453,7 +459,7 @@ public: else { nsRefPtr file = cursor->mFiles[0]; cursor->mFiles.RemoveElementAt(0); - val = nsIFileToJsval(cursor->GetOwner(), file, cursor->mEditable); + val = nsIFileToJsval(cursor->GetOwner(), file); cursor->mOkToCallContinue = true; } @@ -578,13 +584,11 @@ NS_IMPL_RELEASE_INHERITED(nsDOMDeviceStorageCursor, DOMRequest) nsDOMDeviceStorageCursor::nsDOMDeviceStorageCursor(nsIDOMWindow* aWindow, nsIURI* aURI, DeviceStorageFile* aFile, - bool aEditable, PRUint64 aSince) : DOMRequest(aWindow) , mOkToCallContinue(false) , mFile(aFile) , mURI(aURI) - , mEditable(aEditable) , mSince(aSince) { } @@ -677,9 +681,8 @@ nsDOMDeviceStorageCursor::Continue() class PostResultEvent : public nsRunnable { public: - PostResultEvent(nsRefPtr& aRequest, bool aEditable, DeviceStorageFile* aFile) - : mEditable(aEditable) - , mFile(aFile) + PostResultEvent(nsRefPtr& aRequest, DeviceStorageFile* aFile) + : mFile(aFile) { mRequest.swap(aRequest); } @@ -698,7 +701,7 @@ public: jsval result = JSVAL_NULL; if (mFile) { - result = nsIFileToJsval(mRequest->GetOwner(), mFile, mEditable); + result = nsIFileToJsval(mRequest->GetOwner(), mFile); } else { result = StringToJsval(mRequest->GetOwner(), mPath); } @@ -709,7 +712,6 @@ public: } private: - bool mEditable; nsRefPtr mFile; nsString mPath; nsRefPtr mRequest; @@ -808,16 +810,12 @@ private: nsRefPtr mFile; nsRefPtr mRequest; }; - - class ReadFileEvent : public nsRunnable { public: ReadFileEvent(DeviceStorageFile* aFile, - bool aEditable, nsRefPtr& aRequest) : mFile(aFile) - , mEditable(aEditable) { mRequest.swap(aRequest); } @@ -830,7 +828,7 @@ public: nsRefPtr r; - if (!mEditable) { + if (!mFile->mEditable) { bool check = false; mFile->mFile->Exists(&check); if (!check) { @@ -839,7 +837,7 @@ public: } if (!r) { - r = new PostResultEvent(mRequest, mEditable, mFile); + r = new PostResultEvent(mRequest, mFile); } NS_DispatchToMainThread(r); return NS_OK; @@ -847,7 +845,6 @@ public: private: nsRefPtr mFile; - bool mEditable; nsRefPtr mRequest; }; @@ -902,14 +899,12 @@ public: nsIURI *aURI, DeviceStorageFile *aFile, DOMRequest* aRequest, - bool aEditable, nsIDOMBlob *aBlob = nsnull) : mRequestType(aRequestType) , mWindow(aWindow) , mURI(aURI) , mFile(aFile) , mRequest(aRequest) - , mEditable(aEditable) , mBlob(aBlob) {} NS_DECL_CYCLE_COLLECTING_ISUPPORTS @@ -982,7 +977,7 @@ public: } case DEVICE_STORAGE_REQUEST_READ: { - r = new ReadFileEvent(mFile, mEditable, mRequest); + r = new ReadFileEvent(mFile, mRequest); break; } case DEVICE_STORAGE_REQUEST_DELETE: @@ -1007,7 +1002,6 @@ private: nsRefPtr mFile; nsRefPtr mRequest; - bool mEditable; nsCOMPtr mBlob; }; @@ -1165,7 +1159,7 @@ nsDOMDeviceStorage::AddNamed(nsIDOMBlob *aBlob, } else { r = new DeviceStorageRequest(DeviceStorageRequest::DEVICE_STORAGE_REQUEST_WRITE, - win, mURI, dsf, request, true, aBlob); + win, mURI, dsf, request, aBlob); } NS_DispatchToMainThread(r); return NS_OK; @@ -1215,12 +1209,13 @@ nsDOMDeviceStorage::GetInternal(const JS::Value & aPath, } nsRefPtr dsf = new DeviceStorageFile(mFile, path); + dsf->setEditable(aEditable); if (!dsf->isSafePath()) { r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_FILE_NAME, dsf); } else { r = new DeviceStorageRequest(DeviceStorageRequest::DEVICE_STORAGE_REQUEST_READ, - win, mURI, dsf, request, aEditable); + win, mURI, dsf, request); } NS_DispatchToMainThread(r); return NS_OK; @@ -1255,7 +1250,7 @@ nsDOMDeviceStorage::Delete(const JS::Value & aPath, JSContext* aCx, nsIDOMDOMReq } else { r = new DeviceStorageRequest(DeviceStorageRequest::DEVICE_STORAGE_REQUEST_DELETE, - win, mURI, dsf, request, true); + win, mURI, dsf, request); } NS_DispatchToMainThread(r); return NS_OK; @@ -1336,7 +1331,9 @@ nsDOMDeviceStorage::EnumerateInternal(const JS::Value & aName, } nsRefPtr dsf = new DeviceStorageFile(mFile, path); - nsRefPtr cursor = new nsDOMDeviceStorageCursor(win, mURI, dsf, aEditable, since); + dsf->setEditable(aEditable); + + nsRefPtr cursor = new nsDOMDeviceStorageCursor(win, mURI, dsf, since); nsRefPtr r = new DeviceStorageCursorRequest(cursor); NS_ADDREF(*aRetval = cursor); diff --git a/dom/tests/mochitest/devicestorage/Makefile.in b/dom/devicestorage/test/Makefile.in similarity index 90% rename from dom/tests/mochitest/devicestorage/Makefile.in rename to dom/devicestorage/test/Makefile.in index 844f0f1e0621..7a2d5b843f3f 100644 --- a/dom/tests/mochitest/devicestorage/Makefile.in +++ b/dom/devicestorage/test/Makefile.in @@ -2,11 +2,11 @@ # 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 = ../../../.. +DEPTH = ../../.. topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ -relativesrcdir = dom/tests/mochitest/devicestorage +relativesrcdir = dom/devicestorage/test/ include $(DEPTH)/config/autoconf.mk diff --git a/dom/tests/mochitest/devicestorage/devicestorage_common.js b/dom/devicestorage/test/devicestorage_common.js similarity index 100% rename from dom/tests/mochitest/devicestorage/devicestorage_common.js rename to dom/devicestorage/test/devicestorage_common.js diff --git a/dom/tests/mochitest/devicestorage/test_basic.html b/dom/devicestorage/test/test_basic.html similarity index 100% rename from dom/tests/mochitest/devicestorage/test_basic.html rename to dom/devicestorage/test/test_basic.html diff --git a/dom/tests/mochitest/devicestorage/test_dotdot.html b/dom/devicestorage/test/test_dotdot.html similarity index 100% rename from dom/tests/mochitest/devicestorage/test_dotdot.html rename to dom/devicestorage/test/test_dotdot.html diff --git a/dom/tests/mochitest/devicestorage/test_enumerate.html b/dom/devicestorage/test/test_enumerate.html similarity index 100% rename from dom/tests/mochitest/devicestorage/test_enumerate.html rename to dom/devicestorage/test/test_enumerate.html diff --git a/dom/tests/mochitest/devicestorage/test_enumerateMultipleContinue.html b/dom/devicestorage/test/test_enumerateMultipleContinue.html similarity index 100% rename from dom/tests/mochitest/devicestorage/test_enumerateMultipleContinue.html rename to dom/devicestorage/test/test_enumerateMultipleContinue.html diff --git a/dom/tests/mochitest/devicestorage/test_enumerateNoParam.html b/dom/devicestorage/test/test_enumerateNoParam.html similarity index 100% rename from dom/tests/mochitest/devicestorage/test_enumerateNoParam.html rename to dom/devicestorage/test/test_enumerateNoParam.html diff --git a/dom/tests/mochitest/devicestorage/test_enumerateOptions.html b/dom/devicestorage/test/test_enumerateOptions.html similarity index 100% rename from dom/tests/mochitest/devicestorage/test_enumerateOptions.html rename to dom/devicestorage/test/test_enumerateOptions.html diff --git a/dom/tests/mochitest/devicestorage/test_lastModificationFilter.html b/dom/devicestorage/test/test_lastModificationFilter.html similarity index 100% rename from dom/tests/mochitest/devicestorage/test_lastModificationFilter.html rename to dom/devicestorage/test/test_lastModificationFilter.html diff --git a/dom/tests/mochitest/devicestorage/test_overwrite.html b/dom/devicestorage/test/test_overwrite.html similarity index 100% rename from dom/tests/mochitest/devicestorage/test_overwrite.html rename to dom/devicestorage/test/test_overwrite.html diff --git a/dom/tests/mochitest/devicestorage/test_sanity.html b/dom/devicestorage/test/test_sanity.html similarity index 100% rename from dom/tests/mochitest/devicestorage/test_sanity.html rename to dom/devicestorage/test/test_sanity.html diff --git a/dom/interfaces/core/nsIDOMElement.idl b/dom/interfaces/core/nsIDOMElement.idl index 6b4f47c89d45..7c7f3b07851d 100644 --- a/dom/interfaces/core/nsIDOMElement.idl +++ b/dom/interfaces/core/nsIDOMElement.idl @@ -156,6 +156,12 @@ interface nsIDOMElement : nsIDOMNode */ readonly attribute long clientHeight; + /* The maximum offset that the element can be scrolled to + (i.e., the value that scrollLeft/scrollTop would be clamped to if they were + set to arbitrarily large values. */ + readonly attribute long scrollLeftMax; + readonly attribute long scrollTopMax; + // Selectors API /** diff --git a/dom/interfaces/html/nsIDOMHTMLInputElement.idl b/dom/interfaces/html/nsIDOMHTMLInputElement.idl index 09c28953f3a8..17fc3ea9828c 100644 --- a/dom/interfaces/html/nsIDOMHTMLInputElement.idl +++ b/dom/interfaces/html/nsIDOMHTMLInputElement.idl @@ -20,7 +20,7 @@ interface nsIDOMValidityState; * http://www.whatwg.org/specs/web-apps/current-work/ */ -[scriptable, uuid(05FEDF7E-3050-4143-AB97-B994F3CC9329)] +[scriptable, uuid(ac2e2b14-b987-452c-a071-5823b2406b85)] interface nsIDOMHTMLInputElement : nsIDOMHTMLElement { attribute DOMString accept; @@ -40,6 +40,7 @@ interface nsIDOMHTMLInputElement : nsIDOMHTMLElement readonly attribute nsIDOMFileList files; + attribute unsigned long height; attribute boolean indeterminate; readonly attribute nsIDOMHTMLElement list; @@ -56,6 +57,7 @@ interface nsIDOMHTMLInputElement : nsIDOMHTMLElement attribute DOMString align; attribute unsigned long size; + attribute unsigned long width; attribute DOMString src; attribute DOMString type; diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 05243027cf42..82920b9e9a11 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -4,7 +4,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifdef MOZ_WIDGET_GTK2 +#ifdef MOZ_WIDGET_GTK #include #endif @@ -218,7 +218,7 @@ ContentChild::Init(MessageLoop* aIOLoop, base::ProcessHandle aParentHandle, IPC::Channel* aChannel) { -#ifdef MOZ_WIDGET_GTK2 +#ifdef MOZ_WIDGET_GTK // sigh gtk_init(NULL, NULL); #endif diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 38052bd8e83c..b378cc59f6d9 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -507,6 +507,7 @@ ContentParent::RecvSetClipboardText(const nsString& text, const PRInt32& whichCl nsCOMPtr trans = do_CreateInstance("@mozilla.org/widget/transferable;1", &rv); NS_ENSURE_SUCCESS(rv, true); + trans->Init(nsnull); // If our data flavor has already been added, this will fail. But we don't care trans->AddDataFlavor(kUnicodeMime); @@ -531,6 +532,7 @@ ContentParent::RecvGetClipboardText(const PRInt32& whichClipboard, nsString* tex nsCOMPtr trans = do_CreateInstance("@mozilla.org/widget/transferable;1", &rv); NS_ENSURE_SUCCESS(rv, true); + trans->Init(nsnull); clipboard->GetData(trans, whichClipboard); nsCOMPtr tmp; diff --git a/dom/plugins/base/nsNPAPIPlugin.cpp b/dom/plugins/base/nsNPAPIPlugin.cpp index e703161b51f4..a5927897127d 100644 --- a/dom/plugins/base/nsNPAPIPlugin.cpp +++ b/dom/plugins/base/nsNPAPIPlugin.cpp @@ -56,11 +56,13 @@ #endif // needed for nppdf plugin -#ifdef MOZ_WIDGET_GTK2 +#if (MOZ_WIDGET_GTK) #include #include +#if (MOZ_WIDGET_GTK == 2) #include "gtk2xtbin.h" #endif +#endif #ifdef XP_OS2 #define INCL_DOS @@ -2013,7 +2015,7 @@ _getvalue(NPP npp, NPNVariable variable, void *result) } case NPNVToolkit: { -#ifdef MOZ_WIDGET_GTK2 +#ifdef MOZ_WIDGET_GTK *((NPNToolkitType*)result) = NPNVGtk2; #endif @@ -2028,7 +2030,7 @@ _getvalue(NPP npp, NPNVariable variable, void *result) } case NPNVSupportsXEmbedBool: { -#ifdef MOZ_WIDGET_GTK2 +#ifdef MOZ_WIDGET_GTK *(NPBool*)result = true; #elif defined(MOZ_WIDGET_QT) // Desktop Flash fail to initialize if browser does not support NPNVSupportsXEmbedBool @@ -2055,7 +2057,7 @@ _getvalue(NPP npp, NPNVariable variable, void *result) case NPNVSupportsWindowless: { #if defined(XP_WIN) || defined(XP_MACOSX) || \ - (defined(MOZ_X11) && (defined(MOZ_WIDGET_GTK2) || defined(MOZ_WIDGET_QT))) + (defined(MOZ_X11) && (defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT))) *(NPBool*)result = true; #else *(NPBool*)result = false; diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp index ef8c5de34ed8..4fb58bfae2bf 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -734,7 +734,7 @@ NS_IMETHODIMP nsPluginInstanceOwner::GetNetscapeWindow(void *value) } return rv; -#elif (defined(MOZ_WIDGET_GTK2) || defined(MOZ_WIDGET_QT)) && defined(MOZ_X11) +#elif (defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT)) && defined(MOZ_X11) // X11 window managers want the toplevel window for WM_TRANSIENT_FOR. nsIWidget* win = mObjectFrame->GetNearestWidget(); if (!win) @@ -2432,7 +2432,7 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const nsGUIEvent& anEvent) nsIntPoint rootPoint(-1,-1); if (widget) rootPoint = anEvent.refPoint + widget->WidgetToScreenOffset(); -#ifdef MOZ_WIDGET_GTK2 +#ifdef MOZ_WIDGET_GTK Window root = GDK_ROOT_WINDOW(); #elif defined(MOZ_WIDGET_QT) Window root = RootWindowOfScreen(DefaultScreenOfDisplay(mozilla::DefaultXDisplay())); @@ -2520,7 +2520,7 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const nsGUIEvent& anEvent) if (anEvent.pluginEvent) { XKeyEvent &event = pluginEvent.xkey; -#ifdef MOZ_WIDGET_GTK2 +#ifdef MOZ_WIDGET_GTK event.root = GDK_ROOT_WINDOW(); event.time = anEvent.time; const GdkEventKey* gdkEvent = @@ -3046,17 +3046,11 @@ void nsPluginInstanceOwner::Paint(gfxContext* aContext, aContext->Translate(pluginRect.TopLeft()); Renderer renderer(window, this, pluginSize, pluginDirtyRect); -#ifdef MOZ_WIDGET_GTK2 - // This is the visual used by the widgets, 24-bit if available. - GdkVisual* gdkVisual = gdk_rgb_get_visual(); - Visual* visual = gdk_x11_visual_get_xvisual(gdkVisual); - Screen* screen = - gdk_x11_screen_get_xscreen(gdk_visual_get_screen(gdkVisual)); -#else + Display* dpy = mozilla::DefaultXDisplay(); Screen* screen = DefaultScreenOfDisplay(dpy); Visual* visual = DefaultVisualOfScreen(screen); -#endif + renderer.Draw(aContext, nsIntSize(window->width, window->height), rendererFlags, screen, visual, nsnull); } diff --git a/dom/plugins/base/nsPluginNativeWindowGtk2.cpp b/dom/plugins/base/nsPluginNativeWindowGtk2.cpp index 2d9a5dbcdf03..09ec470c7fdf 100644 --- a/dom/plugins/base/nsPluginNativeWindowGtk2.cpp +++ b/dom/plugins/base/nsPluginNativeWindowGtk2.cpp @@ -16,6 +16,7 @@ #include #include +#include "gtk2compat.h" #include "gtk2xtbin.h" class nsPluginNativeWindowGtk2 : public nsPluginNativeWindow { @@ -147,8 +148,8 @@ nsresult nsPluginNativeWindowGtk2::CallSetWindow(nsRefPtr nsresult nsPluginNativeWindowGtk2::CreateXEmbedWindow() { NS_ASSERTION(!mSocketWidget,"Already created a socket widget!"); - - GdkWindow *parent_win = gdk_window_lookup((XID)window); + GdkDisplay *display = gdk_display_get_default(); + GdkWindow *parent_win = gdk_x11_window_lookup_for_display(display, (XID)window); mSocketWidget = gtk_socket_new(); //attach the socket to the container widget @@ -179,7 +180,10 @@ nsresult nsPluginNativeWindowGtk2::CreateXEmbedWindow() { // background and this would happen immediately (before the plug is // created). Setting the background to None prevents the server from // painting this window, avoiding flicker. - gdk_window_set_back_pixmap(mSocketWidget->window, NULL, FALSE); + // TODO GTK3 +#if (MOZ_WIDGET_GTK == 2) + gdk_window_set_back_pixmap(gtk_widget_get_window(mSocketWidget), NULL, FALSE); +#endif // Resize before we show SetAllocation(); @@ -196,11 +200,17 @@ nsresult nsPluginNativeWindowGtk2::CreateXEmbedWindow() { return NS_ERROR_FAILURE; mWsInfo.display = GDK_WINDOW_XDISPLAY(gdkWindow); +#if (MOZ_WIDGET_GTK == 2) mWsInfo.colormap = GDK_COLORMAP_XCOLORMAP(gdk_drawable_get_colormap(gdkWindow)); GdkVisual* gdkVisual = gdk_drawable_get_visual(gdkWindow); - mWsInfo.visual = GDK_VISUAL_XVISUAL(gdkVisual); mWsInfo.depth = gdkVisual->depth; - +#else + mWsInfo.colormap = None; + GdkVisual* gdkVisual = gdk_window_get_visual(gdkWindow); + mWsInfo.depth = gdk_visual_get_depth(gdkVisual); +#endif + mWsInfo.visual = GDK_VISUAL_XVISUAL(gdkVisual); + return NS_OK; } @@ -223,7 +233,8 @@ nsresult nsPluginNativeWindowGtk2::CreateXtWindow() { printf("About to create new xtbin of %i X %i from %p...\n", width, height, (void*)window); #endif - GdkWindow *gdkWindow = gdk_window_lookup((XID)window); + GdkDisplay *display = gdk_display_get_default(); + GdkWindow *gdkWindow = gdk_x11_window_lookup_for_display(display, (XID)window); mSocketWidget = gtk_xtbin_new(gdkWindow, 0); // Check to see if creating the xtbin failed for some reason. // if it did, we can't go any further. @@ -270,8 +281,9 @@ socket_unrealize_cb(GtkWidget *widget, gpointer data) { // Unmap and reparent any child windows that GDK does not yet know about. // (See bug 540114 comment 10.) - GdkWindow* socket_window = widget->window; - Display* display = GDK_DISPLAY(); + GdkWindow* socket_window = gtk_widget_get_window(widget); + GdkDisplay* gdkDisplay = gdk_display_get_default(); + Display* display = GDK_DISPLAY_XDISPLAY(gdkDisplay); // Ignore X errors that may happen if windows get destroyed (possibly // requested by the plugin) between XQueryTree and when we operate on them. @@ -280,13 +292,13 @@ socket_unrealize_cb(GtkWidget *widget, gpointer data) Window root, parent; Window* children; unsigned int nchildren; - if (!XQueryTree(display, gdk_x11_drawable_get_xid(socket_window), + if (!XQueryTree(display, gdk_x11_window_get_xid(socket_window), &root, &parent, &children, &nchildren)) return; for (unsigned int i = 0; i < nchildren; ++i) { Window child = children[i]; - if (!gdk_window_lookup(child)) { + if (!gdk_x11_window_lookup_for_display(gdkDisplay, child)) { // This window is not known to GDK. XUnmapWindow(display, child); XReparentWindow(display, child, DefaultRootWindow(display), 0, 0); diff --git a/dom/plugins/ipc/PluginInstanceChild.cpp b/dom/plugins/ipc/PluginInstanceChild.cpp index c1dfbe3a9982..8e7193b3e8cf 100644 --- a/dom/plugins/ipc/PluginInstanceChild.cpp +++ b/dom/plugins/ipc/PluginInstanceChild.cpp @@ -44,9 +44,12 @@ using mozilla::ipc::ProcessChild; using namespace mozilla::plugins; using namespace std; -#ifdef MOZ_WIDGET_GTK2 +#ifdef MOZ_WIDGET_GTK #include +#if (MOZ_WIDGET_GTK == 3) +#include +#endif #include #include #include "gtk2xtbin.h" diff --git a/dom/plugins/ipc/PluginInstanceParent.cpp b/dom/plugins/ipc/PluginInstanceParent.cpp index dce4333a819c..19b66b65aee9 100644 --- a/dom/plugins/ipc/PluginInstanceParent.cpp +++ b/dom/plugins/ipc/PluginInstanceParent.cpp @@ -36,7 +36,7 @@ extern const PRUnichar* kOOPPPluginFocusEventId; UINT gOOPPPluginFocusEvent = RegisterWindowMessage(kOOPPPluginFocusEventId); extern const PRUnichar* kFlashFullscreenClass; -#elif defined(MOZ_WIDGET_GTK2) +#elif defined(MOZ_WIDGET_GTK) #include #elif defined(XP_MACOSX) #include @@ -1268,7 +1268,7 @@ PluginInstanceParent::NPP_HandleEvent(void* event) // Release any active pointer grab so that the plugin X client can // grab the pointer if it wishes. Display *dpy = DefaultXDisplay(); -# ifdef MOZ_WIDGET_GTK2 +# ifdef MOZ_WIDGET_GTK // GDK attempts to (asynchronously) track whether there is an active // grab so ungrab through GDK. gdk_pointer_ungrab(npevent->xbutton.time); diff --git a/dom/plugins/ipc/PluginModuleChild.cpp b/dom/plugins/ipc/PluginModuleChild.cpp index 4a1eed0ef266..83d6e2fb26a4 100644 --- a/dom/plugins/ipc/PluginModuleChild.cpp +++ b/dom/plugins/ipc/PluginModuleChild.cpp @@ -17,8 +17,12 @@ #include "mozilla/ipc/SyncChannel.h" -#ifdef MOZ_WIDGET_GTK2 +#ifdef MOZ_WIDGET_GTK #include +#if (MOZ_WIDGET_GTK == 3) +#include +#endif +#include "gtk2compat.h" #endif #include "nsIFile.h" @@ -90,7 +94,7 @@ PluginModuleChild::PluginModuleChild() , mInitializeFunc(0) #if defined(OS_WIN) || defined(OS_MACOSX) , mGetEntryPointsFunc(0) -#elif defined(MOZ_WIDGET_GTK2) +#elif defined(MOZ_WIDGET_GTK) , mNestedLoopTimerId(0) #elif defined(MOZ_WIDGET_QT) , mNestedLoopTimerObject(0) @@ -229,7 +233,7 @@ PluginModuleChild::Init(const std::string& aPluginFilename, return true; } -#if defined(MOZ_WIDGET_GTK2) +#if defined(MOZ_WIDGET_GTK) typedef void (*GObjectDisposeFn)(GObject*); typedef gboolean (*GtkWidgetScrollEventFn)(GtkWidget*, GdkEventScroll*); typedef void (*GtkPlugEmbeddedFn)(GtkPlug*); @@ -269,16 +273,16 @@ wrap_gtk_plug_dispose(GObject* object) { static gboolean gtk_plug_scroll_event(GtkWidget *widget, GdkEventScroll *gdk_event) { - if (!GTK_WIDGET_TOPLEVEL(widget)) // in same process as its GtkSocket + if (!gtk_widget_is_toplevel(widget)) // in same process as its GtkSocket return FALSE; // event not handled; propagate to GtkSocket - GdkWindow* socket_window = GTK_PLUG(widget)->socket_window; + GdkWindow* socket_window = gtk_plug_get_socket_window(GTK_PLUG(widget)); if (!socket_window) return FALSE; // Propagate the event to the embedder. - GdkScreen* screen = gdk_drawable_get_screen(socket_window); - GdkWindow* plug_window = widget->window; + GdkScreen* screen = gdk_window_get_screen(socket_window); + GdkWindow* plug_window = gtk_widget_get_window(widget); GdkWindow* event_window = gdk_event->window; gint x = gdk_event->x; gint y = gdk_event->y; @@ -324,9 +328,9 @@ gtk_plug_scroll_event(GtkWidget *widget, GdkEventScroll *gdk_event) memset(&xevent, 0, sizeof(xevent)); xevent.xbutton.type = ButtonPress; - xevent.xbutton.window = GDK_WINDOW_XWINDOW(socket_window); - xevent.xbutton.root = GDK_WINDOW_XWINDOW(gdk_screen_get_root_window(screen)); - xevent.xbutton.subwindow = GDK_WINDOW_XWINDOW(plug_window); + xevent.xbutton.window = gdk_x11_window_get_xid(socket_window); + xevent.xbutton.root = gdk_x11_window_get_xid(gdk_screen_get_root_window(screen)); + xevent.xbutton.subwindow = gdk_x11_window_get_xid(plug_window); xevent.xbutton.time = gdk_event->time; xevent.xbutton.x = x; xevent.xbutton.y = y; @@ -354,7 +358,7 @@ gtk_plug_scroll_event(GtkWidget *widget, GdkEventScroll *gdk_event) static void wrap_gtk_plug_embedded(GtkPlug* plug) { - GdkWindow* socket_window = plug->socket_window; + GdkWindow* socket_window = gtk_plug_get_socket_window(plug); if (socket_window) { if (gtk_check_version(2,18,7) != NULL // older && g_object_get_data(G_OBJECT(socket_window), @@ -497,7 +501,7 @@ PluginModuleChild::ShouldContinueFromReplyTimeout() bool PluginModuleChild::InitGraphics() { -#if defined(MOZ_WIDGET_GTK2) +#if defined(MOZ_WIDGET_GTK) // Work around plugins that don't interact well with GDK // client-side windows. PR_SetEnv("GDK_NATIVE_WINDOWS=1"); @@ -1072,7 +1076,7 @@ _getvalue(NPP aNPP, switch (aVariable) { // Copied from nsNPAPIPlugin.cpp case NPNVToolkit: -#if defined(MOZ_WIDGET_GTK2) || defined(MOZ_WIDGET_QT) +#if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT) *static_cast(aValue) = NPNVGtk2; return NPERR_NO_ERROR; #endif diff --git a/dom/plugins/ipc/PluginModuleChild.h b/dom/plugins/ipc/PluginModuleChild.h index dc170c95b18a..ac7709bb707b 100644 --- a/dom/plugins/ipc/PluginModuleChild.h +++ b/dom/plugins/ipc/PluginModuleChild.h @@ -306,7 +306,7 @@ private: void InitQuirksModes(const nsCString& aMimeType); bool InitGraphics(); void DeinitGraphics(); -#if defined(MOZ_WIDGET_GTK2) +#if defined(MOZ_WIDGET_GTK) static gboolean DetectNestedEventLoop(gpointer data); static gboolean ProcessBrowserEvents(gpointer data); @@ -340,7 +340,7 @@ private: NPPluginFuncs mFunctions; NPSavedData mSavedData; -#if defined(MOZ_WIDGET_GTK2) +#if defined(MOZ_WIDGET_GTK) // If a plugin spins a nested glib event loop in response to a // synchronous IPC message from the browser, the loop might break // only after the browser responds to a request sent by the diff --git a/dom/plugins/ipc/PluginModuleParent.cpp b/dom/plugins/ipc/PluginModuleParent.cpp index ff66d60f92e8..bb7d4f227ed9 100644 --- a/dom/plugins/ipc/PluginModuleParent.cpp +++ b/dom/plugins/ipc/PluginModuleParent.cpp @@ -4,7 +4,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifdef MOZ_WIDGET_GTK2 +#ifdef MOZ_WIDGET_GTK #include #elif XP_MACOSX #include "PluginInterposeOSX.h" @@ -964,7 +964,7 @@ PluginModuleParent::AnswerProcessSomeEvents() return true; } -#elif !defined(MOZ_WIDGET_GTK2) +#elif !defined(MOZ_WIDGET_GTK) bool PluginModuleParent::AnswerProcessSomeEvents() { diff --git a/dom/plugins/test/testplugin/nptest.cpp b/dom/plugins/test/testplugin/nptest.cpp index 5112137dc1c3..e9336bba1850 100644 --- a/dom/plugins/test/testplugin/nptest.cpp +++ b/dom/plugins/test/testplugin/nptest.cpp @@ -3221,7 +3221,7 @@ hangPlugin(NPObject* npobj, const NPVariant* args, uint32_t argCount, return true; } -#if defined(MOZ_WIDGET_GTK2) +#if defined(MOZ_WIDGET_GTK) bool getClipboardText(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) diff --git a/dom/system/OSFileConstants.cpp b/dom/system/OSFileConstants.cpp index 8e7e1c810539..f112be88d78d 100644 --- a/dom/system/OSFileConstants.cpp +++ b/dom/system/OSFileConstants.cpp @@ -202,6 +202,19 @@ static dom::ConstantSpec gLibcProperties[] = #endif // defined(EWOULDBLOCK) INT_CONSTANT(EXDEV), + // Constants used to define data structures + // + // Many data structures have different fields/sizes/etc. on + // various OSes / versions of the same OS / platforms. For these + // data structures, we need to compute and export from C the size + // and, if necessary, the offset of fields, so as to be able to + // define the structure in JS. + +#if defined(XP_UNIX) + // The size of |mode_t|. + {"OSFILE_SIZEOF_MODE_T", INT_TO_JSVAL(sizeof (mode_t)) }, +#endif // defined(XP_UNIX) + PROP_END }; @@ -349,7 +362,7 @@ bool DefineOSFileConstants(JSContext *cx, JSObject *global) } jsval valVersion = STRING_TO_JSVAL(strVersion); - if (!JS_SetProperty(cx, objSys, "Version", &valVersion)) { + if (!JS_SetProperty(cx, objSys, "Name", &valVersion)) { return false; } } diff --git a/dom/tests/mochitest/Makefile.in b/dom/tests/mochitest/Makefile.in index 481e24a89842..6411d139867c 100644 --- a/dom/tests/mochitest/Makefile.in +++ b/dom/tests/mochitest/Makefile.in @@ -18,7 +18,6 @@ DIRS += \ ajax \ bugs \ chrome \ - devicestorage \ general \ whatwg \ geolocation \ diff --git a/dom/tests/mochitest/general/test_clipboard_events.html b/dom/tests/mochitest/general/test_clipboard_events.html index 1e5c5c875e0c..24abb2bf4ab8 100644 --- a/dom/tests/mochitest/general/test_clipboard_events.html +++ b/dom/tests/mochitest/general/test_clipboard_events.html @@ -74,10 +74,17 @@ window.onfocus = function() SimpleTest.waitForExplicitFinish(); window.focus(); +function getLoadContext() { + return window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIWebNavigation) + .QueryInterface(Components.interfaces.nsILoadContext); +} + function getClipboardText() { var trans = Components.classes["@mozilla.org/widget/transferable;1"] .createInstance(); trans = trans.QueryInterface(Components.interfaces.nsITransferable); + trans.init(getLoadContext()); trans.addDataFlavor("text/unicode"); var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"] diff --git a/editor/libeditor/base/DeleteRangeTxn.cpp b/editor/libeditor/base/DeleteRangeTxn.cpp index f8edcbb48bd2..64266042b60f 100644 --- a/editor/libeditor/base/DeleteRangeTxn.cpp +++ b/editor/libeditor/base/DeleteRangeTxn.cpp @@ -4,33 +4,21 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "DeleteRangeTxn.h" -#include "nsIDOMRange.h" -#include "nsIDOMNodeList.h" #include "DeleteTextTxn.h" #include "DeleteElementTxn.h" #include "nsIContentIterator.h" -#include "nsIContent.h" #include "nsComponentManagerUtils.h" #include "mozilla/Util.h" using namespace mozilla; -#ifdef DEBUG -static bool gNoisy = false; -#endif - // note that aEditor is not refcounted DeleteRangeTxn::DeleteRangeTxn() -: EditAggregateTxn() -,mRange() -,mStartParent() -,mStartOffset(0) -,mEndParent() -,mCommonParent() -,mEndOffset(0) -,mEditor(nsnull) -,mRangeUpdater(nsnull) + : EditAggregateTxn(), + mRange(), + mEditor(nsnull), + mRangeUpdater(nsnull) { } @@ -39,264 +27,203 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(DeleteRangeTxn) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DeleteRangeTxn, EditAggregateTxn) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRange) - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mStartParent) - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mEndParent) - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCommonParent) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DeleteRangeTxn, EditAggregateTxn) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mRange) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mStartParent) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mEndParent) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCommonParent) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mRange, nsIDOMRange) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteRangeTxn) NS_INTERFACE_MAP_END_INHERITING(EditAggregateTxn) -NS_IMETHODIMP DeleteRangeTxn::Init(nsEditor *aEditor, - nsIDOMRange *aRange, - nsRangeUpdater *aRangeUpdater) +nsresult +DeleteRangeTxn::Init(nsEditor* aEditor, + nsRange* aRange, + nsRangeUpdater* aRangeUpdater) { - NS_ASSERTION(aEditor && aRange, "bad state"); - if (!aEditor || !aRange) { return NS_ERROR_NOT_INITIALIZED; } + MOZ_ASSERT(aEditor && aRange); mEditor = aEditor; - mRange = do_QueryInterface(aRange); + mRange = aRange->CloneRange(); mRangeUpdater = aRangeUpdater; - - DebugOnly result = aRange->GetStartContainer(getter_AddRefs(mStartParent)); - NS_ASSERTION((NS_SUCCEEDED(result)), "GetStartParent failed."); - result = aRange->GetEndContainer(getter_AddRefs(mEndParent)); - NS_ASSERTION((NS_SUCCEEDED(result)), "GetEndParent failed."); - result = aRange->GetStartOffset(&mStartOffset); - NS_ASSERTION((NS_SUCCEEDED(result)), "GetStartOffset failed."); - result = aRange->GetEndOffset(&mEndOffset); - NS_ASSERTION((NS_SUCCEEDED(result)), "GetEndOffset failed."); - result = aRange->GetCommonAncestorContainer(getter_AddRefs(mCommonParent)); - NS_ASSERTION((NS_SUCCEEDED(result)), "GetCommonParent failed."); - if (!mEditor->IsModifiableNode(mStartParent)) { - return NS_ERROR_FAILURE; - } - - if (mStartParent!=mEndParent && - (!mEditor->IsModifiableNode(mEndParent) || - !mEditor->IsModifiableNode(mCommonParent))) - { - return NS_ERROR_FAILURE; - } - -#ifdef DEBUG - { - nsCOMPtr start = do_QueryInterface(mStartParent); - MOZ_ASSERT(start); - NS_ASSERTION(mStartOffset <= PRInt32(start->Length()), "bad start offset"); - - nsCOMPtr end = do_QueryInterface(mEndParent); - MOZ_ASSERT(end); - NS_ASSERTION(mEndOffset <= PRInt32(end->Length()), "bad end offset"); - - if (gNoisy) { - printf ("DeleteRange: %d of %p to %d of %p\n", - mStartOffset, (void *)mStartParent, mEndOffset, (void *)mEndParent); - } - } -#endif // DEBUG + NS_ENSURE_TRUE(mEditor->IsModifiableNode(mRange->GetStartParent()), + NS_ERROR_FAILURE); + NS_ENSURE_TRUE(mEditor->IsModifiableNode(mRange->GetEndParent()), + NS_ERROR_FAILURE); + NS_ENSURE_TRUE(mEditor->IsModifiableNode(mRange->GetCommonAncestor()), + NS_ERROR_FAILURE); return NS_OK; } -NS_IMETHODIMP DeleteRangeTxn::DoTransaction(void) +NS_IMETHODIMP +DeleteRangeTxn::DoTransaction() { -#ifdef DEBUG - if (gNoisy) { printf("Do Delete Range\n"); } -#endif + MOZ_ASSERT(mRange && mEditor); + nsresult res; - NS_ENSURE_TRUE(mStartParent && mEndParent && mCommonParent && mEditor, NS_ERROR_NOT_INITIALIZED); - - nsresult result; // build the child transactions + nsCOMPtr startParent = mRange->GetStartParent(); + PRInt32 startOffset = mRange->StartOffset(); + nsCOMPtr endParent = mRange->GetEndParent(); + PRInt32 endOffset = mRange->EndOffset(); + MOZ_ASSERT(startParent && endParent); - if (mStartParent==mEndParent) - { // the selection begins and ends in the same node - result = CreateTxnsToDeleteBetween(mStartParent, mStartOffset, mEndOffset); - } - else - { // the selection ends in a different node from where it started - // delete the relevant content in the start node - result = CreateTxnsToDeleteContent(mStartParent, mStartOffset, nsIEditor::eNext); - if (NS_SUCCEEDED(result)) - { - // delete the intervening nodes - result = CreateTxnsToDeleteNodesBetween(); - if (NS_SUCCEEDED(result)) - { - // delete the relevant content in the end node - result = CreateTxnsToDeleteContent(mEndParent, mEndOffset, nsIEditor::ePrevious); - } - } + if (startParent == endParent) { + // the selection begins and ends in the same node + res = CreateTxnsToDeleteBetween(startParent, startOffset, endOffset); + NS_ENSURE_SUCCESS(res, res); + } else { + // the selection ends in a different node from where it started. delete + // the relevant content in the start node + res = CreateTxnsToDeleteContent(startParent, startOffset, nsIEditor::eNext); + NS_ENSURE_SUCCESS(res, res); + // delete the intervening nodes + res = CreateTxnsToDeleteNodesBetween(); + NS_ENSURE_SUCCESS(res, res); + // delete the relevant content in the end node + res = CreateTxnsToDeleteContent(endParent, endOffset, nsIEditor::ePrevious); + NS_ENSURE_SUCCESS(res, res); } // if we've successfully built this aggregate transaction, then do it. - if (NS_SUCCEEDED(result)) { - result = EditAggregateTxn::DoTransaction(); - } + res = EditAggregateTxn::DoTransaction(); + NS_ENSURE_SUCCESS(res, res); - NS_ENSURE_SUCCESS(result, result); - // only set selection to deletion point if editor gives permission bool bAdjustSelection; mEditor->ShouldTxnSetSelection(&bAdjustSelection); - if (bAdjustSelection) - { - nsCOMPtr selection; - result = mEditor->GetSelection(getter_AddRefs(selection)); - // At this point, it is possible that the frame for our root element - // might have been destroyed, in which case, the above call returns - // an error. We eat that error here intentionally. See bug 574558 - // for a sample case where this happens. - NS_ENSURE_SUCCESS(result, NS_OK); + if (bAdjustSelection) { + nsRefPtr selection = mEditor->GetSelection(); NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); - result = selection->Collapse(mStartParent, mStartOffset); - } - else - { - // do nothing - dom range gravity will adjust selection + res = selection->Collapse(startParent, startOffset); + NS_ENSURE_SUCCESS(res, res); } + // else do nothing - dom range gravity will adjust selection - return result; + return NS_OK; } -NS_IMETHODIMP DeleteRangeTxn::UndoTransaction(void) +NS_IMETHODIMP +DeleteRangeTxn::UndoTransaction() { -#ifdef DEBUG - if (gNoisy) { printf("Undo Delete Range\n"); } -#endif - - NS_ENSURE_TRUE(mStartParent && mEndParent && mCommonParent && mEditor, NS_ERROR_NOT_INITIALIZED); + MOZ_ASSERT(mRange && mEditor); return EditAggregateTxn::UndoTransaction(); } -NS_IMETHODIMP DeleteRangeTxn::RedoTransaction(void) +NS_IMETHODIMP +DeleteRangeTxn::RedoTransaction() { -#ifdef DEBUG - if (gNoisy) { printf("Redo Delete Range\n"); } -#endif - - NS_ENSURE_TRUE(mStartParent && mEndParent && mCommonParent && mEditor, NS_ERROR_NOT_INITIALIZED); + MOZ_ASSERT(mRange && mEditor); return EditAggregateTxn::RedoTransaction(); } -NS_IMETHODIMP DeleteRangeTxn::GetTxnDescription(nsAString& aString) +NS_IMETHODIMP +DeleteRangeTxn::GetTxnDescription(nsAString& aString) { aString.AssignLiteral("DeleteRangeTxn"); return NS_OK; } -NS_IMETHODIMP -DeleteRangeTxn::CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent, - PRUint32 aStartOffset, - PRUint32 aEndOffset) +nsresult +DeleteRangeTxn::CreateTxnsToDeleteBetween(nsINode* aNode, + PRInt32 aStartOffset, + PRInt32 aEndOffset) { // see what kind of node we have - nsCOMPtr textNode = do_QueryInterface(aStartParent); - if (textNode) { - // if the node is a text node, then delete text content + if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) { + // if the node is a chardata node, then delete chardata content nsRefPtr txn = new DeleteTextTxn(); PRInt32 numToDel; - if (aStartOffset==aEndOffset) + if (aStartOffset == aEndOffset) { numToDel = 1; - else - numToDel = aEndOffset-aStartOffset; + } else { + numToDel = aEndOffset - aStartOffset; + } - nsresult rv = txn->Init(mEditor, textNode, aStartOffset, numToDel, mRangeUpdater); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr charDataNode = do_QueryInterface(aNode); + nsresult res = txn->Init(mEditor, charDataNode, aStartOffset, numToDel, + mRangeUpdater); + NS_ENSURE_SUCCESS(res, res); AppendChild(txn); return NS_OK; } - nsCOMPtr startParent = do_QueryInterface(aStartParent); - NS_ENSURE_STATE(startParent); - NS_ASSERTION(aEndOffset <= startParent->GetChildCount(), "bad aEndOffset"); - - nsCOMPtr child = startParent->GetChildAt(aStartOffset); + nsCOMPtr child = aNode->GetChildAt(aStartOffset); NS_ENSURE_STATE(child); - nsresult result = NS_OK; - for (PRUint32 i = aStartOffset; i < aEndOffset; ++i) { + nsresult res = NS_OK; + for (PRInt32 i = aStartOffset; i < aEndOffset; ++i) { nsRefPtr txn = new DeleteElementTxn(); - result = txn->Init(mEditor, child->AsDOMNode(), mRangeUpdater); - if (NS_SUCCEEDED(result)) + res = txn->Init(mEditor, child->AsDOMNode(), mRangeUpdater); + if (NS_SUCCEEDED(res)) { AppendChild(txn); + } child = child->GetNextSibling(); } - return result; + + NS_ENSURE_SUCCESS(res, res); + return NS_OK; } -NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent, - PRUint32 aOffset, - nsIEditor::EDirection aAction) +nsresult +DeleteRangeTxn::CreateTxnsToDeleteContent(nsINode* aNode, + PRInt32 aOffset, + nsIEditor::EDirection aAction) { - nsresult result = NS_OK; // see what kind of node we have - nsCOMPtr textNode = do_QueryInterface(aParent); - if (textNode) - { // if the node is a text node, then delete text content + if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) { + // if the node is a chardata node, then delete chardata content PRUint32 start, numToDelete; - if (nsIEditor::eNext == aAction) - { - start=aOffset; - textNode->GetLength(&numToDelete); - numToDelete -= aOffset; + if (nsIEditor::eNext == aAction) { + start = aOffset; + numToDelete = aNode->Length() - aOffset; + } else { + start = 0; + numToDelete = aOffset; } - else - { - start=0; - numToDelete=aOffset; - } - - if (numToDelete) - { - nsRefPtr txn = new DeleteTextTxn(); - NS_ENSURE_TRUE(txn, NS_ERROR_OUT_OF_MEMORY); - result = txn->Init(mEditor, textNode, start, numToDelete, mRangeUpdater); - if (NS_SUCCEEDED(result)) - AppendChild(txn); + if (numToDelete) { + nsRefPtr txn = new DeleteTextTxn(); + + nsCOMPtr charDataNode = do_QueryInterface(aNode); + nsresult res = txn->Init(mEditor, charDataNode, start, numToDelete, + mRangeUpdater); + NS_ENSURE_SUCCESS(res, res); + + AppendChild(txn); } } - return result; + return NS_OK; } -NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteNodesBetween() +nsresult +DeleteRangeTxn::CreateTxnsToDeleteNodesBetween() { - nsCOMPtr iter = do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1"); - NS_ENSURE_TRUE(iter, NS_ERROR_NULL_POINTER); + nsCOMPtr iter = NS_NewContentSubtreeIterator(); - nsresult result = iter->Init(mRange); - NS_ENSURE_SUCCESS(result, result); + nsresult res = iter->Init(mRange); + NS_ENSURE_SUCCESS(res, res); - while (!iter->IsDone() && NS_SUCCEEDED(result)) - { - nsCOMPtr node = do_QueryInterface(iter->GetCurrentNode()); + while (!iter->IsDone()) { + nsCOMPtr node = iter->GetCurrentNode(); NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER); nsRefPtr txn = new DeleteElementTxn(); - NS_ENSURE_TRUE(txn, NS_ERROR_OUT_OF_MEMORY); - result = txn->Init(mEditor, node, mRangeUpdater); - if (NS_SUCCEEDED(result)) - AppendChild(txn); + res = txn->Init(mEditor, node->AsDOMNode(), mRangeUpdater); + NS_ENSURE_SUCCESS(res, res); + AppendChild(txn); + iter->Next(); } - return result; + return NS_OK; } - diff --git a/editor/libeditor/base/DeleteRangeTxn.h b/editor/libeditor/base/DeleteRangeTxn.h index d47ddf2c3342..67dda71f81bf 100644 --- a/editor/libeditor/base/DeleteRangeTxn.h +++ b/editor/libeditor/base/DeleteRangeTxn.h @@ -7,12 +7,10 @@ #define DeleteRangeTxn_h__ #include "EditAggregateTxn.h" -#include "nsIDOMNode.h" -#include "nsIDOMRange.h" +#include "nsRange.h" #include "nsEditor.h" #include "nsCOMPtr.h" -class nsIDOMRange; class nsRangeUpdater; /** @@ -25,9 +23,9 @@ public: * @param aEditor the object providing basic editing operations * @param aRange the range to delete */ - NS_IMETHOD Init(nsEditor *aEditor, - nsIDOMRange *aRange, - nsRangeUpdater *aRangeUpdater); + nsresult Init(nsEditor* aEditor, + nsRange* aRange, + nsRangeUpdater* aRangeUpdater); DeleteRangeTxn(); @@ -40,41 +38,26 @@ public: protected: - NS_IMETHOD CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent, - PRUint32 aStartOffset, - PRUint32 aEndOffset); + nsresult CreateTxnsToDeleteBetween(nsINode* aNode, + PRInt32 aStartOffset, + PRInt32 aEndOffset); - NS_IMETHOD CreateTxnsToDeleteNodesBetween(); + nsresult CreateTxnsToDeleteNodesBetween(); + + nsresult CreateTxnsToDeleteContent(nsINode* aParent, + PRInt32 aOffset, + nsIEditor::EDirection aAction); - NS_IMETHOD CreateTxnsToDeleteContent(nsIDOMNode *aParent, - PRUint32 aOffset, - nsIEditor::EDirection aAction); - protected: - - /** p1 in the range */ - nsCOMPtr mRange; // is this really an owning ptr? /** p1 in the range */ - nsCOMPtr mStartParent; - - /** p1 offset */ - PRInt32 mStartOffset; - - /** p2 in the range */ - nsCOMPtr mEndParent; - - /** the closest common parent of p1 and p2 */ - nsCOMPtr mCommonParent; - - /** p2 offset */ - PRInt32 mEndOffset; + nsRefPtr mRange; /** the editor for this transaction */ nsEditor* mEditor; /** range updater object */ - nsRangeUpdater *mRangeUpdater; + nsRangeUpdater* mRangeUpdater; }; #endif diff --git a/editor/libeditor/base/crashtests/766845.xhtml b/editor/libeditor/base/crashtests/766845.xhtml new file mode 100644 index 000000000000..409e210109b8 --- /dev/null +++ b/editor/libeditor/base/crashtests/766845.xhtml @@ -0,0 +1,27 @@ + + + + + +
+ + diff --git a/editor/libeditor/base/crashtests/crashtests.list b/editor/libeditor/base/crashtests/crashtests.list index fee7b0210baf..649278a9946a 100644 --- a/editor/libeditor/base/crashtests/crashtests.list +++ b/editor/libeditor/base/crashtests/crashtests.list @@ -13,3 +13,4 @@ load 713427-2.xhtml load 762183.html load 766360.html load 766413.html +load 766845.xhtml diff --git a/editor/libeditor/html/nsHTMLDataTransfer.cpp b/editor/libeditor/html/nsHTMLDataTransfer.cpp index 2243b4f0021d..7b89bbdddb75 100644 --- a/editor/libeditor/html/nsHTMLDataTransfer.cpp +++ b/editor/libeditor/html/nsHTMLDataTransfer.cpp @@ -93,6 +93,7 @@ #include "nsContentUtils.h" #include "mozilla/Preferences.h" #include "nsIParserUtils.h" +#include "nsILoadContext.h" using namespace mozilla; @@ -985,6 +986,10 @@ NS_IMETHODIMP nsHTMLEditor::PrepareHTMLTransferable(nsITransferable **aTransfera // Get the nsITransferable interface for getting the data from the clipboard if (aTransferable) { + nsCOMPtr destdoc = GetDocument(); + nsILoadContext* loadContext = destdoc ? destdoc->GetLoadContext() : nsnull; + (*aTransferable)->Init(loadContext); + // Create the desired DataFlavor for the type of data // we want to get out of the transferable // This should only happen in html editors, not plaintext @@ -1535,6 +1540,7 @@ NS_IMETHODIMP nsHTMLEditor::Paste(PRInt32 aSelectionType) nsCOMPtr contextTrans = do_CreateInstance("@mozilla.org/widget/transferable;1"); NS_ENSURE_TRUE(contextTrans, NS_ERROR_NULL_POINTER); + contextTrans->Init(nsnull); contextTrans->AddDataFlavor(kHTMLContext); clipboard->GetData(contextTrans, aSelectionType); contextTrans->GetTransferData(kHTMLContext, getter_AddRefs(contextDataObj), &contextLen); @@ -1542,6 +1548,7 @@ NS_IMETHODIMP nsHTMLEditor::Paste(PRInt32 aSelectionType) nsCOMPtr infoTrans = do_CreateInstance("@mozilla.org/widget/transferable;1"); NS_ENSURE_TRUE(infoTrans, NS_ERROR_NULL_POINTER); + infoTrans->Init(nsnull); infoTrans->AddDataFlavor(kHTMLInfo); clipboard->GetData(infoTrans, aSelectionType); infoTrans->GetTransferData(kHTMLInfo, getter_AddRefs(infoDataObj), &infoLen); @@ -1775,6 +1782,10 @@ NS_IMETHODIMP nsHTMLEditor::PasteAsPlaintextQuotation(PRInt32 aSelectionType) NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(trans, NS_ERROR_FAILURE); + nsCOMPtr destdoc = GetDocument(); + nsILoadContext* loadContext = destdoc ? destdoc->GetLoadContext() : nsnull; + trans->Init(loadContext); + // We only handle plaintext pastes here trans->AddDataFlavor(kUnicodeMime); diff --git a/editor/libeditor/html/tests/test_CF_HTML_clipboard.html b/editor/libeditor/html/tests/test_CF_HTML_clipboard.html index 02a84bfa8fa7..3bfb217cc7c0 100644 --- a/editor/libeditor/html/tests/test_CF_HTML_clipboard.html +++ b/editor/libeditor/html/tests/test_CF_HTML_clipboard.html @@ -27,6 +27,12 @@ function copyCF_HTML(cfhtml, success, failure) { const Ci = Components.interfaces; const CF_HTML = "application/x-moz-nativehtml"; + function getLoadContext() { + return window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsILoadContext); + } + var cb = Cc["@mozilla.org/widget/clipboard;1"]. getService(Ci.nsIClipboard); @@ -47,6 +53,7 @@ function copyCF_HTML(cfhtml, success, failure) { var trans = Cc["@mozilla.org/widget/transferable;1"]. createInstance(Ci.nsITransferable); + trans.init(getLoadContext()); trans.addDataFlavor(CF_HTML); cb.getData(trans, cb.kGlobalClipboard); var data = {}; @@ -62,6 +69,7 @@ function copyCF_HTML(cfhtml, success, failure) { var trans = Cc["@mozilla.org/widget/transferable;1"]. createInstance(Ci.nsITransferable); + trans.init(getLoadContext()); trans.addDataFlavor(CF_HTML); var data = Cc["@mozilla.org/supports-cstring;1"]. createInstance(Ci.nsISupportsCString); diff --git a/editor/libeditor/html/tests/test_bug489202.xul b/editor/libeditor/html/tests/test_bug489202.xul index 01395753688e..5cedbb9f9517 100644 --- a/editor/libeditor/html/tests/test_bug489202.xul +++ b/editor/libeditor/html/tests/test_bug489202.xul @@ -33,11 +33,18 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=489202 var Cc = Components.classes; var Ci = Components.interfaces; +function getLoadContext() { + return window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsILoadContext); +} + function runTest() { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); var trans = Cc["@mozilla.org/widget/transferable;1"] .createInstance(Ci.nsITransferable); + trans.init(getLoadContext()); trans.addDataFlavor("text/html"); var test_data = 'mozilla.org'; var cstr = Cc["@mozilla.org/supports-string;1"] diff --git a/editor/libeditor/html/tests/test_bug520189.html b/editor/libeditor/html/tests/test_bug520189.html index 1491260b68f4..36a014e2f0d7 100644 --- a/editor/libeditor/html/tests/test_bug520189.html +++ b/editor/libeditor/html/tests/test_bug520189.html @@ -548,6 +548,13 @@ function doNextTest() { doNextTest(); } +function getLoadContext() { + const Ci = Components.interfaces; + return window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsILoadContext); +} + function runTest(test) { var elem = document.getElementById(test.id); if ("isIFrame" in test) { @@ -560,6 +567,7 @@ function runTest(test) { var trans = Components.classes["@mozilla.org/widget/transferable;1"] .createInstance(Components.interfaces.nsITransferable); + trans.init(getLoadContext()); var data = Components.classes["@mozilla.org/supports-string;1"] .createInstance(Components.interfaces.nsISupportsString); data.data = test.payload; diff --git a/editor/libeditor/html/tests/test_bug525389.html b/editor/libeditor/html/tests/test_bug525389.html index 416eec529fe2..5c8b3940e27b 100644 --- a/editor/libeditor/html/tests/test_bug525389.html +++ b/editor/libeditor/html/tests/test_bug525389.html @@ -12,6 +12,12 @@ var Cc = Components.classes; var Ci = Components.interfaces; +function getLoadContext() { + return window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsILoadContext); +} + function runTest() { var pasteCount = 0; var pasteFunc = function (event) { pasteCount++; }; @@ -49,6 +55,7 @@ function runTest() { function getTransferableFromClipboard(asHTML) { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); var trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable); + trans.init(getLoadContext()); if (asHTML) { trans.addDataFlavor("text/html"); } else { @@ -88,6 +95,7 @@ function runTest() { // Create the transferable. var trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable); + trans.init(getLoadContext()); // Add the data to the transferable. if (asHTML) { diff --git a/editor/libeditor/text/nsPlaintextDataTransfer.cpp b/editor/libeditor/text/nsPlaintextDataTransfer.cpp index 4ac126b0b165..9b6bce8117c6 100644 --- a/editor/libeditor/text/nsPlaintextDataTransfer.cpp +++ b/editor/libeditor/text/nsPlaintextDataTransfer.cpp @@ -30,7 +30,6 @@ #include "nsIDragService.h" #include "nsIDOMUIEvent.h" #include "nsCopySupport.h" -#include "nsITransferable.h" // Misc #include "nsEditorUtils.h" @@ -50,6 +49,10 @@ NS_IMETHODIMP nsPlaintextEditor::PrepareTransferable(nsITransferable **transfera // Get the nsITransferable interface for getting the data from the clipboard if (transferable) { + nsCOMPtr destdoc = GetDocument(); + nsILoadContext* loadContext = destdoc ? destdoc->GetLoadContext() : nsnull; + (*transferable)->Init(loadContext); + (*transferable)->AddDataFlavor(kUnicodeMime); (*transferable)->AddDataFlavor(kMozTextInternal); }; diff --git a/editor/libeditor/text/tests/test_bug596333.html b/editor/libeditor/text/tests/test_bug596333.html index c0b6a5ee9fc3..bdc4307e5053 100644 --- a/editor/libeditor/text/tests/test_bug596333.html +++ b/editor/libeditor/text/tests/test_bug596333.html @@ -19,6 +19,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=596333 + + +
+ + +
position relative
+
+ + + diff --git a/layout/reftests/position-dynamic-changes/relative/move-top-left-table-ref.html b/layout/reftests/position-dynamic-changes/relative/move-top-left-table-ref.html new file mode 100644 index 000000000000..748e1d7eec99 --- /dev/null +++ b/layout/reftests/position-dynamic-changes/relative/move-top-left-table-ref.html @@ -0,0 +1,13 @@ + + + + + + +
+ + +
position relative
+
+ + diff --git a/layout/reftests/position-dynamic-changes/relative/move-top-left-table.html b/layout/reftests/position-dynamic-changes/relative/move-top-left-table.html new file mode 100644 index 000000000000..e9eb3a981f1a --- /dev/null +++ b/layout/reftests/position-dynamic-changes/relative/move-top-left-table.html @@ -0,0 +1,17 @@ + + + + + + + +
+ + +
position relative
+
+ + + diff --git a/layout/reftests/position-dynamic-changes/relative/reftest.list b/layout/reftests/position-dynamic-changes/relative/reftest.list index 5a32fb647616..77605ee07a30 100644 --- a/layout/reftests/position-dynamic-changes/relative/reftest.list +++ b/layout/reftests/position-dynamic-changes/relative/reftest.list @@ -1,2 +1,4 @@ fuzzy-if(d2d,85,20) == move-right-bottom.html move-right-bottom-ref.html # Bug 742176 random-if(cocoaWidget) fuzzy-if(d2d,85,20) == move-top-left.html move-top-left-ref.html # Bug 688545, bug 742176 +fuzzy-if(d2d,85,20) == move-right-bottom-table.html move-right-bottom-table-ref.html # Bug 742176 +random-if(cocoaWidget) fuzzy-if(d2d,85,20) == move-top-left-table.html move-top-left-table-ref.html # Bug 688545, bug 742176 diff --git a/mobile/android/base/ConfirmPreference.java b/mobile/android/base/ConfirmPreference.java deleted file mode 100644 index 95ee019c9d7a..000000000000 --- a/mobile/android/base/ConfirmPreference.java +++ /dev/null @@ -1,51 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * 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; - -import org.mozilla.gecko.db.BrowserDB; - -import android.content.Context; -import android.preference.DialogPreference; -import android.util.AttributeSet; -import android.util.Log; - -class ConfirmPreference extends DialogPreference { - private static final String LOGTAG = "GeckoConfirmPreference"; - - private String mAction = null; - private Context mContext = null; - public ConfirmPreference(Context context, AttributeSet attrs) { - super(context, attrs); - mAction = attrs.getAttributeValue(null, "action"); - mContext = context; - } - public ConfirmPreference(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - mAction = attrs.getAttributeValue(null, "action"); - mContext = context; - } - protected void onDialogClosed(boolean positiveResult) { - if (!positiveResult) - return; - if ("clear_history".equalsIgnoreCase(mAction)) { - GeckoAppShell.getHandler().post(new Runnable(){ - public void run() { - BrowserDB.clearHistory(mContext.getContentResolver()); - GeckoApp.mAppContext.mFavicons.clearFavicons(); - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("browser:purge-session-history", null)); - GeckoApp.mAppContext.handleClearHistory(); - } - }); - } else if ("clear_private_data".equalsIgnoreCase(mAction)) { - GeckoAppShell.getHandler().post(new Runnable(){ - public void run() { - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Sanitize:ClearAll", null)); - } - }); - } - Log.i(LOGTAG, "action: " + mAction); - } -} diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 5ef58013789f..b8d6f287722f 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -1246,6 +1246,9 @@ abstract public class GeckoApp invalidateOptionsMenu(); } }); + } else if (event.equals("Share:Text")) { + String text = message.getString("text"); + GeckoAppShell.openUriExternal(text, "text/plain", "", "", Intent.ACTION_SEND, ""); } } catch (Exception e) { Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e); @@ -1389,7 +1392,8 @@ abstract public class GeckoApp final String oldURL = tab.getURL(); GeckoAppShell.getHandler().postDelayed(new Runnable() { public void run() { - if (!oldURL.equals(tab.getURL())) + // tab.getURL() may return null + if (!TextUtils.equals(oldURL, tab.getURL())) return; getAndProcessThumbnailForTab(tab); @@ -1922,6 +1926,7 @@ abstract public class GeckoApp GeckoAppShell.registerGeckoEventListener("WebApps:Install", GeckoApp.mAppContext); GeckoAppShell.registerGeckoEventListener("WebApps:Uninstall", GeckoApp.mAppContext); GeckoAppShell.registerGeckoEventListener("DesktopMode:Changed", GeckoApp.mAppContext); + GeckoAppShell.registerGeckoEventListener("Share:Text", GeckoApp.mAppContext); if (SmsManager.getInstance() != null) { SmsManager.getInstance().start(); @@ -2265,6 +2270,7 @@ abstract public class GeckoApp GeckoAppShell.unregisterGeckoEventListener("WebApps:Install", GeckoApp.mAppContext); GeckoAppShell.unregisterGeckoEventListener("WebApps:Uninstall", GeckoApp.mAppContext); GeckoAppShell.unregisterGeckoEventListener("DesktopMode:Changed", GeckoApp.mAppContext); + GeckoAppShell.unregisterGeckoEventListener("Share:Text", GeckoApp.mAppContext); if (mFavicons != null) mFavicons.close(); diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 0358a3f4e559..993526f66597 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -2363,7 +2363,7 @@ class ScreenshotHandler { static void scheduleCheckerboardScreenshotEvent(int tabId, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, int bw, int bh) { float totalSize = sw * sh; int numSlices = (int) Math.ceil(totalSize / 100000); - if (numSlices == 0) + if (numSlices == 0 || dw == 0 || dh == 0) return; int srcSliceSize = (int) Math.ceil(sh / numSlices); int dstSliceSize = (int) Math.ceil(dh / numSlices); diff --git a/mobile/android/base/GeckoPreferences.java b/mobile/android/base/GeckoPreferences.java index 2d1b2d7cd02e..3b390adad657 100644 --- a/mobile/android/base/GeckoPreferences.java +++ b/mobile/android/base/GeckoPreferences.java @@ -24,6 +24,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.EditText; import android.widget.LinearLayout; +import android.widget.Toast; import android.text.TextWatcher; import android.text.TextUtils; import android.content.DialogInterface; @@ -41,12 +42,14 @@ public class GeckoPreferences private ArrayList mPreferencesList; private PreferenceScreen mPreferenceScreen; private static boolean sIsCharEncodingEnabled = false; + private static final String NON_PREF_PREFIX = "android.not_a_preference."; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); GeckoAppShell.registerGeckoEventListener("Preferences:Data", this); + GeckoAppShell.registerGeckoEventListener("Sanitize:Finished", this); } @Override @@ -64,6 +67,7 @@ public class GeckoPreferences protected void onDestroy() { super.onDestroy(); GeckoAppShell.unregisterGeckoEventListener("Preferences:Data", this); + GeckoAppShell.unregisterGeckoEventListener("Sanitize:Finished", this); } public void handleMessage(String event, JSONObject message) { @@ -71,6 +75,15 @@ public class GeckoPreferences if (event.equals("Preferences:Data")) { JSONArray jsonPrefs = message.getJSONArray("preferences"); refresh(jsonPrefs); + } else if (event.equals("Sanitize:Finished")) { + boolean success = message.getBoolean("success"); + final int stringRes = success ? R.string.private_data_success : R.string.private_data_fail; + final Context context = this; + GeckoAppShell.getMainHandler().post(new Runnable () { + public void run() { + Toast.makeText(context, stringRes, Toast.LENGTH_SHORT).show(); + } + }); } } catch (Exception e) { Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e); @@ -93,7 +106,15 @@ public class GeckoPreferences initGroups((PreferenceGroup)pref); else { pref.setOnPreferenceChangeListener(this); - if (pref.getKey() != null) + + // Some Preference UI elements are not actually preferences, + // but they require a key to work correctly. For example, + // "Clear private data" requires a key for its state to be + // saved when the orientation changes. It uses the + // "android.not_a_preference.privacy.clear" key - which doesn't + // exist in Gecko - to satisfy this requirement. + String key = pref.getKey(); + if (key != null && !key.startsWith(NON_PREF_PREFIX)) mPreferencesList.add(pref.getKey()); } } diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 4e8a7e3c43df..c293349b7934 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -30,7 +30,6 @@ FENNEC_JAVA_FILES = \ AwesomeBarTabs.java \ BrowserApp.java \ BrowserToolbar.java \ - ConfirmPreference.java \ SyncPreference.java \ db/AndroidBrowserDB.java \ db/BrowserDB.java \ @@ -71,7 +70,9 @@ FENNEC_JAVA_FILES = \ LinkTextView.java \ MenuItemActionBar.java \ MenuItemDefault.java \ + MultiChoicePreference.java \ NSSBridge.java \ + PrivateDataPreference.java \ PropertyAnimator.java \ ProfileMigrator.java \ PromptService.java \ diff --git a/mobile/android/base/MultiChoicePreference.java b/mobile/android/base/MultiChoicePreference.java new file mode 100644 index 000000000000..8ad138d1eed4 --- /dev/null +++ b/mobile/android/base/MultiChoicePreference.java @@ -0,0 +1,230 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * 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; + +import android.app.AlertDialog.Builder; +import android.content.res.TypedArray; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.preference.DialogPreference; +import android.preference.PreferenceManager; +import android.preference.Preference.BaseSavedState; +import android.util.AttributeSet; +import android.util.Log; + +class MultiChoicePreference extends DialogPreference { + private static final String LOGTAG = "GeckoMultiChoicePreference"; + + private boolean mValues[]; + private boolean mPrevValues[]; + private CharSequence mEntryKeys[]; + private CharSequence mEntries[]; + private CharSequence mInitialValues[]; + + public MultiChoicePreference(Context context, AttributeSet attrs) { + super(context, attrs); + + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MultiChoicePreference); + mEntries = a.getTextArray(R.styleable.MultiChoicePreference_entries); + mEntryKeys = a.getTextArray(R.styleable.MultiChoicePreference_entryKeys); + mInitialValues = a.getTextArray(R.styleable.MultiChoicePreference_initialValues); + a.recycle(); + + loadPersistedValues(); + } + + public MultiChoicePreference(Context context) { + this(context, null); + } + + /** + * Sets the human-readable entries to be shown in the list. This will be + * shown in subsequent dialogs. + *

+ * Each entry must have a corresponding index in + * {@link #setEntryKeys(CharSequence[])} and + * {@link #setInitialValues(CharSequence[])}. + * + * @param entries The entries. + */ + public void setEntries(CharSequence[] entries) { + mEntries = entries.clone(); + } + + /** + * @param entriesResId The entries array as a resource. + */ + public void setEntries(int entriesResId) { + setEntries(getContext().getResources().getTextArray(entriesResId)); + } + + /** + * Sets the preference keys for preferences shown in the list. + * + * @param entryKeys The entry keys. + */ + public void setEntryKeys(CharSequence[] entryKeys) { + mEntryKeys = entryKeys.clone(); + loadPersistedValues(); + } + + /** + * @param entryKeysResId The entryKeys array as a resource. + */ + public void setEntryKeys(int entryKeysResId) { + setEntryKeys(getContext().getResources().getTextArray(entryKeysResId)); + } + + /** + * The array of initial entry values in this list. Each entryValue + * corresponds to an entryKey. These values are used if a) the preference + * isn't persisted, or b) the preference is persisted but hasn't yet been + * set. + * + * @param initialValues The entry values + */ + public void setInitialValues(CharSequence[] initialValues) { + mInitialValues = initialValues.clone(); + loadPersistedValues(); + } + + /** + * @param initialValuesResId The initialValues array as a resource. + */ + public void setInitialValues(int initialValuesResId) { + setInitialValues(getContext().getResources().getTextArray(initialValuesResId)); + } + + /** + * The list of translated strings corresponding to each preference. + * + * @return The array of entries. + */ + public CharSequence[] getEntries() { + return mEntries.clone(); + } + + /** + * The list of keys corresponding to each preference. + * + * @return The array of keys. + */ + public CharSequence[] getEntryKeys() { + return mEntryKeys.clone(); + } + + /** + * The list of initial values for each preference. Each string in this list + * should be either "true" or "false". + * + * @return The array of initial values. + */ + public CharSequence[] getInitialValues() { + return mInitialValues.clone(); + } + + /** + * The list of values for each preference. These values are updated after + * the dialog has been displayed. + * + * @return The array of values. + */ + public boolean[] getValues() { + return mValues.clone(); + } + + @Override + protected void onPrepareDialogBuilder(Builder builder) { + if (mEntries == null || mEntryKeys == null || mInitialValues == null) { + throw new IllegalStateException( + "MultiChoicePreference requires entries, entryKeys, and initialValues arrays."); + } + + if (mEntries.length != mEntryKeys.length || mEntryKeys.length != mInitialValues.length) { + throw new IllegalStateException( + "MultiChoicePreference entries, entryKeys, and initialValues arrays must be the same length"); + } + + builder.setMultiChoiceItems(mEntries, mValues, new DialogInterface.OnMultiChoiceClickListener() { + public void onClick(DialogInterface dialog, int which, boolean val) { + // mValues is automatically updated when checkboxes are clicked + } + }); + } + + @Override + protected void onDialogClosed(boolean positiveResult) { + if (!positiveResult) { + // user cancelled; reset checkbox values to their previous state + mValues = mPrevValues.clone(); + return; + } else { + mPrevValues = mValues.clone(); + } + + GeckoAppShell.getHandler().post(new Runnable() { + public void run() { + for (int i = 0; i < mEntryKeys.length; i++) { + String key = mEntryKeys[i].toString(); + persistBoolean(key, mValues[i]); + } + } + }); + } + + protected boolean persistBoolean(String key, boolean value) { + if (isPersistent()) { + if (value == getPersistedBoolean(!value)) { + // It's already there, so the same as persisting + return true; + } + + PreferenceManager.getDefaultSharedPreferences(getContext()) + .edit().putBoolean(key, value).commit(); + return true; + } + return false; + } + + protected boolean getPersistedBoolean(String key, boolean defaultReturnValue) { + if (!isPersistent()) + return defaultReturnValue; + + return PreferenceManager.getDefaultSharedPreferences(getContext()) + .getBoolean(key, defaultReturnValue); + } + + /** + * Loads persistent prefs from shared preferences. If the preferences + * aren't persistent or haven't yet been stored, they will be set to their + * initial values. + */ + private void loadPersistedValues() { + if (mEntryKeys == null || mInitialValues == null) + return; + + final int entryCount = mEntryKeys.length; + if (entryCount != mEntries.length || entryCount != mInitialValues.length) { + throw new IllegalStateException( + "MultiChoicePreference entryKeys and initialValues arrays must be the same length"); + } + + mValues = new boolean[entryCount]; + GeckoAppShell.getHandler().post(new Runnable() { + public void run() { + for (int i = 0; i < entryCount; i++) { + String key = mEntryKeys[i].toString(); + boolean initialValue = mInitialValues[i].equals("true"); + mValues[i] = getPersistedBoolean(key, initialValue); + } + mPrevValues = mValues.clone(); + } + }); + } +} diff --git a/mobile/android/base/PrivateDataPreference.java b/mobile/android/base/PrivateDataPreference.java new file mode 100644 index 000000000000..40c6650c6ca8 --- /dev/null +++ b/mobile/android/base/PrivateDataPreference.java @@ -0,0 +1,72 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * 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; + +import org.mozilla.gecko.db.BrowserDB; + +import org.json.JSONObject; +import org.json.JSONException; + +import java.util.Map; + +import android.app.AlertDialog.Builder; +import android.content.Context; +import android.content.DialogInterface; +import android.util.AttributeSet; +import android.util.Log; + +class PrivateDataPreference extends MultiChoicePreference { + private static final String LOGTAG = "GeckoPrivateDataPreference"; + private static final String PREF_KEY_PREFIX = "private.data."; + + private Context mContext; + + public PrivateDataPreference(Context context, AttributeSet attrs) { + super(context, attrs); + mContext = context; + } + + @Override + protected void onDialogClosed(boolean positiveResult) { + super.onDialogClosed(positiveResult); + + if (!positiveResult) + return; + + CharSequence keys[] = getEntryKeys(); + boolean values[] = getValues(); + JSONObject json = new JSONObject(); + + for (int i = 0; i < keys.length; i++) { + // Privacy pref checkbox values are stored in Android prefs to + // remember their check states. The key names are private.data.X, + // where X is a string from Gecko sanitization. This prefix is + // removed here so we can send the values to Gecko, which then does + // the sanitization for each key. + String key = keys[i].toString().substring(PREF_KEY_PREFIX.length()); + boolean value = values[i]; + try { + json.put(key, value); + } catch (JSONException e) { + Log.e(LOGTAG, "JSON error", e); + } + + // clear private data in java + if (key.equals("history") && value) { + GeckoAppShell.getHandler().post(new Runnable() { + public void run() { + BrowserDB.clearHistory(mContext.getContentResolver()); + GeckoApp.mAppContext.mFavicons.clearFavicons(); + GeckoApp.mAppContext.handleClearHistory(); + } + }); + } + } + + // clear private data in gecko + GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Sanitize:ClearData", json.toString())); + } +} diff --git a/mobile/android/base/ProfileMigrator.java b/mobile/android/base/ProfileMigrator.java index aa13f3068a0d..76c8a72999e8 100644 --- a/mobile/android/base/ProfileMigrator.java +++ b/mobile/android/base/ProfileMigrator.java @@ -268,6 +268,13 @@ public class ProfileMigrator { mLongOperationStopCallback = null; } + public ProfileMigrator(Context context, ContentResolver contentResolver) { + mContext = context; + mCr = contentResolver; + mLongOperationStartCallback = null; + mLongOperationStopCallback = null; + } + // Define callbacks to run if the operation will take a while. // Stop callback is only run if there was a start callback that was run. public void setLongOperationCallbacks(Runnable start, @@ -277,6 +284,11 @@ public class ProfileMigrator { mLongOperationStartRun = false; } + public void launchPlacesTest(File profileDir) { + resetMigration(); + launchPlaces(profileDir, DEFAULT_HISTORY_MIGRATE_COUNT); + } + public void launchPlaces(File profileDir) { boolean timeThisRun = false; Telemetry.Timer timer = null; @@ -326,6 +338,15 @@ public class ProfileMigrator { false); } + // Only to be used for testing. Allows forcing Migration to rerun. + private void resetMigration() { + SharedPreferences.Editor editor = getPreferences().edit(); + editor.putBoolean(PREFS_MIGRATE_BOOKMARKS_DONE, false); + editor.putBoolean(PREFS_MIGRATE_HISTORY_DONE, false); + editor.putInt(PREFS_MIGRATE_HISTORY_COUNT, 0); + editor.commit(); + } + // Has migration run before? protected boolean hasMigrationRun() { return areBookmarksMigrated() diff --git a/mobile/android/base/gfx/ScreenshotLayer.java b/mobile/android/base/gfx/ScreenshotLayer.java index c65e1716251c..c41b99e5c9d3 100644 --- a/mobile/android/base/gfx/ScreenshotLayer.java +++ b/mobile/android/base/gfx/ScreenshotLayer.java @@ -27,6 +27,7 @@ import java.nio.FloatBuffer; public class ScreenshotLayer extends SingleTileLayer { private static final int SCREENSHOT_SIZE_LIMIT = 1048576; + private static final int BYTES_FOR_16BPP = 2; private ScreenshotImage mImage; // Size of the image buffer private IntSize mBufferSize; @@ -85,7 +86,7 @@ public class ScreenshotLayer extends SingleTileLayer { public static ScreenshotLayer create(Bitmap bitmap) { IntSize size = new IntSize(bitmap.getWidth(), bitmap.getHeight()); // allocate a buffer that can hold our max screenshot size - ByteBuffer buffer = GeckoAppShell.allocateDirectBuffer(SCREENSHOT_SIZE_LIMIT * 2); + ByteBuffer buffer = GeckoAppShell.allocateDirectBuffer(SCREENSHOT_SIZE_LIMIT * BYTES_FOR_16BPP); // construct the screenshot layer ScreenshotLayer sl = new ScreenshotLayer(new ScreenshotImage(buffer, size.width, size.height, CairoImage.FORMAT_RGB16_565), size); // paint the passed in bitmap into the buffer @@ -131,8 +132,11 @@ public class ScreenshotLayer extends SingleTileLayer { } void copyBuffer(ByteBuffer src, ByteBuffer dst, Rect rect, int stride) { - int start = rect.left + rect.top * stride; - int end = Math.min(Math.min(rect.right + (rect.bottom - 1) * stride, src.capacity()), dst.limit()); + int start = (rect.top * stride) + (rect.left * BYTES_FOR_16BPP); + int end = ((rect.bottom - 1) * stride) + (rect.right * BYTES_FOR_16BPP); + // clamp stuff just to be safe + start = Math.max(0, Math.min(dst.limit(), Math.min(src.limit(), start))); + end = Math.max(start, Math.min(dst.limit(), Math.min(src.capacity(), end))); dst.position(start); src.position(start).limit(end); dst.put(src); @@ -141,7 +145,7 @@ public class ScreenshotLayer extends SingleTileLayer { synchronized void setBitmap(ByteBuffer data, int width, int height, int format, Rect rect) { mSize = new IntSize(width, height); mFormat = format; - copyBuffer(data.asReadOnlyBuffer(), mBuffer.duplicate(), rect, width * 2); + copyBuffer(data.asReadOnlyBuffer(), mBuffer.duplicate(), rect, width * BYTES_FOR_16BPP); } synchronized void setBitmap(Bitmap bitmap, int width, int height, int format) { diff --git a/mobile/android/base/locales/en-US/android_strings.dtd b/mobile/android/base/locales/en-US/android_strings.dtd index efefb1597b96..043f51acd5af 100644 --- a/mobile/android/base/locales/en-US/android_strings.dtd +++ b/mobile/android/base/locales/en-US/android_strings.dtd @@ -73,10 +73,7 @@ - - - @@ -91,6 +88,13 @@ + + + + + + + @@ -149,6 +153,7 @@ + @@ -180,6 +185,9 @@ with that structure, consider a translation which ignores the preceding domain a just addresses the organization to follow, e.g. "This site is run by " --> + + + diff --git a/mobile/android/base/resources/values/arrays.xml b/mobile/android/base/resources/values/arrays.xml index 1bea7c7c3e95..b0f9e709fd3f 100644 --- a/mobile/android/base/resources/values/arrays.xml +++ b/mobile/android/base/resources/values/arrays.xml @@ -41,4 +41,31 @@ @string/awesomebar_bookmarks_title @string/awesomebar_history_title + + @string/pref_private_data_history + @string/pref_private_data_formdata + @string/pref_private_data_cookies + @string/pref_private_data_cache + @string/pref_private_data_sessions + @string/pref_private_data_offlineApps + @string/pref_private_data_siteSettings + + + true + true + true + true + true + true + true + + + private.data.history + private.data.formdata + private.data.cookies + private.data.cache + private.data.sessions + private.data.offlineApps + private.data.siteSettings + diff --git a/mobile/android/base/resources/values/attrs.xml b/mobile/android/base/resources/values/attrs.xml index 8d245f6faf23..d6e4f43c0cc5 100644 --- a/mobile/android/base/resources/values/attrs.xml +++ b/mobile/android/base/resources/values/attrs.xml @@ -27,5 +27,11 @@ + + + + + + diff --git a/mobile/android/base/resources/xml/preferences.xml.in b/mobile/android/base/resources/xml/preferences.xml.in index d81c683fa3d0..fd421ef21f14 100644 --- a/mobile/android/base/resources/xml/preferences.xml.in +++ b/mobile/android/base/resources/xml/preferences.xml.in @@ -1,3 +1,4 @@ +#filter substitution + &pref_char_encoding; &pref_char_encoding_on; &pref_char_encoding_off; - &pref_clear_history; - &pref_clear_history_confirm; &pref_clear_private_data; - &pref_clear_private_data_confirm; &pref_plugins; &pref_plugins_enabled; &pref_plugins_tap_to_play; @@ -95,6 +92,13 @@ &pref_font_size_xlarge; &pref_sync; &pref_search_suggestions; + &pref_private_data_history; + &pref_private_data_formdata; + &pref_private_data_cookies; + &pref_private_data_cache; + &pref_private_data_sessions; + &pref_private_data_offlineApps; + &pref_private_data_siteSettings; &pref_import_android; &reload; @@ -151,6 +155,7 @@ &button_ok; &button_cancel; + &button_clear_data; &abouthome_addons_title; &abouthome_addons_browse; @@ -185,6 +190,10 @@ &identity_connected_to; &identity_run_by; + + &private_data_success; + &private_data_fail; + &bookmarkhistory_button_import; &bookmarkhistory_import_both; diff --git a/mobile/android/base/tests/BaseTest.java.in b/mobile/android/base/tests/BaseTest.java.in index 593049dce647..2a414aa224d2 100644 --- a/mobile/android/base/tests/BaseTest.java.in +++ b/mobile/android/base/tests/BaseTest.java.in @@ -22,6 +22,9 @@ import java.util.HashMap; * A convenient base class suitable for most Robocop tests. */ abstract class BaseTest extends ActivityInstrumentationTestCase2 { + public static final int TEST_MOCHITEST = 0; + public static final int TEST_TALOS = 1; + private static final String TARGET_PACKAGE_ID = "org.mozilla.gecko"; private static final String LAUNCH_ACTIVITY_FULL_CLASSNAME="@ANDROID_PACKAGE_NAME@.App"; @@ -33,7 +36,6 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2 { protected Actions mActions; protected String mBaseUrl; protected String mRawBaseUrl; - private String mTestType; private String mLogFile; protected String mProfile; @@ -49,6 +51,8 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2 { super(TARGET_PACKAGE_ID, mLauncherActivityClass); } + protected abstract int getTestType(); + @Override protected void setUp() throws Exception { // Load config file from sdcard (setup by python script) @@ -72,6 +76,15 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2 { mLogFile = (String)config.get("logfile"); mBaseUrl = ((String)config.get("host")).replaceAll("(/$)", ""); mRawBaseUrl = ((String)config.get("rawhost")).replaceAll("(/$)", ""); + + // Initialize the asserter + if (getTestType() == TEST_TALOS) { + mAsserter = new FennecTalosAssert(); + } else { + mAsserter = new FennecMochitestAssert(); + } + mAsserter.setLogFile(mLogFile); + mAsserter.setTestName(this.getClass().getName()); } @Override @@ -88,18 +101,6 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2 { } } - public void setTestType(String type) { - mTestType = type; - - if (mTestType.equals("talos")) { - mAsserter = new FennecTalosAssert(); - } else { - mAsserter = new FennecMochitestAssert(); - } - mAsserter.setLogFile(mLogFile); - mAsserter.setTestName(this.getClass().getName()); - } - @Override public void tearDown() throws Exception { try { diff --git a/mobile/android/base/tests/ContentProviderTest.java.in b/mobile/android/base/tests/ContentProviderTest.java.in index 9d02ad558806..da63225502ba 100644 --- a/mobile/android/base/tests/ContentProviderTest.java.in +++ b/mobile/android/base/tests/ContentProviderTest.java.in @@ -10,7 +10,10 @@ import android.content.Context; import android.content.ContentProviderResult; import android.content.ContentProviderOperation; import android.content.OperationApplicationException; +import android.content.res.AssetManager; import android.content.res.Resources; +import android.content.pm.ApplicationInfo; +import android.content.SharedPreferences; import android.database.Cursor; import android.os.Build; import android.net.Uri; @@ -33,34 +36,61 @@ import java.util.HashMap; * will not affect or be affected by any UI-related code. This is basically * a heavily adapted port of Android's ProviderTestCase2 to work on Mozilla's * infrastructure. + * + * For some tests, we need to have access to UI parts, or at least launch + * the activity so the assets with test data become available, which requires + * that we derive this test from BaseTest and consequently pull in some more + * UI code than we'd ideally want. Furthermore, we need to pass the + * Activity and not the instrumentation Context for the UI part to find some + * of its required resources. + * Similarly, we need to pass the Activity instead of the Instrumentation + * Context down to some of our users (still wrapped in the Delegating Provider) + * because they will stop working correctly if we do not. A typical problem + * is that databases used in the ContentProvider will be attempted to be + * opened twice. */ -abstract class ContentProviderTest extends AndroidTestCase { +abstract class ContentProviderTest extends BaseTest { protected ContentProvider mProvider; protected MockContentResolver mResolver; - protected Assert mAsserter; protected ClassLoader mClassLoader; protected ArrayList mTests; protected Class mProviderClass; protected Class mProviderContract; protected String mProviderAuthority; protected IsolatedContext mProviderContext; - protected String mLogFile; private class ContentProviderMockContext extends MockContext { @Override public Resources getResources() { - return getContext().getResources(); + // We will fail to find some resources if we don't point + // at the original activity. + return ((Context)getActivity()).getResources(); + } + + @Override + public String getPackageResourcePath() { + return getInstrumentation().getContext().getPackageResourcePath(); } @Override public File getDir(String name, int mode) { - return getContext().getDir(this.getClass().getSimpleName() + "_" + name, mode); + return getInstrumentation().getContext().getDir(this.getClass().getSimpleName() + "_" + name, mode); } @Override public Context getApplicationContext() { return this; } + + @Override + public SharedPreferences getSharedPreferences(String name, int mode) { + return getInstrumentation().getContext().getSharedPreferences(name, mode); + } + + @Override + public ApplicationInfo getApplicationInfo() { + return getInstrumentation().getContext().getApplicationInfo(); + } } private class DelegatingTestContentProvider extends ContentProvider { @@ -139,7 +169,7 @@ abstract class ContentProviderTest extends AndroidTestCase { RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext( new ContentProviderMockContext(), - getContext(), + (Context)getActivity(), filenamePrefix); mProviderContext = new IsolatedContext(mResolver, targetContextWrapper); @@ -153,12 +183,6 @@ abstract class ContentProviderTest extends AndroidTestCase { mResolver.addProvider(mProviderAuthority, mProvider); } - private void loadRobotiumConfig() { - String configFile = FennecNativeDriver.getFile("/mnt/sdcard/robotium.config"); - HashMap config = FennecNativeDriver.convertTextToTable(configFile); - mLogFile = (String) config.get("logfile"); - } - public Uri getContentUri(String className) throws Exception { return getUriColumn(className, "CONTENT_URI"); } @@ -188,17 +212,6 @@ abstract class ContentProviderTest extends AndroidTestCase { mAsserter.setTestName(this.getClass().getName() + " - " + testName); } - public void setTestType(String type) { - if (type.equals("talos")) { - mAsserter = new FennecTalosAssert(); - } else { - mAsserter = new FennecMochitestAssert(); - } - - mAsserter.setLogFile(mLogFile); - mAsserter.setTestName(this.getClass().getName()); - } - public void setUp() throws Exception { throw new Exception("You should call setUp(providerClassName, authorityUriField) instead"); } @@ -206,10 +219,9 @@ abstract class ContentProviderTest extends AndroidTestCase { public void setUp(String providerClassName, String authorityUriField) throws Exception { super.setUp(); - mClassLoader = getContext().getClassLoader(); + mClassLoader = getInstrumentation().getContext().getClassLoader(); mTests = new ArrayList(); - loadRobotiumConfig(); setUpProviderClassAndAuthority(providerClassName, authorityUriField); setUpContentProvider(); } @@ -233,4 +245,8 @@ abstract class ContentProviderTest extends AndroidTestCase { super.tearDown(); } + + public AssetManager getAssetManager() { + return getInstrumentation().getContext().getAssets(); + } } diff --git a/mobile/android/base/tests/PixelTest.java.in b/mobile/android/base/tests/PixelTest.java.in index 13b1137ffbc1..7554538282d4 100644 --- a/mobile/android/base/tests/PixelTest.java.in +++ b/mobile/android/base/tests/PixelTest.java.in @@ -3,7 +3,7 @@ package @ANDROID_PACKAGE_NAME@.tests; import @ANDROID_PACKAGE_NAME@.*; -class PixelTest extends BaseTest { +abstract class PixelTest extends BaseTest { private static final long PAINT_CLEAR_DELAY = 1000; // milliseconds protected final PaintedSurface loadAndPaint(String url) { diff --git a/mobile/android/base/tests/assets/places.sqlite.zip b/mobile/android/base/tests/assets/places.sqlite.zip new file mode 100644 index 000000000000..39d06926c772 Binary files /dev/null and b/mobile/android/base/tests/assets/places.sqlite.zip differ diff --git a/mobile/android/base/tests/robocop.ini b/mobile/android/base/tests/robocop.ini index b3845576fc9d..b7708337712f 100644 --- a/mobile/android/base/tests/robocop.ini +++ b/mobile/android/base/tests/robocop.ini @@ -1,6 +1,7 @@ [testAwesomebar] [testBookmark] [testBookmarklets] +[testMigration] [testLoad] [testNewTab] [testPanCorrectness] diff --git a/mobile/android/base/tests/testAboutPage.java.in b/mobile/android/base/tests/testAboutPage.java.in index 10c982d55abe..26f564056b43 100644 --- a/mobile/android/base/tests/testAboutPage.java.in +++ b/mobile/android/base/tests/testAboutPage.java.in @@ -6,8 +6,12 @@ import android.app.Activity; import android.util.Log; public class testAboutPage extends BaseTest { + @Override + protected int getTestType() { + return TEST_MOCHITEST; + } + public void testAboutPage() { - setTestType("mochitest"); mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); // Load the about: page diff --git a/mobile/android/base/tests/testAwesomebar.java.in b/mobile/android/base/tests/testAwesomebar.java.in index 26726cd4694b..ee634173d52b 100644 --- a/mobile/android/base/tests/testAwesomebar.java.in +++ b/mobile/android/base/tests/testAwesomebar.java.in @@ -4,9 +4,12 @@ package @ANDROID_PACKAGE_NAME@.tests; import @ANDROID_PACKAGE_NAME@.*; public class testAwesomebar extends BaseTest { - public void testAwesomebar() { - setTestType("mochitest"); + @Override + protected int getTestType() { + return TEST_MOCHITEST; + } + public void testAwesomebar() { mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); String url = getAbsoluteUrl("/robocop/robocop_blank_01.html"); diff --git a/mobile/android/base/tests/testAxisLocking.java.in b/mobile/android/base/tests/testAxisLocking.java.in index a8c54d09d1a9..d54b68efb216 100644 --- a/mobile/android/base/tests/testAxisLocking.java.in +++ b/mobile/android/base/tests/testAxisLocking.java.in @@ -13,8 +13,12 @@ import android.app.Instrumentation; * - Verify that the 45-degree angle was not thrown out and it dragged diagonally */ public class testAxisLocking extends PixelTest { + @Override + protected int getTestType() { + return TEST_MOCHITEST; + } + public void testAxisLocking() { - setTestType("mochitest"); String url = getAbsoluteUrl("/robocop/robocop_boxes.html"); MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mDriver.getGeckoLeft(), mDriver.getGeckoTop()); diff --git a/mobile/android/base/tests/testBookmark.java.in b/mobile/android/base/tests/testBookmark.java.in index b2e2701e7e8c..840ca3966fc0 100644 --- a/mobile/android/base/tests/testBookmark.java.in +++ b/mobile/android/base/tests/testBookmark.java.in @@ -30,8 +30,12 @@ public class testBookmark extends BaseTest { "https://addons.mozilla.org/en-US/android/" }; + @Override + protected int getTestType() { + return TEST_MOCHITEST; + } + public void testBookmark() { - setTestType("mochitest"); BOOKMARK_URL = getAbsoluteUrl(BOOKMARK_URL); mClassLoader = getActivity().getApplicationContext().getClassLoader(); diff --git a/mobile/android/base/tests/testBookmarklets.java.in b/mobile/android/base/tests/testBookmarklets.java.in index 0a2326f3c28d..29a1aa849d18 100644 --- a/mobile/android/base/tests/testBookmarklets.java.in +++ b/mobile/android/base/tests/testBookmarklets.java.in @@ -10,13 +10,17 @@ import android.net.Uri; import android.provider.Browser; public class testBookmarklets extends BaseTest { + @Override + protected int getTestType() { + return TEST_MOCHITEST; + } + public void testBookmarklets() { final String url = getAbsoluteUrl("/robocop/robocop_blank_01.html"); final String title = "alertBookmarklet"; final String js = "javascript:alert(12 + .34)"; boolean alerted; - setTestType("mochitest"); mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); // load a standard page so bookmarklets work @@ -47,6 +51,9 @@ public class testBookmarklets extends BaseTest { } }, 3000); mAsserter.is(alerted, true, "Alert was shown for clicked bookmarklet"); + + // remove the bookmarklet + removeBookmarklet(js); } private void addOrUpdateBookmarklet(String title, String url) { @@ -77,4 +84,12 @@ public class testBookmarklets extends BaseTest { if (updated == 0) resolver.insert(bookmarksUri, values); } + + private void removeBookmarklet(String url) { + ContentResolver resolver = getActivity().getContentResolver(); + Uri bookmarksUri = Uri.parse("content://@ANDROID_PACKAGE_NAME@.db.browser/bookmarks"); + bookmarksUri = bookmarksUri.buildUpon().appendQueryParameter("profile", "default").build(); + + resolver.delete(bookmarksUri, "url = ?", new String[] { url }); + } } diff --git a/mobile/android/base/tests/testBrowserProvider.java.in b/mobile/android/base/tests/testBrowserProvider.java.in index 518a9706ee4c..e3c55293d4ae 100644 --- a/mobile/android/base/tests/testBrowserProvider.java.in +++ b/mobile/android/base/tests/testBrowserProvider.java.in @@ -88,6 +88,11 @@ public class testBrowserProvider extends ContentProviderTest { private int mCombinedDisplayNormal; private int mCombinedDisplayReader; + @Override + protected int getTestType() { + return TEST_MOCHITEST; + } + private void loadContractInfo() throws Exception { mBookmarksUri = getContentUri("Bookmarks"); mHistoryUri = getContentUri("History"); @@ -320,8 +325,6 @@ public class testBrowserProvider extends ContentProviderTest { } public void testBrowserProvider() throws Exception { - setTestType("mochitest"); - loadMobileFolderId(); for (int i = 0; i < mTests.size(); i++) { diff --git a/mobile/android/base/tests/testBrowserProviderPerf.java.in b/mobile/android/base/tests/testBrowserProviderPerf.java.in index b7c63d403ce3..0abcdab08ae2 100644 --- a/mobile/android/base/tests/testBrowserProviderPerf.java.in +++ b/mobile/android/base/tests/testBrowserProviderPerf.java.in @@ -58,6 +58,11 @@ public class testBrowserProviderPerf extends ContentProviderTest { private String mHistoryThumbnailCol; private String mHistoryLastVisitedCol; + @Override + protected int getTestType() { + return TEST_TALOS; + } + private void loadFilterMethod() throws Exception { Class browserDBClass = mClassLoader.loadClass("org.mozilla.gecko.db.BrowserDB"); @@ -232,8 +237,6 @@ public class testBrowserProviderPerf extends ContentProviderTest { } public void testBrowserProviderPerf() throws Exception { - setTestType("talos"); - initializeBrowserProvider(); loadMobileFolderId(); diff --git a/mobile/android/base/tests/testCheck.java.in b/mobile/android/base/tests/testCheck.java.in index cb559bcab586..4d19b2412fd8 100644 --- a/mobile/android/base/tests/testCheck.java.in +++ b/mobile/android/base/tests/testCheck.java.in @@ -12,8 +12,12 @@ public class testCheck extends PixelTest { } } + @Override + protected int getTestType() { + return TEST_TALOS; + } + public void testCheck() { - setTestType("talos"); String url = getAbsoluteUrl("/startup_test/fennecmark/timecube.html"); mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); diff --git a/mobile/android/base/tests/testCheck2.java.in b/mobile/android/base/tests/testCheck2.java.in index c2d57b6e86d5..0df315a0f183 100644 --- a/mobile/android/base/tests/testCheck2.java.in +++ b/mobile/android/base/tests/testCheck2.java.in @@ -4,8 +4,12 @@ package @ANDROID_PACKAGE_NAME@.tests; import @ANDROID_PACKAGE_NAME@.*; public class testCheck2 extends PixelTest { + @Override + protected int getTestType() { + return TEST_TALOS; + } + public void testCheck2() { - setTestType("talos"); String url = getAbsoluteUrl("/startup_test/fennecmark/cnn/cnn.com/index.html"); mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); diff --git a/mobile/android/base/tests/testCheck3.java.in b/mobile/android/base/tests/testCheck3.java.in index f1b34eaab649..99eb4c1e4fc2 100644 --- a/mobile/android/base/tests/testCheck3.java.in +++ b/mobile/android/base/tests/testCheck3.java.in @@ -5,8 +5,12 @@ import @ANDROID_PACKAGE_NAME@.*; import java.lang.reflect.Method; public class testCheck3 extends PixelTest { + @Override + protected int getTestType() { + return TEST_TALOS; + } + public void testCheck3() { - setTestType("talos"); String url = getAbsoluteUrl("/startup_test/fennecmark/cnn/cnn.com/index.html"); // Disable Fennec's low-res screenshot for the duration of this test; // this distinguishes this test from testCheck2, which is otherwise diff --git a/mobile/android/base/tests/testFlingCorrectness.java.in b/mobile/android/base/tests/testFlingCorrectness.java.in index 8cbfcd26f7f6..9a1e1d1352ad 100644 --- a/mobile/android/base/tests/testFlingCorrectness.java.in +++ b/mobile/android/base/tests/testFlingCorrectness.java.in @@ -11,8 +11,12 @@ import android.app.Instrumentation; * - Fling the page downwards so we get back to the top and verify. */ public class testFlingCorrectness extends PixelTest { + @Override + protected int getTestType() { + return TEST_MOCHITEST; + } + public void testFlingCorrectness() { - setTestType("mochitest"); String url = getAbsoluteUrl("/robocop/robocop_boxes.html"); MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mDriver.getGeckoLeft(), mDriver.getGeckoTop()); diff --git a/mobile/android/base/tests/testFormHistory.java.in b/mobile/android/base/tests/testFormHistory.java.in index cee4e549d49a..46c53d787886 100644 --- a/mobile/android/base/tests/testFormHistory.java.in +++ b/mobile/android/base/tests/testFormHistory.java.in @@ -21,8 +21,13 @@ import java.util.ArrayList; */ public class testFormHistory extends BaseTest { private static final String DB_NAME = "formhistory.sqlite"; + + @Override + protected int getTestType() { + return TEST_MOCHITEST; + } + public void testFormHistory() { - setTestType("mochitest"); Context context = (Context)getActivity(); ContentResolver cr = context.getContentResolver(); ContentValues[] cvs = new ContentValues[1]; diff --git a/mobile/android/base/tests/testJarReader.java.in b/mobile/android/base/tests/testJarReader.java.in index 346b625f6bc7..a3b105c1eb8b 100644 --- a/mobile/android/base/tests/testJarReader.java.in +++ b/mobile/android/base/tests/testJarReader.java.in @@ -19,8 +19,12 @@ import java.io.StringWriter; * as loading some invalid jar urls. */ public class testJarReader extends BaseTest { + @Override + protected int getTestType() { + return TEST_MOCHITEST; + } + public void testJarReader() { - setTestType("mochitest"); try { ClassLoader classLoader = getActivity().getClassLoader(); Class gjrClass = classLoader.loadClass("org.mozilla.gecko.GeckoJarReader"); diff --git a/mobile/android/base/tests/testLoad.java.in b/mobile/android/base/tests/testLoad.java.in index e4f34edc7d32..26d7b080be4a 100644 --- a/mobile/android/base/tests/testLoad.java.in +++ b/mobile/android/base/tests/testLoad.java.in @@ -10,8 +10,12 @@ import @ANDROID_PACKAGE_NAME@.*; * - verifies the displayed url is correct */ public class testLoad extends PixelTest { + @Override + protected int getTestType() { + return TEST_MOCHITEST; + } + public void testLoad() { - setTestType("mochitest"); String url = getAbsoluteUrl("/robocop/robocop_boxes.html"); mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); diff --git a/mobile/android/base/tests/testMigration.java.in b/mobile/android/base/tests/testMigration.java.in new file mode 100644 index 000000000000..837142150655 --- /dev/null +++ b/mobile/android/base/tests/testMigration.java.in @@ -0,0 +1,325 @@ +#filter substitution +package @ANDROID_PACKAGE_NAME@.tests; + +import @ANDROID_PACKAGE_NAME@.*; +import android.app.Activity; +import android.content.ContentValues; +import android.content.ContentResolver; +import android.content.res.AssetManager; +import android.database.Cursor; +import android.content.Context; +import android.net.Uri; +import android.provider.Browser; +import android.util.Log; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.ClassLoader; +import java.lang.reflect.Method; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * Tests the profile migration. Unpacks an old database, moves + * it to the profile, then call the BrowserProvider Control URI + * to launch a migration and check the results. + */ +public class testMigration extends ContentProviderTest { + // Big files are zipped to allow us to manually extract them, + // the APK extractor will fail >1Mb. + private static final String ASSET_SUFFIX = ".zip"; + private static final String DB_NAME = "places.sqlite"; + + @Override + protected int getTestType() { + return TEST_MOCHITEST; + } + + private File extractAsset(String dbName) { + File oldDbLocation = null; + boolean foundFile = false; + String fullAssetName = dbName + ASSET_SUFFIX; + + AssetManager assets = getAssetManager(); + try { + String[] assetList = assets.list(""); + String assetString = ""; + for (String file: assetList) { + assetString = assetString.concat(file); + assetString = assetString.concat(" "); + if (file.equals(fullAssetName)) { + foundFile = true; + } + } + mAsserter.is(foundFile, true, fullAssetName + " found in assets: " + assetString); + } catch (IOException e) { + String stackTrace = Log.getStackTraceString(e); + mAsserter.is(false, true, "Error getting asset dir: " + stackTrace); + } + + // Extract the old places database from assets + // and write it out in the profile directory + try { + InputStream profData = assets.open(fullAssetName); + File profile = new File(mProfile); + oldDbLocation = new File(profile, dbName); + OutputStream out = new FileOutputStream(oldDbLocation); + + ZipInputStream zis = new ZipInputStream(new BufferedInputStream(profData)); + try { + ZipEntry ze; + while ((ze = zis.getNextEntry()) != null) { + byte[] buffer = new byte[1024]; + int count; + while ((count = zis.read(buffer)) != -1) { + out.write(buffer, 0, count); + } + String fileName = ze.getName(); + mAsserter.is(fileName, DB_NAME, "Filename match: " + fileName); + } + } finally { + zis.close(); + } + + profData.close(); + out.flush(); + out.close(); + } catch (IOException e) { + String stackTrace = Log.getStackTraceString(e); + mAsserter.is(false, true, "Error getting profile data: " + stackTrace); + } + + return oldDbLocation; + } + + public void testMigration() { + Context context = (Context)getActivity(); + + File oldDbLocation = extractAsset(DB_NAME); + mAsserter.is(oldDbLocation.exists(), true, "OK old file exists: " + + oldDbLocation.toString()); + + // Set up BrowserDB + try { + Class browserDBClass = + mClassLoader.loadClass("org.mozilla.gecko.db.BrowserDB"); + Method initializeMethod = + browserDBClass.getDeclaredMethod("initialize", String.class); + initializeMethod.invoke(null, "default"); + } catch (Exception ex) { + mAsserter.is(true, false, "Error setting up BrowserDB: " + + ex.getMessage()); + return; + } + + // Use reflection to look up the ProfileMigrator things we need + // access to (constructor, launch), as well as BrowserContract.Control. + Method launchPlacesTest; + Constructor constructor; + Class pmClass; + + try { + pmClass = mClassLoader.loadClass("org.mozilla.gecko.ProfileMigrator"); + launchPlacesTest = pmClass.getMethod("launchPlacesTest", File.class); + constructor = pmClass.getConstructor(Context.class, ContentResolver.class); + } catch(ClassNotFoundException ex) { + mAsserter.is(false, true, "Error getting class: " + ex.getMessage()); + return; + } catch (java.lang.NoSuchMethodException ex) { + mAsserter.is(true, false, "Unable to find method: " + ex.getMessage()); + return; + } + + Object pm = null; + try { + // Construct ProfileMigrator object + pm = constructor.newInstance(context, mResolver); + } catch (Exception ex) { + mAsserter.is(true, false, "Error instantiating ProfileMigrator instance: " + + ex.getMessage()); + return; + } + + // Reset history entries from previous tests + clearHistory(); + + // Launch the Profile Migration + try { + launchPlacesTest.invoke(pm, new File(mProfile)); + } catch (Exception ex) { + String stackTrace = Log.getStackTraceString(ex); + mAsserter.is(true, false, "Unable to invoke launchPlacesTest:" + stackTrace); + return; + } + + // Run the tests to see if that worked + testViaContentProvider(); + testViaBrowserDB(); + + mAsserter.is(oldDbLocation.exists(), false, "OK old file gone now"); + } + + public void clearHistory() { + Method clearHistory = null; + + try { + Class browserDB = + mClassLoader.loadClass("org.mozilla.gecko.db.BrowserDB"); + clearHistory = + browserDB.getMethod("clearHistory", ContentResolver.class); + } catch (java.lang.ClassNotFoundException ex) { + mAsserter.is(true, false, "Unable to find class"); + return; + } catch (java.lang.NoSuchMethodException ex) { + mAsserter.is(true, false, "Unable to find method"); + return; + } + + try { + clearHistory.invoke(null, mResolver); + } catch (Exception ex) { + String stackTrace = Log.getStackTraceString(ex); + mAsserter.is(true, false, "Exception clearing history:" + stackTrace); + } + } + + // stolen from testBookmarks + public void testViaBrowserDB() { + Method isBookmarked = null; + Method getAllVisitedHistory = null; + Constructor constructor = null; + + try { + Class localBrowserDB = + mClassLoader.loadClass("org.mozilla.gecko.db.LocalBrowserDB"); + isBookmarked = + localBrowserDB.getMethod("isBookmark", ContentResolver.class, String.class); + getAllVisitedHistory = + localBrowserDB.getMethod("getAllVisitedHistory", ContentResolver.class); + constructor = localBrowserDB.getConstructor(String.class); + } catch (java.lang.ClassNotFoundException ex) { + mAsserter.is(true, false, "Unable to find class"); + return; + } catch (java.lang.NoSuchMethodException ex) { + mAsserter.is(true, false, "Unable to find method:" + ex.getMessage()); + return; + } + + Object db = null; + try { + String defaultProfile = "default"; + db = constructor.newInstance(defaultProfile); + } catch (Exception ex) { + mAsserter.is(true, false, "Error instantiating LocalBrowserDB instance: " + + ex.getMessage()); + return; + } + + final String[] knownBookmarks = new String[] { + "http://www.androidpolice.com/", + "https://developer.mozilla.org/En/Mozilla_Coding_Style_Guide", + "http://planet.mozilla.org/", + "http://www.crockford.com/", + "https://wiki.mozilla.org/Mobile/Fennec/Android" + }; + final String ABOUT_HOME_URL = "about:home"; + + for (String url: knownBookmarks) { + try { + // Check for some bookmarks we know must exist + boolean isbookmark = + (Boolean)isBookmarked.invoke(db, + mResolver, + url); + mAsserter.is(isbookmark, true, "Expected page is bookmarked: " + url); + } catch (Exception ex) { + mAsserter.is(true, false, "Exception checking bookmark existence"); + ex.printStackTrace(); + return; + } + } + + // Check the amount of history, this should match the following on the + // database in assets: + // SELECT COUNT(DISTINCT(url)) FROM moz_historyvisits + // JOIN moz_places ON moz_historyvisits.place_id = moz_places.id + final int PLACES_KNOWN_HISTORY_URLS = 184; + try { + Cursor cursor = + (Cursor)getAllVisitedHistory.invoke(db, + mResolver); + int historyCount = cursor.getCount(); + cursor.close(); + mAsserter.is(historyCount, PLACES_KNOWN_HISTORY_URLS, + "History count " + historyCount + + ", expected was " + PLACES_KNOWN_HISTORY_URLS); + } catch (Exception ex) { + mAsserter.is(true, false, "Exception checking history count"); + ex.printStackTrace(); + } + } + + public void testViaContentProvider() { + String ensureHistory; + String ensureBookmarks; + + Uri controlUri; + try { + Class browserContract = + mClassLoader.loadClass("org.mozilla.gecko.db.BrowserContract"); + Class browserContractControl = + mClassLoader.loadClass("org.mozilla.gecko.db.BrowserContract$Control"); + controlUri = (Uri)browserContractControl.getField("CONTENT_URI").get(null); + String profilePath = (String)browserContract.getField("PARAM_PROFILE_PATH").get(null); + Uri.Builder builder = controlUri.buildUpon(); + controlUri = builder.build(); + ensureHistory = + (String)browserContractControl.getField("ENSURE_HISTORY_MIGRATED").get(null); + ensureBookmarks = + (String)browserContractControl.getField("ENSURE_BOOKMARKS_MIGRATED").get(null); + } catch (Exception ex) { + mAsserter.is(true, false, "Reflection error getting Control Uri"); + ex.printStackTrace(); + return; + } + + Cursor c = mResolver.query(controlUri, + new String[] { ensureHistory, + ensureBookmarks }, + null, + null, + null); + + int historyMigrated = 0; + int bookmarksMigrated = 0; + + if (c.moveToFirst()) { + historyMigrated = c.getInt(0); + bookmarksMigrated = c.getInt(1); + } + c.close(); + + mAsserter.is(historyMigrated, 1, "History migrated"); + mAsserter.is(bookmarksMigrated, 1, "Bookmarks migrated"); + } + + @Override + public void setUp() throws Exception { + super.setUp("@ANDROID_PACKAGE_NAME@.db.BrowserProvider", "AUTHORITY"); + } + + public void tearDown() throws Exception { + // remove the database file + File profile = new File(mProfile); + File db = new File(profile, DB_NAME); + db.delete(); + + super.tearDown(); + } +} diff --git a/mobile/android/base/tests/testNewTab.java.in b/mobile/android/base/tests/testNewTab.java.in index 3fcfef0a3a67..b9448c1a84c4 100644 --- a/mobile/android/base/tests/testNewTab.java.in +++ b/mobile/android/base/tests/testNewTab.java.in @@ -14,9 +14,12 @@ public class testNewTab extends BaseTest { private Element closeTab = null; private int tabCountInt = 0; - public void testNewTab() { - setTestType("mochitest"); + @Override + protected int getTestType() { + return TEST_MOCHITEST; + } + public void testNewTab() { String url = getAbsoluteUrl("/robocop/robocop_blank_01.html"); String url2 = getAbsoluteUrl("/robocop/robocop_blank_02.html"); diff --git a/mobile/android/base/tests/testOverscroll.java.in b/mobile/android/base/tests/testOverscroll.java.in index 349312fcb4c8..834b0eabb38c 100644 --- a/mobile/android/base/tests/testOverscroll.java.in +++ b/mobile/android/base/tests/testOverscroll.java.in @@ -11,8 +11,12 @@ import android.app.Instrumentation; * - Drag page rightwards by 100 pixels into overscroll, verify it snaps back. */ public class testOverscroll extends PixelTest { + @Override + protected int getTestType() { + return TEST_MOCHITEST; + } + public void testOverscroll() { - setTestType("mochitest"); String url = getAbsoluteUrl("/robocop/robocop_boxes.html"); MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mDriver.getGeckoLeft(), mDriver.getGeckoTop()); diff --git a/mobile/android/base/tests/testPan.java.in b/mobile/android/base/tests/testPan.java.in index 79e6a9098af8..38b29a065baa 100644 --- a/mobile/android/base/tests/testPan.java.in +++ b/mobile/android/base/tests/testPan.java.in @@ -9,9 +9,12 @@ import @ANDROID_PACKAGE_NAME@.*; * that fennec draws at. */ public class testPan extends PixelTest { + @Override + protected int getTestType() { + return TEST_TALOS; + } public void testPan() { - setTestType("talos"); String url = getAbsoluteUrl("/startup_test/fennecmark/wikipedia.html"); mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); diff --git a/mobile/android/base/tests/testPanCorrectness.java.in b/mobile/android/base/tests/testPanCorrectness.java.in index 8657069b6944..26827b5d4dfd 100644 --- a/mobile/android/base/tests/testPanCorrectness.java.in +++ b/mobile/android/base/tests/testPanCorrectness.java.in @@ -11,8 +11,12 @@ import android.app.Instrumentation; * - drags page leftwards by 100 pixels and verifies it draws */ public class testPanCorrectness extends PixelTest { + @Override + protected int getTestType() { + return TEST_MOCHITEST; + } + public void testPanCorrectness() { - setTestType("mochitest"); String url = getAbsoluteUrl("/robocop/robocop_boxes.html"); MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mDriver.getGeckoLeft(), mDriver.getGeckoTop()); diff --git a/mobile/android/base/tests/testPasswordEncrypt.java.in b/mobile/android/base/tests/testPasswordEncrypt.java.in index 425857211c42..01a748e62988 100644 --- a/mobile/android/base/tests/testPasswordEncrypt.java.in +++ b/mobile/android/base/tests/testPasswordEncrypt.java.in @@ -16,8 +16,12 @@ import org.json.JSONException; import org.json.JSONObject; public class testPasswordEncrypt extends BaseTest { + @Override + protected int getTestType() { + return TEST_MOCHITEST; + } + public void testPasswordEncrypt() { - setTestType("mochitest"); Context context = (Context)getActivity(); ContentResolver cr = context.getContentResolver(); mAsserter.isnot(cr, null, "Found a content resolver"); diff --git a/mobile/android/base/tests/testPasswordProvider.java.in b/mobile/android/base/tests/testPasswordProvider.java.in index 43359f1cc375..1fcd846d5f32 100644 --- a/mobile/android/base/tests/testPasswordProvider.java.in +++ b/mobile/android/base/tests/testPasswordProvider.java.in @@ -19,8 +19,13 @@ import java.io.File; */ public class testPasswordProvider extends BaseTest { private static final String DB_NAME = "signons.sqlite"; + + @Override + protected int getTestType() { + return TEST_MOCHITEST; + } + public void testPasswordProvider() { - setTestType("mochitest"); Context context = (Context)getActivity(); ContentResolver cr = context.getContentResolver(); ContentValues[] cvs = new ContentValues[1]; diff --git a/mobile/android/base/tests/testPermissions.java.in b/mobile/android/base/tests/testPermissions.java.in index 28ae411b281d..a398c6348fb1 100644 --- a/mobile/android/base/tests/testPermissions.java.in +++ b/mobile/android/base/tests/testPermissions.java.in @@ -10,8 +10,12 @@ public class testPermissions extends PixelTest { private PaintedSurface mPaintedSurface; private Actions.RepeatedEventExpecter mPaintExpecter; + @Override + protected int getTestType() { + return TEST_MOCHITEST; + } + public void testPermissions() { - setTestType("mochitest"); mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); geolocationTest(); diff --git a/mobile/android/base/tests/testSearchSuggestions.java.in b/mobile/android/base/tests/testSearchSuggestions.java.in index 1b9fc88b64ee..a5e575edd260 100644 --- a/mobile/android/base/tests/testSearchSuggestions.java.in +++ b/mobile/android/base/tests/testSearchSuggestions.java.in @@ -26,8 +26,12 @@ public class testSearchSuggestions extends BaseTest { private static final String TEST_QUERY = "foo barz"; private static final String SUGGESTION_TEMPLATE = "/robocop/robocop_suggestions.sjs?query=__searchTerms__"; + @Override + protected int getTestType() { + return TEST_MOCHITEST; + } + public void testSearchSuggestions() { - setTestType("mochitest"); mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); // Map of expected values. See robocop_suggestions.sjs. diff --git a/mobile/android/base/tests/testWebContentContextMenu.java.in b/mobile/android/base/tests/testWebContentContextMenu.java.in index 33de5b6296b7..b9f6ebf32e82 100644 --- a/mobile/android/base/tests/testWebContentContextMenu.java.in +++ b/mobile/android/base/tests/testWebContentContextMenu.java.in @@ -6,8 +6,12 @@ import android.app.Activity; import android.util.DisplayMetrics; public class testWebContentContextMenu extends BaseTest { + @Override + protected int getTestType() { + return TEST_MOCHITEST; + } + public void testWebContentContextMenu() { - setTestType("mochitest"); mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); DisplayMetrics dm = new DisplayMetrics(); diff --git a/mobile/android/base/tests/test_bug720538.java.in b/mobile/android/base/tests/test_bug720538.java.in index 3ef959089800..2b94db9647be 100644 --- a/mobile/android/base/tests/test_bug720538.java.in +++ b/mobile/android/base/tests/test_bug720538.java.in @@ -5,8 +5,12 @@ import @ANDROID_PACKAGE_NAME@.*; import android.app.Instrumentation; public class test_bug720538 extends PixelTest { + @Override + protected int getTestType() { + return TEST_MOCHITEST; + } + public void test_bug720538() { - setTestType("mochitest"); String url = getAbsoluteUrl("/robocop/test_bug720538.html"); mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); diff --git a/mobile/android/chrome/content/HelperApps.js b/mobile/android/chrome/content/HelperApps.js new file mode 100644 index 000000000000..6b7f642b21a7 --- /dev/null +++ b/mobile/android/chrome/content/HelperApps.js @@ -0,0 +1,60 @@ +/* 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/. */ +"use strict"; + + +var HelperApps = { + get defaultHttpHandlers() { + let protoHandlers = this.getAppsForProtocol("http"); + + var results = {}; + for (var i = 0; i < protoHandlers.length; i++) { + try { + let protoApp = protoHandlers.queryElementAt(i, Ci.nsIHandlerApp); + results[protoApp.name] = protoApp; + } catch(e) {} + } + + delete this.defaultHttpHandlers; + return this.defaultHttpHandlers = results; + }, + + get protoSvc() { + delete this.protoSvc; + return this.protoSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"].getService(Ci.nsIExternalProtocolService); + }, + + get urlHandlerService() { + delete this.urlHandlerService; + return this.urlHandlerService = Cc["@mozilla.org/uriloader/external-url-handler-service;1"].getService(Ci.nsIExternalURLHandlerService); + }, + + getAppsForProtocol: function getAppsForProtocol(uri) { + let handlerInfoProto = this.protoSvc.getProtocolHandlerInfoFromOS(uri, {}); + return handlerInfoProto.possibleApplicationHandlers; + }, + + getAppsForUri: function getAppsFor(uri) { + let found = []; + let handlerInfoProto = this.urlHandlerService.getURLHandlerInfoFromOS(uri, {}); + let urlHandlers = handlerInfoProto.possibleApplicationHandlers; + for (var i = 0; i < urlHandlers.length; i++) { + let urlApp = urlHandlers.queryElementAt(i, Ci.nsIHandlerApp); + if (!this.defaultHttpHandlers[urlApp.name]) { + found.push(urlApp); + } + } + return found; + }, + + openUriInApp: function openUriInApp(uri) { + var possibleHandlers = this.getAppsForUri(uri); + if (possibleHandlers.length == 1) { + possibleHandlers[0].launchWithURI(uri); + } else if (possibleHandlers.length > 0) { + let handlerInfoProto = this.urlHandlerService.getURLHandlerInfoFromOS(uri, {}); + handlerInfoProto.preferredApplicationHandler.launchWithURI(uri); + } + } +}; diff --git a/mobile/android/chrome/content/aboutApps.js b/mobile/android/chrome/content/aboutApps.js index 518a1534f19b..3b605d90d52f 100644 --- a/mobile/android/chrome/content/aboutApps.js +++ b/mobile/android/chrome/content/aboutApps.js @@ -44,7 +44,7 @@ function onLoad(aEvent) { AppsUI.shortcut = contextmenus.add(gStrings.GetStringFromName("appsContext.shortcut"), contextmenus.SelectorContext("div[mozApp]"), function(aTarget) { let manifest = aTarget.manifest; - gChromeWin.WebappsUI.createShortcut(manifest.name, manifest.fullLaunchPath(), manifest.iconURLForSize("64"), "webapp"); + gChromeWin.WebappsUI.createShortcut(manifest.name, manifest.fullLaunchPath(), gChromeWin.WebappsUI.getBiggestIcon(manifest.icons), "webapp"); }); AppsUI.uninstall = contextmenus.add(gStrings.GetStringFromName("appsContext.uninstall"), contextmenus.SelectorContext("div[mozApp]"), function(aTarget) { @@ -97,7 +97,7 @@ function addApplication(aApp) { container.setAttribute("title", manifest.name); let img = document.createElement("img"); - img.src = manifest.iconURLForSize("64"); + img.src = gChromeWin.WebappsUI.getBiggestIcon(manifest.icons); img.setAttribute("title", manifest.name); let title = document.createElement("div"); diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 38920a3d3694..7dfc725232c9 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -10,7 +10,7 @@ let Cu = Components.utils; let Cr = Components.results; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm") +Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/AddonManager.jsm"); Cu.import("resource://gre/modules/FileUtils.jsm"); #ifdef ACCESSIBILITY @@ -29,6 +29,7 @@ XPCOMUtils.defineLazyGetter(this, "DebuggerServer", function() { // Lazily-loaded browser scripts: [ + ["HelperApps", "chrome://browser/content/HelperApps.js"], ["SelectHelper", "chrome://browser/content/SelectHelper.js"], ["Readability", "chrome://browser/content/Readability.js"], ].forEach(function (aScript) { @@ -154,7 +155,7 @@ var BrowserApp = { Services.obs.addObserver(this, "Preferences:Get", false); Services.obs.addObserver(this, "Preferences:Set", false); Services.obs.addObserver(this, "ScrollTo:FocusedInput", false); - Services.obs.addObserver(this, "Sanitize:ClearAll", false); + Services.obs.addObserver(this, "Sanitize:ClearData", false); Services.obs.addObserver(this, "PanZoom:PanZoom", false); Services.obs.addObserver(this, "FullScreen:Exit", false); Services.obs.addObserver(this, "Viewport:Change", false); @@ -780,6 +781,31 @@ var BrowserApp = { } }, + sanitize: function (aItems) { + let sanitizer = new Sanitizer(); + let json = JSON.parse(aItems); + let success = true; + + for (let key in json) { + if (!json[key]) + continue; + + try { + sanitizer.clearItem(key); + } catch (e) { + dump("sanitize error: " + e); + success = false; + } + } + + sendMessageToJava({ + gecko: { + type: "Sanitize:Finished", + success: success + } + }); + }, + scrollToFocusedInput: function(aBrowser) { let doc = aBrowser.contentDocument; if (!doc) @@ -896,8 +922,8 @@ var BrowserApp = { this.setPreferences(aData); } else if (aTopic == "ScrollTo:FocusedInput") { this.scrollToFocusedInput(browser); - } else if (aTopic == "Sanitize:ClearAll") { - Sanitizer.sanitize(); + } else if (aTopic == "Sanitize:ClearData") { + this.sanitize(aData); } else if (aTopic == "FullScreen:Exit") { browser.contentDocument.mozCancelFullScreen(); } else if (aTopic == "Viewport:Change") { @@ -1243,7 +1269,6 @@ var NativeWindow = { // only send the contextmenu event to content if we are planning to show a context menu (i.e. not on every long tap) if (this.menuitems) { - BrowserEventHandler.blockClick = true; let event = rootElement.ownerDocument.createEvent("MouseEvent"); event.initMouseEvent("contextmenu", true, true, content, 0, aX, aY, aX, aY, false, false, false, false, @@ -1423,9 +1448,16 @@ var SelectionHandler = { // aX/aY are in top-level window browser coordinates startSelection: function sh_startSelection(aElement, aX, aY) { - // Clear out any existing selection - if (this._active) + if (this._active) { + // If the user long tapped on the selection, show a context menu + if (this._pointInSelection(aX, aY)) { + this.showContextMenu(aX, aY); + return; + } + + // Clear out any existing selection this.endSelection(); + } // Get the element's view this._view = aElement.ownerDocument.defaultView; @@ -1477,6 +1509,53 @@ var SelectionHandler = { this._active = true; }, + showContextMenu: function sh_showContextMenu(aX, aY) { + let [SELECT_ALL, COPY, SHARE] = [0, 1, 2]; + let listitems = [ + { label: Strings.browser.GetStringFromName("contextmenu.selectAll"), id: SELECT_ALL }, + { label: Strings.browser.GetStringFromName("contextmenu.copy"), id: COPY }, + { label: Strings.browser.GetStringFromName("contextmenu.share"), id: SHARE } + ]; + + let msg = { + gecko: { + type: "Prompt:Show", + title: "", + listitems: listitems + } + }; + let id = JSON.parse(sendMessageToJava(msg)).button; + + switch (id) { + case SELECT_ALL: { + let selectionController = this._view.QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIWebNavigation). + QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsISelectionDisplay). + QueryInterface(Ci.nsISelectionController); + selectionController.selectAll(); + this.updateCacheForSelection(); + this.positionHandles(); + break; + } + case COPY: { + // Passing coordinates to endSelection takes care of copying for us + this.endSelection(aX, aY); + break; + } + case SHARE: { + let selectedText = this.endSelection(); + sendMessageToJava({ + gecko: { + type: "Share:Text", + text: selectedText + } + }); + break; + } + } + }, + // aX/aY are in top-level window browser coordinates moveSelection: function sh_moveSelection(aIsStartHandle, aX, aY) { /* XXX bug 765367: Because the handles are in the document, the element @@ -1608,26 +1687,21 @@ var SelectionHandler = { element = ElementTouchHelper.anyElementFromPoint(contentWindow, aX, aY); // Only try copying text if the tap happens in the same view - if (element.ownerDocument.defaultView == this._view) { - let offset = this._getViewOffset(); - let pointInSelection = (aX - offset.x > this.cache.rect.left && - aX - offset.x < this.cache.rect.right) && - (aY - offset.y > this.cache.rect.top && - aY - offset.y < this.cache.rect.bottom); - if (pointInSelection) { - let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper); - clipboard.copyString(selectedText); - NativeWindow.toast.show(Strings.browser.GetStringFromName("selectionHelper.textCopied"), "short"); - } + if (element.ownerDocument.defaultView == this._view && this._pointInSelection(aX, aY)) { + let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper); + clipboard.copyString(selectedText, element.ownerDocument); + NativeWindow.toast.show(Strings.browser.GetStringFromName("selectionHelper.textCopied"), "short"); } } this._isRTL = false; this._view = null; this.cache = null; + + return selectedText; }, - _getViewOffset: function sh_getViewOffset() { + _pointInSelection: function sh_pointInSelection(aX, aY) { let offset = { x: 0, y: 0 }; let win = this._view; @@ -1640,7 +1714,8 @@ var SelectionHandler = { win = win.parent; } - return offset; + return (aX - offset.x > this.cache.rect.left && aX - offset.x < this.cache.rect.right) && + (aY - offset.y > this.cache.rect.top && aY - offset.y < this.cache.rect.bottom); }, // Returns true if the selection has been reversed. Takes optional aIsStartHandle @@ -1825,18 +1900,31 @@ var UserAgent = { if (tab == null) break; - // Send XUL UA to YouTube; temporary hack to make videos play - if (channel.URI.host.indexOf("youtube") != -1) { - let ua = Cc["@mozilla.org/network/protocol;1?name=http"].getService(Ci.nsIHttpProtocolHandler).userAgent; -#expand let version = "__MOZ_APP_VERSION__"; - ua += " Fennec/" + version; - channel.setRequestHeader("User-Agent", ua, false); + let apps = HelperApps.getAppsForUri(channel.URI); + if (apps.length > 0) { + let message = apps.length == 1 ? Strings.browser.formatStringFromName("helperapps.openWithApp", [apps[0].name], 1) : + Strings.browser.GetStringFromName("helperapps.openWithList"); + let buttons = [{ + label: Strings.browser.GetStringFromName("helperapps.open"), + callback: function() { + aSubject.QueryInterface(Ci.nsIRequest).cancel(Components.results.NS_ERROR_ABORT); + HelperApps.openUriInApp(channel.URI); + } + }, + { + label: Strings.browser.GetStringFromName("helperapps.cancel"), + callback: function() { } + }]; + // Persist this over page loads. Pages that expect to open in helper apps often redirect + // Youtube redirects twice, so I've forced this to two for now + let options = { persistence: 2 }; + let name = "helperapps-" + (apps.length > 1 ? "list" : apps[0].name); + NativeWindow.doorhanger.show(message, name, buttons, self.id, options); } // Send desktop UA if "Request Desktop Site" is enabled if (tab.desktopMode && (channel.loadFlags & Ci.nsIChannel.LOAD_DOCUMENT_URI)) channel.setRequestHeader("User-Agent", this.DESKTOP_UA, false); - break; } } @@ -2323,11 +2411,17 @@ Tab.prototype = { let x = aViewport.x / aViewport.zoom; let y = aViewport.y / aViewport.zoom; - // Set scroll position + // Set scroll position and scroll-port clamping size + let [pageWidth, pageHeight] = this.getPageSize(this.browser.contentDocument, + aViewport.width, aViewport.height); + let scrollPortWidth = Math.min(gScreenWidth / aViewport.zoom, pageWidth); + let scrollPortHeight = Math.min(gScreenHeight / aViewport.zoom, pageHeight); + let win = this.browser.contentWindow; - win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).setScrollPositionClampingScrollPortSize( - gScreenWidth / aViewport.zoom, gScreenHeight / aViewport.zoom); + win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils). + setScrollPositionClampingScrollPortSize(scrollPortWidth, scrollPortHeight); win.scrollTo(x, y); + this.userScrollPos.x = win.scrollX; this.userScrollPos.y = win.scrollY; this.setResolution(aViewport.zoom, false); @@ -4792,9 +4886,9 @@ var ClipboardHelper = { let selectionEnd = aElement.selectionEnd; if (selectionStart != selectionEnd) { string = aElement.value.slice(selectionStart, selectionEnd); - this.clipboardHelper.copyString(string); + this.clipboardHelper.copyString(string, aElement.ownerDocument); } else { - this.clipboardHelper.copyString(aElement.value); + this.clipboardHelper.copyString(aElement.value, aElement.ownerDocument); } }, @@ -5043,7 +5137,7 @@ var PermissionsHelper = { // If we implement a two-line UI, we will need to pass the label and // value individually and let java handle the formatting let setting = Strings.browser.formatStringFromName("siteSettings.labelToValue", - [ label, valueString ], 2) + [ label, valueString ], 2); permissions.push({ type: type, setting: setting @@ -5508,34 +5602,42 @@ var SearchEngines = { NativeWindow.contextmenus.remove(this._contextMenuId); }, + _handleSearchEnginesGet: function _handleSearchEnginesGet(rv) { + if (!Components.isSuccessCode(rv)) { + Cu.reportError("Could not initialize search service, bailing out."); + return; + } + let engineData = Services.search.getVisibleEngines({}); + let searchEngines = engineData.map(function (engine) { + return { + name: engine.name, + iconURI: (engine.iconURI ? engine.iconURI.spec : null) + }; + }); + + let suggestTemplate = null; + let suggestEngine = null; + if (Services.prefs.getBoolPref("browser.search.suggest.enabled")) { + let engine = this.getSuggestionEngine(); + if (engine != null) { + suggestEngine = engine.name; + suggestTemplate = engine.getSubmission("__searchTerms__", "application/x-suggestions+json").uri.spec; + } + } + + sendMessageToJava({ + gecko: { + type: "SearchEngines:Data", + searchEngines: searchEngines, + suggestEngine: suggestEngine, + suggestTemplate: suggestTemplate + } + }); + }, + observe: function observe(aSubject, aTopic, aData) { if (aTopic == "SearchEngines:Get") { - let engineData = Services.search.getVisibleEngines({}); - let searchEngines = engineData.map(function (engine) { - return { - name: engine.name, - iconURI: (engine.iconURI ? engine.iconURI.spec : null) - }; - }); - - let suggestTemplate = null; - let suggestEngine = null; - if (Services.prefs.getBoolPref("browser.search.suggest.enabled")) { - let engine = this.getSuggestionEngine(); - if (engine != null) { - suggestEngine = engine.name; - suggestTemplate = engine.getSubmission("__searchTerms__", "application/x-suggestions+json").uri.spec; - } - } - - sendMessageToJava({ - gecko: { - type: "SearchEngines:Data", - searchEngines: searchEngines, - suggestEngine: suggestEngine, - suggestTemplate: suggestTemplate - } - }); + Services.search.init(this._handleSearchEnginesGet.bind(this)); } }, @@ -5608,26 +5710,32 @@ var SearchEngines = { stmts[0] = mDBConn.createStatement("SELECT favicon FROM images WHERE url_key = ?"); stmts[0].bindStringParameter(0, docURI.spec); let favicon = null; - mDBConn.executeAsync(stmts, stmts.length, { - handleResult: function (results) { - let bytes = results.getNextRow().getResultByName("favicon"); - favicon = "data:image/png;base64," + btoa(String.fromCharCode.apply(null, bytes)); - }, - handleCompletion: function (reason) { - // if there's already an engine with this name, add a number to - // make the name unique (e.g., "Google" becomes "Google 2") - let name = title.value; - for (let i = 2; Services.search.getEngineByName(name); i++) - name = title.value + " " + i; - - Services.search.addEngineWithDetails(name, favicon, null, null, method, formURL); - let engine = Services.search.getEngineByName(name); - engine.wrappedJSObject._queryCharset = charset; - for each (let param in formData) { - if (param.name && param.value) - engine.addParam(param.name, param.value, null); - } + Services.search.init(function addEngine_cb(rv) { + if (!Components.isSuccessCode(rv)) { + Cu.reportError("Could not initialize search service, bailing out."); + return; } + mDBConn.executeAsync(stmts, stmts.length, { + handleResult: function (results) { + let bytes = results.getNextRow().getResultByName("favicon"); + favicon = "data:image/png;base64," + btoa(String.fromCharCode.apply(null, bytes)); + }, + handleCompletion: function (reason) { + // if there's already an engine with this name, add a number to + // make the name unique (e.g., "Google" becomes "Google 2") + let name = title.value; + for (let i = 2; Services.search.getEngineByName(name); i++) + name = title.value + " " + i; + + Services.search.addEngineWithDetails(name, favicon, null, null, method, formURL); + let engine = Services.search.getEngineByName(name); + engine.wrappedJSObject._queryCharset = charset; + for each (let param in formData) { + if (param.name && param.value) + engine.addParam(param.name, param.value, null); + } + } + }); }); } }; @@ -5696,7 +5804,7 @@ var WebappsUI = { // Add a homescreen shortcut -- we can't use createShortcut, since we need to pass // a unique ID for Android webapp allocation - this.makeBase64Icon(manifest.iconURLForSize("64"), + this.makeBase64Icon(this.getBiggestIcon(manifest.icons), function(icon) { sendMessageToJava({ gecko: { @@ -5732,7 +5840,18 @@ var WebappsUI = { break; } }, + + getBiggestIcon: function getBiggestIcon(aIcons) { + if (!aIcons) + return "chrome://browser/skin/images/default-app-icon.png"; + let iconSizes = Object.keys(aIcons); + if (iconSizes.length == 0) + return "chrome://browser/skin/images/default-app-icon.png"; + iconSizes.sort(function(a, b) a - b); + return aIcons[iconSizes.pop()]; + }, + doInstall: function doInstall(aData) { let manifest = new DOMApplicationManifest(aData.app.manifest, aData.app.origin); let name = manifest.name ? manifest.name : manifest.fullLaunchPath(); diff --git a/mobile/android/chrome/jar.mn b/mobile/android/chrome/jar.mn index 3ad91c4c4d53..b0b60f415a1f 100644 --- a/mobile/android/chrome/jar.mn +++ b/mobile/android/chrome/jar.mn @@ -36,6 +36,7 @@ chrome.jar: * content/downloads.js (content/downloads.js) content/netError.xhtml (content/netError.xhtml) content/SelectHelper.js (content/SelectHelper.js) + content/HelperApps.js (content/HelperApps.js) content/dbg-browser-actors.js (content/dbg-browser-actors.js) % override chrome://global/content/config.xul chrome://browser/content/config.xhtml diff --git a/mobile/android/locales/en-US/chrome/browser.properties b/mobile/android/locales/en-US/chrome/browser.properties index b1230dd78fc5..743206457959 100644 --- a/mobile/android/locales/en-US/chrome/browser.properties +++ b/mobile/android/locales/en-US/chrome/browser.properties @@ -208,6 +208,7 @@ selectionHelper.textCopied=Text copied to clipboard # Context menu contextmenu.openInNewTab=Open Link in New Tab +contextmenu.share=Share contextmenu.shareLink=Share Link contextmenu.bookmarkLink=Bookmark Link contextmenu.changeInputMethod=Select Input Method @@ -264,3 +265,9 @@ remoteIncomingPromptTitle=Incoming Connection # dialog that prompts the user to allow the incoming connection. remoteIncomingPromptMessage=An incoming request to permit remote debugging connection was detected. A remote client can take complete control over your browser! Allow connection? remoteIncomingPromptDisable=Disable + +# Helper apps +helperapps.openWithApp=The %S App can open this link. +helperapps.openWithList=You have apps installed that can open this link. +helperapps.open=Open with App +helperapps.cancel=Cancel diff --git a/mobile/android/themes/core/images/default-app-icon.png b/mobile/android/themes/core/images/default-app-icon.png new file mode 100644 index 000000000000..7c5e48cb8bea Binary files /dev/null and b/mobile/android/themes/core/images/default-app-icon.png differ diff --git a/mobile/android/themes/core/jar.mn b/mobile/android/themes/core/jar.mn index 426f75404f07..a664a9e9caa9 100644 --- a/mobile/android/themes/core/jar.mn +++ b/mobile/android/themes/core/jar.mn @@ -32,6 +32,7 @@ chrome.jar: skin/images/checkbox_unchecked.png (images/checkbox_unchecked.png) skin/images/checkbox_unchecked_disabled.png (images/checkbox_unchecked_disabled.png) skin/images/checkbox_unchecked_pressed.png (images/checkbox_unchecked_pressed.png) + skin/images/default-app-icon.png (images/default-app-icon.png) skin/images/dropmarker.svg (images/dropmarker.svg) skin/images/errorpage-warning.png (images/errorpage-warning.png) skin/images/errorpage-warning.png (images/errorpage-warning.png) diff --git a/mobile/xul/chrome/content/ContextCommands.js b/mobile/xul/chrome/content/ContextCommands.js index 988e87f7560a..c82806414fa9 100644 --- a/mobile/xul/chrome/content/ContextCommands.js +++ b/mobile/xul/chrome/content/ContextCommands.js @@ -6,7 +6,7 @@ var ContextCommands = { copy: function cc_copy() { let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper); - clipboard.copyString(ContextHelper.popupState.string); + clipboard.copyString(ContextHelper.popupState.string, Browser.contentWindow.document); let target = ContextHelper.popupState.target; if (target) @@ -72,22 +72,22 @@ var ContextCommands = { copyLink: function cc_copyLink() { let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper); - clipboard.copyString(ContextHelper.popupState.linkURL); + clipboard.copyString(ContextHelper.popupState.linkURL, Browser.contentWindow.document); }, copyEmail: function cc_copyEmail() { let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper); - clipboard.copyString(ContextHelper.popupState.linkURL.substr(ContextHelper.popupState.linkURL.indexOf(':')+1)); + clipboard.copyString(ContextHelper.popupState.linkURL.substr(ContextHelper.popupState.linkURL.indexOf(':')+1), Browser.contentWindow.document); }, copyPhone: function cc_copyPhone() { let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper); - clipboard.copyString(ContextHelper.popupState.linkURL.substr(ContextHelper.popupState.linkURL.indexOf(':')+1)); + clipboard.copyString(ContextHelper.popupState.linkURL.substr(ContextHelper.popupState.linkURL.indexOf(':')+1), Browser.contentWindow.document); }, copyImageLocation: function cc_copyImageLocation() { let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper); - clipboard.copyString(ContextHelper.popupState.mediaURL); + clipboard.copyString(ContextHelper.popupState.mediaURL, Browser.contentWindow.document); }, shareLink: function cc_shareLink() { diff --git a/mobile/xul/chrome/content/console.js b/mobile/xul/chrome/content/console.js index 5294d8414822..e6f1ad99ea36 100644 --- a/mobile/xul/chrome/content/console.js +++ b/mobile/xul/chrome/content/console.js @@ -263,7 +263,7 @@ let ConsoleView = { onConsoleBoxKeyPress: function cv_onConsoleBoxKeyPress(aEvent) { if ((aEvent.charCode == 99 || aEvent.charCode == 67) && aEvent.ctrlKey && this._list && this._list.selectedItem) { let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper); - clipboard.copyString(this._list.selectedItem.getAttribute("msg")); + clipboard.copyString(this._list.selectedItem.getAttribute("msg"), document); } }, diff --git a/mobile/xul/chrome/content/content.js b/mobile/xul/chrome/content/content.js index 8bb473205c08..60789592d2e5 100644 --- a/mobile/xul/chrome/content/content.js +++ b/mobile/xul/chrome/content/content.js @@ -1493,7 +1493,7 @@ var SelectionHandler = { if (pointInSelection && this.selectedText.length) { let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper); - clipboard.copyString(this.selectedText); + clipboard.copyString(this.selectedText, this.contentWindow.document); sendAsyncMessage("Browser:SelectionCopied", { succeeded: true }); } else { sendAsyncMessage("Browser:SelectionCopied", { succeeded: false }); diff --git a/mobile/xul/chrome/tests/browser_tapping_edit.js b/mobile/xul/chrome/tests/browser_tapping_edit.js index b1e4c139a476..af5d44397215 100644 --- a/mobile/xul/chrome/tests/browser_tapping_edit.js +++ b/mobile/xul/chrome/tests/browser_tapping_edit.js @@ -237,7 +237,7 @@ gTests.push({ // Put some data on the clipboard to get "paste" to be active let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper); - clipboard.copyString("We are testing Firefox"); + clipboard.copyString("We are testing Firefox", content.document); let event = content.document.createEvent("PopupEvents"); event.initEvent("contextmenu", true, true); diff --git a/netwerk/test/httpserver/httpd.js b/netwerk/test/httpserver/httpd.js index 9e4aa2ca065e..ab27f547d0c6 100644 --- a/netwerk/test/httpserver/httpd.js +++ b/netwerk/test/httpserver/httpd.js @@ -1610,7 +1610,10 @@ RequestReader.prototype = // between fields, even though only a single SP is required (section 19.3) var request = line.split(/[ \t]+/); if (!request || request.length != 3) + { + dumpn("*** No request in line"); throw HTTP_400; + } metadata._method = request[0]; @@ -1618,7 +1621,10 @@ RequestReader.prototype = var ver = request[2]; var match = ver.match(/^HTTP\/(\d+\.\d+)$/); if (!match) + { + dumpn("*** No HTTP version in line"); throw HTTP_400; + } // determine HTTP version try @@ -1643,7 +1649,10 @@ RequestReader.prototype = { // No absolute paths in the request line in HTTP prior to 1.1 if (!metadata._httpVersion.atLeast(nsHttpVersion.HTTP_1_1)) + { + dumpn("*** Metadata version too low"); throw HTTP_400; + } try { @@ -1657,11 +1666,18 @@ RequestReader.prototype = if (port === -1) { if (scheme === "http") + { port = 80; + } else if (scheme === "https") + { port = 443; + } else + { + dumpn("*** Unknown scheme: " + scheme); throw HTTP_400; + } } } catch (e) @@ -1669,11 +1685,15 @@ RequestReader.prototype = // If the host is not a valid host on the server, the response MUST be a // 400 (Bad Request) error message (section 5.2). Alternately, the URI // is malformed. + dumpn("*** Threw when dealing with URI: " + e); throw HTTP_400; } if (!serverIdentity.has(scheme, host, port) || fullPath.charAt(0) != "/") + { + dumpn("*** serverIdentity unknown or path does not start with '/'"); throw HTTP_400; + } } var splitter = fullPath.indexOf("?"); @@ -1717,6 +1737,8 @@ RequestReader.prototype = var line = {}; while (true) { + dumpn("*** Last name: '" + lastName + "'"); + dumpn("*** Last val: '" + lastVal + "'"); NS_ASSERT(!((lastVal === undefined) ^ (lastName === undefined)), lastName === undefined ? "lastVal without lastName? lastVal: '" + lastVal + "'" : @@ -1731,6 +1753,7 @@ RequestReader.prototype = } var lineText = line.value; + dumpn("*** Line text: '" + lineText + "'"); var firstChar = lineText.charAt(0); // blank line means end of headers @@ -1745,7 +1768,7 @@ RequestReader.prototype = } catch (e) { - dumpn("*** e == " + e); + dumpn("*** setHeader threw on last header, e == " + e); throw HTTP_400; } } @@ -1763,7 +1786,7 @@ RequestReader.prototype = // multi-line header if we've already seen a header line if (!lastName) { - // we don't have a header to continue! + dumpn("We don't have a header to continue!"); throw HTTP_400; } @@ -1782,7 +1805,7 @@ RequestReader.prototype = } catch (e) { - dumpn("*** e == " + e); + dumpn("*** setHeader threw on a header, e == " + e); throw HTTP_400; } } @@ -1790,7 +1813,7 @@ RequestReader.prototype = var colon = lineText.indexOf(":"); // first colon must be splitter if (colon < 1) { - // no colon or missing header field-name + dumpn("*** No colon or missing header field-name"); throw HTTP_400; } @@ -1888,6 +1911,13 @@ LineData.prototype = if (length < 0) { this._start = data.length; + + // But if our data ends in a CR, we have to back up one, because + // the first byte in the next packet might be an LF and if we + // start looking at data.length we won't find it. + if (data.length > 0 && data[data.length - 1] === CR) + --this._start; + return false; } @@ -2527,7 +2557,10 @@ ServerHandler.prototype = { var rangeMatch = metadata.getHeader("Range").match(/^bytes=(\d+)?-(\d+)?$/); if (!rangeMatch) + { + dumpn("*** Range header bogosity: '" + metadata.getHeader("Range") + "'"); throw HTTP_400; + } if (rangeMatch[1] !== undefined) start = parseInt(rangeMatch[1], 10); @@ -2536,7 +2569,10 @@ ServerHandler.prototype = end = parseInt(rangeMatch[2], 10); if (start === undefined && end === undefined) + { + dumpn("*** More Range header bogosity: '" + metadata.getHeader("Range") + "'"); throw HTTP_400; + } // No start given, so the end is really the count of bytes from the // end of the file. @@ -2951,6 +2987,7 @@ ServerHandler.prototype = } catch (e) { + dumpn("*** toInternalPath threw " + e); throw HTTP_400; // malformed path } @@ -4642,7 +4679,10 @@ const headerUtils = normalizeFieldName: function(fieldName) { if (fieldName == "") + { + dumpn("*** Empty fieldName"); throw Cr.NS_ERROR_INVALID_ARG; + } for (var i = 0, sz = fieldName.length; i < sz; i++) { @@ -4693,9 +4733,13 @@ const headerUtils = val = val.replace(/^ +/, "").replace(/ +$/, ""); // that should have taken care of all CTLs, so val should contain no CTLs + dumpn("*** Normalized value: '" + val + "'"); for (var i = 0, len = val.length; i < len; i++) if (isCTL(val.charCodeAt(i))) + { + dump("*** Char " + i + " has charcode " + val.charCodeAt(i)); throw Cr.NS_ERROR_INVALID_ARG; + } // XXX disallows quoted-pair where CHAR is a CTL -- will not invalidly // normalize, however, so this can be construed as a tightening of the diff --git a/netwerk/test/httpserver/test/test_linedata.js b/netwerk/test/httpserver/test/test_linedata.js new file mode 100644 index 000000000000..758854c40228 --- /dev/null +++ b/netwerk/test/httpserver/test/test_linedata.js @@ -0,0 +1,20 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- +*/ +/* 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/. */ + +// test that the LineData internal data structure works correctly + +function run_test() +{ + var data = new LineData(); + data.appendBytes(["a".charCodeAt(0), CR]); + + var out = { value: "" }; + do_check_false(data.readLine(out)); + + data.appendBytes([LF]); + do_check_true(data.readLine(out)); + do_check_eq(out.value, "a"); +} diff --git a/netwerk/test/httpserver/test/xpcshell.ini b/netwerk/test/httpserver/test/xpcshell.ini index 2e866eb070ad..76c380cfae76 100644 --- a/netwerk/test/httpserver/test/xpcshell.ini +++ b/netwerk/test/httpserver/test/xpcshell.ini @@ -13,6 +13,7 @@ tail = [test_header_array.js] [test_headers.js] [test_host.js] +[test_linedata.js] [test_name_scheme.js] [test_processasync.js] [test_qi.js] diff --git a/services/sync/tps/extensions/mozmill/resource/modules/inspection.js b/services/sync/tps/extensions/mozmill/resource/modules/inspection.js index 09d786584034..399952f122a1 100644 --- a/services/sync/tps/extensions/mozmill/resource/modules/inspection.js +++ b/services/sync/tps/extensions/mozmill/resource/modules/inspection.js @@ -231,7 +231,7 @@ var isMagicAnonymousDiv = function (_document, node) { var copyToClipboard = function(str){ const gClipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"] .getService(Components.interfaces.nsIClipboardHelper); - gClipboardHelper.copyString(str); + gClipboardHelper.copyString(str, _window.document); } var getControllerAndDocument = function (_document, _window) { diff --git a/testing/jetpack/jetpack-location.txt b/testing/jetpack/jetpack-location.txt index 98869e6b305f..cd9b2fbc4167 100644 --- a/testing/jetpack/jetpack-location.txt +++ b/testing/jetpack/jetpack-location.txt @@ -1 +1 @@ -http://hg.mozilla.org/projects/addon-sdk/archive/041eaf6ae5fb.tar.bz2 +http://hg.mozilla.org/projects/addon-sdk/archive/8d6150ab4903.tar.bz2 diff --git a/testing/mochitest/android.json b/testing/mochitest/android.json index 5c5f3d44cb72..f8e6661a2872 100644 --- a/testing/mochitest/android.json +++ b/testing/mochitest/android.json @@ -18,10 +18,10 @@ "content/base/test/test_csp_redirects.html": "TIMED_OUT", "content/base/test/test_mozfiledataurl.html": "TIMED_OUT", "content/base/test/test_mutationobservers.html": "", + "content/base/test/test_plugin_freezing.html": "CLICK_TO_PLAY", "content/base/test/test_range_bounds.html": "", "content/base/test/test_reentrant_flush.html": "RANDOM", "content/base/test/test_sync_xhr_timer.xhtml": "RANDOM", - "content/base/test/test_text_wholeText.html": "RANDOM", "content/base/test/test_websocket.html": "", "content/base/test/test_websocket_basic.html": "", "content/base/test/test_websocket_hello.html": "", @@ -151,14 +151,14 @@ "dom/tests/mochitest/bugs/test_bug641552.html": "", "dom/tests/mochitest/bugs/test_resize_move_windows.html": "TIMED_OUT", "dom/tests/mochitest/bugs/test_window_bar.html": "", - "dom/tests/mochitest/devicestorage/test_basic.html": "", - "dom/tests/mochitest/devicestorage/test_dotdot.html": "", - "dom/tests/mochitest/devicestorage/test_enumerate.html": "", - "dom/tests/mochitest/devicestorage/test_enumerateMultipleContinue.html": "", - "dom/tests/mochitest/devicestorage/test_enumerateOptions.html": "", - "dom/tests/mochitest/devicestorage/test_lastModificationFilter.html": "", - "dom/tests/mochitest/devicestorage/test_overwrite.html": "", - "dom/tests/mochitest/devicestorage/test_sanity.html": "", + "dom/devicestorage/test/test_basic.html": "", + "dom/devicestorage/test/test_dotdot.html": "", + "dom/devicestorage/test/test_enumerate.html": "", + "dom/devicestorage/test/test_enumerateMultipleContinue.html": "", + "dom/devicestorage/test/test_enumerateOptions.html": "", + "dom/devicestorage/test/test_lastModificationFilter.html": "", + "dom/devicestorage/test/test_overwrite.html": "", + "dom/devicestorage/test/test_sanity.html": "", "dom/tests/mochitest/dom-level2-core/test_documentimportnode03.html": "", "dom/tests/mochitest/dom-level2-core/test_documentimportnode04.html": "", "dom/tests/mochitest/dom-level2-core/test_documentimportnode21.html": "", diff --git a/testing/mochitest/tests/SimpleTest/specialpowersAPI.js b/testing/mochitest/tests/SimpleTest/specialpowersAPI.js index 8df453f184f3..775393fc1322 100644 --- a/testing/mochitest/tests/SimpleTest/specialpowersAPI.js +++ b/testing/mochitest/tests/SimpleTest/specialpowersAPI.js @@ -938,10 +938,10 @@ SpecialPowersAPI.prototype = { return aDocument.documentURIObject; }, - copyString: function(str) { + copyString: function(str, doc) { Components.classes["@mozilla.org/widget/clipboardhelper;1"]. getService(Components.interfaces.nsIClipboardHelper). - copyString(str); + copyString(str, doc); }, openDialog: function(win, args) { @@ -996,6 +996,8 @@ SpecialPowersAPI.prototype = { var xferable = Components.classes["@mozilla.org/widget/transferable;1"]. createInstance(Components.interfaces.nsITransferable); + xferable.init(this._getDocShell(content.window) + .QueryInterface(Components.interfaces.nsILoadContext)); xferable.addDataFlavor(flavor); this._cb.getData(xferable, this._cb.kGlobalClipboard); var data = {}; @@ -1009,10 +1011,10 @@ SpecialPowersAPI.prototype = { return data.QueryInterface(Components.interfaces.nsISupportsString).data; }, - clipboardCopyString: function(preExpectedVal) { + clipboardCopyString: function(preExpectedVal, doc) { var cbHelperSvc = Components.classes["@mozilla.org/widget/clipboardhelper;1"]. getService(Components.interfaces.nsIClipboardHelper); - cbHelperSvc.copyString(preExpectedVal); + cbHelperSvc.copyString(preExpectedVal, doc); }, supportsSelectionClipboard: function() { diff --git a/toolkit/components/console/content/consoleBindings.xml b/toolkit/components/console/content/consoleBindings.xml index 3b9febf62116..f4a2de32b89f 100644 --- a/toolkit/components/console/content/consoleBindings.xml +++ b/toolkit/components/console/content/consoleBindings.xml @@ -260,7 +260,7 @@ const clipI = Components.interfaces.nsIClipboardHelper; var clipboard = Components.classes[clipURI].getService(clipI); - clipboard.copyString(this.mSelectedItem.toString()); + clipboard.copyString(this.mSelectedItem.toString(), document); } catch (ex) { // Unable to copy anything, die quietly } diff --git a/toolkit/components/osfile/osfile_shared.jsm b/toolkit/components/osfile/osfile_shared.jsm index d567109a212c..d4011028c1c1 100644 --- a/toolkit/components/osfile/osfile_shared.jsm +++ b/toolkit/components/osfile/osfile_shared.jsm @@ -218,8 +218,48 @@ LOG("Projected as a regular number"); return projectValue; }; + exports.OS.Shared.projectValue = projectValue; + + /** + * Get the appropriate type for an unsigned int of the given size. + * + * This function is useful to define types such as |mode_t| whose + * actual width depends on the OS/platform. + * + * @param {number} size The number of bytes requested. + */ + Types.uintn_t = function uintn_t(size) { + switch (size) { + case 1: return Types.uint8_t; + case 2: return Types.uint16_t; + case 4: return Types.uint32_t; + case 8: return Types.uint64_t; + default: + throw new Error("Cannot represent unsigned integers of " + size + " bytes"); + } + }; + + /** + * Get the appropriate type for an signed int of the given size. + * + * This function is useful to define types such as |mode_t| whose + * actual width depends on the OS/platform. + * + * @param {number} size The number of bytes requested. + */ + Types.intn_t = function intn_t(size) { + switch (size) { + case 1: return Types.int8_t; + case 2: return Types.int16_t; + case 4: return Types.int32_t; + case 8: return Types.int64_t; + default: + throw new Error("Cannot represent integers of " + size + " bytes"); + } + }; + /** * Actual implementation of common C types. */ @@ -279,6 +319,34 @@ ctypes.unsigned_int, projector(ctypes.unsigned_int, false)); + /** + * A C integer (8-bits). + */ + Types.int8_t = + new Type("int8_t", + ctypes.int8_t, + projectValue); + + Types.uint8_t = + new Type("uint8_t", + ctypes.uint8_t, + projectValue); + + /** + * A C integer (16-bits). + * + * Also known as WORD under Windows. + */ + Types.int16_t = + new Type("int16_t", + ctypes.int16_t, + projectValue); + + Types.uint16_t = + new Type("uint16_t", + ctypes.uint16_t, + projectValue); + /** * A C integer (32-bits). * @@ -327,15 +395,6 @@ return !!(x.value); }); - /** - * A file access mode - * Implemented as a C integer. - */ - Types.mode_t = - new Type("mode_t", - ctypes.int, - projector(ctypes.int, true)); - /** * A user identifier. * Implemented as a C integer. diff --git a/toolkit/components/osfile/osfile_unix_back.jsm b/toolkit/components/osfile/osfile_unix_back.jsm index b2be2aae581d..849dda7cfe94 100644 --- a/toolkit/components/osfile/osfile_unix_back.jsm +++ b/toolkit/components/osfile/osfile_unix_back.jsm @@ -139,11 +139,16 @@ new Type("string", ctypes.char.ptr); - // Note: support for strings in js-ctypes is very limited. // Once bug 552551 has progressed, we should extend this // type using ctypes.readString/ctypes.writeString + /** + * Type |mode_t| + */ + Types.mode_t = Object.create( + Types.intn_t(OS.Constants.libc.OSFILE_SIZEOF_MODE_T), + {name: {value: "mode_t"}}); // Declare libc functions as functions of |OS.Unix.File| diff --git a/toolkit/components/passwordmgr/content/passwordManager.js b/toolkit/components/passwordmgr/content/passwordManager.js index c179e5995c42..c8899cc19010 100644 --- a/toolkit/components/passwordmgr/content/passwordManager.js +++ b/toolkit/components/passwordmgr/content/passwordManager.js @@ -322,7 +322,7 @@ function CopyPassword() { getService(Components.interfaces.nsIClipboardHelper); var row = document.getElementById("signonsTree").currentIndex; var password = signonsTreeView.getCellText(row, {id : "passwordCol" }); - clipboard.copyString(password); + clipboard.copyString(password, document); } function UpdateCopyPassword() { diff --git a/toolkit/components/places/nsLivemarkService.js b/toolkit/components/places/nsLivemarkService.js index a7858f3706ae..d989aa1bbe97 100644 --- a/toolkit/components/places/nsLivemarkService.js +++ b/toolkit/components/places/nsLivemarkService.js @@ -248,8 +248,8 @@ LivemarkService.prototype = { this._reportDeprecatedMethod(); }, - createLivemark: function LS_createLivemark(aParentId, aTitle, aSiteURI, - aFeedURI, aIndex) + createLivemark: function DEPRECATED_LS_createLivemark(aParentId, aTitle, aSiteURI, + aFeedURI, aIndex) { this._reportDeprecatedMethod(); this._ensureSynchronousCache(); diff --git a/toolkit/components/search/nsSearchSuggestions.js b/toolkit/components/search/nsSearchSuggestions.js index 9e179833adbd..d24caa4744c2 100644 --- a/toolkit/components/search/nsSearchSuggestions.js +++ b/toolkit/components/search/nsSearchSuggestions.js @@ -399,6 +399,26 @@ SuggestAutoComplete.prototype = { if (!previousResult) this._formHistoryResult = null; + // Start search immediately if possible, otherwise once the search + // service is initialized + if (Services.search.isInitialized) { + this._triggerSearch(searchString, searchParam, listener); + return; + } + + Services.search.init((function startSearch_cb(aResult) { + if (!Components.isSuccessCode(aResult)) { + Cu.reportError("Could not initialize search service, bailing out: " + aResult); + return; + } + this._triggerSearch(searchString, searchParam, listener); + }).bind(this)); + }, + + /** + * Actual implementation of search. + */ + _triggerSearch: function(searchString, searchParam, listener) { // If there's an existing request, stop it. There is no smart filtering // here as there is when looking through history/form data because the // result set returned by the server is different for every typed value - diff --git a/toolkit/components/viewconfig/content/config.js b/toolkit/components/viewconfig/content/config.js index 6e092fcff024..4ecf6259f1c2 100644 --- a/toolkit/components/viewconfig/content/config.js +++ b/toolkit/components/viewconfig/content/config.js @@ -543,17 +543,17 @@ function updateContextMenu() function copyPref() { var pref = gPrefView[view.selection.currentIndex]; - gClipboardHelper.copyString(pref.prefCol + ';' + pref.valueCol); + gClipboardHelper.copyString(pref.prefCol + ';' + pref.valueCol, document); } function copyName() { - gClipboardHelper.copyString(gPrefView[view.selection.currentIndex].prefCol); + gClipboardHelper.copyString(gPrefView[view.selection.currentIndex].prefCol, document); } function copyValue() { - gClipboardHelper.copyString(gPrefView[view.selection.currentIndex].valueCol); + gClipboardHelper.copyString(gPrefView[view.selection.currentIndex].valueCol, document); } function ModifySelected() diff --git a/toolkit/components/viewsource/content/viewSource.js b/toolkit/components/viewsource/content/viewSource.js index 48153af89089..af77efb3625d 100644 --- a/toolkit/components/viewsource/content/viewSource.js +++ b/toolkit/components/viewsource/content/viewSource.js @@ -755,5 +755,5 @@ function contextMenuCopyLinkOrEmail() { var href = gContextMenu.triggerNode.href; var clipboard = Cc['@mozilla.org/widget/clipboardhelper;1']. getService(Ci.nsIClipboardHelper); - clipboard.copyString(href.substring(href.indexOf(':') + 1)); + clipboard.copyString(href.substring(href.indexOf(':') + 1), document); } diff --git a/toolkit/content/aboutSupport.js b/toolkit/content/aboutSupport.js index 80794360fb75..3a1e0df2e584 100644 --- a/toolkit/content/aboutSupport.js +++ b/toolkit/content/aboutSupport.js @@ -95,6 +95,7 @@ window.onload = function () { populateExtensionsSection(); populateGraphicsSection(); populateJavaScriptSection(); + populateAccessibilitySection(); populateLibVersionsSection(); } @@ -404,6 +405,20 @@ function populateJavaScriptSection() { document.getElementById("javascript-incremental-gc").textContent = enabled ? "1" : "0"; } +function populateAccessibilitySection() { + var active; + try { + active = Components.manager.QueryInterface(Ci.nsIServiceManager) + .isServiceInstantiatedByContractID( + "@mozilla.org/accessibilityService;1", + Ci.nsISupports); + } catch (ex) { + active = false; + } + + document.getElementById("a11y-activated").textContent = active ? "1" : "0"; +} + function getPrefValue(aName) { let value = ""; let type = Services.prefs.getPrefType(aName); @@ -478,6 +493,12 @@ function appendChildren(parentElem, childNodes) { parentElem.appendChild(childNodes[i]); } +function getLoadContext() { + return window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsILoadContext); +} + function copyContentsToClipboard() { // Get the HTML and text representations for the important part of the page. let contentsDiv = document.getElementById("contents"); @@ -491,6 +512,7 @@ function copyContentsToClipboard() { let transferable = Cc["@mozilla.org/widget/transferable;1"] .createInstance(Ci.nsITransferable); + transferable.init(getLoadContext()); // Add the HTML flavor. transferable.addDataFlavor("text/html"); diff --git a/toolkit/content/aboutSupport.xhtml b/toolkit/content/aboutSupport.xhtml index a4e0d9934d58..2fab9adf0ae6 100644 --- a/toolkit/content/aboutSupport.xhtml +++ b/toolkit/content/aboutSupport.xhtml @@ -239,6 +239,24 @@ + +

+ &aboutSupport.a11yTitle; +

+ + + + + + + + + +
+ &aboutSupport.a11yActivated; + +
+

&aboutSupport.libraryVersionsTitle; diff --git a/toolkit/content/globalOverlay.js b/toolkit/content/globalOverlay.js index f62d8dc5abd8..ba894c4a0739 100644 --- a/toolkit/content/globalOverlay.js +++ b/toolkit/content/globalOverlay.js @@ -16,22 +16,11 @@ function closeWindow(aClose, aPromptFunction) if (++windowCount == 2) break; } - - var inPrivateBrowsing = false; - try { - if (["@mozilla.org/privatebrowsing;1"] in Components.classes) { - var pbSvc = Components.classes["@mozilla.org/privatebrowsing;1"] - .getService(Components.interfaces.nsIPrivateBrowsingService); - inPrivateBrowsing = pbSvc.privateBrowsingEnabled; - } - } catch(e) { - // safe to ignore - } - + // If we're down to the last window and someone tries to shut down, check to make sure we can! if (windowCount == 1 && !canQuitApplication("lastwindow")) return false; - else if (windowCount != 1 || inPrivateBrowsing) + else if (windowCount != 1) #endif if (typeof(aPromptFunction) == "function" && !aPromptFunction()) return false; diff --git a/toolkit/content/nsDragAndDrop.js b/toolkit/content/nsDragAndDrop.js index 4e2f6529c75d..dfbe584ff6ea 100644 --- a/toolkit/content/nsDragAndDrop.js +++ b/toolkit/content/nsDragAndDrop.js @@ -130,7 +130,9 @@ var nsTransferable = { { const kXferableContractID = "@mozilla.org/widget/transferable;1"; const kXferableIID = Components.interfaces.nsITransferable; - return Components.classes[kXferableContractID].createInstance(kXferableIID); + var trans = Components.classes[kXferableContractID].createInstance(kXferableIID); + trans.init(null); + return trans; } }; diff --git a/toolkit/content/tests/chrome/test_autocomplete_delayOnPaste.xul b/toolkit/content/tests/chrome/test_autocomplete_delayOnPaste.xul index 768230a1ff47..9305fdaba861 100644 --- a/toolkit/content/tests/chrome/test_autocomplete_delayOnPaste.xul +++ b/toolkit/content/tests/chrome/test_autocomplete_delayOnPaste.xul @@ -109,7 +109,7 @@ function runTest() { gACTimer = Date.now(); Components.classes["@mozilla.org/widget/clipboardhelper;1"] .getService(Components.interfaces.nsIClipboardHelper) - .copyStringToClipboard(SEARCH_STRING, Components.interfaces.nsIClipboard.kGlobalClipboard); + .copyStringToClipboard(SEARCH_STRING, document, Components.interfaces.nsIClipboard.kGlobalClipboard); }, cbCallback, cbCallback); } ]]> diff --git a/toolkit/content/tests/chrome/test_bug253481.xul b/toolkit/content/tests/chrome/test_bug253481.xul index 8567bce21320..bb4d5bf374b9 100644 --- a/toolkit/content/tests/chrome/test_bug253481.xul +++ b/toolkit/content/tests/chrome/test_bug253481.xul @@ -69,7 +69,7 @@ var expectedResults = { SimpleTest.waitForClipboard(testString, function() { var clip = Components.classes["@mozilla.org/widget/clipboardhelper;1"] .getService(Components.interfaces.nsIClipboardHelper); - clip.copyString(testString); + clip.copyString(testString, document); }, function() { for (let [item, expected] in Iterator(expectedResults)) { testPaste(item, $(item), expected); diff --git a/toolkit/content/tests/widgets/Makefile.in b/toolkit/content/tests/widgets/Makefile.in index 08482608ab95..04673ff70cca 100644 --- a/toolkit/content/tests/widgets/Makefile.in +++ b/toolkit/content/tests/widgets/Makefile.in @@ -18,12 +18,12 @@ _CHROME_FILES = \ $(NULL) _TEST_FILES = \ - test_contextmenu_nested.xul \ + test_contextmenu_nested.xul \ test_tree_column_reorder.xul \ tree_shared.js \ test_mousecapture_area.html \ popup_shared.js \ - test_videocontrols.html \ + test_videocontrols.html \ test_videocontrols_video_direction.html \ test_videocontrols_audio_direction.html \ test_audiocontrols_dimensions.html \ @@ -41,8 +41,8 @@ _TEST_FILES = \ videocontrols_direction-2e.html \ videocontrols_direction_test.js \ use_large_cache.js \ - audio.wav \ - video.ogg \ + $(topsrcdir)/content/media/test/audio.wav \ + $(topsrcdir)/content/media/test/seek_with_sound.ogg \ videomask.css \ $(NULL) diff --git a/toolkit/content/tests/widgets/test_videocontrols.html b/toolkit/content/tests/widgets/test_videocontrols.html index ffe33ea450a5..c1114e7a6fe1 100644 --- a/toolkit/content/tests/widgets/test_videocontrols.html +++ b/toolkit/content/tests/widgets/test_videocontrols.html @@ -10,7 +10,7 @@

- +
diff --git a/toolkit/content/widgets/videocontrols.xml b/toolkit/content/widgets/videocontrols.xml
index 63048fe3bcc0..16581b74b15e 100644
--- a/toolkit/content/widgets/videocontrols.xml
+++ b/toolkit/content/widgets/videocontrols.xml
@@ -422,8 +422,10 @@
                         // because of bug 718107: switching to fullscreen may
                         // cause the bindings to detach and reattach, hence
                         // unsetting the attribute.
-                        if (!this.isAudioOnly && !this.video.mozHasAudio)
+                        if (!this.isAudioOnly && !this.video.mozHasAudio) {
                           this.muteButton.setAttribute("noAudio", "true");
+                          this.muteButton.setAttribute("disabled", "true");
+                        }
                     }
 
                     if (this.isAudioOnly)
@@ -544,6 +546,7 @@
                             this.showDuration(Math.round(this.video.duration * 1000));
                             if (!this.isAudioOnly && !this.video.mozHasAudio) {
                               this.muteButton.setAttribute("noAudio", "true");
+                              this.muteButton.setAttribute("disabled", "true");
                             }
                             break;
                         case "loadeddata":
@@ -818,6 +821,9 @@
                 },
 
                 onVolumeMouseInOut : function (event) {
+                    if (this.isVideoWithoutAudioTrack()) {
+                        return;
+                    }
                     // Ignore events caused by transitions between mute button and volumeStack,
                     // or between nodes inside these two elements.
                     if (this.isEventWithin(event, this.muteButton, this.volumeStack))
@@ -983,7 +989,16 @@
                     // script was controlling video playback.
                 },
 
+                isVideoWithoutAudioTrack : function() {
+                  return this.video.readyState >= this.video.HAVE_METADATA &&
+                         !this.isAudioOnly &&
+                         !this.video.mozHasAudio;
+                },
+               
                 toggleMute : function () {
+                    if (this.isVideoWithoutAudioTrack()) {
+                        return;
+                    }
                     this.video.muted = !this.video.muted;
 
                     // We'll handle style changes in the event listener for
@@ -1399,7 +1414,7 @@
                     addListener(this.clickToPlay, "click", this.clickToPlayClickHandler);
                     addListener(this.controlsSpacer, "click", this.clickToPlayClickHandler);
 
-                    if (!this.isAudioOnly && this.video.mozHasAudio) {
+                    if (!this.isAudioOnly) {
                       addListener(this.muteButton, "mouseover", this.onVolumeMouseInOut);
                       addListener(this.muteButton, "mouseout", this.onVolumeMouseInOut);
                       addListener(this.volumeStack, "mouseover", this.onVolumeMouseInOut);
diff --git a/toolkit/locales/en-US/chrome/global/aboutSupport.dtd b/toolkit/locales/en-US/chrome/global/aboutSupport.dtd
index 934cb40143a8..c41af16480ff 100644
--- a/toolkit/locales/en-US/chrome/global/aboutSupport.dtd
+++ b/toolkit/locales/en-US/chrome/global/aboutSupport.dtd
@@ -51,6 +51,9 @@ variant of aboutSupport.showDir.label. -->
 
 
 
+
+
+
 
 
 
diff --git a/toolkit/mozapps/downloads/content/downloads.js b/toolkit/mozapps/downloads/content/downloads.js
index 8fdb70d9d735..41033ff42349 100644
--- a/toolkit/mozapps/downloads/content/downloads.js
+++ b/toolkit/mozapps/downloads/content/downloads.js
@@ -317,7 +317,7 @@ function copySourceLocation(aDownload)
   if (gPerformAllCallback === null) {
     let uris = [];
     gPerformAllCallback = function(aURI) aURI ? uris.push(aURI) :
-      clipboard.copyString(uris.join("\n"));
+      clipboard.copyString(uris.join("\n"), document);
   }
 
   // We have a callback to use, so use it to add a uri
@@ -325,7 +325,7 @@ function copySourceLocation(aDownload)
     gPerformAllCallback(uri);
   else {
     // It's a plain copy source, so copy it
-    clipboard.copyString(uri);
+    clipboard.copyString(uri, document);
   }
 }
 
@@ -712,6 +712,7 @@ var gDownloadDNDObserver =
 function pasteHandler() {
   let trans = Cc["@mozilla.org/widget/transferable;1"].
               createInstance(Ci.nsITransferable);
+  trans.init(null);
   let flavors = ["text/x-moz-url", "text/unicode"];
   flavors.forEach(trans.addDataFlavor);
 
diff --git a/toolkit/mozapps/downloads/tests/chrome/test_multi_select.xul b/toolkit/mozapps/downloads/tests/chrome/test_multi_select.xul
index 30da1f2a9831..219d7ab93ac0 100644
--- a/toolkit/mozapps/downloads/tests/chrome/test_multi_select.xul
+++ b/toolkit/mozapps/downloads/tests/chrome/test_multi_select.xul
@@ -83,6 +83,12 @@ function test()
     }
   };
 
+  function getLoadContext() {
+    return window.QueryInterface(Ci.nsIInterfaceRequestor)
+                 .getInterface(Ci.nsIWebNavigation)
+                 .QueryInterface(Ci.nsILoadContext);
+  }
+
   function continueTest(win) {
     let $ = function(aId) win.document.getElementById(aId);
     let downloadView = $("downloadView");
@@ -99,6 +105,7 @@ function test()
                  getService(Ci.nsIClipboard);
       let trans = Cc["@mozilla.org/widget/transferable;1"].
                   createInstance(Ci.nsITransferable);
+      trans.init(getLoadContext());
       trans.addDataFlavor("text/unicode");
       clip.getData(trans, clip.kGlobalClipboard);
       let str = {};
diff --git a/tools/profiler/TableTicker.cpp b/tools/profiler/TableTicker.cpp
index fac2a90dee62..d2bb98758049 100644
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -17,6 +17,14 @@
 #include "mozilla/StackWalk.h"
 #include "JSObjectBuilder.h"
 
+// Meta
+#include "nsXPCOM.h"
+#include "nsXPCOMCID.h"
+#include "nsIHttpProtocolHandler.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIXULRuntime.h"
+#include "nsIXULAppInfo.h"
+
 // we eventually want to make this runtime switchable
 #if defined(MOZ_PROFILING) && (defined(XP_UNIX) && !defined(XP_MACOSX))
  #ifndef ANDROID
@@ -68,6 +76,7 @@ using namespace mozilla;
  #define snprintf _snprintf
 #endif
 
+static const int DYNAMIC_MAX_STRING = 512;
 
 mozilla::ThreadLocal tlsStack;
 mozilla::ThreadLocal tlsTicker;
@@ -121,6 +130,7 @@ private:
   friend class ThreadProfile;
   union {
     const char* mTagData;
+    char mTagChars[sizeof(void*)];
     void* mTagPtr;
     double mTagFloat;
     Address mTagAddress;
@@ -226,6 +236,30 @@ public:
     mWritePos = mLastFlushPos;
   }
 
+  char* processDynamicTag(int readPos, int* tagsConsumed, char* tagBuff)
+  {
+    int readAheadPos = (readPos + 1) % mEntrySize;
+    int tagBuffPos = 0;
+
+    // Read the string stored in mTagData until the null character is seen
+    bool seenNullByte = false;
+    while (readAheadPos != mLastFlushPos && !seenNullByte) {
+      (*tagsConsumed)++;
+      ProfileEntry readAheadEntry = mEntries[readAheadPos];
+      for (size_t pos = 0; pos < sizeof(void*); pos++) {
+        tagBuff[tagBuffPos] = readAheadEntry.mTagChars[pos];
+        if (tagBuff[tagBuffPos] == '\0' || tagBuffPos == DYNAMIC_MAX_STRING-2) {
+          seenNullByte = true;
+          break;
+        }
+        tagBuffPos++;
+      }
+      if (!seenNullByte)
+        readAheadPos = (readAheadPos + 1) % mEntrySize;
+    }
+    return tagBuff;
+  }
+
   friend std::ostream& operator<<(std::ostream& stream, const ThreadProfile& profile);
 
   JSObject *ToJSObject(JSContext *aCx)
@@ -241,11 +275,25 @@ public:
 
     int readPos = mReadPos;
     while (readPos != mLastFlushPos) {
+      // Number of tag consumed
+      int incBy = 1;
       ProfileEntry entry = mEntries[readPos];
+
+      // Read ahead to the next tag, if it's a 'd' tag process it now
+      const char* tagStringData = entry.mTagData;
+      int readAheadPos = (readPos + 1) % mEntrySize;
+      char tagBuff[DYNAMIC_MAX_STRING];
+      // Make sure the string is always null terminated if it fills up DYNAMIC_MAX_STRING-2
+      tagBuff[DYNAMIC_MAX_STRING-1] = '\0';
+
+      if (readAheadPos != mLastFlushPos && mEntries[readAheadPos].mTagName == 'd') {
+        tagStringData = processDynamicTag(readPos, &incBy, tagBuff);
+      }
+
       switch (entry.mTagName) {
         case 's':
           sample = b.CreateObject();
-          b.DefineProperty(sample, "name", (const char*)entry.mTagData);
+          b.DefineProperty(sample, "name", tagStringData);
           frames = b.CreateArray();
           b.DefineProperty(sample, "frames", frames);
           b.ArrayPush(samples, sample);
@@ -255,18 +303,21 @@ public:
           {
             if (sample) {
               JSObject *frame = b.CreateObject();
-              char tagBuff[1024];
-              // Bug 753041
-              // We need a double cast here to tell GCC that we don't want to sign
-              // extend 32-bit addresses starting with 0xFXXXXXX.
-              unsigned long long pc = (unsigned long long)(uintptr_t)entry.mTagPtr;
-              snprintf(tagBuff, 1024, "%#llx", pc);
-              b.DefineProperty(frame, "location", tagBuff);
+              if (entry.mTagName == 'l') {
+                // Bug 753041
+                // We need a double cast here to tell GCC that we don't want to sign
+                // extend 32-bit addresses starting with 0xFXXXXXX.
+                unsigned long long pc = (unsigned long long)(uintptr_t)entry.mTagPtr;
+                snprintf(tagBuff, DYNAMIC_MAX_STRING, "%#llx", pc);
+                b.DefineProperty(frame, "location", tagBuff);
+              } else {
+                b.DefineProperty(frame, "location", tagStringData);
+              }
               b.ArrayPush(frames, frame);
             }
           }
       }
-      readPos = (readPos + 1) % mEntrySize;
+      readPos = (readPos + incBy) % mEntrySize;
     }
 
     return profile;
@@ -334,6 +385,7 @@ class TableTicker: public Sampler {
   }
 
   JSObject *ToJSObject(JSContext *aCx);
+  JSObject *GetMetaJSObject(JSObjectBuilder& b);
 
 private:
   // Not implemented on platforms which do not support backtracing
@@ -414,6 +466,59 @@ void TableTicker::HandleSaveRequest()
   NS_DispatchToMainThread(runnable);
 }
 
+JSObject* TableTicker::GetMetaJSObject(JSObjectBuilder& b)
+{
+  JSObject *meta = b.CreateObject();
+
+  b.DefineProperty(meta, "version", 2);
+  b.DefineProperty(meta, "interval", interval());
+  b.DefineProperty(meta, "stackwalk", mUseStackWalk);
+  b.DefineProperty(meta, "jank", mJankOnly);
+  b.DefineProperty(meta, "processType", XRE_GetProcessType());
+
+  nsresult res;
+  nsCOMPtr http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res);
+  if (!NS_FAILED(res)) {
+    nsCAutoString string;
+
+    res = http->GetPlatform(string);
+    if (!NS_FAILED(res))
+      b.DefineProperty(meta, "platform", string.Data());
+
+    res = http->GetOscpu(string);
+    if (!NS_FAILED(res))
+      b.DefineProperty(meta, "oscpu", string.Data());
+
+    res = http->GetMisc(string);
+    if (!NS_FAILED(res))
+      b.DefineProperty(meta, "misc", string.Data());
+  }
+
+  nsCOMPtr runtime = do_GetService("@mozilla.org/xre/runtime;1");
+  if (runtime) {
+    nsCAutoString string;
+
+    res = runtime->GetXPCOMABI(string);
+    if (!NS_FAILED(res))
+      b.DefineProperty(meta, "abi", string.Data());
+
+    res = runtime->GetWidgetToolkit(string);
+    if (!NS_FAILED(res))
+      b.DefineProperty(meta, "toolkit", string.Data());
+  }
+
+  nsCOMPtr appInfo = do_GetService("@mozilla.org/xre/app-info;1");
+  if (appInfo) {
+    nsCAutoString string;
+
+    res = appInfo->GetName(string);
+    if (!NS_FAILED(res))
+      b.DefineProperty(meta, "product", string.Data());
+  }
+
+  return meta;
+}
+
 JSObject* TableTicker::ToJSObject(JSContext *aCx)
 {
   JSObjectBuilder b(aCx);
@@ -421,7 +526,8 @@ JSObject* TableTicker::ToJSObject(JSContext *aCx)
   JSObject *profile = b.CreateObject();
 
   // Put meta data
-  // TODO: List things like feature, version number, profile time start/end
+  JSObject *meta = GetMetaJSObject(b);
+  b.DefineProperty(profile, "meta", meta);
 
   // Lists the samples for each ThreadProfile
   JSObject *threads = b.CreateArray();
@@ -560,11 +666,30 @@ void doSampleStackTrace(ProfileStack *aStack, ThreadProfile &aProfile, TickSampl
   // Sample
   // 's' tag denotes the start of a sample block
   // followed by 0 or more 'c' tags.
+  aProfile.addTag(ProfileEntry('s', "(root)"));
   for (mozilla::sig_safe_t i = 0; i < aStack->mStackPointer; i++) {
-    if (i == 0) {
-      aProfile.addTag(ProfileEntry('s', aStack->mStack[i]));
+    // First entry has tagName 's' (start)
+    // Check for magic pointer bit 1 to indicate copy
+    const char* sampleLabel = aStack->mStack[i].mLabel;
+    if (aStack->mStack[i].isCopyLabel()) {
+      // Store the string using 1 or more 'd' (dynamic) tags
+      // that will happen to the preceding tag
+
+      aProfile.addTag(ProfileEntry('c', ""));
+      // Add one to store the null termination
+      size_t strLen = strlen(sampleLabel) + 1;
+      for (size_t j = 0; j < strLen;) {
+        // Store as many characters in the void* as the platform allows
+        char text[sizeof(void*)];
+        for (size_t pos = 0; pos < sizeof(void*) && j+pos < strLen; pos++) {
+          text[pos] = sampleLabel[j+pos];
+        }
+        j += sizeof(void*);
+        // Take '*((void**)(&text[0]))' to pass the char[] as a single void*
+        aProfile.addTag(ProfileEntry('d', *((void**)(&text[0]))));
+      }
     } else {
-      aProfile.addTag(ProfileEntry('c', aStack->mStack[i]));
+      aProfile.addTag(ProfileEntry('c', sampleLabel));
     }
   }
 #ifdef ENABLE_SPS_LEAF_DATA
@@ -657,6 +782,8 @@ std::ostream& operator<<(std::ostream& stream, const ProfileEntry& entry)
     unsigned long long pc = (unsigned long long)(uintptr_t)entry.mTagPtr;
     snprintf(tagBuff, 1024, "l-%#llx\n", pc);
     stream << tagBuff;
+  } else if (entry.mTagName == 'd') {
+    // TODO implement 'd' tag for text profile
   } else {
     stream << entry.mTagName << "-" << entry.mTagData << "\n";
   }
diff --git a/tools/profiler/sampler.h b/tools/profiler/sampler.h
index 47fd45705307..14301dd6226a 100644
--- a/tools/profiler/sampler.h
+++ b/tools/profiler/sampler.h
@@ -71,6 +71,11 @@
 #define SAMPLER_GET_RESPONSIVENESS() NULL
 #define SAMPLER_GET_FEATURES() NULL
 #define SAMPLE_LABEL(name_space, info)
+// Provide a default literal string to use if profiling is disabled
+// and a printf argument to be computed if profiling is enabled.
+// NOTE: This will store the formated string on the stack and consume
+//       over 128 bytes on the stack.
+#define SAMPLE_LABEL_PRINTF(name_space, info, format, ...)
 #define SAMPLE_LABEL_FN(name_space, info)
 #define SAMPLE_MARKER(info)
 
diff --git a/tools/profiler/sps_sampler.h b/tools/profiler/sps_sampler.h
index 219ae41942e5..cc2e14bbd16e 100644
--- a/tools/profiler/sps_sampler.h
+++ b/tools/profiler/sps_sampler.h
@@ -5,6 +5,7 @@
 
 #include 
 #include 
+#include 
 #include "mozilla/ThreadLocal.h"
 #include "nscore.h"
 #include "jsapi.h"
@@ -50,6 +51,7 @@ extern bool stack_key_initialized;
 #define SAMPLER_APPEND_LINE_NUMBER(id) SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, __LINE__)
 
 #define SAMPLE_LABEL(name_space, info) mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info)
+#define SAMPLE_LABEL_PRINTF(name_space, info, format, ...) mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, format, __VA_ARGS__)
 #define SAMPLE_MARKER(info) mozilla_sampler_add_marker(info)
 
 /* we duplicate this code here to avoid header dependencies
@@ -124,7 +126,7 @@ LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) =
 
 // Returns a handdle to pass on exit. This can check that we are popping the
 // correct callstack.
-inline void* mozilla_sampler_call_enter(const char *aInfo);
+inline void* mozilla_sampler_call_enter(const char *aInfo, void *aFrameAddress = NULL, bool aCopy = false);
 inline void  mozilla_sampler_call_exit(void* handle);
 inline void  mozilla_sampler_add_marker(const char *aInfo);
 
@@ -154,8 +156,67 @@ private:
   void* mHandle;
 };
 
+static const int SAMPLER_MAX_STRING = 128;
+class NS_STACK_CLASS SamplerStackFramePrintfRAII {
+public:
+  // we only copy the strings at save time, so to take multiple parameters we'd need to copy them then.
+  SamplerStackFramePrintfRAII(const char *aDefault, const char *aFormat, ...) {
+    if (mozilla_sampler_is_active()) {
+      va_list args;
+      va_start(args, aFormat);
+      char buff[SAMPLER_MAX_STRING];
+
+      // We have to use seperate printf's because we're using
+      // the vargs.
+#if _MSC_VER
+      _vsnprintf(buff, SAMPLER_MAX_STRING, aFormat, args);
+      _snprintf(mDest, SAMPLER_MAX_STRING, "%s %s", aDefault, buff);
+#else
+      vsnprintf(buff, SAMPLER_MAX_STRING, aFormat, args);
+      snprintf(mDest, SAMPLER_MAX_STRING, "%s %s", aDefault, buff);
+#endif
+      mHandle = mozilla_sampler_call_enter(mDest, this, true);
+      va_end(args);
+    } else {
+      mHandle = mozilla_sampler_call_enter(aDefault);
+    }
+  }
+  ~SamplerStackFramePrintfRAII() {
+    mozilla_sampler_call_exit(mHandle);
+  }
+private:
+  char mDest[SAMPLER_MAX_STRING];
+  void* mHandle;
+};
+
 } //mozilla
 
+class StackEntry
+{
+public:
+  // Encode the address and aCopy by dropping the last bit of aStackAddress
+  // and storing aCopy there.
+  static const void* EncodeStackAddress(const void* aStackAddress, bool aCopy) {
+    aStackAddress = reinterpret_cast(
+                      reinterpret_cast(aStackAddress) & ~0x1);
+    if (!aCopy)
+      aStackAddress = reinterpret_cast(
+                        reinterpret_cast(aStackAddress) | 0x1);
+    return aStackAddress;
+  }
+
+  bool isCopyLabel() const volatile {
+    return !((uintptr_t)mStackAddress & 0x1);
+  }
+
+  const char* mLabel;
+  // Tagged pointer. Less significant bit used to
+  // track if mLabel needs a copy. Note that we don't
+  // need the last bit of the stack address for proper ordering.
+  // Last bit 1 = Don't copy, Last bit 0 = Copy.
+  const void* mStackAddress;
+};
+
 // the SamplerStack members are read by signal
 // handlers, so the mutation of them needs to be signal-safe.
 struct ProfileStack
@@ -191,7 +252,7 @@ public:
       clearMarkers();
     }
     if (aMarkerId < 0 ||
-	static_cast(aMarkerId) >= mMarkerPointer) {
+      static_cast(aMarkerId) >= mMarkerPointer) {
       return NULL;
     }
     return mMarkers[aMarkerId];
@@ -205,6 +266,11 @@ public:
   }
 
   void push(const char *aName)
+  {
+    push(aName, NULL, false);
+  }
+
+  void push(const char *aName, void *aStackAddress, bool aCopy)
   {
     if (size_t(mStackPointer) >= mozilla::ArrayLength(mStack)) {
       mDroppedStackEntries++;
@@ -213,7 +279,9 @@ public:
 
     // Make sure we increment the pointer after the name has
     // been written such that mStack is always consistent.
-    mStack[mStackPointer] = aName;
+    mStack[mStackPointer].mLabel = aName;
+    mStack[mStackPointer].mStackAddress = StackEntry::EncodeStackAddress(aStackAddress, aCopy);
+
     // Prevent the optimizer from re-ordering these instructions
     STORE_SEQUENCER();
     mStackPointer++;
@@ -232,7 +300,7 @@ public:
   }
 
   // Keep a list of active checkpoints
-  char const * volatile mStack[1024];
+  StackEntry volatile mStack[1024];
   // Keep a list of active markers to be applied to the next sample taken
   char const * volatile mMarkers[1024];
   volatile mozilla::sig_safe_t mStackPointer;
@@ -243,7 +311,7 @@ public:
   volatile mozilla::sig_safe_t mQueueClearMarker;
 };
 
-inline void* mozilla_sampler_call_enter(const char *aInfo)
+inline void* mozilla_sampler_call_enter(const char *aInfo, void *aFrameAddress, bool aCopy)
 {
   // check if we've been initialized to avoid calling pthread_getspecific
   // with a null tlsStack which will return undefined results.
@@ -258,7 +326,7 @@ inline void* mozilla_sampler_call_enter(const char *aInfo)
   if (!stack) {
     return stack;
   }
-  stack->push(aInfo);
+  stack->push(aInfo, aFrameAddress, aCopy);
 
   // The handle is meant to support future changes
   // but for now it is simply use to save a call to
diff --git a/webapprt/prefs.js b/webapprt/prefs.js
index bb37cef45912..4245ab0da209 100644
--- a/webapprt/prefs.js
+++ b/webapprt/prefs.js
@@ -26,3 +26,9 @@ pref("extensions.blocklist.level", 2);
 pref("extensions.blocklist.url", "https://addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PING_COUNT%/%TOTAL_PING_COUNT%/%DAYS_SINCE_LAST_PING%/");
 pref("extensions.blocklist.detailsURL", "https://www.mozilla.com/%LOCALE%/blocklist/");
 pref("extensions.blocklist.itemURL", "https://addons.mozilla.org/%LOCALE%/%APP%/blocked/%blockID%");
+
+pref("full-screen-api.enabled", true);
+
+
+// Enable smooth scrolling
+pref("general.smoothScroll", true);
diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm
index 510bf66fa787..c960b536ab30 100644
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -4645,6 +4645,7 @@ NSEvent* gLastDragMouseDownEvent = nil;
       NS_ERROR("no transferable");
       return nil;
     }
+    item->Init(nsnull);
 
     item->SetTransferData(kFilePromiseDirectoryMime, macLocalFile, sizeof(nsIFile*));
     
@@ -4799,6 +4800,7 @@ NSEvent* gLastDragMouseDownEvent = nil;
   nsCOMPtr trans = do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
   if (NS_FAILED(rv))
     return NO;
+  trans->Init(nsnull);
 
   trans->AddDataFlavor(kUnicodeMime);
   trans->AddDataFlavor(kHTMLMime);
diff --git a/widget/gtk2/gtk2compat.h b/widget/gtk2/gtk2compat.h
index db03364bc286..766c6514d2f3 100644
--- a/widget/gtk2/gtk2compat.h
+++ b/widget/gtk2/gtk2compat.h
@@ -116,6 +116,12 @@ gtk_widget_set_window(GtkWidget *widget, GdkWindow *window)
 {
   widget->window = window;
 }
+
+static inline gboolean
+gtk_widget_is_toplevel(GtkWidget *widget)
+{
+  return GTK_WIDGET_TOPLEVEL(widget);
+}
 #endif
 
 #if !GTK_CHECK_VERSION(2, 20, 0)
diff --git a/widget/nsIClipboardHelper.idl b/widget/nsIClipboardHelper.idl
index f8d9e0e247bf..ac0e6b2f0f44 100644
--- a/widget/nsIClipboardHelper.idl
+++ b/widget/nsIClipboardHelper.idl
@@ -11,11 +11,13 @@
 #include "nsString.h" // needed for AString -> nsAString, unfortunately
 %}
 
+interface nsIDOMDocument;
+
 /**
  * helper service for common uses of nsIClipboard.
  */
 
-[scriptable, uuid(44073a98-1dd2-11b2-8600-d0ae854dbe93)]
+[scriptable, uuid(836316a9-6834-45e7-92af-5a4b9bd01a31)]
 interface nsIClipboardHelper : nsISupports
 {
 
@@ -23,16 +25,18 @@ interface nsIClipboardHelper : nsISupports
    * copy string to given clipboard
    *
    * @param aString, the string to copy to the clipboard
+   * @param aDoc, the source document for the string, if available
    * @param aClipboardID, the ID of the clipboard to copy to
    *        (eg. kSelectionClipboard -- see nsIClipboard.idl)
    */
-  void copyStringToClipboard(in AString aString, in long aClipboardID);
+  void copyStringToClipboard(in AString aString, [optional] in nsIDOMDocument aDoc, in long aClipboardID);
 
   /**
    * copy string to (default) clipboard
    *
    * @param aString, the string to copy to the clipboard
+   * @param aDoc, the source document for the string, if available
    */
-  void copyString(in AString aString);
+  void copyString(in AString aString, [optional] in nsIDOMDocument aDoc);
 
 };
diff --git a/widget/nsITransferable.idl b/widget/nsITransferable.idl
index 59dbac623033..cd7ef4c5bc67 100644
--- a/widget/nsITransferable.idl
+++ b/widget/nsITransferable.idl
@@ -63,6 +63,7 @@
   *
   */
 interface nsITransferable;
+interface nsILoadContext;
 
 [scriptable, uuid(7E225E5F-711C-11D7-9FAE-000393636592)]
 interface nsIFlavorDataProvider : nsISupports
@@ -80,11 +81,21 @@ interface nsIFlavorDataProvider : nsISupports
 };
 
 
-[scriptable, uuid(8B5314BC-DB01-11d2-96CE-0060B0FB9956)]
+[scriptable, uuid(983cb266-535b-4ca5-8ef2-2cee08d061e6)]
 interface nsITransferable : nsISupports
 {
   const long kFlavorHasDataProvider = 0;
-  
+
+  /**
+   * Initializes a transferable object.  This should be called on alli
+   * transferable objects.  Failure to do so will result in fatal assertions in
+   * debug builds.
+   *
+   * @param aContext the load context associated with the transferable object.
+   *        This can be set to null if a load context is not available.
+   */
+  void init(in nsILoadContext aContext);
+
   /**
     * Computes a list of flavors (mime types as nsISupportsCString) that the transferable 
     * can export, either through intrinsic knowledge or output data converters.
@@ -161,5 +172,7 @@ interface nsITransferable : nsISupports
 
   attribute nsIFormatConverter converter;
 
+  [noscript] readonly attribute boolean isPrivateData;
+
 };
 
diff --git a/widget/os2/nsDragService.cpp b/widget/os2/nsDragService.cpp
index fd585786f3a7..856e7c1d6413 100644
--- a/widget/os2/nsDragService.cpp
+++ b/widget/os2/nsDragService.cpp
@@ -844,6 +844,7 @@ NS_IMETHODIMP nsDragService::NativeDragEnter(PDRAGINFO pdinfo)
     nsCOMPtr trans(
             do_CreateInstance("@mozilla.org/widget/transferable;1", &rv));
     if (trans) {
+      trans->Init(nsnull);
 
       bool isUrl = DrgVerifyType(pditem, "UniformResourceLocator");
       bool isAlt = (WinGetKeyState(HWND_DESKTOP, VK_ALT) & 0x8000);
diff --git a/widget/tests/test_bug444800.xul b/widget/tests/test_bug444800.xul
index 1860bb4f9e42..14d572baac30 100644
--- a/widget/tests/test_bug444800.xul
+++ b/widget/tests/test_bug444800.xul
@@ -39,6 +39,13 @@ function copyImageToClipboard()
   document.popupNode = tmpNode;
 }
 
+function getLoadContext() {
+  const Ci = Components.interfaces;
+  return window.QueryInterface(Ci.nsIInterfaceRequestor)
+               .getInterface(Ci.nsIWebNavigation)
+               .QueryInterface(Ci.nsILoadContext);
+}
+
 function runImageClipboardTests(aCBSvc, aImageType)
 {
   // Verify that hasDataMatchingFlavors() is working correctly.
@@ -50,6 +57,7 @@ function runImageClipboardTests(aCBSvc, aImageType)
   // Verify that getData() is working correctly.
   var xfer = Components.classes["@mozilla.org/widget/transferable;1"]
                        .createInstance(Components.interfaces.nsITransferable);
+  xfer.init(getLoadContext());
   xfer.addDataFlavor(aImageType);
   aCBSvc.getData(xfer, knsIClipboard.kGlobalClipboard);
 
diff --git a/widget/tests/test_bug462106.xul b/widget/tests/test_bug462106.xul
index a59207ed064d..9b3f43af123a 100644
--- a/widget/tests/test_bug462106.xul
+++ b/widget/tests/test_bug462106.xul
@@ -29,12 +29,19 @@ const Ci = Components.interfaces;
 function copy(str) {
   Cc["@mozilla.org/widget/clipboardhelper;1"].
   getService(Ci.nsIClipboardHelper).
-  copyString(str);
+  copyString(str, document);
+}
+
+function getLoadContext() {
+  return window.QueryInterface(Ci.nsIInterfaceRequestor)
+               .getInterface(Ci.nsIWebNavigation)
+               .QueryInterface(Ci.nsILoadContext);
 }
 
 function paste() {
   let trans = Cc["@mozilla.org/widget/transferable;1"].
               createInstance(Ci.nsITransferable);
+  trans.init(getLoadContext());
   trans.addDataFlavor("text/unicode");
   let clip = Cc["@mozilla.org/widget/clipboard;1"].
              getService(Ci.nsIClipboard);
diff --git a/widget/tests/test_bug466599.xul b/widget/tests/test_bug466599.xul
index 05374eecd19c..a70f47add7ca 100644
--- a/widget/tests/test_bug466599.xul
+++ b/widget/tests/test_bug466599.xul
@@ -23,6 +23,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=466599
 
   /** Test for Bug 466599 **/
 
+function getLoadContext() {
+  const Ci = Components.interfaces;
+  return window.QueryInterface(Ci.nsIInterfaceRequestor)
+               .getInterface(Ci.nsIWebNavigation)
+               .QueryInterface(Ci.nsILoadContext);
+}
+
 function copyToClipboard(txt)
 {
   var clipid = Components.interfaces.nsIClipboard;
@@ -34,6 +41,7 @@ function copyToClipboard(txt)
     Components.classes['@mozilla.org/widget/transferable;1'].createInstance(Components.interfaces.nsITransferable);
   if (!trans)
     return false;
+  trans.init(getLoadContext());
   trans.addDataFlavor('text/html');
   var str =
     Components.classes['@mozilla.org/supports-string;1'].createInstance(Components.interfaces.nsISupportsString);
@@ -57,6 +65,7 @@ function readFromClipboard()
     Components.classes['@mozilla.org/widget/transferable;1'].createInstance(Components.interfaces.nsITransferable);
   if (!trans)
     return;
+  trans.init(getLoadContext());
   trans.addDataFlavor('text/html');
   clip.getData(trans,clipid.kGlobalClipboard);
   var str = new Object();
diff --git a/widget/tests/test_bug565392.html b/widget/tests/test_bug565392.html
index 0699e68aee66..83cdba12e139 100644
--- a/widget/tests/test_bug565392.html
+++ b/widget/tests/test_bug565392.html
@@ -28,9 +28,16 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=565392
   var clipboard = Cc["@mozilla.org/widget/clipboard;1"]
                     .getService(Ci.nsIClipboard);
   
+  function getLoadContext() {
+    return window.QueryInterface(Ci.nsIInterfaceRequestor)
+                 .getInterface(Ci.nsIWebNavigation)
+                 .QueryInterface(Ci.nsILoadContext);
+  }
+
   function getTransferableFile(file) {
     var transferable = Cc['@mozilla.org/widget/transferable;1']
                          .createInstance(Ci.nsITransferable);
+    transferable.init(getLoadContext());
     transferable.setTransferData("application/x-moz-file", file, 0);
     return transferable;
   }
@@ -42,6 +49,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=565392
   function getClipboardData(mime) {
     var transferable = Cc['@mozilla.org/widget/transferable;1']
                          .createInstance(Ci.nsITransferable);
+    transferable.init(getLoadContext());
     transferable.addDataFlavor(mime);
     clipboard.getData(transferable, 1);
     var data = {};
diff --git a/widget/tests/test_bug673301.xul b/widget/tests/test_bug673301.xul
index b32242d1fc44..37b5b800d6f9 100644
--- a/widget/tests/test_bug673301.xul
+++ b/widget/tests/test_bug673301.xul
@@ -14,11 +14,19 @@