NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

parserbase.py (78144B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\carbonui\control\editutils\parserbase.py
      2 import _weakref
      3 import sys
      4 import types
      5 import urllib2
      6 import blue
      7 import carbonui.const as uiconst
      8 import eveLocalization
      9 import eveformat
     10 import log
     11 import stackless
     12 import telemetry
     13 from carbonui import fontflags
     14 from carbonui.control.editutils import parser
     15 from carbonui.control.editutils.css import css as Css
     16 from carbonui.control.browser import browserutil
     17 from carbonui.control.inlines import get_inline_deco_class
     18 from carbonui.control.scrollentries import ScrollEntryNode, SE_TextlineCore
     19 from carbonui.primitives.container import Container
     20 from carbonui.text.color import TextColor
     21 from carbonui.util.bunch import Bunch
     22 from carbonui.util.various_unsorted import GetBrowser, ParseHTMLColor, GetTrace
     23 from carbonui.control.editutils.parser import Parser, HtmlOptionalClose, HtmlAutoClose
     24 from carbonui.control.label import LabelOverride as Label
     25 from carbonui.uicore import uicore
     26 from eveprefs import prefs
     27 
     28 class ParserBaseCore(object):
     29     __guid__ = 'eveparser.ParserBaseCore'
     30     __update_on_reload__ = 1
     31 
     32     def Prepare(self):
     33         self.getWidths = 0
     34         self.minWidth = 0
     35         self.totalWidth = 0
     36         self.xmargin = 8
     37         self.tagdepth = 0
     38         self.readonly = 1
     39         self.htmldebug = 0
     40         self.glyphString = None
     41         self.plainload = 0
     42         self.title = None
     43         self.charset = 'cp1252'
     44         self.textbuffers = []
     45         self.css = Css()
     46         self.sr.parser = Parser(self)
     47         self.sr.parser.notify_load = self.Loading
     48         self.sr.window = None
     49         if self.sr.Get('content', None):
     50             parent = self.sr.content.parent
     51             idx = self.sr.content.parent.children.index(self.sr.content)
     52         else:
     53             parent = self
     54             idx = -1
     55         self.sr.underlays_content = Container(parent=parent, name='underlays_content', align=uiconst.TOPLEFT, idx=idx + 1)
     56         self.sr.overlays_content = Container(parent=parent, name='overlays_content', align=uiconst.TOPLEFT, idx=idx)
     57         self.sr.background = Container(parent=parent, name='background')
     58         self.sr.cacheContainer = Container(name='cacheContainer', parent=self, state=uiconst.UI_HIDDEN)
     59 
     60     def Loading(self, *args):
     61         if self.sr.window and hasattr(self.sr.window, 'ShowLoadBar'):
     62             self.sr.window.ShowLoadBar(*args)
     63 
     64     def SetStatus(self, *args):
     65         if self.sr.window and hasattr(self.sr.window, 'ShowStatus'):
     66             self.sr.window.ShowStatus(*args)
     67 
     68     def ShowHint(self, hint = None):
     69         if self.sr.window and hasattr(self.sr.window, 'ShowHint'):
     70             self.sr.window.ShowHint(hint)
     71 
     72     @telemetry.ZONE_METHOD
     73     def OnStartTag(self, tag, attrs):
     74         if not self or not getattr(self, 'sr', None):
     75             return
     76         closeTags = HtmlOptionalClose.get(tag, ())
     77         if self.attrStack[-1]['tag'] in closeTags:
     78             self.OnEndTag(self.attrStack[-1]['tag'])
     79         if tag == 'table' and self.sr.table:
     80             self.blockBufferClose += 1
     81         elif tag == 'div' and self.divs:
     82             self.divs += 1
     83         if self.attrStack[-1]['bufferStack'] is not None:
     84             attrs.form = self.sr.form
     85             self.attrStack[-1]['bufferStack'].append(('OnStartTag', (tag, attrs)))
     86             return
     87         f = getattr(self, 'OnStart_%s' % tag, None)
     88         if f:
     89             f(attrs)
     90         else:
     91             self.OnStart_default(tag, attrs)
     92 
     93     @telemetry.ZONE_METHOD
     94     def OnEndTag(self, tag):
     95         if not self or not getattr(self, 'sr', None):
     96             return
     97         if self.attrStack[-1]['bufferStack'] is not None:
     98             if tag == 'table' and self.sr.table:
     99                 self.blockBufferClose = max(0, self.blockBufferClose - 1)
    100             if tag == 'div' and self.divs:
    101                 self.divs = max(0, self.divs - 1)
    102             if (tag in 'div' and self.sr.table is None or tag in ('td', 'th', 'tr') and self.sr.table) and not self.blockBufferClose and not self.divs:
    103                 self.attrStack[-1]['bufferStack'].append(('FlushBuffer', (None,)))
    104             else:
    105                 self.attrStack[-1]['bufferStack'].append(('OnEndTag', (tag,)))
    106                 return
    107         closeTags = HtmlAutoClose.get(tag, ())
    108         if self.attrStack[-1]['tag'] != tag and self.attrStack[-1]['tag'] in closeTags:
    109             self.OnEndTag(self.attrStack[-1]['tag'])
    110         f = getattr(self, 'OnEnd_%s' % tag, None)
    111         if f:
    112             f()
    113         else:
    114             self.OnEnd_default(tag)
    115 
    116     def OnData(self, data, *args):
    117         if not self or not getattr(self, 'sr', None):
    118             return
    119         if self.attrStack[-1]['bufferStack'] is not None:
    120             self.attrStack[-1]['bufferStack'].append(('OnData', (data,)))
    121             return
    122         tag, attrs = self.attrStack[-1]['tag'], self.attrStack[-1]['attr']
    123         data = self.sr.buffer + data
    124         self.sr.buffer = u''
    125         if not data:
    126             return
    127         f = getattr(self, 'OnData_%s' % (tag or ''), None)
    128         if f:
    129             f(data, attrs)
    130         elif isinstance(data, basestring):
    131             data = self.StripText(data)
    132             if data:
    133                 self.AddTextToBuffer(data, fromW='Ondata')
    134         else:
    135             log.LogWarn('NonString data outside any tag', repr(data))
    136 
    137     def StripText(self, text):
    138         text = text.replace('\n', u' ').replace('\r', u' ').replace('\t', u' ')
    139         if self.plainload:
    140             return text
    141         while text.find(u'  ') != -1:
    142             text = text.replace(u'  ', u' ')
    143 
    144         if not self.textbuffer:
    145             text = text.lstrip()
    146         return text
    147 
    148     @telemetry.ZONE_METHOD
    149     def OnStart_default(self, tag, attrs, defaultStyles = None):
    150         self.attrStack.append(self.attrStack[-1].copy())
    151         self.attrStack[-1]['tag'] = tag
    152         self.attrStack[-1]['attr'] = attrs
    153         self.SetStyles(defaultStyles)
    154         self.ApplyCSS(tag, attrs)
    155         self.ParseStdStyles(attrs)
    156 
    157     @telemetry.ZONE_METHOD
    158     def OnEnd_default(self, tag):
    159         idx = -1
    160         while self.attrStack[idx]['tag'] != tag:
    161             log.LogInfo('Incorrect order of tags in html string, trying to close', tag, 'but lasttag is', self.attrStack[idx]['tag'])
    162             idx -= 1
    163             if len(self.attrStack) <= abs(idx):
    164                 log.LogInfo('Reached begining of attrStack while trying to close tag:', tag)
    165                 return
    166 
    167         self.attrStack.pop(idx)
    168         if len(self.attrStack) > abs(idx):
    169             self.attrStack[idx - 1]['lasttag'] = tag
    170 
    171     def FlushBuffer(self, fakeLines = None):
    172         data = self.StripText(self.sr.buffer)
    173         self.sr.buffer = u''
    174         if data:
    175             self.AddTextToBuffer(data)
    176         if self.textbuffer:
    177             obj = self.textbuffer[-1]
    178             if obj.type == '<text>' and obj.letters == u' ':
    179                 self.textbuffer.pop(-1)
    180         if self.getWidths:
    181             self.BreakLines(self.textbuffer)
    182             self.textbuffer = []
    183             return
    184         if fakeLines is not None:
    185             _lines = self.BuildEntries(fakeLines, self.textbuffer, self.attrStack[-1])
    186             self.textbuffers.append(([self.GetTextObject('')], self.attrStack[-1]))
    187             self.AddLines(_lines)
    188             return
    189         if len(self.textbuffer):
    190             self.textbuffers.append((self.textbuffer, self.attrStack[-1]))
    191             lines = self.BreakLines(self.textbuffer)
    192             scrollNodes = self.BuildEntries(lines, self.textbuffer, self.attrStack[-1])
    193             self.AddLines(scrollNodes)
    194         self.textbuffer = []
    195 
    196     def GetFontFlagFromStack(self):
    197         fontFlag = 0
    198         if self.attrStack[-1]['font-style'] == 'i':
    199             fontFlag |= fontflags.i
    200         if self.attrStack[-1]['font-weight'] == 'b' or self.a:
    201             fontFlag |= fontflags.b
    202         if 'u' in self.attrStack[-1]['text-decoration'] or self.a:
    203             fontFlag |= fontflags.u
    204         return fontFlag
    205 
    206     @telemetry.ZONE_METHOD
    207     def _LoadHTML(self, htmlstr):
    208         if self.destroyed:
    209             return
    210         self.title = None
    211         self.sr.htmlstr = htmlstr
    212         self._Reset()
    213         if self.destroyed:
    214             return
    215         self.sr.parser.reset()
    216         self.sr.parser.feed(self.sr.htmlstr)
    217         if self.destroyed:
    218             return
    219         self.sr.parser.close()
    220 
    221     def LoadBuffer(self, buff, getWidths = 0, setWidth = None, singleWordMax = None):
    222         self._Reset()
    223         self.getWidths = getWidths
    224         self.minWidth = 0
    225         self.totalWidth = 0
    226         self.singleWordMax = singleWordMax or self.sr.browser and self.sr.browser.GetContentWidth() - self.sr.browser.xmargin * 2
    227         if setWidth is not None:
    228             self.sr.width = setWidth
    229         self.attrStack[-1]['background-color'] = None
    230         for func, args in buff:
    231             if hasattr(self, func):
    232                 getattr(self, func, None)(*args)
    233 
    234         if self.getWidths:
    235             return
    236 
    237     @telemetry.ZONE_METHOD
    238     def _Reset(self):
    239         for form in self.sr.get('forms', []):
    240             if form is not None:
    241                 for attrs, wnd in form.fields:
    242                     wnd.Close()
    243 
    244         self.textbuffers = []
    245         self.textbuffer = []
    246         self.attrStack = []
    247         self.areamap = []
    248         self.a = None
    249         self.blockquote = 0
    250         self.list = 0
    251         self.contentHeight = 0
    252         self.contentWidth = 0
    253         self.blockBufferClose = 0
    254         self.divs = 0
    255         self.sr.table = None
    256         self.sr.buffer = ''
    257         self.sr.listIndex = []
    258         self.sr.overlays = []
    259         self.sr.underlays = []
    260         self.sr.lines = []
    261         self.sr.forms = []
    262         self.sr.anchor = None
    263         self.sr.form = None
    264         self.sr.select = None
    265         attrEntry = {}
    266         attrEntry['font-size'] = getattr(self, 'defaultFontSize', 12)
    267         attrEntry['font-style'] = ''
    268         attrEntry['font-weight'] = ''
    269         attrEntry['basefontsize'] = 3
    270         attrEntry['text-decoration'] = ''
    271         attrEntry['color'] = getattr(self, 'defaultFontColor', TextColor.NORMAL.rgba)
    272         attrEntry['background-color'] = None
    273         attrEntry['background-image'] = None
    274         attrEntry['background-image-color'] = (1.0, 1.0, 1.0, 1.0)
    275         attrEntry['background-image-width'] = 0
    276         attrEntry['background-image-height'] = 0
    277         attrEntry['background-image-left'] = 0
    278         attrEntry['background-image-top'] = 0
    279         attrEntry['background-repeat'] = 'repeat'
    280         attrEntry['background-attachment'] = 'scroll'
    281         attrEntry['line-height'] = 12
    282         attrEntry['align'] = None
    283         attrEntry['text-align'] = None
    284         attrEntry['text-indent'] = 0
    285         attrEntry['vertical-align'] = 3
    286         attrEntry['letter-spacing'] = 0
    287         attrEntry['word-spacing'] = 0
    288         attrEntry['line-height'] = None
    289         attrEntry['margin-left'] = 0
    290         attrEntry['margin-right'] = 0
    291         attrEntry['margin-top'] = 0
    292         attrEntry['margin-bottom'] = 0
    293         attrEntry['padding-left'] = 0
    294         attrEntry['padding-right'] = 0
    295         attrEntry['padding-top'] = 0
    296         attrEntry['padding-bottom'] = 0
    297         attrEntry['border-left-width'] = 0
    298         attrEntry['border-right-width'] = 0
    299         attrEntry['border-top-width'] = 0
    300         attrEntry['border-bottom-width'] = 0
    301         attrEntry['border-left-color'] = (1.0, 1.0, 1.0, 1.0)
    302         attrEntry['border-right-color'] = (1.0, 1.0, 1.0, 1.0)
    303         attrEntry['border-top-color'] = (1.0, 1.0, 1.0, 1.0)
    304         attrEntry['border-bottom-color'] = (1.0, 1.0, 1.0, 1.0)
    305         attrEntry['border-left-style'] = None
    306         attrEntry['border-right-style'] = None
    307         attrEntry['border-top-style'] = None
    308         attrEntry['border-bottom-style'] = None
    309         attrEntry['border-collapse'] = 0
    310         attrEntry['link-color'] = (1.0, 0.65, 0.0, 1.0)
    311         attrEntry['alink-color'] = None
    312         attrEntry['vlink-color'] = (1.0, 0.8, 0.0, 1.0)
    313         attrEntry['class'] = 'default'
    314         attrEntry['tag'] = 'default'
    315         attrEntry['lasttag'] = ''
    316         attrEntry['attr'] = None
    317         attrEntry['position'] = None
    318         attrEntry['float'] = None
    319         attrEntry['id'] = None
    320         attrEntry['bufferStack'] = None
    321         attrEntry['pstart'] = 0
    322         attrEntry['background-position'] = []
    323         self.attrStack.append(attrEntry)
    324         self.css.Reset()
    325         self.Reset()
    326 
    327     def GetLinespace(self):
    328         return int(self.attrStack[-1]['line-height'] or self.attrStack[-1]['font-size'])
    329 
    330     def Reset(self, *args):
    331         pass
    332 
    333     def LoadBodyAttrs(self, *args):
    334         pass
    335 
    336     def BuildEntries(self, lines, stack, attrs):
    337         if stack and stack[0].type == '<hr>':
    338             from carbonui.control.edit_components import hr as SE_hr
    339             data = {'decoClass': SE_hr,
    340              'leftmargin': stack[0].lpush,
    341              'rightmargin': stack[0].rpush,
    342              'attrs': stack[0].attrs,
    343              'bgcolor': None}
    344             se = ScrollEntryNode(**data)
    345             return [se]
    346         _lines = []
    347         for each in lines:
    348             entry = self.GetTextEntry(attrs, stack, *each)
    349             _lines.append(entry)
    350 
    351         return _lines
    352 
    353     def GetFirstLine(self):
    354         lpush, rpush = self.GetMargins(0, self.GetLinespace())
    355         textObj = self.GetTextObject('')
    356         self.AddObjectToBuffer(textObj)
    357         self.textbuffers.append(([textObj], self.attrStack[-1]))
    358         return self.GetTextEntry(self.attrStack[-1], [textObj], lpush=lpush, rpush=rpush)
    359 
    360     def GetTextEntry(self, attrs, stack, glyphString = None, inlines = None, links = None, lpush = 0, rpush = 0, bBox = None, baseHeight = 12, baseLine = 10, abspos = 0):
    361         lineWidth = 0
    362         if bBox is not None:
    363             lineWidth = bBox.Width()
    364         data = {'decoClass': SE_TextlineCore,
    365          'glyphString': glyphString or [],
    366          'inlines': inlines or [],
    367          'pos': abspos,
    368          'stack': stack,
    369          'lpush': lpush,
    370          'rpush': rpush,
    371          'maxBaseHeight': baseHeight,
    372          'maxBaseLine': baseLine,
    373          'bBox': bBox,
    374          'lineWidth': lineWidth,
    375          'align': attrs['text-align'],
    376          'links': links or [],
    377          'SelectionHandler': self,
    378          'URLHandler': self.sr.window}
    379         entry = ScrollEntryNode(**data)
    380         self.contentHeight = max(self.contentHeight, self.contentHeight + baseHeight)
    381         self.contentWidth = max(self.ReverseScaleDpi(lineWidth) + lpush + rpush, self.contentWidth)
    382         return entry
    383 
    384     def InsertLines(self, stack, abspos, idx, attrs):
    385         lines = self.BreakLines(stack, abspos=abspos)
    386         nodes = self.BuildEntries(lines, stack, attrs)
    387         self.AddNodes(idx, nodes)
    388         return nodes
    389 
    390     def Simplify(self, quiet = 0):
    391         rem = []
    392         for idx, (stack, attrs) in enumerate(self.textbuffers):
    393             oldSize = None
    394             oldColor = None
    395             oldFlags = None
    396             oldA = None
    397             i = 0
    398             while i < len(stack):
    399                 obj = stack[i]
    400                 if obj.type == '<text>':
    401                     if len(obj.letters) == 0 and len(stack) > 1:
    402                         stack.pop(i)
    403                         continue
    404                     if obj.a:
    405                         obj.color = (1.0, 0.65, 0.0, 1.0)
    406                         obj.lcolor = (1.0, 0.65, 0.0, 1.0)
    407                         obj.fontFlags |= fontflags.b | fontflags.u
    408                     if obj.fontSize == oldSize and obj.fontFlags == oldFlags and obj.color == oldColor and obj.a == oldA and stack[i - 1].type == '<text>':
    409                         stack[i - 1].letters += obj.letters
    410                         stack.pop(i)
    411                         continue
    412                     oldSize = obj.fontSize
    413                     oldColor = obj.color
    414                     oldFlags = obj.fontFlags
    415                     oldA = obj.a
    416                 i += 1
    417 
    418             if not len(stack):
    419                 rem.append(idx)
    420 
    421         for idx in rem:
    422             self.textbuffers.pop(idx)
    423 
    424     def AddLines(self, lines):
    425         self.sr.lines += lines
    426 
    427     def AddTextToBuffer(self, text, width = None, fromW = None):
    428         self.AddObjectToBuffer(self.GetTextObject(text, width))
    429 
    430     @telemetry.ZONE_METHOD
    431     def GetTextObject(self, text, width = None):
    432         obj = Bunch()
    433         obj.spacing = self.attrStack[-1]['letter-spacing']
    434         obj.a = self.a
    435         if obj.a:
    436             visited = settings.public.ui.VisitedURLs or []
    437             obj.color = [self.attrStack[-1]['link-color'], self.attrStack[-1]['vlink-color'] or self.attrStack[-1]['link-color']][obj.a.href in visited]
    438             obj.lcolor = self.attrStack[-1]['alink-color'] or obj.color
    439         else:
    440             obj.color = self.attrStack[-1]['color']
    441         obj.fontFlags = self.GetFontFlagFromStack()
    442         obj.valign = self.attrStack[-1]['vertical-align']
    443         obj.type = '<text>'
    444         obj.width = width
    445         if type(text) != types.UnicodeType:
    446             text = unicode(text, 'utf-8', 'replace')
    447         obj.letters = text
    448         obj.wordSpacing = self.attrStack[-1]['word-spacing']
    449         obj.letterSpacing = self.attrStack[-1]['letter-spacing']
    450         obj.fontSize = obj.fontsize = self.attrStack[-1]['font-size']
    451         return obj
    452 
    453     @telemetry.ZONE_METHOD
    454     def AddObjectToBuffer(self, obj):
    455         obj.depth = len(self.attrStack) - 1
    456         obj.tags = []
    457         obj.tagattrs = []
    458         while obj.depth > self.tagdepth:
    459             self.tagdepth += 1
    460             obj.tags.append(self.attrStack[self.tagdepth]['tag'])
    461             obj.tagattrs.append(self.attrStack[self.tagdepth]['attr'])
    462 
    463         self.textbuffer.append(obj)
    464 
    465     @telemetry.ZONE_METHOD
    466     def GetWidthInBuffer(self):
    467         totalWidth = 0
    468         for obj in self.textbuffer:
    469             if obj.type == '<overlay>':
    470                 continue
    471             elif obj.type == '<text>':
    472                 textWidth, textHeight = Label.MeasureTextSize(obj.letters.replace(u'\xa0', u' '), fontsize=obj.fontSize, letterspace=obj.letterSpacing, bold=bool(obj.fontFlags & fontflags.b), italic=bool(obj.fontFlags & fontflags.i))
    473                 totalWidth += textWidth
    474             elif obj.type in '<table><img><input>':
    475                 totalWidth += obj.width
    476 
    477         return totalWidth
    478 
    479     @telemetry.ZONE_METHOD
    480     def NewLine(self):
    481         self.FlushBuffer()
    482         if type(self.attrStack[-1]['line-height']) == str and self.attrStack[-1]['line-height'].endswith('%'):
    483             self.attrStack[-1]['line-height'] = int(float(self.attrStack[-1]['line-height'][:-1]) * self.attrStack[-1]['font-size'] / 100.0)
    484         linespace = self.attrStack[-1]['line-height'] or self.attrStack[-1]['font-size']
    485         lpush, rpush = self.GetMargins(self.contentHeight, self.contentHeight + linespace)
    486         self.FlushBuffer([([],
    487           [],
    488           [],
    489           lpush,
    490           rpush,
    491           None,
    492           linespace,
    493           0,
    494           0)])
    495 
    496     @telemetry.ZONE_METHOD
    497     def GetMargins(self, fromY, toY):
    498         blockquote = self.blockquote * 32
    499         list = self.list * 32
    500         if self.destroyed:
    501             return (0, 0)
    502         contentWidth = self.GetContentWidth()
    503         self.attrStack[-1]['margin-left'] = self.GetPercent(self.attrStack[-1]['margin-left'], contentWidth)
    504         self.attrStack[-1]['margin-right'] = self.GetPercent(self.attrStack[-1]['margin-right'], contentWidth)
    505         lpush = self.attrStack[-1]['margin-left'] + self.attrStack[-1]['padding-left'] + self.attrStack[-1]['border-left-width'] + blockquote + list + self.xmargin
    506         rpush = self.attrStack[-1]['margin-right'] + self.attrStack[-1]['padding-right'] + self.attrStack[-1]['border-right-width'] + blockquote + self.xmargin
    507         for overlay, attrs, x, y in self.sr.overlays:
    508             if overlay.state == uiconst.UI_HIDDEN:
    509                 continue
    510             w = overlay.width
    511             h = overlay.height
    512             y = attrs.top
    513             if toY > y and fromY < y + h:
    514                 if attrs.align == 'left':
    515                     lpush = max(lpush, getattr(attrs, 'left', 0) + w)
    516                 elif attrs.align == 'right':
    517                     rpush = max(rpush, getattr(attrs, 'left', 0) + w)
    518 
    519         return (lpush, rpush)
    520 
    521     @telemetry.ZONE_METHOD
    522     def StackPosToObjPos(self, stack, abspos, allobj = 0):
    523         objpos = 0
    524         pos = abspos
    525         for obj in stack:
    526             if not obj.letters:
    527                 if allobj == 1 and pos < 1:
    528                     objpos = stack.index(obj)
    529                     break
    530                 pos -= 1
    531                 continue
    532             if pos < len(obj.letters):
    533                 objpos = stack.index(obj)
    534                 break
    535             pos -= len(obj.letters)
    536         else:
    537             return (0, abspos)
    538 
    539         return (objpos, pos)
    540 
    541     @telemetry.ZONE_METHOD
    542     def GetWidths(self, textbuffer):
    543         s = uicore.font.GetGlyphString()
    544         s.shadow = None
    545         for obj in textbuffer:
    546             fParams = obj
    547             fParams.fontsize = obj.fontSize
    548             if obj.type == '<overlay>':
    549                 continue
    550             elif obj.type == '<text>':
    551                 if not obj.letters:
    552                     continue
    553                 fParams = obj
    554                 fParams.fontsize = obj.fontSize
    555                 fParams.letterspace = obj.letterSpacing
    556                 fParams.wordspace = obj.wordSpacing
    557                 fParams.bold = bool(obj.fontFlags & fontflags.b)
    558                 fParams.italic = bool(obj.fontFlags & fontflags.i)
    559                 fParams.underline = bool(obj.fontFlags & fontflags.u)
    560                 s.Append(fParams, obj.letters)
    561             elif obj.type in ('<table>', '<img>'):
    562                 s.AddSpace(fParams, obj.control.width)
    563             else:
    564                 s.AddSpace(fParams, obj.width)
    565 
    566         w = 0
    567         wordWidth = 0
    568         for t in s:
    569             if t[4] == u' ':
    570                 maxWidth = max(wordWidth, w)
    571                 w = 0
    572             else:
    573                 w += t[0]
    574             if w > self.singleWordMax:
    575                 w = 0
    576                 continue
    577             wordWidth = max(wordWidth, w)
    578 
    579         self.minWidth = max(self.minWidth, wordWidth)
    580         self.totalWidth = max(self.totalWidth, s.width)
    581 
    582     @telemetry.ZONE_METHOD
    583     def GetLine(self, textbuffer, startpos, recurse):
    584         s = self.glyphString
    585         if s:
    586             s.Reset()
    587         else:
    588             s = uicore.font.GetGlyphString()
    589             s.shadow = None
    590             self.glyphString = s
    591         bufnum, pos = self.StackPosToObjPos(textbuffer, startpos, allobj=1)
    592         inlines = []
    593         links = []
    594         ustart = astart = None
    595         wordlen = 0
    596         aa = None
    597         acolor = None
    598         activeLinkText = None
    599         curr = stackless.getcurrent()
    600         onMainThread = curr.is_main
    601         contentWidth = self.GetContentWidth()
    602         startTime = blue.os.GetWallclockTime()
    603         while contentWidth <= 0 and not onMainThread and not self.destroyed:
    604             blue.synchro.Yield()
    605             contentWidth = self.GetContentWidth()
    606             if blue.os.TimeDiffInMs(startTime, blue.os.GetWallclockTime()) > 500:
    607                 contentWidth = 256
    608                 warning = 'Someone is trying to load text into Edit which has 0 or less width. This controls visible state under desktop is: ' + str(self.IsVisible()) + '. Edit location: ' + GetTrace(self, trace='', div='/')
    609                 log.LogWarn(warning)
    610                 break
    611 
    612         for i in xrange(bufnum, len(textbuffer)):
    613             obj = textbuffer[i]
    614             fParams = obj
    615             fParams.fontsize = self.attrStack[-1]['font-size']
    616             if obj.type == '<overlay>':
    617                 obj.attrs.top = self.contentHeight
    618                 obj.overlay.top = self.contentHeight
    619                 obj.overlay.state = uiconst.UI_NORMAL
    620                 if obj.attrs.Get('align', None) == 'right':
    621                     obj.overlay.left = contentWidth - obj.overlay.width - obj.attrs.left
    622                 if not obj.overlay.loaded:
    623                     obj.overlay.Load()
    624             elif obj.type == '<text>':
    625                 fParams.fontsize = fontsize = obj.fontSize
    626                 fParams.letterspace = obj.letterSpacing
    627                 fParams.wordspace = obj.wordSpacing
    628                 fParams.underline = bool(obj.fontFlags & fontflags.u)
    629                 fParams.bold = bool(obj.fontFlags & fontflags.b)
    630                 fParams.italic = bool(obj.fontFlags & fontflags.i)
    631                 if astart is not None and aa != obj.a:
    632                     links.append((astart,
    633                      uicore.ReverseScaleDpi(s.width),
    634                      aa,
    635                      acolor,
    636                      activeLinkText))
    637                     astart = None
    638                     activeLinkText = None
    639                 if obj.a and astart is None:
    640                     astart = uicore.ReverseScaleDpi(s.width)
    641                     aa = obj.a
    642                     acolor = obj.lcolor
    643                     activeLinkText = obj.letters
    644                 if pos:
    645                     s.Append(fParams, obj.letters[pos:])
    646                     pos = 0
    647                 else:
    648                     s.Append(fParams, obj.letters)
    649             elif obj.type in '<table><img><input>':
    650                 objectWidth = obj.width
    651                 obj.inlineWidth = obj.control.width
    652                 obj.inlineHeight = obj.control.height
    653                 inlines.append((obj, uicore.ReverseScaleDpi(s.GetWidth())))
    654                 s.AddSpace(fParams, objectWidth)
    655             else:
    656                 s.AddSpace(fParams, obj.width)
    657 
    658         if astart is not None:
    659             links.append((astart,
    660              uicore.ReverseScaleDpi(s.width),
    661              aa,
    662              acolor,
    663              activeLinkText))
    664         rpush, lpush = self.GetMargins(self.contentHeight + self.batchHeight, self.contentHeight + self.batchHeight + self.GetLinespace())
    665         contentWidth = self.GetContentWidth()
    666         text = s.GetText()
    667         wpl = eveLocalization.WrapPointList(unicode(text), session.languageID)
    668         wrapPoints = wpl.GetLinebreakPoints()
    669         scaledWrapWidth = 0
    670         scaledGlyphStringPosition = 0
    671         lastWrapIndex = 0
    672         lastLineWrapIndex = 0
    673         lastLineXPosition = 0
    674         maxwidth = contentWidth - (lpush + rpush)
    675         i = 0
    676         for t in s:
    677             advance = t[0]
    678             if uicore.ReverseScaleDpi(scaledWrapWidth + advance) > maxwidth:
    679                 if lastWrapIndex == lastLineWrapIndex:
    680                     lastWrapIndex = i
    681                 line = uicore.font.GetGlyphString()
    682                 line.shadow = None
    683                 line += s[lastLineWrapIndex:lastWrapIndex]
    684                 scaledNewLineWidth = sum([ each[0] for each in line ])
    685                 yield (rpush,
    686                  lpush,
    687                  startpos + lastLineWrapIndex,
    688                  line,
    689                  self.GetOthers(inlines, links, uicore.ReverseScaleDpi(scaledGlyphStringPosition), uicore.ReverseScaleDpi(scaledNewLineWidth), fParams))
    690                 if not recurse:
    691                     return
    692                 lastLineWrapIndex = lastWrapIndex
    693                 scaledWrapWidth -= scaledNewLineWidth
    694                 scaledGlyphStringPosition += scaledNewLineWidth
    695                 rpush, lpush = self.GetMargins(self.contentHeight + self.batchHeight, self.contentHeight + self.batchHeight + self.GetLinespace())
    696                 maxwidth = contentWidth - (lpush + rpush)
    697             if i in wrapPoints:
    698                 lastWrapIndex = i
    699             scaledWrapWidth += advance
    700             i += 1
    701 
    702         if i > lastLineWrapIndex:
    703             line = uicore.font.GetGlyphString()
    704             line.shadow = None
    705             line += s[lastLineWrapIndex:]
    706             scaledNewLineWidth = sum([ each[0] for each in line ])
    707             yield (rpush,
    708              lpush,
    709              startpos + lastLineWrapIndex,
    710              line,
    711              self.GetOthers(inlines, links, uicore.ReverseScaleDpi(scaledGlyphStringPosition), uicore.ReverseScaleDpi(scaledNewLineWidth), fParams))
    712 
    713     @telemetry.ZONE_METHOD
    714     def GetOthers(self, inlines, links, x, width, fParam):
    715         endpos = x + width
    716         newinlines = []
    717         for obj, pos in inlines:
    718             if x <= pos < endpos:
    719                 newinlines.append((obj, pos - x))
    720 
    721         newlinks = []
    722         for s, e, aa, color, linkText in links:
    723             if not aa.href:
    724                 continue
    725             e = min(e - x, width + s)
    726             s = max(s - x, 0)
    727             if s < e and s < width:
    728                 link = Bunch()
    729                 link.left = s
    730                 link.hint = aa.alt or ''
    731                 link.url = aa.href
    732                 link.width = e - s
    733                 link.alink_color = color
    734                 link.linkText = linkText
    735                 if not aa.href.startswith('showinfo:'):
    736                     link.draggable = False
    737                 newlinks.append(link)
    738 
    739         return (newinlines, newlinks)
    740 
    741     @telemetry.ZONE_METHOD
    742     def BreakLines(self, textbuffer, batchHeight = 0, abspos = 0, recurse = 1):
    743         if self.getWidths:
    744             self.GetWidths(textbuffer)
    745             return
    746         if not len(textbuffer):
    747             return []
    748         self.batchHeight = batchHeight
    749         lines = []
    750         for rpush, lpush, startpos, s, others in self.GetLine(textbuffer, abspos, recurse):
    751             inlines, links = others
    752             bBox = s.GetBBox()
    753             baseHeight = max(uicore.ReverseScaleDpi(s.baseHeight), self.attrStack[-1]['line-height']) or self.attrStack[-1]['font-size']
    754             baseLine = uicore.ReverseScaleDpi(s.baseLine)
    755             for obj, pos in inlines:
    756                 objectWidth = obj.width
    757                 objectHeight = obj.height
    758                 obj.inlineWidth = obj.control.width
    759                 obj.inlineHeight = obj.control.height
    760                 valign = getattr(obj, 'valign', parser.ALIGNBASELINE)
    761                 if valign in (parser.ALIGNTOP, parser.ALIGNSUB, parser.ALIGNSUPER):
    762                     baseHeight = max(baseHeight, objectHeight)
    763                 elif valign == parser.ALIGNMIDDLE:
    764                     baseLine = max(baseLine, max(baseHeight, objectHeight) / 2 - (baseHeight - baseLine) / 2)
    765                     baseHeight = max(baseHeight, objectHeight)
    766                 elif valign == parser.ALIGNBOTTOM:
    767                     diffBefore = baseHeight - baseLine
    768                     baseHeight = max(baseHeight, objectHeight)
    769                     baseLine = baseHeight - diffBefore
    770                 elif valign == parser.ALIGNBASELINE:
    771                     if objectHeight > baseLine:
    772                         diffBefore = baseHeight - baseLine
    773                         baseHeight = objectHeight + diffBefore
    774                         baseLine = objectHeight
    775 
    776             lines.append((s,
    777              inlines,
    778              links,
    779              rpush,
    780              lpush,
    781              bBox,
    782              baseHeight,
    783              baseLine,
    784              startpos))
    785             self.batchHeight += baseHeight
    786 
    787         return lines
    788 
    789     def AddOverlay(self, attrs, x, y, lpush = None, rpush = None):
    790         attrs.height = getattr(attrs, 'height', 1) or 1
    791         if lpush is None or rpush is None:
    792             lpush, rpush = self.GetMargins(y, y + attrs.height)
    793         if attrs.align == 'right':
    794             attrs.left = rpush + getattr(attrs, 'rightmargin', 0)
    795         elif attrs.align == 'left':
    796             attrs.left = lpush + getattr(attrs, 'leftmargin', 0)
    797         else:
    798             attrs.left = x + lpush
    799         attrs.top = y
    800         overlay = self.GetOverlay_Underlay(self.GetScrollEntry(attrs), self.sr.overlays_content)
    801         self.sr.overlays.append((overlay,
    802          attrs,
    803          x,
    804          y))
    805         return overlay
    806 
    807     def GetScrollEntry(self, attrs):
    808         decoClass = get_inline_deco_class(attrs.type)
    809         return ScrollEntryNode(decoClass=decoClass, attrs=attrs)
    810 
    811     def AddUnderlay(self, attrs, x, y):
    812         attrs.left = x
    813         attrs.top = y
    814         underlay = self.GetOverlay_Underlay(self.GetScrollEntry(attrs), self.sr.underlays_content)
    815         self.sr.underlays.append((underlay,
    816          attrs,
    817          x,
    818          y))
    819 
    820     def AddFloater(self):
    821         attrs = self.attrStack[-1]['attr']
    822         attrs.type = 'div'
    823         attrs.content = self.attrStack[-1]['bufferStack'][:]
    824         self.attrStack[-1]['bufferStack'] = None
    825         attrs.top = getattr(attrs, 'top', self.contentHeight)
    826         attrs.stack = self.attrStack[-1].copy()
    827         floater = self.GetOverlay_Underlay(self.GetScrollEntry(attrs), self.sr.overlays_content)
    828         floater.state = uiconst.UI_NORMAL
    829         floater.SetOrder(0)
    830         contentWidth = self.GetContentWidth()
    831         if self.attrStack[-1]['float'] and not getattr(attrs, 'left', 0):
    832             attrs.align = self.attrStack[-1]['float']
    833             y = attrs.top
    834             lpush, rpush = self.GetMargins(-1, -1)
    835             if lpush + floater.width + rpush <= contentWidth:
    836                 while True:
    837                     lpush, rpush = self.GetMargins(y, y + floater.height)
    838                     if lpush + floater.width + rpush < contentWidth:
    839                         break
    840                     y += floater.height
    841                     if y > 100000:
    842                         raise RuntimeError("OK, at this point it seems obviousI don't know how to render this one.I give up so I won't loop forever.")
    843 
    844             floater.left = attrs.left = lpush
    845             floater.top = attrs.top = y
    846         self.contentHeight = max(self.contentHeight, floater.height)
    847         self.contentWidth = max(self.contentWidth, floater.width)
    848         self.sr.overlays.append((floater,
    849          attrs,
    850          getattr(attrs, 'left', 0),
    851          attrs.top))
    852         self.blockBufferClose = 0
    853 
    854     def GetOverlay_Underlay(self, data, container):
    855         if getattr(data, 'attrs', None) and getattr(data.attrs, 'uiwindow', None):
    856             overlay = data.attrs.uiwindow
    857             overlay.SetParent(container)
    858         else:
    859             overlay = data.decoClass(parent=container)
    860             overlay.state = uiconst.UI_HIDDEN
    861             overlay.data = data
    862             overlay.Startup(self)
    863             overlay.loaded = 0
    864         return overlay
    865 
    866     def AddBorder(self, y, height):
    867         if self.getWidths:
    868             return
    869         s = self.attrStack[-1]
    870         if s['background-color'] or s['border-left-width'] != 0 or s['border-right-width'] != 0 or s['border-top-width'] != 0 or s['border-bottom-width'] != 0:
    871             lpush, rpush = self.GetMargins(y, y + 1)
    872             contentWidth = self.GetContentWidth()
    873             attrs = Bunch()
    874             attrs.bgcolor = s['background-color']
    875             attrs.lbstyle = s['border-left-style']
    876             attrs.rbstyle = s['border-right-style']
    877             attrs.tbstyle = s['border-top-style']
    878             attrs.bbstyle = s['border-bottom-style']
    879             attrs.lbcolor = s['border-left-color']
    880             attrs.rbcolor = s['border-right-color']
    881             attrs.tbcolor = s['border-top-color']
    882             attrs.bbcolor = s['border-bottom-color']
    883             attrs.lbwidth = s['border-left-width']
    884             attrs.rbwidth = s['border-right-width']
    885             attrs.tbwidth = s['border-top-width']
    886             attrs.bbwidth = s['border-bottom-width']
    887             attrs.type = 'border'
    888             attrs.height = height - s['margin-top'] - s['margin-bottom']
    889             attrs.width = contentWidth - (lpush + rpush - s['padding-left'] - s['padding-right'] - s['border-left-width'] - s['border-right-width'])
    890             attrs.border = 10
    891             self.AddUnderlay(attrs, lpush - s['padding-left'] - s['border-left-width'], y + s['margin-top'])
    892 
    893     def AddTopBorders(self):
    894         if self.getWidths:
    895             return
    896         self.attrStack[-1]['margin-top'] = self.GetPercent(self.attrStack[-1]['margin-top'], self.contentHeight)
    897         height = self.attrStack[-1]['margin-top'] + self.attrStack[-1]['padding-top'] + self.attrStack[-1]['border-top-width']
    898         if height:
    899             lpush, rpush = self.GetMargins(self.contentHeight, self.contentHeight + height)
    900             data = {'decoClass': SE_TextlineCore,
    901              'align': 'left',
    902              'lpush': lpush,
    903              'rpush': rpush,
    904              'maxBaseHeight': height,
    905              'stack': [],
    906              'pos': 0}
    907             _lines = [ScrollEntryNode(**data)]
    908             self.contentHeight = max(self.contentHeight, self.contentHeight + height)
    909             self.AddLines(_lines)
    910 
    911     def AddBottomBorders(self):
    912         if self.getWidths:
    913             return
    914         self.attrStack[-1]['margin-bottom'] = self.GetPercent(self.attrStack[-1]['margin-bottom'], self.contentHeight)
    915         height = self.attrStack[-1]['margin-bottom'] + self.attrStack[-1]['padding-bottom'] + self.attrStack[-1]['border-bottom-width']
    916         if height:
    917             lpush, rpush = self.GetMargins(self.contentHeight, self.contentHeight + height)
    918             data = {'decoClass': SE_TextlineCore,
    919              'align': 'left',
    920              'lpush': lpush,
    921              'rpush': rpush,
    922              'maxBaseHeight': height,
    923              'stack': [],
    924              'pos': 0}
    925             _lines = [ScrollEntryNode(**data)]
    926             self.contentHeight = max(self.contentHeight, self.contentHeight + height)
    927             self.AddLines(_lines)
    928 
    929     def ApplyCSS(self, tag, attrs):
    930         s = self.css.ApplyCSS(tag, attrs, self.attrStack)
    931         self.SetStyles(s)
    932         self.attrStack[-1]['tag'] = tag
    933         self.attrStack[-1]['attr'] = attrs
    934 
    935     def SetStyles(self, s):
    936         if s is not None:
    937             self.attrStack[-1].update(s)
    938 
    939     def ParseStdStyles(self, attrs):
    940         if getattr(attrs, 'style', None):
    941             s = self.css.ParseCSS(attrs)
    942             self.attrStack[-1].update(s)
    943         if getattr(attrs, 'id', None):
    944             c = self.css.GetClass(attrs.id)
    945             if c:
    946                 for key, value in c.iteritems():
    947                     if key in ('tag', 'attr'):
    948                         continue
    949                     self.attrStack[-1][key] = value
    950 
    951         elif getattr(attrs, 'class', None):
    952             c = self.css.GetClass(getattr(attrs, 'class', None))
    953             if c:
    954                 for key, value in c.iteritems():
    955                     if key in ('tag', 'attr'):
    956                         continue
    957                     self.attrStack[-1][key] = value
    958 
    959     def AddStyles(self, stack, css):
    960         self.attrStack.append(stack)
    961         self.css = css.copy()
    962 
    963     def CheckURL(self, URL, getAnchor = 0):
    964         browser = GetBrowser(self)
    965         currentURL = None
    966         if browser:
    967             currentURL = browser.sr.currentURL
    968         URL, anchor = browserutil.ParseURL(URL, currentURL)
    969         if URL.startswith('./'):
    970             if currentURL.endswith('/'):
    971                 base = currentURL
    972             else:
    973                 base = browserutil.DirUp(currentURL)
    974             URL = base + URL[2:]
    975         elif URL.startswith('../'):
    976             base = browserutil.DirUp(browserutil.DirUp(currentURL))
    977             URL = base + URL[3:]
    978         if getAnchor:
    979             return (URL, anchor)
    980         return URL
    981 
    982     def LocalizeURL(self, url):
    983         if prefs.languageID != 'EN' and url and (url[:4] == 'res:' or url.find('res') > 0):
    984             current = None
    985             if '/en/' not in url:
    986                 return url
    987             newUrl = url.replace('/en/', '/%s/' % prefs.languageID.lower())
    988             if blue.paths.exists(newUrl):
    989                 return newUrl
    990             log.LogWarn('No localized image found for', url, 'with languageID', prefs.languageID)
    991         return url
    992 
    993     @telemetry.ZONE_METHOD
    994     def OnStart_body(self, attrs):
    995         self.OnStart_default('body', attrs)
    996         self.bodyAttrs = attrs
    997         self.LoadBodyAttrs(attrs)
    998 
    999     @telemetry.ZONE_METHOD
   1000     def OnEnd_body(self):
   1001         self.FlushBuffer()
   1002         self.OnEnd_default('body')
   1003 
   1004     @telemetry.ZONE_METHOD
   1005     def OnStart_meta(self, attrs):
   1006         content = attrs.content
   1007         if content:
   1008             options = content.split(';')
   1009             for option in options:
   1010                 option = option.strip()
   1011                 if option.startswith('charset='):
   1012                     charset = option[8:].strip().lower()
   1013                     try:
   1014                         import codecs
   1015                         codecs.lookup(str(charset))
   1016                         self.charset = charset
   1017                     except:
   1018                         sys.exc_clear()
   1019 
   1020     def IsIconOrSimilarType(self, attrs):
   1021         if attrs.src.startswith('icon:'):
   1022             return True
   1023         return False
   1024 
   1025     def OnStart_img_Game(self, attrs):
   1026         pass
   1027 
   1028     @telemetry.ZONE_METHOD
   1029     def OnStart_img(self, attrs):
   1030         attrs.a = self.a
   1031         attrs.loadlater = 0
   1032         gameHandling = self.OnStart_img_Game(attrs)
   1033         if gameHandling:
   1034             texture, tWidth, tHeight = gameHandling
   1035             attrs.width = getattr(attrs, 'width', None) or tWidth
   1036             attrs.height = getattr(attrs, 'height', None) or tHeight
   1037         elif attrs.src.startswith('res:'):
   1038             if getattr(attrs, 'localize', False):
   1039                 attrs.src = self.LocalizeURL(attrs.src)
   1040             texture, tWidth, tHeight = sm.GetService('browserImage').GetTextureFromURL(attrs.src, fromWhere='OnStart_img')
   1041             attrs.width = getattr(attrs, 'width', None) or tWidth
   1042             attrs.height = getattr(attrs, 'height', None) or tHeight
   1043         else:
   1044             browser = GetBrowser(self)
   1045             currentURL = None
   1046             if browser:
   1047                 currentURL = browser.sr.currentURL
   1048             attrs.src = self.CheckURL(attrs.src)
   1049             tWidth, tHeight = (32, 32)
   1050             if attrs.width is None or attrs.height is None:
   1051                 cache = sm.GetService('browserImage').GetTextureInfoFromURL(attrs.src, currentURL, fromWhere='OnStart_img')
   1052                 if cache is not None:
   1053                     path, tWidth, tHeight, bw, bh = cache
   1054             texture = None
   1055             attrs.loadlater = 1
   1056             attrs.currentURL = currentURL
   1057         if attrs.width:
   1058             if unicode(attrs.width).endswith('%'):
   1059                 attrs.width = int(float(unicode(attrs.width)[:-1]) / 100.0 * self.sr.content.width)
   1060             else:
   1061                 attrs.width = int(attrs.width)
   1062         if attrs.height:
   1063             if unicode(attrs.height).endswith('%'):
   1064                 attrs.height = int(float(unicode(attrs.height)[:-1]) / 100.0 * self.sr.content.height)
   1065             else:
   1066                 attrs.height = int(attrs.height)
   1067         if not attrs.width:
   1068             if attrs.height:
   1069                 attrs.width = int(attrs.height * (tWidth / float(tHeight)))
   1070             else:
   1071                 attrs.width = tWidth
   1072         if not attrs.height:
   1073             if attrs.width:
   1074                 attrs.height = int(attrs.width * (tHeight / float(tWidth)))
   1075             else:
   1076                 attrs.height = tHeight
   1077         s = {'border-left-width': 0,
   1078          'border-right-width': 0,
   1079          'border-top-width': 0,
   1080          'border-bottom-width': 0,
   1081          'margin-left': 0,
   1082          'margin-right': 0,
   1083          'margin-top': 0,
   1084          'margin-bottom': 0,
   1085          'float': None}
   1086         for side in ['left',
   1087          'top',
   1088          'right',
   1089          'bottom']:
   1090             if attrs.border is not None:
   1091                 s['border-%s-width' % side] = self.GetInt(attrs.border)
   1092             if attrs.bordercolor:
   1093                 s['border-%s-color' % side] = ParseHTMLColor(attrs.bordercolor, 1)
   1094 
   1095         if attrs.hspace is not None:
   1096             s['margin-left'] = int(attrs.hspace)
   1097             s['margin-right'] = int(attrs.hspace)
   1098         if attrs.vspace is not None:
   1099             s['margin-top'] = s['margin-bottom'] = int(attrs.vspace)
   1100         if attrs.usemap:
   1101             if attrs.usemap.startswith('#'):
   1102                 umap = attrs.usemap[1:]
   1103                 for each in self.areamap:
   1104                     if each['name'] == umap:
   1105                         attrs.areamap = each['coords']
   1106                         break
   1107 
   1108         attrs.texture = texture
   1109         attrs.type = 'img'
   1110         if getattr(attrs, 'bgcolor', None):
   1111             attrs.bgcolor = ParseHTMLColor(attrs.bgcolor, 1)
   1112         lpush, rpush = self.GetMargins(self.contentHeight, self.contentHeight + attrs.height)
   1113         self.OnStart_default('img', attrs, s)
   1114         for side in ['left',
   1115          'top',
   1116          'right',
   1117          'bottom']:
   1118             setattr(attrs, '%sborder' % side, self.attrStack[-1]['border-%s-width' % side])
   1119             setattr(attrs, '%scolor' % side, self.attrStack[-1]['border-%s-color' % side])
   1120             setattr(attrs, '%smargin' % side, self.attrStack[-1]['margin-%s' % side])
   1121 
   1122         if self.attrStack[-1]['float']:
   1123             attrs.align = self.attrStack[-1]['float']
   1124         if (getattr(attrs, 'align', None) or 'None').lower() not in ('left', 'right'):
   1125             img = self.GetInline(self.GetScrollEntry(attrs))
   1126             obj = Bunch()
   1127             obj.type = '<img>'
   1128             obj.width = attrs.width
   1129             obj.height = attrs.height
   1130             obj.attrs = attrs
   1131             obj.valign = [None, 1][attrs.align == 'center'] or self.attrStack[-1]['vertical-align']
   1132             obj.control = img
   1133             self.AddObjectToBuffer(obj)
   1134         else:
   1135             overlay = self.AddOverlay(attrs, 0, self.contentHeight, lpush=lpush, rpush=rpush)
   1136             obj = Bunch()
   1137             obj.type = '<overlay>'
   1138             obj.attrs = attrs
   1139             obj.overlay = overlay
   1140             obj.lpush = lpush
   1141             obj.rpush = rpush
   1142             self.AddObjectToBuffer(obj)
   1143         self.attrStack.pop()
   1144 
   1145     def OnStart_script(self, attrs):
   1146         self.OnStart_default('script', attrs)
   1147 
   1148     def OnData_script(self, data, attrs):
   1149         pass
   1150 
   1151     def OnEnd_script(self):
   1152         self.OnEnd_default('script')
   1153 
   1154     def OnStart_b(self, attrs):
   1155         self.OnStart_default('b', attrs)
   1156         self.attrStack[-1]['font-weight'] = 'b'
   1157 
   1158     def OnEnd_b(self):
   1159         self.OnEnd_default('b')
   1160         self.attrStack[-1]['font-weight'] = ''
   1161 
   1162     def OnStart_i(self, attrs):
   1163         self.OnStart_default('i', attrs)
   1164         self.attrStack[-1]['font-style'] = 'i'
   1165 
   1166     def OnEnd_i(self):
   1167         self.OnEnd_default('i')
   1168         self.attrStack[-1]['font-style'] = ''
   1169 
   1170     def OnStart_u(self, attrs):
   1171         self.OnStart_default('u', attrs)
   1172         if 'u' not in self.attrStack[-1]['text-decoration']:
   1173             self.attrStack[-1]['text-decoration'] += 'u'
   1174 
   1175     def OnEnd_u(self):
   1176         self.OnEnd_default('u')
   1177         self.attrStack[-1]['text-decoration'] = self.attrStack[-1]['text-decoration'].replace('u', '')
   1178 
   1179     def OnData_pre(self, data, attrs):
   1180         for line in data.replace('\r', '').split('\n'):
   1181             self.AddTextToBuffer(line, fromW='pre')
   1182             if not self.textbuffer:
   1183                 self.NewLine()
   1184             else:
   1185                 self.FlushBuffer()
   1186 
   1187     @telemetry.ZONE_METHOD
   1188     def OnStart_li(self, attrs):
   1189         if self.attrStack[-1]['tag'] == 'li':
   1190             self.OnEnd_li()
   1191         self.FlushBuffer()
   1192         tag = self.attrStack and self.attrStack[-1]['tag']
   1193         if not self.sr.listIndex:
   1194             self.sr.listIndex = [0]
   1195         self.sr.listIndex[-1] += 1
   1196         if tag == 'ul':
   1197             bullType = attrs.__dict__.get('type', None) or self.attrStack[-1]['attr'].__dict__.get('type', 'disc')
   1198             text = {'disc': unichr(8226),
   1199              'square': unichr(9632),
   1200              'circle': unichr(9675)}.get(bullType.lower(), unichr(8226))
   1201         elif tag == 'ol':
   1202             idx = self.sr.listIndex[-1]
   1203             orderType = attrs.__dict__.get('type', None) or self.attrStack[-1]['attr'].__dict__.get('type', '1')
   1204             if orderType in 'aA':
   1205                 text = '%s.' % unichr(96 + idx)
   1206             elif orderType in 'iI':
   1207                 text = eveformat.number_roman(idx).lower()
   1208             else:
   1209                 text = '%s.' % self.sr.listIndex[-1]
   1210             if orderType in 'AI':
   1211                 text = text.upper()
   1212         else:
   1213             text = unichr(8226)
   1214         obj = Bunch()
   1215         obj.width = -24
   1216         obj.type = '<nbsp>'
   1217         self.AddObjectToBuffer(obj)
   1218         self.AddTextToBuffer(text, fromW='OnStart_li')
   1219         obj = Bunch()
   1220         obj.width = 24 - self.GetWidthInBuffer()
   1221         obj.type = '<nbsp>'
   1222         self.AddObjectToBuffer(obj)
   1223         self.OnStart_default('li', attrs)
   1224         self.list += 1
   1225 
   1226     @telemetry.ZONE_METHOD
   1227     def OnEnd_li(self):
   1228         self.FlushBuffer()
   1229         self.OnEnd_default('li')
   1230         self.list = max(0, self.list - 1)
   1231 
   1232     @telemetry.ZONE_METHOD
   1233     def OnStartList(self):
   1234         if self.attrStack and self.attrStack[-1]['tag'] not in ('ul', 'ol'):
   1235             self.FlushBuffer()
   1236         self.sr.listIndex.append(0)
   1237 
   1238     def OnDataList(self, data, attrs):
   1239         if data.strip():
   1240             log.LogError('Character data directly in UL?')
   1241 
   1242     @telemetry.ZONE_METHOD
   1243     def OnEndList(self):
   1244         if len(self.attrStack) > 1 and self.attrStack[-2]['tag'] not in ('ul', 'ol'):
   1245             self.FlushBuffer()
   1246         if len(self.sr.listIndex):
   1247             self.sr.listIndex.pop()
   1248 
   1249     @telemetry.ZONE_METHOD
   1250     def OnStart_ol(self, attrs):
   1251         self.OnStart_default('ol', attrs)
   1252         self.OnStartList()
   1253 
   1254     OnData_ol = OnDataList
   1255 
   1256     @telemetry.ZONE_METHOD
   1257     def OnEnd_ol(self):
   1258         self.OnEndList()
   1259         self.OnEnd_default('ol')
   1260 
   1261     @telemetry.ZONE_METHOD
   1262     def OnStart_ul(self, attrs):
   1263         self.OnStart_default('ul', attrs)
   1264         self.OnStartList()
   1265 
   1266     OnData_ul = OnDataList
   1267 
   1268     @telemetry.ZONE_METHOD
   1269     def OnEnd_ul(self):
   1270         self.OnEndList()
   1271         self.OnEnd_default('ul')
   1272 
   1273     @telemetry.ZONE_METHOD
   1274     def OnStart_sub(self, attrs):
   1275         s = {'vertical-align': 4}
   1276         self.OnStart_default('sub', attrs, s)
   1277 
   1278     @telemetry.ZONE_METHOD
   1279     def OnEnd_sub(self):
   1280         self.OnEnd_default('sub')
   1281 
   1282     @telemetry.ZONE_METHOD
   1283     def OnStart_sup(self, attrs):
   1284         s = {'vertical-align': 5}
   1285         self.OnStart_default('sup', attrs, s)
   1286 
   1287     @telemetry.ZONE_METHOD
   1288     def OnEnd_sup(self):
   1289         self.OnEnd_default('sup')
   1290 
   1291     @telemetry.ZONE_METHOD
   1292     def OnStart_h1(self, attrs):
   1293         s = {'font-size': 28,
   1294          'font-weight': 'b'}
   1295         if attrs.align:
   1296             s['text-align'] = attrs.align
   1297         self.OnStart_default('h1', attrs, s)
   1298         self.FlushBuffer()
   1299 
   1300     @telemetry.ZONE_METHOD
   1301     def OnEnd_h1(self):
   1302         self.FlushBuffer()
   1303         self.OnEnd_default('h1')
   1304 
   1305     @telemetry.ZONE_METHOD
   1306     def OnStart_h2(self, attrs):
   1307         s = {'font-size': 24,
   1308          'font-weight': 'b'}
   1309         if attrs.align:
   1310             s['text-align'] = attrs.align
   1311         self.OnStart_default('h2', attrs, s)
   1312         self.FlushBuffer()
   1313 
   1314     @telemetry.ZONE_METHOD
   1315     def OnEnd_h2(self):
   1316         self.FlushBuffer()
   1317         self.OnEnd_default('h2')
   1318 
   1319     @telemetry.ZONE_METHOD
   1320     def OnStart_h3(self, attrs):
   1321         s = {'font-size': 20,
   1322          'font-weight': 'b'}
   1323         if attrs.align:
   1324             s['text-align'] = attrs.align
   1325         self.OnStart_default('h3', attrs, s)
   1326         self.FlushBuffer()
   1327 
   1328     @telemetry.ZONE_METHOD
   1329     def OnEnd_h3(self):
   1330         self.FlushBuffer()
   1331         self.OnEnd_default('h3')
   1332 
   1333     @telemetry.ZONE_METHOD
   1334     def OnStart_h4(self, attrs):
   1335         s = {'font-size': 18,
   1336          'font-weight': 'b'}
   1337         if attrs.align:
   1338             s['text-align'] = attrs.align
   1339         self.OnStart_default('h4', attrs, s)
   1340         self.FlushBuffer()
   1341 
   1342     @telemetry.ZONE_METHOD
   1343     def OnEnd_h4(self):
   1344         self.FlushBuffer()
   1345         self.OnEnd_default('h4')
   1346 
   1347     @telemetry.ZONE_METHOD
   1348     def OnStart_h5(self, attrs):
   1349         s = {'font-size': 16,
   1350          'font-weight': 'b'}
   1351         if attrs.align:
   1352             s['text-align'] = attrs.align
   1353         self.OnStart_default('h5', attrs, s)
   1354         self.FlushBuffer()
   1355 
   1356     @telemetry.ZONE_METHOD
   1357     def OnEnd_h5(self):
   1358         self.FlushBuffer()
   1359         self.OnEnd_default('h5')
   1360 
   1361     @telemetry.ZONE_METHOD
   1362     def OnStart_h6(self, attrs):
   1363         s = {'font-size': 14,
   1364          'font-weight': 'bold'}
   1365         if attrs.align:
   1366             s['text-align'] = attrs.align
   1367         self.OnStart_default('h6', attrs, s)
   1368         self.FlushBuffer()
   1369 
   1370     @telemetry.ZONE_METHOD
   1371     def OnEnd_h6(self):
   1372         self.FlushBuffer()
   1373         self.OnEnd_default('h6')
   1374 
   1375     @telemetry.ZONE_METHOD
   1376     def OnStart_style(self, attrs):
   1377         self.OnStart_default('style', attrs)
   1378 
   1379     @telemetry.ZONE_METHOD
   1380     def OnData_style(self, data, attrs):
   1381         self.css.ParseCSSData(data)
   1382 
   1383     @telemetry.ZONE_METHOD
   1384     def OnEnd_style(self):
   1385         self.OnEnd_default('style')
   1386 
   1387     @telemetry.ZONE_METHOD
   1388     def OnStart_link(self, attrs):
   1389         rel = attrs.rel
   1390         t = attrs.type
   1391         href = attrs.href
   1392         if not rel or not href:
   1393             return
   1394         if rel.lower() == 'stylesheet':
   1395             url = self.CheckURL(href)
   1396             browser = GetBrowser(self)
   1397             currentURL = None
   1398             if browser:
   1399                 currentURL = browser.sr.currentURL
   1400             if href.startswith('res:') or href.startswith('script:'):
   1401                 try:
   1402                     f = blue.ResFile()
   1403                     f.OpenAlways(href)
   1404                     self.css.ParseCSSData(str(f.Read()))
   1405                     del f
   1406                 except Exception:
   1407                     log.LogException()
   1408 
   1409                 return
   1410             fullPath = browserutil.ParseURL(url, currentURL)[0]
   1411             try:
   1412                 buff = browserutil.GetStringFromURL(*(browserutil.ParseURL(fullPath)[:1] + (None,))).read()
   1413                 self.css.ParseCSSData(buff)
   1414             except urllib2.URLError as what:
   1415                 sys.exc_clear()
   1416             except Exception:
   1417                 log.LogException()
   1418 
   1419     @telemetry.ZONE_METHOD
   1420     def OnStart_title(self, attrs):
   1421         self.OnStart_default('title', attrs)
   1422 
   1423     @telemetry.ZONE_METHOD
   1424     def OnData_title(self, data, attrs):
   1425         data = data.strip()
   1426         self.title = data
   1427         if self.sr.window and getattr(self.sr.window, 'SetCaption', None):
   1428             self.sr.window.SetCaption(data)
   1429 
   1430     @telemetry.ZONE_METHOD
   1431     def OnEnd_title(self):
   1432         self.OnEnd_default('title')
   1433 
   1434     @telemetry.ZONE_METHOD
   1435     def OnStart_form(self, attrs):
   1436         if not self or getattr(self, 'sr', None) is None:
   1437             return
   1438         if self.sr.form:
   1439             self.OnEnd_form()
   1440         if self.sr.Get('browser', None):
   1441             self.sr.form = browserutil.NewBrowserForm(attrs, _weakref.proxy(self.sr.browser))
   1442         else:
   1443             self.sr.form = browserutil.NewBrowserForm(attrs, _weakref.proxy(self))
   1444 
   1445     @telemetry.ZONE_METHOD
   1446     def OnEnd_form(self):
   1447         self.sr.forms.append(self.sr.form)
   1448         self.sr.form = None
   1449 
   1450     @telemetry.ZONE_METHOD
   1451     def OnStart_input(self, attrs):
   1452         form = self.sr.form or getattr(attrs, 'form', None)
   1453         if form is None:
   1454             form = self.sr.form = browserutil.NewBrowserForm(attrs, _weakref.proxy(self))
   1455         s = {'width': None,
   1456          'height': None}
   1457         self.OnStart_default('input', attrs, s)
   1458         if not hasattr(attrs, 'valign'):
   1459             attrs.valign = 1
   1460         attrs.fontcolor = self.attrStack[-1]['color']
   1461         attrs.bordercolor = self.attrStack[-1]['color']
   1462         attrs.form = form
   1463         attrs.width = self.attrStack[-1]['width']
   1464         attrs.height = self.attrStack[-1]['height']
   1465         if getattr(attrs, 'value', None):
   1466             attrs.value = self.sr.parser.decode_string(attrs.value)
   1467         obj = form.AddInput(attrs, not self.getWidths)
   1468         if obj:
   1469             self.AddObjectToBuffer(obj)
   1470         self.attrStack.pop()
   1471 
   1472     def OnEnd_input(self):
   1473         pass
   1474 
   1475     @telemetry.ZONE_METHOD
   1476     def OnStart_select(self, attrs):
   1477         form = self.sr.form or getattr(attrs, 'form', None)
   1478         if form is None:
   1479             form = self.sr.form = browserutil.NewBrowserForm(attrs, _weakref.proxy(self))
   1480         self.sr.select = Bunch()
   1481         self.sr.select.attrs = attrs
   1482         self.sr.select.attrs.form = form
   1483         self.sr.select.options = []
   1484         s = {'width': None,
   1485          'height': None}
   1486         self.OnStart_default('select', attrs, s)
   1487         attrs.fontcolor = self.attrStack[-1]['color']
   1488         attrs.width = self.attrStack[-1]['width']
   1489         attrs.height = self.attrStack[-1]['height']
   1490 
   1491     @telemetry.ZONE_METHOD
   1492     def OnEnd_select(self):
   1493         if self.sr.select is None:
   1494             raise RuntimeError('ParsingError', {'what': 'Unmatched select end'})
   1495         obj = self.sr.select.attrs.form.AddSelect(self.sr.select.attrs, self.sr.select.options, not self.getWidths)
   1496         if obj:
   1497             self.AddObjectToBuffer(obj)
   1498         self.sr.select = None
   1499         self.OnEnd_default('select')
   1500 
   1501     def OnData_select(self, data, attrs):
   1502         pass
   1503 
   1504     def OnStart_option(self, attrs):
   1505         self.OnStart_default('option', attrs)
   1506 
   1507     def OnData_option(self, data, attrs):
   1508         self.sr.buffer += data
   1509 
   1510     @telemetry.ZONE_METHOD
   1511     def OnEnd_option(self):
   1512         if self.sr.select is not None:
   1513             self.sr.buffer = self.sr.buffer.replace('\n', '').replace('\r', '')
   1514             value = self.attrStack[-1]['attr'].value
   1515             if value is None:
   1516                 value = self.sr.buffer
   1517             self.sr.select.options.append((self.sr.buffer, value, self.attrStack[-1]['attr'].selected))
   1518             self.sr.buffer = ''
   1519         else:
   1520             log.LogWarn('Bogus HTML syntax, Option outside any select')
   1521         self.OnEnd_default('option')
   1522 
   1523     @telemetry.ZONE_METHOD
   1524     def OnStart_textarea(self, attrs):
   1525         s = {'width': None,
   1526          'height': None}
   1527         self.OnStart_default('textarea', attrs, s)
   1528         form = self.sr.form or getattr(attrs, 'form', None)
   1529         if form is None:
   1530             form = self.sr.form = browserutil.NewBrowserForm(attrs, _weakref.proxy(self))
   1531         attrs.fontcolor = self.attrStack[-1]['color']
   1532         attrs.width = self.attrStack[-1]['width']
   1533         attrs.height = self.attrStack[-1]['height']
   1534         attrs.form = form
   1535 
   1536     @telemetry.ZONE_METHOD
   1537     def OnData_textarea(self, data, attrs):
   1538         data = data.replace('\n', '<br>').replace('\r', '')
   1539         self.sr.buffer += data
   1540 
   1541     @telemetry.ZONE_METHOD
   1542     def OnEnd_textarea(self):
   1543         attrs = self.attrStack[-1]['attr']
   1544         attrs.value = self.sr.buffer
   1545         obj = attrs.form.AddTextArea(attrs, not self.getWidths)
   1546         if obj:
   1547             self.AddObjectToBuffer(obj)
   1548         self.sr.buffer = ''
   1549         self.OnEnd_default('textarea')
   1550 
   1551     @telemetry.ZONE_METHOD
   1552     def OnStart_center(self, attrs):
   1553         self.FlushBuffer()
   1554         self.OnStart_default('center', attrs)
   1555         self.attrStack[-1]['text-align'] = 'center'
   1556 
   1557     @telemetry.ZONE_METHOD
   1558     def OnEnd_center(self):
   1559         self.FlushBuffer()
   1560         self.OnEnd_default('center')
   1561 
   1562     @telemetry.ZONE_METHOD
   1563     def OnStart_p(self, attrs):
   1564         if self.attrStack[-1]['tag'] == 'p':
   1565             self.OnEnd_p()
   1566         s = {}
   1567         s['text-align'] = getattr(attrs, 'align', None)
   1568         self.OnStart_default('p', attrs, s)
   1569         self.NewLine()
   1570         if int(self.attrStack[-1]['text-indent']) > 0:
   1571             obj = Bunch()
   1572             obj.width = int(self.attrStack[-1]['text-indent'])
   1573             obj.type = '<list>'
   1574             self.AddObjectToBuffer(obj)
   1575         self.attrStack[-1]['pstart'] = self.contentHeight
   1576         self.AddTopBorders()
   1577 
   1578     @telemetry.ZONE_METHOD
   1579     def OnEnd_p(self):
   1580         if self.attrStack[-1]['margin-bottom'] == 0:
   1581             self.NewLine()
   1582         else:
   1583             self.FlushBuffer()
   1584         self.AddBottomBorders()
   1585         self.AddBorder(self.attrStack[-1]['pstart'], self.contentHeight - self.attrStack[-1]['pstart'])
   1586         self.OnEnd_default('p')
   1587 
   1588     @telemetry.ZONE_METHOD
   1589     def OnStart_font(self, attrs):
   1590         s = {}
   1591         fontsize = getattr(attrs, 'size', None)
   1592         basefontsize = None
   1593         if unicode(fontsize).startswith('+') or unicode(fontsize).startswith('-'):
   1594             try:
   1595                 m = int(fontsize[1:])
   1596             except:
   1597                 m = 0
   1598                 sys.exc_clear()
   1599 
   1600             basefontsize = max(0, min(7, self.attrStack[-1]['basefontsize'] + [-1, 1][unicode(fontsize).startswith('+')] * m))
   1601         elif len(unicode(fontsize)) == 1 and unicode(fontsize) in '1234567':
   1602             try:
   1603                 basefontsize = int(fontsize)
   1604             except:
   1605                 sys.exc_clear()
   1606 
   1607         if basefontsize is not None:
   1608             fontsize = {1: 6,
   1609              2: 8,
   1610              3: 10,
   1611              4: 12,
   1612              5: 14,
   1613              6: 17,
   1614              7: 20}.get(basefontsize, fontsize)
   1615             s['basefontsize'] = basefontsize
   1616         spacing = getattr(attrs, 'letter-spacing', None) or getattr(attrs, 'space', None)
   1617         if fontsize is not None and unicode(fontsize).isdigit():
   1618             s['font-size'] = int(fontsize)
   1619         if spacing is not None:
   1620             s['letter-spacing'] = int(spacing)
   1621         if getattr(attrs, 'color', None):
   1622             s['color'] = ParseHTMLColor(getattr(attrs, 'color', None), 1)
   1623         self.OnStart_default('font', attrs, s)
   1624 
   1625     @telemetry.ZONE_METHOD
   1626     def OnEnd_font(self):
   1627         self.OnEnd_default('font')
   1628 
   1629     @telemetry.ZONE_METHOD
   1630     def OnStart_br(self, attrs):
   1631         if not self.textbuffer:
   1632             self.NewLine()
   1633         else:
   1634             self.FlushBuffer()
   1635 
   1636     @telemetry.ZONE_METHOD
   1637     def OnStart_hr(self, attrs):
   1638         self.FlushBuffer()
   1639         s = {'margin-top': 2,
   1640          'margin-bottom': 2,
   1641          'padding-top': 0,
   1642          'padding-bottom': 0,
   1643          'border-top-width': 0,
   1644          'border-bottom-width': 0}
   1645         self.OnStart_default('hr', attrs, s)
   1646         self.AddTopBorders()
   1647         self.FlushBuffer()
   1648         lineHeight = int(getattr(attrs, 'size', 0) or 1)
   1649         lpush, rpush = self.GetMargins(self.contentHeight, self.contentHeight + lineHeight)
   1650         obj = Bunch()
   1651         obj.type = '<hr>'
   1652         obj.width = 0
   1653         obj.height = lineHeight
   1654         obj.lpush = lpush
   1655         obj.rpush = rpush
   1656         obj.attrs = attrs
   1657         self.AddObjectToBuffer(obj)
   1658         self.contentHeight += lineHeight
   1659         self.FlushBuffer()
   1660         self.AddBottomBorders()
   1661         self.attrStack.pop()
   1662 
   1663     def ClearAttrs(self, s):
   1664         for side in ['left',
   1665          'top',
   1666          'right',
   1667          'bottom']:
   1668             s['border-%s-width' % side] = 0
   1669             s['border-%s-color' % side] = None
   1670             s['margin-%s' % side] = 0
   1671 
   1672         s['background-color'] = None
   1673 
   1674     def OnStart_blockquote(self, attrs):
   1675         self.NewLine()
   1676         self.OnStart_default('blockquote', attrs)
   1677         self.blockquote += 1
   1678 
   1679     def OnEnd_blockquote(self):
   1680         self.NewLine()
   1681         self.OnEnd_default('blockquote')
   1682         self.blockquote = max(0, self.blockquote - 1)
   1683 
   1684     def OnStart_var(self, attrs):
   1685         s = {'font-style': 'i'}
   1686         self.OnStart_default('var', attrs, s)
   1687 
   1688     def OnEnd_var(self):
   1689         self.OnEnd_default('var')
   1690 
   1691     def OnStart_cite(self, attrs):
   1692         s = {'font-style': 'i'}
   1693         self.OnStart_default('cite', attrs, s)
   1694 
   1695     def OnEnd_cite(self):
   1696         self.OnEnd_default('cite')
   1697 
   1698     def OnStart_address(self, attrs):
   1699         s = {'font-style': 'i'}
   1700         self.OnStart_default('address', attrs, s)
   1701 
   1702     def OnEnd_address(self):
   1703         self.OnEnd_default('address')
   1704 
   1705     def OnStart_dfn(self, attrs):
   1706         s = {'font-style': 'i'}
   1707         self.OnStart_default('dfn', attrs, s)
   1708 
   1709     def OnEnd_dfn(self):
   1710         self.OnEnd_default('dfn')
   1711 
   1712     def OnStart_code(self, attrs):
   1713         self.OnStart_default('code', attrs, s)
   1714         self.plainload = 1
   1715 
   1716     def OnEnd_code(self):
   1717         self.plainload = 0
   1718         self.OnEnd_default('code')
   1719 
   1720     def OnStart_frame(self, attrs):
   1721         pass
   1722 
   1723     @telemetry.ZONE_METHOD
   1724     def OnStart_div(self, attrs):
   1725         self.FlushBuffer()
   1726         contentWidth = self.GetContentWidth()
   1727         if hasattr(attrs, 'width') and type(attrs.width) == str and attrs.width.endswith('%'):
   1728             attrs.width = self.GetInt(attrs.width) * contentWidth / 100.0
   1729         else:
   1730             attrs.width = int(getattr(attrs, 'width', None) or contentWidth)
   1731         attrs.height = int(getattr(attrs, 'height', None) or 0)
   1732         attrs.left = int(getattr(attrs, 'left', None) or 0)
   1733         attrs.top = int(getattr(attrs, 'top', self.contentHeight) or 0)
   1734         s = {'position': 'unset',
   1735          'text-align': 'left',
   1736          'float': None,
   1737          'width': attrs.width,
   1738          'height': attrs.height,
   1739          'left': attrs.left,
   1740          'top': attrs.top}
   1741         for side in ['left',
   1742          'top',
   1743          'right',
   1744          'bottom']:
   1745             s['border-%s-width' % side] = int(attrs.border or 0)
   1746             if attrs.bordercolor:
   1747                 s['border-%s-color' % side] = ParseHTMLColor(attrs.bordercolor, 1)
   1748 
   1749         if attrs.align:
   1750             s['text-align'] = attrs.align
   1751             s['align'] = attrs.align
   1752         if attrs.bgcolor is not None:
   1753             s['background-color'] = ParseHTMLColor(attrs.bgcolor, 1)
   1754         self.OnStart_default('div', attrs, s)
   1755         if self.attrStack[-1]['width'] is not None:
   1756             attrs.width = self.attrStack[-1]['width']
   1757         if self.attrStack[-1]['height'] is not None:
   1758             attrs.width = self.attrStack[-1]['height']
   1759         if self.attrStack[-1]['position'] in ('absolute', 'relative') or self.attrStack[-1]['float'] in ('left', 'right'):
   1760             self.attrStack[-1]['bufferStack'] = [('AddStyles', (self.attrStack[-1].copy(), self.css.copy()))]
   1761             self.divs += 1
   1762         else:
   1763             self.attrStack[-1]['pstart'] = self.contentHeight
   1764             self.AddTopBorders()
   1765 
   1766     @telemetry.ZONE_METHOD
   1767     def OnEnd_div(self):
   1768         if self.attrStack[-1]['bufferStack'] and self.attrStack[-1]['tag'] == 'div':
   1769             self.divs = max(0, self.divs - 1)
   1770             self.AddFloater()
   1771         else:
   1772             self.FlushBuffer()
   1773             self.AddBottomBorders()
   1774             self.AddBorder(self.attrStack[-1]['pstart'], self.contentHeight - self.attrStack[-1]['pstart'])
   1775         self.OnEnd_default('div')
   1776 
   1777     @telemetry.ZONE_METHOD
   1778     def OnStart_table(self, attrs):
   1779         if self.sr.table is not None:
   1780             self.OnEnd_table()
   1781         self.FlushBuffer()
   1782         self.sr.table = attrs
   1783         self.sr.table.rows = []
   1784         self.sr.table.colgroups = []
   1785         s = self.GetTableAttrs(attrs)
   1786         self.OnStart_default('table', attrs, s)
   1787         if self.attrStack[-1]['border-collapse']:
   1788             for side in ['left',
   1789              'top',
   1790              'right',
   1791              'bottom']:
   1792                 self.attrStack[-1]['spacing-%s' % side] = 0
   1793 
   1794         self.attrStack[-1]['table-width'] = attrs.width
   1795         self.attrStack[-1]['table-height'] = attrs.height
   1796 
   1797     @telemetry.ZONE_METHOD
   1798     def OnEnd_table(self):
   1799         if self.sr.table is None:
   1800             self.OnEnd_default('table')
   1801             self.FlushBuffer()
   1802             return
   1803         attrs = self.attrStack[-1]['attr']
   1804         attrs.type = 'table'
   1805         attrs.charset = self.charset
   1806         se = self.GetScrollEntry(attrs)
   1807         se.attrs = self.sr.table
   1808         table = self.GetInline(se)
   1809         if getattr(attrs, 'align', None) is None:
   1810             obj = Bunch()
   1811             obj.type = '<table>'
   1812             obj.width = table.width
   1813             obj.height = table.height
   1814             obj.attrs = attrs
   1815             obj.valign = self.attrStack[-1]['vertical-align']
   1816             obj.control = table
   1817             self.AddObjectToBuffer(obj)
   1818         else:
   1819             attrs.height = table.height
   1820             attrs.width = table.width
   1821             table.Close()
   1822             self.AddOverlay(attrs, 0, self.contentHeight)
   1823         if self.destroyed:
   1824             return
   1825         self.sr.table = None
   1826         self.OnEnd_default('table')
   1827         self.FlushBuffer()
   1828 
   1829     def OnData_table(self, data, attrs):
   1830         pass
   1831 
   1832     def GetTableAttrs(self, attrs):
   1833         s = {}
   1834         for side in ['left',
   1835          'top',
   1836          'right',
   1837          'bottom']:
   1838             s['border-%s-width' % side] = 0
   1839             s['border-%s-color' % side] = self.attrStack[-1]['color']
   1840             s['spacing-%s' % side] = 1
   1841             s['padding-%s' % side] = 2
   1842 
   1843         s['background-color'] = None
   1844         s['background-image'] = None
   1845         for side in ['left',
   1846          'top',
   1847          'right',
   1848          'bottom']:
   1849             if hasattr(attrs, 'border') and attrs.border and unicode(attrs.border).isdigit():
   1850                 s['border-%s-width' % side] = int(attrs.border)
   1851                 s['border-%s-style' % side] = 1
   1852             if getattr(attrs, 'bordercolor', None):
   1853                 s['border-%s-color' % side] = ParseHTMLColor(attrs.bordercolor, 1, error=1) or s['border-%s-color' % side]
   1854             if getattr(attrs, 'cellspacing', None) is not None:
   1855                 s['spacing-%s' % side] = max(0, int((int(attrs.cellspacing) + [0, 1][side in ('right', 'bottom')]) / 2))
   1856             if getattr(attrs, 'cellpadding', None) is not None:
   1857                 s['padding-%s' % side] = max(0, int(attrs.cellpadding))
   1858 
   1859         if getattr(attrs, 'bgcolor', None) is not None:
   1860             s['background-color'] = ParseHTMLColor(attrs.bgcolor, 1)
   1861         return s
   1862 
   1863     def OnStart_colgroup(self, attrs):
   1864         if self.sr.table is None:
   1865             return
   1866         s = {}
   1867         s['colgroup-width'] = attrs.width
   1868         s['colgroup-span'] = attrs.span
   1869         s['cols'] = []
   1870         self.OnStart_default('colgroup', attrs, s)
   1871 
   1872     def OnData_colgroup(self, data, attrs):
   1873         pass
   1874 
   1875     def OnEnd_colgroup(self):
   1876         s = self.attrStack[-1]
   1877         for i in xrange(s['colgroup-span'] or len(s['cols']) or 1):
   1878             if len(s['cols']):
   1879                 self.sr.table.colgroups.append(s['cols'].pop(0) or s['colgroup-width'])
   1880             else:
   1881                 self.sr.table.colgroups.append(s['colgroup-width'])
   1882 
   1883         self.OnEnd_default('colgroup')
   1884 
   1885     def OnStart_col(self, attrs):
   1886         if self.sr.table is None:
   1887             return
   1888         if 'cols' not in self.attrStack[-1]:
   1889             return
   1890         for i in xrange(attrs.span or 1):
   1891             self.attrStack[-1]['cols'].append(attrs.width)
   1892 
   1893     @telemetry.ZONE_METHOD
   1894     def OnStart_tr(self, attrs):
   1895         if self.sr.table is None:
   1896             return
   1897         attrs.cols = []
   1898         if unicode(attrs.height).isdigit():
   1899             attrs.height = int(attrs.height or 0)
   1900         else:
   1901             attrs.height = 0
   1902         s = {}
   1903         for side in ['left',
   1904          'top',
   1905          'right',
   1906          'bottom']:
   1907             if getattr(attrs, 'border', 0):
   1908                 s['border-%s-width' % side] = int(attrs.border or 0)
   1909             if getattr(attrs, 'bordercolor', None):
   1910                 s['border-%s-color' % side] = ParseHTMLColor(attrs.bordercolor or '#000000', 1)
   1911             if hasattr(attrs, 'cellspacing') and attrs.cellspacing is not None:
   1912                 s['spacing-%s' % side] = max(0, int((int(attrs.cellspacing) + [0, 1][side in ('right', 'bottom')]) / 2))
   1913             if hasattr(attrs, 'cellpadding') and attrs.cellpadding is not None:
   1914                 s['padding-%s' % side] = max(0, int(attrs.cellpadding))
   1915 
   1916         if attrs.valign is not None:
   1917             s['vertical-align'] = getattr(parser, 'ALIGN%s' % unicode(attrs.valign).upper(), 1)
   1918         else:
   1919             s['vertical-align'] = parser.ALIGNTOP
   1920         s['background-image'] = None
   1921         s['background-color'] = None
   1922         if attrs.bgcolor is not None:
   1923             s['background-color'] = ParseHTMLColor(attrs.bgcolor, 1)
   1924         self.OnStart_default('tr', attrs, s)
   1925         attrs.styles = self.attrStack[-1].copy()
   1926         self.sr.table.rows.append(attrs)
   1927 
   1928     @telemetry.ZONE_METHOD
   1929     def OnEnd_tr(self):
   1930         self.OnEnd_default('tr')
   1931 
   1932     def OnData_tr(self, data, attrs):
   1933         pass
   1934 
   1935     def GetInt(self, stringVal):
   1936         if stringVal is None:
   1937             return stringVal
   1938         stringVal = filter(lambda x: x in '0123456789', unicode(stringVal))
   1939         return int(stringVal)
   1940 
   1941     def GetPercent(self, value, reference):
   1942         if type(value) == str and value.endswith('%'):
   1943             value = self.GetInt(value)
   1944             return int(value * reference / 100.0)
   1945         return int(value)
   1946 
   1947     @telemetry.ZONE_METHOD
   1948     def GetInline(self, data):
   1949         inline = data.decoClass(parent=self.sr.cacheContainer)
   1950         inline.data = data
   1951         inline.Startup(self)
   1952         inline.loaded = 0
   1953         return inline
   1954 
   1955     @telemetry.ZONE_METHOD
   1956     def OnStart_td(self, attrs):
   1957         if self.sr.table is None:
   1958             return
   1959         if not getattr(self.sr.table, 'rows', None):
   1960             attrs.height = getattr(attrs, 'height', None)
   1961             self.OnStart_tr(attrs)
   1962         h = unicode(getattr(attrs, 'height', None))
   1963         if h and h[-1] == '%':
   1964             attrs.height = None
   1965         attrs.content = []
   1966         s = {}
   1967         s['text-align'] = 'left'
   1968         for side in ['left',
   1969          'top',
   1970          'right',
   1971          'bottom']:
   1972             s['border-%s-width' % side] = int(getattr(attrs, 'border', 0))
   1973             if getattr(attrs, 'bordercolor', None):
   1974                 s['border-%s-color' % side] = ParseHTMLColor(attrs.bordercolor or '#000000', 1)
   1975 
   1976         if getattr(attrs, 'valign', None):
   1977             s['vertical-align'] = getattr(parser, 'ALIGN%s' % unicode(attrs.valign).upper(), 1)
   1978         s['text-align'] = getattr(attrs, 'align', None) or s['text-align']
   1979         if attrs.bgcolor is not None:
   1980             s['background-color'] = ParseHTMLColor(attrs.bgcolor, 1)
   1981         self.OnStart_default('td', attrs, s)
   1982         attrs.styles = self.attrStack[-1].copy()
   1983         attrs.css = self.css.copy()
   1984         self.attrStack[-1]['bufferStack'] = []
   1985         self.sr.table.rows[-1].cols.append(attrs)
   1986 
   1987     @telemetry.ZONE_METHOD
   1988     def OnEnd_td(self):
   1989         if self.attrStack[-1]['bufferStack'] is not None and self.attrStack[-1]['tag'] == 'td':
   1990             if self.sr.table and len(self.sr.table.rows[-1].cols):
   1991                 self.sr.table.rows[-1].cols[-1].content = self.attrStack[-1]['bufferStack'][:]
   1992         else:
   1993             log.LogWarn('Ending unknown td')
   1994         self.OnEnd_default('td')
   1995 
   1996     @telemetry.ZONE_METHOD
   1997     def OnStart_th(self, attrs):
   1998         if self.sr.table is None:
   1999             return
   2000         if not getattr(self.sr.table, 'rows', None):
   2001             attrs.height = getattr(attrs, 'height', None)
   2002             self.OnStart_tr(attrs)
   2003         h = unicode(getattr(attrs, 'height', None))
   2004         if h and h[-1] == '%':
   2005             attrs.height = None
   2006         attrs.content = []
   2007         s = {}
   2008         s['text-align'] = 'center'
   2009         s['font-weight'] = 'b'
   2010         for side in ['left',
   2011          'top',
   2012          'right',
   2013          'bottom']:
   2014             s['border-%s-width' % side] = int(getattr(attrs, 'border', 0))
   2015             if getattr(attrs, 'bordercolor', None):
   2016                 s['border-%s-color' % side] = ParseHTMLColor(attrs.bordercolor or '#000000', 1)
   2017 
   2018         if getattr(attrs, 'valign', None):
   2019             s['vertical-align'] = getattr(parser, 'ALIGN%s' % unicode(attrs.valign).upper(), 1)
   2020         s['text-align'] = getattr(attrs, 'align', None) or s['text-align']
   2021         if attrs.bgcolor is not None:
   2022             s['background-color'] = ParseHTMLColor(attrs.bgcolor, 1)
   2023         self.OnStart_default('th', attrs, s)
   2024         attrs.styles = self.attrStack[-1].copy()
   2025         attrs.css = self.css.copy()
   2026         self.attrStack[-1]['bufferStack'] = []
   2027         self.sr.table.rows[-1].cols.append(attrs)
   2028 
   2029     @telemetry.ZONE_METHOD
   2030     def OnEnd_th(self):
   2031         if self.attrStack[-1]['bufferStack'] is not None and self.attrStack[-1]['tag'] == 'th':
   2032             if self.sr.table and len(self.sr.table.rows[-1].cols):
   2033                 self.sr.table.rows[-1].cols[-1].content = self.attrStack[-1]['bufferStack'][:]
   2034         else:
   2035             log.LogWarn('Ending unknown th')
   2036         self.OnEnd_default('th')
   2037 
   2038     @telemetry.ZONE_METHOD
   2039     def OnStart_a(self, attrs):
   2040         s = {'color': self.attrStack[-1]['link-color'],
   2041          'link-color': None}
   2042         visited = settings.public.ui.VisitedURLs or []
   2043         self.OnStart_default('a', attrs, s)
   2044         self.CheckForMailAddress(attrs)
   2045         self.attrStack[-1]['link-color'] = self.attrStack[-1]['link-color'] or self.attrStack[-1]['color']
   2046         attrs.color = [self.attrStack[-1]['link-color'], self.attrStack[-1]['vlink-color'] or self.attrStack[-1]['link-color']][attrs.href in visited]
   2047         self.a = attrs
   2048 
   2049     def CheckForMailAddress(self, attrs):
   2050         pass
   2051 
   2052     def OnEnd_a(self):
   2053         self.a = None
   2054         self.OnEnd_default('a')
   2055 
   2056     def OnStart_span(self, attrs):
   2057         self.OnStart_default('span', attrs)
   2058         self.ParseStdStyles(attrs)
   2059 
   2060     def OnEnd_span(self):
   2061         self.OnEnd_default('span')
   2062 
   2063     def OnStart_map(self, attrs):
   2064         self.OnStart_default('map', attrs)
   2065         if attrs.name:
   2066             amap = {}
   2067             amap['name'] = attrs.name
   2068             amap['coords'] = []
   2069             self.areamap.append(amap)
   2070         else:
   2071             return
   2072 
   2073     def OnData_map(self, data, attrs):
   2074         pass
   2075 
   2076     def OnEnd_map(self):
   2077         self.OnEnd_default('map')
   2078 
   2079     @telemetry.ZONE_METHOD
   2080     def OnStart_area(self, attrs):
   2081         if attrs.shape and attrs.coords and attrs.href:
   2082             if attrs.shape != 'rect':
   2083                 return
   2084             if attrs.coords.count(',') != 3:
   2085                 return
   2086             self.CheckForMailAddress(attrs)
   2087             x0, y0, x1, y1 = attrs.coords.split(',')
   2088             area = {}
   2089             area['x0'] = int(x0)
   2090             area['y0'] = int(y0)
   2091             area['x1'] = int(x1)
   2092             area['y1'] = int(y1)
   2093             area['url'] = attrs.href
   2094             self.areamap[-1]['coords'].append(area)
   2095 
   2096 
   2097 class ParserBaseCoreOverride(ParserBaseCore):
   2098 
   2099     @classmethod
   2100     def __reload_update__(cls, old_cls):
   2101         return old_cls