Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
87 changes: 14 additions & 73 deletions cloudformation_reflesher/content.js
Original file line number Diff line number Diff line change
@@ -1,112 +1,64 @@
let refreshInterval = null;
let currentInterval = 3000; // デフォルトの間隔を保持 (popup.jsのデフォルトと合わせる)
let isRunning = false; // 実行状態を保持
let currentInterval = 3000;
let isRunning = false;

// デバッグログ出力
function debugLog(message, data = null) {
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] ${message}`, data || '');
}

// エラーログ出力
function errorLog(message, error = null) {
const timestamp = new Date().toISOString();
console.error(`[${timestamp}] Error: ${message}`, error || '');
}

// リフレッシュボタンを探す関数
function findRefreshButtons() {
debugLog('更新ボタンを検索中...');

try {
const buttons = document.querySelectorAll('button[data-testid="refresh-button"]');
const buttonArray = Array.from(buttons);

debugLog(`更新ボタンを${buttonArray.length}個検出:`, buttonArray.map(button => ({
className: button.className,
ariaLabel: button.getAttribute('aria-label'),
textContent: button.textContent,
isVisible: button.offsetParent !== null
})));

return buttonArray;
return Array.from(document.querySelectorAll('button[data-testid="refresh-button"]'));
} catch (error) {
errorLog('更新ボタンの検索中にエラーが発生しました', error);
console.error('更新ボタンの検索中にエラーが発生しました', error);
return [];
}
}

// リフレッシュ処理を実行する関数
function performRefresh() {
debugLog('更新処理を開始...');

try {
const refreshButtons = findRefreshButtons();
if (refreshButtons.length > 0) {
refreshButtons.forEach((button, index) => {
refreshButtons.forEach((button) => {
try {
button.click();
debugLog(`ボタン${index + 1}をクリックしました`, {
className: button.className,
ariaLabel: button.getAttribute('aria-label')
});
} catch (error) {
errorLog(`ボタン${index + 1}のクリックに失敗しました`, error);
console.error('ボタンのクリックに失敗しました', error);
}
});
debugLog(`更新完了(${refreshButtons.length}個のボタンをクリック)`);
} else {
errorLog('更新ボタンが見つかりませんでした');
console.error('更新ボタンが見つかりませんでした');
}
} catch (error) {
errorLog('更新処理でエラーが発生しました', error);
console.error('更新処理でエラーが発生しました', error);
}
}

// 自動更新の開始
function startAutoRefresh(interval) {
// intervalが未定義または不正な場合はデフォルト値を使用
const validInterval = (typeof interval === 'number' && interval >= 1000) ? interval : 3000;
currentInterval = validInterval; // 現在の間隔を保存
debugLog(`自動更新を開始(間隔: ${currentInterval / 1000}秒)`);
currentInterval = validInterval;

try {
stopAutoRefresh(); // 既存のインターバルをクリア
stopAutoRefresh();
refreshInterval = setInterval(performRefresh, currentInterval);
isRunning = true; // 実行状態を更新
debugLog('インターバルタイマーを設定しました');
isRunning = true;
} catch (error) {
errorLog('自動更新の開始に失敗しました', error);
console.error('自動更新の開始に失敗しました', error);
}
}

// 自動更新の停止
function stopAutoRefresh() {
debugLog('自動更新を停止します');

try {
if (refreshInterval) {
clearInterval(refreshInterval);
refreshInterval = null;
isRunning = false; // 実行状態を更新
debugLog('インターバルタイマーをクリアしました');
} else {
// refreshIntervalがnullでもisRunningをfalseに設定
isRunning = false;
}
isRunning = false;
} catch (error) {
errorLog('自動更新の停止に失敗しました', error);
console.error('自動更新の停止に失敗しました', error);
}
}

// popup.jsからのメッセージを受信
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
debugLog('メッセージを受信:', message);

try {
switch (message.action) {
case 'ping':
debugLog('接続確認を受信');
sendResponse({ status: 'ok' });
break;
case 'start':
Expand All @@ -118,30 +70,19 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
sendResponse({ status: 'stopped' });
break;
case 'getStatus':
debugLog('状態確認リクエストを受信');
sendResponse({
status: 'statusUpdate',
isRunning: isRunning,
interval: currentInterval
});
break;
default:
errorLog('不明なアクション:', message.action);
sendResponse({ status: 'error', message: 'Unknown action' });
break;
}
} catch (error) {
errorLog('メッセージ処理でエラーが発生しました', error);
// エラーが発生した場合でもレスポンスを返す
sendResponse({ status: 'error', message: error.message });
}

// 非同期レスポンスのために true を返す必要がある
return true;
});

// 初期化時のデバッグ情報
debugLog('content.jsを初期化しました', {
url: window.location.href,
timestamp: new Date().toISOString()
});
81 changes: 19 additions & 62 deletions cloudformation_reflesher/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,15 @@ document.addEventListener('DOMContentLoaded', () => {
const intervalInput = document.getElementById('interval');
const statusDiv = document.getElementById('status');

// デバッグログ出力
function debugLog(message, data = null) {
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] ${message}`, data || '');
}

// エラーログ出力とUI更新
function showError(message) {
const timestamp = new Date().toISOString();
console.error(`[${timestamp}] Error: ${message}`);
statusDiv.className = 'status error'; // エラー用のスタイルクラスを追加(CSSも後で調整推奨)
console.error(message);
statusDiv.className = 'status error';
statusDiv.textContent = `エラー: ${message}`;
// エラー時はボタンを無効化するなど、状態を明確にする
toggleButton.disabled = true;
toggleButton.textContent = 'エラー';
}

// UIを更新する関数
function updateUI(state) {
debugLog('UIを更新:', state);
if (!state) {
showError('状態情報の取得に失敗しました');
return;
Expand All @@ -31,133 +20,101 @@ document.addEventListener('DOMContentLoaded', () => {
toggleButton.textContent = isRunning ? '停止' : '開始';
statusDiv.className = `status ${isRunning ? 'running' : 'stopped'}`;
statusDiv.textContent = isRunning ? '実行中' : '停止中';
intervalInput.value = interval / 1000; // msから秒に変換
toggleButton.disabled = false; // エラーでなければボタンを有効化
intervalInput.value = interval / 1000;
toggleButton.disabled = false;
}

// 現在のタブを取得
async function getCurrentTab() {
try {
debugLog('現在のタブを取得中...');
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (!tab) {
throw new Error('アクティブなタブが見つかりません');
}
// URLチェックを追加
if (!tab.url || (!tab.url.includes('console.aws.amazon.com') || !tab.url.includes('cloudformation'))) {
if (!tab.url || !tab.url.includes('console.aws.amazon.com')) {
throw new Error('このページでは利用できません');
}
debugLog('タブ情報:', tab);
return tab;
} catch (error) {
showError(error.message || 'タブの取得またはURLの検証に失敗しました');
throw error; // エラーを再スローして呼び出し元で捕捉
throw error;
}
}

// content.jsとの接続を確認し、状態を取得
async function checkConnectionAndGetStatus(tabId) {
debugLog('content.jsとの接続確認と状態取得を開始...');
try {
// まず 'getStatus' を試みる (接続確認も兼ねる)
const response = await chrome.tabs.sendMessage(tabId, { action: 'getStatus' });
debugLog('状態を取得:', response);
if (response && response.status === 'statusUpdate') {
return { isRunning: response.isRunning, interval: response.interval };
} else {
throw new Error('無効な応答を受信しました');
}
} catch (error) {
debugLog('getStatusに失敗、pingを試行:', error);
// getStatusが失敗した場合、content scriptが古いか未注入の可能性があるためpingを試す
try {
await chrome.tabs.sendMessage(tabId, { action: 'ping' });
debugLog('ping成功、デフォルト状態でUIを初期化');
// pingは成功したが状態は不明なため、デフォルト停止状態を返す
return { isRunning: false, interval: 3000 };
} catch (pingError) {
debugLog('pingも失敗:', pingError);
throw new Error('content.jsとの接続に失敗しました。ページを再読み込みしてください。');
}
}
}

// 更新状態の切り替え
async function toggleRefresh() {
debugLog('更新状態の切り替えを開始...');
toggleButton.disabled = true; // 処理中はボタンを無効化
toggleButton.disabled = true;

try {
const tab = await getCurrentTab(); // URL検証もここで行われる
const tab = await getCurrentTab();

// 入力値の検証
const intervalSeconds = parseInt(intervalInput.value);
if (isNaN(intervalSeconds) || intervalSeconds < 1) {
showError('更新間隔は1以上の数値を入力してください');
// 状態取得してUIを元に戻す
const currentState = await checkConnectionAndGetStatus(tab.id);
updateUI(currentState);
return;
}
const intervalMs = intervalSeconds * 1000;
debugLog(`更新間隔を設定: ${intervalMs}ms`);

// 現在のボタンのテキストに基づいてアクションを決定
const currentAction = toggleButton.textContent === '開始' ? 'start' : 'stop';
debugLog(`実行アクション: ${currentAction}`);

// content.jsへメッセージ送信
const response = await chrome.tabs.sendMessage(tab.id, {
action: currentAction,
interval: intervalMs // startの場合のみ意味を持つが、常に送る
interval: intervalMs
});
debugLog('メッセージ応答を受信:', response);

// 応答に基づいてUIを更新
if (response && (response.status === 'started' || response.status === 'stopped')) {
// 再度状態を取得してUIに反映するのが最も確実
const newState = await checkConnectionAndGetStatus(tab.id);
updateUI(newState);
const newState = await checkConnectionAndGetStatus(tab.id);
updateUI(newState);
} else {
throw new Error('content.jsから無効な応答がありました');
}

} catch (error) {
showError(error.message || '更新処理でエラーが発生しました');
// エラー発生時も状態を再取得してUIを更新しようと試みる
try {
const tab = await getCurrentTab();
const currentState = await checkConnectionAndGetStatus(tab.id);
updateUI(currentState);
} catch (statusError) {
// 状態取得も失敗した場合はどうしようもない
debugLog('エラー後の状態取得にも失敗:', statusError);
// 状態取得も失敗した場合は何もしない
}
} finally {
// 成功・失敗に関わらず、最終的にボタンの状態を更新
if (statusDiv.className.includes('error')) {
toggleButton.disabled = true;
} else {
toggleButton.disabled = false;
}
if (statusDiv.className.includes('error')) {
toggleButton.disabled = true;
} else {
toggleButton.disabled = false;
}
}
}

// 初期化処理
async function initializePopup() {
try {
const tab = await getCurrentTab(); // URL検証もここで行われる
const tab = await getCurrentTab();
const initialState = await checkConnectionAndGetStatus(tab.id);
updateUI(initialState);
} catch (error) {
// getCurrentTabやcheckConnectionAndGetStatus内でshowErrorが呼ばれるはず
debugLog('初期化中にエラーが発生:', error);
// getCurrentTab や checkConnectionAndGetStatus 内で showError が呼ばれる
}
}

// ボタンクリックイベントの設定
toggleButton.addEventListener('click', toggleRefresh);

// ポップアップ表示時に初期化処理を実行
initializePopup();
});