// Outlook Web Inbox Count Extension v1.4 // Changelog v1.4: // - Added outlook.cloud.microsoft to manifest matches (Microsoft migrated Outlook Web to new domain) // Changelog v1.3: // - Fixed Firefox compatibility by adding browser_specific_settings to manifest // - Fixed bug where findTotalItems() wasn't returning values due to forEach callbacks // - Changed forEach loops to for loops to enable proper return statements // - Extended retry mechanism with more attempts (1s, 2s, 3s, 5s, 8s, 10s, 15s, 20s) // - Added re-logging of environment info on each retry to track page load progress // Changelog v1.2: // - Added comprehensive debugging information to help diagnose user issues // - Added URL detection and page structure analysis // - Enhanced logging for DOM element discovery // - Added language/locale detection // Changelog v1.1: // - Fixed browser hanging issue by adding throttling mechanism // - Improved MutationObserver to be more selective and prevent infinite loops // - Enhanced DOM selectors with multiple fallbacks for different Outlook versions // - Reduced polling frequency to improve performance // - Added better pattern matching for inbox count extraction console.log("Outlook Inbox Count Extension v1.4: Script started"); // Enhanced debugging information function logEnvironmentInfo() { console.log("=== OUTLOOK INBOX EXTENSION DEBUG INFO ==="); console.log("URL:", window.location.href); console.log("Domain:", window.location.hostname); console.log("User Agent:", navigator.userAgent); console.log("Language:", navigator.language); console.log("Document ready state:", document.readyState); console.log("Page title:", document.title); // Check for New Outlook indicators const isNewOutlook = document.querySelector('[data-app-section="NewMailModule"]') || document.querySelector('div[role="main"][data-app-section]') || document.body.classList.contains('new-outlook') || window.location.href.includes('mail.office365.com'); console.log("Detected New Outlook interface:", isNewOutlook); // Log all elements that might be inbox-related console.log("=== DOM ANALYSIS ==="); const potentialInboxElements = document.querySelectorAll('[data-folder-name], [title*="Inbox"], [aria-label*="Inbox"], button[title*="nbox"], div[title*="nbox"]'); console.log("Found", potentialInboxElements.length, "potential inbox elements:"); potentialInboxElements.forEach((el, i) => { console.log(`Element ${i}:`, { tagName: el.tagName, className: el.className, title: el.getAttribute('title'), dataFolderName: el.getAttribute('data-folder-name'), ariaLabel: el.getAttribute('aria-label'), textContent: el.textContent?.substring(0, 100) }); }); console.log("=== END DOM ANALYSIS ==="); } let isProcessing = false; let lastProcessTime = 0; const THROTTLE_DELAY = 1000; // 1 second throttle function findInboxElement() { console.log("=== SEARCHING FOR INBOX ELEMENT ==="); // Try multiple selectors to find inbox element const selectors = [ 'div[title^="Inbox -"][data-folder-name="inbox"]', 'div[data-folder-name="inbox"]', 'div[title*="Inbox"]', 'button[title^="Inbox -"]', 'button[data-folder-name="inbox"]', // Additional selectors for different Outlook versions 'button[aria-label*="Inbox"]', 'div[aria-label*="Inbox"]', '[data-folder-id="inbox"]', '[data-automation-id*="inbox"]', 'span[title*="Inbox"]' ]; console.log("Trying", selectors.length, "different selectors..."); for (let i = 0; i < selectors.length; i++) { const selector = selectors[i]; console.log(`Trying selector ${i + 1}/${selectors.length}:`, selector); const elements = document.querySelectorAll(selector); console.log(`Found ${elements.length} elements with this selector`); if (elements.length > 0) { elements.forEach((el, idx) => { console.log(` Element ${idx}:`, { tagName: el.tagName, className: el.className, title: el.getAttribute('title'), ariaLabel: el.getAttribute('aria-label'), dataFolderName: el.getAttribute('data-folder-name'), textContent: el.textContent?.substring(0, 50) + '...' }); }); const inboxDiv = elements[0]; // Use the first match console.log("✓ Using first element as inbox:", inboxDiv); const span = inboxDiv.querySelector('span:not(.added-count)'); if (span) { console.log("✓ Found inner span element:", span); return span; } // If no span found, try to use the element itself console.log("ℹ No inner span found, using element itself"); return inboxDiv; } } console.log("❌ Inbox element not found with any selector"); console.log("=== END INBOX ELEMENT SEARCH ==="); return null; } function findTotalItems() { console.log("=== SEARCHING FOR TOTAL ITEMS COUNT ==="); // Try multiple selectors and patterns const selectors = [ 'div[title^="Inbox -"][data-folder-name="inbox"]', 'div[data-folder-name="inbox"]', 'div[title*="Inbox"]', 'button[title^="Inbox -"]', 'button[data-folder-name="inbox"]', 'button[aria-label*="Inbox"]', 'div[aria-label*="Inbox"]', '[data-folder-id="inbox"]' ]; const patterns = [ /Inbox - (\d+) items/i, /Inbox \((\d+)\)/i, /(\d+) items/i, /\((\d+)\)/i, // Additional patterns for different languages/formats /Inbox.*?(\d+)/i, /(\d+).*?unread/i, /(\d+).*?message/i, /(\d+).*?email/i ]; // Also check document title which often contains count console.log("Checking document title:", document.title); for (const pattern of patterns) { const match = document.title.match(pattern); if (match) { const totalItems = match[1]; console.log(`✓ Total items found in document title: ${totalItems}`); return totalItems; } } console.log("Checking DOM elements..."); for (const selector of selectors) { const elements = document.querySelectorAll(selector); console.log(`Checking selector: ${selector} (${elements.length} elements)`); for (let idx = 0; idx < elements.length; idx++) { const inboxDiv = elements[idx]; const sources = [ { name: 'title', value: inboxDiv.getAttribute('title') }, { name: 'aria-label', value: inboxDiv.getAttribute('aria-label') }, { name: 'textContent', value: inboxDiv.textContent } ]; for (const source of sources) { if (!source.value) continue; console.log(` Element ${idx} ${source.name}:`, source.value.substring(0, 100)); for (const pattern of patterns) { const match = source.value.match(pattern); if (match) { const totalItems = match[1]; console.log(`✓ Total items found in ${source.name}: ${totalItems}`); return totalItems; } } } } } console.log("❌ Total items count not found in any source"); console.log("=== END TOTAL ITEMS SEARCH ==="); return null; } function addInboxCount() { const now = Date.now(); // Throttle execution to prevent excessive calls if (isProcessing || (now - lastProcessTime) < THROTTLE_DELAY) { return; } isProcessing = true; lastProcessTime = now; console.log("addInboxCount function called"); const inboxElement = findInboxElement(); if (!inboxElement) { console.log("Inbox element not found, will try again later"); isProcessing = false; return; } const totalItems = findTotalItems(); console.log("Total items:", totalItems); // Check if we've already added the count let countSpan = inboxElement.querySelector('.added-count'); if (!countSpan) { // Create a new span element for the count countSpan = document.createElement('span'); countSpan.style.marginLeft = '5px'; countSpan.style.fontWeight = 'bold'; countSpan.className = 'added-count'; // Add the count next to the inbox label inboxElement.appendChild(countSpan); console.log("Count span added to inbox label"); } // Update the count if (totalItems) { countSpan.textContent = ` (${totalItems})`; countSpan.style.color = ''; // Reset to default color } else { countSpan.textContent = ' (-)'; countSpan.style.color = '#888'; // Grey color } console.log("Count span updated"); isProcessing = false; } function init() { console.log("=== INITIALIZING OUTLOOK INBOX COUNT EXTENSION ==="); // Log environment information first logEnvironmentInfo(); console.log("Starting initial inbox count attempt..."); addInboxCount(); // Try to add the inbox count multiple times during startup with extended delays // Firefox may need more time for Outlook's dynamic content to load console.log("Scheduling additional attempts at 1s, 2s, 3s, 5s, 8s, 10s, 15s, and 20s..."); const retryTimes = [1000, 2000, 3000, 5000, 8000, 10000, 15000, 20000]; retryTimes.forEach(delay => { setTimeout(() => { console.log(`=== ${delay/1000}-SECOND RETRY ===`); logEnvironmentInfo(); // Re-log to see if page has loaded addInboxCount(); }, delay); }); // Then, check every 15 seconds (slightly increased for debugging) setInterval(() => { console.log("=== PERIODIC CHECK ==="); addInboxCount(); }, 15000); console.log("=== INITIALIZATION COMPLETE ==="); } // Use a MutationObserver to watch for changes in the DOM const observer = new MutationObserver((mutations) => { let shouldUpdate = false; for (let mutation of mutations) { // Only update if attributes changed on elements that might be inbox-related if (mutation.type === 'attributes' && mutation.attributeName === 'title') { const target = mutation.target; if (target.hasAttribute && target.hasAttribute('data-folder-name')) { shouldUpdate = true; break; } } // Or if new inbox elements were added else if (mutation.type === 'childList') { const nodes = [...(mutation.addedNodes || [])]; if (nodes.some(node => node.nodeType === 1 && (node.hasAttribute?.('data-folder-name') || node.querySelector?.('[data-folder-name="inbox"]')))) { shouldUpdate = true; break; } } } if (shouldUpdate) { addInboxCount(); } }); // Start observing with more targeted configuration observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['title', 'data-folder-name'] }); // Run the init function when the DOM is fully loaded if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", init); } else { init(); } // Listen for messages from the background script chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.message === "URL_CHANGED") { console.log("URL changed, running inbox count function"); addInboxCount(); } });