DevTools mobile có Pause thật (debugger) + overlay
// ==UserScript==
// @name Mini DevTools Mobile v2
// @namespace http://tampermonkey.net/
// @version 2.0
// @description DevTools mobile có Pause thật (debugger) + overlay
// @author You
// @match *://*/*
// @include *
// @grant none
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
function waitForBody(cb) {
if (document.body) cb();
else setTimeout(function() { waitForBody(cb); }, 50);
}
waitForBody(function() {
// ========== PAUSE OVERLAY ==========
var pauseOverlay = document.createElement('div');
pauseOverlay.id = 'dtm-pause-overlay';
pauseOverlay.innerHTML =
'<div style="text-align:center;background:rgba(0,0,0,0.9);padding:20px;border-radius:12px;border:2px solid #ff6b6b;">' +
'<div style="font-size:40px;">⏸️</div>' +
'<div style="font-size:18px;font-weight:bold;color:#ff6b6b;margin:10px 0;">PAUSED IN DEBUGGER</div>' +
'<div id="dtm-pause-info" style="color:#aaa;font-size:12px;margin:5px 0;">Script paused</div>' +
'<button id="dtm-resume-btn" style="margin-top:15px;padding:12px 30px;background:#4caf50;color:white;' +
'border:none;border-radius:8px;font-size:16px;cursor:pointer;">▶️ RESUME</button>' +
'<button id="dtm-step-btn" style="margin-left:8px;padding:12px 20px;background:#007acc;color:white;' +
'border:none;border-radius:8px;font-size:16px;cursor:pointer;">⏭️ Step</button>' +
'</div>';
pauseOverlay.style.cssText =
'display:none;position:fixed;top:0;left:0;width:100%;height:100%;z-index:99999999;' +
'background:rgba(0,0,0,0.7);justify-content:center;align-items:center;flex-direction:column;';
document.body.appendChild(pauseOverlay);
// ========== NÚT NỔI ==========
var floatBtn = document.createElement('div');
floatBtn.textContent = '🔧';
floatBtn.style.cssText =
'position:fixed;top:15px;right:15px;z-index:9999999;width:45px;height:45px;' +
'background:#007acc;color:white;border-radius:50%;text-align:center;' +
'line-height:45px;font-size:22px;box-shadow:0 4px 12px rgba(0,0,0,0.5);' +
'user-select:none;-webkit-user-select:none;' +
'-webkit-tap-highlight-color:transparent;';
var dragData = { dragging: false, sx:0, sy:0, sl:0, st:0 };
floatBtn.addEventListener('touchstart', function(e) {
var t = e.touches[0];
dragData.sx = t.clientX; dragData.sy = t.clientY;
dragData.sl = floatBtn.offsetLeft; dragData.st = floatBtn.offsetTop;
dragData.dragging = false;
});
floatBtn.addEventListener('touchmove', function(e) {
var t = e.touches[0];
if (Math.abs(t.clientX - dragData.sx) > 5 || Math.abs(t.clientY - dragData.sy) > 5) {
dragData.dragging = true;
floatBtn.style.left = Math.max(0, Math.min(window.innerWidth - 45, dragData.sl + t.clientX - dragData.sx)) + 'px';
floatBtn.style.top = Math.max(0, Math.min(window.innerHeight - 45, dragData.st + t.clientY - dragData.sy)) + 'px';
floatBtn.style.right = 'auto';
}
});
floatBtn.addEventListener('touchend', function() {
if (!dragData.dragging) togglePanel();
});
document.body.appendChild(floatBtn);
// ========== PANEL ==========
var panel = document.createElement('div');
panel.id = 'dtm-panel';
panel.innerHTML =
'<div id="dtm-header">' +
'<span>🔧 DevTools</span>' +
'<div style="display:flex;gap:3px;flex-wrap:wrap;">' +
'<button id="dtm-tab-console" class="dtm-tab active">📜 JS</button>' +
'<button id="dtm-tab-network" class="dtm-tab">🌐 Net</button>' +
'<button id="dtm-tab-sources" class="dtm-tab">📁 Src</button>' +
'<button id="dtm-tab-storage" class="dtm-tab">💾 Stg</button>' +
'<button id="dtm-tab-iframe" class="dtm-tab">🖼️ Iframe</button>' +
'<button id="dtm-btn-pause">⏸️ Pause</button>' +
'<button id="dtm-btn-close">❌</button>' +
'</div>' +
'</div>' +
'<div id="dtm-log"></div>' +
'<textarea id="dtm-input" placeholder="Gõ lệnh JS rồi Enter..."></textarea>';
panel.style.cssText =
'position:fixed;bottom:0;left:0;right:0;height:45vh;background:#1e1e1e;' +
'color:#d4d4d4;font-family:monospace;font-size:11px;z-index:9999998;' +
'display:none;flex-direction:column;border-top:3px solid #007acc;' +
'border-radius:12px 12px 0 0;overflow:hidden;';
document.body.appendChild(panel);
// Style
var styleEl = document.createElement('style');
styleEl.textContent =
'#dtm-header{display:flex;justify-content:space-between;align-items:center;padding:8px;' +
'background:#252526;border-bottom:1px solid #3e3e3e;flex-shrink:0;flex-wrap:wrap;gap:4px;}' +
'#dtm-header span{font-weight:bold;color:#007acc;font-size:13px;}' +
'.dtm-tab{background:#3e3e3e;color:#ccc;border:1px solid #555;padding:5px 8px;' +
'border-radius:3px;font-size:10px;white-space:nowrap;}' +
'.dtm-tab.active{background:#007acc;color:white;}' +
'#dtm-log{flex:1;overflow-y:auto;padding:8px;font-size:10px;line-height:1.4;' +
'white-space:pre-wrap;word-break:break-all;}' +
'#dtm-input{width:100%;background:#2d2d2d;color:#4fc1ff;border:none;' +
'border-top:1px solid #3e3e3e;padding:10px;font-family:monospace;font-size:13px;' +
'outline:none;box-sizing:border-box;flex-shrink:0;}' +
'.dtm-i{color:#4fc1ff;}.dtm-w{color:#ffcc00;}.dtm-e{color:#ff6b6b;}' +
'.dtm-s{color:#4caf50;}.dtm-n{color:#ce9178;}';
document.head.appendChild(styleEl);
// ========== REFS ==========
function G(id) { return document.getElementById(id); }
function logEl() { return G('dtm-log'); }
function inputEl() { return G('dtm-input'); }
// ========== TOGGLE ==========
function togglePanel() {
var p = G('dtm-panel');
if (p.style.display === 'none' || p.style.display === '') {
p.style.display = 'flex';
floatBtn.textContent = '▼';
floatBtn.style.background = '#e74c3c';
} else {
p.style.display = 'none';
floatBtn.textContent = '🔧';
floatBtn.style.background = '#007acc';
}
}
// ========== LOG ==========
function dtLog(msg, cls) {
cls = cls || 'i';
var l = logEl();
if (!l) return;
var d = document.createElement('div');
d.className = 'dtm-' + cls;
d.textContent = '[' + new Date().toLocaleTimeString() + '] ' + msg;
l.appendChild(d);
l.scrollTop = l.scrollHeight;
}
// ========== OVERRIDE CONSOLE ==========
var _cl = console.log.bind(console);
var _cw = console.warn.bind(console);
var _ce = console.error.bind(console);
console.log = function() {
_cl.apply(console, arguments);
dtLog([].slice.call(arguments).map(function(a) {
return typeof a === 'object' ? JSON.stringify(a) : String(a);
}).join(' '), 'i');
};
console.warn = function() { _cw.apply(console, arguments); dtLog([].slice.call(arguments).join(' '), 'w'); };
console.error = function() { _ce.apply(console, arguments); dtLog([].slice.call(arguments).join(' '), 'e'); };
// ========== NETWORK ==========
var of = window.fetch;
window.fetch = function() {
var url = typeof arguments[0] === 'string' ? arguments[0] : arguments[0].url;
dtLog('📡 → ' + url, 'n');
return of.apply(this, arguments).then(function(r) {
dtLog('📡 ← ' + r.status + ' ' + url, 'n');
return r;
});
};
var oxo = XMLHttpRequest.prototype.open;
var oxs = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function(m, url) { this._u = url; this._m = m; return oxo.apply(this, arguments); };
XMLHttpRequest.prototype.send = function() {
var s = this;
dtLog('📡 ' + s._m + ' → ' + s._u, 'n');
s.addEventListener('load', function() { dtLog('📡 ← ' + s.status, 'n'); });
return oxs.apply(s, arguments);
};
// ========== IFRAME ==========
function getGameWindow() {
var ifs = document.querySelectorAll('iframe');
for (var i = 0; i < ifs.length; i++) {
try { if (ifs[i].contentWindow) return ifs[i].contentWindow; } catch(e) {}
}
return window;
}
// ========== EXEC ==========
function exec(cmd) {
var ctx = getGameWindow();
try {
var r = (function() { return eval(cmd); }).call(ctx);
if (r !== undefined) dtLog(String(r), 's');
} catch(e) {
dtLog('❌ ' + e.message, 'e');
}
}
// ========== PAUSE THẬT (debugger) ==========
var pauseActive = false;
var pauseDebuggerInterval = null;
function doPause() {
if (pauseActive) return;
pauseActive = true;
// Hiện overlay
G('dtm-pause-overlay').style.display = 'flex';
G('dtm-pause-info').textContent = 'Game bị đóng băng - Debugger active';
G('dtm-btn-pause').textContent = '▶️ Resume';
G('dtm-btn-pause').style.background = '#006400';
// Chạy debugger liên tục để giữ trạng thái pause
// Mỗi lần engine gặp debugger; nó sẽ dừng
pauseDebuggerInterval = setInterval(function() {
debugger; // ⬅️ ĐÂY LÀ PAUSE THẬT
}, 50);
dtLog('⏸️ PAUSE - debugger activated', 'w');
}
function doResume() {
if (!pauseActive) return;
pauseActive = false;
// Tắt overlay
G('dtm-pause-overlay').style.display = 'none';
G('dtm-btn-pause').textContent = '⏸️ Pause';
G('dtm-btn-pause').style.background = '#8b0000';
// Dừng debugger loop
if (pauseDebuggerInterval) {
clearInterval(pauseDebuggerInterval);
pauseDebuggerInterval = null;
}
dtLog('▶️ RESUME', 's');
}
// ========== SOURCES TAB ==========
function showSources() {
var l = logEl();
l.innerHTML = '';
dtLog('📁 SCRIPTS:', 's');
var ctx = getGameWindow();
var doc;
try { doc = ctx.document; } catch(e) { doc = document; }
var scripts = doc.querySelectorAll('script[src]');
scripts.forEach(function(s, i) {
dtLog(' 📄 ' + (s.src.split('/').pop() || 'script_' + i), 'i');
});
dtLog('📁 INLINE SCRIPTS: ' + doc.querySelectorAll('script:not([src])').length, 'i');
dtLog('📁 CANVAS: ' + doc.querySelectorAll('canvas').length, 'i');
// Thử lấy danh sách global vars
var keys = Object.keys(ctx).filter(function(k) {
return !k.startsWith('webkit') && !k.startsWith('on') && typeof ctx[k] !== 'function';
});
dtLog('📁 GLOBAL VARS: ' + keys.length, 'i');
keys.slice(0, 30).forEach(function(k) {
var v = ctx[k];
var t = typeof v;
if (t === 'object' && v !== null) {
dtLog(' 📦 ' + k + ': ' + (Array.isArray(v) ? 'Array['+v.length+']' : 'Object{...}'), 'i');
} else if (t !== 'function') {
dtLog(' 📌 ' + k + ': ' + String(v).substring(0, 80), 'i');
}
});
}
// ========== STORAGE ==========
function showStorage() {
var l = logEl();
l.innerHTML = '';
try {
var ls = getGameWindow().localStorage || localStorage;
dtLog('💾 LOCAL STORAGE (' + ls.length + ' keys):', 's');
for (var i = 0; i < ls.length; i++) {
var k = ls.key(i);
dtLog(' ' + k + ': ' + ls.getItem(k).substring(0, 150), 'i');
}
} catch(e) { dtLog('Lỗi: ' + e.message, 'e'); }
}
// ========== IFRAME INFO ==========
function showIframe() {
var l = logEl();
l.innerHTML = '';
var ifs = document.querySelectorAll('iframe');
dtLog('🖼️ Iframes: ' + ifs.length, 'i');
ifs.forEach(function(f, i) {
dtLog(' #' + i + ': ' + (f.src || '(no src)'), 'i');
try {
dtLog(' Canvas: ' + f.contentDocument.querySelectorAll('canvas').length, 's');
} catch(e) {
dtLog(' ⚠️ Cross-origin', 'w');
}
});
}
// ========== TAB SWITCH ==========
function switchTab(tab) {
['console','network','sources','storage','iframe'].forEach(function(t) {
var b = G('dtm-tab-' + t);
if (b) b.classList.remove('active');
});
var a = G('dtm-tab-' + tab);
if (a) a.classList.add('active');
if (logEl()) logEl().innerHTML = '';
switch(tab) {
case 'network': dtLog('🌐 Đang theo dõi...', 'i'); break;
case 'sources': showSources(); break;
case 'storage': showStorage(); break;
case 'iframe': showIframe(); break;
default: dtLog('📜 Sẵn sàng', 'i');
}
}
// ========== EVENT BINDINGS ==========
G('dtm-btn-close').addEventListener('click', togglePanel);
G('dtm-btn-pause').addEventListener('click', function() {
pauseActive ? doResume() : doPause();
});
// Resume từ overlay
G('dtm-resume-btn').addEventListener('click', doResume);
G('dtm-step-btn').addEventListener('click', function() {
// Step: resume 1 nhịp rồi pause lại
doResume();
setTimeout(doPause, 100);
});
G('dtm-tab-console').addEventListener('click', function() { switchTab('console'); });
G('dtm-tab-network').addEventListener('click', function() { switchTab('network'); });
G('dtm-tab-sources').addEventListener('click', function() { switchTab('sources'); });
G('dtm-tab-storage').addEventListener('click', function() { switchTab('storage'); });
G('dtm-tab-iframe').addEventListener('click', function() { switchTab('iframe'); });
G('dtm-input').addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
var cmd = this.value.trim();
if (cmd) {
dtLog('> ' + cmd, 'i');
exec(cmd);
this.value = '';
}
}
});
// ========== STARTUP ==========
dtLog('✅ DevTools Mobile v2 sẵn sàng', 's');
dtLog('👆 Bấm 🔧 để mở panel', 'i');
dtLog('⏸️ Nút Pause = debugger thật', 'i');
});
})();