Repository: microsoft/playwright-python

Issue:
Error accessing page and error closing browser if extension is loaded: "AttributeError: 'NoneType' object has no attribute '_pages'"
Hi!

Both for sync api and async api I have the same error, happens about 80% of the time. `browser.close()` will produce `AttributeError: 'NoneType' object has no attribute '_pages'` where stack trace comes outside of my script and I cannot catch it with `try: except AttributeError` within my code.

On top of this, `playwright-python` opens a new blank page + metamask page (not-expected); JS Playwright only opens + metamask page.

Extensions I used are 1) Metamask; 

I cannot access the page, PlayWright shows 3 pages visually but lists only 2, only the blank ones; MetaMask page is both non-accessible and also 80% of the time raises outside-script stack trace from browser closed "AttributeError: 'NoneType' object has no attribute '_pages'"

```
from playwright.sync_api import sync_playwright

# these need to be replaced to your ones; Metamask unpacked can be downloaded here
# https://github.com/MetaMask/metamask-extension/releases/tag/v9.5.0
EXT_1_DIR = "C:/Users/<individual_path>/metamask_unpck"
USER_DIR = "C:/Users/<individual_path>/User Data/Profile 2"

ARGS = [
    "--disable-extensions-except={}".format(EXT_1_DIR),
    "--load-extension={}".format(EXT_1_DIR)
]

with sync_playwright() as p:
    browser = p.chromium.launch_persistent_context(USER_DIR, headless=False, args=ARGS)
    page = browser.new_page()
    _ = input("Wait till Metamask loads? ")
    print("len_pages()", len(browser.pages))
    for pg in browser.pages:
    	pg.close()

    _ = input("Close browser? ")
    browser.close()
```


    
This issue is `playwright-python` specific and there's no issue on `playwright-for-javascript` pls help. The issue is the same for both `sync_api` and `async_api` but I used `sync_api` in this example as it's a tad simpler and a tad less code.

Here's illustrated powershell output screenshot;
https://imgur.com/a/iMDSPcS

OS: Windows 10, playwright-python 1.11.0; Python 3.9

PROBLEM TL;DR: prevents me from using MetaMask extension and raises error on browser.close() on top - an issue I don't have in PlayWright for JS. Prob. same issue on other than MetaMask extensions as well, but I didn't have time to bulk up the sample.






Discussion:

Resolving patch:
diff --git a/playwright/_impl/_browser_context.py b/playwright/_impl/_browser_context.py
--- a/playwright/_impl/_browser_context.py
+++ b/playwright/_impl/_browser_context.py
@@ -94,7 +94,6 @@ def __repr__(self) -> str:
         return f"<BrowserContext browser={self.browser}>"
 
     def _on_page(self, page: Page) -> None:
-        page._set_browser_context(self)
         self._pages.append(page)
         self.emit(BrowserContext.Events.Page, page)
         if page._opener and not page._opener.is_closed():
diff --git a/playwright/_impl/_page.py b/playwright/_impl/_page.py
--- a/playwright/_impl/_page.py
+++ b/playwright/_impl/_page.py
@@ -111,7 +111,7 @@ def __init__(
         self, parent: ChannelOwner, type: str, guid: str, initializer: Dict
     ) -> None:
         super().__init__(parent, type, guid, initializer)
-        self._browser_context: BrowserContext = None  # type: ignore
+        self._browser_context: BrowserContext = parent
         self.accessibility = Accessibility(self._channel)
         self.keyboard = Keyboard(self._channel)
         self.mouse = Mouse(self._channel)
@@ -126,7 +126,9 @@ def __init__(
         self._bindings: Dict[str, Any] = {}
         self._routes: List[RouteHandlerEntry] = []
         self._owned_context: Optional["BrowserContext"] = None
-        self._timeout_settings: TimeoutSettings = TimeoutSettings(None)
+        self._timeout_settings: TimeoutSettings = TimeoutSettings(
+            self._browser_context._timeout_settings
+        )
         self._video: Optional[Video] = None
         self._opener = cast("Page", from_nullable_channel(initializer.get("opener")))
 
@@ -217,10 +219,6 @@ def __init__(
     def __repr__(self) -> str:
         return f"<Page url={self.url!r}>"
 
-    def _set_browser_context(self, context: "BrowserContext") -> None:
-        self._browser_context = context
-        self._timeout_settings = TimeoutSettings(context._timeout_settings)
-
     def _on_request_failed(
         self,
         request: Request,
@@ -271,7 +269,10 @@ def _on_worker(self, worker: "Worker") -> None:
 
     def _on_close(self) -> None:
         self._is_closed = True
-        self._browser_context._pages.remove(self)
+        if self in self._browser_context._pages:
+            self._browser_context._pages.remove(self)
+        if self in self._browser_context._background_pages:
+            self._browser_context._background_pages.remove(self)
         self.emit(Page.Events.Close)
 
     def _on_crash(self) -> None:


Test patch: