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

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.wicket.markup.head;

import org.apache.wicket.util.value.AttributeMap;

/**
* Special header NON-PRIORITY item for ajax evaluations
*
* @author Andrew Kondratev
*/
public class AfterDomRenderAjaxJavaScriptHeaderItem extends JavaScriptContentHeaderItem
{
/**
* Creates a new {@code AfterDomRenderAjaxJavaScriptHeaderItem}.
*
* @param javaScript
* javascript content to be rendered.
*/
protected AfterDomRenderAjaxJavaScriptHeaderItem(CharSequence javaScript)
{
super(javaScript, null, null);
}

public static AfterDomRenderAjaxJavaScriptHeaderItem forScript(CharSequence javaScript) {
return new AfterDomRenderAjaxJavaScriptHeaderItem(javaScript);
}

@Override
protected void renderExtraAttributes(AttributeMap attributes)
{
// evaluate this header item after components were rendered
attributes.add("data-wicket-evaluation", "after");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public void render(Response response)
attributes.putAttribute(JavaScriptUtils.ATTR_TYPE, "text/javascript");
attributes.putAttribute(JavaScriptUtils.ATTR_ID, getId());
attributes.putAttribute(JavaScriptUtils.ATTR_CSP_NONCE, getNonce());
renderExtraAttributes(attributes);
JavaScriptUtils.writeInlineScript(response, getJavaScript(), attributes);

if (hasCondition)
Expand All @@ -82,6 +83,10 @@ public void render(Response response)
}
}

protected void renderExtraAttributes(AttributeMap attributes) {
// no-op by default
}

@Override
public Iterable<?> getRenderTokens()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public void render(HeaderItem item)

String policy = getContentSecurityPolicy(nonce);

super.render(MetaDataHeaderItem.forHttpEquiv(CONTENT_SECURITY_POLICY, policy));
super.render(MetaDataHeaderItem.forHttpEquiv(CONTENT_SECURITY_POLICY, policy).addTagAttribute("id", "meta-csp"));
}

((AbstractCspHeaderItem)item).setNonce(nonce);
Expand All @@ -88,6 +88,6 @@ public void render(HeaderItem item)
*/
protected String getContentSecurityPolicy(String nonce)
{
return String.format("script-src 'unsafe-eval' 'strict-dynamic' 'nonce-%1$s'; style-src 'nonce-%1$s';", nonce);
return String.format("script-src 'strict-dynamic' 'nonce-%1$s'; style-src 'nonce-%1$s';", nonce);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import org.apache.wicket.util.lang.Args;
import org.apache.wicket.util.lang.Generics;
import org.apache.wicket.util.string.AppendingStringBuffer;
import org.apache.wicket.util.string.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -57,11 +58,9 @@
* <p>
* The elements of such response are:
* <ul>
* <li>priority-evaluate - an item of the prepend JavaScripts</li>
* <li>component - the markup of the updated component</li>
* <li>evaluate - an item of the onDomReady and append JavaScripts</li>
* <li>header-contribution - all HeaderItems which have been contributed in
* components' and their behaviors' #renderHead(Component, IHeaderResponse)</li>
* components' and their behaviors' #renderHead(Component, IHeaderResponse) and also with {@link #appendJavaScript} and {@link #prependJavaScript}</li>
* </ul>
*/
public abstract class PartialPageUpdate
Expand Down Expand Up @@ -92,6 +91,7 @@ public abstract class PartialPageUpdate
*/
protected final Map<String, Component> markupIdToComponent = new LinkedHashMap<String, Component>();


/**
* A flag that indicates that components cannot be added anymore.
* See https://issues.apache.org/jira/browse/WICKET-3564
Expand Down Expand Up @@ -155,6 +155,8 @@ public void writeTo(final Response response, final String encoding)

onBeforeRespond(response);

setUpHeader();

// process added components
writeComponents(response, encoding);

Expand All @@ -171,6 +173,8 @@ public void writeTo(final Response response, final String encoding)
evaluationScripts.addAll(appendJavaScripts);
writeNormalEvaluations(response, evaluationScripts);

closeHeader(response);

writeFooter(response, encoding);
} finally {
if (header != null && originalHeaderContainer!= null) {
Expand Down Expand Up @@ -259,11 +263,14 @@ private void writeComponents(Response response, String encoding)
{
writeComponent(response, component.getAjaxRegionMarkupId(), component, encoding);
}
}

private void closeHeader(Response response)
{
if (header != null)
{
RequestCycle cycle = RequestCycle.get();

// some header responses buffer all calls to render*** until close is called.
// when they are closed, they do something (i.e. aggregate all JS resource urls to a
// single url), and then "flush" (by writing to the real response) before closing.
Expand Down Expand Up @@ -550,15 +557,6 @@ public IHeaderResponse getHeaderResponse()
*/
protected void writeHeaderContribution(final Response response, final Component component)
{
headerRendering = true;

// create the htmlheadercontainer if needed
if (header == null)
{
header = new PartialHtmlHeaderContainer(this);
page.addOrReplace(header);
}

RequestCycle requestCycle = component.getRequestCycle();

// save old response, set new
Expand All @@ -579,6 +577,17 @@ protected void writeHeaderContribution(final Response response, final Component
headerRendering = false;
}

private void setUpHeader()
{
headerRendering = true;
// create the htmlheadercontainer if needed
if (header == null)
{
header = new PartialHtmlHeaderContainer(this);
page.addOrReplace(header);
}
}

/**
* Sets the Content-Type header to indicate the type of the response.
*
Expand Down Expand Up @@ -612,7 +621,7 @@ private static class PartialHtmlHeaderContainer extends HtmlHeaderContainer
/**
* Constructor.
*
* @param update
* @param pageUpdate
* the partial page update
*/
public PartialHtmlHeaderContainer(PartialPageUpdate pageUpdate)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@
*/
package org.apache.wicket.page;

import java.util.Collection;

import org.apache.wicket.Component;
import org.apache.wicket.Page;
import org.apache.wicket.markup.head.AfterDomRenderAjaxJavaScriptHeaderItem;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.request.Response;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.http.WebResponse;
import org.apache.wicket.util.string.Strings;

import java.util.Collection;

/**
* A {@link PartialPageUpdate} that serializes itself to XML.
*/
Expand Down Expand Up @@ -121,17 +123,16 @@ protected void writeHeaderContribution(Response response)
@Override
protected void writeNormalEvaluations(final Response response, final Collection<CharSequence> scripts)
{
writeEvaluations(response, "evaluate", scripts);

writeEvaluations(scripts, false);
}

@Override
protected void writePriorityEvaluations(Response response, Collection<CharSequence> scripts)
{
writeEvaluations(response, "priority-evaluate", scripts);
writeEvaluations(scripts, true);
}

private void writeEvaluations(final Response response, String elementName, Collection<CharSequence> scripts)
private void writeEvaluations(Collection<CharSequence> scripts, boolean isPriority)
{
if (scripts.size() > 0)
{
Expand All @@ -140,32 +141,19 @@ private void writeEvaluations(final Response response, String elementName, Colle
{
combinedScript.append("(function(){").append(script).append("})();");
}
writeEvaluation(elementName, response, combinedScript);
writeEvaluation(combinedScript, isPriority);
}
}

/**
* @param invocation
* type of invocation tag, usually {@literal evaluate} or
* {@literal priority-evaluate}
* @param response
* @param js
*/
private void writeEvaluation(final String invocation, final Response response, final CharSequence js)
private void writeEvaluation(final CharSequence js, boolean isPriority)
{
response.write("<");
response.write(invocation);
response.write(">");

response.write("<![CDATA[");
response.write(encode(js));
response.write("]]>");

response.write("</");
response.write(invocation);
response.write(">");

bodyBuffer.reset();
if (header != null)
{
header.getHeaderResponse().render(
isPriority
? JavaScriptHeaderItem.forScript(js, null)
: AfterDomRenderAjaxJavaScriptHeaderItem.forScript(js));
}
}

protected CharSequence encode(CharSequence str)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.apache.wicket.util.string.AppendingStringBuffer;
import org.apache.wicket.core.util.string.JavaScriptUtils;
import org.apache.wicket.util.string.interpolator.MapVariableInterpolator;
import org.apache.wicket.util.value.AttributeMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -53,6 +54,10 @@
*/
public class AjaxServerAndClientTimeFilter implements IResponseFilter
{
private static final String HEADER_CONTRIBUTION_START = "<header-contribution><![CDATA[<head xmlns:wicket=\"http://wicket.apache.org\">";
private static final String HEADER_CONTRIBUTION_END = "</head>]]></header-contribution>";
public static final String CDATA_SCRIPT_END = "/*]]]]><![CDATA[>*/</script>";

private static Logger log = LoggerFactory.getLogger(AjaxServerAndClientTimeFilter.class);

/**
Expand All @@ -66,34 +71,62 @@ public AppendingStringBuffer filter(AppendingStringBuffer responseBuffer)
int ajaxStart = responseBuffer.indexOf("<ajax-response>");
int ajaxEnd = responseBuffer.indexOf("</ajax-response>");
long timeTaken = System.currentTimeMillis() - RequestCycle.get().getStartTime();
if (headIndex != -1 && bodyIndex != -1)
{
AppendingStringBuffer endScript = new AppendingStringBuffer(150);
endScript.append("\n").append(JavaScriptUtils.SCRIPT_OPEN_TAG);
endScript.append("\nwindow.defaultStatus='");
endScript.append(getStatusString(timeTaken, "ServerAndClientTimeFilter.statustext"));
endScript.append("';\n").append(JavaScriptUtils.SCRIPT_CLOSE_TAG).append("\n");
responseBuffer.insert(bodyIndex - 1, endScript);
responseBuffer.insert(headIndex + 6, "\n" + JavaScriptUtils.SCRIPT_OPEN_TAG +
"\nvar clientTimeVariable = new Date().getTime();\n" +
JavaScriptUtils.SCRIPT_CLOSE_TAG + "\n");
}
else if (ajaxStart != -1 && ajaxEnd != -1)
String nonce = getNonce();
boolean hasBody = headIndex != -1 && bodyIndex != -1;
boolean hasAjaxResponse = ajaxStart != -1 && ajaxEnd != -1;

if (hasBody || hasAjaxResponse)
{
AppendingStringBuffer startScript = new AppendingStringBuffer(250);
startScript.append("<evaluate><![CDATA[window.defaultStatus='");
startScript.append(getStatusString(timeTaken,
"ajax.ServerAndClientTimeFilter.statustext"));
startScript.append("';]]></evaluate>");
responseBuffer.insert(ajaxEnd, startScript.toString());
responseBuffer.insert(ajaxStart + 15,
"<priority-evaluate><![CDATA[clientTimeVariable = new Date().getTime();]]></priority-evaluate>");
startScript.append(createScriptOpenTag(false, nonce));
startScript.append(JavaScriptUtils.SCRIPT_CONTENT_PREFIX);
startScript.append("clientTimeVariable = new Date().getTime();");
startScript.append(CDATA_SCRIPT_END);
AppendingStringBuffer endScript = new AppendingStringBuffer(300);
endScript.append(createScriptOpenTag(true, nonce));
endScript.append(JavaScriptUtils.SCRIPT_CONTENT_PREFIX);
endScript.append("\nwindow.defaultStatus='" + getStatusString(timeTaken, "ajax.ServerAndClientTimeFilter.statustext"));
endScript.append(CDATA_SCRIPT_END);

if (hasBody)
{
responseBuffer.insert(bodyIndex, endScript);
responseBuffer.insert(headIndex + 6, startScript);
}
else
{
AppendingStringBuffer headerContributionStartBuffer = new AppendingStringBuffer(500);
headerContributionStartBuffer.append(HEADER_CONTRIBUTION_START);
headerContributionStartBuffer.append(startScript);
headerContributionStartBuffer.append(HEADER_CONTRIBUTION_END);
AppendingStringBuffer headerContributionEndBuffer = new AppendingStringBuffer(500);
headerContributionEndBuffer.append(HEADER_CONTRIBUTION_START);
headerContributionEndBuffer.append(endScript);
headerContributionEndBuffer.append(HEADER_CONTRIBUTION_END);
responseBuffer.insert(ajaxEnd, headerContributionEndBuffer);
responseBuffer.insert(ajaxStart + 15, headerContributionStartBuffer);
}
}
log.info(timeTaken + "ms server time taken for request " +
RequestCycle.get().getRequest().getUrl() + " response size: " + responseBuffer.length());
return responseBuffer;
}

private String createScriptOpenTag(boolean isAfterDomRender, String nonce)
{
AttributeMap attrs = new AttributeMap();
attrs.putAttribute(JavaScriptUtils.ATTR_TYPE, "text/javascript");
if (isAfterDomRender)
{
attrs.putAttribute("data-wicket-evaluation", "after");
}
if (nonce != null)
{
attrs.putAttribute(JavaScriptUtils.ATTR_CSP_NONCE, "nonce");
}
return "<script" + attrs.toString() + ">";
}

/**
* Returns a locale specific status message about the server and client time.
*
Expand All @@ -111,8 +144,16 @@ private String getStatusString(long timeTaken, String resourceKey)
.getString(resourceKey, null,
"Server parsetime: ${servertime}, Client parsetime: ${clienttime}");
final Map<String, String> map = new HashMap<String, String>(4);
map.put("clienttime", "' + (new Date().getTime() - clientTimeVariable)/1000 + 's");
map.put("clienttime", "' + (new Date().getTime() - clientTimeVariable)/1000 + 's';");
map.put("servertime", ((double)timeTaken) / 1000 + "s");
return MapVariableInterpolator.interpolate(txt, map);
}

/**
* Get CSP nonce
*/
protected String getNonce() {
return null;
}

}
Loading