summaryrefslogtreecommitdiff
path: root/lib/htmlpurifier/library/HTMLPurifier/ChildDef/List.php
blob: cdaa2893a7e2d534d56ba26ccaeea95b70d84cf1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
<?php

/**
 * Definition for list containers ul and ol.
 */
class HTMLPurifier_ChildDef_List extends HTMLPurifier_ChildDef
{
    public $type = 'list';
    // lying a little bit, so that we can handle ul and ol ourselves
    // XXX: This whole business with 'wrap' is all a bit unsatisfactory
    public $elements = array('li' => true, 'ul' => true, 'ol' => true);
    public function validateChildren($tokens_of_children, $config, $context) {
        // Flag for subclasses
        $this->whitespace = false;

        // if there are no tokens, delete parent node
        if (empty($tokens_of_children)) return false;

        // the new set of children
        $result = array();

        // current depth into the nest
        $nesting = 0;

        // a little sanity check to make sure it's not ALL whitespace
        $all_whitespace = true;

        $seen_li = false;
        $need_close_li = false;

        foreach ($tokens_of_children as $token) {
            if (!empty($token->is_whitespace)) {
                $result[] = $token;
                continue;
            }
            $all_whitespace = false; // phew, we're not talking about whitespace

            if ($nesting == 1 && $need_close_li) {
                $result[] = new HTMLPurifier_Token_End('li');
                $nesting--;
                $need_close_li = false;
            }

            $is_child = ($nesting == 0);

            if ($token instanceof HTMLPurifier_Token_Start) {
                $nesting++;
            } elseif ($token instanceof HTMLPurifier_Token_End) {
                $nesting--;
            }

            if ($is_child) {
                if ($token->name === 'li') {
                    // good
                    $seen_li = true;
                } elseif ($token->name === 'ul' || $token->name === 'ol') {
                    // we want to tuck this into the previous li
                    $need_close_li = true;
                    $nesting++;
                    if (!$seen_li) {
                        // create a new li element
                        $result[] = new HTMLPurifier_Token_Start('li');
                    } else {
                        // backtrack until </li> found
                        while(true) {
                            $t = array_pop($result);
                            if ($t instanceof HTMLPurifier_Token_End) {
                                // XXX actually, these invariants could very plausibly be violated
                                // if we are doing silly things with modifying the set of allowed elements.
                                // FORTUNATELY, it doesn't make a difference, since the allowed
                                // elements are hard-coded here!
                                if ($t->name !== 'li') {
                                    trigger_error("Only li present invariant violated in List ChildDef", E_USER_ERROR);
                                    return false;
                                }
                                break;
                            } elseif ($t instanceof HTMLPurifier_Token_Empty) { // bleagh
                                if ($t->name !== 'li') {
                                    trigger_error("Only li present invariant violated in List ChildDef", E_USER_ERROR);
                                    return false;
                                }
                                // XXX this should have a helper for it...
                                $result[] = new HTMLPurifier_Token_Start('li', $t->attr, $t->line, $t->col, $t->armor);
                                break;
                            } else {
                                if (!$t->is_whitespace) {
                                    trigger_error("Only whitespace present invariant violated in List ChildDef", E_USER_ERROR);
                                    return false;
                                }
                            }
                        }
                    }
                } else {
                    // start wrapping (this doesn't precisely mimic
                    // browser behavior, but what browsers do is kind of
                    // hard to mimic in a standards compliant way
                    // XXX Actually, this has no impact in practice,
                    // because this gets handled earlier. Arguably,
                    // we should rip out all of that processing
                    $result[] = new HTMLPurifier_Token_Start('li');
                    $nesting++;
                    $seen_li = true;
                    $need_close_li = true;
                }
            }
            $result[] = $token;
        }
        if ($need_close_li) {
            $result[] = new HTMLPurifier_Token_End('li');
        }
        if (empty($result)) return false;
        if ($all_whitespace) {
            return false;
        }
        if ($tokens_of_children == $result) return true;
        return $result;
    }
}

// vim: et sw=4 sts=4