Merge mozilla-central to mozilla-inbound

This commit is contained in:
Ed Morley 2012-01-31 10:59:27 +00:00
commit 1470e78771
40 changed files with 1152 additions and 352 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}
};

View File

@ -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];
},
/**

View File

@ -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.
*/

View File

@ -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();

View File

@ -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;

View File

@ -78,6 +78,12 @@
margin: 0 auto;
}
/* CELLS */
.cell {
outline: 1px dashed #ccc;
outline-offset: -1px;
}
/* SITES */
.site {
background-color: #ececec;

View File

@ -78,6 +78,12 @@
margin: 0 auto;
}
/* CELLS */
.cell {
outline: 1px dashed #ccc;
outline-offset: -1px;
}
/* SITES */
.site {
background-color: #ececec;

View File

@ -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;

View File

@ -78,6 +78,12 @@
margin: 0 auto;
}
/* CELLS */
.cell {
outline: 1px dashed #ccc;
outline-offset: -1px;
}
/* SITES */
.site {
background-color: #ececec;

View File

@ -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

View File

@ -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); }

View File

@ -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) {

View File

@ -434,6 +434,8 @@ nsContentUtils::Init()
"dom.event.handling-user-input-time-limit",
1000);
nsGenericElement::InitCCCallbacks();
sInitialized = true;
return NS_OK;

View File

@ -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)

View File

@ -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();

View File

@ -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.

View File

@ -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();
};

View File

@ -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)",

View File

@ -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

View File

@ -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;

View File

@ -2199,7 +2199,7 @@ nsSMILTimedElement::NotifyNewInterval()
container->SyncPauseTime();
}
NotifyTimeDependentsParams params = { mCurrentInterval, container };
NotifyTimeDependentsParams params = { this, container };
mTimeDependents.EnumerateEntries(NotifyNewIntervalCallback, &params);
}
@ -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;
}

View File

@ -366,7 +366,7 @@ protected:
};
struct NotifyTimeDependentsParams {
nsSMILInterval* mCurrentInterval;
nsSMILTimedElement* mTimedElement;
nsSMILTimeContainer* mTimeContainer;
};

View File

@ -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;
}

View File

@ -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();
}
}

View File

@ -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

View File

@ -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,

View File

@ -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;
}
}
}

View File

@ -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;
};
}

View File

@ -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);
}
}
}

View File

@ -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;
};
}
}

View File

@ -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 */
};

View File

@ -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))

View File

@ -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.
*

View File

@ -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;

View File

@ -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 \

View File

@ -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>

View File

@ -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");
}

View File

@ -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");
}