Firefox Developer Tools
デバッグツール
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に慣れた開発者の習得コスト
- モバイル対応: モバイルデバッグ機能が限定的
主要リンク
- Firefox Developer Tools公式
- Firefox DevTools公式ドキュメント
- Firefox DevTools GitHub
- CSS Grid Inspector
- Flexbox Inspector
- Firefox DevTools Tips
書き方の例
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();