// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Note: The following chrome:// URLs are not actually fetched at runtime. They
// are handled in
// components/security_interstitials/core/browser/resources:bundle_js, which
// finds the correct files and inlines them.
import {HIDDEN_CLASS, preventDefaultOnPoundLinkClicks, SecurityInterstitialCommandId, sendCommand} from 'chrome://interstitials/common/resources/interstitial_common.js';
import {mobileNav} from 'chrome://interstitials/common/resources/interstitial_mobile_nav.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {setupEnhancedProtectionMessage} from './enhanced_protection_message.js';
import {setupExtendedReportingCheckbox} from './extended_reporting.js';
import {setupSSLDebuggingInfo} from './ssl.js';
// This is the shared code for the new (Chrome 37) security interstitials. It is
// used for both SSL interstitials and Safe Browsing interstitials.
let expandedDetails = false;
let keyPressState = 0;
// Only begin clickjacking delay tracking when the DOM contents have
// fully loaded.
let timePageLastFocused = null;
// The amount of delay (in ms) before the proceed button accepts
// a "click" event.
const PROCEED_CLICKJACKING_DELAY = 500;
/**
* This checks whether the clickjacking delay has been passed
* since page was first loaded or last focused.
* @return {boolean} Whether the clickjacking delay has passed or not.
*/
function clickjackingDelayHasPassed() {
return (
timePageLastFocused != null &&
(window.performance.now() - timePageLastFocused >=
PROCEED_CLICKJACKING_DELAY));
}
/**
* This allows errors to be skippped by typing a secret phrase into the page.
* @param {string} e The key that was just pressed.
*/
function handleKeypress(e) {
// HTTPS errors are serious and should not be ignored. For testing purposes,
// other approaches are both safer and have fewer side-effects.
// See https://goo.gl/ZcZixP for more details.
const BYPASS_SEQUENCE = window.atob('dGhpc2lzdW5zYWZl');
if (BYPASS_SEQUENCE.charCodeAt(keyPressState) === e.keyCode) {
keyPressState++;
if (keyPressState === BYPASS_SEQUENCE.length) {
sendCommand(SecurityInterstitialCommandId.CMD_PROCEED);
keyPressState = 0;
}
} else {
keyPressState = 0;
}
}
function setupEvents() {
// `loadTimeDataRaw` is injected to the `window` scope from C++.
loadTimeData.data = window.loadTimeDataRaw;
const overridable = loadTimeData.getBoolean('overridable');
const interstitialType = loadTimeData.getString('type');
const ssl = interstitialType === 'SSL';
const captivePortal = interstitialType === 'CAPTIVE_PORTAL';
const badClock = ssl && loadTimeData.getBoolean('bad_clock');
const lookalike = interstitialType === 'LOOKALIKE';
const billing =
interstitialType === 'SAFEBROWSING' && loadTimeData.getBoolean('billing');
const blockedInterception = interstitialType === 'BLOCKED_INTERCEPTION';
const insecureForm = interstitialType == 'INSECURE_FORM';
const httpsOnly = interstitialType == 'HTTPS_ONLY';
const enterpriseBlock = interstitialType === 'ENTERPRISE_BLOCK';
const enterpriseWarn = interstitialType === 'ENTERPRISE_WARN';
const supervisedUserVerify = interstitialType === 'SUPERVISED_USER_VERIFY';
const hidePrimaryButton = loadTimeData.getBoolean('hide_primary_button');
const showRecurrentErrorParagraph =
loadTimeData.getBoolean('show_recurrent_error_paragraph');
const body = document.querySelector('#body');
if (ssl || blockedInterception) {
body.classList.add(badClock ? 'bad-clock' : 'ssl');
if (loadTimeData.valueExists('errorCode')) {
const errorCode = document.querySelector('#error-code');
errorCode.textContent = loadTimeData.getString('errorCode');
errorCode.classList.remove(HIDDEN_CLASS);
}
} else if (captivePortal) {
body.classList.add('captive-portal');
} else if (billing) {
body.classList.add('safe-browsing-billing');
} else if (lookalike) {
body.classList.add('lookalike-url');
} else if (insecureForm) {
body.classList.add('insecure-form');
} else if (httpsOnly) {
body.classList.add('https-only');
if (loadTimeData.valueExists('august2024Refresh') &&
loadTimeData.getBoolean('august2024Refresh')) {
body.classList.add('https-only-august2024-refresh');
}
} else if (enterpriseBlock) {
body.classList.add('enterprise-block');
} else if (enterpriseWarn) {
body.classList.add('enterprise-warn');
} else if (supervisedUserVerify) {
body.classList.add('supervised-user-verify');
} else {
body.classList.add('safe-browsing');
// Override the default theme color.
document.querySelector('meta[name=theme-color]')
.setAttribute('content', 'rgb(217, 48, 37)');
}
document.querySelector('#icon').classList.add('icon');
const primaryButton = document.querySelector('#primary-button');
if (hidePrimaryButton) {
primaryButton.classList.add(HIDDEN_CLASS);
} else {
primaryButton.addEventListener('click', function() {
switch (interstitialType) {
case 'CAPTIVE_PORTAL':
case 'SUPERVISED_USER_VERIFY':
sendCommand(SecurityInterstitialCommandId.CMD_OPEN_LOGIN);
break;
case 'SSL':
if (badClock) {
sendCommand(SecurityInterstitialCommandId.CMD_OPEN_DATE_SETTINGS);
} else if (overridable) {
sendCommand(SecurityInterstitialCommandId.CMD_DONT_PROCEED);
} else {
sendCommand(SecurityInterstitialCommandId.CMD_RELOAD);
}
break;
case 'SAFEBROWSING':
case 'ENTERPRISE_BLOCK':
case 'ENTERPRISE_WARN':
case 'ORIGIN_POLICY':
sendCommand(SecurityInterstitialCommandId.CMD_DONT_PROCEED);
break;
case 'HTTPS_ONLY':
case 'INSECURE_FORM':
case 'LOOKALIKE':
sendCommand(SecurityInterstitialCommandId.CMD_DONT_PROCEED);
break;
default:
throw new Error('Invalid interstitial type');
}
});
}
if (lookalike || insecureForm || httpsOnly || enterpriseWarn) {
const proceedButton = document.querySelector('#proceed-button');
proceedButton.classList.remove(HIDDEN_CLASS);
proceedButton.textContent = loadTimeData.getString('proceedButtonText');
proceedButton.addEventListener('click', function(event) {
if (clickjackingDelayHasPassed()) {
sendCommand(SecurityInterstitialCommandId.CMD_PROCEED);
}
});
}
if (lookalike) {
// Lookalike interstitials with a suggested URL have a link in the title:
// "Did you mean <link>example.com</link>?". Handle those clicks. Lookalike
// interstitails without a suggested URL don't have this link.
const dontProceedLink = document.querySelector('#dont-proceed-link');
if (dontProceedLink) {
dontProceedLink.addEventListener('click', function(event) {
sendCommand(SecurityInterstitialCommandId.CMD_DONT_PROCEED);
});
}
}
if (overridable) {
const overrideElement =
document.querySelector(billing ? '#proceed-button' : '#proceed-link');
// Captive portal page isn't overridable.
overrideElement.addEventListener('click', function(event) {
if (!billing || clickjackingDelayHasPassed()) {
sendCommand(SecurityInterstitialCommandId.CMD_PROCEED);
}
});
if (ssl) {
overrideElement.classList.add('small-link');
} else if (billing) {
overrideElement.classList.remove(HIDDEN_CLASS);
overrideElement.textContent = loadTimeData.getString('proceedButtonText');
}
} else if (!ssl) {
document.querySelector('#final-paragraph').classList.add(HIDDEN_CLASS);
}
if (!ssl || !showRecurrentErrorParagraph) {
document.querySelector('#recurrent-error-message')
.classList.add(HIDDEN_CLASS);
} else {
body.classList.add('showing-recurrent-error-message');
}
const diagnosticLink = document.querySelector('#diagnostic-link');
if (diagnosticLink) {
diagnosticLink.addEventListener('click', function(event) {
sendCommand(SecurityInterstitialCommandId.CMD_OPEN_DIAGNOSTIC);
});
}
const learnMoreLink = document.querySelector('#learn-more-link');
if (learnMoreLink) {
learnMoreLink.addEventListener('click', function(event) {
sendCommand(SecurityInterstitialCommandId.CMD_OPEN_HELP_CENTER);
});
}
const detailsButton = document.querySelector('#details-button');
if (captivePortal || billing || lookalike || insecureForm || httpsOnly ||
enterpriseWarn || enterpriseBlock || supervisedUserVerify) {
// Captive portal, billing, lookalike pages, insecure form, enterprise warn,
// enterprise block, and HTTPS only mode interstitials don't
// have details buttons.
detailsButton.classList.add('hidden');
} else {
detailsButton.setAttribute(
'aria-expanded',
!document.querySelector('#details').classList.contains(HIDDEN_CLASS));
detailsButton.addEventListener('click', function(event) {
const hiddenDetails =
document.querySelector('#details').classList.toggle(HIDDEN_CLASS);
detailsButton.setAttribute('aria-expanded', !hiddenDetails);
const mainContent = document.querySelector('#main-content');
if (mobileNav) {
// Details appear over the main content on small screens.
mainContent.classList.toggle(HIDDEN_CLASS, !hiddenDetails);
} else {
mainContent.classList.remove(HIDDEN_CLASS);
}
detailsButton.innerText = hiddenDetails ?
loadTimeData.getString('openDetails') :
loadTimeData.getString('closeDetails');
if (!expandedDetails) {
// Record a histogram entry only the first time that details is opened.
sendCommand(SecurityInterstitialCommandId.CMD_SHOW_MORE_SECTION);
expandedDetails = true;
}
});
}
const reportErrorLink = document.querySelector('#report-error-link');
if (reportErrorLink) {
reportErrorLink.addEventListener('click', function(event) {
sendCommand(SecurityInterstitialCommandId.CMD_REPORT_PHISHING_ERROR);
});
}
if (lookalike) {
console.warn(loadTimeData.getString('lookalikeConsoleMessage'));
}
if (document.getElementById('icon')) {
document.getElementById('icon').classList.add('new-icon');
}
preventDefaultOnPoundLinkClicks();
setupExtendedReportingCheckbox();
setupEnhancedProtectionMessage();
setupSSLDebuggingInfo();
document.addEventListener('keypress', handleKeypress);
// Begin tracking for the clickjacking delay.
timePageLastFocused = window.performance.now();
window.addEventListener(
'focus', () => timePageLastFocused = window.performance.now());
}
document.addEventListener('DOMContentLoaded', setupEvents);