From 8149d0c6f53d181c9dca4df9177152d9f03ff0dd Mon Sep 17 00:00:00 2001 From: Paul Rouget Date: Sat, 14 Jan 2012 06:08:29 +0100 Subject: [PATCH 01/20] Bug 712223 - [highlighter] Breadcrumbs bar doesn't always have the same size on Windows and Linux. r=dao r=jwalker --- browser/themes/gnomestripe/browser.css | 5 +++++ browser/themes/winstripe/browser.css | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/browser/themes/gnomestripe/browser.css b/browser/themes/gnomestripe/browser.css index 2482a8e8b9d2..4c2844d52363 100644 --- a/browser/themes/gnomestripe/browser.css +++ b/browser/themes/gnomestripe/browser.css @@ -2106,6 +2106,11 @@ panel[dimmed="true"] { margin-bottom: -1px; } +#inspector-breadcrumbs > .scrollbutton-up, +#inspector-breadcrumbs > .scrollbutton-down { + -moz-appearance: none; +} + .inspector-breadcrumbs-button { -moz-appearance: none; background-color: transparent; diff --git a/browser/themes/winstripe/browser.css b/browser/themes/winstripe/browser.css index d43021a092ed..729e41d9113d 100644 --- a/browser/themes/winstripe/browser.css +++ b/browser/themes/winstripe/browser.css @@ -2786,6 +2786,11 @@ panel[dimmed="true"] { margin: -1px 0; } +#inspector-breadcrumbs > .scrollbutton-up, +#inspector-breadcrumbs > .scrollbutton-down { + -moz-appearance: none; +} + .inspector-breadcrumbs-button { -moz-appearance: none; background-color: transparent; From bd4f2fb9cc1d9fddb414ec486767e11435c64a1b Mon Sep 17 00:00:00 2001 From: Paul Rouget Date: Fri, 27 Jan 2012 15:28:47 +0100 Subject: [PATCH 02/20] Bug 720468 - Add an "*" at the beginning of a file name if the file needs to be saved. r=dao --- browser/devtools/styleeditor/styleeditor.css | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/browser/devtools/styleeditor/styleeditor.css b/browser/devtools/styleeditor/styleeditor.css index dd55e52ae721..50e36324acf8 100644 --- a/browser/devtools/styleeditor/styleeditor.css +++ b/browser/devtools/styleeditor/styleeditor.css @@ -73,6 +73,14 @@ li.error > .stylesheet-info > .stylesheet-more > .stylesheet-error-message { -moz-box-pack: center; } +.stylesheet-name { + white-space: nowrap; +} + +li.unsaved > hgroup > h1 > .stylesheet-name:before { + content: "*"; +} + .stylesheet-enabled { display: -moz-box; } From 6b0d3e9093f11350bebdd0c630f136a81fbb4b3c Mon Sep 17 00:00:00 2001 From: Victor Porof Date: Fri, 27 Jan 2012 23:44:59 +0200 Subject: [PATCH 03/20] Bug 719039 - After the Highlighter was refactored, ESCAPE key closes Inspector when Tilt is open. r=rcampbell --- browser/devtools/tilt/Tilt.jsm | 23 ++++++++++++------- browser/devtools/tilt/TiltVisualizer.jsm | 22 +++++++++++++----- .../test/browser_tilt_05_destruction-esc.js | 3 +++ 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/browser/devtools/tilt/Tilt.jsm b/browser/devtools/tilt/Tilt.jsm index 84eb85662235..273a7dd3d6ff 100644 --- a/browser/devtools/tilt/Tilt.jsm +++ b/browser/devtools/tilt/Tilt.jsm @@ -230,7 +230,7 @@ Tilt.prototype = { */ _onTabSelect: function T__onTabSelect() { - if (this.visualizers[this.currentWindowId]) { + if (this.currentInstance) { Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.SHOWN, null); } else { Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.HIDDEN, null); @@ -245,10 +245,8 @@ Tilt.prototype = { * the newly selected node */ update: function T_update(aNode) { - let id = this.currentWindowId; - - if (this.visualizers[id]) { - this.visualizers[id].presenter.highlightNode(aNode); + if (this.currentInstance) { + this.currentInstance.presenter.highlightNode(aNode); } }, @@ -277,6 +275,7 @@ Tilt.prototype = { this._whenShown.bind(this), TILT_NOTIFICATIONS.SHOWN, false); Services.obs.addObserver( this._whenHidden.bind(this), TILT_NOTIFICATIONS.HIDDEN, false); + Services.obs.addObserver(function(aSubject, aTopic, aWinId) { this.destroy(aWinId); }.bind(this), this.chromeWindow.InspectorUI.INSPECTOR_NOTIFICATIONS.DESTROYED, false); @@ -287,7 +286,7 @@ Tilt.prototype = { // FIXME: this shouldn't be done here, see bug #705131 let onOpened = function() { - if (this.visualizers[this.currentWindowId]) { + if (this.currentInstance) { this.chromeWindow.InspectorUI.stopInspecting(); this.inspectButton.disabled = true; this.highlighterContainer.style.display = "none"; @@ -326,8 +325,16 @@ Tilt.prototype = { */ get currentWindowId() { - let gBrowser = this.chromeWindow.gBrowser; - return TiltUtils.getWindowId(gBrowser.selectedBrowser.contentWindow); + return TiltUtils.getWindowId( + this.chromeWindow.gBrowser.selectedBrowser.contentWindow); + }, + + /** + * Gets the visualizer instance for the current tab. + */ + get currentInstance() + { + return this.visualizers[this.currentWindowId]; }, /** diff --git a/browser/devtools/tilt/TiltVisualizer.jsm b/browser/devtools/tilt/TiltVisualizer.jsm index b7bda17dd3fc..62c81a919f56 100644 --- a/browser/devtools/tilt/TiltVisualizer.jsm +++ b/browser/devtools/tilt/TiltVisualizer.jsm @@ -1052,6 +1052,7 @@ TiltVisualizer.Controller.prototype = { canvas.addEventListener("MozMousePixelScroll", this.onMozScroll, false); canvas.addEventListener("keydown", this.onKeyDown, false); canvas.addEventListener("keyup", this.onKeyUp, false); + canvas.addEventListener("keypress", this.onKeyPress, true); canvas.addEventListener("blur", this.onBlur, false); // handle resize events to change the arcball dimensions @@ -1074,6 +1075,7 @@ TiltVisualizer.Controller.prototype = { canvas.removeEventListener("MozMousePixelScroll", this.onMozScroll, false); canvas.removeEventListener("keydown", this.onKeyDown, false); canvas.removeEventListener("keyup", this.onKeyUp, false); + canvas.removeEventListener("keypress", this.onKeyPress, true); canvas.removeEventListener("blur", this.onBlur, false); presenter.contentWindow.removeEventListener("resize", this.onResize,false); @@ -1217,16 +1219,10 @@ TiltVisualizer.Controller.prototype = { onKeyUp: function TVC_onKeyUp(e) { let code = e.keyCode || e.which; - let tilt = this.presenter.chromeWindow.Tilt; - if (code === e.DOM_VK_ESCAPE) { - tilt.destroy(tilt.currentWindowId, true); - return; - } if (code === e.DOM_VK_X) { this.presenter.deleteNode(); } - if (!e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey) { e.preventDefault(); e.stopPropagation(); @@ -1234,6 +1230,20 @@ TiltVisualizer.Controller.prototype = { } }, + /** + * Called when a key is pressed. + */ + onKeyPress: function TVC_onKeyPress(e) + { + let tilt = this.presenter.chromeWindow.Tilt; + + if (e.keyCode === e.DOM_VK_ESCAPE) { + e.preventDefault(); + e.stopPropagation(); + tilt.destroy(tilt.currentWindowId, true); + } + }, + /** * Called when the canvas looses focus. */ diff --git a/browser/devtools/tilt/test/browser_tilt_05_destruction-esc.js b/browser/devtools/tilt/test/browser_tilt_05_destruction-esc.js index 1d5238aaabb2..ca41d5da0db0 100644 --- a/browser/devtools/tilt/test/browser_tilt_05_destruction-esc.js +++ b/browser/devtools/tilt/test/browser_tilt_05_destruction-esc.js @@ -31,6 +31,9 @@ function cleanup() { is(Tilt.visualizers[id], null, "The current instance of the visualizer wasn't destroyed properly."); + ok(InspectorUI.highlighter && InspectorUI.breadcrumbs, + "The Inspector should not close while Tilt is opened."); + Services.obs.removeObserver(cleanup, DESTROYED); gBrowser.removeCurrentTab(); finish(); From 75abcccc496f9202b6c462a4079b4581f35b708a Mon Sep 17 00:00:00 2001 From: Cedric Vivier Date: Thu, 26 Jan 2012 01:53:00 +0100 Subject: [PATCH 04/20] Bug 720291 - Intermittent browser_styleeditor_new.js | content's background color has been updated to red - Got rgb(255, 5, 5) (or 6, 6), expected rgb(255, 0, 0). r=dcamp --- .../test/browser_styleeditor_new.js | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/browser/devtools/styleeditor/test/browser_styleeditor_new.js b/browser/devtools/styleeditor/test/browser_styleeditor_new.js index c9b468be8041..994c098fce63 100644 --- a/browser/devtools/styleeditor/test/browser_styleeditor_new.js +++ b/browser/devtools/styleeditor/test/browser_styleeditor_new.js @@ -34,6 +34,17 @@ let gAddedCount = 0; // to add new stylesheet after the 2 initial stylesheets let gNewEditor; // to make sure only one new stylesheet got created let gUpdateCount = 0; // to make sure only one Update event is triggered let gCommitCount = 0; // to make sure only one Commit event is triggered +let gTransitionEndCount = 0; + +function finishOnTransitionEndAndCommit() { + if (gCommitCount && gTransitionEndCount) { + is(gUpdateCount, 1, "received one Update event"); + is(gCommitCount, 1, "received one Commit event"); + is(gTransitionEndCount, 1, "received one transitionend event"); + + finish(); + } +} function testEditorAdded(aChrome, aEditor) { @@ -86,6 +97,16 @@ function testEditorAdded(aChrome, aEditor) is(aEditor.sourceEditor.getText(), TESTCASE_CSS_SOURCE + "}", "rule bracket has been auto-closed"); + // we know that the testcase above will start a CSS transition + content.addEventListener("transitionend", function () { + gTransitionEndCount++; + + let computedStyle = content.getComputedStyle(content.document.body, null); + is(computedStyle.backgroundColor, "rgb(255, 0, 0)", + "content's background color has been updated to red"); + + executeSoon(finishOnTransitionEndAndCommit); + }, false); }, gChromeWindow) ; }, @@ -109,22 +130,13 @@ function testEditorAdded(aChrome, aEditor) is(parseInt(ruleCount), 1, "new editor shows 1 rule after modification"); - let computedStyle = content.getComputedStyle(content.document.body, null); - is(computedStyle.backgroundColor, "rgb(255, 0, 0)", - "content's background color has been updated to red"); - ok(!content.document.documentElement.classList.contains(TRANSITION_CLASS), "StyleEditor's transition class has been removed from content"); - executeSoon(function () { - is(gUpdateCount, 1, "received only one Update event (throttle)"); - is(gCommitCount, 1, "received only one Commit event (throttle)"); + aEditor.removeActionListener(listener); + gNewEditor = null; - aEditor.removeActionListener(listener); - - gNewEditor = null; - finish(); - }); + executeSoon(finishOnTransitionEndAndCommit); } }; From 11d39393eca88f1e5dcab4afa7857d0a6920674c Mon Sep 17 00:00:00 2001 From: Serge Gautherie Date: Mon, 30 Jan 2012 19:35:35 +0100 Subject: [PATCH 05/20] Bug 722019. (Av2) Remove some useless/redundant listbox rules, in gnomestripe and winstripe. r=neil. --- toolkit/themes/gnomestripe/global/listbox.css | 13 ------------- toolkit/themes/winstripe/global/listbox.css | 13 ------------- 2 files changed, 26 deletions(-) diff --git a/toolkit/themes/gnomestripe/global/listbox.css b/toolkit/themes/gnomestripe/global/listbox.css index f78362cf3127..f4e4f1b14e27 100644 --- a/toolkit/themes/gnomestripe/global/listbox.css +++ b/toolkit/themes/gnomestripe/global/listbox.css @@ -165,16 +165,3 @@ listheader[sortable="true"]:hover:active { min-height: 13px; background: -moz-Field no-repeat 50% 50%; } - -.listcell-check[checked="true"] { - background-image: url("chrome://global/skin/checkbox/cbox-check.gif"); -} - -.listcell-check[disabled="true"] { - border-color: GrayText; - background-image: url("chrome://global/skin/checkbox/cbox-dis.gif"); -} - -.listcell-check[disabled="true"][checked="true"] { - background-image: url("chrome://global/skin/checkbox/cbox-check-dis.gif"); -} diff --git a/toolkit/themes/winstripe/global/listbox.css b/toolkit/themes/winstripe/global/listbox.css index 84000fdeb81d..70b4c10cc3f2 100644 --- a/toolkit/themes/winstripe/global/listbox.css +++ b/toolkit/themes/winstripe/global/listbox.css @@ -175,16 +175,3 @@ listheader[sortable="true"]:hover:active { min-height: 13px; background: -moz-Field no-repeat 50% 50%; } - -.listcell-check[checked="true"] { - background-image: url("chrome://global/skin/checkbox/cbox-check.gif"); -} - -.listcell-check[disabled="true"] { - border-color: GrayText; - background-image: url("chrome://global/skin/checkbox/cbox-dis.gif"); -} - -.listcell-check[disabled="true"][checked="true"] { - background-image: url("chrome://global/skin/checkbox/cbox-check-dis.gif"); -} From fe8c4a07f7b690c9e19d8d393784d9e1576f1a4d Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Mon, 30 Jan 2012 15:20:56 -0500 Subject: [PATCH 06/20] Bug 722428: Sprinkle weakref pixie dust on SpecialPowers to avoid leaks until shutdown. r=ctalbert --- .../mochitest/specialpowers/content/specialpowers.js | 2 +- .../mochitest/tests/SimpleTest/specialpowersAPI.js | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/testing/mochitest/specialpowers/content/specialpowers.js b/testing/mochitest/specialpowers/content/specialpowers.js index b2805de9d35d..f7f471132648 100644 --- a/testing/mochitest/specialpowers/content/specialpowers.js +++ b/testing/mochitest/specialpowers/content/specialpowers.js @@ -39,7 +39,7 @@ */ function SpecialPowers(window) { - this.window = window; + this.window = Components.utils.getWeakReference(window); this._encounteredCrashDumpFiles = []; this._unexpectedCrashDumpFiles = { }; this._crashDumpDir = null; diff --git a/testing/mochitest/tests/SimpleTest/specialpowersAPI.js b/testing/mochitest/tests/SimpleTest/specialpowersAPI.js index 953b4e00b8eb..8a669a20ca62 100644 --- a/testing/mochitest/tests/SimpleTest/specialpowersAPI.js +++ b/testing/mochitest/tests/SimpleTest/specialpowersAPI.js @@ -63,6 +63,7 @@ function bindDOMWindowUtils(aWindow) { var util = aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIDOMWindowUtils); + var weakUtil = Components.utils.getWeakReference(util); // This bit of magic brought to you by the letters // B Z, and E, S and the number 5. // @@ -77,7 +78,10 @@ function bindDOMWindowUtils(aWindow) { if (prop in desc && typeof(desc[prop]) == "function") { var oldval = desc[prop]; try { - desc[prop] = function() { return oldval.apply(util, arguments); }; + desc[prop] = function() { + utils = weakUtil.get(); + return oldval.apply(utils, arguments); + }; } catch (ex) { dump("WARNING: Special Powers failed to rebind function: " + desc + "::" + prop + "\n"); } @@ -426,7 +430,7 @@ SpecialPowersAPI.prototype = { }, getDOMWindowUtils: function(aWindow) { - if (aWindow == this.window && this.DOMWindowUtils != null) + if (aWindow == this.window.get() && this.DOMWindowUtils != null) return this.DOMWindowUtils; return bindDOMWindowUtils(aWindow); @@ -814,7 +818,7 @@ SpecialPowersAPI.prototype = { }, snapshotWindow: function (win, withCaret) { - var el = this.window.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); + var el = this.window.get().document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); el.width = win.innerWidth; el.height = win.innerHeight; var ctx = el.getContext("2d"); @@ -1029,7 +1033,7 @@ SpecialPowersAPI.prototype = { }, snapshotWindow: function (win, withCaret) { - var el = this.window.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); + var el = this.window.get().document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); el.width = win.innerWidth; el.height = win.innerHeight; var ctx = el.getContext("2d"); From ef677e07dd893c3c33fb253224a17ab73d4e6ea7 Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Mon, 30 Jan 2012 22:06:18 +0200 Subject: [PATCH 07/20] Bug 721543 - Call forgetSkippable before CC, r=mccr8 --HG-- extra : rebase_source : 4be50f8bcab1418c563394379abb6b4f074697b3 --- dom/base/nsDOMWindowUtils.cpp | 10 +- dom/base/nsJSEnvironment.cpp | 128 +++++++++++++++++----- dom/base/nsJSEnvironment.h | 8 +- dom/interfaces/base/nsIDOMWindowUtils.idl | 20 +++- 4 files changed, 130 insertions(+), 36 deletions(-) diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 963eaa048baa..19b950fdc338 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -806,7 +806,8 @@ nsDOMWindowUtils::Focus(nsIDOMElement* aElement) } NS_IMETHODIMP -nsDOMWindowUtils::GarbageCollect(nsICycleCollectorListener *aListener) +nsDOMWindowUtils::GarbageCollect(nsICycleCollectorListener *aListener, + PRInt32 aExtraForgetSkippableCalls) { SAMPLE_LABEL("GC", "GarbageCollect"); // Always permit this in debug builds. @@ -817,13 +818,14 @@ nsDOMWindowUtils::GarbageCollect(nsICycleCollectorListener *aListener) #endif nsJSContext::GarbageCollectNow(js::gcreason::DOM_UTILS); - nsJSContext::CycleCollectNow(aListener); + nsJSContext::CycleCollectNow(aListener, aExtraForgetSkippableCalls); return NS_OK; } NS_IMETHODIMP -nsDOMWindowUtils::CycleCollect(nsICycleCollectorListener *aListener) +nsDOMWindowUtils::CycleCollect(nsICycleCollectorListener *aListener, + PRInt32 aExtraForgetSkippableCalls) { // Always permit this in debug builds. #ifndef DEBUG @@ -832,7 +834,7 @@ nsDOMWindowUtils::CycleCollect(nsICycleCollectorListener *aListener) } #endif - nsJSContext::CycleCollectNow(aListener); + nsJSContext::CycleCollectNow(aListener, aExtraForgetSkippableCalls); return NS_OK; } diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 971fa58ebbf8..ac7321b55d8c 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -139,6 +139,10 @@ static PRLogModuleInfo* gJSDiagnostics; // and doing the actual CC. #define NS_CC_DELAY 5000 // ms +#define NS_CC_SKIPPABLE_DELAY 250 // ms + +#define NS_CC_FORCED (5 * 60 * PR_USEC_PER_SEC) // 5 min + #define JAVASCRIPT nsIProgrammingLanguage::JAVASCRIPT // if you add statics here, add them to the list in nsJSRuntime::Startup @@ -162,6 +166,15 @@ static bool sLoadingInProgress; static PRUint32 sCCollectedWaitingForGC; static bool sPostGCEventsToConsole; +static PRUint32 sCCTimerFireCount = 0; +static PRUint32 sMinForgetSkippableTime = PR_UINT32_MAX; +static PRUint32 sMaxForgetSkippableTime = 0; +static PRUint32 sTotalForgetSkippableTime = 0; +static PRUint32 sRemovedPurples = 0; +static PRUint32 sForgetSkippableBeforeCC = 0; +static PRUint32 sPreviousSuspectedCount = 0; + +static bool sCleanupSinceLastGC = true; nsScriptNameSpaceManager *gNameSpaceManager; @@ -3256,7 +3269,8 @@ nsJSContext::ShrinkGCBuffersNow() //Static void -nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener) +nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener, + PRInt32 aExtraForgetSkippableCalls) { if (!NS_IsMainThread()) { return; @@ -3270,6 +3284,15 @@ nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener) PRTime start = PR_Now(); PRUint32 suspected = nsCycleCollector_suspectedCount(); + + for (PRInt32 i = 0; i < aExtraForgetSkippableCalls; ++i) { + nsCycleCollector_forgetSkippable(); + } + + // nsCycleCollector_forgetSkippable may mark some gray js to black. + if (!sCleanupSinceLastGC && aExtraForgetSkippableCalls >= 0) { + nsCycleCollector_forgetSkippable(); + } PRUint32 collected = nsCycleCollector_collect(aListener); sCCollectedWaitingForGC += collected; @@ -3295,18 +3318,35 @@ nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener) sFirstCollectionTime = now; } - NS_NAMED_LITERAL_STRING(kFmt, - "CC(T+%.1f) collected: %lu (%lu waiting for GC), suspected: %lu, duration: %llu ms."); + NS_NAMED_MULTILINE_LITERAL_STRING(kFmt, + NS_LL("CC(T+%.1f) collected: %lu (%lu waiting for GC), suspected: %lu, duration: %llu ms.\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; + sMinForgetSkippableTime = (sMinForgetSkippableTime == PR_UINT32_MAX) + ? 0 : sMinForgetSkippableTime; msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), double(delta) / PR_USEC_PER_SEC, collected, sCCollectedWaitingForGC, suspected, - (now - start) / PR_USEC_PER_MSEC)); + (now - start) / PR_USEC_PER_MSEC, + sForgetSkippableBeforeCC, + sMinForgetSkippableTime / PR_USEC_PER_MSEC, + sMaxForgetSkippableTime / PR_USEC_PER_MSEC, + (sTotalForgetSkippableTime / cleanups) / + PR_USEC_PER_MSEC, + sTotalForgetSkippableTime / PR_USEC_PER_MSEC, + sRemovedPurples)); nsCOMPtr cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID); if (cs) { cs->LogStringMessage(msg.get()); } } + sMinForgetSkippableTime = PR_UINT32_MAX; + sMaxForgetSkippableTime = 0; + sTotalForgetSkippableTime = 0; + sRemovedPurples = 0; + sForgetSkippableBeforeCC = 0; + sCleanupSinceLastGC = true; } // static @@ -3331,9 +3371,50 @@ ShrinkGCBuffersTimerFired(nsITimer *aTimer, void *aClosure) void CCTimerFired(nsITimer *aTimer, void *aClosure) { - NS_RELEASE(sCCTimer); + if (sDidShutdown) { + return; + } + ++sCCTimerFireCount; + if (sCCTimerFireCount < (NS_CC_DELAY / NS_CC_SKIPPABLE_DELAY)) { + PRUint32 suspected = nsCycleCollector_suspectedCount(); + if ((sPreviousSuspectedCount + 100) > suspected) { + // Just few new suspected objects, return early. + return; + } + sPreviousSuspectedCount = suspected; + PRTime startTime; + if (sPostGCEventsToConsole) { + startTime = PR_Now(); + } + nsCycleCollector_forgetSkippable(); + sCleanupSinceLastGC = true; + if (sPostGCEventsToConsole) { + PRTime delta = PR_Now() - startTime; + if (sMinForgetSkippableTime > delta) { + sMinForgetSkippableTime = delta; + } + if (sMaxForgetSkippableTime < delta) { + sMaxForgetSkippableTime = delta; + } + sTotalForgetSkippableTime += delta; + sRemovedPurples += (suspected - nsCycleCollector_suspectedCount()); + ++sForgetSkippableBeforeCC; + } + } else { + sPreviousSuspectedCount = 0; + nsJSContext::KillCCTimer(); + if (nsCycleCollector_suspectedCount() > 500 || + sLastCCEndTime + NS_CC_FORCED < PR_Now()) { + nsJSContext::CycleCollectNow(); + } + } +} - nsJSContext::CycleCollectNow(); +// static +bool +nsJSContext::CleanupSinceLastGC() +{ + return sCleanupSinceLastGC; } // static @@ -3414,30 +3495,21 @@ nsJSContext::PokeShrinkGCBuffers() void nsJSContext::MaybePokeCC() { - if (nsCycleCollector_suspectedCount() > 1000) { - PokeCC(); - } -} - -// static -void -nsJSContext::PokeCC() -{ - if (sCCTimer || !sGCHasRun) { - // There's already a timer for GC'ing, or GC hasn't run yet, just return. + if (sCCTimer) { return; } - CallCreateInstance("@mozilla.org/timer;1", &sCCTimer); - - if (!sCCTimer) { - // Failed to create timer (probably because we're in XPCOM shutdown) - return; + if (nsCycleCollector_suspectedCount() > 100 || + sLastCCEndTime + NS_CC_FORCED < PR_Now()) { + sCCTimerFireCount = 0; + CallCreateInstance("@mozilla.org/timer;1", &sCCTimer); + if (!sCCTimer) { + return; + } + sCCTimer->InitWithFuncCallback(CCTimerFired, nsnull, + NS_CC_SKIPPABLE_DELAY, + nsITimer::TYPE_REPEATING_SLACK); } - - sCCTimer->InitWithFuncCallback(CCTimerFired, nsnull, - NS_CC_DELAY, - nsITimer::TYPE_ONE_SHOT); } //static @@ -3504,6 +3576,8 @@ DOMGCFinishedCallback(JSRuntime *rt, JSCompartment *comp, const char *status) } sCCollectedWaitingForGC = 0; + sCleanupSinceLastGC = false; + if (sGCTimer) { // If we were waiting for a GC to happen, kill the timer. nsJSContext::KillGCTimer(); @@ -3523,7 +3597,7 @@ DOMGCFinishedCallback(JSRuntime *rt, JSCompartment *comp, const char *status) // If this was a full GC, poke the CC to run soon. if (!comp) { sGCHasRun = true; - nsJSContext::PokeCC(); + nsJSContext::MaybePokeCC(); } } diff --git a/dom/base/nsJSEnvironment.h b/dom/base/nsJSEnvironment.h index c8d834887193..43b503e976bd 100644 --- a/dom/base/nsJSEnvironment.h +++ b/dom/base/nsJSEnvironment.h @@ -183,7 +183,10 @@ public: static void GarbageCollectNow(js::gcreason::Reason reason, PRUint32 gckind = nsGCNormal); static void ShrinkGCBuffersNow(); - static void CycleCollectNow(nsICycleCollectorListener *aListener = nsnull); + // 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); static void PokeGC(js::gcreason::Reason aReason); static void KillGCTimer(); @@ -191,12 +194,13 @@ public: static void PokeShrinkGCBuffers(); static void KillShrinkGCBuffersTimer(); - static void PokeCC(); static void MaybePokeCC(); static void KillCCTimer(); virtual void GC(js::gcreason::Reason aReason); + static bool CleanupSinceLastGC(); + nsIScriptGlobalObject* GetCachedGlobalObject() { // Verify that we have a global so that this diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index efc272d1bea2..2f070969e861 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -70,7 +70,7 @@ interface nsIDOMFile; interface nsIFile; interface nsIDOMTouch; -[scriptable, uuid(e01171b0-712a-47ce-8552-b7b2ef0a2507)] +[scriptable, uuid(ab6e9c71-8aa1-40bb-8bf9-65e16429055f)] interface nsIDOMWindowUtils : nsISupports { /** @@ -403,8 +403,15 @@ interface nsIDOMWindowUtils : nsISupports { * @param aListener listener that receives information about the CC graph * (see @mozilla.org/cycle-collector-logger;1 for a logger * component) + * @param aExtraForgetSkippableCalls indicates how many times + * nsCycleCollector_forgetSkippable will + * be called before running cycle collection. + * -1 prevents the default + * nsCycleCollector_forgetSkippable call + * which happens after garbage collection. */ - void garbageCollect([optional] in nsICycleCollectorListener aListener); + void garbageCollect([optional] in nsICycleCollectorListener aListener, + [optional] in long aExtraForgetSkippableCalls); /** * Force a cycle collection without garbage collection. @@ -415,8 +422,15 @@ interface nsIDOMWindowUtils : nsISupports { * @param aListener listener that receives information about the CC graph * (see @mozilla.org/cycle-collector-logger;1 for a logger * component) + * @param aExtraForgetSkippableCalls indicates how many times + * nsCycleCollector_forgetSkippable will + * be called before running cycle collection. + * -1 prevents the default + * nsCycleCollector_forgetSkippable call + * which happens after garbage collection. */ - void cycleCollect([optional] in nsICycleCollectorListener aListener); + void cycleCollect([optional] in nsICycleCollectorListener aListener, + [optional] in long aExtraForgetSkippableCalls); /** Synthesize a simple gesture event for a window. The event types * supported are: MozSwipeGesture, MozMagnifyGestureStart, From bc55d7028ae195963556e960c64536e399f1af3d Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Mon, 30 Jan 2012 22:07:42 +0200 Subject: [PATCH 08/20] Bug 721548 - Cleanup purple buffer during cycle-collector-forget-skippable, r=mrrc8 --HG-- extra : rebase_source : 24265a6e07a778fb308d6273183d5d5e09a06be2 --- content/base/src/nsCCUncollectableMarker.cpp | 150 ++++++++++++++++--- 1 file changed, 133 insertions(+), 17 deletions(-) diff --git a/content/base/src/nsCCUncollectableMarker.cpp b/content/base/src/nsCCUncollectableMarker.cpp index d857d8bcd1eb..d11efa829889 100644 --- a/content/base/src/nsCCUncollectableMarker.cpp +++ b/content/base/src/nsCCUncollectableMarker.cpp @@ -54,6 +54,13 @@ #include "nsIXULWindow.h" #include "nsIAppShellService.h" #include "nsAppShellCID.h" +#include "nsEventListenerManager.h" +#include "nsContentUtils.h" +#include "nsGlobalWindow.h" +#include "nsJSEnvironment.h" +#include "nsInProcessTabChildGlobal.h" +#include "nsFrameLoader.h" +#include "nsGenericElement.h" static bool sInited = 0; PRUint32 nsCCUncollectableMarker::sGeneration = 0; @@ -87,29 +94,122 @@ nsCCUncollectableMarker::Init() rv = obs->AddObserver(marker, "cycle-collector-begin", false); NS_ENSURE_SUCCESS(rv, rv); + rv = obs->AddObserver(marker, "cycle-collector-forget-skippable", false); + NS_ENSURE_SUCCESS(rv, rv); sInited = true; return NS_OK; } +static void +MarkUserData(void* aNode, nsIAtom* aKey, void* aValue, void* aData) +{ + nsIDocument* d = static_cast(aNode)->GetCurrentDoc(); + if (d && nsCCUncollectableMarker::InGeneration(d->GetMarkedCCGeneration())) { + nsGenericElement::MarkUserData(aNode, aKey, aValue, aData); + } +} + +static void +MarkUserDataHandler(void* aNode, nsIAtom* aKey, void* aValue, void* aData) +{ + nsIDocument* d = static_cast(aNode)->GetCurrentDoc(); + if (d && nsCCUncollectableMarker::InGeneration(d->GetMarkedCCGeneration())) { + nsGenericElement::MarkUserDataHandler(aNode, aKey, aValue, aData); + } +} + +static void +MarkMessageManagers() +{ + nsCOMPtr globalMM = + do_GetService("@mozilla.org/globalmessagemanager;1"); + if (!globalMM) { + return; + } + + globalMM->MarkForCC(); + PRUint32 childCount = 0; + globalMM->GetChildCount(&childCount); + for (PRUint32 i = 0; i < childCount; ++i) { + nsCOMPtr windowMM; + globalMM->GetChildAt(i, getter_AddRefs(windowMM)); + if (!windowMM) { + continue; + } + windowMM->MarkForCC(); + PRUint32 tabChildCount = 0; + windowMM->GetChildCount(&tabChildCount); + for (PRUint32 j = 0; j < tabChildCount; ++j) { + nsCOMPtr tabMM; + windowMM->GetChildAt(j, getter_AddRefs(tabMM)); + if (!tabMM) { + continue; + } + tabMM->MarkForCC(); + //XXX hack warning, but works, since we know that + // callback data is frameloader. + void* cb = static_cast(tabMM.get())-> + GetCallbackData(); + nsFrameLoader* fl = static_cast(cb); + if (fl) { + nsIDOMEventTarget* et = fl->GetTabChildGlobalAsEventTarget(); + if (!et) { + continue; + } + static_cast(et)->MarkForCC(); + nsEventListenerManager* elm = et->GetListenerManager(false); + if (elm) { + elm->UnmarkGrayJSListeners(); + } + } + } + } +} + void -MarkContentViewer(nsIContentViewer* aViewer) +MarkContentViewer(nsIContentViewer* aViewer, bool aCleanupJS, + bool aPrepareForCC) { if (!aViewer) { return; } nsIDocument *doc = aViewer->GetDocument(); - if (doc) { + if (doc && + doc->GetMarkedCCGeneration() != nsCCUncollectableMarker::sGeneration) { doc->MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration); + if (aCleanupJS) { + nsEventListenerManager* elm = doc->GetListenerManager(false); + if (elm) { + elm->UnmarkGrayJSListeners(); + } + nsCOMPtr win = do_QueryInterface(doc->GetInnerWindow()); + if (win) { + elm = win->GetListenerManager(false); + if (elm) { + elm->UnmarkGrayJSListeners(); + } + static_cast(win.get())->UnmarkGrayTimers(); + } + + doc->PropertyTable(DOM_USER_DATA_HANDLER)-> + EnumerateAll(MarkUserDataHandler, &nsCCUncollectableMarker::sGeneration); + } else if (aPrepareForCC) { + // Unfortunately we need to still mark user data just before running CC so + // that it has the right generation. + doc->PropertyTable(DOM_USER_DATA)-> + EnumerateAll(MarkUserData, &nsCCUncollectableMarker::sGeneration); + } } } -void MarkDocShell(nsIDocShellTreeNode* aNode); +void MarkDocShell(nsIDocShellTreeNode* aNode, bool aCleanupJS, + bool aPrepareForCC); void -MarkSHEntry(nsISHEntry* aSHEntry) +MarkSHEntry(nsISHEntry* aSHEntry, bool aCleanupJS, bool aPrepareForCC) { if (!aSHEntry) { return; @@ -117,13 +217,13 @@ MarkSHEntry(nsISHEntry* aSHEntry) nsCOMPtr cview; aSHEntry->GetContentViewer(getter_AddRefs(cview)); - MarkContentViewer(cview); + MarkContentViewer(cview, aCleanupJS, aPrepareForCC); nsCOMPtr child; PRInt32 i = 0; while (NS_SUCCEEDED(aSHEntry->ChildShellAt(i++, getter_AddRefs(child))) && child) { - MarkDocShell(child); + MarkDocShell(child, aCleanupJS, aPrepareForCC); } nsCOMPtr shCont = do_QueryInterface(aSHEntry); @@ -132,13 +232,13 @@ MarkSHEntry(nsISHEntry* aSHEntry) for (i = 0; i < count; ++i) { nsCOMPtr childEntry; shCont->GetChildAt(i, getter_AddRefs(childEntry)); - MarkSHEntry(childEntry); + MarkSHEntry(childEntry, aCleanupJS, aPrepareForCC); } } void -MarkDocShell(nsIDocShellTreeNode* aNode) +MarkDocShell(nsIDocShellTreeNode* aNode, bool aCleanupJS, bool aPrepareForCC) { nsCOMPtr shell = do_QueryInterface(aNode); if (!shell) { @@ -147,7 +247,7 @@ MarkDocShell(nsIDocShellTreeNode* aNode) nsCOMPtr cview; shell->GetContentViewer(getter_AddRefs(cview)); - MarkContentViewer(cview); + MarkContentViewer(cview, aCleanupJS, aPrepareForCC); nsCOMPtr webNav = do_QueryInterface(shell); nsCOMPtr history; @@ -160,7 +260,7 @@ MarkDocShell(nsIDocShellTreeNode* aNode) history->GetEntryAtIndex(i, false, getter_AddRefs(historyEntry)); nsCOMPtr shEntry = do_QueryInterface(historyEntry); - MarkSHEntry(shEntry); + MarkSHEntry(shEntry, aCleanupJS, aPrepareForCC); } } @@ -169,12 +269,13 @@ MarkDocShell(nsIDocShellTreeNode* aNode) for (i = 0; i < childCount; ++i) { nsCOMPtr child; aNode->GetChildAt(i, getter_AddRefs(child)); - MarkDocShell(child); + MarkDocShell(child, aCleanupJS, aPrepareForCC); } } void -MarkWindowList(nsISimpleEnumerator* aWindowList) +MarkWindowList(nsISimpleEnumerator* aWindowList, bool aCleanupJS, + bool aPrepareForCC) { nsCOMPtr iter; while (NS_SUCCEEDED(aWindowList->GetNext(getter_AddRefs(iter))) && @@ -184,7 +285,7 @@ MarkWindowList(nsISimpleEnumerator* aWindowList) nsCOMPtr rootDocShell = do_QueryInterface(window->GetDocShell()); - MarkDocShell(rootDocShell); + MarkDocShell(rootDocShell, aCleanupJS, aPrepareForCC); } } } @@ -202,13 +303,23 @@ nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic, // No need for kungFuDeathGrip here, yay observerservice! obs->RemoveObserver(this, "xpcom-shutdown"); obs->RemoveObserver(this, "cycle-collector-begin"); + obs->RemoveObserver(this, "cycle-collector-forget-skippable"); sGeneration = 0; return NS_OK; } - NS_ASSERTION(!strcmp(aTopic, "cycle-collector-begin"), "wrong topic"); + NS_ASSERTION(!strcmp(aTopic, "cycle-collector-begin") || + !strcmp(aTopic, "cycle-collector-forget-skippable"), "wrong topic"); + + // JS cleanup can be slow. Do it only if there has been a GC. + bool cleanupJS = + !nsJSContext::CleanupSinceLastGC() && + !strcmp(aTopic, "cycle-collector-forget-skippable"); + + bool prepareForCC = !strcmp(aTopic, "cycle-collector-begin"); + // Increase generation to effectivly unmark all current objects if (!++sGeneration) { @@ -225,7 +336,7 @@ nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic, rv = med->GetEnumerator(nsnull, getter_AddRefs(windowList)); NS_ENSURE_SUCCESS(rv, rv); - MarkWindowList(windowList); + MarkWindowList(windowList, cleanupJS, prepareForCC); } nsCOMPtr ww = @@ -234,7 +345,7 @@ nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic, rv = ww->GetWindowEnumerator(getter_AddRefs(windowList)); NS_ENSURE_SUCCESS(rv, rv); - MarkWindowList(windowList); + MarkWindowList(windowList, cleanupJS, prepareForCC); } nsCOMPtr appShell = @@ -246,10 +357,15 @@ nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic, nsCOMPtr shell; hw->GetDocShell(getter_AddRefs(shell)); nsCOMPtr shellTreeNode = do_QueryInterface(shell); - MarkDocShell(shellTreeNode); + MarkDocShell(shellTreeNode, cleanupJS, prepareForCC); } } + if (cleanupJS) { + nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments(sGeneration); + MarkMessageManagers(); + } + #ifdef MOZ_XUL nsXULPrototypeCache* xulCache = nsXULPrototypeCache::GetInstance(); if (xulCache) { From ee55c49fb21634bc792b6b4c497fa150543fb3b4 Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Mon, 30 Jan 2012 22:08:13 +0200 Subject: [PATCH 09/20] Bug 721515 - Add Documents, elements and textnodes to BBP, r=mccr8,jst --HG-- extra : rebase_source : 103b7572ee627990ed53f214b31d0badfdb05585 --- content/base/public/nsIContent.h | 7 +- content/base/public/nsINode.h | 21 +- content/base/src/nsContentUtils.cpp | 2 + content/base/src/nsDocument.cpp | 12 + content/base/src/nsDocument.h | 4 +- content/base/src/nsGenericDOMDataNode.cpp | 12 + content/base/src/nsGenericDOMDataNode.h | 12 +- content/base/src/nsGenericElement.cpp | 408 +++++++++++++++++++++- content/base/src/nsGenericElement.h | 20 +- 9 files changed, 475 insertions(+), 23 deletions(-) diff --git a/content/base/public/nsIContent.h b/content/base/public/nsIContent.h index 49389b4c5c27..3284d806b467 100644 --- a/content/base/public/nsIContent.h +++ b/content/base/public/nsIContent.h @@ -78,8 +78,8 @@ enum nsLinkState { // IID for the nsIContent interface #define NS_ICONTENT_IID \ -{ 0xdc68f070, 0x226d, 0x11e1, \ - { 0xbf, 0xc2, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 } } +{ 0x94671671, 0x9e1b, 0x447a, \ + { 0xad, 0xb7, 0xc3, 0x2e, 0x05, 0x6a, 0x96, 0xc9 } } /** * A node of content in a document's content model. This interface @@ -948,6 +948,9 @@ public: virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor); + virtual bool IsPurple() = 0; + virtual void RemovePurple() = 0; + protected: /** * Hook for implementing GetID. This is guaranteed to only be diff --git a/content/base/public/nsINode.h b/content/base/public/nsINode.h index 8061a1a8cdb3..4c20f1b8c7d8 100644 --- a/content/base/public/nsINode.h +++ b/content/base/public/nsINode.h @@ -288,8 +288,8 @@ private: // IID for the nsINode interface #define NS_INODE_IID \ -{ 0xd026d280, 0x5b25, 0x41c0, \ - { 0x92, 0xcf, 0x6, 0xf6, 0xf, 0xb, 0x9a, 0xfe } } +{ 0xfcd3b0d1, 0x75db, 0x46c4, \ + { 0xa1, 0xf5, 0x07, 0xc2, 0x09, 0xf8, 0x1f, 0x44 } } /** * An internal interface that abstracts some DOMNode-related parts that both @@ -1223,6 +1223,13 @@ private: NodeIsCommonAncestorForRangeInSelection, // Set if the node is a descendant of a node with the above bit set. NodeIsDescendantOfCommonAncestorForRangeInSelection, + // Set if CanSkipInCC check has been done for this subtree root. + NodeIsCCMarkedRoot, + // Maybe set if this node is in black subtree. + NodeIsCCBlackTree, + // Maybe set if the node is a root of a subtree + // which needs to be kept in the purple buffer. + NodeIsPurpleRoot, // Guard value BooleanFlagCount }; @@ -1270,6 +1277,16 @@ public: void ClearDescendantOfCommonAncestorForRangeInSelection() { ClearBoolFlag(NodeIsDescendantOfCommonAncestorForRangeInSelection); } + void SetCCMarkedRoot(bool aValue) + { SetBoolFlag(NodeIsCCMarkedRoot, aValue); } + bool CCMarkedRoot() const { return GetBoolFlag(NodeIsCCMarkedRoot); } + void SetInCCBlackTree(bool aValue) + { SetBoolFlag(NodeIsCCBlackTree, aValue); } + bool InCCBlackTree() const { return GetBoolFlag(NodeIsCCBlackTree); } + void SetIsPurpleRoot(bool aValue) + { SetBoolFlag(NodeIsPurpleRoot, aValue); } + bool IsPurpleRoot() const { return GetBoolFlag(NodeIsPurpleRoot); } + protected: void SetParentIsContent(bool aValue) { SetBoolFlag(ParentIsContent, aValue); } void SetInDocument() { SetBoolFlag(IsInDocument); } diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 52f42c7fc2b3..79a177b37b57 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -421,6 +421,8 @@ nsContentUtils::Init() "dom.event.handling-user-input-time-limit", 1000); + nsGenericElement::InitCCCallbacks(); + sInitialized = true; return NS_OK; diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index d9babf897b79..8c13166b9bbb 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -1722,6 +1722,18 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocument) NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(nsDocument, nsNodeUtils::LastRelease(this)) +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDocument) + return nsGenericElement::CanSkip(tmp); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDocument) + return nsGenericElement::CanSkipInCC(tmp); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDocument) + return nsGenericElement::CanSkipThis(tmp); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END + static PLDHashOperator SubDocTraverser(PLDHashTable *table, PLDHashEntryHdr *hdr, PRUint32 number, void *arg) diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index 72d3303f9706..f88563c3aa6b 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -880,8 +880,8 @@ public: MaybeRescheduleAnimationFrameNotifications(); } - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsDocument, - nsIDocument) + NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsDocument, + nsIDocument) void DoNotifyPossibleTitleChange(); diff --git a/content/base/src/nsGenericDOMDataNode.cpp b/content/base/src/nsGenericDOMDataNode.cpp index 037dd4f96aa3..4f1c0f35fc2f 100644 --- a/content/base/src/nsGenericDOMDataNode.cpp +++ b/content/base/src/nsGenericDOMDataNode.cpp @@ -97,6 +97,18 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGenericDOMDataNode) nsINode::Trace(tmp, aCallback, aClosure); NS_IMPL_CYCLE_COLLECTION_TRACE_END +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGenericDOMDataNode) + return nsGenericElement::CanSkip(tmp); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGenericDOMDataNode) + return nsGenericElement::CanSkipInCC(tmp); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGenericDOMDataNode) + return nsGenericElement::CanSkipThis(tmp); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGenericDOMDataNode) // Always need to traverse script objects, so do that before we check // if we're uncollectable. diff --git a/content/base/src/nsGenericDOMDataNode.h b/content/base/src/nsGenericDOMDataNode.h index 6739da3b3f4b..2d87862ee343 100644 --- a/content/base/src/nsGenericDOMDataNode.h +++ b/content/base/src/nsGenericDOMDataNode.h @@ -272,7 +272,7 @@ public: void ToCString(nsAString& aBuf, PRInt32 aOffset, PRInt32 aLen) const; #endif - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsGenericDOMDataNode) + NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(nsGenericDOMDataNode) protected: virtual mozilla::dom::Element* GetNameSpaceElement() @@ -348,6 +348,16 @@ protected: nsTextFragment mText; +public: + virtual bool IsPurple() + { + return mRefCnt.IsPurple(); + } + virtual void RemovePurple() + { + mRefCnt.RemovePurple(); + } + private: already_AddRefed GetCurrentValueAtom(); }; diff --git a/content/base/src/nsGenericElement.cpp b/content/base/src/nsGenericElement.cpp index 8968756bb059..7f33f31077d3 100644 --- a/content/base/src/nsGenericElement.cpp +++ b/content/base/src/nsGenericElement.cpp @@ -153,7 +153,7 @@ #include "nsSVGFeatures.h" #include "nsDOMMemoryReporter.h" #include "nsWrapperCacheInlines.h" - +#include "nsCycleCollector.h" #include "xpcpublic.h" #include "xpcprivate.h" @@ -1208,11 +1208,20 @@ nsINode::Trace(nsINode *tmp, TraceCallback cb, void *closure) nsContentUtils::TraceWrapper(tmp, cb, closure); } -static bool -IsXBL(nsINode* aNode) + +static +bool UnoptimizableCCNode(nsINode* aNode) { - return aNode->IsElement() && - aNode->AsElement()->IsInNamespace(kNameSpaceID_XBL); + const PtrBits problematicFlags = (NODE_IS_ANONYMOUS | + NODE_IS_IN_ANONYMOUS_SUBTREE | + NODE_IS_NATIVE_ANONYMOUS_ROOT | + NODE_MAY_BE_IN_BINDING_MNGR | + NODE_IS_INSERTION_PARENT); + return aNode->HasFlag(problematicFlags) || + aNode->NodeType() == nsIDOMNode::ATTRIBUTE_NODE || + // For strange cases like xbl:content/xbl:children + (aNode->IsElement() && + aNode->AsElement()->IsInNamespace(kNameSpaceID_XBL)); } /* static */ @@ -1227,18 +1236,11 @@ nsINode::Traverse(nsINode *tmp, nsCycleCollectionTraversalCallback &cb) if (nsCCUncollectableMarker::sGeneration) { // If we're black no need to traverse. - if (tmp->IsBlack()) { + if (tmp->IsBlack() || tmp->InCCBlackTree()) { return false; } - const PtrBits problematicFlags = - (NODE_IS_ANONYMOUS | - NODE_IS_IN_ANONYMOUS_SUBTREE | - NODE_IS_NATIVE_ANONYMOUS_ROOT | - NODE_MAY_BE_IN_BINDING_MNGR | - NODE_IS_INSERTION_PARENT); - - if (!tmp->HasFlag(problematicFlags) && !IsXBL(tmp)) { + if (!UnoptimizableCCNode(tmp)) { // If we're in a black document, return early. if ((currentDoc && currentDoc->IsBlack())) { return false; @@ -1246,7 +1248,7 @@ nsINode::Traverse(nsINode *tmp, nsCycleCollectionTraversalCallback &cb) // If we're not in anonymous content and we have a black parent, // return early. nsIContent* parent = tmp->GetParent(); - if (parent && !IsXBL(parent) && parent->IsBlack()) { + if (parent && !UnoptimizableCCNode(parent) && parent->IsBlack()) { NS_ABORT_IF_FALSE(parent->IndexOf(tmp) >= 0, "Parent doesn't own us?"); return false; } @@ -4256,6 +4258,382 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGenericElement) nsINode::Trace(tmp, aCallback, aClosure); NS_IMPL_CYCLE_COLLECTION_TRACE_END +static JSObject* +GetJSObjectChild(nsINode* aNode) +{ + if (aNode->PreservingWrapper()) { + return aNode->GetWrapperPreserveColor(); + } + return aNode->GetExpandoObjectPreserveColor(); +} + +static bool +NeedsScriptTraverse(nsINode* aNode) +{ + JSObject* o = GetJSObjectChild(aNode); + return o && xpc_IsGrayGCThing(o); +} + +void +nsGenericElement::MarkUserData(void* aObject, nsIAtom* aKey, void* aChild, + void* aData) +{ + PRUint32* gen = static_cast(aData); + xpc_MarkInCCGeneration(static_cast(aChild), *gen); +} + +void +nsGenericElement::MarkUserDataHandler(void* aObject, nsIAtom* aKey, + void* aChild, void* aData) +{ + nsCOMPtr wjs = + do_QueryInterface(static_cast(aChild)); + xpc_UnmarkGrayObject(wjs); +} + +static void +MarkNodeChildren(nsINode* aNode) +{ + JSObject* o = GetJSObjectChild(aNode); + xpc_UnmarkGrayObject(o); + + nsEventListenerManager* elm = aNode->GetListenerManager(false); + if (elm) { + elm->UnmarkGrayJSListeners(); + } + + if (aNode->HasProperties()) { + nsIDocument* ownerDoc = aNode->OwnerDoc(); + ownerDoc->PropertyTable(DOM_USER_DATA)-> + Enumerate(aNode, nsGenericElement::MarkUserData, + &nsCCUncollectableMarker::sGeneration); + ownerDoc->PropertyTable(DOM_USER_DATA_HANDLER)-> + Enumerate(aNode, nsGenericElement::MarkUserDataHandler, + &nsCCUncollectableMarker::sGeneration); + } +} + +nsINode* +FindOptimizableSubtreeRoot(nsINode* aNode) +{ + nsINode* p; + while ((p = aNode->GetNodeParent())) { + if (UnoptimizableCCNode(aNode)) { + return nsnull; + } + aNode = p; + } + + if (UnoptimizableCCNode(aNode)) { + return nsnull; + } + return aNode; +} + +nsAutoTArray* gCCBlackMarkedNodes = nsnull; + +void +ClearBlackMarkedNodes() +{ + if (!gCCBlackMarkedNodes) { + return; + } + PRUint32 len = gCCBlackMarkedNodes->Length(); + for (PRUint32 i = 0; i < len; ++i) { + nsINode* n = gCCBlackMarkedNodes->ElementAt(i); + n->SetCCMarkedRoot(false); + n->SetInCCBlackTree(false); + } + delete gCCBlackMarkedNodes; + gCCBlackMarkedNodes = nsnull; +} + +// static +bool +nsGenericElement::CanSkipInCC(nsINode* aNode) +{ + // Don't try to optimize anything during shutdown. + if (nsCCUncollectableMarker::sGeneration == 0) { + return false; + } + + // Bail out early if aNode is somewhere in anonymous content, + // or otherwise unusual. + if (UnoptimizableCCNode(aNode)) { + return false; + } + + nsIDocument* currentDoc = aNode->GetCurrentDoc(); + if (currentDoc && + nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration())) { + return !NeedsScriptTraverse(aNode); + } + + nsINode* root = + currentDoc ? static_cast(currentDoc) : + FindOptimizableSubtreeRoot(aNode); + if (!root) { + return false; + } + + // Subtree has been traversed already. + if (root->CCMarkedRoot()) { + return root->InCCBlackTree() && !NeedsScriptTraverse(aNode); + } + + if (!gCCBlackMarkedNodes) { + gCCBlackMarkedNodes = new nsAutoTArray; + } + + // nodesToUnpurple contains nodes which will be removed + // from the purple buffer if the DOM tree is black. + nsAutoTArray nodesToUnpurple; + // grayNodes need script traverse, so they aren't removed from + // the purple buffer, but are marked to be in black subtree so that + // traverse is faster. + nsAutoTArray grayNodes; + + bool foundBlack = root->IsBlack(); + if (root != currentDoc) { + currentDoc = nsnull; + if (NeedsScriptTraverse(root)) { + grayNodes.AppendElement(root); + } else if (static_cast(root)->IsPurple()) { + nodesToUnpurple.AppendElement(static_cast(root)); + } + } + + // Traverse the subtree and check if we could know without CC + // that it is black. + // Note, this traverse is non-virtual and inline, so it should be a lot faster + // than CC's generic traverse. + for (nsIContent* node = root->GetFirstChild(); node; + node = node->GetNextNode(root)) { + foundBlack = foundBlack || node->IsBlack(); + if (foundBlack && 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 + // handled, it is fast to check that currentDoc is in CCGeneration. + break; + } + if (NeedsScriptTraverse(node)) { + // Gray nodes need real CC traverse. + grayNodes.AppendElement(node); + } else if (node->IsPurple()) { + nodesToUnpurple.AppendElement(node); + } + } + + root->SetCCMarkedRoot(true); + root->SetInCCBlackTree(foundBlack); + gCCBlackMarkedNodes->AppendElement(root); + + if (!foundBlack) { + return false; + } + + if (currentDoc) { + // Special case documents. If we know the document is black, + // we can mark the document to be in CCGeneration. + currentDoc-> + MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration); + } else { + for (PRUint32 i = 0; i < grayNodes.Length(); ++i) { + nsINode* node = grayNodes[i]; + node->SetInCCBlackTree(true); + } + gCCBlackMarkedNodes->AppendElements(grayNodes); + } + + // Subtree is black, we can remove non-gray purple nodes from + // purple buffer. + for (PRUint32 i = 0; i < nodesToUnpurple.Length(); ++i) { + nsIContent* purple = nodesToUnpurple[i]; + // Can't remove currently handled purple node. + if (purple != aNode) { + purple->RemovePurple(); + } + } + return !NeedsScriptTraverse(aNode); +} + +nsAutoTArray* gPurpleRoots = nsnull; + +void ClearPurpleRoots() +{ + if (!gPurpleRoots) { + return; + } + PRUint32 len = gPurpleRoots->Length(); + for (PRUint32 i = 0; i < len; ++i) { + nsINode* n = gPurpleRoots->ElementAt(i); + n->SetIsPurpleRoot(false); + } + delete gPurpleRoots; + gPurpleRoots = nsnull; +} + +static bool +ShouldClearPurple(nsIContent* aContent) +{ + if (aContent && aContent->IsPurple()) { + return true; + } + + JSObject* o = GetJSObjectChild(aContent); + if (o && xpc_IsGrayGCThing(o)) { + return true; + } + + if (aContent->GetListenerManager(false)) { + return true; + } + + return aContent->HasProperties(); +} + +// CanSkip checks if aNode is black, and if it is, returns +// true. If aNode is in a black DOM tree, CanSkip may also remove other objects +// from purple buffer and unmark event listeners and user data. +// If the root of the DOM tree is a document, less optimizations are done +// since checking the blackness of the current document is usually fast and we +// don't want slow down such common cases. +bool +nsGenericElement::CanSkip(nsINode* aNode) +{ + // Don't try to optimize anything during shutdown. + if (nsCCUncollectableMarker::sGeneration == 0) { + return false; + } + + // Bail out early if aNode is somewhere in anonymous content, + // or otherwise unusual. + if (UnoptimizableCCNode(aNode)) { + return false; + } + + nsIDocument* currentDoc = aNode->GetCurrentDoc(); + if (currentDoc && + nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration())) { + MarkNodeChildren(aNode); + return true; + } + + nsINode* root = currentDoc ? static_cast(currentDoc) : + FindOptimizableSubtreeRoot(aNode); + if (!root) { + return false; + } + + // Subtree has been traversed already, and aNode + // wasn't removed from purple buffer. No need to do more here. + if (root->IsPurpleRoot()) { + return false; + } + + // nodesToClear contains nodes which are either purple or + // gray. + nsAutoTArray nodesToClear; + + bool foundBlack = root->IsBlack(); + if (root != currentDoc) { + currentDoc = nsnull; + if (ShouldClearPurple(static_cast(root))) { + nodesToClear.AppendElement(static_cast(root)); + } + } + + // Traverse the subtree and check if we could know without CC + // that it is black. + // Note, this traverse is non-virtual and inline, so it should be a lot faster + // than CC's generic traverse. + for (nsIContent* node = root->GetFirstChild(); node; + node = node->GetNextNode(root)) { + foundBlack = foundBlack || node->IsBlack(); + if (foundBlack) { + 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 + // handled, it is fast to check that the currentDoc is in CCGeneration. + break; + } + // No need to put stuff to the nodesToClear array, if we can clear it + // already here. + if (node->IsPurple() && node != aNode) { + 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); + } + } + + if (!foundBlack) { + if (!gPurpleRoots) { + gPurpleRoots = new nsAutoTArray(); + } + root->SetIsPurpleRoot(true); + gPurpleRoots->AppendElement(root); + return false; + } + + if (currentDoc) { + // Special case documents. If we know the document is black, + // we can mark the document to be in CCGeneration. + currentDoc-> + MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration); + MarkNodeChildren(currentDoc); + } + + // Subtree is black, so we can remove purple nodes from + // purple buffer and mark stuff that to be certainly alive. + for (PRUint32 i = 0; i < nodesToClear.Length(); ++i) { + nsIContent* n = nodesToClear[i]; + MarkNodeChildren(n); + // Can't remove currently handled purple node. + if (n != aNode && n->IsPurple()) { + n->RemovePurple(); + } + } + return true; +} + +bool +nsGenericElement::CanSkipThis(nsINode* aNode) +{ + if (nsCCUncollectableMarker::sGeneration == 0) { + return false; + } + if (aNode->IsBlack()) { + return true; + } + nsIDocument* c = aNode->GetCurrentDoc(); + return + ((c && nsCCUncollectableMarker::InGeneration(c->GetMarkedCCGeneration())) || + aNode->InCCBlackTree()) && !NeedsScriptTraverse(aNode); +} + +void +nsGenericElement::InitCCCallbacks() +{ + nsCycleCollector_setForgetSkippableCallback(ClearPurpleRoots); + nsCycleCollector_setBeforeUnlinkCallback(ClearBlackMarkedNodes); +} + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGenericElement) + return nsGenericElement::CanSkip(tmp); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGenericElement) + return nsGenericElement::CanSkipInCC(tmp); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGenericElement) + return nsGenericElement::CanSkipThis(tmp); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END + static const char* kNSURIs[] = { " ([none])", " (xmlns)", diff --git a/content/base/src/nsGenericElement.h b/content/base/src/nsGenericElement.h index 21e3bf5d5619..b9b9d21fac06 100644 --- a/content/base/src/nsGenericElement.h +++ b/content/base/src/nsGenericElement.h @@ -602,7 +602,7 @@ public: */ virtual nsAttrInfo GetAttrInfo(PRInt32 aNamespaceID, nsIAtom* aName) const; - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsGenericElement) + NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(nsGenericElement) virtual void NodeInfoChanged(nsINodeInfo* aOldNodeInfo) { @@ -613,6 +613,24 @@ public: */ void FireNodeRemovedForChildren(); + virtual bool IsPurple() + { + return mRefCnt.IsPurple(); + } + + virtual void RemovePurple() + { + mRefCnt.RemovePurple(); + } + + static bool CanSkip(nsINode* aNode); + static bool CanSkipInCC(nsINode* aNode); + static bool CanSkipThis(nsINode* aNode); + static void InitCCCallbacks(); + static void MarkUserData(void* aObject, nsIAtom* aKey, void* aChild, + void *aData); + static void MarkUserDataHandler(void* aObject, nsIAtom* aKey, void* aChild, + void* aData); protected: /** * Set attribute and (if needed) notify documentobservers and fire off From cf6b7f3b9e5b7433bb716c5b8392e6002e2c30f2 Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Mon, 30 Jan 2012 13:18:04 -0800 Subject: [PATCH 10/20] Back out f410bdf30132 (bug 722428) for mochitest bustage --- .../mochitest/specialpowers/content/specialpowers.js | 2 +- .../mochitest/tests/SimpleTest/specialpowersAPI.js | 12 ++++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/testing/mochitest/specialpowers/content/specialpowers.js b/testing/mochitest/specialpowers/content/specialpowers.js index f7f471132648..b2805de9d35d 100644 --- a/testing/mochitest/specialpowers/content/specialpowers.js +++ b/testing/mochitest/specialpowers/content/specialpowers.js @@ -39,7 +39,7 @@ */ function SpecialPowers(window) { - this.window = Components.utils.getWeakReference(window); + this.window = window; this._encounteredCrashDumpFiles = []; this._unexpectedCrashDumpFiles = { }; this._crashDumpDir = null; diff --git a/testing/mochitest/tests/SimpleTest/specialpowersAPI.js b/testing/mochitest/tests/SimpleTest/specialpowersAPI.js index 8a669a20ca62..953b4e00b8eb 100644 --- a/testing/mochitest/tests/SimpleTest/specialpowersAPI.js +++ b/testing/mochitest/tests/SimpleTest/specialpowersAPI.js @@ -63,7 +63,6 @@ function bindDOMWindowUtils(aWindow) { var util = aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIDOMWindowUtils); - var weakUtil = Components.utils.getWeakReference(util); // This bit of magic brought to you by the letters // B Z, and E, S and the number 5. // @@ -78,10 +77,7 @@ function bindDOMWindowUtils(aWindow) { if (prop in desc && typeof(desc[prop]) == "function") { var oldval = desc[prop]; try { - desc[prop] = function() { - utils = weakUtil.get(); - return oldval.apply(utils, arguments); - }; + desc[prop] = function() { return oldval.apply(util, arguments); }; } catch (ex) { dump("WARNING: Special Powers failed to rebind function: " + desc + "::" + prop + "\n"); } @@ -430,7 +426,7 @@ SpecialPowersAPI.prototype = { }, getDOMWindowUtils: function(aWindow) { - if (aWindow == this.window.get() && this.DOMWindowUtils != null) + if (aWindow == this.window && this.DOMWindowUtils != null) return this.DOMWindowUtils; return bindDOMWindowUtils(aWindow); @@ -818,7 +814,7 @@ SpecialPowersAPI.prototype = { }, snapshotWindow: function (win, withCaret) { - var el = this.window.get().document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); + var el = this.window.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); el.width = win.innerWidth; el.height = win.innerHeight; var ctx = el.getContext("2d"); @@ -1033,7 +1029,7 @@ SpecialPowersAPI.prototype = { }, snapshotWindow: function (win, withCaret) { - var el = this.window.get().document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); + var el = this.window.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); el.width = win.innerWidth; el.height = win.innerHeight; var ctx = el.getContext("2d"); From 5d4b9bd29a150b8ef8ae3b70d4f9430344c269a7 Mon Sep 17 00:00:00 2001 From: Neil Rashbrook Date: Mon, 30 Jan 2012 22:28:21 +0000 Subject: [PATCH 11/20] Better test for bug 649840 r=ehsan --- .../satchel/test/test_bug_511615.html | 4 +- .../content/tests/chrome/test_bug649840.xul | 66 ------------------- 2 files changed, 3 insertions(+), 67 deletions(-) delete mode 100644 toolkit/content/tests/chrome/test_bug649840.xul diff --git a/toolkit/components/satchel/test/test_bug_511615.html b/toolkit/components/satchel/test/test_bug_511615.html index 0ba5ccd01216..0854689639df 100644 --- a/toolkit/components/satchel/test/test_bug_511615.html +++ b/toolkit/components/satchel/test/test_bug_511615.html @@ -12,7 +12,7 @@ Form History test: form field autocomplete

-
+
@@ -32,6 +32,7 @@ Form History test: form field autocomplete netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); var autocompletePopup = getAutocompletePopup(); +autocompletePopup.style.direction = "ltr"; var input = $_(1, "field1"); @@ -366,6 +367,7 @@ function runTest(testNum) { case 211: checkPopupOpen(false); checkForm(""); + is(autocompletePopup.style.direction, "rtl", "direction should have been changed from ltr to rtl"); SimpleTest.finish(); return; diff --git a/toolkit/content/tests/chrome/test_bug649840.xul b/toolkit/content/tests/chrome/test_bug649840.xul deleted file mode 100644 index f2c0c0dc0489..000000000000 --- a/toolkit/content/tests/chrome/test_bug649840.xul +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - From c179e0057ad5300255cf5a8d8c83663b7aef7ce4 Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Mon, 30 Jan 2012 23:20:54 +0000 Subject: [PATCH 12/20] Backout 8705f07a49ec (bug 649840) for bustage --- .../satchel/test/test_bug_511615.html | 4 +- .../content/tests/chrome/test_bug649840.xul | 66 +++++++++++++++++++ 2 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 toolkit/content/tests/chrome/test_bug649840.xul diff --git a/toolkit/components/satchel/test/test_bug_511615.html b/toolkit/components/satchel/test/test_bug_511615.html index 0854689639df..0ba5ccd01216 100644 --- a/toolkit/components/satchel/test/test_bug_511615.html +++ b/toolkit/components/satchel/test/test_bug_511615.html @@ -12,7 +12,7 @@ Form History test: form field autocomplete

-
+
@@ -32,7 +32,6 @@ Form History test: form field autocomplete netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); var autocompletePopup = getAutocompletePopup(); -autocompletePopup.style.direction = "ltr"; var input = $_(1, "field1"); @@ -367,7 +366,6 @@ function runTest(testNum) { case 211: checkPopupOpen(false); checkForm(""); - is(autocompletePopup.style.direction, "rtl", "direction should have been changed from ltr to rtl"); SimpleTest.finish(); return; diff --git a/toolkit/content/tests/chrome/test_bug649840.xul b/toolkit/content/tests/chrome/test_bug649840.xul new file mode 100644 index 000000000000..f2c0c0dc0489 --- /dev/null +++ b/toolkit/content/tests/chrome/test_bug649840.xul @@ -0,0 +1,66 @@ + + + + + + + From be421c5a23196f4e0fc9b0641648a0723278b72b Mon Sep 17 00:00:00 2001 From: Neil Rashbrook Date: Tue, 31 Jan 2012 00:27:23 +0100 Subject: [PATCH 13/20] Better test for bug 649840. r=ehsan. + bustage-fix by sgautherie. --- .../satchel/test/test_bug_511615.html | 4 +- toolkit/content/tests/chrome/Makefile.in | 1 - .../content/tests/chrome/test_bug649840.xul | 66 ------------------- 3 files changed, 3 insertions(+), 68 deletions(-) delete mode 100644 toolkit/content/tests/chrome/test_bug649840.xul diff --git a/toolkit/components/satchel/test/test_bug_511615.html b/toolkit/components/satchel/test/test_bug_511615.html index 0ba5ccd01216..0854689639df 100644 --- a/toolkit/components/satchel/test/test_bug_511615.html +++ b/toolkit/components/satchel/test/test_bug_511615.html @@ -12,7 +12,7 @@ Form History test: form field autocomplete

-
+
@@ -32,6 +32,7 @@ Form History test: form field autocomplete netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); var autocompletePopup = getAutocompletePopup(); +autocompletePopup.style.direction = "ltr"; var input = $_(1, "field1"); @@ -366,6 +367,7 @@ function runTest(testNum) { case 211: checkPopupOpen(false); checkForm(""); + is(autocompletePopup.style.direction, "rtl", "direction should have been changed from ltr to rtl"); SimpleTest.finish(); return; diff --git a/toolkit/content/tests/chrome/Makefile.in b/toolkit/content/tests/chrome/Makefile.in index 3d585c110d3c..5e5fd4d640b7 100644 --- a/toolkit/content/tests/chrome/Makefile.in +++ b/toolkit/content/tests/chrome/Makefile.in @@ -72,7 +72,6 @@ _TEST_FILES = findbar_window.xul \ test_bug570192.xul \ test_bug624329.xul \ bug624329_window.xul \ - test_bug649840.xul \ test_popup_preventdefault_chrome.xul \ window_popup_preventdefault_chrome.xul \ test_largemenu.xul \ diff --git a/toolkit/content/tests/chrome/test_bug649840.xul b/toolkit/content/tests/chrome/test_bug649840.xul deleted file mode 100644 index f2c0c0dc0489..000000000000 --- a/toolkit/content/tests/chrome/test_bug649840.xul +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - From 7f2b82582bf29c993c269fdf35910d5204767cdd Mon Sep 17 00:00:00 2001 From: Brian Birtles Date: Tue, 31 Jan 2012 09:54:10 +0900 Subject: [PATCH 14/20] Bug 720103 - Pass timed element not interval to notify new interval callback; r=dholbert --- content/smil/nsSMILTimedElement.cpp | 11 ++++++++--- content/smil/nsSMILTimedElement.h | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/content/smil/nsSMILTimedElement.cpp b/content/smil/nsSMILTimedElement.cpp index fefd6944c136..dbfa2904a738 100644 --- a/content/smil/nsSMILTimedElement.cpp +++ b/content/smil/nsSMILTimedElement.cpp @@ -2199,7 +2199,7 @@ nsSMILTimedElement::NotifyNewInterval() container->SyncPauseTime(); } - NotifyTimeDependentsParams params = { mCurrentInterval, container }; + NotifyTimeDependentsParams params = { this, container }; mTimeDependents.EnumerateEntries(NotifyNewIntervalCallback, ¶ms); } @@ -2309,9 +2309,14 @@ nsSMILTimedElement::NotifyNewIntervalCallback(TimeValueSpecPtrKey* aKey, NotifyTimeDependentsParams* params = static_cast(aData); NS_ABORT_IF_FALSE(params, "null data ptr while enumerating hashtable"); - NS_ABORT_IF_FALSE(params->mCurrentInterval, "null current-interval ptr"); + nsSMILInterval* interval = params->mTimedElement->mCurrentInterval; + // It's possible that in notifying one new time dependent of a new interval + // that a chain reaction is triggered which results in the original interval + // disappearing. If that's the case we can skip sending further notifications. + if (!interval) + return PL_DHASH_STOP; nsSMILTimeValueSpec* spec = aKey->GetKey(); - spec->HandleNewInterval(*params->mCurrentInterval, params->mTimeContainer); + spec->HandleNewInterval(*interval, params->mTimeContainer); return PL_DHASH_NEXT; } diff --git a/content/smil/nsSMILTimedElement.h b/content/smil/nsSMILTimedElement.h index 1d796cc86051..59e4d041594f 100644 --- a/content/smil/nsSMILTimedElement.h +++ b/content/smil/nsSMILTimedElement.h @@ -366,7 +366,7 @@ protected: }; struct NotifyTimeDependentsParams { - nsSMILInterval* mCurrentInterval; + nsSMILTimedElement* mTimedElement; nsSMILTimeContainer* mTimeContainer; }; From 973f6647173aecd6a0092d00b7bee89dc70044a5 Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Tue, 31 Jan 2012 11:37:21 +0900 Subject: [PATCH 15/20] Bug 692145 - Wallpaper text offset out-of-bounds crash. r=masayuki --- content/events/src/nsContentEventHandler.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/content/events/src/nsContentEventHandler.cpp b/content/events/src/nsContentEventHandler.cpp index 6bd8a21d77ff..15972fd5f276 100644 --- a/content/events/src/nsContentEventHandler.cpp +++ b/content/events/src/nsContentEventHandler.cpp @@ -214,12 +214,11 @@ static PRUint32 CountNewlinesIn(nsIContent* aContent, PRUint32 aMaxOffset) const nsTextFragment* text = aContent->GetText(); if (!text) return 0; - if (aMaxOffset == PR_UINT32_MAX) { - // search the entire string - aMaxOffset = text->GetLength(); - } + NS_ASSERTION(aMaxOffset == PR_UINT32_MAX || aMaxOffset <= text->GetLength(), + "text offset is out-of-bounds"); + const PRUint32 length = NS_MIN(aMaxOffset, text->GetLength()); PRUint32 newlines = 0; - for (PRUint32 i = 0; i < aMaxOffset; ++i) { + for (PRUint32 i = 0; i < length; ++i) { if (text->CharAt(i) == '\n') { ++newlines; } From ae186d280d8dd1659500a631b6bbdf89f88ebf81 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 31 Jan 2012 11:37:26 +0900 Subject: [PATCH 16/20] Bug 692145 Separate CountNewlinesIn() to CountNewlinesInXPLength() and CountNewlinesInNativeLength() r=ehsan --- content/events/src/nsContentEventHandler.cpp | 33 +++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/content/events/src/nsContentEventHandler.cpp b/content/events/src/nsContentEventHandler.cpp index 15972fd5f276..eeb98ff91d53 100644 --- a/content/events/src/nsContentEventHandler.cpp +++ b/content/events/src/nsContentEventHandler.cpp @@ -207,16 +207,17 @@ static void AppendSubString(nsAString& aString, nsIContent* aContent, } #if defined(XP_WIN) -static PRUint32 CountNewlinesIn(nsIContent* aContent, PRUint32 aMaxOffset) +static PRUint32 CountNewlinesInXPLength(nsIContent* aContent, + PRUint32 aXPLength) { NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT), "aContent is not a text node!"); const nsTextFragment* text = aContent->GetText(); if (!text) return 0; - NS_ASSERTION(aMaxOffset == PR_UINT32_MAX || aMaxOffset <= text->GetLength(), + NS_ASSERTION(aXPLength == PR_UINT32_MAX || aXPLength <= text->GetLength(), "text offset is out-of-bounds"); - const PRUint32 length = NS_MIN(aMaxOffset, text->GetLength()); + const PRUint32 length = NS_MIN(aXPLength, text->GetLength()); PRUint32 newlines = 0; for (PRUint32 i = 0; i < length; ++i) { if (text->CharAt(i) == '\n') { @@ -225,6 +226,28 @@ static PRUint32 CountNewlinesIn(nsIContent* aContent, PRUint32 aMaxOffset) } return newlines; } + +static PRUint32 CountNewlinesInNativeLength(nsIContent* aContent, + PRUint32 aNativeLength) +{ + NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT), + "aContent is not a text node!"); + const nsTextFragment* text = aContent->GetText(); + if (!text) { + return 0; + } + const PRUint32 xpLength = text->GetLength(); + PRUint32 newlines = 0; + for (PRUint32 i = 0, nativeOffset = 0; + i < xpLength && nativeOffset < aNativeLength; + ++i, ++nativeOffset) { + if (text->CharAt(i) == '\n') { + ++newlines; + ++nativeOffset; + } + } + return newlines; +} #endif static PRUint32 GetNativeTextLength(nsIContent* aContent, PRUint32 aMaxLength = PR_UINT32_MAX) @@ -239,7 +262,7 @@ static PRUint32 GetNativeTextLength(nsIContent* aContent, PRUint32 aMaxLength = // On Windows, the length of a native newline ("\r\n") is twice the length of // the XP newline ("\n"), so XP length is equal to the length of the native // offset plus the number of newlines encountered in the string. - CountNewlinesIn(aContent, aMaxLength); + CountNewlinesInXPLength(aContent, aMaxLength); #else // On other platforms, the native and XP newlines are the same. 0; @@ -271,7 +294,7 @@ static PRUint32 ConvertToXPOffset(nsIContent* aContent, PRUint32 aNativeOffset) // On Windows, the length of a native newline ("\r\n") is twice the length of // the XP newline ("\n"), so XP offset is equal to the length of the native // offset minus the number of newlines encountered in the string. - return aNativeOffset - CountNewlinesIn(aContent, aNativeOffset); + return aNativeOffset - CountNewlinesInNativeLength(aContent, aNativeOffset); #else // On other platforms, the native and XP newlines are the same. return aNativeOffset; From b17cef0f8547d45fbefb026196508cfbd388407b Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Tue, 31 Jan 2012 01:46:54 -0500 Subject: [PATCH 17/20] Bug 720482. Make CG snapshots work more like the other backends. r=joe,mattwoodrow,bas This has big improvement in the performance of GetImageData() because we make the snapshot lazily instead of using the vm_copy() method of CGBitmapContextCreateImage() --- gfx/2d/DrawTargetCG.cpp | 161 ++++++++++++++++++++++++------------- gfx/2d/DrawTargetCG.h | 4 +- gfx/2d/SourceSurfaceCG.cpp | 85 +++++++++++++++++++- gfx/2d/SourceSurfaceCG.h | 33 ++++++++ gfx/2d/Types.h | 1 + 5 files changed, 226 insertions(+), 58 deletions(-) diff --git a/gfx/2d/DrawTargetCG.cpp b/gfx/2d/DrawTargetCG.cpp index d5b51fe50793..47fb6c25a0d2 100644 --- a/gfx/2d/DrawTargetCG.cpp +++ b/gfx/2d/DrawTargetCG.cpp @@ -108,12 +108,14 @@ CGBlendMode ToBlendMode(CompositionOp op) -DrawTargetCG::DrawTargetCG() +DrawTargetCG::DrawTargetCG() : mSnapshot(NULL) { } DrawTargetCG::~DrawTargetCG() { + MarkChanged(); + // We need to conditionally release these because Init can fail without initializing these. if (mColorSpace) CGColorSpaceRelease(mColorSpace); @@ -125,8 +127,11 @@ DrawTargetCG::~DrawTargetCG() TemporaryRef DrawTargetCG::Snapshot() { - RefPtr newSurf = new SourceSurfaceCG(CGBitmapContextCreateImage(mCg)); - return newSurf; + if (!mSnapshot) { + mSnapshot = new SourceSurfaceCGBitmapContext(this); + } + + return mSnapshot; } TemporaryRef @@ -157,6 +162,18 @@ DrawTargetCG::CreateSourceSurfaceFromData(unsigned char *aData, return newSurf; } +static CGImageRef +GetImageFromSourceSurface(SourceSurface *aSurface) +{ + if (aSurface->GetType() == SURFACE_COREGRAPHICS_IMAGE) + return static_cast(aSurface)->GetImage(); + else if (aSurface->GetType() == SURFACE_COREGRAPHICS_CGCONTEXT) + return static_cast(aSurface)->GetImage(); + else if (aSurface->GetType() == SURFACE_DATA) + return static_cast(aSurface)->GetImage(); + assert(0); +} + TemporaryRef DrawTargetCG::OptimizeSourceSurface(SourceSurface *aSurface) const { @@ -181,6 +198,7 @@ class UnboundnessFixer //XXX: The size here is in default user space units, of the layer relative to the graphics context. // is the clip bounds still correct if, for example, we have a scale applied to the context? mLayer = CGLayerCreateWithContext(baseCg, mClipBounds.size, NULL); + //XXX: if the size is 0x0 we get a NULL CGContext back from GetContext mCg = CGLayerGetContext(mLayer); // CGContext's default to have the origin at the bottom left // so flip it to the top left and adjust for the origin @@ -213,43 +231,43 @@ DrawTargetCG::DrawSurface(SourceSurface *aSurface, const DrawSurfaceOptions &aSurfOptions, const DrawOptions &aDrawOptions) { + MarkChanged(); + CGImageRef image; CGImageRef subimage = NULL; - if (aSurface->GetType() == SURFACE_COREGRAPHICS_IMAGE) { - CGContextSaveGState(mCg); + CGContextSaveGState(mCg); - CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp)); - UnboundnessFixer fixer; - CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp); - CGContextSetAlpha(cg, aDrawOptions.mAlpha); + CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp)); + UnboundnessFixer fixer; + CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp); + CGContextSetAlpha(cg, aDrawOptions.mAlpha); - CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform)); - image = static_cast(aSurface)->GetImage(); - /* we have two options here: - * - create a subimage -- this is slower - * - fancy things with clip and different dest rects */ - { - subimage = CGImageCreateWithImageInRect(image, RectToCGRect(aSource)); - image = subimage; - } - - CGContextScaleCTM(cg, 1, -1); - - CGRect flippedRect = CGRectMake(aDest.x, -(aDest.y + aDest.height), - aDest.width, aDest.height); - - //XXX: we should implement this for patterns too - if (aSurfOptions.mFilter == FILTER_POINT) - CGContextSetInterpolationQuality(cg, kCGInterpolationNone); - - CGContextDrawImage(cg, flippedRect, image); - - fixer.Fix(mCg); - - CGContextRestoreGState(mCg); - - CGImageRelease(subimage); + CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform)); + image = GetImageFromSourceSurface(aSurface); + /* we have two options here: + * - create a subimage -- this is slower + * - fancy things with clip and different dest rects */ + { + subimage = CGImageCreateWithImageInRect(image, RectToCGRect(aSource)); + image = subimage; } + + CGContextScaleCTM(cg, 1, -1); + + CGRect flippedRect = CGRectMake(aDest.x, -(aDest.y + aDest.height), + aDest.width, aDest.height); + + //XXX: we should implement this for patterns too + if (aSurfOptions.mFilter == FILTER_POINT) + CGContextSetInterpolationQuality(cg, kCGInterpolationNone); + + CGContextDrawImage(cg, flippedRect, image); + + fixer.Fix(mCg); + + CGContextRestoreGState(mCg); + + CGImageRelease(subimage); } static CGColorRef ColorToCGColor(CGColorSpaceRef aColorSpace, const Color& aColor) @@ -373,7 +391,7 @@ CreateCGPattern(const Pattern &aPattern, CGAffineTransform aUserSpace) { const SurfacePattern& pat = static_cast(aPattern); // XXX: is .get correct here? - CGImageRef image = static_cast(pat.mSurface.get())->GetImage(); + CGImageRef image = GetImageFromSourceSurface(pat.mSurface.get()); CGFloat xStep, yStep; switch (pat.mExtendMode) { case EXTEND_CLAMP: @@ -465,6 +483,8 @@ DrawTargetCG::FillRect(const Rect &aRect, const Pattern &aPattern, const DrawOptions &aDrawOptions) { + MarkChanged(); + CGContextSaveGState(mCg); UnboundnessFixer fixer; @@ -489,6 +509,8 @@ DrawTargetCG::FillRect(const Rect &aRect, void DrawTargetCG::StrokeLine(const Point &p1, const Point &p2, const Pattern &aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions &aDrawOptions) { + MarkChanged(); + CGContextSaveGState(mCg); UnboundnessFixer fixer; @@ -524,6 +546,8 @@ DrawTargetCG::StrokeRect(const Rect &aRect, const StrokeOptions &aStrokeOptions, const DrawOptions &aDrawOptions) { + MarkChanged(); + CGContextSaveGState(mCg); UnboundnessFixer fixer; @@ -571,6 +595,8 @@ DrawTargetCG::StrokeRect(const Rect &aRect, void DrawTargetCG::ClearRect(const Rect &aRect) { + MarkChanged(); + CGContextSaveGState(mCg); CGContextConcatCTM(mCg, GfxMatrixToCGAffineTransform(mTransform)); @@ -582,6 +608,8 @@ DrawTargetCG::ClearRect(const Rect &aRect) void DrawTargetCG::Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions &aDrawOptions) { + MarkChanged(); + CGContextSaveGState(mCg); UnboundnessFixer fixer; @@ -622,6 +650,8 @@ DrawTargetCG::Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOpt void DrawTargetCG::Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aDrawOptions) { + MarkChanged(); + assert(aPath->GetBackendType() == BACKEND_COREGRAPHICS); CGContextSaveGState(mCg); @@ -660,6 +690,8 @@ DrawTargetCG::Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions void DrawTargetCG::FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pattern &aPattern, const DrawOptions &aDrawOptions) { + MarkChanged(); + assert(aBuffer.mNumGlyphs); CGContextSaveGState(mCg); @@ -723,10 +755,12 @@ DrawTargetCG::CopySurface(SourceSurface *aSurface, const IntRect& aSourceRect, const IntPoint &aDestination) { + MarkChanged(); + CGImageRef image; CGImageRef subimage = NULL; if (aSurface->GetType() == SURFACE_COREGRAPHICS_IMAGE) { - image = static_cast(aSurface)->GetImage(); + image = GetImageFromSourceSurface(aSurface); /* we have two options here: * - create a subimage -- this is slower * - fancy things with clip and different dest rects */ @@ -758,33 +792,33 @@ DrawTargetCG::CopySurface(SourceSurface *aSurface, void DrawTargetCG::DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest, const Color &aColor, const Point &aOffset, Float aSigma, CompositionOp aOperator) { + MarkChanged(); + CGImageRef image; CGImageRef subimage = NULL; - if (aSurface->GetType() == SURFACE_COREGRAPHICS_IMAGE) { - image = static_cast(aSurface)->GetImage(); + image = GetImageFromSourceSurface(aSurface); - IntSize size = aSurface->GetSize(); - CGContextSaveGState(mCg); - //XXX do we need to do the fixup here? - CGContextSetBlendMode(mCg, ToBlendMode(aOperator)); + IntSize size = aSurface->GetSize(); + CGContextSaveGState(mCg); + //XXX do we need to do the fixup here? + CGContextSetBlendMode(mCg, ToBlendMode(aOperator)); - CGContextScaleCTM(mCg, 1, -1); + CGContextScaleCTM(mCg, 1, -1); - CGRect flippedRect = CGRectMake(aDest.x, -(aDest.y + size.height), - size.width, size.height); + CGRect flippedRect = CGRectMake(aDest.x, -(aDest.y + size.height), + size.width, size.height); - CGColorRef color = ColorToCGColor(mColorSpace, aColor); - CGSize offset = {aOffset.x, -aOffset.y}; - // CoreGraphics needs twice sigma as it's amount of blur - CGContextSetShadowWithColor(mCg, offset, 2*aSigma, color); - CGColorRelease(color); + CGColorRef color = ColorToCGColor(mColorSpace, aColor); + CGSize offset = {aOffset.x, -aOffset.y}; + // CoreGraphics needs twice sigma as it's amount of blur + CGContextSetShadowWithColor(mCg, offset, 2*aSigma, color); + CGColorRelease(color); - CGContextDrawImage(mCg, flippedRect, image); + CGContextDrawImage(mCg, flippedRect, image); - CGContextRestoreGState(mCg); + CGContextRestoreGState(mCg); - CGImageRelease(subimage); - } + CGImageRelease(subimage); } bool @@ -795,6 +829,7 @@ DrawTargetCG::Init(CGContextRef cgContext, const IntSize &aSize) if (aSize.width == 0 || aSize.height == 0) { mColorSpace = NULL; mCg = NULL; + mData = NULL; return false; } @@ -829,6 +864,7 @@ DrawTargetCG::Init(const IntSize &aSize, SurfaceFormat &) if (aSize.width == 0 || aSize.height == 0) { mColorSpace = NULL; mCg = NULL; + mData = NULL; return false; } @@ -894,6 +930,7 @@ DrawTargetCG::Mask(const Pattern &aSource, const Pattern &aMask, const DrawOptions &aDrawOptions) { + MarkChanged(); CGContextSaveGState(mCg); @@ -910,7 +947,7 @@ DrawTargetCG::Mask(const Pattern &aSource, //FillRect(rect, aSource, drawOptions); } else if (aMask.GetType() == PATTERN_SURFACE) { const SurfacePattern& pat = static_cast(aMask); - CGImageRef mask = static_cast(pat.mSurface.get())->GetImage(); + CGImageRef mask = GetImageFromSourceSurface(pat.mSurface.get()); Rect rect(0,0, CGImageGetWidth(mask), CGImageGetHeight(mask)); // XXX: probably we need to do some flipping of the image or something CGContextClipToMask(mCg, RectToCGRect(rect), mask); @@ -974,6 +1011,18 @@ DrawTargetCG::PopClip() CGContextRestoreGState(mCg); } +void +DrawTargetCG::MarkChanged() +{ + if (mSnapshot) { + if (mSnapshot->refCount() > 1) { + // We only need to worry about snapshots that someone else knows about + mSnapshot->DrawTargetWillChange(); + } + mSnapshot = NULL; + } +} + } diff --git a/gfx/2d/DrawTargetCG.h b/gfx/2d/DrawTargetCG.h index 046bb4b43c4a..f7d85b62303c 100644 --- a/gfx/2d/DrawTargetCG.h +++ b/gfx/2d/DrawTargetCG.h @@ -40,6 +40,7 @@ #include "2D.h" #include "Rect.h" #include "PathCG.h" +#include "SourceSurfaceCG.h" namespace mozilla { namespace gfx { @@ -174,7 +175,7 @@ public: return mCg; } private: - bool InitCGRenderTarget(); + void MarkChanged(); IntSize mSize; CGColorSpaceRef mColorSpace; @@ -184,6 +185,7 @@ private: SurfaceFormat mFormat; + RefPtr mSnapshot; }; } diff --git a/gfx/2d/SourceSurfaceCG.cpp b/gfx/2d/SourceSurfaceCG.cpp index 2264d997e792..f070d1f51275 100644 --- a/gfx/2d/SourceSurfaceCG.cpp +++ b/gfx/2d/SourceSurfaceCG.cpp @@ -36,6 +36,7 @@ * ***** END LICENSE BLOCK ***** */ #include "SourceSurfaceCG.h" +#include "DrawTargetCG.h" namespace mozilla { namespace gfx { @@ -309,11 +310,93 @@ DataSourceSurfaceCG::GetData() //CFDataRelease(data); // unfortunately the the method above only works for read-only access and // we need read-write for DataSourceSurfaces - return (unsigned char*)mData; } +SourceSurfaceCGBitmapContext::SourceSurfaceCGBitmapContext(DrawTargetCG *aDrawTarget) +{ + mDrawTarget = aDrawTarget; + mCg = (CGContextRef)aDrawTarget->GetNativeSurface(NATIVE_SURFACE_CGCONTEXT); + CGContextRetain(mCg); + mSize.width = CGBitmapContextGetWidth(mCg); + mSize.height = CGBitmapContextGetHeight(mCg); + mStride = CGBitmapContextGetBytesPerRow(mCg); + mData = CGBitmapContextGetData(mCg); + + mImage = NULL; +} + +void SourceSurfaceCGBitmapContext::EnsureImage() const +{ + if (!mImage) { + if (mCg) { + mImage = CGBitmapContextCreateImage(mCg); + } else { + //XXX: we should avoid creating this colorspace everytime + CGColorSpaceRef colorSpace = NULL; + CGBitmapInfo bitinfo = 0; + CGDataProviderRef dataProvider = NULL; + int bitsPerComponent = 8; + int bitsPerPixel = 32; + + colorSpace = CGColorSpaceCreateDeviceRGB(); + bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; + + dataProvider = CGDataProviderCreateWithData (mData, + mData, + mSize.height * mStride, + releaseCallback); + + mImage = CGImageCreate (mSize.width, mSize.height, + bitsPerComponent, + bitsPerPixel, + mStride, + colorSpace, + bitinfo, + dataProvider, + NULL, + true, + kCGRenderingIntentDefault); + + CGDataProviderRelease(dataProvider); + CGColorSpaceRelease (colorSpace); + } + } +} + +IntSize +SourceSurfaceCGBitmapContext::GetSize() const +{ + return mSize; +} + +void +SourceSurfaceCGBitmapContext::DrawTargetWillChange() +{ + if (mDrawTarget) { + size_t stride = CGBitmapContextGetBytesPerRow(mCg); + size_t height = CGBitmapContextGetHeight(mCg); + //XXX: infalliable malloc? + mData = malloc(stride * height); + memcpy(mData, CGBitmapContextGetData(mCg), stride*height); + CGContextRelease(mCg); + mCg = NULL; + mDrawTarget = NULL; + } +} + +SourceSurfaceCGBitmapContext::~SourceSurfaceCGBitmapContext() +{ + if (!mImage && !mCg) { + // neither mImage or mCg owns the data + free(mData); + } + if (mCg) + CGContextRelease(mCg); + if (mImage) + CGImageRelease(mImage); +} } } diff --git a/gfx/2d/SourceSurfaceCG.h b/gfx/2d/SourceSurfaceCG.h index 1aa1b6ae45cc..cbf0a98dae73 100644 --- a/gfx/2d/SourceSurfaceCG.h +++ b/gfx/2d/SourceSurfaceCG.h @@ -44,6 +44,8 @@ namespace mozilla { namespace gfx { +class DrawTargetCG; + class SourceSurfaceCG : public SourceSurface { public: @@ -105,5 +107,36 @@ private: * for now we just store it in mFormat */ }; +class SourceSurfaceCGBitmapContext : public DataSourceSurface +{ +public: + SourceSurfaceCGBitmapContext(DrawTargetCG *); + ~SourceSurfaceCGBitmapContext(); + + virtual SurfaceType GetType() const { return SURFACE_COREGRAPHICS_CGCONTEXT; } + virtual IntSize GetSize() const; + virtual SurfaceFormat GetFormat() const { return FORMAT_B8G8R8A8; } + + CGImageRef GetImage() { EnsureImage(); return mImage; } + + virtual unsigned char *GetData() { return static_cast(mData); } + + virtual int32_t Stride() { return mStride; } + +private: + //XXX: do the other backends friend their DrawTarget? + friend class DrawTargetCG; + void DrawTargetWillChange(); + void EnsureImage() const; + + DrawTargetCG *mDrawTarget; + CGContextRef mCg; + mutable CGImageRef mImage; + void *mData; + int32_t mStride; + IntSize mSize; +}; + + } } diff --git a/gfx/2d/Types.h b/gfx/2d/Types.h index d390de9f9e98..bc10885bb22b 100644 --- a/gfx/2d/Types.h +++ b/gfx/2d/Types.h @@ -55,6 +55,7 @@ enum SurfaceType SURFACE_CAIRO, /* Surface wrapping a cairo surface */ SURFACE_CAIRO_IMAGE, /* Data surface wrapping a cairo image surface */ SURFACE_COREGRAPHICS_IMAGE, /* Surface wrapping a CoreGraphics Image */ + SURFACE_COREGRAPHICS_CGCONTEXT, /* Surface wrapping a CG context */ SURFACE_SKIA /* Surface wrapping a Skia bitmap */ }; From 272182617fcf1a7f1d92ba7e0b249221be4f6ce3 Mon Sep 17 00:00:00 2001 From: Tim Taubert Date: Tue, 31 Jan 2012 10:08:34 +0100 Subject: [PATCH 18/20] Bug 721087 - [New Tab Page] Cells should have an outline to indicate their positions; ui-r=limi r=dao --- browser/themes/gnomestripe/newtab/newTab.css | 6 ++++++ browser/themes/pinstripe/newtab/newTab.css | 6 ++++++ browser/themes/winstripe/newtab/newTab.css | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/browser/themes/gnomestripe/newtab/newTab.css b/browser/themes/gnomestripe/newtab/newTab.css index 0fe9fdc64353..3d02fe2a4dca 100644 --- a/browser/themes/gnomestripe/newtab/newTab.css +++ b/browser/themes/gnomestripe/newtab/newTab.css @@ -78,6 +78,12 @@ margin: 0 auto; } +/* CELLS */ +.cell { + outline: 1px dashed #ccc; + outline-offset: -1px; +} + /* SITES */ .site { background-color: #ececec; diff --git a/browser/themes/pinstripe/newtab/newTab.css b/browser/themes/pinstripe/newtab/newTab.css index 889f136bac85..a4c911e8ec26 100644 --- a/browser/themes/pinstripe/newtab/newTab.css +++ b/browser/themes/pinstripe/newtab/newTab.css @@ -78,6 +78,12 @@ margin: 0 auto; } +/* CELLS */ +.cell { + outline: 1px dashed #ccc; + outline-offset: -1px; +} + /* SITES */ .site { background-color: #ececec; diff --git a/browser/themes/winstripe/newtab/newTab.css b/browser/themes/winstripe/newtab/newTab.css index 0fe9fdc64353..3d02fe2a4dca 100644 --- a/browser/themes/winstripe/newtab/newTab.css +++ b/browser/themes/winstripe/newtab/newTab.css @@ -78,6 +78,12 @@ margin: 0 auto; } +/* CELLS */ +.cell { + outline: 1px dashed #ccc; + outline-offset: -1px; +} + /* SITES */ .site { background-color: #ececec; From 7857cfe044b591a1ade398e79521f0f1e4adc8be Mon Sep 17 00:00:00 2001 From: Marco Bonardo Date: Tue, 31 Jan 2012 11:29:20 +0100 Subject: [PATCH 19/20] Bug 720792 (part 1) - Backout autocomplete delay changes, for compatibility problems. r=gavin --- browser/app/profile/firefox.js | 2 +- .../autocomplete/nsAutoCompleteController.cpp | 6 - .../components/places/nsPlacesAutoComplete.js | 135 +++++++----------- 3 files changed, 55 insertions(+), 88 deletions(-) diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 4eab638b37ae..69a7213d2fc1 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -293,7 +293,7 @@ pref("browser.urlbar.maxRichResults", 12); // The amount of time (ms) to wait after the user has stopped typing // before starting to perform autocomplete. 50 is the default set in // autocomplete.xml. -pref("browser.urlbar.delay", 0); +pref("browser.urlbar.delay", 50); // The special characters below can be typed into the urlbar to either restrict // the search to visited history, bookmarked, tagged pages; or force a match on diff --git a/toolkit/components/autocomplete/nsAutoCompleteController.cpp b/toolkit/components/autocomplete/nsAutoCompleteController.cpp index 99e47b1935fc..95aec25dd240 100644 --- a/toolkit/components/autocomplete/nsAutoCompleteController.cpp +++ b/toolkit/components/autocomplete/nsAutoCompleteController.cpp @@ -1098,12 +1098,6 @@ nsAutoCompleteController::StartSearchTimer() PRUint32 timeout; mInput->GetTimeout(&timeout); - if (timeout == 0) { - // The consumer wants to execute the search synchronously - StartSearch(); - return NS_OK; - } - nsresult rv; mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); if (NS_FAILED(rv)) diff --git a/toolkit/components/places/nsPlacesAutoComplete.js b/toolkit/components/places/nsPlacesAutoComplete.js index bb88306c2d1a..82826c72dce4 100644 --- a/toolkit/components/places/nsPlacesAutoComplete.js +++ b/toolkit/components/places/nsPlacesAutoComplete.js @@ -96,9 +96,6 @@ const kQueryIndexOpenPageCount = 10; const kQueryTypeKeyword = 0; const kQueryTypeFiltered = 1; -// Autocomplete minimum time before query is executed -const kAsyncQueriesWaitTime = 50; - // This separator is used as an RTL-friendly way to split the title and tags. // It can also be used by an nsIAutoCompleteResult consumer to re-split the // "comment" back into the title and the tag. @@ -513,17 +510,60 @@ nsPlacesAutoComplete.prototype = { // Stop the search in case the controller has not taken care of it. this.stopSearch(); - // Unlike urlInlineComplete, we don't want this search to start - // synchronously. Wait kAsyncQueriesWaitTime before launching the query. - this._startTimer = Cc["@mozilla.org/timer;1"] - .createInstance(Ci.nsITimer); - let timerCallback = (function() { - this._doStartSearch(aSearchString, aSearchParam, - aPreviousResult, aListener); - }).bind(this); - this._startTimer.initWithCallback(timerCallback, - kAsyncQueriesWaitTime, - Ci.nsITimer.TYPE_ONE_SHOT); + // Note: We don't use aPreviousResult to make sure ordering of results are + // consistent. See bug 412730 for more details. + + // We want to store the original string with no leading or trailing + // whitespace for case sensitive searches. + this._originalSearchString = aSearchString.trim(); + + this._currentSearchString = + fixupSearchText(this._originalSearchString.toLowerCase()); + + let searchParamParts = aSearchParam.split(" "); + this._enableActions = searchParamParts.indexOf("enable-actions") != -1; + + this._listener = aListener; + let result = Cc["@mozilla.org/autocomplete/simple-result;1"]. + createInstance(Ci.nsIAutoCompleteSimpleResult); + result.setSearchString(aSearchString); + result.setListener(this); + this._result = result; + + // If we are not enabled, we need to return now. + if (!this._enabled) { + this._finishSearch(true); + return; + } + + // Reset our search behavior to the default. + if (this._currentSearchString) { + this._behavior = this._defaultBehavior; + } + else { + this._behavior = this._emptySearchDefaultBehavior; + } + // For any given search, we run up to four queries: + // 1) keywords (this._keywordQuery) + // 2) adaptive learning (this._adaptiveQuery) + // 3) open pages not supported by history (this._openPagesQuery) + // 4) query from this._getSearch + // (1) only gets ran if we get any filtered tokens from this._getSearch, + // since if there are no tokens, there is nothing to match, so there is no + // reason to run the query). + let {query, tokens} = + this._getSearch(this._getUnfilteredSearchTokens(this._currentSearchString)); + let queries = tokens.length ? + [this._getBoundKeywordQuery(tokens), this._getBoundAdaptiveQuery(), this._getBoundOpenPagesQuery(tokens), query] : + [this._getBoundAdaptiveQuery(), this._getBoundOpenPagesQuery(tokens), query]; + + // Start executing our queries. + this._telemetryStartTime = Date.now(); + this._executeQueries(queries); + + // Set up our persistent state for the duration of the search. + this._searchTokens = tokens; + this._usedPlaces = {}; }, stopSearch: function PAC_stopSearch() @@ -536,11 +576,6 @@ nsPlacesAutoComplete.prototype = { } this._finishSearch(false); - - if (this._startTimer) { - this._startTimer.cancel(); - delete this._startTimer; - } }, ////////////////////////////////////////////////////////////////////////////// @@ -691,68 +726,6 @@ nsPlacesAutoComplete.prototype = { get _databaseInitialized() Object.getOwnPropertyDescriptor(this, "_db").value !== undefined, - _doStartSearch: function PAC_doStartSearch(aSearchString, aSearchParam, - aPreviousResult, aListener) - { - this._startTimer.cancel(); - delete this._startTimer; - - // Note: We don't use aPreviousResult to make sure ordering of results are - // consistent. See bug 412730 for more details. - - // We want to store the original string with no leading or trailing - // whitespace for case sensitive searches. - this._originalSearchString = aSearchString.trim(); - - this._currentSearchString = - fixupSearchText(this._originalSearchString.toLowerCase()); - - let searchParamParts = aSearchParam.split(" "); - this._enableActions = searchParamParts.indexOf("enable-actions") != -1; - - this._listener = aListener; - let result = Cc["@mozilla.org/autocomplete/simple-result;1"]. - createInstance(Ci.nsIAutoCompleteSimpleResult); - result.setSearchString(aSearchString); - result.setListener(this); - this._result = result; - - // If we are not enabled, we need to return now. - if (!this._enabled) { - this._finishSearch(true); - return; - } - - // Reset our search behavior to the default. - if (this._currentSearchString) { - this._behavior = this._defaultBehavior; - } - else { - this._behavior = this._emptySearchDefaultBehavior; - } - // For any given search, we run up to four queries: - // 1) keywords (this._keywordQuery) - // 2) adaptive learning (this._adaptiveQuery) - // 3) open pages not supported by history (this._openPagesQuery) - // 4) query from this._getSearch - // (1) only gets ran if we get any filtered tokens from this._getSearch, - // since if there are no tokens, there is nothing to match, so there is no - // reason to run the query). - let {query, tokens} = - this._getSearch(this._getUnfilteredSearchTokens(this._currentSearchString)); - let queries = tokens.length ? - [this._getBoundKeywordQuery(tokens), this._getBoundAdaptiveQuery(), this._getBoundOpenPagesQuery(tokens), query] : - [this._getBoundAdaptiveQuery(), this._getBoundOpenPagesQuery(tokens), query]; - - // Start executing our queries. - this._telemetryStartTime = Date.now(); - this._executeQueries(queries); - - // Set up our persistent state for the duration of the search. - this._searchTokens = tokens; - this._usedPlaces = {}; - }, - /** * Generates the tokens used in searching from a given string. * From b366cdcdcd4bee080da235426ebb4888d3afae8b Mon Sep 17 00:00:00 2001 From: Marco Bonardo Date: Tue, 31 Jan 2012 11:29:25 +0100 Subject: [PATCH 20/20] Bug 720792 (part 1.5) - Disable inline autocomplete till part 2 lands. --- browser/app/profile/firefox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 69a7213d2fc1..7f10318c0991 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -280,7 +280,7 @@ pref("browser.urlbar.doubleClickSelectsAll", true); #else pref("browser.urlbar.doubleClickSelectsAll", false); #endif -pref("browser.urlbar.autoFill", true); +pref("browser.urlbar.autoFill", false); // 0: Match anywhere (e.g., middle of words) // 1: Match on word boundaries and then try matching anywhere // 2: Match only on word boundaries (e.g., after / or .)