michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: import os michael@0: import xml.dom.minidom michael@0: import StringIO michael@0: michael@0: RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#" michael@0: EM_NS = "http://www.mozilla.org/2004/em-rdf#" michael@0: michael@0: class RDF(object): michael@0: def __str__(self): michael@0: # real files have an .encoding attribute and use it when you michael@0: # write() unicode into them: they read()/write() unicode and michael@0: # put encoded bytes in the backend file. StringIO objects michael@0: # read()/write() unicode and put unicode in the backing store, michael@0: # so we must encode the output of getvalue() to get a michael@0: # bytestring. (cStringIO objects are weirder: they effectively michael@0: # have .encoding hardwired to "ascii" and put only bytes in michael@0: # the backing store, so we can't use them here). michael@0: # michael@0: # The encoding= argument to dom.writexml() merely sets the XML header's michael@0: # encoding= attribute. It still writes unencoded unicode to the output file, michael@0: # so we have to encode it for real afterwards. michael@0: # michael@0: # Also see: https://bugzilla.mozilla.org/show_bug.cgi?id=567660 michael@0: michael@0: buf = StringIO.StringIO() michael@0: self.dom.writexml(buf, encoding="utf-8") michael@0: return buf.getvalue().encode('utf-8') michael@0: michael@0: class RDFUpdate(RDF): michael@0: def __init__(self): michael@0: impl = xml.dom.minidom.getDOMImplementation() michael@0: self.dom = impl.createDocument(RDF_NS, "RDF", None) michael@0: self.dom.documentElement.setAttribute("xmlns", RDF_NS) michael@0: self.dom.documentElement.setAttribute("xmlns:em", EM_NS) michael@0: michael@0: def _make_node(self, name, value, parent): michael@0: elem = self.dom.createElement(name) michael@0: elem.appendChild(self.dom.createTextNode(value)) michael@0: parent.appendChild(elem) michael@0: return elem michael@0: michael@0: def add(self, manifest, update_link): michael@0: desc = self.dom.createElement("Description") michael@0: desc.setAttribute( michael@0: "about", michael@0: "urn:mozilla:extension:%s" % manifest.get("em:id") michael@0: ) michael@0: self.dom.documentElement.appendChild(desc) michael@0: michael@0: updates = self.dom.createElement("em:updates") michael@0: desc.appendChild(updates) michael@0: michael@0: seq = self.dom.createElement("Seq") michael@0: updates.appendChild(seq) michael@0: michael@0: li = self.dom.createElement("li") michael@0: seq.appendChild(li) michael@0: michael@0: li_desc = self.dom.createElement("Description") michael@0: li.appendChild(li_desc) michael@0: michael@0: self._make_node("em:version", manifest.get("em:version"), michael@0: li_desc) michael@0: michael@0: apps = manifest.dom.documentElement.getElementsByTagName( michael@0: "em:targetApplication" michael@0: ) michael@0: michael@0: for app in apps: michael@0: target_app = self.dom.createElement("em:targetApplication") michael@0: li_desc.appendChild(target_app) michael@0: michael@0: ta_desc = self.dom.createElement("Description") michael@0: target_app.appendChild(ta_desc) michael@0: michael@0: for name in ["em:id", "em:minVersion", "em:maxVersion"]: michael@0: elem = app.getElementsByTagName(name)[0] michael@0: self._make_node(name, elem.firstChild.nodeValue, ta_desc) michael@0: michael@0: self._make_node("em:updateLink", update_link, ta_desc) michael@0: michael@0: class RDFManifest(RDF): michael@0: def __init__(self, path): michael@0: self.dom = xml.dom.minidom.parse(path) michael@0: michael@0: def set(self, property, value): michael@0: elements = self.dom.documentElement.getElementsByTagName(property) michael@0: if not elements: michael@0: raise ValueError("Element with value not found: %s" % property) michael@0: if not elements[0].firstChild: michael@0: elements[0].appendChild(self.dom.createTextNode(value)) michael@0: else: michael@0: elements[0].firstChild.nodeValue = value michael@0: michael@0: def get(self, property, default=None): michael@0: elements = self.dom.documentElement.getElementsByTagName(property) michael@0: if not elements: michael@0: return default michael@0: return elements[0].firstChild.nodeValue michael@0: michael@0: def remove(self, property): michael@0: elements = self.dom.documentElement.getElementsByTagName(property) michael@0: if not elements: michael@0: return True michael@0: else: michael@0: for i in elements: michael@0: i.parentNode.removeChild(i); michael@0: michael@0: return True; michael@0: michael@0: def gen_manifest(template_root_dir, target_cfg, jid, michael@0: update_url=None, bootstrap=True, enable_mobile=False): michael@0: install_rdf = os.path.join(template_root_dir, "install.rdf") michael@0: manifest = RDFManifest(install_rdf) michael@0: dom = manifest.dom michael@0: michael@0: manifest.set("em:id", jid) michael@0: manifest.set("em:version", michael@0: target_cfg.get('version', '1.0')) michael@0: manifest.set("em:name", michael@0: target_cfg.get('title', target_cfg.get('fullName', target_cfg['name']))) michael@0: manifest.set("em:description", michael@0: target_cfg.get("description", "")) michael@0: manifest.set("em:creator", michael@0: target_cfg.get("author", "")) michael@0: manifest.set("em:bootstrap", str(bootstrap).lower()) michael@0: # XPIs remain packed by default, but package.json can override that. The michael@0: # RDF format accepts "true" as True, anything else as False. We expect michael@0: # booleans in the .json file, not strings. michael@0: manifest.set("em:unpack", "true" if target_cfg.get("unpack") else "false") michael@0: michael@0: for translator in target_cfg.get("translators", [ ]): michael@0: elem = dom.createElement("em:translator"); michael@0: elem.appendChild(dom.createTextNode(translator)) michael@0: dom.documentElement.getElementsByTagName("Description")[0].appendChild(elem) michael@0: michael@0: for contributor in target_cfg.get("contributors", [ ]): michael@0: elem = dom.createElement("em:contributor"); michael@0: elem.appendChild(dom.createTextNode(contributor)) michael@0: dom.documentElement.getElementsByTagName("Description")[0].appendChild(elem) michael@0: michael@0: if update_url: michael@0: manifest.set("em:updateURL", update_url) michael@0: else: michael@0: manifest.remove("em:updateURL") michael@0: michael@0: if target_cfg.get("preferences"): michael@0: manifest.set("em:optionsType", "2") michael@0: else: michael@0: manifest.remove("em:optionsType") michael@0: michael@0: if enable_mobile: michael@0: target_app = dom.createElement("em:targetApplication") michael@0: dom.documentElement.getElementsByTagName("Description")[0].appendChild(target_app) michael@0: michael@0: ta_desc = dom.createElement("Description") michael@0: target_app.appendChild(ta_desc) michael@0: michael@0: elem = dom.createElement("em:id") michael@0: elem.appendChild(dom.createTextNode("{aa3c5121-dab2-40e2-81ca-7ea25febc110}")) michael@0: ta_desc.appendChild(elem) michael@0: michael@0: elem = dom.createElement("em:minVersion") michael@0: elem.appendChild(dom.createTextNode("26.0")) michael@0: ta_desc.appendChild(elem) michael@0: michael@0: elem = dom.createElement("em:maxVersion") michael@0: elem.appendChild(dom.createTextNode("30.0a1")) michael@0: ta_desc.appendChild(elem) michael@0: michael@0: if target_cfg.get("homepage"): michael@0: manifest.set("em:homepageURL", target_cfg.get("homepage")) michael@0: else: michael@0: manifest.remove("em:homepageURL") michael@0: michael@0: return manifest michael@0: michael@0: if __name__ == "__main__": michael@0: print "Running smoke test." michael@0: root = os.path.join(os.path.dirname(__file__), '../../app-extension') michael@0: manifest = gen_manifest(root, {'name': 'test extension'}, michael@0: 'fakeid', 'http://foo.com/update.rdf') michael@0: update = RDFUpdate() michael@0: update.add(manifest, "https://foo.com/foo.xpi") michael@0: exercise_str = str(manifest) + str(update) michael@0: for tagname in ["em:targetApplication", "em:version", "em:id"]: michael@0: if not len(update.dom.getElementsByTagName(tagname)): michael@0: raise Exception("tag does not exist: %s" % tagname) michael@0: if not update.dom.getElementsByTagName(tagname)[0].firstChild: michael@0: raise Exception("tag has no children: %s" % tagname) michael@0: print "Success!"