mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-27 12:15:33 +00:00
merge to latest
This commit is contained in:
commit
7af2a885ec
@ -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.
|
||||
|
@ -120,6 +120,7 @@ CPPSRCS = \
|
||||
nsContentSink.cpp \
|
||||
nsContentUtils.cpp \
|
||||
nsCopySupport.cpp \
|
||||
nsCrossSiteListenerProxy.cpp \
|
||||
nsDataDocumentContentPolicy.cpp \
|
||||
nsDOMAttribute.cpp \
|
||||
nsDOMAttributeMap.cpp \
|
||||
|
421
content/base/src/nsCrossSiteListenerProxy.cpp
Normal file
421
content/base/src/nsCrossSiteListenerProxy.cpp
Normal 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;
|
||||
}
|
94
content/base/src/nsCrossSiteListenerProxy.h
Normal file
94
content/base/src/nsCrossSiteListenerProxy.h
Normal 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;
|
||||
};
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
//
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
45
content/base/test/file_CrossSiteXHR_cache_server.sjs
Normal file
45
content/base/test/file_CrossSiteXHR_cache_server.sjs
Normal 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");
|
||||
}
|
82
content/base/test/file_CrossSiteXHR_inner.html
Normal file
82
content/base/test/file_CrossSiteXHR_inner.html
Normal 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>
|
87
content/base/test/file_CrossSiteXHR_server.sjs
Normal file
87
content/base/test/file_CrossSiteXHR_server.sjs
Normal 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;
|
||||
}
|
||||
}
|
578
content/base/test/test_CrossSiteXHR.html
Normal file
578
content/base/test/test_CrossSiteXHR.html
Normal 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>
|
356
content/base/test/test_CrossSiteXHR_cache.html
Normal file
356
content/base/test/test_CrossSiteXHR_cache.html
Normal 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>
|
@ -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,
|
||||
|
@ -1750,6 +1750,12 @@ found:
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsEventListenerManager::HasListeners()
|
||||
{
|
||||
return !mListeners.IsEmpty();
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsEventListenerManager::HasUnloadListeners()
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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).
|
||||
|
@ -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)
|
||||
|
BIN
content/media/video/test/sound.ogg
Normal file
BIN
content/media/video/test/sound.ogg
Normal file
Binary file not shown.
28
content/media/video/test/test_audio1.html
Normal file
28
content/media/video/test/test_audio1.html
Normal 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>
|
27
content/media/video/test/test_audio2.html
Normal file
27
content/media/video/test/test_audio2.html
Normal 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>
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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).
|
||||
|
@ -2629,7 +2629,6 @@ nsLayoutUtils::CalculateContentBottom(nsIFrame* aFrame)
|
||||
childList = aFrame->GetAdditionalChildListName(nextListID);
|
||||
nextListID++;
|
||||
} while (childList);
|
||||
|
||||
}
|
||||
|
||||
return contentBottom;
|
||||
|
@ -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__ */
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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 |
@ -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
|
||||
|
6
layout/reftests/text-shadow/basic-negcoord-ref.xul
Normal file
6
layout/reftests/text-shadow/basic-negcoord-ref.xul
Normal 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>
|
5
layout/reftests/text-shadow/basic-negcoord.xul
Normal file
5
layout/reftests/text-shadow/basic-negcoord.xul
Normal 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>
|
6
layout/reftests/text-shadow/basic-ref.xul
Normal file
6
layout/reftests/text-shadow/basic-ref.xul
Normal 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>
|
5
layout/reftests/text-shadow/basic.xul
Normal file
5
layout/reftests/text-shadow/basic.xul
Normal 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>
|
3
layout/reftests/text-shadow/blur-notref.xul
Normal file
3
layout/reftests/text-shadow/blur-notref.xul
Normal 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>
|
3
layout/reftests/text-shadow/blur.xul
Normal file
3
layout/reftests/text-shadow/blur.xul
Normal 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>
|
6
layout/reftests/text-shadow/color-inherit-ref.xul
Normal file
6
layout/reftests/text-shadow/color-inherit-ref.xul
Normal 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>
|
5
layout/reftests/text-shadow/color-inherit.xul
Normal file
5
layout/reftests/text-shadow/color-inherit.xul
Normal 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>
|
9
layout/reftests/text-shadow/multiple-noblur-ref.xul
Normal file
9
layout/reftests/text-shadow/multiple-noblur-ref.xul
Normal 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>
|
5
layout/reftests/text-shadow/multiple-noblur.xul
Normal file
5
layout/reftests/text-shadow/multiple-noblur.xul
Normal 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>
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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_*/
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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__
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -104,6 +104,7 @@ EXPORTS = \
|
||||
nsStringEnumerator.h \
|
||||
nsHashPropertyBag.h \
|
||||
nsWhitespaceTokenizer.h \
|
||||
nsCommaseparatedTokenizer.h \
|
||||
$(NULL)
|
||||
|
||||
XPIDLSRCS = \
|
||||
|
193
xpcom/ds/nsCommaSeparatedTokenizer.h
Normal file
193
xpcom/ds/nsCommaSeparatedTokenizer.h
Normal 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 */
|
@ -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___
|
||||
|
Loading…
Reference in New Issue
Block a user