diff --git a/addon-sdk/source/README b/addon-sdk/source/README index 3959d4e878ad..021c9f4c5cd1 100644 --- a/addon-sdk/source/README +++ b/addon-sdk/source/README @@ -6,7 +6,8 @@ Before proceeding, please make sure you've installed Python 2.5, http://python.org/download/ -Note that Python 3.0 and 3.1 are not supported in this release. +Note that Python 3 is not supported on any platform, and Python 2.7.6 +is not supported on Windows. For Windows users, MozillaBuild (https://wiki.mozilla.org/MozillaBuild) will install the correct version of Python and the MSYS package, which @@ -22,12 +23,12 @@ Windows users using cmd.exe should instead run: bin\activate.bat -Then go to https://addons.mozilla.org/developers/docs/sdk/latest/dev-guide to -browse the SDK documentation. +Then go to https://developer.mozilla.org/en-US/Add-ons/SDK/ +to browse the SDK documentation. If you get an error when running cfx or have any other problems getting started, see the "Troubleshooting" guide at: -https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/tutorials/troubleshooting.html +https://developer.mozilla.org/en-US/Add-ons/SDK/Tutorials/Troubleshooting Bugs ------- diff --git a/addon-sdk/source/app-extension/bootstrap.js b/addon-sdk/source/app-extension/bootstrap.js index 6bb014a13970..d430d70ebf52 100644 --- a/addon-sdk/source/app-extension/bootstrap.js +++ b/addon-sdk/source/app-extension/bootstrap.js @@ -221,6 +221,8 @@ function startup(data, reasonCode) { resultFile: options.resultFile, // Arguments passed as --static-args staticArgs: options.staticArgs, + // Add-on preferences branch name + preferencesBranch: options.preferencesBranch, // Arguments related to test runner. modules: { diff --git a/addon-sdk/source/lib/diffpatcher/diff.js b/addon-sdk/source/lib/diffpatcher/diff.js index aa8236850646..967c137e65ce 100644 --- a/addon-sdk/source/lib/diffpatcher/diff.js +++ b/addon-sdk/source/lib/diffpatcher/diff.js @@ -1,6 +1,6 @@ "use strict"; -var method = require("method/core") +var method = require("../method/core") // Method is designed to work with data structures representing application // state. Calling it with a state should return object representing `delta` diff --git a/addon-sdk/source/lib/diffpatcher/patch.js b/addon-sdk/source/lib/diffpatcher/patch.js index 366826a215b8..9271e88932ce 100644 --- a/addon-sdk/source/lib/diffpatcher/patch.js +++ b/addon-sdk/source/lib/diffpatcher/patch.js @@ -1,6 +1,6 @@ "use strict"; -var method = require("method/core") +var method = require("../method/core") var rebase = require("./rebase") // Method is designed to work with data structures representing application diff --git a/addon-sdk/source/lib/sdk/content/mod.js b/addon-sdk/source/lib/sdk/content/mod.js index 6b5d9ce4c9a0..6ce0c7608d38 100644 --- a/addon-sdk/source/lib/sdk/content/mod.js +++ b/addon-sdk/source/lib/sdk/content/mod.js @@ -8,7 +8,7 @@ module.metadata = { }; const { Ci } = require("chrome"); -const method = require("method/core"); +const method = require("../../method/core"); const { add, remove, iterator } = require("../lang/weak-set"); let getTargetWindow = method("getTargetWindow"); diff --git a/addon-sdk/source/lib/sdk/content/utils.js b/addon-sdk/source/lib/sdk/content/utils.js index 0ee7a14b0209..058750e43c1c 100644 --- a/addon-sdk/source/lib/sdk/content/utils.js +++ b/addon-sdk/source/lib/sdk/content/utils.js @@ -10,7 +10,7 @@ module.metadata = { let { merge } = require('../util/object'); let assetsURI = require('../self').data.url(); let isArray = Array.isArray; -let method = require('method/core'); +let method = require('../../method/core'); function isAddonContent({ contentURL }) { return typeof(contentURL) === 'string' && contentURL.indexOf(assetsURI) === 0; diff --git a/addon-sdk/source/lib/sdk/self.js b/addon-sdk/source/lib/sdk/self.js index 23dd8c823076..c720be890b71 100644 --- a/addon-sdk/source/lib/sdk/self.js +++ b/addon-sdk/source/lib/sdk/self.js @@ -9,7 +9,7 @@ module.metadata = { const { CC } = require('chrome'); const { id, name, prefixURI, rootURI, metadata, - version, loadReason } = require('@loader/options'); + version, loadReason, preferencesBranch } = require('@loader/options'); const { readURISync } = require('./net/url'); @@ -24,16 +24,16 @@ const uri = (path="") => // associated unique URI string that can be used for that. exports.uri = 'addon:' + id; exports.id = id; +exports.preferencesBranch = preferencesBranch || id; exports.name = name; exports.loadReason = loadReason; exports.version = version; // If `rootURI` is jar:file://...!/ than add-on is packed. -exports.packed = rootURI.indexOf('jar:') === 0 +exports.packed = (rootURI || '').indexOf('jar:') === 0; exports.data = Object.freeze({ url: uri, load: function read(path) { return readURISync(uri(path)); } }); -exports.isPrivateBrowsingSupported = ((metadata.permissions || {})['private-browsing'] === true) ? - true : false; +exports.isPrivateBrowsingSupported = ((metadata || {}).permissions || {})['private-browsing'] === true; diff --git a/addon-sdk/source/lib/sdk/simple-prefs.js b/addon-sdk/source/lib/sdk/simple-prefs.js index 4ce302a5805a..3472f44187eb 100644 --- a/addon-sdk/source/lib/sdk/simple-prefs.js +++ b/addon-sdk/source/lib/sdk/simple-prefs.js @@ -9,10 +9,10 @@ module.metadata = { const { emit, off } = require("./event/core"); const { PrefsTarget } = require("./preferences/event-target"); -const { id } = require("./self"); +const { preferencesBranch, id } = require("./self"); const { on } = require("./system/events"); -const ADDON_BRANCH = "extensions." + id + "."; +const ADDON_BRANCH = "extensions." + preferencesBranch + "."; const BUTTON_PRESSED = id + "-cmdPressed"; const target = PrefsTarget({ branchName: ADDON_BRANCH }); diff --git a/addon-sdk/source/lib/sdk/ui/button/view.js b/addon-sdk/source/lib/sdk/ui/button/view.js index f11086aa16a0..82463c552e65 100644 --- a/addon-sdk/source/lib/sdk/ui/button/view.js +++ b/addon-sdk/source/lib/sdk/ui/button/view.js @@ -142,6 +142,7 @@ function create(options) { node.setAttribute('label', label); node.setAttribute('tooltiptext', label); node.setAttribute('image', image); + node.setAttribute('sdk-button', 'true'); views.set(id, { area: this.currentArea, diff --git a/addon-sdk/source/lib/sdk/ui/id.js b/addon-sdk/source/lib/sdk/ui/id.js index 0e1d69dd9b3b..bd992818c1aa 100644 --- a/addon-sdk/source/lib/sdk/ui/id.js +++ b/addon-sdk/source/lib/sdk/ui/id.js @@ -7,7 +7,7 @@ module.metadata = { 'stability': 'experimental' }; -const method = require('method/core'); +const method = require('../../method/core'); const { uuid } = require('../util/uuid'); // NOTE: use lang/functional memoize when it is updated to use WeakMap diff --git a/addon-sdk/source/lib/sdk/ui/sidebar/actions.js b/addon-sdk/source/lib/sdk/ui/sidebar/actions.js index cd4edb7e4f57..2be335181cf6 100644 --- a/addon-sdk/source/lib/sdk/ui/sidebar/actions.js +++ b/addon-sdk/source/lib/sdk/ui/sidebar/actions.js @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 'use strict'; -const method = require('method/core'); +const method = require('../../../method/core'); exports.show = method('show'); exports.hide = method('hide'); diff --git a/addon-sdk/source/lib/sdk/ui/sidebar/utils.js b/addon-sdk/source/lib/sdk/ui/sidebar/utils.js index 05225dad4209..6c44ea4a2d23 100644 --- a/addon-sdk/source/lib/sdk/ui/sidebar/utils.js +++ b/addon-sdk/source/lib/sdk/ui/sidebar/utils.js @@ -3,6 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 'use strict'; -const method = require('method/core'); +const method = require('../../../method/core'); exports.isShowing = method('isShowing'); diff --git a/addon-sdk/source/lib/sdk/view/core.js b/addon-sdk/source/lib/sdk/view/core.js index b321e8527827..85bd758bd023 100644 --- a/addon-sdk/source/lib/sdk/view/core.js +++ b/addon-sdk/source/lib/sdk/view/core.js @@ -9,7 +9,7 @@ module.metadata = { }; var { Ci } = require("chrome"); -var method = require("method/core"); +var method = require("../../method/core"); // Returns DOM node associated with a view for // the given `value`. If `value` has no view associated diff --git a/addon-sdk/source/python-lib/cuddlefish/__init__.py b/addon-sdk/source/python-lib/cuddlefish/__init__.py index 8218bae9ca73..8b0d773184cb 100644 --- a/addon-sdk/source/python-lib/cuddlefish/__init__.py +++ b/addon-sdk/source/python-lib/cuddlefish/__init__.py @@ -806,6 +806,8 @@ def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None, if options.templatedir: app_extension_dir = os.path.abspath(options.templatedir) + elif os.path.exists(os.path.join(options.pkgdir, "app-extension")): + app_extension_dir = os.path.join(options.pkgdir, "app-extension") else: mydir = os.path.dirname(os.path.abspath(__file__)) app_extension_dir = os.path.join(mydir, "../../app-extension") diff --git a/addon-sdk/source/python-lib/cuddlefish/docs/__init__.py b/addon-sdk/source/python-lib/cuddlefish/docs/__init__.py deleted file mode 100644 index 5501cd4b3305..000000000000 --- a/addon-sdk/source/python-lib/cuddlefish/docs/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# 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/. - diff --git a/addon-sdk/source/python-lib/cuddlefish/docs/apiparser.py b/addon-sdk/source/python-lib/cuddlefish/docs/apiparser.py deleted file mode 100644 index b6ccf2262338..000000000000 --- a/addon-sdk/source/python-lib/cuddlefish/docs/apiparser.py +++ /dev/null @@ -1,392 +0,0 @@ -# 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 sys, re, textwrap - -VERSION = 4 - -class ParseError(Exception): - # args[1] is the line number that caused the problem - def __init__(self, why, lineno): - self.why = why - self.lineno = lineno - def __str__(self): - return ("ParseError: the JS API docs were unparseable on line %d: %s" % - (self.lineno, self.why)) - -class Accumulator: - def __init__(self, holder, firstline): - self.holder = holder - self.firstline = firstline - self.otherlines = [] - def addline(self, line): - self.otherlines.append(line) - def finish(self): - # take a list of strings like: - # "initial stuff" (this is in firstline) - # " more stuff" (this is in lines[0]) - # " yet more stuff" - # " indented block" - # " indented block" - # " nonindented stuff" (lines[-1]) - # - # calculate the indentation level by looking at all but the first - # line, and removing the whitespace they all have in common. Then - # join the results with newlines and return a single string. - pieces = [] - if self.firstline: - pieces.append(self.firstline) - if self.otherlines: - pieces.append(textwrap.dedent("\n".join(self.otherlines))) - self.holder["description"] = "\n".join(pieces) - - -class APIParser: - def parse(self, lines, lineno): - api = {"line_number": lineno + 1} -# assign the name from the first line, of the form "" - title_line = lines[lineno].rstrip("\n") - api["name"] = self._parse_title_line(title_line, lineno + 1) - lineno += 1 -# finished with the first line, assigned the name - working_set = self._initialize_working_set() - props = [] - currentPropHolder = api -# fetch the next line, of the form "@tag [name] {datatype} description" -# and parse it into tag, info, description - tag, info, firstline = self._parseTypeLine(lines[lineno], lineno + 1) - api["type"] = tag -# if this API element is a property then datatype must be set - if tag == 'property': - api['datatype'] = info['datatype'] - # info is ignored - currentAccumulator = Accumulator(api, firstline) - lineno += 1 - while (lineno) < len(lines): - line = lines[lineno].rstrip("\n") - # accumulate any multiline descriptive text belonging to - # the preceding "@" section - if self._is_description_line(line): - currentAccumulator.addline(line) - else: - currentAccumulator.finish() - if line.startswith(" element - tag, info, desc = self._parseTypeLine(line, lineno + 1) - currentAccumulator = Accumulator(info, desc) - if tag == "prop": - # build up props[] - props.append(info) - elif tag == "returns": - # close off the @prop list - if props and currentPropHolder: - currentPropHolder["props"] = props - props = [] - api["returns"] = info - currentPropHolder = info - elif tag == "param": - # close off the @prop list - if props and currentPropHolder: - currentPropHolder["props"] = props - props = [] - working_set["params"].append(info) - currentPropHolder = info - elif tag == "argument": - # close off the @prop list - if props and currentPropHolder: - currentPropHolder["props"] = props - props = [] - working_set["arguments"].append(info) - currentPropHolder = info - else: - raise ParseError("unknown '@' section header %s in \ - '%s'" % (tag, line), lineno + 1) - lineno += 1 - raise ParseError("closing tag not found for ", lineno + 1) - - def _parse_title_line(self, title_line, lineno): - if "name" not in title_line: - raise ParseError("Opening tag must have a name attribute.", - lineno) - m = re.search("name=['\"]{0,1}([-\w\.]*?)['\"]", title_line) - if not m: - raise ParseError("No value for name attribute found in " - "opening tag.", lineno) - return m.group(1) - - def _is_description_line(self, line): - return not ( (line.lstrip().startswith("@")) or - (line.lstrip().startswith(" 0: - signature += params[0]["name"] - for param in params[1:]: - signature += ", " + param["name"] - signature += ")" - api_element["signature"] = signature - - def _assemble_api_element(self, api_element, working_set): - # if any of this working set's lists are non-empty, - # add it to the current api element - if (api_element["type"] == "constructor") or \ - (api_element["type"] == "function") or \ - (api_element["type"] == "method"): - self._assemble_signature(api_element, working_set["params"]) - if len(working_set["params"]) > 0: - api_element["params"] = working_set["params"] - if len(working_set["properties"]) > 0: - api_element["properties"] = working_set["properties"] - if len(working_set["constructors"]) > 0: - api_element["constructors"] = working_set["constructors"] - if len(working_set["methods"]) > 0: - api_element["methods"] = working_set["methods"] - if len(working_set["events"]) > 0: - api_element["events"] = working_set["events"] - if len(working_set["arguments"]) > 0: - api_element["arguments"] = working_set["arguments"] - - def _validate_info(self, tag, info, line, lineno): - if tag == 'property': - if not 'datatype' in info: - raise ParseError("No type found for @property.", lineno) - elif tag == "param": - if info.get("required", False) and "default" in info: - raise ParseError( - "required parameters should not have defaults: '%s'" - % line, lineno) - elif tag == "prop": - if "datatype" not in info: - raise ParseError("@prop lines must include {type}: '%s'" % - line, lineno) - if "name" not in info: - raise ParseError("@prop lines must provide a name: '%s'" % - line, lineno) - - def _parseTypeLine(self, line, lineno): - # handle these things: - # @method - # @returns description - # @returns {string} description - # @param NAME {type} description - # @param NAME - # @prop NAME {type} description - # @prop NAME - # returns: - # tag: type of api element - # info: linenumber, required, default, name, datatype - # description - - info = {"line_number": lineno} - line = line.rstrip("\n") - pieces = line.split() - - if not pieces: - raise ParseError("line is too short: '%s'" % line, lineno) - if not pieces[0].startswith("@"): - raise ParseError("type line should start with @: '%s'" % line, - lineno) - tag = pieces[0][1:] - skip = 1 - - expect_name = tag in ("param", "prop") - - if len(pieces) == 1: - description = "" - else: - if pieces[1].startswith("{"): - # NAME is missing, pieces[1] is TYPE - pass - else: - if expect_name: - info["required"] = not pieces[1].startswith("[") - name = pieces[1].strip("[ ]") - if "=" in name: - name, info["default"] = name.split("=") - info["name"] = name - skip += 1 - - if len(pieces) > skip and pieces[skip].startswith("{"): - info["datatype"] = pieces[skip].strip("{ }") - skip += 1 - - # we've got the metadata, now extract the description - pieces = line.split(None, skip) - if len(pieces) > skip: - description = pieces[skip] - else: - description = "" - self._validate_info(tag, info, line, lineno) - return tag, info, description - -def parse_hunks(text): - # return a list of tuples. Each is one of: - # ("raw", string) : non-API blocks - # ("api-json", dict) : API blocks - yield ("version", VERSION) - lines = text.splitlines(True) - line_number = 0 - markdown_string = "" - while line_number < len(lines): - line = lines[line_number] - if line.startswith(" 0: - yield ("markdown", markdown_string) - markdown_string = "" - api, line_number = APIParser().parse(lines, line_number) - # this business with 'leftover' is a horrible thing to do, - # and exists only to collect the \n after the closing /api tag. - # It's not needed probably, except to help keep compatibility - # with the previous behaviour - leftover = lines[line_number].lstrip("") - if len(leftover) > 0: - markdown_string += leftover - line_number = line_number + 1 - yield ("api-json", api) - else: - markdown_string += line - line_number = line_number + 1 - if len(markdown_string) > 0: - yield ("markdown", markdown_string) - -class TestRenderer: - # render docs for test purposes - - def getm(self, d, key): - return d.get(key, "") - - def join_lines(self, text): - return " ".join([line.strip() for line in text.split("\n")]) - - def render_prop(self, p): - s = "props[%s]: " % self.getm(p, "name") - pieces = [] - for k in ("type", "description", "required", "default"): - if k in p: - pieces.append("%s=%s" % (k, self.join_lines(str(p[k])))) - return s + ", ".join(pieces) - - def render_param(self, p): - pieces = [] - for k in ("name", "type", "description", "required", "default"): - if k in p: - pieces.append("%s=%s" % (k, self.join_lines(str(p[k])))) - yield ", ".join(pieces) - for prop in p.get("props", []): - yield " " + self.render_prop(prop) - - def render_method(self, method): - yield "name= %s" % self.getm(method, "name") - yield "type= %s" % self.getm(method, "type") - yield "description= %s" % self.getm(method, "description") - signature = method.get("signature") - if signature: - yield "signature= %s" % self.getm(method, "signature") - params = method.get("params", []) - if params: - yield "parameters:" - for p in params: - for pline in self.render_param(p): - yield " " + pline - r = method.get("returns", None) - if r: - yield "returns:" - if "type" in r: - yield " type= %s" % r["type"] - if "description" in r: - yield " description= %s" % self.join_lines(r["description"]) - props = r.get("props", []) - for p in props: - yield " " + self.render_prop(p) - - def format_api(self, api): - for mline in self.render_method(api): - yield mline - constructors = api.get("constructors", []) - if constructors: - yield "constructors:" - for m in constructors: - for mline in self.render_method(m): - yield " " + mline - methods = api.get("methods", []) - if methods: - yield "methods:" - for m in methods: - for mline in self.render_method(m): - yield " " + mline - properties = api.get("properties", []) - if properties: - yield "properties:" - for p in properties: - yield " " + self.render_prop(p) - - def render_docs(self, docs_json, outf=sys.stdout): - - for (t,data) in docs_json: - if t == "api-json": - for line in self.format_api(data): - line = line.rstrip("\n") - outf.write("API: " + line + "\n") - else: - for line in str(data).split("\n"): - outf.write("MD :" + line + "\n") - -def hunks_to_dict(docs_json): - exports = {} - for (t,data) in docs_json: - if t != "api-json": - continue - if data["name"]: - exports[data["name"]] = data - return exports - -if __name__ == "__main__": - json = False - if sys.argv[1] == "--json": - json = True - del sys.argv[1] - docs_text = open(sys.argv[1]).read() - docs_parsed = list(parse_hunks(docs_text)) - if json: - import simplejson - print simplejson.dumps(docs_parsed, indent=2) - else: - TestRenderer().render_docs(docs_parsed) diff --git a/addon-sdk/source/python-lib/cuddlefish/docs/apirenderer.py b/addon-sdk/source/python-lib/cuddlefish/docs/apirenderer.py deleted file mode 100644 index 533dff51a9d7..000000000000 --- a/addon-sdk/source/python-lib/cuddlefish/docs/apirenderer.py +++ /dev/null @@ -1,301 +0,0 @@ -# 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 sys, os -import markdown -import apiparser - -# list of all the 'class' and 'id' attributes assigned to -#
and tags by the renderer. -API_REFERENCE = 'api_reference' -MODULE_API_DOCS_CLASS = 'module_api_docs' -MODULE_API_DOCS_ID = '_module_api_docs' -API_HEADER = 'api_header' -API_NAME = 'api_name' -API_COMPONENT_GROUP = 'api_component_group' -API_COMPONENT = 'api_component' -DATATYPE = 'datatype' -RETURNS = 'returns' -PARAMETER_SET = 'parameter_set' -MODULE_DESCRIPTION = 'module_description' - -HTML_HEADER = ''' -\n -\n -\n - \n - \n - \n - \n - Add-on SDK Documentation\n - \n -\n - \n -\n -\n -\n''' - -HTML_FOOTER = ''' -\n -\n -\n''' - -def indent(text_in): - text_out = '' - lines = text_in.splitlines(True) - indentation_level = 0 - indentation_depth = 2 - for line in lines: - if (line.startswith('' in line: - indentation_level += 1 - else: - if (line.startswith('
')): - indentation_level -= 1 - text_out += ((' ' * indentation_depth) * indentation_level) + line - return text_out - -def tag_wrap_id(text, classname, id, tag = 'div'): - return ''.join(['\n<'+ tag + ' id="', id, '" class="', \ - classname, '">\n', text + '\n\n']) - -def tag_wrap(text, classname, tag = 'div', inline = False): - if inline: - return ''.join(['\n<' + tag + ' class="', classname, '">', \ - text, '\n']) - else: - return ''.join(['\n<' + tag + ' class="', classname, '">', \ - text, '\n\n']) - -def tag_wrap_inline(text, classname, tag = 'div'): - return ''.join(['\n<' + tag + ' class="', classname, '">', \ - text, '\n']) - -def span_wrap(text, classname): - return ''.join(['', \ - text, '']) - -class API_Renderer(object): - def __init__(self, json, tag): - self.name = json.get('name', None) - self.tag = tag - self.description = json.get('description', '') - self.json = json - - def render_name(self): - raise Exception('not implemented in this class') - - def render_description(self): - return markdown.markdown(self.description) - - def render_subcomponents(self): - raise Exception('not implemented in this class') - - def get_tag(self): - return self.tag - -class Class_Doc(API_Renderer): - def __init__(self, json, tag): - API_Renderer.__init__(self, json, tag) - - def render_name(self): - return self.name - - def render_subcomponents(self): - return render_object_contents(self.json, 'h5', 'h6') - -class Event_Doc(API_Renderer): - def __init__(self, json, tag): - API_Renderer.__init__(self, json, tag) - self.arguments_json = json.get('arguments', None) - - def render_name(self): - return self.name - - def render_subcomponents(self): - if not self.arguments_json: - return '' - text = ''.join([render_comp(Argument_Doc(argument_json, 'div')) \ - for argument_json in self.arguments_json]) - return tag_wrap(text, PARAMETER_SET) - -class Argument_Doc(API_Renderer): - def __init__(self, json, tag): - API_Renderer.__init__(self, json, tag) - self.datatype = json.get('datatype', None) - - def render_name(self): - return span_wrap(self.datatype, DATATYPE) - - def render_subcomponents(self): - return '' - -class Function_Doc(API_Renderer): - def __init__(self, json, tag): - API_Renderer.__init__(self, json, tag) - self.signature = json['signature'] - self.returns = json.get('returns', None) - self.parameters_json = json.get('params', None) - - def render_name(self): - return self.signature - - def render_subcomponents(self): - return self._render_parameters() + self._render_returns() - - def _render_parameters(self): - if not self.parameters_json: - return '' - text = ''.join([render_comp(Parameter_Doc(parameter_json, 'div')) \ - for parameter_json in self.parameters_json]) - return tag_wrap(text, PARAMETER_SET) - - def _render_returns(self): - if not self.returns: - return '' - text = 'Returns: ' + span_wrap(self.returns['datatype'], DATATYPE) - text += markdown.markdown(self.returns['description']) - return tag_wrap(text, RETURNS) - -class Property_Doc(API_Renderer): - def __init__(self, json, tag): - API_Renderer.__init__(self, json, tag) - self.datatype = json.get('datatype', None) - self.required = json.get('required', True) - self.default = json.get('default', False) - - def render_name(self): - rendered = self.name - if self.default: - rendered = rendered + " = " + self.default - if self.datatype: - rendered = rendered + ' : ' + span_wrap(self.datatype, DATATYPE) - if not self.required: - rendered = '[ ' + rendered + ' ]' - return rendered - - def render_subcomponents(self): - return render_object_contents(self.json) - -class Parameter_Doc(Property_Doc): - def __init__(self, json, tag): - Property_Doc.__init__(self, json, tag) - self.properties_json = json.get('props', None) - - def render_subcomponents(self): - if not self.properties_json: - return '' - text = ''.join([render_comp(Property_Doc(property_json, 'div')) \ - for property_json in self.properties_json]) - return text - -def render_object_contents(json, tag = 'div', comp_tag = 'div'): - ctors = json.get('constructors', None) - text = render_comp_group(ctors, 'Constructors', Function_Doc, tag, comp_tag) - methods = json.get('methods', None) - text += render_comp_group(methods, 'Methods', Function_Doc, tag, comp_tag) - properties = json.get('properties', None) - text += render_comp_group(properties, 'Properties', Property_Doc, tag, comp_tag) - events = json.get('events', None) - text += render_comp_group(events, 'Events', Event_Doc, tag, comp_tag) - return text - -def render_comp(component): - # a component is wrapped inside a single div marked 'API_COMPONENT' - # containing: - # 1) the component name, marked 'API_NAME' - text = tag_wrap(component.render_name(), API_NAME, component.get_tag(), True) - # 2) the component description - text += component.render_description() - # 3) the component contents - text += component.render_subcomponents() - return tag_wrap(text, API_COMPONENT) - -def render_comp_group(group, group_name, ctor, tag = 'div', comp_tag = 'div'): - if not group: - return '' - # component group is a list of components in a single div called - # 'API_COMPONENT_GROUP' containing: - # 1) a title for the group marked with 'API_HEADER' - text = tag_wrap(group_name, API_HEADER, tag, True) - # 2) each component - text += ''.join([render_comp(ctor(api, comp_tag)) for api in group]) - return tag_wrap(text, API_COMPONENT_GROUP) - -def render_descriptions(descriptions_md): - text = ''.join([description_md for description_md in descriptions_md]) - return tag_wrap(markdown.markdown(text), MODULE_DESCRIPTION) - -def render_api_reference(api_docs): - if (len(api_docs) == 0): - return '' - # at the top level api reference is in a single div marked 'API_REFERENCE', - # containing: - # 1) a title 'API Reference' marked with 'API_HEADER' - text = tag_wrap('API Reference', API_HEADER, 'h2', True) - # 2) a component group called 'Classes' containing any class elements - classes = [api for api in api_docs if api['type'] == 'class'] - text += render_comp_group(classes, 'Classes', Class_Doc, 'h3', 'h4') - # 3) a component group called 'Functions' containing any global functions - functions = [api for api in api_docs if api['type'] == 'function'] - text += render_comp_group(functions, 'Functions', Function_Doc, 'h3', 'h4') - # 4) a component group called 'Properties' containing any global properties - properties = [api for api in api_docs if api['type'] == 'property'] - text += render_comp_group(properties, 'Properties', Property_Doc, 'h3', 'h4') - # 5) a component group called 'Events' containing any global events - events = [api for api in api_docs if api['type'] == 'event'] - text += render_comp_group(events, 'Events', Event_Doc, 'h3', 'h4') - return tag_wrap(text, API_REFERENCE) - -# take the JSON output of apiparser -# return the HTML DIV containing the rendered component -def json_to_div(json, markdown_filename, module_name): - descriptions = [hunk[1] for hunk in json if hunk[0]=='markdown'] - api_docs = [hunk[1] for hunk in json if hunk[0]=='api-json'] - text = "

" + module_name + "

" - text += render_descriptions(descriptions) - text += render_api_reference(api_docs) - text = tag_wrap_id(text, MODULE_API_DOCS_CLASS, \ - module_name + MODULE_API_DOCS_ID) - return text.encode('utf8') - -# take the JSON output of apiparser -# return standalone HTML containing the rendered component -def json_to_html(json, markdown_filename): - return indent(HTML_HEADER + \ - json_to_div(json, markdown_filename) + HTML_FOOTER) - -# take the name of a Markdown file -# return the HTML DIV containing the rendered component -def md_to_div(markdown_filename, module_name): - markdown_contents = open(markdown_filename).read().decode('utf8') - json = list(apiparser.parse_hunks(markdown_contents)) - return json_to_div(json, markdown_filename, module_name) - -# take the name of a Markdown file -# return standalone HTML containing the rendered component -def md_to_html(markdown_filename, module_name): - return indent(HTML_HEADER + md_to_div(markdown_filename, module_name) + HTML_FOOTER) - -if __name__ == '__main__': - if (len(sys.argv) == 0): - print 'Supply the name of a docs file to parse' - else: - print md_to_html(sys.argv[1]) diff --git a/addon-sdk/source/python-lib/cuddlefish/docs/documentationitem.py b/addon-sdk/source/python-lib/cuddlefish/docs/documentationitem.py deleted file mode 100755 index 982b47e5d565..000000000000 --- a/addon-sdk/source/python-lib/cuddlefish/docs/documentationitem.py +++ /dev/null @@ -1,164 +0,0 @@ -# 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 sys, os, re, simplejson - -class DocumentationItemInfo(object): - def __init__(self, env_root, md_path, filename): - self.env_root = env_root - # full path to MD file, without filename - self.source_path = md_path - # MD filename - self.source_filename = filename - - def env_root(self): - return self.env_root - - def source_path(self): - return self.source_path - - def source_filename(self): - return self.source_filename - - def base_filename(self): - return self.source_filename[:-len(".md")] - - def source_path_and_filename(self): - return os.sep.join([self.source_path, self.source_filename]) - - def source_path_relative_from_env_root(self): - return self.source_path[len(self.env_root) + 1:] - -class DevGuideItemInfo(DocumentationItemInfo): - def __init__(self, env_root, devguide_root, md_path, filename): - DocumentationItemInfo.__init__(self, env_root, md_path, filename) - self.devguide_root = devguide_root - - def source_path_relative_from_devguide_root(self): - return self.source_path[len(self.devguide_root) + 1:] - - def destination_path(self): - root_pieces = self.devguide_root.split(os.sep) - root_pieces[-1] = "dev-guide" - return os.sep.join([os.sep.join(root_pieces), self.source_path_relative_from_devguide_root()]) - -class ModuleInfo(DocumentationItemInfo): - def __init__(self, env_root, module_root, md_path, filename): - DocumentationItemInfo.__init__(self, env_root, md_path, filename) - self.module_root = module_root - self.metadata = self.get_metadata() - - def remove_comments(self, text): - def replacer(match): - s = match.group(0) - if s.startswith('/'): - return "" - else: - return s - pattern = re.compile( - r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"', - re.DOTALL | re.MULTILINE - ) - return re.sub(pattern, replacer, text) - - def get_metadata(self): - if self.level() == "third-party": - return simplejson.loads("{}") - try: - js = unicode(open(self.js_module_path(),"r").read(), 'utf8') - except IOError: - raise Exception, "JS module: '" + path_to_js + \ - "', corresponding to documentation file: '"\ - + self.source_path_and_filename() + "' wasn't found" - js = self.remove_comments(js) - js_lines = js.splitlines(True) - metadata = '' - reading_metadata = False - for line in js_lines: - if reading_metadata: - if line.startswith("};"): - break - metadata += line - continue - if line.startswith("module.metadata"): - reading_metadata = True - metadata = metadata.replace("'", '"') - return simplejson.loads("{" + metadata + "}") - - def source_path_relative_from_module_root(self): - return self.source_path[len(self.module_root) + 1:] - - def destination_path(self): - if self.level() == "third-party": - return os.sep.join([self.env_root, "doc", "modules", "packages"]) - root_pieces = self.module_root.split(os.sep) - root_pieces[-1] = "modules" - relative_pieces = self.source_path_relative_from_module_root().split(os.sep) - return os.sep.join(root_pieces + relative_pieces) - - def js_module_path(self): - return os.path.join(self.env_root, "lib", \ - self.source_path_relative_from_module_root(), \ - self.source_filename[:-len(".md")] + ".js") - - def relative_url(self): - if self.level() == "third-party": - relative_pieces = ["packages"] - else: - relative_pieces = self.source_path_relative_from_module_root().split(os.sep) - return "/".join(relative_pieces) + "/" + self.base_filename() + ".html" - - def name(self): - if os.sep.join([self.module_root, "sdk"]) == self.source_path or self.level() == "third-party": - return self.source_filename[:-3] - else: - path_from_root_pieces = self.source_path_relative_from_module_root().split(os.sep) - return "/".join(["/".join(path_from_root_pieces[1:]), self.source_filename[:-len(".md")]]) - - def level(self): - if self.source_path_relative_from_env_root().startswith("packages"): - return "third-party" - else: - if os.sep.join([self.module_root, "sdk"]) == self.source_path: - return "high" - else: - return "low" - -def get_modules_in_package(env_root, package_docs_dir, module_list, ignore_files_in_root): - for (dirpath, dirnames, filenames) in os.walk(package_docs_dir): - for filename in filenames: - # ignore files in the root - if ignore_files_in_root and package_docs_dir == dirpath: - continue - if filename.endswith(".md"): - module_list.append(ModuleInfo(env_root, package_docs_dir, dirpath, filename)) - -def get_module_list(env_root): - module_list = [] - # get the built-in modules - module_root = os.sep.join([env_root, "doc", "module-source"]) - get_modules_in_package(env_root, module_root, module_list, True) - # get the third-party modules - packages_root = os.sep.join([env_root, "packages"]) - if os.path.exists(packages_root): - for entry in os.listdir(packages_root): - if os.path.isdir(os.sep.join([packages_root, entry])): - package_docs = os.sep.join([packages_root, entry, "docs"]) - if os.path.exists(package_docs): - get_modules_in_package(env_root, package_docs, module_list, False) - module_list.sort(key=lambda x: x.name()) - return module_list - -def get_devguide_list(env_root): - devguide_list = [] - devguide_root = os.sep.join([env_root, "doc", "dev-guide-source"]) - for (dirpath, dirnames, filenames) in os.walk(devguide_root): - for filename in filenames: - if filename.endswith(".md"): - devguide_list.append(DevGuideItemInfo(env_root, devguide_root, dirpath, filename)) - return devguide_list - -if __name__ == "__main__": - module_list = get_module_list(sys.argv[1]) - print [module_info.name for module_info in module_list] diff --git a/addon-sdk/source/python-lib/cuddlefish/docs/generate.py b/addon-sdk/source/python-lib/cuddlefish/docs/generate.py deleted file mode 100755 index 6140951e99c2..000000000000 --- a/addon-sdk/source/python-lib/cuddlefish/docs/generate.py +++ /dev/null @@ -1,198 +0,0 @@ -# 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 sys -import shutil -import hashlib -import tarfile -import StringIO - -from cuddlefish._version import get_versions -from cuddlefish.docs import apiparser -from cuddlefish.docs import apirenderer -from cuddlefish.docs import webdocs -from documentationitem import get_module_list -from documentationitem import get_devguide_list -from documentationitem import ModuleInfo -from documentationitem import DevGuideItemInfo -from linkrewriter import rewrite_links -import simplejson as json - -DIGEST = "status.md5" -TGZ_FILENAME = "addon-sdk-docs.tgz" - -def get_sdk_docs_path(env_root): - return os.path.join(env_root, "doc") - -def get_base_url(env_root): - sdk_docs_path = get_sdk_docs_path(env_root).lstrip("/") - return "file://"+"/"+"/".join(sdk_docs_path.split(os.sep))+"/" - -def clean_generated_docs(docs_dir): - status_file = os.path.join(docs_dir, "status.md5") - if os.path.exists(status_file): - os.remove(status_file) - index_file = os.path.join(docs_dir, "index.html") - if os.path.exists(index_file): - os.remove(index_file) - dev_guide_dir = os.path.join(docs_dir, "dev-guide") - if os.path.exists(dev_guide_dir): - shutil.rmtree(dev_guide_dir) - api_doc_dir = os.path.join(docs_dir, "modules") - if os.path.exists(api_doc_dir): - shutil.rmtree(api_doc_dir) - -def generate_static_docs(env_root, override_version=get_versions()["version"]): - clean_generated_docs(get_sdk_docs_path(env_root)) - generate_docs(env_root, override_version, stdout=StringIO.StringIO()) - tgz = tarfile.open(TGZ_FILENAME, 'w:gz') - tgz.add(get_sdk_docs_path(env_root), "doc") - tgz.close() - return TGZ_FILENAME - -def generate_local_docs(env_root): - return generate_docs(env_root, get_versions()["version"], get_base_url(env_root)) - -def generate_named_file(env_root, filename_and_path): - module_list = get_module_list(env_root) - web_docs = webdocs.WebDocs(env_root, module_list, get_versions()["version"], get_base_url(env_root)) - abs_path = os.path.abspath(filename_and_path) - path, filename = os.path.split(abs_path) - if abs_path.startswith(os.path.join(env_root, 'doc', 'module-source')): - module_root = os.sep.join([env_root, "doc", "module-source"]) - module_info = ModuleInfo(env_root, module_root, path, filename) - write_module_doc(env_root, web_docs, module_info, False) - elif abs_path.startswith(os.path.join(get_sdk_docs_path(env_root), 'dev-guide-source')): - devguide_root = os.sep.join([env_root, "doc", "dev-guide-source"]) - devguideitem_info = DevGuideItemInfo(env_root, devguide_root, path, filename) - write_devguide_doc(env_root, web_docs, devguideitem_info, False) - else: - raise ValueError("Not a valid path to a documentation file") - -def generate_docs(env_root, version=get_versions()["version"], base_url=None, stdout=sys.stdout): - docs_dir = get_sdk_docs_path(env_root) - # if the generated docs don't exist, generate everything - if not os.path.exists(os.path.join(docs_dir, "dev-guide")): - print >>stdout, "Generating documentation..." - generate_docs_from_scratch(env_root, version, base_url) - current_status = calculate_current_status(env_root) - open(os.path.join(docs_dir, DIGEST), "w").write(current_status) - else: - current_status = calculate_current_status(env_root) - previous_status_file = os.path.join(docs_dir, DIGEST) - docs_are_up_to_date = False - if os.path.exists(previous_status_file): - docs_are_up_to_date = current_status == open(previous_status_file, "r").read() - # if the docs are not up to date, generate everything - if not docs_are_up_to_date: - print >>stdout, "Regenerating documentation..." - generate_docs_from_scratch(env_root, version, base_url) - open(os.path.join(docs_dir, DIGEST), "w").write(current_status) - return get_base_url(env_root) + "index.html" - -# this function builds a hash of the name and last modification date of: -# * every file in "doc/sdk" which ends in ".md" -# * every file in "doc/dev-guide-source" which ends in ".md" -# * every file in "doc/static-files" which does not start with "." -def calculate_current_status(env_root): - docs_dir = get_sdk_docs_path(env_root) - current_status = hashlib.md5() - module_src_dir = os.path.join(env_root, "doc", "module-source") - for (dirpath, dirnames, filenames) in os.walk(module_src_dir): - for filename in filenames: - if filename.endswith(".md"): - current_status.update(filename) - current_status.update(str(os.path.getmtime(os.path.join(dirpath, filename)))) - guide_src_dir = os.path.join(docs_dir, "dev-guide-source") - for (dirpath, dirnames, filenames) in os.walk(guide_src_dir): - for filename in filenames: - if filename.endswith(".md"): - current_status.update(filename) - current_status.update(str(os.path.getmtime(os.path.join(dirpath, filename)))) - package_dir = os.path.join(env_root, "packages") - for (dirpath, dirnames, filenames) in os.walk(package_dir): - for filename in filenames: - if filename.endswith(".md"): - current_status.update(filename) - current_status.update(str(os.path.getmtime(os.path.join(dirpath, filename)))) - base_html_file = os.path.join(docs_dir, "static-files", "base.html") - current_status.update(base_html_file) - current_status.update(str(os.path.getmtime(os.path.join(dirpath, base_html_file)))) - return current_status.digest() - -def generate_docs_from_scratch(env_root, version, base_url): - docs_dir = get_sdk_docs_path(env_root) - module_list = get_module_list(env_root) - web_docs = webdocs.WebDocs(env_root, module_list, version, base_url) - must_rewrite_links = True - if base_url: - must_rewrite_links = False - clean_generated_docs(docs_dir) - - # py2.5 doesn't have ignore=, so we delete tempfiles afterwards. If we - # required >=py2.6, we could use ignore=shutil.ignore_patterns("*~") - for (dirpath, dirnames, filenames) in os.walk(docs_dir): - for n in filenames: - if n.endswith("~"): - os.unlink(os.path.join(dirpath, n)) - - # generate api docs for all modules - if not os.path.exists(os.path.join(docs_dir, "modules")): - os.mkdir(os.path.join(docs_dir, "modules")) - [write_module_doc(env_root, web_docs, module_info, must_rewrite_links) for module_info in module_list] - - # generate third-party module index - third_party_index_file = os.sep.join([env_root, "doc", "module-source", "third-party-modules.md"]) - third_party_module_list = [module_info for module_info in module_list if module_info.level() == "third-party"] - write_module_index(env_root, web_docs, third_party_index_file, third_party_module_list, must_rewrite_links) - - - # generate high-level module index - high_level_index_file = os.sep.join([env_root, "doc", "module-source", "high-level-modules.md"]) - high_level_module_list = [module_info for module_info in module_list if module_info.level() == "high"] - write_module_index(env_root, web_docs, high_level_index_file, high_level_module_list, must_rewrite_links) - - # generate low-level module index - low_level_index_file = os.sep.join([env_root, "doc", "module-source", "low-level-modules.md"]) - low_level_module_list = [module_info for module_info in module_list if module_info.level() == "low"] - write_module_index(env_root, web_docs, low_level_index_file, low_level_module_list, must_rewrite_links) - - # generate dev-guide docs - devguide_list = get_devguide_list(env_root) - [write_devguide_doc(env_root, web_docs, devguide_info, must_rewrite_links) for devguide_info in devguide_list] - - # make /md/dev-guide/welcome.html the top level index file - doc_html = web_docs.create_guide_page(os.path.join(docs_dir, 'dev-guide-source', 'index.md')) - write_file(env_root, doc_html, docs_dir, 'index', False) - -def write_module_index(env_root, web_docs, source_file, module_list, must_rewrite_links): - doc_html = web_docs.create_module_index(source_file, module_list) - base_filename, extension = os.path.splitext(os.path.basename(source_file)) - destination_path = os.sep.join([env_root, "doc", "modules"]) - write_file(env_root, doc_html, destination_path, base_filename, must_rewrite_links) - -def write_module_doc(env_root, web_docs, module_info, must_rewrite_links): - doc_html = web_docs.create_module_page(module_info) - write_file(env_root, doc_html, module_info.destination_path(), module_info.base_filename(), must_rewrite_links) - -def write_devguide_doc(env_root, web_docs, devguide_info, must_rewrite_links): - doc_html = web_docs.create_guide_page(devguide_info.source_path_and_filename()) - write_file(env_root, doc_html, devguide_info.destination_path(), devguide_info.base_filename(), must_rewrite_links) - -def write_file(env_root, doc_html, dest_dir, filename, must_rewrite_links): - if not os.path.exists(dest_dir): - os.makedirs(dest_dir) - dest_path_html = os.path.join(dest_dir, filename) + ".html" - replace_file(env_root, dest_path_html, doc_html, must_rewrite_links) - return dest_path_html - -def replace_file(env_root, dest_path, file_contents, must_rewrite_links): - if os.path.exists(dest_path): - os.remove(dest_path) - # before we copy the final version, we'll rewrite the links - # I'll do this last, just because we know definitely what the dest_path is at this point - if must_rewrite_links and dest_path.endswith(".html"): - file_contents = rewrite_links(env_root, get_sdk_docs_path(env_root), file_contents, dest_path) - open(dest_path, "w").write(file_contents) diff --git a/addon-sdk/source/python-lib/cuddlefish/docs/linkrewriter.py b/addon-sdk/source/python-lib/cuddlefish/docs/linkrewriter.py deleted file mode 100644 index 11b0f1110c3e..000000000000 --- a/addon-sdk/source/python-lib/cuddlefish/docs/linkrewriter.py +++ /dev/null @@ -1,78 +0,0 @@ -# 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 sys -import HTMLParser -import urlparse - -def rewrite_links(env_root, sdk_docs_path, page, dest_path): - dest_path_depth = len(dest_path.split(os.sep)) -1 # because dest_path includes filename - docs_root_depth = len(sdk_docs_path.split(os.sep)) - relative_depth = dest_path_depth - docs_root_depth - linkRewriter = LinkRewriter("../" * relative_depth) - return linkRewriter.rewrite_links(page) - -class LinkRewriter(HTMLParser.HTMLParser): - def __init__(self, link_prefix): - HTMLParser.HTMLParser.__init__(self) - self.stack = [] - self.link_prefix = link_prefix - - def rewrite_links(self, page): - self.feed(page) - self.close() - page = ''.join(self.stack) - self.stack = [] - return page - - def handle_decl(self, decl): - self.stack.append("") - - def handle_comment(self, decl): - self.stack.append("") - - def handle_starttag(self, tag, attrs): - self.stack.append(self.__html_start_tag(tag, self._rewrite_link(attrs))) - - def handle_entityref(self, name): - self.stack.append("&" + name + ";") - - def handle_endtag(self, tag): - self.stack.append(self.__html_end_tag(tag)) - - def handle_startendtag(self, tag, attrs): - self.stack.append(self.__html_startend_tag(tag, self._rewrite_link(attrs))) - - def _update_attribute(self, attr_name, attrs): - attr_value = attrs.get(attr_name, '') - if attr_value: - parsed = urlparse.urlparse(attr_value) - if not parsed.scheme: - attrs[attr_name] = self.link_prefix + attr_value - - def _rewrite_link(self, attrs): - attrs = dict(attrs) - self._update_attribute('href', attrs) - self._update_attribute('src', attrs) - self._update_attribute('action', attrs) - return attrs - - def handle_data(self, data): - self.stack.append(data) - - def __html_start_tag(self, tag, attrs): - return '<%s%s>' % (tag, self.__html_attrs(attrs)) - - def __html_startend_tag(self, tag, attrs): - return '<%s%s/>' % (tag, self.__html_attrs(attrs)) - - def __html_end_tag(self, tag): - return '' % (tag) - - def __html_attrs(self, attrs): - _attrs = '' - if attrs: - _attrs = ' %s' % (' '.join([('%s="%s"' % (k,v)) for k,v in dict(attrs).iteritems()])) - return _attrs diff --git a/addon-sdk/source/python-lib/cuddlefish/docs/renderapi.readme.md b/addon-sdk/source/python-lib/cuddlefish/docs/renderapi.readme.md deleted file mode 100644 index d12af3d4199b..000000000000 --- a/addon-sdk/source/python-lib/cuddlefish/docs/renderapi.readme.md +++ /dev/null @@ -1,209 +0,0 @@ - - - -This document describes the structure of the HTML generated by the renderapi.py -tool. The particular HTML id and class attributes embedded in the files, -as well as their organization, represent the interface between the tool and any -front-end code wanting to style the docs in some particular way. - -renderapi generates two sorts of files: - -- a file called ".div": this is the contents of the parsed -Markdown file rendered inside a well-defined DIV tag - -- a file called ".html": this is the DIV from above inserted into -a simple HTML template that references a sample CSS file which styles the -contents of the DIV. This CSS file is the same as the one used by the SDK -itself. - -DIV tags --------- -The following class and id attributes are used in the DIV: - -renderapi uses a number of class attributes and a single id attribute in the DIV: - -id attribute "_module_api_docs" -class attribute "api_reference" -class attribute "module_api_docs" -class attribute "api_header" -class attribute "api_name" -class attribute "api_component_group" -class attribute "api_component" -class attribute "datatype" -class attribute "returns" -class attribute "parameter_set" -class attribute "module_description" - -DIV structure -------------- -The top level DIV is marked with the id attribute and the "module_api_docs" class -attribute: - -
- //module doc contents -
- - -Inside this: - -- the first item is an

heading containing the name of the module: - -- all "markdown" hunks (that is, all descriptive text not occurring -inside tags) are rendered inside a DIV marked with the -"module-description" class attribute - -- all content is rendered, enclosed in a single tag marked -with the "api_reference" class attribute: - -
-
- //descriptions -
-
- //api reference -
-
- -If there is no content, then the "api-reference" section is absent. - -### API Reference structure ### - -The first item in API reference is an

heading title marked with the -"api_header" attribute. This might have the text content "API Reference" -(but you should not rely on that): - -
- -

API Reference

- - //api contents - -
- -After the title come one or more component groups. - -#### Component Group #### - -A component group is marked with the "api_component_group" attribute. The -component group is a collection of some sort of component: for example, a group -of classes, a group of functions, or a group of events. - -Each component group starts off with a header marked with the -"api_header" attribute and is followed by one or more sections marked with the -"api_component" attribute. -At the top level (that is, when they are directly under the "API Reference" -heading), the "api_header" items are

headings, otherwise they are divs. - -
- -

API Reference

- -
- -

Classes

- -
- // the first class -
- -
- // another class -
- -
- -
- //some different components - -

Functions

- -
- the first function -
- -
- another function -
- -
- -
- -#### Component #### - -API components represent actual objects in the API like classes, functions, -properties and events. - -Each component starts with a section marked with the -"api_name" tag, which includes the name of the component in the API: for -example "postMessage(message)". - -Components at the top level (i.e., directly under h3 headings) are

-headings, otherwise they are divs. - -After the name, the component's contents are listed. Different sorts of -components may have different sorts of contents: for example, a function might -have parameters. If the component is composite then it may contain its own -component group. For example, a class may contain methods and properties, -which might be grouped together. - -
- -

Panel

- -
- -
- Methods -
- -
- show() -
- -
- -
- -Other attributes ------------------------------ - -### Datatype ### -All primitive data types, like "string" and "number", are marked with the -"datatype" class attribute: - -
- -
- label : string -
- -

A required string description of the widget used for accessibility, - title bars, and error reporting.

- -
- -### Returns ### - -Functions mark return values with the "returns" class attribute. - -
- -
- get() -
- - Make a `GET` request. - -
- Returns: Request -
- -
- -### Parameter_set ### - -Functions that take parameters mark them with the parameter_set class -attribute. diff --git a/addon-sdk/source/python-lib/cuddlefish/docs/webdocs.py b/addon-sdk/source/python-lib/cuddlefish/docs/webdocs.py deleted file mode 100755 index 7436a1955b25..000000000000 --- a/addon-sdk/source/python-lib/cuddlefish/docs/webdocs.py +++ /dev/null @@ -1,111 +0,0 @@ -# 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, re, errno -import markdown -import cgi - -from cuddlefish import packaging -from cuddlefish.docs import apirenderer -from cuddlefish._version import get_versions - -INDEX_PAGE = '/doc/static-files/base.html' -BASE_URL_INSERTION_POINT = '\n' - return result - -def insert_after(target, insertion_point_id, text_to_insert): - insertion_point = target.find(insertion_point_id) + len(insertion_point_id) - return target[:insertion_point] + text_to_insert + target[insertion_point:] - -class WebDocs(object): - def __init__(self, root, module_list, version=get_versions()["version"], base_url = None): - self.root = root - self.module_list = module_list - self.version = version - self.pkg_cfg = packaging.build_pkg_cfg(root) - self.packages_json = packaging.build_pkg_index(self.pkg_cfg) - self.base_page = self._create_base_page(root, base_url) - - def create_guide_page(self, path): - md_content = unicode(open(path, 'r').read(), 'utf8') - guide_content = markdown.markdown(md_content) - return self._create_page(guide_content) - - def create_module_page(self, module_info): - path, ext = os.path.splitext(module_info.source_path_and_filename()) - md_path = path + '.md' - module_content = apirenderer.md_to_div(md_path, module_info.name()) - stability = module_info.metadata.get("stability", "undefined") - stability_note = tag_wrap(stability, "a", {"class":"stability-note stability-" + stability, \ - "href":"dev-guide/guides/stability.html"}) - module_content = stability_note + module_content - return self._create_page(module_content) - - def create_module_index(self, path, module_list): - md_content = unicode(open(path, 'r').read(), 'utf8') - index_content = markdown.markdown(md_content) - module_list_content = self._make_module_text(module_list) - index_content = insert_after(index_content, MODULE_INDEX_INSERTION_POINT, module_list_content) - return self._create_page(index_content) - - def _create_page(self, page_content): - page = self._insert_title(self.base_page, page_content) - page = insert_after(page, CONTENT_ID, page_content) - return page.encode('utf8') - - def _make_module_text(self, module_list): - module_text = '' - for module in module_list: - module_link = tag_wrap(module.name(), 'a', \ - {'href': "/".join(["modules", module.relative_url()])}) - module_list_item = tag_wrap(module_link, "li") - module_text += module_list_item - return module_text - - def _create_base_page(self, root, base_url): - base_page = unicode(open(root + INDEX_PAGE, 'r').read(), 'utf8') - if base_url: - base_tag = 'href="' + base_url + '"' - base_page = insert_after(base_page, BASE_URL_INSERTION_POINT, base_tag) - base_page = insert_after(base_page, VERSION_INSERTION_POINT, "Version " + self.version) - - third_party_module_list = [module_info for module_info in self.module_list if module_info.level() == "third-party"] - third_party_module_text = self._make_module_text(third_party_module_list) - base_page = insert_after(base_page, \ - THIRD_PARTY_MODULE_SUMMARIES, third_party_module_text) - - high_level_module_list = [module_info for module_info in self.module_list if module_info.level() == "high"] - high_level_module_text = self._make_module_text(high_level_module_list) - base_page = insert_after(base_page, \ - HIGH_LEVEL_MODULE_SUMMARIES, high_level_module_text) - - low_level_module_list = [module_info for module_info in self.module_list if module_info.level() == "low"] - low_level_module_text = self._make_module_text(low_level_module_list) - base_page = insert_after(base_page, \ - LOW_LEVEL_MODULE_SUMMARIES, low_level_module_text) - return base_page - - def _insert_title(self, target, content): - match = re.search('

.*

', content) - if match: - title = match.group(0)[len('

'):-len('

')] + ' - ' + \ - DEFAULT_TITLE - else: - title = DEFAULT_TITLE - target = insert_after(target, TITLE_ID, title) - return target diff --git a/addon-sdk/source/python-lib/cuddlefish/options_defaults.py b/addon-sdk/source/python-lib/cuddlefish/options_defaults.py index 5931a26ce1e6..4f8d1e4780c9 100644 --- a/addon-sdk/source/python-lib/cuddlefish/options_defaults.py +++ b/addon-sdk/source/python-lib/cuddlefish/options_defaults.py @@ -2,7 +2,7 @@ # 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/. -def parse_options_defaults(options, jetpack_id): +def parse_options_defaults(options, preferencesBranch): # this returns a unicode string pref_list = [] @@ -21,6 +21,6 @@ def parse_options_defaults(options, jetpack_id): else: value = str(pref["value"]) - pref_list.append("pref(\"extensions." + jetpack_id + "." + pref["name"] + "\", " + value + ");") + pref_list.append("pref(\"extensions." + preferencesBranch + "." + pref["name"] + "\", " + value + ");") return "\n".join(pref_list) + "\n" diff --git a/addon-sdk/source/python-lib/cuddlefish/options_xul.py b/addon-sdk/source/python-lib/cuddlefish/options_xul.py index a82c77d83774..1d8af2ff36ca 100644 --- a/addon-sdk/source/python-lib/cuddlefish/options_xul.py +++ b/addon-sdk/source/python-lib/cuddlefish/options_xul.py @@ -45,7 +45,7 @@ def validate_prefs(options): # TODO: Check that pref["type"] matches default value type -def parse_options(options, jetpack_id): +def parse_options(options, jetpack_id, preferencesBranch): doc = Document() root = doc.createElement("vbox") root.setAttribute("xmlns", "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul") @@ -58,7 +58,7 @@ def parse_options(options, jetpack_id): setting = doc.createElement("setting") setting.setAttribute("pref-name", pref["name"]) setting.setAttribute("data-jetpack-id", jetpack_id) - setting.setAttribute("pref", "extensions." + jetpack_id + "." + pref["name"]) + setting.setAttribute("pref", "extensions." + preferencesBranch + "." + pref["name"]) setting.setAttribute("type", pref["type"]) setting.setAttribute("title", pref["title"]) diff --git a/addon-sdk/source/python-lib/cuddlefish/packaging.py b/addon-sdk/source/python-lib/cuddlefish/packaging.py index 113679e2bc88..557070ffd1db 100644 --- a/addon-sdk/source/python-lib/cuddlefish/packaging.py +++ b/addon-sdk/source/python-lib/cuddlefish/packaging.py @@ -396,6 +396,21 @@ def generate_build_for_target(pkg_cfg, target, deps, if ('preferences' in target_cfg): build['preferences'] = target_cfg.preferences + if 'id' in target_cfg: + # NOTE: logic duplicated from buildJID() + jid = target_cfg['id'] + if not ('@' in jid or jid.startswith('{')): + jid += '@jetpack' + build['preferencesBranch'] = jid + + if 'preferences-branch' in target_cfg: + # check it's a non-empty, valid branch name + preferencesBranch = target_cfg['preferences-branch'] + if re.match('^[\w{@}-]+$', preferencesBranch): + build['preferencesBranch'] = preferencesBranch + elif not is_running_tests: + print >>sys.stderr, "IGNORING preferences-branch (not a valid branch name)" + return build def _get_files_in_dir(path): diff --git a/addon-sdk/source/python-lib/cuddlefish/tests/__init__.py b/addon-sdk/source/python-lib/cuddlefish/tests/__init__.py index c1a7fd231763..60e7e88b3c31 100644 --- a/addon-sdk/source/python-lib/cuddlefish/tests/__init__.py +++ b/addon-sdk/source/python-lib/cuddlefish/tests/__init__.py @@ -35,19 +35,6 @@ def get_tests(): if len(test.examples) > 0: tests.append(doctest.DocTestCase(test)) - md_dir = os.path.join(env_root, 'dev-guide') - doctest_opts = (doctest.NORMALIZE_WHITESPACE | - doctest.REPORT_UDIFF) - for dirpath, dirnames, filenames in os.walk(md_dir): - for filename in filenames: - if filename.endswith('.md'): - absname = os.path.join(dirpath, filename) - tests.append(doctest.DocFileTest( - absname, - module_relative=False, - optionflags=doctest_opts - )) - return tests def run(verbose=False): diff --git a/addon-sdk/source/python-lib/cuddlefish/tests/preferences-files/packages/curly-id/lib/main.js b/addon-sdk/source/python-lib/cuddlefish/tests/preferences-files/packages/curly-id/lib/main.js new file mode 100644 index 000000000000..b7e0a1d5ef11 --- /dev/null +++ b/addon-sdk/source/python-lib/cuddlefish/tests/preferences-files/packages/curly-id/lib/main.js @@ -0,0 +1,4 @@ +/* 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/. */ + diff --git a/addon-sdk/source/python-lib/cuddlefish/tests/preferences-files/packages/curly-id/package.json b/addon-sdk/source/python-lib/cuddlefish/tests/preferences-files/packages/curly-id/package.json new file mode 100644 index 000000000000..1d47b4d812a3 --- /dev/null +++ b/addon-sdk/source/python-lib/cuddlefish/tests/preferences-files/packages/curly-id/package.json @@ -0,0 +1,14 @@ +{ + "id": "{34a1eae1-c20a-464f-9b0e-000000000000}", + "fullName": "curly ID test", + "author": "Tomislav Jovanovic", + + "preferences": [{ + "name": "test13", + "type": "integer", + "title": "test13", + "value": 26 + }], + + "preferences-branch": "invalid^branch*name" +} diff --git a/addon-sdk/source/python-lib/cuddlefish/tests/preferences-files/packages/preferences-branch/lib/main.js b/addon-sdk/source/python-lib/cuddlefish/tests/preferences-files/packages/preferences-branch/lib/main.js new file mode 100644 index 000000000000..b7e0a1d5ef11 --- /dev/null +++ b/addon-sdk/source/python-lib/cuddlefish/tests/preferences-files/packages/preferences-branch/lib/main.js @@ -0,0 +1,4 @@ +/* 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/. */ + diff --git a/addon-sdk/source/python-lib/cuddlefish/tests/preferences-files/packages/preferences-branch/package.json b/addon-sdk/source/python-lib/cuddlefish/tests/preferences-files/packages/preferences-branch/package.json new file mode 100644 index 000000000000..b738db7d0669 --- /dev/null +++ b/addon-sdk/source/python-lib/cuddlefish/tests/preferences-files/packages/preferences-branch/package.json @@ -0,0 +1,14 @@ +{ + "id": "test-preferences-branch", + "fullName": "preferences-branch test", + "author": "Tomislav Jovanovic", + + "preferences": [{ + "name": "test42", + "type": "bool", + "title": "test42", + "value": true + }], + + "preferences-branch": "human-readable" +} diff --git a/addon-sdk/source/python-lib/cuddlefish/tests/test_packaging.py b/addon-sdk/source/python-lib/cuddlefish/tests/test_packaging.py index ccd281293a0c..6944dc394267 100644 --- a/addon-sdk/source/python-lib/cuddlefish/tests/test_packaging.py +++ b/addon-sdk/source/python-lib/cuddlefish/tests/test_packaging.py @@ -22,7 +22,8 @@ def get_configs(pkg_name, dirname='static-files'): build = packaging.generate_build_for_target( pkg_cfg=pkg_cfg, target=pkg_name, - deps=deps + deps=deps, + is_running_tests=True, ) return Bunch(target_cfg=target_cfg, pkg_cfg=pkg_cfg, build=build) diff --git a/addon-sdk/source/python-lib/cuddlefish/tests/test_xpi.py b/addon-sdk/source/python-lib/cuddlefish/tests/test_xpi.py index d8adcf7c81ff..fc217cdc3994 100644 --- a/addon-sdk/source/python-lib/cuddlefish/tests/test_xpi.py +++ b/addon-sdk/source/python-lib/cuddlefish/tests/test_xpi.py @@ -40,10 +40,11 @@ class PrefsTests(unittest.TestCase): def testPackageWithSimplePrefs(self): self.makexpi('simple-prefs') + packageName = 'jid1-fZHqN9JfrDBa8A@jetpack' self.failUnless('options.xul' in self.xpi.namelist()) optsxul = self.xpi.read('options.xul').decode("utf-8") - self.failUnlessEqual(self.xpi_harness_options["jetpackID"], - "jid1-fZHqN9JfrDBa8A@jetpack") + self.failUnlessEqual(self.xpi_harness_options["jetpackID"], packageName) + self.failUnlessEqual(self.xpi_harness_options["preferencesBranch"], packageName) root = ElementTree.XML(optsxul.encode('utf-8')) @@ -53,7 +54,6 @@ class PrefsTests(unittest.TestCase): settings = root.findall(xulNamespacePrefix + 'setting') def assertPref(setting, name, prefType, title): - packageName = 'jid1-fZHqN9JfrDBa8A@jetpack' self.failUnlessEqual(setting.get('data-jetpack-id'), packageName) self.failUnlessEqual(setting.get('pref'), 'extensions.' + packageName + '.' + name) @@ -88,6 +88,25 @@ class PrefsTests(unittest.TestCase): ] self.failUnlessEqual(prefsjs, "\n".join(exp)+"\n") + def testPackageWithPreferencesBranch(self): + self.makexpi('preferences-branch') + self.failUnless('options.xul' in self.xpi.namelist()) + optsxul = self.xpi.read('options.xul').decode("utf-8") + self.failUnlessEqual(self.xpi_harness_options["preferencesBranch"], + "human-readable") + + root = ElementTree.XML(optsxul.encode('utf-8')) + xulNamespacePrefix = \ + "{http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul}" + + setting = root.find(xulNamespacePrefix + 'setting') + self.failUnlessEqual(setting.get('pref'), + 'extensions.human-readable.test42') + + prefsjs = self.xpi.read('defaults/preferences/prefs.js').decode("utf-8") + self.failUnlessEqual(prefsjs, + 'pref("extensions.human-readable.test42", true);\n') + def testPackageWithNoPrefs(self): self.makexpi('no-prefs') self.failIf('options.xul' in self.xpi.namelist()) @@ -96,6 +115,33 @@ class PrefsTests(unittest.TestCase): prefsjs = self.xpi.read('defaults/preferences/prefs.js').decode("utf-8") self.failUnlessEqual(prefsjs, "") + def testPackageWithInvalidPreferencesBranch(self): + self.makexpi('curly-id') + self.failIfEqual(self.xpi_harness_options["preferencesBranch"], + "invalid^branch*name") + self.failUnlessEqual(self.xpi_harness_options["preferencesBranch"], + "{34a1eae1-c20a-464f-9b0e-000000000000}") + + def testPackageWithCurlyID(self): + self.makexpi('curly-id') + self.failUnlessEqual(self.xpi_harness_options["jetpackID"], + "{34a1eae1-c20a-464f-9b0e-000000000000}") + + self.failUnless('options.xul' in self.xpi.namelist()) + optsxul = self.xpi.read('options.xul').decode("utf-8") + + root = ElementTree.XML(optsxul.encode('utf-8')) + xulNamespacePrefix = \ + "{http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul}" + + setting = root.find(xulNamespacePrefix + 'setting') + self.failUnlessEqual(setting.get('pref'), + 'extensions.{34a1eae1-c20a-464f-9b0e-000000000000}.test13') + + prefsjs = self.xpi.read('defaults/preferences/prefs.js').decode("utf-8") + self.failUnlessEqual(prefsjs, + 'pref("extensions.{34a1eae1-c20a-464f-9b0e-000000000000}.test13", 26);\n') + class Bug588119Tests(unittest.TestCase): def makexpi(self, pkg_name): diff --git a/addon-sdk/source/python-lib/cuddlefish/xpi.py b/addon-sdk/source/python-lib/cuddlefish/xpi.py index 6cc365f3cc14..f71f9e495e36 100644 --- a/addon-sdk/source/python-lib/cuddlefish/xpi.py +++ b/addon-sdk/source/python-lib/cuddlefish/xpi.py @@ -77,14 +77,15 @@ def build_xpi(template_root_dir, manifest, xpi_path, validate_prefs(harness_options["preferences"]) opts_xul = parse_options(harness_options["preferences"], - harness_options["jetpackID"]) + harness_options["jetpackID"], + harness_options["preferencesBranch"]) open('.options.xul', 'wb').write(opts_xul.encode("utf-8")) zf.write('.options.xul', 'options.xul') os.remove('.options.xul') from options_defaults import parse_options_defaults prefs_js = parse_options_defaults(harness_options["preferences"], - harness_options["jetpackID"]) + harness_options["preferencesBranch"]) open('.prefs.js', 'wb').write(prefs_js.encode("utf-8")) else: diff --git a/addon-sdk/source/python-lib/markdown/AUTHORS b/addon-sdk/source/python-lib/markdown/AUTHORS deleted file mode 100644 index cfe2b347828c..000000000000 --- a/addon-sdk/source/python-lib/markdown/AUTHORS +++ /dev/null @@ -1,43 +0,0 @@ -Primary Authors -=============== - -Yuri Takteyev , who has written much of the current code -while procrastingating his Ph.D. - -Waylan Limberg , who has written most of the available -extensions and later was asked to join Yuri, fixing nummrious bugs, adding -documentation and making general improvements to the existing codebase, -included a complete refactor of the core. - -Artem Yunusov, who as part of a 2008 GSoC project, has refactored inline -patterns, replaced the NanoDOM with ElementTree support and made various other -improvements. - -Manfed Stienstra , who wrote the original version of -the script and is responsible for various parts of the existing codebase. - -David Wolever, who refactored the extension API and made other improvements -as he helped to integrate Markdown into Dr.Project. - -Other Contributors -================== - -The incomplete list of individuals below have provided patches or otherwise -contributed to the project in various ways. We would like to thank everyone -who has contributed to the progect in any way. - -Eric Abrahamsen -Jeff Balogh -Sergej Chodarev -Chris Clark -Tiago Cogumbreiro -Kjell Magne Fauske -G. Clark Haynes -Daniel Krech -Steward Midwinter -Jack Miller -Neale Pickett -John Szakmeister -Malcolm Tredinnick -Ben Wilson -and many others who helped by reporting bugs diff --git a/addon-sdk/source/python-lib/markdown/LICENSE b/addon-sdk/source/python-lib/markdown/LICENSE deleted file mode 100644 index 4cd8b1455d7d..000000000000 --- a/addon-sdk/source/python-lib/markdown/LICENSE +++ /dev/null @@ -1,30 +0,0 @@ -Copyright 2007, 2008 The Python Markdown Project (v. 1.7 and later) -Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) -Copyright 2004 Manfred Stienstra (the original version) - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -* Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. -* Neither the name of the nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE PYTHON MARKDOWN PROJECT ''AS IS'' AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL ANY CONTRIBUTORS TO THE PYTHON MARKDOWN PROJECT -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - diff --git a/addon-sdk/source/python-lib/markdown/__init__.py b/addon-sdk/source/python-lib/markdown/__init__.py deleted file mode 100644 index 09a225e88946..000000000000 --- a/addon-sdk/source/python-lib/markdown/__init__.py +++ /dev/null @@ -1,603 +0,0 @@ -""" -Python Markdown -=============== - -Python Markdown converts Markdown to HTML and can be used as a library or -called from the command line. - -## Basic usage as a module: - - import markdown - md = Markdown() - html = md.convert(your_text_string) - -## Basic use from the command line: - - python markdown.py source.txt > destination.html - -Run "python markdown.py --help" to see more options. - -## Extensions - -See for more -information and instructions on how to extend the functionality of -Python Markdown. Read that before you try modifying this file. - -## Authors and License - -Started by [Manfred Stienstra](http://www.dwerg.net/). Continued and -maintained by [Yuri Takhteyev](http://www.freewisdom.org), [Waylan -Limberg](http://achinghead.com/) and [Artem Yunusov](http://blog.splyer.com). - -Contact: markdown@freewisdom.org - -Copyright 2007, 2008 The Python Markdown Project (v. 1.7 and later) -Copyright 200? Django Software Foundation (OrderedDict implementation) -Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) -Copyright 2004 Manfred Stienstra (the original version) - -License: BSD (see docs/LICENSE for details). -""" - -version = "2.0" -version_info = (2,0,0, "Final") - -import re -import codecs -import sys -import warnings -import logging -from logging import DEBUG, INFO, WARN, ERROR, CRITICAL - - -""" -CONSTANTS -============================================================================= -""" - -""" -Constants you might want to modify ------------------------------------------------------------------------------ -""" - -# default logging level for command-line use -COMMAND_LINE_LOGGING_LEVEL = CRITICAL -TAB_LENGTH = 4 # expand tabs to this many spaces -ENABLE_ATTRIBUTES = True # @id = xyz -> <... id="xyz"> -SMART_EMPHASIS = True # this_or_that does not become thisorthat -DEFAULT_OUTPUT_FORMAT = 'xhtml1' # xhtml or html4 output -HTML_REMOVED_TEXT = "[HTML_REMOVED]" # text used instead of HTML in safe mode -BLOCK_LEVEL_ELEMENTS = re.compile("p|div|h[1-6]|blockquote|pre|table|dl|ol|ul" - "|script|noscript|form|fieldset|iframe|math" - "|ins|del|hr|hr/|style|li|dt|dd|thead|tbody" - "|tr|th|td") -DOC_TAG = "div" # Element used to wrap document - later removed - -# Placeholders -STX = u'\u0002' # Use STX ("Start of text") for start-of-placeholder -ETX = u'\u0003' # Use ETX ("End of text") for end-of-placeholder -INLINE_PLACEHOLDER_PREFIX = STX+"klzzwxh:" -INLINE_PLACEHOLDER = INLINE_PLACEHOLDER_PREFIX + "%s" + ETX -AMP_SUBSTITUTE = STX+"amp"+ETX - - -""" -Constants you probably do not need to change ------------------------------------------------------------------------------ -""" - -RTL_BIDI_RANGES = ( (u'\u0590', u'\u07FF'), - # Hebrew (0590-05FF), Arabic (0600-06FF), - # Syriac (0700-074F), Arabic supplement (0750-077F), - # Thaana (0780-07BF), Nko (07C0-07FF). - (u'\u2D30', u'\u2D7F'), # Tifinagh - ) - - -""" -AUXILIARY GLOBAL FUNCTIONS -============================================================================= -""" - - -def message(level, text): - """ A wrapper method for logging debug messages. """ - logger = logging.getLogger('MARKDOWN') - if logger.handlers: - # The logger is configured - logger.log(level, text) - if level > WARN: - sys.exit(0) - elif level > WARN: - raise MarkdownException, text - else: - warnings.warn(text, MarkdownWarning) - - -def isBlockLevel(tag): - """Check if the tag is a block level HTML tag.""" - return BLOCK_LEVEL_ELEMENTS.match(tag) - -""" -MISC AUXILIARY CLASSES -============================================================================= -""" - -class AtomicString(unicode): - """A string which should not be further processed.""" - pass - - -class MarkdownException(Exception): - """ A Markdown Exception. """ - pass - - -class MarkdownWarning(Warning): - """ A Markdown Warning. """ - pass - - -""" -OVERALL DESIGN -============================================================================= - -Markdown processing takes place in four steps: - -1. A bunch of "preprocessors" munge the input text. -2. BlockParser() parses the high-level structural elements of the - pre-processed text into an ElementTree. -3. A bunch of "treeprocessors" are run against the ElementTree. One such - treeprocessor runs InlinePatterns against the ElementTree, detecting inline - markup. -4. Some post-processors are run against the text after the ElementTree has - been serialized into text. -5. The output is written to a string. - -Those steps are put together by the Markdown() class. - -""" - -import preprocessors -import blockprocessors -import treeprocessors -import inlinepatterns -import postprocessors -import blockparser -import etree_loader -import odict - -# Extensions should use "markdown.etree" instead of "etree" (or do `from -# markdown import etree`). Do not import it by yourself. - -etree = etree_loader.importETree() - -# Adds the ability to output html4 -import html4 - - -class Markdown: - """Convert Markdown to HTML.""" - - def __init__(self, - extensions=[], - extension_configs={}, - safe_mode = False, - output_format=DEFAULT_OUTPUT_FORMAT): - """ - Creates a new Markdown instance. - - Keyword arguments: - - * extensions: A list of extensions. - If they are of type string, the module mdx_name.py will be loaded. - If they are a subclass of markdown.Extension, they will be used - as-is. - * extension-configs: Configuration setting for extensions. - * safe_mode: Disallow raw html. One of "remove", "replace" or "escape". - * output_format: Format of output. Supported formats are: - * "xhtml1": Outputs XHTML 1.x. Default. - * "xhtml": Outputs latest supported version of XHTML (currently XHTML 1.1). - * "html4": Outputs HTML 4 - * "html": Outputs latest supported version of HTML (currently HTML 4). - Note that it is suggested that the more specific formats ("xhtml1" - and "html4") be used as "xhtml" or "html" may change in the future - if it makes sense at that time. - - """ - - self.safeMode = safe_mode - self.registeredExtensions = [] - self.docType = "" - self.stripTopLevelTags = True - - # Preprocessors - self.preprocessors = odict.OrderedDict() - self.preprocessors["html_block"] = \ - preprocessors.HtmlBlockPreprocessor(self) - self.preprocessors["reference"] = \ - preprocessors.ReferencePreprocessor(self) - # footnote preprocessor will be inserted with "amp_substitute" - - # Map format keys to serializers - self.output_formats = { - 'html' : html4.to_html_string, - 'html4' : html4.to_html_string, - 'xhtml' : etree.tostring, - 'xhtml1': etree.tostring, - } - - self.references = {} - self.htmlStash = preprocessors.HtmlStash() - self.registerExtensions(extensions = extensions, - configs = extension_configs) - self.set_output_format(output_format) - self.reset() - - def registerExtensions(self, extensions, configs): - """ - Register extensions with this instance of Markdown. - - Keyword aurguments: - - * extensions: A list of extensions, which can either - be strings or objects. See the docstring on Markdown. - * configs: A dictionary mapping module names to config options. - - """ - for ext in extensions: - if isinstance(ext, basestring): - ext = load_extension(ext, configs.get(ext, [])) - try: - ext.extendMarkdown(self, globals()) - except AttributeError: - message(ERROR, "Incorrect type! Extension '%s' is " - "neither a string or an Extension." %(repr(ext))) - - - def registerExtension(self, extension): - """ This gets called by the extension """ - self.registeredExtensions.append(extension) - - def reset(self): - """ - Resets all state variables so that we can start with a new text. - """ - self.htmlStash.reset() - self.references.clear() - - for extension in self.registeredExtensions: - extension.reset() - - def set_output_format(self, format): - """ Set the output format for the class instance. """ - try: - self.serializer = self.output_formats[format.lower()] - except KeyError: - message(CRITICAL, 'Invalid Output Format: "%s". Use one of %s.' \ - % (format, self.output_formats.keys())) - - def convert(self, source): - """ - Convert markdown to serialized XHTML or HTML. - - Keyword arguments: - - * source: Source text as a Unicode string. - - """ - - # Fixup the source text - if not source.strip(): - return u"" # a blank unicode string - try: - source = unicode(source) - except UnicodeDecodeError: - message(CRITICAL, 'UnicodeDecodeError: Markdown only accepts unicode or ascii input.') - return u"" - - source = source.replace(STX, "").replace(ETX, "") - source = source.replace("\r\n", "\n").replace("\r", "\n") + "\n\n" - source = re.sub(r'\n\s+\n', '\n\n', source) - source = source.expandtabs(TAB_LENGTH) - - # Split into lines and run the line preprocessors. - self.lines = source.split("\n") - for prep in self.preprocessors.values(): - self.lines = prep.run(self.lines) - - # Parse the high-level elements. - root = self.parser.parseDocument(self.lines).getroot() - - # Run the tree-processors - for treeprocessor in self.treeprocessors.values(): - newRoot = treeprocessor.run(root) - if newRoot: - root = newRoot - - # Serialize _properly_. Strip top-level tags. - output, length = codecs.utf_8_decode(self.serializer(root, encoding="utf8")) - if self.stripTopLevelTags: - start = output.index('<%s>'%DOC_TAG)+len(DOC_TAG)+2 - end = output.rindex(''%DOC_TAG) - output = output[start:end].strip() - - # Run the text post-processors - for pp in self.postprocessors.values(): - output = pp.run(output) - - return output.strip() - - def convertFile(self, input=None, output=None, encoding=None): - """Converts a markdown file and returns the HTML as a unicode string. - - Decodes the file using the provided encoding (defaults to utf-8), - passes the file content to markdown, and outputs the html to either - the provided stream or the file with provided name, using the same - encoding as the source file. - - **Note:** This is the only place that decoding and encoding of unicode - takes place in Python-Markdown. (All other code is unicode-in / - unicode-out.) - - Keyword arguments: - - * input: Name of source text file. - * output: Name of output file. Writes to stdout if `None`. - * encoding: Encoding of input and output files. Defaults to utf-8. - - """ - - encoding = encoding or "utf-8" - - # Read the source - input_file = codecs.open(input, mode="r", encoding=encoding) - text = input_file.read() - input_file.close() - text = text.lstrip(u'\ufeff') # remove the byte-order mark - - # Convert - html = self.convert(text) - - # Write to file or stdout - if isinstance(output, (str, unicode)): - output_file = codecs.open(output, "w", encoding=encoding) - output_file.write(html) - output_file.close() - else: - output.write(html.encode(encoding)) - - -""" -Extensions ------------------------------------------------------------------------------ -""" - -class Extension: - """ Base class for extensions to subclass. """ - def __init__(self, configs = {}): - """Create an instance of an Extention. - - Keyword arguments: - - * configs: A dict of configuration setting used by an Extension. - """ - self.config = configs - - def getConfig(self, key): - """ Return a setting for the given key or an empty string. """ - if key in self.config: - return self.config[key][0] - else: - return "" - - def getConfigInfo(self): - """ Return all config settings as a list of tuples. """ - return [(key, self.config[key][1]) for key in self.config.keys()] - - def setConfig(self, key, value): - """ Set a config setting for `key` with the given `value`. """ - self.config[key][0] = value - - def extendMarkdown(self, md, md_globals): - """ - Add the various proccesors and patterns to the Markdown Instance. - - This method must be overriden by every extension. - - Keyword arguments: - - * md: The Markdown instance. - - * md_globals: Global variables in the markdown module namespace. - - """ - pass - - -def load_extension(ext_name, configs = []): - """Load extension by name, then return the module. - - The extension name may contain arguments as part of the string in the - following format: "extname(key1=value1,key2=value2)" - - """ - - # Parse extensions config params (ignore the order) - configs = dict(configs) - pos = ext_name.find("(") # find the first "(" - if pos > 0: - ext_args = ext_name[pos+1:-1] - ext_name = ext_name[:pos] - pairs = [x.split("=") for x in ext_args.split(",")] - configs.update([(x.strip(), y.strip()) for (x, y) in pairs]) - - # Setup the module names - ext_module = 'markdown.extensions' - module_name_new_style = '.'.join([ext_module, ext_name]) - module_name_old_style = '_'.join(['mdx', ext_name]) - - # Try loading the extention first from one place, then another - try: # New style (markdown.extensons.) - module = __import__(module_name_new_style, {}, {}, [ext_module]) - except ImportError: - try: # Old style (mdx.) - module = __import__(module_name_old_style) - except ImportError: - message(WARN, "Failed loading extension '%s' from '%s' or '%s'" - % (ext_name, module_name_new_style, module_name_old_style)) - # Return None so we don't try to initiate none-existant extension - return None - - # If the module is loaded successfully, we expect it to define a - # function called makeExtension() - try: - return module.makeExtension(configs.items()) - except AttributeError: - message(CRITICAL, "Failed to initiate extension '%s'" % ext_name) - - -def load_extensions(ext_names): - """Loads multiple extensions""" - extensions = [] - for ext_name in ext_names: - extension = load_extension(ext_name) - if extension: - extensions.append(extension) - return extensions - - -""" -EXPORTED FUNCTIONS -============================================================================= - -Those are the two functions we really mean to export: markdown() and -markdownFromFile(). -""" - -def markdown(text, - extensions = [], - safe_mode = False, - output_format = DEFAULT_OUTPUT_FORMAT): - """Convert a markdown string to HTML and return HTML as a unicode string. - - This is a shortcut function for `Markdown` class to cover the most - basic use case. It initializes an instance of Markdown, loads the - necessary extensions and runs the parser on the given text. - - Keyword arguments: - - * text: Markdown formatted text as Unicode or ASCII string. - * extensions: A list of extensions or extension names (may contain config args). - * safe_mode: Disallow raw html. One of "remove", "replace" or "escape". - * output_format: Format of output. Supported formats are: - * "xhtml1": Outputs XHTML 1.x. Default. - * "xhtml": Outputs latest supported version of XHTML (currently XHTML 1.1). - * "html4": Outputs HTML 4 - * "html": Outputs latest supported version of HTML (currently HTML 4). - Note that it is suggested that the more specific formats ("xhtml1" - and "html4") be used as "xhtml" or "html" may change in the future - if it makes sense at that time. - - Returns: An HTML document as a string. - - """ - md = Markdown(extensions=load_extensions(extensions), - safe_mode=safe_mode, - output_format=output_format) - return md.convert(text) - - -def markdownFromFile(input = None, - output = None, - extensions = [], - encoding = None, - safe_mode = False, - output_format = DEFAULT_OUTPUT_FORMAT): - """Read markdown code from a file and write it to a file or a stream.""" - md = Markdown(extensions=load_extensions(extensions), - safe_mode=safe_mode, - output_format=output_format) - md.convertFile(input, output, encoding) - - - diff --git a/addon-sdk/source/python-lib/markdown/blockparser.py b/addon-sdk/source/python-lib/markdown/blockparser.py deleted file mode 100644 index e18b3384877d..000000000000 --- a/addon-sdk/source/python-lib/markdown/blockparser.py +++ /dev/null @@ -1,95 +0,0 @@ - -import markdown - -class State(list): - """ Track the current and nested state of the parser. - - This utility class is used to track the state of the BlockParser and - support multiple levels if nesting. It's just a simple API wrapped around - a list. Each time a state is set, that state is appended to the end of the - list. Each time a state is reset, that state is removed from the end of - the list. - - Therefore, each time a state is set for a nested block, that state must be - reset when we back out of that level of nesting or the state could be - corrupted. - - While all the methods of a list object are available, only the three - defined below need be used. - - """ - - def set(self, state): - """ Set a new state. """ - self.append(state) - - def reset(self): - """ Step back one step in nested state. """ - self.pop() - - def isstate(self, state): - """ Test that top (current) level is of given state. """ - if len(self): - return self[-1] == state - else: - return False - -class BlockParser: - """ Parse Markdown blocks into an ElementTree object. - - A wrapper class that stitches the various BlockProcessors together, - looping through them and creating an ElementTree object. - """ - - def __init__(self): - self.blockprocessors = markdown.odict.OrderedDict() - self.state = State() - - def parseDocument(self, lines): - """ Parse a markdown document into an ElementTree. - - Given a list of lines, an ElementTree object (not just a parent Element) - is created and the root element is passed to the parser as the parent. - The ElementTree object is returned. - - This should only be called on an entire document, not pieces. - - """ - # Create a ElementTree from the lines - self.root = markdown.etree.Element(markdown.DOC_TAG) - self.parseChunk(self.root, '\n'.join(lines)) - return markdown.etree.ElementTree(self.root) - - def parseChunk(self, parent, text): - """ Parse a chunk of markdown text and attach to given etree node. - - While the ``text`` argument is generally assumed to contain multiple - blocks which will be split on blank lines, it could contain only one - block. Generally, this method would be called by extensions when - block parsing is required. - - The ``parent`` etree Element passed in is altered in place. - Nothing is returned. - - """ - self.parseBlocks(parent, text.split('\n\n')) - - def parseBlocks(self, parent, blocks): - """ Process blocks of markdown text and attach to given etree node. - - Given a list of ``blocks``, each blockprocessor is stepped through - until there are no blocks left. While an extension could potentially - call this method directly, it's generally expected to be used internally. - - This is a public method as an extension may need to add/alter additional - BlockProcessors which call this method to recursively parse a nested - block. - - """ - while blocks: - for processor in self.blockprocessors.values(): - if processor.test(parent, blocks[0]): - processor.run(parent, blocks) - break - - diff --git a/addon-sdk/source/python-lib/markdown/blockprocessors.py b/addon-sdk/source/python-lib/markdown/blockprocessors.py deleted file mode 100644 index 79f4db93bc57..000000000000 --- a/addon-sdk/source/python-lib/markdown/blockprocessors.py +++ /dev/null @@ -1,460 +0,0 @@ -""" -CORE MARKDOWN BLOCKPARSER -============================================================================= - -This parser handles basic parsing of Markdown blocks. It doesn't concern itself -with inline elements such as **bold** or *italics*, but rather just catches -blocks, lists, quotes, etc. - -The BlockParser is made up of a bunch of BlockProssors, each handling a -different type of block. Extensions may add/replace/remove BlockProcessors -as they need to alter how markdown blocks are parsed. - -""" - -import re -import markdown - -class BlockProcessor: - """ Base class for block processors. - - Each subclass will provide the methods below to work with the source and - tree. Each processor will need to define it's own ``test`` and ``run`` - methods. The ``test`` method should return True or False, to indicate - whether the current block should be processed by this processor. If the - test passes, the parser will call the processors ``run`` method. - - """ - - def __init__(self, parser=None): - self.parser = parser - - def lastChild(self, parent): - """ Return the last child of an etree element. """ - if len(parent): - return parent[-1] - else: - return None - - def detab(self, text): - """ Remove a tab from the front of each line of the given text. """ - newtext = [] - lines = text.split('\n') - for line in lines: - if line.startswith(' '*markdown.TAB_LENGTH): - newtext.append(line[markdown.TAB_LENGTH:]) - elif not line.strip(): - newtext.append('') - else: - break - return '\n'.join(newtext), '\n'.join(lines[len(newtext):]) - - def looseDetab(self, text, level=1): - """ Remove a tab from front of lines but allowing dedented lines. """ - lines = text.split('\n') - for i in range(len(lines)): - if lines[i].startswith(' '*markdown.TAB_LENGTH*level): - lines[i] = lines[i][markdown.TAB_LENGTH*level:] - return '\n'.join(lines) - - def test(self, parent, block): - """ Test for block type. Must be overridden by subclasses. - - As the parser loops through processors, it will call the ``test`` method - on each to determine if the given block of text is of that type. This - method must return a boolean ``True`` or ``False``. The actual method of - testing is left to the needs of that particular block type. It could - be as simple as ``block.startswith(some_string)`` or a complex regular - expression. As the block type may be different depending on the parent - of the block (i.e. inside a list), the parent etree element is also - provided and may be used as part of the test. - - Keywords: - - * ``parent``: A etree element which will be the parent of the block. - * ``block``: A block of text from the source which has been split at - blank lines. - """ - pass - - def run(self, parent, blocks): - """ Run processor. Must be overridden by subclasses. - - When the parser determines the appropriate type of a block, the parser - will call the corresponding processor's ``run`` method. This method - should parse the individual lines of the block and append them to - the etree. - - Note that both the ``parent`` and ``etree`` keywords are pointers - to instances of the objects which should be edited in place. Each - processor must make changes to the existing objects as there is no - mechanism to return new/different objects to replace them. - - This means that this method should be adding SubElements or adding text - to the parent, and should remove (``pop``) or add (``insert``) items to - the list of blocks. - - Keywords: - - * ``parent``: A etree element which is the parent of the current block. - * ``blocks``: A list of all remaining blocks of the document. - """ - pass - - -class ListIndentProcessor(BlockProcessor): - """ Process children of list items. - - Example: - * a list item - process this part - - or this part - - """ - - INDENT_RE = re.compile(r'^(([ ]{%s})+)'% markdown.TAB_LENGTH) - ITEM_TYPES = ['li'] - LIST_TYPES = ['ul', 'ol'] - - def test(self, parent, block): - return block.startswith(' '*markdown.TAB_LENGTH) and \ - not self.parser.state.isstate('detabbed') and \ - (parent.tag in self.ITEM_TYPES or \ - (len(parent) and parent[-1] and \ - (parent[-1].tag in self.LIST_TYPES) - ) - ) - - def run(self, parent, blocks): - block = blocks.pop(0) - level, sibling = self.get_level(parent, block) - block = self.looseDetab(block, level) - - self.parser.state.set('detabbed') - if parent.tag in self.ITEM_TYPES: - # The parent is already a li. Just parse the child block. - self.parser.parseBlocks(parent, [block]) - elif sibling.tag in self.ITEM_TYPES: - # The sibling is a li. Use it as parent. - self.parser.parseBlocks(sibling, [block]) - elif len(sibling) and sibling[-1].tag in self.ITEM_TYPES: - # The parent is a list (``ol`` or ``ul``) which has children. - # Assume the last child li is the parent of this block. - if sibling[-1].text: - # If the parent li has text, that text needs to be moved to a p - block = '%s\n\n%s' % (sibling[-1].text, block) - sibling[-1].text = '' - self.parser.parseChunk(sibling[-1], block) - else: - self.create_item(sibling, block) - self.parser.state.reset() - - def create_item(self, parent, block): - """ Create a new li and parse the block with it as the parent. """ - li = markdown.etree.SubElement(parent, 'li') - self.parser.parseBlocks(li, [block]) - - def get_level(self, parent, block): - """ Get level of indent based on list level. """ - # Get indent level - m = self.INDENT_RE.match(block) - if m: - indent_level = len(m.group(1))/markdown.TAB_LENGTH - else: - indent_level = 0 - if self.parser.state.isstate('list'): - # We're in a tightlist - so we already are at correct parent. - level = 1 - else: - # We're in a looselist - so we need to find parent. - level = 0 - # Step through children of tree to find matching indent level. - while indent_level > level: - child = self.lastChild(parent) - if child and (child.tag in self.LIST_TYPES or child.tag in self.ITEM_TYPES): - if child.tag in self.LIST_TYPES: - level += 1 - parent = child - else: - # No more child levels. If we're short of indent_level, - # we have a code block. So we stop here. - break - return level, parent - - -class CodeBlockProcessor(BlockProcessor): - """ Process code blocks. """ - - def test(self, parent, block): - return block.startswith(' '*markdown.TAB_LENGTH) - - def run(self, parent, blocks): - sibling = self.lastChild(parent) - block = blocks.pop(0) - theRest = '' - if sibling and sibling.tag == "pre" and len(sibling) \ - and sibling[0].tag == "code": - # The previous block was a code block. As blank lines do not start - # new code blocks, append this block to the previous, adding back - # linebreaks removed from the split into a list. - code = sibling[0] - block, theRest = self.detab(block) - code.text = markdown.AtomicString('%s\n%s\n' % (code.text, block.rstrip())) - else: - # This is a new codeblock. Create the elements and insert text. - pre = markdown.etree.SubElement(parent, 'pre') - code = markdown.etree.SubElement(pre, 'code') - block, theRest = self.detab(block) - code.text = markdown.AtomicString('%s\n' % block.rstrip()) - if theRest: - # This block contained unindented line(s) after the first indented - # line. Insert these lines as the first block of the master blocks - # list for future processing. - blocks.insert(0, theRest) - - -class BlockQuoteProcessor(BlockProcessor): - - RE = re.compile(r'(^|\n)[ ]{0,3}>[ ]?(.*)') - - def test(self, parent, block): - return bool(self.RE.search(block)) - - def run(self, parent, blocks): - block = blocks.pop(0) - m = self.RE.search(block) - if m: - before = block[:m.start()] # Lines before blockquote - # Pass lines before blockquote in recursively for parsing forst. - self.parser.parseBlocks(parent, [before]) - # Remove ``> `` from begining of each line. - block = '\n'.join([self.clean(line) for line in - block[m.start():].split('\n')]) - sibling = self.lastChild(parent) - if sibling and sibling.tag == "blockquote": - # Previous block was a blockquote so set that as this blocks parent - quote = sibling - else: - # This is a new blockquote. Create a new parent element. - quote = markdown.etree.SubElement(parent, 'blockquote') - # Recursively parse block with blockquote as parent. - self.parser.parseChunk(quote, block) - - def clean(self, line): - """ Remove ``>`` from beginning of a line. """ - m = self.RE.match(line) - if line.strip() == ">": - return "" - elif m: - return m.group(2) - else: - return line - -class OListProcessor(BlockProcessor): - """ Process ordered list blocks. """ - - TAG = 'ol' - # Detect an item (``1. item``). ``group(1)`` contains contents of item. - RE = re.compile(r'^[ ]{0,3}\d+\.[ ](.*)') - # Detect items on secondary lines. they can be of either list type. - CHILD_RE = re.compile(r'^[ ]{0,3}((\d+\.)|[*+-])[ ](.*)') - # Detect indented (nested) items of either type - INDENT_RE = re.compile(r'^[ ]{4,7}((\d+\.)|[*+-])[ ].*') - - def test(self, parent, block): - return bool(self.RE.match(block)) - - def run(self, parent, blocks): - # Check fr multiple items in one block. - items = self.get_items(blocks.pop(0)) - sibling = self.lastChild(parent) - if sibling and sibling.tag in ['ol', 'ul']: - # Previous block was a list item, so set that as parent - lst = sibling - # make sure previous item is in a p. - if len(lst) and lst[-1].text and not len(lst[-1]): - p = markdown.etree.SubElement(lst[-1], 'p') - p.text = lst[-1].text - lst[-1].text = '' - # parse first block differently as it gets wrapped in a p. - li = markdown.etree.SubElement(lst, 'li') - self.parser.state.set('looselist') - firstitem = items.pop(0) - self.parser.parseBlocks(li, [firstitem]) - self.parser.state.reset() - else: - # This is a new list so create parent with appropriate tag. - lst = markdown.etree.SubElement(parent, self.TAG) - self.parser.state.set('list') - # Loop through items in block, recursively parsing each with the - # appropriate parent. - for item in items: - if item.startswith(' '*markdown.TAB_LENGTH): - # Item is indented. Parse with last item as parent - self.parser.parseBlocks(lst[-1], [item]) - else: - # New item. Create li and parse with it as parent - li = markdown.etree.SubElement(lst, 'li') - self.parser.parseBlocks(li, [item]) - self.parser.state.reset() - - def get_items(self, block): - """ Break a block into list items. """ - items = [] - for line in block.split('\n'): - m = self.CHILD_RE.match(line) - if m: - # This is a new item. Append - items.append(m.group(3)) - elif self.INDENT_RE.match(line): - # This is an indented (possibly nested) item. - if items[-1].startswith(' '*markdown.TAB_LENGTH): - # Previous item was indented. Append to that item. - items[-1] = '%s\n%s' % (items[-1], line) - else: - items.append(line) - else: - # This is another line of previous item. Append to that item. - items[-1] = '%s\n%s' % (items[-1], line) - return items - - -class UListProcessor(OListProcessor): - """ Process unordered list blocks. """ - - TAG = 'ul' - RE = re.compile(r'^[ ]{0,3}[*+-][ ](.*)') - - -class HashHeaderProcessor(BlockProcessor): - """ Process Hash Headers. """ - - # Detect a header at start of any line in block - RE = re.compile(r'(^|\n)(?P#{1,6})(?P
.*?)#*(\n|$)') - - def test(self, parent, block): - return bool(self.RE.search(block)) - - def run(self, parent, blocks): - block = blocks.pop(0) - m = self.RE.search(block) - if m: - before = block[:m.start()] # All lines before header - after = block[m.end():] # All lines after header - if before: - # As the header was not the first line of the block and the - # lines before the header must be parsed first, - # recursively parse this lines as a block. - self.parser.parseBlocks(parent, [before]) - # Create header using named groups from RE - h = markdown.etree.SubElement(parent, 'h%d' % len(m.group('level'))) - h.text = m.group('header').strip() - if after: - # Insert remaining lines as first block for future parsing. - blocks.insert(0, after) - else: - # This should never happen, but just in case... - message(CRITICAL, "We've got a problem header!") - - -class SetextHeaderProcessor(BlockProcessor): - """ Process Setext-style Headers. """ - - # Detect Setext-style header. Must be first 2 lines of block. - RE = re.compile(r'^.*?\n[=-]{3,}', re.MULTILINE) - - def test(self, parent, block): - return bool(self.RE.match(block)) - - def run(self, parent, blocks): - lines = blocks.pop(0).split('\n') - # Determine level. ``=`` is 1 and ``-`` is 2. - if lines[1].startswith('='): - level = 1 - else: - level = 2 - h = markdown.etree.SubElement(parent, 'h%d' % level) - h.text = lines[0].strip() - if len(lines) > 2: - # Block contains additional lines. Add to master blocks for later. - blocks.insert(0, '\n'.join(lines[2:])) - - -class HRProcessor(BlockProcessor): - """ Process Horizontal Rules. """ - - RE = r'[ ]{0,3}(?P[*_-])[ ]?((?P=ch)[ ]?){2,}[ ]*' - # Detect hr on any line of a block. - SEARCH_RE = re.compile(r'(^|\n)%s(\n|$)' % RE) - # Match a hr on a single line of text. - MATCH_RE = re.compile(r'^%s$' % RE) - - def test(self, parent, block): - return bool(self.SEARCH_RE.search(block)) - - def run(self, parent, blocks): - lines = blocks.pop(0).split('\n') - prelines = [] - # Check for lines in block before hr. - for line in lines: - m = self.MATCH_RE.match(line) - if m: - break - else: - prelines.append(line) - if len(prelines): - # Recursively parse lines before hr so they get parsed first. - self.parser.parseBlocks(parent, ['\n'.join(prelines)]) - # create hr - hr = markdown.etree.SubElement(parent, 'hr') - # check for lines in block after hr. - lines = lines[len(prelines)+1:] - if len(lines): - # Add lines after hr to master blocks for later parsing. - blocks.insert(0, '\n'.join(lines)) - - -class EmptyBlockProcessor(BlockProcessor): - """ Process blocks and start with an empty line. """ - - # Detect a block that only contains whitespace - # or only whitespace on the first line. - RE = re.compile(r'^\s*\n') - - def test(self, parent, block): - return bool(self.RE.match(block)) - - def run(self, parent, blocks): - block = blocks.pop(0) - m = self.RE.match(block) - if m: - # Add remaining line to master blocks for later. - blocks.insert(0, block[m.end():]) - sibling = self.lastChild(parent) - if sibling and sibling.tag == 'pre' and sibling[0] and \ - sibling[0].tag == 'code': - # Last block is a codeblock. Append to preserve whitespace. - sibling[0].text = markdown.AtomicString('%s/n/n/n' % sibling[0].text ) - - -class ParagraphProcessor(BlockProcessor): - """ Process Paragraph blocks. """ - - def test(self, parent, block): - return True - - def run(self, parent, blocks): - block = blocks.pop(0) - if block.strip(): - # Not a blank block. Add to parent, otherwise throw it away. - if self.parser.state.isstate('list'): - # The parent is a tight-list. Append to parent.text - if parent.text: - parent.text = '%s\n%s' % (parent.text, block) - else: - parent.text = block.lstrip() - else: - # Create a regular paragraph - p = markdown.etree.SubElement(parent, 'p') - p.text = block.lstrip() diff --git a/addon-sdk/source/python-lib/markdown/commandline.py b/addon-sdk/source/python-lib/markdown/commandline.py deleted file mode 100644 index 1eedc6dbb1b4..000000000000 --- a/addon-sdk/source/python-lib/markdown/commandline.py +++ /dev/null @@ -1,96 +0,0 @@ -""" -COMMAND-LINE SPECIFIC STUFF -============================================================================= - -The rest of the code is specifically for handling the case where Python -Markdown is called from the command line. -""" - -import markdown -import sys -import logging -from logging import DEBUG, INFO, WARN, ERROR, CRITICAL - -EXECUTABLE_NAME_FOR_USAGE = "python markdown.py" -""" The name used in the usage statement displayed for python versions < 2.3. -(With python 2.3 and higher the usage statement is generated by optparse -and uses the actual name of the executable called.) """ - -OPTPARSE_WARNING = """ -Python 2.3 or higher required for advanced command line options. -For lower versions of Python use: - - %s INPUT_FILE > OUTPUT_FILE - -""" % EXECUTABLE_NAME_FOR_USAGE - -def parse_options(): - """ - Define and parse `optparse` options for command-line usage. - """ - - try: - optparse = __import__("optparse") - except: - if len(sys.argv) == 2: - return {'input': sys.argv[1], - 'output': None, - 'safe': False, - 'extensions': [], - 'encoding': None }, CRITICAL - else: - print OPTPARSE_WARNING - return None, None - - parser = optparse.OptionParser(usage="%prog INPUTFILE [options]") - parser.add_option("-f", "--file", dest="filename", default=sys.stdout, - help="write output to OUTPUT_FILE", - metavar="OUTPUT_FILE") - parser.add_option("-e", "--encoding", dest="encoding", - help="encoding for input and output files",) - parser.add_option("-q", "--quiet", default = CRITICAL, - action="store_const", const=CRITICAL+10, dest="verbose", - help="suppress all messages") - parser.add_option("-v", "--verbose", - action="store_const", const=INFO, dest="verbose", - help="print info messages") - parser.add_option("-s", "--safe", dest="safe", default=False, - metavar="SAFE_MODE", - help="safe mode ('replace', 'remove' or 'escape' user's HTML tag)") - parser.add_option("-o", "--output_format", dest="output_format", - default='xhtml1', metavar="OUTPUT_FORMAT", - help="Format of output. One of 'xhtml1' (default) or 'html4'.") - parser.add_option("--noisy", - action="store_const", const=DEBUG, dest="verbose", - help="print debug messages") - parser.add_option("-x", "--extension", action="append", dest="extensions", - help = "load extension EXTENSION", metavar="EXTENSION") - - (options, args) = parser.parse_args() - - if not len(args) == 1: - parser.print_help() - return None, None - else: - input_file = args[0] - - if not options.extensions: - options.extensions = [] - - return {'input': input_file, - 'output': options.filename, - 'safe_mode': options.safe, - 'extensions': options.extensions, - 'encoding': options.encoding, - 'output_format': options.output_format}, options.verbose - -def run(): - """Run Markdown from the command line.""" - - # Parse options and adjust logging level if necessary - options, logging_level = parse_options() - if not options: sys.exit(0) - if logging_level: logging.getLogger('MARKDOWN').setLevel(logging_level) - - # Run - markdown.markdownFromFile(**options) diff --git a/addon-sdk/source/python-lib/markdown/etree_loader.py b/addon-sdk/source/python-lib/markdown/etree_loader.py deleted file mode 100644 index e2599b2cb965..000000000000 --- a/addon-sdk/source/python-lib/markdown/etree_loader.py +++ /dev/null @@ -1,33 +0,0 @@ - -from markdown import message, CRITICAL -import sys - -## Import -def importETree(): - """Import the best implementation of ElementTree, return a module object.""" - etree_in_c = None - try: # Is it Python 2.5+ with C implemenation of ElementTree installed? - import xml.etree.cElementTree as etree_in_c - except ImportError: - try: # Is it Python 2.5+ with Python implementation of ElementTree? - import xml.etree.ElementTree as etree - except ImportError: - try: # An earlier version of Python with cElementTree installed? - import cElementTree as etree_in_c - except ImportError: - try: # An earlier version of Python with Python ElementTree? - import elementtree.ElementTree as etree - except ImportError: - message(CRITICAL, "Failed to import ElementTree") - sys.exit(1) - if etree_in_c and etree_in_c.VERSION < "1.0": - message(CRITICAL, "For cElementTree version 1.0 or higher is required.") - sys.exit(1) - elif etree_in_c : - return etree_in_c - elif etree.VERSION < "1.1": - message(CRITICAL, "For ElementTree version 1.1 or higher is required") - sys.exit(1) - else : - return etree - diff --git a/addon-sdk/source/python-lib/markdown/extensions/__init__.py b/addon-sdk/source/python-lib/markdown/extensions/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/addon-sdk/source/python-lib/markdown/extensions/abbr.py b/addon-sdk/source/python-lib/markdown/extensions/abbr.py deleted file mode 100644 index 783220e20e67..000000000000 --- a/addon-sdk/source/python-lib/markdown/extensions/abbr.py +++ /dev/null @@ -1,95 +0,0 @@ -''' -Abbreviation Extension for Python-Markdown -========================================== - -This extension adds abbreviation handling to Python-Markdown. - -Simple Usage: - - >>> import markdown - >>> text = """ - ... Some text with an ABBR and a REF. Ignore REFERENCE and ref. - ... - ... *[ABBR]: Abbreviation - ... *[REF]: Abbreviation Reference - ... """ - >>> markdown.markdown(text, ['abbr']) - u'

Some text with an ABBR and a REF. Ignore REFERENCE and ref.

' - -Copyright 2007-2008 -* [Waylan Limberg](http://achinghead.com/) -* [Seemant Kulleen](http://www.kulleen.org/) - - -''' - -import markdown, re -from markdown import etree - -# Global Vars -ABBR_REF_RE = re.compile(r'[*]\[(?P[^\]]*)\][ ]?:\s*(?P.*)') - -class AbbrExtension(markdown.Extension): - """ Abbreviation Extension for Python-Markdown. """ - - def extendMarkdown(self, md, md_globals): - """ Insert AbbrPreprocessor before ReferencePreprocessor. """ - md.preprocessors.add('abbr', AbbrPreprocessor(md), '<reference') - - -class AbbrPreprocessor(markdown.preprocessors.Preprocessor): - """ Abbreviation Preprocessor - parse text for abbr references. """ - - def run(self, lines): - ''' - Find and remove all Abbreviation references from the text. - Each reference is set as a new AbbrPattern in the markdown instance. - - ''' - new_text = [] - for line in lines: - m = ABBR_REF_RE.match(line) - if m: - abbr = m.group('abbr').strip() - title = m.group('title').strip() - self.markdown.inlinePatterns['abbr-%s'%abbr] = \ - AbbrPattern(self._generate_pattern(abbr), title) - else: - new_text.append(line) - return new_text - - def _generate_pattern(self, text): - ''' - Given a string, returns an regex pattern to match that string. - - 'HTML' -> r'(?P<abbr>[H][T][M][L])' - - Note: we force each char as a literal match (in brackets) as we don't - know what they will be beforehand. - - ''' - chars = list(text) - for i in range(len(chars)): - chars[i] = r'[%s]' % chars[i] - return r'(?P<abbr>\b%s\b)' % (r''.join(chars)) - - -class AbbrPattern(markdown.inlinepatterns.Pattern): - """ Abbreviation inline pattern. """ - - def __init__(self, pattern, title): - markdown.inlinepatterns.Pattern.__init__(self, pattern) - self.title = title - - def handleMatch(self, m): - abbr = etree.Element('abbr') - abbr.text = m.group('abbr') - abbr.set('title', self.title) - return abbr - -def makeExtension(configs=None): - return AbbrExtension(configs=configs) - -if __name__ == "__main__": - import doctest - doctest.testmod() diff --git a/addon-sdk/source/python-lib/markdown/extensions/codehilite.py b/addon-sdk/source/python-lib/markdown/extensions/codehilite.py deleted file mode 100644 index c5d496b2b168..000000000000 --- a/addon-sdk/source/python-lib/markdown/extensions/codehilite.py +++ /dev/null @@ -1,224 +0,0 @@ -#!/usr/bin/python - -""" -CodeHilite Extension for Python-Markdown -======================================== - -Adds code/syntax highlighting to standard Python-Markdown code blocks. - -Copyright 2006-2008 [Waylan Limberg](http://achinghead.com/). - -Project website: <http://www.freewisdom.org/project/python-markdown/CodeHilite> -Contact: markdown@freewisdom.org - -License: BSD (see ../docs/LICENSE for details) - -Dependencies: -* [Python 2.3+](http://python.org/) -* [Markdown 2.0+](http://www.freewisdom.org/projects/python-markdown/) -* [Pygments](http://pygments.org/) - -""" - -import markdown - -# --------------- CONSTANTS YOU MIGHT WANT TO MODIFY ----------------- - -try: - TAB_LENGTH = markdown.TAB_LENGTH -except AttributeError: - TAB_LENGTH = 4 - - -# ------------------ The Main CodeHilite Class ---------------------- -class CodeHilite: - """ - Determine language of source code, and pass it into the pygments hilighter. - - Basic Usage: - >>> code = CodeHilite(src = 'some text') - >>> html = code.hilite() - - * src: Source string or any object with a .readline attribute. - - * linenos: (Boolen) Turn line numbering 'on' or 'off' (off by default). - - * css_class: Set class name of wrapper div ('codehilite' by default). - - Low Level Usage: - >>> code = CodeHilite() - >>> code.src = 'some text' # String or anything with a .readline attr. - >>> code.linenos = True # True or False; Turns line numbering on or of. - >>> html = code.hilite() - - """ - - def __init__(self, src=None, linenos=False, css_class="codehilite"): - self.src = src - self.lang = None - self.linenos = linenos - self.css_class = css_class - - def hilite(self): - """ - Pass code to the [Pygments](http://pygments.pocoo.org/) highliter with - optional line numbers. The output should then be styled with css to - your liking. No styles are applied by default - only styling hooks - (i.e.: <span class="k">). - - returns : A string of html. - - """ - - self.src = self.src.strip('\n') - - self._getLang() - - try: - from pygments import highlight - from pygments.lexers import get_lexer_by_name, guess_lexer, \ - TextLexer - from pygments.formatters import HtmlFormatter - except ImportError: - # just escape and pass through - txt = self._escape(self.src) - if self.linenos: - txt = self._number(txt) - else : - txt = '<div class="%s"><pre>%s</pre></div>\n'% \ - (self.css_class, txt) - return txt - else: - try: - lexer = get_lexer_by_name(self.lang) - except ValueError: - try: - lexer = guess_lexer(self.src) - except ValueError: - lexer = TextLexer() - formatter = HtmlFormatter(linenos=self.linenos, - cssclass=self.css_class) - return highlight(self.src, lexer, formatter) - - def _escape(self, txt): - """ basic html escaping """ - txt = txt.replace('&', '&') - txt = txt.replace('<', '<') - txt = txt.replace('>', '>') - txt = txt.replace('"', '"') - return txt - - def _number(self, txt): - """ Use <ol> for line numbering """ - # Fix Whitespace - txt = txt.replace('\t', ' '*TAB_LENGTH) - txt = txt.replace(" "*4, "    ") - txt = txt.replace(" "*3, "   ") - txt = txt.replace(" "*2, "  ") - - # Add line numbers - lines = txt.splitlines() - txt = '<div class="codehilite"><pre><ol>\n' - for line in lines: - txt += '\t<li>%s</li>\n'% line - txt += '</ol></pre></div>\n' - return txt - - - def _getLang(self): - """ - Determines language of a code block from shebang lines and whether said - line should be removed or left in place. If the sheband line contains a - path (even a single /) then it is assumed to be a real shebang lines and - left alone. However, if no path is given (e.i.: #!python or :::python) - then it is assumed to be a mock shebang for language identifitation of a - code fragment and removed from the code block prior to processing for - code highlighting. When a mock shebang (e.i: #!python) is found, line - numbering is turned on. When colons are found in place of a shebang - (e.i.: :::python), line numbering is left in the current state - off - by default. - - """ - - import re - - #split text into lines - lines = self.src.split("\n") - #pull first line to examine - fl = lines.pop(0) - - c = re.compile(r''' - (?:(?:::+)|(?P<shebang>[#]!)) # Shebang or 2 or more colons. - (?P<path>(?:/\w+)*[/ ])? # Zero or 1 path - (?P<lang>[\w+-]*) # The language - ''', re.VERBOSE) - # search first line for shebang - m = c.search(fl) - if m: - # we have a match - try: - self.lang = m.group('lang').lower() - except IndexError: - self.lang = None - if m.group('path'): - # path exists - restore first line - lines.insert(0, fl) - if m.group('shebang'): - # shebang exists - use line numbers - self.linenos = True - else: - # No match - lines.insert(0, fl) - - self.src = "\n".join(lines).strip("\n") - - - -# ------------------ The Markdown Extension ------------------------------- -class HiliteTreeprocessor(markdown.treeprocessors.Treeprocessor): - """ Hilight source code in code blocks. """ - - def run(self, root): - """ Find code blocks and store in htmlStash. """ - blocks = root.getiterator('pre') - for block in blocks: - children = block.getchildren() - if len(children) == 1 and children[0].tag == 'code': - code = CodeHilite(children[0].text, - linenos=self.config['force_linenos'][0], - css_class=self.config['css_class'][0]) - placeholder = self.markdown.htmlStash.store(code.hilite(), - safe=True) - # Clear codeblock in etree instance - block.clear() - # Change to p element which will later - # be removed when inserting raw html - block.tag = 'p' - block.text = placeholder - - -class CodeHiliteExtension(markdown.Extension): - """ Add source code hilighting to markdown codeblocks. """ - - def __init__(self, configs): - # define default configs - self.config = { - 'force_linenos' : [False, "Force line numbers - Default: False"], - 'css_class' : ["codehilite", - "Set class name for wrapper <div> - Default: codehilite"], - } - - # Override defaults with user settings - for key, value in configs: - self.setConfig(key, value) - - def extendMarkdown(self, md, md_globals): - """ Add HilitePostprocessor to Markdown instance. """ - hiliter = HiliteTreeprocessor(md) - hiliter.config = self.config - md.treeprocessors.add("hilite", hiliter, "_begin") - - -def makeExtension(configs={}): - return CodeHiliteExtension(configs=configs) - diff --git a/addon-sdk/source/python-lib/markdown/extensions/def_list.py b/addon-sdk/source/python-lib/markdown/extensions/def_list.py deleted file mode 100644 index 73a1c85ee1b5..000000000000 --- a/addon-sdk/source/python-lib/markdown/extensions/def_list.py +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env Python -""" -Definition List Extension for Python-Markdown -============================================= - -Added parsing of Definition Lists to Python-Markdown. - -A simple example: - - Apple - : Pomaceous fruit of plants of the genus Malus in - the family Rosaceae. - : An american computer company. - - Orange - : The fruit of an evergreen tree of the genus Citrus. - -Copyright 2008 - [Waylan Limberg](http://achinghead.com) - -""" - -import markdown, re -from markdown import etree - - -class DefListProcessor(markdown.blockprocessors.BlockProcessor): - """ Process Definition Lists. """ - - RE = re.compile(r'(^|\n)[ ]{0,3}:[ ]{1,3}(.*?)(\n|$)') - - def test(self, parent, block): - return bool(self.RE.search(block)) - - def run(self, parent, blocks): - block = blocks.pop(0) - m = self.RE.search(block) - terms = [l.strip() for l in block[:m.start()].split('\n') if l.strip()] - d, theRest = self.detab(block[m.end():]) - if d: - d = '%s\n%s' % (m.group(2), d) - else: - d = m.group(2) - #import ipdb; ipdb.set_trace() - sibling = self.lastChild(parent) - if not terms and sibling.tag == 'p': - # The previous paragraph contains the terms - state = 'looselist' - terms = sibling.text.split('\n') - parent.remove(sibling) - # Aquire new sibling - sibling = self.lastChild(parent) - else: - state = 'list' - - if sibling and sibling.tag == 'dl': - # This is another item on an existing list - dl = sibling - if len(dl) and dl[-1].tag == 'dd' and len(dl[-1]): - state = 'looselist' - else: - # This is a new list - dl = etree.SubElement(parent, 'dl') - # Add terms - for term in terms: - dt = etree.SubElement(dl, 'dt') - dt.text = term - # Add definition - self.parser.state.set(state) - dd = etree.SubElement(dl, 'dd') - self.parser.parseBlocks(dd, [d]) - self.parser.state.reset() - - if theRest: - blocks.insert(0, theRest) - -class DefListIndentProcessor(markdown.blockprocessors.ListIndentProcessor): - """ Process indented children of definition list items. """ - - ITEM_TYPES = ['dd'] - LIST_TYPES = ['dl'] - - def create_item(parent, block): - """ Create a new dd and parse the block with it as the parent. """ - dd = markdown.etree.SubElement(parent, 'dd') - self.parser.parseBlocks(dd, [block]) - - - -class DefListExtension(markdown.Extension): - """ Add definition lists to Markdown. """ - - def extendMarkdown(self, md, md_globals): - """ Add an instance of DefListProcessor to BlockParser. """ - md.parser.blockprocessors.add('defindent', - DefListIndentProcessor(md.parser), - '>indent') - md.parser.blockprocessors.add('deflist', - DefListProcessor(md.parser), - '>ulist') - - -def makeExtension(configs={}): - return DefListExtension(configs=configs) - diff --git a/addon-sdk/source/python-lib/markdown/extensions/extra.py b/addon-sdk/source/python-lib/markdown/extensions/extra.py deleted file mode 100644 index 4a2ffbf224c3..000000000000 --- a/addon-sdk/source/python-lib/markdown/extensions/extra.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python -""" -Python-Markdown Extra Extension -=============================== - -A compilation of various Python-Markdown extensions that imitates -[PHP Markdown Extra](http://michelf.com/projects/php-markdown/extra/). - -Note that each of the individual extensions still need to be available -on your PYTHONPATH. This extension simply wraps them all up as a -convenience so that only one extension needs to be listed when -initiating Markdown. See the documentation for each individual -extension for specifics about that extension. - -In the event that one or more of the supported extensions are not -available for import, Markdown will issue a warning and simply continue -without that extension. - -There may be additional extensions that are distributed with -Python-Markdown that are not included here in Extra. Those extensions -are not part of PHP Markdown Extra, and therefore, not part of -Python-Markdown Extra. If you really would like Extra to include -additional extensions, we suggest creating your own clone of Extra -under a differant name. You could also edit the `extensions` global -variable defined below, but be aware that such changes may be lost -when you upgrade to any future version of Python-Markdown. - -""" - -import markdown - -extensions = ['fenced_code', - 'footnotes', - 'headerid', - 'def_list', - 'tables', - 'abbr', - ] - - -class ExtraExtension(markdown.Extension): - """ Add various extensions to Markdown class.""" - - def extendMarkdown(self, md, md_globals): - """ Register extension instances. """ - md.registerExtensions(extensions, self.config) - -def makeExtension(configs={}): - return ExtraExtension(configs=dict(configs)) diff --git a/addon-sdk/source/python-lib/markdown/extensions/fenced_code.py b/addon-sdk/source/python-lib/markdown/extensions/fenced_code.py deleted file mode 100644 index 307b1dcbda87..000000000000 --- a/addon-sdk/source/python-lib/markdown/extensions/fenced_code.py +++ /dev/null @@ -1,117 +0,0 @@ -#!/usr/bin/env python - -""" -Fenced Code Extension for Python Markdown -========================================= - -This extension adds Fenced Code Blocks to Python-Markdown. - - >>> import markdown - >>> text = ''' - ... A paragraph before a fenced code block: - ... - ... ~~~ - ... Fenced code block - ... ~~~ - ... ''' - >>> html = markdown.markdown(text, extensions=['fenced_code']) - >>> html - u'<p>A paragraph before a fenced code block:</p>\\n<pre><code>Fenced code block\\n</code></pre>' - -Works with safe_mode also (we check this because we are using the HtmlStash): - - >>> markdown.markdown(text, extensions=['fenced_code'], safe_mode='replace') - u'<p>A paragraph before a fenced code block:</p>\\n<pre><code>Fenced code block\\n</code></pre>' - -Include tilde's in a code block and wrap with blank lines: - - >>> text = ''' - ... ~~~~~~~~ - ... - ... ~~~~ - ... - ... ~~~~~~~~''' - >>> markdown.markdown(text, extensions=['fenced_code']) - u'<pre><code>\\n~~~~\\n\\n</code></pre>' - -Multiple blocks and language tags: - - >>> text = ''' - ... ~~~~{.python} - ... block one - ... ~~~~ - ... - ... ~~~~.html - ... <p>block two</p> - ... ~~~~''' - >>> markdown.markdown(text, extensions=['fenced_code']) - u'<pre><code class="python">block one\\n</code></pre>\\n\\n<pre><code class="html"><p>block two</p>\\n</code></pre>' - -Copyright 2007-2008 [Waylan Limberg](http://achinghead.com/). - -Project website: <http://www.freewisdom.org/project/python-markdown/Fenced__Code__Blocks> -Contact: markdown@freewisdom.org - -License: BSD (see ../docs/LICENSE for details) - -Dependencies: -* [Python 2.3+](http://python.org) -* [Markdown 2.0+](http://www.freewisdom.org/projects/python-markdown/) - -""" - -import markdown, re - -# Global vars -FENCED_BLOCK_RE = re.compile( \ - r'(?P<fence>^~{3,})[ ]*(\{?\.(?P<lang>[a-zA-Z0-9_-]*)\}?)?[ ]*\n(?P<code>.*?)(?P=fence)[ ]*$', - re.MULTILINE|re.DOTALL - ) -CODE_WRAP = '<pre><code%s>%s</code></pre>' -LANG_TAG = ' class="%s"' - - -class FencedCodeExtension(markdown.Extension): - - def extendMarkdown(self, md, md_globals): - """ Add FencedBlockPreprocessor to the Markdown instance. """ - - md.preprocessors.add('fenced_code_block', - FencedBlockPreprocessor(md), - "_begin") - - -class FencedBlockPreprocessor(markdown.preprocessors.Preprocessor): - - def run(self, lines): - """ Match and store Fenced Code Blocks in the HtmlStash. """ - text = "\n".join(lines) - while 1: - m = FENCED_BLOCK_RE.search(text) - if m: - lang = '' - if m.group('lang'): - lang = LANG_TAG % m.group('lang') - code = CODE_WRAP % (lang, self._escape(m.group('code'))) - placeholder = self.markdown.htmlStash.store(code, safe=True) - text = '%s\n%s\n%s'% (text[:m.start()], placeholder, text[m.end():]) - else: - break - return text.split("\n") - - def _escape(self, txt): - """ basic html escaping """ - txt = txt.replace('&', '&') - txt = txt.replace('<', '<') - txt = txt.replace('>', '>') - txt = txt.replace('"', '"') - return txt - - -def makeExtension(configs=None): - return FencedCodeExtension() - - -if __name__ == "__main__": - import doctest - doctest.testmod() diff --git a/addon-sdk/source/python-lib/markdown/extensions/footnotes.py b/addon-sdk/source/python-lib/markdown/extensions/footnotes.py deleted file mode 100644 index 6dacab7b2cc2..000000000000 --- a/addon-sdk/source/python-lib/markdown/extensions/footnotes.py +++ /dev/null @@ -1,293 +0,0 @@ -""" -========================= FOOTNOTES ================================= - -This section adds footnote handling to markdown. It can be used as -an example for extending python-markdown with relatively complex -functionality. While in this case the extension is included inside -the module itself, it could just as easily be added from outside the -module. Not that all markdown classes above are ignorant about -footnotes. All footnote functionality is provided separately and -then added to the markdown instance at the run time. - -Footnote functionality is attached by calling extendMarkdown() -method of FootnoteExtension. The method also registers the -extension to allow it's state to be reset by a call to reset() -method. - -Example: - Footnotes[^1] have a label[^label] and a definition[^!DEF]. - - [^1]: This is a footnote - [^label]: A footnote on "label" - [^!DEF]: The footnote for definition - -""" - -import re, markdown -from markdown import etree - -FN_BACKLINK_TEXT = "zz1337820767766393qq" -NBSP_PLACEHOLDER = "qq3936677670287331zz" -DEF_RE = re.compile(r'(\ ?\ ?\ ?)\[\^([^\]]*)\]:\s*(.*)') -TABBED_RE = re.compile(r'((\t)|( ))(.*)') - -class FootnoteExtension(markdown.Extension): - """ Footnote Extension. """ - - def __init__ (self, configs): - """ Setup configs. """ - self.config = {'PLACE_MARKER': - ["///Footnotes Go Here///", - "The text string that marks where the footnotes go"]} - - for key, value in configs: - self.config[key][0] = value - - self.reset() - - def extendMarkdown(self, md, md_globals): - """ Add pieces to Markdown. """ - md.registerExtension(self) - self.parser = md.parser - # Insert a preprocessor before ReferencePreprocessor - md.preprocessors.add("footnote", FootnotePreprocessor(self), - "<reference") - # Insert an inline pattern before ImageReferencePattern - FOOTNOTE_RE = r'\[\^([^\]]*)\]' # blah blah [^1] blah - md.inlinePatterns.add("footnote", FootnotePattern(FOOTNOTE_RE, self), - "<reference") - # Insert a tree-processor that would actually add the footnote div - # This must be before the inline treeprocessor so inline patterns - # run on the contents of the div. - md.treeprocessors.add("footnote", FootnoteTreeprocessor(self), - "<inline") - # Insert a postprocessor after amp_substitute oricessor - md.postprocessors.add("footnote", FootnotePostprocessor(self), - ">amp_substitute") - - def reset(self): - """ Clear the footnotes on reset. """ - self.footnotes = markdown.odict.OrderedDict() - - def findFootnotesPlaceholder(self, root): - """ Return ElementTree Element that contains Footnote placeholder. """ - def finder(element): - for child in element: - if child.text: - if child.text.find(self.getConfig("PLACE_MARKER")) > -1: - return child, True - if child.tail: - if child.tail.find(self.getConfig("PLACE_MARKER")) > -1: - return (child, element), False - finder(child) - return None - - res = finder(root) - return res - - def setFootnote(self, id, text): - """ Store a footnote for later retrieval. """ - self.footnotes[id] = text - - def makeFootnoteId(self, id): - """ Return footnote link id. """ - return 'fn:%s' % id - - def makeFootnoteRefId(self, id): - """ Return footnote back-link id. """ - return 'fnref:%s' % id - - def makeFootnotesDiv(self, root): - """ Return div of footnotes as et Element. """ - - if not self.footnotes.keys(): - return None - - div = etree.Element("div") - div.set('class', 'footnote') - hr = etree.SubElement(div, "hr") - ol = etree.SubElement(div, "ol") - - for id in self.footnotes.keys(): - li = etree.SubElement(ol, "li") - li.set("id", self.makeFootnoteId(id)) - self.parser.parseChunk(li, self.footnotes[id]) - backlink = etree.Element("a") - backlink.set("href", "#" + self.makeFootnoteRefId(id)) - backlink.set("rev", "footnote") - backlink.set("title", "Jump back to footnote %d in the text" % \ - (self.footnotes.index(id)+1)) - backlink.text = FN_BACKLINK_TEXT - - if li.getchildren(): - node = li[-1] - if node.tag == "p": - node.text = node.text + NBSP_PLACEHOLDER - node.append(backlink) - else: - p = etree.SubElement(li, "p") - p.append(backlink) - return div - - -class FootnotePreprocessor(markdown.preprocessors.Preprocessor): - """ Find all footnote references and store for later use. """ - - def __init__ (self, footnotes): - self.footnotes = footnotes - - def run(self, lines): - lines = self._handleFootnoteDefinitions(lines) - text = "\n".join(lines) - return text.split("\n") - - def _handleFootnoteDefinitions(self, lines): - """ - Recursively find all footnote definitions in lines. - - Keywords: - - * lines: A list of lines of text - - Return: A list of lines with footnote definitions removed. - - """ - i, id, footnote = self._findFootnoteDefinition(lines) - - if id : - plain = lines[:i] - detabbed, theRest = self.detectTabbed(lines[i+1:]) - self.footnotes.setFootnote(id, - footnote + "\n" - + "\n".join(detabbed)) - more_plain = self._handleFootnoteDefinitions(theRest) - return plain + [""] + more_plain - else : - return lines - - def _findFootnoteDefinition(self, lines): - """ - Find the parts of a footnote definition. - - Keywords: - - * lines: A list of lines of text. - - Return: A three item tuple containing the index of the first line of a - footnote definition, the id of the definition and the body of the - definition. - - """ - counter = 0 - for line in lines: - m = DEF_RE.match(line) - if m: - return counter, m.group(2), m.group(3) - counter += 1 - return counter, None, None - - def detectTabbed(self, lines): - """ Find indented text and remove indent before further proccesing. - - Keyword arguments: - - * lines: an array of strings - - Returns: a list of post processed items and the unused - remainder of the original list - - """ - items = [] - item = -1 - i = 0 # to keep track of where we are - - def detab(line): - match = TABBED_RE.match(line) - if match: - return match.group(4) - - for line in lines: - if line.strip(): # Non-blank line - line = detab(line) - if line: - items.append(line) - i += 1 - continue - else: - return items, lines[i:] - - else: # Blank line: _maybe_ we are done. - i += 1 # advance - - # Find the next non-blank line - for j in range(i, len(lines)): - if lines[j].strip(): - next_line = lines[j]; break - else: - break # There is no more text; we are done. - - # Check if the next non-blank line is tabbed - if detab(next_line): # Yes, more work to do. - items.append("") - continue - else: - break # No, we are done. - else: - i += 1 - - return items, lines[i:] - - -class FootnotePattern(markdown.inlinepatterns.Pattern): - """ InlinePattern for footnote markers in a document's body text. """ - - def __init__(self, pattern, footnotes): - markdown.inlinepatterns.Pattern.__init__(self, pattern) - self.footnotes = footnotes - - def handleMatch(self, m): - sup = etree.Element("sup") - a = etree.SubElement(sup, "a") - id = m.group(2) - sup.set('id', self.footnotes.makeFootnoteRefId(id)) - a.set('href', '#' + self.footnotes.makeFootnoteId(id)) - a.set('rel', 'footnote') - a.text = str(self.footnotes.footnotes.index(id) + 1) - return sup - - -class FootnoteTreeprocessor(markdown.treeprocessors.Treeprocessor): - """ Build and append footnote div to end of document. """ - - def __init__ (self, footnotes): - self.footnotes = footnotes - - def run(self, root): - footnotesDiv = self.footnotes.makeFootnotesDiv(root) - if footnotesDiv: - result = self.footnotes.findFootnotesPlaceholder(root) - if result: - node, isText = result - if isText: - node.text = None - node.getchildren().insert(0, footnotesDiv) - else: - child, element = node - ind = element.getchildren().find(child) - element.getchildren().insert(ind + 1, footnotesDiv) - child.tail = None - fnPlaceholder.parent.replaceChild(fnPlaceholder, footnotesDiv) - else: - root.append(footnotesDiv) - -class FootnotePostprocessor(markdown.postprocessors.Postprocessor): - """ Replace placeholders with html entities. """ - - def run(self, text): - text = text.replace(FN_BACKLINK_TEXT, "↩") - return text.replace(NBSP_PLACEHOLDER, " ") - -def makeExtension(configs=[]): - """ Return an instance of the FootnoteExtension """ - return FootnoteExtension(configs=configs) - diff --git a/addon-sdk/source/python-lib/markdown/extensions/headerid.py b/addon-sdk/source/python-lib/markdown/extensions/headerid.py deleted file mode 100644 index f70a7a9d650c..000000000000 --- a/addon-sdk/source/python-lib/markdown/extensions/headerid.py +++ /dev/null @@ -1,195 +0,0 @@ -#!/usr/bin/python - -""" -HeaderID Extension for Python-Markdown -====================================== - -Adds ability to set HTML IDs for headers. - -Basic usage: - - >>> import markdown - >>> text = "# Some Header # {#some_id}" - >>> md = markdown.markdown(text, ['headerid']) - >>> md - u'<h1 id="some_id">Some Header</h1>' - -All header IDs are unique: - - >>> text = ''' - ... #Header - ... #Another Header {#header} - ... #Third Header {#header}''' - >>> md = markdown.markdown(text, ['headerid']) - >>> md - u'<h1 id="header">Header</h1>\\n<h1 id="header_1">Another Header</h1>\\n<h1 id="header_2">Third Header</h1>' - -To fit within a html template's hierarchy, set the header base level: - - >>> text = ''' - ... #Some Header - ... ## Next Level''' - >>> md = markdown.markdown(text, ['headerid(level=3)']) - >>> md - u'<h3 id="some_header">Some Header</h3>\\n<h4 id="next_level">Next Level</h4>' - -Turn off auto generated IDs: - - >>> text = ''' - ... # Some Header - ... # Header with ID # { #foo }''' - >>> md = markdown.markdown(text, ['headerid(forceid=False)']) - >>> md - u'<h1>Some Header</h1>\\n<h1 id="foo">Header with ID</h1>' - -Use with MetaData extension: - - >>> text = '''header_level: 2 - ... header_forceid: Off - ... - ... # A Header''' - >>> md = markdown.markdown(text, ['headerid', 'meta']) - >>> md - u'<h2>A Header</h2>' - -Copyright 2007-2008 [Waylan Limberg](http://achinghead.com/). - -Project website: <http://www.freewisdom.org/project/python-markdown/HeaderId> -Contact: markdown@freewisdom.org - -License: BSD (see ../docs/LICENSE for details) - -Dependencies: -* [Python 2.3+](http://python.org) -* [Markdown 2.0+](http://www.freewisdom.org/projects/python-markdown/) - -""" - -import markdown -from markdown import etree -import re -from string import ascii_lowercase, digits, punctuation - -ID_CHARS = ascii_lowercase + digits + '-_' -IDCOUNT_RE = re.compile(r'^(.*)_([0-9]+)$') - - -class HeaderIdProcessor(markdown.blockprocessors.BlockProcessor): - """ Replacement BlockProcessor for Header IDs. """ - - # Detect a header at start of any line in block - RE = re.compile(r"""(^|\n) - (?P<level>\#{1,6}) # group('level') = string of hashes - (?P<header>.*?) # group('header') = Header text - \#* # optional closing hashes - (?:[ \t]*\{[ \t]*\#(?P<id>[-_:a-zA-Z0-9]+)[ \t]*\})? - (\n|$) # ^^ group('id') = id attribute - """, - re.VERBOSE) - - IDs = [] - - def test(self, parent, block): - return bool(self.RE.search(block)) - - def run(self, parent, blocks): - block = blocks.pop(0) - m = self.RE.search(block) - if m: - before = block[:m.start()] # All lines before header - after = block[m.end():] # All lines after header - if before: - # As the header was not the first line of the block and the - # lines before the header must be parsed first, - # recursively parse this lines as a block. - self.parser.parseBlocks(parent, [before]) - # Create header using named groups from RE - start_level, force_id = self._get_meta() - level = len(m.group('level')) + start_level - if level > 6: - level = 6 - h = markdown.etree.SubElement(parent, 'h%d' % level) - h.text = m.group('header').strip() - if m.group('id'): - h.set('id', self._unique_id(m.group('id'))) - elif force_id: - h.set('id', self._create_id(m.group('header').strip())) - if after: - # Insert remaining lines as first block for future parsing. - blocks.insert(0, after) - else: - # This should never happen, but just in case... - message(CRITICAL, "We've got a problem header!") - - def _get_meta(self): - """ Return meta data suported by this ext as a tuple """ - level = int(self.config['level'][0]) - 1 - force = self._str2bool(self.config['forceid'][0]) - if hasattr(self.md, 'Meta'): - if self.md.Meta.has_key('header_level'): - level = int(self.md.Meta['header_level'][0]) - 1 - if self.md.Meta.has_key('header_forceid'): - force = self._str2bool(self.md.Meta['header_forceid'][0]) - return level, force - - def _str2bool(self, s, default=False): - """ Convert a string to a booleen value. """ - s = str(s) - if s.lower() in ['0', 'f', 'false', 'off', 'no', 'n']: - return False - elif s.lower() in ['1', 't', 'true', 'on', 'yes', 'y']: - return True - return default - - def _unique_id(self, id): - """ Ensure ID is unique. Append '_1', '_2'... if not """ - while id in self.IDs: - m = IDCOUNT_RE.match(id) - if m: - id = '%s_%d'% (m.group(1), int(m.group(2))+1) - else: - id = '%s_%d'% (id, 1) - self.IDs.append(id) - return id - - def _create_id(self, header): - """ Return ID from Header text. """ - h = '' - for c in header.lower().replace(' ', '_'): - if c in ID_CHARS: - h += c - elif c not in punctuation: - h += '+' - return self._unique_id(h) - - -class HeaderIdExtension (markdown.Extension): - def __init__(self, configs): - # set defaults - self.config = { - 'level' : ['1', 'Base level for headers.'], - 'forceid' : ['True', 'Force all headers to have an id.'] - } - - for key, value in configs: - self.setConfig(key, value) - - def extendMarkdown(self, md, md_globals): - md.registerExtension(self) - self.processor = HeaderIdProcessor(md.parser) - self.processor.md = md - self.processor.config = self.config - # Replace existing hasheader in place. - md.parser.blockprocessors['hashheader'] = self.processor - - def reset(self): - self.processor.IDs = [] - - -def makeExtension(configs=None): - return HeaderIdExtension(configs=configs) - -if __name__ == "__main__": - import doctest - doctest.testmod() - diff --git a/addon-sdk/source/python-lib/markdown/extensions/html_tidy.py b/addon-sdk/source/python-lib/markdown/extensions/html_tidy.py deleted file mode 100644 index 5105e33bc96b..000000000000 --- a/addon-sdk/source/python-lib/markdown/extensions/html_tidy.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python - -""" -HTML Tidy Extension for Python-Markdown -======================================= - -Runs [HTML Tidy][] on the output of Python-Markdown using the [uTidylib][] -Python wrapper. Both libtidy and uTidylib must be installed on your system. - -Note than any Tidy [options][] can be passed in as extension configs. So, -for example, to output HTML rather than XHTML, set ``output_xhtml=0``. To -indent the output, set ``indent=auto`` and to have Tidy wrap the output in -``<html>`` and ``<body>`` tags, set ``show_body_only=0``. - -[HTML Tidy]: http://tidy.sourceforge.net/ -[uTidylib]: http://utidylib.berlios.de/ -[options]: http://tidy.sourceforge.net/docs/quickref.html - -Copyright (c)2008 [Waylan Limberg](http://achinghead.com) - -License: [BSD](http://www.opensource.org/licenses/bsd-license.php) - -Dependencies: -* [Python2.3+](http://python.org) -* [Markdown 2.0+](http://www.freewisdom.org/projects/python-markdown/) -* [HTML Tidy](http://utidylib.berlios.de/) -* [uTidylib](http://utidylib.berlios.de/) - -""" - -import markdown -import tidy - -class TidyExtension(markdown.Extension): - - def __init__(self, configs): - # Set defaults to match typical markdown behavior. - self.config = dict(output_xhtml=1, - show_body_only=1, - ) - # Merge in user defined configs overriding any present if nessecary. - for c in configs: - self.config[c[0]] = c[1] - - def extendMarkdown(self, md, md_globals): - # Save options to markdown instance - md.tidy_options = self.config - # Add TidyProcessor to postprocessors - md.postprocessors['tidy'] = TidyProcessor(md) - - -class TidyProcessor(markdown.postprocessors.Postprocessor): - - def run(self, text): - # Pass text to Tidy. As Tidy does not accept unicode we need to encode - # it and decode its return value. - return unicode(tidy.parseString(text.encode('utf-8'), - **self.markdown.tidy_options)) - - -def makeExtension(configs=None): - return TidyExtension(configs=configs) diff --git a/addon-sdk/source/python-lib/markdown/extensions/imagelinks.py b/addon-sdk/source/python-lib/markdown/extensions/imagelinks.py deleted file mode 100644 index ee0b708c7930..000000000000 --- a/addon-sdk/source/python-lib/markdown/extensions/imagelinks.py +++ /dev/null @@ -1,119 +0,0 @@ -""" -========================= IMAGE LINKS ================================= - - -Turns paragraphs like - -<~~~~~~~~~~~~~~~~~~~~~~~~ -dir/subdir -dir/subdir -dir/subdir -~~~~~~~~~~~~~~ -dir/subdir -dir/subdir -dir/subdir -~~~~~~~~~~~~~~~~~~~> - -Into mini-photo galleries. - -""" - -import re, markdown -import url_manager - - -IMAGE_LINK = """<a href="%s"><img src="%s" title="%s"/></a>""" -SLIDESHOW_LINK = """<a href="%s" target="_blank">[slideshow]</a>""" -ALBUM_LINK = """ <a href="%s">[%s]</a>""" - - -class ImageLinksExtension(markdown.Extension): - - def extendMarkdown(self, md, md_globals): - - md.preprocessors.add("imagelink", ImageLinkPreprocessor(md), "_begin") - - -class ImageLinkPreprocessor(markdown.preprocessors.Preprocessor): - - def run(self, lines): - - url = url_manager.BlogEntryUrl(url_manager.BlogUrl("all"), - "2006/08/29/the_rest_of_our") - - - all_images = [] - blocks = [] - in_image_block = False - - new_lines = [] - - for line in lines: - - if line.startswith("<~~~~~~~"): - albums = [] - rows = [] - in_image_block = True - - if not in_image_block: - - new_lines.append(line) - - else: - - line = line.strip() - - if line.endswith("~~~~~~>") or not line: - in_image_block = False - new_block = "<div><br/><center><span class='image-links'>\n" - - album_url_hash = {} - - for row in rows: - for photo_url, title in row: - new_block += " " - new_block += IMAGE_LINK % (photo_url, - photo_url.get_thumbnail(), - title) - - album_url_hash[str(photo_url.get_album())] = 1 - - new_block += "<br/>" - - new_block += "</span>" - new_block += SLIDESHOW_LINK % url.get_slideshow() - - album_urls = album_url_hash.keys() - album_urls.sort() - - if len(album_urls) == 1: - new_block += ALBUM_LINK % (album_urls[0], "complete album") - else : - for i in range(len(album_urls)) : - new_block += ALBUM_LINK % (album_urls[i], - "album %d" % (i + 1) ) - - new_lines.append(new_block + "</center><br/></div>") - - elif line[1:6] == "~~~~~" : - rows.append([]) # start a new row - else : - parts = line.split() - line = parts[0] - title = " ".join(parts[1:]) - - album, photo = line.split("/") - photo_url = url.get_photo(album, photo, - len(all_images)+1) - all_images.append(photo_url) - rows[-1].append((photo_url, title)) - - if not album in albums : - albums.append(album) - - return new_lines - - -def makeExtension(configs): - return ImageLinksExtension(configs) - diff --git a/addon-sdk/source/python-lib/markdown/extensions/meta.py b/addon-sdk/source/python-lib/markdown/extensions/meta.py deleted file mode 100644 index 1b555b22d22e..000000000000 --- a/addon-sdk/source/python-lib/markdown/extensions/meta.py +++ /dev/null @@ -1,90 +0,0 @@ -#!usr/bin/python - -""" -Meta Data Extension for Python-Markdown -======================================= - -This extension adds Meta Data handling to markdown. - -Basic Usage: - - >>> import markdown - >>> text = '''Title: A Test Doc. - ... Author: Waylan Limberg - ... John Doe - ... Blank_Data: - ... - ... The body. This is paragraph one. - ... ''' - >>> md = markdown.Markdown(['meta']) - >>> md.convert(text) - u'<p>The body. This is paragraph one.</p>' - >>> md.Meta - {u'blank_data': [u''], u'author': [u'Waylan Limberg', u'John Doe'], u'title': [u'A Test Doc.']} - -Make sure text without Meta Data still works (markdown < 1.6b returns a <p>). - - >>> text = ' Some Code - not extra lines of meta data.' - >>> md = markdown.Markdown(['meta']) - >>> md.convert(text) - u'<pre><code>Some Code - not extra lines of meta data.\\n</code></pre>' - >>> md.Meta - {} - -Copyright 2007-2008 [Waylan Limberg](http://achinghead.com). - -Project website: <http://www.freewisdom.org/project/python-markdown/Meta-Data> -Contact: markdown@freewisdom.org - -License: BSD (see ../docs/LICENSE for details) - -""" - -import markdown, re - -# Global Vars -META_RE = re.compile(r'^[ ]{0,3}(?P<key>[A-Za-z0-9_-]+):\s*(?P<value>.*)') -META_MORE_RE = re.compile(r'^[ ]{4,}(?P<value>.*)') - -class MetaExtension (markdown.Extension): - """ Meta-Data extension for Python-Markdown. """ - - def extendMarkdown(self, md, md_globals): - """ Add MetaPreprocessor to Markdown instance. """ - - md.preprocessors.add("meta", MetaPreprocessor(md), "_begin") - - -class MetaPreprocessor(markdown.preprocessors.Preprocessor): - """ Get Meta-Data. """ - - def run(self, lines): - """ Parse Meta-Data and store in Markdown.Meta. """ - meta = {} - key = None - while 1: - line = lines.pop(0) - if line.strip() == '': - break # blank line - done - m1 = META_RE.match(line) - if m1: - key = m1.group('key').lower().strip() - meta[key] = [m1.group('value').strip()] - else: - m2 = META_MORE_RE.match(line) - if m2 and key: - # Add another line to existing key - meta[key].append(m2.group('value').strip()) - else: - lines.insert(0, line) - break # no meta data - done - self.markdown.Meta = meta - return lines - - -def makeExtension(configs={}): - return MetaExtension(configs=configs) - -if __name__ == "__main__": - import doctest - doctest.testmod() diff --git a/addon-sdk/source/python-lib/markdown/extensions/rss.py b/addon-sdk/source/python-lib/markdown/extensions/rss.py deleted file mode 100644 index 1274da260ea0..000000000000 --- a/addon-sdk/source/python-lib/markdown/extensions/rss.py +++ /dev/null @@ -1,114 +0,0 @@ -import markdown -from markdown import etree - -DEFAULT_URL = "http://www.freewisdom.org/projects/python-markdown/" -DEFAULT_CREATOR = "Yuri Takhteyev" -DEFAULT_TITLE = "Markdown in Python" -GENERATOR = "http://www.freewisdom.org/projects/python-markdown/markdown2rss" - -month_map = { "Jan" : "01", - "Feb" : "02", - "March" : "03", - "April" : "04", - "May" : "05", - "June" : "06", - "July" : "07", - "August" : "08", - "September" : "09", - "October" : "10", - "November" : "11", - "December" : "12" } - -def get_time(heading): - - heading = heading.split("-")[0] - heading = heading.strip().replace(",", " ").replace(".", " ") - - month, date, year = heading.split() - month = month_map[month] - - return rdftime(" ".join((month, date, year, "12:00:00 AM"))) - -def rdftime(time): - - time = time.replace(":", " ") - time = time.replace("/", " ") - time = time.split() - return "%s-%s-%sT%s:%s:%s-08:00" % (time[0], time[1], time[2], - time[3], time[4], time[5]) - - -def get_date(text): - return "date" - -class RssExtension (markdown.Extension): - - def extendMarkdown(self, md, md_globals): - - self.config = { 'URL' : [DEFAULT_URL, "Main URL"], - 'CREATOR' : [DEFAULT_CREATOR, "Feed creator's name"], - 'TITLE' : [DEFAULT_TITLE, "Feed title"] } - - md.xml_mode = True - - # Insert a tree-processor that would actually add the title tag - treeprocessor = RssTreeProcessor(md) - treeprocessor.ext = self - md.treeprocessors['rss'] = treeprocessor - md.stripTopLevelTags = 0 - md.docType = '<?xml version="1.0" encoding="utf-8"?>\n' - -class RssTreeProcessor(markdown.treeprocessors.Treeprocessor): - - def run (self, root): - - rss = etree.Element("rss") - rss.set("version", "2.0") - - channel = etree.SubElement(rss, "channel") - - for tag, text in (("title", self.ext.getConfig("TITLE")), - ("link", self.ext.getConfig("URL")), - ("description", None)): - - element = etree.SubElement(channel, tag) - element.text = text - - for child in root: - - if child.tag in ["h1", "h2", "h3", "h4", "h5"]: - - heading = child.text.strip() - item = etree.SubElement(channel, "item") - link = etree.SubElement(item, "link") - link.text = self.ext.getConfig("URL") - title = etree.SubElement(item, "title") - title.text = heading - - guid = ''.join([x for x in heading if x.isalnum()]) - guidElem = etree.SubElement(item, "guid") - guidElem.text = guid - guidElem.set("isPermaLink", "false") - - elif child.tag in ["p"]: - try: - description = etree.SubElement(item, "description") - except UnboundLocalError: - # Item not defined - moving on - pass - else: - if len(child): - content = "\n".join([etree.tostring(node) - for node in child]) - else: - content = child.text - pholder = self.markdown.htmlStash.store( - "<![CDATA[ %s]]>" % content) - description.text = pholder - - return rss - - -def makeExtension(configs): - - return RssExtension(configs) diff --git a/addon-sdk/source/python-lib/markdown/extensions/tables.py b/addon-sdk/source/python-lib/markdown/extensions/tables.py deleted file mode 100644 index 1d3c92054a4d..000000000000 --- a/addon-sdk/source/python-lib/markdown/extensions/tables.py +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env Python -""" -Tables Extension for Python-Markdown -==================================== - -Added parsing of tables to Python-Markdown. - -A simple example: - - First Header | Second Header - ------------- | ------------- - Content Cell | Content Cell - Content Cell | Content Cell - -Copyright 2009 - [Waylan Limberg](http://achinghead.com) -""" -import markdown -from markdown import etree - - -class TableProcessor(markdown.blockprocessors.BlockProcessor): - """ Process Tables. """ - - def test(self, parent, block): - rows = block.split('\n') - return (len(rows) > 2 and '|' in rows[0] and - '|' in rows[1] and '-' in rows[1] and - rows[1][0] in ['|', ':', '-']) - - def run(self, parent, blocks): - """ Parse a table block and build table. """ - block = blocks.pop(0).split('\n') - header = block[:2] - rows = block[2:] - # Get format type (bordered by pipes or not) - border = False - if header[0].startswith('|'): - border = True - # Get alignment of columns - align = [] - for c in self._split_row(header[1], border): - if c.startswith(':') and c.endswith(':'): - align.append('center') - elif c.startswith(':'): - align.append('left') - elif c.endswith(':'): - align.append('right') - else: - align.append(None) - # Build table - table = etree.SubElement(parent, 'table') - thead = etree.SubElement(table, 'thead') - self._build_row(header[0], thead, align, border) - tbody = etree.SubElement(table, 'tbody') - for row in rows: - self._build_row(row, tbody, align, border) - - def _build_row(self, row, parent, align, border): - """ Given a row of text, build table cells. """ - tr = etree.SubElement(parent, 'tr') - tag = 'td' - if parent.tag == 'thead': - tag = 'th' - cells = self._split_row(row, border) - # We use align here rather than cells to ensure every row - # contains the same number of columns. - for i, a in enumerate(align): - c = etree.SubElement(tr, tag) - try: - c.text = cells[i].strip() - except IndexError: - c.text = "" - if a: - c.set('align', a) - - def _split_row(self, row, border): - """ split a row of text into list of cells. """ - if border: - if row.startswith('|'): - row = row[1:] - if row.endswith('|'): - row = row[:-1] - return row.split('|') - - -class TableExtension(markdown.Extension): - """ Add tables to Markdown. """ - - def extendMarkdown(self, md, md_globals): - """ Add an instance of TableProcessor to BlockParser. """ - md.parser.blockprocessors.add('table', - TableProcessor(md.parser), - '<hashheader') - - -def makeExtension(configs={}): - return TableExtension(configs=configs) diff --git a/addon-sdk/source/python-lib/markdown/extensions/toc.py b/addon-sdk/source/python-lib/markdown/extensions/toc.py deleted file mode 100644 index 1624ccf64b90..000000000000 --- a/addon-sdk/source/python-lib/markdown/extensions/toc.py +++ /dev/null @@ -1,140 +0,0 @@ -""" -Table of Contents Extension for Python-Markdown -* * * - -(c) 2008 [Jack Miller](http://codezen.org) - -Dependencies: -* [Markdown 2.0+](http://www.freewisdom.org/projects/python-markdown/) - -""" -import markdown -from markdown import etree -import re - -class TocTreeprocessor(markdown.treeprocessors.Treeprocessor): - # Iterator wrapper to get parent and child all at once - def iterparent(self, root): - for parent in root.getiterator(): - for child in parent: - yield parent, child - - def run(self, doc): - div = etree.Element("div") - div.attrib["class"] = "toc" - last_li = None - - # Add title to the div - if self.config["title"][0]: - header = etree.SubElement(div, "span") - header.attrib["class"] = "toctitle" - header.text = self.config["title"][0] - - level = 0 - list_stack=[div] - header_rgx = re.compile("[Hh][123456]") - - # Get a list of id attributes - used_ids = [] - for c in doc.getiterator(): - if "id" in c.attrib: - used_ids.append(c.attrib["id"]) - - for (p, c) in self.iterparent(doc): - if not c.text: - continue - - # To keep the output from screwing up the - # validation by putting a <div> inside of a <p> - # we actually replace the <p> in its entirety. - # We do not allow the marker inside a header as that - # would causes an enless loop of placing a new TOC - # inside previously generated TOC. - - if c.text.find(self.config["marker"][0]) > -1 and not header_rgx.match(c.tag): - for i in range(len(p)): - if p[i] == c: - p[i] = div - break - - if header_rgx.match(c.tag): - tag_level = int(c.tag[-1]) - - # Regardless of how many levels we jumped - # only one list should be created, since - # empty lists containing lists are illegal. - - if tag_level < level: - list_stack.pop() - level = tag_level - - if tag_level > level: - newlist = etree.Element("ul") - if last_li: - last_li.append(newlist) - else: - list_stack[-1].append(newlist) - list_stack.append(newlist) - level = tag_level - - # Do not override pre-existing ids - if not "id" in c.attrib: - id = self.config["slugify"][0](c.text) - if id in used_ids: - ctr = 1 - while "%s_%d" % (id, ctr) in used_ids: - ctr += 1 - id = "%s_%d" % (id, ctr) - used_ids.append(id) - c.attrib["id"] = id - else: - id = c.attrib["id"] - - # List item link, to be inserted into the toc div - last_li = etree.Element("li") - link = etree.SubElement(last_li, "a") - link.text = c.text - link.attrib["href"] = '#' + id - - if int(self.config["anchorlink"][0]): - anchor = etree.SubElement(c, "a") - anchor.text = c.text - anchor.attrib["href"] = "#" + id - anchor.attrib["class"] = "toclink" - c.text = "" - - list_stack[-1].append(last_li) - -class TocExtension(markdown.Extension): - def __init__(self, configs): - self.config = { "marker" : ["[TOC]", - "Text to find and replace with Table of Contents -" - "Defaults to \"[TOC]\""], - "slugify" : [self.slugify, - "Function to generate anchors based on header text-" - "Defaults to a built in slugify function."], - "title" : [None, - "Title to insert into TOC <div> - " - "Defaults to None"], - "anchorlink" : [0, - "1 if header should be a self link" - "Defaults to 0"]} - - for key, value in configs: - self.setConfig(key, value) - - # This is exactly the same as Django's slugify - def slugify(self, value): - """ Slugify a string, to make it URL friendly. """ - import unicodedata - value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore') - value = unicode(re.sub('[^\w\s-]', '', value).strip().lower()) - return re.sub('[-\s]+','-',value) - - def extendMarkdown(self, md, md_globals): - tocext = TocTreeprocessor(md) - tocext.config = self.config - md.treeprocessors.add("toc", tocext, "_begin") - -def makeExtension(configs={}): - return TocExtension(configs=configs) diff --git a/addon-sdk/source/python-lib/markdown/extensions/wikilinks.py b/addon-sdk/source/python-lib/markdown/extensions/wikilinks.py deleted file mode 100644 index df44e1cd009e..000000000000 --- a/addon-sdk/source/python-lib/markdown/extensions/wikilinks.py +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/env python - -''' -WikiLinks Extension for Python-Markdown -====================================== - -Converts [[WikiLinks]] to relative links. Requires Python-Markdown 2.0+ - -Basic usage: - - >>> import markdown - >>> text = "Some text with a [[WikiLink]]." - >>> html = markdown.markdown(text, ['wikilinks']) - >>> html - u'<p>Some text with a <a class="wikilink" href="/WikiLink/">WikiLink</a>.</p>' - -Whitespace behavior: - - >>> markdown.markdown('[[ foo bar_baz ]]', ['wikilinks']) - u'<p><a class="wikilink" href="/foo_bar_baz/">foo bar_baz</a></p>' - >>> markdown.markdown('foo [[ ]] bar', ['wikilinks']) - u'<p>foo bar</p>' - -To define custom settings the simple way: - - >>> markdown.markdown(text, - ... ['wikilinks(base_url=/wiki/,end_url=.html,html_class=foo)'] - ... ) - u'<p>Some text with a <a class="foo" href="/wiki/WikiLink.html">WikiLink</a>.</p>' - -Custom settings the complex way: - - >>> md = markdown.Markdown( - ... extensions = ['wikilinks'], - ... extension_configs = {'wikilinks': [ - ... ('base_url', 'http://example.com/'), - ... ('end_url', '.html'), - ... ('html_class', '') ]}, - ... safe_mode = True) - >>> md.convert(text) - u'<p>Some text with a <a href="http://example.com/WikiLink.html">WikiLink</a>.</p>' - -Use MetaData with mdx_meta.py (Note the blank html_class in MetaData): - - >>> text = """wiki_base_url: http://example.com/ - ... wiki_end_url: .html - ... wiki_html_class: - ... - ... Some text with a [[WikiLink]].""" - >>> md = markdown.Markdown(extensions=['meta', 'wikilinks']) - >>> md.convert(text) - u'<p>Some text with a <a href="http://example.com/WikiLink.html">WikiLink</a>.</p>' - -MetaData should not carry over to next document: - - >>> md.convert("No [[MetaData]] here.") - u'<p>No <a class="wikilink" href="/MetaData/">MetaData</a> here.</p>' - -Define a custom URL builder: - - >>> def my_url_builder(label, base, end): - ... return '/bar/' - >>> md = markdown.Markdown(extensions=['wikilinks'], - ... extension_configs={'wikilinks' : [('build_url', my_url_builder)]}) - >>> md.convert('[[foo]]') - u'<p><a class="wikilink" href="/bar/">foo</a></p>' - -From the command line: - - python markdown.py -x wikilinks(base_url=http://example.com/,end_url=.html,html_class=foo) src.txt - -By [Waylan Limberg](http://achinghead.com/). - -License: [BSD](http://www.opensource.org/licenses/bsd-license.php) - -Dependencies: -* [Python 2.3+](http://python.org) -* [Markdown 2.0+](http://www.freewisdom.org/projects/python-markdown/) -''' - -import markdown -import re - -def build_url(label, base, end): - """ Build a url from the label, a base, and an end. """ - clean_label = re.sub(r'([ ]+_)|(_[ ]+)|([ ]+)', '_', label) - return '%s%s%s'% (base, clean_label, end) - - -class WikiLinkExtension(markdown.Extension): - def __init__(self, configs): - # set extension defaults - self.config = { - 'base_url' : ['/', 'String to append to beginning or URL.'], - 'end_url' : ['/', 'String to append to end of URL.'], - 'html_class' : ['wikilink', 'CSS hook. Leave blank for none.'], - 'build_url' : [build_url, 'Callable formats URL from label.'], - } - - # Override defaults with user settings - for key, value in configs : - self.setConfig(key, value) - - def extendMarkdown(self, md, md_globals): - self.md = md - - # append to end of inline patterns - WIKILINK_RE = r'\[\[([A-Za-z0-9_ -]+)\]\]' - wikilinkPattern = WikiLinks(WIKILINK_RE, self.config) - wikilinkPattern.md = md - md.inlinePatterns.add('wikilink', wikilinkPattern, "<not_strong") - - -class WikiLinks(markdown.inlinepatterns.Pattern): - def __init__(self, pattern, config): - markdown.inlinepatterns.Pattern.__init__(self, pattern) - self.config = config - - def handleMatch(self, m): - if m.group(2).strip(): - base_url, end_url, html_class = self._getMeta() - label = m.group(2).strip() - url = self.config['build_url'][0](label, base_url, end_url) - a = markdown.etree.Element('a') - a.text = label - a.set('href', url) - if html_class: - a.set('class', html_class) - else: - a = '' - return a - - def _getMeta(self): - """ Return meta data or config data. """ - base_url = self.config['base_url'][0] - end_url = self.config['end_url'][0] - html_class = self.config['html_class'][0] - if hasattr(self.md, 'Meta'): - if self.md.Meta.has_key('wiki_base_url'): - base_url = self.md.Meta['wiki_base_url'][0] - if self.md.Meta.has_key('wiki_end_url'): - end_url = self.md.Meta['wiki_end_url'][0] - if self.md.Meta.has_key('wiki_html_class'): - html_class = self.md.Meta['wiki_html_class'][0] - return base_url, end_url, html_class - - -def makeExtension(configs=None) : - return WikiLinkExtension(configs=configs) - - -if __name__ == "__main__": - import doctest - doctest.testmod() - diff --git a/addon-sdk/source/python-lib/markdown/html4.py b/addon-sdk/source/python-lib/markdown/html4.py deleted file mode 100644 index 08f241d57aae..000000000000 --- a/addon-sdk/source/python-lib/markdown/html4.py +++ /dev/null @@ -1,274 +0,0 @@ -# markdown/html4.py -# -# Add html4 serialization to older versions of Elementree -# Taken from ElementTree 1.3 preview with slight modifications -# -# Copyright (c) 1999-2007 by Fredrik Lundh. All rights reserved. -# -# fredrik@pythonware.com -# http://www.pythonware.com -# -# -------------------------------------------------------------------- -# The ElementTree toolkit is -# -# Copyright (c) 1999-2007 by Fredrik Lundh -# -# By obtaining, using, and/or copying this software and/or its -# associated documentation, you agree that you have read, understood, -# and will comply with the following terms and conditions: -# -# Permission to use, copy, modify, and distribute this software and -# its associated documentation for any purpose and without fee is -# hereby granted, provided that the above copyright notice appears in -# all copies, and that both that copyright notice and this permission -# notice appear in supporting documentation, and that the name of -# Secret Labs AB or the author not be used in advertising or publicity -# pertaining to distribution of the software without specific, written -# prior permission. -# -# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD -# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- -# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR -# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY -# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, -# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS -# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE -# OF THIS SOFTWARE. -# -------------------------------------------------------------------- - - -import markdown -ElementTree = markdown.etree.ElementTree -QName = markdown.etree.QName -Comment = markdown.etree.Comment -PI = markdown.etree.PI -ProcessingInstruction = markdown.etree.ProcessingInstruction - -HTML_EMPTY = ("area", "base", "basefont", "br", "col", "frame", "hr", - "img", "input", "isindex", "link", "meta" "param") - -try: - HTML_EMPTY = set(HTML_EMPTY) -except NameError: - pass - -_namespace_map = { - # "well-known" namespace prefixes - "http://www.w3.org/XML/1998/namespace": "xml", - "http://www.w3.org/1999/xhtml": "html", - "http://www.w3.org/1999/02/22-rdf-syntax-ns#": "rdf", - "http://schemas.xmlsoap.org/wsdl/": "wsdl", - # xml schema - "http://www.w3.org/2001/XMLSchema": "xs", - "http://www.w3.org/2001/XMLSchema-instance": "xsi", - # dublic core - "http://purl.org/dc/elements/1.1/": "dc", -} - - -def _raise_serialization_error(text): - raise TypeError( - "cannot serialize %r (type %s)" % (text, type(text).__name__) - ) - -def _encode(text, encoding): - try: - return text.encode(encoding, "xmlcharrefreplace") - except (TypeError, AttributeError): - _raise_serialization_error(text) - -def _escape_cdata(text, encoding): - # escape character data - try: - # it's worth avoiding do-nothing calls for strings that are - # shorter than 500 character, or so. assume that's, by far, - # the most common case in most applications. - if "&" in text: - text = text.replace("&", "&") - if "<" in text: - text = text.replace("<", "<") - if ">" in text: - text = text.replace(">", ">") - return text.encode(encoding, "xmlcharrefreplace") - except (TypeError, AttributeError): - _raise_serialization_error(text) - - -def _escape_attrib(text, encoding): - # escape attribute value - try: - if "&" in text: - text = text.replace("&", "&") - if "<" in text: - text = text.replace("<", "<") - if ">" in text: - text = text.replace(">", ">") - if "\"" in text: - text = text.replace("\"", """) - if "\n" in text: - text = text.replace("\n", " ") - return text.encode(encoding, "xmlcharrefreplace") - except (TypeError, AttributeError): - _raise_serialization_error(text) - -def _escape_attrib_html(text, encoding): - # escape attribute value - try: - if "&" in text: - text = text.replace("&", "&") - if ">" in text: - text = text.replace(">", ">") - if "\"" in text: - text = text.replace("\"", """) - return text.encode(encoding, "xmlcharrefreplace") - except (TypeError, AttributeError): - _raise_serialization_error(text) - - -def _serialize_html(write, elem, encoding, qnames, namespaces): - tag = elem.tag - text = elem.text - if tag is Comment: - write("<!--%s-->" % _escape_cdata(text, encoding)) - elif tag is ProcessingInstruction: - write("<?%s?>" % _escape_cdata(text, encoding)) - else: - tag = qnames[tag] - if tag is None: - if text: - write(_escape_cdata(text, encoding)) - for e in elem: - _serialize_html(write, e, encoding, qnames, None) - else: - write("<" + tag) - items = elem.items() - if items or namespaces: - items.sort() # lexical order - for k, v in items: - if isinstance(k, QName): - k = k.text - if isinstance(v, QName): - v = qnames[v.text] - else: - v = _escape_attrib_html(v, encoding) - # FIXME: handle boolean attributes - write(" %s=\"%s\"" % (qnames[k], v)) - if namespaces: - items = namespaces.items() - items.sort(key=lambda x: x[1]) # sort on prefix - for v, k in items: - if k: - k = ":" + k - write(" xmlns%s=\"%s\"" % ( - k.encode(encoding), - _escape_attrib(v, encoding) - )) - write(">") - tag = tag.lower() - if text: - if tag == "script" or tag == "style": - write(_encode(text, encoding)) - else: - write(_escape_cdata(text, encoding)) - for e in elem: - _serialize_html(write, e, encoding, qnames, None) - if tag not in HTML_EMPTY: - write("</" + tag + ">") - if elem.tail: - write(_escape_cdata(elem.tail, encoding)) - -def write_html(root, f, - # keyword arguments - encoding="us-ascii", - default_namespace=None): - assert root is not None - if not hasattr(f, "write"): - f = open(f, "wb") - write = f.write - if not encoding: - encoding = "us-ascii" - qnames, namespaces = _namespaces( - root, encoding, default_namespace - ) - _serialize_html( - write, root, encoding, qnames, namespaces - ) - -# -------------------------------------------------------------------- -# serialization support - -def _namespaces(elem, encoding, default_namespace=None): - # identify namespaces used in this tree - - # maps qnames to *encoded* prefix:local names - qnames = {None: None} - - # maps uri:s to prefixes - namespaces = {} - if default_namespace: - namespaces[default_namespace] = "" - - def encode(text): - return text.encode(encoding) - - def add_qname(qname): - # calculate serialized qname representation - try: - if qname[:1] == "{": - uri, tag = qname[1:].split("}", 1) - prefix = namespaces.get(uri) - if prefix is None: - prefix = _namespace_map.get(uri) - if prefix is None: - prefix = "ns%d" % len(namespaces) - if prefix != "xml": - namespaces[uri] = prefix - if prefix: - qnames[qname] = encode("%s:%s" % (prefix, tag)) - else: - qnames[qname] = encode(tag) # default element - else: - if default_namespace: - # FIXME: can this be handled in XML 1.0? - raise ValueError( - "cannot use non-qualified names with " - "default_namespace option" - ) - qnames[qname] = encode(qname) - except TypeError: - _raise_serialization_error(qname) - - # populate qname and namespaces table - try: - iterate = elem.iter - except AttributeError: - iterate = elem.getiterator # cET compatibility - for elem in iterate(): - tag = elem.tag - if isinstance(tag, QName) and tag.text not in qnames: - add_qname(tag.text) - elif isinstance(tag, basestring): - if tag not in qnames: - add_qname(tag) - elif tag is not None and tag is not Comment and tag is not PI: - _raise_serialization_error(tag) - for key, value in elem.items(): - if isinstance(key, QName): - key = key.text - if key not in qnames: - add_qname(key) - if isinstance(value, QName) and value.text not in qnames: - add_qname(value.text) - text = elem.text - if isinstance(text, QName) and text.text not in qnames: - add_qname(text.text) - return qnames, namespaces - -def to_html_string(element, encoding=None): - class dummy: - pass - data = [] - file = dummy() - file.write = data.append - write_html(ElementTree(element).getroot(),file,encoding) - return "".join(data) diff --git a/addon-sdk/source/python-lib/markdown/inlinepatterns.py b/addon-sdk/source/python-lib/markdown/inlinepatterns.py deleted file mode 100644 index 89fa3b2ef41d..000000000000 --- a/addon-sdk/source/python-lib/markdown/inlinepatterns.py +++ /dev/null @@ -1,371 +0,0 @@ -""" -INLINE PATTERNS -============================================================================= - -Inline patterns such as *emphasis* are handled by means of auxiliary -objects, one per pattern. Pattern objects must be instances of classes -that extend markdown.Pattern. Each pattern object uses a single regular -expression and needs support the following methods: - - pattern.getCompiledRegExp() # returns a regular expression - - pattern.handleMatch(m) # takes a match object and returns - # an ElementTree element or just plain text - -All of python markdown's built-in patterns subclass from Pattern, -but you can add additional patterns that don't. - -Also note that all the regular expressions used by inline must -capture the whole block. For this reason, they all start with -'^(.*)' and end with '(.*)!'. In case with built-in expression -Pattern takes care of adding the "^(.*)" and "(.*)!". - -Finally, the order in which regular expressions are applied is very -important - e.g. if we first replace http://.../ links with <a> tags -and _then_ try to replace inline html, we would end up with a mess. -So, we apply the expressions in the following order: - -* escape and backticks have to go before everything else, so - that we can preempt any markdown patterns by escaping them. - -* then we handle auto-links (must be done before inline html) - -* then we handle inline HTML. At this point we will simply - replace all inline HTML strings with a placeholder and add - the actual HTML to a hash. - -* then inline images (must be done before links) - -* then bracketed links, first regular then reference-style - -* finally we apply strong and emphasis -""" - -import markdown -import re -from urlparse import urlparse, urlunparse -import sys -if sys.version >= "3.0": - from html import entities as htmlentitydefs -else: - import htmlentitydefs - -""" -The actual regular expressions for patterns ------------------------------------------------------------------------------ -""" - -NOBRACKET = r'[^\]\[]*' -BRK = ( r'\[(' - + (NOBRACKET + r'(\[')*6 - + (NOBRACKET+ r'\])*')*6 - + NOBRACKET + r')\]' ) -NOIMG = r'(?<!\!)' - -BACKTICK_RE = r'(?<!\\)(`+)(.+?)(?<!`)\2(?!`)' # `e=f()` or ``e=f("`")`` -ESCAPE_RE = r'\\(.)' # \< -EMPHASIS_RE = r'(\*)([^\*]*)\2' # *emphasis* -STRONG_RE = r'(\*{2}|_{2})(.*?)\2' # **strong** -STRONG_EM_RE = r'(\*{3}|_{3})(.*?)\2' # ***strong*** - -if markdown.SMART_EMPHASIS: - EMPHASIS_2_RE = r'(?<!\S)(_)(\S.*?)\2' # _emphasis_ -else: - EMPHASIS_2_RE = r'(_)(.*?)\2' # _emphasis_ - -LINK_RE = NOIMG + BRK + \ -r'''\(\s*(<.*?>|((?:(?:\(.*?\))|[^\(\)]))*?)\s*((['"])(.*)\12)?\)''' -# [text](url) or [text](<url>) - -IMAGE_LINK_RE = r'\!' + BRK + r'\s*\((<.*?>|([^\)]*))\)' -# ![alttxt](http://x.com/) or ![alttxt](<http://x.com/>) -REFERENCE_RE = NOIMG + BRK+ r'\s*\[([^\]]*)\]' # [Google][3] -IMAGE_REFERENCE_RE = r'\!' + BRK + '\s*\[([^\]]*)\]' # ![alt text][2] -NOT_STRONG_RE = r'( \* )' # stand-alone * or _ -AUTOLINK_RE = r'<((?:f|ht)tps?://[^>]*)>' # <http://www.123.com> -AUTOMAIL_RE = r'<([^> \!]*@[^> ]*)>' # <me@example.com> - -HTML_RE = r'(\<([a-zA-Z/][^\>]*?|\!--.*?--)\>)' # <...> -ENTITY_RE = r'(&[\#a-zA-Z0-9]*;)' # & -LINE_BREAK_RE = r' \n' # two spaces at end of line -LINE_BREAK_2_RE = r' $' # two spaces at end of text - - -def dequote(string): - """Remove quotes from around a string.""" - if ( ( string.startswith('"') and string.endswith('"')) - or (string.startswith("'") and string.endswith("'")) ): - return string[1:-1] - else: - return string - -ATTR_RE = re.compile("\{@([^\}]*)=([^\}]*)}") # {@id=123} - -def handleAttributes(text, parent): - """Set values of an element based on attribute definitions ({@id=123}).""" - def attributeCallback(match): - parent.set(match.group(1), match.group(2).replace('\n', ' ')) - return ATTR_RE.sub(attributeCallback, text) - - -""" -The pattern classes ------------------------------------------------------------------------------ -""" - -class Pattern: - """Base class that inline patterns subclass. """ - - def __init__ (self, pattern, markdown_instance=None): - """ - Create an instant of an inline pattern. - - Keyword arguments: - - * pattern: A regular expression that matches a pattern - - """ - self.pattern = pattern - self.compiled_re = re.compile("^(.*?)%s(.*?)$" % pattern, re.DOTALL) - - # Api for Markdown to pass safe_mode into instance - self.safe_mode = False - if markdown_instance: - self.markdown = markdown_instance - - def getCompiledRegExp (self): - """ Return a compiled regular expression. """ - return self.compiled_re - - def handleMatch(self, m): - """Return a ElementTree element from the given match. - - Subclasses should override this method. - - Keyword arguments: - - * m: A re match object containing a match of the pattern. - - """ - pass - - def type(self): - """ Return class name, to define pattern type """ - return self.__class__.__name__ - -BasePattern = Pattern # for backward compatibility - -class SimpleTextPattern (Pattern): - """ Return a simple text of group(2) of a Pattern. """ - def handleMatch(self, m): - text = m.group(2) - if text == markdown.INLINE_PLACEHOLDER_PREFIX: - return None - return text - -class SimpleTagPattern (Pattern): - """ - Return element of type `tag` with a text attribute of group(3) - of a Pattern. - - """ - def __init__ (self, pattern, tag): - Pattern.__init__(self, pattern) - self.tag = tag - - def handleMatch(self, m): - el = markdown.etree.Element(self.tag) - el.text = m.group(3) - return el - - -class SubstituteTagPattern (SimpleTagPattern): - """ Return a eLement of type `tag` with no children. """ - def handleMatch (self, m): - return markdown.etree.Element(self.tag) - - -class BacktickPattern (Pattern): - """ Return a `<code>` element containing the matching text. """ - def __init__ (self, pattern): - Pattern.__init__(self, pattern) - self.tag = "code" - - def handleMatch(self, m): - el = markdown.etree.Element(self.tag) - el.text = markdown.AtomicString(m.group(3).strip()) - return el - - -class DoubleTagPattern (SimpleTagPattern): - """Return a ElementTree element nested in tag2 nested in tag1. - - Useful for strong emphasis etc. - - """ - def handleMatch(self, m): - tag1, tag2 = self.tag.split(",") - el1 = markdown.etree.Element(tag1) - el2 = markdown.etree.SubElement(el1, tag2) - el2.text = m.group(3) - return el1 - - -class HtmlPattern (Pattern): - """ Store raw inline html and return a placeholder. """ - def handleMatch (self, m): - rawhtml = m.group(2) - inline = True - place_holder = self.markdown.htmlStash.store(rawhtml) - return place_holder - - -class LinkPattern (Pattern): - """ Return a link element from the given match. """ - def handleMatch(self, m): - el = markdown.etree.Element("a") - el.text = m.group(2) - title = m.group(11) - href = m.group(9) - - if href: - if href[0] == "<": - href = href[1:-1] - el.set("href", self.sanitize_url(href.strip())) - else: - el.set("href", "") - - if title: - title = dequote(title) #.replace('"', """) - el.set("title", title) - return el - - def sanitize_url(self, url): - """ - Sanitize a url against xss attacks in "safe_mode". - - Rather than specifically blacklisting `javascript:alert("XSS")` and all - its aliases (see <http://ha.ckers.org/xss.html>), we whitelist known - safe url formats. Most urls contain a network location, however some - are known not to (i.e.: mailto links). Script urls do not contain a - location. Additionally, for `javascript:...`, the scheme would be - "javascript" but some aliases will appear to `urlparse()` to have no - scheme. On top of that relative links (i.e.: "foo/bar.html") have no - scheme. Therefore we must check "path", "parameters", "query" and - "fragment" for any literal colons. We don't check "scheme" for colons - because it *should* never have any and "netloc" must allow the form: - `username:password@host:port`. - - """ - locless_schemes = ['', 'mailto', 'news'] - scheme, netloc, path, params, query, fragment = url = urlparse(url) - safe_url = False - if netloc != '' or scheme in locless_schemes: - safe_url = True - - for part in url[2:]: - if ":" in part: - safe_url = False - - if self.markdown.safeMode and not safe_url: - return '' - else: - return urlunparse(url) - -class ImagePattern(LinkPattern): - """ Return a img element from the given match. """ - def handleMatch(self, m): - el = markdown.etree.Element("img") - src_parts = m.group(9).split() - if src_parts: - src = src_parts[0] - if src[0] == "<" and src[-1] == ">": - src = src[1:-1] - el.set('src', self.sanitize_url(src)) - else: - el.set('src', "") - if len(src_parts) > 1: - el.set('title', dequote(" ".join(src_parts[1:]))) - - if markdown.ENABLE_ATTRIBUTES: - truealt = handleAttributes(m.group(2), el) - else: - truealt = m.group(2) - - el.set('alt', truealt) - return el - -class ReferencePattern(LinkPattern): - """ Match to a stored reference and return link element. """ - def handleMatch(self, m): - if m.group(9): - id = m.group(9).lower() - else: - # if we got something like "[Google][]" - # we'll use "google" as the id - id = m.group(2).lower() - - if not id in self.markdown.references: # ignore undefined refs - return None - href, title = self.markdown.references[id] - - text = m.group(2) - return self.makeTag(href, title, text) - - def makeTag(self, href, title, text): - el = markdown.etree.Element('a') - - el.set('href', self.sanitize_url(href)) - if title: - el.set('title', title) - - el.text = text - return el - - -class ImageReferencePattern (ReferencePattern): - """ Match to a stored reference and return img element. """ - def makeTag(self, href, title, text): - el = markdown.etree.Element("img") - el.set("src", self.sanitize_url(href)) - if title: - el.set("title", title) - el.set("alt", text) - return el - - -class AutolinkPattern (Pattern): - """ Return a link Element given an autolink (`<http://example/com>`). """ - def handleMatch(self, m): - el = markdown.etree.Element("a") - el.set('href', m.group(2)) - el.text = markdown.AtomicString(m.group(2)) - return el - -class AutomailPattern (Pattern): - """ - Return a mailto link Element given an automail link (`<foo@example.com>`). - """ - def handleMatch(self, m): - el = markdown.etree.Element('a') - email = m.group(2) - if email.startswith("mailto:"): - email = email[len("mailto:"):] - - def codepoint2name(code): - """Return entity definition by code, or the code if not defined.""" - entity = htmlentitydefs.codepoint2name.get(code) - if entity: - return "%s%s;" % (markdown.AMP_SUBSTITUTE, entity) - else: - return "%s#%d;" % (markdown.AMP_SUBSTITUTE, code) - - letters = [codepoint2name(ord(letter)) for letter in email] - el.text = markdown.AtomicString(''.join(letters)) - - mailto = "mailto:" + email - mailto = "".join([markdown.AMP_SUBSTITUTE + '#%d;' % - ord(letter) for letter in mailto]) - el.set('href', mailto) - return el - diff --git a/addon-sdk/source/python-lib/markdown/odict.py b/addon-sdk/source/python-lib/markdown/odict.py deleted file mode 100644 index bf3ef0718255..000000000000 --- a/addon-sdk/source/python-lib/markdown/odict.py +++ /dev/null @@ -1,162 +0,0 @@ -class OrderedDict(dict): - """ - A dictionary that keeps its keys in the order in which they're inserted. - - Copied from Django's SortedDict with some modifications. - - """ - def __new__(cls, *args, **kwargs): - instance = super(OrderedDict, cls).__new__(cls, *args, **kwargs) - instance.keyOrder = [] - return instance - - def __init__(self, data=None): - if data is None: - data = {} - super(OrderedDict, self).__init__(data) - if isinstance(data, dict): - self.keyOrder = data.keys() - else: - self.keyOrder = [] - for key, value in data: - if key not in self.keyOrder: - self.keyOrder.append(key) - - def __deepcopy__(self, memo): - from copy import deepcopy - return self.__class__([(key, deepcopy(value, memo)) - for key, value in self.iteritems()]) - - def __setitem__(self, key, value): - super(OrderedDict, self).__setitem__(key, value) - if key not in self.keyOrder: - self.keyOrder.append(key) - - def __delitem__(self, key): - super(OrderedDict, self).__delitem__(key) - self.keyOrder.remove(key) - - def __iter__(self): - for k in self.keyOrder: - yield k - - def pop(self, k, *args): - result = super(OrderedDict, self).pop(k, *args) - try: - self.keyOrder.remove(k) - except ValueError: - # Key wasn't in the dictionary in the first place. No problem. - pass - return result - - def popitem(self): - result = super(OrderedDict, self).popitem() - self.keyOrder.remove(result[0]) - return result - - def items(self): - return zip(self.keyOrder, self.values()) - - def iteritems(self): - for key in self.keyOrder: - yield key, super(OrderedDict, self).__getitem__(key) - - def keys(self): - return self.keyOrder[:] - - def iterkeys(self): - return iter(self.keyOrder) - - def values(self): - return [super(OrderedDict, self).__getitem__(k) for k in self.keyOrder] - - def itervalues(self): - for key in self.keyOrder: - yield super(OrderedDict, self).__getitem__(key) - - def update(self, dict_): - for k, v in dict_.items(): - self.__setitem__(k, v) - - def setdefault(self, key, default): - if key not in self.keyOrder: - self.keyOrder.append(key) - return super(OrderedDict, self).setdefault(key, default) - - def value_for_index(self, index): - """Return the value of the item at the given zero-based index.""" - return self[self.keyOrder[index]] - - def insert(self, index, key, value): - """Insert the key, value pair before the item with the given index.""" - if key in self.keyOrder: - n = self.keyOrder.index(key) - del self.keyOrder[n] - if n < index: - index -= 1 - self.keyOrder.insert(index, key) - super(OrderedDict, self).__setitem__(key, value) - - def copy(self): - """Return a copy of this object.""" - # This way of initializing the copy means it works for subclasses, too. - obj = self.__class__(self) - obj.keyOrder = self.keyOrder[:] - return obj - - def __repr__(self): - """ - Replace the normal dict.__repr__ with a version that returns the keys - in their sorted order. - """ - return '{%s}' % ', '.join(['%r: %r' % (k, v) for k, v in self.items()]) - - def clear(self): - super(OrderedDict, self).clear() - self.keyOrder = [] - - def index(self, key): - """ Return the index of a given key. """ - return self.keyOrder.index(key) - - def index_for_location(self, location): - """ Return index or None for a given location. """ - if location == '_begin': - i = 0 - elif location == '_end': - i = None - elif location.startswith('<') or location.startswith('>'): - i = self.index(location[1:]) - if location.startswith('>'): - if i >= len(self): - # last item - i = None - else: - i += 1 - else: - raise ValueError('Not a valid location: "%s". Location key ' - 'must start with a ">" or "<".' % location) - return i - - def add(self, key, value, location): - """ Insert by key location. """ - i = self.index_for_location(location) - if i is not None: - self.insert(i, key, value) - else: - self.__setitem__(key, value) - - def link(self, key, location): - """ Change location of an existing item. """ - n = self.keyOrder.index(key) - del self.keyOrder[n] - i = self.index_for_location(location) - try: - if i is not None: - self.keyOrder.insert(i, key) - else: - self.keyOrder.append(key) - except Error: - # restore to prevent data loss and reraise - self.keyOrder.insert(n, key) - raise Error diff --git a/addon-sdk/source/python-lib/markdown/postprocessors.py b/addon-sdk/source/python-lib/markdown/postprocessors.py deleted file mode 100644 index 80227bb9097e..000000000000 --- a/addon-sdk/source/python-lib/markdown/postprocessors.py +++ /dev/null @@ -1,77 +0,0 @@ -""" -POST-PROCESSORS -============================================================================= - -Markdown also allows post-processors, which are similar to preprocessors in -that they need to implement a "run" method. However, they are run after core -processing. - -""" - - -import markdown - -class Processor: - def __init__(self, markdown_instance=None): - if markdown_instance: - self.markdown = markdown_instance - -class Postprocessor(Processor): - """ - Postprocessors are run after the ElementTree it converted back into text. - - Each Postprocessor implements a "run" method that takes a pointer to a - text string, modifies it as necessary and returns a text string. - - Postprocessors must extend markdown.Postprocessor. - - """ - - def run(self, text): - """ - Subclasses of Postprocessor should implement a `run` method, which - takes the html document as a single text string and returns a - (possibly modified) string. - - """ - pass - - -class RawHtmlPostprocessor(Postprocessor): - """ Restore raw html to the document. """ - - def run(self, text): - """ Iterate over html stash and restore "safe" html. """ - for i in range(self.markdown.htmlStash.html_counter): - html, safe = self.markdown.htmlStash.rawHtmlBlocks[i] - if self.markdown.safeMode and not safe: - if str(self.markdown.safeMode).lower() == 'escape': - html = self.escape(html) - elif str(self.markdown.safeMode).lower() == 'remove': - html = '' - else: - html = markdown.HTML_REMOVED_TEXT - if safe or not self.markdown.safeMode: - text = text.replace("<p>%s</p>" % - (markdown.preprocessors.HTML_PLACEHOLDER % i), - html + "\n") - text = text.replace(markdown.preprocessors.HTML_PLACEHOLDER % i, - html) - return text - - def escape(self, html): - """ Basic html escaping """ - html = html.replace('&', '&') - html = html.replace('<', '<') - html = html.replace('>', '>') - return html.replace('"', '"') - - -class AndSubstitutePostprocessor(Postprocessor): - """ Restore valid entities """ - def __init__(self): - pass - - def run(self, text): - text = text.replace(markdown.AMP_SUBSTITUTE, "&") - return text diff --git a/addon-sdk/source/python-lib/markdown/preprocessors.py b/addon-sdk/source/python-lib/markdown/preprocessors.py deleted file mode 100644 index 712a1e8755d1..000000000000 --- a/addon-sdk/source/python-lib/markdown/preprocessors.py +++ /dev/null @@ -1,214 +0,0 @@ - -""" -PRE-PROCESSORS -============================================================================= - -Preprocessors work on source text before we start doing anything too -complicated. -""" - -import re -import markdown - -HTML_PLACEHOLDER_PREFIX = markdown.STX+"wzxhzdk:" -HTML_PLACEHOLDER = HTML_PLACEHOLDER_PREFIX + "%d" + markdown.ETX - -class Processor: - def __init__(self, markdown_instance=None): - if markdown_instance: - self.markdown = markdown_instance - -class Preprocessor (Processor): - """ - Preprocessors are run after the text is broken into lines. - - Each preprocessor implements a "run" method that takes a pointer to a - list of lines of the document, modifies it as necessary and returns - either the same pointer or a pointer to a new list. - - Preprocessors must extend markdown.Preprocessor. - - """ - def run(self, lines): - """ - Each subclass of Preprocessor should override the `run` method, which - takes the document as a list of strings split by newlines and returns - the (possibly modified) list of lines. - - """ - pass - -class HtmlStash: - """ - This class is used for stashing HTML objects that we extract - in the beginning and replace with place-holders. - """ - - def __init__ (self): - """ Create a HtmlStash. """ - self.html_counter = 0 # for counting inline html segments - self.rawHtmlBlocks=[] - - def store(self, html, safe=False): - """ - Saves an HTML segment for later reinsertion. Returns a - placeholder string that needs to be inserted into the - document. - - Keyword arguments: - - * html: an html segment - * safe: label an html segment as safe for safemode - - Returns : a placeholder string - - """ - self.rawHtmlBlocks.append((html, safe)) - placeholder = HTML_PLACEHOLDER % self.html_counter - self.html_counter += 1 - return placeholder - - def reset(self): - self.html_counter = 0 - self.rawHtmlBlocks = [] - - -class HtmlBlockPreprocessor(Preprocessor): - """Remove html blocks from the text and store them for later retrieval.""" - - right_tag_patterns = ["</%s>", "%s>"] - - def _get_left_tag(self, block): - return block[1:].replace(">", " ", 1).split()[0].lower() - - def _get_right_tag(self, left_tag, block): - for p in self.right_tag_patterns: - tag = p % left_tag - i = block.rfind(tag) - if i > 2: - return tag.lstrip("<").rstrip(">"), i + len(p)-2 + len(left_tag) - return block.rstrip()[-len(left_tag)-2:-1].lower(), len(block) - - def _equal_tags(self, left_tag, right_tag): - if left_tag == 'div' or left_tag[0] in ['?', '@', '%']: # handle PHP, etc. - return True - if ("/" + left_tag) == right_tag: - return True - if (right_tag == "--" and left_tag == "--"): - return True - elif left_tag == right_tag[1:] \ - and right_tag[0] != "<": - return True - else: - return False - - def _is_oneliner(self, tag): - return (tag in ['hr', 'hr/']) - - def run(self, lines): - text = "\n".join(lines) - new_blocks = [] - text = text.split("\n\n") - items = [] - left_tag = '' - right_tag = '' - in_tag = False # flag - - while text: - block = text[0] - if block.startswith("\n"): - block = block[1:] - text = text[1:] - - if block.startswith("\n"): - block = block[1:] - - if not in_tag: - if block.startswith("<"): - left_tag = self._get_left_tag(block) - right_tag, data_index = self._get_right_tag(left_tag, block) - - if data_index < len(block): - text.insert(0, block[data_index:]) - block = block[:data_index] - - if not (markdown.isBlockLevel(left_tag) \ - or block[1] in ["!", "?", "@", "%"]): - new_blocks.append(block) - continue - - if self._is_oneliner(left_tag): - new_blocks.append(block.strip()) - continue - - if block[1] == "!": - # is a comment block - left_tag = "--" - right_tag, data_index = self._get_right_tag(left_tag, block) - # keep checking conditions below and maybe just append - - if block.rstrip().endswith(">") \ - and self._equal_tags(left_tag, right_tag): - new_blocks.append( - self.markdown.htmlStash.store(block.strip())) - continue - else: #if not block[1] == "!": - # if is block level tag and is not complete - - if markdown.isBlockLevel(left_tag) or left_tag == "--" \ - and not block.rstrip().endswith(">"): - items.append(block.strip()) - in_tag = True - else: - new_blocks.append( - self.markdown.htmlStash.store(block.strip())) - - continue - - new_blocks.append(block) - - else: - items.append(block.strip()) - - right_tag, data_index = self._get_right_tag(left_tag, block) - - if self._equal_tags(left_tag, right_tag): - # if find closing tag - in_tag = False - new_blocks.append( - self.markdown.htmlStash.store('\n\n'.join(items))) - items = [] - - if items: - new_blocks.append(self.markdown.htmlStash.store('\n\n'.join(items))) - new_blocks.append('\n') - - new_text = "\n\n".join(new_blocks) - return new_text.split("\n") - - -class ReferencePreprocessor(Preprocessor): - """ Remove reference definitions from text and store for later use. """ - - RE = re.compile(r'^(\ ?\ ?\ ?)\[([^\]]*)\]:\s*([^ ]*)(.*)$', re.DOTALL) - - def run (self, lines): - new_text = []; - for line in lines: - m = self.RE.match(line) - if m: - id = m.group(2).strip().lower() - t = m.group(4).strip() # potential title - if not t: - self.markdown.references[id] = (m.group(3), t) - elif (len(t) >= 2 - and (t[0] == t[-1] == "\"" - or t[0] == t[-1] == "\'" - or (t[0] == "(" and t[-1] == ")") ) ): - self.markdown.references[id] = (m.group(3), t[1:-1]) - else: - new_text.append(line) - else: - new_text.append(line) - - return new_text #+ "\n" diff --git a/addon-sdk/source/python-lib/markdown/treeprocessors.py b/addon-sdk/source/python-lib/markdown/treeprocessors.py deleted file mode 100644 index 1dc612a95e85..000000000000 --- a/addon-sdk/source/python-lib/markdown/treeprocessors.py +++ /dev/null @@ -1,329 +0,0 @@ -import markdown -import re - -def isString(s): - """ Check if it's string """ - return isinstance(s, unicode) or isinstance(s, str) - -class Processor: - def __init__(self, markdown_instance=None): - if markdown_instance: - self.markdown = markdown_instance - -class Treeprocessor(Processor): - """ - Treeprocessors are run on the ElementTree object before serialization. - - Each Treeprocessor implements a "run" method that takes a pointer to an - ElementTree, modifies it as necessary and returns an ElementTree - object. - - Treeprocessors must extend markdown.Treeprocessor. - - """ - def run(self, root): - """ - Subclasses of Treeprocessor should implement a `run` method, which - takes a root ElementTree. This method can return another ElementTree - object, and the existing root ElementTree will be replaced, or it can - modify the current tree and return None. - """ - pass - - -class InlineProcessor(Treeprocessor): - """ - A Treeprocessor that traverses a tree, applying inline patterns. - """ - - def __init__ (self, md): - self.__placeholder_prefix = markdown.INLINE_PLACEHOLDER_PREFIX - self.__placeholder_suffix = markdown.ETX - self.__placeholder_length = 4 + len(self.__placeholder_prefix) \ - + len(self.__placeholder_suffix) - self.__placeholder_re = re.compile(markdown.INLINE_PLACEHOLDER % r'([0-9]{4})') - self.markdown = md - - def __makePlaceholder(self, type): - """ Generate a placeholder """ - id = "%04d" % len(self.stashed_nodes) - hash = markdown.INLINE_PLACEHOLDER % id - return hash, id - - def __findPlaceholder(self, data, index): - """ - Extract id from data string, start from index - - Keyword arguments: - - * data: string - * index: index, from which we start search - - Returns: placeholder id and string index, after the found placeholder. - """ - - m = self.__placeholder_re.search(data, index) - if m: - return m.group(1), m.end() - else: - return None, index + 1 - - def __stashNode(self, node, type): - """ Add node to stash """ - placeholder, id = self.__makePlaceholder(type) - self.stashed_nodes[id] = node - return placeholder - - def __handleInline(self, data, patternIndex=0): - """ - Process string with inline patterns and replace it - with placeholders - - Keyword arguments: - - * data: A line of Markdown text - * patternIndex: The index of the inlinePattern to start with - - Returns: String with placeholders. - - """ - if not isinstance(data, markdown.AtomicString): - startIndex = 0 - while patternIndex < len(self.markdown.inlinePatterns): - data, matched, startIndex = self.__applyPattern( - self.markdown.inlinePatterns.value_for_index(patternIndex), - data, patternIndex, startIndex) - if not matched: - patternIndex += 1 - return data - - def __processElementText(self, node, subnode, isText=True): - """ - Process placeholders in Element.text or Element.tail - of Elements popped from self.stashed_nodes. - - Keywords arguments: - - * node: parent node - * subnode: processing node - * isText: bool variable, True - it's text, False - it's tail - - Returns: None - - """ - if isText: - text = subnode.text - subnode.text = None - else: - text = subnode.tail - subnode.tail = None - - childResult = self.__processPlaceholders(text, subnode) - - if not isText and node is not subnode: - pos = node.getchildren().index(subnode) - node.remove(subnode) - else: - pos = 0 - - childResult.reverse() - for newChild in childResult: - node.insert(pos, newChild) - - def __processPlaceholders(self, data, parent): - """ - Process string with placeholders and generate ElementTree tree. - - Keyword arguments: - - * data: string with placeholders instead of ElementTree elements. - * parent: Element, which contains processing inline data - - Returns: list with ElementTree elements with applied inline patterns. - """ - def linkText(text): - if text: - if result: - if result[-1].tail: - result[-1].tail += text - else: - result[-1].tail = text - else: - if parent.text: - parent.text += text - else: - parent.text = text - - result = [] - strartIndex = 0 - while data: - index = data.find(self.__placeholder_prefix, strartIndex) - if index != -1: - id, phEndIndex = self.__findPlaceholder(data, index) - - if id in self.stashed_nodes: - node = self.stashed_nodes.get(id) - - if index > 0: - text = data[strartIndex:index] - linkText(text) - - if not isString(node): # it's Element - for child in [node] + node.getchildren(): - if child.tail: - if child.tail.strip(): - self.__processElementText(node, child, False) - if child.text: - if child.text.strip(): - self.__processElementText(child, child) - else: # it's just a string - linkText(node) - strartIndex = phEndIndex - continue - - strartIndex = phEndIndex - result.append(node) - - else: # wrong placeholder - end = index + len(prefix) - linkText(data[strartIndex:end]) - strartIndex = end - else: - text = data[strartIndex:] - linkText(text) - data = "" - - return result - - def __applyPattern(self, pattern, data, patternIndex, startIndex=0): - """ - Check if the line fits the pattern, create the necessary - elements, add it to stashed_nodes. - - Keyword arguments: - - * data: the text to be processed - * pattern: the pattern to be checked - * patternIndex: index of current pattern - * startIndex: string index, from which we starting search - - Returns: String with placeholders instead of ElementTree elements. - - """ - match = pattern.getCompiledRegExp().match(data[startIndex:]) - leftData = data[:startIndex] - - if not match: - return data, False, 0 - - node = pattern.handleMatch(match) - - if node is None: - return data, True, len(leftData) + match.span(len(match.groups()))[0] - - if not isString(node): - if not isinstance(node.text, markdown.AtomicString): - # We need to process current node too - for child in [node] + node.getchildren(): - if not isString(node): - if child.text: - child.text = self.__handleInline(child.text, - patternIndex + 1) - if child.tail: - child.tail = self.__handleInline(child.tail, - patternIndex) - - placeholder = self.__stashNode(node, pattern.type()) - - return "%s%s%s%s" % (leftData, - match.group(1), - placeholder, match.groups()[-1]), True, 0 - - def run(self, tree): - """Apply inline patterns to a parsed Markdown tree. - - Iterate over ElementTree, find elements with inline tag, apply inline - patterns and append newly created Elements to tree. If you don't - want process your data with inline paterns, instead of normal string, - use subclass AtomicString: - - node.text = markdown.AtomicString("data won't be processed with inline patterns") - - Arguments: - - * markdownTree: ElementTree object, representing Markdown tree. - - Returns: ElementTree object with applied inline patterns. - - """ - self.stashed_nodes = {} - - stack = [tree] - - while stack: - currElement = stack.pop() - insertQueue = [] - for child in currElement.getchildren(): - if child.text and not isinstance(child.text, markdown.AtomicString): - text = child.text - child.text = None - lst = self.__processPlaceholders(self.__handleInline( - text), child) - stack += lst - insertQueue.append((child, lst)) - - if child.getchildren(): - stack.append(child) - - for element, lst in insertQueue: - if element.text: - element.text = \ - markdown.inlinepatterns.handleAttributes(element.text, - element) - i = 0 - for newChild in lst: - # Processing attributes - if newChild.tail: - newChild.tail = \ - markdown.inlinepatterns.handleAttributes(newChild.tail, - element) - if newChild.text: - newChild.text = \ - markdown.inlinepatterns.handleAttributes(newChild.text, - newChild) - element.insert(i, newChild) - i += 1 - return tree - - -class PrettifyTreeprocessor(Treeprocessor): - """ Add linebreaks to the html document. """ - - def _prettifyETree(self, elem): - """ Recursively add linebreaks to ElementTree children. """ - - i = "\n" - if markdown.isBlockLevel(elem.tag) and elem.tag not in ['code', 'pre']: - if (not elem.text or not elem.text.strip()) \ - and len(elem) and markdown.isBlockLevel(elem[0].tag): - elem.text = i - for e in elem: - if markdown.isBlockLevel(e.tag): - self._prettifyETree(e) - if not elem.tail or not elem.tail.strip(): - elem.tail = i - if not elem.tail or not elem.tail.strip(): - elem.tail = i - - def run(self, root): - """ Add linebreaks to ElementTree root object. """ - - self._prettifyETree(root) - # Do <br />'s seperately as they are often in the middle of - # inline content and missed by _prettifyETree. - brs = root.getiterator('br') - for br in brs: - if not br.tail or not br.tail.strip(): - br.tail = '\n' - else: - br.tail = '\n%s' % br.tail diff --git a/addon-sdk/source/test/addons/curly-id/lib/main.js b/addon-sdk/source/test/addons/curly-id/lib/main.js new file mode 100644 index 000000000000..e186f9c04cab --- /dev/null +++ b/addon-sdk/source/test/addons/curly-id/lib/main.js @@ -0,0 +1,43 @@ +/* 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/. */ +'use strict'; + +const simple = require('sdk/simple-prefs'); +const service = require('sdk/preferences/service'); +const { id, preferencesBranch } = require('sdk/self'); +const { AddonManager } = require('chrome').Cu.import('resource://gre/modules/AddonManager.jsm'); + +exports.testCurlyID = function(assert) { + assert.equal(id, '{34a1eae1-c20a-464f-9b0e-000000000000}', 'curly ID is curly'); + + assert.equal(simple.prefs.test13, 26, 'test13 is 26'); + + simple.prefs.test14 = '15'; + assert.equal(service.get('extensions.{34a1eae1-c20a-464f-9b0e-000000000000}.test14'), '15', 'test14 is 15'); + + assert.equal(service.get('extensions.{34a1eae1-c20a-464f-9b0e-000000000000}.test14'), simple.prefs.test14, 'simple test14 also 15'); + +} + +exports.testInvalidPreferencesBranch = function(assert) { + assert.notEqual(preferencesBranch, 'invalid^branch*name', 'invalid preferences-branch value ignored'); + + assert.equal(preferencesBranch, '{34a1eae1-c20a-464f-9b0e-000000000000}', 'preferences-branch is {34a1eae1-c20a-464f-9b0e-000000000000}'); + +} + +// from `/test/test-self.js`, adapted to `sdk/test/assert` API +exports.testSelfID = function(assert, done) { + + assert.equal(typeof(id), 'string', 'self.id is a string'); + assert.ok(id.length > 0, 'self.id not empty'); + + AddonManager.getAddonByID(id, function(addon) { + assert.ok(addon, 'found addon with self.id'); + done(); + }); + +} + +require('sdk/test/runner').runTestsFromModule(module); diff --git a/addon-sdk/source/test/addons/curly-id/package.json b/addon-sdk/source/test/addons/curly-id/package.json new file mode 100644 index 000000000000..1d47b4d812a3 --- /dev/null +++ b/addon-sdk/source/test/addons/curly-id/package.json @@ -0,0 +1,14 @@ +{ + "id": "{34a1eae1-c20a-464f-9b0e-000000000000}", + "fullName": "curly ID test", + "author": "Tomislav Jovanovic", + + "preferences": [{ + "name": "test13", + "type": "integer", + "title": "test13", + "value": 26 + }], + + "preferences-branch": "invalid^branch*name" +} diff --git a/addon-sdk/source/test/addons/predefined-id-with-at/lib/main.js b/addon-sdk/source/test/addons/predefined-id-with-at/lib/main.js new file mode 100644 index 000000000000..a006383fc545 --- /dev/null +++ b/addon-sdk/source/test/addons/predefined-id-with-at/lib/main.js @@ -0,0 +1,34 @@ +/* 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/. */ +'use strict'; + +const { id, preferencesBranch } = require('sdk/self'); +const simple = require('sdk/simple-prefs'); +const service = require('sdk/preferences/service'); +const { AddonManager } = require('chrome').Cu.import('resource://gre/modules/AddonManager.jsm', {}); + +const expected_id = 'predefined-id@test'; + +exports.testExpectedID = function(assert) { + assert.equal(id, expected_id, 'ID is as expected'); + assert.equal(preferencesBranch, expected_id, 'preferences-branch is ' + expected_id); + + assert.equal(simple.prefs.test, 5, 'test pref is 5'); + + simple.prefs.test2 = '25'; + assert.equal(service.get('extensions.'+expected_id+'.test2'), '25', 'test pref is 25'); + assert.equal(service.get('extensions.'+expected_id+'.test2'), simple.prefs.test2, 'test pref is 25'); +} + +exports.testSelfID = function(assert, done) { + assert.equal(typeof(id), 'string', 'self.id is a string'); + assert.ok(id.length > 0, 'self.id not empty'); + + AddonManager.getAddonByID(id, function(addon) { + assert.equal(addon.id, id, 'found addon with self.id'); + done(); + }); +} + +require('sdk/test/runner').runTestsFromModule(module); diff --git a/addon-sdk/source/test/addons/predefined-id-with-at/package.json b/addon-sdk/source/test/addons/predefined-id-with-at/package.json new file mode 100644 index 000000000000..5e56b1984df1 --- /dev/null +++ b/addon-sdk/source/test/addons/predefined-id-with-at/package.json @@ -0,0 +1,11 @@ +{ + "id": "predefined-id@test", + "fullName": "predefined ID test", + "author": "Erik Vold", + "preferences": [{ + "name": "test", + "type": "integer", + "title": "test", + "value": 5 + }] +} diff --git a/addon-sdk/source/test/addons/preferences-branch/lib/main.js b/addon-sdk/source/test/addons/preferences-branch/lib/main.js new file mode 100644 index 000000000000..1f0433ffabe1 --- /dev/null +++ b/addon-sdk/source/test/addons/preferences-branch/lib/main.js @@ -0,0 +1,35 @@ +/* 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/. */ + +'use strict'; + +const { id, preferencesBranch } = require('sdk/self'); +const simple = require('sdk/simple-prefs'); +const service = require('sdk/preferences/service'); +const { AddonManager } = require('chrome').Cu.import('resource://gre/modules/AddonManager.jsm'); + +exports.testPreferencesBranch = function(assert) { + assert.equal(preferencesBranch, 'human-readable', 'preferencesBranch is human-readable'); + + assert.equal(simple.prefs.test42, true, 'test42 is true'); + + simple.prefs.test43 = 'movie'; + assert.equal(service.get('extensions.human-readable.test43'), 'movie', 'test43 is a movie'); + +} + +// from `/test/test-self.js`, adapted to `sdk/test/assert` API +exports.testSelfID = function(assert, done) { + + assert.equal(typeof(id), 'string', 'self.id is a string'); + assert.ok(id.length > 0, 'self.id not empty'); + + AddonManager.getAddonByID(id, function(addon) { + assert.ok(addon, 'found addon with self.id'); + done(); + }); + +} + +require('sdk/test/runner').runTestsFromModule(module); diff --git a/addon-sdk/source/test/addons/preferences-branch/package.json b/addon-sdk/source/test/addons/preferences-branch/package.json new file mode 100644 index 000000000000..b738db7d0669 --- /dev/null +++ b/addon-sdk/source/test/addons/preferences-branch/package.json @@ -0,0 +1,14 @@ +{ + "id": "test-preferences-branch", + "fullName": "preferences-branch test", + "author": "Tomislav Jovanovic", + + "preferences": [{ + "name": "test42", + "type": "bool", + "title": "test42", + "value": true + }], + + "preferences-branch": "human-readable" +} diff --git a/addon-sdk/source/test/addons/simple-prefs-regression/app-extension/application.ini b/addon-sdk/source/test/addons/simple-prefs-regression/app-extension/application.ini new file mode 100644 index 000000000000..6cec69a16c97 --- /dev/null +++ b/addon-sdk/source/test/addons/simple-prefs-regression/app-extension/application.ini @@ -0,0 +1,11 @@ +[App] +Vendor=Varma +Name=Test App +Version=1.0 +BuildID=20060101 +Copyright=Copyright (c) 2009 Atul Varma +ID=xulapp@toolness.com + +[Gecko] +MinVersion=1.9.2.0 +MaxVersion=2.0.* diff --git a/addon-sdk/source/test/addons/simple-prefs-regression/app-extension/bootstrap.js b/addon-sdk/source/test/addons/simple-prefs-regression/app-extension/bootstrap.js new file mode 100644 index 000000000000..6bb014a13970 --- /dev/null +++ b/addon-sdk/source/test/addons/simple-prefs-regression/app-extension/bootstrap.js @@ -0,0 +1,337 @@ +/* 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/. */ + +// @see http://mxr.mozilla.org/mozilla-central/source/js/src/xpconnect/loader/mozJSComponentLoader.cpp + +'use strict'; + +// IMPORTANT: Avoid adding any initialization tasks here, if you need to do +// something before add-on is loaded consider addon/runner module instead! + +const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu, + results: Cr, manager: Cm } = Components; +const ioService = Cc['@mozilla.org/network/io-service;1']. + getService(Ci.nsIIOService); +const resourceHandler = ioService.getProtocolHandler('resource'). + QueryInterface(Ci.nsIResProtocolHandler); +const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')(); +const scriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1']. + getService(Ci.mozIJSSubScriptLoader); +const prefService = Cc['@mozilla.org/preferences-service;1']. + getService(Ci.nsIPrefService). + QueryInterface(Ci.nsIPrefBranch); +const appInfo = Cc["@mozilla.org/xre/app-info;1"]. + getService(Ci.nsIXULAppInfo); +const vc = Cc["@mozilla.org/xpcom/version-comparator;1"]. + getService(Ci.nsIVersionComparator); + + +const REASON = [ 'unknown', 'startup', 'shutdown', 'enable', 'disable', + 'install', 'uninstall', 'upgrade', 'downgrade' ]; + +const bind = Function.call.bind(Function.bind); + +let loader = null; +let unload = null; +let cuddlefishSandbox = null; +let nukeTimer = null; + +// Utility function that synchronously reads local resource from the given +// `uri` and returns content string. +function readURI(uri) { + let ioservice = Cc['@mozilla.org/network/io-service;1']. + getService(Ci.nsIIOService); + let channel = ioservice.newChannel(uri, 'UTF-8', null); + let stream = channel.open(); + + let cstream = Cc['@mozilla.org/intl/converter-input-stream;1']. + createInstance(Ci.nsIConverterInputStream); + cstream.init(stream, 'UTF-8', 0, 0); + + let str = {}; + let data = ''; + let read = 0; + do { + read = cstream.readString(0xffffffff, str); + data += str.value; + } while (read != 0); + + cstream.close(); + + return data; +} + +// We don't do anything on install & uninstall yet, but in a future +// we should allow add-ons to cleanup after uninstall. +function install(data, reason) {} +function uninstall(data, reason) {} + +function startup(data, reasonCode) { + try { + let reason = REASON[reasonCode]; + // URI for the root of the XPI file. + // 'jar:' URI if the addon is packed, 'file:' URI otherwise. + // (Used by l10n module in order to fetch `locale` folder) + let rootURI = data.resourceURI.spec; + + // TODO: Maybe we should perform read harness-options.json asynchronously, + // since we can't do anything until 'sessionstore-windows-restored' anyway. + let options = JSON.parse(readURI(rootURI + './harness-options.json')); + + let id = options.jetpackID; + let name = options.name; + + // Clean the metadata + options.metadata[name]['permissions'] = options.metadata[name]['permissions'] || {}; + + // freeze the permissionss + Object.freeze(options.metadata[name]['permissions']); + // freeze the metadata + Object.freeze(options.metadata[name]); + + // Register a new resource 'domain' for this addon which is mapping to + // XPI's `resources` folder. + // Generate the domain name by using jetpack ID, which is the extension ID + // by stripping common characters that doesn't work as a domain name: + let uuidRe = + /^\{([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\}$/; + + let domain = id. + toLowerCase(). + replace(/@/g, '-at-'). + replace(/\./g, '-dot-'). + replace(uuidRe, '$1'); + + let prefixURI = 'resource://' + domain + '/'; + let resourcesURI = ioService.newURI(rootURI + '/resources/', null, null); + resourceHandler.setSubstitution(domain, resourcesURI); + + // Create path to URLs mapping supported by loader. + let paths = { + // Relative modules resolve to add-on package lib + './': prefixURI + name + '/lib/', + './tests/': prefixURI + name + '/tests/', + '': 'resource://gre/modules/commonjs/' + }; + + // Maps addon lib and tests ressource folders for each package + paths = Object.keys(options.metadata).reduce(function(result, name) { + result[name + '/'] = prefixURI + name + '/lib/' + result[name + '/tests/'] = prefixURI + name + '/tests/' + return result; + }, paths); + + // We need to map tests folder when we run sdk tests whose package name + // is stripped + if (name == 'addon-sdk') + paths['tests/'] = prefixURI + name + '/tests/'; + + let useBundledSDK = options['force-use-bundled-sdk']; + if (!useBundledSDK) { + try { + useBundledSDK = prefService.getBoolPref("extensions.addon-sdk.useBundledSDK"); + } + catch (e) { + // Pref doesn't exist, allow using Firefox shipped SDK + } + } + + // Starting with Firefox 21.0a1, we start using modules shipped into firefox + // Still allow using modules from the xpi if the manifest tell us to do so. + // And only try to look for sdk modules in xpi if the xpi actually ship them + if (options['is-sdk-bundled'] && + (vc.compare(appInfo.version, '21.0a1') < 0 || useBundledSDK)) { + // Maps sdk module folders to their resource folder + paths[''] = prefixURI + 'addon-sdk/lib/'; + // test.js is usually found in root commonjs or SDK_ROOT/lib/ folder, + // so that it isn't shipped in the xpi. Keep a copy of it in sdk/ folder + // until we no longer support SDK modules in XPI: + paths['test'] = prefixURI + 'addon-sdk/lib/sdk/test.js'; + } + + // Retrieve list of module folder overloads based on preferences in order to + // eventually used a local modules instead of files shipped into Firefox. + let branch = prefService.getBranch('extensions.modules.' + id + '.path'); + paths = branch.getChildList('', {}).reduce(function (result, name) { + // Allows overloading of any sub folder by replacing . by / in pref name + let path = name.substr(1).split('.').join('/'); + // Only accept overloading folder by ensuring always ending with `/` + if (path) path += '/'; + let fileURI = branch.getCharPref(name); + + // On mobile, file URI has to end with a `/` otherwise, setSubstitution + // takes the parent folder instead. + if (fileURI[fileURI.length-1] !== '/') + fileURI += '/'; + + // Maps the given file:// URI to a resource:// in order to avoid various + // failure that happens with file:// URI and be close to production env + let resourcesURI = ioService.newURI(fileURI, null, null); + let resName = 'extensions.modules.' + domain + '.commonjs.path' + name; + resourceHandler.setSubstitution(resName, resourcesURI); + + result[path] = 'resource://' + resName + '/'; + return result; + }, paths); + + // Make version 2 of the manifest + let manifest = options.manifest; + + // Import `cuddlefish.js` module using a Sandbox and bootstrap loader. + let cuddlefishPath = 'loader/cuddlefish.js'; + let cuddlefishURI = 'resource://gre/modules/commonjs/sdk/' + cuddlefishPath; + if (paths['sdk/']) { // sdk folder has been overloaded + // (from pref, or cuddlefish is still in the xpi) + cuddlefishURI = paths['sdk/'] + cuddlefishPath; + } + else if (paths['']) { // root modules folder has been overloaded + cuddlefishURI = paths[''] + 'sdk/' + cuddlefishPath; + } + + cuddlefishSandbox = loadSandbox(cuddlefishURI); + let cuddlefish = cuddlefishSandbox.exports; + + // Normalize `options.mainPath` so that it looks like one that will come + // in a new version of linker. + let main = options.mainPath; + + unload = cuddlefish.unload; + loader = cuddlefish.Loader({ + paths: paths, + // modules manifest. + manifest: manifest, + + // Add-on ID used by different APIs as a unique identifier. + id: id, + // Add-on name. + name: name, + // Add-on version. + version: options.metadata[name].version, + // Add-on package descriptor. + metadata: options.metadata[name], + // Add-on load reason. + loadReason: reason, + + prefixURI: prefixURI, + // Add-on URI. + rootURI: rootURI, + // options used by system module. + // File to write 'OK' or 'FAIL' (exit code emulation). + resultFile: options.resultFile, + // Arguments passed as --static-args + staticArgs: options.staticArgs, + + // Arguments related to test runner. + modules: { + '@test/options': { + allTestModules: options.allTestModules, + iterations: options.iterations, + filter: options.filter, + profileMemory: options.profileMemory, + stopOnError: options.stopOnError, + verbose: options.verbose, + parseable: options.parseable, + checkMemory: options.check_memory, + } + } + }); + + let module = cuddlefish.Module('sdk/loader/cuddlefish', cuddlefishURI); + let require = cuddlefish.Require(loader, module); + + require('sdk/addon/runner').startup(reason, { + loader: loader, + main: main, + prefsURI: rootURI + 'defaults/preferences/prefs.js' + }); + } catch (error) { + dump('Bootstrap error: ' + + (error.message ? error.message : String(error)) + '\n' + + (error.stack || error.fileName + ': ' + error.lineNumber) + '\n'); + throw error; + } +}; + +function loadSandbox(uri) { + let proto = { + sandboxPrototype: { + loadSandbox: loadSandbox, + ChromeWorker: ChromeWorker + } + }; + let sandbox = Cu.Sandbox(systemPrincipal, proto); + // Create a fake commonjs environnement just to enable loading loader.js + // correctly + sandbox.exports = {}; + sandbox.module = { uri: uri, exports: sandbox.exports }; + sandbox.require = function (id) { + if (id !== "chrome") + throw new Error("Bootstrap sandbox `require` method isn't implemented."); + + return Object.freeze({ Cc: Cc, Ci: Ci, Cu: Cu, Cr: Cr, Cm: Cm, + CC: bind(CC, Components), components: Components, + ChromeWorker: ChromeWorker }); + }; + scriptLoader.loadSubScript(uri, sandbox, 'UTF-8'); + return sandbox; +} + +function unloadSandbox(sandbox) { + if ("nukeSandbox" in Cu) + Cu.nukeSandbox(sandbox); +} + +function setTimeout(callback, delay) { + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + timer.initWithCallback({ notify: callback }, delay, + Ci.nsITimer.TYPE_ONE_SHOT); + return timer; +} + +function shutdown(data, reasonCode) { + let reason = REASON[reasonCode]; + if (loader) { + unload(loader, reason); + unload = null; + + // Don't waste time cleaning up if the application is shutting down + if (reason != "shutdown") { + // Avoid leaking all modules when something goes wrong with one particular + // module. Do not clean it up immediatly in order to allow executing some + // actions on addon disabling. + // We need to keep a reference to the timer, otherwise it is collected + // and won't ever fire. + nukeTimer = setTimeout(nukeModules, 1000); + } + } +}; + +function nukeModules() { + nukeTimer = null; + // module objects store `exports` which comes from sandboxes + // We should avoid keeping link to these object to avoid leaking sandboxes + for (let key in loader.modules) { + delete loader.modules[key]; + } + // Direct links to sandboxes should be removed too + for (let key in loader.sandboxes) { + let sandbox = loader.sandboxes[key]; + delete loader.sandboxes[key]; + // Bug 775067: From FF17 we can kill all CCW from a given sandbox + unloadSandbox(sandbox); + } + loader = null; + + // both `toolkit/loader` and `system/xul-app` are loaded as JSM's via + // `cuddlefish.js`, and needs to be unloaded to avoid memory leaks, when + // the addon is unload. + + unloadSandbox(cuddlefishSandbox.loaderSandbox); + unloadSandbox(cuddlefishSandbox.xulappSandbox); + + // Bug 764840: We need to unload cuddlefish otherwise it will stay alive + // and keep a reference to this compartment. + unloadSandbox(cuddlefishSandbox); + cuddlefishSandbox = null; +} diff --git a/addon-sdk/source/test/addons/simple-prefs-regression/app-extension/defaults/preferences/prefs.js b/addon-sdk/source/test/addons/simple-prefs-regression/app-extension/defaults/preferences/prefs.js new file mode 100644 index 000000000000..ad2cf3b79658 --- /dev/null +++ b/addon-sdk/source/test/addons/simple-prefs-regression/app-extension/defaults/preferences/prefs.js @@ -0,0 +1,3 @@ +pref("extensions.test-simple-prefs@jetpack.somePreference", "TEST"); +pref("extensions.test-simple-prefs@jetpack.myInteger", 8); +pref("extensions.test-simple-prefs@jetpack.myHiddenInt", 5); diff --git a/addon-sdk/source/test/addons/simple-prefs-regression/app-extension/install.rdf b/addon-sdk/source/test/addons/simple-prefs-regression/app-extension/install.rdf new file mode 100644 index 000000000000..8fc710557b21 --- /dev/null +++ b/addon-sdk/source/test/addons/simple-prefs-regression/app-extension/install.rdf @@ -0,0 +1,33 @@ +<?xml version="1.0"?> +<!-- 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/. --> + + +<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:em="http://www.mozilla.org/2004/em-rdf#"> + <Description about="urn:mozilla:install-manifest"> + <em:id>xulapp@toolness.com</em:id> + <em:version>1.0</em:version> + <em:type>2</em:type> + <em:bootstrap>true</em:bootstrap> + <em:unpack>false</em:unpack> + + <!-- Firefox --> + <em:targetApplication> + <Description> + <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> + <em:minVersion>21.0</em:minVersion> + <em:maxVersion>25.0a1</em:maxVersion> + </Description> + </em:targetApplication> + + <!-- Front End MetaData --> + <em:name>Test App</em:name> + <em:description>Harness for tests.</em:description> + <em:creator>Mozilla Corporation</em:creator> + <em:homepageURL></em:homepageURL> + <em:optionsType></em:optionsType> + <em:updateURL></em:updateURL> + </Description> +</RDF> diff --git a/addon-sdk/source/test/addons/simple-prefs-regression/app-extension/options.xul b/addon-sdk/source/test/addons/simple-prefs-regression/app-extension/options.xul new file mode 100644 index 000000000000..89b95e8d7ad5 --- /dev/null +++ b/addon-sdk/source/test/addons/simple-prefs-regression/app-extension/options.xul @@ -0,0 +1,5 @@ +<?xml version="1.0" ?> +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <setting data-jetpack-id="test-simple-prefs@jetpack" pref="extensions.test-simple-prefs@jetpack.somePreference" pref-name="somePreference" title="some-title" type="string">Some short description for the preference</setting> + <setting data-jetpack-id="test-simple-prefs@jetpack" pref="extensions.test-simple-prefs@jetpack.myInteger" pref-name="myInteger" title="my-int" type="integer">How many of them we have.</setting> +</vbox> diff --git a/addon-sdk/source/test/addons/simple-prefs-regression/lib/main.js b/addon-sdk/source/test/addons/simple-prefs-regression/lib/main.js new file mode 100644 index 000000000000..d36a441fbd6f --- /dev/null +++ b/addon-sdk/source/test/addons/simple-prefs-regression/lib/main.js @@ -0,0 +1,97 @@ +/* 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/. */ +'use strict'; + +const { Cu } = require('chrome'); +const sp = require('sdk/simple-prefs'); +const app = require('sdk/system/xul-app'); +const self = require('sdk/self'); +const tabs = require('sdk/tabs'); +const { preferencesBranch } = require('sdk/self'); + +const { AddonManager } = Cu.import('resource://gre/modules/AddonManager.jsm', {}); + +exports.testRegression = function(assert) { + assert.equal(self.preferencesBranch, self.id, 'preferencesBranch returns id here'); +} + +exports.testDefaultValues = function (assert) { + assert.equal(sp.prefs.myHiddenInt, 5, 'myHiddenInt default is 5'); + assert.equal(sp.prefs.myInteger, 8, 'myInteger default is 8'); + assert.equal(sp.prefs.somePreference, 'TEST', 'somePreference default is correct'); +} + +exports.testOptionsType = function(assert, done) { + AddonManager.getAddonByID(self.id, function(aAddon) { + assert.equal(aAddon.optionsType, AddonManager.OPTIONS_TYPE_INLINE, 'options type is inline'); + done(); + }); +} + +if (app.is('Firefox')) { + exports.testAOM = function(assert, done) { + tabs.open({ + url: 'about:addons', + onReady: function(tab) { + tab.attach({ + contentScriptWhen: 'end', + contentScript: 'function onLoad() {\n' + + 'unsafeWindow.removeEventListener("load", onLoad, false);\n' + + 'AddonManager.getAddonByID("' + self.id + '", function(aAddon) {\n' + + 'unsafeWindow.gViewController.viewObjects.detail.node.addEventListener("ViewChanged", function whenViewChanges() {\n' + + 'unsafeWindow.gViewController.viewObjects.detail.node.removeEventListener("ViewChanged", whenViewChanges, false);\n' + + 'setTimeout(function() {\n' + // TODO: figure out why this is necessary.. + 'self.postMessage({\n' + + 'somePreference: getAttributes(unsafeWindow.document.querySelector("setting[title=\'some-title\']")),\n' + + 'myInteger: getAttributes(unsafeWindow.document.querySelector("setting[title=\'my-int\']")),\n' + + 'myHiddenInt: getAttributes(unsafeWindow.document.querySelector("setting[title=\'hidden-int\']"))\n' + + '});\n' + + '}, 250);\n' + + '}, false);\n' + + 'unsafeWindow.gViewController.commands.cmd_showItemDetails.doCommand(aAddon, true);\n' + + '});\n' + + 'function getAttributes(ele) {\n' + + 'if (!ele) return {};\n' + + 'return {\n' + + 'pref: ele.getAttribute("pref"),\n' + + 'type: ele.getAttribute("type"),\n' + + 'title: ele.getAttribute("title"),\n' + + 'desc: ele.getAttribute("desc")\n' + + '}\n' + + '}\n' + + '}\n' + + // Wait for the load event ? + 'if (document.readyState == "complete") {\n' + + 'onLoad()\n' + + '} else {\n' + + 'unsafeWindow.addEventListener("load", onLoad, false);\n' + + '}\n', + onMessage: function(msg) { + // test somePreference + assert.equal(msg.somePreference.type, 'string', 'some pref is a string'); + assert.equal(msg.somePreference.pref, 'extensions.'+self.preferencesBranch+'.somePreference', 'somePreference path is correct'); + assert.equal(msg.somePreference.title, 'some-title', 'somePreference title is correct'); + assert.equal(msg.somePreference.desc, 'Some short description for the preference', 'somePreference description is correct'); + + // test myInteger + assert.equal(msg.myInteger.type, 'integer', 'myInteger is a int'); + assert.equal(msg.myInteger.pref, 'extensions.'+self.preferencesBranch+'.myInteger', 'extensions.test-simple-prefs.myInteger'); + assert.equal(msg.myInteger.title, 'my-int', 'myInteger title is correct'); + assert.equal(msg.myInteger.desc, 'How many of them we have.', 'myInteger desc is correct'); + + // test myHiddenInt + assert.equal(msg.myHiddenInt.type, undefined, 'myHiddenInt was not displayed'); + assert.equal(msg.myHiddenInt.pref, undefined, 'myHiddenInt was not displayed'); + assert.equal(msg.myHiddenInt.title, undefined, 'myHiddenInt was not displayed'); + assert.equal(msg.myHiddenInt.desc, undefined, 'myHiddenInt was not displayed'); + + tab.close(done); + } + }); + } + }); + } +} + +require('sdk/test/runner').runTestsFromModule(module); diff --git a/addon-sdk/source/test/addons/simple-prefs-regression/package.json b/addon-sdk/source/test/addons/simple-prefs-regression/package.json new file mode 100644 index 000000000000..1de5e8ac2f6e --- /dev/null +++ b/addon-sdk/source/test/addons/simple-prefs-regression/package.json @@ -0,0 +1,24 @@ +{ + "id": "test-simple-prefs", + "preferences": [{ + "name": "somePreference", + "title": "some-title", + "description": "Some short description for the preference", + "type": "string", + "value": "TEST" + }, + { + "description": "How many of them we have.", + "name": "myInteger", + "type": "integer", + "value": 8, + "title": "my-int" + }, { + "name": "myHiddenInt", + "type": "integer", + "hidden": true, + "value": 5, + "title": "hidden-int" + }], + "preferences-branch": "simple-prefs-regression" +} diff --git a/addon-sdk/source/test/addons/simple-prefs/lib/main.js b/addon-sdk/source/test/addons/simple-prefs/lib/main.js index 222f927e16a7..ea56086d9a52 100644 --- a/addon-sdk/source/test/addons/simple-prefs/lib/main.js +++ b/addon-sdk/source/test/addons/simple-prefs/lib/main.js @@ -8,6 +8,7 @@ const sp = require('sdk/simple-prefs'); const app = require('sdk/system/xul-app'); const self = require('sdk/self'); const tabs = require('sdk/tabs'); +const { preferencesBranch } = require('sdk/self'); const { AddonManager } = Cu.import('resource://gre/modules/AddonManager.jsm', {}); @@ -89,4 +90,8 @@ if (app.is('Firefox')) { } } +exports.testDefaultPreferencesBranch = function(assert) { + assert.equal(preferencesBranch, self.id, 'preferencesBranch default the same as self.id'); +} + require('sdk/test/runner').runTestsFromModule(module); diff --git a/addon-sdk/source/test/addons/standard-id/lib/main.js b/addon-sdk/source/test/addons/standard-id/lib/main.js new file mode 100644 index 000000000000..ab1db12f2225 --- /dev/null +++ b/addon-sdk/source/test/addons/standard-id/lib/main.js @@ -0,0 +1,44 @@ +/* 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/. */ + +'use strict'; + +const { id, preferencesBranch } = require('sdk/self'); +const simple = require('sdk/simple-prefs'); +const service = require('sdk/preferences/service'); +const { AddonManager } = require('chrome').Cu.import('resource://gre/modules/AddonManager.jsm'); + +exports.testStandardID = function(assert) { + assert.equal(id, 'standard-id@jetpack', 'standard ID is standard'); + + assert.equal(simple.prefs.test13, 26, 'test13 is 26'); + + simple.prefs.test14 = '15'; + assert.equal(service.get('extensions.standard-id@jetpack.test14'), '15', 'test14 is 15'); + + assert.equal(service.get('extensions.standard-id@jetpack.test14'), simple.prefs.test14, 'simple test14 also 15'); + +} + +exports.testInvalidPreferencesBranch = function(assert) { + assert.notEqual(preferencesBranch, 'invalid^branch*name', 'invalid preferences-branch value ignored'); + + assert.equal(preferencesBranch, 'standard-id@jetpack', 'preferences-branch is standard-id@jetpack'); + +} + +// from `/test/test-self.js`, adapted to `sdk/test/assert` API +exports.testSelfID = function(assert, done) { + + assert.equal(typeof(id), 'string', 'self.id is a string'); + assert.ok(id.length > 0, 'self.id not empty'); + + AddonManager.getAddonByID(id, function(addon) { + assert.ok(addon, 'found addon with self.id'); + done(); + }); + +} + +require('sdk/test/runner').runTestsFromModule(module); diff --git a/addon-sdk/source/test/addons/standard-id/package.json b/addon-sdk/source/test/addons/standard-id/package.json new file mode 100644 index 000000000000..3d3e0ce801fd --- /dev/null +++ b/addon-sdk/source/test/addons/standard-id/package.json @@ -0,0 +1,14 @@ +{ + "id": "standard-id", + "fullName": "standard ID test", + "author": "Tomislav Jovanovic", + + "preferences": [{ + "name": "test13", + "type": "integer", + "title": "test13", + "value": 26 + }], + + "preferences-branch": "invalid^branch*name" +} diff --git a/addon-sdk/source/test/fixtures/loader/self/main.js b/addon-sdk/source/test/fixtures/loader/self/main.js new file mode 100644 index 000000000000..ab2b159da7d5 --- /dev/null +++ b/addon-sdk/source/test/fixtures/loader/self/main.js @@ -0,0 +1,8 @@ +/* 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/. */ + +"use strict"; + +var self = require("sdk/self"); +exports.self = self; diff --git a/addon-sdk/source/test/test-self.js b/addon-sdk/source/test/test-self.js index 47b00fe77657..0170a27f360b 100644 --- a/addon-sdk/source/test/test-self.js +++ b/addon-sdk/source/test/test-self.js @@ -6,6 +6,8 @@ const { Cc, Ci, Cu, Cm, components } = require("chrome"); const xulApp = require("sdk/system/xul-app"); const self = require("sdk/self"); +const { Loader, main, unload } = require("toolkit/loader"); +const loaderOptions = require("@loader/options"); const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {}); @@ -59,4 +61,19 @@ exports.testSelfID = function(assert, done) { }); } +exports.testSelfHandlesLackingLoaderOptions = function (assert) { + let root = module.uri.substr(0, module.uri.lastIndexOf('/')); + let uri = root + '/fixtures/loader/self/'; + let sdkPath = loaderOptions.paths[''] + 'sdk'; + let loader = Loader({ paths: { '': uri, 'sdk': sdkPath }}); + let program = main(loader, 'main'); + let self = program.self; + assert.pass("No errors thrown when including sdk/self without loader options"); + assert.equal(self.isPrivateBrowsingSupported, false, + "safely checks sdk/self.isPrivateBrowsingSupported"); + assert.equal(self.packed, false, + "safely checks sdk/self.packed"); + unload(loader); +}; + require("sdk/test").run(exports); diff --git a/browser/base/content/browser-fxaccounts.js b/browser/base/content/browser-fxaccounts.js new file mode 100644 index 000000000000..812ed75e8c66 --- /dev/null +++ b/browser/base/content/browser-fxaccounts.js @@ -0,0 +1,59 @@ +# 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/. + +XPCOMUtils.defineLazyGetter(this, "FxAccountsCommon", function () { + return Cu.import("resource://gre/modules/FxAccountsCommon.js", {}); +}); + +let gFxAccounts = { + + _initialized: false, + + get topics() { + delete this.topics; + return this.topics = [ + FxAccountsCommon.ONVERIFIED_NOTIFICATION + ]; + }, + + init: function () { + if (this._initialized) { + return; + } + + for (let topic of this.topics) { + Services.obs.addObserver(this, topic, false); + } + + this._initialized = true; + }, + + uninit: function () { + if (!this._initialized) { + return; + } + + for (let topic of this.topics) { + Services.obs.removeObserver(this, topic); + } + + this._initialized = false; + }, + + observe: function (subject, topic) { + this.showDoorhanger(); + }, + + showDoorhanger: function () { + let panel = document.getElementById("sync-popup"); + let anchor = document.getElementById("PanelUI-menu-button"); + + let iconAnchor = + document.getAnonymousElementByAttribute(anchor, "class", + "toolbarbutton-icon"); + + panel.hidden = false; + panel.openPopup(iconAnchor || anchor, "bottomcenter topright"); + } +}; diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index a3037379f784..8f1a3477a49a 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -146,6 +146,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "SitePermissions", XPCOMUtils.defineLazyModuleGetter(this, "SessionStore", "resource:///modules/sessionstore/SessionStore.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts", + "resource://gre/modules/FxAccounts.jsm"); + #ifdef MOZ_CRASHREPORTER XPCOMUtils.defineLazyModuleGetter(this, "TabCrashReporter", "resource:///modules/TabCrashReporter.jsm"); @@ -183,6 +186,8 @@ let gInitialPages = [ #include browser-syncui.js #endif +#include browser-fxaccounts.js + XPCOMUtils.defineLazyGetter(this, "Win7Features", function () { #ifdef XP_WIN // Bug 666808 - AeroPeek support for e10s @@ -1134,6 +1139,7 @@ var gBrowserInit = { #ifdef MOZ_SERVICES_SYNC // initialize the sync UI gSyncUI.init(); + gFxAccounts.init(); #endif #ifdef MOZ_DATA_REPORTING @@ -1238,6 +1244,10 @@ var gBrowserInit = { FullScreen.cleanup(); +#ifdef MOZ_SERVICES_SYNC + gFxAccounts.uninit(); +#endif + Services.obs.removeObserver(gPluginHandler.pluginCrashed, "plugin-crashed"); try { @@ -1702,21 +1712,11 @@ function openLocation() { } else { // If there are no open browser windows, open a new one - win = window.openDialog("chrome://browser/content/", "_blank", - "chrome,all,dialog=no", BROWSER_NEW_TAB_URL); - win.addEventListener("load", openLocationCallback, false); + window.openDialog("chrome://browser/content/", "_blank", + "chrome,all,dialog=no", BROWSER_NEW_TAB_URL); } - return; } #endif - openDialog("chrome://browser/content/openLocation.xul", "_blank", - "chrome,modal,titlebar", window); -} - -function openLocationCallback() -{ - // make sure the DOM is ready - setTimeout(function() { this.openLocation(); }, 0); } function BrowserOpenTab() diff --git a/browser/base/content/browser.xul b/browser/base/content/browser.xul index f2812852a26b..03e7a2691016 100644 --- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -382,6 +382,18 @@ </hbox> </panel> + <!-- Sync Panel --> + <panel id="sync-popup" type="arrow" hidden="true" noautofocus="true" + level="top" onclick="this.hidePopup();"> + <hbox id="sync-popup-outer"> + <image id="sync-popup-icon"/> + <vbox id="sync-popup-inner"> + <description id="sync-popup-desc-syncing" value="&syncPanel.descriptionSyncing;"/> + <description id="sync-popup-desc-prefs">&syncPanel.descriptionPrefs;</description> + </vbox> + </hbox> + </panel> + <!-- Bookmarks and history tooltip --> <tooltip id="bhTooltip"/> diff --git a/browser/base/content/openLocation.js b/browser/base/content/openLocation.js deleted file mode 100644 index 4ebcf29d1534..000000000000 --- a/browser/base/content/openLocation.js +++ /dev/null @@ -1,143 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ - -/* 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/. */ - -var browser; -var dialog = {}; -var pref = null; -let openLocationModule = {}; -try { - pref = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); -} catch (ex) { - // not critical, remain silent -} - -Components.utils.import("resource:///modules/openLocationLastURL.jsm", openLocationModule); -Components.utils.import("resource://gre/modules/Task.jsm"); -let gOpenLocationLastURL = new openLocationModule.OpenLocationLastURL(window.opener); - -function onLoad() -{ - dialog.input = document.getElementById("dialog.input"); - dialog.open = document.documentElement.getButton("accept"); - dialog.openWhereList = document.getElementById("openWhereList"); - dialog.openTopWindow = document.getElementById("currentWindow"); - dialog.bundle = document.getElementById("openLocationBundle"); - - if ("arguments" in window && window.arguments.length >= 1) - browser = window.arguments[0]; - - dialog.openWhereList.selectedItem = dialog.openTopWindow; - - if (pref) { - try { - var useAutoFill = pref.getBoolPref("browser.urlbar.autoFill"); - if (useAutoFill) - dialog.input.setAttribute("completedefaultindex", "true"); - } catch (ex) {} - - try { - var value = pref.getIntPref("general.open_location.last_window_choice"); - var element = dialog.openWhereList.getElementsByAttribute("value", value)[0]; - if (element) - dialog.openWhereList.selectedItem = element; - dialog.input.value = gOpenLocationLastURL.value; - } - catch(ex) { - } - if (dialog.input.value) - dialog.input.select(); // XXX should probably be done automatically - } - - doEnabling(); -} - -function doEnabling() -{ - dialog.open.disabled = !dialog.input.value; -} - -function open() -{ - Task.spawn(function() { - let url; - let postData = null; - let mayInheritPrincipal = false; - - if (browser) { - let data = yield browser.getShortcutOrURIAndPostData(dialog.input.value); - url = data.url; - postData = data.postData; - mayInheritPrincipal = data.mayInheritPrincipal; - } else { - url = dialog.input.value; - } - - try { - // Whichever target we use for the load, we allow third-party services to - // fixup the URI - switch (dialog.openWhereList.value) { - case "0": - var webNav = Components.interfaces.nsIWebNavigation; - var flags = webNav.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP | - webNav.LOAD_FLAGS_FIXUP_SCHEME_TYPOS; - if (!mayInheritPrincipal) - flags |= webNav.LOAD_FLAGS_DISALLOW_INHERIT_OWNER; - browser.gBrowser.loadURIWithFlags(url, flags, null, null, postData); - break; - case "1": - window.opener.delayedOpenWindow(getBrowserURL(), "all,dialog=no", - url, postData, null, null, true); - break; - case "3": - browser.delayedOpenTab(url, null, null, postData, true); - break; - } - } - catch(exception) { - } - - if (pref) { - gOpenLocationLastURL.value = dialog.input.value; - pref.setIntPref("general.open_location.last_window_choice", dialog.openWhereList.value); - } - - // Delay closing slightly to avoid timing bug on Linux. - window.close(); - }); - - return false; -} - -function createInstance(contractid, iidName) -{ - var iid = Components.interfaces[iidName]; - return Components.classes[contractid].createInstance(iid); -} - -const nsIFilePicker = Components.interfaces.nsIFilePicker; -function onChooseFile() -{ - try { - let fp = Components.classes["@mozilla.org/filepicker;1"]. - createInstance(nsIFilePicker); - let fpCallback = function fpCallback_done(aResult) { - if (aResult == nsIFilePicker.returnOK && fp.fileURL.spec && - fp.fileURL.spec.length > 0) { - dialog.input.value = fp.fileURL.spec; - } - doEnabling(); - }; - - fp.init(window, dialog.bundle.getString("chooseFileDialogTitle"), - nsIFilePicker.modeOpen); - fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText | - nsIFilePicker.filterImages | nsIFilePicker.filterXML | - nsIFilePicker.filterHTML); - fp.open(fpCallback); - } catch (ex) { - } -} diff --git a/browser/base/content/openLocation.xul b/browser/base/content/openLocation.xul deleted file mode 100644 index 7bafed0fe7fa..000000000000 --- a/browser/base/content/openLocation.xul +++ /dev/null @@ -1,57 +0,0 @@ -<?xml version="1.0"?> - -<!-- 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/. --> - -<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> - -<!DOCTYPE dialog SYSTEM "chrome://browser/locale/openLocation.dtd"> - -<dialog id="openLocation" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - title="&caption.label;" - onload="onLoad()" - buttonlabelaccept="&openBtn.label;" - buttoniconaccept="open" - ondialogaccept="open()" - style="width: 40em;" - persist="screenX screenY" - screenX="24" screenY="24"> - - <script type="application/javascript" src="chrome://global/content/globalOverlay.js"/> - <script type="application/javascript" src="chrome://browser/content/openLocation.js"/> - <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/> - - <stringbundle id="openLocationBundle" src="chrome://browser/locale/openLocation.properties"/> - - <hbox> - <separator orient="vertical" class="thin"/> - <vbox flex="1"> - <description>&enter.label;</description> - <separator class="thin"/> - - <hbox align="center"> - <textbox id="dialog.input" flex="1" type="autocomplete" - completeselectedindex="true" - autocompletesearch="urlinline history" - enablehistory="true" - class="uri-element" - oninput="doEnabling();"/> - <button label="&chooseFile.label;" oncommand="onChooseFile();"/> - </hbox> - <hbox align="center"> - <label value="&openWhere.label;"/> - <menulist id="openWhereList"> - <menupopup> - <menuitem value="0" id="currentWindow" label="&topTab.label;"/> - <menuitem value="3" label="&newTab.label;"/> - <menuitem value="1" label="&newWindow.label;"/> - </menupopup> - </menulist> - <spacer flex="1"/> - </hbox> - </vbox> - </hbox> - -</dialog> diff --git a/browser/base/content/sanitize.js b/browser/base/content/sanitize.js index 3195eb9acf6f..bc73e4da8485 100644 --- a/browser/base/content/sanitize.js +++ b/browser/base/content/sanitize.js @@ -220,14 +220,6 @@ Sanitizer.prototype = { } catch (e) { } - // Clear last URL of the Open Web Location dialog - var prefs = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - try { - prefs.clearUserPref("general.open_location.last_url"); - } - catch (e) { } - try { var seer = Components.classes["@mozilla.org/network/seer;1"] .getService(Components.interfaces.nsINetworkSeer); diff --git a/browser/base/jar.mn b/browser/base/jar.mn index cb13a9de0127..c8bc3c7d5a7f 100644 --- a/browser/base/jar.mn +++ b/browser/base/jar.mn @@ -102,8 +102,6 @@ browser.jar: content/browser/sync/progress.js (content/sync/progress.js) content/browser/sync/progress.xhtml (content/sync/progress.xhtml) #endif - content/browser/openLocation.js (content/openLocation.js) - content/browser/openLocation.xul (content/openLocation.xul) content/browser/safeMode.css (content/safeMode.css) content/browser/safeMode.js (content/safeMode.js) content/browser/safeMode.xul (content/safeMode.xul) diff --git a/browser/components/customizableui/content/customizeMode.inc.xul b/browser/components/customizableui/content/customizeMode.inc.xul index 3ff66b99aaee..87e2a3ba70c6 100644 --- a/browser/components/customizableui/content/customizeMode.inc.xul +++ b/browser/components/customizableui/content/customizeMode.inc.xul @@ -16,7 +16,7 @@ &customizeMode.menuAndToolbars.emptyLink; </label> </hbox> - <vbox id="customization-palette" flex="1"/> + <vbox id="customization-palette" class="customization-palette" flex="1"/> <spacer id="customization-spacer" flex="1"/> <hbox> <button id="customization-toolbar-visibility-button" label="&customizeMode.toolbars;" class="customizationmode-button" type="menu"> diff --git a/browser/components/privatebrowsing/test/browser/browser.ini b/browser/components/privatebrowsing/test/browser/browser.ini index 2b71c0723132..bea859111373 100644 --- a/browser/components/privatebrowsing/test/browser/browser.ini +++ b/browser/components/privatebrowsing/test/browser/browser.ini @@ -31,9 +31,7 @@ support-files = [browser_privatebrowsing_localStorage_before_after.js] [browser_privatebrowsing_noSessionRestoreMenuOption.js] [browser_privatebrowsing_nonbrowser.js] -[browser_privatebrowsing_openLocationLastURL.js] [browser_privatebrowsing_opendir.js] -[browser_privatebrowsing_openlocation.js] [browser_privatebrowsing_placesTitleNoUpdate.js] [browser_privatebrowsing_placestitle.js] [browser_privatebrowsing_popupblocker.js] diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_openLocationLastURL.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_openLocationLastURL.js deleted file mode 100644 index e68aaec3c2a6..000000000000 --- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_openLocationLastURL.js +++ /dev/null @@ -1,63 +0,0 @@ -/* 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/. */ - -function test() { - waitForExplicitFinish(); - const URL_1 = "mozilla.org"; - const URL_2 = "mozilla.com"; - - function testURL(aOpenLocation, aTestNumber, aValue) { - is(aOpenLocation.value, aValue, - "Test: " + aTestNumber + ": Validate last url value."); - } - - whenNewWindowLoaded({private: false}, function(normalWindow) { - whenNewWindowLoaded({private: true}, function(privateWindow) { - let openLocationLastURL = getLocationModule(normalWindow); - let openLocationLastURLPB = getLocationModule(privateWindow); - - // Clean to start testing. - is(typeof openLocationLastURL, "object", "Validate Normal type of last url."); - is(typeof openLocationLastURLPB, "object", "Validate PB type of last url."); - openLocationLastURL.reset(); - openLocationLastURLPB.reset(); - testURL(openLocationLastURL, 1, ""); - - // Test without private browsing. - openLocationLastURL.value = URL_1; - testURL(openLocationLastURL, 2, URL_1); - openLocationLastURL.value = ""; - testURL(openLocationLastURL, 3, ""); - openLocationLastURL.value = URL_2; - testURL(openLocationLastURL, 4, URL_2); - clearHistory(); - testURL(openLocationLastURL, 5, ""); - - // Test changing private browsing. - openLocationLastURL.value = URL_2; - testURL(openLocationLastURLPB, 6, ""); - testURL(openLocationLastURL, 7, URL_2); - openLocationLastURLPB.value = URL_1; - testURL(openLocationLastURLPB, 8, URL_1); - testURL(openLocationLastURL, 9, URL_2); - openLocationLastURLPB.value = URL_1; - testURL(openLocationLastURLPB, 10, URL_1); - - // Test cleaning history. - clearHistory(); - testURL(openLocationLastURLPB, 11, ""); - testURL(openLocationLastURL, 12, ""); - - privateWindow.close(); - normalWindow.close(); - finish(); - }); - }); -} - -function getLocationModule(aWindow) { - let openLocationModule = {}; - Cu.import("resource:///modules/openLocationLastURL.jsm", openLocationModule); - return new openLocationModule.OpenLocationLastURL(aWindow); -} \ No newline at end of file diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_openlocation.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_openlocation.js deleted file mode 100644 index 0707004ba96d..000000000000 --- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_openlocation.js +++ /dev/null @@ -1,100 +0,0 @@ -/* 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/. */ - -// This test makes sure that Open Location dialog is usable inside the private browsing -// mode without leaving any trace of the URLs visited. - -function test() { - // initialization - waitForExplicitFinish(); - - function openLocation(aWindow, url, autofilled, callback) { - function observer(aSubject, aTopic, aData) { - switch (aTopic) { - case "domwindowopened": - let dialog = aSubject.QueryInterface(Ci.nsIDOMWindow); - dialog.addEventListener("load", function () { - dialog.removeEventListener("load", arguments.callee, false); - - let browser = aWindow.gBrowser.selectedBrowser; - browser.addEventListener("load", function() { - // Ignore non-related loads (could be about:privatebrowsing for example, see bug 817932) - if (browser.currentURI.spec != url) { - return; - } - - browser.removeEventListener("load", arguments.callee, true); - - executeSoon(callback); - }, true); - - SimpleTest.waitForFocus(function() { - let input = dialog.document.getElementById("dialog.input"); - is(input.value, autofilled, "The input field should be correctly auto-filled"); - input.focus(); - for (let i = 0; i < url.length; ++i) - EventUtils.synthesizeKey(url[i], {}, dialog); - EventUtils.synthesizeKey("VK_RETURN", {}, dialog); - }, dialog); - }, false); - break; - - case "domwindowclosed": - Services.ww.unregisterNotification(arguments.callee); - break; - } - } - - executeSoon(function() { - Services.ww.registerNotification(observer); - gPrefService.setIntPref("general.open_location.last_window_choice", 0); - aWindow.openDialog("chrome://browser/content/openLocation.xul", "_blank", - "chrome,titlebar", aWindow); - }); - } - - let windowsToClose = []; - function testOnWindow(options, callback) { - let win = OpenBrowserWindow(options); - win.addEventListener("load", function onLoad() { - win.removeEventListener("load", onLoad, false); - windowsToClose.push(win); - callback(win); - }, false); - }; - - registerCleanupFunction(function() { - windowsToClose.forEach(function(win) { - win.close(); - }); - }); - - if (gPrefService.prefHasUserValue("general.open_location.last_url")) - gPrefService.clearUserPref("general.open_location.last_url"); - - testOnWindow({private: false}, function(win) { - openLocation(win, "http://example.com/", "", function() { - testOnWindow({private: false}, function(win) { - openLocation(win, "http://example.org/", "http://example.com/", function() { - testOnWindow({private: true}, function(win) { - openLocation(win, "about:logo", "", function() { - testOnWindow({private: true}, function(win) { - openLocation(win, "about:buildconfig", "about:logo", function() { - testOnWindow({private: false}, function(win) { - openLocation(win, "about:blank", "http://example.org/", function() { - gPrefService.clearUserPref("general.open_location.last_url"); - if (gPrefService.prefHasUserValue("general.open_location.last_window_choice")) - gPrefService.clearUserPref("general.open_location.last_window_choice"); - finish(); - }); - }); - }); - }); - }); - }); - }); - }); - }); - }); -} diff --git a/browser/locales/en-US/chrome/browser/browser.dtd b/browser/locales/en-US/chrome/browser/browser.dtd index 6b9f5a32b65e..d56c13681ba6 100644 --- a/browser/locales/en-US/chrome/browser/browser.dtd +++ b/browser/locales/en-US/chrome/browser/browser.dtd @@ -96,6 +96,9 @@ These should match what Safari and other Apple applications use on OS X Lion. -- <!ENTITY showAllTabsCmd.label "Show All Tabs"> <!ENTITY showAllTabsCmd.accesskey "A"> +<!ENTITY syncPanel.descriptionSyncing "&brandShortName; is now syncing."> +<!ENTITY syncPanel.descriptionPrefs "You can manage Sync from your browser's Preferences."> + <!ENTITY fullScreenMinimize.tooltip "Minimize"> <!ENTITY fullScreenRestore.tooltip "Restore"> <!ENTITY fullScreenClose.tooltip "Close"> diff --git a/browser/locales/en-US/chrome/browser/openLocation.dtd b/browser/locales/en-US/chrome/browser/openLocation.dtd deleted file mode 100644 index 240989220c56..000000000000 --- a/browser/locales/en-US/chrome/browser/openLocation.dtd +++ /dev/null @@ -1,14 +0,0 @@ -<!-- 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/. --> - -<!-- extracted from content/openLocation.xul --> - -<!ENTITY enter.label "Enter the web location (URL), or specify the local file you would like to open:"> -<!ENTITY chooseFile.label "Choose Fileā€¦"> -<!ENTITY newTab.label "New Tab"> -<!ENTITY newWindow.label "New Window"> -<!ENTITY topTab.label "Current Tab"> -<!ENTITY caption.label "Open Web Location"> -<!ENTITY openWhere.label "Open in:"> -<!ENTITY openBtn.label "Open"> diff --git a/browser/locales/en-US/chrome/browser/openLocation.properties b/browser/locales/en-US/chrome/browser/openLocation.properties deleted file mode 100644 index 2d08ff806317..000000000000 --- a/browser/locales/en-US/chrome/browser/openLocation.properties +++ /dev/null @@ -1,5 +0,0 @@ -# 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/. - -chooseFileDialogTitle=Choose File diff --git a/browser/locales/jar.mn b/browser/locales/jar.mn index 5761b50e8bfb..76971b0a9e8b 100644 --- a/browser/locales/jar.mn +++ b/browser/locales/jar.mn @@ -59,8 +59,6 @@ locale/browser/devtools/app-manager.properties (%chrome/browser/devtools/app-manager.properties) locale/browser/newTab.dtd (%chrome/browser/newTab.dtd) locale/browser/newTab.properties (%chrome/browser/newTab.properties) - locale/browser/openLocation.dtd (%chrome/browser/openLocation.dtd) - locale/browser/openLocation.properties (%chrome/browser/openLocation.properties) locale/browser/pageInfo.dtd (%chrome/browser/pageInfo.dtd) locale/browser/pageInfo.properties (%chrome/browser/pageInfo.properties) locale/browser/quitDialog.properties (%chrome/browser/quitDialog.properties) diff --git a/browser/metro/base/content/downloads.js b/browser/metro/base/content/downloads.js index c181d01f430c..dd71075769f6 100644 --- a/browser/metro/base/content/downloads.js +++ b/browser/metro/base/content/downloads.js @@ -4,7 +4,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ const URI_GENERIC_ICON_DOWNLOAD = "chrome://browser/skin/images/alert-downloads-30.png"; -const TOAST_URI_GENERIC_ICON_DOWNLOAD = "ms-appx:///metro/chrome/chrome/skin/images/alert-downloads-30.png" var MetroDownloadsView = { /** @@ -222,17 +221,14 @@ var MetroDownloadsView = { BrowserUI.addAndShowTab(uri, Browser.selectedTab); }, - showAlert: function dh_showAlert(aName, aMessage, aTitle, aIcon, aObserver) { + showAlert: function dh_showAlert(aName, aMessage, aTitle, aObserver) { var notifier = Cc["@mozilla.org/alerts-service;1"] .getService(Ci.nsIAlertsService); if (!aTitle) aTitle = Strings.browser.GetStringFromName("alertDownloads"); - if (!aIcon) - aIcon = TOAST_URI_GENERIC_ICON_DOWNLOAD; - - notifier.showAlertNotification(aIcon, aTitle, aMessage, true, "", aObserver, aName); + notifier.showAlertNotification("", aTitle, aMessage, true, "", aObserver, aName); }, showNotification: function dh_showNotification(title, msg, buttons, priority) { @@ -355,7 +351,7 @@ var MetroDownloadsView = { } } } - this.showAlert(name, msg, title, null, observer); + this.showAlert(name, msg, title, observer); }, _resetCompletedDownloads: function () { diff --git a/browser/metro/locales/en-US/chrome/browser.dtd b/browser/metro/locales/en-US/chrome/browser.dtd index 2a61ad0b03c4..cc7b0c870632 100644 --- a/browser/metro/locales/en-US/chrome/browser.dtd +++ b/browser/metro/locales/en-US/chrome/browser.dtd @@ -118,10 +118,7 @@ firstRunHistory.label ) These strings appear on the Firefox Start page the first time Firefox for Windows 8 Touch (Metro) is launched. Each one - has an arrow pointing toward the feature it references. The code - to display these strings is not enabled yet, but will be soon. - For now, you can see this mockup for an example of how they are - used: https://bug941284.bugzilla.mozilla.org/attachment.cgi?id=8344046 + has an arrow pointing toward the feature it references. --> <!ENTITY firstRunTabs.label "Looking for your tabs? Just pull down or right-click"> <!ENTITY firstRunTopSites.label "Go to the sites you visit most"> @@ -130,3 +127,21 @@ <!ENTITY firstRunHistory.label "See where you've been on the Web"> <!ENTITY firstRunWelcome.label "Welcome to &brandShortName;"> <!ENTITY firstRunDifferent.label "Different by design"> + + <!-- LOCALIZATION NOTE (firstRunBack.label, + firstRunNewTab.label, + firstRunStar.label, + firstRunPin.label, + firstRunGotIt.label ) + These strings appear as a content overlay the first few times a page + is visited. Each one has an arrow pointing toward the feature it + references. The code to display these strings is not enabled yet, + but will be soon. For now, you can see this mockup for an example + of how they are used: + https://bug949213.bugzilla.mozilla.org/attachment.cgi?id=8363973 + --> +<!ENTITY firstRunBack.label "Tap to go back to the previous page"> +<!ENTITY firstRunNewTab.label "Add a new tab to explore a new site"> +<!ENTITY firstRunStar.label "Use bookmarks to save pages for later"> +<!ENTITY firstRunPin.label "Pin any site to your Windows Start screen for quick access"> +<!ENTITY firstRunGotIt.label "Got it!"> diff --git a/browser/modules/AboutHome.jsm b/browser/modules/AboutHome.jsm index abbf9d5ef549..06d0fff074cf 100644 --- a/browser/modules/AboutHome.jsm +++ b/browser/modules/AboutHome.jsm @@ -15,6 +15,8 @@ Components.utils.import("resource://gre/modules/Services.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts", + "resource://gre/modules/FxAccounts.jsm"); // Url to fetch snippets, in the urlFormatter service format. const SNIPPETS_URL_PREF = "browser.aboutHomeSnippets.updateUrl"; @@ -146,7 +148,21 @@ let AboutHome = { break; case "AboutHome:Sync": - window.openPreferences("paneSync"); + let weave = Cc["@mozilla.org/weave/service;1"] + .getService(Ci.nsISupports) + .wrappedJSObject; + + if (weave.fxAccountsEnabled) { + fxAccounts.getSignedInUser().then(userData => { + if (userData) { + window.openPreferences("paneSync"); + } else { + window.loadURI("about:accounts"); + } + }); + } else { + window.openPreferences("paneSync"); + } break; case "AboutHome:Settings": diff --git a/browser/modules/moz.build b/browser/modules/moz.build index 19620c7f30ba..a26bf4c4b1ed 100644 --- a/browser/modules/moz.build +++ b/browser/modules/moz.build @@ -12,7 +12,6 @@ EXTRA_JS_MODULES += [ 'ContentClick.jsm', 'NetworkPrioritizer.jsm', 'offlineAppCache.jsm', - 'openLocationLastURL.jsm', 'SharedFrame.jsm', 'SignInToWebsite.jsm', 'SitePermissions.jsm', diff --git a/browser/modules/openLocationLastURL.jsm b/browser/modules/openLocationLastURL.jsm deleted file mode 100644 index 0d653df28bac..000000000000 --- a/browser/modules/openLocationLastURL.jsm +++ /dev/null @@ -1,85 +0,0 @@ -/* 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/. */ - -const LAST_URL_PREF = "general.open_location.last_url"; -const nsISupportsString = Components.interfaces.nsISupportsString; -const Ci = Components.interfaces; - -Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); - -this.EXPORTED_SYMBOLS = [ "OpenLocationLastURL" ]; - -let prefSvc = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); -let gOpenLocationLastURLData = ""; - -let observer = { - QueryInterface: function (aIID) { - if (aIID.equals(Components.interfaces.nsIObserver) || - aIID.equals(Components.interfaces.nsISupports) || - aIID.equals(Components.interfaces.nsISupportsWeakReference)) - return this; - throw Components.results.NS_NOINTERFACE; - }, - observe: function (aSubject, aTopic, aData) { - switch (aTopic) { - case "last-pb-context-exited": - gOpenLocationLastURLData = ""; - break; - case "browser:purge-session-history": - prefSvc.clearUserPref(LAST_URL_PREF); - gOpenLocationLastURLData = ""; - break; - } - } -}; - -let os = Components.classes["@mozilla.org/observer-service;1"] - .getService(Components.interfaces.nsIObserverService); -os.addObserver(observer, "last-pb-context-exited", true); -os.addObserver(observer, "browser:purge-session-history", true); - - -this.OpenLocationLastURL = function OpenLocationLastURL(aWindow) { - this.window = aWindow; -} - -OpenLocationLastURL.prototype = { - isPrivate: function OpenLocationLastURL_isPrivate() { - // Assume not in private browsing mode, unless the browser window is - // in private mode. - if (!this.window) - return false; - - return PrivateBrowsingUtils.isWindowPrivate(this.window); - }, - get value() { - if (this.isPrivate()) - return gOpenLocationLastURLData; - else { - try { - return prefSvc.getComplexValue(LAST_URL_PREF, nsISupportsString).data; - } - catch (e) { - return ""; - } - } - }, - set value(val) { - if (typeof val != "string") - val = ""; - if (this.isPrivate()) - gOpenLocationLastURLData = val; - else { - let str = Components.classes["@mozilla.org/supports-string;1"] - .createInstance(Components.interfaces.nsISupportsString); - str.data = val; - prefSvc.setComplexValue(LAST_URL_PREF, nsISupportsString, str); - } - }, - reset: function() { - prefSvc.clearUserPref(LAST_URL_PREF); - gOpenLocationLastURLData = ""; - } -}; diff --git a/browser/themes/linux/browser.css b/browser/themes/linux/browser.css index 29742c250591..0babb9e017f8 100644 --- a/browser/themes/linux/browser.css +++ b/browser/themes/linux/browser.css @@ -1681,6 +1681,26 @@ toolbarbutton.chevron > .toolbarbutton-icon { margin-top: 10px; } +/* Sync Panel */ + +#sync-popup { + padding: 10px; +} + +#sync-popup-icon { + width: 32px; + background: url("chrome://browser/content/abouthome/sync.png") center center no-repeat; +} + +#sync-popup-inner { + width: 0; + padding-left: 10px; +} + +#sync-popup-desc-prefs { + margin: 0; +} + /* Status panel */ .statuspanel-label { diff --git a/browser/themes/osx/browser.css b/browser/themes/osx/browser.css index 9d22c267f770..15e28034e7fa 100644 --- a/browser/themes/osx/browser.css +++ b/browser/themes/osx/browser.css @@ -1102,8 +1102,8 @@ toolbar .toolbarbutton-1:not([type="menu-button"]), } } -.toolbarbutton-1:not([type="menu-button"]), -.toolbarbutton-1 > .toolbarbutton-menubutton-button, +toolbar .toolbarbutton-1:not([type="menu-button"]), +toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button, #restore-button { min-width: 28px; } @@ -3588,6 +3588,33 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker { margin-top: .5em; } +/* Sync Panel */ + +#sync-popup { + padding: 10px; +} + +#sync-popup-icon { + width: 32px; + background: url("chrome://browser/content/abouthome/sync.png") center center no-repeat; +} + +@media (min-resolution: 2dppx) { + #sync-popup-icon { + background: url("chrome://browser/content/abouthome/sync@2x.png") center center no-repeat; + background-size: 32px 32px; + } +} + +#sync-popup-inner { + width: 0; + padding-left: 10px; +} + +#sync-popup-desc-prefs { + margin: 0; +} + /* Status panel */ .statuspanel-label { diff --git a/browser/themes/shared/customizableui/panelUIOverlay.inc.css b/browser/themes/shared/customizableui/panelUIOverlay.inc.css index 072d47cb863a..b6c45cb764bf 100644 --- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css +++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css @@ -80,10 +80,6 @@ box-shadow: none; } -#PanelUI-contents { - padding: .5em 0; -} - .panelUI-grid .toolbarbutton-menubutton-button > .toolbarbutton-multiline-text, .panelUI-grid .toolbarbutton-1 > .toolbarbutton-multiline-text { margin: 2px 0 0; @@ -113,6 +109,7 @@ panelview:not([mainview]) .toolbarbutton-text, flex: auto; margin-left: auto; margin-right: auto; + padding: .5em 0; max-width: @menuPanelWidth@; } @@ -133,11 +130,8 @@ panelview:not([mainview]) .toolbarbutton-text, } toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"]:not(.panel-wide-item), -#PanelUI-contents toolbarbutton, -toolbarpaletteitem[place="panel"] > toolbarbutton, -toolbarpaletteitem[place="palette"] > toolbarbutton, -toolbarpaletteitem[place="panel"] > toolbaritem > toolbarbutton, -toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton, +.panelUI-grid .toolbarbutton-1, +.customization-palette .toolbarbutton-1, .panel-customization-placeholder-child { -moz-appearance: none; -moz-box-orient: vertical; @@ -153,6 +147,24 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton, border: 1px solid transparent; } +.panelUI-grid .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker { + -moz-box-align: center; + width: 16px; + -moz-margin-start: -16px; + height: 40px; + max-height: 40px; + padding: 0; +} + +.panelUI-grid .toolbarbutton-1:not([buttonover])@buttonStateHover@ > .toolbarbutton-menubutton-dropmarker { + background-color: hsla(210,4%,10%,.1) !important; + border-radius: 0 0 0 2px; +} + +.panelUI-grid .toolbarbutton-1:not([buttonover])@buttonStateHover@ > .toolbarbutton-menubutton-dropmarker:-moz-locale-dir(rtl) { + border-radius: 0 0 2px 0; +} + .panel-combined-button[disabled] > .toolbarbutton-icon { opacity: .5; } @@ -175,14 +187,11 @@ toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"]:not(.panel-wide-it text-align: center; } -#PanelUI-contents .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-icon, -.panel-customization-placeholder-child > .toolbarbutton-icon, -#bookmarks-menu-button[cui-areatype="menu-panel"] > .toolbarbutton-menubutton-button > .toolbarbutton-icon, -toolbarbutton[cui-areatype="menu-panel"] > .toolbarbutton-icon, -toolbaritem[cui-areatype="menu-panel"] > toolbarbutton > .toolbarbutton-icon, -toolbarpaletteitem[place="palette"] > #bookmarks-menu-button > .toolbarbutton-menubutton-button > .toolbarbutton-icon, -toolbarpaletteitem[place="palette"] > toolbarbutton > .toolbarbutton-icon, -toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton > .toolbarbutton-icon { +.panelUI-grid .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon, +.panelUI-grid .toolbarbutton-1 > .toolbarbutton-icon, +.customization-palette .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon, +.customization-palette .toolbarbutton-1 > .toolbarbutton-icon, +.panel-customization-placeholder-child > .toolbarbutton-icon { min-width: 32px; min-height: 32px; margin: 4px; @@ -335,6 +344,7 @@ panelview .toolbarbutton-1, panelview .subviewbutton, .widget-overflow-list .toolbarbutton-1, .customizationmode-button, +.panelUI-grid .toolbarbutton-1 > .toolbarbutton-menubutton-button, #edit-controls@inAnyPanel@ > toolbarbutton, #zoom-controls@inAnyPanel@ > toolbarbutton, #BMB_bookmarksPopup > menu, @@ -383,6 +393,10 @@ panelview .toolbarbutton-1, margin-top: 6px; } +.panelUI-grid .toolbarbutton-1 > .toolbarbutton-menubutton-button { + border: 0; +} + panelview .toolbarbutton-1@buttonStateHover@, panelview .subviewbutton@buttonStateHover@, .widget-overflow-list .toolbarbutton-1@buttonStateHover@, diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css index ef3dd3aa9b4e..949e558055c0 100644 --- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -2140,6 +2140,26 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] { margin-top: .5em; } +/* Sync Panel */ + +#sync-popup { + padding: 10px; +} + +#sync-popup-icon { + width: 32px; + background: url("chrome://browser/content/abouthome/sync.png") center center no-repeat; +} + +#sync-popup-inner { + width: 0; + padding-left: 10px; +} + +#sync-popup-desc-prefs { + margin: 0; +} + /* Status panel */ .statuspanel-label { diff --git a/mobile/android/base/db/HomeListsDatabaseHelper.java b/mobile/android/base/db/HomeListsDatabaseHelper.java deleted file mode 100644 index 621e0f5b66c4..000000000000 --- a/mobile/android/base/db/HomeListsDatabaseHelper.java +++ /dev/null @@ -1,48 +0,0 @@ -/* 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/. */ - -package org.mozilla.gecko.db; - -import org.mozilla.gecko.GeckoProfile; -import org.mozilla.gecko.db.BrowserContract.HomeListItems; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.SQLException; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.util.Log; - -final class HomeListsDatabaseHelper extends SQLiteOpenHelper { - - static final int DATABASE_VERSION = 1; - static final String DATABASE_NAME = "homelists.db"; - - static final String TABLE_ITEMS = "items"; - - public HomeListsDatabaseHelper(Context context, String databasePath) { - super(context, databasePath, null, DATABASE_VERSION); - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL("CREATE TABLE " + TABLE_ITEMS + "(" + - HomeListItems._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - HomeListItems.PROVIDER_ID + " TEXT," + - HomeListItems.TITLE + " TEXT," + - HomeListItems.URL + " TEXT)" - ); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - // Unimpelemented - } - - @Override - public void onOpen(SQLiteDatabase db) { - // Unimplemented - } -} diff --git a/mobile/android/base/db/HomeListsProvider.java b/mobile/android/base/db/HomeListsProvider.java index b758b05040a3..70b7483c5cb1 100644 --- a/mobile/android/base/db/HomeListsProvider.java +++ b/mobile/android/base/db/HomeListsProvider.java @@ -4,37 +4,40 @@ package org.mozilla.gecko.db; +import org.mozilla.gecko.R; import org.mozilla.gecko.db.BrowserContract.HomeListItems; import org.mozilla.gecko.db.PerProfileDatabases.DatabaseHelperFactory; -import org.mozilla.gecko.mozglue.RobocopTarget; +import org.mozilla.gecko.sqlite.SQLiteBridge; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; -import android.content.ContentProvider; -import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; import android.database.Cursor; import android.database.MatrixCursor; import android.database.MatrixCursor.RowBuilder; -import android.database.SQLException; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; import android.os.Build; import android.text.TextUtils; import android.util.Log; -public class HomeListsProvider extends ContentProvider { +import java.io.InputStream; +import java.io.IOException; + +public class HomeListsProvider extends SQLiteBridgeContentProvider { private static final String LOGTAG = "GeckoHomeListsProvider"; - // Flag to disable DB usage. This prevents us from creating (and later needing to upgrade) a DB. - private static final boolean DB_DISABLED = true; + // This should be kept in sync with the db version in mobile/android/modules/HomeProvider.jsm + private static int DB_VERSION = 1; + private static String DB_FILENAME = "home.sqlite"; - private PerProfileDatabases<HomeListsDatabaseHelper> mDatabases; + private static final String TABLE_ITEMS = "items"; // Endpoint to return static fake data. static final int ITEMS_FAKE = 100; - static final int ITEMS = 101; static final int ITEMS_ID = 102; @@ -42,295 +45,29 @@ public class HomeListsProvider extends ContentProvider { static { URI_MATCHER.addURI(BrowserContract.HOME_LISTS_AUTHORITY, "items/fake", ITEMS_FAKE); - URI_MATCHER.addURI(BrowserContract.HOME_LISTS_AUTHORITY, "items", ITEMS); URI_MATCHER.addURI(BrowserContract.HOME_LISTS_AUTHORITY, "items/#", ITEMS_ID); } - private static boolean logDebug = Log.isLoggable(LOGTAG, Log.DEBUG); - private static boolean logVerbose = Log.isLoggable(LOGTAG, Log.VERBOSE); - protected static void trace(String message) { - if (logVerbose) { - Log.v(LOGTAG, message); - } - } - - protected static void debug(String message) { - if (logDebug) { - Log.d(LOGTAG, message); - } - } - - private boolean isTest(Uri uri) { - String isTest = uri.getQueryParameter(BrowserContract.PARAM_IS_TEST); - return !TextUtils.isEmpty(isTest); - } - - private SQLiteDatabase getReadableDatabase(Uri uri) { - if (DB_DISABLED) { - throw new UnsupportedOperationException("Database operations are disabled!"); - } - - String profile = null; - if (uri != null) { - profile = uri.getQueryParameter(BrowserContract.PARAM_PROFILE); - } - return mDatabases.getDatabaseHelperForProfile(profile, isTest(uri)).getReadableDatabase(); - } - - private SQLiteDatabase getWritableDatabase(Uri uri) { - if (DB_DISABLED) { - throw new UnsupportedOperationException("Database operations are disabled!"); - } - - String profile = null; - if (uri != null) { - profile = uri.getQueryParameter(BrowserContract.PARAM_PROFILE); - } - return mDatabases.getDatabaseHelperForProfile(profile, isTest(uri)).getWritableDatabase(); - } - - @Override - public boolean onCreate() { - if (DB_DISABLED) { - return true; - } - synchronized (this) { - mDatabases = new PerProfileDatabases<HomeListsDatabaseHelper>(getContext(), HomeListsDatabaseHelper.DATABASE_NAME, - new DatabaseHelperFactory<HomeListsDatabaseHelper>() { - @Override - public HomeListsDatabaseHelper makeDatabaseHelper(Context context, String databasePath) { - return new HomeListsDatabaseHelper(context, databasePath); - } - }); - } - return true; + public HomeListsProvider() { + super(LOGTAG); } @Override public String getType(Uri uri) { - trace("Getting URI type: " + uri); - final int match = URI_MATCHER.match(uri); + switch (match) { case ITEMS_FAKE: { - trace("URI is ITEMS_FAKE: " + uri); return HomeListItems.CONTENT_TYPE; } case ITEMS: { - trace("URI is ITEMS: " + uri); return HomeListItems.CONTENT_TYPE; } - case ITEMS_ID: { - trace("URI is ITEMS_ID: " + uri); - return HomeListItems.CONTENT_ITEM_TYPE; + default: { + throw new UnsupportedOperationException("Unknown type " + uri); } } - - debug("URI has unrecognized type: " + uri); - return null; - } - - @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { - trace("Calling delete on URI: " + uri); - - final SQLiteDatabase db = getWritableDatabase(uri); - int deleted = 0; - - if (Build.VERSION.SDK_INT >= 11) { - trace("Beginning delete transaction: " + uri); - db.beginTransaction(); - try { - deleted = deleteInTransaction(uri, selection, selectionArgs); - db.setTransactionSuccessful(); - trace("Successful delete transaction: " + uri); - } finally { - db.endTransaction(); - } - } else { - deleted = deleteInTransaction(uri, selection, selectionArgs); - } - - if (deleted > 0) { - getContext().getContentResolver().notifyChange(uri, null); - } - return deleted; - } - - public int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) { - trace("Calling delete in transaction on URI: " + uri); - - final SQLiteDatabase db = getWritableDatabase(uri); - int deleted = 0; - - final int match = URI_MATCHER.match(uri); - switch (match) { - case ITEMS: { - trace("Delete on ITEMS: " + uri); - deleted = db.delete(HomeListsDatabaseHelper.TABLE_ITEMS, selection, selectionArgs); - break; - } - case ITEMS_ID: { - trace("Delete on ITEMS: " + uri); - // Not implemented - break; - } - default: - throw new UnsupportedOperationException("Unknown delete URI " + uri); - } - - debug("Deleted " + deleted + " rows for URI: " + uri); - return deleted; - } - - @Override - public Uri insert(Uri uri, ContentValues values) { - trace("Calling insert on URI: " + uri); - - final SQLiteDatabase db = getWritableDatabase(uri); - Uri result = null; - - if (Build.VERSION.SDK_INT >= 11) { - trace("Beginning insert transaction: " + uri); - db.beginTransaction(); - try { - result = insertInTransaction(uri, values); - db.setTransactionSuccessful(); - trace("Successful insert transaction: " + uri); - } finally { - db.endTransaction(); - } - } else { - result = insertInTransaction(uri, values); - } - - if (result != null) { - getContext().getContentResolver().notifyChange(uri, null); - } - return result; - } - - @Override - public int bulkInsert(Uri uri, ContentValues[] values) { - if (values == null) { - return 0; - } - - final SQLiteDatabase db = getWritableDatabase(uri); - final int numValues = values.length; - - int successes = 0; - - db.beginTransaction(); - try { - for (int i = 0; i < numValues; i++) { - try { - insertInTransaction(uri, values[i]); - successes++; - } catch (SQLException e) { - Log.e(LOGTAG, "SQLException in bulkInsert", e); - - // Restart the transaction to continue insertions. - db.setTransactionSuccessful(); - db.endTransaction(); - db.beginTransaction(); - } - } - trace("Flushing DB bulkinsert..."); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - - if (successes > 0) { - getContext().getContentResolver().notifyChange(uri, null); - } - return successes; - } - - public Uri insertInTransaction(Uri uri, ContentValues values) { - trace("Calling insert in transaction on URI: " + uri); - - final SQLiteDatabase db = getWritableDatabase(uri); - long id = -1; - - final int match = URI_MATCHER.match(uri); - switch (match) { - case ITEMS: { - trace("Insert on ITEMS: " + uri); - id = db.insertOrThrow(HomeListsDatabaseHelper.TABLE_ITEMS, null, values); - break; - } - case ITEMS_ID: { - trace("Insert on ITEMS_ID: " + uri); - // Not implemented - break; - } - default: - throw new UnsupportedOperationException("Unknown insert URI " + uri); - } - - if (id >= 0) { - debug("Inserted ID in database: " + id); - return ContentUris.withAppendedId(uri, id); - } - return null; - } - - @Override - public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { - trace("Calling update on URI: " + uri); - - final SQLiteDatabase db = getWritableDatabase(uri); - int updated = 0; - - if (Build.VERSION.SDK_INT >= 11) { - trace("Beginning update transaction: " + uri); - db.beginTransaction(); - try { - updated = updateInTransaction(uri, values, selection, selectionArgs); - db.setTransactionSuccessful(); - trace("Successful update transaction: " + uri); - } finally { - db.endTransaction(); - } - } else { - updated = updateInTransaction(uri, values, selection, selectionArgs); - } - - if (updated > 0) { - getContext().getContentResolver().notifyChange(uri, null); - } - return updated; - } - - - public int updateInTransaction(Uri uri, ContentValues values, String selection, - String[] selectionArgs) { - trace("Calling update in transaction on URI: " + uri); - - final SQLiteDatabase db = getWritableDatabase(uri); - int updated = 0; - - final int match = URI_MATCHER.match(uri); - switch (match) { - case ITEMS: { - trace("Update on ITEMS: " + uri); - updated = db.update(HomeListsDatabaseHelper.TABLE_ITEMS, values, selection, selectionArgs); - break; - } - case ITEMS_ID: { - trace("Update on ITEMS_ID: " + uri); - // Not implemented - break; - } - default: - throw new UnsupportedOperationException("Unknown update URI " + uri); - } - - debug("Updated " + updated + " rows for URI: " + uri); - return updated; } @Override @@ -339,34 +76,28 @@ public class HomeListsProvider extends ContentProvider { // If we're querying the fake items, don't try to get the database. if (match == ITEMS_FAKE) { - trace("Query on ITEMS_FAKE: " + uri); return queryFakeItems(uri, projection, selection, selectionArgs, sortOrder); } - final SQLiteDatabase db = getReadableDatabase(uri); - Cursor cursor = null; - - switch (match) { - case ITEMS: { - trace("Query on ITEMS: " + uri); - cursor = db.query(HomeListsDatabaseHelper.TABLE_ITEMS, projection, selection, selectionArgs, null, null, sortOrder, null); - break; - } - case ITEMS_ID: { - trace("Query on ITEMS_ID: " + uri); - // Not implemented - break; - } - default: - throw new UnsupportedOperationException("Unknown query URI " + uri); - } - return cursor; + // Otherwise, let the SQLiteContentProvider implementation take care of this query for us! + return super.query(uri, projection, selection, selectionArgs, sortOrder); } /** * Returns a cursor populated with static fake data. */ private Cursor queryFakeItems(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + JSONArray items = null; + try { + items = new JSONArray(getRawFakeItems()); + } catch (IOException e) { + Log.e(LOGTAG, "Error getting fake list items", e); + return null; + } catch (JSONException e) { + Log.e(LOGTAG, "Error parsing fake-list-items.json", e); + return null; + } + final String[] itemsColumns = new String[] { HomeListItems._ID, HomeListItems.PROVIDER_ID, @@ -374,10 +105,81 @@ public class HomeListsProvider extends ContentProvider { HomeListItems.TITLE }; - // XXX: Return more items (from JSON file?) and filter fake items by provider specified in selection final MatrixCursor c = new MatrixCursor(itemsColumns); - c.addRow(new Object[] { 1, "fake-provider", "http://example.com", "Example" }); - c.addRow(new Object[] { 2, "fake-provider", "http://mozilla.org", "Mozilla" }); + for (int i = 0; i < items.length(); i++) { + try { + final JSONObject item = items.getJSONObject(i); + c.addRow(new Object[] { + item.getInt("id"), + item.getString("provider_id"), + item.getString("url"), + item.getString("title") + }); + } catch (JSONException e) { + Log.e(LOGTAG, "Error creating cursor row for fake list item", e); + } + } return c; } + + private String getRawFakeItems() throws IOException { + final InputStream inputStream = getContext().getResources().openRawResource(R.raw.fake_list_items); + final byte[] buffer = new byte[1024]; + StringBuilder s = new StringBuilder(); + int count; + + while ((count = inputStream.read(buffer)) != -1) { + s.append(new String(buffer, 0, count)); + } + return s.toString(); + } + + /** + * SQLiteBridgeContentProvider implementation + */ + + @Override + protected String getDBName(){ + return DB_FILENAME; + } + + @Override + protected int getDBVersion(){ + return DB_VERSION; + } + + @Override + public String getTable(Uri uri) { + final int match = URI_MATCHER.match(uri); + switch (match) { + case ITEMS: { + return TABLE_ITEMS; + } + default: { + throw new UnsupportedOperationException("Unknown table " + uri); + } + } + } + + @Override + public String getSortOrder(Uri uri, String aRequested) { + return null; + } + + @Override + public void setupDefaults(Uri uri, ContentValues values) { } + + @Override + public void initGecko() { } + + @Override + public void onPreInsert(ContentValues values, Uri uri, SQLiteBridge db) { } + + @Override + public void onPreUpdate(ContentValues values, Uri uri, SQLiteBridge db) { } + + @Override + public void onPostQuery(Cursor cursor, Uri uri, SQLiteBridge db) { } + + } diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build index 01d92b2bc1a2..0b1e61df40e7 100644 --- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -115,7 +115,6 @@ gbjar.sources += [ 'db/BrowserProvider.java', 'db/DBUtils.java', 'db/FormHistoryProvider.java', - 'db/HomeListsDatabaseHelper.java', 'db/HomeListsProvider.java', 'db/LocalBrowserDB.java', 'db/PasswordsProvider.java', diff --git a/mobile/android/base/resources/raw/fake_list_items.json b/mobile/android/base/resources/raw/fake_list_items.json new file mode 100644 index 000000000000..257aeed9ce5b --- /dev/null +++ b/mobile/android/base/resources/raw/fake_list_items.json @@ -0,0 +1,11 @@ +[{ + "id": 1, + "provider_id": "fake-provider", + "url": "http://example.com", + "title": "Example" +}, { + "id": 2, + "provider_id": "fake-provider", + "url": "http://mozilla.org", + "title": "Mozilla" +}] diff --git a/mobile/android/base/tests/robocop.ini b/mobile/android/base/tests/robocop.ini index a7e2af62f494..3781f2ae834b 100644 --- a/mobile/android/base/tests/robocop.ini +++ b/mobile/android/base/tests/robocop.ini @@ -39,6 +39,7 @@ skip-if = processor == "x86" [testJarReader] [testLinkContextMenu] [testHomeListsProvider] +[testHomeProvider] [testLoad] [testMailToContextMenu] [testMasterPassword] diff --git a/mobile/android/base/tests/testHomeProvider.java b/mobile/android/base/tests/testHomeProvider.java new file mode 100644 index 000000000000..063c36340587 --- /dev/null +++ b/mobile/android/base/tests/testHomeProvider.java @@ -0,0 +1,9 @@ +package org.mozilla.gecko.tests; + +import org.mozilla.gecko.*; + +public class testHomeProvider extends JavascriptTest { + public testHomeProvider() { + super("testHomeProvider.js"); + } +} diff --git a/mobile/android/base/tests/testHomeProvider.js b/mobile/android/base/tests/testHomeProvider.js new file mode 100644 index 000000000000..f15df18bc375 --- /dev/null +++ b/mobile/android/base/tests/testHomeProvider.js @@ -0,0 +1,45 @@ +// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- +/* 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/. */ + +const { utils: Cu } = Components; + +Cu.import("resource://gre/modules/HomeProvider.jsm"); +Cu.import("resource://gre/modules/osfile.jsm"); +Cu.import("resource://gre/modules/Sqlite.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); + +const TEST_DATASET_ID = "test-dataset-id"; +const TEST_URL = "http://test.com"; + +const DB_PATH = OS.Path.join(OS.Constants.Path.profileDir, "home.sqlite"); + +add_task(function test_save_and_delete() { + // Use the HomeProvider API to save some data. + let storage = HomeProvider.getStorage(TEST_DATASET_ID); + yield storage.save([{ url: TEST_URL }]); + + // Peek in the DB to make sure we have the right data. + let db = yield Sqlite.openConnection({ path: DB_PATH }); + + // Make sure the items table was created. + do_check_true(yield db.tableExists("items")); + + // Make sure the correct values for the item ended up in there. + let result = yield db.execute("SELECT * FROM items", null, function onRow(row){ + do_check_eq(row.getResultByName("dataset_id"), TEST_DATASET_ID); + do_check_eq(row.getResultByName("url"), TEST_URL); + }); + + // Use the HomeProvider API to delete the data. + yield storage.deleteAll(); + + // Make sure the data was deleted. + let result = yield db.execute("SELECT * FROM items"); + do_check_eq(result.length, 0); + + db.close(); +}); + +run_next_test(); diff --git a/mobile/android/modules/HomeProvider.jsm b/mobile/android/modules/HomeProvider.jsm new file mode 100644 index 000000000000..1d4e7182202c --- /dev/null +++ b/mobile/android/modules/HomeProvider.jsm @@ -0,0 +1,114 @@ +// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- +/* 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/. */ + +"use strict"; + +this.EXPORTED_SYMBOLS = [ "HomeProvider" ]; + +const { utils: Cu } = Components; + +Cu.import("resource://gre/modules/osfile.jsm"); +Cu.import("resource://gre/modules/Promise.jsm"); +Cu.import("resource://gre/modules/Sqlite.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); + +const SCHEMA_VERSION = 1; + +const DB_PATH = OS.Path.join(OS.Constants.Path.profileDir, "home.sqlite"); + +/** + * All SQL statements should be defined here. + */ +const SQL = { + createItemsTable: + "CREATE TABLE items (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + + "dataset_id TEXT NOT NULL, " + + "url TEXT," + + "primary_text TEXT," + + "secondary_text TEXT" + + ")", + + insertItem: + "INSERT INTO items (dataset_id, url) " + + "VALUES (:dataset_id, :url)", + + deleteFromDataset: + "DELETE FROM items WHERE dataset_id = :dataset_id" +} + +this.HomeProvider = Object.freeze({ + /** + * Returns a storage associated with a given dataset identifer. + * + * @param datasetId + * (string) Unique identifier for the dataset. + * + * @return HomeStorage + */ + getStorage: function(datasetId) { + return new HomeStorage(datasetId); + } +}); + +this.HomeStorage = function(datasetId) { + this.datasetId = datasetId; +}; + +HomeStorage.prototype = { + /** + * Saves data rows to the DB. + * + * @param data + * (array) JSON array of row items + * + * @return Promise + * @resolves When the operation has completed. + */ + save: function(data) { + return Task.spawn(function save_task() { + let db = yield Sqlite.openConnection({ path: DB_PATH }); + + try { + // XXX: Factor this out to some migration path. + if (!(yield db.tableExists("items"))) { + yield db.execute(SQL.createItemsTable); + } + + // Insert data into DB. + for (let item of data) { + // XXX: Directly pass item as params? More validation for item? Batch insert? + let params = { + dataset_id: this.datasetId, + url: item.url + }; + yield db.executeCached(SQL.insertItem, params); + } + } finally { + yield db.close(); + } + }.bind(this)); + }, + + /** + * Deletes all rows associated with this storage. + * + * @return Promise + * @resolves When the operation has completed. + */ + deleteAll: function() { + return Task.spawn(function delete_all_task() { + let db = yield Sqlite.openConnection({ path: DB_PATH }); + + try { + // XXX: Check to make sure table exists. + let params = { dataset_id: this.datasetId }; + yield db.executeCached(SQL.deleteFromDataset, params); + } finally { + yield db.close(); + } + }.bind(this)); + } +}; diff --git a/mobile/android/modules/moz.build b/mobile/android/modules/moz.build index 3f35bc3f70e0..650c379288f5 100644 --- a/mobile/android/modules/moz.build +++ b/mobile/android/modules/moz.build @@ -8,6 +8,7 @@ EXTRA_JS_MODULES += [ 'ContactService.jsm', 'HelperApps.jsm', 'Home.jsm', + 'HomeProvider.jsm', 'JNI.jsm', 'LightweightThemeConsumer.jsm', 'Notifications.jsm', diff --git a/toolkit/themes/osx/global/richlistbox.css b/toolkit/themes/osx/global/richlistbox.css index 276ec0bb99e8..605c89abb30c 100644 --- a/toolkit/themes/osx/global/richlistbox.css +++ b/toolkit/themes/osx/global/richlistbox.css @@ -25,12 +25,3 @@ richlistbox:focus > richlistitem[selected="true"] { color: HighlightText; } -richlistbox[seltype="multiple"]:focus > richlistitem[current="true"] { - outline: 1px dotted Highlight; - outline-offset: -1px; -} - -richlistbox[seltype="multiple"]:focus > richlistitem[current="true"][selected="true"] { - outline: 1px dotted #F3D982; /* TODO: find a suitable system color */ -} - diff --git a/widget/windows/winrt/ToastNotificationHandler.cpp b/widget/windows/winrt/ToastNotificationHandler.cpp index 1488273e125b..30405eff7d38 100644 --- a/widget/windows/winrt/ToastNotificationHandler.cpp +++ b/widget/windows/winrt/ToastNotificationHandler.cpp @@ -26,13 +26,7 @@ ToastNotificationHandler::DisplayNotification(HSTRING title, { mCookie = aCookie; - ComPtr<IToastNotificationManagerStatics> toastNotificationManagerStatics; - AssertHRESULT(GetActivationFactory(HStringReference(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), - toastNotificationManagerStatics.GetAddressOf())); - - ComPtr<IXmlDocument> toastXml; - toastNotificationManagerStatics->GetTemplateContent(ToastTemplateType::ToastTemplateType_ToastImageAndText03, &toastXml); - + ComPtr<IXmlDocument> toastXml = InitializeXmlForTemplate(ToastTemplateType::ToastTemplateType_ToastImageAndText03); ComPtr<IXmlNodeList> toastTextElements, toastImageElements; ComPtr<IXmlNode> titleTextNodeRoot, msgTextNodeRoot, imageNodeRoot, srcAttribute; @@ -56,27 +50,70 @@ ToastNotificationHandler::DisplayNotification(HSTRING title, SetNodeValueString(msg, msgTextNodeRoot.Get(), toastXml.Get()); SetNodeValueString(imagePath, srcAttribute.Get(), toastXml.Get()); + CreateWindowsNotificationFromXml(toastXml.Get()); +} + +void +ToastNotificationHandler::DisplayTextNotification(HSTRING title, + HSTRING msg, + const nsAString& aCookie) +{ + mCookie = aCookie; + + ComPtr<IXmlDocument> toastXml = InitializeXmlForTemplate(ToastTemplateType::ToastTemplateType_ToastText03); + ComPtr<IXmlNodeList> toastTextElements; + ComPtr<IXmlNode> titleTextNodeRoot, msgTextNodeRoot; + + HSTRING textNodeStr; + HSTRING_HEADER textHeader; + WindowsCreateStringReference(L"text", 4, &textHeader, &textNodeStr); + toastXml->GetElementsByTagName(textNodeStr, &toastTextElements); + + AssertHRESULT(toastTextElements->Item(0, &titleTextNodeRoot)); + AssertHRESULT(toastTextElements->Item(1, &msgTextNodeRoot)); + + SetNodeValueString(title, titleTextNodeRoot.Get(), toastXml.Get()); + SetNodeValueString(msg, msgTextNodeRoot.Get(), toastXml.Get()); + + CreateWindowsNotificationFromXml(toastXml.Get()); +} + +ComPtr<IXmlDocument> +ToastNotificationHandler::InitializeXmlForTemplate(ToastTemplateType templateType) { + ComPtr<IXmlDocument> toastXml; + + AssertRetHRESULT(GetActivationFactory(HStringReference(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), + mToastNotificationManagerStatics.GetAddressOf()), nullptr); + + mToastNotificationManagerStatics->GetTemplateContent(templateType, &toastXml); + + return toastXml; +} + +void +ToastNotificationHandler::CreateWindowsNotificationFromXml(IXmlDocument *toastXml) +{ ComPtr<IToastNotification> notification; ComPtr<IToastNotificationFactory> factory; AssertHRESULT(GetActivationFactory(HStringReference(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), - factory.GetAddressOf())); - AssertHRESULT(factory->CreateToastNotification(toastXml.Get(), ¬ification)); + factory.GetAddressOf())); + AssertHRESULT(factory->CreateToastNotification(toastXml, ¬ification)); EventRegistrationToken activatedToken; AssertHRESULT(notification->add_Activated(Callback<ToastActivationHandler>(this, - &ToastNotificationHandler::OnActivate).Get(), &activatedToken)); + &ToastNotificationHandler::OnActivate).Get(), &activatedToken)); EventRegistrationToken dismissedToken; AssertHRESULT(notification->add_Dismissed(Callback<ToastDismissHandler>(this, - &ToastNotificationHandler::OnDismiss).Get(), &dismissedToken)); + &ToastNotificationHandler::OnDismiss).Get(), &dismissedToken)); ComPtr<IToastNotifier> notifier; - toastNotificationManagerStatics->CreateToastNotifier(¬ifier); + mToastNotificationManagerStatics->CreateToastNotifier(¬ifier); notifier->Show(notification.Get()); MetroUtils::FireObserver("metro_native_toast_shown", mCookie.get()); } -void ToastNotificationHandler::SetNodeValueString(HSTRING inputString, ComPtr<IXmlNode> node, ComPtr<IXmlDocument> xml) { +void ToastNotificationHandler::SetNodeValueString(HSTRING inputString, ComPtr<IXmlNode> node, ComPtr<IXmlDocument> xml) { ComPtr<IXmlText> inputText; ComPtr<IXmlNode> inputTextNode, pAppendedChild; diff --git a/widget/windows/winrt/ToastNotificationHandler.h b/widget/windows/winrt/ToastNotificationHandler.h index 9753de626092..25665fa0ecee 100644 --- a/widget/windows/winrt/ToastNotificationHandler.h +++ b/widget/windows/winrt/ToastNotificationHandler.h @@ -15,6 +15,8 @@ using namespace Microsoft::WRL; class ToastNotificationHandler { typedef ABI::Windows::UI::Notifications::IToastNotification IToastNotification; typedef ABI::Windows::UI::Notifications::IToastDismissedEventArgs IToastDismissedEventArgs; + typedef ABI::Windows::UI::Notifications::IToastNotificationManagerStatics IToastNotificationManagerStatics; + typedef ABI::Windows::UI::Notifications::ToastTemplateType ToastTemplateType; typedef ABI::Windows::Data::Xml::Dom::IXmlNode IXmlNode; typedef ABI::Windows::Data::Xml::Dom::IXmlDocument IXmlDocument; @@ -24,10 +26,15 @@ class ToastNotificationHandler { ~ToastNotificationHandler() {}; void DisplayNotification(HSTRING title, HSTRING msg, HSTRING imagePath, const nsAString& aCookie); + void DisplayTextNotification(HSTRING title, HSTRING msg, const nsAString& aCookie); HRESULT OnActivate(IToastNotification *notification, IInspectable *inspectable); HRESULT OnDismiss(IToastNotification *notification, IToastDismissedEventArgs* aArgs); private: nsString mCookie; + ComPtr<IToastNotificationManagerStatics> mToastNotificationManagerStatics; + + void CreateWindowsNotificationFromXml(IXmlDocument *toastXml); + ComPtr<IXmlDocument> InitializeXmlForTemplate(ToastTemplateType templateType); }; diff --git a/widget/windows/winrt/nsWinMetroUtils.cpp b/widget/windows/winrt/nsWinMetroUtils.cpp index 2640a588d194..0c7d4a1929b2 100644 --- a/widget/windows/winrt/nsWinMetroUtils.cpp +++ b/widget/windows/winrt/nsWinMetroUtils.cpp @@ -210,8 +210,13 @@ nsWinMetroUtils::ShowNativeToast(const nsAString &aTitle, HSTRING title = HStringReference(aTitle.BeginReading()).Get(); HSTRING msg = HStringReference(aMessage.BeginReading()).Get(); - HSTRING imagePath = HStringReference(anImage.BeginReading()).Get(); - notification_handler->DisplayNotification(title, msg, imagePath, aCookie); + + if (anImage.Length() > 0) { + HSTRING imagePath = HStringReference(anImage.BeginReading()).Get(); + notification_handler->DisplayNotification(title, msg, imagePath, aCookie); + } else { + notification_handler->DisplayTextNotification(title, msg, aCookie); + } return NS_OK; }