diff options
Diffstat (limited to 'addon-sdk/source/python-lib/cuddlefish/rdf.py')
-rw-r--r-- | addon-sdk/source/python-lib/cuddlefish/rdf.py | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/addon-sdk/source/python-lib/cuddlefish/rdf.py b/addon-sdk/source/python-lib/cuddlefish/rdf.py new file mode 100644 index 000000000..2964897c1 --- /dev/null +++ b/addon-sdk/source/python-lib/cuddlefish/rdf.py @@ -0,0 +1,214 @@ +# 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/. + +import os +import xml.dom.minidom +import StringIO + +RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#" +EM_NS = "http://www.mozilla.org/2004/em-rdf#" + +class RDF(object): + def __str__(self): + # real files have an .encoding attribute and use it when you + # write() unicode into them: they read()/write() unicode and + # put encoded bytes in the backend file. StringIO objects + # read()/write() unicode and put unicode in the backing store, + # so we must encode the output of getvalue() to get a + # bytestring. (cStringIO objects are weirder: they effectively + # have .encoding hardwired to "ascii" and put only bytes in + # the backing store, so we can't use them here). + # + # The encoding= argument to dom.writexml() merely sets the XML header's + # encoding= attribute. It still writes unencoded unicode to the output file, + # so we have to encode it for real afterwards. + # + # Also see: https://bugzilla.mozilla.org/show_bug.cgi?id=567660 + + buf = StringIO.StringIO() + self.dom.writexml(buf, encoding="utf-8") + return buf.getvalue().encode('utf-8') + +class RDFUpdate(RDF): + def __init__(self): + impl = xml.dom.minidom.getDOMImplementation() + self.dom = impl.createDocument(RDF_NS, "RDF", None) + self.dom.documentElement.setAttribute("xmlns", RDF_NS) + self.dom.documentElement.setAttribute("xmlns:em", EM_NS) + + def _make_node(self, name, value, parent): + elem = self.dom.createElement(name) + elem.appendChild(self.dom.createTextNode(value)) + parent.appendChild(elem) + return elem + + def add(self, manifest, update_link): + desc = self.dom.createElement("Description") + desc.setAttribute( + "about", + "urn:mozilla:extension:%s" % manifest.get("em:id") + ) + self.dom.documentElement.appendChild(desc) + + updates = self.dom.createElement("em:updates") + desc.appendChild(updates) + + seq = self.dom.createElement("Seq") + updates.appendChild(seq) + + li = self.dom.createElement("li") + seq.appendChild(li) + + li_desc = self.dom.createElement("Description") + li.appendChild(li_desc) + + self._make_node("em:version", manifest.get("em:version"), + li_desc) + + apps = manifest.dom.documentElement.getElementsByTagName( + "em:targetApplication" + ) + + for app in apps: + target_app = self.dom.createElement("em:targetApplication") + li_desc.appendChild(target_app) + + ta_desc = self.dom.createElement("Description") + target_app.appendChild(ta_desc) + + for name in ["em:id", "em:minVersion", "em:maxVersion"]: + elem = app.getElementsByTagName(name)[0] + self._make_node(name, elem.firstChild.nodeValue, ta_desc) + + self._make_node("em:updateLink", update_link, ta_desc) + +class RDFManifest(RDF): + def __init__(self, path): + self.dom = xml.dom.minidom.parse(path) + + def set(self, property, value): + elements = self.dom.documentElement.getElementsByTagName(property) + if not elements: + raise ValueError("Element with value not found: %s" % property) + if not elements[0].firstChild: + elements[0].appendChild(self.dom.createTextNode(value)) + else: + elements[0].firstChild.nodeValue = value + + def get(self, property, default=None): + elements = self.dom.documentElement.getElementsByTagName(property) + if not elements: + return default + return elements[0].firstChild.nodeValue + + def remove(self, property): + elements = self.dom.documentElement.getElementsByTagName(property) + if not elements: + return True + else: + for i in elements: + i.parentNode.removeChild(i); + + return True; + +def gen_manifest(template_root_dir, target_cfg, jid, + update_url=None, bootstrap=True, enable_mobile=False): + install_rdf = os.path.join(template_root_dir, "install.rdf") + manifest = RDFManifest(install_rdf) + dom = manifest.dom + + manifest.set("em:id", jid) + manifest.set("em:version", + target_cfg.get('version', '1.0')) + manifest.set("em:name", + target_cfg.get('title', target_cfg.get('fullName', target_cfg['name']))) + manifest.set("em:description", + target_cfg.get("description", "")) + manifest.set("em:creator", + target_cfg.get("author", "")) + manifest.set("em:bootstrap", str(bootstrap).lower()) + # XPIs remain packed by default, but package.json can override that. The + # RDF format accepts "true" as True, anything else as False. We expect + # booleans in the .json file, not strings. + manifest.set("em:unpack", "true" if target_cfg.get("unpack") else "false") + + if target_cfg.get('hasEmbeddedWebExtension', False): + elem = dom.createElement("em:hasEmbeddedWebExtension"); + elem.appendChild(dom.createTextNode("true")) + dom.documentElement.getElementsByTagName("Description")[0].appendChild(elem) + + for translator in target_cfg.get("translators", [ ]): + elem = dom.createElement("em:translator"); + elem.appendChild(dom.createTextNode(translator)) + dom.documentElement.getElementsByTagName("Description")[0].appendChild(elem) + + for developer in target_cfg.get("developers", [ ]): + elem = dom.createElement("em:developer"); + elem.appendChild(dom.createTextNode(developer)) + dom.documentElement.getElementsByTagName("Description")[0].appendChild(elem) + + for contributor in target_cfg.get("contributors", [ ]): + elem = dom.createElement("em:contributor"); + elem.appendChild(dom.createTextNode(contributor)) + dom.documentElement.getElementsByTagName("Description")[0].appendChild(elem) + + if update_url: + manifest.set("em:updateURL", update_url) + else: + manifest.remove("em:updateURL") + + if target_cfg.get("preferences"): + manifest.set("em:optionsType", "2") + + # workaround until bug 971249 is fixed + # https://bugzilla.mozilla.org/show_bug.cgi?id=971249 + manifest.set("em:optionsURL", "data:text/xml,<placeholder/>") + + # workaround for workaround, for testing simple-prefs-regression + if (os.path.exists(os.path.join(template_root_dir, "options.xul"))): + manifest.remove("em:optionsURL") + else: + manifest.remove("em:optionsType") + manifest.remove("em:optionsURL") + + if enable_mobile: + target_app = dom.createElement("em:targetApplication") + dom.documentElement.getElementsByTagName("Description")[0].appendChild(target_app) + + ta_desc = dom.createElement("Description") + target_app.appendChild(ta_desc) + + elem = dom.createElement("em:id") + elem.appendChild(dom.createTextNode("{aa3c5121-dab2-40e2-81ca-7ea25febc110}")) + ta_desc.appendChild(elem) + + elem = dom.createElement("em:minVersion") + elem.appendChild(dom.createTextNode("26.0")) + ta_desc.appendChild(elem) + + elem = dom.createElement("em:maxVersion") + elem.appendChild(dom.createTextNode("30.0a1")) + ta_desc.appendChild(elem) + + if target_cfg.get("homepage"): + manifest.set("em:homepageURL", target_cfg.get("homepage")) + else: + manifest.remove("em:homepageURL") + + return manifest + +if __name__ == "__main__": + print "Running smoke test." + root = os.path.join(os.path.dirname(__file__), '../../app-extension') + manifest = gen_manifest(root, {'name': 'test extension'}, + 'fakeid', 'http://foo.com/update.rdf') + update = RDFUpdate() + update.add(manifest, "https://foo.com/foo.xpi") + exercise_str = str(manifest) + str(update) + for tagname in ["em:targetApplication", "em:version", "em:id"]: + if not len(update.dom.getElementsByTagName(tagname)): + raise Exception("tag does not exist: %s" % tagname) + if not update.dom.getElementsByTagName(tagname)[0].firstChild: + raise Exception("tag has no children: %s" % tagname) + print "Success!" |