cookie.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. "use strict";
  5. /**
  6. * XXX: bug 1221488 is required to make these commands run on the server.
  7. * If we want these commands to run on remote devices/connections, they need to
  8. * run on the server (runAt=server). Unfortunately, cookie commands not only
  9. * need to run on the server, they also need to access to the parent process to
  10. * retrieve and manipulate cookies via nsICookieManager2.
  11. * However, server-running commands have no way of accessing the parent process
  12. * for now.
  13. *
  14. * So, because these cookie commands, as of today, only run in the developer
  15. * toolbar (the gcli command bar), and because this toolbar is only available on
  16. * a local Firefox desktop tab (not in webide or the browser toolbox), we can
  17. * make the commands run on the client.
  18. * This way, they'll always run in the parent process.
  19. */
  20. const { Ci, Cc } = require("chrome");
  21. const l10n = require("gcli/l10n");
  22. const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
  23. XPCOMUtils.defineLazyGetter(this, "cookieMgr", function() {
  24. return Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
  25. });
  26. /**
  27. * Check host value and remove port part as it is not used
  28. * for storing cookies.
  29. *
  30. * Parameter will usually be `new URL(context.environment.target.url).host`
  31. */
  32. function sanitizeHost(host) {
  33. if (host == null || host == "") {
  34. throw new Error(l10n.lookup("cookieListOutNonePage"));
  35. }
  36. return host.split(":")[0];
  37. }
  38. /**
  39. * The cookie 'expires' value needs converting into something more readable.
  40. *
  41. * And the unit of expires is sec, the unit that in argument of Date() needs
  42. * millisecond.
  43. */
  44. function translateExpires(expires) {
  45. if (expires == 0) {
  46. return l10n.lookup("cookieListOutSession");
  47. }
  48. let expires_msec = expires * 1000;
  49. return (new Date(expires_msec)).toLocaleString();
  50. }
  51. /**
  52. * Check if a given cookie matches a given host
  53. */
  54. function isCookieAtHost(cookie, host) {
  55. if (cookie.host == null) {
  56. return host == null;
  57. }
  58. if (cookie.host.startsWith(".")) {
  59. return ("." + host).endsWith(cookie.host);
  60. }
  61. if (cookie.host === "") {
  62. return host.startsWith("file://" + cookie.path);
  63. }
  64. return cookie.host == host;
  65. }
  66. exports.items = [
  67. {
  68. name: "cookie",
  69. description: l10n.lookup("cookieDesc"),
  70. manual: l10n.lookup("cookieManual")
  71. },
  72. {
  73. item: "command",
  74. runAt: "client",
  75. name: "cookie list",
  76. description: l10n.lookup("cookieListDesc"),
  77. manual: l10n.lookup("cookieListManual"),
  78. returnType: "cookies",
  79. exec: function(args, context) {
  80. if (context.environment.target.isRemote) {
  81. throw new Error("The cookie gcli commands only work in a local tab, " +
  82. "see bug 1221488");
  83. }
  84. let host = new URL(context.environment.target.url).host;
  85. let contentWindow = context.environment.window;
  86. host = sanitizeHost(host);
  87. let enm = cookieMgr.getCookiesFromHost(host, contentWindow.document.
  88. nodePrincipal.
  89. originAttributes);
  90. let cookies = [];
  91. while (enm.hasMoreElements()) {
  92. let cookie = enm.getNext().QueryInterface(Ci.nsICookie2);
  93. if (isCookieAtHost(cookie, host)) {
  94. cookies.push({
  95. host: cookie.host,
  96. name: cookie.name,
  97. value: cookie.value,
  98. path: cookie.path,
  99. expires: cookie.expires,
  100. isDomain: cookie.isDomain,
  101. isHttpOnly: cookie.isHttpOnly,
  102. isSecure: cookie.isSecure,
  103. isSession: cookie.isSession,
  104. });
  105. }
  106. }
  107. return cookies;
  108. }
  109. },
  110. {
  111. item: "command",
  112. runAt: "client",
  113. name: "cookie remove",
  114. description: l10n.lookup("cookieRemoveDesc"),
  115. manual: l10n.lookup("cookieRemoveManual"),
  116. params: [
  117. {
  118. name: "name",
  119. type: "string",
  120. description: l10n.lookup("cookieRemoveKeyDesc"),
  121. }
  122. ],
  123. exec: function(args, context) {
  124. if (context.environment.target.isRemote) {
  125. throw new Error("The cookie gcli commands only work in a local tab, " +
  126. "see bug 1221488");
  127. }
  128. let host = new URL(context.environment.target.url).host;
  129. let contentWindow = context.environment.window;
  130. host = sanitizeHost(host);
  131. let enm = cookieMgr.getCookiesFromHost(host, contentWindow.document.
  132. nodePrincipal.
  133. originAttributes);
  134. while (enm.hasMoreElements()) {
  135. let cookie = enm.getNext().QueryInterface(Ci.nsICookie);
  136. if (isCookieAtHost(cookie, host)) {
  137. if (cookie.name == args.name) {
  138. cookieMgr.remove(cookie.host, cookie.name, cookie.path,
  139. false, cookie.originAttributes);
  140. }
  141. }
  142. }
  143. }
  144. },
  145. {
  146. item: "converter",
  147. from: "cookies",
  148. to: "view",
  149. exec: function(cookies, context) {
  150. if (cookies.length == 0) {
  151. let host = new URL(context.environment.target.url).host;
  152. host = sanitizeHost(host);
  153. let msg = l10n.lookupFormat("cookieListOutNoneHost", [ host ]);
  154. return context.createView({ html: "<span>" + msg + "</span>" });
  155. }
  156. for (let cookie of cookies) {
  157. cookie.expires = translateExpires(cookie.expires);
  158. let noAttrs = !cookie.isDomain && !cookie.isHttpOnly &&
  159. !cookie.isSecure && !cookie.isSession;
  160. cookie.attrs = ((cookie.isDomain ? "isDomain " : "") +
  161. (cookie.isHttpOnly ? "isHttpOnly " : "") +
  162. (cookie.isSecure ? "isSecure " : "") +
  163. (cookie.isSession ? "isSession " : "") +
  164. (noAttrs ? l10n.lookup("cookieListOutNone") : ""))
  165. .trim();
  166. }
  167. return context.createView({
  168. html:
  169. "<ul class='gcli-cookielist-list'>" +
  170. " <li foreach='cookie in ${cookies}'>" +
  171. " <div>${cookie.name}=${cookie.value}</div>" +
  172. " <table class='gcli-cookielist-detail'>" +
  173. " <tr>" +
  174. " <td>" + l10n.lookup("cookieListOutHost") + "</td>" +
  175. " <td>${cookie.host}</td>" +
  176. " </tr>" +
  177. " <tr>" +
  178. " <td>" + l10n.lookup("cookieListOutPath") + "</td>" +
  179. " <td>${cookie.path}</td>" +
  180. " </tr>" +
  181. " <tr>" +
  182. " <td>" + l10n.lookup("cookieListOutExpires") + "</td>" +
  183. " <td>${cookie.expires}</td>" +
  184. " </tr>" +
  185. " <tr>" +
  186. " <td>" + l10n.lookup("cookieListOutAttributes") + "</td>" +
  187. " <td>${cookie.attrs}</td>" +
  188. " </tr>" +
  189. " <tr><td colspan='2'>" +
  190. " <span class='gcli-out-shortcut' onclick='${onclick}'" +
  191. " data-command='cookie set ${cookie.name} '" +
  192. " >" + l10n.lookup("cookieListOutEdit") + "</span>" +
  193. " <span class='gcli-out-shortcut'" +
  194. " onclick='${onclick}' ondblclick='${ondblclick}'" +
  195. " data-command='cookie remove ${cookie.name}'" +
  196. " >" + l10n.lookup("cookieListOutRemove") + "</span>" +
  197. " </td></tr>" +
  198. " </table>" +
  199. " </li>" +
  200. "</ul>",
  201. data: {
  202. options: { allowEval: true },
  203. cookies: cookies,
  204. onclick: context.update,
  205. ondblclick: context.updateExec
  206. }
  207. });
  208. }
  209. },
  210. {
  211. item: "command",
  212. runAt: "client",
  213. name: "cookie set",
  214. description: l10n.lookup("cookieSetDesc"),
  215. manual: l10n.lookup("cookieSetManual"),
  216. params: [
  217. {
  218. name: "name",
  219. type: "string",
  220. description: l10n.lookup("cookieSetKeyDesc")
  221. },
  222. {
  223. name: "value",
  224. type: "string",
  225. description: l10n.lookup("cookieSetValueDesc")
  226. },
  227. {
  228. group: l10n.lookup("cookieSetOptionsDesc"),
  229. params: [
  230. {
  231. name: "path",
  232. type: { name: "string", allowBlank: true },
  233. defaultValue: "/",
  234. description: l10n.lookup("cookieSetPathDesc")
  235. },
  236. {
  237. name: "domain",
  238. type: "string",
  239. defaultValue: null,
  240. description: l10n.lookup("cookieSetDomainDesc")
  241. },
  242. {
  243. name: "secure",
  244. type: "boolean",
  245. description: l10n.lookup("cookieSetSecureDesc")
  246. },
  247. {
  248. name: "httpOnly",
  249. type: "boolean",
  250. description: l10n.lookup("cookieSetHttpOnlyDesc")
  251. },
  252. {
  253. name: "session",
  254. type: "boolean",
  255. description: l10n.lookup("cookieSetSessionDesc")
  256. },
  257. {
  258. name: "expires",
  259. type: "string",
  260. defaultValue: "Jan 17, 2038",
  261. description: l10n.lookup("cookieSetExpiresDesc")
  262. },
  263. ]
  264. }
  265. ],
  266. exec: function(args, context) {
  267. if (context.environment.target.isRemote) {
  268. throw new Error("The cookie gcli commands only work in a local tab, " +
  269. "see bug 1221488");
  270. }
  271. let host = new URL(context.environment.target.url).host;
  272. host = sanitizeHost(host);
  273. let time = Date.parse(args.expires) / 1000;
  274. let contentWindow = context.environment.window;
  275. cookieMgr.add(args.domain ? "." + args.domain : host,
  276. args.path ? args.path : "/",
  277. args.name,
  278. args.value,
  279. args.secure,
  280. args.httpOnly,
  281. args.session,
  282. time,
  283. contentWindow.document.
  284. nodePrincipal.
  285. originAttributes);
  286. }
  287. }
  288. ];