1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.rendering.internal.renderer.xhtml.link

File AbstractXHTMLLinkTypeRenderer.java

 

Coverage histogram

../../../../../../../img/srcFileCovDistChart9.png
38% of files have more coverage

Code metrics

18
46
10
1
268
123
24
0.52
4.6
10
2.4

Classes

Class Line # Actions
AbstractXHTMLLinkTypeRenderer 43 46 0% 24 7
0.905405490.5%
 

Contributing tests

This file is covered by 121 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.rendering.internal.renderer.xhtml.link;
21   
22    import java.util.ArrayList;
23    import java.util.Arrays;
24    import java.util.LinkedHashMap;
25    import java.util.List;
26    import java.util.Map;
27   
28    import javax.inject.Inject;
29   
30    import org.apache.commons.lang3.StringUtils;
31    import org.xwiki.component.manager.ComponentLookupException;
32    import org.xwiki.component.manager.ComponentManager;
33    import org.xwiki.rendering.listener.reference.ResourceReference;
34    import org.xwiki.rendering.renderer.printer.XHTMLWikiPrinter;
35    import org.xwiki.rendering.renderer.reference.link.URILabelGenerator;
36   
37    /**
38    * Common code for XHTML Link Type Renderer implementations.
39    *
40    * @version $Id: 39e572049668d6d749e147b536ca3d70846b2abb $
41    * @since 2.5M2
42    */
 
43    public abstract class AbstractXHTMLLinkTypeRenderer implements XHTMLLinkTypeRenderer
44    {
45    /**
46    * The XHTML element <code>class</code> parameter.
47    */
48    protected static final String CLASS = "class";
49   
50    /**
51    * The name of the XHTML format element.
52    */
53    protected static final String SPAN = "span";
54   
55    private static final String NOOPENER = "noopener";
56   
57    private static final String NOREFERRER = "noreferrer";
58   
59    private static final String REL = "rel";
60   
61    /**
62    * Used to look for {@link org.xwiki.rendering.renderer.reference.link.URILabelGenerator} component implementations
63    * when computing labels.
64    */
65    @Inject
66    protected ComponentManager componentManager;
67   
68    /**
69    * The XHTML printer to use to output links as XHTML.
70    */
71    private XHTMLWikiPrinter xhtmlPrinter;
72   
73    /**
74    * @see #setHasLabel(boolean)
75    */
76    private boolean hasLabel;
77   
78    /**
79    * @return See {@link #setHasLabel(boolean)}
80    */
 
81  812 toggle protected boolean hasLabel()
82    {
83  812 return this.hasLabel;
84    }
85   
 
86  1624 toggle @Override
87    public void setHasLabel(boolean hasLabel)
88    {
89  1624 this.hasLabel = hasLabel;
90    }
91   
 
92  1624 toggle @Override
93    public void setXHTMLWikiPrinter(XHTMLWikiPrinter printer)
94    {
95  1624 this.xhtmlPrinter = printer;
96    }
97   
 
98  3497 toggle @Override
99    public XHTMLWikiPrinter getXHTMLWikiPrinter()
100    {
101  3497 return this.xhtmlPrinter;
102    }
103   
104    /**
105    * Hook called when rendering the beginning of a link to allow implementation classes to augment the passed span and
106    * anchor attributes as they see fit.
107    *
108    * @param reference the reference of the link being rendered
109    * @param spanAttributes the HTML attributes for the SPAN HTML element added around the ANCHOR HTML element
110    * @param anchorAttributes the HTML attributes for the ANCHOR element
111    */
112    protected abstract void beginLinkExtraAttributes(ResourceReference reference, Map<String, String> spanAttributes,
113    Map<String, String> anchorAttributes);
114   
115    /**
116    * Default implementation for computing a link label when no label has been specified. Can be overwritten by
117    * implementations to provide a different algorithm.
118    *
119    * @param reference the reference of the link for which to compute the label
120    * @return the computed label
121    */
 
122  51 toggle protected String computeLabel(ResourceReference reference)
123    {
124    // Look for a component implementing URILabelGenerator with a role hint matching the link scheme.
125    // If not found then use the full reference as the label.
126    // If there's no scheme separator then use the full reference as the label. Note that this can happen
127    // when we're not in wiki mode (since all links are considered URIs when not in wiki mode).
128  51 String label;
129  51 try {
130  51 URILabelGenerator uriLabelGenerator =
131    this.componentManager.getInstance(URILabelGenerator.class, reference.getType().getScheme());
132  17 label = uriLabelGenerator.generateLabel(reference);
133    } catch (ComponentLookupException e) {
134  34 label = reference.getReference();
135    }
136  51 return label;
137    }
138   
 
139  479 toggle @Override
140    public void beginLink(ResourceReference reference, boolean freestanding, Map<String, String> parameters)
141    {
142  479 Map<String, String> spanAttributes = new LinkedHashMap<String, String>();
143  479 Map<String, String> anchorAttributes = new LinkedHashMap<String, String>();
144   
145    // Add all parameters to the A attributes
146  479 anchorAttributes.putAll(parameters);
147   
148  479 if (isExternalLink(reference)) {
149  110 spanAttributes.put(CLASS, "wikiexternallink");
150  110 handleTargetAttribute(anchorAttributes);
151    } else {
152  369 spanAttributes.put(CLASS, "wikiinternallink");
153    }
154  479 if (freestanding) {
155  58 anchorAttributes.put(CLASS, addAttributeValue(anchorAttributes.get(CLASS), "wikimodel-freestanding"));
156    }
157   
158  479 beginLinkExtraAttributes(reference, spanAttributes, anchorAttributes);
159   
160  479 getXHTMLWikiPrinter().printXMLStartElement(SPAN, spanAttributes);
161  479 getXHTMLWikiPrinter().printXMLStartElement(XHTMLLinkRenderer.ANCHOR, anchorAttributes);
162    }
163   
164    /**
165    * When a link is open in an other window or in an other frame, the loaded page has some restricted access to the
166    * parent window. Among other things, it has the ability to redirect it to an other page, which can lead to
167    * dangerous phishing attacks.
168    *
169    * See: https://mathiasbynens.github.io/rel-noopener/ or https://dev.to/phishing or
170    * http://jira.xwiki.org/browse/XRENDERING-462
171    *
172    * To avoid this vulnerability, we automatically add the "noopener" value to the "rel" attribute of the anchor.
173    * But because Firefox does not handle it, we also need to add the "noreferer" value.
174    *
175    * @param anchorAttributes the anchor attributes (that going to be changed)
176    * @since 7.4.5
177    * @since 8.2.2
178    * @since 8.3M2
179    */
 
180  110 toggle private void handleTargetAttribute(Map<String, String> anchorAttributes)
181    {
182  110 String target = anchorAttributes.get("target");
183   
184    // Target can have these values:
185    //
186    // "_blank" which opens the link in a new window
187    // "_self" which opens the link in the same window (default)
188    // "_top" and "_parent" which control the top or the parent window of some frame
189    // "frame-name" (it could be anything) which opens the link in the frame called "frame-name".
190    //
191    // "_self", "_top" and "_parent" are the only safe values. So we need to handle any other value...
192  110 if (StringUtils.isNotBlank(target)
193    && !"_self".equals(target) && !"_parent".equals(target) && !"_top".equals(target)) {
194  1 List<String> relAttributes = new ArrayList<>();
195   
196    // Parse the current values
197  1 String relAttribute = anchorAttributes.get(REL);
198  1 if (relAttribute != null) {
199  0 relAttributes.addAll(Arrays.asList(relAttribute.split(" ")));
200    }
201   
202    // Add the "noopener" attribute
203  1 if (!relAttributes.contains(NOOPENER)) {
204  1 relAttributes.add(NOOPENER);
205    }
206   
207    // Add the "noreferrer" attribute
208  1 if (!relAttributes.contains(NOREFERRER)) {
209  1 relAttributes.add(NOREFERRER);
210    }
211   
212    // Serialize the attributes
213  1 if (!relAttributes.isEmpty()) {
214  1 anchorAttributes.put(REL, String.join(" ", relAttributes));
215    }
216    }
217    }
218   
 
219  812 toggle @Override
220    public void endLink(ResourceReference reference, boolean freestanding, Map<String, String> parameters)
221    {
222    // If there was no link content then generate it based on the passed reference
223  812 if (!hasLabel()) {
224  83 getXHTMLWikiPrinter().printXMLStartElement(SPAN, new String[][] { { CLASS, "wikigeneratedlinkcontent" } });
225  83 getXHTMLWikiPrinter().printXML(computeLabel(reference));
226  83 getXHTMLWikiPrinter().printXMLEndElement(SPAN);
227    }
228   
229  812 getXHTMLWikiPrinter().printXMLEndElement(XHTMLLinkRenderer.ANCHOR);
230  812 getXHTMLWikiPrinter().printXMLEndElement(SPAN);
231    }
232   
233    /**
234    * Add an attribute value to an existing attribute. This is useful for example for adding a value to an HTML CLASS
235    * attribute.
236    *
237    * @param currentValue the current value of the attribute (can be null)
238    * @param valueToAdd the value to add
239    * @return the current value augmented by the value to add
240    */
 
241  58 toggle private String addAttributeValue(String currentValue, String valueToAdd)
242    {
243  58 String newValue;
244  58 if (currentValue == null || currentValue.length() == 0) {
245  58 newValue = "";
246    } else {
247  0 newValue = currentValue + " ";
248    }
249  58 return newValue + valueToAdd;
250    }
251   
252    /**
253    * Check if this link is internal or external to the application or rendered document. For example, when running the
254    * rendering engine inside a wiki, links to other wiki documents are considered to be internal, while links to HTTP
255    * URLs are considered external. Another example, when rendering standalone documents, references to other parts of
256    * the document are internal, while URLs are external references. Subclasses should override this method to use
257    * their own decision process.
258    *
259    * @param reference the reference used by this link, in case the status of the link depends on the particular
260    * resource being referenced
261    * @return {@code true} if the referenced resource is external to the containing application or the rendered
262    * document, or if the notion of "internal references" doesn't even make sense, {@code false} otherwise
263    */
 
264  110 toggle protected boolean isExternalLink(ResourceReference reference)
265    {
266  110 return true;
267    }
268    }