Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Bug 708190: Apply page-mod on existing documents. #404

Closed
wants to merge 11 commits into from

4 participants

ochameau Irakli Gozalishvili Dave Townsend wbamberg
ochameau
Collaborator

Here is random comments that can be usefull for review:

  • I've added a new apply attribute that works like allow attribute of symbiont. I've done that as we are planning to add similar flags in order to attach content script only on top level document (i.e. ignore iframes)
  • page-mod contains a small utility method that allows to list all opened tabs. I tried using tab-browser but it ended up being messy and most likely very badly efficient. As I don't know we want to document and maintain this method I've kept it as a page-mod private method. I've used yield to match window-utils behavior but I can use regular iteration too.

I'm waiting for feedback about the public API before writing docs.

packages/addon-kit/docs/page-mod.md
@@ -367,6 +367,12 @@ Creates a PageMod.
367 367
     option are loaded *after* those specified by the `contentStyleFile` option.
368 368
     Optional.
369 369
 
  370
+  @prop [target] {array}
6
Irakli Gozalishvili Collaborator
Gozala added a note May 07, 2012

As I mentioned in bugzilla I don't think target is a good property name here, it's used for many different things in different contexts, most commonly as a an event target. Also commonly to refer to a target element. In this case it has totally different meaning that may be confusing. I can also imagine that in a future we may use target property limit mods for tabs of the specific window for example.

I have suggested to use name handle or cover instead. If you dislike those names maybe we could find something better.

Irakli Gozalishvili Collaborator
Gozala added a note May 16, 2012

@ochameau mentioned on IRC that he does not likes names I have proposed. @Mossop @wbamberg what do you think ? Do they read awkward ? Maybe you have a better suggestions ?

wbamberg Collaborator
wbamberg added a note May 16, 2012

"handle" and "cover" both sound very generic to me. As I said yesterday on IRC, the best I can think of is "attachTo". I like the way it reads like a complete command: 'attachTo:["existing"]'.

Irakli Gozalishvili Collaborator
Gozala added a note May 16, 2012

We should also make sure that it will work for other possible values too: attachTo: [ "existing", "top", "frames" ], which reads bit awkward to me, but I'm non native so I could be wrong. Also attachTo conflicts something else I had in mind for bug 755963 maybe we should decide on naming there as well to make sure we won't run into naming issues again.

Dave Townsend Collaborator
Mossop added a note May 17, 2012

target already has a meaning in the web which seems close enough to what we're talking about here that we should avoid it to save confusion. attachTo reads well for all of the cases to me, but unless there is a better option for Bug 755963 we should avoid it. I guess applyTo is too similar as well. I'm traditionally poor a picking names out of my head, but maybe windowTypes or something along those lines?

ochameau Collaborator
ochameau added a note May 28, 2012

@Mossop I have the same limitation when it comes to names!
documentType may be an option. window sounds weird to me as there is no windows. I don't know for you but the word window is only associated to top level window for me. And singular as it looks like we are always using singular? (contentScript, contentScriptFile, ...)
@Gozala @wbamberg your thoughts?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
packages/addon-kit/docs/page-mod.md
@@ -367,6 +367,12 @@ Creates a PageMod.
367 367
     option are loaded *after* those specified by the `contentStyleFile` option.
368 368
     Optional.
369 369
 
  370
+  @prop [target] {array}
  371
+    Option to specify on which documents should apply the PageMod.
1
Irakli Gozalishvili Collaborator
Gozala added a note May 07, 2012

"which documents PageMod should be applied" is a correct from I think.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
packages/addon-kit/lib/page-mod.js
@@ -172,6 +181,19 @@ const PageMod = Loader.compose(EventEmitter, {
172 181
 
173 182
   _loadingWindows: [],
174 183
 
  184
+  _applyOnExistingDocuments: function _applyOnExistingDocuments() {
  185
+    for each(let tab in allTabsIterator()) {
  186
+      for each(let rule in this.include) {
3
Irakli Gozalishvili Collaborator
Gozala added a note May 07, 2012

could you write utility function like isMatching(tab.url, this.include) instead, that way you won't need to beak and it would by way easier to follow. It took me a while to figure out what was going on here.

Irakli Gozalishvili Collaborator
Gozala added a note May 07, 2012

So it will be like:

if (isMatching(tab.url, this.include))
  this._onContent(tab.content);
ochameau Collaborator
ochameau added a note May 16, 2012

Done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
packages/addon-kit/lib/page-mod.js
@@ -321,3 +348,29 @@ const PageModManager = Registry.resolve({
321 348
   }
322 349
 });
323 350
 const pageModManager = PageModManager();
  351
+
  352
+// Iterate over all tabs on all currently opened windows
  353
+function allTabsIterator() {
  354
+  // Iterate over all chrome windows
  355
+  for (let window in windowIterator()) {
  356
+    // Get a reference to the main <xul:tabbrowser> node
  357
+    let tabbrowser = window.document.getElementById("content");
  358
+    // It may not be a browser window but a jsconsole ...
  359
+    if (!tabbrowser)
  360
+      continue;
  361
+    let tabs = tabbrowser.tabContainer;
  362
+    if (!tabs)
  363
+      continue;
  364
+    // Iterate over its tabs
  365
+    for(let i = 0; i < tabs.children.length; i++) {
1
Irakli Gozalishvili Collaborator
Gozala added a note May 07, 2012

Please add a space before (.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
packages/addon-kit/lib/page-mod.js
@@ -321,3 +348,29 @@ const PageModManager = Registry.resolve({
321 348
   }
322 349
 });
323 350
 const pageModManager = PageModManager();
  351
+
  352
+// Iterate over all tabs on all currently opened windows
  353
+function allTabsIterator() {
2
Irakli Gozalishvili Collaborator
Gozala added a note May 07, 2012

Do you really need an iterator here ? I think plain arrays map / reduce would work just fine here as you iterate over each tab anyway.
Also it won't require non-standard features making it easier for newcomers.

ochameau Collaborator
ochameau added a note May 16, 2012

I totally agree. I used iterator to match existing window-utils iterators.
I removed it in favor of an array.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
packages/addon-kit/lib/page-mod.js
((8 lines not shown))
  355
+  for (let window in windowIterator()) {
  356
+    // Get a reference to the main <xul:tabbrowser> node
  357
+    let tabbrowser = window.document.getElementById("content");
  358
+    // It may not be a browser window but a jsconsole ...
  359
+    if (!tabbrowser)
  360
+      continue;
  361
+    let tabs = tabbrowser.tabContainer;
  362
+    if (!tabs)
  363
+      continue;
  364
+    // Iterate over its tabs
  365
+    for(let i = 0; i < tabs.children.length; i++) {
  366
+      let tab = tabs.children[i];
  367
+      let browser = tab.linkedBrowser;
  368
+      yield {
  369
+        tab: tab,
  370
+        browser: browser,
2
Irakli Gozalishvili Collaborator
Gozala added a note May 07, 2012

You only seem to use uri and content so what's a point of adding tab and browser and use of getters are even more confusing.

ochameau Collaborator
ochameau added a note May 16, 2012

I removed attributes that weren't used, and removed the use of getters.
It would make sense to use getters if we expose this. tab and browser never change, whereas uri and content can, that's why getter end up being important if usecode keep a reference to such tab object.
But as it isn't usefull for page-mod code, I removed them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
packages/addon-kit/lib/page-mod.js
((14 lines not shown))
  361
+    let tabs = tabbrowser.tabContainer;
  362
+    if (!tabs)
  363
+      continue;
  364
+    // Iterate over its tabs
  365
+    for(let i = 0; i < tabs.children.length; i++) {
  366
+      let tab = tabs.children[i];
  367
+      let browser = tab.linkedBrowser;
  368
+      yield {
  369
+        tab: tab,
  370
+        browser: browser,
  371
+        get uri() browser.currentURI.spec,
  372
+        get content() browser.contentWindow
  373
+      };
  374
+    }
  375
+  }
  376
+}
4
Irakli Gozalishvili Collaborator
Gozala added a note May 07, 2012

I would very much prefer to have more utility functions than this complex iterator. For example we already have utility function for getting window tabs:
https://github.com/mozilla/addon-sdk/blob/master/packages/api-utils/lib/tabs/utils.js#L23

I think what we need is:

  1. window/utils.getWindows to get all windows
  2. getBrowser(tab) -> tab.linkedBrowser
  3. getWindow(tab) -> getBrowser(tab).contentWindow
  4. getURI(tab) -> getBrowser(tab).currentURI.spec

Than in _applyOnExistingDocuments you will just do:

getWindows().
  reduce(function(tabs, window) { return tabs.concat(getTabs(window)); }, []).
  filter(function(tab) { return isMatching(getURI(tab), this.include)); }, this).
  forEach(function(tab) { this._onContent(getWindow(tab); }, this)
ochameau Collaborator
ochameau added a note May 16, 2012

I still kept this internal method. I can move it to tabs/utils but I can't use existing methods as-is.
getTabs for example is inefficient. The overuse of reduce and filter is very inefficient too.
So I can see multiple options here:

  • keep it as-is: an internal method in page-mod
  • move it as-is to tabs/utils
  • move it to tabs/utils and try to use some methods from this module. I would have to modify getTabs and avoid using reduce.

I'm extra carefull about performances here as this code is going to be executed on any new content document (once per installed addon using page-mod!), so that it is going to slow down web pages loading. We have to lower this effect as much as possible.

FYI, performances of reduce http://jsperf.com/eval-join-vs-reduce/3

Irakli Gozalishvili Collaborator
Gozala added a note May 16, 2012

I think this is sign of premature optimization at cost of code maintainability. According to that benchmark reduce took 0.00323918114 ms. In addition these benchmarks does not really apply to this case as since they compare simple array
iterations which is different from our case where we wrap xpcom simple enumerator with JS iterator wrapped in yet another JS iterator + access to different getters. Cost of reduce here is lost in cost of other more expensive operations and is completely irrelevant. In addition it's known that built-in map / filter / reduce is much slower than hand written one bug 743634 also visible here http://jsperf.com/eval-join-vs-reduce/4 and finally JS team doing some awsome work on function inlineing which will be able to compile such filter / map / reduce to a same thing as plain for loop so we better let them do optimizations. We can start doing targeted optimizations once that will be a bottleneck.

That all being said I'm ok with keeping it as an internal utility function for the time being.

Irakli Gozalishvili Collaborator
Gozala added a note May 16, 2012

I knew that operating on iterators was slower than regular array operations and your comment above irritated my curiosity so I tried following https://gist.github.com/2714933 as it turns out .filter(...).forEach(...) is indeed faster. That not to say I changed my mind, you still can go ahead with internal utility function & utility functions I suggested earlier can be added in a future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
packages/addon-kit/lib/page-mod.js
@@ -172,6 +181,23 @@ const PageMod = Loader.compose(EventEmitter, {
172 181
 
173 182
   _loadingWindows: [],
174 183
 
  184
+  _applyOnExistingDocuments: function _applyOnExistingDocuments() {
  185
+    // Returns true if the URL match one rule
  186
+    function isMatchingURL(uri, rules) {
  187
+      for each(let rule in rules) {
  188
+        if (RULES[rule].test(uri))
  189
+          return true;
  190
+      }
  191
+      return false;
  192
+    };
  193
+    for each (let tab in getAllTabs()) {
  194
+      if (isMatchingURL(tab.uri, this.include)) {
  195
+        // Fake a newly created document
  196
+        this._onContent(tab.content);
  197
+      }
  198
+    }
1
Irakli Gozalishvili Collaborator
Gozala added a note May 16, 2012

Again simple .filter(isMatchingURL).forEach(..) is easier to read and maintain + is not moz specific.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
packages/addon-kit/lib/page-mod.js
@@ -183,7 +209,12 @@ const PageMod = Loader.compose(EventEmitter, {
183 209
       this._loadingWindows.push(window);
184 210
     }
185 211
 
186  
-    if ('start' == this.contentScriptWhen) {
  212
+    // Immediatly evaluate content script if the document state is already
  213
+    // matching contentScriptWhen expectations
  214
+    let state = window.document.readyState;
  215
+    if ('start' == this.contentScriptWhen ||
  216
+        'complete' == state ||
  217
+        ('ready' == this.contentScriptWhen && state == 'interactive') ) {
2
Irakli Gozalishvili Collaborator
Gozala added a note May 16, 2012

We're stick to === convention unless there is a reason to prefer ==.

ochameau Collaborator
ochameau added a note May 28, 2012

No particular reason, just used to type == ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Irakli Gozalishvili
Collaborator
Gozala commented May 16, 2012

To summarize I'm ok with this change once naming issue is resolved

https://github.com/mozilla/addon-sdk/pull/404/files#r835814

Feel free to decide if you want to address other comments or ask for another review if you desire. Otherwise r+ by with target property renamed.

ochameau
Collaborator

@gozala, I followed the trend by using more code from tab/utils, but I had to simplify it a bit.
I don't think it was a good idea to support multiple tabbrowser, it may even lead to unexpected behavior if some addon adds new ones.

Otherwise, the naming issue is quite tough, no one came up with ideal name that makes sense without conflict.
From what I can summarize from https://etherpad.mozilla.org/new-page-mod-options there is two option in debate:
target which conflict with some common html meaning, or attachTo which may conflict with https://bugzilla.mozilla.org/show_bug.cgi?id=755963

Ideally we would use attachTo here and find something distinct for bug 755963. Otherwise I think that we can't forbid us from using such common word as target because of its usage in html.

In https://github.com/mozilla/addon-sdk/wiki/JEP-Content-scripts you tend to suggest this attachTo method to be defined on ContentScript class, not PageMod one. You named it spawn. So would it be ok to use PageMod.attachTo and ContentScript.attach/spawn/attachTo/execute/runOn... ?

Dave Townsend
Collaborator

attachTo wins

ochameau
Collaborator

Manually landed here: 876e9d7

ochameau ochameau closed this September 06, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
6  packages/addon-kit/docs/page-mod.md
Source Rendered
@@ -411,6 +411,12 @@ Creates a PageMod.
411 411
     option are loaded *after* those specified by the `contentStyleFile` option.
412 412
     Optional.
413 413
 
  414
+  @prop [attachTo] {array}
  415
+    Option to specify on which documents PageMod should be applied.
  416
+    For now, it only accepts one value: "existing". If `attachTo` contains
  417
+    "existing", the PageMod will be automatically applied on already opened
  418
+    tabs.
  419
+
414 420
   @prop [onAttach] {function}
415 421
 A function to call when the PageMod attaches content scripts to
416 422
 a matching page. The function will be called with one argument, a `worker`
53  packages/addon-kit/lib/page-mod.js
@@ -15,7 +15,10 @@ const { validateOptions : validate } = require('api-utils/api-utils');
15 15
 const { validationAttributes } = require('api-utils/content/loader');
16 16
 const { Cc, Ci } = require('chrome');
17 17
 const { merge } = require('api-utils/utils/object');
18  
-const { getTabForContentWindow } = require('api-utils/tabs/utils');
  18
+const { windowIterator } = require("window-utils");
  19
+const { isBrowser } = require('api-utils/window/utils');
  20
+const { getTabs, getTabContentWindow, getTabForContentWindow,
  21
+        getURI: getTabURI } = require("api-utils/tabs/utils");
19 22
 
20 23
 const styleSheetService = Cc["@mozilla.org/content/style-sheet-service;1"].
21 24
                             getService(Ci.nsIStyleSheetService);
@@ -82,6 +85,7 @@ function readURI(uri) {
82 85
 const PageMod = Loader.compose(EventEmitter, {
83 86
   on: EventEmitter.required,
84 87
   _listeners: EventEmitter.required,
  88
+  attachTo: [],
85 89
   contentScript: Loader.required,
86 90
   contentScriptFile: Loader.required,
87 91
   contentScriptWhen: Loader.required,
@@ -105,6 +109,8 @@ const PageMod = Loader.compose(EventEmitter, {
105 109
       this.on('attach', options.onAttach);
106 110
     if ('onError' in options)
107 111
       this.on('error', options.onError);
  112
+    if ('attachTo' in options)
  113
+      this.attachTo = options.attachTo;
108 114
 
109 115
     let include = options.include;
110 116
     let rules = this.include = Rules();
@@ -138,6 +144,11 @@ const PageMod = Loader.compose(EventEmitter, {
138 144
     pageModManager.add(this._public);
139 145
 
140 146
     this._loadingWindows = [];
  147
+
  148
+    // `_applyOnExistingDocuments` has to be called after `pageModManager.add()`
  149
+    // otherwise its calls to `_onContent` method won't do anything.
  150
+    if ('attachTo' in options && options.attachTo.indexOf('existing') !== -1)
  151
+      this._applyOnExistingDocuments();
141 152
   },
142 153
 
143 154
   destroy: function destroy() {
@@ -156,12 +167,38 @@ const PageMod = Loader.compose(EventEmitter, {
156 167
 
157 168
   _loadingWindows: [],
158 169
 
  170
+  _applyOnExistingDocuments: function _applyOnExistingDocuments() {
  171
+    let mod = this;
  172
+    // Returns true if the tab match one rule
  173
+    function isMatchingURI(uri) {
  174
+      // Use Array.some as `include` isn't a native array
  175
+      return Array.some(mod.include, function (rule) {
  176
+        return RULES[rule].test(uri);
  177
+      });
  178
+    }
  179
+    getAllTabs().
  180
+      filter(function (tab) {
  181
+        return isMatchingURI(getTabURI(tab));
  182
+      }).
  183
+      forEach(function (tab) {
  184
+        // Fake a newly created document
  185
+        mod._onContent(getTabContentWindow(tab));
  186
+      });
  187
+  },
  188
+
159 189
   _onContent: function _onContent(window) {
160 190
     // not registered yet
161 191
     if (!pageModManager.has(this))
162 192
       return;
163 193
 
164  
-    if ('start' == this.contentScriptWhen) {
  194
+    // Immediatly evaluate content script if the document state is already
  195
+    // matching contentScriptWhen expectations
  196
+    let state = window.document.readyState;
  197
+    if ('start' === this.contentScriptWhen ||
  198
+        // Is `load` event already dispatched?
  199
+        'complete' === state ||
  200
+        // Is DOMContentLoaded already dispatched and waiting for it?
  201
+        ('ready' === this.contentScriptWhen && state === 'interactive') ) {
165 202
       this._createWorker(window);
166 203
       return;
167 204
     }
@@ -298,3 +335,15 @@ const PageModManager = Registry.resolve({
298 335
   }
299 336
 });
300 337
 const pageModManager = PageModManager();
  338
+
  339
+// Returns all tabs on all currently opened windows
  340
+function getAllTabs() {
  341
+  let tabs = [];
  342
+  // Iterate over all chrome windows
  343
+  for (let window in windowIterator()) {
  344
+    if (!isBrowser(window))
  345
+      continue;
  346
+    tabs = tabs.concat(getTabs(window));
  347
+  }
  348
+  return tabs;
  349
+}
23  packages/addon-kit/tests/test-page-mod.js
@@ -350,6 +350,29 @@ exports.testRelatedTab = function(test) {
350 350
 
351 351
 };
352 352
 
  353
+exports.testWorksWithExistingTabs = function(test) {
  354
+  test.waitUntilDone();
  355
+
  356
+  let url = "data:text/html;charset=utf-8," + encodeURI("Test unique document");
  357
+  let { PageMod } = require("page-mod");
  358
+  tabs.open({
  359
+    url: url,
  360
+    onReady: function onReady(tab) {
  361
+      let pageMod = new PageMod({
  362
+        include: url,
  363
+        attachTo: ["existing"],
  364
+        onAttach: function(worker) {
  365
+          test.assertEqual(tab, worker.tab, "A worker has been created on this existing tab");
  366
+          pageMod.destroy();
  367
+          tab.close();
  368
+          test.done();
  369
+        }
  370
+      });
  371
+    }
  372
+  });
  373
+
  374
+};
  375
+
353 376
 exports['test tab worker on message'] = function(test) {
354 377
   test.waitUntilDone();
355 378
 
10  packages/api-utils/lib/tabs/observer.js
@@ -8,7 +8,7 @@
8 8
 const { EventEmitterTrait: EventEmitter } = require("../events");
9 9
 const { DOMEventAssembler } = require("../events/assembler");
10 10
 const { Trait } = require("../light-traits");
11  
-const { getActiveTab, getTabs, getTabContainers } = require("./utils");
  11
+const { getActiveTab, getTabs, getTabContainer } = require("./utils");
12 12
 const { browserWindowIterator } = require("../window-utils");
13 13
 const { isBrowser } = require('../window/utils');
14 14
 const { observer: windowObserver } = require("../windows/observer");
@@ -64,9 +64,7 @@ observer.on("select", onTabSelect);
64 64
 // containers to the observed list.
65 65
 function onWindowOpen(chromeWindow) {
66 66
   if (!isBrowser(chromeWindow)) return; // Ignore if it's not a browser window.
67  
-  getTabContainers(chromeWindow).forEach(function (container) {
68  
-    observer.observe(container);
69  
-  });
  67
+  observer.observe(getTabContainer(chromeWindow));
70 68
 }
71 69
 windowObserver.on("open", onWindowOpen);
72 70
 
@@ -78,9 +76,7 @@ function onWindowClose(chromeWindow) {
78 76
     observer._emit("deactivate", selectedTab);
79 77
     selectedTab = null;
80 78
   }
81  
-  getTabContainers(chromeWindow).forEach(function (container) {
82  
-    observer.ignore(container);
83  
-  });
  79
+  observer.ignore(getTabContainer(chromeWindow));
84 80
 }
85 81
 windowObserver.on("close", onWindowClose);
86 82
 
37  packages/api-utils/lib/tabs/utils.js
@@ -7,26 +7,18 @@
7 7
 
8 8
 const { Ci } = require("chrome");
9 9
 
10  
-function getTabContainer(tabBrowser) {
11  
-  return tabBrowser.tabContainer;
  10
+function getTabBrowser(window) {
  11
+  return window.gBrowser;
12 12
 }
13  
-exports.getTabContainer = getTabContainer;
14  
-
15  
-function getTabBrowsers(window) {
16  
-  return Array.slice(window.document.getElementsByTagName("tabbrowser"));
17  
-}
18  
-exports.getTabBrowsers = getTabBrowsers;
  13
+exports.getTabBrowser = getTabBrowser;
19 14
 
20  
-function getTabContainers(window) {
21  
-  return getTabBrowsers(window).map(getTabContainer);
  15
+function getTabContainer(window) {
  16
+  return getTabBrowser(window).tabContainer;
22 17
 }
23  
-exports.getTabContainers = getTabContainers;
  18
+exports.getTabContainer = getTabContainer;
24 19
 
25 20
 function getTabs(window) {
26  
-  return getTabContainers(window).reduce(function (tabs, container) {
27  
-    tabs.push.apply(tabs, container.children);
28  
-    return tabs;
29  
-  }, []);
  21
+  return Array.slice(getTabContainer(window).children);
30 22
 }
31 23
 exports.getTabs = getTabs;
32 24
 
@@ -51,12 +43,12 @@ function isTabOpen(tab) {
51 43
 exports.isTabOpen = isTabOpen;
52 44
 
53 45
 function closeTab(tab) {
54  
-  return getGBrowserForTab(tab).removeTab(tab);
  46
+  return getTabBrowserForTab(tab).removeTab(tab);
55 47
 }
56 48
 exports.closeTab = closeTab;
57 49
 
58 50
 function activateTab(tab) {
59  
-  getGBrowserForTab(tab).selectedTab = tab;
  51
+  getTabBrowserForTab(tab).selectedTab = tab;
60 52
 }
61 53
 exports.activateTab = activateTab;
62 54
 
@@ -65,13 +57,13 @@ function getURI(tab) {
65 57
 }
66 58
 exports.getURI = getURI;
67 59
 
68  
-function getGBrowserForTab(tab) {
  60
+function getTabBrowserForTab(tab) {
69 61
   return getOwnerWindow(tab).gBrowser;
70 62
 }
71  
-exports.getGBrowserForTab = getGBrowserForTab;
  63
+exports.getTabBrowserForTab = getTabBrowserForTab;
72 64
 
73 65
 function getBrowserForTab(tab) {
74  
-  return getGBrowserForTab(tab).getBrowserForTab(tab);
  66
+  return tab.linkedBrowser;
75 67
 }
76 68
 exports.getBrowserForTab = getBrowserForTab;
77 69
 
@@ -80,6 +72,11 @@ function getTabTitle(tab) {
80 72
 }
81 73
 exports.getTabTitle = getTabTitle;
82 74
 
  75
+function getTabContentWindow(tab) {
  76
+  return getBrowserForTab(tab).contentWindow;
  77
+}
  78
+exports.getTabContentWindow = getTabContentWindow;
  79
+
83 80
 function getTabForContentWindow(window) {
84 81
   // Retrieve the topmost frame container. It can be either <xul:browser>,
85 82
   // <xul:iframe/> or <html:iframe/>. But in our case, it should be xul:browser.
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.