Files
tubestation/addon-sdk/source/python-lib/cuddlefish/rdf.py
Luca Greco d4e3fe7590 Bug 1269347 - Part2 Add support for embedded webextension in SDK test addons. r=mossop
This commit make it possible the creation of hybrid addons as part of the set of
SDK test addons.

Unfortunately these SDK test addons are build using the old cfx tool and the its
related module loader, and so this patch applies the changes needed to:

- optionally include an `hasEmbeddedWebExtension` option in the generated "install.rdf"
  (if the test addon "package.json" contains a property with the same name
  in its "package.json")

- initialize the "sdk/webextension" from the bootstrap method params
  (as already done in the previous patch for the new SDK module loader)

- include the "webextension/" dir in the generated xpi, if any (similarly
  to how it is already done for the "chrome/" dir, which an SDK addon can optionally
  contain)

MozReview-Commit-ID: 4WS5ZYkl8FY
2016-05-23 23:02:18 +02:00

215 lines
8.3 KiB
Python

# 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!"