1 /* 2 * Copyright (C) 2007 Andrea Anzani <andrea.anzani@gmail.com> 3 * Copyright (C) 2007, 2010 Ryan Leavengood <leavengood@gmail.com> 4 * Copyright (C) 2009 Maxime Simon <simon.maxime@gmail.com> 5 * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de> 6 * Copyright (C) 2010 Michael Lotz <mmlr@mlotz.ch> 7 * Copyright (C) 2010 Rene Gollent <rene@gollent.com> 8 * Copyright 2013-2015 Haiku, Inc. All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include "BrowserWindow.h" 33 34 #include <Alert.h> 35 #include <Application.h> 36 #include <Bitmap.h> 37 #include <Button.h> 38 #include <Catalog.h> 39 #include <CheckBox.h> 40 #include <Clipboard.h> 41 #include <ControlLook.h> 42 #include <Debug.h> 43 #include <Directory.h> 44 #include <Entry.h> 45 #include <File.h> 46 #include <FilePanel.h> 47 #include <FindDirectory.h> 48 #include <GridLayoutBuilder.h> 49 #include <GroupLayout.h> 50 #include <GroupLayoutBuilder.h> 51 #include <IconMenuItem.h> 52 #include <Keymap.h> 53 #include <LayoutBuilder.h> 54 #include <Locale.h> 55 #include <ObjectList.h> 56 #include <MenuBar.h> 57 #include <MenuItem.h> 58 #include <MessageRunner.h> 59 #include <NodeInfo.h> 60 #include <NodeMonitor.h> 61 #include <Path.h> 62 #include <Roster.h> 63 #include <Screen.h> 64 #include <SeparatorView.h> 65 #include <Size.h> 66 #include <SpaceLayoutItem.h> 67 #include <StatusBar.h> 68 #include <StringView.h> 69 #include <TextControl.h> 70 #include <UnicodeChar.h> 71 #include <Url.h> 72 73 #include <map> 74 #include <stdio.h> 75 76 #include "AuthenticationPanel.h" 77 #include "BaseURL.h" 78 #include "BitmapButton.h" 79 #include "BookmarkBar.h" 80 #include "BrowserApp.h" 81 #include "BrowsingHistory.h" 82 #include "CredentialsStorage.h" 83 #include "IconButton.h" 84 #include "NavMenu.h" 85 #include "SettingsKeys.h" 86 #include "SettingsMessage.h" 87 #include "TabManager.h" 88 #include "URLInputGroup.h" 89 #include "WebPage.h" 90 #include "WebView.h" 91 #include "WebViewConstants.h" 92 #include "WindowIcon.h" 93 94 95 #undef B_TRANSLATION_CONTEXT 96 #define B_TRANSLATION_CONTEXT "WebPositive Window" 97 98 99 enum { 100 OPEN_LOCATION = 'open', 101 SAVE_PAGE = 'save', 102 GO_BACK = 'goba', 103 GO_FORWARD = 'gofo', 104 STOP = 'stop', 105 HOME = 'home', 106 GOTO_URL = 'goul', 107 RELOAD = 'reld', 108 SHOW_HIDE_BOOKMARK_BAR = 'shbb', 109 CLEAR_HISTORY = 'clhs', 110 111 CREATE_BOOKMARK = 'crbm', 112 SHOW_BOOKMARKS = 'shbm', 113 114 ZOOM_FACTOR_INCREASE = 'zfin', 115 ZOOM_FACTOR_DECREASE = 'zfdc', 116 ZOOM_FACTOR_RESET = 'zfrs', 117 ZOOM_TEXT_ONLY = 'zfto', 118 119 TOGGLE_FULLSCREEN = 'tgfs', 120 TOGGLE_AUTO_HIDE_INTERFACE_IN_FULLSCREEN = 'tgah', 121 CHECK_AUTO_HIDE_INTERFACE = 'cahi', 122 123 SHOW_PAGE_SOURCE = 'spgs', 124 125 EDIT_SHOW_FIND_GROUP = 'sfnd', 126 EDIT_HIDE_FIND_GROUP = 'hfnd', 127 EDIT_FIND_NEXT = 'fndn', 128 EDIT_FIND_PREVIOUS = 'fndp', 129 FIND_TEXT_CHANGED = 'ftxt', 130 131 SELECT_TAB = 'sltb', 132 }; 133 134 135 static const int32 kModifiers = B_SHIFT_KEY | B_COMMAND_KEY 136 | B_CONTROL_KEY | B_OPTION_KEY | B_MENU_KEY; 137 138 139 static const char* kHandledProtocols[] = { 140 "http", 141 "https", 142 "file", 143 "about", 144 "data", 145 "gopher" 146 }; 147 148 149 static BLayoutItem* 150 layoutItemFor(BView* view) 151 { 152 BLayout* layout = view->Parent()->GetLayout(); 153 int32 index = layout->IndexOfView(view); 154 return layout->ItemAt(index); 155 } 156 157 158 class BookmarkMenu : public BNavMenu { 159 public: 160 BookmarkMenu(const char* title, BHandler* target, const entry_ref* navDir) 161 : 162 BNavMenu(title, B_REFS_RECEIVED, target) 163 { 164 // Add these items here already, so the shortcuts work even when 165 // the menu has never been opened yet. 166 _AddStaticItems(); 167 168 SetNavDir(navDir); 169 } 170 171 virtual void AttachedToWindow() 172 { 173 RemoveItems(0, CountItems(), true); 174 ForceRebuild(); 175 BNavMenu::AttachedToWindow(); 176 if (CountItems() > 0) 177 AddItem(new BSeparatorItem(), 0); 178 _AddStaticItems(); 179 DoLayout(); 180 } 181 182 private: 183 void _AddStaticItems() 184 { 185 AddItem(new BMenuItem(B_TRANSLATE("Manage bookmarks"), 186 new BMessage(SHOW_BOOKMARKS), 'M'), 0); 187 AddItem(new BMenuItem(B_TRANSLATE("Bookmark this page"), 188 new BMessage(CREATE_BOOKMARK), 'B'), 0); 189 } 190 }; 191 192 193 class PageUserData : public BWebView::UserData { 194 public: 195 PageUserData(BView* focusedView) 196 : 197 fFocusedView(focusedView), 198 fPageIcon(NULL), 199 fURLInputSelectionStart(-1), 200 fURLInputSelectionEnd(-1) 201 { 202 } 203 204 ~PageUserData() 205 { 206 delete fPageIcon; 207 } 208 209 void SetFocusedView(BView* focusedView) 210 { 211 fFocusedView = focusedView; 212 } 213 214 BView* FocusedView() const 215 { 216 return fFocusedView; 217 } 218 219 void SetPageIcon(const BBitmap* icon) 220 { 221 delete fPageIcon; 222 if (icon) 223 fPageIcon = new BBitmap(icon); 224 else 225 fPageIcon = NULL; 226 } 227 228 const BBitmap* PageIcon() const 229 { 230 return fPageIcon; 231 } 232 233 void SetURLInputContents(const char* text) 234 { 235 fURLInputContents = text; 236 } 237 238 const BString& URLInputContents() const 239 { 240 return fURLInputContents; 241 } 242 243 void SetURLInputSelection(int32 selectionStart, int32 selectionEnd) 244 { 245 fURLInputSelectionStart = selectionStart; 246 fURLInputSelectionEnd = selectionEnd; 247 } 248 249 int32 URLInputSelectionStart() const 250 { 251 return fURLInputSelectionStart; 252 } 253 254 int32 URLInputSelectionEnd() const 255 { 256 return fURLInputSelectionEnd; 257 } 258 259 private: 260 BView* fFocusedView; 261 BBitmap* fPageIcon; 262 BString fURLInputContents; 263 int32 fURLInputSelectionStart; 264 int32 fURLInputSelectionEnd; 265 }; 266 267 268 class CloseButton : public BButton { 269 public: 270 CloseButton(BMessage* message) 271 : 272 BButton("close button", NULL, message), 273 fOverCloseRect(false) 274 { 275 // Button is 16x16 regardless of font size 276 SetExplicitMinSize(BSize(15, 15)); 277 SetExplicitMaxSize(BSize(15, 15)); 278 } 279 280 virtual void Draw(BRect updateRect) 281 { 282 BRect frame = Bounds(); 283 BRect closeRect(frame.InsetByCopy(4, 4)); 284 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 285 float tint = B_DARKEN_1_TINT; 286 287 if (fOverCloseRect) 288 tint *= 1.4; 289 else 290 tint *= 1.2; 291 292 if (Value() == B_CONTROL_ON && fOverCloseRect) { 293 // Draw the button frame 294 be_control_look->DrawButtonFrame(this, frame, updateRect, 295 base, base, BControlLook::B_ACTIVATED 296 | BControlLook::B_BLEND_FRAME); 297 be_control_look->DrawButtonBackground(this, frame, 298 updateRect, base, BControlLook::B_ACTIVATED); 299 closeRect.OffsetBy(1, 1); 300 tint *= 1.2; 301 } else { 302 SetHighColor(base); 303 FillRect(updateRect); 304 } 305 306 // Draw the × 307 base = tint_color(base, tint); 308 SetHighColor(base); 309 SetPenSize(2); 310 StrokeLine(closeRect.LeftTop(), closeRect.RightBottom()); 311 StrokeLine(closeRect.LeftBottom(), closeRect.RightTop()); 312 SetPenSize(1); 313 } 314 315 virtual void MouseMoved(BPoint where, uint32 transit, 316 const BMessage* dragMessage) 317 { 318 switch (transit) { 319 case B_ENTERED_VIEW: 320 fOverCloseRect = true; 321 Invalidate(); 322 break; 323 case B_EXITED_VIEW: 324 fOverCloseRect = false; 325 Invalidate(); 326 break; 327 case B_INSIDE_VIEW: 328 fOverCloseRect = true; 329 break; 330 case B_OUTSIDE_VIEW: 331 fOverCloseRect = false; 332 break; 333 } 334 335 BButton::MouseMoved(where, transit, dragMessage); 336 } 337 338 private: 339 bool fOverCloseRect; 340 }; 341 342 343 // #pragma mark - BrowserWindow 344 345 346 BrowserWindow::BrowserWindow(BRect frame, SettingsMessage* appSettings, 347 const BString& url, BUrlContext* context, uint32 interfaceElements, 348 BWebView* webView) 349 : 350 BWebWindow(frame, kApplicationName, 351 B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 352 B_AUTO_UPDATE_SIZE_LIMITS | B_ASYNCHRONOUS_CONTROLS), 353 fIsFullscreen(false), 354 fInterfaceVisible(false), 355 fMenusRunning(false), 356 fPulseRunner(NULL), 357 fVisibleInterfaceElements(interfaceElements), 358 fContext(context), 359 fAppSettings(appSettings), 360 fZoomTextOnly(true), 361 fShowTabsIfSinglePageOpen(true), 362 fAutoHideInterfaceInFullscreenMode(false), 363 fAutoHidePointer(false), 364 fBookmarkBar(NULL) 365 { 366 // Begin listening to settings changes and read some current values. 367 fAppSettings->AddListener(BMessenger(this)); 368 // fZoomTextOnly = fAppSettings->GetValue("zoom text only", fZoomTextOnly); 369 fShowTabsIfSinglePageOpen = fAppSettings->GetValue( 370 kSettingsKeyShowTabsIfSinglePageOpen, fShowTabsIfSinglePageOpen); 371 372 fAutoHidePointer = fAppSettings->GetValue(kSettingsKeyAutoHidePointer, 373 fAutoHidePointer); 374 375 fNewWindowPolicy = fAppSettings->GetValue(kSettingsKeyNewWindowPolicy, 376 (uint32)OpenStartPage); 377 fNewTabPolicy = fAppSettings->GetValue(kSettingsKeyNewTabPolicy, 378 (uint32)OpenBlankPage); 379 fStartPageURL = fAppSettings->GetValue(kSettingsKeyStartPageURL, 380 kDefaultStartPageURL); 381 fSearchPageURL = fAppSettings->GetValue(kSettingsKeySearchPageURL, 382 kDefaultSearchPageURL); 383 384 // Create the interface elements 385 BMessage* newTabMessage = new BMessage(NEW_TAB); 386 newTabMessage->AddString("url", ""); 387 newTabMessage->AddPointer("window", this); 388 newTabMessage->AddBool("select", true); 389 fTabManager = new TabManager(BMessenger(this), newTabMessage); 390 391 // Menu 392 #if INTEGRATE_MENU_INTO_TAB_BAR 393 BMenu* mainMenu = fTabManager->Menu(); 394 #else 395 BMenu* mainMenu = new BMenuBar("Main menu"); 396 #endif 397 BMenu* menu = new BMenu(B_TRANSLATE("Window")); 398 BMessage* newWindowMessage = new BMessage(NEW_WINDOW); 399 newWindowMessage->AddString("url", ""); 400 BMenuItem* newItem = new BMenuItem(B_TRANSLATE("New window"), 401 newWindowMessage, 'N'); 402 menu->AddItem(newItem); 403 newItem->SetTarget(be_app); 404 newItem = new BMenuItem(B_TRANSLATE("New tab"), 405 new BMessage(*newTabMessage), 'T'); 406 menu->AddItem(newItem); 407 newItem->SetTarget(be_app); 408 menu->AddItem(new BMenuItem(B_TRANSLATE("Open location"), 409 new BMessage(OPEN_LOCATION), 'L')); 410 menu->AddSeparatorItem(); 411 menu->AddItem(new BMenuItem(B_TRANSLATE("Close window"), 412 new BMessage(B_QUIT_REQUESTED), 'W', B_SHIFT_KEY)); 413 menu->AddItem(new BMenuItem(B_TRANSLATE("Close tab"), 414 new BMessage(CLOSE_TAB), 'W')); 415 menu->AddItem(new BMenuItem(B_TRANSLATE("Save page as" B_UTF8_ELLIPSIS), 416 new BMessage(SAVE_PAGE), 'S')); 417 menu->AddSeparatorItem(); 418 menu->AddItem(new BMenuItem(B_TRANSLATE("Downloads"), 419 new BMessage(SHOW_DOWNLOAD_WINDOW), 'D')); 420 menu->AddItem(new BMenuItem(B_TRANSLATE("Settings"), 421 new BMessage(SHOW_SETTINGS_WINDOW))); 422 menu->AddItem(new BMenuItem(B_TRANSLATE("Cookie manager"), 423 new BMessage(SHOW_COOKIE_WINDOW))); 424 menu->AddItem(new BMenuItem(B_TRANSLATE("Script console"), 425 new BMessage(SHOW_CONSOLE_WINDOW))); 426 BMenuItem* aboutItem = new BMenuItem(B_TRANSLATE("About"), 427 new BMessage(B_ABOUT_REQUESTED)); 428 menu->AddItem(aboutItem); 429 aboutItem->SetTarget(be_app); 430 menu->AddSeparatorItem(); 431 BMenuItem* quitItem = new BMenuItem(B_TRANSLATE("Quit"), 432 new BMessage(B_QUIT_REQUESTED), 'Q'); 433 menu->AddItem(quitItem); 434 quitItem->SetTarget(be_app); 435 mainMenu->AddItem(menu); 436 437 menu = new BMenu(B_TRANSLATE("Edit")); 438 menu->AddItem(fCutMenuItem = new BMenuItem(B_TRANSLATE("Cut"), 439 new BMessage(B_CUT), 'X')); 440 menu->AddItem(fCopyMenuItem = new BMenuItem(B_TRANSLATE("Copy"), 441 new BMessage(B_COPY), 'C')); 442 menu->AddItem(fPasteMenuItem = new BMenuItem(B_TRANSLATE("Paste"), 443 new BMessage(B_PASTE), 'V')); 444 menu->AddSeparatorItem(); 445 menu->AddItem(new BMenuItem(B_TRANSLATE("Find"), 446 new BMessage(EDIT_SHOW_FIND_GROUP), 'F')); 447 menu->AddItem(fFindPreviousMenuItem 448 = new BMenuItem(B_TRANSLATE("Find previous"), 449 new BMessage(EDIT_FIND_PREVIOUS), 'G', B_SHIFT_KEY)); 450 menu->AddItem(fFindNextMenuItem = new BMenuItem(B_TRANSLATE("Find next"), 451 new BMessage(EDIT_FIND_NEXT), 'G')); 452 mainMenu->AddItem(menu); 453 fFindPreviousMenuItem->SetEnabled(false); 454 fFindNextMenuItem->SetEnabled(false); 455 456 menu = new BMenu(B_TRANSLATE("View")); 457 menu->AddItem(new BMenuItem(B_TRANSLATE("Reload"), new BMessage(RELOAD), 458 'R')); 459 // the label will be replaced with the appropriate text later on 460 fBookmarkBarMenuItem = new BMenuItem(B_TRANSLATE("Show bookmark bar"), 461 new BMessage(SHOW_HIDE_BOOKMARK_BAR)); 462 menu->AddItem(fBookmarkBarMenuItem); 463 menu->AddSeparatorItem(); 464 menu->AddItem(new BMenuItem(B_TRANSLATE("Increase size"), 465 new BMessage(ZOOM_FACTOR_INCREASE), '+')); 466 menu->AddItem(new BMenuItem(B_TRANSLATE("Decrease size"), 467 new BMessage(ZOOM_FACTOR_DECREASE), '-')); 468 menu->AddItem(new BMenuItem(B_TRANSLATE("Reset size"), 469 new BMessage(ZOOM_FACTOR_RESET), '0')); 470 fZoomTextOnlyMenuItem = new BMenuItem(B_TRANSLATE("Zoom text only"), 471 new BMessage(ZOOM_TEXT_ONLY)); 472 fZoomTextOnlyMenuItem->SetMarked(fZoomTextOnly); 473 menu->AddItem(fZoomTextOnlyMenuItem); 474 475 menu->AddSeparatorItem(); 476 fFullscreenItem = new BMenuItem(B_TRANSLATE("Full screen"), 477 new BMessage(TOGGLE_FULLSCREEN), B_RETURN); 478 menu->AddItem(fFullscreenItem); 479 menu->AddItem(new BMenuItem(B_TRANSLATE("Page source"), 480 new BMessage(SHOW_PAGE_SOURCE), 'U')); 481 mainMenu->AddItem(menu); 482 483 fHistoryMenu = new BMenu(B_TRANSLATE("History")); 484 fHistoryMenu->AddItem(fBackMenuItem = new BMenuItem(B_TRANSLATE("Back"), 485 new BMessage(GO_BACK), B_LEFT_ARROW)); 486 fHistoryMenu->AddItem(fForwardMenuItem 487 = new BMenuItem(B_TRANSLATE("Forward"), new BMessage(GO_FORWARD), 488 B_RIGHT_ARROW)); 489 fHistoryMenu->AddSeparatorItem(); 490 fHistoryMenuFixedItemCount = fHistoryMenu->CountItems(); 491 mainMenu->AddItem(fHistoryMenu); 492 493 BPath bookmarkPath; 494 entry_ref bookmarkRef; 495 if (_BookmarkPath(bookmarkPath) == B_OK 496 && get_ref_for_path(bookmarkPath.Path(), &bookmarkRef) == B_OK) { 497 BMenu* bookmarkMenu 498 = new BookmarkMenu(B_TRANSLATE("Bookmarks"), this, &bookmarkRef); 499 mainMenu->AddItem(bookmarkMenu); 500 501 BDirectory barDir(&bookmarkRef); 502 BEntry bookmarkBar(&barDir, "Bookmark bar"); 503 entry_ref bookmarkBarRef; 504 // TODO we could also check if the folder is empty here. 505 if (bookmarkBar.Exists() && bookmarkBar.GetRef(&bookmarkBarRef) 506 == B_OK) { 507 fBookmarkBar = new BookmarkBar("Bookmarks", this, &bookmarkBarRef); 508 fBookmarkBarMenuItem->SetEnabled(true); 509 } else 510 fBookmarkBarMenuItem->SetEnabled(false); 511 } 512 513 // Back, Forward, Stop & Home buttons 514 fBackButton = new BIconButton("Back", NULL, new BMessage(GO_BACK)); 515 fBackButton->SetIcon(201); 516 fBackButton->TrimIcon(); 517 518 fForwardButton = new BIconButton("Forward", NULL, new BMessage(GO_FORWARD)); 519 fForwardButton->SetIcon(202); 520 fForwardButton->TrimIcon(); 521 522 fStopButton = new BIconButton("Stop", NULL, new BMessage(STOP)); 523 fStopButton->SetIcon(204); 524 fStopButton->TrimIcon(); 525 526 fHomeButton = new BIconButton("Home", NULL, new BMessage(HOME)); 527 fHomeButton->SetIcon(206); 528 fHomeButton->TrimIcon(); 529 if (!fAppSettings->GetValue(kSettingsKeyShowHomeButton, true)) 530 fHomeButton->Hide(); 531 532 // URL input group 533 fURLInputGroup = new URLInputGroup(new BMessage(GOTO_URL)); 534 535 // Status Bar 536 fStatusText = new BStringView("status", ""); 537 fStatusText->SetAlignment(B_ALIGN_LEFT); 538 fStatusText->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 539 fStatusText->SetExplicitMinSize(BSize(150, 12)); 540 // Prevent the window from growing to fit a long status message... 541 BFont font(be_plain_font); 542 font.SetSize(ceilf(font.Size() * 0.8)); 543 fStatusText->SetFont(&font, B_FONT_SIZE); 544 545 // Loading progress bar 546 fLoadingProgressBar = new BStatusBar("progress"); 547 fLoadingProgressBar->SetMaxValue(100); 548 fLoadingProgressBar->Hide(); 549 fLoadingProgressBar->SetBarHeight(12); 550 551 const float kInsetSpacing = 3; 552 const float kElementSpacing = 5; 553 554 // Find group 555 fFindCloseButton = new CloseButton(new BMessage(EDIT_HIDE_FIND_GROUP)); 556 fFindTextControl = new BTextControl("find", B_TRANSLATE("Find:"), "", NULL); 557 fFindTextControl->SetModificationMessage(new BMessage(FIND_TEXT_CHANGED)); 558 fFindPreviousButton = new BButton(B_TRANSLATE("Previous"), 559 new BMessage(EDIT_FIND_PREVIOUS)); 560 fFindPreviousButton->SetToolTip( 561 B_TRANSLATE_COMMENT("Find previous occurrence of search terms", 562 "find bar previous button tooltip")); 563 fFindNextButton = new BButton(B_TRANSLATE("Next"), 564 new BMessage(EDIT_FIND_NEXT)); 565 fFindNextButton->SetToolTip( 566 B_TRANSLATE_COMMENT("Find next occurrence of search terms", 567 "find bar next button tooltip")); 568 fFindCaseSensitiveCheckBox = new BCheckBox(B_TRANSLATE("Match case")); 569 BGroupLayout* findGroup = BLayoutBuilder::Group<>(B_VERTICAL, 0.0) 570 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) 571 .Add(BGroupLayoutBuilder(B_HORIZONTAL, B_USE_SMALL_SPACING) 572 .Add(fFindCloseButton) 573 .Add(fFindTextControl) 574 .Add(fFindPreviousButton) 575 .Add(fFindNextButton) 576 .Add(fFindCaseSensitiveCheckBox) 577 .SetInsets(kInsetSpacing, kInsetSpacing, 578 kInsetSpacing, kInsetSpacing) 579 ) 580 ; 581 582 // Navigation group 583 BGroupLayout* navigationGroup = BLayoutBuilder::Group<>(B_VERTICAL, 0.0) 584 .Add(BLayoutBuilder::Group<>(B_HORIZONTAL, kElementSpacing) 585 .Add(fBackButton) 586 .Add(fForwardButton) 587 .Add(fStopButton) 588 .Add(fHomeButton) 589 .Add(fURLInputGroup) 590 .SetInsets(kInsetSpacing, kInsetSpacing, kInsetSpacing, 591 kInsetSpacing) 592 ) 593 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) 594 ; 595 596 // Status bar group 597 BGroupLayout* statusGroup = BLayoutBuilder::Group<>(B_VERTICAL, 0.0) 598 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) 599 .Add(BLayoutBuilder::Group<>(B_HORIZONTAL, kElementSpacing) 600 .Add(fStatusText) 601 .Add(fLoadingProgressBar, 0.2) 602 .AddStrut(12 - kElementSpacing) 603 .SetInsets(kInsetSpacing, 0, kInsetSpacing, 0) 604 ) 605 ; 606 607 BitmapButton* toggleFullscreenButton = new BitmapButton(kWindowIconBits, 608 kWindowIconWidth, kWindowIconHeight, kWindowIconFormat, 609 new BMessage(TOGGLE_FULLSCREEN)); 610 toggleFullscreenButton->SetBackgroundMode(BitmapButton::MENUBAR_BACKGROUND); 611 612 BGroupLayout* menuBarGroup = BLayoutBuilder::Group<>(B_HORIZONTAL, 0.0) 613 .Add(mainMenu) 614 .Add(toggleFullscreenButton, 0.0f) 615 ; 616 617 if (fAppSettings->GetValue(kSettingsShowBookmarkBar, true)) 618 _ShowBookmarkBar(true); 619 else 620 _ShowBookmarkBar(false); 621 622 fSavePanel = new BFilePanel(B_SAVE_PANEL, new BMessenger(this), NULL, 0, 623 false); 624 625 // Layout 626 BGroupView* topView = new BGroupView(B_VERTICAL, 0.0); 627 628 #if !INTEGRATE_MENU_INTO_TAB_BAR 629 topView->AddChild(menuBarGroup); 630 #endif 631 topView->AddChild(fTabManager->TabGroup()); 632 topView->AddChild(navigationGroup); 633 if (fBookmarkBar != NULL) 634 topView->AddChild(fBookmarkBar); 635 topView->AddChild(fTabManager->ContainerView()); 636 topView->AddChild(findGroup); 637 topView->AddChild(statusGroup); 638 639 AddChild(topView); 640 641 fURLInputGroup->MakeFocus(true); 642 643 fMenuGroup = menuBarGroup; 644 fTabGroup = fTabManager->TabGroup()->GetLayout(); 645 fNavigationGroup = navigationGroup; 646 fFindGroup = findGroup; 647 fStatusGroup = statusGroup; 648 fToggleFullscreenButton = layoutItemFor(toggleFullscreenButton); 649 650 fFindGroup->SetVisible(false); 651 fToggleFullscreenButton->SetVisible(false); 652 653 CreateNewTab(url, true, webView); 654 _ShowInterface(true); 655 _SetAutoHideInterfaceInFullscreen(fAppSettings->GetValue( 656 kSettingsKeyAutoHideInterfaceInFullscreenMode, 657 fAutoHideInterfaceInFullscreenMode)); 658 659 AddShortcut('F', B_COMMAND_KEY | B_SHIFT_KEY, 660 new BMessage(EDIT_HIDE_FIND_GROUP)); 661 // TODO: Should be a different shortcut, H is usually for Find selection. 662 AddShortcut('H', B_COMMAND_KEY, new BMessage(HOME)); 663 664 // Add shortcuts to select a particular tab 665 for (int32 i = 1; i <= 9; i++) { 666 BMessage* selectTab = new BMessage(SELECT_TAB); 667 selectTab->AddInt32("tab index", i - 1); 668 char numStr[2]; 669 snprintf(numStr, sizeof(numStr), "%d", (int) i); 670 AddShortcut(numStr[0], B_COMMAND_KEY, selectTab); 671 } 672 673 BKeymap keymap; 674 keymap.SetToCurrent(); 675 BObjectList<const char> unmodified(3, true); 676 if (keymap.GetModifiedCharacters("+", B_SHIFT_KEY, 0, &unmodified) 677 == B_OK) { 678 int32 count = unmodified.CountItems(); 679 for (int32 i = 0; i < count; i++) { 680 uint32 key = BUnicodeChar::FromUTF8(unmodified.ItemAt(i)); 681 if (!HasShortcut(key, 0)) { 682 // Add semantic zoom in shortcut, bug #7428 683 AddShortcut(key, B_COMMAND_KEY, 684 new BMessage(ZOOM_FACTOR_INCREASE)); 685 } 686 } 687 } 688 unmodified.MakeEmpty(); 689 690 be_app->PostMessage(WINDOW_OPENED); 691 } 692 693 694 BrowserWindow::~BrowserWindow() 695 { 696 fAppSettings->RemoveListener(BMessenger(this)); 697 delete fTabManager; 698 delete fPulseRunner; 699 delete fSavePanel; 700 } 701 702 703 void 704 BrowserWindow::DispatchMessage(BMessage* message, BHandler* target) 705 { 706 const char* bytes; 707 int32 modifierKeys; 708 if ((message->what == B_KEY_DOWN || message->what == B_UNMAPPED_KEY_DOWN) 709 && message->FindString("bytes", &bytes) == B_OK 710 && message->FindInt32("modifiers", &modifierKeys) == B_OK) { 711 if (bytes[0] == B_FUNCTION_KEY) { 712 // Some function key Firefox compatibility 713 int32 key; 714 if (message->FindInt32("key", &key) == B_OK) { 715 switch (key) { 716 case B_F5_KEY: 717 PostMessage(RELOAD); 718 break; 719 720 case B_F11_KEY: 721 PostMessage(TOGGLE_FULLSCREEN); 722 break; 723 724 default: 725 break; 726 } 727 } 728 } else if (target == fURLInputGroup->TextView()) { 729 // Handle B_RETURN in the URL text control. This is the easiest 730 // way to react *only* when the user presses the return key in the 731 // address bar, as opposed to trying to load whatever is in there 732 // when the text control just goes out of focus. 733 if (bytes[0] == B_RETURN) { 734 // Do it in such a way that the user sees the Go-button go down. 735 _InvokeButtonVisibly(fURLInputGroup->GoButton()); 736 return; 737 } 738 } else if (target == fFindTextControl->TextView()) { 739 // Handle B_RETURN when the find text control has focus. 740 if (bytes[0] == B_RETURN) { 741 if ((modifierKeys & B_SHIFT_KEY) != 0) 742 _InvokeButtonVisibly(fFindPreviousButton); 743 else 744 _InvokeButtonVisibly(fFindNextButton); 745 return; 746 } else if (bytes[0] == B_ESCAPE) { 747 _InvokeButtonVisibly(fFindCloseButton); 748 return; 749 } 750 } else if (bytes[0] == B_ESCAPE && !fMenusRunning) { 751 if (modifierKeys == B_COMMAND_KEY) 752 _ShowInterface(true); 753 else { 754 // Default escape key behavior: 755 PostMessage(STOP); 756 return; 757 } 758 } 759 } 760 761 if (message->what == B_MOUSE_MOVED || message->what == B_MOUSE_DOWN 762 || message->what == B_MOUSE_UP) { 763 message->FindPoint("where", &fLastMousePos); 764 if (message->FindInt64("when", &fLastMouseMovedTime) != B_OK) 765 fLastMouseMovedTime = system_time(); 766 _CheckAutoHideInterface(); 767 } 768 769 if (message->what == B_MOUSE_WHEEL_CHANGED) { 770 BPoint where; 771 uint32 buttons; 772 CurrentWebView()->GetMouse(&where, &buttons, false); 773 // Only do this when the mouse is over the web view 774 if (CurrentWebView()->Bounds().Contains(where)) { 775 // Zoom and unzoom text on Command + mouse wheel. 776 // This could of course (and maybe should be) implemented in the 777 // WebView, but there would need to be a way for the WebView to 778 // know the setting of the fZoomTextOnly member here. Plus other 779 // clients of the API may not want this feature. 780 if ((modifiers() & B_COMMAND_KEY) != 0) { 781 float deltaY; 782 if (message->FindFloat("be:wheel_delta_y", &deltaY) == B_OK) { 783 if (deltaY < 0) 784 CurrentWebView()->IncreaseZoomFactor(fZoomTextOnly); 785 else 786 CurrentWebView()->DecreaseZoomFactor(fZoomTextOnly); 787 788 return; 789 } 790 } 791 } else { 792 // Also don't scroll up and down if the mouse is not over the 793 // web view 794 return; 795 } 796 } 797 798 BWebWindow::DispatchMessage(message, target); 799 } 800 801 802 void 803 BrowserWindow::MessageReceived(BMessage* message) 804 { 805 switch (message->what) { 806 case OPEN_LOCATION: 807 _ShowInterface(true); 808 if (fURLInputGroup->TextView()->IsFocus()) 809 fURLInputGroup->TextView()->SelectAll(); 810 else 811 fURLInputGroup->MakeFocus(true); 812 break; 813 814 case RELOAD: 815 CurrentWebView()->Reload(); 816 break; 817 818 case SHOW_HIDE_BOOKMARK_BAR: 819 _ShowBookmarkBar(fBookmarkBar->IsHidden()); 820 break; 821 822 case GOTO_URL: 823 { 824 BString url; 825 if (message->FindString("url", &url) != B_OK) 826 url = fURLInputGroup->Text(); 827 828 _SetPageIcon(CurrentWebView(), NULL); 829 _SmartURLHandler(url); 830 831 break; 832 } 833 834 case SAVE_PAGE: 835 { 836 fSavePanel->SetSaveText(CurrentWebView()->MainFrameTitle()); 837 fSavePanel->Show(); 838 break; 839 } 840 841 case B_SAVE_REQUESTED: 842 { 843 entry_ref ref; 844 BString name; 845 846 if (message->FindRef("directory", &ref) == B_OK && 847 message->FindString("name", &name) == B_OK) { 848 BDirectory dir(&ref); 849 BFile output(&dir, name, 850 B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 851 CurrentWebView()->WebPage()->GetContentsAsMHTML(output); 852 } 853 854 break; 855 } 856 857 case GO_BACK: 858 CurrentWebView()->GoBack(); 859 break; 860 861 case GO_FORWARD: 862 CurrentWebView()->GoForward(); 863 break; 864 865 case STOP: 866 CurrentWebView()->StopLoading(); 867 break; 868 869 case HOME: 870 CurrentWebView()->LoadURL(fStartPageURL); 871 break; 872 873 case CLEAR_HISTORY: { 874 BrowsingHistory* history = BrowsingHistory::DefaultInstance(); 875 if (history->CountItems() == 0) 876 break; 877 BAlert* alert = new BAlert(B_TRANSLATE("Confirmation"), 878 B_TRANSLATE("Do you really want to " 879 "clear the browsing history?"), B_TRANSLATE("Clear"), 880 B_TRANSLATE("Cancel")); 881 alert->SetShortcut(1, B_ESCAPE); 882 883 if (alert->Go() == 0) 884 history->Clear(); 885 break; 886 } 887 888 case CREATE_BOOKMARK: 889 _CreateBookmark(); 890 break; 891 892 case SHOW_BOOKMARKS: 893 _ShowBookmarks(); 894 break; 895 896 case B_REFS_RECEIVED: 897 { 898 // Currently the only source of these messages is the bookmarks 899 // menu. 900 // Filter refs into URLs, this also gets rid of refs for folders. 901 // For clicks on sub-folders in the bookmarks menu, we have Tracker 902 // open the corresponding folder. 903 entry_ref ref; 904 uint32 addedCount = 0; 905 for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) { 906 BEntry entry(&ref); 907 uint32 addedSubCount = 0; 908 if (entry.IsDirectory()) { 909 BDirectory directory(&entry); 910 _AddBookmarkURLsRecursively(directory, message, 911 addedSubCount); 912 } else { 913 BFile file(&ref, B_READ_ONLY); 914 BString url; 915 if (_ReadURLAttr(file, url)) { 916 message->AddString("url", url.String()); 917 addedSubCount++; 918 } 919 } 920 if (addedSubCount == 0) { 921 // Don't know what to do with this entry, just pass it 922 // on to the system to handle. Note that this may result 923 // in us opening other supported files via the application 924 // mechanism. 925 be_roster->Launch(&ref); 926 } 927 addedCount += addedSubCount; 928 } 929 message->RemoveName("refs"); 930 if (addedCount > 10) { 931 BString string(B_TRANSLATE_COMMENT("Do you want to open " 932 "%addedCount bookmarks all at once?", "Don't translate " 933 "variable %addedCount.")); 934 string.ReplaceFirst("%addedCount", BString() << addedCount); 935 936 BAlert* alert = new BAlert( 937 B_TRANSLATE("Open bookmarks confirmation"), 938 string.String(), B_TRANSLATE("Cancel"), 939 B_TRANSLATE("Open all")); 940 alert->SetShortcut(0, B_ESCAPE); 941 if (alert->Go() == 0) 942 break; 943 } 944 message->AddPointer("window", this); 945 be_app->PostMessage(message); 946 break; 947 } 948 949 case B_SIMPLE_DATA: 950 { 951 // User possibly dropped files on this window. 952 // If there is more than one entry_ref, let the app handle it 953 // (open one new page per ref). If there is one ref, open it in 954 // this window. 955 type_code type; 956 int32 countFound; 957 if (message->GetInfo("refs", &type, &countFound) != B_OK 958 || type != B_REF_TYPE) { 959 break; 960 } 961 if (countFound > 1) { 962 message->what = B_REFS_RECEIVED; 963 be_app->PostMessage(message); 964 break; 965 } 966 entry_ref ref; 967 if (message->FindRef("refs", &ref) != B_OK) 968 break; 969 BEntry entry(&ref, true); 970 BPath path; 971 if (!entry.Exists() || entry.GetPath(&path) != B_OK) 972 break; 973 974 BUrl url(path); 975 CurrentWebView()->LoadURL(url); 976 break; 977 } 978 979 case ZOOM_FACTOR_INCREASE: 980 CurrentWebView()->IncreaseZoomFactor(fZoomTextOnly); 981 break; 982 case ZOOM_FACTOR_DECREASE: 983 CurrentWebView()->DecreaseZoomFactor(fZoomTextOnly); 984 break; 985 case ZOOM_FACTOR_RESET: 986 CurrentWebView()->ResetZoomFactor(); 987 break; 988 case ZOOM_TEXT_ONLY: 989 fZoomTextOnly = !fZoomTextOnly; 990 fZoomTextOnlyMenuItem->SetMarked(fZoomTextOnly); 991 // TODO: Would be nice to have an instant update if the page is 992 // already zoomed. 993 break; 994 995 case TOGGLE_FULLSCREEN: 996 ToggleFullscreen(); 997 break; 998 999 case TOGGLE_AUTO_HIDE_INTERFACE_IN_FULLSCREEN: 1000 _SetAutoHideInterfaceInFullscreen( 1001 !fAutoHideInterfaceInFullscreenMode); 1002 break; 1003 1004 case CHECK_AUTO_HIDE_INTERFACE: 1005 _CheckAutoHideInterface(); 1006 break; 1007 1008 case SHOW_PAGE_SOURCE: 1009 CurrentWebView()->WebPage()->SendPageSource(); 1010 break; 1011 case B_PAGE_SOURCE_RESULT: 1012 _HandlePageSourceResult(message); 1013 break; 1014 1015 case EDIT_FIND_NEXT: 1016 CurrentWebView()->FindString(fFindTextControl->Text(), true, 1017 fFindCaseSensitiveCheckBox->Value()); 1018 break; 1019 case FIND_TEXT_CHANGED: 1020 { 1021 bool findTextAvailable = strlen(fFindTextControl->Text()) > 0; 1022 fFindPreviousMenuItem->SetEnabled(findTextAvailable); 1023 fFindNextMenuItem->SetEnabled(findTextAvailable); 1024 break; 1025 } 1026 case EDIT_FIND_PREVIOUS: 1027 CurrentWebView()->FindString(fFindTextControl->Text(), false, 1028 fFindCaseSensitiveCheckBox->Value()); 1029 break; 1030 case EDIT_SHOW_FIND_GROUP: 1031 if (!fFindGroup->IsVisible()) 1032 fFindGroup->SetVisible(true); 1033 fFindTextControl->MakeFocus(true); 1034 break; 1035 case EDIT_HIDE_FIND_GROUP: 1036 if (fFindGroup->IsVisible()) { 1037 fFindGroup->SetVisible(false); 1038 if (CurrentWebView() != NULL) 1039 CurrentWebView()->MakeFocus(true); 1040 } 1041 break; 1042 1043 case B_CUT: 1044 case B_COPY: 1045 case B_PASTE: 1046 { 1047 BTextView* textView = dynamic_cast<BTextView*>(CurrentFocus()); 1048 if (textView != NULL) 1049 textView->MessageReceived(message); 1050 else if (CurrentWebView() != NULL) 1051 CurrentWebView()->MessageReceived(message); 1052 break; 1053 } 1054 1055 case B_EDITING_CAPABILITIES_RESULT: 1056 { 1057 BWebView* webView; 1058 if (message->FindPointer("view", 1059 reinterpret_cast<void**>(&webView)) != B_OK 1060 || webView != CurrentWebView()) { 1061 break; 1062 } 1063 bool canCut; 1064 bool canCopy; 1065 bool canPaste; 1066 if (message->FindBool("can cut", &canCut) != B_OK) 1067 canCut = false; 1068 if (message->FindBool("can copy", &canCopy) != B_OK) 1069 canCopy = false; 1070 if (message->FindBool("can paste", &canPaste) != B_OK) 1071 canPaste = false; 1072 fCutMenuItem->SetEnabled(canCut); 1073 fCopyMenuItem->SetEnabled(canCopy); 1074 fPasteMenuItem->SetEnabled(canPaste); 1075 break; 1076 } 1077 1078 case SHOW_DOWNLOAD_WINDOW: 1079 case SHOW_SETTINGS_WINDOW: 1080 case SHOW_CONSOLE_WINDOW: 1081 case SHOW_COOKIE_WINDOW: 1082 message->AddUInt32("workspaces", Workspaces()); 1083 be_app->PostMessage(message); 1084 break; 1085 1086 case CLOSE_TAB: 1087 if (fTabManager->CountTabs() > 1) { 1088 int32 index; 1089 if (message->FindInt32("tab index", &index) != B_OK) 1090 index = fTabManager->SelectedTabIndex(); 1091 _ShutdownTab(index); 1092 _UpdateTabGroupVisibility(); 1093 } else 1094 PostMessage(B_QUIT_REQUESTED); 1095 break; 1096 1097 case SELECT_TAB: 1098 { 1099 int32 index; 1100 if (message->FindInt32("tab index", &index) == B_OK 1101 && fTabManager->SelectedTabIndex() != index 1102 && fTabManager->CountTabs() > index) { 1103 fTabManager->SelectTab(index); 1104 } 1105 1106 break; 1107 } 1108 1109 case TAB_CHANGED: 1110 { 1111 // This message may be received also when the last tab closed, 1112 // i.e. with index == -1. 1113 int32 index; 1114 if (message->FindInt32("tab index", &index) != B_OK) 1115 index = -1; 1116 _TabChanged(index); 1117 break; 1118 } 1119 1120 case SETTINGS_VALUE_CHANGED: 1121 { 1122 BString name; 1123 if (message->FindString("name", &name) != B_OK) 1124 break; 1125 bool flag; 1126 BString string; 1127 uint32 value; 1128 if (name == kSettingsKeyShowTabsIfSinglePageOpen 1129 && message->FindBool("value", &flag) == B_OK) { 1130 if (fShowTabsIfSinglePageOpen != flag) { 1131 fShowTabsIfSinglePageOpen = flag; 1132 _UpdateTabGroupVisibility(); 1133 } 1134 } else if (name == kSettingsKeyAutoHidePointer 1135 && message->FindBool("value", &flag) == B_OK) { 1136 fAutoHidePointer = flag; 1137 if (CurrentWebView()) 1138 CurrentWebView()->SetAutoHidePointer(fAutoHidePointer); 1139 } else if (name == kSettingsKeyStartPageURL 1140 && message->FindString("value", &string) == B_OK) { 1141 fStartPageURL = string; 1142 } else if (name == kSettingsKeySearchPageURL 1143 && message->FindString("value", &string) == B_OK) { 1144 fSearchPageURL = string; 1145 } else if (name == kSettingsKeyNewWindowPolicy 1146 && message->FindUInt32("value", &value) == B_OK) { 1147 fNewWindowPolicy = value; 1148 } else if (name == kSettingsKeyNewTabPolicy 1149 && message->FindUInt32("value", &value) == B_OK) { 1150 fNewTabPolicy = value; 1151 } else if (name == kSettingsKeyAutoHideInterfaceInFullscreenMode 1152 && message->FindBool("value", &flag) == B_OK) { 1153 _SetAutoHideInterfaceInFullscreen(flag); 1154 } else if (name == kSettingsKeyShowHomeButton 1155 && message->FindBool("value", &flag) == B_OK) { 1156 if (flag) 1157 fHomeButton->Show(); 1158 else 1159 fHomeButton->Hide(); 1160 } else if (name == kSettingsShowBookmarkBar 1161 && message->FindBool("value", &flag) == B_OK) { 1162 _ShowBookmarkBar(flag); 1163 } 1164 break; 1165 } 1166 case ADD_CONSOLE_MESSAGE: 1167 be_app->PostMessage(message); 1168 BWebWindow::MessageReceived(message); 1169 break; 1170 1171 default: 1172 BWebWindow::MessageReceived(message); 1173 break; 1174 } 1175 } 1176 1177 1178 status_t 1179 BrowserWindow::Archive(BMessage* archive, bool deep) const 1180 { 1181 status_t ret = archive->AddRect("window frame", Frame()); 1182 1183 for (int i = 0; i < fTabManager->CountTabs(); i++) { 1184 BWebView* view = dynamic_cast<BWebView*>(fTabManager->ViewForTab(i)); 1185 if (view == NULL) { 1186 continue; 1187 } 1188 1189 if (ret == B_OK) 1190 ret = archive->AddString("tab", view->MainFrameURL()); 1191 } 1192 1193 return ret; 1194 } 1195 1196 1197 bool 1198 BrowserWindow::QuitRequested() 1199 { 1200 // TODO: Check for modified form data and ask user for confirmation, etc. 1201 1202 BMessage message(WINDOW_CLOSED); 1203 Archive(&message); 1204 1205 // Iterate over all tabs to delete all BWebViews. 1206 // Do this here, so WebKit tear down happens earlier. 1207 SetCurrentWebView(NULL); 1208 while (fTabManager->CountTabs() > 0) 1209 _ShutdownTab(0); 1210 1211 message.AddRect("window frame", WindowFrame()); 1212 be_app->PostMessage(&message); 1213 return true; 1214 } 1215 1216 1217 void 1218 BrowserWindow::MenusBeginning() 1219 { 1220 _UpdateHistoryMenu(); 1221 _UpdateClipboardItems(); 1222 fMenusRunning = true; 1223 } 1224 1225 1226 void 1227 BrowserWindow::MenusEnded() 1228 { 1229 fMenusRunning = false; 1230 } 1231 1232 1233 void 1234 BrowserWindow::ScreenChanged(BRect screenSize, color_space format) 1235 { 1236 if (fIsFullscreen) 1237 _ResizeToScreen(); 1238 } 1239 1240 1241 void 1242 BrowserWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces) 1243 { 1244 if (fIsFullscreen) 1245 _ResizeToScreen(); 1246 } 1247 1248 1249 static bool 1250 viewIsChild(const BView* parent, const BView* view) 1251 { 1252 if (parent == view) 1253 return true; 1254 1255 int32 count = parent->CountChildren(); 1256 for (int32 i = 0; i < count; i++) { 1257 BView* child = parent->ChildAt(i); 1258 if (viewIsChild(child, view)) 1259 return true; 1260 } 1261 return false; 1262 } 1263 1264 1265 void 1266 BrowserWindow::SetCurrentWebView(BWebView* webView) 1267 { 1268 if (webView == CurrentWebView()) 1269 return; 1270 1271 if (CurrentWebView() != NULL) { 1272 // Remember the currently focused view before switching tabs, 1273 // so that we can revert the focus when switching back to this tab 1274 // later. 1275 PageUserData* userData = static_cast<PageUserData*>( 1276 CurrentWebView()->GetUserData()); 1277 if (userData == NULL) { 1278 userData = new PageUserData(CurrentFocus()); 1279 CurrentWebView()->SetUserData(userData); 1280 } 1281 userData->SetFocusedView(CurrentFocus()); 1282 userData->SetURLInputContents(fURLInputGroup->Text()); 1283 int32 selectionStart; 1284 int32 selectionEnd; 1285 fURLInputGroup->TextView()->GetSelection(&selectionStart, 1286 &selectionEnd); 1287 userData->SetURLInputSelection(selectionStart, selectionEnd); 1288 } 1289 1290 BWebWindow::SetCurrentWebView(webView); 1291 1292 if (webView != NULL) { 1293 webView->SetAutoHidePointer(fAutoHidePointer); 1294 1295 _UpdateTitle(webView->MainFrameTitle()); 1296 1297 // Restore the previous focus or focus the web view. 1298 PageUserData* userData = static_cast<PageUserData*>( 1299 webView->GetUserData()); 1300 BView* focusedView = NULL; 1301 if (userData != NULL) 1302 focusedView = userData->FocusedView(); 1303 1304 if (focusedView != NULL 1305 && viewIsChild(GetLayout()->View(), focusedView)) { 1306 focusedView->MakeFocus(true); 1307 } else 1308 webView->MakeFocus(true); 1309 1310 if (userData != NULL) { 1311 fURLInputGroup->SetPageIcon(userData->PageIcon()); 1312 if (userData->URLInputContents().Length()) 1313 fURLInputGroup->SetText(userData->URLInputContents()); 1314 else 1315 fURLInputGroup->SetText(webView->MainFrameURL()); 1316 if (userData->URLInputSelectionStart() >= 0) { 1317 fURLInputGroup->TextView()->Select( 1318 userData->URLInputSelectionStart(), 1319 userData->URLInputSelectionEnd()); 1320 } 1321 } else { 1322 fURLInputGroup->SetPageIcon(NULL); 1323 fURLInputGroup->SetText(webView->MainFrameURL()); 1324 } 1325 1326 // Trigger update of the interface to the new page, by requesting 1327 // to resend all notifications. 1328 webView->WebPage()->ResendNotifications(); 1329 } else 1330 _UpdateTitle(""); 1331 } 1332 1333 1334 bool 1335 BrowserWindow::IsBlankTab() const 1336 { 1337 if (CurrentWebView() == NULL) 1338 return false; 1339 BString requestedURL = CurrentWebView()->MainFrameRequestedURL(); 1340 return requestedURL.Length() == 0 1341 || requestedURL == _NewTabURL(fTabManager->CountTabs() == 1); 1342 } 1343 1344 1345 void 1346 BrowserWindow::CreateNewTab(const BString& _url, bool select, 1347 BWebView* webView) 1348 { 1349 bool applyNewPagePolicy = webView == NULL; 1350 // Executed in app thread (new BWebPage needs to be created in app thread). 1351 if (webView == NULL) 1352 webView = new BWebView("web view", fContext); 1353 1354 bool isNewWindow = fTabManager->CountTabs() == 0; 1355 1356 fTabManager->AddTab(webView, B_TRANSLATE("New tab")); 1357 1358 BString url(_url); 1359 if (applyNewPagePolicy && url.Length() == 0) 1360 url = _NewTabURL(isNewWindow); 1361 1362 if (url.Length() > 0) 1363 webView->LoadURL(url.String()); 1364 1365 if (select) { 1366 fTabManager->SelectTab(fTabManager->CountTabs() - 1); 1367 SetCurrentWebView(webView); 1368 webView->WebPage()->ResendNotifications(); 1369 fURLInputGroup->SetPageIcon(NULL); 1370 fURLInputGroup->SetText(url.String()); 1371 fURLInputGroup->MakeFocus(true); 1372 } 1373 1374 _ShowInterface(true); 1375 _UpdateTabGroupVisibility(); 1376 } 1377 1378 1379 BRect 1380 BrowserWindow::WindowFrame() const 1381 { 1382 if (fIsFullscreen) 1383 return fNonFullscreenWindowFrame; 1384 else 1385 return Frame(); 1386 } 1387 1388 1389 void 1390 BrowserWindow::ToggleFullscreen() 1391 { 1392 if (fIsFullscreen) { 1393 MoveTo(fNonFullscreenWindowFrame.LeftTop()); 1394 ResizeTo(fNonFullscreenWindowFrame.Width(), 1395 fNonFullscreenWindowFrame.Height()); 1396 1397 SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_MOVABLE)); 1398 SetLook(B_DOCUMENT_WINDOW_LOOK); 1399 1400 _ShowInterface(true); 1401 } else { 1402 fNonFullscreenWindowFrame = Frame(); 1403 _ResizeToScreen(); 1404 1405 SetFlags(Flags() | (B_NOT_RESIZABLE | B_NOT_MOVABLE)); 1406 SetLook(B_TITLED_WINDOW_LOOK); 1407 } 1408 fIsFullscreen = !fIsFullscreen; 1409 fFullscreenItem->SetMarked(fIsFullscreen); 1410 fToggleFullscreenButton->SetVisible(fIsFullscreen); 1411 } 1412 1413 1414 // #pragma mark - Notification API 1415 1416 1417 void 1418 BrowserWindow::NavigationRequested(const BString& url, BWebView* view) 1419 { 1420 } 1421 1422 1423 void 1424 BrowserWindow::NewWindowRequested(const BString& url, bool primaryAction) 1425 { 1426 // Always open new windows in the application thread, since 1427 // creating a BWebView will try to grab the application lock. 1428 // But our own WebPage may already try to lock us from within 1429 // the application thread -> dead-lock. Thus we can't wait for 1430 // a reply here. 1431 BMessage message(NEW_TAB); 1432 message.AddPointer("window", this); 1433 message.AddString("url", url); 1434 message.AddBool("select", primaryAction); 1435 be_app->PostMessage(&message); 1436 } 1437 1438 1439 void 1440 BrowserWindow::NewPageCreated(BWebView* view, BRect windowFrame, 1441 bool modalDialog, bool resizable, bool activate) 1442 { 1443 if (windowFrame.IsValid()) { 1444 BrowserWindow* window = new BrowserWindow(windowFrame, fAppSettings, 1445 BString(), fContext, INTERFACE_ELEMENT_STATUS, 1446 view); 1447 window->Show(); 1448 } else 1449 CreateNewTab(BString(), activate, view); 1450 } 1451 1452 1453 void 1454 BrowserWindow::CloseWindowRequested(BWebView* view) 1455 { 1456 int32 index = fTabManager->TabForView(view); 1457 if (index < 0) { 1458 // Tab is already gone. 1459 return; 1460 } 1461 BMessage message(CLOSE_TAB); 1462 message.AddInt32("tab index", index); 1463 PostMessage(&message, this); 1464 } 1465 1466 1467 void 1468 BrowserWindow::LoadNegotiating(const BString& url, BWebView* view) 1469 { 1470 if (view != CurrentWebView()) 1471 return; 1472 1473 fURLInputGroup->SetText(url.String()); 1474 1475 BString status(B_TRANSLATE("Requesting %url")); 1476 status.ReplaceFirst("%url", url); 1477 view->WebPage()->SetStatusMessage(status); 1478 } 1479 1480 1481 void 1482 BrowserWindow::LoadCommitted(const BString& url, BWebView* view) 1483 { 1484 if (view != CurrentWebView()) 1485 return; 1486 1487 // This hook is invoked when the load is commited. 1488 fURLInputGroup->SetText(url.String()); 1489 1490 BString status(B_TRANSLATE("Loading %url")); 1491 status.ReplaceFirst("%url", url); 1492 view->WebPage()->SetStatusMessage(status); 1493 } 1494 1495 1496 void 1497 BrowserWindow::LoadProgress(float progress, BWebView* view) 1498 { 1499 if (view != CurrentWebView()) 1500 return; 1501 1502 if (progress < 100 && fLoadingProgressBar->IsHidden()) 1503 _ShowProgressBar(true); 1504 else if (progress == 100 && !fLoadingProgressBar->IsHidden()) 1505 _ShowProgressBar(false); 1506 fLoadingProgressBar->SetTo(progress); 1507 } 1508 1509 1510 void 1511 BrowserWindow::LoadFailed(const BString& url, BWebView* view) 1512 { 1513 if (view != CurrentWebView()) 1514 return; 1515 1516 BString status(B_TRANSLATE_COMMENT("%url failed", "Loading URL failed. " 1517 "Don't translate variable %url.")); 1518 status.ReplaceFirst("%url", url); 1519 view->WebPage()->SetStatusMessage(status); 1520 if (!fLoadingProgressBar->IsHidden()) 1521 fLoadingProgressBar->Hide(); 1522 } 1523 1524 1525 void 1526 BrowserWindow::LoadFinished(const BString& url, BWebView* view) 1527 { 1528 if (view != CurrentWebView()) 1529 return; 1530 1531 fURLInputGroup->SetText(url.String()); 1532 1533 BString status(B_TRANSLATE_COMMENT("%url finished", "Loading URL " 1534 "finished. Don't translate variable %url.")); 1535 status.ReplaceFirst("%url", url); 1536 view->WebPage()->SetStatusMessage(status); 1537 if (!fLoadingProgressBar->IsHidden()) 1538 fLoadingProgressBar->Hide(); 1539 1540 NavigationCapabilitiesChanged(fBackButton->IsEnabled(), 1541 fForwardButton->IsEnabled(), false, view); 1542 1543 int32 tabIndex = fTabManager->TabForView(view); 1544 if (tabIndex > 0 && strcmp(B_TRANSLATE("New tab"), 1545 fTabManager->TabLabel(tabIndex)) == 0) 1546 fTabManager->SetTabLabel(tabIndex, url); 1547 } 1548 1549 1550 void 1551 BrowserWindow::MainDocumentError(const BString& failingURL, 1552 const BString& localizedDescription, BWebView* view) 1553 { 1554 // Make sure we show the page that contains the view. 1555 if (!_ShowPage(view)) 1556 return; 1557 1558 // Try delegating the URL to an external app instead. 1559 int32 at = failingURL.FindFirst(":"); 1560 if (at > 0) { 1561 BString proto; 1562 failingURL.CopyInto(proto, 0, at); 1563 1564 bool handled = false; 1565 1566 for (unsigned int i = 0; i < sizeof(kHandledProtocols) / sizeof(char*); 1567 i++) { 1568 handled = (proto == kHandledProtocols[i]); 1569 if (handled) 1570 break; 1571 } 1572 1573 if (!handled) { 1574 _SmartURLHandler(failingURL); 1575 return; 1576 } 1577 } 1578 1579 BWebWindow::MainDocumentError(failingURL, localizedDescription, view); 1580 1581 // TODO: Remove the failing URL from the BrowsingHistory! 1582 } 1583 1584 1585 void 1586 BrowserWindow::TitleChanged(const BString& title, BWebView* view) 1587 { 1588 int32 tabIndex = fTabManager->TabForView(view); 1589 if (tabIndex < 0) 1590 return; 1591 1592 fTabManager->SetTabLabel(tabIndex, title); 1593 1594 if (view != CurrentWebView()) 1595 return; 1596 1597 _UpdateTitle(title); 1598 } 1599 1600 1601 void 1602 BrowserWindow::IconReceived(const BBitmap* icon, BWebView* view) 1603 { 1604 // The view may already be gone, since this notification arrives 1605 // asynchronously. 1606 if (!fTabManager->HasView(view)) 1607 return; 1608 1609 _SetPageIcon(view, icon); 1610 } 1611 1612 1613 void 1614 BrowserWindow::ResizeRequested(float width, float height, BWebView* view) 1615 { 1616 if (view != CurrentWebView()) 1617 return; 1618 1619 // Ignore request when there is more than one BWebView embedded. 1620 if (fTabManager->CountTabs() > 1) 1621 return; 1622 1623 // Make sure the new frame is not larger than the screen frame minus 1624 // window decorator border. 1625 BScreen screen(this); 1626 BRect screenFrame = screen.Frame(); 1627 BRect decoratorFrame = DecoratorFrame(); 1628 BRect frame = Frame(); 1629 1630 screenFrame.left += decoratorFrame.left - frame.left; 1631 screenFrame.right += decoratorFrame.right - frame.right; 1632 screenFrame.top += decoratorFrame.top - frame.top; 1633 screenFrame.bottom += decoratorFrame.bottom - frame.bottom; 1634 1635 width = min_c(width, screen.Frame().Width()); 1636 height = min_c(height, screen.Frame().Height()); 1637 1638 frame.right = frame.left + width; 1639 frame.bottom = frame.top + height; 1640 1641 // frame is now not larger than screenFrame, but may still be partly outside 1642 if (!screenFrame.Contains(frame)) { 1643 if (frame.left < screenFrame.left) 1644 frame.OffsetBy(screenFrame.left - frame.left, 0); 1645 else if (frame.right > screenFrame.right) 1646 frame.OffsetBy(screenFrame.right - frame.right, 0); 1647 if (frame.top < screenFrame.top) 1648 frame.OffsetBy(screenFrame.top - frame.top, 0); 1649 else if (frame.bottom > screenFrame.bottom) 1650 frame.OffsetBy(screenFrame.bottom - frame.bottom, 0); 1651 } 1652 1653 MoveTo(frame.left, frame.top); 1654 ResizeTo(width, height); 1655 } 1656 1657 1658 void 1659 BrowserWindow::SetToolBarsVisible(bool flag, BWebView* view) 1660 { 1661 // TODO 1662 // TODO: Ignore request when there is more than one BWebView embedded! 1663 } 1664 1665 1666 void 1667 BrowserWindow::SetStatusBarVisible(bool flag, BWebView* view) 1668 { 1669 // TODO 1670 // TODO: Ignore request when there is more than one BWebView embedded! 1671 } 1672 1673 1674 void 1675 BrowserWindow::SetMenuBarVisible(bool flag, BWebView* view) 1676 { 1677 // TODO 1678 // TODO: Ignore request when there is more than one BWebView embedded! 1679 } 1680 1681 1682 void 1683 BrowserWindow::SetResizable(bool flag, BWebView* view) 1684 { 1685 // TODO: Ignore request when there is more than one BWebView embedded! 1686 1687 if (flag) 1688 SetFlags(Flags() & ~B_NOT_RESIZABLE); 1689 else 1690 SetFlags(Flags() | B_NOT_RESIZABLE); 1691 } 1692 1693 1694 void 1695 BrowserWindow::StatusChanged(const BString& statusText, BWebView* view) 1696 { 1697 if (view != CurrentWebView()) 1698 return; 1699 1700 if (fStatusText) 1701 fStatusText->SetText(statusText.String()); 1702 } 1703 1704 1705 void 1706 BrowserWindow::NavigationCapabilitiesChanged(bool canGoBackward, 1707 bool canGoForward, bool canStop, BWebView* view) 1708 { 1709 if (view != CurrentWebView()) 1710 return; 1711 1712 fBackButton->SetEnabled(canGoBackward); 1713 fForwardButton->SetEnabled(canGoForward); 1714 fStopButton->SetEnabled(canStop); 1715 1716 fBackMenuItem->SetEnabled(canGoBackward); 1717 fForwardMenuItem->SetEnabled(canGoForward); 1718 } 1719 1720 1721 void 1722 BrowserWindow::UpdateGlobalHistory(const BString& url) 1723 { 1724 BrowsingHistory::DefaultInstance()->AddItem(BrowsingHistoryItem(url)); 1725 } 1726 1727 1728 bool 1729 BrowserWindow::AuthenticationChallenge(BString message, BString& inOutUser, 1730 BString& inOutPassword, bool& inOutRememberCredentials, 1731 uint32 failureCount, BWebView* view) 1732 { 1733 CredentialsStorage* persistentStorage 1734 = CredentialsStorage::PersistentInstance(); 1735 CredentialsStorage* sessionStorage 1736 = CredentialsStorage::SessionInstance(); 1737 1738 // TODO: Using the message as key here is not so smart. 1739 HashKeyString key(message); 1740 1741 if (failureCount == 0) { 1742 if (persistentStorage->Contains(key)) { 1743 Credentials credentials = persistentStorage->GetCredentials(key); 1744 inOutUser = credentials.Username(); 1745 inOutPassword = credentials.Password(); 1746 return true; 1747 } else if (sessionStorage->Contains(key)) { 1748 Credentials credentials = sessionStorage->GetCredentials(key); 1749 inOutUser = credentials.Username(); 1750 inOutPassword = credentials.Password(); 1751 return true; 1752 } 1753 } 1754 // Switch to the page for which this authentication is required. 1755 if (!_ShowPage(view)) 1756 return false; 1757 1758 AuthenticationPanel* panel = new AuthenticationPanel(Frame()); 1759 // Panel auto-destructs. 1760 bool success = panel->getAuthentication(message, inOutUser, inOutPassword, 1761 inOutRememberCredentials, failureCount > 0, inOutUser, inOutPassword, 1762 &inOutRememberCredentials); 1763 if (success) { 1764 Credentials credentials(inOutUser, inOutPassword); 1765 if (inOutRememberCredentials) 1766 persistentStorage->PutCredentials(key, credentials); 1767 else 1768 sessionStorage->PutCredentials(key, credentials); 1769 } 1770 return success; 1771 } 1772 1773 1774 // #pragma mark - private 1775 1776 1777 void 1778 BrowserWindow::_UpdateTitle(const BString& title) 1779 { 1780 BString windowTitle = title; 1781 if (windowTitle.Length() > 0) 1782 windowTitle << " - "; 1783 windowTitle << kApplicationName; 1784 SetTitle(windowTitle.String()); 1785 } 1786 1787 1788 void 1789 BrowserWindow::_UpdateTabGroupVisibility() 1790 { 1791 if (Lock()) { 1792 if (fInterfaceVisible) 1793 fTabGroup->SetVisible(_TabGroupShouldBeVisible()); 1794 fTabManager->SetCloseButtonsAvailable(fTabManager->CountTabs() > 1); 1795 Unlock(); 1796 } 1797 } 1798 1799 1800 bool 1801 BrowserWindow::_TabGroupShouldBeVisible() const 1802 { 1803 return (fShowTabsIfSinglePageOpen || fTabManager->CountTabs() > 1) 1804 && (fVisibleInterfaceElements & INTERFACE_ELEMENT_TABS) != 0; 1805 } 1806 1807 1808 void 1809 BrowserWindow::_ShutdownTab(int32 index) 1810 { 1811 BView* view = fTabManager->RemoveTab(index); 1812 BWebView* webView = dynamic_cast<BWebView*>(view); 1813 if (webView == CurrentWebView()) 1814 SetCurrentWebView(NULL); 1815 if (webView != NULL) 1816 webView->Shutdown(); 1817 else 1818 delete view; 1819 } 1820 1821 1822 void 1823 BrowserWindow::_TabChanged(int32 index) 1824 { 1825 SetCurrentWebView(dynamic_cast<BWebView*>(fTabManager->ViewForTab(index))); 1826 } 1827 1828 1829 status_t 1830 BrowserWindow::_BookmarkPath(BPath& path) const 1831 { 1832 status_t ret = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 1833 if (ret != B_OK) 1834 return ret; 1835 1836 ret = path.Append(kApplicationName); 1837 if (ret != B_OK) 1838 return ret; 1839 1840 ret = path.Append("Bookmarks"); 1841 if (ret != B_OK) 1842 return ret; 1843 1844 return create_directory(path.Path(), 0777); 1845 } 1846 1847 1848 void 1849 BrowserWindow::_CreateBookmark() 1850 { 1851 BPath path; 1852 status_t status = _BookmarkPath(path); 1853 if (status != B_OK) { 1854 BString message(B_TRANSLATE_COMMENT("There was an error retrieving " 1855 "the bookmark folder.\n\nError: %error", "Don't translate the " 1856 "variable %error")); 1857 message.ReplaceFirst("%error", strerror(status)); 1858 BAlert* alert = new BAlert(B_TRANSLATE("Bookmark error"), 1859 message.String(), B_TRANSLATE("OK"), NULL, NULL, 1860 B_WIDTH_AS_USUAL, B_STOP_ALERT); 1861 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1862 alert->Go(); 1863 return; 1864 } 1865 BWebView* webView = CurrentWebView(); 1866 BString url(webView->MainFrameURL()); 1867 // Create a bookmark file 1868 BFile bookmarkFile; 1869 BString bookmarkName(webView->MainFrameTitle()); 1870 if (bookmarkName.Length() == 0) { 1871 bookmarkName = url; 1872 int32 leafPos = bookmarkName.FindLast('/'); 1873 if (leafPos >= 0) 1874 bookmarkName.Remove(0, leafPos + 1); 1875 } 1876 // Make sure the bookmark title does not contain chars that are not 1877 // allowed in file names, and is within allowed name length. 1878 bookmarkName.ReplaceAll('/', '-'); 1879 bookmarkName.Truncate(B_FILE_NAME_LENGTH - 1); 1880 1881 // Check that the bookmark exists nowhere in the bookmark hierarchy, 1882 // though the intended file name must match, we don't search the stored 1883 // URLs, only for matching file names. 1884 BDirectory directory(path.Path()); 1885 if (status == B_OK && _CheckBookmarkExists(directory, bookmarkName, url)) { 1886 BString message(B_TRANSLATE_COMMENT("A bookmark for this page " 1887 "(%bookmarkName) already exists.", "Don't translate variable " 1888 "%bookmarkName")); 1889 message.ReplaceFirst("%bookmarkName", bookmarkName); 1890 BAlert* alert = new BAlert(B_TRANSLATE("Bookmark info"), 1891 message.String(), B_TRANSLATE("OK")); 1892 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1893 alert->Go(); 1894 return; 1895 } 1896 1897 BPath entryPath(path); 1898 status = entryPath.Append(bookmarkName); 1899 BEntry entry; 1900 if (status == B_OK) 1901 status = entry.SetTo(entryPath.Path(), true); 1902 if (status == B_OK) { 1903 int32 tries = 1; 1904 while (entry.Exists()) { 1905 // Find a unique name for the bookmark, there may still be a 1906 // file in the way that stores a different URL. 1907 bookmarkName = webView->MainFrameTitle(); 1908 bookmarkName << " " << tries++; 1909 entryPath = path; 1910 status = entryPath.Append(bookmarkName); 1911 if (status == B_OK) 1912 status = entry.SetTo(entryPath.Path(), true); 1913 if (status != B_OK) 1914 break; 1915 } 1916 } 1917 if (status == B_OK) { 1918 status = bookmarkFile.SetTo(&entry, 1919 B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 1920 } 1921 1922 // Write bookmark meta data 1923 if (status == B_OK) 1924 status = bookmarkFile.WriteAttrString("META:url", &url); 1925 if (status == B_OK) { 1926 BString title = webView->MainFrameTitle(); 1927 bookmarkFile.WriteAttrString("META:title", &title); 1928 } 1929 1930 BNodeInfo nodeInfo(&bookmarkFile); 1931 if (status == B_OK) { 1932 status = nodeInfo.SetType("application/x-vnd.Be-bookmark"); 1933 if (status == B_OK) { 1934 PageUserData* userData = static_cast<PageUserData*>( 1935 webView->GetUserData()); 1936 if (userData != NULL && userData->PageIcon() != NULL) { 1937 BBitmap miniIcon(BRect(0, 0, 15, 15), B_BITMAP_NO_SERVER_LINK, 1938 B_CMAP8); 1939 status_t ret = miniIcon.ImportBits(userData->PageIcon()); 1940 if (ret == B_OK) 1941 ret = nodeInfo.SetIcon(&miniIcon, B_MINI_ICON); 1942 if (ret != B_OK) { 1943 fprintf(stderr, "Failed to store mini icon for bookmark: " 1944 "%s\n", strerror(ret)); 1945 } 1946 BBitmap largeIcon(BRect(0, 0, 31, 31), B_BITMAP_NO_SERVER_LINK, 1947 B_CMAP8); 1948 // TODO: Store 32x32 favicon which is often provided by sites. 1949 const uint8* src = (const uint8*)miniIcon.Bits(); 1950 uint32 srcBPR = miniIcon.BytesPerRow(); 1951 uint8* dst = (uint8*)largeIcon.Bits(); 1952 uint32 dstBPR = largeIcon.BytesPerRow(); 1953 for (uint32 y = 0; y < 16; y++) { 1954 const uint8* s = src; 1955 uint8* d = dst; 1956 for (uint32 x = 0; x < 16; x++) { 1957 *d++ = *s; 1958 *d++ = *s++; 1959 } 1960 dst += dstBPR; 1961 s = src; 1962 for (uint32 x = 0; x < 16; x++) { 1963 *d++ = *s; 1964 *d++ = *s++; 1965 } 1966 dst += dstBPR; 1967 src += srcBPR; 1968 } 1969 if (ret == B_OK) 1970 ret = nodeInfo.SetIcon(&largeIcon, B_LARGE_ICON); 1971 if (ret != B_OK) { 1972 fprintf(stderr, "Failed to store large icon for bookmark: " 1973 "%s\n", strerror(ret)); 1974 } 1975 } 1976 } 1977 } 1978 1979 if (status != B_OK) { 1980 BString message(B_TRANSLATE_COMMENT("There was an error creating the " 1981 "bookmark file.\n\nError: %error", "Don't translate variable " 1982 "%error")); 1983 message.ReplaceFirst("%error", strerror(status)); 1984 BAlert* alert = new BAlert(B_TRANSLATE("Bookmark error"), 1985 message.String(), B_TRANSLATE("OK"), NULL, NULL, 1986 B_WIDTH_AS_USUAL, B_STOP_ALERT); 1987 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1988 alert->Go(); 1989 return; 1990 } 1991 } 1992 1993 1994 void 1995 BrowserWindow::_ShowBookmarks() 1996 { 1997 BPath path; 1998 entry_ref ref; 1999 status_t status = _BookmarkPath(path); 2000 if (status == B_OK) 2001 status = get_ref_for_path(path.Path(), &ref); 2002 if (status == B_OK) 2003 status = be_roster->Launch(&ref); 2004 2005 if (status != B_OK && status != B_ALREADY_RUNNING) { 2006 BString message(B_TRANSLATE_COMMENT("There was an error trying to " 2007 "show the Bookmarks folder.\n\nError: %error", 2008 "Don't translate variable %error")); 2009 message.ReplaceFirst("%error", strerror(status)); 2010 BAlert* alert = new BAlert(B_TRANSLATE("Bookmark error"), 2011 message.String(), B_TRANSLATE("OK"), NULL, NULL, 2012 B_WIDTH_AS_USUAL, B_STOP_ALERT); 2013 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 2014 alert->Go(); 2015 return; 2016 } 2017 } 2018 2019 2020 bool BrowserWindow::_CheckBookmarkExists(BDirectory& directory, 2021 const BString& bookmarkName, const BString& url) const 2022 { 2023 BEntry entry; 2024 while (directory.GetNextEntry(&entry) == B_OK) { 2025 if (entry.IsDirectory()) { 2026 BDirectory subBirectory(&entry); 2027 // At least preserve the entry file handle when recursing into 2028 // sub-folders... eventually we will run out, though, with very 2029 // deep hierarchy. 2030 entry.Unset(); 2031 if (_CheckBookmarkExists(subBirectory, bookmarkName, url)) 2032 return true; 2033 } else { 2034 char entryName[B_FILE_NAME_LENGTH]; 2035 if (entry.GetName(entryName) != B_OK || bookmarkName != entryName) 2036 continue; 2037 BString storedURL; 2038 BFile file(&entry, B_READ_ONLY); 2039 if (_ReadURLAttr(file, storedURL)) { 2040 // Just bail if the bookmark already exists 2041 if (storedURL == url) 2042 return true; 2043 } 2044 } 2045 } 2046 return false; 2047 } 2048 2049 2050 bool 2051 BrowserWindow::_ReadURLAttr(BFile& bookmarkFile, BString& url) const 2052 { 2053 return bookmarkFile.InitCheck() == B_OK 2054 && bookmarkFile.ReadAttrString("META:url", &url) == B_OK; 2055 } 2056 2057 2058 void 2059 BrowserWindow::_AddBookmarkURLsRecursively(BDirectory& directory, 2060 BMessage* message, uint32& addedCount) const 2061 { 2062 BEntry entry; 2063 while (directory.GetNextEntry(&entry) == B_OK) { 2064 if (entry.IsDirectory()) { 2065 BDirectory subBirectory(&entry); 2066 // At least preserve the entry file handle when recursing into 2067 // sub-folders... eventually we will run out, though, with very 2068 // deep hierarchy. 2069 entry.Unset(); 2070 _AddBookmarkURLsRecursively(subBirectory, message, addedCount); 2071 } else { 2072 BString storedURL; 2073 BFile file(&entry, B_READ_ONLY); 2074 if (_ReadURLAttr(file, storedURL)) { 2075 message->AddString("url", storedURL.String()); 2076 addedCount++; 2077 } 2078 } 2079 } 2080 } 2081 2082 2083 void 2084 BrowserWindow::_SetPageIcon(BWebView* view, const BBitmap* icon) 2085 { 2086 PageUserData* userData = static_cast<PageUserData*>(view->GetUserData()); 2087 if (userData == NULL) { 2088 userData = new(std::nothrow) PageUserData(NULL); 2089 if (userData == NULL) 2090 return; 2091 view->SetUserData(userData); 2092 } 2093 // The PageUserData makes a copy of the icon, which we pass on to 2094 // the TabManager for display in the respective tab. 2095 userData->SetPageIcon(icon); 2096 fTabManager->SetTabIcon(view, userData->PageIcon()); 2097 if (view == CurrentWebView()) 2098 fURLInputGroup->SetPageIcon(icon); 2099 } 2100 2101 2102 static void 2103 addItemToMenuOrSubmenu(BMenu* menu, BMenuItem* newItem) 2104 { 2105 BString baseURLLabel = baseURL(BString(newItem->Label())); 2106 for (int32 i = menu->CountItems() - 1; i >= 0; i--) { 2107 BMenuItem* item = menu->ItemAt(i); 2108 BString label = item->Label(); 2109 if (label.FindFirst(baseURLLabel) >= 0) { 2110 if (item->Submenu()) { 2111 // Submenu was already added in previous iteration. 2112 item->Submenu()->AddItem(newItem); 2113 return; 2114 } else { 2115 menu->RemoveItem(item); 2116 BMenu* subMenu = new BMenu(baseURLLabel.String()); 2117 subMenu->AddItem(item); 2118 subMenu->AddItem(newItem); 2119 // Add common submenu for this base URL, clickable. 2120 BMessage* message = new BMessage(GOTO_URL); 2121 message->AddString("url", baseURLLabel.String()); 2122 menu->AddItem(new BMenuItem(subMenu, message), i); 2123 return; 2124 } 2125 } 2126 } 2127 menu->AddItem(newItem); 2128 } 2129 2130 2131 static void 2132 addOrDeleteMenu(BMenu* menu, BMenu* toMenu) 2133 { 2134 if (menu->CountItems() > 0) 2135 toMenu->AddItem(menu); 2136 else 2137 delete menu; 2138 } 2139 2140 2141 void 2142 BrowserWindow::_UpdateHistoryMenu() 2143 { 2144 BMenuItem* menuItem; 2145 while ((menuItem = fHistoryMenu->RemoveItem(fHistoryMenuFixedItemCount))) 2146 delete menuItem; 2147 2148 BrowsingHistory* history = BrowsingHistory::DefaultInstance(); 2149 if (!history->Lock()) 2150 return; 2151 2152 int32 count = history->CountItems(); 2153 BMenuItem* clearHistoryItem = new BMenuItem(B_TRANSLATE("Clear history"), 2154 new BMessage(CLEAR_HISTORY)); 2155 clearHistoryItem->SetEnabled(count > 0); 2156 fHistoryMenu->AddItem(clearHistoryItem); 2157 if (count == 0) { 2158 history->Unlock(); 2159 return; 2160 } 2161 fHistoryMenu->AddSeparatorItem(); 2162 2163 BDateTime todayStart = BDateTime::CurrentDateTime(B_LOCAL_TIME); 2164 todayStart.SetTime(BTime(0, 0, 0)); 2165 2166 BDateTime oneDayAgoStart = todayStart; 2167 oneDayAgoStart.Date().AddDays(-1); 2168 2169 BDateTime twoDaysAgoStart = oneDayAgoStart; 2170 twoDaysAgoStart.Date().AddDays(-1); 2171 2172 BDateTime threeDaysAgoStart = twoDaysAgoStart; 2173 threeDaysAgoStart.Date().AddDays(-1); 2174 2175 BDateTime fourDaysAgoStart = threeDaysAgoStart; 2176 fourDaysAgoStart.Date().AddDays(-1); 2177 2178 BDateTime fiveDaysAgoStart = fourDaysAgoStart; 2179 fiveDaysAgoStart.Date().AddDays(-1); 2180 2181 BMenu* todayMenu = new BMenu(B_TRANSLATE("Today")); 2182 BMenu* yesterdayMenu = new BMenu(B_TRANSLATE("Yesterday")); 2183 BMenu* twoDaysAgoMenu = new BMenu( 2184 twoDaysAgoStart.Date().LongDayName().String()); 2185 BMenu* threeDaysAgoMenu = new BMenu( 2186 threeDaysAgoStart.Date().LongDayName().String()); 2187 BMenu* fourDaysAgoMenu = new BMenu( 2188 fourDaysAgoStart.Date().LongDayName().String()); 2189 BMenu* fiveDaysAgoMenu = new BMenu( 2190 fiveDaysAgoStart.Date().LongDayName().String()); 2191 BMenu* earlierMenu = new BMenu(B_TRANSLATE("Earlier")); 2192 2193 for (int32 i = 0; i < count; i++) { 2194 BrowsingHistoryItem historyItem = history->HistoryItemAt(i); 2195 BMessage* message = new BMessage(GOTO_URL); 2196 message->AddString("url", historyItem.URL().String()); 2197 2198 BString truncatedUrl(historyItem.URL()); 2199 be_plain_font->TruncateString(&truncatedUrl, B_TRUNCATE_END, 480); 2200 menuItem = new BMenuItem(truncatedUrl, message); 2201 2202 if (historyItem.DateTime() < fiveDaysAgoStart) 2203 addItemToMenuOrSubmenu(earlierMenu, menuItem); 2204 else if (historyItem.DateTime() < fourDaysAgoStart) 2205 addItemToMenuOrSubmenu(fiveDaysAgoMenu, menuItem); 2206 else if (historyItem.DateTime() < threeDaysAgoStart) 2207 addItemToMenuOrSubmenu(fourDaysAgoMenu, menuItem); 2208 else if (historyItem.DateTime() < twoDaysAgoStart) 2209 addItemToMenuOrSubmenu(threeDaysAgoMenu, menuItem); 2210 else if (historyItem.DateTime() < oneDayAgoStart) 2211 addItemToMenuOrSubmenu(twoDaysAgoMenu, menuItem); 2212 else if (historyItem.DateTime() < todayStart) 2213 addItemToMenuOrSubmenu(yesterdayMenu, menuItem); 2214 else 2215 addItemToMenuOrSubmenu(todayMenu, menuItem); 2216 } 2217 history->Unlock(); 2218 2219 addOrDeleteMenu(todayMenu, fHistoryMenu); 2220 addOrDeleteMenu(yesterdayMenu, fHistoryMenu); 2221 addOrDeleteMenu(twoDaysAgoMenu, fHistoryMenu); 2222 addOrDeleteMenu(threeDaysAgoMenu, fHistoryMenu); 2223 addOrDeleteMenu(fourDaysAgoMenu, fHistoryMenu); 2224 addOrDeleteMenu(fiveDaysAgoMenu, fHistoryMenu); 2225 addOrDeleteMenu(earlierMenu, fHistoryMenu); 2226 } 2227 2228 2229 void 2230 BrowserWindow::_UpdateClipboardItems() 2231 { 2232 BTextView* focusTextView = dynamic_cast<BTextView*>(CurrentFocus()); 2233 if (focusTextView != NULL) { 2234 int32 selectionStart; 2235 int32 selectionEnd; 2236 focusTextView->GetSelection(&selectionStart, &selectionEnd); 2237 bool hasSelection = selectionStart < selectionEnd; 2238 bool canPaste = false; 2239 // A BTextView has the focus. 2240 if (be_clipboard->Lock()) { 2241 BMessage* data = be_clipboard->Data(); 2242 if (data != NULL) 2243 canPaste = data->HasData("text/plain", B_MIME_TYPE); 2244 be_clipboard->Unlock(); 2245 } 2246 fCutMenuItem->SetEnabled(hasSelection); 2247 fCopyMenuItem->SetEnabled(hasSelection); 2248 fPasteMenuItem->SetEnabled(canPaste); 2249 } else if (CurrentWebView() != NULL) { 2250 // Trigger update of the clipboard items, even if the 2251 // BWebView doesn't have focus, we'll dispatch these message 2252 // there anyway. This works so fast that the user can never see 2253 // the wrong enabled state when the menu opens until the result 2254 // message arrives. The initial state needs to be enabled, since 2255 // standard shortcut handling is always wrapped inside MenusBeginning() 2256 // and MenusEnded(), and since we update items asynchronously, we need 2257 // to have them enabled to begin with. 2258 fCutMenuItem->SetEnabled(true); 2259 fCopyMenuItem->SetEnabled(true); 2260 fPasteMenuItem->SetEnabled(true); 2261 2262 CurrentWebView()->WebPage()->SendEditingCapabilities(); 2263 } 2264 } 2265 2266 2267 bool 2268 BrowserWindow::_ShowPage(BWebView* view) 2269 { 2270 if (view != CurrentWebView()) { 2271 int32 tabIndex = fTabManager->TabForView(view); 2272 if (tabIndex < 0) { 2273 // Page seems to be gone already? 2274 return false; 2275 } 2276 fTabManager->SelectTab(tabIndex); 2277 _TabChanged(tabIndex); 2278 UpdateIfNeeded(); 2279 } 2280 return true; 2281 } 2282 2283 2284 void 2285 BrowserWindow::_ResizeToScreen() 2286 { 2287 BScreen screen(this); 2288 MoveTo(0, 0); 2289 ResizeTo(screen.Frame().Width(), screen.Frame().Height()); 2290 } 2291 2292 2293 void 2294 BrowserWindow::_SetAutoHideInterfaceInFullscreen(bool doIt) 2295 { 2296 if (fAutoHideInterfaceInFullscreenMode == doIt) 2297 return; 2298 2299 fAutoHideInterfaceInFullscreenMode = doIt; 2300 if (fAppSettings->GetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode, 2301 doIt) != doIt) { 2302 fAppSettings->SetValue(kSettingsKeyAutoHideInterfaceInFullscreenMode, 2303 doIt); 2304 } 2305 2306 if (fAutoHideInterfaceInFullscreenMode) { 2307 BMessage message(CHECK_AUTO_HIDE_INTERFACE); 2308 fPulseRunner = new BMessageRunner(BMessenger(this), &message, 300000); 2309 } else { 2310 delete fPulseRunner; 2311 fPulseRunner = NULL; 2312 _ShowInterface(true); 2313 } 2314 } 2315 2316 2317 void 2318 BrowserWindow::_CheckAutoHideInterface() 2319 { 2320 if (!fIsFullscreen || !fAutoHideInterfaceInFullscreenMode 2321 || (CurrentWebView() != NULL && !CurrentWebView()->IsFocus())) { 2322 return; 2323 } 2324 2325 if (fLastMousePos.y == 0) 2326 _ShowInterface(true); 2327 else if (fNavigationGroup->IsVisible() 2328 && fLastMousePos.y > fNavigationGroup->Frame().bottom 2329 && system_time() - fLastMouseMovedTime > 1000000) { 2330 // NOTE: Do not re-use navigationGroupBottom in the above 2331 // check, since we only want to hide the interface when it is visible. 2332 _ShowInterface(false); 2333 } 2334 } 2335 2336 2337 void 2338 BrowserWindow::_ShowInterface(bool show) 2339 { 2340 if (fInterfaceVisible == show) 2341 return; 2342 2343 fInterfaceVisible = show; 2344 2345 if (show) { 2346 #if !INTEGRATE_MENU_INTO_TAB_BAR 2347 fMenuGroup->SetVisible( 2348 (fVisibleInterfaceElements & INTERFACE_ELEMENT_MENU) != 0); 2349 #endif 2350 fTabGroup->SetVisible(_TabGroupShouldBeVisible()); 2351 fNavigationGroup->SetVisible( 2352 (fVisibleInterfaceElements & INTERFACE_ELEMENT_NAVIGATION) != 0); 2353 fStatusGroup->SetVisible( 2354 (fVisibleInterfaceElements & INTERFACE_ELEMENT_STATUS) != 0); 2355 } else { 2356 fMenuGroup->SetVisible(false); 2357 fTabGroup->SetVisible(false); 2358 fNavigationGroup->SetVisible(false); 2359 fStatusGroup->SetVisible(false); 2360 } 2361 // TODO: Setting the group visible seems to unhide the status bar. 2362 // Fix in Haiku? 2363 while (!fLoadingProgressBar->IsHidden()) 2364 fLoadingProgressBar->Hide(); 2365 } 2366 2367 2368 void 2369 BrowserWindow::_ShowProgressBar(bool show) 2370 { 2371 if (show) { 2372 if (!fStatusGroup->IsVisible() && (fVisibleInterfaceElements 2373 & INTERFACE_ELEMENT_STATUS) != 0) 2374 fStatusGroup->SetVisible(true); 2375 fLoadingProgressBar->Show(); 2376 } else { 2377 if (!fInterfaceVisible) 2378 fStatusGroup->SetVisible(false); 2379 // TODO: This is also used in _ShowInterface. Without it the status bar 2380 // doesn't always hide again. It may be an Interface Kit bug. 2381 while (!fLoadingProgressBar->IsHidden()) 2382 fLoadingProgressBar->Hide(); 2383 } 2384 } 2385 2386 2387 void 2388 BrowserWindow::_InvokeButtonVisibly(BButton* button) 2389 { 2390 button->SetValue(B_CONTROL_ON); 2391 UpdateIfNeeded(); 2392 button->Invoke(); 2393 snooze(1000); 2394 button->SetValue(B_CONTROL_OFF); 2395 } 2396 2397 2398 BString 2399 BrowserWindow::_NewTabURL(bool isNewWindow) const 2400 { 2401 BString url; 2402 uint32 policy = isNewWindow ? fNewWindowPolicy : fNewTabPolicy; 2403 // Implement new page policy 2404 switch (policy) { 2405 case OpenStartPage: 2406 url = fStartPageURL; 2407 break; 2408 case OpenSearchPage: 2409 url.SetToFormat(fSearchPageURL, ""); 2410 break; 2411 case CloneCurrentPage: 2412 if (CurrentWebView() != NULL) 2413 url = CurrentWebView()->MainFrameURL(); 2414 break; 2415 case OpenBlankPage: 2416 default: 2417 break; 2418 } 2419 return url; 2420 } 2421 2422 2423 BString 2424 BrowserWindow::_EncodeURIComponent(const BString& search) 2425 { 2426 // We have to take care of some of the escaping before we hand over the 2427 // search string to WebKit, if we want queries like "4+3" to not be 2428 // searched as "4 3". 2429 const BString escCharList = " $&`:<>[]{}\"+#%@/;=?\\^|~\',"; 2430 BString result = search; 2431 char hexcode[4]; 2432 2433 for (int32 i = 0; i < result.Length(); i++) { 2434 if (escCharList.FindFirst(result[i]) != B_ERROR) { 2435 sprintf(hexcode, "%02X", (unsigned int)result[i]); 2436 result[i] = '%'; 2437 result.Insert(hexcode, i + 1); 2438 i += 2; 2439 } 2440 } 2441 2442 return result; 2443 } 2444 2445 2446 void 2447 BrowserWindow::_VisitURL(const BString& url) 2448 { 2449 //fURLInputGroup->TextView()->SetText(url); 2450 CurrentWebView()->LoadURL(url.String()); 2451 } 2452 2453 2454 void 2455 BrowserWindow::_VisitSearchEngine(const BString& search) 2456 { 2457 BString engine = ""; 2458 engine.SetToFormat(fSearchPageURL, _EncodeURIComponent(search).String()); 2459 2460 _VisitURL(engine); 2461 } 2462 2463 2464 inline bool 2465 BrowserWindow::_IsValidDomainChar(char ch) 2466 { 2467 // TODO: Currenlty, only a whitespace character breaks a domain name. It 2468 // might be a good idea (or a bad one) to make character filtering based on 2469 // the IDNA 2008 standard. 2470 2471 return ch != ' '; 2472 } 2473 2474 2475 /*! \brief "smart" parser for user-entered URLs 2476 2477 We try to be flexible in what we accept as a valid URL. The protocol may 2478 be missing, or something we can't handle (in that case we run the matching 2479 app). If all attempts to make sense of the input fail, we make a search 2480 engine query for it. 2481 */ 2482 void 2483 BrowserWindow::_SmartURLHandler(const BString& url) 2484 { 2485 // First test if the URL has a protocol field 2486 int32 at = url.FindFirst(":"); 2487 2488 if (at != B_ERROR) { 2489 // There is a protocol, let's see if we can handle it 2490 BString proto; 2491 url.CopyInto(proto, 0, at); 2492 2493 bool handled = false; 2494 2495 // First try the built-in supported ones 2496 for (unsigned int i = 0; i < sizeof(kHandledProtocols) / sizeof(char*); 2497 i++) { 2498 handled = (proto == kHandledProtocols[i]); 2499 if (handled) 2500 break; 2501 } 2502 2503 if (handled) { 2504 // This is the easy case, a complete and well-formed URL, we can 2505 // navigate to it without further efforts. 2506 _VisitURL(url); 2507 return; 2508 } else { 2509 // There is what looks like a protocol, but one we don't know. 2510 // Ask the BRoster if there is a matching filetype and app which 2511 // can handle it. 2512 BString temp; 2513 temp = "application/x-vnd.Be.URL."; 2514 temp += proto; 2515 2516 const char* argv[] = { url.String(), NULL }; 2517 2518 if (be_roster->Launch(temp.String(), 1, argv) == B_OK) 2519 return; 2520 } 2521 } 2522 2523 // There is no protocol or only an unsupported one. So let's try harder to 2524 // guess what the request is. 2525 2526 // "localhost" is a special case, it is a valid domain name but has no dots. 2527 // Handle it separately. 2528 if (url == "localhost") 2529 _VisitURL("http://localhost/"); 2530 else { 2531 // Also handle URLs starting with "localhost" followed by a path. 2532 const char* localhostPrefix = "localhost/"; 2533 2534 if (url.Compare(localhostPrefix, strlen(localhostPrefix)) == 0) 2535 _VisitURL(url); 2536 else { 2537 // In all other cases we try to detect a valid domain name. There 2538 // must be at least one dot and no spaces until the first / in the 2539 // URL. 2540 bool isURL = false; 2541 2542 for (int32 i = 0; i < url.CountChars(); i++) { 2543 if (url[i] == '.') 2544 isURL = true; 2545 else if (url[i] == '/') 2546 break; 2547 else if (!_IsValidDomainChar(url[i])) { 2548 isURL = false; 2549 2550 break; 2551 } 2552 } 2553 2554 if (isURL) { 2555 // This is apparently an URL missing the protocol part. In that 2556 // case we default to http. 2557 BString prefixed = "http://"; 2558 prefixed << url; 2559 _VisitURL(prefixed); 2560 return; 2561 } else { 2562 // We couldn't find anything that looks like an URL. Let's 2563 // assume what we have is a search request and go to the search 2564 // engine. 2565 _VisitSearchEngine(url); 2566 } 2567 } 2568 } 2569 } 2570 2571 2572 void 2573 BrowserWindow::_HandlePageSourceResult(const BMessage* message) 2574 { 2575 // TODO: This should be done in an extra thread perhaps. Doing it in 2576 // the application thread is not much better, since it actually draws 2577 // the pages... 2578 2579 BPath pathToPageSource; 2580 2581 BString url; 2582 status_t ret = message->FindString("url", &url); 2583 if (ret == B_OK && url.FindFirst("file://") == 0) { 2584 // Local file 2585 url.Remove(0, strlen("file://")); 2586 pathToPageSource.SetTo(url.String()); 2587 } else { 2588 // Something else, store it. 2589 // TODO: What if it isn't HTML, but for example SVG? 2590 BString source; 2591 ret = message->FindString("source", &source); 2592 2593 if (ret == B_OK) 2594 ret = find_directory(B_SYSTEM_TEMP_DIRECTORY, &pathToPageSource); 2595 2596 BString tmpFileName("PageSource_"); 2597 tmpFileName << system_time() << ".html"; 2598 if (ret == B_OK) 2599 ret = pathToPageSource.Append(tmpFileName.String()); 2600 2601 BFile pageSourceFile(pathToPageSource.Path(), 2602 B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 2603 if (ret == B_OK) 2604 ret = pageSourceFile.InitCheck(); 2605 2606 if (ret == B_OK) { 2607 ssize_t written = pageSourceFile.Write(source.String(), 2608 source.Length()); 2609 if (written != source.Length()) 2610 ret = (status_t)written; 2611 } 2612 2613 if (ret == B_OK) { 2614 const char* type = "text/html"; 2615 size_t size = strlen(type); 2616 pageSourceFile.WriteAttr("BEOS:TYPE", B_STRING_TYPE, 0, type, size); 2617 // If it fails we don't care. 2618 } 2619 } 2620 2621 entry_ref ref; 2622 if (ret == B_OK) 2623 ret = get_ref_for_path(pathToPageSource.Path(), &ref); 2624 2625 if (ret == B_OK) { 2626 BMessage refsMessage(B_REFS_RECEIVED); 2627 ret = refsMessage.AddRef("refs", &ref); 2628 if (ret == B_OK) { 2629 ret = be_roster->Launch("text/x-source-code", &refsMessage); 2630 if (ret == B_ALREADY_RUNNING) 2631 ret = B_OK; 2632 } 2633 } 2634 2635 if (ret != B_OK) { 2636 char buffer[1024]; 2637 snprintf(buffer, sizeof(buffer), "Failed to show the " 2638 "page source: %s\n", strerror(ret)); 2639 BAlert* alert = new BAlert(B_TRANSLATE("Page source error"), buffer, 2640 B_TRANSLATE("OK")); 2641 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 2642 alert->Go(NULL); 2643 } 2644 } 2645 2646 2647 void 2648 BrowserWindow::_ShowBookmarkBar(bool show) 2649 { 2650 fBookmarkBarMenuItem->SetMarked(show); 2651 2652 if (fBookmarkBar == NULL || fBookmarkBar->IsHidden() != show) 2653 return; 2654 2655 fAppSettings->SetValue(kSettingsShowBookmarkBar, show); 2656 2657 if (show) 2658 fBookmarkBar->Show(); 2659 else 2660 fBookmarkBar->Hide(); 2661 } 2662