Skip to content
Closed
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
26 changes: 26 additions & 0 deletions core/src/main/resources/org/apache/spark/ui/static/table.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,29 @@ function collapseTableAndButton(thisName, table) {
}
}
/* eslint-enable no-unused-vars */

// Event delegation for thread dump page (CSP-compliant)
$(function() {
// toggleThreadStackTrace on row click
$(document).on("click", "tr.accordion-heading[data-thread-id]", function() {
toggleThreadStackTrace($(this).data("thread-id"), false);
});

// expandAll / collapseAll
$(document).on("click", "[data-action=expandAllThreadStackTrace]", function() {
expandAllThreadStackTrace(true);
});
$(document).on("click", "[data-action=collapseAllThreadStackTrace]", function() {
collapseAllThreadStackTrace($(this).data("toggle-button") !== false);
});

// onMouseOverAndOut
$(document).on("mouseenter mouseleave", "tr.accordion-heading[data-thread-id]", function() {
onMouseOverAndOut($(this).data("thread-id"));
});

// onSearchStringChange
$(document).on("input", "[data-search-input]", function() {
onSearchStringChange();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ function getBaseURI() {

function detailsUINode(isMultiline, message) {
if (isMultiline) {
const span = '<span onclick="this.parentNode.querySelector(\'.stacktrace-details\').classList.toggle(\'collapsed\')" class="expand-details">+details</span>';
const span = '<span data-toggle-details=".stacktrace-details" class="expand-details">+details</span>';
const pre = '<pre>' + message + '</pre>';
const div = '<div class="stacktrace-details collapsed">' + pre + '</div>';
return span + div;
Expand Down
52 changes: 51 additions & 1 deletion core/src/main/resources/org/apache/spark/ui/static/webui.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* limitations under the License.
*/

/* global $ */
/* global $, collapseTableAndButton, loadMore, loadNew, toggleDagViz, togglePlanViz, clickPhysicalPlanDetails */
/* eslint-disable no-unused-vars */
var uiRoot = "";
var appBasePath = "";
Expand Down Expand Up @@ -111,3 +111,53 @@ $(function() {
$(this).toggleClass("description-input-full");
});
});

// Event delegation for CSP-compliant inline event handler replacement.
$(function() {
// collapseTable / collapseTableAndButton
$(document).on("click", "[data-collapse-name]", function() {
var name = $(this).data("collapse-name");
var table = $(this).data("collapse-table");
if ($(this).data("collapse-button")) {
collapseTableAndButton(name, table);
} else {
collapseTable(name, table);
}
});

// toggle details (stage-details, stacktrace-details, expand-details)
$(document).on("click", "[data-toggle-details]", function() {
var selector = $(this).data("toggle-details");
this.parentNode.querySelector(selector).classList.toggle("collapsed");
});

// toggle sub-execution list (tr two siblings away from parent tr)
$(document).on("click", "[data-toggle-sub-execution]", function() {
$(this).closest("tr").nextAll("tr.sub-execution-list").first().toggleClass("collapsed");
});

// kill links with confirmation
$(document).on("click", "a.kill-link[data-kill-message]", function(e) {
if (!window.confirm($(this).data("kill-message"))) {
e.preventDefault();
} else if ($(this).closest("form").length > 0) {
e.preventDefault();
$(this).closest("form").submit();
}
});

// loadMore / loadNew buttons
$(document).on("click", ".log-more-btn", function() { loadMore(); });
$(document).on("click", ".log-new-btn", function() { loadNew(); });

// toggleDagViz
$(document).on("click", ".expand-dag-viz[data-forjob]", function() {
toggleDagViz($(this).data("forjob"));
});

// togglePlanViz / clickPhysicalPlanDetails
$(document).on("click", "[data-action=togglePlanViz]", function() { togglePlanViz(); });
$(document).on("click", "[data-action=clickPhysicalPlanDetails]", function() {
clickPhysicalPlanDetails();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import scala.xml.{Node, Unparsed}
import jakarta.servlet.http.HttpServletRequest

import org.apache.spark.status.api.v1.ApplicationInfo
import org.apache.spark.ui.{UIUtils, WebUIPage}
import org.apache.spark.ui.{CspNonce, UIUtils, WebUIPage}
import org.apache.spark.ui.UIUtils.formatImportJavaScript

private[history] class HistoryPage(parent: HistoryServer) extends WebUIPage("") {
Expand Down Expand Up @@ -74,7 +74,8 @@ private[history] class HistoryPage(parent: HistoryServer) extends WebUIPage("")
request, "/static/dataTables.rowsGroup.js")}></script> ++
<script type="module" src={UIUtils.prependBaseUri(
request, "/static/historypage.js")} ></script> ++
<script type="module">{Unparsed(js)}</script> ++ <div id="history-summary"></div>
<script type="module" nonce={CspNonce.get}>{Unparsed(js)}</script> ++
<div id="history-summary"></div>
} else if (requestedIncomplete) {
<h4>No incomplete applications found!</h4>
} else if (eventLogsUnderProcessCount > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import jakarta.servlet.http.HttpServletRequest
import org.apache.spark.SparkConf
import org.apache.spark.deploy.Utils.{getLog, DEFAULT_BYTES}
import org.apache.spark.internal.Logging
import org.apache.spark.ui.{UIUtils, WebUIPage}
import org.apache.spark.ui.{CspNonce, UIUtils, WebUIPage}

private[history] class LogPage(conf: SparkConf) extends WebUIPage("logPage") with Logging {
def render(request: HttpServletRequest): Seq[Node] = {
Expand All @@ -42,12 +42,12 @@ private[history] class LogPage(conf: SparkConf) extends WebUIPage("logPage") wit
</span>

val moreButton =
<button type="button" onclick={"loadMore()"} class="log-more-btn btn btn-secondary">
<button type="button" class="log-more-btn btn btn-secondary">
Load More
</button>

val newButton =
<button type="button" onclick={"loadNew()"} class="log-new-btn btn btn-secondary">
<button type="button" class="log-new-btn btn btn-secondary">
Load New
</button>

Expand All @@ -71,7 +71,7 @@ private[history] class LogPage(conf: SparkConf) extends WebUIPage("logPage") wit
{alert}
<div>{newButton}</div>
</div>
<script>{Unparsed(jsOnload)}</script>
<script nonce={CspNonce.get}>{Unparsed(jsOnload)}</script>
</div>

UIUtils.basicSparkPage(request, content, logType + " log page for history server")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ private[ui] class ApplicationPage(parent: MasterWebUI) extends WebUIPage("app")
<div class="row"> <!-- Executors -->
<div class="col-12">
<span class="collapse-aggregated-executors collapse-table"
onClick="collapseTable('collapse-aggregated-executors','aggregated-executors')">
data-collapse-name="collapse-aggregated-executors"
data-collapse-table="aggregated-executors">
<h4>
<span class="collapse-table-arrow arrow-open"></span>
<a>Executor Summary ({allExecutors.length})</a>
Expand All @@ -128,8 +129,8 @@ private[ui] class ApplicationPage(parent: MasterWebUI) extends WebUIPage("app")
{
if (removedExecutors.nonEmpty) {
<span class="collapse-aggregated-removedExecutors collapse-table"
onClick="collapseTable('collapse-aggregated-removedExecutors',
'aggregated-removedExecutors')">
data-collapse-name="collapse-aggregated-removedExecutors"
data-collapse-table="aggregated-removedExecutors">
<h4>
<span class="collapse-table-arrow arrow-open"></span>
<a>Removed Executors ({removedExecutors.length})</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ private[ui] class EnvironmentPage(
</div>
<span>
<span class="collapse-aggregated-runtimeInformation collapse-table"
onClick="collapseTable('collapse-aggregated-runtimeInformation',
'aggregated-runtimeInformation')">
data-collapse-name="collapse-aggregated-runtimeInformation"
data-collapse-table="aggregated-runtimeInformation">
<h4>
<span class="collapse-table-arrow arrow-open"></span>
<a>Runtime Information</a>
Expand All @@ -77,8 +77,8 @@ private[ui] class EnvironmentPage(
{runtimeInformationTable}
</div>
<span class="collapse-aggregated-sparkProperties collapse-table"
onClick="collapseTable('collapse-aggregated-sparkProperties',
'aggregated-sparkProperties')">
data-collapse-name="collapse-aggregated-sparkProperties"
data-collapse-table="aggregated-sparkProperties">
<h4>
<span class="collapse-table-arrow arrow-open"></span>
<a>Spark Properties</a>
Expand All @@ -88,8 +88,8 @@ private[ui] class EnvironmentPage(
{sparkPropertiesTable}
</div>
<span class="collapse-aggregated-hadoopProperties collapse-table"
onClick="collapseTable('collapse-aggregated-hadoopProperties',
'aggregated-hadoopProperties')">
data-collapse-name="collapse-aggregated-hadoopProperties"
data-collapse-table="aggregated-hadoopProperties">
<h4>
<span class="collapse-table-arrow arrow-closed"></span>
<a>Hadoop Properties</a>
Expand All @@ -99,8 +99,8 @@ private[ui] class EnvironmentPage(
{hadoopPropertiesTable}
</div>
<span class="collapse-aggregated-systemProperties collapse-table"
onClick="collapseTable('collapse-aggregated-systemProperties',
'aggregated-systemProperties')">
data-collapse-name="collapse-aggregated-systemProperties"
data-collapse-table="aggregated-systemProperties">
<h4>
<span class="collapse-table-arrow arrow-closed"></span>
<a>System Properties</a>
Expand All @@ -110,8 +110,8 @@ private[ui] class EnvironmentPage(
{systemPropertiesTable}
</div>
<span class="collapse-aggregated-metricsProperties collapse-table"
onClick="collapseTable('collapse-aggregated-metricsProperties',
'aggregated-metricsProperties')">
data-collapse-name="collapse-aggregated-metricsProperties"
data-collapse-table="aggregated-metricsProperties">
<h4>
<span class="collapse-table-arrow arrow-closed"></span>
<a>Metrics Properties</a>
Expand All @@ -121,8 +121,8 @@ private[ui] class EnvironmentPage(
{metricsPropertiesTable}
</div>
<span class="collapse-aggregated-classpathEntries collapse-table"
onClick="collapseTable('collapse-aggregated-classpathEntries',
'aggregated-classpathEntries')">
data-collapse-name="collapse-aggregated-classpathEntries"
data-collapse-table="aggregated-classpathEntries">
<h4>
<span class="collapse-table-arrow arrow-closed"></span>
<a>Classpath Entries</a>
Expand All @@ -132,8 +132,8 @@ private[ui] class EnvironmentPage(
{classpathEntriesTable}
</div>
<span class="collapse-aggregated-environmentVariables collapse-table"
onClick="collapseTable('collapse-aggregated-environmentVariables',
'aggregated-environmentVariables')">
data-collapse-name="collapse-aggregated-environmentVariables"
data-collapse-table="aggregated-environmentVariables">
<h4>
<span class="collapse-table-arrow arrow-closed"></span>
<a>Environment Variables</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import jakarta.servlet.http.HttpServletRequest

import org.apache.spark.deploy.Utils.{getLog, DEFAULT_BYTES}
import org.apache.spark.internal.Logging
import org.apache.spark.ui.{UIUtils, WebUIPage}
import org.apache.spark.ui.{CspNonce, UIUtils, WebUIPage}

private[ui] class LogPage(parent: MasterWebUI) extends WebUIPage("logPage") with Logging {
def render(request: HttpServletRequest): Seq[Node] = {
Expand All @@ -41,12 +41,12 @@ private[ui] class LogPage(parent: MasterWebUI) extends WebUIPage("logPage") with
</span>

val moreButton =
<button type="button" onclick={"loadMore()"} class="log-more-btn btn btn-secondary">
<button type="button" class="log-more-btn btn btn-secondary">
Load More
</button>

val newButton =
<button type="button" onclick={"loadNew()"} class="log-new-btn btn btn-secondary">
<button type="button" class="log-new-btn btn btn-secondary">
Load New
</button>

Expand All @@ -70,7 +70,7 @@ private[ui] class LogPage(parent: MasterWebUI) extends WebUIPage("logPage") with
{alert}
<div>{newButton}</div>
</div>
<script>{Unparsed(jsOnload)}</script>
<script nonce={CspNonce.get}>{Unparsed(jsOnload)}</script>
</div>

UIUtils.basicSparkPage(request, content, logType + " log page for master")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,8 @@ private[ui] class MasterPage(parent: MasterWebUI) extends WebUIPage("") {
<div class="row">
<div class="col-12">
<span class="collapse-aggregated-workers collapse-table"
onClick="collapseTable('collapse-aggregated-workers','aggregated-workers')">
data-collapse-name="collapse-aggregated-workers"
data-collapse-table="aggregated-workers">
<h4>
<span class="collapse-table-arrow arrow-open"></span>
<a>Workers ({workers.length})</a>
Expand All @@ -197,7 +198,8 @@ private[ui] class MasterPage(parent: MasterWebUI) extends WebUIPage("") {
<div class="row">
<div class="col-12">
<span id="running-app" class="collapse-aggregated-activeApps collapse-table"
onClick="collapseTable('collapse-aggregated-activeApps','aggregated-activeApps')">
data-collapse-name="collapse-aggregated-activeApps"
data-collapse-table="aggregated-activeApps">
<h4>
<span class="collapse-table-arrow arrow-open"></span>
<a>Running Applications ({activeApps.length})</a>
Expand All @@ -214,8 +216,8 @@ private[ui] class MasterPage(parent: MasterWebUI) extends WebUIPage("") {
<div class="row">
<div class="col-12">
<span class="collapse-aggregated-activeDrivers collapse-table"
onClick="collapseTable('collapse-aggregated-activeDrivers',
'aggregated-activeDrivers')">
data-collapse-name="collapse-aggregated-activeDrivers"
data-collapse-table="aggregated-activeDrivers">
<h4>
<span class="collapse-table-arrow arrow-open"></span>
<a>Running Drivers ({activeDrivers.length})</a>
Expand All @@ -233,8 +235,8 @@ private[ui] class MasterPage(parent: MasterWebUI) extends WebUIPage("") {
<div class="row">
<div class="col-12">
<span id="completed-app" class="collapse-aggregated-completedApps collapse-table"
onClick="collapseTable('collapse-aggregated-completedApps',
'aggregated-completedApps')">
data-collapse-name="collapse-aggregated-completedApps"
data-collapse-table="aggregated-completedApps">
<h4>
<span class="collapse-table-arrow arrow-open"></span>
<a>Completed Applications ({completedApps.length})</a>
Expand All @@ -252,8 +254,8 @@ private[ui] class MasterPage(parent: MasterWebUI) extends WebUIPage("") {
<div class="row">
<div class="col-12">
<span class="collapse-aggregated-completedDrivers collapse-table"
onClick="collapseTable('collapse-aggregated-completedDrivers',
'aggregated-completedDrivers')">
data-collapse-name="collapse-aggregated-completedDrivers"
data-collapse-table="aggregated-completedDrivers">
<h4>
<span class="collapse-table-arrow arrow-open"></span>
<a>Completed Drivers ({completedDrivers.length})</a>
Expand Down Expand Up @@ -300,13 +302,12 @@ private[ui] class MasterPage(parent: MasterWebUI) extends WebUIPage("") {
private def appRow(app: ApplicationInfo): Seq[Node] = {
val killLink = if (parent.killEnabled &&
(app.state == ApplicationState.RUNNING || app.state == ApplicationState.WAITING)) {
val confirm =
s"if (window.confirm('Are you sure you want to kill application ${app.id} ?')) " +
"{ this.parentNode.submit(); return true; } else { return false; }"
<form action="app/kill/" method="POST" style="display:inline">
<input type="hidden" name="id" value={app.id}/>
<input type="hidden" name="terminate" value="true"/>
<a href="#" onclick={confirm} class="kill-link">(kill)</a>
<a href="#"
data-kill-message={s"Are you sure you want to kill application ${app.id} ?"}
class="kill-link">(kill)</a>
</form>
}
<tr>
Expand Down Expand Up @@ -350,13 +351,12 @@ private[ui] class MasterPage(parent: MasterWebUI) extends WebUIPage("") {
val killLink = if (parent.killEnabled &&
(driver.state == DriverState.RUNNING ||
driver.state == DriverState.SUBMITTED)) {
val confirm =
s"if (window.confirm('Are you sure you want to kill driver ${driver.id} ?')) " +
"{ this.parentNode.submit(); return true; } else { return false; }"
<form action="driver/kill/" method="POST" style="display:inline">
<input type="hidden" name="id" value={driver.id}/>
<input type="hidden" name="terminate" value="true"/>
<a href="#" onclick={confirm} class="kill-link">(kill)</a>
<a href="#"
data-kill-message={s"Are you sure you want to kill driver ${driver.id} ?"}
class="kill-link">(kill)</a>
</form>
}
<tr>
Expand Down
Loading