Google Chrome extensions: popup windows handling - google-chrome-extension

My extension should open a popup window (a normal popup window, not default extensions popup) with a following conditions:
only one window could be opened in the moment,
I want to save window size and position in localStorage, and restore them on next open.
How to do so?
I tried:
chrome.browserAction.onClicked.addListener(function() {
// 1. In this case, variable win is a window object. But it's empty. There
// is no properties/methods to operate with.
var win = window.open('http://example.com','windowname');
chrome.windows.create({
'url': 'http://example.com',
'type': 'popup'
}, function(win) {
// 2. In this case, variable win has some properties, but they are static
// and won't change on window resize/close.
});
});
Any ideas?

I think, you can do this with window.open().
1-only one window could be opened in the moment,
If you give same name, window.open(),
if there aren't any exist windows with same name, It will create a new one
else if there is a openned window with the same name, it will just refresh it.
2- I want to save window size and position in localStorage, and restore them on next open.
Yes, you can do this by attaching window.onresize event,
Here is a basic example,
chrome.browserAction.onClicked.addListener(function() {
var wOpt = getWindowDefaultOptions(); // options from localStorage
var win = window.open("http://www.example.com", "myscriptpopup", "width="+wOpt.width+", height="+wOpt.height+", top="+wOpt.top+", left="+wOpt.left);
win.onresize = function() {
var newOptions = {};
newOptions.width = this.innerWidth;
newOptions.height = this.innerHeight;
newOptions.top = this.screenY;
newOptions.left = this.screenX;
setWindowDefaultOptions(newOptions); //save Options to localStorage
}
});
//Default Options For Window
var winOptions = {
"width" : "200",
"height" : "100",
"top" : "50",
"left" : "50"
};

Related

Chrome extension for selected text which matches a specific form [duplicate]

I am trying to create entries on the Chrome context menu based on what is selected.
I found several questions about this on Stackoverflow, and for all of them the answer is: use a content script with a "mousedown" listener that looks at the current selection and creates the Context Menu.
I implemented this, but it does not always work. Sometimes all the log messages say that the context menu was modified as I wanted, but the context menu that appears is not updated.
Based on this I suspected it was a race condition: sometimes chrome starts rendering the context menu before the code ran completely.
I tried adding a eventListener to "contextmenu" and "mouseup". The later triggers when the user selects the text with the mouse, so it changes the contextmenu much before it appears (even seconds). Even with this technique, I still see the same error happening!
This happens very often in Chrome 22.0.1229.94 (Mac), occasionally in Chromium 20.0.1132.47 (linux) and it did not happen in 2 minutes trying on Windows (Chrome 22.0.1229.94).
What is happening exactly? How can I fix that? Is there any other workaround?
Here is a simplified version of my code (not so simple because I am keeping the log messages):
manifest.json:
{
"name": "Test",
"version": "0.1",
"permissions": ["contextMenus"],
"content_scripts": [{
"matches": ["http://*/*", "https://*/*"],
"js": ["content_script.js"]
}],
"background": {
"scripts": ["background.js"]
},
"manifest_version": 2
}
content_script.js
function loadContextMenu() {
var selection = window.getSelection().toString().trim();
chrome.extension.sendMessage({request: 'loadContextMenu', selection: selection}, function (response) {
console.log('sendMessage callback');
});
}
document.addEventListener('mousedown', function(event){
if (event.button == 2) {
loadContextMenu();
}
}, true);
background.js
function SelectionType(str) {
if (str.match("^[0-9]+$"))
return "number";
else if (str.match("^[a-z]+$"))
return "lowercase string";
else
return "other";
}
chrome.extension.onMessage.addListener(function(msg, sender, sendResponse) {
console.log("msg.request = " + msg.request);
if (msg.request == "loadContextMenu") {
var type = SelectionType(msg.selection);
console.log("selection = " + msg.selection + ", type = " + type);
if (type == "number" || type == "lowercase string") {
console.log("Creating context menu with title = " + type);
chrome.contextMenus.removeAll(function() {
console.log("contextMenus.removeAll callback");
chrome.contextMenus.create(
{"title": type,
"contexts": ["selection"],
"onclick": function(info, tab) {alert(1);}},
function() {
console.log("ContextMenu.create callback! Error? " + chrome.extension.lastError);});
});
} else {
console.log("Removing context menu")
chrome.contextMenus.removeAll(function() {
console.log("contextMenus.removeAll callback");
});
}
console.log("handling message 'loadContextMenu' done.");
}
sendResponse({});
});
The contextMenus API is used to define context menu entries. It does not need to be called right before a context menu is opened. So, instead of creating the entries on the contextmenu event, use the selectionchange event to continuously update the contextmenu entry.
I will show a simple example which just displays the selected text in the context menu entry, to show that the entries are synchronized well.
Use this content script:
document.addEventListener('selectionchange', function() {
var selection = window.getSelection().toString().trim();
chrome.runtime.sendMessage({
request: 'updateContextMenu',
selection: selection
});
});
At the background, we're going to create the contextmenu entry only once. After that, we update the contextmenu item (using the ID which we get from chrome.contextMenus.create).
When the selection is empty, we remove the context menu entry if needed.
// ID to manage the context menu entry
var cmid;
var cm_clickHandler = function(clickData, tab) {
alert('Selected ' + clickData.selectionText + ' in ' + tab.url);
};
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
if (msg.request === 'updateContextMenu') {
var type = msg.selection;
if (type == '') {
// Remove the context menu entry
if (cmid != null) {
chrome.contextMenus.remove(cmid);
cmid = null; // Invalidate entry now to avoid race conditions
} // else: No contextmenu ID, so nothing to remove
} else { // Add/update context menu entry
var options = {
title: type,
contexts: ['selection'],
onclick: cm_clickHandler
};
if (cmid != null) {
chrome.contextMenus.update(cmid, options);
} else {
// Create new menu, and remember the ID
cmid = chrome.contextMenus.create(options);
}
}
}
});
To keep this example simple, I assumed that there's only one context menu entry. If you want to support more entries, create an array or hash to store the IDs.
Tips
Optimization - To reduce the number of chrome.contextMenus API calls, cache the relevant values of the parameters. Then, use a simple === comparison to check whether the contextMenu item need to be created/updated.
Debugging - All chrome.contextMenus methods are asynchronous. To debug your code, pass a callback function to the .create, .remove or .update methods.
MDN doc for menus.create(), 'title' param
You can use "%s" in the string. If you do this in a menu item, and some text is selected in the page when the menu is shown, then the selected text will be interpolated into the title.
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/menus/create
Thus
browser.contextMenus.create({
id: 'menu-search',
title: "Search '%s'", // selected text as %s
contexts: ['selection'], // show only if selection exist
})

dialog.showMessageBoxSync(null, options) getting hidden

I have an Electron app. If I use dialog.showmessageBoxSync normally it has to wait for user input. The options are: close, cancel or ok.
It is working fine but if I click outside of the dialog box (anywhere inside my app) then this message box hidden. I'm unable to click on any option.
How can I make the message box stay focused until the user chooses a button to click or closes the dialog box? The user should be forced to respond to the message box before continuing to work in the rest of the app.
dialog.showMessageBoxSync({
type: "info",
buttons: ["Ok,", "Cancel"],
defaultId: 0,
title: "",
message:""
cancelId: 1,
})
I'd suggest passing in a parent window
From the docs
The browserWindow argument allows the dialog to attach itself to a
parent window, making it modal.
const iconPath = upath.toUnix(upath.join(__dirname, "app", "assets", "icon.png"));
const dialogIcon = nativeImage.createFromPath(iconPath);
var options = {
type: 'question',
buttons: ['&Yes', '&No'],
title: 'Confirm Quit',
icon: dialogIcon,
normalizeAccessKeys: true,
message: 'Do you really want to close the application?'
};
const win = BrowserWindow.getFocusedWindow();
dialog.showMessageBox(win, options)
.then((choice) => {
if (choice.response === 0) {
quitApplication();
}
}).catch(err => {
console.log('ERROR', err);
});

How to put selection text in chrome.contextMenus?

How can I add selection text in context.Menus?
I want to create a Chrome extension which will work similarly to the right-click search function in Google Chrome (i.e. right click on selected text -> "Search 'selection text')
I made a preview
I assume this is something with chrome.contextMenus.update but i don't know how to make it work
background.js:
chrome.runtime.onInstalled.addListener(function () {
var context = "selection";
var title = "Search";
var id = chrome.contextMenus.create({
"title": title,
"contexts": [context],
"id": "context" + context
});
});
// add click event
chrome.contextMenus.onClicked.addListener(onClickHandler);
// The onClicked callback function.
function onClickHandler(info, tab) {
var sText = info.selectionText;
var url = "https://www.google.com/search?source=hp&q=" + encodeURIComponent(sText);
window.open(url, '_blank');
};

Chrome Extension context menu, differentiate image and link event

In my chrome extension I'm adding two context items "Get link" and "Get Image". The main difference being when setting them both up they have the "context" of link and image respectively. But when right clicking on an image that is acting as a link you get the option of both:
when either of those are clicked the data that comes into the listener seems to be identical, I need to be able to differentiate the two to know if the context is that of an image or a link to handle them differently. Here is my code:
chrome.runtime.onInstalled.addListener(function() {
var context = "image";
var title = "Copy Image";
var id = chrome.contextMenus.create({"title": title, "contexts":[context],
"id": "context" + context});
});
chrome.runtime.onInstalled.addListener(function() {
var context = "link";
var title = "Copy link";
var id = chrome.contextMenus.create({"title": title, "contexts":[context],
"id": "context" + context});
});
chrome.contextMenus.onClicked.addListener(onClickHandler);
function onClickHandler(info, tab) {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
chrome.tabs.sendMessage(tabs[0].id, {action: "imageAdded", subject: info.srcUrl}, function(response) {
});
If you want know which menu item was clicked, you can get the id value of the clicked context menu item in the menuItemId property of the object passed into the onClicked handler:
function onClickHandler(info, tab) {
console.log(info.menuItemId);
//...
}
Take a look at Parameter of onClicked callback, you could differentiate the image/link via mediaType
One of 'image', 'video', or 'audio' if the context menu was activated on one of these types of elements.

When I close a window and then open a different one, the selected tab is recreated by Chrome

I am writing a Chrome extension that saves/restores your browsers window state - So, I save the state of a given window:
var properties = [ "top",
"left",
"width",
"height",
"incognito",
"focused",
"type"
];
var json = {};
var cache = chrome_window_object;
// copy only the keys we care about:
_.each(properties,function(key,value) {
json[key] = cache[key];
});
// then copy the URLs of the tabs, if they exist:
if(cache.tabs) {
json.url = [];
_.each(cache.tabs,function(tab) {
json.url.push(tab.url);
});
}
return json;
At some point in the future, I remove all windows:
closeAllWindows: function(done_callback) {
function got_all(windows) {
var index = 0;
// use a closure to only close one window at a time:
function close_next() {
if(windows.length <= index) return;
var window = windows[index++];
chrome.windows.remove(window,close_next);
}
// start closing windows:
close_next();
}
chrome.windows.getAll(got_all);
}
and then I restore the window using:
chrome.windows.create(json_from_before);
The window that is created has an extra tab in it, whatever was in the window that I just closed... I am completely floored, and I assume the problem is something that I am doing in the code that I haven't posted (it's a big extension). I've spent a few hours checking code line by line and making sure I'm not explicitly asking for this tab to be created. So - has anybody seen anything like this before?

Resources