]> git.donarmstrong.com Git - dactyl.git/blob - common/content/browser.js
746b46ec01352d65b0524660bc190d2d73b6da6d
[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 (top == window)
32                 this._triggerLoadAutocmd("PageLoadPre", win.document, win.location.href != "null" ? window.location.href : uri);
33         }
34     },
35
36     _triggerLoadAutocmd: function _triggerLoadAutocmd(name, doc, uri) {
37         if (!(uri || doc.location))
38             return;
39
40         uri = isObject(uri) ? uri : util.newURI(uri || doc.location.href);
41         let args = {
42             url: { toString: function () uri.spec, valueOf: function () uri },
43             title: doc.title
44         };
45
46         if (dactyl.has("tabs")) {
47             args.tab = tabs.getContentIndex(doc) + 1;
48             args.doc = {
49                 valueOf: function () doc,
50                 toString: function () "tabs.getTab(" + (args.tab - 1) + ").linkedBrowser.contentDocument"
51             };
52         }
53
54         autocommands.trigger(name, args);
55     },
56
57     events: {
58         DOMContentLoaded: function onDOMContentLoaded(event) {
59             let doc = event.originalTarget;
60             if (doc instanceof HTMLDocument)
61                 this._triggerLoadAutocmd("DOMLoad", doc);
62         },
63
64         // TODO: see what can be moved to onDOMContentLoaded()
65         // event listener which is is called on each page load, even if the
66         // page is loaded in a background tab
67         load: function onLoad(event) {
68             let doc = event.originalTarget;
69             if (doc instanceof Document)
70                 dactyl.initDocument(doc);
71
72             if (doc instanceof HTMLDocument) {
73                 if (doc.defaultView.frameElement) {
74                     // document is part of a frameset
75
76                     // hacky way to get rid of "Transferring data from ..." on sites with frames
77                     // when you click on a link inside a frameset, because asyncUpdateUI
78                     // is not triggered there (Gecko bug?)
79                     this.timeout(function () { statusline.updateStatus(); }, 10);
80                 }
81                 else {
82                     // code which should happen for all (also background) newly loaded tabs goes here:
83                     if (doc != config.browser.contentDocument)
84                         dactyl.echomsg({ domains: [util.getHost(doc.location)], message: _("buffer.backgroundLoaded", (doc.title || doc.location.href)) }, 3);
85
86                     this._triggerLoadAutocmd("PageLoad", doc);
87                 }
88             }
89         }
90     },
91
92     /**
93      * @property {Object} The document loading progress listener.
94      */
95     progressListener: {
96         // XXX: function may later be needed to detect a canceled synchronous openURL()
97         onStateChange: util.wrapCallback(function onStateChange(webProgress, request, flags, status) {
98             onStateChange.superapply(this, arguments);
99             // STATE_IS_DOCUMENT | STATE_IS_WINDOW is important, because we also
100             // receive statechange events for loading images and other parts of the web page
101             if (flags & (Ci.nsIWebProgressListener.STATE_IS_DOCUMENT | Ci.nsIWebProgressListener.STATE_IS_WINDOW)) {
102                 dactyl.applyTriggerObserver("browser.stateChange", arguments);
103                 // This fires when the load event is initiated
104                 // only thrown for the current tab, not when another tab changes
105                 if (flags & Ci.nsIWebProgressListener.STATE_START) {
106                     while (document.commandDispatcher.focusedWindow == webProgress.DOMWindow
107                            && modes.have(modes.INPUT))
108                         modes.pop();
109
110                 }
111                 else if (flags & Ci.nsIWebProgressListener.STATE_STOP) {
112                     // Workaround for bugs 591425 and 606877, dactyl bug #81
113                     config.browser.mCurrentBrowser.collapsed = false;
114                     if (!dactyl.focusedElement || dactyl.focusedElement === document.documentElement)
115                         dactyl.focusContent();
116                 }
117             }
118         }),
119         onSecurityChange: util.wrapCallback(function onSecurityChange(webProgress, request, state) {
120             onSecurityChange.superapply(this, arguments);
121             dactyl.applyTriggerObserver("browser.securityChange", arguments);
122         }),
123         onStatusChange: util.wrapCallback(function onStatusChange(webProgress, request, status, message) {
124             onStatusChange.superapply(this, arguments);
125             dactyl.applyTriggerObserver("browser.statusChange", arguments);
126         }),
127         onProgressChange: util.wrapCallback(function onProgressChange(webProgress, request, curSelfProgress, maxSelfProgress, curTotalProgress, maxTotalProgress) {
128             onProgressChange.superapply(this, arguments);
129             dactyl.applyTriggerObserver("browser.progressChange", arguments);
130         }),
131         // happens when the users switches tabs
132         onLocationChange: util.wrapCallback(function onLocationChange(webProgress, request, uri) {
133             onLocationChange.superapply(this, arguments);
134
135             dactyl.applyTriggerObserver("browser.locationChange", arguments);
136
137             let win = webProgress.DOMWindow;
138             if (win && uri) {
139                 let oldURI = win.document.dactylURI;
140                 if (win.document.dactylLoadIdx === webProgress.loadedTransIndex
141                     || !oldURI || uri.spec.replace(/#.*/, "") !== oldURI.replace(/#.*/, ""))
142                     for (let frame in values(buffer.allFrames(win)))
143                         frame.document.dactylFocusAllowed = false;
144                 win.document.dactylURI = uri.spec;
145                 win.document.dactylLoadIdx = webProgress.loadedTransIndex;
146             }
147
148             // Workaround for bugs 591425 and 606877, dactyl bug #81
149             let collapse = uri && uri.scheme === "dactyl" && webProgress.isLoadingDocument;
150             if (collapse)
151                 dactyl.focus(document.documentElement);
152             config.browser.mCurrentBrowser.collapsed = collapse;
153
154             util.timeout(function () {
155                 browser._triggerLoadAutocmd("LocationChange",
156                                             (win || content).document,
157                                             uri);
158             });
159         }),
160         // called at the very end of a page load
161         asyncUpdateUI: util.wrapCallback(function asyncUpdateUI() {
162             asyncUpdateUI.superapply(this, arguments);
163             util.timeout(function () { statusline.updateStatus(); }, 100);
164         }),
165         setOverLink: util.wrapCallback(function setOverLink(link, b) {
166             setOverLink.superapply(this, arguments);
167             dactyl.triggerObserver("browser.overLink", link);
168         }),
169     }
170 }, {
171 }, {
172     events: function initEvents(dactyl, modules, window) {
173         events.listen(config.browser, browser, "events", true);
174     },
175     commands: function initCommands(dactyl, modules, window) {
176         commands.add(["o[pen]"],
177             "Open one or more URLs in the current tab",
178             function (args) { dactyl.open(args[0] || "about:blank"); },
179             {
180                 completer: function (context) completion.url(context),
181                 domains: function (args) array.compact(dactyl.parseURLs(args[0] || "").map(
182                     function (url) util.getHost(url))),
183                 literal: 0,
184                 privateData: true
185             });
186
187         commands.add(["redr[aw]"],
188             "Redraw the screen",
189             function () {
190                 window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils)
191                       .redraw();
192                 statusline.updateStatus();
193                 commandline.clear();
194             },
195             { argCount: "0" });
196     },
197     mappings: function initMappings(dactyl, modules, window) {
198         // opening websites
199         mappings.add([modes.NORMAL],
200             ["o"], "Open one or more URLs",
201             function () { CommandExMode().open("open "); });
202
203         mappings.add([modes.NORMAL], ["O"],
204             "Open one or more URLs, based on current location",
205             function () { CommandExMode().open("open " + buffer.uri.spec); });
206
207         mappings.add([modes.NORMAL], ["t"],
208             "Open one or more URLs in a new tab",
209             function () { CommandExMode().open("tabopen "); });
210
211         mappings.add([modes.NORMAL], ["T"],
212             "Open one or more URLs in a new tab, based on current location",
213             function () { CommandExMode().open("tabopen " + buffer.uri.spec); });
214
215         mappings.add([modes.NORMAL], ["w"],
216             "Open one or more URLs in a new window",
217             function () { CommandExMode().open("winopen "); });
218
219         mappings.add([modes.NORMAL], ["W"],
220             "Open one or more URLs in a new window, based on current location",
221             function () { CommandExMode().open("winopen " + buffer.uri.spec); });
222
223         mappings.add([modes.NORMAL], ["~"],
224             "Open home directory",
225             function () { dactyl.open("~"); });
226
227         mappings.add([modes.NORMAL], ["gh"],
228             "Open homepage",
229             function () { BrowserHome(); });
230
231         mappings.add([modes.NORMAL], ["gH"],
232             "Open homepage in a new tab",
233             function () {
234                 let homepages = gHomeButton.getHomePage();
235                 dactyl.open(homepages, { from: "homepage", where: dactyl.NEW_TAB });
236             });
237
238         mappings.add([modes.MAIN], ["<C-l>"],
239             "Redraw the screen",
240             function () { ex.redraw(); });
241     }
242 });
243
244 // vim: set fdm=marker sw=4 ts=4 et: