diff options
Diffstat (limited to '')
-rw-r--r-- | image/decoders/icon/nsIconURI.cpp | 666 |
1 files changed, 666 insertions, 0 deletions
diff --git a/image/decoders/icon/nsIconURI.cpp b/image/decoders/icon/nsIconURI.cpp new file mode 100644 index 0000000000..49e5302082 --- /dev/null +++ b/image/decoders/icon/nsIconURI.cpp @@ -0,0 +1,666 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set sw=2 sts=2 ts=2 et tw=80: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsIconURI.h" + +#include "mozilla/ArrayUtils.h" +#include "mozilla/ipc/URIUtils.h" +#include "mozilla/Sprintf.h" + +#include "nsIClassInfoImpl.h" +#include "nsIIOService.h" +#include "nsISerializable.h" +#include "nsIObjectInputStream.h" +#include "nsIObjectOutputStream.h" +#include "nsIURL.h" +#include "nsNetUtil.h" +#include "plstr.h" +#include "nsCRT.h" +#include <stdlib.h> + +using namespace mozilla; +using namespace mozilla::ipc; + +#define DEFAULT_IMAGE_SIZE 16 + +#if defined(MAX_PATH) +# define SANE_FILE_NAME_LEN MAX_PATH +#elif defined(PATH_MAX) +# define SANE_FILE_NAME_LEN PATH_MAX +#else +# define SANE_FILE_NAME_LEN 1024 +#endif + +static NS_DEFINE_CID(kThisIconURIImplementationCID, + NS_THIS_ICONURI_IMPLEMENTATION_CID); + +static const char* const kSizeStrings[] = {"button", "toolbar", "toolbarsmall", + "menu", "dnd", "dialog"}; + +static const char* const kStateStrings[] = {"normal", "disabled"}; + +//////////////////////////////////////////////////////////////////////////////// + +NS_IMPL_CLASSINFO(nsMozIconURI, nullptr, nsIClassInfo::THREADSAFE, + NS_ICONURI_CID) +// Empty CI getter. We only need nsIClassInfo for Serialization +NS_IMPL_CI_INTERFACE_GETTER0(nsMozIconURI) + +nsMozIconURI::nsMozIconURI() + : mSize(DEFAULT_IMAGE_SIZE), mIconSize(-1), mIconState(-1) {} + +nsMozIconURI::~nsMozIconURI() {} + +NS_IMPL_ADDREF(nsMozIconURI) +NS_IMPL_RELEASE(nsMozIconURI) + +NS_INTERFACE_MAP_BEGIN(nsMozIconURI) + if (aIID.Equals(kThisIconURIImplementationCID)) { + foundInterface = static_cast<nsIURI*>(this); + } else + NS_INTERFACE_MAP_ENTRY(nsIMozIconURI) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIURI) + NS_INTERFACE_MAP_ENTRY(nsIURI) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsINestedURI, mIconURL) + NS_INTERFACE_MAP_ENTRY(nsISerializable) + NS_IMPL_QUERY_CLASSINFO(nsMozIconURI) +NS_INTERFACE_MAP_END + +#define MOZICON_SCHEME "moz-icon:" +#define MOZICON_SCHEME_LEN (sizeof(MOZICON_SCHEME) - 1) + +//////////////////////////////////////////////////////////////////////////////// +// nsIURI methods: + +NS_IMETHODIMP +nsMozIconURI::GetSpec(nsACString& aSpec) { + aSpec = MOZICON_SCHEME; + + if (mIconURL) { + nsAutoCString fileIconSpec; + nsresult rv = mIconURL->GetSpec(fileIconSpec); + NS_ENSURE_SUCCESS(rv, rv); + aSpec += fileIconSpec; + } else if (!mStockIcon.IsEmpty()) { + aSpec += "//stock/"; + aSpec += mStockIcon; + } else { + aSpec += "//"; + aSpec += mFileName; + } + + aSpec += "?size="; + if (mIconSize >= 0) { + aSpec += kSizeStrings[mIconSize]; + } else { + char buf[20]; + SprintfLiteral(buf, "%d", mSize); + aSpec.Append(buf); + } + + if (mIconState >= 0) { + aSpec += "&state="; + aSpec += kStateStrings[mIconState]; + } + + if (!mContentType.IsEmpty()) { + aSpec += "&contentType="; + aSpec += mContentType.get(); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsMozIconURI::GetSpecIgnoringRef(nsACString& result) { return GetSpec(result); } + +NS_IMETHODIMP +nsMozIconURI::GetDisplaySpec(nsACString& aUnicodeSpec) { + return GetSpec(aUnicodeSpec); +} + +NS_IMETHODIMP +nsMozIconURI::GetDisplayHostPort(nsACString& aUnicodeHostPort) { + return GetHostPort(aUnicodeHostPort); +} + +NS_IMETHODIMP +nsMozIconURI::GetDisplayHost(nsACString& aUnicodeHost) { + return GetHost(aUnicodeHost); +} + +NS_IMETHODIMP +nsMozIconURI::GetDisplayPrePath(nsACString& aPrePath) { + return GetPrePath(aPrePath); +} + +NS_IMETHODIMP +nsMozIconURI::GetHasRef(bool* result) { + *result = false; + return NS_OK; +} + +NS_IMETHODIMP +nsMozIconURI::GetHasUserPass(bool* result) { + *result = false; + return NS_OK; +} + +NS_IMETHODIMP +nsMozIconURI::GetHasQuery(bool* result) { + *result = false; + return NS_OK; +} + +NS_IMPL_NSIURIMUTATOR_ISUPPORTS(nsMozIconURI::Mutator, nsIURISetters, + nsIURIMutator, nsISerializable) + +NS_IMETHODIMP +nsMozIconURI::Mutate(nsIURIMutator** aMutator) { + RefPtr<nsMozIconURI::Mutator> mutator = new nsMozIconURI::Mutator(); + nsresult rv = mutator->InitFromURI(this); + if (NS_FAILED(rv)) { + return rv; + } + mutator.forget(aMutator); + return NS_OK; +} + +// helper function for parsing out attributes like size, and contentType +// from the icon url. +// takes a string like ?size=32&contentType=text/html and returns a new string +// containing just the attribute value. i.e you could pass in this string with +// an attribute name of 'size=', this will return 32 +// Assumption: attribute pairs in the string are separated by '&'. +static void extractAttributeValue(const char* aSearchString, + const char* aAttributeName, + nsCString& aResult) { + aResult.Truncate(); + + if (aSearchString && aAttributeName) { + // search the string for attributeName + uint32_t attributeNameSize = strlen(aAttributeName); + const char* startOfAttribute = PL_strcasestr(aSearchString, aAttributeName); + if (startOfAttribute && + (*(startOfAttribute - 1) == '?' || *(startOfAttribute - 1) == '&')) { + startOfAttribute += attributeNameSize; // skip over the attributeName + // is there something after the attribute name + if (*startOfAttribute) { + const char* endofAttribute = strchr(startOfAttribute, '&'); + if (endofAttribute) { + aResult.Assign(Substring(startOfAttribute, endofAttribute)); + } else { + aResult.Assign(startOfAttribute); + } + } // if we have a attribute value + } // if we have a attribute name + } // if we got non-null search string and attribute name values +} + +nsresult nsMozIconURI::SetSpecInternal(const nsACString& aSpec) { + // Reset everything to default values. + mIconURL = nullptr; + mSize = DEFAULT_IMAGE_SIZE; + mContentType.Truncate(); + mFileName.Truncate(); + mStockIcon.Truncate(); + mIconSize = -1; + mIconState = -1; + + nsAutoCString iconSpec(aSpec); + if (!Substring(iconSpec, 0, MOZICON_SCHEME_LEN) + .EqualsLiteral(MOZICON_SCHEME) || + (!Substring(iconSpec, MOZICON_SCHEME_LEN, 7).EqualsLiteral("file://") && + // Checking for the leading '//' will match both the '//stock/' and + // '//.foo' cases: + !Substring(iconSpec, MOZICON_SCHEME_LEN, 2).EqualsLiteral("//"))) { + return NS_ERROR_MALFORMED_URI; + } + + int32_t questionMarkPos = iconSpec.Find("?"); + if (questionMarkPos != -1 && + static_cast<int32_t>(iconSpec.Length()) > (questionMarkPos + 1)) { + extractAttributeValue(iconSpec.get(), "contentType=", mContentType); + + nsAutoCString sizeString; + extractAttributeValue(iconSpec.get(), "size=", sizeString); + if (!sizeString.IsEmpty()) { + const char* sizeStr = sizeString.get(); + for (uint32_t i = 0; i < ArrayLength(kSizeStrings); i++) { + if (nsCRT::strcasecmp(sizeStr, kSizeStrings[i]) == 0) { + mIconSize = i; + break; + } + } + + int32_t sizeValue = atoi(sizeString.get()); + if (sizeValue > 0) { + mSize = sizeValue; + } + } + + nsAutoCString stateString; + extractAttributeValue(iconSpec.get(), "state=", stateString); + if (!stateString.IsEmpty()) { + const char* stateStr = stateString.get(); + for (uint32_t i = 0; i < ArrayLength(kStateStrings); i++) { + if (nsCRT::strcasecmp(stateStr, kStateStrings[i]) == 0) { + mIconState = i; + break; + } + } + } + } + + int32_t pathLength = iconSpec.Length() - MOZICON_SCHEME_LEN; + if (questionMarkPos != -1) { + pathLength = questionMarkPos - MOZICON_SCHEME_LEN; + } + if (pathLength < 3) { + return NS_ERROR_MALFORMED_URI; + } + + nsAutoCString iconPath(Substring(iconSpec, MOZICON_SCHEME_LEN, pathLength)); + + // Icon URI path can have three forms: + // (1) //stock/<icon-identifier> + // (2) //<some dummy file with an extension> + // (3) a valid URL + + if (!strncmp("//stock/", iconPath.get(), 8)) { + mStockIcon.Assign(Substring(iconPath, 8)); + // An icon identifier must always be specified. + if (mStockIcon.IsEmpty()) { + return NS_ERROR_MALFORMED_URI; + } + return NS_OK; + } + + if (StringBeginsWith(iconPath, "//"_ns)) { + // Sanity check this supposed dummy file name. + if (iconPath.Length() > SANE_FILE_NAME_LEN) { + return NS_ERROR_MALFORMED_URI; + } + iconPath.Cut(0, 2); + mFileName.Assign(iconPath); + } + + nsresult rv; + nsCOMPtr<nsIIOService> ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIURI> uri; + ioService->NewURI(iconPath, nullptr, nullptr, getter_AddRefs(uri)); + mIconURL = do_QueryInterface(uri); + if (mIconURL) { + // The inner URI should be a 'file:' one. If not, bail. + if (!mIconURL->SchemeIs("file")) { + return NS_ERROR_MALFORMED_URI; + } + mFileName.Truncate(); + } else if (mFileName.IsEmpty()) { + return NS_ERROR_MALFORMED_URI; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsMozIconURI::GetPrePath(nsACString& prePath) { + prePath = MOZICON_SCHEME; + return NS_OK; +} + +NS_IMETHODIMP +nsMozIconURI::GetScheme(nsACString& aScheme) { + aScheme = "moz-icon"; + return NS_OK; +} + +nsresult nsMozIconURI::SetScheme(const nsACString& aScheme) { + // doesn't make sense to set the scheme of a moz-icon URL + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsMozIconURI::GetUsername(nsACString& aUsername) { return NS_ERROR_FAILURE; } + +nsresult nsMozIconURI::SetUsername(const nsACString& aUsername) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsMozIconURI::GetPassword(nsACString& aPassword) { return NS_ERROR_FAILURE; } + +nsresult nsMozIconURI::SetPassword(const nsACString& aPassword) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsMozIconURI::GetUserPass(nsACString& aUserPass) { return NS_ERROR_FAILURE; } + +nsresult nsMozIconURI::SetUserPass(const nsACString& aUserPass) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsMozIconURI::GetHostPort(nsACString& aHostPort) { return NS_ERROR_FAILURE; } + +nsresult nsMozIconURI::SetHostPort(const nsACString& aHostPort) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsMozIconURI::GetHost(nsACString& aHost) { return NS_ERROR_FAILURE; } + +nsresult nsMozIconURI::SetHost(const nsACString& aHost) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsMozIconURI::GetPort(int32_t* aPort) { return NS_ERROR_FAILURE; } + +nsresult nsMozIconURI::SetPort(int32_t aPort) { return NS_ERROR_FAILURE; } + +NS_IMETHODIMP +nsMozIconURI::GetPathQueryRef(nsACString& aPath) { + aPath.Truncate(); + return NS_OK; +} + +nsresult nsMozIconURI::SetPathQueryRef(const nsACString& aPath) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsMozIconURI::GetFilePath(nsACString& aFilePath) { + aFilePath.Truncate(); + return NS_OK; +} + +nsresult nsMozIconURI::SetFilePath(const nsACString& aFilePath) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsMozIconURI::GetQuery(nsACString& aQuery) { + aQuery.Truncate(); + return NS_OK; +} + +nsresult nsMozIconURI::SetQuery(const nsACString& aQuery) { + return NS_ERROR_FAILURE; +} + +nsresult nsMozIconURI::SetQueryWithEncoding(const nsACString& aQuery, + const Encoding* aEncoding) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsMozIconURI::GetRef(nsACString& aRef) { + aRef.Truncate(); + return NS_OK; +} + +nsresult nsMozIconURI::SetRef(const nsACString& aRef) { + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsMozIconURI::Equals(nsIURI* other, bool* result) { + *result = false; + NS_ENSURE_ARG_POINTER(other); + MOZ_ASSERT(result, "null pointer"); + + nsAutoCString spec1; + nsAutoCString spec2; + + nsresult rv = GetSpec(spec1); + NS_ENSURE_SUCCESS(rv, rv); + rv = other->GetSpec(spec2); + NS_ENSURE_SUCCESS(rv, rv); + + if (!nsCRT::strcasecmp(spec1.get(), spec2.get())) { + *result = true; + } else { + *result = false; + } + return NS_OK; +} + +NS_IMETHODIMP +nsMozIconURI::EqualsExceptRef(nsIURI* other, bool* result) { + // GetRef/SetRef not supported by nsMozIconURI, so + // EqualsExceptRef() is the same as Equals(). + return Equals(other, result); +} + +NS_IMETHODIMP +nsMozIconURI::SchemeIs(const char* aScheme, bool* aEquals) { + MOZ_ASSERT(aEquals, "null pointer"); + if (!aScheme) { + *aEquals = false; + return NS_OK; + } + + *aEquals = nsCRT::strcasecmp("moz-icon", aScheme) == 0; + return NS_OK; +} + +nsresult nsMozIconURI::Clone(nsIURI** result) { + nsCOMPtr<nsIURL> newIconURL; + if (mIconURL) { + newIconURL = mIconURL; + } + + RefPtr<nsMozIconURI> uri = new nsMozIconURI(); + newIconURL.swap(uri->mIconURL); + uri->mSize = mSize; + uri->mContentType = mContentType; + uri->mFileName = mFileName; + uri->mStockIcon = mStockIcon; + uri->mIconSize = mIconSize; + uri->mIconState = mIconState; + uri.forget(result); + + return NS_OK; +} + +NS_IMETHODIMP +nsMozIconURI::Resolve(const nsACString& relativePath, nsACString& result) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsMozIconURI::GetAsciiSpec(nsACString& aSpecA) { return GetSpec(aSpecA); } + +NS_IMETHODIMP +nsMozIconURI::GetAsciiHostPort(nsACString& aHostPortA) { + return GetHostPort(aHostPortA); +} + +NS_IMETHODIMP +nsMozIconURI::GetAsciiHost(nsACString& aHostA) { return GetHost(aHostA); } + +//////////////////////////////////////////////////////////////////////////////// +// nsIIconUri methods: + +NS_IMETHODIMP +nsMozIconURI::GetIconURL(nsIURL** aFileUrl) { + *aFileUrl = mIconURL; + NS_IF_ADDREF(*aFileUrl); + return NS_OK; +} + +NS_IMETHODIMP +nsMozIconURI::GetImageSize(uint32_t* aImageSize) +// measured by # of pixels in a row. defaults to 16. +{ + *aImageSize = mSize; + return NS_OK; +} + +NS_IMETHODIMP +nsMozIconURI::GetContentType(nsACString& aContentType) { + aContentType = mContentType; + return NS_OK; +} + +NS_IMETHODIMP +nsMozIconURI::GetFileExtension(nsACString& aFileExtension) { + // First, try to get the extension from mIconURL if we have one + if (mIconURL) { + nsAutoCString fileExt; + if (NS_SUCCEEDED(mIconURL->GetFileExtension(fileExt))) { + if (!fileExt.IsEmpty()) { + // unfortunately, this code doesn't give us the required '.' in + // front of the extension so we have to do it ourselves. + aFileExtension.Assign('.'); + aFileExtension.Append(fileExt); + } + } + return NS_OK; + } + + if (!mFileName.IsEmpty()) { + // truncate the extension out of the file path... + const char* chFileName = mFileName.get(); // get the underlying buffer + const char* fileExt = strrchr(chFileName, '.'); + if (!fileExt) { + return NS_OK; + } + aFileExtension = fileExt; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsMozIconURI::GetStockIcon(nsACString& aStockIcon) { + aStockIcon = mStockIcon; + return NS_OK; +} + +NS_IMETHODIMP +nsMozIconURI::GetIconSize(nsACString& aSize) { + if (mIconSize >= 0) { + aSize = kSizeStrings[mIconSize]; + } else { + aSize.Truncate(); + } + return NS_OK; +} + +NS_IMETHODIMP +nsMozIconURI::GetIconState(nsACString& aState) { + if (mIconState >= 0) { + aState = kStateStrings[mIconState]; + } else { + aState.Truncate(); + } + return NS_OK; +} + +void nsMozIconURI::Serialize(URIParams& aParams) { + IconURIParams params; + + if (mIconURL) { + URIParams iconURLParams; + SerializeURI(mIconURL, iconURLParams); + if (iconURLParams.type() == URIParams::T__None) { + // Serialization failed, bail. + return; + } + + params.uri() = Some(std::move(iconURLParams)); + } else { + params.uri() = Nothing(); + } + + params.size() = mSize; + params.fileName() = mFileName; + params.stockIcon() = mStockIcon; + params.iconSize() = mIconSize; + params.iconState() = mIconState; + + aParams = params; +} + +bool nsMozIconURI::Deserialize(const URIParams& aParams) { + if (aParams.type() != URIParams::TIconURIParams) { + MOZ_ASSERT_UNREACHABLE("Received unknown URI from other process!"); + return false; + } + + const IconURIParams& params = aParams.get_IconURIParams(); + if (params.uri().isSome()) { + nsCOMPtr<nsIURI> uri = DeserializeURI(params.uri().ref()); + mIconURL = do_QueryInterface(uri); + if (!mIconURL) { + MOZ_ASSERT_UNREACHABLE("bad nsIURI passed"); + return false; + } + } + + mSize = params.size(); + mContentType = params.contentType(); + mFileName = params.fileName(); + mStockIcon = params.stockIcon(); + + if (params.iconSize() < -1 || + params.iconSize() >= (int32_t)ArrayLength(kSizeStrings)) { + return false; + } + mIconSize = params.iconSize(); + + if (params.iconState() < -1 || + params.iconState() >= (int32_t)ArrayLength(kStateStrings)) { + return false; + } + mIconState = params.iconState(); + + return true; +} + +NS_IMETHODIMP +nsMozIconURI::GetInnerURI(nsIURI** aURI) { + nsCOMPtr<nsIURI> iconURL = mIconURL; + if (!iconURL) { + *aURI = nullptr; + return NS_ERROR_FAILURE; + } + + iconURL.forget(aURI); + return NS_OK; +} + +NS_IMETHODIMP +nsMozIconURI::GetInnermostURI(nsIURI** aURI) { + return NS_ImplGetInnermostURI(this, aURI); +} + +NS_IMETHODIMP +nsMozIconURI::Read(nsIObjectInputStream* aStream) { + MOZ_ASSERT_UNREACHABLE("Use nsIURIMutator.read() instead"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult nsMozIconURI::ReadPrivate(nsIObjectInputStream* aStream) { + nsAutoCString spec; + nsresult rv = aStream->ReadCString(spec); + NS_ENSURE_SUCCESS(rv, rv); + return SetSpecInternal(spec); +} + +NS_IMETHODIMP +nsMozIconURI::Write(nsIObjectOutputStream* aStream) { + nsAutoCString spec; + nsresult rv = GetSpec(spec); + NS_ENSURE_SUCCESS(rv, rv); + return aStream->WriteStringZ(spec.get()); +} |