Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "AddonPathService.h"
7 :
8 : #include "amIAddonManager.h"
9 : #include "nsIURI.h"
10 : #include "nsXULAppAPI.h"
11 : #include "jsapi.h"
12 : #include "nsServiceManagerUtils.h"
13 : #include "nsLiteralString.h"
14 : #include "nsThreadUtils.h"
15 : #include "nsIIOService.h"
16 : #include "nsNetUtil.h"
17 : #include "nsIAddonPolicyService.h"
18 : #include "nsIFileURL.h"
19 : #include "nsIResProtocolHandler.h"
20 : #include "nsIChromeRegistry.h"
21 : #include "nsIJARURI.h"
22 : #include "nsJSUtils.h"
23 : #include "mozilla/dom/ScriptSettings.h"
24 : #include "mozilla/dom/ToJSValue.h"
25 : #include "mozilla/AddonPathService.h"
26 : #include "mozilla/Omnijar.h"
27 :
28 : #include <algorithm>
29 :
30 : namespace mozilla {
31 :
32 : struct PathEntryComparator
33 : {
34 : typedef AddonPathService::PathEntry PathEntry;
35 :
36 247 : bool Equals(const PathEntry& entry1, const PathEntry& entry2) const
37 : {
38 247 : return entry1.mPath == entry2.mPath;
39 : }
40 :
41 657 : bool LessThan(const PathEntry& entry1, const PathEntry& entry2) const
42 : {
43 657 : return entry1.mPath < entry2.mPath;
44 : }
45 : };
46 :
47 1 : AddonPathService::AddonPathService()
48 : {
49 1 : }
50 :
51 0 : AddonPathService::~AddonPathService()
52 : {
53 0 : sInstance = nullptr;
54 0 : }
55 :
56 48 : NS_IMPL_ISUPPORTS(AddonPathService, amIAddonPathService)
57 :
58 : AddonPathService *AddonPathService::sInstance;
59 :
60 : /* static */ AddonPathService*
61 1 : AddonPathService::GetInstance()
62 : {
63 1 : if (!sInstance) {
64 1 : sInstance = new AddonPathService();
65 : }
66 1 : NS_ADDREF(sInstance);
67 1 : return sInstance;
68 : }
69 :
70 : static JSAddonId*
71 17 : ConvertAddonId(const nsAString& addonIdString)
72 : {
73 34 : AutoSafeJSContext cx;
74 34 : JS::RootedValue strv(cx);
75 17 : if (!mozilla::dom::ToJSValue(cx, addonIdString, &strv)) {
76 0 : return nullptr;
77 : }
78 34 : JS::RootedString str(cx, strv.toString());
79 17 : return JS::NewAddonId(cx, str);
80 : }
81 :
82 : JSAddonId*
83 147 : AddonPathService::Find(const nsAString& path)
84 : {
85 : // Use binary search to find the nearest entry that is <= |path|.
86 : PathEntryComparator comparator;
87 147 : unsigned index = mPaths.IndexOfFirstElementGt(PathEntry(path, nullptr), comparator);
88 147 : if (index == 0) {
89 12 : return nullptr;
90 : }
91 135 : const PathEntry& entry = mPaths[index - 1];
92 :
93 : // Return the entry's addon if its path is a prefix of |path|.
94 135 : if (StringBeginsWith(path, entry.mPath)) {
95 15 : return entry.mAddonId;
96 : }
97 120 : return nullptr;
98 : }
99 :
100 : NS_IMETHODIMP
101 0 : AddonPathService::FindAddonId(const nsAString& path, nsAString& addonIdString)
102 : {
103 0 : if (JSAddonId* id = Find(path)) {
104 0 : JSFlatString* flat = JS_ASSERT_STRING_IS_FLAT(JS::StringOfAddonId(id));
105 0 : AssignJSFlatString(addonIdString, flat);
106 : }
107 0 : return NS_OK;
108 : }
109 :
110 : /* static */ JSAddonId*
111 185 : AddonPathService::FindAddonId(const nsAString& path)
112 : {
113 : // If no service has been created, then we're not going to find anything.
114 185 : if (!sInstance) {
115 38 : return nullptr;
116 : }
117 :
118 147 : return sInstance->Find(path);
119 : }
120 :
121 : NS_IMETHODIMP
122 17 : AddonPathService::InsertPath(const nsAString& path, const nsAString& addonIdString)
123 : {
124 17 : JSAddonId* addonId = ConvertAddonId(addonIdString);
125 :
126 : // Add the new path in sorted order.
127 : PathEntryComparator comparator;
128 17 : mPaths.InsertElementSorted(PathEntry(path, addonId), comparator);
129 17 : return NS_OK;
130 : }
131 :
132 : NS_IMETHODIMP
133 0 : AddonPathService::MapURIToAddonId(nsIURI* aURI, nsAString& addonIdString)
134 : {
135 0 : if (JSAddonId* id = MapURIToAddonID(aURI)) {
136 0 : JSFlatString* flat = JS_ASSERT_STRING_IS_FLAT(JS::StringOfAddonId(id));
137 0 : AssignJSFlatString(addonIdString, flat);
138 : }
139 0 : return NS_OK;
140 : }
141 :
142 : static nsresult
143 464 : ResolveURI(nsIURI* aURI, nsAString& out)
144 : {
145 : bool equals;
146 : nsresult rv;
147 928 : nsCOMPtr<nsIURI> uri;
148 928 : nsAutoCString spec;
149 :
150 : // Resolve resource:// URIs. At the end of this if/else block, we
151 : // have both spec and uri variables identifying the same URI.
152 464 : if (NS_SUCCEEDED(aURI->SchemeIs("resource", &equals)) && equals) {
153 278 : nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
154 139 : if (NS_WARN_IF(NS_FAILED(rv)))
155 0 : return rv;
156 :
157 278 : nsCOMPtr<nsIProtocolHandler> ph;
158 139 : rv = ioService->GetProtocolHandler("resource", getter_AddRefs(ph));
159 139 : if (NS_WARN_IF(NS_FAILED(rv)))
160 0 : return rv;
161 :
162 278 : nsCOMPtr<nsIResProtocolHandler> irph(do_QueryInterface(ph, &rv));
163 139 : if (NS_WARN_IF(NS_FAILED(rv)))
164 0 : return rv;
165 :
166 139 : rv = irph->ResolveURI(aURI, spec);
167 139 : if (NS_WARN_IF(NS_FAILED(rv)))
168 0 : return rv;
169 :
170 139 : rv = ioService->NewURI(spec, nullptr, nullptr, getter_AddRefs(uri));
171 139 : if (NS_WARN_IF(NS_FAILED(rv)))
172 0 : return rv;
173 325 : } else if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &equals)) && equals) {
174 : // Going through the Chrome Registry may be prohibitively slow for many of
175 : // the well-known chrome:// URI packages, so check for a few of them here
176 : // first in order to fail early if we don't have a chrome:// URI which
177 : // could have been provided by an add-on.
178 286 : nsAutoCString package;
179 281 : rv = aURI->GetHostPort(package);
180 843 : if (NS_WARN_IF(NS_FAILED(rv)) ||
181 562 : package.EqualsLiteral("branding") ||
182 429 : package.EqualsLiteral("browser") ||
183 296 : package.EqualsLiteral("branding") ||
184 153 : package.EqualsLiteral("global") ||
185 10 : package.EqualsLiteral("global-platform") ||
186 10 : package.EqualsLiteral("mozapps") ||
187 10 : package.EqualsLiteral("necko") ||
188 10 : package.EqualsLiteral("passwordmgr") ||
189 291 : package.EqualsLiteral("pippki") ||
190 5 : package.EqualsLiteral("pipnss")) {
191 : // Returning a failure code means the URI isn't associated with an add-on
192 : // ID.
193 276 : return NS_ERROR_FAILURE;
194 : }
195 :
196 : nsCOMPtr<nsIChromeRegistry> chromeReg =
197 10 : mozilla::services::GetChromeRegistryService();
198 5 : if (NS_WARN_IF(!chromeReg))
199 0 : return NS_ERROR_UNEXPECTED;
200 :
201 5 : rv = chromeReg->ConvertChromeURL(aURI, getter_AddRefs(uri));
202 5 : if (NS_WARN_IF(NS_FAILED(rv)))
203 0 : return rv;
204 : } else {
205 44 : uri = aURI;
206 : }
207 :
208 188 : if (NS_SUCCEEDED(uri->SchemeIs("jar", &equals)) && equals) {
209 0 : nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri, &rv);
210 0 : if (NS_WARN_IF(NS_FAILED(rv)))
211 0 : return rv;
212 :
213 0 : nsCOMPtr<nsIURI> jarFileURI;
214 0 : rv = jarURI->GetJARFile(getter_AddRefs(jarFileURI));
215 0 : if (NS_WARN_IF(NS_FAILED(rv)))
216 0 : return rv;
217 :
218 0 : return ResolveURI(jarFileURI, out);
219 : }
220 :
221 188 : if (NS_SUCCEEDED(uri->SchemeIs("file", &equals)) && equals) {
222 370 : nsCOMPtr<nsIFileURL> baseFileURL = do_QueryInterface(uri, &rv);
223 185 : if (NS_WARN_IF(NS_FAILED(rv)))
224 0 : return rv;
225 :
226 370 : nsCOMPtr<nsIFile> file;
227 185 : rv = baseFileURL->GetFile(getter_AddRefs(file));
228 185 : if (NS_WARN_IF(NS_FAILED(rv)))
229 0 : return rv;
230 :
231 185 : return file->GetPath(out);
232 : }
233 3 : return NS_ERROR_FAILURE;
234 : }
235 :
236 : JSAddonId*
237 533 : MapURIToAddonID(nsIURI* aURI)
238 : {
239 533 : if (!NS_IsMainThread() || !XRE_IsParentProcess()) {
240 69 : return nullptr;
241 : }
242 :
243 : bool equals;
244 : nsresult rv;
245 464 : if (NS_SUCCEEDED(aURI->SchemeIs("moz-extension", &equals)) && equals) {
246 0 : nsCOMPtr<nsIAddonPolicyService> service = do_GetService("@mozilla.org/addons/policy-service;1");
247 0 : if (service) {
248 0 : nsString addonId;
249 0 : rv = service->ExtensionURIToAddonId(aURI, addonId);
250 0 : if (NS_FAILED(rv))
251 0 : return nullptr;
252 :
253 0 : return ConvertAddonId(addonId);
254 : }
255 : }
256 :
257 928 : nsAutoString filePath;
258 464 : rv = ResolveURI(aURI, filePath);
259 464 : if (NS_FAILED(rv))
260 279 : return nullptr;
261 :
262 370 : nsCOMPtr<nsIFile> greJar = Omnijar::GetPath(Omnijar::GRE);
263 370 : nsCOMPtr<nsIFile> appJar = Omnijar::GetPath(Omnijar::APP);
264 185 : if (greJar && appJar) {
265 0 : nsAutoString greJarString, appJarString;
266 0 : if (NS_FAILED(greJar->GetPath(greJarString)) || NS_FAILED(appJar->GetPath(appJarString)))
267 0 : return nullptr;
268 :
269 : // If |aURI| is part of either Omnijar, then it can't be part of an
270 : // add-on. This catches pretty much all URLs for Firefox content.
271 0 : if (filePath.Equals(greJarString) || filePath.Equals(appJarString))
272 0 : return nullptr;
273 : }
274 :
275 : // If it's not part of Firefox, we resort to binary searching through the
276 : // add-on paths.
277 185 : return AddonPathService::FindAddonId(filePath);
278 : }
279 :
280 : } // namespace mozilla
|