Using find bar routines programmatically causes find bar to appear - firefox

Update: It appears that the fix for bug 967982 is what's causing this. It would still be great to know either (a) how to work around this or (b) if there's an alternative way to find text in a given page.
One of my Firefox extensions programmatically uses the browser's find bar to locate text on a web page. In recent versions of Firefox, executing my add-ons code causes the find bar to appear when a search term is not found, something it didn't do previously. My routine looks like this:
// Reuse the find bar mechanisms in Firefox (we get a bunch of stuff for free that way)
FindInPage: function(term, e) {
var findBar = document.defaultView.gFindBar;
var shiftKey = e.shiftKey;
var findObj;
var cachedFindTerm;
if ("_find" in findBar) {
findObj = {
find: function(t) {
findBar._find(findBar._findField.value = t);
},
findNext: function() {
findBar._findAgain(false);
},
findPrevious: function() {
findBar._findAgain(true);
}
};
cachedFindTerm = gFindBar._findField.value;
} else {
findObj = findBar;
cachedFindTerm = getBrowser().findString;
}
if (cachedFindTerm == term) {
if(shiftKey)
findObj.findPrevious();
else
findObj.findNext();
} else {
findObj.find(term);
if(shiftKey)
findObj.findPrevious();
}
},
Is there some way I can prevent the find bar from appearing when this block gets executed? I'm aware that using private functions like this is not a best practice, but is there a better way to find a given word in a web page? Some sort of search API available to add-on authors that I'm not aware of?

Related

How to implement message passing callbacks in an all-in-one (Edge/Firefox/Chrome) browser extension's content script?

Development Environment OS: Windows 7 Enterprise LTS
Browser compatibility minimum requirements: Should support all Edge, Firefox, Chrome browsers, as of 2018.
Current ongoing issue: Unable to run VM on dev workstation; Cannot run Windows 10 VMs to debug Microsoft Edge extensions.
To explain:
An "all-in-one browser extension" refers to a browser extension code that uses the same code with minor differences to work on various WebExtensions / Chrome Extensions supported browsers. At bare minimum, the same codebase should work and run on Edge, Firefox, and Chrome with very minor changes.
Callbacks on the content scripts for Edge/Firefox/Chrome extensions are handled differently.
For unknown reasons, I cannot run VM on my workstation machine. When VM is running, VM client is black. This is a localized issue on my end that I cannot resolve, so I'm forced to find a different solution/alternative.
How are they handled differently on the content scripts:
Edge: browser.runtime.sendMessage uses callbacks, and returns undefined.
Firefox: browser.runtime.sendMessage uses Promises, and returns a Promise.
Chrome: chrome.runtime.sendMessage uses callbacks, and returns undefined.
According to various references:
Firefox / Chrome / MS Edge extensions using chrome.* or browser.*
https://www.smashingmagazine.com/2017/04/browser-extension-edge-chrome-firefox-opera-brave-vivaldi/
On the content scripts, you can declare the following JavaScript snippet at the top in order to create a global variable that can be referenced everywhere else:
//Global "browser" namespace definition.
window.browser = (function() {
return window.msBrowser || window.browser || window.chrome;
})();
Unfortunately, because of the issue I'm experiencing (VM not running), I cannot tell if window.msBrowser is still being used. And this solution is not helpful for me when handling message callbacks when using namespace.runtime.sendMessage.
With all that said, my main question is: How to write a message passing function that can handle callbacks properly?
Currently, I'm using the following code:
function sendGlobalMessage(messageRequest, callback) {
if (chrome && window.openDatabase) {
//This is Chrome browser
chrome.runtime.sendMessage(messageRequest, callback);
}
else if (browser) {
try {
//Edge will error out because of a quirk in Edge IndexedDB implementation.
//See https://gist.github.com/nolanlawson/a841ee23436410f37168
let db = window.indexedDB.open("edge", (Math.pow(2, 30) + 1));
db.onerror = function(e) {
throw new Error("edge is found");
};
db.onsuccess = function(e) {
//This is Firefox browser.
browser.runtime.sendMessage(messageRequest).then(callback);
};
}
catch (e) {
//This is Edge browser
browser.runtime.sendMessage(messageRequest, callback);
}
}
}
I truly felt this is a hacky solution, because the code is based off of browser platform exclusive quirks in order to separate chrome.runtime.sendMessage and browser.runtime.sendMessage API calls, so as to handle callbacks in their respective platforms. I really wanted to change this.
So I'm asking what better ways are there, out there, that is useful to detect the different platforms, and handle message passing callbacks properly at the same time?
Thanks in advance.
I believed I solved it.
EDIT: The FINAL final version (updated and more stable, less message passing):
//Global "browser" namespace definition, defined as "namespace". Can be renamed to anything else.
window.namespace = (function() {
return window.browser || window.chrome;
})();
function sendGlobalResponse(message, callback){
if (window.namespace === window.chrome) {
//Chrome
window.namespace.runtime.sendMessage(message, callback);
}
else if (window.namespace === window.browser) {
//Using instanceof to check for object type, and use the returned evaluation as a truthy value.
let supportPromises = false;
try {
supportPromises = window.namespace.runtime.getPlatformInfo() instanceof Promise;
}
catch(e) { }
if (supportPromises){
//Firefox
window.namespace.runtime.sendMessage(message).then(callback);
}
else {
//Edge
window.namespace.runtime.sendMessage(message, callback);
}
}
}
(Original Post):
The final version (Now obsoleted):
//Global "browser" namespace definition.
window.namespace = (function() {
return window.browser || window.chrome;
})();
function sendGlobalResponse(message, callback){
if (window.namespace === window.chrome) {
//Chrome
window.namespace.runtime.sendMessage(message, callback);
}
else if (window.namespace === window.browser) {
let returnValue = window.namespace.runtime.sendMessage({});
if (typeof returnValue === "undefined"){
//Edge
window.namespace.runtime.sendMessage(message, callback);
}
else {
//Firefox
window.namespace.runtime.sendMessage(message).then(callback);
}
}
}
In the second if statement, by checking to see if the return value of a window.browser.runtime.sendMessage is a Promise or undefined, we can detect if the platform is Firefox or Edge.
I think this is the only solution to handle message passing callbacks/message responses on the content scripts.
I really couldn't think of a better solution than this. So I'll be using this from now on.
But if anyone else knows a better way, a way where you don't need to send out 1 extra dummy message for Firefox and Edge per function call, that would be great!
It sucks that anything inside the content script is not persistent, and even if you store information about what platform the code is being run on, you still have to fetch the information from the background script before filtering out which runtime.sendMessage function to call on, so it doesn't really save much time.

How do you get any useful call stack information when debugging react native?

When I try to debug react native, it usually looks like this.
at ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (ReactCompositeComponent.js:785)
at ReactCompositeComponentWrapper._renderValidatedComponent (ReactCompositeComponent.js:811)
at ReactCompositeComponentWrapper._updateRenderedComponent (ReactCompositeComponent.js:735)
at ReactCompositeComponentWrapper._performComponentUpdate (ReactCompositeComponent.js:715)
at ReactCompositeComponentWrapper.updateComponent (ReactCompositeComponent.js:634)
at ReactCompositeComponentWrapper.receiveComponent (ReactCompositeComponent.js:534)
at Object.receiveComponent (ReactReconciler.js:131)
at ReactCompositeComponentWrapper._updateRenderedComponent (ReactCompositeComponent.js:737)
There are no useful information and if I use debugger command to see what initiated the 50-depth call stack, then it almost always comes down to the onmessage method in `debuggerWorker.js', and it's almost zero useful.
How do you really debug your react native app?
onmessage = function(message) {
var object = message.data;
var sendReply = function(result) {
postMessage({replyID: object.id, result: result});
};
var handler = messageHandlers[object.method];
if (handler) {
// Special cased handlers
handler(object, sendReply);
} else {
// Other methods get called on the bridge
var returnValue = [[], [], [], 0];
try {
if (typeof __fbBatchedBridge === 'object') {
returnValue = __fbBatchedBridge[object.method].apply(null, object.arguments);
}
} finally {
sendReply(JSON.stringify(returnValue));
}
}
};
I tend to use the stack traces to figure out the general area where the error occurred and then use the web debugger to actually find out what when wrong.
https://facebook.github.io/react-native/docs/debugging.html#chrome-developer-tools
Its actually pretty nice, you can open all your js files in the sources tab of the chrome dev tools and then put debug points in and just step through your file and view your variables.

Is it possible to migrate preferences from a Firefox XUL addon to an SDK addon?

I'm down to the (hopefully) last hurdle in the process of migrating our extension from XUL to Firefox SDK, but I've got one last sticking point:
Preferences.
There are a number of preferences set that simply MUST be migrated when the SDK version of the addon is installed over the top of the XUL addon. These preferences are not exposed to the end user for various reasons. The preference namespacing between the two architectures are completely different. For example -
A "version_number" preference in XUL is named arbitrarily by the developer, and appears as such in about:config :
my.preference.name
However, in SDK, they are scoped to the extension in question:
extensions.[extension ID].my.preference.name
Can preferences from XUL addons be migrated for re-use inside SDK addons? If so, how?
While it didn't seem possible to read from preferences outside the SDK addon's namespace, it WAS possible to write into the EXPECTED namespace in the old XUL extension. The solution we came up with was to publish a small, final point release of the old XUL addon with a small bit of extra logic responsible for carrying out this migration before we publish the new SDK version to AMO.
Here's a pseudocode representation of our approach:
ContentScript.js
function initNewFFInstallation() {
...
if (checkIsUpgrade()) {
var keys = getPrefKeys()
migratePrefs(keys);
}
}
Utils.js - acts as a bridge to expose Overlay functionality to the content script
Util.prototype.getPrefsBranch = function() {
return Components.classes["#mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefService).
getBranch("my.prefs.ns.");
}
Util.prototype.getV2PrefsBranch = function() {
return Components.classes["#mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefService).
getBranch("extensions.[SDK_ADDON_ID].");
}
Util.prototype.setBoolPref = function(key, val) {
this.getPrefsBranch().setBoolPref(key, val);
this.getPrefsBranch().setBoolPref(key, val);
}
Util.prototype.setCharPref = function(key, val) {
this.getPrefsBranch().setCharPref (key, val);
this.getV2PrefsBranch().setCharPref (key, val);
}
//copies all the preferences over
Util.prototype.migratePrefs = function(boolPrefKeys, charPrefKeys) {
boolPrefKeys.each(function (key) {
this.getV2PrefsBranch().setBoolPref(key, this.getPrefsBranch().getBoolPref(key));
});
charPrefKeys.forEach(function (key) {
this.getV2PrefsBranch().setCharPref(key, this.getPrefsBranch().getCharPref(key));
});
}
Then in our scriptcompiler.js, which actually injects the scripts onto the page, the util methods are hooked onto the SafeWindow object.
injectScript: function(script, unsafeContentWin) {
var safeWin=new XPCNativeWrapper(unsafeContentWin);
var sandbox=new Components.utils.Sandbox(safeWin, {"sandboxPrototype":safeWin});
sandbox.window=safeWin;
sandbox.document=sandbox.window.document;
sandbox.unsafeWindow=unsafeContentWin;
var util = new Util();
//...other APIs
sandbox.migratePreferences=app_gmCompiler.hitch(util , "migratePreferences");
try {
Components.utils.evalInSandbox("(function(){"+script+"})()", sandbox);
} catch (e) {
}
}

Observe for highlight?

Is it possible to use nsISelectionController to watch when a a highlight/selection is made?
I know that there are different selection scopes. I want to watch when a user makes a selection in the default scope seen at MXR - nsISelectionController Constants.
Kind of like an addEventListener on select change but on the text nodes of the document.
Thanks
I found a solution but it doesn't use nsIController as #Neil had recommended in a SO topic HERE to look at viewSource.js.
Im still interested in a nsIController solution if possible, im trying to understand that sucker it confuses me.
So this is how you observer for a selection:
var mylis = {
timeout: 0,
notifySelectionChanged: function(doc, sel, reason)
{
if (!this.timeout) {
this.timeout = setTimeout(function() {
console.log('notifySelectionChanged','doc=',doc,'sel=',sel,'reason=',reason);
mylis.timeout = 0;
}, 1000);
}
}
}
gBrowser.contentWindow.getSelection().QueryInterface(Ci.nsISelectionPrivate).addSelectionListener(mylis);
//gBrowser.contentWindow.getSelection().QueryInterface(Ci.nsISelectionPrivate).removeSelectionListener(mylis);
the timeout is important because otherwise it will slow down the browser thread. you can see as you highlight its all gimicky. viewSource.js used 100ms so I would reocmmend that.
MXR - viewSource.js

How to efficiently remove first element from a selection?

I have a page that displays some data using d3.js. Due to the heavy processing load, when the page load it freezes the browser for a few seconds.
I have determined that this "browser locking" behavior is due mostly to a line of the form:
selection.attr('d', linefn);
...where selection contains around 10K items.
I would like to replace this line with something like
function process_rest () {
if (selection.size() > 0) {
var next_item = first(selection); // function first() is hypothetical!
next_item.attr('d', linefn);
selection = rest(selection); // function rest() is hypothetical!
setTimeout(process_rest, 100);
return;
}
finish_up();
}
setTimeout(process_rest, 100);
I'm looking for an efficient way to implement either first and rest. My very naive guess would be something like:
function first(selection) {
return d3.select(selection[0][0]);
}
function rest(selection) {
selection[0] = selection[0].slice(1);
return selection;
}
...but, AFAIK, this is going "behind the API", or at least feels like it. Is there an "official" (i.e. documented) way to achieve the same result?
EDIT: deleted the shift variant (it's safer not to update selection until after the processing of the first element has been successfully completed).
You can simply use .each():
selection.each(function(d, i) {
setTimeout(function() { d3.select(this).attr("d", linefn); }, i * 100);
});

Resources