]> git.donarmstrong.com Git - dactyl.git/blob - common/content/browser.js
Import r6948 from upstream hg supporting Firefox up to 24.*
[dactyl.git] / common / content / browser.js
1 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
2 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
3 // Copyright (c) 2008-2012 Kris Maglione <maglione.k at Gmail>
4 //
5 // This work is licensed for reuse under an MIT license. Details are
6 // given in the LICENSE.txt file included with this file.
7 "use strict";
8
9 /** @scope modules */
10
11 /**
12  * @instance browser
13  */
14 var Browser = Module("browser", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
15     init: function init() {
16         this.cleanupProgressListener = overlay.overlayObject(window.XULBrowserWindow,
17                                                              this.progressListener);
18         util.addObserver(this);
19     },
20
21     destroy: function () {
22         this.cleanupProgressListener();
23         this.observe.unregister();
24     },
25
26     observers: {
27         "chrome-document-global-created": function (win, uri) { this.observe(win, "content-document-global-created", uri); },
28         "content-document-global-created": function (win, uri) {
29             let top = util.topWindow(win);
30
31             if (uri == "null")
32                 uri = null;
33
34             if (top == window && (win.location.href || uri))
35                 this._triggerLoadAutocmd("PageLoadPre", win.document, win.location.href || uri);
36         }
37     },
38
39     _triggerLoadAutocmd: function _triggerLoadAutocmd(name, doc, uri) {
40         if (!(uri || doc.location))
41             return;
42
43         uri = isObject(uri) ? uri : util.newURI(uri || doc.location.href);
44         let args = {
45             url: { toString: function () uri.spec, valueOf: function () uri },
46             title: doc.title
47         };
48
49         if (!dactyl.has("tabs"))
50             update(args, { doc: doc, win: doc.defaultView });
51         else {
52             args.tab = tabs.getContentIndex(doc) + 1;
53             args.doc = {
54                 valueOf: function () doc,
55                 toString: function () "tabs.getTab(" + (args.tab - 1) + ").linkedBrowser.contentDocument"
56             };
57             args.win = {
58                 valueOf: function () doc.defaultView,
59                 toString: function () "tabs.getTab(" + (args.tab - 1) + ").linkedBrowser.contentWindow"
60             };
61         }
62
63         autocommands.trigger(name, args);
64     },
65
66     events: {
67         DOMContentLoaded: function onDOMContentLoaded(event) {
68             let doc = event.originalTarget;
69             if (doc instanceof Ci.nsIDOMHTMLDocument)
70                 this._triggerLoadAutocmd("DOMLoad", doc);
71         },
72
73         // TODO: see what can be moved to onDOMContentLoaded()
74         // event listener which is is called on each page load, even if the
75         // page is loaded in a background tab
76         load: function onLoad(event) {
77             let doc = event.originalTarget;
78             if (doc instanceof Document)
79                 dactyl.initDocument(doc);
80
81             if (doc instanceof Ci.nsIDOMHTMLDocument) {
82                 if (doc.defaultView.frameElement) {
83                     // document is part of a frameset
84
85                     // hacky way to get rid of "Transferring data from ..." on sites with frames
86                     // when you click on a link inside a frameset, because asyncUpdateUI
87                     // is not triggered there (Gecko bug?)
88                     this.timeout(function () { statusline.updateStatus(); }, 10);
89                 }
90                 else {
91                     // code which should happen for all (also background) newly loaded tabs goes here:
92                     if (doc != config.browser.contentDocument)
93                         dactyl.echomsg({ domains: [util.getHost(doc.location)], message: _("buffer.backgroundLoaded", (doc.title || doc.location.href)) }, 3);
94
95                     this._triggerLoadAutocmd("PageLoad", doc);
96                 }
97             }
98         }
99     },
100
101     /**
102      * @property {Object} The document loading progress listener.
103      */
104     progressListener: {
105         // XXX: function may later be needed to detect a canceled synchronous openURL()
106         onStateChange: util.wrapCallback(function onStateChange(webProgress, request, flags, status) {
107             const L = Ci.nsIWebProgressListener;
108
109             if (request)
110                 dactyl.applyTriggerObserver("browser.stateChange", arguments);
111
112             if (flags & (L.STATE_IS_DOCUMENT | L.STATE_IS_WINDOW)) {
113                 // This fires when the load event is initiated
114                 // only thrown for the current tab, not when another tab changes
115                 if (flags & L.STATE_START) {
116                     while (document.commandDispatcher.focusedWindow == webProgress.DOMWindow
117                            && modes.have(modes.INPUT))
118                         modes.pop();
119
120                 }
121                 else if (flags & L.STATE_STOP) {
122                     // Workaround for bugs 591425 and 606877, dactyl bug #81
123                     config.browser.mCurrentBrowser.collapsed = false;
124                     if (!dactyl.focusedElement || dactyl.focusedElement === document.documentElement)
125                         dactyl.focusContent();
126                 }
127             }
128
129             onStateChange.superapply(this, arguments);
130         }),
131         onSecurityChange: util.wrapCallback(function onSecurityChange(webProgress, request, state) {
132             onSecurityChange.superapply(this, arguments);
133             dactyl.applyTriggerObserver("browser.securityChange", arguments);
134         }),
135         onStatusChange: util.wrapCallback(function onStatusChange(webProgress, request, status, message) {
136             onStatusChange.superapply(this, arguments);
137             dactyl.applyTriggerObserver("browser.statusChange", arguments);
138         }),
139         onProgressChange: util.wrapCallback(function onProgressChange(webProgress, request, curSelfProgress, maxSelfProgress, curTotalProgress, maxTotalProgress) {
140             onProgressChange.superapply(this, arguments);
141             dactyl.applyTriggerObserver("browser.progressChange", arguments);
142         }),
143         // happens when the users switches tabs
144         onLocationChange: util.wrapCallback(function onLocationChange(webProgress, request, uri) {
145             onLocationChange.superapply(this, arguments);
146
147             dactyl.applyTriggerObserver("browser.locationChange", arguments);
148
149             let win = webProgress.DOMWindow;
150             if (win && uri) {
151                 Buffer(win).updateZoom();
152
153                 let oldURI = overlay.getData(win.document)["uri"];
154                 if (overlay.getData(win.document)["load-idx"] === webProgress.loadedTransIndex
155                     || !oldURI || uri.spec.replace(/#.*/, "") !== oldURI.replace(/#.*/, ""))
156                     for (let frame in values(buffer.allFrames(win)))
157                         overlay.setData(frame.document, "focus-allowed", false);
158
159                 overlay.setData(win.document, "uri", uri.spec);
160                 overlay.setData(win.document, "load-idx", webProgress.loadedTransIndex);
161             }
162
163             // Workaround for bugs 591425 and 606877, dactyl bug #81
164             let collapse = uri && uri.scheme === "dactyl" && webProgress.isLoadingDocument;
165             if (collapse)
166                 dactyl.focus(document.documentElement);
167             config.browser.mCurrentBrowser.collapsed = collapse;
168
169             util.timeout(function () {
170                 browser._triggerLoadAutocmd("LocationChange",
171                                             (win || content).document,
172                                             uri);
173             });
174         }),
175         // called at the very end of a page load
176         asyncUpdateUI: util.wrapCallback(function asyncUpdateUI() {
177             asyncUpdateUI.superapply(this, arguments);
178             util.timeout(function () { statusline.updateStatus(); }, 100);
179         }),
180         setOverLink: util.wrapCallback(function setOverLink(link, b) {
181             setOverLink.superapply(this, arguments);
182             dactyl.triggerObserver("browser.overLink", link);
183         })
184     }
185 }, {
186 }, {
187     events: function initEvents(dactyl, modules, window) {
188         events.listen(config.browser, browser, "events", true);
189     },
190     commands: function initCommands(dactyl, modules, window) {
191         commands.add(["o[pen]"],
192             "Open one or more URLs in the current tab",
193             function (args) { dactyl.open(args[0] || "about:blank"); },
194             {
195                 completer: function (context) completion.url(context),
196                 domains: function (args) array.compact(dactyl.parseURLs(args[0] || "").map(
197                     function (url) util.getHost(url))),
198                 literal: 0,
199                 privateData: true
200             });
201
202         commands.add(["redr[aw]"],
203             "Redraw the screen",
204             function () {
205                 statusline.overLink = null;
206                 statusline.updateStatus();
207                 commandline.clear();
208                 window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils)
209                       .redraw();
210             },
211             { argCount: "0" });
212     },
213     mappings: function initMappings(dactyl, modules, window) {
214         let openModes = array.toObject([
215             [dactyl.CURRENT_TAB, ""],
216             [dactyl.NEW_TAB, "tab"],
217             [dactyl.NEW_BACKGROUND_TAB, "background tab"],
218             [dactyl.NEW_WINDOW, "win"]
219         ]);
220
221         function open(mode, args) {
222             if (dactyl.forceTarget in openModes)
223                 mode = openModes[dactyl.forceTarget];
224
225             CommandExMode().open(mode + "open " + (args || ""));
226         }
227
228         function decode(uri) util.losslessDecodeURI(uri)
229                                  .replace(/%20(?!(?:%20)*$)/g, " ")
230                                  .replace(RegExp(options["urlseparator"], "g"), encodeURIComponent);
231
232         mappings.add([modes.NORMAL],
233             ["o"], "Open one or more URLs",
234             function () { open(""); });
235
236         mappings.add([modes.NORMAL], ["O"],
237             "Open one or more URLs, based on current location",
238             function () { open("", decode(buffer.uri.spec)); });
239
240         mappings.add([modes.NORMAL], ["s"],
241             "Open a search prompt",
242             function () { open("", options["defsearch"] + " "); });
243
244         mappings.add([modes.NORMAL], ["S"],
245             "Open a search prompt for a new tab",
246             function () { open("tab", options["defsearch"] + " "); });
247
248         mappings.add([modes.NORMAL], ["t"],
249             "Open one or more URLs in a new tab",
250             function () { CommandExMode().open("tabopen "); });
251
252         mappings.add([modes.NORMAL], ["T"],
253             "Open one or more URLs in a new tab, based on current location",
254             function () { open("tab", decode(buffer.uri.spec)); });
255
256         mappings.add([modes.NORMAL], ["w"],
257             "Open one or more URLs in a new window",
258             function () { open("win"); });
259
260         mappings.add([modes.NORMAL], ["W"],
261             "Open one or more URLs in a new window, based on current location",
262             function () { open("win", decode(buffer.uri.spec)); });
263
264         mappings.add([modes.NORMAL], ["<open-home-directory>", "~"],
265             "Open home directory",
266             function () { dactyl.open("~"); });
267
268         mappings.add([modes.NORMAL], ["<open-homepage>", "gh"],
269             "Open homepage",
270             function () { window.BrowserHome(); });
271
272         mappings.add([modes.NORMAL], ["<tab-open-homepage>", "gH"],
273             "Open homepage in a new tab",
274             function () {
275                 let homepages = window.gHomeButton.getHomePage();
276                 dactyl.open(homepages, { from: "homepage", where: dactyl.NEW_TAB });
277             });
278
279         mappings.add([modes.MAIN], ["<redraw-screen>", "<C-l>"],
280             "Redraw the screen",
281             function () { ex.redraw(); });
282     }
283 });
284
285 // vim: set fdm=marker sw=4 sts=4 ts=8 et: