- Open Firefox
- Navigate to
about:debugging - Click "This Firefox" in the sidebar
- Click "Load Temporary Add-on..."
- Select your
manifest.jsonfile
- Extension loads immediately
- Open new tab to test dashboard
- Check browser console (F12) for errors
- Test all functionality
# Install web-ext for better development experience
npm install -g web-ext
# Run extension with auto-reload
web-ext run --source-dir=./
# Lint extension for Firefox compatibility
web-ext lint --source-dir=./- Background script: Click "Inspect" next to extension
- Content scripts: Use web inspector on the page
- Console errors: Check both background and content script consoles
- Open Chrome/Edge
- Navigate to
chrome://extensions/oredge://extensions/ - Enable "Developer mode" (toggle in top-right)
- Click "Load unpacked"
- Select your extension directory
- Extension appears immediately in extensions list
- Open new tab to test dashboard
- Check for permission warnings
- Verify all features work
- Background script: Click "Inspect views: background page"
- Content scripts: Right-click page → "Inspect"
- Errors: Check "Errors" section in extensions page
- Reload: Use "Reload" button after code changes
- Xcode 12+ required
- macOS 10.15+ for development
- Safari 14+ for testing
- Manifest Version: Safari uses manifest v2, not v3
- Background Scripts: Different persistence model
- Permissions: More restrictive permission model
- Bundle Identifier: Required for Safari extensions
- App Store: Must go through App Store for distribution
# Convert web extension to Safari extension
xcrun safari-web-extension-converter /path/to/extension
# This creates an Xcode project for Safari// Problem: Different browser objects
// Chrome uses 'chrome', Firefox uses 'browser'
// Solution: Use compatibility layer
if (typeof browser === "undefined") {
var browser = chrome;
}// Manifest v2 (Safari, older extensions)
"background": {
"scripts": ["background.js"],
"persistent": false
}
// Manifest v3 (Chrome, newer Firefox)
"background": {
"service_worker": "background.js"
}// Chrome: More permissive
// Firefox: Stricter security model
// Safari: Most restrictive
// Best practice: Request minimal permissions
"permissions": [
"storage", // Usually safe
"tabs" // May need user confirmation
]// Problem: Different timing and contexts
// Solution: Check if DOM is ready
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}// Chrome: chrome.storage
// Firefox: browser.storage (Promise-based)
// Safari: Limited storage options
// Solution: Use localStorage for simple data
// or implement storage abstraction layer// Different browsers have different CORS policies
// Always declare host permissions in manifest
"host_permissions": [
"https://api.quotable.io/*"
]// Different browsers support different icon sizes
// Provide multiple sizes for compatibility
"icons": {
"16": "icons/icon16.png", // Chrome/Edge
"48": "icons/icon48.png", // Firefox
"96": "icons/icon96.png", // Safari
"128": "icons/icon128.png" // Chrome Web Store
}- New tab override works
- Clock displays and updates
- Quotes load from API
- Focus input saves to localStorage
- Links can be added via button
- Links can be removed
- Data persists across sessions
- Extension loads in Firefox
- Extension loads in Chrome
- Extension loads in Edge
- All features work in each browser
- No console errors
- Permissions are granted properly
- Network failure (quote API down)
- localStorage unavailable
- Permission denied for tabs API
- Invalid URLs in links
// Use event-driven background scripts
// Avoid persistent background scripts when possible
// Minimize memory usage// Lazy load content scripts
// Remove event listeners when not needed
// Use efficient DOM queries// Use appropriate storage type:
// - localStorage: Simple, synchronous
// - browser.storage.local: Async, larger capacity
// - browser.storage.sync: Cross-device syncThis testing approach ensures your extension works reliably across all major browsers while avoiding common pitfalls.