aboutsummaryrefslogtreecommitdiff
path: root/tools/addon-sdk-1.12/lib/sdk/selection.js
diff options
context:
space:
mode:
Diffstat (limited to 'tools/addon-sdk-1.12/lib/sdk/selection.js')
-rw-r--r--tools/addon-sdk-1.12/lib/sdk/selection.js481
1 files changed, 0 insertions, 481 deletions
diff --git a/tools/addon-sdk-1.12/lib/sdk/selection.js b/tools/addon-sdk-1.12/lib/sdk/selection.js
deleted file mode 100644
index da94efe..0000000
--- a/tools/addon-sdk-1.12/lib/sdk/selection.js
+++ /dev/null
@@ -1,481 +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/. */
-
-"use strict";
-
-module.metadata = {
- "stability": "stable"
-};
-
-if (!require("./system/xul-app").is("Firefox")) {
- throw new Error([
- "The selection module currently supports only Firefox. In the future ",
- "we would like it to support other applications, however. Please see ",
- "https://bugzilla.mozilla.org/show_bug.cgi?id=560716 for more information."
- ].join(""));
-}
-
-let { Ci, Cc } = require("chrome"),
- { setTimeout } = require("./timers"),
- { emit, off } = require("./event/core"),
- { Unknown } = require("./platform/xpcom"),
- { Class, obscure } = require("./core/heritage"),
- { EventTarget } = require("./event/target"),
- observers = require("./deprecated/observer-service"),
- { ns } = require("./core/namespace");
-
-// When a document is not visible anymore the selection object is detached, and
-// a new selection object is created when it becomes visible again.
-// That makes the previous selection's listeners added previously totally
-// useless – the listeners are not notified anymore.
-// To fix that we're listening for `document-shown` event in order to add
-// the listeners to the new selection object created.
-//
-// See bug 665386 for further details.
-
-let selections = ns();
-
-observers.add("document-shown", function (document) {
- var window = document.defaultView;
-
- // We are not interested in documents without valid defaultView
- if (!window)
- return;
-
- let selection = selections(window).selection;
-
- // We want to handle only the windows where we added selection's listeners
- if (selection) {
- let currentSelection = window.getSelection();
-
- // If the current selection for the window given is different from the one
- // stored in the namespace, we need to add the listeners again, and replace
- // the previous selection in our list with the new one.
- //
- // Notice that we don't have to remove the listeners from the old selection,
- // because is detached. An attempt to remove the listener, will raise an
- // error (see http://mxr.mozilla.org/mozilla-central/source/layout/generic/nsSelection.cpp#5343 )
- //
- // We ensure that the current selection is an instance of
- // `nsISelectionPrivate` before working on it, in case is `null`.
- if (currentSelection instanceof Ci.nsISelectionPrivate &&
- currentSelection !== selection) {
-
- currentSelection.addSelectionListener(SelectionListenerManager);
- selections(window).selection = currentSelection;
- }
- }
-});
-
-const windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].
- getService(Ci.nsIWindowMediator);
-
-// The selection type HTML
-const HTML = 0x01;
-
-// The selection type TEXT
-const TEXT = 0x02;
-
-// The selection type DOM (internal use only)
-const DOM = 0x03;
-
-// A more developer-friendly message than the caught exception when is not
-// possible change a selection.
-const ERR_CANNOT_CHANGE_SELECTION =
- "It isn't possible to change the selection, as there isn't currently a selection";
-
-const Selection = Class({
- /**
- * Creates an object from which a selection can be set, get, etc. Each
- * object has an associated with a range number. Range numbers are the
- * 0-indexed counter of selection ranges as explained at
- * https://developer.mozilla.org/en/DOM/Selection.
- *
- * @param rangeNumber
- * The zero-based range index into the selection
- */
- initialize: function initialize(rangeNumber) {
- // In order to hide the private `rangeNumber` argument from API consumers
- // while still enabling Selection getters/setters to access it, we define
- // it as non enumerable, non configurable property. While consumers still
- // may discover it they won't be able to do any harm which is good enough
- // in this case.
- Object.defineProperties(this, {
- rangeNumber: {
- enumerable: false,
- configurable: false,
- value: rangeNumber
- }
- });
- },
- get text() { return getSelection(TEXT, this.rangeNumber); },
- set text(value) { setSelection(value, this.rangeNumber); },
- get html() { return getSelection(HTML, this.rangeNumber); },
- set html(value) { setSelection(value, this.rangeNumber); },
- get isContiguous() {
- let selection = getSelection(DOM);
-
- // If there are multiple ranges, the selection is definitely discontiguous.
- // It returns `false` also if there are no selection; and `true` if there is
- // a single non empty range, or a selection in a text field - contiguous or
- // not (text field selection APIs doesn't support multiple selections).
-
- if (selection.rangeCount > 1)
- return false;
-
- return !!(safeGetRange(selection, 0) || getElementWithSelection());
- }
-});
-
-/**
- * Returns the most recent content window
- */
-function context() {
- // Overlay names should probably go into the xul-app module instead of here
- return windowMediator.getMostRecentWindow("navigator:browser").document.
- commandDispatcher.focusedWindow;
-}
-
-/**
- * Returns the current selection from most recent content window. Depending on
- * the specified |type|, the value returned can be a string of text, stringified
- * HTML, or a DOM selection object as described at
- * https://developer.mozilla.org/en/DOM/Selection.
- *
- * @param type
- * Specifies the return type of the selection. Valid values are the one
- * of the constants HTML, TEXT, or DOM.
- *
- * @param rangeNumber
- * Specifies the zero-based range index of the returned selection.
- */
-function getSelection(type, rangeNumber) {
- let window, selection;
- try {
- window = context();
- selection = window.getSelection();
- }
- catch (e) {
- return null;
- }
-
- // Get the selected content as the specified type
- if (type == DOM)
- return selection;
- else if (type == TEXT) {
- let range = safeGetRange(selection, rangeNumber);
-
- if (range)
- return range.toString();
-
- let node = getElementWithSelection(window);
-
- if (!node)
- return null;
-
- return node.value.substring(node.selectionStart, node.selectionEnd);
- }
- else if (type == HTML) {
- let range = safeGetRange(selection, rangeNumber);
- // Another way, but this includes the xmlns attribute for all elements in
- // Gecko 1.9.2+ :
- // return Cc["@mozilla.org/xmlextras/xmlserializer;1"].
- // createInstance(Ci.nsIDOMSerializer).serializeToSTring(range.
- // cloneContents());
- if (!range)
- return null;
- let node = window.document.createElement("span");
- node.appendChild(range.cloneContents());
- return node.innerHTML;
- }
- throw new Error("Type " + type + " is unrecognized.");
-}
-
-/**
- * Returns the specified range in a selection without throwing an exception.
- *
- * @param selection
- * A selection object as described at
- * https://developer.mozilla.org/en/DOM/Selection
- *
- * @param rangeNumber
- * Specifies the zero-based range index of the returned selection.
- */
-function safeGetRange(selection, rangeNumber) {
- try {
- let range = selection.getRangeAt(rangeNumber);
- if (!range || range.toString() == "")
- return null;
- return range;
- }
- catch (e) {
- return null;
- }
-}
-
-/**
- * Returns a reference of the DOM's active element for the window given, if it
- * supports the text field selection API and has a text selected.
- *
- * Note:
- * we need this method because window.getSelection doesn't return a selection
- * for text selected in a form field (see bug 85686)
- *
- * @param {nsIWindow} [window]
- * A reference to a window
- */
-function getElementWithSelection(window) {
- let element;
-
- try {
- element = (window || context()).document.activeElement;
- }
- catch (e) {
- element = null;
- }
-
- if (!element)
- return null;
-
- let { value, selectionStart, selectionEnd } = element;
-
- let hasSelection = typeof value === "string" &&
- !isNaN(selectionStart) &&
- !isNaN(selectionEnd) &&
- selectionStart !== selectionEnd;
-
- return hasSelection ? element : null;
-}
-/**
- * Sets the current selection of the most recent content document by changing
- * the existing selected text/HTML range to the specified value.
- *
- * @param val
- * The value for the new selection
- *
- * @param rangeNumber
- * The zero-based range index of the selection to be set
- *
- */
-function setSelection(val, rangeNumber) {
- // Make sure we have a window context & that there is a current selection.
- // Selection cannot be set unless there is an existing selection.
- let window, selection;
-
- try {
- window = context();
- selection = window.getSelection();
- }
- catch (e) {
- throw new Error(ERR_CANNOT_CHANGE_SELECTION);
- }
-
- let range = safeGetRange(selection, rangeNumber);
-
- if (range) {
- // Get rid of the current selection and insert our own
- range.deleteContents();
- let node = window.document.createElement("span");
- range.surroundContents(node);
-
- // Some relevant JEP-111 requirements:
-
- // Setting the text property replaces the selection with the value to
- // which the property is set and sets the html property to the same value
- // to which the text property is being set.
-
- // Setting the html property replaces the selection with the value to
- // which the property is set and sets the text property to the text version
- // of the HTML value.
-
- // This sets both the HTML and text properties.
- node.innerHTML = val;
- } else {
- let node = getElementWithSelection(window);
-
- if (!node)
- throw new Error(ERR_CANNOT_CHANGE_SELECTION);
-
- let { value, selectionStart, selectionEnd } = node;
-
- let newSelectionEnd = selectionStart + val.length;
-
- node.value = value.substring(0, selectionStart) +
- val +
- value.substring(selectionEnd, value.length);
-
- node.setSelectionRange(selectionStart, newSelectionEnd);
- }
-}
-
-function onLoad(event) {
- SelectionListenerManager.onLoad(event);
-}
-
-function onUnload(event) {
- SelectionListenerManager.onUnload(event);
-}
-
-function onSelect() {
- SelectionListenerManager.onSelect();
-}
-
-let SelectionListenerManager = Class({
- extends: Unknown,
- interfaces: [ 'nsISelectionListener' ],
- /**
- * This is the nsISelectionListener implementation. This function is called
- * by Gecko when a selection is changed interactively.
- *
- * We only pay attention to the SELECTALL, KEYPRESS, and MOUSEUP selection
- * reasons. All reasons are listed here:
- *
- * http://mxr.mozilla.org/mozilla1.9.2/source/content/base/public/
- * nsISelectionListener.idl
- *
- * The other reasons (NO_REASON, DRAG_REASON, MOUSEDOWN_REASON) aren't
- * applicable to us.
- */
- notifySelectionChanged: function notifySelectionChanged(document, selection,
- reason) {
- if (!["SELECTALL", "KEYPRESS", "MOUSEUP"].some(function(type) reason &
- Ci.nsISelectionListener[type + "_REASON"]) || selection.toString() == "")
- return;
-
- this.onSelect();
- },
-
- onSelect : function onSelect() {
- setTimeout(emit, 0, module.exports, "select");
- },
-
- /**
- * Part of the Tracker implementation. This function is called by the
- * tabs module when a browser is being tracked. Often, that means a new tab
- * has been opened, but it can also mean an addon has been installed while
- * tabs are already opened. In that case, this function is called for those
- * already-opened tabs.
- *
- * @param browser
- * The browser being tracked
- */
- onTrack: function onTrack(browser) {
- browser.addEventListener("load", onLoad, true);
- browser.addEventListener("unload", onUnload, true);
- },
-
- onLoad: function onLoad(event) {
- // Nothing to do without a useful window
- let window = event.target.defaultView;
- if (!window)
- return;
-
- // Wrap the add selection call with some number of setTimeout 0 because some
- // reason it's possible to add a selection listener "too early". 2 sometimes
- // works for gmail, and more consistently with 3, so make it 5 to be safe.
- let count = 0;
- let self = this;
- function wrap(count, func) {
- if (count-- > 0)
- setTimeout(wrap, 0);
- else
- self.addSelectionListener(window);
- }
- wrap();
- },
-
- addSelectionListener: function addSelectionListener(window) {
- // Don't add the selection's listener more than once to the same window.
- if ("selection" in selections(window))
- return;
-
- let selection = window.getSelection();
-
- // We ensure that the current selection is an instance of
- // `nsISelectionPrivate` before working on it, in case is `null`.
- if (selection instanceof Ci.nsISelectionPrivate) {
- selection.addSelectionListener(this);
-
- // nsISelectionListener implementation seems not fire a notification if
- // a selection is in a text field, therefore we need to add a listener to
- // window.onselect, that is fired only for text fields.
- // For consistency, we add it only when the nsISelectionListener is added.
- //
- // https://developer.mozilla.org/en/DOM/window.onselect
- window.addEventListener("select", onSelect, true);
-
- selections(window).selection = selection;
- }
- },
-
- onUnload: function onUnload(event) {
- // Nothing to do without a useful window
- let window = event.target.defaultView;
- if (!window)
- return;
- this.removeSelectionListener(window);
- off(exports);
- },
-
- removeSelectionListener: function removeSelectionListener(window) {
- // Don't remove the selection's listener to a window that wasn't handled.
- if (!("selection" in selections(window)))
- return;
-
- let selection = window.getSelection();
-
- // We ensure that the current selection is an instance of
- // `nsISelectionPrivate` before working on it, in case is `null`.
- if (selection instanceof Ci.nsISelectionPrivate) {
- selection.removeSelectionListener(this);
-
- window.removeEventListener("select", onSelect);
-
- delete selections(window).selection;
- }
- },
-
- /**
- * Part of the TabTracker implementation. This function is called by the
- * tabs module when a browser is being untracked. Usually, that means a tab
- * has been closed.
- *
- * @param browser
- * The browser being untracked
- */
- onUntrack: function onUntrack(browser) {
- browser.removeEventListener("load", onLoad, true);
- browser.removeEventListener("unload", onUnload, true);
- }
-})();
-
-/**
- * Install |SelectionListenerManager| as tab tracker in order to watch
- * tab opening/closing
- */
-require("./deprecated/tab-browser").Tracker(SelectionListenerManager);
-
-var SelectionIterator = Class(obscure({
- /**
- * Exports an iterator so that discontiguous selections can be iterated.
- *
- * If discontiguous selections are in a text field, only the first one
- * is returned because the text field selection APIs doesn't support
- * multiple selections.
- */
- __iterator__: function() {
- let selection = getSelection(DOM);
- let count = selection.rangeCount || (getElementWithSelection() ? 1 : 0);
-
- for (let i = 0; i < count; i++)
- yield Selection(i);
- }
-}));
-
-var selection = Class({
- extends: EventTarget,
- implements: [ Selection, SelectionIterator ]
-})(0);
-
-module.exports = selection;