1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/python-lib/cuddlefish/rdf.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,195 @@ 1.4 +# This Source Code Form is subject to the terms of the Mozilla Public 1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.7 + 1.8 +import os 1.9 +import xml.dom.minidom 1.10 +import StringIO 1.11 + 1.12 +RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#" 1.13 +EM_NS = "http://www.mozilla.org/2004/em-rdf#" 1.14 + 1.15 +class RDF(object): 1.16 + def __str__(self): 1.17 + # real files have an .encoding attribute and use it when you 1.18 + # write() unicode into them: they read()/write() unicode and 1.19 + # put encoded bytes in the backend file. StringIO objects 1.20 + # read()/write() unicode and put unicode in the backing store, 1.21 + # so we must encode the output of getvalue() to get a 1.22 + # bytestring. (cStringIO objects are weirder: they effectively 1.23 + # have .encoding hardwired to "ascii" and put only bytes in 1.24 + # the backing store, so we can't use them here). 1.25 + # 1.26 + # The encoding= argument to dom.writexml() merely sets the XML header's 1.27 + # encoding= attribute. It still writes unencoded unicode to the output file, 1.28 + # so we have to encode it for real afterwards. 1.29 + # 1.30 + # Also see: https://bugzilla.mozilla.org/show_bug.cgi?id=567660 1.31 + 1.32 + buf = StringIO.StringIO() 1.33 + self.dom.writexml(buf, encoding="utf-8") 1.34 + return buf.getvalue().encode('utf-8') 1.35 + 1.36 +class RDFUpdate(RDF): 1.37 + def __init__(self): 1.38 + impl = xml.dom.minidom.getDOMImplementation() 1.39 + self.dom = impl.createDocument(RDF_NS, "RDF", None) 1.40 + self.dom.documentElement.setAttribute("xmlns", RDF_NS) 1.41 + self.dom.documentElement.setAttribute("xmlns:em", EM_NS) 1.42 + 1.43 + def _make_node(self, name, value, parent): 1.44 + elem = self.dom.createElement(name) 1.45 + elem.appendChild(self.dom.createTextNode(value)) 1.46 + parent.appendChild(elem) 1.47 + return elem 1.48 + 1.49 + def add(self, manifest, update_link): 1.50 + desc = self.dom.createElement("Description") 1.51 + desc.setAttribute( 1.52 + "about", 1.53 + "urn:mozilla:extension:%s" % manifest.get("em:id") 1.54 + ) 1.55 + self.dom.documentElement.appendChild(desc) 1.56 + 1.57 + updates = self.dom.createElement("em:updates") 1.58 + desc.appendChild(updates) 1.59 + 1.60 + seq = self.dom.createElement("Seq") 1.61 + updates.appendChild(seq) 1.62 + 1.63 + li = self.dom.createElement("li") 1.64 + seq.appendChild(li) 1.65 + 1.66 + li_desc = self.dom.createElement("Description") 1.67 + li.appendChild(li_desc) 1.68 + 1.69 + self._make_node("em:version", manifest.get("em:version"), 1.70 + li_desc) 1.71 + 1.72 + apps = manifest.dom.documentElement.getElementsByTagName( 1.73 + "em:targetApplication" 1.74 + ) 1.75 + 1.76 + for app in apps: 1.77 + target_app = self.dom.createElement("em:targetApplication") 1.78 + li_desc.appendChild(target_app) 1.79 + 1.80 + ta_desc = self.dom.createElement("Description") 1.81 + target_app.appendChild(ta_desc) 1.82 + 1.83 + for name in ["em:id", "em:minVersion", "em:maxVersion"]: 1.84 + elem = app.getElementsByTagName(name)[0] 1.85 + self._make_node(name, elem.firstChild.nodeValue, ta_desc) 1.86 + 1.87 + self._make_node("em:updateLink", update_link, ta_desc) 1.88 + 1.89 +class RDFManifest(RDF): 1.90 + def __init__(self, path): 1.91 + self.dom = xml.dom.minidom.parse(path) 1.92 + 1.93 + def set(self, property, value): 1.94 + elements = self.dom.documentElement.getElementsByTagName(property) 1.95 + if not elements: 1.96 + raise ValueError("Element with value not found: %s" % property) 1.97 + if not elements[0].firstChild: 1.98 + elements[0].appendChild(self.dom.createTextNode(value)) 1.99 + else: 1.100 + elements[0].firstChild.nodeValue = value 1.101 + 1.102 + def get(self, property, default=None): 1.103 + elements = self.dom.documentElement.getElementsByTagName(property) 1.104 + if not elements: 1.105 + return default 1.106 + return elements[0].firstChild.nodeValue 1.107 + 1.108 + def remove(self, property): 1.109 + elements = self.dom.documentElement.getElementsByTagName(property) 1.110 + if not elements: 1.111 + return True 1.112 + else: 1.113 + for i in elements: 1.114 + i.parentNode.removeChild(i); 1.115 + 1.116 + return True; 1.117 + 1.118 +def gen_manifest(template_root_dir, target_cfg, jid, 1.119 + update_url=None, bootstrap=True, enable_mobile=False): 1.120 + install_rdf = os.path.join(template_root_dir, "install.rdf") 1.121 + manifest = RDFManifest(install_rdf) 1.122 + dom = manifest.dom 1.123 + 1.124 + manifest.set("em:id", jid) 1.125 + manifest.set("em:version", 1.126 + target_cfg.get('version', '1.0')) 1.127 + manifest.set("em:name", 1.128 + target_cfg.get('title', target_cfg.get('fullName', target_cfg['name']))) 1.129 + manifest.set("em:description", 1.130 + target_cfg.get("description", "")) 1.131 + manifest.set("em:creator", 1.132 + target_cfg.get("author", "")) 1.133 + manifest.set("em:bootstrap", str(bootstrap).lower()) 1.134 + # XPIs remain packed by default, but package.json can override that. The 1.135 + # RDF format accepts "true" as True, anything else as False. We expect 1.136 + # booleans in the .json file, not strings. 1.137 + manifest.set("em:unpack", "true" if target_cfg.get("unpack") else "false") 1.138 + 1.139 + for translator in target_cfg.get("translators", [ ]): 1.140 + elem = dom.createElement("em:translator"); 1.141 + elem.appendChild(dom.createTextNode(translator)) 1.142 + dom.documentElement.getElementsByTagName("Description")[0].appendChild(elem) 1.143 + 1.144 + for contributor in target_cfg.get("contributors", [ ]): 1.145 + elem = dom.createElement("em:contributor"); 1.146 + elem.appendChild(dom.createTextNode(contributor)) 1.147 + dom.documentElement.getElementsByTagName("Description")[0].appendChild(elem) 1.148 + 1.149 + if update_url: 1.150 + manifest.set("em:updateURL", update_url) 1.151 + else: 1.152 + manifest.remove("em:updateURL") 1.153 + 1.154 + if target_cfg.get("preferences"): 1.155 + manifest.set("em:optionsType", "2") 1.156 + else: 1.157 + manifest.remove("em:optionsType") 1.158 + 1.159 + if enable_mobile: 1.160 + target_app = dom.createElement("em:targetApplication") 1.161 + dom.documentElement.getElementsByTagName("Description")[0].appendChild(target_app) 1.162 + 1.163 + ta_desc = dom.createElement("Description") 1.164 + target_app.appendChild(ta_desc) 1.165 + 1.166 + elem = dom.createElement("em:id") 1.167 + elem.appendChild(dom.createTextNode("{aa3c5121-dab2-40e2-81ca-7ea25febc110}")) 1.168 + ta_desc.appendChild(elem) 1.169 + 1.170 + elem = dom.createElement("em:minVersion") 1.171 + elem.appendChild(dom.createTextNode("26.0")) 1.172 + ta_desc.appendChild(elem) 1.173 + 1.174 + elem = dom.createElement("em:maxVersion") 1.175 + elem.appendChild(dom.createTextNode("30.0a1")) 1.176 + ta_desc.appendChild(elem) 1.177 + 1.178 + if target_cfg.get("homepage"): 1.179 + manifest.set("em:homepageURL", target_cfg.get("homepage")) 1.180 + else: 1.181 + manifest.remove("em:homepageURL") 1.182 + 1.183 + return manifest 1.184 + 1.185 +if __name__ == "__main__": 1.186 + print "Running smoke test." 1.187 + root = os.path.join(os.path.dirname(__file__), '../../app-extension') 1.188 + manifest = gen_manifest(root, {'name': 'test extension'}, 1.189 + 'fakeid', 'http://foo.com/update.rdf') 1.190 + update = RDFUpdate() 1.191 + update.add(manifest, "https://foo.com/foo.xpi") 1.192 + exercise_str = str(manifest) + str(update) 1.193 + for tagname in ["em:targetApplication", "em:version", "em:id"]: 1.194 + if not len(update.dom.getElementsByTagName(tagname)): 1.195 + raise Exception("tag does not exist: %s" % tagname) 1.196 + if not update.dom.getElementsByTagName(tagname)[0].firstChild: 1.197 + raise Exception("tag has no children: %s" % tagname) 1.198 + print "Success!"