diff options
Diffstat (limited to 'toolkit/mozapps/extensions/AddonPathService.cpp')
-rw-r--r-- | toolkit/mozapps/extensions/AddonPathService.cpp | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/toolkit/mozapps/extensions/AddonPathService.cpp b/toolkit/mozapps/extensions/AddonPathService.cpp new file mode 100644 index 000000000..006149100 --- /dev/null +++ b/toolkit/mozapps/extensions/AddonPathService.cpp @@ -0,0 +1,233 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "AddonPathService.h" + +#include "amIAddonManager.h" +#include "nsIURI.h" +#include "nsXULAppAPI.h" +#include "jsapi.h" +#include "nsServiceManagerUtils.h" +#include "nsLiteralString.h" +#include "nsThreadUtils.h" +#include "nsIIOService.h" +#include "nsNetUtil.h" +#include "nsIFileURL.h" +#include "nsIResProtocolHandler.h" +#include "nsIChromeRegistry.h" +#include "nsIJARURI.h" +#include "nsJSUtils.h" +#include "mozilla/dom/ScriptSettings.h" +#include "mozilla/dom/ToJSValue.h" +#include "mozilla/AddonPathService.h" +#include "mozilla/Omnijar.h" + +#include <algorithm> + +namespace mozilla { + +struct PathEntryComparator +{ + typedef AddonPathService::PathEntry PathEntry; + + bool Equals(const PathEntry& entry1, const PathEntry& entry2) const + { + return entry1.mPath == entry2.mPath; + } + + bool LessThan(const PathEntry& entry1, const PathEntry& entry2) const + { + return entry1.mPath < entry2.mPath; + } +}; + +AddonPathService::AddonPathService() +{ +} + +AddonPathService::~AddonPathService() +{ + sInstance = nullptr; +} + +NS_IMPL_ISUPPORTS(AddonPathService, amIAddonPathService) + +AddonPathService *AddonPathService::sInstance; + +/* static */ AddonPathService* +AddonPathService::GetInstance() +{ + if (!sInstance) { + sInstance = new AddonPathService(); + } + NS_ADDREF(sInstance); + return sInstance; +} + +static JSAddonId* +ConvertAddonId(const nsAString& addonIdString) +{ + AutoSafeJSContext cx; + JS::RootedValue strv(cx); + if (!mozilla::dom::ToJSValue(cx, addonIdString, &strv)) { + return nullptr; + } + JS::RootedString str(cx, strv.toString()); + return JS::NewAddonId(cx, str); +} + +JSAddonId* +AddonPathService::Find(const nsAString& path) +{ + // Use binary search to find the nearest entry that is <= |path|. + PathEntryComparator comparator; + unsigned index = mPaths.IndexOfFirstElementGt(PathEntry(path, nullptr), comparator); + if (index == 0) { + return nullptr; + } + const PathEntry& entry = mPaths[index - 1]; + + // Return the entry's addon if its path is a prefix of |path|. + if (StringBeginsWith(path, entry.mPath)) { + return entry.mAddonId; + } + return nullptr; +} + +NS_IMETHODIMP +AddonPathService::FindAddonId(const nsAString& path, nsAString& addonIdString) +{ + if (JSAddonId* id = Find(path)) { + JSFlatString* flat = JS_ASSERT_STRING_IS_FLAT(JS::StringOfAddonId(id)); + AssignJSFlatString(addonIdString, flat); + } + return NS_OK; +} + +/* static */ JSAddonId* +AddonPathService::FindAddonId(const nsAString& path) +{ + // If no service has been created, then we're not going to find anything. + if (!sInstance) { + return nullptr; + } + + return sInstance->Find(path); +} + +NS_IMETHODIMP +AddonPathService::InsertPath(const nsAString& path, const nsAString& addonIdString) +{ + JSAddonId* addonId = ConvertAddonId(addonIdString); + + // Add the new path in sorted order. + PathEntryComparator comparator; + mPaths.InsertElementSorted(PathEntry(path, addonId), comparator); + return NS_OK; +} + +static nsresult +ResolveURI(nsIURI* aURI, nsAString& out) +{ + bool equals; + nsresult rv; + nsCOMPtr<nsIURI> uri; + nsAutoCString spec; + + // Resolve resource:// URIs. At the end of this if/else block, we + // have both spec and uri variables identifying the same URI. + if (NS_SUCCEEDED(aURI->SchemeIs("resource", &equals)) && equals) { + nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv); + if (NS_WARN_IF(NS_FAILED(rv))) + return rv; + + nsCOMPtr<nsIProtocolHandler> ph; + rv = ioService->GetProtocolHandler("resource", getter_AddRefs(ph)); + if (NS_WARN_IF(NS_FAILED(rv))) + return rv; + + nsCOMPtr<nsIResProtocolHandler> irph(do_QueryInterface(ph, &rv)); + if (NS_WARN_IF(NS_FAILED(rv))) + return rv; + + rv = irph->ResolveURI(aURI, spec); + if (NS_WARN_IF(NS_FAILED(rv))) + return rv; + + rv = ioService->NewURI(spec, nullptr, nullptr, getter_AddRefs(uri)); + if (NS_WARN_IF(NS_FAILED(rv))) + return rv; + } else if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &equals)) && equals) { + nsCOMPtr<nsIChromeRegistry> chromeReg = + mozilla::services::GetChromeRegistryService(); + if (NS_WARN_IF(!chromeReg)) + return NS_ERROR_UNEXPECTED; + + rv = chromeReg->ConvertChromeURL(aURI, getter_AddRefs(uri)); + if (NS_WARN_IF(NS_FAILED(rv))) + return rv; + } else { + uri = aURI; + } + + if (NS_SUCCEEDED(uri->SchemeIs("jar", &equals)) && equals) { + nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) + return rv; + + nsCOMPtr<nsIURI> jarFileURI; + rv = jarURI->GetJARFile(getter_AddRefs(jarFileURI)); + if (NS_WARN_IF(NS_FAILED(rv))) + return rv; + + return ResolveURI(jarFileURI, out); + } + + if (NS_SUCCEEDED(uri->SchemeIs("file", &equals)) && equals) { + nsCOMPtr<nsIFileURL> baseFileURL = do_QueryInterface(uri, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) + return rv; + + nsCOMPtr<nsIFile> file; + rv = baseFileURL->GetFile(getter_AddRefs(file)); + if (NS_WARN_IF(NS_FAILED(rv))) + return rv; + + return file->GetPath(out); + } + return NS_ERROR_FAILURE; +} + +JSAddonId* +MapURIToAddonID(nsIURI* aURI) +{ + if (!NS_IsMainThread() || !XRE_IsParentProcess()) { + return nullptr; + } + + nsAutoString filePath; + nsresult rv = ResolveURI(aURI, filePath); + if (NS_FAILED(rv)) + return nullptr; + + nsCOMPtr<nsIFile> greJar = Omnijar::GetPath(Omnijar::GRE); + nsCOMPtr<nsIFile> appJar = Omnijar::GetPath(Omnijar::APP); + if (greJar && appJar) { + nsAutoString greJarString, appJarString; + if (NS_FAILED(greJar->GetPath(greJarString)) || NS_FAILED(appJar->GetPath(appJarString))) + return nullptr; + + // If |aURI| is part of either Omnijar, then it can't be part of an + // add-on. This catches pretty much all URLs for Firefox content. + if (filePath.Equals(greJarString) || filePath.Equals(appJarString)) + return nullptr; + } + + // If it's not part of Firefox, we resort to binary searching through the + // add-on paths. + return AddonPathService::FindAddonId(filePath); +} + +} |