michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: michael@0: #include "nsIconURI.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsIIOService.h" michael@0: #include "nsIURL.h" michael@0: #include "prprf.h" michael@0: #include "plstr.h" michael@0: #include michael@0: michael@0: using namespace mozilla; michael@0: michael@0: #define DEFAULT_IMAGE_SIZE 16 michael@0: michael@0: #if defined(MAX_PATH) michael@0: #define SANE_FILE_NAME_LEN MAX_PATH michael@0: #elif defined(PATH_MAX) michael@0: #define SANE_FILE_NAME_LEN PATH_MAX michael@0: #else michael@0: #define SANE_FILE_NAME_LEN 1024 michael@0: #endif michael@0: michael@0: // helper function for parsing out attributes like size, and contentType michael@0: // from the icon url. michael@0: static void extractAttributeValue(const char * searchString, const char * attributeName, nsCString& aResult); michael@0: michael@0: static const char *kSizeStrings[] = michael@0: { michael@0: "button", michael@0: "toolbar", michael@0: "toolbarsmall", michael@0: "menu", michael@0: "dnd", michael@0: "dialog" michael@0: }; michael@0: michael@0: static const char *kStateStrings[] = michael@0: { michael@0: "normal", michael@0: "disabled" michael@0: }; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: nsMozIconURI::nsMozIconURI() michael@0: : mSize(DEFAULT_IMAGE_SIZE), michael@0: mIconSize(-1), michael@0: mIconState(-1) michael@0: { michael@0: } michael@0: michael@0: nsMozIconURI::~nsMozIconURI() michael@0: { michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsMozIconURI, nsIMozIconURI, nsIURI) michael@0: michael@0: #define MOZICON_SCHEME "moz-icon:" michael@0: #define MOZICON_SCHEME_LEN (sizeof(MOZICON_SCHEME) - 1) michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // nsIURI methods: michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetSpec(nsACString &aSpec) michael@0: { michael@0: aSpec = MOZICON_SCHEME; michael@0: michael@0: if (mIconURL) michael@0: { michael@0: nsAutoCString fileIconSpec; michael@0: nsresult rv = mIconURL->GetSpec(fileIconSpec); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: aSpec += fileIconSpec; michael@0: } michael@0: else if (!mStockIcon.IsEmpty()) michael@0: { michael@0: aSpec += "//stock/"; michael@0: aSpec += mStockIcon; michael@0: } michael@0: else michael@0: { michael@0: aSpec += "//"; michael@0: aSpec += mFileName; michael@0: } michael@0: michael@0: aSpec += "?size="; michael@0: if (mIconSize >= 0) michael@0: { michael@0: aSpec += kSizeStrings[mIconSize]; michael@0: } michael@0: else michael@0: { michael@0: char buf[20]; michael@0: PR_snprintf(buf, sizeof(buf), "%d", mSize); michael@0: aSpec.Append(buf); michael@0: } michael@0: michael@0: if (mIconState >= 0) { michael@0: aSpec += "&state="; michael@0: aSpec += kStateStrings[mIconState]; michael@0: } michael@0: michael@0: if (!mContentType.IsEmpty()) michael@0: { michael@0: aSpec += "&contentType="; michael@0: aSpec += mContentType.get(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetSpecIgnoringRef(nsACString &result) michael@0: { michael@0: return GetSpec(result); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetHasRef(bool *result) michael@0: { michael@0: *result = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // takes a string like ?size=32&contentType=text/html and returns a new string michael@0: // containing just the attribute value. i.e you could pass in this string with michael@0: // an attribute name of 'size=', this will return 32 michael@0: // Assumption: attribute pairs in the string are separated by '&'. michael@0: void extractAttributeValue(const char * searchString, const char * attributeName, nsCString& result) michael@0: { michael@0: //NS_ENSURE_ARG_POINTER(extractAttributeValue); michael@0: michael@0: result.Truncate(); michael@0: michael@0: if (searchString && attributeName) michael@0: { michael@0: // search the string for attributeName michael@0: uint32_t attributeNameSize = strlen(attributeName); michael@0: const char * startOfAttribute = PL_strcasestr(searchString, attributeName); michael@0: if (startOfAttribute && michael@0: ( *(startOfAttribute-1) == '?' || *(startOfAttribute-1) == '&') ) michael@0: { michael@0: startOfAttribute += attributeNameSize; // skip over the attributeName michael@0: if (*startOfAttribute) // is there something after the attribute name michael@0: { michael@0: const char * endofAttribute = strchr(startOfAttribute, '&'); michael@0: if (endofAttribute) michael@0: result.Assign(Substring(startOfAttribute, endofAttribute)); michael@0: else michael@0: result.Assign(startOfAttribute); michael@0: } // if we have a attribute value michael@0: } // if we have a attribute name michael@0: } // if we got non-null search string and attribute name values michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::SetSpec(const nsACString &aSpec) michael@0: { michael@0: // Reset everything to default values. michael@0: mIconURL = nullptr; michael@0: mSize = DEFAULT_IMAGE_SIZE; michael@0: mContentType.Truncate(); michael@0: mFileName.Truncate(); michael@0: mStockIcon.Truncate(); michael@0: mIconSize = -1; michael@0: mIconState = -1; michael@0: michael@0: nsAutoCString iconSpec(aSpec); michael@0: if (!Substring(iconSpec, 0, MOZICON_SCHEME_LEN).EqualsLiteral(MOZICON_SCHEME)) michael@0: return NS_ERROR_MALFORMED_URI; michael@0: michael@0: int32_t questionMarkPos = iconSpec.Find("?"); michael@0: if (questionMarkPos != -1 && static_cast(iconSpec.Length()) > (questionMarkPos + 1)) michael@0: { michael@0: extractAttributeValue(iconSpec.get(), "contentType=", mContentType); michael@0: michael@0: nsAutoCString sizeString; michael@0: extractAttributeValue(iconSpec.get(), "size=", sizeString); michael@0: if (!sizeString.IsEmpty()) michael@0: { michael@0: const char *sizeStr = sizeString.get(); michael@0: for (uint32_t i = 0; i < ArrayLength(kSizeStrings); i++) michael@0: { michael@0: if (PL_strcasecmp(sizeStr, kSizeStrings[i]) == 0) michael@0: { michael@0: mIconSize = i; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: int32_t sizeValue = atoi(sizeString.get()); michael@0: if (sizeValue) michael@0: mSize = sizeValue; michael@0: } michael@0: michael@0: nsAutoCString stateString; michael@0: extractAttributeValue(iconSpec.get(), "state=", stateString); michael@0: if (!stateString.IsEmpty()) michael@0: { michael@0: const char *stateStr = stateString.get(); michael@0: for (uint32_t i = 0; i < ArrayLength(kStateStrings); i++) michael@0: { michael@0: if (PL_strcasecmp(stateStr, kStateStrings[i]) == 0) michael@0: { michael@0: mIconState = i; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: int32_t pathLength = iconSpec.Length() - MOZICON_SCHEME_LEN; michael@0: if (questionMarkPos != -1) michael@0: pathLength = questionMarkPos - MOZICON_SCHEME_LEN; michael@0: if (pathLength < 3) michael@0: return NS_ERROR_MALFORMED_URI; michael@0: michael@0: nsAutoCString iconPath(Substring(iconSpec, MOZICON_SCHEME_LEN, pathLength)); michael@0: michael@0: // Icon URI path can have three forms: michael@0: // (1) //stock/ michael@0: // (2) // michael@0: // (3) a valid URL michael@0: michael@0: if (!strncmp("//stock/", iconPath.get(), 8)) michael@0: { michael@0: mStockIcon.Assign(Substring(iconPath, 8)); michael@0: // An icon identifier must always be specified. michael@0: if (mStockIcon.IsEmpty()) michael@0: return NS_ERROR_MALFORMED_URI; michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (StringBeginsWith(iconPath, NS_LITERAL_CSTRING("//"))) michael@0: { michael@0: // Sanity check this supposed dummy file name. michael@0: if (iconPath.Length() > SANE_FILE_NAME_LEN) michael@0: return NS_ERROR_MALFORMED_URI; michael@0: iconPath.Cut(0, 2); michael@0: mFileName.Assign(iconPath); michael@0: } michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr uri; michael@0: ioService->NewURI(iconPath, nullptr, nullptr, getter_AddRefs(uri)); michael@0: mIconURL = do_QueryInterface(uri); michael@0: if (mIconURL) michael@0: mFileName.Truncate(); michael@0: else if (mFileName.IsEmpty()) michael@0: return NS_ERROR_MALFORMED_URI; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetPrePath(nsACString &prePath) michael@0: { michael@0: prePath = MOZICON_SCHEME; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetScheme(nsACString &aScheme) michael@0: { michael@0: aScheme = "moz-icon"; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::SetScheme(const nsACString &aScheme) michael@0: { michael@0: // doesn't make sense to set the scheme of a moz-icon URL michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetUsername(nsACString &aUsername) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::SetUsername(const nsACString &aUsername) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetPassword(nsACString &aPassword) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::SetPassword(const nsACString &aPassword) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetUserPass(nsACString &aUserPass) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::SetUserPass(const nsACString &aUserPass) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetHostPort(nsACString &aHostPort) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::SetHostPort(const nsACString &aHostPort) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetHost(nsACString &aHost) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::SetHost(const nsACString &aHost) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetPort(int32_t *aPort) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::SetPort(int32_t aPort) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetPath(nsACString &aPath) michael@0: { michael@0: aPath.Truncate(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::SetPath(const nsACString &aPath) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetRef(nsACString &aRef) michael@0: { michael@0: aRef.Truncate(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::SetRef(const nsACString &aRef) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::Equals(nsIURI *other, bool *result) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(other); michael@0: NS_PRECONDITION(result, "null pointer"); michael@0: michael@0: nsAutoCString spec1; michael@0: nsAutoCString spec2; michael@0: michael@0: other->GetSpec(spec2); michael@0: GetSpec(spec1); michael@0: if (!PL_strcasecmp(spec1.get(), spec2.get())) michael@0: *result = true; michael@0: else michael@0: *result = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::EqualsExceptRef(nsIURI *other, bool *result) michael@0: { michael@0: // GetRef/SetRef not supported by nsMozIconURI, so michael@0: // EqualsExceptRef() is the same as Equals(). michael@0: return Equals(other, result); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::SchemeIs(const char *i_Scheme, bool *o_Equals) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(o_Equals); michael@0: if (!i_Scheme) return NS_ERROR_INVALID_ARG; michael@0: michael@0: *o_Equals = PL_strcasecmp("moz-icon", i_Scheme) ? false : true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::Clone(nsIURI **result) michael@0: { michael@0: nsCOMPtr newIconURL; michael@0: if (mIconURL) michael@0: { michael@0: nsCOMPtr newURI; michael@0: nsresult rv = mIconURL->Clone(getter_AddRefs(newURI)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: newIconURL = do_QueryInterface(newURI, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: michael@0: nsMozIconURI *uri = new nsMozIconURI(); michael@0: newIconURL.swap(uri->mIconURL); michael@0: uri->mSize = mSize; michael@0: uri->mContentType = mContentType; michael@0: uri->mFileName = mFileName; michael@0: uri->mStockIcon = mStockIcon; michael@0: uri->mIconSize = mIconSize; michael@0: uri->mIconState = mIconState; michael@0: NS_ADDREF(*result = uri); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::CloneIgnoringRef(nsIURI **result) michael@0: { michael@0: // GetRef/SetRef not supported by nsMozIconURI, so michael@0: // CloneIgnoringRef() is the same as Clone(). michael@0: return Clone(result); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::Resolve(const nsACString &relativePath, nsACString &result) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetAsciiSpec(nsACString &aSpecA) michael@0: { michael@0: return GetSpec(aSpecA); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetAsciiHost(nsACString &aHostA) michael@0: { michael@0: return GetHost(aHostA); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetOriginCharset(nsACString &result) michael@0: { michael@0: result.Truncate(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // nsIIconUri methods: michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetIconURL(nsIURL* * aFileUrl) michael@0: { michael@0: *aFileUrl = mIconURL; michael@0: NS_IF_ADDREF(*aFileUrl); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::SetIconURL(nsIURL* aFileUrl) michael@0: { michael@0: // this isn't called anywhere, needs to go through SetSpec parsing michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetImageSize(uint32_t * aImageSize) // measured by # of pixels in a row. defaults to 16. michael@0: { michael@0: *aImageSize = mSize; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::SetImageSize(uint32_t aImageSize) // measured by # of pixels in a row. defaults to 16. michael@0: { michael@0: mSize = aImageSize; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetContentType(nsACString &aContentType) michael@0: { michael@0: aContentType = mContentType; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::SetContentType(const nsACString &aContentType) michael@0: { michael@0: mContentType = aContentType; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetFileExtension(nsACString &aFileExtension) michael@0: { michael@0: // First, try to get the extension from mIconURL if we have one michael@0: if (mIconURL) michael@0: { michael@0: nsAutoCString fileExt; michael@0: if (NS_SUCCEEDED(mIconURL->GetFileExtension(fileExt))) michael@0: { michael@0: if (!fileExt.IsEmpty()) michael@0: { michael@0: // unfortunately, this code doesn't give us the required '.' in front of the extension michael@0: // so we have to do it ourselves.. michael@0: aFileExtension.Assign('.'); michael@0: aFileExtension.Append(fileExt); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!mFileName.IsEmpty()) michael@0: { michael@0: // truncate the extension out of the file path... michael@0: const char * chFileName = mFileName.get(); // get the underlying buffer michael@0: const char * fileExt = strrchr(chFileName, '.'); michael@0: if (!fileExt) michael@0: return NS_OK; michael@0: aFileExtension = fileExt; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetStockIcon(nsACString &aStockIcon) michael@0: { michael@0: aStockIcon = mStockIcon; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetIconSize(nsACString &aSize) michael@0: { michael@0: if (mIconSize >= 0) michael@0: aSize = kSizeStrings[mIconSize]; michael@0: else michael@0: aSize.Truncate(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMozIconURI::GetIconState(nsACString &aState) michael@0: { michael@0: if (mIconState >= 0) michael@0: aState = kStateStrings[mIconState]; michael@0: else michael@0: aState.Truncate(); michael@0: return NS_OK; michael@0: } michael@0: ////////////////////////////////////////////////////////////////////////////////