* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE * License as published by the Free Software Foundation; either * version 3 of the License, or any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU AFFERO GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU Affero General Public * License along with this library. If not, see . * */ namespace Test\AppFramework; use OC\AppFramework\App; use OC\AppFramework\Http\Dispatcher; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Response; function rrmdir($directory) { $files = array_diff(scandir($directory), array('.','..')); foreach ($files as $file) { if (is_dir($directory . '/' . $file)) { rrmdir($directory . '/' . $file); } else { unlink($directory . '/' . $file); } } return rmdir($directory); } class AppTest extends \Test\TestCase { private $container; private $io; private $api; private $controller; private $dispatcher; private $params; private $headers; private $output; private $controllerName; private $controllerMethod; private $appPath; protected function setUp(): void { parent::setUp(); $this->container = new \OC\AppFramework\DependencyInjection\DIContainer('test', array()); $this->controller = $this->createMock(Controller::class); $this->dispatcher = $this->createMock(Dispatcher::class); $this->io = $this->createMock(Http\IOutput::class); $this->headers = array('key' => 'value'); $this->output = 'hi'; $this->controllerName = 'Controller'; $this->controllerMethod = 'method'; $this->container[$this->controllerName] = $this->controller; $this->container['Dispatcher'] = $this->dispatcher; $this->container['OCP\\AppFramework\\Http\\IOutput'] = $this->io; $this->container['urlParams'] = array(); $this->appPath = __DIR__ . '/../../../apps/namespacetestapp'; $infoXmlPath = $this->appPath . '/appinfo/info.xml'; mkdir($this->appPath . '/appinfo', 0777, true); $xml = '' . '' . 'namespacetestapp' . 'NameSpaceTestApp' . ''; file_put_contents($infoXmlPath, $xml); } public function testControllerNameAndMethodAreBeingPassed(){ $return = ['HTTP/2.0 200 OK', [], [], null, new Response()]; $this->dispatcher->expects($this->once()) ->method('dispatch') ->with($this->equalTo($this->controller), $this->equalTo($this->controllerMethod)) ->will($this->returnValue($return)); $this->io->expects($this->never()) ->method('setOutput'); App::main($this->controllerName, $this->controllerMethod, $this->container); } public function testBuildAppNamespace() { $ns = App::buildAppNamespace('someapp'); $this->assertEquals('OCA\Someapp', $ns); } public function testBuildAppNamespaceCore() { $ns = App::buildAppNamespace('someapp', 'OC\\'); $this->assertEquals('OC\Someapp', $ns); } public function testBuildAppNamespaceInfoXml() { $ns = App::buildAppNamespace('namespacetestapp', 'OCA\\'); $this->assertEquals('OCA\NameSpaceTestApp', $ns); } protected function tearDown(): void { rrmdir($this->appPath); parent::tearDown(); } public function testOutputIsPrinted(){ $return = ['HTTP/2.0 200 OK', [], [], $this->output, new Response()]; $this->dispatcher->expects($this->once()) ->method('dispatch') ->with($this->equalTo($this->controller), $this->equalTo($this->controllerMethod)) ->will($this->returnValue($return)); $this->io->expects($this->once()) ->method('setOutput') ->with($this->equalTo($this->output)); App::main($this->controllerName, $this->controllerMethod, $this->container, []); } public function dataNoOutput() { return [ ['HTTP/2.0 204 No content'], ['HTTP/2.0 304 Not modified'], ]; } /** * @dataProvider dataNoOutput */ public function testNoOutput(string $statusCode) { $return = [$statusCode, [], [], $this->output, new Response()]; $this->dispatcher->expects($this->once()) ->method('dispatch') ->with($this->equalTo($this->controller), $this->equalTo($this->controllerMethod)) ->will($this->returnValue($return)); $this->io->expects($this->once()) ->method('setHeader') ->with($this->equalTo($statusCode)); $this->io->expects($this->never()) ->method('setOutput'); App::main($this->controllerName, $this->controllerMethod, $this->container, []); } public function testCallbackIsCalled(){ $mock = $this->getMockBuilder('OCP\AppFramework\Http\ICallbackResponse') ->getMock(); $return = ['HTTP/2.0 200 OK', [], [], $this->output, $mock]; $this->dispatcher->expects($this->once()) ->method('dispatch') ->with($this->equalTo($this->controller), $this->equalTo($this->controllerMethod)) ->will($this->returnValue($return)); $mock->expects($this->once()) ->method('callback'); App::main($this->controllerName, $this->controllerMethod, $this->container, []); } public function testCoreApp() { $this->container['AppName'] = 'core'; $this->container['OC\Core\Controller\Foo'] = $this->controller; $return = ['HTTP/2.0 200 OK', [], [], null, new Response()]; $this->dispatcher->expects($this->once()) ->method('dispatch') ->with($this->equalTo($this->controller), $this->equalTo($this->controllerMethod)) ->will($this->returnValue($return)); $this->io->expects($this->never()) ->method('setOutput'); App::main('Foo', $this->controllerMethod, $this->container); } public function testSettingsApp() { $this->container['AppName'] = 'settings'; $this->container['OCA\Settings\Controller\Foo'] = $this->controller; $return = ['HTTP/2.0 200 OK', [], [], null, new Response()]; $this->dispatcher->expects($this->once()) ->method('dispatch') ->with($this->equalTo($this->controller), $this->equalTo($this->controllerMethod)) ->will($this->returnValue($return)); $this->io->expects($this->never()) ->method('setOutput'); App::main('Foo', $this->controllerMethod, $this->container); } public function testApp() { $this->container['AppName'] = 'bar'; $this->container['OCA\Bar\Controller\Foo'] = $this->controller; $return = ['HTTP/2.0 200 OK', [], [], null, new Response()]; $this->dispatcher->expects($this->once()) ->method('dispatch') ->with($this->equalTo($this->controller), $this->equalTo($this->controllerMethod)) ->will($this->returnValue($return)); $this->io->expects($this->never()) ->method('setOutput'); App::main('Foo', $this->controllerMethod, $this->container); } } 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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package html

import (
	"golang.org/x/net/html/atom"
)

// A NodeType is the type of a Node.
type NodeType uint32

const (
	ErrorNode NodeType = iota
	TextNode
	DocumentNode
	ElementNode
	CommentNode
	DoctypeNode
	scopeMarkerNode
)

// Section 12.2.4.3 says "The markers are inserted when entering applet,
// object, marquee, template, td, th, and caption elements, and are used
// to prevent formatting from "leaking" into applet, object, marquee,
// template, td, th, and caption elements".
var scopeMarker = Node{Type: scopeMarkerNode}

// A Node consists of a NodeType and some Data (tag name for element nodes,
// content for text) and are part of a tree of Nodes. Element nodes may also
// have a Namespace and contain a slice of Attributes. Data is unescaped, so
// that it looks like "a<b" rather than "a&lt;b". For element nodes, DataAtom
// is the atom for Data, or zero if Data is not a known tag name.
//
// An empty Namespace implies a "http://www.w3.org/1999/xhtml" namespace.
// Similarly, "math" is short for "http://www.w3.org/1998/Math/MathML", and
// "svg" is short for "http://www.w3.org/2000/svg".
type Node struct {
	Parent, FirstChild, LastChild, PrevSibling, NextSibling *Node

	Type      NodeType
	DataAtom  atom.Atom
	Data      string
	Namespace string
	Attr      []Attribute
}

// InsertBefore inserts newChild as a child of n, immediately before oldChild
// in the sequence of n's children. oldChild may be nil, in which case newChild
// is appended to the end of n's children.
//
// It will panic if newChild already has a parent or siblings.
func (n *Node) InsertBefore(newChild, oldChild *Node) {
	if newChild.Parent != nil || newChild.PrevSibling != nil || newChild.NextSibling != nil {
		panic("html: InsertBefore called for an attached child Node")
	}
	var prev, next *Node
	if oldChild != nil {
		prev, next = oldChild.PrevSibling, oldChild
	} else {
		prev = n.LastChild
	}
	if prev != nil {
		prev.NextSibling = newChild
	} else {
		n.FirstChild = newChild
	}
	if next != nil {
		next.PrevSibling = newChild
	} else {
		n.LastChild = newChild
	}
	newChild.Parent = n
	newChild.PrevSibling = prev
	newChild.NextSibling = next
}

// AppendChild adds a node c as a child of n.
//
// It will panic if c already has a parent or siblings.
func (n *Node) AppendChild(c *Node) {
	if c.Parent != nil || c.PrevSibling != nil || c.NextSibling != nil {
		panic("html: AppendChild called for an attached child Node")
	}
	last := n.LastChild
	if last != nil {
		last.NextSibling = c
	} else {
		n.FirstChild = c
	}
	n.LastChild = c
	c.Parent = n
	c.PrevSibling = last
}

// RemoveChild removes a node c that is a child of n. Afterwards, c will have
// no parent and no siblings.
//
// It will panic if c's parent is not n.
func (n *Node) RemoveChild(c *Node) {
	if c.Parent != n {
		panic("html: RemoveChild called for a non-child Node")
	}
	if n.FirstChild == c {
		n.FirstChild = c.NextSibling
	}
	if c.NextSibling != nil {
		c.NextSibling.PrevSibling = c.PrevSibling
	}
	if n.LastChild == c {
		n.LastChild = c.PrevSibling
	}
	if c.PrevSibling != nil {
		c.PrevSibling.NextSibling = c.NextSibling
	}
	c.Parent = nil
	c.PrevSibling = nil
	c.NextSibling = nil
}

// reparentChildren reparents all of src's child nodes to dst.
func reparentChildren(dst, src *Node) {
	for {
		child := src.FirstChild
		if child == nil {
			break
		}
		src.RemoveChild(child)
		dst.AppendChild(child)
	}
}

// clone returns a new node with the same type, data and attributes.
// The clone has no parent, no siblings and no children.
func (n *Node) clone() *Node {
	m := &Node{
		Type:     n.Type,
		DataAtom: n.DataAtom,
		Data:     n.Data,
		Attr:     make([]Attribute, len(n.Attr)),
	}
	copy(m.Attr, n.Attr)
	return m
}

// nodeStack is a stack of nodes.
type nodeStack []*Node

// pop pops the stack. It will panic if s is empty.
func (s *nodeStack) pop() *Node {
	i := len(*s)
	n := (*s)[i-1]
	*s = (*s)[:i-1]
	return n
}

// top returns the most recently pushed node, or nil if s is empty.
func (s *nodeStack) top() *Node {
	if i := len(*s); i > 0 {
		return (*s)[i-1]
	}
	return nil
}

// index returns the index of the top-most occurrence of n in the stack, or -1
// if n is not present.
func (s *nodeStack) index(n *Node) int {
	for i := len(*s) - 1; i >= 0; i-- {
		if (*s)[i] == n {
			return i
		}
	}
	return -1
}

// contains returns whether a is within s.
func (s *nodeStack) contains(a atom.Atom) bool {
	for _, n := range *s {
		if n.DataAtom == a && n.Namespace == "" {
			return true
		}
	}
	return false
}

// insert inserts a node at the given index.
func (s *nodeStack) insert(i int, n *Node) {
	(*s) = append(*s, nil)
	copy((*s)[i+1:], (*s)[i:])
	(*s)[i] = n
}

// remove removes a node from the stack. It is a no-op if n is not present.
func (s *nodeStack) remove(n *Node) {
	i := s.index(n)
	if i == -1 {
		return
	}
	copy((*s)[i:], (*s)[i+1:])
	j := len(*s) - 1
	(*s)[j] = nil
	*s = (*s)[:j]
}

type insertionModeStack []insertionMode

func (s *insertionModeStack) pop() (im insertionMode) {
	i := len(*s)
	im = (*s)[i-1]
	*s = (*s)[:i-1]
	return im
}

func (s *insertionModeStack) top() insertionMode {
	if i := len(*s); i > 0 {
		return (*s)[i-1]
	}
	return nil
}