зеркало из https://github.com/mozilla/pjs.git
Merge m-c to s-c.
This commit is contained in:
Коммит
6cb2c1daa8
|
@ -362,8 +362,8 @@ window[chromehidden~="toolbar"] toolbar:not(.toolbar-primary):not(.chromeclass-m
|
|||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#full-screen-warning-container[fade-warning-out] {
|
||||
|
|
|
@ -976,7 +976,7 @@
|
|||
</hbox>
|
||||
|
||||
<hbox id="full-screen-warning-container" hidden="true" fadeout="true">
|
||||
<hbox style="min-width: 100%;" pack="center"> <!-- Inner hbox needed due to bug 579776. -->
|
||||
<hbox style="width: 100%;" pack="center"> <!-- Inner hbox needed due to bug 579776. -->
|
||||
<hbox id="full-screen-warning-message">
|
||||
<description id="full-screen-warning-text" value="&domFullScreenWarning.label;"></description>
|
||||
</hbox>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
function test() {
|
||||
waitForExplicitFinish();
|
||||
ignoreAllUncaughtExceptions();
|
||||
|
||||
// test the main (normal) browser window
|
||||
testCustomize(window, testChromeless);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
function test() {
|
||||
waitForExplicitFinish();
|
||||
ignoreAllUncaughtExceptions();
|
||||
|
||||
testCustomize(window, finish);
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
ignoreAllUncaughtExceptions();
|
||||
|
||||
const BOOKMARKS_SIDEBAR_ID = "viewBookmarksSidebar";
|
||||
const BOOKMARKS_SIDEBAR_TREE_ID = "bookmarks-view";
|
||||
|
|
|
@ -38,16 +38,9 @@
|
|||
|
||||
#include "nsIShellService.idl"
|
||||
|
||||
[scriptable, uuid(16e7e8da-8bef-4f41-be5f-045b2e9918e1)]
|
||||
[scriptable, uuid(89b0a761-d9a0-4c39-ab83-d81566459a31)]
|
||||
interface nsIWindowsShellService : nsIShellService
|
||||
{
|
||||
/**
|
||||
* The number of unread mail messages for the current user.
|
||||
*
|
||||
* @return The number of unread (new) mail messages for the current user.
|
||||
*/
|
||||
readonly attribute unsigned long unreadMailCount;
|
||||
|
||||
/**
|
||||
* Provides the shell service an opportunity to do some Win7+ shortcut
|
||||
* maintenance needed on initial startup of the browser.
|
||||
|
|
|
@ -803,66 +803,6 @@ nsWindowsShellService::SetDesktopBackgroundColor(PRUint32 aColor)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowsShellService::GetUnreadMailCount(PRUint32* aCount)
|
||||
{
|
||||
*aCount = 0;
|
||||
|
||||
HKEY accountKey;
|
||||
if (GetMailAccountKey(&accountKey)) {
|
||||
DWORD type, unreadCount;
|
||||
DWORD len = sizeof unreadCount;
|
||||
DWORD res = ::RegQueryValueExW(accountKey, L"MessageCount", 0,
|
||||
&type, (LPBYTE)&unreadCount, &len);
|
||||
if (REG_SUCCEEDED(res))
|
||||
*aCount = unreadCount;
|
||||
|
||||
// Close the key we opened.
|
||||
::RegCloseKey(accountKey);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsWindowsShellService::GetMailAccountKey(HKEY* aResult)
|
||||
{
|
||||
NS_NAMED_LITERAL_STRING(unread,
|
||||
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\UnreadMail\\");
|
||||
|
||||
HKEY mailKey;
|
||||
DWORD res = ::RegOpenKeyExW(HKEY_CURRENT_USER, unread.get(), 0,
|
||||
KEY_ENUMERATE_SUB_KEYS, &mailKey);
|
||||
|
||||
PRInt32 i = 0;
|
||||
do {
|
||||
PRUnichar subkeyName[MAX_BUF];
|
||||
DWORD len = sizeof subkeyName;
|
||||
res = ::RegEnumKeyExW(mailKey, i++, subkeyName, &len, NULL, NULL,
|
||||
NULL, NULL);
|
||||
if (REG_SUCCEEDED(res)) {
|
||||
HKEY accountKey;
|
||||
res = ::RegOpenKeyExW(mailKey, PromiseFlatString(subkeyName).get(),
|
||||
0, KEY_READ, &accountKey);
|
||||
if (REG_SUCCEEDED(res)) {
|
||||
*aResult = accountKey;
|
||||
|
||||
// Close the key we opened.
|
||||
::RegCloseKey(mailKey);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
while (1);
|
||||
|
||||
// Close the key we opened.
|
||||
::RegCloseKey(mailKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowsShellService::OpenApplicationWithURI(nsILocalFile* aApplication,
|
||||
const nsACString& aURI)
|
||||
|
|
|
@ -59,8 +59,6 @@ public:
|
|||
protected:
|
||||
bool IsDefaultBrowserVista(bool* aIsDefaultBrowser);
|
||||
|
||||
bool GetMailAccountKey(HKEY* aResult);
|
||||
|
||||
private:
|
||||
bool mCheckedThisSession;
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
ignoreAllUncaughtExceptions();
|
||||
|
||||
let nodes = [
|
||||
{nodeId: "i1111", result: "i1 i11 i111 i1111"},
|
||||
|
|
|
@ -8136,8 +8136,6 @@ if test "$MOZ_TREE_CAIRO"; then
|
|||
SANITY_CHECKING_FEATURE="#undef CAIRO_DO_SANITY_CHECKING"
|
||||
fi
|
||||
|
||||
PNG_FUNCTIONS_FEATURE="#define CAIRO_HAS_PNG_FUNCTIONS 1"
|
||||
|
||||
AC_SUBST(PS_SURFACE_FEATURE)
|
||||
AC_SUBST(PDF_SURFACE_FEATURE)
|
||||
AC_SUBST(SVG_SURFACE_FEATURE)
|
||||
|
|
|
@ -110,17 +110,6 @@ public:
|
|||
return mState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request an update of the link state for this element. This will
|
||||
* make sure that if the element is a link at all then either
|
||||
* NS_EVENT_STATE_VISITED or NS_EVENT_STATE_UNVISITED is set in
|
||||
* mState, and a history lookup kicked off if needed to find out
|
||||
* whether the link is really visited. This method will NOT send any
|
||||
* state change notifications. If you want them to happen for this
|
||||
* call, you need to handle them yourself.
|
||||
*/
|
||||
virtual void RequestLinkStateUpdate();
|
||||
|
||||
/**
|
||||
* Ask this element to update its state. If aNotify is false, then
|
||||
* state change notifications will not be dispatched; in that
|
||||
|
@ -132,6 +121,11 @@ public:
|
|||
* removing it from the document).
|
||||
*/
|
||||
void UpdateState(bool aNotify);
|
||||
|
||||
/**
|
||||
* Method to update mState with link state information. This does not notify.
|
||||
*/
|
||||
void UpdateLinkState(nsEventStates aState);
|
||||
|
||||
protected:
|
||||
/**
|
||||
|
@ -142,11 +136,6 @@ protected:
|
|||
*/
|
||||
virtual nsEventStates IntrinsicState() const;
|
||||
|
||||
/**
|
||||
* Method to update mState with link state information. This does not notify.
|
||||
*/
|
||||
void UpdateLinkState(nsEventStates aState);
|
||||
|
||||
/**
|
||||
* Method to add state bits. This should be called from subclass
|
||||
* constructors to set up our event state correctly at construction
|
||||
|
|
|
@ -1735,6 +1735,12 @@ public:
|
|||
*/
|
||||
static bool HasPluginWithUncontrolledEventDispatch(nsIContent* aContent);
|
||||
|
||||
/**
|
||||
* Returns the root document in a document hierarchy. Normally this will
|
||||
* be the chrome document.
|
||||
*/
|
||||
static nsIDocument* GetRootDocument(nsIDocument* aDoc);
|
||||
|
||||
/**
|
||||
* Returns the time limit on handling user input before
|
||||
* nsEventStateManager::IsHandlingUserInput() stops returning true.
|
||||
|
|
|
@ -166,6 +166,7 @@ public:
|
|||
// &&-ed in, this is safe.
|
||||
mAllowDNSPrefetch(true),
|
||||
mIsBeingUsedAsImage(false),
|
||||
mHasLinksToUpdate(false),
|
||||
mPartID(0)
|
||||
{
|
||||
SetInDocument();
|
||||
|
@ -1489,7 +1490,7 @@ public:
|
|||
/**
|
||||
* This method is similar to GetElementById() from nsIDOMDocument but it
|
||||
* returns a mozilla::dom::Element instead of a nsIDOMElement.
|
||||
* It prevents converting nsIDOMElement to mozill:dom::Element which is
|
||||
* It prevents converting nsIDOMElement to mozilla::dom::Element which is
|
||||
* already converted from mozilla::dom::Element.
|
||||
*/
|
||||
virtual Element* GetElementById(const nsAString& aElementId) = 0;
|
||||
|
@ -1554,6 +1555,17 @@ public:
|
|||
virtual nsresult SetNavigationTiming(nsDOMNavigationTiming* aTiming) = 0;
|
||||
|
||||
virtual Element* FindImageMap(const nsAString& aNormalizedMapName) = 0;
|
||||
|
||||
// Add aLink to the set of links that need their status resolved.
|
||||
void RegisterPendingLinkUpdate(mozilla::dom::Link* aLink);
|
||||
|
||||
// Remove aLink from the set of links that need their status resolved.
|
||||
// This function must be called when links are removed from the document.
|
||||
void UnregisterPendingLinkUpdate(mozilla::dom::Link* aElement);
|
||||
|
||||
// Update state on links in mLinksToUpdate. This function must
|
||||
// be called prior to selector matching.
|
||||
void FlushPendingLinkUpdates();
|
||||
|
||||
#define DEPRECATED_OPERATION(_op) e##_op,
|
||||
enum DeprecatedOperations {
|
||||
|
@ -1644,6 +1656,11 @@ protected:
|
|||
// These are non-owning pointers, the elements are responsible for removing
|
||||
// themselves when they go away.
|
||||
nsAutoPtr<nsTHashtable<nsPtrHashKey<nsIContent> > > mFreezableElements;
|
||||
|
||||
// The set of all links that need their status resolved. Links must add themselves
|
||||
// to this set by calling RegisterPendingLinkUpdate when added to a document and must
|
||||
// remove themselves by calling UnregisterPendingLinkUpdate when removed from a document.
|
||||
nsTHashtable<nsPtrHashKey<mozilla::dom::Link> > mLinksToUpdate;
|
||||
|
||||
// SMIL Animation Controller, lazily-initialized in GetAnimationController
|
||||
nsRefPtr<nsSMILAnimationController> mAnimationController;
|
||||
|
@ -1724,6 +1741,9 @@ protected:
|
|||
// file, etc.
|
||||
bool mIsSyntheticDocument;
|
||||
|
||||
// True if this document has links whose state needs updating
|
||||
bool mHasLinksToUpdate;
|
||||
|
||||
// The document's script global object, the object from which the
|
||||
// document can get its script context and scope. This is the
|
||||
// *inner* window object.
|
||||
|
|
|
@ -109,6 +109,9 @@ public:
|
|||
* changes or false if it should not.
|
||||
*/
|
||||
void ResetLinkState(bool aNotify);
|
||||
|
||||
// This method nevers returns a null element.
|
||||
Element* GetElement() const { return mElement; }
|
||||
|
||||
protected:
|
||||
virtual ~Link();
|
||||
|
|
|
@ -5893,6 +5893,20 @@ nsContentUtils::HasPluginWithUncontrolledEventDispatch(nsIContent* aContent)
|
|||
return result;
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsIDocument*
|
||||
nsContentUtils::GetRootDocument(nsIDocument* aDoc)
|
||||
{
|
||||
if (!aDoc) {
|
||||
return nsnull;
|
||||
}
|
||||
nsIDocument* doc = aDoc;
|
||||
while (doc->GetParentDocument()) {
|
||||
doc = doc->GetParentDocument();
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
nsContentUtils::ReleaseWrapper(nsISupports* aScriptObjectHolder,
|
||||
|
|
|
@ -51,6 +51,17 @@
|
|||
|
||||
NS_IMPL_ISUPPORTS1(nsDataDocumentContentPolicy, nsIContentPolicy)
|
||||
|
||||
// Helper method for ShouldLoad()
|
||||
// Checks a URI for the given flags. Returns true if the URI has the flags,
|
||||
// and false if not (or if we weren't able to tell).
|
||||
static bool
|
||||
HasFlags(nsIURI* aURI, PRUint32 aURIFlags)
|
||||
{
|
||||
bool hasFlags;
|
||||
nsresult rv = NS_URIChainHasFlags(aURI, aURIFlags, &hasFlags);
|
||||
return NS_SUCCEEDED(rv) && hasFlags;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDataDocumentContentPolicy::ShouldLoad(PRUint32 aContentType,
|
||||
nsIURI *aContentLocation,
|
||||
|
@ -87,21 +98,26 @@ nsDataDocumentContentPolicy::ShouldLoad(PRUint32 aContentType,
|
|||
}
|
||||
|
||||
if (doc->IsBeingUsedAsImage()) {
|
||||
// Allow local resources for SVG-as-an-image documents, but disallow
|
||||
// everything else, to prevent data leakage
|
||||
bool hasFlags;
|
||||
nsresult rv = NS_URIChainHasFlags(aContentLocation,
|
||||
nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
|
||||
&hasFlags);
|
||||
if (NS_FAILED(rv) || !hasFlags) {
|
||||
// resource is not local (or we couldn't tell) - reject!
|
||||
// We only allow SVG images to load content from URIs that are local and
|
||||
// also satisfy one of the following conditions:
|
||||
// - URI inherits security context, e.g. data URIs
|
||||
// OR
|
||||
// - URI loadable by subsumers, e.g. moz-filedata URIs
|
||||
// Any URI that doesn't meet these requirements will be rejected below.
|
||||
if (!HasFlags(aContentLocation,
|
||||
nsIProtocolHandler::URI_IS_LOCAL_RESOURCE) ||
|
||||
(!HasFlags(aContentLocation,
|
||||
nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT) &&
|
||||
!HasFlags(aContentLocation,
|
||||
nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS))) {
|
||||
*aDecision = nsIContentPolicy::REJECT_TYPE;
|
||||
|
||||
// report error, if we can.
|
||||
// Report error, if we can.
|
||||
if (node) {
|
||||
nsIPrincipal* requestingPrincipal = node->NodePrincipal();
|
||||
nsRefPtr<nsIURI> principalURI;
|
||||
rv = requestingPrincipal->GetURI(getter_AddRefs(principalURI));
|
||||
nsresult rv =
|
||||
requestingPrincipal->GetURI(getter_AddRefs(principalURI));
|
||||
if (NS_SUCCEEDED(rv) && principalURI) {
|
||||
nsScriptSecurityManager::ReportError(
|
||||
nsnull, NS_LITERAL_STRING("CheckSameOriginError"), principalURI,
|
||||
|
@ -112,8 +128,8 @@ nsDataDocumentContentPolicy::ShouldLoad(PRUint32 aContentType,
|
|||
doc->GetDocumentURI()) {
|
||||
// Check for (& disallow) recursive image-loads
|
||||
bool isRecursiveLoad;
|
||||
rv = aContentLocation->EqualsExceptRef(doc->GetDocumentURI(),
|
||||
&isRecursiveLoad);
|
||||
nsresult rv = aContentLocation->EqualsExceptRef(doc->GetDocumentURI(),
|
||||
&isRecursiveLoad);
|
||||
if (NS_FAILED(rv) || isRecursiveLoad) {
|
||||
NS_WARNING("Refusing to recursively load image");
|
||||
*aDecision = nsIContentPolicy::REJECT_TYPE;
|
||||
|
@ -122,12 +138,12 @@ nsDataDocumentContentPolicy::ShouldLoad(PRUint32 aContentType,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// Allow all loads for non-external-resource documents
|
||||
if (!doc->GetDisplayDocument()) {
|
||||
// Allow all loads for non-resource documents
|
||||
if (!doc->IsResourceDoc()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// For external resources, blacklist some load types
|
||||
// For resource documents, blacklist some load types
|
||||
if (aContentType == nsIContentPolicy::TYPE_OBJECT ||
|
||||
aContentType == nsIContentPolicy::TYPE_DOCUMENT ||
|
||||
aContentType == nsIContentPolicy::TYPE_SUBDOCUMENT ||
|
||||
|
|
|
@ -1554,6 +1554,8 @@ nsDocument::nsDocument(const char* aContentType)
|
|||
|
||||
// Start out mLastStyleSheetSet as null, per spec
|
||||
SetDOMStringToNull(mLastStyleSheetSet);
|
||||
|
||||
mLinksToUpdate.Init();
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
|
@ -7971,6 +7973,41 @@ nsIDocument::EnumerateFreezableElements(FreezableElementEnumerator aEnumerator,
|
|||
mFreezableElements->EnumerateEntries(EnumerateFreezables, &data);
|
||||
}
|
||||
|
||||
void
|
||||
nsIDocument::RegisterPendingLinkUpdate(Link* aLink)
|
||||
{
|
||||
mLinksToUpdate.PutEntry(aLink);
|
||||
mHasLinksToUpdate = true;
|
||||
}
|
||||
|
||||
void
|
||||
nsIDocument::UnregisterPendingLinkUpdate(Link* aLink)
|
||||
{
|
||||
if (!mHasLinksToUpdate)
|
||||
return;
|
||||
|
||||
mLinksToUpdate.RemoveEntry(aLink);
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
EnumeratePendingLinkUpdates(nsPtrHashKey<Link>* aEntry, void* aData)
|
||||
{
|
||||
aEntry->GetKey()->GetElement()->UpdateLinkState(aEntry->GetKey()->LinkState());
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
nsIDocument::FlushPendingLinkUpdates()
|
||||
{
|
||||
if (!mHasLinksToUpdate)
|
||||
return;
|
||||
|
||||
nsAutoScriptBlocker scriptBlocker;
|
||||
mLinksToUpdate.EnumerateEntries(EnumeratePendingLinkUpdates, nsnull);
|
||||
mLinksToUpdate.Clear();
|
||||
mHasLinksToUpdate = false;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIDocument>
|
||||
nsIDocument::CreateStaticClone(nsISupports* aCloneContainer)
|
||||
{
|
||||
|
@ -8535,19 +8572,6 @@ GetCommonAncestor(nsIDocument* aDoc1, nsIDocument* aDoc2)
|
|||
return parent;
|
||||
}
|
||||
|
||||
// Returns the root document in a document hierarchy.
|
||||
static nsIDocument*
|
||||
GetRootDocument(nsIDocument* aDoc)
|
||||
{
|
||||
if (!aDoc)
|
||||
return nsnull;
|
||||
nsIDocument* doc = aDoc;
|
||||
while (doc->GetParentDocument()) {
|
||||
doc = doc->GetParentDocument();
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
class nsCallRequestFullScreen : public nsRunnable
|
||||
{
|
||||
public:
|
||||
|
@ -8622,8 +8646,8 @@ nsDocument::RequestFullScreen(Element* aElement, bool aWasCallerChrome)
|
|||
}
|
||||
|
||||
// Remember the root document, so that if a full-screen document is hidden
|
||||
// we can reset full-screen state the remaining visible full-screen documents.
|
||||
sFullScreenRootDoc = do_GetWeakReference(GetRootDocument(this));
|
||||
// we can reset full-screen state in the remaining visible full-screen documents.
|
||||
sFullScreenRootDoc = do_GetWeakReference(nsContentUtils::GetRootDocument(this));
|
||||
|
||||
// Set the full-screen element. This sets the full-screen style on the
|
||||
// element, and the full-screen-ancestor styles on ancestors of the element
|
||||
|
|
|
@ -1248,11 +1248,6 @@ Element::NotifyStateChange(nsEventStates aStates)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Element::RequestLinkStateUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Element::UpdateLinkState(nsEventStates aState)
|
||||
{
|
||||
|
@ -5385,6 +5380,7 @@ inline static nsresult FindMatchingElements(nsINode* aRoot,
|
|||
nsIDocument* doc = aRoot->OwnerDoc();
|
||||
TreeMatchContext matchingContext(false, nsRuleWalker::eRelevantLinkUnvisited,
|
||||
doc);
|
||||
doc->FlushPendingLinkUpdates();
|
||||
|
||||
// Fast-path selectors involving IDs. We can only do this if aRoot
|
||||
// is in the document and the document is not in quirks mode, since
|
||||
|
@ -5493,6 +5489,7 @@ nsGenericElement::MozMatchesSelector(const nsAString& aSelector, nsresult* aResu
|
|||
*aResult = ParseSelectorList(this, aSelector, getter_Transfers(selectorList));
|
||||
|
||||
if (NS_SUCCEEDED(*aResult)) {
|
||||
OwnerDoc()->FlushPendingLinkUpdates();
|
||||
TreeMatchContext matchingContext(false,
|
||||
nsRuleWalker::eRelevantLinkUnvisited,
|
||||
OwnerDoc());
|
||||
|
|
|
@ -81,6 +81,7 @@ _TEST_FILES_0 = \
|
|||
test_2d.composite.uncovered.image.source-in.html \
|
||||
test_2d.composite.uncovered.image.source-out.html \
|
||||
test_2d.drawImage.zerocanvas.html \
|
||||
test_toDataURL_alpha.html \
|
||||
test_toDataURL_lowercase_ascii.html \
|
||||
test_toDataURL_parameters.html \
|
||||
test_mozGetAsFile.html \
|
||||
|
|
|
@ -0,0 +1,206 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Canvas test: toDataURL parameters (Bug 564388)</title>
|
||||
<script src="/MochiKit/MochiKit.js"></script>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
For image types that do not support an alpha channel, the image must be
|
||||
composited onto a solid black background using the source-over operator,
|
||||
and the resulting image must be the one used to create the data: URL.
|
||||
</p>
|
||||
<p> See:
|
||||
<a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-canvas-todataurl">
|
||||
http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-canvas-todataurl
|
||||
</a>
|
||||
</p>
|
||||
<p>Mozilla
|
||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=650720">Bug 650720</a>
|
||||
</p>
|
||||
<p class="output">Output:</p>
|
||||
<!--
|
||||
Author's note:
|
||||
To add more cases to this test:
|
||||
- To add a row (another color value)
|
||||
* Add a row to the table below, using the canvas id format
|
||||
(c<row>-<col>)
|
||||
* Update runTests to include the new row in the loop
|
||||
- To add a column (another image format)
|
||||
* Add a column to the table below, using the canvas id format above
|
||||
* Update runTests to call do_canvas, passing your column number,
|
||||
the image format, and any options to pass to the toDataUrl function
|
||||
|
||||
Vaguely derived from Philip Taylor's toDataURL.jpeg.alpha test:
|
||||
http://philip.html5.org/tests/canvas/suite/tests/toDataURL.jpeg.alpha.html
|
||||
-->
|
||||
<table>
|
||||
<tr>
|
||||
<th>Type:</th>
|
||||
<th>image/png</th>
|
||||
<th>image/jpeg</th>
|
||||
<th>image/bmp<br />(24 bpp)</th>
|
||||
<th>image/bmp<br />(32 bpp)</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="c1">rgba(128, 255, 128, 0.5)</td>
|
||||
<td><canvas id="c1-1" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
<td><canvas id="c1-2" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
<td><canvas id="c1-3" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
<td><canvas id="c1-4" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="c2">rgba(255, 128, 128, 0.75)</td>
|
||||
<td><canvas id="c2-1" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
<td><canvas id="c2-2" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
<td><canvas id="c2-3" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
<td><canvas id="c2-4" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="c3">rgba(128, 128, 255, 0.25)</td>
|
||||
<td><canvas id="c3-1" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
<td><canvas id="c3-2" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
<td><canvas id="c3-3" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
<td><canvas id="c3-4" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="c4">rgba(255, 255, 255, 1.0)</td>
|
||||
<td><canvas id="c4-1" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
<td><canvas id="c4-2" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
<td><canvas id="c4-3" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
<td><canvas id="c4-4" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="c5">rgba(255, 255, 255, 0)</td>
|
||||
<td><canvas id="c5-1" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
<td><canvas id="c5-2" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
<td><canvas id="c5-3" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
<td><canvas id="c5-4" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="c6">rgba(0, 0, 0, 1.0)</td>
|
||||
<td><canvas id="c6-1" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
<td><canvas id="c6-2" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
<td><canvas id="c6-3" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
<td><canvas id="c6-4" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="c7">rgba(0, 0, 0, 0)</td>
|
||||
<td><canvas id="c7-1" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
<td><canvas id="c7-2" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
<td><canvas id="c7-3" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
<td><canvas id="c7-4" class="output" width="100" height="50">
|
||||
<p class="fallback">FAIL (fallback content)</p></canvas></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
var finishedTests = [];
|
||||
|
||||
function isPixel(ctx, x,y, r,g,b,a, d)
|
||||
{
|
||||
var pos = x + "," + y;
|
||||
var colour = r + "," + g + "," + b + "," + a;
|
||||
var pixel = ctx.getImageData(x, y, 1, 1);
|
||||
var pr = pixel.data[0],
|
||||
pg = pixel.data[1],
|
||||
pb = pixel.data[2],
|
||||
pa = pixel.data[3];
|
||||
ok(r-d <= pr && pr <= r+d &&
|
||||
g-d <= pg && pg <= g+d &&
|
||||
b-d <= pb && pb <= b+d &&
|
||||
a-d <= pa && pa <= a+d,
|
||||
"pixel "+pos+" of "+ctx.canvas.id+" is "+pr+","+pg+","+pb+","+pa+
|
||||
"; expected "+colour+" +/- "+d);
|
||||
}
|
||||
|
||||
|
||||
function do_canvas(row, col, type, options)
|
||||
{
|
||||
finishedTests[row + '_' + col] = false;
|
||||
var canvas = document.getElementById('c' + row + '-' + col);
|
||||
var ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.fillStyle = document.getElementById('c' + row).textContent;
|
||||
ctx.fillRect(0, 0, 100, 50);
|
||||
var data = canvas.toDataURL(type, options);
|
||||
|
||||
ctx.fillStyle = '#000';
|
||||
ctx.fillRect(0, 0, 100, 50);
|
||||
var img = new Image();
|
||||
|
||||
var color = document.getElementById('c' + row).textContent;
|
||||
color = color.substr(5, color.length - 6); // strip off the 'argb()'
|
||||
var colors = color.replace(' ', '', 'g').split(',');
|
||||
var r = colors[0]*colors[3],
|
||||
g = colors[1]*colors[3],
|
||||
b = colors[2]*colors[3];
|
||||
|
||||
img.onload = function ()
|
||||
{
|
||||
ctx.drawImage(img, 0, 0);
|
||||
isPixel(ctx, 50,25, r,g,b,255, 8);
|
||||
finishedTests[row + '_' + col] = true;
|
||||
};
|
||||
img.src = data;
|
||||
}
|
||||
|
||||
function checkFinished()
|
||||
{
|
||||
for (var t in finishedTests) {
|
||||
if (!finishedTests[t]) {
|
||||
setTimeout(checkFinished, 500);
|
||||
return;
|
||||
}
|
||||
}
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function runTests()
|
||||
{
|
||||
for (var row = 1; row <= 7; row++) {
|
||||
do_canvas(row, 1, 'image/png');
|
||||
do_canvas(row, 2, 'image/jpeg');
|
||||
do_canvas(row, 3, 'image/bmp');
|
||||
do_canvas(row, 4, 'image/bmp', '-moz-parse-options:bpp=32');
|
||||
}
|
||||
|
||||
setTimeout(checkFinished, 500);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
addLoadEvent(runTests);
|
||||
|
||||
</script>
|
||||
|
||||
</html>
|
|
@ -118,7 +118,6 @@ public:
|
|||
virtual bool IsLink(nsIURI** aURI) const;
|
||||
virtual void GetLinkTarget(nsAString& aTarget);
|
||||
virtual nsLinkState GetLinkState() const;
|
||||
virtual void RequestLinkStateUpdate();
|
||||
virtual already_AddRefed<nsIURI> GetHrefURI() const;
|
||||
|
||||
nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
|
||||
|
@ -216,9 +215,13 @@ nsHTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Prefetch links
|
||||
if (aDocument && nsHTMLDNSPrefetch::IsAllowed(OwnerDoc())) {
|
||||
nsHTMLDNSPrefetch::PrefetchLow(this);
|
||||
if (aDocument) {
|
||||
aDocument->RegisterPendingLinkUpdate(this);
|
||||
if (nsHTMLDNSPrefetch::IsAllowed(OwnerDoc())) {
|
||||
nsHTMLDNSPrefetch::PrefetchLow(this);
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -228,6 +231,11 @@ nsHTMLAnchorElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
|||
// If this link is ever reinserted into a document, it might
|
||||
// be under a different xml:base, so forget the cached state now.
|
||||
Link::ResetLinkState(false);
|
||||
|
||||
nsIDocument* doc = GetCurrentDoc();
|
||||
if (doc) {
|
||||
doc->UnregisterPendingLinkUpdate(this);
|
||||
}
|
||||
|
||||
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
|
||||
}
|
||||
|
@ -389,12 +397,6 @@ nsHTMLAnchorElement::GetLinkState() const
|
|||
return Link::GetLinkState();
|
||||
}
|
||||
|
||||
void
|
||||
nsHTMLAnchorElement::RequestLinkStateUpdate()
|
||||
{
|
||||
UpdateLinkState(Link::LinkState());
|
||||
}
|
||||
|
||||
already_AddRefed<nsIURI>
|
||||
nsHTMLAnchorElement::GetHrefURI() const
|
||||
{
|
||||
|
|
|
@ -99,7 +99,6 @@ public:
|
|||
virtual bool IsLink(nsIURI** aURI) const;
|
||||
virtual void GetLinkTarget(nsAString& aTarget);
|
||||
virtual nsLinkState GetLinkState() const;
|
||||
virtual void RequestLinkStateUpdate();
|
||||
virtual already_AddRefed<nsIURI> GetHrefURI() const;
|
||||
|
||||
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
|
@ -213,7 +212,10 @@ nsHTMLAreaElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
|||
bool aCompileEventHandlers)
|
||||
{
|
||||
Link::ResetLinkState(false);
|
||||
|
||||
if (aDocument) {
|
||||
aDocument->RegisterPendingLinkUpdate(this);
|
||||
}
|
||||
|
||||
return nsGenericHTMLElement::BindToTree(aDocument, aParent,
|
||||
aBindingParent,
|
||||
aCompileEventHandlers);
|
||||
|
@ -225,6 +227,11 @@ nsHTMLAreaElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
|||
// If this link is ever reinserted into a document, it might
|
||||
// be under a different xml:base, so forget the cached state now.
|
||||
Link::ResetLinkState(false);
|
||||
|
||||
nsIDocument* doc = GetCurrentDoc();
|
||||
if (doc) {
|
||||
doc->UnregisterPendingLinkUpdate(this);
|
||||
}
|
||||
|
||||
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
|
||||
}
|
||||
|
@ -314,12 +321,6 @@ nsHTMLAreaElement::GetLinkState() const
|
|||
return Link::GetLinkState();
|
||||
}
|
||||
|
||||
void
|
||||
nsHTMLAreaElement::RequestLinkStateUpdate()
|
||||
{
|
||||
UpdateLinkState(Link::LinkState());
|
||||
}
|
||||
|
||||
already_AddRefed<nsIURI>
|
||||
nsHTMLAreaElement::GetHrefURI() const
|
||||
{
|
||||
|
|
|
@ -111,7 +111,6 @@ public:
|
|||
virtual bool IsLink(nsIURI** aURI) const;
|
||||
virtual void GetLinkTarget(nsAString& aTarget);
|
||||
virtual nsLinkState GetLinkState() const;
|
||||
virtual void RequestLinkStateUpdate();
|
||||
virtual already_AddRefed<nsIURI> GetHrefURI() const;
|
||||
|
||||
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
|
||||
|
@ -213,6 +212,10 @@ nsHTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
|||
aBindingParent,
|
||||
aCompileEventHandlers);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (aDocument) {
|
||||
aDocument->RegisterPendingLinkUpdate(this);
|
||||
}
|
||||
|
||||
void (nsHTMLLinkElement::*update)() = &nsHTMLLinkElement::UpdateStyleSheetInternal;
|
||||
nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, update));
|
||||
|
@ -246,6 +249,9 @@ nsHTMLLinkElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
|||
// Once we have XPCOMGC we shouldn't need to call UnbindFromTree during Unlink
|
||||
// and so this messy event dispatch can go away.
|
||||
nsCOMPtr<nsIDocument> oldDoc = GetCurrentDoc();
|
||||
if (oldDoc) {
|
||||
oldDoc->UnregisterPendingLinkUpdate(this);
|
||||
}
|
||||
CreateAndDispatchEvent(oldDoc, NS_LITERAL_STRING("DOMLinkRemoved"));
|
||||
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
|
||||
UpdateStyleSheetInternal(oldDoc);
|
||||
|
@ -379,12 +385,6 @@ nsHTMLLinkElement::GetLinkState() const
|
|||
return Link::GetLinkState();
|
||||
}
|
||||
|
||||
void
|
||||
nsHTMLLinkElement::RequestLinkStateUpdate()
|
||||
{
|
||||
UpdateLinkState(Link::LinkState());
|
||||
}
|
||||
|
||||
already_AddRefed<nsIURI>
|
||||
nsHTMLLinkElement::GetHrefURI() const
|
||||
{
|
||||
|
|
|
@ -274,6 +274,9 @@ _TEST_FILES = \
|
|||
test_bug583533.html \
|
||||
test_restore_from_parser_fragment.html \
|
||||
test_bug617528.html \
|
||||
test_bug660959-1.html \
|
||||
test_bug660959-2.html \
|
||||
test_bug660959-3.html \
|
||||
test_checked.html \
|
||||
test_bug677658.html \
|
||||
test_bug677463.html \
|
||||
|
@ -286,6 +289,8 @@ _TEST_FILES = \
|
|||
file_fullscreen-denied-inner.html \
|
||||
file_fullscreen-hidden.html \
|
||||
file_fullscreen-navigation.html \
|
||||
file_fullscreen-esc-exit.html \
|
||||
file_fullscreen-esc-exit-inner.html \
|
||||
test_li_attributes_reflection.html \
|
||||
test_ol_attributes_reflection.html \
|
||||
test_bug651956.html \
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=700764
|
||||
|
||||
Verify that an ESC key press in a subdoc of a full-screen doc causes us to
|
||||
exit DOM full-screen mode.
|
||||
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 700764</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<style>
|
||||
body:not(:-moz-full-screen) {
|
||||
background-color: blue;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 700764 **/
|
||||
|
||||
function ok(condition, msg) {
|
||||
parent.ok(condition, msg);
|
||||
}
|
||||
|
||||
function is(a, b, msg) {
|
||||
parent.is(a, b, msg);
|
||||
}
|
||||
|
||||
var escKeyReceived = false;
|
||||
var escKeySent = false;
|
||||
|
||||
function keyHandler(event) {
|
||||
if (escKeyReceived == Components.interfaces.nsIDOMKeyEvent.DOM_VK_ESC) {
|
||||
escKeyReceived = true;
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("keydown", keyHandler, true);
|
||||
window.addEventListener("keyup", keyHandler, true);
|
||||
window.addEventListener("keypress", keyHandler, true);
|
||||
|
||||
function startTest() {
|
||||
ok(!document.mozFullScreen, "Subdoc should not be in full-screen mode");
|
||||
ok(parent.document.mozFullScreen, "Parent should be in full-screen mode");
|
||||
escKeySent = true;
|
||||
window.focus();
|
||||
synthesizeKey("VK_ESCAPE", {});
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
<p>Inner frame</p>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,63 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=700764
|
||||
|
||||
Verify that an ESC key press in a subdoc of a full-screen doc causes us to
|
||||
exit DOM full-screen mode.
|
||||
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 700764</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<style>
|
||||
body:-moz-full-screen, div:-moz-full-screen {
|
||||
background-color: red;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body onload="startTest();">
|
||||
|
||||
<script type="application/javascript">
|
||||
|
||||
function ok(condition, msg) {
|
||||
opener.ok(condition, msg);
|
||||
}
|
||||
|
||||
function is(a, b, msg) {
|
||||
opener.is(a, b, msg);
|
||||
}
|
||||
|
||||
function finish() {
|
||||
opener.nextTest();
|
||||
}
|
||||
|
||||
function fullscreenchange1(event) {
|
||||
ok(document.mozFullScreen, "Should have entered full-screen mode");
|
||||
document.removeEventListener("mozfullscreenchange", fullscreenchange1, false);
|
||||
document.addEventListener("mozfullscreenchange", fullscreenchange2, false);
|
||||
ok(!document.getElementById("subdoc").contentWindow.escKeySent, "Should not yet have sent ESC key press.");
|
||||
document.getElementById("subdoc").contentWindow.startTest();
|
||||
}
|
||||
|
||||
function fullscreenchange2(event) {
|
||||
document.removeEventListener("mozfullscreenchange", fullscreenchange2, false);
|
||||
ok(document.getElementById("subdoc").contentWindow.escKeySent, "Should have sent ESC key press.");
|
||||
ok(!document.getElementById("subdoc").contentWindow.escKeyReceived, "ESC key press to exit should not be delivered.");
|
||||
ok(!document.mozFullScreen, "Should have left full-screen mode on ESC key press");
|
||||
finish();
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
document.addEventListener("mozfullscreenchange", fullscreenchange1, false);
|
||||
SimpleTest.waitForFocus(function() {document.body.mozRequestFullScreen();});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<!-- This subframe conducts the test. -->
|
||||
<iframe id="subdoc" src="file_fullscreen-esc-exit-inner.html"></iframe>
|
||||
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=660959
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 660959</title>
|
||||
<script type="application/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="reflect.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=660959">Mozilla Bug 660959</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
<a href="#" id="testa"></a>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script>
|
||||
is($("content").querySelector(":link, :visited"), $("testa"),
|
||||
"Should find a link even in a display:none subtree");
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,31 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=660959
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 660959</title>
|
||||
<script type="application/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="reflect.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<style>
|
||||
:link, :visited {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=660959">Mozilla Bug 660959</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
<a href="#" id="a"></a>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
var a = document.getElementById("a");
|
||||
is(window.getComputedStyle(a).color, "rgb(255, 0, 0)", "Link is not right color?");
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,29 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=660959
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 660959</title>
|
||||
<script type="application/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="reflect.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=660959">Mozilla Bug 660959</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
<a href="http://www.example.com"></a>
|
||||
<div id="foo">
|
||||
<span id="test"></span>
|
||||
</div>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script>
|
||||
is($("foo").querySelector(":link + * span, :visited + * span"), $("test"),
|
||||
"Should be able to find link siblings even in a display:none subtree");
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -36,6 +36,7 @@ SpecialPowers.setBoolPref("full-screen-api.allow-trusted-requests-only", false);
|
|||
// run in an iframe, which by default will not have the mozallowfullscreen
|
||||
// attribute set, so full-screen won't work.
|
||||
var gTestWindows = [
|
||||
"file_fullscreen-esc-exit.html",
|
||||
"file_fullscreen-denied.html",
|
||||
"file_fullscreen-api.html",
|
||||
"file_fullscreen-api-keys.html",
|
||||
|
|
|
@ -1600,6 +1600,16 @@ nsHTMLDocument::Open(const nsAString& aContentTypeOrUrl,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// No calling document.open() without a script global object
|
||||
if (!mScriptGlobalObject) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsPIDOMWindow* outer = GetWindow();
|
||||
if (!outer || (GetInnerWindow() != outer->GetCurrentInnerWindow())) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// check whether we're in the middle of unload. If so, ignore this call.
|
||||
nsCOMPtr<nsIDocShell> shell = do_QueryReferent(mDocumentContainer);
|
||||
if (!shell) {
|
||||
|
|
|
@ -89,19 +89,23 @@ nsMathMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
|||
aCompileEventHandlers);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (aDocument && !aDocument->GetMathMLEnabled()) {
|
||||
// Enable MathML and setup the style sheet during binding, not element
|
||||
// construction, because we could move a MathML element from the document
|
||||
// that created it to another document.
|
||||
aDocument->SetMathMLEnabled();
|
||||
aDocument->EnsureCatalogStyleSheet(kMathMLStyleSheetURI);
|
||||
if (aDocument) {
|
||||
aDocument->RegisterPendingLinkUpdate(this);
|
||||
|
||||
if (!aDocument->GetMathMLEnabled()) {
|
||||
// Enable MathML and setup the style sheet during binding, not element
|
||||
// construction, because we could move a MathML element from the document
|
||||
// that created it to another document.
|
||||
aDocument->SetMathMLEnabled();
|
||||
aDocument->EnsureCatalogStyleSheet(kMathMLStyleSheetURI);
|
||||
|
||||
// Rebuild style data for the presshell, because style system
|
||||
// optimizations may have taken place assuming MathML was disabled.
|
||||
// (See nsRuleNode::CheckSpecifiedProperties.)
|
||||
nsCOMPtr<nsIPresShell> shell = aDocument->GetShell();
|
||||
if (shell) {
|
||||
shell->GetPresContext()->PostRebuildAllStyleDataEvent(nsChangeHint(0));
|
||||
// Rebuild style data for the presshell, because style system
|
||||
// optimizations may have taken place assuming MathML was disabled.
|
||||
// (See nsRuleNode::CheckSpecifiedProperties.)
|
||||
nsCOMPtr<nsIPresShell> shell = aDocument->GetShell();
|
||||
if (shell) {
|
||||
shell->GetPresContext()->PostRebuildAllStyleDataEvent(nsChangeHint(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,6 +118,11 @@ nsMathMLElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
|||
// If this link is ever reinserted into a document, it might
|
||||
// be under a different xml:base, so forget the cached state now.
|
||||
Link::ResetLinkState(false);
|
||||
|
||||
nsIDocument* doc = GetCurrentDoc();
|
||||
if (doc) {
|
||||
doc->UnregisterPendingLinkUpdate(this);
|
||||
}
|
||||
|
||||
nsMathMLElementBase::UnbindFromTree(aDeep, aNullParent);
|
||||
}
|
||||
|
@ -622,12 +631,6 @@ nsMathMLElement::GetLinkState() const
|
|||
return Link::GetLinkState();
|
||||
}
|
||||
|
||||
void
|
||||
nsMathMLElement::RequestLinkStateUpdate()
|
||||
{
|
||||
UpdateLinkState(Link::LinkState());
|
||||
}
|
||||
|
||||
already_AddRefed<nsIURI>
|
||||
nsMathMLElement::GetHrefURI() const
|
||||
{
|
||||
|
|
|
@ -116,7 +116,6 @@ public:
|
|||
virtual bool IsLink(nsIURI** aURI) const;
|
||||
virtual void GetLinkTarget(nsAString& aTarget);
|
||||
virtual nsLinkState GetLinkState() const;
|
||||
virtual void RequestLinkStateUpdate();
|
||||
virtual already_AddRefed<nsIURI> GetHrefURI() const;
|
||||
nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
|
||||
const nsAString& aValue, bool aNotify)
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="reftest-wait">
|
||||
<script>
|
||||
<![CDATA[
|
||||
function boom()
|
||||
{
|
||||
document.documentElement.removeChild(document.getElementById("a"));
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
]]>
|
||||
</script>
|
||||
<animate id="a" begin="a.end; -0.1s" end="a.begin+0.2s" onend="boom()"/>
|
||||
<animate id="a" begin="a.end; -0.1s" end="a.begin+0.2s"/>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 398 B |
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<animate id="b" end="b.end" dur="3s" />
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 90 B |
|
@ -42,4 +42,6 @@ load 670313-1.svg
|
|||
load 678822-1.svg
|
||||
load 678847-1.svg
|
||||
load 678938-1.svg
|
||||
load 690994-1.svg
|
||||
load 697640-1.svg
|
||||
load 699325-1.svg
|
||||
|
|
|
@ -1622,10 +1622,6 @@ nsSMILTimedElement::GetNextInterval(const nsSMILInterval* aPrevInterval,
|
|||
beginAfter = aPrevInterval->End()->Time();
|
||||
prevIntervalWasZeroDur
|
||||
= aPrevInterval->End()->Time() == aPrevInterval->Begin()->Time();
|
||||
if (aFixedBeginTime) {
|
||||
prevIntervalWasZeroDur &=
|
||||
aPrevInterval->Begin()->Time() == aFixedBeginTime->Time();
|
||||
}
|
||||
} else {
|
||||
beginAfter.SetMillis(LL_MININT);
|
||||
}
|
||||
|
@ -1647,17 +1643,17 @@ nsSMILTimedElement::GetNextInterval(const nsSMILInterval* aPrevInterval,
|
|||
tempBegin = new nsSMILInstanceTime(nsSMILTimeValue(0));
|
||||
} else {
|
||||
PRInt32 beginPos = 0;
|
||||
// If we're updating the current interval then skip any begin time that is
|
||||
// dependent on the current interval's begin time. e.g.
|
||||
// <animate id="a" begin="b.begin; a.begin+2s"...
|
||||
// If b's interval disappears whilst 'a' is in the waiting state the begin
|
||||
// time at "a.begin+2s" should be skipped since 'a' never begun.
|
||||
do {
|
||||
tempBegin =
|
||||
GetNextGreaterOrEqual(mBeginInstances, beginAfter, beginPos);
|
||||
if (!tempBegin || !tempBegin->Time().IsDefinite()) {
|
||||
return false;
|
||||
}
|
||||
// If we're updating the current interval then skip any begin time that is
|
||||
// dependent on the current interval's begin time. e.g.
|
||||
// <animate id="a" begin="b.begin; a.begin+2s"...
|
||||
// If b's interval disappears whilst 'a' is in the waiting state the begin
|
||||
// time at "a.begin+2s" should be skipped since 'a' never begun.
|
||||
} while (aReplacedInterval &&
|
||||
tempBegin->GetBaseTime() == aReplacedInterval->Begin());
|
||||
}
|
||||
|
@ -1668,37 +1664,55 @@ nsSMILTimedElement::GetNextInterval(const nsSMILInterval* aPrevInterval,
|
|||
// Calculate end time
|
||||
{
|
||||
PRInt32 endPos = 0;
|
||||
// As above with begin times, avoid creating self-referential loops
|
||||
// between instance times by checking that the newly found end instance
|
||||
// time is not already dependent on the end of the current interval.
|
||||
do {
|
||||
tempEnd =
|
||||
GetNextGreaterOrEqual(mEndInstances, tempBegin->Time(), endPos);
|
||||
|
||||
// SMIL doesn't allow for coincident zero-duration intervals, so if the
|
||||
// previous interval was zero-duration, and tempEnd is going to give us
|
||||
// another zero duration interval, then look for another end to use
|
||||
// instead.
|
||||
if (tempEnd && prevIntervalWasZeroDur &&
|
||||
tempEnd->Time() == beginAfter) {
|
||||
tempEnd = GetNextGreater(mEndInstances, tempBegin->Time(), endPos);
|
||||
}
|
||||
// As above with begin times, avoid creating self-referential loops
|
||||
// between instance times by checking that the newly found end instance
|
||||
// time is not already dependent on the end of the current interval.
|
||||
} while (tempEnd && aReplacedInterval &&
|
||||
tempEnd->GetBaseTime() == aReplacedInterval->End());
|
||||
|
||||
// If the last interval ended at the same point and was zero-duration and
|
||||
// this one is too, look for another end to use instead
|
||||
if (tempEnd && tempEnd->Time() == tempBegin->Time() &&
|
||||
prevIntervalWasZeroDur) {
|
||||
tempEnd = GetNextGreater(mEndInstances, tempBegin->Time(), endPos);
|
||||
}
|
||||
|
||||
// If all the ends are before the beginning we have a bad interval UNLESS:
|
||||
// a) We never had any end attribute to begin with (and hence we should
|
||||
// just use the active duration after allowing for the possibility of
|
||||
// an end instance provided by a DOM call), OR
|
||||
// b) We have no definite end instances (SMIL only says "if the instance
|
||||
// list is empty"--but if we have indefinite/unresolved instance times
|
||||
// then there must be a good reason we haven't used them (since they
|
||||
// will be >= tempBegin) such as avoiding creating a self-referential
|
||||
// loop. In any case, the interval should be allowed to be open.), OR
|
||||
// c) We have end events which leave the interval open-ended.
|
||||
bool openEndedIntervalOk = mEndSpecs.IsEmpty() ||
|
||||
!HaveDefiniteEndTimes() ||
|
||||
if (!tempEnd) {
|
||||
// If all the ends are before the beginning we have a bad interval
|
||||
// UNLESS:
|
||||
// a) We never had any end attribute to begin with (the SMIL pseudocode
|
||||
// places this condition earlier in the flow but that fails to allow
|
||||
// for DOM calls when no "indefinite" condition is given), OR
|
||||
// b) We never had any end instance times to begin with, OR
|
||||
// c) We have end events which leave the interval open-ended.
|
||||
bool openEndedIntervalOk = mEndSpecs.IsEmpty() ||
|
||||
mEndInstances.IsEmpty() ||
|
||||
EndHasEventConditions();
|
||||
if (!tempEnd && !openEndedIntervalOk)
|
||||
return false; // Bad interval
|
||||
|
||||
// The above conditions correspond with the SMIL pseudocode but SMIL
|
||||
// doesn't address self-dependent instance times which we choose to
|
||||
// ignore.
|
||||
//
|
||||
// Therefore we add a qualification of (b) above that even if
|
||||
// there are end instance times but they all depend on the end of the
|
||||
// current interval we should act as if they didn't exist and allow the
|
||||
// open-ended interval.
|
||||
//
|
||||
// In the following condition we don't use |= because it doesn't provide
|
||||
// short-circuit behavior.
|
||||
openEndedIntervalOk = openEndedIntervalOk ||
|
||||
(aReplacedInterval &&
|
||||
AreEndTimesDependentOn(aReplacedInterval->End()));
|
||||
|
||||
if (!openEndedIntervalOk) {
|
||||
return false; // Bad interval
|
||||
}
|
||||
}
|
||||
|
||||
nsSMILTimeValue intervalEnd = tempEnd
|
||||
? tempEnd->Time() : nsSMILTimeValue();
|
||||
|
@ -1710,17 +1724,21 @@ nsSMILTimedElement::GetNextInterval(const nsSMILInterval* aPrevInterval,
|
|||
}
|
||||
NS_ABORT_IF_FALSE(tempEnd, "Failed to get end point for next interval");
|
||||
|
||||
// If we get two zero-length intervals in a row we will potentially have an
|
||||
// infinite loop so we break it here by searching for the next begin time
|
||||
// greater than tempEnd on the next time around.
|
||||
if (tempEnd->Time().IsDefinite() && tempBegin->Time() == tempEnd->Time()) {
|
||||
// When we choose the interval endpoints, we don't allow coincident
|
||||
// zero-duration intervals, so if we arrive here and we have a zero-duration
|
||||
// interval starting at the same point as a previous zero-duration interval,
|
||||
// then it must be because we've applied constraints to the active duration.
|
||||
// In that case, we will potentially run into an infinite loop, so we break
|
||||
// it by searching for the next interval that starts AFTER our current
|
||||
// zero-duration interval.
|
||||
if (prevIntervalWasZeroDur && tempEnd->Time() == beginAfter) {
|
||||
if (prevIntervalWasZeroDur) {
|
||||
beginAfter.SetMillis(tempEnd->Time().GetMillis() + 1);
|
||||
beginAfter.SetMillis(tempBegin->Time().GetMillis() + 1);
|
||||
prevIntervalWasZeroDur = false;
|
||||
continue;
|
||||
}
|
||||
prevIntervalWasZeroDur = true;
|
||||
}
|
||||
prevIntervalWasZeroDur = tempBegin->Time() == tempEnd->Time();
|
||||
|
||||
// Check for valid interval
|
||||
if (tempEnd->Time() > zeroTime ||
|
||||
|
@ -2252,17 +2270,6 @@ nsSMILTimedElement::GetPreviousInterval() const
|
|||
: mOldIntervals[mOldIntervals.Length()-1].get();
|
||||
}
|
||||
|
||||
bool
|
||||
nsSMILTimedElement::HaveDefiniteEndTimes() const
|
||||
{
|
||||
if (mEndInstances.IsEmpty())
|
||||
return false;
|
||||
|
||||
// mEndInstances is sorted so if the first time is not definite then none of
|
||||
// them are
|
||||
return mEndInstances[0]->Time().IsDefinite();
|
||||
}
|
||||
|
||||
bool
|
||||
nsSMILTimedElement::EndHasEventConditions() const
|
||||
{
|
||||
|
@ -2273,6 +2280,21 @@ nsSMILTimedElement::EndHasEventConditions() const
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
nsSMILTimedElement::AreEndTimesDependentOn(
|
||||
const nsSMILInstanceTime* aBase) const
|
||||
{
|
||||
if (mEndInstances.IsEmpty())
|
||||
return false;
|
||||
|
||||
for (PRUint32 i = 0; i < mEndInstances.Length(); ++i) {
|
||||
if (mEndInstances[i]->GetBaseTime() != aBase) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Hashtable callback functions
|
||||
|
||||
|
|
|
@ -525,8 +525,9 @@ protected:
|
|||
const nsSMILInstanceTime* GetEffectiveBeginInstance() const;
|
||||
const nsSMILInterval* GetPreviousInterval() const;
|
||||
bool HasPlayed() const { return !mOldIntervals.IsEmpty(); }
|
||||
bool HaveDefiniteEndTimes() const;
|
||||
bool EndHasEventConditions() const;
|
||||
bool AreEndTimesDependentOn(
|
||||
const nsSMILInstanceTime* aBase) const;
|
||||
|
||||
// Reset the current interval by first passing ownership to a temporary
|
||||
// variable so that if Unlink() results in us receiving a callback,
|
||||
|
|
|
@ -145,6 +145,10 @@ nsSVGAElement::BindToTree(nsIDocument *aDocument, nsIContent *aParent,
|
|||
aBindingParent,
|
||||
aCompileEventHandlers);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (aDocument) {
|
||||
aDocument->RegisterPendingLinkUpdate(this);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -155,6 +159,11 @@ nsSVGAElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
|||
// If this link is ever reinserted into a document, it might
|
||||
// be under a different xml:base, so forget the cached state now.
|
||||
Link::ResetLinkState(false);
|
||||
|
||||
nsIDocument* doc = GetCurrentDoc();
|
||||
if (doc) {
|
||||
doc->UnregisterPendingLinkUpdate(this);
|
||||
}
|
||||
|
||||
nsSVGAElementBase::UnbindFromTree(aDeep, aNullParent);
|
||||
}
|
||||
|
@ -165,12 +174,6 @@ nsSVGAElement::GetLinkState() const
|
|||
return Link::GetLinkState();
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGAElement::RequestLinkStateUpdate()
|
||||
{
|
||||
UpdateLinkState(Link::LinkState());
|
||||
}
|
||||
|
||||
already_AddRefed<nsIURI>
|
||||
nsSVGAElement::GetHrefURI() const
|
||||
{
|
||||
|
|
|
@ -91,7 +91,6 @@ public:
|
|||
virtual bool IsLink(nsIURI** aURI) const;
|
||||
virtual void GetLinkTarget(nsAString& aTarget);
|
||||
virtual nsLinkState GetLinkState() const;
|
||||
virtual void RequestLinkStateUpdate();
|
||||
virtual already_AddRefed<nsIURI> GetHrefURI() const;
|
||||
virtual nsEventStates IntrinsicState() const;
|
||||
nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
<script class="testbody" type="application/javascript">
|
||||
<![CDATA[
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.ignoreAllUncaughtExceptions();
|
||||
|
||||
Components.utils.import("resource://gre/modules/NetUtil.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
|
|
@ -62,6 +62,8 @@ import android.webkit.MimeTypeMap;
|
|||
import android.media.MediaScannerConnection;
|
||||
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
|
||||
import android.provider.Settings;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
import android.util.*;
|
||||
import android.net.Uri;
|
||||
|
@ -411,8 +413,6 @@ public class GeckoAppShell
|
|||
GeckoAppShell.nativeRun(combinedArgs);
|
||||
}
|
||||
|
||||
private static GeckoEvent mLastDrawEvent;
|
||||
|
||||
private static void sendPendingEventsToGecko() {
|
||||
try {
|
||||
while (!gPendingEvents.isEmpty()) {
|
||||
|
@ -1359,6 +1359,13 @@ public class GeckoAppShell
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean getAccessibilityEnabled() {
|
||||
AccessibilityManager accessibilityManager =
|
||||
(AccessibilityManager) GeckoApp.mAppContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
|
||||
return accessibilityManager.isEnabled();
|
||||
}
|
||||
|
||||
public static void addPluginView(final View view,
|
||||
final double x, final double y,
|
||||
final double w, final double h) {
|
||||
|
@ -1620,6 +1627,16 @@ public class GeckoAppShell
|
|||
}
|
||||
}
|
||||
|
||||
// unused
|
||||
public static String handleGeckoMessage(String message) {
|
||||
return "";
|
||||
}
|
||||
// unused
|
||||
static void checkUriVisited(String uri) {}
|
||||
// unused
|
||||
static void markUriVisited(final String uri) {}
|
||||
|
||||
|
||||
public static void enableBatteryNotifications() {
|
||||
GeckoBatteryManager.enableNotifications();
|
||||
}
|
||||
|
|
|
@ -73,6 +73,8 @@ public class GeckoEvent {
|
|||
public static final int SURFACE_DESTROYED = 14;
|
||||
public static final int GECKO_EVENT_SYNC = 15;
|
||||
public static final int ACTIVITY_START = 17;
|
||||
public static final int SAVE_STATE = 18;
|
||||
public static final int BROADCAST = 19;
|
||||
|
||||
public static final int IME_COMPOSITION_END = 0;
|
||||
public static final int IME_COMPOSITION_BEGIN = 1;
|
||||
|
@ -104,7 +106,7 @@ public class GeckoEvent {
|
|||
public int mMetaState, mFlags;
|
||||
public int mKeyCode, mUnicodeChar;
|
||||
public int mOffset, mCount;
|
||||
public String mCharacters;
|
||||
public String mCharacters, mCharactersExtra;
|
||||
public int mRangeType, mRangeStyles;
|
||||
public int mRangeForeColor, mRangeBackColor;
|
||||
public Location mLocation;
|
||||
|
@ -223,6 +225,12 @@ public class GeckoEvent {
|
|||
mP1 = new Point(screenw, screenh);
|
||||
}
|
||||
|
||||
public GeckoEvent(String subject, String data) {
|
||||
mType = BROADCAST;
|
||||
mCharacters = subject;
|
||||
mCharactersExtra = data;
|
||||
}
|
||||
|
||||
public GeckoEvent(String uri) {
|
||||
mType = LOAD_URI;
|
||||
mCharacters = uri;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.ignoreAllUncaughtExceptions();
|
||||
|
||||
function getMisspelledWords(editor) {
|
||||
return editor.selectionController.getSelection(Components.interfaces.nsISelectionController.SELECTION_SPELLCHECK).toString();
|
||||
|
|
|
@ -308,6 +308,63 @@ struct BaseRect {
|
|||
T XMost() const { return x + width; }
|
||||
T YMost() const { return y + height; }
|
||||
|
||||
// Round the rectangle edges to integer coordinates, such that the rounded
|
||||
// rectangle has the same set of pixel centers as the original rectangle.
|
||||
// Edges at offset 0.5 round up.
|
||||
// Suitable for most places where integral device coordinates
|
||||
// are needed, but note that any translation should be applied first to
|
||||
// avoid pixel rounding errors.
|
||||
// Note that this is *not* rounding to nearest integer if the values are negative.
|
||||
// They are always rounding as floor(n + 0.5).
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14
|
||||
// If you need similar method which is using NS_round(), you should create
|
||||
// new |RoundAwayFromZero()| method.
|
||||
void Round()
|
||||
{
|
||||
T x0 = static_cast<T>(floor(T(X()) + 0.5));
|
||||
T y0 = static_cast<T>(floor(T(Y()) + 0.5));
|
||||
T x1 = static_cast<T>(floor(T(XMost()) + 0.5));
|
||||
T y1 = static_cast<T>(floor(T(YMost()) + 0.5));
|
||||
|
||||
x = x0;
|
||||
y = y0;
|
||||
|
||||
width = x1 - x0;
|
||||
height = y1 - y0;
|
||||
}
|
||||
|
||||
// Snap the rectangle edges to integer coordinates, such that the
|
||||
// original rectangle contains the resulting rectangle.
|
||||
void RoundIn()
|
||||
{
|
||||
T x0 = static_cast<T>(ceil(T(X())));
|
||||
T y0 = static_cast<T>(ceil(T(Y())));
|
||||
T x1 = static_cast<T>(floor(T(XMost())));
|
||||
T y1 = static_cast<T>(floor(T(YMost())));
|
||||
|
||||
x = x0;
|
||||
y = y0;
|
||||
|
||||
width = x1 - x0;
|
||||
height = y1 - y0;
|
||||
}
|
||||
|
||||
// Snap the rectangle edges to integer coordinates, such that the
|
||||
// resulting rectangle contains the original rectangle.
|
||||
void RoundOut()
|
||||
{
|
||||
T x0 = static_cast<T>(floor(T(X())));
|
||||
T y0 = static_cast<T>(floor(T(Y())));
|
||||
T x1 = static_cast<T>(ceil(T(XMost())));
|
||||
T y1 = static_cast<T>(ceil(T(YMost())));
|
||||
|
||||
x = x0;
|
||||
y = y0;
|
||||
|
||||
width = x1 - x0;
|
||||
height = y1 - y0;
|
||||
}
|
||||
|
||||
// Scale 'this' by aScale, converting coordinates to integers so that the result is
|
||||
// the smallest integer-coordinate rectangle containing the unrounded result.
|
||||
// Note: this can turn an empty rectangle into a non-empty rectangle
|
||||
|
|
|
@ -0,0 +1,526 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla gfx.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
|
||||
#include "CheckedInt.h"
|
||||
#include "mozilla/Util.h"
|
||||
|
||||
#include "mozilla/gfx/Blur.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* Box blur involves looking at one pixel, and setting its value to the average
|
||||
* of its neighbouring pixels.
|
||||
* @param aInput The input buffer.
|
||||
* @param aOutput The output buffer.
|
||||
* @param aLeftLobe The number of pixels to blend on the left.
|
||||
* @param aRightLobe The number of pixels to blend on the right.
|
||||
* @param aWidth The number of columns in the buffers.
|
||||
* @param aRows The number of rows in the buffers.
|
||||
* @param aSkipRect An area to skip blurring in.
|
||||
* XXX shouldn't we pass stride in separately here?
|
||||
*/
|
||||
static void
|
||||
BoxBlurHorizontal(unsigned char* aInput,
|
||||
unsigned char* aOutput,
|
||||
int32_t aLeftLobe,
|
||||
int32_t aRightLobe,
|
||||
int32_t aWidth,
|
||||
int32_t aRows,
|
||||
const IntRect& aSkipRect)
|
||||
{
|
||||
MOZ_ASSERT(aWidth > 0);
|
||||
|
||||
int32_t boxSize = aLeftLobe + aRightLobe + 1;
|
||||
bool skipRectCoversWholeRow = 0 >= aSkipRect.x &&
|
||||
aWidth <= aSkipRect.XMost();
|
||||
|
||||
for (int32_t y = 0; y < aRows; y++) {
|
||||
// Check whether the skip rect intersects this row. If the skip
|
||||
// rect covers the whole surface in this row, we can avoid
|
||||
// this row entirely (and any others along the skip rect).
|
||||
bool inSkipRectY = y >= aSkipRect.y &&
|
||||
y < aSkipRect.YMost();
|
||||
if (inSkipRectY && skipRectCoversWholeRow) {
|
||||
y = aSkipRect.YMost() - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
int32_t alphaSum = 0;
|
||||
for (int32_t i = 0; i < boxSize; i++) {
|
||||
int32_t pos = i - aLeftLobe;
|
||||
// See assertion above; if aWidth is zero, then we would have no
|
||||
// valid position to clamp to.
|
||||
pos = max(pos, 0);
|
||||
pos = min(pos, aWidth - 1);
|
||||
alphaSum += aInput[aWidth * y + pos];
|
||||
}
|
||||
for (int32_t x = 0; x < aWidth; x++) {
|
||||
// Check whether we are within the skip rect. If so, go
|
||||
// to the next point outside the skip rect.
|
||||
if (inSkipRectY && x >= aSkipRect.x &&
|
||||
x < aSkipRect.XMost()) {
|
||||
x = aSkipRect.XMost();
|
||||
if (x >= aWidth)
|
||||
break;
|
||||
|
||||
// Recalculate the neighbouring alpha values for
|
||||
// our new point on the surface.
|
||||
alphaSum = 0;
|
||||
for (int32_t i = 0; i < boxSize; i++) {
|
||||
int32_t pos = x + i - aLeftLobe;
|
||||
// See assertion above; if aWidth is zero, then we would have no
|
||||
// valid position to clamp to.
|
||||
pos = max(pos, 0);
|
||||
pos = min(pos, aWidth - 1);
|
||||
alphaSum += aInput[aWidth * y + pos];
|
||||
}
|
||||
}
|
||||
int32_t tmp = x - aLeftLobe;
|
||||
int32_t last = max(tmp, 0);
|
||||
int32_t next = min(tmp + boxSize, aWidth - 1);
|
||||
|
||||
aOutput[aWidth * y + x] = alphaSum / boxSize;
|
||||
|
||||
alphaSum += aInput[aWidth * y + next] -
|
||||
aInput[aWidth * y + last];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Identical to BoxBlurHorizontal, except it blurs top and bottom instead of
|
||||
* left and right.
|
||||
* XXX shouldn't we pass stride in separately here?
|
||||
*/
|
||||
static void
|
||||
BoxBlurVertical(unsigned char* aInput,
|
||||
unsigned char* aOutput,
|
||||
int32_t aTopLobe,
|
||||
int32_t aBottomLobe,
|
||||
int32_t aWidth,
|
||||
int32_t aRows,
|
||||
const IntRect& aSkipRect)
|
||||
{
|
||||
MOZ_ASSERT(aRows > 0);
|
||||
|
||||
int32_t boxSize = aTopLobe + aBottomLobe + 1;
|
||||
bool skipRectCoversWholeColumn = 0 >= aSkipRect.y &&
|
||||
aRows <= aSkipRect.YMost();
|
||||
|
||||
for (int32_t x = 0; x < aWidth; x++) {
|
||||
bool inSkipRectX = x >= aSkipRect.x &&
|
||||
x < aSkipRect.XMost();
|
||||
if (inSkipRectX && skipRectCoversWholeColumn) {
|
||||
x = aSkipRect.XMost() - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
int32_t alphaSum = 0;
|
||||
for (int32_t i = 0; i < boxSize; i++) {
|
||||
int32_t pos = i - aTopLobe;
|
||||
// See assertion above; if aRows is zero, then we would have no
|
||||
// valid position to clamp to.
|
||||
pos = max(pos, 0);
|
||||
pos = min(pos, aRows - 1);
|
||||
alphaSum += aInput[aWidth * pos + x];
|
||||
}
|
||||
for (int32_t y = 0; y < aRows; y++) {
|
||||
if (inSkipRectX && y >= aSkipRect.y &&
|
||||
y < aSkipRect.YMost()) {
|
||||
y = aSkipRect.YMost();
|
||||
if (y >= aRows)
|
||||
break;
|
||||
|
||||
alphaSum = 0;
|
||||
for (int32_t i = 0; i < boxSize; i++) {
|
||||
int32_t pos = y + i - aTopLobe;
|
||||
// See assertion above; if aRows is zero, then we would have no
|
||||
// valid position to clamp to.
|
||||
pos = max(pos, 0);
|
||||
pos = min(pos, aRows - 1);
|
||||
alphaSum += aInput[aWidth * pos + x];
|
||||
}
|
||||
}
|
||||
int32_t tmp = y - aTopLobe;
|
||||
int32_t last = max(tmp, 0);
|
||||
int32_t next = min(tmp + boxSize, aRows - 1);
|
||||
|
||||
aOutput[aWidth * y + x] = alphaSum/boxSize;
|
||||
|
||||
alphaSum += aInput[aWidth * next + x] -
|
||||
aInput[aWidth * last + x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ComputeLobes(int32_t aRadius, int32_t aLobes[3][2])
|
||||
{
|
||||
int32_t major, minor, final;
|
||||
|
||||
/* See http://www.w3.org/TR/SVG/filters.html#feGaussianBlur for
|
||||
* some notes about approximating the Gaussian blur with box-blurs.
|
||||
* The comments below are in the terminology of that page.
|
||||
*/
|
||||
int32_t z = aRadius / 3;
|
||||
switch (aRadius % 3) {
|
||||
case 0:
|
||||
// aRadius = z*3; choose d = 2*z + 1
|
||||
major = minor = final = z;
|
||||
break;
|
||||
case 1:
|
||||
// aRadius = z*3 + 1
|
||||
// This is a tricky case since there is no value of d which will
|
||||
// yield a radius of exactly aRadius. If d is odd, i.e. d=2*k + 1
|
||||
// for some integer k, then the radius will be 3*k. If d is even,
|
||||
// i.e. d=2*k, then the radius will be 3*k - 1.
|
||||
// So we have to choose values that don't match the standard
|
||||
// algorithm.
|
||||
major = z + 1;
|
||||
minor = final = z;
|
||||
break;
|
||||
case 2:
|
||||
// aRadius = z*3 + 2; choose d = 2*z + 2
|
||||
major = final = z + 1;
|
||||
minor = z;
|
||||
break;
|
||||
default:
|
||||
// Mathematical impossibility!
|
||||
MOZ_ASSERT(false);
|
||||
major = minor = final = 0;
|
||||
}
|
||||
MOZ_ASSERT(major + minor + final == aRadius);
|
||||
|
||||
aLobes[0][0] = major;
|
||||
aLobes[0][1] = minor;
|
||||
aLobes[1][0] = minor;
|
||||
aLobes[1][1] = major;
|
||||
aLobes[2][0] = final;
|
||||
aLobes[2][1] = final;
|
||||
}
|
||||
|
||||
static void
|
||||
SpreadHorizontal(unsigned char* aInput,
|
||||
unsigned char* aOutput,
|
||||
int32_t aRadius,
|
||||
int32_t aWidth,
|
||||
int32_t aRows,
|
||||
int32_t aStride,
|
||||
const IntRect& aSkipRect)
|
||||
{
|
||||
if (aRadius == 0) {
|
||||
memcpy(aOutput, aInput, aStride * aRows);
|
||||
return;
|
||||
}
|
||||
|
||||
bool skipRectCoversWholeRow = 0 >= aSkipRect.x &&
|
||||
aWidth <= aSkipRect.XMost();
|
||||
for (int32_t y = 0; y < aRows; y++) {
|
||||
// Check whether the skip rect intersects this row. If the skip
|
||||
// rect covers the whole surface in this row, we can avoid
|
||||
// this row entirely (and any others along the skip rect).
|
||||
bool inSkipRectY = y >= aSkipRect.y &&
|
||||
y < aSkipRect.YMost();
|
||||
if (inSkipRectY && skipRectCoversWholeRow) {
|
||||
y = aSkipRect.YMost() - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int32_t x = 0; x < aWidth; x++) {
|
||||
// Check whether we are within the skip rect. If so, go
|
||||
// to the next point outside the skip rect.
|
||||
if (inSkipRectY && x >= aSkipRect.x &&
|
||||
x < aSkipRect.XMost()) {
|
||||
x = aSkipRect.XMost();
|
||||
if (x >= aWidth)
|
||||
break;
|
||||
}
|
||||
|
||||
int32_t sMin = max(x - aRadius, 0);
|
||||
int32_t sMax = min(x + aRadius, aWidth - 1);
|
||||
int32_t v = 0;
|
||||
for (int32_t s = sMin; s <= sMax; ++s) {
|
||||
v = max<int32_t>(v, aInput[aStride * y + s]);
|
||||
}
|
||||
aOutput[aStride * y + x] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
SpreadVertical(unsigned char* aInput,
|
||||
unsigned char* aOutput,
|
||||
int32_t aRadius,
|
||||
int32_t aWidth,
|
||||
int32_t aRows,
|
||||
int32_t aStride,
|
||||
const IntRect& aSkipRect)
|
||||
{
|
||||
if (aRadius == 0) {
|
||||
memcpy(aOutput, aInput, aStride * aRows);
|
||||
return;
|
||||
}
|
||||
|
||||
bool skipRectCoversWholeColumn = 0 >= aSkipRect.y &&
|
||||
aRows <= aSkipRect.YMost();
|
||||
for (int32_t x = 0; x < aWidth; x++) {
|
||||
bool inSkipRectX = x >= aSkipRect.x &&
|
||||
x < aSkipRect.XMost();
|
||||
if (inSkipRectX && skipRectCoversWholeColumn) {
|
||||
x = aSkipRect.XMost() - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int32_t y = 0; y < aRows; y++) {
|
||||
// Check whether we are within the skip rect. If so, go
|
||||
// to the next point outside the skip rect.
|
||||
if (inSkipRectX && y >= aSkipRect.y &&
|
||||
y < aSkipRect.YMost()) {
|
||||
y = aSkipRect.YMost();
|
||||
if (y >= aRows)
|
||||
break;
|
||||
}
|
||||
|
||||
int32_t sMin = max(y - aRadius, 0);
|
||||
int32_t sMax = min(y + aRadius, aRows - 1);
|
||||
int32_t v = 0;
|
||||
for (int32_t s = sMin; s <= sMax; ++s) {
|
||||
v = max<int32_t>(v, aInput[aStride * s + x]);
|
||||
}
|
||||
aOutput[aStride * y + x] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static CheckedInt<int32_t>
|
||||
RoundUpToMultipleOf4(int32_t aVal)
|
||||
{
|
||||
CheckedInt<int32_t> val(aVal);
|
||||
|
||||
val += 3;
|
||||
val /= 4;
|
||||
val *= 4;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect,
|
||||
const IntSize& aSpreadRadius,
|
||||
const IntSize& aBlurRadius,
|
||||
const Rect* aDirtyRect,
|
||||
const Rect* aSkipRect)
|
||||
: mSpreadRadius(aSpreadRadius),
|
||||
mBlurRadius(aBlurRadius),
|
||||
mData(NULL)
|
||||
{
|
||||
Rect rect(aRect);
|
||||
rect.Inflate(Size(aBlurRadius + aSpreadRadius));
|
||||
rect.RoundOut();
|
||||
|
||||
if (aDirtyRect) {
|
||||
// If we get passed a dirty rect from layout, we can minimize the
|
||||
// shadow size and make painting faster.
|
||||
mHasDirtyRect = true;
|
||||
mDirtyRect = *aDirtyRect;
|
||||
Rect requiredBlurArea = mDirtyRect.Intersect(rect);
|
||||
requiredBlurArea.Inflate(Size(aBlurRadius + aSpreadRadius));
|
||||
rect = requiredBlurArea.Intersect(rect);
|
||||
} else {
|
||||
mHasDirtyRect = false;
|
||||
}
|
||||
|
||||
if (rect.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aSkipRect) {
|
||||
// If we get passed a skip rect, we can lower the amount of
|
||||
// blurring/spreading we need to do. We convert it to IntRect to avoid
|
||||
// expensive int<->float conversions if we were to use Rect instead.
|
||||
Rect skipRect = *aSkipRect;
|
||||
skipRect.RoundIn();
|
||||
skipRect.Deflate(Size(aBlurRadius + aSpreadRadius));
|
||||
mSkipRect = IntRect(skipRect.x, skipRect.y, skipRect.width, skipRect.height);
|
||||
|
||||
IntRect shadowIntRect(rect.x, rect.y, rect.width, rect.height);
|
||||
mSkipRect.IntersectRect(mSkipRect, shadowIntRect);
|
||||
|
||||
if (mSkipRect.IsEqualInterior(shadowIntRect))
|
||||
return;
|
||||
|
||||
mSkipRect -= shadowIntRect.TopLeft();
|
||||
} else {
|
||||
mSkipRect = IntRect(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
mRect = IntRect(rect.x, rect.y, rect.width, rect.height);
|
||||
|
||||
CheckedInt<int32_t> stride = RoundUpToMultipleOf4(mRect.width);
|
||||
if (stride.valid()) {
|
||||
mStride = stride.value();
|
||||
|
||||
CheckedInt<int32_t> size = CheckedInt<int32_t>(mStride) * mRect.height *
|
||||
sizeof(unsigned char);
|
||||
if (size.valid()) {
|
||||
mData = static_cast<unsigned char*>(malloc(size.value()));
|
||||
memset(mData, 0, size.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AlphaBoxBlur::~AlphaBoxBlur()
|
||||
{
|
||||
free(mData);
|
||||
}
|
||||
|
||||
unsigned char*
|
||||
AlphaBoxBlur::GetData()
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
|
||||
IntSize
|
||||
AlphaBoxBlur::GetSize()
|
||||
{
|
||||
IntSize size(mRect.width, mRect.height);
|
||||
return size;
|
||||
}
|
||||
|
||||
int32_t
|
||||
AlphaBoxBlur::GetStride()
|
||||
{
|
||||
return mStride;
|
||||
}
|
||||
|
||||
IntRect
|
||||
AlphaBoxBlur::GetRect()
|
||||
{
|
||||
return mRect;
|
||||
}
|
||||
|
||||
Rect*
|
||||
AlphaBoxBlur::GetDirtyRect()
|
||||
{
|
||||
if (mHasDirtyRect) {
|
||||
return &mDirtyRect;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
AlphaBoxBlur::Blur()
|
||||
{
|
||||
if (!mData) {
|
||||
return;
|
||||
}
|
||||
|
||||
// no need to do all this if not blurring or spreading
|
||||
if (mBlurRadius != IntSize(0,0) || mSpreadRadius != IntSize(0,0)) {
|
||||
int32_t stride = GetStride();
|
||||
|
||||
// No need to use CheckedInt here - we have validated it in the constructor.
|
||||
size_t szB = stride * GetSize().height * sizeof(unsigned char);
|
||||
unsigned char* tmpData = static_cast<unsigned char*>(malloc(szB));
|
||||
if (!tmpData)
|
||||
return; // OOM
|
||||
|
||||
memset(tmpData, 0, szB);
|
||||
|
||||
if (mSpreadRadius.width > 0 || mSpreadRadius.height > 0) {
|
||||
SpreadHorizontal(mData, tmpData, mSpreadRadius.width, GetSize().width, GetSize().height, stride, mSkipRect);
|
||||
SpreadVertical(tmpData, mData, mSpreadRadius.height, GetSize().width, GetSize().height, stride, mSkipRect);
|
||||
}
|
||||
|
||||
if (mBlurRadius.width > 0) {
|
||||
int32_t lobes[3][2];
|
||||
ComputeLobes(mBlurRadius.width, lobes);
|
||||
BoxBlurHorizontal(mData, tmpData, lobes[0][0], lobes[0][1], stride, GetSize().height, mSkipRect);
|
||||
BoxBlurHorizontal(tmpData, mData, lobes[1][0], lobes[1][1], stride, GetSize().height, mSkipRect);
|
||||
BoxBlurHorizontal(mData, tmpData, lobes[2][0], lobes[2][1], stride, GetSize().height, mSkipRect);
|
||||
} else {
|
||||
memcpy(tmpData, mData, stride * GetSize().height);
|
||||
}
|
||||
|
||||
if (mBlurRadius.height > 0) {
|
||||
int32_t lobes[3][2];
|
||||
ComputeLobes(mBlurRadius.height, lobes);
|
||||
BoxBlurVertical(tmpData, mData, lobes[0][0], lobes[0][1], stride, GetSize().height, mSkipRect);
|
||||
BoxBlurVertical(mData, tmpData, lobes[1][0], lobes[1][1], stride, GetSize().height, mSkipRect);
|
||||
BoxBlurVertical(tmpData, mData, lobes[2][0], lobes[2][1], stride, GetSize().height, mSkipRect);
|
||||
} else {
|
||||
memcpy(mData, tmpData, stride * GetSize().height);
|
||||
}
|
||||
|
||||
free(tmpData);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the box blur size (which we're calling the blur radius) from
|
||||
* the standard deviation.
|
||||
*
|
||||
* Much of this, the 3 * sqrt(2 * pi) / 4, is the known value for
|
||||
* approximating a Gaussian using box blurs. This yields quite a good
|
||||
* approximation for a Gaussian. Then we multiply this by 1.5 since our
|
||||
* code wants the radius of the entire triple-box-blur kernel instead of
|
||||
* the diameter of an individual box blur. For more details, see:
|
||||
* http://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement
|
||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=590039#c19
|
||||
*/
|
||||
static const Float GAUSSIAN_SCALE_FACTOR = (3 * sqrt(2 * M_PI) / 4) * 1.5;
|
||||
|
||||
IntSize
|
||||
AlphaBoxBlur::CalculateBlurRadius(const Point& aStd)
|
||||
{
|
||||
IntSize size(static_cast<int32_t>(floor(aStd.x * GAUSSIAN_SCALE_FACTOR + 0.5)),
|
||||
static_cast<int32_t>(floor(aStd.y * GAUSSIAN_SCALE_FACTOR + 0.5)));
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla gfx.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "mozilla/gfx/Rect.h"
|
||||
#include "mozilla/gfx/Point.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* Implementation of a triple box blur approximation of a Gaussian blur.
|
||||
*
|
||||
* A Gaussian blur is good for blurring because, when done independently
|
||||
* in the horizontal and vertical directions, it matches the result that
|
||||
* would be obtained using a different (rotated) set of axes. A triple
|
||||
* box blur is a very close approximation of a Gaussian.
|
||||
*
|
||||
* Creates an 8-bit alpha channel context for callers to draw in,
|
||||
* spreads the contents of that context, and blurs the contents.
|
||||
*
|
||||
* A spread N makes each output pixel the maximum value of all source
|
||||
* pixels within a square of side length 2N+1 centered on the output pixel.
|
||||
*
|
||||
* A temporary surface is created in the Init function. The caller then draws
|
||||
* any desired content onto the context acquired through GetContext, and lastly
|
||||
* calls Paint to apply the blurred content as an alpha mask.
|
||||
*/
|
||||
class AlphaBoxBlur
|
||||
{
|
||||
public:
|
||||
|
||||
/** Constructs a box blur and initializes the backing surface.
|
||||
*
|
||||
* @param aRect The coordinates of the surface to create in device units.
|
||||
*
|
||||
* @param aBlurRadius The blur radius in pixels. This is the radius of the
|
||||
* entire (triple) kernel function. Each individual box blur has radius
|
||||
* approximately 1/3 this value, or diameter approximately 2/3 this value.
|
||||
* This parameter should nearly always be computed using CalculateBlurRadius,
|
||||
* below.
|
||||
*
|
||||
* @param aDirtyRect A pointer to a dirty rect, measured in device units, if
|
||||
* available. This will be used for optimizing the blur operation. It is
|
||||
* safe to pass NULL here.
|
||||
*
|
||||
* @param aSkipRect A pointer to a rect, measured in device units, that
|
||||
* represents an area where blurring is unnecessary and shouldn't be done for
|
||||
* speed reasons. It is safe to pass NULL here.
|
||||
*/
|
||||
AlphaBoxBlur(const Rect& aRect,
|
||||
const IntSize& aSpreadRadius,
|
||||
const IntSize& aBlurRadius,
|
||||
const Rect* aDirtyRect,
|
||||
const Rect* aSkipRect);
|
||||
|
||||
~AlphaBoxBlur();
|
||||
|
||||
/**
|
||||
* Return the pointer to memory allocated by the constructor for the 8-bit
|
||||
* alpha surface you need to be blurred. After you draw to this surface, call
|
||||
* Blur(), below, to have its contents blurred.
|
||||
*/
|
||||
unsigned char* GetData();
|
||||
|
||||
/**
|
||||
* Return the size, in pixels, of the 8-bit alpha surface backed by the
|
||||
* pointer returned by GetData().
|
||||
*/
|
||||
IntSize GetSize();
|
||||
|
||||
/**
|
||||
* Return the stride, in bytes, of the 8-bit alpha surface backed by the
|
||||
* pointer returned by GetData().
|
||||
*/
|
||||
int32_t GetStride();
|
||||
|
||||
/**
|
||||
* Returns the device-space rectangle the 8-bit alpha surface covers.
|
||||
*/
|
||||
IntRect GetRect();
|
||||
|
||||
/**
|
||||
* Return a pointer to a dirty rect, as passed in to the constructor, or NULL
|
||||
* if none was passed in.
|
||||
*/
|
||||
Rect* GetDirtyRect();
|
||||
|
||||
/**
|
||||
* Perform the blur in-place on the surface backed by the pointer returned by
|
||||
* GetData().
|
||||
*/
|
||||
void Blur();
|
||||
|
||||
/**
|
||||
* Calculates a blur radius that, when used with box blur, approximates a
|
||||
* Gaussian blur with the given standard deviation. The result of this
|
||||
* function should be used as the aBlurRadius parameter to AlphaBoxBlur's
|
||||
* constructor, above.
|
||||
*/
|
||||
static IntSize CalculateBlurRadius(const Point& aStandardDeviation);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* A rect indicating the area where blurring is unnecessary, and the blur
|
||||
* algorithm should skip over it.
|
||||
*/
|
||||
IntRect mSkipRect;
|
||||
|
||||
/**
|
||||
* The device-space rectangle the the backing 8-bit alpha surface covers.
|
||||
*/
|
||||
IntRect mRect;
|
||||
|
||||
/**
|
||||
* A copy of the dirty rect passed to the constructor. This will only be valid if
|
||||
* mHasDirtyRect is true.
|
||||
*/
|
||||
Rect mDirtyRect;
|
||||
|
||||
/**
|
||||
* The spread radius, in pixels.
|
||||
*/
|
||||
IntSize mSpreadRadius;
|
||||
|
||||
/**
|
||||
* The blur radius, in pixels.
|
||||
*/
|
||||
IntSize mBlurRadius;
|
||||
|
||||
/**
|
||||
* A pointer to the backing 8-bit alpha surface.
|
||||
*/
|
||||
unsigned char* mData;
|
||||
|
||||
/**
|
||||
* The stride of the data contained in mData.
|
||||
*/
|
||||
int32_t mStride;
|
||||
|
||||
/**
|
||||
* Whether mDirtyRect contains valid data.
|
||||
*/
|
||||
bool mHasDirtyRect;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -56,6 +56,7 @@ EXPORTS_mozilla/gfx = \
|
|||
BaseMargin.h \
|
||||
BaseRect.h \
|
||||
BaseSize.h \
|
||||
Blur.h \
|
||||
PathHelpers.h \
|
||||
Point.h \
|
||||
Matrix.h \
|
||||
|
@ -68,6 +69,7 @@ CPPSRCS = \
|
|||
Matrix.cpp \
|
||||
DrawTargetCairo.cpp \
|
||||
SourceSurfaceCairo.cpp \
|
||||
Blur.cpp \
|
||||
$(NULL)
|
||||
|
||||
|
||||
|
|
|
@ -45,26 +45,21 @@
|
|||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
struct Point :
|
||||
public BasePoint<Float, Point> {
|
||||
typedef BasePoint<Float, Point> Super;
|
||||
Point() : Super() {}
|
||||
Point(Float aX, Float aY) : Super(aX, aY) {}
|
||||
};
|
||||
|
||||
struct IntPoint :
|
||||
public BasePoint<int32_t, Point> {
|
||||
typedef BasePoint<int32_t, Point> Super;
|
||||
public BasePoint<int32_t, IntPoint> {
|
||||
typedef BasePoint<int32_t, IntPoint> Super;
|
||||
|
||||
IntPoint() : Super() {}
|
||||
IntPoint(int32_t aX, int32_t aY) : Super(aX, aY) {}
|
||||
};
|
||||
|
||||
struct Size :
|
||||
public BaseSize<Float, Size> {
|
||||
typedef BaseSize<Float, Size> Super;
|
||||
struct Point :
|
||||
public BasePoint<Float, Point> {
|
||||
typedef BasePoint<Float, Point> Super;
|
||||
|
||||
Size() : Super() {}
|
||||
Size(Float aWidth, Float aHeight) : Super(aWidth, aHeight) {}
|
||||
Point() : Super() {}
|
||||
Point(Float aX, Float aY) : Super(aX, aY) {}
|
||||
Point(const IntPoint& point) : Super(point.x, point.y) {}
|
||||
};
|
||||
|
||||
struct IntSize :
|
||||
|
@ -75,6 +70,15 @@ struct IntSize :
|
|||
IntSize(int32_t aWidth, int32_t aHeight) : Super(aWidth, aHeight) {}
|
||||
};
|
||||
|
||||
struct Size :
|
||||
public BaseSize<Float, Size> {
|
||||
typedef BaseSize<Float, Size> Super;
|
||||
|
||||
Size() : Super() {}
|
||||
Size(Float aWidth, Float aHeight) : Super(aWidth, aHeight) {}
|
||||
explicit Size(const IntSize& size) : Super(size.width, size.height) {}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -55,17 +55,6 @@ struct Margin :
|
|||
: Super(aLeft, aTop, aRight, aBottom) {}
|
||||
};
|
||||
|
||||
struct Rect :
|
||||
public BaseRect<Float, Rect, Point, Size, Margin> {
|
||||
typedef BaseRect<Float, Rect, Point, mozilla::gfx::Size, Margin> Super;
|
||||
|
||||
Rect() : Super() {}
|
||||
Rect(Point aPos, mozilla::gfx::Size aSize) :
|
||||
Super(aPos, aSize) {}
|
||||
Rect(Float _x, Float _y, Float _width, Float _height) :
|
||||
Super(_x, _y, _width, _height) {}
|
||||
};
|
||||
|
||||
struct IntRect :
|
||||
public BaseRect<int32_t, IntRect, IntPoint, IntSize, Margin> {
|
||||
typedef BaseRect<int32_t, IntRect, IntPoint, mozilla::gfx::IntSize, Margin> Super;
|
||||
|
@ -75,6 +64,24 @@ struct IntRect :
|
|||
Super(aPos, aSize) {}
|
||||
IntRect(int32_t _x, int32_t _y, int32_t _width, int32_t _height) :
|
||||
Super(_x, _y, _width, _height) {}
|
||||
|
||||
// Rounding isn't meaningful on an integer rectangle.
|
||||
void Round() {}
|
||||
void RoundIn() {}
|
||||
void RoundOut() {}
|
||||
};
|
||||
|
||||
struct Rect :
|
||||
public BaseRect<Float, Rect, Point, Size, Margin> {
|
||||
typedef BaseRect<Float, Rect, Point, mozilla::gfx::Size, Margin> Super;
|
||||
|
||||
Rect() : Super() {}
|
||||
Rect(Point aPos, mozilla::gfx::Size aSize) :
|
||||
Super(aPos, aSize) {}
|
||||
Rect(Float _x, Float _y, Float _width, Float _height) :
|
||||
Super(_x, _y, _width, _height) {}
|
||||
explicit Rect(const IntRect& rect) :
|
||||
Super(rect.x, rect.y, rect.width, rect.height) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -145,8 +145,9 @@ void
|
|||
BasicPlanarYCbCrImage::SetData(const Data& aData)
|
||||
{
|
||||
// Do some sanity checks to prevent integer overflow
|
||||
if (aData.mYSize.width > 16384 || aData.mYSize.height > 16384) {
|
||||
NS_ERROR("Illegal width or height");
|
||||
if (aData.mYSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
|
||||
aData.mYSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
|
||||
NS_ERROR("Illegal image source width or height");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -159,6 +160,11 @@ BasicPlanarYCbCrImage::SetData(const Data& aData)
|
|||
|
||||
gfxIntSize size(mScaleHint);
|
||||
gfxUtils::GetYCbCrToRGBDestFormatAndSize(aData, format, size);
|
||||
if (size.width > PlanarYCbCrImage::MAX_DIMENSION ||
|
||||
size.height > PlanarYCbCrImage::MAX_DIMENSION) {
|
||||
NS_ERROR("Illegal image dest width or height");
|
||||
return;
|
||||
}
|
||||
|
||||
mStride = gfxASurface::FormatStrideForWidth(format, size.width);
|
||||
mBuffer = AllocateBuffer(size.height * mStride);
|
||||
|
|
|
@ -48,8 +48,8 @@ BlendState ComponentAlphaBlend
|
|||
SrcBlend = One;
|
||||
DestBlend = Inv_Src1_Color;
|
||||
BlendOp = Add;
|
||||
SrcBlendAlpha = Src1_Alpha;
|
||||
DestBlendAlpha = Inv_Src1_Alpha;
|
||||
SrcBlendAlpha = One;
|
||||
DestBlendAlpha = Inv_Src_Alpha;
|
||||
BlendOpAlpha = Add;
|
||||
RenderTargetWriteMask[0] = 0x0F; // All
|
||||
};
|
||||
|
@ -172,7 +172,7 @@ PS_OUTPUT ComponentAlphaShader(const VS_OUTPUT aVertex) : SV_Target
|
|||
|
||||
result.vSrc = tRGB.Sample(LayerTextureSamplerLinear, aVertex.vTexCoords);
|
||||
result.vAlpha = 1.0 - tRGBWhite.Sample(LayerTextureSamplerLinear, aVertex.vTexCoords) + result.vSrc;
|
||||
result.vAlpha.a = result.vAlpha.g;
|
||||
result.vSrc.a = result.vAlpha.g;
|
||||
result.vSrc *= fLayerOpacity;
|
||||
result.vAlpha *= fLayerOpacity;
|
||||
return result;
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -37,14 +37,12 @@
|
|||
|
||||
#include "gfxBlur.h"
|
||||
|
||||
#include "nsMathUtils.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/gfx/Blur.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
gfxAlphaBoxBlur::gfxAlphaBoxBlur()
|
||||
: mBlur(nsnull)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -59,418 +57,63 @@ gfxAlphaBoxBlur::Init(const gfxRect& aRect,
|
|||
const gfxRect* aDirtyRect,
|
||||
const gfxRect* aSkipRect)
|
||||
{
|
||||
mSpreadRadius = aSpreadRadius;
|
||||
mBlurRadius = aBlurRadius;
|
||||
|
||||
gfxRect rect(aRect);
|
||||
rect.Inflate(aBlurRadius + aSpreadRadius);
|
||||
rect.RoundOut();
|
||||
|
||||
Rect rect(aRect.x, aRect.y, aRect.width, aRect.height);
|
||||
IntSize spreadRadius(aSpreadRadius.width, aSpreadRadius.height);
|
||||
IntSize blurRadius(aBlurRadius.width, aBlurRadius.height);
|
||||
nsAutoPtr<Rect> dirtyRect;
|
||||
if (aDirtyRect) {
|
||||
// If we get passed a dirty rect from layout, we can minimize the
|
||||
// shadow size and make painting faster.
|
||||
mHasDirtyRect = true;
|
||||
mDirtyRect = *aDirtyRect;
|
||||
gfxRect requiredBlurArea = mDirtyRect.Intersect(rect);
|
||||
requiredBlurArea.Inflate(aBlurRadius + aSpreadRadius);
|
||||
rect = requiredBlurArea.Intersect(rect);
|
||||
} else {
|
||||
mHasDirtyRect = false;
|
||||
dirtyRect = new Rect(aDirtyRect->x, aDirtyRect->y, aDirtyRect->width, aDirtyRect->height);
|
||||
}
|
||||
|
||||
// Check rect empty after accounting for aDirtyRect, since that may have
|
||||
// make the rectangle empty. BoxBlurVertical and BoxBlurHorizontal require
|
||||
// that we have a nonzero number of rows and columns.
|
||||
if (rect.IsEmpty())
|
||||
return nsnull;
|
||||
|
||||
nsAutoPtr<Rect> skipRect;
|
||||
if (aSkipRect) {
|
||||
// If we get passed a skip rect, we can lower the amount of
|
||||
// blurring/spreading we need to do. We convert it to nsIntRect to avoid
|
||||
// expensive int<->float conversions if we were to use gfxRect instead.
|
||||
gfxRect skipRect = *aSkipRect;
|
||||
skipRect.RoundIn();
|
||||
skipRect.Deflate(aBlurRadius + aSpreadRadius);
|
||||
gfxUtils::GfxRectToIntRect(skipRect, &mSkipRect);
|
||||
nsIntRect shadowIntRect;
|
||||
gfxUtils::GfxRectToIntRect(rect, &shadowIntRect);
|
||||
mSkipRect.IntersectRect(mSkipRect, shadowIntRect);
|
||||
if (mSkipRect.IsEqualInterior(shadowIntRect))
|
||||
return nsnull;
|
||||
|
||||
mSkipRect -= shadowIntRect.TopLeft();
|
||||
} else {
|
||||
mSkipRect = nsIntRect(0, 0, 0, 0);
|
||||
skipRect = new Rect(aSkipRect->x, aSkipRect->y, aSkipRect->width, aSkipRect->height);
|
||||
}
|
||||
|
||||
mBlur = new AlphaBoxBlur(rect, spreadRadius, blurRadius, dirtyRect, skipRect);
|
||||
|
||||
unsigned char* data = mBlur->GetData();
|
||||
if (!data)
|
||||
return nsnull;
|
||||
|
||||
IntSize size = mBlur->GetSize();
|
||||
// Make an alpha-only surface to draw on. We will play with the data after
|
||||
// everything is drawn to create a blur effect.
|
||||
mImageSurface = new gfxImageSurface(gfxIntSize(static_cast<PRInt32>(rect.Width()), static_cast<PRInt32>(rect.Height())),
|
||||
mImageSurface = new gfxImageSurface(data, gfxIntSize(size.width, size.height),
|
||||
mBlur->GetStride(),
|
||||
gfxASurface::ImageFormatA8);
|
||||
if (!mImageSurface || mImageSurface->CairoStatus())
|
||||
if (mImageSurface->CairoStatus())
|
||||
return nsnull;
|
||||
|
||||
IntRect irect = mBlur->GetRect();
|
||||
gfxPoint topleft(irect.TopLeft().x, irect.TopLeft().y);
|
||||
|
||||
// Use a device offset so callers don't need to worry about translating
|
||||
// coordinates, they can draw as if this was part of the destination context
|
||||
// at the coordinates of rect.
|
||||
mImageSurface->SetDeviceOffset(-rect.TopLeft());
|
||||
mImageSurface->SetDeviceOffset(-topleft);
|
||||
|
||||
mContext = new gfxContext(mImageSurface);
|
||||
|
||||
return mContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Box blur involves looking at one pixel, and setting its value to the average
|
||||
* of its neighbouring pixels.
|
||||
* @param aInput The input buffer.
|
||||
* @param aOutput The output buffer.
|
||||
* @param aLeftLobe The number of pixels to blend on the left.
|
||||
* @param aRightLobe The number of pixels to blend on the right.
|
||||
* @param aWidth The number of columns in the buffers.
|
||||
* @param aRows The number of rows in the buffers.
|
||||
* @param aSkipRect An area to skip blurring in.
|
||||
* XXX shouldn't we pass stride in separately here?
|
||||
*/
|
||||
static void
|
||||
BoxBlurHorizontal(unsigned char* aInput,
|
||||
unsigned char* aOutput,
|
||||
PRInt32 aLeftLobe,
|
||||
PRInt32 aRightLobe,
|
||||
PRInt32 aWidth,
|
||||
PRInt32 aRows,
|
||||
const nsIntRect& aSkipRect)
|
||||
{
|
||||
NS_ASSERTION(aWidth > 0, "Can't handle zero width here");
|
||||
|
||||
PRInt32 boxSize = aLeftLobe + aRightLobe + 1;
|
||||
bool skipRectCoversWholeRow = 0 >= aSkipRect.x &&
|
||||
aWidth <= aSkipRect.XMost();
|
||||
if (boxSize == 1) {
|
||||
memcpy(aOutput, aInput, aWidth*aRows);
|
||||
return;
|
||||
}
|
||||
PRUint32 reciprocal = (PRUint64(1) << 32)/boxSize;
|
||||
|
||||
for (PRInt32 y = 0; y < aRows; y++) {
|
||||
// Check whether the skip rect intersects this row. If the skip
|
||||
// rect covers the whole surface in this row, we can avoid
|
||||
// this row entirely (and any others along the skip rect).
|
||||
bool inSkipRectY = y >= aSkipRect.y &&
|
||||
y < aSkipRect.YMost();
|
||||
if (inSkipRectY && skipRectCoversWholeRow) {
|
||||
y = aSkipRect.YMost() - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
PRUint32 alphaSum = 0;
|
||||
for (PRInt32 i = 0; i < boxSize; i++) {
|
||||
PRInt32 pos = i - aLeftLobe;
|
||||
// See assertion above; if aWidth is zero, then we would have no
|
||||
// valid position to clamp to.
|
||||
pos = NS_MAX(pos, 0);
|
||||
pos = NS_MIN(pos, aWidth - 1);
|
||||
alphaSum += aInput[aWidth * y + pos];
|
||||
}
|
||||
for (PRInt32 x = 0; x < aWidth; x++) {
|
||||
// Check whether we are within the skip rect. If so, go
|
||||
// to the next point outside the skip rect.
|
||||
if (inSkipRectY && x >= aSkipRect.x &&
|
||||
x < aSkipRect.XMost()) {
|
||||
x = aSkipRect.XMost();
|
||||
if (x >= aWidth)
|
||||
break;
|
||||
|
||||
// Recalculate the neighbouring alpha values for
|
||||
// our new point on the surface.
|
||||
alphaSum = 0;
|
||||
for (PRInt32 i = 0; i < boxSize; i++) {
|
||||
PRInt32 pos = x + i - aLeftLobe;
|
||||
// See assertion above; if aWidth is zero, then we would have no
|
||||
// valid position to clamp to.
|
||||
pos = NS_MAX(pos, 0);
|
||||
pos = NS_MIN(pos, aWidth - 1);
|
||||
alphaSum += aInput[aWidth * y + pos];
|
||||
}
|
||||
}
|
||||
PRInt32 tmp = x - aLeftLobe;
|
||||
PRInt32 last = NS_MAX(tmp, 0);
|
||||
PRInt32 next = NS_MIN(tmp + boxSize, aWidth - 1);
|
||||
|
||||
aOutput[aWidth * y + x] = (PRUint64(alphaSum)*reciprocal) >> 32;
|
||||
|
||||
alphaSum += aInput[aWidth * y + next] -
|
||||
aInput[aWidth * y + last];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Identical to BoxBlurHorizontal, except it blurs top and bottom instead of
|
||||
* left and right.
|
||||
* XXX shouldn't we pass stride in separately here?
|
||||
*/
|
||||
static void
|
||||
BoxBlurVertical(unsigned char* aInput,
|
||||
unsigned char* aOutput,
|
||||
PRInt32 aTopLobe,
|
||||
PRInt32 aBottomLobe,
|
||||
PRInt32 aWidth,
|
||||
PRInt32 aRows,
|
||||
const nsIntRect& aSkipRect)
|
||||
{
|
||||
NS_ASSERTION(aRows > 0, "Can't handle zero rows here");
|
||||
|
||||
PRInt32 boxSize = aTopLobe + aBottomLobe + 1;
|
||||
bool skipRectCoversWholeColumn = 0 >= aSkipRect.y &&
|
||||
aRows <= aSkipRect.YMost();
|
||||
if (boxSize == 1) {
|
||||
memcpy(aOutput, aInput, aWidth*aRows);
|
||||
return;
|
||||
}
|
||||
PRUint32 reciprocal = (PRUint64(1) << 32)/boxSize;
|
||||
|
||||
for (PRInt32 x = 0; x < aWidth; x++) {
|
||||
bool inSkipRectX = x >= aSkipRect.x &&
|
||||
x < aSkipRect.XMost();
|
||||
if (inSkipRectX && skipRectCoversWholeColumn) {
|
||||
x = aSkipRect.XMost() - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
PRUint32 alphaSum = 0;
|
||||
for (PRInt32 i = 0; i < boxSize; i++) {
|
||||
PRInt32 pos = i - aTopLobe;
|
||||
// See assertion above; if aRows is zero, then we would have no
|
||||
// valid position to clamp to.
|
||||
pos = NS_MAX(pos, 0);
|
||||
pos = NS_MIN(pos, aRows - 1);
|
||||
alphaSum += aInput[aWidth * pos + x];
|
||||
}
|
||||
for (PRInt32 y = 0; y < aRows; y++) {
|
||||
if (inSkipRectX && y >= aSkipRect.y &&
|
||||
y < aSkipRect.YMost()) {
|
||||
y = aSkipRect.YMost();
|
||||
if (y >= aRows)
|
||||
break;
|
||||
|
||||
alphaSum = 0;
|
||||
for (PRInt32 i = 0; i < boxSize; i++) {
|
||||
PRInt32 pos = y + i - aTopLobe;
|
||||
// See assertion above; if aRows is zero, then we would have no
|
||||
// valid position to clamp to.
|
||||
pos = NS_MAX(pos, 0);
|
||||
pos = NS_MIN(pos, aRows - 1);
|
||||
alphaSum += aInput[aWidth * pos + x];
|
||||
}
|
||||
}
|
||||
PRInt32 tmp = y - aTopLobe;
|
||||
PRInt32 last = NS_MAX(tmp, 0);
|
||||
PRInt32 next = NS_MIN(tmp + boxSize, aRows - 1);
|
||||
|
||||
aOutput[aWidth * y + x] = (PRUint64(alphaSum)*reciprocal) >> 32;
|
||||
|
||||
alphaSum += aInput[aWidth * next + x] -
|
||||
aInput[aWidth * last + x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ComputeLobes(PRInt32 aRadius, PRInt32 aLobes[3][2])
|
||||
{
|
||||
PRInt32 major, minor, final;
|
||||
|
||||
/* See http://www.w3.org/TR/SVG/filters.html#feGaussianBlur for
|
||||
* some notes about approximating the Gaussian blur with box-blurs.
|
||||
* The comments below are in the terminology of that page.
|
||||
*/
|
||||
PRInt32 z = aRadius/3;
|
||||
switch (aRadius % 3) {
|
||||
case 0:
|
||||
// aRadius = z*3; choose d = 2*z + 1
|
||||
major = minor = final = z;
|
||||
break;
|
||||
case 1:
|
||||
// aRadius = z*3 + 1
|
||||
// This is a tricky case since there is no value of d which will
|
||||
// yield a radius of exactly aRadius. If d is odd, i.e. d=2*k + 1
|
||||
// for some integer k, then the radius will be 3*k. If d is even,
|
||||
// i.e. d=2*k, then the radius will be 3*k - 1.
|
||||
// So we have to choose values that don't match the standard
|
||||
// algorithm.
|
||||
major = z + 1;
|
||||
minor = final = z;
|
||||
break;
|
||||
case 2:
|
||||
// aRadius = z*3 + 2; choose d = 2*z + 2
|
||||
major = final = z + 1;
|
||||
minor = z;
|
||||
break;
|
||||
default:
|
||||
NS_ERROR("Mathematical impossibility.");
|
||||
major = minor = final = 0;
|
||||
}
|
||||
NS_ASSERTION(major + minor + final == aRadius,
|
||||
"Lobes don't sum to the right length");
|
||||
|
||||
aLobes[0][0] = major;
|
||||
aLobes[0][1] = minor;
|
||||
aLobes[1][0] = minor;
|
||||
aLobes[1][1] = major;
|
||||
aLobes[2][0] = final;
|
||||
aLobes[2][1] = final;
|
||||
}
|
||||
|
||||
static void
|
||||
SpreadHorizontal(unsigned char* aInput,
|
||||
unsigned char* aOutput,
|
||||
PRInt32 aRadius,
|
||||
PRInt32 aWidth,
|
||||
PRInt32 aRows,
|
||||
PRInt32 aStride,
|
||||
const nsIntRect& aSkipRect)
|
||||
{
|
||||
if (aRadius == 0) {
|
||||
memcpy(aOutput, aInput, aStride*aRows);
|
||||
return;
|
||||
}
|
||||
|
||||
bool skipRectCoversWholeRow = 0 >= aSkipRect.x &&
|
||||
aWidth <= aSkipRect.XMost();
|
||||
for (PRInt32 y = 0; y < aRows; y++) {
|
||||
// Check whether the skip rect intersects this row. If the skip
|
||||
// rect covers the whole surface in this row, we can avoid
|
||||
// this row entirely (and any others along the skip rect).
|
||||
bool inSkipRectY = y >= aSkipRect.y &&
|
||||
y < aSkipRect.YMost();
|
||||
if (inSkipRectY && skipRectCoversWholeRow) {
|
||||
y = aSkipRect.YMost() - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (PRInt32 x = 0; x < aWidth; x++) {
|
||||
// Check whether we are within the skip rect. If so, go
|
||||
// to the next point outside the skip rect.
|
||||
if (inSkipRectY && x >= aSkipRect.x &&
|
||||
x < aSkipRect.XMost()) {
|
||||
x = aSkipRect.XMost();
|
||||
if (x >= aWidth)
|
||||
break;
|
||||
}
|
||||
|
||||
PRInt32 sMin = NS_MAX(x - aRadius, 0);
|
||||
PRInt32 sMax = NS_MIN(x + aRadius, aWidth - 1);
|
||||
PRInt32 v = 0;
|
||||
for (PRInt32 s = sMin; s <= sMax; ++s) {
|
||||
v = NS_MAX<PRInt32>(v, aInput[aStride * y + s]);
|
||||
}
|
||||
aOutput[aStride * y + x] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
SpreadVertical(unsigned char* aInput,
|
||||
unsigned char* aOutput,
|
||||
PRInt32 aRadius,
|
||||
PRInt32 aWidth,
|
||||
PRInt32 aRows,
|
||||
PRInt32 aStride,
|
||||
const nsIntRect& aSkipRect)
|
||||
{
|
||||
if (aRadius == 0) {
|
||||
memcpy(aOutput, aInput, aStride*aRows);
|
||||
return;
|
||||
}
|
||||
|
||||
bool skipRectCoversWholeColumn = 0 >= aSkipRect.y &&
|
||||
aRows <= aSkipRect.YMost();
|
||||
for (PRInt32 x = 0; x < aWidth; x++) {
|
||||
bool inSkipRectX = x >= aSkipRect.x &&
|
||||
x < aSkipRect.XMost();
|
||||
if (inSkipRectX && skipRectCoversWholeColumn) {
|
||||
x = aSkipRect.XMost() - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (PRInt32 y = 0; y < aRows; y++) {
|
||||
// Check whether we are within the skip rect. If so, go
|
||||
// to the next point outside the skip rect.
|
||||
if (inSkipRectX && y >= aSkipRect.y &&
|
||||
y < aSkipRect.YMost()) {
|
||||
y = aSkipRect.YMost();
|
||||
if (y >= aRows)
|
||||
break;
|
||||
}
|
||||
|
||||
PRInt32 sMin = NS_MAX(y - aRadius, 0);
|
||||
PRInt32 sMax = NS_MIN(y + aRadius, aRows - 1);
|
||||
PRInt32 v = 0;
|
||||
for (PRInt32 s = sMin; s <= sMax; ++s) {
|
||||
v = NS_MAX<PRInt32>(v, aInput[aStride * s + x]);
|
||||
}
|
||||
aOutput[aStride * y + x] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx, const gfxPoint& offset)
|
||||
{
|
||||
if (!mContext)
|
||||
return;
|
||||
|
||||
unsigned char* boxData = mImageSurface->Data();
|
||||
mBlur->Blur();
|
||||
|
||||
// no need to do all this if not blurring or spreading
|
||||
if (mBlurRadius != gfxIntSize(0,0) || mSpreadRadius != gfxIntSize(0,0)) {
|
||||
nsTArray<unsigned char> tempAlphaDataBuf;
|
||||
PRSize szB = mImageSurface->GetDataSize();
|
||||
if (!tempAlphaDataBuf.SetLength(szB))
|
||||
return; // OOM
|
||||
|
||||
unsigned char* tmpData = tempAlphaDataBuf.Elements();
|
||||
// .SetLength above doesn't initialise the new elements since
|
||||
// they are unsigned chars and so have no default constructor.
|
||||
// So we have to initialise them by hand.
|
||||
memset(tmpData, 0, szB);
|
||||
|
||||
PRInt32 stride = mImageSurface->Stride();
|
||||
PRInt32 rows = mImageSurface->Height();
|
||||
PRInt32 width = mImageSurface->Width();
|
||||
|
||||
if (mSpreadRadius.width > 0 || mSpreadRadius.height > 0) {
|
||||
SpreadHorizontal(boxData, tmpData, mSpreadRadius.width, width, rows, stride, mSkipRect);
|
||||
SpreadVertical(tmpData, boxData, mSpreadRadius.height, width, rows, stride, mSkipRect);
|
||||
}
|
||||
|
||||
if (mBlurRadius.width > 0) {
|
||||
PRInt32 lobes[3][2];
|
||||
ComputeLobes(mBlurRadius.width, lobes);
|
||||
BoxBlurHorizontal(boxData, tmpData, lobes[0][0], lobes[0][1], stride, rows, mSkipRect);
|
||||
BoxBlurHorizontal(tmpData, boxData, lobes[1][0], lobes[1][1], stride, rows, mSkipRect);
|
||||
BoxBlurHorizontal(boxData, tmpData, lobes[2][0], lobes[2][1], stride, rows, mSkipRect);
|
||||
} else {
|
||||
memcpy(tmpData, boxData, stride*rows);
|
||||
}
|
||||
|
||||
if (mBlurRadius.height > 0) {
|
||||
PRInt32 lobes[3][2];
|
||||
ComputeLobes(mBlurRadius.height, lobes);
|
||||
BoxBlurVertical(tmpData, boxData, lobes[0][0], lobes[0][1], stride, rows, mSkipRect);
|
||||
BoxBlurVertical(boxData, tmpData, lobes[1][0], lobes[1][1], stride, rows, mSkipRect);
|
||||
BoxBlurVertical(tmpData, boxData, lobes[2][0], lobes[2][1], stride, rows, mSkipRect);
|
||||
} else {
|
||||
memcpy(boxData, tmpData, stride*rows);
|
||||
}
|
||||
}
|
||||
Rect* dirtyrect = mBlur->GetDirtyRect();
|
||||
|
||||
// Avoid a semi-expensive clip operation if we can, otherwise
|
||||
// clip to the dirty rect
|
||||
if (mHasDirtyRect) {
|
||||
if (dirtyrect) {
|
||||
aDestinationCtx->Save();
|
||||
aDestinationCtx->NewPath();
|
||||
aDestinationCtx->Rectangle(mDirtyRect);
|
||||
gfxRect dirty(dirtyrect->x, dirtyrect->y, dirtyrect->width, dirtyrect->height);
|
||||
aDestinationCtx->Rectangle(dirty);
|
||||
aDestinationCtx->Clip();
|
||||
aDestinationCtx->Mask(mImageSurface, offset);
|
||||
aDestinationCtx->Restore();
|
||||
|
@ -479,23 +122,9 @@ gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx, const gfxPoint& offset)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the box blur size (which we're calling the blur radius) from
|
||||
* the standard deviation.
|
||||
*
|
||||
* Much of this, the 3 * sqrt(2 * pi) / 4, is the known value for
|
||||
* approximating a Gaussian using box blurs. This yields quite a good
|
||||
* approximation for a Gaussian. Then we multiply this by 1.5 since our
|
||||
* code wants the radius of the entire triple-box-blur kernel instead of
|
||||
* the diameter of an individual box blur. For more details, see:
|
||||
* http://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement
|
||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=590039#c19
|
||||
*/
|
||||
static const gfxFloat GAUSSIAN_SCALE_FACTOR = (3 * sqrt(2 * M_PI) / 4) * 1.5;
|
||||
|
||||
gfxIntSize gfxAlphaBoxBlur::CalculateBlurRadius(const gfxPoint& aStd)
|
||||
{
|
||||
return gfxIntSize(
|
||||
static_cast<PRInt32>(floor(aStd.x * GAUSSIAN_SCALE_FACTOR + 0.5)),
|
||||
static_cast<PRInt32>(floor(aStd.y * GAUSSIAN_SCALE_FACTOR + 0.5)));
|
||||
Point std(aStd.x, aStd.y);
|
||||
IntSize size = AlphaBoxBlur::CalculateBlurRadius(std);
|
||||
return gfxIntSize(size.width, size.height);
|
||||
}
|
||||
|
|
|
@ -42,7 +42,12 @@
|
|||
#include "gfxImageSurface.h"
|
||||
#include "gfxTypes.h"
|
||||
#include "gfxUtils.h"
|
||||
#include "nsRect.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
class AlphaBoxBlur;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of a triple box blur approximation of a Gaussian blur.
|
||||
|
@ -122,15 +127,6 @@ public:
|
|||
static gfxIntSize CalculateBlurRadius(const gfxPoint& aStandardDeviation);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The spread radius, in pixels.
|
||||
*/
|
||||
gfxIntSize mSpreadRadius;
|
||||
/**
|
||||
* The blur radius, in pixels.
|
||||
*/
|
||||
gfxIntSize mBlurRadius;
|
||||
|
||||
/**
|
||||
* The context of the temporary alpha surface.
|
||||
*/
|
||||
|
@ -141,18 +137,10 @@ protected:
|
|||
*/
|
||||
nsRefPtr<gfxImageSurface> mImageSurface;
|
||||
|
||||
/**
|
||||
* A copy of the dirty rect passed to Init(). This will only be valid if
|
||||
* mHasDirtyRect is TRUE.
|
||||
*/
|
||||
gfxRect mDirtyRect;
|
||||
/**
|
||||
* A rect indicating the area where blurring is unnecessary, and the blur
|
||||
* algorithm should skip over it.
|
||||
*/
|
||||
nsIntRect mSkipRect;
|
||||
|
||||
bool mHasDirtyRect;
|
||||
/**
|
||||
* The object that actually does the blurring for us.
|
||||
*/
|
||||
nsAutoPtr<mozilla::gfx::AlphaBoxBlur> mBlur;
|
||||
};
|
||||
|
||||
#endif /* GFX_BLUR_H */
|
||||
|
|
|
@ -55,52 +55,6 @@ gfxRect::WithinEpsilonOfIntegerPixels(gfxFloat aEpsilon) const
|
|||
WithinEpsilonOfInteger(height, aEpsilon));
|
||||
}
|
||||
|
||||
void
|
||||
gfxRect::Round()
|
||||
{
|
||||
// Note that don't use NS_round here. See the comment for this method in gfxRect.h
|
||||
gfxFloat x0 = floor(X() + 0.5);
|
||||
gfxFloat y0 = floor(Y() + 0.5);
|
||||
gfxFloat x1 = floor(XMost() + 0.5);
|
||||
gfxFloat y1 = floor(YMost() + 0.5);
|
||||
|
||||
x = x0;
|
||||
y = y0;
|
||||
|
||||
width = x1 - x0;
|
||||
height = y1 - y0;
|
||||
}
|
||||
|
||||
void
|
||||
gfxRect::RoundIn()
|
||||
{
|
||||
gfxFloat x0 = ceil(X());
|
||||
gfxFloat y0 = ceil(Y());
|
||||
gfxFloat x1 = floor(XMost());
|
||||
gfxFloat y1 = floor(YMost());
|
||||
|
||||
x = x0;
|
||||
y = y0;
|
||||
|
||||
width = x1 - x0;
|
||||
height = y1 - y0;
|
||||
}
|
||||
|
||||
void
|
||||
gfxRect::RoundOut()
|
||||
{
|
||||
gfxFloat x0 = floor(X());
|
||||
gfxFloat y0 = floor(Y());
|
||||
gfxFloat x1 = ceil(XMost());
|
||||
gfxFloat y1 = ceil(YMost());
|
||||
|
||||
x = x0;
|
||||
y = y0;
|
||||
|
||||
width = x1 - x0;
|
||||
height = y1 - y0;
|
||||
}
|
||||
|
||||
/* Clamp r to CAIRO_COORD_MIN .. CAIRO_COORD_MAX
|
||||
* these are to be device coordinates.
|
||||
*
|
||||
|
|
|
@ -107,27 +107,6 @@ struct THEBES_API gfxRect :
|
|||
*/
|
||||
bool WithinEpsilonOfIntegerPixels(gfxFloat aEpsilon) const;
|
||||
|
||||
// Round the rectangle edges to integer coordinates, such that the rounded
|
||||
// rectangle has the same set of pixel centers as the original rectangle.
|
||||
// Edges at offset 0.5 round up.
|
||||
// Suitable for most places where integral device coordinates
|
||||
// are needed, but note that any translation should be applied first to
|
||||
// avoid pixel rounding errors.
|
||||
// Note that this is *not* rounding to nearest integer if the values are negative.
|
||||
// They are always rounding as floor(n + 0.5).
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14
|
||||
// If you need similar method which is using NS_round(), you should create
|
||||
// new |RoundAwayFromZero()| method.
|
||||
void Round();
|
||||
|
||||
// Snap the rectangle edges to integer coordinates, such that the
|
||||
// original rectangle contains the resulting rectangle.
|
||||
void RoundIn();
|
||||
|
||||
// Snap the rectangle edges to integer coordinates, such that the
|
||||
// resulting rectangle contains the original rectangle.
|
||||
void RoundOut();
|
||||
|
||||
gfxPoint AtCorner(mozilla::css::Corner corner) const {
|
||||
switch (corner) {
|
||||
case NS_CORNER_TOP_LEFT: return TopLeft();
|
||||
|
|
|
@ -441,15 +441,12 @@ nsBMPEncoder::ConvertHostARGBRow(const PRUint8* aSrc, PRUint8* aDest,
|
|||
PRUint8 *pixelOut = &aDest[x * BytesPerPixel(mBMPInfoHeader.bpp)];
|
||||
|
||||
PRUint8 alpha = (pixelIn & 0xff000000) >> 24;
|
||||
if (alpha == 0) {
|
||||
pixelOut[0] = pixelOut[1] = pixelOut[2] = 0;
|
||||
} else {
|
||||
pixelOut[0] = (((pixelIn & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
|
||||
pixelOut[1] = (((pixelIn & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
|
||||
pixelOut[2] = (((pixelIn & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha;
|
||||
if(mBMPInfoHeader.bpp == 32) {
|
||||
pixelOut[3] = alpha;
|
||||
}
|
||||
pixelOut[0] = (((pixelIn & 0xff0000) >> 16));
|
||||
pixelOut[1] = (((pixelIn & 0x00ff00) >> 8));
|
||||
pixelOut[2] = (((pixelIn & 0x0000ff) >> 0));
|
||||
|
||||
if (mBMPInfoHeader.bpp == 32) {
|
||||
pixelOut[3] = alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -365,14 +365,9 @@ nsJPEGEncoder::ConvertHostARGBRow(const PRUint8* aSrc, PRUint8* aDest,
|
|||
const PRUint32& pixelIn = ((const PRUint32*)(aSrc))[x];
|
||||
PRUint8 *pixelOut = &aDest[x * 3];
|
||||
|
||||
PRUint8 alpha = (pixelIn & 0xff000000) >> 24;
|
||||
if (alpha == 0) {
|
||||
pixelOut[0] = pixelOut[1] = pixelOut[2] = 0;
|
||||
} else {
|
||||
pixelOut[0] = (((pixelIn & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
|
||||
pixelOut[1] = (((pixelIn & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
|
||||
pixelOut[2] = (((pixelIn & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha;
|
||||
}
|
||||
pixelOut[0] = (((pixelIn & 0xff0000) >> 16));
|
||||
pixelOut[1] = (((pixelIn & 0x00ff00) >> 8));
|
||||
pixelOut[2] = (((pixelIn & 0x0000ff) >> 0));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,10 +36,16 @@ function failTest ()
|
|||
}
|
||||
|
||||
ok(false, "timing out after " + currentTest.timeout + "ms. "
|
||||
+ "Animated image still doesn't look correct, " + "after call #"
|
||||
+ currentTest.onStopFrameCounter + " to onStopFrame");
|
||||
+ "Animated image still doesn't look correct, after poll #"
|
||||
+ currentTest.pollCounter);
|
||||
currentTest.wereFailures = true;
|
||||
|
||||
if (currentTest.currentSnapshotDataURI) {
|
||||
currentTest.outputDebugInfo("Snapshot #" + currentTest.pollCounter,
|
||||
"snapNum" + currentTest.pollCounter,
|
||||
currentTest.currentSnapshotDataURI);
|
||||
}
|
||||
|
||||
currentTest.enableDisplay(document.getElementById(currentTest.debugElementId));
|
||||
|
||||
currentTest.cleanUpAndFinish();
|
||||
|
@ -96,7 +102,7 @@ function AnimationTest(pollFreq, timeout, referenceElementId, imageElementId,
|
|||
this.srcAttr = srcAttr;
|
||||
this.debugElementId = debugElementId;
|
||||
this.referenceSnapshot = ""; // value will be set in takeReferenceSnapshot()
|
||||
this.onStopFrameCounter = 0;
|
||||
this.pollCounter = 0;
|
||||
this.isTestFinished = false;
|
||||
this.numRefsTaken = 0;
|
||||
this.blankWaitTime = 0;
|
||||
|
@ -124,6 +130,7 @@ AnimationTest.prototype.outputDebugInfo = function(message, id, dataUri)
|
|||
debugElement.appendChild(newDataUriElement);
|
||||
var brElement = document.createElement("br");
|
||||
debugElement.appendChild(brElement);
|
||||
todo(false, "Debug (" + id + "): " + message + " " + dataUri);
|
||||
};
|
||||
|
||||
AnimationTest.prototype.isFinished = function()
|
||||
|
@ -199,14 +206,12 @@ AnimationTest.prototype.setupPolledImage = function ()
|
|||
var currentSnapshot = snapshotWindow(window, false);
|
||||
var result = compareSnapshots(currentSnapshot, this.referenceSnapshot, true);
|
||||
|
||||
var dataString = "Snapshot #" + this.onStopFrameCounter;
|
||||
this.outputDebugInfo(dataString, 'snap' + this.onStopFrameCounter,
|
||||
currentSnapshot.toDataURL());
|
||||
this.currentSnapshotDataURI = currentSnapshot.toDataURL();
|
||||
|
||||
if (result[0]) {
|
||||
// SUCCESS!
|
||||
ok(true, "Animated image looks correct, " + "at call #"
|
||||
+ this.onStopFrameCounter + " to onStopFrame");
|
||||
ok(true, "Animated image looks correct, at poll #"
|
||||
+ this.pollCounter);
|
||||
|
||||
this.cleanUpAndFinish();
|
||||
}
|
||||
|
@ -226,7 +231,7 @@ AnimationTest.prototype.checkImage = function ()
|
|||
return;
|
||||
}
|
||||
|
||||
this.onStopFrameCounter++;
|
||||
this.pollCounter++;
|
||||
|
||||
// We need this for some tests, because we need to force the
|
||||
// test image to be visible.
|
||||
|
@ -237,14 +242,12 @@ AnimationTest.prototype.checkImage = function ()
|
|||
var currentSnapshot = snapshotWindow(window, false);
|
||||
var result = compareSnapshots(currentSnapshot, this.referenceSnapshot, true);
|
||||
|
||||
var dataString = "Snapshot #" + this.onStopFrameCounter;
|
||||
this.outputDebugInfo(dataString, 'snap' + this.onStopFrameCounter,
|
||||
currentSnapshot.toDataURL());
|
||||
this.currentSnapshotDataURI = currentSnapshot.toDataURL();
|
||||
|
||||
if (result[0]) {
|
||||
// SUCCESS!
|
||||
ok(true, "Animated image looks correct, " + "at call #"
|
||||
+ this.onStopFrameCounter + " to onStopFrame");
|
||||
ok(true, "Animated image looks correct, at poll #"
|
||||
+ this.pollCounter);
|
||||
|
||||
this.cleanUpAndFinish();
|
||||
}
|
||||
|
|
Двоичные данные
image/test/unit/image1png16x16.jpg
Двоичные данные
image/test/unit/image1png16x16.jpg
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 1.1 KiB После Ширина: | Высота: | Размер: 1.1 KiB |
Двоичные данные
image/test/unit/image1png64x64.jpg
Двоичные данные
image/test/unit/image1png64x64.jpg
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 4.4 KiB После Ширина: | Высота: | Размер: 4.4 KiB |
|
@ -171,7 +171,7 @@ var encodedBytes = streamToArray(istream);
|
|||
var refName = "image1png16x16.jpg";
|
||||
var refFile = do_get_file(refName);
|
||||
istream = getFileInputStream(refFile);
|
||||
do_check_eq(istream.available(), 1081);
|
||||
do_check_eq(istream.available(), 1078);
|
||||
var referenceBytes = streamToArray(istream);
|
||||
|
||||
// compare the encoder's output to the reference file.
|
||||
|
@ -190,7 +190,7 @@ encodedBytes = streamToArray(istream);
|
|||
refName = "image1png64x64.jpg";
|
||||
refFile = do_get_file(refName);
|
||||
istream = getFileInputStream(refFile);
|
||||
do_check_eq(istream.available(), 4493);
|
||||
do_check_eq(istream.available(), 4503);
|
||||
referenceBytes = streamToArray(istream);
|
||||
|
||||
// compare the encoder's output to the reference file.
|
||||
|
|
|
@ -6930,12 +6930,20 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot)
|
|||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
case TOK_STAR:
|
||||
if (tc->inStrictMode()) {
|
||||
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
pn = qualifiedIdentifier();
|
||||
if (!pn)
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
case TOK_AT:
|
||||
if (tc->inStrictMode()) {
|
||||
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
pn = attributeIdentifier();
|
||||
if (!pn)
|
||||
return NULL;
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/
|
||||
*/
|
||||
|
||||
function checkSyntaxError(code) {
|
||||
var error;
|
||||
try {
|
||||
eval(code);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
assertEq(error.name, 'SyntaxError');
|
||||
}
|
||||
|
||||
checkSyntaxError('"use strict"; *');
|
||||
checkSyntaxError('"use strict"; @7');
|
|
@ -1400,7 +1400,16 @@ JSObject::makeDenseArraySlow(JSContext *cx)
|
|||
if (slots[i].isMagic(JS_ARRAY_HOLE))
|
||||
continue;
|
||||
|
||||
setSlot(next, slots[i]);
|
||||
/*
|
||||
* No barrier is needed here because the set of reachable objects before
|
||||
* and after slowification is the same. During slowification, the
|
||||
* autoArray rooter guarantees that all slots will be marked.
|
||||
*
|
||||
* It's important that we avoid a barrier here because the fixed slots
|
||||
* of a dense array can be garbage; a write barrier after the switch to
|
||||
* a slow array could cause a crash.
|
||||
*/
|
||||
initSlotUnchecked(next, slots[i]);
|
||||
|
||||
if (!addDataProperty(cx, id, next, JSPROP_ENUMERATE)) {
|
||||
setMap(oldMap);
|
||||
|
|
|
@ -342,7 +342,8 @@ regexp_finalize(JSContext *cx, JSObject *obj)
|
|||
static void
|
||||
regexp_trace(JSTracer *trc, JSObject *obj)
|
||||
{
|
||||
obj->asRegExp()->purge(trc->context);
|
||||
if (IS_GC_MARKING_TRACER(trc))
|
||||
obj->asRegExp()->purge(trc->context);
|
||||
}
|
||||
|
||||
Class js::RegExpClass = {
|
||||
|
|
|
@ -4547,7 +4547,8 @@ nsCSSFrameConstructor::ResolveStyleContext(nsStyleContext* aParentStyleContext,
|
|||
nsFrameConstructorState* aState)
|
||||
{
|
||||
nsStyleSet *styleSet = mPresShell->StyleSet();
|
||||
|
||||
aContent->OwnerDoc()->FlushPendingLinkUpdates();
|
||||
|
||||
if (aContent->IsElement()) {
|
||||
if (aState) {
|
||||
return styleSet->ResolveStyleFor(aContent->AsElement(),
|
||||
|
|
|
@ -1111,6 +1111,7 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext,
|
|||
nsIContent* content = localContent ? localContent : aParentContent;
|
||||
|
||||
if (content && content->IsElement()) {
|
||||
content->OwnerDoc()->FlushPendingLinkUpdates();
|
||||
RestyleTracker::RestyleData restyleData;
|
||||
if (aRestyleTracker.GetRestyleData(content->AsElement(), &restyleData)) {
|
||||
if (NS_UpdateHint(aMinChange, restyleData.mChangeHint)) {
|
||||
|
|
|
@ -6283,13 +6283,13 @@ IsFullScreenAndRestrictedKeyEvent(nsIContent* aTarget, const nsEvent* aEvent)
|
|||
|
||||
// Bail out if the event is not a key event, or the target's document is
|
||||
// not in DOM full screen mode, or full-screen key input is not restricted.
|
||||
nsIDocument *doc;
|
||||
nsIDocument *root = nsnull;
|
||||
if (!aTarget ||
|
||||
(aEvent->message != NS_KEY_DOWN &&
|
||||
aEvent->message != NS_KEY_UP &&
|
||||
aEvent->message != NS_KEY_PRESS) ||
|
||||
!(doc = aTarget->OwnerDoc()) ||
|
||||
!doc->IsFullScreenDoc() ||
|
||||
!(root = nsContentUtils::GetRootDocument(aTarget->OwnerDoc())) ||
|
||||
!root->IsFullScreenDoc() ||
|
||||
!nsContentUtils::IsFullScreenKeyInputRestricted()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -6368,9 +6368,10 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsIView *aView,
|
|||
case NS_KEY_UP: {
|
||||
nsIDocument *doc = mCurrentEventContent ?
|
||||
mCurrentEventContent->OwnerDoc() : nsnull;
|
||||
if (doc &&
|
||||
doc->IsFullScreenDoc() &&
|
||||
static_cast<const nsKeyEvent*>(aEvent)->keyCode == NS_VK_ESCAPE) {
|
||||
nsIDocument* root = nsnull;
|
||||
if (static_cast<const nsKeyEvent*>(aEvent)->keyCode == NS_VK_ESCAPE &&
|
||||
(root = nsContentUtils::GetRootDocument(doc)) &&
|
||||
root->IsFullScreenDoc()) {
|
||||
// Prevent default action on ESC key press when exiting
|
||||
// DOM full-screen mode. This prevents the browser ESC key
|
||||
// handler from stopping all loads in the document, which
|
||||
|
@ -6382,7 +6383,7 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsIView *aView,
|
|||
// ESC key released while in DOM full-screen mode.
|
||||
// Exit full-screen mode.
|
||||
NS_DispatchToCurrentThread(
|
||||
NS_NewRunnableMethod(mCurrentEventContent->OwnerDoc(),
|
||||
NS_NewRunnableMethod(root,
|
||||
&nsIDocument::CancelFullScreen));
|
||||
}
|
||||
} else if (IsFullScreenAndRestrictedKeyEvent(mCurrentEventContent, aEvent)) {
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
<!DOCTYPE html>
|
||||
<!-- This test checks to be sure we can render SVG-as-an-image
|
||||
from a MozBlobBuilder-generated 'moz-filedata' URI. -->
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<script>
|
||||
function go() {
|
||||
// Generate a moz-filedata URL encoding of an SVG document
|
||||
var filedataURL = generateMozFiledataURL();
|
||||
|
||||
// Tell our img element to render the URL
|
||||
var img = document.getElementsByTagName("img")[0]
|
||||
img.src = filedataURL;
|
||||
|
||||
// Once our img loads, take reftest snapshot.
|
||||
img.addEventListener("load", function() {
|
||||
document.documentElement.removeAttribute("class");
|
||||
});
|
||||
}
|
||||
|
||||
// Helper function -- returns a moz-filedata URL representing a
|
||||
// 100x100 fully-lime SVG document.
|
||||
function generateMozFiledataURL() {
|
||||
var blobBuilder = new self.MozBlobBuilder;
|
||||
var svg =
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">' +
|
||||
'<rect height="100%" width="100%" fill="lime"/>' +
|
||||
'</svg>';
|
||||
blobBuilder.append(svg);
|
||||
return self.URL.createObjectURL(blobBuilder.getBlob("image/svg+xml"));
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="go()">
|
||||
<img src="">
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,46 @@
|
|||
<!DOCTYPE html>
|
||||
<!-- This test checks to be sure we allow MozBlobBuilder-generated
|
||||
'moz-filedata' URIs *inside of* SVG-as-an-image. -->
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<script>
|
||||
function go() {
|
||||
// Generate a moz-filedata URL encoding of an SVG document
|
||||
var filedataURL = generateMozFiledataURL();
|
||||
|
||||
// Now generate a data URI, containing our moz-filedata URI
|
||||
var outerSVG =
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" ' +
|
||||
'xmlns:xlink="http://www.w3.org/1999/xlink" ' +
|
||||
'width="100" height="100">' +
|
||||
'<image height="100" width="100" ' +
|
||||
'xlink:href="' + filedataURL + '"/>' +
|
||||
'</svg>';
|
||||
|
||||
// Tell our img element to render the URL
|
||||
var img = document.getElementsByTagName("img")[0]
|
||||
img.src = "data:image/svg+xml," + outerSVG;
|
||||
|
||||
// Once our img loads, take reftest snapshot.
|
||||
img.addEventListener("load", function() {
|
||||
document.documentElement.removeAttribute("class");
|
||||
});
|
||||
}
|
||||
|
||||
// Helper function -- returns a moz-filedata URL representing a
|
||||
// 100x100 fully-lime SVG document.
|
||||
function generateMozFiledataURL() {
|
||||
var blobBuilder = new self.MozBlobBuilder;
|
||||
var svg =
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">' +
|
||||
'<rect height="100%" width="100%" fill="lime"/>' +
|
||||
'</svg>';
|
||||
blobBuilder.append(svg);
|
||||
return self.URL.createObjectURL(blobBuilder.getBlob("image/svg+xml"));
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="go()">
|
||||
<img src="">
|
||||
</body>
|
||||
</html>
|
|
@ -55,6 +55,8 @@ fails == canvas-drawImage-slice-1b.html lime100x100-ref.html # XXX all edges fuz
|
|||
random == img-and-image-1.html img-and-image-1-ref.svg # bug 645267
|
||||
|
||||
# More complex <img> tests
|
||||
== img-blobBuilder-1.html lime100x100-ref.html
|
||||
== img-blobBuilder-2.html lime100x100-ref.html
|
||||
== img-content-outside-viewBox-1.html img-content-outside-viewBox-1-ref.html
|
||||
== img-dyn-1.html img-dyn-1-ref.html
|
||||
== img-foreignObject-1.html lime100x100-ref.html
|
||||
|
@ -120,11 +122,11 @@ random == img-and-image-1.html img-and-image-1-ref.svg # bug 645267
|
|||
# tests for external resources vs. data URIs in SVG as an image
|
||||
== svg-image-datauri-1.html lime100x100.svg
|
||||
HTTP == svg-image-datauri-1.html lime100x100.svg
|
||||
fails-if(Android) == svg-image-external-1.html lime100x100.svg
|
||||
== svg-image-external-1.html blue100x100.svg
|
||||
HTTP == svg-image-external-1.html blue100x100.svg
|
||||
== svg-stylesheet-datauri-1.html lime100x100.svg
|
||||
HTTP == svg-stylesheet-datauri-1.html lime100x100.svg
|
||||
random == svg-stylesheet-external-1.html lime100x100.svg # see bug 629885 comment 9
|
||||
== svg-stylesheet-external-1.html blue100x100.svg
|
||||
HTTP == svg-stylesheet-external-1.html blue100x100.svg
|
||||
|
||||
# test that :visited status is ignored in image documents
|
||||
|
|
|
@ -1249,8 +1249,6 @@ nsCSSRuleProcessor::GetWindowsThemeIdentifier()
|
|||
nsEventStates
|
||||
nsCSSRuleProcessor::GetContentState(Element* aElement)
|
||||
{
|
||||
// FIXME: RequestLinkStateUpdate is a hack; see bug 660959.
|
||||
aElement->RequestLinkStateUpdate();
|
||||
nsEventStates state = aElement->State();
|
||||
|
||||
// If we are not supposed to mark visited links as such, be sure to
|
||||
|
@ -1271,8 +1269,6 @@ nsCSSRuleProcessor::GetContentState(Element* aElement)
|
|||
bool
|
||||
nsCSSRuleProcessor::IsLink(Element* aElement)
|
||||
{
|
||||
// FIXME: RequestLinkStateUpdate is a hack; see bug 660959.
|
||||
aElement->RequestLinkStateUpdate();
|
||||
nsEventStates state = aElement->State();
|
||||
return state.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED);
|
||||
}
|
||||
|
|
|
@ -447,6 +447,7 @@ nsComputedDOMStyle::GetPropertyCSSValue(const nsAString& aPropertyName,
|
|||
|
||||
nsCOMPtr<nsIDocument> document = do_QueryReferent(mDocumentWeak);
|
||||
NS_ENSURE_TRUE(document, NS_ERROR_NOT_AVAILABLE);
|
||||
document->FlushPendingLinkUpdates();
|
||||
|
||||
nsCSSProperty prop = nsCSSProps::LookupProperty(aPropertyName);
|
||||
|
||||
|
|
|
@ -175,37 +175,6 @@ nsSVGMarkerFrame::PaintMark(nsSVGRenderState *aContext,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsRect
|
||||
nsSVGMarkerFrame::RegionMark(nsSVGPathGeometryFrame *aMarkedFrame,
|
||||
const nsSVGMark *aMark, float aStrokeWidth)
|
||||
{
|
||||
// If the flag is set when we get here, it means this marker frame
|
||||
// has already been used in calculating the current mark region, and
|
||||
// the document has a marker reference loop.
|
||||
if (mInUse)
|
||||
return nsRect(0,0,0,0);
|
||||
|
||||
AutoMarkerReferencer markerRef(this, aMarkedFrame);
|
||||
|
||||
mStrokeWidth = aStrokeWidth;
|
||||
mX = aMark->x;
|
||||
mY = aMark->y;
|
||||
mAutoAngle = aMark->angle;
|
||||
|
||||
// Force children to update their covered region
|
||||
for (nsIFrame* kid = mFrames.FirstChild();
|
||||
kid;
|
||||
kid = kid->GetNextSibling()) {
|
||||
nsISVGChildFrame* child = do_QueryFrame(kid);
|
||||
if (child)
|
||||
child->UpdateCoveredRegion();
|
||||
}
|
||||
|
||||
// Now get the combined covered region
|
||||
return nsSVGUtils::GetCoveredRegion(mFrames);
|
||||
}
|
||||
|
||||
gfxRect
|
||||
nsSVGMarkerFrame::GetMarkBBoxContribution(const gfxMatrix &aToBBoxUserspace,
|
||||
PRUint32 aFlags,
|
||||
|
|
|
@ -92,9 +92,6 @@ public:
|
|||
nsSVGMark *aMark,
|
||||
float aStrokeWidth);
|
||||
|
||||
nsRect RegionMark(nsSVGPathGeometryFrame *aMarkedFrame,
|
||||
const nsSVGMark *aMark, float aStrokeWidth);
|
||||
|
||||
gfxRect GetMarkBBoxContribution(const gfxMatrix &aToBBoxUserspace,
|
||||
PRUint32 aFlags,
|
||||
nsSVGPathGeometryFrame *aMarkedFrame,
|
||||
|
|
|
@ -16,6 +16,7 @@ XUL <resizer> tests
|
|||
<!-- test code goes here -->
|
||||
<script type="application/javascript"><![CDATA[
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.ignoreAllUncaughtExceptions();
|
||||
|
||||
function openPopup()
|
||||
{
|
||||
|
|
|
@ -168,7 +168,7 @@ Tester.prototype = {
|
|||
"\tTodo: " + todoCount + "\n");
|
||||
} else {
|
||||
this.dumper.dump("TEST-UNEXPECTED-FAIL | (browser-test.js) | " +
|
||||
"No tests to run. Did you pass an invalid --test-path?");
|
||||
"No tests to run. Did you pass an invalid --test-path?\n");
|
||||
}
|
||||
|
||||
this.dumper.dump("\n*** End BrowserChrome Test Results ***\n");
|
||||
|
@ -188,7 +188,7 @@ Tester.prototype = {
|
|||
if (this.currentTest)
|
||||
this.currentTest.addResult(new testMessage(msg));
|
||||
else
|
||||
this.dumper.dump("TEST-INFO | (browser-test.js) | " + msg);
|
||||
this.dumper.dump("TEST-INFO | (browser-test.js) | " + msg.replace(/\n$/, "") + "\n");
|
||||
} catch (ex) {
|
||||
// Swallow exception so we don't lead to another error being reported,
|
||||
// throwing us into an infinite loop
|
||||
|
@ -471,6 +471,10 @@ function testScope(aTester, aTest) {
|
|||
self.SimpleTest.expectUncaughtException();
|
||||
};
|
||||
|
||||
this.ignoreAllUncaughtExceptions = function test_ignoreAllUncaughtExceptions() {
|
||||
self.SimpleTest.ignoreAllUncaughtExceptions();
|
||||
};
|
||||
|
||||
this.finish = function test_finish() {
|
||||
self.__done = true;
|
||||
if (self.SimpleTest._expectingUncaughtException) {
|
||||
|
|
|
@ -15,24 +15,35 @@
|
|||
**/
|
||||
|
||||
var SimpleTest = { };
|
||||
|
||||
var parentRunner = null;
|
||||
if (parent) {
|
||||
parentRunner = parent.TestRunner;
|
||||
if (!parentRunner && parent.wrappedJSObject) {
|
||||
parentRunner = parent.wrappedJSObject.TestRunner;
|
||||
var isPrimaryTestWindow = !!parent.TestRunner;
|
||||
|
||||
// Finds the TestRunner for this test run and the SpecialPowers object (in
|
||||
// case it is not defined) from a parent/opener window.
|
||||
//
|
||||
// Finding the SpecialPowers object is needed when we have ChromePowers in
|
||||
// harness.xul and we need SpecialPowers in the iframe, and also for tests
|
||||
// like test_focus.xul where we open a window which opens another window which
|
||||
// includes SimpleTest.js.
|
||||
(function() {
|
||||
function ancestor(w) {
|
||||
return w.parent != w ? w.parent : w.opener;
|
||||
}
|
||||
|
||||
// This is the case where we have ChromePowers in harness.xul and we need it in the iframe
|
||||
if (window.SpecialPowers == undefined && parent.SpecialPowers !== undefined) {
|
||||
window.SpecialPowers = parent.SpecialPowers;
|
||||
var w = ancestor(window);
|
||||
while (w && (!parentRunner || !window.SpecialPowers)) {
|
||||
if (!parentRunner) {
|
||||
parentRunner = w.TestRunner;
|
||||
if (!parentRunner && w.wrappedJSObject) {
|
||||
parentRunner = w.wrappedJSObject.TestRunner;
|
||||
}
|
||||
}
|
||||
if (!window.SpecialPowers) {
|
||||
window.SpecialPowers = w.SpecialPowers;
|
||||
}
|
||||
w = ancestor(w);
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround test_focus.xul where we open a window which opens another window which includes SimpleTest.js
|
||||
if (window.SpecialPowers == undefined && window.opener && window.opener.SpecialPowers !== undefined) {
|
||||
window.SpecialPowers = window.opener.SpecialPowers;
|
||||
}
|
||||
})();
|
||||
|
||||
/* Helper functions pulled out of various MochiKit modules */
|
||||
if (typeof(repr) == 'undefined') {
|
||||
|
@ -703,12 +714,22 @@ SimpleTest.expectUncaughtException = function () {
|
|||
SimpleTest._expectingUncaughtException = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates to the test framework that all of the uncaught exceptions
|
||||
* during the test are known problems that should be fixed in the future,
|
||||
* but which should not cause the test to fail currently.
|
||||
*/
|
||||
SimpleTest.ignoreAllUncaughtExceptions = function () {
|
||||
SimpleTest._ignoringAllUncaughtExceptions = true;
|
||||
};
|
||||
|
||||
addLoadEvent(function() {
|
||||
if (SimpleTest._stopOnLoad) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
});
|
||||
if (isPrimaryTestWindow) {
|
||||
addLoadEvent(function() {
|
||||
if (SimpleTest._stopOnLoad) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --------------- Test.Builder/Test.More isDeeply() -----------------
|
||||
|
||||
|
@ -924,19 +945,18 @@ window.onerror = function simpletestOnerror(errorMsg, url, lineNumber) {
|
|||
|
||||
// Log the message.
|
||||
// XXX Chrome mochitests sometimes trigger this window.onerror handler,
|
||||
// but there are a number of uncaught JS exceptions from those tests
|
||||
// currently, so we can't log them as errors just yet. For now, when
|
||||
// not in a plain mochitest, just dump it so that the error is visible but
|
||||
// doesn't cause a test failure. See bug 652494.
|
||||
// but there are a number of uncaught JS exceptions from those tests.
|
||||
// For now, for tests that self identify as having unintentional uncaught
|
||||
// exceptions, just dump it so that the error is visible but doesn't cause
|
||||
// a test failure. See bug 652494.
|
||||
var message = "An error occurred: " + errorMsg + " at " + url + ":" + lineNumber;
|
||||
var href = SpecialPowers.getPrivilegedProps(window, 'location.href');
|
||||
var isPlainMochitest = href.substring(0,7) != "chrome:";
|
||||
var isExpected = !!SimpleTest._expectingUncaughtException;
|
||||
if (isPlainMochitest) {
|
||||
if (!SimpleTest._ignoringAllUncaughtExceptions) {
|
||||
SimpleTest.ok(isExpected, funcIdentifier, message);
|
||||
SimpleTest._expectingUncaughtException = false;
|
||||
} else {
|
||||
SimpleTest.info(funcIdentifier + " " + message);
|
||||
SimpleTest.todo(false, funcIdentifier, message);
|
||||
}
|
||||
// There is no Components.stack.caller to log. (See bug 511888.)
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// CFGrowlAdditions.c
|
||||
// Growl
|
||||
//
|
||||
// Created by Mac-arena the Bored Zo on Wed Jun 18 2004.
|
||||
// Created by Peter Hosey on Wed Jun 18 2004.
|
||||
// Copyright 2005-2006 The Growl Project.
|
||||
//
|
||||
// This file is under the BSD License, refer to License.txt for details
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// CFGrowlAdditions.h
|
||||
// Growl
|
||||
//
|
||||
// Created by Mac-arena the Bored Zo on Wed Jun 18 2004.
|
||||
// Created by Peter Hosey on Wed Jun 18 2004.
|
||||
// Copyright 2005-2006 The Growl Project.
|
||||
//
|
||||
// This file is under the BSD License, refer to License.txt for details
|
||||
|
|
|
@ -293,7 +293,7 @@ static BOOL registerWhenGrowlIsReady = NO;
|
|||
/*if Growl launches, and the user hasn't already said NO to installing
|
||||
* it, store this notification for posting
|
||||
*/
|
||||
if (!userChoseNotToInstallGrowl) {
|
||||
if (!([self isGrowlInstalled] || userChoseNotToInstallGrowl)) {
|
||||
if (!queuedGrowlNotifications)
|
||||
queuedGrowlNotifications = [[NSMutableArray alloc] init];
|
||||
[queuedGrowlNotifications addObject:userInfo];
|
||||
|
@ -448,7 +448,7 @@ static BOOL registerWhenGrowlIsReady = NO;
|
|||
} else {
|
||||
[mRegDict removeObjectForKey:GROWL_APP_LOCATION];
|
||||
}
|
||||
[myURL release];
|
||||
[NSMakeCollectable(myURL) release];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -549,7 +549,7 @@ static BOOL registerWhenGrowlIsReady = NO;
|
|||
if (!iconData) {
|
||||
NSURL *URL = copyCurrentProcessURL();
|
||||
iconData = [copyIconDataForURL(URL) autorelease];
|
||||
[URL release];
|
||||
[NSMakeCollectable(URL) release];
|
||||
}
|
||||
|
||||
return iconData;
|
||||
|
@ -724,25 +724,96 @@ static BOOL registerWhenGrowlIsReady = NO;
|
|||
|
||||
#pragma mark -
|
||||
|
||||
+ (OSStatus) getPSN:(struct ProcessSerialNumber *)outAppPSN forAppWithBundleAtPath:(NSString *)appPath {
|
||||
OSStatus err;
|
||||
while ((err = GetNextProcess(outAppPSN)) == noErr) {
|
||||
NSDictionary *dict = [NSMakeCollectable(ProcessInformationCopyDictionary(outAppPSN, kProcessDictionaryIncludeAllInformationMask)) autorelease];
|
||||
NSString *bundlePath = [dict objectForKey:@"BundlePath"];
|
||||
if ([bundlePath isEqualToString:appPath]) {
|
||||
//Match!
|
||||
break;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
+ (BOOL) launchApplicationWithBundleAtPath:(NSString *)appPath openDocumentURL:(NSURL *)regItemURL {
|
||||
const struct LSLaunchURLSpec spec = {
|
||||
.appURL = (CFURLRef)[NSURL fileURLWithPath:appPath],
|
||||
.itemURLs = (CFArrayRef)(regItemURL ? [NSArray arrayWithObject:regItemURL] : nil),
|
||||
.passThruParams = NULL,
|
||||
.launchFlags = kLSLaunchDontAddToRecents | kLSLaunchDontSwitch | kLSLaunchNoParams | kLSLaunchAsync,
|
||||
.asyncRefCon = NULL
|
||||
};
|
||||
OSStatus err = LSOpenFromURLSpec(&spec, NULL);
|
||||
if (err != noErr) {
|
||||
NSLog(@"Could not launch application at path %@ (with document %@) because LSOpenFromURLSpec returned %i (%s)", appPath, regItemURL, err, GetMacOSStatusCommentString(err));
|
||||
}
|
||||
return (err == noErr);
|
||||
}
|
||||
+ (BOOL) sendOpenEventToProcessWithProcessSerialNumber:(struct ProcessSerialNumber *)appPSN openDocumentURL:(NSURL *)regItemURL {
|
||||
OSStatus err;
|
||||
BOOL success = NO;
|
||||
AEStreamRef stream = AEStreamCreateEvent(kCoreEventClass, kAEOpenDocuments,
|
||||
//Target application
|
||||
typeProcessSerialNumber, appPSN, sizeof(*appPSN),
|
||||
kAutoGenerateReturnID, kAnyTransactionID);
|
||||
if (!stream) {
|
||||
NSLog(@"%@: Could not create open-document event to register this application with Growl", [self class]);
|
||||
} else {
|
||||
if (regItemURL) {
|
||||
NSString *regItemURLString = [regItemURL absoluteString];
|
||||
NSData *regItemURLUTF8Data = [regItemURLString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
err = AEStreamWriteKeyDesc(stream, keyDirectObject, typeFileURL, [regItemURLUTF8Data bytes], [regItemURLUTF8Data length]);
|
||||
if (err != noErr) {
|
||||
NSLog(@"%@: Could not set direct object of open-document event to register this application with Growl because AEStreamWriteKeyDesc returned %li/%s", [self class], (long)err, GetMacOSStatusCommentString(err));
|
||||
}
|
||||
}
|
||||
|
||||
AppleEvent event;
|
||||
err = AEStreamClose(stream, &event);
|
||||
if (err != noErr) {
|
||||
NSLog(@"%@: Could not finish open-document event to register this application with Growl because AEStreamClose returned %li/%s", [self class], (long)err, GetMacOSStatusCommentString(err));
|
||||
} else {
|
||||
err = AESendMessage(&event, /*reply*/ NULL, kAENoReply | kAEDontReconnect | kAENeverInteract | kAEDontRecord, kAEDefaultTimeout);
|
||||
if (err != noErr) {
|
||||
NSLog(@"%@: Could not send open-document event to register this application with Growl because AESend returned %li/%s", [self class], (long)err, GetMacOSStatusCommentString(err));
|
||||
}
|
||||
|
||||
AEDisposeDesc(&event);
|
||||
}
|
||||
|
||||
success = (err == noErr);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
+ (BOOL) _launchGrowlIfInstalledWithRegistrationDictionary:(NSDictionary *)regDict {
|
||||
BOOL success = NO;
|
||||
NSBundle *growlPrefPaneBundle;
|
||||
NSBundle *growlPrefPaneBundle = nil;
|
||||
NSString *growlHelperAppPath;
|
||||
|
||||
#ifdef DEBUG
|
||||
//For a debug build, first look for a running GHA. It might not actually be within a Growl prefpane bundle.
|
||||
//First look for a running GHA. It might not actually be within a Growl prefpane bundle.
|
||||
growlHelperAppPath = [[GrowlPathUtilities runningHelperAppBundle] bundlePath];
|
||||
if (!growlHelperAppPath) {
|
||||
if (growlHelperAppPath) {
|
||||
//The GHA bundle path should be: .../Growl.prefPane/Contents/Resources/GrowlHelperApp.app
|
||||
NSArray *growlHelperAppPathComponents = [growlHelperAppPath pathComponents];
|
||||
NSString *growlPrefPaneBundlePath = [NSString pathWithComponents:[growlHelperAppPathComponents subarrayWithRange:(NSRange){ 0UL, [growlHelperAppPathComponents count] - 3UL }]];
|
||||
growlPrefPaneBundle = [NSBundle bundleWithPath:growlPrefPaneBundlePath];
|
||||
//Make sure we actually got a Growl.prefPane and not, say, a Growl project folder. (NSBundle can be liberal in its acceptance of a directory as a bundle at times.)
|
||||
if (![[growlPrefPaneBundle bundleIdentifier] isEqualToString:GROWL_PREFPANE_BUNDLE_IDENTIFIER])
|
||||
growlPrefPaneBundle = nil;
|
||||
}
|
||||
//If we didn't get a Growl prefpane bundle by finding the bundle that contained GHA, look it up directly.
|
||||
if (!growlPrefPaneBundle) {
|
||||
growlPrefPaneBundle = [GrowlPathUtilities growlPrefPaneBundle];
|
||||
}
|
||||
//If we don't already have the path to a running GHA, then…
|
||||
if (!growlHelperAppPath) {
|
||||
//Look for an installed-but-not-running GHA.
|
||||
growlHelperAppPath = [growlPrefPaneBundle pathForResource:@"GrowlHelperApp"
|
||||
ofType:@"app"];
|
||||
}
|
||||
NSLog(@"Will use GrowlHelperApp at %@", growlHelperAppPath);
|
||||
#else
|
||||
growlPrefPaneBundle = [GrowlPathUtilities growlPrefPaneBundle];
|
||||
growlHelperAppPath = [growlPrefPaneBundle pathForResource:@"GrowlHelperApp"
|
||||
ofType:@"app"];
|
||||
#endif
|
||||
|
||||
#ifdef GROWL_WITH_INSTALLER
|
||||
if (growlPrefPaneBundle) {
|
||||
|
@ -762,16 +833,12 @@ static BOOL registerWhenGrowlIsReady = NO;
|
|||
struct ProcessSerialNumber appPSN = {
|
||||
0, kNoProcess
|
||||
};
|
||||
while ((err = GetNextProcess(&appPSN)) == noErr) {
|
||||
NSDictionary *dict = [(id)ProcessInformationCopyDictionary(&appPSN, kProcessDictionaryIncludeAllInformationMask) autorelease];
|
||||
NSString *bundlePath = [dict objectForKey:@"BundlePath"];
|
||||
if ([bundlePath isEqualToString:growlHelperAppPath]) {
|
||||
//Match!
|
||||
break;
|
||||
}
|
||||
}
|
||||
err = [self getPSN:&appPSN forAppWithBundleAtPath:growlHelperAppPath];
|
||||
BOOL foundGrowlProcess = (err == noErr);
|
||||
BOOL foundNoGrowlProcess = (err == procNotFound);
|
||||
|
||||
if (err == noErr) {
|
||||
//If both of these are false, the process search failed with an error (and I don't mean procNotFound).
|
||||
if (foundGrowlProcess || foundNoGrowlProcess) {
|
||||
NSURL *regItemURL = nil;
|
||||
BOOL passRegDict = NO;
|
||||
|
||||
|
@ -814,35 +881,10 @@ static BOOL registerWhenGrowlIsReady = NO;
|
|||
}
|
||||
}
|
||||
|
||||
AEStreamRef stream = AEStreamCreateEvent(kCoreEventClass, kAEOpenDocuments,
|
||||
//Target application
|
||||
typeProcessSerialNumber, &appPSN, sizeof(appPSN),
|
||||
kAutoGenerateReturnID, kAnyTransactionID);
|
||||
if (!stream) {
|
||||
NSLog(@"%@: Could not create open-document event to register this application with Growl", [self class]);
|
||||
} else {
|
||||
if (passRegDict) {
|
||||
NSString *regItemURLString = [regItemURL absoluteString];
|
||||
NSData *regItemURLUTF8Data = [regItemURLString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
err = AEStreamWriteKeyDesc(stream, keyDirectObject, typeFileURL, [regItemURLUTF8Data bytes], [regItemURLUTF8Data length]);
|
||||
if (err != noErr) {
|
||||
NSLog(@"%@: Could not set direct object of open-document event to register this application with Growl because AEStreamWriteKeyDesc returned %li/%s", [self class], (long)err, GetMacOSStatusCommentString(err));
|
||||
}
|
||||
}
|
||||
|
||||
AppleEvent event;
|
||||
err = AEStreamClose(stream, &event);
|
||||
if (err != noErr) {
|
||||
NSLog(@"%@: Could not finish open-document event to register this application with Growl because AEStreamClose returned %li/%s", [self class], (long)err, GetMacOSStatusCommentString(err));
|
||||
} else {
|
||||
err = AESendMessage(&event, /*reply*/ NULL, kAENoReply | kAEDontReconnect | kAENeverInteract | kAEDontRecord, kAEDefaultTimeout);
|
||||
if (err != noErr) {
|
||||
NSLog(@"%@: Could not send open-document event to register this application with Growl because AESend returned %li/%s", [self class], (long)err, GetMacOSStatusCommentString(err));
|
||||
}
|
||||
}
|
||||
|
||||
success = (err == noErr);
|
||||
}
|
||||
if (foundNoGrowlProcess)
|
||||
success = [self launchApplicationWithBundleAtPath:growlHelperAppPath openDocumentURL:(passRegDict ? regItemURL : nil)];
|
||||
else
|
||||
success = [self sendOpenEventToProcessWithProcessSerialNumber:&appPSN openDocumentURL:(passRegDict ? regItemURL : nil)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -293,6 +293,7 @@ struct GrowlNetworkNotification {
|
|||
#define GROWL_PATHWAY_EXTENSION XSTR("growlPathway")
|
||||
#define GROWL_VIEW_EXTENSION XSTR("growlView")
|
||||
#define GROWL_STYLE_EXTENSION XSTR("growlStyle")
|
||||
#define GROWL_PATHEXTENSION_TICKET XSTR("growlTicket")
|
||||
|
||||
/* --- These following macros are intended for plug-ins --- */
|
||||
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "GrowlPathUtilities.h"
|
||||
#import "GrowlPreferencesController.h"
|
||||
#import "GrowlTicketController.h"
|
||||
#import "GrowlDefinesInternal.h"
|
||||
|
||||
static NSBundle *helperAppBundle;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) The Growl Project, 2004-2009
|
||||
Copyright (c) The Growl Project, 2004-2011
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
|
|
@ -92,8 +92,7 @@ DispatchNamedNotification(const nsAString &aName,
|
|||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
if ([GrowlApplicationBridge isGrowlInstalled] == NO ||
|
||||
[GrowlApplicationBridge isGrowlRunning] == NO)
|
||||
if ([GrowlApplicationBridge isGrowlRunning] == NO)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
mozGrowlDelegate *delegate =
|
||||
|
@ -146,9 +145,6 @@ nsAlertsService::Init()
|
|||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
if ([GrowlApplicationBridge isGrowlInstalled] == NO)
|
||||
return NS_ERROR_SERVICE_NOT_AVAILABLE;
|
||||
|
||||
NS_ASSERTION([GrowlApplicationBridge growlDelegate] == nil,
|
||||
"We already registered with Growl!");
|
||||
|
||||
|
|
|
@ -160,9 +160,9 @@
|
|||
#define NS_TYPEAHEADFIND_CID \
|
||||
{ 0xe7f70966, 0x9a37, 0x48d7, { 0x8a, 0xeb, 0x35, 0x99, 0x8f, 0x31, 0x09, 0x0e} }
|
||||
|
||||
// {42ef1d52-3351-4973-98f8-d18f089bccfa}
|
||||
// {5edc87c2-6960-44e5-8431-bdfbb56f6aff}
|
||||
#define NS_URLCLASSIFIERPREFIXSET_CID \
|
||||
{ 0x42ef1d52, 0x3351, 0x4973, { 0x98, 0xf8, 0xd1, 0x8f, 0x08, 0x9b, 0xcc, 0xfa} }
|
||||
{ 0x5edc87c2, 0x6960, 0x44e5, { 0x84, 0x31, 0xbd, 0xfb, 0xb5, 0x6f, 0x6a, 0xff} }
|
||||
|
||||
// {5eb7c3c1-ec1f-4007-87cc-eefb37d68ce6}
|
||||
#define NS_URLCLASSIFIERDBSERVICE_CID \
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
|
||||
interface nsIArray;
|
||||
|
||||
[scriptable, uuid(42ef1d52-3351-4973-98f8-d18f089bccfa)]
|
||||
[scriptable, uuid(5edc87c2-6960-44e5-8431-bdfbb56f6aff)]
|
||||
interface nsIUrlClassifierPrefixSet : nsISupports
|
||||
{
|
||||
void setPrefixes([const, array, size_is(aLength)] in unsigned long aPrefixes,
|
||||
|
@ -51,7 +51,7 @@ interface nsIUrlClassifierPrefixSet : nsISupports
|
|||
boolean contains(in unsigned long aPrefix);
|
||||
boolean probe(in unsigned long aPrefix, in unsigned long aKey,
|
||||
inout boolean aReady);
|
||||
PRUint32 estimateSize();
|
||||
PRUint32 sizeOfIncludingThis(in boolean aCountMe);
|
||||
PRUint32 getKey();
|
||||
boolean isEmpty();
|
||||
void loadFromFile(in nsIFile aFile);
|
||||
|
|
|
@ -3653,7 +3653,7 @@ nsUrlClassifierDBServiceWorker::LoadPrefixSet(nsCOMPtr<nsIFile> & aFile)
|
|||
|
||||
#ifdef DEBUG
|
||||
PRUint32 size = 0;
|
||||
rv = mPrefixSet->EstimateSize(&size);
|
||||
rv = mPrefixSet->SizeOfIncludingThis(true, &size);
|
||||
LOG(("SB tree done, size = %d bytes\n", size));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
#endif
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "nsCOMPtr.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsString.h"
|
||||
#include "nsUrlClassifierPrefixSet.h"
|
||||
#include "nsIUrlClassifierPrefixSet.h"
|
||||
#include "nsIRandomGenerator.h"
|
||||
|
@ -67,6 +68,78 @@ static const PRLogModuleInfo *gUrlClassifierPrefixSetLog = nsnull;
|
|||
#define LOG_ENABLED() (false)
|
||||
#endif
|
||||
|
||||
class nsPrefixSetReporter : public nsIMemoryReporter
|
||||
{
|
||||
public:
|
||||
nsPrefixSetReporter(nsUrlClassifierPrefixSet * aParent, const nsACString & aName);
|
||||
virtual ~nsPrefixSetReporter() {};
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIMEMORYREPORTER
|
||||
|
||||
private:
|
||||
nsCString mPath;
|
||||
nsUrlClassifierPrefixSet * mParent;
|
||||
};
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(nsPrefixSetReporter, nsIMemoryReporter)
|
||||
|
||||
nsPrefixSetReporter::nsPrefixSetReporter(nsUrlClassifierPrefixSet * aParent,
|
||||
const nsACString & aName)
|
||||
: mParent(aParent)
|
||||
{
|
||||
mPath.Assign(NS_LITERAL_CSTRING("explicit/storage/prefixset"));
|
||||
if (!aName.IsEmpty()) {
|
||||
mPath.Append("/");
|
||||
mPath.Append(aName);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPrefixSetReporter::GetProcess(nsACString & aProcess)
|
||||
{
|
||||
aProcess.Truncate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPrefixSetReporter::GetPath(nsACString & aPath)
|
||||
{
|
||||
aPath.Assign(mPath);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPrefixSetReporter::GetKind(PRInt32 * aKind)
|
||||
{
|
||||
*aKind = nsIMemoryReporter::KIND_HEAP;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPrefixSetReporter::GetUnits(PRInt32 * aUnits)
|
||||
{
|
||||
*aUnits = nsIMemoryReporter::UNITS_BYTES;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPrefixSetReporter::GetAmount(PRInt64 * aAmount)
|
||||
{
|
||||
PRUint32 size;
|
||||
nsresult rv = mParent->SizeOfIncludingThis(true, &size);
|
||||
*aAmount = size;
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPrefixSetReporter::GetDescription(nsACString & aDescription)
|
||||
{
|
||||
aDescription.Assign(NS_LITERAL_CSTRING("Memory used by a PrefixSet for "
|
||||
"UrlClassifier, in bytes."));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(nsUrlClassifierPrefixSet, nsIUrlClassifierPrefixSet)
|
||||
|
||||
nsUrlClassifierPrefixSet::nsUrlClassifierPrefixSet()
|
||||
|
@ -84,6 +157,14 @@ nsUrlClassifierPrefixSet::nsUrlClassifierPrefixSet()
|
|||
if (NS_FAILED(rv)) {
|
||||
LOG(("Failed to initialize PrefixSet"));
|
||||
}
|
||||
|
||||
mReporter = new nsPrefixSetReporter(this, NS_LITERAL_CSTRING("all"));
|
||||
NS_RegisterMemoryReporter(mReporter);
|
||||
}
|
||||
|
||||
nsUrlClassifierPrefixSet::~nsUrlClassifierPrefixSet()
|
||||
{
|
||||
NS_UnregisterMemoryReporter(mReporter);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -199,6 +280,8 @@ PRUint32 nsUrlClassifierPrefixSet::BinSearch(PRUint32 start,
|
|||
NS_IMETHODIMP
|
||||
nsUrlClassifierPrefixSet::Contains(PRUint32 aPrefix, bool * aFound)
|
||||
{
|
||||
mPrefixSetLock.AssertCurrentThreadOwns();
|
||||
|
||||
*aFound = false;
|
||||
|
||||
if (!mHasPrefixes) {
|
||||
|
@ -244,15 +327,18 @@ nsUrlClassifierPrefixSet::Contains(PRUint32 aPrefix, bool * aFound)
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierPrefixSet::EstimateSize(PRUint32 * aSize)
|
||||
nsUrlClassifierPrefixSet::SizeOfIncludingThis(bool aCountMe, PRUint32 * aSize)
|
||||
{
|
||||
MutexAutoLock lock(mPrefixSetLock);
|
||||
*aSize = sizeof(bool);
|
||||
if (mHasPrefixes) {
|
||||
*aSize += sizeof(PRUint16) * mDeltas.Length();
|
||||
*aSize += sizeof(PRUint32) * mIndexPrefixes.Length();
|
||||
*aSize += sizeof(PRUint32) * mIndexStarts.Length();
|
||||
if (aCountMe) {
|
||||
size_t usable = moz_malloc_usable_size(this);
|
||||
*aSize = (PRUint32)(usable ? usable : sizeof(*this));
|
||||
} else {
|
||||
*aSize = 0;
|
||||
}
|
||||
*aSize += mDeltas.SizeOf();
|
||||
*aSize += mIndexPrefixes.SizeOf();
|
||||
*aSize += mIndexStarts.SizeOf();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,16 +45,19 @@
|
|||
#include "nsID.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIUrlClassifierPrefixSet.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "nsToolkitCompsCID.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/CondVar.h"
|
||||
#include "mozilla/FileUtils.h"
|
||||
|
||||
class nsPrefixSetReporter;
|
||||
|
||||
class nsUrlClassifierPrefixSet : public nsIUrlClassifierPrefixSet
|
||||
{
|
||||
public:
|
||||
nsUrlClassifierPrefixSet();
|
||||
virtual ~nsUrlClassifierPrefixSet() {};
|
||||
virtual ~nsUrlClassifierPrefixSet();
|
||||
|
||||
// Can send an empty Array to clean the tree
|
||||
NS_IMETHOD SetPrefixes(const PRUint32* aArray, PRUint32 aLength);
|
||||
|
@ -69,7 +72,7 @@ public:
|
|||
NS_IMETHOD Probe(PRUint32 aPrefix, PRUint32 aKey, bool* aReady, bool* aFound);
|
||||
// Return the estimated size of the set on disk and in memory,
|
||||
// in bytes
|
||||
NS_IMETHOD EstimateSize(PRUint32* aSize);
|
||||
NS_IMETHOD SizeOfIncludingThis(bool aCountMe, PRUint32* aSize);
|
||||
NS_IMETHOD IsEmpty(bool * aEmpty);
|
||||
NS_IMETHOD LoadFromFile(nsIFile* aFile);
|
||||
NS_IMETHOD StoreToFile(nsIFile* aFile);
|
||||
|
@ -85,6 +88,7 @@ protected:
|
|||
|
||||
mozilla::Mutex mPrefixSetLock;
|
||||
mozilla::CondVar mSetIsReady;
|
||||
nsRefPtr<nsPrefixSetReporter> mReporter;
|
||||
|
||||
PRUint32 BinSearch(PRUint32 start, PRUint32 end, PRUint32 target);
|
||||
nsresult LoadFromFd(mozilla::AutoFDClose & fileFd);
|
||||
|
@ -103,6 +107,7 @@ protected:
|
|||
nsTArray<PRUint32> mIndexStarts;
|
||||
// array containing deltas from indices.
|
||||
nsTArray<PRUint16> mDeltas;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -28,6 +28,12 @@ function arrContains(arr, target) {
|
|||
return (!(i < 0 || i >= arr.length) && arr[i] == target);
|
||||
}
|
||||
|
||||
function wrappedProbe(pset, prefix) {
|
||||
let key = pset.getKey();
|
||||
let dummy = {};
|
||||
return pset.probe(prefix, key, dummy);
|
||||
};
|
||||
|
||||
// doRandomLookups: we use this to test for false membership with random input
|
||||
// over the range of prefixes (unsigned 32-bits integers).
|
||||
// pset: a nsIUrlClassifierPrefixSet to test.
|
||||
|
@ -39,7 +45,7 @@ function doRandomLookups(pset, prefixes, N) {
|
|||
while (arrContains(prefixes, randInt))
|
||||
randInt = Math.floor(Math.random() * Math.pow(2, 32));
|
||||
|
||||
do_check_false(pset.contains(randInt));
|
||||
do_check_false(wrappedProbe(pset, randInt));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,7 +56,7 @@ function doExpectedLookups(pset, prefixes, N) {
|
|||
for (let i = 0; i < N; i++) {
|
||||
prefixes.forEach(function (x) {
|
||||
dump("Checking " + x + "\n");
|
||||
do_check_true(pset.contains(x));
|
||||
do_check_true(wrappedProbe(pset, x));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -63,11 +69,11 @@ function testBasicPset() {
|
|||
let prefixes = [2,100,50,2000,78000,1593203];
|
||||
pset.setPrefixes(prefixes, prefixes.length);
|
||||
|
||||
do_check_true(pset.contains(100));
|
||||
do_check_false(pset.contains(100000));
|
||||
do_check_true(pset.contains(1593203));
|
||||
do_check_false(pset.contains(999));
|
||||
do_check_false(pset.contains(0));
|
||||
do_check_true(wrappedProbe(pset, 100));
|
||||
do_check_false(wrappedProbe(pset, 100000));
|
||||
do_check_true(wrappedProbe(pset, 1593203));
|
||||
do_check_false(wrappedProbe(pset, 999));
|
||||
do_check_false(wrappedProbe(pset, 0));
|
||||
}
|
||||
|
||||
function testDuplicates() {
|
||||
|
@ -76,12 +82,12 @@ function testDuplicates() {
|
|||
let prefixes = [1,1,2,2,2,3,3,3,3,3,3,5,6,6,7,7,9,9,9];
|
||||
pset.setPrefixes(prefixes, prefixes.length);
|
||||
|
||||
do_check_true(pset.contains(1));
|
||||
do_check_true(pset.contains(2));
|
||||
do_check_true(pset.contains(5));
|
||||
do_check_true(pset.contains(9));
|
||||
do_check_false(pset.contains(4));
|
||||
do_check_false(pset.contains(8));
|
||||
do_check_true(wrappedProbe(pset, 1));
|
||||
do_check_true(wrappedProbe(pset, 2));
|
||||
do_check_true(wrappedProbe(pset, 5));
|
||||
do_check_true(wrappedProbe(pset, 9));
|
||||
do_check_false(wrappedProbe(pset, 4));
|
||||
do_check_false(wrappedProbe(pset, 8));
|
||||
}
|
||||
|
||||
function testSimplePset() {
|
||||
|
@ -114,7 +120,7 @@ function testReSetPrefixes() {
|
|||
|
||||
doExpectedLookups(pset, secondPrefixes, 1);
|
||||
for (let i = 0; i < prefixes.length; i++) {
|
||||
do_check_false(pset.contains(prefixes[i]));
|
||||
do_check_false(wrappedProbe(pset, prefixes[i]));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,12 +148,12 @@ function testTinySet() {
|
|||
let prefixes = [1];
|
||||
pset.setPrefixes(prefixes, prefixes.length);
|
||||
|
||||
do_check_true(pset.contains(1));
|
||||
do_check_false(pset.contains(100000));
|
||||
do_check_true(wrappedProbe(pset, 1));
|
||||
do_check_false(wrappedProbe(pset, 100000));
|
||||
|
||||
prefixes = [];
|
||||
pset.setPrefixes(prefixes, prefixes.length);
|
||||
do_check_false(pset.contains(1));
|
||||
do_check_false(wrappedProbe(pset, 1));
|
||||
}
|
||||
|
||||
let tests = [testBasicPset,
|
||||
|
|
|
@ -2623,12 +2623,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
<h1><a name="growl"></a>Growl License</h1>
|
||||
|
||||
<p>This license applies to certain files in the directory
|
||||
<span class="path">toolkit/components/alerts/src/mac/growl/</span> and
|
||||
<span class="path">camino/growl/</span>. (This code only ships in the Mac
|
||||
OS X version of the product.)
|
||||
<span class="path">toolkit/components/alerts/mac/growl/</span>.
|
||||
(This code only ships in the Mac OS X version of the product.)
|
||||
|
||||
<pre>
|
||||
Copyright (c) The Growl Project, 2004-2009
|
||||
Copyright (c) The Growl Project, 2004-2011
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
|
|
|
@ -2942,16 +2942,18 @@ var XPIProvider = {
|
|||
|
||||
let state = this.getInstallLocationStates();
|
||||
|
||||
// If the database exists then the previous file cache can be trusted
|
||||
// otherwise the database needs to be recreated
|
||||
let dbFile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_DATABASE], true);
|
||||
updateDatabase |= !dbFile.exists();
|
||||
if (!updateDatabase) {
|
||||
// If the state has changed then we must update the database
|
||||
let cache = Prefs.getCharPref(PREF_INSTALL_CACHE, null);
|
||||
updateDatabase |= cache != JSON.stringify(state);
|
||||
}
|
||||
|
||||
// If the database doesn't exist and there are add-ons installed then we
|
||||
// must update the database however if there are no add-ons then there is
|
||||
// no need to update the database.
|
||||
if (!XPIDatabase.dbfileExists)
|
||||
updateDatabase = state.length > 0;
|
||||
|
||||
if (!updateDatabase) {
|
||||
let bootstrapDescriptors = [this.bootstrappedAddons[b].descriptor
|
||||
for (b in this.bootstrappedAddons)];
|
||||
|
@ -2963,7 +2965,7 @@ var XPIProvider = {
|
|||
bootstrapDescriptors.splice(pos, 1);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (bootstrapDescriptors.length > 0) {
|
||||
WARN("Bootstrap state is invalid (missing add-ons: " + bootstrapDescriptors.toSource() + ")");
|
||||
updateDatabase = true;
|
||||
|
@ -2977,7 +2979,7 @@ var XPIProvider = {
|
|||
// If the database needs to be updated then open it and then update it
|
||||
// from the filesystem
|
||||
if (updateDatabase || hasPendingChanges) {
|
||||
let migrateData = XPIDatabase.openConnection(false);
|
||||
let migrateData = XPIDatabase.openConnection(false, true);
|
||||
|
||||
try {
|
||||
extensionListChanged = this.processFileChanges(state, manifests,
|
||||
|
@ -4022,6 +4024,8 @@ var XPIDatabase = {
|
|||
addonCache: [],
|
||||
// The nested transaction count
|
||||
transactionCount: 0,
|
||||
// The database file
|
||||
dbfile: FileUtils.getFile(KEY_PROFILEDIR, [FILE_DATABASE], true),
|
||||
|
||||
// The statements used by the database
|
||||
statements: {
|
||||
|
@ -4113,6 +4117,11 @@ var XPIDatabase = {
|
|||
rollbackSavepoint: "ROLLBACK TO SAVEPOINT 'default'"
|
||||
},
|
||||
|
||||
get dbfileExists() {
|
||||
delete this.dbfileExists;
|
||||
return this.dbfileExists = this.dbfile.exists();
|
||||
},
|
||||
|
||||
/**
|
||||
* Begins a new transaction in the database. Transactions may be nested. Data
|
||||
* written by an inner transaction may be rolled back on its own. Rolling back
|
||||
|
@ -4176,6 +4185,7 @@ var XPIDatabase = {
|
|||
// Attempt to open the database
|
||||
try {
|
||||
connection = Services.storage.openUnsharedDatabase(aDBFile);
|
||||
this.dbfileExists = true;
|
||||
}
|
||||
catch (e) {
|
||||
ERROR("Failed to open database (1st attempt)", e);
|
||||
|
@ -4213,12 +4223,17 @@ var XPIDatabase = {
|
|||
* @return the migration data from the database if it was an old schema or
|
||||
* null otherwise.
|
||||
*/
|
||||
openConnection: function XPIDB_openConnection(aRebuildOnError) {
|
||||
this.initialized = true;
|
||||
let dbfile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_DATABASE], true);
|
||||
openConnection: function XPIDB_openConnection(aRebuildOnError, aForceOpen) {
|
||||
delete this.connection;
|
||||
|
||||
this.connection = this.openDatabaseFile(dbfile);
|
||||
if (!aForceOpen && !this.dbfileExists) {
|
||||
this.connection = null;
|
||||
return {};
|
||||
}
|
||||
|
||||
this.initialized = true;
|
||||
|
||||
this.connection = this.openDatabaseFile(this.dbfile);
|
||||
|
||||
let migrateData = null;
|
||||
// If the database was corrupt or missing then the new blank database will
|
||||
|
@ -4235,11 +4250,11 @@ var XPIDatabase = {
|
|||
// Delete the existing database
|
||||
this.connection.close();
|
||||
try {
|
||||
if (dbfile.exists())
|
||||
dbfile.remove(true);
|
||||
if (this.dbfileExists)
|
||||
this.dbfile.remove(true);
|
||||
|
||||
// Reopen an empty database
|
||||
this.connection = this.openDatabaseFile(dbfile);
|
||||
this.connection = this.openDatabaseFile(this.dbfile);
|
||||
}
|
||||
catch (e) {
|
||||
ERROR("Failed to remove old database", e);
|
||||
|
@ -4250,7 +4265,6 @@ var XPIDatabase = {
|
|||
}
|
||||
else if (Prefs.getIntPref(PREF_DB_SCHEMA, 0) == 0) {
|
||||
// Only migrate data from the RDF if we haven't done it before
|
||||
LOG("Migrating data from extensions.rdf");
|
||||
migrateData = this.getMigrateDataFromRDF();
|
||||
}
|
||||
|
||||
|
@ -4350,6 +4364,7 @@ var XPIDatabase = {
|
|||
// Migrate data from extensions.rdf
|
||||
let rdffile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_OLD_DATABASE], true);
|
||||
if (rdffile.exists()) {
|
||||
LOG("Migrating data from extensions.rdf");
|
||||
let ds = gRDF.GetDataSourceBlocking(Services.io.newFileURI(rdffile).spec);
|
||||
let root = Cc["@mozilla.org/rdf/container;1"].
|
||||
createInstance(Ci.nsIRDFContainer);
|
||||
|
@ -4963,6 +4978,9 @@ var XPIDatabase = {
|
|||
* @return an array of names of install locations
|
||||
*/
|
||||
getInstallLocations: function XPIDB_getInstallLocations() {
|
||||
if (!this.connection)
|
||||
return [];
|
||||
|
||||
let stmt = this.getStatement("getInstallLocations");
|
||||
|
||||
return [row.location for each (row in resultRows(stmt))];
|
||||
|
@ -4976,6 +4994,9 @@ var XPIDatabase = {
|
|||
* @return an array of DBAddonInternals
|
||||
*/
|
||||
getAddonsInLocation: function XPIDB_getAddonsInLocation(aLocation) {
|
||||
if (!this.connection)
|
||||
return [];
|
||||
|
||||
let stmt = this.getStatement("getAddonsInLocation");
|
||||
|
||||
stmt.params.location = aLocation;
|
||||
|
@ -4994,6 +5015,11 @@ var XPIDatabase = {
|
|||
* A callback to pass the DBAddonInternal to
|
||||
*/
|
||||
getAddonInLocation: function XPIDB_getAddonInLocation(aId, aLocation, aCallback) {
|
||||
if (!this.connection) {
|
||||
aCallback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
let stmt = this.getStatement("getAddonInLocation");
|
||||
|
||||
stmt.params.id = aId;
|
||||
|
@ -5020,6 +5046,11 @@ var XPIDatabase = {
|
|||
* A callback to pass the DBAddonInternal to
|
||||
*/
|
||||
getVisibleAddonForID: function XPIDB_getVisibleAddonForID(aId, aCallback) {
|
||||
if (!this.connection) {
|
||||
aCallback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
let stmt = this.getStatement("getVisibleAddonForID");
|
||||
|
||||
stmt.params.id = aId;
|
||||
|
@ -5045,6 +5076,11 @@ var XPIDatabase = {
|
|||
* A callback to pass the array of DBAddonInternals to
|
||||
*/
|
||||
getVisibleAddons: function XPIDB_getVisibleAddons(aTypes, aCallback) {
|
||||
if (!this.connection) {
|
||||
aCallback([]);
|
||||
return;
|
||||
}
|
||||
|
||||
let stmt = null;
|
||||
if (!aTypes || aTypes.length == 0) {
|
||||
stmt = this.getStatement("getVisibleAddons");
|
||||
|
@ -5076,6 +5112,9 @@ var XPIDatabase = {
|
|||
* @return an array of DBAddonInternals
|
||||
*/
|
||||
getAddonsByType: function XPIDB_getAddonsByType(aType) {
|
||||
if (!this.connection)
|
||||
return [];
|
||||
|
||||
let stmt = this.getStatement("getAddonsByType");
|
||||
|
||||
stmt.params.type = aType;
|
||||
|
@ -5090,6 +5129,9 @@ var XPIDatabase = {
|
|||
* @return a DBAddonInternal
|
||||
*/
|
||||
getVisibleAddonForInternalName: function XPIDB_getVisibleAddonForInternalName(aInternalName) {
|
||||
if (!this.connection)
|
||||
return null;
|
||||
|
||||
let stmt = this.getStatement("getVisibleAddonForInternalName");
|
||||
|
||||
let addon = null;
|
||||
|
@ -5112,6 +5154,11 @@ var XPIDatabase = {
|
|||
*/
|
||||
getVisibleAddonsWithPendingOperations:
|
||||
function XPIDB_getVisibleAddonsWithPendingOperations(aTypes, aCallback) {
|
||||
if (!this.connection) {
|
||||
aCallback([]);
|
||||
return;
|
||||
}
|
||||
|
||||
let stmt = null;
|
||||
if (!aTypes || aTypes.length == 0) {
|
||||
stmt = this.getStatement("getVisibleAddonsWithPendingOperations");
|
||||
|
@ -5143,6 +5190,9 @@ var XPIDatabase = {
|
|||
* @return an array of DBAddonInternals
|
||||
*/
|
||||
getAddons: function XPIDB_getAddons() {
|
||||
if (!this.connection)
|
||||
return [];
|
||||
|
||||
let stmt = this.getStatement("getAddons");
|
||||
|
||||
return [this.makeAddonFromRow(row) for each (row in resultRows(stmt))];;
|
||||
|
@ -5157,6 +5207,10 @@ var XPIDatabase = {
|
|||
* The file descriptor of the add-on
|
||||
*/
|
||||
addAddonMetadata: function XPIDB_addAddonMetadata(aAddon, aDescriptor) {
|
||||
// If there is no DB yet then forcibly create one
|
||||
if (!this.connection)
|
||||
this.openConnection(false, true);
|
||||
|
||||
this.beginTransaction();
|
||||
|
||||
var self = this;
|
||||
|
@ -5430,28 +5484,34 @@ var XPIDatabase = {
|
|||
let enabledAddons = [];
|
||||
let text = "[ExtensionDirs]\r\n";
|
||||
let count = 0;
|
||||
let stmt;
|
||||
|
||||
let stmt = this.getStatement("getActiveAddons");
|
||||
if (this.connection) {
|
||||
stmt = this.getStatement("getActiveAddons");
|
||||
|
||||
for (let row in resultRows(stmt)) {
|
||||
text += "Extension" + (count++) + "=" + row.descriptor + "\r\n";
|
||||
enabledAddons.push(row.id + ":" + row.version);
|
||||
for (let row in resultRows(stmt)) {
|
||||
text += "Extension" + (count++) + "=" + row.descriptor + "\r\n";
|
||||
enabledAddons.push(row.id + ":" + row.version);
|
||||
}
|
||||
}
|
||||
|
||||
// The selected skin may come from an inactive theme (the default theme
|
||||
// when a lightweight theme is applied for example)
|
||||
text += "\r\n[ThemeDirs]\r\n";
|
||||
if (Prefs.getBoolPref(PREF_EM_DSS_ENABLED)) {
|
||||
stmt = this.getStatement("getThemes");
|
||||
}
|
||||
else {
|
||||
stmt = this.getStatement("getActiveTheme");
|
||||
stmt.params.internalName = XPIProvider.selectedSkin;
|
||||
}
|
||||
count = 0;
|
||||
for (let row in resultRows(stmt)) {
|
||||
text += "Extension" + (count++) + "=" + row.descriptor + "\r\n";
|
||||
enabledAddons.push(row.id + ":" + row.version);
|
||||
|
||||
if (this.connection) {
|
||||
if (Prefs.getBoolPref(PREF_EM_DSS_ENABLED)) {
|
||||
stmt = this.getStatement("getThemes");
|
||||
}
|
||||
else {
|
||||
stmt = this.getStatement("getActiveTheme");
|
||||
stmt.params.internalName = XPIProvider.selectedSkin;
|
||||
}
|
||||
count = 0;
|
||||
for (let row in resultRows(stmt)) {
|
||||
text += "Extension" + (count++) + "=" + row.descriptor + "\r\n";
|
||||
enabledAddons.push(row.id + ":" + row.version);
|
||||
}
|
||||
}
|
||||
|
||||
var fos = FileUtils.openSafeFileOutputStream(addonsList);
|
||||
|
|
|
@ -125,6 +125,10 @@ function run_test() {
|
|||
|
||||
startupManager();
|
||||
|
||||
let file = gProfD.clone();
|
||||
file.append("extensions.sqlite");
|
||||
do_check_false(file.exists());
|
||||
|
||||
run_test_1();
|
||||
}
|
||||
|
||||
|
@ -167,6 +171,10 @@ function run_test_1() {
|
|||
}
|
||||
|
||||
function check_test_1() {
|
||||
let file = gProfD.clone();
|
||||
file.append("extensions.sqlite");
|
||||
do_check_true(file.exists());
|
||||
|
||||
AddonManager.getAllInstalls(function(installs) {
|
||||
// There should be no active installs now since the install completed and
|
||||
// doesn't require a restart.
|
||||
|
|
|
@ -247,13 +247,15 @@ function run_test_1() {
|
|||
do_check_false(t2.appDisabled);
|
||||
do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
|
||||
|
||||
// After restarting the database won't be open and so can be replaced with
|
||||
// a bad file
|
||||
restartManager();
|
||||
// Shutdown and replace the database with a corrupt file (a directory
|
||||
// serves this purpose). On startup the add-ons manager won't rebuild
|
||||
// because there is a file there still.
|
||||
shutdownManager();
|
||||
var dbfile = gProfD.clone();
|
||||
dbfile.append("extensions.sqlite");
|
||||
dbfile.remove(true);
|
||||
dbfile.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0755);
|
||||
startupManager(false);
|
||||
|
||||
// Accessing the add-ons should open and recover the database
|
||||
AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
|
||||
|
|
|
@ -247,13 +247,15 @@ function run_test_1() {
|
|||
do_check_false(t2.appDisabled);
|
||||
do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
|
||||
|
||||
// After restarting the database won't be open and so can be replaced with
|
||||
// a bad file
|
||||
restartManager();
|
||||
// Shutdown and replace the database with a corrupt file (a directory
|
||||
// serves this purpose). On startup the add-ons manager won't rebuild
|
||||
// because there is a file there still.
|
||||
shutdownManager();
|
||||
var dbfile = gProfD.clone();
|
||||
dbfile.append("extensions.sqlite");
|
||||
dbfile.remove(true);
|
||||
dbfile.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0755);
|
||||
startupManager(false);
|
||||
|
||||
// Accessing the add-ons should open and recover the database
|
||||
AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
|
||||
|
|
|
@ -132,6 +132,10 @@ function run_test() {
|
|||
check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
|
||||
check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
|
||||
|
||||
let file = gProfD.clone();
|
||||
file.append("extensions.sqlite");
|
||||
do_check_false(file.exists());
|
||||
|
||||
AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
|
||||
"addon2@tests.mozilla.org",
|
||||
"addon3@tests.mozilla.org",
|
||||
|
@ -183,6 +187,10 @@ function run_test_1() {
|
|||
check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
|
||||
do_check_true(gCachePurged);
|
||||
|
||||
let file = gProfD.clone();
|
||||
file.append("extensions.sqlite");
|
||||
do_check_true(file.exists());
|
||||
|
||||
AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
|
||||
"addon2@tests.mozilla.org",
|
||||
"addon3@tests.mozilla.org",
|
||||
|
|
|
@ -161,6 +161,11 @@ AndroidBridge::Init(JNIEnv *jEnv,
|
|||
jDisableBatteryNotifications = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "disableBatteryNotifications", "()V");
|
||||
jGetCurrentBatteryInformation = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getCurrentBatteryInformation", "()[D");
|
||||
|
||||
jGetAccessibilityEnabled = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getAccessibilityEnabled", "()Z");
|
||||
jHandleGeckoMessage = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "handleGeckoMessage", "(Ljava/lang/String;)Ljava/lang/String;");
|
||||
jCheckUriVisited = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "checkUriVisited", "(Ljava/lang/String;)V");
|
||||
jMarkUriVisited = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "markUriVisited", "(Ljava/lang/String;)V");
|
||||
|
||||
jEGLContextClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("javax/microedition/khronos/egl/EGLContext"));
|
||||
jEGL10Class = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("javax/microedition/khronos/egl/EGL10"));
|
||||
jEGLSurfaceImplClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("com/google/android/gles_jni/EGLSurfaceImpl"));
|
||||
|
@ -670,6 +675,13 @@ AndroidBridge::HideProgressDialogOnce()
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
AndroidBridge::GetAccessibilityEnabled()
|
||||
{
|
||||
ALOG_BRIDGE("AndroidBridge::GetAccessibilityEnabled");
|
||||
return mJNIEnv->CallStaticBooleanMethod(mGeckoAppShellClass, jGetAccessibilityEnabled);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidBridge::PerformHapticFeedback(bool aIsLongPress)
|
||||
{
|
||||
|
@ -839,6 +851,12 @@ AndroidBridge::SetSurfaceView(jobject obj)
|
|||
mSurfaceView.Init(obj);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidBridge::SetSoftwareLayerClient(jobject obj)
|
||||
{
|
||||
mSoftwareLayerClient.Init(obj);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidBridge::ShowInputMethodPicker()
|
||||
{
|
||||
|
@ -1002,10 +1020,8 @@ AndroidBridge::CreateShortcut(const nsAString& aTitle, const nsAString& aURI, co
|
|||
void
|
||||
AndroidBridge::PostToJavaThread(nsIRunnable* aRunnable, bool aMainThread)
|
||||
{
|
||||
__android_log_print(ANDROID_LOG_INFO, "GeckoBridge", "%s", __PRETTY_FUNCTION__);
|
||||
JNIEnv* env = AndroidBridge::AttachThread(false);
|
||||
if (!env) {
|
||||
__android_log_print(ANDROID_LOG_INFO, "GeckoBridge", "no jni env in %s!!", __PRETTY_FUNCTION__);
|
||||
return;
|
||||
}
|
||||
mRunnableQueue.AppendObject(aRunnable);
|
||||
|
@ -1016,27 +1032,21 @@ AndroidBridge::PostToJavaThread(nsIRunnable* aRunnable, bool aMainThread)
|
|||
env->ExceptionDescribe();
|
||||
env->ExceptionClear();
|
||||
}
|
||||
__android_log_print(ANDROID_LOG_INFO, "GeckoBridge", "leaving %s", __PRETTY_FUNCTION__);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidBridge::ExecuteNextRunnable()
|
||||
{
|
||||
__android_log_print(ANDROID_LOG_INFO, "GeckoBridge", "%s", __PRETTY_FUNCTION__);
|
||||
|
||||
JNIEnv* env = AndroidBridge::AttachThread(false);
|
||||
if (!env) {
|
||||
__android_log_print(ANDROID_LOG_INFO, "GeckoBridge", "no jni env in %s!!", __PRETTY_FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mRunnableQueue.Count() > 0) {
|
||||
nsIRunnable* r = mRunnableQueue[0];
|
||||
__android_log_print(ANDROID_LOG_INFO, "GeckoBridge", "going to run %p", r);
|
||||
r->Run();
|
||||
mRunnableQueue.RemoveObjectAt(0);
|
||||
}
|
||||
__android_log_print(ANDROID_LOG_INFO, "GeckoBridge", "leaving %s", __PRETTY_FUNCTION__);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1269,6 +1279,54 @@ AndroidBridge::GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInf
|
|||
mJNIEnv->ReleaseDoubleArrayElements(arr, info, 0);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidBridge::HandleGeckoMessage(const nsAString &aMessage, nsAString &aRet)
|
||||
{
|
||||
ALOG_BRIDGE("%s", __PRETTY_FUNCTION__);
|
||||
JNIEnv* env = AndroidBridge::AttachThread(false);
|
||||
if (!env) {
|
||||
ALOG_BRIDGE("no jni env in %s!!", __PRETTY_FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
AutoLocalJNIFrame jniFrame(1);
|
||||
jstring jMessage = mJNIEnv->NewString(nsPromiseFlatString(aMessage).get(), aMessage.Length());
|
||||
jstring returnMessage = static_cast<jstring>(env->CallStaticObjectMethod(mGeckoAppShellClass, jHandleGeckoMessage, jMessage));
|
||||
|
||||
jthrowable ex = env->ExceptionOccurred();
|
||||
if (ex) {
|
||||
env->ExceptionDescribe();
|
||||
env->ExceptionClear();
|
||||
}
|
||||
nsJNIString jniStr(returnMessage);
|
||||
aRet.Assign(jniStr);
|
||||
ALOG_BRIDGE("leaving %s", __PRETTY_FUNCTION__);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidBridge::CheckURIVisited(const nsAString& aURI)
|
||||
{
|
||||
AutoLocalJNIFrame jniFrame(1);
|
||||
jstring jstrURI = mJNIEnv->NewString(nsPromiseFlatString(aURI).get(), aURI.Length());
|
||||
mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jCheckUriVisited, jstrURI);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidBridge::MarkURIVisited(const nsAString& aURI)
|
||||
{
|
||||
AutoLocalJNIFrame jniFrame(1);
|
||||
jstring jstrURI = mJNIEnv->NewString(nsPromiseFlatString(aURI).get(), aURI.Length());
|
||||
mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jMarkUriVisited, jstrURI);
|
||||
}
|
||||
|
||||
void AndroidBridge::EmitGeckoAccessibilityEvent (PRInt32 eventType, const nsAString& role, const nsAString& text, const nsAString& description, bool enabled, bool checked, bool password) {
|
||||
AutoLocalJNIFrame jniFrame;
|
||||
jstring jstrRole = mJNIEnv->NewString(nsPromiseFlatString(role).get(), role.Length());
|
||||
jstring jstrText = mJNIEnv->NewString(nsPromiseFlatString(text).get(), text.Length());
|
||||
jstring jstrDescription = mJNIEnv->NewString(nsPromiseFlatString(description).get(), description.Length());
|
||||
mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jEmitGeckoAccessibilityEvent, eventType, jstrRole, jstrText, jstrDescription, enabled, checked, password);
|
||||
}
|
||||
|
||||
void *
|
||||
AndroidBridge::LockBitmap(jobject bitmap)
|
||||
{
|
||||
|
@ -1379,3 +1437,21 @@ AndroidBridge::UnlockWindow(void* window)
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Implementation file */
|
||||
NS_IMPL_ISUPPORTS1(nsAndroidBridge, nsIAndroidBridge)
|
||||
|
||||
nsAndroidBridge::nsAndroidBridge()
|
||||
{
|
||||
}
|
||||
|
||||
nsAndroidBridge::~nsAndroidBridge()
|
||||
{
|
||||
}
|
||||
|
||||
/* void handleGeckoEvent (in AString message); */
|
||||
NS_IMETHODIMP nsAndroidBridge::HandleGeckoMessage(const nsAString & message, nsAString &aRet NS_OUTPARAM)
|
||||
{
|
||||
AndroidBridge::Bridge()->HandleGeckoMessage(message, aRet);
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче