addon-sdk/source/python-lib/cuddlefish/rdf.py

changeset 0
6474c204b198
     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!"

mercurial