merge to latest

This commit is contained in:
John Daggett 2008-10-01 10:12:17 +09:00
commit 7af2a885ec
79 changed files with 4070 additions and 1281 deletions

View File

@ -101,7 +101,7 @@ interface nsIXMLHttpRequestUpload : nsIXMLHttpRequestEventTarget {
* you're aware of all the security implications. And then think twice about
* it.
*/
[scriptable, uuid(acda85ab-d06c-4176-b834-6d129ca97ca3)]
[scriptable, uuid(48ce10a0-c585-4e8f-a5f5-1ac1e47cc501)]
interface nsIXMLHttpRequest : nsISupports
{
/**
@ -321,6 +321,16 @@ interface nsIXMLHttpRequest : nsISupports
*/
attribute boolean mozBackgroundRequest;
/**
* When set to true attempts to make cross-site Access-Control requests
* with credentials such as cookies and authorization headers.
*
* Never affects same-site requests.
*
* Defaults to false.
*/
attribute boolean withCredentials;
/**
* Initialize the object for use from C++ code with the principal, script
* context, and owner window that should be used.

View File

@ -120,6 +120,7 @@ CPPSRCS = \
nsContentSink.cpp \
nsContentUtils.cpp \
nsCopySupport.cpp \
nsCrossSiteListenerProxy.cpp \
nsDataDocumentContentPolicy.cpp \
nsDOMAttribute.cpp \
nsDOMAttributeMap.cpp \

View File

@ -0,0 +1,421 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** 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.org code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Jonas Sicking <jonas@sicking.cc> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of 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 "nsCrossSiteListenerProxy.h"
#include "nsIChannel.h"
#include "nsIHttpChannel.h"
#include "nsDOMError.h"
#include "nsContentUtils.h"
#include "nsIScriptSecurityManager.h"
#include "nsNetUtil.h"
#include "nsIParser.h"
#include "nsParserCIID.h"
#include "nsICharsetAlias.h"
#include "nsMimeTypes.h"
#include "nsIStreamConverterService.h"
#include "nsStringStream.h"
#include "nsParserUtils.h"
#include "nsGkAtoms.h"
#include "nsWhitespaceTokenizer.h"
#include "nsIChannelEventSink.h"
#include "nsCommaSeparatedTokenizer.h"
static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
NS_IMPL_ISUPPORTS4(nsCrossSiteListenerProxy, nsIStreamListener,
nsIRequestObserver, nsIChannelEventSink,
nsIInterfaceRequestor)
nsCrossSiteListenerProxy::nsCrossSiteListenerProxy(nsIStreamListener* aOuter,
nsIPrincipal* aRequestingPrincipal,
nsIChannel* aChannel,
PRBool aWithCredentials,
nsresult* aResult)
: mOuterListener(aOuter),
mRequestingPrincipal(aRequestingPrincipal),
mWithCredentials(aWithCredentials),
mRequestApproved(PR_FALSE),
mHasBeenCrossSite(PR_FALSE),
mIsPreflight(PR_FALSE)
{
aChannel->GetNotificationCallbacks(getter_AddRefs(mOuterNotificationCallbacks));
aChannel->SetNotificationCallbacks(this);
*aResult = UpdateChannel(aChannel);
}
nsCrossSiteListenerProxy::nsCrossSiteListenerProxy(nsIStreamListener* aOuter,
nsIPrincipal* aRequestingPrincipal,
nsIChannel* aChannel,
PRBool aWithCredentials,
const nsCString& aPreflightMethod,
const nsTArray<nsCString>& aPreflightHeaders,
nsresult* aResult)
: mOuterListener(aOuter),
mRequestingPrincipal(aRequestingPrincipal),
mWithCredentials(aWithCredentials),
mRequestApproved(PR_FALSE),
mHasBeenCrossSite(PR_FALSE),
mIsPreflight(PR_TRUE),
mPreflightMethod(aPreflightMethod),
mPreflightHeaders(aPreflightHeaders)
{
aChannel->GetNotificationCallbacks(getter_AddRefs(mOuterNotificationCallbacks));
aChannel->SetNotificationCallbacks(this);
*aResult = UpdateChannel(aChannel);
}
NS_IMETHODIMP
nsCrossSiteListenerProxy::OnStartRequest(nsIRequest* aRequest,
nsISupports* aContext)
{
mRequestApproved = NS_SUCCEEDED(CheckRequestApproved(aRequest));
if (!mRequestApproved) {
aRequest->Cancel(NS_ERROR_DOM_BAD_URI);
mOuterListener->OnStartRequest(aRequest, aContext);
return NS_ERROR_DOM_BAD_URI;
}
return mOuterListener->OnStartRequest(aRequest, aContext);
}
PRBool
IsValidHTTPToken(const nsCSubstring& aToken)
{
if (aToken.IsEmpty()) {
return PR_FALSE;
}
nsCSubstring::const_char_iterator iter, end;
aToken.BeginReading(iter);
aToken.EndReading(end);
while (iter != end) {
if (*iter <= 32 ||
*iter >= 127 ||
*iter == '(' ||
*iter == ')' ||
*iter == '<' ||
*iter == '>' ||
*iter == '@' ||
*iter == ',' ||
*iter == ';' ||
*iter == ':' ||
*iter == '\\' ||
*iter == '\"' ||
*iter == '/' ||
*iter == '[' ||
*iter == ']' ||
*iter == '?' ||
*iter == '=' ||
*iter == '{' ||
*iter == '}') {
return PR_FALSE;
}
++iter;
}
return PR_TRUE;
}
nsresult
GetOrigin(nsIPrincipal* aPrincipal, nsCString& aOrigin)
{
aOrigin.AssignLiteral("null");
nsCString host;
nsCOMPtr<nsIURI> uri;
nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
rv = uri->GetAsciiHost(host);
NS_ENSURE_SUCCESS(rv, rv);
if (!host.IsEmpty()) {
nsCString scheme;
rv = uri->GetScheme(scheme);
NS_ENSURE_SUCCESS(rv, rv);
aOrigin = scheme + NS_LITERAL_CSTRING("://") + host;
// If needed, append the port
PRInt32 port;
uri->GetPort(&port);
if (port != -1) {
PRInt32 defaultPort = NS_GetDefaultPort(scheme.get());
if (port != defaultPort) {
aOrigin.Append(":");
aOrigin.AppendInt(port);
}
}
}
else {
aOrigin.AssignLiteral("null");
}
return NS_OK;
}
nsresult
nsCrossSiteListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
{
// Check if this was actually a cross domain request
if (!mHasBeenCrossSite) {
return NS_OK;
}
// Check if the request failed
nsresult status;
nsresult rv = aRequest->GetStatus(&status);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_SUCCESS(status, status);
// Test that things worked on a HTTP level
nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
NS_ENSURE_TRUE(http, NS_ERROR_DOM_BAD_URI);
PRBool succeeded;
rv = http->GetRequestSucceeded(&succeeded);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(succeeded, NS_ERROR_DOM_BAD_URI);
// Check the Access-Control-Allow-Origin header
nsCAutoString allowedOriginHeader;
rv = http->GetResponseHeader(
NS_LITERAL_CSTRING("Access-Control-Allow-Origin"), allowedOriginHeader);
NS_ENSURE_SUCCESS(rv, rv);
if (mWithCredentials || !allowedOriginHeader.EqualsLiteral("*")) {
nsCAutoString origin;
rv = GetOrigin(mRequestingPrincipal, origin);
NS_ENSURE_SUCCESS(rv, rv);
if (!allowedOriginHeader.Equals(origin)) {
return NS_ERROR_DOM_BAD_URI;
}
}
// Check Access-Control-Allow-Credentials header
if (mWithCredentials) {
nsCAutoString allowCredentialsHeader;
rv = http->GetResponseHeader(
NS_LITERAL_CSTRING("Access-Control-Allow-Credentials"), allowCredentialsHeader);
NS_ENSURE_SUCCESS(rv, rv);
if (!allowCredentialsHeader.EqualsLiteral("true")) {
return NS_ERROR_DOM_BAD_URI;
}
}
if (mIsPreflight) {
nsCAutoString headerVal;
// The "Access-Control-Allow-Methods" header contains a comma separated
// list of method names.
http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Methods"),
headerVal);
PRBool foundMethod = mPreflightMethod.EqualsLiteral("GET") ||
mPreflightMethod.EqualsLiteral("POST");
nsCCommaSeparatedTokenizer methodTokens(headerVal);
while(methodTokens.hasMoreTokens()) {
const nsDependentCSubstring& method = methodTokens.nextToken();
if (method.IsEmpty()) {
continue;
}
if (!IsValidHTTPToken(method)) {
return NS_ERROR_DOM_BAD_URI;
}
foundMethod |= mPreflightMethod.Equals(method);
}
NS_ENSURE_TRUE(foundMethod, NS_ERROR_DOM_BAD_URI);
// The "Access-Control-Allow-Headers" header contains a comma separated
// list of header names.
http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"),
headerVal);
nsTArray<nsCString> headers;
nsCCommaSeparatedTokenizer headerTokens(headerVal);
while(headerTokens.hasMoreTokens()) {
const nsDependentCSubstring& header = headerTokens.nextToken();
if (header.IsEmpty()) {
continue;
}
if (!IsValidHTTPToken(header)) {
return NS_ERROR_DOM_BAD_URI;
}
headers.AppendElement(header);
}
for (PRUint32 i = 0; i < mPreflightHeaders.Length(); ++i) {
if (!headers.Contains(mPreflightHeaders[i],
nsCaseInsensitiveCStringArrayComparator())) {
return NS_ERROR_DOM_BAD_URI;
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsCrossSiteListenerProxy::OnStopRequest(nsIRequest* aRequest,
nsISupports* aContext,
nsresult aStatusCode)
{
return mOuterListener->OnStopRequest(aRequest, aContext, aStatusCode);
}
NS_IMETHODIMP
nsCrossSiteListenerProxy::OnDataAvailable(nsIRequest* aRequest,
nsISupports* aContext,
nsIInputStream* aInputStream,
PRUint32 aOffset,
PRUint32 aCount)
{
if (!mRequestApproved) {
return NS_ERROR_DOM_BAD_URI;
}
return mOuterListener->OnDataAvailable(aRequest, aContext, aInputStream,
aOffset, aCount);
}
NS_IMETHODIMP
nsCrossSiteListenerProxy::GetInterface(const nsIID & aIID, void **aResult)
{
if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
*aResult = static_cast<nsIChannelEventSink*>(this);
NS_ADDREF_THIS();
return NS_OK;
}
return mOuterNotificationCallbacks ?
mOuterNotificationCallbacks->GetInterface(aIID, aResult) :
NS_ERROR_NO_INTERFACE;
}
NS_IMETHODIMP
nsCrossSiteListenerProxy::OnChannelRedirect(nsIChannel *aOldChannel,
nsIChannel *aNewChannel,
PRUint32 aFlags)
{
nsresult rv = CheckRequestApproved(aOldChannel);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIChannelEventSink> outer =
do_GetInterface(mOuterNotificationCallbacks);
if (outer) {
rv = outer->OnChannelRedirect(aOldChannel, aNewChannel, aFlags);
NS_ENSURE_SUCCESS(rv, rv);
}
return UpdateChannel(aNewChannel);
}
nsresult
nsCrossSiteListenerProxy::UpdateChannel(nsIChannel* aChannel)
{
nsCOMPtr<nsIURI> uri;
nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
// Check that the uri is ok to load
rv = nsContentUtils::GetSecurityManager()->
CheckLoadURIWithPrincipal(mRequestingPrincipal, uri,
nsIScriptSecurityManager::STANDARD);
NS_ENSURE_SUCCESS(rv, rv);
if (!mHasBeenCrossSite &&
NS_SUCCEEDED(mRequestingPrincipal->CheckMayLoad(uri, PR_FALSE))) {
return NS_OK;
}
// It's a cross site load
mHasBeenCrossSite = PR_TRUE;
nsCString userpass;
uri->GetUserPass(userpass);
NS_ENSURE_TRUE(userpass.IsEmpty(), NS_ERROR_DOM_BAD_URI);
// Add the Origin header
nsCAutoString origin;
rv = GetOrigin(mRequestingPrincipal, origin);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aChannel);
NS_ENSURE_TRUE(http, NS_ERROR_FAILURE);
rv = http->SetRequestHeader(NS_LITERAL_CSTRING("Origin"), origin, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
// Add preflight headers if this is a preflight request
if (mIsPreflight) {
rv = http->
SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Method"),
mPreflightMethod, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
if (!mPreflightHeaders.IsEmpty()) {
nsCAutoString headers;
for (PRUint32 i = 0; i < mPreflightHeaders.Length(); ++i) {
if (i != 0) {
headers += ',';
}
headers += mPreflightHeaders[i];
}
rv = http->
SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Headers"),
headers, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
}
}
// Make cookie-less if needed
if (mIsPreflight || !mWithCredentials) {
nsLoadFlags flags;
rv = http->GetLoadFlags(&flags);
NS_ENSURE_SUCCESS(rv, rv);
flags |= nsIRequest::LOAD_ANONYMOUS;
rv = http->SetLoadFlags(flags);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}

View File

@ -0,0 +1,94 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** 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.org code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Jonas Sicking <jonas@sicking.cc> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of 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 "nsIStreamListener.h"
#include "nsIInterfaceRequestor.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsIURI.h"
#include "nsTArray.h"
#include "nsIContentSink.h"
#include "nsIXMLContentSink.h"
#include "nsIExpatSink.h"
#include "nsIInterfaceRequestor.h"
#include "nsIChannelEventSink.h"
class nsIURI;
class nsIParser;
class nsIPrincipal;
extern PRBool
IsValidHTTPToken(const nsCSubstring& aToken);
class nsCrossSiteListenerProxy : public nsIStreamListener,
public nsIInterfaceRequestor,
public nsIChannelEventSink
{
public:
nsCrossSiteListenerProxy(nsIStreamListener* aOuter,
nsIPrincipal* aRequestingPrincipal,
nsIChannel* aChannel,
PRBool aWithCredentials,
nsresult* aResult);
nsCrossSiteListenerProxy(nsIStreamListener* aOuter,
nsIPrincipal* aRequestingPrincipal,
nsIChannel* aChannel,
PRBool aWithCredentials,
const nsCString& aPreflightMethod,
const nsTArray<nsCString>& aPreflightHeaders,
nsresult* aResult);
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSICHANNELEVENTSINK
private:
nsresult UpdateChannel(nsIChannel* aChannel);
nsresult CheckRequestApproved(nsIRequest* aRequest);
nsCOMPtr<nsIStreamListener> mOuterListener;
nsCOMPtr<nsIPrincipal> mRequestingPrincipal;
nsCOMPtr<nsIInterfaceRequestor> mOuterNotificationCallbacks;
PRBool mWithCredentials;
PRBool mRequestApproved;
PRBool mHasBeenCrossSite;
PRBool mIsPreflight;
nsCString mPreflightMethod;
nsTArray<nsCString> mPreflightHeaders;
};

View File

@ -59,6 +59,7 @@
#include "nsAutoPtr.h"
#include "nsLoadListenerProxy.h"
#include "nsStreamUtils.h"
#include "nsCrossSiteListenerProxy.h"
/**
* This class manages loading a single XML document
@ -219,12 +220,10 @@ nsSyncLoader::LoadDocument(nsIChannel* aChannel,
}
if (aLoaderPrincipal) {
nsCOMPtr<nsIURI> docURI;
rv = aChannel->GetOriginalURI(getter_AddRefs(docURI));
NS_ENSURE_SUCCESS(rv, rv);
rv = aLoaderPrincipal->CheckMayLoad(docURI, PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
listener = new nsCrossSiteListenerProxy(listener, aLoaderPrincipal,
mChannel, PR_FALSE, &rv);
NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_SUCCESS(rv, rv);
}
// Register as a load listener on the document
@ -370,18 +369,6 @@ nsSyncLoader::OnChannelRedirect(nsIChannel *aOldChannel,
mChannel = aNewChannel;
nsCOMPtr<nsIURI> oldURI;
nsresult rv = aOldChannel->GetURI(getter_AddRefs(oldURI));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> newURI;
rv = aNewChannel->GetURI(getter_AddRefs(newURI));
NS_ENSURE_SUCCESS(rv, rv);
rv = nsContentUtils::GetSecurityManager()->
CheckSameOriginURI(oldURI, newURI, PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}

View File

@ -81,15 +81,16 @@
#include "nsContentPolicyUtils.h"
#include "nsContentErrors.h"
#include "nsLayoutStatics.h"
#include "nsCrossSiteListenerProxy.h"
#include "nsDOMError.h"
#include "nsIHTMLDocument.h"
#include "nsIDOM3Document.h"
#include "nsWhitespaceTokenizer.h"
#include "nsIMultiPartChannel.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsIStorageStream.h"
#include "nsIPromptFactory.h"
#include "nsIWindowWatcher.h"
#include "nsCommaSeparatedTokenizer.h"
#define LOAD_STR "load"
#define ERROR_STR "error"
@ -124,6 +125,9 @@
// This is set when we've got the headers for a multipart XMLHttpRequest,
// but haven't yet started to process the first part.
#define XML_HTTP_REQUEST_MPART_HEADERS (1 << 15) // Internal
#define XML_HTTP_REQUEST_USE_XSITE_AC (1 << 16) // Internal
#define XML_HTTP_REQUEST_NEED_AC_PREFLIGHT (1 << 17) // Internal
#define XML_HTTP_REQUEST_AC_WITH_CREDENTIALS (1 << 18) // Internal
#define XML_HTTP_REQUEST_LOADSTATES \
(XML_HTTP_REQUEST_UNINITIALIZED | \
@ -134,6 +138,8 @@
XML_HTTP_REQUEST_SENT | \
XML_HTTP_REQUEST_STOPPED)
#define ACCESS_CONTROL_CACHE_SIZE 100
#define NS_BADCERTHANDLER_CONTRACTID \
"@mozilla.org/content/xmlhttprequest-bad-cert-handler;1"
@ -277,6 +283,220 @@ nsMultipartProxyListener::OnDataAvailable(nsIRequest *aRequest,
count);
}
// Class used as streamlistener and notification callback when
// doing the initial GET request for an access-control check
class nsACProxyListener : public nsIStreamListener,
public nsIInterfaceRequestor,
public nsIChannelEventSink
{
public:
nsACProxyListener(nsIChannel* aOuterChannel,
nsIStreamListener* aOuterListener,
nsISupports* aOuterContext,
nsIPrincipal* aReferrerPrincipal,
const nsACString& aRequestMethod,
PRBool aWithCredentials)
: mOuterChannel(aOuterChannel), mOuterListener(aOuterListener),
mOuterContext(aOuterContext), mReferrerPrincipal(aReferrerPrincipal),
mRequestMethod(aRequestMethod), mWithCredentials(aWithCredentials)
{ }
NS_DECL_ISUPPORTS
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSICHANNELEVENTSINK
private:
void AddResultToCache(nsIRequest* aRequest);
nsCOMPtr<nsIChannel> mOuterChannel;
nsCOMPtr<nsIStreamListener> mOuterListener;
nsCOMPtr<nsISupports> mOuterContext;
nsCOMPtr<nsIPrincipal> mReferrerPrincipal;
nsCString mRequestMethod;
PRBool mWithCredentials;
};
NS_IMPL_ISUPPORTS4(nsACProxyListener, nsIStreamListener, nsIRequestObserver,
nsIInterfaceRequestor, nsIChannelEventSink)
void
nsACProxyListener::AddResultToCache(nsIRequest *aRequest)
{
nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
NS_ASSERTION(http, "Request was not http");
// The "Access-Control-Max-Age" header should return an age in seconds.
nsCAutoString headerVal;
http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Max-Age"),
headerVal);
if (headerVal.IsEmpty()) {
return;
}
// Sanitize the string. We only allow 'delta-seconds' as specified by
// http://dev.w3.org/2006/waf/access-control (digits 0-9 with no leading or
// trailing non-whitespace characters).
PRUint32 age = 0;
nsCSubstring::const_char_iterator iter, end;
headerVal.BeginReading(iter);
headerVal.EndReading(end);
while (iter != end) {
if (*iter < '0' || *iter > '9') {
return;
}
age = age * 10 + (*iter - '0');
// Cap at 24 hours. This also avoids overflow
age = PR_MIN(age, 86400);
++iter;
}
if (!age || !nsXMLHttpRequest::EnsureACCache()) {
return;
}
// String seems fine, go ahead and cache.
// Note that we have already checked that these headers follow the correct
// syntax.
nsCOMPtr<nsIURI> uri;
http->GetURI(getter_AddRefs(uri));
// PR_Now gives microseconds
PRTime expirationTime = PR_Now() + (PRUint64)age * PR_USEC_PER_SEC;
nsAccessControlLRUCache::CacheEntry* entry =
nsXMLHttpRequest::sAccessControlCache->
GetEntry(uri, mReferrerPrincipal, mWithCredentials, PR_TRUE);
if (!entry) {
return;
}
// The "Access-Control-Allow-Methods" header contains a comma separated
// list of method names.
http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Methods"),
headerVal);
nsCCommaSeparatedTokenizer methods(headerVal);
while(methods.hasMoreTokens()) {
const nsDependentCSubstring& method = methods.nextToken();
if (method.IsEmpty()) {
continue;
}
PRUint32 i;
for (i = 0; i < entry->mMethods.Length(); ++i) {
if (entry->mMethods[i].token.Equals(method)) {
entry->mMethods[i].expirationTime = expirationTime;
break;
}
}
if (i == entry->mMethods.Length()) {
nsAccessControlLRUCache::TokenTime* newMethod =
entry->mMethods.AppendElement();
if (!newMethod) {
return;
}
newMethod->token = method;
newMethod->expirationTime = expirationTime;
}
}
// The "Access-Control-Allow-Headers" header contains a comma separated
// list of method names.
http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"),
headerVal);
nsCCommaSeparatedTokenizer headers(headerVal);
while(headers.hasMoreTokens()) {
const nsDependentCSubstring& header = headers.nextToken();
if (header.IsEmpty()) {
continue;
}
PRUint32 i;
for (i = 0; i < entry->mHeaders.Length(); ++i) {
if (entry->mHeaders[i].token.Equals(header)) {
entry->mHeaders[i].expirationTime = expirationTime;
break;
}
}
if (i == entry->mHeaders.Length()) {
nsAccessControlLRUCache::TokenTime* newHeader =
entry->mHeaders.AppendElement();
if (!newHeader) {
return;
}
newHeader->token = header;
newHeader->expirationTime = expirationTime;
}
}
}
NS_IMETHODIMP
nsACProxyListener::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
{
nsresult status;
nsresult rv = aRequest->GetStatus(&status);
if (NS_SUCCEEDED(rv)) {
rv = status;
}
if (NS_SUCCEEDED(rv)) {
// Everything worked, try to cache and then fire off the actual request.
AddResultToCache(aRequest);
rv = mOuterChannel->AsyncOpen(mOuterListener, mOuterContext);
}
if (NS_FAILED(rv)) {
mOuterChannel->Cancel(rv);
mOuterListener->OnStartRequest(mOuterChannel, mOuterContext);
mOuterListener->OnStopRequest(mOuterChannel, mOuterContext, rv);
return rv;
}
return NS_OK;
}
NS_IMETHODIMP
nsACProxyListener::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
nsresult aStatus)
{
return NS_OK;
}
/** nsIStreamListener methods **/
NS_IMETHODIMP
nsACProxyListener::OnDataAvailable(nsIRequest *aRequest,
nsISupports *ctxt,
nsIInputStream *inStr,
PRUint32 sourceOffset,
PRUint32 count)
{
return NS_OK;
}
NS_IMETHODIMP
nsACProxyListener::OnChannelRedirect(nsIChannel *aOldChannel,
nsIChannel *aNewChannel,
PRUint32 aFlags)
{
// No redirects allowed for now.
return NS_ERROR_DOM_BAD_URI;
}
NS_IMETHODIMP
nsACProxyListener::GetInterface(const nsIID & aIID, void **aResult)
{
return QueryInterface(aIID, aResult);
}
/**
* Gets the nsIDocument given the script context. Will return nsnull on failure.
*
@ -617,11 +837,201 @@ NS_INTERFACE_MAP_END_INHERITING(nsXHREventTarget)
NS_IMPL_ADDREF_INHERITED(nsXMLHttpRequestUpload, nsXHREventTarget)
NS_IMPL_RELEASE_INHERITED(nsXMLHttpRequestUpload, nsXHREventTarget)
void
nsAccessControlLRUCache::CacheEntry::PurgeExpired(PRTime now)
{
PRUint32 i;
for (i = 0; i < mMethods.Length(); ++i) {
if (now >= mMethods[i].expirationTime) {
mMethods.RemoveElementAt(i--);
}
}
for (i = 0; i < mHeaders.Length(); ++i) {
if (now >= mHeaders[i].expirationTime) {
mHeaders.RemoveElementAt(i--);
}
}
}
PRBool
nsAccessControlLRUCache::CacheEntry::CheckRequest(const nsCString& aMethod,
const nsTArray<nsCString>& aHeaders)
{
PurgeExpired(PR_Now());
if (!aMethod.EqualsLiteral("GET") && !aMethod.EqualsLiteral("POST")) {
PRUint32 i;
for (i = 0; i < mMethods.Length(); ++i) {
if (aMethod.Equals(mMethods[i].token))
break;
}
if (i == mMethods.Length()) {
return PR_FALSE;
}
}
for (PRUint32 i = 0; i < aHeaders.Length(); ++i) {
PRUint32 j;
for (j = 0; j < mHeaders.Length(); ++j) {
if (aHeaders[i].Equals(mHeaders[j].token,
nsCaseInsensitiveCStringComparator())) {
break;
}
}
if (j == mHeaders.Length()) {
return PR_FALSE;
}
}
return PR_TRUE;
}
nsAccessControlLRUCache::CacheEntry*
nsAccessControlLRUCache::GetEntry(nsIURI* aURI,
nsIPrincipal* aPrincipal,
PRBool aWithCredentials,
PRBool aCreate)
{
nsCString key;
if (!GetCacheKey(aURI, aPrincipal, aWithCredentials, key)) {
NS_WARNING("Invalid cache key!");
return nsnull;
}
CacheEntry* entry;
if (mTable.Get(key, &entry)) {
// Entry already existed so just return it. Also update the LRU list.
// Move to the head of the list.
PR_REMOVE_LINK(entry);
PR_INSERT_LINK(entry, &mList);
return entry;
}
if (!aCreate) {
return nsnull;
}
// This is a new entry, allocate and insert into the table now so that any
// failures don't cause items to be removed from a full cache.
entry = new CacheEntry(key);
if (!entry) {
NS_WARNING("Failed to allocate new cache entry!");
return nsnull;
}
if (!mTable.Put(key, entry)) {
// Failed, clean up the new entry.
delete entry;
NS_WARNING("Failed to add entry to the access control cache!");
return nsnull;
}
PR_INSERT_LINK(entry, &mList);
NS_ASSERTION(mTable.Count() <= ACCESS_CONTROL_CACHE_SIZE + 1,
"Something is borked, too many entries in the cache!");
// Now enforce the max count.
if (mTable.Count() > ACCESS_CONTROL_CACHE_SIZE) {
// Try to kick out all the expired entries.
PRTime now = PR_Now();
mTable.Enumerate(RemoveExpiredEntries, &now);
// If that didn't remove anything then kick out the least recently used
// entry.
if (mTable.Count() > ACCESS_CONTROL_CACHE_SIZE) {
CacheEntry* lruEntry = static_cast<CacheEntry*>(PR_LIST_TAIL(&mList));
PR_REMOVE_LINK(lruEntry);
// This will delete 'lruEntry'.
mTable.Remove(lruEntry->mKey);
NS_ASSERTION(mTable.Count() >= ACCESS_CONTROL_CACHE_SIZE,
"Somehow tried to remove an entry that was never added!");
}
}
return entry;
}
void
nsAccessControlLRUCache::Clear()
{
PR_INIT_CLIST(&mList);
mTable.Clear();
}
/* static */ PR_CALLBACK PLDHashOperator
nsAccessControlLRUCache::RemoveExpiredEntries(const nsACString& aKey,
nsAutoPtr<CacheEntry>& aValue,
void* aUserData)
{
PRTime* now = static_cast<PRTime*>(aUserData);
aValue->PurgeExpired(*now);
if (aValue->mHeaders.IsEmpty() &&
aValue->mHeaders.IsEmpty()) {
// Expired, remove from the list as well as the hash table.
PR_REMOVE_LINK(aValue);
return PL_DHASH_REMOVE;
}
return PL_DHASH_NEXT;
}
/* static */ PRBool
nsAccessControlLRUCache::GetCacheKey(nsIURI* aURI,
nsIPrincipal* aPrincipal,
PRBool aWithCredentials,
nsACString& _retval)
{
NS_ASSERTION(aURI, "Null uri!");
NS_ASSERTION(aPrincipal, "Null principal!");
NS_NAMED_LITERAL_CSTRING(space, " ");
nsCOMPtr<nsIURI> uri;
nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, PR_FALSE);
nsCAutoString scheme, host, port;
if (uri) {
uri->GetScheme(scheme);
uri->GetHost(host);
port.AppendInt(NS_GetRealPort(uri));
}
nsCAutoString cred;
if (aWithCredentials) {
_retval.AssignLiteral("cred");
}
else {
_retval.AssignLiteral("nocred");
}
nsCAutoString spec;
rv = aURI->GetSpec(spec);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
_retval.Assign(cred + space + scheme + space + host + space + port + space +
spec);
return PR_TRUE;
}
/////////////////////////////////////////////
//
//
/////////////////////////////////////////////
// Will be initialized in nsXMLHttpRequest::EnsureACCache.
nsAccessControlLRUCache* nsXMLHttpRequest::sAccessControlCache = nsnull;
nsXMLHttpRequest::nsXMLHttpRequest()
: mRequestObserver(nsnull), mState(XML_HTTP_REQUEST_UNINITIALIZED),
mUploadTransferred(0), mUploadTotal(0), mUploadComplete(PR_TRUE),
@ -1003,6 +1413,20 @@ NS_IMETHODIMP nsXMLHttpRequest::GetResponseText(nsAString& aResponseText)
NS_IMETHODIMP
nsXMLHttpRequest::GetStatus(PRUint32 *aStatus)
{
*aStatus = 0;
if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
// Make sure we don't leak status information from denied cross-site
// requests.
if (mChannel) {
nsresult status;
mChannel->GetStatus(&status);
if (NS_FAILED(status)) {
return NS_OK;
}
}
}
nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
if (httpChannel) {
@ -1021,7 +1445,6 @@ nsXMLHttpRequest::GetStatus(PRUint32 *aStatus)
return rv;
}
*aStatus = 0;
return NS_OK;
}
@ -1053,6 +1476,9 @@ nsXMLHttpRequest::Abort()
if (mChannel) {
mChannel->Cancel(NS_BINDING_ABORTED);
}
if (mACGetChannel) {
mACGetChannel->Cancel(NS_BINDING_ABORTED);
}
mDocument = nsnull;
mResponseBody.Truncate();
mState |= XML_HTTP_REQUEST_ABORTED;
@ -1092,6 +1518,10 @@ nsXMLHttpRequest::GetAllResponseHeaders(char **_retval)
NS_ENSURE_ARG_POINTER(_retval);
*_retval = nsnull;
if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
return NS_OK;
}
nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
if (httpChannel) {
@ -1120,6 +1550,37 @@ nsXMLHttpRequest::GetResponseHeader(const nsACString& header,
nsresult rv = NS_OK;
_retval.Truncate();
// Check for dangerous headers
if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
// Make sure we don't leak header information from denied cross-site
// requests.
if (mChannel) {
nsresult status;
mChannel->GetStatus(&status);
if (NS_FAILED(status)) {
return NS_OK;
}
}
const char *kCrossOriginSafeHeaders[] = {
"cache-control", "content-language", "content-type", "expires",
"last-modified", "pragma"
};
PRBool safeHeader = PR_FALSE;
PRUint32 i;
for (i = 0; i < NS_ARRAY_LENGTH(kCrossOriginSafeHeaders); ++i) {
if (header.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) {
safeHeader = PR_TRUE;
break;
}
}
if (!safeHeader) {
return NS_OK;
}
}
nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
if (httpChannel) {
@ -1201,7 +1662,10 @@ nsXMLHttpRequest::DispatchProgressEvent(nsPIDOMEventTarget* aTarget,
PRUint64 aLoaded, PRUint64 aTotal,
PRUint64 aPosition, PRUint64 aTotalSize)
{
if (aType.IsEmpty()) {
NS_ASSERTION(aTarget, "null target");
if (aType.IsEmpty() ||
(!AllowUploadProgress() &&
(aTarget == mUpload || aType.EqualsLiteral(UPLOADPROGRESS_STR)))) {
return;
}
@ -1264,25 +1728,34 @@ IsSystemPrincipal(nsIPrincipal* aPrincipal)
}
static PRBool
IsSameOrigin(nsIPrincipal* aPrincipal, nsIChannel* aChannel)
CheckMayLoad(nsIPrincipal* aPrincipal, nsIChannel* aChannel)
{
NS_ASSERTION(!IsSystemPrincipal(aPrincipal), "Shouldn't get here!");
nsCOMPtr<nsIURI> codebase;
nsresult rv = aPrincipal->GetURI(getter_AddRefs(codebase));
NS_ENSURE_SUCCESS(rv, PR_FALSE);
NS_ASSERTION(codebase, "Principal must have a URI!");
nsCOMPtr<nsIURI> channelURI;
rv = aChannel->GetURI(getter_AddRefs(channelURI));
nsresult rv = aChannel->GetURI(getter_AddRefs(channelURI));
NS_ENSURE_SUCCESS(rv, PR_FALSE);
rv = nsContentUtils::GetSecurityManager()->
CheckSameOriginURI(codebase, channelURI, PR_FALSE);
rv = aPrincipal->CheckMayLoad(channelURI, PR_FALSE);
return NS_SUCCEEDED(rv);
}
nsresult
nsXMLHttpRequest::CheckChannelForCrossSiteRequest(nsIChannel* aChannel)
{
// First check if this is a same-origin request, or if cross-site requests
// are enabled.
if ((mState & XML_HTTP_REQUEST_XSITEENABLED) ||
CheckMayLoad(mPrincipal, aChannel)) {
return NS_OK;
}
// This is a cross-site request
mState |= XML_HTTP_REQUEST_USE_XSITE_AC;
return NS_OK;
}
/* noscript void openRequest (in AUTF8String method, in AUTF8String url, in boolean async, in AString user, in AString password); */
NS_IMETHODIMP
nsXMLHttpRequest::OpenRequest(const nsACString& method,
@ -1444,18 +1917,11 @@ nsXMLHttpRequest::Open(const nsACString& method, const nsACString& url)
rv = NS_NewURI(getter_AddRefs(targetURI), url, nsnull, GetBaseURI());
if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
if (!secMan) {
return NS_ERROR_FAILURE;
}
// Find out if UniversalBrowserRead privileges are enabled
if (nsContentUtils::IsCallerTrustedForRead()) {
mState |= XML_HTTP_REQUEST_XSITEENABLED;
} else {
mState &= ~XML_HTTP_REQUEST_XSITEENABLED;
rv = mPrincipal->CheckMayLoad(targetURI, PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
}
if (argc > 2) {
@ -1635,6 +2101,13 @@ nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
doc->SetPrincipal(documentPrincipal);
}
if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mDocument);
if (htmlDoc) {
htmlDoc->DisableCookieAccess();
}
}
// Reset responseBody
mResponseBody.Truncate();
@ -2109,6 +2582,75 @@ nsXMLHttpRequest::Send(nsIVariant *aBody)
mState |= XML_HTTP_REQUEST_SYNCLOOPING;
}
rv = CheckChannelForCrossSiteRequest(mChannel);
NS_ENSURE_SUCCESS(rv, rv);
PRBool withCredentials = !!(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS);
if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
// Check if we need to do a preflight request.
NS_ENSURE_TRUE(httpChannel, NS_ERROR_DOM_BAD_URI);
nsCAutoString method;
httpChannel->GetRequestMethod(method);
if (!mACUnsafeHeaders.IsEmpty() ||
HasListenersFor(NS_LITERAL_STRING(UPLOADPROGRESS_STR)) ||
(mUpload && mUpload->HasListeners())) {
mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT;
}
else if (method.LowerCaseEqualsLiteral("post")) {
nsCAutoString contentTypeHeader;
httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
contentTypeHeader);
nsCAutoString contentType, charset;
NS_ParseContentType(contentTypeHeader, contentType, charset);
NS_ENSURE_SUCCESS(rv, rv);
if (!contentType.LowerCaseEqualsLiteral("text/plain")) {
mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT;
}
}
else if (!method.LowerCaseEqualsLiteral("get")) {
mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT;
}
// If so, set up the preflight
if (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT) {
// Check to see if this initial OPTIONS request has already been cached
// in our special Access Control Cache.
nsCOMPtr<nsIURI> uri;
rv = mChannel->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
nsAccessControlLRUCache::CacheEntry* entry =
sAccessControlCache ?
sAccessControlCache->GetEntry(uri, mPrincipal, withCredentials, PR_FALSE) :
nsnull;
if (!entry || !entry->CheckRequest(method, mACUnsafeHeaders)) {
// Either it wasn't cached or the cached result has expired. Build a
// channel for the OPTIONS request.
nsCOMPtr<nsILoadGroup> loadGroup;
GetLoadGroup(getter_AddRefs(loadGroup));
nsLoadFlags loadFlags;
rv = mChannel->GetLoadFlags(&loadFlags);
NS_ENSURE_SUCCESS(rv, rv);
rv = NS_NewChannel(getter_AddRefs(mACGetChannel), uri, nsnull,
loadGroup, nsnull, loadFlags);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIHttpChannel> acHttp = do_QueryInterface(mACGetChannel);
NS_ASSERTION(acHttp, "Failed to QI to nsIHttpChannel!");
rv = acHttp->SetRequestMethod(NS_LITERAL_CSTRING("OPTIONS"));
NS_ENSURE_SUCCESS(rv, rv);
}
}
}
// Hook us up to listen to redirects and the like
mChannel->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
mChannel->SetNotificationCallbacks(this);
@ -2122,6 +2664,15 @@ nsXMLHttpRequest::Send(nsIVariant *aBody)
}
}
if (!(mState & XML_HTTP_REQUEST_XSITEENABLED)) {
// Always create a nsCrossSiteListenerProxy here even if it's
// a same-origin request right now, since it could be redirected.
listener = new nsCrossSiteListenerProxy(listener, mPrincipal, mChannel,
withCredentials, &rv);
NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_SUCCESS(rv, rv);
}
// Bypass the network cache in cases where it makes no sense:
// 1) Multipart responses are very large and would likely be doomed by the
// cache once they grow too large, so they are not worth caching.
@ -2138,6 +2689,10 @@ nsXMLHttpRequest::Send(nsIVariant *aBody)
else if (mState & XML_HTTP_REQUEST_SYNCLOOPING) {
AddLoadFlags(mChannel,
nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
if (mACGetChannel) {
AddLoadFlags(mACGetChannel,
nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
}
}
// Since we expect XML data, set the type hint accordingly
@ -2145,12 +2700,32 @@ nsXMLHttpRequest::Send(nsIVariant *aBody)
// ignoring return value, as this is not critical
mChannel->SetContentType(NS_LITERAL_CSTRING("application/xml"));
// Start reading from the channel
rv = mChannel->AsyncOpen(listener, nsnull);
// If we're doing a cross-site non-GET request we need to first do
// a GET request to the same URI. Set that up if needed
if (mACGetChannel) {
nsCOMPtr<nsIStreamListener> acProxyListener =
new nsACProxyListener(mChannel, listener, nsnull, mPrincipal, method,
withCredentials);
NS_ENSURE_TRUE(acProxyListener, NS_ERROR_OUT_OF_MEMORY);
acProxyListener =
new nsCrossSiteListenerProxy(acProxyListener, mPrincipal, mACGetChannel,
withCredentials, method, mACUnsafeHeaders,
&rv);
NS_ENSURE_TRUE(acProxyListener, NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_SUCCESS(rv, rv);
rv = mACGetChannel->AsyncOpen(acProxyListener, nsnull);
}
else {
// Start reading from the channel
rv = mChannel->AsyncOpen(listener, nsnull);
}
if (NS_FAILED(rv)) {
// Drop our ref to the channel to avoid cycles
mChannel = nsnull;
mACGetChannel = nsnull;
return rv;
}
@ -2192,6 +2767,24 @@ nsXMLHttpRequest::SetRequestHeader(const nsACString& header,
{
nsresult rv;
// Make sure we don't store an invalid header name in mACUnsafeHeaders
if (!IsValidHTTPToken(header)) {
return NS_ERROR_FAILURE;
}
// Check that we haven't already opened the channel. We can't rely on
// the channel throwing from mChannel->SetRequestHeader since we might
// still be waiting for mACGetChannel to actually open mChannel
if (mACGetChannel) {
PRBool pending;
rv = mACGetChannel->IsPending(&pending);
NS_ENSURE_SUCCESS(rv, rv);
if (pending) {
return NS_ERROR_IN_PROGRESS;
}
}
if (!mChannel) // open() initializes mChannel, and open()
return NS_ERROR_FAILURE; // must be called before first setRequestHeader()
@ -2213,8 +2806,7 @@ nsXMLHttpRequest::SetRequestHeader(const nsACString& header,
const char *kInvalidHeaders[] = {
"accept-charset", "accept-encoding", "connection", "content-length",
"content-transfer-encoding", "date", "expect", "host", "keep-alive",
"referer", "access-control-origin", "te", "trailer",
"transfer-encoding", "upgrade", "via", "xmlhttprequest-security-check"
"referer", "te", "trailer", "transfer-encoding", "upgrade", "via"
};
PRUint32 i;
for (i = 0; i < NS_ARRAY_LENGTH(kInvalidHeaders); ++i) {
@ -2230,6 +2822,24 @@ nsXMLHttpRequest::SetRequestHeader(const nsACString& header,
NS_WARNING("refusing to set request header");
return NS_OK;
}
// Check for dangerous cross-site headers
PRBool safeHeader = !!(mState & XML_HTTP_REQUEST_XSITEENABLED);
if (!safeHeader) {
const char *kCrossOriginSafeHeaders[] = {
"accept", "accept-language", "content-type"
};
for (i = 0; i < NS_ARRAY_LENGTH(kCrossOriginSafeHeaders); ++i) {
if (header.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) {
safeHeader = PR_TRUE;
break;
}
}
}
if (!safeHeader) {
mACUnsafeHeaders.AppendElement(header);
}
}
// We need to set, not add to, the header.
@ -2331,6 +2941,33 @@ nsXMLHttpRequest::SetMozBackgroundRequest(PRBool aMozBackgroundRequest)
return NS_OK;
}
/* attribute boolean withCredentials; */
NS_IMETHODIMP
nsXMLHttpRequest::GetWithCredentials(PRBool *_retval)
{
*_retval = !!(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS);
return NS_OK;
}
/* attribute boolean withCredentials; */
NS_IMETHODIMP
nsXMLHttpRequest::SetWithCredentials(PRBool aWithCredentials)
{
// Return error if we're already processing a request
if (XML_HTTP_REQUEST_SENT & mState) {
return NS_ERROR_FAILURE;
}
if (aWithCredentials) {
mState |= XML_HTTP_REQUEST_AC_WITH_CREDENTIALS;
}
else {
mState &= ~XML_HTTP_REQUEST_AC_WITH_CREDENTIALS;
}
return NS_OK;
}
// nsIDOMEventListener
nsresult
@ -2439,21 +3076,14 @@ nsXMLHttpRequest::OnChannelRedirect(nsIChannel *aOldChannel,
nsresult rv;
if (!(mState & XML_HTTP_REQUEST_XSITEENABLED)) {
nsCOMPtr<nsIURI> oldURI;
rv = aOldChannel->GetURI(getter_AddRefs(oldURI));
NS_ENSURE_SUCCESS(rv, rv);
rv = CheckChannelForCrossSiteRequest(aNewChannel);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> newURI;
rv = aNewChannel->GetURI(getter_AddRefs(newURI));
NS_ENSURE_SUCCESS(rv, rv);
rv = nsContentUtils::GetSecurityManager()->
CheckSameOriginURI(oldURI, newURI, PR_TRUE);
if (NS_FAILED(rv)) {
mErrorLoad = PR_TRUE;
return rv;
}
// Disable redirects for preflighted cross-site requests entirely for now
// Note, do this after the call to CheckChannelForCrossSiteRequest
// to make sure that XML_HTTP_REQUEST_USE_XSITE_AC is up-to-date
if ((mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT)) {
return NS_ERROR_DOM_BAD_URI;
}
if (mChannelEventSink) {
@ -2533,6 +3163,13 @@ nsXMLHttpRequest::OnStatus(nsIRequest *aRequest, nsISupports *aContext, nsresult
return NS_OK;
}
PRBool
nsXMLHttpRequest::AllowUploadProgress()
{
return !(mState & XML_HTTP_REQUEST_USE_XSITE_AC) ||
(mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT);
}
/////////////////////////////////////////////////////
// nsIInterfaceRequestor methods:
//

View File

@ -75,6 +75,71 @@
class nsILoadGroup;
class nsAccessControlLRUCache
{
public:
struct TokenTime
{
nsCString token;
PRTime expirationTime;
};
struct CacheEntry : public PRCList
{
CacheEntry(nsCString& aKey)
: mKey(aKey)
{
MOZ_COUNT_CTOR(nsAccessControlLRUCache::CacheEntry);
}
~CacheEntry()
{
MOZ_COUNT_DTOR(nsAccessControlLRUCache::CacheEntry);
}
void PurgeExpired(PRTime now);
PRBool CheckRequest(const nsCString& aMethod,
const nsTArray<nsCString>& aCustomHeaders);
nsCString mKey;
nsTArray<TokenTime> mMethods;
nsTArray<TokenTime> mHeaders;
};
nsAccessControlLRUCache()
{
MOZ_COUNT_CTOR(nsAccessControlLRUCache);
PR_INIT_CLIST(&mList);
}
~nsAccessControlLRUCache()
{
Clear();
MOZ_COUNT_DTOR(nsAccessControlLRUCache);
}
PRBool Initialize()
{
return mTable.Init();
}
CacheEntry* GetEntry(nsIURI* aURI, nsIPrincipal* aPrincipal,
PRBool aWithCredentials, PRBool aCreate);
void Clear();
private:
PR_STATIC_CALLBACK(PLDHashOperator)
RemoveExpiredEntries(const nsACString& aKey, nsAutoPtr<CacheEntry>& aValue,
void* aUserData);
static PRBool GetCacheKey(nsIURI* aURI, nsIPrincipal* aPrincipal,
PRBool aWithCredentials, nsACString& _retval);
nsClassHashtable<nsCStringHashKey, CacheEntry> mTable;
PRCList mList;
};
class nsDOMEventListenerWrapper : public nsIDOMEventListener
{
public:
@ -169,6 +234,11 @@ public:
NS_FORWARD_NSIDOMEVENTTARGET(nsXHREventTarget::)
NS_FORWARD_NSIDOMNSEVENTTARGET(nsXHREventTarget::)
NS_DECL_NSIXMLHTTPREQUESTUPLOAD
PRBool HasListeners()
{
return mListenerManager && mListenerManager->HasListeners();
}
};
class nsXMLHttpRequest : public nsXHREventTarget,
@ -234,20 +304,20 @@ public:
// and aTotalSize is LL_MAXUINT when unknown. Both those values are
// used by nsXMLHttpProgressEvent. Normal progress event should not use
// headers in aLoaded and aTotal is 0 when unknown.
static void DispatchProgressEvent(nsPIDOMEventTarget* aTarget,
const nsAString& aType,
// Whether to use nsXMLHttpProgressEvent,
// which implements LS Progress Event.
PRBool aUseLSEventWrapper,
PRBool aLengthComputable,
// For Progress Events
PRUint64 aLoaded, PRUint64 aTotal,
// For LS Progress Events
PRUint64 aPosition, PRUint64 aTotalSize);
static void DispatchProgressEvent(nsPIDOMEventTarget* aTarget,
const nsAString& aType,
PRBool aLengthComputable,
PRUint64 aLoaded, PRUint64 aTotal)
void DispatchProgressEvent(nsPIDOMEventTarget* aTarget,
const nsAString& aType,
// Whether to use nsXMLHttpProgressEvent,
// which implements LS Progress Event.
PRBool aUseLSEventWrapper,
PRBool aLengthComputable,
// For Progress Events
PRUint64 aLoaded, PRUint64 aTotal,
// For LS Progress Events
PRUint64 aPosition, PRUint64 aTotalSize);
void DispatchProgressEvent(nsPIDOMEventTarget* aTarget,
const nsAString& aType,
PRBool aLengthComputable,
PRUint64 aLoaded, PRUint64 aTotal)
{
DispatchProgressEvent(aTarget, aType, PR_FALSE,
aLengthComputable, aLoaded, aTotal,
@ -262,6 +332,34 @@ public:
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXMLHttpRequest,
nsXHREventTarget)
static PRBool EnsureACCache()
{
if (sAccessControlCache)
return PR_TRUE;
nsAutoPtr<nsAccessControlLRUCache> newCache(new nsAccessControlLRUCache());
NS_ENSURE_TRUE(newCache, PR_FALSE);
if (newCache->Initialize()) {
sAccessControlCache = newCache.forget();
return PR_TRUE;
}
return PR_FALSE;
}
static void ShutdownACCache()
{
if (sAccessControlCache) {
delete sAccessControlCache;
sAccessControlCache = nsnull;
}
}
PRBool AllowUploadProgress();
static nsAccessControlLRUCache* sAccessControlCache;
protected:
friend class nsMultipartProxyListener;
@ -290,12 +388,12 @@ protected:
already_AddRefed<nsIHttpChannel> GetCurrentHttpChannel();
/**
* Check if mChannel is ok for a cross-site request by making sure no
* Check if aChannel is ok for a cross-site request by making sure no
* inappropriate headers are set, and no username/password is set.
*
* Also updates the XML_HTTP_REQUEST_USE_XSITE_AC bit.
*/
nsresult CheckChannelForCrossSiteRequest();
nsresult CheckChannelForCrossSiteRequest(nsIChannel* aChannel);
nsCOMPtr<nsISupports> mContext;
nsCOMPtr<nsIPrincipal> mPrincipal;
@ -303,6 +401,8 @@ protected:
// mReadRequest is different from mChannel for multipart requests
nsCOMPtr<nsIRequest> mReadRequest;
nsCOMPtr<nsIDOMDocument> mDocument;
nsCOMPtr<nsIChannel> mACGetChannel;
nsTArray<nsCString> mACUnsafeHeaders;
nsRefPtr<nsDOMEventListenerWrapper> mOnUploadProgressListener;
nsRefPtr<nsDOMEventListenerWrapper> mOnReadystatechangeListener;
@ -342,10 +442,6 @@ protected:
PRUint32 mState;
// List of potentially dangerous headers explicitly set using
// SetRequestHeader.
nsTArray<nsCString> mExtraRequestHeaders;
nsRefPtr<nsXMLHttpRequestUpload> mUpload;
PRUint32 mUploadTransferred;
PRUint32 mUploadTotal;

View File

@ -217,6 +217,11 @@ _TEST_FILES = test_bug5141.html \
test_bug454326.html \
test_bug457746.html \
bug457746.sjs \
test_CrossSiteXHR.html \
file_CrossSiteXHR_inner.html \
file_CrossSiteXHR_server.sjs \
test_CrossSiteXHR_cache.html \
file_CrossSiteXHR_cache_server.sjs \
$(NULL)
libs:: $(_TEST_FILES)

View File

@ -0,0 +1,45 @@
function d(s) { dump(s + "\n"); }
function handleRequest(request, response)
{
var query = {};
request.queryString.split('&').forEach(function (val) {
var [name, value] = val.split('=');
query[name] = unescape(value);
});
var isPreflight = request.method == "OPTIONS";
// Send response
response.setHeader("Access-Control-Allow-Origin", query.allowOrigin);
if (isPreflight) {
var secData = {};
if (request.hasHeader("Access-Control-Request-Headers")) {
var magicHeader =
request.getHeader("Access-Control-Request-Headers").split(",").
filter(function(name) /^magic-/.test(name))[0];
}
if (magicHeader) {
secData = eval(unescape(magicHeader.substr(6)));
secData.allowHeaders = (secData.allowHeaders || "") + "," + magicHeader;
}
if (secData.allowHeaders)
response.setHeader("Access-Control-Allow-Headers", secData.allowHeaders);
if (secData.allowMethods)
response.setHeader("Access-Control-Allow-Methods", secData.allowMethods);
if (secData.cacheTime)
response.setHeader("Access-Control-Max-Age", secData.cacheTime.toString());
return;
}
response.setHeader("Content-Type", "application/xml", false);
response.write("<res>hello pass</res>\n");
}

View File

@ -0,0 +1,82 @@
<!DOCTYPE HTML>
<html>
<head>
<script>
window.addEventListener('message', function(e) {
sendData = null;
req = eval(e.data);
var res = {
didFail: false,
events: [],
progressEvents: 0
};
var xhr = new XMLHttpRequest();
for each(type in ["load", "abort", "error", "loadstart"]) {
xhr.addEventListener(type, function(e) {
res.events.push(e.type);
}, false);
}
xhr.addEventListener("readystatechange", function(e) {
res.events.push("rs" + xhr.readyState);
}, false);
xhr.addEventListener("progress", function(e) {
res.progressEvents++;
}, false);
if (req.uploadProgress) {
if (req.uploadProgress == "uploadProgress") {
xhr.addEventListener("uploadProgress", function(e) {
res.progressEvents++;
}, false);
}
else {
xhr.upload.addEventListener(req.uploadProgress, function(e) {
res.progressEvents++;
}, false);
}
}
xhr.onload = function () {
res.status = xhr.status;
res.responseXML = xhr.responseXML ?
(new XMLSerializer()).serializeToString(xhr.responseXML) :
null;
res.responseText = xhr.responseText;
post(e, res);
};
xhr.onerror = function () {
res.didFail = true;
res.status = xhr.status;
res.responseXML = xhr.responseXML ?
(new XMLSerializer()).serializeToString(xhr.responseXML) :
null;
res.responseText = xhr.responseText;
post(e, res);
}
if (req.withCred)
xhr.withCredentials = true;
res.events.push("opening");
xhr.open(req.method, req.url, true);
for (header in req.headers) {
xhr.setRequestHeader(header, req.headers[header]);
}
res.events.push("sending");
xhr.send(sendData);
}, false);
function post(e, res) {
e.source.postMessage(res.toSource(), "http://localhost:8888");
}
</script>
</head>
<body>
Inner page
</body>
</html>

View File

@ -0,0 +1,87 @@
function handleRequest(request, response)
{
try {
var query = {};
request.queryString.split('&').forEach(function (val) {
[name, value] = val.split('=');
query[name] = unescape(value);
});
var isPreflight = request.method == "OPTIONS";
// Check that request was correct
if (!isPreflight && "headers" in query) {
headers = eval(query.headers);
for(headerName in headers) {
if (request.getHeader(headerName) != headers[headerName]) {
throw "Header " + headerName + " had wrong value. Expected " +
headers[headerName] + " got " + request.getHeader(headerName);
}
}
}
if (isPreflight && "requestHeaders" in query &&
request.getHeader("Access-Control-Request-Headers") != query.requestHeaders) {
throw "Access-Control-Request-Headers had wrong value. Expected " +
query.requestHeaders + " got " +
request.getHeader("Access-Control-Request-Headers");
}
if (isPreflight && "requestMethod" in query &&
request.getHeader("Access-Control-Request-Method") != query.requestMethod) {
throw "Access-Control-Request-Method had wrong value. Expected " +
query.requestMethod + " got " +
request.getHeader("Access-Control-Request-Method");
}
if ("origin" in query && request.getHeader("Origin") != query.origin) {
throw "Origin had wrong value. Expected " + query.origin + " got " +
request.getHeader("Origin");
}
if ("cookie" in query) {
cookies = {};
request.getHeader("Cookie").split(/ *; */).forEach(function (val) {
[name, value] = val.split('=');
cookies[name] = unescape(value);
});
query.cookie.split(",").forEach(function (val) {
[name, value] = val.split('=');
if (cookies[name] != value) {
throw "Cookie " + name + " had wrong value. Expected " + value +
" got " + cookies[name];
}
});
}
if ("noCookie" in query && request.hasHeader("Cookie")) {
throw "Got cookies when didn't expect to";
}
// Send response
if (query.allowOrigin && (!isPreflight || !query.noAllowPreflight))
response.setHeader("Access-Control-Allow-Origin", query.allowOrigin);
if (query.allowCred)
response.setHeader("Access-Control-Allow-Credentials", "true");
if (query.setCookie)
response.setHeader("Set-Cookie", query.setCookie + "; path=/");
if (isPreflight) {
if (query.allowHeaders)
response.setHeader("Access-Control-Allow-Headers", query.allowHeaders);
if (query.allowMethods)
response.setHeader("Access-Control-Allow-Methods", query.allowMethods);
return;
}
response.setHeader("Content-Type", "application/xml", false);
response.write("<res>hello pass</res>\n");
} catch (e) {
dump(e + "\n");
throw e;
}
}

View File

@ -0,0 +1,578 @@
<!DOCTYPE HTML>
<html>
<head>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
<title>Test for Cross Site XMLHttpRequest</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="gen.next()">
<p id="display">
<iframe id=loader></iframe>
</p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="application/javascript;version=1.8">
SimpleTest.waitForExplicitFinish();
var origins =
[['http://example.org'],
['http://example.org:80', 'http://example.org'],
['http://sub1.test1.example.org'],
['http://test2.example.org:8000'],
//['https://example.com:443'],
//['https://sub1.test1.example.com:443'],
['http://sub1.\xe4lt.example.org:8000', 'http://sub1.xn--lt-uia.example.org:8000'],
['http://sub2.\xe4lt.example.org', 'http://sub2.xn--lt-uia.example.org'],
['http://ex\xe4mple.test', 'http://xn--exmple-cua.test'],
['http://\u03c0\u03b1\u03c1\u03ac\u03b4\u03b5\u03b9\u03b3\u03bc\u03b1.\u03b4\u03bf\u03ba\u03b9\u03bc\u03ae',
'http://xn--hxajbheg2az3al.xn--jxalpdlp'],
];
window.addEventListener("message", function(e) {
gen.send(e.data);
}, false);
gen = runTest();
function runTest() {
var loader = document.getElementById('loader');
var loaderWindow = loader.contentWindow;
loader.onload = function () { gen.next() };
// Test preflight-less requests
baseURL = "http://localhost:8888/tests/content/base/test/" +
"file_CrossSiteXHR_server.sjs?";
for each(originPair in origins) {
origin = originPair[1] || originPair[0];
loader.src = originPair[0] + "/tests/content/base/test/file_CrossSiteXHR_inner.html";
yield;
port = /:\d+/;
passTests = [
origin,
"*",
" \t " + origin + "\t \t",
"\t \t* \t ",
];
failTests = [
"",
" ",
port.test(origin) ? origin.replace(port, "")
: origin + ":1234",
port.test(origin) ? origin.replace(port, ":")
: origin + ":",
origin + "/",
origin + "#",
origin + "?",
origin + "\\",
origin + "%",
origin + "@",
origin + "/hello",
"foo:bar@" + origin,
"* " + origin,
origin + " " + origin,
"allow <" + origin + ">",
"<" + origin + ">",
"<*>",
origin.substr(0, 5) == "https" ? origin.replace("https", "http")
: origin.replace("http", "https"),
origin.replace("://", "://www."),
origin.replace("://", ":// "),
origin.replace(/\/[^.]+\./, "/"),
];
for each(method in ["GET", "POST"]) {
var headers = method == "POST" ?
{ "Content-Type": "text/plain" } :
null;
for each(allowOrigin in passTests) {
req = {
url: baseURL +
"allowOrigin=" + escape(allowOrigin) +
"&origin=" + escape(origin),
method: method,
headers: headers,
};
loaderWindow.postMessage(req.toSource(), origin);
res = eval(yield);
is(res.didFail, false, "shouldn't have failed");
is(res.status, 200, "wrong status");
is(res.responseXML,
"<res>hello pass</res>",
"wrong responseXML in test for " + allowOrigin);
is(res.responseText, "<res>hello pass</res>\n",
"wrong responseText in test for " + allowOrigin);
is(res.events.join(","),
"opening,rs1,sending,rs1,loadstart,rs2,rs3,rs4,load",
"wrong responseText in test for " + allowOrigin);
}
for each(allowOrigin in failTests) {
req = {
url: baseURL + "allowOrigin=" + escape(allowOrigin),
method: method,
headers: headers,
};
loaderWindow.postMessage(req.toSource(), origin);
res = eval(yield);
is(res.didFail, true, "should have failed for " + allowOrigin);
is(res.responseText, "", "should have no text for " + allowOrigin);
is(res.status, 0, "should have no status for " + allowOrigin);
is(res.responseXML, null, "should have no XML for " + allowOrigin);
is(res.events.join(","),
"opening,rs1,sending,rs1,loadstart,rs2,rs4,error",
"wrong events in test for " + allowOrigin);
is(res.progressEvents, 0,
"wrong events in test for " + allowOrigin);
}
}
}
// Test preflighted requests
loader.src = "http://example.org/tests/content/base/test/file_CrossSiteXHR_inner.html";
origin = "http://example.org";
yield;
passTests = [{ method: "GET",
headers: { "x-my-header": "myValue" },
allowHeaders: "x-my-header",
},
{ method: "GET",
headers: { "x-my-header": "myValue",
"second-header": "secondValue",
"third-header": "thirdValue" },
allowHeaders: "x-my-header, second-header, third-header",
},
{ method: "GET",
headers: { "x-my-header": "myValue",
"second-header": "secondValue",
"third-header": "thirdValue" },
allowHeaders: "x-my-header,second-header,third-header",
},
{ method: "GET",
headers: { "x-my-header": "myValue",
"second-header": "secondValue",
"third-header": "thirdValue" },
allowHeaders: "x-my-header ,second-header ,third-header",
},
{ method: "GET",
headers: { "x-my-header": "myValue",
"second-header": "secondValue",
"third-header": "thirdValue" },
allowHeaders: "x-my-header , second-header , third-header",
},
{ method: "GET",
headers: { "x-my-header": "myValue",
"second-header": "secondValue" },
allowHeaders: ", x-my-header, , ,, second-header, , ",
},
{ method: "GET",
headers: { "x-my-header": "myValue",
"second-header": "secondValue" },
allowHeaders: "x-my-header, second-header, unused-header",
},
{ method: "GET",
headers: { "x-my-header": "myValue" },
allowHeaders: "X-My-Header",
},
{ method: "GET",
headers: { "x-my-header": "myValue",
"long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header": "secondValue" },
allowHeaders: "x-my-header, long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header",
},
{ method: "GET",
headers: { "x-my%-header": "myValue" },
allowHeaders: "x-my%-header",
},
{ method: "GET",
headers: { "Content-Type": "baz/bin",
"Accept": "foo/bar",
"Accept-Language": "sv-SE" },
},
{ method: "POST",
headers: { "Content-Type": "text/plain" },
noAllowPreflight: 1,
},
{ method: "POST",
},
{ method: "POST",
headers: { "Content-Type": "foo/bar" },
},
{ method: "POST",
headers: { "Content-Type": "text/plain",
"Accept": "foo/bar",
"Accept-Language": "sv-SE" },
noAllowPreflight: 1,
},
{ method: "POST",
headers: { "Accept": "foo/bar",
"Accept-Language": "sv-SE",
"x-my-header": "myValue" },
allowHeaders: "x-my-header",
},
{ method: "POST",
headers: { "Content-Type": "text/plain",
"x-my-header": "myValue" },
allowHeaders: "x-my-header",
},
{ method: "POST",
headers: { "Content-Type": "foo/bar",
"x-my-header": "myValue" },
allowHeaders: "x-my-header",
},
{ method: "POST",
headers: { "x-my-header": "myValue" },
allowHeaders: "x-my-header",
},
{ method: "POST",
headers: { "x-my-header": "myValue" },
allowHeaders: "x-my-header, $_%",
},
{ method: "XXDELETE",
allowMethods: "XXDELETE",
},
{ method: "XXDELETE",
allowMethods: "POST, PUT, XXDELETE",
},
{ method: "XXDELETE",
allowMethods: "POST, XXDELETE, PUT",
},
{ method: "XXDELETE",
allowMethods: "XXDELETE, POST, PUT",
},
{ method: "XXDELETE",
allowMethods: "POST ,PUT ,XXDELETE",
},
{ method: "XXDELETE",
allowMethods: "POST,PUT,XXDELETE",
},
{ method: "XXDELETE",
allowMethods: "POST , PUT , XXDELETE",
},
{ method: "XXDELETE",
allowMethods: " ,, PUT ,, , , XXDELETE , ,",
},
{ method: "POST",
headers: { "Content-Type": "text/plain" },
uploadProgress: "uploadprogress",
},
{ method: "POST",
headers: { "Content-Type": "text/plain" },
uploadProgress: "progress",
},
];
failTests = [{ method: "GET",
headers: { "x-my-header": "myValue" },
},
{ method: "GET",
headers: { "x-my-header": "myValue" },
allowHeaders: "",
},
{ method: "GET",
headers: { "x-my-header": "myValue" },
allowHeaders: "y-my-header",
},
{ method: "GET",
headers: { "x-my-header": "myValue" },
allowHeaders: "x-my-header y-my-header",
},
{ method: "GET",
headers: { "x-my-header": "myValue" },
allowHeaders: "x-my-header, y-my-header z",
},
{ method: "GET",
headers: { "x-my-header": "myValue" },
allowHeaders: "x-my-header, y-my-he(ader",
},
{ method: "GET",
headers: { "x-my-header": "myValue",
"y-my-header": "secondValue" },
allowHeaders: "x-my-header",
},
{ method: "GET",
headers: { "x-my-header": "" },
},
{ method: "GET",
headers: { "x-my-header": "",
"y-my-header": "" },
allowHeaders: "x-my-header",
},
{ method: "POST",
noAllowPreflight: 1,
},
{ method: "POST",
headers: { "Content-Type": "foo/bar" },
noAllowPreflight: 1,
},
{ method: "XXDELETE",
},
{ method: "XXDELETE",
allowMethods: "",
},
{ method: "XXDELETE",
allowMethods: "PUT",
},
{ method: "XXDELETE",
allowMethods: "XXDELETEZ",
},
{ method: "XXDELETE",
allowMethods: "XXDELETE PUT",
},
{ method: "XXDELETE",
allowMethods: "XXDELETE, PUT Z",
},
{ method: "XXDELETE",
allowMethods: "XXDELETE, PU(T",
},
{ method: "XXDELETE",
allowMethods: "PUT XXDELETE",
},
{ method: "XXDELETE",
allowMethods: "PUT Z, XXDELETE",
},
{ method: "XXDELETE",
allowMethods: "PU(T, XXDELETE",
},
{ method: "MYMETHOD",
allowMethods: "myMethod",
},
{ method: "PUT",
allowMethods: "put",
},
{ method: "POST",
headers: { "Content-Type": "text/plain" },
noAllowPreflight: 1,
uploadProgress: "uploadprogress",
},
{ method: "POST",
headers: { "Content-Type": "text/plain" },
noAllowPreflight: 1,
uploadProgress: "progress",
},
];
for each(test in passTests) {
req = {
url: baseURL + "&allowOrigin=" + escape(origin) +
"&origin=" + escape(origin) +
"&requestMethod=" + test.method,
method: test.method,
headers: test.headers,
uploadProgress: test.uploadProgress,
};
if (test.noAllowPreflight)
req.url += "&noAllowPreflight";
if ("headers" in test) {
req.url += "&headers=" + escape(test.headers.toSource());
reqHeaders =
escape([name for (name in test.headers)].filter(function(name)
name != "Content-Type" &&
name != "Accept" &&
name != "Accept-Language").join(","));
req.url += reqHeaders ? "&requestHeaders=" + reqHeaders : "";
}
if ("allowHeaders" in test)
req.url += "&allowHeaders=" + escape(test.allowHeaders);
if ("allowMethods" in test)
req.url += "&allowMethods=" + escape(test.allowMethods);
loaderWindow.postMessage(req.toSource(), origin);
res = eval(yield);
is(res.didFail, false,
"shouldn't have failed in test for " + test.toSource());
is(res.status, 200, "wrong status in test for " + test.toSource());
is(res.responseXML, "<res>hello pass</res>",
"wrong responseXML in test for " + test.toSource());
is(res.responseText, "<res>hello pass</res>\n",
"wrong responseText in test for " + test.toSource());
is(res.events.join(","),
"opening,rs1,sending,rs1,loadstart,rs2,rs3,rs4,load",
"wrong responseText in test for " + test.toSource());
}
/*SimpleTest.finish();
yield;*/
for each(test in failTests) {
req = {
url: baseURL + "allowOrigin=" + escape(origin),
method: test.method,
headers: test.headers,
uploadProgress: test.uploadProgress,
};
if (test.noAllowPreflight)
req.url += "&noAllowPreflight";
if ("allowHeaders" in test)
req.url += "&allowHeaders=" + escape(test.allowHeaders);
if ("allowMethods" in test)
req.url += "&allowMethods=" + escape(test.allowMethods);
loaderWindow.postMessage(req.toSource(), origin);
res = eval(yield);
is(res.didFail, true,
"should have failed in test for " + test.toSource());
is(res.status, 0, "wrong status in test for " + test.toSource());
is(res.responseXML, null,
"wrong responseXML in test for " + test.toSource());
is(res.responseText, "",
"wrong responseText in test for " + test.toSource());
is(res.events.join(","),
"opening,rs1,sending,rs1,loadstart,rs2,rs4,error",
"wrong events in test for " + test.toSource());
is(res.progressEvents, 0,
"wrong events in test for " + test.toSource());
}
// Test cookie behavior
tests = [{ pass: 1,
method: "GET",
withCred: 1,
allowCred: 1,
},
{ pass: 0,
method: "GET",
withCred: 1,
allowCred: 0,
},
{ pass: 0,
method: "GET",
withCred: 1,
allowCred: 1,
origin: "*",
},
{ pass: 1,
method: "GET",
withCred: 0,
allowCred: 1,
origin: "*",
},
{ pass: 1,
method: "GET",
setCookie: "a=1",
withCred: 1,
allowCred: 1,
},
{ pass: 1,
method: "GET",
cookie: "a=1",
withCred: 1,
allowCred: 1,
},
{ pass: 1,
method: "GET",
noCookie: 1,
withCred: 0,
allowCred: 1,
},
{ pass: 0,
method: "GET",
noCookie: 1,
withCred: 1,
allowCred: 1,
},
{ pass: 1,
method: "GET",
setCookie: "a=2",
withCred: 0,
allowCred: 1,
},
{ pass: 1,
method: "GET",
cookie: "a=1",
withCred: 1,
allowCred: 1,
},
{ pass: 1,
method: "GET",
setCookie: "a=2",
withCred: 1,
allowCred: 1,
},
{ pass: 1,
method: "GET",
cookie: "a=2",
withCred: 1,
allowCred: 1,
},
];
for each(test in tests) {
req = {
url: baseURL + "allowOrigin=" + escape(test.origin || origin),
method: test.method,
headers: test.headers,
withCred: test.withCred,
};
if (test.allowCred)
req.url += "&allowCred";
if (test.setCookie)
req.url += "&setCookie=" + escape(test.setCookie);
if (test.cookie)
req.url += "&cookie=" + escape(test.cookie);
if (test.noCookie)
req.url += "&noCookie";
if ("allowHeaders" in test)
req.url += "&allowHeaders=" + escape(test.allowHeaders);
if ("allowMethods" in test)
req.url += "&allowMethods=" + escape(test.allowMethods);
loaderWindow.postMessage(req.toSource(), origin);
res = eval(yield);
if (test.pass) {
is(res.didFail, false,
"shouldn't have failed in test for " + test.toSource());
is(res.status, 200, "wrong status in test for " + test.toSource());
is(res.responseXML, "<res>hello pass</res>",
"wrong responseXML in test for " + test.toSource());
is(res.responseText, "<res>hello pass</res>\n",
"wrong responseText in test for " + test.toSource());
is(res.events.join(","),
"opening,rs1,sending,rs1,loadstart,rs2,rs3,rs4,load",
"wrong responseText in test for " + test.toSource());
}
else {
is(res.didFail, true,
"should have failed in test for " + test.toSource());
is(res.status, 0, "wrong status in test for " + test.toSource());
is(res.responseXML, null,
"wrong responseXML in test for " + test.toSource());
is(res.responseText, "",
"wrong responseText in test for " + test.toSource());
is(res.events.join(","),
"opening,rs1,sending,rs1,loadstart,rs2,rs4,error",
"wrong events in test for " + test.toSource());
is(res.progressEvents, 0,
"wrong events in test for " + test.toSource());
}
}
SimpleTest.finish();
yield;
}
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,356 @@
<!DOCTYPE HTML>
<html>
<head>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
<title>Test for Cross Site XMLHttpRequest</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="gen.next()">
<p id="display">
<iframe id=loader></iframe>
</p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="application/javascript;version=1.7">
SimpleTest.waitForExplicitFinish();
window.addEventListener("message", function(e) {
gen.send(e.data);
}, false);
gen = runTest();
function runTest() {
var loader = document.getElementById('loader');
var loaderWindow = loader.contentWindow;
loader.onload = function () { gen.next() };
loader.src = "http://example.org/tests/content/base/test/file_CrossSiteXHR_inner.html";
origin = "http://example.org";
yield;
tests = [{ pass: 0,
method: "GET",
headers: { "x-my-header": "myValue" },
},
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue" },
allowHeaders: "x-my-header",
cacheTime: 3600
},
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue" },
},
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue" },
},
{ pass: 0,
method: "GET",
headers: { "x-my-header": "myValue",
"y-my-header": "second" },
},
{ pass: 1,
method: "GET",
headers: { "y-my-header": "hello" },
allowHeaders: "y-my-header",
},
{ pass: 0,
method: "GET",
headers: { "y-my-header": "hello" },
},
{ pass: 1,
method: "GET",
headers: { "y-my-header": "hello" },
allowHeaders: "y-my-header",
cacheTime: 3600,
},
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue",
"y-my-header": "second" },
},
{ newTest: "*******" },
{ pass: 0,
method: "GET",
headers: { "x-my-header": "myValue" },
},
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue" },
allowHeaders: "x-my-header",
cacheTime: 2
},
{ pause: 2.1 },
{ pass: 0,
method: "GET",
headers: { "x-my-header": "myValue" },
},
{ newTest: "*******" },
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue" },
allowHeaders: "x-my-header, y-my-header",
cacheTime: 3600
},
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue" },
},
{ pass: 1,
method: "GET",
headers: { "y-my-header": "myValue" },
},
{ pass: 0,
method: "GET",
headers: { "z-my-header": "myValue" },
},
{ newTest: "*******" },
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue" },
allowHeaders: "x-my-header",
cacheTime: "\t 3600 \t ",
},
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue" },
},
{ newTest: "*******" },
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue" },
allowHeaders: "x-my-header",
cacheTime: "3600 3",
},
{ pass: 0,
method: "GET",
headers: { "x-my-header": "myValue" },
},
{ newTest: "*******" },
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue" },
allowHeaders: "x-my-header",
cacheTime: "asdf",
},
{ pass: 0,
method: "GET",
headers: { "x-my-header": "myValue" },
},
{ newTest: "*******" },
{ pass: 1,
method: "GET",
headers: { "first-header": "myValue" },
allowHeaders: "first-header",
cacheTime: 2,
},
{ pass: 1,
method: "GET",
headers: { "second-header": "myValue" },
allowHeaders: "second-header",
cacheTime: 3600,
},
{ pass: 0,
method: "GET",
headers: { "third-header": "myValue" },
},
{ pass: 1,
method: "GET",
headers: { "third-header": "myValue" },
allowHeaders: "third-header",
cacheTime: 2,
},
{ pause: 2.1 },
{ pass: 0,
method: "GET",
headers: { "first-header": "myValue" },
},
{ pass: 1,
method: "GET",
headers: { "second-header": "myValue" },
},
{ pass: 0,
method: "GET",
headers: { "third-header": "myValue" },
},
{ newTest: "*******" },
{ pass: 0,
method: "XXDELETE",
},
{ pass: 1,
method: "XXDELETE",
allowMethods: "XXDELETE",
cacheTime: 3600
},
{ pass: 1,
method: "XXDELETE",
},
{ pass: 1,
method: "XXDELETE",
},
{ pass: 0,
method: "PATCH",
},
{ pass: 1,
method: "PATCH",
allowMethods: "PATCH",
},
{ pass: 0,
method: "PATCH",
},
{ pass: 1,
method: "PATCH",
allowMethods: "PATCH",
cacheTime: 3600,
},
{ pass: 1,
method: "PATCH",
},
{ pass: 1,
method: "XXDELETE",
},
{ pass: 0,
method: "XXPUT",
},
{ newTest: "*******" },
{ pass: 0,
method: "XXDELETE",
},
{ pass: 1,
method: "XXDELETE",
allowMethods: "XXDELETE",
cacheTime: 2
},
{ pause: 2.1 },
{ pass: 0,
method: "XXDELETE",
},
{ newTest: "*******" },
{ pass: 1,
method: "XXDELETE",
allowMethods: "XXDELETE, XXPUT",
cacheTime: 3600
},
{ pass: 1,
method: "XXDELETE",
},
{ pass: 1,
method: "XXPUT",
},
{ pass: 0,
method: "PATCH",
},
{ newTest: "*******" },
{ pass: 1,
method: "FIRST",
allowMethods: "FIRST",
cacheTime: 2,
},
{ pass: 1,
method: "SECOND",
allowMethods: "SECOND",
cacheTime: 3600,
},
{ pass: 0,
method: "THIRD",
},
{ pass: 1,
method: "THIRD",
allowMethods: "THIRD",
cacheTime: 2,
},
{ pause: 2.1 },
{ pass: 0,
method: "FIRST",
},
{ pass: 1,
method: "SECOND",
},
{ pass: 0,
method: "THIRD",
},
];
baseURL = "http://localhost:8888/tests/content/base/test/" +
"file_CrossSiteXHR_cache_server.sjs?";
var unique = Date.now();
for each (test in tests) {
if (test.newTest) {
unique++;
continue;
}
if (test.pause) {
setTimeout(function() { gen.next() }, test.pause * 1000);
yield;
continue;
}
req = {
url: baseURL + "c=" + unique +
"&allowOrigin=" + escape(origin),
method: test.method,
headers: test.headers,
};
if (test.cacheTime || test.allowHeaders || test.allowMethods) {
sec = { allowHeaders: test.allowHeaders,
allowMethods: test.allowMethods,
cacheTime: test.cacheTime };
req.headers = req.headers || {};
req.headers["magic-" + escape(sec.toSource())] = "";
}
loaderWindow.postMessage(req.toSource(), origin);
res = eval(yield);
testName = test.toSource() + " (index " + tests.indexOf(test) + ")";
if (test.pass) {
is(res.didFail, false,
"shouldn't have failed in test for " + testName);
is(res.status, 200, "wrong status in test for " + testName);
is(res.responseXML, "<res>hello pass</res>",
"wrong responseXML in test for " + testName);
is(res.responseText, "<res>hello pass</res>\n",
"wrong responseText in test for " + testName);
is(res.events.join(","),
"opening,rs1,sending,rs1,loadstart,rs2,rs3,rs4,load",
"wrong events in test for " + testName);
}
else {
is(res.didFail, true,
"should have failed in test for " + testName);
is(res.status, 0, "wrong status in test for " + testName);
is(res.responseXML, null,
"wrong responseXML in test for " + testName);
is(res.responseText, "",
"wrong responseText in test for " + testName);
is(res.events.join(","),
"opening,rs1,sending,rs1,loadstart,rs2,rs4,error",
"wrong events in test for " + testName);
is(res.progressEvents, 0,
"wrong events in test for " + testName);
}
}
SimpleTest.finish();
yield;
}
</script>
</pre>
</body>
</html>

View File

@ -52,8 +52,8 @@ class nsIAtom;
* Event listener manager interface.
*/
#define NS_IEVENTLISTENERMANAGER_IID \
{ 0x6ee5eeeb, 0x1bf3, 0x4865, \
{ 0xa9, 0x52, 0x3b, 0x3e, 0x97, 0x9b, 0x4a, 0xb3 } }
{ 0x0056ac6b, 0xc25b, 0x4fbb, \
{ 0x92, 0x98, 0x8d, 0xce, 0x53, 0x6e } }
class nsIEventListenerManager : public nsISupports {
@ -188,6 +188,11 @@ public:
* Returns PR_TRUE if there is at least one event listener for aEventName.
*/
virtual PRBool HasListenersFor(const nsAString& aEventName) = 0;
/**
* Returns PR_TRUE if there is at least one event listener.
*/
virtual PRBool HasListeners() = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIEventListenerManager,

View File

@ -1750,6 +1750,12 @@ found:
return PR_FALSE;
}
PRBool
nsEventListenerManager::HasListeners()
{
return !mListeners.IsEmpty();
}
PRBool
nsEventListenerManager::HasUnloadListeners()
{

View File

@ -49,6 +49,8 @@
class nsIDOMEvent;
class nsIAtom;
class nsIWidget;
struct nsPoint;
struct EventTypeData;
typedef struct {
@ -129,6 +131,8 @@ public:
virtual PRBool HasListenersFor(const nsAString& aEventName);
virtual PRBool HasListeners();
static PRUint32 GetIdentifierForEvent(nsIAtom* aEvent);
// nsIDOMEventTarget

View File

@ -36,6 +36,7 @@
*
* ***** END LICENSE BLOCK ***** */
#include "nsIDOMHTMLAudioElement.h"
#include "nsIJSNativeInitializer.h"
#include "nsHTMLMediaElement.h"
#include "nsVideoDecoder.h"
@ -43,7 +44,8 @@ typedef PRUint16 nsMediaNetworkState;
typedef PRUint16 nsMediaReadyState;
class nsHTMLAudioElement : public nsHTMLMediaElement,
public nsIDOMHTMLAudioElement
public nsIDOMHTMLAudioElement,
public nsIJSNativeInitializer
{
public:
nsHTMLAudioElement(nsINodeInfo *aNodeInfo, PRBool aFromParser = PR_FALSE);
@ -67,6 +69,10 @@ public:
// nsIDOMHTMLAudioElement
NS_DECL_NSIDOMHTMLAUDIOELEMENT
// nsIJSNativeInitializer
NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* aContext,
JSObject* aObj, PRUint32 argc, jsval* argv);
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,

View File

@ -155,6 +155,7 @@ INCLUDES += \
-I$(srcdir)/../../../xbl/src \
-I$(srcdir)/../../../../layout/style \
-I$(srcdir)/../../../../layout/tables \
-I$(srcdir)/../../../../dom/src/base \
-I$(srcdir) \
$(NULL)

View File

@ -55,6 +55,7 @@
#include "nsXPCOMStrings.h"
#include "prlock.h"
#include "nsThreadUtils.h"
#include "nsJSUtils.h"
#include "nsIScriptSecurityManager.h"
#include "nsIXPConnect.h"
@ -68,13 +69,33 @@
#include "nsIDOMProgressEvent.h"
#include "nsHTMLMediaError.h"
NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Audio)
nsGenericHTMLElement*
NS_NewHTMLAudioElement(nsINodeInfo *aNodeInfo, PRBool aFromParser)
{
/*
* nsHTMLAudioElement's will be created without a nsINodeInfo passed in
* if someone says "var audio = new Audio();" in JavaScript, in a case like
* that we request the nsINodeInfo from the document's nodeinfo list.
*/
nsCOMPtr<nsINodeInfo> nodeInfo(aNodeInfo);
if (!nodeInfo) {
nsCOMPtr<nsIDocument> doc =
do_QueryInterface(nsContentUtils::GetDocumentFromCaller());
NS_ENSURE_TRUE(doc, nsnull);
nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::audio, nsnull,
kNameSpaceID_None);
NS_ENSURE_TRUE(nodeInfo, nsnull);
}
return new nsHTMLAudioElement(nodeInfo);
}
NS_IMPL_ADDREF_INHERITED(nsHTMLAudioElement, nsHTMLMediaElement)
NS_IMPL_RELEASE_INHERITED(nsHTMLAudioElement, nsHTMLMediaElement)
NS_HTML_CONTENT_INTERFACE_TABLE_HEAD(nsHTMLAudioElement, nsHTMLMediaElement)
NS_INTERFACE_TABLE_INHERITED1(nsHTMLAudioElement, nsIDOMHTMLAudioElement)
NS_INTERFACE_TABLE_INHERITED2(nsHTMLAudioElement, nsIDOMHTMLAudioElement, nsIJSNativeInitializer)
NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLAudioElement)
NS_IMPL_ELEMENT_CLONE(nsHTMLAudioElement)
@ -118,3 +139,21 @@ nsresult nsHTMLAudioElement::InitializeDecoder(nsAString& aChosenMediaResource)
return nsHTMLMediaElement::InitializeDecoder(aChosenMediaResource);
}
NS_IMETHODIMP
nsHTMLAudioElement::Initialize(nsISupports* aOwner, JSContext* aContext,
JSObject *aObj, PRUint32 argc, jsval *argv)
{
if (argc <= 0) {
// Nothing to do here if we don't get any arguments.
return NS_OK;
}
// The only (optional) argument is the url of the audio
JSString* jsstr = JS_ValueToString(aContext, argv[0]);
if (!jsstr)
return NS_ERROR_FAILURE;
nsDependentJSString str(jsstr);
return SetAttr(kNameSpaceID_None, nsGkAtoms::src, str, PR_TRUE);
}

View File

@ -1713,6 +1713,10 @@ nsHTMLDocument::GetCookie(nsAString& aCookie)
aCookie.Truncate(); // clear current cookie in case service fails;
// no cookie isn't an error condition.
if (mDisableCookieAccess) {
return NS_OK;
}
// not having a cookie service isn't an error
nsCOMPtr<nsICookieService> service = do_GetService(NS_COOKIESERVICE_CONTRACTID);
if (service) {
@ -1739,6 +1743,10 @@ nsHTMLDocument::GetCookie(nsAString& aCookie)
NS_IMETHODIMP
nsHTMLDocument::SetCookie(const nsAString& aCookie)
{
if (mDisableCookieAccess) {
return NS_OK;
}
// not having a cookie service isn't an error
nsCOMPtr<nsICookieService> service = do_GetService(NS_COOKIESERVICE_CONTRACTID);
if (service && mDocumentURI) {

View File

@ -197,6 +197,11 @@ public:
return mEditingState;
}
virtual void DisableCookieAccess()
{
mDisableCookieAccess = PR_TRUE;
}
virtual nsIContent* GetBodyContentExternal();
class nsAutoEditingState {
@ -356,6 +361,9 @@ protected:
static jsval sCutCopyInternal_id;
static jsval sPasteInternal_id;
// When false, the .cookies property is completely disabled
PRBool mDisableCookieAccess;
// Parser used for constructing document fragments.
nsCOMPtr<nsIParser> mFragmentParser;
};

View File

@ -180,6 +180,11 @@ public:
virtual nsresult GetDocumentAllResult(const nsAString& aID,
nsISupports** aResult) = 0;
/**
* Disables getting and setting cookies
*/
virtual void DisableCookieAccess() = 0;
/**
* Get the first <body> child of the root <html>, but don't do
* anything <frameset>-related (like nsIDOMHTMLDocument::GetBody).

View File

@ -43,7 +43,9 @@ relativesrcdir = content/media/video/test
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
_TEST_FILES = test_autoplay.html \
_TEST_FILES = test_audio1.html \
test_audio2.html \
test_autoplay.html \
test_constants.html \
test_controls.html \
test_currentTime.html \
@ -53,6 +55,7 @@ _TEST_FILES = test_autoplay.html \
test_playbackRate.html \
test_readyState.html \
test_start.html \
sound.ogg \
# test_bug448534.html \
320x240.ogg \
$(NULL)

Binary file not shown.

View File

@ -0,0 +1,28 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Media test: Audio Constructor Test 1</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
var timeout = setTimeout(function () {
ok(false, "Test timed out");
SimpleTest.finish();
}, 30000);
var a1 = new Audio();
a1.onload = function() {
ok(a1.networkState == a1.LOADED, "Audio onload");
clearTimeout(timeout);
SimpleTest.finish();
}
a1.src = 'sound.ogg';
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,27 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Media test: Audio Constructor Test 2</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
var timeout = setTimeout(function () {
ok(false, "Test timed out");
SimpleTest.finish();
}, 30000);
var a1 = new Audio('sound.ogg');
a1.onload = function() {
ok(a1.networkState == a1.LOADED, "Audio onload");
clearTimeout(timeout);
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>

View File

@ -70,6 +70,7 @@
#include "nsAttrName.h"
#include "nsIScriptError.h"
#include "nsIURL.h"
#include "nsCrossSiteListenerProxy.h"
#include "nsDOMError.h"
static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
@ -95,7 +96,6 @@ getSpec(nsIChannel* aChannel, nsAString& aSpec)
class txStylesheetSink : public nsIXMLContentSink,
public nsIExpatSink,
public nsIStreamListener,
public nsIChannelEventSink,
public nsIInterfaceRequestor
{
public:
@ -105,7 +105,6 @@ public:
NS_DECL_NSIEXPATSINK
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSICHANNELEVENTSINK
NS_DECL_NSIINTERFACEREQUESTOR
// nsIContentSink
@ -137,13 +136,12 @@ txStylesheetSink::txStylesheetSink(txStylesheetCompiler* aCompiler,
mListener = do_QueryInterface(aParser);
}
NS_IMPL_ISUPPORTS7(txStylesheetSink,
NS_IMPL_ISUPPORTS6(txStylesheetSink,
nsIXMLContentSink,
nsIContentSink,
nsIExpatSink,
nsIStreamListener,
nsIRequestObserver,
nsIChannelEventSink,
nsIInterfaceRequestor)
NS_IMETHODIMP
@ -375,29 +373,6 @@ txStylesheetSink::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
return rv;
}
NS_IMETHODIMP
txStylesheetSink::OnChannelRedirect(nsIChannel *aOldChannel,
nsIChannel *aNewChannel,
PRUint32 aFlags)
{
NS_PRECONDITION(aNewChannel, "Redirecting to null channel?");
nsCOMPtr<nsIURI> oldURI;
nsresult rv = aOldChannel->GetURI(getter_AddRefs(oldURI));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> newURI;
rv = aNewChannel->GetURI(getter_AddRefs(newURI));
NS_ENSURE_SUCCESS(rv, rv);
rv = nsContentUtils::GetSecurityManager()->
CheckSameOriginURI(oldURI, newURI, PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHODIMP
txStylesheetSink::GetInterface(const nsIID& aIID, void** aResult)
{
@ -421,7 +396,7 @@ txStylesheetSink::GetInterface(const nsIID& aIID, void** aResult)
return NS_OK;
}
return QueryInterface(aIID, aResult);
return NS_ERROR_NO_INTERFACE;
}
class txCompileObserver : public txACompileObserver
@ -494,13 +469,19 @@ txCompileObserver::loadURI(const nsAString& aUri,
GetCodebasePrincipal(referrerUri, getter_AddRefs(referrerPrincipal));
NS_ENSURE_SUCCESS(rv, rv);
// Do security check
rv = nsContentUtils::
CheckSecurityBeforeLoad(uri, referrerPrincipal,
nsIScriptSecurityManager::STANDARD, PR_FALSE,
nsIContentPolicy::TYPE_STYLESHEET,
nsnull, NS_LITERAL_CSTRING("application/xml"));
// Content Policy
PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_STYLESHEET,
uri,
referrerPrincipal,
nsnull,
NS_LITERAL_CSTRING("application/xml"),
nsnull,
&shouldLoad);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_CP_REJECTED(shouldLoad)) {
return NS_ERROR_DOM_BAD_URI;
}
return startLoad(uri, aCompiler, referrerPrincipal);
}
@ -556,7 +537,14 @@ txCompileObserver::startLoad(nsIURI* aUri, txStylesheetCompiler* aCompiler,
parser->SetContentSink(sink);
parser->Parse(aUri);
return channel->AsyncOpen(sink, parser);
// Always install in case of redirects
nsCOMPtr<nsIStreamListener> listener =
new nsCrossSiteListenerProxy(sink, aReferrerPrincipal, channel,
PR_FALSE, &rv);
NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_SUCCESS(rv, rv);
return channel->AsyncOpen(listener, parser);
}
nsresult
@ -567,14 +555,20 @@ TX_LoadSheet(nsIURI* aUri, txMozillaXSLTProcessor* aProcessor,
aUri->GetSpec(spec);
PR_LOG(txLog::xslt, PR_LOG_ALWAYS, ("TX_LoadSheet: %s\n", spec.get()));
// Pass source document as the context
nsresult rv = nsContentUtils::
CheckSecurityBeforeLoad(aUri, aCallerPrincipal,
nsIScriptSecurityManager::STANDARD, PR_FALSE,
nsIContentPolicy::TYPE_STYLESHEET,
aProcessor->GetSourceContentModel(),
NS_LITERAL_CSTRING("application/xml"));
// Content Policy
PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
nsresult rv =
NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_STYLESHEET,
aUri,
aCallerPrincipal,
aProcessor->GetSourceContentModel(),
NS_LITERAL_CSTRING("application/xml"),
nsnull,
&shouldLoad);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_CP_REJECTED(shouldLoad)) {
return NS_ERROR_DOM_BAD_URI;
}
nsRefPtr<txCompileObserver> observer =
new txCompileObserver(aProcessor, aLoadGroup);
@ -716,13 +710,19 @@ txSyncCompileObserver::loadURI(const nsAString& aUri,
GetCodebasePrincipal(referrerUri, getter_AddRefs(referrerPrincipal));
NS_ENSURE_SUCCESS(rv, rv);
// Security checks
rv = nsContentUtils::
CheckSecurityBeforeLoad(uri, referrerPrincipal,
nsIScriptSecurityManager::STANDARD,
PR_FALSE, nsIContentPolicy::TYPE_STYLESHEET,
nsnull, NS_LITERAL_CSTRING("application/xml"));
// Content Policy
PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_STYLESHEET,
uri,
referrerPrincipal,
nsnull,
NS_LITERAL_CSTRING("application/xml"),
nsnull,
&shouldLoad);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_CP_REJECTED(shouldLoad)) {
return NS_ERROR_DOM_BAD_URI;
}
// This is probably called by js, a loadGroup for the channel doesn't
// make sense.

View File

@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -79,6 +79,15 @@ public:
PRUnichar) const;
};
class nsCaseInsensitiveStringArrayComparator
{
public:
template<class A, class B>
PRBool Equals(const A& a, const B& b) const {
return a.Equals(b, nsCaseInsensitiveStringComparator());
}
};
inline PRBool
CaseInsensitiveFindInReadable(const nsAString& aPattern,
nsAString::const_iterator& aSearchStart,

View File

@ -148,7 +148,7 @@
#include "nsMathMLParts.h"
#endif
#ifdef MOZ_SVG
#include "nsSVGUtils.h"
#include "nsSVGEffects.h"
#endif
nsIFrame*
@ -9826,7 +9826,7 @@ nsCSSFrameConstructor::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
NS_ASSERTION(frame, "This shouldn't happen");
#ifdef MOZ_SVG
if (hint & nsChangeHint_UpdateEffects) {
nsSVGUtils::UpdateEffects(frame);
nsSVGEffects::UpdateEffects(frame);
}
#endif
if (hint & nsChangeHint_ReflowFrame) {

View File

@ -102,11 +102,10 @@ class gfxContext;
typedef short SelectionType;
typedef PRUint32 nsFrameState;
// 134e504f-4fd1-4590-9f5d-899afee63d0f
// 5c103bc2-788e-4bbe-b82e-635bea34e78f
#define NS_IPRESSHELL_IID \
{ 0x134e504f, 0x4fd1, 0x4590, \
{ 0x9f, 0x5d, 0x89, 0x9a, 0xfe, 0xe6, 0x3d, 0x0f } }
{ 0x5c103bc2, 0x788e, 0x4bbe, \
{ 0xb8, 0x2e, 0x63, 0x5b, 0xea, 0x34, 0xe7, 0x8f } }
// Constants for ScrollContentIntoView() function
#define NS_PRESSHELL_SCROLL_TOP 0
@ -165,6 +164,8 @@ public:
* times to make form controls behave nicely when printed.
*/
NS_IMETHOD Destroy() = 0;
PRBool IsDestroying() { return mIsDestroying; }
// All frames owned by the shell are allocated from an arena. They are also recycled
// using free lists (separate free lists being maintained for each size_t).

View File

@ -2629,7 +2629,6 @@ nsLayoutUtils::CalculateContentBottom(nsIFrame* aFrame)
childList = aFrame->GetAdditionalChildListName(nextListID);
nextListID++;
} while (childList);
}
return contentBottom;

View File

@ -225,4 +225,12 @@
// {93ad72a6-02cd-4716-9626-d47d5ec275ec}
#define NS_DOMJSON_CID \
{ 0x93ad72a6, 0x02cd, 0x4716, { 0x96, 0x26, 0xd4, 0x7d, 0x5e, 0xc2, 0x75, 0xec } }
#ifdef MOZ_MEDIA
#define NS_HTMLAUDIOELEMENT_CID \
{ /* 1d40026b-4c44-4f6f-b158-26bb5e9c65e9 */ \
0x1d40026b, 0x4c44, 0x4f6f, \
{0xb1, 0x58, 0x26, 0xbb, 0x5e, 0x9c, 0x65, 0xe9}}
#endif
#endif /* nsLayoutCID_h__ */

View File

@ -223,6 +223,11 @@ class nsIDocumentLoaderFactory;
#define NS_HTMLOPTIONELEMENT_CONTRACTID \
"@mozilla.org/content/element/html;1?name=option"
#ifdef MOZ_MEDIA
#define NS_HTMLAUDIOELEMENT_CONTRACTID \
"@mozilla.org/content/element/html;1?name=audio"
#endif
/* 0ddf4df8-4dbb-4133-8b79-9afb966514f5 */
#define NS_PLUGINDOCLOADERFACTORY_CID \
{ 0x0ddf4df8, 0x4dbb, 0x4133, { 0x8b, 0x79, 0x9a, 0xfb, 0x96, 0x65, 0x14, 0xf5 } }
@ -492,6 +497,7 @@ MAKE_CTOR(CreatePreContentIterator, nsIContentIterator, NS_NewPre
MAKE_CTOR(CreateSubtreeIterator, nsIContentIterator, NS_NewContentSubtreeIterator)
// CreateHTMLImgElement, see below
// CreateHTMLOptionElement, see below
// CreateHTMLAudioElement, see below
MAKE_CTOR(CreateTextEncoder, nsIDocumentEncoder, NS_NewTextEncoder)
MAKE_CTOR(CreateHTMLCopyTextEncoder, nsIDocumentEncoder, NS_NewHTMLCopyTextEncoder)
MAKE_CTOR(CreateXMLContentSerializer, nsIContentSerializer, NS_NewXMLContentSerializer)
@ -668,6 +674,53 @@ UnregisterHTMLOptionElement(nsIComponentManager* aCompMgr,
return NS_OK;
}
#ifdef MOZ_MEDIA
static NS_IMETHODIMP
CreateHTMLAudioElement(nsISupports* aOuter, REFNSIID aIID, void** aResult)
{
*aResult = nsnull;
if (aOuter)
return NS_ERROR_NO_AGGREGATION;
// Note! NS_NewHTMLAudioElement is special cased to handle a null nodeinfo
nsCOMPtr<nsIContent> inst(NS_NewHTMLAudioElement(nsnull));
return inst ? inst->QueryInterface(aIID, aResult) : NS_ERROR_OUT_OF_MEMORY;
}
static NS_IMETHODIMP
RegisterHTMLAudioElement(nsIComponentManager *aCompMgr,
nsIFile* aPath,
const char* aRegistryLocation,
const char* aComponentType,
const nsModuleComponentInfo* aInfo)
{
nsCOMPtr<nsICategoryManager> catman =
do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
if (!catman)
return NS_ERROR_FAILURE;
nsXPIDLCString previous;
nsresult rv = catman->AddCategoryEntry(JAVASCRIPT_GLOBAL_CONSTRUCTOR_CATEGORY,
"Audio", NS_HTMLAUDIOELEMENT_CONTRACTID,
PR_TRUE, PR_TRUE, getter_Copies(previous));
NS_ENSURE_SUCCESS(rv, rv);
return catman->AddCategoryEntry(JAVASCRIPT_GLOBAL_CONSTRUCTOR_PROTO_ALIAS_CATEGORY,
"Audio", "HTMLAudioElement",
PR_TRUE, PR_TRUE, getter_Copies(previous));
}
static NS_IMETHODIMP
UnregisterHTMLAudioElement(nsIComponentManager* aCompMgr,
nsIFile* aPath,
const char* aRegistryLocation,
const nsModuleComponentInfo* aInfo)
{
// XXX remove category entry
return NS_OK;
}
#endif
static NS_METHOD
RegisterDataDocumentContentPolicy(nsIComponentManager *aCompMgr,
nsIFile* aPath,
@ -1000,7 +1053,7 @@ static const nsModuleComponentInfo gComponents[] = {
nsnull,
nsInspectorCSSUtilsConstructor },
// Needed to support "new Option;" and "new Image;" in JavaScript
// Needed to support "new Option;", "new Image;" and "new Audio;" in JavaScript
{ "HTML img element",
NS_HTMLIMAGEELEMENT_CID,
NS_HTMLIMGELEMENT_CONTRACTID,
@ -1015,6 +1068,15 @@ static const nsModuleComponentInfo gComponents[] = {
RegisterHTMLOptionElement,
UnregisterHTMLOptionElement },
#ifdef MOZ_MEDIA
{ "HTML audio element",
NS_HTMLAUDIOELEMENT_CID,
NS_HTMLAUDIOELEMENT_CONTRACTID,
CreateHTMLAudioElement,
RegisterHTMLAudioElement,
UnregisterHTMLAudioElement },
#endif
#ifdef MOZ_ENABLE_CANVAS
{ "Canvas 2D Rendering Context",
NS_CANVASRENDERINGCONTEXT2D_CID,

View File

@ -122,6 +122,7 @@
#ifdef MOZ_SVG
#include "nsSVGIntegrationUtils.h"
#include "nsSVGEffects.h"
#endif
#include "gfxContext.h"
@ -430,7 +431,8 @@ nsFrame::Init(nsIContent* aContent,
// Make bits that are currently off (see constructor) the same:
mState |= state & (NS_FRAME_SELECTED_CONTENT |
NS_FRAME_INDEPENDENT_SELECTION |
NS_FRAME_IS_SPECIAL);
NS_FRAME_IS_SPECIAL |
NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS);
}
if (mParent) {
nsFrameState state = mParent->GetStateBits();
@ -442,7 +444,7 @@ nsFrame::Init(nsIContent* aContent,
if (GetStyleDisplay()->HasTransform()) {
// The frame gets reconstructed if we toggle the -moz-transform
// property, so we can set this bit here and then ignore it.
mState |= NS_FRAME_MAY_BE_TRANSFORMED;
mState |= NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS;
}
DidSetStyleContext();
@ -495,6 +497,10 @@ nsFrame::RemoveFrame(nsIAtom* aListName,
void
nsFrame::Destroy()
{
#ifdef MOZ_SVG
nsSVGEffects::InvalidateDirectRenderingObservers(this);
#endif
// Get the view pointer now before the frame properties disappear
// when we call NotifyDestroyingFrame()
nsIView* view = GetView();
@ -675,7 +681,7 @@ nsIFrame::GetPaddingRect() const
PRBool
nsIFrame::IsTransformed() const
{
return (mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
return (mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) &&
GetStyleDisplay()->HasTransform();
}
@ -1198,7 +1204,8 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
/* If we're being transformed, we need to invert the matrix transform so that we don't
* grab points in the wrong coordinate system!
*/
if ((mState & NS_FRAME_MAY_BE_TRANSFORMED) && disp->HasTransform())
if ((mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) &&
disp->HasTransform())
dirtyRect = nsDisplayTransform::UntransformRect(dirtyRect, this, nsPoint(0, 0));
if (applyAbsPosClipping) {
@ -1318,7 +1325,8 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
/* If we're going to apply a transformation, wrap everything in an
* nsDisplayTransform.
*/
if ((mState & NS_FRAME_MAY_BE_TRANSFORMED) && disp->HasTransform()) {
if ((mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) &&
disp->HasTransform()) {
nsDisplayTransform* transform = new (aBuilder) nsDisplayTransform(this, &resultList);
if (!transform)
return NS_ERROR_OUT_OF_MEMORY;
@ -1468,7 +1476,7 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
// Child is composited if it's transformed, partially transparent, or has
// SVG effects.
PRBool isComposited = disp->mOpacity != 1.0f ||
((aChild->mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
((aChild->mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) &&
aChild->GetStyleDisplay()->HasTransform())
#ifdef MOZ_SVG
|| nsSVGIntegrationUtils::UsingEffectsForFrame(aChild)
@ -3684,7 +3692,7 @@ nsIFrame::InvalidateInternalAfterResize(const nsRect& aDamageRect, nscoord aX,
*
* See bug #452496 for more details.
*/
if ((mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
if ((mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) &&
GetStyleDisplay()->HasTransform()) {
nsRect newDamageRect;
newDamageRect.UnionRect(nsDisplayTransform::TransformRect
@ -3762,7 +3770,7 @@ nsIFrame::GetTransformMatrix(nsIFrame **aOutAncestor)
return gfxMatrix();
/* Keep iterating while the frame can't possibly be transformed. */
while (!((*aOutAncestor)->mState & NS_FRAME_MAY_BE_TRANSFORMED)) {
while (!(*aOutAncestor)->IsTransformed()) {
/* If no parent, stop iterating. Otherwise, update the ancestor. */
nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(*aOutAncestor);
if (!parent)
@ -3932,7 +3940,7 @@ nsIFrame::GetOverflowRectRelativeToParent() const
nsRect
nsIFrame::GetOverflowRectRelativeToSelf() const
{
if (!(mState & NS_FRAME_MAY_BE_TRANSFORMED) ||
if (!(mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) ||
!GetStyleDisplay()->HasTransform())
return GetOverflowRect();
return *static_cast<nsRect*>
@ -5632,7 +5640,7 @@ nsIFrame::FinishAndStoreOverflow(nsRect* aOverflowArea, nsSize aNewSize)
*aOverflowArea = GetAdditionalOverflow(*aOverflowArea, aNewSize);
/* If we're transformed, transform the overflow rect by the current transformation. */
if ((mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
if ((mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) &&
GetStyleDisplay()->HasTransform()) {
// Save overflow area before the transform
SetRectProperty(this, nsGkAtoms::preTransformBBoxProperty, *aOverflowArea);

View File

@ -105,10 +105,10 @@ struct nsMargin;
typedef class nsIFrame nsIBox;
// IID for the nsIFrame interface
// 626a1563-1bae-4a6e-8d2c-2dc2c13048dd
// 3459e7bb-2b22-4eb3-b60d-27d9f851b919
#define NS_IFRAME_IID \
{ 0x626a1563, 0x1bae, 0x4a6e, \
{ 0x8d, 0x2c, 0x2d, 0xc2, 0xc1, 0x30, 0x48, 0xdd } }
{ 0x3459e7bb, 0x2b22, 0x4eb3, \
{ 0xb6, 0x0d, 0x27, 0xd9, 0xf8, 0x51, 0xb9, 0x19 } }
/**
* Indication of how the frame can be split. This is used when doing runaround
@ -236,7 +236,9 @@ enum {
// to its coordinate system (e.g. CSS transform, SVG foreignObject).
// This is used primarily in GetTransformMatrix to optimize for the
// common case.
NS_FRAME_MAY_BE_TRANSFORMED = 0x00010000,
// ALSO, if this bit is set, the frame's first-continuation may
// have an associated nsSVGRenderingObserverList.
NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS = 0x00010000,
#ifdef IBMBIDI
// If this bit is set, the frame itself is a bidi continuation,
@ -1582,22 +1584,23 @@ public:
eMathML = 1 << 0,
eSVG = 1 << 1,
eSVGForeignObject = 1 << 2,
eBidiInlineContainer = 1 << 3,
eSVGContainer = 1 << 3,
eBidiInlineContainer = 1 << 4,
// the frame is for a replaced element, such as an image
eReplaced = 1 << 4,
eReplaced = 1 << 5,
// Frame that contains a block but looks like a replaced element
// from the outside
eReplacedContainsBlock = 1 << 5,
eReplacedContainsBlock = 1 << 6,
// A frame that participates in inline reflow, i.e., one that
// requires nsHTMLReflowState::mLineLayout.
eLineParticipant = 1 << 6,
eXULBox = 1 << 7,
eCanContainOverflowContainers = 1 << 8,
eBlockFrame = 1 << 9,
eLineParticipant = 1 << 7,
eXULBox = 1 << 8,
eCanContainOverflowContainers = 1 << 9,
eBlockFrame = 1 << 10,
// If this bit is set, the frame doesn't allow ignorable whitespace as
// children. For example, the whitespace between <table>\n<tr>\n<td>
// will be excluded during the construction of children.
eExcludesIgnorableWhitespace = 1 << 10,
eExcludesIgnorableWhitespace = 1 << 11,
// These are to allow nsFrame::Init to assert that IsFrameOfType
// implementations all call the base class method. They are only

View File

@ -4,7 +4,7 @@
-->
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
class="reftest-wait"
onload="setTimeout(doTest,10)"
onload="setTimeout(doTest,500)"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Testing that dynamic changes to the element for a given ID are reflected in clip-path</title>
<defs>

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -19,7 +19,14 @@ include moz-only/reftest.list
== dynamic-conditions-01.svg pass.svg
== dynamic-clipPath-01.svg pass.svg
== dynamic-feFlood-01.svg pass.svg
== dynamic-filter-contents-01.svg dynamic-filter-contents-01-ref.svg
== dynamic-gradient-contents-01.svg pass.svg
== dynamic-link-style-01.svg pass.svg
== dynamic-mask-contents-01.svg pass.svg
== dynamic-pattern-01.svg pass.svg
== dynamic-pattern-02.svg pass.svg
== dynamic-pattern-contents-01.svg pass.svg
== dynamic-pattern-contents-02.svg pass.svg
== dynamic-rect-01.svg dynamic-rect-01-ref.svg
== dynamic-rect-02.svg dynamic-rect-02-ref.svg
== dynamic-rect-03.svg dynamic-rect-03-ref.svg

View File

@ -0,0 +1,6 @@
<box xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<stack>
<label top="20px" left="20px" style="color: green;" value="Hello!"/>
<label top="24px" left="24px" style="color: black;" value="Hello!"/>
</stack>
</box>

View File

@ -0,0 +1,5 @@
<box xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<stack>
<label top="24px" left="24px" style="color: black; text-shadow: green -4px -4px" value="Hello!"/>
</stack>
</box>

View File

@ -0,0 +1,6 @@
<box xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<stack>
<label top="33px" left="33px" style="color: grey;" value="Hello"/>
<label top="30px" left="30px" style="color: green;" value="Hello"/>
</stack>
</box>

View File

@ -0,0 +1,5 @@
<box xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<stack>
<label top="30px" left="30px" style="color: green; text-shadow: grey 3px 3px;" value="Hello"/>
</stack>
</box>

View File

@ -0,0 +1,3 @@
<box xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<label style="text-shadow: blue 4px 4px" value="The shadow should be blurred"/>
</box>

View File

@ -0,0 +1,3 @@
<box xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<label style="text-shadow: blue 4px 4px 2px" value="The shadow should be blurred"/>
</box>

View File

@ -0,0 +1,6 @@
<box xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<stack>
<label top="27px" left="27px" style="color:blue;" value="Hello"/>
<label top="24px" left="24px" style="color:blue;" value="Hello"/>
</stack>
</box>

View File

@ -0,0 +1,5 @@
<box xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<stack>
<label top="24px" left="24px" style="color:blue; text-shadow: 3px 3px;" value="Hello"/>
</stack>
</box>

View File

@ -0,0 +1,9 @@
<box xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<stack>
<label top="33px" left="33px" style="color:grey;" value="Hello"/>
<label top="30px" left="30px" style="color:green;" value="Hello"/>
<label top="27px" left="27px" style="color:red;" value="Hello"/>
<label top="24px" left="24px" style="color:purple;" value="Hello"/>
<label top="20px" left="20px" style="color:black;" value="Hello"/>
</stack>
</box>

View File

@ -0,0 +1,5 @@
<box xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<stack>
<label top="20px" left="20px" style="color:black; text-shadow: purple 4px 4px, red 7px 7px, green 10px 10px, grey 13px 13px;" value="Hello"/>
</stack>
</box>

View File

@ -1,3 +1,9 @@
== basic.xul basic-ref.xul
== basic-negcoord.xul basic-negcoord-ref.xul
!= blur.xul blur-notref.xul
== color-inherit.xul color-inherit-ref.xul
== multiple-noblur.xul multiple-noblur-ref.xul
== basic.html basic-ref.html
== basic-negcoord.html basic-negcoord-ref.html
!= blur.html blur-notref.html
@ -7,3 +13,4 @@
== multiple-noblur.html multiple-noblur-ref.html
== quirks-decor-noblur.html quirks-decor-noblur-ref.html
== standards-decor-noblur.html standards-decor-noblur-ref.html

View File

@ -450,6 +450,7 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther)
#ifdef MOZ_SVG
maxHint = nsChangeHint(NS_STYLE_HINT_REFLOW | nsChangeHint_UpdateEffects);
DO_STRUCT_DIFFERENCE(SVGReset);
DO_STRUCT_DIFFERENCE(SVG);
#endif
// At this point, we know that the worst kind of damage we could do is
@ -475,9 +476,6 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther)
// re-render to occur. VISUAL Structs: Color, Background
DO_STRUCT_DIFFERENCE(Color);
DO_STRUCT_DIFFERENCE(Background);
#ifdef MOZ_SVG
DO_STRUCT_DIFFERENCE(SVG);
#endif
#undef DO_STRUCT_DIFFERENCE

View File

@ -803,12 +803,39 @@ nsStyleSVG::nsStyleSVG(const nsStyleSVG& aSource)
mTextRendering = aSource.mTextRendering;
}
static PRBool PaintURIChanged(const nsStyleSVGPaint& aPaint1,
const nsStyleSVGPaint& aPaint2)
{
if (aPaint1.mType != aPaint2.mType) {
return aPaint1.mType == eStyleSVGPaintType_Server ||
aPaint2.mType == eStyleSVGPaintType_Server;
}
return aPaint1.mType == eStyleSVGPaintType_Server &&
!EqualURIs(aPaint1.mPaint.mPaintServer, aPaint2.mPaint.mPaintServer);
}
nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const
{
if ( mFill != aOther.mFill ||
mStroke != aOther.mStroke ||
nsChangeHint hint = nsChangeHint(0);
!EqualURIs(mMarkerEnd, aOther.mMarkerEnd) ||
if (mTextRendering != aOther.mTextRendering) {
NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
// May be needed for non-svg frames
NS_UpdateHint(hint, nsChangeHint_ReflowFrame);
}
if (mFill != aOther.mFill ||
mStroke != aOther.mStroke) {
NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
if (PaintURIChanged(mFill, aOther.mFill) ||
PaintURIChanged(mStroke, aOther.mStroke)) {
NS_UpdateHint(hint, nsChangeHint_UpdateEffects);
}
// Nothing more to do, below we can only set "repaint"
return hint;
}
if ( !EqualURIs(mMarkerEnd, aOther.mMarkerEnd) ||
!EqualURIs(mMarkerMid, aOther.mMarkerMid) ||
!EqualURIs(mMarkerStart, aOther.mMarkerStart) ||
@ -823,28 +850,32 @@ nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const
mColorInterpolation != aOther.mColorInterpolation ||
mColorInterpolationFilters != aOther.mColorInterpolationFilters ||
mFillRule != aOther.mFillRule ||
mPointerEvents != aOther.mPointerEvents ||
mShapeRendering != aOther.mShapeRendering ||
mStrokeDasharrayLength != aOther.mStrokeDasharrayLength ||
mStrokeLinecap != aOther.mStrokeLinecap ||
mStrokeLinejoin != aOther.mStrokeLinejoin ||
mTextAnchor != aOther.mTextAnchor ||
mTextRendering != aOther.mTextRendering)
return NS_STYLE_HINT_VISUAL;
mTextAnchor != aOther.mTextAnchor) {
NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
return hint;
}
// length of stroke dasharrays are the same (tested above) - check entries
for (PRUint32 i=0; i<mStrokeDasharrayLength; i++)
if (mStrokeDasharray[i] != aOther.mStrokeDasharray[i])
return NS_STYLE_HINT_VISUAL;
if (mStrokeDasharray[i] != aOther.mStrokeDasharray[i]) {
NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
return hint;
}
return NS_STYLE_HINT_NONE;
return hint;
}
#ifdef DEBUG
/* static */
nsChangeHint nsStyleSVG::MaxDifference()
{
return NS_STYLE_HINT_VISUAL;
return NS_CombineHint(NS_CombineHint(nsChangeHint_UpdateEffects,
nsChangeHint_ReflowFrame),
nsChangeHint_RepaintFrame);
}
#endif
@ -891,14 +922,12 @@ nsChangeHint nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) cons
NS_UpdateHint(hint, nsChangeHint_UpdateEffects);
NS_UpdateHint(hint, nsChangeHint_ReflowFrame);
NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
}
if (mStopColor != aOther.mStopColor ||
mFloodColor != aOther.mFloodColor ||
mLightingColor != aOther.mLightingColor ||
mStopOpacity != aOther.mStopOpacity ||
mFloodOpacity != aOther.mFloodOpacity ||
mDominantBaseline != aOther.mDominantBaseline)
} else if (mStopColor != aOther.mStopColor ||
mFloodColor != aOther.mFloodColor ||
mLightingColor != aOther.mLightingColor ||
mStopOpacity != aOther.mStopOpacity ||
mFloodOpacity != aOther.mFloodOpacity ||
mDominantBaseline != aOther.mDominantBaseline)
NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
return hint;

View File

@ -104,10 +104,11 @@ include $(topsrcdir)/config/config.mk
FORCE_STATIC_LIB = 1
EXPORTS = \
nsSVGIntegrationUtils.h \
nsSVGUtils.h \
nsSVGEffects.h \
nsSVGFilterInstance.h \
nsSVGForeignObjectFrame.h \
nsSVGIntegrationUtils.h \
nsSVGUtils.h \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -102,12 +102,11 @@ public:
nsIFrame *mClipParent;
nsCOMPtr<nsIDOMSVGMatrix> mClipParentMatrix;
// recursion prevention flag
PRPackedBool mInUse;
// nsSVGContainerFrame methods:
virtual already_AddRefed<nsIDOMSVGMatrix> GetCanvasTM();
// recursion prevention flag
PRPackedBool mInUse;
};
#endif

View File

@ -71,7 +71,8 @@ public:
virtual PRBool IsFrameOfType(PRUint32 aFlags) const
{
return nsSVGContainerFrameBase::IsFrameOfType(aFlags & ~(nsIFrame::eSVG));
return nsSVGContainerFrameBase::IsFrameOfType(
aFlags & ~(nsIFrame::eSVG | nsIFrame::eSVGContainer));
}
};

View File

@ -38,12 +38,18 @@
#include "nsSVGEffects.h"
#include "nsISupportsImpl.h"
#include "nsSVGOuterSVGFrame.h"
#include "nsSVGFilterFrame.h"
#include "nsSVGClipPathFrame.h"
#include "nsSVGMaskFrame.h"
NS_IMPL_ISUPPORTS1(nsSVGPropertyBase, nsIMutationObserver)
NS_IMPL_ISUPPORTS1(nsSVGRenderingObserver, nsIMutationObserver)
nsSVGPropertyBase::nsSVGPropertyBase(nsIURI *aURI,
nsIFrame *aFrame)
: mElement(this), mFrame(aFrame)
nsSVGRenderingObserver::nsSVGRenderingObserver(nsIURI *aURI,
nsIFrame *aFrame)
: mElement(this), mFrame(aFrame),
mFramePresShell(aFrame->PresContext()->PresShell()),
mReferencedFrame(nsnull),
mReferencedFramePresShell(nsnull)
{
// Start watching the target element
mElement.Reset(aFrame->GetContent(), aURI);
@ -52,22 +58,45 @@ nsSVGPropertyBase::nsSVGPropertyBase(nsIURI *aURI,
}
}
nsSVGPropertyBase::~nsSVGPropertyBase()
nsSVGRenderingObserver::~nsSVGRenderingObserver()
{
if (mElement.get()) {
mElement.get()->RemoveMutationObserver(this);
}
if (mReferencedFrame && !mReferencedFramePresShell->IsDestroying()) {
nsSVGEffects::RemoveRenderingObserver(mReferencedFrame, this);
}
}
nsIFrame*
nsSVGPropertyBase::GetReferencedFrame(nsIAtom* aFrameType, PRBool* aOK)
nsSVGRenderingObserver::GetReferencedFrame()
{
if (mReferencedFrame && !mReferencedFramePresShell->IsDestroying()) {
NS_ASSERTION(mElement.get() &&
static_cast<nsGenericElement*>(mElement.get())->GetPrimaryFrame() == mReferencedFrame,
"Cached frame is incorrect!");
return mReferencedFrame;
}
if (mElement.get()) {
nsIFrame *frame =
static_cast<nsGenericElement*>(mElement.get())->GetPrimaryFrame();
if (frame && frame->GetType() == aFrameType)
if (frame) {
mReferencedFrame = frame;
mReferencedFramePresShell = mReferencedFrame->PresContext()->PresShell();
nsSVGEffects::AddRenderingObserver(mReferencedFrame, this);
return frame;
}
}
return nsnull;
}
nsIFrame*
nsSVGRenderingObserver::GetReferencedFrame(nsIAtom* aFrameType, PRBool* aOK)
{
nsIFrame* frame = GetReferencedFrame();
if (frame && frame->GetType() == aFrameType)
return frame;
if (aOK) {
*aOK = PR_FALSE;
}
@ -75,57 +104,93 @@ nsSVGPropertyBase::GetReferencedFrame(nsIAtom* aFrameType, PRBool* aOK)
}
void
nsSVGPropertyBase::AttributeChanged(nsIDocument *aDocument,
nsIContent *aContent,
PRInt32 aNameSpaceID,
nsIAtom *aAttribute,
PRInt32 aModType,
PRUint32 aStateMask)
nsSVGRenderingObserver::DoUpdate()
{
if (mFramePresShell->IsDestroying()) {
// mFrame is no longer valid. Bail out.
mFrame = nsnull;
return;
}
if (mReferencedFrame) {
nsSVGEffects::RemoveRenderingObserver(mReferencedFrame, this);
mReferencedFrame = nsnull;
mReferencedFramePresShell = nsnull;
}
if (mFrame && mFrame->IsFrameOfType(nsIFrame::eSVG)) {
// Changes should propagate out to things that might be observing
// the referencing frame or its ancestors.
nsSVGEffects::InvalidateRenderingObservers(mFrame);
}
}
void
nsSVGRenderingObserver::InvalidateViaReferencedFrame()
{
// Clear mReferencedFrame since the referenced frame has already
// dropped its reference back to us
mReferencedFrame = nsnull;
mReferencedFramePresShell = nsnull;
DoUpdate();
}
void
nsSVGRenderingObserver::AttributeChanged(nsIDocument *aDocument,
nsIContent *aContent,
PRInt32 aNameSpaceID,
nsIAtom *aAttribute,
PRInt32 aModType,
PRUint32 aStateMask)
{
DoUpdate();
}
void
nsSVGPropertyBase::ContentAppended(nsIDocument *aDocument,
nsIContent *aContainer,
PRInt32 aNewIndexInContainer)
nsSVGRenderingObserver::ContentAppended(nsIDocument *aDocument,
nsIContent *aContainer,
PRInt32 aNewIndexInContainer)
{
DoUpdate();
}
void
nsSVGPropertyBase::ContentInserted(nsIDocument *aDocument,
nsIContent *aContainer,
nsIContent *aChild,
PRInt32 aIndexInContainer)
nsSVGRenderingObserver::ContentInserted(nsIDocument *aDocument,
nsIContent *aContainer,
nsIContent *aChild,
PRInt32 aIndexInContainer)
{
DoUpdate();
}
void
nsSVGPropertyBase::ContentRemoved(nsIDocument *aDocument,
nsIContent *aContainer,
nsIContent *aChild,
PRInt32 aIndexInContainer)
nsSVGRenderingObserver::ContentRemoved(nsIDocument *aDocument,
nsIContent *aContainer,
nsIContent *aChild,
PRInt32 aIndexInContainer)
{
DoUpdate();
}
NS_IMPL_ISUPPORTS_INHERITED1(nsSVGFilterProperty,
nsSVGPropertyBase,
nsSVGRenderingObserver,
nsISVGFilterProperty)
nsSVGFilterProperty::nsSVGFilterProperty(nsIURI *aURI,
nsIFrame *aFilteredFrame)
: nsSVGPropertyBase(aURI, aFilteredFrame)
: nsSVGRenderingObserver(aURI, aFilteredFrame)
{
UpdateRect();
}
nsSVGFilterFrame *
nsSVGFilterProperty::GetFilterFrame() {
return static_cast<nsSVGFilterFrame *>
(GetReferencedFrame(nsGkAtoms::svgFilterFrame, nsnull));
}
void
nsSVGFilterProperty::UpdateRect()
{
nsSVGFilterFrame *filter = GetFilterFrame(nsnull);
nsSVGFilterFrame *filter = GetFilterFrame();
if (filter) {
mFilterRect = filter->GetFilterBBox(mFrame, nsnull);
mFilterRect.ScaleRoundOut(filter->PresContext()->AppUnitsPerDevPixel());
@ -134,96 +199,69 @@ nsSVGFilterProperty::UpdateRect()
}
}
static void
InvalidateAllContinuations(nsIFrame* aFrame)
{
for (nsIFrame* f = aFrame; f; f = f->GetNextContinuation()) {
f->InvalidateOverflowRect();
}
}
void
nsSVGFilterProperty::DoUpdate()
{
nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(mFrame);
if (outerSVGFrame) {
outerSVGFrame->Invalidate(mFilterRect);
UpdateRect();
outerSVGFrame->Invalidate(mFilterRect);
}
}
void
nsSVGFilterProperty::ParentChainChanged(nsIContent *aContent)
{
if (aContent->IsInDoc())
nsSVGRenderingObserver::DoUpdate();
if (!mFrame)
return;
nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(mFrame);
if (outerSVGFrame) {
outerSVGFrame->InvalidateCoveredRegion(mFrame);
}
mFrame->DeleteProperty(nsGkAtoms::filter);
}
void
nsSVGClipPathProperty::DoUpdate()
{
nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(mFrame);
if (outerSVGFrame) {
outerSVGFrame->InvalidateCoveredRegion(mFrame);
if (mFrame->IsFrameOfType(nsIFrame::eSVG)) {
nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(mFrame);
if (outerSVGFrame) {
outerSVGFrame->Invalidate(mFilterRect);
UpdateRect();
outerSVGFrame->Invalidate(mFilterRect);
}
} else {
InvalidateAllContinuations(mFrame);
// Reflow so that changes in the filter overflow area get picked up
mFramePresShell->FrameNeedsReflow(
mFrame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
}
}
void
nsSVGClipPathProperty::ParentChainChanged(nsIContent *aContent)
nsSVGPaintingProperty::DoUpdate()
{
if (aContent->IsInDoc())
nsSVGRenderingObserver::DoUpdate();
if (!mFrame)
return;
nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(mFrame);
if (outerSVGFrame) {
outerSVGFrame->InvalidateCoveredRegion(mFrame);
}
mFrame->DeleteProperty(nsGkAtoms::clipPath);
}
void
nsSVGMaskProperty::DoUpdate()
{
nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(mFrame);
if (outerSVGFrame) {
outerSVGFrame->InvalidateCoveredRegion(mFrame);
if (mFrame->IsFrameOfType(nsIFrame::eSVG)) {
nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(mFrame);
if (outerSVGFrame) {
outerSVGFrame->InvalidateCoveredRegion(mFrame);
}
} else {
InvalidateAllContinuations(mFrame);
}
}
void
nsSVGMaskProperty::ParentChainChanged(nsIContent *aContent)
{
if (aContent->IsInDoc())
return;
nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(mFrame);
if (outerSVGFrame) {
outerSVGFrame->InvalidateCoveredRegion(mFrame);
}
mFrame->DeleteProperty(nsGkAtoms::mask);
}
static nsSVGPropertyBase *
static nsSVGRenderingObserver *
CreateFilterProperty(nsIURI *aURI, nsIFrame *aFrame)
{ return new nsSVGFilterProperty(aURI, aFrame); }
static nsSVGPropertyBase *
CreateMaskProperty(nsIURI *aURI, nsIFrame *aFrame)
{ return new nsSVGMaskProperty(aURI, aFrame); }
static nsSVGRenderingObserver *
CreatePaintingProperty(nsIURI *aURI, nsIFrame *aFrame)
{ return new nsSVGPaintingProperty(aURI, aFrame); }
static nsSVGPropertyBase *
CreateClipPathProperty(nsIURI *aURI, nsIFrame *aFrame)
{ return new nsSVGClipPathProperty(aURI, aFrame); }
static nsSVGPropertyBase *
static nsSVGRenderingObserver *
GetEffectProperty(nsIURI *aURI, nsIFrame *aFrame, nsIAtom *aProp,
nsSVGPropertyBase * (* aCreate)(nsIURI *, nsIFrame *))
nsSVGRenderingObserver * (* aCreate)(nsIURI *, nsIFrame *))
{
if (!aURI)
return nsnull;
nsSVGPropertyBase *prop = static_cast<nsSVGPropertyBase*>(aFrame->GetProperty(aProp));
nsSVGRenderingObserver *prop =
static_cast<nsSVGRenderingObserver*>(aFrame->GetProperty(aProp));
if (prop)
return prop;
prop = aCreate(aURI, aFrame);
@ -236,6 +274,13 @@ GetEffectProperty(nsIURI *aURI, nsIFrame *aFrame, nsIAtom *aProp,
return prop;
}
nsSVGPaintingProperty *
nsSVGEffects::GetPaintingProperty(nsIURI *aURI, nsIFrame *aFrame, nsIAtom *aProp)
{
return static_cast<nsSVGPaintingProperty*>(
GetEffectProperty(aURI, aFrame, aProp, CreatePaintingProperty));
}
nsSVGEffects::EffectProperties
nsSVGEffects::GetEffectProperties(nsIFrame *aFrame)
{
@ -245,19 +290,36 @@ nsSVGEffects::GetEffectProperties(nsIFrame *aFrame)
const nsStyleSVGReset *style = aFrame->GetStyleSVGReset();
result.mFilter = static_cast<nsSVGFilterProperty*>
(GetEffectProperty(style->mFilter, aFrame, nsGkAtoms::filter, CreateFilterProperty));
result.mClipPath = static_cast<nsSVGClipPathProperty*>
(GetEffectProperty(style->mClipPath, aFrame, nsGkAtoms::clipPath, CreateClipPathProperty));
result.mMask = static_cast<nsSVGMaskProperty*>
(GetEffectProperty(style->mMask, aFrame, nsGkAtoms::mask, CreateMaskProperty));
result.mClipPath = GetPaintingProperty(style->mClipPath, aFrame, nsGkAtoms::clipPath);
result.mMask = GetPaintingProperty(style->mMask, aFrame, nsGkAtoms::mask);
return result;
}
nsSVGClipPathFrame *
nsSVGEffects::EffectProperties::GetClipPathFrame(PRBool *aOK) {
if (!mClipPath)
return nsnull;
return static_cast<nsSVGClipPathFrame *>
(mClipPath->GetReferencedFrame(nsGkAtoms::svgClipPathFrame, aOK));
}
nsSVGMaskFrame *
nsSVGEffects::EffectProperties::GetMaskFrame(PRBool *aOK) {
if (!mMask)
return nsnull;
return static_cast<nsSVGMaskFrame *>
(mMask->GetReferencedFrame(nsGkAtoms::svgMaskFrame, aOK));
}
void
nsSVGEffects::UpdateEffects(nsIFrame *aFrame)
{
aFrame->DeleteProperty(nsGkAtoms::filter);
aFrame->DeleteProperty(nsGkAtoms::mask);
aFrame->DeleteProperty(nsGkAtoms::clipPath);
aFrame->DeleteProperty(nsGkAtoms::stroke);
aFrame->DeleteProperty(nsGkAtoms::fill);
}
nsSVGFilterProperty *
@ -270,3 +332,98 @@ nsSVGEffects::GetFilterProperty(nsIFrame *aFrame)
return static_cast<nsSVGFilterProperty *>(aFrame->GetProperty(nsGkAtoms::filter));
}
static PLDHashOperator PR_CALLBACK
GatherEnumerator(nsVoidPtrHashKey* aEntry, void* aArg)
{
nsTArray<nsSVGRenderingObserver*>* array =
static_cast<nsTArray<nsSVGRenderingObserver*>*>(aArg);
array->AppendElement(static_cast<nsSVGRenderingObserver*>(
const_cast<void*>(aEntry->GetKey())));
return PL_DHASH_REMOVE;
}
void
nsSVGRenderingObserverList::InvalidateAll()
{
if (mObservers.Count() == 0)
return;
nsAutoTArray<nsSVGRenderingObserver*,10> observers;
mObservers.EnumerateEntries(GatherEnumerator, &observers);
for (PRUint32 i = 0; i < observers.Length(); ++i) {
observers[i]->InvalidateViaReferencedFrame();
}
}
static nsSVGRenderingObserverList *
GetObserverList(nsIFrame *aFrame)
{
if (!(aFrame->GetStateBits() & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS))
return nsnull;
return static_cast<nsSVGRenderingObserverList*>(aFrame->GetProperty(nsGkAtoms::observer));
}
void
nsSVGEffects::AddRenderingObserver(nsIFrame *aFrame, nsSVGRenderingObserver *aObserver)
{
NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame must be first continuation");
nsSVGRenderingObserverList *observerList = GetObserverList(aFrame);
if (!observerList) {
observerList = new nsSVGRenderingObserverList();
if (!observerList)
return;
for (nsIFrame* f = aFrame; f; f = f->GetNextContinuation()) {
f->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS);
}
aFrame->SetProperty(nsGkAtoms::observer, observerList);
}
observerList->Add(aObserver);
}
void
nsSVGEffects::RemoveRenderingObserver(nsIFrame *aFrame, nsSVGRenderingObserver *aObserver)
{
NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame must be first continuation");
nsSVGRenderingObserverList *observerList = GetObserverList(aFrame);
if (observerList) {
observerList->Remove(aObserver);
// Don't remove the property even if the observer list is empty.
// This might not be a good time to modify the frame property
// hashtables.
}
}
void
nsSVGEffects::InvalidateRenderingObservers(nsIFrame *aFrame)
{
NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame must be first continuation");
nsSVGRenderingObserverList *observerList = GetObserverList(aFrame);
if (observerList) {
observerList->InvalidateAll();
return;
}
// Check ancestor SVG containers. The root frame cannot be of type
// eSVGContainer so we don't have to check f for null here.
for (nsIFrame *f = aFrame->GetParent();
f->IsFrameOfType(nsIFrame::eSVGContainer); f = f->GetParent()) {
observerList = GetObserverList(f);
if (observerList) {
observerList->InvalidateAll();
return;
}
}
}
void
nsSVGEffects::InvalidateDirectRenderingObservers(nsIFrame *aFrame)
{
nsSVGRenderingObserverList *observerList = GetObserverList(aFrame);
if (observerList) {
observerList->InvalidateAll();
}
}

View File

@ -43,14 +43,26 @@
#include "nsReferencedElement.h"
#include "nsStubMutationObserver.h"
#include "nsSVGUtils.h"
#include "nsSVGFilterFrame.h"
#include "nsSVGClipPathFrame.h"
#include "nsSVGMaskFrame.h"
#include "nsTHashtable.h"
class nsSVGPropertyBase : public nsStubMutationObserver {
class nsSVGClipPathFrame;
class nsSVGFilterFrame;
class nsSVGMaskFrame;
/*
* SVG elements reference supporting resources by element ID. We need to
* track when those resources change and when the DOM changes in ways
* that affect which element is referenced by a given ID (e.g., when
* element IDs change). The code here is responsible for that.
*
* When a frame references a supporting resource, we create a property
* object derived from nsSVGRenderingObserver to manage the relationship. The
* property object is attached to the referencing frame.
*/
class nsSVGRenderingObserver : public nsStubMutationObserver {
public:
nsSVGPropertyBase(nsIURI* aURI, nsIFrame *aFrame);
virtual ~nsSVGPropertyBase();
nsSVGRenderingObserver(nsIURI* aURI, nsIFrame *aFrame);
virtual ~nsSVGRenderingObserver();
// nsISupports
NS_DECL_ISUPPORTS
@ -61,13 +73,22 @@ public:
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
void InvalidateViaReferencedFrame();
nsIFrame* GetReferencedFrame();
/**
* @param aOK this is only for the convenience of callers. We set *aOK to false
* if this function returns null.
*/
nsIFrame* GetReferencedFrame(nsIAtom* aFrameType, PRBool* aOK);
protected:
// This is called when the referenced filter/mask/clipPath changes
virtual void DoUpdate() = 0;
// This is called when the referenced resource changes.
virtual void DoUpdate();
class SourceReference : public nsReferencedElement {
public:
SourceReference(nsSVGPropertyBase* aContainer) : mContainer(aContainer) {}
SourceReference(nsSVGRenderingObserver* aContainer) : mContainer(aContainer) {}
protected:
virtual void ContentChanged(nsIContent* aFrom, nsIContent* aTo) {
if (aFrom) {
@ -85,41 +106,41 @@ protected:
*/
virtual PRBool IsPersistent() { return PR_TRUE; }
private:
nsSVGPropertyBase* mContainer;
nsSVGRenderingObserver* mContainer;
};
/**
* @param aOK this is only for the convenience of callers. We set *aOK to false
* if this function returns null.
*/
nsIFrame* GetReferencedFrame(nsIAtom* aFrameType, PRBool* aOK);
SourceReference mElement;
nsIFrame *mFrame;
// The frame that this property is attached to
nsIFrame *mFrame;
// When a presshell is torn down, we don't delete the properties for
// each frame until after the frames are destroyed. So here we remember
// the presshell for the frames we care about and, before we use the frame,
// we test the presshell to see if it's destroying itself. If it is,
// then the frame pointer is not valid and we know the frame has gone away.
nsIPresShell *mFramePresShell;
nsIFrame *mReferencedFrame;
nsIPresShell *mReferencedFramePresShell;
};
class nsSVGFilterProperty :
public nsSVGPropertyBase, public nsISVGFilterProperty {
public nsSVGRenderingObserver, public nsISVGFilterProperty {
public:
nsSVGFilterProperty(nsIURI *aURI, nsIFrame *aFilteredFrame);
nsSVGFilterFrame *GetFilterFrame(PRBool *aOK) {
return static_cast<nsSVGFilterFrame *>
(GetReferencedFrame(nsGkAtoms::svgFilterFrame, aOK));
}
/**
* @return the filter frame, or null if there is no filter frame
*/
nsSVGFilterFrame *GetFilterFrame();
void UpdateRect();
// nsISupports
NS_DECL_ISUPPORTS
// nsIMutationObserver
NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED
// nsISVGFilterProperty
virtual void Invalidate() { DoUpdate(); }
private:
// nsSVGPropertyBase
// nsSVGRenderingObserver
virtual void DoUpdate();
// Tracks a bounding box for the filtered mFrame. We need this so that
@ -129,69 +150,125 @@ private:
nsRect mFilterRect;
};
class nsSVGClipPathProperty : public nsSVGPropertyBase {
class nsSVGPaintingProperty : public nsSVGRenderingObserver {
public:
nsSVGClipPathProperty(nsIURI *aURI, nsIFrame *aClippedFrame)
: nsSVGPropertyBase(aURI, aClippedFrame) {}
nsSVGPaintingProperty(nsIURI *aURI, nsIFrame *aClippedFrame)
: nsSVGRenderingObserver(aURI, aClippedFrame) {}
nsSVGClipPathFrame *GetClipPathFrame(PRBool *aOK) {
return static_cast<nsSVGClipPathFrame *>
(GetReferencedFrame(nsGkAtoms::svgClipPathFrame, aOK));
}
// nsIMutationObserver
NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED
private:
protected:
virtual void DoUpdate();
};
class nsSVGMaskProperty : public nsSVGPropertyBase {
/**
* A manager for one-shot nsSVGRenderingObserver tracking.
* nsSVGRenderingObservers can be added or removed. They are not strongly
* referenced so an observer must be removed before it before it dies.
* When InvalidateAll is called, all outstanding references get
* InvalidateViaReferencedFrame()
* called on them and the list is cleared. The intent is that
* the observer will force repainting of whatever part of the document
* is needed, and then at paint time the observer will be re-added.
*
* InvalidateAll must be called before this object is destroyed, i.e.
* before the referenced frame is destroyed. This should normally happen
* via nsSVGContainerFrame::RemoveFrame, since only frames in the frame
* tree should be referenced.
*/
class nsSVGRenderingObserverList {
public:
nsSVGMaskProperty(nsIURI *aURI, nsIFrame *aMaskedFrame)
: nsSVGPropertyBase(aURI, aMaskedFrame) {}
nsSVGRenderingObserverList() { mObservers.Init(5); }
~nsSVGRenderingObserverList() { InvalidateAll(); }
nsSVGMaskFrame *GetMaskFrame(PRBool *aOK) {
return static_cast<nsSVGMaskFrame *>
(GetReferencedFrame(nsGkAtoms::svgMaskFrame, aOK));
}
// nsIMutationObserver
NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED
void Add(nsSVGRenderingObserver* aObserver)
{ mObservers.PutEntry(aObserver); }
void Remove(nsSVGRenderingObserver* aObserver)
{ mObservers.RemoveEntry(aObserver); }
void InvalidateAll();
private:
virtual void DoUpdate();
nsTHashtable<nsVoidPtrHashKey> mObservers;
};
class nsSVGEffects {
public:
struct EffectProperties {
nsSVGFilterProperty* mFilter;
nsSVGMaskProperty* mMask;
nsSVGClipPathProperty* mClipPath;
nsSVGPaintingProperty* mMask;
nsSVGPaintingProperty* mClipPath;
/**
* @return the clip-path frame, or null if there is no clip-path frame
* @param aOK if a clip-path was specified but the designated element
* does not exist or is an element of the wrong type, *aOK is set
* to false. Otherwise *aOK is untouched.
*/
nsSVGClipPathFrame *GetClipPathFrame(PRBool *aOK);
/**
* @return the mask frame, or null if there is no mask frame
* @param aOK if a mask was specified but the designated element
* does not exist or is an element of the wrong type, *aOK is set
* to false. Otherwise *aOK is untouched.
*/
nsSVGMaskFrame *GetMaskFrame(PRBool *aOK);
/**
* @return the filter frame, or null if there is no filter frame
* @param aOK if a filter was specified but the designated element
* does not exist or is an element of the wrong type, *aOK is set
* to false. Otherwise *aOK is untouched.
*/
nsSVGFilterFrame *GetFilterFrame(PRBool *aOK) {
if (!mFilter)
return nsnull;
nsSVGFilterFrame *filter = mFilter->GetFilterFrame();
if (!filter) {
*aOK = PR_FALSE;
}
return filter;
}
};
/**
* @param aFrame should be the first continuation
*/
static EffectProperties GetEffectProperties(nsIFrame *aFrame);
/**
* Called by nsCSSFrameConstructor when style changes require the
* effect properties on aFrame to be updated
*/
static void UpdateEffects(nsIFrame *aFrame);
/**
* @param aFrame should be the first continuation
*/
static nsSVGFilterProperty *GetFilterProperty(nsIFrame *aFrame);
static nsSVGFilterFrame *GetFilterFrame(nsIFrame *aFrame) {
nsSVGFilterProperty *prop = GetFilterProperty(aFrame);
PRBool ok;
return prop ? prop->GetFilterFrame(&ok) : nsnull;
return prop ? prop->GetFilterFrame() : nsnull;
}
/**
* @param aFrame must be a first-continuation.
*/
static void AddRenderingObserver(nsIFrame *aFrame, nsSVGRenderingObserver *aObserver);
/**
* @param aFrame must be a first-continuation.
*/
static void RemoveRenderingObserver(nsIFrame *aFrame, nsSVGRenderingObserver *aObserver);
/**
* This can be called on any first-continuation frame. We walk up to
* the nearest observable frame and invalidate its observers.
*/
static void InvalidateRenderingObservers(nsIFrame *aFrame);
/**
* This can be called on any frame. Direct observers
* of this frame are all invalidated.
*/
static void InvalidateDirectRenderingObservers(nsIFrame *aFrame);
/**
* Get an nsSVGPaintingProperty for the frame, creating a fresh one if necessary
*/
static nsSVGPaintingProperty *
GetPaintingProperty(nsIURI *aURI, nsIFrame *aFrame, nsIAtom *aProp);
};
#endif /*NSSVGEFFECTS_H_*/

View File

@ -78,7 +78,8 @@ nsSVGForeignObjectFrame::nsSVGForeignObjectFrame(nsStyleContext* aContext)
mPropagateTransform(PR_TRUE),
mInReflow(PR_FALSE)
{
AddStateBits(NS_FRAME_REFLOW_ROOT | NS_FRAME_MAY_BE_TRANSFORMED);
AddStateBits(NS_FRAME_REFLOW_ROOT |
NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS);
}
//----------------------------------------------------------------------

View File

@ -40,26 +40,11 @@
#include "nsSVGPaintServerFrame.h"
#include "nsContentUtils.h"
#include "gfxContext.h"
//----------------------------------------------------------------------
// nsISupports methods
NS_INTERFACE_MAP_BEGIN(nsSVGGeometryFrame)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY(nsISVGValueObserver)
NS_INTERFACE_MAP_END_INHERITING(nsSVGGeometryFrameBase)
#include "nsSVGEffects.h"
//----------------------------------------------------------------------
// nsIFrame methods
void
nsSVGGeometryFrame::Destroy()
{
// Remove the properties before the frame goes away, since we need it for QI
RemovePaintServerProperties();
nsSVGGeometryFrameBase::Destroy();
}
NS_IMETHODIMP
nsSVGGeometryFrame::Init(nsIContent* aContent,
nsIFrame* aParent,
@ -71,99 +56,21 @@ nsSVGGeometryFrame::Init(nsIContent* aContent,
return rv;
}
NS_IMETHODIMP
nsSVGGeometryFrame::DidSetStyleContext()
{
// One of the styles that might have been changed are the urls that
// point to gradients, etc. Drop our cached values to those
RemovePaintServerProperties();
return NS_OK;
}
//----------------------------------------------------------------------
// nsISVGValueObserver methods:
NS_IMETHODIMP
nsSVGGeometryFrame::WillModifySVGObservable(nsISVGValue* observable,
nsISVGValue::modificationType aModType)
{
return NS_OK;
}
NS_IMETHODIMP
nsSVGGeometryFrame::DidModifySVGObservable(nsISVGValue* observable,
nsISVGValue::modificationType aModType)
{
if (!(GetStateBits() & NS_STATE_SVG_PSERVER_MASK))
return NS_OK;
nsIFrame *frame;
CallQueryInterface(observable, &frame);
if (!frame)
return NS_OK;
PRBool refresh = PR_FALSE;
if (GetStateBits() & NS_STATE_SVG_FILL_PSERVER) {
nsIFrame *ps = static_cast<nsIFrame*>(GetProperty(nsGkAtoms::fill));
if (frame == ps) {
if (aModType == nsISVGValue::mod_die) {
DeleteProperty(nsGkAtoms::fill);
RemoveStateBits(NS_STATE_SVG_FILL_PSERVER);
}
refresh = PR_TRUE;
}
}
if (GetStateBits() & NS_STATE_SVG_STROKE_PSERVER) {
nsIFrame *ps = static_cast<nsIFrame*>(GetProperty(nsGkAtoms::stroke));
if (frame == ps) {
if (aModType == nsISVGValue::mod_die) {
DeleteProperty(nsGkAtoms::stroke);
RemoveStateBits(NS_STATE_SVG_STROKE_PSERVER);
}
refresh = PR_TRUE;
}
}
if (refresh) {
nsISVGChildFrame* svgFrame = nsnull;
CallQueryInterface(this, &svgFrame);
if (svgFrame) {
nsSVGUtils::UpdateGraphic(svgFrame);
}
}
return NS_OK;
}
//----------------------------------------------------------------------
void
nsSVGGeometryFrame::RemovePaintServerProperties()
{
DeleteProperty(nsGkAtoms::fill);
DeleteProperty(nsGkAtoms::stroke);
RemoveStateBits(NS_STATE_SVG_PSERVER_MASK);
}
nsSVGPaintServerFrame *
nsSVGGeometryFrame::GetPaintServer(const nsStyleSVGPaint *aPaint)
nsSVGGeometryFrame::GetPaintServer(const nsStyleSVGPaint *aPaint,
nsIAtom *aType)
{
if (aPaint->mType != eStyleSVGPaintType_Server)
return nsnull;
nsIURI *uri;
uri = aPaint->mPaint.mPaintServer;
if (!uri)
nsSVGPaintingProperty *property =
nsSVGEffects::GetPaintingProperty(aPaint->mPaint.mPaintServer, this, aType);
if (!property)
return nsnull;
nsIFrame *result;
if (NS_FAILED(nsSVGUtils::GetReferencedFrame(&result, uri, mContent,
PresContext()->PresShell())))
nsIFrame *result = property->GetReferencedFrame();
if (!result)
return nsnull;
nsIAtom *type = result->GetType();
@ -172,16 +79,7 @@ nsSVGGeometryFrame::GetPaintServer(const nsStyleSVGPaint *aPaint)
type != nsGkAtoms::svgPatternFrame)
return nsnull;
// Loop check for pattern
if (type == nsGkAtoms::svgPatternFrame &&
nsContentUtils::ContentIsDescendantOf(mContent, result->GetContent()))
return nsnull;
nsSVGPaintServerFrame *server =
static_cast<nsSVGPaintServerFrame*>(result);
server->AddObserver(this);
return server;
return static_cast<nsSVGPaintServerFrame*>(result);
}
float
@ -255,60 +153,6 @@ nsSVGGeometryFrame::GetClipRule()
return GetStyleSVG()->mClipRule;
}
static void
PServerPropertyDtor(void *aObject, nsIAtom *aPropertyName,
void *aPropertyValue, void *aData)
{
nsIFrame *ps = static_cast<nsIFrame*>(aPropertyValue);
nsSVGUtils::RemoveObserver(static_cast<nsIFrame*>(aObject), ps);
}
PRBool
nsSVGGeometryFrame::HasStroke()
{
if (!(GetStateBits() & NS_STATE_SVG_STROKE_PSERVER)) {
nsIFrame *ps = GetPaintServer(&GetStyleSVG()->mStroke);
if (ps) {
SetProperty(nsGkAtoms::stroke, ps, PServerPropertyDtor);
AddStateBits(NS_STATE_SVG_STROKE_PSERVER);
}
}
// cairo will stop rendering if stroke-width is less than or equal to zero
if (GetStrokeWidth() <= 0)
return PR_FALSE;
// Check for eStyleSVGPaintType_Server as the NS_STATE_SVG_STROKE_PSERVER
// state bit is only set if we have a valid URL. If we don't, we still have
// to stroke although we will be using the fallback colour
if (GetStyleSVG()->mStroke.mType == eStyleSVGPaintType_Color ||
GetStyleSVG()->mStroke.mType == eStyleSVGPaintType_Server)
return PR_TRUE;
return PR_FALSE;
}
PRBool
nsSVGGeometryFrame::HasFill()
{
if (!(GetStateBits() & NS_STATE_SVG_FILL_PSERVER)) {
nsIFrame *ps = GetPaintServer(&GetStyleSVG()->mFill);
if (ps) {
SetProperty(nsGkAtoms::fill, ps, PServerPropertyDtor);
AddStateBits(NS_STATE_SVG_FILL_PSERVER);
}
}
// Check for eStyleSVGPaintType_Server as the NS_STATE_SVG_FILL_PSERVER
// state bit is only set if we have a valid URL. If we don't, we still have
// to fill although we will be using the fallback colour
if (GetStyleSVG()->mFill.mType == eStyleSVGPaintType_Color ||
GetStyleSVG()->mFill.mType == eStyleSVGPaintType_Server)
return PR_TRUE;
return PR_FALSE;
}
PRBool
nsSVGGeometryFrame::IsClipChild()
{
@ -357,17 +201,14 @@ nsSVGGeometryFrame::SetupCairoFill(gfxContext *aContext)
float opacity = MaybeOptimizeOpacity(GetStyleSVG()->mFillOpacity);
if (GetStateBits() & NS_STATE_SVG_FILL_PSERVER) {
nsSVGPaintServerFrame *ps = static_cast<nsSVGPaintServerFrame*>
(GetProperty(nsGkAtoms::fill));
if (ps->SetupPaintServer(aContext, this, opacity))
return PR_TRUE;
// On failure, use the fallback colour in case we have an
// objectBoundingBox where the width or height of the object is zero.
// See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
}
nsSVGPaintServerFrame *ps =
GetPaintServer(&GetStyleSVG()->mFill, nsGkAtoms::fill);
if (ps && ps->SetupPaintServer(aContext, this, opacity))
return PR_TRUE;
// On failure, use the fallback colour in case we have an
// objectBoundingBox where the width or height of the object is zero.
// See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
if (GetStyleSVG()->mFill.mType == eStyleSVGPaintType_Server) {
SetupCairoColor(aContext,
GetStyleSVG()->mFill.mFallbackColor,
@ -380,12 +221,19 @@ nsSVGGeometryFrame::SetupCairoFill(gfxContext *aContext)
return PR_TRUE;
}
void
PRBool
nsSVGGeometryFrame::SetupCairoStrokeGeometry(gfxContext *aContext)
{
aContext->SetLineWidth(GetStrokeWidth());
const nsStyleSVG* style = GetStyleSVG();
if (style->mStroke.mType == eStyleSVGPaintType_None)
return PR_FALSE;
float width = GetStrokeWidth();
if (width <= 0)
return PR_FALSE;
aContext->SetLineWidth(width);
switch (GetStyleSVG()->mStrokeLinecap) {
switch (style->mStrokeLinecap) {
case NS_STYLE_STROKE_LINECAP_BUTT:
aContext->SetLineCap(gfxContext::LINE_CAP_BUTT);
break;
@ -397,9 +245,9 @@ nsSVGGeometryFrame::SetupCairoStrokeGeometry(gfxContext *aContext)
break;
}
aContext->SetMiterLimit(GetStyleSVG()->mStrokeMiterlimit);
aContext->SetMiterLimit(style->mStrokeMiterlimit);
switch (GetStyleSVG()->mStrokeLinejoin) {
switch (style->mStrokeLinejoin) {
case NS_STYLE_STROKE_LINEJOIN_MITER:
aContext->SetLineJoin(gfxContext::LINE_JOIN_MITER);
break;
@ -410,12 +258,15 @@ nsSVGGeometryFrame::SetupCairoStrokeGeometry(gfxContext *aContext)
aContext->SetLineJoin(gfxContext::LINE_JOIN_BEVEL);
break;
}
return PR_TRUE;
}
void
PRBool
nsSVGGeometryFrame::SetupCairoStrokeHitGeometry(gfxContext *aContext)
{
SetupCairoStrokeGeometry(aContext);
if (!SetupCairoStrokeGeometry(aContext))
return PR_FALSE;
gfxFloat *dashArray;
PRUint32 count;
@ -424,26 +275,25 @@ nsSVGGeometryFrame::SetupCairoStrokeHitGeometry(gfxContext *aContext)
aContext->SetDash(dashArray, count, GetStrokeDashoffset());
delete [] dashArray;
}
return PR_TRUE;
}
PRBool
nsSVGGeometryFrame::SetupCairoStroke(gfxContext *aContext)
{
SetupCairoStrokeHitGeometry(aContext);
if (!SetupCairoStrokeHitGeometry(aContext))
return PR_FALSE;
float opacity = MaybeOptimizeOpacity(GetStyleSVG()->mStrokeOpacity);
if (GetStateBits() & NS_STATE_SVG_STROKE_PSERVER) {
nsSVGPaintServerFrame *ps = static_cast<nsSVGPaintServerFrame*>
(GetProperty(nsGkAtoms::stroke));
if (ps->SetupPaintServer(aContext, this, opacity))
return PR_TRUE;
// On failure, use the fallback colour in case we have an
// objectBoundingBox where the width or height of the object is zero.
// See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
}
nsSVGPaintServerFrame *ps =
GetPaintServer(&GetStyleSVG()->mStroke, nsGkAtoms::stroke);
if (ps && ps->SetupPaintServer(aContext, this, opacity))
return PR_TRUE;
// On failure, use the fallback colour in case we have an
// objectBoundingBox where the width or height of the object is zero.
// See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
if (GetStyleSVG()->mStroke.mType == eStyleSVGPaintType_Server) {
SetupCairoColor(aContext,
GetStyleSVG()->mStroke.mFallbackColor,

View File

@ -38,8 +38,6 @@
#define __NS_SVGGEOMETRYFRAME_H__
#include "nsFrame.h"
#include "nsWeakReference.h"
#include "nsISVGValueObserver.h"
class nsSVGPaintServerFrame;
class gfxContext;
@ -52,34 +50,22 @@ typedef nsFrame nsSVGGeometryFrameBase;
* cairo context information and stores the fill/stroke paint
* servers. */
class nsSVGGeometryFrame : public nsSVGGeometryFrameBase,
public nsISVGValueObserver
class nsSVGGeometryFrame : public nsSVGGeometryFrameBase
{
protected:
nsSVGGeometryFrame(nsStyleContext *aContext) : nsSVGGeometryFrameBase(aContext) {}
public:
// nsIFrame interface:
virtual void Destroy();
NS_IMETHOD Init(nsIContent* aContent,
nsIFrame* aParent,
nsIFrame* aPrevInFlow);
NS_IMETHOD DidSetStyleContext();
virtual PRBool IsFrameOfType(PRUint32 aFlags) const
{
return nsSVGGeometryFrameBase::IsFrameOfType(aFlags & ~(nsIFrame::eSVG));
}
// nsISupports interface:
NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
// nsISVGValueObserver interface:
NS_IMETHOD WillModifySVGObservable(nsISVGValue* observable,
nsISVGValue::modificationType aModType);
NS_IMETHOD DidModifySVGObservable(nsISVGValue* observable,
nsISVGValue::modificationType aModType);
// nsSVGGeometryFrame methods:
NS_IMETHOD GetCanvasTM(nsIDOMSVGMatrix * *aCanvasTM) = 0;
PRUint16 GetClipRule();
@ -87,25 +73,21 @@ public:
float GetStrokeWidth();
// Check if this geometry needs to be filled or stroked. These also
// have the side effect of looking up the paint server if that is
// the indicated type and storing it in a property, so need to be
// called before SetupCairo{Fill,Stroke}.
PRBool HasFill();
PRBool HasStroke();
/*
* Set up a cairo context for filling a path
* @return PR_FALSE to skip rendering
*/
PRBool SetupCairoFill(gfxContext *aContext);
// Set up a cairo context for measuring a stroked path
void SetupCairoStrokeGeometry(gfxContext *aContext);
// Set up a cairo context for hit testing a stroked path
void SetupCairoStrokeHitGeometry(gfxContext *aContext);
/*
* Set up a cairo context for measuring a stroked path
* @return PR_FALSE if there is no stroke
*/
PRBool SetupCairoStrokeGeometry(gfxContext *aContext);
/*
* Set up a cairo context for hit testing a stroked path
* @return PR_FALSE if there is no stroke
*/
PRBool SetupCairoStrokeHitGeometry(gfxContext *aContext);
/*
* Set up a cairo context for stroking a path
* @return PR_FALSE to skip rendering
@ -113,12 +95,12 @@ public:
PRBool SetupCairoStroke(gfxContext *aContext);
protected:
nsSVGPaintServerFrame *GetPaintServer(const nsStyleSVGPaint *aPaint);
nsSVGPaintServerFrame *GetPaintServer(const nsStyleSVGPaint *aPaint,
nsIAtom *aType);
private:
nsresult GetStrokeDashArray(double **arr, PRUint32 *count);
float GetStrokeDashoffset();
void RemovePaintServerProperties();
// Returns opacity that should be used in rendering this primitive.
// In the general case the return value is just the passed opacity.

View File

@ -338,7 +338,7 @@ nsSVGGlyphFrame::PaintSVG(nsSVGRenderState *aContext, nsIntRect *aDirtyRect)
gfx->Save();
SetupGlobalTransform(gfx);
if (HasFill() && SetupCairoFill(gfx)) {
if (SetupCairoFill(gfx)) {
gfxMatrix matrix = gfx->CurrentMatrix();
CharacterIterator iter(this, PR_TRUE);
iter.SetInitialMatrix(gfx);
@ -347,7 +347,7 @@ nsSVGGlyphFrame::PaintSVG(nsSVGRenderState *aContext, nsIntRect *aDirtyRect)
gfx->SetMatrix(matrix);
}
if (HasStroke() && SetupCairoStroke(gfx)) {
if (SetupCairoStroke(gfx)) {
// SetupCairoStroke will clear mTextRun whenever
// there is a pattern or gradient on the text
CharacterIterator iter(this, PR_TRUE);
@ -433,11 +433,10 @@ nsSVGGlyphFrame::UpdateCoveredRegion()
gfxRect extent;
if (HasStroke()) {
if (SetupCairoStrokeGeometry(tmpCtx)) {
AddCharactersToPath(&iter, tmpCtx);
SetupCairoStrokeGeometry(tmpCtx);
extent = tmpCtx->UserToDevice(tmpCtx->GetUserStrokeExtent());
} else if (HasFill()) {
} else if (GetStyleSVG()->mFill.mType != eStyleSVGPaintType_None) {
AddBoundingBoxesToPath(&iter, tmpCtx);
tmpCtx->IdentityMatrix();
extent = tmpCtx->GetUserPathExtent();

View File

@ -41,6 +41,7 @@
#include "nsIDOMSVGAnimTransformList.h"
#include "nsSVGTransformList.h"
#include "nsSVGMatrix.h"
#include "nsSVGEffects.h"
#include "nsIDOMSVGStopElement.h"
#include "nsSVGGradientElement.h"
#include "nsSVGGeometryFrame.h"
@ -55,9 +56,8 @@
nsSVGGradientFrame::nsSVGGradientFrame(nsStyleContext* aContext,
nsIDOMSVGURIReference *aRef) :
nsSVGGradientFrameBase(aContext),
mNextGrad(nsnull),
mLoopFlag(PR_FALSE),
mInitialized(PR_FALSE)
mNoHRefURI(PR_FALSE)
{
if (aRef) {
// Get the href
@ -65,97 +65,14 @@ nsSVGGradientFrame::nsSVGGradientFrame(nsStyleContext* aContext,
}
}
nsSVGGradientFrame::~nsSVGGradientFrame()
{
WillModify(mod_die);
// Notify the world that we're dying
DidModify(mod_die);
if (mNextGrad)
mNextGrad->RemoveObserver(this);
}
//----------------------------------------------------------------------
// nsISupports methods:
NS_INTERFACE_MAP_BEGIN(nsSVGGradientFrame)
NS_INTERFACE_MAP_ENTRY(nsISVGValueObserver)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_END_INHERITING(nsSVGGradientFrameBase)
//----------------------------------------------------------------------
// nsISVGValueObserver methods:
NS_IMETHODIMP
nsSVGGradientFrame::WillModifySVGObservable(nsISVGValue* observable,
modificationType aModType)
{
// return if we have an mObservers loop
if (mLoopFlag) {
// XXXjwatt: we should really send an error to the JavaScript Console here:
NS_WARNING("gradient reference loop detected while notifying observers!");
return NS_OK;
}
// Don't pass on mod_die - our gradient observers would stop observing us!
if (aModType == mod_die)
aModType = mod_other;
WillModify(aModType);
return NS_OK;
}
NS_IMETHODIMP
nsSVGGradientFrame::DidModifySVGObservable(nsISVGValue* observable,
nsISVGValue::modificationType aModType)
{
// return if we have an mObservers loop
if (mLoopFlag) {
// XXXjwatt: we should really send an error to the JavaScript Console here:
NS_WARNING("gradient reference loop detected while notifying observers!");
return NS_OK;
}
// If we reference another gradient and it's going away, null out mNextGrad
if (mNextGrad && aModType == nsISVGValue::mod_die) {
nsIFrame *gradient = nsnull;
CallQueryInterface(observable, &gradient);
if (mNextGrad == gradient)
mNextGrad = nsnull;
}
// Don't pass on mod_die - our gradient observers would stop observing us!
if (aModType == mod_die)
aModType = mod_other;
DidModify(aModType);
return NS_OK;
}
//----------------------------------------------------------------------
// nsIFrame methods:
NS_IMETHODIMP
nsSVGGradientFrame::DidSetStyleContext()
{
WillModify();
DidModify();
return NS_OK;
}
NS_IMETHODIMP
nsSVGGradientFrame::RemoveFrame(nsIAtom* aListName,
nsIFrame* aOldFrame)
{
WillModify();
DidModify();
PRBool result = mFrames.DestroyFrame(aOldFrame);
return result ? NS_OK : NS_ERROR_FAILURE;
}
nsIAtom*
nsSVGGradientFrame::GetType() const
{
return nsGkAtoms::svgGradientFrame;
nsSVGEffects::InvalidateRenderingObservers(this);
return nsSVGGradientFrameBase::DidSetStyleContext();
}
NS_IMETHODIMP
@ -167,19 +84,14 @@ nsSVGGradientFrame::AttributeChanged(PRInt32 aNameSpaceID,
(aAttribute == nsGkAtoms::gradientUnits ||
aAttribute == nsGkAtoms::gradientTransform ||
aAttribute == nsGkAtoms::spreadMethod)) {
WillModify();
DidModify();
return NS_OK;
}
if (aNameSpaceID == kNameSpaceID_XLink &&
aAttribute == nsGkAtoms::href) {
if (mNextGrad)
mNextGrad->RemoveObserver(this);
WillModify();
GetRefedGradientFromHref();
DidModify();
return NS_OK;
nsSVGEffects::InvalidateRenderingObservers(this);
} else if (aNameSpaceID == kNameSpaceID_XLink &&
aAttribute == nsGkAtoms::href) {
// Blow away our reference, if any
DeleteProperty(nsGkAtoms::href);
mNoHRefURI = PR_FALSE;
// And update whoever references us
nsSVGEffects::InvalidateRenderingObservers(this);
}
return nsSVGGradientFrameBase::AttributeChanged(aNameSpaceID,
@ -289,14 +201,11 @@ nsSVGGradientFrame::GetGradientTransform(nsSVGGeometryFrame *aSource)
}
}
nsIContent *gradient = GetGradientWithAttr(nsGkAtoms::gradientTransform);
if (!gradient)
gradient = mContent; // use our gradient to get the correct default value
nsSVGGradientElement *element =
GetGradientWithAttr(nsGkAtoms::gradientTransform, mContent);
nsSVGGradientElement *gradElement = static_cast<nsSVGGradientElement*>
(gradient);
nsCOMPtr<nsIDOMSVGTransformList> trans;
gradElement->mGradientTransform->GetAnimVal(getter_AddRefs(trans));
element->mGradientTransform->GetAnimVal(getter_AddRefs(trans));
nsCOMPtr<nsIDOMSVGMatrix> gradientTransform =
nsSVGTransformList::GetConsolidationMatrix(trans);
@ -309,14 +218,10 @@ nsSVGGradientFrame::GetGradientTransform(nsSVGGeometryFrame *aSource)
PRUint16
nsSVGGradientFrame::GetSpreadMethod()
{
nsIContent *gradient = GetGradientWithAttr(nsGkAtoms::spreadMethod);
if (!gradient)
gradient = mContent; // use our gradient to get the correct default value
nsSVGGradientElement *element =
GetGradientWithAttr(nsGkAtoms::spreadMethod, mContent);
nsSVGGradientElement *gradElement = static_cast<nsSVGGradientElement*>
(gradient);
return gradElement->mEnumAttributes[nsSVGGradientElement::SPREADMETHOD].GetAnimValue();
return element->mEnumAttributes[nsSVGGradientElement::SPREADMETHOD].GetAnimValue();
}
//----------------------------------------------------------------------
@ -387,101 +292,97 @@ nsSVGGradientFrame::SetupPaintServer(gfxContext *aContext,
// Private (helper) methods
void
nsSVGGradientFrame::GetRefedGradientFromHref()
nsSVGGradientFrame *
nsSVGGradientFrame::GetReferencedGradient()
{
mNextGrad = nsnull;
mInitialized = PR_TRUE;
if (mNoHRefURI)
return nsnull;
// Fetch our gradient element's xlink:href attribute
nsAutoString href;
mHref->GetAnimVal(href);
if (href.IsEmpty()) {
return; // no URL
}
nsSVGPaintingProperty *property =
static_cast<nsSVGPaintingProperty*>(GetProperty(nsGkAtoms::href));
// Convert href to an nsIURI
nsCOMPtr<nsIURI> targetURI;
nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
mContent->GetCurrentDoc(), base);
// Fetch and store a pointer to the referenced gradient element's frame.
// Note that we are using *our* frame tree for this call, otherwise we're
// going to have to get the PresShell in each call
nsIFrame *nextGrad;
if (NS_SUCCEEDED(nsSVGUtils::GetReferencedFrame(&nextGrad, targetURI, mContent,
PresContext()->PresShell()))) {
nsIAtom* frameType = nextGrad->GetType();
if (frameType != nsGkAtoms::svgLinearGradientFrame &&
frameType != nsGkAtoms::svgRadialGradientFrame)
return;
mNextGrad = reinterpret_cast<nsSVGGradientFrame*>(nextGrad);
// Add ourselves to the observer list
if (mNextGrad) {
// Can't use the NS_ADD macro here because of nsISupports ambiguity
mNextGrad->AddObserver(this);
if (!property) {
// Fetch our gradient element's xlink:href attribute
nsAutoString href;
mHref->GetAnimVal(href);
if (href.IsEmpty()) {
mNoHRefURI = PR_TRUE;
return nsnull; // no URL
}
// Convert href to an nsIURI
nsCOMPtr<nsIURI> targetURI;
nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
mContent->GetCurrentDoc(), base);
property = nsSVGEffects::GetPaintingProperty(targetURI, this, nsGkAtoms::href);
if (!property)
return nsnull;
}
nsIFrame *result = property->GetReferencedFrame();
if (!result)
return nsnull;
nsIAtom* frameType = result->GetType();
if (frameType != nsGkAtoms::svgLinearGradientFrame &&
frameType != nsGkAtoms::svgRadialGradientFrame)
return nsnull;
return static_cast<nsSVGGradientFrame*>(result);
}
// This is implemented to return nsnull if the attribute is not set so that
// GetFx and GetFy can use the values of cx and cy instead of the defaults.
nsIContent*
nsSVGGradientFrame::GetGradientWithAttr(nsIAtom *aAttrName)
nsSVGGradientElement *
nsSVGGradientFrame::GetGradientWithAttr(nsIAtom *aAttrName, nsIContent *aDefault)
{
if (mContent->HasAttr(kNameSpaceID_None, aAttrName))
return mContent;
return static_cast<nsSVGGradientElement *>(mContent);
if (!mInitialized) // make sure mNextGrad has been initialized
GetRefedGradientFromHref();
nsSVGGradientElement *grad = static_cast<nsSVGGradientElement *>(aDefault);
if (!mNextGrad)
return nsnull;
nsIContent *grad = nsnull;
nsSVGGradientFrame *next = GetReferencedGradient();
if (!next)
return grad;
// Set mLoopFlag before checking mNextGrad->mLoopFlag in case we are mNextGrad
mLoopFlag = PR_TRUE;
// XXXjwatt: we should really send an error to the JavaScript Console here:
NS_WARN_IF_FALSE(!mNextGrad->mLoopFlag, "gradient reference loop detected "
"while inheriting attribute!");
if (!mNextGrad->mLoopFlag)
grad = mNextGrad->GetGradientWithAttr(aAttrName);
NS_WARN_IF_FALSE(!next->mLoopFlag, "gradient reference loop detected "
"while inheriting attribute!");
if (!next->mLoopFlag)
grad = next->GetGradientWithAttr(aAttrName, aDefault);
mLoopFlag = PR_FALSE;
return grad;
}
nsIContent*
nsSVGGradientFrame::GetGradientWithAttr(nsIAtom *aAttrName, nsIAtom *aGradType)
nsSVGGradientElement *
nsSVGGradientFrame::GetGradientWithAttr(nsIAtom *aAttrName, nsIAtom *aGradType,
nsIContent *aDefault)
{
if (GetType() == aGradType && mContent->HasAttr(kNameSpaceID_None, aAttrName))
return mContent;
return static_cast<nsSVGGradientElement *>(mContent);
if (!mInitialized)
GetRefedGradientFromHref(); // make sure mNextGrad has been initialized
nsSVGGradientElement *grad = static_cast<nsSVGGradientElement *>(aDefault);
if (!mNextGrad)
return nsnull;
nsIContent *grad = nsnull;
nsSVGGradientFrame *next = GetReferencedGradient();
if (!next)
return grad;
// Set mLoopFlag before checking mNextGrad->mLoopFlag in case we are mNextGrad
mLoopFlag = PR_TRUE;
// XXXjwatt: we should really send an error to the JavaScript Console here:
NS_WARN_IF_FALSE(!mNextGrad->mLoopFlag, "gradient reference loop detected "
"while inheriting attribute!");
if (!mNextGrad->mLoopFlag)
grad = mNextGrad->GetGradientWithAttr(aAttrName, aGradType);
NS_WARN_IF_FALSE(!next->mLoopFlag, "gradient reference loop detected "
"while inheriting attribute!");
if (!next->mLoopFlag)
grad = next->GetGradientWithAttr(aAttrName, aGradType, aDefault);
mLoopFlag = PR_FALSE;
return grad;
}
PRInt32
PRInt32
nsSVGGradientFrame::GetStopFrame(PRInt32 aIndex, nsIFrame * *aStopFrame)
{
PRInt32 stopCount = 0;
@ -502,10 +403,8 @@ nsSVGGradientFrame::GetStopFrame(PRInt32 aIndex, nsIFrame * *aStopFrame)
// Our gradient element doesn't have stops - try to "inherit" them
if (!mInitialized)
GetRefedGradientFromHref(); // make sure mNextGrad has been initialized
if (!mNextGrad) {
nsSVGGradientFrame *next = GetReferencedGradient();
if (!next) {
if (aStopFrame)
*aStopFrame = nsnull;
return 0;
@ -514,10 +413,10 @@ nsSVGGradientFrame::GetStopFrame(PRInt32 aIndex, nsIFrame * *aStopFrame)
// Set mLoopFlag before checking mNextGrad->mLoopFlag in case we are mNextGrad
mLoopFlag = PR_TRUE;
// XXXjwatt: we should really send an error to the JavaScript Console here:
NS_WARN_IF_FALSE(!mNextGrad->mLoopFlag, "gradient reference loop detected "
"while inheriting stop!");
if (!mNextGrad->mLoopFlag)
stopCount = mNextGrad->GetStopFrame(aIndex, aStopFrame);
NS_WARN_IF_FALSE(!next->mLoopFlag, "gradient reference loop detected "
"while inheriting stop!");
if (!next->mLoopFlag)
stopCount = next->GetStopFrame(aIndex, aStopFrame);
mLoopFlag = PR_FALSE;
return stopCount;
@ -528,14 +427,9 @@ nsSVGGradientFrame::GetGradientUnits()
{
// This getter is called every time the others are called - maybe cache it?
nsIContent *gradient = GetGradientWithAttr(nsGkAtoms::gradientUnits);
if (!gradient)
gradient = mContent; // use our gradient to get the correct default value
nsSVGGradientElement *gradElement = static_cast<nsSVGGradientElement*>
(gradient);
return gradElement->mEnumAttributes[nsSVGGradientElement::GRADIENTUNITS].GetAnimValue();
nsSVGGradientElement *element =
GetGradientWithAttr(nsGkAtoms::gradientUnits, mContent);
return element->mEnumAttributes[nsSVGGradientElement::GRADIENTUNITS].GetAnimValue();
}
// -------------------------------------------------------------------------
@ -558,9 +452,7 @@ nsSVGLinearGradientFrame::AttributeChanged(PRInt32 aNameSpaceID,
aAttribute == nsGkAtoms::y1 ||
aAttribute == nsGkAtoms::x2 ||
aAttribute == nsGkAtoms::y2)) {
WillModify();
DidModify();
return NS_OK;
nsSVGEffects::InvalidateRenderingObservers(this);
}
return nsSVGGradientFrame::AttributeChanged(aNameSpaceID,
@ -573,12 +465,8 @@ float
nsSVGLinearGradientFrame::GradientLookupAttribute(nsIAtom *aAtomName,
PRUint16 aEnumName)
{
nsIContent *gradient = GetLinearGradientWithAttr(aAtomName);
if (!gradient)
gradient = mContent; // use our gradient to get the correct default value
nsSVGLinearGradientElement *element =
static_cast<nsSVGLinearGradientElement*>(gradient);
GetLinearGradientWithAttr(aAtomName, mContent);
// Object bounding box units are handled by setting the appropriate
// transform in GetGradientTransform, but we need to handle user
@ -633,9 +521,7 @@ nsSVGRadialGradientFrame::AttributeChanged(PRInt32 aNameSpaceID,
aAttribute == nsGkAtoms::cy ||
aAttribute == nsGkAtoms::fx ||
aAttribute == nsGkAtoms::fy)) {
WillModify();
DidModify();
return NS_OK;
nsSVGEffects::InvalidateRenderingObservers(this);
}
return nsSVGGradientFrame::AttributeChanged(aNameSpaceID,
@ -647,21 +533,16 @@ nsSVGRadialGradientFrame::AttributeChanged(PRInt32 aNameSpaceID,
float
nsSVGRadialGradientFrame::GradientLookupAttribute(nsIAtom *aAtomName,
PRUint16 aEnumName,
nsIContent *aElement)
nsSVGRadialGradientElement *aElement)
{
nsIContent *gradient;
nsSVGRadialGradientElement *element;
if (aElement) {
gradient = aElement;
element = aElement;
} else {
gradient = GetRadialGradientWithAttr(aAtomName);
if (!gradient)
gradient = mContent; // use our gradient to get the correct default value
element = GetRadialGradientWithAttr(aAtomName, mContent);
}
nsSVGRadialGradientElement *element =
static_cast<nsSVGRadialGradientElement*>(gradient);
// Object bounding box units are handled by setting the appropriate
// transform in GetGradientTransform, but we need to handle user
// space units as part of the individual Get* routines. Fixes 323669.
@ -688,14 +569,14 @@ nsSVGRadialGradientFrame::CreateGradient()
cy = GradientLookupAttribute(nsGkAtoms::cy, nsSVGRadialGradientElement::CY);
r = GradientLookupAttribute(nsGkAtoms::r, nsSVGRadialGradientElement::R);
nsIContent *gradient;
nsSVGRadialGradientElement *gradient;
if (!(gradient = GetRadialGradientWithAttr(nsGkAtoms::fx)))
if (!(gradient = GetRadialGradientWithAttr(nsGkAtoms::fx, nsnull)))
fx = cx; // if fx isn't set, we must use cx
else
fx = GradientLookupAttribute(nsGkAtoms::fx, nsSVGRadialGradientElement::FX, gradient);
if (!(gradient = GetRadialGradientWithAttr(nsGkAtoms::fy)))
if (!(gradient = GetRadialGradientWithAttr(nsGkAtoms::fy, nsnull)))
fy = cy; // if fy isn't set, we must use cy
else
fy = GradientLookupAttribute(nsGkAtoms::fy, nsSVGRadialGradientElement::FY, gradient);
@ -728,7 +609,7 @@ nsSVGRadialGradientFrame::CreateGradient()
// Public functions
// -------------------------------------------------------------------------
nsIFrame*
nsIFrame*
NS_NewSVGLinearGradientFrame(nsIPresShell* aPresShell,
nsIContent* aContent,
nsStyleContext* aContext)
@ -738,7 +619,7 @@ NS_NewSVGLinearGradientFrame(nsIPresShell* aPresShell,
NS_ERROR("Can't create frame! Content is not an SVG linearGradient");
return nsnull;
}
nsCOMPtr<nsIDOMSVGURIReference> aRef = do_QueryInterface(aContent);
NS_ASSERTION(aRef, "NS_NewSVGLinearGradientFrame -- Content doesn't support nsIDOMSVGURIReference");
@ -755,7 +636,7 @@ NS_NewSVGRadialGradientFrame(nsIPresShell* aPresShell,
NS_ERROR("Can't create frame! Content is not an SVG radialGradient");
return nsnull;
}
nsCOMPtr<nsIDOMSVGURIReference> aRef = do_QueryInterface(aContent);
NS_ASSERTION(aRef, "NS_NewSVGRadialGradientFrame -- Content doesn't support nsIDOMSVGURIReference");

View File

@ -48,42 +48,26 @@
class nsIDOMSVGStopElement;
typedef nsSVGPaintServerFrame nsSVGGradientFrameBase;
typedef nsSVGPaintServerFrame nsSVGGradientFrameBase;
class nsSVGGradientFrame : public nsSVGGradientFrameBase,
public nsISVGValueObserver
/**
* Gradients can refer to other gradients. We create an nsSVGPaintingProperty
* with property type nsGkAtoms::href to track the referenced gradient.
*/
class nsSVGGradientFrame : public nsSVGGradientFrameBase
{
protected:
nsSVGGradientFrame(nsStyleContext* aContext,
nsIDOMSVGURIReference *aRef);
virtual ~nsSVGGradientFrame();
public:
// nsSVGPaintServerFrame methods:
virtual PRBool SetupPaintServer(gfxContext *aContext,
nsSVGGeometryFrame *aSource,
float aGraphicOpacity);
// nsISupports interface:
NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
private:
NS_IMETHOD_(nsrefcnt) AddRef() { return 1; }
NS_IMETHOD_(nsrefcnt) Release() { return 1; }
public:
// nsISVGValueObserver interface:
NS_IMETHOD WillModifySVGObservable(nsISVGValue* observable,
nsISVGValue::modificationType aModType);
NS_IMETHOD DidModifySVGObservable(nsISVGValue* observable,
nsISVGValue::modificationType aModType);
// nsIFrame interface:
NS_IMETHOD DidSetStyleContext();
NS_IMETHOD RemoveFrame(nsIAtom* aListName,
nsIFrame* aOldFrame);
virtual nsIAtom* GetType() const; // frame type: nsGkAtoms::svgGradientFrame
NS_IMETHOD AttributeChanged(PRInt32 aNameSpaceID,
nsIAtom* aAttribute,
@ -102,26 +86,24 @@ public:
{
return NS_OK; // override - our frames don't directly render
}
private:
// Helper methods to aid gradient implementation
// ---------------------------------------------
// The SVG specification allows gradient elements to reference another
// gradient element to "inherit" its attributes or gradient stops. Reference
// chains of arbitrary length are allowed, and loop checking is essential!
// Use the following helpers to safely get attributes and stops.
// Parse our xlink:href and set mNextGrad if we reference another gradient.
void GetRefedGradientFromHref();
// Parse our xlink:href and set up our nsSVGPaintingProperty if we
// reference another gradient and we don't have a property. Return
// the referenced gradient's frame if available, null otherwise.
nsSVGGradientFrame* GetReferencedGradient();
// Helpers to look at our gradient and then along its reference chain (if any)
// to find the first gradient with the specified attribute.
nsIContent* GetGradientWithAttr(nsIAtom *aAttrName);
// Returns aDefault if no content with that attribute is found
nsSVGGradientElement* GetGradientWithAttr(nsIAtom *aAttrName, nsIContent *aDefault);
// Some attributes are only valid on one type of gradient, and we *must* get
// the right type or we won't have the data structures we require.
nsIContent* GetGradientWithAttr(nsIAtom *aAttrName, nsIAtom *aGradType);
// Returns aDefault if no content with that attribute is found
nsSVGGradientElement* GetGradientWithAttr(nsIAtom *aAttrName, nsIAtom *aGradType,
nsIContent *aDefault);
// Optionally get a stop frame (returns stop index/count)
PRInt32 GetStopFrame(PRInt32 aIndex, nsIFrame * *aStopFrame);
@ -136,28 +118,15 @@ protected:
virtual already_AddRefed<gfxPattern> CreateGradient() = 0;
// Use these inline methods instead of GetGradientWithAttr(..., aGradType)
nsIContent* GetLinearGradientWithAttr(nsIAtom *aAttrName)
nsSVGLinearGradientElement* GetLinearGradientWithAttr(nsIAtom *aAttrName, nsIContent *aDefault)
{
return GetGradientWithAttr(aAttrName, nsGkAtoms::svgLinearGradientFrame);
return static_cast<nsSVGLinearGradientElement*>(
GetGradientWithAttr(aAttrName, nsGkAtoms::svgLinearGradientFrame, aDefault));
}
nsIContent* GetRadialGradientWithAttr(nsIAtom *aAttrName)
nsSVGRadialGradientElement* GetRadialGradientWithAttr(nsIAtom *aAttrName, nsIContent *aDefault)
{
return GetGradientWithAttr(aAttrName, nsGkAtoms::svgRadialGradientFrame);
}
// We must loop check notifications too: see bug 330387 comment 18 + testcase
// and comment 19. The mLoopFlag check is in Will/DidModifySVGObservable.
void WillModify(modificationType aModType = mod_other)
{
mLoopFlag = PR_TRUE;
nsSVGValue::WillModify(aModType);
mLoopFlag = PR_FALSE;
}
void DidModify(modificationType aModType = mod_other)
{
mLoopFlag = PR_TRUE;
nsSVGValue::DidModify(aModType);
mLoopFlag = PR_FALSE;
return static_cast<nsSVGRadialGradientElement*>(
GetGradientWithAttr(aAttrName, nsGkAtoms::svgRadialGradientFrame, aDefault));
}
// Get the value of our gradientUnits attribute
@ -168,22 +137,16 @@ protected:
private:
// href of the other gradient we reference (if any)
// XXX this should go away, we can watch our content directly
nsCOMPtr<nsIDOMSVGAnimatedString> mHref;
// Frame of the gradient we reference (if any). Do NOT use this directly.
// Use Get[Xxx]GradientWithAttr instead to ensure proper loop checking.
nsSVGGradientFrame *mNextGrad;
// Flag to mark this frame as "in use" during recursive calls along our
// gradient's reference chain so we can detect reference loops. See:
// http://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementHrefAttribute
PRPackedBool mLoopFlag;
// Ideally we'd set mNextGrad by implementing Init(), but the frame of the
// gradient we reference isn't available at that stage. Our only option is to
// set mNextGrad lazily in GetGradientWithAttr, and to make that efficient
// we need this flag. Our class size is the same since it just fills padding.
PRPackedBool mInitialized;
// Gradients often don't reference other gradients, so here we cache
// the fact that that isn't happening.
PRPackedBool mNoHRefURI;
};
@ -195,7 +158,7 @@ typedef nsSVGGradientFrame nsSVGLinearGradientFrameBase;
class nsSVGLinearGradientFrame : public nsSVGLinearGradientFrameBase
{
friend nsIFrame* NS_NewSVGLinearGradientFrame(nsIPresShell* aPresShell,
friend nsIFrame* NS_NewSVGLinearGradientFrame(nsIPresShell* aPresShell,
nsIContent* aContent,
nsStyleContext* aContext);
protected:
@ -232,7 +195,7 @@ typedef nsSVGGradientFrame nsSVGRadialGradientFrameBase;
class nsSVGRadialGradientFrame : public nsSVGRadialGradientFrameBase
{
friend nsIFrame* NS_NewSVGRadialGradientFrame(nsIPresShell* aPresShell,
friend nsIFrame* NS_NewSVGRadialGradientFrame(nsIPresShell* aPresShell,
nsIContent* aContent,
nsStyleContext* aContext);
protected:
@ -258,7 +221,7 @@ public:
protected:
float GradientLookupAttribute(nsIAtom *aAtomName, PRUint16 aEnumName,
nsIContent *aElement = nsnull);
nsSVGRadialGradientElement *aElement = nsnull);
virtual already_AddRefed<gfxPattern> CreateGradient();
};

View File

@ -44,6 +44,9 @@
#include "nsDisplayList.h"
#include "nsSVGMatrix.h"
#include "nsSVGFilterPaintCallback.h"
#include "nsSVGFilterFrame.h"
#include "nsSVGClipPathFrame.h"
#include "nsSVGMaskFrame.h"
// ----------------------------------------------------------------------
@ -106,13 +109,12 @@ nsRect
nsSVGIntegrationUtils::ComputeFrameEffectsRect(nsIFrame* aFrame,
const nsRect& aOverflowRect)
{
PRBool isOK;
nsIFrame* firstFrame =
nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame);
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(firstFrame);
nsSVGFilterFrame *filterFrame = effectProperties.mFilter ?
effectProperties.mFilter->GetFilterFrame(&isOK) : nsnull;
effectProperties.mFilter->GetFilterFrame() : nsnull;
if (!filterFrame)
return aOverflowRect;
@ -270,12 +272,9 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(nsIRenderingContext* aCtx,
*/
PRBool isOK = PR_TRUE;
nsSVGClipPathFrame *clipPathFrame = effectProperties.mClipPath ?
effectProperties.mClipPath->GetClipPathFrame(&isOK) : nsnull;
nsSVGFilterFrame *filterFrame = effectProperties.mFilter ?
effectProperties.mFilter->GetFilterFrame(&isOK) : nsnull;
nsSVGMaskFrame *maskFrame = effectProperties.mMask ?
effectProperties.mMask->GetMaskFrame(&isOK) : nsnull;
nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
nsSVGFilterFrame *filterFrame = effectProperties.GetFilterFrame(&isOK);
nsSVGMaskFrame *maskFrame = effectProperties.GetMaskFrame(&isOK);
PRBool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : PR_TRUE;

View File

@ -35,6 +35,7 @@
* ***** END LICENSE BLOCK ***** */
#include "nsFrame.h"
#include "nsSVGEffects.h"
class nsSVGLeafFrame : public nsFrame
{
@ -56,6 +57,7 @@ public:
}
#endif
NS_IMETHOD DidSetStyleContext();
};
nsIFrame*
@ -63,3 +65,11 @@ NS_NewSVGLeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsSVGLeafFrame(aContext);
}
NS_IMETHODIMP
nsSVGLeafFrame::DidSetStyleContext()
{
nsresult rv = nsFrame::DidSetStyleContext();
nsSVGEffects::InvalidateRenderingObservers(this);
return rv;
}

View File

@ -35,7 +35,3 @@
* ***** END LICENSE BLOCK ***** */
#include "nsSVGPaintServerFrame.h"
NS_INTERFACE_MAP_BEGIN(nsSVGPaintServerFrame)
NS_INTERFACE_MAP_ENTRY(nsISVGValue)
NS_INTERFACE_MAP_END_INHERITING(nsSVGPaintServerFrameBase)

View File

@ -38,15 +38,13 @@
#define __NS_SVGPAINTSERVERFRAME_H__
#include "nsSVGContainerFrame.h"
#include "nsSVGValue.h"
class gfxContext;
class nsSVGGeometryFrame;
typedef nsSVGContainerFrame nsSVGPaintServerFrameBase;
class nsSVGPaintServerFrame : public nsSVGPaintServerFrameBase,
public nsSVGValue
class nsSVGPaintServerFrame : public nsSVGPaintServerFrameBase
{
protected:
nsSVGPaintServerFrame(nsStyleContext* aContext) :
@ -60,12 +58,6 @@ public:
virtual PRBool SetupPaintServer(gfxContext *aContext,
nsSVGGeometryFrame *aSource,
float aOpacity) = 0;
// nsISupports interface:
NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
// nsISVGValue interface:
NS_IMETHOD SetValueString(const nsAString &aValue) { return NS_OK; }
NS_IMETHOD GetValueString(nsAString& aValue) { return NS_ERROR_NOT_IMPLEMENTED; }
};
#endif // __NS_SVGPAINTSERVERFRAME_H__

View File

@ -363,8 +363,8 @@ nsSVGPathGeometryFrame::GetFrameForPoint(const nsPoint &aPoint)
if (mask & HITTEST_MASK_FILL)
isHit = context.PointInFill(userSpacePoint);
if (!isHit && (mask & HITTEST_MASK_STROKE)) {
SetupCairoStrokeHitGeometry(&context);
if (!isHit && (mask & HITTEST_MASK_STROKE) &&
SetupCairoStrokeHitGeometry(&context)) {
isHit = context.PointInStroke(userSpacePoint);
}
@ -431,8 +431,7 @@ nsSVGPathGeometryFrame::UpdateCoveredRegion()
gfxRect extent;
if (HasStroke()) {
SetupCairoStrokeGeometry(&context);
if (SetupCairoStrokeGeometry(&context)) {
extent = context.GetUserStrokeExtent();
if (!IsDegeneratePath(extent)) {
extent = context.UserToDevice(extent);
@ -653,11 +652,11 @@ nsSVGPathGeometryFrame::Render(nsSVGRenderState *aContext)
break;
}
if (HasFill() && SetupCairoFill(gfx)) {
if (SetupCairoFill(gfx)) {
gfx->Fill();
}
if (HasStroke() && SetupCairoStroke(gfx)) {
if (SetupCairoStroke(gfx)) {
gfx->Stroke();
}

View File

@ -48,6 +48,7 @@
#include "nsSVGMatrix.h"
#include "nsSVGRect.h"
#include "nsSVGUtils.h"
#include "nsSVGEffects.h"
#include "nsSVGOuterSVGFrame.h"
#include "nsSVGPatternElement.h"
#include "nsSVGGeometryFrame.h"
@ -68,8 +69,8 @@ static void printRect(char *msg, nsIDOMSVGRect *aRect);
nsSVGPatternFrame::nsSVGPatternFrame(nsStyleContext* aContext,
nsIDOMSVGURIReference *aRef) :
nsSVGPatternFrameBase(aContext),
mNextPattern(nsnull),
mLoopFlag(PR_FALSE)
mLoopFlag(PR_FALSE), mPaintLoopFlag(PR_FALSE),
mNoHRefURI(PR_FALSE)
{
if (aRef) {
// Get the hRef
@ -77,61 +78,14 @@ nsSVGPatternFrame::nsSVGPatternFrame(nsStyleContext* aContext,
}
}
nsSVGPatternFrame::~nsSVGPatternFrame()
{
WillModify(mod_die);
if (mNextPattern)
mNextPattern->RemoveObserver(this);
// Notify the world that we're dying
DidModify(mod_die);
}
//----------------------------------------------------------------------
// nsISupports methods:
NS_INTERFACE_MAP_BEGIN(nsSVGPatternFrame)
NS_INTERFACE_MAP_ENTRY(nsISVGValueObserver)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_END_INHERITING(nsSVGPatternFrameBase)
//----------------------------------------------------------------------
// nsISVGValueObserver methods:
NS_IMETHODIMP
nsSVGPatternFrame::WillModifySVGObservable(nsISVGValue* observable,
modificationType aModType)
{
WillModify(aModType);
return NS_OK;
}
NS_IMETHODIMP
nsSVGPatternFrame::DidModifySVGObservable(nsISVGValue* observable,
nsISVGValue::modificationType aModType)
{
nsIFrame *pattern = nsnull;
CallQueryInterface(observable, &pattern);
// Is this a pattern we are observing that is going away?
if (mNextPattern && aModType == nsISVGValue::mod_die && pattern) {
// Yes, we need to handle this differently
if (mNextPattern == pattern) {
mNextPattern = nsnull;
}
}
// Something we depend on was modified -- pass it on!
DidModify(aModType);
return NS_OK;
}
//----------------------------------------------------------------------
// nsIFrame methods:
NS_IMETHODIMP
nsSVGPatternFrame::DidSetStyleContext()
{
WillModify();
DidModify();
return NS_OK;
nsSVGEffects::InvalidateRenderingObservers(this);
return nsSVGPatternFrameBase::DidSetStyleContext();
}
NS_IMETHODIMP
@ -149,19 +103,16 @@ nsSVGPatternFrame::AttributeChanged(PRInt32 aNameSpaceID,
aAttribute == nsGkAtoms::height ||
aAttribute == nsGkAtoms::preserveAspectRatio ||
aAttribute == nsGkAtoms::viewBox)) {
WillModify();
DidModify();
return NS_OK;
}
nsSVGEffects::InvalidateRenderingObservers(this);
}
if (aNameSpaceID == kNameSpaceID_XLink &&
aAttribute == nsGkAtoms::href) {
if (mNextPattern)
mNextPattern->RemoveObserver(this);
mNextPattern = nsnull;
WillModify();
DidModify();
return NS_OK;
// Blow away our reference, if any
DeleteProperty(nsGkAtoms::href);
mNoHRefURI = PR_FALSE;
// And update whoever references us
nsSVGEffects::InvalidateRenderingObservers(this);
}
return nsSVGPatternFrameBase::AttributeChanged(aNameSpaceID,
@ -181,11 +132,11 @@ nsSVGPatternFrame::GetType() const
// need to return *our current* transformation
// matrix, which depends on our units parameters
// and X, Y, Width, and Height
already_AddRefed<nsIDOMSVGMatrix>
already_AddRefed<nsIDOMSVGMatrix>
nsSVGPatternFrame::GetCanvasTM()
{
nsIDOMSVGMatrix *rCTM;
if (mCTM) {
rCTM = mCTM;
NS_IF_ADDREF(rCTM);
@ -200,7 +151,7 @@ nsSVGPatternFrame::GetCanvasTM()
NS_NewSVGMatrix(&rCTM);
}
}
return rCTM;
return rCTM;
}
nsresult
@ -212,7 +163,7 @@ nsSVGPatternFrame::PaintPattern(gfxASurface** surface,
/*
* General approach:
* Set the content geometry stuff
* Calculate our bbox (using x,y,width,height & patternUnits &
* Calculate our bbox (using x,y,width,height & patternUnits &
* patternTransform)
* Create the surface
* Calculate the content transformation matrix
@ -341,10 +292,17 @@ nsSVGPatternFrame::PaintPattern(gfxASurface** surface,
// Set our geometrical parent
mSource = aSource;
for (nsIFrame* kid = firstKid; kid;
kid = kid->GetNextSibling()) {
nsSVGUtils::PaintChildWithEffects(&tmpState, nsnull, kid);
// Delay checking mPaintLoopFlag until here so we can give back a clear
// surface if there's a loop
if (!mPaintLoopFlag) {
mPaintLoopFlag = PR_TRUE;
for (nsIFrame* kid = firstKid; kid;
kid = kid->GetNextSibling()) {
nsSVGUtils::PaintChildWithEffects(&tmpState, nsnull, kid);
}
mPaintLoopFlag = PR_FALSE;
}
mSource = nsnull;
if (aGraphicOpacity != 1.0f) {
@ -365,16 +323,21 @@ nsSVGPatternFrame::PaintPattern(gfxASurface** surface,
NS_IMETHODIMP
nsSVGPatternFrame::GetPatternFirstChild(nsIFrame **kid)
{
nsresult rv = NS_OK;
// Do we have any children ourselves?
if (!(*kid = mFrames.FirstChild())) {
// No, see if we chain to someone who does
if (checkURITarget())
rv = mNextPattern->GetPatternFirstChild(kid);
else
rv = NS_ERROR_FAILURE; // No children = error
*kid = mFrames.FirstChild();
if (*kid)
return NS_OK;
// No, see if we chain to someone who does
nsSVGPatternFrame *next = GetReferencedPattern();
mLoopFlag = PR_TRUE;
if (!next || next->mLoopFlag) {
mLoopFlag = PR_FALSE;
return NS_ERROR_FAILURE;
}
nsresult rv = next->GetPatternFirstChild(kid);
mLoopFlag = PR_FALSE;
return rv;
}
@ -382,246 +345,157 @@ nsSVGPatternFrame::GetPatternFirstChild(nsIFrame **kid)
PRUint16
nsSVGPatternFrame::GetPatternUnits()
{
PRUint16 rv;
// See if we need to get the value from another pattern
if (!checkURITarget(nsGkAtoms::patternUnits)) {
// No, return the values
nsSVGPatternElement *patternElement = static_cast<nsSVGPatternElement*>
(mContent);
rv = patternElement->mEnumAttributes[nsSVGPatternElement::PATTERNUNITS].GetAnimValue();
} else {
// Yes, get it from the target
rv = mNextPattern->GetPatternUnits();
}
mLoopFlag = PR_FALSE;
return rv;
nsSVGPatternElement *patternElement =
GetPatternWithAttr(nsGkAtoms::patternUnits, mContent);
return patternElement->mEnumAttributes[nsSVGPatternElement::PATTERNUNITS].GetAnimValue();
}
PRUint16
nsSVGPatternFrame::GetPatternContentUnits()
{
PRUint16 rv;
// See if we need to get the value from another pattern
if (!checkURITarget(nsGkAtoms::patternContentUnits)) {
// No, return the values
nsSVGPatternElement *patternElement = static_cast<nsSVGPatternElement*>
(mContent);
rv = patternElement->mEnumAttributes[nsSVGPatternElement::PATTERNCONTENTUNITS].GetAnimValue();
} else {
// Yes, get it from the target
rv = mNextPattern->GetPatternContentUnits();
}
mLoopFlag = PR_FALSE;
return rv;
nsSVGPatternElement *patternElement =
GetPatternWithAttr(nsGkAtoms::patternContentUnits, mContent);
return patternElement->mEnumAttributes[nsSVGPatternElement::PATTERNCONTENTUNITS].GetAnimValue();
}
gfxMatrix
nsSVGPatternFrame::GetPatternTransform()
{
gfxMatrix matrix;
// See if we need to get the value from another pattern
if (!checkURITarget(nsGkAtoms::patternTransform)) {
// No, return the values
nsSVGPatternElement *patternElement = static_cast<nsSVGPatternElement*>
(mContent);
nsCOMPtr<nsIDOMSVGTransformList> lTrans;
patternElement->mPatternTransform->GetAnimVal(getter_AddRefs(lTrans));
nsCOMPtr<nsIDOMSVGMatrix> patternTransform =
nsSVGTransformList::GetConsolidationMatrix(lTrans);
if (patternTransform) {
matrix = nsSVGUtils::ConvertSVGMatrixToThebes(patternTransform);
}
} else {
// Yes, get it from the target
matrix = mNextPattern->GetPatternTransform();
}
mLoopFlag = PR_FALSE;
nsSVGPatternElement *patternElement =
GetPatternWithAttr(nsGkAtoms::patternTransform, mContent);
gfxMatrix matrix;
nsCOMPtr<nsIDOMSVGTransformList> lTrans;
patternElement->mPatternTransform->GetAnimVal(getter_AddRefs(lTrans));
nsCOMPtr<nsIDOMSVGMatrix> patternTransform =
nsSVGTransformList::GetConsolidationMatrix(lTrans);
if (patternTransform) {
matrix = nsSVGUtils::ConvertSVGMatrixToThebes(patternTransform);
}
return matrix;
}
NS_IMETHODIMP
nsSVGPatternFrame::GetViewBox(nsIDOMSVGRect **aViewBox)
{
// See if we need to get the value from another pattern
if (!checkURITarget(nsGkAtoms::viewBox)) {
// No, return the values
nsCOMPtr<nsIDOMSVGFitToViewBox> patternElement =
do_QueryInterface(mContent);
nsCOMPtr<nsIDOMSVGAnimatedRect> viewBox;
patternElement->GetViewBox(getter_AddRefs(viewBox));
viewBox->GetAnimVal(aViewBox);
} else {
// Yes, get it from the target
mNextPattern->GetViewBox(aViewBox);
}
mLoopFlag = PR_FALSE;
return NS_OK;
nsSVGPatternElement *patternElement =
GetPatternWithAttr(nsGkAtoms::viewBox, mContent);
nsCOMPtr<nsIDOMSVGAnimatedRect> viewBox;
patternElement->GetViewBox(getter_AddRefs(viewBox));
return viewBox->GetAnimVal(aViewBox);
}
NS_IMETHODIMP
nsSVGPatternFrame::GetPreserveAspectRatio(nsIDOMSVGAnimatedPreserveAspectRatio
nsSVGPatternFrame::GetPreserveAspectRatio(nsIDOMSVGAnimatedPreserveAspectRatio
**aPreserveAspectRatio)
{
// See if we need to get the value from another pattern
if (!checkURITarget(nsGkAtoms::preserveAspectRatio)) {
// No, return the values
nsCOMPtr<nsIDOMSVGFitToViewBox> patternElement =
do_QueryInterface(mContent);
patternElement->GetPreserveAspectRatio(aPreserveAspectRatio);
} else {
// Yes, get it from the target
mNextPattern->GetPreserveAspectRatio(aPreserveAspectRatio);
}
mLoopFlag = PR_FALSE;
return NS_OK;
nsSVGPatternElement *patternElement =
GetPatternWithAttr(nsGkAtoms::preserveAspectRatio, mContent);
return patternElement->GetPreserveAspectRatio(aPreserveAspectRatio);
}
nsSVGLength2 *
nsSVGPatternFrame::GetX()
{
nsSVGLength2 *rv = nsnull;
// See if we need to get the value from another pattern
if (checkURITarget(nsGkAtoms::x)) {
// Yes, get it from the target
rv = mNextPattern->GetX();
} else {
// No, return the values
nsSVGPatternElement *pattern =
static_cast<nsSVGPatternElement*>(mContent);
rv = &pattern->mLengthAttributes[nsSVGPatternElement::X];
}
mLoopFlag = PR_FALSE;
return rv;
nsSVGPatternElement *pattern = GetPatternWithAttr(nsGkAtoms::x, mContent);
return &pattern->mLengthAttributes[nsSVGPatternElement::X];
}
nsSVGLength2 *
nsSVGPatternFrame::GetY()
{
nsSVGLength2 *rv = nsnull;
// See if we need to get the value from another pattern
if (checkURITarget(nsGkAtoms::y)) {
// Yes, get it from the target
rv = mNextPattern->GetY();
} else {
// No, return the values
nsSVGPatternElement *pattern =
static_cast<nsSVGPatternElement*>(mContent);
rv = &pattern->mLengthAttributes[nsSVGPatternElement::Y];
}
mLoopFlag = PR_FALSE;
return rv;
nsSVGPatternElement *pattern = GetPatternWithAttr(nsGkAtoms::y, mContent);
return &pattern->mLengthAttributes[nsSVGPatternElement::Y];
}
nsSVGLength2 *
nsSVGPatternFrame::GetWidth()
{
nsSVGLength2 *rv = nsnull;
// See if we need to get the value from another pattern
if (checkURITarget(nsGkAtoms::width)) {
// Yes, get it from the target
rv = mNextPattern->GetWidth();
} else {
// No, return the values
nsSVGPatternElement *pattern =
static_cast<nsSVGPatternElement*>(mContent);
rv = &pattern->mLengthAttributes[nsSVGPatternElement::WIDTH];
}
mLoopFlag = PR_FALSE;
return rv;
nsSVGPatternElement *pattern = GetPatternWithAttr(nsGkAtoms::width, mContent);
return &pattern->mLengthAttributes[nsSVGPatternElement::WIDTH];
}
nsSVGLength2 *
nsSVGPatternFrame::GetHeight()
{
nsSVGLength2 *rv = nsnull;
// See if we need to get the value from another pattern
if (checkURITarget(nsGkAtoms::height)) {
// Yes, get it from the target
rv = mNextPattern->GetHeight();
} else {
// No, return the values
nsSVGPatternElement *pattern =
static_cast<nsSVGPatternElement*>(mContent);
rv = &pattern->mLengthAttributes[nsSVGPatternElement::HEIGHT];
}
mLoopFlag = PR_FALSE;
return rv;
nsSVGPatternElement *pattern = GetPatternWithAttr(nsGkAtoms::height, mContent);
return &pattern->mLengthAttributes[nsSVGPatternElement::HEIGHT];
}
// Private (helper) methods
PRBool
nsSVGPatternFrame::checkURITarget(nsIAtom *attr) {
// Was the attribute explicitly set?
if (mContent->HasAttr(kNameSpaceID_None, attr)) {
// Yes, just return
return PR_FALSE;
nsSVGPatternFrame *
nsSVGPatternFrame::GetReferencedPattern()
{
if (mNoHRefURI)
return nsnull;
nsSVGPaintingProperty *property =
static_cast<nsSVGPaintingProperty*>(GetProperty(nsGkAtoms::href));
if (!property) {
// Fetch our pattern element's xlink:href attribute
nsAutoString href;
mHref->GetAnimVal(href);
if (href.IsEmpty()) {
mNoHRefURI = PR_TRUE;
return nsnull; // no URL
}
// Convert href to an nsIURI
nsCOMPtr<nsIURI> targetURI;
nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
mContent->GetCurrentDoc(), base);
property = nsSVGEffects::GetPaintingProperty(targetURI, this, nsGkAtoms::href);
if (!property)
return nsnull;
}
return checkURITarget();
nsIFrame *result = property->GetReferencedFrame();
if (!result)
return nsnull;
nsIAtom* frameType = result->GetType();
if (frameType != nsGkAtoms::svgPatternFrame)
return nsnull;
return static_cast<nsSVGPatternFrame*>(result);
}
PRBool
nsSVGPatternFrame::checkURITarget(void) {
nsIFrame *nextPattern;
mLoopFlag = PR_TRUE; // Set our loop detection flag
// Have we already figured out the next Pattern?
if (mNextPattern != nsnull) {
return PR_TRUE;
}
nsSVGPatternElement *
nsSVGPatternFrame::GetPatternWithAttr(nsIAtom *aAttrName, nsIContent *aDefault)
{
if (mContent->HasAttr(kNameSpaceID_None, aAttrName))
return static_cast<nsSVGPatternElement *>(mContent);
// check if we reference another pattern to "inherit" its children
// or attributes
nsAutoString href;
mHref->GetAnimVal(href);
// Do we have URI?
if (href.IsEmpty()) {
return PR_FALSE; // No, return the default
}
nsSVGPatternElement *pattern = static_cast<nsSVGPatternElement *>(aDefault);
nsCOMPtr<nsIURI> targetURI;
nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI),
href, mContent->GetCurrentDoc(), base);
nsSVGPatternFrame *next = GetReferencedPattern();
if (!next)
return pattern;
// Note that we are using *our* frame tree for this call,
// otherwise we're going to have to get the PresShell in each call
if (NS_SUCCEEDED(
nsSVGUtils::GetReferencedFrame(&nextPattern, targetURI,
mContent,
PresContext()->PresShell()))) {
nsIAtom* frameType = nextPattern->GetType();
if (frameType != nsGkAtoms::svgPatternFrame)
return PR_FALSE;
mNextPattern = (nsSVGPatternFrame *)nextPattern;
// Are we looping?
if (mNextPattern->mLoopFlag) {
// Yes, remove the reference and return an error
NS_WARNING("Pattern loop detected!");
mNextPattern = nsnull;
return PR_FALSE;
}
// Add ourselves to the observer list
if (mNextPattern) {
// Can't use the NS_ADD macro here because of nsISupports ambiguity
mNextPattern->AddObserver(this);
}
return PR_TRUE;
}
return PR_FALSE;
// Set mLoopFlag before checking mNextGrad->mLoopFlag in case we are mNextGrad
mLoopFlag = PR_TRUE;
// XXXjwatt: we should really send an error to the JavaScript Console here:
NS_WARN_IF_FALSE(!next->mLoopFlag, "gradient reference loop detected "
"while inheriting attribute!");
if (!next->mLoopFlag)
pattern = next->GetPatternWithAttr(aAttrName, aDefault);
mLoopFlag = PR_FALSE;
return pattern;
}
// -------------------------------------------------------------------------
// Helper functions
// -------------------------------------------------------------------------
nsresult
nsSVGPatternFrame::GetPatternRect(nsIDOMSVGRect **patternRect,
nsresult
nsSVGPatternFrame::GetPatternRect(nsIDOMSVGRect **patternRect,
nsIDOMSVGRect *bbox,
nsIDOMSVGMatrix *callerCTM,
nsSVGElement *content)
@ -677,7 +551,7 @@ nsSVGPatternFrame::ConstructCTM(nsIDOMSVGMatrix **aCTM,
float width, height;
callerBBox->GetWidth(&width);
callerBBox->GetHeight(&height);
NS_NewSVGMatrix(getter_AddRefs(tCTM), width, 0.0f, 0.0f,
NS_NewSVGMatrix(getter_AddRefs(tCTM), width, 0.0f, 0.0f,
height, 0.0f, 0.0f);
} else {
float scale = nsSVGUtils::MaxExpansion(callerCTM);
@ -748,7 +622,7 @@ nsSVGPatternFrame::GetPatternMatrix(nsIDOMSVGRect *bbox,
}
nsresult
nsSVGPatternFrame::GetCallerGeometry(nsIDOMSVGMatrix **aCTM,
nsSVGPatternFrame::GetCallerGeometry(nsIDOMSVGMatrix **aCTM,
nsIDOMSVGRect **aBBox,
nsSVGElement **aContent,
nsSVGGeometryFrame *aSource)
@ -781,7 +655,7 @@ nsSVGPatternFrame::GetCallerGeometry(nsIDOMSVGMatrix **aCTM,
CallQueryInterface(aSource, &callerSVGFrame);
callerSVGFrame->SetMatrixPropagation(PR_FALSE);
callerSVGFrame->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
callerSVGFrame->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
nsISVGChildFrame::TRANSFORM_CHANGED );
callerSVGFrame->GetBBox(aBBox);
callerSVGFrame->SetMatrixPropagation(PR_TRUE);
@ -888,7 +762,7 @@ nsIFrame* NS_NewSVGPatternFrame(nsIPresShell* aPresShell,
}
nsCOMPtr<nsIDOMSVGURIReference> ref = do_QueryInterface(aContent);
NS_ASSERTION(ref,
NS_ASSERTION(ref,
"NS_NewSVGPatternFrame -- Content doesn't support nsIDOMSVGURIReference");
#ifdef DEBUG_scooter
@ -907,11 +781,11 @@ static void printCTM(char *msg, gfxMatrix aCTM)
static void printCTM(char *msg, nsIDOMSVGMatrix *aCTM)
{
float a,b,c,d,e,f;
aCTM->GetA(&a);
aCTM->GetB(&b);
aCTM->GetA(&a);
aCTM->GetB(&b);
aCTM->GetC(&c);
aCTM->GetD(&d);
aCTM->GetE(&e);
aCTM->GetD(&d);
aCTM->GetE(&e);
aCTM->GetF(&f);
printf("%s {%f,%f,%f,%f,%f,%f}\n",msg,a,b,c,d,e,f);
}
@ -919,10 +793,10 @@ static void printCTM(char *msg, nsIDOMSVGMatrix *aCTM)
static void printRect(char *msg, nsIDOMSVGRect *aRect)
{
float x,y,width,height;
aRect->GetX(&x);
aRect->GetY(&y);
aRect->GetWidth(&width);
aRect->GetHeight(&height);
aRect->GetX(&x);
aRect->GetY(&y);
aRect->GetWidth(&width);
aRect->GetHeight(&height);
printf("%s {%f,%f,%f,%f}\n",msg,x,y,width,height);
}
#endif

View File

@ -39,8 +39,6 @@
#ifndef __NS_SVGPATTERNFRAME_H__
#define __NS_SVGPATTERNFRAME_H__
#include "nsISVGValueObserver.h"
#include "nsWeakReference.h"
#include "nsIDOMSVGAnimatedString.h"
#include "nsIDOMSVGMatrix.h"
#include "nsSVGPaintServerFrame.h"
@ -55,11 +53,14 @@ class gfxASurface;
typedef nsSVGPaintServerFrame nsSVGPatternFrameBase;
class nsSVGPatternFrame : public nsSVGPatternFrameBase,
public nsISVGValueObserver
/**
* Patterns can refer to other patterns. We create an nsSVGPaintingProperty
* with property type nsGkAtoms::href to track the referenced pattern.
*/
class nsSVGPatternFrame : public nsSVGPatternFrameBase
{
public:
friend nsIFrame* NS_NewSVGPatternFrame(nsIPresShell* aPresShell,
friend nsIFrame* NS_NewSVGPatternFrame(nsIPresShell* aPresShell,
nsIContent* aContent,
nsStyleContext* aContext);
@ -75,19 +76,7 @@ public:
nsSVGGeometryFrame *aSource,
float aGraphicOpacity);
// nsISupports interface:
NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
private:
NS_IMETHOD_(nsrefcnt) AddRef() { return 1; }
NS_IMETHOD_(nsrefcnt) Release() { return 1; }
public:
// nsISVGValueObserver interface:
NS_IMETHOD WillModifySVGObservable(nsISVGValue* observable,
nsISVGValue::modificationType aModType);
NS_IMETHOD DidModifySVGObservable(nsISVGValue* observable,
nsISVGValue::modificationType aModType);
// nsSVGContainerFrame methods:
virtual already_AddRefed<nsIDOMSVGMatrix> GetCanvasTM();
@ -117,11 +106,13 @@ protected:
nsSVGPatternFrame(nsStyleContext* aContext,
nsIDOMSVGURIReference *aRef);
virtual ~nsSVGPatternFrame();
// Internal methods for handling referenced patterns
PRBool checkURITarget(nsIAtom *);
PRBool checkURITarget();
nsSVGPatternFrame* GetReferencedPattern();
// Helper to look at our pattern and then along its reference chain (if any)
// to find the first pattern with the specified attribute. Returns
// null if there isn't one.
nsSVGPatternElement* GetPatternWithAttr(nsIAtom *aAttrName, nsIContent *aDefault);
//
nsSVGLength2 *GetX();
nsSVGLength2 *GetY();
@ -132,7 +123,7 @@ protected:
PRUint16 GetPatternContentUnits();
gfxMatrix GetPatternTransform();
NS_IMETHOD GetPreserveAspectRatio(nsIDOMSVGAnimatedPreserveAspectRatio
NS_IMETHOD GetPreserveAspectRatio(nsIDOMSVGAnimatedPreserveAspectRatio
**aPreserveAspectRatio);
NS_IMETHOD GetPatternFirstChild(nsIFrame **kid);
NS_IMETHOD GetViewBox(nsIDOMSVGRect * *aMatrix);
@ -146,23 +137,26 @@ protected:
nsresult ConstructCTM(nsIDOMSVGMatrix **ctm,
nsIDOMSVGRect *callerBBox,
nsIDOMSVGMatrix *callerCTM);
nsresult GetCallerGeometry(nsIDOMSVGMatrix **aCTM,
nsresult GetCallerGeometry(nsIDOMSVGMatrix **aCTM,
nsIDOMSVGRect **aBBox,
nsSVGElement **aContent,
nsSVGElement **aContent,
nsSVGGeometryFrame *aSource);
private:
// this is a *temporary* reference to the frame of the element currently
// referencing our pattern. This must be temporary because different
// referencing frames will all reference this one frame
nsSVGGeometryFrame *mSource;
nsCOMPtr<nsIDOMSVGMatrix> mCTM;
nsSVGGeometryFrame *mSource;
nsCOMPtr<nsIDOMSVGMatrix> mCTM;
protected:
nsSVGPatternFrame *mNextPattern;
nsCOMPtr<nsIDOMSVGAnimatedString> mHref;
PRPackedBool mLoopFlag;
nsCOMPtr<nsIDOMSVGAnimatedString> mHref;
// This flag is used to detect loops in xlink:href processing
PRPackedBool mLoopFlag;
// This flag is used to detect loops when painting this pattern
// ends up recursively painting itself
PRPackedBool mPaintLoopFlag;
PRPackedBool mNoHRefURI;
};
#endif

View File

@ -40,7 +40,7 @@
#include "nsStyleContext.h"
#include "nsFrame.h"
#include "nsGkAtoms.h"
#include "nsISVGValue.h"
#include "nsSVGEffects.h"
// This is a very simple frame whose only purpose is to capture style change
// events and propagate them to the parent. Most of the heavy lifting is done
@ -94,9 +94,8 @@ public:
NS_IMETHODIMP
nsSVGStopFrame::DidSetStyleContext()
{
// Tell our parent
if (mParent)
mParent->DidSetStyleContext();
nsSVGStopFrameBase::DidSetStyleContext();
nsSVGEffects::InvalidateRenderingObservers(this);
return NS_OK;
}
@ -113,21 +112,8 @@ nsSVGStopFrame::AttributeChanged(PRInt32 aNameSpaceID,
{
if (aNameSpaceID == kNameSpaceID_None &&
aAttribute == nsGkAtoms::offset) {
// Need to tell our parent gradients that something happened.
// Calling {Begin,End}Update on an nsISVGValue, which
// nsSVGGradientFrame implements, causes its observers (the
// referencing graphics frames) to be notified.
if (mParent) {
nsISVGValue *svgParent;
CallQueryInterface(mParent, &svgParent);
if (svgParent) {
svgParent->BeginBatchUpdate();
svgParent->EndBatchUpdate();
}
}
return NS_OK;
}
nsSVGEffects::InvalidateRenderingObservers(this);
}
return nsSVGStopFrameBase::AttributeChanged(aNameSpaceID,
aAttribute, aModType);

View File

@ -594,7 +594,7 @@ nsSVGUtils::FindFilterInvalidation(nsIFrame *aFrame, const nsRect& aRect)
nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
if (property) {
nsSVGFilterFrame *filter = property->GetFilterFrame(nsnull);
nsSVGFilterFrame *filter = property->GetFilterFrame();
if (filter) {
rect = filter->GetInvalidationBBox(aFrame, rect);
}
@ -622,6 +622,8 @@ nsSVGUtils::UpdateGraphic(nsISVGChildFrame *aSVGFrame)
nsIFrame *frame;
CallQueryInterface(aSVGFrame, &frame);
nsSVGEffects::InvalidateRenderingObservers(frame);
if (frame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)
return;
@ -985,8 +987,7 @@ nsSVGUtils::PaintChildWithEffects(nsSVGRenderState *aContext,
nsSVGEffects::GetEffectProperties(aFrame);
PRBool isOK = PR_TRUE;
nsSVGFilterFrame *filterFrame = effectProperties.mFilter ?
effectProperties.mFilter->GetFilterFrame(&isOK) : nsnull;
nsSVGFilterFrame *filterFrame = effectProperties.GetFilterFrame(&isOK);
/* Check if we need to draw anything. HasValidCoveredRect only returns
* true for path geometry and glyphs, so basically we're traversing
@ -1024,10 +1025,8 @@ nsSVGUtils::PaintChildWithEffects(nsSVGRenderState *aContext,
gfxContext *gfx = aContext->GetGfxContext();
PRBool complexEffects = PR_FALSE;
nsSVGClipPathFrame *clipPathFrame = effectProperties.mClipPath ?
effectProperties.mClipPath->GetClipPathFrame(&isOK) : nsnull;
nsSVGMaskFrame *maskFrame = effectProperties.mMask ?
effectProperties.mMask->GetMaskFrame(&isOK) : nsnull;
nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
nsSVGMaskFrame *maskFrame = effectProperties.GetMaskFrame(&isOK);
PRBool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : PR_TRUE;
@ -1105,14 +1104,6 @@ nsSVGUtils::PaintChildWithEffects(nsSVGRenderState *aContext,
gfx->Restore();
}
void
nsSVGUtils::UpdateEffects(nsIFrame *aFrame)
{
aFrame->DeleteProperty(nsGkAtoms::filter);
aFrame->DeleteProperty(nsGkAtoms::mask);
aFrame->DeleteProperty(nsGkAtoms::clipPath);
}
PRBool
nsSVGUtils::HitTestClip(nsIFrame *aFrame, const nsPoint &aPoint)
{
@ -1121,7 +1112,7 @@ nsSVGUtils::HitTestClip(nsIFrame *aFrame, const nsPoint &aPoint)
if (!props.mClipPath)
return PR_TRUE;
nsSVGClipPathFrame *clipPathFrame = props.mClipPath->GetClipPathFrame(nsnull);
nsSVGClipPathFrame *clipPathFrame = props.GetClipPathFrame(nsnull);
if (!clipPathFrame) {
// clipPath is not a valid resource, so nothing gets painted, so
// hit-testing must fail.
@ -1435,8 +1426,9 @@ nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame)
if (type == nsGkAtoms::svgImageFrame)
return PR_TRUE;
if (type == nsGkAtoms::svgPathGeometryFrame) {
nsSVGGeometryFrame *geom = static_cast<nsSVGGeometryFrame*>(aFrame);
if (!(geom->HasFill() && geom->HasStroke()))
const nsStyleSVG *style = aFrame->GetStyleSVG();
if (style->mFill.mType == eStyleSVGPaintType_None &&
style->mStroke.mType == eStyleSVGPaintType_None)
return PR_TRUE;
}
}

View File

@ -90,17 +90,10 @@ class nsISVGChildFrame;
#define NS_STATE_SVG_DIRTY 0x00400000
/* Do we have a paint server for fill with a valid URL? */
#define NS_STATE_SVG_FILL_PSERVER 0x00800000
/* Do we have a paint server for stroke with a valid URL? */
#define NS_STATE_SVG_STROKE_PSERVER 0x01000000
/* Do we have any paint servers with valid URLs? */
#define NS_STATE_SVG_PSERVER_MASK 0x01800000
/* are we the child of a non-display container? */
#define NS_STATE_SVG_NONDISPLAY_CHILD 0x02000000
#define NS_STATE_SVG_NONDISPLAY_CHILD 0x00800000
#define NS_STATE_SVG_PROPAGATE_TRANSFORM 0x04000000
#define NS_STATE_SVG_PROPAGATE_TRANSFORM 0x01000000
/**
* Byte offsets of channels in a native packed gfxColor or cairo image surface.
@ -353,13 +346,6 @@ public:
nsIntRect *aDirtyRect,
nsIFrame *aFrame);
/**
* Called by nsCSSFrameConstructor when style changes require the
* effect properties on aFrame to be updated
*/
static void
UpdateEffects(nsIFrame *aFrame);
/* Hit testing - check if point hits the clipPath of indicated
* frame. Returns true if no clipPath set. */
static PRBool

View File

@ -369,35 +369,30 @@ nsTextBoxFrame::PaintTitle(nsIRenderingContext& aRenderingContext,
if (mTitle.IsEmpty())
return;
nsRect textRect(aPt, GetSize());
textRect.Deflate(GetUsedBorderAndPadding());
nsRect textRect(CalcTextRect(aRenderingContext, aPt));
// determine (cropped) title and underline position
nsPresContext* presContext = PresContext();
LayoutTitle(presContext, aRenderingContext, textRect);
// make the rect as small as our (cropped) text.
nscoord outerWidth = textRect.width;
textRect.width = mTitleWidth;
// Align our text within the overall rect by checking our text-align property.
const nsStyleVisibility* vis = GetStyleVisibility();
// Paint the text shadow before doing any foreground stuff
const nsStyleText* textStyle = GetStyleText();
if (textStyle->mTextAlign == NS_STYLE_TEXT_ALIGN_CENTER)
textRect.x += (outerWidth - textRect.width)/2;
else if (textStyle->mTextAlign == NS_STYLE_TEXT_ALIGN_RIGHT) {
if (vis->mDirection == NS_STYLE_DIRECTION_LTR)
textRect.x += (outerWidth - textRect.width);
}
else {
if (vis->mDirection == NS_STYLE_DIRECTION_RTL)
textRect.x += (outerWidth - textRect.width);
if (textStyle->mTextShadow) {
// Text shadow happens with the last value being painted at the back,
// ie. it is painted first.
for (PRUint32 i = textStyle->mTextShadow->Length(); i > 0; --i) {
PaintOneShadow(aRenderingContext.ThebesContext(),
textRect,
textStyle->mTextShadow->ShadowAt(i - 1),
GetStyleColor()->mColor);
}
}
// don't draw if the title is not dirty
if (PR_FALSE == aDirtyRect.Intersects(textRect))
return;
DrawText(aRenderingContext, textRect, 0);
}
void
nsTextBoxFrame::DrawText(nsIRenderingContext& aRenderingContext,
const nsRect& aTextRect,
const nscolor& aOverrideColor)
{
nsPresContext* presContext = PresContext();
// paint the title
nscolor overColor;
@ -414,7 +409,7 @@ nsTextBoxFrame::PaintTitle(nsIRenderingContext& aRenderingContext,
const nsStyleTextReset* styleText = context->GetStyleTextReset();
if (decorMask & styleText->mTextDecoration) { // a decoration defined here
nscolor color = context->GetStyleColor()->mColor;
nscolor color = aOverrideColor ? aOverrideColor : context->GetStyleColor()->mColor;
if (NS_STYLE_TEXT_DECORATION_UNDERLINE & decorMask & styleText->mTextDecoration) {
underColor = color;
@ -449,11 +444,11 @@ nsTextBoxFrame::PaintTitle(nsIRenderingContext& aRenderingContext,
fontMet->GetMaxAscent(ascent);
nscoord baseline =
presContext->RoundAppUnitsToNearestDevPixels(textRect.y + ascent);
presContext->RoundAppUnitsToNearestDevPixels(aTextRect.y + ascent);
nsRefPtr<gfxContext> ctx = aRenderingContext.ThebesContext();
gfxPoint pt(presContext->AppUnitsToGfxUnits(textRect.x),
presContext->AppUnitsToGfxUnits(textRect.y));
gfxFloat width = presContext->AppUnitsToGfxUnits(textRect.width);
gfxPoint pt(presContext->AppUnitsToGfxUnits(aTextRect.x),
presContext->AppUnitsToGfxUnits(aTextRect.y));
gfxFloat width = presContext->AppUnitsToGfxUnits(aTextRect.width);
gfxFloat ascentPixel = presContext->AppUnitsToGfxUnits(ascent);
if (decorations & (NS_FONT_DECORATION_OVERLINE | NS_FONT_DECORATION_UNDERLINE)) {
fontMet->GetUnderline(offset, size);
@ -489,7 +484,7 @@ nsTextBoxFrame::PaintTitle(nsIRenderingContext& aRenderingContext,
CalculateUnderline(aRenderingContext);
aRenderingContext.SetColor(GetStyleColor()->mColor);
aRenderingContext.SetColor(aOverrideColor ? aOverrideColor : GetStyleColor()->mColor);
#ifdef IBMBIDI
nsresult rv = NS_ERROR_FAILURE;
@ -508,7 +503,7 @@ nsTextBoxFrame::PaintTitle(nsIRenderingContext& aRenderingContext,
posResolve.logicalIndex = mAccessKeyInfo->mAccesskeyIndex;
rv = bidiUtils->RenderText(mCroppedTitle.get(), mCroppedTitle.Length(), direction,
presContext, aRenderingContext,
textRect.x, baseline,
aTextRect.x, baseline,
&posResolve,
1);
mAccessKeyInfo->mBeforeWidth = posResolve.visualLeftTwips;
@ -517,7 +512,7 @@ nsTextBoxFrame::PaintTitle(nsIRenderingContext& aRenderingContext,
{
rv = bidiUtils->RenderText(mCroppedTitle.get(), mCroppedTitle.Length(), direction,
presContext, aRenderingContext,
textRect.x, baseline);
aTextRect.x, baseline);
}
}
}
@ -537,17 +532,63 @@ nsTextBoxFrame::PaintTitle(nsIRenderingContext& aRenderingContext,
mAccessKeyInfo->mBeforeWidth = 0;
}
aRenderingContext.DrawString(mCroppedTitle, textRect.x, baseline);
aRenderingContext.DrawString(mCroppedTitle, aTextRect.x, baseline);
}
if (mAccessKeyInfo && mAccessKeyInfo->mAccesskeyIndex != kNotFound) {
aRenderingContext.FillRect(textRect.x + mAccessKeyInfo->mBeforeWidth,
textRect.y + mAccessKeyInfo->mAccessOffset,
aRenderingContext.FillRect(aTextRect.x + mAccessKeyInfo->mBeforeWidth,
aTextRect.y + mAccessKeyInfo->mAccessOffset,
mAccessKeyInfo->mAccessWidth,
mAccessKeyInfo->mAccessUnderlineSize);
}
}
void nsTextBoxFrame::PaintOneShadow(gfxContext* aCtx,
const nsRect& aTextRect,
nsCSSShadowItem* aShadowDetails,
const nscolor& aForegroundColor) {
nsPoint shadowOffset(aShadowDetails->mXOffset,
aShadowDetails->mYOffset);
nscoord blurRadius = PR_MAX(aShadowDetails->mRadius, 0);
nsRect shadowRect(aTextRect);
shadowRect.MoveBy(shadowOffset);
gfxRect shadowRectGFX(shadowRect.x, shadowRect.y, shadowRect.width, shadowRect.height);
nsContextBoxBlur contextBoxBlur;
gfxContext* shadowContext = contextBoxBlur.Init(shadowRectGFX, blurRadius,
PresContext()->AppUnitsPerDevPixel(),
aCtx);
if (!shadowContext)
return;
nscolor shadowColor;
if (aShadowDetails->mHasColor)
shadowColor = aShadowDetails->mColor;
else
shadowColor = aForegroundColor;
// Conjure an nsIRenderingContext from a gfxContext for DrawText
nsCOMPtr<nsIRenderingContext> renderingContext = nsnull;
nsIDeviceContext* devCtx = PresContext()->DeviceContext();
devCtx->CreateRenderingContextInstance(*getter_AddRefs(renderingContext));
if (!renderingContext) return;
renderingContext->Init(devCtx, shadowContext);
aCtx->Save();
aCtx->NewPath();
aCtx->SetColor(gfxRGBA(shadowColor));
// Draw the text onto our alpha-only surface to capture the alpha values.
// Remember that the box blur context has a device offset on it, so we don't need to
// translate any coordinates to fit on the surface.
DrawText(*renderingContext, shadowRect, shadowColor);
contextBoxBlur.DoPaint();
aCtx->Restore();
}
void
nsTextBoxFrame::LayoutTitle(nsPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
@ -888,7 +929,16 @@ nsTextBoxFrame::DoLayout(nsBoxLayoutState& aBoxLayoutState)
mState |= NS_STATE_NEED_LAYOUT;
return nsLeafBoxFrame::DoLayout(aBoxLayoutState);
nsresult rv = nsLeafBoxFrame::DoLayout(aBoxLayoutState);
const nsStyleText* textStyle = GetStyleText();
if (textStyle->mTextShadow) {
nsPoint origin(0,0);
nsRect textRect = CalcTextRect(*aBoxLayoutState.GetRenderingContext(), origin);
nsRect overflowRect(nsLayoutUtils::GetTextShadowRectsUnion(textRect, this));
FinishAndStoreOverflow(&overflowRect, GetSize());
}
return rv;
}
/* virtual */ void
@ -928,6 +978,36 @@ nsTextBoxFrame::CalcTextSize(nsBoxLayoutState& aBoxLayoutState)
}
}
nsRect
nsTextBoxFrame::CalcTextRect(nsIRenderingContext &aRenderingContext, const nsPoint &aTextOrigin)
{
nsRect textRect(aTextOrigin, GetSize());
textRect.Deflate(GetUsedBorderAndPadding());
// determine (cropped) title and underline position
nsPresContext* presContext = PresContext();
LayoutTitle(presContext, aRenderingContext, textRect);
// make the rect as small as our (cropped) text.
nscoord outerWidth = textRect.width;
textRect.width = mTitleWidth;
// Align our text within the overall rect by checking our text-align property.
const nsStyleVisibility* vis = GetStyleVisibility();
const nsStyleText* textStyle = GetStyleText();
if (textStyle->mTextAlign == NS_STYLE_TEXT_ALIGN_CENTER)
textRect.x += (outerWidth - textRect.width)/2;
else if (textStyle->mTextAlign == NS_STYLE_TEXT_ALIGN_RIGHT) {
if (vis->mDirection == NS_STYLE_DIRECTION_LTR)
textRect.x += (outerWidth - textRect.width);
}
else {
if (vis->mDirection == NS_STYLE_DIRECTION_RTL)
textRect.x += (outerWidth - textRect.width);
}
return textRect;
}
/**
* Ok return our dimensions
*/

View File

@ -104,6 +104,8 @@ protected:
void CalcTextSize(nsBoxLayoutState& aBoxLayoutState);
nsRect CalcTextRect(nsIRenderingContext &aRenderingContext, const nsPoint &aTextOrigin);
nsTextBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext);
void CalculateTitleForWidth(nsPresContext* aPresContext,
@ -123,6 +125,16 @@ private:
PRBool AlwaysAppendAccessKey();
PRBool InsertSeparatorBeforeAccessKey();
void DrawText(nsIRenderingContext& aRenderingContext,
const nsRect& aTextRect,
const nscolor& aOverrideColor);
void PaintOneShadow(gfxContext * aCtx,
const nsRect& aTextRect,
nsCSSShadowItem* aShadowDetails,
const nscolor& aForegroundColor);
CroppingStyle mCropType;
nsString mTitle;
nsString mCroppedTitle;

View File

@ -214,4 +214,11 @@ interface nsIRequest : nsISupports
const unsigned long VALIDATE_ALWAYS = 1 << 11;
const unsigned long VALIDATE_NEVER = 1 << 12;
const unsigned long VALIDATE_ONCE_PER_SESSION = 1 << 13;
/**
* When set, this flag indicates that no user-specific data should be added
* to the request when opened. This means that things like authorization
* tokens or cookie headers should not be added.
*/
const unsigned long LOAD_ANONYMOUS = 1 << 14;
};

View File

@ -663,6 +663,10 @@ nsHttpChannel::SetupTransaction()
void
nsHttpChannel::AddCookiesToRequest()
{
if (mLoadFlags & LOAD_ANONYMOUS) {
return;
}
nsXPIDLCString cookie;
nsICookieService *cs = gHttpHandler->GetCookieService();
@ -1746,13 +1750,22 @@ nsHttpChannel::OpenOfflineCacheEntryForWriting()
nsresult
nsHttpChannel::GenerateCacheKey(nsACString &cacheKey)
{
cacheKey.Truncate();
if (mLoadFlags & LOAD_ANONYMOUS) {
cacheKey.AssignLiteral("anon&");
}
if (mPostID) {
char buf[32];
PR_snprintf(buf, sizeof(buf), "id=%x&uri=", mPostID);
cacheKey.Assign(buf);
} else
cacheKey.Truncate();
PR_snprintf(buf, sizeof(buf), "id=%x&", mPostID);
cacheKey.Append(buf);
}
if (!cacheKey.IsEmpty()) {
cacheKey.AppendLiteral("uri=");
}
// Strip any trailing #ref from the URL before using it as the key
const char *spec = mFallbackChannel ? mFallbackKey.get() : mSpec.get();
const char *p = strchr(spec, '#');
@ -2888,6 +2901,10 @@ nsHttpChannel::ProcessAuthentication(PRUint32 httpStatus)
LOG(("nsHttpChannel::ProcessAuthentication [this=%x code=%u]\n",
this, httpStatus));
if (mLoadFlags & LOAD_ANONYMOUS) {
return NS_ERROR_NOT_AVAILABLE;
}
const char *challenges;
PRBool proxyAuth = (httpStatus == 407);
@ -3608,6 +3625,10 @@ nsHttpChannel::AddAuthorizationHeaders()
{
LOG(("nsHttpChannel::AddAuthorizationHeaders? [this=%x]\n", this));
if (mLoadFlags & LOAD_ANONYMOUS) {
return;
}
// this getter never fails
nsHttpAuthCache *authCache = gHttpHandler->AuthCache();
@ -4500,6 +4521,10 @@ nsHttpChannel::GetResponseVersion(PRUint32 *major, PRUint32 *minor)
NS_IMETHODIMP
nsHttpChannel::SetCookie(const char *aCookieHeader)
{
if (mLoadFlags & LOAD_ANONYMOUS) {
return NS_OK;
}
// empty header isn't an error
if (!(aCookieHeader && *aCookieHeader))
return NS_OK;

View File

@ -2298,12 +2298,13 @@ ServerHandler.prototype =
Ci.nsIFileInputStream.CLOSE_ON_EOF);
var sis = new ScriptableInputStream(fis);
var s = Cu.Sandbox(gGlobalObject);
s.importFunction(dump, "dump");
Cu.evalInSandbox(sis.read(file.fileSize), s);
s.handleRequest(metadata, response);
}
catch (e)
{
dumpn("*** error running SJS: " + e);
dump("*** error running SJS: " + e + " on line " + (e.lineNumber-2192) + "\n");
throw HTTP_500;
}
}

View File

@ -104,6 +104,7 @@ EXPORTS = \
nsStringEnumerator.h \
nsHashPropertyBag.h \
nsWhitespaceTokenizer.h \
nsCommaseparatedTokenizer.h \
$(NULL)
XPIDLSRCS = \

View File

@ -0,0 +1,193 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** 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 TransforMiiX XSLT processor code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Peter Van der Beken <peterv@propagandism.org>
*
* 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 ***** */
#ifndef __nsCommaSeparatedTokenizer_h
#define __nsCommaSeparatedTokenizer_h
#include "nsDependentSubstring.h"
/**
* This parses a comma separated string into tokens. Whitespace surrounding
* tokens are not treated as part of tokens, however whitespace inside a token
* is. If the final token is the empty string it is not returned.
*
* Some examples:
*
* "foo, bar, baz" -> "foo" "bar" "baz"
* "foo,bar,baz" -> "foo" "bar" "baz"
* "foo , bar hi , baz" -> "foo" "bar hi" "baz"
* "foo, ,bar,baz" -> "foo" "" "bar" "baz"
* "foo,,bar,baz" -> "foo" "" "bar" "baz"
* "foo,bar,baz," -> "foo" "bar" "baz"
*/
class nsCommaSeparatedTokenizer
{
public:
nsCommaSeparatedTokenizer(const nsSubstring& aSource)
{
aSource.BeginReading(mIter);
aSource.EndReading(mEnd);
while (mIter != mEnd && isWhitespace(*mIter)) {
++mIter;
}
}
/**
* Checks if any more tokens are available.
*/
PRBool hasMoreTokens()
{
NS_ASSERTION(mIter == mEnd || !isWhitespace(*mIter),
"Should be at beginning of token if there is one");
return mIter != mEnd;
}
/**
* Returns the next token.
*/
const nsDependentSubstring nextToken()
{
nsSubstring::const_char_iterator end = mIter, begin = mIter;
NS_ASSERTION(mIter == mEnd || !isWhitespace(*mIter),
"Should be at beginning of token if there is one");
// Search until we hit comma or end
while (mIter != mEnd && *mIter != ',') {
while (mIter != mEnd && !isWhitespace(*mIter) && *mIter != ',') {
++mIter;
}
end = mIter;
while (mIter != mEnd && isWhitespace(*mIter)) {
++mIter;
}
}
// Skip comma
if (mIter != mEnd) {
NS_ASSERTION(*mIter == ',', "Ended loop too soon");
++mIter;
while (mIter != mEnd && isWhitespace(*mIter)) {
++mIter;
}
}
return Substring(begin, end);
}
private:
nsSubstring::const_char_iterator mIter, mEnd;
PRBool isWhitespace(PRUnichar aChar)
{
return aChar <= ' ' &&
(aChar == ' ' || aChar == '\n' ||
aChar == '\r'|| aChar == '\t');
}
};
class nsCCommaSeparatedTokenizer
{
public:
nsCCommaSeparatedTokenizer(const nsCSubstring& aSource)
{
aSource.BeginReading(mIter);
aSource.EndReading(mEnd);
while (mIter != mEnd && isWhitespace(*mIter)) {
++mIter;
}
}
/**
* Checks if any more tokens are available.
*/
PRBool hasMoreTokens()
{
return mIter != mEnd;
}
/**
* Returns the next token.
*/
const nsDependentCSubstring nextToken()
{
nsCSubstring::const_char_iterator end = mIter, begin = mIter;
// Search until we hit comma or end
while (mIter != mEnd && *mIter != ',') {
while (mIter != mEnd && !isWhitespace(*mIter) && *mIter != ',') {
++mIter;
}
end = mIter;
while (mIter != mEnd && isWhitespace(*mIter)) {
++mIter;
}
}
// Skip comma
if (mIter != mEnd) {
NS_ASSERTION(*mIter == ',', "Ended loop too soon");
++mIter;
while (mIter != mEnd && isWhitespace(*mIter)) {
++mIter;
}
}
return Substring(begin, end);
}
private:
nsCSubstring::const_char_iterator mIter, mEnd;
PRBool isWhitespace(unsigned char aChar)
{
return aChar <= ' ' &&
(aChar == ' ' || aChar == '\n' ||
aChar == '\r'|| aChar == '\t');
}
};
#endif /* __nsWhitespaceTokenizer_h */

View File

@ -84,6 +84,14 @@ class NS_COM nsCaseInsensitiveCStringComparator
virtual int operator()( char_type, char_type ) const;
};
class nsCaseInsensitiveCStringArrayComparator
{
public:
template<class A, class B>
PRBool Equals(const A& a, const B& b) const {
return a.Equals(b, nsCaseInsensitiveCStringComparator());
}
};
// included here for backwards compatibility
#ifndef nsSubstringTuple_h___