Firefox Developer Tools

デバッグJavaScriptCSSブラウザフロントエンドCSS GridFlexboxMozilla Firefox

デバッグツール

Firefox Developer Tools

概要

Firefox Developer Toolsは、Mozilla FirefoxブラウザーのWeb開発者ツールです。CSS Grid、Flexboxの視覚的デバッグ機能が優秀で、JavaScript、ネットワーク分析も充実しています。

詳細

Firefox Developer Tools(以下DevTools)は、2009年にFirebugアドオンの後継として開発が開始され、現在ではFirefoxブラウザーに標準統合された包括的なWeb開発者向けツールセットです。MozillaのオープンWeb推進の理念に基づき、Web標準の正確な実装とクロスブラウザー互換性を重視した設計が特徴的です。

最大の特徴は、CSS関連のデバッグ機能の優秀さです。特にCSS Grid Inspector、Flexbox Inspector、CSS Animationsツールなど、レイアウト系CSS機能の視覚的デバッグにおいて、Chrome DevToolsを上回る部分があります。Grid Inspectorでは、グリッドライン、グリッドエリア、ギャップを視覚的に表示し、複雑なグリッドレイアウトの理解を大幅に支援します。Flexbox Inspectorも同様に、フレックスアイテムの配置、方向、伸縮を直感的に理解できる表示機能を提供します。

JavaScript デバッグ機能も充実しており、Debugger(旧称:Sources)パネルでは、ブレークポイント設定、変数監視、コールスタック分析、ソースマップサポートなど、モダンWeb開発に必要な機能を全て備えています。Web Assembly(WASM)のデバッグサポートも早期から実装されており、低レベル言語との統合開発においても強力です。

Network Monitor(ネットワークモニター)では、HTTP/HTTPSリクエストの詳細分析、セキュリティヘッダーの確認、CORS問題の特定など、ネットワーク関連の問題解決に必要な機能を提供します。Response Editor機能により、サーバーレスポンスの編集・テストも可能です。

パフォーマンス ツールも独自性があり、Gecko エンジン特有の最適化情報やメモリ使用量の詳細分析が可能です。また、アクセシビリティ インスペクターは業界でも高く評価されており、WCAG準拠のWebサイト構築において重要な役割を果たしています。

メリット・デメリット

メリット

  • 優秀なCSS デバッグ: Grid、Flexbox の視覚的デバッグが秀逸
  • 独自性のある機能: Chrome DevToolsにない独自機能多数
  • Web標準準拠: オープンWeb推進による正確な標準実装
  • アクセシビリティ: 業界最高レベルのアクセシビリティ ツール
  • プライバシー重視: ユーザーデータ保護を重視した設計
  • オープンソース: 完全にオープンソースで透明性が高い
  • 軽量: Chrome DevToolsより軽量な動作
  • 豊富なアドオン: 拡張性の高いアドオンエコシステム

デメリット

  • 市場シェア: Chrome DevToolsに比べて利用者が少ない
  • サードパーティ統合: 開発ツールとの統合が限定的
  • ドキュメント: Chrome DevToolsに比べてチュートリアルが少ない
  • 企業サポート: エンタープライズ環境での採用が少ない
  • 更新頻度: 機能追加のペースが若干遅い
  • 学習コスト: Chrome DevToolsに慣れた開発者の習得コスト
  • モバイル対応: モバイルデバッグ機能が限定的

主要リンク

書き方の例

DevToolsの起動と基本操作

// Firefox DevTools起動方法
// 1. F12キー
// 2. Ctrl+Shift+I (Windows/Linux)
// 3. Cmd+Opt+I (Mac)
// 4. メニュー → Web開発 → 開発者ツール
// 5. 右クリック → 要素を調査

// 特定要素の検査
// 右クリック → 要素を調査
// または Ctrl+Shift+C でインスペクターモード

Inspector - CSS Grid と Flexbox の視覚デバッグ

/* CSS Grid の高度なデバッグ */
.grid-container {
    display: grid;
    /* Grid Inspector で以下が視覚化される */
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: auto 200px auto;
    grid-template-areas: 
        "header header header"
        "sidebar main main"
        "footer footer footer";
    gap: 20px;
}

.grid-item {
    grid-area: main;
    /* Grid Inspector でアイテム配置を確認 */
}

/* Flexbox の詳細デバッグ */
.flex-container {
    display: flex;
    /* Flexbox Inspector で以下を視覚化 */
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 15px;
}

.flex-item {
    /* フレックスアイテムのプロパティが視覚化される */
    flex: 1 1 200px;
    align-self: flex-start;
}

/* Subgrid サポート(Firefox独自機能) */
.subgrid-container {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
}

.subgrid-item {
    display: grid;
    grid-template-columns: subgrid;  /* Firefox DevTools で確認可能 */
    grid-column: span 2;
}

Console - JavaScript デバッグと実行

// Firefox Console の独自機能
console.log('基本ログ出力');
console.info('情報メッセージ');
console.warn('警告メッセージ');
console.error('エラーメッセージ');

// オブジェクトの詳細表示
const complexObject = {
    name: 'Firefox DevTools',
    features: ['CSS Grid', 'Flexbox', 'WASM'],
    stats: { users: 100000, rating: 4.5 }
};
console.dir(complexObject);  // オブジェクト構造の詳細表示
console.table([{name: 'John', age: 30}, {name: 'Jane', age: 25}]);

// パフォーマンス測定
console.time('computation');
let result = 0;
for (let i = 0; i < 1000000; i++) {
    result += Math.sqrt(i);
}
console.timeEnd('computation');

// グループ化
console.group('User Operations');
console.log('Login attempt');
console.log('Data fetch');
console.groupEnd();

// 高度なConsole機能
console.trace();  // スタックトレース
console.count('iteration');  // カウンター
console.assert(result > 0, 'Result should be positive');

// DOM 要素セレクター(Firefox独自)
$('selector');   // document.querySelector
$$('selector');  // document.querySelectorAll
$0;             // 最後に選択した要素

Debugger - JavaScript のステップデバッグ

// Firefox Debugger での高度なデバッグ
class UserManager {
    constructor() {
        this.users = [];
        this.currentUser = null;
    }
    
    async loadUsers() {
        // ブレークポイント設定箇所
        try {
            const response = await fetch('/api/users');
            const userData = await response.json();
            
            // ここでデバッガーが停止
            this.users = userData.map(user => {
                return {
                    id: user.id,
                    name: user.name,
                    email: user.email,
                    isActive: user.status === 'active'
                };
            });
            
            return this.users;
        } catch (error) {
            // エラーハンドリングでのデバッグ
            console.error('Failed to load users:', error);
            throw error;
        }
    }
    
    findUser(criteria) {
        // 条件付きブレークポイント例
        // 条件: criteria.email.includes('@admin')
        return this.users.find(user => {
            if (criteria.id) return user.id === criteria.id;
            if (criteria.email) return user.email === criteria.email;
            if (criteria.name) return user.name.includes(criteria.name);
            return false;
        });
    }
    
    // 非同期処理のデバッグ
    async authenticateUser(credentials) {
        const user = this.findUser({ email: credentials.email });
        
        if (!user) {
            throw new Error('User not found');
        }
        
        // 認証処理のシミュレーション
        const isValid = await this.validateCredentials(credentials);
        
        if (isValid) {
            this.currentUser = user;
            return user;
        } else {
            throw new Error('Invalid credentials');
        }
    }
    
    async validateCredentials(credentials) {
        // 認証API呼び出しのシミュレーション
        return new Promise(resolve => {
            setTimeout(() => {
                // ここでログポイント設定可能
                resolve(credentials.password === 'correct');
            }, 100);
        });
    }
}

// Firefox Debugger での操作例:
// 1. ブレークポイント設定(行番号クリック)
// 2. 条件付きブレークポイント(右クリック → 条件付きブレークポイント追加)
// 3. ログポイント(右クリック → ログポイント追加)
// 4. Watch式(Watch パネルで変数監視)
// 5. Call Stack(関数呼び出し履歴確認)

const userManager = new UserManager();

Network Monitor - 詳細なネットワーク分析

// Network Monitor で監視される通信例
class APIClient {
    constructor(baseURL) {
        this.baseURL = baseURL;
        this.defaultHeaders = {
            'Content-Type': 'application/json',
            'Accept': 'application/json'
        };
    }
    
    async request(endpoint, options = {}) {
        const url = `${this.baseURL}${endpoint}`;
        
        // Network Monitor で詳細確認可能
        const config = {
            method: options.method || 'GET',
            headers: { ...this.defaultHeaders, ...options.headers },
            ...options
        };
        
        if (config.method !== 'GET' && options.data) {
            config.body = JSON.stringify(options.data);
        }
        
        try {
            const response = await fetch(url, config);
            
            // レスポンスヘッダーの確認
            console.log('Response headers:', [...response.headers.entries()]);
            
            if (!response.ok) {
                throw new Error(`HTTP ${response.status}: ${response.statusText}`);
            }
            
            return await response.json();
        } catch (error) {
            console.error('Network request failed:', error);
            throw error;
        }
    }
    
    // HTTP メソッド別のショートカット
    async get(endpoint) {
        return this.request(endpoint);
    }
    
    async post(endpoint, data) {
        return this.request(endpoint, { method: 'POST', data });
    }
    
    async put(endpoint, data) {
        return this.request(endpoint, { method: 'PUT', data });
    }
    
    async delete(endpoint) {
        return this.request(endpoint, { method: 'DELETE' });
    }
}

// WebSocket 通信の監視
class WebSocketManager {
    constructor(url) {
        this.url = url;
        this.socket = null;
        this.messageQueue = [];
    }
    
    connect() {
        // WebSocket通信もNetwork Monitorで監視可能
        this.socket = new WebSocket(this.url);
        
        this.socket.onopen = (event) => {
            console.log('WebSocket connected:', event);
            this.processMessageQueue();
        };
        
        this.socket.onmessage = (event) => {
            console.log('WebSocket message received:', event.data);
            this.handleMessage(JSON.parse(event.data));
        };
        
        this.socket.onerror = (error) => {
            console.error('WebSocket error:', error);
        };
        
        this.socket.onclose = (event) => {
            console.log('WebSocket closed:', event.code, event.reason);
        };
    }
    
    send(message) {
        if (this.socket && this.socket.readyState === WebSocket.OPEN) {
            this.socket.send(JSON.stringify(message));
        } else {
            this.messageQueue.push(message);
        }
    }
    
    processMessageQueue() {
        while (this.messageQueue.length > 0) {
            const message = this.messageQueue.shift();
            this.send(message);
        }
    }
    
    handleMessage(data) {
        // メッセージ処理ロジック
        switch (data.type) {
            case 'user_update':
                this.updateUser(data.payload);
                break;
            case 'notification':
                this.showNotification(data.payload);
                break;
            default:
                console.warn('Unknown message type:', data.type);
        }
    }
}

// Network Monitor での確認項目:
// 1. Request/Response Headers
// 2. Request/Response Body
// 3. Timing 情報
// 4. Security(HTTPS、CORS等)
// 5. WebSocket Frame の送受信内容

Performance - パフォーマンス プロファイリング

// Firefox Performance ツールでの最適化例
class PerformanceTestCase {
    constructor() {
        this.data = [];
        this.cache = new Map();
    }
    
    // CPU集約的処理(プロファイリング対象)
    generateLargeDataset(size) {
        console.time('data-generation');
        
        // Performance ツールで関数単位の実行時間を確認可能
        const data = [];
        for (let i = 0; i < size; i++) {
            data.push({
                id: i,
                value: Math.random() * 1000,
                category: ['A', 'B', 'C'][i % 3],
                timestamp: Date.now(),
                computed: this.expensiveComputation(i)
            });
        }
        
        console.timeEnd('data-generation');
        return data;
    }
    
    expensiveComputation(input) {
        // 重い計算処理
        let result = input;
        for (let i = 0; i < 1000; i++) {
            result = Math.sqrt(result + i) * Math.sin(i);
        }
        return result;
    }
    
    // DOM操作のパフォーマンス測定
    renderData(container, data) {
        console.time('dom-rendering');
        
        // パフォーマンス問題のある実装例
        container.innerHTML = '';  // 全要素削除
        
        data.forEach(item => {
            const element = document.createElement('div');
            element.className = 'data-item';
            element.innerHTML = `
                <span class="id">${item.id}</span>
                <span class="value">${item.value.toFixed(2)}</span>
                <span class="category">${item.category}</span>
            `;
            container.appendChild(element);  // 個別追加(非効率)
        });
        
        console.timeEnd('dom-rendering');
    }
    
    // 最適化されたDOM操作
    renderDataOptimized(container, data) {
        console.time('optimized-rendering');
        
        // DocumentFragment使用で最適化
        const fragment = document.createDocumentFragment();
        
        data.forEach(item => {
            const element = document.createElement('div');
            element.className = 'data-item';
            
            // innerHTML使用を避けてパフォーマンス向上
            const idSpan = document.createElement('span');
            idSpan.className = 'id';
            idSpan.textContent = item.id;
            
            const valueSpan = document.createElement('span');
            valueSpan.className = 'value';
            valueSpan.textContent = item.value.toFixed(2);
            
            const categorySpan = document.createElement('span');
            categorySpan.className = 'category';
            categorySpan.textContent = item.category;
            
            element.appendChild(idSpan);
            element.appendChild(valueSpan);
            element.appendChild(categorySpan);
            
            fragment.appendChild(element);
        });
        
        container.innerHTML = '';
        container.appendChild(fragment);  // 一括追加
        
        console.timeEnd('optimized-rendering');
    }
    
    // メモリ使用量の監視
    monitorMemoryUsage() {
        if (performance.memory) {
            const memory = performance.memory;
            console.log('Memory usage:', {
                used: (memory.usedJSHeapSize / 1024 / 1024).toFixed(2) + ' MB',
                total: (memory.totalJSHeapSize / 1024 / 1024).toFixed(2) + ' MB',
                limit: (memory.jsHeapSizeLimit / 1024 / 1024).toFixed(2) + ' MB'
            });
        }
    }
}

// Performance ツールでの測定実行
const perfTest = new PerformanceTestCase();
const container = document.getElementById('data-container');

// Recording開始前に以下実行
// 1. Performance タブ → Record ボタンクリック
// 2. 以下コード実行
// 3. Stop ボタンクリック

const testData = perfTest.generateLargeDataset(1000);
perfTest.renderData(container, testData);
perfTest.monitorMemoryUsage();

Storage Inspector - ストレージとアプリケーション状態

// Firefox Storage Inspector での確認項目
class StorageManager {
    constructor() {
        this.dbName = 'MyAppDB';
        this.dbVersion = 1;
        this.db = null;
    }
    
    // Local Storage 操作(Storage Inspector で確認)
    saveToLocalStorage(key, data) {
        try {
            localStorage.setItem(key, JSON.stringify(data));
            console.log(`Saved to localStorage: ${key}`);
        } catch (error) {
            console.error('LocalStorage save failed:', error);
        }
    }
    
    loadFromLocalStorage(key) {
        try {
            const data = localStorage.getItem(key);
            return data ? JSON.parse(data) : null;
        } catch (error) {
            console.error('LocalStorage load failed:', error);
            return null;
        }
    }
    
    // Session Storage 操作
    saveToSessionStorage(key, data) {
        sessionStorage.setItem(key, JSON.stringify(data));
    }
    
    // IndexedDB 操作(詳細監視可能)
    async initIndexedDB() {
        return new Promise((resolve, reject) => {
            const request = indexedDB.open(this.dbName, this.dbVersion);
            
            request.onerror = () => reject(request.error);
            request.onsuccess = () => {
                this.db = request.result;
                resolve(this.db);
            };
            
            request.onupgradeneeded = (event) => {
                const db = event.target.result;
                
                // オブジェクトストア作成
                if (!db.objectStoreNames.contains('users')) {
                    const store = db.createObjectStore('users', { keyPath: 'id' });
                    store.createIndex('email', 'email', { unique: true });
                    store.createIndex('name', 'name', { unique: false });
                }
            };
        });
    }
    
    async saveToIndexedDB(storeName, data) {
        const transaction = this.db.transaction([storeName], 'readwrite');
        const store = transaction.objectStore(storeName);
        
        return new Promise((resolve, reject) => {
            const request = store.add(data);
            request.onsuccess = () => resolve(request.result);
            request.onerror = () => reject(request.error);
        });
    }
    
    // Cookie 操作(Storage Inspector で確認)
    setCookie(name, value, days = 7) {
        const expires = new Date();
        expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000));
        
        document.cookie = `${name}=${value}; expires=${expires.toUTCString()}; path=/; SameSite=Strict`;
    }
    
    getCookie(name) {
        const cookies = document.cookie.split(';');
        for (let cookie of cookies) {
            const [cookieName, cookieValue] = cookie.trim().split('=');
            if (cookieName === name) {
                return cookieValue;
            }
        }
        return null;
    }
    
    // Cache API 操作
    async cacheResources() {
        if ('caches' in window) {
            const cache = await caches.open('my-app-v1');
            
            const urlsToCache = [
                '/',
                '/css/app.css',
                '/js/app.js',
                '/images/logo.png'
            ];
            
            await cache.addAll(urlsToCache);
            console.log('Resources cached successfully');
        }
    }
}

// Storage Inspector での確認項目:
// 1. Local Storage
// 2. Session Storage  
// 3. IndexedDB
// 4. Cookies
// 5. Cache Storage
// 6. Extension Storage

const storage = new StorageManager();

Accessibility Inspector - アクセシビリティ監査

<!-- Firefox Accessibility Inspector での監査対象HTML -->
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>アクセシビリティテスト</title>
</head>
<body>
    <!-- 適切なセマンティックHTML -->
    <header>
        <nav role="navigation" aria-label="メインナビゲーション">
            <ul>
                <li><a href="#main" aria-label="メインコンテンツにスキップ">メイン</a></li>
                <li><a href="#about">概要</a></li>
                <li><a href="#contact">お問い合わせ</a></li>
            </ul>
        </nav>
    </header>
    
    <main id="main" role="main">
        <h1>メインタイトル</h1>
        
        <!-- フォームのアクセシビリティ -->
        <form>
            <fieldset>
                <legend>ユーザー情報</legend>
                
                <label for="name">名前 <span aria-label="必須">(必須)</span></label>
                <input type="text" id="name" name="name" required aria-describedby="name-error">
                <div id="name-error" class="error" aria-live="polite"></div>
                
                <label for="email">メールアドレス</label>
                <input type="email" id="email" name="email" aria-describedby="email-help">
                <div id="email-help" class="help">例: [email protected]</div>
            </fieldset>
            
            <button type="submit" aria-describedby="submit-help">送信</button>
            <div id="submit-help" class="help">フォームを送信するにはこのボタンをクリックしてください</div>
        </form>
        
        <!-- 画像のアクセシビリティ -->
        <img src="chart.png" alt="2023年売上グラフ:第1四半期100万円、第2四半期150万円、第3四半期120万円、第4四半期180万円">
        
        <!-- テーブルのアクセシビリティ -->
        <table>
            <caption>月別売上データ</caption>
            <thead>
                <tr>
                    <th scope="col"></th>
                    <th scope="col">売上</th>
                    <th scope="col">前年同月比</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <th scope="row">1月</th>
                    <td>100万円</td>
                    <td>+10%</td>
                </tr>
            </tbody>
        </table>
    </main>
</body>
</html>
// Accessibility Inspector での JavaScript 監査
class AccessibilityManager {
    constructor() {
        this.currentFocus = null;
        this.announcements = [];
    }
    
    // ARIA Live Region での動的更新
    announceMessage(message, priority = 'polite') {
        const announcer = document.getElementById('announcer') || 
                         this.createAnnouncer();
        
        announcer.setAttribute('aria-live', priority);
        announcer.textContent = message;
        
        // メッセージをログに記録
        this.announcements.push({
            message,
            priority,
            timestamp: new Date().toISOString()
        });
    }
    
    createAnnouncer() {
        const announcer = document.createElement('div');
        announcer.id = 'announcer';
        announcer.className = 'sr-only';  // スクリーンリーダー専用
        announcer.setAttribute('aria-live', 'polite');
        announcer.setAttribute('aria-atomic', 'true');
        document.body.appendChild(announcer);
        return announcer;
    }
    
    // キーボードナビゲーション管理
    setupKeyboardNavigation() {
        document.addEventListener('keydown', (event) => {
            // Accessibility Inspector でキーボードイベント監視
            switch (event.key) {
                case 'Tab':
                    this.handleTabNavigation(event);
                    break;
                case 'Escape':
                    this.handleEscapeKey(event);
                    break;
                case 'Enter':
                case ' ':
                    this.handleActivation(event);
                    break;
            }
        });
    }
    
    handleTabNavigation(event) {
        const focusableElements = this.getFocusableElements();
        const currentIndex = focusableElements.indexOf(document.activeElement);
        
        if (event.shiftKey) {
            // Shift+Tab: 前の要素
            const prevIndex = currentIndex > 0 ? currentIndex - 1 : focusableElements.length - 1;
            focusableElements[prevIndex]?.focus();
        } else {
            // Tab: 次の要素
            const nextIndex = currentIndex < focusableElements.length - 1 ? currentIndex + 1 : 0;
            focusableElements[nextIndex]?.focus();
        }
    }
    
    getFocusableElements() {
        const selector = 'a[href], button, input, textarea, select, [tabindex]:not([tabindex="-1"])';
        return Array.from(document.querySelectorAll(selector))
                   .filter(el => !el.disabled && el.offsetParent !== null);
    }
    
    // フォーカス管理
    trapFocus(container) {
        const focusableElements = container.querySelectorAll(
            'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
        );
        
        const firstFocusable = focusableElements[0];
        const lastFocusable = focusableElements[focusableElements.length - 1];
        
        container.addEventListener('keydown', (event) => {
            if (event.key === 'Tab') {
                if (event.shiftKey) {
                    if (document.activeElement === firstFocusable) {
                        lastFocusable.focus();
                        event.preventDefault();
                    }
                } else {
                    if (document.activeElement === lastFocusable) {
                        firstFocusable.focus();
                        event.preventDefault();
                    }
                }
            }
        });
        
        firstFocusable?.focus();
    }
}

// Accessibility Inspector での確認項目:
// 1. Color contrast ratios
// 2. Keyboard navigation
// 3. Screen reader compatibility  
// 4. ARIA attributes
// 5. Semantic HTML structure
// 6. Focus management

const accessibility = new AccessibilityManager();
accessibility.setupKeyboardNavigation();