/extensions/permissions/nsContentBlocker.cpp

http://github.com/zpao/v8monkey · C++ · 377 lines · 245 code · 51 blank · 81 comment · 34 complexity · 4b6eefcfe3cf25171dba03efc165bd94 MD5 · raw file

  1. /* ***** BEGIN LICENSE BLOCK *****
  2. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3. *
  4. * The contents of this file are subject to the Mozilla Public License Version
  5. * 1.1 (the "License"); you may not use this file except in compliance with
  6. * the License. You may obtain a copy of the License at
  7. * http://www.mozilla.org/MPL/
  8. *
  9. * Software distributed under the License is distributed on an "AS IS" basis,
  10. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11. * for the specific language governing rights and limitations under the
  12. * License.
  13. *
  14. * The Original Code is content blocker code.
  15. *
  16. * The Initial Developer of the Original Code is
  17. * Michiel van Leeuwen <mvl@exedo.nl>.
  18. * Portions created by the Initial Developer are Copyright (C) 2004
  19. * the Initial Developer. All Rights Reserved.
  20. *
  21. * Contributor(s):
  22. *
  23. * Alternatively, the contents of this file may be used under the terms of
  24. * either the GNU General Public License Version 2 or later (the "GPL"), or
  25. * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  26. * in which case the provisions of the GPL or the LGPL are applicable instead
  27. * of those above. If you wish to allow use of your version of this file only
  28. * under the terms of either the GPL or the LGPL, and not to allow others to
  29. * use your version of this file under the terms of the MPL, indicate your
  30. * decision by deleting the provisions above and replace them with the notice
  31. * and other provisions required by the GPL or the LGPL. If you do not delete
  32. * the provisions above, a recipient may use your version of this file under
  33. * the terms of any one of the MPL, the GPL or the LGPL.
  34. *
  35. * ***** END LICENSE BLOCK ***** */
  36. #include "nsContentBlocker.h"
  37. #include "nsIDocument.h"
  38. #include "nsIContent.h"
  39. #include "nsIURI.h"
  40. #include "nsIServiceManager.h"
  41. #include "nsIDocShellTreeItem.h"
  42. #include "nsIPrefService.h"
  43. #include "nsIPrefBranch.h"
  44. #include "nsIDocShell.h"
  45. #include "nsString.h"
  46. #include "nsContentPolicyUtils.h"
  47. #include "nsIObjectLoadingContent.h"
  48. // Possible behavior pref values
  49. // Those map to the nsIPermissionManager values where possible
  50. #define BEHAVIOR_ACCEPT nsIPermissionManager::ALLOW_ACTION
  51. #define BEHAVIOR_REJECT nsIPermissionManager::DENY_ACTION
  52. #define BEHAVIOR_NOFOREIGN 3
  53. // From nsIContentPolicy
  54. static const char *kTypeString[] = {"other",
  55. "script",
  56. "image",
  57. "stylesheet",
  58. "object",
  59. "document",
  60. "subdocument",
  61. "refresh",
  62. "xbl",
  63. "ping",
  64. "xmlhttprequest",
  65. "objectsubrequest",
  66. "dtd",
  67. "font",
  68. "media",
  69. "websocket"};
  70. #define NUMBER_OF_TYPES NS_ARRAY_LENGTH(kTypeString)
  71. PRUint8 nsContentBlocker::mBehaviorPref[NUMBER_OF_TYPES];
  72. NS_IMPL_ISUPPORTS3(nsContentBlocker,
  73. nsIContentPolicy,
  74. nsIObserver,
  75. nsSupportsWeakReference)
  76. nsContentBlocker::nsContentBlocker()
  77. {
  78. memset(mBehaviorPref, BEHAVIOR_ACCEPT, NUMBER_OF_TYPES);
  79. }
  80. nsresult
  81. nsContentBlocker::Init()
  82. {
  83. nsresult rv;
  84. mPermissionManager = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
  85. NS_ENSURE_SUCCESS(rv, rv);
  86. nsCOMPtr<nsIPrefService> prefService = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
  87. NS_ENSURE_SUCCESS(rv, rv);
  88. nsCOMPtr<nsIPrefBranch> prefBranch;
  89. rv = prefService->GetBranch("permissions.default.", getter_AddRefs(prefBranch));
  90. NS_ENSURE_SUCCESS(rv, rv);
  91. // Migrate old image blocker pref
  92. nsCOMPtr<nsIPrefBranch> oldPrefBranch;
  93. oldPrefBranch = do_QueryInterface(prefService);
  94. PRInt32 oldPref;
  95. rv = oldPrefBranch->GetIntPref("network.image.imageBehavior", &oldPref);
  96. if (NS_SUCCEEDED(rv) && oldPref) {
  97. PRInt32 newPref;
  98. switch (oldPref) {
  99. default:
  100. newPref = BEHAVIOR_ACCEPT;
  101. break;
  102. case 1:
  103. newPref = BEHAVIOR_NOFOREIGN;
  104. break;
  105. case 2:
  106. newPref = BEHAVIOR_REJECT;
  107. break;
  108. }
  109. prefBranch->SetIntPref("image", newPref);
  110. oldPrefBranch->ClearUserPref("network.image.imageBehavior");
  111. }
  112. // The branch is not a copy of the prefservice, but a new object, because
  113. // it is a non-default branch. Adding obeservers to it will only work if
  114. // we make sure that the object doesn't die. So, keep a reference to it.
  115. mPrefBranchInternal = do_QueryInterface(prefBranch, &rv);
  116. NS_ENSURE_SUCCESS(rv, rv);
  117. rv = mPrefBranchInternal->AddObserver("", this, true);
  118. PrefChanged(prefBranch, nsnull);
  119. return rv;
  120. }
  121. #undef LIMIT
  122. #define LIMIT(x, low, high, default) ((x) >= (low) && (x) <= (high) ? (x) : (default))
  123. void
  124. nsContentBlocker::PrefChanged(nsIPrefBranch *aPrefBranch,
  125. const char *aPref)
  126. {
  127. PRInt32 val;
  128. #define PREF_CHANGED(_P) (!aPref || !strcmp(aPref, _P))
  129. for(PRUint32 i = 0; i < NUMBER_OF_TYPES; ++i) {
  130. if (PREF_CHANGED(kTypeString[i]) &&
  131. NS_SUCCEEDED(aPrefBranch->GetIntPref(kTypeString[i], &val)))
  132. mBehaviorPref[i] = LIMIT(val, 1, 3, 1);
  133. }
  134. }
  135. // nsIContentPolicy Implementation
  136. NS_IMETHODIMP
  137. nsContentBlocker::ShouldLoad(PRUint32 aContentType,
  138. nsIURI *aContentLocation,
  139. nsIURI *aRequestingLocation,
  140. nsISupports *aRequestingContext,
  141. const nsACString &aMimeGuess,
  142. nsISupports *aExtra,
  143. PRInt16 *aDecision)
  144. {
  145. *aDecision = nsIContentPolicy::ACCEPT;
  146. nsresult rv;
  147. // Ony support NUMBER_OF_TYPES content types. that all there is at the
  148. // moment, but you never know...
  149. if (aContentType > NUMBER_OF_TYPES)
  150. return NS_OK;
  151. // we can't do anything without this
  152. if (!aContentLocation)
  153. return NS_OK;
  154. // we only want to check http, https, ftp
  155. // for chrome:// and resources and others, no need to check.
  156. nsCAutoString scheme;
  157. aContentLocation->GetScheme(scheme);
  158. if (!scheme.LowerCaseEqualsLiteral("ftp") &&
  159. !scheme.LowerCaseEqualsLiteral("http") &&
  160. !scheme.LowerCaseEqualsLiteral("https"))
  161. return NS_OK;
  162. bool shouldLoad, fromPrefs;
  163. rv = TestPermission(aContentLocation, aRequestingLocation, aContentType,
  164. &shouldLoad, &fromPrefs);
  165. NS_ENSURE_SUCCESS(rv, rv);
  166. if (!shouldLoad) {
  167. if (fromPrefs) {
  168. *aDecision = nsIContentPolicy::REJECT_TYPE;
  169. } else {
  170. *aDecision = nsIContentPolicy::REJECT_SERVER;
  171. }
  172. }
  173. if (aContentType != nsIContentPolicy::TYPE_OBJECT || aMimeGuess.IsEmpty())
  174. return NS_OK;
  175. // For TYPE_OBJECT we should check what aMimeGuess might tell us
  176. // about what sort of object it is.
  177. nsCOMPtr<nsIObjectLoadingContent> objectLoader =
  178. do_QueryInterface(aRequestingContext);
  179. if (!objectLoader)
  180. return NS_OK;
  181. PRUint32 contentType;
  182. rv = objectLoader->GetContentTypeForMIMEType(aMimeGuess, &contentType);
  183. if (NS_FAILED(rv))
  184. return rv;
  185. switch (contentType) {
  186. case nsIObjectLoadingContent::TYPE_IMAGE:
  187. aContentType = nsIContentPolicy::TYPE_IMAGE;
  188. break;
  189. case nsIObjectLoadingContent::TYPE_DOCUMENT:
  190. aContentType = nsIContentPolicy::TYPE_SUBDOCUMENT;
  191. break;
  192. default:
  193. return NS_OK;
  194. }
  195. NS_ASSERTION(aContentType != nsIContentPolicy::TYPE_OBJECT,
  196. "Shouldn't happen. Infinite loops are bad!");
  197. // Found a type that tells us more about what we're loading. Try
  198. // the permissions check again!
  199. return ShouldLoad(aContentType, aContentLocation, aRequestingLocation,
  200. aRequestingContext, aMimeGuess, aExtra, aDecision);
  201. }
  202. NS_IMETHODIMP
  203. nsContentBlocker::ShouldProcess(PRUint32 aContentType,
  204. nsIURI *aContentLocation,
  205. nsIURI *aRequestingLocation,
  206. nsISupports *aRequestingContext,
  207. const nsACString &aMimeGuess,
  208. nsISupports *aExtra,
  209. PRInt16 *aDecision)
  210. {
  211. // For loads where aRequestingContext is chrome, we should just
  212. // accept. Those are most likely toplevel loads in windows, and
  213. // chrome generally knows what it's doing anyway.
  214. nsCOMPtr<nsIDocShellTreeItem> item =
  215. do_QueryInterface(NS_CP_GetDocShellFromContext(aRequestingContext));
  216. if (item) {
  217. PRInt32 type;
  218. item->GetItemType(&type);
  219. if (type == nsIDocShellTreeItem::typeChrome) {
  220. *aDecision = nsIContentPolicy::ACCEPT;
  221. return NS_OK;
  222. }
  223. }
  224. // This isn't a load from chrome. Just do a ShouldLoad() check --
  225. // we want the same answer here
  226. return ShouldLoad(aContentType, aContentLocation, aRequestingLocation,
  227. aRequestingContext, aMimeGuess, aExtra, aDecision);
  228. }
  229. nsresult
  230. nsContentBlocker::TestPermission(nsIURI *aCurrentURI,
  231. nsIURI *aFirstURI,
  232. PRInt32 aContentType,
  233. bool *aPermission,
  234. bool *aFromPrefs)
  235. {
  236. *aFromPrefs = false;
  237. // This default will also get used if there is an unknown value in the
  238. // permission list, or if the permission manager returns unknown values.
  239. *aPermission = true;
  240. // check the permission list first; if we find an entry, it overrides
  241. // default prefs.
  242. // Don't forget the aContentType ranges from 1..8, while the
  243. // array is indexed 0..7
  244. PRUint32 permission;
  245. nsresult rv = mPermissionManager->TestPermission(aCurrentURI,
  246. kTypeString[aContentType - 1],
  247. &permission);
  248. NS_ENSURE_SUCCESS(rv, rv);
  249. // If there is nothing on the list, use the default.
  250. if (!permission) {
  251. permission = mBehaviorPref[aContentType - 1];
  252. *aFromPrefs = true;
  253. }
  254. // Use the fact that the nsIPermissionManager values map to
  255. // the BEHAVIOR_* values above.
  256. switch (permission) {
  257. case BEHAVIOR_ACCEPT:
  258. *aPermission = true;
  259. break;
  260. case BEHAVIOR_REJECT:
  261. *aPermission = false;
  262. break;
  263. case BEHAVIOR_NOFOREIGN:
  264. // Third party checking
  265. // Need a requesting uri for third party checks to work.
  266. if (!aFirstURI)
  267. return NS_OK;
  268. bool trustedSource = false;
  269. rv = aFirstURI->SchemeIs("chrome", &trustedSource);
  270. NS_ENSURE_SUCCESS(rv,rv);
  271. if (!trustedSource) {
  272. rv = aFirstURI->SchemeIs("resource", &trustedSource);
  273. NS_ENSURE_SUCCESS(rv,rv);
  274. }
  275. if (trustedSource)
  276. return NS_OK;
  277. // compare tails of names checking to see if they have a common domain
  278. // we do this by comparing the tails of both names where each tail
  279. // includes at least one dot
  280. // A more generic method somewhere would be nice
  281. nsCAutoString currentHost;
  282. rv = aCurrentURI->GetAsciiHost(currentHost);
  283. NS_ENSURE_SUCCESS(rv, rv);
  284. // Search for two dots, starting at the end.
  285. // If there are no two dots found, ++dot will turn to zero,
  286. // that will return the entire string.
  287. PRInt32 dot = currentHost.RFindChar('.');
  288. dot = currentHost.RFindChar('.', dot-1);
  289. ++dot;
  290. // Get the domain, ie the last part of the host (www.domain.com -> domain.com)
  291. // This will break on co.uk
  292. const nsCSubstring &tail =
  293. Substring(currentHost, dot, currentHost.Length() - dot);
  294. nsCAutoString firstHost;
  295. rv = aFirstURI->GetAsciiHost(firstHost);
  296. NS_ENSURE_SUCCESS(rv, rv);
  297. // If the tail is longer then the whole firstHost, it will never match
  298. if (firstHost.Length() < tail.Length()) {
  299. *aPermission = false;
  300. return NS_OK;
  301. }
  302. // Get the last part of the firstUri with the same length as |tail|
  303. const nsCSubstring &firstTail =
  304. Substring(firstHost, firstHost.Length() - tail.Length(), tail.Length());
  305. // Check that both tails are the same, and that just before the tail in
  306. // |firstUri| there is a dot. That means both url are in the same domain
  307. if ((firstHost.Length() > tail.Length() &&
  308. firstHost.CharAt(firstHost.Length() - tail.Length() - 1) != '.') ||
  309. !tail.Equals(firstTail)) {
  310. *aPermission = false;
  311. }
  312. break;
  313. }
  314. return NS_OK;
  315. }
  316. NS_IMETHODIMP
  317. nsContentBlocker::Observe(nsISupports *aSubject,
  318. const char *aTopic,
  319. const PRUnichar *aData)
  320. {
  321. NS_ASSERTION(!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic),
  322. "unexpected topic - we only deal with pref changes!");
  323. if (mPrefBranchInternal)
  324. PrefChanged(mPrefBranchInternal, NS_LossyConvertUTF16toASCII(aData).get());
  325. return NS_OK;
  326. }