mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Merge mozilla-central to mozilla-inbound
This commit is contained in:
commit
1470e78771
@ -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 .)
|
||||
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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];
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -78,6 +78,12 @@
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* CELLS */
|
||||
.cell {
|
||||
outline: 1px dashed #ccc;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
/* SITES */
|
||||
.site {
|
||||
background-color: #ececec;
|
||||
|
@ -78,6 +78,12 @@
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* CELLS */
|
||||
.cell {
|
||||
outline: 1px dashed #ccc;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
/* SITES */
|
||||
.site {
|
||||
background-color: #ececec;
|
||||
|
@ -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;
|
||||
|
@ -78,6 +78,12 @@
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* CELLS */
|
||||
.cell {
|
||||
outline: 1px dashed #ccc;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
/* SITES */
|
||||
.site {
|
||||
background-color: #ececec;
|
||||
|
@ -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
|
||||
|
@ -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); }
|
||||
|
@ -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<nsINode*>(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<nsINode*>(aNode)->GetCurrentDoc();
|
||||
if (d && nsCCUncollectableMarker::InGeneration(d->GetMarkedCCGeneration())) {
|
||||
nsGenericElement::MarkUserDataHandler(aNode, aKey, aValue, aData);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
MarkMessageManagers()
|
||||
{
|
||||
nsCOMPtr<nsIChromeFrameMessageManager> 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<nsITreeItemFrameMessageManager> 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<nsITreeItemFrameMessageManager> 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<nsFrameMessageManager*>(tabMM.get())->
|
||||
GetCallbackData();
|
||||
nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
|
||||
if (fl) {
|
||||
nsIDOMEventTarget* et = fl->GetTabChildGlobalAsEventTarget();
|
||||
if (!et) {
|
||||
continue;
|
||||
}
|
||||
static_cast<nsInProcessTabChildGlobal*>(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<nsIDOMEventTarget> win = do_QueryInterface(doc->GetInnerWindow());
|
||||
if (win) {
|
||||
elm = win->GetListenerManager(false);
|
||||
if (elm) {
|
||||
elm->UnmarkGrayJSListeners();
|
||||
}
|
||||
static_cast<nsGlobalWindow*>(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<nsIContentViewer> cview;
|
||||
aSHEntry->GetContentViewer(getter_AddRefs(cview));
|
||||
MarkContentViewer(cview);
|
||||
MarkContentViewer(cview, aCleanupJS, aPrepareForCC);
|
||||
|
||||
nsCOMPtr<nsIDocShellTreeItem> child;
|
||||
PRInt32 i = 0;
|
||||
while (NS_SUCCEEDED(aSHEntry->ChildShellAt(i++, getter_AddRefs(child))) &&
|
||||
child) {
|
||||
MarkDocShell(child);
|
||||
MarkDocShell(child, aCleanupJS, aPrepareForCC);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISHContainer> shCont = do_QueryInterface(aSHEntry);
|
||||
@ -132,13 +232,13 @@ MarkSHEntry(nsISHEntry* aSHEntry)
|
||||
for (i = 0; i < count; ++i) {
|
||||
nsCOMPtr<nsISHEntry> 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<nsIDocShell> shell = do_QueryInterface(aNode);
|
||||
if (!shell) {
|
||||
@ -147,7 +247,7 @@ MarkDocShell(nsIDocShellTreeNode* aNode)
|
||||
|
||||
nsCOMPtr<nsIContentViewer> cview;
|
||||
shell->GetContentViewer(getter_AddRefs(cview));
|
||||
MarkContentViewer(cview);
|
||||
MarkContentViewer(cview, aCleanupJS, aPrepareForCC);
|
||||
|
||||
nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(shell);
|
||||
nsCOMPtr<nsISHistory> history;
|
||||
@ -160,7 +260,7 @@ MarkDocShell(nsIDocShellTreeNode* aNode)
|
||||
history->GetEntryAtIndex(i, false, getter_AddRefs(historyEntry));
|
||||
nsCOMPtr<nsISHEntry> shEntry = do_QueryInterface(historyEntry);
|
||||
|
||||
MarkSHEntry(shEntry);
|
||||
MarkSHEntry(shEntry, aCleanupJS, aPrepareForCC);
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,12 +269,13 @@ MarkDocShell(nsIDocShellTreeNode* aNode)
|
||||
for (i = 0; i < childCount; ++i) {
|
||||
nsCOMPtr<nsIDocShellTreeItem> 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<nsISupports> iter;
|
||||
while (NS_SUCCEEDED(aWindowList->GetNext(getter_AddRefs(iter))) &&
|
||||
@ -184,7 +285,7 @@ MarkWindowList(nsISimpleEnumerator* aWindowList)
|
||||
nsCOMPtr<nsIDocShellTreeNode> 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<nsIWindowWatcher> 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<nsIAppShellService> appShell =
|
||||
@ -246,10 +357,15 @@ nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
nsCOMPtr<nsIDocShell> shell;
|
||||
hw->GetDocShell(getter_AddRefs(shell));
|
||||
nsCOMPtr<nsIDocShellTreeNode> 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) {
|
||||
|
@ -434,6 +434,8 @@ nsContentUtils::Init()
|
||||
"dom.event.handling-user-input-time-limit",
|
||||
1000);
|
||||
|
||||
nsGenericElement::InitCCCallbacks();
|
||||
|
||||
sInitialized = true;
|
||||
|
||||
return NS_OK;
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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<nsIAtom> GetCurrentValueAtom();
|
||||
};
|
||||
|
@ -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<PRUint32*>(aData);
|
||||
xpc_MarkInCCGeneration(static_cast<nsISupports*>(aChild), *gen);
|
||||
}
|
||||
|
||||
void
|
||||
nsGenericElement::MarkUserDataHandler(void* aObject, nsIAtom* aKey,
|
||||
void* aChild, void* aData)
|
||||
{
|
||||
nsCOMPtr<nsIXPConnectWrappedJS> wjs =
|
||||
do_QueryInterface(static_cast<nsISupports*>(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<nsINode*, 1020>* 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<nsINode*>(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<nsINode*, 1020>;
|
||||
}
|
||||
|
||||
// nodesToUnpurple contains nodes which will be removed
|
||||
// from the purple buffer if the DOM tree is black.
|
||||
nsAutoTArray<nsIContent*, 1020> 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<nsINode*, 1020> grayNodes;
|
||||
|
||||
bool foundBlack = root->IsBlack();
|
||||
if (root != currentDoc) {
|
||||
currentDoc = nsnull;
|
||||
if (NeedsScriptTraverse(root)) {
|
||||
grayNodes.AppendElement(root);
|
||||
} else if (static_cast<nsIContent*>(root)->IsPurple()) {
|
||||
nodesToUnpurple.AppendElement(static_cast<nsIContent*>(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<nsINode*, 1020>* 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<nsINode*>(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<nsIContent*, 1020> nodesToClear;
|
||||
|
||||
bool foundBlack = root->IsBlack();
|
||||
if (root != currentDoc) {
|
||||
currentDoc = nsnull;
|
||||
if (ShouldClearPurple(static_cast<nsIContent*>(root))) {
|
||||
nodesToClear.AppendElement(static_cast<nsIContent*>(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<nsINode*, 1020>();
|
||||
}
|
||||
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)",
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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<NotifyTimeDependentsParams*>(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;
|
||||
}
|
||||
|
@ -366,7 +366,7 @@ protected:
|
||||
};
|
||||
|
||||
struct NotifyTimeDependentsParams {
|
||||
nsSMILInterval* mCurrentInterval;
|
||||
nsSMILTimedElement* mTimedElement;
|
||||
nsSMILTimeContainer* mTimeContainer;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<nsIConsoleService> 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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<SourceSurface>
|
||||
DrawTargetCG::Snapshot()
|
||||
{
|
||||
RefPtr<SourceSurfaceCG> newSurf = new SourceSurfaceCG(CGBitmapContextCreateImage(mCg));
|
||||
return newSurf;
|
||||
if (!mSnapshot) {
|
||||
mSnapshot = new SourceSurfaceCGBitmapContext(this);
|
||||
}
|
||||
|
||||
return mSnapshot;
|
||||
}
|
||||
|
||||
TemporaryRef<DrawTarget>
|
||||
@ -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<SourceSurfaceCG*>(aSurface)->GetImage();
|
||||
else if (aSurface->GetType() == SURFACE_COREGRAPHICS_CGCONTEXT)
|
||||
return static_cast<SourceSurfaceCGBitmapContext*>(aSurface)->GetImage();
|
||||
else if (aSurface->GetType() == SURFACE_DATA)
|
||||
return static_cast<DataSourceSurfaceCG*>(aSurface)->GetImage();
|
||||
assert(0);
|
||||
}
|
||||
|
||||
TemporaryRef<SourceSurface>
|
||||
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<SourceSurfaceCG*>(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<const SurfacePattern&>(aPattern);
|
||||
// XXX: is .get correct here?
|
||||
CGImageRef image = static_cast<SourceSurfaceCG*>(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<SourceSurfaceCG*>(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<SourceSurfaceCG*>(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
|
||||
@ -904,6 +938,7 @@ DrawTargetCG::Mask(const Pattern &aSource,
|
||||
const Pattern &aMask,
|
||||
const DrawOptions &aDrawOptions)
|
||||
{
|
||||
MarkChanged();
|
||||
|
||||
CGContextSaveGState(mCg);
|
||||
|
||||
@ -920,7 +955,7 @@ DrawTargetCG::Mask(const Pattern &aSource,
|
||||
//FillRect(rect, aSource, drawOptions);
|
||||
} else if (aMask.GetType() == PATTERN_SURFACE) {
|
||||
const SurfacePattern& pat = static_cast<const SurfacePattern&>(aMask);
|
||||
CGImageRef mask = static_cast<SourceSurfaceCG*>(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);
|
||||
@ -984,6 +1019,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -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<SourceSurfaceCGBitmapContext> mSnapshot;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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<unsigned char*>(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;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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 */
|
||||
};
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -12,7 +12,7 @@ Form History test: form field autocomplete
|
||||
<p id="display"></p>
|
||||
|
||||
<!-- we presumably can't hide the content for this test. -->
|
||||
<div id="content">
|
||||
<div id="content" style="direction: rtl;">
|
||||
<!-- unused -->
|
||||
<form id="unused" onsubmit="return false;">
|
||||
<input type="text" name="field1" value="unused">
|
||||
@ -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;
|
||||
|
@ -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 \
|
||||
|
@ -1,66 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=649840
|
||||
-->
|
||||
<window title="Mozilla Bug 649840"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
|
||||
<textbox id="textLTR" type="autocomplete" autocompletesearch="simple"/>
|
||||
<textbox id="textRTL" type="autocomplete" autocompletesearch="simple"/>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=649840"
|
||||
target="_blank">Mozilla Bug 649840</a>
|
||||
</body>
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
/** Test for Bug 649840 **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(runTest);
|
||||
|
||||
function runTest()
|
||||
{
|
||||
var textLTR = $("textLTR");
|
||||
var textRTL = $("textRTL");
|
||||
|
||||
textLTR.style.direction = "ltr";
|
||||
textRTL.style.direction = "rtl";
|
||||
|
||||
textLTR.value="abcd";
|
||||
textRTL.value="ابجد";
|
||||
|
||||
// open and close the popups to update the popupdir attribute value
|
||||
textLTR.openPopup();
|
||||
textLTR.closePopup();
|
||||
textRTL.openPopup();
|
||||
textRTL.closePopup();
|
||||
|
||||
is(textLTR.popup.style.direction, textLTR.style.direction, "LTR textbox test fails");
|
||||
is(textRTL.popup.style.direction, textRTL.style.direction, "RTL textbox test fails");
|
||||
|
||||
// switch directions of the two textboxes
|
||||
textLTR.style.direction = "rtl";
|
||||
textRTL.style.direction = "ltr";
|
||||
|
||||
// open and close the popups to update the popupdir attribute value
|
||||
textLTR.openPopup();
|
||||
textLTR.closePopup();
|
||||
textRTL.openPopup();
|
||||
textRTL.closePopup();
|
||||
|
||||
is(textLTR.popup.style.direction, textLTR.style.direction, "RTL-switched textbox test fails");
|
||||
is(textRTL.popup.style.direction, textRTL.style.direction, "LTR-switched textbox test fails");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
]]>
|
||||
</script>
|
||||
</window>
|
@ -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");
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user