2026-01-09 11:20:46 +00:00
|
|
|
"""Tab and frame orchestration stubs."""
|
|
|
|
|
|
|
|
|
|
from typing import Optional
|
2026-01-09 12:31:48 +00:00
|
|
|
import logging
|
2026-01-09 11:20:46 +00:00
|
|
|
|
|
|
|
|
from ..network.url import URL
|
2026-01-09 13:11:46 +00:00
|
|
|
from ..network import http
|
|
|
|
|
from ..parser.html import parse_html, Element
|
2026-01-09 13:24:01 +00:00
|
|
|
from ..templates import render_startpage, render_error_page
|
2026-01-09 11:20:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class Frame:
|
|
|
|
|
def __init__(self, tab: "Tab", parent_frame=None, frame_element=None):
|
|
|
|
|
self.tab = tab
|
|
|
|
|
self.parent_frame = parent_frame
|
|
|
|
|
self.frame_element = frame_element
|
2026-01-09 13:11:46 +00:00
|
|
|
self.document: Optional[Element] = None
|
2026-01-09 11:20:46 +00:00
|
|
|
|
|
|
|
|
def load(self, url: URL, payload: Optional[bytes] = None):
|
2026-01-09 13:11:46 +00:00
|
|
|
"""Fetch and parse the URL content."""
|
2026-01-09 13:24:01 +00:00
|
|
|
logger = logging.getLogger("bowser.frame")
|
|
|
|
|
|
|
|
|
|
# Handle special about: URLs
|
|
|
|
|
if url.origin == "about:startpage":
|
|
|
|
|
html = render_startpage()
|
|
|
|
|
self.document = parse_html(html)
|
|
|
|
|
self.tab.current_url = url
|
|
|
|
|
return
|
|
|
|
|
|
2026-01-09 13:11:46 +00:00
|
|
|
try:
|
|
|
|
|
status, content_type, body = http.request(url, payload)
|
|
|
|
|
|
|
|
|
|
if status == 200:
|
|
|
|
|
# Decode response
|
|
|
|
|
text = body.decode('utf-8', errors='replace')
|
|
|
|
|
|
|
|
|
|
# Parse HTML
|
|
|
|
|
self.document = parse_html(text)
|
|
|
|
|
self.tab.current_url = url
|
|
|
|
|
else:
|
|
|
|
|
# Error handling - show error page
|
2026-01-09 13:24:01 +00:00
|
|
|
html = render_error_page(status, str(url))
|
|
|
|
|
self.document = parse_html(html)
|
2026-01-09 13:11:46 +00:00
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
# Network error - show error page
|
2026-01-09 13:24:01 +00:00
|
|
|
html = render_error_page(0, str(url), str(e))
|
|
|
|
|
self.document = parse_html(html)
|
|
|
|
|
logger.error(f"Failed to load {url}: {e}")
|
2026-01-09 11:20:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class Tab:
|
|
|
|
|
def __init__(self, browser: "Browser", tab_height: int = 40):
|
|
|
|
|
self.browser = browser
|
|
|
|
|
self.tab_height = tab_height
|
|
|
|
|
self.current_url: Optional[URL] = None
|
|
|
|
|
self.main_frame = Frame(self)
|
2026-01-09 12:31:48 +00:00
|
|
|
self.history: list[URL] = []
|
|
|
|
|
self.history_index: int = -1
|
2026-01-09 11:20:46 +00:00
|
|
|
|
|
|
|
|
def load(self, url: URL, payload: Optional[bytes] = None):
|
2026-01-09 12:31:48 +00:00
|
|
|
# push into history (truncate forward)
|
|
|
|
|
if self.history_index < len(self.history) - 1:
|
|
|
|
|
self.history = self.history[: self.history_index + 1]
|
|
|
|
|
self.history.append(url)
|
|
|
|
|
self.history_index += 1
|
|
|
|
|
self.browser._log(f"Tab load: {url}", logging.INFO)
|
2026-01-09 11:20:46 +00:00
|
|
|
self.main_frame.load(url, payload)
|
2026-01-09 12:31:48 +00:00
|
|
|
|
|
|
|
|
def go_back(self) -> bool:
|
|
|
|
|
if self.history_index > 0:
|
|
|
|
|
self.history_index -= 1
|
|
|
|
|
url = self.history[self.history_index]
|
|
|
|
|
self.browser._log("Tab go_back", logging.DEBUG)
|
|
|
|
|
self.main_frame.load(url)
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def go_forward(self) -> bool:
|
|
|
|
|
if self.history_index < len(self.history) - 1:
|
|
|
|
|
self.history_index += 1
|
|
|
|
|
url = self.history[self.history_index]
|
|
|
|
|
self.browser._log("Tab go_forward", logging.DEBUG)
|
|
|
|
|
self.main_frame.load(url)
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def reload(self) -> bool:
|
|
|
|
|
if 0 <= self.history_index < len(self.history):
|
|
|
|
|
url = self.history[self.history_index]
|
|
|
|
|
self.browser._log("Tab reload", logging.DEBUG)
|
|
|
|
|
self.main_frame.load(url)
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def title(self) -> str:
|
|
|
|
|
if self.current_url is None:
|
|
|
|
|
return "New Tab"
|
|
|
|
|
return str(self.current_url)
|