/* 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/. */ "use strict"; /** * XXX: bug 1221488 is required to make these commands run on the server. * If we want these commands to run on remote devices/connections, they need to * run on the server (runAt=server). Unfortunately, cookie commands not only * need to run on the server, they also need to access to the parent process to * retrieve and manipulate cookies via nsICookieManager2. * However, server-running commands have no way of accessing the parent process * for now. * * So, because these cookie commands, as of today, only run in the developer * toolbar (the gcli command bar), and because this toolbar is only available on * a local Firefox desktop tab (not in webide or the browser toolbox), we can * make the commands run on the client. * This way, they'll always run in the parent process. */ const { Ci, Cc } = require("chrome"); const l10n = require("gcli/l10n"); const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyGetter(this, "cookieMgr", function() { return Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2); }); /** * Check host value and remove port part as it is not used * for storing cookies. * * Parameter will usually be `new URL(context.environment.target.url).host` */ function sanitizeHost(host) { if (host == null || host == "") { throw new Error(l10n.lookup("cookieListOutNonePage")); } return host.split(":")[0]; } /** * The cookie 'expires' value needs converting into something more readable. * * And the unit of expires is sec, the unit that in argument of Date() needs * millisecond. */ function translateExpires(expires) { if (expires == 0) { return l10n.lookup("cookieListOutSession"); } let expires_msec = expires * 1000; return (new Date(expires_msec)).toLocaleString(); } /** * Check if a given cookie matches a given host */ function isCookieAtHost(cookie, host) { if (cookie.host == null) { return host == null; } if (cookie.host.startsWith(".")) { return ("." + host).endsWith(cookie.host); } if (cookie.host === "") { return host.startsWith("file://" + cookie.path); } return cookie.host == host; } exports.items = [ { name: "cookie", description: l10n.lookup("cookieDesc"), manual: l10n.lookup("cookieManual") }, { item: "command", runAt: "client", name: "cookie list", description: l10n.lookup("cookieListDesc"), manual: l10n.lookup("cookieListManual"), returnType: "cookies", exec: function(args, context) { if (context.environment.target.isRemote) { throw new Error("The cookie gcli commands only work in a local tab, " + "see bug 1221488"); } let host = new URL(context.environment.target.url).host; let contentWindow = context.environment.window; host = sanitizeHost(host); let enm = cookieMgr.getCookiesFromHost(host, contentWindow.document. nodePrincipal. originAttributes); let cookies = []; while (enm.hasMoreElements()) { let cookie = enm.getNext().QueryInterface(Ci.nsICookie2); if (isCookieAtHost(cookie, host)) { cookies.push({ host: cookie.host, name: cookie.name, value: cookie.value, path: cookie.path, expires: cookie.expires, isDomain: cookie.isDomain, isHttpOnly: cookie.isHttpOnly, isSecure: cookie.isSecure, isSession: cookie.isSession, }); } } return cookies; } }, { item: "command", runAt: "client", name: "cookie remove", description: l10n.lookup("cookieRemoveDesc"), manual: l10n.lookup("cookieRemoveManual"), params: [ { name: "name", type: "string", description: l10n.lookup("cookieRemoveKeyDesc"), } ], exec: function(args, context) { if (context.environment.target.isRemote) { throw new Error("The cookie gcli commands only work in a local tab, " + "see bug 1221488"); } let host = new URL(context.environment.target.url).host; let contentWindow = context.environment.window; host = sanitizeHost(host); let enm = cookieMgr.getCookiesFromHost(host, contentWindow.document. nodePrincipal. originAttributes); while (enm.hasMoreElements()) { let cookie = enm.getNext().QueryInterface(Ci.nsICookie); if (isCookieAtHost(cookie, host)) { if (cookie.name == args.name) { cookieMgr.remove(cookie.host, cookie.name, cookie.path, false, cookie.originAttributes); } } } } }, { item: "converter", from: "cookies", to: "view", exec: function(cookies, context) { if (cookies.length == 0) { let host = new URL(context.environment.target.url).host; host = sanitizeHost(host); let msg = l10n.lookupFormat("cookieListOutNoneHost", [ host ]); return context.createView({ html: "<span>" + msg + "</span>" }); } for (let cookie of cookies) { cookie.expires = translateExpires(cookie.expires); let noAttrs = !cookie.isDomain && !cookie.isHttpOnly && !cookie.isSecure && !cookie.isSession; cookie.attrs = ((cookie.isDomain ? "isDomain " : "") + (cookie.isHttpOnly ? "isHttpOnly " : "") + (cookie.isSecure ? "isSecure " : "") + (cookie.isSession ? "isSession " : "") + (noAttrs ? l10n.lookup("cookieListOutNone") : "")) .trim(); } return context.createView({ html: "<ul class='gcli-cookielist-list'>" + " <li foreach='cookie in ${cookies}'>" + " <div>${cookie.name}=${cookie.value}</div>" + " <table class='gcli-cookielist-detail'>" + " <tr>" + " <td>" + l10n.lookup("cookieListOutHost") + "</td>" + " <td>${cookie.host}</td>" + " </tr>" + " <tr>" + " <td>" + l10n.lookup("cookieListOutPath") + "</td>" + " <td>${cookie.path}</td>" + " </tr>" + " <tr>" + " <td>" + l10n.lookup("cookieListOutExpires") + "</td>" + " <td>${cookie.expires}</td>" + " </tr>" + " <tr>" + " <td>" + l10n.lookup("cookieListOutAttributes") + "</td>" + " <td>${cookie.attrs}</td>" + " </tr>" + " <tr><td colspan='2'>" + " <span class='gcli-out-shortcut' onclick='${onclick}'" + " data-command='cookie set ${cookie.name} '" + " >" + l10n.lookup("cookieListOutEdit") + "</span>" + " <span class='gcli-out-shortcut'" + " onclick='${onclick}' ondblclick='${ondblclick}'" + " data-command='cookie remove ${cookie.name}'" + " >" + l10n.lookup("cookieListOutRemove") + "</span>" + " </td></tr>" + " </table>" + " </li>" + "</ul>", data: { options: { allowEval: true }, cookies: cookies, onclick: context.update, ondblclick: context.updateExec } }); } }, { item: "command", runAt: "client", name: "cookie set", description: l10n.lookup("cookieSetDesc"), manual: l10n.lookup("cookieSetManual"), params: [ { name: "name", type: "string", description: l10n.lookup("cookieSetKeyDesc") }, { name: "value", type: "string", description: l10n.lookup("cookieSetValueDesc") }, { group: l10n.lookup("cookieSetOptionsDesc"), params: [ { name: "path", type: { name: "string", allowBlank: true }, defaultValue: "/", description: l10n.lookup("cookieSetPathDesc") }, { name: "domain", type: "string", defaultValue: null, description: l10n.lookup("cookieSetDomainDesc") }, { name: "secure", type: "boolean", description: l10n.lookup("cookieSetSecureDesc") }, { name: "httpOnly", type: "boolean", description: l10n.lookup("cookieSetHttpOnlyDesc") }, { name: "session", type: "boolean", description: l10n.lookup("cookieSetSessionDesc") }, { name: "expires", type: "string", defaultValue: "Jan 17, 2038", description: l10n.lookup("cookieSetExpiresDesc") }, ] } ], exec: function(args, context) { if (context.environment.target.isRemote) { throw new Error("The cookie gcli commands only work in a local tab, " + "see bug 1221488"); } let host = new URL(context.environment.target.url).host; host = sanitizeHost(host); let time = Date.parse(args.expires) / 1000; let contentWindow = context.environment.window; cookieMgr.add(args.domain ? "." + args.domain : host, args.path ? args.path : "/", args.name, args.value, args.secure, args.httpOnly, args.session, time, contentWindow.document. nodePrincipal. originAttributes); } } ];