]> git.donarmstrong.com Git - dactyl.git/blob - common/content/browser.js
Import 1.0b7.1 supporting Firefox up to 8.*
[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-2011 by 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 = util.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 HTMLDocument)
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 HTMLDocument) {
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                 let oldURI = win.document.dactylURI;
152                 if (win.document.dactylLoadIdx === webProgress.loadedTransIndex
153                     || !oldURI || uri.spec.replace(/#.*/, "") !== oldURI.replace(/#.*/, ""))
154                     for (let frame in values(buffer.allFrames(win)))
155                         frame.document.dactylFocusAllowed = false;
156                 win.document.dactylURI = uri.spec;
157                 win.document.dactylLoadIdx = webProgress.loadedTransIndex;
158             }
159
160             // Workaround for bugs 591425 and 606877, dactyl bug #81
161             let collapse = uri && uri.scheme === "dactyl" && webProgress.isLoadingDocument;
162             if (collapse)
163                 dactyl.focus(document.documentElement);
164             config.browser.mCurrentBrowser.collapsed = collapse;
165
166             util.timeout(function () {
167                 browser._triggerLoadAutocmd("LocationChange",
168                                             (win || content).document,
169                                             uri);
170             });
171         }),
172         // called at the very end of a page load
173         asyncUpdateUI: util.wrapCallback(function asyncUpdateUI() {
174             asyncUpdateUI.superapply(this, arguments);
175             util.timeout(function () { statusline.updateStatus(); }, 100);
176         }),
177         setOverLink: util.wrapCallback(function setOverLink(link, b) {
178             setOverLink.superapply(this, arguments);
179             dactyl.triggerObserver("browser.overLink", link);
180         }),
181     }
182 }, {
183 }, {
184     events: function initEvents(dactyl, modules, window) {
185         events.listen(config.browser, browser, "events", true);
186     },
187     commands: function initCommands(dactyl, modules, window) {
188         commands.add(["o[pen]"],
189             "Open one or more URLs in the current tab",
190             function (args) { dactyl.open(args[0] || "about:blank"); },
191             {
192                 completer: function (context) completion.url(context),
193                 domains: function (args) array.compact(dactyl.parseURLs(args[0] || "").map(
194                     function (url) util.getHost(url))),
195                 literal: 0,
196                 privateData: true
197             });
198
199         commands.add(["redr[aw]"],
200             "Redraw the screen",
201             function () {
202                 window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils)
203                       .redraw();
204                 statusline.updateStatus();
205                 commandline.clear();
206             },
207             { argCount: "0" });
208     },
209     mappings: function initMappings(dactyl, modules, window) {
210         // opening websites
211         mappings.add([modes.NORMAL],
212             ["o"], "Open one or more URLs",
213             function () { CommandExMode().open("open "); });
214
215         function decode(uri) util.losslessDecodeURI(uri)
216                                  .replace(/%20(?!(?:%20)*$)/g, " ")
217                                  .replace(RegExp(options["urlseparator"], "g"), encodeURIComponent);
218
219         mappings.add([modes.NORMAL], ["O"],
220             "Open one or more URLs, based on current location",
221             function () { CommandExMode().open("open " + decode(buffer.uri.spec)); });
222
223         mappings.add([modes.NORMAL], ["t"],
224             "Open one or more URLs in a new tab",
225             function () { CommandExMode().open("tabopen "); });
226
227         mappings.add([modes.NORMAL], ["T"],
228             "Open one or more URLs in a new tab, based on current location",
229             function () { CommandExMode().open("tabopen " + decode(buffer.uri.spec)); });
230
231         mappings.add([modes.NORMAL], ["w"],
232             "Open one or more URLs in a new window",
233             function () { CommandExMode().open("winopen "); });
234
235         mappings.add([modes.NORMAL], ["W"],
236             "Open one or more URLs in a new window, based on current location",
237             function () { CommandExMode().open("winopen " + decode(buffer.uri.spec)); });
238
239         mappings.add([modes.NORMAL], ["<open-home-directory>", "~"],
240             "Open home directory",
241             function () { dactyl.open("~"); });
242
243         mappings.add([modes.NORMAL], ["<open-homepage>", "gh"],
244             "Open homepage",
245             function () { BrowserHome(); });
246
247         mappings.add([modes.NORMAL], ["<tab-open-homepage>", "gH"],
248             "Open homepage in a new tab",
249             function () {
250                 let homepages = gHomeButton.getHomePage();
251                 dactyl.open(homepages, { from: "homepage", where: dactyl.NEW_TAB });
252             });
253
254         mappings.add([modes.MAIN], ["<redraw-screen>", "<C-l>"],
255             "Redraw the screen",
256             function () { ex.redraw(); });
257     }
258 });
259
260 // vim: set fdm=marker sw=4 ts=4 et: