Skip to content
Closed
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
1c17668
Add host permission prompts for Safari
adomasven Jul 28, 2026
71b87d9
Restore intro paragraph in Chromium site-access prompt
dstillman Jul 31, 2026
70972e9
Prevent overlapping permission modals on the Safari preferences pane
dstillman Jul 31, 2026
6f9264e
Show a single Safari Settings instructions paragraph in combined prompts
dstillman Jul 31, 2026
dbff69d
Rework Safari localhost prompting in the save flow
dstillman Jul 31, 2026
3044593
Keep the Z toolbar icon on Safari until translator detection runs
dstillman Jul 31, 2026
50b2798
Show a neutral toolbar status on excluded pages without localhost access
dstillman Jul 31, 2026
ddbb46f
Expose HostPermissions.hasPermission() to injected pages
dstillman Aug 3, 2026
ade8a8e
Update Google Docs submodule
dstillman Aug 10, 2026
585d500
Reword the localhost permission explanation
dstillman Aug 3, 2026
34b0a5c
Show the localhost permission explanation for blocked integration req…
dstillman Aug 3, 2026
6a5441a
Stop pointing to Safari's permission dialog once a request has been b…
dstillman Aug 5, 2026
88e9d38
Recommend all-websites access in the pre-request permission explanation
dstillman Aug 6, 2026
5a63dc9
Recommend all-websites access once per browser session instead of onc…
dstillman Aug 6, 2026
af5ad1a
Reload pages where Safari re-injects content scripts after a permissi…
dstillman Aug 7, 2026
42d7373
Inject content scripts on demand for user actions on Safari
dstillman Aug 7, 2026
7f68174
Use plural Safari Settings instructions for multiple domains
dstillman Aug 7, 2026
4db8be5
Don't cache script-injection state when a page already has scripts
dstillman Aug 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ var injectIncludeFirefox = ['browser-polyfill.js'].concat(
['api.js'],
injectIncludeLast);

var injectIncludeSafari = ['browser-polyfill.js'].concat(
var injectIncludeSafari = ['reinjectGuard.js', 'browser-polyfill.js'].concat(
injectInclude,
['api.js'],
['frameMessaging.js'],
Expand Down Expand Up @@ -158,6 +158,7 @@ if (!argv.p) {
injectIncludeManifestV3.push('test/testInject.js');
}
var backgroundIncludeBrowserExt = ['browser-polyfill.js'].concat(backgroundInclude, [
'hostPermissions.js',
'webRequestIntercept.js',
'contentTypeHandler.js',
'saveWithoutProgressWindow.js',
Expand Down Expand Up @@ -334,6 +335,17 @@ function processFile() {
backgroundScripts.map((s) => `"${s}"`).join(',\n\t\t\t'))
.replace("/*INJECT SCRIPTS*/",
injectScripts.map((s) => `"${s}"`).join(',\n\t\t\t'))
if (basename == 'manifest.json' && browser == 'safari') {
// Safari runs content scripts only on sites where the user has granted
// access, so the pre-detection gray webpage icon can show indefinitely --
// default to the Z instead
let manifest = JSON.parse(contents);
manifest.browser_action.default_icon = {
16: "images/zotero-z-16px.png",
32: "images/zotero-z-32px.png"
};
contents = JSON.stringify(manifest, null, '\t');
}
}

contents = contents
Expand Down
78 changes: 17 additions & 61 deletions src/browserExt/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,14 @@ Zotero.Connector_Browser = new function() {
var isPDF = tabInfo.isPDF;
var translators = tabInfo.translators;

// Safari runs content scripts only on sites where the user has granted access, and
// clicking the button on other sites enables the Connector on the site instead of
// saving. Until a content script reports detection results, keep the default Z icon
// rather than showing a save action that hasn't been determined.
if (Zotero.isSafari && !translators && !isPDF) {
return;
}

// Show the save menu if we have more than one save option to show, which is true in all cases
// other than for PDFs with no translator
var showSaveMenu = (translators && translators.length) || !isPDF;
Expand Down Expand Up @@ -761,7 +769,7 @@ Zotero.Connector_Browser = new function() {
// it's not treated like we do it within a gesture
await browser.permissions.request({permissions: ['clipboardWrite']});
}
const shouldContinue = await _checkPermissions(tab);
const shouldContinue = await Zotero.HostPermissions.checkChromiumActionPermissions(tab);
if (!shouldContinue) {
return;
}
Expand Down Expand Up @@ -798,7 +806,13 @@ Zotero.Connector_Browser = new function() {
if (isOnline) {
icon = "images/zotero-new-z-16px.png";
title = "Zotero is Online";
} else {
}
else if (isOnline === null) {
// Zotero's status is unknown without localhost access, so don't claim it's offline
icon = "images/zotero-new-z-16px.png";
title = "Zotero Connector";
}
else {
icon = "images/zotero-z-16px-offline.png";
title = "Zotero is Offline";
}
Expand Down Expand Up @@ -1038,66 +1052,8 @@ Zotero.Connector_Browser = new function() {
return false;
}

/**
* Check if we have permission to run on all sites.
* Prompts the user if permissions are insufficient.
* @param {Object} tab - The current tab object
* @returns {Promise<boolean>} - Returns false if the action should not proceed
*/
async function _checkPermissions(tab) {
// Firefox doesn't have per-site permissions in MV2.
if (Zotero.isFirefox) {
return true;
}

try {
const hasPermissions = await browser.permissions.contains({
origins: ["https://*/*"]
});

if (hasPermissions) {
return true;
}

const messageIntro = Zotero.getString("permissions_siteAccess_message_intro");
let promptProps = {
title: Zotero.getString("permissions_siteAccess_title"),
button1Text: Zotero.getString("permissions_siteAccess_openPreferences"),
button2Text: Zotero.getString("general_cancel"),
button3Text: Zotero.getString("general_continueAnyway"),
message: messageIntro + Zotero.getString("permissions_siteAccess_message")
};
if (Zotero.isSafari) {
promptProps = {
title: Zotero.getString("permissions_siteAccess_title"),
button1Text: Zotero.getString("general_cancel"),
button2Text: "",
button3Text: Zotero.getString("general_continueAnyway"),
message: messageIntro + Zotero.getString(
"permissions_siteAccess_message_safari",
Zotero.getString('appConnector', ZOTERO_CONFIG.CLIENT_NAME)
)
};
}

const result = await Zotero.Messaging.sendMessage('confirm', promptProps, tab);

if (result) {
if (!Zotero.isSafari && result.button === 1) {
browser.tabs.create({
url: `about:extensions/?id=${browser.runtime.id}`
});
}
return result.button === 3;
}
} catch (e) {
Zotero.debug('Error checking permissions: ' + e.message);
return true;
}
}

async function _browserAction(tab) {
const shouldContinue = await _checkPermissions(tab);
const shouldContinue = await Zotero.HostPermissions.checkChromiumActionPermissions(tab);
if (!shouldContinue) {
return;
}
Expand Down
Loading